Skip to content

Commit

Permalink
✨ add fetching grapher configs by UUID (#3879)
Browse files Browse the repository at this point in the history
This PR adds fetching of grapher configs from R2 via a CF function by using grapher config UUIDs instead of slugs. Because we have UUIDs for all chart configs (e.g. the ones defined in the ETL grapher config layer) we can get them for charts other than standalone charts that are published (which are the only ones that have valid, unique urls)
  • Loading branch information
danyx23 authored Sep 10, 2024
2 parents 3a153b9 + d8b5bc0 commit 756dd74
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 9 deletions.
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

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,
},
})
}

0 comments on commit 756dd74

Please sign in to comment.