diff --git a/src/buildQueryURL.ts b/src/buildQueryURL.ts index fe862ef9..6588c097 100644 --- a/src/buildQueryURL.ts +++ b/src/buildQueryURL.ts @@ -1,6 +1,19 @@ import { castArray } from "./lib/castArray" import { devMsg } from "./lib/devMsg" +import { version } from "../package.json" + +/** + * The query parameter used to indicate if the client is in development mode to + * the API. + */ +const PRISMIC_DEV_PARAM = "x-d" + +/** + * The query parameter used to indicate the version of the client to the API. + */ +const PRISMIC_CLIENT_VERSION_PARAM = "x-c" + /** * Create a union of the given object's values, and optionally specify which * keys to get the values from. @@ -374,5 +387,11 @@ export const buildQueryURL = ( } } + url.searchParams.set(PRISMIC_CLIENT_VERSION_PARAM, `js-${version}`) + + if (process.env.NODE_ENV === "development") { + url.searchParams.set(PRISMIC_DEV_PARAM, "1") + } + return url.toString() } diff --git a/test/buildQueryURL.test.ts b/test/buildQueryURL.test.ts index 5396d043..4fc5d2b8 100644 --- a/test/buildQueryURL.test.ts +++ b/test/buildQueryURL.test.ts @@ -1,12 +1,15 @@ import { expect, it, vi } from "vitest" +import { version } from "../package.json" + import * as prismic from "../src" const endpoint = prismic.getRepositoryEndpoint("qwerty") +const xClientVersionParam = `&x-c=js-${version}` it("includes ref", () => { expect(prismic.buildQueryURL(endpoint, { ref: "ref" })).toBe( - "https://qwerty.cdn.prismic.io/api/v2/documents/search?ref=ref", + `https://qwerty.cdn.prismic.io/api/v2/documents/search?ref=ref${xClientVersionParam}`, ) }) @@ -19,7 +22,7 @@ it("supports single filter", () => { }), ), ).toBe( - "https://qwerty.cdn.prismic.io/api/v2/documents/search?q=[[has(my.document.title)]]&ref=ref", + `https://qwerty.cdn.prismic.io/api/v2/documents/search?q=[[has(my.document.title)]]&ref=ref${xClientVersionParam}`, ) // TODO: Remove when we remove support for deprecated `predicates` argument. @@ -31,7 +34,7 @@ it("supports single filter", () => { }), ), ).toBe( - "https://qwerty.cdn.prismic.io/api/v2/documents/search?q=[[has(my.document.title)]]&ref=ref", + `https://qwerty.cdn.prismic.io/api/v2/documents/search?q=[[has(my.document.title)]]&ref=ref${xClientVersionParam}`, ) }) @@ -47,7 +50,7 @@ it("supports multiple filters", () => { }), ), ).toBe( - "https://qwerty.cdn.prismic.io/api/v2/documents/search?q=[[has(my.document.title)]]&q=[[has(my.document.subtitle)]]&ref=ref", + `https://qwerty.cdn.prismic.io/api/v2/documents/search?q=[[has(my.document.title)]]&q=[[has(my.document.subtitle)]]&ref=ref${xClientVersionParam}`, ) // TODO: Remove when we remove support for deprecated `predicates` argument. @@ -62,7 +65,7 @@ it("supports multiple filters", () => { }), ), ).toBe( - "https://qwerty.cdn.prismic.io/api/v2/documents/search?q=[[has(my.document.title)]]&q=[[has(my.document.subtitle)]]&ref=ref", + `https://qwerty.cdn.prismic.io/api/v2/documents/search?q=[[has(my.document.title)]]&q=[[has(my.document.subtitle)]]&ref=ref${xClientVersionParam}`, ) }) @@ -85,7 +88,7 @@ it("supports params", () => { }), ), ).toBe( - "https://qwerty.cdn.prismic.io/api/v2/documents/search?ref=ref&access_token=accessToken&pageSize=1&page=1&after=after&fetch=fetch&fetchLinks=fetchLinks&graphQuery=graphQuery&lang=lang&orderings=[orderings]&routes=routes&brokenRoute=brokenRoute", + `https://qwerty.cdn.prismic.io/api/v2/documents/search?ref=ref&access_token=accessToken&pageSize=1&page=1&after=after&fetch=fetch&fetchLinks=fetchLinks&graphQuery=graphQuery&lang=lang&orderings=[orderings]&routes=routes&brokenRoute=brokenRoute${xClientVersionParam}`, ) }) @@ -105,7 +108,9 @@ it("ignores nullish params", () => { orderings: undefined, }), ), - ).toBe("https://qwerty.cdn.prismic.io/api/v2/documents/search?ref=ref") + ).toBe( + `https://qwerty.cdn.prismic.io/api/v2/documents/search?ref=ref${xClientVersionParam}`, + ) }) it("supports array fetch param", () => { @@ -117,7 +122,7 @@ it("supports array fetch param", () => { }), ), ).toBe( - "https://qwerty.cdn.prismic.io/api/v2/documents/search?ref=ref&fetch=title,subtitle", + `https://qwerty.cdn.prismic.io/api/v2/documents/search?ref=ref&fetch=title,subtitle${xClientVersionParam}`, ) }) @@ -130,7 +135,7 @@ it("supports array fetchLinks param", () => { }), ), ).toBe( - "https://qwerty.cdn.prismic.io/api/v2/documents/search?ref=ref&fetchLinks=page.link,page.second_link", + `https://qwerty.cdn.prismic.io/api/v2/documents/search?ref=ref&fetchLinks=page.link,page.second_link${xClientVersionParam}`, ) }) @@ -144,7 +149,7 @@ it("supports empty orderings param", () => { }), ), ).toBe( - "https://qwerty.cdn.prismic.io/api/v2/documents/search?ref=ref&orderings=[]", + `https://qwerty.cdn.prismic.io/api/v2/documents/search?ref=ref&orderings=[]${xClientVersionParam}`, ) expect( @@ -155,7 +160,7 @@ it("supports empty orderings param", () => { }), ), ).toBe( - "https://qwerty.cdn.prismic.io/api/v2/documents/search?ref=ref&orderings=[]", + `https://qwerty.cdn.prismic.io/api/v2/documents/search?ref=ref&orderings=[]${xClientVersionParam}`, ) }) @@ -169,7 +174,7 @@ it("supports array orderings param", () => { }), ), ).toBe( - "https://qwerty.cdn.prismic.io/api/v2/documents/search?ref=ref&orderings=[page.title,page.subtitle]", + `https://qwerty.cdn.prismic.io/api/v2/documents/search?ref=ref&orderings=[page.title,page.subtitle]${xClientVersionParam}`, ) }) @@ -183,7 +188,7 @@ it("supports setting direction of ordering param", () => { }), ), ).toBe( - "https://qwerty.cdn.prismic.io/api/v2/documents/search?ref=ref&orderings=[page.title,page.subtitle]", + `https://qwerty.cdn.prismic.io/api/v2/documents/search?ref=ref&orderings=[page.title,page.subtitle]${xClientVersionParam}`, ) expect( @@ -198,7 +203,7 @@ it("supports setting direction of ordering param", () => { }), ), ).toBe( - "https://qwerty.cdn.prismic.io/api/v2/documents/search?ref=ref&orderings=[page.title+desc,page.subtitle+desc]", + `https://qwerty.cdn.prismic.io/api/v2/documents/search?ref=ref&orderings=[page.title+desc,page.subtitle+desc]${xClientVersionParam}`, ) }) @@ -219,7 +224,7 @@ it("supports single item routes param", () => { ).toBe( `https://qwerty.cdn.prismic.io/api/v2/documents/search?ref=ref&routes=[${JSON.stringify( route, - )}]`, + )}]${xClientVersionParam}`, ) }) @@ -247,10 +252,28 @@ it("supports array routes param", () => { ).toBe( `https://qwerty.cdn.prismic.io/api/v2/documents/search?ref=ref&routes=${JSON.stringify( routes, - )}`, + )}${xClientVersionParam}`, ) }) +it("forwards `x-c` param in production", () => { + expect(prismic.buildQueryURL(endpoint, { ref: "ref" })).toBe( + `https://qwerty.cdn.prismic.io/api/v2/documents/search?ref=ref${xClientVersionParam}`, + ) +}) + +it("forwards `x-c` and `x-d` param in development", () => { + const originalEnv = { ...process.env } + + process.env.NODE_ENV = "development" + + expect(prismic.buildQueryURL(endpoint, { ref: "ref" })).toBe( + `https://qwerty.cdn.prismic.io/api/v2/documents/search?ref=ref${xClientVersionParam}&x-d=1`, + ) + + process.env = originalEnv +}) + it("warns if NODE_ENV is development and a string is provided to `orderings`", () => { const originalEnv = { ...process.env } diff --git a/test/client-buildQueryURL.test.ts b/test/client-buildQueryURL.test.ts index 1aff7f14..92a1b730 100644 --- a/test/client-buildQueryURL.test.ts +++ b/test/client-buildQueryURL.test.ts @@ -4,8 +4,12 @@ import { createTestClient } from "./__testutils__/createClient" import { getMasterRef } from "./__testutils__/getMasterRef" import { mockPrismicRestAPIV2 } from "./__testutils__/mockPrismicRestAPIV2" +import { version } from "../package.json" + import type * as prismic from "../src" +const xClientVersion = `js-${version}` + it("builds a query URL using the master ref", async (ctx) => { const repositoryResponse = ctx.mock.api.repository() const ref = getMasterRef(repositoryResponse) @@ -21,6 +25,7 @@ it("builds a query URL using the master ref", async (ctx) => { const expectedSearchParams = new URLSearchParams({ ref, + "x-c": xClientVersion, }) url.searchParams.delete("integrationFieldsRef") url.searchParams.sort() @@ -31,6 +36,21 @@ it("builds a query URL using the master ref", async (ctx) => { expect(url.searchParams.toString()).toBe(expectedSearchParams.toString()) }) +it("includes the `x-d` param in development", async (ctx) => { + const originalEnv = { ...process.env } + process.env.NODE_ENV = "development" + + mockPrismicRestAPIV2({ ctx }) + + const client = createTestClient() + const res = await client.buildQueryURL() + const url = new URL(res) + + expect(url.searchParams.get("x-d")).toBe("1") + + process.env = originalEnv +}) + it("includes params if provided", async (ctx) => { const params: prismic.BuildQueryURLArgs = { accessToken: "custom-accessToken", @@ -49,6 +69,7 @@ it("includes params if provided", async (ctx) => { lang: params.lang?.toString() ?? "", // TODO: Remove when Authorization header support works in browsers with CORS. access_token: params.accessToken ?? "", + "x-c": xClientVersion, }) url.searchParams.delete("integrationFieldsRef") @@ -78,6 +99,7 @@ it("includes default params if provided", async (ctx) => { lang: clientConfig.defaultParams?.lang?.toString() ?? "", // TODO: Remove when Authorization header support works in browsers with CORS. access_token: clientConfig.accessToken ?? "", + "x-c": xClientVersion, }) url.searchParams.delete("integrationFieldsRef") @@ -111,6 +133,7 @@ it("merges params and default params if provided", async (ctx) => { page: clientConfig.defaultParams?.page?.toString() ?? "", // TODO: Remove when Authorization header support works in browsers with CORS. access_token: clientConfig.accessToken ?? "", + "x-c": xClientVersion, }) url.searchParams.delete("integrationFieldsRef")