@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
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turning a provider's webhook delivery into the module events a definition listens for. Pure; no I/O.
|
|
3
|
+
*
|
|
4
|
+
* Providers disagree about everything: where the topic is (a body field, a header, two fields combined), whether one
|
|
5
|
+
* POST carries one event or a batch (HubSpot sends an array, QuickBooks nests entities inside notifications), which
|
|
6
|
+
* account it is about, and whether the first request is a handshake to answer synchronously (Slack url_verification,
|
|
7
|
+
* Asana X-Hook-Secret). A manifest says so in its `webhook` block, with the same template expressions definitions
|
|
8
|
+
* use, so none of it is code:
|
|
9
|
+
*
|
|
10
|
+
* "webhook": {
|
|
11
|
+
* "scope": "app", // one URL for every account ("installation": one per install)
|
|
12
|
+
* "batch": "body", // path to the array of events; [outer, inner] for two levels
|
|
13
|
+
* "topic": "{{item.subscriptionType}}", // the provider's topic; a list tries each until one is non-empty
|
|
14
|
+
* "eventId": "{{item.eventId}}", // dedupe id
|
|
15
|
+
* "account": "{{item.portalId}}", // the connected account (entity externalId) the event is about
|
|
16
|
+
* "verify": "verifyWebhookSignature", // optional Api method: ({ headers, body, query }) -> boolean
|
|
17
|
+
* "handshake": { "when": "...", "status": 200, "headers": {...}, "body": {...} },
|
|
18
|
+
* "unwrap": "payload" // a form-encoded field holding the JSON body (Slack interactivity)
|
|
19
|
+
* }
|
|
20
|
+
*
|
|
21
|
+
* and each event says which provider topic it is (`match`, default: the event key) and optionally a condition on the
|
|
22
|
+
* record (`when`): a phone module reports a missed call as call.completed with status missed.
|
|
23
|
+
*
|
|
24
|
+
* Template context: { body, headers, query, item, parent }. `item` is the event (the body itself when there is no
|
|
25
|
+
* batch); `parent` is the outer item of a two-level batch (a QuickBooks notification, which carries the realmId).
|
|
26
|
+
*/
|
|
27
|
+
const { resolve, evaluate, getPath } = require('./expressions');
|
|
28
|
+
|
|
29
|
+
const list = (v) => (v == null ? [] : Array.isArray(v) ? v : [v]);
|
|
30
|
+
const str = (v) => (v == null ? '' : typeof v === 'object' ? JSON.stringify(v) : String(v));
|
|
31
|
+
|
|
32
|
+
/** The first template in `tpls` that renders to a non-empty string. */
|
|
33
|
+
function renderFirst(tpls, ctx) {
|
|
34
|
+
for (const t of list(tpls)) {
|
|
35
|
+
const v = str(resolve(t, ctx)).trim();
|
|
36
|
+
if (v) return v;
|
|
37
|
+
}
|
|
38
|
+
return '';
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Lower-case header names, as Express and Lambda deliver them, so manifests can always write headers.x-shopify-topic. */
|
|
42
|
+
function normaliseHeaders(headers = {}) {
|
|
43
|
+
return Object.fromEntries(Object.entries(headers || {}).map(([k, v]) => [k.toLowerCase(), Array.isArray(v) ? v[0] : v]));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** The delivery as the manifest sees it: lower-case headers, and the JSON body unwrapped from a form field if declared. */
|
|
47
|
+
function prepare(webhook = {}, delivery) {
|
|
48
|
+
let body = delivery.body;
|
|
49
|
+
if (webhook.unwrap && body && typeof body[webhook.unwrap] === 'string') {
|
|
50
|
+
try { body = JSON.parse(body[webhook.unwrap]); } catch { /* not JSON: leave the body as delivered */ }
|
|
51
|
+
}
|
|
52
|
+
return { body, headers: normaliseHeaders(delivery.headers), query: delivery.query || {} };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Split one delivery into events: [{ item, parent }]. */
|
|
56
|
+
function splitBatch(webhook = {}, input) {
|
|
57
|
+
const paths = list(webhook.batch);
|
|
58
|
+
const at = (obj, p) => (p === 'body' ? obj.body : p === '$' ? obj : getPath(obj, '$.' + p));
|
|
59
|
+
if (!paths.length) return [{ item: input.body, parent: null }];
|
|
60
|
+
const outer = at(input, paths[0]);
|
|
61
|
+
const outerItems = Array.isArray(outer) ? outer : outer == null ? [] : [outer];
|
|
62
|
+
if (paths.length === 1) return outerItems.map((item) => ({ item, parent: null }));
|
|
63
|
+
return outerItems.flatMap((parent) => {
|
|
64
|
+
const inner = getPath(parent, '$.' + paths[1]);
|
|
65
|
+
return (Array.isArray(inner) ? inner : inner == null ? [] : [inner]).map((item) => ({ item, parent }));
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Every module event in one delivery.
|
|
71
|
+
* @param {object} manifest the module's capability manifest
|
|
72
|
+
* @param {{ body, headers?, query? }} delivery
|
|
73
|
+
* @returns {Array<{ topic: string, providerTopic: string, eventId: string|null, account: string|null, event: object, record: any }>}
|
|
74
|
+
* topic is the manifest's event key (what definitions name); event is the item the workflow sees as $.event.
|
|
75
|
+
*/
|
|
76
|
+
function eventsIn(manifest, delivery) {
|
|
77
|
+
const webhook = manifest?.webhook;
|
|
78
|
+
if (!webhook) return [];
|
|
79
|
+
const input = prepare(webhook, delivery);
|
|
80
|
+
const out = [];
|
|
81
|
+
for (const { item, parent } of splitBatch(webhook, input)) {
|
|
82
|
+
const ctx = { ...input, item, parent };
|
|
83
|
+
const providerTopic = renderFirst(webhook.topic, ctx);
|
|
84
|
+
if (!providerTopic) continue;
|
|
85
|
+
const eventId = renderFirst(webhook.eventId, ctx) || null;
|
|
86
|
+
const account = webhook.account ? renderFirst(webhook.account, ctx) || null : null;
|
|
87
|
+
for (const [key, ev] of Object.entries(manifest.events || {})) {
|
|
88
|
+
if (ev.delivery && ev.delivery !== 'webhook') continue;
|
|
89
|
+
if ((ev.match ?? key) !== providerTopic) continue;
|
|
90
|
+
const record = ev.record ? getPath(item, '$.' + ev.record) : item;
|
|
91
|
+
if (ev.when && !evaluate(ev.when, { event: item, record, headers: input.headers, parent })) continue;
|
|
92
|
+
out.push({ topic: key, providerTopic, eventId, account, event: item, record });
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return out;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** A handshake to answer at receipt, before anything is queued: { status, headers, body } or null. */
|
|
99
|
+
function handshakeFor(manifest, delivery) {
|
|
100
|
+
const h = manifest?.webhook?.handshake;
|
|
101
|
+
if (!h) return null;
|
|
102
|
+
const ctx = prepare(manifest.webhook, delivery);
|
|
103
|
+
if (!evaluate(h.when, ctx)) return null;
|
|
104
|
+
return { status: h.status || 200, headers: resolve(h.headers || {}, ctx), body: h.body === undefined ? '' : resolve(h.body, ctx) };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Which module of a definition a delivery came from. The URL names it (?module=<module name>); without that, the one
|
|
109
|
+
* module whose manifest recognises an event in the body. Returns module keys of the definition (usually one).
|
|
110
|
+
*/
|
|
111
|
+
function modulesFor(def, manifests, delivery) {
|
|
112
|
+
const named = delivery.query?.module;
|
|
113
|
+
const keys = Object.entries(def.modules || {}).filter(([, m]) => (named ? m.module === named : true)).map(([k]) => k);
|
|
114
|
+
if (named) return keys;
|
|
115
|
+
const recognised = keys.filter((k) => eventsIn(manifests[def.modules[k].module], delivery).length > 0);
|
|
116
|
+
return recognised.length === 1 ? recognised : [];
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
module.exports = { eventsIn, handshakeFor, modulesFor, splitBatch, renderFirst, normaliseHeaders };
|
package/integrations/index.js
CHANGED
|
@@ -457,6 +457,35 @@ class IntegrationBase {
|
|
|
457
457
|
);
|
|
458
458
|
}
|
|
459
459
|
|
|
460
|
+
/**
|
|
461
|
+
* Insert-only mapping write; usable as an atomic claim/lock row.
|
|
462
|
+
* @throws {MappingAlreadyExistsError} If a mapping for sourceId exists
|
|
463
|
+
*/
|
|
464
|
+
async createMapping(sourceId, mapping) {
|
|
465
|
+
if (!sourceId) {
|
|
466
|
+
throw new Error(`sourceId must be set`);
|
|
467
|
+
}
|
|
468
|
+
return await this.integrationMappingRepository.createMapping(
|
|
469
|
+
this.id,
|
|
470
|
+
sourceId,
|
|
471
|
+
mapping
|
|
472
|
+
);
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* Deletes the mapping for sourceId. A missing mapping is not an error
|
|
477
|
+
* (resolves with deletedCount 0).
|
|
478
|
+
*/
|
|
479
|
+
async deleteMapping(sourceId) {
|
|
480
|
+
if (!sourceId) {
|
|
481
|
+
throw new Error(`sourceId must be set`);
|
|
482
|
+
}
|
|
483
|
+
return await this.integrationMappingRepository.deleteMapping(
|
|
484
|
+
this.id,
|
|
485
|
+
sourceId
|
|
486
|
+
);
|
|
487
|
+
}
|
|
488
|
+
|
|
460
489
|
/**
|
|
461
490
|
* CHILDREN CAN OVERRIDE THESE CONFIGURATION METHODS
|
|
462
491
|
*/
|
|
@@ -31,6 +31,9 @@ const {
|
|
|
31
31
|
GetIntegrationInstance,
|
|
32
32
|
} = require('./use-cases/get-integration-instance');
|
|
33
33
|
const { UpdateIntegration } = require('./use-cases/update-integration');
|
|
34
|
+
const {
|
|
35
|
+
getModulesDefinitionFromIntegrationClasses,
|
|
36
|
+
} = require('./utils/map-integration-dto');
|
|
34
37
|
const {
|
|
35
38
|
GetModuleInstanceFromType,
|
|
36
39
|
} = require('../modules/use-cases/get-module-instance-from-type');
|
|
@@ -51,51 +54,47 @@ const {
|
|
|
51
54
|
const {
|
|
52
55
|
createUserRepository,
|
|
53
56
|
} = require('../user/repositories/user-repository-factory');
|
|
57
|
+
const {
|
|
58
|
+
GetUserFromBearerToken,
|
|
59
|
+
} = require('../user/use-cases/get-user-from-bearer-token');
|
|
60
|
+
const {
|
|
61
|
+
GetUserFromXFriggHeaders,
|
|
62
|
+
} = require('../user/use-cases/get-user-from-x-frigg-headers');
|
|
63
|
+
const {
|
|
64
|
+
GetUserFromAdopterJwt,
|
|
65
|
+
} = require('../user/use-cases/get-user-from-adopter-jwt');
|
|
54
66
|
const {
|
|
55
67
|
AuthenticateWithSharedSecret,
|
|
56
68
|
} = require('../user/use-cases/authenticate-with-shared-secret');
|
|
57
69
|
const { AuthenticateUser } = require('../user/use-cases/authenticate-user');
|
|
58
|
-
const {
|
|
59
|
-
buildUserAuthentication,
|
|
60
|
-
} = require('../user/create-user-authentication');
|
|
61
70
|
const {
|
|
62
71
|
ProcessAuthorizationCallback,
|
|
63
72
|
} = require('../modules/use-cases/process-authorization-callback');
|
|
64
|
-
const { createScopePolicy } = require('../modules/scope-policy');
|
|
65
|
-
const {
|
|
66
|
-
AuthorizeEntityForUser,
|
|
67
|
-
} = require('../modules/use-cases/authorize-entity-for-user');
|
|
68
|
-
const {
|
|
69
|
-
CreateEntityFromCredential,
|
|
70
|
-
} = require('../modules/use-cases/create-entity-from-credential');
|
|
71
|
-
const {
|
|
72
|
-
ResolveIntegrationOwnership,
|
|
73
|
-
} = require('./use-cases/resolve-integration-ownership');
|
|
74
|
-
const { GetOwnedIntegrationRecord } = require('./use-cases/integration-operations-for-user');
|
|
75
73
|
|
|
76
74
|
const log = getLogger('frigg.integrations');
|
|
77
75
|
|
|
78
76
|
function createIntegrationRouter() {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
const {
|
|
82
|
-
integrations: integrationClasses,
|
|
83
|
-
userConfig,
|
|
84
|
-
moduleDefinitions,
|
|
85
|
-
ownership,
|
|
86
|
-
entityTypeRegistry,
|
|
87
|
-
} = loadAppDefinition();
|
|
88
|
-
// Ownership by scope (ADR-056 §3): one object every use case asks.
|
|
89
|
-
const scopePolicy = createScopePolicy({ ownership, entityTypeRegistry });
|
|
77
|
+
const { integrations: integrationClasses, userConfig } =
|
|
78
|
+
loadAppDefinition();
|
|
90
79
|
const moduleRepository = createModuleRepository();
|
|
91
80
|
const integrationRepository = createIntegrationRepository();
|
|
92
81
|
const credentialRepository = createCredentialRepository();
|
|
93
82
|
const userRepository = createUserRepository();
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
}
|
|
83
|
+
|
|
84
|
+
const getUserFromBearerToken = new GetUserFromBearerToken({
|
|
85
|
+
userRepository,
|
|
86
|
+
userConfig,
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
const getUserFromXFriggHeaders = new GetUserFromXFriggHeaders({
|
|
90
|
+
userRepository,
|
|
91
|
+
userConfig,
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
const getUserFromAdopterJwt = new GetUserFromAdopterJwt({
|
|
95
|
+
userRepository,
|
|
96
|
+
userConfig,
|
|
97
|
+
});
|
|
99
98
|
|
|
100
99
|
const authenticateWithSharedSecret = new AuthenticateWithSharedSecret();
|
|
101
100
|
|
|
@@ -109,7 +108,8 @@ function createIntegrationRouter() {
|
|
|
109
108
|
|
|
110
109
|
const moduleFactory = new ModuleFactory({
|
|
111
110
|
moduleRepository,
|
|
112
|
-
moduleDefinitions
|
|
111
|
+
moduleDefinitions:
|
|
112
|
+
getModulesDefinitionFromIntegrationClasses(integrationClasses),
|
|
113
113
|
});
|
|
114
114
|
const deleteIntegrationForUser = new DeleteIntegrationForUser({
|
|
115
115
|
integrationRepository,
|
|
@@ -122,19 +122,6 @@ function createIntegrationRouter() {
|
|
|
122
122
|
integrationClasses,
|
|
123
123
|
moduleFactory,
|
|
124
124
|
moduleRepository,
|
|
125
|
-
scopePolicy,
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
// Decides who owns a new integration and which entities the caller may bind
|
|
129
|
-
// (the v1 create used to check neither), and who may reach an existing one.
|
|
130
|
-
const resolveIntegrationOwnership = new ResolveIntegrationOwnership({
|
|
131
|
-
moduleRepository,
|
|
132
|
-
integrationClasses,
|
|
133
|
-
scopePolicy,
|
|
134
|
-
});
|
|
135
|
-
const getOwnedIntegrationRecord = new GetOwnedIntegrationRecord({
|
|
136
|
-
integrationRepository,
|
|
137
|
-
scopePolicy,
|
|
138
125
|
});
|
|
139
126
|
|
|
140
127
|
const getCredentialForUser = new GetCredentialForUser({
|
|
@@ -149,8 +136,8 @@ function createIntegrationRouter() {
|
|
|
149
136
|
|
|
150
137
|
const getEntitiesForUser = new GetEntitiesForUser({
|
|
151
138
|
moduleRepository,
|
|
152
|
-
moduleDefinitions
|
|
153
|
-
|
|
139
|
+
moduleDefinitions:
|
|
140
|
+
getModulesDefinitionFromIntegrationClasses(integrationClasses),
|
|
154
141
|
});
|
|
155
142
|
|
|
156
143
|
const getIntegrationInstance = new GetIntegrationInstance({
|
|
@@ -166,65 +153,54 @@ function createIntegrationRouter() {
|
|
|
166
153
|
});
|
|
167
154
|
|
|
168
155
|
const getModuleInstanceFromType = new GetModuleInstanceFromType({
|
|
169
|
-
moduleDefinitions
|
|
156
|
+
moduleDefinitions:
|
|
157
|
+
getModulesDefinitionFromIntegrationClasses(integrationClasses),
|
|
170
158
|
});
|
|
171
159
|
|
|
172
160
|
const getEntityOptionsByType = new GetEntityOptionsByType({
|
|
173
|
-
moduleDefinitions
|
|
161
|
+
moduleDefinitions:
|
|
162
|
+
getModulesDefinitionFromIntegrationClasses(integrationClasses),
|
|
174
163
|
});
|
|
175
164
|
|
|
176
165
|
const testModuleAuth = new TestModuleAuth({
|
|
177
166
|
moduleRepository,
|
|
178
|
-
moduleDefinitions
|
|
179
|
-
|
|
167
|
+
moduleDefinitions:
|
|
168
|
+
getModulesDefinitionFromIntegrationClasses(integrationClasses),
|
|
180
169
|
});
|
|
181
170
|
|
|
182
171
|
const getModule = new GetModule({
|
|
183
172
|
moduleRepository,
|
|
184
|
-
moduleDefinitions
|
|
185
|
-
|
|
173
|
+
moduleDefinitions:
|
|
174
|
+
getModulesDefinitionFromIntegrationClasses(integrationClasses),
|
|
186
175
|
});
|
|
187
176
|
|
|
188
177
|
const getEntityOptionsById = new GetEntityOptionsById({
|
|
189
178
|
moduleRepository,
|
|
190
|
-
moduleDefinitions
|
|
191
|
-
|
|
179
|
+
moduleDefinitions:
|
|
180
|
+
getModulesDefinitionFromIntegrationClasses(integrationClasses),
|
|
192
181
|
});
|
|
193
182
|
|
|
194
183
|
const refreshEntityOptions = new RefreshEntityOptions({
|
|
195
184
|
moduleRepository,
|
|
196
|
-
moduleDefinitions
|
|
197
|
-
|
|
185
|
+
moduleDefinitions:
|
|
186
|
+
getModulesDefinitionFromIntegrationClasses(integrationClasses),
|
|
198
187
|
});
|
|
199
188
|
|
|
200
189
|
const getPossibleIntegrations = new GetPossibleIntegrations({
|
|
201
190
|
integrationClasses,
|
|
202
191
|
});
|
|
203
192
|
|
|
204
|
-
// The owner of what an authorization creates follows its scope (ADR-056 §3.2).
|
|
205
193
|
const processAuthorizationCallback = new ProcessAuthorizationCallback({
|
|
206
194
|
moduleRepository,
|
|
207
195
|
credentialRepository,
|
|
208
196
|
integrationRepository,
|
|
209
|
-
moduleDefinitions
|
|
210
|
-
|
|
211
|
-
const authorizeEntityForUser = new AuthorizeEntityForUser({
|
|
212
|
-
scopePolicy,
|
|
213
|
-
processAuthorizationCallback,
|
|
214
|
-
});
|
|
215
|
-
// v1 POST /api/entity: same owner resolution as /api/authorize.
|
|
216
|
-
const createEntityFromCredential = new CreateEntityFromCredential({
|
|
217
|
-
authorizeEntityForUser,
|
|
218
|
-
processAuthorizationCallback,
|
|
219
|
-
credentialRepository,
|
|
220
|
-
moduleDefinitions,
|
|
197
|
+
moduleDefinitions:
|
|
198
|
+
getModulesDefinitionFromIntegrationClasses(integrationClasses),
|
|
221
199
|
});
|
|
222
200
|
|
|
223
201
|
const router = express();
|
|
224
202
|
|
|
225
203
|
setIntegrationRoutes(router, authenticateUser, {
|
|
226
|
-
resolveIntegrationOwnership,
|
|
227
|
-
getOwnedIntegrationRecord,
|
|
228
204
|
createIntegration,
|
|
229
205
|
deleteIntegrationForUser,
|
|
230
206
|
getIntegrationsForUser,
|
|
@@ -233,6 +209,11 @@ function createIntegrationRouter() {
|
|
|
233
209
|
updateIntegration,
|
|
234
210
|
getPossibleIntegrations,
|
|
235
211
|
});
|
|
212
|
+
// Data-defined integrations (ADR-063): only mounted when the app registers a data-defined class.
|
|
213
|
+
require('./data-defined/router').setDataDefinedRoutes(router, authenticateUser, {
|
|
214
|
+
getIntegrationInstance,
|
|
215
|
+
integrationClasses,
|
|
216
|
+
});
|
|
236
217
|
setEntityRoutes(router, authenticateUser, {
|
|
237
218
|
getCredentialForUser,
|
|
238
219
|
getModuleInstanceFromType,
|
|
@@ -241,8 +222,7 @@ function createIntegrationRouter() {
|
|
|
241
222
|
getModule,
|
|
242
223
|
getEntityOptionsById,
|
|
243
224
|
refreshEntityOptions,
|
|
244
|
-
|
|
245
|
-
createEntityFromCredential,
|
|
225
|
+
processAuthorizationCallback,
|
|
246
226
|
});
|
|
247
227
|
return router;
|
|
248
228
|
}
|
|
@@ -279,8 +259,6 @@ function checkRequiredParams(params, requiredKeys) {
|
|
|
279
259
|
*/
|
|
280
260
|
function setIntegrationRoutes(router, authenticateUser, useCases) {
|
|
281
261
|
const {
|
|
282
|
-
resolveIntegrationOwnership,
|
|
283
|
-
getOwnedIntegrationRecord,
|
|
284
262
|
createIntegration,
|
|
285
263
|
deleteIntegrationForUser,
|
|
286
264
|
getIntegrationsForUser,
|
|
@@ -289,22 +267,15 @@ function setIntegrationRoutes(router, authenticateUser, useCases) {
|
|
|
289
267
|
updateIntegration,
|
|
290
268
|
getPossibleIntegrations,
|
|
291
269
|
} = useCases;
|
|
292
|
-
|
|
293
|
-
// The owner of an integration the caller may reach (404 otherwise), which is
|
|
294
|
-
// what the id-and-owner use cases below expect. `manage: true` is for
|
|
295
|
-
// changing or deleting: an organization's integration needs an admin (403).
|
|
296
|
-
const ownerOf = async (integrationId, user, options) =>
|
|
297
|
-
(await getOwnedIntegrationRecord.execute(integrationId, user, options))
|
|
298
|
-
.userId;
|
|
299
|
-
|
|
300
270
|
router.route('/api/integrations').get(
|
|
301
271
|
catchAsyncError(async (req, res) => {
|
|
302
272
|
const user = await authenticateUser.execute(req);
|
|
303
|
-
const
|
|
273
|
+
const userId = user.getId();
|
|
274
|
+
const integrations = await getIntegrationsForUser.execute(userId);
|
|
304
275
|
const results = {
|
|
305
276
|
entities: {
|
|
306
277
|
options: await getPossibleIntegrations.execute(),
|
|
307
|
-
authorized: await getEntitiesForUser.execute(
|
|
278
|
+
authorized: await getEntitiesForUser.execute(userId),
|
|
308
279
|
},
|
|
309
280
|
integrations: integrations,
|
|
310
281
|
};
|
|
@@ -316,6 +287,7 @@ function setIntegrationRoutes(router, authenticateUser, useCases) {
|
|
|
316
287
|
router.route('/api/integrations').post(
|
|
317
288
|
catchAsyncError(async (req, res) => {
|
|
318
289
|
const user = await authenticateUser.execute(req);
|
|
290
|
+
const userId = user.getId();
|
|
319
291
|
const params = checkRequiredParams(req.body, [
|
|
320
292
|
'entities',
|
|
321
293
|
'config',
|
|
@@ -323,17 +295,10 @@ function setIntegrationRoutes(router, authenticateUser, useCases) {
|
|
|
323
295
|
|
|
324
296
|
get(params.config, 'type');
|
|
325
297
|
|
|
326
|
-
const { ownerId, organizationId, adoptUnscoped } =
|
|
327
|
-
await resolveIntegrationOwnership.execute({
|
|
328
|
-
entityIds: params.entities,
|
|
329
|
-
config: params.config,
|
|
330
|
-
user,
|
|
331
|
-
});
|
|
332
298
|
const integration = await createIntegration.execute(
|
|
333
299
|
params.entities,
|
|
334
|
-
|
|
335
|
-
params.config
|
|
336
|
-
{ organizationId, adoptUnscoped }
|
|
300
|
+
userId,
|
|
301
|
+
params.config
|
|
337
302
|
);
|
|
338
303
|
|
|
339
304
|
res.status(201).json(integration);
|
|
@@ -343,11 +308,12 @@ function setIntegrationRoutes(router, authenticateUser, useCases) {
|
|
|
343
308
|
router.route('/api/integrations/:integrationId').patch(
|
|
344
309
|
catchAsyncError(async (req, res) => {
|
|
345
310
|
const user = await authenticateUser.execute(req);
|
|
311
|
+
const userId = user.getId();
|
|
346
312
|
const params = checkRequiredParams(req.body, ['config']);
|
|
347
313
|
|
|
348
314
|
const integration = await updateIntegration.execute(
|
|
349
315
|
req.params.integrationId,
|
|
350
|
-
|
|
316
|
+
userId,
|
|
351
317
|
params.config
|
|
352
318
|
);
|
|
353
319
|
res.json(integration);
|
|
@@ -360,7 +326,7 @@ function setIntegrationRoutes(router, authenticateUser, useCases) {
|
|
|
360
326
|
const params = checkRequiredParams(req.params, ['integrationId']);
|
|
361
327
|
await deleteIntegrationForUser.execute(
|
|
362
328
|
params.integrationId,
|
|
363
|
-
|
|
329
|
+
user.getId()
|
|
364
330
|
);
|
|
365
331
|
res.status(204).json({});
|
|
366
332
|
})
|
|
@@ -372,7 +338,7 @@ function setIntegrationRoutes(router, authenticateUser, useCases) {
|
|
|
372
338
|
const params = checkRequiredParams(req.params, ['integrationId']);
|
|
373
339
|
const integration = await getIntegrationInstance.execute(
|
|
374
340
|
params.integrationId,
|
|
375
|
-
|
|
341
|
+
user.getId()
|
|
376
342
|
);
|
|
377
343
|
res.json(await integration.send('GET_CONFIG_OPTIONS'));
|
|
378
344
|
})
|
|
@@ -388,7 +354,7 @@ function setIntegrationRoutes(router, authenticateUser, useCases) {
|
|
|
388
354
|
]);
|
|
389
355
|
const integration = await getIntegrationInstance.execute(
|
|
390
356
|
params.integrationId,
|
|
391
|
-
|
|
357
|
+
user.getId()
|
|
392
358
|
);
|
|
393
359
|
|
|
394
360
|
res.json(
|
|
@@ -402,7 +368,7 @@ function setIntegrationRoutes(router, authenticateUser, useCases) {
|
|
|
402
368
|
const params = checkRequiredParams(req.params, ['integrationId']);
|
|
403
369
|
const integration = await getIntegrationInstance.execute(
|
|
404
370
|
params.integrationId,
|
|
405
|
-
|
|
371
|
+
user.getId()
|
|
406
372
|
);
|
|
407
373
|
res.json(await integration.send('GET_USER_ACTIONS', req.body));
|
|
408
374
|
})
|
|
@@ -419,7 +385,7 @@ function setIntegrationRoutes(router, authenticateUser, useCases) {
|
|
|
419
385
|
]);
|
|
420
386
|
const integration = await getIntegrationInstance.execute(
|
|
421
387
|
params.integrationId,
|
|
422
|
-
|
|
388
|
+
user.getId()
|
|
423
389
|
);
|
|
424
390
|
|
|
425
391
|
res.json(
|
|
@@ -444,7 +410,7 @@ function setIntegrationRoutes(router, authenticateUser, useCases) {
|
|
|
444
410
|
]);
|
|
445
411
|
const integration = await getIntegrationInstance.execute(
|
|
446
412
|
params.integrationId,
|
|
447
|
-
|
|
413
|
+
user.getId()
|
|
448
414
|
);
|
|
449
415
|
|
|
450
416
|
res.json(
|
|
@@ -465,7 +431,7 @@ function setIntegrationRoutes(router, authenticateUser, useCases) {
|
|
|
465
431
|
]);
|
|
466
432
|
const integration = await getIntegrationInstance.execute(
|
|
467
433
|
params.integrationId,
|
|
468
|
-
|
|
434
|
+
user.getId()
|
|
469
435
|
);
|
|
470
436
|
res.json(await integration.send(params.actionId, req.body));
|
|
471
437
|
})
|
|
@@ -482,7 +448,7 @@ function setIntegrationRoutes(router, authenticateUser, useCases) {
|
|
|
482
448
|
const params = checkRequiredParams(req.params, ['integrationId']);
|
|
483
449
|
const integration = await getIntegrationInstance.execute(
|
|
484
450
|
params.integrationId,
|
|
485
|
-
|
|
451
|
+
user.getId()
|
|
486
452
|
);
|
|
487
453
|
|
|
488
454
|
// We could perhaps augment router with dynamic options? Haven't decided yet, but here may be the place
|
|
@@ -502,7 +468,7 @@ function setIntegrationRoutes(router, authenticateUser, useCases) {
|
|
|
502
468
|
const params = checkRequiredParams(req.params, ['integrationId']);
|
|
503
469
|
const instance = await getIntegrationInstance.execute(
|
|
504
470
|
params.integrationId,
|
|
505
|
-
|
|
471
|
+
user.getId()
|
|
506
472
|
);
|
|
507
473
|
|
|
508
474
|
if (!instance) {
|
|
@@ -540,21 +506,16 @@ function setEntityRoutes(router, authenticateUser, useCases) {
|
|
|
540
506
|
getModule,
|
|
541
507
|
getEntityOptionsById,
|
|
542
508
|
refreshEntityOptions,
|
|
543
|
-
|
|
544
|
-
createEntityFromCredential,
|
|
509
|
+
processAuthorizationCallback,
|
|
545
510
|
} = useCases;
|
|
546
511
|
|
|
547
512
|
router.route('/api/authorize').get(
|
|
548
513
|
catchAsyncError(async (req, res) => {
|
|
549
514
|
const user = await authenticateUser.execute(req);
|
|
515
|
+
const userId = user.getId();
|
|
550
516
|
const params = checkRequiredParams(req.query, ['entityType']);
|
|
551
|
-
// The owner POST stores under, so GET and POST describe one connection.
|
|
552
|
-
const { ownerId } = authorizeEntityForUser.resolveOwnership({
|
|
553
|
-
user,
|
|
554
|
-
entityType: params.entityType,
|
|
555
|
-
});
|
|
556
517
|
const module = await getModuleInstanceFromType.execute(
|
|
557
|
-
|
|
518
|
+
userId,
|
|
558
519
|
params.entityType,
|
|
559
520
|
{ state: req.query.state }
|
|
560
521
|
);
|
|
@@ -573,15 +534,11 @@ function setEntityRoutes(router, authenticateUser, useCases) {
|
|
|
573
534
|
router.route('/api/authorize').post(
|
|
574
535
|
catchAsyncError(async (req, res) => {
|
|
575
536
|
const user = await authenticateUser.execute(req);
|
|
537
|
+
const userId = user.getId();
|
|
576
538
|
const params = checkRequiredParams(req.body, [
|
|
577
539
|
'entityType',
|
|
578
540
|
'data',
|
|
579
541
|
]);
|
|
580
|
-
const ownership = authorizeEntityForUser.resolveOwnership({
|
|
581
|
-
user,
|
|
582
|
-
entityType: params.entityType,
|
|
583
|
-
});
|
|
584
|
-
const userId = ownership.ownerId;
|
|
585
542
|
|
|
586
543
|
const dataKeys =
|
|
587
544
|
params.data && typeof params.data === 'object'
|
|
@@ -593,13 +550,11 @@ function setEntityRoutes(router, authenticateUser, useCases) {
|
|
|
593
550
|
dataKeys,
|
|
594
551
|
});
|
|
595
552
|
|
|
596
|
-
const
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
ownership,
|
|
602
|
-
});
|
|
553
|
+
const entityDetails = await processAuthorizationCallback.execute(
|
|
554
|
+
userId,
|
|
555
|
+
params.entityType,
|
|
556
|
+
params.data
|
|
557
|
+
);
|
|
603
558
|
|
|
604
559
|
log.info('Entity authorized', {
|
|
605
560
|
eventName: 'frigg.integrations.authorized',
|
|
@@ -616,20 +571,35 @@ function setEntityRoutes(router, authenticateUser, useCases) {
|
|
|
616
571
|
router.route('/api/entity').post(
|
|
617
572
|
catchAsyncError(async (req, res) => {
|
|
618
573
|
const user = await authenticateUser.execute(req);
|
|
574
|
+
const userId = user.getId();
|
|
619
575
|
const params = checkRequiredParams(req.body, [
|
|
620
576
|
'entityType',
|
|
621
577
|
'data',
|
|
622
578
|
]);
|
|
623
579
|
checkRequiredParams(req.body.data, ['credential_id']);
|
|
624
580
|
|
|
625
|
-
//
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
581
|
+
// May want to pass along the user ID as well so credential ID's can't be fished???
|
|
582
|
+
const credential = await getCredentialForUser.execute(
|
|
583
|
+
params.data.credential_id,
|
|
584
|
+
userId
|
|
585
|
+
);
|
|
586
|
+
|
|
587
|
+
if (!credential) {
|
|
588
|
+
throw Boom.badRequest('Invalid credential ID');
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
const module = await getModuleInstanceFromType.execute(
|
|
592
|
+
userId,
|
|
593
|
+
params.entityType
|
|
594
|
+
);
|
|
595
|
+
const entityDetails = await module.getEntityDetails(
|
|
596
|
+
module.api,
|
|
597
|
+
null,
|
|
598
|
+
null,
|
|
599
|
+
userId
|
|
632
600
|
);
|
|
601
|
+
|
|
602
|
+
res.json(await module.findOrCreateEntity(entityDetails));
|
|
633
603
|
})
|
|
634
604
|
);
|
|
635
605
|
|