Files
iios/packages/iios-service/src/interactions/ingest.dto.ts
T
maaz519 4b7dc98c6b feat(service): ingest endpoint with kernel gates (P1.4)
POST /v1/interactions/ingest: fail-closed OPA gate -> resolve scope/handle(MDM,
no silent merge)/actor/channel/thread -> idempotent write of interaction+parts+
outbox CloudEvent in one tx. 4 DB-backed gate tests green (idempotent /
fail-closed / unresolved-handle / outbox event). 16 tests total.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 21:24:09 +05:30

57 lines
1.7 KiB
TypeScript

import { Type } from 'class-transformer';
import {
IsArray,
IsISO8601,
IsObject,
IsOptional,
IsString,
ValidateNested,
} from 'class-validator';
class ScopeDto {
@IsString() orgId!: string;
@IsString() appId!: string;
@IsOptional() @IsString() buId?: string;
@IsOptional() @IsString() tenantId?: string;
@IsOptional() @IsString() workspaceId?: string;
@IsOptional() @IsString() projectId?: string;
@IsOptional() @IsString() userScopeId?: string;
}
class ChannelDto {
@IsString() type!: string;
@IsOptional() @IsString() externalChannelId?: string;
@IsOptional() @IsObject() capabilityContract?: Record<string, unknown>;
}
class SourceDto {
@IsString() handleKind!: string;
@IsString() externalId!: string;
@IsOptional() @IsString() displayName?: string;
}
class ThreadDto {
@IsOptional() @IsString() externalThreadId?: string;
@IsOptional() @IsString() subject?: string;
}
class PartDto {
@IsString() kind!: string;
@IsOptional() @IsString() bodyText?: string;
@IsOptional() @IsString() contentRef?: string;
@IsOptional() @IsString() mimeType?: string;
}
/** Validates the POST /v1/interactions/ingest body (conforms to IngestInteractionRequest). */
export class IngestRequestDto {
@ValidateNested() @Type(() => ScopeDto) scope!: ScopeDto;
@ValidateNested() @Type(() => ChannelDto) channel!: ChannelDto;
@ValidateNested() @Type(() => SourceDto) source!: SourceDto;
@IsOptional() @IsString() kind?: string;
@IsOptional() @ValidateNested() @Type(() => ThreadDto) thread?: ThreadDto;
@IsArray() @ValidateNested({ each: true }) @Type(() => PartDto) parts!: PartDto[];
@IsISO8601() occurredAt!: string;
@IsOptional() @IsString() providerEventId?: string;
@IsOptional() @IsObject() metadata?: Record<string, unknown>;
}