@yamato-daiwa/express-extensions 1.0.1 → 1.1.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.
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { HttpError } from "routing-controllers";
|
|
2
|
+
import { RawObjectDataProcessor, type ReadonlyParsedJSON_Object } from "@yamato-daiwa/es-extensions";
|
|
3
|
+
declare abstract class QueryParametersProcessor {
|
|
4
|
+
private static defaultDeserializer;
|
|
5
|
+
static setDefaultDeserializer(deserializer: QueryParametersProcessor.Deserializer): void;
|
|
6
|
+
static process<QueryParameters extends ReadonlyParsedJSON_Object>(propertiesSpecification: RawObjectDataProcessor.PropertiesSpecification, deserializer?: QueryParametersProcessor.Deserializer): (object: object, method: string, index: number) => void;
|
|
7
|
+
}
|
|
8
|
+
declare namespace QueryParametersProcessor {
|
|
9
|
+
type Deserializer = (serializedQueryParameters: string) => ReadonlyParsedJSON_Object;
|
|
10
|
+
class QueryParametersDeserializingError extends HttpError {
|
|
11
|
+
static readonly NAME: string;
|
|
12
|
+
static localization: QueryParametersDeserializingError.Localization;
|
|
13
|
+
args: Array<unknown>;
|
|
14
|
+
constructor(argumentsForInternalLogging?: Array<unknown>);
|
|
15
|
+
toJSON(): Readonly<{
|
|
16
|
+
type: string;
|
|
17
|
+
title: string;
|
|
18
|
+
message: string;
|
|
19
|
+
status: number;
|
|
20
|
+
}>;
|
|
21
|
+
}
|
|
22
|
+
namespace QueryParametersDeserializingError {
|
|
23
|
+
type Localization = Readonly<{
|
|
24
|
+
defaultTitle: string;
|
|
25
|
+
description: string;
|
|
26
|
+
}>;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export default QueryParametersProcessor;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const routing_controllers_1 = require("routing-controllers");
|
|
7
|
+
const es_extensions_1 = require("@yamato-daiwa/es-extensions");
|
|
8
|
+
const qs_1 = __importDefault(require("qs"));
|
|
9
|
+
class QueryParametersProcessor {
|
|
10
|
+
static setDefaultDeserializer(deserializer) {
|
|
11
|
+
QueryParametersProcessor.defaultDeserializer = deserializer;
|
|
12
|
+
}
|
|
13
|
+
static process(propertiesSpecification, deserializer = QueryParametersProcessor.defaultDeserializer) {
|
|
14
|
+
return (0, routing_controllers_1.createParamDecorator)({
|
|
15
|
+
required: true,
|
|
16
|
+
value(action) {
|
|
17
|
+
const request = action.request;
|
|
18
|
+
let serializedQueryParameters;
|
|
19
|
+
try {
|
|
20
|
+
serializedQueryParameters = request.url.split("?")[1];
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
es_extensions_1.Logger.throwErrorAndLog({
|
|
24
|
+
errorInstance: new QueryParametersProcessor.QueryParametersDeserializingError(),
|
|
25
|
+
title: QueryParametersProcessor.QueryParametersDeserializingError.localization.defaultTitle,
|
|
26
|
+
occurrenceLocation: "QueryParametersProcessor.process(propertiesSpecification, deserializer)",
|
|
27
|
+
innerError: error
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
const deserializedQueryParameters = deserializer(serializedQueryParameters ?? "");
|
|
31
|
+
const processingResult = es_extensions_1.RawObjectDataProcessor.process(deserializedQueryParameters, {
|
|
32
|
+
nameForLogging: "DeserializedQueryParameters",
|
|
33
|
+
subtype: es_extensions_1.RawObjectDataProcessor.ObjectSubtypes.fixedKeyAndValuePairsObject,
|
|
34
|
+
properties: propertiesSpecification
|
|
35
|
+
});
|
|
36
|
+
if (processingResult.rawDataIsInvalid) {
|
|
37
|
+
throw new routing_controllers_1.BadRequestError(processingResult.validationErrorsMessages.join("\n"));
|
|
38
|
+
}
|
|
39
|
+
return processingResult.processedData;
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
QueryParametersProcessor.defaultDeserializer = qs_1.default.parse;
|
|
45
|
+
(function (QueryParametersProcessor) {
|
|
46
|
+
class QueryParametersDeserializingError extends routing_controllers_1.HttpError {
|
|
47
|
+
constructor(argumentsForInternalLogging = []) {
|
|
48
|
+
super(es_extensions_1.HTTP_StatusCodes.internalServerError);
|
|
49
|
+
Object.setPrototypeOf(this, QueryParametersDeserializingError.prototype);
|
|
50
|
+
this.name = QueryParametersDeserializingError.NAME;
|
|
51
|
+
this.message = QueryParametersDeserializingError.localization.description;
|
|
52
|
+
this.args = argumentsForInternalLogging;
|
|
53
|
+
}
|
|
54
|
+
toJSON() {
|
|
55
|
+
return {
|
|
56
|
+
type: QueryParametersDeserializingError.NAME,
|
|
57
|
+
title: QueryParametersDeserializingError.localization.defaultTitle,
|
|
58
|
+
message: this.message,
|
|
59
|
+
status: this.httpCode
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
QueryParametersDeserializingError.NAME = "QueryParametersDeserializingError";
|
|
64
|
+
QueryParametersDeserializingError.localization = {
|
|
65
|
+
defaultTitle: "Query Parameters Deserializing Failed",
|
|
66
|
+
description: "The error has occurred during deserializing of query parameters"
|
|
67
|
+
};
|
|
68
|
+
QueryParametersProcessor.QueryParametersDeserializingError = QueryParametersDeserializingError;
|
|
69
|
+
})(QueryParametersProcessor || (QueryParametersProcessor = {}));
|
|
70
|
+
exports.default = QueryParametersProcessor;
|
package/README.md
CHANGED
|
@@ -12,16 +12,17 @@ npm i @yamato-daiwa/express-extensions -E
|
|
|
12
12
|
|
|
13
13
|
Also, install the following peer dependencies if not installed yet.
|
|
14
14
|
|
|
15
|
-
+ **body-parser**: ~
|
|
16
|
-
+ **express**: ~
|
|
15
|
+
+ **body-parser**: ~2.2.0
|
|
16
|
+
+ **express**: ~5.1.0
|
|
17
17
|
+ **express-session**: ~1.18.0
|
|
18
|
-
+ **routing-controllers**: ~0.
|
|
18
|
+
+ **routing-controllers**: ~0.11.0
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
## Functionality
|
|
22
22
|
|
|
23
23
|
+ [`ExpressMiddleware`](#expressmiddleware)
|
|
24
|
-
+ [`Route`]()
|
|
24
|
+
+ [`Route`](#route)
|
|
25
|
+
+ [`QueryParametersProcessor`](#queryparametersprocessor)
|
|
25
26
|
|
|
26
27
|
+ Session
|
|
27
28
|
|
|
@@ -98,6 +99,142 @@ The adapter for `Method` decorator from **routing-controllers** to `HTTP_Methods
|
|
|
98
99
|
["fundamental-constants"](https://www.npmjs.com/package/fundamental-constants)/["@yamato-daiwa/es-extensions"](https://www.npmjs.com/package/@yamato-daiwa/es-extensions).
|
|
99
100
|
|
|
100
101
|
|
|
102
|
+
### `QueryParametersProcessor`
|
|
103
|
+
|
|
104
|
+
`@QueryParametersProcessor.process` is the alternative
|
|
105
|
+
[`@QueryParams`](https://github.com/typestack/routing-controllers?tab=readme-ov-file#inject-query-parameters) from
|
|
106
|
+
**routing-controllers**.
|
|
107
|
+
In comparison with `@QueryParams`,
|
|
108
|
+
|
|
109
|
+
+ Has customizable deserialization (does not use pre-deserialized `request.query`)
|
|
110
|
+
+ Validation without additional classes via [`RawObjectDataProcessor` API](https://github.com/TokugawaTakeshi/Yamato-Daiwa-ES-Extensions/blob/master/CoreLibrary/Package/Documentation/RawObjectDataProcessor/RawObjectDataProcessor.md);
|
|
111
|
+
+ Possible to modify object to which query string has been deserialized as far as `RawObjectDataProcessor` supports.
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
import { Controller, Get, Reader } from "routing-controllers";
|
|
116
|
+
import { RawObjectDataProcessor } from "@yamato-daiwa/es-extensions";
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
@Controller()
|
|
120
|
+
export default class UserController {
|
|
121
|
+
|
|
122
|
+
@Get("/componets/users/edtior/new")
|
|
123
|
+
@Reader("Components/UserEditor/ForNewUser/EditorForNewUser.mvc.component.hbs")
|
|
124
|
+
protected async renderUserEditorFragmentForNewUser(
|
|
125
|
+
@QueryParametersProcessor.process({
|
|
126
|
+
userType: {
|
|
127
|
+
type: String,
|
|
128
|
+
required: true,
|
|
129
|
+
allowedAlternatives: Object.values(User.Types)
|
|
130
|
+
},
|
|
131
|
+
willBeFirstAmongOnesOfSameType: {
|
|
132
|
+
preValidationModifications: [ destringifyBooleanValue ],
|
|
133
|
+
type: Boolean,
|
|
134
|
+
required: true
|
|
135
|
+
},
|
|
136
|
+
currentContOfUsersOfSameType: {
|
|
137
|
+
preValidationModifications: [ convertPotentialStringToIntegerIfPossible ],
|
|
138
|
+
type: Number,
|
|
139
|
+
numbersSet: RawObjectDataProcessor.NumbersSets.naturalNumber,
|
|
140
|
+
required: true
|
|
141
|
+
}
|
|
142
|
+
})
|
|
143
|
+
{
|
|
144
|
+
userType,
|
|
145
|
+
willBeFirstAmongOnesOfSameType,
|
|
146
|
+
currentContOfUsersOfSameType
|
|
147
|
+
}: Readonly<{
|
|
148
|
+
|
|
149
|
+
}>
|
|
150
|
+
): Promise<EditorForNewUserMVC_FragmentVariables> {
|
|
151
|
+
|
|
152
|
+
console.log(userType)
|
|
153
|
+
console.log(willBeFirstAmongOnesOfSameType)
|
|
154
|
+
console.log(currentContOfUsersOfSameType)
|
|
155
|
+
|
|
156
|
+
// ...
|
|
157
|
+
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
#### Query parameters deserializing
|
|
164
|
+
|
|
165
|
+
To set the default deserializer, use `setDefaultDeserializer` static method of `QueryParametersProcessor`.
|
|
166
|
+
|
|
167
|
+
```
|
|
168
|
+
import { QueryParametersProcessor } from "@yamato-daiwa/express-extensions";
|
|
169
|
+
import QueryString from "qs";
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
QueryParametersProcessor.setDefaultDeserializer(QueryString.parse)
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
The parameter must be the functions accepts the serialized (thus the string) query parameters and return
|
|
176
|
+
JSON-compatible type.
|
|
177
|
+
|
|
178
|
+
Obvious but frequently missed out: the deserializing algorithm on backend must correspond to serializing
|
|
179
|
+
algorithm on frontend.
|
|
180
|
+
Well, the `QueryString.parse()` is predefined deserializer for `QueryParametersProcessor` but even you are
|
|
181
|
+
fine with this one, add above code to your application to explicitly show that you selected this serializer
|
|
182
|
+
consciously.
|
|
183
|
+
|
|
184
|
+
Additionally, you can set the custom deserializer per request what is basically not recommended but inevitably
|
|
185
|
+
if development of the client part is out your control.
|
|
186
|
+
|
|
187
|
+
```typescript
|
|
188
|
+
import { Controller, Get, Reader } from "routing-controllers";
|
|
189
|
+
import { RawObjectDataProcessor, type ReadonlyParsedJSON_Object } from "@yamato-daiwa/es-extensions";
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
@Controller()
|
|
193
|
+
export default class UserController {
|
|
194
|
+
|
|
195
|
+
@Get("/componets/users/edtior/new")
|
|
196
|
+
@Reader("Components/UserEditor/ForNewUser/EditorForNewUser.mvc.component.hbs")
|
|
197
|
+
protected async renderUserEditorFragmentForNewUser(
|
|
198
|
+
@QueryParametersProcessor.process(
|
|
199
|
+
{
|
|
200
|
+
userType: {
|
|
201
|
+
type: String,
|
|
202
|
+
required: true,
|
|
203
|
+
allowedAlternatives: Object.values(User.Types)
|
|
204
|
+
},
|
|
205
|
+
// ...
|
|
206
|
+
},
|
|
207
|
+
(queryString: string): ReadonlyParsedJSON_Object => {
|
|
208
|
+
// ....
|
|
209
|
+
}
|
|
210
|
+
)
|
|
211
|
+
{
|
|
212
|
+
userType,
|
|
213
|
+
willBeFirstAmongOnesOfSameType,
|
|
214
|
+
currentContOfUsersOfSameType
|
|
215
|
+
}: Readonly<{
|
|
216
|
+
|
|
217
|
+
}>
|
|
218
|
+
): Promise<EditorForNewUserMVC_FragmentVariables> {
|
|
219
|
+
|
|
220
|
+
console.log(userType)
|
|
221
|
+
console.log(willBeFirstAmongOnesOfSameType)
|
|
222
|
+
console.log(currentContOfUsersOfSameType)
|
|
223
|
+
|
|
224
|
+
// ...
|
|
225
|
+
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
#### `QueryParametersDeserializingError`
|
|
232
|
+
|
|
233
|
+
If error will occur during query parameters deserializing, the `QueryParametersProcessor.QueryParametersDeserializingError`
|
|
234
|
+
will be thrown.
|
|
235
|
+
You can detect it via `error instanceof QueryParametersProcessor.QueryParametersDeserializingError`
|
|
236
|
+
or `error instanceof HttpError` because `QueryParametersProcessor.QueryParametersDeserializingError`
|
|
237
|
+
extended from `HttpError` of **routing-controllers**.
|
|
101
238
|
|
|
102
239
|
|
|
103
240
|
### Session
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yamato-daiwa/express-extensions",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Additional functionality for Express.js and also \"routing-controllers\" aimed to reduce the routine code.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"nodejs",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"Distributable"
|
|
27
27
|
],
|
|
28
28
|
"engines": {
|
|
29
|
-
"node": ">=
|
|
29
|
+
"node": ">=20.0.0"
|
|
30
30
|
},
|
|
31
31
|
"peerDependencies": {
|
|
32
32
|
"body-parser": "~2.2.0",
|
|
@@ -35,12 +35,13 @@
|
|
|
35
35
|
"routing-controllers": "~0.11.0"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@yamato-daiwa/es-extensions": "1.
|
|
39
|
-
"fundamental-constants": "0.
|
|
38
|
+
"@yamato-daiwa/es-extensions": "1.8.0-alpha.13",
|
|
39
|
+
"fundamental-constants": "0.8.0",
|
|
40
|
+
"qs": "6.14.0"
|
|
40
41
|
},
|
|
41
42
|
"devDependencies": {
|
|
42
43
|
"@types/express-session": "1.18.1",
|
|
43
|
-
"@yamato-daiwa/style_guides": "0.6.
|
|
44
|
+
"@yamato-daiwa/style_guides": "0.6.8",
|
|
44
45
|
"body-parser": "2.2.0",
|
|
45
46
|
"express": "5.1.0",
|
|
46
47
|
"express-session": "1.18.1",
|