@friggframework/core 2.0.0--canary.548.c8ae0ca.0 → 2.0.0--canary.545.dba001a.0
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/CHANGELOG.md +66 -67
- package/CLAUDE.md +148 -124
- package/README.md +165 -130
- package/admin-scripts/index.js +52 -0
- package/admin-scripts/repositories/admin-api-key-repository-documentdb.js +21 -0
- package/admin-scripts/repositories/admin-api-key-repository-factory.js +51 -0
- package/admin-scripts/repositories/admin-api-key-repository-interface.js +121 -0
- package/admin-scripts/repositories/admin-api-key-repository-mongo.js +155 -0
- package/admin-scripts/repositories/admin-api-key-repository-postgres.js +189 -0
- package/admin-scripts/repositories/admin-process-repository-documentdb.js +21 -0
- package/admin-scripts/repositories/admin-process-repository-factory.js +49 -0
- package/admin-scripts/repositories/admin-process-repository-interface.js +150 -0
- package/admin-scripts/repositories/admin-process-repository-mongo.js +213 -0
- package/admin-scripts/repositories/admin-process-repository-postgres.js +251 -0
- package/admin-scripts/repositories/script-execution-repository-documentdb.js +21 -0
- package/admin-scripts/repositories/script-execution-repository-factory.js +51 -0
- package/admin-scripts/repositories/script-execution-repository-interface.js +193 -0
- package/admin-scripts/repositories/script-execution-repository-mongo.js +278 -0
- package/admin-scripts/repositories/script-execution-repository-postgres.js +320 -0
- package/admin-scripts/repositories/script-schedule-repository-documentdb.js +21 -0
- package/admin-scripts/repositories/script-schedule-repository-factory.js +49 -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/admin-script-commands.js +348 -0
- package/application/commands/integration-commands.js +12 -6
- package/application/index.js +5 -3
- package/assertions/get.js +17 -7
- package/associations/association.js +59 -57
- package/core/CLAUDE.md +222 -156
- package/core/Worker.js +33 -12
- package/credential/credential-router.js +262 -0
- package/credential/repositories/credential-repository-documentdb.js +71 -1
- package/credential/repositories/credential-repository-factory.js +4 -6
- package/credential/repositories/credential-repository-interface.js +8 -2
- package/credential/repositories/credential-repository-mongo.js +45 -4
- package/credential/repositories/credential-repository-postgres.js +44 -3
- package/credential/repositories/credential-repository.js +3 -1
- package/credential/use-cases/delete-credential-for-user.js +50 -0
- package/credential/use-cases/get-credential-for-user.js +21 -3
- package/credential/use-cases/list-credentials-for-user.js +31 -0
- package/credential/use-cases/reauthorize-credential.js +103 -0
- package/credential/use-cases/update-authentication-status.js +5 -2
- package/database/MONGODB_TRANSACTION_FIX.md +72 -52
- package/database/config.js +13 -59
- package/database/documentdb-encryption-service.js +29 -13
- package/database/documentdb-utils.js +21 -12
- package/database/encryption/README.md +53 -36
- package/database/encryption/encryption-schema-registry.js +29 -23
- package/database/encryption/field-encryption-service.js +15 -4
- package/database/encryption/logger.js +3 -2
- package/database/encryption/prisma-encryption-extension.js +5 -8
- package/database/prisma.js +22 -14
- package/database/repositories/health-check-repository-factory.js +7 -5
- package/database/repositories/health-check-repository-interface.js +15 -5
- package/database/repositories/health-check-repository-postgres.js +1 -1
- package/database/use-cases/check-database-health-use-case.js +3 -2
- package/database/use-cases/check-database-state-use-case.js +8 -7
- package/database/use-cases/get-database-state-via-worker-use-case.js +15 -12
- package/database/use-cases/get-migration-status-use-case.js +4 -2
- package/database/use-cases/run-database-migration-use-case.js +41 -9
- package/database/use-cases/test-encryption-use-case.js +4 -5
- package/database/use-cases/trigger-database-migration-use-case.js +9 -8
- package/database/utils/mongodb-collection-utils.js +11 -3
- package/database/utils/mongodb-schema-init.js +10 -4
- package/database/utils/prisma-runner.js +144 -74
- package/database/utils/prisma-schema-parser.js +2 -2
- package/docs/PROCESS_MANAGEMENT_QUEUE_SPEC.md +135 -80
- package/encrypt/Cryptor.js +54 -49
- package/encrypt/aes-encryption-key-provider.js +82 -0
- package/encrypt/encryption-key-provider-interface.js +50 -0
- package/errors/fetch-error.js +1 -1
- package/generated/prisma-mongodb/edge.js +59 -4
- package/generated/prisma-mongodb/index-browser.js +49 -1
- package/generated/prisma-mongodb/index.d.ts +4023 -61
- package/generated/prisma-mongodb/index.js +59 -4
- package/generated/prisma-mongodb/package.json +1 -1
- package/generated/prisma-mongodb/schema.prisma +87 -0
- package/generated/prisma-mongodb/wasm.js +59 -4
- package/generated/prisma-postgresql/edge.js +58 -4
- package/generated/prisma-postgresql/index-browser.js +48 -1
- package/generated/prisma-postgresql/index.d.ts +4738 -54
- package/generated/prisma-postgresql/index.js +58 -4
- package/generated/prisma-postgresql/package.json +1 -1
- package/generated/prisma-postgresql/schema.prisma +85 -0
- package/generated/prisma-postgresql/wasm.js +58 -4
- package/handlers/WEBHOOKS.md +104 -81
- package/handlers/app-definition-loader.js +53 -10
- package/handlers/app-handler-helpers.js +14 -3
- package/handlers/backend-utils.js +0 -1
- package/handlers/database-migration-handler.js +64 -45
- package/handlers/middleware/admin-auth.js +53 -0
- package/handlers/routers/HEALTHCHECK.md +170 -135
- package/handlers/routers/admin.js +450 -0
- package/handlers/routers/auth.js +23 -2
- package/handlers/routers/db-migration.handler.js +2 -3
- package/handlers/routers/db-migration.js +102 -66
- package/handlers/routers/docs.js +180 -0
- package/handlers/routers/health.js +39 -249
- package/handlers/routers/integration-defined-routers.js +4 -4
- package/handlers/routers/integration-webhook-routers.js +17 -7
- package/handlers/routers/middleware/loadUser.js +39 -0
- package/handlers/routers/middleware/requireAdmin.js +43 -0
- package/handlers/routers/middleware/requireLoggedInUser.js +19 -0
- package/handlers/routers/websocket.js +9 -3
- package/handlers/use-cases/check-integrations-health-use-case.js +13 -6
- package/handlers/workers/db-migration.js +74 -35
- package/index.js +50 -16
- package/infrastructure/scheduler/index.js +15 -11
- package/infrastructure/scheduler/netlify-scheduler-adapter.js +185 -0
- package/infrastructure/scheduler/scheduler-service-factory.js +38 -31
- package/integrations/WEBHOOK-QUICKSTART.md +21 -14
- package/integrations/index.js +4 -0
- package/integrations/integration-base.js +9 -6
- package/integrations/integration-router.js +1828 -157
- package/integrations/options.js +47 -20
- package/integrations/repositories/integration-mapping-repository-factory.js +4 -8
- package/integrations/repositories/integration-repository-documentdb.js +53 -24
- package/integrations/repositories/integration-repository-factory.js +6 -6
- package/integrations/repositories/integration-repository-interface.js +27 -9
- package/integrations/repositories/integration-repository-postgres.js +3 -3
- package/integrations/repositories/process-repository-factory.js +4 -7
- package/integrations/repositories/process-repository-interface.js +9 -5
- package/integrations/repositories/process-repository-mongo.js +7 -3
- package/integrations/repositories/process-repository-postgres.js +2 -2
- package/integrations/tests/doubles/config-capturing-integration.js +9 -8
- package/integrations/tests/doubles/dummy-integration-class.js +81 -8
- package/integrations/tests/doubles/test-integration-repository.js +51 -11
- package/integrations/use-cases/create-integration.js +36 -32
- package/integrations/use-cases/create-process.js +20 -9
- package/integrations/use-cases/delete-integration-for-user.js +5 -4
- package/integrations/use-cases/execute-proxy-request.js +564 -0
- package/integrations/use-cases/get-integration-for-user.js +25 -10
- package/integrations/use-cases/get-integration-instance-by-definition.js +13 -7
- package/integrations/use-cases/get-integrations-for-user.js +4 -1
- package/integrations/use-cases/get-possible-integrations.js +1 -1
- package/integrations/use-cases/get-process.js +5 -6
- package/integrations/use-cases/index.js +3 -1
- package/integrations/use-cases/load-integration-context.js +1 -5
- package/integrations/use-cases/update-process-state.js +10 -8
- package/integrations/utils/map-integration-dto.js +9 -3
- package/jest-setup.js +1 -1
- package/jest-teardown.js +1 -1
- package/lambda/TimeoutCatcher.js +32 -30
- package/lambda/index.js +1 -1
- package/logs/index.js +3 -3
- package/modules/domain/entities/AuthorizationSession.js +131 -0
- package/modules/domain/entities/index.js +10 -0
- package/modules/module.js +4 -9
- package/modules/repositories/authorization-session-repository-factory.js +53 -0
- package/modules/repositories/authorization-session-repository-interface.js +75 -0
- package/modules/repositories/authorization-session-repository-mongo.js +169 -0
- package/modules/repositories/authorization-session-repository-postgres.js +170 -0
- package/modules/repositories/module-repository-documentdb.js +89 -41
- package/modules/repositories/module-repository-factory.js +4 -6
- package/modules/repositories/module-repository-interface.js +13 -0
- package/modules/repositories/module-repository-mongo.js +40 -5
- package/modules/repositories/module-repository-postgres.js +42 -5
- package/modules/repositories/module-repository.js +28 -0
- package/modules/requester/api-key.js +0 -2
- package/modules/requester/basic.js +0 -1
- package/modules/requester/oauth-2.js +3 -14
- package/modules/test/mock-api/definition.js +15 -10
- package/modules/test/mock-api/mocks/hubspot.js +36 -36
- package/modules/tests/doubles/test-module-factory.js +10 -4
- package/modules/tests/doubles/test-module-repository.js +1 -1
- package/modules/use-cases/delete-module-entity.js +23 -0
- package/modules/use-cases/get-authorization-requirements.js +110 -0
- package/modules/use-cases/get-entities-for-user.js +1 -1
- package/modules/use-cases/get-entity-options-by-id.js +8 -6
- package/modules/use-cases/get-module-entity-by-id.js +16 -0
- package/modules/use-cases/get-module.js +12 -9
- package/modules/use-cases/process-authorization-callback.js +12 -6
- package/modules/use-cases/process-authorization-step.js +160 -0
- package/modules/use-cases/refresh-entity-options.js +8 -6
- package/modules/use-cases/start-authorization-session.js +86 -0
- package/modules/use-cases/test-module-auth.js +8 -6
- package/modules/use-cases/update-module-entity.js +27 -0
- package/modules/utils/map-module-dto.js +2 -2
- package/openapi/openapi-spec-generator.js +370 -0
- package/openapi/openapi-v1.yaml +1360 -0
- package/openapi/openapi-v2.yaml +1535 -0
- package/package.json +7 -9
- package/prisma-mongodb/schema.prisma +87 -0
- package/prisma-postgresql/migrations/20260222000000_add_admin_process_and_script_schedule/migration.sql +55 -0
- package/prisma-postgresql/schema.prisma +85 -0
- package/providers/index.js +13 -0
- package/providers/resolve-provider.js +93 -0
- package/queues/index.js +19 -0
- package/queues/providers/index.js +7 -0
- package/queues/providers/netlify-background-provider.js +76 -0
- package/queues/providers/qstash-queue-provider.js +122 -0
- package/queues/queue-client-interface.js +55 -0
- package/queues/queue-provider-factory.js +93 -0
- package/queues/queue-provider.js +46 -0
- package/queues/queuer-util.js +33 -25
- package/syncs/repositories/sync-repository-documentdb.js +39 -19
- package/syncs/repositories/sync-repository-factory.js +4 -4
- package/syncs/repositories/sync-repository-interface.js +6 -2
- package/syncs/repositories/sync-repository-postgres.js +19 -13
- package/syncs/sync.js +104 -100
- package/token/repositories/token-repository-factory.js +4 -6
- package/types/assertions/index.d.ts +72 -72
- package/types/core/index.d.ts +98 -53
- package/types/errors/index.d.ts +61 -54
- package/types/eslint-config/index.d.ts +40 -40
- package/types/lambda/index.d.ts +26 -26
- package/types/logs/index.d.ts +4 -4
- package/types/prettier-config/index.d.ts +5 -5
- package/types/test-environment/index.d.ts +14 -14
- package/types/tsconfig.json +95 -93
- package/user/repositories/user-repository-factory.js +4 -4
- package/user/tests/doubles/test-user-repository.js +5 -2
- package/user/use-cases/authenticate-user.js +2 -4
- package/user/use-cases/authenticate-with-shared-secret.js +1 -1
- package/user/use-cases/create-individual-user.js +8 -7
- package/user/use-cases/create-organization-user.js +1 -1
- package/user/use-cases/create-token-for-user-id.js +1 -1
- package/user/use-cases/delete-user.js +47 -0
- package/user/use-cases/get-user-from-adopter-jwt.js +0 -2
- package/user/use-cases/get-user-from-bearer-token.js +7 -3
- package/user/use-cases/get-user-from-x-frigg-headers.js +14 -11
- package/user/use-cases/login-user.js +8 -8
- package/user/user.js +18 -4
- package/utils/backend-path.js +2 -2
- package/utils/index.js +4 -1
- package/websocket/repositories/websocket-connection-repository-documentdb.js +46 -21
- package/websocket/repositories/websocket-connection-repository-factory.js +4 -8
- package/websocket/repositories/websocket-connection-repository-mongo.js +26 -16
- package/websocket/repositories/websocket-connection-repository-postgres.js +26 -15
- package/websocket/repositories/websocket-connection-repository.js +26 -20
- package/websocket/websocket-message-sender-interface.js +38 -0
- package/database/adapters/lambda-invoker.js +0 -97
- package/database/repositories/migration-status-repository-s3.js +0 -137
- package/infrastructure/scheduler/eventbridge-scheduler-adapter.js +0 -184
|
@@ -49,16 +49,28 @@ const {
|
|
|
49
49
|
const {
|
|
50
50
|
CheckDatabaseStateUseCase,
|
|
51
51
|
} = require('../../database/use-cases/check-database-state-use-case');
|
|
52
|
-
const {
|
|
53
|
-
MigrationStatusRepositoryS3,
|
|
54
|
-
} = require('../../database/repositories/migration-status-repository-s3');
|
|
55
|
-
|
|
56
52
|
// Inject prisma-runner as dependency
|
|
57
53
|
const prismaRunner = require('../../database/utils/prisma-runner');
|
|
58
54
|
|
|
59
|
-
//
|
|
60
|
-
const
|
|
61
|
-
|
|
55
|
+
// Migration status repository is loaded from the resolved provider.
|
|
56
|
+
const { resolveProvider } = require('../../providers/resolve-provider');
|
|
57
|
+
|
|
58
|
+
let _migrationStatusRepository = null;
|
|
59
|
+
function getMigrationStatusRepository() {
|
|
60
|
+
if (!_migrationStatusRepository) {
|
|
61
|
+
const provider = resolveProvider();
|
|
62
|
+
const MigrationStatusRepository = provider.MigrationStatusRepositoryS3;
|
|
63
|
+
if (!MigrationStatusRepository) {
|
|
64
|
+
throw new Error(
|
|
65
|
+
`Provider '${provider.name}' does not export a MigrationStatusRepository`
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
const bucketName =
|
|
69
|
+
process.env.S3_BUCKET_NAME || process.env.MIGRATION_STATUS_BUCKET;
|
|
70
|
+
_migrationStatusRepository = new MigrationStatusRepository(bucketName);
|
|
71
|
+
}
|
|
72
|
+
return _migrationStatusRepository;
|
|
73
|
+
}
|
|
62
74
|
|
|
63
75
|
/**
|
|
64
76
|
* Sanitizes error messages to prevent credential leaks
|
|
@@ -68,19 +80,27 @@ const migrationStatusRepository = new MigrationStatusRepositoryS3(bucketName);
|
|
|
68
80
|
function sanitizeError(errorMessage) {
|
|
69
81
|
if (!errorMessage) return 'Unknown error';
|
|
70
82
|
|
|
71
|
-
return
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
83
|
+
return (
|
|
84
|
+
String(errorMessage)
|
|
85
|
+
// Remove PostgreSQL connection strings
|
|
86
|
+
.replace(
|
|
87
|
+
/postgresql:\/\/[^@\s]+@[^\s/]+/gi,
|
|
88
|
+
'postgresql://***:***@***'
|
|
89
|
+
)
|
|
90
|
+
// Remove MongoDB connection strings
|
|
91
|
+
.replace(
|
|
92
|
+
/mongodb(\+srv)?:\/\/[^@\s]+@[^\s/]+/gi,
|
|
93
|
+
'mongodb$1://***:***@***'
|
|
94
|
+
)
|
|
95
|
+
// Remove password parameters
|
|
96
|
+
.replace(/password[=:]\s*[^\s,;)]+/gi, 'password=***')
|
|
97
|
+
// Remove API keys
|
|
98
|
+
.replace(/apikey[=:]\s*[^\s,;)]+/gi, 'apikey=***')
|
|
99
|
+
.replace(/api[_-]?key[=:]\s*[^\s,;)]+/gi, 'api_key=***')
|
|
100
|
+
// Remove tokens
|
|
101
|
+
.replace(/token[=:]\s*[^\s,;)]+/gi, 'token=***')
|
|
102
|
+
.replace(/bearer\s+[^\s,;)]+/gi, 'bearer ***')
|
|
103
|
+
);
|
|
84
104
|
}
|
|
85
105
|
|
|
86
106
|
/**
|
|
@@ -145,11 +165,18 @@ exports.handler = async (event, context) => {
|
|
|
145
165
|
console.log('Database Migration Lambda Started');
|
|
146
166
|
console.log('========================================');
|
|
147
167
|
console.log('Event:', JSON.stringify(event, null, 2));
|
|
148
|
-
console.log(
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
168
|
+
console.log(
|
|
169
|
+
'Context:',
|
|
170
|
+
JSON.stringify(
|
|
171
|
+
{
|
|
172
|
+
requestId: context.requestId,
|
|
173
|
+
functionName: context.functionName,
|
|
174
|
+
remainingTimeInMillis: context.getRemainingTimeInMillis(),
|
|
175
|
+
},
|
|
176
|
+
null,
|
|
177
|
+
2
|
|
178
|
+
)
|
|
179
|
+
);
|
|
153
180
|
|
|
154
181
|
// Extract migration parameters from event
|
|
155
182
|
const { migrationId, dbType, stage } = extractMigrationParams(event);
|
|
@@ -164,7 +191,9 @@ exports.handler = async (event, context) => {
|
|
|
164
191
|
console.log(`========================================`);
|
|
165
192
|
|
|
166
193
|
try {
|
|
167
|
-
const checkDbStateUseCase = new CheckDatabaseStateUseCase({
|
|
194
|
+
const checkDbStateUseCase = new CheckDatabaseStateUseCase({
|
|
195
|
+
prismaRunner,
|
|
196
|
+
});
|
|
168
197
|
const status = await checkDbStateUseCase.execute(dbType, stage);
|
|
169
198
|
|
|
170
199
|
console.log('✓ Database state check completed');
|
|
@@ -217,8 +246,10 @@ exports.handler = async (event, context) => {
|
|
|
217
246
|
|
|
218
247
|
// Update migration status to RUNNING (if migrationId provided)
|
|
219
248
|
if (migrationId) {
|
|
220
|
-
console.log(
|
|
221
|
-
|
|
249
|
+
console.log(
|
|
250
|
+
`\n✓ Updating migration status to RUNNING: ${migrationId}`
|
|
251
|
+
);
|
|
252
|
+
await getMigrationStatusRepository().update({
|
|
222
253
|
migrationId,
|
|
223
254
|
stage,
|
|
224
255
|
state: 'RUNNING',
|
|
@@ -255,8 +286,10 @@ exports.handler = async (event, context) => {
|
|
|
255
286
|
|
|
256
287
|
// Update migration status to COMPLETED (if migrationId provided)
|
|
257
288
|
if (migrationId) {
|
|
258
|
-
console.log(
|
|
259
|
-
|
|
289
|
+
console.log(
|
|
290
|
+
`\n✓ Updating migration status to COMPLETED: ${migrationId}`
|
|
291
|
+
);
|
|
292
|
+
await getMigrationStatusRepository().update({
|
|
260
293
|
migrationId,
|
|
261
294
|
stage,
|
|
262
295
|
state: 'COMPLETED',
|
|
@@ -284,7 +317,6 @@ exports.handler = async (event, context) => {
|
|
|
284
317
|
statusCode: 200,
|
|
285
318
|
body: JSON.stringify(responseBody),
|
|
286
319
|
};
|
|
287
|
-
|
|
288
320
|
} catch (error) {
|
|
289
321
|
console.error('\n========================================');
|
|
290
322
|
console.error('Migration Failed');
|
|
@@ -317,8 +349,10 @@ exports.handler = async (event, context) => {
|
|
|
317
349
|
// Update migration status to FAILED (if migrationId provided)
|
|
318
350
|
if (migrationId) {
|
|
319
351
|
try {
|
|
320
|
-
console.log(
|
|
321
|
-
|
|
352
|
+
console.log(
|
|
353
|
+
`\n✓ Updating migration status to FAILED: ${migrationId}`
|
|
354
|
+
);
|
|
355
|
+
await getMigrationStatusRepository().update({
|
|
322
356
|
migrationId,
|
|
323
357
|
stage,
|
|
324
358
|
state: 'FAILED',
|
|
@@ -327,7 +361,10 @@ exports.handler = async (event, context) => {
|
|
|
327
361
|
failedAt: new Date().toISOString(),
|
|
328
362
|
});
|
|
329
363
|
} catch (updateError) {
|
|
330
|
-
console.error(
|
|
364
|
+
console.error(
|
|
365
|
+
'Failed to update migration status:',
|
|
366
|
+
updateError.message
|
|
367
|
+
);
|
|
331
368
|
// Continue - don't let status update failure block error response
|
|
332
369
|
}
|
|
333
370
|
}
|
|
@@ -337,7 +374,9 @@ exports.handler = async (event, context) => {
|
|
|
337
374
|
error: sanitizedError,
|
|
338
375
|
errorType: error.name || 'Error',
|
|
339
376
|
// Only include stack traces in development environments
|
|
340
|
-
...(stage === 'dev' || stage === 'local' || stage === 'test'
|
|
377
|
+
...(stage === 'dev' || stage === 'local' || stage === 'test'
|
|
378
|
+
? { stack: error.stack }
|
|
379
|
+
: {}),
|
|
341
380
|
};
|
|
342
381
|
|
|
343
382
|
if (migrationId) {
|
package/index.js
CHANGED
|
@@ -21,7 +21,6 @@ const {
|
|
|
21
21
|
} = require('./database/index');
|
|
22
22
|
const {
|
|
23
23
|
createUserRepository,
|
|
24
|
-
UserRepositoryMongo,
|
|
25
24
|
UserRepositoryPostgres,
|
|
26
25
|
} = require('./user/repositories/user-repository-factory');
|
|
27
26
|
const {
|
|
@@ -30,9 +29,7 @@ const {
|
|
|
30
29
|
const {
|
|
31
30
|
GetUserFromAdopterJwt,
|
|
32
31
|
} = require('./user/use-cases/get-user-from-adopter-jwt');
|
|
33
|
-
const {
|
|
34
|
-
AuthenticateUser,
|
|
35
|
-
} = require('./user/use-cases/authenticate-user');
|
|
32
|
+
const { AuthenticateUser } = require('./user/use-cases/authenticate-user');
|
|
36
33
|
|
|
37
34
|
const {
|
|
38
35
|
CredentialRepository,
|
|
@@ -43,18 +40,14 @@ const {
|
|
|
43
40
|
const {
|
|
44
41
|
IntegrationMappingRepository,
|
|
45
42
|
} = require('./integrations/repositories/integration-mapping-repository');
|
|
46
|
-
const {
|
|
47
|
-
CreateProcess,
|
|
48
|
-
} = require('./integrations/use-cases/create-process');
|
|
43
|
+
const { CreateProcess } = require('./integrations/use-cases/create-process');
|
|
49
44
|
const {
|
|
50
45
|
UpdateProcessState,
|
|
51
46
|
} = require('./integrations/use-cases/update-process-state');
|
|
52
47
|
const {
|
|
53
48
|
UpdateProcessMetrics,
|
|
54
49
|
} = require('./integrations/use-cases/update-process-metrics');
|
|
55
|
-
const {
|
|
56
|
-
GetProcess,
|
|
57
|
-
} = require('./integrations/use-cases/get-process');
|
|
50
|
+
const { GetProcess } = require('./integrations/use-cases/get-process');
|
|
58
51
|
const { Cryptor } = require('./encrypt');
|
|
59
52
|
const {
|
|
60
53
|
BaseError,
|
|
@@ -70,12 +63,11 @@ const {
|
|
|
70
63
|
checkRequiredParams,
|
|
71
64
|
getModulesDefinitionFromIntegrationClasses,
|
|
72
65
|
LoadIntegrationContextUseCase,
|
|
66
|
+
createProcessRepository,
|
|
73
67
|
} = require('./integrations/index');
|
|
74
68
|
const { TimeoutCatcher } = require('./lambda/index');
|
|
75
69
|
const { debug, initDebugLog, flushDebugLog } = require('./logs/index');
|
|
76
70
|
const {
|
|
77
|
-
Credential,
|
|
78
|
-
Entity,
|
|
79
71
|
ApiKeyRequester,
|
|
80
72
|
BasicAuthRequester,
|
|
81
73
|
OAuth2Requester,
|
|
@@ -86,7 +78,31 @@ const {
|
|
|
86
78
|
const application = require('./application');
|
|
87
79
|
const utils = require('./utils');
|
|
88
80
|
|
|
89
|
-
const {
|
|
81
|
+
const {
|
|
82
|
+
QueuerUtil,
|
|
83
|
+
QueueProvider,
|
|
84
|
+
QueueClientInterface,
|
|
85
|
+
createQueueProvider,
|
|
86
|
+
QUEUE_PROVIDERS,
|
|
87
|
+
} = require('./queues');
|
|
88
|
+
|
|
89
|
+
const {
|
|
90
|
+
EncryptionKeyProviderInterface,
|
|
91
|
+
} = require('./encrypt/encryption-key-provider-interface');
|
|
92
|
+
const {
|
|
93
|
+
AesEncryptionKeyProvider,
|
|
94
|
+
} = require('./encrypt/aes-encryption-key-provider');
|
|
95
|
+
const {
|
|
96
|
+
WebSocketMessageSenderInterface,
|
|
97
|
+
StaleConnectionError,
|
|
98
|
+
} = require('./websocket/websocket-message-sender-interface');
|
|
99
|
+
|
|
100
|
+
const {
|
|
101
|
+
resolveProvider,
|
|
102
|
+
determineProviderName,
|
|
103
|
+
providerPackageName,
|
|
104
|
+
KNOWN_PROVIDERS,
|
|
105
|
+
} = require('./providers');
|
|
90
106
|
|
|
91
107
|
module.exports = {
|
|
92
108
|
// assertions
|
|
@@ -110,7 +126,7 @@ module.exports = {
|
|
|
110
126
|
TokenRepository,
|
|
111
127
|
WebsocketConnectionRepository,
|
|
112
128
|
createUserRepository,
|
|
113
|
-
UserRepositoryMongo,
|
|
129
|
+
get UserRepositoryMongo() { return require('./user/repositories/user-repository-factory').UserRepositoryMongo; },
|
|
114
130
|
UserRepositoryPostgres,
|
|
115
131
|
GetUserFromXFriggHeaders,
|
|
116
132
|
GetUserFromAdopterJwt,
|
|
@@ -138,6 +154,7 @@ module.exports = {
|
|
|
138
154
|
UpdateProcessState,
|
|
139
155
|
UpdateProcessMetrics,
|
|
140
156
|
GetProcess,
|
|
157
|
+
createProcessRepository,
|
|
141
158
|
|
|
142
159
|
// application - Command factories for integration developers
|
|
143
160
|
application,
|
|
@@ -147,6 +164,7 @@ module.exports = {
|
|
|
147
164
|
createEntityCommands: application.createEntityCommands,
|
|
148
165
|
createCredentialCommands: application.createCredentialCommands,
|
|
149
166
|
createSchedulerCommands: application.createSchedulerCommands,
|
|
167
|
+
createAdminScriptCommands: application.createAdminScriptCommands,
|
|
150
168
|
findIntegrationContextByExternalEntityId:
|
|
151
169
|
application.findIntegrationContextByExternalEntityId,
|
|
152
170
|
integrationCommands: application.integrationCommands,
|
|
@@ -160,8 +178,6 @@ module.exports = {
|
|
|
160
178
|
flushDebugLog,
|
|
161
179
|
|
|
162
180
|
// module plugin
|
|
163
|
-
Credential,
|
|
164
|
-
Entity,
|
|
165
181
|
ApiKeyRequester,
|
|
166
182
|
BasicAuthRequester,
|
|
167
183
|
OAuth2Requester,
|
|
@@ -170,6 +186,24 @@ module.exports = {
|
|
|
170
186
|
ModuleFactory,
|
|
171
187
|
// queues
|
|
172
188
|
QueuerUtil,
|
|
189
|
+
QueueProvider,
|
|
190
|
+
QueueClientInterface,
|
|
191
|
+
createQueueProvider,
|
|
192
|
+
QUEUE_PROVIDERS,
|
|
193
|
+
|
|
194
|
+
// encryption interfaces
|
|
195
|
+
EncryptionKeyProviderInterface,
|
|
196
|
+
AesEncryptionKeyProvider,
|
|
197
|
+
|
|
198
|
+
// websocket interfaces
|
|
199
|
+
WebSocketMessageSenderInterface,
|
|
200
|
+
StaleConnectionError,
|
|
201
|
+
|
|
202
|
+
// providers
|
|
203
|
+
resolveProvider,
|
|
204
|
+
determineProviderName,
|
|
205
|
+
providerPackageName,
|
|
206
|
+
KNOWN_PROVIDERS,
|
|
173
207
|
|
|
174
208
|
// utils
|
|
175
209
|
...utils,
|
|
@@ -4,30 +4,34 @@
|
|
|
4
4
|
* Provides scheduling capabilities for one-time jobs.
|
|
5
5
|
* Follows hexagonal architecture with interface + adapters pattern.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* Use createSchedulerService() to get the correct adapter for the
|
|
8
|
+
* active provider. The factory uses resolveProvider() internally.
|
|
9
|
+
*
|
|
10
|
+
* Adapters:
|
|
11
|
+
* - AWS EventBridge: via @friggframework/provider-aws (SchedulerAdapter)
|
|
12
|
+
* - Netlify: NetlifySchedulerAdapter (ships with core, no AWS deps)
|
|
13
|
+
* - Mock: MockSchedulerAdapter (local development)
|
|
10
14
|
*/
|
|
11
15
|
|
|
12
16
|
const { SchedulerServiceInterface } = require('./scheduler-service-interface');
|
|
13
|
-
const { EventBridgeSchedulerAdapter } = require('./eventbridge-scheduler-adapter');
|
|
14
|
-
const { MockSchedulerAdapter } = require('./mock-scheduler-adapter');
|
|
15
17
|
const {
|
|
16
18
|
createSchedulerService,
|
|
17
19
|
SCHEDULER_PROVIDERS,
|
|
18
|
-
determineProvider,
|
|
19
20
|
} = require('./scheduler-service-factory');
|
|
20
21
|
|
|
21
22
|
module.exports = {
|
|
22
23
|
// Interface (Port)
|
|
23
24
|
SchedulerServiceInterface,
|
|
24
25
|
|
|
25
|
-
//
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
// Core adapters (no external SDK dependencies)
|
|
27
|
+
get MockSchedulerAdapter() {
|
|
28
|
+
return require('./mock-scheduler-adapter').MockSchedulerAdapter;
|
|
29
|
+
},
|
|
30
|
+
get NetlifySchedulerAdapter() {
|
|
31
|
+
return require('./netlify-scheduler-adapter').NetlifySchedulerAdapter;
|
|
32
|
+
},
|
|
28
33
|
|
|
29
|
-
// Factory
|
|
34
|
+
// Factory — resolves the correct adapter via provider plugin system
|
|
30
35
|
createSchedulerService,
|
|
31
36
|
SCHEDULER_PROVIDERS,
|
|
32
|
-
determineProvider,
|
|
33
37
|
};
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Netlify Scheduler Adapter
|
|
3
|
+
*
|
|
4
|
+
* Implements SchedulerServiceInterface for Netlify deployments.
|
|
5
|
+
*
|
|
6
|
+
* Netlify Scheduled Functions use cron syntax but are defined statically in
|
|
7
|
+
* netlify.toml or via the @netlify/functions schedule() helper. They cannot
|
|
8
|
+
* be created dynamically at runtime like EventBridge Scheduler.
|
|
9
|
+
*
|
|
10
|
+
* Strategy for one-time scheduled jobs on Netlify:
|
|
11
|
+
* 1. Store the schedule in the database (same as mock adapter pattern)
|
|
12
|
+
* 2. A Netlify Scheduled Function runs on a cron interval (e.g., every 5 min)
|
|
13
|
+
* 3. The cron function queries for due schedules and dispatches them
|
|
14
|
+
* to a background function for execution
|
|
15
|
+
*
|
|
16
|
+
* This is a "poll-and-dispatch" pattern vs EventBridge's "push" pattern.
|
|
17
|
+
* Trade-off: slightly less precise timing (up to cron interval delay),
|
|
18
|
+
* but works on any platform without cloud-specific scheduling APIs.
|
|
19
|
+
*
|
|
20
|
+
* For integrations that need the queue provider to dispatch jobs:
|
|
21
|
+
* - queueResourceId maps to the background function URL path
|
|
22
|
+
* - Payload is stored and forwarded when the cron fires
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
const { SchedulerServiceInterface } = require('./scheduler-service-interface');
|
|
26
|
+
|
|
27
|
+
class NetlifySchedulerAdapter extends SchedulerServiceInterface {
|
|
28
|
+
/**
|
|
29
|
+
* @param {Object} options
|
|
30
|
+
* @param {Object} options.repository - Repository for persisting schedules
|
|
31
|
+
* Must implement: save(schedule), delete(scheduleName),
|
|
32
|
+
* findByName(scheduleName), findDue(now)
|
|
33
|
+
* @param {Object} [options.queueProvider] - Queue provider for dispatching due jobs
|
|
34
|
+
*/
|
|
35
|
+
constructor(options = {}) {
|
|
36
|
+
super();
|
|
37
|
+
this.repository = options.repository;
|
|
38
|
+
this.queueProvider = options.queueProvider;
|
|
39
|
+
|
|
40
|
+
if (!this.repository) {
|
|
41
|
+
console.warn(
|
|
42
|
+
'[NetlifyScheduler] No repository provided. ' +
|
|
43
|
+
'Schedules will be stored in memory (lost on function restart). ' +
|
|
44
|
+
'Provide a database-backed repository for production use.'
|
|
45
|
+
);
|
|
46
|
+
this._inMemorySchedules = new Map();
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async scheduleOneTime({
|
|
51
|
+
scheduleName,
|
|
52
|
+
scheduleAt,
|
|
53
|
+
queueResourceId,
|
|
54
|
+
payload,
|
|
55
|
+
}) {
|
|
56
|
+
if (!scheduleName) throw new Error('scheduleName is required');
|
|
57
|
+
if (!scheduleAt || !(scheduleAt instanceof Date))
|
|
58
|
+
throw new Error('scheduleAt must be a valid Date object');
|
|
59
|
+
if (!queueResourceId) throw new Error('queueResourceId is required');
|
|
60
|
+
|
|
61
|
+
const scheduleData = {
|
|
62
|
+
scheduleName,
|
|
63
|
+
scheduledAt: scheduleAt.toISOString(),
|
|
64
|
+
queueResourceId,
|
|
65
|
+
payload,
|
|
66
|
+
createdAt: new Date().toISOString(),
|
|
67
|
+
state: 'PENDING',
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
if (this.repository) {
|
|
71
|
+
await this.repository.save(scheduleData);
|
|
72
|
+
} else {
|
|
73
|
+
this._inMemorySchedules.set(scheduleName, scheduleData);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
console.log(
|
|
77
|
+
`[NetlifyScheduler] Scheduled: ${scheduleName} for ${scheduleAt.toISOString()}`
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
return {
|
|
81
|
+
scheduledJobId: `netlify-schedule-${scheduleName}`,
|
|
82
|
+
scheduledAt: scheduleAt.toISOString(),
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async deleteSchedule(scheduleName) {
|
|
87
|
+
if (!scheduleName) throw new Error('scheduleName is required');
|
|
88
|
+
|
|
89
|
+
if (this.repository) {
|
|
90
|
+
await this.repository.delete(scheduleName);
|
|
91
|
+
} else {
|
|
92
|
+
this._inMemorySchedules.delete(scheduleName);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
console.log(`[NetlifyScheduler] Deleted schedule: ${scheduleName}`);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async getScheduleStatus(scheduleName) {
|
|
99
|
+
if (!scheduleName) throw new Error('scheduleName is required');
|
|
100
|
+
|
|
101
|
+
let schedule;
|
|
102
|
+
if (this.repository) {
|
|
103
|
+
schedule = await this.repository.findByName(scheduleName);
|
|
104
|
+
} else {
|
|
105
|
+
schedule = this._inMemorySchedules.get(scheduleName);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (!schedule) {
|
|
109
|
+
return { exists: false };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
exists: true,
|
|
114
|
+
scheduledAt: schedule.scheduledAt,
|
|
115
|
+
state: schedule.state,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Process due schedules. Called by the Netlify cron function.
|
|
121
|
+
*
|
|
122
|
+
* Finds all schedules where scheduledAt <= now, dispatches them
|
|
123
|
+
* to the queue provider, and marks them as completed.
|
|
124
|
+
*
|
|
125
|
+
* @returns {Promise<{ processed: number, errors: number }>}
|
|
126
|
+
*/
|
|
127
|
+
async processDueSchedules() {
|
|
128
|
+
if (!this.repository) {
|
|
129
|
+
console.warn(
|
|
130
|
+
'[NetlifyScheduler] Cannot process due schedules without a repository'
|
|
131
|
+
);
|
|
132
|
+
return { processed: 0, errors: 0 };
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const now = new Date();
|
|
136
|
+
const dueSchedules = await this.repository.findDue(now);
|
|
137
|
+
|
|
138
|
+
let processed = 0;
|
|
139
|
+
let errors = 0;
|
|
140
|
+
|
|
141
|
+
for (const schedule of dueSchedules) {
|
|
142
|
+
try {
|
|
143
|
+
// Mark as PROCESSING before dispatch so the next cron tick
|
|
144
|
+
// won't re-pick this schedule (findDue only returns PENDING).
|
|
145
|
+
await this.repository.save({
|
|
146
|
+
...schedule,
|
|
147
|
+
state: 'PROCESSING',
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
if (this.queueProvider) {
|
|
151
|
+
await this.queueProvider.send(
|
|
152
|
+
schedule.payload,
|
|
153
|
+
schedule.queueResourceId
|
|
154
|
+
);
|
|
155
|
+
} else {
|
|
156
|
+
console.log(
|
|
157
|
+
`[NetlifyScheduler] No queue provider — logging payload for: ${schedule.scheduleName}`,
|
|
158
|
+
JSON.stringify(schedule.payload)
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
await this.repository.delete(schedule.scheduleName);
|
|
163
|
+
processed++;
|
|
164
|
+
} catch (error) {
|
|
165
|
+
console.error(
|
|
166
|
+
`[NetlifyScheduler] Error processing schedule ${schedule.scheduleName}:`,
|
|
167
|
+
error
|
|
168
|
+
);
|
|
169
|
+
// Mark as FAILED so it won't be re-dispatched automatically.
|
|
170
|
+
// Operators can inspect FAILED schedules and retry manually.
|
|
171
|
+
await this.repository
|
|
172
|
+
.save({ ...schedule, state: 'FAILED' })
|
|
173
|
+
.catch(() => {});
|
|
174
|
+
errors++;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
console.log(
|
|
179
|
+
`[NetlifyScheduler] Processed ${processed} due schedules, ${errors} errors`
|
|
180
|
+
);
|
|
181
|
+
return { processed, errors };
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
module.exports = { NetlifySchedulerAdapter };
|
|
@@ -4,70 +4,77 @@
|
|
|
4
4
|
* Creates scheduler service instances based on configuration.
|
|
5
5
|
* Returns implementations of SchedulerServiceInterface.
|
|
6
6
|
*
|
|
7
|
+
* Uses the provider plugin system (resolveProvider) to load the correct
|
|
8
|
+
* scheduler adapter for the active platform (AWS, Netlify, etc.).
|
|
9
|
+
*
|
|
7
10
|
* Environment Detection:
|
|
8
|
-
* - SCHEDULER_PROVIDER=eventbridge -> Use AWS EventBridge Scheduler
|
|
9
11
|
* - SCHEDULER_PROVIDER=mock -> Use in-memory mock scheduler
|
|
10
12
|
* - Default in dev/test/local stages -> Mock scheduler
|
|
11
|
-
* -
|
|
13
|
+
* - Otherwise -> Resolved provider's SchedulerAdapter
|
|
12
14
|
*/
|
|
13
15
|
|
|
14
|
-
const {
|
|
15
|
-
const { MockSchedulerAdapter } = require('./mock-scheduler-adapter');
|
|
16
|
+
const { resolveProvider } = require('../../providers/resolve-provider');
|
|
16
17
|
|
|
17
18
|
const SCHEDULER_PROVIDERS = {
|
|
18
|
-
EVENTBRIDGE: 'eventbridge',
|
|
19
19
|
MOCK: 'mock',
|
|
20
20
|
};
|
|
21
21
|
|
|
22
22
|
const LOCAL_STAGES = ['dev', 'test', 'local'];
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
|
-
* Determine
|
|
25
|
+
* Determine if mock scheduler should be used
|
|
26
26
|
*
|
|
27
|
-
* @
|
|
27
|
+
* @param {string} [explicit] - Explicit provider override
|
|
28
|
+
* @returns {boolean}
|
|
28
29
|
*/
|
|
29
|
-
function
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
return explicitProvider;
|
|
30
|
+
function shouldUseMock(explicit) {
|
|
31
|
+
if (explicit === SCHEDULER_PROVIDERS.MOCK) {
|
|
32
|
+
return true;
|
|
33
33
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
return SCHEDULER_PROVIDERS.MOCK;
|
|
34
|
+
if (!explicit) {
|
|
35
|
+
const stage = process.env.STAGE || 'dev';
|
|
36
|
+
return LOCAL_STAGES.includes(stage);
|
|
38
37
|
}
|
|
39
|
-
|
|
40
|
-
return SCHEDULER_PROVIDERS.EVENTBRIDGE;
|
|
38
|
+
return false;
|
|
41
39
|
}
|
|
42
40
|
|
|
43
41
|
/**
|
|
44
42
|
* Create a scheduler service instance
|
|
45
43
|
*
|
|
46
44
|
* @param {Object} options
|
|
47
|
-
* @param {string} options.provider - Scheduler provider ('
|
|
45
|
+
* @param {string} options.provider - Scheduler provider ('mock', or omit for auto-detect)
|
|
48
46
|
* @param {string} options.region - AWS region (for EventBridge)
|
|
49
47
|
* @param {boolean} options.verbose - Verbose logging (for Mock)
|
|
48
|
+
* @param {Object} options.repository - Schedule repository (for Netlify - persists schedules)
|
|
49
|
+
* @param {Object} options.queueProvider - Queue provider (for Netlify - dispatches due jobs)
|
|
50
50
|
* @returns {SchedulerServiceInterface} Implementation of scheduler interface
|
|
51
51
|
*/
|
|
52
52
|
function createSchedulerService(options = {}) {
|
|
53
|
-
const
|
|
53
|
+
const explicit = options.provider || process.env.SCHEDULER_PROVIDER;
|
|
54
|
+
|
|
55
|
+
if (shouldUseMock(explicit)) {
|
|
56
|
+
const { MockSchedulerAdapter } = require('./mock-scheduler-adapter');
|
|
57
|
+
return new MockSchedulerAdapter({ verbose: options.verbose });
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Use the resolved provider's scheduler adapter.
|
|
61
|
+
// Legacy SCHEDULER_PROVIDER values (eventbridge, netlify) are mapped
|
|
62
|
+
// to provider names for backward compatibility.
|
|
63
|
+
const LEGACY_MAP = { eventbridge: 'aws', netlify: 'netlify' };
|
|
64
|
+
const providerName = LEGACY_MAP[explicit] || explicit;
|
|
65
|
+
const providerOverride = providerName ? { provider: providerName } : {};
|
|
66
|
+
const provider = resolveProvider(null, providerOverride);
|
|
54
67
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
case SCHEDULER_PROVIDERS.MOCK:
|
|
61
|
-
return new MockSchedulerAdapter({
|
|
62
|
-
verbose: options.verbose,
|
|
63
|
-
});
|
|
64
|
-
default:
|
|
65
|
-
throw new Error(`Unknown scheduler provider: ${provider}`);
|
|
68
|
+
const Adapter = provider.SchedulerAdapter;
|
|
69
|
+
if (!Adapter) {
|
|
70
|
+
throw new Error(
|
|
71
|
+
`Provider '${provider.name}' does not export a SchedulerAdapter`
|
|
72
|
+
);
|
|
66
73
|
}
|
|
74
|
+
return new Adapter(options);
|
|
67
75
|
}
|
|
68
76
|
|
|
69
77
|
module.exports = {
|
|
70
78
|
createSchedulerService,
|
|
71
79
|
SCHEDULER_PROVIDERS,
|
|
72
|
-
determineProvider,
|
|
73
80
|
};
|