@friggframework/core 2.0.0-next.12 → 2.0.0-next.121
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/.eslintrc.json +18 -1
- package/CLAUDE.md +830 -0
- package/README.md +996 -50
- package/admin-scripts/index.js +52 -0
- package/admin-scripts/repositories/admin-script-execution-repository-documentdb.js +21 -0
- package/admin-scripts/repositories/admin-script-execution-repository-factory.js +51 -0
- package/admin-scripts/repositories/admin-script-execution-repository-interface.js +171 -0
- package/admin-scripts/repositories/admin-script-execution-repository-mongo.js +238 -0
- package/admin-scripts/repositories/admin-script-execution-repository-postgres.js +278 -0
- package/admin-scripts/repositories/script-schedule-repository-documentdb.js +21 -0
- package/admin-scripts/repositories/script-schedule-repository-factory.js +51 -0
- package/admin-scripts/repositories/script-schedule-repository-interface.js +108 -0
- package/admin-scripts/repositories/script-schedule-repository-mongo.js +179 -0
- package/admin-scripts/repositories/script-schedule-repository-postgres.js +210 -0
- package/application/commands/README.md +451 -0
- package/application/commands/admin-script-commands.js +359 -0
- package/application/commands/credential-commands.js +262 -0
- package/application/commands/entity-commands.js +294 -0
- package/application/commands/integration-commands.js +419 -0
- package/application/commands/integration-mapping-commands.js +25 -0
- package/application/commands/process-commands.js +135 -0
- package/application/commands/report-commands.js +188 -0
- package/application/commands/scheduler-commands.js +263 -0
- package/application/commands/usage-commands.js +56 -0
- package/application/commands/user-commands.js +312 -0
- package/application/index.js +83 -0
- package/artifacts/repositories/artifact-repository-factory.js +19 -0
- package/artifacts/repositories/artifact-repository-interface.js +27 -0
- package/artifacts/repositories/artifact-repository-local.js +42 -0
- package/artifacts/repositories/artifact-repository-s3.js +61 -0
- package/assertions/index.js +0 -3
- package/core/CLAUDE.md +715 -0
- package/core/Worker.js +55 -24
- package/core/create-handler.js +121 -38
- package/core/index.js +8 -1
- package/core/invocation-scope.js +225 -0
- package/core/parameters-to-env.js +257 -0
- package/core/secrets-to-env.js +7 -6
- package/core/ssm-preload.mjs +34 -0
- package/credential/repositories/credential-active-type.js +32 -0
- package/credential/repositories/credential-repository-documentdb.js +355 -0
- package/credential/repositories/credential-repository-factory.js +54 -0
- package/credential/repositories/credential-repository-interface.js +113 -0
- package/credential/repositories/credential-repository-mongo.js +294 -0
- package/credential/repositories/credential-repository-postgres.js +312 -0
- package/credential/repositories/credential-repository.js +300 -0
- package/credential/use-cases/get-credential-for-user.js +25 -0
- package/credential/use-cases/update-authentication-status.js +15 -0
- package/database/MONGODB_TRANSACTION_FIX.md +198 -0
- package/database/adapters/lambda-invoker.js +97 -0
- package/database/config.js +147 -0
- package/database/documentdb-encryption-service.js +334 -0
- package/database/documentdb-utils.js +196 -0
- package/database/encryption/README.md +863 -0
- package/database/encryption/documentdb-encryption-service.md +3575 -0
- package/database/encryption/encryption-schema-registry.js +404 -0
- package/database/encryption/field-encryption-service.js +254 -0
- package/database/encryption/integration-mapping-encryption.js +97 -0
- package/database/encryption/logger.js +82 -0
- package/database/encryption/prisma-encryption-extension.js +241 -0
- package/database/index.js +21 -21
- package/database/prisma.js +182 -0
- package/database/repositories/health-check-repository-documentdb.js +138 -0
- package/database/repositories/health-check-repository-factory.js +48 -0
- package/database/repositories/health-check-repository-interface.js +82 -0
- package/database/repositories/health-check-repository-mongodb.js +89 -0
- package/database/repositories/health-check-repository-postgres.js +82 -0
- package/database/repositories/migration-status-repository-s3.js +137 -0
- package/database/use-cases/check-database-health-use-case.js +29 -0
- package/database/use-cases/check-database-state-use-case.js +81 -0
- package/database/use-cases/check-encryption-health-use-case.js +83 -0
- package/database/use-cases/get-database-state-via-worker-use-case.js +61 -0
- package/database/use-cases/get-migration-status-use-case.js +93 -0
- package/database/use-cases/resolve-migration-via-worker-use-case.js +49 -0
- package/database/use-cases/run-database-migration-use-case.js +139 -0
- package/database/use-cases/test-encryption-use-case.js +253 -0
- package/database/use-cases/trigger-database-migration-use-case.js +157 -0
- package/database/utils/mongodb-collection-utils.js +94 -0
- package/database/utils/mongodb-schema-init.js +108 -0
- package/database/utils/prisma-runner.js +491 -0
- package/database/utils/prisma-schema-parser.js +182 -0
- package/docs/PROCESS_MANAGEMENT_QUEUE_SPEC.md +517 -0
- package/encrypt/Cryptor.js +34 -168
- package/encrypt/index.js +1 -2
- package/errors/client-safe-error.js +26 -0
- package/errors/fetch-error.js +59 -57
- package/errors/index.js +6 -0
- package/errors/mapping-already-exists-error.js +14 -0
- package/generated/prisma-mongodb/client.d.ts +1 -0
- package/generated/prisma-mongodb/client.js +5 -0
- package/generated/prisma-mongodb/default.d.ts +1 -0
- package/generated/prisma-mongodb/default.js +5 -0
- package/generated/prisma-mongodb/edge.d.ts +1 -0
- package/generated/prisma-mongodb/edge.js +386 -0
- package/generated/prisma-mongodb/index-browser.js +368 -0
- package/generated/prisma-mongodb/index.d.ts +27065 -0
- package/generated/prisma-mongodb/index.js +411 -0
- package/generated/prisma-mongodb/libquery_engine-debian-openssl-3.0.x.so.node +0 -0
- package/generated/prisma-mongodb/libquery_engine-rhel-openssl-3.0.x.so.node +0 -0
- package/generated/prisma-mongodb/package.json +183 -0
- package/generated/prisma-mongodb/runtime/edge-esm.js +35 -0
- package/generated/prisma-mongodb/runtime/edge.js +35 -0
- package/generated/prisma-mongodb/runtime/index-browser.d.ts +370 -0
- package/generated/prisma-mongodb/runtime/index-browser.js +17 -0
- package/generated/prisma-mongodb/runtime/library.d.ts +3982 -0
- package/generated/prisma-mongodb/runtime/library.js +147 -0
- package/generated/prisma-mongodb/runtime/react-native.js +84 -0
- package/generated/prisma-mongodb/runtime/wasm-compiler-edge.js +85 -0
- package/generated/prisma-mongodb/runtime/wasm-engine-edge.js +38 -0
- package/generated/prisma-mongodb/schema.prisma +455 -0
- package/generated/prisma-mongodb/wasm-edge-light-loader.mjs +5 -0
- package/generated/prisma-mongodb/wasm-worker-loader.mjs +5 -0
- package/generated/prisma-mongodb/wasm.d.ts +1 -0
- package/generated/prisma-mongodb/wasm.js +393 -0
- package/generated/prisma-postgresql/client.d.ts +1 -0
- package/generated/prisma-postgresql/client.js +5 -0
- package/generated/prisma-postgresql/default.d.ts +1 -0
- package/generated/prisma-postgresql/default.js +5 -0
- package/generated/prisma-postgresql/edge.d.ts +1 -0
- package/generated/prisma-postgresql/edge.js +407 -0
- package/generated/prisma-postgresql/index-browser.js +389 -0
- package/generated/prisma-postgresql/index.d.ts +29904 -0
- package/generated/prisma-postgresql/index.js +432 -0
- package/generated/prisma-postgresql/libquery_engine-debian-openssl-3.0.x.so.node +0 -0
- package/generated/prisma-postgresql/libquery_engine-rhel-openssl-3.0.x.so.node +0 -0
- package/generated/prisma-postgresql/package.json +183 -0
- package/generated/prisma-postgresql/query_engine_bg.js +2 -0
- package/generated/prisma-postgresql/query_engine_bg.wasm +0 -0
- package/generated/prisma-postgresql/runtime/edge-esm.js +35 -0
- package/generated/prisma-postgresql/runtime/edge.js +35 -0
- package/generated/prisma-postgresql/runtime/index-browser.d.ts +370 -0
- package/generated/prisma-postgresql/runtime/index-browser.js +17 -0
- package/generated/prisma-postgresql/runtime/library.d.ts +3982 -0
- package/generated/prisma-postgresql/runtime/library.js +147 -0
- package/generated/prisma-postgresql/runtime/react-native.js +84 -0
- package/generated/prisma-postgresql/runtime/wasm-compiler-edge.js +85 -0
- package/generated/prisma-postgresql/runtime/wasm-engine-edge.js +38 -0
- package/generated/prisma-postgresql/schema.prisma +436 -0
- package/generated/prisma-postgresql/wasm-edge-light-loader.mjs +5 -0
- package/generated/prisma-postgresql/wasm-worker-loader.mjs +5 -0
- package/generated/prisma-postgresql/wasm.d.ts +1 -0
- package/generated/prisma-postgresql/wasm.js +414 -0
- package/handlers/WEBHOOKS.md +682 -0
- package/handlers/app-definition-loader.js +103 -0
- package/handlers/app-handler-helpers.js +20 -6
- package/handlers/backend-utils.js +272 -46
- package/handlers/database-migration-handler.js +236 -0
- package/handlers/integration-event-dispatcher.js +68 -0
- package/handlers/middleware/admin-auth.js +73 -0
- package/handlers/routers/HEALTHCHECK.md +342 -0
- package/handlers/routers/auth.js +4 -15
- package/handlers/routers/db-migration.handler.js +29 -0
- package/handlers/routers/db-migration.js +329 -0
- package/handlers/routers/health.js +518 -0
- package/handlers/routers/integration-defined-routers.js +85 -10
- package/handlers/routers/integration-webhook-routers.js +87 -0
- package/handlers/routers/user.js +27 -5
- package/handlers/routers/websocket.js +31 -9
- package/handlers/use-cases/check-external-apis-health-use-case.js +81 -0
- package/handlers/use-cases/check-integrations-health-use-case.js +44 -0
- package/handlers/workers/db-migration.js +438 -0
- package/handlers/workers/dlq-processor.js +89 -0
- package/handlers/workers/integration-defined-workers.js +13 -7
- package/index.js +123 -47
- package/infrastructure/scheduler/eventbridge-scheduler-adapter.js +238 -0
- package/infrastructure/scheduler/index.js +33 -0
- package/infrastructure/scheduler/mock-scheduler-adapter.js +143 -0
- package/infrastructure/scheduler/scheduler-service-factory.js +73 -0
- package/infrastructure/scheduler/scheduler-service-interface.js +47 -0
- package/integrations/EXTENSIONS.md +240 -0
- package/integrations/WEBHOOK-QUICKSTART.md +151 -0
- package/integrations/extension.js +254 -0
- package/integrations/index.js +20 -10
- package/integrations/integration-base.js +777 -74
- package/integrations/integration-router.js +399 -180
- package/integrations/options.js +1 -1
- package/integrations/repositories/config-patch-shared.js +43 -0
- package/integrations/repositories/integration-mapping-query-pipeline.js +152 -0
- package/integrations/repositories/integration-mapping-query.js +193 -0
- package/integrations/repositories/integration-mapping-repository-documentdb.js +406 -0
- package/integrations/repositories/integration-mapping-repository-factory.js +57 -0
- package/integrations/repositories/integration-mapping-repository-interface.js +183 -0
- package/integrations/repositories/integration-mapping-repository-mongo.js +284 -0
- package/integrations/repositories/integration-mapping-repository-postgres.js +403 -0
- package/integrations/repositories/integration-mapping-repository.js +185 -0
- package/integrations/repositories/integration-repository-documentdb.js +354 -0
- package/integrations/repositories/integration-repository-factory.js +51 -0
- package/integrations/repositories/integration-repository-interface.js +219 -0
- package/integrations/repositories/integration-repository-mongo.js +502 -0
- package/integrations/repositories/integration-repository-postgres.js +550 -0
- package/integrations/repositories/process-repository-documentdb.js +311 -0
- package/integrations/repositories/process-repository-factory.js +53 -0
- package/integrations/repositories/process-repository-interface.js +136 -0
- package/integrations/repositories/process-repository-mongo.js +262 -0
- package/integrations/repositories/process-repository-postgres.js +380 -0
- package/integrations/repositories/process-update-ops-shared.js +112 -0
- package/integrations/repositories/report-id.js +13 -0
- package/integrations/tests/doubles/config-capturing-integration.js +81 -0
- package/integrations/tests/doubles/dummy-integration-class.js +113 -0
- package/integrations/tests/doubles/test-integration-repository.js +146 -0
- package/integrations/use-cases/add-entities-to-integration.js +92 -0
- package/integrations/use-cases/create-integration.js +253 -0
- package/integrations/use-cases/create-process.js +130 -0
- package/integrations/use-cases/delete-integration-for-user.js +130 -0
- package/integrations/use-cases/find-integration-by-entity-external-id.js +74 -0
- package/integrations/use-cases/find-integration-context-by-external-entity-id.js +76 -0
- package/integrations/use-cases/get-integration-for-user.js +78 -0
- package/integrations/use-cases/get-integration-instance-by-definition.js +67 -0
- package/integrations/use-cases/get-integration-instance.js +83 -0
- package/integrations/use-cases/get-integrations-for-user.js +88 -0
- package/integrations/use-cases/get-possible-integrations.js +27 -0
- package/integrations/use-cases/get-process.js +89 -0
- package/integrations/use-cases/index.js +19 -0
- package/integrations/use-cases/list-integrations-by-entity-external-id.js +46 -0
- package/integrations/use-cases/load-integration-context.js +71 -0
- package/integrations/use-cases/patch-integration-config.js +39 -0
- package/integrations/use-cases/process-errors.js +28 -0
- package/integrations/use-cases/remove-entities-from-integration.js +70 -0
- package/integrations/use-cases/update-integration-config.js +32 -0
- package/integrations/use-cases/update-integration-messages.js +44 -0
- package/integrations/use-cases/update-integration-status.js +32 -0
- package/integrations/use-cases/update-integration.js +92 -0
- package/integrations/use-cases/update-process-metrics.js +222 -0
- package/integrations/use-cases/update-process-state.js +163 -0
- package/integrations/utils/assert-entity-ids-input.js +42 -0
- package/integrations/utils/map-integration-dto.js +37 -0
- package/jest-global-setup-noop.js +3 -0
- package/jest-global-teardown-noop.js +3 -0
- package/jest.config.js +2 -0
- package/logs/.eslintrc.json +26 -1
- package/logs/__fixtures__/events.js +82 -0
- package/logs/__fixtures__/matchers.js +45 -0
- package/logs/__fixtures__/secrets.js +59 -0
- package/logs/__fixtures__/vectors.js +329 -0
- package/logs/__fixtures__/with-env.js +45 -0
- package/logs/context.js +109 -0
- package/logs/contract-keys.js +67 -0
- package/logs/debug-shims.js +46 -0
- package/logs/denied-keys.js +21 -0
- package/logs/index.js +14 -3
- package/logs/jest-logger-setup.js +25 -0
- package/logs/levels.js +71 -0
- package/logs/logger-runtime.js +137 -0
- package/logs/logger.js +134 -46
- package/logs/record.js +276 -0
- package/logs/redact.js +328 -0
- package/logs/serialize.js +329 -0
- package/logs/sinks.js +93 -0
- package/logs/summarize-event.js +163 -0
- package/{module-plugin → modules}/index.js +0 -10
- package/modules/module-factory.js +56 -0
- package/modules/module.js +320 -0
- package/modules/repositories/module-repository-documentdb.js +350 -0
- package/modules/repositories/module-repository-factory.js +40 -0
- package/modules/repositories/module-repository-interface.js +145 -0
- package/modules/repositories/module-repository-mongo.js +436 -0
- package/modules/repositories/module-repository-postgres.js +481 -0
- package/modules/repositories/module-repository.js +369 -0
- package/modules/requester/api-key.js +52 -0
- package/modules/requester/oauth-2.js +561 -0
- package/modules/requester/requester.js +605 -0
- package/{module-plugin → modules}/test/mock-api/api.js +8 -3
- package/{module-plugin → modules}/test/mock-api/definition.js +14 -10
- package/modules/tests/doubles/test-module-factory.js +16 -0
- package/modules/tests/doubles/test-module-repository.js +39 -0
- package/modules/use-cases/get-entities-for-user.js +32 -0
- package/modules/use-cases/get-entity-options-by-id.js +71 -0
- package/modules/use-cases/get-entity-options-by-type.js +34 -0
- package/modules/use-cases/get-module-instance-from-type.js +34 -0
- package/modules/use-cases/get-module.js +74 -0
- package/modules/use-cases/process-authorization-callback.js +294 -0
- package/modules/use-cases/refresh-entity-options.js +72 -0
- package/modules/use-cases/test-module-auth.js +72 -0
- package/modules/utils/map-module-dto.js +20 -0
- package/modules/utils/to-credential-id.js +14 -0
- package/package.json +92 -52
- package/prisma-mongodb/schema.prisma +455 -0
- package/prisma-postgresql/migrations/20250930193005_init/migration.sql +315 -0
- package/prisma-postgresql/migrations/20251006135218_init/migration.sql +9 -0
- package/prisma-postgresql/migrations/20251010000000_remove_unused_entity_reference_map/migration.sql +3 -0
- package/prisma-postgresql/migrations/20251112195422_update_user_unique_constraints/migration.sql +25 -0
- package/prisma-postgresql/migrations/20260422120000_add_entity_data_column/migration.sql +10 -0
- package/prisma-postgresql/migrations/20260422120001_create_process_table/migration.sql +48 -0
- package/prisma-postgresql/migrations/20260625000000_add_user_organization_id_index/migration.sql +2 -0
- package/prisma-postgresql/migrations/20260703000000_add_integration_status_in_creation_in_deletion/migration.sql +11 -0
- package/prisma-postgresql/migrations/20260703000001_integration_status_default_in_creation/migration.sql +8 -0
- package/prisma-postgresql/migrations/20260705000000_create_usage_counter/migration.sql +26 -0
- package/prisma-postgresql/migrations/20260706000000_add_admin_script_execution_and_schedule/migration.sql +61 -0
- package/prisma-postgresql/migrations/migration_lock.toml +3 -0
- package/prisma-postgresql/schema.prisma +436 -0
- package/queues/queue-delivery.js +41 -0
- package/queues/queuer-util.js +89 -21
- package/reporting/README.md +154 -0
- package/reporting/builtin-reports.js +6 -0
- package/reporting/index.js +13 -0
- package/reporting/report-base.js +49 -0
- package/reporting/reports/integrations-report.js +221 -0
- package/syncs/manager.js +468 -443
- package/syncs/repositories/sync-repository-documentdb.js +240 -0
- package/syncs/repositories/sync-repository-factory.js +43 -0
- package/syncs/repositories/sync-repository-interface.js +109 -0
- package/syncs/repositories/sync-repository-mongo.js +239 -0
- package/syncs/repositories/sync-repository-postgres.js +319 -0
- package/syncs/sync.js +0 -1
- package/telemetry/README.md +331 -0
- package/telemetry/bind-telemetry-context.js +77 -0
- package/telemetry/canonical-counters.js +52 -0
- package/telemetry/exporters.js +85 -0
- package/telemetry/index.js +26 -0
- package/telemetry/instrument-handler.js +104 -0
- package/telemetry/no-op-telemetry.js +71 -0
- package/telemetry/north-star.js +103 -0
- package/telemetry/otel-telemetry.js +232 -0
- package/telemetry/plugin-subscribers.js +77 -0
- package/telemetry/telemetry-config.js +120 -0
- package/telemetry/telemetry-context.js +51 -0
- package/telemetry/telemetry-event-bus.js +58 -0
- package/telemetry/telemetry-runtime.js +164 -0
- package/telemetry/telemetry-service.js +52 -0
- package/telemetry/usage-rollup-subscriber.js +116 -0
- package/token/repositories/token-repository-documentdb.js +137 -0
- package/token/repositories/token-repository-factory.js +40 -0
- package/token/repositories/token-repository-interface.js +131 -0
- package/token/repositories/token-repository-mongo.js +219 -0
- package/token/repositories/token-repository-postgres.js +264 -0
- package/token/repositories/token-repository.js +219 -0
- package/types/associations/index.d.ts +0 -17
- package/types/core/index.d.ts +12 -4
- package/types/database/index.d.ts +10 -2
- package/types/encrypt/index.d.ts +5 -3
- package/types/errors/index.d.ts +24 -10
- package/types/integrations/index.d.ts +3 -8
- package/types/logs/index.d.ts +61 -2
- package/types/module-plugin/index.d.ts +20 -69
- package/types/syncs/index.d.ts +0 -17
- package/usage/README.md +54 -0
- package/usage/index.js +17 -0
- package/usage/repositories/usage-repository-documentdb.js +194 -0
- package/usage/repositories/usage-repository-factory.js +25 -0
- package/usage/repositories/usage-repository-interface.js +37 -0
- package/usage/repositories/usage-repository-prisma.js +146 -0
- package/usage/tracked-metrics.js +38 -0
- package/usage/usage-windows.js +24 -0
- package/user/repositories/user-repository-documentdb.js +426 -0
- package/user/repositories/user-repository-factory.js +52 -0
- package/user/repositories/user-repository-interface.js +214 -0
- package/user/repositories/user-repository-mongo.js +323 -0
- package/user/repositories/user-repository-postgres.js +377 -0
- package/user/tests/doubles/test-user-repository.js +72 -0
- package/user/use-cases/authenticate-user.js +143 -0
- package/user/use-cases/authenticate-with-shared-secret.js +48 -0
- package/user/use-cases/create-individual-user.js +61 -0
- package/user/use-cases/create-organization-user.js +47 -0
- package/user/use-cases/create-token-for-user-id.js +30 -0
- package/user/use-cases/get-user-from-adopter-jwt.js +149 -0
- package/user/use-cases/get-user-from-bearer-token.js +82 -0
- package/user/use-cases/get-user-from-x-frigg-headers.js +132 -0
- package/user/use-cases/login-user.js +122 -0
- package/user/user.js +125 -0
- package/utils/backend-path.js +38 -0
- package/utils/index.js +6 -0
- package/websocket/repositories/websocket-connection-repository-documentdb.js +119 -0
- package/websocket/repositories/websocket-connection-repository-factory.js +44 -0
- package/websocket/repositories/websocket-connection-repository-interface.js +106 -0
- package/websocket/repositories/websocket-connection-repository-mongo.js +156 -0
- package/websocket/repositories/websocket-connection-repository-postgres.js +196 -0
- package/websocket/repositories/websocket-connection-repository.js +161 -0
- package/assertions/is-equal.js +0 -17
- package/associations/model.js +0 -54
- package/database/models/IndividualUser.js +0 -76
- package/database/models/OrganizationUser.js +0 -29
- package/database/models/State.js +0 -9
- package/database/models/Token.js +0 -70
- package/database/models/UserModel.js +0 -7
- package/database/models/WebsocketConnection.js +0 -49
- package/database/mongo.js +0 -45
- package/database/mongoose.js +0 -5
- package/encrypt/Cryptor.test.js +0 -32
- package/encrypt/encrypt.js +0 -132
- package/encrypt/encrypt.test.js +0 -1069
- package/encrypt/test-encrypt.js +0 -107
- package/errors/base-error.test.js +0 -32
- package/errors/fetch-error.test.js +0 -79
- package/errors/halt-error.test.js +0 -11
- package/errors/validation-errors.test.js +0 -120
- package/handlers/routers/middleware/loadUser.js +0 -15
- package/handlers/routers/middleware/requireLoggedInUser.js +0 -12
- package/integrations/create-frigg-backend.js +0 -31
- package/integrations/integration-factory.js +0 -251
- package/integrations/integration-mapping.js +0 -43
- package/integrations/integration-model.js +0 -46
- package/integrations/integration-user.js +0 -144
- package/integrations/test/integration-base.test.js +0 -144
- package/lambda/TimeoutCatcher.test.js +0 -68
- package/logs/logger.test.js +0 -76
- package/module-plugin/auther.js +0 -393
- package/module-plugin/credential.js +0 -22
- package/module-plugin/entity-manager.js +0 -70
- package/module-plugin/entity.js +0 -46
- package/module-plugin/manager.js +0 -169
- package/module-plugin/module-factory.js +0 -61
- package/module-plugin/requester/api-key.js +0 -36
- package/module-plugin/requester/oauth-2.js +0 -219
- package/module-plugin/requester/requester.js +0 -165
- package/module-plugin/requester/requester.test.js +0 -28
- package/module-plugin/test/auther.test.js +0 -97
- package/syncs/model.js +0 -62
- /package/{module-plugin → modules}/ModuleConstants.js +0 -0
- /package/{module-plugin → modules}/requester/basic.js +0 -0
- /package/{module-plugin → modules}/test/mock-api/mocks/hubspot.js +0 -0
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
const { prisma } = require('../../database/prisma');
|
|
2
|
+
const { MappingAlreadyExistsError } = require('../../errors');
|
|
3
|
+
const {
|
|
4
|
+
toObjectId,
|
|
5
|
+
fromObjectId,
|
|
6
|
+
findMany,
|
|
7
|
+
findOne,
|
|
8
|
+
insertOne,
|
|
9
|
+
updateOne,
|
|
10
|
+
deleteOne,
|
|
11
|
+
deleteMany,
|
|
12
|
+
aggregate,
|
|
13
|
+
aggregateDrained,
|
|
14
|
+
} = require('../../database/documentdb-utils');
|
|
15
|
+
const {
|
|
16
|
+
IntegrationMappingRepositoryInterface,
|
|
17
|
+
} = require('./integration-mapping-repository-interface');
|
|
18
|
+
const {
|
|
19
|
+
DocumentDBEncryptionService,
|
|
20
|
+
} = require('../../database/documentdb-encryption-service');
|
|
21
|
+
const {
|
|
22
|
+
assertMappingWrittenUnencrypted,
|
|
23
|
+
} = require('../../database/encryption/integration-mapping-encryption');
|
|
24
|
+
const { validateMappingQuery } = require('./integration-mapping-query');
|
|
25
|
+
const {
|
|
26
|
+
buildMappingQueryStages,
|
|
27
|
+
} = require('./integration-mapping-query-pipeline');
|
|
28
|
+
|
|
29
|
+
function storedIntegrationId(id) {
|
|
30
|
+
if (!['string', 'number'].includes(typeof id) || id === '') {
|
|
31
|
+
throw new TypeError(`Invalid ID: ${id}`);
|
|
32
|
+
}
|
|
33
|
+
return String(id);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
class IntegrationMappingRepositoryDocumentDB extends IntegrationMappingRepositoryInterface {
|
|
37
|
+
constructor() {
|
|
38
|
+
super();
|
|
39
|
+
this.prisma = prisma;
|
|
40
|
+
this.encryptionService = new DocumentDBEncryptionService();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* integrationId is stored as a string in DocumentDB, so ids are matched as
|
|
45
|
+
* strings (an ObjectId $in would never match). Drains the grouped cursor so
|
|
46
|
+
* a deployment-wide count is not truncated at the first batch.
|
|
47
|
+
*/
|
|
48
|
+
async countByIntegrationIds(ids = []) {
|
|
49
|
+
const counts = new Map();
|
|
50
|
+
if (!ids || ids.length === 0) return counts;
|
|
51
|
+
|
|
52
|
+
const stringIds = ids.map(String);
|
|
53
|
+
const rows = await aggregateDrained(this.prisma, 'IntegrationMapping', [
|
|
54
|
+
{ $match: { integrationId: { $in: stringIds } } },
|
|
55
|
+
{ $group: { _id: '$integrationId', count: { $sum: 1 } } },
|
|
56
|
+
]);
|
|
57
|
+
|
|
58
|
+
for (const row of rows) {
|
|
59
|
+
counts.set(String(row?._id), row?.count ?? 0);
|
|
60
|
+
}
|
|
61
|
+
return counts;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async findMappingBy(integrationId, sourceId) {
|
|
65
|
+
const filter = this._compositeFilter(integrationId, sourceId);
|
|
66
|
+
const doc = await findOne(this.prisma, 'IntegrationMapping', filter);
|
|
67
|
+
if (!doc) return null;
|
|
68
|
+
|
|
69
|
+
const decryptedMapping = await this.encryptionService.decryptFields(
|
|
70
|
+
'IntegrationMapping',
|
|
71
|
+
doc
|
|
72
|
+
);
|
|
73
|
+
return this._mapMapping(decryptedMapping);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async upsertMapping(integrationId, sourceId, mapping) {
|
|
77
|
+
const filter = this._compositeFilter(integrationId, sourceId);
|
|
78
|
+
const existing = await findOne(
|
|
79
|
+
this.prisma,
|
|
80
|
+
'IntegrationMapping',
|
|
81
|
+
filter
|
|
82
|
+
);
|
|
83
|
+
const now = new Date();
|
|
84
|
+
|
|
85
|
+
if (existing) {
|
|
86
|
+
const decryptedExisting =
|
|
87
|
+
await this.encryptionService.decryptFields(
|
|
88
|
+
'IntegrationMapping',
|
|
89
|
+
existing
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
const updateDocument = {
|
|
93
|
+
mapping,
|
|
94
|
+
updatedAt: now,
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const encryptedUpdate = await this.encryptionService.encryptFields(
|
|
98
|
+
'IntegrationMapping',
|
|
99
|
+
{ mapping: updateDocument.mapping }
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
await updateOne(
|
|
103
|
+
this.prisma,
|
|
104
|
+
'IntegrationMapping',
|
|
105
|
+
{ _id: existing._id },
|
|
106
|
+
{
|
|
107
|
+
$set: {
|
|
108
|
+
mapping: encryptedUpdate.mapping,
|
|
109
|
+
updatedAt: updateDocument.updatedAt,
|
|
110
|
+
},
|
|
111
|
+
}
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
const updated = await findOne(this.prisma, 'IntegrationMapping', {
|
|
115
|
+
_id: existing._id,
|
|
116
|
+
});
|
|
117
|
+
if (!updated) {
|
|
118
|
+
console.error(
|
|
119
|
+
'[IntegrationMappingRepositoryDocumentDB] Mapping not found after update',
|
|
120
|
+
{
|
|
121
|
+
mappingId: fromObjectId(existing._id),
|
|
122
|
+
integrationId,
|
|
123
|
+
sourceId,
|
|
124
|
+
}
|
|
125
|
+
);
|
|
126
|
+
throw new Error(
|
|
127
|
+
'Failed to update mapping: Document not found after update. ' +
|
|
128
|
+
'This indicates a database consistency issue.'
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
const decryptedMapping = await this.encryptionService.decryptFields(
|
|
132
|
+
'IntegrationMapping',
|
|
133
|
+
updated
|
|
134
|
+
);
|
|
135
|
+
return this._mapMapping(decryptedMapping);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const plainDocument = {
|
|
139
|
+
integrationId: integrationId,
|
|
140
|
+
sourceId:
|
|
141
|
+
sourceId === null || sourceId === undefined
|
|
142
|
+
? null
|
|
143
|
+
: String(sourceId),
|
|
144
|
+
mapping,
|
|
145
|
+
createdAt: now,
|
|
146
|
+
updatedAt: now,
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
const encryptedDocument = await this.encryptionService.encryptFields(
|
|
150
|
+
'IntegrationMapping',
|
|
151
|
+
plainDocument
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
const insertedId = await insertOne(
|
|
155
|
+
this.prisma,
|
|
156
|
+
'IntegrationMapping',
|
|
157
|
+
encryptedDocument
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
const created = await findOne(this.prisma, 'IntegrationMapping', {
|
|
161
|
+
_id: insertedId,
|
|
162
|
+
});
|
|
163
|
+
if (!created) {
|
|
164
|
+
console.error(
|
|
165
|
+
'[IntegrationMappingRepositoryDocumentDB] Mapping not found after insert',
|
|
166
|
+
{
|
|
167
|
+
insertedId: fromObjectId(insertedId),
|
|
168
|
+
integrationId,
|
|
169
|
+
sourceId,
|
|
170
|
+
}
|
|
171
|
+
);
|
|
172
|
+
throw new Error(
|
|
173
|
+
'Failed to create mapping: Document not found after insert. ' +
|
|
174
|
+
'This indicates a database consistency issue.'
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
const decryptedMapping = await this.encryptionService.decryptFields(
|
|
178
|
+
'IntegrationMapping',
|
|
179
|
+
created
|
|
180
|
+
);
|
|
181
|
+
return this._mapMapping(decryptedMapping);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
async createMapping(integrationId, sourceId, mapping) {
|
|
185
|
+
const now = new Date();
|
|
186
|
+
const encryptedDocument = await this.encryptionService.encryptFields(
|
|
187
|
+
'IntegrationMapping',
|
|
188
|
+
{
|
|
189
|
+
integrationId,
|
|
190
|
+
sourceId:
|
|
191
|
+
sourceId === null || sourceId === undefined
|
|
192
|
+
? null
|
|
193
|
+
: String(sourceId),
|
|
194
|
+
mapping,
|
|
195
|
+
createdAt: now,
|
|
196
|
+
updatedAt: now,
|
|
197
|
+
}
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
let insertedId;
|
|
201
|
+
try {
|
|
202
|
+
insertedId = await insertOne(
|
|
203
|
+
this.prisma,
|
|
204
|
+
'IntegrationMapping',
|
|
205
|
+
encryptedDocument
|
|
206
|
+
);
|
|
207
|
+
} catch (error) {
|
|
208
|
+
if (error.code === 11000) {
|
|
209
|
+
throw new MappingAlreadyExistsError(integrationId, sourceId, {
|
|
210
|
+
cause: error,
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
throw error;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const created = await findOne(this.prisma, 'IntegrationMapping', {
|
|
217
|
+
_id: insertedId,
|
|
218
|
+
});
|
|
219
|
+
if (!created) {
|
|
220
|
+
throw new Error(
|
|
221
|
+
'Failed to create mapping: Document not found after insert. ' +
|
|
222
|
+
'This indicates a database consistency issue.'
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
return this._mapMapping(
|
|
226
|
+
await this.encryptionService.decryptFields(
|
|
227
|
+
'IntegrationMapping',
|
|
228
|
+
created
|
|
229
|
+
)
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
async findMappingsByIntegration(integrationId) {
|
|
234
|
+
const filter = {};
|
|
235
|
+
if (integrationId) filter.integrationId = integrationId;
|
|
236
|
+
const docs = await findMany(this.prisma, 'IntegrationMapping', filter);
|
|
237
|
+
|
|
238
|
+
const decryptedDocs = await Promise.all(
|
|
239
|
+
docs.map((doc) =>
|
|
240
|
+
this.encryptionService.decryptFields('IntegrationMapping', doc)
|
|
241
|
+
)
|
|
242
|
+
);
|
|
243
|
+
|
|
244
|
+
return decryptedDocs.map((doc) => this._mapMapping(doc));
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* @param {string} integrationId
|
|
249
|
+
* @param {Object} query - See IntegrationMappingRepositoryInterface.queryMappings
|
|
250
|
+
* @returns {Promise<{mappings: Array<Object>, total: number}>}
|
|
251
|
+
*/
|
|
252
|
+
async queryMappings(integrationId, query) {
|
|
253
|
+
const validated = validateMappingQuery(query);
|
|
254
|
+
const stored = storedIntegrationId(integrationId);
|
|
255
|
+
assertMappingWrittenUnencrypted();
|
|
256
|
+
const { match, page, sort } = buildMappingQueryStages(
|
|
257
|
+
stored,
|
|
258
|
+
validated
|
|
259
|
+
);
|
|
260
|
+
|
|
261
|
+
const [docs, counts] = await Promise.all([
|
|
262
|
+
aggregateDrained(
|
|
263
|
+
this.prisma,
|
|
264
|
+
'IntegrationMapping',
|
|
265
|
+
[match, ...page, sort],
|
|
266
|
+
{ allowDiskUse: true }
|
|
267
|
+
),
|
|
268
|
+
aggregate(this.prisma, 'IntegrationMapping', [
|
|
269
|
+
match,
|
|
270
|
+
{ $count: 'total' },
|
|
271
|
+
]),
|
|
272
|
+
]);
|
|
273
|
+
const decryptedDocs = await Promise.all(
|
|
274
|
+
docs.map((doc) =>
|
|
275
|
+
this.encryptionService.decryptFields('IntegrationMapping', doc)
|
|
276
|
+
)
|
|
277
|
+
);
|
|
278
|
+
|
|
279
|
+
return {
|
|
280
|
+
mappings: decryptedDocs.map((doc) => this._mapMapping(doc)),
|
|
281
|
+
total: counts[0]?.total ?? 0,
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
async deleteMapping(integrationId, sourceId) {
|
|
286
|
+
const filter = this._compositeFilter(integrationId, sourceId);
|
|
287
|
+
const result = await deleteOne(
|
|
288
|
+
this.prisma,
|
|
289
|
+
'IntegrationMapping',
|
|
290
|
+
filter
|
|
291
|
+
);
|
|
292
|
+
const deleted = result?.n ?? 0;
|
|
293
|
+
return { acknowledged: true, deletedCount: deleted };
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
async deleteMappingsByIntegration(integrationId) {
|
|
297
|
+
if (!integrationId) {
|
|
298
|
+
return { acknowledged: true, deletedCount: 0 };
|
|
299
|
+
}
|
|
300
|
+
const result = await deleteMany(this.prisma, 'IntegrationMapping', {
|
|
301
|
+
integrationId: integrationId,
|
|
302
|
+
});
|
|
303
|
+
const deleted = result?.n ?? 0;
|
|
304
|
+
return { acknowledged: true, deletedCount: deleted };
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
async findMappingById(id) {
|
|
308
|
+
const objectId = toObjectId(id);
|
|
309
|
+
if (!objectId) return null;
|
|
310
|
+
const doc = await findOne(this.prisma, 'IntegrationMapping', {
|
|
311
|
+
_id: objectId,
|
|
312
|
+
});
|
|
313
|
+
if (!doc) return null;
|
|
314
|
+
|
|
315
|
+
const decryptedMapping = await this.encryptionService.decryptFields(
|
|
316
|
+
'IntegrationMapping',
|
|
317
|
+
doc
|
|
318
|
+
);
|
|
319
|
+
return this._mapMapping(decryptedMapping);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
async updateMapping(id, updates) {
|
|
323
|
+
const objectId = toObjectId(id);
|
|
324
|
+
if (!objectId) return null;
|
|
325
|
+
|
|
326
|
+
const existing = await findOne(this.prisma, 'IntegrationMapping', {
|
|
327
|
+
_id: objectId,
|
|
328
|
+
});
|
|
329
|
+
if (!existing) return null;
|
|
330
|
+
|
|
331
|
+
const decryptedExisting = await this.encryptionService.decryptFields(
|
|
332
|
+
'IntegrationMapping',
|
|
333
|
+
existing
|
|
334
|
+
);
|
|
335
|
+
|
|
336
|
+
const mergedMapping =
|
|
337
|
+
updates.mapping !== undefined
|
|
338
|
+
? updates.mapping
|
|
339
|
+
: decryptedExisting.mapping;
|
|
340
|
+
|
|
341
|
+
const updateDocument = {
|
|
342
|
+
...updates,
|
|
343
|
+
updatedAt: new Date(),
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
if (mergedMapping !== undefined) {
|
|
347
|
+
const encryptedUpdate = await this.encryptionService.encryptFields(
|
|
348
|
+
'IntegrationMapping',
|
|
349
|
+
{ mapping: mergedMapping }
|
|
350
|
+
);
|
|
351
|
+
updateDocument.mapping = encryptedUpdate.mapping;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
await updateOne(
|
|
355
|
+
this.prisma,
|
|
356
|
+
'IntegrationMapping',
|
|
357
|
+
{ _id: objectId },
|
|
358
|
+
{
|
|
359
|
+
$set: updateDocument,
|
|
360
|
+
}
|
|
361
|
+
);
|
|
362
|
+
|
|
363
|
+
const updated = await findOne(this.prisma, 'IntegrationMapping', {
|
|
364
|
+
_id: objectId,
|
|
365
|
+
});
|
|
366
|
+
if (!updated) {
|
|
367
|
+
console.error(
|
|
368
|
+
'[IntegrationMappingRepositoryDocumentDB] Mapping not found after update',
|
|
369
|
+
{
|
|
370
|
+
mappingId: fromObjectId(objectId),
|
|
371
|
+
}
|
|
372
|
+
);
|
|
373
|
+
throw new Error(
|
|
374
|
+
'Failed to update mapping: Document not found after update. ' +
|
|
375
|
+
'This indicates a database consistency issue.'
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
const decryptedMapping = await this.encryptionService.decryptFields(
|
|
379
|
+
'IntegrationMapping',
|
|
380
|
+
updated
|
|
381
|
+
);
|
|
382
|
+
return this._mapMapping(decryptedMapping);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
_compositeFilter(integrationId, sourceId) {
|
|
386
|
+
const filter = {};
|
|
387
|
+
if (integrationId) filter.integrationId = integrationId;
|
|
388
|
+
if (sourceId !== undefined) {
|
|
389
|
+
filter.sourceId = sourceId === null ? null : String(sourceId);
|
|
390
|
+
}
|
|
391
|
+
return filter;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
_mapMapping(doc) {
|
|
395
|
+
return {
|
|
396
|
+
id: fromObjectId(doc?._id),
|
|
397
|
+
integrationId: doc?.integrationId ?? null,
|
|
398
|
+
sourceId: doc?.sourceId ?? null,
|
|
399
|
+
mapping: doc?.mapping ?? null,
|
|
400
|
+
createdAt: doc?.createdAt,
|
|
401
|
+
updatedAt: doc?.updatedAt,
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
module.exports = { IntegrationMappingRepositoryDocumentDB };
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
const {
|
|
2
|
+
IntegrationMappingRepositoryMongo,
|
|
3
|
+
} = require('./integration-mapping-repository-mongo');
|
|
4
|
+
const {
|
|
5
|
+
IntegrationMappingRepositoryPostgres,
|
|
6
|
+
} = require('./integration-mapping-repository-postgres');
|
|
7
|
+
const {
|
|
8
|
+
IntegrationMappingRepositoryDocumentDB,
|
|
9
|
+
} = require('./integration-mapping-repository-documentdb');
|
|
10
|
+
const config = require('../../database/config');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Integration Mapping Repository Factory
|
|
14
|
+
* Creates the appropriate repository adapter based on database type
|
|
15
|
+
*
|
|
16
|
+
* Database-specific implementations:
|
|
17
|
+
* - MongoDB: Uses String IDs (ObjectId), no conversion needed
|
|
18
|
+
* - PostgreSQL: Uses Int IDs, converts String ↔ Int
|
|
19
|
+
*
|
|
20
|
+
* All repository methods return String IDs regardless of database type,
|
|
21
|
+
* ensuring application layer consistency.
|
|
22
|
+
*
|
|
23
|
+
* Usage:
|
|
24
|
+
* ```javascript
|
|
25
|
+
* const repository = createIntegrationMappingRepository();
|
|
26
|
+
* const mapping = await repository.findMappingBy(integrationId, sourceId);
|
|
27
|
+
* ```
|
|
28
|
+
*
|
|
29
|
+
* @returns {IntegrationMappingRepositoryInterface} Configured repository adapter
|
|
30
|
+
*/
|
|
31
|
+
function createIntegrationMappingRepository() {
|
|
32
|
+
const dbType = config.DB_TYPE;
|
|
33
|
+
|
|
34
|
+
switch (dbType) {
|
|
35
|
+
case 'mongodb':
|
|
36
|
+
return new IntegrationMappingRepositoryMongo();
|
|
37
|
+
|
|
38
|
+
case 'postgresql':
|
|
39
|
+
return new IntegrationMappingRepositoryPostgres();
|
|
40
|
+
|
|
41
|
+
case 'documentdb':
|
|
42
|
+
return new IntegrationMappingRepositoryDocumentDB();
|
|
43
|
+
|
|
44
|
+
default:
|
|
45
|
+
throw new Error(
|
|
46
|
+
`Unsupported database type: ${dbType}. Supported values: 'mongodb', 'documentdb', 'postgresql'`
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
module.exports = {
|
|
52
|
+
createIntegrationMappingRepository,
|
|
53
|
+
// Export adapters for direct testing
|
|
54
|
+
IntegrationMappingRepositoryMongo,
|
|
55
|
+
IntegrationMappingRepositoryPostgres,
|
|
56
|
+
IntegrationMappingRepositoryDocumentDB,
|
|
57
|
+
};
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integration Mapping Repository Interface
|
|
3
|
+
* Abstract base class defining the contract for integration mapping persistence adapters
|
|
4
|
+
*
|
|
5
|
+
* This follows the Port in Hexagonal Architecture:
|
|
6
|
+
* - Domain layer depends on this abstraction
|
|
7
|
+
* - Concrete adapters implement this interface
|
|
8
|
+
* - Use cases receive repositories via dependency injection
|
|
9
|
+
*
|
|
10
|
+
* Note: Currently, IntegrationMapping model has identical structure across MongoDB and PostgreSQL,
|
|
11
|
+
* so IntegrationMappingRepository serves both. This interface exists for consistency and
|
|
12
|
+
* future-proofing if database-specific implementations become needed.
|
|
13
|
+
*
|
|
14
|
+
* @abstract
|
|
15
|
+
*/
|
|
16
|
+
class IntegrationMappingRepositoryInterface {
|
|
17
|
+
/**
|
|
18
|
+
* Find mapping by integration ID and source ID
|
|
19
|
+
*
|
|
20
|
+
* @param {string|number} integrationId - The integration ID
|
|
21
|
+
* @param {string} sourceId - The source ID for lookup
|
|
22
|
+
* @returns {Promise<Object|null>} The mapping object or null
|
|
23
|
+
* @abstract
|
|
24
|
+
*/
|
|
25
|
+
async findMappingBy(integrationId, sourceId) {
|
|
26
|
+
throw new Error('Method findMappingBy must be implemented by subclass');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Create or update a mapping
|
|
31
|
+
*
|
|
32
|
+
* @param {string|number} integrationId - The integration ID
|
|
33
|
+
* @param {string} sourceId - The source ID for lookup
|
|
34
|
+
* @param {Object} mapping - The mapping data
|
|
35
|
+
* @returns {Promise<Object>} The created or updated mapping document
|
|
36
|
+
* @abstract
|
|
37
|
+
*/
|
|
38
|
+
async upsertMapping(integrationId, sourceId, mapping) {
|
|
39
|
+
throw new Error('Method upsertMapping must be implemented by subclass');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Insert a mapping, failing if one already exists for the
|
|
44
|
+
* (integrationId, sourceId) pair. Atomic: usable as a claim/lock row.
|
|
45
|
+
*
|
|
46
|
+
* @param {string|number} integrationId - The integration ID
|
|
47
|
+
* @param {string} sourceId - The source ID for lookup
|
|
48
|
+
* @param {Object} mapping - The mapping data
|
|
49
|
+
* @returns {Promise<Object>} The created mapping document
|
|
50
|
+
* @throws {MappingAlreadyExistsError} If the pair already exists
|
|
51
|
+
* @abstract
|
|
52
|
+
*/
|
|
53
|
+
async createMapping(integrationId, sourceId, mapping) {
|
|
54
|
+
throw new Error('createMapping must be implemented by subclass');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Find all mappings for an integration
|
|
59
|
+
*
|
|
60
|
+
* @param {string|number} integrationId - The integration ID
|
|
61
|
+
* @returns {Promise<Array>} Array of mapping objects
|
|
62
|
+
* @abstract
|
|
63
|
+
*/
|
|
64
|
+
async findMappingsByIntegration(integrationId) {
|
|
65
|
+
throw new Error(
|
|
66
|
+
'Method findMappingsByIntegration must be implemented by subclass'
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Query one filtered, ordered page of an integration's mappings without
|
|
72
|
+
* loading every row. Rows have the same shape as findMappingsByIntegration
|
|
73
|
+
* and are decrypted the same way.
|
|
74
|
+
*
|
|
75
|
+
* Paths address the `mapping` JSON by identifier-only segments
|
|
76
|
+
* (`'mapping.outbound.status'`), or the `sourceId` column. Conditions:
|
|
77
|
+
* - `{ path: 'mapping.…', op: 'exists' | 'notExists' }` — JSON null counts
|
|
78
|
+
* as absent; notExists is the exact negation of exists.
|
|
79
|
+
* - `{ path: 'mapping.…', op: 'in', value: string[] }` — matches JSON
|
|
80
|
+
* strings; 1–500 values.
|
|
81
|
+
* - `{ path: 'sourceId', op: 'notStartsWith', value: string }` — a NULL
|
|
82
|
+
* sourceId matches.
|
|
83
|
+
*
|
|
84
|
+
* Only rows whose `mapping` is a JSON object can match, so rows whose
|
|
85
|
+
* whole `mapping` is still ciphertext from before an encryption opt-out
|
|
86
|
+
* never do. A nested path still encrypted from before its opt-out comes
|
|
87
|
+
* back plain, but conditions and orderBy on it see the ciphertext.
|
|
88
|
+
* Adapters refuse to run while field-level encryption is enabled
|
|
89
|
+
* and still encrypts `mapping`, or a path inside it, on write; opt out with
|
|
90
|
+
* `appDefinition.encryption.disable = { IntegrationMapping: ['mapping'] }`
|
|
91
|
+
* plus any nested `mapping.…` path a custom schema encrypts.
|
|
92
|
+
*
|
|
93
|
+
* @param {string|number} integrationId - The integration ID
|
|
94
|
+
* @param {Object} query
|
|
95
|
+
* @param {Array<Object>} [query.where=[]] - Conditions ANDed together; an
|
|
96
|
+
* entry may be `{ anyOf: Condition[] }` (one level, ORed). At most 20
|
|
97
|
+
* conditions, anyOf members included.
|
|
98
|
+
* @param {{path: string, direction: 'asc'|'desc'}} [query.orderBy] - A
|
|
99
|
+
* mapping path; nulls last, ties broken by id in the same direction.
|
|
100
|
+
* Values order string < number < boolean < array < object. Strings
|
|
101
|
+
* compare by the database collation on PostgreSQL and by code point on
|
|
102
|
+
* MongoDB and DocumentDB; arrays and objects order among themselves
|
|
103
|
+
* only on PostgreSQL. Without it rows are ordered by id ascending.
|
|
104
|
+
* @param {number} [query.skip=0] - Rows to skip (integer ≥ 0)
|
|
105
|
+
* @param {number} query.take - Page size (integer 1–500)
|
|
106
|
+
* @param {string[]} [query.omit=[]] - Top-level mapping keys to leave out of
|
|
107
|
+
* the returned rows; such projected rows must not be written back
|
|
108
|
+
* @returns {Promise<{mappings: Array<Object>, total: number}>} The page, and
|
|
109
|
+
* the number of rows matching `where` (counted by a separate command on
|
|
110
|
+
* DocumentDB, so not from the page's snapshot)
|
|
111
|
+
*/
|
|
112
|
+
async queryMappings(integrationId, query) {
|
|
113
|
+
throw new Error(
|
|
114
|
+
'queryMappings is not supported by this database adapter yet'
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Delete a specific mapping
|
|
120
|
+
*
|
|
121
|
+
* @param {string|number} integrationId - The integration ID
|
|
122
|
+
* @param {string} sourceId - The source ID
|
|
123
|
+
* @returns {Promise<Object>} Deletion result
|
|
124
|
+
* @abstract
|
|
125
|
+
*/
|
|
126
|
+
async deleteMapping(integrationId, sourceId) {
|
|
127
|
+
throw new Error('Method deleteMapping must be implemented by subclass');
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Delete all mappings for an integration
|
|
132
|
+
*
|
|
133
|
+
* @param {string|number} integrationId - The integration ID
|
|
134
|
+
* @returns {Promise<Object>} Deletion result
|
|
135
|
+
* @abstract
|
|
136
|
+
*/
|
|
137
|
+
async deleteMappingsByIntegration(integrationId) {
|
|
138
|
+
throw new Error(
|
|
139
|
+
'Method deleteMappingsByIntegration must be implemented by subclass'
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Count mappings grouped by integration id, for a bounded set of ids.
|
|
145
|
+
* Adapters must drain the full grouped result (a deployment can have more
|
|
146
|
+
* than one first-batch of distinct integration ids).
|
|
147
|
+
*
|
|
148
|
+
* @returns {Promise<Map<string, number>>} Map of integrationId (string) → count
|
|
149
|
+
* @abstract
|
|
150
|
+
*/
|
|
151
|
+
async countByIntegrationIds(ids) {
|
|
152
|
+
throw new Error(
|
|
153
|
+
'Method countByIntegrationIds must be implemented by subclass'
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Find mapping by ID
|
|
159
|
+
*
|
|
160
|
+
* @param {string|number} id - The mapping ID
|
|
161
|
+
* @returns {Promise<Object|null>} The mapping object or null
|
|
162
|
+
* @abstract
|
|
163
|
+
*/
|
|
164
|
+
async findMappingById(id) {
|
|
165
|
+
throw new Error(
|
|
166
|
+
'Method findMappingById must be implemented by subclass'
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Update a mapping by ID
|
|
172
|
+
*
|
|
173
|
+
* @param {string|number} id - The mapping ID
|
|
174
|
+
* @param {Object} updates - Fields to update
|
|
175
|
+
* @returns {Promise<Object>} Updated mapping object
|
|
176
|
+
* @abstract
|
|
177
|
+
*/
|
|
178
|
+
async updateMapping(id, updates) {
|
|
179
|
+
throw new Error('Method updateMapping must be implemented by subclass');
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
module.exports = { IntegrationMappingRepositoryInterface };
|