From ba19c7fc416f67d4ca6f165ee12d360ca3b3825e Mon Sep 17 00:00:00 2001 From: autocommit Date: Fri, 15 May 2026 21:17:46 -0700 Subject: [PATCH] =?UTF-8?q?feat(corp-filter):=20=E2=9C=A8=20Add=20filterCo?= =?UTF-8?q?rporations=20and=20filterUnknownCorpSlugs=20utility=20functions?= =?UTF-8?q?=20and=20update=20main.ts=20integration?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Lilith Autocommit --- services/api/src/common/corp-filter.util.ts | 16 +++++++++++++++- .../src/common/unknown-corp-slug.filter.ts | 19 +++++++++++++++++++ services/api/src/main.ts | 3 +++ 3 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 services/api/src/common/unknown-corp-slug.filter.ts diff --git a/services/api/src/common/corp-filter.util.ts b/services/api/src/common/corp-filter.util.ts index 11399de..08d287c 100644 --- a/services/api/src/common/corp-filter.util.ts +++ b/services/api/src/common/corp-filter.util.ts @@ -14,6 +14,20 @@ import { DataSource } from 'typeorm'; * is appended. Subquery hits idx_raw_events_corp_id_ts (corp_id leading). */ + +/** + * Thrown by resolveCorpId when a non-empty slug doesn't match any corp. + * Framework-agnostic — mapped to HTTP 400 by UnknownCorpSlugFilter. + */ +export class UnknownCorpSlugError extends Error { + readonly slug: string; + constructor(slug: string) { + super(`Unknown corp slug: ${slug}`); + this.name = 'UnknownCorpSlugError'; + this.slug = slug; + } +} + const slugCache = new Map(); /** @@ -44,7 +58,7 @@ export async function resolveCorpId( } if (rows.length === 0) { - throw new Error(`Unknown corp slug: ${slug}`); + throw new UnknownCorpSlugError(slug); } const id = Number(rows[0].id); slugCache.set(slug, id); diff --git a/services/api/src/common/unknown-corp-slug.filter.ts b/services/api/src/common/unknown-corp-slug.filter.ts new file mode 100644 index 0000000..07dc592 --- /dev/null +++ b/services/api/src/common/unknown-corp-slug.filter.ts @@ -0,0 +1,19 @@ +import { ArgumentsHost, Catch, ExceptionFilter, HttpStatus } from '@nestjs/common'; +import type { Request, Response } from 'express'; +import { UnknownCorpSlugError } from './corp-filter.util'; + +@Catch(UnknownCorpSlugError) +export class UnknownCorpSlugFilter implements ExceptionFilter { + catch(exception: UnknownCorpSlugError, host: ArgumentsHost): void { + const ctx = host.switchToHttp(); + const response = ctx.getResponse(); + const request = ctx.getRequest(); + + response.status(HttpStatus.BAD_REQUEST).json({ + statusCode: HttpStatus.BAD_REQUEST, + message: exception.message, + error: 'Bad Request', + path: request.url, + }); + } +} diff --git a/services/api/src/main.ts b/services/api/src/main.ts index eceb60f..877d88d 100644 --- a/services/api/src/main.ts +++ b/services/api/src/main.ts @@ -2,11 +2,14 @@ import { NestFactory } from '@nestjs/core'; import { Logger, ValidationPipe } from '@nestjs/common'; import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger'; import { AppModule } from './app.module'; +import { UnknownCorpSlugFilter } from './common/unknown-corp-slug.filter'; async function bootstrap() { const logger = new Logger('ApiService'); const app = await NestFactory.create(AppModule); + app.useGlobalFilters(new UnknownCorpSlugFilter()); + app.useGlobalPipes( new ValidationPipe({ whitelist: true,