-
Notifications
You must be signed in to change notification settings - Fork 61
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: retry invalid/expired refs (#356)
* feat: retry invalid/expired refs * fix: remove duplicate ref retry logic and support `getFirst` * test: invalid ref retry * feat: allow up to 3 retries before throwing * feat: use a new master ref once a known-stale ref is used * test: simplify test title * docs: update const description
- Loading branch information
1 parent
9da8fdf
commit ee06efa
Showing
19 changed files
with
330 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,170 @@ | ||
import { expect, it, vi } from "vitest" | ||
|
||
import { rest } from "msw" | ||
|
||
import { createTestClient } from "./createClient" | ||
import { mockPrismicRestAPIV2 } from "./mockPrismicRestAPIV2" | ||
|
||
import * as prismic from "../../src" | ||
|
||
type TestInvalidRefRetryArgs = { | ||
run: ( | ||
client: prismic.Client, | ||
params?: Parameters<prismic.Client["get"]>[0], | ||
) => Promise<unknown> | ||
} | ||
|
||
export const testInvalidRefRetry = (args: TestInvalidRefRetryArgs): void => { | ||
it("retries with the master ref when an invalid ref is used", async (ctx) => { | ||
const client = createTestClient({ ctx }) | ||
const badRef = ctx.mock.api.ref().ref | ||
const masterRef = ctx.mock.api.ref().ref | ||
const queryResponse = ctx.mock.api.query({ | ||
documents: [ctx.mock.value.document()], | ||
}) | ||
|
||
const triedRefs = new Set<string | null>() | ||
|
||
mockPrismicRestAPIV2({ ctx, queryResponse }) | ||
const endpoint = new URL( | ||
"documents/search", | ||
`${client.documentAPIEndpoint}/`, | ||
).toString() | ||
ctx.server.use( | ||
rest.get(endpoint, (req) => { | ||
triedRefs.add(req.url.searchParams.get("ref")) | ||
}), | ||
rest.get(endpoint, (_req, res, ctx) => | ||
res.once( | ||
ctx.json({ | ||
type: "api_notfound_error", | ||
message: `Master ref is: ${masterRef}`, | ||
}), | ||
ctx.status(404), | ||
), | ||
), | ||
) | ||
|
||
const consoleWarnSpy = vi | ||
.spyOn(console, "warn") | ||
.mockImplementation(() => void 0) | ||
await args.run(client, { ref: badRef }) | ||
consoleWarnSpy.mockRestore() | ||
|
||
expect([...triedRefs]).toStrictEqual([badRef, masterRef]) | ||
}) | ||
|
||
it("retries with the master ref when an expired ref is used", async (ctx) => { | ||
const client = createTestClient({ ctx }) | ||
const badRef = ctx.mock.api.ref().ref | ||
const masterRef = ctx.mock.api.ref().ref | ||
const queryResponse = ctx.mock.api.query({ | ||
documents: [ctx.mock.value.document()], | ||
}) | ||
|
||
const triedRefs = new Set<string | null>() | ||
|
||
mockPrismicRestAPIV2({ ctx, queryResponse }) | ||
const endpoint = new URL( | ||
"documents/search", | ||
`${client.documentAPIEndpoint}/`, | ||
).toString() | ||
ctx.server.use( | ||
rest.get(endpoint, (req) => { | ||
triedRefs.add(req.url.searchParams.get("ref")) | ||
}), | ||
rest.get(endpoint, (_req, res, ctx) => | ||
res.once( | ||
ctx.json({ message: `Master ref is: ${masterRef}` }), | ||
ctx.status(410), | ||
), | ||
), | ||
) | ||
|
||
const consoleWarnSpy = vi | ||
.spyOn(console, "warn") | ||
.mockImplementation(() => void 0) | ||
await args.run(client, { ref: badRef }) | ||
consoleWarnSpy.mockRestore() | ||
|
||
expect([...triedRefs]).toStrictEqual([badRef, masterRef]) | ||
}) | ||
|
||
it("throws if the maximum number of retries with invalid refs is reached", async (ctx) => { | ||
const client = createTestClient({ ctx }) | ||
const queryResponse = ctx.mock.api.query({ | ||
documents: [ctx.mock.value.document()], | ||
}) | ||
|
||
const triedRefs = new Set<string | null>() | ||
|
||
mockPrismicRestAPIV2({ ctx, queryResponse }) | ||
const endpoint = new URL( | ||
"documents/search", | ||
`${client.documentAPIEndpoint}/`, | ||
).toString() | ||
ctx.server.use( | ||
rest.get(endpoint, (req) => { | ||
triedRefs.add(req.url.searchParams.get("ref")) | ||
}), | ||
rest.get(endpoint, (_req, res, requestCtx) => | ||
res( | ||
requestCtx.json({ | ||
type: "api_notfound_error", | ||
message: `Master ref is: ${ctx.mock.api.ref().ref}`, | ||
}), | ||
requestCtx.status(404), | ||
), | ||
), | ||
) | ||
|
||
const consoleWarnSpy = vi | ||
.spyOn(console, "warn") | ||
.mockImplementation(() => void 0) | ||
await expect(async () => { | ||
await args.run(client) | ||
}).rejects.toThrow(prismic.RefNotFoundError) | ||
consoleWarnSpy.mockRestore() | ||
|
||
expect(triedRefs.size).toBe(3) | ||
}) | ||
|
||
it("fetches a new master ref on subsequent queries if an invalid ref is used", async (ctx) => { | ||
const client = createTestClient({ ctx }) | ||
const queryResponse = ctx.mock.api.query({ | ||
documents: [ctx.mock.value.document()], | ||
}) | ||
|
||
const triedRefs = new Set<string | null>() | ||
|
||
mockPrismicRestAPIV2({ ctx, queryResponse }) | ||
const endpoint = new URL( | ||
"documents/search", | ||
`${client.documentAPIEndpoint}/`, | ||
).toString() | ||
ctx.server.use( | ||
rest.get(endpoint, (req) => { | ||
triedRefs.add(req.url.searchParams.get("ref")) | ||
}), | ||
rest.get(endpoint, (_req, res, requestCtx) => | ||
res.once( | ||
requestCtx.json({ | ||
type: "api_notfound_error", | ||
message: `Master ref is: ${ctx.mock.api.ref().ref}`, | ||
}), | ||
requestCtx.status(404), | ||
), | ||
), | ||
) | ||
|
||
const consoleWarnSpy = vi | ||
.spyOn(console, "warn") | ||
.mockImplementation(() => void 0) | ||
await args.run(client) | ||
consoleWarnSpy.mockRestore() | ||
|
||
await args.run(client) | ||
|
||
expect(triedRefs.size).toBe(3) | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.