-
Notifications
You must be signed in to change notification settings - Fork 0
Refactor: Structured logging and type safety improvements #9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
d454128
8cbfabe
70ae350
ba272b2
1878a68
396782e
f75f2ab
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| type: patch | ||
| area: api | ||
| summary: Improve error handling with structured logging and better type safety in tests | ||
| --- |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,16 +1,23 @@ | ||
| import { Injectable, OnModuleDestroy, OnModuleInit } from "@nestjs/common"; | ||
| import { Injectable, Logger, OnModuleDestroy, OnModuleInit } from "@nestjs/common"; | ||
| import { PrismaClient } from "@prisma/client"; | ||
| import { ensureEnvironmentLoaded } from "@corpsim/db"; | ||
|
|
||
| ensureEnvironmentLoaded(); | ||
|
|
||
| interface PrismaErrorWithCode extends Error { | ||
| errorCode?: string; | ||
| } | ||
|
|
||
| function isPrismaErrorWithCode(error: unknown): error is PrismaErrorWithCode { | ||
| return error instanceof Error && "errorCode" in error; | ||
| } | ||
|
Comment on lines
+7
to
+13
|
||
|
|
||
| function shouldRetryConnect(error: unknown): boolean { | ||
| if (!(error instanceof Error)) { | ||
| if (!isPrismaErrorWithCode(error)) { | ||
| return false; | ||
| } | ||
|
|
||
| const prismaCode = (error as { errorCode?: string }).errorCode; | ||
| if (prismaCode === "P1001") { | ||
| if (error.errorCode === "P1001") { | ||
| return true; | ||
| } | ||
|
|
||
|
|
@@ -25,6 +32,8 @@ function sleep(ms: number): Promise<void> { | |
|
|
||
| @Injectable() | ||
| export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy { | ||
| private readonly logger = new Logger(PrismaService.name); | ||
|
|
||
| async onModuleInit(): Promise<void> { | ||
| const maxAttempts = 30; | ||
| const baseDelayMs = 1_000; | ||
|
|
@@ -39,8 +48,8 @@ export class PrismaService extends PrismaClient implements OnModuleInit, OnModul | |
| } | ||
|
|
||
| const delayMs = Math.min(baseDelayMs * attempt, 5_000); | ||
| console.warn( | ||
| `[prisma] database not reachable on attempt ${attempt}/${maxAttempts}; retrying in ${delayMs}ms` | ||
| this.logger.warn( | ||
| `Database not reachable on attempt ${attempt}/${maxAttempts}; retrying in ${delayMs}ms` | ||
| ); | ||
| await sleep(delayMs); | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| import { Prisma, PrismaClient } from "@prisma/client"; | ||
|
|
||
| /** | ||
| * Creates a minimal PrismaClient mock that supports $transaction. | ||
| * | ||
| * Note: This uses `as unknown as PrismaClient` because the actual test | ||
| * implementations only use the $transaction method, but the service functions | ||
| * expect the full PrismaClient type. This is a deliberate trade-off to keep | ||
| * test mocks simple while maintaining type compatibility. | ||
| */ | ||
| export function createPrismaTransactionMock( | ||
| tx: Prisma.TransactionClient | ||
| ): PrismaClient { | ||
| return { | ||
| $transaction: async <T>( | ||
| callback: (transactionClient: Prisma.TransactionClient) => Promise<T> | ||
| ): Promise<T> => callback(tx) | ||
| } as unknown as PrismaClient; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The NestJS Logger.error method signature expects a stack trace string as the second argument, not a context object. Consider restructuring this to pass the stack trace directly as the second parameter, or format it as a string. For example:
this.logger.error('Unhandled exception at ${request?.method} ${request?.url}: ${message}', stack)where message and stack are extracted from the exception.