candle-annotator/openspec/specs/backend-api/spec.md
Marko Djordjevic 4121a87875 sync: apply multi-chart-management delta specs to main specs
- Created new chart-management capability spec
- Updated data-ingestion: chart-scoped candles, duplicate filename handling
- Updated backend-api: all endpoints gain chartId parameter, chart CRUD
- Updated chart-canvas: chart switching, scoped data fetching
- Updated label-management: annotations scoped to active chart
- Updated ui-shell: upload creates/selects chart, theme-aware link styling
2026-02-13 09:06:37 +01:00

6.7 KiB

ADDED Requirements

Requirement: Upload endpoint

The system SHALL provide a POST /api/upload endpoint that accepts a CSV file via multipart form data. The endpoint SHALL create a new chart (named from the uploaded filename without extension), parse the CSV using papaparse, validate the format, and insert all candle records into the candles table with the new chart's chart_id within a single database transaction. On success, the endpoint SHALL return a JSON response with the chart id, chart name, and the count of inserted records. On failure, it SHALL return an appropriate error status and message without creating a chart.

Scenario: Successful upload

  • WHEN a valid CSV file named "BTC-1H.csv" is sent to POST /api/upload
  • THEN endpoint creates a chart named "BTC-1H", inserts candles with that chart_id, and returns { "success": true, "count": <number>, "chart": { "id": <number>, "name": "BTC-1H" } } with HTTP 200

Scenario: Invalid CSV upload

  • WHEN a CSV with missing or invalid headers is sent to POST /api/upload
  • THEN endpoint returns { "error": "<description>" } with HTTP 400 and does not create a chart

Scenario: No file provided

  • WHEN POST /api/upload is called without a file
  • THEN endpoint returns { "error": "No file provided" } with HTTP 400

Scenario: Duplicate filename

  • WHEN a CSV named "BTC-1H.csv" is uploaded and a chart named "BTC-1H" already exists
  • THEN endpoint creates a chart named "BTC-1H-2" (or next available suffix) and inserts candles under that chart

Requirement: Get annotations endpoint

The system SHALL provide a GET /api/annotations endpoint that accepts an optional chartId query parameter. When chartId is provided, the endpoint SHALL return only annotations belonging to that chart. When chartId is omitted, the endpoint SHALL return annotations for the most recently created chart. Each annotation object SHALL include: id, chart_id, timestamp, label_type, geometry (parsed from JSON string or null), and created_at.

Scenario: Fetch annotations for specific chart

  • WHEN GET /api/annotations?chartId=3 is called
  • THEN endpoint returns a JSON array of annotations where chart_id equals 3, with HTTP 200

Scenario: Fetch annotations without chartId

  • WHEN GET /api/annotations is called without a chartId parameter
  • THEN endpoint returns annotations for the most recently created chart with HTTP 200

Scenario: No annotations exist for chart

  • WHEN GET /api/annotations?chartId=3 is called and no annotations exist for chart 3
  • THEN endpoint returns an empty JSON array [] with HTTP 200

Requirement: Create annotation endpoint

The system SHALL provide a POST /api/annotations endpoint that accepts a JSON body with fields: timestamp (required, integer), label_type (required, string), chart_id (required, integer), and geometry (optional, object). The endpoint SHALL validate the input, verify the chart exists, serialize geometry to JSON string if present, and insert the record into the annotations table. On success, it SHALL return the created annotation object with its assigned id.

Scenario: Create a marker annotation

  • WHEN POST /api/annotations is called with { "timestamp": 1700000000, "label_type": "break_up", "chart_id": 3 }
  • THEN endpoint saves the annotation with chart_id 3 and returns the created object with id and HTTP 201

Scenario: Create a line annotation

  • WHEN POST /api/annotations is called with { "timestamp": 1700000000, "label_type": "line", "chart_id": 3, "geometry": { "startTime": 1700000000, "startPrice": 1.05, "endTime": 1700100000, "endPrice": 1.06 } }
  • THEN endpoint saves the annotation with chart_id 3 and serialized geometry JSON, returns the created object with HTTP 201

Scenario: Invalid annotation data

  • WHEN POST /api/annotations is called with missing required fields (timestamp, label_type, or chart_id)
  • THEN endpoint returns { "error": "<description>" } with HTTP 400

Scenario: Annotation for non-existent chart

  • WHEN POST /api/annotations is called with a chart_id that does not exist
  • THEN endpoint returns { "error": "Chart not found" } with HTTP 404

Requirement: Delete annotation endpoint

The system SHALL provide a DELETE /api/annotations/[id] endpoint that removes an annotation by its ID. On success, it SHALL return HTTP 200. If the annotation does not exist, it SHALL return HTTP 404.

Scenario: Delete existing annotation

  • WHEN DELETE /api/annotations/5 is called and annotation with id 5 exists
  • THEN endpoint removes the annotation and returns HTTP 200

Scenario: Delete non-existent annotation

  • WHEN DELETE /api/annotations/999 is called and no annotation with that id exists
  • THEN endpoint returns HTTP 404

Requirement: Export annotations endpoint

The system SHALL provide a GET /api/export endpoint that accepts an optional chartId query parameter. When chartId is provided, the endpoint SHALL export only annotations for that chart. When chartId is omitted, the endpoint SHALL export annotations for the most recently created chart. The CSV SHALL have columns: timestamp, label_type, price. The response SHALL set Content-Type: text/csv and Content-Disposition: attachment; filename="annotations.csv" headers.

Scenario: Export for specific chart

  • WHEN GET /api/export?chartId=3 is called and annotations exist for chart 3
  • THEN endpoint returns a CSV file download with only annotations belonging to chart 3

Scenario: Export without chartId

  • WHEN GET /api/export is called without a chartId parameter
  • THEN endpoint exports annotations for the most recently created chart

Requirement: Get candles endpoint

The system SHALL provide a GET /api/candles endpoint that accepts an optional chartId query parameter. When chartId is provided, the endpoint SHALL return only candles belonging to that chart. When chartId is omitted, the endpoint SHALL return candles for the most recently created chart. Results SHALL be ordered by time ascending. Each object SHALL include: time, open, high, low, close.

Scenario: Fetch candles for specific chart

  • WHEN GET /api/candles?chartId=3 is called
  • THEN endpoint returns a JSON array of candle objects where chart_id equals 3, ordered by time ascending with HTTP 200

Scenario: Fetch candles without chartId

  • WHEN GET /api/candles is called without a chartId parameter
  • THEN endpoint returns candles for the most recently created chart with HTTP 200

Scenario: No candles exist for chart

  • WHEN GET /api/candles?chartId=3 is called and no candles exist for chart 3
  • THEN endpoint returns an empty JSON array [] with HTTP 200