@friggframework/core 2.0.0--canary.548.c8ae0ca.0 → 2.0.0--canary.545.c40eca4.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 +474 -0
- package/handlers/routers/auth.js +51 -9
- 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 +84 -283
- package/handlers/routers/integration-defined-routers.js +44 -33
- package/handlers/routers/integration-webhook-routers.js +73 -52
- 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/user.js +30 -14
- package/handlers/routers/websocket.js +18 -4
- package/handlers/use-cases/check-integrations-health-use-case.js +13 -6
- package/handlers/workers/db-migration.js +74 -35
- package/handlers/workers/integration-defined-workers.js +31 -20
- 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
package/package.json
CHANGED
|
@@ -1,12 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@friggframework/core",
|
|
3
3
|
"prettier": "@friggframework/prettier-config",
|
|
4
|
-
"version": "2.0.0--canary.
|
|
4
|
+
"version": "2.0.0--canary.545.c40eca4.0",
|
|
5
5
|
"dependencies": {
|
|
6
|
-
"@aws-sdk/client-apigatewaymanagementapi": "^3.588.0",
|
|
7
|
-
"@aws-sdk/client-kms": "^3.588.0",
|
|
8
|
-
"@aws-sdk/client-lambda": "^3.714.0",
|
|
9
|
-
"@aws-sdk/client-sqs": "^3.588.0",
|
|
10
6
|
"@hapi/boom": "^10.0.1",
|
|
11
7
|
"bcryptjs": "^2.4.3",
|
|
12
8
|
"body-parser": "^1.20.2",
|
|
@@ -19,6 +15,7 @@
|
|
|
19
15
|
"express-async-handler": "^1.2.0",
|
|
20
16
|
"form-data": "^4.0.0",
|
|
21
17
|
"fs-extra": "^11.2.0",
|
|
18
|
+
"js-yaml": "^4.1.0",
|
|
22
19
|
"lodash": "4.17.21",
|
|
23
20
|
"lodash.get": "^4.4.2",
|
|
24
21
|
"node-fetch": "^2.6.7",
|
|
@@ -38,9 +35,9 @@
|
|
|
38
35
|
}
|
|
39
36
|
},
|
|
40
37
|
"devDependencies": {
|
|
41
|
-
"@friggframework/eslint-config": "2.0.0--canary.
|
|
42
|
-
"@friggframework/prettier-config": "2.0.0--canary.
|
|
43
|
-
"@friggframework/test": "2.0.0--canary.
|
|
38
|
+
"@friggframework/eslint-config": "2.0.0--canary.545.c40eca4.0",
|
|
39
|
+
"@friggframework/prettier-config": "2.0.0--canary.545.c40eca4.0",
|
|
40
|
+
"@friggframework/test": "2.0.0--canary.545.c40eca4.0",
|
|
44
41
|
"@prisma/client": "^6.17.0",
|
|
45
42
|
"@types/lodash": "4.17.15",
|
|
46
43
|
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
|
@@ -53,6 +50,7 @@
|
|
|
53
50
|
"prettier": "^2.7.1",
|
|
54
51
|
"prisma": "^6.17.0",
|
|
55
52
|
"sinon": "^16.1.1",
|
|
53
|
+
"supertest": "^7.1.4",
|
|
56
54
|
"typescript": "^5.0.2"
|
|
57
55
|
},
|
|
58
56
|
"scripts": {
|
|
@@ -80,5 +78,5 @@
|
|
|
80
78
|
"publishConfig": {
|
|
81
79
|
"access": "public"
|
|
82
80
|
},
|
|
83
|
-
"gitHead": "
|
|
81
|
+
"gitHead": "c40eca49e72472f0499b24ce709a655a7503727b"
|
|
84
82
|
}
|
|
@@ -360,3 +360,90 @@ model WebsocketConnection {
|
|
|
360
360
|
@@index([connectionId])
|
|
361
361
|
@@map("WebsocketConnection")
|
|
362
362
|
}
|
|
363
|
+
|
|
364
|
+
// ============================================================================
|
|
365
|
+
// ADMIN PROCESS MODELS
|
|
366
|
+
// ============================================================================
|
|
367
|
+
|
|
368
|
+
/// Admin process state machine
|
|
369
|
+
enum AdminProcessState {
|
|
370
|
+
PENDING
|
|
371
|
+
RUNNING
|
|
372
|
+
COMPLETED
|
|
373
|
+
FAILED
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/// Admin trigger types
|
|
377
|
+
enum AdminTrigger {
|
|
378
|
+
MANUAL
|
|
379
|
+
SCHEDULED
|
|
380
|
+
QUEUE
|
|
381
|
+
WEBHOOK
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/// Admin process tracking (like Process but without user/integration FK)
|
|
385
|
+
/// Used for: admin scripts, db migrations, system maintenance tasks
|
|
386
|
+
model AdminProcess {
|
|
387
|
+
id String @id @default(auto()) @map("_id") @db.ObjectId
|
|
388
|
+
name String // e.g., "oauth-token-refresh", "db-migration-xyz"
|
|
389
|
+
type String // e.g., "ADMIN_SCRIPT", "DB_MIGRATION"
|
|
390
|
+
|
|
391
|
+
// State machine
|
|
392
|
+
state AdminProcessState @default(PENDING)
|
|
393
|
+
|
|
394
|
+
// Flexible storage (mirrors Process model pattern)
|
|
395
|
+
context Json @default("{}") // input, trigger, audit info, script version
|
|
396
|
+
results Json @default("{}") // output, logs, metrics, errors
|
|
397
|
+
|
|
398
|
+
// Hierarchy support
|
|
399
|
+
childProcesses String[] @db.ObjectId
|
|
400
|
+
parentProcessId String? @db.ObjectId
|
|
401
|
+
|
|
402
|
+
// Timestamps
|
|
403
|
+
createdAt DateTime @default(now())
|
|
404
|
+
updatedAt DateTime @updatedAt
|
|
405
|
+
|
|
406
|
+
@@index([name, createdAt(sort: Desc)])
|
|
407
|
+
@@index([state])
|
|
408
|
+
@@index([type])
|
|
409
|
+
@@map("AdminProcess")
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/// Script scheduling configuration for hybrid scheduling (SQS + EventBridge)
|
|
413
|
+
model ScriptSchedule {
|
|
414
|
+
id String @id @default(auto()) @map("_id") @db.ObjectId
|
|
415
|
+
scriptName String @unique
|
|
416
|
+
enabled Boolean @default(false)
|
|
417
|
+
cronExpression String?
|
|
418
|
+
timezone String @default("UTC")
|
|
419
|
+
lastTriggeredAt DateTime?
|
|
420
|
+
nextTriggerAt DateTime?
|
|
421
|
+
|
|
422
|
+
// External Scheduler (e.g., AWS EventBridge Scheduler)
|
|
423
|
+
externalScheduleId String?
|
|
424
|
+
externalScheduleName String?
|
|
425
|
+
|
|
426
|
+
createdAt DateTime @default(now())
|
|
427
|
+
updatedAt DateTime @updatedAt
|
|
428
|
+
|
|
429
|
+
@@index([enabled])
|
|
430
|
+
@@map("ScriptSchedule")
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/// One-time scheduled jobs for poll-and-dispatch scheduling pattern.
|
|
434
|
+
/// Used by providers without native one-time scheduling APIs (e.g., Netlify).
|
|
435
|
+
/// A cron function queries for due jobs and dispatches them to a queue.
|
|
436
|
+
model ScheduledJob {
|
|
437
|
+
id String @id @default(auto()) @map("_id") @db.ObjectId
|
|
438
|
+
scheduleName String @unique
|
|
439
|
+
scheduledAt DateTime
|
|
440
|
+
queueResourceId String
|
|
441
|
+
payload Json?
|
|
442
|
+
state String @default("PENDING")
|
|
443
|
+
|
|
444
|
+
createdAt DateTime @default(now())
|
|
445
|
+
updatedAt DateTime @updatedAt
|
|
446
|
+
|
|
447
|
+
@@index([state, scheduledAt])
|
|
448
|
+
@@map("ScheduledJob")
|
|
449
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
-- CreateEnum
|
|
2
|
+
CREATE TYPE "AdminProcessState" AS ENUM ('PENDING', 'RUNNING', 'COMPLETED', 'FAILED');
|
|
3
|
+
|
|
4
|
+
-- CreateEnum
|
|
5
|
+
CREATE TYPE "AdminTrigger" AS ENUM ('MANUAL', 'SCHEDULED', 'QUEUE', 'WEBHOOK');
|
|
6
|
+
|
|
7
|
+
-- CreateTable
|
|
8
|
+
CREATE TABLE "AdminProcess" (
|
|
9
|
+
"id" SERIAL NOT NULL,
|
|
10
|
+
"name" TEXT NOT NULL,
|
|
11
|
+
"type" TEXT NOT NULL,
|
|
12
|
+
"state" "AdminProcessState" NOT NULL DEFAULT 'PENDING',
|
|
13
|
+
"context" JSONB NOT NULL DEFAULT '{}',
|
|
14
|
+
"results" JSONB NOT NULL DEFAULT '{}',
|
|
15
|
+
"parentProcessId" INTEGER,
|
|
16
|
+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
17
|
+
"updatedAt" TIMESTAMP(3) NOT NULL,
|
|
18
|
+
|
|
19
|
+
CONSTRAINT "AdminProcess_pkey" PRIMARY KEY ("id")
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
-- CreateTable
|
|
23
|
+
CREATE TABLE "ScriptSchedule" (
|
|
24
|
+
"id" SERIAL NOT NULL,
|
|
25
|
+
"scriptName" TEXT NOT NULL,
|
|
26
|
+
"enabled" BOOLEAN NOT NULL DEFAULT false,
|
|
27
|
+
"cronExpression" TEXT,
|
|
28
|
+
"timezone" TEXT NOT NULL DEFAULT 'UTC',
|
|
29
|
+
"lastTriggeredAt" TIMESTAMP(3),
|
|
30
|
+
"nextTriggerAt" TIMESTAMP(3),
|
|
31
|
+
"externalScheduleId" TEXT,
|
|
32
|
+
"externalScheduleName" TEXT,
|
|
33
|
+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
34
|
+
"updatedAt" TIMESTAMP(3) NOT NULL,
|
|
35
|
+
|
|
36
|
+
CONSTRAINT "ScriptSchedule_pkey" PRIMARY KEY ("id")
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
-- CreateIndex
|
|
40
|
+
CREATE INDEX "AdminProcess_name_createdAt_idx" ON "AdminProcess"("name", "createdAt" DESC);
|
|
41
|
+
|
|
42
|
+
-- CreateIndex
|
|
43
|
+
CREATE INDEX "AdminProcess_state_idx" ON "AdminProcess"("state");
|
|
44
|
+
|
|
45
|
+
-- CreateIndex
|
|
46
|
+
CREATE INDEX "AdminProcess_type_idx" ON "AdminProcess"("type");
|
|
47
|
+
|
|
48
|
+
-- CreateIndex
|
|
49
|
+
CREATE UNIQUE INDEX "ScriptSchedule_scriptName_key" ON "ScriptSchedule"("scriptName");
|
|
50
|
+
|
|
51
|
+
-- CreateIndex
|
|
52
|
+
CREATE INDEX "ScriptSchedule_enabled_idx" ON "ScriptSchedule"("enabled");
|
|
53
|
+
|
|
54
|
+
-- AddForeignKey
|
|
55
|
+
ALTER TABLE "AdminProcess" ADD CONSTRAINT "AdminProcess_parentProcessId_fkey" FOREIGN KEY ("parentProcessId") REFERENCES "AdminProcess"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
|
@@ -343,3 +343,88 @@ model WebsocketConnection {
|
|
|
343
343
|
|
|
344
344
|
@@index([connectionId])
|
|
345
345
|
}
|
|
346
|
+
|
|
347
|
+
// ============================================================================
|
|
348
|
+
// ADMIN PROCESS MODELS
|
|
349
|
+
// ============================================================================
|
|
350
|
+
|
|
351
|
+
/// Admin process state machine
|
|
352
|
+
enum AdminProcessState {
|
|
353
|
+
PENDING
|
|
354
|
+
RUNNING
|
|
355
|
+
COMPLETED
|
|
356
|
+
FAILED
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/// Admin trigger types
|
|
360
|
+
enum AdminTrigger {
|
|
361
|
+
MANUAL
|
|
362
|
+
SCHEDULED
|
|
363
|
+
QUEUE
|
|
364
|
+
WEBHOOK
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/// Admin process tracking (like Process but without user/integration FK)
|
|
368
|
+
/// Used for: admin scripts, db migrations, system maintenance tasks
|
|
369
|
+
model AdminProcess {
|
|
370
|
+
id Int @id @default(autoincrement())
|
|
371
|
+
name String // e.g., "oauth-token-refresh", "db-migration-xyz"
|
|
372
|
+
type String // e.g., "ADMIN_SCRIPT", "DB_MIGRATION"
|
|
373
|
+
|
|
374
|
+
// State machine
|
|
375
|
+
state AdminProcessState @default(PENDING)
|
|
376
|
+
|
|
377
|
+
// Flexible storage (mirrors Process model pattern)
|
|
378
|
+
context Json @default("{}") // input, trigger, audit info, script version
|
|
379
|
+
results Json @default("{}") // output, logs, metrics, errors
|
|
380
|
+
|
|
381
|
+
// Hierarchy support - self-referential relation
|
|
382
|
+
parentProcessId Int?
|
|
383
|
+
parentProcess AdminProcess? @relation("AdminProcessHierarchy", fields: [parentProcessId], references: [id], onDelete: SetNull)
|
|
384
|
+
childProcesses AdminProcess[] @relation("AdminProcessHierarchy")
|
|
385
|
+
|
|
386
|
+
// Timestamps
|
|
387
|
+
createdAt DateTime @default(now())
|
|
388
|
+
updatedAt DateTime @updatedAt
|
|
389
|
+
|
|
390
|
+
@@index([name, createdAt(sort: Desc)])
|
|
391
|
+
@@index([state])
|
|
392
|
+
@@index([type])
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/// Script scheduling configuration for hybrid scheduling (SQS + EventBridge)
|
|
396
|
+
model ScriptSchedule {
|
|
397
|
+
id Int @id @default(autoincrement())
|
|
398
|
+
scriptName String @unique
|
|
399
|
+
enabled Boolean @default(false)
|
|
400
|
+
cronExpression String?
|
|
401
|
+
timezone String @default("UTC")
|
|
402
|
+
lastTriggeredAt DateTime?
|
|
403
|
+
nextTriggerAt DateTime?
|
|
404
|
+
|
|
405
|
+
// External Scheduler (e.g., AWS EventBridge Scheduler)
|
|
406
|
+
externalScheduleId String?
|
|
407
|
+
externalScheduleName String?
|
|
408
|
+
|
|
409
|
+
createdAt DateTime @default(now())
|
|
410
|
+
updatedAt DateTime @updatedAt
|
|
411
|
+
|
|
412
|
+
@@index([enabled])
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/// One-time scheduled jobs for poll-and-dispatch scheduling pattern.
|
|
416
|
+
/// Used by providers without native one-time scheduling APIs (e.g., Netlify).
|
|
417
|
+
/// A cron function queries for due jobs and dispatches them to a queue.
|
|
418
|
+
model ScheduledJob {
|
|
419
|
+
id Int @id @default(autoincrement())
|
|
420
|
+
scheduleName String @unique
|
|
421
|
+
scheduledAt DateTime
|
|
422
|
+
queueResourceId String
|
|
423
|
+
payload Json?
|
|
424
|
+
state String @default("PENDING")
|
|
425
|
+
|
|
426
|
+
createdAt DateTime @default(now())
|
|
427
|
+
updatedAt DateTime @updatedAt
|
|
428
|
+
|
|
429
|
+
@@index([state, scheduledAt])
|
|
430
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider Resolver
|
|
3
|
+
*
|
|
4
|
+
* Resolves a provider name from the appDefinition into the corresponding
|
|
5
|
+
* @friggframework/provider-{name} package. This is the bridge between
|
|
6
|
+
* `appDefinition.provider` and the provider plugin interface.
|
|
7
|
+
*
|
|
8
|
+
* Resolution order:
|
|
9
|
+
* 1. Explicit `providerName` argument
|
|
10
|
+
* 2. `appDefinition.provider`
|
|
11
|
+
* 3. `FRIGG_PROVIDER` environment variable
|
|
12
|
+
* 4. Default: 'aws'
|
|
13
|
+
*
|
|
14
|
+
* Package naming convention:
|
|
15
|
+
* provider: 'netlify' → require('@friggframework/provider-netlify')
|
|
16
|
+
* provider: 'aws' → require('@friggframework/provider-aws')
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* const provider = resolveProvider({ provider: 'netlify' });
|
|
20
|
+
* // provider.name === 'netlify'
|
|
21
|
+
* // provider.deploy(appDefinition, options)
|
|
22
|
+
* // provider.createHandler(options)
|
|
23
|
+
* // provider.QueueProvider (class)
|
|
24
|
+
* // etc.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
const KNOWN_PROVIDERS = ['aws', 'netlify'];
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Determine the provider name from available sources.
|
|
31
|
+
*
|
|
32
|
+
* @param {Object} [appDefinition] - Frigg app definition
|
|
33
|
+
* @param {string} [providerName] - Explicit override
|
|
34
|
+
* @returns {string} Provider name (e.g. 'netlify', 'aws')
|
|
35
|
+
*/
|
|
36
|
+
function determineProviderName(appDefinition, providerName) {
|
|
37
|
+
if (providerName) {
|
|
38
|
+
return providerName;
|
|
39
|
+
}
|
|
40
|
+
if (appDefinition?.provider) {
|
|
41
|
+
return appDefinition.provider;
|
|
42
|
+
}
|
|
43
|
+
if (process.env.FRIGG_PROVIDER) {
|
|
44
|
+
return process.env.FRIGG_PROVIDER;
|
|
45
|
+
}
|
|
46
|
+
return 'aws';
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Resolve a provider name to a package name.
|
|
51
|
+
*
|
|
52
|
+
* @param {string} name - Provider name (e.g. 'netlify')
|
|
53
|
+
* @returns {string} npm package name (e.g. '@friggframework/provider-netlify')
|
|
54
|
+
*/
|
|
55
|
+
function providerPackageName(name) {
|
|
56
|
+
return `@friggframework/provider-${name}`;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Resolve and load the provider plugin for the given app definition.
|
|
61
|
+
*
|
|
62
|
+
* @param {Object} [appDefinition] - Frigg app definition (reads .provider)
|
|
63
|
+
* @param {Object} [options]
|
|
64
|
+
* @param {string} [options.provider] - Explicit provider name override
|
|
65
|
+
* @returns {Object} Provider plugin (conforms to the provider plugin interface)
|
|
66
|
+
* @throws {Error} If the provider package is not installed
|
|
67
|
+
*/
|
|
68
|
+
function resolveProvider(appDefinition, options = {}) {
|
|
69
|
+
const name = determineProviderName(appDefinition, options.provider);
|
|
70
|
+
const packageName = providerPackageName(name);
|
|
71
|
+
|
|
72
|
+
try {
|
|
73
|
+
return require(packageName);
|
|
74
|
+
} catch (error) {
|
|
75
|
+
if (error.code === 'MODULE_NOT_FOUND') {
|
|
76
|
+
const hint = KNOWN_PROVIDERS.includes(name)
|
|
77
|
+
? `Install it with: npm install ${packageName}`
|
|
78
|
+
: `Is '${name}' a valid Frigg provider? Known providers: ${KNOWN_PROVIDERS.join(', ')}`;
|
|
79
|
+
|
|
80
|
+
throw new Error(
|
|
81
|
+
`Provider '${name}' specified in appDefinition but package '${packageName}' is not installed.\n ${hint}`
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
throw error;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
module.exports = {
|
|
89
|
+
resolveProvider,
|
|
90
|
+
determineProviderName,
|
|
91
|
+
providerPackageName,
|
|
92
|
+
KNOWN_PROVIDERS,
|
|
93
|
+
};
|
package/queues/index.js
CHANGED
|
@@ -1,4 +1,23 @@
|
|
|
1
1
|
const { QueuerUtil } = require('./queuer-util');
|
|
2
|
+
const { QueueProvider } = require('./queue-provider');
|
|
3
|
+
const { QueueClientInterface } = require('./queue-client-interface');
|
|
4
|
+
const {
|
|
5
|
+
createQueueProvider,
|
|
6
|
+
determineProvider,
|
|
7
|
+
QUEUE_PROVIDERS,
|
|
8
|
+
} = require('./queue-provider-factory');
|
|
9
|
+
const {
|
|
10
|
+
NetlifyBackgroundProvider,
|
|
11
|
+
QStashQueueProvider,
|
|
12
|
+
} = require('./providers');
|
|
13
|
+
|
|
2
14
|
module.exports = {
|
|
3
15
|
QueuerUtil,
|
|
16
|
+
QueueProvider,
|
|
17
|
+
QueueClientInterface,
|
|
18
|
+
createQueueProvider,
|
|
19
|
+
determineProvider,
|
|
20
|
+
QUEUE_PROVIDERS,
|
|
21
|
+
NetlifyBackgroundProvider,
|
|
22
|
+
QStashQueueProvider,
|
|
4
23
|
};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Netlify Background Functions Queue Provider (Adapter)
|
|
3
|
+
*
|
|
4
|
+
* Uses Netlify Background Functions for async job processing.
|
|
5
|
+
* Background functions have a 15-minute execution limit and are triggered via HTTP POST.
|
|
6
|
+
* They return 202 immediately while processing continues.
|
|
7
|
+
*
|
|
8
|
+
* Queue ID format: The function name or URL path for the background function.
|
|
9
|
+
* Convention: function files named with '-background' suffix (e.g., 'worker-background.js')
|
|
10
|
+
*
|
|
11
|
+
* Limitations:
|
|
12
|
+
* - No built-in retry (must implement manually)
|
|
13
|
+
* - No dead-letter queue
|
|
14
|
+
* - No FIFO ordering guarantees
|
|
15
|
+
* - 15-minute max execution time
|
|
16
|
+
*/
|
|
17
|
+
const { QueueProvider } = require('../queue-provider');
|
|
18
|
+
|
|
19
|
+
class NetlifyBackgroundProvider extends QueueProvider {
|
|
20
|
+
constructor(options = {}) {
|
|
21
|
+
super();
|
|
22
|
+
// Base URL for triggering background functions (e.g., https://mysite.netlify.app)
|
|
23
|
+
this.baseUrl = options.baseUrl || process.env.URL || '';
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Send a message by invoking a Netlify Background Function via HTTP POST.
|
|
28
|
+
*
|
|
29
|
+
* @param {Object} message - Message payload
|
|
30
|
+
* @param {string} queueId - Background function path (e.g., '/.netlify/functions/worker-background')
|
|
31
|
+
*/
|
|
32
|
+
async send(message, queueId) {
|
|
33
|
+
const url = `${this.baseUrl}${queueId}`;
|
|
34
|
+
const response = await fetch(url, {
|
|
35
|
+
method: 'POST',
|
|
36
|
+
headers: { 'Content-Type': 'application/json' },
|
|
37
|
+
body: JSON.stringify(message),
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
if (!response.ok && response.status !== 202) {
|
|
41
|
+
throw new Error(
|
|
42
|
+
`Failed to invoke background function at ${queueId}: ${response.status} ${response.statusText}`
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return { status: response.status, queueId };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Send batch of messages sequentially (background functions are HTTP-triggered).
|
|
51
|
+
*/
|
|
52
|
+
async batchSend(entries = [], queueId) {
|
|
53
|
+
const results = [];
|
|
54
|
+
for (const entry of entries) {
|
|
55
|
+
const result = await this.send(entry, queueId);
|
|
56
|
+
results.push(result);
|
|
57
|
+
}
|
|
58
|
+
return { results };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Parse Netlify Background Function event.
|
|
63
|
+
* Background functions receive the raw HTTP event body as the event.
|
|
64
|
+
*/
|
|
65
|
+
parseEvent(event) {
|
|
66
|
+
const body =
|
|
67
|
+
typeof event.body === 'string'
|
|
68
|
+
? JSON.parse(event.body)
|
|
69
|
+
: event.body;
|
|
70
|
+
|
|
71
|
+
// Background functions receive a single message per invocation
|
|
72
|
+
return [body];
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
module.exports = { NetlifyBackgroundProvider };
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QStash Queue Provider (Adapter)
|
|
3
|
+
*
|
|
4
|
+
* Upstash QStash implementation - platform-agnostic HTTP-based message queue.
|
|
5
|
+
* Works on any platform (Netlify, Vercel, AWS, bare metal, etc.)
|
|
6
|
+
*
|
|
7
|
+
* Features:
|
|
8
|
+
* - Configurable retry with exponential backoff
|
|
9
|
+
* - Dead-letter queue via callbacks
|
|
10
|
+
* - At-least-once delivery
|
|
11
|
+
* - Scheduling support (delay, cron)
|
|
12
|
+
* - Message deduplication
|
|
13
|
+
*
|
|
14
|
+
* Requires:
|
|
15
|
+
* - QSTASH_TOKEN env var (from Upstash console)
|
|
16
|
+
* - QSTASH_CURRENT_SIGNING_KEY and QSTASH_NEXT_SIGNING_KEY for verification
|
|
17
|
+
*
|
|
18
|
+
* Queue ID format: The destination URL that QStash will POST to when delivering the message.
|
|
19
|
+
*/
|
|
20
|
+
const { QueueProvider } = require('../queue-provider');
|
|
21
|
+
|
|
22
|
+
const QSTASH_API_BASE = 'https://qstash.upstash.io/v2';
|
|
23
|
+
|
|
24
|
+
class QStashQueueProvider extends QueueProvider {
|
|
25
|
+
constructor(options = {}) {
|
|
26
|
+
super();
|
|
27
|
+
this.token =
|
|
28
|
+
options.token || process.env.QSTASH_TOKEN;
|
|
29
|
+
this.retries =
|
|
30
|
+
options.retries !== undefined ? options.retries : 3;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Send a message via QStash.
|
|
35
|
+
*
|
|
36
|
+
* @param {Object} message - Message payload
|
|
37
|
+
* @param {string} queueId - Destination URL that QStash will POST to
|
|
38
|
+
*/
|
|
39
|
+
async send(message, queueId) {
|
|
40
|
+
if (!this.token) {
|
|
41
|
+
throw new Error(
|
|
42
|
+
'QSTASH_TOKEN is required. Get one at https://console.upstash.com'
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const headers = {
|
|
47
|
+
Authorization: `Bearer ${this.token}`,
|
|
48
|
+
'Content-Type': 'application/json',
|
|
49
|
+
'Upstash-Retries': String(this.retries),
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const response = await fetch(
|
|
53
|
+
`${QSTASH_API_BASE}/publish/${queueId}`,
|
|
54
|
+
{
|
|
55
|
+
method: 'POST',
|
|
56
|
+
headers,
|
|
57
|
+
body: JSON.stringify(message),
|
|
58
|
+
}
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
if (!response.ok) {
|
|
62
|
+
const errorBody = await response.text().catch(() => 'unknown');
|
|
63
|
+
throw new Error(
|
|
64
|
+
`QStash publish failed: ${response.status} ${response.statusText} - ${errorBody}`
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return response.json();
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Send batch of messages via QStash batch API.
|
|
73
|
+
*/
|
|
74
|
+
async batchSend(entries = [], queueId) {
|
|
75
|
+
if (!this.token) {
|
|
76
|
+
throw new Error(
|
|
77
|
+
'QSTASH_TOKEN is required. Get one at https://console.upstash.com'
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const messages = entries.map((entry) => ({
|
|
82
|
+
destination: queueId,
|
|
83
|
+
body: JSON.stringify(entry),
|
|
84
|
+
headers: { 'Content-Type': 'application/json' },
|
|
85
|
+
retries: this.retries,
|
|
86
|
+
}));
|
|
87
|
+
|
|
88
|
+
const response = await fetch(`${QSTASH_API_BASE}/batch`, {
|
|
89
|
+
method: 'POST',
|
|
90
|
+
headers: {
|
|
91
|
+
Authorization: `Bearer ${this.token}`,
|
|
92
|
+
'Content-Type': 'application/json',
|
|
93
|
+
},
|
|
94
|
+
body: JSON.stringify(messages),
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
if (!response.ok) {
|
|
98
|
+
const errorBody = await response.text().catch(() => 'unknown');
|
|
99
|
+
throw new Error(
|
|
100
|
+
`QStash batch publish failed: ${response.status} - ${errorBody}`
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return response.json();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Parse QStash webhook delivery event.
|
|
109
|
+
* QStash POSTs the message body directly to the destination URL.
|
|
110
|
+
*/
|
|
111
|
+
parseEvent(event) {
|
|
112
|
+
const body =
|
|
113
|
+
typeof event.body === 'string'
|
|
114
|
+
? JSON.parse(event.body)
|
|
115
|
+
: event.body;
|
|
116
|
+
|
|
117
|
+
// QStash delivers a single message per invocation
|
|
118
|
+
return [body];
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
module.exports = { QStashQueueProvider };
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Queue Client Interface (Port)
|
|
3
|
+
*
|
|
4
|
+
* Defines the contract for low-level queue message operations.
|
|
5
|
+
* Used by Worker class and QueuerUtil for sending/receiving queue messages.
|
|
6
|
+
*
|
|
7
|
+
* Following Frigg's hexagonal architecture pattern:
|
|
8
|
+
* - Port defines WHAT the service does (contract)
|
|
9
|
+
* - Adapters implement HOW (AWS SQS, Netlify, QStash, etc.)
|
|
10
|
+
*
|
|
11
|
+
* Distinct from QueueProvider which is the higher-level provider for
|
|
12
|
+
* integration-level queue operations (send/batchSend/parseEvent).
|
|
13
|
+
* QueueClientInterface is the lower-level "how do I talk to the queue service" port.
|
|
14
|
+
*/
|
|
15
|
+
class QueueClientInterface {
|
|
16
|
+
/**
|
|
17
|
+
* Send a single message to a queue
|
|
18
|
+
*
|
|
19
|
+
* @param {Object} params
|
|
20
|
+
* @param {string} params.QueueUrl - Queue endpoint/identifier
|
|
21
|
+
* @param {string} params.MessageBody - Serialized message body
|
|
22
|
+
* @param {number} [params.DelaySeconds] - Delay before message becomes visible
|
|
23
|
+
* @returns {Promise<{MessageId: string}>} Result with message identifier
|
|
24
|
+
*/
|
|
25
|
+
async sendMessage(params) {
|
|
26
|
+
throw new Error('Method sendMessage must be implemented by subclass');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Send a batch of messages to a queue
|
|
31
|
+
*
|
|
32
|
+
* @param {Object} params
|
|
33
|
+
* @param {string} params.QueueUrl - Queue endpoint/identifier
|
|
34
|
+
* @param {Array<{Id: string, MessageBody: string}>} params.Entries - Messages to send
|
|
35
|
+
* @returns {Promise<Object>} Batch send result
|
|
36
|
+
*/
|
|
37
|
+
async sendMessageBatch(params) {
|
|
38
|
+
throw new Error(
|
|
39
|
+
'Method sendMessageBatch must be implemented by subclass'
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Resolve a queue name to a URL/endpoint
|
|
45
|
+
*
|
|
46
|
+
* @param {Object} params
|
|
47
|
+
* @param {string} params.QueueName - Queue name to resolve
|
|
48
|
+
* @returns {Promise<string>} Resolved queue URL/endpoint
|
|
49
|
+
*/
|
|
50
|
+
async getQueueUrl(params) {
|
|
51
|
+
throw new Error('Method getQueueUrl must be implemented by subclass');
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
module.exports = { QueueClientInterface };
|