bootpress 6.0.2 → 7.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 +35 -13
- package/helpers/index.d.ts +3 -7
- package/helpers/index.js +77 -22
- package/index.d.ts +2 -0
- package/index.js +48 -12
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@ Recommended tool: [create-bootpress-app](https://www.npmjs.com/package/create-bo
|
|
|
12
12
|
```ts
|
|
13
13
|
import express from "express";
|
|
14
14
|
import { HttpError, PassParams, RestService } from "bootpress";
|
|
15
|
-
import {
|
|
15
|
+
import { as, getOrThrow } from "bootpress/helpers";
|
|
16
16
|
|
|
17
17
|
const app = express();
|
|
18
18
|
app.use(express.json());
|
|
@@ -23,8 +23,8 @@ const UserServiceImpl = {
|
|
|
23
23
|
return this.users;
|
|
24
24
|
},
|
|
25
25
|
findUserById(idInParams: string) {
|
|
26
|
-
const id =
|
|
27
|
-
return getOrThrow(this.users.find(user => user
|
|
26
|
+
const id = as(idInParams, "integer");
|
|
27
|
+
return getOrThrow(this.users.find(user => user === id), new HttpError(404, "Not Found"));
|
|
28
28
|
}
|
|
29
29
|
};
|
|
30
30
|
|
|
@@ -37,33 +37,34 @@ app.get("/users/:id", PassParams("id")(UserService.findUserById));
|
|
|
37
37
|
#### Advanced usage:
|
|
38
38
|
```ts
|
|
39
39
|
import { HttpError, HttpResponse, PassBody, PassParams, PassQueries, RestService } from "bootpress";
|
|
40
|
-
import {
|
|
40
|
+
import { as, asStrict, getOrThrow } from "bootpress/helpers";
|
|
41
41
|
|
|
42
42
|
class PostServiceImpl {
|
|
43
43
|
posts = [1, 2, 3, 4, 5];
|
|
44
|
-
findById(id:
|
|
45
|
-
console.log("looking for " + id);
|
|
44
|
+
findById(id: string) {
|
|
46
45
|
return getOrThrow(
|
|
47
|
-
this.posts.find(p => p
|
|
46
|
+
this.posts.find(p => p === as(id, "integer")),
|
|
48
47
|
new HttpError(404, "Post is not found")
|
|
49
48
|
);
|
|
50
49
|
}
|
|
51
50
|
add(body: any) {
|
|
52
|
-
let casted =
|
|
51
|
+
let casted = asStrict(body, {
|
|
53
52
|
"id": "number"
|
|
54
53
|
});
|
|
55
54
|
this.posts.push(casted.id);
|
|
56
55
|
return new HttpResponse(201, casted.id);
|
|
57
56
|
}
|
|
58
57
|
delete(deleteInQuery: string, idInQuery: string) {
|
|
59
|
-
const idx = deleteInQuery === "yes" ? this.posts.indexOf(
|
|
58
|
+
const idx = deleteInQuery === "yes" ? this.posts.indexOf(as(idInQuery, "integer")) : -1;
|
|
60
59
|
if (idx > -1) {
|
|
61
|
-
this
|
|
62
|
-
this
|
|
60
|
+
this.#logDeleted(idInQuery);
|
|
61
|
+
return this.posts.splice(idx, 1);
|
|
62
|
+
}else {
|
|
63
|
+
throw new HttpError(404, "Post is not found")
|
|
63
64
|
}
|
|
64
65
|
}
|
|
65
66
|
// use private methods to
|
|
66
|
-
#
|
|
67
|
+
#logDeleted(id: number | string) {
|
|
67
68
|
console.warn(`post ${id} is deleted`)
|
|
68
69
|
}
|
|
69
70
|
findAll() {
|
|
@@ -126,4 +127,25 @@ class LogServiceImpl {
|
|
|
126
127
|
const LogService = new LogServiceImpl();
|
|
127
128
|
|
|
128
129
|
app.get("/logs", LogService.findAll() as RequestHandler)
|
|
129
|
-
```
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## v7.0.0 Release Notes:
|
|
133
|
+
|
|
134
|
+
### Deprecated helper methods:
|
|
135
|
+
- asSchema
|
|
136
|
+
- asString
|
|
137
|
+
- asBoolean
|
|
138
|
+
- asInteger
|
|
139
|
+
- asNumber
|
|
140
|
+
|
|
141
|
+
Please use "as" or "asStrict" instead of these functions. For example:
|
|
142
|
+
|
|
143
|
+
```ts
|
|
144
|
+
//const x: string = asString(o); // deprecated
|
|
145
|
+
const x: string = as(o, "string");
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Added / Changed helper methods:
|
|
149
|
+
- asStrict : Asserts types strictly
|
|
150
|
+
- PassBodyAs(schema): Body must be as same as schema
|
|
151
|
+
- ParseBodyAs(schema): Body have to be parsable to schema
|
package/helpers/index.d.ts
CHANGED
|
@@ -31,12 +31,7 @@ type TypedSchema<T> = {
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
export function getOrThrow<T, E extends HttpError>(data: T, error: E): T;
|
|
34
|
-
export function getOrElse<T, E>(data: T, defaultValue: E): T | E;
|
|
35
|
-
export function asBoolean(o: any): boolean;
|
|
36
|
-
export function asNumber(o: any): number;
|
|
37
|
-
export function asInteger(o: any): number;
|
|
38
|
-
export function asString(o: any): string;
|
|
39
|
-
export function asSchema<T extends JsSchema>(o: any, jsSchema: T): TypedSchema<T>;
|
|
34
|
+
export function getOrElse<T, E>(data: T, defaultValue: E): E extends NonNullable<infer T> ? E : T | E;
|
|
40
35
|
export function schema<T extends JsSchema>(schema: T): T;
|
|
41
36
|
|
|
42
37
|
type ExtendedTypeMap = {
|
|
@@ -60,4 +55,5 @@ type ExtendedTypeMap = {
|
|
|
60
55
|
|
|
61
56
|
type ExtendedTypeKeys = keyof ExtendedTypeMap;
|
|
62
57
|
type ExtValOf<T extends ExtendedTypeKeys> = ExtendedTypeMap[T]
|
|
63
|
-
export function as<T extends (ExtendedTypeKeys | JsSchema | ArraySchema)>(o:any,
|
|
58
|
+
export function as<T extends (ExtendedTypeKeys | JsSchema | ArraySchema)>(o:any, type: T, errorVariableName?: string): T extends ExtendedTypeKeys ? ExtValOf<T> : TypedSchema<T>;
|
|
59
|
+
export function asStrict<T extends (ExtendedTypeKeys | JsSchema | ArraySchema)>(o:any, type: T, errorVariableName?: string): T extends ExtendedTypeKeys ? ExtValOf<T> : TypedSchema<T>;
|
package/helpers/index.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
const { HttpError } = require("../types");
|
|
2
2
|
|
|
3
|
+
const allowedPrimitives = ["string", "number", "boolean", "integer"];
|
|
4
|
+
|
|
3
5
|
function getOrThrow(data, error) {
|
|
4
6
|
if (data === null || data === undefined) {
|
|
5
7
|
throw error;
|
|
@@ -68,7 +70,7 @@ function asInteger(o, errorMessage = undefined, errorStatus = 400) {
|
|
|
68
70
|
}
|
|
69
71
|
|
|
70
72
|
function asString(o, errorMessage = undefined, errorStatus = 400) {
|
|
71
|
-
errorMessage = errorMessage ?? `Value ${o} should have been a string but it's
|
|
73
|
+
errorMessage = errorMessage ?? `Value ${o} should have been a string but it's ${typeof o}`;
|
|
72
74
|
const validTypes = ["string", "number"];
|
|
73
75
|
if (validTypes.includes(typeof o)) {
|
|
74
76
|
return '' + o;
|
|
@@ -77,7 +79,10 @@ function asString(o, errorMessage = undefined, errorStatus = 400) {
|
|
|
77
79
|
}
|
|
78
80
|
}
|
|
79
81
|
|
|
80
|
-
function asSchema(o, schema) {
|
|
82
|
+
function asSchema(o, schema, strict = false) {
|
|
83
|
+
if(!(schema instanceof Object) || Array.isArray(schema)){
|
|
84
|
+
throw new HttpError(500, `Schema is not valid ${JSON.stringify(schema)}`);
|
|
85
|
+
}
|
|
81
86
|
const schemaKeyValues = Object.entries(schema);
|
|
82
87
|
let result = {};
|
|
83
88
|
for (let i = 0; i < schemaKeyValues.length; i++) {
|
|
@@ -90,20 +95,19 @@ function asSchema(o, schema) {
|
|
|
90
95
|
}
|
|
91
96
|
}
|
|
92
97
|
const expectedType = schemaKeyValues[i][1];
|
|
93
|
-
// const errorMessage = o[key] == null ? `Value of ${key} should have been a ${expectedType} but it's null` : `Value of ${key} should have been a ${expectedType} but it's a ${typeof o[key]}`;
|
|
94
98
|
|
|
95
99
|
if (typeof expectedType === "object") {
|
|
96
100
|
if (Array.isArray(expectedType)) {
|
|
97
101
|
result[key] = [];
|
|
98
102
|
for (let j = 0; j < o[key].length; j++) {
|
|
99
|
-
result[key][j] = asSchema(o[key][j], expectedType[0])
|
|
103
|
+
result[key][j] = asSchema(o[key][j], expectedType[0], strict)
|
|
100
104
|
}
|
|
101
105
|
} else {
|
|
102
|
-
result[key] = asSchema(o[key], expectedType);
|
|
106
|
+
result[key] = asSchema(o[key], expectedType, strict);
|
|
103
107
|
}
|
|
104
108
|
}
|
|
105
109
|
else if (typeof expectedType === "string") {
|
|
106
|
-
result[key] = as(o[key], expectedType, key);
|
|
110
|
+
result[key] = strict ? asStrict(o[key], expectedType, key) : as(o[key], expectedType, key);
|
|
107
111
|
}
|
|
108
112
|
else {
|
|
109
113
|
throw new HttpError(500, `Type of a schema key should be a primitive type or another schema`);
|
|
@@ -117,14 +121,10 @@ function schema(schema) {
|
|
|
117
121
|
return schema;
|
|
118
122
|
}
|
|
119
123
|
|
|
120
|
-
function
|
|
124
|
+
function asPrimitiveArrayOf(o, elementType) {
|
|
121
125
|
if (Array.isArray(o)) {
|
|
122
126
|
for (let i = 0; i < o.length; i++) {
|
|
123
|
-
|
|
124
|
-
asInteger(o[i]);
|
|
125
|
-
} else if (typeof o[i] != elementType) {
|
|
126
|
-
throw new HttpError(400, `Each element in array should have been a ${elementType} but ${o[i]} is present with type ${typeof o[i]}`);
|
|
127
|
-
}
|
|
127
|
+
o[i] = checkPrimitive(o[i], elementType, "Array elemet");
|
|
128
128
|
}
|
|
129
129
|
return o;
|
|
130
130
|
} else {
|
|
@@ -133,17 +133,17 @@ function asArrayOf(o, elementType) {
|
|
|
133
133
|
}
|
|
134
134
|
|
|
135
135
|
function as(o, type, namedErrorVariable = o) {
|
|
136
|
-
if (typeof type
|
|
136
|
+
if (typeof type === "string") {
|
|
137
137
|
if (type.endsWith("[]")) {
|
|
138
138
|
// array check
|
|
139
139
|
const elementType = type.replace("[]", "");
|
|
140
|
-
return
|
|
140
|
+
return asPrimitiveArrayOf(o, elementType);
|
|
141
141
|
} else { // non array types:
|
|
142
142
|
if (type.endsWith("?") && o == null) {
|
|
143
143
|
return null;
|
|
144
144
|
} else if (type.endsWith("?") && o != null) {
|
|
145
145
|
const actualType = type.replace("?", "");
|
|
146
|
-
return as(o, actualType);
|
|
146
|
+
return as(o, actualType, namedErrorVariable);
|
|
147
147
|
}
|
|
148
148
|
// primitive check
|
|
149
149
|
switch (type) {
|
|
@@ -159,7 +159,7 @@ function as(o, type, namedErrorVariable = o) {
|
|
|
159
159
|
throw new HttpError(500, `Unsupported type ${type}`);
|
|
160
160
|
}
|
|
161
161
|
}
|
|
162
|
-
} else if (typeof type
|
|
162
|
+
} else if (typeof type === "object" && type != null) {
|
|
163
163
|
if (Array.isArray(type)) {
|
|
164
164
|
if (type.length > 1) {
|
|
165
165
|
throw new HttpError(500, `You can define only one schema for types ArrayOf<Schema>`);
|
|
@@ -185,14 +185,69 @@ function as(o, type, namedErrorVariable = o) {
|
|
|
185
185
|
}
|
|
186
186
|
}
|
|
187
187
|
|
|
188
|
+
function asStrict(o, type, namedErrorVariable = o) {
|
|
189
|
+
if (typeof type === "string") {
|
|
190
|
+
if (type.endsWith("[]")) {
|
|
191
|
+
// array check
|
|
192
|
+
const elementType = type.replace("[]", "");
|
|
193
|
+
return asPrimitiveArrayOf(o, elementType);
|
|
194
|
+
} else { // non array types:
|
|
195
|
+
if (type.endsWith("?") && o == null) {
|
|
196
|
+
return null;
|
|
197
|
+
} else if (type.endsWith("?") && o != null) {
|
|
198
|
+
const actualType = type.replace("?", "");
|
|
199
|
+
return asStrict(o, actualType, namedErrorVariable);
|
|
200
|
+
}
|
|
201
|
+
// primitive check
|
|
202
|
+
if (!allowedPrimitives.includes(type)) {
|
|
203
|
+
throw new HttpError(500, `Unsupported type ${type}`);
|
|
204
|
+
}
|
|
205
|
+
return checkPrimitive(o, type, namedErrorVariable);
|
|
206
|
+
}
|
|
207
|
+
} else if (typeof type === "object" && type != null) {
|
|
208
|
+
if (Array.isArray(type)) {
|
|
209
|
+
if (type.length > 1) {
|
|
210
|
+
throw new HttpError(500, `You can define only one schema for types ArrayOf<Schema>`);
|
|
211
|
+
} else if (type.length < 1) {
|
|
212
|
+
throw new HttpError(500, `You must define a schema for types ArrayOf<Schema>`);
|
|
213
|
+
}
|
|
214
|
+
// array schema validation
|
|
215
|
+
if (!Array.isArray(o)) {
|
|
216
|
+
throw new HttpError(400, `Provided value should have been an array but it's ${typeof o}`)
|
|
217
|
+
}
|
|
218
|
+
const providedSchema = type[0];
|
|
219
|
+
let result = [];
|
|
220
|
+
for (let i = 0; i < o.length; i++) {
|
|
221
|
+
result.push(asSchema(o[i], providedSchema, true));
|
|
222
|
+
}
|
|
223
|
+
return result;
|
|
224
|
+
} else {
|
|
225
|
+
// schema validation
|
|
226
|
+
return asSchema(o, type, true);
|
|
227
|
+
}
|
|
228
|
+
} else {
|
|
229
|
+
throw new HttpError(500, `Unsupported type check ${type}`)
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function checkPrimitive(o, type, varName = o) {
|
|
234
|
+
const error = new HttpError(400, `${varName} should have been a ${type}. But it's ${typeof o}`);
|
|
235
|
+
if (type === "integer") {
|
|
236
|
+
if (!Number.isInteger(o)) {
|
|
237
|
+
throw error;
|
|
238
|
+
}
|
|
239
|
+
return o;
|
|
240
|
+
}
|
|
241
|
+
if (typeof o !== type) {
|
|
242
|
+
throw error;
|
|
243
|
+
}
|
|
244
|
+
return o;
|
|
245
|
+
}
|
|
246
|
+
|
|
188
247
|
module.exports = {
|
|
189
248
|
getOrThrow,
|
|
190
249
|
getOrElse,
|
|
191
|
-
asBoolean,
|
|
192
|
-
asNumber,
|
|
193
|
-
asInteger,
|
|
194
|
-
asString,
|
|
195
|
-
asSchema,
|
|
196
250
|
schema,
|
|
197
|
-
as
|
|
251
|
+
as,
|
|
252
|
+
asStrict
|
|
198
253
|
}
|
package/index.d.ts
CHANGED
|
@@ -16,6 +16,7 @@ declare function RestMethod<T>(callback: () => T): RequestHandler;
|
|
|
16
16
|
declare function Restify(target: any, key: string, desc: PropertyDescriptor): PropertyDescriptor;
|
|
17
17
|
|
|
18
18
|
declare function PassBody(serviceFunction: RequestHandler | RequsetHandlerWithArgs): RequestHandler
|
|
19
|
+
declare function ParseBodyAs(type: ValidTypeKeys | JsSchema | ArraySchema ): (serviceFunction: RequestHandler | RequsetHandlerWithArgs) => RequestHandler
|
|
19
20
|
declare function PassBodyAs(type: ValidTypeKeys | JsSchema | ArraySchema ): (serviceFunction: RequestHandler | RequsetHandlerWithArgs) => RequestHandler
|
|
20
21
|
declare function PassAllParams(serviceFunction: RequestHandler | RequsetHandlerWithArgs): RequestHandler
|
|
21
22
|
declare function PassAllQueries(serviceFunction: RequestHandler | RequsetHandlerWithArgs): RequestHandler
|
|
@@ -38,6 +39,7 @@ export {
|
|
|
38
39
|
PassAllCookies,
|
|
39
40
|
PassBody,
|
|
40
41
|
PassBodyAs,
|
|
42
|
+
ParseBodyAs,
|
|
41
43
|
PassRequest,
|
|
42
44
|
PassResponse
|
|
43
45
|
}
|
package/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const { as } = require("./helpers");
|
|
1
|
+
const { as, asStrict } = require("./helpers");
|
|
2
2
|
const { HttpError } = require("./types");
|
|
3
3
|
|
|
4
4
|
const protectedProperties = [
|
|
@@ -155,13 +155,26 @@ function PassAllQueries(actualHandler) {
|
|
|
155
155
|
}
|
|
156
156
|
}
|
|
157
157
|
|
|
158
|
-
function
|
|
159
|
-
return (
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
158
|
+
function ParseBodyAs(type) {
|
|
159
|
+
return (actualHandler) => {
|
|
160
|
+
return (...args) => {
|
|
161
|
+
if (isRequstHandlerArgs(args)) {
|
|
162
|
+
const req = args.at(-3); const res = args.at(-2);
|
|
163
|
+
try {
|
|
164
|
+
return actualHandler(as(req.body, type))(req, res);
|
|
165
|
+
} catch (e) {
|
|
166
|
+
reply(res, e.status || 500, e.message || e);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
} else {
|
|
170
|
+
return (req, res) => {
|
|
171
|
+
try {
|
|
172
|
+
return actualHandler(...args, as(req.body, type))(req, res);
|
|
173
|
+
} catch (e) {
|
|
174
|
+
reply(res, e.status || 500, e.message || e)
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
165
178
|
}
|
|
166
179
|
}
|
|
167
180
|
}
|
|
@@ -172,16 +185,16 @@ function PassBodyAs(type) {
|
|
|
172
185
|
if (isRequstHandlerArgs(args)) {
|
|
173
186
|
const req = args.at(-3); const res = args.at(-2);
|
|
174
187
|
try {
|
|
175
|
-
return actualHandler(
|
|
188
|
+
return actualHandler(asStrict(req.body, type))(req, res);
|
|
176
189
|
} catch (e) {
|
|
177
190
|
reply(res, e.status || 500, e.message || e);
|
|
178
191
|
}
|
|
179
192
|
|
|
180
193
|
} else {
|
|
181
194
|
return (req, res) => {
|
|
182
|
-
try{
|
|
183
|
-
return actualHandler(...args,
|
|
184
|
-
}catch(e){
|
|
195
|
+
try {
|
|
196
|
+
return actualHandler(...args, asStrict(req.body, type))(req, res);
|
|
197
|
+
} catch (e) {
|
|
185
198
|
reply(res, e.status || 500, e.message || e)
|
|
186
199
|
}
|
|
187
200
|
}
|
|
@@ -190,6 +203,28 @@ function PassBodyAs(type) {
|
|
|
190
203
|
}
|
|
191
204
|
}
|
|
192
205
|
|
|
206
|
+
function PassBody(actualHandler) {
|
|
207
|
+
return (...args) => {
|
|
208
|
+
if (isRequstHandlerArgs(args)) {
|
|
209
|
+
const req = args.at(-3); const res = args.at(-2);
|
|
210
|
+
try {
|
|
211
|
+
return actualHandler(req.body)(req, res);
|
|
212
|
+
} 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
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
193
228
|
function PassRequest(actualHandler) {
|
|
194
229
|
return (...args) => {
|
|
195
230
|
if (isRequstHandlerArgs(args)) {
|
|
@@ -249,6 +284,7 @@ module.exports = {
|
|
|
249
284
|
PassCookies,
|
|
250
285
|
PassBody,
|
|
251
286
|
PassBodyAs,
|
|
287
|
+
ParseBodyAs,
|
|
252
288
|
PassRequest,
|
|
253
289
|
PassResponse
|
|
254
290
|
}
|