@tsed/platform-response-filter 8.3.6 → 8.4.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/lib/esm/index.js +0 -1
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/services/PlatformResponseFilter.js +51 -27
- package/lib/esm/services/PlatformResponseFilter.js.map +1 -1
- package/lib/types/index.d.ts +0 -1
- package/lib/types/services/PlatformResponseFilter.d.ts +17 -6
- package/package.json +13 -13
- package/src/index.ts +0 -1
- package/src/services/PlatformResponseFilter.spec.ts +63 -15
- package/src/services/PlatformResponseFilter.ts +59 -31
- package/lib/esm/utils/renderView.js +0 -13
- package/lib/esm/utils/renderView.js.map +0 -1
- package/lib/types/utils/renderView.d.ts +0 -2
- package/src/utils/renderView.spec.ts +0 -64
- package/src/utils/renderView.ts +0 -15
package/lib/esm/index.js
CHANGED
|
@@ -9,5 +9,4 @@ export * from "./interfaces/ResponseFilterMethods.js";
|
|
|
9
9
|
export * from "./services/PlatformContentTypeResolver.js";
|
|
10
10
|
export * from "./services/PlatformContentTypesContainer.js";
|
|
11
11
|
export * from "./services/PlatformResponseFilter.js";
|
|
12
|
-
export * from "./utils/renderView.js";
|
|
13
12
|
//# sourceMappingURL=index.js.map
|
package/lib/esm/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,cAAc,iCAAiC,CAAC;AAChD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,sCAAsC,CAAC;AACrD,cAAc,iCAAiC,CAAC;AAChD,cAAc,uCAAuC,CAAC;AACtD,cAAc,2CAA2C,CAAC;AAC1D,cAAc,6CAA6C,CAAC;AAC5D,cAAc,sCAAsC,CAAC
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,cAAc,iCAAiC,CAAC;AAChD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,sCAAsC,CAAC;AACrD,cAAc,iCAAiC,CAAC;AAChD,cAAc,uCAAuC,CAAC;AACtD,cAAc,2CAA2C,CAAC;AAC1D,cAAc,6CAA6C,CAAC;AAC5D,cAAc,sCAAsC,CAAC"}
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { isSerializable } from "@tsed/core";
|
|
2
2
|
import { constant, inject, injectable } from "@tsed/di";
|
|
3
3
|
import { serialize } from "@tsed/json-mapper";
|
|
4
|
-
import {
|
|
4
|
+
import { TemplateRenderError } from "../errors/TemplateRenderError.js";
|
|
5
5
|
import { PLATFORM_CONTENT_TYPE_RESOLVER } from "./PlatformContentTypeResolver.js";
|
|
6
6
|
import { PLATFORM_CONTENT_TYPES_CONTAINER } from "./PlatformContentTypesContainer.js";
|
|
7
7
|
/**
|
|
8
|
+
* PlatformResponseFilter is responsible for transforming the response data
|
|
9
|
+
* to the appropriate format based on the endpoint metadata and context.
|
|
10
|
+
*
|
|
8
11
|
* @platform
|
|
9
12
|
*/
|
|
10
13
|
export class PlatformResponseFilter {
|
|
@@ -14,21 +17,37 @@ export class PlatformResponseFilter {
|
|
|
14
17
|
this.contentTypeResolver = inject(PLATFORM_CONTENT_TYPE_RESOLVER);
|
|
15
18
|
}
|
|
16
19
|
/**
|
|
17
|
-
*
|
|
18
|
-
* @param data
|
|
19
|
-
* @param ctx
|
|
20
|
+
* Transform the data to the right format.
|
|
21
|
+
* @param data The data to transform.
|
|
22
|
+
* @param $ctx The context.
|
|
20
23
|
*/
|
|
21
|
-
transform(data, ctx) {
|
|
22
|
-
const {
|
|
23
|
-
if (
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
if (
|
|
28
|
-
|
|
24
|
+
async transform(data, $ctx) {
|
|
25
|
+
const { endpoint } = $ctx;
|
|
26
|
+
if (endpoint) {
|
|
27
|
+
if (endpoint.view) {
|
|
28
|
+
data = await this.renderView(data, $ctx);
|
|
29
|
+
}
|
|
30
|
+
else if (isSerializable(data)) {
|
|
31
|
+
data = await this.serialize(data, $ctx);
|
|
29
32
|
}
|
|
30
33
|
}
|
|
31
|
-
return data;
|
|
34
|
+
return this.resolve(data, $ctx);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Render the view with the given data.
|
|
38
|
+
* @param data The data to render.
|
|
39
|
+
* @param $ctx The context.
|
|
40
|
+
* @protected
|
|
41
|
+
*/
|
|
42
|
+
async renderView(data, $ctx) {
|
|
43
|
+
const { response, endpoint } = $ctx;
|
|
44
|
+
try {
|
|
45
|
+
const { path, options } = endpoint.view;
|
|
46
|
+
return await response.render(path, { ...options, ...data, $ctx });
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
throw new TemplateRenderError(endpoint.targetName, endpoint.propertyKey, err);
|
|
50
|
+
}
|
|
32
51
|
}
|
|
33
52
|
/**
|
|
34
53
|
* Serialize data before calling filters
|
|
@@ -37,20 +56,25 @@ export class PlatformResponseFilter {
|
|
|
37
56
|
*/
|
|
38
57
|
async serialize(data, ctx) {
|
|
39
58
|
const { response, endpoint } = ctx;
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
59
|
+
const responseOpts = endpoint.getResponseOptions(response.statusCode, {
|
|
60
|
+
includes: this.getIncludes(ctx)
|
|
61
|
+
});
|
|
62
|
+
data = serialize(data, {
|
|
63
|
+
useAlias: true,
|
|
64
|
+
additionalProperties: this.additionalProperties,
|
|
65
|
+
...responseOpts,
|
|
66
|
+
endpoint: true
|
|
67
|
+
});
|
|
68
|
+
return data;
|
|
69
|
+
}
|
|
70
|
+
resolve(data, ctx) {
|
|
71
|
+
const { response } = ctx;
|
|
72
|
+
if (ctx.endpoint?.operation) {
|
|
73
|
+
const bestContentType = this.contentTypeResolver(data, ctx);
|
|
74
|
+
bestContentType && response.contentType(bestContentType);
|
|
75
|
+
const resolved = this.container.resolve(bestContentType);
|
|
76
|
+
if (resolved) {
|
|
77
|
+
return resolved.transform(data, ctx);
|
|
54
78
|
}
|
|
55
79
|
}
|
|
56
80
|
return data;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PlatformResponseFilter.js","sourceRoot":"","sources":["../../../src/services/PlatformResponseFilter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,cAAc,EAAC,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAc,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAC,MAAM,UAAU,CAAC;AACnE,OAAO,EAAC,SAAS,EAAC,MAAM,mBAAmB,CAAC;AAE5C,OAAO,EAAC,
|
|
1
|
+
{"version":3,"file":"PlatformResponseFilter.js","sourceRoot":"","sources":["../../../src/services/PlatformResponseFilter.ts"],"names":[],"mappings":"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"}
|
package/lib/types/index.d.ts
CHANGED
|
@@ -9,4 +9,3 @@ export * from "./interfaces/ResponseFilterMethods.js";
|
|
|
9
9
|
export * from "./services/PlatformContentTypeResolver.js";
|
|
10
10
|
export * from "./services/PlatformContentTypesContainer.js";
|
|
11
11
|
export * from "./services/PlatformResponseFilter.js";
|
|
12
|
-
export * from "./utils/renderView.js";
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { BaseContext } from "@tsed/di";
|
|
2
2
|
/**
|
|
3
|
+
* PlatformResponseFilter is responsible for transforming the response data
|
|
4
|
+
* to the appropriate format based on the endpoint metadata and context.
|
|
5
|
+
*
|
|
3
6
|
* @platform
|
|
4
7
|
*/
|
|
5
8
|
export declare class PlatformResponseFilter {
|
|
@@ -10,16 +13,24 @@ export declare class PlatformResponseFilter {
|
|
|
10
13
|
};
|
|
11
14
|
protected contentTypeResolver: (data: any, ctx: BaseContext) => any;
|
|
12
15
|
/**
|
|
13
|
-
*
|
|
14
|
-
* @param data
|
|
15
|
-
* @param ctx
|
|
16
|
+
* Transform the data to the right format.
|
|
17
|
+
* @param data The data to transform.
|
|
18
|
+
* @param $ctx The context.
|
|
19
|
+
*/
|
|
20
|
+
transform(data: unknown, $ctx: BaseContext): Promise<unknown>;
|
|
21
|
+
/**
|
|
22
|
+
* Render the view with the given data.
|
|
23
|
+
* @param data The data to render.
|
|
24
|
+
* @param $ctx The context.
|
|
25
|
+
* @protected
|
|
16
26
|
*/
|
|
17
|
-
|
|
27
|
+
protected renderView(data: unknown, $ctx: BaseContext): Promise<any>;
|
|
18
28
|
/**
|
|
19
29
|
* Serialize data before calling filters
|
|
20
30
|
* @param data
|
|
21
31
|
* @param ctx
|
|
22
32
|
*/
|
|
23
|
-
serialize(data: unknown, ctx: BaseContext): Promise<unknown>;
|
|
24
|
-
|
|
33
|
+
protected serialize(data: unknown, ctx: BaseContext): Promise<unknown>;
|
|
34
|
+
protected resolve(data: any, ctx: BaseContext): any;
|
|
35
|
+
protected getIncludes(ctx: BaseContext): string[] | undefined;
|
|
25
36
|
}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@tsed/platform-response-filter",
|
|
3
3
|
"description": "Platform response filter module for Ts.ED Framework",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"version": "8.
|
|
5
|
+
"version": "8.4.0",
|
|
6
6
|
"source": "./src/index.ts",
|
|
7
7
|
"main": "./lib/esm/index.js",
|
|
8
8
|
"module": "./lib/esm/index.js",
|
|
@@ -26,23 +26,23 @@
|
|
|
26
26
|
"tslib": "2.7.0"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
|
-
"@tsed/barrels": "8.
|
|
30
|
-
"@tsed/core": "8.
|
|
31
|
-
"@tsed/di": "8.
|
|
32
|
-
"@tsed/exceptions": "8.
|
|
33
|
-
"@tsed/json-mapper": "8.
|
|
34
|
-
"@tsed/schema": "8.
|
|
35
|
-
"@tsed/typescript": "8.
|
|
29
|
+
"@tsed/barrels": "8.4.0",
|
|
30
|
+
"@tsed/core": "8.4.0",
|
|
31
|
+
"@tsed/di": "8.4.0",
|
|
32
|
+
"@tsed/exceptions": "8.4.0",
|
|
33
|
+
"@tsed/json-mapper": "8.4.0",
|
|
34
|
+
"@tsed/schema": "8.4.0",
|
|
35
|
+
"@tsed/typescript": "8.4.0",
|
|
36
36
|
"eslint": "9.12.0",
|
|
37
37
|
"typescript": "5.4.5",
|
|
38
38
|
"vitest": "2.1.2"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
41
|
-
"@tsed/core": "8.
|
|
42
|
-
"@tsed/di": "8.
|
|
43
|
-
"@tsed/exceptions": "8.
|
|
44
|
-
"@tsed/json-mapper": "8.
|
|
45
|
-
"@tsed/schema": "8.
|
|
41
|
+
"@tsed/core": "8.4.0",
|
|
42
|
+
"@tsed/di": "8.4.0",
|
|
43
|
+
"@tsed/exceptions": "8.4.0",
|
|
44
|
+
"@tsed/json-mapper": "8.4.0",
|
|
45
|
+
"@tsed/schema": "8.4.0"
|
|
46
46
|
},
|
|
47
47
|
"peerDependenciesMeta": {
|
|
48
48
|
"@tsed/core": {
|
package/src/index.ts
CHANGED
|
@@ -9,4 +9,3 @@ export * from "./interfaces/ResponseFilterMethods.js";
|
|
|
9
9
|
export * from "./services/PlatformContentTypeResolver.js";
|
|
10
10
|
export * from "./services/PlatformContentTypesContainer.js";
|
|
11
11
|
export * from "./services/PlatformResponseFilter.js";
|
|
12
|
-
export * from "./utils/renderView.js";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {catchAsyncError} from "@tsed/core";
|
|
2
2
|
import {PlatformTest} from "@tsed/platform-http/testing";
|
|
3
3
|
import {Context} from "@tsed/platform-params";
|
|
4
|
-
import {EndpointMetadata, Get, Returns, View} from "@tsed/schema";
|
|
4
|
+
import {EndpointMetadata, Get, Ignore, Property, Returns, View} from "@tsed/schema";
|
|
5
5
|
|
|
6
6
|
import {ResponseFilter} from "../decorators/responseFilter.js";
|
|
7
7
|
import {ResponseFilterMethods} from "../interfaces/ResponseFilterMethods.js";
|
|
@@ -29,7 +29,7 @@ class AllFilter implements ResponseFilterMethods {
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
describe("PlatformResponseFilter", () => {
|
|
32
|
-
describe("transform()", () => {
|
|
32
|
+
describe("transform() with registered filters", () => {
|
|
33
33
|
describe("when filter list is given", () => {
|
|
34
34
|
beforeEach(() =>
|
|
35
35
|
PlatformTest.create({
|
|
@@ -164,7 +164,6 @@ describe("PlatformResponseFilter", () => {
|
|
|
164
164
|
});
|
|
165
165
|
});
|
|
166
166
|
});
|
|
167
|
-
|
|
168
167
|
describe("when filter list is not given", () => {
|
|
169
168
|
beforeEach(() =>
|
|
170
169
|
PlatformTest.create({
|
|
@@ -228,18 +227,14 @@ describe("PlatformResponseFilter", () => {
|
|
|
228
227
|
});
|
|
229
228
|
});
|
|
230
229
|
});
|
|
231
|
-
describe("
|
|
232
|
-
beforeEach(() =>
|
|
233
|
-
PlatformTest.create({
|
|
234
|
-
responseFilters: [CustomJsonFilter, AllFilter, ApplicationJsonFilter]
|
|
235
|
-
})
|
|
236
|
-
);
|
|
230
|
+
describe("transform() without registered filters", () => {
|
|
231
|
+
beforeEach(() => PlatformTest.create());
|
|
237
232
|
afterEach(() => PlatformTest.reset());
|
|
238
233
|
it("should transform value", async () => {
|
|
239
234
|
const platformResponseFilter = PlatformTest.get<PlatformResponseFilter>(PlatformResponseFilter);
|
|
240
235
|
const ctx = PlatformTest.createRequestContext();
|
|
241
236
|
|
|
242
|
-
const result = await platformResponseFilter.
|
|
237
|
+
const result = await platformResponseFilter.transform({test: "test"}, ctx);
|
|
243
238
|
|
|
244
239
|
expect(result).toEqual({test: "test"});
|
|
245
240
|
});
|
|
@@ -255,7 +250,7 @@ describe("PlatformResponseFilter", () => {
|
|
|
255
250
|
|
|
256
251
|
vi.spyOn(ctx.endpoint, "getResponseOptions");
|
|
257
252
|
|
|
258
|
-
const result = await platformResponseFilter.
|
|
253
|
+
const result = await platformResponseFilter.transform({test: "test"}, ctx);
|
|
259
254
|
|
|
260
255
|
expect(result).toEqual({test: "test"});
|
|
261
256
|
expect(ctx.endpoint.getResponseOptions).toHaveBeenCalledWith(200, {includes: undefined});
|
|
@@ -274,7 +269,7 @@ describe("PlatformResponseFilter", () => {
|
|
|
274
269
|
|
|
275
270
|
ctx.request.query.includes = [];
|
|
276
271
|
|
|
277
|
-
const result = await platformResponseFilter.
|
|
272
|
+
const result = await platformResponseFilter.transform({test: "test"}, ctx);
|
|
278
273
|
|
|
279
274
|
expect(result).toEqual({test: "test"});
|
|
280
275
|
expect(ctx.endpoint.getResponseOptions).toHaveBeenCalledWith(200, {includes: []});
|
|
@@ -293,7 +288,7 @@ describe("PlatformResponseFilter", () => {
|
|
|
293
288
|
|
|
294
289
|
ctx.request.query.includes = ["test,test2"];
|
|
295
290
|
|
|
296
|
-
const result = await platformResponseFilter.
|
|
291
|
+
const result = await platformResponseFilter.transform({test: "test"}, ctx);
|
|
297
292
|
|
|
298
293
|
expect(result).toEqual({test: "test"});
|
|
299
294
|
expect(ctx.endpoint.getResponseOptions).toHaveBeenCalledWith(200, {
|
|
@@ -312,7 +307,7 @@ describe("PlatformResponseFilter", () => {
|
|
|
312
307
|
ctx.endpoint = EndpointMetadata.get(Test, "test");
|
|
313
308
|
vi.spyOn(ctx.response, "render").mockResolvedValue("template");
|
|
314
309
|
|
|
315
|
-
const result = await platformResponseFilter.
|
|
310
|
+
const result = await platformResponseFilter.transform({test: "test"}, ctx);
|
|
316
311
|
|
|
317
312
|
expect(result).toEqual("template");
|
|
318
313
|
});
|
|
@@ -328,9 +323,62 @@ describe("PlatformResponseFilter", () => {
|
|
|
328
323
|
ctx.endpoint = EndpointMetadata.get(Test, "test");
|
|
329
324
|
vi.spyOn(ctx.response, "render").mockRejectedValue(new Error("parsing error"));
|
|
330
325
|
|
|
331
|
-
const result = await catchAsyncError(() => platformResponseFilter.
|
|
326
|
+
const result = await catchAsyncError(() => platformResponseFilter.transform({test: "test"}, ctx));
|
|
332
327
|
|
|
333
328
|
expect(result?.message).toEqual("Template rendering error: Test.test()\nError: parsing error");
|
|
334
329
|
});
|
|
330
|
+
|
|
331
|
+
it("should render content", async () => {
|
|
332
|
+
class Model {
|
|
333
|
+
@Property()
|
|
334
|
+
data: string;
|
|
335
|
+
|
|
336
|
+
@Ignore()
|
|
337
|
+
test: string;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
class Test {
|
|
341
|
+
@Get("/")
|
|
342
|
+
@View("view", {options: "options"})
|
|
343
|
+
@Returns(200, Model)
|
|
344
|
+
test() {}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
const platformResponseFilter = PlatformTest.get<PlatformResponseFilter>(PlatformResponseFilter);
|
|
348
|
+
const ctx = PlatformTest.createRequestContext();
|
|
349
|
+
|
|
350
|
+
ctx.endpoint = EndpointMetadata.get(Test, "test");
|
|
351
|
+
|
|
352
|
+
vi.spyOn(ctx.response, "render").mockResolvedValue("HTML");
|
|
353
|
+
|
|
354
|
+
ctx.data = {data: "data"};
|
|
355
|
+
|
|
356
|
+
await platformResponseFilter.transform(ctx.data, ctx);
|
|
357
|
+
|
|
358
|
+
expect(ctx.response.render).toHaveBeenCalledWith("view", {
|
|
359
|
+
$ctx: ctx,
|
|
360
|
+
data: "data",
|
|
361
|
+
options: "options"
|
|
362
|
+
});
|
|
363
|
+
});
|
|
364
|
+
it("should render content and throw an error", async () => {
|
|
365
|
+
class Test {
|
|
366
|
+
@Get("/")
|
|
367
|
+
@View("view", {options: "options"})
|
|
368
|
+
test() {}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
const platformResponseFilter = PlatformTest.get<PlatformResponseFilter>(PlatformResponseFilter);
|
|
372
|
+
const ctx = PlatformTest.createRequestContext();
|
|
373
|
+
ctx.endpoint = EndpointMetadata.get(Test, "test");
|
|
374
|
+
|
|
375
|
+
vi.spyOn(ctx.response, "render").mockRejectedValue(new Error("parser error"));
|
|
376
|
+
|
|
377
|
+
ctx.data = {data: "data"};
|
|
378
|
+
|
|
379
|
+
let actualError: any = await catchAsyncError(() => platformResponseFilter.transform(ctx.data, ctx));
|
|
380
|
+
|
|
381
|
+
expect(actualError.message).toEqual("Template rendering error: Test.test()\nError: parser error");
|
|
382
|
+
});
|
|
335
383
|
});
|
|
336
384
|
});
|
|
@@ -2,11 +2,14 @@ import {isSerializable} from "@tsed/core";
|
|
|
2
2
|
import {BaseContext, constant, inject, injectable} from "@tsed/di";
|
|
3
3
|
import {serialize} from "@tsed/json-mapper";
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import {TemplateRenderError} from "../errors/TemplateRenderError.js";
|
|
6
6
|
import {PLATFORM_CONTENT_TYPE_RESOLVER} from "./PlatformContentTypeResolver.js";
|
|
7
7
|
import {PLATFORM_CONTENT_TYPES_CONTAINER} from "./PlatformContentTypesContainer.js";
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
+
* PlatformResponseFilter is responsible for transforming the response data
|
|
11
|
+
* to the appropriate format based on the endpoint metadata and context.
|
|
12
|
+
*
|
|
10
13
|
* @platform
|
|
11
14
|
*/
|
|
12
15
|
export class PlatformResponseFilter {
|
|
@@ -15,26 +18,39 @@ export class PlatformResponseFilter {
|
|
|
15
18
|
protected contentTypeResolver = inject<PLATFORM_CONTENT_TYPE_RESOLVER>(PLATFORM_CONTENT_TYPE_RESOLVER);
|
|
16
19
|
|
|
17
20
|
/**
|
|
18
|
-
*
|
|
19
|
-
* @param data
|
|
20
|
-
* @param ctx
|
|
21
|
+
* Transform the data to the right format.
|
|
22
|
+
* @param data The data to transform.
|
|
23
|
+
* @param $ctx The context.
|
|
21
24
|
*/
|
|
22
|
-
transform(data: unknown, ctx: BaseContext) {
|
|
23
|
-
const {
|
|
25
|
+
async transform(data: unknown, $ctx: BaseContext): Promise<unknown> {
|
|
26
|
+
const {endpoint} = $ctx;
|
|
24
27
|
|
|
25
|
-
if (
|
|
26
|
-
|
|
28
|
+
if (endpoint) {
|
|
29
|
+
if (endpoint.view) {
|
|
30
|
+
data = await this.renderView(data, $ctx);
|
|
31
|
+
} else if (isSerializable(data)) {
|
|
32
|
+
data = await this.serialize(data, $ctx);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
27
35
|
|
|
28
|
-
|
|
36
|
+
return this.resolve(data, $ctx);
|
|
37
|
+
}
|
|
29
38
|
|
|
30
|
-
|
|
39
|
+
/**
|
|
40
|
+
* Render the view with the given data.
|
|
41
|
+
* @param data The data to render.
|
|
42
|
+
* @param $ctx The context.
|
|
43
|
+
* @protected
|
|
44
|
+
*/
|
|
45
|
+
protected async renderView(data: unknown, $ctx: BaseContext) {
|
|
46
|
+
const {response, endpoint} = $ctx;
|
|
47
|
+
try {
|
|
48
|
+
const {path, options} = endpoint.view;
|
|
31
49
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
50
|
+
return await response.render(path, {...options, ...(data as object), $ctx});
|
|
51
|
+
} catch (err) {
|
|
52
|
+
throw new TemplateRenderError(endpoint.targetName, endpoint.propertyKey, err);
|
|
35
53
|
}
|
|
36
|
-
|
|
37
|
-
return data;
|
|
38
54
|
}
|
|
39
55
|
|
|
40
56
|
/**
|
|
@@ -42,30 +58,42 @@ export class PlatformResponseFilter {
|
|
|
42
58
|
* @param data
|
|
43
59
|
* @param ctx
|
|
44
60
|
*/
|
|
45
|
-
async serialize(data: unknown, ctx: BaseContext) {
|
|
61
|
+
protected async serialize(data: unknown, ctx: BaseContext) {
|
|
46
62
|
const {response, endpoint} = ctx;
|
|
47
63
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
64
|
+
const responseOpts = endpoint.getResponseOptions(response.statusCode, {
|
|
65
|
+
includes: this.getIncludes(ctx)
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
data = serialize(data, {
|
|
69
|
+
useAlias: true,
|
|
70
|
+
additionalProperties: this.additionalProperties,
|
|
71
|
+
...responseOpts,
|
|
72
|
+
endpoint: true
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
return data;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
protected resolve(data: any, ctx: BaseContext) {
|
|
79
|
+
const {response} = ctx;
|
|
80
|
+
|
|
81
|
+
if (ctx.endpoint?.operation) {
|
|
82
|
+
const bestContentType = this.contentTypeResolver(data, ctx);
|
|
83
|
+
|
|
84
|
+
bestContentType && response.contentType(bestContentType);
|
|
85
|
+
|
|
86
|
+
const resolved = this.container.resolve(bestContentType);
|
|
87
|
+
|
|
88
|
+
if (resolved) {
|
|
89
|
+
return resolved.transform(data, ctx);
|
|
62
90
|
}
|
|
63
91
|
}
|
|
64
92
|
|
|
65
93
|
return data;
|
|
66
94
|
}
|
|
67
95
|
|
|
68
|
-
|
|
96
|
+
protected getIncludes(ctx: BaseContext) {
|
|
69
97
|
if (ctx.request.query.includes) {
|
|
70
98
|
return [].concat(ctx.request.query.includes).flatMap((include: string) => include.split(","));
|
|
71
99
|
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { TemplateRenderError } from "../errors/TemplateRenderError.js";
|
|
2
|
-
export async function renderView(data, $ctx) {
|
|
3
|
-
const { response, endpoint } = $ctx;
|
|
4
|
-
try {
|
|
5
|
-
const { data } = $ctx;
|
|
6
|
-
const { path, options } = endpoint.view;
|
|
7
|
-
return await response.render(path, { ...options, ...data, $ctx });
|
|
8
|
-
}
|
|
9
|
-
catch (err) {
|
|
10
|
-
throw new TemplateRenderError(endpoint.targetName, endpoint.propertyKey, err);
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
//# sourceMappingURL=renderView.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"renderView.js","sourceRoot":"","sources":["../../../src/utils/renderView.ts"],"names":[],"mappings":"AAEA,OAAO,EAAC,mBAAmB,EAAC,MAAM,kCAAkC,CAAC;AAErE,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,IAAS,EAAE,IAAiB;IAC3D,MAAM,EAAC,QAAQ,EAAE,QAAQ,EAAC,GAAG,IAAI,CAAC;IAClC,IAAI,CAAC;QACH,MAAM,EAAC,IAAI,EAAC,GAAG,IAAI,CAAC;QACpB,MAAM,EAAC,IAAI,EAAE,OAAO,EAAC,GAAG,QAAQ,CAAC,IAAI,CAAC;QAEtC,OAAO,MAAM,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,EAAC,GAAG,OAAO,EAAE,GAAG,IAAI,EAAE,IAAI,EAAC,CAAC,CAAC;IAClE,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,mBAAmB,CAAC,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC;IAChF,CAAC;AACH,CAAC"}
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import {PlatformTest} from "@tsed/platform-http/testing";
|
|
2
|
-
import {EndpointMetadata, Get, Ignore, Property, Returns, View} from "@tsed/schema";
|
|
3
|
-
|
|
4
|
-
import {renderView} from "./renderView.js";
|
|
5
|
-
|
|
6
|
-
describe("renderView", () => {
|
|
7
|
-
beforeEach(() => PlatformTest.create());
|
|
8
|
-
afterEach(() => PlatformTest.reset());
|
|
9
|
-
|
|
10
|
-
it("should render content", async () => {
|
|
11
|
-
class Model {
|
|
12
|
-
@Property()
|
|
13
|
-
data: string;
|
|
14
|
-
|
|
15
|
-
@Ignore()
|
|
16
|
-
test: string;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
class Test {
|
|
20
|
-
@Get("/")
|
|
21
|
-
@View("view", {options: "options"})
|
|
22
|
-
@Returns(200, Model)
|
|
23
|
-
test() {}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const ctx = PlatformTest.createRequestContext();
|
|
27
|
-
ctx.endpoint = EndpointMetadata.get(Test, "test");
|
|
28
|
-
|
|
29
|
-
vi.spyOn(ctx.response, "render").mockResolvedValue("HTML");
|
|
30
|
-
|
|
31
|
-
ctx.data = {data: "data"};
|
|
32
|
-
|
|
33
|
-
await renderView(ctx.data, ctx);
|
|
34
|
-
|
|
35
|
-
expect(ctx.response.render).toHaveBeenCalledWith("view", {
|
|
36
|
-
$ctx: ctx,
|
|
37
|
-
data: "data",
|
|
38
|
-
options: "options"
|
|
39
|
-
});
|
|
40
|
-
});
|
|
41
|
-
it("should throw an error", async () => {
|
|
42
|
-
class Test {
|
|
43
|
-
@Get("/")
|
|
44
|
-
@View("view", {options: "options"})
|
|
45
|
-
test() {}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const ctx = PlatformTest.createRequestContext();
|
|
49
|
-
ctx.endpoint = EndpointMetadata.get(Test, "test");
|
|
50
|
-
|
|
51
|
-
vi.spyOn(ctx.response, "render").mockRejectedValue(new Error("parser error"));
|
|
52
|
-
|
|
53
|
-
ctx.data = {data: "data"};
|
|
54
|
-
|
|
55
|
-
let actualError: any;
|
|
56
|
-
try {
|
|
57
|
-
await renderView(ctx.data, ctx);
|
|
58
|
-
} catch (er) {
|
|
59
|
-
actualError = er;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
expect(actualError.message).toEqual("Template rendering error: Test.test()\nError: parser error");
|
|
63
|
-
});
|
|
64
|
-
});
|
package/src/utils/renderView.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import {BaseContext} from "@tsed/di";
|
|
2
|
-
|
|
3
|
-
import {TemplateRenderError} from "../errors/TemplateRenderError.js";
|
|
4
|
-
|
|
5
|
-
export async function renderView(data: any, $ctx: BaseContext) {
|
|
6
|
-
const {response, endpoint} = $ctx;
|
|
7
|
-
try {
|
|
8
|
-
const {data} = $ctx;
|
|
9
|
-
const {path, options} = endpoint.view;
|
|
10
|
-
|
|
11
|
-
return await response.render(path, {...options, ...data, $ctx});
|
|
12
|
-
} catch (err) {
|
|
13
|
-
throw new TemplateRenderError(endpoint.targetName, endpoint.propertyKey, err);
|
|
14
|
-
}
|
|
15
|
-
}
|