Course Map JSON Import (#1152)
Structured import of a course map from our own export.json format β the first
non-vision import path (PDF/image upload still routes through Gemini). A file
produced by GET /api/course-maps/:id/export.json round-trips losslessly.
Endpoint
POST /api/course-maps/import (auth: bearer, owner = caller)
- Body: a course-map document (the raw
export.jsoncontent).Content-Type: application/json. - Validated against the published JSON Schema
(
@course-map/shared/schema/course-map.v1.json) via ajv, plus cross-reference checks (services/import-json.ts):400 INVALID_JSONβ body isnβt JSON.400 SCHEMA_INVALIDβ fails the schema;details[]lists the violations.400 REFS_INVALIDβ duplicate course id, or an edge endpoint that isnβt a course on the map;details[]lists them.
- On success: inserts a user-owned
course_mapsrow (extraction_status = 'extracted',extraction_model = 'json-import',extraction_raw_json= the document), then fans out courses/edges through the sharedpersistExtractionpath. Returns201with theCourseMapDto. A persistence failure rolls the row back (500 PERSIST_FAILED) so no empty map is left.
The map name is the documentβs title (schema-required, length-bounded). The
upload UI lets the user rename on import by overwriting title before posting.
Round-trip guarantee
composeCourseMapFromRows (read) and the courseInsertRows / edgeInsertRows
mappings in extraction/persist.ts (write) are inverses, so
export β import β re-export reproduces the same document. This is enforced by a
round-trip test in __tests__/services/import-json.test.ts. Note the export
shape doesnβt carry per-semester creditHours or notes (both derived/absent),
so neither participates in the round-trip.
UI
apps/course-map/src/app/upload/page.tsx accepts .json alongside PDF/images;
a JSON file is read client-side and POSTed to /import (no extraction step),
then the user lands in the editor. importCourseMapJson in lib/api.ts folds
the serverβs details[] into the thrown error so schema problems are visible.
Scope
JSON only (lossless, schema-backed). CSV import is a possible follow-up (lossy β no edges or colors). HTML/PDF βimportβ is the existing vision path.