feat(11-06): TenderSavedSearch model + CRUD service (FILTER-06)
GREEN phase — adds TenderSavedSearch (userId+tenantId scoped, filters
Json, @@unique([userId,name])), the migration (applied to local dev DB),
and TenderSavedSearchService following the FavoritesService/
TenderTriageService pattern: manual where:{userId} scoping (no
forTenant()/RLS), ownership check before update/remove, P2002 unique
conflicts translated to ConflictException.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
import { IsNotEmpty, IsObject, IsOptional, IsString, MaxLength } from 'class-validator';
|
||||
|
||||
/**
|
||||
* Body DTO for POST /modules/tender-radar/saved-searches (FILTER-06).
|
||||
*
|
||||
* Security (T-11-14 / V4 — IDOR): deliberately has NO userId or tenantId
|
||||
* field — both are always derived server-side from the auth context
|
||||
* (FavoritesController.extractContext pattern) in TendersController, never
|
||||
* trusted from the request body.
|
||||
*
|
||||
* `filters` is validated only as a plain object (T-11-17 / V5) — its shape
|
||||
* mirrors the FilterPanel's URL-searchParams contract on the frontend, but
|
||||
* the backend does not re-validate individual filter keys here; Prisma
|
||||
* stores it as parametrized JSONB (no string interpolation, V5).
|
||||
*/
|
||||
export class CreateSavedSearchDto {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@MaxLength(100)
|
||||
name!: string;
|
||||
|
||||
@IsObject()
|
||||
filters!: Record<string, unknown>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Body DTO for PATCH /modules/tender-radar/saved-searches/:searchId.
|
||||
* Both fields optional — only provided fields are updated (FavoritesService
|
||||
* UpdateFavoriteDto pattern).
|
||||
*/
|
||||
export class UpdateSavedSearchDto {
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@MaxLength(100)
|
||||
name?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsObject()
|
||||
filters?: Record<string, unknown>;
|
||||
}
|
||||
Reference in New Issue
Block a user