@63klabs/cache-data 1.3.5 → 1.3.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 +49 -2
- package/CONTRIBUTING.md +167 -0
- package/README.md +139 -27
- package/package.json +13 -5
- package/src/lib/dao-cache.js +1418 -294
- package/src/lib/dao-endpoint.js +165 -41
- package/src/lib/tools/AWS.classes.js +82 -0
- package/src/lib/tools/CachedParametersSecrets.classes.js +98 -7
- package/src/lib/tools/ClientRequest.class.js +43 -10
- package/src/lib/tools/Connections.classes.js +148 -13
- package/src/lib/tools/DebugAndLog.class.js +244 -75
- package/src/lib/tools/ImmutableObject.class.js +44 -2
- package/src/lib/tools/RequestInfo.class.js +38 -0
- package/src/lib/tools/Response.class.js +245 -81
- package/src/lib/tools/ResponseDataModel.class.js +123 -47
- package/src/lib/tools/Timer.class.js +138 -26
- package/src/lib/tools/index.js +89 -2
- package/src/lib/tools/utils.js +40 -4
- package/src/lib/utils/InMemoryCache.js +221 -0
package/src/lib/dao-endpoint.js
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* via the APIRequest class.
|
|
12
12
|
*
|
|
13
13
|
* The class itself is not exposed, instead various functions can be used
|
|
14
|
-
* to access the class. For exmaple,
|
|
14
|
+
* to access the class. For exmaple, get(connection, data)
|
|
15
15
|
*
|
|
16
16
|
* The connection parameter is used to pass connection information to the
|
|
17
17
|
* API (host, path, query, etc).
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
*
|
|
22
22
|
* @example
|
|
23
23
|
* // access function that utilizes the class
|
|
24
|
-
* const
|
|
24
|
+
* const get = async (connection, data = null) => {
|
|
25
25
|
* return (new Endpoint(connection).get());
|
|
26
26
|
* };
|
|
27
27
|
*/
|
|
@@ -33,19 +33,20 @@
|
|
|
33
33
|
*/
|
|
34
34
|
|
|
35
35
|
/**
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
* @
|
|
39
|
-
* @property {string}
|
|
40
|
-
* @property {string}
|
|
41
|
-
* @property {string}
|
|
42
|
-
* @property {string}
|
|
43
|
-
* @property {string}
|
|
44
|
-
* @property {
|
|
45
|
-
* @property {
|
|
46
|
-
* @property {
|
|
47
|
-
* @property {
|
|
48
|
-
* @property {
|
|
36
|
+
* Connection configuration object for making endpoint requests.
|
|
37
|
+
*
|
|
38
|
+
* @typedef {Object} ConnectionObject
|
|
39
|
+
* @property {string} [method="GET"] - HTTP method (GET, POST, PUT, DELETE, etc.)
|
|
40
|
+
* @property {string} [uri] - Complete URI including protocol, host, and path (alternative to separate protocol/host/path)
|
|
41
|
+
* @property {string} [protocol="https"] - Protocol to use (http or https)
|
|
42
|
+
* @property {string} [host] - Hostname or IP address of the endpoint
|
|
43
|
+
* @property {string} [path] - Path portion of the URL
|
|
44
|
+
* @property {string|null} [body=null] - Request body for POST/PUT requests
|
|
45
|
+
* @property {Object.<string, string|number|boolean>|null} [parameters=null] - Query string parameters as key-value pairs
|
|
46
|
+
* @property {Object.<string, string>|null} [headers=null] - HTTP headers as key-value pairs
|
|
47
|
+
* @property {Object} [options] - Additional request options
|
|
48
|
+
* @property {number} [options.timeout] - Request timeout in milliseconds
|
|
49
|
+
* @property {string} [note="Get data from endpoint"] - Descriptive note for logging purposes
|
|
49
50
|
*/
|
|
50
51
|
|
|
51
52
|
/*
|
|
@@ -57,27 +58,112 @@
|
|
|
57
58
|
const tools = require("./tools/index.js");
|
|
58
59
|
|
|
59
60
|
/**
|
|
61
|
+
* Makes a GET request to a remote endpoint with the specified connection configuration.
|
|
62
|
+
*
|
|
63
|
+
* This function provides a simple interface for making HTTP requests to external APIs
|
|
64
|
+
* or services. It supports both URI-based and component-based (protocol/host/path)
|
|
65
|
+
* connection specifications. Query parameters can be provided either in the connection
|
|
66
|
+
* object or as a separate query parameter, which will be merged together.
|
|
67
|
+
*
|
|
68
|
+
* The response body is automatically parsed as JSON if possible, otherwise returned as text.
|
|
69
|
+
*
|
|
70
|
+
* @param {ConnectionObject} connection - Connection configuration object specifying the endpoint details
|
|
71
|
+
* @param {Object} [query={}] - Additional query data to merge with connection parameters. If query.parameters is provided, it will be merged with connection.parameters
|
|
72
|
+
* @returns {Promise<{success: boolean, statusCode: number, body: Object|string|null, headers: Object}>} Response object containing success status, HTTP status code, parsed body, and response headers
|
|
73
|
+
* @throws {Error} Throws an error if the request fails due to network issues or invalid configuration
|
|
60
74
|
*
|
|
61
|
-
* @param {ConnectionObject} connection An object with details about the connection (method, uri, host, etc)
|
|
62
|
-
* @param {Object} query Additional data to perform a query for the request.
|
|
63
|
-
* @returns {Object} The response
|
|
64
75
|
* @example
|
|
65
|
-
|
|
66
|
-
|
|
76
|
+
* // Using separate host and path
|
|
77
|
+
* const { endpoint } = require("@63klabs/cache-data");
|
|
78
|
+
* const response = await endpoint.get(
|
|
79
|
+
* { host: "api.example.com", path: "/data" },
|
|
80
|
+
* { parameters: { q: "Chicago" } }
|
|
81
|
+
* );
|
|
82
|
+
* console.log(response.body);
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* // Using complete URI
|
|
86
|
+
* const { endpoint } = require("@63klabs/cache-data");
|
|
87
|
+
* const response = await endpoint.get(
|
|
88
|
+
* { uri: "https://api.example.com/data" },
|
|
89
|
+
* { parameters: { q: "Chicago" } }
|
|
90
|
+
* );
|
|
91
|
+
* console.log(response.body);
|
|
92
|
+
*
|
|
93
|
+
* @example
|
|
94
|
+
* // With custom headers and timeout
|
|
95
|
+
* const { endpoint } = require("@63klabs/cache-data");
|
|
96
|
+
* const response = await endpoint.get({
|
|
97
|
+
* host: "api.example.com",
|
|
98
|
+
* path: "/secure/data",
|
|
99
|
+
* headers: { "Authorization": "Bearer token123" },
|
|
100
|
+
* options: { timeout: 5000 }
|
|
101
|
+
* });
|
|
102
|
+
*
|
|
103
|
+
* @example
|
|
104
|
+
* // POST request with body
|
|
105
|
+
* const { endpoint } = require("@63klabs/cache-data");
|
|
106
|
+
* const response = await endpoint.get({
|
|
107
|
+
* method: "POST",
|
|
108
|
+
* uri: "https://api.example.com/submit",
|
|
109
|
+
* body: JSON.stringify({ name: "John", age: 30 }),
|
|
110
|
+
* headers: { "Content-Type": "application/json" }
|
|
111
|
+
* });
|
|
67
112
|
*/
|
|
68
|
-
const get = async (connection, query =
|
|
113
|
+
const get = async (connection, query = {}) => {
|
|
114
|
+
if (query === null) { query = {} };
|
|
69
115
|
return (new Endpoint(connection, query).get());
|
|
70
116
|
};
|
|
71
117
|
|
|
72
118
|
/**
|
|
73
|
-
*
|
|
74
|
-
*
|
|
119
|
+
* Endpoint request class for making HTTP requests to remote APIs.
|
|
120
|
+
*
|
|
121
|
+
* This class provides a bare-bones implementation for making API requests and can be used
|
|
122
|
+
* as a template to create more elaborate request handlers with custom logic for data
|
|
123
|
+
* manipulation before/after requests. The class handles connection configuration, parameter
|
|
124
|
+
* merging, and automatic JSON parsing of responses.
|
|
125
|
+
*
|
|
126
|
+
* The class is typically not instantiated directly but accessed through convenience functions
|
|
127
|
+
* like `endpoint.get()`. However, it can be extended to add custom pre/post-processing logic.
|
|
128
|
+
*
|
|
129
|
+
* @example
|
|
130
|
+
* // Direct instantiation (advanced usage)
|
|
131
|
+
* const endpoint = new Endpoint({ host: "api.example.com", path: "/data" });
|
|
132
|
+
* const response = await endpoint.get();
|
|
133
|
+
*
|
|
134
|
+
* @example
|
|
135
|
+
* // Extending for custom logic
|
|
136
|
+
* class CustomEndpoint extends Endpoint {
|
|
137
|
+
* async get() {
|
|
138
|
+
* // Custom pre-processing
|
|
139
|
+
* const response = await super.get();
|
|
140
|
+
* // Custom post-processing
|
|
141
|
+
* return response;
|
|
142
|
+
* }
|
|
143
|
+
* }
|
|
75
144
|
*/
|
|
76
145
|
class Endpoint {
|
|
77
146
|
|
|
78
147
|
/**
|
|
148
|
+
* Creates a new Endpoint instance with the specified connection configuration.
|
|
149
|
+
*
|
|
150
|
+
* The constructor initializes the request configuration by merging connection settings
|
|
151
|
+
* with query parameters. If query.parameters are provided, they are merged with
|
|
152
|
+
* connection.parameters. All connection properties are set with appropriate defaults.
|
|
79
153
|
*
|
|
80
|
-
* @param {ConnectionObject} connection
|
|
154
|
+
* @param {ConnectionObject} connection - Connection configuration object with endpoint details
|
|
155
|
+
* @param {Object} [query={}] - Additional query data to merge with connection. If query.parameters is provided, it will be merged with connection.parameters
|
|
156
|
+
*
|
|
157
|
+
* @example
|
|
158
|
+
* // Basic constructor usage
|
|
159
|
+
* const endpoint = new Endpoint({ host: "api.example.com", path: "/users" });
|
|
160
|
+
*
|
|
161
|
+
* @example
|
|
162
|
+
* // With query parameters
|
|
163
|
+
* const endpoint = new Endpoint(
|
|
164
|
+
* { host: "api.example.com", path: "/search" },
|
|
165
|
+
* { parameters: { q: "javascript", limit: 10 } }
|
|
166
|
+
* );
|
|
81
167
|
*/
|
|
82
168
|
constructor(connection, query = {}) {
|
|
83
169
|
|
|
@@ -109,12 +195,22 @@ class Endpoint {
|
|
|
109
195
|
};
|
|
110
196
|
|
|
111
197
|
/**
|
|
112
|
-
*
|
|
113
|
-
*
|
|
114
|
-
*
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
*
|
|
198
|
+
* Sets a request setting from the connection object or uses a default value.
|
|
199
|
+
*
|
|
200
|
+
* This internal helper method checks if a key exists in the connection object.
|
|
201
|
+
* If the key exists, it returns the value; otherwise, it sets the key to the
|
|
202
|
+
* default value and returns that default. This ensures all request settings
|
|
203
|
+
* have valid values.
|
|
204
|
+
*
|
|
205
|
+
* @param {ConnectionObject} connection - The connection object to check for the key
|
|
206
|
+
* @param {string} key - The property key to check for and retrieve
|
|
207
|
+
* @param {*} defaultValue - The default value to use if the key is not found
|
|
208
|
+
* @returns {*} The value from the connection object if found, otherwise the default value
|
|
209
|
+
*
|
|
210
|
+
* @example
|
|
211
|
+
* // Internal usage within constructor
|
|
212
|
+
* this.request.method = this._setRequestSetting(connection, "method", "GET");
|
|
213
|
+
* // If connection.method exists, uses that value; otherwise uses "GET"
|
|
118
214
|
*/
|
|
119
215
|
_setRequestSetting(connection, key, defaultValue) {
|
|
120
216
|
if (!(key in connection)) {
|
|
@@ -125,18 +221,36 @@ class Endpoint {
|
|
|
125
221
|
};
|
|
126
222
|
|
|
127
223
|
/**
|
|
128
|
-
*
|
|
129
|
-
* is called.
|
|
224
|
+
* Executes the HTTP request and returns the response.
|
|
130
225
|
*
|
|
131
|
-
*
|
|
132
|
-
*
|
|
226
|
+
* This method sends the configured request to the remote endpoint using the APIRequest
|
|
227
|
+
* class. It automatically attempts to parse the response body as JSON. If the body is
|
|
228
|
+
* not valid JSON, it is kept as text. The method caches the response so subsequent
|
|
229
|
+
* calls return the same result without making additional requests.
|
|
230
|
+
*
|
|
231
|
+
* This method can be overridden in subclasses to add custom pre-processing (before the
|
|
232
|
+
* request) or post-processing (after the response) logic.
|
|
233
|
+
*
|
|
234
|
+
* @returns {Promise<{success: boolean, statusCode: number, body: Object|string|null, headers: Object}>} Response object with success status, HTTP status code, parsed body, and headers
|
|
235
|
+
* @throws {Error} Throws an error if the request fails due to network issues, timeout, or invalid configuration
|
|
236
|
+
*
|
|
237
|
+
* @example
|
|
238
|
+
* // Basic usage through the get() function
|
|
239
|
+
* const endpoint = new Endpoint({ host: "api.example.com", path: "/data" });
|
|
240
|
+
* const response = await endpoint.get();
|
|
241
|
+
* if (response.success) {
|
|
242
|
+
* console.log(response.body);
|
|
243
|
+
* }
|
|
133
244
|
*
|
|
134
245
|
* @example
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
*
|
|
246
|
+
* // Handling errors
|
|
247
|
+
* try {
|
|
248
|
+
* const endpoint = new Endpoint({ uri: "https://api.example.com/data" });
|
|
249
|
+
* const response = await endpoint.get();
|
|
250
|
+
* console.log(response.statusCode, response.body);
|
|
251
|
+
* } catch (error) {
|
|
252
|
+
* console.error("Request failed:", error.message);
|
|
253
|
+
* }
|
|
140
254
|
*/
|
|
141
255
|
async get() {
|
|
142
256
|
|
|
@@ -173,8 +287,18 @@ class Endpoint {
|
|
|
173
287
|
}
|
|
174
288
|
|
|
175
289
|
/**
|
|
176
|
-
*
|
|
177
|
-
*
|
|
290
|
+
* Internal method that makes the actual HTTP request using the APIRequest class.
|
|
291
|
+
*
|
|
292
|
+
* This method creates an APIRequest instance with the configured request settings
|
|
293
|
+
* and sends the request. It handles errors by logging them and returning a formatted
|
|
294
|
+
* error response. This method is called internally by the get() method.
|
|
295
|
+
*
|
|
296
|
+
* @returns {Promise<{success: boolean, statusCode: number, body: Object|string|null, headers: Object}>} Response object from the APIRequest
|
|
297
|
+
* @throws {Error} Throws an error if the APIRequest instantiation or send operation fails
|
|
298
|
+
*
|
|
299
|
+
* @example
|
|
300
|
+
* // Internal usage (not typically called directly)
|
|
301
|
+
* const response = await this._call();
|
|
178
302
|
*/
|
|
179
303
|
async _call() {
|
|
180
304
|
|
|
@@ -1,9 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Can a value be considered true?
|
|
3
|
+
* "true", "1", 1, and true are all true
|
|
4
|
+
* @param {boolean|number|string|null|undefined} value
|
|
5
|
+
* @returns {boolean} Whether or not the value could be considered true
|
|
6
|
+
*/
|
|
1
7
|
const isTrue = (value) => {
|
|
2
8
|
return (value !== null && typeof value !== 'undefined' &&
|
|
3
9
|
(value === true || value === 1 || value === "1" ||
|
|
4
10
|
(typeof value === 'string' && value.toLowerCase() === "true")));
|
|
5
11
|
};
|
|
6
12
|
|
|
13
|
+
/**
|
|
14
|
+
* Should we enable X-Ray?
|
|
15
|
+
* Checks for either CacheData_AWSXRayOn or CACHE_DATA_AWS_X_RAY_ON in process.env
|
|
16
|
+
* @returns {boolean} True if X-Ray is enabled
|
|
17
|
+
*/
|
|
7
18
|
const USE_XRAY = isTrue(process.env?.CacheData_AWSXRayOn) || isTrue(process.env?.CACHE_DATA_AWS_X_RAY_ON);
|
|
8
19
|
|
|
9
20
|
let AWSXRay = null;
|
|
@@ -88,12 +99,19 @@ class AWS {
|
|
|
88
99
|
static #nodeVer = [];
|
|
89
100
|
static #aws_region = null;
|
|
90
101
|
|
|
102
|
+
/**
|
|
103
|
+
* @private
|
|
104
|
+
* @returns {boolean} True if X-Ray is enabled and initialized
|
|
105
|
+
*/
|
|
91
106
|
static get #XRayOn() {
|
|
92
107
|
return initializeXRay() !== null;
|
|
93
108
|
}
|
|
94
109
|
|
|
95
110
|
constructor() {}
|
|
96
111
|
|
|
112
|
+
/**
|
|
113
|
+
* @returns {number[]} Node version in array [major, minor, patch]
|
|
114
|
+
*/
|
|
97
115
|
static get nodeVersionArray() {
|
|
98
116
|
if (this.#nodeVer.length === 0) {
|
|
99
117
|
// split this.NODE_VER into an array of integers
|
|
@@ -102,6 +120,12 @@ class AWS {
|
|
|
102
120
|
return this.#nodeVer;
|
|
103
121
|
};
|
|
104
122
|
|
|
123
|
+
/**
|
|
124
|
+
* Returns the AWS Region from process.env.AWS_REGION.
|
|
125
|
+
* If not set, it will return 'us-east-1' and issue a warning.
|
|
126
|
+
*
|
|
127
|
+
* @returns {string} AWS Region
|
|
128
|
+
*/
|
|
105
129
|
static get region() {
|
|
106
130
|
if (this.#aws_region === null) {
|
|
107
131
|
|
|
@@ -122,17 +146,59 @@ class AWS {
|
|
|
122
146
|
return this.#aws_region;
|
|
123
147
|
}
|
|
124
148
|
|
|
149
|
+
/**
|
|
150
|
+
* @returns {string} Node.js version ex: '24.12.0'
|
|
151
|
+
*/
|
|
125
152
|
static get NODE_VER() { return ( ("versions" in process && "node" in process.versions) ? process.versions.node : "0.0.0"); }
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* @returns {number} Node.js major version ex: 24
|
|
156
|
+
*/
|
|
126
157
|
static get NODE_VER_MAJOR() { return ( this.nodeVersionArray[0] ); }
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* @returns {number} Node.js minor version ex: 12
|
|
161
|
+
*/
|
|
127
162
|
static get NODE_VER_MINOR() { return ( this.nodeVersionArray[1] ); }
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* @returns {number} Node.js patch version ex: 0
|
|
166
|
+
*/
|
|
128
167
|
static get NODE_VER_PATCH() { return ( this.nodeVersionArray[2] ); }
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* @returns {string} Node.js major.minor version ex: '24.12'
|
|
171
|
+
*/
|
|
129
172
|
static get NODE_VER_MAJOR_MINOR() { return (this.nodeVersionArray[0] + "." + this.nodeVersionArray[1]); }
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* @returns {number[]} Node.js version array [major, minor, patch]
|
|
176
|
+
*/
|
|
130
177
|
static get NODE_VER_ARRAY() { return (this.nodeVersionArray); }
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* @returns {'V2'|'V3'} AWS SDK Version being used based on Node.js version. V2 for Node.js < 18, V3 for Node.js >= 18
|
|
181
|
+
*/
|
|
131
182
|
static get SDK_VER() { return ((this.NODE_VER_MAJOR < 18) ? "V2" : "V3"); }
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* @returns {string} AWS Region
|
|
186
|
+
*/
|
|
132
187
|
static get REGION() { return ( this.region ); }
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* @returns {boolean} True if using AWS SDK v2
|
|
191
|
+
*/
|
|
133
192
|
static get SDK_V2() { return (this.SDK_VER === "V2"); }
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* @returns {boolean} True if using AWS SDK v3
|
|
196
|
+
*/
|
|
134
197
|
static get SDK_V3() { return (this.SDK_VER === "V3"); }
|
|
135
198
|
|
|
199
|
+
/**
|
|
200
|
+
* @returns {object} Information about the current Node.js and AWS environment
|
|
201
|
+
*/
|
|
136
202
|
static get INFO() {
|
|
137
203
|
return ( {
|
|
138
204
|
NODE_VER: this.NODE_VER,
|
|
@@ -149,6 +215,10 @@ class AWS {
|
|
|
149
215
|
});
|
|
150
216
|
}
|
|
151
217
|
|
|
218
|
+
/**
|
|
219
|
+
* @private
|
|
220
|
+
* @returns {object} SDK v3 objects
|
|
221
|
+
*/
|
|
152
222
|
static #SDK = (
|
|
153
223
|
function(){
|
|
154
224
|
|
|
@@ -209,6 +279,9 @@ class AWS {
|
|
|
209
279
|
}
|
|
210
280
|
)();
|
|
211
281
|
|
|
282
|
+
/**
|
|
283
|
+
* @returns {object} DynamoDB Document Client and functions for put, get, scan, delete, update
|
|
284
|
+
*/
|
|
212
285
|
static get dynamo() {
|
|
213
286
|
return {
|
|
214
287
|
client: this.#SDK.dynamo.client,
|
|
@@ -221,6 +294,9 @@ class AWS {
|
|
|
221
294
|
};
|
|
222
295
|
}
|
|
223
296
|
|
|
297
|
+
/**
|
|
298
|
+
* @returns {object} S3 Client and functions for put, get
|
|
299
|
+
*/
|
|
224
300
|
static get s3() {
|
|
225
301
|
return {
|
|
226
302
|
client: this.#SDK.s3.client,
|
|
@@ -230,6 +306,9 @@ class AWS {
|
|
|
230
306
|
};
|
|
231
307
|
}
|
|
232
308
|
|
|
309
|
+
/**
|
|
310
|
+
* @returns {object} SSM Client and functions for getByName, getByPath
|
|
311
|
+
*/
|
|
233
312
|
static get ssm() {
|
|
234
313
|
return {
|
|
235
314
|
client: this.#SDK.ssm.client,
|
|
@@ -239,6 +318,9 @@ class AWS {
|
|
|
239
318
|
};
|
|
240
319
|
}
|
|
241
320
|
|
|
321
|
+
/**
|
|
322
|
+
* @returns {object} AWS X-Ray SDK
|
|
323
|
+
*/
|
|
242
324
|
static get XRay() {
|
|
243
325
|
return AWSXRay;
|
|
244
326
|
}
|
|
@@ -18,11 +18,24 @@ const Timer = require('./Timer.class');
|
|
|
18
18
|
* https://aws.amazon.com/blogs/compute/using-the-aws-parameter-and-secrets-lambda-extension-to-cache-parameters-and-secrets/
|
|
19
19
|
*************************************************************************** */
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
/**
|
|
22
|
+
* @class CachedParameterSecrets - Container class for CachedParameterSecret objects
|
|
23
|
+
* @example
|
|
24
|
+
* // Create parameters and secrets
|
|
25
|
+
* const dbPassword = new CachedSSMParameter('/myapp/db/password');
|
|
26
|
+
* const apiKey = new CachedSecret('myapp-api-key');
|
|
27
|
+
*
|
|
28
|
+
* // Prime all parameters and secrets before use
|
|
29
|
+
* await CachedParameterSecrets.prime();
|
|
30
|
+
*
|
|
31
|
+
* // Get all names
|
|
32
|
+
* const names = CachedParameterSecrets.getNames();
|
|
33
|
+
* console.log(names); // ['/myapp/db/password', 'myapp-api-key']
|
|
34
|
+
*
|
|
35
|
+
* // Get specific parameter
|
|
36
|
+
* const param = CachedParameterSecrets.get('/myapp/db/password');
|
|
37
|
+
* const value = await param.getValue();
|
|
24
38
|
*/
|
|
25
|
-
|
|
26
39
|
class CachedParameterSecrets {
|
|
27
40
|
/**
|
|
28
41
|
* @typedef {Array<CachedParameterSecret>}
|
|
@@ -30,14 +43,14 @@ class CachedParameterSecrets {
|
|
|
30
43
|
static #cachedParameterSecrets = [];
|
|
31
44
|
|
|
32
45
|
/**
|
|
33
|
-
* @param {CachedParameterSecret} The CachedParameterSecret object to add
|
|
46
|
+
* @param {CachedParameterSecret} cachedParameterSecretObject - The CachedParameterSecret object to add
|
|
34
47
|
*/
|
|
35
48
|
static add (cachedParameterSecretObject) {
|
|
36
49
|
CachedParameterSecrets.#cachedParameterSecrets.push(cachedParameterSecretObject);
|
|
37
50
|
}
|
|
38
51
|
|
|
39
52
|
/**
|
|
40
|
-
* @param {string} The Parameter name or Secret Id to locate
|
|
53
|
+
* @param {string} name - The Parameter name or Secret Id to locate
|
|
41
54
|
* @returns {CachedParameterSecret}
|
|
42
55
|
*/
|
|
43
56
|
static get (name) {
|
|
@@ -58,6 +71,9 @@ class CachedParameterSecrets {
|
|
|
58
71
|
return objects;
|
|
59
72
|
};
|
|
60
73
|
|
|
74
|
+
/**
|
|
75
|
+
* @returns {object: Array<object>} An object containing an array of CachedParameterSecret.toObject()
|
|
76
|
+
*/
|
|
61
77
|
static toObject() {
|
|
62
78
|
// return an object of cachedParameterSecret.toObject()
|
|
63
79
|
return {objects: CachedParameterSecrets.toArray()};
|
|
@@ -124,7 +140,7 @@ class CachedParameterSecrets {
|
|
|
124
140
|
}
|
|
125
141
|
|
|
126
142
|
/**
|
|
127
|
-
*
|
|
143
|
+
* @class CachedParameterSecret - Base class for CachedSSMParameter and CachedSecret
|
|
128
144
|
* Accesses data through Systems Manager Parameter Store and Secrets Manager Lambda Extension
|
|
129
145
|
* Since the Lambda Extension runs a localhost via http, it handles it's own http request. Also,
|
|
130
146
|
* since the lambda extension needs time to boot during a cold start, it is not available during
|
|
@@ -355,6 +371,7 @@ class CachedParameterSecret {
|
|
|
355
371
|
* The value comes back decrypted.
|
|
356
372
|
* It will return the current, cached copy which may have expired.
|
|
357
373
|
* @returns {string} The value of the Secret or Parameter
|
|
374
|
+
* @throws {Error} If the secret is null or .get(), .getValue(), or .refresh() has not been called first
|
|
358
375
|
*/
|
|
359
376
|
sync_getValue() {
|
|
360
377
|
if (this.isValid()) {
|
|
@@ -455,7 +472,40 @@ class CachedParameterSecret {
|
|
|
455
472
|
|
|
456
473
|
}
|
|
457
474
|
|
|
475
|
+
/**
|
|
476
|
+
* CachedSSMParameter extends CachedParameterSecret and is used to retrieve parameters from AWS Systems Manager Parameter Store
|
|
477
|
+
* @extends CachedParameterSecret
|
|
478
|
+
* @example
|
|
479
|
+
* // Create a cached SSM parameter
|
|
480
|
+
* const dbPassword = new CachedSSMParameter('/myapp/db/password');
|
|
481
|
+
*
|
|
482
|
+
* // Get the parameter value (async)
|
|
483
|
+
* const password = await dbPassword.getValue();
|
|
484
|
+
* console.log(password); // 'my-secret-password'
|
|
485
|
+
*
|
|
486
|
+
* @example
|
|
487
|
+
* // Use with synchronous functions after priming
|
|
488
|
+
* const apiKey = new CachedSSMParameter('/myapp/api/key');
|
|
489
|
+
*
|
|
490
|
+
* async function init() {
|
|
491
|
+
* // Prime the parameter in the background
|
|
492
|
+
* apiKey.prime();
|
|
493
|
+
*
|
|
494
|
+
* // Do other initialization work...
|
|
495
|
+
*
|
|
496
|
+
* // Ensure parameter is loaded before sync usage
|
|
497
|
+
* await apiKey.prime();
|
|
498
|
+
*
|
|
499
|
+
* // Now safe to use in sync functions
|
|
500
|
+
* const key = apiKey.sync_getValue();
|
|
501
|
+
* return key;
|
|
502
|
+
* }
|
|
503
|
+
*/
|
|
458
504
|
class CachedSSMParameter extends CachedParameterSecret {
|
|
505
|
+
/**
|
|
506
|
+
* Returns the URL path for the AWS Parameters and Secrets Lambda Extension to retrieve this SSM parameter
|
|
507
|
+
* @returns {string} The URL path with encoded parameter name
|
|
508
|
+
*/
|
|
459
509
|
getPath() {
|
|
460
510
|
const uriEncodedSecret = encodeURIComponent(this.name);
|
|
461
511
|
return `/systemsmanager/parameters/get/?name=${uriEncodedSecret}&withDecryption=true`;
|
|
@@ -469,8 +519,49 @@ class CachedSSMParameter extends CachedParameterSecret {
|
|
|
469
519
|
}
|
|
470
520
|
}
|
|
471
521
|
|
|
522
|
+
/**
|
|
523
|
+
* CachedSecret extends CachedParameterSecret and is used to retrieve secrets from AWS Secrets Manager
|
|
524
|
+
* @extends CachedParameterSecret
|
|
525
|
+
* @example
|
|
526
|
+
* // Create a cached secret
|
|
527
|
+
* const dbCredentials = new CachedSecret('myapp-database-credentials');
|
|
528
|
+
*
|
|
529
|
+
* // Get the secret value (async)
|
|
530
|
+
* const credentials = await dbCredentials.getValue();
|
|
531
|
+
* console.log(credentials); // '{"username":"admin","password":"secret"}'
|
|
532
|
+
*
|
|
533
|
+
* @example
|
|
534
|
+
* // Use with JSON secrets
|
|
535
|
+
* const apiSecret = new CachedSecret('myapp-api-secret');
|
|
536
|
+
*
|
|
537
|
+
* async function connectToAPI() {
|
|
538
|
+
* const secretString = await apiSecret.getValue();
|
|
539
|
+
* const secretData = JSON.parse(secretString);
|
|
540
|
+
*
|
|
541
|
+
* return {
|
|
542
|
+
* apiKey: secretData.apiKey,
|
|
543
|
+
* apiSecret: secretData.apiSecret
|
|
544
|
+
* };
|
|
545
|
+
* }
|
|
546
|
+
*
|
|
547
|
+
* @example
|
|
548
|
+
* // Prime multiple secrets at once
|
|
549
|
+
* const secret1 = new CachedSecret('secret-1');
|
|
550
|
+
* const secret2 = new CachedSecret('secret-2');
|
|
551
|
+
*
|
|
552
|
+
* // Prime all secrets in parallel
|
|
553
|
+
* await CachedParameterSecrets.prime();
|
|
554
|
+
*
|
|
555
|
+
* // Now all secrets are loaded and cached
|
|
556
|
+
* const value1 = secret1.sync_getValue();
|
|
557
|
+
* const value2 = secret2.sync_getValue();
|
|
558
|
+
*/
|
|
472
559
|
class CachedSecret extends CachedParameterSecret {
|
|
473
560
|
|
|
561
|
+
/**
|
|
562
|
+
* Returns the URL path for the AWS Parameters and Secrets Lambda Extension to retrieve this secret
|
|
563
|
+
* @returns {string} The URL path with encoded secret ID
|
|
564
|
+
*/
|
|
474
565
|
getPath() {
|
|
475
566
|
const uriEncodedSecret = encodeURIComponent(this.name);
|
|
476
567
|
return `/secretsmanager/get?secretId=${uriEncodedSecret}&withDecryption=true`;
|
|
@@ -1,11 +1,48 @@
|
|
|
1
1
|
const RequestInfo = require('./RequestInfo.class');
|
|
2
2
|
const Timer = require('./Timer.class');
|
|
3
3
|
const DebugAndLog = require('./DebugAndLog.class');
|
|
4
|
+
const { safeClone } = require('./utils');
|
|
4
5
|
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Extends RequestInfo
|
|
8
9
|
* Can be used to create a custom ClientRequest object
|
|
10
|
+
* @example
|
|
11
|
+
* // Initialize ClientRequest with validations
|
|
12
|
+
* ClientRequest.init({
|
|
13
|
+
* validations: {
|
|
14
|
+
* referrers: ['example.com', 'myapp.com'],
|
|
15
|
+
* parameters: {
|
|
16
|
+
* queryStringParameters: {
|
|
17
|
+
* limit: (value) => !isNaN(value) && value > 0 && value <= 100,
|
|
18
|
+
* page: (value) => !isNaN(value) && value > 0
|
|
19
|
+
* },
|
|
20
|
+
* pathParameters: {
|
|
21
|
+
* id: (value) => /^[a-zA-Z0-9-]+$/.test(value)
|
|
22
|
+
* }
|
|
23
|
+
* }
|
|
24
|
+
* }
|
|
25
|
+
* });
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* // Use in Lambda handler
|
|
29
|
+
* exports.handler = async (event, context) => {
|
|
30
|
+
* const clientRequest = new ClientRequest(event, context);
|
|
31
|
+
*
|
|
32
|
+
* if (!clientRequest.isValid()) {
|
|
33
|
+
* return { statusCode: 400, body: 'Invalid request' };
|
|
34
|
+
* }
|
|
35
|
+
*
|
|
36
|
+
* const userId = clientRequest.getPathAt(1);
|
|
37
|
+
* const queryParams = clientRequest.getQueryStringParameters();
|
|
38
|
+
*
|
|
39
|
+
* // Add logging for monitoring
|
|
40
|
+
* clientRequest.addPathLog(`users/${userId}`);
|
|
41
|
+
* clientRequest.addQueryLog(`limit=${queryParams.limit}`);
|
|
42
|
+
*
|
|
43
|
+
* // Process request...
|
|
44
|
+
* return { statusCode: 200, body: 'Success' };
|
|
45
|
+
* };
|
|
9
46
|
*/
|
|
10
47
|
class ClientRequest extends RequestInfo {
|
|
11
48
|
|
|
@@ -22,7 +59,7 @@ class ClientRequest extends RequestInfo {
|
|
|
22
59
|
/* What and who of the request */
|
|
23
60
|
#event = null;
|
|
24
61
|
#context = null;
|
|
25
|
-
#authorizations =
|
|
62
|
+
#authorizations = safeClone(ClientRequest.#unauthenticatedAuthorizations);
|
|
26
63
|
#roles = [];
|
|
27
64
|
|
|
28
65
|
/* The request data */
|
|
@@ -83,8 +120,8 @@ class ClientRequest extends RequestInfo {
|
|
|
83
120
|
* This is used to initialize the ClientRequest class for all requests.
|
|
84
121
|
* Add ClientRequest.init(options) to the Config.init process or at the
|
|
85
122
|
* top of the main index.js file outside of the handler.
|
|
86
|
-
* @param {
|
|
87
|
-
* @
|
|
123
|
+
* @param {object} options - Configuration options with validations property containing referrers and parameters
|
|
124
|
+
* @throws {Error} If options is not an object
|
|
88
125
|
*/
|
|
89
126
|
static init(options) {
|
|
90
127
|
if (typeof options === 'object') {
|
|
@@ -193,12 +230,8 @@ class ClientRequest extends RequestInfo {
|
|
|
193
230
|
}
|
|
194
231
|
|
|
195
232
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
* @param {array<string>} arr array to slice
|
|
199
|
-
* @param {number} n number of elements to return
|
|
200
|
-
* @returns {array<string>} array of elements
|
|
201
|
-
*/
|
|
233
|
+
// Utility function for getPathArray and getResourceArray
|
|
234
|
+
// Returns array slice based on n parameter
|
|
202
235
|
#getArray(arr, n = 0) {
|
|
203
236
|
if (n === 0 || arr.length <= n || (n < 0 && arr.length <= (n*-1))) {
|
|
204
237
|
return arr;
|
|
@@ -352,7 +385,7 @@ class ClientRequest extends RequestInfo {
|
|
|
352
385
|
if (this.isAuthenticated()) {
|
|
353
386
|
return this.#authorizations;
|
|
354
387
|
} else {
|
|
355
|
-
return
|
|
388
|
+
return safeClone(ClientRequest.#unauthenticatedAuthorizations);
|
|
356
389
|
}
|
|
357
390
|
};
|
|
358
391
|
|