Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 12 additions & 5 deletions backend-contract/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,11 @@ backend-contract/
index.ts top-level barrel; re-exports ./processing
processing/
task-spec.ts VolView's zod task-spec schema
wire.ts neutral wire shapes: input value, job status, job history,
result-intent vocabulary
wire.ts neutral wire shapes: input value, staged-input
descriptor, job status, job history, result-intent
vocabulary
annotations.ts the annotations interchange FILE format (rulers,
rectangles, polygons) in world LPS mm
openapi.ts the REST surface as an OpenAPI 3.1 document (wire schemas
injected from the zod codegen)
schema-json.ts zod -> JSON Schema codegen
Expand Down Expand Up @@ -52,8 +55,12 @@ Task specs require two validation passes. First validate the generated
`task-spec.schema.json`, then enforce the cross-field rules implemented by
`validateTaskSpecSemantics` (or an equivalent implementation in the backend's
language). Standard JSON Schema cannot compare sibling values such as
`default <= max`. Backend conformance tests must also assert that every payload
under `fixtures/negative/` is rejected by the combined validation path.
`default <= max`. The annotations interchange file needs the same two passes:
validate `annotations-file.schema.json`, then enforce
`validateAnnotationsFileSemantics` — every nonempty `labelName` must be declared
in its own tool-kind label namespace, which JSON Schema cannot express either.
Backend conformance tests must also assert that every payload under
`fixtures/negative/` is rejected by the combined validation path.

## The neutral REST surface (OpenAPI)

