GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — feat(app): FastAPI routes for the dispatcher + wire codec by estebanzimanyi · Pull Request #8 · MobilityDB/MobilityAPI-Python

Conversation

@estebanzimanyi

@estebanzimanyi estebanzimanyi commented May 21, 2026

Copy link
Copy Markdown
Member

Summary

Step 5 of the MobilityAPI ingestion plan: exposes the catalog-driven Dispatcher (#6) + WireCodec (#7) as HTTP endpoints via FastAPI.

File Purpose
mobilityapi/app.py create_app(dispatcher, codec) factory; no global singletons.
mobilityapi/routers/catalog.py GET /catalog (list) + GET /catalog/{name} (one signature).
mobilityapi/routers/functions.py POST /functions/{name} — invoke with JSON body, decode/encode opaque MEOS types via the codec.
tests/test_app.py 11 HTTP-level tests via Starlette TestClient.

Route contract

POST /functions/{name}

Request:

{ "params": { "<arg>": <wire_value>, ... } }

Response (serialised return):

{ "result": "<wire_value>", "encoding": "mfjson" }

Response (scalar return):

{ "result": <value>, "encoding": null }

Error mapping

Status Cause
404 Function name not in dispatcher catalog
400 Missing / extra parameters; param encoding has no codec decoder
500 Result encoding has no codec encoder

Flow

POST /functions/{name}  ──┐
                          │ 1. Decode opaque-type params
                          │    via codec.decode(encoding, wire_value)
                          ▼
                       Dispatcher.dispatch(name, decoded_params)
                          │ 2. Resolver returns a Python callable
                          │    (pymeos.functions.{name} in prod;
                          │     stub in tests).
                          ▼
                       result
                          │ 3. Encode the result via
                          │    codec.encode(encoding, value)
                          │    if catalog marks return as serialised.
                          ▼
                      InvokeResponse(result=…, encoding=…)

Step in the ingestion plan

This is step 5 of the plan documented in docs/MEOS_API_INGESTION_PLAN.md:

  1. ✅ Plan + missing-work scope — docs: MobilityAPI ingestion plan for MEOS-API output + OpenAPI missing-work integration #3
  2. ✅ Vendor MEOS-API artefacts — chore(vendor): vendor the MEOS-API artefacts #4
  3. ✅ Vendor-drift CI workflow — ci(vendor): add a vendor-drift workflow #5
  4. ✅ Dispatcher foundation — feat(dispatcher): catalog-driven MEOS-function dispatcher #6
  5. ✅ WireCodec + resolvers — feat(resolvers,wire): pluggable resolvers + wire-layer codec #7
  6. ✅ FastAPI routes (this PR)
  7. Migrate first resource/* endpoint to use Dispatcher (follow-up)

Tests

$ python3 -m pytest tests/test_app.py tests/test_dispatcher.py tests/test_resolvers.py tests/test_wire.py
38 passed in 0.37s

11 new HTTP tests cover:

  • /catalog lists exposable functions (sorted by name)
  • /catalog/{name} returns full signature; 404 on unknown
  • /functions/{name} invokes scalar, serialised-in/scalar-out, serialised-in/serialised-out shapes
  • 404 on unknown function; 400 on missing param; 400 on extra param
  • 400 on serialised param whose encoding has no decoder

Depends on

  • chore(vendor): vendor the MEOS-API artefacts #4 — vendored MEOS-API artefacts (catalog)
  • feat(dispatcher): catalog-driven MEOS-function dispatcher #6 — Dispatcher
  • feat(resolvers,wire): pluggable resolvers + wire-layer codec #7 — WireCodec + resolvers

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
estebanzimanyi added 3 commits May 20, 2026 17:07
@estebanzimanyi
chore(vendor): vendor MEOS-API artefacts (step 2 of the ingestion plan)
4d5098f
Vendor MobilityAPI's read-only copy of MEOS-API's published catalog +
projection artefacts under `vendor/meos-api/`, plus a Makefile target
`make vendor-meos-api` that regenerates them from upstream.

Files added:

  vendor/meos-api/
    PROVENANCE.json              -- per-artefact source URLs + regenerate cmd
    README.md                    -- refresh procedure
    meos-idl.json                -- 3546 fns / 70 structs / 16 enums
                                    (generated by MEOS-API run.py over
                                    MobilityDB master meos/include headers)
    meos-coverage.json           -- structural worklist (from open PR MobilityDB#4)
    meos-object-model-parity.json -- 29-pair portable-parity (from open PR MobilityDB#10)

The Makefile target clones MEOS-API + MobilityDB shallowly, installs
libclang, runs MEOS-API's `run.py` against MobilityDB's MEOS headers,
and copies the produced JSON artefacts into `vendor/meos-api/`.

Two of the four artefacts (`meos-coverage.json`,
`meos-object-model-parity.json`) currently come from open MEOS-API PR
branches because their generators are not yet on master; PROVENANCE.json
makes that explicit. The Makefile gracefully skips them if absent.

Step 2 of `docs/MEOS_API_INGESTION_PLAN.md`. The drift gate workflow
that fails on stale artefacts is step 3 (separate stacked PR).
@estebanzimanyi
feat(dispatcher): catalog-driven MEOS-function dispatcher (step 4 fou…
6bf5621
…ndation)

Step 4 of docs/MEOS_API_INGESTION_PLAN.md — the catalog-driven
dispatcher that the 5 'REPLACE' resource modules will delegate to in
follow-up PRs.

mobilityapi/dispatcher.py:

- Loads vendor/meos-api/meos-idl.json at construction; honours an
  explicit catalog_path= for tests.
- Filters to network.exposable functions only when enrichment fields
  are present; otherwise treats every function as exposable.
- FunctionSignature dataclass: name / category / params / return_type
  / decode_per_param / encode_return / description, all populated
  from the catalog (enriched or bare).
- dispatch(function_name, params) -> Any:
  * validates the parameter set against the catalog signature
    (missing or unexpected names raise TypeError)
  * resolves the MEOS function via an injected resolver callable
    (production: getattr(pymeos.functions, name); tests: stub
    registry)
  * invokes it with the validated keyword args
  * returns the result; the caller owns encoding to JSON / WKB

tests/test_dispatcher.py (12 tests, all passing locally):

- catalog load (default path, explicit path, FileNotFoundError)
- FunctionSignature.from_catalog_entry (basic fields, enriched wire
  metadata, fallback when wire absent, non-exposable filtering)
- dispatch contract (resolver invocation, unknown function, missing
  param, unexpected param, default stub resolver)
- integration sanity (the 5 MovFeat dispatch candidates named in the
  ingestion plan are present in the vendored catalog)

What this PR does NOT change:

- Existing hand-written endpoint modules in resource/* remain
  unchanged. The plan's 5 REPLACE candidates migrate to the
  dispatcher module-by-module in follow-up PRs:
  temporal_geom_seq/, temporal_geom_query/{velocity,acceleration,
  distance}, temporal_properties/.
- PyMEOS is not yet a dependency (the dispatcher is resolver-
  agnostic; the production resolver lands when the first endpoint
  migrates).

Stacks on MobilityDB#4 (vendor MEOS-API artefacts) so vendor/meos-api/meos-idl.json
is in the tree.
@estebanzimanyi
feat(resolvers,wire): pluggable resolvers + wire-layer codec (step 4)
43841a0
Stacks on the MobilityDB#6 dispatcher foundation.  Adds the two layers a
production endpoint migration needs alongside the dispatcher:

mobilityapi/resolvers.py:
  - stub_resolver(registry)    — explicit name->callable map for tests
  - pymeos_resolver()          — production resolver that looks up
                                 functions in pymeos.functions; lazy-
                                 imports PyMEOS, raises ImportError
                                 with an actionable message when it's
                                 absent
  - default_resolver(prefer_pymeos=True) — production-first probe
                                 with a stub fallback that raises
                                 NotImplementedError on first call

mobilityapi/wire.py:
  - WireCodec                  — keyed by encoding name (mfjson, text,
                                 wkb, hexwkb); decode wire-value to
                                 PyMEOS obj; encode PyMEOS obj back
  - stub_codec(decoders, encoders) — explicit-map constructor for tests
  - pymeos_codec()             — production codec bridging to PyMEOS
                                 factory entry points
  - ENCODING_{MFJSON,TEXT,WKB,HEXWKB} — canonical encoding-name
                                 constants matching the catalog's
                                 x-meos-{decode,encode} fields

mobilityapi/__init__.py re-exports all three names so endpoint
migrations import from one module.

15 new unit tests (tests/test_resolvers.py + tests/test_wire.py),
all passing locally.  CI workflow's pytest job is extended to cover
all three test files together.

Production wiring lands when the first endpoint migrates: install
pymeos, replace getattr(pymeos.functions, name) plumbing with
default_resolver(), point WireCodec at pymeos_codec().  The
intervening migration PRs are bounded ~50-100 LoC each.
@estebanzimanyi
estebanzimanyi force-pushed the feat/step5-fastapi-endpoints branch from c977b29 to a0626ef Compare May 21, 2026 06:41
@estebanzimanyi
feat(app): FastAPI routes for Dispatcher + WireCodec (step 5)
435c657
Adds mobilityapi/app.py + two routers that expose the catalog-driven
Dispatcher (PR MobilityDB#6) and WireCodec (PR MobilityDB#7) as HTTP endpoints:

  - GET  /catalog          -> list dispatcher-exposable functions
  - GET  /catalog/{name}   -> full signature for one function
  - POST /functions/{name} -> invoke with JSON body, decode/encode
                              opaque MEOS types via the WireCodec

The app is built by create_app(dispatcher, codec) — both dependencies
are explicit; no global singletons. Production wires both to PyMEOS;
tests pass stubs.

The POST /functions/{name} flow:
  1. Decode opaque-type params via codec.decode(encoding, wire_value).
  2. Dispatch via Dispatcher.dispatch(name, params).
  3. Encode the result via codec.encode(encoding, value) if the catalog
     marks the return as serialised.

Error mapping:
  - Unknown function           -> 404
  - Missing / extra parameters -> 400
  - Param encoding has no codec decoder -> 400
  - Result encoding has no codec encoder -> 500

tests/test_app.py: 11 HTTP-level tests against a tiny in-test catalog
covering scalar, serialised-in / scalar-out, and serialised-in /
serialised-out shapes, plus the four error paths. All 38 framework
tests pass.
@estebanzimanyi
estebanzimanyi force-pushed the feat/step5-fastapi-endpoints branch from a0626ef to 435c657 Compare May 21, 2026 06:42
@estebanzimanyi estebanzimanyi changed the title feat(app): FastAPI routes for Dispatcher + WireCodec (step 5) feat(app): FastAPI routes for the dispatcher + wire codec May 22, 2026
@estebanzimanyi

estebanzimanyi commented Jun 1, 2026

Copy link
Copy Markdown
Member Author

Folded into the consolidated master baseline (636f459); the FastAPI/MEOS-API-ingestion line is preserved at the archive/fastapi/* tags and revived as the auto-generation alignment plan. Old history at archive/master-20260601 and archive/main-basehttp-20260601.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@estebanzimanyi estebanzimanyi closed this Jun 1, 2026
@estebanzimanyi
estebanzimanyi deleted the feat/step5-fastapi-endpoints branch June 1, 2026 15:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

No reviews

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

1 participant

@estebanzimanyi
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.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.