Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ add fetching grapher configs by UUID #3879

Merged
merged 1 commit into from
Sep 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 32 additions & 8 deletions functions/_common/grapherRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,8 +166,20 @@ interface FetchGrapherConfigResult {
etag: string | undefined
}

interface GrapherSlug {
type: "slug"
id: string
}

interface GrapherUuid {
type: "uuid"
id: string
}

type GrapherIdentifier = GrapherSlug | GrapherUuid

export async function fetchUnparsedGrapherConfig(
slug: string,
identifier: GrapherIdentifier,
env: Env,
etag?: string
) {
Expand All @@ -178,10 +190,15 @@ export async function fetchUnparsedGrapherConfig(
? [env.GRAPHER_CONFIG_R2_BUCKET_PATH]
: ["by-branch", env.CF_PAGES_BRANCH]

const directory =
identifier.type === "slug"
? R2GrapherConfigDirectory.publishedGrapherBySlug
: R2GrapherConfigDirectory.byUUID

Comment on lines +193 to +197
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A kingdom for match() :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought about pulling in ts-pattern but it felt a bit premature at this point :)

const key = excludeUndefined([
...topLevelDirectory,
R2GrapherConfigDirectory.publishedGrapherBySlug,
`${slug}.json`,
directory,
`${identifier.id}.json`,
]).join("/")

console.log("fetching grapher config from this key", key)
Expand All @@ -197,8 +214,8 @@ export async function fetchUnparsedGrapherConfig(
const topLevelDirectory = env.GRAPHER_CONFIG_R2_BUCKET_FALLBACK_PATH
const fallbackKey = excludeUndefined([
topLevelDirectory,
R2GrapherConfigDirectory.publishedGrapherBySlug,
`${slug}.json`,
directory,
`${identifier.id}.json`,
]).join("/")
fallbackUrl = new URL(
fallbackKey,
Expand All @@ -211,11 +228,15 @@ export async function fetchUnparsedGrapherConfig(
}

export async function fetchGrapherConfig(
slug: string,
identifier: GrapherIdentifier,
env: Env,
etag?: string
): Promise<FetchGrapherConfigResult> {
const fetchResponse = await fetchUnparsedGrapherConfig(slug, env, etag)
const fetchResponse = await fetchUnparsedGrapherConfig(
identifier,
env,
etag
)

if (fetchResponse.status === 404) {
// we throw 404 errors instead of returning a 404 response so that the router
Expand Down Expand Up @@ -253,7 +274,10 @@ async function fetchAndRenderGrapherToSvg(
): Promise<string> {
const grapherLogger = new TimeLogger("grapher")

const grapherConfigResponse = await fetchGrapherConfig(slug, env)
const grapherConfigResponse = await fetchGrapherConfig(
{ type: "slug", id: slug },
env
)

if (grapherConfigResponse.status === 404) {
// we throw 404 errors instad of returning a 404 response so that the router
Expand Down
6 changes: 5 additions & 1 deletion functions/grapher/[slug].ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,11 @@ async function handleConfigRequest(
const shouldCache = searchParams.get("nocache") === null
console.log("Preparing json response for ", slug)

const grapherPageResp = await fetchUnparsedGrapherConfig(slug, env, etag)
const grapherPageResp = await fetchUnparsedGrapherConfig(
{ type: "slug", id: slug },
env,
etag
)

if (grapherPageResp.status === 304) {
console.log("Returning 304 for ", slug)
Expand Down
66 changes: 66 additions & 0 deletions functions/grapher/by-uuid/[uuid].ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { Env } from "../../_common/env.js"
import { fetchGrapherConfig } from "../../_common/grapherRenderer.js"
import { IRequestStrict, Router, error, StatusError } from "itty-router"

const router = Router<IRequestStrict, [URL, Env, string]>()
router
.get(
"/grapher/by-uuid/:uuid.config.json",
async ({ params: { uuid } }, { searchParams }, env, etag) =>
handleConfigRequest(uuid, searchParams, env, etag)
)
.all("*", () => error(404, "Route not defined"))

export const onRequest: PagesFunction = async (context) => {
const { request, env } = context
const url = new URL(request.url)

return router
.fetch(
request,
url,
{ ...env, url },
request.headers.get("if-none-match")
)
.catch((e) => {
if (e instanceof StatusError) {
return error(e.status, e.message)
}

return error(500, e)
})
}

async function handleConfigRequest(
uuid: string,
searchParams: URLSearchParams,
env: Env,
etag: string | undefined
) {
const shouldCache = searchParams.get("nocache") === null
console.log("Preparing json response for uuid ", uuid)

const grapherPageResp = await fetchGrapherConfig(
{ type: "uuid", id: uuid },
env,
etag
)

if (grapherPageResp.status === 304) {
return new Response(null, { status: 304 })
}

console.log("Grapher page response", grapherPageResp.grapherConfig.title)

const cacheControl = shouldCache
? "public, s-maxage=3600, max-age=0, must-revalidate"
: "public, s-maxage=0, max-age=0, must-revalidate"

return new Response(JSON.stringify(grapherPageResp.grapherConfig), {
headers: {
"content-type": "application/json",
"Cache-Control": cacheControl,
ETag: grapherPageResp.etag,
},
})
}