From ac4b5bd1cd1740400c472fa152ea7462b05c1d87 Mon Sep 17 00:00:00 2001 From: ahnpnl Date: Mon, 24 Feb 2025 10:40:11 +0100 Subject: [PATCH] test: reproduce issue 2979 --- examples/example-app-v19/package.json | 3 +- .../src/app/app.component.spec.ts | 13 +++++ .../src/app/response.serializer.ts | 49 +++++++++++++++++++ examples/example-app-v19/yarn.lock | 8 +++ 4 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 examples/example-app-v19/src/app/response.serializer.ts diff --git a/examples/example-app-v19/package.json b/examples/example-app-v19/package.json index 3c5b01e854..455ddfe41d 100644 --- a/examples/example-app-v19/package.json +++ b/examples/example-app-v19/package.json @@ -32,6 +32,7 @@ "@types/node": "^22.13.5", "jest": "^29.7.0", "jest-preset-angular": "^14.5.2", - "typescript": "~5.6.2" + "typescript": "~5.6.2", + "zod": "^3.24.2" } } diff --git a/examples/example-app-v19/src/app/app.component.spec.ts b/examples/example-app-v19/src/app/app.component.spec.ts index 5722261032..3ff1cf162d 100644 --- a/examples/example-app-v19/src/app/app.component.spec.ts +++ b/examples/example-app-v19/src/app/app.component.spec.ts @@ -1,11 +1,14 @@ +import { HttpResponse } from '@angular/common/http'; import { Component, DebugElement, NO_ERRORS_SCHEMA } from '@angular/core'; import { ComponentFixture, fakeAsync, TestBed, tick, waitForAsync } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; import { provideRouter, Router, RouterLink } from '@angular/router'; +import { z } from 'zod'; import { AppComponent } from './app.component'; import { appConfig } from './app.config'; import { UserService } from './model'; +import { serializeResponse } from './response.serializer'; @Component({ selector: 'app-banner', template: '' }) class BannerStubComponent {} @@ -83,4 +86,14 @@ function tests() { expect(TestBed.inject(Router).url).toBe('/heroes'); })); + + it('should serialize response', async () => { + const zodSchema = z.object({ + name: z.string(), + }); + + const serializedResponse = await serializeResponse(zodSchema, new HttpResponse({ body: { name: 'test' } })); + + expect(serializedResponse.body).toStrictEqual({ name: 'test' }); + }); } diff --git a/examples/example-app-v19/src/app/response.serializer.ts b/examples/example-app-v19/src/app/response.serializer.ts new file mode 100644 index 0000000000..54481bb6ee --- /dev/null +++ b/examples/example-app-v19/src/app/response.serializer.ts @@ -0,0 +1,49 @@ +import { HttpResponse } from '@angular/common/http'; +import { ZodError, ZodObject, ZodTypeAny, z } from 'zod'; + +export async function serializeResponse( + zodRef: T, + response: HttpResponse, + debug = false, +) { + const { data: body, error } = await serializeResponseBody(zodRef, response.body, debug); + + if (error && debug) { + reportSchemaDeviation(error, response); + } + + return response.clone({ body }); +} + +export type SerializeBodyResult = Promise<{ + data: z.infer; + error?: ZodError; +}>; + +export async function serializeResponseBody( + zodRef: T, + body: unknown, + strict = false, +): SerializeBodyResult { + const { data, error, success } = + zodRef instanceof ZodObject + ? await (strict ? zodRef.strict().safeParseAsync(body) : zodRef.safeParseAsync(body)) + : await zodRef.safeParseAsync(body); + + if (success) { + return { data }; + } + + return { error, data }; +} + +function reportSchemaDeviation(error: ZodError, response: HttpResponse) { + const debug = (msg: string) => console.log(`%c${msg}`, 'color: orange;'); + + debug('----- [DataAccess] ⚠️ Response schema errors detected (dev mode) -----'); + debug(`Request ${response.url}`); + debug(`Response ${response.status} (${response.statusText})`); + console.warn(error.errors); + const { formErrors } = error.flatten(); + debug(formErrors.join(',')); +} diff --git a/examples/example-app-v19/yarn.lock b/examples/example-app-v19/yarn.lock index 1a6be2d501..3afcadde5d 100644 --- a/examples/example-app-v19/yarn.lock +++ b/examples/example-app-v19/yarn.lock @@ -5649,6 +5649,7 @@ __metadata: rxjs: "npm:^7.8.2" tslib: "npm:^2.8.1" typescript: "npm:~5.6.2" + zod: "npm:^3.24.2" zone.js: "npm:~0.15.0" languageName: unknown linkType: soft @@ -11116,6 +11117,13 @@ __metadata: languageName: node linkType: hard +"zod@npm:^3.24.2": + version: 3.24.2 + resolution: "zod@npm:3.24.2" + checksum: 10/604c62a8cf8e330d78b106a557f4b44f5d14845d20b1360a423ccc09b58cb8525ccf7e4b40cf1bd4852d22393d2c67774b5817ec5a2fedab25f543b36ed15943 + languageName: node + linkType: hard + "zone.js@npm:~0.15.0": version: 0.15.0 resolution: "zone.js@npm:0.15.0"