Skip to content

Commit 7297a1f

Browse files
Merge pull request #6734 from Shopify/execute_commands_remove_query_stdin_support
remove STDIN support on `execute` commands
2 parents a28e8f4 + 6d211c7 commit 7297a1f

File tree

6 files changed

+15
-78
lines changed

6 files changed

+15
-78
lines changed

packages/app/src/cli/commands/app/bulk/execute.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export default class BulkExecute extends AppLinkedCommand {
2121
async run(): Promise<AppLinkedCommandOutput> {
2222
const {flags} = await this.parse(BulkExecute)
2323

24-
const {query, appContextResult, store} = await prepareExecuteContext(flags, 'bulk execute')
24+
const {query, appContextResult, store} = await prepareExecuteContext(flags)
2525

2626
await executeBulkOperation({
2727
organization: appContextResult.organization,

packages/app/src/cli/commands/app/execute.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export default class Execute extends AppLinkedCommand {
2020
async run(): Promise<AppLinkedCommandOutput> {
2121
const {flags} = await this.parse(Execute)
2222

23-
const {query, appContextResult, store} = await prepareExecuteContext(flags, 'execute')
23+
const {query, appContextResult, store} = await prepareExecuteContext(flags)
2424

2525
await executeOperation({
2626
organization: appContextResult.organization,

packages/app/src/cli/flags.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,16 +38,16 @@ export const appFlags = {
3838
export const bulkOperationFlags = {
3939
query: Flags.string({
4040
char: 'q',
41-
description: 'The GraphQL query or mutation to run as a bulk operation. If omitted, reads from standard input.',
41+
description: 'The GraphQL query or mutation to run as a bulk operation.',
4242
env: 'SHOPIFY_FLAG_QUERY',
4343
required: false,
44-
exclusive: ['query-file'],
44+
exactlyOne: ['query', 'query-file'],
4545
}),
4646
'query-file': Flags.string({
4747
description: "Path to a file containing the GraphQL query or mutation. Can't be used with --query.",
4848
env: 'SHOPIFY_FLAG_QUERY_FILE',
4949
parse: async (input) => resolvePath(input),
50-
exclusive: ['query'],
50+
exactlyOne: ['query', 'query-file'],
5151
}),
5252
variables: Flags.string({
5353
char: 'v',
@@ -92,13 +92,13 @@ export const operationFlags = {
9292
description: 'The GraphQL query or mutation, as a string.',
9393
env: 'SHOPIFY_FLAG_QUERY',
9494
required: false,
95-
exclusive: ['query-file'],
95+
exactlyOne: ['query', 'query-file'],
9696
}),
9797
'query-file': Flags.string({
9898
description: "Path to a file containing the GraphQL query or mutation. Can't be used with --query.",
9999
env: 'SHOPIFY_FLAG_QUERY_FILE',
100100
parse: async (input) => resolvePath(input),
101-
exclusive: ['query'],
101+
exactlyOne: ['query', 'query-file'],
102102
}),
103103
variables: Flags.string({
104104
char: 'v',

packages/app/src/cli/utilities/execute-command-helpers.test.ts

Lines changed: 2 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import {linkedAppContext} from '../services/app-context.js'
33
import {storeContext} from '../services/store-context.js'
44
import {validateSingleOperation} from '../services/graphql/common.js'
55
import {readFile, fileExists} from '@shopify/cli-kit/node/fs'
6-
import {readStdinString} from '@shopify/cli-kit/node/system'
76
import {describe, test, expect, vi, beforeEach} from 'vitest'
87

98
vi.mock('../services/app-context.js')
@@ -116,48 +115,18 @@ describe('prepareExecuteContext', () => {
116115
beforeEach(() => {
117116
vi.mocked(linkedAppContext).mockResolvedValue(mockAppContextResult as any)
118117
vi.mocked(storeContext).mockResolvedValue(mockStore as any)
119-
vi.mocked(readStdinString).mockResolvedValue('')
120118
})
121119

122120
test('uses query from flags when provided', async () => {
123121
const result = await prepareExecuteContext(mockFlags)
124122

125123
expect(result.query).toBe(mockFlags.query)
126-
expect(readStdinString).not.toHaveBeenCalled()
127124
})
128125

129-
test('reads query from stdin when flag not provided', async () => {
130-
const stdinQuery = 'query { products { edges { node { id } } } }'
131-
vi.mocked(readStdinString).mockResolvedValue(stdinQuery)
132-
133-
const flagsWithoutQuery = {...mockFlags, query: undefined}
134-
const result = await prepareExecuteContext(flagsWithoutQuery)
135-
136-
expect(readStdinString).toHaveBeenCalled()
137-
expect(result.query).toBe(stdinQuery)
138-
})
139-
140-
test('throws AbortError when no query provided via flag or stdin', async () => {
141-
vi.mocked(readStdinString).mockResolvedValue('')
142-
143-
const flagsWithoutQuery = {...mockFlags, query: undefined}
144-
145-
await expect(prepareExecuteContext(flagsWithoutQuery)).rejects.toThrow('No query provided')
146-
})
147-
148-
test('includes command name in error message', async () => {
149-
vi.mocked(readStdinString).mockResolvedValue('')
150-
126+
test('throws BugError when no query provided', async () => {
151127
const flagsWithoutQuery = {...mockFlags, query: undefined}
152128

153-
try {
154-
await prepareExecuteContext(flagsWithoutQuery, 'bulk execute')
155-
expect.fail('Should have thrown an error')
156-
// eslint-disable-next-line no-catch-all/no-catch-all
157-
} catch (error: any) {
158-
expect(error.message).toContain('No query provided')
159-
expect(error.tryMessage).toMatch(/shopify app bulk execute/)
160-
}
129+
await expect(prepareExecuteContext(flagsWithoutQuery)).rejects.toThrow('exactlyOne constraint')
161130
})
162131

163132
test('returns query, app context, and store', async () => {
@@ -188,7 +157,6 @@ describe('prepareExecuteContext', () => {
188157
expect(fileExists).toHaveBeenCalledWith('/path/to/query.graphql')
189158
expect(readFile).toHaveBeenCalledWith('/path/to/query.graphql', {encoding: 'utf8'})
190159
expect(result.query).toBe(queryFileContent)
191-
expect(readStdinString).not.toHaveBeenCalled()
192160
})
193161

194162
test('throws AbortError when query file does not exist', async () => {
@@ -200,17 +168,6 @@ describe('prepareExecuteContext', () => {
200168
expect(readFile).not.toHaveBeenCalled()
201169
})
202170

203-
test('falls back to stdin when neither query nor query-file provided', async () => {
204-
const stdinQuery = 'query { shop { name } }'
205-
vi.mocked(readStdinString).mockResolvedValue(stdinQuery)
206-
207-
const flagsWithoutQueryOrFile = {...mockFlags, query: undefined}
208-
const result = await prepareExecuteContext(flagsWithoutQueryOrFile)
209-
210-
expect(readStdinString).toHaveBeenCalled()
211-
expect(result.query).toBe(stdinQuery)
212-
})
213-
214171
test('validates GraphQL query using validateSingleOperation', async () => {
215172
await prepareExecuteContext(mockFlags)
216173

packages/app/src/cli/utilities/execute-command-helpers.ts

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@ import {linkedAppContext, LoadedAppContextOutput} from '../services/app-context.
22
import {storeContext} from '../services/store-context.js'
33
import {validateSingleOperation} from '../services/graphql/common.js'
44
import {OrganizationStore} from '../models/organization.js'
5-
import {readStdinString} from '@shopify/cli-kit/node/system'
6-
import {AbortError} from '@shopify/cli-kit/node/error'
5+
import {AbortError, BugError} from '@shopify/cli-kit/node/error'
76
import {readFile, fileExists} from '@shopify/cli-kit/node/fs'
87
import {outputContent, outputToken} from '@shopify/cli-kit/node/output'
98

@@ -55,16 +54,12 @@ export async function prepareAppStoreContext(flags: AppStoreContextFlags): Promi
5554

5655
/**
5756
* Prepares the execution context for GraphQL operations.
58-
* Handles query input from flag, file, or stdin, validates GraphQL syntax, and sets up app and store contexts.
57+
* Handles query input from flag or file, validates GraphQL syntax, and sets up app and store contexts.
5958
*
6059
* @param flags - Command flags containing configuration options.
61-
* @param commandName - Name of the command for error messages (e.g., 'execute', 'bulk execute').
6260
* @returns Context object containing query, app context, and store information.
6361
*/
64-
export async function prepareExecuteContext(
65-
flags: ExecuteCommandFlags,
66-
commandName = 'execute',
67-
): Promise<ExecuteContext> {
62+
export async function prepareExecuteContext(flags: ExecuteCommandFlags): Promise<ExecuteContext> {
6863
let query: string | undefined
6964

7065
if (flags.query) {
@@ -77,14 +72,11 @@ export async function prepareExecuteContext(
7772
)
7873
}
7974
query = await readFile(queryFile, {encoding: 'utf8'})
80-
} else {
81-
query = await readStdinString()
8275
}
8376

8477
if (!query) {
85-
throw new AbortError(
86-
'No query provided. Use the --query flag, --query-file flag, or pipe input via stdin.',
87-
`Example: shopify app ${commandName} --query-file query.graphql`,
78+
throw new BugError(
79+
'Query should have been provided via --query or --query-file flags due to exactlyOne constraint. This indicates the oclif flag validation failed.',
8880
)
8981
}
9082

packages/cli/oclif.manifest.json

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -242,11 +242,8 @@
242242
},
243243
"query": {
244244
"char": "q",
245-
"description": "The GraphQL query or mutation to run as a bulk operation. If omitted, reads from standard input.",
245+
"description": "The GraphQL query or mutation to run as a bulk operation.",
246246
"env": "SHOPIFY_FLAG_QUERY",
247-
"exclusive": [
248-
"query-file"
249-
],
250247
"hasDynamicHelp": false,
251248
"multiple": false,
252249
"name": "query",
@@ -256,9 +253,6 @@
256253
"query-file": {
257254
"description": "Path to a file containing the GraphQL query or mutation. Can't be used with --query.",
258255
"env": "SHOPIFY_FLAG_QUERY_FILE",
259-
"exclusive": [
260-
"query"
261-
],
262256
"hasDynamicHelp": false,
263257
"multiple": false,
264258
"name": "query-file",
@@ -1296,9 +1290,6 @@
12961290
"char": "q",
12971291
"description": "The GraphQL query or mutation, as a string.",
12981292
"env": "SHOPIFY_FLAG_QUERY",
1299-
"exclusive": [
1300-
"query-file"
1301-
],
13021293
"hasDynamicHelp": false,
13031294
"multiple": false,
13041295
"name": "query",
@@ -1308,9 +1299,6 @@
13081299
"query-file": {
13091300
"description": "Path to a file containing the GraphQL query or mutation. Can't be used with --query.",
13101301
"env": "SHOPIFY_FLAG_QUERY_FILE",
1311-
"exclusive": [
1312-
"query"
1313-
],
13141302
"hasDynamicHelp": false,
13151303
"multiple": false,
13161304
"name": "query-file",

0 commit comments

Comments
 (0)