-
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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)) { | ||
let delayMs: number | undefined | ||
|
||
try { | ||
|
@@ -120,6 +120,19 @@ async function makeVerboseRequest<T extends {headers: Headers; status: number}>( | |
} | ||
} | ||
|
||
function errorsIncludeStatus429(error: ClientError): boolean { | ||
if (error.response.status === 429) { | ||
return true | ||
} | ||
|
||
// GraphQL returns a 401 with a string error message when auth fails | ||
// Therefore error.response.errros can be a string or GraphQLError[] | ||
if (typeof error.response.errors === 'string') { | ||
return false | ||
} | ||
return error.response.errors?.some((error) => error.extensions?.code === '429') ?? false | ||
} | ||
Comment on lines
+130
to
+134
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 commentThe 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 commentThe 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 |
||
|
||
export async function simpleRequestWithDebugLog<T extends {headers: Headers; status: number}>( | ||
{request, url}: RequestOptions<T>, | ||
errorHandler?: (error: unknown, requestId: string | undefined) => unknown, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
headers.authorization = authString | ||
headers['X-Shopify-Access-Token'] = authString | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,10 +2,16 @@ import {graphqlRequest, graphqlRequestDoc, GraphQLResponseOptions, GraphQLVariab | |
import {AdminSession} from '../session.js' | ||
import {outputContent, outputToken} from '../../../public/node/output.js' | ||
import {BugError, AbortError} from '../error.js' | ||
import {restRequestBody, restRequestHeaders, restRequestUrl} from '../../../private/node/api/rest.js' | ||
import { | ||
restRequestBody, | ||
restRequestHeaders, | ||
restRequestUrl, | ||
isThemeAccessSession, | ||
} from '../../../private/node/api/rest.js' | ||
import {fetch} from '../http.js' | ||
import {PublicApiVersions} from '../../../cli/api/graphql/admin/generated/public_api_versions.js' | ||
import {normalizeStoreFqdn} from '../context/fqdn.js' | ||
import {themeKitAccessDomain} from '../../../private/node/constants.js' | ||
import {ClientError, Variables} from 'graphql-request' | ||
import {TypedDocumentNode} from '@graphql-typed-document-node/core' | ||
|
||
|
@@ -21,8 +27,9 @@ export async function adminRequest<T>(query: string, session: AdminSession, vari | |
const api = 'Admin' | ||
const version = await fetchLatestSupportedApiVersion(session) | ||
const store = await normalizeStoreFqdn(session.storeFqdn) | ||
const url = adminUrl(store, version) | ||
return graphqlRequest({query, api, url, token: session.token, variables}) | ||
const url = adminUrl(store, version, session) | ||
const addedHeaders = themeAccessHeaders(session) | ||
return graphqlRequest({query, api, addedHeaders, url, token: session.token, variables}) | ||
} | ||
|
||
/** | ||
|
@@ -47,15 +54,23 @@ export async function adminRequestDoc<TResult, TVariables extends Variables>( | |
apiVersion = await fetchLatestSupportedApiVersion(session) | ||
} | ||
const store = await normalizeStoreFqdn(session.storeFqdn) | ||
const addedHeaders = themeAccessHeaders(session) | ||
const opts = { | ||
url: adminUrl(store, apiVersion), | ||
url: adminUrl(store, apiVersion, session), | ||
api: 'Admin', | ||
token: session.token, | ||
addedHeaders, | ||
} | ||
const result = graphqlRequestDoc<TResult, TVariables>({...opts, query, variables, responseOptions}) | ||
return result | ||
} | ||
|
||
function themeAccessHeaders(session: AdminSession): {[header: string]: string} { | ||
return isThemeAccessSession(session) | ||
? {'X-Shopify-Shop': session.storeFqdn, 'X-Shopify-Access-Token': session.token} | ||
: {} | ||
} | ||
|
||
/** | ||
* GraphQL query to retrieve the latest supported API version. | ||
* | ||
|
@@ -121,11 +136,17 @@ async function fetchApiVersions(session: AdminSession): Promise<ApiVersion[]> { | |
* | ||
* @param store - Store FQDN. | ||
* @param version - API version. | ||
* @param session - User session. | ||
* @returns - Admin API URL. | ||
*/ | ||
export function adminUrl(store: string, version: string | undefined): string { | ||
export function adminUrl(store: string, version: string | undefined, session?: AdminSession): string { | ||
const realVersion = version ?? 'unstable' | ||
return `https://${store}/admin/api/${realVersion}/graphql.json` | ||
|
||
const url = | ||
session && isThemeAccessSession(session) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 commentThe reason will be displayed to describe this comment to others. Learn more. Yes in the issue description I mentioned There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 commentThe reason will be displayed to describe this comment to others. Learn more. |
||
? `https://${themeKitAccessDomain}/cli/admin/api/${realVersion}/graphql.json` | ||
: `https://${store}/admin/api/${realVersion}/graphql.json` | ||
return url | ||
} | ||
|
||
interface ApiVersion { | ||
|
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