@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/core/CLAUDE.md
CHANGED
|
@@ -4,12 +4,12 @@ This file provides guidance to Claude Code when working with the Frigg Framework
|
|
|
4
4
|
|
|
5
5
|
## Critical Context (Read First)
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
7
|
+
- **Package Purpose**: Core runtime system and foundational classes for Frigg Lambda execution
|
|
8
|
+
- **Main Components**: Handler factory, Worker base class, Delegate pattern, Module loading
|
|
9
|
+
- **Core Architecture**: Lambda-optimized runtime with connection pooling, error handling, secrets management
|
|
10
|
+
- **Key Integration**: AWS Lambda, SQS job processing, MongoDB connections, AWS Secrets Manager
|
|
11
|
+
- **Security Model**: Automatic secrets injection, database connection management, user-facing error sanitization
|
|
12
|
+
- **DO NOT**: Expose internal errors to users, bypass connection pooling, skip database initialization
|
|
13
13
|
|
|
14
14
|
## Core Components Architecture
|
|
15
15
|
|
|
@@ -18,45 +18,50 @@ This file provides guidance to Claude Code when working with the Frigg Framework
|
|
|
18
18
|
**Purpose**: Factory for creating Lambda handlers with consistent infrastructure setup
|
|
19
19
|
|
|
20
20
|
**Key Features**:
|
|
21
|
-
|
|
22
|
-
-
|
|
23
|
-
-
|
|
24
|
-
-
|
|
25
|
-
-
|
|
21
|
+
|
|
22
|
+
- **Database Connection Management**: Automatic MongoDB connection with pooling
|
|
23
|
+
- **Secrets Management**: AWS Secrets Manager integration via `SECRET_ARN` env var
|
|
24
|
+
- **Error Sanitization**: Prevents internal details from leaking to end users
|
|
25
|
+
- **Debug Logging**: Request/response logging with structured debug info
|
|
26
|
+
- **Connection Optimization**: `context.callbackWaitsForEmptyEventLoop = false` for reuse
|
|
26
27
|
|
|
27
28
|
**Handler Configuration Options**:
|
|
29
|
+
|
|
28
30
|
```javascript
|
|
29
31
|
const handler = createHandler({
|
|
30
|
-
eventName: 'MyIntegration',
|
|
31
|
-
isUserFacingResponse: true,
|
|
32
|
+
eventName: 'MyIntegration', // For logging/debugging
|
|
33
|
+
isUserFacingResponse: true, // true = sanitize errors, false = pass through
|
|
32
34
|
method: async (event, context) => {}, // Your Lambda function logic
|
|
33
|
-
shouldUseDatabase: true
|
|
35
|
+
shouldUseDatabase: true, // false = skip MongoDB connection
|
|
34
36
|
});
|
|
35
37
|
```
|
|
36
38
|
|
|
37
39
|
**Error Handling Patterns**:
|
|
38
|
-
|
|
39
|
-
-
|
|
40
|
-
-
|
|
40
|
+
|
|
41
|
+
- **User-Facing**: Returns 500 with generic "Internal Error Occurred" message
|
|
42
|
+
- **Server-to-Server**: Re-throws errors for AWS to handle
|
|
43
|
+
- **Halt Errors**: `error.isHaltError = true` logs but returns success (no retry)
|
|
41
44
|
|
|
42
45
|
### Worker Base Class (`Worker.js:9-83`)
|
|
43
46
|
|
|
44
47
|
**Purpose**: Base class for SQS job processing with standardized patterns
|
|
45
48
|
|
|
46
49
|
**Core Responsibilities**:
|
|
47
|
-
|
|
48
|
-
-
|
|
49
|
-
-
|
|
50
|
-
-
|
|
50
|
+
|
|
51
|
+
- **Queue Management**: Get SQS queue URLs and send messages
|
|
52
|
+
- **Batch Processing**: Process multiple SQS records in sequence
|
|
53
|
+
- **Message Validation**: Extensible parameter validation system
|
|
54
|
+
- **Error Handling**: Structured error handling for async job processing
|
|
51
55
|
|
|
52
56
|
**Usage Pattern**:
|
|
57
|
+
|
|
53
58
|
```javascript
|
|
54
59
|
class MyWorker extends Worker {
|
|
55
60
|
async _run(params, context = {}) {
|
|
56
61
|
// Your job processing logic here
|
|
57
62
|
// params are already JSON.parsed from SQS message body
|
|
58
63
|
}
|
|
59
|
-
|
|
64
|
+
|
|
60
65
|
_validateParams(params) {
|
|
61
66
|
// Validate required parameters
|
|
62
67
|
this._verifyParamExists(params, 'requiredField');
|
|
@@ -69,13 +74,17 @@ await worker.run(event, context); // Process SQS Records
|
|
|
69
74
|
```
|
|
70
75
|
|
|
71
76
|
**Message Sending**:
|
|
77
|
+
|
|
72
78
|
```javascript
|
|
73
|
-
await worker.send(
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
+
await worker.send(
|
|
80
|
+
{
|
|
81
|
+
QueueUrl: 'https://sqs.region.amazonaws.com/account/queue',
|
|
82
|
+
jobType: 'processAttachment',
|
|
83
|
+
integrationId: 'abc123',
|
|
84
|
+
// ... other job parameters
|
|
85
|
+
},
|
|
86
|
+
delaySeconds
|
|
87
|
+
);
|
|
79
88
|
```
|
|
80
89
|
|
|
81
90
|
### Delegate Pattern System (`Delegate.js:3-27`)
|
|
@@ -83,29 +92,35 @@ await worker.send({
|
|
|
83
92
|
**Purpose**: Observer/delegation pattern for decoupled component communication
|
|
84
93
|
|
|
85
94
|
**Core Concepts**:
|
|
86
|
-
|
|
87
|
-
-
|
|
88
|
-
-
|
|
89
|
-
-
|
|
95
|
+
|
|
96
|
+
- **Notification System**: Components notify delegates of events/state changes
|
|
97
|
+
- **Type Safety**: `delegateTypes` array defines valid notification strings
|
|
98
|
+
- **Bidirectional**: Supports both sending and receiving notifications
|
|
99
|
+
- **Null Safety**: Gracefully handles missing delegates
|
|
90
100
|
|
|
91
101
|
**Implementation Pattern**:
|
|
102
|
+
|
|
92
103
|
```javascript
|
|
93
104
|
class MyIntegration extends Delegate {
|
|
94
105
|
constructor(params) {
|
|
95
106
|
super(params);
|
|
96
|
-
this.delegateTypes = [
|
|
107
|
+
this.delegateTypes = [
|
|
108
|
+
'processComplete',
|
|
109
|
+
'errorOccurred',
|
|
110
|
+
'statusUpdate',
|
|
111
|
+
];
|
|
97
112
|
}
|
|
98
|
-
|
|
113
|
+
|
|
99
114
|
async processData(data) {
|
|
100
115
|
// Do work
|
|
101
116
|
await this.notify('statusUpdate', { progress: 50 });
|
|
102
117
|
// More work
|
|
103
118
|
await this.notify('processComplete', { result: data });
|
|
104
119
|
}
|
|
105
|
-
|
|
120
|
+
|
|
106
121
|
async receiveNotification(notifier, delegateString, object) {
|
|
107
122
|
// Handle notifications from other components
|
|
108
|
-
switch(delegateString) {
|
|
123
|
+
switch (delegateString) {
|
|
109
124
|
case 'dataReady':
|
|
110
125
|
await this.processData(object);
|
|
111
126
|
break;
|
|
@@ -119,55 +134,63 @@ class MyIntegration extends Delegate {
|
|
|
119
134
|
**Purpose**: Dynamic loading and registration of integration modules
|
|
120
135
|
|
|
121
136
|
**Key Features**:
|
|
122
|
-
|
|
123
|
-
-
|
|
124
|
-
-
|
|
125
|
-
-
|
|
137
|
+
|
|
138
|
+
- **Package Discovery**: Automatically find `@friggframework/api-module-*` packages
|
|
139
|
+
- **Module Registration**: Load and register integration classes
|
|
140
|
+
- **Configuration Management**: Handle module-specific configuration
|
|
141
|
+
- **Dependency Resolution**: Manage inter-module dependencies
|
|
126
142
|
|
|
127
143
|
## Runtime Lifecycle & Patterns
|
|
128
144
|
|
|
129
145
|
### Lambda Handler Lifecycle
|
|
146
|
+
|
|
130
147
|
1. **Pre-Execution Setup**:
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
148
|
+
|
|
149
|
+
```javascript
|
|
150
|
+
initDebugLog(eventName, event); // Debug logging setup
|
|
151
|
+
await secretsToEnv(); // Secrets Manager injection
|
|
152
|
+
context.callbackWaitsForEmptyEventLoop = false; // Connection pooling
|
|
153
|
+
```
|
|
136
154
|
|
|
137
155
|
2. **Database Connection**:
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
156
|
+
|
|
157
|
+
```javascript
|
|
158
|
+
if (shouldUseDatabase) {
|
|
159
|
+
await connectToDatabase(); // MongoDB connection with pooling
|
|
160
|
+
}
|
|
161
|
+
```
|
|
143
162
|
|
|
144
163
|
3. **Method Execution**:
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
164
|
+
|
|
165
|
+
```javascript
|
|
166
|
+
return await method(event, context); // Your integration logic
|
|
167
|
+
```
|
|
148
168
|
|
|
149
169
|
4. **Error Handling & Cleanup**:
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
170
|
+
```javascript
|
|
171
|
+
flushDebugLog(error); // Debug info flush on error
|
|
172
|
+
// Sanitized error response for user-facing endpoints
|
|
173
|
+
```
|
|
154
174
|
|
|
155
175
|
### SQS Job Processing Lifecycle
|
|
176
|
+
|
|
156
177
|
1. **Batch Processing**: Process all records in `event.Records` sequentially
|
|
157
|
-
2. **Message Parsing**: JSON.parse message body for parameters
|
|
178
|
+
2. **Message Parsing**: JSON.parse message body for parameters
|
|
158
179
|
3. **Validation**: Run custom validation on parsed parameters
|
|
159
180
|
4. **Execution**: Call `_run()` method with validated parameters
|
|
160
181
|
5. **Error Propagation**: Let AWS handle retries/DLQ for failed jobs
|
|
161
182
|
|
|
162
183
|
### Secrets Management Integration
|
|
163
|
-
|
|
164
|
-
-
|
|
165
|
-
-
|
|
166
|
-
-
|
|
184
|
+
|
|
185
|
+
- **Automatic Injection**: If `SECRET_ARN` environment variable is set
|
|
186
|
+
- **Environment Variables**: Secrets automatically set as `process.env` variables
|
|
187
|
+
- **Security**: No secrets logging or exposure in error messages
|
|
188
|
+
- **Caching**: Secrets cached for Lambda container lifetime
|
|
167
189
|
|
|
168
190
|
## Database Connection Patterns
|
|
169
191
|
|
|
170
192
|
### Connection Pooling Strategy
|
|
193
|
+
|
|
171
194
|
```javascript
|
|
172
195
|
// Mongoose connection reuse across Lambda invocations
|
|
173
196
|
context.callbackWaitsForEmptyEventLoop = false;
|
|
@@ -175,45 +198,51 @@ await connectToDatabase(); // Reuses existing connection if available
|
|
|
175
198
|
```
|
|
176
199
|
|
|
177
200
|
### Database Usage Patterns
|
|
201
|
+
|
|
178
202
|
```javascript
|
|
179
203
|
// Conditional database connection
|
|
180
204
|
const handler = createHandler({
|
|
181
|
-
shouldUseDatabase: false,
|
|
205
|
+
shouldUseDatabase: false, // Skip for database-free operations
|
|
182
206
|
method: async (event) => {
|
|
183
207
|
// No DB operations needed
|
|
184
208
|
return { statusCode: 200, body: 'OK' };
|
|
185
|
-
}
|
|
209
|
+
},
|
|
186
210
|
});
|
|
187
211
|
```
|
|
188
212
|
|
|
189
213
|
## Error Handling Architecture
|
|
190
214
|
|
|
191
215
|
### Error Classification
|
|
216
|
+
|
|
192
217
|
1. **User-Facing Errors**: `isUserFacingResponse: true`
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
218
|
+
|
|
219
|
+
- Returns generic 500 error message
|
|
220
|
+
- Prevents information disclosure
|
|
221
|
+
- Logs full error details internally
|
|
196
222
|
|
|
197
223
|
2. **Server-to-Server Errors**: `isUserFacingResponse: false`
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
224
|
+
|
|
225
|
+
- Re-throws original error for AWS handling
|
|
226
|
+
- Used for SQS, SNS, and internal API calls
|
|
227
|
+
- Enables proper retry mechanisms
|
|
201
228
|
|
|
202
229
|
3. **Halt Errors**: `error.isHaltError = true`
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
230
|
+
- Logs error but returns success
|
|
231
|
+
- Prevents infinite retries for known issues
|
|
232
|
+
- Used for graceful degradation scenarios
|
|
206
233
|
|
|
207
234
|
### Debug Logging Strategy
|
|
235
|
+
|
|
208
236
|
```javascript
|
|
209
|
-
initDebugLog(eventName, event);
|
|
237
|
+
initDebugLog(eventName, event); // Start logging context
|
|
210
238
|
// ... your code ...
|
|
211
|
-
flushDebugLog(error);
|
|
239
|
+
flushDebugLog(error); // Flush on error (includes full context)
|
|
212
240
|
```
|
|
213
241
|
|
|
214
242
|
## Integration Development Patterns
|
|
215
243
|
|
|
216
244
|
### Extending Worker for Job Processing
|
|
245
|
+
|
|
217
246
|
```javascript
|
|
218
247
|
class AttachmentWorker extends Worker {
|
|
219
248
|
_validateParams(params) {
|
|
@@ -221,7 +250,7 @@ class AttachmentWorker extends Worker {
|
|
|
221
250
|
this._verifyParamExists(params, 'attachmentUrl');
|
|
222
251
|
this._verifyParamExists(params, 'destination');
|
|
223
252
|
}
|
|
224
|
-
|
|
253
|
+
|
|
225
254
|
async _run(params, context) {
|
|
226
255
|
const { integrationId, attachmentUrl, destination } = params;
|
|
227
256
|
// Process attachment upload/download
|
|
@@ -232,37 +261,39 @@ class AttachmentWorker extends Worker {
|
|
|
232
261
|
```
|
|
233
262
|
|
|
234
263
|
### Creating Custom Handlers
|
|
264
|
+
|
|
235
265
|
```javascript
|
|
236
266
|
const myIntegrationHandler = createHandler({
|
|
237
267
|
eventName: 'MyIntegration',
|
|
238
|
-
isUserFacingResponse: true,
|
|
239
|
-
shouldUseDatabase: true,
|
|
268
|
+
isUserFacingResponse: true, // Sanitize errors for users
|
|
269
|
+
shouldUseDatabase: true, // Need database access
|
|
240
270
|
method: async (event, context) => {
|
|
241
271
|
// Your integration logic here
|
|
242
272
|
// Database is already connected
|
|
243
273
|
// Secrets are in process.env
|
|
244
|
-
|
|
274
|
+
|
|
245
275
|
return {
|
|
246
276
|
statusCode: 200,
|
|
247
|
-
body: JSON.stringify({ success: true })
|
|
277
|
+
body: JSON.stringify({ success: true }),
|
|
248
278
|
};
|
|
249
|
-
}
|
|
279
|
+
},
|
|
250
280
|
});
|
|
251
281
|
```
|
|
252
282
|
|
|
253
283
|
### Delegate Pattern for Integration Communication
|
|
284
|
+
|
|
254
285
|
```javascript
|
|
255
286
|
class IntegrationManager extends Delegate {
|
|
256
287
|
constructor() {
|
|
257
288
|
super();
|
|
258
289
|
this.delegateTypes = [
|
|
259
290
|
'authenticationComplete',
|
|
260
|
-
'syncStarted',
|
|
291
|
+
'syncStarted',
|
|
261
292
|
'syncComplete',
|
|
262
|
-
'errorOccurred'
|
|
293
|
+
'errorOccurred',
|
|
263
294
|
];
|
|
264
295
|
}
|
|
265
|
-
|
|
296
|
+
|
|
266
297
|
async startSync(integrationId) {
|
|
267
298
|
await this.notify('syncStarted', { integrationId });
|
|
268
299
|
// ... sync logic ...
|
|
@@ -274,21 +305,24 @@ class IntegrationManager extends Delegate {
|
|
|
274
305
|
## Performance Optimization Patterns
|
|
275
306
|
|
|
276
307
|
### Connection Reuse
|
|
308
|
+
|
|
277
309
|
```javascript
|
|
278
310
|
// ALWAYS set this in handlers for performance
|
|
279
311
|
context.callbackWaitsForEmptyEventLoop = false;
|
|
280
312
|
```
|
|
281
313
|
|
|
282
314
|
### Conditional Database Usage
|
|
315
|
+
|
|
283
316
|
```javascript
|
|
284
317
|
// Skip database for lightweight operations
|
|
285
318
|
const handler = createHandler({
|
|
286
|
-
shouldUseDatabase: false,
|
|
287
|
-
method: healthCheckMethod
|
|
319
|
+
shouldUseDatabase: false, // Faster cold starts
|
|
320
|
+
method: healthCheckMethod,
|
|
288
321
|
});
|
|
289
322
|
```
|
|
290
323
|
|
|
291
324
|
### SQS Batch Processing Optimization
|
|
325
|
+
|
|
292
326
|
```javascript
|
|
293
327
|
// Process records sequentially (not parallel) for resource control
|
|
294
328
|
for (const record of records) {
|
|
@@ -305,6 +339,7 @@ The Frigg Framework follows DDD/Hexagonal Architecture with clear separation bet
|
|
|
305
339
|
**Purpose**: Abstract database and external system access into dedicated repository classes.
|
|
306
340
|
|
|
307
341
|
**Structure**:
|
|
342
|
+
|
|
308
343
|
```javascript
|
|
309
344
|
// Example: packages/core/database/websocket-connection-repository.js
|
|
310
345
|
class WebsocketConnectionRepository {
|
|
@@ -335,25 +370,28 @@ class WebsocketConnectionRepository {
|
|
|
335
370
|
```
|
|
336
371
|
|
|
337
372
|
**Repository Responsibilities**:
|
|
338
|
-
|
|
339
|
-
-
|
|
340
|
-
-
|
|
341
|
-
-
|
|
342
|
-
-
|
|
343
|
-
-
|
|
373
|
+
|
|
374
|
+
- ✅ **CRUD operations** - Create, Read, Update, Delete database records
|
|
375
|
+
- ✅ **Query execution** - Run database queries and return results
|
|
376
|
+
- ✅ **Data access only** - No interpretation or decision-making
|
|
377
|
+
- ✅ **Atomic operations** - Each method performs one database operation
|
|
378
|
+
- ❌ **NO business logic** - Don't decide what data means or what to do with it
|
|
379
|
+
- ❌ **NO orchestration** - Don't coordinate multiple operations
|
|
344
380
|
|
|
345
381
|
**Real Repository Examples**:
|
|
346
|
-
|
|
347
|
-
-
|
|
348
|
-
-
|
|
349
|
-
-
|
|
350
|
-
-
|
|
382
|
+
|
|
383
|
+
- `WebsocketConnectionRepository` - WebSocket persistence (packages/core/database/websocket-connection-repository.js)
|
|
384
|
+
- `SyncRepository` - Sync object management (packages/core/syncs/sync-repository.js)
|
|
385
|
+
- `IntegrationMappingRepository` - Integration mappings (packages/core/integrations/integration-mapping-repository.js)
|
|
386
|
+
- `TokenRepository` - Token operations (packages/core/database/token-repository.js)
|
|
387
|
+
- `HealthCheckRepository` - Health check data access (packages/core/database/health-check-repository.js)
|
|
351
388
|
|
|
352
389
|
### Use Case Pattern in Core
|
|
353
390
|
|
|
354
391
|
**Purpose**: Contain business logic, orchestration, and workflow coordination.
|
|
355
392
|
|
|
356
393
|
**Structure**:
|
|
394
|
+
|
|
357
395
|
```javascript
|
|
358
396
|
// Example: packages/core/database/use-cases/check-database-health-use-case.js
|
|
359
397
|
class CheckDatabaseHealthUseCase {
|
|
@@ -364,7 +402,8 @@ class CheckDatabaseHealthUseCase {
|
|
|
364
402
|
|
|
365
403
|
async execute() {
|
|
366
404
|
// 1. Get raw data from repository
|
|
367
|
-
const { stateName, isConnected } =
|
|
405
|
+
const { stateName, isConnected } =
|
|
406
|
+
this.repository.getDatabaseConnectionState();
|
|
368
407
|
|
|
369
408
|
// 2. Apply business logic - determine health status
|
|
370
409
|
const result = {
|
|
@@ -383,38 +422,43 @@ class CheckDatabaseHealthUseCase {
|
|
|
383
422
|
```
|
|
384
423
|
|
|
385
424
|
**Use Case Responsibilities**:
|
|
386
|
-
|
|
387
|
-
-
|
|
388
|
-
-
|
|
389
|
-
-
|
|
390
|
-
-
|
|
391
|
-
-
|
|
392
|
-
-
|
|
425
|
+
|
|
426
|
+
- ✅ **Business logic** - Make decisions based on data
|
|
427
|
+
- ✅ **Orchestration** - Coordinate multiple repository calls
|
|
428
|
+
- ✅ **Validation** - Enforce business rules
|
|
429
|
+
- ✅ **Workflow** - Determine what happens next
|
|
430
|
+
- ✅ **Error handling** - Handle domain-specific errors
|
|
431
|
+
- ❌ **NO direct database access** - Always use repositories
|
|
432
|
+
- ❌ **NO HTTP concerns** - Don't know about status codes or headers
|
|
393
433
|
|
|
394
434
|
**Real Use Case Examples**:
|
|
395
|
-
|
|
396
|
-
-
|
|
435
|
+
|
|
436
|
+
- `CheckDatabaseHealthUseCase` - Database health business logic (packages/core/database/use-cases/check-database-health-use-case.js)
|
|
437
|
+
- `TestEncryptionUseCase` - Encryption testing workflow (packages/core/database/use-cases/test-encryption-use-case.js)
|
|
397
438
|
|
|
398
439
|
### Handler Pattern in Core
|
|
399
440
|
|
|
400
441
|
**Purpose**: Translate Lambda/HTTP/SQS events into use case calls.
|
|
401
442
|
|
|
402
443
|
**Handler Should ONLY**:
|
|
403
|
-
|
|
404
|
-
-
|
|
405
|
-
-
|
|
406
|
-
-
|
|
444
|
+
|
|
445
|
+
- Define routes and event handlers
|
|
446
|
+
- Call use cases (NOT repositories)
|
|
447
|
+
- Map use case results to HTTP/Lambda responses
|
|
448
|
+
- Handle protocol-specific concerns (status codes, headers)
|
|
407
449
|
|
|
408
450
|
**❌ WRONG - Handler contains business logic**:
|
|
451
|
+
|
|
409
452
|
```javascript
|
|
410
453
|
// BAD: Business logic in handler
|
|
411
454
|
router.get('/health', async (req, res) => {
|
|
412
455
|
const state = mongoose.connection.readyState;
|
|
413
|
-
const isHealthy = state === 1;
|
|
456
|
+
const isHealthy = state === 1; // ❌ Business logic in handler
|
|
414
457
|
|
|
415
|
-
if (isHealthy) {
|
|
458
|
+
if (isHealthy) {
|
|
459
|
+
// ❌ Orchestration in handler
|
|
416
460
|
const pingStart = Date.now();
|
|
417
|
-
await mongoose.connection.db.admin().ping();
|
|
461
|
+
await mongoose.connection.db.admin().ping(); // ❌ Direct DB access
|
|
418
462
|
const responseTime = Date.now() - pingStart;
|
|
419
463
|
res.json({ status: 'healthy', responseTime });
|
|
420
464
|
}
|
|
@@ -422,11 +466,12 @@ router.get('/health', async (req, res) => {
|
|
|
422
466
|
```
|
|
423
467
|
|
|
424
468
|
**✅ CORRECT - Handler delegates to use case**:
|
|
469
|
+
|
|
425
470
|
```javascript
|
|
426
471
|
// GOOD: Handler calls use case
|
|
427
472
|
const healthCheckRepository = new HealthCheckRepository();
|
|
428
473
|
const checkDatabaseHealthUseCase = new CheckDatabaseHealthUseCase({
|
|
429
|
-
healthCheckRepository
|
|
474
|
+
healthCheckRepository,
|
|
430
475
|
});
|
|
431
476
|
|
|
432
477
|
router.get('/health', async (req, res) => {
|
|
@@ -442,9 +487,11 @@ router.get('/health', async (req, res) => {
|
|
|
442
487
|
### Dependency Direction
|
|
443
488
|
|
|
444
489
|
**The Golden Rule**:
|
|
490
|
+
|
|
445
491
|
> "Handlers ONLY call Use Cases, NEVER Repositories or Business Logic directly"
|
|
446
492
|
|
|
447
493
|
**Correct Flow**:
|
|
494
|
+
|
|
448
495
|
```
|
|
449
496
|
Handler/Router (createHandler)
|
|
450
497
|
↓ calls
|
|
@@ -456,28 +503,32 @@ Database/External System
|
|
|
456
503
|
```
|
|
457
504
|
|
|
458
505
|
**Why This Matters**:
|
|
459
|
-
|
|
460
|
-
-
|
|
461
|
-
-
|
|
462
|
-
-
|
|
506
|
+
|
|
507
|
+
- **Testability**: Use cases can be tested with mocked repositories
|
|
508
|
+
- **Reusability**: Use cases can be called from handlers, CLI, background jobs
|
|
509
|
+
- **Maintainability**: Business logic is centralized, not scattered across handlers
|
|
510
|
+
- **Flexibility**: Swap repository implementations without changing use cases
|
|
463
511
|
|
|
464
512
|
### Migration from Old Patterns
|
|
465
513
|
|
|
466
514
|
**Old Pattern (Mongoose models everywhere)**:
|
|
515
|
+
|
|
467
516
|
```javascript
|
|
468
517
|
// BAD: Direct model access in handlers
|
|
469
518
|
const handler = createHandler({
|
|
470
519
|
method: async (event) => {
|
|
471
|
-
const user = await User.findById(event.userId);
|
|
472
|
-
if (!user.isActive) {
|
|
520
|
+
const user = await User.findById(event.userId); // ❌ Direct model access
|
|
521
|
+
if (!user.isActive) {
|
|
522
|
+
// ❌ Business logic in handler
|
|
473
523
|
throw new Error('User not active');
|
|
474
524
|
}
|
|
475
|
-
await Sync.create({ userId: user.id });
|
|
476
|
-
}
|
|
525
|
+
await Sync.create({ userId: user.id }); // ❌ Direct model access
|
|
526
|
+
},
|
|
477
527
|
});
|
|
478
528
|
```
|
|
479
529
|
|
|
480
530
|
**New Pattern (Repository + Use Case)**:
|
|
531
|
+
|
|
481
532
|
```javascript
|
|
482
533
|
// GOOD: Repository abstracts data access
|
|
483
534
|
class UserRepository {
|
|
@@ -502,7 +553,8 @@ class ActivateUserSyncUseCase {
|
|
|
502
553
|
async execute(userId) {
|
|
503
554
|
const user = await this.userRepo.findById(userId);
|
|
504
555
|
|
|
505
|
-
if (!user.isActive) {
|
|
556
|
+
if (!user.isActive) {
|
|
557
|
+
// ✅ Business logic in use case
|
|
506
558
|
throw new Error('User not active');
|
|
507
559
|
}
|
|
508
560
|
|
|
@@ -515,10 +567,10 @@ const handler = createHandler({
|
|
|
515
567
|
method: async (event) => {
|
|
516
568
|
const useCase = new ActivateUserSyncUseCase({
|
|
517
569
|
userRepository: new UserRepository(),
|
|
518
|
-
syncRepository: new SyncRepository()
|
|
570
|
+
syncRepository: new SyncRepository(),
|
|
519
571
|
});
|
|
520
572
|
return await useCase.execute(event.userId);
|
|
521
|
-
}
|
|
573
|
+
},
|
|
522
574
|
});
|
|
523
575
|
```
|
|
524
576
|
|
|
@@ -533,7 +585,7 @@ class ProcessAttachmentWorker extends Worker {
|
|
|
533
585
|
// Inject repositories into use case
|
|
534
586
|
this.useCase = new ProcessAttachmentUseCase({
|
|
535
587
|
asanaRepository: new AsanaRepository(),
|
|
536
|
-
frontifyRepository: new FrontifyRepository()
|
|
588
|
+
frontifyRepository: new FrontifyRepository(),
|
|
537
589
|
});
|
|
538
590
|
}
|
|
539
591
|
|
|
@@ -551,20 +603,23 @@ class ProcessAttachmentWorker extends Worker {
|
|
|
551
603
|
### When to Extract to Repository/Use Case
|
|
552
604
|
|
|
553
605
|
**Extract to Repository when you see**:
|
|
554
|
-
|
|
555
|
-
-
|
|
556
|
-
-
|
|
557
|
-
-
|
|
606
|
+
|
|
607
|
+
- Direct Mongoose model calls (`User.findById()`, `Sync.create()`)
|
|
608
|
+
- Database queries in handlers or business logic
|
|
609
|
+
- External API calls scattered across codebase
|
|
610
|
+
- File system or AWS SDK operations in handlers
|
|
558
611
|
|
|
559
612
|
**Extract to Use Case when you see**:
|
|
560
|
-
|
|
561
|
-
-
|
|
562
|
-
-
|
|
563
|
-
-
|
|
613
|
+
|
|
614
|
+
- Business logic in handlers (if/else based on data)
|
|
615
|
+
- Orchestration of multiple operations
|
|
616
|
+
- Validation and error handling logic
|
|
617
|
+
- Workflow coordination
|
|
564
618
|
|
|
565
619
|
### Testing with Repository/Use Case Pattern
|
|
566
620
|
|
|
567
621
|
**Repository Tests** (Integration tests with real DB):
|
|
622
|
+
|
|
568
623
|
```javascript
|
|
569
624
|
describe('WebsocketConnectionRepository', () => {
|
|
570
625
|
it('creates connection record', async () => {
|
|
@@ -576,17 +631,18 @@ describe('WebsocketConnectionRepository', () => {
|
|
|
576
631
|
```
|
|
577
632
|
|
|
578
633
|
**Use Case Tests** (Unit tests with mocked repositories):
|
|
634
|
+
|
|
579
635
|
```javascript
|
|
580
636
|
describe('CheckDatabaseHealthUseCase', () => {
|
|
581
637
|
it('returns unhealthy when disconnected', async () => {
|
|
582
638
|
const mockRepo = {
|
|
583
639
|
getDatabaseConnectionState: () => ({
|
|
584
640
|
stateName: 'disconnected',
|
|
585
|
-
isConnected: false
|
|
586
|
-
})
|
|
641
|
+
isConnected: false,
|
|
642
|
+
}),
|
|
587
643
|
};
|
|
588
644
|
const useCase = new CheckDatabaseHealthUseCase({
|
|
589
|
-
healthCheckRepository: mockRepo
|
|
645
|
+
healthCheckRepository: mockRepo,
|
|
590
646
|
});
|
|
591
647
|
const result = await useCase.execute();
|
|
592
648
|
expect(result.status).toBe('unhealthy');
|
|
@@ -595,12 +651,13 @@ describe('CheckDatabaseHealthUseCase', () => {
|
|
|
595
651
|
```
|
|
596
652
|
|
|
597
653
|
**Handler Tests** (HTTP/Lambda response tests):
|
|
654
|
+
|
|
598
655
|
```javascript
|
|
599
656
|
describe('Health Handler', () => {
|
|
600
657
|
it('returns 503 when unhealthy', async () => {
|
|
601
658
|
// Mock use case
|
|
602
659
|
const mockUseCase = {
|
|
603
|
-
execute: async () => ({ status: 'unhealthy' })
|
|
660
|
+
execute: async () => ({ status: 'unhealthy' }),
|
|
604
661
|
};
|
|
605
662
|
// Test HTTP response
|
|
606
663
|
const response = await handler(mockEvent, mockContext);
|
|
@@ -612,6 +669,7 @@ describe('Health Handler', () => {
|
|
|
612
669
|
## Anti-Patterns to Avoid
|
|
613
670
|
|
|
614
671
|
### Core Runtime Anti-Patterns
|
|
672
|
+
|
|
615
673
|
❌ **Don't expose internal errors** to user-facing endpoints - use `isUserFacingResponse: true`
|
|
616
674
|
❌ **Don't skip connection optimization** - always set `callbackWaitsForEmptyEventLoop = false`
|
|
617
675
|
❌ **Don't parallel process SQS records** - sequential processing prevents resource exhaustion
|
|
@@ -621,6 +679,7 @@ describe('Health Handler', () => {
|
|
|
621
679
|
❌ **Don't ignore delegate types** - define valid `delegateTypes` array for type safety
|
|
622
680
|
|
|
623
681
|
### DDD/Hexagonal Architecture Anti-Patterns
|
|
682
|
+
|
|
624
683
|
❌ **Don't access models directly in handlers** - create repositories to abstract data access
|
|
625
684
|
❌ **Don't put business logic in handlers** - extract to use cases
|
|
626
685
|
❌ **Don't call repositories from handlers** - always go through use cases
|
|
@@ -632,13 +691,14 @@ describe('Health Handler', () => {
|
|
|
632
691
|
## Testing Patterns
|
|
633
692
|
|
|
634
693
|
### Handler Testing
|
|
694
|
+
|
|
635
695
|
```javascript
|
|
636
696
|
const { createHandler } = require('@friggframework/core/core');
|
|
637
697
|
|
|
638
698
|
const testHandler = createHandler({
|
|
639
699
|
isUserFacingResponse: false, // Get full errors in tests
|
|
640
|
-
shouldUseDatabase: false,
|
|
641
|
-
method: yourTestMethod
|
|
700
|
+
shouldUseDatabase: false, // Mock/skip DB in tests
|
|
701
|
+
method: yourTestMethod,
|
|
642
702
|
});
|
|
643
703
|
|
|
644
704
|
// Test with mock event/context
|
|
@@ -646,12 +706,13 @@ const result = await testHandler(mockEvent, mockContext);
|
|
|
646
706
|
```
|
|
647
707
|
|
|
648
708
|
### Worker Testing
|
|
709
|
+
|
|
649
710
|
```javascript
|
|
650
711
|
class TestWorker extends Worker {
|
|
651
712
|
_validateParams(params) {
|
|
652
713
|
this._verifyParamExists(params, 'testField');
|
|
653
714
|
}
|
|
654
|
-
|
|
715
|
+
|
|
655
716
|
async _run(params, context) {
|
|
656
717
|
// Your test logic
|
|
657
718
|
return { processed: true };
|
|
@@ -661,30 +722,35 @@ class TestWorker extends Worker {
|
|
|
661
722
|
// Test SQS record processing
|
|
662
723
|
const worker = new TestWorker();
|
|
663
724
|
await worker.run({
|
|
664
|
-
Records: [
|
|
665
|
-
|
|
666
|
-
|
|
725
|
+
Records: [
|
|
726
|
+
{
|
|
727
|
+
body: JSON.stringify({ testField: 'value' }),
|
|
728
|
+
},
|
|
729
|
+
],
|
|
667
730
|
});
|
|
668
731
|
```
|
|
669
732
|
|
|
670
733
|
## Environment Variables
|
|
671
734
|
|
|
672
735
|
### Required Variables
|
|
673
|
-
|
|
674
|
-
-
|
|
736
|
+
|
|
737
|
+
- `AWS_REGION`: AWS region for SQS operations
|
|
738
|
+
- `SECRET_ARN`: (Optional) AWS Secrets Manager secret ARN for automatic injection
|
|
675
739
|
|
|
676
740
|
### Database Variables
|
|
677
|
-
|
|
678
|
-
-
|
|
741
|
+
|
|
742
|
+
- MongoDB connection variables (handled by `../database/mongo`)
|
|
743
|
+
- See database module documentation for complete list
|
|
679
744
|
|
|
680
745
|
### Queue Variables
|
|
681
|
-
|
|
682
|
-
-
|
|
746
|
+
|
|
747
|
+
- Queue URLs typically passed as parameters, not environment variables
|
|
748
|
+
- Use Worker's `getQueueURL()` method for dynamic queue discovery
|
|
683
749
|
|
|
684
750
|
## Security Considerations
|
|
685
751
|
|
|
686
|
-
-
|
|
687
|
-
-
|
|
688
|
-
-
|
|
689
|
-
-
|
|
690
|
-
-
|
|
752
|
+
- **Secrets**: Never log or expose secrets in error messages
|
|
753
|
+
- **Error Messages**: Always sanitize errors for user-facing responses
|
|
754
|
+
- **Database**: Connection pooling reuses connections securely
|
|
755
|
+
- **SQS**: Message validation prevents injection attacks
|
|
756
|
+
- **Logging**: Debug logs include sensitive data - handle carefully in production
|