@friggframework/core 2.0.0-next.49 → 2.0.0-next.50

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.
@@ -501,8 +501,9 @@ database: {
501
501
  # Run encryption tests
502
502
  npm test -- database/encryption/
503
503
 
504
- # Tests use explicit database type parameter for testing:
505
- # createHealthCheckRepository('mongodb')
504
+ # Tests use explicit prismaClient injection:
505
+ # const { prisma } = require('../prisma');
506
+ # const repository = createHealthCheckRepository({ prismaClient: prisma });
506
507
  ```
507
508
 
508
509
  ## Error Handling & Logging
@@ -3,25 +3,31 @@ const { HealthCheckRepositoryPostgreSQL } = require('./health-check-repository-p
3
3
  const config = require('../config');
4
4
 
5
5
  /**
6
- * Health Check Repository Factory
7
- * Creates the appropriate repository adapter based on database type
6
+ * Factory function to create a health check repository for the configured database type.
7
+ * Requires explicit prismaClient injection to support IoC container patterns.
8
8
  *
9
- * Usage:
10
- * ```javascript
11
- * const repository = createHealthCheckRepository();
12
- * ```
9
+ * @param {Object} options
10
+ * @param {Object} options.prismaClient - Prisma client instance (required for dependency injection)
11
+ * @returns {HealthCheckRepositoryInterface} Database-specific health check repository
12
+ * @throws {Error} If prismaClient is not provided
13
13
  *
14
- * @returns {HealthCheckRepositoryInterface} Configured repository adapter
14
+ * @example
15
+ * const { prisma } = require('../prisma');
16
+ * const repository = createHealthCheckRepository({ prismaClient: prisma });
15
17
  */
16
- function createHealthCheckRepository() {
18
+ function createHealthCheckRepository({ prismaClient } = {}) {
19
+ if (!prismaClient) {
20
+ throw new Error('prismaClient is required');
21
+ }
22
+
17
23
  const dbType = config.DB_TYPE;
18
24
 
19
25
  switch (dbType) {
20
26
  case 'mongodb':
21
- return new HealthCheckRepositoryMongoDB();
27
+ return new HealthCheckRepositoryMongoDB({ prismaClient });
22
28
 
23
29
  case 'postgresql':
24
- return new HealthCheckRepositoryPostgreSQL();
30
+ return new HealthCheckRepositoryPostgreSQL({ prismaClient });
25
31
 
26
32
  default:
27
33
  throw new Error(
@@ -32,7 +38,6 @@ function createHealthCheckRepository() {
32
38
 
33
39
  module.exports = {
34
40
  createHealthCheckRepository,
35
- // Export adapters for direct testing
36
41
  HealthCheckRepositoryMongoDB,
37
42
  HealthCheckRepositoryPostgreSQL,
38
43
  };
@@ -76,9 +76,10 @@ class HealthCheckRepositoryInterface {
76
76
  /**
77
77
  * Get database connection state
78
78
  *
79
- * @returns {Object} Connection state info
79
+ * @returns {Promise<Object>} Connection state info
80
+ * @abstract
80
81
  */
81
- getDatabaseConnectionState() {
82
+ async getDatabaseConnectionState() {
82
83
  throw new Error('Method getDatabaseConnectionState must be implemented by subclass');
83
84
  }
84
85
  }
@@ -1,62 +1,78 @@
1
- const { prisma } = require('../prisma');
2
1
  const { mongoose } = require('../mongoose');
3
2
  const {
4
3
  HealthCheckRepositoryInterface,
5
4
  } = require('./health-check-repository-interface');
6
5
 
7
- /**
8
- * MongoDB-specific Health Check Repository
9
- *
10
- * Provides MongoDB-specific database operations for health testing.
11
- * Uses Mongoose for MongoDB-specific operations (raw access, ping).
12
- */
13
6
  class HealthCheckRepositoryMongoDB extends HealthCheckRepositoryInterface {
14
- constructor() {
7
+ /**
8
+ * @param {Object} params
9
+ * @param {Object} params.prismaClient - Prisma client instance
10
+ */
11
+ constructor({ prismaClient }) {
15
12
  super();
13
+ this.prisma = prismaClient;
16
14
  }
17
15
 
18
- getDatabaseConnectionState() {
19
- const stateMap = {
20
- 0: 'disconnected',
21
- 1: 'connected',
22
- 2: 'connecting',
23
- 3: 'disconnecting',
24
- };
25
- const readyState = mongoose.connection.readyState;
16
+ /**
17
+ * @returns {Promise<{readyState: number, stateName: string, isConnected: boolean}>}
18
+ */
19
+ async getDatabaseConnectionState() {
20
+ let isConnected = false;
21
+ let stateName = 'unknown';
22
+
23
+ try {
24
+ await this.prisma.$runCommandRaw({ ping: 1 });
25
+ isConnected = true;
26
+ stateName = 'connected';
27
+ } catch (error) {
28
+ stateName = 'disconnected';
29
+ }
26
30
 
27
31
  return {
28
- readyState,
29
- stateName: stateMap[readyState],
30
- isConnected: readyState === 1,
32
+ readyState: isConnected ? 1 : 0,
33
+ readyState: isConnected ? 1 : 0,
34
+ stateName,
35
+ isConnected,
31
36
  };
32
37
  }
33
38
 
34
39
  async pingDatabase(maxTimeMS = 2000) {
35
40
  const pingStart = Date.now();
36
- await mongoose.connection.db.admin().ping({ maxTimeMS });
41
+
42
+ // Create a timeout promise that rejects after maxTimeMS
43
+ const timeoutPromise = new Promise((_, reject) =>
44
+ setTimeout(() => reject(new Error('Database ping timeout')), maxTimeMS)
45
+ );
46
+
47
+ // Race between the database ping and the timeout
48
+ await Promise.race([
49
+ prisma.$queryRaw`SELECT 1`.catch(() => {
50
+ // For MongoDB, use runCommandRaw instead
51
+ return prisma.$runCommandRaw({ ping: 1 });
52
+ }),
53
+ timeoutPromise
54
+ ]);
55
+
56
+ return Date.now() - pingStart;
57
+ }
37
58
  return Date.now() - pingStart;
38
59
  }
39
60
 
40
61
  async createCredential(credentialData) {
41
- // Note: Collection existence is ensured at application startup via
42
- // initializeMongoDBSchema() in database/utils/mongodb-schema-init.js
43
- // This prevents "Cannot create namespace in multi-document transaction" errors
44
- return await prisma.credential.create({
62
+ return await this.prisma.credential.create({
45
63
  data: credentialData,
46
64
  });
47
65
  }
48
66
 
49
67
  async findCredentialById(id) {
50
- return await prisma.credential.findUnique({
68
+ return await this.prisma.credential.findUnique({
51
69
  where: { id },
52
70
  });
53
71
  }
54
72
 
55
73
  /**
56
- * Get raw credential from MongoDB bypassing Prisma encryption extension
57
- * Uses Mongoose to access raw MongoDB collection
58
- * @param {string} id - Credential ID
59
- * @returns {Promise<Object|null>} Raw credential from database
74
+ * @param {string} id
75
+ * @returns {Promise<Object|null>}
60
76
  */
61
77
  async getRawCredentialById(id) {
62
78
  const { ObjectId } = require('mongodb');
@@ -66,7 +82,7 @@ class HealthCheckRepositoryMongoDB extends HealthCheckRepositoryInterface {
66
82
  }
67
83
 
68
84
  async deleteCredential(id) {
69
- await prisma.credential.delete({
85
+ await this.prisma.credential.delete({
70
86
  where: { id },
71
87
  });
72
88
  }
@@ -1,59 +1,67 @@
1
- const { prisma } = require('../prisma');
2
1
  const {
3
2
  HealthCheckRepositoryInterface,
4
3
  } = require('./health-check-repository-interface');
5
4
 
6
- /**
7
- * PostgreSQL-specific Health Check Repository
8
- *
9
- * Provides PostgreSQL-specific database operations for health testing.
10
- * Uses Prisma raw queries for PostgreSQL-specific operations.
11
- */
12
5
  class HealthCheckRepositoryPostgreSQL extends HealthCheckRepositoryInterface {
13
- constructor() {
6
+ /**
7
+ * @param {Object} params
8
+ * @param {Object} params.prismaClient - Prisma client instance
9
+ */
10
+ constructor({ prismaClient }) {
14
11
  super();
12
+ this.prisma = prismaClient;
15
13
  }
16
14
 
17
- getDatabaseConnectionState() {
18
- // PostgreSQL connection state via Prisma
19
- // Note: Prisma doesn't expose connection state like Mongoose
20
- // We check if prisma is connected by attempting a query
15
+ /**
16
+ * @returns {Promise<{readyState: number, stateName: string, isConnected: boolean}>}
17
+ */
18
+ async getDatabaseConnectionState() {
19
+ let isConnected = false;
20
+ let stateName = 'unknown';
21
+
22
+ try {
23
+ await this.prisma.$queryRaw`SELECT 1`;
24
+ isConnected = true;
25
+ stateName = 'connected';
26
+ } catch (error) {
27
+ stateName = 'disconnected';
28
+ }
29
+
21
30
  return {
22
- readyState: 1, // Assume connected if Prisma instance exists
23
- stateName: 'connected',
24
- isConnected: true,
31
+ readyState: isConnected ? 1 : 0,
32
+ stateName,
33
+ isConnected,
25
34
  };
26
35
  }
27
36
 
37
+ /**
38
+ * @param {number} maxTimeMS
39
+ * @returns {Promise<number>} Response time in milliseconds
40
+ */
28
41
  async pingDatabase(maxTimeMS = 2000) {
29
42
  const pingStart = Date.now();
30
-
31
- // PostgreSQL ping using SELECT 1
32
- await prisma.$queryRaw`SELECT 1`;
33
-
43
+ await this.prisma.$queryRaw`SELECT 1`;
34
44
  return Date.now() - pingStart;
35
45
  }
36
46
 
37
47
  async createCredential(credentialData) {
38
- return await prisma.credential.create({
48
+ return await this.prisma.credential.create({
39
49
  data: credentialData,
40
50
  });
41
51
  }
42
52
 
43
53
  async findCredentialById(id) {
44
- return await prisma.credential.findUnique({
54
+ return await this.prisma.credential.findUnique({
45
55
  where: { id },
46
56
  });
47
57
  }
48
58
 
49
59
  /**
50
- * Get raw credential from PostgreSQL bypassing Prisma encryption extension
51
- * Uses $queryRaw to access raw PostgreSQL table
52
- * @param {string} id - Credential ID
53
- * @returns {Promise<Object|null>} Raw credential from database
60
+ * @param {string} id
61
+ * @returns {Promise<Object|null>}
54
62
  */
55
63
  async getRawCredentialById(id) {
56
- const results = await prisma.$queryRaw`
64
+ const results = await this.prisma.$queryRaw`
57
65
  SELECT * FROM "Credential" WHERE id = ${id}
58
66
  `;
59
67
 
@@ -61,12 +69,11 @@ class HealthCheckRepositoryPostgreSQL extends HealthCheckRepositoryInterface {
61
69
  return null;
62
70
  }
63
71
 
64
- // Return first result
65
72
  return results[0];
66
73
  }
