Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions packages/app/src/cli/commands/dev/execute.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import Execute from './execute.js'
import {executeDevPlatformOperation} from '../../services/dev/execute.js'
import {outputResult} from '@shopify/cli-kit/node/output'
import {afterEach, beforeEach, expect, test, vi} from 'vitest'
import {Parser} from '@oclif/core'

vi.mock('../../services/dev/execute.js')
vi.mock('@shopify/cli-kit/node/output', async (importOriginal) => ({
...(await importOriginal<typeof import('@shopify/cli-kit/node/output')>()),
outputResult: vi.fn(),
}))

const originalExitCode = process.exitCode

beforeEach(() => {
process.exitCode = undefined
})

afterEach(() => {
process.exitCode = originalExitCode
})

test('forwards a GraphQL request without an app project or log-specific flags and prints the full response', async () => {
const query = '{ __schema { queryType { name } } }'
const response = {data: {__schema: {queryType: {name: 'QueryRoot'}}}}
vi.mocked(executeDevPlatformOperation).mockResolvedValue({response, failed: false})

await Execute.run(['--query', query], import.meta.url)

expect(executeDevPlatformOperation).toHaveBeenCalledWith({
query,
queryFile: undefined,
variables: undefined,
variableFile: undefined,
operationName: undefined,
demo: false,
})
expect(outputResult).toHaveBeenCalledExactlyOnceWith(JSON.stringify(response, null, 2))
expect(process.exitCode).toBeUndefined()
})

test('forwards stdin, variables, operation name and the local demo selection', async () => {
vi.mocked(executeDevPlatformOperation).mockResolvedValue({response: {data: {app: null}}, failed: false})

await Execute.run(
['--query-file', '-', '--variables', '{"key":"test-app"}', '--operation-name', 'Logs', '--demo'],
import.meta.url,
)

expect(executeDevPlatformOperation).toHaveBeenCalledWith({
query: undefined,
queryFile: '-',
variables: '{"key":"test-app"}',
variableFile: undefined,
operationName: 'Logs',
demo: true,
})
})

test('preserves partial data and errors in stdout while setting a failing exit status', async () => {
const response = {data: {app: null}, errors: [{message: 'Access denied', path: ['app']}]}
vi.mocked(executeDevPlatformOperation).mockResolvedValue({response, failed: true})

await Execute.run(['--query', '{ app(key: "test-app") { key } }'], import.meta.url)

expect(outputResult).toHaveBeenCalledExactlyOnceWith(JSON.stringify(response, null, 2))
expect(process.exitCode).toBe(1)
})

test.each([
{args: []},
{args: ['--query', '{ __typename }', '--query-file', 'query.graphql']},
{args: ['--query', '{ __typename }', '--variables', '{}', '--variable-file', 'variables.json']},
{args: ['--query', '{ __typename }', '--minutes', '15']},
{args: ['--query', '{ __typename }', '--client-id', 'test-app']},
{args: ['--query', '{ __typename }', '--list-filters']},
])('rejects conflicting, missing or log-specific flags %j', async ({args}) => {
await expect(Parser.parse(args, {flags: Execute.flags})).rejects.toThrow()
})
49 changes: 49 additions & 0 deletions packages/app/src/cli/commands/dev/execute.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import {executeDevPlatformOperation} from '../../services/dev/execute.js'
import {operationFlags} from '../../flags.js'
import BaseCommand from '@shopify/cli-kit/node/base-command'
import {globalFlags} from '@shopify/cli-kit/node/cli'
import {outputResult} from '@shopify/cli-kit/node/output'
import {Flags} from '@oclif/core'

export default class Execute extends BaseCommand {
static hidden = true
static summary = 'Prototype only: execute a GraphQL request against the local Dev Platform API.'
static description =
'Prints the complete GraphQL JSON response. The query selects apps, filters, and returned fields. ' +
'GraphQL or HTTP errors produce a nonzero exit status, preserving partial data when available.'

static flags = {
...globalFlags,
query: operationFlags.query,
'query-file': Flags.string({
description: 'Path to a GraphQL document, or - to read from stdin.',
env: 'SHOPIFY_FLAG_QUERY_FILE',
exactlyOne: ['query', 'query-file'],
}),
variables: operationFlags.variables,
'variable-file': operationFlags['variable-file'],
'operation-name': Flags.string({
description: 'The operation to execute when the document contains multiple operations.',
env: 'SHOPIFY_FLAG_OPERATION_NAME',
}),
demo: Flags.boolean({
default: false,
env: 'SHOPIFY_FLAG_DEMO',
description: 'Use the loopback demo with seeded auth, not Identity login.',
}),
}

public async run(): Promise<void> {
const {flags} = await this.parse(Execute)
const {response, failed} = await executeDevPlatformOperation({
query: flags.query,
queryFile: flags['query-file'],
variables: flags.variables,
variableFile: flags['variable-file'],
operationName: flags['operation-name'],
demo: flags.demo,
})
outputResult(JSON.stringify(response, null, 2))
if (failed) process.exitCode = 1
}
}
2 changes: 2 additions & 0 deletions packages/app/src/cli/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import DoctorSubmit from './commands/app/doctor/submit.js'
import Doctor from './commands/app/doctor.js'
import Logs from './commands/app/logs.js'
import Sources from './commands/app/app-logs/sources.js'
import DevExecute from './commands/dev/execute.js'
import EnvPull from './commands/app/env/pull.js'
import EnvShow from './commands/app/env/show.js'
import BulkExecute from './commands/app/bulk/execute.js'
Expand Down Expand Up @@ -61,6 +62,7 @@ export const commands: {[key: string]: typeof AppLinkedCommand | typeof AppUnlin
'app:doctor': Doctor,
'app:logs': Logs,
'app:logs:sources': Sources,
'dev:execute': DevExecute,
'app:import-custom-data-definitions': ImportCustomDataDefinitions,
'app:import-extensions': ImportExtensions,
'app:info': AppInfo,
Expand Down
Loading
Loading