@shopify/graphql-client
Advanced tools
Comparing version 0.8.0 to 0.9.0
@@ -8,3 +8,3 @@ 'use strict'; | ||
} | ||
function generateGetGQLClientParams({ getHeaders, getApiUrl, }) { | ||
function generateGetGQLClientParams({ getHeaders, getApiUrl }) { | ||
return (operation, options) => { | ||
@@ -11,0 +11,0 @@ const props = [operation]; |
@@ -22,3 +22,3 @@ 'use strict'; | ||
const versionError = `${client}: the provided apiVersion ("${apiVersion}")`; | ||
const supportedVersion = `Current supported API versions: ${currentSupportedApiVersions.join(", ")}`; | ||
const supportedVersion = `Currently supported API versions: ${currentSupportedApiVersions.join(", ")}`; | ||
if (!apiVersion || typeof apiVersion !== "string") { | ||
@@ -31,3 +31,3 @@ throw new Error(`${versionError} is invalid. ${supportedVersion}`); | ||
logger({ | ||
type: "UNSUPPORTED_API_VERSION", | ||
type: "Unsupported_Api_Version", | ||
content: { | ||
@@ -40,3 +40,3 @@ apiVersion, | ||
else { | ||
console.warn(`${versionError} is deprecated or not supported. ${supportedVersion}`); | ||
console.warn(`${versionError} is likely deprecated or not supported. ${supportedVersion}`); | ||
} | ||
@@ -43,0 +43,0 @@ } |
@@ -6,8 +6,4 @@ type CustomFetchApi = (url: string, init?: { | ||
}) => Promise<Response>; | ||
interface OperationVariables$1 { | ||
[key: string]: any; | ||
} | ||
interface Headers { | ||
[key: string]: string; | ||
} | ||
type OperationVariables$1 = Record<string, any>; | ||
type Headers = Record<string, string | string[]>; | ||
interface ResponseErrors { | ||
@@ -17,10 +13,11 @@ networkStatusCode?: number; | ||
graphQLErrors?: any[]; | ||
response?: Response; | ||
} | ||
interface GQLExtensions { | ||
[key: string]: any; | ||
type GQLExtensions = Record<string, any>; | ||
interface FetchResponseBody<TData = any> { | ||
data?: Partial<TData>; | ||
extensions?: GQLExtensions; | ||
} | ||
interface ClientResponse<TData = unknown> { | ||
data?: TData; | ||
interface ClientResponse<TData = any> extends FetchResponseBody<TData> { | ||
errors?: ResponseErrors; | ||
extensions?: GQLExtensions; | ||
} | ||
@@ -71,3 +68,3 @@ interface LogContent { | ||
fetch: (...props: RequestParams) => Promise<Response>; | ||
request: <TData = unknown>(...props: RequestParams) => Promise<ClientResponse<TData>>; | ||
request: <TData = any>(...props: RequestParams) => Promise<ClientResponse<TData>>; | ||
} | ||
@@ -89,10 +86,7 @@ | ||
}; | ||
[key: number | symbol]: never; | ||
} | ||
type UnpackedInput<InputType> = "input" extends keyof InputType ? InputType["input"] : InputType; | ||
type UnpackedInputMaybe<InputType> = InputType extends InputMaybe<infer R> ? InputMaybe<UnpackedInput<R>> : UnpackedInput<InputType>; | ||
type OperationVariables<Operation extends keyof Operations, Operations extends AllOperations> = Operations[Operation]["variables"] extends { | ||
[key: string]: never; | ||
} ? { | ||
[key: string]: never; | ||
} : { | ||
type OperationVariables<Operation extends keyof Operations, Operations extends AllOperations> = Operations[Operation]["variables"] extends Record<string, never> ? Record<string, never> : { | ||
variables?: { | ||
@@ -102,3 +96,3 @@ [k in keyof Operations[Operation]["variables"]]: UnpackedInputMaybe<Operations[Operation]["variables"][k]>; | ||
}; | ||
type ResponseWithType<T = any> = Omit<GraphQLClient["fetch"], "json"> & { | ||
type ResponseWithType<T = any> = Omit<Response, "json"> & { | ||
json: () => Promise<T>; | ||
@@ -109,3 +103,3 @@ }; | ||
interface UnsupportedApiVersionLog extends LogContent { | ||
type: "UNSUPPORTED_API_VERSION"; | ||
type: "Unsupported_Api_Version"; | ||
content: { | ||
@@ -130,5 +124,3 @@ apiVersion: string; | ||
} & (Operation extends keyof Operations ? OperationVariables<Operation, Operations> : { | ||
variables?: { | ||
[key: string]: any; | ||
}; | ||
variables?: Record<string, any>; | ||
}); | ||
@@ -139,5 +131,3 @@ type ApiClientRequestParams<Operation extends keyof Operations, Operations extends AllOperations> = [ | ||
]; | ||
type ApiClientFetch<Operations extends AllOperations = AllOperations> = <Operation extends keyof Operations = string>(...params: ApiClientRequestParams<Operation, Operations>) => Promise<ResponseWithType<{ | ||
data?: ReturnData<Operation, Operations>; | ||
}>>; | ||
type ApiClientFetch<Operations extends AllOperations = AllOperations> = <Operation extends keyof Operations = string>(...params: ApiClientRequestParams<Operation, Operations>) => Promise<ResponseWithType<FetchResponseBody<ReturnData<Operation, Operations>>>>; | ||
type ApiClientRequest<Operations extends AllOperations = AllOperations> = <TData = undefined, Operation extends keyof Operations = string>(...params: ApiClientRequestParams<Operation, Operations>) => Promise<ClientResponse<TData extends undefined ? ReturnData<Operation, Operations> : TData>>; | ||
@@ -170,8 +160,18 @@ interface ApiClient<TClientConfig extends ApiClientConfig = ApiClientConfig, Operations extends AllOperations = AllOperations> { | ||
interface GenerateHttpFetchOptions { | ||
clientLogger: Logger; | ||
fetchApi?: CustomFetchApi; | ||
client?: string; | ||
defaultRetryWaitTime?: number; | ||
retriableCodes?: number[]; | ||
} | ||
declare function generateHttpFetch({ clientLogger, fetchApi, client, defaultRetryWaitTime, retriableCodes, }: GenerateHttpFetchOptions): (requestParams: [url: string, init?: { | ||
method?: string | undefined; | ||
headers?: HeadersInit | undefined; | ||
body?: string | undefined; | ||
} | undefined], count: number, maxRetries: number) => ReturnType<GraphQLClient["fetch"]>; | ||
declare function generateGetHeaders(config: ApiClientConfig): ApiClient["getHeaders"]; | ||
declare function generateGetGQLClientParams<Operations extends AllOperations = AllOperations>({ getHeaders, getApiUrl, }: { | ||
getHeaders: ApiClient["getHeaders"]; | ||
getApiUrl: ApiClient["getApiUrl"]; | ||
}): <Operation extends keyof Operations>(operation: Operation, options?: ApiClientRequestOptions<Operation, Operations> | undefined) => RequestParams; | ||
declare function generateGetGQLClientParams<Operations extends AllOperations = AllOperations>({ getHeaders, getApiUrl }: Pick<ApiClient, "getHeaders" | "getApiUrl">): <Operation extends keyof Operations>(operation: Operation, options?: ApiClientRequestOptions<Operation, Operations> | undefined) => RequestParams; | ||
export { AllOperations, ApiClient, ApiClientConfig, ApiClientFetch, ApiClientLogContentTypes, ApiClientLogger, ApiClientRequest, ApiClientRequestOptions, ApiClientRequestParams, ClientConfig, ClientOptions, ClientResponse, CustomFetchApi, GQLExtensions, GraphQLClient, HTTPResponseLog, HTTPRetryLog, Headers, InputMaybe, LogContent, LogContentTypes, Logger, OperationVariables, RequestOptions, RequestParams, ResponseErrors, UnsupportedApiVersionLog, createGraphQLClient, generateGetGQLClientParams, generateGetHeaders, getCurrentApiVersion, getCurrentSupportedApiVersions, getErrorMessage, validateApiVersion, validateDomainAndGetStoreUrl, validateRetries }; | ||
export { AllOperations, ApiClient, ApiClientConfig, ApiClientFetch, ApiClientLogContentTypes, ApiClientLogger, ApiClientRequest, ApiClientRequestOptions, ApiClientRequestParams, ClientConfig, ClientOptions, ClientResponse, CustomFetchApi, FetchResponseBody, GQLExtensions, GraphQLClient, HTTPResponseLog, HTTPRetryLog, Headers, InputMaybe, LogContent, LogContentTypes, Logger, OperationVariables, RequestOptions, RequestParams, ResponseErrors, ResponseWithType, ReturnData, UnsupportedApiVersionLog, createGraphQLClient, generateGetGQLClientParams, generateGetHeaders, generateHttpFetch, getCurrentApiVersion, getCurrentSupportedApiVersions, getErrorMessage, validateApiVersion, validateDomainAndGetStoreUrl, validateRetries }; |
@@ -6,6 +6,21 @@ 'use strict'; | ||
const MAX_RETRIES = 3; | ||
const GQL_API_ERROR = "An error occurred while fetching from the API. Review 'graphQLErrors' for details."; | ||
const UNEXPECTED_CONTENT_TYPE_ERROR = "Response returned unexpected Content-Type:"; | ||
const NO_DATA_OR_ERRORS_ERROR = "An unknown error has occurred. The API did not return a data object or any errors in its response."; | ||
const CONTENT_TYPES = { | ||
json: "application/json", | ||
multipart: "multipart/mixed", | ||
}; | ||
const RETRY_WAIT_TIME = 1000; | ||
const RETRIABLE_STATUS_CODES = [429, 503]; | ||
exports.CLIENT = CLIENT; | ||
exports.CONTENT_TYPES = CONTENT_TYPES; | ||
exports.GQL_API_ERROR = GQL_API_ERROR; | ||
exports.MAX_RETRIES = MAX_RETRIES; | ||
exports.MIN_RETRIES = MIN_RETRIES; | ||
exports.NO_DATA_OR_ERRORS_ERROR = NO_DATA_OR_ERRORS_ERROR; | ||
exports.RETRIABLE_STATUS_CODES = RETRIABLE_STATUS_CODES; | ||
exports.RETRY_WAIT_TIME = RETRY_WAIT_TIME; | ||
exports.UNEXPECTED_CONTENT_TYPE_ERROR = UNEXPECTED_CONTENT_TYPE_ERROR; | ||
//# sourceMappingURL=constants.js.map |
'use strict'; | ||
var httpFetch = require('./http-fetch.js'); | ||
var constants = require('./constants.js'); | ||
var utilities = require('./utilities.js'); | ||
const GQL_API_ERROR = `${constants.CLIENT}: An error occurred while fetching from the API. Review 'graphQLErrors' for details.`; | ||
const UNEXPECTED_CONTENT_TYPE_ERROR = `${constants.CLIENT}: Response returned unexpected Content-Type:`; | ||
const CONTENT_TYPES = { | ||
json: "application/json", | ||
multipart: "multipart/mixed", | ||
}; | ||
const RETRY_WAIT_TIME = 1000; | ||
const RETRIABLE_STATUS_CODES = [429, 503]; | ||
function createGraphQLClient({ headers, url, fetchApi = fetch, retries = 0, logger, }) { | ||
@@ -22,4 +15,8 @@ utilities.validateRetries({ client: constants.CLIENT, retries }); | ||
const clientLogger = generateClientLogger(logger); | ||
const httpFetch = generateHttpFetch(fetchApi, clientLogger); | ||
const fetch = generateFetch(httpFetch, config); | ||
const httpFetch$1 = httpFetch.generateHttpFetch({ | ||
fetchApi, | ||
clientLogger, | ||
defaultRetryWaitTime: constants.RETRY_WAIT_TIME, | ||
}); | ||
const fetch = generateFetch(httpFetch$1, config); | ||
const request = generateRequest(fetch); | ||
@@ -32,4 +29,8 @@ return { | ||
} | ||
async function sleep(waitTime) { | ||
return new Promise((resolve) => setTimeout(resolve, waitTime)); | ||
function generateClientLogger(logger) { | ||
return (logContent) => { | ||
if (logger) { | ||
logger(logContent); | ||
} | ||
}; | ||
} | ||
@@ -39,4 +40,4 @@ async function processJSONResponse(response) { | ||
return { | ||
...(data ? { data } : {}), | ||
...(extensions ? { extensions } : {}), | ||
...utilities.getKeyValueIfValid("data", data), | ||
...utilities.getKeyValueIfValid("extensions", extensions), | ||
...(errors || !data | ||
@@ -46,6 +47,5 @@ ? { | ||
networkStatusCode: response.status, | ||
message: errors | ||
? GQL_API_ERROR | ||
: `${constants.CLIENT}: An unknown error has occurred. The API did not return a data object or any errors in its response.`, | ||
...(errors ? { graphQLErrors: errors } : {}), | ||
message: utilities.formatErrorMessage(errors ? constants.GQL_API_ERROR : constants.NO_DATA_OR_ERRORS_ERROR), | ||
...utilities.getKeyValueIfValid("graphQLErrors", errors), | ||
response, | ||
}, | ||
@@ -56,51 +56,2 @@ } | ||
} | ||
function generateClientLogger(logger) { | ||
return (logContent) => { | ||
if (logger) { | ||
logger(logContent); | ||
} | ||
}; | ||
} | ||
function generateHttpFetch(fetchApi, clientLogger) { | ||
const httpFetch = async (requestParams, count, maxRetries) => { | ||
const nextCount = count + 1; | ||
const maxTries = maxRetries + 1; | ||
let response; | ||
try { | ||
response = await fetchApi(...requestParams); | ||
clientLogger({ | ||
type: "HTTP-Response", | ||
content: { | ||
requestParams, | ||
response, | ||
}, | ||
}); | ||
if (!response.ok && | ||
RETRIABLE_STATUS_CODES.includes(response.status) && | ||
nextCount <= maxTries) { | ||
throw new Error(); | ||
} | ||
return response; | ||
} | ||
catch (error) { | ||
if (nextCount <= maxTries) { | ||
await sleep(RETRY_WAIT_TIME); | ||
clientLogger({ | ||
type: "HTTP-Retry", | ||
content: { | ||
requestParams, | ||
lastResponse: response, | ||
retryAttempt: count, | ||
maxRetries, | ||
}, | ||
}); | ||
return httpFetch(requestParams, nextCount, maxRetries); | ||
} | ||
throw new Error(`${constants.CLIENT}:${maxRetries > 0 | ||
? ` Attempted maximum number of ${maxRetries} network retries. Last message -` | ||
: ""} ${utilities.getErrorMessage(error)}`); | ||
} | ||
}; | ||
return httpFetch; | ||
} | ||
function generateFetch(httpFetch, { url, headers, retries }) { | ||
@@ -114,2 +65,6 @@ return async (operation, options = {}) => { | ||
utilities.validateRetries({ client: constants.CLIENT, retries: overrideRetries }); | ||
const flatHeaders = Object.fromEntries(Object.entries({ ...headers, ...overrideHeaders }).map(([key, value]) => [ | ||
key, | ||
Array.isArray(value) ? value.join(", ") : value.toString(), | ||
])); | ||
const fetchParams = [ | ||
@@ -119,6 +74,3 @@ overrideUrl ?? url, | ||
method: "POST", | ||
headers: { | ||
...headers, | ||
...overrideHeaders, | ||
}, | ||
headers: flatHeaders, | ||
body, | ||
@@ -140,11 +92,13 @@ }, | ||
networkStatusCode: status, | ||
message: statusText, | ||
message: utilities.formatErrorMessage(statusText), | ||
response, | ||
}, | ||
}; | ||
} | ||
if (!contentType.includes(CONTENT_TYPES.json)) { | ||
if (!contentType.includes(constants.CONTENT_TYPES.json)) { | ||
return { | ||
errors: { | ||
networkStatusCode: status, | ||
message: `${UNEXPECTED_CONTENT_TYPE_ERROR} ${contentType}`, | ||
message: utilities.formatErrorMessage(`${constants.UNEXPECTED_CONTENT_TYPE_ERROR} ${contentType}`), | ||
response, | ||
}, | ||
@@ -151,0 +105,0 @@ }; |
@@ -5,2 +5,5 @@ 'use strict'; | ||
function formatErrorMessage(message, client = constants.CLIENT) { | ||
return message.startsWith(`${client}`) ? message : `${client}: ${message}`; | ||
} | ||
function getErrorMessage(error) { | ||
@@ -17,5 +20,15 @@ return error instanceof Error ? error.message : JSON.stringify(error); | ||
} | ||
function getKeyValueIfValid(key, value) { | ||
return value && | ||
(typeof value !== "object" || | ||
Array.isArray(value) || | ||
(typeof value === "object" && Object.keys(value).length > 0)) | ||
? { [key]: value } | ||
: {}; | ||
} | ||
exports.formatErrorMessage = formatErrorMessage; | ||
exports.getErrorMessage = getErrorMessage; | ||
exports.getKeyValueIfValid = getKeyValueIfValid; | ||
exports.validateRetries = validateRetries; | ||
//# sourceMappingURL=utilities.js.map |
@@ -7,2 +7,3 @@ 'use strict'; | ||
var apiVersions = require('./api-client-utilities/api-versions.js'); | ||
var httpFetch = require('./graphql-client/http-fetch.js'); | ||
var utilities$1 = require('./api-client-utilities/utilities.js'); | ||
@@ -19,4 +20,5 @@ | ||
exports.getCurrentSupportedApiVersions = apiVersions.getCurrentSupportedApiVersions; | ||
exports.generateHttpFetch = httpFetch.generateHttpFetch; | ||
exports.generateGetGQLClientParams = utilities$1.generateGetGQLClientParams; | ||
exports.generateGetHeaders = utilities$1.generateGetHeaders; | ||
//# sourceMappingURL=index.js.map |
export { validateRetries, getErrorMessage } from "../graphql-client/utilities"; | ||
export * from "./validations"; | ||
export * from "./api-versions"; | ||
export * from "../graphql-client/http-fetch"; | ||
export * from "./utilities"; | ||
export type * from "./types"; | ||
//# sourceMappingURL=index.d.ts.map |
@@ -1,2 +0,1 @@ | ||
import { GraphQLClient } from "../graphql-client/types"; | ||
export type InputMaybe<_R = never> = never; | ||
@@ -8,10 +7,7 @@ export interface AllOperations { | ||
}; | ||
[key: number | symbol]: never; | ||
} | ||
type UnpackedInput<InputType> = "input" extends keyof InputType ? InputType["input"] : InputType; | ||
type UnpackedInputMaybe<InputType> = InputType extends InputMaybe<infer R> ? InputMaybe<UnpackedInput<R>> : UnpackedInput<InputType>; | ||
export type OperationVariables<Operation extends keyof Operations, Operations extends AllOperations> = Operations[Operation]["variables"] extends { | ||
[key: string]: never; | ||
} ? { | ||
[key: string]: never; | ||
} : { | ||
export type OperationVariables<Operation extends keyof Operations, Operations extends AllOperations> = Operations[Operation]["variables"] extends Record<string, never> ? Record<string, never> : { | ||
variables?: { | ||
@@ -21,3 +17,3 @@ [k in keyof Operations[Operation]["variables"]]: UnpackedInputMaybe<Operations[Operation]["variables"][k]>; | ||
}; | ||
export type ResponseWithType<T = any> = Omit<GraphQLClient["fetch"], "json"> & { | ||
export type ResponseWithType<T = any> = Omit<Response, "json"> & { | ||
json: () => Promise<T>; | ||
@@ -24,0 +20,0 @@ }; |
@@ -1,6 +0,6 @@ | ||
import { LogContentTypes, LogContent, Logger as BaseLogger, Headers, ClientResponse } from "../graphql-client/types"; | ||
import { LogContentTypes, LogContent, Logger as BaseLogger, Headers, ClientResponse, FetchResponseBody } from "../graphql-client/types"; | ||
import { AllOperations, OperationVariables, ResponseWithType, ReturnData } from "./operation-types"; | ||
export { AllOperations, InputMaybe, OperationVariables, } from "./operation-types"; | ||
export { AllOperations, InputMaybe, OperationVariables, ReturnData, ResponseWithType, } from "./operation-types"; | ||
export interface UnsupportedApiVersionLog extends LogContent { | ||
type: "UNSUPPORTED_API_VERSION"; | ||
type: "Unsupported_Api_Version"; | ||
content: { | ||
@@ -25,5 +25,3 @@ apiVersion: string; | ||
} & (Operation extends keyof Operations ? OperationVariables<Operation, Operations> : { | ||
variables?: { | ||
[key: string]: any; | ||
}; | ||
variables?: Record<string, any>; | ||
}); | ||
@@ -34,5 +32,3 @@ export type ApiClientRequestParams<Operation extends keyof Operations, Operations extends AllOperations> = [ | ||
]; | ||
export type ApiClientFetch<Operations extends AllOperations = AllOperations> = <Operation extends keyof Operations = string>(...params: ApiClientRequestParams<Operation, Operations>) => Promise<ResponseWithType<{ | ||
data?: ReturnData<Operation, Operations>; | ||
}>>; | ||
export type ApiClientFetch<Operations extends AllOperations = AllOperations> = <Operation extends keyof Operations = string>(...params: ApiClientRequestParams<Operation, Operations>) => Promise<ResponseWithType<FetchResponseBody<ReturnData<Operation, Operations>>>>; | ||
export type ApiClientRequest<Operations extends AllOperations = AllOperations> = <TData = undefined, Operation extends keyof Operations = string>(...params: ApiClientRequestParams<Operation, Operations>) => Promise<ClientResponse<TData extends undefined ? ReturnData<Operation, Operations> : TData>>; | ||
@@ -39,0 +35,0 @@ export interface ApiClient<TClientConfig extends ApiClientConfig = ApiClientConfig, Operations extends AllOperations = AllOperations> { |
import { RequestParams } from "../graphql-client/types"; | ||
import { AllOperations, ApiClient, ApiClientConfig, ApiClientRequestOptions } from "./types"; | ||
export declare function generateGetHeaders(config: ApiClientConfig): ApiClient["getHeaders"]; | ||
export declare function generateGetGQLClientParams<Operations extends AllOperations = AllOperations>({ getHeaders, getApiUrl, }: { | ||
getHeaders: ApiClient["getHeaders"]; | ||
getApiUrl: ApiClient["getApiUrl"]; | ||
}): <Operation extends keyof Operations>(operation: Operation, options?: ApiClientRequestOptions<Operation, Operations> | undefined) => RequestParams; | ||
export declare function generateGetGQLClientParams<Operations extends AllOperations = AllOperations>({ getHeaders, getApiUrl }: Pick<ApiClient, "getHeaders" | "getApiUrl">): <Operation extends keyof Operations>(operation: Operation, options?: ApiClientRequestOptions<Operation, Operations> | undefined) => RequestParams; | ||
//# sourceMappingURL=utilities.d.ts.map |
export declare const CLIENT = "GraphQL Client"; | ||
export declare const MIN_RETRIES = 0; | ||
export declare const MAX_RETRIES = 3; | ||
export declare const GQL_API_ERROR = "An error occurred while fetching from the API. Review 'graphQLErrors' for details."; | ||
export declare const UNEXPECTED_CONTENT_TYPE_ERROR = "Response returned unexpected Content-Type:"; | ||
export declare const NO_DATA_OR_ERRORS_ERROR = "An unknown error has occurred. The API did not return a data object or any errors in its response."; | ||
export declare const CONTENT_TYPES: { | ||
json: string; | ||
multipart: string; | ||
}; | ||
export declare const RETRY_WAIT_TIME = 1000; | ||
export declare const RETRIABLE_STATUS_CODES: number[]; | ||
//# sourceMappingURL=constants.d.ts.map |
@@ -6,8 +6,4 @@ export type CustomFetchApi = (url: string, init?: { | ||
}) => Promise<Response>; | ||
interface OperationVariables { | ||
[key: string]: any; | ||
} | ||
export interface Headers { | ||
[key: string]: string; | ||
} | ||
type OperationVariables = Record<string, any>; | ||
export type Headers = Record<string, string | string[]>; | ||
export interface ResponseErrors { | ||
@@ -17,10 +13,11 @@ networkStatusCode?: number; | ||
graphQLErrors?: any[]; | ||
response?: Response; | ||
} | ||
export interface GQLExtensions { | ||
[key: string]: any; | ||
export type GQLExtensions = Record<string, any>; | ||
export interface FetchResponseBody<TData = any> { | ||
data?: Partial<TData>; | ||
extensions?: GQLExtensions; | ||
} | ||
export interface ClientResponse<TData = unknown> { | ||
data?: TData; | ||
export interface ClientResponse<TData = any> extends FetchResponseBody<TData> { | ||
errors?: ResponseErrors; | ||
extensions?: GQLExtensions; | ||
} | ||
@@ -71,5 +68,5 @@ export interface LogContent { | ||
fetch: (...props: RequestParams) => Promise<Response>; | ||
request: <TData = unknown>(...props: RequestParams) => Promise<ClientResponse<TData>>; | ||
request: <TData = any>(...props: RequestParams) => Promise<ClientResponse<TData>>; | ||
} | ||
export {}; | ||
//# sourceMappingURL=types.d.ts.map |
@@ -0,1 +1,2 @@ | ||
export declare function formatErrorMessage(message: string, client?: string): string; | ||
export declare function getErrorMessage(error: any): string; | ||
@@ -6,2 +7,5 @@ export declare function validateRetries({ client, retries, }: { | ||
}): void; | ||
export declare function getKeyValueIfValid(key: string, value?: any): { | ||
[x: string]: any; | ||
}; | ||
//# sourceMappingURL=utilities.d.ts.map |
@@ -1,2 +0,2 @@ | ||
/*! shopify/graphql-client@0.8.0 -- Copyright (c) 2023-present, Shopify Inc. -- license (MIT): https://github.com/Shopify/shopify-api-js/blob/main/LICENSE.md */ | ||
/*! shopify/graphql-client@0.9.0 -- Copyright (c) 2023-present, Shopify Inc. -- license (MIT): https://github.com/Shopify/shopify-api-js/blob/main/LICENSE.md */ | ||
(function (global, factory) { | ||
@@ -11,3 +11,15 @@ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : | ||
const MAX_RETRIES = 3; | ||
const GQL_API_ERROR = "An error occurred while fetching from the API. Review 'graphQLErrors' for details."; | ||
const UNEXPECTED_CONTENT_TYPE_ERROR = "Response returned unexpected Content-Type:"; | ||
const NO_DATA_OR_ERRORS_ERROR = "An unknown error has occurred. The API did not return a data object or any errors in its response."; | ||
const CONTENT_TYPES = { | ||
json: "application/json", | ||
multipart: "multipart/mixed", | ||
}; | ||
const RETRY_WAIT_TIME = 1000; | ||
const RETRIABLE_STATUS_CODES = [429, 503]; | ||
function formatErrorMessage(message, client = CLIENT) { | ||
return message.startsWith(`${client}`) ? message : `${client}: ${message}`; | ||
} | ||
function getErrorMessage(error) { | ||
@@ -24,57 +36,12 @@ return error instanceof Error ? error.message : JSON.stringify(error); | ||
} | ||
function getKeyValueIfValid(key, value) { | ||
return value && | ||
(typeof value !== "object" || | ||
Array.isArray(value) || | ||
(typeof value === "object" && Object.keys(value).length > 0)) | ||
? { [key]: value } | ||
: {}; | ||
} | ||
const GQL_API_ERROR = `${CLIENT}: An error occurred while fetching from the API. Review 'graphQLErrors' for details.`; | ||
const UNEXPECTED_CONTENT_TYPE_ERROR = `${CLIENT}: Response returned unexpected Content-Type:`; | ||
const CONTENT_TYPES = { | ||
json: "application/json", | ||
multipart: "multipart/mixed", | ||
}; | ||
const RETRY_WAIT_TIME = 1000; | ||
const RETRIABLE_STATUS_CODES = [429, 503]; | ||
function createGraphQLClient({ headers, url, fetchApi = fetch, retries = 0, logger, }) { | ||
validateRetries({ client: CLIENT, retries }); | ||
const config = { | ||
headers, | ||
url, | ||
retries, | ||
}; | ||
const clientLogger = generateClientLogger(logger); | ||
const httpFetch = generateHttpFetch(fetchApi, clientLogger); | ||
const fetch = generateFetch(httpFetch, config); | ||
const request = generateRequest(fetch); | ||
return { | ||
config, | ||
fetch, | ||
request, | ||
}; | ||
} | ||
async function sleep(waitTime) { | ||
return new Promise((resolve) => setTimeout(resolve, waitTime)); | ||
} | ||
async function processJSONResponse(response) { | ||
const { errors, data, extensions } = await response.json(); | ||
return { | ||
...(data ? { data } : {}), | ||
...(extensions ? { extensions } : {}), | ||
...(errors || !data | ||
? { | ||
errors: { | ||
networkStatusCode: response.status, | ||
message: errors | ||
? GQL_API_ERROR | ||
: `${CLIENT}: An unknown error has occurred. The API did not return a data object or any errors in its response.`, | ||
...(errors ? { graphQLErrors: errors } : {}), | ||
}, | ||
} | ||
: {}), | ||
}; | ||
} | ||
function generateClientLogger(logger) { | ||
return (logContent) => { | ||
if (logger) { | ||
logger(logContent); | ||
} | ||
}; | ||
} | ||
function generateHttpFetch(fetchApi, clientLogger) { | ||
function generateHttpFetch({ clientLogger, fetchApi = fetch, client = CLIENT, defaultRetryWaitTime = RETRY_WAIT_TIME, retriableCodes = RETRIABLE_STATUS_CODES, }) { | ||
const httpFetch = async (requestParams, count, maxRetries) => { | ||
@@ -94,3 +61,3 @@ const nextCount = count + 1; | ||
if (!response.ok && | ||
RETRIABLE_STATUS_CODES.includes(response.status) && | ||
retriableCodes.includes(response.status) && | ||
nextCount <= maxTries) { | ||
@@ -103,3 +70,4 @@ throw new Error(); | ||
if (nextCount <= maxTries) { | ||
await sleep(RETRY_WAIT_TIME); | ||
const retryAfter = response?.headers.get("Retry-After"); | ||
await sleep(retryAfter ? parseInt(retryAfter, 10) : defaultRetryWaitTime); | ||
clientLogger({ | ||
@@ -116,5 +84,5 @@ type: "HTTP-Retry", | ||
} | ||
throw new Error(`${CLIENT}:${maxRetries > 0 | ||
? ` Attempted maximum number of ${maxRetries} network retries. Last message -` | ||
: ""} ${getErrorMessage(error)}`); | ||
throw new Error(formatErrorMessage(`${maxRetries > 0 | ||
? `Attempted maximum number of ${maxRetries} network retries. Last message - ` | ||
: ""}${getErrorMessage(error)}`, client)); | ||
} | ||
@@ -124,2 +92,51 @@ }; | ||
} | ||
async function sleep(waitTime) { | ||
return new Promise((resolve) => setTimeout(resolve, waitTime)); | ||
} | ||
function createGraphQLClient({ headers, url, fetchApi = fetch, retries = 0, logger, }) { | ||
validateRetries({ client: CLIENT, retries }); | ||
const config = { | ||
headers, | ||
url, | ||
retries, | ||
}; | ||
const clientLogger = generateClientLogger(logger); | ||
const httpFetch = generateHttpFetch({ | ||
fetchApi, | ||
clientLogger, | ||
defaultRetryWaitTime: RETRY_WAIT_TIME, | ||
}); | ||
const fetch = generateFetch(httpFetch, config); | ||
const request = generateRequest(fetch); | ||
return { | ||
config, | ||
fetch, | ||
request, | ||
}; | ||
} | ||
function generateClientLogger(logger) { | ||
return (logContent) => { | ||
if (logger) { | ||
logger(logContent); | ||
} | ||
}; | ||
} | ||
async function processJSONResponse(response) { | ||
const { errors, data, extensions } = await response.json(); | ||
return { | ||
...getKeyValueIfValid("data", data), | ||
...getKeyValueIfValid("extensions", extensions), | ||
...(errors || !data | ||
? { | ||
errors: { | ||
networkStatusCode: response.status, | ||
message: formatErrorMessage(errors ? GQL_API_ERROR : NO_DATA_OR_ERRORS_ERROR), | ||
...getKeyValueIfValid("graphQLErrors", errors), | ||
response, | ||
}, | ||
} | ||
: {}), | ||
}; | ||
} | ||
function generateFetch(httpFetch, { url, headers, retries }) { | ||
@@ -133,2 +150,6 @@ return async (operation, options = {}) => { | ||
validateRetries({ client: CLIENT, retries: overrideRetries }); | ||
const flatHeaders = Object.fromEntries(Object.entries({ ...headers, ...overrideHeaders }).map(([key, value]) => [ | ||
key, | ||
Array.isArray(value) ? value.join(", ") : value.toString(), | ||
])); | ||
const fetchParams = [ | ||
@@ -138,6 +159,3 @@ overrideUrl ?? url, | ||
method: "POST", | ||
headers: { | ||
...headers, | ||
...overrideHeaders, | ||
}, | ||
headers: flatHeaders, | ||
body, | ||
@@ -159,3 +177,4 @@ }, | ||
networkStatusCode: status, | ||
message: statusText, | ||
message: formatErrorMessage(statusText), | ||
response, | ||
}, | ||
@@ -168,3 +187,4 @@ }; | ||
networkStatusCode: status, | ||
message: `${UNEXPECTED_CONTENT_TYPE_ERROR} ${contentType}`, | ||
message: formatErrorMessage(`${UNEXPECTED_CONTENT_TYPE_ERROR} ${contentType}`), | ||
response, | ||
}, | ||
@@ -171,0 +191,0 @@ }; |
@@ -1,3 +0,3 @@ | ||
/*! shopify/graphql-client@0.8.0 -- Copyright (c) 2023-present, Shopify Inc. -- license (MIT): https://github.com/Shopify/shopify-api-js/blob/main/LICENSE.md */ | ||
!function(e,r){"object"==typeof exports&&"undefined"!=typeof module?r(exports):"function"==typeof define&&define.amd?define(["exports"],r):r((e="undefined"!=typeof globalThis?globalThis:e||self).ShopifyGraphQLClient={})}(this,(function(e){"use strict";const r="GraphQL Client",t=0,n=3;function s(e){return e instanceof Error?e.message:JSON.stringify(e)}function o({client:e,retries:r}){if(void 0!==r&&("number"!=typeof r||r<t||r>n))throw new Error(`${e}: The provided "retries" value (${r}) is invalid - it cannot be less than ${t} or greater than ${n}`)}const i=`${r}: An error occurred while fetching from the API. Review 'graphQLErrors' for details.`,a=`${r}: Response returned unexpected Content-Type:`,u={json:"application/json",multipart:"multipart/mixed"},c=1e3,d=[429,503];e.createGraphQLClient=function({headers:e,url:t,fetchApi:n=m,retries:f=0,logger:p}){o({client:r,retries:f});const h={headers:e,url:t,retries:f},l=function(e){return r=>{e&&e(r)}}(p),m=function(e,{url:t,headers:n,retries:s}){return async(i,a={})=>{const{variables:u,headers:c,url:d,retries:f}=a,p=JSON.stringify({query:i,variables:u});o({client:r,retries:f});const h=[d??t,{method:"POST",headers:{...n,...c},body:p}];return e(h,1,f??s)}}(function(e,t){const n=async(o,i,a)=>{const u=i+1,f=a+1;let p;try{if(p=await e(...o),t({type:"HTTP-Response",content:{requestParams:o,response:p}}),!p.ok&&d.includes(p.status)&&u<=f)throw new Error;return p}catch(e){if(u<=f)return await async function(e){return new Promise((r=>setTimeout(r,e)))}(c),t({type:"HTTP-Retry",content:{requestParams:o,lastResponse:p,retryAttempt:i,maxRetries:a}}),n(o,u,a);throw new Error(`${r}:${a>0?` Attempted maximum number of ${a} network retries. Last message -`:""} ${s(e)}`)}};return n}(n,l),h),y=function(e){return async(...t)=>{try{const n=await e(...t),{status:s,statusText:o}=n,c=n.headers.get("content-type")||"";return n.ok?c.includes(u.json)?async function(e){const{errors:t,data:n,extensions:s}=await e.json();return{...n?{data:n}:{},...s?{extensions:s}:{},...t||!n?{errors:{networkStatusCode:e.status,message:t?i:`${r}: An unknown error has occurred. The API did not return a data object or any errors in its response.`,...t?{graphQLErrors:t}:{}}}:{}}}(n):{errors:{networkStatusCode:s,message:`${a} ${c}`}}:{errors:{networkStatusCode:s,message:o}}}catch(e){return{errors:{message:s(e)}}}}}(m);return{config:h,fetch:m,request:y}}})); | ||
/*! shopify/graphql-client@0.9.0 -- Copyright (c) 2023-present, Shopify Inc. -- license (MIT): https://github.com/Shopify/shopify-api-js/blob/main/LICENSE.md */ | ||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).ShopifyGraphQLClient={})}(this,(function(e){"use strict";const t="GraphQL Client",r=0,n=3,s="An error occurred while fetching from the API. Review 'graphQLErrors' for details.",o="Response returned unexpected Content-Type:",i="An unknown error has occurred. The API did not return a data object or any errors in its response.",a={json:"application/json",multipart:"multipart/mixed"},c=1e3,u=[429,503];function f(e,r=t){return e.startsWith(`${r}`)?e:`${r}: ${e}`}function d(e){return e instanceof Error?e.message:JSON.stringify(e)}function p({client:e,retries:t}){if(void 0!==t&&("number"!=typeof t||t<r||t>n))throw new Error(`${e}: The provided "retries" value (${t}) is invalid - it cannot be less than ${r} or greater than ${n}`)}function l(e,t){return t&&("object"!=typeof t||Array.isArray(t)||"object"==typeof t&&Object.keys(t).length>0)?{[e]:t}:{}}function h({clientLogger:e,fetchApi:r=fetch,client:n=t,defaultRetryWaitTime:s=c,retriableCodes:o=u}){const i=async(t,a,c)=>{const u=a+1,p=c+1;let l;try{if(l=await r(...t),e({type:"HTTP-Response",content:{requestParams:t,response:l}}),!l.ok&&o.includes(l.status)&&u<=p)throw new Error;return l}catch(r){if(u<=p){const r=l?.headers.get("Retry-After");return await async function(e){return new Promise((t=>setTimeout(t,e)))}(r?parseInt(r,10):s),e({type:"HTTP-Retry",content:{requestParams:t,lastResponse:l,retryAttempt:a,maxRetries:c}}),i(t,u,c)}throw new Error(f(`${c>0?`Attempted maximum number of ${c} network retries. Last message - `:""}${d(r)}`,n))}};return i}e.createGraphQLClient=function({headers:e,url:r,fetchApi:n=w,retries:u=0,logger:y}){p({client:t,retries:u});const m={headers:e,url:r,retries:u},g=function(e){return t=>{e&&e(t)}}(y),w=function(e,{url:r,headers:n,retries:s}){return async(o,i={})=>{const{variables:a,headers:c,url:u,retries:f}=i,d=JSON.stringify({query:o,variables:a});p({client:t,retries:f});const l=Object.fromEntries(Object.entries({...n,...c}).map((([e,t])=>[e,Array.isArray(t)?t.join(", "):t.toString()])));return e([u??r,{method:"POST",headers:l,body:d}],1,f??s)}}(h({fetchApi:n,clientLogger:g,defaultRetryWaitTime:c}),m),b=function(e){return async(...t)=>{try{const r=await e(...t),{status:n,statusText:c}=r,u=r.headers.get("content-type")||"";return r.ok?u.includes(a.json)?async function(e){const{errors:t,data:r,extensions:n}=await e.json();return{...l("data",r),...l("extensions",n),...t||!r?{errors:{networkStatusCode:e.status,message:f(t?s:i),...l("graphQLErrors",t),response:e}}:{}}}(r):{errors:{networkStatusCode:n,message:f(`${o} ${u}`),response:r}}:{errors:{networkStatusCode:n,message:f(c),response:r}}}catch(e){return{errors:{message:d(e)}}}}}(w);return{config:m,fetch:w,request:b}}})); | ||
//# sourceMappingURL=graphql-client.min.js.map |
{ | ||
"name": "@shopify/graphql-client", | ||
"version": "0.8.0", | ||
"version": "0.9.0", | ||
"description": "Shopify GraphQL Client - A lightweight generic GraphQL JS client to interact with Shopify GraphQL APIs", | ||
@@ -90,3 +90,3 @@ "repository": { | ||
"jest-fetch-mock": "^3.0.3", | ||
"regenerator-runtime": "^0.14.0", | ||
"regenerator-runtime": "^0.14.1", | ||
"web-streams-polyfill": "^3.2.1" | ||
@@ -93,0 +93,0 @@ }, |
@@ -37,3 +37,3 @@ # GraphQL Client | ||
| fetchApi? | `(url: string, init?: {method?: string, headers?: HeaderInit, body?: string}) => Promise<Response>` | A replacement `fetch` function that will be used in all client network requests. By default, the client uses `window.fetch()`. | | ||
| logger? | `(logContent: `[HTTPResponseLog](#httpresponselog)`\|`[HTTPRetryLog](#httpretrylog)`) => void` | A logger function that accepts [log content objects](#log-content-types). This logger will be called in certain conditions with contextual information. | | ||
| logger? | `(logContent: `[`HTTPResponseLog`](#httpresponselog)`\|`[`HTTPRetryLog`](#httpretrylog)`) => void` | A logger function that accepts [log content objects](#log-content-types). This logger will be called in certain conditions with contextual information. | | ||
@@ -45,4 +45,4 @@ ## Client properties | ||
| config | `{url: string, headers: {[key: string]: string}, retries: number}` | Configuration for the client | | ||
| fetch | `<TData>(operation: string, options?: `[RequestOptions](#requestoptions-properties)`) => Promise<Response>` | Fetches data from the GraphQL API using the provided GQL operation string and [RequestOptions](#requestoptions-properties) object and returns the network response | | ||
| request | `<TData>(operation: string, options?: `[RequestOptions](#requestoptions-properties)`) => Promise<`[ClientResponse\<TData\>](#ClientResponsetdata)`>` | Fetches data from the GraphQL API using the provided GQL operation string and [RequestOptions](#requestoptions-properties) object and returns a [normalized response object](#clientresponsetdata) | | ||
| fetch | `(operation: string, options?: `[`RequestOptions`](#requestoptions-properties)`) => Promise<Response>` | Fetches data from the GraphQL API using the provided GQL operation string and [`RequestOptions`](#requestoptions-properties) object and returns the network response | | ||
| request | `<TData>(operation: string, options?: `[`RequestOptions`](#requestoptions-properties)`) => Promise<`[`ClientResponse<TData>`](#ClientResponsetdata)`>` | Fetches data from the GraphQL API using the provided GQL operation string and [`RequestOptions`](#requestoptions-properties) object and returns a [normalized response object](#clientresponsetdata) | | ||
@@ -53,3 +53,3 @@ ## `RequestOptions` properties | ||
| ---------- | --------------------- | ---------------------------------------------------------------- | | ||
| variables? | `OperationVariables` | Variable values needed in the graphQL operation | | ||
| variables? | `{[key: string]: any}` | Variable values needed in the graphQL operation | | ||
| url? | `string` | Alternative request API URL | | ||
@@ -63,4 +63,4 @@ | headers? | `{[key: string]: string}` | Additional and/or replacement headers to be used in the request | | ||
| ----------- | --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ||
| data? | `TData \| any` | Data returned from the GraphQL API. If `TData` was provided to the function, the return type is `TData`, else it returns type `any`. | | ||
| errors? | [ResponseErrors](#responseerrors) | Errors object that contains any API or network errors that occured while fetching the data from the API. It does not include any `UserErrors`. | | ||
| data? | `Partial<TData> \| any` | Data returned from the GraphQL API. If `TData` was provided to the function, the return type is `TData`, else it returns type `any`. | | ||
| errors? | [`ResponseErrors`](#responseerrors) | Errors object that contains any API or network errors that occured while fetching the data from the API. It does not include any `UserErrors`. | | ||
| extensions? | `{[key: string]: any}` | Additional information on the GraphQL response data and context. It can include the `context` object that contains the context settings used to generate the returned API response. | | ||
@@ -75,2 +75,3 @@ | ||
| graphQLErrors? | `any[]` | The GraphQL API errors returned by the server | | ||
| response? | `Response` | The raw response object from the network fetch call | | ||
@@ -209,3 +210,3 @@ | ||
| type | `LogType['HTTP-Response']` | The type of log content. Is always set to `HTTP-Response` | | ||
| content | `{`[requestParams](#requestparams)`: [url, init?], response: Response}` | Contextual data regarding the request and received response | | ||
| content | `{`[`requestParams`](#requestparams)`: [url, init?], response: Response}` | Contextual data regarding the request and received response | | ||
@@ -219,3 +220,3 @@ ### `HTTPRetryLog` | ||
| type | `LogType['HTTP-Retry']` | The type of log content. Is always set to `HTTP-Retry` | | ||
| content | `{`[requestParams](#requestparams)`: [url, init?], lastResponse?: Response, retryAttempt: number, maxRetries: number}` | Contextual data regarding the upcoming retry attempt. <br /><br/>`requestParams`: [parameters](#requestparams) used in the request<br/>`lastResponse`: previous response <br/> `retryAttempt`: the current retry attempt count <br/> `maxRetries`: the maximum number of retries | | ||
| content | `{`[`requestParams`](#requestparams)`: [url, init?], lastResponse?: Response, retryAttempt: number, maxRetries: number}` | Contextual data regarding the upcoming retry attempt. <br /><br/>`requestParams`: [parameters](#requestparams) used in the request<br/>`lastResponse`: previous response <br/> `retryAttempt`: the current retry attempt count <br/> `maxRetries`: the maximum number of retries | | ||
@@ -222,0 +223,0 @@ ### `RequestParams` |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
201229
68
1209
222
16