@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**: ~1.20.0
16
- + **express**: ~4.21.0
15
+ + **body-parser**: ~2.2.0
16
+ + **express**: ~5.1.0
17
17
  + **express-session**: ~1.18.0
18
- + **routing-controllers**: ~0.10.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.1",
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": ">=18.0.0"
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.7.2",
39
- "fundamental-constants": "0.7.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.5",
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",