@friggframework/core 2.0.0--canary.686.df38e8e.0 → 2.0.0--canary.710.9045bbc.0
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/CHANGELOG.md +0 -16
- package/CLAUDE.md +41 -29
- package/README.md +0 -105
- package/application/commands/README.md +17 -11
- package/application/commands/entity-commands.js +18 -60
- package/application/commands/integration-commands.js +73 -20
- package/application/commands/scheduler-commands.js +7 -11
- package/application/commands/user-commands.js +5 -17
- package/application/index.js +2 -6
- package/core/Worker.js +7 -31
- package/core/create-handler.js +0 -9
- package/credential/repositories/credential-repository-documentdb.js +0 -34
- package/credential/repositories/credential-repository-interface.js +0 -15
- package/credential/repositories/credential-repository-mongo.js +0 -23
- package/credential/repositories/credential-repository-postgres.js +0 -28
- package/database/config.js +24 -48
- package/database/documentdb-encryption-service.js +22 -9
- package/database/documentdb-utils.js +0 -2
- package/database/encryption/README.md +17 -81
- package/database/encryption/documentdb-encryption-service.md +1 -78
- package/database/encryption/encryption-schema-registry.js +46 -123
- package/database/encryption/field-encryption-service.js +5 -69
- package/database/encryption/integration-mapping-encryption.js +1 -1
- package/database/prisma.js +38 -15
- package/database/use-cases/check-encryption-health-use-case.js +52 -43
- package/errors/index.js +4 -0
- package/errors/mapping-already-exists-error.js +14 -0
- package/generated/prisma-mongodb/edge.js +4 -43
- package/generated/prisma-mongodb/index-browser.js +1 -40
- package/generated/prisma-mongodb/index.d.ts +111 -4739
- package/generated/prisma-mongodb/index.js +4 -43
- package/generated/prisma-mongodb/package.json +1 -1
- package/generated/prisma-mongodb/schema.prisma +0 -92
- package/generated/prisma-mongodb/wasm.js +4 -43
- package/generated/prisma-postgresql/edge.js +4 -43
- package/generated/prisma-postgresql/index-browser.js +1 -40
- package/generated/prisma-postgresql/index.d.ts +33 -4966
- package/generated/prisma-postgresql/index.js +4 -43
- package/generated/prisma-postgresql/package.json +1 -1
- package/generated/prisma-postgresql/schema.prisma +0 -87
- package/generated/prisma-postgresql/wasm.js +4 -43
- package/handlers/WEBHOOKS.md +29 -0
- package/handlers/app-definition-loader.js +19 -121
- package/handlers/app-handler-helpers.js +3 -25
- package/handlers/backend-utils.js +25 -31
- package/handlers/middleware/admin-auth.js +1 -14
- package/handlers/routers/auth.js +10 -46
- package/handlers/routers/health.js +4 -13
- package/handlers/routers/integration-defined-routers.js +92 -144
- package/handlers/routers/integration-webhook-routers.js +64 -102
- package/handlers/workers/integration-defined-workers.js +2 -5
- package/index.js +3 -65
- package/infrastructure/scheduler/eventbridge-scheduler-adapter.js +67 -13
- package/infrastructure/scheduler/index.js +6 -19
- package/infrastructure/scheduler/scheduler-service-factory.js +7 -55
- package/integrations/index.js +0 -1
- package/integrations/integration-base.js +29 -0
- package/integrations/integration-router.js +96 -131
- package/integrations/repositories/integration-mapping-repository-documentdb.js +50 -0
- package/integrations/repositories/integration-mapping-repository-interface.js +15 -0
- package/integrations/repositories/integration-mapping-repository-mongo.js +29 -0
- package/integrations/repositories/integration-mapping-repository-postgres.js +30 -0
- package/integrations/repositories/integration-mapping-repository.js +29 -0
- package/integrations/repositories/integration-repository-documentdb.js +35 -35
- package/integrations/repositories/integration-repository-interface.js +31 -17
- package/integrations/repositories/integration-repository-mongo.js +129 -60
- package/integrations/repositories/integration-repository-postgres.js +134 -62
- package/integrations/repositories/process-repository-mongo.js +1 -1
- package/integrations/tests/doubles/test-integration-repository.js +1 -12
- package/integrations/use-cases/add-entities-to-integration.js +92 -0
- package/integrations/use-cases/create-integration.js +75 -72
- package/integrations/use-cases/get-integrations-for-user.js +40 -80
- package/integrations/use-cases/index.js +1 -7
- package/integrations/use-cases/remove-entities-from-integration.js +70 -0
- package/integrations/utils/assert-entity-ids-input.js +42 -0
- package/integrations/utils/map-integration-dto.js +14 -43
- package/jest-setup.js +13 -2
- package/jest-teardown.js +5 -2
- package/jest.config.js +4 -0
- package/jest.db.config.js +23 -0
- package/modules/index.js +0 -14
- package/modules/module-factory.js +0 -12
- package/modules/module.js +2 -53
- package/modules/repositories/module-repository-documentdb.js +6 -51
- package/modules/repositories/module-repository-interface.js +2 -19
- package/modules/repositories/module-repository-mongo.js +74 -67
- package/modules/repositories/module-repository-postgres.js +74 -66
- package/modules/repositories/module-repository.js +1 -2
- package/modules/requester/requester.js +2 -27
- package/modules/use-cases/get-entities-for-user.js +13 -26
- package/modules/use-cases/get-entity-options-by-id.js +4 -10
- package/modules/use-cases/get-entity-options-by-type.js +1 -1
- package/modules/use-cases/get-module-instance-from-type.js +1 -1
- package/modules/use-cases/get-module.js +4 -10
- package/modules/use-cases/process-authorization-callback.js +19 -53
- package/modules/use-cases/refresh-entity-options.js +4 -10
- package/modules/use-cases/test-module-auth.js +4 -10
- package/modules/utils/map-module-dto.js +5 -3
- package/modules/utils/to-credential-id.js +14 -0
- package/package.json +7 -6
- package/prisma-mongodb/schema.prisma +0 -92
- package/prisma-postgresql/schema.prisma +0 -87
- package/queues/index.js +0 -25
- package/queues/queuer-util.js +6 -5
- package/types/index.d.ts +0 -2
- package/user/repositories/user-repository-documentdb.js +2 -27
- package/user/repositories/user-repository-interface.js +0 -18
- package/user/repositories/user-repository-mongo.js +0 -24
- package/user/repositories/user-repository-postgres.js +0 -18
- package/user/tests/doubles/test-user-repository.js +1 -1
- package/user/use-cases/authenticate-user.js +1 -7
- package/user/use-cases/get-user-from-adopter-jwt.js +124 -95
- package/user/use-cases/get-user-from-bearer-token.js +7 -2
- package/user/use-cases/get-user-from-x-frigg-headers.js +80 -83
- package/user/user.js +1 -64
- package/credential/use-cases/credential-operations-for-user.js +0 -202
- package/credential/use-cases/reauthorize-credential-for-user.js +0 -73
- package/database/encryption/encryption-config.js +0 -219
- package/database/encryption/test-support/fake-ciphertext.js +0 -15
- package/database/organization-where.js +0 -18
- package/database/unique-violation.js +0 -25
- package/extensions/bootstrap-runner.js +0 -43
- package/extensions/extension-loader.js +0 -187
- package/extensions/index.js +0 -41
- package/extensions/initialize-app.js +0 -123
- package/extensions/route-mounter.js +0 -103
- package/extensions/schema-composer.js +0 -188
- package/infrastructure/scheduler/netlify-scheduler-adapter.js +0 -220
- package/infrastructure/scheduler/scheduled-job-repository.js +0 -118
- package/integrations/repositories/integration-not-found.js +0 -14
- package/integrations/schedule-config.js +0 -80
- package/integrations/use-cases/dispatch-ongoing-syncs.js +0 -100
- package/integrations/use-cases/integration-operations-for-user.js +0 -256
- package/integrations/use-cases/resolve-integration-ownership.js +0 -177
- package/management-api/create-management-api-router.js +0 -49
- package/management-api/error-response.js +0 -116
- package/management-api/management-api-config.js +0 -151
- package/management-api/meta-router.js +0 -76
- package/management-api/openapi/build-openapi.js +0 -144
- package/management-api/openapi/components.js +0 -408
- package/management-api/openapi/v1-operations.js +0 -83
- package/management-api/openapi/v2-operations.js +0 -264
- package/management-api/route-registry.js +0 -411
- package/management-api/test-support/http-client.js +0 -39
- package/management-api/use-cases/get-management-api-meta.js +0 -57
- package/management-api/v1-deprecation.js +0 -120
- package/management-api/v2/create-v2-router.js +0 -85
- package/management-api/v2/dependencies.js +0 -281
- package/management-api/v2/dto.js +0 -143
- package/management-api/v2/handlers/authorization.js +0 -94
- package/management-api/v2/handlers/credentials.js +0 -33
- package/management-api/v2/handlers/entities.js +0 -77
- package/management-api/v2/handlers/integrations.js +0 -128
- package/management-api/v2/handlers/proxy.js +0 -34
- package/management-api/v2/request.js +0 -85
- package/management-api/v2/test-support/fake-dependencies.js +0 -103
- package/modules/domain/authorization-session.js +0 -144
- package/modules/entity-scope.js +0 -15
- package/modules/entity-type-registry.js +0 -311
- package/modules/proxy/proxy-errors.js +0 -20
- package/modules/proxy/proxy-path.js +0 -179
- package/modules/proxy/proxy-policy.js +0 -248
- package/modules/repositories/authorization-session-repository-documentdb.js +0 -112
- package/modules/repositories/authorization-session-repository-factory.js +0 -31
- package/modules/repositories/authorization-session-repository-interface.js +0 -47
- package/modules/repositories/authorization-session-repository-prisma.js +0 -87
- package/modules/repositories/entity-not-found.js +0 -18
- package/modules/requester/proxy-request.js +0 -244
- package/modules/scope-config.js +0 -192
- package/modules/scope-policy.js +0 -112
- package/modules/scope-resolution.js +0 -288
- package/modules/scope-visibility.js +0 -247
- package/modules/tests/doubles/in-memory-authorization-session-repository.js +0 -45
- package/modules/tests/doubles/ownership-matching.js +0 -18
- package/modules/use-cases/authorize-entity-for-user.js +0 -61
- package/modules/use-cases/create-entity-from-credential.js +0 -113
- package/modules/use-cases/entity-operations-for-user.js +0 -162
- package/modules/use-cases/entity-types.js +0 -139
- package/modules/use-cases/execute-entity-proxy-request.js +0 -286
- package/modules/use-cases/get-authorization-requirements.js +0 -100
- package/modules/use-cases/get-entity-module-for-user.js +0 -113
- package/modules/use-cases/manage-access.js +0 -21
- package/modules/use-cases/multi-step-authorization.js +0 -329
- package/prisma-postgresql/migrations/20261003000000_add_authorization_session/migration.sql +0 -28
- package/prisma-postgresql/migrations/20261015000000_add_organization_membership/migration.sql +0 -26
- package/prisma-postgresql/migrations/20261016000000_add_entity_integration_organization/migration.sql +0 -25
- package/prisma-postgresql/migrations/20261017000000_add_scheduled_job/migration.sql +0 -28
- package/providers/index.js +0 -23
- package/providers/resolve-provider.js +0 -93
- package/providers/runtime-detection.js +0 -124
- package/queues/providers/index.js +0 -11
- package/queues/providers/netlify-background-provider.js +0 -187
- package/queues/providers/qstash-queue-provider.js +0 -219
- package/queues/providers/sqs-queue-provider.js +0 -70
- package/queues/queue-client-interface.js +0 -55
- package/queues/queue-message-auth.js +0 -193
- package/queues/queue-provider-factory.js +0 -101
- package/queues/queue-provider.js +0 -65
- package/queues/sqs-queue-client.js +0 -81
- package/types/modules/index.d.ts +0 -197
- package/types/user/index.d.ts +0 -134
- package/user/adopter-jwt-config.js +0 -177
- package/user/adopter-jwt-errors.js +0 -59
- package/user/adopter-token-verifier.js +0 -122
- package/user/app-user-helpers.js +0 -97
- package/user/create-user-authentication.js +0 -64
- package/user/membership.js +0 -51
- package/user/repositories/organization-membership-record.js +0 -19
- package/user/repositories/organization-membership-repository-documentdb.js +0 -106
- package/user/repositories/organization-membership-repository-factory.js +0 -42
- package/user/repositories/organization-membership-repository-interface.js +0 -57
- package/user/repositories/organization-membership-repository-mongo.js +0 -13
- package/user/repositories/organization-membership-repository-postgres.js +0 -29
- package/user/repositories/organization-membership-repository-prisma.js +0 -81
- package/user/tests/doubles/adopter-jwt-fixtures.js +0 -220
- package/user/use-cases/ensure-organization-membership.js +0 -92
- package/user/use-cases/find-individual-users-by-organization.js +0 -54
|
@@ -14,7 +14,6 @@ const {
|
|
|
14
14
|
IntegrationRepositoryInterface,
|
|
15
15
|
} = require('./integration-repository-interface');
|
|
16
16
|
const { validateConfigPatch } = require('./config-patch-shared');
|
|
17
|
-
const { integrationNotFoundError } = require('./integration-not-found');
|
|
18
17
|
|
|
19
18
|
class IntegrationRepositoryDocumentDB extends IntegrationRepositoryInterface {
|
|
20
19
|
constructor() {
|
|
@@ -29,28 +28,6 @@ class IntegrationRepositoryDocumentDB extends IntegrationRepositoryInterface {
|
|
|
29
28
|
return records.map((doc) => this._mapIntegration(doc));
|
|
30
29
|
}
|
|
31
30
|
|
|
32
|
-
/**
|
|
33
|
-
* Find integrations matching any of the owner/organization pairs, in one
|
|
34
|
-
* query. `organizationId: null` matches rows with no organization (a raw
|
|
35
|
-
* MongoDB `null` also matches a missing field). The caller (a use case)
|
|
36
|
-
* decides which pairs; this only runs them.
|
|
37
|
-
*
|
|
38
|
-
* @param {Array<{userId: string, organizationId: (string|null)}>} clauses
|
|
39
|
-
* @returns {Promise<Array>}
|
|
40
|
-
*/
|
|
41
|
-
async findIntegrationsByOwnership(clauses) {
|
|
42
|
-
if (!Array.isArray(clauses) || clauses.length === 0) return [];
|
|
43
|
-
// Drained: a list can hold more than the first cursor batch (~101).
|
|
44
|
-
const records = await findManyDrained(this.prisma, 'Integration', {
|
|
45
|
-
$or: clauses.map(({ userId, organizationId }) => ({
|
|
46
|
-
userId: toObjectId(userId),
|
|
47
|
-
organizationId:
|
|
48
|
-
organizationId === null ? null : toObjectId(organizationId),
|
|
49
|
-
})),
|
|
50
|
-
});
|
|
51
|
-
return records.map((doc) => this._mapIntegration(doc));
|
|
52
|
-
}
|
|
53
|
-
|
|
54
31
|
async findIntegrations({ type, status } = {}) {
|
|
55
32
|
const filter = {};
|
|
56
33
|
if (type) {
|
|
@@ -91,11 +68,11 @@ class IntegrationRepositoryDocumentDB extends IntegrationRepositoryInterface {
|
|
|
91
68
|
async findIntegrationById(id) {
|
|
92
69
|
const objectId = toObjectId(id);
|
|
93
70
|
if (!objectId) {
|
|
94
|
-
throw
|
|
71
|
+
throw new Error(`Integration with id ${id} not found`);
|
|
95
72
|
}
|
|
96
73
|
const doc = await findOne(this.prisma, 'Integration', { _id: objectId });
|
|
97
74
|
if (!doc) {
|
|
98
|
-
throw
|
|
75
|
+
throw new Error(`Integration with id ${id} not found`);
|
|
99
76
|
}
|
|
100
77
|
return this._mapIntegration(doc);
|
|
101
78
|
}
|
|
@@ -155,18 +132,10 @@ class IntegrationRepositoryDocumentDB extends IntegrationRepositoryInterface {
|
|
|
155
132
|
return true;
|
|
156
133
|
}
|
|
157
134
|
|
|
158
|
-
|
|
159
|
-
* @param {Array<string>} entities - entity ids
|
|
160
|
-
* @param {string} userId - owner (the person, or the organization's user)
|
|
161
|
-
* @param {Object} config - Integration configuration
|
|
162
|
-
* @param {{organizationId?: (string|null)}} [tenancy] - the organization the
|
|
163
|
-
* integration is created in (ADR-056 §3.6)
|
|
164
|
-
*/
|
|
165
|
-
async createIntegration(entities, userId, config, tenancy = {}) {
|
|
135
|
+
async createIntegration(entities, userId, config) {
|
|
166
136
|
const now = new Date();
|
|
167
137
|
const document = {
|
|
168
138
|
userId: toObjectId(userId) || null,
|
|
169
|
-
organizationId: toObjectId(tenancy.organizationId) || null,
|
|
170
139
|
config,
|
|
171
140
|
version: '0.0.0',
|
|
172
141
|
status: 'IN_CREATION',
|
|
@@ -195,6 +164,38 @@ class IntegrationRepositoryDocumentDB extends IntegrationRepositoryInterface {
|
|
|
195
164
|
return this._mapIntegration(created);
|
|
196
165
|
}
|
|
197
166
|
|
|
167
|
+
async addEntitiesToIntegration(integrationId, entityIds) {
|
|
168
|
+
const objectId = toObjectId(integrationId);
|
|
169
|
+
if (!objectId) return null;
|
|
170
|
+
const result = await updateOne(
|
|
171
|
+
this.prisma,
|
|
172
|
+
'Integration',
|
|
173
|
+
{ _id: objectId },
|
|
174
|
+
{
|
|
175
|
+
$addToSet: { entityIds: { $each: toObjectIdArray(entityIds) } },
|
|
176
|
+
$set: { updatedAt: { $date: new Date().toISOString() } },
|
|
177
|
+
}
|
|
178
|
+
);
|
|
179
|
+
if (!result?.n) return null;
|
|
180
|
+
return this.findIntegrationById(integrationId);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
async removeEntitiesFromIntegration(integrationId, entityIds) {
|
|
184
|
+
const objectId = toObjectId(integrationId);
|
|
185
|
+
if (!objectId) return null;
|
|
186
|
+
const result = await updateOne(
|
|
187
|
+
this.prisma,
|
|
188
|
+
'Integration',
|
|
189
|
+
{ _id: objectId },
|
|
190
|
+
{
|
|
191
|
+
$pullAll: { entityIds: toObjectIdArray(entityIds) },
|
|
192
|
+
$set: { updatedAt: { $date: new Date().toISOString() } },
|
|
193
|
+
}
|
|
194
|
+
);
|
|
195
|
+
if (!result?.n) return null;
|
|
196
|
+
return this.findIntegrationById(integrationId);
|
|
197
|
+
}
|
|
198
|
+
|
|
198
199
|
async findIntegrationByUserId(userId) {
|
|
199
200
|
const objectId = toObjectId(userId);
|
|
200
201
|
if (!objectId) return null;
|
|
@@ -329,7 +330,6 @@ class IntegrationRepositoryDocumentDB extends IntegrationRepositoryInterface {
|
|
|
329
330
|
id: fromObjectId(doc?._id),
|
|
330
331
|
entitiesIds: (doc?.entityIds || []).map((value) => fromObjectId(value)),
|
|
331
332
|
userId: fromObjectId(doc?.userId),
|
|
332
|
-
organizationId: fromObjectId(doc?.organizationId) ?? null,
|
|
333
333
|
config: doc?.config ?? null,
|
|
334
334
|
version: doc?.version ?? null,
|
|
335
335
|
status: doc?.status ?? null,
|
|
@@ -10,20 +10,6 @@
|
|
|
10
10
|
* @abstract
|
|
11
11
|
*/
|
|
12
12
|
class IntegrationRepositoryInterface {
|
|
13
|
-
/**
|
|
14
|
-
* Find integrations matching any of the owner/organization pairs, in one
|
|
15
|
-
* query (ADR-056 §3.6). `organizationId: null` matches rows with no
|
|
16
|
-
* organization. Which pairs is decided by the caller
|
|
17
|
-
* (modules/entity-scope.js), not here.
|
|
18
|
-
*
|
|
19
|
-
* @param {Array<{userId: string|number, organizationId: (string|number|null)}>} clauses
|
|
20
|
-
* @returns {Promise<Array>} Array of integration objects
|
|
21
|
-
* @abstract
|
|
22
|
-
*/
|
|
23
|
-
async findIntegrationsByOwnership(clauses) {
|
|
24
|
-
throw new Error('Method findIntegrationsByOwnership must be implemented by subclass');
|
|
25
|
-
}
|
|
26
|
-
|
|
27
13
|
/**
|
|
28
14
|
* Find all integrations for a user
|
|
29
15
|
*
|
|
@@ -137,12 +123,10 @@ class IntegrationRepositoryInterface {
|
|
|
137
123
|
* @param {Array<string|number>} entities - Array of entity IDs
|
|
138
124
|
* @param {string|number} userId - User ID
|
|
139
125
|
* @param {Object} config - Integration configuration
|
|
140
|
-
* @param {{organizationId?: (string|number|null)}} [tenancy] - the organization the
|
|
141
|
-
* integration is created in (ADR-056 §3.6)
|
|
142
126
|
* @returns {Promise<Object>} Created integration object
|
|
143
127
|
* @abstract
|
|
144
128
|
*/
|
|
145
|
-
async createIntegration(entities, userId, config
|
|
129
|
+
async createIntegration(entities, userId, config) {
|
|
146
130
|
throw new Error('Method createIntegration must be implemented by subclass');
|
|
147
131
|
}
|
|
148
132
|
|
|
@@ -200,6 +184,36 @@ class IntegrationRepositoryInterface {
|
|
|
200
184
|
'Method findIntegrationsByEntityId must be implemented by subclass'
|
|
201
185
|
);
|
|
202
186
|
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Attach entities to an existing integration. Entities that are already
|
|
190
|
+
* attached are left as they are.
|
|
191
|
+
*
|
|
192
|
+
* @param {string|number} integrationId - Integration ID
|
|
193
|
+
* @param {Array<string|number>} entityIds - Entity IDs to attach
|
|
194
|
+
* @returns {Promise<Object|null>} Updated integration, or null when the integration does not exist
|
|
195
|
+
* @abstract
|
|
196
|
+
*/
|
|
197
|
+
async addEntitiesToIntegration(integrationId, entityIds) {
|
|
198
|
+
throw new Error(
|
|
199
|
+
'Method addEntitiesToIntegration must be implemented by subclass'
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Detach entities from an integration. Entities that are not attached
|
|
205
|
+
* are ignored; the entities themselves are not deleted.
|
|
206
|
+
*
|
|
207
|
+
* @param {string|number} integrationId - Integration ID
|
|
208
|
+
* @param {Array<string|number>} entityIds - Entity IDs to detach
|
|
209
|
+
* @returns {Promise<Object|null>} Updated integration, or null when the integration does not exist
|
|
210
|
+
* @abstract
|
|
211
|
+
*/
|
|
212
|
+
async removeEntitiesFromIntegration(integrationId, entityIds) {
|
|
213
|
+
throw new Error(
|
|
214
|
+
'Method removeEntitiesFromIntegration must be implemented by subclass'
|
|
215
|
+
);
|
|
216
|
+
}
|
|
203
217
|
}
|
|
204
218
|
|
|
205
219
|
module.exports = { IntegrationRepositoryInterface };
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
const { prisma } = require('../../database/prisma');
|
|
2
|
-
const { organizationWhere } = require('../../database/organization-where');
|
|
3
2
|
const {
|
|
4
3
|
IntegrationRepositoryInterface,
|
|
5
4
|
} = require('./integration-repository-interface');
|
|
6
5
|
const { validateConfigPatch } = require('./config-patch-shared');
|
|
7
|
-
const { integrationNotFoundError } = require('./integration-not-found');
|
|
8
6
|
|
|
9
7
|
/**
|
|
10
8
|
* MongoDB Integration Repository Adapter
|
|
@@ -27,25 +25,6 @@ class IntegrationRepositoryMongo extends IntegrationRepositoryInterface {
|
|
|
27
25
|
this.prisma = prisma;
|
|
28
26
|
}
|
|
29
27
|
|
|
30
|
-
/**
|
|
31
|
-
* The one place a integration integration row becomes the record the
|
|
32
|
-
* application sees (ids of its entities, its organization).
|
|
33
|
-
* @private
|
|
34
|
-
*/
|
|
35
|
-
_mapIntegration(integration) {
|
|
36
|
-
return {
|
|
37
|
-
id: integration.id,
|
|
38
|
-
entitiesIds: integration.entities.map((e) => e.id),
|
|
39
|
-
userId: integration.userId,
|
|
40
|
-
organizationId: integration.organizationId ?? null,
|
|
41
|
-
config: integration.config,
|
|
42
|
-
version: integration.version,
|
|
43
|
-
status: integration.status,
|
|
44
|
-
messages: integration.messages,
|
|
45
|
-
createdAt: integration.createdAt,
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
|
|
49
28
|
/**
|
|
50
29
|
* Find all integrations for a user
|
|
51
30
|
* Replaces: IntegrationModel.find({ user: userId }).populate('entities')
|
|
@@ -62,32 +41,16 @@ class IntegrationRepositoryMongo extends IntegrationRepositoryInterface {
|
|
|
62
41
|
});
|
|
63
42
|
|
|
64
43
|
// Map to domain objects (maintains same API)
|
|
65
|
-
return integrations.map((integration) => (
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
*/
|
|
76
|
-
async findIntegrationsByOwnership(clauses) {
|
|
77
|
-
if (!Array.isArray(clauses) || clauses.length === 0) return [];
|
|
78
|
-
const integrations = await this.prisma.integration.findMany({
|
|
79
|
-
where: {
|
|
80
|
-
OR: clauses.map(({ userId, organizationId }) => ({
|
|
81
|
-
userId,
|
|
82
|
-
...organizationWhere(organizationId),
|
|
83
|
-
})),
|
|
84
|
-
},
|
|
85
|
-
include: {
|
|
86
|
-
entities: true,
|
|
87
|
-
},
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
return integrations.map((integration) => (this._mapIntegration(integration)));
|
|
44
|
+
return integrations.map((integration) => ({
|
|
45
|
+
id: integration.id,
|
|
46
|
+
entitiesIds: integration.entities.map((e) => e.id),
|
|
47
|
+
userId: integration.userId,
|
|
48
|
+
config: integration.config,
|
|
49
|
+
version: integration.version,
|
|
50
|
+
status: integration.status,
|
|
51
|
+
messages: integration.messages,
|
|
52
|
+
createdAt: integration.createdAt,
|
|
53
|
+
}));
|
|
91
54
|
}
|
|
92
55
|
|
|
93
56
|
/**
|
|
@@ -114,7 +77,16 @@ class IntegrationRepositoryMongo extends IntegrationRepositoryInterface {
|
|
|
114
77
|
},
|
|
115
78
|
});
|
|
116
79
|
|
|
117
|
-
return integrations.map((integration) => (
|
|
80
|
+
return integrations.map((integration) => ({
|
|
81
|
+
id: integration.id,
|
|
82
|
+
entitiesIds: integration.entities.map((e) => e.id),
|
|
83
|
+
userId: integration.userId,
|
|
84
|
+
config: integration.config,
|
|
85
|
+
version: integration.version,
|
|
86
|
+
status: integration.status,
|
|
87
|
+
messages: integration.messages,
|
|
88
|
+
createdAt: integration.createdAt,
|
|
89
|
+
}));
|
|
118
90
|
}
|
|
119
91
|
|
|
120
92
|
/**
|
|
@@ -193,7 +165,15 @@ class IntegrationRepositoryMongo extends IntegrationRepositoryInterface {
|
|
|
193
165
|
throw new Error(`Integration with name ${name} not found`);
|
|
194
166
|
}
|
|
195
167
|
|
|
196
|
-
return
|
|
168
|
+
return {
|
|
169
|
+
id: integration.id,
|
|
170
|
+
entitiesIds: integration.entities.map((e) => e.id),
|
|
171
|
+
userId: integration.userId,
|
|
172
|
+
config: integration.config,
|
|
173
|
+
version: integration.version,
|
|
174
|
+
status: integration.status,
|
|
175
|
+
messages: integration.messages,
|
|
176
|
+
};
|
|
197
177
|
}
|
|
198
178
|
|
|
199
179
|
/**
|
|
@@ -212,10 +192,18 @@ class IntegrationRepositoryMongo extends IntegrationRepositoryInterface {
|
|
|
212
192
|
});
|
|
213
193
|
|
|
214
194
|
if (!integration) {
|
|
215
|
-
throw
|
|
195
|
+
throw new Error(`Integration with id ${id} not found`);
|
|
216
196
|
}
|
|
217
197
|
|
|
218
|
-
return
|
|
198
|
+
return {
|
|
199
|
+
id: integration.id,
|
|
200
|
+
entitiesIds: integration.entities.map((e) => e.id),
|
|
201
|
+
userId: integration.userId,
|
|
202
|
+
config: integration.config,
|
|
203
|
+
version: integration.version,
|
|
204
|
+
status: integration.status,
|
|
205
|
+
messages: integration.messages,
|
|
206
|
+
};
|
|
219
207
|
}
|
|
220
208
|
|
|
221
209
|
/**
|
|
@@ -302,7 +290,15 @@ class IntegrationRepositoryMongo extends IntegrationRepositoryInterface {
|
|
|
302
290
|
},
|
|
303
291
|
});
|
|
304
292
|
|
|
305
|
-
return integrations.map((integration) => (
|
|
293
|
+
return integrations.map((integration) => ({
|
|
294
|
+
id: integration.id,
|
|
295
|
+
entitiesIds: integration.entities.map((e) => e.id),
|
|
296
|
+
userId: integration.userId,
|
|
297
|
+
config: integration.config,
|
|
298
|
+
version: integration.version,
|
|
299
|
+
status: integration.status,
|
|
300
|
+
messages: integration.messages,
|
|
301
|
+
}));
|
|
306
302
|
}
|
|
307
303
|
|
|
308
304
|
/**
|
|
@@ -314,16 +310,13 @@ class IntegrationRepositoryMongo extends IntegrationRepositoryInterface {
|
|
|
314
310
|
* @param {Array<string>} entities - Array of entity IDs (MongoDB ObjectIds)
|
|
315
311
|
* @param {string} userId - User ID (MongoDB ObjectId)
|
|
316
312
|
* @param {Object} config - Integration configuration
|
|
317
|
-
* @param {{organizationId?: (string|null)}} [tenancy] - the organization the
|
|
318
|
-
* integration is created in (ADR-056 §3.6)
|
|
319
313
|
* @returns {Promise<Object>} Created integration object
|
|
320
314
|
*/
|
|
321
|
-
async createIntegration(entities, userId, config
|
|
315
|
+
async createIntegration(entities, userId, config) {
|
|
322
316
|
const data = {
|
|
323
317
|
config,
|
|
324
318
|
version: '0.0.0',
|
|
325
319
|
userId: userId,
|
|
326
|
-
organizationId: tenancy.organizationId ?? null,
|
|
327
320
|
entityIds: entities,
|
|
328
321
|
};
|
|
329
322
|
|
|
@@ -334,7 +327,67 @@ class IntegrationRepositoryMongo extends IntegrationRepositoryInterface {
|
|
|
334
327
|
},
|
|
335
328
|
});
|
|
336
329
|
|
|
337
|
-
return
|
|
330
|
+
return {
|
|
331
|
+
id: integration.id,
|
|
332
|
+
entitiesIds: integration.entities.map((e) => e.id),
|
|
333
|
+
userId: integration.userId,
|
|
334
|
+
config: integration.config,
|
|
335
|
+
version: integration.version,
|
|
336
|
+
status: integration.status,
|
|
337
|
+
messages: integration.messages,
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Attach entities to an existing integration. Entities already attached
|
|
343
|
+
* are left as they are.
|
|
344
|
+
*
|
|
345
|
+
* @param {string} integrationId - Integration ID
|
|
346
|
+
* @param {Array<string>} entityIds - Entity IDs to attach
|
|
347
|
+
* @returns {Promise<Object|null>} Updated integration, or null when the integration does not exist
|
|
348
|
+
*/
|
|
349
|
+
async addEntitiesToIntegration(integrationId, entityIds) {
|
|
350
|
+
|
|
351
|
+
try {
|
|
352
|
+
await this.prisma.integration.update({
|
|
353
|
+
where: { id: integrationId },
|
|
354
|
+
data: {
|
|
355
|
+
entities: {
|
|
356
|
+
connect: entityIds.map((id) => ({ id })),
|
|
357
|
+
},
|
|
358
|
+
},
|
|
359
|
+
});
|
|
360
|
+
} catch (error) {
|
|
361
|
+
if (error.code === 'P2025') return null;
|
|
362
|
+
throw error;
|
|
363
|
+
}
|
|
364
|
+
return this.findIntegrationById(integrationId);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* Detach entities from an integration. Entities that are not attached
|
|
369
|
+
* are ignored. The entities themselves are not deleted.
|
|
370
|
+
*
|
|
371
|
+
* @param {string} integrationId - Integration ID
|
|
372
|
+
* @param {Array<string>} entityIds - Entity IDs to detach
|
|
373
|
+
* @returns {Promise<Object|null>} Updated integration, or null when the integration does not exist
|
|
374
|
+
*/
|
|
375
|
+
async removeEntitiesFromIntegration(integrationId, entityIds) {
|
|
376
|
+
|
|
377
|
+
try {
|
|
378
|
+
await this.prisma.integration.update({
|
|
379
|
+
where: { id: integrationId },
|
|
380
|
+
data: {
|
|
381
|
+
entities: {
|
|
382
|
+
disconnect: entityIds.map((id) => ({ id })),
|
|
383
|
+
},
|
|
384
|
+
},
|
|
385
|
+
});
|
|
386
|
+
} catch (error) {
|
|
387
|
+
if (error.code === 'P2025') return null;
|
|
388
|
+
throw error;
|
|
389
|
+
}
|
|
390
|
+
return this.findIntegrationById(integrationId);
|
|
338
391
|
}
|
|
339
392
|
|
|
340
393
|
/**
|
|
@@ -356,7 +409,15 @@ class IntegrationRepositoryMongo extends IntegrationRepositoryInterface {
|
|
|
356
409
|
return null;
|
|
357
410
|
}
|
|
358
411
|
|
|
359
|
-
return
|
|
412
|
+
return {
|
|
413
|
+
id: integration.id,
|
|
414
|
+
entitiesIds: integration.entities.map((e) => e.id),
|
|
415
|
+
userId: integration.userId,
|
|
416
|
+
config: integration.config,
|
|
417
|
+
version: integration.version,
|
|
418
|
+
status: integration.status,
|
|
419
|
+
messages: integration.messages,
|
|
420
|
+
};
|
|
360
421
|
}
|
|
361
422
|
|
|
362
423
|
/**
|
|
@@ -380,7 +441,15 @@ class IntegrationRepositoryMongo extends IntegrationRepositoryInterface {
|
|
|
380
441
|
},
|
|
381
442
|
});
|
|
382
443
|
|
|
383
|
-
return
|
|
444
|
+
return {
|
|
445
|
+
id: integration.id,
|
|
446
|
+
entitiesIds: integration.entities.map((e) => e.id),
|
|
447
|
+
userId: integration.userId,
|
|
448
|
+
config: integration.config,
|
|
449
|
+
version: integration.version,
|
|
450
|
+
status: integration.status,
|
|
451
|
+
messages: integration.messages,
|
|
452
|
+
};
|
|
384
453
|
}
|
|
385
454
|
|
|
386
455
|
/**
|
|
@@ -402,7 +471,7 @@ class IntegrationRepositoryMongo extends IntegrationRepositoryInterface {
|
|
|
402
471
|
for (const [key, value] of Object.entries(patch)) {
|
|
403
472
|
$set[`config.${key}`] = value;
|
|
404
473
|
}
|
|
405
|
-
$set.updatedAt = new Date();
|
|
474
|
+
$set.updatedAt = { $date: new Date().toISOString() };
|
|
406
475
|
|
|
407
476
|
const result = await this.prisma.$runCommandRaw({
|
|
408
477
|
findAndModify: 'Integration',
|