@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,39 @@
|
|
|
1
|
+
class TestModuleRepository {
|
|
2
|
+
constructor() {
|
|
3
|
+
this.entities = new Map();
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
addEntity(entity) {
|
|
7
|
+
this.entities.set(entity.id, entity);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
async findEntityById(id) {
|
|
11
|
+
return this.entities.get(id);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async findEntitiesByIds(ids) {
|
|
15
|
+
return ids.map((id) => this.entities.get(id));
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async findEntity(filter) {
|
|
19
|
+
if (!filter || typeof filter !== 'object') {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (filter.id && this.entities.has(filter.id)) {
|
|
24
|
+
return this.entities.get(filter.id);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (filter.externalId) {
|
|
28
|
+
for (const entity of this.entities.values()) {
|
|
29
|
+
if (entity.externalId === filter.externalId) {
|
|
30
|
+
return entity;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
module.exports = { TestModuleRepository };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const { Module } = require('../module');
|
|
2
|
+
const { mapModuleClassToModuleDTO } = require('../utils/map-module-dto');
|
|
3
|
+
|
|
4
|
+
class GetEntitiesForUser {
|
|
5
|
+
constructor({ moduleRepository, moduleDefinitions }) {
|
|
6
|
+
this.moduleRepository = moduleRepository;
|
|
7
|
+
|
|
8
|
+
this.definitionMap = new Map();
|
|
9
|
+
for (const definition of moduleDefinitions) {
|
|
10
|
+
this.definitionMap.set(definition.moduleName, definition);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async execute(userId) {
|
|
15
|
+
const entities = await this.moduleRepository.findEntitiesByUserId(
|
|
16
|
+
userId
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
return entities.map((entity) => {
|
|
20
|
+
const definition = this.definitionMap.get(entity.moduleName);
|
|
21
|
+
|
|
22
|
+
const moduleInstance = new Module({
|
|
23
|
+
userId,
|
|
24
|
+
definition: definition,
|
|
25
|
+
entity: entity,
|
|
26
|
+
});
|
|
27
|
+
return mapModuleClassToModuleDTO(moduleInstance);
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
module.exports = { GetEntitiesForUser };
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
const { Module } = require('../module');
|
|
2
|
+
|
|
3
|
+
class GetEntityOptionsById {
|
|
4
|
+
/**
|
|
5
|
+
* @param {Object} params
|
|
6
|
+
* @param {import('../repositories/module-repository-interface').ModuleRepositoryInterface} params.moduleRepository
|
|
7
|
+
* @param {} params.moduleDefinitions
|
|
8
|
+
*/
|
|
9
|
+
constructor({ moduleRepository, moduleDefinitions }) {
|
|
10
|
+
this.moduleRepository = moduleRepository;
|
|
11
|
+
this.moduleDefinitions = moduleDefinitions;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Retrieve entity options for a given entity
|
|
16
|
+
*
|
|
17
|
+
* @param {string|number} entityId - Entity ID to retrieve options for
|
|
18
|
+
* @param {string|number|import('../../user/user').User} userIdOrUser - User ID or User object for validation
|
|
19
|
+
* @returns {Promise<Object>} Entity options
|
|
20
|
+
*/
|
|
21
|
+
async execute(entityId, userIdOrUser) {
|
|
22
|
+
// Support both userId (backward compatible) and User object (new pattern)
|
|
23
|
+
const userId = typeof userIdOrUser === 'object' && userIdOrUser?.getId
|
|
24
|
+
? userIdOrUser.getId()
|
|
25
|
+
: userIdOrUser;
|
|
26
|
+
|
|
27
|
+
const entity = await this.moduleRepository.findEntityById(
|
|
28
|
+
entityId,
|
|
29
|
+
userId
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
if (!entity) {
|
|
33
|
+
throw new Error(`Entity ${entityId} not found`);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Validate entity ownership
|
|
37
|
+
const isOwned = typeof userIdOrUser === 'object' && userIdOrUser?.ownsUserId
|
|
38
|
+
? userIdOrUser.ownsUserId(entity.userId)
|
|
39
|
+
: entity.userId?.toString() === userId?.toString();
|
|
40
|
+
|
|
41
|
+
if (!isOwned) {
|
|
42
|
+
throw new Error(
|
|
43
|
+
`Entity ${entityId} does not belong to user ${userId}`
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const entityType = entity.moduleName;
|
|
48
|
+
const moduleDefinition = this.moduleDefinitions.find((def) => {
|
|
49
|
+
const modelName =
|
|
50
|
+
Module.getEntityModelFromDefinition(def).modelName;
|
|
51
|
+
return entityType === modelName;
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
if (!moduleDefinition) {
|
|
55
|
+
throw new Error(
|
|
56
|
+
`Module definition not found for entity type: ${entityType}`
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const module = new Module({
|
|
61
|
+
userId,
|
|
62
|
+
entity,
|
|
63
|
+
definition: moduleDefinition,
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const entityOptions = await module.getEntityOptions();
|
|
67
|
+
return entityOptions;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
module.exports = { GetEntityOptionsById };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
const { Module } = require('../module');
|
|
2
|
+
|
|
3
|
+
class GetEntityOptionsByType {
|
|
4
|
+
/**
|
|
5
|
+
* @param {Object} params
|
|
6
|
+
* @param {} params.moduleDefinitions
|
|
7
|
+
*/
|
|
8
|
+
constructor({ moduleDefinitions }) {
|
|
9
|
+
this.moduleDefinitions = moduleDefinitions;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Retrieve a Module instance for a given user and entity/module type.
|
|
14
|
+
* @param {string} userId
|
|
15
|
+
* @param {string} type – human-readable module/entity type (e.g. "Hubspot")
|
|
16
|
+
*/
|
|
17
|
+
async execute(userId, type) {
|
|
18
|
+
const moduleDefinition = this.moduleDefinitions.find(
|
|
19
|
+
(def) => def.getName() === type
|
|
20
|
+
);
|
|
21
|
+
if (!moduleDefinition) {
|
|
22
|
+
throw new Error(`Module definition not found for type: ${type}`);
|
|
23
|
+
}
|
|
24
|
+
const moduleInstance = new Module({
|
|
25
|
+
userId,
|
|
26
|
+
definition: moduleDefinition,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const entityOptions = await moduleInstance.getEntityOptions();
|
|
30
|
+
return entityOptions;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
module.exports = { GetEntityOptionsByType };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
const { Module } = require('../module');
|
|
2
|
+
|
|
3
|
+
class GetModuleInstanceFromType {
|
|
4
|
+
/**
|
|
5
|
+
* @param {Object} params
|
|
6
|
+
* @param {} params.moduleDefinitions
|
|
7
|
+
*/
|
|
8
|
+
constructor({ moduleDefinitions }) {
|
|
9
|
+
this.moduleDefinitions = moduleDefinitions;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Retrieve a Module instance for a given user and entity/module type.
|
|
14
|
+
* @param {string} userId
|
|
15
|
+
* @param {string} type – human-readable module/entity type (e.g. "Hubspot")
|
|
16
|
+
* @param {Object} [options]
|
|
17
|
+
* @param {string} [options.state] – optional OAuth state value to be forwarded to the API client (round-trips through the OAuth provider).
|
|
18
|
+
*/
|
|
19
|
+
async execute(userId, type, options = {}) {
|
|
20
|
+
const moduleDefinition = this.moduleDefinitions.find(
|
|
21
|
+
(def) => def.getName() === type
|
|
22
|
+
);
|
|
23
|
+
if (!moduleDefinition) {
|
|
24
|
+
throw new Error(`Module definition not found for type: ${type}`);
|
|
25
|
+
}
|
|
26
|
+
return new Module({
|
|
27
|
+
userId,
|
|
28
|
+
definition: moduleDefinition,
|
|
29
|
+
state: options.state,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
module.exports = { GetModuleInstanceFromType };
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
const { Module } = require('../module');
|
|
2
|
+
|
|
3
|
+
class GetModule {
|
|
4
|
+
constructor({ moduleRepository, moduleDefinitions }) {
|
|
5
|
+
this.moduleRepository = moduleRepository;
|
|
6
|
+
this.moduleDefinitions = moduleDefinitions;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Get module instance for an entity
|
|
11
|
+
*
|
|
12
|
+
* @param {string|number} entityId - Entity ID to retrieve
|
|
13
|
+
* @param {string|number|import('../../user/user').User} userIdOrUser - User ID or User object for validation
|
|
14
|
+
* @returns {Promise<Object>} Module details
|
|
15
|
+
*/
|
|
16
|
+
async execute(entityId, userIdOrUser) {
|
|
17
|
+
// Support both userId (backward compatible) and User object (new pattern)
|
|
18
|
+
const userId = typeof userIdOrUser === 'object' && userIdOrUser?.getId
|
|
19
|
+
? userIdOrUser.getId()
|
|
20
|
+
: userIdOrUser;
|
|
21
|
+
|
|
22
|
+
const entity = await this.moduleRepository.findEntityById(
|
|
23
|
+
entityId,
|
|
24
|
+
userId
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
if (!entity) {
|
|
28
|
+
throw new Error(`Entity ${entityId} not found`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Validate entity ownership
|
|
32
|
+
// If User object provided, use ownsUserId to check linked users
|
|
33
|
+
// Otherwise fall back to simple equality check
|
|
34
|
+
const isOwned = typeof userIdOrUser === 'object' && userIdOrUser?.ownsUserId
|
|
35
|
+
? userIdOrUser.ownsUserId(entity.userId)
|
|
36
|
+
: entity.userId?.toString() === userId?.toString();
|
|
37
|
+
|
|
38
|
+
if (!isOwned) {
|
|
39
|
+
throw new Error(
|
|
40
|
+
`Entity ${entityId} does not belong to user ${userId}`
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const entityType = entity.moduleName;
|
|
45
|
+
const moduleDefinition = this.moduleDefinitions.find((def) => {
|
|
46
|
+
const modelName = Module.getEntityModelFromDefinition(def).modelName;
|
|
47
|
+
return entityType === modelName;
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
if (!moduleDefinition) {
|
|
51
|
+
throw new Error(
|
|
52
|
+
`Module definition not found for entity type: ${entityType}`
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const module = new Module({
|
|
57
|
+
userId,
|
|
58
|
+
entity,
|
|
59
|
+
definition: moduleDefinition,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// todo: this properties should be methods in the Module class
|
|
63
|
+
return {
|
|
64
|
+
id: module.entity.id,
|
|
65
|
+
name: module.entity.name,
|
|
66
|
+
moduleName: module.entity.moduleName,
|
|
67
|
+
credential: module.credential,
|
|
68
|
+
externalId: module.entity.externalId,
|
|
69
|
+
userId: module.entity.user.toString(),
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
module.exports = { GetModule };
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
const { Module } = require('../module');
|
|
2
|
+
const { ModuleConstants } = require('../ModuleConstants');
|
|
3
|
+
const { getLogger } = require('../../logs');
|
|
4
|
+
|
|
5
|
+
const log = getLogger('frigg.modules.authorization_callback');
|
|
6
|
+
|
|
7
|
+
// Statuses considered "broken" for an integration whose credentials have just
|
|
8
|
+
// been successfully re-authorized. Both ERROR (system-driven auth failure) and
|
|
9
|
+
// DISABLED (user paused the integration) are flipped back to ENABLED when the
|
|
10
|
+
// user completes a new authorization flow.
|
|
11
|
+
const STATUSES_RESET_ON_REAUTH = ['ERROR', 'DISABLED'];
|
|
12
|
+
|
|
13
|
+
class ProcessAuthorizationCallback {
|
|
14
|
+
/**
|
|
15
|
+
* @param {Object} params - Configuration parameters.
|
|
16
|
+
* @param {import('../repositories/module-repository-factory').ModuleRepositoryInterface} params.moduleRepository - Repository for module data operations.
|
|
17
|
+
* @param {import('../../credential/repositories/credential-repository-factory').CredentialRepositoryInterface} params.credentialRepository - Repository for credential data operations.
|
|
18
|
+
* @param {Array<Object>} params.moduleDefinitions - Array of module definitions.
|
|
19
|
+
* @param {import('../../integrations/repositories/integration-repository-interface').IntegrationRepositoryInterface} [params.integrationRepository] - Repository for integration data operations. When provided, integrations in a broken state linked to the re-authorized entity are restored to ENABLED.
|
|
20
|
+
*/
|
|
21
|
+
constructor({
|
|
22
|
+
moduleRepository,
|
|
23
|
+
credentialRepository,
|
|
24
|
+
moduleDefinitions,
|
|
25
|
+
integrationRepository,
|
|
26
|
+
}) {
|
|
27
|
+
this.moduleRepository = moduleRepository;
|
|
28
|
+
this.credentialRepository = credentialRepository;
|
|
29
|
+
this.moduleDefinitions = moduleDefinitions;
|
|
30
|
+
this.integrationRepository = integrationRepository;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async execute(userId, entityType, params) {
|
|
34
|
+
log.debug('Authorization callback started', {
|
|
35
|
+
eventName: `${log.name}.started`,
|
|
36
|
+
userId,
|
|
37
|
+
entityType,
|
|
38
|
+
hasCode: Boolean(params && params.code),
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
const moduleDefinition = this.moduleDefinitions.find((def) => {
|
|
42
|
+
return entityType === def.moduleName;
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
if (!moduleDefinition) {
|
|
46
|
+
throw new Error(
|
|
47
|
+
`Module definition not found for entity type: ${entityType}`
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Bootstrap the Module with the existing entity (if any) so the API
|
|
52
|
+
// requester is preloaded with prior tokens. This enables a refresh
|
|
53
|
+
// fallback when callers lack a fresh OAuth code, and lets us match a
|
|
54
|
+
// re-auth back to the existing credential record by id.
|
|
55
|
+
const existingEntities =
|
|
56
|
+
await this.moduleRepository.findEntitiesByUserIdAndModuleName(
|
|
57
|
+
userId,
|
|
58
|
+
entityType
|
|
59
|
+
);
|
|
60
|
+
const existingEntity =
|
|
61
|
+
existingEntities && existingEntities.length > 0
|
|
62
|
+
? existingEntities[0]
|
|
63
|
+
: null;
|
|
64
|
+
|
|
65
|
+
if (existingEntity) {
|
|
66
|
+
log.debug('Existing entity found', {
|
|
67
|
+
eventName: `${log.name}.existing_entity_found`,
|
|
68
|
+
entityId: existingEntity.id,
|
|
69
|
+
credentialId: existingEntity.credential?.id,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const module = new Module({
|
|
74
|
+
userId,
|
|
75
|
+
entity: existingEntity,
|
|
76
|
+
definition: moduleDefinition,
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
const authType = module.apiClass.requesterType;
|
|
80
|
+
log.debug('Module created', {
|
|
81
|
+
eventName: `${log.name}.module_created`,
|
|
82
|
+
moduleName: module.getName(),
|
|
83
|
+
authType,
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
let tokenResponse;
|
|
87
|
+
if (authType === ModuleConstants.authType.oauth2) {
|
|
88
|
+
log.debug('Exchanging authorization code for token', {
|
|
89
|
+
eventName: `${log.name}.token_exchanging`,
|
|
90
|
+
});
|
|
91
|
+
tokenResponse = await moduleDefinition.requiredAuthMethods.getToken(
|
|
92
|
+
module.api,
|
|
93
|
+
params
|
|
94
|
+
);
|
|
95
|
+
log.debug('Token exchange completed', {
|
|
96
|
+
eventName: `${log.name}.token_exchanged`,
|
|
97
|
+
tokenKeys: Object.keys(tokenResponse || {}),
|
|
98
|
+
});
|
|
99
|
+
// Belt-and-suspenders: persist tokens explicitly here rather than
|
|
100
|
+
// relying solely on the DLGT_TOKEN_UPDATE notification chain
|
|
101
|
+
// inside setTokens. The notification path remains in place but
|
|
102
|
+
// has been observed to no-op silently in some prod paths,
|
|
103
|
+
// leaving newly-issued tokens unsaved while the user-visible
|
|
104
|
+
// OAuth flow appears to succeed.
|
|
105
|
+
await this.onTokenUpdate(module, moduleDefinition, userId);
|
|
106
|
+
} else {
|
|
107
|
+
log.debug('Setting auth params', {
|
|
108
|
+
eventName: `${log.name}.auth_params_setting`,
|
|
109
|
+
});
|
|
110
|
+
tokenResponse =
|
|
111
|
+
await moduleDefinition.requiredAuthMethods.setAuthParams(
|
|
112
|
+
module.api,
|
|
113
|
+
params
|
|
114
|
+
);
|
|
115
|
+
await this.onTokenUpdate(module, moduleDefinition, userId);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
log.debug('Credential persisted', {
|
|
119
|
+
eventName: `${log.name}.credential_persisted`,
|
|
120
|
+
credentialId: module.credential?.id,
|
|
121
|
+
authIsValid: module.credential?.authIsValid,
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
log.debug('Testing auth', { eventName: `${log.name}.auth_testing` });
|
|
125
|
+
const authRes = await module.testAuth();
|
|
126
|
+
if (!authRes) {
|
|
127
|
+
throw new Error('Authorization failed');
|
|
128
|
+
}
|
|
129
|
+
log.debug('Auth test passed', {
|
|
130
|
+
eventName: `${log.name}.auth_test_passed`,
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
log.debug('Fetching entity details', {
|
|
134
|
+
eventName: `${log.name}.entity_details_fetching`,
|
|
135
|
+
});
|
|
136
|
+
const entityDetails =
|
|
137
|
+
await moduleDefinition.requiredAuthMethods.getEntityDetails(
|
|
138
|
+
module.api,
|
|
139
|
+
params,
|
|
140
|
+
tokenResponse,
|
|
141
|
+
userId
|
|
142
|
+
);
|
|
143
|
+
log.debug('Entity details received', {
|
|
144
|
+
eventName: `${log.name}.entity_details_received`,
|
|
145
|
+
externalId: entityDetails.identifiers?.externalId,
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
Object.assign(
|
|
149
|
+
entityDetails.details,
|
|
150
|
+
module.apiParamsFromEntity(module.api)
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
log.debug('Finding or creating entity', {
|
|
154
|
+
eventName: `${log.name}.entity_resolving`,
|
|
155
|
+
});
|
|
156
|
+
const persistedEntity = await this.findOrCreateEntity(
|
|
157
|
+
entityDetails,
|
|
158
|
+
entityType,
|
|
159
|
+
module.credential.id
|
|
160
|
+
);
|
|
161
|
+
log.debug('Authorization callback completed', {
|
|
162
|
+
eventName: `${log.name}.completed`,
|
|
163
|
+
entityId: persistedEntity.id,
|
|
164
|
+
credentialId: module.credential.id,
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
// Best-effort: a hiccup here must not fail a successful re-auth whose
|
|
168
|
+
// credential + entity are already persisted. Operators can recover
|
|
169
|
+
// stuck integrations manually.
|
|
170
|
+
try {
|
|
171
|
+
const restoredCount = await this.restoreIntegrationsForEntity(
|
|
172
|
+
persistedEntity.id
|
|
173
|
+
);
|
|
174
|
+
log.debug('Integrations restored', {
|
|
175
|
+
eventName: `${log.name}.integrations_restored`,
|
|
176
|
+
entityId: persistedEntity.id,
|
|
177
|
+
restoredCount,
|
|
178
|
+
});
|
|
179
|
+
} catch (err) {
|
|
180
|
+
log.error('Failed to restore integrations after re-auth', {
|
|
181
|
+
eventName: `${log.name}.integrations_restore_failed`,
|
|
182
|
+
entityId: persistedEntity.id,
|
|
183
|
+
error: err,
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return {
|
|
188
|
+
credential_id: module.credential.id,
|
|
189
|
+
entity_id: persistedEntity.id,
|
|
190
|
+
type: module.getName(),
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
async restoreIntegrationsForEntity(entityId) {
|
|
195
|
+
if (!this.integrationRepository) return 0;
|
|
196
|
+
const integrations =
|
|
197
|
+
await this.integrationRepository.findIntegrationsByEntityId(
|
|
198
|
+
entityId
|
|
199
|
+
);
|
|
200
|
+
let restored = 0;
|
|
201
|
+
for (const integration of integrations) {
|
|
202
|
+
if (STATUSES_RESET_ON_REAUTH.includes(integration.status)) {
|
|
203
|
+
log.info('Integration restored to ENABLED after re-auth', {
|
|
204
|
+
eventName: `${log.name}.integration_restored`,
|
|
205
|
+
integrationId: integration.id,
|
|
206
|
+
previousStatus: integration.status,
|
|
207
|
+
entityId,
|
|
208
|
+
});
|
|
209
|
+
await this.integrationRepository.updateIntegrationStatus(
|
|
210
|
+
integration.id,
|
|
211
|
+
'ENABLED'
|
|
212
|
+
);
|
|
213
|
+
restored++;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return restored;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
async onTokenUpdate(module, moduleDefinition, userId) {
|
|
220
|
+
const credentialDetails =
|
|
221
|
+
await moduleDefinition.requiredAuthMethods.getCredentialDetails(
|
|
222
|
+
module.api,
|
|
223
|
+
userId
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
Object.assign(
|
|
227
|
+
credentialDetails.details,
|
|
228
|
+
module.apiParamsFromCredential(module.api)
|
|
229
|
+
);
|
|
230
|
+
credentialDetails.details.authIsValid = true;
|
|
231
|
+
|
|
232
|
+
const persisted = await this.credentialRepository.upsertCredential(credentialDetails);
|
|
233
|
+
module.credential = persisted;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
async findOrCreateEntity(entityDetails, moduleName, credentialId) {
|
|
237
|
+
const { identifiers, details } = entityDetails;
|
|
238
|
+
|
|
239
|
+
// Support both 'user' and 'userId' field names from module definitions
|
|
240
|
+
// Some modules use 'user' (legacy), others use 'userId' (newer pattern)
|
|
241
|
+
const userId = identifiers.user || identifiers.userId;
|
|
242
|
+
|
|
243
|
+
if (!userId) {
|
|
244
|
+
throw new Error(
|
|
245
|
+
`Module definition for ${moduleName} must return 'user' or 'userId' in identifiers from getEntityDetails(). ` +
|
|
246
|
+
`Without userId, entity lookup would match across all users (security issue).`
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const existingEntity = await this.moduleRepository.findEntity({
|
|
251
|
+
externalId: identifiers.externalId,
|
|
252
|
+
user: userId,
|
|
253
|
+
moduleName: moduleName,
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
if (existingEntity) {
|
|
257
|
+
// Repoint the entity's credentialId when re-auth produced a
|
|
258
|
+
// different credential than the one currently linked. This
|
|
259
|
+
// happens when the user re-authenticates against a different
|
|
260
|
+
// workspace/account of the same provider — `upsertCredential`
|
|
261
|
+
// matches/creates a credential keyed by externalId, but
|
|
262
|
+
// findEntity matches the entity by its own externalId, leaving
|
|
263
|
+
// the entity still linked to the prior workspace's credential
|
|
264
|
+
// unless we explicitly update the link.
|
|
265
|
+
const existingCredentialId = existingEntity.credential?.id;
|
|
266
|
+
if (
|
|
267
|
+
credentialId &&
|
|
268
|
+
String(existingCredentialId) !== String(credentialId)
|
|
269
|
+
) {
|
|
270
|
+
log.info('Entity credential repointed after re-auth', {
|
|
271
|
+
eventName: `${log.name}.entity_credential_repointed`,
|
|
272
|
+
entityId: existingEntity.id,
|
|
273
|
+
previousCredentialId: existingCredentialId,
|
|
274
|
+
credentialId,
|
|
275
|
+
});
|
|
276
|
+
const updated = await this.moduleRepository.updateEntity(
|
|
277
|
+
existingEntity.id,
|
|
278
|
+
{ credential: credentialId }
|
|
279
|
+
);
|
|
280
|
+
if (updated) return updated;
|
|
281
|
+
}
|
|
282
|
+
return existingEntity;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
return await this.moduleRepository.createEntity({
|
|
286
|
+
...identifiers,
|
|
287
|
+
...details,
|
|
288
|
+
moduleName: moduleName,
|
|
289
|
+
credential: credentialId,
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
module.exports = { ProcessAuthorizationCallback };
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
const { Module } = require('../module');
|
|
2
|
+
|
|
3
|
+
class RefreshEntityOptions {
|
|
4
|
+
/**
|
|
5
|
+
* @param {Object} params
|
|
6
|
+
* @param {import('../repositories/module-repository-interface').ModuleRepositoryInterface} params.moduleRepository
|
|
7
|
+
* @param {} params.moduleDefinitions
|
|
8
|
+
*/
|
|
9
|
+
constructor({ moduleRepository, moduleDefinitions }) {
|
|
10
|
+
this.moduleRepository = moduleRepository;
|
|
11
|
+
this.moduleDefinitions = moduleDefinitions;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Refresh entity options for a given entity
|
|
16
|
+
*
|
|
17
|
+
* @param {string|number} entityId - Entity ID to refresh
|
|
18
|
+
* @param {string|number|import('../../user/user').User} userIdOrUser - User ID or User object for validation
|
|
19
|
+
* @param {Object} options - Refresh options
|
|
20
|
+
* @returns {Promise<Object>} Updated entity options
|
|
21
|
+
*/
|
|
22
|
+
async execute(entityId, userIdOrUser, options) {
|
|
23
|
+
// Support both userId (backward compatible) and User object (new pattern)
|
|
24
|
+
const userId = typeof userIdOrUser === 'object' && userIdOrUser?.getId
|
|
25
|
+
? userIdOrUser.getId()
|
|
26
|
+
: userIdOrUser;
|
|
27
|
+
|
|
28
|
+
const entity = await this.moduleRepository.findEntityById(
|
|
29
|
+
entityId,
|
|
30
|
+
userId
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
if (!entity) {
|
|
34
|
+
throw new Error(`Entity ${entityId} not found`);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Validate entity ownership
|
|
38
|
+
const isOwned = typeof userIdOrUser === 'object' && userIdOrUser?.ownsUserId
|
|
39
|
+
? userIdOrUser.ownsUserId(entity.userId)
|
|
40
|
+
: entity.userId?.toString() === userId?.toString();
|
|
41
|
+
|
|
42
|
+
if (!isOwned) {
|
|
43
|
+
throw new Error(
|
|
44
|
+
`Entity ${entityId} does not belong to user ${userId}`
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const entityType = entity.moduleName;
|
|
49
|
+
const moduleDefinition = this.moduleDefinitions.find((def) => {
|
|
50
|
+
const modelName =
|
|
51
|
+
Module.getEntityModelFromDefinition(def).modelName;
|
|
52
|
+
return entityType === modelName;
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
if (!moduleDefinition) {
|
|
56
|
+
throw new Error(
|
|
57
|
+
`Module definition not found for entity type: ${entityType}`
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const module = new Module({
|
|
62
|
+
userId,
|
|
63
|
+
entity,
|
|
64
|
+
definition: moduleDefinition,
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
await module.refreshEntityOptions(options);
|
|
68
|
+
return module.getEntityOptions();
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
module.exports = { RefreshEntityOptions };
|