@odatnurd/cf-requests 0.1.1 → 0.1.3
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 +99 -1
- package/aegis/index.js +121 -0
- package/lib/handlers.js +2 -2
- package/package.json +7 -4
package/README.md
CHANGED
|
@@ -78,17 +78,115 @@ export const $post = routeHandler(
|
|
|
78
78
|
);
|
|
79
79
|
```
|
|
80
80
|
|
|
81
|
+
|
|
82
|
+
## Testing Utilities (Optional)
|
|
83
|
+
|
|
84
|
+
This package includes an optional set of helpers to facilitate testing your own
|
|
85
|
+
projects with the [Aegis](https://www.npmjs.com/package/@axel669/aegis) test
|
|
86
|
+
runner.
|
|
87
|
+
|
|
88
|
+
To use these utilities, you must install the required peer dependencies into
|
|
89
|
+
your own project's `devDependencies` if you have not already done so.
|
|
90
|
+
|
|
91
|
+
```sh
|
|
92
|
+
pnpm add -D @odatnurd/d1-query @axel669/aegis miniflare fs-jetpack
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
> ℹ️ If you are actively using
|
|
96
|
+
> [@odatnurd/d1-query](https://www.npmjs.com/package/@odatnurd/d1-query) in your
|
|
97
|
+
> project, that library should be installed as a regular `dependency` and not a
|
|
98
|
+
> `devDependency`
|
|
99
|
+
|
|
100
|
+
The `@odatnurd/cf-requests` module exports the following functions:
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
### Helper Functions
|
|
104
|
+
|
|
105
|
+
```javascript
|
|
106
|
+
export function initializeResponseChecks() {}
|
|
107
|
+
```
|
|
108
|
+
Registers all [custom checks](#custom-checks) with Aegis. This should be called
|
|
109
|
+
once at the top of your `aegis.config.js` file.
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
```javascript
|
|
114
|
+
export async function schemaTest(dataType, schema, data, validator = undefined) {}
|
|
115
|
+
```
|
|
116
|
+
Takes a `dataType` and `schema` as would be provided to the `validate` function
|
|
117
|
+
and runs the validation to see what the result is. The function will return
|
|
118
|
+
either:
|
|
119
|
+
|
|
120
|
+
* `Valid Data`: An Object that represents the validated and masked data
|
|
121
|
+
* `Invalid Data`: A `Response` object that carries the error payload
|
|
122
|
+
|
|
123
|
+
Using this, it is possible to validate that a schema works as expected without
|
|
124
|
+
having to use it in the actual request first.
|
|
125
|
+
|
|
126
|
+
> ℹ️ By default, the test will use the `validate` function to perform the data
|
|
127
|
+
> validation. If desired, you can pass an optional `validator` function as the
|
|
128
|
+
> final argument. This must take the same arguments as `validate` does, and
|
|
129
|
+
> follow the same contract. This allows for testing of other schema libraries,
|
|
130
|
+
> such as during migrations to this library.
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
### Configuration
|
|
134
|
+
|
|
135
|
+
You can import the helper functions into your `aegis.config.js` file to easily
|
|
136
|
+
set up a test environment, optionally also populating one or more SQL files into
|
|
137
|
+
the database first in order to set up testing.
|
|
138
|
+
|
|
139
|
+
**Example `aegis.config.js`:**
|
|
140
|
+
|
|
141
|
+
```js
|
|
142
|
+
import { initializeCustomChecks, aegisSetup, aegisTeardown } from '@odatnurd/cf-aegis';
|
|
143
|
+
import { initializeResponseChecks } from '@odatnurd/cf-requests/aegis';
|
|
144
|
+
|
|
145
|
+
initializeCustomChecks();
|
|
146
|
+
initializeResponseChecks()
|
|
147
|
+
|
|
148
|
+
export const config = {
|
|
149
|
+
files: [
|
|
150
|
+
"test/**/*.test.js",
|
|
151
|
+
],
|
|
152
|
+
hooks: {
|
|
153
|
+
async setup(ctx) {
|
|
154
|
+
await aegisSetup(ctx, 'test/setup.sql', 'DB');
|
|
155
|
+
},
|
|
156
|
+
|
|
157
|
+
async teardown(ctx) {
|
|
158
|
+
await aegisTeardown(ctx);
|
|
159
|
+
},
|
|
160
|
+
},
|
|
161
|
+
failAction: "afterSection",
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
### Custom Checks
|
|
167
|
+
|
|
168
|
+
The `initializeResponseChecks()` function registers several custom checks with Aegis
|
|
169
|
+
to simplify testing database-related logic.
|
|
170
|
+
|
|
171
|
+
* `.isResponse($)`: Checks if a value is a `Response` object.
|
|
172
|
+
* `.isNotResponse($)`: Checks if a value is not a `Response` object.
|
|
173
|
+
* `.isResponseWithStatus($, count)`: Checks if an object is a `Response` with a
|
|
174
|
+
specific `status` code.
|
|
175
|
+
|
|
176
|
+
|
|
81
177
|
## Methods
|
|
82
178
|
|
|
83
179
|
```js
|
|
84
180
|
export function success(ctx, message, result, status) {}
|
|
85
181
|
```
|
|
86
182
|
|
|
87
|
-
Indicate a successful return in JSON with the given `HTTP` status code
|
|
183
|
+
Indicate a successful return in JSON with the given `HTTP` status code; the
|
|
184
|
+
status code is used to construct the JSON as well as the response:
|
|
88
185
|
|
|
89
186
|
```js
|
|
90
187
|
{
|
|
91
188
|
"success": true,
|
|
189
|
+
status,
|
|
92
190
|
message,
|
|
93
191
|
data: result
|
|
94
192
|
}
|
package/aegis/index.js
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/******************************************************************************/
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
import { addCheck } from '@axel669/aegis';
|
|
5
|
+
import { validate } from '../lib/handlers.js';
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
/******************************************************************************/
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
/*
|
|
12
|
+
* Initializes some custom Aegis checks that make testing of schema and data
|
|
13
|
+
* requests easier.
|
|
14
|
+
*
|
|
15
|
+
* This is entirely optional.
|
|
16
|
+
*/
|
|
17
|
+
export function initializeRequestChecks() {
|
|
18
|
+
// Check that a value is a response object from our middleware
|
|
19
|
+
addCheck.value.isResponse(
|
|
20
|
+
source => source instanceof Response
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
// Check that a value is NOT a response object
|
|
24
|
+
addCheck.value.isNotResponse(
|
|
25
|
+
source => (source instanceof Response) === false
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
addCheck.value.isResponseWithStatus(
|
|
29
|
+
(source, status) => source instanceof Response && source.status == status
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
/******************************************************************************/
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
/* A helper function to be able to test the schema validation options in the
|
|
38
|
+
* library. This takes a schema object and data type such as you would pass to
|
|
39
|
+
* the validate() function, along with an input data object, and exercises that
|
|
40
|
+
* the schema works as expected.
|
|
41
|
+
*
|
|
42
|
+
* The result of the call is either a JSON object that represents the validated
|
|
43
|
+
* and masked input data if the schema validated the data, or a Response object
|
|
44
|
+
* that carries the failure of the validation. This would be a response of code
|
|
45
|
+
* 400 with a JSON body that carries the actual validation failure message
|
|
46
|
+
* within it. */
|
|
47
|
+
export async function schemaTest(dataType, schema, data, validator) {
|
|
48
|
+
// If a validator is provided, use it; otherwise use ours. This requires that
|
|
49
|
+
// you provide a call-compatible validator. This is here only to support some
|
|
50
|
+
// migrations of old code that is using a different validator than the one
|
|
51
|
+
// this library currently uses.
|
|
52
|
+
validator = validator ??= validate;
|
|
53
|
+
|
|
54
|
+
// Use the Hono factory to create our middleware, just as a caller would.
|
|
55
|
+
// Create a middleware using the Hono factory method for this, using the
|
|
56
|
+
// schema object and data type provided.
|
|
57
|
+
const middleware = validator(dataType, schema);
|
|
58
|
+
|
|
59
|
+
// As a result of the middleware, we will either capture the validated (and
|
|
60
|
+
// masked) input JSON data, or we will capture an error response. As a part of
|
|
61
|
+
// this we also capture what the eventual status of the call would be if this
|
|
62
|
+
// generates a response, so that we can put it into the response object.
|
|
63
|
+
let validData = null;
|
|
64
|
+
let errorResponse = null;
|
|
65
|
+
let responseStatus = 200;
|
|
66
|
+
|
|
67
|
+
// A fake next to pass to the middleware when we execute it, so that it does
|
|
68
|
+
// not throw an error.
|
|
69
|
+
const next = () => {};
|
|
70
|
+
|
|
71
|
+
// In order to run the test we need to create a fake Hono context object to
|
|
72
|
+
// pass to the middleware; this mimics the smallest possible footprint of
|
|
73
|
+
// Hono context for our purposes.
|
|
74
|
+
const ctx = {
|
|
75
|
+
req: {
|
|
76
|
+
// These methods are used by the validator to pull the parsed data out of
|
|
77
|
+
// the request in order to validate it.
|
|
78
|
+
param: () => data,
|
|
79
|
+
json: async () => data,
|
|
80
|
+
query: () => data,
|
|
81
|
+
|
|
82
|
+
// The validator invokes this to get headers out of the request when the
|
|
83
|
+
// data type is JSON.
|
|
84
|
+
header: (name) => {
|
|
85
|
+
if (name.toLowerCase() === 'content-type' && dataType === 'json') {
|
|
86
|
+
return 'application/json';
|
|
87
|
+
}
|
|
88
|
+
return undefined;
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
// When validation succeeds, it invokes this to store the data back into
|
|
92
|
+
// the context.
|
|
93
|
+
addValidatedData: (target, data) => validData = data
|
|
94
|
+
},
|
|
95
|
+
|
|
96
|
+
// Used to capture a failure; the validator will invoke status to set the
|
|
97
|
+
// required HTTP response and then invoke the json() method to populate the
|
|
98
|
+
// error.
|
|
99
|
+
status: (inStatus) => { responseStatus = inStatus; },
|
|
100
|
+
json: (payload) => {
|
|
101
|
+
errorResponse = new Response(
|
|
102
|
+
JSON.stringify(payload), {
|
|
103
|
+
status: responseStatus,
|
|
104
|
+
statusText: "Bad Request",
|
|
105
|
+
headers: { "Content-Type": "application/json" }
|
|
106
|
+
}
|
|
107
|
+
);
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
// Run the middleware; we either capture a result in the error payload or the
|
|
112
|
+
// validation result.
|
|
113
|
+
await middleware(ctx, next);
|
|
114
|
+
|
|
115
|
+
// Return the error payload if validation failed, otherwise return the
|
|
116
|
+
// validated data from the success path.
|
|
117
|
+
return errorResponse ?? validData;
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
/******************************************************************************/
|
package/lib/handlers.js
CHANGED
|
@@ -16,7 +16,7 @@ export const success = (ctx, message, result, status) => {
|
|
|
16
16
|
result ??= [];
|
|
17
17
|
|
|
18
18
|
ctx.status(status);
|
|
19
|
-
return ctx.json({ success: true, message, data: result });
|
|
19
|
+
return ctx.json({ success: true, status, message, data: result });
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
|
|
@@ -31,7 +31,7 @@ export const fail = (ctx, message, status, result) => {
|
|
|
31
31
|
status ??= 400;
|
|
32
32
|
|
|
33
33
|
ctx.status(status);
|
|
34
|
-
return ctx.json({ success: false, message, data: result });
|
|
34
|
+
return ctx.json({ success: false, status, message, data: result });
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@odatnurd/cf-requests",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Simple Cloudflare Hono request wrapper",
|
|
5
5
|
"author": "OdatNurd (https://odatnurd.net)",
|
|
6
6
|
"homepage": "https://github.com/OdatNurd/cf-requests",
|
|
@@ -13,15 +13,18 @@
|
|
|
13
13
|
"type": "module",
|
|
14
14
|
"main": "lib/handlers.js",
|
|
15
15
|
"exports": {
|
|
16
|
-
".": "./lib/handlers.js"
|
|
16
|
+
".": "./lib/handlers.js",
|
|
17
|
+
"./aegis": "./aegis/index.js"
|
|
17
18
|
},
|
|
18
19
|
"files": [
|
|
19
|
-
"lib
|
|
20
|
+
"lib",
|
|
21
|
+
"aegis"
|
|
20
22
|
],
|
|
21
23
|
"keywords": [
|
|
22
24
|
"cloudflare",
|
|
23
25
|
"hono",
|
|
24
|
-
"routing"
|
|
26
|
+
"routing",
|
|
27
|
+
"aegis"
|
|
25
28
|
],
|
|
26
29
|
"peerDependencies": {
|
|
27
30
|
"hono": "^4.7.0"
|