♻️ refactor: improve ResponseArgs validation with Zod schema

- Replace manual type checking with Zod schema validation in isResponseArgs helper
- Simplify error handling logic in catchResponseArgsErrorPipe operator
- Remove redundant conditional checks by leveraging Zod's safeParse
- Remove unused ResponseArgs import from operator file

This improves type safety and validation robustness by using a declarative schema-based approach.
This commit is contained in:
Lorenz Hilpert
2025-11-20 15:22:16 +01:00
parent 17cb0802c3
commit 3e960b0f44
2 changed files with 52 additions and 60 deletions

View File

@@ -1,11 +1,13 @@
import z from 'zod';
import { ResponseArgs } from '../models';
const ResponseArgsSchema = z.object({
error: z.boolean(),
message: z.string().optional(),
invalidProperties: z.record(z.string()).optional(),
result: z.any().optional(),
});
export function isResponseArgs<T>(args: any): args is ResponseArgs<T> {
return (
args &&
typeof args === 'object' &&
'error' in args &&
'invalidProperties' in args &&
'message' in args
);
return ResponseArgsSchema.safeParse(args).success;
}

View File

@@ -1,53 +1,43 @@
import { catchError, mergeMap } from 'rxjs/operators';
import { OperatorFunction, throwError, pipe } from 'rxjs';
import { ResponseArgsError } from '../errors';
import { HttpErrorResponse } from '@angular/common/http';
import { ResponseArgs } from '../models';
import { isResponseArgs } from '../helpers';
/**
*
* Operator that catches errors from an observable stream and transforms them into
*
* `ResponseArgsError` instances when applicable.
* This operator is useful for handling HTTP errors that return a structured
* response conforming to the `ResponseArgs` interface.
*
* If the error is already a `ResponseArgsError`, it is re-thrown as is.
* If the error is an `HttpErrorResponse` with a valid `ResponseArgs` payload,
* it creates and throws a new `ResponseArgsError`.
* For all other error types, it re-throws the original error.
*
*/
export const catchResponseArgsErrorPipe = <T>(): OperatorFunction<T, T> =>
pipe(
catchError((err: unknown) => {
if (err instanceof ResponseArgsError) {
return throwError(() => err);
}
if (err instanceof HttpErrorResponse && err.error) {
const payload = err.error as Partial<ResponseArgs<unknown>>;
if (payload.error === true) {
return throwError(
() =>
new ResponseArgsError({
error: true,
message: payload.message,
invalidProperties: payload.invalidProperties ?? {},
}),
);
}
}
return throwError(() => err);
}),
mergeMap((response) => {
if (isResponseArgs(response) && response.error === true) {
return throwError(() => new ResponseArgsError(response));
}
return [response];
}),
);
import { catchError, mergeMap } from 'rxjs/operators';
import { OperatorFunction, throwError, pipe } from 'rxjs';
import { ResponseArgsError } from '../errors';
import { HttpErrorResponse } from '@angular/common/http';
import { isResponseArgs } from '../helpers';
/**
*
* Operator that catches errors from an observable stream and transforms them into
*
* `ResponseArgsError` instances when applicable.
* This operator is useful for handling HTTP errors that return a structured
* response conforming to the `ResponseArgs` interface.
*
* If the error is already a `ResponseArgsError`, it is re-thrown as is.
* If the error is an `HttpErrorResponse` with a valid `ResponseArgs` payload,
* it creates and throws a new `ResponseArgsError`.
* For all other error types, it re-throws the original error.
*
*/
export const catchResponseArgsErrorPipe = <T>(): OperatorFunction<T, T> =>
pipe(
catchError((err: unknown) => {
if (err instanceof ResponseArgsError) {
return throwError(() => err);
}
if (err instanceof HttpErrorResponse) {
const payload = err.error;
if (isResponseArgs(payload)) {
return throwError(() => new ResponseArgsError(payload));
}
}
return throwError(() => err);
}),
mergeMap((response) => {
if (isResponseArgs(response) && response.error === true) {
return throwError(() => new ResponseArgsError(response));
}
return [response];
}),
);