@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,245 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Application layer for integrations published as definitions (ADR-063): publishing definition versions, the catalogue the
|
|
3
|
-
* runtime reads from, and per-installation upgrades. Business rules live here; the repositories only store rows.
|
|
4
|
-
*
|
|
5
|
-
* createDefinitionCatalog() what defineSharedIntegrationClass({ definitions }) reads: get(id, version), latest(id)
|
|
6
|
-
* PublishDefinitionVersion validate (schema + references) → checkBump against the latest published → store
|
|
7
|
-
* DeprecateDefinitionVersion PUBLISHED → DEPRECATED (still runs for its pins, cannot be newly installed)
|
|
8
|
-
* ListPublishedDefinitions the install catalogue: latest published version of each definition
|
|
9
|
-
* GetDefinitionHistory every version of one definition, newest first
|
|
10
|
-
* PlanInstallationUpgrade planUpgrade(installation, pinned, target), without changing anything
|
|
11
|
-
*/
|
|
12
|
-
const { checksum } = require('../canonical');
|
|
13
|
-
const { checkBump, planUpgrade } = require('../versioning');
|
|
14
|
-
|
|
15
|
-
class DefinitionError extends Error {
|
|
16
|
-
constructor(message, { status = 400, code, details } = {}) { super(message); this.name = 'DefinitionError'; this.status = status; this.code = code; this.details = details; }
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const SEMVER = /^(\d+)\.(\d+)\.(\d+)(?:-([\w.]+))?$/;
|
|
20
|
-
function compareVersions(a, b) {
|
|
21
|
-
const x = SEMVER.exec(a), y = SEMVER.exec(b);
|
|
22
|
-
if (!x || !y) return String(a).localeCompare(String(b));
|
|
23
|
-
for (let i = 1; i <= 3; i += 1) if (Number(x[i]) !== Number(y[i])) return Number(x[i]) - Number(y[i]);
|
|
24
|
-
if (x[4] && !y[4]) return -1; // 1.0.0-beta < 1.0.0
|
|
25
|
-
if (!x[4] && y[4]) return 1;
|
|
26
|
-
return String(x[4] || '').localeCompare(String(y[4] || ''));
|
|
27
|
-
}
|
|
28
|
-
const newestFirst = (rows) => [...rows].sort((a, b) => compareVersions(b.version, a.version));
|
|
29
|
-
const SERVABLE = new Set(['PUBLISHED', 'DEPRECATED', 'RETIRED']); // a DRAFT never reaches the runtime
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* The definition store the runtime reads from. Published rows are immutable, so they are cached by id@version for the
|
|
33
|
-
* life of the process; a pinned checksum is verified, so an installation provably runs the document it was pinned to.
|
|
34
|
-
*/
|
|
35
|
-
function createDefinitionCatalog({ repository } = {}) {
|
|
36
|
-
const repo = () => repository || (repository = require('../repositories/definition-version-repository').createDefinitionVersionRepository());
|
|
37
|
-
const cache = new Map();
|
|
38
|
-
return {
|
|
39
|
-
async get(definitionId, version, { checksum: expected } = {}) {
|
|
40
|
-
const key = `${definitionId}@${version}`;
|
|
41
|
-
let row = cache.get(key);
|
|
42
|
-
if (!row) {
|
|
43
|
-
row = await repo().findVersion(definitionId, version);
|
|
44
|
-
if (!row || !SERVABLE.has(row.status)) return null;
|
|
45
|
-
cache.set(key, row);
|
|
46
|
-
}
|
|
47
|
-
if (expected && expected !== row.checksum) throw new DefinitionError(`${key} does not match the pinned checksum`, { status: 409, code: 'CHECKSUM_MISMATCH' });
|
|
48
|
-
return row.document;
|
|
49
|
-
},
|
|
50
|
-
/** The latest PUBLISHED version (not deprecated, not draft): what a new installation pins. */
|
|
51
|
-
async latest(definitionId) {
|
|
52
|
-
const rows = (await repo().listVersions(definitionId)).filter((r) => r.status === 'PUBLISHED');
|
|
53
|
-
return rows.length ? newestFirst(rows)[0].document : null;
|
|
54
|
-
},
|
|
55
|
-
async checksumOf(definitionId, version) {
|
|
56
|
-
return (await repo().findVersion(definitionId, version))?.checksum || null;
|
|
57
|
-
},
|
|
58
|
-
/** The workspace that built a definition, or null for the app's own catalogue. */
|
|
59
|
-
async ownerOf(definitionId) {
|
|
60
|
-
const rows = await repo().listVersions(definitionId);
|
|
61
|
-
return rows[0]?.workspaceId ?? null;
|
|
62
|
-
},
|
|
63
|
-
/** The install catalogue: latest published version of each definition (with `workspaceId`, that workspace's too). */
|
|
64
|
-
list: (context) => new ListPublishedDefinitions({ repository: repo() }).execute(context),
|
|
65
|
-
/** The repository behind this catalogue, for publishing (bundled stores have none). */
|
|
66
|
-
get repository() { return repo(); },
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/** The one answer for an integration id that is not this workspace's, whether it is someone else's or does not exist. */
|
|
71
|
-
const notInWorkspace = (id) => new DefinitionError(`No integration "${id}" in this workspace`, { status: 404, code: 'NOT_FOUND' });
|
|
72
|
-
|
|
73
|
-
class PublishDefinitionVersion {
|
|
74
|
-
/**
|
|
75
|
-
* @param {{ repository, manifests: object, check?: (def, manifests) => { valid, errors, warnings } }} deps
|
|
76
|
-
* check defaults to schema-check's checkDefinition (JSON Schema + references)
|
|
77
|
-
*/
|
|
78
|
-
constructor({ repository, manifests, check, isReserved }) {
|
|
79
|
-
this.repository = repository;
|
|
80
|
-
this.manifests = manifests;
|
|
81
|
-
this.check = check || require('../schema-check').checkDefinition;
|
|
82
|
-
/** Names a definition may not take: the app's registered (code) integrations. */
|
|
83
|
-
this.isReserved = isReserved || (() => false);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/** @returns {Promise<{ version: object, created: boolean, changeLevel: string|null, warnings: array }>} */
|
|
87
|
-
/** @param {{ workspaceId?: string }} p workspaceId: the workspace building its own integration (null: the app's catalogue) */
|
|
88
|
-
async execute({ document, notes, createdBy, publish = true, strict = false, workspaceId = null } = {}) {
|
|
89
|
-
if (!document || typeof document !== 'object') throw new DefinitionError('Send the definition as `document`');
|
|
90
|
-
const result = this.check(document, this.manifests);
|
|
91
|
-
if (!result.valid || (strict && result.warnings.length)) throw new DefinitionError(`${document.id}@${document.version} is not valid`, { code: 'INVALID', details: { errors: result.errors, warnings: result.warnings } });
|
|
92
|
-
|
|
93
|
-
// An id belongs to one owner: the app's catalogue, or one workspace. Nobody publishes over someone else's, nor
|
|
94
|
-
// takes the name of a code integration.
|
|
95
|
-
if (this.isReserved(document.id)) throw new DefinitionError(`"${document.id}" is the name of an integration this app already has`, { status: 409, code: 'ID_TAKEN' });
|
|
96
|
-
const versions = await this.repository.listVersions(document.id);
|
|
97
|
-
const other = versions.find((v) => (v.workspaceId ?? null) !== (workspaceId ?? null));
|
|
98
|
-
if (other) {
|
|
99
|
-
// Another WORKSPACE's id must not be confirmed to this one: answer as a miss, exactly as reading it does
|
|
100
|
-
// (GetOwnDefinition; ADR-052 section 3: a foreign resource is a 404, so ids cannot be probed). The catalogue's
|
|
101
|
-
// ids are public (they are listed), and the admin is not a tenant, so those may say what is wrong.
|
|
102
|
-
if (workspaceId != null && other.workspaceId != null) throw notInWorkspace(document.id);
|
|
103
|
-
throw new DefinitionError(`"${document.id}" is already used by another integration`, { status: 409, code: 'ID_TAKEN' });
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
const sum = checksum(document);
|
|
107
|
-
const existing = await this.repository.findVersion(document.id, document.version);
|
|
108
|
-
if (existing && existing.status !== 'DRAFT') {
|
|
109
|
-
if (existing.checksum === sum) return { version: existing, created: false, changeLevel: existing.changeLevel, warnings: result.warnings };
|
|
110
|
-
throw new DefinitionError(`${document.id}@${document.version} is already ${existing.status.toLowerCase()} and cannot change. Publish a new version.`, { status: 409, code: 'IMMUTABLE' });
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
const published = newestFirst(versions.filter((r) => r.status === 'PUBLISHED' || r.status === 'DEPRECATED'));
|
|
114
|
-
const previous = published.find((r) => r.version !== document.version) || null;
|
|
115
|
-
let changeLevel = null;
|
|
116
|
-
if (previous) {
|
|
117
|
-
if (compareVersions(document.version, previous.version) <= 0) throw new DefinitionError(`${document.version} is not newer than the latest published ${previous.version}`, { status: 409, code: 'NOT_NEWER' });
|
|
118
|
-
const bump = checkBump(previous.document, document);
|
|
119
|
-
if (!bump.ok) throw new DefinitionError(bump.message, { status: 409, code: 'BUMP_TOO_SMALL', details: { required: bump.required, declared: bump.declared, changes: bump.changes } });
|
|
120
|
-
changeLevel = bump.required;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
const row = {
|
|
124
|
-
definitionId: document.id, version: document.version, schemaVersion: document.schemaVersion, checksum: sum, document,
|
|
125
|
-
changeLevel, previousVersion: previous?.version || null, notes: notes ?? null, createdBy: createdBy ?? null, workspaceId: workspaceId ?? null,
|
|
126
|
-
status: publish ? 'PUBLISHED' : 'DRAFT', publishedAt: publish ? new Date() : null,
|
|
127
|
-
};
|
|
128
|
-
if (existing) {
|
|
129
|
-
// A draft may be rewritten, and published, as long as it is still a draft when we write (checked by the
|
|
130
|
-
// database, so two concurrent publishes cannot both win).
|
|
131
|
-
const updated = await this.repository.updateIfStatus(document.id, document.version, 'DRAFT', row);
|
|
132
|
-
if (!updated) throw new DefinitionError(`${document.id}@${document.version} changed while publishing; try again`, { status: 409, code: 'CONFLICT' });
|
|
133
|
-
return { version: updated, created: false, changeLevel, warnings: result.warnings };
|
|
134
|
-
}
|
|
135
|
-
try {
|
|
136
|
-
return { version: await this.repository.createVersion(row), created: true, changeLevel, warnings: result.warnings };
|
|
137
|
-
} catch (e) {
|
|
138
|
-
if (e?.code === 'P2002') {
|
|
139
|
-
// Lost a race to the same version: the same document is the same publish; anything else is a conflict.
|
|
140
|
-
const winner = await this.repository.findVersion(document.id, document.version);
|
|
141
|
-
if (winner?.checksum === sum) return { version: winner, created: false, changeLevel: winner.changeLevel, warnings: result.warnings };
|
|
142
|
-
throw new DefinitionError(`${document.id}@${document.version} was published with different content just now`, { status: 409, code: 'CONFLICT' });
|
|
143
|
-
}
|
|
144
|
-
throw e;
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
class DeprecateDefinitionVersion {
|
|
150
|
-
constructor({ repository }) { this.repository = repository; }
|
|
151
|
-
async execute({ definitionId, version }) {
|
|
152
|
-
const row = await this.repository.updateIfStatus(definitionId, version, 'PUBLISHED', { status: 'DEPRECATED' });
|
|
153
|
-
if (row) return row;
|
|
154
|
-
const existing = await this.repository.findVersion(definitionId, version);
|
|
155
|
-
if (!existing) throw new DefinitionError(`${definitionId}@${version} does not exist`, { status: 404, code: 'NOT_FOUND' });
|
|
156
|
-
throw new DefinitionError(`${definitionId}@${version} is ${existing.status.toLowerCase()}; only a published version can be deprecated`, { status: 409, code: 'WRONG_STATUS' });
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
class ListPublishedDefinitions {
|
|
161
|
-
constructor({ repository }) { this.repository = repository; }
|
|
162
|
-
/**
|
|
163
|
-
* Latest published version of each definition, with what an install screen shows: the app's catalogue, plus the
|
|
164
|
-
* integrations `workspaceId` built (another workspace's are never listed). Without a workspace, every one.
|
|
165
|
-
*/
|
|
166
|
-
async execute({ workspaceId } = {}) {
|
|
167
|
-
const byId = new Map();
|
|
168
|
-
for (const r of await this.repository.listByStatus(['PUBLISHED'])) {
|
|
169
|
-
if (workspaceId && r.workspaceId && r.workspaceId !== String(workspaceId)) continue;
|
|
170
|
-
const cur = byId.get(r.definitionId);
|
|
171
|
-
if (!cur || compareVersions(r.version, cur.version) > 0) byId.set(r.definitionId, r);
|
|
172
|
-
}
|
|
173
|
-
const out = [];
|
|
174
|
-
for (const r of byId.values()) {
|
|
175
|
-
const doc = (await this.repository.findVersion(r.definitionId, r.version)).document;
|
|
176
|
-
out.push({
|
|
177
|
-
id: r.definitionId, version: r.version, checksum: r.checksum, publishedAt: r.publishedAt, workspaceId: r.workspaceId ?? null,
|
|
178
|
-
display: doc.display, modules: Object.fromEntries(Object.entries(doc.modules || {}).map(([k, m]) => [k, m.module])),
|
|
179
|
-
counts: { syncs: (doc.syncs || []).length, links: (doc.links || []).length, workflows: (doc.workflows || []).length, ui: (doc.ui || []).length },
|
|
180
|
-
});
|
|
181
|
-
}
|
|
182
|
-
return out.sort((a, b) => String(a.display?.label || a.id).localeCompare(String(b.display?.label || b.id)));
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
class GetDefinitionHistory {
|
|
187
|
-
constructor({ repository }) { this.repository = repository; }
|
|
188
|
-
async execute({ definitionId }) {
|
|
189
|
-
const rows = newestFirst(await this.repository.listVersions(definitionId));
|
|
190
|
-
if (!rows.length) throw new DefinitionError(`No definition "${definitionId}"`, { status: 404, code: 'NOT_FOUND' });
|
|
191
|
-
return rows.map(({ document, ...meta }) => meta);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
/**
|
|
196
|
-
* The installation an approval decision is about, for someone who may not own it: an approval step names its
|
|
197
|
-
* approvers, and they decide from their own sign-in. Whether this user may decide a particular run is the run's
|
|
198
|
-
* business (decide()); here, only who owns it.
|
|
199
|
-
*/
|
|
200
|
-
/**
|
|
201
|
-
* A workspace's own integration, for its editor: every version (drafts included) and the newest document. Anyone
|
|
202
|
-
* else's, or the app's catalogue, is "not found": the editor exists only for what this workspace built.
|
|
203
|
-
*/
|
|
204
|
-
class GetOwnDefinition {
|
|
205
|
-
constructor({ repository }) { this.repository = repository; }
|
|
206
|
-
async execute({ definitionId, workspaceId }) {
|
|
207
|
-
const rows = newestFirst(await this.repository.listVersions(definitionId));
|
|
208
|
-
if (!rows.length || !workspaceId || rows[0].workspaceId !== String(workspaceId)) throw notInWorkspace(definitionId);
|
|
209
|
-
return { id: definitionId, versions: rows.map(({ document, ...meta }) => meta), document: rows[0].document };
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
class GetIntegrationForDecision {
|
|
214
|
-
constructor({ getIntegrationInstance }) { this.getIntegrationInstance = getIntegrationInstance; }
|
|
215
|
-
async execute(integrationId, userId) {
|
|
216
|
-
const record = await this.getIntegrationInstance.integrationRepository.findIntegrationById(integrationId).catch(() => null);
|
|
217
|
-
if (!record) throw new DefinitionError(`Integration ${integrationId} not found`, { status: 404, code: 'NOT_FOUND' });
|
|
218
|
-
const isOwner = String(record.userId) === String(userId);
|
|
219
|
-
const integration = await this.getIntegrationInstance.execute(integrationId, record.userId);
|
|
220
|
-
return { integration, isOwner };
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
class PlanInstallationUpgrade {
|
|
225
|
-
constructor({ catalog }) { this.catalog = catalog; }
|
|
226
|
-
/** @returns {Promise<{ from, to, allowed, needs, actions, config, changes }>} */
|
|
227
|
-
async execute({ config, toVersion }) {
|
|
228
|
-
const pin = config?.definition;
|
|
229
|
-
if (!pin?.id || !pin?.version) throw new DefinitionError('This installation does not name a definition', { code: 'NO_PIN' });
|
|
230
|
-
const from = await this.catalog.get(pin.id, pin.version);
|
|
231
|
-
const target = toVersion || (await this.catalog.latest(pin.id))?.version;
|
|
232
|
-
if (!target) throw new DefinitionError(`No published version of "${pin.id}"`, { status: 404, code: 'NOT_FOUND' });
|
|
233
|
-
const to = await this.catalog.get(pin.id, target);
|
|
234
|
-
if (!from || !to) throw new DefinitionError(`${pin.id}@${!from ? pin.version : target} is not published`, { status: 404, code: 'NOT_FOUND' });
|
|
235
|
-
const plan = planUpgrade(config, from, to);
|
|
236
|
-
plan.config.definition = { ...plan.config.definition, checksum: await this.catalog.checksumOf(pin.id, target) };
|
|
237
|
-
return { from: pin.version, to: target, ...plan, changes: require('../versioning').diff(from, to).changes };
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
module.exports = {
|
|
242
|
-
DefinitionError, compareVersions, createDefinitionCatalog,
|
|
243
|
-
PublishDefinitionVersion, DeprecateDefinitionVersion, ListPublishedDefinitions, GetDefinitionHistory, PlanInstallationUpgrade,
|
|
244
|
-
GetIntegrationForDecision, GetOwnDefinition,
|
|
245
|
-
};
|