@friggframework/core 2.0.0--canary.522.cbd3d5a.0 → 2.0.0--canary.517.35ee143.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 +67 -66
- package/CLAUDE.md +124 -148
- package/README.md +130 -165
- package/admin-scripts/index.js +16 -30
- package/admin-scripts/repositories/{admin-api-key-repository-documentdb.js → admin-process-repository-documentdb.js} +5 -5
- package/admin-scripts/repositories/{admin-api-key-repository-factory.js → admin-process-repository-factory.js} +15 -19
- 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-schedule-repository-factory.js +2 -6
- package/admin-scripts/repositories/script-schedule-repository-interface.js +14 -36
- package/admin-scripts/repositories/script-schedule-repository-mongo.js +13 -25
- package/admin-scripts/repositories/script-schedule-repository-postgres.js +13 -25
- package/application/commands/admin-script-commands.js +102 -266
- package/application/commands/integration-commands.js +6 -12
- package/application/index.js +3 -1
- package/assertions/get.js +4 -1
- package/associations/association.js +57 -59
- package/associations/model.js +41 -41
- package/core/CLAUDE.md +156 -222
- package/core/Worker.js +1 -5
- package/credential/repositories/credential-repository-documentdb.js +1 -71
- package/credential/repositories/credential-repository-interface.js +2 -8
- package/credential/repositories/credential-repository-mongo.js +4 -45
- package/credential/repositories/credential-repository-postgres.js +3 -44
- package/credential/repositories/credential-repository.js +4 -6
- package/credential/use-cases/get-credential-for-user.js +3 -21
- package/credential/use-cases/update-authentication-status.js +2 -5
- package/database/MONGODB_TRANSACTION_FIX.md +52 -72
- package/database/adapters/lambda-invoker.js +7 -8
- package/database/config.js +13 -15
- package/database/documentdb-encryption-service.js +13 -29
- package/database/documentdb-utils.js +12 -21
- package/database/encryption/README.md +36 -53
- package/database/encryption/encryption-schema-registry.js +9 -21
- package/database/encryption/field-encryption-service.js +4 -15
- package/database/encryption/logger.js +2 -3
- package/database/encryption/prisma-encryption-extension.js +8 -5
- package/database/index.js +2 -4
- package/database/models/IndividualUser.js +15 -15
- package/database/models/OrganizationUser.js +4 -6
- package/database/models/UserModel.js +2 -2
- package/database/models/WebsocketConnection.js +1 -4
- package/database/mongoose.js +2 -2
- package/database/prisma.js +14 -22
- package/database/repositories/health-check-repository-documentdb.js +7 -22
- package/database/repositories/health-check-repository-factory.js +3 -9
- package/database/repositories/health-check-repository-interface.js +5 -15
- package/database/repositories/health-check-repository-mongodb.js +6 -9
- package/database/repositories/health-check-repository-postgres.js +1 -1
- package/database/repositories/migration-status-repository-s3.js +5 -10
- package/database/use-cases/check-database-health-use-case.js +2 -3
- package/database/use-cases/check-database-state-use-case.js +7 -9
- package/database/use-cases/get-database-state-via-worker-use-case.js +12 -15
- package/database/use-cases/get-migration-status-use-case.js +2 -4
- package/database/use-cases/run-database-migration-use-case.js +9 -41
- package/database/use-cases/test-encryption-use-case.js +5 -4
- package/database/use-cases/trigger-database-migration-use-case.js +8 -9
- package/database/utils/mongodb-collection-utils.js +3 -11
- package/database/utils/mongodb-schema-init.js +4 -10
- package/database/utils/prisma-runner.js +74 -144
- package/database/utils/prisma-schema-parser.js +2 -2
- package/docs/PROCESS_MANAGEMENT_QUEUE_SPEC.md +80 -135
- package/encrypt/Cryptor.js +2 -8
- package/errors/fetch-error.js +1 -1
- package/generated/prisma-mongodb/edge.js +16 -59
- package/generated/prisma-mongodb/index-browser.js +12 -62
- package/generated/prisma-mongodb/index.d.ts +4850 -8136
- package/generated/prisma-mongodb/index.js +16 -59
- package/generated/prisma-mongodb/package.json +1 -1
- package/generated/prisma-mongodb/schema.prisma +33 -75
- package/generated/prisma-mongodb/wasm.js +16 -59
- package/generated/prisma-postgresql/edge.js +15 -59
- package/generated/prisma-postgresql/index-browser.js +11 -62
- package/generated/prisma-postgresql/index.d.ts +5963 -8930
- package/generated/prisma-postgresql/index.js +15 -59
- package/generated/prisma-postgresql/package.json +1 -1
- package/generated/prisma-postgresql/schema.prisma +33 -73
- package/generated/prisma-postgresql/wasm.js +15 -59
- package/handlers/WEBHOOKS.md +81 -104
- package/handlers/app-definition-loader.js +1 -1
- package/handlers/app-handler-helpers.js +2 -14
- package/handlers/backend-utils.js +1 -0
- package/handlers/database-migration-handler.js +45 -64
- package/handlers/middleware/admin-auth.js +53 -0
- package/handlers/routers/HEALTHCHECK.md +135 -170
- package/handlers/routers/auth.js +2 -23
- package/handlers/routers/db-migration.handler.js +3 -2
- package/handlers/routers/db-migration.js +33 -69
- package/handlers/routers/health.js +8 -23
- package/handlers/routers/integration-defined-routers.js +4 -4
- package/handlers/routers/integration-webhook-routers.js +7 -17
- package/handlers/routers/websocket.js +3 -9
- package/handlers/use-cases/check-integrations-health-use-case.js +6 -13
- package/handlers/workers/db-migration.js +26 -54
- package/index.js +9 -5
- package/integrations/WEBHOOK-QUICKSTART.md +14 -21
- package/integrations/index.js +0 -4
- package/integrations/integration-base.js +6 -9
- package/integrations/integration-router.js +146 -1817
- package/integrations/options.js +20 -47
- package/integrations/repositories/integration-repository-documentdb.js +24 -53
- package/integrations/repositories/integration-repository-factory.js +2 -6
- package/integrations/repositories/integration-repository-interface.js +9 -27
- package/integrations/repositories/integration-repository-postgres.js +3 -3
- package/integrations/repositories/process-repository-factory.js +1 -0
- package/integrations/repositories/process-repository-interface.js +5 -9
- package/integrations/repositories/process-repository-mongo.js +3 -7
- package/integrations/repositories/process-repository-postgres.js +2 -2
- package/integrations/tests/doubles/config-capturing-integration.js +8 -9
- package/integrations/tests/doubles/dummy-integration-class.js +8 -81
- package/integrations/tests/doubles/test-integration-repository.js +11 -51
- package/integrations/use-cases/create-integration.js +32 -36
- package/integrations/use-cases/create-process.js +9 -20
- package/integrations/use-cases/delete-integration-for-user.js +4 -5
- package/integrations/use-cases/get-integration-for-user.js +10 -25
- package/integrations/use-cases/get-integration-instance-by-definition.js +7 -13
- package/integrations/use-cases/get-integrations-for-user.js +1 -4
- package/integrations/use-cases/get-possible-integrations.js +1 -1
- package/integrations/use-cases/get-process.js +6 -5
- package/integrations/use-cases/index.js +1 -3
- package/integrations/use-cases/load-integration-context.js +5 -1
- package/integrations/use-cases/update-process-state.js +8 -10
- package/integrations/utils/map-integration-dto.js +3 -9
- package/jest-setup.js +1 -1
- package/jest-teardown.js +1 -1
- package/lambda/TimeoutCatcher.js +30 -32
- package/lambda/index.js +1 -1
- package/logs/index.js +3 -3
- package/modules/module.js +1 -3
- package/modules/repositories/module-repository-documentdb.js +48 -114
- package/modules/repositories/module-repository-interface.js +0 -13
- package/modules/repositories/module-repository-mongo.js +6 -46
- package/modules/repositories/module-repository-postgres.js +6 -48
- package/modules/repositories/module-repository.js +1 -34
- package/modules/requester/api-key.js +2 -0
- package/modules/requester/basic.js +1 -0
- package/modules/requester/oauth-2.js +2 -3
- package/modules/test/mock-api/definition.js +10 -15
- package/modules/test/mock-api/mocks/hubspot.js +36 -36
- package/modules/tests/doubles/test-module-factory.js +4 -10
- package/modules/tests/doubles/test-module-repository.js +1 -1
- package/modules/use-cases/get-entities-for-user.js +1 -1
- package/modules/use-cases/get-entity-options-by-id.js +6 -8
- package/modules/use-cases/get-module.js +9 -12
- package/modules/use-cases/process-authorization-callback.js +6 -12
- package/modules/use-cases/refresh-entity-options.js +6 -8
- package/modules/use-cases/test-module-auth.js +6 -8
- package/modules/utils/map-module-dto.js +2 -2
- package/package.json +5 -7
- package/prisma-mongodb/schema.prisma +33 -75
- package/prisma-postgresql/schema.prisma +33 -73
- package/queues/queuer-util.js +1 -5
- package/syncs/model.js +51 -51
- package/syncs/repositories/sync-repository-documentdb.js +19 -39
- package/syncs/repositories/sync-repository-interface.js +2 -6
- package/syncs/repositories/sync-repository-postgres.js +13 -19
- package/syncs/sync.js +100 -104
- package/token/repositories/token-repository-factory.js +3 -1
- package/types/assertions/index.d.ts +72 -72
- package/types/associations/index.d.ts +57 -57
- package/types/core/index.d.ts +53 -59
- package/types/database/index.d.ts +2 -2
- package/types/encrypt/index.d.ts +3 -3
- package/types/errors/index.d.ts +54 -61
- package/types/eslint-config/index.d.ts +40 -40
- package/types/integrations/index.d.ts +183 -185
- package/types/lambda/index.d.ts +26 -26
- package/types/logs/index.d.ts +4 -4
- package/types/module-plugin/index.d.ts +237 -239
- package/types/prettier-config/index.d.ts +5 -5
- package/types/syncs/index.d.ts +108 -102
- package/types/test-environment/index.d.ts +14 -14
- package/types/tsconfig.json +93 -95
- package/user/tests/doubles/test-user-repository.js +2 -5
- package/user/use-cases/authenticate-user.js +4 -2
- package/user/use-cases/authenticate-with-shared-secret.js +1 -1
- package/user/use-cases/create-individual-user.js +7 -8
- 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/get-user-from-adopter-jwt.js +2 -0
- package/user/use-cases/get-user-from-bearer-token.js +3 -7
- package/user/use-cases/get-user-from-x-frigg-headers.js +11 -14
- package/user/use-cases/login-user.js +8 -8
- package/user/user.js +4 -18
- package/utils/backend-path.js +2 -2
- package/utils/index.js +1 -4
- package/websocket/repositories/websocket-connection-repository-documentdb.js +8 -21
- package/websocket/repositories/websocket-connection-repository-mongo.js +4 -8
- package/websocket/repositories/websocket-connection-repository-postgres.js +4 -8
- package/websocket/repositories/websocket-connection-repository.js +4 -8
- package/admin-scripts/repositories/admin-api-key-repository-interface.js +0 -121
- package/admin-scripts/repositories/admin-api-key-repository-mongo.js +0 -155
- package/admin-scripts/repositories/admin-api-key-repository-postgres.js +0 -189
- package/admin-scripts/repositories/script-execution-repository-documentdb.js +0 -21
- package/admin-scripts/repositories/script-execution-repository-factory.js +0 -55
- package/admin-scripts/repositories/script-execution-repository-interface.js +0 -193
- package/admin-scripts/repositories/script-execution-repository-mongo.js +0 -278
- package/admin-scripts/repositories/script-execution-repository-postgres.js +0 -320
- package/credential/credential-router.js +0 -262
- package/credential/use-cases/delete-credential-for-user.js +0 -50
- package/credential/use-cases/list-credentials-for-user.js +0 -31
- package/credential/use-cases/reauthorize-credential.js +0 -103
- package/handlers/routers/admin.js +0 -450
- package/handlers/routers/docs.js +0 -180
- package/handlers/routers/middleware/loadUser.js +0 -39
- package/handlers/routers/middleware/requireAdmin.js +0 -41
- package/handlers/routers/middleware/requireLoggedInUser.js +0 -19
- package/integrations/use-cases/execute-proxy-request.js +0 -564
- package/modules/domain/entities/AuthorizationSession.js +0 -131
- package/modules/domain/entities/index.js +0 -10
- package/modules/repositories/authorization-session-repository-factory.js +0 -55
- package/modules/repositories/authorization-session-repository-interface.js +0 -75
- package/modules/repositories/authorization-session-repository-mongo.js +0 -169
- package/modules/repositories/authorization-session-repository-postgres.js +0 -170
- package/modules/use-cases/delete-module-entity.js +0 -23
- package/modules/use-cases/get-authorization-requirements.js +0 -110
- package/modules/use-cases/get-module-entity-by-id.js +0 -16
- package/modules/use-cases/process-authorization-step.js +0 -160
- package/modules/use-cases/start-authorization-session.js +0 -86
- package/modules/use-cases/update-module-entity.js +0 -27
- package/openapi/openapi-spec-generator.js +0 -370
- package/openapi/openapi-v1.yaml +0 -1360
- package/openapi/openapi-v2.yaml +0 -1535
- package/user/use-cases/delete-user.js +0 -47
package/handlers/WEBHOOKS.md
CHANGED
|
@@ -5,36 +5,31 @@ This document explains how to implement webhook handling for your Frigg integrat
|
|
|
5
5
|
## Overview
|
|
6
6
|
|
|
7
7
|
Frigg provides a scalable webhook architecture that:
|
|
8
|
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
- **Throttles database connections** using SQS to handle webhook bursts
|
|
8
|
+
- **Receives webhooks without database connections** for fast response times
|
|
9
|
+
- **Queues webhooks to SQS** for async processing
|
|
10
|
+
- **Processes webhooks with fully hydrated integrations** (with DB and API modules loaded)
|
|
11
|
+
- **Supports custom signature verification** for security
|
|
12
|
+
- **Throttles database connections** using SQS to handle webhook bursts
|
|
14
13
|
|
|
15
14
|
## Architecture
|
|
16
15
|
|
|
17
16
|
The webhook flow consists of two stages:
|
|
18
17
|
|
|
19
18
|
### Stage 1: HTTP Webhook Receiver (No DB)
|
|
20
|
-
|
|
21
19
|
```
|
|
22
20
|
Webhook → Lambda → WEBHOOK_RECEIVED event → Queue to SQS → 200 OK Response
|
|
23
21
|
```
|
|
24
|
-
|
|
25
|
-
-
|
|
26
|
-
-
|
|
27
|
-
- Messages queued for processing
|
|
22
|
+
- Fast response (no database query)
|
|
23
|
+
- Optional signature verification
|
|
24
|
+
- Messages queued for processing
|
|
28
25
|
|
|
29
26
|
### Stage 2: Queue Worker (DB-Connected)
|
|
30
|
-
|
|
31
27
|
```
|
|
32
28
|
SQS Queue → Lambda Worker → ON_WEBHOOK event → Process with hydrated integration
|
|
33
29
|
```
|
|
34
|
-
|
|
35
|
-
-
|
|
36
|
-
-
|
|
37
|
-
- Can use integration context
|
|
30
|
+
- Full database access
|
|
31
|
+
- API modules loaded
|
|
32
|
+
- Can use integration context
|
|
38
33
|
|
|
39
34
|
## Enabling Webhooks
|
|
40
35
|
|
|
@@ -64,9 +59,7 @@ class MyIntegration extends IntegrationBase {
|
|
|
64
59
|
static Definition = {
|
|
65
60
|
name: 'my-integration',
|
|
66
61
|
version: '1.0.0',
|
|
67
|
-
modules: {
|
|
68
|
-
/* ... */
|
|
69
|
-
},
|
|
62
|
+
modules: { /* ... */ },
|
|
70
63
|
webhooks: {
|
|
71
64
|
enabled: true,
|
|
72
65
|
// Future options will be added here
|
|
@@ -80,24 +73,20 @@ class MyIntegration extends IntegrationBase {
|
|
|
80
73
|
When webhooks are enabled, two routes are automatically created:
|
|
81
74
|
|
|
82
75
|
### General Webhook
|
|
83
|
-
|
|
84
76
|
```
|
|
85
77
|
POST /api/{integrationName}-integration/webhooks
|
|
86
78
|
```
|
|
87
|
-
|
|
88
|
-
-
|
|
89
|
-
-
|
|
90
|
-
- Creates unhydrated integration instance
|
|
79
|
+
- No integration ID required
|
|
80
|
+
- Useful for system-wide events
|
|
81
|
+
- Creates unhydrated integration instance
|
|
91
82
|
|
|
92
83
|
### Integration-Specific Webhook
|
|
93
|
-
|
|
94
84
|
```
|
|
95
85
|
POST /api/{integrationName}-integration/webhooks/:integrationId
|
|
96
86
|
```
|
|
97
|
-
|
|
98
|
-
-
|
|
99
|
-
-
|
|
100
|
-
- Recommended for most use cases
|
|
87
|
+
- Includes integration ID in URL
|
|
88
|
+
- Worker loads full integration with DB and modules
|
|
89
|
+
- Recommended for most use cases
|
|
101
90
|
|
|
102
91
|
## Event Handlers
|
|
103
92
|
|
|
@@ -106,7 +95,6 @@ POST /api/{integrationName}-integration/webhooks/:integrationId
|
|
|
106
95
|
Triggered when a webhook HTTP request is received (no database connection).
|
|
107
96
|
|
|
108
97
|
#### Default Behavior
|
|
109
|
-
|
|
110
98
|
Queues the webhook to SQS and responds with `200 OK`:
|
|
111
99
|
|
|
112
100
|
```javascript
|
|
@@ -137,7 +125,7 @@ class MyIntegration extends IntegrationBase {
|
|
|
137
125
|
// Verify webhook signature
|
|
138
126
|
const signature = req.headers['x-webhook-signature'];
|
|
139
127
|
const expectedSignature = this.calculateSignature(req.body);
|
|
140
|
-
|
|
128
|
+
|
|
141
129
|
if (signature !== expectedSignature) {
|
|
142
130
|
return res.status(401).json({ error: 'Invalid signature' });
|
|
143
131
|
}
|
|
@@ -168,7 +156,6 @@ class MyIntegration extends IntegrationBase {
|
|
|
168
156
|
Triggered by the queue worker (with database connection and hydrated integration).
|
|
169
157
|
|
|
170
158
|
#### Default Behavior
|
|
171
|
-
|
|
172
159
|
Logs the webhook data (override this!):
|
|
173
160
|
|
|
174
161
|
```javascript
|
|
@@ -275,26 +262,27 @@ class SlackIntegration extends IntegrationBase {
|
|
|
275
262
|
const crypto = require('crypto');
|
|
276
263
|
const signingSecret = process.env.SLACK_SIGNING_SECRET;
|
|
277
264
|
const timestamp = req.headers['x-slack-request-timestamp'];
|
|
278
|
-
|
|
265
|
+
|
|
279
266
|
// Validate timestamp is recent (within 5 minutes)
|
|
280
267
|
const currentTime = Math.floor(Date.now() / 1000);
|
|
281
268
|
if (Math.abs(currentTime - parseInt(timestamp)) > 300) {
|
|
282
269
|
return false; // Request is older than 5 minutes
|
|
283
270
|
}
|
|
284
|
-
|
|
271
|
+
|
|
285
272
|
const hmac = crypto.createHmac('sha256', signingSecret);
|
|
286
273
|
hmac.update(`v0:${timestamp}:${JSON.stringify(req.body)}`);
|
|
287
274
|
const expected = `v0=${hmac.digest('hex')}`;
|
|
288
|
-
|
|
275
|
+
|
|
289
276
|
// Check lengths first to avoid errors in timingSafeEqual
|
|
290
|
-
const expectedBuffer = Buffer.from(expected)
|
|
291
|
-
const signatureBuffer = Buffer.from(signature)
|
|
292
|
-
|
|
277
|
+
const expectedBuffer = Buffer.from(expected)
|
|
278
|
+
const signatureBuffer = Buffer.from(signature)
|
|
279
|
+
|
|
293
280
|
if (expectedBuffer.length !== signatureBuffer.length) {
|
|
294
|
-
return false
|
|
281
|
+
return false
|
|
295
282
|
}
|
|
283
|
+
|
|
284
|
+
return crypto.timingSafeEqual(expectedBuffer, signatureBuffer)
|
|
296
285
|
|
|
297
|
-
return crypto.timingSafeEqual(expectedBuffer, signatureBuffer);
|
|
298
286
|
}
|
|
299
287
|
}
|
|
300
288
|
```
|
|
@@ -457,7 +445,6 @@ describe('MyIntegration Webhooks', () => {
|
|
|
457
445
|
## Best Practices
|
|
458
446
|
|
|
459
447
|
### 1. Always Verify Signatures
|
|
460
|
-
|
|
461
448
|
```javascript
|
|
462
449
|
async onWebhookReceived({ req, res }) {
|
|
463
450
|
// Verify before queueing
|
|
@@ -470,17 +457,14 @@ async onWebhookReceived({ req, res }) {
|
|
|
470
457
|
```
|
|
471
458
|
|
|
472
459
|
### 2. Respond Quickly
|
|
473
|
-
|
|
474
460
|
The `WEBHOOK_RECEIVED` handler should complete in < 3 seconds:
|
|
475
|
-
|
|
476
|
-
-
|
|
477
|
-
-
|
|
478
|
-
- Return 200 OK
|
|
461
|
+
- Verify signature
|
|
462
|
+
- Queue message
|
|
463
|
+
- Return 200 OK
|
|
479
464
|
|
|
480
465
|
Heavy processing goes in `ON_WEBHOOK`.
|
|
481
466
|
|
|
482
467
|
### 3. Handle Idempotency
|
|
483
|
-
|
|
484
468
|
```javascript
|
|
485
469
|
async onWebhook({ data }) {
|
|
486
470
|
const { body } = data;
|
|
@@ -500,7 +484,6 @@ async onWebhook({ data }) {
|
|
|
500
484
|
```
|
|
501
485
|
|
|
502
486
|
### 4. Error Handling
|
|
503
|
-
|
|
504
487
|
```javascript
|
|
505
488
|
async onWebhook({ data }) {
|
|
506
489
|
try {
|
|
@@ -508,7 +491,7 @@ async onWebhook({ data }) {
|
|
|
508
491
|
} catch (error) {
|
|
509
492
|
// Log error - message will go to DLQ after retries
|
|
510
493
|
console.error('Webhook processing failed:', error);
|
|
511
|
-
|
|
494
|
+
|
|
512
495
|
// Update integration status if needed
|
|
513
496
|
await this.updateIntegrationMessages.execute(
|
|
514
497
|
this.id,
|
|
@@ -517,7 +500,7 @@ async onWebhook({ data }) {
|
|
|
517
500
|
error.message,
|
|
518
501
|
Date.now()
|
|
519
502
|
);
|
|
520
|
-
|
|
503
|
+
|
|
521
504
|
throw error; // Re-throw for retry/DLQ
|
|
522
505
|
}
|
|
523
506
|
}
|
|
@@ -530,20 +513,18 @@ async onWebhook({ data }) {
|
|
|
530
513
|
When `webhooks: true` is set, the Frigg infrastructure automatically creates:
|
|
531
514
|
|
|
532
515
|
1. **HTTP Lambda Function**
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
- Fast cold start
|
|
516
|
+
- Handler: `integration-webhook-routers.js`
|
|
517
|
+
- No database connection
|
|
518
|
+
- Fast cold start
|
|
537
519
|
|
|
538
520
|
2. **Webhook Routes**
|
|
521
|
+
- `POST /api/{name}-integration/webhooks`
|
|
522
|
+
- `POST /api/{name}-integration/webhooks/:integrationId`
|
|
539
523
|
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
- Processes from existing integration queue
|
|
545
|
-
- Handles `ON_WEBHOOK` events
|
|
546
|
-
- Full database access
|
|
524
|
+
3. **Queue Worker**
|
|
525
|
+
- Processes from existing integration queue
|
|
526
|
+
- Handles `ON_WEBHOOK` events
|
|
527
|
+
- Full database access
|
|
547
528
|
|
|
548
529
|
### Serverless Configuration (Automatic)
|
|
549
530
|
|
|
@@ -551,22 +532,22 @@ The following is generated automatically in `serverless.yml`:
|
|
|
551
532
|
|
|
552
533
|
```yaml
|
|
553
534
|
functions:
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
535
|
+
myintegrationWebhook:
|
|
536
|
+
handler: node_modules/@friggframework/core/handlers/routers/integration-webhook-routers.handlers.myintegrationWebhook.handler
|
|
537
|
+
events:
|
|
538
|
+
- httpApi:
|
|
539
|
+
path: /api/myintegration-integration/webhooks
|
|
540
|
+
method: POST
|
|
541
|
+
- httpApi:
|
|
542
|
+
path: /api/myintegration-integration/webhooks/{integrationId}
|
|
543
|
+
method: POST
|
|
544
|
+
|
|
545
|
+
myintegrationQueueWorker:
|
|
546
|
+
handler: node_modules/@friggframework/core/handlers/workers/integration-defined-workers.handlers.myintegration.queueWorker
|
|
547
|
+
events:
|
|
548
|
+
- sqs:
|
|
549
|
+
arn: !GetAtt MyintegrationQueue.Arn
|
|
550
|
+
batchSize: 1
|
|
570
551
|
```
|
|
571
552
|
|
|
572
553
|
## Event Handler Reference
|
|
@@ -579,14 +560,13 @@ functions:
|
|
|
579
560
|
**Must:** Respond to `res` with status code
|
|
580
561
|
|
|
581
562
|
**Parameters:**
|
|
582
|
-
|
|
583
|
-
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
-
|
|
589
|
-
- Call `res.status(code).json(data)` to respond
|
|
563
|
+
- `req` - Express request object
|
|
564
|
+
- `req.body` - Webhook payload
|
|
565
|
+
- `req.params.integrationId` - Integration ID (if in URL)
|
|
566
|
+
- `req.headers` - HTTP headers
|
|
567
|
+
- `req.query` - Query parameters
|
|
568
|
+
- `res` - Express response object
|
|
569
|
+
- Call `res.status(code).json(data)` to respond
|
|
590
570
|
|
|
591
571
|
### onWebhook({ data, context })
|
|
592
572
|
|
|
@@ -596,13 +576,12 @@ functions:
|
|
|
596
576
|
**Can:** Use `this.modules`, `this.config`, DB operations
|
|
597
577
|
|
|
598
578
|
**Parameters:**
|
|
599
|
-
|
|
600
|
-
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
- `context` - Lambda context object
|
|
579
|
+
- `data` - Queued webhook data
|
|
580
|
+
- `data.integrationId` - Integration ID (if provided)
|
|
581
|
+
- `data.body` - Original webhook payload
|
|
582
|
+
- `data.headers` - Original HTTP headers
|
|
583
|
+
- `data.query` - Original query parameters
|
|
584
|
+
- `context` - Lambda context object
|
|
606
585
|
|
|
607
586
|
## Queue Helper
|
|
608
587
|
|
|
@@ -629,7 +608,6 @@ Automatically uses the correct SQS queue URL based on integration name.
|
|
|
629
608
|
**Error:** `Queue URL not found for {NAME}_QUEUE_URL`
|
|
630
609
|
|
|
631
610
|
**Solution:** Ensure environment variable is set:
|
|
632
|
-
|
|
633
611
|
```bash
|
|
634
612
|
export MY_INTEGRATION_QUEUE_URL=https://sqs.us-east-1.amazonaws.com/...
|
|
635
613
|
```
|
|
@@ -637,7 +615,6 @@ export MY_INTEGRATION_QUEUE_URL=https://sqs.us-east-1.amazonaws.com/...
|
|
|
637
615
|
### Webhook Not Responding
|
|
638
616
|
|
|
639
617
|
**Check:**
|
|
640
|
-
|
|
641
618
|
1. Is `webhooks: true` in Definition?
|
|
642
619
|
2. Is webhook endpoint deployed?
|
|
643
620
|
3. Are you sending POST requests?
|
|
@@ -646,7 +623,6 @@ export MY_INTEGRATION_QUEUE_URL=https://sqs.us-east-1.amazonaws.com/...
|
|
|
646
623
|
### Worker Not Processing
|
|
647
624
|
|
|
648
625
|
**Check:**
|
|
649
|
-
|
|
650
626
|
1. Is SQS queue receiving messages?
|
|
651
627
|
2. Is queue worker Lambda function deployed?
|
|
652
628
|
3. Check CloudWatch logs for worker errors
|
|
@@ -662,15 +638,16 @@ export MY_INTEGRATION_QUEUE_URL=https://sqs.us-east-1.amazonaws.com/...
|
|
|
662
638
|
|
|
663
639
|
## Performance
|
|
664
640
|
|
|
665
|
-
-
|
|
666
|
-
-
|
|
667
|
-
-
|
|
668
|
-
-
|
|
641
|
+
- **HTTP Response:** < 100ms (signature check + queue)
|
|
642
|
+
- **Worker Processing:** Based on your logic
|
|
643
|
+
- **Concurrency:** Controlled by SQS worker `reservedConcurrency: 5`
|
|
644
|
+
- **Burst Handling:** Unlimited HTTP, throttled processing
|
|
669
645
|
|
|
670
646
|
## Related Files
|
|
671
647
|
|
|
672
|
-
-
|
|
673
|
-
-
|
|
674
|
-
-
|
|
675
|
-
-
|
|
676
|
-
-
|
|
648
|
+
- `packages/core/integrations/integration-base.js` - Event definitions and default handlers
|
|
649
|
+
- `packages/core/handlers/routers/integration-webhook-routers.js` - HTTP webhook routes
|
|
650
|
+
- `packages/core/handlers/backend-utils.js` - Queue worker with hydration logic
|
|
651
|
+
- `packages/core/handlers/integration-event-dispatcher.js` - Event dispatching
|
|
652
|
+
- `packages/devtools/infrastructure/serverless-template.js` - Automatic infrastructure generation
|
|
653
|
+
|
|
@@ -3,7 +3,6 @@ const express = require('express');
|
|
|
3
3
|
const bodyParser = require('body-parser');
|
|
4
4
|
const cors = require('cors');
|
|
5
5
|
const Boom = require('@hapi/boom');
|
|
6
|
-
const loadUserManager = require('./routers/middleware/loadUser');
|
|
7
6
|
const serverlessHttp = require('serverless-http');
|
|
8
7
|
|
|
9
8
|
const createApp = (applyMiddleware) => {
|
|
@@ -20,8 +19,6 @@ const createApp = (applyMiddleware) => {
|
|
|
20
19
|
})
|
|
21
20
|
);
|
|
22
21
|
|
|
23
|
-
app.use(loadUserManager);
|
|
24
|
-
|
|
25
22
|
if (applyMiddleware) applyMiddleware(app);
|
|
26
23
|
|
|
27
24
|
// Handle sending error response and logging server errors to console
|
|
@@ -42,18 +39,9 @@ const createApp = (applyMiddleware) => {
|
|
|
42
39
|
return app;
|
|
43
40
|
};
|
|
44
41
|
|
|
45
|
-
function createAppHandler(
|
|
46
|
-
eventName,
|
|
47
|
-
router,
|
|
48
|
-
shouldUseDatabase = true,
|
|
49
|
-
basePath = null
|
|
50
|
-
) {
|
|
42
|
+
function createAppHandler(eventName, router, shouldUseDatabase = true) {
|
|
51
43
|
const app = createApp((app) => {
|
|
52
|
-
|
|
53
|
-
app.use(basePath, router);
|
|
54
|
-
} else {
|
|
55
|
-
app.use(router);
|
|
56
|
-
}
|
|
44
|
+
app.use(router);
|
|
57
45
|
});
|
|
58
46
|
return createHandler({
|
|
59
47
|
eventName,
|
|
@@ -101,6 +101,7 @@ const loadIntegrationForWebhook = async (integrationId) => {
|
|
|
101
101
|
};
|
|
102
102
|
|
|
103
103
|
const loadIntegrationForProcess = async (processId, integrationClass) => {
|
|
104
|
+
|
|
104
105
|
const { processRepository, integrationRepository, moduleRepository } =
|
|
105
106
|
initializeRepositories();
|
|
106
107
|
|
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Database Migration Handler for AWS Lambda
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
4
|
* Executes Prisma migrations in a Lambda environment.
|
|
5
5
|
* Based on AWS best practices for running migrations in serverless environments.
|
|
6
|
-
*
|
|
6
|
+
*
|
|
7
7
|
* Supported Commands:
|
|
8
8
|
* - deploy: Apply pending migrations to the database (production-safe)
|
|
9
9
|
* - reset: Reset database and apply all migrations (DANGEROUS - dev only)
|
|
10
|
-
*
|
|
10
|
+
*
|
|
11
11
|
* Usage:
|
|
12
12
|
* // Via Lambda invoke
|
|
13
13
|
* {
|
|
14
14
|
* "command": "deploy" // or "reset"
|
|
15
15
|
* }
|
|
16
|
-
*
|
|
16
|
+
*
|
|
17
17
|
* Requirements:
|
|
18
18
|
* - Prisma CLI must be included in deployment or Lambda layer
|
|
19
19
|
* - DATABASE_URL environment variable must be set
|
|
20
20
|
* - VPC configuration for Aurora access
|
|
21
|
-
*
|
|
21
|
+
*
|
|
22
22
|
* Reference: https://www.prisma.io/docs/guides/deployment/deployment-guides/deploying-to-aws-lambda
|
|
23
23
|
*/
|
|
24
24
|
|
|
@@ -27,7 +27,7 @@ const path = require('path');
|
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
29
|
* Execute Prisma migration command
|
|
30
|
-
*
|
|
30
|
+
*
|
|
31
31
|
* @param {string} command - Migration command ('deploy' or 'reset')
|
|
32
32
|
* @param {string} schemaPath - Path to Prisma schema file
|
|
33
33
|
* @returns {Promise<number>} Exit code
|
|
@@ -35,18 +35,16 @@ const path = require('path');
|
|
|
35
35
|
async function executePrismaMigration(command, schemaPath) {
|
|
36
36
|
console.log(`Executing Prisma migration: ${command}`);
|
|
37
37
|
console.log(`Schema path: ${schemaPath}`);
|
|
38
|
-
console.log(
|
|
39
|
-
`Database URL: ${process.env.DATABASE_URL ? '[SET]' : '[NOT SET]'}`
|
|
40
|
-
);
|
|
38
|
+
console.log(`Database URL: ${process.env.DATABASE_URL ? '[SET]' : '[NOT SET]'}`);
|
|
41
39
|
|
|
42
40
|
return new Promise((resolve, reject) => {
|
|
43
41
|
// Build command arguments
|
|
44
42
|
const args = ['migrate', command];
|
|
45
|
-
|
|
43
|
+
|
|
46
44
|
// Add command-specific options
|
|
47
45
|
if (command === 'reset') {
|
|
48
|
-
args.push('--force');
|
|
49
|
-
args.push('--skip-generate');
|
|
46
|
+
args.push('--force'); // Skip confirmation prompt
|
|
47
|
+
args.push('--skip-generate'); // Skip client generation (already done in layer)
|
|
50
48
|
}
|
|
51
49
|
|
|
52
50
|
// Add schema path if provided
|
|
@@ -55,7 +53,7 @@ async function executePrismaMigration(command, schemaPath) {
|
|
|
55
53
|
}
|
|
56
54
|
|
|
57
55
|
console.log(`Running: prisma ${args.join(' ')}`);
|
|
58
|
-
|
|
56
|
+
|
|
59
57
|
// Execute Prisma CLI
|
|
60
58
|
execFile(
|
|
61
59
|
path.resolve('./node_modules/prisma/build/index.js'),
|
|
@@ -65,7 +63,7 @@ async function executePrismaMigration(command, schemaPath) {
|
|
|
65
63
|
...process.env,
|
|
66
64
|
// Ensure Prisma uses the correct binary target
|
|
67
65
|
PRISMA_CLI_BINARY_TARGETS: 'rhel-openssl-3.0.x',
|
|
68
|
-
}
|
|
66
|
+
}
|
|
69
67
|
},
|
|
70
68
|
(error, stdout, stderr) => {
|
|
71
69
|
// Log all output
|
|
@@ -77,10 +75,7 @@ async function executePrismaMigration(command, schemaPath) {
|
|
|
77
75
|
}
|
|
78
76
|
|
|
79
77
|
if (error) {
|
|
80
|
-
console.error(
|
|
81
|
-
`Migration ${command} exited with error:`,
|
|
82
|
-
error.message
|
|
83
|
-
);
|
|
78
|
+
console.error(`Migration ${command} exited with error:`, error.message);
|
|
84
79
|
console.error(`Exit code: ${error.code || 1}`);
|
|
85
80
|
resolve(error.code || 1);
|
|
86
81
|
} else {
|
|
@@ -97,11 +92,11 @@ async function executePrismaMigration(command, schemaPath) {
|
|
|
97
92
|
*/
|
|
98
93
|
function validateCommand(command) {
|
|
99
94
|
const validCommands = ['deploy', 'reset'];
|
|
100
|
-
|
|
95
|
+
|
|
101
96
|
if (!validCommands.includes(command)) {
|
|
102
97
|
throw new Error(
|
|
103
98
|
`Invalid migration command: "${command}". ` +
|
|
104
|
-
|
|
99
|
+
`Valid commands are: ${validCommands.join(', ')}`
|
|
105
100
|
);
|
|
106
101
|
}
|
|
107
102
|
|
|
@@ -111,12 +106,10 @@ function validateCommand(command) {
|
|
|
111
106
|
if (stage === 'production' || stage === 'prod') {
|
|
112
107
|
throw new Error(
|
|
113
108
|
'BLOCKED: "reset" command is not allowed in production environment. ' +
|
|
114
|
-
|
|
109
|
+
'This command would delete all data. Use "deploy" instead.'
|
|
115
110
|
);
|
|
116
111
|
}
|
|
117
|
-
console.warn(
|
|
118
|
-
'⚠️ WARNING: "reset" will DELETE all data and reset the database!'
|
|
119
|
-
);
|
|
112
|
+
console.warn('⚠️ WARNING: "reset" will DELETE all data and reset the database!');
|
|
120
113
|
}
|
|
121
114
|
}
|
|
122
115
|
|
|
@@ -126,17 +119,14 @@ function validateCommand(command) {
|
|
|
126
119
|
function getSchemaPath() {
|
|
127
120
|
// In Lambda, schemas are in @friggframework/core/generated/
|
|
128
121
|
const baseSchemaPath = './node_modules/@friggframework/core/generated';
|
|
129
|
-
|
|
122
|
+
|
|
130
123
|
// Check if Postgres is enabled
|
|
131
|
-
if (
|
|
132
|
-
process.env.DATABASE_URL?.includes('postgresql') ||
|
|
133
|
-
process.env.DATABASE_URL?.includes('postgres')
|
|
134
|
-
) {
|
|
124
|
+
if (process.env.DATABASE_URL?.includes('postgresql') || process.env.DATABASE_URL?.includes('postgres')) {
|
|
135
125
|
const schemaPath = `${baseSchemaPath}/prisma-postgresql/schema.prisma`;
|
|
136
126
|
console.log(`Using PostgreSQL schema: ${schemaPath}`);
|
|
137
127
|
return schemaPath;
|
|
138
128
|
}
|
|
139
|
-
|
|
129
|
+
|
|
140
130
|
// Check if MongoDB is enabled
|
|
141
131
|
if (process.env.DATABASE_URL?.includes('mongodb')) {
|
|
142
132
|
const schemaPath = `${baseSchemaPath}/prisma-mongodb/schema.prisma`;
|
|
@@ -145,15 +135,13 @@ function getSchemaPath() {
|
|
|
145
135
|
}
|
|
146
136
|
|
|
147
137
|
// Default to PostgreSQL
|
|
148
|
-
console.log(
|
|
149
|
-
'DATABASE_URL not set or database type unknown, defaulting to PostgreSQL'
|
|
150
|
-
);
|
|
138
|
+
console.log('DATABASE_URL not set or database type unknown, defaulting to PostgreSQL');
|
|
151
139
|
return `${baseSchemaPath}/prisma-postgresql/schema.prisma`;
|
|
152
140
|
}
|
|
153
141
|
|
|
154
142
|
/**
|
|
155
143
|
* Lambda handler for database migrations
|
|
156
|
-
*
|
|
144
|
+
*
|
|
157
145
|
* @param {Object} event - Lambda event
|
|
158
146
|
* @param {string} event.command - Migration command ('deploy' or 'reset')
|
|
159
147
|
* @param {Object} context - Lambda context
|
|
@@ -161,48 +149,41 @@ function getSchemaPath() {
|
|
|
161
149
|
*/
|
|
162
150
|
exports.handler = async (event, context) => {
|
|
163
151
|
const startTime = Date.now();
|
|
164
|
-
|
|
152
|
+
|
|
165
153
|
console.log('='.repeat(60));
|
|
166
154
|
console.log('Database Migration Handler');
|
|
167
155
|
console.log('='.repeat(60));
|
|
168
156
|
console.log('Event:', JSON.stringify(event, null, 2));
|
|
169
|
-
console.log(
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
logGroupName: context.logGroupName,
|
|
177
|
-
},
|
|
178
|
-
null,
|
|
179
|
-
2
|
|
180
|
-
)
|
|
181
|
-
);
|
|
182
|
-
|
|
157
|
+
console.log('Context:', JSON.stringify({
|
|
158
|
+
functionName: context.functionName,
|
|
159
|
+
functionVersion: context.functionVersion,
|
|
160
|
+
memoryLimitInMB: context.memoryLimitInMB,
|
|
161
|
+
logGroupName: context.logGroupName,
|
|
162
|
+
}, null, 2));
|
|
163
|
+
|
|
183
164
|
try {
|
|
184
165
|
// Get migration command (default to 'deploy')
|
|
185
166
|
const command = event.command || 'deploy';
|
|
186
|
-
|
|
167
|
+
|
|
187
168
|
// Validate command
|
|
188
169
|
validateCommand(command);
|
|
189
|
-
|
|
170
|
+
|
|
190
171
|
// Check required environment variables
|
|
191
172
|
if (!process.env.DATABASE_URL) {
|
|
192
173
|
throw new Error(
|
|
193
174
|
'DATABASE_URL environment variable is not set. ' +
|
|
194
|
-
|
|
175
|
+
'Cannot connect to database for migrations.'
|
|
195
176
|
);
|
|
196
177
|
}
|
|
197
|
-
|
|
178
|
+
|
|
198
179
|
// Determine schema path
|
|
199
180
|
const schemaPath = getSchemaPath();
|
|
200
|
-
|
|
181
|
+
|
|
201
182
|
// Execute migration
|
|
202
183
|
const exitCode = await executePrismaMigration(command, schemaPath);
|
|
203
|
-
|
|
184
|
+
|
|
204
185
|
const duration = Date.now() - startTime;
|
|
205
|
-
|
|
186
|
+
|
|
206
187
|
if (exitCode === 0) {
|
|
207
188
|
const result = {
|
|
208
189
|
success: true,
|
|
@@ -211,27 +192,26 @@ exports.handler = async (event, context) => {
|
|
|
211
192
|
duration: `${duration}ms`,
|
|
212
193
|
timestamp: new Date().toISOString(),
|
|
213
194
|
};
|
|
214
|
-
|
|
195
|
+
|
|
215
196
|
console.log('='.repeat(60));
|
|
216
197
|
console.log('Migration completed successfully');
|
|
217
198
|
console.log(JSON.stringify(result, null, 2));
|
|
218
199
|
console.log('='.repeat(60));
|
|
219
|
-
|
|
200
|
+
|
|
220
201
|
return result;
|
|
221
202
|
} else {
|
|
222
|
-
throw new Error(
|
|
223
|
-
`Migration ${command} failed with exit code ${exitCode}`
|
|
224
|
-
);
|
|
203
|
+
throw new Error(`Migration ${command} failed with exit code ${exitCode}`);
|
|
225
204
|
}
|
|
205
|
+
|
|
226
206
|
} catch (error) {
|
|
227
207
|
const duration = Date.now() - startTime;
|
|
228
|
-
|
|
208
|
+
|
|
229
209
|
console.error('='.repeat(60));
|
|
230
210
|
console.error('Migration failed');
|
|
231
211
|
console.error('Error:', error.message);
|
|
232
212
|
console.error('Stack:', error.stack);
|
|
233
213
|
console.error('='.repeat(60));
|
|
234
|
-
|
|
214
|
+
|
|
235
215
|
const errorResult = {
|
|
236
216
|
success: false,
|
|
237
217
|
command: event.command || 'unknown',
|
|
@@ -239,8 +219,9 @@ exports.handler = async (event, context) => {
|
|
|
239
219
|
duration: `${duration}ms`,
|
|
240
220
|
timestamp: new Date().toISOString(),
|
|
241
221
|
};
|
|
242
|
-
|
|
222
|
+
|
|
243
223
|
// Return error (don't throw) so Lambda doesn't retry
|
|
244
224
|
return errorResult;
|
|
245
225
|
}
|
|
246
226
|
};
|
|
227
|
+
|