Implement the three ingestion REST endpoints, ClickHouse repository implementations, flush scheduler, and IngestionService that wires controllers to the WriteBuffer.
Purpose: This is the core data pipeline -- agents POST data to endpoints, IngestionService buffers it, ClickHouseFlushScheduler drains and batch-inserts to ClickHouse. Backpressure returns 503 when buffer full.
Output: Working ingestion flow verified by integration tests against Testcontainers ClickHouse.
- insertBatch: INSERT INTO agent_metrics with batchUpdate
5. Create ClickHouseFlushScheduler:
-@Component, @EnableScheduling on the app config or main class
- Inject three WriteBuffer instances and three repository implementations
- Inject IngestionConfig for batchSize
-@Scheduled(fixedDelayString="${ingestion.flush-interval-ms:1000}") flushAll(): drains each buffer up to batchSize, calls insertBatch if non-empty. Wrap each in try-catch to log errors without stopping the scheduler.
- Implement SmartLifecycle: on stop(), flush all remaining data (loop drain until empty) before returning.
<done>IngestionService routes data to WriteBuffers. ClickHouse repositories implement batch inserts via JdbcTemplate. FlushScheduler drains buffers on interval and flushes remaining on shutdown.</done>
</task>
<task type="auto" tdd="true">
<name>Task 2: Ingestion REST controllers and integration tests</name>
- POST /api/v1/data/executions with single RouteExecution JSON returns 202
- POST /api/v1/data/executions with array of RouteExecutions returns 202
- POST /api/v1/data/diagrams with single RouteGraph returns 202
- POST /api/v1/data/diagrams with array of RouteGraphs returns 202
- POST /api/v1/data/metrics with metrics payload returns 202
- After flush interval, posted data is queryable in ClickHouse
- When buffer is full, POST returns 503 with Retry-After header
- Unknown JSON fields in request body are accepted (not rejected)
</behavior>
<action>
1. Create ExecutionController:
-@RestController@RequestMapping("/api/v1/data")
- POST /executions: accepts @RequestBody that handles both single RouteExecution and List<RouteExecution>. Use a custom deserializer or accept Object and check type, OR simply always accept as List (require agents to send arrays). Per protocol, accept both single and array.
- Calls ingestionService.acceptExecutions(). If returns false -> 503 with Retry-After: 5 header. If true -> 202 Accepted.
- Add @Operation annotations for OpenAPI documentation.
2. Create DiagramController:
-@RestController@RequestMapping("/api/v1/data")
- POST /diagrams: same pattern, accepts single or array of RouteGraph, delegates to ingestionService.
- Configure test with tiny buffer (ingestion.buffer-capacity=5)
- Fill buffer by posting enough data
- Next POST returns 503 with Retry-After header
- Verify previously buffered data is NOT lost (still flushes to ClickHouse)
Note: All integration tests must include X-Cameleer-Protocol-Version:1 header (API-04 will be enforced by Plan 03's interceptor, but include the header now for forward compatibility).
<done>All three ingestion endpoints return 202 on valid data. Data arrives in ClickHouse after flush. Buffer-full returns 503 with Retry-After. Unknown JSON fields accepted. Integration tests green.</done>
All four integration test classes green. Data flows from HTTP POST through WriteBuffer through FlushScheduler to ClickHouse. Backpressure returns 503 when buffer full without losing existing data.
</success_criteria>
<output>
After completion, create `.planning/phases/01-ingestion-pipeline-api-foundation/01-02-SUMMARY.md`