Skip to content

Commit 770846a

Browse files
committed
Harden store list auto fallback against any Business Platform failure
1 parent b9b2150 commit 770846a

3 files changed

Lines changed: 56 additions & 12 deletions

File tree

packages/store/src/cli/services/store/list/bp-source.test.ts

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,9 @@ describe('listBusinessPlatformStores', () => {
297297
status: 'unavailable',
298298
error: expect.any(AbortError),
299299
})
300-
expect(result.status === 'unavailable' ? result.error.message : '').toContain('Access denied for accessibleShops')
300+
expect(result.status === 'unavailable' ? (result.error as Error).message : '').toContain(
301+
'Access denied for accessibleShops',
302+
)
301303
expect(output.debug()).toContain(
302304
'Business Platform store listing failed for organization Acme (1234): Access denied for accessibleShops',
303305
)
@@ -306,14 +308,31 @@ describe('listBusinessPlatformStores', () => {
306308
)
307309
})
308310

309-
test('rethrows non-abort errors', async () => {
311+
test('returns an unavailable status for non-abort errors so auto can fall back', async () => {
312+
const output = mockAndCaptureOutput()
313+
310314
vi.mocked(ensureAuthenticatedBusinessPlatform).mockResolvedValue('bp-token')
311315
vi.mocked(fetchOrganizationsWithAccessInfo).mockResolvedValue({
312316
organizations: [{id: '1234', businessName: 'Acme'}],
313317
currentUserResolved: true,
314318
})
315319
vi.mocked(businessPlatformOrganizationsRequestDoc).mockRejectedValueOnce(new Error('Network exploded'))
316320

317-
await expect(listBusinessPlatformStores()).rejects.toThrow('Network exploded')
321+
const result = await listBusinessPlatformStores()
322+
323+
expect(result).toMatchObject({status: 'unavailable'})
324+
expect(result.status === 'unavailable' ? (result.error as Error).message : '').toContain('Network exploded')
325+
expect(output.debug()).toContain(
326+
'Business Platform store listing failed for the current session: Network exploded',
327+
)
328+
})
329+
330+
test('returns an unavailable status when Business Platform authentication fails', async () => {
331+
vi.mocked(ensureAuthenticatedBusinessPlatform).mockRejectedValue(new Error('Network exploded'))
332+
333+
const result = await listBusinessPlatformStores()
334+
335+
expect(result).toMatchObject({status: 'unavailable'})
336+
expect(result.status === 'unavailable' ? (result.error as Error).message : '').toContain('Network exploded')
318337
})
319338
})

packages/store/src/cli/services/store/list/bp-source.ts

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import {type BusinessPlatformStoreListEntry, type FailedStoreListRetrieval} from
33
import {businessPlatformOrganizationsRequestDoc} from '@shopify/cli-kit/node/api/business-platform'
44
import {type UnauthorizedHandler} from '@shopify/cli-kit/node/api/graphql'
55
import {normalizeStoreFqdn} from '@shopify/cli-kit/node/context/fqdn'
6-
import {AbortError} from '@shopify/cli-kit/node/error'
76
import {outputDebug} from '@shopify/cli-kit/node/output'
87
import {ensureAuthenticatedBusinessPlatform} from '@shopify/cli-kit/node/session'
98
import {fetchOrganizationsWithAccessInfo} from '@shopify/organizations'
@@ -26,7 +25,7 @@ interface UnresolvedBusinessPlatformStoreListResult {
2625

2726
interface UnavailableBusinessPlatformStoreListResult {
2827
status: 'unavailable'
29-
error: AbortError
28+
error: unknown
3029
}
3130

3231
type ListBusinessPlatformStoresResult =
@@ -101,14 +100,14 @@ export async function listBusinessPlatformStores(
101100
: {}),
102101
}
103102
} catch (error) {
104-
if (error instanceof AbortError) {
105-
return {
106-
status: 'unavailable',
107-
error,
108-
}
103+
// Any failure while authenticating or listing through Business Platform is treated as
104+
// "unavailable" so `--source auto` can fall back to locally stored store auth. Callers that
105+
// explicitly request `--source bp` rethrow this error, so real failures still surface there.
106+
outputDebug(`Business Platform store listing failed for the current session: ${errorMessage(error)}`)
107+
return {
108+
status: 'unavailable',
109+
error,
109110
}
110-
111-
throw error
112111
}
113112
}
114113

packages/store/src/cli/services/store/list/index.test.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,23 @@ describe('listStores', () => {
135135
})
136136
})
137137

138+
test('falls back to local when Business Platform fails with a non-abort error in auto mode', async () => {
139+
vi.spyOn(localSource, 'listLocalStores').mockReturnValue([{store: 'shop.myshopify.com', userId: '42'}])
140+
vi.spyOn(bpSource, 'listBusinessPlatformStores').mockResolvedValue({
141+
status: 'unavailable',
142+
error: new Error('Network exploded'),
143+
})
144+
145+
const result = await listStores()
146+
147+
expect(result).toEqual({
148+
entries: [{store: 'shop.myshopify.com', userId: '42'}],
149+
source: 'local',
150+
notice:
151+
'Business Platform store listing was unavailable for the current session. Showing locally stored store auth instead.',
152+
})
153+
})
154+
138155
test('returns Business Platform unresolved results without fallback when user explicitly requests bp', async () => {
139156
vi.spyOn(bpSource, 'listBusinessPlatformStores').mockResolvedValue({
140157
status: 'unresolved-session',
@@ -158,4 +175,13 @@ describe('listStores', () => {
158175

159176
await expect(listStores({source: 'bp'})).rejects.toThrow('Access denied for accessibleShops')
160177
})
178+
179+
test('rethrows non-abort unavailable errors when user explicitly requests bp', async () => {
180+
vi.spyOn(bpSource, 'listBusinessPlatformStores').mockResolvedValue({
181+
status: 'unavailable',
182+
error: new Error('Network exploded'),
183+
})
184+
185+
await expect(listStores({source: 'bp'})).rejects.toThrow('Network exploded')
186+
})
161187
})

0 commit comments

Comments
 (0)