@friggframework/devtools 2.0.0--canary.654.3cd4278.0 → 2.0.0--canary.651.dadf4d6.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/frigg-cli/init-command/backend-first-handler.js +1 -1
- package/frigg-cli/package.json +1 -1
- package/infrastructure/domains/parameters/offload-utils.js +0 -7
- package/infrastructure/domains/parameters/offload-utils.test.js +0 -13
- package/infrastructure/domains/security/iam-generator.js +0 -1
- package/infrastructure/domains/security/iam-generator.test.js +1 -30
- package/infrastructure/domains/security/templates/frigg-deployment-iam-stack.yaml +0 -1
- package/infrastructure/domains/security/templates/iam-policy-basic.json +1 -2
- package/infrastructure/domains/security/templates/iam-policy-full.json +1 -2
- package/infrastructure/domains/shared/environment-builder.js +0 -17
- package/infrastructure/domains/shared/environment-builder.test.js +0 -61
- package/infrastructure/domains/shared/utilities/base-definition-factory.js +1 -4
- package/infrastructure/domains/shared/utilities/base-definition-factory.js.bak +338 -0
- package/infrastructure/domains/shared/utilities/base-definition-factory.test.js +0 -51
- package/infrastructure/infrastructure-composer.test.js +1 -45
- package/package.json +8 -8
- package/infrastructure/domains/shared/utilities/logging-config.js +0 -82
- package/infrastructure/domains/shared/utilities/logging-config.test.js +0 -52
package/frigg-cli/package.json
CHANGED
|
@@ -53,13 +53,6 @@ const FRAMEWORK_ENV_BLOCKLIST = new Set([
|
|
|
53
53
|
'FRIGG_SSM_OFFLOADED_KEYS',
|
|
54
54
|
'FRIGG_SSM_CACHE_TTL',
|
|
55
55
|
'WORKER_FUNCTION_NAME',
|
|
56
|
-
// Read by the logger, telemetry flush and Prisma at INIT, before SSM loads
|
|
57
|
-
'FRIGG_LOG_LEVEL',
|
|
58
|
-
'AWS_LAMBDA_LOG_LEVEL',
|
|
59
|
-
'AWS_LAMBDA_LOG_FORMAT',
|
|
60
|
-
'OTEL_FLUSH_TIMEOUT_MS',
|
|
61
|
-
'DEBUG_VERBOSE',
|
|
62
|
-
'PRISMA_LOG_LEVEL',
|
|
63
56
|
// Reserved AWS Lambda runtime variables
|
|
64
57
|
'_HANDLER',
|
|
65
58
|
'_X_AMZN_TRACE_ID',
|
|
@@ -190,17 +190,4 @@ describe('offload-utils', () => {
|
|
|
190
190
|
expect(FRAMEWORK_ENV_BLOCKLIST.has(key)).toBe(true);
|
|
191
191
|
}
|
|
192
192
|
});
|
|
193
|
-
|
|
194
|
-
it('blocklist covers the logger keys read at INIT (ADR-048)', () => {
|
|
195
|
-
for (const key of [
|
|
196
|
-
'FRIGG_LOG_LEVEL',
|
|
197
|
-
'AWS_LAMBDA_LOG_LEVEL',
|
|
198
|
-
'AWS_LAMBDA_LOG_FORMAT',
|
|
199
|
-
'OTEL_FLUSH_TIMEOUT_MS',
|
|
200
|
-
'DEBUG_VERBOSE',
|
|
201
|
-
'PRISMA_LOG_LEVEL',
|
|
202
|
-
]) {
|
|
203
|
-
expect(FRAMEWORK_ENV_BLOCKLIST.has(key)).toBe(true);
|
|
204
|
-
}
|
|
205
|
-
});
|
|
206
193
|
});
|
|
@@ -1,11 +1,4 @@
|
|
|
1
|
-
const
|
|
2
|
-
const path = require('path');
|
|
3
|
-
const {
|
|
4
|
-
generateIAMCloudFormation,
|
|
5
|
-
getFeatureSummary,
|
|
6
|
-
generateBasicIAMPolicy,
|
|
7
|
-
generateFullIAMPolicy,
|
|
8
|
-
} = require('./iam-generator');
|
|
1
|
+
const { generateIAMCloudFormation, getFeatureSummary } = require('./iam-generator');
|
|
9
2
|
|
|
10
3
|
describe('IAM Generator', () => {
|
|
11
4
|
describe('getFeatureSummary', () => {
|
|
@@ -257,28 +250,6 @@ describe('IAM Generator', () => {
|
|
|
257
250
|
expect(yaml).toContain('iam:ListPolicyVersions');
|
|
258
251
|
});
|
|
259
252
|
|
|
260
|
-
it('should grant logs:DeleteRetentionPolicy so logging.retentionInDays can be removed', () => {
|
|
261
|
-
const yaml = generateIAMCloudFormation({
|
|
262
|
-
appName: 'test-app',
|
|
263
|
-
features: getFeatureSummary({ name: 'test-app' }).features,
|
|
264
|
-
});
|
|
265
|
-
|
|
266
|
-
expect(yaml).toContain('logs:PutRetentionPolicy');
|
|
267
|
-
expect(yaml).toContain('logs:DeleteRetentionPolicy');
|
|
268
|
-
});
|
|
269
|
-
|
|
270
|
-
it('should grant logs:DeleteRetentionPolicy in the static policy templates', () => {
|
|
271
|
-
for (const policy of [generateBasicIAMPolicy(), generateFullIAMPolicy()]) {
|
|
272
|
-
const actions = policy.Statement.flatMap((statement) => statement.Action);
|
|
273
|
-
expect(actions).toContain('logs:DeleteRetentionPolicy');
|
|
274
|
-
}
|
|
275
|
-
const stackYaml = fs.readFileSync(
|
|
276
|
-
path.join(__dirname, 'templates/frigg-deployment-iam-stack.yaml'),
|
|
277
|
-
'utf8'
|
|
278
|
-
);
|
|
279
|
-
expect(stackYaml).toContain("'logs:DeleteRetentionPolicy'");
|
|
280
|
-
});
|
|
281
|
-
|
|
282
253
|
it('should include internal-error-queue pattern in SQS resources', () => {
|
|
283
254
|
const appDefinition = {
|
|
284
255
|
name: 'test-app',
|
|
@@ -238,7 +238,6 @@ Resources:
|
|
|
238
238
|
- 'logs:FilterLogEvents'
|
|
239
239
|
- 'logs:PutLogEvents'
|
|
240
240
|
- 'logs:PutRetentionPolicy'
|
|
241
|
-
- 'logs:DeleteRetentionPolicy'
|
|
242
241
|
Resource:
|
|
243
242
|
- !Sub 'arn:aws:logs:*:${AWS::AccountId}:log-group:/aws/lambda/*frigg*'
|
|
244
243
|
- !Sub 'arn:aws:logs:*:${AWS::AccountId}:log-group:/aws/lambda/*frigg*:*'
|
|
@@ -183,8 +183,7 @@
|
|
|
183
183
|
"logs:DescribeLogStreams",
|
|
184
184
|
"logs:FilterLogEvents",
|
|
185
185
|
"logs:PutLogEvents",
|
|
186
|
-
"logs:PutRetentionPolicy"
|
|
187
|
-
"logs:DeleteRetentionPolicy"
|
|
186
|
+
"logs:PutRetentionPolicy"
|
|
188
187
|
],
|
|
189
188
|
"Resource": [
|
|
190
189
|
"arn:aws:logs:*:*:log-group:/aws/lambda/*frigg*",
|
|
@@ -183,8 +183,7 @@
|
|
|
183
183
|
"logs:DescribeLogStreams",
|
|
184
184
|
"logs:FilterLogEvents",
|
|
185
185
|
"logs:PutLogEvents",
|
|
186
|
-
"logs:PutRetentionPolicy"
|
|
187
|
-
"logs:DeleteRetentionPolicy"
|
|
186
|
+
"logs:PutRetentionPolicy"
|
|
188
187
|
],
|
|
189
188
|
"Resource": [
|
|
190
189
|
"arn:aws:logs:*:*:log-group:/aws/lambda/*frigg*",
|
|
@@ -13,7 +13,6 @@ const {
|
|
|
13
13
|
isSsmOffloadActive,
|
|
14
14
|
getOffloadedKeys,
|
|
15
15
|
} = require('../parameters/offload-utils');
|
|
16
|
-
const { normalizeLogLevel } = require('./utilities/logging-config');
|
|
17
16
|
|
|
18
17
|
// OTLP-family exporters read their endpoint/headers from these standard env
|
|
19
18
|
// vars (ADR-011). When such an exporter is configured we auto-register them as
|
|
@@ -45,20 +44,6 @@ function addTelemetryEnvPassthrough(appDefinition, envVars) {
|
|
|
45
44
|
}
|
|
46
45
|
}
|
|
47
46
|
|
|
48
|
-
// ADR-048 §12: logging.level wins over an `environment` passthrough. INFO is
|
|
49
|
-
// the core default, so it emits no env var.
|
|
50
|
-
function addLoggingEnv(appDefinition, envVars) {
|
|
51
|
-
const level = appDefinition?.logging?.level;
|
|
52
|
-
if (level === undefined) return;
|
|
53
|
-
|
|
54
|
-
const normalized = normalizeLogLevel(level);
|
|
55
|
-
if (normalized === 'INFO') {
|
|
56
|
-
delete envVars.FRIGG_LOG_LEVEL;
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
envVars.FRIGG_LOG_LEVEL = normalized;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
47
|
/**
|
|
63
48
|
* Get environment variables from AppDefinition
|
|
64
49
|
*
|
|
@@ -135,8 +120,6 @@ function getAppEnvironmentVars(appDefinition) {
|
|
|
135
120
|
envKeys.push(key);
|
|
136
121
|
}
|
|
137
122
|
|
|
138
|
-
addLoggingEnv(appDefinition, envVars);
|
|
139
|
-
|
|
140
123
|
if (envKeys.length > 0) {
|
|
141
124
|
console.log(
|
|
142
125
|
` Found ${envKeys.length} environment variables: ${envKeys.join(
|
|
@@ -147,67 +147,6 @@ describe('Environment Builder', () => {
|
|
|
147
147
|
});
|
|
148
148
|
});
|
|
149
149
|
|
|
150
|
-
describe('logging env (ADR-048)', () => {
|
|
151
|
-
it.each(['debug', 'WARN', 'Error'])(
|
|
152
|
-
'emits literal upper-case FRIGG_LOG_LEVEL for level "%s"',
|
|
153
|
-
(level) => {
|
|
154
|
-
const result = getAppEnvironmentVars({ logging: { level } });
|
|
155
|
-
|
|
156
|
-
expect(result.FRIGG_LOG_LEVEL).toBe(level.toUpperCase());
|
|
157
|
-
}
|
|
158
|
-
);
|
|
159
|
-
|
|
160
|
-
it.each(['info', 'INFO'])(
|
|
161
|
-
'emits nothing for the default level "%s"',
|
|
162
|
-
(level) => {
|
|
163
|
-
const result = getAppEnvironmentVars({ logging: { level } });
|
|
164
|
-
|
|
165
|
-
expect(result).not.toHaveProperty('FRIGG_LOG_LEVEL');
|
|
166
|
-
}
|
|
167
|
-
);
|
|
168
|
-
|
|
169
|
-
it('emits nothing when logging or logging.level is absent', () => {
|
|
170
|
-
expect(getAppEnvironmentVars({})).not.toHaveProperty(
|
|
171
|
-
'FRIGG_LOG_LEVEL'
|
|
172
|
-
);
|
|
173
|
-
expect(
|
|
174
|
-
getAppEnvironmentVars({ logging: { retentionInDays: 30 } })
|
|
175
|
-
).not.toHaveProperty('FRIGG_LOG_LEVEL');
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
it('lets logging.level win over an environment passthrough', () => {
|
|
179
|
-
const result = getAppEnvironmentVars({
|
|
180
|
-
environment: { FRIGG_LOG_LEVEL: true },
|
|
181
|
-
logging: { level: 'error' },
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
expect(result.FRIGG_LOG_LEVEL).toBe('ERROR');
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
it('drops an environment passthrough when logging.level is INFO', () => {
|
|
188
|
-
const result = getAppEnvironmentVars({
|
|
189
|
-
environment: { FRIGG_LOG_LEVEL: true },
|
|
190
|
-
logging: { level: 'info' },
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
expect(result).not.toHaveProperty('FRIGG_LOG_LEVEL');
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
it('keeps an environment passthrough when logging.level is absent', () => {
|
|
197
|
-
const result = getAppEnvironmentVars({
|
|
198
|
-
environment: { FRIGG_LOG_LEVEL: true },
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
expect(result.FRIGG_LOG_LEVEL).toBe("${env:FRIGG_LOG_LEVEL, ''}");
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
it('throws on an unknown level', () => {
|
|
205
|
-
expect(() =>
|
|
206
|
-
getAppEnvironmentVars({ logging: { level: 'verbose' } })
|
|
207
|
-
).toThrow(/logging\.level/);
|
|
208
|
-
});
|
|
209
|
-
});
|
|
210
|
-
|
|
211
150
|
describe("getAppEnvironmentVars() - 'ssm' offload", () => {
|
|
212
151
|
const originalSkipDiscovery = process.env.FRIGG_SKIP_AWS_DISCOVERY;
|
|
213
152
|
|
|
@@ -9,7 +9,6 @@
|
|
|
9
9
|
|
|
10
10
|
const { buildEnvironment } = require('../environment-builder');
|
|
11
11
|
const { nestedNodeModulesExcludes } = require('./nested-node-modules');
|
|
12
|
-
const { buildLoggingProviderConfig } = require('./logging-config');
|
|
13
12
|
|
|
14
13
|
/**
|
|
15
14
|
* Create base serverless definition with core functions and resources
|
|
@@ -34,7 +33,6 @@ function createBaseDefinition(
|
|
|
34
33
|
usePrismaLayer = true
|
|
35
34
|
) {
|
|
36
35
|
const region = process.env.AWS_REGION || 'us-east-1';
|
|
37
|
-
const loggingConfig = buildLoggingProviderConfig(AppDefinition.logging);
|
|
38
36
|
|
|
39
37
|
// Package config for handlers that skip esbuild (need node_modules dependencies)
|
|
40
38
|
// Include backend src/ and index.js since handlers load the app definition
|
|
@@ -166,7 +164,7 @@ function createBaseDefinition(
|
|
|
166
164
|
};
|
|
167
165
|
|
|
168
166
|
return {
|
|
169
|
-
frameworkVersion:
|
|
167
|
+
frameworkVersion: '>=3.17.0',
|
|
170
168
|
service: AppDefinition.name || 'create-frigg-app',
|
|
171
169
|
package: {
|
|
172
170
|
individually: true,
|
|
@@ -220,7 +218,6 @@ function createBaseDefinition(
|
|
|
220
218
|
name: '${opt:stage, "dev"}-${self:service}',
|
|
221
219
|
disableDefaultEndpoint: false,
|
|
222
220
|
},
|
|
223
|
-
...loggingConfig.provider,
|
|
224
221
|
},
|
|
225
222
|
plugins: [
|
|
226
223
|
'serverless-esbuild',
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base Serverless Definition Factory
|
|
3
|
+
*
|
|
4
|
+
* Utility Layer - Hexagonal Architecture
|
|
5
|
+
*
|
|
6
|
+
* Creates the base serverless.yml configuration with core functions,
|
|
7
|
+
* resources, plugins, and provider settings.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const { buildEnvironment } = require('../environment-builder');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Create base serverless definition with core functions and resources
|
|
14
|
+
*
|
|
15
|
+
* This creates the foundation serverless configuration that all
|
|
16
|
+
* Frigg applications need, including:
|
|
17
|
+
* - Core Lambda functions (auth, user, health, dbMigrate)
|
|
18
|
+
* - Error handling infrastructure (SQS, SNS, CloudWatch)
|
|
19
|
+
* - Prisma Lambda Layer
|
|
20
|
+
* - Base plugins and esbuild configuration
|
|
21
|
+
*
|
|
22
|
+
* @param {Object} AppDefinition - Application definition
|
|
23
|
+
* @param {Object} appEnvironmentVars - Environment variables from app definition
|
|
24
|
+
* @param {Object} discoveredResources - AWS resources discovered during build
|
|
25
|
+
* @returns {Object} Base serverless definition
|
|
26
|
+
*/
|
|
27
|
+
function createBaseDefinition(
|
|
28
|
+
AppDefinition,
|
|
29
|
+
appEnvironmentVars,
|
|
30
|
+
discoveredResources
|
|
31
|
+
) {
|
|
32
|
+
const region = process.env.AWS_REGION || 'us-east-1';
|
|
33
|
+
|
|
34
|
+
// Function-level package config to exclude Prisma and AWS SDK
|
|
35
|
+
const functionPackageConfig = {
|
|
36
|
+
exclude: [
|
|
37
|
+
// Exclude AWS SDK (already in Lambda runtime or externalized by esbuild)
|
|
38
|
+
'node_modules/aws-sdk/**',
|
|
39
|
+
'node_modules/@aws-sdk/**',
|
|
40
|
+
|
|
41
|
+
// Exclude Prisma (provided via Lambda Layer)
|
|
42
|
+
'node_modules/@prisma/**',
|
|
43
|
+
'node_modules/.prisma/**',
|
|
44
|
+
'node_modules/prisma/**',
|
|
45
|
+
'node_modules/@friggframework/core/generated/**',
|
|
46
|
+
|
|
47
|
+
// Exclude nested node_modules from symlinked frigg packages (for npm link development)
|
|
48
|
+
'node_modules/@friggframework/core/node_modules/**',
|
|
49
|
+
'node_modules/@friggframework/devtools/node_modules/**',
|
|
50
|
+
|
|
51
|
+
// Exclude development/test files from backend project
|
|
52
|
+
'coverage/**',
|
|
53
|
+
'test/**',
|
|
54
|
+
'src/**',
|
|
55
|
+
'layers/**',
|
|
56
|
+
'**/*.test.js',
|
|
57
|
+
'**/*.spec.js',
|
|
58
|
+
'.git/**',
|
|
59
|
+
'.github/**',
|
|
60
|
+
],
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
frameworkVersion: '>=3.17.0',
|
|
65
|
+
service: AppDefinition.name || 'create-frigg-app',
|
|
66
|
+
package: {
|
|
67
|
+
individually: true,
|
|
68
|
+
},
|
|
69
|
+
useDotenv: true,
|
|
70
|
+
provider: {
|
|
71
|
+
name: AppDefinition.provider || 'aws',
|
|
72
|
+
...(process.env.AWS_PROFILE && { profile: process.env.AWS_PROFILE }),
|
|
73
|
+
runtime: 'nodejs22.x', // Node.js 22.x (latest Lambda runtime with AWS SDK v3)
|
|
74
|
+
timeout: 29, // Set to 29s to give buffer before API Gateway's 30s timeout
|
|
75
|
+
region,
|
|
76
|
+
stage: '${opt:stage}',
|
|
77
|
+
environment: buildEnvironment(appEnvironmentVars, discoveredResources),
|
|
78
|
+
iamRoleStatements: [
|
|
79
|
+
{
|
|
80
|
+
Effect: 'Allow',
|
|
81
|
+
Action: ['sns:Publish'],
|
|
82
|
+
Resource: { Ref: 'InternalErrorBridgeTopic' },
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
Effect: 'Allow',
|
|
86
|
+
Action: [
|
|
87
|
+
'sqs:SendMessage',
|
|
88
|
+
'sqs:SendMessageBatch',
|
|
89
|
+
'sqs:GetQueueUrl',
|
|
90
|
+
'sqs:GetQueueAttributes',
|
|
91
|
+
],
|
|
92
|
+
Resource: [
|
|
93
|
+
{ 'Fn::GetAtt': ['InternalErrorQueue', 'Arn'] },
|
|
94
|
+
{
|
|
95
|
+
'Fn::Join': [
|
|
96
|
+
':',
|
|
97
|
+
[
|
|
98
|
+
'arn:aws:sqs:${self:provider.region}:*:${self:service}--${self:provider.stage}-*Queue',
|
|
99
|
+
],
|
|
100
|
+
],
|
|
101
|
+
},
|
|
102
|
+
],
|
|
103
|
+
},
|
|
104
|
+
],
|
|
105
|
+
httpApi: {
|
|
106
|
+
payload: '2.0',
|
|
107
|
+
cors: {
|
|
108
|
+
allowedOrigins: ['*'],
|
|
109
|
+
allowedHeaders: ['*'],
|
|
110
|
+
allowedMethods: ['*'],
|
|
111
|
+
allowCredentials: false,
|
|
112
|
+
},
|
|
113
|
+
name: '${opt:stage, "dev"}-${self:service}',
|
|
114
|
+
disableDefaultEndpoint: false,
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
plugins: [
|
|
118
|
+
'serverless-esbuild',
|
|
119
|
+
'serverless-dotenv-plugin',
|
|
120
|
+
'serverless-offline-sqs',
|
|
121
|
+
'serverless-offline',
|
|
122
|
+
'@friggframework/serverless-plugin',
|
|
123
|
+
],
|
|
124
|
+
custom: {
|
|
125
|
+
esbuild: {
|
|
126
|
+
bundle: true,
|
|
127
|
+
minify: true,
|
|
128
|
+
sourcemap: true,
|
|
129
|
+
target: 'node22',
|
|
130
|
+
platform: 'node',
|
|
131
|
+
format: 'cjs',
|
|
132
|
+
external: [
|
|
133
|
+
'@aws-sdk/*',
|
|
134
|
+
'aws-sdk',
|
|
135
|
+
'@prisma/client',
|
|
136
|
+
'prisma',
|
|
137
|
+
'.prisma/*',
|
|
138
|
+
],
|
|
139
|
+
packager: 'npm',
|
|
140
|
+
keepNames: true,
|
|
141
|
+
keepOutputDirectory: false, // Clean up .esbuild directory after packaging
|
|
142
|
+
exclude: [
|
|
143
|
+
'aws-sdk',
|
|
144
|
+
'@aws-sdk/*',
|
|
145
|
+
'@prisma/client',
|
|
146
|
+
'prisma',
|
|
147
|
+
],
|
|
148
|
+
},
|
|
149
|
+
'serverless-offline': {
|
|
150
|
+
httpPort: 3001,
|
|
151
|
+
lambdaPort: 4001,
|
|
152
|
+
websocketPort: 3002,
|
|
153
|
+
location: '.', // Set base directory for handler resolution to current directory
|
|
154
|
+
skipCacheInvalidation: false,
|
|
155
|
+
},
|
|
156
|
+
'serverless-offline-sqs': {
|
|
157
|
+
autoCreate: false,
|
|
158
|
+
apiVersion: '2012-11-05',
|
|
159
|
+
endpoint: 'http://localhost:4566',
|
|
160
|
+
region,
|
|
161
|
+
accessKeyId: 'root',
|
|
162
|
+
secretAccessKey: 'root',
|
|
163
|
+
skipCacheInvalidation: false,
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
functions: {
|
|
167
|
+
auth: {
|
|
168
|
+
handler: 'node_modules/@friggframework/core/handlers/routers/auth.handler',
|
|
169
|
+
layers: [{ Ref: 'PrismaLambdaLayer' }],
|
|
170
|
+
skipEsbuild: true, // Handlers in node_modules don't need bundling
|
|
171
|
+
package: functionPackageConfig,
|
|
172
|
+
events: [
|
|
173
|
+
{ httpApi: { path: '/api/integrations', method: 'ANY' } },
|
|
174
|
+
{
|
|
175
|
+
httpApi: {
|
|
176
|
+
path: '/api/integrations/{proxy+}',
|
|
177
|
+
method: 'ANY',
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
{ httpApi: { path: '/api/authorize', method: 'ANY' } },
|
|
181
|
+
],
|
|
182
|
+
},
|
|
183
|
+
user: {
|
|
184
|
+
handler: 'node_modules/@friggframework/core/handlers/routers/user.handler',
|
|
185
|
+
layers: [{ Ref: 'PrismaLambdaLayer' }],
|
|
186
|
+
skipEsbuild: true, // Handlers in node_modules don't need bundling
|
|
187
|
+
package: functionPackageConfig,
|
|
188
|
+
events: [{ httpApi: { path: '/user/{proxy+}', method: 'ANY' } }],
|
|
189
|
+
},
|
|
190
|
+
health: {
|
|
191
|
+
handler: 'node_modules/@friggframework/core/handlers/routers/health.handler',
|
|
192
|
+
layers: [{ Ref: 'PrismaLambdaLayer' }],
|
|
193
|
+
skipEsbuild: true, // Handlers in node_modules don't need bundling
|
|
194
|
+
package: functionPackageConfig,
|
|
195
|
+
events: [
|
|
196
|
+
{ httpApi: { path: '/health', method: 'GET' } },
|
|
197
|
+
{ httpApi: { path: '/health/{proxy+}', method: 'GET' } },
|
|
198
|
+
],
|
|
199
|
+
},
|
|
200
|
+
dbMigrate: {
|
|
201
|
+
handler: 'node_modules/@friggframework/core/handlers/database-migration-handler.handler',
|
|
202
|
+
// DO NOT use Prisma layer - this function includes Prisma CLI separately
|
|
203
|
+
skipEsbuild: true, // Handlers in node_modules don't need bundling
|
|
204
|
+
timeout: 300, // 5 minutes for long-running migrations
|
|
205
|
+
memorySize: 1024, // Extra memory for Prisma CLI and migration operations
|
|
206
|
+
reservedConcurrency: 1, // Prevent concurrent migrations (CRITICAL for data safety)
|
|
207
|
+
description: 'Runs database migrations via Prisma CLI (invoke manually from CI/CD or triggers). Prisma CLI bundled separately.',
|
|
208
|
+
package: {
|
|
209
|
+
individually: true,
|
|
210
|
+
patterns: [
|
|
211
|
+
// Include handler
|
|
212
|
+
'node_modules/@friggframework/core/handlers/database-migration-handler.js',
|
|
213
|
+
|
|
214
|
+
// Include ONLY PostgreSQL Prisma client (exclude MongoDB)
|
|
215
|
+
'node_modules/@friggframework/core/generated/prisma-postgresql/**',
|
|
216
|
+
'!node_modules/@friggframework/core/generated/prisma-mongodb/**', // Exclude MongoDB client entirely
|
|
217
|
+
|
|
218
|
+
// Include Prisma runtime
|
|
219
|
+
'node_modules/@prisma/client/**',
|
|
220
|
+
'node_modules/.prisma/**',
|
|
221
|
+
'node_modules/prisma/**', // Prisma CLI
|
|
222
|
+
|
|
223
|
+
// Exclude unnecessary engines and files
|
|
224
|
+
'!node_modules/prisma/node_modules/**',
|
|
225
|
+
'!**/query-engine-darwin*', // Exclude macOS binaries (keep rhel for Lambda)
|
|
226
|
+
'!**/runtime/*.wasm', // WASM engines
|
|
227
|
+
'!**/*.md',
|
|
228
|
+
'!**/*.map',
|
|
229
|
+
'!**/LICENSE*',
|
|
230
|
+
'!**/*.d.ts',
|
|
231
|
+
'!**/*.d.mts',
|
|
232
|
+
],
|
|
233
|
+
},
|
|
234
|
+
maximumEventAge: 60,
|
|
235
|
+
maximumRetryAttempts: 0,
|
|
236
|
+
tags: {
|
|
237
|
+
Purpose: 'DatabaseMigration',
|
|
238
|
+
ManagedBy: 'Frigg',
|
|
239
|
+
},
|
|
240
|
+
environment: {
|
|
241
|
+
CI: '1',
|
|
242
|
+
PRISMA_HIDE_UPDATE_MESSAGE: '1',
|
|
243
|
+
PRISMA_MIGRATE_SKIP_SEED: '1',
|
|
244
|
+
},
|
|
245
|
+
},
|
|
246
|
+
},
|
|
247
|
+
layers: {
|
|
248
|
+
prisma: {
|
|
249
|
+
path: 'layers/prisma',
|
|
250
|
+
name: '${self:service}-prisma-${sls:stage}',
|
|
251
|
+
description: 'Prisma runtime client only (NO CLI) with rhel-openssl-3.0.x binaries (~10-15MB). CLI packaged separately in dbMigrate function.',
|
|
252
|
+
compatibleRuntimes: ['nodejs20.x', 'nodejs22.x'],
|
|
253
|
+
retain: false,
|
|
254
|
+
},
|
|
255
|
+
},
|
|
256
|
+
resources: {
|
|
257
|
+
Resources: {
|
|
258
|
+
InternalErrorQueue: {
|
|
259
|
+
Type: 'AWS::SQS::Queue',
|
|
260
|
+
Properties: {
|
|
261
|
+
QueueName:
|
|
262
|
+
'${self:service}-internal-error-queue-${self:provider.stage}',
|
|
263
|
+
MessageRetentionPeriod: 300,
|
|
264
|
+
},
|
|
265
|
+
},
|
|
266
|
+
InternalErrorBridgeTopic: {
|
|
267
|
+
Type: 'AWS::SNS::Topic',
|
|
268
|
+
Properties: {
|
|
269
|
+
Subscription: [
|
|
270
|
+
{
|
|
271
|
+
Protocol: 'sqs',
|
|
272
|
+
Endpoint: {
|
|
273
|
+
'Fn::GetAtt': ['InternalErrorQueue', 'Arn'],
|
|
274
|
+
},
|
|
275
|
+
},
|
|
276
|
+
],
|
|
277
|
+
},
|
|
278
|
+
},
|
|
279
|
+
InternalErrorBridgePolicy: {
|
|
280
|
+
Type: 'AWS::SQS::QueuePolicy',
|
|
281
|
+
Properties: {
|
|
282
|
+
Queues: [{ Ref: 'InternalErrorQueue' }],
|
|
283
|
+
PolicyDocument: {
|
|
284
|
+
Version: '2012-10-17',
|
|
285
|
+
Statement: [
|
|
286
|
+
{
|
|
287
|
+
Sid: 'Allow Dead Letter SNS to publish to SQS',
|
|
288
|
+
Effect: 'Allow',
|
|
289
|
+
Principal: { Service: 'sns.amazonaws.com' },
|
|
290
|
+
Resource: {
|
|
291
|
+
'Fn::GetAtt': [
|
|
292
|
+
'InternalErrorQueue',
|
|
293
|
+
'Arn',
|
|
294
|
+
],
|
|
295
|
+
},
|
|
296
|
+
Action: [
|
|
297
|
+
'SQS:SendMessage',
|
|
298
|
+
'SQS:SendMessageBatch',
|
|
299
|
+
],
|
|
300
|
+
Condition: {
|
|
301
|
+
ArnEquals: {
|
|
302
|
+
'aws:SourceArn': {
|
|
303
|
+
Ref: 'InternalErrorBridgeTopic',
|
|
304
|
+
},
|
|
305
|
+
},
|
|
306
|
+
},
|
|
307
|
+
},
|
|
308
|
+
],
|
|
309
|
+
},
|
|
310
|
+
},
|
|
311
|
+
},
|
|
312
|
+
ApiGatewayAlarm5xx: {
|
|
313
|
+
Type: 'AWS::CloudWatch::Alarm',
|
|
314
|
+
Properties: {
|
|
315
|
+
AlarmDescription: 'API Gateway 5xx Errors',
|
|
316
|
+
Namespace: 'AWS/ApiGateway',
|
|
317
|
+
MetricName: '5XXError',
|
|
318
|
+
Statistic: 'Sum',
|
|
319
|
+
Threshold: 0,
|
|
320
|
+
ComparisonOperator: 'GreaterThanThreshold',
|
|
321
|
+
EvaluationPeriods: 1,
|
|
322
|
+
Period: 60,
|
|
323
|
+
AlarmActions: [{ Ref: 'InternalErrorBridgeTopic' }],
|
|
324
|
+
Dimensions: [
|
|
325
|
+
{ Name: 'ApiId', Value: { Ref: 'HttpApi' } },
|
|
326
|
+
{ Name: 'Stage', Value: '${self:provider.stage}' },
|
|
327
|
+
],
|
|
328
|
+
},
|
|
329
|
+
},
|
|
330
|
+
},
|
|
331
|
+
},
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
module.exports = {
|
|
336
|
+
createBaseDefinition,
|
|
337
|
+
};
|
|
338
|
+
|
|
@@ -317,54 +317,3 @@ describe('nested node_modules (lambda.keepNestedNodeModules)', () => {
|
|
|
317
317
|
}
|
|
318
318
|
});
|
|
319
319
|
});
|
|
320
|
-
|
|
321
|
-
describe('logging (ADR-048)', () => {
|
|
322
|
-
const build = (logging) =>
|
|
323
|
-
createBaseDefinition({ name: 'test-app', ...(logging && { logging }) }, {}, {});
|
|
324
|
-
|
|
325
|
-
it('emits no logs, logRetentionInDays or frameworkVersion change when logging is absent', () => {
|
|
326
|
-
const result = build();
|
|
327
|
-
|
|
328
|
-
expect(result.provider.logs).toBeUndefined();
|
|
329
|
-
expect(result.provider.logRetentionInDays).toBeUndefined();
|
|
330
|
-
expect(result.frameworkVersion).toBe('>=3.17.0');
|
|
331
|
-
});
|
|
332
|
-
|
|
333
|
-
it('validates logging.level but leaves the Lambda log format and frameworkVersion alone by default', () => {
|
|
334
|
-
const result = build({ level: 'warn' });
|
|
335
|
-
|
|
336
|
-
expect(result.provider.logs).toBeUndefined();
|
|
337
|
-
expect(result.frameworkVersion).toBe('>=3.17.0');
|
|
338
|
-
});
|
|
339
|
-
|
|
340
|
-
it.each(['verbose', 'warning', '', 3])('throws on unknown level %p', (level) => {
|
|
341
|
-
expect(() => build({ level })).toThrow(/logging\.level/);
|
|
342
|
-
});
|
|
343
|
-
|
|
344
|
-
it('maps retentionInDays to provider.logRetentionInDays without a logs block', () => {
|
|
345
|
-
const result = build({ retentionInDays: 30 });
|
|
346
|
-
|
|
347
|
-
expect(result.provider.logRetentionInDays).toBe(30);
|
|
348
|
-
expect(result.provider.logs).toBeUndefined();
|
|
349
|
-
expect(result.frameworkVersion).toBe('>=3.17.0');
|
|
350
|
-
});
|
|
351
|
-
|
|
352
|
-
it('accepts 1096, which CloudWatch allows but the osls schema omits', () => {
|
|
353
|
-
expect(build({ retentionInDays: 1096 }).provider.logRetentionInDays).toBe(1096);
|
|
354
|
-
});
|
|
355
|
-
|
|
356
|
-
it.each([10, 0, -1, 30.5, '30'])(
|
|
357
|
-
'throws on retentionInDays %p, which CloudWatch rejects',
|
|
358
|
-
(retentionInDays) => {
|
|
359
|
-
expect(() => build({ retentionInDays })).toThrow(
|
|
360
|
-
/logging\.retentionInDays/
|
|
361
|
-
);
|
|
362
|
-
}
|
|
363
|
-
);
|
|
364
|
-
|
|
365
|
-
it('sets retention when level is also set', () => {
|
|
366
|
-
const result = build({ level: 'debug', retentionInDays: 14 });
|
|
367
|
-
|
|
368
|
-
expect(result.provider.logRetentionInDays).toBe(14);
|
|
369
|
-
});
|
|
370
|
-
});
|
|
@@ -150,7 +150,7 @@ describe('composeServerlessDefinition', () => {
|
|
|
150
150
|
|
|
151
151
|
expect(result.service).toBe('test-app');
|
|
152
152
|
expect(result.provider.name).toBe('aws');
|
|
153
|
-
expect(result.provider.runtime).toBe('
|
|
153
|
+
expect(result.provider.runtime).toBe('nodejs20.x');
|
|
154
154
|
expect(result.provider.region).toBe('us-east-1');
|
|
155
155
|
expect(result.provider.stage).toBe('${opt:stage}');
|
|
156
156
|
expect(result.frameworkVersion).toBe('>=3.17.0');
|
|
@@ -238,50 +238,6 @@ describe('composeServerlessDefinition', () => {
|
|
|
238
238
|
});
|
|
239
239
|
});
|
|
240
240
|
|
|
241
|
-
describe('Logging (ADR-048)', () => {
|
|
242
|
-
it('carries logging into FRIGG_LOG_LEVEL and logRetentionInDays, with no Lambda LoggingConfig by default', async () => {
|
|
243
|
-
const result = await composeServerlessDefinition({
|
|
244
|
-
integrations: [mockIntegration],
|
|
245
|
-
logging: { level: 'debug', retentionInDays: 30 },
|
|
246
|
-
});
|
|
247
|
-
|
|
248
|
-
expect(result.frameworkVersion).toBe('>=3.17.0');
|
|
249
|
-
expect(result.provider.logs).toBeUndefined();
|
|
250
|
-
expect(result.provider.logRetentionInDays).toBe(30);
|
|
251
|
-
expect(result.provider.environment.FRIGG_LOG_LEVEL).toBe('DEBUG');
|
|
252
|
-
});
|
|
253
|
-
|
|
254
|
-
it('sets no function-level logs, which would replace the provider block', async () => {
|
|
255
|
-
const result = await composeServerlessDefinition({
|
|
256
|
-
integrations: [mockIntegration],
|
|
257
|
-
logging: { level: 'warn' },
|
|
258
|
-
});
|
|
259
|
-
|
|
260
|
-
const functions = Object.values(result.functions);
|
|
261
|
-
expect(functions.length).toBeGreaterThan(0);
|
|
262
|
-
expect(functions.every((fn) => fn.logs === undefined)).toBe(true);
|
|
263
|
-
});
|
|
264
|
-
|
|
265
|
-
it('lets logging.level win over environment: { FRIGG_LOG_LEVEL: true }', async () => {
|
|
266
|
-
const result = await composeServerlessDefinition({
|
|
267
|
-
integrations: [],
|
|
268
|
-
environment: { FRIGG_LOG_LEVEL: true },
|
|
269
|
-
logging: { level: 'error' },
|
|
270
|
-
});
|
|
271
|
-
|
|
272
|
-
expect(result.provider.environment.FRIGG_LOG_LEVEL).toBe('ERROR');
|
|
273
|
-
});
|
|
274
|
-
|
|
275
|
-
it('changes nothing when logging is absent', async () => {
|
|
276
|
-
const result = await composeServerlessDefinition({ integrations: [] });
|
|
277
|
-
|
|
278
|
-
expect(result.frameworkVersion).toBe('>=3.17.0');
|
|
279
|
-
expect(result.provider.logs).toBeUndefined();
|
|
280
|
-
expect(result.provider.logRetentionInDays).toBeUndefined();
|
|
281
|
-
expect(result.provider.environment).not.toHaveProperty('FRIGG_LOG_LEVEL');
|
|
282
|
-
});
|
|
283
|
-
});
|
|
284
|
-
|
|
285
241
|
describe('VPC Configuration', () => {
|
|
286
242
|
it('should add VPC configuration when vpc.enable is true with discover mode', async () => {
|
|
287
243
|
const appDefinition = {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@friggframework/devtools",
|
|
3
3
|
"prettier": "@friggframework/prettier-config",
|
|
4
|
-
"version": "2.0.0--canary.
|
|
4
|
+
"version": "2.0.0--canary.651.dadf4d6.0",
|
|
5
5
|
"bin": {
|
|
6
6
|
"frigg": "./frigg-cli/index.js"
|
|
7
7
|
},
|
|
@@ -26,9 +26,9 @@
|
|
|
26
26
|
"@babel/eslint-parser": "^7.18.9",
|
|
27
27
|
"@babel/parser": "^7.25.3",
|
|
28
28
|
"@babel/traverse": "^7.25.3",
|
|
29
|
-
"@friggframework/core": "2.0.0--canary.
|
|
30
|
-
"@friggframework/schemas": "2.0.0--canary.
|
|
31
|
-
"@friggframework/test": "2.0.0--canary.
|
|
29
|
+
"@friggframework/core": "2.0.0--canary.651.dadf4d6.0",
|
|
30
|
+
"@friggframework/schemas": "2.0.0--canary.651.dadf4d6.0",
|
|
31
|
+
"@friggframework/test": "2.0.0--canary.651.dadf4d6.0",
|
|
32
32
|
"@hapi/boom": "^10.0.1",
|
|
33
33
|
"@inquirer/prompts": "^5.3.8",
|
|
34
34
|
"axios": "^1.18.0",
|
|
@@ -56,12 +56,12 @@
|
|
|
56
56
|
"validate-npm-package-name": "^5.0.0"
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|
|
59
|
-
"@friggframework/eslint-config": "2.0.0--canary.
|
|
60
|
-
"@friggframework/prettier-config": "2.0.0--canary.
|
|
59
|
+
"@friggframework/eslint-config": "2.0.0--canary.651.dadf4d6.0",
|
|
60
|
+
"@friggframework/prettier-config": "2.0.0--canary.651.dadf4d6.0",
|
|
61
61
|
"aws-sdk-client-mock": "^4.1.0",
|
|
62
62
|
"aws-sdk-client-mock-jest": "^4.1.0",
|
|
63
63
|
"jest": "^30.1.3",
|
|
64
|
-
"osls": "^3.
|
|
64
|
+
"osls": "^3.40.1",
|
|
65
65
|
"prettier": "^2.7.1",
|
|
66
66
|
"serverless-dotenv-plugin": "^6.0.0",
|
|
67
67
|
"serverless-esbuild": "^1.54.3",
|
|
@@ -89,5 +89,5 @@
|
|
|
89
89
|
"publishConfig": {
|
|
90
90
|
"access": "public"
|
|
91
91
|
},
|
|
92
|
-
"gitHead": "
|
|
92
|
+
"gitHead": "dadf4d6a1a064d6ab883b486f2f851a58d093cf8"
|
|
93
93
|
}
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
const LOG_LEVELS = ['TRACE', 'DEBUG', 'INFO', 'WARN', 'ERROR', 'FATAL'];
|
|
2
|
-
|
|
3
|
-
// CloudWatch Logs PutRetentionPolicy values. The osls schema copy lacks 1096.
|
|
4
|
-
const RETENTION_DAYS = [
|
|
5
|
-
1, 3, 5, 7, 14, 30, 60, 90, 120, 150, 180, 365, 400, 545, 731, 1096, 1827,
|
|
6
|
-
2192, 2557, 2922, 3288, 3653,
|
|
7
|
-
];
|
|
8
|
-
|
|
9
|
-
// First osls version that compiles provider.logs.lambda into LoggingConfig.
|
|
10
|
-
const LOGGING_CONFIG_FRAMEWORK_VERSION = '>=3.58.0';
|
|
11
|
-
|
|
12
|
-
// ADR-048 Open question 3: off until the Phase 0 spike proves Lambda JSON mode
|
|
13
|
-
// keeps direct fd 1 lines intact. Until then Lambda stays on Text and
|
|
14
|
-
// FRIGG_LOG_LEVEL filters in process.
|
|
15
|
-
const EMIT_LAMBDA_JSON_LOGGING_CONFIG = false;
|
|
16
|
-
|
|
17
|
-
function normalizeLogLevel(level) {
|
|
18
|
-
const normalized = typeof level === 'string' ? level.toUpperCase() : level;
|
|
19
|
-
if (!LOG_LEVELS.includes(normalized)) {
|
|
20
|
-
throw new Error(
|
|
21
|
-
`Invalid logging.level ${JSON.stringify(level)}. Use one of: ${LOG_LEVELS.join(', ')} (any case).`
|
|
22
|
-
);
|
|
23
|
-
}
|
|
24
|
-
return normalized;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function validateRetentionInDays(retentionInDays) {
|
|
28
|
-
if (!RETENTION_DAYS.includes(retentionInDays)) {
|
|
29
|
-
throw new Error(
|
|
30
|
-
`Invalid logging.retentionInDays ${JSON.stringify(retentionInDays)}. CloudWatch accepts: ${RETENTION_DAYS.join(', ')}.`
|
|
31
|
-
);
|
|
32
|
-
}
|
|
33
|
-
return retentionInDays;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Map appDefinition.logging to provider-level serverless settings.
|
|
38
|
-
* Returns an empty object when logging is absent (ADR-048 §12: no change).
|
|
39
|
-
*
|
|
40
|
-
* @param {Object} [logging] - appDefinition.logging
|
|
41
|
-
* @param {{ lambdaJson?: boolean }} [options]
|
|
42
|
-
* @returns {{ frameworkVersion?: string, provider?: Object }}
|
|
43
|
-
*/
|
|
44
|
-
function buildLoggingProviderConfig(
|
|
45
|
-
logging,
|
|
46
|
-
{ lambdaJson = EMIT_LAMBDA_JSON_LOGGING_CONFIG } = {}
|
|
47
|
-
) {
|
|
48
|
-
if (!logging) return {};
|
|
49
|
-
|
|
50
|
-
const provider = {};
|
|
51
|
-
const result = {};
|
|
52
|
-
|
|
53
|
-
if (logging.level !== undefined) {
|
|
54
|
-
const level = normalizeLogLevel(logging.level);
|
|
55
|
-
if (lambdaJson) {
|
|
56
|
-
result.frameworkVersion = LOGGING_CONFIG_FRAMEWORK_VERSION;
|
|
57
|
-
provider.logs = {
|
|
58
|
-
lambda: {
|
|
59
|
-
logFormat: 'JSON',
|
|
60
|
-
applicationLogLevel: level,
|
|
61
|
-
systemLogLevel: 'INFO',
|
|
62
|
-
},
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
if (logging.retentionInDays !== undefined) {
|
|
68
|
-
provider.logRetentionInDays = validateRetentionInDays(
|
|
69
|
-
logging.retentionInDays
|
|
70
|
-
);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
if (Object.keys(provider).length > 0) result.provider = provider;
|
|
74
|
-
return result;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
module.exports = {
|
|
78
|
-
LOG_LEVELS,
|
|
79
|
-
RETENTION_DAYS,
|
|
80
|
-
normalizeLogLevel,
|
|
81
|
-
buildLoggingProviderConfig,
|
|
82
|
-
};
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
const { buildLoggingProviderConfig } = require('./logging-config');
|
|
2
|
-
|
|
3
|
-
describe('buildLoggingProviderConfig (ADR-048)', () => {
|
|
4
|
-
it('emits no Lambda LoggingConfig and no osls floor by default, until the Phase 0 spike passes', () => {
|
|
5
|
-
const result = buildLoggingProviderConfig({ level: 'warn' });
|
|
6
|
-
|
|
7
|
-
expect(result.provider).toBeUndefined();
|
|
8
|
-
expect(result.frameworkVersion).toBeUndefined();
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
it('still validates the level when the LoggingConfig is off', () => {
|
|
12
|
-
expect(() => buildLoggingProviderConfig({ level: 'verbose' })).toThrow(
|
|
13
|
-
/logging\.level/
|
|
14
|
-
);
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
describe('with lambdaJson', () => {
|
|
18
|
-
const build = (logging) =>
|
|
19
|
-
buildLoggingProviderConfig(logging, { lambdaJson: true });
|
|
20
|
-
|
|
21
|
-
it('maps level to provider.logs.lambda and sets frameworkVersion >=3.58.0', () => {
|
|
22
|
-
expect(build({ level: 'warn' })).toEqual({
|
|
23
|
-
frameworkVersion: '>=3.58.0',
|
|
24
|
-
provider: {
|
|
25
|
-
logs: {
|
|
26
|
-
lambda: {
|
|
27
|
-
logFormat: 'JSON',
|
|
28
|
-
applicationLogLevel: 'WARN',
|
|
29
|
-
systemLogLevel: 'INFO',
|
|
30
|
-
},
|
|
31
|
-
},
|
|
32
|
-
},
|
|
33
|
-
});
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
it.each(['info', 'INFO', 'Info', 'fatal', 'TRACE'])(
|
|
37
|
-
'accepts level "%s" in any case',
|
|
38
|
-
(level) => {
|
|
39
|
-
expect(
|
|
40
|
-
build({ level }).provider.logs.lambda.applicationLogLevel
|
|
41
|
-
).toBe(level.toUpperCase());
|
|
42
|
-
}
|
|
43
|
-
);
|
|
44
|
-
|
|
45
|
-
it('sets both level and retention together', () => {
|
|
46
|
-
const result = build({ level: 'debug', retentionInDays: 14 });
|
|
47
|
-
|
|
48
|
-
expect(result.provider.logs.lambda.applicationLogLevel).toBe('DEBUG');
|
|
49
|
-
expect(result.provider.logRetentionInDays).toBe(14);
|
|
50
|
-
});
|
|
51
|
-
});
|
|
52
|
-
});
|