67
74
 
68
75
  async deleteCredential(id) {
69
- await prisma.credential.delete({
76
+ await this.prisma.credential.delete({
70
77
  where: { id },
71
78
  });
72
79
  }
@@ -1,22 +1,17 @@
1
- /**
2
- * Use Case for checking database health.
3
- * Contains business logic for determining database connectivity and health status.
4
- */
5
1
  class CheckDatabaseHealthUseCase {
6
2
  /**
7
3
  * @param {Object} params
8
- * @param {import('../health-check-repository-interface').HealthCheckRepositoryInterface} params.healthCheckRepository
4
+ * @param {import('../repositories/health-check-repository-interface').HealthCheckRepositoryInterface} params.healthCheckRepository
9
5
  */
10
6
  constructor({ healthCheckRepository }) {
11
7
  this.repository = healthCheckRepository;
12
8
  }
13
9
 
14
10
  /**
15
- * Execute database health check
16
- * @returns {Promise<Object>} Health check result with status, state, and response time
11
+ * @returns {Promise<{status: string, state: string, responseTime?: number}>}
17
12
  */
18
13
  async execute() {
19
- const { stateName, isConnected } = this.repository.getDatabaseConnectionState();
14
+ const { stateName, isConnected } = await this.repository.getDatabaseConnectionState();
20
15
 
21
16
  const result = {
22
17
  status: isConnected ? 'healthy' : 'unhealthy',
@@ -14,6 +14,7 @@ const {
14
14
  const {
15
15
  createHealthCheckRepository,
16
16
  } = require('../../database/repositories/health-check-repository-factory');
17
+ const { prisma } = require('../../database/prisma');
17
18
  const {
18
19
  TestEncryptionUseCase,
19
20
  } = require('../../database/use-cases/test-encryption-use-case');
@@ -31,7 +32,7 @@ const {
31
32
  } = require('../use-cases/check-integrations-health-use-case');
32
33
 
33
34
  const router = Router();
34
- const healthCheckRepository = createHealthCheckRepository();
35
+ const healthCheckRepository = createHealthCheckRepository({ prismaClient: prisma });
35
36
 
36
37
  // Load integrations and create factories just like auth router does
37
38
  // This verifies the system can properly load integrations
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@friggframework/core",
3
3
  "prettier": "@friggframework/prettier-config",
4
- "version": "2.0.0-next.49",
4
+ "version": "2.0.0-next.50",
5
5
  "dependencies": {
6
6
  "@aws-sdk/client-apigatewaymanagementapi": "^3.588.0",
7
7
  "@aws-sdk/client-kms": "^3.588.0",
@@ -38,9 +38,9 @@
38
38
  }
39
39
  },
40
40
  "devDependencies": {
41
- "@friggframework/eslint-config": "2.0.0-next.49",
42
- "@friggframework/prettier-config": "2.0.0-next.49",
43
- "@friggframework/test": "2.0.0-next.49",
41
+ "@friggframework/eslint-config": "2.0.0-next.50",
42
+ "@friggframework/prettier-config": "2.0.0-next.50",
43
+ "@friggframework/test": "2.0.0-next.50",
44
44
  "@prisma/client": "^6.17.0",
45
45
  "@types/lodash": "4.17.15",
46
46
  "@typescript-eslint/eslint-plugin": "^8.0.0",
@@ -80,5 +80,5 @@
80
80
  "publishConfig": {
81
81
  "access": "public"
82
82
  },
83
- "gitHead": "ab5b2d2e5f0a24bc4af6b3dc0ac592c358638bfa"
83
+ "gitHead": "8a6cfd1c1b42d5c9ec77cb6a2d30a8789fe686a5"
84
84
  }