Expand All @@ -69,7 +76,7 @@ enums.
| `listJobHistory` | `GET /jobs` | → paged `JobHistorySummary[]` (optional) |
| `getJobHistoryDetail` | `GET /jobs/{jobId}/detail` | → logs + submitted parameters on demand |
| `deleteJob` | `DELETE /jobs/{jobId}` | → cascading deletion: execution record, results, staged inputs (terminal jobs only; 409 otherwise) |
| `stageInput` | `POST /stage` | parent-bound labelmap multipart → `StageResponse` (optional) |
| `stageInput` | `POST /stage` | parent-bound labelmap or annotations multipart → `StageResponse` (optional) |
| `getJob` | `GET /jobs/{jobId}` | → `NeutralJobStatus` |
| `getJobResults` | `GET /jobs/{jobId}/results` | → result intents, or explicit error |
| `cancelJob` | `POST /jobs/{jobId}/cancel` | → `NeutralJobStatus` |
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"schemaVersion": 2,
"space": "LPS",
"tools": {
"rulers": [
{
"firstPoint": [0, 0, 0],
"secondPoint": [1, 1, 0],
"frameOfReference": {
"planeNormal": [0, 0, 1],
"planeOrigin": [0, 0, 0]
}
}
]
}
}
16 changes: 16 additions & 0 deletions backend-contract/fixtures/negative/annotations-bad-space.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"schemaVersion": 1,
"space": "RAS",
"tools": {
"rulers": [
{
"firstPoint": [0, 0, 0],
"secondPoint": [1, 1, 0],
"frameOfReference": {
"planeNormal": [0, 0, 1],
"planeOrigin": [0, 0, 0]
}
}
]
}
}
24 changes: 24 additions & 0 deletions backend-contract/fixtures/negative/annotations-dangling-label.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"schemaVersion": 1,
"space": "LPS",
"labels": {
"rectangles": {
"lesion": {
"color": "#00ff00"
}
}
},
"tools": {
"rulers": [
{
"firstPoint": [0, 0, 0],
"secondPoint": [1, 1, 0],
"frameOfReference": {
"planeNormal": [0, 0, 1],
"planeOrigin": [0, 0, 0]
},
"labelName": "lesion"
}
]
}
}
18 changes: 18 additions & 0 deletions backend-contract/fixtures/negative/annotations-session-field.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"schemaVersion": 1,
"space": "LPS",
"tools": {
"rulers": [
{
"firstPoint": [0, 0, 0],
"secondPoint": [1, 1, 0],
"frameOfReference": {
"planeNormal": [0, 0, 1],
"planeOrigin": [0, 0, 0]
},
"imageID": "session-only-image-handle",
"color": "#ff0000"
}
]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"schemaVersion": 1,
"space": "LPS",
"tools": {
"polygons": [
{
"points": [
[0, 0, 0],
[10, 0, 0]
],
"frameOfReference": {
"planeNormal": [0, 0, 1],
"planeOrigin": [0, 0, 0]
}
}
]
}
}
16 changes: 16 additions & 0 deletions backend-contract/fixtures/negative/annotations-zero-normal.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"schemaVersion": 1,
"space": "LPS",
"tools": {
"rulers": [
{
"firstPoint": [0, 0, 0],
"secondPoint": [1, 1, 0],
"frameOfReference": {
"planeNormal": [0, 0, 0],
"planeOrigin": [0, 0, 0]
}
}
]
}
}
10 changes: 10 additions & 0 deletions backend-contract/fixtures/negative/stage-input-unknown-type.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"type": "pointset",
"name": "landmarks.json",
"referenceImage": {
"type": "image",
"uris": [
"/api/v1/file/aaaaaaaaaaaaaaaaaaaaaaaa/proxiable/001.dcm"
]
}
}
72 changes: 72 additions & 0 deletions backend-contract/fixtures/wire/annotations-file.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
{
"schemaVersion": 1,
"space": "LPS",
"labels": {
"rulers": {
"lesion": {
"color": "#ff0000",
"strokeWidth": 2
}
},
"rectangles": {
"lesion": {
"color": "#00ff00",
"strokeWidth": 1,
"fillColor": "#00ff0033"
}
},
"polygons": {
"roi": {
"color": "#0000ff"
}
}
},
"tools": {
"rulers": [
{
"firstPoint": [-30.5, 12.25, -12.5],
"secondPoint": [18.75, 44, -12.5],
"frameOfReference": {
"planeNormal": [0, 0, 1],
"planeOrigin": [0, 0, -12.5]
},
"slice": 42,
"labelName": "lesion",
"name": "Ruler",
"metadata": {
"measuredBy": "reader-1"
}
}
],
"rectangles": [
{
"firstPoint": [-30.5, 12.25, -12.5],
"secondPoint": [18.75, 44, -12.5],
"frameOfReference": {
"planeNormal": [0, 0, 1],
"planeOrigin": [0, 0, -12.5]
},
"slice": 42,
"labelName": "lesion",
"name": "Rectangle"
}
],
"polygons": [
{
"points": [
[-20, 0, -12.5],
[10, 0, -12.5],
[10, 30, -12.5],
[-20, 30, -12.5]
],
"frameOfReference": {
"planeNormal": [0, 0, 1],
"planeOrigin": [0, 0, -12.5]
},
"slice": 42,
"labelName": "roi",
"name": "Polygon"
}
]
}
}
11 changes: 11 additions & 0 deletions backend-contract/fixtures/wire/intent.add-annotations.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"id": "6600000000000000000000e4",
"intent": "add-annotations",
"url": "/api/v1/file/6600000000000000000000e4/proxiable/rois.annotations.json",
"name": "rois.annotations.json",
"source": {
"providerId": "analysis-provider",
"jobId": "job-abc123",
"outputId": "outputAnnotations"
}
}
12 changes: 12 additions & 0 deletions backend-contract/fixtures/wire/stage-input.annotations.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"type": "annotations",
"name": "chest-ct.annotations.json",
"referenceImage": {
"type": "image",
"format": "dicom-series",
"uris": [
"/api/v1/file/aaaaaaaaaaaaaaaaaaaaaaaa/proxiable/001.dcm",
"/api/v1/file/bbbbbbbbbbbbbbbbbbbbbbbb/proxiable/002.dcm"
]
}
}
Loading
Loading