Skip to content

Commit afa2a4c

Browse files
committed
Add error handling for hydrogenStorefronts access denial
1 parent 351d55d commit afa2a4c

File tree

3 files changed

+81
-28
lines changed

3 files changed

+81
-28
lines changed

.changeset/twenty-rice-give.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@shopify/cli-hydrogen': patch
3+
---
4+
5+
Throw AbortError for hydrogenStorefronts GraphQL Access Denied Error

packages/cli/src/lib/graphql/admin/client.test.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,30 @@ describe('adminRequest', () => {
7272
});
7373
});
7474

75+
describe("when the user doesn't have access to hydrogenStorefronts", () => {
76+
it('throws an AbortError', async () => {
77+
const fakeGraphqlError = {
78+
errors: [
79+
{
80+
message: 'Access denied for hydrogenStorefronts field',
81+
},
82+
],
83+
};
84+
85+
vi.mocked(graphqlRequest).mockRejectedValue(fakeGraphqlError);
86+
87+
const response = adminRequest<TestSchema>('', {
88+
token: '',
89+
storeFqdn: '',
90+
});
91+
92+
await expect(response).rejects.toThrowError(AbortError);
93+
await expect(response).rejects.toMatchInlineSnapshot(
94+
`[Error: Couldn't access Hydrogen storefronts]`,
95+
);
96+
});
97+
});
98+
7599
describe("when the user doesn't have access to hydrogenStorefrontCreate", () => {
76100
it('throws an AbortError', async () => {
77101
const fakeGraphqlError = {

packages/cli/src/lib/graphql/admin/client.ts

Lines changed: 52 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,54 @@ interface GraphqlError {
2323
* @param variables - GraphQL variables to pass to the query.
2424
* @returns The response of the query of generic type <T>.
2525
*/
26+
const KNOWN_USER_ERRORS: {
27+
pattern: string;
28+
abort: ConstructorParameters<typeof AbortError>;
29+
}[] = [
30+
{
31+
pattern: 'app is not installed',
32+
abort: [
33+
"Hydrogen sales channel isn't installed",
34+
'Install the Hydrogen sales channel on your store to start creating and linking Hydrogen storefronts: https://apps.shopify.com/hydrogen',
35+
],
36+
},
37+
{
38+
pattern: 'Access denied for hydrogenStorefrontCreate field',
39+
abort: [
40+
"Couldn't connect storefront to Shopify",
41+
[
42+
'Common reasons for this error include:',
43+
{
44+
list: {
45+
items: [
46+
"The Hydrogen sales channel isn't installed on the store.",
47+
"You don't have the required account permission to manage apps or channels.",
48+
"You're trying to connect to an ineligible store type (Trial, Development store)",
49+
],
50+
},
51+
},
52+
],
53+
],
54+
},
55+
{
56+
pattern: 'Access denied for hydrogenStorefronts field',
57+
abort: [
58+
"Couldn't access Hydrogen storefronts",
59+
[
60+
'Common reasons for this error include:',
61+
{
62+
list: {
63+
items: [
64+
"You don't have full access to apps or access to the Hydrogen channel.",
65+
"The Hydrogen sales channel isn't installed on the store.",
66+
],
67+
},
68+
},
69+
],
70+
],
71+
},
72+
];
73+
2674
export async function adminRequest<T>(
2775
query: string,
2876
session: AdminSession,
@@ -42,34 +90,10 @@ export async function adminRequest<T>(
4290
} catch (error: any) {
4391
const errors: GraphqlError[] = error.errors;
4492

45-
if (
46-
errors?.some?.((error) => error.message.includes('app is not installed'))
47-
) {
48-
throw new AbortError(
49-
"Hydrogen sales channel isn't installed",
50-
'Install the Hydrogen sales channel on your store to start creating and linking Hydrogen storefronts: https://apps.shopify.com/hydrogen',
51-
);
52-
}
53-
54-
if (
55-
errors?.some?.((error) =>
56-
error.message.includes(
57-
'Access denied for hydrogenStorefrontCreate field',
58-
),
59-
)
60-
) {
61-
throw new AbortError("Couldn't connect storefront to Shopify", [
62-
'Common reasons for this error include:',
63-
{
64-
list: {
65-
items: [
66-
"The Hydrogen sales channel isn't installed on the store.",
67-
"You don't have the required account permission to manage apps or channels.",
68-
"You're trying to connect to an ineligible store type (Trial, Development store)",
69-
],
70-
},
71-
},
72-
]);
93+
for (const {pattern, abort} of KNOWN_USER_ERRORS) {
94+
if (errors?.some?.((e) => e.message.includes(pattern))) {
95+
throw new AbortError(...abort);
96+
}
7397
}
7498

7599
throw error;

0 commit comments

Comments
 (0)