Skip to content

Commit

Permalink
on("error") pour load et qui récupère l'erreur du serveur
Browse files Browse the repository at this point in the history
  • Loading branch information
JabX committed Jan 27, 2025
1 parent d400872 commit b2424ae
Show file tree
Hide file tree
Showing 7 changed files with 82 additions and 42 deletions.
2 changes: 1 addition & 1 deletion packages/core/src/focus4.core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ declare module "i18next" {
}
}

export {coreFetch, downloadFile, getFileObjectUrl, requestStore} from "./network";
export {coreFetch, downloadFile, getFileObjectUrl, isHandledError, requestStore} from "./network";
export {makeRouter, param, startHistory} from "./router";
export {MessageStore, messageStore, UserStore} from "./stores";
export {config, themeable} from "./utils";
Expand Down
8 changes: 8 additions & 0 deletions packages/core/src/network/error-parsing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,3 +134,11 @@ export function handleProblemDetails(problemDetails: ProblemDetails): HandledPro

return {...problemDetails, $messages};
}

/**
* Vérifie si l'erreur retournée par un appel a bien été traitée et est donc bien au format attendu.
* @param error L'erreur.
*/
export function isHandledError(error: unknown): error is HandledProblemDetails {
return typeof error === "object" && !!error && "$messages" in error;
}
1 change: 1 addition & 0 deletions packages/core/src/network/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export {isHandledError} from "./error-parsing";
export {coreFetch, downloadFile, getFileObjectUrl} from "./fetch";
export {RequestStore, requestStore} from "./store";

Expand Down
2 changes: 1 addition & 1 deletion packages/docs/basics/02-fetch.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ Après traitement des erreurs, `coreFetch` renverra une Promise rejetée avec la
- De `type` et `status` si ce n'était pas un `ProblemDetails`.
- D'une propriété `$messages` qui contient la liste des messages générés (dans l'ordre) par la lecture de la réponse.

Vous pouvez utiliser le type exporté `HandledProblemDetails` si besoin pour la représenter.
Vous pouvez utiliser la fonction `isHandledError` dans vos catchs (ou dans `on("error")` sur un formulaire) pour récupérer ces données.

## `RequestStore`

Expand Down
10 changes: 6 additions & 4 deletions packages/docs/model/10-form-actions.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -123,20 +123,22 @@ nécessairement défini dans le `useFormActions` en cours.
La fonction `on` est étendue pour les actions de formulaires et propose de définir des handlers pour les évènements suivant en plus de `"load"` :

- `"init"` : appelé après l'appel à `init()`
- `"error"` : appelé en cas d'erreur retournée par le serveur lors de la sauvegarde.
- `"error"` : appelé en cas d'erreur retournée par le serveur lors du chargement ou de la sauvegarde.
- `"create"` : appelé après une sauvegarde en succès sur le serveur, avec `create`.
- `"update"` : appelé après une sauvegarde en succès sur le serveur, avec `update`.
- `"save"` : appelé après une sauvegarde en succès sur le serveur, avec `save`.
- `"cancel"` : appelé avec `onClickCancel()`.
- `"edit"` : appelé avec `onClickEdit()`.

On peut en spécifier un seul ou bien un array d'évènements. Le handler passé en second paramètre sera appelé avec les deux paramètres suivants :
On peut en spécifier un seul ou bien un array d'évènements. Le handler passé en second paramètre sera appelé avec les trois paramètres suivants :

- Le nom de l'évènement qui l'a déclenché.
- La valeur retournée par le service, si c'est un handler lié à un service qui renvoie des données (`"init"`, `"load"`, `"create"`, `"update"` ou `"save"`).

- La valeur retournée par le service, si c'est un handler lié à un service qui renvoie des données (`"init"`, `"load"`, `"create"`, `"update"`, `"save"`),
ou le nom du service en erreur pour `"error"`.
En particulier, si votre service de création retourne un `id` par exemple, vous pourrez le récupérer ici.

- Pour `"error"`, la réponse du serveur retournée par le service en erreur.

### `errorDisplay(mode)`

Permet de configurer le mode d'affichage des erreurs sur les champs du formulaire. Correspond à [`errorDisplay`](/docs/modèle-métier-afficher-des-champs--docs#errordisplay) dans les options de champ.
Expand Down
41 changes: 23 additions & 18 deletions packages/stores/src/entity/form/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ interface FormActionHandlers<
create?: ((event: "create", data: C) => void)[];
update?: ((event: "update", data: U) => void)[];
save?: ((event: "save", data: S) => void)[];
error?: ((event: "error") => void)[];
error?: ((event: "error", data: "load" | "init" | "save" | "create" | "update", error: unknown) => void)[];
}

/** Props passées au composant de formulaire. */
Expand Down Expand Up @@ -140,15 +140,16 @@ export class FormActions<
this.errorDisplay = "always";
}

const service = this.builder.saveService
? "save"
: this.builder.updateService && this.params
? "update"
: this.builder.createService && !this.params
? "create"
: undefined;

// On ne fait rien si on est déjà en chargement et qu'on a pas le bon service disponible à appeler.
if (
this.isLoading ||
!(
this.builder.saveService ||
(this.builder.createService && !this.params) ||
(this.builder.updateService && this.params)
)
) {
if (this.isLoading || !service) {
return;
}

Expand Down Expand Up @@ -216,19 +217,19 @@ export class FormActions<
this.errorDisplay = "after-focus";
}

