@friggframework/core 2.0.0-next.12 → 2.0.0-next.120
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 +184 -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 +91 -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,284 @@
|
|
|
1
|
+
const { prisma } = require('../../database/prisma');
|
|
2
|
+
const { MappingAlreadyExistsError } = require('../../errors');
|
|
3
|
+
const {
|
|
4
|
+
assertMappingWrittenUnencrypted,
|
|
5
|
+
decryptQueriedMappings,
|
|
6
|
+
} = require('../../database/encryption/integration-mapping-encryption');
|
|
7
|
+
const {
|
|
8
|
+
IntegrationMappingRepositoryInterface,
|
|
9
|
+
} = require('./integration-mapping-repository-interface');
|
|
10
|
+
const { validateMappingQuery } = require('./integration-mapping-query');
|
|
11
|
+
const {
|
|
12
|
+
buildMappingQueryStages,
|
|
13
|
+
} = require('./integration-mapping-query-pipeline');
|
|
14
|
+
|
|
15
|
+
const OBJECT_ID_REGEX = /^[0-9a-fA-F]{24}$/;
|
|
16
|
+
|
|
17
|
+
const fromRawDate = (raw) => new Date(raw.$date);
|
|
18
|
+
|
|
19
|
+
function strictObjectId(id) {
|
|
20
|
+
if (typeof id !== 'string' || !OBJECT_ID_REGEX.test(id)) {
|
|
21
|
+
throw new TypeError(`Invalid ID: ${id} is not an ObjectId`);
|
|
22
|
+
}
|
|
23
|
+
return id;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* MongoDB Integration Mapping Repository Adapter
|
|
28
|
+
* Handles persistence of integration mappings used for data transformation
|
|
29
|
+
*
|
|
30
|
+
* MongoDB-specific characteristics:
|
|
31
|
+
* - Uses String IDs (ObjectId)
|
|
32
|
+
* - No ID conversion needed (IDs are already strings)
|
|
33
|
+
* - mapping data stored in JSON field
|
|
34
|
+
*/
|
|
35
|
+
class IntegrationMappingRepositoryMongo extends IntegrationMappingRepositoryInterface {
|
|
36
|
+
constructor() {
|
|
37
|
+
super();
|
|
38
|
+
this.prisma = prisma;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Convert any value to string (handles null/undefined)
|
|
43
|
+
* @private
|
|
44
|
+
* @param {*} value - Value to convert
|
|
45
|
+
* @returns {string|null|undefined} String value or null/undefined
|
|
46
|
+
*/
|
|
47
|
+
_toString(value) {
|
|
48
|
+
if (value === null || value === undefined) return value;
|
|
49
|
+
return String(value);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Find mapping by integration ID and source ID
|
|
54
|
+
* Replaces: IntegrationMapping.findBy(integrationId, sourceId)
|
|
55
|
+
*
|
|
56
|
+
* @param {string} integrationId - The integration ID
|
|
57
|
+
* @param {string} sourceId - The source ID for lookup
|
|
58
|
+
* @returns {Promise<Object|null>} The mapping object with string IDs or null
|
|
59
|
+
*/
|
|
60
|
+
async findMappingBy(integrationId, sourceId) {
|
|
61
|
+
return await this.prisma.integrationMapping.findFirst({
|
|
62
|
+
where: {
|
|
63
|
+
integrationId,
|
|
64
|
+
sourceId: this._toString(sourceId),
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Create or update a mapping
|
|
71
|
+
* Replaces: IntegrationMapping.upsert(integrationId, sourceId, mapping)
|
|
72
|
+
*
|
|
73
|
+
* @param {string} integrationId - The integration ID
|
|
74
|
+
* @param {string} sourceId - The source ID for lookup
|
|
75
|
+
* @param {Object} mapping - The mapping data
|
|
76
|
+
* @returns {Promise<Object>} The created or updated mapping document with string IDs
|
|
77
|
+
*/
|
|
78
|
+
async upsertMapping(integrationId, sourceId, mapping) {
|
|
79
|
+
return await this.prisma.integrationMapping.upsert({
|
|
80
|
+
where: {
|
|
81
|
+
integrationId_sourceId: {
|
|
82
|
+
integrationId,
|
|
83
|
+
sourceId: this._toString(sourceId),
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
update: {
|
|
87
|
+
mapping,
|
|
88
|
+
},
|
|
89
|
+
create: {
|
|
90
|
+
integrationId,
|
|
91
|
+
sourceId: this._toString(sourceId),
|
|
92
|
+
mapping,
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Insert a mapping; fails if (integrationId, sourceId) already exists
|
|
99
|
+
*
|
|
100
|
+
* @param {string} integrationId - The integration ID
|
|
101
|
+
* @param {string} sourceId - The source ID
|
|
102
|
+
* @param {Object} mapping - The mapping data
|
|
103
|
+
* @returns {Promise<Object>} The created mapping document
|
|
104
|
+
* @throws {MappingAlreadyExistsError} If the pair already exists
|
|
105
|
+
*/
|
|
106
|
+
async createMapping(integrationId, sourceId, mapping) {
|
|
107
|
+
try {
|
|
108
|
+
return await this.prisma.integrationMapping.create({
|
|
109
|
+
data: {
|
|
110
|
+
integrationId,
|
|
111
|
+
sourceId: this._toString(sourceId),
|
|
112
|
+
mapping,
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
} catch (error) {
|
|
116
|
+
if (error.code === 'P2002') {
|
|
117
|
+
throw new MappingAlreadyExistsError(integrationId, sourceId, {
|
|
118
|
+
cause: error,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
throw error;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Find all mappings for an integration
|
|
127
|
+
* Replaces: IntegrationMapping.find({ integration: integrationId })
|
|
128
|
+
*
|
|
129
|
+
* @param {string} integrationId - The integration ID
|
|
130
|
+
* @returns {Promise<Array>} Array of mapping documents with string IDs
|
|
131
|
+
*/
|
|
132
|
+
async findMappingsByIntegration(integrationId) {
|
|
133
|
+
return await this.prisma.integrationMapping.findMany({
|
|
134
|
+
where: { integrationId },
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Delete a mapping by integration and source ID
|
|
140
|
+
* Replaces: IntegrationMapping.deleteOne({ integration, sourceId })
|
|
141
|
+
*
|
|
142
|
+
* @param {string} integrationId - The integration ID
|
|
143
|
+
* @param {string} sourceId - The source ID
|
|
144
|
+
* @returns {Promise<Object>} The deletion result
|
|
145
|
+
*/
|
|
146
|
+
async deleteMapping(integrationId, sourceId) {
|
|
147
|
+
try {
|
|
148
|
+
await this.prisma.integrationMapping.delete({
|
|
149
|
+
where: {
|
|
150
|
+
integrationId_sourceId: {
|
|
151
|
+
integrationId,
|
|
152
|
+
sourceId: this._toString(sourceId),
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
});
|
|
156
|
+
return { acknowledged: true, deletedCount: 1 };
|
|
157
|
+
} catch (error) {
|
|
158
|
+
if (error.code === 'P2025') {
|
|
159
|
+
// Record not found
|
|
160
|
+
return { acknowledged: true, deletedCount: 0 };
|
|
161
|
+
}
|
|
162
|
+
throw error;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Delete all mappings for an integration
|
|
168
|
+
* Replaces: IntegrationMapping.deleteMany({ integration: integrationId })
|
|
169
|
+
*
|
|
170
|
+
* @param {string} integrationId - The integration ID
|
|
171
|
+
* @returns {Promise<Object>} The deletion result
|
|
172
|
+
*/
|
|
173
|
+
async deleteMappingsByIntegration(integrationId) {
|
|
174
|
+
const result = await this.prisma.integrationMapping.deleteMany({
|
|
175
|
+
where: { integrationId },
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
return {
|
|
179
|
+
acknowledged: true,
|
|
180
|
+
deletedCount: result.count,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Count mappings grouped by integration id for a bounded id set.
|
|
186
|
+
*
|
|
187
|
+
* @param {Array<string>} ids - Integration ids
|
|
188
|
+
* @returns {Promise<Map<string, number>>} integrationId (string) → count
|
|
189
|
+
*/
|
|
190
|
+
async countByIntegrationIds(ids = []) {
|
|
191
|
+
const counts = new Map();
|
|
192
|
+
if (!ids || ids.length === 0) return counts;
|
|
193
|
+
|
|
194
|
+
const groups = await this.prisma.integrationMapping.groupBy({
|
|
195
|
+
by: ['integrationId'],
|
|
196
|
+
where: { integrationId: { in: ids } },
|
|
197
|
+
_count: { _all: true },
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
for (const group of groups) {
|
|
201
|
+
counts.set(String(group.integrationId), group._count._all);
|
|
202
|
+
}
|
|
203
|
+
return counts;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* @param {string} integrationId
|
|
208
|
+
* @param {Object} query - See IntegrationMappingRepositoryInterface.queryMappings
|
|
209
|
+
* @returns {Promise<{mappings: Array<Object>, total: number}>}
|
|
210
|
+
*/
|
|
211
|
+
async queryMappings(integrationId, query) {
|
|
212
|
+
const validated = validateMappingQuery(query);
|
|
213
|
+
const objectId = strictObjectId(integrationId);
|
|
214
|
+
assertMappingWrittenUnencrypted();
|
|
215
|
+
const { match, page } = buildMappingQueryStages(
|
|
216
|
+
{ $oid: objectId },
|
|
217
|
+
validated
|
|
218
|
+
);
|
|
219
|
+
|
|
220
|
+
const result = await this.prisma.$runCommandRaw({
|
|
221
|
+
aggregate: 'IntegrationMapping',
|
|
222
|
+
pipeline: [
|
|
223
|
+
match,
|
|
224
|
+
{
|
|
225
|
+
$facet: {
|
|
226
|
+
mappings: page,
|
|
227
|
+
total: [{ $count: 'total' }],
|
|
228
|
+
},
|
|
229
|
+
},
|
|
230
|
+
],
|
|
231
|
+
cursor: {},
|
|
232
|
+
allowDiskUse: true,
|
|
233
|
+
});
|
|
234
|
+
const [{ mappings, total }] = result.cursor.firstBatch;
|
|
235
|
+
|
|
236
|
+
return {
|
|
237
|
+
mappings: await decryptQueriedMappings(
|
|
238
|
+
mappings.map((doc) => this._fromRawMapping(doc))
|
|
239
|
+
),
|
|
240
|
+
total: total[0]?.total ?? 0,
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* A raw aggregate document, in the shape findMappingsByIntegration returns.
|
|
246
|
+
* @private
|
|
247
|
+
*/
|
|
248
|
+
_fromRawMapping(doc) {
|
|
249
|
+
return {
|
|
250
|
+
id: doc._id.$oid,
|
|
251
|
+
integrationId: doc.integrationId.$oid,
|
|
252
|
+
sourceId: doc.sourceId ?? null,
|
|
253
|
+
mapping: doc.mapping ?? null,
|
|
254
|
+
createdAt: fromRawDate(doc.createdAt),
|
|
255
|
+
updatedAt: fromRawDate(doc.updatedAt),
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Find mapping by ID
|
|
261
|
+
* @param {string} id - Mapping ID
|
|
262
|
+
* @returns {Promise<Object|null>} Mapping object with string IDs or null
|
|
263
|
+
*/
|
|
264
|
+
async findMappingById(id) {
|
|
265
|
+
return await this.prisma.integrationMapping.findUnique({
|
|
266
|
+
where: { id },
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Update mapping by ID
|
|
272
|
+
* @param {string} id - Mapping ID
|
|
273
|
+
* @param {Object} updates - Fields to update
|
|
274
|
+
* @returns {Promise<Object>} Updated mapping object with string IDs
|
|
275
|
+
*/
|
|
276
|
+
async updateMapping(id, updates) {
|
|
277
|
+
return await this.prisma.integrationMapping.update({
|
|
278
|
+
where: { id },
|
|
279
|
+
data: updates,
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
module.exports = { IntegrationMappingRepositoryMongo };
|
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
const { prisma } = require('../../database/prisma');
|
|
2
|
+
const { MappingAlreadyExistsError } = require('../../errors');
|
|
3
|
+
const {
|
|
4
|
+
assertMappingWrittenUnencrypted,
|
|
5
|
+
decryptQueriedMappings,
|
|
6
|
+
} = require('../../database/encryption/integration-mapping-encryption');
|
|
7
|
+
const {
|
|
8
|
+
IntegrationMappingRepositoryInterface,
|
|
9
|
+
} = require('./integration-mapping-repository-interface');
|
|
10
|
+
const { strictIntId } = require('./report-id');
|
|
11
|
+
const { validateMappingQuery } = require('./integration-mapping-query');
|
|
12
|
+
|
|
13
|
+
const COLUMNS = { mapping: '"mapping"', sourceId: '"sourceId"' };
|
|
14
|
+
const SQL_DIRECTIONS = { asc: 'ASC', desc: 'DESC' };
|
|
15
|
+
|
|
16
|
+
const jsonPathOperand = (column, path) => ({
|
|
17
|
+
json: `${column} #> ${path}::text[]`,
|
|
18
|
+
text: `${column} #>> ${path}::text[]`,
|
|
19
|
+
});
|
|
20
|
+
const jsonType = (json) => `COALESCE(jsonb_typeof(${json}), 'null')`;
|
|
21
|
+
|
|
22
|
+
const CONDITION_SQL = {
|
|
23
|
+
exists: ({ json }) => `${jsonType(json)} <> 'null'`,
|
|
24
|
+
notExists: ({ json }) => `${jsonType(json)} = 'null'`,
|
|
25
|
+
in: ({ json, text, value }) =>
|
|
26
|
+
`(jsonb_typeof(${json}) = 'string' AND ${text} = ANY(${value}::text[]))`,
|
|
27
|
+
notStartsWith: ({ text, value }) =>
|
|
28
|
+
`(${text} IS NULL OR NOT starts_with(${text}, ${value}::text))`,
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* PostgreSQL Integration Mapping Repository Adapter
|
|
33
|
+
* Handles persistence of integration mappings used for data transformation
|
|
34
|
+
*
|
|
35
|
+
* PostgreSQL-specific characteristics:
|
|
36
|
+
* - Uses Int IDs with autoincrement
|
|
37
|
+
* - Requires ID conversion: String (app layer) ↔ Int (database)
|
|
38
|
+
* - All returned IDs are converted to strings for application layer consistency
|
|
39
|
+
*/
|
|
40
|
+
class IntegrationMappingRepositoryPostgres extends IntegrationMappingRepositoryInterface {
|
|
41
|
+
constructor() {
|
|
42
|
+
super();
|
|
43
|
+
this.prisma = prisma;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Convert string ID to integer for PostgreSQL queries
|
|
48
|
+
* @private
|
|
49
|
+
* @param {string|number|null|undefined} id - ID to convert
|
|
50
|
+
* @returns {number|null|undefined} Integer ID or null/undefined
|
|
51
|
+
* @throws {Error} If ID cannot be converted to integer
|
|
52
|
+
*/
|
|
53
|
+
_stringToInt(id) {
|
|
54
|
+
if (id === null || id === undefined) return id;
|
|
55
|
+
const parsed = parseInt(id, 10);
|
|
56
|
+
if (isNaN(parsed)) {
|
|
57
|
+
throw new Error(`Invalid ID: ${id} cannot be converted to integer`);
|
|
58
|
+
}
|
|
59
|
+
return parsed;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Convert any value to string (handles null/undefined)
|
|
64
|
+
* @private
|
|
65
|
+
* @param {*} value - Value to convert
|
|
66
|
+
* @returns {string|null|undefined} String value or null/undefined
|
|
67
|
+
*/
|
|
68
|
+
_toString(value) {
|
|
69
|
+
if (value === null || value === undefined) return value;
|
|
70
|
+
return String(value);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Convert integer to string for application layer
|
|
75
|
+
* @private
|
|
76
|
+
* @param {number|null|undefined} id - Integer ID from database
|
|
77
|
+
* @returns {string|null|undefined} String ID or null/undefined
|
|
78
|
+
*/
|
|
79
|
+
_intToString(id) {
|
|
80
|
+
if (id === null || id === undefined) return id;
|
|
81
|
+
return id.toString();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Legacy alias for _stringToInt (for backward compatibility)
|
|
86
|
+
* @private
|
|
87
|
+
*/
|
|
88
|
+
_convertId(id) {
|
|
89
|
+
return this._stringToInt(id);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Convert mapping object IDs to strings
|
|
94
|
+
* @private
|
|
95
|
+
* @param {Object|null} mapping - Mapping object from database
|
|
96
|
+
* @returns {Object|null} Mapping with string IDs
|
|
97
|
+
*/
|
|
98
|
+
_convertMappingIds(mapping) {
|
|
99
|
+
if (!mapping) return mapping;
|
|
100
|
+
return {
|
|
101
|
+
...mapping,
|
|
102
|
+
id: this._intToString(mapping.id),
|
|
103
|
+
integrationId: this._intToString(mapping.integrationId),
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Find mapping by integration ID and source ID
|
|
109
|
+
* Replaces: IntegrationMapping.findBy(integrationId, sourceId)
|
|
110
|
+
*
|
|
111
|
+
* @param {string} integrationId - The integration ID (string from application layer)
|
|
112
|
+
* @param {string} sourceId - The source ID for lookup
|
|
113
|
+
* @returns {Promise<Object|null>} The mapping object with string IDs or null
|
|
114
|
+
*/
|
|
115
|
+
async findMappingBy(integrationId, sourceId) {
|
|
116
|
+
const mapping = await this.prisma.integrationMapping.findFirst({
|
|
117
|
+
where: {
|
|
118
|
+
integrationId: this._stringToInt(integrationId),
|
|
119
|
+
sourceId: this._toString(sourceId),
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
return this._convertMappingIds(mapping);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Create or update a mapping
|
|
127
|
+
* Replaces: IntegrationMapping.upsert(integrationId, sourceId, mapping)
|
|
128
|
+
*
|
|
129
|
+
* @param {string} integrationId - The integration ID (string from application layer)
|
|
130
|
+
* @param {string} sourceId - The source ID for lookup
|
|
131
|
+
* @param {Object} mapping - The mapping data
|
|
132
|
+
* @returns {Promise<Object>} The created or updated mapping document with string IDs
|
|
133
|
+
*/
|
|
134
|
+
async upsertMapping(integrationId, sourceId, mapping) {
|
|
135
|
+
const result = await this.prisma.integrationMapping.upsert({
|
|
136
|
+
where: {
|
|
137
|
+
integrationId_sourceId: {
|
|
138
|
+
integrationId: this._stringToInt(integrationId),
|
|
139
|
+
sourceId: this._toString(sourceId),
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
update: {
|
|
143
|
+
mapping,
|
|
144
|
+
},
|
|
145
|
+
create: {
|
|
146
|
+
integrationId: this._stringToInt(integrationId),
|
|
147
|
+
sourceId: this._toString(sourceId),
|
|
148
|
+
mapping,
|
|
149
|
+
},
|
|
150
|
+
});
|
|
151
|
+
return this._convertMappingIds(result);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Insert a mapping; fails if (integrationId, sourceId) already exists
|
|
156
|
+
*
|
|
157
|
+
* @param {string} integrationId - The integration ID
|
|
158
|
+
* @param {string} sourceId - The source ID
|
|
159
|
+
* @param {Object} mapping - The mapping data
|
|
160
|
+
* @returns {Promise<Object>} The created mapping document
|
|
161
|
+
* @throws {MappingAlreadyExistsError} If the pair already exists
|
|
162
|
+
*/
|
|
163
|
+
async createMapping(integrationId, sourceId, mapping) {
|
|
164
|
+
try {
|
|
165
|
+
const created = await this.prisma.integrationMapping.create({
|
|
166
|
+
data: {
|
|
167
|
+
integrationId: this._stringToInt(integrationId),
|
|
168
|
+
sourceId: this._toString(sourceId),
|
|
169
|
+
mapping,
|
|
170
|
+
},
|
|
171
|
+
});
|
|
172
|
+
return this._convertMappingIds(created);
|
|
173
|
+
} catch (error) {
|
|
174
|
+
if (error.code === 'P2002') {
|
|
175
|
+
throw new MappingAlreadyExistsError(integrationId, sourceId, {
|
|
176
|
+
cause: error,
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
throw error;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Find all mappings for an integration
|
|
185
|
+
* Replaces: IntegrationMapping.find({ integration: integrationId })
|
|
186
|
+
*
|
|
187
|
+
* @param {string} integrationId - The integration ID (string from application layer)
|
|
188
|
+
* @returns {Promise<Array>} Array of mapping documents with string IDs
|
|
189
|
+
*/
|
|
190
|
+
async findMappingsByIntegration(integrationId) {
|
|
191
|
+
const intIntegrationId = this._convertId(integrationId);
|
|
192
|
+
const mappings = await this.prisma.integrationMapping.findMany({
|
|
193
|
+
where: { integrationId: intIntegrationId },
|
|
194
|
+
});
|
|
195
|
+
return mappings.map((m) => this._convertMappingIds(m));
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Delete a mapping by integration and source ID
|
|
200
|
+
* Replaces: IntegrationMapping.deleteOne({ integration, sourceId })
|
|
201
|
+
*
|
|
202
|
+
* @param {string} integrationId - The integration ID (string from application layer)
|
|
203
|
+
* @param {string} sourceId - The source ID
|
|
204
|
+
* @returns {Promise<Object>} The deletion result
|
|
205
|
+
*/
|
|
206
|
+
async deleteMapping(integrationId, sourceId) {
|
|
207
|
+
try {
|
|
208
|
+
await this.prisma.integrationMapping.delete({
|
|
209
|
+
where: {
|
|
210
|
+
integrationId_sourceId: {
|
|
211
|
+
integrationId: this._stringToInt(integrationId),
|
|
212
|
+
sourceId: this._toString(sourceId),
|
|
213
|
+
},
|
|
214
|
+
},
|
|
215
|
+
});
|
|
216
|
+
return { acknowledged: true, deletedCount: 1 };
|
|
217
|
+
} catch (error) {
|
|
218
|
+
if (error.code === 'P2025') {
|
|
219
|
+
// Record not found
|
|
220
|
+
return { acknowledged: true, deletedCount: 0 };
|
|
221
|
+
}
|
|
222
|
+
throw error;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Delete all mappings for an integration
|
|
228
|
+
* Replaces: IntegrationMapping.deleteMany({ integration: integrationId })
|
|
229
|
+
*
|
|
230
|
+
* @param {string} integrationId - The integration ID (string from application layer)
|
|
231
|
+
* @returns {Promise<Object>} The deletion result
|
|
232
|
+
*/
|
|
233
|
+
async deleteMappingsByIntegration(integrationId) {
|
|
234
|
+
const intIntegrationId = this._convertId(integrationId);
|
|
235
|
+
const result = await this.prisma.integrationMapping.deleteMany({
|
|
236
|
+
where: { integrationId: intIntegrationId },
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
return {
|
|
240
|
+
acknowledged: true,
|
|
241
|
+
deletedCount: result.count,
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Count mappings grouped by integration id for a bounded id set.
|
|
247
|
+
*
|
|
248
|
+
* @param {Array<string|number>} ids - Integration ids
|
|
249
|
+
* @returns {Promise<Map<string, number>>} integrationId (string) → count
|
|
250
|
+
*/
|
|
251
|
+
async countByIntegrationIds(ids = []) {
|
|
252
|
+
const counts = new Map();
|
|
253
|
+
if (!ids || ids.length === 0) return counts;
|
|
254
|
+
|
|
255
|
+
// Strict (matches findAllForReport): reject partially-numeric ids instead of coercing.
|
|
256
|
+
const intIds = ids.map((id) => strictIntId(id));
|
|
257
|
+
const groups = await this.prisma.integrationMapping.groupBy({
|
|
258
|
+
by: ['integrationId'],
|
|
259
|
+
where: { integrationId: { in: intIds } },
|
|
260
|
+
_count: { _all: true },
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
for (const group of groups) {
|
|
264
|
+
counts.set(
|
|
265
|
+
this._intToString(group.integrationId),
|
|
266
|
+
group._count._all
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
return counts;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* @param {string} integrationId
|
|
274
|
+
* @param {Object} query - See IntegrationMappingRepositoryInterface.queryMappings
|
|
275
|
+
* @returns {Promise<{mappings: Array<Object>, total: number}>}
|
|
276
|
+
*/
|
|
277
|
+
async queryMappings(integrationId, query) {
|
|
278
|
+
const { where, orderBy, skip, take, omit } =
|
|
279
|
+
validateMappingQuery(query);
|
|
280
|
+
const intIntegrationId = strictIntId(integrationId);
|
|
281
|
+
assertMappingWrittenUnencrypted();
|
|
282
|
+
|
|
283
|
+
const params = [];
|
|
284
|
+
const bind = (v) => {
|
|
285
|
+
params.push(v);
|
|
286
|
+
return `$${params.length}`;
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
const whereSql = [
|
|
290
|
+
`"integrationId" = ${bind(intIntegrationId)}::int`,
|
|
291
|
+
`jsonb_typeof("mapping") = 'object'`,
|
|
292
|
+
...where.map((entry) => this._whereEntrySql(entry, bind)),
|
|
293
|
+
].join(' AND ');
|
|
294
|
+
const orderSql = orderBy ? this._orderSql(orderBy, bind) : `"id" ASC`;
|
|
295
|
+
const mappingSql =
|
|
296
|
+
omit.length > 0
|
|
297
|
+
? `"mapping" - ${bind(omit)}::text[] AS "mapping"`
|
|
298
|
+
: `"mapping"`;
|
|
299
|
+
|
|
300
|
+
const sql = `
|
|
301
|
+
WITH "matched" AS (
|
|
302
|
+
SELECT "id", "integrationId", "sourceId", "mapping", "createdAt", "updatedAt"
|
|
303
|
+
FROM "IntegrationMapping"
|
|
304
|
+
WHERE ${whereSql}
|
|
305
|
+
),
|
|
306
|
+
"page" AS (
|
|
307
|
+
SELECT * FROM "matched"
|
|
308
|
+
ORDER BY ${orderSql}
|
|
309
|
+
OFFSET ${bind(skip)}::bigint
|
|
310
|
+
LIMIT ${bind(take)}::int
|
|
311
|
+
)
|
|
312
|
+
SELECT "id", "integrationId", "sourceId", ${mappingSql}, "createdAt", "updatedAt",
|
|
313
|
+
(SELECT COUNT(*)::int FROM "matched") AS "__total"
|
|
314
|
+
FROM (VALUES (1)) AS "one"
|
|
315
|
+
LEFT JOIN "page" ON true
|
|
316
|
+
ORDER BY ${orderSql}
|
|
317
|
+
`;
|
|
318
|
+
const rows = await this.prisma.$queryRawUnsafe(sql, ...params);
|
|
319
|
+
const mappings = await decryptQueriedMappings(
|
|
320
|
+
rows
|
|
321
|
+
.filter((row) => row.id !== null)
|
|
322
|
+
.map(({ __total, ...row }) => this._convertMappingIds(row))
|
|
323
|
+
);
|
|
324
|
+
|
|
325
|
+
return { mappings, total: rows[0].__total };
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* One where entry: a condition, or an anyOf group as a parenthesized OR.
|
|
330
|
+
* @private
|
|
331
|
+
*/
|
|
332
|
+
_whereEntrySql(entry, bind) {
|
|
333
|
+
if (!entry.anyOf) return this._conditionSql(entry, bind);
|
|
334
|
+
const alternatives = entry.anyOf.map((condition) =>
|
|
335
|
+
this._conditionSql(condition, bind)
|
|
336
|
+
);
|
|
337
|
+
return `(${alternatives.join(' OR ')})`;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* SQL predicate for one validated queryMappings condition. `exists`
|
|
342
|
+
* treats JSON null as absent, and `notExists` is its exact negation.
|
|
343
|
+
* @private
|
|
344
|
+
*/
|
|
345
|
+
_conditionSql({ field, path, op, value }, bind) {
|
|
346
|
+
const column = COLUMNS[field];
|
|
347
|
+
const operand = path
|
|
348
|
+
? jsonPathOperand(column, bind(path))
|
|
349
|
+
: { text: column };
|
|
350
|
+
return CONDITION_SQL[op]({
|
|
351
|
+
...operand,
|
|
352
|
+
value: value === undefined ? undefined : bind(value),
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* NULLIF folds JSON null into SQL NULL, so both sort after every value.
|
|
358
|
+
* @private
|
|
359
|
+
*/
|
|
360
|
+
_orderSql({ path, direction }, bind) {
|
|
361
|
+
const { json } = jsonPathOperand(COLUMNS.mapping, bind(path));
|
|
362
|
+
const value = `NULLIF(${json}, 'null'::jsonb)`;
|
|
363
|
+
const sqlDirection = SQL_DIRECTIONS[direction];
|
|
364
|
+
return `${value} ${sqlDirection} NULLS LAST, "id" ${sqlDirection}`;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* Find mapping by ID
|
|
369
|
+
* @param {string} id - Mapping ID (string from application layer)
|
|
370
|
+
* @returns {Promise<Object|null>} Mapping object with string IDs or null
|
|
371
|
+
*/
|
|
372
|
+
async findMappingById(id) {
|
|
373
|
+
const intId = this._convertId(id);
|
|
374
|
+
const mapping = await this.prisma.integrationMapping.findUnique({
|
|
375
|
+
where: { id: intId },
|
|
376
|
+
});
|
|
377
|
+
return this._convertMappingIds(mapping);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* Update mapping by ID
|
|
382
|
+
* @param {string} id - Mapping ID (string from application layer)
|
|
383
|
+
* @param {Object} updates - Fields to update (with string IDs from application layer)
|
|
384
|
+
* @returns {Promise<Object>} Updated mapping object with string IDs
|
|
385
|
+
*/
|
|
386
|
+
async updateMapping(id, updates) {
|
|
387
|
+
const intId = this._convertId(id);
|
|
388
|
+
|
|
389
|
+
// Convert integrationId if present in updates
|
|
390
|
+
const data = { ...updates };
|
|
391
|
+
if (data.integrationId !== undefined) {
|
|
392
|
+
data.integrationId = this._convertId(data.integrationId);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
const mapping = await this.prisma.integrationMapping.update({
|
|
396
|
+
where: { id: intId },
|
|
397
|
+
data,
|
|
398
|
+
});
|
|
399
|
+
return this._convertMappingIds(mapping);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
module.exports = { IntegrationMappingRepositoryPostgres };
|