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 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<string, any>> = { [K in keyof T]:
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
- declare function RestService<T extends Record<string, any>>(clazz: new () => T): RestedService<T>;
14
- declare function RestService<T extends Record<string, any>>(service: T): RestedService<T>;
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: string, desc: PropertyDescriptor): PropertyDescriptor;
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 alteredDescriptors = Object.fromEntries(Object.entries(descriptors).filter(keyvalue => !protectedProperties.includes(keyvalue[0])).map(keyvalue => {
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
- return [
36
- propertyName,
37
- {
38
- value: ((...args) =>
39
- (req, res) => {
40
- try {
41
- const result = value.bind(service)(...args);
42
- if (result === undefined) {
43
- throw new HttpError(200, "Your method is executed but it returned undefined. Please avoid using 'void' methods as service methods.");
44
- } else if (result === null) {
45
- throw new HttpError(200, "Your method is executed but it returned null. At least a value is expected to be returned.");
46
- }
47
- reply(res, result.status || 200, result.data || result)
48
- } catch (e) {
49
- reply(res, e.status || 500, e.message || e);
50
- }
51
- }),
52
- configurable: keyvalue[1].configurable,
53
- writable: keyvalue[1].writable,
54
- enumerable: false
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
- return keyvalue;
58
+ newService[propertyName] = service[propertyName];
59
59
  }
60
- }));
61
- Object.defineProperties(service, alteredDescriptors);
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
- reply(res, result.status || 200, result.data || result);
70
- return result;
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 || 500, e.message || e)
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
- reply(res, result.status || 200, result.data || result);
86
- return result;
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 || 500, e.message || e);
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
- return (...args) => {
208
- if (isRequstHandlerArgs(args)) {
209
- const req = args.at(-3); const res = args.at(-2);
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bootpress",
3
- "version": "7.1.1",
3
+ "version": "8.0.0",
4
4
  "description": "REST service methods for express",
5
5
  "main": "index.js",
6
6
  "repository": {
File without changes