Skip to content

Commit 23c136c

Browse files
Merge pull request #6686 from Shopify/shopify_app_execute
Add `app execute` command for GraphQL operations
2 parents 22deb5f + 24bff60 commit 23c136c

24 files changed

+1313
-156
lines changed

.changeset/pretty-brooms-heal.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@shopify/app': minor
3+
---
4+
5+
Added `shopify app execute` for convenient execution of queries and mutations against the Admin API.
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// This is an autogenerated file. Don't edit this file manually.
2+
import {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'
3+
4+
const data: ReferenceEntityTemplateSchema = {
5+
name: 'app execute',
6+
description: `Executes an Admin API GraphQL query or mutation on the specified dev store.`,
7+
overviewPreviewDescription: `Execute GraphQL queries and mutations.`,
8+
type: 'command',
9+
isVisualComponent: false,
10+
defaultExample: {
11+
codeblock: {
12+
tabs: [
13+
{
14+
title: 'app execute',
15+
code: './examples/app-execute.example.sh',
16+
language: 'bash',
17+
},
18+
],
19+
title: 'app execute',
20+
},
21+
},
22+
definitions: [
23+
{
24+
title: 'Flags',
25+
description: 'The following flags are available for the `app execute` command:',
26+
type: 'appexecute',
27+
},
28+
],
29+
category: 'app',
30+
related: [
31+
],
32+
}
33+
34+
export default data
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
shopify app execute [flags]
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// This is an autogenerated file. Don't edit this file manually.
2+
export interface appexecute {
3+
/**
4+
* The Client ID of your app.
5+
* @environment SHOPIFY_FLAG_CLIENT_ID
6+
*/
7+
'--client-id <value>'?: string
8+
9+
/**
10+
* The name of the app configuration.
11+
* @environment SHOPIFY_FLAG_APP_CONFIG
12+
*/
13+
'-c, --config <value>'?: string
14+
15+
/**
16+
* Disable color output.
17+
* @environment SHOPIFY_FLAG_NO_COLOR
18+
*/
19+
'--no-color'?: ''
20+
21+
/**
22+
* The file name where results should be written, instead of STDOUT.
23+
* @environment SHOPIFY_FLAG_OUTPUT_FILE
24+
*/
25+
'--output-file <value>'?: string
26+
27+
/**
28+
* The path to your app directory.
29+
* @environment SHOPIFY_FLAG_PATH
30+
*/
31+
'--path <value>'?: string
32+
33+
/**
34+
* The GraphQL query or mutation, as a string.
35+
* @environment SHOPIFY_FLAG_QUERY
36+
*/
37+
'-q, --query <value>'?: string
38+
39+
/**
40+
* Reset all your settings.
41+
* @environment SHOPIFY_FLAG_RESET
42+
*/
43+
'--reset'?: ''
44+
45+
/**
46+
* The myshopify.com domain of the store to execute against. The app must be installed on the store. If not specified, you will be prompted to select a store.
47+
* @environment SHOPIFY_FLAG_STORE
48+
*/
49+
'-s, --store <value>'?: string
50+
51+
/**
52+
* The values for any GraphQL variables in your query or mutation, in JSON format.
53+
* @environment SHOPIFY_FLAG_VARIABLES
54+
*/
55+
'-v, --variables <value>'?: string
56+
57+
/**
58+
* Increase the verbosity of the output.
59+
* @environment SHOPIFY_FLAG_VERBOSE
60+
*/
61+
'--verbose'?: ''
62+
63+
/**
64+
* The API version to use for the query or mutation. Defaults to the latest stable version.
65+
* @environment SHOPIFY_FLAG_VERSION
66+
*/
67+
'--version <value>'?: string
68+
}

docs-shopify.dev/generated/generated_docs_data.json

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -998,6 +998,143 @@
998998
"category": "app",
999999
"related": []
10001000
},
1001+
{
1002+
"name": "app execute",
1003+
"description": "Executes an Admin API GraphQL query or mutation on the specified dev store.",
1004+
"overviewPreviewDescription": "Execute GraphQL queries and mutations.",
1005+
"type": "command",
1006+
"isVisualComponent": false,
1007+
"defaultExample": {
1008+
"codeblock": {
1009+
"tabs": [
1010+
{
1011+
"title": "app execute",
1012+
"code": "shopify app execute [flags]",
1013+
"language": "bash"
1014+
}
1015+
],
1016+
"title": "app execute"
1017+
}
1018+
},
1019+
"definitions": [
1020+
{
1021+
"title": "Flags",
1022+
"description": "The following flags are available for the `app execute` command:",
1023+
"type": "appexecute",
1024+
"typeDefinitions": {
1025+
"appexecute": {
1026+
"filePath": "docs-shopify.dev/commands/interfaces/app-execute.interface.ts",
1027+
"name": "appexecute",
1028+
"description": "",
1029+
"members": [
1030+
{
1031+
"filePath": "docs-shopify.dev/commands/interfaces/app-execute.interface.ts",
1032+
"syntaxKind": "PropertySignature",
1033+
"name": "--client-id <value>",
1034+
"value": "string",
1035+
"description": "The Client ID of your app.",
1036+
"isOptional": true,
1037+
"environmentValue": "SHOPIFY_FLAG_CLIENT_ID"
1038+
},
1039+
{
1040+
"filePath": "docs-shopify.dev/commands/interfaces/app-execute.interface.ts",
1041+
"syntaxKind": "PropertySignature",
1042+
"name": "--no-color",
1043+
"value": "\"\"",
1044+
"description": "Disable color output.",
1045+
"isOptional": true,
1046+
"environmentValue": "SHOPIFY_FLAG_NO_COLOR"
1047+
},
1048+
{
1049+
"filePath": "docs-shopify.dev/commands/interfaces/app-execute.interface.ts",
1050+
"syntaxKind": "PropertySignature",
1051+
"name": "--output-file <value>",
1052+
"value": "string",
1053+
"description": "The file name where results should be written, instead of STDOUT.",
1054+
"isOptional": true,
1055+
"environmentValue": "SHOPIFY_FLAG_OUTPUT_FILE"
1056+
},
1057+
{
1058+
"filePath": "docs-shopify.dev/commands/interfaces/app-execute.interface.ts",
1059+
"syntaxKind": "PropertySignature",
1060+
"name": "--path <value>",
1061+
"value": "string",
1062+
"description": "The path to your app directory.",
1063+
"isOptional": true,
1064+
"environmentValue": "SHOPIFY_FLAG_PATH"
1065+
},
1066+
{
1067+
"filePath": "docs-shopify.dev/commands/interfaces/app-execute.interface.ts",
1068+
"syntaxKind": "PropertySignature",
1069+
"name": "--reset",
1070+
"value": "\"\"",
1071+
"description": "Reset all your settings.",
1072+
"isOptional": true,
1073+
"environmentValue": "SHOPIFY_FLAG_RESET"
1074+
},
1075+
{
1076+
"filePath": "docs-shopify.dev/commands/interfaces/app-execute.interface.ts",
1077+
"syntaxKind": "PropertySignature",
1078+
"name": "--verbose",
1079+
"value": "\"\"",
1080+
"description": "Increase the verbosity of the output.",
1081+
"isOptional": true,
1082+
"environmentValue": "SHOPIFY_FLAG_VERBOSE"
1083+
},
1084+
{
1085+
"filePath": "docs-shopify.dev/commands/interfaces/app-execute.interface.ts",
1086+
"syntaxKind": "PropertySignature",
1087+
"name": "--version <value>",
1088+
"value": "string",
1089+
"description": "The API version to use for the query or mutation. Defaults to the latest stable version.",
1090+
"isOptional": true,
1091+
"environmentValue": "SHOPIFY_FLAG_VERSION"
1092+
},
1093+
{
1094+
"filePath": "docs-shopify.dev/commands/interfaces/app-execute.interface.ts",
1095+
"syntaxKind": "PropertySignature",
1096+
"name": "-c, --config <value>",
1097+
"value": "string",
1098+
"description": "The name of the app configuration.",
1099+
"isOptional": true,
1100+
"environmentValue": "SHOPIFY_FLAG_APP_CONFIG"
1101+
},
1102+
{
1103+
"filePath": "docs-shopify.dev/commands/interfaces/app-execute.interface.ts",
1104+
"syntaxKind": "PropertySignature",
1105+
"name": "-q, --query <value>",
1106+
"value": "string",
1107+
"description": "The GraphQL query or mutation, as a string.",
1108+
"isOptional": true,
1109+
"environmentValue": "SHOPIFY_FLAG_QUERY"
1110+
},
1111+
{
1112+
"filePath": "docs-shopify.dev/commands/interfaces/app-execute.interface.ts",
1113+
"syntaxKind": "PropertySignature",
1114+
"name": "-s, --store <value>",
1115+
"value": "string",
1116+
"description": "The myshopify.com domain of the store to execute against. The app must be installed on the store. If not specified, you will be prompted to select a store.",
1117+
"isOptional": true,
1118+
"environmentValue": "SHOPIFY_FLAG_STORE"
1119+
},
1120+
{
1121+
"filePath": "docs-shopify.dev/commands/interfaces/app-execute.interface.ts",
1122+
"syntaxKind": "PropertySignature",
1123+
"name": "-v, --variables <value>",
1124+
"value": "string",
1125+
"description": "The values for any GraphQL variables in your query or mutation, in JSON format.",
1126+
"isOptional": true,
1127+
"environmentValue": "SHOPIFY_FLAG_VARIABLES"
1128+
}
1129+
],
1130+
"value": "export interface appexecute {\n /**\n * The Client ID of your app.\n * @environment SHOPIFY_FLAG_CLIENT_ID\n */\n '--client-id <value>'?: string\n\n /**\n * The name of the app configuration.\n * @environment SHOPIFY_FLAG_APP_CONFIG\n */\n '-c, --config <value>'?: string\n\n /**\n * Disable color output.\n * @environment SHOPIFY_FLAG_NO_COLOR\n */\n '--no-color'?: ''\n\n /**\n * The file name where results should be written, instead of STDOUT.\n * @environment SHOPIFY_FLAG_OUTPUT_FILE\n */\n '--output-file <value>'?: string\n\n /**\n * The path to your app directory.\n * @environment SHOPIFY_FLAG_PATH\n */\n '--path <value>'?: string\n\n /**\n * The GraphQL query or mutation, as a string.\n * @environment SHOPIFY_FLAG_QUERY\n */\n '-q, --query <value>'?: string\n\n /**\n * Reset all your settings.\n * @environment SHOPIFY_FLAG_RESET\n */\n '--reset'?: ''\n\n /**\n * The myshopify.com domain of the store to execute against. The app must be installed on the store. If not specified, you will be prompted to select a store.\n * @environment SHOPIFY_FLAG_STORE\n */\n '-s, --store <value>'?: string\n\n /**\n * The values for any GraphQL variables in your query or mutation, in JSON format.\n * @environment SHOPIFY_FLAG_VARIABLES\n */\n '-v, --variables <value>'?: string\n\n /**\n * Increase the verbosity of the output.\n * @environment SHOPIFY_FLAG_VERBOSE\n */\n '--verbose'?: ''\n\n /**\n * The API version to use for the query or mutation. Defaults to the latest stable version.\n * @environment SHOPIFY_FLAG_VERSION\n */\n '--version <value>'?: string\n}"
1131+
}
1132+
}
1133+
}
1134+
],
1135+
"category": "app",
1136+
"related": []
1137+
},
10011138
{
10021139
"name": "app function build",
10031140
"description": "Compiles the function in your current directory to WebAssembly (Wasm) for testing purposes.",

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

Lines changed: 4 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,14 @@
11
import {appFlags, bulkOperationFlags} from '../../../flags.js'
22
import AppLinkedCommand, {AppLinkedCommandOutput} from '../../../utilities/app-linked-command.js'
3-
import {linkedAppContext} from '../../../services/app-context.js'
4-
import {storeContext} from '../../../services/store-context.js'
53
import {executeBulkOperation} from '../../../services/bulk-operations/execute-bulk-operation.js'
4+
import {prepareExecuteContext} from '../../../utilities/execute-command-helpers.js'
65
import {globalFlags} from '@shopify/cli-kit/node/cli'
7-
import {readStdinString} from '@shopify/cli-kit/node/system'
8-
import {AbortError} from '@shopify/cli-kit/node/error'
96

107
export default class BulkExecute extends AppLinkedCommand {
118
static summary = 'Execute bulk operations.'
129

13-
static description = 'Execute bulk operations against the Shopify Admin API.'
10+
static description =
11+
'Executes an Admin API GraphQL query or mutation on the specified dev store, as a bulk operation.'
1412

1513
static hidden = true
1614

@@ -23,26 +21,7 @@ export default class BulkExecute extends AppLinkedCommand {
2321
async run(): Promise<AppLinkedCommandOutput> {
2422
const {flags} = await this.parse(BulkExecute)
2523

26-
const query = flags.query ?? (await readStdinString())
27-
if (!query) {
28-
throw new AbortError(
29-
'No query provided. Use the --query flag or pipe input via stdin.',
30-
'Example: echo "query { ... }" | shopify app bulk execute',
31-
)
32-
}
33-
34-
const appContextResult = await linkedAppContext({
35-
directory: flags.path,
36-
clientId: flags['client-id'],
37-
forceRelink: flags.reset,
38-
userProvidedConfigName: flags.config,
39-
})
40-
41-
const store = await storeContext({
42-
appContextResult,
43-
storeFqdn: flags.store,
44-
forceReselectStore: flags.reset,
45-
})
24+
const {query, appContextResult, store} = await prepareExecuteContext(flags, 'bulk execute')
4625

4726
await executeBulkOperation({
4827
remoteApp: appContextResult.remoteApp,

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

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import {appFlags} from '../../../flags.js'
22
import AppLinkedCommand, {AppLinkedCommandOutput} from '../../../utilities/app-linked-command.js'
3-
import {linkedAppContext} from '../../../services/app-context.js'
4-
import {storeContext} from '../../../services/store-context.js'
3+
import {prepareAppStoreContext} from '../../../utilities/execute-command-helpers.js'
54
import {getBulkOperationStatus, listBulkOperations} from '../../../services/bulk-operations/bulk-operation-status.js'
65
import {Flags} from '@oclif/core'
76
import {globalFlags} from '@shopify/cli-kit/node/cli'
@@ -33,18 +32,7 @@ export default class BulkStatus extends AppLinkedCommand {
3332
async run(): Promise<AppLinkedCommandOutput> {
3433
const {flags} = await this.parse(BulkStatus)
3534

36-
const appContextResult = await linkedAppContext({
37-
directory: flags.path,
38-
clientId: flags['client-id'],
39-
forceRelink: flags.reset,
40-
userProvidedConfigName: flags.config,
41-
})
42-
43-
const store = await storeContext({
44-
appContextResult,
45-
storeFqdn: flags.store,
46-
forceReselectStore: flags.reset,
47-
})
35+
const {appContextResult, store} = await prepareAppStoreContext(flags)
4836

4937
if (flags.id) {
5038
await getBulkOperationStatus({
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import {appFlags, operationFlags} from '../../flags.js'
2+
import AppLinkedCommand, {AppLinkedCommandOutput} from '../../utilities/app-linked-command.js'
3+
import {executeOperation} from '../../services/execute-operation.js'
4+
import {prepareExecuteContext} from '../../utilities/execute-command-helpers.js'
5+
import {globalFlags} from '@shopify/cli-kit/node/cli'
6+
7+
export default class Execute extends AppLinkedCommand {
8+
static summary = 'Execute GraphQL queries and mutations.'
9+
10+
static description = 'Executes an Admin API GraphQL query or mutation on the specified dev store.'
11+
12+
static flags = {
13+
...globalFlags,
14+
...appFlags,
15+
...operationFlags,
16+
}
17+
18+
async run(): Promise<AppLinkedCommandOutput> {
19+
const {flags} = await this.parse(Execute)
20+
21+
const {query, appContextResult, store} = await prepareExecuteContext(flags, 'execute')
22+
23+
await executeOperation({
24+
remoteApp: appContextResult.remoteApp,
25+
storeFqdn: store.shopDomain,
26+
query,
27+
variables: flags.variables,
28+
outputFile: flags['output-file'],
29+
...(flags.version && {version: flags.version}),
30+
})
31+
32+
return {app: appContextResult.app}
33+
}
34+
}

0 commit comments

Comments
 (0)