Skip to content

Commit 12bbc72

Browse files
use our new watchBulkOperation rendering when --watch is provided
1 parent aeb490e commit 12bbc72

File tree

5 files changed

+88
-18
lines changed

5 files changed

+88
-18
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ export default class Execute extends AppLinkedCommand {
3939
storeFqdn: store.shopDomain,
4040
query: flags.query,
4141
variables: flags.variables,
42+
watch: flags.watch,
4243
})
4344

4445
return {app: appContextResult.app}

packages/app/src/cli/flags.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,9 @@ export const bulkOperationFlags = {
5555
env: 'SHOPIFY_FLAG_STORE',
5656
parse: async (input) => normalizeStoreFqdn(input),
5757
}),
58+
watch: Flags.boolean({
59+
description: 'Wait for bulk operation results before exiting.',
60+
env: 'SHOPIFY_FLAG_WATCH',
61+
default: false,
62+
}),
5863
}

packages/app/src/cli/services/bulk-operations/execute-bulk-operation.test.ts

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
import {executeBulkOperation} from './execute-bulk-operation.js'
22
import {runBulkOperationQuery} from './run-query.js'
33
import {runBulkOperationMutation} from './run-mutation.js'
4+
import {watchBulkOperation} from './watch-bulk-operation.js'
45
import {AppLinkedInterface} from '../../models/app/app.js'
5-
import {renderSuccess, renderWarning} from '@shopify/cli-kit/node/ui'
6+
import {renderSuccess, renderInfo, renderWarning} from '@shopify/cli-kit/node/ui'
67
import {ensureAuthenticatedAdmin} from '@shopify/cli-kit/node/session'
78
import {describe, test, expect, vi, beforeEach} from 'vitest'
89

910
vi.mock('./run-query.js')
1011
vi.mock('./run-mutation.js')
12+
vi.mock('./watch-bulk-operation.js')
1113
vi.mock('@shopify/cli-kit/node/ui')
1214
vi.mock('@shopify/cli-kit/node/session')
1315

@@ -135,9 +137,12 @@ describe('executeBulkOperation', () => {
135137
query,
136138
})
137139

138-
expect(renderSuccess).toHaveBeenCalledWith({
139-
headline: 'Bulk operation started successfully!',
140-
body: 'Congrats!',
140+
expect(renderInfo).toHaveBeenCalledWith({
141+
customSections: expect.arrayContaining([
142+
expect.objectContaining({
143+
title: 'Bulk operation started!',
144+
}),
145+
]),
141146
})
142147
})
143148

@@ -165,4 +170,36 @@ describe('executeBulkOperation', () => {
165170

166171
expect(renderSuccess).not.toHaveBeenCalled()
167172
})
173+
174+
test('executeBulkOperation waits until operation completes and prints final results when watch flag is true', async () => {
175+
const query = '{ products { edges { node { id } } } }'
176+
const mockResponse = {
177+
bulkOperation: successfulBulkOperation,
178+
userErrors: [],
179+
}
180+
const completedOperation = {
181+
...successfulBulkOperation,
182+
status: 'COMPLETED',
183+
url: 'https://example.com/download',
184+
objectCount: '650',
185+
}
186+
187+
vi.mocked(runBulkOperationQuery).mockResolvedValue(mockResponse as any)
188+
vi.mocked(watchBulkOperation).mockResolvedValue(completedOperation as any)
189+
190+
await executeBulkOperation({
191+
app: mockApp,
192+
storeFqdn,
193+
query,
194+
watch: true,
195+
})
196+
197+
expect(watchBulkOperation).toHaveBeenCalledWith(mockAdminSession, successfulBulkOperation.id)
198+
expect(renderSuccess).toHaveBeenCalledWith(
199+
expect.objectContaining({
200+
headline: 'Bulk operation complete!',
201+
body: expect.stringContaining('https://example.com/download'),
202+
}),
203+
)
204+
})
168205
})

packages/app/src/cli/services/bulk-operations/execute-bulk-operation.ts

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {runBulkOperationQuery} from './run-query.js'
22
import {runBulkOperationMutation} from './run-mutation.js'
3+
import {watchBulkOperation, type BulkOperation} from './watch-bulk-operation.js'
34
import {AppLinkedInterface} from '../../models/app/app.js'
45
import {renderSuccess, renderInfo, renderWarning} from '@shopify/cli-kit/node/ui'
56
import {outputContent, outputToken} from '@shopify/cli-kit/node/output'
@@ -12,10 +13,11 @@ interface ExecuteBulkOperationInput {
1213
storeFqdn: string
1314
query: string
1415
variables?: string[]
16+
watch?: boolean
1517
}
1618

1719
export async function executeBulkOperation(input: ExecuteBulkOperationInput): Promise<void> {
18-
const {app, storeFqdn, query, variables} = input
20+
const {app, storeFqdn, query, variables, watch = false} = input
1921

2022
renderInfo({
2123
headline: 'Starting bulk operation.',
@@ -49,11 +51,36 @@ export async function executeBulkOperation(input: ExecuteBulkOperationInput): Pr
4951
return
5052
}
5153

52-
const result = bulkOperationResponse?.bulkOperation
53-
if (result) {
54-
const infoSections = [
54+
const createdOperation = bulkOperationResponse?.bulkOperation
55+
if (createdOperation) {
56+
if (watch) {
57+
const finalOperation = await watchBulkOperation(adminSession, createdOperation.id)
58+
renderBulkOperationComplete(finalOperation)
59+
} else {
60+
renderBulkOperationStarted(createdOperation)
61+
}
62+
}
63+
}
64+
65+
function renderBulkOperationComplete(operation: BulkOperation): void {
66+
const url = operation.url ?? ''
67+
68+
renderSuccess({
69+
headline: 'Bulk operation complete!',
70+
body: outputContent`ID: ${outputToken.cyan(operation.id)}\nStatus: ${outputToken.yellow(
71+
operation.status,
72+
)}\nObject count: ${outputToken.gray(String(operation.objectCount))}\n\nDownload results ${outputToken.link(
73+
'here.',
74+
url,
75+
)}`.value,
76+
})
77+
}
78+
79+
function renderBulkOperationStarted(result: {id: string; status: string; createdAt: unknown}): void {
80+
renderInfo({
81+
customSections: [
5582
{
56-
title: 'Bulk Operation Created',
83+
title: 'Bulk operation started!',
5784
body: [
5885
{
5986
list: {
@@ -66,15 +93,8 @@ export async function executeBulkOperation(input: ExecuteBulkOperationInput): Pr
6693
},
6794
],
6895
},
69-
]
70-
71-
renderInfo({customSections: infoSections})
72-
73-
renderSuccess({
74-
headline: 'Bulk operation started successfully!',
75-
body: 'Congrats!',
76-
})
77-
}
96+
],
97+
})
7898
}
7999

80100
function isMutation(graphqlOperation: string): boolean {

packages/cli/oclif.manifest.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -901,6 +901,13 @@
901901
"hidden": false,
902902
"name": "verbose",
903903
"type": "boolean"
904+
},
905+
"watch": {
906+
"allowNo": false,
907+
"description": "Wait for bulk operation results before exiting.",
908+
"env": "SHOPIFY_FLAG_WATCH",
909+
"name": "watch",
910+
"type": "boolean"
904911
}
905912
},
906913
"hasDynamicHelp": false,

0 commit comments

Comments
 (0)