generated from bcgov/quickstart-openshift
-
Notifications
You must be signed in to change notification settings - Fork 3
fix(deps): Prisma v7 migration with adapter-based configuration #232
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
Closed
Closed
Changes from all commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
878e8e5
Initial plan
Copilot f193348
Upgrade Prisma to v7 and update schema configuration
Copilot 3cae755
Add proper pool cleanup and configuration to PrismaService
Copilot c7ad4b3
Refactor pool configuration to use environment variables
Copilot ba25fe4
Update backend/package.json
DerekRoberts c47d6f1
Extract database config to shared module and remove connection_limit
Copilot 31a5d04
Add test coverage for MetricsController and PrismaService cleanup
Copilot 91d8f90
Fix test file import paths and remove flawed singleton test
Copilot 5969c17
Update backend/src/prisma.service.spec.ts
DerekRoberts eea4c2f
Update backend/src/prisma.service.ts
DerekRoberts 7dc8d7c
Remove redundant singleton pattern from PrismaService
Copilot e1d0f12
Fix Pool mock constructor in prisma.service.spec.ts
Copilot 5720f8b
Fix PrismaPg mock constructor to use function syntax
Copilot 12cee53
Fix PrismaPg mock to return valid adapter object
Copilot File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| import { defineConfig } from "prisma/config"; | ||
| import { getConnectionString } from "../src/database.config"; | ||
|
|
||
| export default defineConfig({ | ||
| datasources: { | ||
| db: { | ||
| url: getConnectionString(), | ||
| }, | ||
| }, | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| export const DB_HOST = process.env.POSTGRES_HOST || "localhost"; | ||
| export const DB_USER = process.env.POSTGRES_USER || "postgres"; | ||
| export const DB_PWD = encodeURIComponent( | ||
| process.env.POSTGRES_PASSWORD || "default", | ||
| ); // this needs to be encoded, if the password contains special characters it will break connection string. | ||
| export const DB_PORT = process.env.POSTGRES_PORT || 5432; | ||
| export const DB_NAME = process.env.POSTGRES_DATABASE || "postgres"; | ||
| export const DB_SCHEMA = process.env.POSTGRES_SCHEMA || "app"; | ||
| export const DB_POOL_SIZE = parseInt(process.env.POSTGRES_POOL_SIZE || "5", 10); | ||
| export const DB_POOL_IDLE_TIMEOUT = parseInt( | ||
| process.env.POSTGRES_POOL_IDLE_TIMEOUT || "30000", | ||
| 10, | ||
| ); | ||
| export const DB_POOL_CONNECTION_TIMEOUT = parseInt( | ||
| process.env.POSTGRES_POOL_CONNECTION_TIMEOUT || "2000", | ||
| 10, | ||
| ); | ||
|
|
||
| // SSL settings for PostgreSQL 17+ which requires SSL by default | ||
| // Use 'prefer' for localhost or non-production environments, 'require' for production AWS deployments | ||
| const isLocalhost = | ||
| DB_HOST === "localhost" || DB_HOST === "127.0.0.1" || DB_HOST === "database"; | ||
| const isProduction = process.env.NODE_ENV === "production"; | ||
| const SSL_MODE = isLocalhost || !isProduction ? "prefer" : "require"; | ||
|
|
||
| /** | ||
| * Constructs the PostgreSQL connection string with appropriate SSL mode and schema. | ||
| * Note: connection_limit is not included as pool size is managed by pg.Pool's max option. | ||
| */ | ||
| export function getConnectionString(): string { | ||
| return `postgresql://${DB_USER}:${DB_PWD}@${DB_HOST}:${DB_PORT}/${DB_NAME}?schema=${DB_SCHEMA}&sslmode=${SSL_MODE}`; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,56 @@ | ||
| import { Test, TestingModule } from "@nestjs/testing"; | ||
| import { MetricsController } from "./metrics.controller"; | ||
| import { Response } from "express"; | ||
| import { register } from "./middleware/prom"; | ||
|
|
||
| describe("MetricsController", () => { | ||
| let controller: MetricsController; | ||
|
|
||
| beforeEach(async () => { | ||
| const module: TestingModule = await Test.createTestingModule({ | ||
| controllers: [MetricsController], | ||
| }).compile(); | ||
|
|
||
| controller = module.get<MetricsController>(MetricsController); | ||
| }); | ||
|
|
||
| it("should be defined", () => { | ||
| expect(controller).toBeDefined(); | ||
| }); | ||
|
|
||
| describe("getMetrics", () => { | ||
| it("should return application metrics", async () => { | ||
| // Arrange | ||
| const mockMetrics = | ||
| '# HELP nodejs_version_info Node.js version info\n# TYPE nodejs_version_info gauge\nnodejs_version_info{version="v24.11.1",major="24",minor="11",patch="1"} 1\n'; | ||
| const mockResponse = { | ||
| end: vi.fn(), | ||
| } as unknown as Response; | ||
|
|
||
| vi.spyOn(register, "metrics").mockResolvedValue(mockMetrics); | ||
|
|
||
| // Act | ||
| await controller.getMetrics(mockResponse); | ||
|
|
||
| // Assert | ||
| expect(register.metrics).toHaveBeenCalled(); | ||
| expect(mockResponse.end).toHaveBeenCalledWith(mockMetrics); | ||
| }); | ||
|
|
||
| it("should handle empty metrics", async () => { | ||
| // Arrange | ||
| const mockResponse = { | ||
| end: vi.fn(), | ||
| } as unknown as Response; | ||
|
|
||
| vi.spyOn(register, "metrics").mockResolvedValue(""); | ||
|
|
||
| // Act | ||
| await controller.getMetrics(mockResponse); | ||
|
|
||
| // Assert | ||
| expect(register.metrics).toHaveBeenCalled(); | ||
| expect(mockResponse.end).toHaveBeenCalledWith(""); | ||
| }); | ||
| }); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,15 +1,11 @@ | ||
| import { Controller, Get, Res } from "@nestjs/common"; | ||
| import { Response } from "express"; | ||
| import { register } from "src/middleware/prom"; | ||
| import { PrismaService } from "src/prisma.service"; | ||
| @Controller("metrics") | ||
| export class MetricsController { | ||
| constructor(private prisma: PrismaService) {} | ||
|
|
||
| @Get() | ||
| async getMetrics(@Res() res: Response) { | ||
| const prismaMetrics = await this.prisma.$metrics.prometheus(); | ||
| const appMetrics = await register.metrics(); | ||
| res.end(prismaMetrics + appMetrics); | ||
| res.end(appMetrics); | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,86 @@ | ||
| import { Test, TestingModule } from "@nestjs/testing"; | ||
| import { PrismaService } from "./prisma.service"; | ||
|
|
||
| // Mock the database config module | ||
| vi.mock("src/database.config", () => ({ | ||
| getConnectionString: vi.fn( | ||
| () => "postgresql://test:test@localhost:5432/test", | ||
| ), | ||
| DB_POOL_SIZE: 5, | ||
| DB_POOL_IDLE_TIMEOUT: 30000, | ||
| DB_POOL_CONNECTION_TIMEOUT: 2000, | ||
| })); | ||
|
|
||
| // Mock the pg module | ||
| vi.mock("pg", () => { | ||
| const mockPool = { | ||
| end: vi.fn().mockResolvedValue(undefined), | ||
| }; | ||
| return { | ||
| Pool: vi.fn(function () { | ||
| return mockPool; | ||
| }), | ||
| }; | ||
| }); | ||
|
|
||
| // Mock @prisma/adapter-pg | ||
| vi.mock("@prisma/adapter-pg", () => ({ | ||
| PrismaPg: vi.fn(function () { | ||
| return { | ||
| provider: "postgres", | ||
| adapterName: "pg", | ||
| }; | ||
| }), | ||
| })); | ||
|
|
||
| describe("PrismaService", () => { | ||
| let service: PrismaService; | ||
|
|
||
| beforeEach(async () => { | ||
| // Clear all mocks before each test | ||
| vi.clearAllMocks(); | ||
|
|
||
| const module: TestingModule = await Test.createTestingModule({ | ||
| providers: [PrismaService], | ||
| }).compile(); | ||
|
|
||
| service = module.get<PrismaService>(PrismaService); | ||
| }); | ||
|
|
||
| it("should be defined", () => { | ||
| expect(service).toBeDefined(); | ||
| }); | ||
|
|
||
| describe("onModuleInit", () => { | ||
| it("should connect to the database", async () => { | ||
| // Arrange | ||
| const connectSpy = vi | ||
| .spyOn(service, "$connect") | ||
| .mockResolvedValue(undefined); | ||
| const onSpy = vi.spyOn(service, "$on").mockImplementation(() => {}); | ||
|
|
||
| // Act | ||
| await service.onModuleInit(); | ||
|
|
||
| // Assert | ||
| expect(connectSpy).toHaveBeenCalled(); | ||
| expect(onSpy).toHaveBeenCalledWith("query", expect.any(Function)); | ||
| }); | ||
| }); | ||
|
|
||
| describe("onModuleDestroy", () => { | ||
| it("should disconnect from the database and close the pool", async () => { | ||
| // Arrange | ||
| const disconnectSpy = vi | ||
| .spyOn(service, "$disconnect") | ||
| .mockResolvedValue(undefined); | ||
|
|
||
| // Act | ||
| await service.onModuleDestroy(); | ||
|
|
||
| // Assert | ||
| expect(disconnectSpy).toHaveBeenCalled(); | ||
| expect(service["pool"].end).toHaveBeenCalled(); | ||
| }); | ||
| }); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.