feat(#90): implement EVENT subscriptions for federation
Implement event pub/sub messaging for federation to enable real-time event streaming between federated instances. Features: - Event subscription management (subscribe/unsubscribe) - Event publishing to subscribed instances - Event acknowledgment protocol - Server-side event filtering based on subscriptions - Full signature verification and connection validation Implementation: - FederationEventSubscription model for storing subscriptions - EventService with complete event lifecycle management - EventController with authenticated and public endpoints - EventMessage, EventAck, and SubscriptionDetails types - Comprehensive DTOs for all event operations API Endpoints: - POST /api/v1/federation/events/subscribe - POST /api/v1/federation/events/unsubscribe - POST /api/v1/federation/events/publish - GET /api/v1/federation/events/subscriptions - GET /api/v1/federation/events/messages - POST /api/v1/federation/incoming/event (public) - POST /api/v1/federation/incoming/event/ack (public) Testing: - 18 unit tests for EventService (89.09% coverage) - 11 unit tests for EventController (83.87% coverage) - All 29 tests passing - Follows TDD red-green-refactor cycle Technical Notes: - Reuses existing FederationMessage model with eventType field - Follows patterns from QueryService and CommandService - Uses existing signature and connection infrastructure - Supports hierarchical event type naming (e.g., "task.created") Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
-- Add eventType column to federation_messages table
|
||||
ALTER TABLE "federation_messages" ADD COLUMN "event_type" TEXT;
|
||||
|
||||
-- Add index for eventType
|
||||
CREATE INDEX "federation_messages_event_type_idx" ON "federation_messages"("event_type");
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "federation_event_subscriptions" (
|
||||
"id" UUID NOT NULL,
|
||||
"workspace_id" UUID NOT NULL,
|
||||
"connection_id" UUID NOT NULL,
|
||||
"event_type" TEXT NOT NULL,
|
||||
"metadata" JSONB NOT NULL DEFAULT '{}',
|
||||
"is_active" BOOLEAN NOT NULL DEFAULT true,
|
||||
"created_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMPTZ NOT NULL,
|
||||
|
||||
CONSTRAINT "federation_event_subscriptions_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "federation_event_subscriptions_workspace_id_idx" ON "federation_event_subscriptions"("workspace_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "federation_event_subscriptions_connection_id_idx" ON "federation_event_subscriptions"("connection_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "federation_event_subscriptions_event_type_idx" ON "federation_event_subscriptions"("event_type");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "federation_event_subscriptions_workspace_id_is_active_idx" ON "federation_event_subscriptions"("workspace_id", "is_active");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "federation_event_subscriptions_workspace_id_connection_id_even_key" ON "federation_event_subscriptions"("workspace_id", "connection_id", "event_type");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "federation_event_subscriptions" ADD CONSTRAINT "federation_event_subscriptions_connection_id_fkey" FOREIGN KEY ("connection_id") REFERENCES "federation_connections"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "federation_event_subscriptions" ADD CONSTRAINT "federation_event_subscriptions_workspace_id_fkey" FOREIGN KEY ("workspace_id") REFERENCES "workspaces"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
@@ -268,9 +268,10 @@ model Workspace {
|
||||
personalities Personality[]
|
||||
llmSettings WorkspaceLlmSettings?
|
||||
qualityGates QualityGate[]
|
||||
runnerJobs RunnerJob[]
|
||||
federationConnections FederationConnection[]
|
||||
federationMessages FederationMessage[]
|
||||
runnerJobs RunnerJob[]
|
||||
federationConnections FederationConnection[]
|
||||
federationMessages FederationMessage[]
|
||||
federationEventSubscriptions FederationEventSubscription[]
|
||||
|
||||
@@index([ownerId])
|
||||
@@map("workspaces")
|
||||
@@ -1287,8 +1288,9 @@ model FederationConnection {
|
||||
disconnectedAt DateTime? @map("disconnected_at") @db.Timestamptz
|
||||
|
||||
// Relations
|
||||
workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade)
|
||||
messages FederationMessage[]
|
||||
workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade)
|
||||
messages FederationMessage[]
|
||||
eventSubscriptions FederationEventSubscription[]
|
||||
|
||||
@@unique([workspaceId, remoteInstanceId])
|
||||
@@index([workspaceId])
|
||||
@@ -1330,6 +1332,7 @@ model FederationMessage {
|
||||
// Message content
|
||||
query String? @db.Text
|
||||
commandType String? @map("command_type") @db.Text
|
||||
eventType String? @map("event_type") @db.Text // For EVENT messages
|
||||
payload Json? @default("{}")
|
||||
response Json? @default("{}")
|
||||
|
||||
@@ -1353,5 +1356,30 @@ model FederationMessage {
|
||||
@@index([connectionId])
|
||||
@@index([messageId])
|
||||
@@index([correlationId])
|
||||
@@index([eventType])
|
||||
@@map("federation_messages")
|
||||
}
|
||||
|
||||
model FederationEventSubscription {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
workspaceId String @map("workspace_id") @db.Uuid
|
||||
connectionId String @map("connection_id") @db.Uuid
|
||||
|
||||
// Event subscription details
|
||||
eventType String @map("event_type")
|
||||
metadata Json @default("{}")
|
||||
isActive Boolean @default(true) @map("is_active")
|
||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz
|
||||
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz
|
||||
|
||||
// Relations
|
||||
connection FederationConnection @relation(fields: [connectionId], references: [id], onDelete: Cascade)
|
||||
workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@unique([workspaceId, connectionId, eventType])
|
||||
@@index([workspaceId])
|
||||
@@index([connectionId])
|
||||
@@index([eventType])
|
||||
@@index([workspaceId, isActive])
|
||||
@@map("federation_event_subscriptions")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user