@friggframework/core 2.0.0-next.10 → 2.0.0-next.101
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +702 -0
- package/README.md +959 -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 +166 -0
- package/admin-scripts/repositories/admin-script-execution-repository-mongo.js +228 -0
- package/admin-scripts/repositories/admin-script-execution-repository-postgres.js +268 -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 +358 -0
- package/application/commands/credential-commands.js +245 -0
- package/application/commands/entity-commands.js +336 -0
- package/application/commands/integration-commands.js +343 -0
- package/application/commands/process-commands.js +135 -0
- package/application/commands/scheduler-commands.js +263 -0
- package/application/commands/user-commands.js +312 -0
- package/application/index.js +82 -0
- package/assertions/index.js +0 -3
- package/core/CLAUDE.md +690 -0
- package/core/Worker.js +60 -24
- package/core/create-handler.js +84 -6
- package/credential/repositories/credential-repository-documentdb.js +304 -0
- package/credential/repositories/credential-repository-factory.js +54 -0
- package/credential/repositories/credential-repository-interface.js +98 -0
- package/credential/repositories/credential-repository-mongo.js +269 -0
- package/credential/repositories/credential-repository-postgres.js +287 -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 +154 -0
- package/database/documentdb-encryption-service.js +330 -0
- package/database/documentdb-utils.js +136 -0
- package/database/encryption/README.md +839 -0
- package/database/encryption/documentdb-encryption-service.md +3575 -0
- package/database/encryption/encryption-schema-registry.js +401 -0
- package/database/encryption/field-encryption-service.js +254 -0
- package/database/encryption/logger.js +79 -0
- package/database/encryption/prisma-encryption-extension.js +230 -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/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 +477 -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 +15 -7
- package/errors/index.js +2 -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 +374 -0
- package/generated/prisma-mongodb/index-browser.js +356 -0
- package/generated/prisma-mongodb/index.d.ts +25667 -0
- package/generated/prisma-mongodb/index.js +399 -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 +432 -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 +381 -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 +395 -0
- package/generated/prisma-postgresql/index-browser.js +377 -0
- package/generated/prisma-postgresql/index.d.ts +28455 -0
- package/generated/prisma-postgresql/index.js +420 -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 +414 -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 +402 -0
- package/handlers/WEBHOOKS.md +653 -0
- package/handlers/app-definition-loader.js +43 -0
- package/handlers/app-handler-helpers.js +3 -3
- package/handlers/backend-utils.js +271 -42
- package/handlers/database-migration-handler.js +227 -0
- package/handlers/integration-event-dispatcher.js +54 -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 +311 -0
- package/handlers/routers/health.js +518 -0
- package/handlers/routers/integration-defined-routers.js +85 -10
- package/handlers/routers/integration-webhook-routers.js +67 -0
- package/handlers/routers/reporting.js +9 -0
- package/handlers/routers/user.js +27 -5
- package/handlers/routers/websocket.js +5 -3
- 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 +352 -0
- package/handlers/workers/dlq-processor.js +63 -0
- package/handlers/workers/integration-defined-workers.js +13 -7
- package/index.js +91 -46
- 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 +633 -72
- package/integrations/integration-router.js +398 -180
- package/integrations/options.js +1 -1
- package/integrations/repositories/config-patch-shared.js +43 -0
- package/integrations/repositories/integration-mapping-repository-documentdb.js +280 -0
- package/integrations/repositories/integration-mapping-repository-factory.js +57 -0
- package/integrations/repositories/integration-mapping-repository-interface.js +106 -0
- package/integrations/repositories/integration-mapping-repository-mongo.js +161 -0
- package/integrations/repositories/integration-mapping-repository-postgres.js +227 -0
- package/integrations/repositories/integration-mapping-repository.js +156 -0
- package/integrations/repositories/integration-repository-documentdb.js +284 -0
- package/integrations/repositories/integration-repository-factory.js +51 -0
- package/integrations/repositories/integration-repository-interface.js +173 -0
- package/integrations/repositories/integration-repository-mongo.js +414 -0
- package/integrations/repositories/integration-repository-postgres.js +452 -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/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/create-integration.js +215 -0
- package/integrations/use-cases/create-process.js +130 -0
- package/integrations/use-cases/delete-integration-for-user.js +119 -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/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 +216 -0
- package/integrations/use-cases/update-process-state.js +163 -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/logs/logger.js +0 -4
- package/{module-plugin → modules}/index.js +0 -10
- package/modules/module-factory.js +56 -0
- package/modules/module.js +287 -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 +396 -0
- package/modules/requester/requester.js +334 -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 +243 -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 +18 -0
- package/package.json +82 -50
- package/prisma-mongodb/schema.prisma +432 -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/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 +414 -0
- package/queues/queuer-util.js +103 -21
- package/reporting/README.md +86 -0
- package/reporting/index.js +17 -0
- package/reporting/reporting-router.js +77 -0
- package/reporting/repositories/reporting-repository-documentdb.js +127 -0
- package/reporting/repositories/reporting-repository-factory.js +35 -0
- package/reporting/repositories/reporting-repository-interface.js +16 -0
- package/reporting/repositories/reporting-repository-mongo.js +54 -0
- package/reporting/repositories/reporting-repository-postgres.js +70 -0
- package/reporting/use-cases/index.js +6 -0
- package/reporting/use-cases/list-integrations-report.js +142 -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/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/integrations/index.d.ts +3 -8
- package/types/module-plugin/index.d.ts +17 -69
- package/types/syncs/index.d.ts +0 -17
- package/user/repositories/user-repository-documentdb.js +458 -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 +127 -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 +77 -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,334 @@
|
|
|
1
|
+
const fetch = require('node-fetch');
|
|
2
|
+
const { Delegate } = require('../../core');
|
|
3
|
+
const { FetchError } = require('../../errors');
|
|
4
|
+
const { get } = require('../../assertions');
|
|
5
|
+
|
|
6
|
+
const DEFAULT_REQUEST_TIMEOUT_MS = 60_000;
|
|
7
|
+
const MAX_AUTH_RETRIES = 3;
|
|
8
|
+
|
|
9
|
+
class Requester extends Delegate {
|
|
10
|
+
constructor(params) {
|
|
11
|
+
super(params);
|
|
12
|
+
this.backOff = get(params, 'backOff', [1, 3, 10, 30, 60, 180]);
|
|
13
|
+
this.isRefreshable = false;
|
|
14
|
+
this.refreshCount = 0;
|
|
15
|
+
this.authGraceRetryCount = 0;
|
|
16
|
+
this.DLGT_INVALID_AUTH = 'INVALID_AUTH';
|
|
17
|
+
this.delegateTypes.push(this.DLGT_INVALID_AUTH);
|
|
18
|
+
this.agent = get(params, 'agent', null);
|
|
19
|
+
|
|
20
|
+
// Per-attempt HTTP timeout. Without this the framework called fetch()
|
|
21
|
+
// with no AbortController and no timeout — a silently-hung TCP
|
|
22
|
+
// connection (server accepts but never responds) blocked the calling
|
|
23
|
+
// promise forever, cascading into stalled batches, stalled syncs,
|
|
24
|
+
// and worker-lambda timeouts.
|
|
25
|
+
//
|
|
26
|
+
// Configuration precedence:
|
|
27
|
+
// 1. Instance param: new Requester({ requestTimeoutMs: 30_000 })
|
|
28
|
+
// 2. Class static: static requestTimeoutMs = 30_000
|
|
29
|
+
// 3. Default: DEFAULT_REQUEST_TIMEOUT_MS (60s)
|
|
30
|
+
//
|
|
31
|
+
// Pass 0 (or null) to disable the timeout entirely — reserved for
|
|
32
|
+
// test doubles and documented long-running endpoints.
|
|
33
|
+
// Intentionally NOT using `get(params, ...)` here — the Frigg
|
|
34
|
+
// `get` helper throws RequiredPropertyError if the key is missing
|
|
35
|
+
// and no default is provided, which would collide with the fall-
|
|
36
|
+
// through to the class-level static override.
|
|
37
|
+
const instanceTimeout = params?.requestTimeoutMs;
|
|
38
|
+
this.requestTimeoutMs =
|
|
39
|
+
instanceTimeout !== undefined && instanceTimeout !== null
|
|
40
|
+
? instanceTimeout
|
|
41
|
+
: this.constructor.requestTimeoutMs ??
|
|
42
|
+
DEFAULT_REQUEST_TIMEOUT_MS;
|
|
43
|
+
|
|
44
|
+
// Allow passing in the fetch function
|
|
45
|
+
// Instance methods can use this.fetch without differentiating
|
|
46
|
+
this.fetch = get(params, 'fetch', fetch);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
parsedBody = async (resp) => {
|
|
50
|
+
const contentType = resp.headers.get('Content-Type') || '';
|
|
51
|
+
|
|
52
|
+
if (
|
|
53
|
+
contentType.match(/^application\/json/) ||
|
|
54
|
+
contentType.match(/^application\/vnd.api\+json/) ||
|
|
55
|
+
contentType.match(/^application\/hal\+json/)
|
|
56
|
+
) {
|
|
57
|
+
return resp.json();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return resp.text();
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* @param {string} url - The request URL, relative or absolute.
|
|
65
|
+
* @param {Object} options - Fetch options (method, headers, body, query,
|
|
66
|
+
* returnFullRes, etc.) built by the `_get`/`_post`/`_patch`/`_put`/
|
|
67
|
+
* `_delete` wrappers.
|
|
68
|
+
* @param {number} attempt - 0-based count of retries already made for
|
|
69
|
+
* this call. Indexes `this.backOff` for the next delay and is passed
|
|
70
|
+
* back in on each recursive retry.
|
|
71
|
+
*/
|
|
72
|
+
async _request(url, options, attempt = 0) {
|
|
73
|
+
let encodedUrl = encodeURI(url);
|
|
74
|
+
if (options.query) {
|
|
75
|
+
let queryBuild = '?';
|
|
76
|
+
for (const key in options.query) {
|
|
77
|
+
queryBuild += `${encodeURIComponent(key)}=${encodeURIComponent(
|
|
78
|
+
options.query[key]
|
|
79
|
+
)}&`;
|
|
80
|
+
}
|
|
81
|
+
encodedUrl += queryBuild.slice(0, -1);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
options.headers = await this.addAuthHeaders(options.headers);
|
|
85
|
+
|
|
86
|
+
if (this.agent) options.agent = this.agent;
|
|
87
|
+
|
|
88
|
+
// Per-attempt timeout — fresh AbortController per call so the retry
|
|
89
|
+
// recursion (with its own backoff sleeps) always gets a clean
|
|
90
|
+
// signal. Timer is cleared in the finally block regardless of
|
|
91
|
+
// outcome.
|
|
92
|
+
const timeoutMs = this.requestTimeoutMs;
|
|
93
|
+
const controller = timeoutMs > 0 ? new AbortController() : null;
|
|
94
|
+
const timeoutHandle = controller
|
|
95
|
+
? setTimeout(() => controller.abort(), timeoutMs)
|
|
96
|
+
: null;
|
|
97
|
+
const fetchOptions = controller
|
|
98
|
+
? { ...options, signal: controller.signal }
|
|
99
|
+
: options;
|
|
100
|
+
|
|
101
|
+
// Timer must stay active through body consumption. node-fetch v2
|
|
102
|
+
// resolves the fetch() promise when headers arrive, not when the
|
|
103
|
+
// body is fully read — so a server that sends headers and then
|
|
104
|
+
// stalls the body would still hang parsedBody() or
|
|
105
|
+
// FetchError.create()'s response.text() call. We clear the timer
|
|
106
|
+
// only after the body is fully consumed (success path) or
|
|
107
|
+
// deliberately before each recursive retry so the new attempt
|
|
108
|
+
// starts with its own fresh timer.
|
|
109
|
+
let timerCleared = false;
|
|
110
|
+
const clearRequestTimer = () => {
|
|
111
|
+
if (!timerCleared && timeoutHandle) {
|
|
112
|
+
clearTimeout(timeoutHandle);
|
|
113
|
+
timerCleared = true;
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
try {
|
|
118
|
+
let response;
|
|
119
|
+
try {
|
|
120
|
+
response = await this.fetch(encodedUrl, fetchOptions);
|
|
121
|
+
} catch (e) {
|
|
122
|
+
// AbortController fires AbortError (name) / ETIMEDOUT-shaped
|
|
123
|
+
// errors (type on node-fetch) when we hit the timeout. No
|
|
124
|
+
// retry on timeout: a slow endpoint is a downstream problem,
|
|
125
|
+
// and each retry would wait another `timeoutMs` before giving
|
|
126
|
+
// up — amplifying the hang into a per-record multi-minute
|
|
127
|
+
// stall at batch scale.
|
|
128
|
+
const isTimeout =
|
|
129
|
+
e?.name === 'AbortError' || e?.type === 'aborted';
|
|
130
|
+
if (e?.code === 'ECONNRESET' && attempt < this.backOff.length) {
|
|
131
|
+
clearRequestTimer();
|
|
132
|
+
const delay = this.backOff[attempt] * 1000;
|
|
133
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
134
|
+
return this._request(url, options, attempt + 1);
|
|
135
|
+
}
|
|
136
|
+
const fetchError = await FetchError.create({
|
|
137
|
+
resource: encodedUrl,
|
|
138
|
+
init: options,
|
|
139
|
+
responseBody: isTimeout
|
|
140
|
+
? `Request timed out after ${timeoutMs}ms`
|
|
141
|
+
: e,
|
|
142
|
+
});
|
|
143
|
+
if (isTimeout) {
|
|
144
|
+
// Flag + machine-readable fields so callers can
|
|
145
|
+
// distinguish a timeout from a generic network error
|
|
146
|
+
// without parsing the message (which FetchError
|
|
147
|
+
// sanitizes outside of STAGE=dev).
|
|
148
|
+
fetchError.isTimeout = true;
|
|
149
|
+
fetchError.timeoutMs = timeoutMs;
|
|
150
|
+
}
|
|
151
|
+
throw fetchError;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const { status } = response;
|
|
155
|
+
|
|
156
|
+
// If the status is retriable and there are back off requests left, retry the request
|
|
157
|
+
if (
|
|
158
|
+
(status === 429 || status >= 500) &&
|
|
159
|
+
attempt < this.backOff.length
|
|
160
|
+
) {
|
|
161
|
+
clearRequestTimer();
|
|
162
|
+
const delay = this.backOff[attempt] * 1000;
|
|
163
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
164
|
+
return this._request(url, options, attempt + 1);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (status === 401) {
|
|
168
|
+
if (!this.isRefreshable) {
|
|
169
|
+
// Up to MAX_AUTH_RETRIES grace retries before invalidating
|
|
170
|
+
// — a 401 alone isn't proof the credential is bad.
|
|
171
|
+
if (
|
|
172
|
+
this.authGraceRetryCount < MAX_AUTH_RETRIES &&
|
|
173
|
+
this.authGraceRetryCount < this.backOff.length
|
|
174
|
+
) {
|
|
175
|
+
const delay =
|
|
176
|
+
this.backOff[this.authGraceRetryCount] * 1000;
|
|
177
|
+
this.authGraceRetryCount++;
|
|
178
|
+
clearRequestTimer();
|
|
179
|
+
await new Promise((resolve) =>
|
|
180
|
+
setTimeout(resolve, delay)
|
|
181
|
+
);
|
|
182
|
+
return this._request(url, options, attempt + 1);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
throw await this._invalidateAuth(
|
|
186
|
+
encodedUrl,
|
|
187
|
+
options,
|
|
188
|
+
response
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (this.refreshCount < MAX_AUTH_RETRIES) {
|
|
193
|
+
this.refreshCount++;
|
|
194
|
+
const refreshSucceeded = await this.refreshAuth();
|
|
195
|
+
if (refreshSucceeded) {
|
|
196
|
+
clearRequestTimer();
|
|
197
|
+
return this._request(url, options, attempt + 1);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
throw await this._invalidateAuth(
|
|
201
|
+
encodedUrl,
|
|
202
|
+
options,
|
|
203
|
+
response
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
throw await this._invalidateAuth(encodedUrl, options, response);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// If the error wasn't retried, throw. FetchError.create reads
|
|
211
|
+
// the response body (response.text()) — timer must still be
|
|
212
|
+
// alive to catch a stalled body stream.
|
|
213
|
+
if (status >= 400) {
|
|
214
|
+
const fetchError = await FetchError.create({
|
|
215
|
+
resource: encodedUrl,
|
|
216
|
+
init: options,
|
|
217
|
+
response,
|
|
218
|
+
});
|
|
219
|
+
throw this._maybeFlagTimeoutDuringBodyRead(
|
|
220
|
+
fetchError,
|
|
221
|
+
timeoutMs
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Successful response: reset the per-instance refresh budget so
|
|
226
|
+
// a later 401 in the same Requester lifetime can attempt refresh
|
|
227
|
+
// again instead of silently falling through.
|
|
228
|
+
this.refreshCount = 0;
|
|
229
|
+
this.authGraceRetryCount = 0;
|
|
230
|
+
|
|
231
|
+
// parsedBody consumes the response body stream. If the server
|
|
232
|
+
// stalls mid-stream the timer (still armed) aborts it.
|
|
233
|
+
return options.returnFullRes
|
|
234
|
+
? response
|
|
235
|
+
: await this.parsedBody(response);
|
|
236
|
+
} catch (e) {
|
|
237
|
+
// If the abort fired during body consumption, node-fetch emits
|
|
238
|
+
// the error as an AbortError on the body stream. Surface the
|
|
239
|
+
// same isTimeout flag callers use for header-phase timeouts.
|
|
240
|
+
throw this._maybeFlagTimeoutDuringBodyRead(e, timeoutMs);
|
|
241
|
+
} finally {
|
|
242
|
+
clearRequestTimer();
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
async _invalidateAuth(encodedUrl, options, response) {
|
|
247
|
+
const fetchError = await FetchError.create({
|
|
248
|
+
resource: encodedUrl,
|
|
249
|
+
init: options,
|
|
250
|
+
response,
|
|
251
|
+
});
|
|
252
|
+
await this.notify(this.DLGT_INVALID_AUTH, fetchError);
|
|
253
|
+
return fetchError;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
_maybeFlagTimeoutDuringBodyRead(err, timeoutMs) {
|
|
257
|
+
if (!err || typeof err !== 'object') return err;
|
|
258
|
+
if (err.isTimeout) return err;
|
|
259
|
+
const isAbort = err.name === 'AbortError' || err.type === 'aborted';
|
|
260
|
+
if (!isAbort) return err;
|
|
261
|
+
err.isTimeout = true;
|
|
262
|
+
err.timeoutMs = timeoutMs;
|
|
263
|
+
return err;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
async _get(options) {
|
|
267
|
+
const fetchOptions = {
|
|
268
|
+
method: 'GET',
|
|
269
|
+
credentials: 'include',
|
|
270
|
+
headers: options.headers || {},
|
|
271
|
+
query: options.query || {},
|
|
272
|
+
returnFullRes: options.returnFullRes || false,
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
const res = await this._request(options.url, fetchOptions);
|
|
276
|
+
return res;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
async _post(options, stringify = true) {
|
|
280
|
+
const fetchOptions = {
|
|
281
|
+
method: 'POST',
|
|
282
|
+
credentials: 'include',
|
|
283
|
+
headers: options.headers || {},
|
|
284
|
+
query: options.query || {},
|
|
285
|
+
body: stringify ? JSON.stringify(options.body) : options.body,
|
|
286
|
+
returnFullRes: options.returnFullRes || false,
|
|
287
|
+
};
|
|
288
|
+
const res = await this._request(options.url, fetchOptions);
|
|
289
|
+
return res;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
async _patch(options, stringify = true) {
|
|
293
|
+
const fetchOptions = {
|
|
294
|
+
method: 'PATCH',
|
|
295
|
+
credentials: 'include',
|
|
296
|
+
headers: options.headers || {},
|
|
297
|
+
query: options.query || {},
|
|
298
|
+
body: stringify ? JSON.stringify(options.body) : options.body,
|
|
299
|
+
returnFullRes: options.returnFullRes || false,
|
|
300
|
+
};
|
|
301
|
+
const res = await this._request(options.url, fetchOptions);
|
|
302
|
+
return res;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
async _put(options, stringify = true) {
|
|
306
|
+
const fetchOptions = {
|
|
307
|
+
method: 'PUT',
|
|
308
|
+
credentials: 'include',
|
|
309
|
+
headers: options.headers || {},
|
|
310
|
+
query: options.query || {},
|
|
311
|
+
body: stringify ? JSON.stringify(options.body) : options.body,
|
|
312
|
+
returnFullRes: options.returnFullRes || false,
|
|
313
|
+
};
|
|
314
|
+
const res = await this._request(options.url, fetchOptions);
|
|
315
|
+
return res;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
async _delete(options) {
|
|
319
|
+
const fetchOptions = {
|
|
320
|
+
method: 'DELETE',
|
|
321
|
+
credentials: 'include',
|
|
322
|
+
headers: options.headers || {},
|
|
323
|
+
query: options.query || {},
|
|
324
|
+
returnFullRes: options.returnFullRes || true,
|
|
325
|
+
};
|
|
326
|
+
return this._request(options.url, fetchOptions);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
async refreshAuth() {
|
|
330
|
+
throw new Error('refreshAuth not yet defined in child of Requester');
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
module.exports = { Requester };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
const { get } = require('
|
|
2
|
-
const { OAuth2Requester } = require('
|
|
1
|
+
const { get } = require('../../../assertions');
|
|
2
|
+
const { OAuth2Requester } = require('../..');
|
|
3
3
|
|
|
4
4
|
class Api extends OAuth2Requester {
|
|
5
5
|
constructor(params) {
|
|
@@ -23,7 +23,12 @@ class Api extends OAuth2Requester {
|
|
|
23
23
|
return this.authorizationUri;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
getAuthorizationRequirements() {
|
|
27
|
+
return {
|
|
28
|
+
url: this.getAuthUri(),
|
|
29
|
+
type: 'oauth2',
|
|
30
|
+
};
|
|
31
|
+
}
|
|
27
32
|
}
|
|
28
33
|
|
|
29
34
|
module.exports = { Api };
|
|
@@ -1,22 +1,26 @@
|
|
|
1
1
|
require('dotenv').config();
|
|
2
|
-
const {Api} = require('./api');
|
|
3
|
-
const {get} = require('
|
|
4
|
-
const config = {name: 'anapi'}
|
|
2
|
+
const { Api } = require('./api');
|
|
3
|
+
const { get } = require('../../../assertions');
|
|
4
|
+
const config = { name: 'anapi' }
|
|
5
5
|
|
|
6
6
|
const Definition = {
|
|
7
7
|
API: Api,
|
|
8
|
-
|
|
8
|
+
getAuthorizationRequirements: () => ({
|
|
9
|
+
url: 'http://localhost:3000/redirect/anapi',
|
|
10
|
+
type: 'oauth2',
|
|
11
|
+
}),
|
|
12
|
+
getName: function () { return config.name },
|
|
9
13
|
moduleName: config.name,
|
|
10
14
|
modelName: 'AnApi',
|
|
11
15
|
requiredAuthMethods: {
|
|
12
|
-
getToken: async function(api, params){
|
|
16
|
+
getToken: async function (api, params) {
|
|
13
17
|
const code = get(params.data, 'code');
|
|
14
18
|
return api.getTokenFromCode(code);
|
|
15
19
|
},
|
|
16
|
-
getEntityDetails: async function(api, callbackParams, tokenResponse, userId) {
|
|
20
|
+
getEntityDetails: async function (api, callbackParams, tokenResponse, userId) {
|
|
17
21
|
const userDetails = await api.getUserDetails();
|
|
18
22
|
return {
|
|
19
|
-
identifiers: { externalId: userDetails.portalId,
|
|
23
|
+
identifiers: { externalId: userDetails.portalId, userId },
|
|
20
24
|
details: { name: userDetails.hub_domain },
|
|
21
25
|
}
|
|
22
26
|
},
|
|
@@ -26,14 +30,14 @@ const Definition = {
|
|
|
26
30
|
],
|
|
27
31
|
entity: [],
|
|
28
32
|
},
|
|
29
|
-
getCredentialDetails: async function(api, userId) {
|
|
33
|
+
getCredentialDetails: async function (api, userId) {
|
|
30
34
|
const userDetails = await api.getUserDetails();
|
|
31
35
|
return {
|
|
32
|
-
identifiers: { externalId: userDetails.portalId,
|
|
36
|
+
identifiers: { externalId: userDetails.portalId, userId },
|
|
33
37
|
details: {}
|
|
34
38
|
};
|
|
35
39
|
},
|
|
36
|
-
testAuthRequest: async function(api){
|
|
40
|
+
testAuthRequest: async function (api) {
|
|
37
41
|
return api.getUserDetails()
|
|
38
42
|
},
|
|
39
43
|
},
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
class TestModuleFactory {
|
|
2
|
+
constructor() { }
|
|
3
|
+
|
|
4
|
+
async getModuleInstance(entityId, userId) {
|
|
5
|
+
// return minimal stub module with getName and api property
|
|
6
|
+
return {
|
|
7
|
+
getName() { return 'stubModule'; },
|
|
8
|
+
api: {},
|
|
9
|
+
entityId,
|
|
10
|
+
userId,
|
|
11
|
+
testAuth: async () => true,
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
module.exports = { TestModuleFactory };
|
|
@@ -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 };
|