bootpress 7.1.1 → 8.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 +98 -2
- package/index.d.ts +5 -4
- package/index.js +66 -47
- package/package.json +1 -1
- /package/types/{index.ts → index.d.ts} +0 -0
package/README.md
CHANGED
|
@@ -4,10 +4,16 @@
|
|
|
4
4
|
<p align=center>Express but Spring Boot like</p>
|
|
5
5
|
|
|
6
6
|
## Quick Start
|
|
7
|
-
Recommended tool: [create-bootpress-app](https://www.npmjs.com/package/create-bootpress-app)
|
|
7
|
+
Recommended tool: **[create-bootpress-app](https://www.npmjs.com/package/create-bootpress-app)**
|
|
8
|
+
___
|
|
8
9
|
|
|
9
10
|
## Methods
|
|
10
|
-
### **<u>RestService</u>**: Converts all methods to Express RequestHandlers
|
|
11
|
+
### **<u>RestService</u>**: Converts all methods to Express RequestHandlers.
|
|
12
|
+
Http Response Status will inherit status field of returned value by method or 200 by default.
|
|
13
|
+
|
|
14
|
+
Http Response Body will be mapped to 'data' field or value itself by default.
|
|
15
|
+
|
|
16
|
+
If you want to explicitly specify a field named 'data' or 'status' it's recommended to encapsulate your value with HttpResponse class.
|
|
11
17
|
#### Basic usage:
|
|
12
18
|
```ts
|
|
13
19
|
import express from "express";
|
|
@@ -128,6 +134,96 @@ const LogService = new LogServiceImpl();
|
|
|
128
134
|
|
|
129
135
|
app.get("/logs", LogService.findAll() as RequestHandler)
|
|
130
136
|
```
|
|
137
|
+
|
|
138
|
+
### **Argument Passers**
|
|
139
|
+
|
|
140
|
+
```PassBody(serviceFunction)``` -> Passes body to service function without any validation
|
|
141
|
+
|
|
142
|
+
```ParseBodyAs(type)(serviceFunction)``` -> Parses body to specified type then passes it to service function
|
|
143
|
+
|
|
144
|
+
```PassBodyAs(type)(serviceFunction)``` -> Validates body with provided type and passes it to service function
|
|
145
|
+
|
|
146
|
+
```PassAllParams(serviceFunction)``` -> Passes all path parameters to service function as a Record<string, string> (pure js object that contains key-value pairs)
|
|
147
|
+
|
|
148
|
+
```PassAllQueries(serviceFunction)``` -> Passes query to service function as Record<string, string>
|
|
149
|
+
|
|
150
|
+
```PassAllCookies(serviceFunction)``` -> Passes cookies to service function as Record<string, string>
|
|
151
|
+
|
|
152
|
+
```PassParams(...paramNames)(serviceFunction)``` -> Passes specified parameters as arguments to service function
|
|
153
|
+
|
|
154
|
+
```PassQueries(...queryNames)(serviceFunction)``` -> Passes specified queries as arguments to service function
|
|
155
|
+
|
|
156
|
+
```PassCookies(...cookieNames)(serviceFunction)``` -> Passes specified cookies as arguments to service function
|
|
157
|
+
|
|
158
|
+
```PassRequest(serviceFunction)``` -> Passes express request object to service function
|
|
159
|
+
|
|
160
|
+
```PassResponse(serviceFunction)``` -> Passes express response object to service function
|
|
161
|
+
|
|
162
|
+
### Chaining argument passers:
|
|
163
|
+
Argument passers can be chained by passing result of one as serviceFunction parameter to other. e.g.:
|
|
164
|
+
```ts
|
|
165
|
+
// in rest service class:
|
|
166
|
+
function serviceFunction(cookies, body){
|
|
167
|
+
...
|
|
168
|
+
}
|
|
169
|
+
// in router:
|
|
170
|
+
router.post("/", PassAllCookies( PassBodyAs(yourSchema)(restService.serviceFunction) ));
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
## Helper Methods
|
|
175
|
+
|
|
176
|
+
### **getOrThrow(value, httpError)**
|
|
177
|
+
Returns the value back if it's not null, undefined or empty array.
|
|
178
|
+
### **getOrElse(value, defaultValue)**
|
|
179
|
+
Returns the value if it's not null or undefined otherwise returns the default value.
|
|
180
|
+
### **schema(object)**
|
|
181
|
+
Helps you to define a JS Schema.
|
|
182
|
+
### **as(any, string | object | array)**
|
|
183
|
+
Tries to parse any value to provided type.
|
|
184
|
+
|
|
185
|
+
If type of provided type is string then it's a primitive key and valid values are:
|
|
186
|
+
```
|
|
187
|
+
"string"
|
|
188
|
+
"string[]"
|
|
189
|
+
"boolean"
|
|
190
|
+
"boolean[]"
|
|
191
|
+
"number"
|
|
192
|
+
"number[]"
|
|
193
|
+
"integer"
|
|
194
|
+
"integer[]"
|
|
195
|
+
"string?"
|
|
196
|
+
"string[]?"
|
|
197
|
+
"boolean?"
|
|
198
|
+
"boolean[]?"
|
|
199
|
+
"number?"
|
|
200
|
+
"number[]?"
|
|
201
|
+
"integer?"
|
|
202
|
+
"integer[]?"
|
|
203
|
+
```
|
|
204
|
+
If typeof provided type is object then it's a JS Schema and structure must follow:
|
|
205
|
+
```
|
|
206
|
+
{
|
|
207
|
+
"property": string | object | array // Nullable primitives not allowed here instead use question mark end of the property key
|
|
208
|
+
"nullableProperty?": string | object | array
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
If typeof provided type is an array the structure must follow:
|
|
213
|
+
```
|
|
214
|
+
[
|
|
215
|
+
yourJsSchemaObject
|
|
216
|
+
]
|
|
217
|
+
```
|
|
218
|
+
There must be only one element in an array schema which defines ````ArrayOf<Schema>````
|
|
219
|
+
### **asStrict(any, string | object | array)**
|
|
220
|
+
Same as 'as' method but doesn't try to parse different types instead throws error.
|
|
221
|
+
|
|
222
|
+
## v8.0.0 Release Notes:
|
|
223
|
+
- Added support for async service functions. (You don't need to await if you wrapped your service with Bootpress functions)
|
|
224
|
+
- Bugfix for falsy response values
|
|
225
|
+
- Simplified implementation
|
|
226
|
+
|
|
131
227
|
## v7.1.0 Release Notes:
|
|
132
228
|
- getOrThrow: Throws specified error when value is an empty array too.
|
|
133
229
|
## v7.0.0 Release Notes:
|
package/index.d.ts
CHANGED
|
@@ -4,16 +4,17 @@ import { ArraySchema, JsSchema, ValidTypeKeys } from "./helpers"
|
|
|
4
4
|
type RequestHandler = (req: Request, res: Response) => void
|
|
5
5
|
type RequsetHandlerWithArgs = (...args: any[]) => RequestHandler
|
|
6
6
|
|
|
7
|
-
type RestedService<T extends Record<
|
|
7
|
+
type RestedService<T extends Record<PropertyKey, any>> = { [K in keyof T]:
|
|
8
8
|
T[K] extends Function
|
|
9
9
|
? (...args: Parameters<T[K]>) => RequestHandler
|
|
10
10
|
: T[K]
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
type InstanceOrClass<T extends Record<PropertyKey, any>> = T | (new () => T);
|
|
14
|
+
|
|
15
|
+
declare function RestService<T extends Record<PropertyKey, any>>(service: InstanceOrClass<T>): RestedService<T>;
|
|
15
16
|
declare function RestMethod<T>(callback: () => T): RequestHandler;
|
|
16
|
-
declare function Restify(target: any, key:
|
|
17
|
+
declare function Restify(target: any, key: PropertyKey, desc: PropertyDescriptor): PropertyDescriptor;
|
|
17
18
|
|
|
18
19
|
declare function PassBody(serviceFunction: RequestHandler | RequsetHandlerWithArgs): RequestHandler
|
|
19
20
|
declare function ParseBodyAs(type: ValidTypeKeys | JsSchema | ArraySchema ): (serviceFunction: RequestHandler | RequsetHandlerWithArgs) => RequestHandler
|
package/index.js
CHANGED
|
@@ -28,48 +28,57 @@ function RestService(service) {
|
|
|
28
28
|
...Object.getOwnPropertyDescriptors(service),
|
|
29
29
|
...Object.getOwnPropertyDescriptors(service.__proto__ || {})
|
|
30
30
|
};
|
|
31
|
-
const
|
|
31
|
+
const newService = {};
|
|
32
|
+
Object.entries(descriptors).filter(keyvalue => !protectedProperties.includes(keyvalue[0])).forEach(keyvalue => {
|
|
32
33
|
const propertyName = keyvalue[0];
|
|
33
34
|
const value = keyvalue[1].value;
|
|
34
35
|
if (typeof value == "function" && !propertyName.startsWith("#")) {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
(
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}
|
|
56
|
-
]
|
|
36
|
+
newService[propertyName] = ((...args) =>
|
|
37
|
+
(req, res) => {
|
|
38
|
+
try {
|
|
39
|
+
const result = service[propertyName](...args);
|
|
40
|
+
if (result == undefined) {
|
|
41
|
+
reply(res, 204, null);
|
|
42
|
+
}
|
|
43
|
+
else if (result instanceof Promise) {
|
|
44
|
+
result.then(r => {
|
|
45
|
+
reply(res, r.status ?? 200, r.data ?? r);
|
|
46
|
+
}).catch(e => {
|
|
47
|
+
reply(res, e.status ?? 500, e.message ?? e);
|
|
48
|
+
})
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
reply(res, result.status ?? 200, result.data ?? result)
|
|
52
|
+
}
|
|
53
|
+
} catch (e) {
|
|
54
|
+
reply(res, e.status ?? 500, e.message ?? e);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
57
|
} else {
|
|
58
|
-
|
|
58
|
+
newService[propertyName] = service[propertyName];
|
|
59
59
|
}
|
|
60
|
-
})
|
|
61
|
-
|
|
62
|
-
return service;
|
|
60
|
+
})
|
|
61
|
+
return newService;
|
|
63
62
|
}
|
|
64
63
|
|
|
65
64
|
function RestMethod(callback) {
|
|
66
65
|
return (req, res) => {
|
|
67
66
|
try {
|
|
68
67
|
const result = callback();
|
|
69
|
-
|
|
70
|
-
|
|
68
|
+
if (result == undefined) {
|
|
69
|
+
reply(res, 204, null);
|
|
70
|
+
}
|
|
71
|
+
else if (result instanceof Promise) {
|
|
72
|
+
result.then(r => {
|
|
73
|
+
reply(res, r.status ?? 200, r.data ?? r);
|
|
74
|
+
}).catch(e => {
|
|
75
|
+
reply(res, e.status ?? 500, e.message ?? e);
|
|
76
|
+
})
|
|
77
|
+
} else {
|
|
78
|
+
reply(res, result.status ?? 200, result.data ?? result);
|
|
79
|
+
}
|
|
71
80
|
} catch (e) {
|
|
72
|
-
reply(res, e.status
|
|
81
|
+
reply(res, e.status ?? 500, e.message ?? e)
|
|
73
82
|
}
|
|
74
83
|
}
|
|
75
84
|
}
|
|
@@ -82,10 +91,20 @@ function Restify(target, key, desc) {
|
|
|
82
91
|
return (req, res) => {
|
|
83
92
|
try {
|
|
84
93
|
const result = oldFunc(...args);
|
|
85
|
-
|
|
86
|
-
|
|
94
|
+
if (result == undefined) {
|
|
95
|
+
reply(res, 204, null);
|
|
96
|
+
}
|
|
97
|
+
else if (result instanceof Promise) {
|
|
98
|
+
result.then(r => {
|
|
99
|
+
reply(res, r.status ?? 200, r.data ?? r);
|
|
100
|
+
}).catch(e => {
|
|
101
|
+
reply(res, e.status ?? 500, e.message ?? e);
|
|
102
|
+
})
|
|
103
|
+
} else {
|
|
104
|
+
reply(res, result.status ?? 200, result.data ?? result);
|
|
105
|
+
}
|
|
87
106
|
} catch (e) {
|
|
88
|
-
reply(res, e.status
|
|
107
|
+
reply(res, e.status ?? 500, e.message ?? e);
|
|
89
108
|
}
|
|
90
109
|
}
|
|
91
110
|
}).bind(target)
|
|
@@ -204,25 +223,25 @@ function PassBodyAs(type) {
|
|
|
204
223
|
}
|
|
205
224
|
|
|
206
225
|
function PassBody(actualHandler) {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
226
|
+
return (...args) => {
|
|
227
|
+
if (isRequstHandlerArgs(args)) {
|
|
228
|
+
const req = args.at(-3); const res = args.at(-2);
|
|
229
|
+
try {
|
|
230
|
+
return actualHandler(req.body)(req, res);
|
|
231
|
+
} catch (e) {
|
|
232
|
+
reply(res, e.status || 500, e.message || e);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
} else {
|
|
236
|
+
return (req, res) => {
|
|
210
237
|
try {
|
|
211
|
-
return actualHandler(req.body)(req, res);
|
|
238
|
+
return actualHandler(...args, req.body)(req, res);
|
|
212
239
|
} catch (e) {
|
|
213
|
-
reply(res, e.status || 500, e.message || e)
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
} else {
|
|
217
|
-
return (req, res) => {
|
|
218
|
-
try {
|
|
219
|
-
return actualHandler(...args, req.body)(req, res);
|
|
220
|
-
} catch (e) {
|
|
221
|
-
reply(res, e.status || 500, e.message || e)
|
|
222
|
-
}
|
|
240
|
+
reply(res, e.status || 500, e.message || e)
|
|
223
241
|
}
|
|
224
242
|
}
|
|
225
243
|
}
|
|
244
|
+
}
|
|
226
245
|
}
|
|
227
246
|
|
|
228
247
|
function PassRequest(actualHandler) {
|
package/package.json
CHANGED
|
File without changes
|