Skip to content

chore: remove legacy Graphene/Flask (post-cutover cleanup) [WIP]#98

Merged
chrisbc merged 13 commits into
deploy-testfrom
chore/remove-legacy-graphene
Jun 26, 2026
Merged

chore: remove legacy Graphene/Flask (post-cutover cleanup) [WIP]#98
chrisbc merged 13 commits into
deploy-testfrom
chore/remove-legacy-graphene

Conversation

@chrisbc

@chrisbc chrisbc commented Jun 25, 2026

Copy link
Copy Markdown
Member

Draft / WIP — the post-cutover legacy cleanup (MIGRATION_LOG.md § Cutover plan §5). Per the plan, this is done entirely in test and iterated here; it promotes to main as one validated PR alongside the migration (your call).

⚠️ Do not merge until the boxes below are checked and cli_ab_test -A prod -B test is 9/9 against the cleaned test stage. Until then the migration runs legacy-alongside (Strawberry serves; Graphene stays as the differential comparator + CompositeRuptureSections delegation target).

Why staged

Deleting Graphene removes two things the new code still uses at runtime/test:

  • the CompositeRuptureSections resolvers delegate to the legacy graphene statics (_GrapheneSections);
  • test_strawberry_parity.py diffs against the graphene schema_root.
    So the resolver de-graphene + a snapshot-based parity test must land before the graphene modules can go.

Checklist

  • Delete dead Flask/wsgi entry: handler.py, solvis_graphql_api.py (Flask) + its test
  • De-graphene the 3 graphene-returning compute fns → return plain data: color_scale.get_colour_scale, location_schema.get_location_detail_list, composite_solution.schema.paginated_filtered_ruptures/build_ruptures_connection
  • Port CompositeRuptureSections resolvers off _GrapheneSections — call get_fault_section_aggregates + the mfd/geojson/colour logic directly (keep those compute fns)
  • Delete graphene modules / strip graphene classes (keep the compute fns): schema.py (root), solution_schema.py, composite_solution/composite_solution.py; strip from location_schema.py, color_scale/color_scale.py, geojson_style.py, composite_rupture_detail.py, composite_rupture_sections.py, filtered_ruptures_args.py, filter_set_logic_options.py
  • Tests: convert test_strawberry_parity.py to snapshot assertions (drop the graphene comparator); delete the 14 graphene-Client(schema_root) test files (corpus replay + cli_ab_test cover the surface); keep the graphene-free fn tests (test_color_scale_fn, test_rupture_sorting_function)
  • Rename strawberry_schema.pyschema.py; fix imports (app.py, tools/schema_parity.py, tests)
  • Drop deps: graphene, graphql-server, flask, flask-cors, serverless-wsgi (python); keep graphql-relay
  • Re-validate: suite + ruff + mypy + corpus replay; deploy to test; cli_ab_test 9/9

chrisbc added 2 commits June 26, 2026 11:11
First step of the post-cutover legacy cleanup. handler.py (serverless-wsgi
container workaround) and solvis_graphql_api.py (Flask app) are dead — app.py's
Mangum handler is the entry. Removed them + the create_app test. Graphene schema
+ the strawberry delegation stay until the resolver de-graphene (checklist in PR).
Suite still green (86 passed).
color_scale/compute.py: graphene-free compute (get_normaliser, log_intervals,
get_colour_values, compute_colour_scale -> ColourScaleResult dataclass).
strawberry QueryRoot.color_scale now uses it instead of the graphene
get_colour_scale. Differential parity green (vs live graphene).
@codecov

codecov Bot commented Jun 25, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.46182% with 19 lines in your changes missing coverage. Please review.
✅ Project coverage is 93.82%. Comparing base (090c3c2) to head (ecf0f3b).
⚠️ Report is 14 commits behind head on deploy-test.

