@friggframework/core 2.0.0--canary.703.12b980b.0 → 2.0.0--canary.707.df80bfd.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 +11 -0
- package/CLAUDE.md +28 -0
- package/README.md +105 -0
- package/application/commands/integration-commands.js +20 -10
- package/application/commands/scheduler-commands.js +11 -7
- package/application/commands/user-commands.js +17 -5
- package/application/index.js +6 -2
- package/core/Worker.js +31 -7
- package/core/create-handler.js +9 -0
- package/credential/repositories/credential-repository-documentdb.js +34 -0
- package/credential/repositories/credential-repository-interface.js +15 -0
- package/credential/repositories/credential-repository-mongo.js +23 -0
- package/credential/repositories/credential-repository-postgres.js +28 -0
- package/credential/use-cases/credential-operations-for-user.js +202 -0
- package/credential/use-cases/reauthorize-credential-for-user.js +73 -0
- package/database/config.js +48 -24
- package/database/documentdb-encryption-service.js +9 -22
- package/database/documentdb-utils.js +2 -2
- package/database/encryption/README.md +78 -14
- package/database/encryption/documentdb-encryption-service.md +78 -1
- package/database/encryption/encryption-config.js +219 -0
- package/database/encryption/encryption-schema-registry.js +121 -24
- package/database/encryption/field-encryption-service.js +69 -5
- package/database/encryption/integration-mapping-encryption.js +1 -1
- package/database/encryption/test-support/fake-ciphertext.js +15 -0
- package/database/organization-where.js +18 -0
- package/database/prisma.js +15 -38
- package/database/unique-violation.js +25 -0
- package/database/use-cases/check-encryption-health-use-case.js +43 -52
- package/errors/index.js +0 -4
- package/extensions/bootstrap-runner.js +43 -0
- package/extensions/extension-loader.js +230 -0
- package/extensions/index.js +44 -0
- package/extensions/initialize-app.js +123 -0
- package/extensions/prisma-composer.js +270 -0
- package/extensions/route-mounter.js +103 -0
- package/extensions/schema-composer.js +188 -0
- package/generated/prisma-mongodb/edge.js +33 -42
- package/generated/prisma-mongodb/index-browser.js +30 -39
- package/generated/prisma-mongodb/index.d.ts +3187 -2005
- package/generated/prisma-mongodb/index.js +33 -42
- package/generated/prisma-mongodb/package.json +1 -1
- package/generated/prisma-mongodb/schema.prisma +86 -59
- package/generated/prisma-mongodb/wasm.js +33 -42
- package/generated/prisma-postgresql/edge.js +33 -42
- package/generated/prisma-postgresql/index-browser.js +30 -39
- package/generated/prisma-postgresql/index.d.ts +3380 -2177
- package/generated/prisma-postgresql/index.js +33 -42
- package/generated/prisma-postgresql/package.json +1 -1
- package/generated/prisma-postgresql/schema.prisma +81 -57
- package/generated/prisma-postgresql/wasm.js +33 -42
- package/handlers/WEBHOOKS.md +0 -29
- package/handlers/app-definition-loader.js +128 -19
- package/handlers/app-handler-helpers.js +27 -8
- package/handlers/backend-utils.js +31 -25
- package/handlers/integration-event-dispatcher.js +3 -7
- package/handlers/middleware/admin-auth.js +14 -1
- package/handlers/routers/auth.js +46 -10
- package/handlers/routers/health.js +13 -4
- package/handlers/routers/integration-defined-routers.js +144 -92
- package/handlers/routers/integration-webhook-routers.js +100 -50
- package/handlers/workers/integration-defined-workers.js +5 -2
- package/index.js +66 -16
- package/infrastructure/scheduler/index.js +19 -6
- package/infrastructure/scheduler/netlify-scheduler-adapter.js +216 -0
- package/infrastructure/scheduler/scheduled-job-repository.js +118 -0
- package/infrastructure/scheduler/scheduler-service-factory.js +55 -7
- package/integrations/index.js +1 -14
- package/integrations/integration-base.js +2 -163
- package/integrations/integration-router.js +132 -102
- package/integrations/repositories/integration-mapping-repository-documentdb.js +0 -50
- package/integrations/repositories/integration-mapping-repository-interface.js +0 -15
- package/integrations/repositories/integration-mapping-repository-mongo.js +0 -29
- package/integrations/repositories/integration-mapping-repository-postgres.js +0 -30
- package/integrations/repositories/integration-mapping-repository.js +0 -29
- package/integrations/repositories/integration-not-found.js +14 -0
- package/integrations/repositories/integration-repository-documentdb.js +37 -9
- package/integrations/repositories/integration-repository-interface.js +17 -4
- package/integrations/repositories/integration-repository-mongo.js +64 -96
- package/integrations/repositories/integration-repository-postgres.js +63 -81
- package/integrations/repositories/process-repository-mongo.js +1 -1
- package/integrations/schedule-config.js +80 -0
- package/integrations/tests/doubles/test-integration-repository.js +12 -1
- package/integrations/use-cases/create-integration.js +56 -31
- package/integrations/use-cases/delete-integration-for-user.js +4 -4
- package/integrations/use-cases/dispatch-ongoing-syncs.js +100 -0
- package/integrations/use-cases/get-integration-for-user.js +2 -4
- package/integrations/use-cases/get-integration-instance.js +4 -4
- package/integrations/use-cases/get-integrations-for-user.js +79 -39
- package/integrations/use-cases/get-possible-integrations.js +2 -7
- package/integrations/use-cases/index.js +7 -1
- package/integrations/use-cases/integration-operations-for-user.js +256 -0
- package/integrations/use-cases/resolve-integration-ownership.js +177 -0
- package/integrations/use-cases/update-integration.js +4 -4
- package/integrations/utils/map-integration-dto.js +43 -14
- package/management-api/create-management-api-router.js +49 -0
- package/management-api/error-response.js +116 -0
- package/management-api/management-api-config.js +151 -0
- package/management-api/meta-router.js +76 -0
- package/management-api/openapi/build-openapi.js +144 -0
- package/management-api/openapi/components.js +415 -0
- package/management-api/openapi/v1-operations.js +83 -0
- package/management-api/openapi/v2-operations.js +264 -0
- package/management-api/route-registry.js +411 -0
- package/management-api/test-support/http-client.js +39 -0
- package/management-api/use-cases/get-management-api-meta.js +57 -0
- package/management-api/v1-deprecation.js +120 -0
- package/management-api/v2/create-v2-router.js +85 -0
- package/management-api/v2/dependencies.js +281 -0
- package/management-api/v2/dto.js +143 -0
- package/management-api/v2/handlers/authorization.js +94 -0
- package/management-api/v2/handlers/credentials.js +33 -0
- package/management-api/v2/handlers/entities.js +77 -0
- package/management-api/v2/handlers/integrations.js +128 -0
- package/management-api/v2/handlers/proxy.js +34 -0
- package/management-api/v2/request.js +85 -0
- package/management-api/v2/test-support/fake-dependencies.js +103 -0
- package/modules/domain/authorization-session.js +144 -0
- package/modules/entity-scope.js +15 -0
- package/modules/entity-type-registry.js +311 -0
- package/modules/index.js +14 -0
- package/modules/module-factory.js +12 -0
- package/modules/module.js +53 -2
- package/modules/proxy/proxy-bases.js +169 -0
- package/modules/proxy/proxy-errors.js +31 -0
- package/modules/proxy/proxy-path.js +183 -0
- package/modules/proxy/proxy-policy.js +277 -0
- package/modules/repositories/authorization-session-repository-documentdb.js +112 -0
- package/modules/repositories/authorization-session-repository-factory.js +31 -0
- package/modules/repositories/authorization-session-repository-interface.js +47 -0
- package/modules/repositories/authorization-session-repository-prisma.js +87 -0
- package/modules/repositories/entity-not-found.js +18 -0
- package/modules/repositories/module-repository-documentdb.js +51 -6
- package/modules/repositories/module-repository-interface.js +19 -2
- package/modules/repositories/module-repository-mongo.js +67 -74
- package/modules/repositories/module-repository-postgres.js +66 -74
- package/modules/repositories/module-repository.js +2 -1
- package/modules/requester/proxy-request.js +245 -0
- package/modules/requester/requester.js +35 -5
- package/modules/scope-config.js +192 -0
- package/modules/scope-policy.js +112 -0
- package/modules/scope-resolution.js +288 -0
- package/modules/scope-visibility.js +247 -0
- package/modules/tests/doubles/in-memory-authorization-session-repository.js +45 -0
- package/modules/tests/doubles/ownership-matching.js +18 -0
- package/modules/use-cases/authorize-entity-for-user.js +61 -0
- package/modules/use-cases/create-entity-from-credential.js +113 -0
- package/modules/use-cases/entity-operations-for-user.js +162 -0
- package/modules/use-cases/entity-types.js +139 -0
- package/modules/use-cases/execute-entity-proxy-request.js +331 -0
- package/modules/use-cases/get-authorization-requirements.js +100 -0
- package/modules/use-cases/get-entities-for-user.js +26 -13
- package/modules/use-cases/get-entity-module-for-user.js +113 -0
- package/modules/use-cases/get-entity-options-by-id.js +10 -4
- 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 +10 -4
- package/modules/use-cases/manage-access.js +21 -0
- package/modules/use-cases/multi-step-authorization.js +329 -0
- package/modules/use-cases/process-authorization-callback.js +53 -19
- package/modules/use-cases/refresh-entity-options.js +10 -4
- package/modules/use-cases/test-module-auth.js +10 -4
- package/package.json +6 -7
- package/prisma-mongodb/schema.prisma +86 -59
- package/prisma-postgresql/migrations/20261003000000_add_authorization_session/migration.sql +28 -0
- package/prisma-postgresql/migrations/20261015000000_add_organization_membership/migration.sql +26 -0
- package/prisma-postgresql/migrations/20261016000000_add_entity_integration_organization/migration.sql +25 -0
- package/prisma-postgresql/migrations/20261017000000_add_scheduled_job/migration.sql +28 -0
- package/prisma-postgresql/schema.prisma +81 -57
- package/providers/index.js +23 -0
- package/providers/resolve-provider.js +93 -0
- package/providers/runtime-detection.js +124 -0
- package/queues/index.js +25 -0
- package/queues/providers/index.js +11 -0
- package/queues/providers/netlify-background-provider.js +150 -0
- package/queues/providers/qstash-queue-provider.js +219 -0
- package/queues/providers/sqs-queue-provider.js +70 -0
- package/queues/queue-client-interface.js +55 -0
- package/queues/queue-message-auth.js +193 -0
- package/queues/queue-provider-factory.js +101 -0
- package/queues/queue-provider.js +65 -0
- package/queues/queuer-util.js +2 -9
- package/queues/sqs-queue-client.js +81 -0
- package/types/index.d.ts +2 -0
- package/types/modules/index.d.ts +214 -0
- package/types/user/index.d.ts +134 -0
- package/user/adopter-jwt-config.js +177 -0
- package/user/adopter-jwt-errors.js +59 -0
- package/user/adopter-token-verifier.js +122 -0
- package/user/app-user-helpers.js +97 -0
- package/user/create-user-authentication.js +64 -0
- package/user/membership.js +51 -0
- package/user/repositories/organization-membership-record.js +19 -0
- package/user/repositories/organization-membership-repository-documentdb.js +106 -0
- package/user/repositories/organization-membership-repository-factory.js +42 -0
- package/user/repositories/organization-membership-repository-interface.js +57 -0
- package/user/repositories/organization-membership-repository-mongo.js +13 -0
- package/user/repositories/organization-membership-repository-postgres.js +29 -0
- package/user/repositories/organization-membership-repository-prisma.js +81 -0
- package/user/repositories/user-repository-documentdb.js +27 -2
- package/user/repositories/user-repository-interface.js +18 -0
- package/user/repositories/user-repository-mongo.js +24 -0
- package/user/repositories/user-repository-postgres.js +18 -0
- package/user/tests/doubles/adopter-jwt-fixtures.js +220 -0
- package/user/use-cases/authenticate-user.js +7 -1
- package/user/use-cases/ensure-organization-membership.js +92 -0
- package/user/use-cases/find-individual-users-by-organization.js +54 -0
- package/user/use-cases/get-user-from-adopter-jwt.js +95 -124
- package/user/use-cases/get-user-from-x-frigg-headers.js +83 -80
- package/user/user.js +64 -1
- package/errors/mapping-already-exists-error.js +0 -14
- package/handlers/workers/integration-triggers.js +0 -25
- package/infrastructure/scheduler/recurrence.js +0 -204
- package/integrations/define/DESIGN.md +0 -122
- package/integrations/define/config.js +0 -98
- package/integrations/define/define-app.js +0 -66
- package/integrations/define/define-extension.js +0 -154
- package/integrations/define/define-integration.js +0 -335
- package/integrations/define/examples/deal-alerts.js +0 -140
- package/integrations/define/extension-binding.js +0 -174
- package/integrations/define/index.js +0 -11
- package/integrations/define/json-schema-lite.js +0 -68
- package/integrations/define/subscriptions.js +0 -186
- package/integrations/define/tests/fixtures/hubspot.js +0 -109
- package/integrations/define/tests/fixtures/slack.js +0 -26
- package/integrations/define/use.js +0 -39
- package/integrations/definitions/README.md +0 -278
- package/integrations/definitions/canonical.js +0 -11
- package/integrations/definitions/capabilities/AUDIT.md +0 -480
- package/integrations/definitions/capabilities/acmeerp.capabilities.json +0 -633
- package/integrations/definitions/capabilities/asana.capabilities.json +0 -279
- package/integrations/definitions/capabilities/calendly.capabilities.json +0 -385
- package/integrations/definitions/capabilities/docusign.capabilities.json +0 -314
- package/integrations/definitions/capabilities/fathom.capabilities.json +0 -238
- package/integrations/definitions/capabilities/github.capabilities.json +0 -585
- package/integrations/definitions/capabilities/hubspot.capabilities.json +0 -822
- package/integrations/definitions/capabilities/jira.capabilities.json +0 -391
- package/integrations/definitions/capabilities/klaviyo.capabilities.json +0 -503
- package/integrations/definitions/capabilities/linear.capabilities.json +0 -378
- package/integrations/definitions/capabilities/netsuite.capabilities.json +0 -535
- package/integrations/definitions/capabilities/notion.capabilities.json +0 -208
- package/integrations/definitions/capabilities/phone.capabilities.json +0 -511
- package/integrations/definitions/capabilities/pipedrive.capabilities.json +0 -430
- package/integrations/definitions/capabilities/quickbooks.capabilities.json +0 -635
- package/integrations/definitions/capabilities/salesforce.capabilities.json +0 -568
- package/integrations/definitions/capabilities/shopify.capabilities.json +0 -1128
- package/integrations/definitions/capabilities/slack.capabilities.json +0 -373
- package/integrations/definitions/capabilities/stripe.capabilities.json +0 -630
- package/integrations/definitions/capabilities/webflow.capabilities.json +0 -196
- package/integrations/definitions/capabilities/zendesk.capabilities.json +0 -422
- package/integrations/definitions/compile.js +0 -80
- package/integrations/definitions/config-form.js +0 -137
- package/integrations/definitions/examples/acme-erp-quickbooks.definition.json +0 -467
- package/integrations/definitions/examples/asana-slack.definition.json +0 -219
- package/integrations/definitions/examples/calendly-salesforce.definition.json +0 -459
- package/integrations/definitions/examples/docusign-salesforce.definition.json +0 -305
- package/integrations/definitions/examples/fathom-hubspot-notion.definition.json +0 -575
- package/integrations/definitions/examples/github-linear.definition.json +0 -322
- package/integrations/definitions/examples/hubspot-slack.definition.json +0 -427
- package/integrations/definitions/examples/klaviyo-hubspot.definition.json +0 -336
- package/integrations/definitions/examples/phone-hubspot.definition.json +0 -633
- package/integrations/definitions/examples/pipedrive-hubspot.definition.json +0 -136
- package/integrations/definitions/examples/pipedrive-phone.definition.json +0 -340
- package/integrations/definitions/examples/records/phone-hubspot.definition-version.json +0 -652
- package/integrations/definitions/examples/records/phone-hubspot.integration.json +0 -86
- package/integrations/definitions/examples/records/phone-hubspot.mappings.json +0 -39
- package/integrations/definitions/examples/records/phone-hubspot.workflow-run.json +0 -62
- package/integrations/definitions/examples/shopify-klaviyo.definition.json +0 -488
- package/integrations/definitions/examples/shopify-netsuite.definition.json +0 -622
- package/integrations/definitions/examples/stripe-quickbooks.definition.json +0 -476
- package/integrations/definitions/examples/webflow-hubspot-slack.definition.json +0 -262
- package/integrations/definitions/examples/zendesk-jira.definition.json +0 -604
- package/integrations/definitions/expressions.js +0 -229
- package/integrations/definitions/index.js +0 -48
- package/integrations/definitions/integration-runtime.js +0 -779
- package/integrations/definitions/interpreter.js +0 -104
- package/integrations/definitions/manifests.js +0 -177
- package/integrations/definitions/renderers/common.js +0 -67
- package/integrations/definitions/renderers/html.js +0 -129
- package/integrations/definitions/renderers/hubspot.js +0 -59
- package/integrations/definitions/renderers/preview.js +0 -154
- package/integrations/definitions/renderers/slack.js +0 -146
- package/integrations/definitions/repositories/definition-version-repository.js +0 -67
- package/integrations/definitions/repositories/workflow-run-repository.js +0 -80
- package/integrations/definitions/router.js +0 -170
- package/integrations/definitions/runtime.js +0 -268
- package/integrations/definitions/schema/capability-manifest.v0.1.schema.json +0 -549
- package/integrations/definitions/schema/installation.v0.1.schema.json +0 -130
- package/integrations/definitions/schema/integration-definition.v0.1.schema.json +0 -909
- package/integrations/definitions/schema/records.md +0 -105
- package/integrations/definitions/schema-check.js +0 -58
- package/integrations/definitions/scripts/check-manifests.js +0 -220
- package/integrations/definitions/scripts/validate.js +0 -66
- package/integrations/definitions/steps.js +0 -123
- package/integrations/definitions/sync-engine.js +0 -297
- package/integrations/definitions/tests/helpers-fake-crm.js +0 -49
- package/integrations/definitions/transforms.js +0 -91
- package/integrations/definitions/use-cases/definition-use-cases.js +0 -245
- package/integrations/definitions/validate.js +0 -436
- package/integrations/definitions/versioning.js +0 -176
- package/integrations/definitions/webhooks.js +0 -119
- package/integrations/integration-types.js +0 -73
- package/integrations/triggers.js +0 -244
- package/integrations/use-cases/run-trigger-tick.js +0 -155
- package/integrations/use-cases/run-trigger.js +0 -77
- package/prisma-postgresql/migrations/20261018000000_add_data_defined_integrations/migration.sql +0 -66
- package/prisma-postgresql/migrations/20261019000000_workflow_run_waits/migration.sql +0 -7
- package/prisma-postgresql/migrations/20261019120000_definition_workspace/migration.sql +0 -5
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
const { prisma } = require('../../database/prisma');
|
|
2
|
-
const { MappingAlreadyExistsError } = require('../../errors');
|
|
3
2
|
const {
|
|
4
3
|
IntegrationMappingRepositoryInterface,
|
|
5
4
|
} = require('./integration-mapping-repository-interface');
|
|
@@ -70,34 +69,6 @@ class IntegrationMappingRepository extends IntegrationMappingRepositoryInterface
|
|
|
70
69
|
});
|
|
71
70
|
}
|
|
72
71
|
|
|
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
|
-
|
|
101
72
|
/**
|
|
102
73
|
* Find all mappings for an integration
|
|
103
74
|
* Replaces: IntegrationMapping.find({ integration: integrationId })
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The error `findIntegrationById` throws for an id with no row, and the one
|
|
3
|
+
* predicate that recognises it. The adapters build the error here and the
|
|
4
|
+
* use cases match it here, so the message is written once and a missing
|
|
5
|
+
* integration can be told from any other failure without widening a regex.
|
|
6
|
+
*/
|
|
7
|
+
const MESSAGE = /^Integration with id .+ not found$/;
|
|
8
|
+
|
|
9
|
+
const integrationNotFoundError = (id) =>
|
|
10
|
+
new Error(`Integration with id ${id} not found`);
|
|
11
|
+
|
|
12
|
+
const isIntegrationNotFound = (error) => MESSAGE.test(error?.message || '');
|
|
13
|
+
|
|
14
|
+
module.exports = { integrationNotFoundError, isIntegrationNotFound };
|
|
@@ -14,6 +14,7 @@ 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');
|
|
17
18
|
|
|
18
19
|
class IntegrationRepositoryDocumentDB extends IntegrationRepositoryInterface {
|
|
19
20
|
constructor() {
|
|
@@ -28,7 +29,29 @@ class IntegrationRepositoryDocumentDB extends IntegrationRepositoryInterface {
|
|
|
28
29
|
return records.map((doc) => this._mapIntegration(doc));
|
|
29
30
|
}
|
|
30
31
|
|
|
31
|
-
|
|
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
|
+
async findIntegrations({ type, status } = {}) {
|
|
32
55
|
const filter = {};
|
|
33
56
|
if (type) {
|
|
34
57
|
filter['config.type'] = type;
|
|
@@ -36,11 +59,7 @@ class IntegrationRepositoryDocumentDB extends IntegrationRepositoryInterface {
|
|
|
36
59
|
if (status) {
|
|
37
60
|
filter.status = status;
|
|
38
61
|
}
|
|
39
|
-
|
|
40
|
-
// Extended JSON: $runCommandRaw does not match a BSON ObjectId instance inside an operator.
|
|
41
|
-
filter._id = { $gt: { $oid: fromObjectId(toObjectId(after)) } };
|
|
42
|
-
}
|
|
43
|
-
const records = await findMany(this.prisma, 'Integration', filter, limit ? { sort: { _id: 1 }, limit, batchSize: limit } : {});
|
|
62
|
+
const records = await findMany(this.prisma, 'Integration', filter);
|
|
44
63
|
return records.map((doc) => this._mapIntegration(doc));
|
|
45
64
|
}
|
|
46
65
|
|
|
@@ -72,11 +91,11 @@ class IntegrationRepositoryDocumentDB extends IntegrationRepositoryInterface {
|
|
|
72
91
|
async findIntegrationById(id) {
|
|
73
92
|
const objectId = toObjectId(id);
|
|
74
93
|
if (!objectId) {
|
|
75
|
-
throw
|
|
94
|
+
throw integrationNotFoundError(id);
|
|
76
95
|
}
|
|
77
96
|
const doc = await findOne(this.prisma, 'Integration', { _id: objectId });
|
|
78
97
|
if (!doc) {
|
|
79
|
-
throw
|
|
98
|
+
throw integrationNotFoundError(id);
|
|
80
99
|
}
|
|
81
100
|
return this._mapIntegration(doc);
|
|
82
101
|
}
|
|
@@ -136,10 +155,18 @@ class IntegrationRepositoryDocumentDB extends IntegrationRepositoryInterface {
|
|
|
136
155
|
return true;
|
|
137
156
|
}
|
|
138
157
|
|
|
139
|
-
|
|
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 = {}) {
|
|
140
166
|
const now = new Date();
|
|
141
167
|
const document = {
|
|
142
168
|
userId: toObjectId(userId) || null,
|
|
169
|
+
organizationId: toObjectId(tenancy.organizationId) || null,
|
|
143
170
|
config,
|
|
144
171
|
version: '0.0.0',
|
|
145
172
|
status: 'IN_CREATION',
|
|
@@ -302,6 +329,7 @@ class IntegrationRepositoryDocumentDB extends IntegrationRepositoryInterface {
|
|
|
302
329
|
id: fromObjectId(doc?._id),
|
|
303
330
|
entitiesIds: (doc?.entityIds || []).map((value) => fromObjectId(value)),
|
|
304
331
|
userId: fromObjectId(doc?.userId),
|
|
332
|
+
organizationId: fromObjectId(doc?.organizationId) ?? null,
|
|
305
333
|
config: doc?.config ?? null,
|
|
306
334
|
version: doc?.version ?? null,
|
|
307
335
|
status: doc?.status ?? null,
|
|
@@ -10,6 +10,20 @@
|
|
|
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
|
+
|
|
13
27
|
/**
|
|
14
28
|
* Find all integrations for a user
|
|
15
29
|
*
|
|
@@ -28,9 +42,6 @@ class IntegrationRepositoryInterface {
|
|
|
28
42
|
* @param {Object} [filter={}]
|
|
29
43
|
* @param {string} [filter.type] - Integration type (config.type)
|
|
30
44
|
* @param {string} [filter.status] - Integration status
|
|
31
|
-
* @param {number} [filter.limit] - Page size: at most this many rows, in id order
|
|
32
|
-
* @param {string|number} [filter.after] - With limit: only rows whose id sorts after this one (the last id of the
|
|
33
|
-
* previous page), so a caller can page through every row (the trigger tick does, ADR-055)
|
|
34
45
|
* @returns {Promise<Array>} Array of integration objects (possibly empty)
|
|
35
46
|
* @abstract
|
|
36
47
|
*/
|
|
@@ -126,10 +137,12 @@ class IntegrationRepositoryInterface {
|
|
|
126
137
|
* @param {Array<string|number>} entities - Array of entity IDs
|
|
127
138
|
* @param {string|number} userId - User ID
|
|
128
139
|
* @param {Object} config - Integration configuration
|
|
140
|
+
* @param {{organizationId?: (string|number|null)}} [tenancy] - the organization the
|
|
141
|
+
* integration is created in (ADR-056 §3.6)
|
|
129
142
|
* @returns {Promise<Object>} Created integration object
|
|
130
143
|
* @abstract
|
|
131
144
|
*/
|
|
132
|
-
async createIntegration(entities, userId, config) {
|
|
145
|
+
async createIntegration(entities, userId, config, tenancy) {
|
|
133
146
|
throw new Error('Method createIntegration must be implemented by subclass');
|
|
134
147
|
}
|
|
135
148
|
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
const { prisma } = require('../../database/prisma');
|
|
2
|
+
const { organizationWhere } = require('../../database/organization-where');
|
|
2
3
|
const {
|
|
3
4
|
IntegrationRepositoryInterface,
|
|
4
5
|
} = require('./integration-repository-interface');
|
|
5
6
|
const { validateConfigPatch } = require('./config-patch-shared');
|
|
7
|
+
const { integrationNotFoundError } = require('./integration-not-found');
|
|
6
8
|
|
|
7
9
|
/**
|
|
8
10
|
* MongoDB Integration Repository Adapter
|
|
@@ -25,6 +27,25 @@ class IntegrationRepositoryMongo extends IntegrationRepositoryInterface {
|
|
|
25
27
|
this.prisma = prisma;
|
|
26
28
|
}
|
|
27
29
|
|
|
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
|
+
|
|
28
49
|
/**
|
|
29
50
|
* Find all integrations for a user
|
|
30
51
|
* Replaces: IntegrationModel.find({ user: userId }).populate('entities')
|
|
@@ -41,16 +62,32 @@ class IntegrationRepositoryMongo extends IntegrationRepositoryInterface {
|
|
|
41
62
|
});
|
|
42
63
|
|
|
43
64
|
// Map to domain objects (maintains same API)
|
|
44
|
-
return integrations.map((integration) => (
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
65
|
+
return integrations.map((integration) => (this._mapIntegration(integration)));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Find integrations matching any of the owner/organization pairs, in one
|
|
70
|
+
* query. `organizationId: null` matches rows with no organization. The
|
|
71
|
+
* caller (a use case) decides which pairs; this only runs them.
|
|
72
|
+
*
|
|
73
|
+
* @param {Array<{userId: string, organizationId: (string|null)}>} clauses
|
|
74
|
+
* @returns {Promise<Array>} Array of integration objects
|
|
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)));
|
|
54
91
|
}
|
|
55
92
|
|
|
56
93
|
/**
|
|
@@ -61,47 +98,23 @@ class IntegrationRepositoryMongo extends IntegrationRepositoryInterface {
|
|
|
61
98
|
* @param {string} [filter.status] - Integration status
|
|
62
99
|
* @returns {Promise<Array>} Array of integration objects (possibly empty)
|
|
63
100
|
*/
|
|
64
|
-
async findIntegrations({ type, status
|
|
101
|
+
async findIntegrations({ type, status } = {}) {
|
|
65
102
|
const where = {};
|
|
103
|
+
if (type) {
|
|
104
|
+
where.config = { path: ['type'], equals: type };
|
|
105
|
+
}
|
|
66
106
|
if (status) {
|
|
67
107
|
where.status = status;
|
|
68
108
|
}
|
|
69
|
-
if (limit && after !== undefined && after !== null) {
|
|
70
|
-
where.id = { gt: after };
|
|
71
|
-
}
|
|
72
|
-
if (type) {
|
|
73
|
-
// Prisma's MongoDB connector has no JSON path filter (`config: { path }` is refused), so the ids of the
|
|
74
|
-
// matching rows come from a raw find on config.type, then the rows load as usual.
|
|
75
|
-
const filter = { 'config.type': type };
|
|
76
|
-
if (status) filter.status = status;
|
|
77
|
-
if (where.id) filter._id = { $gt: { $oid: String(after) } };
|
|
78
|
-
const found = await this.prisma.integration.findRaw({
|
|
79
|
-
filter,
|
|
80
|
-
options: { projection: { _id: 1 }, sort: { _id: 1 }, ...(limit ? { limit } : {}) },
|
|
81
|
-
});
|
|
82
|
-
const ids = found.map((doc) => doc._id?.$oid ?? String(doc._id));
|
|
83
|
-
if (!ids.length) return [];
|
|
84
|
-
where.id = { in: ids };
|
|
85
|
-
}
|
|
86
109
|
|
|
87
110
|
const integrations = await this.prisma.integration.findMany({
|
|
88
111
|
where,
|
|
89
112
|
include: {
|
|
90
113
|
entities: true,
|
|
91
114
|
},
|
|
92
|
-
...(limit ? { orderBy: { id: 'asc' }, take: limit } : {}),
|
|
93
115
|
});
|
|
94
116
|
|
|
95
|
-
return integrations.map((integration) => (
|
|
96
|
-
id: integration.id,
|
|
97
|
-
entitiesIds: integration.entities.map((e) => e.id),
|
|
98
|
-
userId: integration.userId,
|
|
99
|
-
config: integration.config,
|
|
100
|
-
version: integration.version,
|
|
101
|
-
status: integration.status,
|
|
102
|
-
messages: integration.messages,
|
|
103
|
-
createdAt: integration.createdAt,
|
|
104
|
-
}));
|
|
117
|
+
return integrations.map((integration) => (this._mapIntegration(integration)));
|
|
105
118
|
}
|
|
106
119
|
|
|
107
120
|
/**
|
|
@@ -180,15 +193,7 @@ class IntegrationRepositoryMongo extends IntegrationRepositoryInterface {
|
|
|
180
193
|
throw new Error(`Integration with name ${name} not found`);
|
|
181
194
|
}
|
|
182
195
|
|
|
183
|
-
return
|
|
184
|
-
id: integration.id,
|
|
185
|
-
entitiesIds: integration.entities.map((e) => e.id),
|
|
186
|
-
userId: integration.userId,
|
|
187
|
-
config: integration.config,
|
|
188
|
-
version: integration.version,
|
|
189
|
-
status: integration.status,
|
|
190
|
-
messages: integration.messages,
|
|
191
|
-
};
|
|
196
|
+
return this._mapIntegration(integration);
|
|
192
197
|
}
|
|
193
198
|
|
|
194
199
|
/**
|
|
@@ -207,18 +212,10 @@ class IntegrationRepositoryMongo extends IntegrationRepositoryInterface {
|
|
|
207
212
|
});
|
|
208
213
|
|
|
209
214
|
if (!integration) {
|
|
210
|
-
throw
|
|
215
|
+
throw integrationNotFoundError(id);
|
|
211
216
|
}
|
|
212
217
|
|
|
213
|
-
return
|
|
214
|
-
id: integration.id,
|
|
215
|
-
entitiesIds: integration.entities.map((e) => e.id),
|
|
216
|
-
userId: integration.userId,
|
|
217
|
-
config: integration.config,
|
|
218
|
-
version: integration.version,
|
|
219
|
-
status: integration.status,
|
|
220
|
-
messages: integration.messages,
|
|
221
|
-
};
|
|
218
|
+
return this._mapIntegration(integration);
|
|
222
219
|
}
|
|
223
220
|
|
|
224
221
|
/**
|
|
@@ -305,15 +302,7 @@ class IntegrationRepositoryMongo extends IntegrationRepositoryInterface {
|
|
|
305
302
|
},
|
|
306
303
|
});
|
|
307
304
|
|
|
308
|
-
return integrations.map((integration) => (
|
|
309
|
-
id: integration.id,
|
|
310
|
-
entitiesIds: integration.entities.map((e) => e.id),
|
|
311
|
-
userId: integration.userId,
|
|
312
|
-
config: integration.config,
|
|
313
|
-
version: integration.version,
|
|
314
|
-
status: integration.status,
|
|
315
|
-
messages: integration.messages,
|
|
316
|
-
}));
|
|
305
|
+
return integrations.map((integration) => (this._mapIntegration(integration)));
|
|
317
306
|
}
|
|
318
307
|
|
|
319
308
|
/**
|
|
@@ -325,13 +314,16 @@ class IntegrationRepositoryMongo extends IntegrationRepositoryInterface {
|
|
|
325
314
|
* @param {Array<string>} entities - Array of entity IDs (MongoDB ObjectIds)
|
|
326
315
|
* @param {string} userId - User ID (MongoDB ObjectId)
|
|
327
316
|
* @param {Object} config - Integration configuration
|
|
317
|
+
* @param {{organizationId?: (string|null)}} [tenancy] - the organization the
|
|
318
|
+
* integration is created in (ADR-056 §3.6)
|
|
328
319
|
* @returns {Promise<Object>} Created integration object
|
|
329
320
|
*/
|
|
330
|
-
async createIntegration(entities, userId, config) {
|
|
321
|
+
async createIntegration(entities, userId, config, tenancy = {}) {
|
|
331
322
|
const data = {
|
|
332
323
|
config,
|
|
333
324
|
version: '0.0.0',
|
|
334
325
|
userId: userId,
|
|
326
|
+
organizationId: tenancy.organizationId ?? null,
|
|
335
327
|
entityIds: entities,
|
|
336
328
|
};
|
|
337
329
|
|
|
@@ -342,15 +334,7 @@ class IntegrationRepositoryMongo extends IntegrationRepositoryInterface {
|
|
|
342
334
|
},
|
|
343
335
|
});
|
|
344
336
|
|
|
345
|
-
return
|
|
346
|
-
id: integration.id,
|
|
347
|
-
entitiesIds: integration.entities.map((e) => e.id),
|
|
348
|
-
userId: integration.userId,
|
|
349
|
-
config: integration.config,
|
|
350
|
-
version: integration.version,
|
|
351
|
-
status: integration.status,
|
|
352
|
-
messages: integration.messages,
|
|
353
|
-
};
|
|
337
|
+
return this._mapIntegration(integration);
|
|
354
338
|
}
|
|
355
339
|
|
|
356
340
|
/**
|
|
@@ -372,15 +356,7 @@ class IntegrationRepositoryMongo extends IntegrationRepositoryInterface {
|
|
|
372
356
|
return null;
|
|
373
357
|
}
|
|
374
358
|
|
|
375
|
-
return
|
|
376
|
-
id: integration.id,
|
|
377
|
-
entitiesIds: integration.entities.map((e) => e.id),
|
|
378
|
-
userId: integration.userId,
|
|
379
|
-
config: integration.config,
|
|
380
|
-
version: integration.version,
|
|
381
|
-
status: integration.status,
|
|
382
|
-
messages: integration.messages,
|
|
383
|
-
};
|
|
359
|
+
return this._mapIntegration(integration);
|
|
384
360
|
}
|
|
385
361
|
|
|
386
362
|
/**
|
|
@@ -404,15 +380,7 @@ class IntegrationRepositoryMongo extends IntegrationRepositoryInterface {
|
|
|
404
380
|
},
|
|
405
381
|
});
|
|
406
382
|
|
|
407
|
-
return
|
|
408
|
-
id: integration.id,
|
|
409
|
-
entitiesIds: integration.entities.map((e) => e.id),
|
|
410
|
-
userId: integration.userId,
|
|
411
|
-
config: integration.config,
|
|
412
|
-
version: integration.version,
|
|
413
|
-
status: integration.status,
|
|
414
|
-
messages: integration.messages,
|
|
415
|
-
};
|
|
383
|
+
return this._mapIntegration(integration);
|
|
416
384
|
}
|
|
417
385
|
|
|
418
386
|
/**
|
|
@@ -434,7 +402,7 @@ class IntegrationRepositoryMongo extends IntegrationRepositoryInterface {
|
|
|
434
402
|
for (const [key, value] of Object.entries(patch)) {
|
|
435
403
|
$set[`config.${key}`] = value;
|
|
436
404
|
}
|
|
437
|
-
$set.updatedAt =
|
|
405
|
+
$set.updatedAt = new Date();
|
|
438
406
|
|
|
439
407
|
const result = await this.prisma.$runCommandRaw({
|
|
440
408
|
findAndModify: 'Integration',
|