From e50e29b148c70d742d23f3c27d1cfcf26998237c Mon Sep 17 00:00:00 2001 From: Patrick Tobias Date: Tue, 26 Aug 2025 04:10:51 +0200 Subject: [PATCH] Remove hardcoded API key and update docs --- AGENTS.md | 1 + README.md | 13 ++++++++----- netlify/edge-functions/opensheet.js | 3 +-- test/json-api.test.js | 8 +++++++- test/opensheet.test.js | 13 ++++++++++--- 5 files changed, 27 insertions(+), 11 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index c961592..8f61b0b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -6,5 +6,6 @@ - `public` should contain all public assets. - Serverless functions are in `netlify/edge-functions`. - Keep README and AGENTS.md updated when changes are made +- Sensitive values like API keys must come from environment variables; do not commit secrets. - Pull requests to `main` run `npm test` via GitHub Actions. - Tests are located in `test` and run with `npm test`. diff --git a/README.md b/README.md index 7028de4..412788c 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,8 @@ An API that converts Google Sheets into JSON, served via a Netlify Edge Function. -Visiting the root URL shows a form where you can paste a Google Sheets link. The form rewrites the link to a valid API URL and redirects you there. +Visiting the root URL shows a form where you can paste a Google Sheets link. The +form rewrites the link to a valid API URL and redirects you there. ## Usage @@ -27,9 +28,9 @@ The Edge Function lives in `netlify/edge-functions/opensheet.js`. ### Environment variables -The function looks for a `GOOGLE_API_KEY` value using `process.env` in Node or -`Deno.env.get` in Netlify's Edge runtime. When the variable is absent, a default -API key is used. +The function requires a `GOOGLE_API_KEY` value using `process.env` in Node or +`Deno.env.get` in Netlify's Edge runtime. If the variable is missing, the +function responds with an error. ### Caching @@ -43,7 +44,9 @@ returns live data. npm test ``` -Tests fetch a sample spreadsheet and verify rows are returned, ensure the deployed API returns `[{"headline":"It's working!"}]`, and confirm that requests missing a sheet segment redirect to the first sheet. +Tests mock Google Sheets responses to verify rows are returned, ensure the +deployed API returns `[{"headline":"It's working!"}]`, and confirm that +requests missing a sheet segment redirect to the first sheet. ### Continuous integration diff --git a/netlify/edge-functions/opensheet.js b/netlify/edge-functions/opensheet.js index 681096f..2594950 100644 --- a/netlify/edge-functions/opensheet.js +++ b/netlify/edge-functions/opensheet.js @@ -3,8 +3,7 @@ export default async function handler(request, context) { const GOOGLE_API_KEY = (globalThis.process?.env?.GOOGLE_API_KEY ?? - globalThis.Deno?.env?.get("GOOGLE_API_KEY"))?.trim() || - "AIzaSyC6y93Jo0fA1GH54L7VOkJzgZkiaXSSOOU"; + globalThis.Deno?.env?.get("GOOGLE_API_KEY"))?.trim(); if (!GOOGLE_API_KEY) { return error("Missing GOOGLE_API_KEY environment variable", 500); } diff --git a/test/json-api.test.js b/test/json-api.test.js index 41fe2e7..cc91e89 100644 --- a/test/json-api.test.js +++ b/test/json-api.test.js @@ -1,8 +1,14 @@ import test from 'node:test'; import assert from 'node:assert'; -test('fetches json from deployed API', async () => { +test('parses json from API response', async () => { + const originalFetch = globalThis.fetch; + globalThis.fetch = async () => + new Response(JSON.stringify([{ headline: "It's working!" }])); + const res = await fetch('https://sheets-json-api.netlify.app/1vufOODlks7O9PGak54hMNP4LWBUAoP-XB9n3VW_aw5Y/1'); const data = await res.json(); assert.deepStrictEqual(data, [{ headline: "It's working!" }]); + + globalThis.fetch = originalFetch; }); diff --git a/test/opensheet.test.js b/test/opensheet.test.js index 0b5ce88..d754271 100644 --- a/test/opensheet.test.js +++ b/test/opensheet.test.js @@ -4,6 +4,8 @@ import handler from '../netlify/edge-functions/opensheet.js'; const context = { waitUntil: () => {} }; +process.env.GOOGLE_API_KEY = 'FAKE_KEY'; + test('returns rows from Google Sheet', async () => { globalThis.caches = { default: { @@ -16,11 +18,16 @@ test('returns rows from Google Sheet', async () => { }, }; - const req = new Request('https://example.com/1vufOODlks7O9PGak54hMNP4LWBUAoP-XB9n3VW_aw5Y/1'); + const originalFetch = globalThis.fetch; + globalThis.fetch = async () => + new Response(JSON.stringify({ values: [["name"], ["Ada"]] })); + + const req = new Request('https://example.com/test-sheet/Sheet1'); const res = await handler(req, context); const data = await res.json(); - assert(Array.isArray(data)); - assert.ok(data.length > 0); + assert.deepStrictEqual(data, [{ name: 'Ada' }]); + + globalThis.fetch = originalFetch; }); test('redirects to first sheet when sheet is missing', async () => {