From f1e6f9af431285359e6fb440bcb22204ea3452e9 Mon Sep 17 00:00:00 2001 From: Angelo Ashmore Date: Mon, 8 May 2023 16:59:44 -1000 Subject: [PATCH] revert: remove `optimize` parameter (#290) --- src/createClient.ts | 321 ++++++--------------- test/__testutils__/testConcurrentMethod.ts | 288 +++++++----------- test/client-get.test.ts | 40 --- test/client-getRepository.test.ts | 73 +---- 4 files changed, 186 insertions(+), 536 deletions(-) diff --git a/src/createClient.ts b/src/createClient.ts index c396ae65..d47ddad3 100644 --- a/src/createClient.ts +++ b/src/createClient.ts @@ -51,13 +51,6 @@ export const REPOSITORY_CACHE_TTL = 5000; */ export const GET_ALL_QUERY_DELAY = 500; -/** - * The URL search parameter name used for optimizing `/api/v2` requests. The - * value of the parameter effectively forcibly cache-busts `/api/v2` requests - * after REPOSITORY_CACHE_TTL milliseconds have elapsed. - */ -const OPTIMIZE_REPOSITORY_REQUESTS_VALID_UNTIL_PARAMETER_NAME = "x-valid-until"; - /** * Extracts one or more Prismic document types that match a given Prismic * document type. If no matches are found, no extraction is performed and the @@ -266,7 +259,7 @@ export type ClientConfig = { * Node.js, this function must be provided. */ fetch?: FetchLike; -} & OptimizeParams; +}; /** * Parameters for any client method that use `fetch()`. Only a subset of @@ -282,41 +275,6 @@ type FetchParams = { signal?: AbortSignalLike; }; -/** - * Parameters that determine if or how the client is optimized. - */ -type OptimizeParams = { - /** - * Options that determine if or how the client is optimized. - * - * @experimental These options may change in the future. Please treat - * `optimize` as an undocumented feature. - */ - optimize?: { - /** - * Determines if fetching repository metadata should be optimized. The - * optimizations result in faster queries, less network requests, and more - * accurate cache-busting. - * - * Only opt out of this optimization if you know what you are doing. - * - * @defaultValue `true` - */ - repositoryRequests?: boolean; - - /** - * Determines if concurrent requests to the same URL should be - * optimized. The optimizations result in less network requests and quicker - * responses. - * - * Only opt out of this optimization if you know what you are doing. - * - * @defaultValue `true` - */ - concurrentRequests?: boolean; - }; -}; - /** * Parameters specific to client methods that fetch all documents. These methods * start with `getAll` (for example, `getAllByType`). @@ -460,11 +418,6 @@ export class Client { "ref" | "integrationFieldsRef" | "accessToken" | "routes" >; - /** - * Options that determine if or how the client is optimized. - */ - optimize: NonNullable; - /** * The client's ref mode state. This determines which ref is used during * queries. @@ -546,10 +499,6 @@ export class Client { this.routes = options.routes; this.brokenRoute = options.brokenRoute; this.defaultParams = options.defaultParams; - this.optimize = { - repositoryRequests: options.optimize?.repositoryRequests ?? true, - concurrentRequests: options.optimize?.concurrentRequests ?? true, - }; if (options.ref) { this.queryContentFromRef(options.ref); @@ -650,7 +599,7 @@ export class Client { * @returns A paginated response containing the result of the query. */ async get( - params?: Partial & FetchParams & OptimizeParams, + params?: Partial & FetchParams, ): Promise> { const url = await this.buildQueryURL(params); @@ -672,7 +621,7 @@ export class Client { * The first result of the query, if any. */ async getFirst( - params?: Partial & FetchParams & OptimizeParams, + params?: Partial & FetchParams, ): Promise { const actualParams = { ...params }; if (!(params && params.page) && !params?.pageSize) { @@ -715,8 +664,7 @@ export class Client { async dangerouslyGetAll( params: Partial> & GetAllParams & - FetchParams & - OptimizeParams = {}, + FetchParams = {}, ): Promise { const { limit = Infinity, ...actualParams } = params; const resolvedParams = { @@ -770,7 +718,7 @@ export class Client { */ async getByID( id: string, - params?: Partial & FetchParams & OptimizeParams, + params?: Partial & FetchParams, ): Promise { return await this.getFirst( appendFilters(params, filter.at("document.id", id)), @@ -803,7 +751,7 @@ export class Client { */ async getByIDs( ids: string[], - params?: Partial & FetchParams & OptimizeParams, + params?: Partial & FetchParams, ): Promise> { return await this.get( appendFilters(params, filter.in("document.id", ids)), @@ -838,10 +786,7 @@ export class Client { */ async getAllByIDs( ids: string[], - params?: Partial & - GetAllParams & - FetchParams & - OptimizeParams, + params?: Partial & GetAllParams & FetchParams, ): Promise { return await this.dangerouslyGetAll( appendFilters(params, filter.in("document.id", ids)), @@ -877,7 +822,7 @@ export class Client { >( documentType: TDocumentType, uid: string, - params?: Partial & FetchParams & OptimizeParams, + params?: Partial & FetchParams, ): Promise> { return await this.getFirst>( appendFilters(params, [ @@ -919,7 +864,7 @@ export class Client { >( documentType: TDocumentType, uids: string[], - params?: Partial & FetchParams & OptimizeParams, + params?: Partial & FetchParams, ): Promise>> { return await this.get>( appendFilters(params, [ @@ -963,10 +908,7 @@ export class Client { >( documentType: TDocumentType, uids: string[], - params?: Partial & - GetAllParams & - FetchParams & - OptimizeParams, + params?: Partial & GetAllParams & FetchParams, ): Promise[]> { return await this.dangerouslyGetAll< ExtractDocumentType @@ -1005,7 +947,7 @@ export class Client { TDocumentType extends TDocument["type"] = TDocument["type"], >( documentType: TDocumentType, - params?: Partial & FetchParams & OptimizeParams, + params?: Partial & FetchParams, ): Promise> { return await this.getFirst>( appendFilters(params, typeFilter(documentType)), @@ -1035,7 +977,7 @@ export class Client { TDocumentType extends TDocument["type"] = TDocument["type"], >( documentType: TDocumentType, - params?: Partial & FetchParams & OptimizeParams, + params?: Partial & FetchParams, ): Promise>> { return await this.get>( appendFilters(params, typeFilter(documentType)), @@ -1068,8 +1010,7 @@ export class Client { documentType: TDocumentType, params?: Partial> & GetAllParams & - FetchParams & - OptimizeParams, + FetchParams, ): Promise[]> { return await this.dangerouslyGetAll< ExtractDocumentType @@ -1096,7 +1037,7 @@ export class Client { */ async getByTag( tag: string, - params?: Partial & FetchParams & OptimizeParams, + params?: Partial & FetchParams, ): Promise> { return await this.get( appendFilters(params, someTagsFilter(tag)), @@ -1125,8 +1066,7 @@ export class Client { tag: string, params?: Partial> & GetAllParams & - FetchParams & - OptimizeParams, + FetchParams, ): Promise { return await this.dangerouslyGetAll( appendFilters(params, someTagsFilter(tag)), @@ -1151,7 +1091,7 @@ export class Client { */ async getByEveryTag( tags: string[], - params?: Partial & FetchParams & OptimizeParams, + params?: Partial & FetchParams, ): Promise> { return await this.get( appendFilters(params, everyTagFilter(tags)), @@ -1181,8 +1121,7 @@ export class Client { tags: string[], params?: Partial> & GetAllParams & - FetchParams & - OptimizeParams, + FetchParams, ): Promise { return await this.dangerouslyGetAll( appendFilters(params, everyTagFilter(tags)), @@ -1209,7 +1148,7 @@ export class Client { */ async getBySomeTags( tags: string[], - params?: Partial & FetchParams & OptimizeParams, + params?: Partial & FetchParams, ): Promise> { return await this.get( appendFilters(params, someTagsFilter(tags)), @@ -1240,8 +1179,7 @@ export class Client { tags: string[], params?: Partial> & GetAllParams & - FetchParams & - OptimizeParams, + FetchParams, ): Promise { return await this.dangerouslyGetAll( appendFilters(params, someTagsFilter(tags)), @@ -1254,9 +1192,7 @@ export class Client { * * @returns Repository metadata. */ - async getRepository( - params?: FetchParams & OptimizeParams, - ): Promise { + async getRepository(params?: FetchParams): Promise { // TODO: Restore when Authorization header support works in browsers with CORS. // return await this.fetch(this.endpoint); @@ -1266,28 +1202,6 @@ export class Client { url.searchParams.set("access_token", this.accessToken); } - if ( - params?.optimize?.repositoryRequests ?? - this.optimize.repositoryRequests - ) { - // Add a URL parameter to fingerprint the URL. The URL - // should be valid for the duration of - // REPOSITORY_CACHE_TTL. - // - // This fingerprint works as a second "cache" that - // works outside `getRepositoryCache()`. This is useful - // when multiple client instances exist with an - // external cache; all clients can share the same URL - // cache key for the duration of REPOSITORY_CACHE_TTL. - url.searchParams.set( - OPTIMIZE_REPOSITORY_REQUESTS_VALID_UNTIL_PARAMETER_NAME, - ( - Math.floor(Date.now() / REPOSITORY_CACHE_TTL) * REPOSITORY_CACHE_TTL + - REPOSITORY_CACHE_TTL - ).toString(), - ); - } - return await this.fetch(url.toString(), params); } @@ -1300,7 +1214,7 @@ export class Client { * * @returns A list of all refs for the Prismic repository. */ - async getRefs(params?: FetchParams & OptimizeParams): Promise { + async getRefs(params?: FetchParams): Promise { const repository = await this.getRepository(params); return repository.refs; @@ -1313,10 +1227,7 @@ export class Client { * * @returns The ref with a matching ID, if it exists. */ - async getRefByID( - id: string, - params?: FetchParams & OptimizeParams, - ): Promise { + async getRefByID(id: string, params?: FetchParams): Promise { const refs = await this.getRefs(params); return findRefByID(refs, id); @@ -1329,10 +1240,7 @@ export class Client { * * @returns The ref with a matching label, if it exists. */ - async getRefByLabel( - label: string, - params?: FetchParams & OptimizeParams, - ): Promise { + async getRefByLabel(label: string, params?: FetchParams): Promise { const refs = await this.getRefs(params); return findRefByLabel(refs, label); @@ -1344,7 +1252,7 @@ export class Client { * * @returns The repository's master ref. */ - async getMasterRef(params?: FetchParams & OptimizeParams): Promise { + async getMasterRef(params?: FetchParams): Promise { const refs = await this.getRefs(params); return findMasterRef(refs); @@ -1356,7 +1264,7 @@ export class Client { * * @returns A list of all Releases for the Prismic repository. */ - async getReleases(params?: FetchParams & OptimizeParams): Promise { + async getReleases(params?: FetchParams): Promise { const refs = await this.getRefs(params); return refs.filter((ref) => !ref.isMasterRef); @@ -1369,10 +1277,7 @@ export class Client { * * @returns The Release with a matching ID, if it exists. */ - async getReleaseByID( - id: string, - params?: FetchParams & OptimizeParams, - ): Promise { + async getReleaseByID(id: string, params?: FetchParams): Promise { const releases = await this.getReleases(params); return findRefByID(releases, id); @@ -1385,10 +1290,7 @@ export class Client { * * @returns The ref with a matching label, if it exists. */ - async getReleaseByLabel( - label: string, - params?: FetchParams & OptimizeParams, - ): Promise { + async getReleaseByLabel(label: string, params?: FetchParams): Promise { const releases = await this.getReleases(params); return findRefByLabel(releases, label); @@ -1399,7 +1301,7 @@ export class Client { * * @returns A list of all tags used in the repository. */ - async getTags(params?: FetchParams & OptimizeParams): Promise { + async getTags(params?: FetchParams): Promise { try { const tagsForm = await this.getCachedRepositoryForm("tags", params); @@ -1426,17 +1328,12 @@ export class Client { */ async buildQueryURL({ signal, - optimize, ...params - }: Partial & - FetchParams & - OptimizeParams = {}): Promise { - const ref = - params.ref || (await this.getResolvedRefString({ signal, optimize })); + }: Partial & FetchParams = {}): Promise { + const ref = params.ref || (await this.getResolvedRefString({ signal })); const integrationFieldsRef = params.integrationFieldsRef || - (await this.getCachedRepository({ signal, optimize })) - .integrationFieldsRef || + (await this.getCachedRepository({ signal })).integrationFieldsRef || undefined; return buildQueryURL(this.endpoint, { @@ -1470,9 +1367,7 @@ export class Client { * session. The user should be redirected to this URL. */ async resolvePreviewURL( - args: ResolvePreviewArgs & - FetchParams & - OptimizeParams, + args: ResolvePreviewArgs & FetchParams, ): Promise { let documentID: string | undefined | null = args.documentID; let previewToken: string | undefined | null = args.previewToken; @@ -1509,7 +1404,6 @@ export class Client { if (documentID != null && previewToken != null) { const document = await this.getByID(documentID, { - optimize: args.optimize, signal: args.signal, ref: previewToken, lang: "*", @@ -1717,25 +1611,16 @@ export class Client { * * @returns Cached repository metadata. */ - private async getCachedRepository( - params?: FetchParams & OptimizeParams, - ): Promise { + private async getCachedRepository(params?: FetchParams): Promise { if ( - params?.optimize?.repositoryRequests ?? - this.optimize.repositoryRequests + !this.cachedRepository || + Date.now() >= this.cachedRepositoryExpiration ) { - if ( - !this.cachedRepository || - Date.now() >= this.cachedRepositoryExpiration - ) { - this.cachedRepositoryExpiration = Date.now() + REPOSITORY_CACHE_TTL; - this.cachedRepository = await this.getRepository(params); - } - - return this.cachedRepository; - } else { - return await this.getRepository(params); + this.cachedRepositoryExpiration = Date.now() + REPOSITORY_CACHE_TTL; + this.cachedRepository = await this.getRepository(params); } + + return this.cachedRepository; } /** @@ -1749,7 +1634,7 @@ export class Client { */ private async getCachedRepositoryForm( name: string, - params?: FetchParams & OptimizeParams, + params?: FetchParams, ): Promise
{ const cachedRepository = await this.getCachedRepository(params); const form = cachedRepository.forms[name]; @@ -1788,9 +1673,7 @@ export class Client { * * @returns The ref to use during a query. */ - private async getResolvedRefString( - params?: FetchParams & OptimizeParams, - ): Promise { + private async getResolvedRefString(params?: FetchParams): Promise { if (this.refState.autoPreviewsEnabled) { let previewRef: string | undefined; @@ -1852,94 +1735,56 @@ export class Client { */ private async fetch( url: string, - params: FetchParams & OptimizeParams = {}, + params: FetchParams = {}, ): Promise { - let res: FetchJobResult; - - if ( - params.optimize?.concurrentRequests ?? - this.optimize.concurrentRequests - ) { - let job: Promise; - - const fetchJobKeyInstance = new URL(url); - fetchJobKeyInstance.searchParams.delete( - OPTIMIZE_REPOSITORY_REQUESTS_VALID_UNTIL_PARAMETER_NAME, - ); - const fetchJobKey = fetchJobKeyInstance.toString(); - - // `fetchJobs` is keyed twice: first by the URL and again by is - // signal, if one exists. - // - // Using two keys allows us to reuse fetch requests for - // equivalent URLs, but eject when we detect unique signals. - if ( - this.fetchJobs[fetchJobKey] && - this.fetchJobs[fetchJobKey].has(params.signal) - ) { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - job = this.fetchJobs[fetchJobKey].get(params.signal)!; - } else { - this.fetchJobs[fetchJobKey] = this.fetchJobs[fetchJobKey] || new Map(); - - job = this.fetchFn(url, { - signal: params.signal, - }) - .then(async (res) => { - // We can assume Prismic REST API responses - // will have a `application/json` - // Content Type. If not, this will - // throw, signaling an invalid - // response. - // eslint-disable-next-line @typescript-eslint/no-explicit-any - let json: any = undefined; - try { - json = await res.json(); - } catch { - // noop - } - - return { - status: res.status, - json, - }; - }) - .finally(() => { - this.fetchJobs[fetchJobKey].delete(params.signal); - - if (this.fetchJobs[fetchJobKey].size === 0) { - delete this.fetchJobs[fetchJobKey]; - } - }); - - this.fetchJobs[fetchJobKey].set(params.signal, job); - } + let job: Promise; - res = await job; + // `fetchJobs` is keyed twice: first by the URL and again by is + // signal, if one exists. + // + // Using two keys allows us to reuse fetch requests for + // equivalent URLs, but eject when we detect unique signals. + if (this.fetchJobs[url] && this.fetchJobs[url].has(params.signal)) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + job = this.fetchJobs[url].get(params.signal)!; } else { - const job = await this.fetchFn(url, { + this.fetchJobs[url] = this.fetchJobs[url] || new Map(); + + job = this.fetchFn(url, { signal: params.signal, - }); + }) + .then(async (res) => { + // We can assume Prismic REST API responses + // will have a `application/json` + // Content Type. If not, this will + // throw, signaling an invalid + // response. + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let json: any = undefined; + try { + json = await res.json(); + } catch { + // noop + } + + return { + status: res.status, + json, + }; + }) + .finally(() => { + this.fetchJobs[url].delete(params.signal); - // We can assume Prismic REST API responses - // will have a `application/json` - // Content Type. If not, this will - // throw, signaling an invalid - // response. - // eslint-disable-next-line @typescript-eslint/no-explicit-any - let json: any = undefined; - try { - json = await job.json(); - } catch { - // noop - } + if (this.fetchJobs[url].size === 0) { + delete this.fetchJobs[url]; + } + }); - res = { - status: job.status, - json, - }; + this.fetchJobs[url].set(params.signal, job); } + const res = await job; + if (res.status !== 404 && res.json == null) { throw new PrismicError(undefined, url, res.json); } diff --git a/test/__testutils__/testConcurrentMethod.ts b/test/__testutils__/testConcurrentMethod.ts index 9e95722b..311294ac 100644 --- a/test/__testutils__/testConcurrentMethod.ts +++ b/test/__testutils__/testConcurrentMethod.ts @@ -1,4 +1,4 @@ -import { TestContext, expect, it, vi } from "vitest"; +import { expect, it, vi } from "vitest"; import { rest } from "msw"; import fetch from "node-fetch"; @@ -10,116 +10,6 @@ import { mockPrismicRestAPIV2 } from "./mockPrismicRestAPIV2"; import * as prismic from "../../src"; -type TestConcurrentMethodArgsMode = - | "get" - | "getAll" - | "repository" - | "tags" - | "resolvePreview" - | "NOT-SHARED___graphQL"; - -const EXPECTED_REQUEST_COUNTS: Record< - TestConcurrentMethodArgsMode, - { - enabled: number; - enabledClient?: number; - enabledRequest?: number; - disabled: number; - disabledClient?: number; - disabledRequest?: number; - } -> = { - get: { - enabled: 8, - disabled: 14, - }, - getAll: { - enabled: 13, - disabled: 22, - }, - repository: { - enabled: 5, - disabled: 8, - }, - tags: { - enabled: 8, - disabled: 14, - }, - resolvePreview: { - enabled: 8, - disabled: 14, - }, - "NOT-SHARED___graphQL": { - enabled: 9, - disabled: 14, - disabledRequest: 9, - }, -}; - -type RunTestArgs = Pick & { - ctx: TestContext; - clientParams?: prismic.ClientConfig; - requestParams?: Parameters[0]; - expectedNumberOfRequests: number; -}; - -const runTest = async (args: RunTestArgs) => { - const fetchSpy = vi.fn(fetch); - const controller1 = new AbortController(); - const controller2 = new AbortController(); - - const ref1 = args.ctx.mock.api.ref({ isMasterRef: true }); - const ref2 = args.ctx.mock.api.ref({ isMasterRef: false }); - ref2.id = "id"; - ref2.label = "label"; - const repositoryResponse = args.ctx.mock.api.repository(); - repositoryResponse.refs = [ref1, ref2]; - - const queryResponse = createPagedQueryResponses({ ctx: args.ctx }); - - mockPrismicRestAPIV2({ - ctx: args.ctx, - repositoryResponse, - queryResponse, - // A small delay is needed to simulate a real network - // request. Without the delay, network requests would - // not be shared. - queryDelay: 10, - }); - - const client = createTestClient({ - clientConfig: { ...args.clientParams, fetch: fetchSpy }, - }); - - const graphqlURL = `https://${createRepositoryName()}.cdn.prismic.io/graphql`; - const graphqlResponse = { foo: "bar" }; - args.ctx.server.use( - rest.get(graphqlURL, (_req, res, ctx) => { - return res(ctx.json(graphqlResponse)); - }), - ); - - await Promise.all([ - // Shared - args.run(client, args.requestParams), - args.run(client, args.requestParams), - - // Shared - args.run(client, { ...args.requestParams, signal: controller1.signal }), - args.run(client, { ...args.requestParams, signal: controller1.signal }), - - // Shared - args.run(client, { ...args.requestParams, signal: controller2.signal }), - args.run(client, { ...args.requestParams, signal: controller2.signal }), - ]); - - // Not shared - await args.run(client, args.requestParams); - await args.run(client, args.requestParams); - - expect(fetchSpy.mock.calls.length).toBe(args.expectedNumberOfRequests); -}; - type TestConcurrentMethodArgs = { run: ( client: prismic.Client, @@ -138,83 +28,109 @@ export const testConcurrentMethod = ( description: string, args: TestConcurrentMethodArgs, ): void => { - const expectedRequestCounts = EXPECTED_REQUEST_COUNTS[args.mode]; + it.concurrent(description, async (ctx) => { + const fetchSpy = vi.fn(fetch); + const controller1 = new AbortController(); + const controller2 = new AbortController(); + + const ref1 = ctx.mock.api.ref({ isMasterRef: true }); + const ref2 = ctx.mock.api.ref({ isMasterRef: false }); + ref2.id = "id"; + ref2.label = "label"; + const repositoryResponse = ctx.mock.api.repository(); + repositoryResponse.refs = [ref1, ref2]; - it.concurrent(`${description} (default)`, async (ctx) => { - await runTest({ + const queryResponse = createPagedQueryResponses({ ctx }); + + mockPrismicRestAPIV2({ ctx, - run: args.run, - expectedNumberOfRequests: expectedRequestCounts.enabled, + repositoryResponse, + queryResponse, + // A small delay is needed to simulate a real network + // request. Without the delay, network requests would + // not be shared. + queryDelay: 10, }); - }); - it.concurrent( - `${description} (client optimize.concurrentRequests = true)`, - async (ctx) => { - await runTest({ - ctx, - run: args.run, - clientParams: { - optimize: { - concurrentRequests: true, - }, - }, - expectedNumberOfRequests: - expectedRequestCounts.enabledClient ?? expectedRequestCounts.enabled, - }); - }, - ); - - it.concurrent( - `${description} (request optimize.concurrentRequests = true)`, - async (ctx) => { - await runTest({ - ctx, - run: args.run, - requestParams: { - optimize: { - concurrentRequests: true, - }, - }, - expectedNumberOfRequests: - expectedRequestCounts.enabledRequest ?? expectedRequestCounts.enabled, - }); - }, - ); - - it.concurrent( - `${description} (client optimize.concurrentRequests = false)`, - async (ctx) => { - await runTest({ - ctx, - run: args.run, - clientParams: { - optimize: { - concurrentRequests: false, - }, - }, - expectedNumberOfRequests: - expectedRequestCounts.disabledClient ?? - expectedRequestCounts.disabled, - }); - }, - ); - - it.concurrent( - `${description} (request optimize.concurrentRequests = false)`, - async (ctx) => { - await runTest({ - ctx, - run: args.run, - requestParams: { - optimize: { - concurrentRequests: false, - }, - }, - expectedNumberOfRequests: - expectedRequestCounts.disabledRequest ?? - expectedRequestCounts.disabled, - }); - }, - ); + const client = createTestClient({ clientConfig: { fetch: fetchSpy } }); + + const graphqlURL = `https://${createRepositoryName()}.cdn.prismic.io/graphql`; + const graphqlResponse = { foo: "bar" }; + ctx.server.use( + rest.get(graphqlURL, (req, res, ctx) => { + if (req.headers.get("Prismic-Ref") === ref1.ref) { + return res(ctx.json(graphqlResponse)); + } + }), + ); + + await Promise.all([ + // Shared + args.run(client), + args.run(client), + + // Shared + args.run(client, { signal: controller1.signal }), + args.run(client, { signal: controller1.signal }), + + // Shared + args.run(client, { signal: controller2.signal }), + args.run(client, { signal: controller2.signal }), + ]); + + // Not shared + await args.run(client); + await args.run(client); + + // `get` methods use a total of 6 requests: + // - 1x /api/v2 (shared across all requests) + // - 5x /api/v2/documents/search + + // `getAll` methods use a total of 11 requests: + // - 1x /api/v2 (shared across all requests) + // - 10x /api/v2/documents/search + + switch (args.mode) { + case "get": { + expect(fetchSpy.mock.calls.length).toBe(8); + + break; + } + + case "getAll": { + expect(fetchSpy.mock.calls.length).toBe(13); + + break; + } + + case "repository": { + expect(fetchSpy.mock.calls.length).toBe(5); + + break; + } + + case "tags": { + expect(fetchSpy.mock.calls.length).toBe(8); + + break; + } + + case "resolvePreview": { + expect(fetchSpy.mock.calls.length).toBe(8); + + break; + } + + // GraphQL requests are not shared. + case "NOT-SHARED___graphQL": { + expect(fetchSpy.mock.calls.length).toBe(9); + + break; + } + + default: { + throw new Error(`Invalid mode: ${args.mode}`); + } + } + }); }; diff --git a/test/client-get.test.ts b/test/client-get.test.ts index 36d744af..a1f8f0d9 100644 --- a/test/client-get.test.ts +++ b/test/client-get.test.ts @@ -92,46 +92,6 @@ it("uses cached repository metadata within the client's repository cache TTL", a ); }); -it("does not use the cached repository metadata within the client's repository cache TTL when optimize.repositoryRequests is disabled", async (ctx) => { - const fetchSpy = vi.fn(fetch); - - const client = createTestClient({ - clientConfig: { - fetch: fetchSpy, - optimize: { - repositoryRequests: false, - }, - }, - }); - - const repositoryResponse1 = ctx.mock.api.repository(); - repositoryResponse1.refs = [ctx.mock.api.ref({ isMasterRef: true })]; - mockPrismicRestAPIV2({ ctx, repositoryResponse: repositoryResponse1 }); - - await client.get(); - - // This response response will be used on the second request. - const repositoryResponse2 = ctx.mock.api.repository(); - repositoryResponse2.refs = [ctx.mock.api.ref({ isMasterRef: true })]; - mockPrismicRestAPIV2({ ctx, repositoryResponse: repositoryResponse2 }); - - await client.get(); - - const getRequests = fetchSpy.mock.calls - .filter( - (call) => - new URL(call[0] as string).pathname === "/api/v2/documents/search", - ) - .map((call) => call[0] as string); - - expect(new URL(getRequests[0]).searchParams.get("ref")).toBe( - repositoryResponse1.refs[0].ref, - ); - expect(new URL(getRequests[1]).searchParams.get("ref")).toBe( - repositoryResponse2.refs[0].ref, - ); -}, 10000); - testAbortableMethod("is abortable with an AbortController", { run: (client, signal) => client.get({ signal }), }); diff --git a/test/client-getRepository.test.ts b/test/client-getRepository.test.ts index 3aa73ca9..65f12ebe 100644 --- a/test/client-getRepository.test.ts +++ b/test/client-getRepository.test.ts @@ -1,6 +1,4 @@ -import { expect, it, vi } from "vitest"; - -import fetch from "node-fetch"; +import { expect, it } from "vitest"; import { createTestClient } from "./__testutils__/createClient"; import { mockPrismicRestAPIV2 } from "./__testutils__/mockPrismicRestAPIV2"; @@ -41,75 +39,6 @@ it("includes access token if configured", async (ctx) => { expect(res).toStrictEqual(repositoryResponse); }); -it("uses a cache-busting URL parameter by default", async (ctx) => { - mockPrismicRestAPIV2({ ctx }); - - const fetchSpy = vi.fn(fetch); - const client = createTestClient({ - clientConfig: { - fetch: fetchSpy, - }, - }); - - await client.getRepository(); - - const call = fetchSpy.mock.calls.find( - (call) => new URL(call[0] as string).pathname === "/api/v2", - ); - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const url = new URL(call![0] as string); - - expect(url.searchParams.has("x-valid-until")).toBe(true); -}); - -it("uses a cache-busting URL parameter when `optimizeRepositoryRequest` is `true`", async (ctx) => { - mockPrismicRestAPIV2({ ctx }); - - const fetchSpy = vi.fn(fetch); - const client = createTestClient({ - clientConfig: { - optimize: { - repositoryRequests: true, - }, - fetch: fetchSpy, - }, - }); - - await client.getRepository(); - - const call = fetchSpy.mock.calls.find( - (call) => new URL(call[0] as string).pathname === "/api/v2", - ); - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const url = new URL(call![0] as string); - - expect(url.searchParams.has("x-valid-until")).toBe(true); -}); - -it("does not use a cache-busting URL parameter when `optimizeRepositoryRequest` is `false`", async (ctx) => { - mockPrismicRestAPIV2({ ctx }); - - const fetchSpy = vi.fn(fetch); - const client = createTestClient({ - clientConfig: { - optimize: { - repositoryRequests: false, - }, - fetch: fetchSpy, - }, - }); - - await client.getRepository(); - - const call = fetchSpy.mock.calls.find( - (call) => new URL(call[0] as string).pathname === "/api/v2", - ); - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const url = new URL(call![0] as string); - - expect(url.searchParams.has("x-valid-until")).toBe(false); -}); - testAbortableMethod("is abortable with an AbortController", { run: (client, signal) => client.getRepository({ signal }), });