@friggframework/core 2.0.0-next.41 → 2.0.0-next.42
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/CLAUDE.md +693 -0
- package/README.md +931 -50
- package/application/commands/README.md +421 -0
- package/application/commands/credential-commands.js +224 -0
- package/application/commands/entity-commands.js +315 -0
- package/application/commands/integration-commands.js +160 -0
- package/application/commands/integration-commands.test.js +123 -0
- package/application/commands/user-commands.js +213 -0
- package/application/index.js +69 -0
- package/core/CLAUDE.md +690 -0
- package/core/create-handler.js +0 -6
- package/credential/repositories/credential-repository-factory.js +47 -0
- package/credential/repositories/credential-repository-interface.js +98 -0
- package/credential/repositories/credential-repository-mongo.js +301 -0
- package/credential/repositories/credential-repository-postgres.js +307 -0
- package/credential/repositories/credential-repository.js +307 -0
- package/credential/use-cases/get-credential-for-user.js +21 -0
- package/credential/use-cases/update-authentication-status.js +15 -0
- package/database/config.js +117 -0
- package/database/encryption/README.md +683 -0
- package/database/encryption/encryption-integration.test.js +553 -0
- package/database/encryption/encryption-schema-registry.js +141 -0
- package/database/encryption/encryption-schema-registry.test.js +392 -0
- package/database/encryption/field-encryption-service.js +226 -0
- package/database/encryption/field-encryption-service.test.js +525 -0
- package/database/encryption/logger.js +79 -0
- package/database/encryption/mongo-decryption-fix-verification.test.js +348 -0
- package/database/encryption/postgres-decryption-fix-verification.test.js +371 -0
- package/database/encryption/postgres-relation-decryption.test.js +245 -0
- package/database/encryption/prisma-encryption-extension.js +222 -0
- package/database/encryption/prisma-encryption-extension.test.js +439 -0
- package/database/index.js +25 -12
- package/database/models/readme.md +1 -0
- package/database/prisma.js +162 -0
- package/database/repositories/health-check-repository-factory.js +38 -0
- package/database/repositories/health-check-repository-interface.js +86 -0
- package/database/repositories/health-check-repository-mongodb.js +72 -0
- package/database/repositories/health-check-repository-postgres.js +75 -0
- package/database/repositories/health-check-repository.js +108 -0
- package/database/use-cases/check-database-health-use-case.js +34 -0
- package/database/use-cases/check-encryption-health-use-case.js +82 -0
- package/database/use-cases/test-encryption-use-case.js +252 -0
- package/encrypt/Cryptor.js +20 -152
- package/encrypt/index.js +1 -2
- package/encrypt/test-encrypt.js +0 -2
- package/handlers/app-definition-loader.js +38 -0
- package/handlers/app-handler-helpers.js +0 -3
- package/handlers/auth-flow.integration.test.js +147 -0
- package/handlers/backend-utils.js +25 -45
- package/handlers/integration-event-dispatcher.js +54 -0
- package/handlers/integration-event-dispatcher.test.js +141 -0
- package/handlers/routers/HEALTHCHECK.md +103 -1
- package/handlers/routers/auth.js +3 -14
- package/handlers/routers/health.js +63 -424
- package/handlers/routers/health.test.js +7 -0
- package/handlers/routers/integration-defined-routers.js +8 -5
- package/handlers/routers/user.js +25 -5
- package/handlers/routers/websocket.js +5 -3
- package/handlers/use-cases/check-external-apis-health-use-case.js +81 -0
- package/handlers/use-cases/check-integrations-health-use-case.js +32 -0
- package/handlers/workers/integration-defined-workers.js +6 -3
- package/index.js +45 -22
- package/integrations/index.js +12 -10
- package/integrations/integration-base.js +224 -53
- package/integrations/integration-router.js +386 -178
- package/integrations/options.js +1 -1
- package/integrations/repositories/integration-mapping-repository-factory.js +50 -0
- package/integrations/repositories/integration-mapping-repository-interface.js +106 -0
- package/integrations/repositories/integration-mapping-repository-mongo.js +161 -0
- package/integrations/repositories/integration-mapping-repository-postgres.js +227 -0
- package/integrations/repositories/integration-mapping-repository.js +156 -0
- package/integrations/repositories/integration-repository-factory.js +44 -0
- package/integrations/repositories/integration-repository-interface.js +115 -0
- package/integrations/repositories/integration-repository-mongo.js +271 -0
- package/integrations/repositories/integration-repository-postgres.js +319 -0
- package/integrations/tests/doubles/dummy-integration-class.js +90 -0
- package/integrations/tests/doubles/test-integration-repository.js +99 -0
- package/integrations/tests/use-cases/create-integration.test.js +131 -0
- package/integrations/tests/use-cases/delete-integration-for-user.test.js +150 -0
- package/integrations/tests/use-cases/find-integration-context-by-external-entity-id.test.js +92 -0
- package/integrations/tests/use-cases/get-integration-for-user.test.js +150 -0
- package/integrations/tests/use-cases/get-integration-instance.test.js +176 -0
- package/integrations/tests/use-cases/get-integrations-for-user.test.js +176 -0
- package/integrations/tests/use-cases/get-possible-integrations.test.js +188 -0
- package/integrations/tests/use-cases/update-integration-messages.test.js +142 -0
- package/integrations/tests/use-cases/update-integration-status.test.js +103 -0
- package/integrations/tests/use-cases/update-integration.test.js +141 -0
- package/integrations/use-cases/create-integration.js +83 -0
- package/integrations/use-cases/delete-integration-for-user.js +73 -0
- package/integrations/use-cases/find-integration-context-by-external-entity-id.js +72 -0
- package/integrations/use-cases/get-integration-for-user.js +78 -0
- package/integrations/use-cases/get-integration-instance-by-definition.js +67 -0
- package/integrations/use-cases/get-integration-instance.js +83 -0
- package/integrations/use-cases/get-integrations-for-user.js +87 -0
- package/integrations/use-cases/get-possible-integrations.js +27 -0
- package/integrations/use-cases/index.js +11 -0
- package/integrations/use-cases/load-integration-context-full.test.js +329 -0
- package/integrations/use-cases/load-integration-context.js +71 -0
- package/integrations/use-cases/load-integration-context.test.js +114 -0
- package/integrations/use-cases/update-integration-messages.js +44 -0
- package/integrations/use-cases/update-integration-status.js +32 -0
- package/integrations/use-cases/update-integration.js +93 -0
- package/integrations/utils/map-integration-dto.js +36 -0
- package/jest-global-setup-noop.js +3 -0
- package/jest-global-teardown-noop.js +3 -0
- package/{module-plugin → modules}/entity.js +1 -0
- package/{module-plugin → modules}/index.js +0 -8
- package/modules/module-factory.js +56 -0
- package/modules/module-hydration.test.js +205 -0
- package/modules/module.js +221 -0
- package/modules/repositories/module-repository-factory.js +33 -0
- package/modules/repositories/module-repository-interface.js +129 -0
- package/modules/repositories/module-repository-mongo.js +386 -0
- package/modules/repositories/module-repository-postgres.js +437 -0
- package/modules/repositories/module-repository.js +327 -0
- package/{module-plugin → modules}/test/mock-api/api.js +8 -3
- package/{module-plugin → modules}/test/mock-api/definition.js +12 -8
- package/modules/tests/doubles/test-module-factory.js +16 -0
- package/modules/tests/doubles/test-module-repository.js +39 -0
- package/modules/use-cases/get-entities-for-user.js +32 -0
- package/modules/use-cases/get-entity-options-by-id.js +59 -0
- package/modules/use-cases/get-entity-options-by-type.js +34 -0
- package/modules/use-cases/get-module-instance-from-type.js +31 -0
- package/modules/use-cases/get-module.js +56 -0
- package/modules/use-cases/process-authorization-callback.js +121 -0
- package/modules/use-cases/refresh-entity-options.js +59 -0
- package/modules/use-cases/test-module-auth.js +55 -0
- package/modules/utils/map-module-dto.js +18 -0
- package/package.json +14 -6
- package/prisma-mongodb/schema.prisma +321 -0
- package/prisma-postgresql/migrations/20250930193005_init/migration.sql +315 -0
- package/prisma-postgresql/migrations/20251006135218_init/migration.sql +9 -0
- package/prisma-postgresql/migrations/migration_lock.toml +3 -0
- package/prisma-postgresql/schema.prisma +303 -0
- package/syncs/manager.js +468 -443
- package/syncs/repositories/sync-repository-factory.js +38 -0
- package/syncs/repositories/sync-repository-interface.js +109 -0
- package/syncs/repositories/sync-repository-mongo.js +239 -0
- package/syncs/repositories/sync-repository-postgres.js +319 -0
- package/syncs/sync.js +0 -1
- package/token/repositories/token-repository-factory.js +33 -0
- package/token/repositories/token-repository-interface.js +131 -0
- package/token/repositories/token-repository-mongo.js +212 -0
- package/token/repositories/token-repository-postgres.js +257 -0
- package/token/repositories/token-repository.js +219 -0
- package/types/integrations/index.d.ts +2 -6
- package/types/module-plugin/index.d.ts +5 -57
- package/types/syncs/index.d.ts +0 -2
- package/user/repositories/user-repository-factory.js +46 -0
- package/user/repositories/user-repository-interface.js +198 -0
- package/user/repositories/user-repository-mongo.js +250 -0
- package/user/repositories/user-repository-postgres.js +311 -0
- package/user/tests/doubles/test-user-repository.js +72 -0
- package/user/tests/use-cases/create-individual-user.test.js +24 -0
- package/user/tests/use-cases/create-organization-user.test.js +28 -0
- package/user/tests/use-cases/create-token-for-user-id.test.js +19 -0
- package/user/tests/use-cases/get-user-from-bearer-token.test.js +64 -0
- package/user/tests/use-cases/login-user.test.js +140 -0
- package/user/use-cases/create-individual-user.js +61 -0
- package/user/use-cases/create-organization-user.js +47 -0
- package/user/use-cases/create-token-for-user-id.js +30 -0
- package/user/use-cases/get-user-from-bearer-token.js +77 -0
- package/user/use-cases/login-user.js +122 -0
- package/user/user.js +77 -0
- package/websocket/repositories/websocket-connection-repository-factory.js +37 -0
- package/websocket/repositories/websocket-connection-repository-interface.js +106 -0
- package/websocket/repositories/websocket-connection-repository-mongo.js +155 -0
- package/websocket/repositories/websocket-connection-repository-postgres.js +195 -0
- package/websocket/repositories/websocket-connection-repository.js +160 -0
- package/database/models/State.js +0 -9
- package/database/models/Token.js +0 -70
- package/database/mongo.js +0 -171
- package/encrypt/Cryptor.test.js +0 -32
- package/encrypt/encrypt.js +0 -104
- package/encrypt/encrypt.test.js +0 -1069
- package/handlers/routers/middleware/loadUser.js +0 -15
- package/handlers/routers/middleware/requireLoggedInUser.js +0 -12
- package/integrations/create-frigg-backend.js +0 -31
- package/integrations/integration-factory.js +0 -251
- package/integrations/integration-mapping.js +0 -43
- package/integrations/integration-model.js +0 -46
- package/integrations/integration-user.js +0 -144
- package/integrations/test/integration-base.test.js +0 -144
- package/module-plugin/auther.js +0 -393
- package/module-plugin/credential.js +0 -22
- package/module-plugin/entity-manager.js +0 -70
- package/module-plugin/manager.js +0 -169
- package/module-plugin/module-factory.js +0 -61
- package/module-plugin/test/auther.test.js +0 -97
- /package/{module-plugin → modules}/ModuleConstants.js +0 -0
- /package/{module-plugin → modules}/requester/api-key.js +0 -0
- /package/{module-plugin → modules}/requester/basic.js +0 -0
- /package/{module-plugin → modules}/requester/oauth-2.js +0 -0
- /package/{module-plugin → modules}/requester/requester.js +0 -0
- /package/{module-plugin → modules}/requester/requester.test.js +0 -0
- /package/{module-plugin → modules}/test/mock-api/mocks/hubspot.js +0 -0
package/core/create-handler.js
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
// REMOVING FOR NOW UNTIL WE ADD WEBPACK BACK IN
|
|
3
3
|
// require('source-map-support').install();
|
|
4
4
|
|
|
5
|
-
const { connectToDatabase } = require('../database/mongo');
|
|
6
5
|
const { initDebugLog, flushDebugLog } = require('../logs');
|
|
7
6
|
const { secretsToEnv } = require('./secrets-to-env');
|
|
8
7
|
|
|
@@ -11,7 +10,6 @@ const createHandler = (optionByName = {}) => {
|
|
|
11
10
|
eventName = 'Event',
|
|
12
11
|
isUserFacingResponse = true,
|
|
13
12
|
method,
|
|
14
|
-
shouldUseDatabase = true,
|
|
15
13
|
} = optionByName;
|
|
16
14
|
|
|
17
15
|
if (!method) {
|
|
@@ -34,10 +32,6 @@ const createHandler = (optionByName = {}) => {
|
|
|
34
32
|
// Helps mongoose reuse the connection. Lowers response times.
|
|
35
33
|
context.callbackWaitsForEmptyEventLoop = false;
|
|
36
34
|
|
|
37
|
-
if (shouldUseDatabase) {
|
|
38
|
-
await connectToDatabase();
|
|
39
|
-
}
|
|
40
|
-
|
|
41
35
|
// Run the Lambda
|
|
42
36
|
return await method(event, context);
|
|
43
37
|
} catch (error) {
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
const { CredentialRepositoryMongo } = require('./credential-repository-mongo');
|
|
2
|
+
const {
|
|
3
|
+
CredentialRepositoryPostgres,
|
|
4
|
+
} = require('./credential-repository-postgres');
|
|
5
|
+
const config = require('../../database/config');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Credential Repository Factory
|
|
9
|
+
* Creates the appropriate repository adapter based on database type
|
|
10
|
+
*
|
|
11
|
+
* Database-specific implementations:
|
|
12
|
+
* - MongoDB: Uses String IDs (ObjectId), no conversion needed
|
|
13
|
+
* - PostgreSQL: Uses Int IDs, converts String ↔ Int
|
|
14
|
+
*
|
|
15
|
+
* All repository methods return String IDs regardless of database type,
|
|
16
|
+
* ensuring application layer consistency.
|
|
17
|
+
*
|
|
18
|
+
* Usage:
|
|
19
|
+
* ```javascript
|
|
20
|
+
* const repository = createCredentialRepository();
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* @returns {CredentialRepositoryInterface} Configured repository adapter
|
|
24
|
+
*/
|
|
25
|
+
function createCredentialRepository() {
|
|
26
|
+
const dbType = config.DB_TYPE;
|
|
27
|
+
|
|
28
|
+
switch (dbType) {
|
|
29
|
+
case 'mongodb':
|
|
30
|
+
return new CredentialRepositoryMongo();
|
|
31
|
+
|
|
32
|
+
case 'postgresql':
|
|
33
|
+
return new CredentialRepositoryPostgres();
|
|
34
|
+
|
|
35
|
+
default:
|
|
36
|
+
throw new Error(
|
|
37
|
+
`Unsupported database type: ${dbType}. Supported values: 'mongodb', 'postgresql'`
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
module.exports = {
|
|
43
|
+
createCredentialRepository,
|
|
44
|
+
// Export adapters for direct testing
|
|
45
|
+
CredentialRepositoryMongo,
|
|
46
|
+
CredentialRepositoryPostgres,
|
|
47
|
+
};
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Credential Repository Interface
|
|
3
|
+
* Abstract base class defining the contract for credential persistence adapters
|
|
4
|
+
*
|
|
5
|
+
* This follows the Port in Hexagonal Architecture:
|
|
6
|
+
* - Domain layer depends on this abstraction
|
|
7
|
+
* - Concrete adapters implement this interface
|
|
8
|
+
* - Use cases receive repositories via dependency injection
|
|
9
|
+
*
|
|
10
|
+
* Note: Currently, Credential model has identical structure across MongoDB and PostgreSQL,
|
|
11
|
+
* so CredentialRepository serves both. This interface exists for consistency and
|
|
12
|
+
* future-proofing if database-specific implementations become needed.
|
|
13
|
+
*
|
|
14
|
+
* @abstract
|
|
15
|
+
*/
|
|
16
|
+
class CredentialRepositoryInterface {
|
|
17
|
+
/**
|
|
18
|
+
* Find credential by ID
|
|
19
|
+
*
|
|
20
|
+
* @param {string|number} id - Credential ID
|
|
21
|
+
* @returns {Promise<Object|null>} Credential object or null
|
|
22
|
+
* @abstract
|
|
23
|
+
*/
|
|
24
|
+
async findCredentialById(id) {
|
|
25
|
+
throw new Error(
|
|
26
|
+
'Method findCredentialById must be implemented by subclass'
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Update authentication status
|
|
32
|
+
*
|
|
33
|
+
* @param {string|number} credentialId - Credential ID
|
|
34
|
+
* @param {boolean} authIsValid - Authentication validity status
|
|
35
|
+
* @returns {Promise<Object>} Update result
|
|
36
|
+
* @abstract
|
|
37
|
+
*/
|
|
38
|
+
async updateAuthenticationStatus(credentialId, authIsValid) {
|
|
39
|
+
throw new Error(
|
|
40
|
+
'Method updateAuthenticationStatus must be implemented by subclass'
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Permanently remove a credential document
|
|
46
|
+
*
|
|
47
|
+
* @param {string|number} credentialId - Credential ID
|
|
48
|
+
* @returns {Promise<Object>} Deletion result
|
|
49
|
+
* @abstract
|
|
50
|
+
*/
|
|
51
|
+
async deleteCredentialById(credentialId) {
|
|
52
|
+
throw new Error(
|
|
53
|
+
'Method deleteCredentialById must be implemented by subclass'
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Create or update credential matching identifiers
|
|
59
|
+
*
|
|
60
|
+
* @param {{identifiers: Object, details: Object}} credentialDetails
|
|
61
|
+
* @returns {Promise<Object>} The persisted credential
|
|
62
|
+
* @abstract
|
|
63
|
+
*/
|
|
64
|
+
async upsertCredential(credentialDetails) {
|
|
65
|
+
throw new Error(
|
|
66
|
+
'Method upsertCredential must be implemented by subclass'
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Find a credential by filter criteria
|
|
72
|
+
*
|
|
73
|
+
* @param {Object} filter - Filter criteria
|
|
74
|
+
* @returns {Promise<Object|null>} Credential object or null if not found
|
|
75
|
+
* @abstract
|
|
76
|
+
*/
|
|
77
|
+
async findCredential(filter) {
|
|
78
|
+
throw new Error(
|
|
79
|
+
'Method findCredential must be implemented by subclass'
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Update a credential by ID
|
|
85
|
+
*
|
|
86
|
+
* @param {string|number} credentialId - Credential ID
|
|
87
|
+
* @param {Object} updates - Fields to update
|
|
88
|
+
* @returns {Promise<Object|null>} Updated credential object or null if not found
|
|
89
|
+
* @abstract
|
|
90
|
+
*/
|
|
91
|
+
async updateCredential(credentialId, updates) {
|
|
92
|
+
throw new Error(
|
|
93
|
+
'Method updateCredential must be implemented by subclass'
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
module.exports = { CredentialRepositoryInterface };
|
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
const { prisma } = require('../../database/prisma');
|
|
2
|
+
const {
|
|
3
|
+
CredentialRepositoryInterface,
|
|
4
|
+
} = require('./credential-repository-interface');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* MongoDB Credential Repository Adapter
|
|
8
|
+
* Handles OAuth credentials and API tokens persistence with MongoDB
|
|
9
|
+
*
|
|
10
|
+
* MongoDB-specific characteristics:
|
|
11
|
+
* - Uses String IDs (ObjectId)
|
|
12
|
+
* - No ID conversion needed (IDs are already strings)
|
|
13
|
+
* - Dynamic schema support via JSON field
|
|
14
|
+
*/
|
|
15
|
+
class CredentialRepositoryMongo extends CredentialRepositoryInterface {
|
|
16
|
+
constructor() {
|
|
17
|
+
super();
|
|
18
|
+
this.prisma = prisma;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Find credential by ID
|
|
23
|
+
* Replaces: Credential.findById(id)
|
|
24
|
+
*
|
|
25
|
+
* @param {string} id - Credential ID
|
|
26
|
+
* @returns {Promise<Object|null>} Credential object or null
|
|
27
|
+
*/
|
|
28
|
+
async findCredentialById(id) {
|
|
29
|
+
const credential = await this.prisma.credential.findUnique({
|
|
30
|
+
where: { id },
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
if (!credential) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Extract data from JSON field
|
|
38
|
+
const data = credential.data || {};
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
_id: credential.id,
|
|
42
|
+
id: credential.id,
|
|
43
|
+
user: credential.userId,
|
|
44
|
+
userId: credential.userId,
|
|
45
|
+
externalId: credential.externalId,
|
|
46
|
+
authIsValid: credential.authIsValid,
|
|
47
|
+
subType: credential.subType,
|
|
48
|
+
...data, // Spread OAuth tokens from JSON field
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Update authentication status
|
|
54
|
+
* Replaces: Credential.updateOne({ _id: credentialId }, { $set: { authIsValid } })
|
|
55
|
+
*
|
|
56
|
+
* @param {string} credentialId - Credential ID
|
|
57
|
+
* @param {boolean} authIsValid - Authentication validity status
|
|
58
|
+
* @returns {Promise<Object>} Update result
|
|
59
|
+
*/
|
|
60
|
+
async updateAuthenticationStatus(credentialId, authIsValid) {
|
|
61
|
+
await this.prisma.credential.update({
|
|
62
|
+
where: { id: credentialId },
|
|
63
|
+
data: { authIsValid },
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
return { acknowledged: true, modifiedCount: 1 };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Permanently remove a credential document
|
|
71
|
+
* Replaces: Credential.deleteOne({ _id: credentialId })
|
|
72
|
+
*
|
|
73
|
+
* @param {string} credentialId - Credential ID
|
|
74
|
+
* @returns {Promise<Object>} Deletion result
|
|
75
|
+
*/
|
|
76
|
+
async deleteCredentialById(credentialId) {
|
|
77
|
+
try {
|
|
78
|
+
await this.prisma.credential.delete({
|
|
79
|
+
where: { id: credentialId },
|
|
80
|
+
});
|
|
81
|
+
return { acknowledged: true, deletedCount: 1 };
|
|
82
|
+
} catch (error) {
|
|
83
|
+
if (error.code === 'P2025') {
|
|
84
|
+
// Record not found
|
|
85
|
+
return { acknowledged: true, deletedCount: 0 };
|
|
86
|
+
}
|
|
87
|
+
throw error;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Create or update credential matching identifiers
|
|
93
|
+
* Replaces: Credential.findOneAndUpdate(query, update, { upsert: true })
|
|
94
|
+
*
|
|
95
|
+
* @param {{identifiers: Object, details: Object}} credentialDetails
|
|
96
|
+
* @returns {Promise<Object>} The persisted credential
|
|
97
|
+
*/
|
|
98
|
+
async upsertCredential(credentialDetails) {
|
|
99
|
+
const { identifiers, details } = credentialDetails;
|
|
100
|
+
if (!identifiers)
|
|
101
|
+
throw new Error('identifiers required to upsert credential');
|
|
102
|
+
|
|
103
|
+
// Build where clause from identifiers
|
|
104
|
+
const where = this._convertIdentifiersToWhere(identifiers);
|
|
105
|
+
|
|
106
|
+
// Separate schema fields from dynamic OAuth data
|
|
107
|
+
const {
|
|
108
|
+
user,
|
|
109
|
+
userId,
|
|
110
|
+
externalId,
|
|
111
|
+
authIsValid,
|
|
112
|
+
subType,
|
|
113
|
+
...oauthData
|
|
114
|
+
} = details;
|
|
115
|
+
|
|
116
|
+
// Find existing credential
|
|
117
|
+
const existing = await this.prisma.credential.findFirst({ where });
|
|
118
|
+
|
|
119
|
+
if (existing) {
|
|
120
|
+
// Update existing - merge OAuth data into existing data JSON
|
|
121
|
+
const mergedData = { ...(existing.data || {}), ...oauthData };
|
|
122
|
+
|
|
123
|
+
const updated = await this.prisma.credential.update({
|
|
124
|
+
where: { id: existing.id },
|
|
125
|
+
data: {
|
|
126
|
+
userId: userId || user || existing.userId,
|
|
127
|
+
externalId:
|
|
128
|
+
externalId !== undefined
|
|
129
|
+
? externalId
|
|
130
|
+
: existing.externalId,
|
|
131
|
+
authIsValid:
|
|
132
|
+
authIsValid !== undefined
|
|
133
|
+
? authIsValid
|
|
134
|
+
: existing.authIsValid,
|
|
135
|
+
subType: subType !== undefined ? subType : existing.subType,
|
|
136
|
+
data: mergedData,
|
|
137
|
+
},
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
return {
|
|
141
|
+
id: updated.id,
|
|
142
|
+
externalId: updated.externalId,
|
|
143
|
+
userId: updated.userId,
|
|
144
|
+
authIsValid: updated.authIsValid,
|
|
145
|
+
...(updated.data || {}),
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Create new credential
|
|
150
|
+
const created = await this.prisma.credential.create({
|
|
151
|
+
data: {
|
|
152
|
+
userId: userId || user,
|
|
153
|
+
externalId,
|
|
154
|
+
authIsValid: authIsValid,
|
|
155
|
+
subType,
|
|
156
|
+
data: oauthData,
|
|
157
|
+
},
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
return {
|
|
161
|
+
id: created.id,
|
|
162
|
+
externalId: created.externalId,
|
|
163
|
+
userId: created.userId,
|
|
164
|
+
authIsValid: created.authIsValid,
|
|
165
|
+
...(created.data || {}),
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Find a credential by filter criteria
|
|
171
|
+
* Replaces: Credential.findOne(query)
|
|
172
|
+
*
|
|
173
|
+
* @param {Object} filter
|
|
174
|
+
* @param {string} [filter.userId] - User ID
|
|
175
|
+
* @param {string} [filter.externalId] - External ID
|
|
176
|
+
* @param {string} [filter.credentialId] - Credential ID
|
|
177
|
+
* @returns {Promise<Object|null>} Credential object or null if not found
|
|
178
|
+
*/
|
|
179
|
+
async findCredential(filter) {
|
|
180
|
+
const where = this._convertFilterToWhere(filter);
|
|
181
|
+
|
|
182
|
+
const credential = await this.prisma.credential.findFirst({
|
|
183
|
+
where,
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
if (!credential) {
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const data = credential.data || {};
|
|
191
|
+
|
|
192
|
+
return {
|
|
193
|
+
id: credential.id,
|
|
194
|
+
userId: credential.userId,
|
|
195
|
+
externalId: credential.externalId,
|
|
196
|
+
authIsValid: credential.authIsValid,
|
|
197
|
+
access_token: data.access_token,
|
|
198
|
+
refresh_token: data.refresh_token,
|
|
199
|
+
domain: data.domain,
|
|
200
|
+
...data,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Update a credential by ID
|
|
206
|
+
* Replaces: Credential.findByIdAndUpdate(credentialId, { $set: updates })
|
|
207
|
+
*
|
|
208
|
+
* @param {string} credentialId - Credential ID
|
|
209
|
+
* @param {Object} updates - Fields to update
|
|
210
|
+
* @returns {Promise<Object|null>} Updated credential object or null if not found
|
|
211
|
+
*/
|
|
212
|
+
async updateCredential(credentialId, updates) {
|
|
213
|
+
// Get existing credential to merge OAuth data
|
|
214
|
+
const existing = await this.prisma.credential.findUnique({
|
|
215
|
+
where: { id: credentialId },
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
if (!existing) {
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Separate schema fields from OAuth data
|
|
223
|
+
const {
|
|
224
|
+
user,
|
|
225
|
+
userId,
|
|
226
|
+
externalId,
|
|
227
|
+
authIsValid,
|
|
228
|
+
subType,
|
|
229
|
+
...oauthData
|
|
230
|
+
} = updates;
|
|
231
|
+
|
|
232
|
+
// Merge OAuth data with existing
|
|
233
|
+
const mergedData = { ...(existing.data || {}), ...oauthData };
|
|
234
|
+
|
|
235
|
+
const updated = await this.prisma.credential.update({
|
|
236
|
+
where: { id: credentialId },
|
|
237
|
+
data: {
|
|
238
|
+
userId: userId || user || existing.userId,
|
|
239
|
+
externalId:
|
|
240
|
+
externalId !== undefined ? externalId : existing.externalId,
|
|
241
|
+
authIsValid:
|
|
242
|
+
authIsValid !== undefined ? authIsValid : existing.authIsValid,
|
|
243
|
+
subType: subType !== undefined ? subType : existing.subType,
|
|
244
|
+
data: mergedData,
|
|
245
|
+
},
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
const data = updated.data || {};
|
|
249
|
+
|
|
250
|
+
return {
|
|
251
|
+
id: updated.id,
|
|
252
|
+
userId: updated.userId,
|
|
253
|
+
externalId: updated.externalId,
|
|
254
|
+
authIsValid: updated.authIsValid,
|
|
255
|
+
access_token: data.access_token,
|
|
256
|
+
refresh_token: data.refresh_token,
|
|
257
|
+
domain: data.domain,
|
|
258
|
+
...data,
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Convert identifiers to Prisma where clause
|
|
264
|
+
* @private
|
|
265
|
+
* @param {Object} identifiers - Identifier fields
|
|
266
|
+
* @returns {Object} Prisma where clause
|
|
267
|
+
*/
|
|
268
|
+
_convertIdentifiersToWhere(identifiers) {
|
|
269
|
+
const where = {};
|
|
270
|
+
|
|
271
|
+
if (identifiers._id) where.id = identifiers._id;
|
|
272
|
+
if (identifiers.id) where.id = identifiers.id;
|
|
273
|
+
if (identifiers.user) where.userId = identifiers.user;
|
|
274
|
+
if (identifiers.userId) where.userId = identifiers.userId;
|
|
275
|
+
if (identifiers.externalId) where.externalId = identifiers.externalId;
|
|
276
|
+
if (identifiers.subType) where.subType = identifiers.subType;
|
|
277
|
+
|
|
278
|
+
return where;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Convert filter to Prisma where clause
|
|
283
|
+
* @private
|
|
284
|
+
* @param {Object} filter - Filter criteria
|
|
285
|
+
* @returns {Object} Prisma where clause
|
|
286
|
+
*/
|
|
287
|
+
_convertFilterToWhere(filter) {
|
|
288
|
+
const where = {};
|
|
289
|
+
|
|
290
|
+
if (filter.credentialId) where.id = filter.credentialId;
|
|
291
|
+
if (filter.id) where.id = filter.id;
|
|
292
|
+
if (filter.user) where.userId = filter.user;
|
|
293
|
+
if (filter.userId) where.userId = filter.userId;
|
|
294
|
+
if (filter.externalId) where.externalId = filter.externalId;
|
|
295
|
+
if (filter.subType) where.subType = filter.subType;
|
|
296
|
+
|
|
297
|
+
return where;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
module.exports = { CredentialRepositoryMongo };
|