@tsed/platform-response-filter 8.3.6 → 8.4.1

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 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
@@ -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;AACrD,cAAc,uBAAuB,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 { renderView } from "../utils/renderView.js";
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
- * Call filters to transform data
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 { response } = ctx;
23
- if (ctx.endpoint?.operation) {
24
- const bestContentType = this.contentTypeResolver(data, ctx);
25
- bestContentType && response.contentType(bestContentType);
26
- const resolved = this.container.resolve(bestContentType);
27
- if (resolved) {
28
- return resolved.transform(data, ctx);
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
- if (endpoint) {
41
- if (endpoint.view) {
42
- data = await renderView(data, ctx);
43
- }
44
- else if (isSerializable(data)) {
45
- const responseOpts = endpoint.getResponseOptions(response.statusCode, {
46
- includes: this.getIncludes(ctx)
47
- });
48
- data = serialize(data, {
49
- useAlias: true,
50
- additionalProperties: this.additionalProperties,
51
- ...responseOpts,
52
- endpoint: true
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,UAAU,EAAC,MAAM,wBAAwB,CAAC;AAClD,OAAO,EAAC,8BAA8B,EAAC,MAAM,kCAAkC,CAAC;AAChF,OAAO,EAAC,gCAAgC,EAAC,MAAM,oCAAoC,CAAC;AAEpF;;GAEG;AACH,MAAM,OAAO,sBAAsB;IAAnC;QACY,yBAAoB,GAAG,QAAQ,CAAU,sBAAsB,CAAC,CAAC;QACjE,cAAS,GAAG,MAAM,CAAmC,gCAAgC,CAAC,CAAC;QACvF,wBAAmB,GAAG,MAAM,CAAiC,8BAA8B,CAAC,CAAC;IA4DzG,CAAC;IA1DC;;;;OAIG;IACH,SAAS,CAAC,IAAa,EAAE,GAAgB;QACvC,MAAM,EAAC,QAAQ,EAAC,GAAG,GAAG,CAAC;QAEvB,IAAI,GAAG,CAAC,QAAQ,EAAE,SAAS,EAAE,CAAC;YAC5B,MAAM,eAAe,GAAG,IAAI,CAAC,mBAAmB,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;YAE5D,eAAe,IAAI,QAAQ,CAAC,WAAW,CAAC,eAAe,CAAC,CAAC;YAEzD,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;YAEzD,IAAI,QAAQ,EAAE,CAAC;gBACb,OAAO,QAAQ,CAAC,SAAS,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;YACvC,CAAC;QACH,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,SAAS,CAAC,IAAa,EAAE,GAAgB;QAC7C,MAAM,EAAC,QAAQ,EAAE,QAAQ,EAAC,GAAG,GAAG,CAAC;QAEjC,IAAI,QAAQ,EAAE,CAAC;YACb,IAAI,QAAQ,CAAC,IAAI,EAAE,CAAC;gBAClB,IAAI,GAAG,MAAM,UAAU,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;YACrC,CAAC;iBAAM,IAAI,cAAc,CAAC,IAAI,CAAC,EAAE,CAAC;gBAChC,MAAM,YAAY,GAAG,QAAQ,CAAC,kBAAkB,CAAC,QAAQ,CAAC,UAAU,EAAE;oBACpE,QAAQ,EAAE,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC;iBAChC,CAAC,CAAC;gBAEH,IAAI,GAAG,SAAS,CAAC,IAAI,EAAE;oBACrB,QAAQ,EAAE,IAAI;oBACd,oBAAoB,EAAE,IAAI,CAAC,oBAAoB;oBAC/C,GAAG,YAAY;oBACf,QAAQ,EAAE,IAAI;iBACf,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAEO,WAAW,CAAC,GAAgB;QAClC,IAAI,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC;YAC/B,OAAO,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,CAAC,OAAe,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;QAChG,CAAC;QAED,OAAO,SAAS,CAAC;IACnB,CAAC;CACF;AAED,UAAU,CAAC,sBAAsB,CAAC,CAAC"}
1
+ {"version":3,"file":"PlatformResponseFilter.js","sourceRoot":"","sources":["../../../src/services/PlatformResponseFilter.ts"],"names":[],"mappings":"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"}
@@ -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
- * Call filters to transform data
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
- transform(data: unknown, ctx: BaseContext): any;
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
- private getIncludes;
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,14 +2,14 @@
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.3.6",
5
+ "version": "8.4.1",
6
6
  "source": "./src/index.ts",
7
7
  "main": "./lib/esm/index.js",
8
8
  "module": "./lib/esm/index.js",
9
9
  "typings": "./lib/types/index.d.ts",
10
10
  "exports": {
11
11
  ".": {
12
- "@tsed/source": "./src/index.ts",
12
+ "tsed-source": "./src/index.ts",
13
13
  "types": "./lib/types/index.d.ts",
14
14
  "import": "./lib/esm/index.js",
15
15
  "default": "./lib/esm/index.js"
@@ -26,23 +26,23 @@
26
26
  "tslib": "2.7.0"
27
27
  },
28
28
  "devDependencies": {
29
- "@tsed/barrels": "8.3.6",
30
- "@tsed/core": "8.3.6",
31
- "@tsed/di": "8.3.6",
32
- "@tsed/exceptions": "8.3.6",
33
- "@tsed/json-mapper": "8.3.6",
34
- "@tsed/schema": "8.3.6",
35
- "@tsed/typescript": "8.3.6",
29
+ "@tsed/barrels": "8.4.1",
30
+ "@tsed/core": "8.4.1",
31
+ "@tsed/di": "8.4.1",
32
+ "@tsed/exceptions": "8.4.1",
33
+ "@tsed/json-mapper": "8.4.1",
34
+ "@tsed/schema": "8.4.1",
35
+ "@tsed/typescript": "8.4.1",
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.3.6",
42
- "@tsed/di": "8.3.6",
43
- "@tsed/exceptions": "8.3.6",
44
- "@tsed/json-mapper": "8.3.6",
45
- "@tsed/schema": "8.3.6"
41
+ "@tsed/core": ">=8.4.1",
42
+ "@tsed/di": ">=8.4.1",
43
+ "@tsed/exceptions": ">=8.4.1",
44
+ "@tsed/json-mapper": ">=8.4.1",
45
+ "@tsed/schema": ">=8.4.1"
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("serialize()", () => {
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.serialize({test: "test"}, ctx);
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.serialize({test: "test"}, ctx);
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.serialize({test: "test"}, ctx);
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.serialize({test: "test"}, ctx);
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.serialize({test: "test"}, ctx);
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.serialize({test: "test"}, ctx));
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 {renderView} from "../utils/renderView.js";
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
- * Call filters to transform data
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 {response} = ctx;
25
+ async transform(data: unknown, $ctx: BaseContext): Promise<unknown> {
26
+ const {endpoint} = $ctx;
24
27
 
25
- if (ctx.endpoint?.operation) {
26
- const bestContentType = this.contentTypeResolver(data, ctx);
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
- bestContentType && response.contentType(bestContentType);
36
+ return this.resolve(data, $ctx);
37
+ }
29
38
 
30
- const resolved = this.container.resolve(bestContentType);
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
- if (resolved) {
33
- return resolved.transform(data, ctx);
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
- if (endpoint) {
49
- if (endpoint.view) {
50
- data = await renderView(data, ctx);
51
- } else if (isSerializable(data)) {
52
- const responseOpts = endpoint.getResponseOptions(response.statusCode, {
53
- includes: this.getIncludes(ctx)
54
- });
55
-
56
- data = serialize(data, {
57
- useAlias: true,
58
- additionalProperties: this.additionalProperties,
59
- ...responseOpts,
60
- endpoint: true
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
- private getIncludes(ctx: BaseContext) {
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,2 +0,0 @@
1
- import { BaseContext } from "@tsed/di";
2
- export declare function renderView(data: any, $ctx: BaseContext): Promise<any>;
@@ -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
- });
@@ -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
- }