@friggframework/core 2.0.0-next.5 → 2.0.0-next.50
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +693 -0
- package/README.md +959 -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 +179 -0
- package/application/commands/user-commands.js +213 -0
- package/application/index.js +69 -0
- package/core/CLAUDE.md +690 -0
- package/core/Worker.js +8 -21
- package/core/create-handler.js +2 -7
- 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 +307 -0
- package/credential/repositories/credential-repository-postgres.js +313 -0
- package/credential/repositories/credential-repository.js +302 -0
- package/credential/use-cases/get-credential-for-user.js +21 -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/encryption/README.md +684 -0
- package/database/encryption/encryption-schema-registry.js +141 -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 +25 -12
- package/database/models/WebsocketConnection.js +16 -10
- package/database/models/readme.md +1 -0
- package/database/prisma.js +222 -0
- package/database/repositories/health-check-repository-factory.js +43 -0
- package/database/repositories/health-check-repository-interface.js +87 -0
- package/database/repositories/health-check-repository-mongodb.js +91 -0
- package/database/repositories/health-check-repository-postgres.js +82 -0
- package/database/repositories/health-check-repository.js +108 -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 +137 -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 +91 -0
- package/database/utils/mongodb-schema-init.js +106 -0
- package/database/utils/prisma-runner.js +400 -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/encrypt/test-encrypt.js +0 -2
- 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 +334 -0
- package/generated/prisma-mongodb/index-browser.js +316 -0
- package/generated/prisma-mongodb/index.d.ts +22898 -0
- package/generated/prisma-mongodb/index.js +359 -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 +3982 -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 +341 -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 +356 -0
- package/generated/prisma-postgresql/index-browser.js +338 -0
- package/generated/prisma-postgresql/index.d.ts +25072 -0
- package/generated/prisma-postgresql/index.js +381 -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 +3982 -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 +363 -0
- package/handlers/WEBHOOKS.md +653 -0
- package/handlers/app-definition-loader.js +38 -0
- package/handlers/app-handler-helpers.js +56 -0
- package/handlers/backend-utils.js +180 -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 +256 -0
- package/handlers/routers/health.js +519 -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/integration-defined-workers.js +27 -0
- package/index.js +77 -22
- package/integrations/WEBHOOK-QUICKSTART.md +151 -0
- package/integrations/index.js +12 -10
- package/integrations/integration-base.js +296 -54
- package/integrations/integration-router.js +381 -182
- 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 +127 -0
- package/integrations/repositories/integration-repository-mongo.js +303 -0
- package/integrations/repositories/integration-repository-postgres.js +352 -0
- package/integrations/repositories/process-repository-factory.js +46 -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/dummy-integration-class.js +83 -0
- package/integrations/tests/doubles/test-integration-repository.js +99 -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 +87 -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 +93 -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 +36 -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}/entity.js +1 -1
- package/{module-plugin → modules}/index.js +0 -8
- package/modules/module-factory.js +56 -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 +377 -0
- package/modules/repositories/module-repository-postgres.js +426 -0
- package/modules/repositories/module-repository.js +316 -0
- package/{module-plugin → modules}/requester/requester.js +1 -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 +55 -0
- package/modules/use-cases/process-authorization-callback.js +122 -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 +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/migration_lock.toml +3 -0
- package/prisma-postgresql/schema.prisma +345 -0
- package/queues/queuer-util.js +28 -15
- 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/core/index.d.ts +2 -2
- package/types/integrations/index.d.ts +2 -6
- package/types/module-plugin/index.d.ts +5 -59
- 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 +291 -0
- package/user/repositories/user-repository-postgres.js +350 -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 +106 -0
- package/user/use-cases/login-user.js +122 -0
- package/user/user.js +93 -0
- package/utils/backend-path.js +38 -0
- package/utils/index.js +6 -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 +156 -0
- package/websocket/repositories/websocket-connection-repository-postgres.js +196 -0
- package/websocket/repositories/websocket-connection-repository.js +161 -0
- package/database/models/State.js +0 -9
- package/database/models/Token.js +0 -70
- package/database/mongo.js +0 -45
- package/encrypt/Cryptor.test.js +0 -32
- package/encrypt/encrypt.js +0 -132
- package/encrypt/encrypt.test.js +0 -1069
- 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/manager.js +0 -169
- package/module-plugin/module-factory.js +0 -61
- package/module-plugin/requester/requester.test.js +0 -28
- 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}/test/mock-api/mocks/hubspot.js +0 -0
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
const {
|
|
2
|
+
createModuleRepository,
|
|
3
|
+
} = require('../../modules/repositories/module-repository-factory');
|
|
4
|
+
|
|
5
|
+
const ERROR_CODE_MAP = {
|
|
6
|
+
ENTITY_NOT_FOUND: 404,
|
|
7
|
+
INVALID_ENTITY_DATA: 400,
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
function mapErrorToResponse(error) {
|
|
11
|
+
const status = ERROR_CODE_MAP[error?.code] || 500;
|
|
12
|
+
return {
|
|
13
|
+
error: status,
|
|
14
|
+
reason: error?.message,
|
|
15
|
+
code: error?.code,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Create entity command factory
|
|
21
|
+
*
|
|
22
|
+
* NOTE: This is an internal API. Integration developers should use createFriggCommands() instead.
|
|
23
|
+
*
|
|
24
|
+
* @returns {Object} Entity command object with CRUD operations
|
|
25
|
+
*/
|
|
26
|
+
function createEntityCommands() {
|
|
27
|
+
const moduleRepo = createModuleRepository();
|
|
28
|
+
|
|
29
|
+
return {
|
|
30
|
+
/**
|
|
31
|
+
* Create a new entity
|
|
32
|
+
* @param {Object} params
|
|
33
|
+
* @param {string} params.userId - User ID who owns this entity
|
|
34
|
+
* @param {string} params.externalId - External identifier from the API module
|
|
35
|
+
* @param {string} params.name - Entity name
|
|
36
|
+
* @param {string} params.moduleName - Module name (e.g., 'husbpot', 'frontify')
|
|
37
|
+
* @param {string} [params.credentialId] - Associated credential ID
|
|
38
|
+
* @returns {Promise<Object>} Created entity object
|
|
39
|
+
*/
|
|
40
|
+
async createEntity({
|
|
41
|
+
userId,
|
|
42
|
+
externalId,
|
|
43
|
+
name,
|
|
44
|
+
moduleName,
|
|
45
|
+
credentialId,
|
|
46
|
+
} = {}) {
|
|
47
|
+
try {
|
|
48
|
+
if (!userId || !externalId || !moduleName) {
|
|
49
|
+
const error = new Error(
|
|
50
|
+
'userId, externalId, and moduleName are required'
|
|
51
|
+
);
|
|
52
|
+
error.code = 'INVALID_ENTITY_DATA';
|
|
53
|
+
throw error;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const entityData = {
|
|
57
|
+
user: userId,
|
|
58
|
+
externalId,
|
|
59
|
+
name,
|
|
60
|
+
moduleName,
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
if (credentialId) {
|
|
64
|
+
entityData.credential = credentialId;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const entity = await moduleRepo.createEntity(entityData);
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
id: entity.id,
|
|
71
|
+
userId: entity.userId,
|
|
72
|
+
externalId: entity.externalId,
|
|
73
|
+
name: entity.name,
|
|
74
|
+
moduleName: entity.moduleName,
|
|
75
|
+
credentialId: entity.credential?._id
|
|
76
|
+
? entity.credential._id.toString()
|
|
77
|
+
: entity.credential,
|
|
78
|
+
};
|
|
79
|
+
} catch (error) {
|
|
80
|
+
return mapErrorToResponse(error);
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Find an entity by filter criteria
|
|
86
|
+
* @param {Object} filter
|
|
87
|
+
* @param {string} [filter.externalId] - External ID to search for
|
|
88
|
+
* @param {string} [filter.userId] - User ID to search for
|
|
89
|
+
* @param {string} [filter.moduleName] - Module name to search for
|
|
90
|
+
* @returns {Promise<Object|null>} Entity object or null if not found
|
|
91
|
+
*/
|
|
92
|
+
async findEntity(filter = {}) {
|
|
93
|
+
try {
|
|
94
|
+
if (
|
|
95
|
+
!filter.externalId &&
|
|
96
|
+
!filter.userId &&
|
|
97
|
+
!filter.moduleName
|
|
98
|
+
) {
|
|
99
|
+
const error = new Error(
|
|
100
|
+
'At least one filter criterion is required'
|
|
101
|
+
);
|
|
102
|
+
error.code = 'INVALID_ENTITY_DATA';
|
|
103
|
+
throw error;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const entity = await moduleRepo.findEntity(filter);
|
|
107
|
+
|
|
108
|
+
if (!entity) {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
id: entity.id,
|
|
114
|
+
userId: entity.userId,
|
|
115
|
+
externalId: entity.externalId,
|
|
116
|
+
name: entity.name,
|
|
117
|
+
moduleName: entity.moduleName,
|
|
118
|
+
credentialId: entity.credential?._id
|
|
119
|
+
? entity.credential._id.toString()
|
|
120
|
+
: entity.credential,
|
|
121
|
+
};
|
|
122
|
+
} catch (error) {
|
|
123
|
+
return mapErrorToResponse(error);
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Find all entities for a user
|
|
129
|
+
* @param {string} userId - User ID to search for
|
|
130
|
+
* @returns {Promise<Array>} Array of entity objects
|
|
131
|
+
*/
|
|
132
|
+
async findEntitiesByUserId(userId) {
|
|
133
|
+
try {
|
|
134
|
+
if (!userId) {
|
|
135
|
+
const error = new Error('userId is required');
|
|
136
|
+
error.code = 'INVALID_ENTITY_DATA';
|
|
137
|
+
throw error;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const entities = await moduleRepo.findEntitiesByUserId(userId);
|
|
141
|
+
|
|
142
|
+
return entities.map((entity) => ({
|
|
143
|
+
id: entity.id,
|
|
144
|
+
userId: entity.userId,
|
|
145
|
+
externalId: entity.externalId,
|
|
146
|
+
name: entity.name,
|
|
147
|
+
moduleName: entity.moduleName,
|
|
148
|
+
credentialId: entity.credential?._id
|
|
149
|
+
? entity.credential._id.toString()
|
|
150
|
+
: entity.credential,
|
|
151
|
+
}));
|
|
152
|
+
} catch (error) {
|
|
153
|
+
if (error.code) {
|
|
154
|
+
return mapErrorToResponse(error);
|
|
155
|
+
}
|
|
156
|
+
// For find operations, return empty array on error instead of error object
|
|
157
|
+
return [];
|
|
158
|
+
}
|
|
159
|
+
},
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Find entities by user ID and module name
|
|
163
|
+
* @param {string} userId - User ID to search for
|
|
164
|
+
* @param {string} moduleName - Module name to filter by
|
|
165
|
+
* @returns {Promise<Array>} Array of entity objects
|
|
166
|
+
*/
|
|
167
|
+
async findEntitiesByUserIdAndModuleName(userId, moduleName) {
|
|
168
|
+
try {
|
|
169
|
+
if (!userId || !moduleName) {
|
|
170
|
+
const error = new Error(
|
|
171
|
+
'userId and moduleName are required'
|
|
172
|
+
);
|
|
173
|
+
error.code = 'INVALID_ENTITY_DATA';
|
|
174
|
+
throw error;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const entities =
|
|
178
|
+
await moduleRepo.findEntitiesByUserIdAndModuleName(
|
|
179
|
+
userId,
|
|
180
|
+
moduleName
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
return entities.map((entity) => ({
|
|
184
|
+
id: entity.id,
|
|
185
|
+
userId: entity.userId,
|
|
186
|
+
externalId: entity.externalId,
|
|
187
|
+
name: entity.name,
|
|
188
|
+
moduleName: entity.moduleName,
|
|
189
|
+
credentialId: entity.credential?._id
|
|
190
|
+
? entity.credential._id.toString()
|
|
191
|
+
: entity.credential,
|
|
192
|
+
}));
|
|
193
|
+
} catch (error) {
|
|
194
|
+
if (error.code) {
|
|
195
|
+
return mapErrorToResponse(error);
|
|
196
|
+
}
|
|
197
|
+
return [];
|
|
198
|
+
}
|
|
199
|
+
},
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Find an entity by ID
|
|
203
|
+
* @param {string} entityId - Entity ID to search for
|
|
204
|
+
* @returns {Promise<Object>} Entity object
|
|
205
|
+
*/
|
|
206
|
+
async findEntityById(entityId) {
|
|
207
|
+
try {
|
|
208
|
+
if (!entityId) {
|
|
209
|
+
const error = new Error('entityId is required');
|
|
210
|
+
error.code = 'INVALID_ENTITY_DATA';
|
|
211
|
+
throw error;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const entity = await moduleRepo.findEntityById(entityId);
|
|
215
|
+
|
|
216
|
+
return {
|
|
217
|
+
id: entity.id,
|
|
218
|
+
userId: entity.userId,
|
|
219
|
+
externalId: entity.externalId,
|
|
220
|
+
name: entity.name,
|
|
221
|
+
moduleName: entity.moduleName,
|
|
222
|
+
credentialId: entity.credential?._id
|
|
223
|
+
? entity.credential._id.toString()
|
|
224
|
+
: entity.credential,
|
|
225
|
+
};
|
|
226
|
+
} catch (error) {
|
|
227
|
+
return mapErrorToResponse(error);
|
|
228
|
+
}
|
|
229
|
+
},
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Update an entity
|
|
233
|
+
* @param {string} entityId - Entity ID to update
|
|
234
|
+
* @param {Object} updates - Fields to update
|
|
235
|
+
* @returns {Promise<Object>} Updated entity object
|
|
236
|
+
*/
|
|
237
|
+
async updateEntity(entityId, updates) {
|
|
238
|
+
try {
|
|
239
|
+
if (!entityId) {
|
|
240
|
+
const error = new Error('entityId is required');
|
|
241
|
+
error.code = 'INVALID_ENTITY_DATA';
|
|
242
|
+
throw error;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const entity = await moduleRepo.updateEntity(entityId, updates);
|
|
246
|
+
|
|
247
|
+
if (!entity) {
|
|
248
|
+
const error = new Error(`Entity ${entityId} not found`);
|
|
249
|
+
error.code = 'ENTITY_NOT_FOUND';
|
|
250
|
+
throw error;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
return {
|
|
254
|
+
id: entity.id,
|
|
255
|
+
userId: entity.userId,
|
|
256
|
+
externalId: entity.externalId,
|
|
257
|
+
name: entity.name,
|
|
258
|
+
moduleName: entity.moduleName,
|
|
259
|
+
credentialId: entity.credential?._id
|
|
260
|
+
? entity.credential._id.toString()
|
|
261
|
+
: entity.credential,
|
|
262
|
+
};
|
|
263
|
+
} catch (error) {
|
|
264
|
+
return mapErrorToResponse(error);
|
|
265
|
+
}
|
|
266
|
+
},
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Delete an entity
|
|
270
|
+
* @param {string} entityId - Entity ID to delete
|
|
271
|
+
* @returns {Promise<Object>} Result object with success flag
|
|
272
|
+
*/
|
|
273
|
+
async deleteEntity(entityId) {
|
|
274
|
+
try {
|
|
275
|
+
if (!entityId) {
|
|
276
|
+
const error = new Error('entityId is required');
|
|
277
|
+
error.code = 'INVALID_ENTITY_DATA';
|
|
278
|
+
throw error;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
await moduleRepo.deleteEntity(entityId);
|
|
282
|
+
|
|
283
|
+
return { success: true };
|
|
284
|
+
} catch (error) {
|
|
285
|
+
return mapErrorToResponse(error);
|
|
286
|
+
}
|
|
287
|
+
},
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Remove credential reference from an entity
|
|
291
|
+
* @param {string} entityId - Entity ID to update
|
|
292
|
+
* @returns {Promise<Object>} Result object with success flag
|
|
293
|
+
*/
|
|
294
|
+
async unsetCredential(entityId) {
|
|
295
|
+
try {
|
|
296
|
+
if (!entityId) {
|
|
297
|
+
const error = new Error('entityId is required');
|
|
298
|
+
error.code = 'INVALID_ENTITY_DATA';
|
|
299
|
+
throw error;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const acknowledged = await moduleRepo.unsetCredential(entityId);
|
|
303
|
+
|
|
304
|
+
return { success: acknowledged };
|
|
305
|
+
} catch (error) {
|
|
306
|
+
return mapErrorToResponse(error);
|
|
307
|
+
}
|
|
308
|
+
},
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
module.exports = {
|
|
313
|
+
createEntityCommands,
|
|
314
|
+
ERROR_CODE_MAP,
|
|
315
|
+
};
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
const {
|
|
2
|
+
createIntegrationRepository,
|
|
3
|
+
} = require('../../integrations/repositories/integration-repository-factory');
|
|
4
|
+
const {
|
|
5
|
+
createModuleRepository,
|
|
6
|
+
} = require('../../modules/repositories/module-repository-factory');
|
|
7
|
+
const { ModuleFactory } = require('../../modules/module-factory');
|
|
8
|
+
const {
|
|
9
|
+
LoadIntegrationContextUseCase,
|
|
10
|
+
} = require('../../integrations/use-cases/load-integration-context');
|
|
11
|
+
const {
|
|
12
|
+
FindIntegrationContextByExternalEntityIdUseCase,
|
|
13
|
+
} = require('../../integrations/use-cases/find-integration-context-by-external-entity-id');
|
|
14
|
+
const {
|
|
15
|
+
GetIntegrationsForUser,
|
|
16
|
+
} = require('../../integrations/use-cases/get-integrations-for-user');
|
|
17
|
+
const {
|
|
18
|
+
CreateIntegration,
|
|
19
|
+
} = require('../../integrations/use-cases/create-integration');
|
|
20
|
+
const {
|
|
21
|
+
getModulesDefinitionFromIntegrationClasses,
|
|
22
|
+
} = require('../../integrations/utils/map-integration-dto');
|
|
23
|
+
|
|
24
|
+
const ERROR_CODE_MAP = {
|
|
25
|
+
ENTITY_NOT_FOUND: 401,
|
|
26
|
+
ENTITY_USER_NOT_FOUND: 401,
|
|
27
|
+
INTEGRATION_NOT_FOUND: 404,
|
|
28
|
+
EXTERNAL_ENTITY_ID_REQUIRED: 400,
|
|
29
|
+
INTEGRATION_RECORD_NOT_FOUND: 404,
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
function mapErrorToResponse(error) {
|
|
33
|
+
const status = ERROR_CODE_MAP[error?.code] || 500;
|
|
34
|
+
return {
|
|
35
|
+
error: status,
|
|
36
|
+
reason: error?.message,
|
|
37
|
+
code: error?.code,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function createIntegrationCommands({ integrationClass } = {}) {
|
|
42
|
+
if (!integrationClass) {
|
|
43
|
+
throw new Error('integrationClass is required');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Always use Frigg's default repositories and use cases
|
|
47
|
+
const integrationRepository = createIntegrationRepository();
|
|
48
|
+
const moduleRepository = createModuleRepository();
|
|
49
|
+
|
|
50
|
+
const moduleDefinitions = getModulesDefinitionFromIntegrationClasses([
|
|
51
|
+
integrationClass,
|
|
52
|
+
]);
|
|
53
|
+
|
|
54
|
+
const moduleFactory = new ModuleFactory({
|
|
55
|
+
moduleRepository,
|
|
56
|
+
moduleDefinitions,
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const loadIntegrationContextUseCase = new LoadIntegrationContextUseCase({
|
|
60
|
+
integrationRepository,
|
|
61
|
+
moduleRepository,
|
|
62
|
+
moduleFactory,
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const findByExternalEntityIdUseCase =
|
|
66
|
+
new FindIntegrationContextByExternalEntityIdUseCase({
|
|
67
|
+
integrationRepository,
|
|
68
|
+
moduleRepository,
|
|
69
|
+
loadIntegrationContextUseCase: loadIntegrationContextUseCase,
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
const getIntegrationsForUserUseCase = new GetIntegrationsForUser({
|
|
73
|
+
integrationRepository,
|
|
74
|
+
integrationClasses: [integrationClass],
|
|
75
|
+
moduleFactory,
|
|
76
|
+
moduleRepository,
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
const createIntegrationUseCase = new CreateIntegration({
|
|
80
|
+
integrationRepository,
|
|
81
|
+
integrationClasses: [integrationClass],
|
|
82
|
+
moduleFactory,
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
async findIntegrationContextByExternalEntityId(externalEntityId) {
|
|
87
|
+
try {
|
|
88
|
+
const { context } = await findByExternalEntityIdUseCase.execute(
|
|
89
|
+
{
|
|
90
|
+
externalEntityId,
|
|
91
|
+
}
|
|
92
|
+
);
|
|
93
|
+
return { context };
|
|
94
|
+
} catch (error) {
|
|
95
|
+
return mapErrorToResponse(error);
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
|
|
99
|
+
async loadIntegrationContextById(integrationId) {
|
|
100
|
+
try {
|
|
101
|
+
const context = await loadIntegrationContextUseCase.execute({
|
|
102
|
+
integrationId,
|
|
103
|
+
});
|
|
104
|
+
return { context };
|
|
105
|
+
} catch (error) {
|
|
106
|
+
return mapErrorToResponse(error);
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Find all integrations for a user
|
|
112
|
+
* @param {string} userId - User ID to search for
|
|
113
|
+
* @returns {Promise<Array>} Array of integration records
|
|
114
|
+
*/
|
|
115
|
+
async findIntegrationsByUserId(userId) {
|
|
116
|
+
try {
|
|
117
|
+
const integrations =
|
|
118
|
+
await getIntegrationsForUserUseCase.execute(userId);
|
|
119
|
+
return integrations;
|
|
120
|
+
} catch (error) {
|
|
121
|
+
return mapErrorToResponse(error);
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Create a new integration
|
|
127
|
+
* @param {Object} params
|
|
128
|
+
* @param {Array<string>} params.entityIds - Array of entity IDs
|
|
129
|
+
* @param {string} params.userId - User ID
|
|
130
|
+
* @param {Object} params.config - Integration configuration (must include type)
|
|
131
|
+
* @returns {Promise<Object>} Created integration object
|
|
132
|
+
*/
|
|
133
|
+
async createIntegration({ entityIds, userId, config }) {
|
|
134
|
+
try {
|
|
135
|
+
const integration = await createIntegrationUseCase.execute(
|
|
136
|
+
entityIds,
|
|
137
|
+
userId,
|
|
138
|
+
config
|
|
139
|
+
);
|
|
140
|
+
return integration;
|
|
141
|
+
} catch (error) {
|
|
142
|
+
return mapErrorToResponse(error);
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Update integration configuration
|
|
148
|
+
* @param {Object} params
|
|
149
|
+
* @param {string} params.integrationId - Integration ID
|
|
150
|
+
* @param {Object} params.config - Updated config object
|
|
151
|
+
* @returns {Promise<Object>} Updated integration
|
|
152
|
+
*/
|
|
153
|
+
async updateIntegrationConfig({ integrationId, config }) {
|
|
154
|
+
try {
|
|
155
|
+
const integration = await integrationRepository.updateIntegrationConfig(
|
|
156
|
+
integrationId,
|
|
157
|
+
config
|
|
158
|
+
);
|
|
159
|
+
return integration;
|
|
160
|
+
} catch (error) {
|
|
161
|
+
return mapErrorToResponse(error);
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
async function findIntegrationContextByExternalEntityId({
|
|
168
|
+
integrationClass,
|
|
169
|
+
externalEntityId,
|
|
170
|
+
} = {}) {
|
|
171
|
+
const commands = createIntegrationCommands({ integrationClass });
|
|
172
|
+
|
|
173
|
+
return commands.findIntegrationContextByExternalEntityId(externalEntityId);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
module.exports = {
|
|
177
|
+
createIntegrationCommands,
|
|
178
|
+
findIntegrationContextByExternalEntityId,
|
|
179
|
+
};
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
const {
|
|
2
|
+
createUserRepository,
|
|
3
|
+
} = require('../../user/repositories/user-repository-factory');
|
|
4
|
+
|
|
5
|
+
const ERROR_CODE_MAP = {
|
|
6
|
+
USER_NOT_FOUND: 404,
|
|
7
|
+
USER_ALREADY_EXISTS: 409,
|
|
8
|
+
INVALID_USER_DATA: 400,
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
function mapErrorToResponse(error) {
|
|
12
|
+
const status = ERROR_CODE_MAP[error?.code] || 500;
|
|
13
|
+
return {
|
|
14
|
+
error: status,
|
|
15
|
+
reason: error?.message,
|
|
16
|
+
code: error?.code,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Create user command factory
|
|
22
|
+
*
|
|
23
|
+
* NOTE: This is an internal API. Integration developers should use createFriggCommands() instead.
|
|
24
|
+
*
|
|
25
|
+
* @returns {Object} User command object with CRUD operations
|
|
26
|
+
*/
|
|
27
|
+
function createUserCommands() {
|
|
28
|
+
const userRepository = createUserRepository();
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
/**
|
|
32
|
+
* Create a new individual user
|
|
33
|
+
* @param {Object} params
|
|
34
|
+
* @param {string} params.username - Username (usually email)
|
|
35
|
+
* @param {string} [params.email] - Email address
|
|
36
|
+
* @param {string} [params.appUserId] - External application user ID
|
|
37
|
+
* @param {string} [params.password] - Password (optional)
|
|
38
|
+
* @returns {Promise<Object>} Created user object
|
|
39
|
+
*/
|
|
40
|
+
async createUser({ username, email, appUserId, password } = {}) {
|
|
41
|
+
try {
|
|
42
|
+
if (!username) {
|
|
43
|
+
const error = new Error('username is required');
|
|
44
|
+
error.code = 'INVALID_USER_DATA';
|
|
45
|
+
throw error;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const userData = { username };
|
|
49
|
+
if (email) userData.email = email;
|
|
50
|
+
if (appUserId) userData.appUserId = appUserId;
|
|
51
|
+
if (password) userData.password = password;
|
|
52
|
+
|
|
53
|
+
const user = await userRepository.createIndividualUser(
|
|
54
|
+
userData
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
id: user.id,
|
|
59
|
+
username: user.username,
|
|
60
|
+
email: user.email,
|
|
61
|
+
appUserId: user.appUserId,
|
|
62
|
+
};
|
|
63
|
+
} catch (error) {
|
|
64
|
+
if (error.code === 11000) {
|
|
65
|
+
// Duplicate key error
|
|
66
|
+
const duplicateError = new Error(
|
|
67
|
+
`User with username '${username}' already exists`
|
|
68
|
+
);
|
|
69
|
+
duplicateError.code = 'USER_ALREADY_EXISTS';
|
|
70
|
+
return mapErrorToResponse(duplicateError);
|
|
71
|
+
}
|
|
72
|
+
return mapErrorToResponse(error);
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Find a user by their application user ID
|
|
78
|
+
* @param {string} appUserId - External application user ID
|
|
79
|
+
* @returns {Promise<Object|null>} User object or null if not found
|
|
80
|
+
*/
|
|
81
|
+
async findUserByAppUserId(appUserId) {
|
|
82
|
+
try {
|
|
83
|
+
if (!appUserId) {
|
|
84
|
+
const error = new Error('appUserId is required');
|
|
85
|
+
error.code = 'INVALID_USER_DATA';
|
|
86
|
+
throw error;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const user = await userRepository.findIndividualUserByAppUserId(
|
|
90
|
+
appUserId
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
if (!user) {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return {
|
|
98
|
+
id: user.id,
|
|
99
|
+
username: user.username,
|
|
100
|
+
email: user.email,
|
|
101
|
+
appUserId: user.appUserId,
|
|
102
|
+
};
|
|
103
|
+
} catch (error) {
|
|
104
|
+
return mapErrorToResponse(error);
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Find a user by their username
|
|
110
|
+
* @param {string} username - Username to search for
|
|
111
|
+
* @returns {Promise<Object|null>} User object or null if not found
|
|
112
|
+
*/
|
|
113
|
+
async findUserByUsername(username) {
|
|
114
|
+
try {
|
|
115
|
+
if (!username) {
|
|
116
|
+
const error = new Error('username is required');
|
|
117
|
+
error.code = 'INVALID_USER_DATA';
|
|
118
|
+
throw error;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const user = await userRepository.findIndividualUserByUsername(
|
|
122
|
+
username
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
if (!user) {
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return {
|
|
130
|
+
id: user.id,
|
|
131
|
+
username: user.username,
|
|
132
|
+
email: user.email,
|
|
133
|
+
appUserId: user.appUserId,
|
|
134
|
+
};
|
|
135
|
+
} catch (error) {
|
|
136
|
+
return mapErrorToResponse(error);
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Find a user by their ID
|
|
142
|
+
* @param {string} userId - User ID to search for
|
|
143
|
+
* @returns {Promise<Object|null>} User object or null if not found
|
|
144
|
+
*/
|
|
145
|
+
async findUserById(userId) {
|
|
146
|
+
try {
|
|
147
|
+
if (!userId) {
|
|
148
|
+
const error = new Error('userId is required');
|
|
149
|
+
error.code = 'INVALID_USER_DATA';
|
|
150
|
+
throw error;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const user = await userRepository.findIndividualUserById(
|
|
154
|
+
userId
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
if (!user) {
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return {
|
|
162
|
+
id: user._id.toString(),
|
|
163
|
+
username: user.username,
|
|
164
|
+
email: user.email,
|
|
165
|
+
appUserId: user.appUserId,
|
|
166
|
+
};
|
|
167
|
+
} catch (error) {
|
|
168
|
+
return mapErrorToResponse(error);
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Update a user by ID
|
|
174
|
+
* @param {string} userId - User ID to update
|
|
175
|
+
* @param {Object} updates - Fields to update
|
|
176
|
+
* @returns {Promise<Object>} Updated user object
|
|
177
|
+
*/
|
|
178
|
+
async updateUser(userId, updates) {
|
|
179
|
+
try {
|
|
180
|
+
if (!userId) {
|
|
181
|
+
const error = new Error('userId is required');
|
|
182
|
+
error.code = 'INVALID_USER_DATA';
|
|
183
|
+
throw error;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const user = await userRepository.IndividualUser.update(
|
|
187
|
+
userId,
|
|
188
|
+
updates
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
if (!user) {
|
|
192
|
+
const error = new Error(`User ${userId} not found`);
|
|
193
|
+
error.code = 'USER_NOT_FOUND';
|
|
194
|
+
throw error;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return {
|
|
198
|
+
id: user._id.toString(),
|
|
199
|
+
username: user.username,
|
|
200
|
+
email: user.email,
|
|
201
|
+
appUserId: user.appUserId,
|
|
202
|
+
};
|
|
203
|
+
} catch (error) {
|
|
204
|
+
return mapErrorToResponse(error);
|
|
205
|
+
}
|
|
206
|
+
},
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
module.exports = {
|
|
211
|
+
createUserCommands,
|
|
212
|
+
ERROR_CODE_MAP,
|
|
213
|
+
};
|