#4994 Process Handling + Schema Update

This commit is contained in:
Nino
2025-03-05 18:04:15 +01:00
parent 86eb0bb494
commit a884adc3a9
7 changed files with 7350 additions and 766 deletions

View File

@@ -1,10 +1,17 @@
import { inject, Injectable } from '@angular/core';
import { QuerySettingsDTO, QueryTokenDTO, ReceiptService } from '@generated/swagger/oms-api';
import { map, Observable, throwError } from 'rxjs';
import { z } from 'zod';
import * as Schemas from './return-search.schemas';
import { inject, Injectable } from "@angular/core";
import { QuerySettingsDTO, ReceiptService } from "@generated/swagger/oms-api";
import { map, Observable, throwError } from "rxjs";
import {
QueryTokenSchema,
QueryTokenInput,
} from "./schemas/query-token.schema";
import {
ListResponseArgs,
ListResponseArgsSchema,
ReceiptListItemSchema,
} from "./schemas/receipt-list-item.schema";
@Injectable({ providedIn: 'root' })
@Injectable({ providedIn: "root" })
export class ReturnSearchService {
#receiptService = inject(ReceiptService);
@@ -12,22 +19,39 @@ export class ReturnSearchService {
return this.#receiptService.ReceiptQueryReceiptSettings().pipe(
map((res) => {
if (res.error || !res.result) {
throw new Error('Failed to fetch query settings');
throw new Error("Failed to fetch query settings");
}
return res.result;
}),
);
}
search(queryToken: z.input<typeof Schemas.QueryTokenSchema>): Observable<any> {
search(queryToken: QueryTokenInput): Observable<ListResponseArgs> {
try {
queryToken = Schemas.QueryTokenSchema.parse(queryToken);
queryToken = QueryTokenSchema.parse(queryToken);
} catch (error) {
return throwError(() => error);
}
return this.#receiptService.ReceiptQueryReceipt({
queryToken,
});
return this.#receiptService
.ReceiptQueryReceipt({
queryToken,
})
.pipe(
map((res) => {
if (res.error || !res.result) {
throw new Error("Failed to fetch receipt list items");
}
return ListResponseArgsSchema.parse({
hits: res.hits,
skip: res.skip,
take: res.take,
error: res.error,
invalidProperties: res.invalidProperties,
result: res.result.map((res) => ReceiptListItemSchema.parse(res)),
});
}),
);
}
}

View File

