@friggframework/core 2.0.0-next.8 → 2.0.0-next.80
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 +694 -0
- package/README.md +959 -50
- package/application/commands/README.md +451 -0
- package/application/commands/credential-commands.js +245 -0
- package/application/commands/entity-commands.js +336 -0
- package/application/commands/integration-commands.js +210 -0
- package/application/commands/scheduler-commands.js +263 -0
- package/application/commands/user-commands.js +283 -0
- package/application/index.js +73 -0
- package/assertions/index.js +0 -3
- package/core/CLAUDE.md +690 -0
- package/core/Worker.js +60 -24
- package/core/create-handler.js +79 -8
- package/credential/repositories/credential-repository-documentdb.js +304 -0
- package/credential/repositories/credential-repository-factory.js +54 -0
- package/credential/repositories/credential-repository-interface.js +98 -0
- package/credential/repositories/credential-repository-mongo.js +269 -0
- package/credential/repositories/credential-repository-postgres.js +287 -0
- package/credential/repositories/credential-repository.js +300 -0
- package/credential/use-cases/get-credential-for-user.js +25 -0
- package/credential/use-cases/update-authentication-status.js +15 -0
- package/database/MONGODB_TRANSACTION_FIX.md +198 -0
- package/database/adapters/lambda-invoker.js +97 -0
- package/database/config.js +154 -0
- package/database/documentdb-encryption-service.js +330 -0
- package/database/documentdb-utils.js +136 -0
- package/database/encryption/README.md +839 -0
- package/database/encryption/documentdb-encryption-service.md +3575 -0
- package/database/encryption/encryption-schema-registry.js +268 -0
- package/database/encryption/field-encryption-service.js +226 -0
- package/database/encryption/logger.js +79 -0
- package/database/encryption/prisma-encryption-extension.js +222 -0
- package/database/index.js +21 -21
- package/database/prisma.js +182 -0
- package/database/repositories/health-check-repository-documentdb.js +138 -0
- package/database/repositories/health-check-repository-factory.js +48 -0
- package/database/repositories/health-check-repository-interface.js +82 -0
- package/database/repositories/health-check-repository-mongodb.js +89 -0
- package/database/repositories/health-check-repository-postgres.js +82 -0
- package/database/repositories/migration-status-repository-s3.js +137 -0
- package/database/use-cases/check-database-health-use-case.js +29 -0
- package/database/use-cases/check-database-state-use-case.js +81 -0
- package/database/use-cases/check-encryption-health-use-case.js +83 -0
- package/database/use-cases/get-database-state-via-worker-use-case.js +61 -0
- package/database/use-cases/get-migration-status-use-case.js +93 -0
- package/database/use-cases/run-database-migration-use-case.js +139 -0
- package/database/use-cases/test-encryption-use-case.js +253 -0
- package/database/use-cases/trigger-database-migration-use-case.js +157 -0
- package/database/utils/mongodb-collection-utils.js +94 -0
- package/database/utils/mongodb-schema-init.js +108 -0
- package/database/utils/prisma-runner.js +477 -0
- package/database/utils/prisma-schema-parser.js +182 -0
- package/docs/PROCESS_MANAGEMENT_QUEUE_SPEC.md +517 -0
- package/encrypt/Cryptor.js +34 -168
- package/encrypt/index.js +1 -2
- package/errors/client-safe-error.js +26 -0
- package/errors/fetch-error.js +15 -7
- package/errors/index.js +2 -0
- package/generated/prisma-mongodb/client.d.ts +1 -0
- package/generated/prisma-mongodb/client.js +4 -0
- package/generated/prisma-mongodb/default.d.ts +1 -0
- package/generated/prisma-mongodb/default.js +4 -0
- package/generated/prisma-mongodb/edge.d.ts +1 -0
- package/generated/prisma-mongodb/edge.js +335 -0
- package/generated/prisma-mongodb/index-browser.js +317 -0
- package/generated/prisma-mongodb/index.d.ts +22955 -0
- package/generated/prisma-mongodb/index.js +360 -0
- package/generated/prisma-mongodb/package.json +183 -0
- package/generated/prisma-mongodb/query-engine-debian-openssl-3.0.x +0 -0
- package/generated/prisma-mongodb/query-engine-rhel-openssl-3.0.x +0 -0
- package/generated/prisma-mongodb/runtime/binary.d.ts +1 -0
- package/generated/prisma-mongodb/runtime/binary.js +289 -0
- package/generated/prisma-mongodb/runtime/edge-esm.js +34 -0
- package/generated/prisma-mongodb/runtime/edge.js +34 -0
- package/generated/prisma-mongodb/runtime/index-browser.d.ts +370 -0
- package/generated/prisma-mongodb/runtime/index-browser.js +16 -0
- package/generated/prisma-mongodb/runtime/library.d.ts +3977 -0
- package/generated/prisma-mongodb/runtime/react-native.js +83 -0
- package/generated/prisma-mongodb/runtime/wasm-compiler-edge.js +84 -0
- package/generated/prisma-mongodb/runtime/wasm-engine-edge.js +36 -0
- package/generated/prisma-mongodb/schema.prisma +362 -0
- package/generated/prisma-mongodb/wasm-edge-light-loader.mjs +4 -0
- package/generated/prisma-mongodb/wasm-worker-loader.mjs +4 -0
- package/generated/prisma-mongodb/wasm.d.ts +1 -0
- package/generated/prisma-mongodb/wasm.js +342 -0
- package/generated/prisma-postgresql/client.d.ts +1 -0
- package/generated/prisma-postgresql/client.js +4 -0
- package/generated/prisma-postgresql/default.d.ts +1 -0
- package/generated/prisma-postgresql/default.js +4 -0
- package/generated/prisma-postgresql/edge.d.ts +1 -0
- package/generated/prisma-postgresql/edge.js +357 -0
- package/generated/prisma-postgresql/index-browser.js +339 -0
- package/generated/prisma-postgresql/index.d.ts +25131 -0
- package/generated/prisma-postgresql/index.js +382 -0
- package/generated/prisma-postgresql/package.json +183 -0
- package/generated/prisma-postgresql/query-engine-debian-openssl-3.0.x +0 -0
- package/generated/prisma-postgresql/query-engine-rhel-openssl-3.0.x +0 -0
- package/generated/prisma-postgresql/query_engine_bg.js +2 -0
- package/generated/prisma-postgresql/query_engine_bg.wasm +0 -0
- package/generated/prisma-postgresql/runtime/binary.d.ts +1 -0
- package/generated/prisma-postgresql/runtime/binary.js +289 -0
- package/generated/prisma-postgresql/runtime/edge-esm.js +34 -0
- package/generated/prisma-postgresql/runtime/edge.js +34 -0
- package/generated/prisma-postgresql/runtime/index-browser.d.ts +370 -0
- package/generated/prisma-postgresql/runtime/index-browser.js +16 -0
- package/generated/prisma-postgresql/runtime/library.d.ts +3977 -0
- package/generated/prisma-postgresql/runtime/react-native.js +83 -0
- package/generated/prisma-postgresql/runtime/wasm-compiler-edge.js +84 -0
- package/generated/prisma-postgresql/runtime/wasm-engine-edge.js +36 -0
- package/generated/prisma-postgresql/schema.prisma +345 -0
- package/generated/prisma-postgresql/wasm-edge-light-loader.mjs +4 -0
- package/generated/prisma-postgresql/wasm-worker-loader.mjs +4 -0
- package/generated/prisma-postgresql/wasm.d.ts +1 -0
- package/generated/prisma-postgresql/wasm.js +364 -0
- package/handlers/WEBHOOKS.md +653 -0
- package/handlers/app-definition-loader.js +38 -0
- package/handlers/app-handler-helpers.js +57 -0
- package/handlers/backend-utils.js +262 -0
- package/handlers/database-migration-handler.js +227 -0
- package/handlers/integration-event-dispatcher.js +54 -0
- package/handlers/routers/HEALTHCHECK.md +342 -0
- package/handlers/routers/auth.js +15 -0
- package/handlers/routers/db-migration.handler.js +29 -0
- package/handlers/routers/db-migration.js +326 -0
- package/handlers/routers/health.js +516 -0
- package/handlers/routers/integration-defined-routers.js +45 -0
- package/handlers/routers/integration-webhook-routers.js +67 -0
- package/handlers/routers/user.js +63 -0
- package/handlers/routers/websocket.js +57 -0
- package/handlers/use-cases/check-external-apis-health-use-case.js +81 -0
- package/handlers/use-cases/check-integrations-health-use-case.js +44 -0
- package/handlers/workers/db-migration.js +352 -0
- package/handlers/workers/dlq-processor.js +63 -0
- package/handlers/workers/integration-defined-workers.js +23 -0
- package/index.js +82 -46
- package/infrastructure/scheduler/eventbridge-scheduler-adapter.js +184 -0
- package/infrastructure/scheduler/index.js +33 -0
- package/infrastructure/scheduler/mock-scheduler-adapter.js +143 -0
- package/infrastructure/scheduler/scheduler-service-factory.js +73 -0
- package/infrastructure/scheduler/scheduler-service-interface.js +47 -0
- package/integrations/WEBHOOK-QUICKSTART.md +151 -0
- package/integrations/index.js +12 -10
- package/integrations/integration-base.js +364 -55
- package/integrations/integration-router.js +375 -179
- package/integrations/options.js +1 -1
- package/integrations/repositories/integration-mapping-repository-documentdb.js +280 -0
- package/integrations/repositories/integration-mapping-repository-factory.js +57 -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-documentdb.js +219 -0
- package/integrations/repositories/integration-repository-factory.js +51 -0
- package/integrations/repositories/integration-repository-interface.js +144 -0
- package/integrations/repositories/integration-repository-mongo.js +330 -0
- package/integrations/repositories/integration-repository-postgres.js +385 -0
- package/integrations/repositories/process-repository-documentdb.js +243 -0
- package/integrations/repositories/process-repository-factory.js +53 -0
- package/integrations/repositories/process-repository-interface.js +90 -0
- package/integrations/repositories/process-repository-mongo.js +190 -0
- package/integrations/repositories/process-repository-postgres.js +217 -0
- package/integrations/tests/doubles/config-capturing-integration.js +81 -0
- package/integrations/tests/doubles/dummy-integration-class.js +105 -0
- package/integrations/tests/doubles/test-integration-repository.js +112 -0
- package/integrations/use-cases/create-integration.js +83 -0
- package/integrations/use-cases/create-process.js +128 -0
- package/integrations/use-cases/delete-integration-for-user.js +101 -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 +88 -0
- package/integrations/use-cases/get-possible-integrations.js +27 -0
- package/integrations/use-cases/get-process.js +87 -0
- package/integrations/use-cases/index.js +19 -0
- package/integrations/use-cases/load-integration-context.js +71 -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 +92 -0
- package/integrations/use-cases/update-process-metrics.js +201 -0
- package/integrations/use-cases/update-process-state.js +119 -0
- package/integrations/utils/map-integration-dto.js +37 -0
- package/jest-global-setup-noop.js +3 -0
- package/jest-global-teardown-noop.js +3 -0
- package/logs/logger.js +0 -4
- package/{module-plugin → modules}/index.js +0 -10
- package/modules/module-factory.js +56 -0
- package/modules/module.js +256 -0
- package/modules/repositories/module-repository-documentdb.js +335 -0
- package/modules/repositories/module-repository-factory.js +40 -0
- package/modules/repositories/module-repository-interface.js +129 -0
- package/modules/repositories/module-repository-mongo.js +408 -0
- package/modules/repositories/module-repository-postgres.js +453 -0
- package/modules/repositories/module-repository.js +345 -0
- package/modules/requester/api-key.js +52 -0
- package/modules/requester/oauth-2.js +396 -0
- package/{module-plugin → modules}/requester/requester.js +4 -2
- package/{module-plugin → modules}/test/mock-api/api.js +8 -3
- package/{module-plugin → modules}/test/mock-api/definition.js +14 -10
- 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 +71 -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 +74 -0
- package/modules/use-cases/process-authorization-callback.js +177 -0
- package/modules/use-cases/refresh-entity-options.js +72 -0
- package/modules/use-cases/test-module-auth.js +72 -0
- package/modules/utils/map-module-dto.js +18 -0
- package/package.json +82 -50
- package/prisma-mongodb/schema.prisma +362 -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/20251010000000_remove_unused_entity_reference_map/migration.sql +3 -0
- package/prisma-postgresql/migrations/20251112195422_update_user_unique_constraints/migration.sql +25 -0
- package/prisma-postgresql/migrations/migration_lock.toml +3 -0
- package/prisma-postgresql/schema.prisma +345 -0
- package/queues/queuer-util.js +103 -21
- package/syncs/manager.js +468 -443
- package/syncs/repositories/sync-repository-documentdb.js +240 -0
- package/syncs/repositories/sync-repository-factory.js +43 -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-documentdb.js +137 -0
- package/token/repositories/token-repository-factory.js +40 -0
- package/token/repositories/token-repository-interface.js +131 -0
- package/token/repositories/token-repository-mongo.js +219 -0
- package/token/repositories/token-repository-postgres.js +264 -0
- package/token/repositories/token-repository.js +219 -0
- package/types/associations/index.d.ts +0 -17
- package/types/core/index.d.ts +12 -4
- package/types/database/index.d.ts +10 -2
- package/types/encrypt/index.d.ts +5 -3
- package/types/integrations/index.d.ts +3 -8
- package/types/module-plugin/index.d.ts +17 -69
- package/types/syncs/index.d.ts +0 -17
- package/user/repositories/user-repository-documentdb.js +441 -0
- package/user/repositories/user-repository-factory.js +52 -0
- package/user/repositories/user-repository-interface.js +201 -0
- package/user/repositories/user-repository-mongo.js +308 -0
- package/user/repositories/user-repository-postgres.js +360 -0
- package/user/tests/doubles/test-user-repository.js +72 -0
- package/user/use-cases/authenticate-user.js +127 -0
- package/user/use-cases/authenticate-with-shared-secret.js +48 -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-adopter-jwt.js +149 -0
- package/user/use-cases/get-user-from-bearer-token.js +77 -0
- package/user/use-cases/get-user-from-x-frigg-headers.js +132 -0
- package/user/use-cases/login-user.js +122 -0
- package/user/user.js +125 -0
- package/utils/backend-path.js +38 -0
- package/utils/index.js +6 -0
- package/websocket/repositories/websocket-connection-repository-documentdb.js +119 -0
- package/websocket/repositories/websocket-connection-repository-factory.js +44 -0
- package/websocket/repositories/websocket-connection-repository-interface.js +106 -0
- package/websocket/repositories/websocket-connection-repository-mongo.js +156 -0
- package/websocket/repositories/websocket-connection-repository-postgres.js +196 -0
- package/websocket/repositories/websocket-connection-repository.js +161 -0
- package/assertions/is-equal.js +0 -17
- package/associations/model.js +0 -54
- package/database/models/IndividualUser.js +0 -76
- package/database/models/OrganizationUser.js +0 -29
- package/database/models/State.js +0 -9
- package/database/models/Token.js +0 -70
- package/database/models/UserModel.js +0 -7
- package/database/models/WebsocketConnection.js +0 -49
- package/database/mongo.js +0 -45
- package/database/mongoose.js +0 -5
- package/encrypt/Cryptor.test.js +0 -32
- package/encrypt/encrypt.js +0 -132
- package/encrypt/encrypt.test.js +0 -1069
- package/encrypt/test-encrypt.js +0 -107
- package/errors/base-error.test.js +0 -32
- package/errors/fetch-error.test.js +0 -79
- package/errors/halt-error.test.js +0 -11
- package/errors/validation-errors.test.js +0 -120
- 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/lambda/TimeoutCatcher.test.js +0 -68
- package/logs/logger.test.js +0 -76
- 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/entity.js +0 -46
- package/module-plugin/manager.js +0 -169
- package/module-plugin/module-factory.js +0 -61
- package/module-plugin/requester/api-key.js +0 -36
- package/module-plugin/requester/oauth-2.js +0 -219
- package/module-plugin/requester/requester.test.js +0 -28
- package/module-plugin/test/auther.test.js +0 -97
- package/syncs/model.js +0 -62
- /package/{module-plugin → modules}/ModuleConstants.js +0 -0
- /package/{module-plugin → modules}/requester/basic.js +0 -0
- /package/{module-plugin → modules}/test/mock-api/mocks/hubspot.js +0 -0
|
@@ -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',
|
|
@@ -10,6 +22,8 @@ const constantsToBeMigrated = {
|
|
|
10
22
|
GET_USER_ACTIONS: 'GET_USER_ACTIONS',
|
|
11
23
|
GET_USER_ACTION_OPTIONS: 'GET_USER_ACTION_OPTIONS',
|
|
12
24
|
REFRESH_USER_ACTION_OPTIONS: 'REFRESH_USER_ACTION_OPTIONS',
|
|
25
|
+
WEBHOOK_RECEIVED: 'WEBHOOK_RECEIVED', // HTTP handler, no DB
|
|
26
|
+
ON_WEBHOOK: 'ON_WEBHOOK', // Queue worker, DB-connected
|
|
13
27
|
// etc...
|
|
14
28
|
},
|
|
15
29
|
types: {
|
|
@@ -19,6 +33,16 @@ const constantsToBeMigrated = {
|
|
|
19
33
|
};
|
|
20
34
|
|
|
21
35
|
class IntegrationBase {
|
|
36
|
+
// todo: maybe we can pass this as Dependency Injection in the sub-class constructor
|
|
37
|
+
integrationRepository = createIntegrationRepository();
|
|
38
|
+
integrationMappingRepository = createIntegrationMappingRepository();
|
|
39
|
+
updateIntegrationStatus = new UpdateIntegrationStatus({
|
|
40
|
+
integrationRepository: this.integrationRepository,
|
|
41
|
+
});
|
|
42
|
+
updateIntegrationMessages = new UpdateIntegrationMessages({
|
|
43
|
+
integrationRepository: this.integrationRepository,
|
|
44
|
+
});
|
|
45
|
+
|
|
22
46
|
static getOptionDetails() {
|
|
23
47
|
const options = new Options({
|
|
24
48
|
module: Object.values(this.Definition.modules)[0], // This is a placeholder until we revamp the frontend
|
|
@@ -26,6 +50,7 @@ class IntegrationBase {
|
|
|
26
50
|
});
|
|
27
51
|
return options.get();
|
|
28
52
|
}
|
|
53
|
+
|
|
29
54
|
/**
|
|
30
55
|
* CHILDREN SHOULD SPECIFY A DEFINITION FOR THE INTEGRATION
|
|
31
56
|
*/
|
|
@@ -50,29 +75,30 @@ class IntegrationBase {
|
|
|
50
75
|
static getCurrentVersion() {
|
|
51
76
|
return this.Definition.version;
|
|
52
77
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
78
|
+
|
|
79
|
+
// REMOVED: registerEventHandlers() - Event handling is now done by IntegrationEventDispatcher
|
|
80
|
+
|
|
81
|
+
constructor(params = {}) {
|
|
82
|
+
this.modules = {};
|
|
83
|
+
this.events = this.events || {};
|
|
84
|
+
this.messages = { errors: [], warnings: [] };
|
|
85
|
+
this._isHydrated = false;
|
|
86
|
+
|
|
87
|
+
if (params && Object.keys(params).length > 0) {
|
|
88
|
+
this.setIntegrationRecord({
|
|
89
|
+
record: {
|
|
90
|
+
id: params.id,
|
|
91
|
+
userId: params.userId,
|
|
92
|
+
entities: params.entities,
|
|
93
|
+
config: params.config,
|
|
94
|
+
status: params.status,
|
|
95
|
+
version: params.version,
|
|
96
|
+
messages: params.messages,
|
|
97
|
+
},
|
|
98
|
+
modules: params.modules || [],
|
|
99
|
+
});
|
|
66
100
|
}
|
|
67
|
-
}
|
|
68
|
-
registerEventHandlers() {
|
|
69
|
-
this.on = {
|
|
70
|
-
...this.defaultEvents,
|
|
71
|
-
...this.events,
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
101
|
|
|
75
|
-
constructor(params) {
|
|
76
102
|
this.defaultEvents = {
|
|
77
103
|
[constantsToBeMigrated.defaultEvents.ON_CREATE]: {
|
|
78
104
|
type: constantsToBeMigrated.types.LIFE_CYCLE_EVENT,
|
|
@@ -106,22 +132,139 @@ class IntegrationBase {
|
|
|
106
132
|
type: constantsToBeMigrated.types.LIFE_CYCLE_EVENT,
|
|
107
133
|
handler: this.refreshActionOptions,
|
|
108
134
|
},
|
|
135
|
+
[constantsToBeMigrated.defaultEvents.WEBHOOK_RECEIVED]: {
|
|
136
|
+
type: constantsToBeMigrated.types.LIFE_CYCLE_EVENT,
|
|
137
|
+
handler: this.onWebhookReceived,
|
|
138
|
+
},
|
|
139
|
+
[constantsToBeMigrated.defaultEvents.ON_WEBHOOK]: {
|
|
140
|
+
type: constantsToBeMigrated.types.LIFE_CYCLE_EVENT,
|
|
141
|
+
handler: this.onWebhook,
|
|
142
|
+
},
|
|
109
143
|
};
|
|
110
|
-
this.loadModules();
|
|
111
144
|
}
|
|
112
145
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
146
|
+
// todo: debate wether we want to keep this pattern to set the record or not.
|
|
147
|
+
/**
|
|
148
|
+
* Persist the database record and module instances onto this integration instance.
|
|
149
|
+
* Accepts either a plain object containing the persisted fields or an object with
|
|
150
|
+
* a `record` property plus a `modules` collection.
|
|
151
|
+
* @param {Object} payload
|
|
152
|
+
* @param {Object} [payload.record]
|
|
153
|
+
* @param {Array} [payload.modules]
|
|
154
|
+
*/
|
|
155
|
+
setIntegrationRecord(payload = {}) {
|
|
156
|
+
if (!payload || Object.keys(payload).length === 0) {
|
|
157
|
+
throw new Error('setIntegrationRecord requires integration data');
|
|
118
158
|
}
|
|
119
|
-
|
|
159
|
+
|
|
160
|
+
const integrationRecord = payload.record;
|
|
161
|
+
const integrationModules = payload.modules ?? [];
|
|
162
|
+
|
|
163
|
+
if (!integrationRecord) {
|
|
164
|
+
throw new Error('Integration record not provided');
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const { id, userId, entities, config, status, version, messages } =
|
|
168
|
+
integrationRecord;
|
|
169
|
+
|
|
170
|
+
this.id = id;
|
|
171
|
+
this.userId = userId;
|
|
172
|
+
this.entities = entities;
|
|
173
|
+
this.config = config;
|
|
174
|
+
this.status = status;
|
|
175
|
+
this.version = version;
|
|
176
|
+
this.messages = messages || { errors: [], warnings: [] };
|
|
177
|
+
|
|
178
|
+
this.modules = this._appendModules(integrationModules);
|
|
179
|
+
|
|
180
|
+
this.record = {
|
|
181
|
+
id: this.id,
|
|
182
|
+
userId: this.userId,
|
|
183
|
+
entities: this.entities,
|
|
184
|
+
config: this.config,
|
|
185
|
+
status: this.status,
|
|
186
|
+
version: this.version,
|
|
187
|
+
messages: this.messages,
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
this._isHydrated = Boolean(this.id);
|
|
191
|
+
return this;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
get isHydrated() {
|
|
195
|
+
return this._isHydrated;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
assertHydrated(message = 'Integration instance is not hydrated') {
|
|
199
|
+
if (!this.isHydrated) {
|
|
200
|
+
throw new Error(message);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Returns the modules as object with keys as module names.
|
|
206
|
+
* Uses the keys from Definition.modules to attach modules correctly.
|
|
207
|
+
*
|
|
208
|
+
* Example:
|
|
209
|
+
* Definition.modules = { attio: {...}, quo: { definition: { getName: () => 'quo-attio' } } }
|
|
210
|
+
* Module with getName()='quo-attio' gets attached as this.quo (not this['quo-attio'])
|
|
211
|
+
*
|
|
212
|
+
* @private
|
|
213
|
+
* @param {Array} integrationModules - Array of module instances
|
|
214
|
+
* @returns {Object} The modules object
|
|
215
|
+
*/
|
|
216
|
+
_appendModules(integrationModules) {
|
|
217
|
+
const modules = {};
|
|
218
|
+
|
|
219
|
+
// Build reverse mapping: definition.getName() → referenceKey
|
|
220
|
+
// e.g., 'quo-attio' → 'quo', 'attio' → 'attio'
|
|
221
|
+
const moduleNameToKey = {};
|
|
222
|
+
if (this.constructor.Definition?.modules) {
|
|
223
|
+
for (const [key, moduleConfig] of Object.entries(this.constructor.Definition.modules)) {
|
|
224
|
+
const definition = moduleConfig.definition;
|
|
225
|
+
if (definition) {
|
|
226
|
+
// Use getName() if available, fallback to moduleName
|
|
227
|
+
const definitionName = typeof definition.getName === 'function'
|
|
228
|
+
? definition.getName()
|
|
229
|
+
: definition.moduleName;
|
|
230
|
+
if (definitionName) {
|
|
231
|
+
moduleNameToKey[definitionName] = key;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
for (const module of integrationModules) {
|
|
238
|
+
const moduleName =
|
|
239
|
+
typeof module.getName === 'function'
|
|
240
|
+
? module.getName()
|
|
241
|
+
: module.name;
|
|
242
|
+
|
|
243
|
+
// Use the reference key from Definition.modules if available,
|
|
244
|
+
// otherwise fall back to moduleName
|
|
245
|
+
const key = moduleNameToKey[moduleName] || moduleName;
|
|
246
|
+
|
|
247
|
+
if (key) {
|
|
248
|
+
modules[key] = module;
|
|
249
|
+
this[key] = module;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Wire the Delegate pattern so Module can notify this integration
|
|
253
|
+
// of events it cannot handle itself (e.g. credential invalidation
|
|
254
|
+
// needing an Integration.status flip). Without this, Module.notify
|
|
255
|
+
// silently no-ops and Integration.status never updates on auth
|
|
256
|
+
// failure.
|
|
257
|
+
if (module && typeof module === 'object') {
|
|
258
|
+
module.delegate = this;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
return modules;
|
|
120
263
|
}
|
|
121
264
|
|
|
122
265
|
async validateConfig() {
|
|
123
266
|
const configOptions = await this.getConfigOptions();
|
|
124
|
-
const currentConfig = this.
|
|
267
|
+
const currentConfig = this.getConfig();
|
|
125
268
|
let needsConfig = false;
|
|
126
269
|
for (const option of configOptions) {
|
|
127
270
|
if (option.required) {
|
|
@@ -133,56 +276,62 @@ class IntegrationBase {
|
|
|
133
276
|
)
|
|
134
277
|
) {
|
|
135
278
|
needsConfig = true;
|
|
136
|
-
this.
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
279
|
+
await this.updateIntegrationMessages.execute(
|
|
280
|
+
this.id,
|
|
281
|
+
'warnings',
|
|
282
|
+
'Config Validation Error',
|
|
283
|
+
`Missing required field of ${option.label}`,
|
|
284
|
+
Date.now()
|
|
285
|
+
);
|
|
141
286
|
}
|
|
142
287
|
}
|
|
143
288
|
}
|
|
144
289
|
if (needsConfig) {
|
|
145
|
-
this.
|
|
146
|
-
await this.record.save();
|
|
290
|
+
await this.updateIntegrationStatus.execute(this.id, 'NEEDS_CONFIG');
|
|
147
291
|
}
|
|
148
292
|
}
|
|
149
293
|
|
|
150
294
|
async testAuth() {
|
|
151
295
|
let didAuthPass = true;
|
|
152
296
|
|
|
153
|
-
for (const module of Object.keys(
|
|
297
|
+
for (const module of Object.keys(this.constructor.Definition.modules)) {
|
|
154
298
|
try {
|
|
155
299
|
await this[module].testAuth();
|
|
156
300
|
} catch {
|
|
157
301
|
didAuthPass = false;
|
|
158
|
-
this.
|
|
159
|
-
|
|
160
|
-
|
|
302
|
+
await this.updateIntegrationMessages.execute(
|
|
303
|
+
this.id,
|
|
304
|
+
'errors',
|
|
305
|
+
'Authentication Error',
|
|
306
|
+
`There was an error with your ${this[
|
|
161
307
|
module
|
|
162
308
|
].constructor.getName()} Entity.
|
|
163
309
|
Please reconnect/re-authenticate, or reach out to Support for assistance.`,
|
|
164
|
-
|
|
165
|
-
|
|
310
|
+
Date.now()
|
|
311
|
+
);
|
|
166
312
|
}
|
|
167
313
|
}
|
|
168
314
|
|
|
169
315
|
if (!didAuthPass) {
|
|
170
|
-
this.
|
|
171
|
-
this.record.markModified('messages.error');
|
|
172
|
-
await this.record.save();
|
|
316
|
+
await this.updateIntegrationStatus.execute(this.id, 'ERROR');
|
|
173
317
|
}
|
|
174
318
|
}
|
|
175
319
|
|
|
176
320
|
async getMapping(sourceId) {
|
|
177
|
-
|
|
321
|
+
// todo: not sure we should call the repository directly from here
|
|
322
|
+
return this.integrationMappingRepository.findMappingBy(
|
|
323
|
+
this.id,
|
|
324
|
+
sourceId
|
|
325
|
+
);
|
|
178
326
|
}
|
|
179
327
|
|
|
180
328
|
async upsertMapping(sourceId, mapping) {
|
|
181
329
|
if (!sourceId) {
|
|
182
330
|
throw new Error(`sourceId must be set`);
|
|
183
331
|
}
|
|
184
|
-
|
|
185
|
-
|
|
332
|
+
// todo: not sure we should call the repository directly from here
|
|
333
|
+
return await this.integrationMappingRepository.upsertMapping(
|
|
334
|
+
this.id,
|
|
186
335
|
sourceId,
|
|
187
336
|
mapping
|
|
188
337
|
);
|
|
@@ -191,13 +340,13 @@ class IntegrationBase {
|
|
|
191
340
|
/**
|
|
192
341
|
* CHILDREN CAN OVERRIDE THESE CONFIGURATION METHODS
|
|
193
342
|
*/
|
|
194
|
-
async onCreate(
|
|
195
|
-
this.
|
|
196
|
-
await this.record.save();
|
|
197
|
-
return this.record;
|
|
343
|
+
async onCreate({ integrationId }) {
|
|
344
|
+
await this.updateIntegrationStatus.execute(integrationId, 'ENABLED');
|
|
198
345
|
}
|
|
199
346
|
|
|
200
|
-
async onUpdate(params) {
|
|
347
|
+
async onUpdate(params) {
|
|
348
|
+
return this.validateConfig();
|
|
349
|
+
}
|
|
201
350
|
|
|
202
351
|
async onDelete(params) {}
|
|
203
352
|
|
|
@@ -224,7 +373,6 @@ class IntegrationBase {
|
|
|
224
373
|
return {};
|
|
225
374
|
}
|
|
226
375
|
async loadUserActions({ actionType } = {}) {
|
|
227
|
-
console.log('loadUserActions called with actionType:', actionType);
|
|
228
376
|
const userActions = {};
|
|
229
377
|
for (const [key, event] of Object.entries(this.events)) {
|
|
230
378
|
if (event.type === constantsToBeMigrated.types.USER_ACTION) {
|
|
@@ -259,6 +407,167 @@ class IntegrationBase {
|
|
|
259
407
|
};
|
|
260
408
|
return options;
|
|
261
409
|
}
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* WEBHOOK EVENT HANDLERS
|
|
413
|
+
*/
|
|
414
|
+
async onWebhookReceived({ req, res }) {
|
|
415
|
+
// Default: queue webhook for processing
|
|
416
|
+
const body = req.body;
|
|
417
|
+
const integrationId = req.params.integrationId || null;
|
|
418
|
+
|
|
419
|
+
await this.queueWebhook({
|
|
420
|
+
integrationId,
|
|
421
|
+
body,
|
|
422
|
+
headers: req.headers,
|
|
423
|
+
query: req.query,
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
res.status(200).json({ received: true });
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
async onWebhook({ data }) {
|
|
430
|
+
// Default: no-op, integrations override this
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
async queueWebhook(data) {
|
|
434
|
+
const { QueuerUtil } = require('../queues');
|
|
435
|
+
|
|
436
|
+
const queueName = `${this.constructor.Definition.name
|
|
437
|
+
.toUpperCase()
|
|
438
|
+
.replace(/-/g, '_')}_QUEUE_URL`;
|
|
439
|
+
const queueUrl = process.env[queueName];
|
|
440
|
+
|
|
441
|
+
if (!queueUrl) {
|
|
442
|
+
throw new Error(`Queue URL not found for ${queueName}`);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
return QueuerUtil.send(
|
|
446
|
+
{
|
|
447
|
+
event: 'ON_WEBHOOK',
|
|
448
|
+
data,
|
|
449
|
+
},
|
|
450
|
+
queueUrl
|
|
451
|
+
);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// === Domain Methods (moved from Integration.js) ===
|
|
455
|
+
|
|
456
|
+
getConfig() {
|
|
457
|
+
return this.config;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
getModule(key) {
|
|
461
|
+
return this.modules[key];
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
setModule(key, module) {
|
|
465
|
+
this.modules[key] = module;
|
|
466
|
+
this[key] = module;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
addError(error) {
|
|
470
|
+
if (!this.messages.errors) {
|
|
471
|
+
this.messages.errors = [];
|
|
472
|
+
}
|
|
473
|
+
this.messages.errors.push(error);
|
|
474
|
+
this.status = 'ERROR';
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
addWarning(warning) {
|
|
478
|
+
if (!this.messages.warnings) {
|
|
479
|
+
this.messages.warnings = [];
|
|
480
|
+
}
|
|
481
|
+
this.messages.warnings.push(warning);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
isActive() {
|
|
485
|
+
return this.status === 'ENABLED' || this.status === 'ACTIVE';
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
needsConfiguration() {
|
|
489
|
+
return this.status === 'NEEDS_CONFIG';
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
hasErrors() {
|
|
493
|
+
return this.status === 'ERROR';
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
belongsToUser(userId) {
|
|
497
|
+
return this.userId.toString() === userId.toString();
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
registerEventHandlers() {
|
|
501
|
+
this.on = {
|
|
502
|
+
...this.defaultEvents,
|
|
503
|
+
...this.events,
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
async initialize() {
|
|
508
|
+
try {
|
|
509
|
+
const additionalUserActions = await this.loadDynamicUserActions();
|
|
510
|
+
this.events = { ...this.events, ...additionalUserActions };
|
|
511
|
+
} catch (e) {
|
|
512
|
+
this.addError(e);
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
this.registerEventHandlers();
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
async send(event, object) {
|
|
519
|
+
if (!this.on[event]) {
|
|
520
|
+
throw new Error(
|
|
521
|
+
`Event ${event} is not defined in the Integration event object`
|
|
522
|
+
);
|
|
523
|
+
}
|
|
524
|
+
return this.on[event].handler.call(this, object);
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
getOptionDetails() {
|
|
528
|
+
const options = new Options({
|
|
529
|
+
module: Object.values(this.constructor.Definition.modules)[0],
|
|
530
|
+
...this.constructor.Definition,
|
|
531
|
+
});
|
|
532
|
+
return options.get();
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
// Legacy method for backward compatibility
|
|
536
|
+
async loadModules() {
|
|
537
|
+
// This method was used in the old architecture for loading modules
|
|
538
|
+
// In the new architecture, modules are injected via constructor
|
|
539
|
+
// For backward compatibility, this is a no-op
|
|
540
|
+
return;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
/**
|
|
544
|
+
* Receives notifications from modules (the Delegate pattern) when
|
|
545
|
+
* something integration-level needs attention. Today this catches the
|
|
546
|
+
* `CREDENTIAL_INVALIDATED` event Module fires from `markCredentialsInvalid`
|
|
547
|
+
* and flips this integration's status to DISABLED so the queue worker
|
|
548
|
+
* stops processing further webhooks until the user re-authorizes.
|
|
549
|
+
*
|
|
550
|
+
* Modules are wired to this delegate in `_appendModules()`, which runs
|
|
551
|
+
* during `setIntegrationRecord()` — this covers every construction path
|
|
552
|
+
* (HTTP read, queue worker, create/update/delete flows, etc.).
|
|
553
|
+
*
|
|
554
|
+
* The delegate string below must match `Module.DLGT_CREDENTIAL_INVALIDATED`
|
|
555
|
+
* in `packages/core/modules/module.js`.
|
|
556
|
+
*
|
|
557
|
+
* @param {Object} notifier - The module that fired the event
|
|
558
|
+
* @param {string} delegateString - Event type string
|
|
559
|
+
* @param {Object} [object] - Optional event payload
|
|
560
|
+
* @returns {Promise<void>}
|
|
561
|
+
*/
|
|
562
|
+
async receiveNotification(notifier, delegateString, object = null) {
|
|
563
|
+
if (delegateString !== 'CREDENTIAL_INVALIDATED') return;
|
|
564
|
+
if (!this.id) return;
|
|
565
|
+
console.log(
|
|
566
|
+
`[Frigg] Module ${notifier?.name || '?'} reported invalid credentials for integration ${this.id} — marking ERROR`
|
|
567
|
+
);
|
|
568
|
+
await this.updateIntegrationStatus.execute(this.id, 'ERROR');
|
|
569
|
+
this.status = 'ERROR';
|
|
570
|
+
}
|
|
262
571
|
}
|
|
263
572
|
|
|
264
573
|
module.exports = { IntegrationBase };
|