Skip to content

Commit 45fe1ee

Browse files
authored
Merge pull request #6588 from Shopify/11-07-implement_simple_bulk_query
Initial Setup: Bulk Operations Infrastructure for Shopify CLI
2 parents a659a26 + 3784bae commit 45fe1ee

File tree

13 files changed

+630
-21
lines changed

13 files changed

+630
-21
lines changed

bin/get-graphql-schemas.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,13 @@ const schemas = [
7171
pathToFile: 'areas/core/shopify/db/graphql/functions_cli_api_schema_unstable_public.graphql',
7272
localPath: './packages/app/src/cli/api/graphql/functions/functions_cli_schema.graphql',
7373
},
74+
{
75+
owner: 'shop',
76+
repo: 'world',
77+
pathToFile: 'areas/core/shopify/db/graphql/admin_schema_unstable_public.graphql',
78+
localPath: './packages/app/src/cli/api/graphql/bulk-operations/admin_schema.graphql',
79+
usesLfs: true,
80+
},
7481
]
7582

7683

graphql.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ export default {
8181
appDev: projectFactory('app-dev', 'app_dev_schema.graphql'),
8282
appManagement: projectFactory('app-management', 'app_management_schema.graphql'),
8383
admin: projectFactory('admin', 'admin_schema.graphql', 'cli-kit'),
84+
bulkOperations: projectFactory('bulk-operations', 'admin_schema.graphql'),
8485
webhooks: projectFactory('webhooks', 'webhooks_schema.graphql'),
8586
functions: projectFactory('functions', 'functions_cli_schema.graphql', 'app'),
8687
},