@@ -1,22 +1,132 @@
import { signalStore, withMethods } from '@ngrx/signals';
import {} from '@ngrx/operators';
import { withEntities } from '@ngrx/signals/entities';
import { pipe } from 'rxjs';
import { patchState, signalStore, type, withMethods } from "@ngrx/signals";
import { rxMethod } from "@ngrx/signals/rxjs-interop";
import {
addEntity,
entityConfig,
updateEntity,
withEntities,
} from "@ngrx/signals/entities";
import { debounceTime, distinctUntilChanged, pipe, switchMap, tap } from "rxjs";
import { ReturnSearchService } from "./return-search.service";
import { tapResponse } from "@ngrx/operators";
import { inject } from "@angular/core";
import { ReceiptListItem, QueryTokenSchema, ListResponseArgs } from "./schemas";
type ReturnSearuchState = {
id: number;
params: Record<string, string>;
results: string[];
hits: number;
status: 'idle' | 'pending' | 'success' | 'error';
error: string | null;
enum ReturnSearchStatus {
Idle = "idle",
Pending = "pending",
Success = "success",
Error = "error",
}
type ReturnSearchEntity = {
processId: number;
status: ReturnSearchStatus;
params?: Record<string, string>;
items?: ReceiptListItem[];
hits?: number;
error?: string | unknown;
};
const config = entityConfig({
entity: type<ReturnSearchEntity>(),
selectId: (entity) => entity.processId,
});
export const ReturnSearchStore = signalStore(
{ providedIn: 'root' },
withEntities<ReturnSearuchState>(),
// withMethods((store) => ({
// search: rxMethod<{ id: number; params: Record<string, string> }>(pipe()),
// paging: rxMethod<{ id: number }>(pipe()),
// })),
{ providedIn: "root" },
withEntities<ReturnSearchEntity>(config),
withMethods((store) => ({
_beforeSearch(processId: number) {
const entity = store.entities()[processId];
if (entity) {
patchState(
store,
updateEntity(
{ id: processId, changes: { status: ReturnSearchStatus.Pending } },
config,
),
);
} else {
const entity: ReturnSearchEntity = {
processId,
status: ReturnSearchStatus.Pending,
};
patchState(store, addEntity(entity, config));
}
},
_handleSearchSuccess({
processId,
response,
}: {
processId: number;
response: ListResponseArgs;
}) {
patchState(
store,
updateEntity(
{
id: processId,
changes: {
status: ReturnSearchStatus.Success,
hits: response.hits,
items: response.result,
},
},
config,
),
);
},
_handleSearchError({
processId,
error,
}: {
processId: number;
error: unknown;
}) {
patchState(
store,
updateEntity(
{
id: processId,
changes: {
items: [],
hits: undefined,
status: ReturnSearchStatus.Error,
error,
},
},
config,
),
);
},
_handleSearchComplete(processId: number) {
patchState(
store,
updateEntity(
{ id: processId, changes: { status: ReturnSearchStatus.Idle } },
config,
),
);
},
})),
withMethods((store, returnSearchService = inject(ReturnSearchService)) => ({
search: rxMethod<{ processId: number; params: Record<string, string> }>(
pipe(
debounceTime(100),
distinctUntilChanged(),
tap(({ processId }) => store._beforeSearch(processId)),
switchMap(({ processId, params }) =>
returnSearchService.search(QueryTokenSchema.parse(params)).pipe(
tapResponse(
(response) => store._handleSearchSuccess({ processId, response }),
(error) => store._handleSearchError({ processId, error }),
() => store._handleSearchComplete(processId),
),
),
),
),
),
// paging: rxMethod<rxMethod<{ processId: number; params: Record<string, string> }>>(pipe()),
})),
);

View File

@@ -0,0 +1,3 @@
export * from "./receipt-list-item.schema";
export * from "./response-args.schema";
export * from "./query-token.schema";

View File

@@ -1,4 +1,4 @@
import { z } from 'zod';
import { z } from "zod";
export const QueryTokenSchema = z.object({
filter: z.object({
@@ -7,3 +7,5 @@ export const QueryTokenSchema = z.object({
skip: z.number().default(0),
take: z.number().default(25),
});
export type QueryTokenInput = z.input<typeof QueryTokenSchema>;

View File

@@ -0,0 +1,34 @@
import { z } from "zod";
import { ResponseArgsSchema } from "./response-args.schema";
// PersonNamesDTO
export const PersonSchema = z.object({
firstName: z.string(),
gender: z.string().optional(),
lastName: z.string(),
title: z.string().optional(),
});
// AddresseeDTO
export const BuyerSchema = z.object({
person: PersonSchema,
});
// ReceiptListItemDTO
export const ReceiptListItemSchema = z.object({
pId: z.string(),
id: z.number(),
label: z.string(),
buyer: BuyerSchema,
});
// ListResponseArgsOfReceiptListItemDTO
export const ListResponseArgsSchema = ResponseArgsSchema.extend({
hits: z.number(),
skip: z.number(),
take: z.number(),
result: ReceiptListItemSchema.array(),
});
export type ListResponseArgs = z.infer<typeof ListResponseArgsSchema>;
export type ReceiptListItem = z.infer<typeof ReceiptListItemSchema>;

View File

@@ -0,0 +1,9 @@
import { z } from "zod";
export const ResponseArgsSchema = z.object({
error: z.boolean(),
invalidProperties: z.record(z.string()).optional(),
message: z.string().optional(),
});
export type ResponseArgs = z.infer<typeof ResponseArgsSchema>;

7874
package-lock.json generated
View File

File diff suppressed because it is too large Load Diff