@friggframework/core 2.0.0-next.11 → 2.0.0-next.110
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 +999 -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 +336 -0
- package/application/commands/integration-commands.js +356 -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 +700 -0
- package/core/Worker.js +60 -24
- package/core/create-handler.js +189 -5
- package/core/parameters-to-env.js +257 -0
- 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 +154 -0
- package/database/documentdb-encryption-service.js +330 -0
- package/database/documentdb-utils.js +192 -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/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 +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 +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 +653 -0
- package/handlers/app-definition-loader.js +67 -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 +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 +80 -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 +427 -0
- package/handlers/workers/dlq-processor.js +63 -0
- package/handlers/workers/integration-defined-workers.js +13 -7
- package/index.js +102 -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 +730 -74
- 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 +303 -0
- package/integrations/repositories/integration-mapping-repository-factory.js +57 -0
- package/integrations/repositories/integration-mapping-repository-interface.js +120 -0
- package/integrations/repositories/integration-mapping-repository-mongo.js +183 -0
- package/integrations/repositories/integration-mapping-repository-postgres.js +255 -0
- package/integrations/repositories/integration-mapping-repository.js +156 -0
- package/integrations/repositories/integration-repository-documentdb.js +322 -0
- package/integrations/repositories/integration-repository-factory.js +51 -0
- package/integrations/repositories/integration-repository-interface.js +189 -0
- package/integrations/repositories/integration-repository-mongo.js +450 -0
- package/integrations/repositories/integration-repository-postgres.js +492 -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/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 +222 -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 +307 -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 +555 -0
- package/modules/requester/requester.js +542 -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 +92 -50
- 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/queuer-util.js +103 -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 +73 -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 +87 -0
- package/telemetry/no-op-telemetry.js +67 -0
- package/telemetry/north-star.js +103 -0
- package/telemetry/otel-telemetry.js +213 -0
- package/telemetry/plugin-subscribers.js +77 -0
- package/telemetry/telemetry-config.js +120 -0
- package/telemetry/telemetry-context.js +40 -0
- package/telemetry/telemetry-event-bus.js +58 -0
- package/telemetry/telemetry-runtime.js +147 -0
- package/telemetry/telemetry-service.js +51 -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/integrations/index.d.ts +3 -8
- 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 +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,25 @@
|
|
|
1
|
+
const {
|
|
2
|
+
createIntegrationMappingRepository,
|
|
3
|
+
} = require('../../integrations/repositories/integration-mapping-repository-factory');
|
|
4
|
+
|
|
5
|
+
function mapErrorToResponse(error) {
|
|
6
|
+
return { error: 500, reason: error?.message, code: error?.code };
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
// Kept separate from integration-commands so the mapping and integration domains stay decoupled.
|
|
10
|
+
function createIntegrationMappingCommands() {
|
|
11
|
+
const mappingRepository = createIntegrationMappingRepository();
|
|
12
|
+
|
|
13
|
+
return {
|
|
14
|
+
// Returns a Map of integrationId → count, or an error object on failure.
|
|
15
|
+
async countByIntegrationIds(ids = []) {
|
|
16
|
+
try {
|
|
17
|
+
return await mappingRepository.countByIntegrationIds(ids);
|
|
18
|
+
} catch (error) {
|
|
19
|
+
return mapErrorToResponse(error);
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
module.exports = { createIntegrationMappingCommands };
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Process Commands
|
|
3
|
+
*
|
|
4
|
+
* Application Layer - Command factory for long-running process tracking.
|
|
5
|
+
*
|
|
6
|
+
* Wraps the Process use cases (create, get, update state, update metrics)
|
|
7
|
+
* behind the same `createXCommands()` surface used by the other domains so
|
|
8
|
+
* integration developers can track processes without touching repositories
|
|
9
|
+
* or the underlying ORM directly.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* const processCommands = createProcessCommands();
|
|
13
|
+
* const process = await processCommands.createProcess({
|
|
14
|
+
* userId: 'user-1',
|
|
15
|
+
* integrationId: 'integration-1',
|
|
16
|
+
* name: 'zoho-crm-contact-sync',
|
|
17
|
+
* type: 'CRM_SYNC',
|
|
18
|
+
* });
|
|
19
|
+
* await processCommands.updateProcessState(process.id, 'FETCHING_TOTAL');
|
|
20
|
+
* await processCommands.updateProcessMetrics(process.id, { processed: 100, success: 100 });
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
const {
|
|
24
|
+
createProcessRepository,
|
|
25
|
+
} = require('../../integrations/repositories/process-repository-factory');
|
|
26
|
+
const { CreateProcess } = require('../../integrations/use-cases/create-process');
|
|
27
|
+
const { GetProcess } = require('../../integrations/use-cases/get-process');
|
|
28
|
+
const {
|
|
29
|
+
UpdateProcessState,
|
|
30
|
+
} = require('../../integrations/use-cases/update-process-state');
|
|
31
|
+
const {
|
|
32
|
+
UpdateProcessMetrics,
|
|
33
|
+
} = require('../../integrations/use-cases/update-process-metrics');
|
|
34
|
+
|
|
35
|
+
const ERROR_CODE_MAP = {
|
|
36
|
+
PROCESS_NOT_FOUND: 404,
|
|
37
|
+
INVALID_PROCESS_DATA: 400,
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
function mapErrorToResponse(error) {
|
|
41
|
+
const status = ERROR_CODE_MAP[error?.code] || 500;
|
|
42
|
+
return {
|
|
43
|
+
error: status,
|
|
44
|
+
reason: error?.message,
|
|
45
|
+
code: error?.code,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Create process tracking commands.
|
|
51
|
+
*
|
|
52
|
+
* @param {Object} [params]
|
|
53
|
+
* @param {Object} [params.websocketService] - Optional WebSocket service
|
|
54
|
+
* forwarded to UpdateProcessMetrics for progress broadcasting.
|
|
55
|
+
* @returns {Object} Process commands object
|
|
56
|
+
*/
|
|
57
|
+
function createProcessCommands({ websocketService } = {}) {
|
|
58
|
+
const processRepository = createProcessRepository();
|
|
59
|
+
|
|
60
|
+
const createProcessUseCase = new CreateProcess({ processRepository });
|
|
61
|
+
const getProcessUseCase = new GetProcess({ processRepository });
|
|
62
|
+
const updateProcessStateUseCase = new UpdateProcessState({
|
|
63
|
+
processRepository,
|
|
64
|
+
});
|
|
65
|
+
const updateProcessMetricsUseCase = new UpdateProcessMetrics({
|
|
66
|
+
processRepository,
|
|
67
|
+
websocketService,
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
/**
|
|
72
|
+
* Create a new process record.
|
|
73
|
+
* @param {Object} processData - Process data (userId, integrationId, name, type, ...)
|
|
74
|
+
* @returns {Promise<Object>} Created process record
|
|
75
|
+
*/
|
|
76
|
+
async createProcess(processData) {
|
|
77
|
+
try {
|
|
78
|
+
return await createProcessUseCase.execute(processData);
|
|
79
|
+
} catch (error) {
|
|
80
|
+
return mapErrorToResponse(error);
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Retrieve a process by ID.
|
|
86
|
+
* @param {string} processId - Process ID
|
|
87
|
+
* @returns {Promise<Object|null>} Process record, or null if not found
|
|
88
|
+
*/
|
|
89
|
+
async getProcess(processId) {
|
|
90
|
+
try {
|
|
91
|
+
return await getProcessUseCase.execute(processId);
|
|
92
|
+
} catch (error) {
|
|
93
|
+
return mapErrorToResponse(error);
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Transition a process to a new state, merging optional context updates.
|
|
99
|
+
* @param {string} processId - Process ID
|
|
100
|
+
* @param {string} newState - New state value
|
|
101
|
+
* @param {Object} [contextUpdates={}] - Context fields to merge
|
|
102
|
+
* @returns {Promise<Object>} Updated process record
|
|
103
|
+
*/
|
|
104
|
+
async updateProcessState(processId, newState, contextUpdates = {}) {
|
|
105
|
+
try {
|
|
106
|
+
return await updateProcessStateUseCase.execute(
|
|
107
|
+
processId,
|
|
108
|
+
newState,
|
|
109
|
+
contextUpdates
|
|
110
|
+
);
|
|
111
|
+
} catch (error) {
|
|
112
|
+
return mapErrorToResponse(error);
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Apply a metrics update (counters + bounded error history) to a process.
|
|
118
|
+
* @param {string} processId - Process ID
|
|
119
|
+
* @param {Object} metricsUpdate - Metrics to add (processed, success, errors, skipped, errorDetails)
|
|
120
|
+
* @returns {Promise<Object>} Updated process record
|
|
121
|
+
*/
|
|
122
|
+
async updateProcessMetrics(processId, metricsUpdate) {
|
|
123
|
+
try {
|
|
124
|
+
return await updateProcessMetricsUseCase.execute(
|
|
125
|
+
processId,
|
|
126
|
+
metricsUpdate
|
|
127
|
+
);
|
|
128
|
+
} catch (error) {
|
|
129
|
+
return mapErrorToResponse(error);
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
module.exports = { createProcessCommands };
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
const ERROR_CODE_MAP = {
|
|
2
|
+
EXECUTION_NOT_FOUND: 404,
|
|
3
|
+
};
|
|
4
|
+
|
|
5
|
+
function mapErrorToResponse(error) {
|
|
6
|
+
const status = ERROR_CODE_MAP[error?.code] || 500;
|
|
7
|
+
return { error: status, reason: error?.message, code: error?.code };
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Report commands share the AdminScriptExecution store, discriminated by
|
|
12
|
+
* type:'REPORT'. That store has no user/integration FK, and findExecutionById
|
|
13
|
+
* rejects non-REPORT rows, so a report lookup can never return another
|
|
14
|
+
* operation type's record (ADR-010 Decision 3).
|
|
15
|
+
*/
|
|
16
|
+
function createReportCommands({ artifactRepository } = {}) {
|
|
17
|
+
const {
|
|
18
|
+
createAdminScriptExecutionRepository,
|
|
19
|
+
} = require('../../admin-scripts/repositories/admin-script-execution-repository-factory');
|
|
20
|
+
|
|
21
|
+
const executionRepository = createAdminScriptExecutionRepository();
|
|
22
|
+
|
|
23
|
+
let artifactRepo = artifactRepository || null;
|
|
24
|
+
function getArtifactRepository() {
|
|
25
|
+
if (!artifactRepo) {
|
|
26
|
+
const {
|
|
27
|
+
createArtifactRepository,
|
|
28
|
+
} = require('../../artifacts/repositories/artifact-repository-factory');
|
|
29
|
+
artifactRepo = createArtifactRepository();
|
|
30
|
+
}
|
|
31
|
+
return artifactRepo;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Resolve to null rather than throw: a read must not fail because signing did.
|
|
35
|
+
async function signArtifact(ref) {
|
|
36
|
+
if (!ref) return null;
|
|
37
|
+
try {
|
|
38
|
+
return await getArtifactRepository().signedUrl(ref);
|
|
39
|
+
} catch (_error) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
async createExecution({
|
|
46
|
+
reportName,
|
|
47
|
+
reportVersion,
|
|
48
|
+
trigger,
|
|
49
|
+
mode,
|
|
50
|
+
input,
|
|
51
|
+
audit,
|
|
52
|
+
seriesName,
|
|
53
|
+
parentExecutionId,
|
|
54
|
+
}) {
|
|
55
|
+
try {
|
|
56
|
+
return await executionRepository.createExecution({
|
|
57
|
+
name: reportName,
|
|
58
|
+
type: 'REPORT',
|
|
59
|
+
parentExecutionId,
|
|
60
|
+
context: {
|
|
61
|
+
reportVersion,
|
|
62
|
+
trigger,
|
|
63
|
+
mode: mode || 'recorded',
|
|
64
|
+
input,
|
|
65
|
+
audit,
|
|
66
|
+
...(seriesName ? { seriesName } : {}),
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
} catch (error) {
|
|
70
|
+
return mapErrorToResponse(error);
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
async findExecutionById(id) {
|
|
75
|
+
try {
|
|
76
|
+
const record = await executionRepository.findExecutionById(id);
|
|
77
|
+
if (!record || record.type !== 'REPORT') {
|
|
78
|
+
const error = new Error(`Report execution ${id} not found`);
|
|
79
|
+
error.code = 'EXECUTION_NOT_FOUND';
|
|
80
|
+
return mapErrorToResponse(error);
|
|
81
|
+
}
|
|
82
|
+
// The stored artifact ref is not retrievable on its own; sign it on read.
|
|
83
|
+
if (record.results?.artifact) {
|
|
84
|
+
record.results = {
|
|
85
|
+
...record.results,
|
|
86
|
+
artifactUrl: await signArtifact(record.results.artifact),
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
return record;
|
|
90
|
+
} catch (error) {
|
|
91
|
+
return mapErrorToResponse(error);
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
|
|
95
|
+
// Never-throws: returns [] on error (non-critical read).
|
|
96
|
+
async listExecutionsByName(reportName, { limit, offset, state } = {}) {
|
|
97
|
+
try {
|
|
98
|
+
return await executionRepository.findExecutionsByName(
|
|
99
|
+
reportName,
|
|
100
|
+
{ type: 'REPORT', limit, offset, state }
|
|
101
|
+
);
|
|
102
|
+
} catch (error) {
|
|
103
|
+
return [];
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
|
|
107
|
+
// No mode index in the store, so fetch by name/window and filter snapshots
|
|
108
|
+
// in JS. Never-throws: returns [] on error.
|
|
109
|
+
async findSnapshotSeries(reportName, { from, to, limit } = {}) {
|
|
110
|
+
try {
|
|
111
|
+
const rows = await executionRepository.findExecutionsByName(
|
|
112
|
+
reportName,
|
|
113
|
+
{
|
|
114
|
+
type: 'REPORT',
|
|
115
|
+
from,
|
|
116
|
+
to,
|
|
117
|
+
sortBy: 'createdAt',
|
|
118
|
+
sortOrder: 'asc',
|
|
119
|
+
limit,
|
|
120
|
+
}
|
|
121
|
+
);
|
|
122
|
+
const snapshots = rows.filter(
|
|
123
|
+
(row) => row.context?.mode === 'snapshot'
|
|
124
|
+
);
|
|
125
|
+
return Promise.all(
|
|
126
|
+
snapshots.map(async (row) => ({
|
|
127
|
+
executionId: row.id,
|
|
128
|
+
capturedAt: row.createdAt,
|
|
129
|
+
summary:
|
|
130
|
+
row.results?.output?.summary ??
|
|
131
|
+
row.results?.summary ??
|
|
132
|
+
null,
|
|
133
|
+
artifactUrl: await signArtifact(row.results?.artifact),
|
|
134
|
+
}))
|
|
135
|
+
);
|
|
136
|
+
} catch (error) {
|
|
137
|
+
return [];
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
|
|
141
|
+
async updateExecutionState(id, state) {
|
|
142
|
+
try {
|
|
143
|
+
return await executionRepository.updateExecutionState(id, state);
|
|
144
|
+
} catch (error) {
|
|
145
|
+
return mapErrorToResponse(error);
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
|
|
149
|
+
async appendExecutionLog(id, logEntry) {
|
|
150
|
+
try {
|
|
151
|
+
return await executionRepository.appendExecutionLog(id, logEntry);
|
|
152
|
+
} catch (error) {
|
|
153
|
+
return mapErrorToResponse(error);
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
|
|
157
|
+
// results.output is the inline JSON payload; results.summary + results.artifact
|
|
158
|
+
// are a large/binary payload's summary + object-store reference.
|
|
159
|
+
async completeExecution(
|
|
160
|
+
id,
|
|
161
|
+
{ state, output, summary, artifact, error, metrics, logs } = {}
|
|
162
|
+
) {
|
|
163
|
+
try {
|
|
164
|
+
if (state) {
|
|
165
|
+
await executionRepository.updateExecutionState(id, state);
|
|
166
|
+
}
|
|
167
|
+
const resultsUpdate = {};
|
|
168
|
+
if (output !== undefined) resultsUpdate.output = output;
|
|
169
|
+
if (summary !== undefined) resultsUpdate.summary = summary;
|
|
170
|
+
if (artifact !== undefined) resultsUpdate.artifact = artifact;
|
|
171
|
+
if (error) resultsUpdate.error = error;
|
|
172
|
+
if (metrics) resultsUpdate.metrics = metrics;
|
|
173
|
+
if (logs) resultsUpdate.logs = logs;
|
|
174
|
+
if (Object.keys(resultsUpdate).length > 0) {
|
|
175
|
+
await executionRepository.updateExecutionResults(
|
|
176
|
+
id,
|
|
177
|
+
resultsUpdate
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
return { success: true };
|
|
181
|
+
} catch (err) {
|
|
182
|
+
return mapErrorToResponse(err);
|
|
183
|
+
}
|
|
184
|
+
},
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
module.exports = { createReportCommands };
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scheduler Commands
|
|
3
|
+
*
|
|
4
|
+
* Application Layer - Command pattern for scheduling operations.
|
|
5
|
+
*
|
|
6
|
+
* Follows hexagonal architecture:
|
|
7
|
+
* - Receives SchedulerServiceInterface via dependency injection
|
|
8
|
+
* - Contains business logic (validation, logging, error mapping)
|
|
9
|
+
* - Protocol-agnostic (doesn't know about HTTP/Lambda)
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* const schedulerCommands = createSchedulerCommands({ integrationName: 'zoho' });
|
|
13
|
+
* await schedulerCommands.scheduleJob({
|
|
14
|
+
* jobId: 'zoho-notif-renewal-abc123',
|
|
15
|
+
* scheduledAt: new Date(Date.now() + 6 * 24 * 60 * 60 * 1000), // 6 days
|
|
16
|
+
* event: 'REFRESH_WEBHOOK',
|
|
17
|
+
* payload: { integrationId: 'abc123' },
|
|
18
|
+
* queueUrl: process.env.ZOHO_QUEUE_URL,
|
|
19
|
+
* });
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
const { createSchedulerService } = require('../../infrastructure/scheduler');
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Derive SQS ARN from SQS URL
|
|
26
|
+
*
|
|
27
|
+
* SQS URL format: https://sqs.{region}.amazonaws.com/{account-id}/{queue-name}
|
|
28
|
+
* SQS ARN format: arn:aws:sqs:{region}:{account-id}:{queue-name}
|
|
29
|
+
*
|
|
30
|
+
* @param {string} queueUrl - SQS queue URL
|
|
31
|
+
* @returns {string} SQS queue ARN
|
|
32
|
+
*/
|
|
33
|
+
function deriveArnFromQueueUrl(queueUrl) {
|
|
34
|
+
try {
|
|
35
|
+
const url = new URL(queueUrl);
|
|
36
|
+
const region = url.hostname.split('.')[1];
|
|
37
|
+
const pathParts = url.pathname.split('/').filter(Boolean);
|
|
38
|
+
const accountId = pathParts[0];
|
|
39
|
+
const queueName = pathParts[1];
|
|
40
|
+
return `arn:aws:sqs:${region}:${accountId}:${queueName}`;
|
|
41
|
+
} catch (error) {
|
|
42
|
+
throw new Error(`Invalid SQS queue URL: ${queueUrl}`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const ERROR_CODE_MAP = {
|
|
47
|
+
SCHEDULER_NOT_CONFIGURED: 503,
|
|
48
|
+
INVALID_JOB_DATA: 400,
|
|
49
|
+
SCHEDULE_NOT_FOUND: 404,
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
function mapErrorToResponse(error) {
|
|
53
|
+
const status = ERROR_CODE_MAP[error?.code] || 500;
|
|
54
|
+
return {
|
|
55
|
+
error: status,
|
|
56
|
+
reason: error?.message,
|
|
57
|
+
code: error?.code,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Create scheduler commands for an integration
|
|
63
|
+
*
|
|
64
|
+
* @param {Object} params
|
|
65
|
+
* @param {string} params.integrationName - Name of the integration (used for logging)
|
|
66
|
+
* @param {SchedulerServiceInterface} [params.schedulerService] - Optional injected scheduler service
|
|
67
|
+
* @returns {Object} Scheduler commands object
|
|
68
|
+
*/
|
|
69
|
+
function createSchedulerCommands({ integrationName, schedulerService }) {
|
|
70
|
+
if (!integrationName) {
|
|
71
|
+
throw new Error('integrationName is required');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Support both dependency injection and lazy creation
|
|
75
|
+
// DI is preferred for testability, lazy creation for convenience
|
|
76
|
+
let _schedulerService = schedulerService || null;
|
|
77
|
+
|
|
78
|
+
function getSchedulerService() {
|
|
79
|
+
if (!_schedulerService) {
|
|
80
|
+
try {
|
|
81
|
+
_schedulerService = createSchedulerService();
|
|
82
|
+
} catch (error) {
|
|
83
|
+
console.warn(
|
|
84
|
+
`[${integrationName}] Scheduler service not available: ${error.message}`
|
|
85
|
+
);
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return _schedulerService;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return {
|
|
93
|
+
/**
|
|
94
|
+
* Schedule a one-time job to be executed at a specific time
|
|
95
|
+
*
|
|
96
|
+
* @param {Object} params
|
|
97
|
+
* @param {string} params.jobId - Unique identifier for the job
|
|
98
|
+
* @param {Date} params.scheduledAt - When to execute the job
|
|
99
|
+
* @param {string} params.event - Event name to trigger
|
|
100
|
+
* @param {Object} params.payload - Additional payload data
|
|
101
|
+
* @param {string} params.queueUrl - Target SQS queue URL (ARN is derived internally)
|
|
102
|
+
* @returns {Promise<{jobArn: string, scheduledAt: string} | {error: number, reason: string}>}
|
|
103
|
+
*/
|
|
104
|
+
async scheduleJob({ jobId, scheduledAt, event, payload, queueUrl }) {
|
|
105
|
+
try {
|
|
106
|
+
if (!jobId) {
|
|
107
|
+
const error = new Error('jobId is required');
|
|
108
|
+
error.code = 'INVALID_JOB_DATA';
|
|
109
|
+
throw error;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (!scheduledAt || !(scheduledAt instanceof Date)) {
|
|
113
|
+
const error = new Error('scheduledAt must be a valid Date');
|
|
114
|
+
error.code = 'INVALID_JOB_DATA';
|
|
115
|
+
throw error;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (!event) {
|
|
119
|
+
const error = new Error('event is required');
|
|
120
|
+
error.code = 'INVALID_JOB_DATA';
|
|
121
|
+
throw error;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (!queueUrl) {
|
|
125
|
+
const error = new Error('queueUrl is required');
|
|
126
|
+
error.code = 'INVALID_JOB_DATA';
|
|
127
|
+
throw error;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Derive ARN from URL (business logic - transformation)
|
|
131
|
+
const queueArn = deriveArnFromQueueUrl(queueUrl);
|
|
132
|
+
|
|
133
|
+
// Get scheduler service (via DI or factory)
|
|
134
|
+
const service = getSchedulerService();
|
|
135
|
+
if (!service) {
|
|
136
|
+
console.warn(
|
|
137
|
+
`[${integrationName}] Scheduler not configured, skipping job schedule`
|
|
138
|
+
);
|
|
139
|
+
return {
|
|
140
|
+
jobId,
|
|
141
|
+
jobArn: null,
|
|
142
|
+
scheduledAt: null,
|
|
143
|
+
warning: 'Scheduler not configured',
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Build the SQS message payload (business logic - assembly)
|
|
148
|
+
const sqsPayload = {
|
|
149
|
+
event,
|
|
150
|
+
integrationName,
|
|
151
|
+
data: payload || {},
|
|
152
|
+
scheduledAt: scheduledAt.toISOString(),
|
|
153
|
+
createdAt: new Date().toISOString(),
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
// Delegate to service (Port interface)
|
|
157
|
+
const result = await service.scheduleOneTime({
|
|
158
|
+
scheduleName: jobId,
|
|
159
|
+
scheduleAt: scheduledAt,
|
|
160
|
+
queueResourceId: queueArn,
|
|
161
|
+
payload: sqsPayload,
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
console.log(
|
|
165
|
+
`[${integrationName}] Scheduled job ${jobId} for ${result.scheduledAt}`
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
return {
|
|
169
|
+
jobId,
|
|
170
|
+
jobArn: result.scheduledJobId,
|
|
171
|
+
scheduledAt: result.scheduledAt,
|
|
172
|
+
};
|
|
173
|
+
} catch (error) {
|
|
174
|
+
console.error(
|
|
175
|
+
`[${integrationName}] Failed to schedule job ${jobId}:`,
|
|
176
|
+
error.message
|
|
177
|
+
);
|
|
178
|
+
return mapErrorToResponse(error);
|
|
179
|
+
}
|
|
180
|
+
},
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Delete a scheduled job
|
|
184
|
+
*
|
|
185
|
+
* @param {string} jobId - Job ID to delete
|
|
186
|
+
* @returns {Promise<{success: boolean, jobId: string} | {error: number, reason: string}>}
|
|
187
|
+
*/
|
|
188
|
+
async deleteJob(jobId) {
|
|
189
|
+
try {
|
|
190
|
+
if (!jobId) {
|
|
191
|
+
const error = new Error('jobId is required');
|
|
192
|
+
error.code = 'INVALID_JOB_DATA';
|
|
193
|
+
throw error;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const service = getSchedulerService();
|
|
197
|
+
if (!service) {
|
|
198
|
+
console.warn(
|
|
199
|
+
`[${integrationName}] Scheduler not configured, skipping job deletion`
|
|
200
|
+
);
|
|
201
|
+
return {
|
|
202
|
+
success: true,
|
|
203
|
+
jobId,
|
|
204
|
+
warning: 'Scheduler not configured',
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
await service.deleteSchedule(jobId);
|
|
209
|
+
|
|
210
|
+
console.log(`[${integrationName}] Deleted scheduled job ${jobId}`);
|
|
211
|
+
|
|
212
|
+
return {
|
|
213
|
+
success: true,
|
|
214
|
+
jobId,
|
|
215
|
+
};
|
|
216
|
+
} catch (error) {
|
|
217
|
+
console.error(
|
|
218
|
+
`[${integrationName}] Failed to delete job ${jobId}:`,
|
|
219
|
+
error.message
|
|
220
|
+
);
|
|
221
|
+
return mapErrorToResponse(error);
|
|
222
|
+
}
|
|
223
|
+
},
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Get the status of a scheduled job
|
|
227
|
+
*
|
|
228
|
+
* @param {string} jobId - Job ID to check
|
|
229
|
+
* @returns {Promise<{exists: boolean, scheduledAt?: string, state?: string} | {error: number, reason: string}>}
|
|
230
|
+
*/
|
|
231
|
+
async getJobStatus(jobId) {
|
|
232
|
+
try {
|
|
233
|
+
if (!jobId) {
|
|
234
|
+
const error = new Error('jobId is required');
|
|
235
|
+
error.code = 'INVALID_JOB_DATA';
|
|
236
|
+
throw error;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const service = getSchedulerService();
|
|
240
|
+
if (!service) {
|
|
241
|
+
return {
|
|
242
|
+
exists: false,
|
|
243
|
+
warning: 'Scheduler not configured',
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const status = await service.getScheduleStatus(jobId);
|
|
248
|
+
|
|
249
|
+
return status;
|
|
250
|
+
} catch (error) {
|
|
251
|
+
console.error(
|
|
252
|
+
`[${integrationName}] Failed to get job status ${jobId}:`,
|
|
253
|
+
error.message
|
|
254
|
+
);
|
|
255
|
+
return mapErrorToResponse(error);
|
|
256
|
+
}
|
|
257
|
+
},
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
module.exports = {
|
|
262
|
+
createSchedulerCommands,
|
|
263
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// `frigg.usage.*` — the read/write surface over the durable usage store.
|
|
2
|
+
const {
|
|
3
|
+
createUsageRepository,
|
|
4
|
+
} = require('../../usage/repositories/usage-repository-factory');
|
|
5
|
+
const { computeUsageWindows } = require('../../usage/usage-windows');
|
|
6
|
+
const { resolveNorthStarEntry } = require('../../telemetry/north-star');
|
|
7
|
+
|
|
8
|
+
function createUsageCommands({ usageRepository } = {}) {
|
|
9
|
+
const repository = usageRepository || createUsageRepository();
|
|
10
|
+
|
|
11
|
+
return {
|
|
12
|
+
// Increments both the day and hour window rows for `at`.
|
|
13
|
+
async recordUsageCounter({
|
|
14
|
+
integrationId,
|
|
15
|
+
integrationType,
|
|
16
|
+
metric,
|
|
17
|
+
value = 1,
|
|
18
|
+
at = new Date(),
|
|
19
|
+
}) {
|
|
20
|
+
const windows = computeUsageWindows(at);
|
|
21
|
+
for (const window of windows) {
|
|
22
|
+
await repository.increment({
|
|
23
|
+
integrationId,
|
|
24
|
+
integrationType,
|
|
25
|
+
metric,
|
|
26
|
+
window,
|
|
27
|
+
value,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
async getTotalsByDimension(args) {
|
|
33
|
+
return repository.getTotalsByDimension(args);
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
async getTimeSeries(args) {
|
|
37
|
+
return repository.getTimeSeries(args);
|
|
38
|
+
},
|
|
39
|
+
|
|
40
|
+
// Caller supplies the North Star config (Definition.telemetry.northStar);
|
|
41
|
+
// resolves the counter for the type (byType > default), null if none.
|
|
42
|
+
async getNorthStarTotals({ northStar, integrationType, since, groupBy = 'integrationType', bucket } = {}) {
|
|
43
|
+
const entry = resolveNorthStarEntry(northStar, integrationType);
|
|
44
|
+
if (!entry) return null;
|
|
45
|
+
const totals = await repository.getTotalsByDimension({
|
|
46
|
+
metric: entry.name,
|
|
47
|
+
groupBy,
|
|
48
|
+
since,
|
|
49
|
+
bucket,
|
|
50
|
+
});
|
|
51
|
+
return { metric: entry.name, totals };
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
module.exports = { createUsageCommands };
|