@friggframework/core 2.0.0-next.40 → 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
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
const { createHandler } = require('@friggframework/core');
|
|
2
|
-
const {
|
|
2
|
+
const { createWebsocketConnectionRepository } = require('../../database/websocket-connection-repository-factory');
|
|
3
|
+
|
|
4
|
+
const websocketConnectionRepository = createWebsocketConnectionRepository();
|
|
3
5
|
|
|
4
6
|
const handleWebSocketConnection = async (event, context) => {
|
|
5
7
|
// Handle different WebSocket events
|
|
@@ -8,7 +10,7 @@ const handleWebSocketConnection = async (event, context) => {
|
|
|
8
10
|
// Handle new connection
|
|
9
11
|
try {
|
|
10
12
|
const connectionId = event.requestContext.connectionId;
|
|
11
|
-
await
|
|
13
|
+
await websocketConnectionRepository.createConnection(connectionId);
|
|
12
14
|
console.log(`Stored new connection: ${connectionId}`);
|
|
13
15
|
return { statusCode: 200, body: 'Connected.' };
|
|
14
16
|
} catch (error) {
|
|
@@ -20,7 +22,7 @@ const handleWebSocketConnection = async (event, context) => {
|
|
|
20
22
|
// Handle disconnection
|
|
21
23
|
try {
|
|
22
24
|
const connectionId = event.requestContext.connectionId;
|
|
23
|
-
await
|
|
25
|
+
await websocketConnectionRepository.deleteConnection(connectionId);
|
|
24
26
|
console.log(`Removed connection: ${connectionId}`);
|
|
25
27
|
return { statusCode: 200, body: 'Disconnected.' };
|
|
26
28
|
} catch (error) {
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
const https = require('https');
|
|
2
|
+
const http = require('http');
|
|
3
|
+
|
|
4
|
+
class CheckExternalApisHealthUseCase {
|
|
5
|
+
constructor({ apis = null } = {}) {
|
|
6
|
+
this.apis = apis || [
|
|
7
|
+
{ name: 'github', url: 'https://api.github.com/status' },
|
|
8
|
+
{ name: 'npm', url: 'https://registry.npmjs.org' },
|
|
9
|
+
];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
async execute() {
|
|
13
|
+
const results = await Promise.all(
|
|
14
|
+
this.apis.map((api) =>
|
|
15
|
+
this._checkExternalAPI(api.url).then((result) => ({
|
|
16
|
+
name: api.name,
|
|
17
|
+
...result,
|
|
18
|
+
}))
|
|
19
|
+
)
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
const apiStatuses = {};
|
|
23
|
+
let allReachable = true;
|
|
24
|
+
|
|
25
|
+
results.forEach(({ name, ...checkResult }) => {
|
|
26
|
+
apiStatuses[name] = checkResult;
|
|
27
|
+
if (!checkResult.reachable) {
|
|
28
|
+
allReachable = false;
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
return { apiStatuses, allReachable };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
_checkExternalAPI(url, timeout = 5000) {
|
|
36
|
+
return new Promise((resolve) => {
|
|
37
|
+
const protocol = url.startsWith('https:') ? https : http;
|
|
38
|
+
const startTime = Date.now();
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
const request = protocol.get(url, { timeout }, (res) => {
|
|
42
|
+
const responseTime = Date.now() - startTime;
|
|
43
|
+
resolve({
|
|
44
|
+
status: 'healthy',
|
|
45
|
+
statusCode: res.statusCode,
|
|
46
|
+
responseTime,
|
|
47
|
+
reachable: res.statusCode < 500,
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
request.on('error', (error) => {
|
|
52
|
+
resolve({
|
|
53
|
+
status: 'unhealthy',
|
|
54
|
+
error: error.message,
|
|
55
|
+
responseTime: Date.now() - startTime,
|
|
56
|
+
reachable: false,
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
request.on('timeout', () => {
|
|
61
|
+
request.destroy();
|
|
62
|
+
resolve({
|
|
63
|
+
status: 'timeout',
|
|
64
|
+
error: 'Request timeout',
|
|
65
|
+
responseTime: timeout,
|
|
66
|
+
reachable: false,
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
} catch (error) {
|
|
70
|
+
resolve({
|
|
71
|
+
status: 'error',
|
|
72
|
+
error: error.message,
|
|
73
|
+
responseTime: Date.now() - startTime,
|
|
74
|
+
reachable: false,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
module.exports = { CheckExternalApisHealthUseCase };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
class CheckIntegrationsHealthUseCase {
|
|
2
|
+
constructor({ moduleFactory, integrationFactory }) {
|
|
3
|
+
this.moduleFactory = moduleFactory;
|
|
4
|
+
this.integrationFactory = integrationFactory;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
execute() {
|
|
8
|
+
const moduleTypes = Array.isArray(this.moduleFactory.moduleTypes)
|
|
9
|
+
? this.moduleFactory.moduleTypes
|
|
10
|
+
: [];
|
|
11
|
+
|
|
12
|
+
const integrationTypes = Array.isArray(
|
|
13
|
+
this.integrationFactory.integrationTypes
|
|
14
|
+
)
|
|
15
|
+
? this.integrationFactory.integrationTypes
|
|
16
|
+
: [];
|
|
17
|
+
|
|
18
|
+
return {
|
|
19
|
+
status: 'healthy',
|
|
20
|
+
modules: {
|
|
21
|
+
count: moduleTypes.length,
|
|
22
|
+
available: moduleTypes,
|
|
23
|
+
},
|
|
24
|
+
integrations: {
|
|
25
|
+
count: integrationTypes.length,
|
|
26
|
+
available: integrationTypes,
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
module.exports = { CheckIntegrationsHealthUseCase };
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
const { createHandler } = require('@friggframework/core');
|
|
2
|
-
const {
|
|
2
|
+
const { loadAppDefinition } = require('../app-definition-loader');
|
|
3
|
+
const { createQueueWorker } = require('../backend-utils');
|
|
3
4
|
|
|
4
5
|
const handlers = {};
|
|
5
|
-
|
|
6
|
-
|
|
6
|
+
const { integrations: integrationClasses } = loadAppDefinition();
|
|
7
|
+
|
|
8
|
+
integrationClasses.forEach((IntegrationClass) => {
|
|
9
|
+
const defaultQueueWorker = createQueueWorker(IntegrationClass);
|
|
7
10
|
|
|
8
11
|
handlers[`${IntegrationClass.Definition.name}`] = {
|
|
9
12
|
queueWorker: createHandler({
|
package/index.js
CHANGED
|
@@ -24,8 +24,26 @@ const {
|
|
|
24
24
|
Token,
|
|
25
25
|
UserModel,
|
|
26
26
|
WebsocketConnection,
|
|
27
|
+
prisma,
|
|
28
|
+
TokenRepository,
|
|
29
|
+
WebsocketConnectionRepository,
|
|
27
30
|
} = require('./database/index');
|
|
28
|
-
const {
|
|
31
|
+
const {
|
|
32
|
+
createUserRepository,
|
|
33
|
+
UserRepositoryMongo,
|
|
34
|
+
UserRepositoryPostgres,
|
|
35
|
+
} = require('./user/repositories/user-repository-factory');
|
|
36
|
+
|
|
37
|
+
const {
|
|
38
|
+
CredentialRepository,
|
|
39
|
+
} = require('./credential/repositories/credential-repository');
|
|
40
|
+
const {
|
|
41
|
+
ModuleRepository,
|
|
42
|
+
} = require('./modules/repositories/module-repository');
|
|
43
|
+
const {
|
|
44
|
+
IntegrationMappingRepository,
|
|
45
|
+
} = require('./integrations/repositories/integration-mapping-repository');
|
|
46
|
+
const { Cryptor } = require('./encrypt');
|
|
29
47
|
const {
|
|
30
48
|
BaseError,
|
|
31
49
|
FetchError,
|
|
@@ -35,30 +53,25 @@ const {
|
|
|
35
53
|
} = require('./errors/index');
|
|
36
54
|
const {
|
|
37
55
|
IntegrationBase,
|
|
38
|
-
IntegrationModel,
|
|
39
56
|
Options,
|
|
40
|
-
IntegrationMapping,
|
|
41
|
-
IntegrationFactory,
|
|
42
|
-
IntegrationHelper,
|
|
43
57
|
createIntegrationRouter,
|
|
44
58
|
checkRequiredParams,
|
|
45
|
-
|
|
59
|
+
getModulesDefinitionFromIntegrationClasses,
|
|
60
|
+
LoadIntegrationContextUseCase,
|
|
46
61
|
} = require('./integrations/index');
|
|
47
62
|
const { TimeoutCatcher } = require('./lambda/index');
|
|
48
63
|
const { debug, initDebugLog, flushDebugLog } = require('./logs/index');
|
|
49
64
|
const {
|
|
50
65
|
Credential,
|
|
51
|
-
EntityManager,
|
|
52
66
|
Entity,
|
|
53
|
-
ModuleManager,
|
|
54
67
|
ApiKeyRequester,
|
|
55
68
|
BasicAuthRequester,
|
|
56
69
|
OAuth2Requester,
|
|
57
70
|
Requester,
|
|
58
71
|
ModuleConstants,
|
|
59
72
|
ModuleFactory,
|
|
60
|
-
|
|
61
|
-
|
|
73
|
+
} = require('./modules/index');
|
|
74
|
+
const application = require('./application');
|
|
62
75
|
const utils = require('./utils');
|
|
63
76
|
|
|
64
77
|
// const {Sync } = require('./syncs/model');
|
|
@@ -92,9 +105,15 @@ module.exports = {
|
|
|
92
105
|
Token,
|
|
93
106
|
UserModel,
|
|
94
107
|
WebsocketConnection,
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
108
|
+
prisma,
|
|
109
|
+
TokenRepository,
|
|
110
|
+
WebsocketConnectionRepository,
|
|
111
|
+
createUserRepository,
|
|
112
|
+
UserRepositoryMongo,
|
|
113
|
+
UserRepositoryPostgres,
|
|
114
|
+
CredentialRepository,
|
|
115
|
+
ModuleRepository,
|
|
116
|
+
IntegrationMappingRepository,
|
|
98
117
|
Cryptor,
|
|
99
118
|
|
|
100
119
|
// errors
|
|
@@ -106,14 +125,22 @@ module.exports = {
|
|
|
106
125
|
|
|
107
126
|
// integrations
|
|
108
127
|
IntegrationBase,
|
|
109
|
-
IntegrationModel,
|
|
110
128
|
Options,
|
|
111
|
-
IntegrationMapping,
|
|
112
|
-
IntegrationFactory,
|
|
113
|
-
IntegrationHelper,
|
|
114
129
|
checkRequiredParams,
|
|
115
130
|
createIntegrationRouter,
|
|
116
|
-
|
|
131
|
+
getModulesDefinitionFromIntegrationClasses,
|
|
132
|
+
LoadIntegrationContextUseCase,
|
|
133
|
+
|
|
134
|
+
// application - Command factories for integration developers
|
|
135
|
+
application,
|
|
136
|
+
createFriggCommands: application.createFriggCommands,
|
|
137
|
+
createIntegrationCommands: application.createIntegrationCommands,
|
|
138
|
+
createUserCommands: application.createUserCommands,
|
|
139
|
+
createEntityCommands: application.createEntityCommands,
|
|
140
|
+
createCredentialCommands: application.createCredentialCommands,
|
|
141
|
+
findIntegrationContextByExternalEntityId:
|
|
142
|
+
application.findIntegrationContextByExternalEntityId,
|
|
143
|
+
integrationCommands: application.integrationCommands,
|
|
117
144
|
|
|
118
145
|
// lambda
|
|
119
146
|
TimeoutCatcher,
|
|
@@ -125,17 +152,13 @@ module.exports = {
|
|
|
125
152
|
|
|
126
153
|
// module plugin
|
|
127
154
|
Credential,
|
|
128
|
-
EntityManager,
|
|
129
155
|
Entity,
|
|
130
|
-
ModuleManager,
|
|
131
156
|
ApiKeyRequester,
|
|
132
157
|
BasicAuthRequester,
|
|
133
158
|
OAuth2Requester,
|
|
134
159
|
Requester,
|
|
135
160
|
ModuleConstants,
|
|
136
161
|
ModuleFactory,
|
|
137
|
-
Auther,
|
|
138
|
-
|
|
139
162
|
// queues
|
|
140
163
|
QueuerUtil,
|
|
141
164
|
|
package/integrations/index.js
CHANGED
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
const { IntegrationBase } = require('./integration-base');
|
|
2
|
-
const { IntegrationModel } = require('./integration-model');
|
|
3
2
|
const { Options } = require('./options');
|
|
4
|
-
const {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
const {
|
|
4
|
+
createIntegrationRouter,
|
|
5
|
+
checkRequiredParams,
|
|
6
|
+
} = require('./integration-router');
|
|
7
|
+
const {
|
|
8
|
+
getModulesDefinitionFromIntegrationClasses,
|
|
9
|
+
} = require('./utils/map-integration-dto');
|
|
10
|
+
const {
|
|
11
|
+
LoadIntegrationContextUseCase,
|
|
12
|
+
} = require('./use-cases/load-integration-context');
|
|
8
13
|
|
|
9
14
|
module.exports = {
|
|
10
15
|
IntegrationBase,
|
|
11
|
-
IntegrationModel,
|
|
12
16
|
Options,
|
|
13
|
-
IntegrationMapping,
|
|
14
|
-
IntegrationFactory,
|
|
15
|
-
IntegrationHelper,
|
|
16
17
|
createIntegrationRouter,
|
|
17
18
|
checkRequiredParams,
|
|
18
|
-
|
|
19
|
+
getModulesDefinitionFromIntegrationClasses,
|
|
20
|
+
LoadIntegrationContextUseCase,
|
|
19
21
|
};
|
|
@@ -1,5 +1,17 @@
|
|
|
1
|
-
const {
|
|
1
|
+
const {
|
|
2
|
+
createIntegrationMappingRepository,
|
|
3
|
+
} = require('./repositories/integration-mapping-repository-factory');
|
|
2
4
|
const { Options } = require('./options');
|
|
5
|
+
const {
|
|
6
|
+
UpdateIntegrationStatus,
|
|
7
|
+
} = require('./use-cases/update-integration-status');
|
|
8
|
+
const {
|
|
9
|
+
createIntegrationRepository,
|
|
10
|
+
} = require('./repositories/integration-repository-factory');
|
|
11
|
+
const {
|
|
12
|
+
UpdateIntegrationMessages,
|
|
13
|
+
} = require('./use-cases/update-integration-messages');
|
|
14
|
+
|
|
3
15
|
const constantsToBeMigrated = {
|
|
4
16
|
defaultEvents: {
|
|
5
17
|
ON_CREATE: 'ON_CREATE',
|
|
@@ -19,6 +31,16 @@ const constantsToBeMigrated = {
|
|
|
19
31
|
};
|
|
20
32
|
|
|
21
33
|
class IntegrationBase {
|
|
34
|
+
// todo: maybe we can pass this as Dependency Injection in the sub-class constructor
|
|
35
|
+
integrationRepository = createIntegrationRepository();
|
|
36
|
+
integrationMappingRepository = createIntegrationMappingRepository();
|
|
37
|
+
updateIntegrationStatus = new UpdateIntegrationStatus({
|
|
38
|
+
integrationRepository: this.integrationRepository,
|
|
39
|
+
});
|
|
40
|
+
updateIntegrationMessages = new UpdateIntegrationMessages({
|
|
41
|
+
integrationRepository: this.integrationRepository,
|
|
42
|
+
});
|
|
43
|
+
|
|
22
44
|
static getOptionDetails() {
|
|
23
45
|
const options = new Options({
|
|
24
46
|
module: Object.values(this.Definition.modules)[0], // This is a placeholder until we revamp the frontend
|
|
@@ -26,6 +48,7 @@ class IntegrationBase {
|
|
|
26
48
|
});
|
|
27
49
|
return options.get();
|
|
28
50
|
}
|
|
51
|
+
|
|
29
52
|
/**
|
|
30
53
|
* CHILDREN SHOULD SPECIFY A DEFINITION FOR THE INTEGRATION
|
|
31
54
|
*/
|
|
@@ -50,29 +73,30 @@ class IntegrationBase {
|
|
|
50
73
|
static getCurrentVersion() {
|
|
51
74
|
return this.Definition.version;
|
|
52
75
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
76
|
+
|
|
77
|
+
// REMOVED: registerEventHandlers() - Event handling is now done by IntegrationEventDispatcher
|
|
78
|
+
|
|
79
|
+
constructor(params = {}) {
|
|
80
|
+
this.modules = {};
|
|
81
|
+
this.events = this.events || {};
|
|
82
|
+
this.messages = { errors: [], warnings: [] };
|
|
83
|
+
this._isHydrated = false;
|
|
84
|
+
|
|
85
|
+
if (params && Object.keys(params).length > 0) {
|
|
86
|
+
this.setIntegrationRecord({
|
|
87
|
+
record: {
|
|
88
|
+
id: params.id,
|
|
89
|
+
userId: params.userId,
|
|
90
|
+
entities: params.entities,
|
|
91
|
+
config: params.config,
|
|
92
|
+
status: params.status,
|
|
93
|
+
version: params.version,
|
|
94
|
+
messages: params.messages,
|
|
95
|
+
},
|
|
96
|
+
modules: params.modules || [],
|
|
97
|
+
});
|
|
66
98
|
}
|
|
67
|
-
}
|
|
68
|
-
registerEventHandlers() {
|
|
69
|
-
this.on = {
|
|
70
|
-
...this.defaultEvents,
|
|
71
|
-
...this.events,
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
99
|
|
|
75
|
-
constructor(params) {
|
|
76
100
|
this.defaultEvents = {
|
|
77
101
|
[constantsToBeMigrated.defaultEvents.ON_CREATE]: {
|
|
78
102
|
type: constantsToBeMigrated.types.LIFE_CYCLE_EVENT,
|
|
@@ -107,21 +131,90 @@ class IntegrationBase {
|
|
|
107
131
|
handler: this.refreshActionOptions,
|
|
108
132
|
},
|
|
109
133
|
};
|
|
110
|
-
this.loadModules();
|
|
111
134
|
}
|
|
112
135
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
136
|
+
// todo: debate wether we want to keep this pattern to set the record or not.
|
|
137
|
+
/**
|
|
138
|
+
* Persist the database record and module instances onto this integration instance.
|
|
139
|
+
* Accepts either a plain object containing the persisted fields or an object with
|
|
140
|
+
* a `record` property plus a `modules` collection.
|
|
141
|
+
* @param {Object} payload
|
|
142
|
+
* @param {Object} [payload.record]
|
|
143
|
+
* @param {Array} [payload.modules]
|
|
144
|
+
*/
|
|
145
|
+
setIntegrationRecord(payload = {}) {
|
|
146
|
+
if (!payload || Object.keys(payload).length === 0) {
|
|
147
|
+
throw new Error('setIntegrationRecord requires integration data');
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const integrationRecord = payload.record;
|
|
151
|
+
const integrationModules = payload.modules ?? [];
|
|
152
|
+
|
|
153
|
+
if (!integrationRecord) {
|
|
154
|
+
throw new Error('Integration record not provided');
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const { id, userId, entities, config, status, version, messages } =
|
|
158
|
+
integrationRecord;
|
|
159
|
+
|
|
160
|
+
this.id = id;
|
|
161
|
+
this.userId = userId;
|
|
162
|
+
this.entities = entities;
|
|
163
|
+
this.config = config;
|
|
164
|
+
this.status = status;
|
|
165
|
+
this.version = version;
|
|
166
|
+
this.messages = messages || { errors: [], warnings: [] };
|
|
167
|
+
|
|
168
|
+
this.modules = this._appendModules(integrationModules);
|
|
169
|
+
|
|
170
|
+
this.record = {
|
|
171
|
+
id: this.id,
|
|
172
|
+
userId: this.userId,
|
|
173
|
+
entities: this.entities,
|
|
174
|
+
config: this.config,
|
|
175
|
+
status: this.status,
|
|
176
|
+
version: this.version,
|
|
177
|
+
messages: this.messages,
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
this._isHydrated = Boolean(this.id);
|
|
181
|
+
return this;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
get isHydrated() {
|
|
185
|
+
return this._isHydrated;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
assertHydrated(message = 'Integration instance is not hydrated') {
|
|
189
|
+
if (!this.isHydrated) {
|
|
190
|
+
throw new Error(message);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Returns the modules as object with keys as module names.
|
|
196
|
+
* @private
|
|
197
|
+
* @param {Array} integrationModules - Array of module instances
|
|
198
|
+
* @returns {Object} The modules object
|
|
199
|
+
*/
|
|
200
|
+
_appendModules(integrationModules) {
|
|
201
|
+
const modules = {};
|
|
202
|
+
for (const module of integrationModules) {
|
|
203
|
+
const key =
|
|
204
|
+
typeof module.getName === 'function'
|
|
205
|
+
? module.getName()
|
|
206
|
+
: module.name;
|
|
207
|
+
if (key) {
|
|
208
|
+
modules[key] = module;
|
|
209
|
+
this[key] = module;
|
|
210
|
+
}
|
|
118
211
|
}
|
|
119
|
-
return
|
|
212
|
+
return modules;
|
|
120
213
|
}
|
|
121
214
|
|
|
122
215
|
async validateConfig() {
|
|
123
216
|
const configOptions = await this.getConfigOptions();
|
|
124
|
-
const currentConfig = this.
|
|
217
|
+
const currentConfig = this.getConfig();
|
|
125
218
|
let needsConfig = false;
|
|
126
219
|
for (const option of configOptions) {
|
|
127
220
|
if (option.required) {
|
|
@@ -133,56 +226,62 @@ class IntegrationBase {
|
|
|
133
226
|
)
|
|
134
227
|
) {
|
|
135
228
|
needsConfig = true;
|
|
136
|
-
this.
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
229
|
+
await this.updateIntegrationMessages.execute(
|
|
230
|
+
this.id,
|
|
231
|
+
'warnings',
|
|
232
|
+
'Config Validation Error',
|
|
233
|
+
`Missing required field of ${option.label}`,
|
|
234
|
+
Date.now()
|
|
235
|
+
);
|
|
141
236
|
}
|
|
142
237
|
}
|
|
143
238
|
}
|
|
144
239
|
if (needsConfig) {
|
|
145
|
-
this.
|
|
146
|
-
await this.record.save();
|
|
240
|
+
await this.updateIntegrationStatus.execute(this.id, 'NEEDS_CONFIG');
|
|
147
241
|
}
|
|
148
242
|
}
|
|
149
243
|
|
|
150
244
|
async testAuth() {
|
|
151
245
|
let didAuthPass = true;
|
|
152
246
|
|
|
153
|
-
for (const module of Object.keys(
|
|
247
|
+
for (const module of Object.keys(this.constructor.Definition.modules)) {
|
|
154
248
|
try {
|
|
155
249
|
await this[module].testAuth();
|
|
156
250
|
} catch {
|
|
157
251
|
didAuthPass = false;
|
|
158
|
-
this.
|
|
159
|
-
|
|
160
|
-
|
|
252
|
+
await this.updateIntegrationMessages.execute(
|
|
253
|
+
this.id,
|
|
254
|
+
'errors',
|
|
255
|
+
'Authentication Error',
|
|
256
|
+
`There was an error with your ${this[
|
|
161
257
|
module
|
|
162
258
|
].constructor.getName()} Entity.
|
|
163
259
|
Please reconnect/re-authenticate, or reach out to Support for assistance.`,
|
|
164
|
-
|
|
165
|
-
|
|
260
|
+
Date.now()
|
|
261
|
+
);
|
|
166
262
|
}
|
|
167
263
|
}
|
|
168
264
|
|
|
169
265
|
if (!didAuthPass) {
|
|
170
|
-
this.
|
|
171
|
-
this.record.markModified('messages.error');
|
|
172
|
-
await this.record.save();
|
|
266
|
+
await this.updateIntegrationStatus.execute(this.id, 'ERROR');
|
|
173
267
|
}
|
|
174
268
|
}
|
|
175
269
|
|
|
176
270
|
async getMapping(sourceId) {
|
|
177
|
-
|
|
271
|
+
// todo: not sure we should call the repository directly from here
|
|
272
|
+
return this.integrationMappingRepository.findMappingBy(
|
|
273
|
+
this.id,
|
|
274
|
+
sourceId
|
|
275
|
+
);
|
|
178
276
|
}
|
|
179
277
|
|
|
180
278
|
async upsertMapping(sourceId, mapping) {
|
|
181
279
|
if (!sourceId) {
|
|
182
280
|
throw new Error(`sourceId must be set`);
|
|
183
281
|
}
|
|
184
|
-
|
|
185
|
-
|
|
282
|
+
// todo: not sure we should call the repository directly from here
|
|
283
|
+
return await this.integrationMappingRepository.upsertMapping(
|
|
284
|
+
this.id,
|
|
186
285
|
sourceId,
|
|
187
286
|
mapping
|
|
188
287
|
);
|
|
@@ -191,10 +290,8 @@ class IntegrationBase {
|
|
|
191
290
|
/**
|
|
192
291
|
* CHILDREN CAN OVERRIDE THESE CONFIGURATION METHODS
|
|
193
292
|
*/
|
|
194
|
-
async onCreate(
|
|
195
|
-
this.
|
|
196
|
-
await this.record.save();
|
|
197
|
-
return this.record;
|
|
293
|
+
async onCreate({ integrationId }) {
|
|
294
|
+
await this.updateIntegrationStatus.execute(integrationId, 'ENABLED');
|
|
198
295
|
}
|
|
199
296
|
|
|
200
297
|
async onUpdate(params) {}
|
|
@@ -259,6 +356,80 @@ class IntegrationBase {
|
|
|
259
356
|
};
|
|
260
357
|
return options;
|
|
261
358
|
}
|
|
359
|
+
|
|
360
|
+
// === Domain Methods (moved from Integration.js) ===
|
|
361
|
+
|
|
362
|
+
getConfig() {
|
|
363
|
+
return this.config;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
getModule(key) {
|
|
367
|
+
return this.modules[key];
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
setModule(key, module) {
|
|
371
|
+
this.modules[key] = module;
|
|
372
|
+
this[key] = module;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
addError(error) {
|
|
376
|
+
if (!this.messages.errors) {
|
|
377
|
+
this.messages.errors = [];
|
|
378
|
+
}
|
|
379
|
+
this.messages.errors.push(error);
|
|
380
|
+
this.status = 'ERROR';
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
addWarning(warning) {
|
|
384
|
+
if (!this.messages.warnings) {
|
|
385
|
+
this.messages.warnings = [];
|
|
386
|
+
}
|
|
387
|
+
this.messages.warnings.push(warning);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
isActive() {
|
|
391
|
+
return this.status === 'ENABLED' || this.status === 'ACTIVE';
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
needsConfiguration() {
|
|
395
|
+
return this.status === 'NEEDS_CONFIG';
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
hasErrors() {
|
|
399
|
+
return this.status === 'ERROR';
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
belongsToUser(userId) {
|
|
403
|
+
return this.userId.toString() === userId.toString();
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
async initialize() {
|
|
407
|
+
// Load dynamic user actions
|
|
408
|
+
try {
|
|
409
|
+
const additionalUserActions = await this.loadDynamicUserActions();
|
|
410
|
+
this.events = { ...this.events, ...additionalUserActions };
|
|
411
|
+
} catch (e) {
|
|
412
|
+
this.addError(e);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// Event handlers are no longer registered here - handled by IntegrationEventDispatcher
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
getOptionDetails() {
|
|
419
|
+
const options = new Options({
|
|
420
|
+
module: Object.values(this.constructor.Definition.modules)[0],
|
|
421
|
+
...this.constructor.Definition,
|
|
422
|
+
});
|
|
423
|
+
return options.get();
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// Legacy method for backward compatibility
|
|
427
|
+
async loadModules() {
|
|
428
|
+
// This method was used in the old architecture for loading modules
|
|
429
|
+
// In the new architecture, modules are injected via constructor
|
|
430
|
+
// For backward compatibility, this is a no-op
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
262
433
|
}
|
|
263
434
|
|
|
264
435
|
module.exports = { IntegrationBase };
|