diff --git a/postman/eventhub-rest-methods.postman_collection.json b/postman/eventhub-rest-methods.postman_collection.json new file mode 100644 index 0000000..43671b0 --- /dev/null +++ b/postman/eventhub-rest-methods.postman_collection.json @@ -0,0 +1,452 @@ +{ + "info": { + "_postman_id": "0f7a6347-6f79-44d9-9d95-6fdb8a7c12c8", + "name": "EventHub REST Methods", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + }, + "variable": [ + { + "key": "baseUrl", + "value": "http://localhost:8080" + }, + { + "key": "planKey", + "value": "kralowetz-tachograph-org-147" + } + ], + "item": [ + { + "name": "Acquisition - Generic", + "item": [ + { + "name": "POST /api/eventhub/acquisition/telematics/positions", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "[\n {\n \"tenantKey\": \"dev\",\n \"providerKey\": \"YELLOWFOX\",\n \"sourceKey\": \"YELLOWFOX_POSITION\",\n \"sourceInstanceKey\": \"dev-yellowfox\",\n \"tenantProviderSettingKey\": \"dev-yellowfox-setting\",\n \"externalFleetKey\": \"fleet-a\",\n \"externalSourceEventId\": \"yf-pos-1001\",\n \"occurredAt\": \"2026-04-30T08:15:00+02:00\",\n \"receivedPartnerAt\": \"2026-04-30T08:15:02+02:00\",\n \"driverRef\": {\n \"sourceEntityId\": \"driver-42\",\n \"driverCard\": {\n \"nation\": \"AT\",\n \"number\": \"D123456789\"\n }\n },\n \"vehicleRef\": {\n \"sourceEntityId\": \"vehicle-7\",\n \"vin\": \"WDB9634031L123456\",\n \"vehicleRegistration\": {\n \"nation\": \"AT\",\n \"number\": \"W12345T\"\n }\n },\n \"odometerM\": 456789000,\n \"latitude\": 48.2082,\n \"longitude\": 16.3738,\n \"positionReason\": \"PING\",\n \"payload\": {\n \"raw\": {\n \"provider\": \"yellowfox\"\n }\n }\n }\n]" + }, + "url": { + "raw": "{{baseUrl}}/api/eventhub/acquisition/telematics/positions", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "eventhub", + "acquisition", + "telematics", + "positions" + ] + } + } + }, + { + "name": "POST /api/eventhub/acquisition/packages", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"package\": {\n \"tenantKey\": \"dev\",\n \"eventSource\": {\n \"providerKey\": \"MANUAL\",\n \"sourceKind\": \"MANUAL\",\n \"sourceKey\": \"MANUAL_INPUT\",\n \"sourceInstanceKey\": \"default\"\n },\n \"sourceGroup\": null,\n \"importScope\": {\n \"type\": \"TENANT_ALL\",\n \"rootSourceOrganisation\": null,\n \"includeChildren\": false,\n \"occurredFrom\": null,\n \"occurredTo\": null\n },\n \"eventFamily\": \"DRIVER_ACTIVITY\",\n \"businessDate\": \"2026-04-30\",\n \"externalPackageId\": \"manual-package-001\"\n },\n \"events\": [\n {\n \"eventId\": \"7ee8f3ef-9f7f-44ef-8f4f-5b335fb2188d\",\n \"externalSourceEventId\": \"manual-evt-1\",\n \"driverRef\": {\n \"sourceEntityId\": \"driver-42\",\n \"driverCard\": {\n \"nation\": \"AT\",\n \"number\": \"D123456789\"\n }\n },\n \"vehicleRef\": null,\n \"occurredAt\": \"2026-04-30T09:00:00+02:00\",\n \"receivedPartnerAt\": \"2026-04-30T09:00:01+02:00\",\n \"receivedHubAt\": \"2026-04-30T09:00:02+02:00\",\n \"eventDomain\": \"DRIVER_ACTIVITY\",\n \"eventType\": \"DRIVE\",\n \"lifecycle\": \"SNAPSHOT\",\n \"odometerM\": 123456000,\n \"position\": null,\n \"eventDetails\": {\n \"type\": \"DRIVER_ACTIVITY\",\n \"attributes\": {\n \"cardSlot\": \"DRIVER\",\n \"cardStatus\": \"INSERTED\",\n \"drivingStatus\": \"SINGLE\"\n }\n },\n \"sourcePackageRef\": null,\n \"payload\": {\n \"raw\": {\n \"manual\": true\n }\n },\n \"manualEntry\": true,\n \"packageInfo\": {\n \"tenantKey\": \"dev\",\n \"eventSource\": {\n \"providerKey\": \"MANUAL\",\n \"sourceKind\": \"MANUAL\",\n \"sourceKey\": \"MANUAL_INPUT\",\n \"sourceInstanceKey\": \"default\"\n },\n \"sourceGroup\": null,\n \"importScope\": {\n \"type\": \"TENANT_ALL\",\n \"rootSourceOrganisation\": null,\n \"includeChildren\": false,\n \"occurredFrom\": null,\n \"occurredTo\": null\n },\n \"eventFamily\": \"DRIVER_ACTIVITY\",\n \"businessDate\": \"2026-04-30\",\n \"externalPackageId\": \"manual-package-001\"\n }\n }\n ]\n}" + }, + "url": { + "raw": "{{baseUrl}}/api/eventhub/acquisition/packages", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "eventhub", + "acquisition", + "packages" + ] + } + } + }, + { + "name": "POST /api/eventhub/acquisition/events", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "[\n {\n \"eventId\": \"f4d32283-df74-4a53-aaef-f0b11fd9f4cd\",\n \"externalSourceEventId\": \"manual-evt-2\",\n \"driverRef\": null,\n \"vehicleRef\": {\n \"sourceEntityId\": \"vehicle-7\",\n \"vin\": \"WDB9634031L123456\",\n \"vehicleRegistration\": {\n \"nation\": \"AT\",\n \"number\": \"W12345T\"\n }\n },\n \"occurredAt\": \"2026-04-30T10:30:00+02:00\",\n \"receivedPartnerAt\": \"2026-04-30T10:30:01+02:00\",\n \"receivedHubAt\": \"2026-04-30T10:30:02+02:00\",\n \"eventDomain\": \"POSITION\",\n \"eventType\": \"POSITION_RECORDED\",\n \"lifecycle\": \"SNAPSHOT\",\n \"odometerM\": 789000000,\n \"position\": {\n \"latitude\": 48.2100,\n \"longitude\": 16.3700\n },\n \"eventDetails\": {\n \"type\": \"POSITION\",\n \"attributes\": {\n \"reason\": \"MANUAL_TEST\"\n }\n },\n \"sourcePackageRef\": null,\n \"payload\": {\n \"raw\": {\n \"manual\": true\n }\n },\n \"manualEntry\": true,\n \"packageInfo\": {\n \"tenantKey\": \"dev\",\n \"eventSource\": {\n \"providerKey\": \"MANUAL\",\n \"sourceKind\": \"MANUAL\",\n \"sourceKey\": \"MANUAL_INPUT\",\n \"sourceInstanceKey\": \"default\"\n },\n \"sourceGroup\": null,\n \"importScope\": {\n \"type\": \"TENANT_ALL\",\n \"rootSourceOrganisation\": null,\n \"includeChildren\": false,\n \"occurredFrom\": null,\n \"occurredTo\": null\n },\n \"eventFamily\": \"POSITION\",\n \"businessDate\": \"2026-04-30\",\n \"externalPackageId\": \"manual-package-002\"\n }\n }\n]" + }, + "url": { + "raw": "{{baseUrl}}/api/eventhub/acquisition/events", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "eventhub", + "acquisition", + "events" + ] + } + } + } + ] + }, + { + "name": "Acquisition - Tachograph", + "item": [ + { + "name": "POST /api/eventhub/acquisition/tachograph/activities", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "[\n {\n \"tenantKey\": \"dev\",\n \"sourceKind\": \"VEHICLE_UNIT\",\n \"sourceInstanceKey\": \"dev-tachograph-db\",\n \"tenantProviderSettingKey\": \"dev-tachograph\",\n \"externalSourceEventId\": \"vu-activity-1001\",\n \"occurredAt\": \"2026-04-30T06:00:00+02:00\",\n \"receivedPartnerAt\": \"2026-04-30T06:05:00+02:00\",\n \"driverRef\": {\n \"sourceEntityId\": \"driver-42\",\n \"driverCard\": {\n \"nation\": \"AT\",\n \"number\": \"D123456789\"\n }\n },\n \"vehicleRef\": {\n \"sourceEntityId\": \"vehicle-7\",\n \"vin\": \"WDB9634031L123456\",\n \"vehicleRegistration\": {\n \"nation\": \"AT\",\n \"number\": \"W12345T\"\n }\n },\n \"activityType\": \"DRIVE\",\n \"lifecycle\": \"SNAPSHOT\",\n \"cardSlot\": \"DRIVER\",\n \"cardStatus\": \"INSERTED\",\n \"drivingStatus\": \"SINGLE\",\n \"payload\": {\n \"raw\": {\n \"source\": \"manual-test\"\n }\n }\n }\n]" + }, + "url": { + "raw": "{{baseUrl}}/api/eventhub/acquisition/tachograph/activities", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "eventhub", + "acquisition", + "tachograph", + "activities" + ] + } + } + }, + { + "name": "POST /api/eventhub/acquisition/tachograph/imports/plan", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"tenantKey\": \"dev\",\n \"eventSource\": {\n \"providerKey\": \"TACHOGRAPH\",\n \"sourceKind\": \"MIXED\",\n \"sourceKey\": \"TACHOGRAPH_DB\",\n \"sourceInstanceKey\": \"dev-tachograph-db\",\n \"tenantProviderSettingKey\": \"dev-tachograph\"\n },\n \"sourceGroup\": {\n \"type\": \"ORGANISATION\",\n \"sourceEntityId\": \"147\",\n \"code\": \"147\",\n \"name\": \"Root Org\"\n },\n \"importScope\": {\n \"type\": \"TENANT_ALL\",\n \"rootSourceOrganisation\": null,\n \"includeChildren\": false,\n \"occurredFrom\": null,\n \"occurredTo\": null\n },\n \"eventFamilies\": [\n \"DRIVER_ACTIVITY\"\n ],\n \"mode\": \"INCREMENTAL_UPDATE\",\n \"refreshMasterDataFirst\": true,\n \"acquisitionStrategy\": \"SOURCE_PACKAGE_WATERMARK\"\n}" + }, + "url": { + "raw": "{{baseUrl}}/api/eventhub/acquisition/tachograph/imports/plan", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "eventhub", + "acquisition", + "tachograph", + "imports", + "plan" + ] + } + } + }, + { + "name": "POST /api/eventhub/acquisition/tachograph/imports/start?execute=true", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"tenantKey\": \"dev\",\n \"eventSource\": {\n \"providerKey\": \"TACHOGRAPH\",\n \"sourceKind\": \"MIXED\",\n \"sourceKey\": \"TACHOGRAPH_DB\",\n \"sourceInstanceKey\": \"dev-tachograph-db\",\n \"tenantProviderSettingKey\": \"dev-tachograph\"\n },\n \"sourceGroup\": {\n \"type\": \"ORGANISATION\",\n \"sourceEntityId\": \"147\",\n \"code\": \"147\",\n \"name\": \"Root Org\"\n },\n \"importScope\": {\n \"type\": \"TENANT_ALL\",\n \"rootSourceOrganisation\": null,\n \"includeChildren\": false,\n \"occurredFrom\": null,\n \"occurredTo\": null\n },\n \"eventFamilies\": [\n \"DRIVER_ACTIVITY\"\n ],\n \"mode\": \"INCREMENTAL_UPDATE\",\n \"refreshMasterDataFirst\": true,\n \"acquisitionStrategy\": \"SOURCE_PACKAGE_WATERMARK\"\n}" + }, + "url": { + "raw": "{{baseUrl}}/api/eventhub/acquisition/tachograph/imports/start?execute=true", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "eventhub", + "acquisition", + "tachograph", + "imports", + "start" + ], + "query": [ + { + "key": "execute", + "value": "true" + } + ] + } + } + }, + { + "name": "POST /api/eventhub/acquisition/tachograph/master-data/refresh", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"tenantKey\": \"dev\",\n \"eventSource\": {\n \"providerKey\": \"TACHOGRAPH\",\n \"sourceKind\": \"MIXED\",\n \"sourceKey\": \"TACHOGRAPH_DB\",\n \"sourceInstanceKey\": \"dev-tachograph-db\",\n \"tenantProviderSettingKey\": \"dev-tachograph\"\n },\n \"sourceGroup\": null,\n \"importScope\": {\n \"type\": \"TENANT_ALL\",\n \"rootSourceOrganisation\": null,\n \"includeChildren\": false,\n \"occurredFrom\": null,\n \"occurredTo\": null\n },\n \"eventFamilies\": [\n \"DRIVER_ACTIVITY\"\n ],\n \"mode\": \"INCREMENTAL_UPDATE\",\n \"refreshMasterDataFirst\": false,\n \"acquisitionStrategy\": \"SOURCE_PACKAGE_WATERMARK\"\n}" + }, + "url": { + "raw": "{{baseUrl}}/api/eventhub/acquisition/tachograph/master-data/refresh", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "eventhub", + "acquisition", + "tachograph", + "master-data", + "refresh" + ] + } + } + }, + { + "name": "GET /api/eventhub/acquisition/tachograph/imports/configured-plans", + "request": { + "method": "GET", + "url": { + "raw": "{{baseUrl}}/api/eventhub/acquisition/tachograph/imports/configured-plans", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "eventhub", + "acquisition", + "tachograph", + "imports", + "configured-plans" + ] + } + } + }, + { + "name": "GET /api/eventhub/acquisition/tachograph/imports/configured-plans/{planKey}", + "request": { + "method": "GET", + "url": { + "raw": "{{baseUrl}}/api/eventhub/acquisition/tachograph/imports/configured-plans/{{planKey}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "eventhub", + "acquisition", + "tachograph", + "imports", + "configured-plans", + "{{planKey}}" + ] + } + } + }, + { + "name": "POST /api/eventhub/acquisition/tachograph/imports/configured-plans/{planKey}/start", + "request": { + "method": "POST", + "url": { + "raw": "{{baseUrl}}/api/eventhub/acquisition/tachograph/imports/configured-plans/{{planKey}}/start?triggerMode=EXECUTE&mode=INCREMENTAL_UPDATE&strategy=SOURCE_PACKAGE_WATERMARK", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "eventhub", + "acquisition", + "tachograph", + "imports", + "configured-plans", + "{{planKey}}", + "start" + ], + "query": [ + { + "key": "triggerMode", + "value": "EXECUTE" + }, + { + "key": "mode", + "value": "INCREMENTAL_UPDATE" + }, + { + "key": "strategy", + "value": "SOURCE_PACKAGE_WATERMARK" + } + ] + } + } + }, + { + "name": "POST /api/eventhub/acquisition/tachograph/imports/configured-plans/{planKey}/master-data/refresh", + "request": { + "method": "POST", + "url": { + "raw": "{{baseUrl}}/api/eventhub/acquisition/tachograph/imports/configured-plans/{{planKey}}/master-data/refresh?mode=INCREMENTAL_UPDATE&strategy=SOURCE_PACKAGE_WATERMARK", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "eventhub", + "acquisition", + "tachograph", + "imports", + "configured-plans", + "{{planKey}}", + "master-data", + "refresh" + ], + "query": [ + { + "key": "mode", + "value": "INCREMENTAL_UPDATE" + }, + { + "key": "strategy", + "value": "SOURCE_PACKAGE_WATERMARK" + } + ] + } + } + } + ] + }, + { + "name": "Acquisition - YellowFox", + "item": [ + { + "name": "POST /api/eventhub/acquisition/yellowfox/d8-bookings", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "[\n {\n \"tenantKey\": \"dev\",\n \"sourceInstanceKey\": \"dev-yellowfox\",\n \"tenantProviderSettingKey\": \"dev-yellowfox-setting\",\n \"externalFleetKey\": \"fleet-a\",\n \"eventId\": \"d8-1001\",\n \"key\": \"booking-1001\",\n \"eventType\": 1,\n \"state\": 0,\n \"occurredAt\": \"2026-04-30T11:00:00+02:00\",\n \"receivedPartnerAt\": \"2026-04-30T11:00:01+02:00\",\n \"driverRef\": {\n \"sourceEntityId\": \"driver-42\",\n \"driverCard\": {\n \"nation\": \"AT\",\n \"number\": \"D123456789\"\n }\n },\n \"vehicleRef\": {\n \"sourceEntityId\": \"vehicle-7\",\n \"vin\": \"WDB9634031L123456\",\n \"vehicleRegistration\": {\n \"nation\": \"AT\",\n \"number\": \"W12345T\"\n }\n },\n \"odometerM\": 12345000,\n \"latitude\": 48.2082,\n \"longitude\": 16.3738,\n \"payload\": {\n \"raw\": {\n \"provider\": \"yellowfox\"\n }\n }\n }\n]" + }, + "url": { + "raw": "{{baseUrl}}/api/eventhub/acquisition/yellowfox/d8-bookings", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "eventhub", + "acquisition", + "yellowfox", + "d8-bookings" + ] + } + } + } + ] + }, + { + "name": "Management", + "item": [ + { + "name": "GET /actuator/health", + "request": { + "method": "GET", + "url": { + "raw": "{{baseUrl}}/actuator/health", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "actuator", + "health" + ] + } + } + }, + { + "name": "GET /actuator/info", + "request": { + "method": "GET", + "url": { + "raw": "{{baseUrl}}/actuator/info", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "actuator", + "info" + ] + } + } + }, + { + "name": "GET /actuator/metrics", + "request": { + "method": "GET", + "url": { + "raw": "{{baseUrl}}/actuator/metrics", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "actuator", + "metrics" + ] + } + } + }, + { + "name": "GET /actuator/camelroutes", + "request": { + "method": "GET", + "url": { + "raw": "{{baseUrl}}/actuator/camelroutes", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "actuator", + "camelroutes" + ] + } + } + } + ] + } + ] +} diff --git a/src/main/resources/db/migration/V10__make_event_hypertable.sql b/src/main/resources/db/migration/V10__make_event_hypertable.sql new file mode 100644 index 0000000..9e844b2 --- /dev/null +++ b/src/main/resources/db/migration/V10__make_event_hypertable.sql @@ -0,0 +1,52 @@ +create extension if not exists timescaledb; + +create table if not exists eventhub.event_source_record ( + source_record_key_hash text primary key, + event_occurred_at timestamptz not null, + event_id uuid not null, + created_at timestamptz not null default now() +); + +insert into eventhub.event_source_record(source_record_key_hash, event_occurred_at, event_id) +select event.source_record_key_hash, event.occurred_at, event.id +from eventhub.event +on conflict (source_record_key_hash) do nothing; + +alter table eventhub.event_detail + drop constraint if exists fk_event_detail_event; + +alter table eventhub.event_source_record + drop constraint if exists fk_event_source_record_event; + +drop index if exists eventhub.ux_event_source_record; + +do $$ +begin + if not exists ( + select 1 + from timescaledb_information.hypertables + where hypertable_schema = 'eventhub' + and hypertable_name = 'event' + ) then + perform create_hypertable( + 'eventhub.event', + 'occurred_at', + chunk_time_interval => interval '7 days', + migrate_data => true + ); + end if; +end $$; + +alter table eventhub.event_detail + add constraint fk_event_detail_event foreign key (event_occurred_at, event_id) + references eventhub.event(occurred_at, id) + on delete cascade; + +alter table eventhub.event_source_record + add constraint fk_event_source_record_event foreign key (event_occurred_at, event_id) + references eventhub.event(occurred_at, id) + on delete cascade + deferrable initially deferred; + +create index if not exists idx_event_source_record_event + on eventhub.event_source_record(event_occurred_at, event_id); diff --git a/src/main/resources/db/migration/V11__ensure_event_source_record.sql b/src/main/resources/db/migration/V11__ensure_event_source_record.sql new file mode 100644 index 0000000..9e844b2 --- /dev/null +++ b/src/main/resources/db/migration/V11__ensure_event_source_record.sql @@ -0,0 +1,52 @@ +create extension if not exists timescaledb; + +create table if not exists eventhub.event_source_record ( + source_record_key_hash text primary key, + event_occurred_at timestamptz not null, + event_id uuid not null, + created_at timestamptz not null default now() +); + +insert into eventhub.event_source_record(source_record_key_hash, event_occurred_at, event_id) +select event.source_record_key_hash, event.occurred_at, event.id +from eventhub.event +on conflict (source_record_key_hash) do nothing; + +alter table eventhub.event_detail + drop constraint if exists fk_event_detail_event; + +alter table eventhub.event_source_record + drop constraint if exists fk_event_source_record_event; + +drop index if exists eventhub.ux_event_source_record; + +do $$ +begin + if not exists ( + select 1 + from timescaledb_information.hypertables + where hypertable_schema = 'eventhub' + and hypertable_name = 'event' + ) then + perform create_hypertable( + 'eventhub.event', + 'occurred_at', + chunk_time_interval => interval '7 days', + migrate_data => true + ); + end if; +end $$; + +alter table eventhub.event_detail + add constraint fk_event_detail_event foreign key (event_occurred_at, event_id) + references eventhub.event(occurred_at, id) + on delete cascade; + +alter table eventhub.event_source_record + add constraint fk_event_source_record_event foreign key (event_occurred_at, event_id) + references eventhub.event(occurred_at, id) + on delete cascade + deferrable initially deferred; + +create index if not exists idx_event_source_record_event + on eventhub.event_source_record(event_occurred_at, event_id); diff --git a/src/main/resources/db/migration/V9__add_event_source_package_id.sql b/src/main/resources/db/migration/V9__add_event_source_package_id.sql new file mode 100644 index 0000000..3a7d83a --- /dev/null +++ b/src/main/resources/db/migration/V9__add_event_source_package_id.sql @@ -0,0 +1,6 @@ +alter table eventhub.event + add column if not exists source_package_id text; + +create index if not exists idx_event_source_package_id + on eventhub.event(source_package_id) + where source_package_id is not null;