Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions packages/app/src/cli/services/dev/fetch.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import {AppManagementClient} from '../../utilities/developer-platform-client/app
import {afterEach, describe, expect, test, vi} from 'vitest'
import {renderFatalError} from '@shopify/cli-kit/node/ui'
import {mockAndCaptureOutput} from '@shopify/cli-kit/node/testing/output'
import {AbortError} from '@shopify/cli-kit/node/error'

const ORG1: Organization = {
id: '1',
Expand Down Expand Up @@ -121,7 +120,7 @@ describe('fetchStore', () => {
const got = fetchStore(ORG1, 'domain1', developerPlatformClient)

// Then
await expect(got).rejects.toThrow(new AbortError(`Could not find Store for domain domain1 in Organization org1.`))
await expect(got).rejects.toThrow(`Your current account (org1) doesn't have access to store domain1.`)
})
})

Expand Down
11 changes: 10 additions & 1 deletion packages/app/src/cli/services/dev/fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,16 @@ export async function fetchStore(
): Promise<OrganizationStore> {
const store = await developerPlatformClient.storeByDomain(org.id, storeFqdn)

if (!store) throw new AbortError(`Could not find Store for domain ${storeFqdn} in Organization ${org.businessName}.`)
if (!store) {
throw new AbortError(
`Your current account (${org.businessName}) doesn't have access to store ${storeFqdn}.`,
undefined,
[
['Select a different store with', {command: '--store'}, 'or', {command: '--reset'}],
['Use', {command: 'shopify auth login'}, 'to log in with a different account that has access to this store'],
],
)
}

return store
}
2 changes: 2 additions & 0 deletions packages/cli-kit/src/private/node/session/exchange.ts
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,8 @@ function tokenRequestErrorHandler({error, store}: {error: string; store?: string
}
if (error === 'invalid_target') {
return new InvalidTargetError(invalidTargetErrorMessage, '', [
['Select a different store with', {command: '--store'}],
['Use', {command: 'shopify auth login'}, 'to log in with a different account that has access to this store'],
'Ensure you have logged in to the store using the Shopify admin at least once.',
'Ensure you are the store owner, or have a staff account if you are attempting to log in to a dev store.',
'Ensure you are using the permanent store domain, not a vanity domain.',
Expand Down
14 changes: 14 additions & 0 deletions packages/cli-kit/src/public/node/api/admin.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {buildHeaders} from '../../../private/node/api/headers.js'
import * as http from '../../../public/node/http.js'
import {defaultThemeKitAccessDomain} from '../../../private/node/constants.js'
import {test, vi, expect, describe} from 'vitest'
import {ClientError} from 'graphql-request'

vi.mock('./graphql.js')
vi.mock('../../../private/node/api/headers.js')
Expand Down Expand Up @@ -90,6 +91,19 @@ describe('admin-graphql-api', () => {
variables: {variables: 'variables'},
})
})

test('throws helpful error when admin API returns 403', async () => {
// Given
const errorResponse = {
status: 403,
errors: [],
headers: new Headers(),
}
vi.mocked(graphqlRequestDoc).mockRejectedValue(new ClientError(errorResponse, {query: ''}))

// When/Then
await expect(admin.supportedApiVersions(Session)).rejects.toThrow(/don't have access to this store/)
})
})

describe('admin-rest-api', () => {
Expand Down
14 changes: 9 additions & 5 deletions packages/cli-kit/src/public/node/api/admin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,13 +170,17 @@ async function fetchApiVersions(session: AdminSession, preferredBehaviour?: Requ
return response.publicApiVersions
} catch (error) {
if (error instanceof ClientError && error.response.status === 403) {
const storeName = session.storeFqdn.replace('.myshopify.com', '')
throw new AbortError(
outputContent`Looks like you don't have access this dev store: (${outputToken.link(
storeName,
outputContent`Looks like you don't have access to this store: ${outputToken.link(
session.storeFqdn,
`https://${session.storeFqdn}`,
)})`,
outputContent`If you're not the owner, create a dev store staff account for yourself`,
)}`,
undefined,
[
['Select a different store with', {command: '--store'}],
['Use', {command: 'shopify auth login'}, 'to log in with a different account that has access to this store'],
"If you're not the owner, create a dev store staff account for yourself",
],
)
}
if (error instanceof ClientError && (error.response.status === 401 || error.response.status === 404)) {
Expand Down