diff --git a/framework/spree/api/utils/create-api-fetch.ts b/framework/spree/api/utils/create-api-fetch.ts index de4af5d3d..d9d78bb60 100644 --- a/framework/spree/api/utils/create-api-fetch.ts +++ b/framework/spree/api/utils/create-api-fetch.ts @@ -11,7 +11,7 @@ import getSpreeSdkMethodFromEndpointPath from 'framework/spree/utils/getSpreeSdk import { SpreeSdkVariables } from 'framework/spree/types' import SpreeSdkMethodFromEndpointPathError from 'framework/spree/errors/SpreeSdkMethodFromEndpointPathError' import { GraphQLFetcher, GraphQLFetcherResult } from '@commerce/api' -import createCreateFetchFetcher from '../../utils/createCreateFetchFetcher' +import createCustomizedFetchFetcher from '../../utils/createCustomizedFetchFetcher' import fetch, { Request } from 'node-fetch' const createApiFetch: ( @@ -22,10 +22,13 @@ const createApiFetch: ( const client = makeClient({ host: requireConfigValue('spreeApiHost') as string, fetcherType: 'custom', - createFetcher: createCreateFetchFetcher({ - fetch, - requestClass: Request, - }), + createFetcher: (fetcherOptions) => { + return createCustomizedFetchFetcher({ + fetch, + requestConstructor: Request, + ...fetcherOptions, + }) + }, }) return async (url, queryData = {}, fetchOptions = {}) => { diff --git a/framework/spree/fetcher.ts b/framework/spree/fetcher.ts index e16c19ca3..64797ee32 100644 --- a/framework/spree/fetcher.ts +++ b/framework/spree/fetcher.ts @@ -12,15 +12,18 @@ import getSpreeSdkMethodFromEndpointPath from './utils/getSpreeSdkMethodFromEndp import SpreeSdkMethodFromEndpointPathError from './errors/SpreeSdkMethodFromEndpointPathError' import type { SpreeSdkVariables } from './types' import type { GraphQLFetcherResult } from '@commerce/api' -import createCreateFetchFetcher from './utils/createCreateFetchFetcher' +import createCustomizedFetchFetcher from './utils/createCustomizedFetchFetcher' const client = makeClient({ host: requireConfigValue('spreeApiHost') as string, fetcherType: 'custom', - createFetcher: createCreateFetchFetcher({ - fetch: globalThis.fetch, - requestClass: globalThis.Request, - }), + createFetcher: (fetcherOptions) => { + return createCustomizedFetchFetcher({ + fetch: globalThis.fetch, + requestConstructor: globalThis.Request, + ...fetcherOptions, + }) + }, }) const fetcher: Fetcher< diff --git a/framework/spree/utils/createCreateFetchFetcher.ts b/framework/spree/utils/createCreateFetchFetcher.ts deleted file mode 100644 index 165c6f18f..000000000 --- a/framework/spree/utils/createCreateFetchFetcher.ts +++ /dev/null @@ -1,77 +0,0 @@ -import * as qs from 'qs' -import { errors } from '@spree/storefront-api-v2-sdk' -import type { CreateFetcher } from '@spree/storefront-api-v2-sdk/types/interfaces/ClientConfig' - -const createCreateFetchFetcher = - ({ fetch: rawFetch, requestClass }): CreateFetcher => - // TODO: Fix rawFetch any type. - (fetcherOptions) => { - const { FetchError } = errors - const sharedHeaders = { - 'Content-Type': 'application/json', - } - - return { - fetch: async (fetchOptions) => { - // This fetcher always returns request equal null, - // because @vercel/fetch doesn't accept a Request object as argument - // and it's not used by NJC anyway. - try { - const { url, params, method, headers } = fetchOptions - const absoluteUrl = new URL(url, fetcherOptions.host) - let payload - - switch (method.toUpperCase()) { - case 'PUT': - case 'POST': - case 'DELETE': - case 'PATCH': - payload = { body: JSON.stringify(params) } - break - default: - payload = null - absoluteUrl.search = qs.stringify(params, { - arrayFormat: 'brackets', - }) - } - - const request: Request = new requestClass(absoluteUrl.toString(), { - method: method.toUpperCase(), - headers: { ...sharedHeaders, ...headers }, - ...payload, - }) - - try { - const response: Response = await rawFetch(request) - - const data = await response.json() - - if (!response.ok) { - // Use the "traditional" approach and reject non 2xx responses. - throw new FetchError(response, request, data) - } - - return { - // Add response key to the prototype so it can be passed inside the GraphQLFetcherResult type. - // TODO: Search for a better solution than adding response to the prototype. - data: Object.setPrototypeOf({ data }, { response }), - } - } catch (error) { - if (error instanceof TypeError) { - throw new FetchError(null, request, null) - } - - throw error - } - } catch (error) { - if (error instanceof FetchError) { - throw error - } - - throw new FetchError(null, null, null, error.message) - } - }, - } - } - -export default createCreateFetchFetcher diff --git a/framework/spree/utils/createCustomizedFetchFetcher.ts b/framework/spree/utils/createCustomizedFetchFetcher.ts new file mode 100644 index 000000000..9ed35a82f --- /dev/null +++ b/framework/spree/utils/createCustomizedFetchFetcher.ts @@ -0,0 +1,81 @@ +import * as qs from 'qs' +import { errors } from '@spree/storefront-api-v2-sdk' +import type { CreateCustomizedFetchFetcher } from '@spree/storefront-api-v2-sdk/types/interfaces/CreateCustomizedFetchFetcher' + +const createCustomizedFetchFetcher: CreateCustomizedFetchFetcher = ( + fetcherOptions +) => { + const { FetchError } = errors + const sharedHeaders = { + 'Content-Type': 'application/json', + } + + const { host, fetch, requestConstructor } = fetcherOptions + + return { + fetch: async (fetchOptions) => { + // This fetcher always returns request equal null, + // because @vercel/fetch doesn't accept a Request object as argument + // and it's not used by NJC anyway. + try { + const { url, params, method, headers } = fetchOptions + const absoluteUrl = new URL(url, host) + let payload + + switch (method.toUpperCase()) { + case 'PUT': + case 'POST': + case 'DELETE': + case 'PATCH': + payload = { body: JSON.stringify(params) } + break + default: + payload = null + absoluteUrl.search = qs.stringify(params, { + arrayFormat: 'brackets', + }) + } + + const request: Request = new requestConstructor( + absoluteUrl.toString(), + { + method: method.toUpperCase(), + headers: { ...sharedHeaders, ...headers }, + ...payload, + } + ) + + try { + const response: Response = await fetch(request) + + const data = await response.json() + + if (!response.ok) { + // Use the "traditional" approach and reject non 2xx responses. + throw new FetchError(response, request, data) + } + + return { + // Add response key to the prototype so it can be passed inside the GraphQLFetcherResult type. + // TODO: Search for a better solution than adding response to the prototype. + data: Object.setPrototypeOf({ data }, { response }), + } + } catch (error) { + if (error instanceof TypeError) { + throw new FetchError(null, request, null) + } + + throw error + } + } catch (error) { + if (error instanceof FetchError) { + throw error + } + + throw new FetchError(null, null, null, error.message) + } + }, + } +} + +export default createCustomizedFetchFetcher