Skip to content

Commit e0df144

Browse files
authored
Merge pull request #22 from Ashi-Ashish/6-define-telematics_event-schema-json
2 parents 671f4d1 + 89f4404 commit e0df144

File tree

7 files changed

+488
-1
lines changed

7 files changed

+488
-1
lines changed
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
name: Schema Validate
2+
on:
3+
pull_request:
4+
push:
5+
branches: [ main ]
6+
jobs:
7+
ajv:
8+
runs-on: ubuntu-latest
9+
steps:
10+
- uses: actions/checkout@v4
11+
- uses: actions/setup-node@v4
12+
with:
13+
node-version: 'lts/*'
14+
- run: npm ci || npm i
15+
- run: npm run schema:check

README.md

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,60 @@ docker compose up -d
1717

1818
Notes:
1919
- docker-compose.yml now reads credentials from environment variables. Docker Compose automatically loads variables from a .env file in the project root.
20-
- It's not recommended to commit real secrets (like POSTGRES_PASSWORD) to GitHub. Use .env locally and secret managers or CI/CD secrets in production.
20+
- It's not recommended to commit real secrets (like POSTGRES_PASSWORD) to GitHub. Use .env locally and secret managers or CI/CD secrets in production.
21+
22+
## Telematics Event Schema
23+
24+
A canonical JSON Schema defines the message contract for events published to the `telematics.events` topic.
25+
26+
- Location: `contracts/telematics_event.schema.json`
27+
- Version: 1.0.0 (backward-compatible changes in 1.x may only add optional fields or broaden enums)
28+
- Idempotency/ordering keys: `event_id`, `vehicle_id`, `ts` (epoch millis)
29+
- Strictness: unknown top-level properties are rejected (additionalProperties: false)
30+
31+
Why we use it
32+
- Producers (e.g., simulator) can auto-generate valid payloads and fail fast on invalid data.
33+
- Consumers (e.g., StreamWorker) can validate and dead-letter bad messages, and upsert by `event_id` safely.
34+
- Enables contract/property tests in CI using a single source of truth.
35+
36+
### Validate payloads with Docker (no local deps)
37+
38+
You can validate JSON files against the schema using `ajv-cli` via a throwaway Node container:
39+
40+
```bash
41+
# Validate example payloads
42+
docker run --rm -v "$PWD":/data node:20 npx -y ajv-cli \
43+
validate -s /data/contracts/telematics_event.schema.json \
44+
-d /data/examples/*.json
45+
46+
# Validate a single file
47+
docker run --rm -v "$PWD":/data node:20 npx -y ajv-cli \
48+
validate -s /data/contracts/telematics_event.schema.json \
49+
-d /data/path/to/your_payload.json
50+
```
51+
52+
Example files are provided:
53+
- `examples/valid_telematics_event.json`
54+
- `examples/invalid_telematics_event__missing_required.json`
55+
56+
### Producer guidance
57+
- Always set `schema_version` to the schema version you targeted (e.g., `1.0.0`).
58+
- Generate stable UUIDs for `event_id` and a stable vehicle identifier for `vehicle_id`.
59+
- Use UTC epoch milliseconds in `ts`.
60+
- Provide `position.lat` and `position.lon`; other fields are optional but recommended when available.
61+
62+
### Evolving the schema
63+
- Backward-compatible changes in 1.x: add optional properties, extend enums, loosen minima/maxima where safe.
64+
- Breaking changes: bump major and publish under a new `$id` path (e.g., `.../2-0-0`) and update `schema_version` expectations.
65+
66+
67+
## Unit consistency
68+
69+
- We standardize on meters-per-second for speed inside the pipeline. The canonical field is `speed_mps` (m/s). If your API/UI needs kilometers-per-hour, convert at the edges (kph = mps * 3.6).
70+
- If you previously used `speed_kph` in docs or prototypes, consider it deprecated in favor of `speed_mps`.
71+
72+
## Forward compatibility stance
73+
74+
- The schema is intentionally strict at the root: `additionalProperties: false`. Unknown top-level fields are rejected to prevent silent contract drift and to keep consumers predictable.
75+
- If you later need looser forward-compatibility (accept unknown fields while validating the known required set), you can switch to JSON Schema 2020-12's `unevaluatedProperties: true` at the root. Keep `required` minimal to avoid false negatives.
76+
- Nullability guidance: prefer omitting fields when a value is unknown rather than sending `null`. If you must send nulls from some sources, we can broaden specific fields to allow unions like `["boolean","null"]` or `["number","null"]` in a backward-compatible minor version.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"schema_version": "1.0.0",
3+
"event_id": "b8d2a6cc-6b8f-4f86-8f7e-6a0f7a2e3b8f",
4+
"vehicle_id": "veh-001",
5+
"lat": 43.4516,
6+
"lon": -80.4925
7+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"schema_version": "1.0.0",
3+
"event_id": "b8d2a6cc-6b8f-4f86-8f7e-6a0f7a2e3b8f",
4+
"vehicle_id": "veh-001",
5+
"driver_id": "drv-001",
6+
"ts": "2025-09-15T12:34:56Z",
7+
"lat": 43.4516,
8+
"lon": -80.4925,
9+
"speed_kph": 62.4,
10+
"heading_deg": 180.0,
11+
"accel_ms2": -0.5,
12+
"fuel_pct": 78.0,
13+
"odometer_km": 12435.7,
14+
"meta": { "route_id": "rt-kwc-01", "source": "simulator" }
15+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
{
2+
"$schema": "https://json-schema.org/draft/2020-12/schema",
3+
"$id": "https://example.com/schemas/telematics_event.schema.json",
4+
"title": "Telematics Event",
5+
"type": "object",
6+
"required": ["event_id", "vehicle_id", "ts", "lat", "lon"],
7+
"additionalProperties": false,
8+
"properties": {
9+
"schema_version": { "type": "string", "const": "1.0.0" },
10+
"event_id": { "type": "string", "format": "uuid" },
11+
"vehicle_id": { "type": "string", "minLength": 1 },
12+
"driver_id": { "type": "string", "minLength": 1 },
13+
"ts": { "type": "string", "format": "date-time" },
14+
"lat": { "type": "number", "minimum": -90, "maximum": 90 },
15+
"lon": { "type": "number", "minimum": -180, "maximum": 180 },
16+
"speed_kph": { "type": "number", "minimum": 0, "maximum": 300 },
17+
"heading_deg": { "type": "number", "minimum": 0, "maximum": 360 },
18+
"accel_ms2": { "type": "number", "minimum": -20, "maximum": 20 },
19+
"fuel_pct": { "type": "number", "minimum": 0, "maximum": 100 },
20+
"odometer_km": { "type": "number", "minimum": 0 },
21+
"meta": {
22+
"type": "object",
23+
"additionalProperties": false,
24+
"properties": {
25+
"route_id": { "type": "string" },
26+
"source": { "type": "string", "enum": ["simulator","device","replay"] }
27+
}
28+
}
29+
}
30+
}

0 commit comments

Comments
 (0)