Skip to content
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
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
13 changes: 8 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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

Expand All @@ -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

Expand Down
3 changes: 1 addition & 2 deletions netlify/edge-functions/opensheet.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
8 changes: 7 additions & 1 deletion test/json-api.test.js
Original file line number Diff line number Diff line change
@@ -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;
});
13 changes: 10 additions & 3 deletions test/opensheet.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand All @@ -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 () => {
Expand Down