if (this.builder.saveService) {
(this.builder.handlers.save ?? []).forEach(handler => handler("save", data as S));
} else if (this.builder.updateService && this.params) {
(this.builder.handlers.update ?? []).forEach(handler => handler("update", data as U));
} else {
(this.builder.handlers.create ?? []).forEach(handler => handler("create", data as C));
}
(this.builder.handlers[service] ?? []).forEach(handler => handler(service as never, data as any));
} catch (e: unknown) {
(this.builder.handlers.error ?? []).forEach(handler => handler("error"));
(this.builder.handlers.error ?? []).forEach(handler => handler("error", service, e));
throw e;
}
}

/** Vide le noeud de formulaire et ainsi son noeud source. */
override clear() {
super.clear();
this.formNode.clear();
}

override register(node?: FN["sourceNode"], builder?: NodeLoadBuilder<FN["sourceNode"], A>) {
const loadDisposer = super.register(node, builder);

Expand Down Expand Up @@ -398,7 +399,11 @@ export class FormActionsBuilder<
*/
override on<E extends keyof FormActionHandlers<FN, C, U, S>>(
event: E | E[],
handler: (event: E, data: Parameters<NonNullable<FormActionHandlers<FN, C, U, S>[E]>[0]>[1]) => void
handler: (
event: E,
data: Parameters<NonNullable<FormActionHandlers<FN, C, U, S>[E]>[0]>[1],
error: Parameters<NonNullable<FormActionHandlers<FN, C, U, S>[E]>[0]>[2]
) => void
): FormActionsBuilder<FN, P, C, U, S> {
return super.on(event as any, handler as any) as any;
}
Expand Down
60 changes: 42 additions & 18 deletions packages/stores/src/entity/store/load.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {isFunction} from "lodash";
import {autorun, computed, makeObservable, observable, runInAction} from "mobx";
import {action, autorun, computed, makeObservable, observable, runInAction} from "mobx";
import {v4} from "uuid";

import {requestStore} from "@focus4/core";
Expand All @@ -16,6 +16,11 @@ import {

import {defaultLoad} from "./store";

interface LoadRegistrationHandlers<SN extends StoreNode | StoreListNode> {
load?: ((event: "load", data: NodeToType<SN>) => void)[];
error?: ((event: "error", data: "load", error: unknown) => void)[];
}

/** Enregistrement de chargement. */
export class LoadRegistration<SN extends StoreListNode | StoreNode = any, A extends readonly any[] = any[]> {
/**
Expand Down Expand Up @@ -51,7 +56,8 @@ export class LoadRegistration<SN extends StoreListNode | StoreNode = any, A exte
builder: observable.ref,
isLoading: computed,
node: observable.ref,
params: computed.struct
params: computed.struct,
clear: action.bound
});
}

Expand Down Expand Up @@ -83,23 +89,34 @@ export class LoadRegistration<SN extends StoreListNode | StoreNode = any, A exte
*/
async load() {
if (this.params !== undefined && this.builder.loadService) {
const data = await requestStore.track([this.trackingId, ...this.builder.trackingIds], () =>
this.builder.loadService!(...this.params!)
);
runInAction(() => {
if (data) {
if (isStoreNode(this.node)) {
this.node.replace(data as EntityToType<any>);
} else if (isStoreListNode(this.node)) {
this.node.replaceNodes(data as EntityToType<any>[]);
try {
const data = await requestStore.track([this.trackingId, ...this.builder.trackingIds], () =>
this.builder.loadService!(...this.params!)
);
runInAction(() => {
if (data) {
if (isStoreNode(this.node)) {
this.node.replace(data as EntityToType<any>);
} else if (isStoreListNode(this.node)) {
this.node.replaceNodes(data as EntityToType<any>[]);
}
}
}

(this.builder.handlers.load ?? []).forEach(handler => handler("load", data));
});
(this.builder.handlers.load ?? []).forEach(handler => handler("load", data));
});
} catch (e: unknown) {
this.clear();
(this.builder.handlers.error ?? []).forEach(handler => handler("error", "load", e));
throw e;
}
}
}

/** Vide le noeud de store associé au service de chargement. */
clear() {
this.node.clear();
}

/**
* Enregistre le service de chargement sur le noeud et crée la réaction de chargmement.
*
Expand Down Expand Up @@ -140,7 +157,7 @@ export class LoadRegistration<SN extends StoreListNode | StoreNode = any, A exte
/** Objet de configuration pour un enregistrement de chargement. */
export class NodeLoadBuilder<SN extends StoreListNode | StoreNode, P extends readonly any[] = never> {
/** @internal */
readonly handlers: {load?: ((event: "load", data: NodeToType<SN>) => void)[]} = {};
readonly handlers: LoadRegistrationHandlers<SN> = {};

/** @internal */
getLoadParams?: () => any | undefined;
Expand Down Expand Up @@ -192,16 +209,23 @@ export class NodeLoadBuilder<SN extends StoreListNode | StoreNode, P extends rea
* @param event Nom de l'évènement.
* @param handler Handler de l'évènement.
*/
on(event: "load"[] | "load", handler: (event: "load", data?: NodeToType<SN>) => void): NodeLoadBuilder<SN, P> {
on<E extends keyof LoadRegistrationHandlers<SN>>(
event: E | E[],
handler: (
event: E,
data: Parameters<NonNullable<LoadRegistrationHandlers<SN>[E]>[0]>[1],
error: Parameters<NonNullable<LoadRegistrationHandlers<SN>[E]>[0]>[2]
) => void
): NodeLoadBuilder<SN, P> {
if (!Array.isArray(event)) {
event = [event];
}

event.forEach(e => {
if (!this.handlers[e]) {
this.handlers[e] = [handler];
this.handlers[e] = [handler as any];
} else {
this.handlers[e].push(handler);
this.handlers[e].push(handler as any);
}
});

Expand Down

0 comments on commit b2424ae

Please sign in to comment.