diff --git a/functions/_common/grapherRenderer.ts b/functions/_common/grapherRenderer.ts index 34fc801c20b..e9d782e7d95 100644 --- a/functions/_common/grapherRenderer.ts +++ b/functions/_common/grapherRenderer.ts @@ -12,6 +12,7 @@ import LatoMedium from "../_common/fonts/LatoLatin-Medium.ttf.bin" import LatoBold from "../_common/fonts/LatoLatin-Bold.ttf.bin" import PlayfairSemiBold from "../_common/fonts/PlayfairDisplayLatin-SemiBold.ttf.bin" import { Env } from "../grapher/thumbnail/[slug].js" +import { OwidTable } from "@ourworldindata/core-table" declare global { // eslint-disable-next-line no-var @@ -126,19 +127,20 @@ const extractOptions = (params: URLSearchParams): ImageOptions => { return options as ImageOptions } -async function fetchAndRenderGrapherToSvg({ - slug, - options, - searchParams, - env, -}: { - slug: string - options: ImageOptions - searchParams: URLSearchParams - env: Env -}) { - const grapherLogger = new TimeLogger("grapher") - +async function initGrapher( + { + slug, + options, + searchParams, + env, + }: { + slug: string + options: ImageOptions + searchParams: URLSearchParams + env: Env + }, + grapherLogger: TimeLogger +) { // Fetch grapher config and extract it from the HTML const grapherConfig: GrapherInterface = await env.ASSETS.fetch( new URL(`/grapher/${slug}`, env.url) @@ -165,9 +167,46 @@ async function fetchAndRenderGrapherToSvg({ grapher.shouldIncludeDetailsInStaticExport = options.details grapherLogger.log("grapherInit") + return grapher +} + +export async function fetchCsvForGrapher(slug: string, env: Env) { + const grapherLogger = new TimeLogger("grapher") + const grapher = await initGrapher( + { + slug, + options: TWITTER_OPTIONS, + searchParams: new URLSearchParams(""), + env, + }, + grapherLogger + ) + await grapher.downloadLegacyDataFromOwidVariableIds() + return new Response(grapher.inputTable.toPrettyCsv(), { + headers: { + "Content-Type": "text/csv", + }, + }) +} + +async function fetchAndRenderGrapherToSvg({ + slug, + options, + searchParams, + env, +}: { + slug: string + options: ImageOptions + searchParams: URLSearchParams + env: Env +}) { + const grapherLogger = new TimeLogger("grapher") + const grapher = await initGrapher( + { slug, options, searchParams, env }, + grapherLogger + ) const promises = [] promises.push(grapher.downloadLegacyDataFromOwidVariableIds()) - if (options.details && grapher.detailsOrderedByReference.length) { promises.push( await fetch("https://ourworldindata.org/dods.json") diff --git a/functions/grapher/csv/[slug].ts b/functions/grapher/csv/[slug].ts new file mode 100644 index 00000000000..1424919b644 --- /dev/null +++ b/functions/grapher/csv/[slug].ts @@ -0,0 +1,51 @@ +import { fetchCsvForGrapher } from "../../_common/grapherRenderer.js" +import { IRequestStrict, Router, error } from "itty-router" + +export interface Env { + ASSETS: { + fetch: typeof fetch + } + url: URL +} + +const router = Router() +router + .get( + "/grapher/csv/:slug", + async ({ params: { slug } }, { searchParams: _ }, env) => + fetchCsvForGrapher(slug, env) + ) + .all("*", () => error(404, "Route not defined")) + +export const onRequestGet: PagesFunction = async (ctx) => { + const { request, env } = ctx + + const url = new URL(request.url) + const shouldCache = !url.searchParams.has("nocache") + + const cache = caches.default + if (shouldCache) { + const maybeCached = await cache.match(request) + if (maybeCached) return maybeCached + } + + console.log("Handling", request.url, request.headers.get("User-Agent")) + + return router + .handle(request, url, { ...env, url }, ctx) + .then((resp: Response) => { + if (shouldCache) { + resp.headers.set( + "Cache-Control", + "public, s-maxage=3600, max-age=3600" + ) + ctx.waitUntil(caches.default.put(request, resp.clone())) + } else + resp.headers.set( + "Cache-Control", + "public, s-maxage=0, max-age=0, must-revalidate" + ) + return resp + }) + .catch((e) => error(500, e)) +}