jaypie 0.1.9 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +669 -3
- package/package.json +5 -5
- package/src/dynamicExport.function.js +0 -2
- package/.eslintrc.cjs +0 -40
- package/.github/workflows/npm-deploy.yml +0 -55
- package/.vscode/settings.json +0 -24
- package/_templates/jaypie/hygen/jest.command.ejs.t +0 -14
- package/_templates/jaypie/hygen/new.ejs.t +0 -31
- package/_templates/jaypie/hygen/prompt.cjs +0 -37
- package/_templates/jaypie/hygen/prompt.ejs.t +0 -44
- package/_templates/jaypie/hygen/test.ejs.t +0 -52
- package/_templates/jaypie/vite/new.ejs.t +0 -28
- package/_templates/jaypie/vite/prompt.cjs +0 -35
- package/_templates/jaypie/vite/test.ejs.t +0 -49
- package/_templates/jaypie/vite/vitest.command.ejs.t +0 -11
- package/_templates/jaypie/vitest/jest.command.ejs.t +0 -11
- package/_templates/jaypie/vitest/prompt.cjs +0 -41
- package/_templates/jaypie/vitest/test.ejs.t +0 -49
- package/_templates/jaypie/workflow-npm/npm-deploy.ejs.t +0 -58
- package/_templates/jaypie/workflow-npm/prompt.cjs +0 -32
- package/src/__tests__/dynamicExport.function.spec.js +0 -162
- package/src/__tests__/index.spec.js +0 -41
- package/src/__tests__/mongoose.package.spec.js +0 -101
- package/testSetup.js +0 -6
- package/vite.config.js +0 -10
package/README.md
CHANGED
|
@@ -44,24 +44,690 @@ Jaypie strives to be "mockable-first" meaning all components should be easily te
|
|
|
44
44
|
|
|
45
45
|
### Installation
|
|
46
46
|
|
|
47
|
+
#### Base Package
|
|
48
|
+
|
|
47
49
|
```bash
|
|
48
50
|
npm install jaypie
|
|
49
51
|
```
|
|
50
52
|
|
|
51
|
-
|
|
53
|
+
`@jaypie/core` is included in `jaypie`. Almost every Jaypie package requires core.
|
|
54
|
+
|
|
55
|
+
#### Peer Dependencies
|
|
56
|
+
|
|
57
|
+
You must install peer dependencies for your project.
|
|
58
|
+
|
|
59
|
+
| Package | Exports | Description |
|
|
60
|
+
| ------- | ------- | ----------- |
|
|
61
|
+
| `@jaypie/aws` | `getSecret` | AWS helpers |
|
|
62
|
+
| `@jaypie/lambda` | `lambdaHandler` | Lambda entry point |
|
|
63
|
+
| `@jaypie/mongoose` | `connectFromSecretEnv`, `disconnect`, `mongoose` | MongoDB management |
|
|
64
|
+
|
|
65
|
+
#### TestKit
|
|
66
|
+
|
|
67
|
+
Matchers, mocks, and utilities to test Jaypie projects.
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
npm install --save-dev @jaypie/testkit
|
|
71
|
+
```
|
|
52
72
|
|
|
53
73
|
### Example
|
|
54
74
|
|
|
55
|
-
|
|
75
|
+
```bash
|
|
76
|
+
npm install jaypie @jaypie/lambda
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
```javascript
|
|
80
|
+
const { InternalError, lambdaHandler, log } = require("jaypie");
|
|
81
|
+
|
|
82
|
+
export const handler = lambdaHandler(async({event}) => {
|
|
83
|
+
// await new Promise(r => setTimeout(r, 2000));
|
|
84
|
+
if (event.something === "problem") {
|
|
85
|
+
throw new InternalError();
|
|
86
|
+
}
|
|
87
|
+
// log.debug("Hello World");
|
|
88
|
+
return "Hello World";
|
|
89
|
+
}, { name: "example"});
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
This example would then be deployed to AWS via CDK or similar orchestration.
|
|
93
|
+
|
|
94
|
+
_A `@jaypie/cdk` package is intended_
|
|
56
95
|
|
|
57
96
|
## 📖 Reference
|
|
58
97
|
|
|
59
|
-
|
|
98
|
+
### AWS
|
|
99
|
+
|
|
100
|
+
```javascript
|
|
101
|
+
import { getSecret } from '@jaypie/aws';
|
|
102
|
+
|
|
103
|
+
const secret = await getSecret("MongoConnectionStringN0NC3-nSg1bR1sh");
|
|
104
|
+
// secret = "mongodb+srv://username:password@env-project.n0nc3.mongodb.net/app?retryWrites=true&w=majority";
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Constants
|
|
108
|
+
|
|
109
|
+
```javascript
|
|
110
|
+
import {
|
|
111
|
+
CDK,
|
|
112
|
+
ERROR,
|
|
113
|
+
HTTP,
|
|
114
|
+
LOG,
|
|
115
|
+
VALIDATE,
|
|
116
|
+
} from "jaypie";
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
#### `CDK`
|
|
120
|
+
|
|
121
|
+
* `CDK.ACCOUNT`
|
|
122
|
+
* `CDK.ENV`
|
|
123
|
+
* `CDK.ROLE`
|
|
124
|
+
* `CDK.SERVICE`
|
|
125
|
+
* `CDK.TAG`
|
|
126
|
+
|
|
127
|
+
See [constants.js in @jaypie/core](https://github.com/finlaysonstudio/jaypie-core/blob/main/src/core/constants.js).
|
|
128
|
+
|
|
129
|
+
#### `ERROR`
|
|
130
|
+
|
|
131
|
+
Default messages and titles for Jaypie errors.
|
|
132
|
+
|
|
133
|
+
* `ERROR.MESSAGE`
|
|
134
|
+
* `ERROR.TITLE`
|
|
135
|
+
|
|
136
|
+
See `HTTP` for status codes.
|
|
137
|
+
|
|
138
|
+
#### `HTTP`
|
|
139
|
+
|
|
140
|
+
* `HTTP.ALLOW.ANY`
|
|
141
|
+
* `HTTP.CODE`: `OK`, `CREATED`, ...
|
|
142
|
+
* `HTTP.CONTENT.ANY`
|
|
143
|
+
* `HTTP.CONTENT.HTML`
|
|
144
|
+
* `HTTP.CONTENT.JSON`
|
|
145
|
+
* `HTTP.CONTENT.TEXT`
|
|
146
|
+
* `HTTP.HEADER`: ...
|
|
147
|
+
* `HTTP.METHOD`: `GET`, `POST`, ...
|
|
148
|
+
|
|
149
|
+
#### `LOG`
|
|
150
|
+
|
|
151
|
+
* `LOG.FORMAT`
|
|
152
|
+
* `LOG.LEVEL`
|
|
153
|
+
|
|
154
|
+
#### `VALIDATE`
|
|
155
|
+
|
|
156
|
+
* `VALIDATE.ANY` - Default
|
|
157
|
+
* `VALIDATE.ARRAY`
|
|
158
|
+
* `VALIDATE.CLASS`
|
|
159
|
+
* `VALIDATE.FUNCTION`
|
|
160
|
+
* `VALIDATE.NUMBER`
|
|
161
|
+
* `VALIDATE.NULL`
|
|
162
|
+
* `VALIDATE.OBJECT`
|
|
163
|
+
* `VALIDATE.STRING`
|
|
164
|
+
* `VALIDATE.UNDEFINED`
|
|
165
|
+
|
|
166
|
+
#### Internal Constants
|
|
167
|
+
|
|
168
|
+
* `JAYPIE` - for consistency across Jaypie
|
|
169
|
+
* `PROJECT` - for consistency across projects
|
|
170
|
+
|
|
171
|
+
### Errors
|
|
172
|
+
|
|
173
|
+
#### Throwing/Catching Errors
|
|
174
|
+
|
|
175
|
+
``` javascript
|
|
176
|
+
// See `Error Reference` for full list
|
|
177
|
+
const { InternalError } = require("@knowdev/errors");
|
|
178
|
+
|
|
179
|
+
try {
|
|
180
|
+
// Code happens...
|
|
181
|
+
throw InternalError("Oh, I am slain!");
|
|
182
|
+
} catch (error) {
|
|
183
|
+
// Is this from a jaypie project?
|
|
184
|
+
if(error.isProjectError) {
|
|
185
|
+
{
|
|
186
|
+
name, // ProjectError
|
|
187
|
+
title, // "Internal Server Error"
|
|
188
|
+
detail, // "Oh, I am slain"
|
|
189
|
+
status, // 500 (HTTP code)
|
|
190
|
+
} = error;
|
|
191
|
+
} else {
|
|
192
|
+
// Not from jaypie
|
|
193
|
+
throw error;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
#### Format Error
|
|
199
|
+
|
|
200
|
+
``` javascript
|
|
201
|
+
if(error.isProjectError) {
|
|
202
|
+
return error.json();
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
#### Multi-Error Usage
|
|
207
|
+
|
|
208
|
+
``` javascript
|
|
209
|
+
const errors = [];
|
|
210
|
+
errors.push(BadGatewayError());
|
|
211
|
+
errors.push(NotFoundError());
|
|
212
|
+
throw MultiError(errors);
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
#### Error Reference
|
|
216
|
+
|
|
217
|
+
| Error | Status | Notes |
|
|
218
|
+
| ----------------------- | ------ | ----- |
|
|
219
|
+
| `BadGatewayError` | 502 | Something I need gave me an error |
|
|
220
|
+
| `BadRequestError` | 400 | You did something wrong |
|
|
221
|
+
| `ConfigurationError` | 500 | "The developer" (probably you) or an associate did something wrong |
|
|
222
|
+
| `ForbiddenError` | 403 | You are not allowed |
|
|
223
|
+
| `GatewayTimeoutError` | 504 | Something I need is taking too long |
|
|
224
|
+
| `GoneError` | 410 | The thing you are looking for was here but is now gone forever |
|
|
225
|
+
| `IllogicalError` | 500 | Code is in a state that "should never happen" |
|
|
226
|
+
| `InternalError` | 500 | General "something went wrong" |
|
|
227
|
+
| `MethodNotAllowedError` | 405 | You tried a good path but the wrong method |
|
|
228
|
+
| `MultiError` | Varies | Takes an array of errors |
|
|
229
|
+
| `NotFoundError` | 404 | The thing you are looking for is not here and maybe never was |
|
|
230
|
+
| `NotImplementedError` | 400 | "The developer" (you again?) didn't finish this part yet - hopefully a temporary message |
|
|
231
|
+
| `RejectedError` | 403 | Request filtered prior to processing |
|
|
232
|
+
| `TeapotError` | 418 | RFC 2324 section-2.3.2 |
|
|
233
|
+
| `UnavailableError` | 503 | The thing you are looking for cannot come to the phone right now |
|
|
234
|
+
| `UnhandledError` | 500 | An error that should have been handled wasn't |
|
|
235
|
+
| `UnreachableCodeError` | 500 |
|
|
236
|
+
|
|
237
|
+
##### Special Errors
|
|
238
|
+
|
|
239
|
+
ALWAYS internal to the app, NEVER something the client did
|
|
240
|
+
|
|
241
|
+
* Configuration
|
|
242
|
+
* "The person writing the code did something wrong" like forgot to pass or passed bad arguments
|
|
243
|
+
* "The person who configured the application made a mistake" like set mutually exclusive settings to true
|
|
244
|
+
* Illogical
|
|
245
|
+
* A combination of truth conditions occurred that should not be able to occur at the same time
|
|
246
|
+
* Not Implemented
|
|
247
|
+
* A marker to come back and finish this, but allows stubbing out HTTP endpoints
|
|
248
|
+
* Unhandled
|
|
249
|
+
* Internal to Jaypie, should not be thrown directly
|
|
250
|
+
* Jaypie expects code in handlers to handler errors and re-throw a Jaypie error
|
|
251
|
+
* If an unexpected error escapes the handler, Jaypie returns this when it is caught
|
|
252
|
+
* Unreachable
|
|
253
|
+
* In theory the block is literally not reachable and we want to put something there to make sure it stays that way
|
|
254
|
+
|
|
255
|
+
### Functions
|
|
256
|
+
|
|
257
|
+
#### `cloneDeep`
|
|
258
|
+
|
|
259
|
+
`lodash.clonedeep` from [NPM](https://www.npmjs.com/package/lodash.clonedeep)
|
|
260
|
+
|
|
261
|
+
```javascript
|
|
262
|
+
import { cloneDeep } from "jaypie";
|
|
263
|
+
|
|
264
|
+
const original = { a: 1, b: { c: 2 }};
|
|
265
|
+
const clone = cloneDeep(original);
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
#### `envBoolean`
|
|
269
|
+
|
|
270
|
+
Look up a key in `process.env` and coerce it into a boolean.
|
|
271
|
+
Returns `true` for `true` (case-insensitive) and `1` for string, boolean, and numeric types.
|
|
272
|
+
Returns `false` for `false` (case-insensitive) and `0` for string, boolean, and numeric types.
|
|
273
|
+
Returns `undefined` otherwise.
|
|
274
|
+
|
|
275
|
+
``` javascript
|
|
276
|
+
const { envBoolean } = require("@knowdev/functions");
|
|
277
|
+
|
|
278
|
+
process.env.AWESOME = true;
|
|
279
|
+
|
|
280
|
+
if (envBoolean("AWESOME")) {
|
|
281
|
+
console.log("Awesome!");
|
|
282
|
+
}
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
##### `envBoolean`: `defaultValue`
|
|
286
|
+
|
|
287
|
+
``` javascript
|
|
288
|
+
const { envBoolean } = require("@knowdev/functions");
|
|
289
|
+
|
|
290
|
+
if (envBoolean("AWESOME", { defaultValue: true })) {
|
|
291
|
+
console.log("Awesome!");
|
|
292
|
+
}
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
#### `force`
|
|
296
|
+
|
|
297
|
+
Coerce a value into a type or throw an error.
|
|
298
|
+
|
|
299
|
+
```javascript
|
|
300
|
+
import { force } from "jaypie";
|
|
301
|
+
|
|
302
|
+
argument = force(thing, Array);
|
|
303
|
+
argument = force([thing], Array);
|
|
304
|
+
// argument = [thing]
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
Forcing arrays is the primary use case.
|
|
308
|
+
|
|
309
|
+
```javascript
|
|
310
|
+
// force supports Array, Object, and String
|
|
311
|
+
argument = force(argument, Array);
|
|
312
|
+
argument = force(argument, Object, "key");
|
|
313
|
+
argument = force(argument, String, "default");
|
|
314
|
+
|
|
315
|
+
// Convenience functions
|
|
316
|
+
argument = force.array(argument);
|
|
317
|
+
argument = force.object(argument, "key");
|
|
318
|
+
argument = force.string(argument);
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
#### `getHeaderFrom`
|
|
322
|
+
|
|
323
|
+
`getHeaderFrom(headerKey:string, searchObject:object)`
|
|
324
|
+
|
|
325
|
+
Case-insensitive search inside `searchObject` for `headerKey`. Also looks in `header` and `headers` child object of `searchObject`, if `headerKey` not found at top-level.
|
|
326
|
+
|
|
327
|
+
#### `placeholders`
|
|
328
|
+
|
|
329
|
+
Lightweight string interpolation
|
|
330
|
+
|
|
331
|
+
```javascript
|
|
332
|
+
import { placeholders } from "jaypie";
|
|
333
|
+
|
|
334
|
+
const string = placeholders("Hello, {name}!", { name: "World" });
|
|
335
|
+
// string = "Hello, World!"
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
The code for placeholders was written by Chris Ferdinandi and distributed under the MIT License in 2018-2019. Their web site is https://gomakethings.com
|
|
339
|
+
|
|
340
|
+
#### `validate`
|
|
341
|
+
|
|
342
|
+
```javascript
|
|
343
|
+
import { validate, VALIDATE } from "jaypie";
|
|
344
|
+
|
|
345
|
+
validate(argument, {
|
|
346
|
+
type: TYPE.ANY,
|
|
347
|
+
falsy: false, // When `true`, allows "falsy" values that match the type (e.g., `0`, `""`)
|
|
348
|
+
required: true, // When `false`, allows `undefined` as a valid value
|
|
349
|
+
throws: true // When `false`, returns `false` instead of throwing error
|
|
350
|
+
});
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
##### Validate Convenience Functions
|
|
354
|
+
|
|
355
|
+
``` javascript
|
|
356
|
+
import { validate } from "jaypie";
|
|
357
|
+
|
|
358
|
+
validate.array(argument);
|
|
359
|
+
validate.class(argument);
|
|
360
|
+
validate.function(argument);
|
|
361
|
+
validate.null(argument);
|
|
362
|
+
validate.number(argument);
|
|
363
|
+
validate.object(argument);
|
|
364
|
+
validate.string(argument);
|
|
365
|
+
validate.undefined(argument);
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
##### Intuitive Validate Types
|
|
369
|
+
|
|
370
|
+
_Does not include any, class, or undefined_
|
|
371
|
+
|
|
372
|
+
``` javascript
|
|
373
|
+
validate(argument, {
|
|
374
|
+
// One of:
|
|
375
|
+
type: Array,
|
|
376
|
+
type: Function,
|
|
377
|
+
type: Number,
|
|
378
|
+
type: null,
|
|
379
|
+
type: Object,
|
|
380
|
+
type: String,
|
|
381
|
+
})
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
### Jaypie Handler
|
|
385
|
+
|
|
386
|
+
The Jaypie handler can be used directly but is more likely to be wrapped in a more specific handler. The Jaypie handler will call lifecycle methods and provide logging. Unhandled errors will be thrown as `UnhandledError`.
|
|
387
|
+
|
|
388
|
+
```javascript
|
|
389
|
+
import { jaypieHandler } from "jaypie";
|
|
390
|
+
|
|
391
|
+
const handler = jaypieHandler(async(...args) => {
|
|
392
|
+
// await new Promise(r => setTimeout(r, 2000));
|
|
393
|
+
// log.var({ args });
|
|
394
|
+
return "Hello World";
|
|
395
|
+
}, { name: "jaypieReference"});
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
#### Jaypie Lifecycle Methods
|
|
399
|
+
|
|
400
|
+
Each function receives the same arguments as the handler.
|
|
401
|
+
|
|
402
|
+
##### `validate: [async Function]`
|
|
403
|
+
|
|
404
|
+
Returns `true` to validate the request. Throw an error or return `false` to reject the request.
|
|
405
|
+
|
|
406
|
+
##### `setup: [async Function]`
|
|
407
|
+
|
|
408
|
+
Called before the handler (e.g., connect to a database). Throw an error to halt execution.
|
|
409
|
+
|
|
410
|
+
#### `handler: async Function`
|
|
411
|
+
|
|
412
|
+
The main function to handle the request. Throw an error to halt execution.
|
|
413
|
+
|
|
414
|
+
##### `teardown: [async Function]`
|
|
415
|
+
|
|
416
|
+
Called after the handler (e.g., disconnect from a database). Runs even if setup or handler throws errors.
|
|
417
|
+
|
|
418
|
+
### Lambda Handler
|
|
419
|
+
|
|
420
|
+
The Lambda handler wraps the Jaypie handler that is specifically for AWS Lambda. It will call lifecycle methods and provide logging. Unhandled errors will be thrown as `UnhandledError`.
|
|
421
|
+
|
|
422
|
+
```javascript
|
|
423
|
+
const { lambdaHandler } = require("jaypie");
|
|
424
|
+
|
|
425
|
+
const handler = lambdaHandler(async({event}) => {
|
|
426
|
+
// await new Promise(r => setTimeout(r, 2000));
|
|
427
|
+
// log.debug("Hello World");
|
|
428
|
+
return "Hello World";
|
|
429
|
+
}, { name: "lambdaReference"});
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
### Logging
|
|
433
|
+
|
|
434
|
+
```javascript
|
|
435
|
+
import {
|
|
436
|
+
log,
|
|
437
|
+
LOG, // LOG.FORMAT, LOG.LEVEL
|
|
438
|
+
} from "jaypie";
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
#### log
|
|
442
|
+
|
|
443
|
+
```javascript
|
|
444
|
+
import { log } from "jaypie";
|
|
445
|
+
|
|
446
|
+
log.trace();
|
|
447
|
+
log.debug();
|
|
448
|
+
log.info();
|
|
449
|
+
log.warn();
|
|
450
|
+
log.error();
|
|
451
|
+
log.fatal();
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
##### log.lib({ lib: "myLib" })
|
|
455
|
+
|
|
456
|
+
Uses `silent` by default. if `process.env.MODULE_LOG_LEVEL` is `true`, follows `process.env.LOG_LEVEL`. If `process.env.MODULE_LOG_LEVEL` is also set, uses that log level.
|
|
457
|
+
|
|
458
|
+
```javascript
|
|
459
|
+
import { log } from "jaypie";
|
|
460
|
+
|
|
461
|
+
log.lib().trace();
|
|
462
|
+
log.lib({ lib: "myLib" }).trace();
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
##### log.var(key, value) or log.var({ key: value })
|
|
466
|
+
|
|
467
|
+
Log a key-value pair. In the `json` format, the key will be tagged as `var` and the value will be the value. Logging marker variables this way can be useful for debugging.
|
|
468
|
+
|
|
469
|
+
```javascript
|
|
470
|
+
import { log } from "jaypie";
|
|
471
|
+
|
|
472
|
+
log.var("message", "Hello, world");
|
|
473
|
+
log.var({ message: "Hello, world" });
|
|
474
|
+
|
|
475
|
+
const message = "Hello, world";
|
|
476
|
+
log.var({ message });
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
##### log.with() - clone
|
|
480
|
+
|
|
481
|
+
Create a new log object with additional tags
|
|
482
|
+
|
|
483
|
+
```javascript
|
|
484
|
+
import { log as defaultLogger } from "jaypie";
|
|
485
|
+
|
|
486
|
+
const log = defaultLogger.with({ customProperty: "customValue" });
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
### Mongoose
|
|
490
|
+
|
|
491
|
+
```javascript
|
|
492
|
+
import {
|
|
493
|
+
connectFromSecretEnv,
|
|
494
|
+
disconnect,
|
|
495
|
+
mongoose,
|
|
496
|
+
} from "jaypie";
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
#### `connectFromSecretEnv`
|
|
500
|
+
|
|
501
|
+
Jaypie lifecycle method to connect to MongoDB using `process.env.MONGO_CONNECTION_STRING`.
|
|
502
|
+
|
|
503
|
+
```javascript
|
|
504
|
+
import { connectFromSecretEnv, disconnect, lambdaHandler, mongoose } from "jaypie";
|
|
505
|
+
|
|
506
|
+
const handler = lambdaHandler(async({event}) => {
|
|
507
|
+
// mongoose is already connected
|
|
508
|
+
return "Hello World";
|
|
509
|
+
}, {
|
|
510
|
+
name: "lambdaReference"
|
|
511
|
+
setup: [connectFromSecretEnv],
|
|
512
|
+
teardown: [disconnect],
|
|
513
|
+
});
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
#### `disconnect`
|
|
517
|
+
|
|
518
|
+
Jaypie lifecycle method to disconnect from MongoDB.
|
|
519
|
+
|
|
520
|
+
```javascript
|
|
521
|
+
import { disconnect, lambdaHandler } from "jaypie";
|
|
522
|
+
|
|
523
|
+
const handler = lambdaHandler(async({event}) => {
|
|
524
|
+
// ...
|
|
525
|
+
}, {
|
|
526
|
+
teardown: [disconnect],
|
|
527
|
+
});
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
#### `mongoose`
|
|
531
|
+
|
|
532
|
+
`mongoose` from [NPM](https://www.npmjs.com/package/mongoose)
|
|
533
|
+
|
|
534
|
+
```javascript
|
|
535
|
+
import { mongoose } from "jaypie";
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
### TestKit
|
|
539
|
+
|
|
540
|
+
```bash
|
|
541
|
+
npm install --save-dev @jaypie/testkit
|
|
542
|
+
```
|
|
543
|
+
|
|
544
|
+
#### Log Spying
|
|
545
|
+
|
|
546
|
+
```javascript
|
|
547
|
+
import { restoreLog, spyLog } from "@jaypie/testkit";
|
|
548
|
+
import { log } from "@jaypie/core";
|
|
549
|
+
|
|
550
|
+
beforeEach(() => {
|
|
551
|
+
spyLog(log);
|
|
552
|
+
});
|
|
553
|
+
afterEach(() => {
|
|
554
|
+
restoreLog(log);
|
|
555
|
+
vi.clearAllMocks();
|
|
556
|
+
});
|
|
557
|
+
|
|
558
|
+
test("log", () => {
|
|
559
|
+
log.warn("Danger");
|
|
560
|
+
expect(log.warn).toHaveBeenCalled();
|
|
561
|
+
expect(log.error).not.toHaveBeenCalled();
|
|
562
|
+
});
|
|
563
|
+
```
|
|
564
|
+
|
|
565
|
+
👺 Logging Conventions:
|
|
566
|
+
|
|
567
|
+
* Only use `log.trace` or `log.var` during "happy path"
|
|
568
|
+
* Use `log.debug` for edge cases
|
|
569
|
+
* Now you can add an "observability" test that will fail as soon as new code triggers an unexpected edge condition
|
|
570
|
+
|
|
571
|
+
```javascript
|
|
572
|
+
describe("Observability", () => {
|
|
573
|
+
it("Does not log above trace", async () => {
|
|
574
|
+
// Arrange
|
|
575
|
+
// TODO: "happy path" setup
|
|
576
|
+
// Act
|
|
577
|
+
await myNewFunction(); // TODO: add any "happy path" parameters
|
|
578
|
+
// Assert
|
|
579
|
+
expect(log.debug).not.toHaveBeenCalled();
|
|
580
|
+
expect(log.info).not.toHaveBeenCalled();
|
|
581
|
+
expect(log.warn).not.toHaveBeenCalled();
|
|
582
|
+
expect(log.error).not.toHaveBeenCalled();
|
|
583
|
+
expect(log.fatal).not.toHaveBeenCalled();
|
|
584
|
+
});
|
|
585
|
+
});
|
|
586
|
+
```
|
|
587
|
+
|
|
588
|
+
> 👺 Follow the "arrange, act, assert" pattern
|
|
589
|
+
|
|
590
|
+
#### Test Matchers
|
|
591
|
+
|
|
592
|
+
testSetup.js
|
|
593
|
+
|
|
594
|
+
```javascript
|
|
595
|
+
import { matchers as jaypieMatchers } from "@jaypie/testkit";
|
|
596
|
+
import * as extendedMatchers from "jest-extended";
|
|
597
|
+
import { expect } from "vitest";
|
|
598
|
+
|
|
599
|
+
expect.extend(extendedMatchers);
|
|
600
|
+
expect.extend(jaypieMatchers);
|
|
601
|
+
```
|
|
602
|
+
|
|
603
|
+
test.spec.js
|
|
604
|
+
|
|
605
|
+
```javascript
|
|
606
|
+
import { ConfigurationError } from "@jaypie/core";
|
|
607
|
+
|
|
608
|
+
const error = new ConfigurationError();
|
|
609
|
+
const json = error.json();
|
|
610
|
+
expect(error).toBeJaypieError();
|
|
611
|
+
expect(json).toBeJaypieError();
|
|
612
|
+
```
|
|
613
|
+
|
|
614
|
+
###### `expect(subject).toBeJaypieError()`
|
|
615
|
+
|
|
616
|
+
Validates instance objects:
|
|
617
|
+
|
|
618
|
+
```javascript
|
|
619
|
+
try {
|
|
620
|
+
throw new Error("Sorpresa!");
|
|
621
|
+
} catch (error) {
|
|
622
|
+
expect(error).not.toBeJaypieError();
|
|
623
|
+
}
|
|
624
|
+
```
|
|
625
|
+
|
|
626
|
+
Validates plain old JSON:
|
|
627
|
+
|
|
628
|
+
```javascript
|
|
629
|
+
expect({ errors: [ { status, title, detail } ] }).toBeJaypieError();
|
|
630
|
+
```
|
|
631
|
+
|
|
632
|
+
Jaypie errors, which are `ProjectErrors`, all have a `.json()` to convert
|
|
633
|
+
|
|
634
|
+
###### `expect(subject).toBeValidSchema()`
|
|
635
|
+
|
|
636
|
+
```javascript
|
|
637
|
+
import { jsonApiErrorSchema, jsonApiSchema } from "@jaypie/testkit";
|
|
638
|
+
|
|
639
|
+
expect(jsonApiErrorSchema).toBeValidSchema();
|
|
640
|
+
expect(jsonApiSchema).toBeValidSchema();
|
|
641
|
+
expect({ project: "mayhem" }).not.toBeValidSchema();
|
|
642
|
+
```
|
|
643
|
+
|
|
644
|
+
From `jest-json-schema` [toBeValidSchema.js](https://github.com/americanexpress/jest-json-schema/blob/main/matchers/toBeValidSchema.js) (not documented in README)
|
|
645
|
+
|
|
646
|
+
###### `expect(subject).toMatchSchema(schema)`
|
|
647
|
+
|
|
648
|
+
```javascript
|
|
649
|
+
import { jsonApiErrorSchema, jsonApiSchema } from "@jaypie/testkit";
|
|
650
|
+
import { ConfigurationError } from "@jaypie/core";
|
|
651
|
+
|
|
652
|
+
const error = new ConfigurationError();
|
|
653
|
+
const json = error.json();
|
|
654
|
+
expect(json).toMatchSchema(jsonApiErrorSchema);
|
|
655
|
+
expect(json).not.toMatchSchema(jsonApiSchema);
|
|
656
|
+
```
|
|
657
|
+
|
|
658
|
+
From `jest-json-schema`; see [README](https://github.com/americanexpress/jest-json-schema?tab=readme-ov-file#tomatchschemaschema)
|
|
659
|
+
|
|
660
|
+
#### TestKit Sundry
|
|
661
|
+
|
|
662
|
+
```
|
|
663
|
+
import {
|
|
664
|
+
jsonApiErrorSchema,
|
|
665
|
+
jsonApiSchema,
|
|
666
|
+
mockLogFactory,
|
|
667
|
+
} from '@jaypie/testkit'
|
|
668
|
+
```
|
|
669
|
+
|
|
670
|
+
##### `jsonApiErrorSchema`
|
|
671
|
+
|
|
672
|
+
A [JSON Schema](https://json-schema.org/) validator for the [JSON:API](https://jsonapi.org/) error schema. Powers the `toBeJaypieError` matcher (via `toMatchSchema`).
|
|
673
|
+
|
|
674
|
+
##### `jsonApiSchema`
|
|
675
|
+
|
|
676
|
+
A [JSON Schema](https://json-schema.org/) validator for the [JSON:API](https://jsonapi.org/) data schema.
|
|
677
|
+
|
|
678
|
+
|
|
679
|
+
##### `mockLogFactory()`
|
|
680
|
+
|
|
681
|
+
Creates a mock of the `log` provided by `@jaypie/core`.
|
|
682
|
+
|
|
683
|
+
```javascript
|
|
684
|
+
import { mockLogFactory } from "@jaypie/testkit";
|
|
685
|
+
|
|
686
|
+
const log = mockLogFactory();
|
|
687
|
+
log.warn("Danger");
|
|
688
|
+
expect(log.warn).toHaveBeenCalled();
|
|
689
|
+
expect(log.error).not.toHaveBeenCalled();
|
|
690
|
+
```
|
|
691
|
+
|
|
692
|
+
### `restoreLog(log)`
|
|
693
|
+
|
|
694
|
+
Restores the `log` provided by `@jaypie/core`, commonly performed `afterEach` with `spyLog` in `beforeEach`. See example with `spyLog`.
|
|
695
|
+
|
|
696
|
+
### `spyLog(log)`
|
|
697
|
+
|
|
698
|
+
Spies on the `log` provided by `@jaypie/core`, commonly performed `beforeEach` with `restoreLog` in `afterEach`.
|
|
699
|
+
|
|
700
|
+
```javascript
|
|
701
|
+
import { restoreLog, spyLog } from "@jaypie/testkit";
|
|
702
|
+
import { log } from "@jaypie/core";
|
|
703
|
+
|
|
704
|
+
beforeEach(() => {
|
|
705
|
+
spyLog(log);
|
|
706
|
+
});
|
|
707
|
+
afterEach(() => {
|
|
708
|
+
restoreLog(log);
|
|
709
|
+
vi.clearAllMocks();
|
|
710
|
+
});
|
|
711
|
+
|
|
712
|
+
test("log", () => {
|
|
713
|
+
log.warn("Danger");
|
|
714
|
+
expect(log.warn).toHaveBeenCalled();
|
|
715
|
+
expect(log.error).not.toHaveBeenCalled();
|
|
716
|
+
});
|
|
717
|
+
```
|
|
718
|
+
|
|
719
|
+
## 🌠 Wishlist
|
|
720
|
+
|
|
721
|
+
* `@jaypie/cdk` - CDK package
|
|
722
|
+
* `@jaypie/express` - Express package
|
|
723
|
+
* ...Nicely organized VitePress documentation 😅
|
|
60
724
|
|
|
61
725
|
## 📝 Changelog
|
|
62
726
|
|
|
63
727
|
| Date | Version | Summary |
|
|
64
728
|
| ---------- | ------- | -------------- |
|
|
729
|
+
| 3/19/2024 | 1.0.0 | First publish with `@jaypie/core@1.0.0` |
|
|
730
|
+
| 3/15/2024 | 0.1.0 | Initial deploy |
|
|
65
731
|
| 3/15/2024 | 0.0.1 | Initial commit |
|
|
66
732
|
|
|
67
733
|
## 📜 License
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jaypie",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"author": "Finlayson Studio",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"test:spec:mongoose.package": "vitest run ./src/__tests__/mongoose.package.spec.js"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@jaypie/core": "^0.5.
|
|
20
|
+
"@jaypie/core": "^0.5.8"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
23
|
"@jaypie/testkit": "^1.0.1",
|
|
@@ -33,9 +33,9 @@
|
|
|
33
33
|
"vitest": "^1.4.0"
|
|
34
34
|
},
|
|
35
35
|
"peerDependencies": {
|
|
36
|
-
"@jaypie/aws": "^0.1.
|
|
37
|
-
"@jaypie/lambda": "^0.1.
|
|
38
|
-
"@jaypie/mongoose": "^0.1.
|
|
36
|
+
"@jaypie/aws": "^0.1.3",
|
|
37
|
+
"@jaypie/lambda": "^0.1.9",
|
|
38
|
+
"@jaypie/mongoose": "^0.1.3"
|
|
39
39
|
},
|
|
40
40
|
"peerDependenciesMeta": {
|
|
41
41
|
"@jaypie/aws": {
|