Local dev: end-to-end Lambda + filesystem-backed S3 mirror - #82
Merged
Conversation
Adds a new `handler` subcommand to the lambda CLI that drives `lambda_handler` against a gitignored local directory mirroring the S3 layout, so devs can iterate on `process_file` modules against the zero-credential local web app without S3, AWS credentials, or LocalStack. The CLI stages a `--source` file into `lambda/.local-s3/<raw-bucket>/<instrument-id>/<run-id>/<filename>`, monkey-patches `s3_utils.download_file` / `upload_file` to copy from/to the mirror, and invokes `lambda_handler` with a synthesized S3 event. The reusable patch surface lives in `local_s3_mirror.py` and mirrors what `tests/integration/conftest.py` already does for CI. Documents the workflow in `docs/local-development.md` and links from the "Adding an instrument" guide. Co-authored-by: Cursor <cursoragent@cursor.com>
Wires the Next.js app into the same `lambda/.local-s3` mirror that the
lambda CLI's `data-hub-process handler` writes to, so file downloads,
server-side reads, archive HEAD checks, and watcher-style PUT uploads
work locally without AWS or LocalStack.
When `LOCAL_S3_MIRROR` is set (and `NODE_ENV != production`), the four
helpers in `web/lib/s3.ts` short-circuit the AWS SDK and resolve to
`<root>/<bucket>/<key>` via a dev-only catch-all route at
`/api/_local-s3/{bucket}/{...key}`. The route handles GET (streams the
file with optional `Content-Disposition`) and PUT (writes the request
body to disk via `pipeline()`).
The dev seed copies fixture bytes from `lambda/tests/fixtures/` into
the mirror for qPCR / gel doc / plate reader runs so seeded runs
render real bytes immediately after `make db-reseed`. Other instrument
types still 404 — devs stage real bytes via the lambda CLI.
Co-authored-by: Cursor <cursoragent@cursor.com>
Contributor
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Next.js App Router treats `_`-prefixed folders as private and excludes them from routing, so requests to /api/_local-s3/... 404'd before the handler ran. Co-authored-by: Cursor <cursoragent@cursor.com>
Mirrors the route folder rename in comments, env example, and the local-development guide, and adds a note explaining why the leading underscore breaks routing. Co-authored-by: Cursor <cursoragent@cursor.com>
…instruments qPCR / gel doc / plate reader rows now seed under the kebab-case ids the lambda's `Instrument` enum and `process_file` modules expect, with instrument-shaped run ids (e.g. Experiment_20260129, 26.02.02_10.45.05, 012926_AR_OD600). Fixture-bearing runs render exactly one file row — the real fixture — instead of mixing in synthetic CSV siblings. Co-authored-by: Cursor <cursoragent@cursor.com>
The seed now probes the dev API with the just-minted PAT and, when reachable, spawns `data-hub-process handler` for each canonical fixture-bearing run so processed artifacts (gel-doc PNGs, plate- reader CSVs, qPCR metadata) appear in the dashboard immediately after a reseed. When the API is down, prints a hint and skips — `npm run db:process-fixtures` re-runs the same step on demand once `npm run dev` is up. Co-authored-by: Cursor <cursoragent@cursor.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Local-dev workflow improvements that together let a developer iterate on
process_file()modules and custom run/file viewer components against the local web app without S3, AWS credentials, or LocalStack.1.
data-hub-process handlerCLINew subcommand on the lambda CLI that runs
lambda_handlerend-to-end against a gitignoredlambda/.local-s3/directory mirroring the S3 layout:--source FILEinto<mirror>/<raw-bucket>/<instrument-id>/<run-id>/<filename>.data_hub_shared.s3_utils.download_file/upload_fileto copy viashutil.copy2(no boto3, no credentials).lambda_handler(event, ctx)so the same dispatch path production uses runs against the local Next.js dev API.Reusable patch surface in
lambda/src/data_hub_lambda/local_s3_mirror.pymirrors whattests/integration/conftest.pyalready does for CI.2. Filesystem-backed S3 mirror in
web/When
LOCAL_S3_MIRRORis set (andNODE_ENV != production), the four helpers inweb/lib/s3.tsshort-circuit the AWS SDK and resolve to<root>/<bucket>/<key>via a dev-only catch-all at/api/local-s3/{bucket}/{...key}:Content-Disposition(matches presigned-URL behavior).pipeline()for clean error/abort handling.(Folder is
local-s3not_local-s3— the App Router treats_-prefixed folders as private and excludes them from routing.)Components don't change: every existing run viewer fetches
/api/v1/files/<id>/download, which 302s to whatevergetPresignedDownloadUrlreturns, so swapping the backend is invisible.3. Realistic seed + automated handler run
process_file(qPCR, gel doc, plate reader) seed under their canonical kebab ids (azure-cielo-qpcr,azure-600-gel-doc,spectramax-id3-plate-reader) with instrument-shaped run ids (Experiment_20260129,26.02.02_10.45.05,012926_AR_OD600, …). Other types keepseed-<type>/seed-run-N.data-hub-process handlerfor each fixture-bearing run so processed artifacts (gel-doc PNGs, plate-reader CSVs, qPCR metadata) appear in the dashboard immediately. If the API is down, it prints a hint and skips.npm run db:process-fixturesre-runs the same step on demand oncenpm run devis up.Files
lambda/src/data_hub_lambda/cli.py— newhandlersubcommandlambda/src/data_hub_lambda/local_s3_mirror.py—patched_s3context managerweb/lib/s3-local-mirror.ts—getLocalMirrorRoot,resolveMirrorPath, URL builders, MIME mapweb/app/api/local-s3/[bucket]/[...key]/route.ts— GET + PUT handlersweb/lib/s3.ts— local-mirror branch on each of the four exportsweb/lib/db/seed.ts—CANONICAL_INSTRUMENT_ID,INSTRUMENT_FIXTURES, fixture copyingweb/scripts/process-fixtures.ts— shared probe + spawn helperweb/scripts/process-seeded-fixtures.ts—npm run db:process-fixturesentry pointweb/scripts/seed-database.ts— callsprocessSeededFixturesafter seedingweb/.env.example,docs/local-development.md,docs/guides/adding-an-instrument.md— documentation.gitignore—lambda/.local-s3/Test plan
make check-allpasses (ruff, pyright, prettier, eslint, tsc).make db-reseed && make devthen visithttp://localhost:3000/instruments/azure-cielo-qpcr/runs/Experiment_20260129— fixture CSV downloads and processed metadata is set on the run.http://localhost:3000/instruments/azure-600-gel-doc/runs/26.02.02_10.45.05— TIFF renders and processed PNG appears under the run.cd lambda && uv run data-hub-process handler agilent-4150-tapestation run-1 sample.csv --source <path>— file row appears with viewable bytes.npm run devnot running → seed prints the skip hint;npm run db:process-fixturesafter starting dev produces the same result as case 1.LOCAL_S3_MIRRORwithNODE_ENV=productiondoes not expose/api/local-s3/...(returns 404).web/tests/integration/,lambda/tests/integration/) continues to pass.Made with Cursor