@63klabs/cache-data 1.2.5 → 1.2.7
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 +29 -1
- package/README.md +52 -1230
- package/SECURITY.md +1 -1
- package/docs/00-example-implementation/README.md +55 -0
- package/docs/00-example-implementation/example-buildspec.yml +15 -0
- package/docs/00-example-implementation/example-config.js +0 -0
- package/docs/00-example-implementation/example-handler.js +0 -0
- package/docs/00-example-implementation/example-template-lambda-function.yml +119 -0
- package/docs/00-example-implementation/example-template-parameters.yml +66 -0
- package/docs/00-example-implementation/example-template-s3-and-dynamodb-cache-store.yml +77 -0
- package/docs/00-example-implementation/generate-put-ssm.py +209 -0
- package/docs/00-example-implementation/template-configuration.json +14 -0
- package/docs/00-quick-start-implementation/README.md +615 -0
- package/docs/01-advanced-implementation-for-web-service/README.md +13 -0
- package/docs/README.md +9 -0
- package/docs/features/README.md +5 -0
- package/docs/features/cache/README.md +3 -0
- package/docs/features/endpoint/README.md +3 -0
- package/docs/features/tools/README.md +341 -0
- package/docs/lambda-optimization/README.md +178 -0
- package/package.json +4 -6
- package/src/lib/dao-cache.js +82 -28
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
# Cache
|
|
1
|
+
# 63Klabs Cache-Data
|
|
2
2
|
|
|
3
|
-
A package for
|
|
3
|
+
A package for AWS Lambda Node.js applications to access and cache data from remote API endpoints (or other sources) utilizing AWS S3 and DynamoDb for the cache storage. Also provides a simple request handling, routing, and response logging framework for running a web service with minimal dependencies.
|
|
4
4
|
|
|
5
5
|
> Note: This repository and package has moved from chadkluck to 63Klabs but is still managed by the same developer.
|
|
6
6
|
|
|
@@ -8,1259 +8,81 @@ A package for node.js applications to access and cache data from remote API endp
|
|
|
8
8
|
|
|
9
9
|
## Description
|
|
10
10
|
|
|
11
|
-
For Lambda functions written in Node.js that require caching of data either of an internal process or external data
|
|
11
|
+
For AWS Lambda functions written in Node.js that require caching of data either of an internal process or external data source such as remote API endpoints. While out of the box it can fetch data from remote endpoint APIs, custom Data Access Objects can be written to provide caching of data from all sorts of sources including resource expensive database calls.
|
|
12
12
|
|
|
13
|
-
It
|
|
13
|
+
It has several utility functions such `DebugAndLog`, `Timer`, and SSM Parameter Store loaders.
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
It can be used in place of Express.js for simple web service applications as it also includes functions for handling and validating requests, routing, and client request logging.
|
|
16
|
+
|
|
17
|
+
This package has been used in production for web service applications receiving over 1 million requests per week with a 75% cache-hit rate lowering latency to less than 100ms in most cases. This is a considerable improvement when faced with resource intense processes, connection pools, API rate limits, and slow endpoints.
|
|
16
18
|
|
|
17
19
|
## Getting Started
|
|
18
20
|
|
|
19
21
|
### Requirements
|
|
20
22
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
- Node >18 runtime on Lambda
|
|
24
|
+
- AWS Lambda, S3 bucket, DynamoDb table, and SSM Parameter Store
|
|
25
|
+
- A basic understanding of CloudFormation, Lambda, S3, DynamoDb, and SSM Parameters
|
|
26
|
+
- A basic understanding of IAM policies, especially the Lambda Execution Role, that will allow Lambda to access S3, DynamoDb, and SSM Parameter Store
|
|
27
|
+
- Lambda function should have between 512MB and 1024MB of memory allocated. (256MB minimum). See [Lambda Optimization: Memory Allocation](./docs/lambda-optimization/README.md#lambda-memory-allocation).
|
|
26
28
|
|
|
27
29
|
### Installing
|
|
28
30
|
|
|
29
|
-
1.
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
```yaml
|
|
52
|
-
Resources:
|
|
53
|
-
|
|
54
|
-
# API Gateway
|
|
55
|
-
|
|
56
|
-
WebApi:
|
|
57
|
-
Type: AWS::Serverless::Api
|
|
58
|
-
Properties:
|
|
59
|
-
Name: !Sub '${APPLICATION-API}-WebApi'
|
|
60
|
-
StageName: !Ref ApiPathBase
|
|
61
|
-
PropagateTags: True
|
|
62
|
-
TracingEnabled: True
|
|
63
|
-
OpenApiVersion: 3.0.0
|
|
64
|
-
|
|
65
|
-
# Lambda Function
|
|
66
|
-
|
|
67
|
-
AppFunction:
|
|
68
|
-
Type: AWS::Serverless::Function
|
|
69
|
-
Properties:
|
|
70
|
-
# ...
|
|
71
|
-
Runtime: nodejs22.x
|
|
72
|
-
MemorySize: 1028
|
|
73
|
-
Role: !GetAtt LambdaExecutionRole.Arn
|
|
74
|
-
|
|
75
|
-
# Lambda Insights and X-Ray
|
|
76
|
-
Tracing: Active # X-Ray
|
|
77
|
-
# Required layers for 1) XRay and Lambda Insights, and 2) AWS Secrets Manager and Parameter Store Extension
|
|
78
|
-
Layers:
|
|
79
|
-
- !Sub "arn:aws:lambda:${AWS::Region}:${ACCT_ID_FOR_AWS_INSIGHTS_EXT}:layer:LambdaInsightsExtension:52" # Check for latest version: https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Lambda-Insights-extension-versionsx86-64.html
|
|
80
|
-
- !Sub "arn:aws:lambda:${AWS::Region}:${ACCT_ID_FOR_AWS_PARAM_AND_SECRETS_EXT}:layer:AWS-Parameters-and-Secrets-Lambda-Extension:11" # https://docs.aws.amazon.com/systems-manager/latest/userguide/ps-integration-lambda-extensions.html#ps-integration-lambda-extensions-add
|
|
81
|
-
|
|
82
|
-
Environment:
|
|
83
|
-
Variables:
|
|
84
|
-
|
|
85
|
-
NODE_ENV: !If [ IsProduction, "production", "development"] # Note we are past the build installation phase so devDependencies are not installed, however, we can utilize certain tools in development mode
|
|
86
|
-
DEPLOY_ENVIRONMENT: !Ref DeployEnvironment # PROD, TEST, DEV - a different category of environment
|
|
87
|
-
LOG_LEVEL: !If [ IsProduction, "0", "5"] # 0 for prod, 2-5 for non-prod
|
|
88
|
-
PARAM_STORE_PATH: "/" # SSM Parameter store can use a hierarchy to organize your apps parameters
|
|
89
|
-
|
|
90
|
-
# Cache-Data settings (from: https://www.npmjs.com/package/@63klabs/cache-data)
|
|
91
|
-
CACHE_DATA_DYNAMO_DB_TABLE: !Ref CacheDataDynamoDbTable
|
|
92
|
-
CACHE_DATA_S3_BUCKET: !Ref CacheDataS3Bucket
|
|
93
|
-
CACHE_DATA_SECURE_DATA_ALGORITHM: !Ref CacheDataCryptSecureDataAlg
|
|
94
|
-
CACHE_DATA_ID_HASH_ALGORITHM: !Ref CacheDataCryptIdHashAlgorithm
|
|
95
|
-
CACHE_DATA_DYNAMO_DB_MAX_CACHE_SIZE_KB: !Ref CacheDataDbMaxCacheSizeInKB
|
|
96
|
-
CACHE_DATA_PURGE_EXPIRED_CACHE_ENTRIES_AFTER_X_HRS: !Ref CacheDataPurgeExpiredCacheEntriesInHours
|
|
97
|
-
CACHE_DATA_ERROR_EXP_IN_SECONDS: !Ref CacheDataErrorExpirationInSeconds
|
|
98
|
-
CACHE_DATA_TIME_ZONE_FOR_INTERVAL: !Ref CacheDataTimeZoneForInterval
|
|
99
|
-
CACHE_DATA_AWS_X_RAY_ON: !Ref CacheDataAWSXRayOn
|
|
100
|
-
|
|
101
|
-
# -- LambdaFunction Execution Role --
|
|
102
|
-
|
|
103
|
-
LambdaExecutionRole:
|
|
104
|
-
Type: AWS::IAM::Role
|
|
105
|
-
Properties:
|
|
106
|
-
RoleName: !Sub "${LAMBDA_EXECUTION_ROLE_NAME}-ExecutionRole"
|
|
107
|
-
Description: "IAM Role that allows the Lambda permission to execute and access resources"
|
|
108
|
-
Path: /
|
|
109
|
-
|
|
110
|
-
AssumeRolePolicyDocument:
|
|
111
|
-
Statement:
|
|
112
|
-
- Effect: Allow
|
|
113
|
-
Principal:
|
|
114
|
-
Service: [lambda.amazonaws.com]
|
|
115
|
-
Action: sts:AssumeRole
|
|
116
|
-
|
|
117
|
-
# These are for application monitoring via LambdaInsights and X-Ray
|
|
118
|
-
ManagedPolicyArns:
|
|
119
|
-
- 'arn:aws:iam::aws:policy/CloudWatchLambdaInsightsExecutionRolePolicy'
|
|
120
|
-
- 'arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess'
|
|
121
|
-
|
|
122
|
-
# These are the resources your Lambda function needs access to
|
|
123
|
-
# Logs, SSM Parameters, DynamoDb, S3, etc.
|
|
124
|
-
# Define specific actions such as get/put (read/write)
|
|
125
|
-
Policies:
|
|
126
|
-
- PolicyName: LambdaResourceAccessPolicies
|
|
127
|
-
PolicyDocument:
|
|
128
|
-
Statement:
|
|
129
|
-
|
|
130
|
-
- Sid: LambdaAccessToWriteLogs
|
|
131
|
-
Action:
|
|
132
|
-
- logs:CreateLogGroup
|
|
133
|
-
- logs:CreateLogStream
|
|
134
|
-
- logs:PutLogEvents
|
|
135
|
-
Effect: Allow
|
|
136
|
-
Resource: !GetAtt AppLogGroup.Arn
|
|
137
|
-
|
|
138
|
-
# cache-data Parameter Read Access (from: https://www.npmjs.com/package/@63klabs/cache-data)
|
|
139
|
-
- Sid: LambdaAccessToSSMParameters
|
|
140
|
-
Action:
|
|
141
|
-
- ssm:DescribeParameters
|
|
142
|
-
- ssm:GetParameters
|
|
143
|
-
- ssm:GetParameter
|
|
144
|
-
- ssm:GetParametersByPath
|
|
145
|
-
Effect: Allow
|
|
146
|
-
Resource:
|
|
147
|
-
- !Sub "arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter${ParameterStoreHierarchy}*"
|
|
148
|
-
|
|
149
|
-
# cache-data S3 bucket (from: https://www.npmjs.com/package/@63klabs/cache-data)
|
|
150
|
-
- Sid: LambdaAccessToS3BucketCacheData
|
|
151
|
-
Action:
|
|
152
|
-
- s3:PutObject
|
|
153
|
-
- s3:GetObject
|
|
154
|
-
- s3:GetObjectVersion
|
|
155
|
-
Effect: Allow
|
|
156
|
-
Resource: !Join [ '', [ !GetAtt CacheDataS3Bucket.Arn, '/cache/*' ] ]
|
|
157
|
-
|
|
158
|
-
# cache-data DynamoDb table (from: https://www.npmjs.com/package/@63klabs/cache-data)
|
|
159
|
-
- Sid: LambdaAccessToDynamoDBTableCacheData
|
|
160
|
-
Action:
|
|
161
|
-
- dynamodb:GetItem
|
|
162
|
-
- dynamodb:Scan
|
|
163
|
-
- dynamodb:Query
|
|
164
|
-
- dynamodb:BatchGetItem
|
|
165
|
-
- dynamodb:PutItem
|
|
166
|
-
- dynamodb:UpdateItem
|
|
167
|
-
- dynamodb:BatchWriteItem
|
|
168
|
-
Effect: Allow
|
|
169
|
-
Resource: !GetAtt CacheDataDynamoDbTable.Arn
|
|
170
|
-
|
|
171
|
-
```
|
|
172
|
-
|
|
173
|
-
The example definition above uses the following parameters. You can either add these to your template's `Parameters` section or hard-code values for your environment variables using the specifications outlined.
|
|
174
|
-
|
|
175
|
-
```yaml
|
|
176
|
-
Parameters:
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
# ---------------------------------------------------------------------------
|
|
180
|
-
# Cache-Data Parameters
|
|
181
|
-
# From: https://www.npmjs.com/package/@63klabs/cache-data
|
|
182
|
-
|
|
183
|
-
CacheDataDbMaxCacheSizeInKB:
|
|
184
|
-
Type: Number
|
|
185
|
-
Description: "DynamoDb does better when storing smaller pieces of data. Choose the cut-off in KB that large objects should be stored in S3 instead (10)"
|
|
186
|
-
Default: 10
|
|
187
|
-
MinValue: 10
|
|
188
|
-
MaxValue: 200
|
|
189
|
-
ConstraintDescription: "Numeric value between 10 and 200 (inclusive)"
|
|
190
|
-
CacheDataCryptIdHashAlgorithm:
|
|
191
|
-
Type: String
|
|
192
|
-
Description: "Hash algorithm used for generating the URI ID to identify cached requests. This is for generating IDs, not crypto."
|
|
193
|
-
Default: "RSA-SHA256"
|
|
194
|
-
AllowedValues: ["RSA-SHA256", "RSA-SHA3-224", "RSA-SHA3-256", "RSA-SHA3-384", "RSA-SHA3-512"]
|
|
195
|
-
ConstraintDescription: "Use possible hashes available from Node.js in the RSA- category (RSA-SHA256 to RSA-SM3)"
|
|
196
|
-
CacheDataCryptSecureDataAlg:
|
|
197
|
-
Type: String
|
|
198
|
-
Description: "Cryptographic algorithm to use for storing sensitive cached data in S3 and DynamoDb"
|
|
199
|
-
Default: "aes-256-cbc"
|
|
200
|
-
AllowedValues: ["aes-256-cbc", "aes-256-cfb", "aes-256-cfb1", "aes-256-cfb8", "aes-256-ofb"]
|
|
201
|
-
ConstraintDescription: "Use possible cipher algorithms available (crypto.getCiphers()) from Node.js in the aes-256-xxx category"
|
|
202
|
-
CacheDataErrorExpirationInSeconds:
|
|
203
|
-
Type: Number
|
|
204
|
-
Description: "How long should errors be cached? This prevents retrying a service that is currently in error too often (300 is recommended)"
|
|
205
|
-
Default: 300
|
|
206
|
-
MinValue: 1
|
|
207
|
-
ConstraintDescription: "Choose a value of 1 or greater"
|
|
208
|
-
CacheDataPurgeExpiredCacheEntriesInHours:
|
|
209
|
-
Type: Number
|
|
210
|
-
Description: "The number of hours expired cached data should be kept before purging. Expired cache data may be used if the source returns an error."
|
|
211
|
-
Default: 24
|
|
212
|
-
MinValue: 1
|
|
213
|
-
ConstraintDescription: "Choose a value of 1 or greater"
|
|
214
|
-
CacheDataPurgeAgeOfCachedBucketObjInDays:
|
|
215
|
-
Type: Number
|
|
216
|
-
Description: "Similar to CacheData_PurgeExpiredCacheEntriesInHours, but for the S3 Bucket. S3 calculates from time object is created/last modified (not accessed). This should be longer than your longest cache expiration set in custom/policies. Keeping objects in S3 for too long increases storage costs. (30 is recommended)"
|
|
217
|
-
Default: 15
|
|
218
|
-
MinValue: 3
|
|
219
|
-
ConstraintDescription: "Choose a value of 3 days or greater. This should be slightly longer than the longest cache expiration expected"
|
|
220
|
-
CacheDataTimeZoneForInterval:
|
|
221
|
-
Type: String
|
|
222
|
-
Description: "Cache-Data may expire using an interval such as every four, six, twelve, ... hours on the hour starting at midnight. What timezone holds the midnight to calculate from?"
|
|
223
|
-
Default: "Etc/UTC"
|
|
224
|
-
AllowedValues: ["Etc/UTC", "America/Puerto_Rico", "America/New_York", "America/Indianapolis", "America/Chicago", "America/Denver", "America/Phoenix", "America/Los_Angeles", "America/Anchorage", "Pacific/Honolulu"] # https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
|
|
225
|
-
ConstraintDescription: "Common examples for United States of America. Accepted values can be changed in the template for your region."
|
|
226
|
-
CacheDataAWSXRayOn:
|
|
227
|
-
Type: String
|
|
228
|
-
Description: "Turn on AWS XRay tracing for Cache-Data"
|
|
229
|
-
Default: "false"
|
|
230
|
-
AllowedValues: ["true", "false"]
|
|
231
|
-
ConstraintDescription: "Accepted values are true or false"
|
|
232
|
-
```
|
|
233
|
-
|
|
234
|
-
#### Cache-Data DynamoDb and S3 CloudFormation Resource Templates
|
|
235
|
-
|
|
236
|
-
Use the following as an example for creating your DynamoDb and S3 cache storage locations.
|
|
237
|
-
|
|
238
|
-
```yaml
|
|
239
|
-
Resources:
|
|
240
|
-
|
|
241
|
-
# ... all your other resources
|
|
242
|
-
# ... make sure your Lambda function has between 512MB and 1024MB allocated (256MB minimum)
|
|
243
|
-
# ... also make sure you added environment variables to your Lambda function
|
|
244
|
-
# ... and make sure your Lambda Execution Role grants access to your DynamoDb and S3 buckets
|
|
245
|
-
|
|
246
|
-
# ---------------------------------------------------------------------------
|
|
247
|
-
# Cache-Data
|
|
248
|
-
# From: https://www.npmjs.com/package/@63klabs/cache-data
|
|
249
|
-
# Your Lambda function will need access via the Execution Role
|
|
250
|
-
|
|
251
|
-
# -- Cache-Data DynamoDb Table --
|
|
252
|
-
|
|
253
|
-
CacheDataDynamoDbTable:
|
|
254
|
-
Type: AWS::DynamoDB::Table
|
|
255
|
-
Description: Table to store Cache-Data.
|
|
256
|
-
Properties:
|
|
257
|
-
TableName: !Sub '${YOUR-DYNAMODB-TABLE}-CacheData'
|
|
258
|
-
AttributeDefinitions:
|
|
259
|
-
- AttributeName: "id_hash"
|
|
260
|
-
AttributeType: "S"
|
|
261
|
-
KeySchema:
|
|
262
|
-
- AttributeName: "id_hash"
|
|
263
|
-
KeyType: "HASH"
|
|
264
|
-
TimeToLiveSpecification:
|
|
265
|
-
AttributeName: "purge_ts"
|
|
266
|
-
Enabled: true
|
|
267
|
-
BillingMode: "PAY_PER_REQUEST"
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
# -- Cache-Data S3 Bucket --
|
|
271
|
-
|
|
272
|
-
CacheDataS3Bucket:
|
|
273
|
-
Type: AWS::S3::Bucket
|
|
274
|
-
Description: S3 Bucket to store Cache-Data too big for DynamoDb. Cache-Data stores objects in /cache directory. The application may store additional data outside of the cache directory.
|
|
275
|
-
Properties:
|
|
276
|
-
BucketName: !Sub "${YOUR-BUCKET-NAME}-cachedata"
|
|
277
|
-
PublicAccessBlockConfiguration:
|
|
278
|
-
BlockPublicAcls: true
|
|
279
|
-
BlockPublicPolicy: true
|
|
280
|
-
IgnorePublicAcls: true
|
|
281
|
-
RestrictPublicBuckets: true
|
|
282
|
-
BucketEncryption:
|
|
283
|
-
ServerSideEncryptionConfiguration:
|
|
284
|
-
- ServerSideEncryptionByDefault:
|
|
285
|
-
SSEAlgorithm: AES256
|
|
286
|
-
LifecycleConfiguration:
|
|
287
|
-
Rules:
|
|
288
|
-
- Id: "ExpireObjects"
|
|
289
|
-
AbortIncompleteMultipartUpload:
|
|
290
|
-
DaysAfterInitiation: 1
|
|
291
|
-
ExpirationInDays: !Ref CacheDataPurgeAgeOfCachedBucketObjInDays
|
|
292
|
-
Prefix: "cache" # this will limit this policy to YOURBUCKETNAME/cache/*
|
|
293
|
-
NoncurrentVersionExpirationInDays: !Ref CacheDataPurgeAgeOfCachedBucketObjInDays
|
|
294
|
-
Status: "Enabled" # Enable only if you are going to use this LifecycleConfiguration
|
|
295
|
-
|
|
296
|
-
# -- Cache-Data S3 Bucket Policy --
|
|
297
|
-
|
|
298
|
-
CacheDataS3BucketPolicy:
|
|
299
|
-
Type: AWS::S3::BucketPolicy
|
|
300
|
-
Properties:
|
|
301
|
-
Bucket: !Ref CacheDataS3Bucket
|
|
302
|
-
PolicyDocument:
|
|
303
|
-
Version: "2012-10-17"
|
|
304
|
-
Id: SecurityPolicy
|
|
305
|
-
Statement:
|
|
306
|
-
- Sid: "DenyNonSecureTransportAccess"
|
|
307
|
-
Effect: Deny
|
|
308
|
-
Principal: "*"
|
|
309
|
-
Action: "s3:*"
|
|
310
|
-
Resource:
|
|
311
|
-
- !GetAtt CacheDataS3Bucket.Arn
|
|
312
|
-
- !Join [ '', [ !GetAtt CacheDataS3Bucket.Arn, '/*' ] ]
|
|
313
|
-
Condition:
|
|
314
|
-
Bool:
|
|
315
|
-
"aws:SecureTransport": false
|
|
316
|
-
|
|
317
|
-
```
|
|
318
|
-
|
|
319
|
-
#### Install npm Package and Add Starter Code
|
|
320
|
-
|
|
321
|
-
1. Go to your application directory
|
|
322
|
-
2. Run the command `npm i @63klabs/cache-data`
|
|
323
|
-
3. Add `const { tools, cache, endpoint } = require('@63klabs/cache-data');` to your script
|
|
324
|
-
4. Add a Config class to your script. This should be placed before the handler so it only is defined during cold starts. This can also be imported from a separate script.
|
|
325
|
-
5. Add `Config.init()` after the Config class but before the handler.
|
|
326
|
-
5. Add `await tools.Config.promise();` in the handler to make sure the Config has completed.
|
|
327
|
-
|
|
328
|
-
```javascript
|
|
329
|
-
const { tools, cache, endpoint } = require('@63klabs/cache-data');
|
|
330
|
-
|
|
331
|
-
class Config extends tools._ConfigSuperClass {
|
|
332
|
-
static async init() {
|
|
333
|
-
tools._ConfigSuperClass._promise = new Promise(async (resolve, reject) => {
|
|
334
|
-
resolve(true);
|
|
335
|
-
};
|
|
336
|
-
};
|
|
337
|
-
|
|
338
|
-
/* initialize the Config */
|
|
339
|
-
obj.Config.init();
|
|
340
|
-
|
|
341
|
-
exports.handler = async (event, context, callback) => {
|
|
342
|
-
await tools.Config.promise();
|
|
343
|
-
let response = {
|
|
344
|
-
statusCode: 200,
|
|
345
|
-
body: JSON.stringify({message: "Hello from Lambda!"}),
|
|
346
|
-
headers: {'content-type': 'application/json'}
|
|
347
|
-
};
|
|
348
|
-
|
|
349
|
-
callback(null, response);
|
|
350
|
-
}
|
|
351
|
-
```
|
|
352
|
-
|
|
353
|
-
Note: `deployEnvironment` is only one of the possible runtime environment variables the script checks for. You may also use `env`, `deployEnvironment`, `environment`, or `stage`. Also note the confusion that may be had when we are talking about "environment" as it refers to both Lambda Runtime Environment Variables as well as a variable denoting a Deployment Environment (Production, Development, Testing, etc.).
|
|
354
|
-
|
|
355
|
-
(Runtime Environment variables are accessed using `process.env.`_`variableName`_.)
|
|
356
|
-
|
|
357
|
-
### Usage
|
|
358
|
-
|
|
359
|
-
Note: There is a sample app and tutorial that works with a CI/CD pipeline available at the repository: [serverless-webservice-template-for-pipeline-atlantis](https://github.com/chadkluck/serverless-webservice-template-for-pipeline-atlantis)
|
|
360
|
-
|
|
361
|
-
#### Config
|
|
362
|
-
|
|
363
|
-
##### Parameters and Secrets
|
|
364
|
-
|
|
365
|
-
Cache-Data requires an 32 character hexidecimal key to encrypt data when at rest. This can be stored in an SSM Parameter named `crypt_secureDataKey`.
|
|
366
|
-
|
|
367
|
-
You have two options for storing and retrieving your SSM Parameters:
|
|
368
|
-
|
|
369
|
-
1. Using the Cache-Data SSM Parameter access function.
|
|
370
|
-
2. Using the AWS Parameter and Secrets Lambda Extension.
|
|
371
|
-
|
|
372
|
-
Both are easily accessed using functions in the Cache-Data toolkit.
|
|
373
|
-
|
|
374
|
-
###### Option 1: Cache-Data SSM Parameter access function
|
|
375
|
-
|
|
376
|
-
This runs in the Config.init() function and can be used to retrieve all of the parameters needed for your application.
|
|
377
|
-
|
|
378
|
-
```javascript
|
|
379
|
-
class Config extends tools._ConfigSuperClass {
|
|
380
|
-
static async init() {
|
|
381
|
-
|
|
382
|
-
tools._ConfigSuperClass._promise = new Promise(async (resolve, reject) => {
|
|
383
|
-
|
|
384
|
-
try {
|
|
385
|
-
|
|
386
|
-
let params = await this._initParameters(
|
|
387
|
-
[
|
|
388
|
-
{
|
|
389
|
-
"group": "app", // so we can do params.app.weatherapikey later
|
|
390
|
-
"path": process.env.PARAM_STORE_PATH
|
|
391
|
-
}
|
|
392
|
-
]
|
|
393
|
-
);
|
|
394
|
-
|
|
395
|
-
// You can access within init() using params.app.crypt_secureDataKey
|
|
396
|
-
|
|
397
|
-
resolve(true);
|
|
398
|
-
} catch(error) {
|
|
399
|
-
reject(null);
|
|
400
|
-
}
|
|
401
|
-
});
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
```
|
|
405
|
-
|
|
406
|
-
Accesses the SSM Parameter Store and places any parameters found under `/apps/my_cool_app/` into a `params.app` variable. You'll see that the cache initialization uses `params.app.crypt_secureDataKey` which is the parameter we created under `/apps/my_cool_app/crypt_secureDataKey`.
|
|
407
|
-
|
|
408
|
-
New deployments and new concurrent instances will pick up changes to a Parameter value, but long-running instances will not. If you change the value of a Parameter then you need to redeploy the application in order to clear out any use of the old value.
|
|
409
|
-
|
|
410
|
-
###### Option 2: AWS Parameter and Secrets Lambda Extension
|
|
411
|
-
|
|
412
|
-
This is a more robust option and works with Secrets Manager as well. It requires the installation of a Lambda layer and then use of the `CachedSecret` and/or `CachedSSMParameter` Class from the Cache-Data tool-kit.
|
|
413
|
-
|
|
414
|
-
Another advantage is that unlike the previous method, this method will pick up on any Secret and Parameter value changes and begin using the new values within 5 minutes (unless you set the cache for longer).
|
|
415
|
-
|
|
416
|
-
First, make sure you install the Lambda layer:
|
|
417
|
-
|
|
418
|
-
```yaml
|
|
419
|
-
Resources:
|
|
420
|
-
|
|
421
|
-
AppFunction:
|
|
422
|
-
Type: AWS::Serverless::Function
|
|
423
|
-
Properties:
|
|
424
|
-
# ...
|
|
425
|
-
Layers:
|
|
426
|
-
- !Sub "arn:aws:lambda:${AWS::Region}:${ACCT_ID_FOR_AWS_PARAM_AND_SECRETS_EXT}:layer:AWS-Parameters-and-Secrets-Lambda-Extension:11" # https://docs.aws.amazon.com/systems-manager/latest/userguide/ps-integration-lambda-extensions.html#ps-integration-lambda-extensions-add
|
|
427
|
-
```
|
|
428
|
-
|
|
429
|
-
Next, in your code, create the object to store your Parameter or Secret.
|
|
430
|
-
|
|
431
|
-
```javascript
|
|
432
|
-
const myKey = new tools.CachedSSMParameter('appSecretKey', {refreshAfter: 1600});
|
|
433
|
-
```
|
|
434
|
-
|
|
435
|
-
Finally, make sure you prime() and await the value.
|
|
436
|
-
|
|
437
|
-
```javascript
|
|
438
|
-
myKey.prime(); // request in the background so you can do other things before using it.
|
|
439
|
-
|
|
440
|
-
// ... do many things
|
|
441
|
-
|
|
442
|
-
let password = await myKey.getValue();
|
|
443
|
-
```
|
|
444
|
-
|
|
445
|
-
If you place it within an object you can also stringify that object and it will replace the reference with the secret. (You need to await the .prime() before processing)
|
|
446
|
-
|
|
447
|
-
```javascript
|
|
448
|
-
myKey.prime(); // request in the background so you can do other things before using it.
|
|
449
|
-
|
|
450
|
-
// ... do many things
|
|
451
|
-
const dbconn = { username: myUsername, password: myKey };
|
|
452
|
-
|
|
453
|
-
await myKey.prime();
|
|
454
|
-
connect(JSON.parse(JSON.stringify(dbconn)));
|
|
455
|
-
|
|
456
|
-
// or use toString()
|
|
457
|
-
await myKey.prime();
|
|
458
|
-
connect( {
|
|
459
|
-
username: `${myUsername}`,
|
|
460
|
-
password: `${myKey}`
|
|
461
|
-
})
|
|
462
|
-
```
|
|
463
|
-
|
|
464
|
-
##### Connections, and Cache
|
|
465
|
-
|
|
466
|
-
The cache object acts as an intermediary between your application and your data (whether it be a remote endpoint or other storage/process mechanism).
|
|
467
|
-
|
|
468
|
-
Before you can use Parameter Store, S3, and DynamoDb for the cache, they need to be set up with the proper access granted to your Lambda function.
|
|
469
|
-
|
|
470
|
-
1. Set up an S3 bucket (Your application will store cache data in `/cache`)
|
|
471
|
-
2. Create a DynamoDb table
|
|
472
|
-
3. Create a Parameter in SSM Parameter store `/app/my_cool_app/crypt_secureDataKey` and set the secret text to a 64 character length hex value. (64 hex characters because we are using a 256 bit key and cipher (`aes-256-ofb`)in the example below)
|
|
473
|
-
4. Make sure you set up IAM policies to allow you Lambda function access to the S3 bucket, DynamoDb table, and SSM Parameter store.
|
|
474
|
-
|
|
475
|
-
Once the S3 bucket, DynamoDb table, and SSM Parameter are set up we can focus on your Lambda function.
|
|
476
|
-
|
|
477
|
-
During your application initialization (but not for each request) we need to initialize the Config object.
|
|
478
|
-
|
|
479
|
-
The class below will do the following three things:
|
|
480
|
-
|
|
481
|
-
1. Bring in the secret key (and other parameters) from SSM Parameter Store.
|
|
482
|
-
2. Create connections with cache settings for each connection
|
|
483
|
-
3. Initialize the Cache
|
|
484
|
-
|
|
485
|
-
This code can be put into a separate file and brought in using a `require` statement. It should be scoped to the highest level of your Lambda function and not in the request handler.
|
|
486
|
-
|
|
487
|
-
```js
|
|
488
|
-
/* EXAMPLE USING the this._initParameters method of obtaining parameters during Config.init() */
|
|
489
|
-
|
|
490
|
-
// require cache-data
|
|
491
|
-
const { tools, cache, endpoint } = require('@63klabs/cache-data');
|
|
492
|
-
|
|
493
|
-
/**
|
|
494
|
-
* Extends tools._ConfigSuperClass
|
|
495
|
-
* Used to create a custom Config interface
|
|
496
|
-
* Usage: should be placed near the top of the script file outside
|
|
497
|
-
* of the event handler. It should be global and must be initialized.
|
|
498
|
-
* @example
|
|
499
|
-
* const obj = require("./classes.js");
|
|
500
|
-
* obj.Config.init();
|
|
501
|
-
*/
|
|
502
|
-
class Config extends tools._ConfigSuperClass {
|
|
503
|
-
|
|
504
|
-
/**
|
|
505
|
-
* This is custom inititialization code for the application. Depending
|
|
506
|
-
* upon needs, the _init functions from the super class may be used
|
|
507
|
-
* as needed. Init is async, and a promise is stored, allowing the
|
|
508
|
-
* lambda function to wait until the promise is finished.
|
|
509
|
-
*/
|
|
510
|
-
static async init() {
|
|
511
|
-
|
|
512
|
-
tools._ConfigSuperClass._promise = new Promise(async (resolve, reject) => {
|
|
513
|
-
|
|
514
|
-
try {
|
|
515
|
-
|
|
516
|
-
let params = await this._initParameters(
|
|
517
|
-
[
|
|
518
|
-
{
|
|
519
|
-
"group": "app", // so we can do params.app.weatherapikey later
|
|
520
|
-
"path": "/apps/my_cool_app/" // process.env.PARAM_STORE_PATH // or store as a Lambda environment variable
|
|
521
|
-
}
|
|
522
|
-
]
|
|
523
|
-
);
|
|
524
|
-
|
|
525
|
-
// after we have the params, we can set the connections
|
|
526
|
-
let connections = new tools.Connections();
|
|
527
|
-
|
|
528
|
-
/* NOTE: instead of hard coding connections, you could import
|
|
529
|
-
from a connections file and then add in any additional values
|
|
530
|
-
such as keys from the Param store
|
|
531
|
-
*/
|
|
532
|
-
|
|
533
|
-
// for games demo from api.chadkluck.net
|
|
534
|
-
connections.add( {
|
|
535
|
-
name: "demo",
|
|
536
|
-
host: "api.chadkluck.net",
|
|
537
|
-
path: "/games",
|
|
538
|
-
parameters: {},
|
|
539
|
-
headers: {
|
|
540
|
-
referer: "https://chadkluck.net"
|
|
541
|
-
},
|
|
542
|
-
cache: [
|
|
543
|
-
{
|
|
544
|
-
profile: "games",
|
|
545
|
-
overrideOriginHeaderExpiration: true, // if the endpoint returns an expiration, do we ignore it for our own?
|
|
546
|
-
defaultExpirationInSeconds: (10 * 60),// , // 10 minutes
|
|
547
|
-
expiresIsOnInterval: true, // for example, a 10 min cache can expire on the hour, 10, 20, 30... after. 24 hour cache can expire at midnight. 6 hour cache can expire at 6am, noon, 6pm, and midnight
|
|
548
|
-
headersToRetain: "", // what headers from the endpoint do we want to keep with the cache data?
|
|
549
|
-
hostId: "demo", // log entry friendly (or not)
|
|
550
|
-
pathId: "games", // log entry friendly (or not)
|
|
551
|
-
encrypt: false // you can set this to true and it will use the key from param store and encrypt data at rest in S3 and DynamoDb
|
|
552
|
-
}
|
|
553
|
-
]
|
|
554
|
-
} );
|
|
555
|
-
|
|
556
|
-
tools._ConfigSuperClass._connections = connections;
|
|
557
|
-
|
|
558
|
-
// Cache settings
|
|
559
|
-
cache.Cache.init({
|
|
560
|
-
dynamoDbTable: "yourDynamoDbTable", // replace with the name of a DynamoDb table to store cached data
|
|
561
|
-
s3Bucket: "yourS3Bucket", // replace with a bucket name to store cache data. Data will be stored in /cache in yourS3Bucket
|
|
562
|
-
secureDataAlgorithm: "aes-256-ofb", // how do we encrypt data at rest
|
|
563
|
-
secureDataKey: Buffer.from(params.app.crypt_secureDataKey, "hex"), // using the parameter from above during Config.init()
|
|
564
|
-
//secureDataKey: new tools.CachedSSMParameter('/apps/my_cool_app/CacheData_SecureDataKey', {refreshAfter: 300}), // if using tools.CachedSSMParameter()
|
|
565
|
-
idHashAlgorithm: "RSA-SHA3-512", // the alg used to create a unique hash identifier for requests so we can tell them apart in the cache
|
|
566
|
-
DynamoDbMaxCacheSize_kb: 20, // data larger than this (in KB) will be stored in S3 to keep DynamoDb running efficently
|
|
567
|
-
purgeExpiredCacheEntriesAfterXHours: 24, // expired caches hang around for a while before we purge just in case there is cause to fall back on them
|
|
568
|
-
defaultExpirationExtensionOnErrorInSeconds: 300, // so as to not overwhelm a down endpoint, or to not cache an error for too long, how often should we check back?
|
|
569
|
-
timeZoneForInterval: "America/Chicago" // if caching on interval, we need a timezone to account for calculating hours, days, and weeks. List: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
|
|
570
|
-
});
|
|
571
|
-
|
|
572
|
-
resolve(true);
|
|
573
|
-
} catch (error) {
|
|
574
|
-
tools.DebugAndLog.error("Could not initialize Config", error);
|
|
575
|
-
reject(false);
|
|
576
|
-
};
|
|
577
|
-
|
|
578
|
-
});
|
|
579
|
-
|
|
580
|
-
};
|
|
581
|
-
};
|
|
582
|
-
```
|
|
583
|
-
|
|
584
|
-
The `connection` code above does the following:
|
|
585
|
-
|
|
586
|
-
1. Defines the host and path (and any parameters and headers to send to a remote endpoint)
|
|
587
|
-
2. Defines the cache settings for that remote endpoint (note, these are only cache settings for that remote endpoint and not the overall cache)
|
|
588
|
-
|
|
589
|
-
Additional connections may be added using additional `connections.add()` functions.
|
|
590
|
-
|
|
591
|
-
The `cache` code does the following:
|
|
592
|
-
|
|
593
|
-
1. Sets the DynamoDb table and S3 bucket to store cached data
|
|
594
|
-
2. Sets the algorithm to securely encrypt data at rest in DynamoDb and S3
|
|
595
|
-
3. Sets the hash algorithm used to create a unique id for each unique request
|
|
596
|
-
4. How big of an object do we save in DynamoDb before storing it in S3? (20K objects are ideal, anything bigger is in S3)
|
|
597
|
-
5. How long to wait before purging expired entries (they aren't purged right away but kept in case of errors)
|
|
598
|
-
6. If there is an error getting fresh data, how long do we extend any existing cache? (so we can back off while endpoint is in error)
|
|
599
|
-
7. Set the time zone for intervals. For example, we can expire on the hour (8am, 12pm, 8pm, etc) but if we expire at the end of the day, when is the "end of the day"? Midnight where? If empty it will be UTC.
|
|
600
|
-
|
|
601
|
-
Each of these are described in their own sections below.
|
|
602
|
-
|
|
603
|
-
Note that it is probably best to not hard code values but instead bring them in as environment variables from your Lambda function.
|
|
604
|
-
|
|
605
|
-
Next, we need to call the initialization in our application, and before the handler can be executed, make sure the promise has resolved.
|
|
606
|
-
|
|
607
|
-
```js
|
|
608
|
-
// note that the Config object is defined in the code above
|
|
609
|
-
|
|
610
|
-
/* initialize the Config */
|
|
611
|
-
Config.init(); // we need to await completion in the async call function
|
|
612
|
-
|
|
613
|
-
/**
|
|
614
|
-
* Lambda function handler
|
|
615
|
-
*/
|
|
616
|
-
exports.handler = async (event, context, callback) => {
|
|
617
|
-
|
|
618
|
-
/* wait for CONFIG to be settled as we need it before continuing. */
|
|
619
|
-
await Config.promise();
|
|
620
|
-
|
|
621
|
-
/* Process the request and wait for result */
|
|
622
|
-
const response = await someFunction(event, context); // some code or function that generates a response
|
|
623
|
-
|
|
624
|
-
/* Send the result back to API Gateway */
|
|
625
|
-
callback(null, response);
|
|
626
|
-
|
|
627
|
-
}
|
|
628
|
-
```
|
|
629
|
-
|
|
630
|
-
Note that you will replace `someFunction()` with your own function that will call and process the data from cache as in the example below.
|
|
631
|
-
|
|
632
|
-
Once the `Config` object is initialized, the following code can be used to access data through the cache.
|
|
633
|
-
|
|
634
|
-
```js
|
|
635
|
-
/*
|
|
636
|
-
Note that cache object was already set by the require statement
|
|
637
|
-
assuming:
|
|
638
|
-
const { tools, cache, endpoint } = require('@63klabs/cache-data');
|
|
639
|
-
*/
|
|
640
|
-
|
|
641
|
-
let connection = Config.getConnection("demo"); // corresponds with the name we gave it during connections.add()
|
|
642
|
-
let conn = connection.toObject(); // we'll "extract" the connection data. .toObject() will create a clone of the data so we can modify if need be
|
|
643
|
-
|
|
644
|
-
let cacheProfile = connection.getCacheProfile("games"); // corresponds with the cache profile we gave within demo for connections.add()
|
|
645
|
-
|
|
646
|
-
const cacheObj = await cache.CacheableDataAccess.getData(
|
|
647
|
-
cacheProfile, // this is your cache profile for an endpoint, included from connection object
|
|
648
|
-
endpoint.getDataDirectFromURI, // this is the function you want to invoke to get fresh data if the cache is stale. (conn and null will be passed to it)
|
|
649
|
-
conn, // connection information which will be passed to endpoint.getDataDirectFromURI() to get fresh data. Also used to identify the object in cache
|
|
650
|
-
null // this parameter can be used to pass additional data to endpoint.getDataDirectFromURI (or any other DAO)
|
|
651
|
-
);
|
|
652
|
-
|
|
653
|
-
let games = cacheObj.getBody(true); // return the data as an object (true) instead of a string (false). You could use false if you want to keep the data as a string (as in xml or html or text)
|
|
654
|
-
```
|
|
655
|
-
|
|
656
|
-
In order to do its job it needs to:
|
|
657
|
-
|
|
658
|
-
1. Know how to access the data. We use a Connection object from Config to do this. You can think of a Connection object as all the pieces of an HTTP request. It identifies the protocol, domain, path, query string, headers, etc. (However, it doesn't have to be an HTTP request.)
|
|
659
|
-
2. Know the function to use to access fresh data from the remote endpoint. Using the Connection object, your can either use a built in HTTP request, or define your own method for processing an http request or other data source.
|
|
660
|
-
3. Know the cache policy for the data. We use a Cache object to do this. It is an object that has information on expiration, headers to save with the data, where cache data is stored, stored data encryption protocol,
|
|
661
|
-
|
|
662
|
-
### cache.CacheableDataAccess.getData() without Connection
|
|
663
|
-
|
|
664
|
-
Note that you can use `cache.CacheableDataAccess.getData()` without a Connection object. You'll notice that we "extract" the connection data from `connection` using `.toObject()`. We do this not just because it creates an object that isn't a reference (thus allowing us to ad hoc modify things like path or parameters without changing the original) but also because any object with any structure may be passed (as long as your passed function is expecting it).
|
|
665
|
-
|
|
666
|
-
The `cacheProfile` variable is also just an object, but must adhere to the structure outlined in the cache declaration previously shown.
|
|
667
|
-
|
|
668
|
-
You can create the cache configuration and connection on the fly without the Connection object:
|
|
669
|
-
|
|
670
|
-
```js
|
|
671
|
-
const cacheProfile ={
|
|
672
|
-
overrideOriginHeaderExpiration: true,
|
|
673
|
-
defaultExpirationExtensionOnErrorInSeconds: 3600,
|
|
674
|
-
defaultExpirationInSeconds: (10 * 60), // 10 minutes
|
|
675
|
-
expiresIsOnInterval: true,
|
|
676
|
-
headersToRetain: ['x-data-id', 'x-data-sha1'],
|
|
677
|
-
hostId: "example",
|
|
678
|
-
pathId: "person",
|
|
679
|
-
encrypt: true
|
|
680
|
-
};
|
|
31
|
+
1. Generate Secret Key to Encrypt Cache:
|
|
32
|
+
- Use the [key generation script](./docs/00-example-implementation/generate-put-ssm.py) during [the build](./docs/00-example-implementation/example-buildspec.yml) to establish a key to encrypt your data.
|
|
33
|
+
2. Lambda CloudFormation Template:
|
|
34
|
+
- See [Lambda template example](./docs/00-example-implementation/example-template-lambda-function.yml)
|
|
35
|
+
- Node: AWS Lambda supported version of Node
|
|
36
|
+
- Memory: Allocate at least 256MB (512-1024MB recommended)
|
|
37
|
+
- Environment Variables: Add the cache-data environment variables to your Lambda function.
|
|
38
|
+
- Execution Role: Include access to S3 and DynamoDb in your Lambda's execution role.
|
|
39
|
+
3. S3 and DynamoDb CloudFormation Template to store your cache:
|
|
40
|
+
- See [S3 and DynamoDb Cache Store template example](./docs/00-example-implementation/example-template-s3-and-dynamodb-cache-store.yml)
|
|
41
|
+
- Include in your application infrastructure template or as separate infrastructure.
|
|
42
|
+
4. Install the @63klabs/cache-data package:
|
|
43
|
+
- `npm install @63klabs/cache-data`
|
|
44
|
+
5. Add code to your Lambda function to utilize caching and other cache-data utilities:
|
|
45
|
+
- See [example code for index and handler](./docs/00-example-implementation/example-handler.js)
|
|
46
|
+
- See [example code for config initialization](./docs/00-example-implementation/example-config.js)
|
|
47
|
+
|
|
48
|
+
It is recommended that you use the quick-start method when implementing for the first time. It comes with default values and requires less CloudFormation yaml and Node code.
|
|
49
|
+
|
|
50
|
+
- [Quick Start Implementation](./docs/00-quick-start-implementation/README.md)
|
|
51
|
+
- [Advanced Implementation for Providing a Web Service](./docs/01-advanced-implementation-for-web-service/README.md)
|
|
52
|
+
- [Additional Documentation](./docs/README.md)
|
|
681
53
|
|
|
682
|
-
|
|
683
|
-
host: "api.example.com",
|
|
684
|
-
path: "/person",
|
|
685
|
-
parameters: {id: id, event: event },
|
|
686
|
-
headers: {}
|
|
687
|
-
};
|
|
688
|
-
|
|
689
|
-
const cacheObj = await cache.CacheableDataAccess.getData(
|
|
690
|
-
cacheProfile,
|
|
691
|
-
myCustomDAO_getData,
|
|
692
|
-
conn,
|
|
693
|
-
null
|
|
694
|
-
);
|
|
695
|
-
```
|
|
696
|
-
|
|
697
|
-
### Connections using CachedSSMParameter or CachedSecret
|
|
698
|
-
|
|
699
|
-
Creating a connection is similar to above, we can add an authorization property to the conection:
|
|
700
|
-
|
|
701
|
-
```js
|
|
702
|
-
authentication: {
|
|
703
|
-
parameters: {
|
|
704
|
-
apikey: new tools.CachedSSMParameter('/apps/my_cool_app/demoAPIkey', {refreshAfter: 300}),
|
|
705
|
-
}
|
|
706
|
-
}
|
|
707
|
-
```
|
|
708
|
-
|
|
709
|
-
Learn more about Connection Authentication below.
|
|
710
|
-
|
|
711
|
-
And when calling Cache.init(), pass a CachedSSMParameter (or CachedSecret) to the secureDataKey property:
|
|
712
|
-
|
|
713
|
-
```js
|
|
714
|
-
secureDataKey: new tools.CachedSSMParameter('/apps/my_cool_app/CacheData_SecureDataKey', {refreshAfter: 1600}),
|
|
715
|
-
```
|
|
716
|
-
|
|
717
|
-
```js
|
|
718
|
-
// for games demo from api.chadkluck.net
|
|
719
|
-
connections.add( {
|
|
720
|
-
name: "demo",
|
|
721
|
-
host: "api.chadkluck.net",
|
|
722
|
-
path: "/games",
|
|
723
|
-
parameters: {},
|
|
724
|
-
headers: {
|
|
725
|
-
referer: "https://chadkluck.net"
|
|
726
|
-
},
|
|
727
|
-
authentication: {
|
|
728
|
-
parameters: {
|
|
729
|
-
apikey: new tools.CachedSSMParameter('/apps/my_cool_app/demoAPIkey', {refreshAfter: 300}), // ADDED
|
|
730
|
-
}
|
|
731
|
-
},
|
|
732
|
-
cache: myCacheProfilesArray
|
|
733
|
-
} );
|
|
734
|
-
|
|
735
|
-
tools._ConfigSuperClass._connections = connections;
|
|
736
|
-
|
|
737
|
-
// Cache settings
|
|
738
|
-
cache.Cache.init({
|
|
739
|
-
dynamoDbTable: "yourDynamoDbTable",
|
|
740
|
-
s3Bucket: "yourS3Bucket",
|
|
741
|
-
secureDataAlgorithm: "aes-256-ofb",
|
|
742
|
-
secureDataKey: new tools.CachedSSMParameter('/apps/my_cool_app/CacheData_SecureDataKey', {refreshAfter: 1600}), // CHANGED FROM params.app
|
|
743
|
-
idHashAlgorithm: "RSA-SHA3-512",
|
|
744
|
-
DynamoDbMaxCacheSize_kb: 20,
|
|
745
|
-
purgeExpiredCacheEntriesAfterXHours: 24,
|
|
746
|
-
defaultExpirationExtensionOnErrorInSeconds: 300,
|
|
747
|
-
timeZoneForInterval: "America/Chicago"
|
|
748
|
-
});
|
|
749
|
-
|
|
750
|
-
```
|
|
751
|
-
|
|
752
|
-
### Connections Authentication
|
|
753
|
-
|
|
754
|
-
You can store your authentication methods separate from the headers, parameters, and body properties. You can also use Basic authorization.
|
|
755
|
-
|
|
756
|
-
Just add an `authentication` property to your connection.
|
|
757
|
-
|
|
758
|
-
```js
|
|
759
|
-
// for games demo from api.chadkluck.net
|
|
760
|
-
connections.add( {
|
|
761
|
-
name: "demo",
|
|
762
|
-
host: "api.chadkluck.net",
|
|
763
|
-
path: "/games",
|
|
764
|
-
headers: {
|
|
765
|
-
referer: "https://chadkluck.net"
|
|
766
|
-
},
|
|
767
|
-
authentication: {
|
|
768
|
-
parameters: {
|
|
769
|
-
apikey: new tools.CachedSSMParameter('/apps/my_cool_app/demoAPIkey', {refreshAfter: 1600}), // ADDED
|
|
770
|
-
}
|
|
771
|
-
},
|
|
772
|
-
cache: myCacheProfilesArray
|
|
773
|
-
} );
|
|
774
|
-
|
|
775
|
-
connections.add( {
|
|
776
|
-
name: "demoauthbasic",
|
|
777
|
-
host: "api.chadkluck.net",
|
|
778
|
-
path: "/games",
|
|
779
|
-
headers: {
|
|
780
|
-
referer: "https://chadkluck.net"
|
|
781
|
-
},
|
|
782
|
-
authentication: {
|
|
783
|
-
basic: {
|
|
784
|
-
username: new tools.CachedSSMParameter('/apps/my_cool_app/demoUsername', {refreshAfter: 300}),
|
|
785
|
-
password: new tools.CachedSSMParameter('/apps/my_cool_app/demoPassword', {refreshAfter: 300}),
|
|
786
|
-
}
|
|
787
|
-
},
|
|
788
|
-
cache: myCacheProfilesArray
|
|
789
|
-
} );
|
|
790
|
-
|
|
791
|
-
connections.add( {
|
|
792
|
-
name: "demoauthheaders",
|
|
793
|
-
host: "api.chadkluck.net",
|
|
794
|
-
path: "/games",
|
|
795
|
-
headers: {
|
|
796
|
-
referer: "https://chadkluck.net"
|
|
797
|
-
},
|
|
798
|
-
authentication: {
|
|
799
|
-
headers: {
|
|
800
|
-
'x-api-key': new tools.CachedSSMParameter('/apps/my_cool_app/apiKey', {refreshAfter: 300})
|
|
801
|
-
}
|
|
802
|
-
},
|
|
803
|
-
cache: myCacheProfilesArray
|
|
804
|
-
} );
|
|
805
|
-
|
|
806
|
-
connections.add( {
|
|
807
|
-
name: "demoauthbody",
|
|
808
|
-
host: "api.chadkluck.net",
|
|
809
|
-
path: "/games",
|
|
810
|
-
headers: {
|
|
811
|
-
referer: "https://chadkluck.net"
|
|
812
|
-
},
|
|
813
|
-
authentication: {
|
|
814
|
-
body: {
|
|
815
|
-
'x-api-key': new tools.CachedSSMParameter('/apps/my_cool_app/apiKey', {refreshAfter: 300}),
|
|
816
|
-
'account': new tools.CachedSSMParameter('/apps/my_cool_app/accountId', {refreshAfter: 3600})
|
|
817
|
-
}
|
|
818
|
-
},
|
|
819
|
-
cache: myCacheProfilesArray
|
|
820
|
-
} );
|
|
821
|
-
```
|
|
822
|
-
|
|
823
|
-
### Connections Options
|
|
824
|
-
|
|
825
|
-
Specify a `timeout` in the connection to pass to the http_get command. Default is `8000`.
|
|
826
|
-
|
|
827
|
-
Specify how duplicate parameters in a query string should be handled. This allows you to craft your query string to match what your endpoint expects when it parses the query string.
|
|
828
|
-
|
|
829
|
-
```javascript
|
|
830
|
-
connections.add({
|
|
831
|
-
method: "POST",
|
|
832
|
-
host: "api.chadkluck.net",
|
|
833
|
-
path: "/echo/",
|
|
834
|
-
headers: headers,
|
|
835
|
-
uri: "",
|
|
836
|
-
protocol: "https",
|
|
837
|
-
body: null,
|
|
838
|
-
parameters: {
|
|
839
|
-
greeting: "Hello",
|
|
840
|
-
planets: ["Earth", "Mars"]
|
|
841
|
-
},
|
|
842
|
-
options: {
|
|
843
|
-
timeout: 8000,
|
|
844
|
-
separateDuplicateParameters: false, // default is false
|
|
845
|
-
separateDuplicateParametersAppendToKey: "", // "" "[]", or "0++", "1++"
|
|
846
|
-
combinedDuplicateParameterDelimiter: ','
|
|
847
|
-
}
|
|
848
|
-
})
|
|
849
|
-
```
|
|
850
|
-
|
|
851
|
-
By default the query string used for the request will be:
|
|
852
|
-
|
|
853
|
-
```text
|
|
854
|
-
?greeting=Hello&planets=Earth,Mars
|
|
855
|
-
```
|
|
856
|
-
|
|
857
|
-
However, by changing `separateDuplicateParameters` to `true` and `separateDuplicateParametersAppendToKey` to `[]`:
|
|
858
|
-
|
|
859
|
-
```text
|
|
860
|
-
?greeting=Hello&planets[]=Earth&planets[]=Mars
|
|
861
|
-
```
|
|
862
|
-
|
|
863
|
-
You can also append an index to the end of the parameter:
|
|
864
|
-
|
|
865
|
-
```javascript
|
|
866
|
-
options = {
|
|
867
|
-
separateDuplicateParameters: true,
|
|
868
|
-
separateDuplicateParametersAppendToKey: "0++", // "" "[]", or "0++", "1++"
|
|
869
|
-
}
|
|
870
|
-
// ?greeting=Hello&planets0=Earth&planets1=Mars
|
|
871
|
-
```
|
|
872
|
-
|
|
873
|
-
Similarly, you can start at index 1 instead of 0:
|
|
874
|
-
|
|
875
|
-
```javascript
|
|
876
|
-
options = {
|
|
877
|
-
separateDuplicateParameters: true,
|
|
878
|
-
separateDuplicateParametersAppendToKey: "1++", // "" "[]", or "0++", "1++"
|
|
879
|
-
}
|
|
880
|
-
// ?greeting=Hello&planets1=Earth&planets2=Mars
|
|
881
|
-
```
|
|
882
|
-
|
|
883
|
-
### tools.Timer
|
|
884
|
-
|
|
885
|
-
In its simplist form we can do the following:
|
|
886
|
-
|
|
887
|
-
```js
|
|
888
|
-
/*
|
|
889
|
-
Assuming:
|
|
890
|
-
const { tools, cache, endpoint } = require('@63klabs/cache-data');
|
|
891
|
-
*/
|
|
892
|
-
|
|
893
|
-
const timerTaskGetGames = new tools.Timer("Getting games", true); // We give it a name for logging, and we set to true so the timer starts right away
|
|
894
|
-
|
|
895
|
-
/* A block of code we want to execute and get timing for */
|
|
896
|
-
// do something
|
|
897
|
-
// do something
|
|
898
|
-
|
|
899
|
-
timerTaskGetGames.stop(); // if debug level is >= 3 (DebugAndLog.DIAG) it will log the elapsed time in ms
|
|
900
|
-
```
|
|
901
|
-
|
|
902
|
-
The above code will create a timer which we can access by the variable name `timerTaskGetGames`. Since we set the second parameter to `true` it will start the timer upon creation.
|
|
903
|
-
|
|
904
|
-
Then a block of code will execute.
|
|
905
|
-
|
|
906
|
-
Then we stop the timer using `.stop()` and if the logging level is 3 or greater it will send a log entry with the elapsed time to the console.
|
|
907
|
-
|
|
908
|
-
You are able to get the current time elapsed in milliseconds from a running Timer by calling `const ms = timerVarName.elapsed()`
|
|
909
|
-
|
|
910
|
-
### tools.DebugAndLog
|
|
911
|
-
|
|
912
|
-
```js
|
|
913
|
-
/*
|
|
914
|
-
Assuming:
|
|
915
|
-
const { tools, cache, endpoint } = require('@63klabs/cache-data');
|
|
916
|
-
*/
|
|
917
|
-
|
|
918
|
-
/* increase the log level - comment out when not needed */
|
|
919
|
-
tools.DebugAndLog.setLogLevel(5, "2022-02-28T04:59:59Z"); // we can increase the debug level with an expiration
|
|
920
|
-
|
|
921
|
-
tools.DebugAndLog.debug("Hello World");
|
|
922
|
-
tools.DebugAndLog.msg("The sky is set to be blue today");
|
|
923
|
-
tools.DebugAndLog.diag("Temperature log:", log);
|
|
924
|
-
|
|
925
|
-
try {
|
|
926
|
-
// some code
|
|
927
|
-
} catch (error) {
|
|
928
|
-
tools.DebugAndLog.error("We have an error in try/catch 1", error);
|
|
929
|
-
}
|
|
930
|
-
|
|
931
|
-
try {
|
|
932
|
-
// some code
|
|
933
|
-
} catch (error) {
|
|
934
|
-
tools.DebugAndLog.warn("We have an error but will log it as a warning in try/catch 2", error);
|
|
935
|
-
}
|
|
936
|
-
```
|
|
937
|
-
|
|
938
|
-
Before calling `Config.init()` you can set the log level using `DebugAndLog.setLogLevel()`. If you set the log level after calling `Config.init()` OR after calling any `DebugAndLog` function, you will get an error. That is because a default log level has already been set and we will not allow the changing of the log level after a script has begun.
|
|
939
|
-
|
|
940
|
-
There are six (6) logging functions.
|
|
941
|
-
|
|
942
|
-
```js
|
|
943
|
-
DebugAndLog.error(msgStr, obj); // logs at ALL logging levels
|
|
944
|
-
DebugAndLog.warn(msgStr, obj); // logs at ALL logging levels
|
|
945
|
-
DebugAndLog.log(msgStr, tagStr, obj); // logs at ALL logging levels
|
|
946
|
-
DebugAndLog.msg(msgStr, obj); // logs at level 1 and above
|
|
947
|
-
DebugAndLog.diag(msgStr, obj); // logs at level 3 and above
|
|
948
|
-
DebugAndLog.debug(msgStr, obj); // logs at level 5
|
|
949
|
-
```
|
|
950
|
-
|
|
951
|
-
In the above the `obj` parameter is optional and is an object you wish to log. Be careful of logging objects that may contain sensitive information.
|
|
952
|
-
|
|
953
|
-
Choose the method based on how verbose you want your logging to be at various script levels.
|
|
954
|
-
|
|
955
|
-
Note that `DebugAndLog.log(msgStr, tagStr)` allows you to add a tag. If a tag is not provided `LOG` will be used and your log entry will look like `[LOG] your message`.
|
|
956
|
-
|
|
957
|
-
If you provide `TEMP` as a tag ('temperature' for example) then the log entry will look something like this: `[TEMP] your message`.
|
|
958
|
-
|
|
959
|
-
## Advanced
|
|
960
|
-
|
|
961
|
-
The examples above should get you started.
|
|
962
|
-
|
|
963
|
-
However, there are advanced uses for the Cache object such as caching processed data not from an endpoint and creating your own Data Access Object (DAO) classes.
|
|
964
|
-
|
|
965
|
-
### Caching data not from a remote endpoint
|
|
966
|
-
|
|
967
|
-
Cache does not have to be from a remote endpoint.
|
|
968
|
-
|
|
969
|
-
Suppose you gather data from six endpoints and process the data in a resource and time intensive process and would like to cache the result for 6 or 24 hours. You can use the Cache object to store any data from any source, either externally or internally.
|
|
970
|
-
|
|
971
|
-
The function parameter passed to the Cache object is the method used to obtain data. Remember the `endpoint.getDataDirectFromURI` from the code sample above? That is just a function to return a bare bones response from an api endpoint. (You can actually extend the `endpoint.Endpoint` class and create your own DAOs that can pre and post process data before returning to your application's cache.)
|
|
972
|
-
|
|
973
|
-
Instead of passing in the function `endpoint.getDataDirectFromURI` you can create any function that will grab or process data and return an object.
|
|
974
|
-
|
|
975
|
-
Remember, when passing functions for another function to execute, do not include the `()` at the end.
|
|
976
|
-
|
|
977
|
-
### Creating your own Data Access Object (DAO)
|
|
978
|
-
|
|
979
|
-
You can either extend `endpoint.Endpoint` or create your own.
|
|
980
|
-
|
|
981
|
-
### Sanitize and Obfuscate functions
|
|
982
|
-
|
|
983
|
-
These functions attempt to scrub items labled as 'secret', 'key', 'token' and 'Authorization' from objects for logging purposes.
|
|
984
|
-
|
|
985
|
-
Sanitization is also performed on objects passed to the DebugAndLog logging functions.
|
|
986
|
-
|
|
987
|
-
#### Sanitize
|
|
988
|
-
|
|
989
|
-
You can pass an object to sanitize for logging purposes.
|
|
990
|
-
|
|
991
|
-
NOTE: This is a tool that attempts to sanitize and may miss sensitive information. Inspect the [regular expression used for performing search](https://regex101.com/r/IJp35p/3) for more information. Care should be taken when logging objects for purposes of debugging.
|
|
992
|
-
|
|
993
|
-
What it attempts to do:
|
|
994
|
-
|
|
995
|
-
- Finds object keys with 'secret', 'key', and 'token' in the name and obfuscates their values.
|
|
996
|
-
- It checks string values for key:value and key=value pairs and obfuscates the value side if the key contains the words 'secret', 'key', or 'token'. For example, parameters in a query string `https://www.example.com?client=435&key=1234EXAMPLE783271234567` would produce `https://www.example.com?client=435&key=******4567`
|
|
997
|
-
- It checks for 'Authentication' object keys and sanitizes the value.
|
|
998
|
-
- It checks for multi-value (arrays) of object keys named with secret, key, or token such as `"Client-Secrets":[123456789,1234567890,90987654321]`
|
|
999
|
-
|
|
1000
|
-
```JavaScript
|
|
1001
|
-
// Note: These fake secrets are hard-coded for demo/test purposes only. NEVER hard-code secrets!
|
|
1002
|
-
const obj = {
|
|
1003
|
-
secret: "98765-EXAMPLE-1234567890efcd",
|
|
1004
|
-
apiKey: "123456-EXAMPLE-123456789bcea",
|
|
1005
|
-
kbToken: "ABCD-EXAMPLE-12345678901234567890",
|
|
1006
|
-
queryString: "?site=456&secret=12345EXAMPLE123456&b=1",
|
|
1007
|
-
headers: {
|
|
1008
|
-
Authorization: "Basic someBase64EXAMPLE1234567"
|
|
1009
|
-
}
|
|
1010
|
-
};
|
|
1011
|
-
|
|
1012
|
-
console.log("My Sanitized Object", tools.sanitize(obj));
|
|
1013
|
-
/* output: My Sanitized Object {
|
|
1014
|
-
secret: '******efcd',
|
|
1015
|
-
apiKey: '******bcea',
|
|
1016
|
-
kbToken: '******7890',
|
|
1017
|
-
queryString: '?site=456&secret=******3456&b=1',
|
|
1018
|
-
headers: { Authorization: 'Basic ******4567' }
|
|
1019
|
-
}
|
|
1020
|
-
*/
|
|
1021
|
-
```
|
|
1022
|
-
|
|
1023
|
-
> It is best to avoid logging ANY data that contains sensitive information. While this function provides an extra layer of protection, it should be used sparingly for debugging purposes (not on-going logging) in non-production environments.
|
|
1024
|
-
|
|
1025
|
-
#### Obfuscate
|
|
1026
|
-
|
|
1027
|
-
You can pass a string to obfuscate.
|
|
1028
|
-
|
|
1029
|
-
For example, `12345EXAMPLE7890` will return `******7890`.
|
|
1030
|
-
|
|
1031
|
-
By default, asterisks are used to pad the left-hand side, and only 4 characters are kept on the right. The length of the string returned is not dependent on the length of the string passed in which in turn obfuscates the original length of the string. However, the right side will not reveal more than 25% of the string (it actually rounds up 1 character so a 2 character string would still reveal the final character).
|
|
1032
|
-
|
|
1033
|
-
Default options can be changed by passing an options object.
|
|
1034
|
-
|
|
1035
|
-
```JavaScript
|
|
1036
|
-
const str = "EXAMPLE1234567890123456789";
|
|
1037
|
-
|
|
1038
|
-
console.log( tools.obfuscate(str) );
|
|
1039
|
-
// output: ******6789
|
|
1040
|
-
|
|
1041
|
-
const opt = { keep: 6, char: 'X', len: 16 };
|
|
1042
|
-
console.log( tools.obfuscate(str, opt) );
|
|
1043
|
-
// output: XXXXXXXXXX456789
|
|
1044
|
-
```
|
|
1045
|
-
|
|
1046
|
-
### AWS-SDK
|
|
1047
|
-
|
|
1048
|
-
The @63klabs/cache-data package will automatically detect and use the correct AWS SDK based on the version of Node.
|
|
1049
|
-
|
|
1050
|
-
Node 16 environments will use AWS-SDK version 2.
|
|
1051
|
-
|
|
1052
|
-
Node 18+ environments will use AWS-SDK version 3.
|
|
1053
|
-
|
|
1054
|
-
Note that `package.json` for @63klabs/cache-data only installs the AWS-SDK on dev environments. This is because AWS Lambda already includes the AWS-SDK without requiring installs. This makes your application lighter and ensures you are always running the most recent SDK release. Given this, that means that AWS SDK v3 is not available in Lambda functions using Node 16, and v2 is not available in Lambda Node >=18 environments.
|
|
1055
|
-
|
|
1056
|
-
Because DynamoDb, S3, and SSM Parameter store are used by cache-data, only those SDKs are included. A client is provided for each along with limited number of commands. To make gets and puts easier a get and put command is mapped for DynamoDb and S3. (Uses appropriate commands underneath for V2 and V3 so your code wouldn't need to change.)
|
|
1057
|
-
|
|
1058
|
-
#### `tools.AWS` Object
|
|
1059
|
-
|
|
1060
|
-
When `tools` is imported, you can use the `tools.AWS` object to perform common read/write operations on S3, DynamoDb, and SSM Parameter Store.
|
|
1061
|
-
|
|
1062
|
-
```javascript
|
|
1063
|
-
const { tools } = require('@63klabs/cache-data');
|
|
1064
|
-
|
|
1065
|
-
console.log(`NODE VERSION ${tools.AWS.NODE_VER} USING AWS SDK ${tools.AWS.SDK_VER}`);
|
|
1066
|
-
console.log(`REGION: ${tools.AWS.REGION}`); // set from Lambda environment variable AWS_REGION
|
|
1067
|
-
|
|
1068
|
-
var getParams = {
|
|
1069
|
-
Bucket: 'mybucket', // bucket name,
|
|
1070
|
-
Key: 'hello.txt' // object to get
|
|
1071
|
-
}
|
|
1072
|
-
|
|
1073
|
-
const result = await tools.AWS.s3.get(getParams);
|
|
1074
|
-
|
|
1075
|
-
let objectData = await s3Body.transformToString(); // V3: Object bodies in V3 are readable streams, so we convert to string
|
|
1076
|
-
// let objectData = data.Body.toString('utf-8'); // V2: Object bodies are Buffers, so we convert to string
|
|
1077
|
-
console.log(`hello.txt Body: ${objectData}`);
|
|
1078
|
-
// outputs "hello.txt Body: Hello, World!"
|
|
1079
|
-
|
|
1080
|
-
```
|
|
1081
|
-
|
|
1082
|
-
The `tools.AWS` object provides the following:
|
|
1083
|
-
|
|
1084
|
-
```js
|
|
1085
|
-
{
|
|
1086
|
-
NODE_VER: '20.6.0',
|
|
1087
|
-
NODE_VER_MAJOR: 20,
|
|
1088
|
-
NODE_VER_MINOR: 6,
|
|
1089
|
-
NODE_VER_PATCH: 0,
|
|
1090
|
-
NODE_VER_MAJOR_MINOR: '20.6',
|
|
1091
|
-
NODE_VER_ARRAY: [ 20, 6, 0 ],
|
|
1092
|
-
REGION: "us-east-1", // Set from Node environment process.env.AWS_REGION
|
|
1093
|
-
SDK_VER: "V3",
|
|
1094
|
-
SDK_V2: false, // if (tools.AWS.SDK_V2) { console.log('AWS SDK Version 2!'); }
|
|
1095
|
-
SDK_V3: true, // if (tools.AWS.SDK_V3) { console.log('AWS SDK Version 3!'); }
|
|
1096
|
-
INFO: { /* an object containing all of the properties listed above */ }
|
|
1097
|
-
dynamo: {
|
|
1098
|
-
client: DynamoDBDocumentClient,
|
|
1099
|
-
put: (params) => client.send(new PutCommand(params)), // const result = await tools.AWS.dynamo.put(params);
|
|
1100
|
-
get: (params) => client.send(new GetCommand(params)), // const result = await tools.AWS.dynamo.get(params);
|
|
1101
|
-
scan: (params) => client.send(new ScanCommand(params)), // const result = await tools.AWS.dynamo.scan(params);
|
|
1102
|
-
delete: (params) => client.send(new DeleteCommand(params)), // const result = await tools.AWS.dynamo.delete(params);
|
|
1103
|
-
update: (params) => client.send(new UpdateCommand(params)), // const result = await tools.AWS.dynamo.update(params);
|
|
1104
|
-
sdk: {
|
|
1105
|
-
DynamoDBClient,
|
|
1106
|
-
DynamoDBDocumentClient,
|
|
1107
|
-
GetCommand,
|
|
1108
|
-
PutCommand
|
|
1109
|
-
}
|
|
1110
|
-
},
|
|
1111
|
-
s3: {
|
|
1112
|
-
client: S3,
|
|
1113
|
-
put: (params) => client.send(new PutObjectCommand(params)), // const result = await tools.AWS.s3.put(params)
|
|
1114
|
-
get: (params) => client.send(new GetObjectCommand(params)), // const result = await tools.AWS.s3.get(params)
|
|
1115
|
-
sdk: {
|
|
1116
|
-
S3,
|
|
1117
|
-
GetObjectCommand,
|
|
1118
|
-
PutObjectCommand
|
|
1119
|
-
}
|
|
1120
|
-
|
|
1121
|
-
},
|
|
1122
|
-
ssm: {
|
|
1123
|
-
client: SSMClient,
|
|
1124
|
-
getByName: (params) => client.send(new GetParametersCommand(query)), // const params = await tools.AWS.ssm.getByName(query)
|
|
1125
|
-
getByPath: (params) => client.send(new GetParametersByPathCommand(query)), // const params = await tools.AWS.ssm.getByPath(query)
|
|
1126
|
-
sdk: {
|
|
1127
|
-
SSMClient,
|
|
1128
|
-
GetParametersByPathCommand,
|
|
1129
|
-
GetParametersCommand
|
|
1130
|
-
}
|
|
1131
|
-
}
|
|
1132
|
-
}
|
|
1133
|
-
```
|
|
1134
|
-
|
|
1135
|
-
Because Node 16 and the AWS SDK v2 are being deprecated, this documentation will mainly cover AWS SDK v3. However, `{DynamoDb, S3, SSM}` are still available when your environment is using Node 16 and AWS SDK v2 by importing `tools` from cache-data and accessing the `AWS` class. (See Using AWS SDK V2 through tools.AWS (Deprecated) below.)
|
|
1136
|
-
|
|
1137
|
-
##### Using AWS SDK V3 through tools.AWS
|
|
1138
|
-
|
|
1139
|
-
To use the AWS SDK you normally have to import the proper SDKs and libraries, create a client, and then send the commands. The way this is accomplished in version 2 and version 3 of the AWS SDK is slightly different. How to use the AWS SDK is beyond the scope of this package. However, since the package uses reads and writes to S3 objects, DynamoDb tables, and SSM Parameter store, it readily makes these commands available through the `AWS` object from `tools`.
|
|
1140
|
-
|
|
1141
|
-
Also, as a shortcut as you move from Node 16 and Node 18 (and above), the methods exposed will not differ as it automatically uses the correct methods for the loaded SDK.
|
|
1142
|
-
|
|
1143
|
-
To use the methds you only need to pass the parameter or query object as you normally would.
|
|
1144
|
-
|
|
1145
|
-
```javascript
|
|
1146
|
-
// Given the two parameter/query objects:
|
|
1147
|
-
|
|
1148
|
-
let paramsForPut = {
|
|
1149
|
-
TableName: 'myTable',
|
|
1150
|
-
Item: {
|
|
1151
|
-
'hash_id': '8e91cef4a27',
|
|
1152
|
-
'episode_name': "There's No Disgrace Like Home",
|
|
1153
|
-
'air_date': "1990-01-28",
|
|
1154
|
-
'production_code': '7G04'
|
|
1155
|
-
}
|
|
1156
|
-
}
|
|
1157
|
-
|
|
1158
|
-
let paramsForGet = {
|
|
1159
|
-
TableName: 'myTable',
|
|
1160
|
-
Key: {'hash_id': '8e91cef4a27'}
|
|
1161
|
-
};
|
|
1162
|
-
```
|
|
1163
|
-
|
|
1164
|
-
```javascript
|
|
1165
|
-
// Using AWS SDK V2
|
|
1166
|
-
const { DynamoDb } = require('aws-sdk');
|
|
1167
|
-
|
|
1168
|
-
const dbDocClient = new DynamoDB.DocumentClient( {region: 'us-east-1'} );
|
|
1169
|
-
|
|
1170
|
-
const dbPutResult = await dbDocClient.put(paramsForNewRecord).promise();
|
|
1171
|
-
const dbGetResult = await dbDocClient.get(paramsForGet).promise();
|
|
1172
|
-
```
|
|
1173
|
-
|
|
1174
|
-
```javascript
|
|
1175
|
-
// Using AWS SDK V3
|
|
1176
|
-
const { DynamoDBClient} = require("@aws-sdk/client-dynamodb");
|
|
1177
|
-
const { DynamoDBDocumentClient, GetCommand, PutCommand} = require("@aws-sdk/lib-dynamodb");
|
|
1178
|
-
|
|
1179
|
-
const dbClient = new DynamoDBClient({ region: AWS.REGION });
|
|
1180
|
-
const dbDocClient = DynamoDBDocumentClient.from(dbClient);
|
|
1181
|
-
|
|
1182
|
-
const dbPutResult = await dbDocClient.send(PutCommand(paramsForNewRecord));
|
|
1183
|
-
const dbGetResult = await dbDocClient.send(GetCommand(paramsForGetRecord));
|
|
1184
|
-
```
|
|
1185
|
-
|
|
1186
|
-
```javascript
|
|
1187
|
-
// Using tools to handle the SDK version and basic calls for you
|
|
1188
|
-
const { tools } = require('@63klabs/cache-data');
|
|
1189
|
-
|
|
1190
|
-
const dbPutResult = await tools.AWS.dynamodb.put(paramsForNewRecord);
|
|
1191
|
-
const dbGetResult = await tools.AWS.dynamodb.get(paramsForGetRecrod);
|
|
1192
|
-
```
|
|
1193
|
-
|
|
1194
|
-
Refer to the section about the tools.AWS above for the variables, methods, and SDK objects available.
|
|
1195
|
-
|
|
1196
|
-
For more on creating parameter/query objects for S3, DynamoDb, and SSM Parameter Store:
|
|
1197
|
-
|
|
1198
|
-
- [Amazon S3 examples using SDK for JavaScript (v3)](https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/javascript_s3_code_examples.html)
|
|
1199
|
-
- [Using the DynamoDB Document Client](https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/dynamodb-example-dynamodb-utilities.html)
|
|
1200
|
-
- []()
|
|
1201
|
-
|
|
1202
|
-
##### Import Additional Commands
|
|
1203
|
-
|
|
1204
|
-
When using AWS SDK version 3, you can import additional commands and use them with the client provided by `tools.AWS`.
|
|
1205
|
-
|
|
1206
|
-
```javascript
|
|
1207
|
-
const { tools } = require('@63klabs/cache-data');
|
|
1208
|
-
const { DeleteObjectCommand } = require('@aws-sdk/client-s3'); // AWS SDK v3
|
|
1209
|
-
|
|
1210
|
-
const command = new DeleteObjectCommand({
|
|
1211
|
-
Bucket: "myBucket",
|
|
1212
|
-
Key: "good-bye.txt"
|
|
1213
|
-
});
|
|
1214
|
-
|
|
1215
|
-
const response = await tools.AWS.s3.client.send(command);
|
|
1216
|
-
```
|
|
1217
|
-
|
|
1218
|
-
#### Using AWS SDK V2 through tools.AWS (Deprecated)
|
|
54
|
+
## Help
|
|
1219
55
|
|
|
1220
|
-
|
|
56
|
+
Make sure you have your S3 bucket, DynamoDb table, and SSM Parameter store set up. Also make sure that you have IAM policies to allow your Lambda function access to these, and CodeBuild to read and write to SSM Parameter store.
|
|
1221
57
|
|
|
1222
|
-
|
|
1223
|
-
// NodeJS 16 using AWS SDK v2
|
|
1224
|
-
const {tools} = require("@63klabs/cache-data");
|
|
58
|
+
Review the [Documentation](./docs/README.md) which includes implementation guides, example code and templates, cache data features, and lambda optimization best practices.
|
|
1225
59
|
|
|
1226
|
-
|
|
1227
|
-
const s3result1 = await tools.AWS.s3.client.putObject(params).promise();
|
|
60
|
+
A full implementation example and tutorial is provided as one of the Atlantis Application Starters available through the [Atlantis Tutorials repository](https://github.com/63klabs/atlantis-tutorials). (Atlantis is a collection of templates and deployment scripts to assist in starting and automating serverless deployments using AWS SAM and CloudFormation.)
|
|
1228
61
|
|
|
1229
|
-
|
|
1230
|
-
const s3client = new tools.AWS.s3.sdk.S3();
|
|
1231
|
-
const s3result2 = await s3Client.putObject(params).promise();
|
|
62
|
+
## Security
|
|
1232
63
|
|
|
1233
|
-
|
|
1234
|
-
const dbResult1 = await tools.AWS.dynamo.client.put(params).promise(); // tools.AWS.dynamo.client uses DynamoDB.DocumentClient
|
|
64
|
+
See [SECURITY](./SECURITY.md) for information on reporting concerns.
|
|
1235
65
|
|
|
1236
|
-
|
|
1237
|
-
const dbClient = new tools.AWS.dynamo.sdk.DynamoDB.DocumentClient( {region: 'us-east-1'} );
|
|
1238
|
-
const dbResult2 = await dbClient.put(params).promise(),
|
|
1239
|
-
```
|
|
66
|
+
## Change Log
|
|
1240
67
|
|
|
1241
|
-
|
|
68
|
+
See [Change Log](CHANGELOG.md) for version history and changes.
|
|
1242
69
|
|
|
1243
|
-
|
|
70
|
+
## Issues, Features, and Enhancements
|
|
1244
71
|
|
|
1245
|
-
|
|
72
|
+
Visit the [Issues section of the @63Klabs Cache-Data GitHub repository](https://github.com/63klabs/cache-data) for information on reported issues, upcoming fixes and enhancements, and to submit requests.
|
|
1246
73
|
|
|
1247
|
-
##
|
|
74
|
+
## License
|
|
1248
75
|
|
|
1249
|
-
|
|
76
|
+
This project is licensed under the MIT License - see the LICENSE.txt file for details
|
|
1250
77
|
|
|
1251
78
|
## Author
|
|
1252
79
|
|
|
1253
|
-
### Chad Kluck
|
|
80
|
+
### Chad Kluck
|
|
1254
81
|
|
|
82
|
+
- Software, DevOps, and Developer Experience Engineer
|
|
83
|
+
- [AWS Certified Developer - Associate](https://www.credly.com/users/chad-kluck/badges)
|
|
1255
84
|
- [Website](https://chadkluck.me/)
|
|
1256
85
|
- [GitHub](https://github.com/chadkluck)
|
|
1257
|
-
- [
|
|
1258
|
-
- [
|
|
1259
|
-
|
|
1260
|
-
## Version History
|
|
1261
|
-
|
|
1262
|
-
Refer to the [Change Log](CHANGELOG.md)
|
|
1263
|
-
|
|
1264
|
-
## License
|
|
1265
|
-
|
|
1266
|
-
This project is licensed under the MIT License - see the LICENSE.txt file for details
|
|
86
|
+
- [GitHub (63Klabs)](https://github.com/63klabs)
|
|
87
|
+
- [Mastodon: @chadkluck@universeodon.com](https://universeodon.com/@chadkluck)
|
|
88
|
+
- [LinkedIn](https://www.linkedin.com/in/chadkluck/)
|