-
Notifications
You must be signed in to change notification settings - Fork 130
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add GQL support for theme access tokens #4717
Conversation
95b1089
to
6144fc8
Compare
We detected some changes at packages/*/src and there are no updates in the .changeset. |
@@ -70,7 +70,7 @@ async function makeVerboseRequest<T extends {headers: Headers; status: number}>( | |||
} | |||
const sanitizedHeaders = sanitizedHeadersOutput(responseHeaders) | |||
|
|||
if (err.response.errors?.some((error) => error.extensions.code === '429') || err.response.status === 429) { | |||
if (err.response.status === 429 || errorsInclude429(err)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unrelated to the functionality of this PR, I noticed sometimes err.response.errors
is a string so this was raising (can't call .some
on a string). Now I check that errors.response.errors
is not a string before calling .some
to look through its contents
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you help me understand how this could be the case? Is graphql-request
(the package we get this type from) outdated?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know the string error message is coming from core. Maybe something about proxying this request through theme access app, we aren't respecting the expected gql return type? Hmm I will investigate
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
GraphQL returns a 401 with this Invalid API key or access token (unrecognized login or wrong password)
error message when auth fails (code). So we do expect that this error can be a string.
I've updated the GraphQLResponse
type so that errors
can be a string
or GraphQLError[]
.
6144fc8
to
285746a
Compare
285746a
to
0808e41
Compare
Coverage report
Show files with reduced coverage 🔻
Test suite run success1931 tests passing in 874 suites. Report generated by 🧪jest coverage report action from f747f67 |
d39cf9f
to
51dd61b
Compare
@@ -56,7 +56,7 @@ export function buildHeaders(token?: string): {[key: string]: string} { | |||
...(firstPartyDev() && {'X-Shopify-Cli-Employee': '1'}), | |||
} | |||
if (token) { | |||
const authString = token.match(/^shp(at|ua|ca)/) ? token : `Bearer ${token}` | |||
const authString = token.match(/^shp(at|ua|ca|tka)/) ? token : `Bearer ${token}` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
shptka_*
represent theme access tokens, and we want those as is, not Bearer ${token}
074e2c5
to
fddc4a5
Compare
fddc4a5
to
3bc66ee
Compare
@@ -70,7 +70,7 @@ async function makeVerboseRequest<T extends {headers: Headers; status: number}>( | |||
} | |||
const sanitizedHeaders = sanitizedHeadersOutput(responseHeaders) | |||
|
|||
if (err.response.errors?.some((error) => error.extensions.code === '429') || err.response.status === 429) { | |||
if (errorsIncludeStatus429(err)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See thread
if (typeof error.response.errors === 'string') { | ||
return false | ||
} | ||
return error.response.errors?.some((error) => error.extensions?.code === '429') ?? false | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My TS isn't the best, ideally I want to do something like this but it didn't seem possible
if (typeof error.response.errors === 'GraphQL[]'){
return error.response.errors?.some((error) => error.extensions?.code === '429') ?? false
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think you can interpret array types at run time, so this is probably fine. This error class is a bit of a mess anyway 🙈
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the type checking here will fix this issue. Thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't have access to this bugsnag, but I assume this is the error.response.errors?.some is not a function
or something error I encountered as well?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this mostly looks great! I'll defer to the CLI experts but my only feedback would be to abstract the graphql config. Nice work!
return true | ||
} | ||
|
||
// More so checking if type of error.response.errors is not GraphQLError[] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not actually typed within this error class as possibly being a string ((property) GraphQLResponse<unknown>.errors?: GraphQLError[] | undefined
) and doing so might open up a whole new can worms with invalid typing throughout the repo (although you could try), so leaving a comment with the context you provided in this comment would be valuable!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Gotcha, I had updated the GraphQLResponse
type to be string | GraphQLError[]
locally but just realized it's a node module and not part of the CLI. I'll leave it as a comment for now
if (typeof error.response.errors === 'string') { | ||
return false | ||
} | ||
return error.response.errors?.some((error) => error.extensions?.code === '429') ?? false | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think you can interpret array types at run time, so this is probably fine. This error class is a bit of a mess anyway 🙈
const realVersion = version ?? 'unstable' | ||
return `https://${store}/admin/api/${realVersion}/graphql.json` | ||
const themeKitAccessDomain = process.env[environmentVariables.themeKitAccessDomain] ?? defaultThemeKitAccessDomain |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I want to say this should maybe be defined in here?
const themeKitAccessDomain = process.env[environmentVariables.themeKitAccessDomain] ?? defaultThemeKitAccessDomain | ||
|
||
const url = | ||
session && isThemeAccessSession(session) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes in the issue description I mentioned In follow up, would like to clean up theme access logic by creating a file for it, rather than implementing it in REST and GraphQL
. I was thinking of doing it as a fast follow since this PR is blocking other CLI GraphQL PRs but I can also try to do it here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thats fair, so long as we can track it with an issue! Thanks
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
0d36687
to
19b1aa5
Compare
} | ||
const result = graphqlRequestDoc<TResult, TVariables>({...opts, query, variables, responseOptions}) | ||
return result | ||
} | ||
|
||
function headers(session: AdminSession): {[header: string]: string} { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
function headers(session: AdminSession): {[header: string]: string} { | |
function themeAccessHeaders(session: AdminSession): {[header: string]: string} { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no 🎩 but everything looks good to me!
19b1aa5
to
f747f67
Compare
Differences in type declarationsWe detected differences in the type declarations generated by Typescript for this branch compared to the baseline ('main' branch). Please, review them to ensure they are backward-compatible. Here are some important things to keep in mind:
New type declarationsWe found no new type declarations in this PR Existing type declarationspackages/cli-kit/dist/private/node/constants.d.ts@@ -58,4 +58,5 @@ export declare const reportingRateLimit: {
timeout: {
days: number;
};
-};
\ No newline at end of file
+};
+export declare const themeKitAccessDomain: string;
\ No newline at end of file
packages/cli-kit/dist/private/node/api/graphql.d.ts@@ -1,5 +1,5 @@
-import { RequestDocument, Variables } from 'graphql-request';
-export declare function debugLogRequestInfo(api: string, query: RequestDocument, variables?: Variables, headers?: {
+import { Variables } from 'graphql-request';
+export declare function debugLogRequestInfo(api: string, query: string, url: string, variables?: Variables, headers?: {
[key: string]: string;
}): void;
export declare function errorHandler(api: string): (error: unknown, requestId?: string) => unknown;
\ No newline at end of file
packages/cli-kit/dist/private/node/api/rest.d.ts@@ -1,9 +1,8 @@
-/// <reference types="node" resolution-mode="require"/>
import { AdminSession } from '@shopify/cli-kit/node/session';
export declare function restRequestBody<T>(requestBody?: T): string | undefined;
export declare function restRequestUrl(session: AdminSession, apiVersion: string, path: string, searchParams?: {
[name: string]: string;
-}, env?: NodeJS.ProcessEnv): string;
+}): string;
export declare function restRequestHeaders(session: AdminSession): {
[key: string]: string;
};
packages/cli-kit/dist/public/node/api/admin.d.ts@@ -34,9 +34,10 @@ export declare function supportedApiVersions(session: AdminSession): Promise<str
*
* @param store - Store FQDN.
* @param version - API version.
+ * @param session - User session.
* @returns - Admin API URL.
*/
-export declare function adminUrl(store: string, version: string | undefined): string;
+export declare function adminUrl(store: string, version: string | undefined, session?: AdminSession): string;
/**
* Executes a REST request against the Admin API.
*
|
WHY are these changes introduced?
Along with https://github.com/Shopify/themekit-access/pull/3703, add support for theme access tokens for GraphQL in the CLI. Will unblocking CLI GraphQL migration effort.
WHAT is this pull request doing?
Add theme access logic for GraphQL requests (proxy to theme access app with appropriate headers).
In follow up, would like to clean up theme access logic by creating a file for it, rather than implementing it in REST and GraphQL
How to test your changes?
See https://github.com/Shopify/themekit-access/pull/3703 for tophatting instructions
Measuring impact
How do we know this change was effective? Please choose one:
Checklist