♻️ 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'; 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> { export function isResponseArgs<T>(args: any): args is ResponseArgs<T> {
return ( return ResponseArgsSchema.safeParse(args).success;
args &&
typeof args === 'object' &&
'error' in args &&
'invalidProperties' in args &&
'message' in args
);
} }

View File

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