@dismissible/nestjs-postgres-storage 0.0.2-canary.585db17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +309 -0
- package/bin/dismissible-prisma.js +57 -0
- package/package.json +66 -0
- package/prisma/generated/prisma/browser.ts +24 -0
- package/prisma/generated/prisma/client.ts +44 -0
- package/prisma/generated/prisma/commonInputTypes.ts +208 -0
- package/prisma/generated/prisma/enums.ts +15 -0
- package/prisma/generated/prisma/internal/class.ts +190 -0
- package/prisma/generated/prisma/internal/prismaNamespace.ts +759 -0
- package/prisma/generated/prisma/internal/prismaNamespaceBrowser.ts +105 -0
- package/prisma/generated/prisma/models/DismissibleItem.ts +1134 -0
- package/prisma/generated/prisma/models.ts +12 -0
- package/prisma/migrations/20251219094936_init/migration.sql +12 -0
- package/prisma/migrations/migration_lock.toml +3 -0
- package/prisma/schema.prisma +23 -0
- package/prisma.config.mjs +33 -0
- package/src/index.ts +6 -0
- package/src/postgres-storage.adapter.spec.ts +160 -0
- package/src/postgres-storage.adapter.ts +90 -0
- package/src/postgres-storage.config.ts +11 -0
- package/src/postgres-storage.module.ts +79 -0
- package/src/prisma-config.spec.ts +93 -0
- package/src/prisma-config.ts +63 -0
- package/src/prisma.service.spec.ts +125 -0
- package/src/prisma.service.ts +44 -0
- package/src/schema-path.spec.ts +22 -0
- package/src/schema-path.ts +17 -0
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { mock } from 'ts-jest-mocker';
|
|
2
|
+
import { PrismaService } from './prisma.service';
|
|
3
|
+
import { PostgresStorageConfig } from './postgres-storage.config';
|
|
4
|
+
import { IDismissibleLogger } from '@dismissible/nestjs-logger';
|
|
5
|
+
|
|
6
|
+
const mockPoolInstance = {};
|
|
7
|
+
const mockPrismaPgInstance = {};
|
|
8
|
+
|
|
9
|
+
jest.mock('pg', () => ({
|
|
10
|
+
Pool: jest.fn().mockImplementation(() => mockPoolInstance),
|
|
11
|
+
}));
|
|
12
|
+
|
|
13
|
+
jest.mock('@prisma/adapter-pg', () => ({
|
|
14
|
+
PrismaPg: jest.fn().mockImplementation(() => mockPrismaPgInstance),
|
|
15
|
+
}));
|
|
16
|
+
|
|
17
|
+
const mockPrismaClientMethods = {
|
|
18
|
+
$connect: jest.fn(),
|
|
19
|
+
$disconnect: jest.fn(),
|
|
20
|
+
$queryRaw: jest.fn(),
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
jest.mock('../prisma/generated/prisma/client', () => {
|
|
24
|
+
class MockPrismaClient {
|
|
25
|
+
$connect = jest.fn();
|
|
26
|
+
$disconnect = jest.fn();
|
|
27
|
+
$queryRaw = jest.fn();
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
30
|
+
PrismaClient: MockPrismaClient,
|
|
31
|
+
};
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
describe('PrismaService', () => {
|
|
35
|
+
let service: PrismaService;
|
|
36
|
+
let mockConfig: PostgresStorageConfig;
|
|
37
|
+
let mockLogger: jest.Mocked<IDismissibleLogger>;
|
|
38
|
+
|
|
39
|
+
beforeEach(() => {
|
|
40
|
+
mockConfig = {
|
|
41
|
+
connectionString: 'postgresql://user:password@localhost:5432/testdb',
|
|
42
|
+
};
|
|
43
|
+
mockLogger = mock<IDismissibleLogger>({ failIfMockNotProvided: false });
|
|
44
|
+
|
|
45
|
+
jest.clearAllMocks();
|
|
46
|
+
mockPrismaClientMethods.$connect.mockClear();
|
|
47
|
+
mockPrismaClientMethods.$disconnect.mockClear();
|
|
48
|
+
mockPrismaClientMethods.$queryRaw.mockClear();
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
describe('constructor', () => {
|
|
52
|
+
it('should create PrismaService with pool and adapter', () => {
|
|
53
|
+
const { Pool } = require('pg');
|
|
54
|
+
const { PrismaPg } = require('@prisma/adapter-pg');
|
|
55
|
+
|
|
56
|
+
service = new PrismaService(mockConfig, mockLogger);
|
|
57
|
+
|
|
58
|
+
expect(service).toBeDefined();
|
|
59
|
+
expect(Pool).toHaveBeenCalledWith({
|
|
60
|
+
connectionString: mockConfig.connectionString,
|
|
61
|
+
});
|
|
62
|
+
expect(PrismaPg).toHaveBeenCalledWith(mockPoolInstance);
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
describe('onModuleInit', () => {
|
|
67
|
+
it('should connect to database and verify connection', async () => {
|
|
68
|
+
service = new PrismaService(mockConfig, mockLogger);
|
|
69
|
+
(service as any).$connect.mockResolvedValue(undefined);
|
|
70
|
+
(service as any).$queryRaw.mockResolvedValue([{ '?column?': 1 }]);
|
|
71
|
+
|
|
72
|
+
await service.onModuleInit();
|
|
73
|
+
|
|
74
|
+
expect(mockLogger.debug).toHaveBeenCalledWith('Connecting to PostgreSQL database');
|
|
75
|
+
expect((service as any).$connect).toHaveBeenCalled();
|
|
76
|
+
expect((service as any).$queryRaw).toHaveBeenCalled();
|
|
77
|
+
expect(mockLogger.debug).toHaveBeenCalledWith('Connected to PostgreSQL database');
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('should throw error when connection fails', async () => {
|
|
81
|
+
service = new PrismaService(mockConfig, mockLogger);
|
|
82
|
+
const connectError = new Error('Connection refused');
|
|
83
|
+
(service as any).$connect = jest.fn().mockRejectedValue(connectError);
|
|
84
|
+
|
|
85
|
+
await expect(service.onModuleInit()).rejects.toThrow('Connection refused');
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('should throw error when query verification fails', async () => {
|
|
89
|
+
service = new PrismaService(mockConfig, mockLogger);
|
|
90
|
+
(service as any).$connect.mockResolvedValue(undefined);
|
|
91
|
+
(service as any).$queryRaw.mockRejectedValue(new Error('Query failed'));
|
|
92
|
+
|
|
93
|
+
await expect(service.onModuleInit()).rejects.toThrow(
|
|
94
|
+
'Database connection failed: Query failed. Ensure PostgreSQL is running and DISMISSIBLE_POSTGRES_STORAGE_CONNECTION_STRING is configured correctly.',
|
|
95
|
+
);
|
|
96
|
+
expect(mockLogger.error).toHaveBeenCalledWith(
|
|
97
|
+
'Failed to connect to PostgreSQL database',
|
|
98
|
+
expect.any(Error),
|
|
99
|
+
);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('should handle non-Error objects in catch block', async () => {
|
|
103
|
+
service = new PrismaService(mockConfig, mockLogger);
|
|
104
|
+
(service as any).$connect.mockResolvedValue(undefined);
|
|
105
|
+
(service as any).$queryRaw.mockRejectedValue('String error');
|
|
106
|
+
|
|
107
|
+
await expect(service.onModuleInit()).rejects.toThrow(
|
|
108
|
+
'Database connection failed: Unknown error. Ensure PostgreSQL is running and DISMISSIBLE_POSTGRES_STORAGE_CONNECTION_STRING is configured correctly.',
|
|
109
|
+
);
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
describe('onModuleDestroy', () => {
|
|
114
|
+
it('should disconnect from database', async () => {
|
|
115
|
+
service = new PrismaService(mockConfig, mockLogger);
|
|
116
|
+
(service as any).$disconnect.mockResolvedValue(undefined);
|
|
117
|
+
|
|
118
|
+
await service.onModuleDestroy();
|
|
119
|
+
|
|
120
|
+
expect(mockLogger.debug).toHaveBeenCalledWith('Disconnecting from PostgreSQL database');
|
|
121
|
+
expect((service as any).$disconnect).toHaveBeenCalled();
|
|
122
|
+
expect(mockLogger.debug).toHaveBeenCalledWith('Disconnected from PostgreSQL database');
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
});
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Injectable, OnModuleInit, OnModuleDestroy, Inject } from '@nestjs/common';
|
|
2
|
+
import { PrismaClient } from '../prisma/generated/prisma/client';
|
|
3
|
+
import { PrismaPg } from '@prisma/adapter-pg';
|
|
4
|
+
import { Pool } from 'pg';
|
|
5
|
+
import { DISMISSIBLE_LOGGER, IDismissibleLogger } from '@dismissible/nestjs-logger';
|
|
6
|
+
import { PostgresStorageConfig } from './postgres-storage.config';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* PrismaService wraps the PrismaClient and handles connection lifecycle.
|
|
10
|
+
* It connects to the database on module initialization and disconnects on shutdown.
|
|
11
|
+
*/
|
|
12
|
+
@Injectable()
|
|
13
|
+
export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy {
|
|
14
|
+
constructor(
|
|
15
|
+
private readonly config: PostgresStorageConfig,
|
|
16
|
+
@Inject(DISMISSIBLE_LOGGER) private readonly logger: IDismissibleLogger,
|
|
17
|
+
) {
|
|
18
|
+
const pool = new Pool({ connectionString: config.connectionString });
|
|
19
|
+
const adapter = new PrismaPg(pool);
|
|
20
|
+
super({ adapter });
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async onModuleInit(): Promise<void> {
|
|
24
|
+
this.logger.debug('Connecting to PostgreSQL database');
|
|
25
|
+
await this.$connect();
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
await this.$queryRaw`SELECT 1`;
|
|
29
|
+
this.logger.debug('Connected to PostgreSQL database');
|
|
30
|
+
} catch (error) {
|
|
31
|
+
this.logger.error('Failed to connect to PostgreSQL database', error);
|
|
32
|
+
throw new Error(
|
|
33
|
+
`Database connection failed: ${error instanceof Error ? error.message : 'Unknown error'}. ` +
|
|
34
|
+
'Ensure PostgreSQL is running and DISMISSIBLE_POSTGRES_STORAGE_CONNECTION_STRING is configured correctly.',
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async onModuleDestroy(): Promise<void> {
|
|
40
|
+
this.logger.debug('Disconnecting from PostgreSQL database');
|
|
41
|
+
await this.$disconnect();
|
|
42
|
+
this.logger.debug('Disconnected from PostgreSQL database');
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { getPrismaSchemaPath } from './schema-path';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
|
|
4
|
+
describe('schema-path', () => {
|
|
5
|
+
describe('getPrismaSchemaPath', () => {
|
|
6
|
+
it('should return the absolute path to the Prisma schema file', () => {
|
|
7
|
+
const schemaPath = getPrismaSchemaPath();
|
|
8
|
+
|
|
9
|
+
expect(schemaPath).toBeDefined();
|
|
10
|
+
expect(typeof schemaPath).toBe('string');
|
|
11
|
+
expect(schemaPath).toContain('prisma');
|
|
12
|
+
expect(schemaPath).toContain('schema.prisma');
|
|
13
|
+
expect(schemaPath).toBe(join(__dirname, '..', 'prisma', 'schema.prisma'));
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('should return a valid path structure', () => {
|
|
17
|
+
const schemaPath = getPrismaSchemaPath();
|
|
18
|
+
|
|
19
|
+
expect(schemaPath).toMatch(/^(\/|[A-Z]:\\)/);
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { join } from 'path';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Returns the absolute path to the Prisma schema file.
|
|
5
|
+
* Useful for consumers who need to run prisma commands programmatically.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import { getPrismaSchemaPath } from '@dismissible/nestjs-postgres-storage';
|
|
10
|
+
* import { execSync } from 'child_process';
|
|
11
|
+
*
|
|
12
|
+
* execSync(`npx prisma generate --schema=${getPrismaSchemaPath()}`);
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
export function getPrismaSchemaPath(): string {
|
|
16
|
+
return join(__dirname, '..', 'prisma', 'schema.prisma');
|
|
17
|
+
}
|