Migrate Graphene → Strawberry (Phases 0–5, combined)#95
Merged
Conversation
… sibling) Captures the repo-specific inventory and runbook deltas before code: - PynamoDB surface is a single wrapped model (BinaryLargeObject) - repo ships its own cross-stage differential validator (cli_ab_test) -> reuse for Phase 5 instead of building drive_live.py - still on poetry; yarn age gate missing; container entry is the serverless-wsgi handler.py workaround (-> Mangum) - both serverless-s3-local + serverless-dynamodb load at boot (§4.7) Epic: GNS-Science/nshm-toshi-api#359
chore: migrate Poetry → uv, flake8/black/isort → ruff
- add tools/dump_legacy_sdl.py (stdout->stderr guard; pyvista warns on import) - commit schema.legacy.graphql (449 lines, 29 types) - log: poetry2uv verified green (ruff + 62 tests); Phase 0 SDL done Refs #86
- 11 real queries lifted from the test suite into fixtures/corpus/*.graphql - test_corpus_replay.py validates each against schema_root (no fixtures needed); re-points to the Strawberry schema later to catch field/type drift - kororaa UI queries to follow (real frontend traffic) Refs #86
- vendor 3 pure-solvis kororaa Relay ops, transformed from gateway shape (SOLVIS_<f> -> <f>, on Query -> on QueryRoot); validated against schema - adds coverage the tests lacked: locations_by_id + radius_geojson(style:), rich rupture node fields (fault_surfaces, magnitude, rate_weighted_mean, ...) - ComboRuptureMap* skipped (multi-API: also fetch KORORAA_textual_content) - log the stale-checkout lesson: kororaa was 9mo behind and showed retired inversion_solution/analyse_solution usage -> false parity alarm Refs #86
- add strawberry/fastapi/mangum/pydantic (legacy graphene/flask kept until cutover)
- app.py: FastAPI + GraphQLRouter + Mangum + CORS
- strawberry_schema.py: minimal QueryRoot.about at SDL parity
(auto_camel_case=False, root type QueryRoot to match legacy + kororaa corpus)
- serverless.yml: image.command -> solvis_graphql_api.app.handler; drop
serverless-wsgi plugin + custom.wsgi
- Dockerfile: CMD -> app.handler; drop bogus /bin/bash -c entrypoint
- verified: amd64 docker build OK + handler imports in-image; uvicorn {about}
byte-matches legacy; full legacy suite 77 passed
Refs #86
…r API) - BinaryLargeObjectModel(pynamodb.Model) -> pydantic BaseModel + thin boto3 DynamoDB CRUD; public BinaryLargeObject wrapper API unchanged so cli + composite_solution.cached are untouched - object_meta stored as a JSON string (JSONAttribute parity; ints not Decimals) - migrate()/drop_tables()/tables preserved (create_table wait -> boto3 waiter) - drop pynamodb dependency; uv re-locked (164 pkgs) - verified: moto contract 4/4, full suite 77 passed, ruff + mypy clean Refs #86
…rity - strawberry_schema.py: full 29-type schema; tools/schema_parity.py gate confirms byte-identical vs schema.legacy.graphql - custom Node interface (id: ID!, not strawberry.relay/GlobalID); QueryRoot root type; auto_camel_case=False; relay PageInfo camelCase via name= - nullability (X|None for nullable-with-default), UNSET vs =null args, and the partial 3-key style-arg default all matched - resolvers reuse the graphene-free compute helpers; light query-root resolvers verified vs legacy output - corpus replay re-pointed at the Strawberry schema (15 pass); add strawberry mypy plugin (new code mypy-clean) - archive-dependent runtime resolvers stubbed -> Phase 3 (need fixtures + tests) Refs #86
- test_strawberry_parity.py: run each query vs Graphene AND Strawberry, assert identical data (in-process analogue of cli_ab_test) - implement CompositeRuptureDetail computed fields (magnitude/area/length/ rake_mean/rate_*) via rupture_detail; fix get_parent_fault_names shape - harness caught 2 bugs the SDL gate can't: relay global-id encoding (id -> to_global_id, Model C1) and float opacity defaults (1.0 not 1); switch the style-arg default to a mapping (instance default breaks graphql-core coercion) - SDL parity still byte-identical; suite 85 passed; ruff + mypy(new) clean Refs #86
- reuse legacy paginated_filtered_ruptures via a dict adapter (_legacy_filter) that exposes the dict+attr access it expects; convert the graphene connection to the Strawberry RuptureDetailConnection - fix: filter_set_options default must be a plain mapping, not an input instance (an instance default trips strawberry argument coercion) - same class of fix as the style-arg default; SDL parity preserved - test_filter_ruptures_parity: pagination + cursors + node global-id + magnitude all byte-match legacy (note: legacy can't serve pageInfo.hasPreviousPage) Refs #86
- CompositeRuptureSections: delegate every resolver to the legacy graphene static resolvers (aggregates/geojson/MFD/colour reused verbatim) via a graphene root built from the input; colour-scale normalisation converted to string value - node(): legacy has no get_node -> null on both schemas (parity-correct stub) - mypy: None-safe _fso_dict, edges/normalisation narrowing, type:ignore on untyped graphene constructors -> new code clean - 11 differential parity checks (full surface) + suite 88 passed; SDL byte-identical Refs #86
- container: amd64 build green with migrated deps; 360MB (< 500MB aim, no multi-stage); Mangum handler + full schema import in-image - pip-audit runtime deps: no known vulnerabilities - plugin chain (s3rver + serverless-dynamodb) loads; no resolutions needed - drop dead serverless-wsgi npm plugin + stale requirements_* scripts; yarn lockfile updated, --immutable clean (§4.6) - memory stays 2096; CI already on -uv test+deploy workflows Refs #86
- docs/PHASE5_CUTOVER.md: in-place replacement, rollback (image-only, no new write shapes), differential validation via the built-in cli_ab_test (prod-vs-test, covers the full migrated surface), promote + 30-min watch, post-healthy legacy-cleanup scope - reuse cli_ab_test (no drive_live.py needed); in-process parity already locked by test_strawberry_parity.py - actual deploy/promote needs AWS creds + prod go-ahead Refs #86
…ationEnum type stub CI gates on mypy. poetry2uv added follow_untyped_imports=true, making mypy type-check into the legacy graphene + solvis code (29 errors). Restore the original lenient posture (ignore_missing_imports only); add a TYPE_CHECKING stub enum for SetOperationEnum so it stays a valid type without follow. Whole package mypy-clean (30 files); parity + 11 differential tests + ruff green.
# Conflicts: # README.md # poetry.lock
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## deploy-test #95 +/- ##
===============================================
- Coverage 95.79% 91.86% -3.94%
===============================================
Files 20 24 +4
Lines 1142 1548 +406
Branches 54 71 +17
===============================================
+ Hits 1094 1422 +328
- Misses 34 102 +68
- Partials 14 24 +10
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
This was referenced Jun 24, 2026
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.
Lands the full Graphene → Strawberry / FastAPI / Mangum migration on
deploy-testas one merge → one test-stage deploy (the stack's intermediate phases aren't independently deployable — runbook G7). Consolidates the per-phase stack #88–#94 + the currentdeploy-test(#81/#82/#84) + a CI mypy fix.Epic: GNS-Science/nshm-toshi-api#359 · Tracking: #86 · Log:
docs/MIGRATION_LOG.mdWhat's included (per-phase detail in #88–#94)
schema.legacy.graphqldocs/PHASE5_CUTOVER.md)CI fix folded in
poetry2uvhad setfollow_untyped_imports = true, making mypy type-check into the legacy graphene/solvis code (29 errors) — restored the original lenient posture + aSetOperationEnumTYPE_CHECKINGstub. Whole package mypy-clean (30 files).Verification (local, on the merged tree)
deploy-testis a clean ancestor (no merge conflict)After merge
Closes #88–#94 (landed via this PR). Merging deploys to the test stage → run
cli_ab_test -A prod -B test; prod promote is a separatedeploy-test → mainstep (seePHASE5_CUTOVER.md).