@friggframework/core 2.0.0-next.1 → 2.0.0-next.100
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 +57 -0
- package/handlers/backend-utils.js +316 -0
- 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 +15 -0
- 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 +117 -0
- package/handlers/routers/integration-webhook-routers.js +67 -0
- package/handlers/routers/reporting.js +9 -0
- package/handlers/routers/user.js +63 -0
- package/handlers/routers/websocket.js +57 -0
- 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 +30 -0
- package/index.js +111 -50
- 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 +619 -72
- package/integrations/integration-router.js +460 -218
- package/integrations/options.js +3 -4
- 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 +274 -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 +285 -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/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 -350
- 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,210 @@
|
|
|
1
|
+
const { prisma } = require('../../database/prisma');
|
|
2
|
+
const {
|
|
3
|
+
ScriptScheduleRepositoryInterface,
|
|
4
|
+
} = require('./script-schedule-repository-interface');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* PostgreSQL Script Schedule Repository Adapter
|
|
8
|
+
* Handles script schedule persistence using Prisma with PostgreSQL
|
|
9
|
+
*
|
|
10
|
+
* PostgreSQL-specific characteristics:
|
|
11
|
+
* - Uses Int IDs with autoincrement
|
|
12
|
+
* - Requires ID conversion: String (app layer) ↔ Int (database)
|
|
13
|
+
* - All returned IDs are converted to strings for application layer consistency
|
|
14
|
+
* - scriptName has unique index
|
|
15
|
+
*/
|
|
16
|
+
class ScriptScheduleRepositoryPostgres extends ScriptScheduleRepositoryInterface {
|
|
17
|
+
constructor() {
|
|
18
|
+
super();
|
|
19
|
+
this.prisma = prisma;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Convert string ID to integer for PostgreSQL queries
|
|
24
|
+
* @private
|
|
25
|
+
* @param {string|number|null|undefined} id - ID to convert
|
|
26
|
+
* @returns {number|null|undefined} Integer ID or null/undefined
|
|
27
|
+
* @throws {Error} If ID cannot be converted to integer
|
|
28
|
+
*/
|
|
29
|
+
_convertId(id) {
|
|
30
|
+
if (id === null || id === undefined) return id;
|
|
31
|
+
const parsed = Number.parseInt(id, 10);
|
|
32
|
+
if (Number.isNaN(parsed)) {
|
|
33
|
+
throw new Error(`Invalid ID: ${id} cannot be converted to integer`);
|
|
34
|
+
}
|
|
35
|
+
return parsed;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Convert schedule object IDs to strings
|
|
40
|
+
* @private
|
|
41
|
+
* @param {Object|null} schedule - Schedule object from database
|
|
42
|
+
* @returns {Object|null} Schedule with string IDs
|
|
43
|
+
*/
|
|
44
|
+
_convertScheduleIds(schedule) {
|
|
45
|
+
if (!schedule) return schedule;
|
|
46
|
+
return {
|
|
47
|
+
...schedule,
|
|
48
|
+
id: schedule.id?.toString(),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Find a schedule by script name
|
|
54
|
+
*
|
|
55
|
+
* @param {string} scriptName - The script name
|
|
56
|
+
* @returns {Promise<Object|null>} Schedule record with string ID or null if not found
|
|
57
|
+
*/
|
|
58
|
+
async findScheduleByScriptName(scriptName) {
|
|
59
|
+
const schedule = await this.prisma.scriptSchedule.findUnique({
|
|
60
|
+
where: { scriptName },
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
return this._convertScheduleIds(schedule);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Create or update a schedule (upsert)
|
|
68
|
+
*
|
|
69
|
+
* @param {Object} params - Schedule parameters
|
|
70
|
+
* @param {string} params.scriptName - Name of the script
|
|
71
|
+
* @param {boolean} params.enabled - Whether schedule is enabled
|
|
72
|
+
* @param {string} params.cronExpression - Cron expression
|
|
73
|
+
* @param {string} [params.timezone] - Timezone (default 'UTC')
|
|
74
|
+
* @param {string} [params.externalScheduleId] - External scheduler ID (e.g., AWS ARN)
|
|
75
|
+
* @param {string} [params.externalScheduleName] - External scheduler name
|
|
76
|
+
* @returns {Promise<Object>} Created or updated schedule record with string ID
|
|
77
|
+
*/
|
|
78
|
+
async upsertSchedule({ scriptName, enabled, cronExpression, timezone, externalScheduleId, externalScheduleName }) {
|
|
79
|
+
const data = {
|
|
80
|
+
enabled,
|
|
81
|
+
cronExpression,
|
|
82
|
+
timezone: timezone || 'UTC',
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
// Only set external scheduler fields if provided
|
|
86
|
+
if (externalScheduleId !== undefined) data.externalScheduleId = externalScheduleId;
|
|
87
|
+
if (externalScheduleName !== undefined) data.externalScheduleName = externalScheduleName;
|
|
88
|
+
|
|
89
|
+
const schedule = await this.prisma.scriptSchedule.upsert({
|
|
90
|
+
where: { scriptName },
|
|
91
|
+
update: data,
|
|
92
|
+
create: {
|
|
93
|
+
scriptName,
|
|
94
|
+
...data,
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
return this._convertScheduleIds(schedule);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Delete a schedule by script name
|
|
103
|
+
*
|
|
104
|
+
* @param {string} scriptName - The script name
|
|
105
|
+
* @returns {Promise<Object>} Deletion result
|
|
106
|
+
*/
|
|
107
|
+
async deleteSchedule(scriptName) {
|
|
108
|
+
try {
|
|
109
|
+
const schedule = await this.prisma.scriptSchedule.delete({
|
|
110
|
+
where: { scriptName },
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
return {
|
|
114
|
+
acknowledged: true,
|
|
115
|
+
deletedCount: 1,
|
|
116
|
+
deleted: this._convertScheduleIds(schedule),
|
|
117
|
+
};
|
|
118
|
+
} catch (error) {
|
|
119
|
+
// Return 0 count if not found
|
|
120
|
+
if (error.code === 'P2025') {
|
|
121
|
+
return {
|
|
122
|
+
acknowledged: true,
|
|
123
|
+
deletedCount: 0,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
throw error;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Update external scheduler information
|
|
132
|
+
*
|
|
133
|
+
* @param {string} scriptName - The script name
|
|
134
|
+
* @param {Object} externalInfo - External schedule information
|
|
135
|
+
* @param {string} [externalInfo.externalScheduleId] - External scheduler ID (e.g., AWS ARN)
|
|
136
|
+
* @param {string} [externalInfo.externalScheduleName] - External scheduler name
|
|
137
|
+
* @returns {Promise<Object>} Updated schedule record with string ID
|
|
138
|
+
*/
|
|
139
|
+
async updateScheduleExternalInfo(scriptName, { externalScheduleId, externalScheduleName }) {
|
|
140
|
+
const data = {};
|
|
141
|
+
if (externalScheduleId !== undefined) data.externalScheduleId = externalScheduleId;
|
|
142
|
+
if (externalScheduleName !== undefined) data.externalScheduleName = externalScheduleName;
|
|
143
|
+
|
|
144
|
+
const schedule = await this.prisma.scriptSchedule.update({
|
|
145
|
+
where: { scriptName },
|
|
146
|
+
data,
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
return this._convertScheduleIds(schedule);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Update last triggered timestamp
|
|
154
|
+
*
|
|
155
|
+
* @param {string} scriptName - The script name
|
|
156
|
+
* @param {Date} [timestamp] - Trigger timestamp (default: now)
|
|
157
|
+
* @returns {Promise<Object>} Updated schedule record with string ID
|
|
158
|
+
*/
|
|
159
|
+
async updateScheduleLastTriggered(scriptName, timestamp) {
|
|
160
|
+
const schedule = await this.prisma.scriptSchedule.update({
|
|
161
|
+
where: { scriptName },
|
|
162
|
+
data: {
|
|
163
|
+
lastTriggeredAt: timestamp || new Date(),
|
|
164
|
+
},
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
return this._convertScheduleIds(schedule);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Update next trigger timestamp
|
|
172
|
+
*
|
|
173
|
+
* @param {string} scriptName - The script name
|
|
174
|
+
* @param {Date} timestamp - Next trigger timestamp
|
|
175
|
+
* @returns {Promise<Object>} Updated schedule record with string ID
|
|
176
|
+
*/
|
|
177
|
+
async updateScheduleNextTrigger(scriptName, timestamp) {
|
|
178
|
+
const schedule = await this.prisma.scriptSchedule.update({
|
|
179
|
+
where: { scriptName },
|
|
180
|
+
data: {
|
|
181
|
+
nextTriggerAt: timestamp,
|
|
182
|
+
},
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
return this._convertScheduleIds(schedule);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* List all schedules
|
|
190
|
+
*
|
|
191
|
+
* @param {Object} [options] - Query options
|
|
192
|
+
* @param {boolean} [options.enabledOnly] - Only return enabled schedules
|
|
193
|
+
* @returns {Promise<Array>} Array of schedule records with string IDs
|
|
194
|
+
*/
|
|
195
|
+
async listSchedules(options = {}) {
|
|
196
|
+
const where = {};
|
|
197
|
+
if (options.enabledOnly) {
|
|
198
|
+
where.enabled = true;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const schedules = await this.prisma.scriptSchedule.findMany({
|
|
202
|
+
where,
|
|
203
|
+
orderBy: { scriptName: 'asc' },
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
return schedules.map((schedule) => this._convertScheduleIds(schedule));
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
module.exports = { ScriptScheduleRepositoryPostgres };
|
|
@@ -0,0 +1,451 @@
|
|
|
1
|
+
# Frigg Commands - Application Service Layer
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
Frigg Commands provide a clean, stable application service layer for all database operations in the Frigg Integration Framework. They abstract away the underlying ORM (currently Mongoose) and provide a consistent API for managing users, credentials, entities, and integrations.
|
|
6
|
+
|
|
7
|
+
## Why Use Commands?
|
|
8
|
+
|
|
9
|
+
### 1. **ORM Independence**
|
|
10
|
+
|
|
11
|
+
Commands isolate your integration code from the underlying database implementation. This allows Frigg to migrate between ORMs (e.g., Mongoose to Prisma) without breaking your integration code.
|
|
12
|
+
|
|
13
|
+
### 2. **Hexagonal Architecture**
|
|
14
|
+
|
|
15
|
+
Commands act as the **application service layer** in hexagonal architecture:
|
|
16
|
+
|
|
17
|
+
- **Domain Layer**: Your use cases and business logic
|
|
18
|
+
- **Application Layer**: Frigg Commands (this layer)
|
|
19
|
+
- **Infrastructure Layer**: Repositories and database models (hidden from you)
|
|
20
|
+
|
|
21
|
+
### 3. **Single Source of Truth**
|
|
22
|
+
|
|
23
|
+
All database operations flow through commands, making it easier to:
|
|
24
|
+
|
|
25
|
+
- Add caching, logging, or monitoring
|
|
26
|
+
- Enforce data validation rules
|
|
27
|
+
- Maintain consistent error handling
|
|
28
|
+
- Track data access patterns
|
|
29
|
+
|
|
30
|
+
### 4. **Future-Proof**
|
|
31
|
+
|
|
32
|
+
When Frigg upgrades its internals, commands maintain backward compatibility. Your integration code continues working without changes.
|
|
33
|
+
|
|
34
|
+
## Installation
|
|
35
|
+
|
|
36
|
+
Commands are available through the `@friggframework/core` package:
|
|
37
|
+
|
|
38
|
+
```javascript
|
|
39
|
+
const { createFriggCommands } = require('@friggframework/core');
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Basic Usage
|
|
43
|
+
|
|
44
|
+
### Initialize Commands
|
|
45
|
+
|
|
46
|
+
```javascript
|
|
47
|
+
const { createFriggCommands } = require('@friggframework/core');
|
|
48
|
+
const MyIntegration = require('./MyIntegration');
|
|
49
|
+
|
|
50
|
+
// Create command set with your integration class
|
|
51
|
+
const commands = createFriggCommands({
|
|
52
|
+
integrationClass: MyIntegration,
|
|
53
|
+
});
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Use Commands in Your Integration
|
|
57
|
+
|
|
58
|
+
```javascript
|
|
59
|
+
class MyIntegration extends IntegrationBase {
|
|
60
|
+
constructor() {
|
|
61
|
+
super();
|
|
62
|
+
this.commands = createFriggCommands({
|
|
63
|
+
integrationClass: MyIntegration,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async hydrateFromExternalUser(externalUserId) {
|
|
68
|
+
// Find integration context by external entity ID
|
|
69
|
+
const result =
|
|
70
|
+
await this.commands.findIntegrationContextByExternalEntityId(
|
|
71
|
+
externalUserId
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
if (result.error) {
|
|
75
|
+
return { error: result.error };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Hydrate integration with retrieved context
|
|
79
|
+
this.setIntegrationRecord(result.context);
|
|
80
|
+
return { record: this.record };
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Use Commands in Use Cases
|
|
86
|
+
|
|
87
|
+
```javascript
|
|
88
|
+
const { createFriggCommands } = require('@friggframework/core');
|
|
89
|
+
|
|
90
|
+
class AuthenticateUserUseCase {
|
|
91
|
+
constructor({ commands } = {}) {
|
|
92
|
+
// Accept injected commands for testing, or create default
|
|
93
|
+
this.commands =
|
|
94
|
+
commands ||
|
|
95
|
+
createFriggCommands({
|
|
96
|
+
integrationClass: MyIntegration,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async execute({ appUserId, username, email }) {
|
|
101
|
+
// Find or create user
|
|
102
|
+
let user = await this.commands.findUserByAppUserId(appUserId);
|
|
103
|
+
|
|
104
|
+
if (!user) {
|
|
105
|
+
user = await this.commands.createUser({
|
|
106
|
+
appUserId,
|
|
107
|
+
username,
|
|
108
|
+
email,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return user;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Available Commands
|
|
118
|
+
|
|
119
|
+
### User Commands
|
|
120
|
+
|
|
121
|
+
Manage Frigg users (individuals or organizations using your integration).
|
|
122
|
+
|
|
123
|
+
```javascript
|
|
124
|
+
// Create a new user
|
|
125
|
+
const user = await commands.createUser({
|
|
126
|
+
username: 'john@example.com',
|
|
127
|
+
email: 'john@example.com',
|
|
128
|
+
appUserId: 'external-user-123',
|
|
129
|
+
password: 'optional-password', // For password-based auth
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// Find user by app-specific user ID
|
|
133
|
+
const user = await commands.findUserByAppUserId('external-user-123');
|
|
134
|
+
|
|
135
|
+
// Find user by username
|
|
136
|
+
const user = await commands.findUserByUsername('john@example.com');
|
|
137
|
+
|
|
138
|
+
// Find user by Frigg internal ID
|
|
139
|
+
const user = await commands.findIndividualUserById('frigg-user-id');
|
|
140
|
+
|
|
141
|
+
// Update user
|
|
142
|
+
const updatedUser = await commands.updateUser('frigg-user-id', {
|
|
143
|
+
email: 'newemail@example.com',
|
|
144
|
+
});
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Credential Commands
|
|
148
|
+
|
|
149
|
+
Manage OAuth tokens and API credentials.
|
|
150
|
+
|
|
151
|
+
```javascript
|
|
152
|
+
// Create credential
|
|
153
|
+
const credential = await commands.createCredential({
|
|
154
|
+
userId: 'frigg-user-id',
|
|
155
|
+
externalId: 'oauth-user-id',
|
|
156
|
+
access_token: 'access_token_value',
|
|
157
|
+
refresh_token: 'refresh_token_value',
|
|
158
|
+
expires_at: new Date('2024-12-31'),
|
|
159
|
+
moduleName: 'asana',
|
|
160
|
+
authIsValid: true,
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
// Find credential
|
|
164
|
+
const credential = await commands.findCredential({
|
|
165
|
+
userId: 'frigg-user-id',
|
|
166
|
+
moduleName: 'asana',
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
// Update credential (e.g., after token refresh)
|
|
170
|
+
const updated = await commands.updateCredential('credential-id', {
|
|
171
|
+
access_token: 'new_access_token',
|
|
172
|
+
expires_at: new Date('2025-01-31'),
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
// Delete credential
|
|
176
|
+
await commands.deleteCredential('credential-id');
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Entity Commands
|
|
180
|
+
|
|
181
|
+
Manage module entities (connections to external services).
|
|
182
|
+
|
|
183
|
+
```javascript
|
|
184
|
+
// Create entity
|
|
185
|
+
const entity = await commands.createEntity({
|
|
186
|
+
userId: 'frigg-user-id',
|
|
187
|
+
externalId: 'asana-workspace-123',
|
|
188
|
+
name: 'My Workspace',
|
|
189
|
+
moduleName: 'asana',
|
|
190
|
+
credentialId: 'credential-id',
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
// Find single entity
|
|
194
|
+
const entity = await commands.findEntity({
|
|
195
|
+
userId: 'frigg-user-id',
|
|
196
|
+
externalId: 'asana-workspace-123',
|
|
197
|
+
moduleName: 'asana',
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
// Find entity by ID
|
|
201
|
+
const entity = await commands.findEntityById('entity-id');
|
|
202
|
+
|
|
203
|
+
// Find all entities for user
|
|
204
|
+
const entities = await commands.findEntitiesByUserId('frigg-user-id');
|
|
205
|
+
|
|
206
|
+
// Find entities by module
|
|
207
|
+
const asanaEntities = await commands.findEntitiesByUserIdAndModuleName(
|
|
208
|
+
'frigg-user-id',
|
|
209
|
+
'asana'
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
// Find multiple entities by IDs
|
|
213
|
+
const entities = await commands.findEntitiesByIds([
|
|
214
|
+
'entity-id-1',
|
|
215
|
+
'entity-id-2',
|
|
216
|
+
]);
|
|
217
|
+
|
|
218
|
+
// Update entity
|
|
219
|
+
const updated = await commands.updateEntity('entity-id', {
|
|
220
|
+
name: 'Updated Workspace Name',
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
// Delete entity
|
|
224
|
+
await commands.deleteEntity('entity-id');
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### Integration Commands
|
|
228
|
+
|
|
229
|
+
Manage integration records and load full integration contexts.
|
|
230
|
+
|
|
231
|
+
```javascript
|
|
232
|
+
// Find integration context by external entity ID
|
|
233
|
+
// Returns { context, error } where context includes record + hydrated modules
|
|
234
|
+
const result = await commands.findIntegrationContextByExternalEntityId(
|
|
235
|
+
'external-user-or-workspace-id'
|
|
236
|
+
);
|
|
237
|
+
|
|
238
|
+
if (!result.error) {
|
|
239
|
+
integration.setIntegrationRecord(result.context);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Load integration context by integration ID
|
|
243
|
+
const result = await commands.loadIntegrationContextById('integration-id');
|
|
244
|
+
|
|
245
|
+
if (!result.error) {
|
|
246
|
+
integration.setIntegrationRecord(result.context);
|
|
247
|
+
}
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
## Architecture Principles
|
|
251
|
+
|
|
252
|
+
### Dependency Injection for Testing
|
|
253
|
+
|
|
254
|
+
Commands support dependency injection for testing:
|
|
255
|
+
|
|
256
|
+
```javascript
|
|
257
|
+
// Production code - uses real repositories
|
|
258
|
+
const commands = createFriggCommands({ integrationClass: MyIntegration });
|
|
259
|
+
|
|
260
|
+
// Test code - inject mocks
|
|
261
|
+
const mockCommands = {
|
|
262
|
+
createUser: jest.fn().mockResolvedValue({ id: 'user-123' }),
|
|
263
|
+
findUserByAppUserId: jest.fn().mockResolvedValue(null),
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
const useCase = new MyUseCase({ commands: mockCommands });
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### Integration vs Unit Testing
|
|
270
|
+
|
|
271
|
+
**Commands are designed for integration testing** - they use real repositories by default:
|
|
272
|
+
|
|
273
|
+
```javascript
|
|
274
|
+
// ❌ Don't do this - commands always use real repositories
|
|
275
|
+
const commands = createFriggCommands({
|
|
276
|
+
userRepository: mockUserRepo, // This parameter doesn't exist
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
// ✅ Do this - inject mocked commands into your use cases
|
|
280
|
+
const useCase = new MyUseCase({
|
|
281
|
+
commands: mockCommands,
|
|
282
|
+
});
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
### Error Handling
|
|
286
|
+
|
|
287
|
+
Commands return domain objects directly. Handle errors at the use case level:
|
|
288
|
+
|
|
289
|
+
```javascript
|
|
290
|
+
try {
|
|
291
|
+
const user = await commands.createUser({ username, email });
|
|
292
|
+
return { success: true, user };
|
|
293
|
+
} catch (error) {
|
|
294
|
+
// Handle database errors
|
|
295
|
+
return { success: false, error: error.message };
|
|
296
|
+
}
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
For integration context operations, errors are returned in the result:
|
|
300
|
+
|
|
301
|
+
```javascript
|
|
302
|
+
const result = await commands.findIntegrationContextByExternalEntityId(userId);
|
|
303
|
+
|
|
304
|
+
if (result.error) {
|
|
305
|
+
return { error: result.error };
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// Use result.context
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
## Migration Guide
|
|
312
|
+
|
|
313
|
+
### From Direct Model Access
|
|
314
|
+
|
|
315
|
+
**Before (❌ Don't do this):**
|
|
316
|
+
|
|
317
|
+
```javascript
|
|
318
|
+
const { User } = require('@friggframework/core');
|
|
319
|
+
|
|
320
|
+
const user = await User.findOne({ appUserId: '123' });
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
**After (✅ Do this):**
|
|
324
|
+
|
|
325
|
+
```javascript
|
|
326
|
+
const { createFriggCommands } = require('@friggframework/core');
|
|
327
|
+
|
|
328
|
+
const commands = createFriggCommands({ integrationClass: MyIntegration });
|
|
329
|
+
const user = await commands.findUserByAppUserId('123');
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
### From IntegrationRepository (Backend Pattern)
|
|
333
|
+
|
|
334
|
+
**Before (❌ Old pattern):**
|
|
335
|
+
|
|
336
|
+
```javascript
|
|
337
|
+
const {
|
|
338
|
+
IntegrationRepository,
|
|
339
|
+
} = require('./repositories/IntegrationRepository');
|
|
340
|
+
|
|
341
|
+
this.integrationRepository = new IntegrationRepository(MyIntegration);
|
|
342
|
+
const result =
|
|
343
|
+
await this.integrationRepository.loadIntegrationRecordByAsanaUser(userId);
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
**After (✅ New pattern):**
|
|
347
|
+
|
|
348
|
+
```javascript
|
|
349
|
+
const { createFriggCommands } = require('@friggframework/core');
|
|
350
|
+
|
|
351
|
+
this.commands = createFriggCommands({ integrationClass: MyIntegration });
|
|
352
|
+
const result = await this.commands.findIntegrationContextByExternalEntityId(
|
|
353
|
+
userId
|
|
354
|
+
);
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
## Best Practices
|
|
358
|
+
|
|
359
|
+
### 1. Create Commands Once
|
|
360
|
+
|
|
361
|
+
Initialize commands in your constructor:
|
|
362
|
+
|
|
363
|
+
```javascript
|
|
364
|
+
class MyIntegration extends IntegrationBase {
|
|
365
|
+
constructor() {
|
|
366
|
+
super();
|
|
367
|
+
this.commands = createFriggCommands({
|
|
368
|
+
integrationClass: MyIntegration,
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
### 2. Pass Commands to Use Cases
|
|
375
|
+
|
|
376
|
+
Use dependency injection for testability:
|
|
377
|
+
|
|
378
|
+
```javascript
|
|
379
|
+
class MyUseCase {
|
|
380
|
+
constructor({ commands } = {}) {
|
|
381
|
+
this.commands =
|
|
382
|
+
commands ||
|
|
383
|
+
createFriggCommands({
|
|
384
|
+
integrationClass: MyIntegration,
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
### 3. Use Specific Finders
|
|
391
|
+
|
|
392
|
+
Use the most specific finder method:
|
|
393
|
+
|
|
394
|
+
```javascript
|
|
395
|
+
// ✅ Good - specific finder
|
|
396
|
+
const user = await commands.findUserByAppUserId('123');
|
|
397
|
+
|
|
398
|
+
// ❌ Less efficient - generic finder
|
|
399
|
+
const user = await commands.findUser({ appUserId: '123' });
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
### 4. Handle Null Returns
|
|
403
|
+
|
|
404
|
+
Most finders return `null` if not found:
|
|
405
|
+
|
|
406
|
+
```javascript
|
|
407
|
+
const user = await commands.findUserByAppUserId('123');
|
|
408
|
+
|
|
409
|
+
if (!user) {
|
|
410
|
+
// Handle user not found
|
|
411
|
+
user = await commands.createUser({ ... });
|
|
412
|
+
}
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
## Command Reference
|
|
416
|
+
|
|
417
|
+
| Category | Command | Description |
|
|
418
|
+
| --------------- | ------------------------------------------------------- | ----------------------------------------- |
|
|
419
|
+
| **User** | `createUser(data)` | Create new Frigg user |
|
|
420
|
+
| | `findUserByAppUserId(appUserId)` | Find by external app user ID |
|
|
421
|
+
| | `findUserByUsername(username)` | Find by username |
|
|
422
|
+
| | `findIndividualUserById(id)` | Find by Frigg user ID |
|
|
423
|
+
| | `updateUser(id, updates)` | Update user properties |
|
|
424
|
+
| **Credential** | `createCredential(data)` | Create OAuth credential |
|
|
425
|
+
| | `findCredential(filter)` | Find credential by filter |
|
|
426
|
+
| | `updateCredential(id, updates)` | Update credential (token refresh) |
|
|
427
|
+
| | `deleteCredential(id)` | Delete credential |
|
|
428
|
+
| **Entity** | `createEntity(data)` | Create module entity |
|
|
429
|
+
| | `findEntity(filter)` | Find entity by filter |
|
|
430
|
+
| | `findEntityById(id)` | Find by entity ID |
|
|
431
|
+
| | `findEntitiesByUserId(userId)` | Find all user entities |
|
|
432
|
+
| | `findEntitiesByUserIdAndModuleName(userId, moduleName)` | Find user entities for module |
|
|
433
|
+
| | `findEntitiesByIds(ids)` | Find multiple by IDs |
|
|
434
|
+
| | `updateEntity(id, updates)` | Update entity properties |
|
|
435
|
+
| | `deleteEntity(id)` | Delete entity |
|
|
436
|
+
| **Integration** | `findIntegrationContextByExternalEntityId(externalId)` | Load integration + modules by external ID |
|
|
437
|
+
| | `loadIntegrationContextById(integrationId)` | Load integration + modules by ID |
|
|
438
|
+
|
|
439
|
+
## Support
|
|
440
|
+
|
|
441
|
+
For questions or issues with commands:
|
|
442
|
+
|
|
443
|
+
1. Check this README
|
|
444
|
+
2. Review the main Frigg documentation
|
|
445
|
+
3. Open an issue on the Frigg Framework repository
|
|
446
|
+
|
|
447
|
+
## Related Documentation
|
|
448
|
+
|
|
449
|
+
- [Frigg Framework Overview](../../README.md)
|
|
450
|
+
- [Integration Development Guide](../../docs/integration-guide.md)
|
|
451
|
+
- [Hexagonal Architecture](../../docs/architecture.md)
|