Files with missing lines Patch % Lines
solvis_graphql_api/schema.py 97.76% 3 Missing and 6 partials ⚠️
solvis_graphql_api/color_scale/compute.py 90.80% 4 Missing and 4 partials ⚠️
solvis_graphql_api/geojson_style_util.py 88.88% 0 Missing and 1 partial ⚠️
solvis_graphql_api/tools/schema_parity.py 0.00% 1 Missing ⚠️
Additional details and impacted files
@@               Coverage Diff               @@
##           deploy-test      #98      +/-   ##
===============================================
+ Coverage        92.78%   93.82%   +1.03%     
===============================================
  Files               24       12      -12     
  Lines             1553     1101     -452     
  Branches            72       64       -8     
===============================================
- Hits              1441     1033     -408     
+ Misses              88       48      -40     
+ Partials            24       20       -4     
Flag Coverage Δ
unittests 93.82% <96.46%> (+1.03%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

chrisbc added 11 commits June 26, 2026 11:21
Build the LocationDetail connection directly from location_by_id +
graphql_relay.offset_to_cursor (matches graphene's array cursors) instead of the
graphene get_location_detail_list. Differential parity green (incl. Node id +
radius_geojson).
Replace the legacy paginated_filtered_ruptures + _to_strawberry_rupture_connection
path with graphene-free helpers built directly in strawberry_schema:
- _ATTRIBUTE_COLUMN_MAP (was CompositeRuptureDetail.ATTRIBUTE_COLUMN_MAP)
- _auto_sorted (port of composite_solution.schema.auto_sorted_dataframe)
- _paginated_ruptures (cached.matched_rupture_sections_gdf + graphql_relay cursors)

filter_ruptures resolver now builds RuptureDetailConnection itself. Drops the
_LegacyFilter/_legacy_filter shims and the composite_solution.schema import.

SDL byte-identical; 11/11 differential parity green.
Port the section compute off the legacy graphene CompositeRuptureSections root.
The strawberry type now carries the filter input directly (filter_input Private)
and resolves graphene-free via new module helpers:
- _fault_section_aggregates / _matched_rupture_sections (cached.* wrappers)
- _mfd_histogram_rows (build_mfd port)
- _fault_surfaces_geojson / _fault_traces_geojson (get_colour_values from compute)
- section color_scale via compute_colour_scale with participation-rate fallback

Drops the _GrapheneSections/_GrapheneFilterArgs imports and _graphene_sections_root
shim; _to_strawberry_color_scale no longer needs the legacy .color_map branch.

Adds test_filter_rupture_sections_colour_parity covering the colour paths the plain
sections query skips (section color_scale fallback, fault_surfaces/fault_traces +
color_scale). SDL byte-identical; 12/12 differential parity green.
…free

The strawberry schema imported two pure (pandas/dict) helpers through graphene-
importing modules. Move them to graphene-free homes so the schema names no graphene
module:
- apply_geojson_style -> new geojson_style_util.py (geojson_style.py re-exports it)
- rupture_detail -> cached.py (composite_rupture_detail.py re-exports it)

strawberry_schema now imports both from the graphene-free modules. The legacy graphene
modules keep working via re-export shims until they are deleted at cutover (the package
__init__ still eager-loads them, which is removed then).

SDL byte-identical; 12/12 differential parity green.
…om tests

The 14 legacy graphene-Client behavioural tests now drive the Strawberry schema via a
graphene.test.Client drop-in (tests/_strawberry_client.py). This keeps their broad coverage
of the migrated resolvers — sorting, filter-set logic, pagination, geojson, node ids — which
the parity corpus alone does not reach. 83 pass, 10 skipped.

test_strawberry_parity converted from an in-process differential (needed the graphene oracle)
to a Strawberry-only snapshot guard; golden outputs captured under tests/__snapshots__ from
the proven-identical schema (regenerate with SNAPSHOT_UPDATE=1).

Fixes one resolver bug the broader suite caught: filter_ruptures passed strawberry.UNSET
through as the sortby 'ascending' value (pandas rejected it); now UNSET fields are omitted so
the legacy '.get("ascending", True)' default applies — matching graphene.

No test imports graphene now. SDL byte-identical; ruff clean.
…e-free

Removes the graphene schema entirely now that the Strawberry schema, the test suite, and the
SDL gate no longer depend on it:

deleted: schema.py (root), location_schema.py, solution_schema.py (dead), geojson_style.py,
color_scale/color_scale.py, composite_solution/{composite_rupture_detail,
composite_rupture_sections,composite_solution,filter_set_logic_options,filtered_ruptures_args,
schema}.py, tools/dump_legacy_sdl.py (the legacy SDL baseline is frozen in schema.legacy.graphql).

Two graphene-free helpers the deleted modules held were relocated to stable homes first:
- auto_sorted_dataframe -> new composite_solution/ruptures.py (also used by the sort unit test)
- _solvis_join -> cached.py (its only consumer)
color compute already lived in color_scale/compute.py.

Gutted composite_solution/__init__.py and color_scale/__init__.py (they eager-loaded the
deleted graphene types). Repointed the two compute unit tests (sort, colour) at the new homes
and dropped the stale get_composite_solution monkeypatches in conftest (everything resolves
through cached now).

No 'import graphene' remains under solvis_graphql_api/. SDL byte-identical; 83 pass, 10 skipped.
With the legacy graphene root schema.py deleted, the canonical name is free. Rename the
Strawberry module to solvis_graphql_api/schema.py and repoint importers (app.py, the SDL
parity gate, _strawberry_client shim, the snapshot + corpus-replay tests). Module path only —
local 'as strawberry_schema' aliases kept. Refreshed the module docstring to point at the
graphene-free compute homes (cached, ruptures, color_scale.compute, geojson_style_util).

app + handler import OK; SDL byte-identical; 83 pass, 10 skipped; ruff clean.
Remove graphene, graphql-server, flask, flask-cors and serverless-wsgi from project
dependencies now that no source imports them. Add graphql-relay as an explicit dep — the
schema's relay global-id/cursor encoding used it, but it was only present transitively via
graphene. Relock: graphene 3.4.3, flask, flask-cors, graphql-server, serverless-wsgi (and
blinker/itsdangerous) all removed from the resolved env; graphql-relay 3.2.0 + graphql-core
3.2.11 (via strawberry) remain.

Also narrows the section color_scale normalization default with explicit is-checks (mypy
couldn't narrow 'not in (None, UNSET)').

Verified with graphene physically uninstalled: app imports, 83 pass / 10 skipped, SDL
byte-identical, mypy + ruff clean.
Add the 2026-06-26 cleanup entry (chunks 1-9: graphene removed from source, tests and deps;
test suites repointed at Strawberry; differential parity → snapshot guard) and mark the §5
post-healthy cleanup checklist item.
… (CI cross-platform)

The snapshot conversion (chunk 6) made three parity tests platform-fragile: byte-exact golden
files captured on macOS didn't match ubuntu CI, because shapely buffer coords and matplotlib
hex colours differ in their low float digits. The original differential test was immune (both
schemas ran in one env, so the difference cancelled) — snapshots lost that property.

Keep byte-exact snapshots for the 9 deterministic queries (rounded scalars), and assert the 3
geometry/geojson queries structurally instead: locations_by_id checks the bug-prone global-id
encoding decodes to ('LocationDetail', id) + a non-empty buffer; sections checks counts/extents/
mfd shape + that styling/colour reached every feature. cli_ab_test remains the authoritative
same-data byte-for-byte geojson parity gate (already 9/9).

83 pass / 10 skipped locally; ruff clean.
…apshot)

The locations snapshot re-stored the entire nzshm_common LOCATIONS constant — a ~2MB,
118k-line fixture of library data. Assert structurally instead (count == len(LOCATIONS) +
a known entry). 12 parity tests pass.
@chrisbc chrisbc marked this pull request as ready for review June 26, 2026 00:59
@chrisbc chrisbc merged commit ffeda01 into deploy-test Jun 26, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant