Skip to content

Commit f9b53e2

Browse files
committed
implement-ensureAuthenticatedAdminAsApp-for-BulkOps-CLI
1 parent eb6a028 commit f9b53e2

File tree

4 files changed

+74
-6
lines changed

4 files changed

+74
-6
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ export default class Execute extends AppLinkedCommand {
3636

3737
await executeBulkOperation({
3838
app: appContextResult.app,
39+
remoteApp: appContextResult.remoteApp,
3940
storeFqdn: store.shopDomain,
4041
query: flags.query,
4142
variables: flags.variables,

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

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,38 @@ import {executeBulkOperation} from './execute-bulk-operation.js'
22
import {runBulkOperationQuery} from './run-query.js'
33
import {runBulkOperationMutation} from './run-mutation.js'
44
import {AppLinkedInterface} from '../../models/app/app.js'
5+
import {OrganizationApp} from '../../models/organization.js'
56
import {renderSuccess, renderWarning} from '@shopify/cli-kit/node/ui'
6-
import {ensureAuthenticatedAdmin} from '@shopify/cli-kit/node/session'
7+
import {ensureAuthenticatedAdminAsApp} from '@shopify/cli-kit/node/session'
78
import {inTemporaryDirectory, writeFile} from '@shopify/cli-kit/node/fs'
89
import {joinPath} from '@shopify/cli-kit/node/path'
910
import {describe, test, expect, vi, beforeEach} from 'vitest'
1011

1112
vi.mock('./run-query.js')
1213
vi.mock('./run-mutation.js')
1314
vi.mock('@shopify/cli-kit/node/ui')
14-
vi.mock('@shopify/cli-kit/node/session')
15+
vi.mock('@shopify/cli-kit/node/session', async () => {
16+
const actual = await vi.importActual('@shopify/cli-kit/node/session')
17+
return {
18+
...actual,
19+
ensureAuthenticatedAdminAsApp: vi.fn(),
20+
}
21+
})
1522

1623
describe('executeBulkOperation', () => {
1724
const mockApp = {
1825
name: 'Test App',
26+
configuration: {
27+
client_id: 'test-app-client-id',
28+
},
1929
} as AppLinkedInterface
2030

31+
const mockRemoteApp = {
32+
apiKey: 'test-app-client-id',
33+
apiSecretKeys: [{secret: 'test-api-secret'}],
34+
title: 'Test App',
35+
} as OrganizationApp
36+
2137
const storeFqdn = 'test-store.myshopify.com'
2238
const mockAdminSession = {token: 'test-token', storeFqdn}
2339

@@ -32,7 +48,7 @@ describe('executeBulkOperation', () => {
3248
}
3349

3450
beforeEach(() => {
35-
vi.mocked(ensureAuthenticatedAdmin).mockResolvedValue(mockAdminSession)
51+
vi.mocked(ensureAuthenticatedAdminAsApp).mockResolvedValue(mockAdminSession)
3652
})
3753

3854
test('runs query operation when GraphQL document starts with query', async () => {
@@ -45,6 +61,7 @@ describe('executeBulkOperation', () => {
4561

4662
await executeBulkOperation({
4763
app: mockApp,
64+
remoteApp: mockRemoteApp,
4865
storeFqdn,
4966
query,
5067
})
@@ -66,6 +83,7 @@ describe('executeBulkOperation', () => {
6683

6784
await executeBulkOperation({
6885
app: mockApp,
86+
remoteApp: mockRemoteApp,
6987
storeFqdn,
7088
query,
7189
})
@@ -87,6 +105,7 @@ describe('executeBulkOperation', () => {
87105

88106
await executeBulkOperation({
89107
app: mockApp,
108+
remoteApp: mockRemoteApp,
90109
storeFqdn,
91110
query: mutation,
92111
})
@@ -110,6 +129,7 @@ describe('executeBulkOperation', () => {
110129

111130
await executeBulkOperation({
112131
app: mockApp,
132+
remoteApp: mockRemoteApp,
113133
storeFqdn,
114134
query: mutation,
115135
variables,
@@ -131,6 +151,7 @@ describe('executeBulkOperation', () => {
131151
vi.mocked(runBulkOperationQuery).mockResolvedValue(mockResponse as any)
132152
await executeBulkOperation({
133153
app: mockApp,
154+
remoteApp: mockRemoteApp,
134155
storeFqdn,
135156
query,
136157
})
@@ -154,6 +175,7 @@ describe('executeBulkOperation', () => {
154175

155176
await executeBulkOperation({
156177
app: mockApp,
178+
remoteApp: mockRemoteApp,
157179
storeFqdn,
158180
query,
159181
})
@@ -172,6 +194,7 @@ describe('executeBulkOperation', () => {
172194
await expect(
173195
executeBulkOperation({
174196
app: mockApp,
197+
remoteApp: mockRemoteApp,
175198
storeFqdn,
176199
query: malformedQuery,
177200
}),
@@ -188,6 +211,7 @@ describe('executeBulkOperation', () => {
188211
await expect(
189212
executeBulkOperation({
190213
app: mockApp,
214+
remoteApp: mockRemoteApp,
191215
storeFqdn,
192216
query: multipleOperations,
193217
}),
@@ -208,6 +232,7 @@ describe('executeBulkOperation', () => {
208232
await expect(
209233
executeBulkOperation({
210234
app: mockApp,
235+
remoteApp: mockRemoteApp,
211236
storeFqdn,
212237
query: noOperations,
213238
}),
@@ -236,6 +261,7 @@ describe('executeBulkOperation', () => {
236261

237262
await executeBulkOperation({
238263
app: mockApp,
264+
remoteApp: mockRemoteApp,
239265
storeFqdn,
240266
query: mutation,
241267
variableFile: variableFilePath,
@@ -258,6 +284,7 @@ describe('executeBulkOperation', () => {
258284
await expect(
259285
executeBulkOperation({
260286
app: mockApp,
287+
remoteApp: mockRemoteApp,
261288
storeFqdn,
262289
query: mutation,
263290
variableFile: nonExistentPath,
@@ -276,6 +303,7 @@ describe('executeBulkOperation', () => {
276303
await expect(
277304
executeBulkOperation({
278305
app: mockApp,
306+
remoteApp: mockRemoteApp,
279307
storeFqdn,
280308
query,
281309
variables,
@@ -296,6 +324,7 @@ describe('executeBulkOperation', () => {
296324
await expect(
297325
executeBulkOperation({
298326
app: mockApp,
327+
remoteApp: mockRemoteApp,
299328
storeFqdn,
300329
query,
301330
variableFile: variableFilePath,

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

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
import {runBulkOperationQuery} from './run-query.js'
22
import {runBulkOperationMutation} from './run-mutation.js'
33
import {AppLinkedInterface} from '../../models/app/app.js'
4+
import {OrganizationApp} from '../../models/organization.js'
45
import {renderSuccess, renderInfo, renderWarning} from '@shopify/cli-kit/node/ui'
56
import {outputContent, outputToken} from '@shopify/cli-kit/node/output'
6-
import {ensureAuthenticatedAdmin} from '@shopify/cli-kit/node/session'
7+
import {ensureAuthenticatedAdminAsApp} from '@shopify/cli-kit/node/session'
78
import {AbortError} from '@shopify/cli-kit/node/error'
89
import {parse} from 'graphql'
910
import {readFile, fileExists} from '@shopify/cli-kit/node/fs'
1011

1112
interface ExecuteBulkOperationInput {
1213
app: AppLinkedInterface
14+
remoteApp: OrganizationApp
1315
storeFqdn: string
1416
query: string
1517
variables?: string[]
@@ -34,13 +36,16 @@ async function parseVariablesToJsonl(variables?: string[], variableFile?: string
3436
}
3537

3638
export async function executeBulkOperation(input: ExecuteBulkOperationInput): Promise<void> {
37-
const {app, storeFqdn, query, variables, variableFile} = input
39+
const {app, remoteApp, storeFqdn, query, variables, variableFile} = input
3840

3941
renderInfo({
4042
headline: 'Starting bulk operation.',
4143
body: `App: ${app.name}\nStore: ${storeFqdn}`,
4244
})
43-
const adminSession = await ensureAuthenticatedAdmin(storeFqdn)
45+
46+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
47+
const apiSecret = remoteApp.apiSecretKeys.find((elm) => elm.secret)!.secret
48+
const adminSession = await ensureAuthenticatedAdminAsApp(storeFqdn, app.configuration.client_id, apiSecret)
4449

4550
const variablesJsonl = await parseVariablesToJsonl(variables, variableFile)
4651

packages/cli-kit/src/public/node/session.test.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,39 @@ describe('ensureAuthenticatedBusinessPlatform', () => {
219219
})
220220
})
221221

222+
describe('ensureAuthenticatedAdminAsApp', () => {
223+
test('returns admin token authenticated as app using client credentials', async () => {
224+
// Given
225+
const apiKey = 'test-api-key'
226+
const apiSecret = 'test-api-secret'
227+
const store = 'mystore.myshopify.com'
228+
const mockToken = 'shpat_admin-as-app-token'
229+
230+
vi.mocked(shopifyFetch).mockResolvedValueOnce({
231+
json: async () => ({access_token: mockToken}),
232+
} as any)
233+
234+
// When
235+
const got = await ensureAuthenticatedAdminAsApp(store, apiKey, apiSecret)
236+
237+
// Then
238+
expect(got).toEqual({token: mockToken, storeFqdn: store})
239+
expect(shopifyFetch).toHaveBeenCalledWith(
240+
expect.stringContaining(`https://${store}/admin/oauth/access_token`),
241+
expect.objectContaining({
242+
method: 'POST',
243+
headers: {
244+
'Content-Type': 'application/json',
245+
},
246+
}),
247+
)
248+
const call = vi.mocked(shopifyFetch).mock.calls[0]![0]
249+
expect(call).toContain('client_id=test-api-key')
250+
expect(call).toContain('client_secret=test-api-secret')
251+
expect(call).toContain('grant_type=client_credentials')
252+
})
253+
})
254+
222255
describe('ensureAuthenticatedAppManagementAndBusinessPlatform', () => {
223256
test('returns app management and business platform tokens if success', async () => {
224257
// Given

0 commit comments

Comments
 (0)