packages/app/project.json

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,8 @@
5858
"{projectRoot}/src/cli/api/graphql/app-dev/generated/**/*.ts",
5959
"{projectRoot}/src/cli/api/graphql/app-management/generated/**/*.ts",
6060
"{projectRoot}/src/cli/api/graphql/webhooks/generated/**/*.ts",
61-
"{projectRoot}/src/cli/api/graphql/functions/generated/**/*.ts"
61+
"{projectRoot}/src/cli/api/graphql/functions/generated/**/*.ts",
62+
"{projectRoot}/src/cli/api/graphql/bulk-operations/generated/**/*.ts"
6263
],
6364
"options": {
6465
"commands": [
@@ -68,7 +69,8 @@
6869
"pnpm eslint 'src/cli/api/graphql/app-dev/generated/**/*.{ts,tsx}' --fix",
6970
"pnpm eslint 'src/cli/api/graphql/app-management/generated/**/*.{ts,tsx}' --fix",
7071
"pnpm eslint 'src/cli/api/graphql/webhooks/generated/**/*.{ts,tsx}' --fix",
71-
"pnpm eslint 'src/cli/api/graphql/functions/generated/**/*.{ts,tsx}' --fix"
72+
"pnpm eslint 'src/cli/api/graphql/functions/generated/**/*.{ts,tsx}' --fix",
73+
"pnpm eslint 'src/cli/api/graphql/bulk-operations/generated/**/*.{ts,tsx}' --fix"
7274
],
7375
"cwd": "packages/app"
7476
}
@@ -150,6 +152,17 @@
150152
"cwd": "{workspaceRoot}"
151153
}
152154
},
155+
"graphql-codegen:generate:bulk-operations": {
156+
"executor": "nx:run-commands",
157+
"inputs": ["{workspaceRoot}/graphql.config.ts", "{projectRoot}/src/cli/api/graphql/bulk-operations/**/*.graphql"],
158+
"outputs": ["{projectRoot}/src/cli/api/graphql/bulk-operations/generated/**/*.ts"],
159+
"options": {
160+
"commands": [
161+
"pnpm exec graphql-codegen --project=bulkOperations"
162+
],
163+
"cwd": "{workspaceRoot}"
164+
}
165+
},
153166
"graphql-codegen:postfix": {
154167
"executor": "nx:run-commands",
155168
"dependsOn": [
@@ -159,7 +172,8 @@
159172
"graphql-codegen:generate:app-dev",
160173
"graphql-codegen:generate:app-management",
161174
"graphql-codegen:generate:webhooks",
162-
"graphql-codegen:generate:functions"
175+
"graphql-codegen:generate:functions",
176+
"graphql-codegen:generate:bulk-operations"
163177
],
164178
"inputs": [{ "dependentTasksOutputFiles": "**/*.ts" }],
165179
"outputs": [
@@ -169,7 +183,8 @@
169183
"{projectRoot}/src/cli/api/graphql/app-dev/generated/**/*.ts",
170184
"{projectRoot}/src/cli/api/graphql/app-management/generated/**/*.ts",
171185
"{projectRoot}/src/cli/api/graphql/webhooks/generated/**/*.ts",
172-
"{projectRoot}/src/cli/api/graphql/functions/generated/**/*.ts"
186+
"{projectRoot}/src/cli/api/graphql/functions/generated/**/*.ts",
187+
"{projectRoot}/src/cli/api/graphql/bulk-operations/generated/**/*.ts"
173188
],
174189
"options": {
175190
"commands": [
@@ -179,11 +194,11 @@
179194
"find ./packages/app/src/cli/api/graphql/app-dev/generated/ -type f -name '*.ts' -exec sh -c 'sed -i \"\" \"s|import \\* as Types from '\\''./types'\\'';|import \\* as Types from '\\''./types.js'\\'';|g; s|export const \\([A-Za-z0-9_]*\\)Document =|export const \\1 =|g\" \"$0\"' {} \\;",
180195
"find ./packages/app/src/cli/api/graphql/app-management/generated/ -type f -name '*.ts' -exec sh -c 'sed -i \"\" \"s|import \\* as Types from '\\''./types'\\'';|import \\* as Types from '\\''./types.js'\\'';|g; s|export const \\([A-Za-z0-9_]*\\)Document =|export const \\1 =|g\" \"$0\"' {} \\;",
181196
"find ./packages/app/src/cli/api/graphql/webhooks/generated/ -type f -name '*.ts' -exec sh -c 'sed -i \"\" \"s|import \\* as Types from '\\''./types'\\'';|import \\* as Types from '\\''./types.js'\\'';|g; s|export const \\([A-Za-z0-9_]*\\)Document =|export const \\1 =|g\" \"$0\"' {} \\;",
182-
"find ./packages/app/src/cli/api/graphql/functions/generated/ -type f -name '*.ts' -exec sh -c 'sed -i \"\" \"s|import \\* as Types from '\\''./types'\\'';|import \\* as Types from '\\''./types.js'\\'';|g; s|export const \\([A-Za-z0-9_]*\\)Document =|export const \\1 =|g\" \"$0\"' {} \\;"
197+
"find ./packages/app/src/cli/api/graphql/functions/generated/ -type f -name '*.ts' -exec sh -c 'sed -i \"\" \"s|import \\* as Types from '\\''./types'\\'';|import \\* as Types from '\\''./types.js'\\'';|g; s|export const \\([A-Za-z0-9_]*\\)Document =|export const \\1 =|g\" \"$0\"' {} \\;",
198+
"find ./packages/app/src/cli/api/graphql/bulk-operations/generated/ -type f -name '*.ts' -exec sh -c 'sed -i \"\" \"s|import \\* as Types from '\\''./types'\\'';|import \\* as Types from '\\''./types.js'\\'';|g; s|export const \\([A-Za-z0-9_]*\\)Document =|export const \\1 =|g\" \"$0\"' {} \\;"
183199
],
184200
"cwd": "{workspaceRoot}"
185201
}
186202
}
187203
}
188204
}
189-
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
/* eslint-disable @typescript-eslint/consistent-type-definitions, @typescript-eslint/no-redundant-type-constituents */
2+
import * as Types from './types.js'
3+
4+
import {TypedDocumentNode as DocumentNode} from '@graphql-typed-document-node/core'
5+
6+
export type BulkOperationRunQueryMutationVariables = Types.Exact<{
7+
query: Types.Scalars['String']['input']
8+
}>
9+
10+
export type BulkOperationRunQueryMutation = {
11+
bulkOperationRunQuery?: {
12+
bulkOperation?: {
13+
completedAt?: unknown | null
14+
createdAt: unknown
15+
errorCode?: Types.BulkOperationErrorCode | null
16+
fileSize?: unknown | null
17+
id: string
18+
objectCount: unknown
19+
partialDataUrl?: string | null
20+
query: string
21+
rootObjectCount: unknown
22+
status: Types.BulkOperationStatus
23+
type: Types.BulkOperationType
24+
url?: string | null
25+
} | null
26+
userErrors: {code?: Types.BulkOperationUserErrorCode | null; field?: string[] | null; message: string}[]
27+
} | null
28+
}
29+
30+
export const BulkOperationRunQuery = {
31+
kind: 'Document',
32+
definitions: [
33+
{
34+
kind: 'OperationDefinition',
35+
operation: 'mutation',
36+
name: {kind: 'Name', value: 'BulkOperationRunQuery'},
37+
variableDefinitions: [
38+
{
39+
kind: 'VariableDefinition',
40+
variable: {kind: 'Variable', name: {kind: 'Name', value: 'query'}},
41+
type: {kind: 'NonNullType', type: {kind: 'NamedType', name: {kind: 'Name', value: 'String'}}},
42+
},
43+
],
44+
selectionSet: {
45+
kind: 'SelectionSet',
46+
selections: [
47+
{
48+
kind: 'Field',
49+
name: {kind: 'Name', value: 'bulkOperationRunQuery'},
50+
arguments: [
51+
{
52+
kind: 'Argument',
53+
name: {kind: 'Name', value: 'query'},
54+
value: {kind: 'Variable', name: {kind: 'Name', value: 'query'}},
55+
},
56+
{
57+
kind: 'Argument',
58+
name: {kind: 'Name', value: 'groupObjects'},
59+
value: {kind: 'BooleanValue', value: false},
60+
},
61+
],
62+
selectionSet: {
63+
kind: 'SelectionSet',
64+
selections: [
65+
{
66+
kind: 'Field',
67+
name: {kind: 'Name', value: 'bulkOperation'},
68+
selectionSet: {
69+
kind: 'SelectionSet',
70+
selections: [
71+
{kind: 'Field', name: {kind: 'Name', value: 'completedAt'}},
72+
{kind: 'Field', name: {kind: 'Name', value: 'createdAt'}},
73+
{kind: 'Field', name: {kind: 'Name', value: 'errorCode'}},
74+
{kind: 'Field', name: {kind: 'Name', value: 'fileSize'}},
75+
{kind: 'Field', name: {kind: 'Name', value: 'id'}},
76+
{kind: 'Field', name: {kind: 'Name', value: 'objectCount'}},
77+
{kind: 'Field', name: {kind: 'Name', value: 'partialDataUrl'}},
78+
{kind: 'Field', name: {kind: 'Name', value: 'query'}},
79+
{kind: 'Field', name: {kind: 'Name', value: 'rootObjectCount'}},
80+
{kind: 'Field', name: {kind: 'Name', value: 'status'}},
81+
{kind: 'Field', name: {kind: 'Name', value: 'type'}},
82+
{kind: 'Field', name: {kind: 'Name', value: 'url'}},
83+
{kind: 'Field', name: {kind: 'Name', value: '__typename'}},
84+
],
85+
},
86+
},
87+
{
88+
kind: 'Field',
89+
name: {kind: 'Name', value: 'userErrors'},
90+
selectionSet: {
91+
kind: 'SelectionSet',
92+
selections: [
93+
{kind: 'Field', name: {kind: 'Name', value: 'code'}},
94+
{kind: 'Field', name: {kind: 'Name', value: 'field'}},
95+
{kind: 'Field', name: {kind: 'Name', value: 'message'}},
96+
{kind: 'Field', name: {kind: 'Name', value: '__typename'}},
97+
],
98+
},
99+
},
100+
{kind: 'Field', name: {kind: 'Name', value: '__typename'}},
101+
],
102+
},
103+
},
104+
],
105+
},
106+
},
107+
],
108+
} as unknown as DocumentNode<BulkOperationRunQueryMutation, BulkOperationRunQueryMutationVariables>
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
/* eslint-disable @typescript-eslint/consistent-type-definitions, @typescript-eslint/naming-convention, @typescript-eslint/no-explicit-any, tsdoc/syntax */
2+
import {JsonMapType} from '@shopify/cli-kit/node/toml'
3+
4+
export type Maybe<T> = T | null
5+
export type InputMaybe<T> = Maybe<T>
6+
export type Exact<T extends {[key: string]: unknown}> = {[K in keyof T]: T[K]}
7+
export type MakeOptional<T, K extends keyof T> = Omit<T, K> & {[SubKey in K]?: Maybe<T[SubKey]>}
8+
export type MakeMaybe<T, K extends keyof T> = Omit<T, K> & {[SubKey in K]: Maybe<T[SubKey]>}
9+
export type MakeEmpty<T extends {[key: string]: unknown}, K extends keyof T> = {[_ in K]?: never}
10+
export type Incremental<T> = T | {[P in keyof T]?: P extends ' $fragmentName' | '__typename' ? T[P] : never}
11+
/** All built-in and custom scalars, mapped to their actual values */
12+
export type Scalars = {
13+
ID: {input: string; output: string}
14+
String: {input: string; output: string}
15+
Boolean: {input: boolean; output: boolean}
16+
Int: {input: number; output: number}
17+
Float: {input: number; output: number}
18+
/**
19+
* An Amazon Web Services Amazon Resource Name (ARN), including the Region and account ID.
20+
* For more information, refer to [Amazon Resource Names](https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html).
21+
*/
22+
ARN: {input: any; output: any}
23+
/**
24+
* Represents non-fractional signed whole numeric values. Since the value may
25+
* exceed the size of a 32-bit integer, it's encoded as a string.
26+
*/
27+
BigInt: {input: any; output: any}
28+
/**
29+
* A string containing a hexadecimal representation of a color.
30+
*
31+
* For example, "#6A8D48".
32+
*/
33+
Color: {input: any; output: any}
34+
/**
35+
* Represents an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601)-encoded date string.
36+
* For example, September 7, 2019 is represented as `"2019-07-16"`.
37+
*/
38+
Date: {input: any; output: any}
39+
/**
40+
* Represents an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601)-encoded date and time string.
41+
* For example, 3:50 pm on September 7, 2019 in the time zone of UTC (Coordinated Universal Time) is
42+
* represented as `"2019-09-07T15:50:00Z`".
43+
*/
44+
DateTime: {input: any; output: any}
45+
/**
46+
* A signed decimal number, which supports arbitrary precision and is serialized as a string.
47+
*
48+
* Example values: `"29.99"`, `"29.999"`.
49+
*/
50+
Decimal: {input: any; output: any}
51+
/**
52+
* A string containing a strict subset of HTML code. Non-allowed tags will be stripped out.
53+
* Allowed tags:
54+
* * `a` (allowed attributes: `href`, `target`)
55+
* * `b`
56+
* * `br`
57+
* * `em`
58+
* * `i`
59+
* * `strong`
60+
* * `u`
61+
* Use [HTML](https://shopify.dev/api/admin-graphql/latest/scalars/HTML) instead if you need to
62+
* include other HTML tags.
63+
*
64+
* Example value: `"Your current domain is <strong>example.myshopify.com</strong>."`
65+
*/
66+
FormattedString: {input: any; output: any}
67+
/**
68+
* A string containing HTML code. Refer to the [HTML spec](https://html.spec.whatwg.org/#elements-3) for a
69+
* complete list of HTML elements.
70+
*
71+
* Example value: `"<p>Grey cotton knit sweater.</p>"`
72+
*/
73+
HTML: {input: any; output: any}
74+
/**
75+
* A [JSON](https://www.json.org/json-en.html) object.
76+
*
77+
* Example value:
78+
* `{
79+
* "product": {
80+
* "id": "gid://shopify/Product/1346443542550",
81+
* "title": "White T-shirt",
82+
* "options": [{
83+
* "name": "Size",
84+
* "values": ["M", "L"]
85+
* }]
86+
* }
87+
* }`
88+
*/
89+
JSON: {input: JsonMapType | string; output: JsonMapType}
90+
/** A monetary value string without a currency symbol or code. Example value: `"100.57"`. */
91+
Money: {input: any; output: any}
92+
/** A scalar value. */
93+
Scalar: {input: any; output: any}
94+
/**
95+
* Represents a unique identifier in the Storefront API. A `StorefrontID` value can
96+
* be used wherever an ID is expected in the Storefront API.
97+
*
98+
* Example value: `"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzEwMDc5Nzg1MTAw"`.
99+
*/
100+
StorefrontID: {input: any; output: any}
101+
/**
102+
* Represents an [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986) and
103+
* [RFC 3987](https://datatracker.ietf.org/doc/html/rfc3987)-compliant URI string.
104+
*
105+
* For example, `"https://example.myshopify.com"` is a valid URL. It includes a scheme (`https`) and a host
106+
* (`example.myshopify.com`).
107+
*/
108+
URL: {input: string; output: string}
109+
/**
110+
* An unsigned 64-bit integer. Represents whole numeric values between 0 and 2^64 - 1 encoded as a string of base-10 digits.
111+
*
112+
* Example value: `"50"`.
113+
*/
114+
UnsignedInt64: {input: any; output: any}
115+
/**
116+
* Time between UTC time and a location's observed time, in the format `"+HH:MM"` or `"-HH:MM"`.
117+
*
118+
* Example value: `"-07:00"`.
119+
*/
120+
UtcOffset: {input: any; output: any}
121+
}
122+
123+
/** Error codes for failed bulk operations. */
124+
export type BulkOperationErrorCode =
125+
/**
126+
* The provided operation `query` returned access denied due to missing
127+
* [access scopes](https://shopify.dev/api/usage/access-scopes).
128+
* Review the requested object permissions and execute the query as a normal non-bulk GraphQL request to see more details.
129+
*/
130+
| 'ACCESS_DENIED'
131+
/**
132+
* The operation resulted in partial or incomplete data due to internal server errors during execution.
133+
* These errors might be intermittent, so you can try performing the same query again.
134+
*/
135+
| 'INTERNAL_SERVER_ERROR'
136+
/**
137+
* The operation resulted in partial or incomplete data due to query timeouts during execution.
138+
* In some cases, timeouts can be avoided by modifying your `query` to select fewer fields.
139+
*/
140+
| 'TIMEOUT'
141+
142+
/** The valid values for the status of a bulk operation. */
143+
export type BulkOperationStatus =
144+
/** The bulk operation has been canceled. */
145+
| 'CANCELED'
146+
/**
147+
* Cancelation has been initiated on the bulk operation. There may be a short delay from when a cancelation
148+
* starts until the operation is actually canceled.
149+
*/
150+
| 'CANCELING'
151+
/** The bulk operation has successfully completed. */
152+
| 'COMPLETED'
153+
/** The bulk operation has been created. */
154+
| 'CREATED'
155+
/** The bulk operation URL has expired. */
156+
| 'EXPIRED'
157+
/**
158+
* The bulk operation has failed. For information on why the operation failed, use
159+
* [BulkOperation.errorCode](https://shopify.dev/api/admin-graphql/latest/enums/bulkoperationerrorcode).
160+
*/
161+
| 'FAILED'
162+
/** The bulk operation is runnning. */
163+
| 'RUNNING'
164+
165+
/** The valid values for the bulk operation's type. */
166+
export type BulkOperationType =
167+
/** The bulk operation is a mutation. */
168+
| 'MUTATION'
169+
/** The bulk operation is a query. */
170+
| 'QUERY'
171+
172+
/** Possible error codes that can be returned by `BulkOperationUserError`. */
173+
export type BulkOperationUserErrorCode =
174+
/** The input value is invalid. */
175+
| 'INVALID'
176+
/** Bulk operations limit reached. Please try again later. */
177+
| 'LIMIT_REACHED'
178+
/** A bulk operation is already in progress. */
179+
| 'OPERATION_IN_PROGRESS'

0 commit comments

Comments
 (0)