Skip to content

Commit 407346c

Browse files
authored
docs: README in every bases/lif/ directory (#922)
## Summary Adds a short README to each base brick so people navigating the repo on GitHub can tell what each service does without opening `core.py`. Each follows the same shape: purpose, endpoints, auth (where relevant), components composed, and the deploying project. ## Coverage 11 new READMEs: - `api_graphql`, `advisor_restapi`, `query_cache_restapi`, `example_data_source_rest_api`, `semantic_search_mcp_server`, `orchestrator_restapi`, `translator_restapi`, `identity_mapper_restapi`, `query_planner_restapi` - `mdr_restapi` — with an endpoint-group table summarizing its 16 endpoint modules under their URL prefixes - `query_cache_module` — a note that this is an empty stub for a future non-HTTP cache interface Skipped: `learner_data_export_api` (only on the open #920 branch; its README will land with that PR). ## Why Reviewer bandwidth constraint means we're chipping away at docs that don't need review. Tier 1 of the README sweep — bases first because they're well-bounded; components batch will follow. ## Test plan - [x] `uv run pre-commit run cspell --files bases/lif/*/README.md` passes - [ ] Spot-check that each README's claimed endpoints and component list match `core.py` after merge 🤖 Generated with [Claude Code](https://claude.com/claude-code)
1 parent 72a7c3b commit 407346c

12 files changed

Lines changed: 237 additions & 0 deletions

File tree

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# `advisor_restapi` — Base
2+
3+
FastAPI base for the LIF Advisor: a conversational interface that lets a user query their own learner data through a LangChain/LangGraph agent. Pairs with the Advisor frontend (`frontends/lif_advisor_app/`).
4+
5+
## Endpoints
6+
- `POST /login` — demo auth against an in-memory user list; returns access + refresh JWTs
7+
- `POST /refresh-token` — exchange refresh token for a new access token
8+
- `GET /initial-message` — greeting message for a freshly-logged-in user
9+
- `POST /start-conversation` — kicks off the conversation by loading the user's profile via the agent
10+
- `POST /continue-conversation` — sends a user message, returns the agent's reply
11+
- `POST /logout` — clears in-memory session state; agent summarizes the interaction first
12+
- `GET /health`
13+
14+
## Auth
15+
HS256 JWTs minted/validated by `lif.auth.core`. Demo-grade — the user list (`users_db`) and password (`LIF_DEMO_USER_PASSWORD`) are hard-coded for demo purposes; not the self-serve auth path (see `docs/design/cross-cutting/self-serve-tenant-auth.md`).
16+
17+
## Composes
18+
- `auth` — JWT helpers
19+
- `langchain_agent``LIFAIAgent` wrapping LangChain/LangGraph + memory
20+
- `logging`
21+
22+
## Deployed as
23+
`projects/lif_advisor_api/`

bases/lif/api_graphql/README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# `api_graphql` — Base
2+
3+
FastAPI + Strawberry GraphQL base that converts an OpenAPI schema (loaded from the MDR at startup) into a GraphQL schema at runtime. The resulting GraphQL types, filters, enums, and root queries are generated dynamically from the OpenAPI JSON — there are no hand-written `.graphql` files for the data model.
4+
5+
## Endpoints
6+
- `POST /graphql` — Strawberry-managed GraphQL endpoint (queries + mutations)
7+
- `GET /graphql` (GraphiQL UI when not authed-and-running-in-prod)
8+
9+
## Auth
10+
API-key authentication via `ApiKeyAuthMiddleware`. Configured by `GRAPHQL_AUTH__API_KEYS` env var (`key1:client1,key2:client2`). When unset, auth is disabled — fine for local dev, never for deployed envs.
11+
12+
## Composes
13+
- `api_key_auth` — middleware
14+
- `lif_schema_config` — env-driven config
15+
- `logging` — logger setup
16+
- `mdr_client` — fetches OpenAPI schema at startup
17+
- `openapi_to_graphql` — the actual OpenAPI → GraphQL generator
18+
19+
## Deployed as
20+
`projects/lif_graphql_api/`
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# `example_data_source_rest_api` — Base
2+
3+
Reference implementation of a non-LIF data source. Exists so the orchestrator's "external adapter" code path has something realistic to integrate with locally; production deployments swap in real SIS/LMS/HR systems via adapter components, not this base.
4+
5+
## Endpoints
6+
A small set of CRUD-style endpoints over a sample person dataset, gated by `x-key` API-key auth (`require_api_key` dependency). See `core.py` for the current shape — it evolves as new adapter scenarios get demoed.
7+
8+
`GET /health` is exempt from auth.
9+
10+
## Composes
11+
- `auth``verify_token` helper for API-key validation
12+
- `example_data_source_service` — sample data + business logic
13+
- `logging`
14+
15+
## Deployed as
16+
`projects/lif_example_data_source_rest_api/`
17+
18+
## See also
19+
[`docs/operations/guides/add-data-source.md`](../../../docs/operations/guides/add-data-source.md) walks through using this service end-to-end as the template for adding a custom data source.
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# `identity_mapper_restapi` — Base
2+
3+
FastAPI base for the LIF Identity Mapper: stores mappings between a person's identifiers across source systems (e.g., SIS ID ↔ LMS ID ↔ HR ID). Required when a single learner shows up under different identifiers in different systems and the orchestrator needs to know they're the same person.
4+
5+
## Endpoints
6+
Identity mappings are scoped per `{org_id}/{person_id}`:
7+
8+
- `POST /organizations/{org_id}/persons/{person_id}/mappings` — create a new `IdentityMapping`
9+
- `GET /organizations/{org_id}/persons/{person_id}/mappings` (and variants) — list / fetch mappings
10+
- `DELETE /organizations/{org_id}/persons/{person_id}/mappings/{mapping_id}` — delete a mapping (204 on success)
11+
12+
Plus exception handlers translating `DataNotFoundException`, `LIFException`, and validation errors into stable HTTP responses.
13+
14+
## Storage
15+
Backed by SQL (MariaDB in the reference deployment) via `identity_mapper_storage_sql`. The storage layer is pluggable through the `IdentityMapperStorage` interface; SQLAlchemy is the only implementation today.
16+
17+
## Composes
18+
- `datatypes``IdentityMapping`
19+
- `exceptions`
20+
- `identity_mapper_service` — business logic
21+
- `identity_mapper_storage` — storage interface
22+
- `identity_mapper_storage_sql` — SQLAlchemy-backed implementation
23+
- `logging`
24+
25+
## Deployed as
26+
`projects/lif_identity_mapper_api/` (the API) + `projects/lif_identity_mapper_mariadb/` (the database)

bases/lif/mdr_restapi/README.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# `mdr_restapi` — Base
2+
3+
FastAPI base for the LIF **Metadata Repository (MDR)**: the control-plane service that holds the LIF schema(s), transformation definitions, value sets, and per-tenant configuration. Most LIF services load their schema and transformation rules from here at startup.
4+
5+
The base is split into many endpoint modules (one per concern) which `core.py` mounts under stable URL prefixes.
6+
7+
## Endpoint groups
8+
9+
| Prefix | Module | What it does |
10+
|---|---|---|
11+
| `/datamodels` | `datamodel_endpoints` | LIF data models — Base LIF, Org LIF, target transformation models |
12+
| `/entities` | `entity_endpoints` | Entity definitions within a data model |
13+
| `/entity_associations` | `entity_association_endpoints` | Entity-to-entity relationships |
14+
| `/attributes` | `attribute_endpoints` | Scalar attributes within entities |
15+
| `/entity_attribute_associations` | `entity_attribute_association_endpoints` | Which attributes belong to which entities |
16+
| `/inclusions` | `inclusions_endpoints` | Reusable attribute groups (e.g., Contact, Address) |
17+
| `/value_sets` + `/value_set_values` | `valueset_endpoint`, `value_set_values_endpoint` | Strict + extensible enumerations |
18+
| `/transformation_groups` | `transformation_endpoint` | JSONata-based source→target transformations |
19+
| `/value_mappings` | `value_mapping_endpoints` | Code/value crosswalks used during transformation |
20+
| `/search` | `search_endpoint` | MDR-wide full-text search |
21+
| `/datamodel_constraints` | `datamodel_constraints_endpoints` | Constraint rules per model |
22+
| `/import_export` | `import_export_endpoints` | Bulk import/export of MDR content |
23+
| `/generate_jinja` | `generate_jinja_endpoint` | Template generation for derived schemas |
24+
| `/tenants` | `tenant_endpoints` | Self-serve tenant lifecycle (#883/#884): provision, workspace listing/selection, invite tokens |
25+
26+
## Auth
27+
`AuthMiddleware` (from `mdr_auth/core`) supports three principals: API-key (services), Cognito JWT (end users), and legacy HS256 JWT (pre-Cognito callers). The middleware also resolves `request.state.tenant_schema` per request based on Cognito groups + optional workspace-selection cookie — see [`docs/design/cross-cutting/self-serve-tenant-auth.md`](../../../docs/design/cross-cutting/self-serve-tenant-auth.md).
28+
29+
## Composes
30+
- `datatypes` — common payload shapes
31+
- `mdr_auth` — auth middleware + JWT/cookie/invite-token helpers
32+
- `mdr_dto` — wire-format DTOs
33+
- `mdr_services` — business logic (tenant_service, transformation_service, etc.)
34+
- `mdr_utils` — config, DB session factory, logger
35+
36+
## Deployed as
37+
`projects/lif_mdr_api/` (API) + `projects/lif_mdr_database/` (Postgres + Flyway migrations).
38+
Frontend: `frontends/mdr-frontend/`.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# `orchestrator_restapi` — Base
2+
3+
FastAPI base for the LIF Orchestrator: receives query plans from the Query Planner, fans out to the configured data-source adapters, gathers + normalizes responses. Long-running work is tracked as `OrchestratorJob`s the caller polls.
4+
5+
## Endpoints
6+
- `POST /jobs` — submit an `OrchestratorJobRequest`; returns a job id (`OrchestratorJobRequestResponse`)
7+
- `GET /jobs/{job_id}` — fetch current `OrchestratorJob` state
8+
- `GET /health`
9+
10+
A `DELETE /jobs/{job_id}` and `GET /jobs/{job_id}/result` are commented out in `core.py` — they were considered and deferred.
11+
12+
## Composes
13+
- `datatypes``OrchestratorJob` request/response shapes
14+
- `logging`
15+
- `orchestrator_service` — actual fan-out, adapter dispatch, response merging
16+
17+
## Deployed as
18+
`projects/lif_orchestrator_api/`
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# `query_cache_module` — Base (stub)
2+
3+
Empty placeholder base. `core.py` has no content; the brick is registered in pyproject and `__init__.py` re-exports `core`, but no application is mounted.
4+
5+
Likely the seed of a non-HTTP (importable-library) interface to query-cache functionality, kept around so a future project can compose `query_cache_module` directly instead of going through `query_cache_restapi`. Until that lands, treat this as scaffolding.
6+
7+
For the actually-deployed HTTP cache, see [`../query_cache_restapi/`](../query_cache_restapi/).
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# `query_cache_restapi` — Base
2+
3+
FastAPI base for the LIF Query Cache: caches MongoDB-backed LIF records so the GraphQL API doesn't re-orchestrate the same data on every request. One Query Cache instance runs per organization in the reference deployment (`query-cache-org1`, `-org2`, `-org3`).
4+
5+
## Endpoints
6+
- `POST /query` — read cached records for a `LIFQuery` filter
7+
- `POST /update` — mutate a cached record via a `LIFUpdate` payload
8+
- `POST /add` — add a new `LIFRecord`
9+
- `POST /save` — bulk-save fragments
10+
- `GET /` — sanity ping
11+
12+
## Composes
13+
- `datatypes``LIFQuery`, `LIFRecord`, `LIFFragment`, `LIFUpdate` shapes
14+
- `exceptions` — common LIF exception types
15+
- `logging`
16+
- `query_cache_service` — the actual cache logic (Mongo-backed)
17+
18+
## Deployed as
19+
`projects/lif_query_cache_api/`
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# `query_planner_restapi` — Base
2+
3+
FastAPI base for the LIF Query Planner: takes a `LIFQuery` and decides *how* to fulfill it — which data sources to hit, which fragments come from cache vs. fresh orchestration, how to route the result through any required translations. The GraphQL API delegates to the Query Planner; the planner in turn calls the Query Cache and Orchestrator.
4+
5+
## Endpoints
6+
- `POST /query` — synchronous query; returns `List[LIFRecord]`
7+
- `POST /query_async` — async variant; returns either records (cache hit) or a `LIFQueryStatusResponse` to poll
8+
- `GET /query/{query_id}/status` — poll status of an in-flight async query
9+
- `POST /update` — apply a `LIFUpdate`
10+
- `POST /orchestration/results` — callback endpoint for the Orchestrator to report back when an async job finishes
11+
- `GET /` — sanity ping
12+
13+
Async polling is bounded by `MIN_POLLING_DELAY_SECONDS` (1) and `MAX_POLLING_DELAY_SECONDS` (16) with `MAX_QUERY_TIMEOUT_SECONDS=60` per the constants in `core.py`.
14+
15+
## Configuration
16+
The planner reads YAML at startup that describes available information sources (the per-org `information_sources_config.yml` files under `deployments/*/`). One planner instance runs per org in the reference deployment.
17+
18+
## Composes
19+
- `datatypes``LIFQuery`, `LIFRecord`, `LIFUpdate`, planner-side types
20+
- `exceptions`
21+
- `logging`
22+
- `query_planner_service``LIFQueryPlannerService` (the actual planning logic)
23+
24+
## Deployed as
25+
`projects/lif_query_planner_api/`
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# `semantic_search_mcp_server` — Base
2+
3+
FastMCP-based [MCP (Model Context Protocol)](https://modelcontextprotocol.io/) server that exposes LIF data via AI-callable tools. Designed for Claude, Cursor, and other MCP-aware clients to do semantic search over learner data fields without learning the GraphQL schema by hand.
4+
5+
## MCP tools
6+
- `lif_query` — semantic search over LIF data fields (translates natural-language fragments into GraphQL queries)
7+
- `lif_mutation` — update LIF data fields (only registered when the schema includes a mutation model)
8+
9+
## HTTP endpoints (Starlette-mounted)
10+
- `GET /health` — readiness check
11+
- `GET /schema/status` — current schema source (`mdr` or `file`), leaf count, root types, filter models
12+
- `POST /schema/refresh` — reload schema from MDR (state only — does not re-register MCP tools)
13+
14+
## Schema loading
15+
At startup, loads the OpenAPI schema from MDR (with optional file fallback per `LIFSchemaConfig`). Uses `SchemaStateManager` to track source + provide thread-safe access. **No silent fallback in production:** if MDR is configured but unreachable, startup fails loudly rather than serving stale schema.
16+
17+
## Composes
18+
- `lif_schema_config` — config + defaults
19+
- `logging`
20+
- `schema_state_manager` — schema lifecycle + refresh
21+
- `semantic_search_service``run_semantic_search`, `run_mutation`
22+
23+
## Deployed as
24+
`projects/lif_semantic_search_mcp_server/`

0 commit comments

Comments
 (0)