From f6e636c37dbbb192ce6dba736e657e857e55e2c2 Mon Sep 17 00:00:00 2001 From: Schalli Date: Tue, 21 Jul 2026 11:15:36 +0200 Subject: [PATCH] feat(10-05): add SourceConfigDto and TenderQueryDto - SourceConfigDto: pollIntervalMin (@Min(5) @Max(1440)), isActive - TenderQueryDto: page/limit pagination (copied from DkvHistoryQueryDto) + status filter --- apps/api/src/tenders/dto/source-config.dto.ts | 35 +++++++++++++++ apps/api/src/tenders/dto/tender-query.dto.ts | 44 +++++++++++++++++++ 2 files changed, 79 insertions(+) create mode 100644 apps/api/src/tenders/dto/source-config.dto.ts create mode 100644 apps/api/src/tenders/dto/tender-query.dto.ts diff --git a/apps/api/src/tenders/dto/source-config.dto.ts b/apps/api/src/tenders/dto/source-config.dto.ts new file mode 100644 index 0000000..7cccc51 --- /dev/null +++ b/apps/api/src/tenders/dto/source-config.dto.ts @@ -0,0 +1,35 @@ +import { IsBoolean, IsInt, IsOptional, Max, Min } from 'class-validator'; + +/** + * DTO for reading/updating the singleton `doe-opendata` poll config + * (`TenderSourcePollConfig`, `sourceType: 'doe-opendata'`). + * + * Security: + * - T-10-15: pollIntervalMin bounded 5-1440 — same DoS-mitigation floor as + * DkvConfigDto.pollIntervalMin (no sub-5-minute polling of the platform-wide + * scheduler). + * + * Note: `pollIntervalMin` is the cron-tick frequency (D-04 default 60) — + * NOT the DÖE fetch frequency. The actual upstream HTTP call is gated + * separately by the day-cursor check inside TenderIngestionService + * (Pitfall A: cron tick frequency != actual-fetch frequency for this + * day-granular source). + */ +export class SourceConfigDto { + /** + * Cron-tick interval in minutes. Minimum 5 minutes (DoS mitigation, T-10-15). + * Maximum 1440 (24h). + */ + @IsOptional() + @IsInt() + @Min(5) + @Max(1440) + pollIntervalMin?: number; + + /** + * Whether the platform-wide DÖE poll cron job is active. + */ + @IsOptional() + @IsBoolean() + isActive?: boolean; +} diff --git a/apps/api/src/tenders/dto/tender-query.dto.ts b/apps/api/src/tenders/dto/tender-query.dto.ts new file mode 100644 index 0000000..c594f90 --- /dev/null +++ b/apps/api/src/tenders/dto/tender-query.dto.ts @@ -0,0 +1,44 @@ +import { Type } from 'class-transformer'; +import { IsIn, IsInt, IsOptional, Max, Min } from 'class-validator'; + +/** + * Query DTO for paginating + filtering the global tender catalog. + * + * Security: + * - T-10-15: pagination bounds (limit @Max(100)) mitigate oversized + * result-set DoS — same convention as DkvHistoryQueryDto. + * + * Used for: GET /modules/tender-radar?page=1&limit=20&status=active + * + * No region/CPV filters here — rich filtering is Phase 11 (FILTER-*). + */ +export class TenderQueryDto { + /** + * Page number (1-based). Defaults to 1 when omitted. + * @Type(() => Number) coerces the query-string value to a number before + * validation — required because HTTP query params always arrive as strings. + */ + @IsOptional() + @IsInt() + @Min(1) + @Type(() => Number) + page?: number; + + /** + * Number of records per page. Defaults to 20 when omitted. + * T-10-15: bounded integer mitigates oversized result-set DoS. + */ + @IsOptional() + @IsInt() + @Min(1) + @Max(100) + @Type(() => Number) + limit?: number; + + /** + * Status filter. Defaults to active-only at the query layer when omitted. + */ + @IsOptional() + @IsIn(['active', 'expired']) + status?: string; +}