@63klabs/cache-data 1.2.2

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.
@@ -0,0 +1,466 @@
1
+ const DebugAndLog = require('./DebugAndLog.class');
2
+
3
+
4
+ /* ****************************************************************************
5
+ * Connection classes
6
+ * ----------------------------------------------------------------------------
7
+ *
8
+ * Classes that store connection and cache information for api requests.
9
+ *
10
+ *************************************************************************** */
11
+
12
+ class Connections {
13
+
14
+ constructor( obj = null ) {
15
+
16
+ };
17
+
18
+ _connections = {};
19
+
20
+ /**
21
+ * Given an object (associative array) create a Connection object to add to
22
+ * our collection of Connections.
23
+ *
24
+ * @param {object} obj
25
+ */
26
+ add( obj ) {
27
+ if ( "name" in obj && obj.name !== null && !(obj.name in this._connections) ) {
28
+ let connection = new Connection(obj);
29
+
30
+ this._connections[obj.name] = connection;
31
+ };
32
+ };
33
+
34
+ /**
35
+ *
36
+ * @param {object} connectionName
37
+ * @returns An object from the named Connection
38
+ */
39
+ get(connectionName) {
40
+ DebugAndLog.debug("Getting connection: "+connectionName,this._connections[connectionName]);
41
+ return ( ( connectionName in this._connections ) ? this._connections[connectionName] : null );
42
+ };
43
+
44
+ toObject() {
45
+ return this._connections;
46
+ };
47
+
48
+ toJSON() {
49
+ // loop through the Connection objects in Connections by key and use each of their .toInfoObject() methods to generate an object
50
+ var obj = {};
51
+ for (var key in this._connections) {
52
+ obj[key] = this._connections[key].toInfoObject();
53
+ }
54
+ return JSON.stringify(obj);
55
+ }
56
+
57
+ };
58
+
59
+ /**
60
+ * The Connection object provides the base for requests but does not carry
61
+ * the request. myConnection.get() will return an object (associative array)
62
+ * that can then be used to generate and submit a request to a DAO class or
63
+ * APIRequest object.
64
+ * You can store and manage multiple connections using the Connections object.
65
+ */
66
+ class Connection {
67
+
68
+
69
+ /**
70
+ * Given an object (associative array) create a Connection
71
+ *
72
+ * @param {{name: string, method: string, uri: string, protocol: string, host: string, path: string, parameters: Array, headers: Array, options: object, note: string, authentication: object|ConnectionAuthentication, cache: Array}} obj
73
+ */
74
+ constructor(obj = null) {
75
+ this._init(obj);
76
+ };
77
+
78
+ _name = null;
79
+ _method = null;
80
+ _uri = null;
81
+ _protocol = null;
82
+ _host = null;
83
+ _path = null;
84
+ _body = null;
85
+ _parameters = null; // {}
86
+ _headers = null; // {}
87
+ _options = null;
88
+ _note = null;
89
+ _authentication = null; // new ConnectionAuthentication();
90
+ _cacheProfiles = null; // {}
91
+
92
+ _cachedAuthObject = null;
93
+
94
+ /**
95
+ * Given an object (associative array with key/value pairs) update the
96
+ * connection information. Note that for parameters and headers this
97
+ * is a destructive operation if new values are passed in. Any keys
98
+ * not passed in will be wiped. It is better to use the .addParameters()
99
+ * and .addHeaders() function first, then pass in an object that does
100
+ * not have the headers and parameters key.
101
+ * @param {*} obj
102
+ */
103
+ _init(obj = null) {
104
+ if ( obj !== null && typeof obj === 'object' ) {
105
+
106
+ // if, and only if the object is not currently named, will we accept a name
107
+ if ( "name" in obj && obj.name !== null && this._name === null ) { this._name = obj.name; }
108
+
109
+ if ( "method" in obj && obj.method !== null ) { this._method = obj.method.toUpperCase(); }
110
+ if ( "uri" in obj && obj.uri !== null ) { this._uri = obj.uri; }
111
+ if ( "protocol" in obj && obj.protocol !== null ) { this._protocol = obj.protocol.replace(/\:$/, '').toLowerCase(); } // The URL().protocol keeps the : we don't need it if present
112
+ if ( "host" in obj && obj.host !== null ) { this._host = obj.host.toLowerCase(); }
113
+ if ( "path" in obj && obj.path !== null ) { this._path = obj.path; }
114
+ if ( "body" in obj && obj.body !== null ) { this._body = obj.body; }
115
+ if ( "parameters" in obj && obj.parameters !== null ) { this._parameters = obj.parameters; }
116
+ if ( "headers" in obj && obj.headers !== null ) { this._headers = obj.headers; }
117
+ if ( "options" in obj && obj.options !== null ) { this._options = obj.options; }
118
+ if ( "note" in obj && obj.note !== null ) { this._note = obj.note; }
119
+ if ( "authentication" in obj && obj.authentication !== null ) { this._setAuthentication(obj.authentication); }
120
+ if ( "cache" in obj && obj.cache !== null) { this._setCacheProfiles(obj.cache); }
121
+
122
+ }
123
+
124
+ };
125
+
126
+ /**
127
+ *
128
+ * @returns {Object}
129
+ */
130
+ getParameters() {
131
+ let params = (this._parameters !== null) ? JSON.parse(JSON.stringify(this._parameters)) : null;
132
+
133
+ if ("parameters" in this._getAuthenticationObject()) {
134
+ if (params === null) { params = {}; }
135
+ // combine auth keys and values with parm keys and values
136
+ params = Object.assign({}, params, this._getAuthenticationObject().parameters);
137
+ }
138
+
139
+ return params;
140
+ };
141
+
142
+ /**
143
+ *
144
+ * @returns {Object}
145
+ */
146
+ getHeaders() {
147
+ let headers = (this._headers !== null) ? JSON.parse(JSON.stringify(this._headers)) : null;
148
+
149
+ if ("headers" in this._getAuthenticationObject()) {
150
+ if (headers === null) { headers = {}; }
151
+ // combine auth keys and values with parm keys and values
152
+ headers = Object.assign({}, headers, this._getAuthenticationObject().headers);
153
+ }
154
+
155
+ return headers;
156
+ };
157
+
158
+ /**
159
+ * @returns {string}
160
+ */
161
+ getBody() {
162
+ let body = ("body" in this._getAuthenticationObject()) ? this._getAuthenticationObject().body : this._body;
163
+
164
+ if (body !== null && typeof body !== 'string') {
165
+ body = JSON.stringify(body);
166
+ }
167
+
168
+ return body;
169
+ };
170
+
171
+ _getAuthenticationObject() {
172
+ if (this._cachedAuthObject === null) {
173
+ this._cachedAuthObject = (this._authentication === null) ? {} : this._authentication.toObject();
174
+ }
175
+ return this._cachedAuthObject;
176
+ }
177
+
178
+ /**
179
+ *
180
+ * @param {string} profileName
181
+ * @returns {Object}
182
+ */
183
+ getCacheProfile( profileName ) {
184
+
185
+ function isProfile(item) {
186
+ return item.profile === profileName;
187
+ };
188
+
189
+ return JSON.parse(JSON.stringify(this._cacheProfiles.find(isProfile)));
190
+ };
191
+
192
+ _setAuthentication(authentication) {
193
+ if (authentication instanceof ConnectionAuthentication ) { this._authentication = authentication }
194
+ else if ( authentication !== null && typeof authentication === 'object' ) { this._authentication = new ConnectionAuthentication(authentication); }
195
+ };
196
+
197
+ _setCacheProfiles(cacheProfiles) {
198
+ this._cacheProfiles = cacheProfiles;
199
+ };
200
+
201
+ _toObject() {
202
+ const obj = {};
203
+ if ( this._method !== null ) { obj.method = this._method; }
204
+ if ( this._uri !== null ) { obj.uri = this._uri; }
205
+ if ( this._protocol !== null ) { obj.protocol = this._protocol; }
206
+ if ( this._host !== null ) { obj.host = this._host; }
207
+ if ( this._path !== null ) { obj.path = this._path; }
208
+
209
+ if ( this._options !== null ) { obj.options = this._options; }
210
+ if ( this._note !== null ) { obj.note = this._note; }
211
+ return obj;
212
+ }
213
+
214
+ /**
215
+ *
216
+ * @returns object (associative array) with connection details in key/pairs
217
+ */
218
+ toObject() {
219
+ const obj = this._toObject();
220
+
221
+ const headers = this.getHeaders();
222
+ const parameters = this.getParameters();
223
+ const body = this.getBody();
224
+
225
+ if ( headers !== null ) { obj.headers = headers; }
226
+ if ( parameters !== null ) { obj.parameters = parameters; }
227
+ if ( body !== null ) { obj.body = body; }
228
+
229
+ if ( this._authentication !== null ) { obj.authentication = this._authentication.toObject(); }
230
+
231
+ return obj;
232
+ };
233
+
234
+ toInfoObject() {
235
+ const obj = this._toObject();
236
+
237
+ if ( this._headers !== null ) { obj.headers = this._headers; }
238
+ if ( this._parameters !== null ) { obj.parameters = this._parameters; }
239
+ if ( this._body !== null ) { obj.body = this._body; }
240
+
241
+ if ( this._authentication !== null && typeof this._authentication === "object" && this._authentication instanceof ConnectionAuthentication) {
242
+ obj.auth = {};
243
+ obj.auth.headers = this._authentication.hasHeader();
244
+ obj.auth.parameters = this._authentication.hasParameter();
245
+ obj.auth.body = this._authentication.hasBody();
246
+ obj.auth.basic = this._authentication.hasBasic();
247
+ }
248
+
249
+ return obj;
250
+ }
251
+
252
+ toString() {
253
+ return `${this._name} ${this._method} ${(this._uri) ? this._uri : this._protocol+"://"+this._host+this._path}${(this._note) ? " "+this._note : ""}`;
254
+ };
255
+
256
+ };
257
+
258
+ /**
259
+ * ConnectionAuthentication allows auth tokens, parameters, etc to be stored
260
+ * separately from regular connection request parameters so that they can be
261
+ * protected from modification or accidental access.
262
+ * Header, parameter, and/or Basic auth key/value pairs can be passed in.
263
+ * Additional methods could be added in the future.
264
+ *
265
+ * new ConnectionAuthentication(
266
+ * {
267
+ * headers: { x-key: "bsomeKeyForAHeaderField", x-user: "myUserID" },
268
+ * parameters: { apikey: "myExampleApiKeyForResource1234" },
269
+ * body: { key: "myExampleApiKeyForResource1283" },
270
+ * basic: { username: "myUsername", password: "myPassword" }
271
+ * }
272
+ * );
273
+ */
274
+ class ConnectionAuthentication {
275
+
276
+ // request parts that could contain auth
277
+ #headers = null;
278
+ #parameters = null;
279
+ #body = null;
280
+
281
+ // auth methods - we merge in later, keep separate for now
282
+ #basic = null;
283
+
284
+ constructor(obj = null) {
285
+
286
+ if ( obj !== null && typeof obj === 'object' ) {
287
+
288
+ // places auth could be placed
289
+ if ( "parameters" in obj && obj.parameters !== null ) { this.#parameters = obj.parameters; }
290
+ if ( "headers" in obj && obj.headers !== null ) { this.#headers = obj.headers; }
291
+ if ( "body" in obj && obj.body !== null ) { this.#body = obj.body; }
292
+
293
+ // auth methods
294
+ if ( "basic" in obj && obj.basic !== null ) { this.#basic = obj.basic; }
295
+
296
+ }
297
+
298
+ };
299
+
300
+ hasHeader() {
301
+ return (this.#headers !== null);
302
+ };
303
+
304
+ hasParameter() {
305
+ return (this.#parameters !== null);
306
+ };
307
+
308
+ hasBody() {
309
+ return (this.#body !== null);
310
+ };
311
+
312
+ hasBasic() {
313
+ return (this.#basic !== null);
314
+ };
315
+
316
+ /**
317
+ * Generates the basic auth Authorization header.
318
+ * Takes the basic object containing a username and password during object
319
+ * construction and concatenates them together in a base64 encoded string
320
+ * and places it in an Authorization header.
321
+ * @returns {object} Object containing the key/value pair of a Authorization header for basic auth
322
+ */
323
+ _getBasicAuthHeader() {
324
+ let obj = {};
325
+ if ( this.hasBasic() ) {
326
+ obj = { Authorization: "" };
327
+ obj.Authorization = "Basic " + Buffer.from(this.#basic.username + ":" + this.#basic.password).toString("base64");
328
+ }
329
+ return obj;
330
+ };
331
+
332
+ /**
333
+ * Combines any auth parameters sent via a query string
334
+ * @returns {object} Object containing key/value pairs for parameters
335
+ */
336
+ _getParameters() {
337
+ let obj = {};
338
+ if ( this.hasParameter() ) { obj = this.#parameters; }
339
+ return obj;
340
+ };
341
+
342
+ /**
343
+ * Combines any auth header fields. If authorizations such as Basic is used the
344
+ * appropriate header field is generated.
345
+ * @returns {object} Object containing key/value pairs for headers
346
+ */
347
+ _getHeaders() {
348
+ let obj = {};
349
+ if ( this.hasHeader() ) { obj = this.#headers; };
350
+
351
+ // auth methods here
352
+ if ( this.hasBasic() ) { obj = Object.assign({}, obj.headers, this._getBasicAuthHeader()); } // merge basic auth into headers
353
+
354
+ return obj;
355
+ };
356
+
357
+ /**
358
+ * Combines any auth fields sent via a body
359
+ * @returns {object} Object containing key/value pairs for body
360
+ */
361
+ _getBody() {
362
+ let obj = {};
363
+ if ( this.hasBody() ) { obj = this.#body; };
364
+ return obj;
365
+ };
366
+
367
+
368
+ /**
369
+ *
370
+ * @returns {object} An object with any header, parameter, or body fields necessary
371
+ * to complete authentication
372
+ */
373
+ toObject() {
374
+ let obj = {};
375
+
376
+ /*
377
+ Object.keys(myObj).length
378
+ Object.keys(myObj) will return the keys of the object (associative array) as an indexed array of keys.
379
+ .length will then return the number of keys in the indexed array. If there are more than 0 then there is content.
380
+ */
381
+
382
+ let headers = this._getHeaders();
383
+ if ( Object.keys(headers).length ) { obj.headers = headers; }
384
+
385
+ let parameters = this._getParameters();
386
+ if ( Object.keys(parameters).length ) { obj.parameters = parameters; }
387
+
388
+ let body = this._getBody();
389
+ if ( Object.keys(body).length ) { obj.body = body; }
390
+
391
+ return JSON.parse(JSON.stringify(obj));
392
+ };
393
+
394
+ // Static methods that could perform authentication could go here
395
+ // such as OAuth 2.0 or Bearer tokens.
396
+ // that way an object containing the right info could be passed in and executed
397
+
398
+ };
399
+
400
+ /**
401
+ * A Connection provides the base for a ClientRequest. A ClientRequest extends the
402
+ * Connection by adding request specific parameters. While a Connection
403
+ * cannot be modified after creation (it is a config), a ClientRequest can be
404
+ * modified as the application or DAO assembles the request.
405
+ */
406
+ class ConnectionRequest extends Connection {
407
+
408
+ constructor(obj) {
409
+ super(obj);
410
+ };
411
+
412
+ /**
413
+ * Add key/value pairs to headers. If the header key already exists,
414
+ * it will be updated with the new one.
415
+ * @param {*} headers
416
+ */
417
+ addHeaders(headers) {
418
+ if ( headers !== null && typeof headers === 'object' ) {
419
+ // if we have headers (not null) then merge new headers in, otherwise just set to passed headers
420
+ this._headers = ( this._headers !== null ) ? Object.assign({}, this._headers, headers) : headers;
421
+ }
422
+ };
423
+
424
+ /**
425
+ * Add a single header key/value pair to headers. If the header key
426
+ * already exists it will be updated with the new one.
427
+ * @param {*} key
428
+ * @param {*} value
429
+ */
430
+ addHeader(key, value) {
431
+ let obj = {};
432
+ obj[key] = value;
433
+ this.addHeaders( obj );
434
+ };
435
+
436
+ /**
437
+ * Add key/value pairs to parameters. If the parameter already exists,
438
+ * it will be updated with the new one.
439
+ * @param {*} parameters
440
+ */
441
+ addParameters(parameters) {
442
+ if ( parameters !== null && typeof parameters === 'object' ) {
443
+ // if we have parameters (not null) then merge new parameters in, otherwise just set to passed parameters
444
+ this._parameters = ( this._parameters !== null ) ? Object.assign({}, this._parameters, parameters) : parameters;
445
+ }
446
+ };
447
+
448
+ /**
449
+ * Add a single parameter key/value pair to parameters. If the parameter key
450
+ * already exists it will be updated with the new one.
451
+ * @param {*} key
452
+ * @param {*} value
453
+ */
454
+ addParameter(key, value) {
455
+ let obj = {};
456
+ obj[key] = value;
457
+ this.addParameter( obj );
458
+ };
459
+ };
460
+
461
+ module.exports = {
462
+ Connections,
463
+ Connection,
464
+ ConnectionRequest,
465
+ ConnectionAuthentication
466
+ };