@friggframework/core 2.0.0--canary.690.08cd07e.0 → 2.0.0--canary.693.d8859c2.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/CLAUDE.md +0 -28
- package/README.md +0 -105
- package/application/commands/integration-commands.js +10 -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-utils.js +0 -2
- package/database/encryption/encryption-schema-registry.js +12 -93
- package/database/prisma.js +2 -4
- package/errors/index.js +4 -0
- package/errors/mapping-already-exists-error.js +14 -0
- package/generated/prisma-mongodb/edge.js +38 -33
- package/generated/prisma-mongodb/index-browser.js +35 -30
- package/generated/prisma-mongodb/index.d.ts +1880 -3191
- package/generated/prisma-mongodb/index.js +38 -33
- package/generated/prisma-mongodb/package.json +1 -1
- package/generated/prisma-mongodb/schema.prisma +50 -86
- package/generated/prisma-mongodb/wasm.js +38 -33
- package/generated/prisma-postgresql/edge.js +38 -33
- package/generated/prisma-postgresql/index-browser.js +35 -30
- package/generated/prisma-postgresql/index.d.ts +2050 -3390
- package/generated/prisma-postgresql/index.js +38 -33
- package/generated/prisma-postgresql/package.json +1 -1
- package/generated/prisma-postgresql/schema.prisma +48 -81
- package/generated/prisma-postgresql/wasm.js +38 -33
- package/handlers/WEBHOOKS.md +29 -0
- package/handlers/app-definition-loader.js +19 -128
- 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/index.js +6 -19
- package/infrastructure/scheduler/scheduler-service-factory.js +7 -55
- package/integrations/data-defined/README.md +169 -0
- package/integrations/data-defined/canonical.js +11 -0
- package/integrations/data-defined/capabilities/acmeerp.capabilities.json +608 -0
- package/integrations/data-defined/capabilities/asana.capabilities.json +265 -0
- package/integrations/data-defined/capabilities/calendly.capabilities.json +385 -0
- package/integrations/data-defined/capabilities/docusign.capabilities.json +314 -0
- package/integrations/data-defined/capabilities/fathom.capabilities.json +228 -0
- package/integrations/data-defined/capabilities/github.capabilities.json +563 -0
- package/integrations/data-defined/capabilities/hubspot.capabilities.json +715 -0
- package/integrations/data-defined/capabilities/jira.capabilities.json +376 -0
- package/integrations/data-defined/capabilities/klaviyo.capabilities.json +475 -0
- package/integrations/data-defined/capabilities/linear.capabilities.json +324 -0
- package/integrations/data-defined/capabilities/netsuite.capabilities.json +491 -0
- package/integrations/data-defined/capabilities/notion.capabilities.json +208 -0
- package/integrations/data-defined/capabilities/phone.capabilities.json +470 -0
- package/integrations/data-defined/capabilities/pipedrive.capabilities.json +388 -0
- package/integrations/data-defined/capabilities/quickbooks.capabilities.json +615 -0
- package/integrations/data-defined/capabilities/salesforce.capabilities.json +507 -0
- package/integrations/data-defined/capabilities/shopify.capabilities.json +1082 -0
- package/integrations/data-defined/capabilities/slack.capabilities.json +363 -0
- package/integrations/data-defined/capabilities/stripe.capabilities.json +580 -0
- package/integrations/data-defined/capabilities/webflow.capabilities.json +196 -0
- package/integrations/data-defined/capabilities/zendesk.capabilities.json +409 -0
- package/integrations/data-defined/compile.js +77 -0
- package/integrations/data-defined/config-form.js +110 -0
- package/integrations/data-defined/data-defined-integration.js +366 -0
- package/integrations/data-defined/examples/acme-erp-quickbooks.definition.json +467 -0
- package/integrations/data-defined/examples/asana-slack.definition.json +220 -0
- package/integrations/data-defined/examples/calendly-salesforce.definition.json +459 -0
- package/integrations/data-defined/examples/docusign-salesforce.definition.json +305 -0
- package/integrations/data-defined/examples/fathom-hubspot-notion.definition.json +575 -0
- package/integrations/data-defined/examples/github-linear.definition.json +316 -0
- package/integrations/data-defined/examples/hubspot-slack.definition.json +409 -0
- package/integrations/data-defined/examples/klaviyo-hubspot.definition.json +339 -0
- package/integrations/data-defined/examples/phone-hubspot.definition.json +637 -0
- package/integrations/data-defined/examples/pipedrive-hubspot.definition.json +136 -0
- package/integrations/data-defined/examples/pipedrive-phone.definition.json +340 -0
- package/integrations/data-defined/examples/records/phone-hubspot.definition-version.json +652 -0
- package/integrations/data-defined/examples/records/phone-hubspot.integration.json +86 -0
- package/integrations/data-defined/examples/records/phone-hubspot.mappings.json +39 -0
- package/integrations/data-defined/examples/records/phone-hubspot.workflow-run.json +62 -0
- package/integrations/data-defined/examples/shopify-klaviyo.definition.json +488 -0
- package/integrations/data-defined/examples/shopify-netsuite.definition.json +622 -0
- package/integrations/data-defined/examples/stripe-quickbooks.definition.json +476 -0
- package/integrations/data-defined/examples/webflow-hubspot-slack.definition.json +262 -0
- package/integrations/data-defined/examples/zendesk-jira.definition.json +604 -0
- package/integrations/data-defined/expressions.js +225 -0
- package/integrations/data-defined/index.js +39 -0
- package/integrations/data-defined/interpreter.js +89 -0
- package/integrations/data-defined/manifests.js +125 -0
- package/integrations/data-defined/repositories/definition-version-repository.js +67 -0
- package/integrations/data-defined/repositories/workflow-run-repository.js +59 -0
- package/integrations/data-defined/router.js +97 -0
- package/integrations/data-defined/runtime.js +208 -0
- package/integrations/data-defined/schema/capability-manifest.v0.1.schema.json +377 -0
- package/integrations/data-defined/schema/installation.v0.1.schema.json +83 -0
- package/integrations/data-defined/schema/integration-definition.v0.1.schema.json +848 -0
- package/integrations/data-defined/schema/records.md +98 -0
- package/integrations/data-defined/schema-check.js +58 -0
- package/integrations/data-defined/scripts/validate.js +66 -0
- package/integrations/data-defined/steps.js +89 -0
- package/integrations/data-defined/transforms.js +91 -0
- package/integrations/data-defined/use-cases/definition-use-cases.js +187 -0
- package/integrations/data-defined/validate.js +370 -0
- package/integrations/data-defined/versioning.js +161 -0
- package/integrations/data-defined/webhooks.js +119 -0
- package/integrations/index.js +0 -1
- package/integrations/integration-base.js +29 -0
- package/integrations/integration-router.js +101 -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 +3 -35
- package/integrations/repositories/integration-repository-interface.js +1 -17
- package/integrations/repositories/integration-repository-mongo.js +77 -60
- package/integrations/repositories/integration-repository-postgres.js +76 -62
- package/integrations/repositories/process-repository-mongo.js +1 -1
- package/integrations/tests/doubles/test-integration-repository.js +1 -12
- package/integrations/use-cases/create-integration.js +20 -55
- package/integrations/use-cases/get-integrations-for-user.js +40 -80
- package/integrations/use-cases/index.js +1 -7
- package/integrations/utils/map-integration-dto.js +14 -43
- 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 +5 -35
- 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/package.json +6 -6
- package/prisma-mongodb/schema.prisma +50 -86
- package/prisma-postgresql/migrations/20261018000000_add_data_defined_integrations/migration.sql +66 -0
- package/prisma-postgresql/schema.prisma +48 -81
- package/queues/index.js +0 -25
- 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/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-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/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 -216
- 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 -415
- 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-bases.js +0 -169
- package/modules/proxy/proxy-errors.js +0 -31
- package/modules/proxy/proxy-path.js +0 -183
- package/modules/proxy/proxy-policy.js +0 -277
- 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 -245
- 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 -331
- 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 -150
- 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 -214
- 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
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const { prisma } = require('../../database/prisma');
|
|
2
|
+
const { MappingAlreadyExistsError } = require('../../errors');
|
|
2
3
|
const {
|
|
3
4
|
toObjectId,
|
|
4
5
|
fromObjectId,
|
|
@@ -180,6 +181,55 @@ class IntegrationMappingRepositoryDocumentDB extends IntegrationMappingRepositor
|
|
|
180
181
|
return this._mapMapping(decryptedMapping);
|
|
181
182
|
}
|
|
182
183
|
|
|
184
|
+
async createMapping(integrationId, sourceId, mapping) {
|
|
185
|
+
const now = new Date();
|
|
186
|
+
const encryptedDocument = await this.encryptionService.encryptFields(
|
|
187
|
+
'IntegrationMapping',
|
|
188
|
+
{
|
|
189
|
+
integrationId,
|
|
190
|
+
sourceId:
|
|
191
|
+
sourceId === null || sourceId === undefined
|
|
192
|
+
? null
|
|
193
|
+
: String(sourceId),
|
|
194
|
+
mapping,
|
|
195
|
+
createdAt: now,
|
|
196
|
+
updatedAt: now,
|
|
197
|
+
}
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
let insertedId;
|
|
201
|
+
try {
|
|
202
|
+
insertedId = await insertOne(
|
|
203
|
+
this.prisma,
|
|
204
|
+
'IntegrationMapping',
|
|
205
|
+
encryptedDocument
|
|
206
|
+
);
|
|
207
|
+
} catch (error) {
|
|
208
|
+
if (error.code === 11000) {
|
|
209
|
+
throw new MappingAlreadyExistsError(integrationId, sourceId, {
|
|
210
|
+
cause: error,
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
throw error;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const created = await findOne(this.prisma, 'IntegrationMapping', {
|
|
217
|
+
_id: insertedId,
|
|
218
|
+
});
|
|
219
|
+
if (!created) {
|
|
220
|
+
throw new Error(
|
|
221
|
+
'Failed to create mapping: Document not found after insert. ' +
|
|
222
|
+
'This indicates a database consistency issue.'
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
return this._mapMapping(
|
|
226
|
+
await this.encryptionService.decryptFields(
|
|
227
|
+
'IntegrationMapping',
|
|
228
|
+
created
|
|
229
|
+
)
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
|
|
183
233
|
async findMappingsByIntegration(integrationId) {
|
|
184
234
|
const filter = {};
|
|
185
235
|
if (integrationId) filter.integrationId = integrationId;
|
|
@@ -39,6 +39,21 @@ class IntegrationMappingRepositoryInterface {
|
|
|
39
39
|
throw new Error('Method upsertMapping must be implemented by subclass');
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
/**
|
|
43
|
+
* Insert a mapping, failing if one already exists for the
|
|
44
|
+
* (integrationId, sourceId) pair. Atomic: usable as a claim/lock row.
|
|
45
|
+
*
|
|
46
|
+
* @param {string|number} integrationId - The integration ID
|
|
47
|
+
* @param {string} sourceId - The source ID for lookup
|
|
48
|
+
* @param {Object} mapping - The mapping data
|
|
49
|
+
* @returns {Promise<Object>} The created mapping document
|
|
50
|
+
* @throws {MappingAlreadyExistsError} If the pair already exists
|
|
51
|
+
* @abstract
|
|
52
|
+
*/
|
|
53
|
+
async createMapping(integrationId, sourceId, mapping) {
|
|
54
|
+
throw new Error('createMapping must be implemented by subclass');
|
|
55
|
+
}
|
|
56
|
+
|
|
42
57
|
/**
|
|
43
58
|
* Find all mappings for an integration
|
|
44
59
|
*
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const { prisma } = require('../../database/prisma');
|
|
2
|
+
const { MappingAlreadyExistsError } = require('../../errors');
|
|
2
3
|
const {
|
|
3
4
|
assertMappingWrittenUnencrypted,
|
|
4
5
|
decryptQueriedMappings,
|
|
@@ -93,6 +94,34 @@ class IntegrationMappingRepositoryMongo extends IntegrationMappingRepositoryInte
|
|
|
93
94
|
});
|
|
94
95
|
}
|
|
95
96
|
|
|
97
|
+
/**
|
|
98
|
+
* Insert a mapping; fails if (integrationId, sourceId) already exists
|
|
99
|
+
*
|
|
100
|
+
* @param {string} integrationId - The integration ID
|
|
101
|
+
* @param {string} sourceId - The source ID
|
|
102
|
+
* @param {Object} mapping - The mapping data
|
|
103
|
+
* @returns {Promise<Object>} The created mapping document
|
|
104
|
+
* @throws {MappingAlreadyExistsError} If the pair already exists
|
|
105
|
+
*/
|
|
106
|
+
async createMapping(integrationId, sourceId, mapping) {
|
|
107
|
+
try {
|
|
108
|
+
return await this.prisma.integrationMapping.create({
|
|
109
|
+
data: {
|
|
110
|
+
integrationId,
|
|
111
|
+
sourceId: this._toString(sourceId),
|
|
112
|
+
mapping,
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
} catch (error) {
|
|
116
|
+
if (error.code === 'P2002') {
|
|
117
|
+
throw new MappingAlreadyExistsError(integrationId, sourceId, {
|
|
118
|
+
cause: error,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
throw error;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
96
125
|
/**
|
|
97
126
|
* Find all mappings for an integration
|
|
98
127
|
* Replaces: IntegrationMapping.find({ integration: integrationId })
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const { prisma } = require('../../database/prisma');
|
|
2
|
+
const { MappingAlreadyExistsError } = require('../../errors');
|
|
2
3
|
const {
|
|
3
4
|
assertMappingWrittenUnencrypted,
|
|
4
5
|
decryptQueriedMappings,
|
|
@@ -150,6 +151,35 @@ class IntegrationMappingRepositoryPostgres extends IntegrationMappingRepositoryI
|
|
|
150
151
|
return this._convertMappingIds(result);
|
|
151
152
|
}
|
|
152
153
|
|
|
154
|
+
/**
|
|
155
|
+
* Insert a mapping; fails if (integrationId, sourceId) already exists
|
|
156
|
+
*
|
|
157
|
+
* @param {string} integrationId - The integration ID
|
|
158
|
+
* @param {string} sourceId - The source ID
|
|
159
|
+
* @param {Object} mapping - The mapping data
|
|
160
|
+
* @returns {Promise<Object>} The created mapping document
|
|
161
|
+
* @throws {MappingAlreadyExistsError} If the pair already exists
|
|
162
|
+
*/
|
|
163
|
+
async createMapping(integrationId, sourceId, mapping) {
|
|
164
|
+
try {
|
|
165
|
+
const created = await this.prisma.integrationMapping.create({
|
|
166
|
+
data: {
|
|
167
|
+
integrationId: this._stringToInt(integrationId),
|
|
168
|
+
sourceId: this._toString(sourceId),
|
|
169
|
+
mapping,
|
|
170
|
+
},
|
|
171
|
+
});
|
|
172
|
+
return this._convertMappingIds(created);
|
|
173
|
+
} catch (error) {
|
|
174
|
+
if (error.code === 'P2002') {
|
|
175
|
+
throw new MappingAlreadyExistsError(integrationId, sourceId, {
|
|
176
|
+
cause: error,
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
throw error;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
153
183
|
/**
|
|
154
184
|
* Find all mappings for an integration
|
|
155
185
|
* Replaces: IntegrationMapping.find({ integration: integrationId })
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const { prisma } = require('../../database/prisma');
|
|
2
|
+
const { MappingAlreadyExistsError } = require('../../errors');
|
|
2
3
|
const {
|
|
3
4
|
IntegrationMappingRepositoryInterface,
|
|
4
5
|
} = require('./integration-mapping-repository-interface');
|
|
@@ -69,6 +70,34 @@ class IntegrationMappingRepository extends IntegrationMappingRepositoryInterface
|
|
|
69
70
|
});
|
|
70
71
|
}
|
|
71
72
|
|
|
73
|
+
/**
|
|
74
|
+
* Insert a mapping; fails if (integrationId, sourceId) already exists
|
|
75
|
+
*
|
|
76
|
+
* @param {string} integrationId - The integration ID
|
|
77
|
+
* @param {string} sourceId - The source ID
|
|
78
|
+
* @param {Object} mapping - The mapping data
|
|
79
|
+
* @returns {Promise<Object>} The created mapping document
|
|
80
|
+
* @throws {MappingAlreadyExistsError} If the pair already exists
|
|
81
|
+
*/
|
|
82
|
+
async createMapping(integrationId, sourceId, mapping) {
|
|
83
|
+
try {
|
|
84
|
+
return await this.prisma.integrationMapping.create({
|
|
85
|
+
data: {
|
|
86
|
+
integrationId,
|
|
87
|
+
sourceId,
|
|
88
|
+
mapping,
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
} catch (error) {
|
|
92
|
+
if (error.code === 'P2002') {
|
|
93
|
+
throw new MappingAlreadyExistsError(integrationId, sourceId, {
|
|
94
|
+
cause: error,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
throw error;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
72
101
|
/**
|
|
73
102
|
* Find all mappings for an integration
|
|
74
103
|
* Replaces: IntegrationMapping.find({ integration: integrationId })
|
|
@@ -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',
|
|
@@ -329,7 +298,6 @@ class IntegrationRepositoryDocumentDB extends IntegrationRepositoryInterface {
|
|
|
329
298
|
id: fromObjectId(doc?._id),
|
|
330
299
|
entitiesIds: (doc?.entityIds || []).map((value) => fromObjectId(value)),
|
|
331
300
|
userId: fromObjectId(doc?.userId),
|
|
332
|
-
organizationId: fromObjectId(doc?.organizationId) ?? null,
|
|
333
301
|
config: doc?.config ?? null,
|
|
334
302
|
version: doc?.version ?? null,
|
|
335
303
|
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
|
|
|
@@ -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,15 @@ 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
|
+
};
|
|
338
339
|
}
|
|
339
340
|
|
|
340
341
|
/**
|
|
@@ -356,7 +357,15 @@ class IntegrationRepositoryMongo extends IntegrationRepositoryInterface {
|
|
|
356
357
|
return null;
|
|
357
358
|
}
|
|
358
359
|
|
|
359
|
-
return
|
|
360
|
+
return {
|
|
361
|
+
id: integration.id,
|
|
362
|
+
entitiesIds: integration.entities.map((e) => e.id),
|
|
363
|
+
userId: integration.userId,
|
|
364
|
+
config: integration.config,
|
|
365
|
+
version: integration.version,
|
|
366
|
+
status: integration.status,
|
|
367
|
+
messages: integration.messages,
|
|
368
|
+
};
|
|
360
369
|
}
|
|
361
370
|
|
|
362
371
|
/**
|
|
@@ -380,7 +389,15 @@ class IntegrationRepositoryMongo extends IntegrationRepositoryInterface {
|
|
|
380
389
|
},
|
|
381
390
|
});
|
|
382
391
|
|
|
383
|
-
return
|
|
392
|
+
return {
|
|
393
|
+
id: integration.id,
|
|
394
|
+
entitiesIds: integration.entities.map((e) => e.id),
|
|
395
|
+
userId: integration.userId,
|
|
396
|
+
config: integration.config,
|
|
397
|
+
version: integration.version,
|
|
398
|
+
status: integration.status,
|
|
399
|
+
messages: integration.messages,
|
|
400
|
+
};
|
|
384
401
|
}
|
|
385
402
|
|
|
386
403
|
/**
|
|
@@ -402,7 +419,7 @@ class IntegrationRepositoryMongo extends IntegrationRepositoryInterface {
|
|
|
402
419
|
for (const [key, value] of Object.entries(patch)) {
|
|
403
420
|
$set[`config.${key}`] = value;
|
|
404
421
|
}
|
|
405
|
-
$set.updatedAt = new Date();
|
|
422
|
+
$set.updatedAt = { $date: new Date().toISOString() };
|
|
406
423
|
|
|
407
424
|
const result = await this.prisma.$runCommandRaw({
|
|
408
425
|
findAndModify: 'Integration',
|