-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #211 from klee-contrib/problem-details
[Core] Gérer les erreurs d'API selon la norme RFC 7807
- Loading branch information
Showing
6 changed files
with
167 additions
and
35 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,34 +1,136 @@ | ||
import {messageStore} from "../stores/message"; | ||
|
||
/** Format attendu des erreurs JSON issues du serveur. */ | ||
export interface ErrorResponse { | ||
[key: string]: any; | ||
/** | ||
* Retour d'un appel serveur en erreur ([RFC9457]). | ||
* | ||
* Si le serveur n'est pas configuré pour renvoyer un `ProblemDetails` en cas d'erreur, la réponse sera wrappée dans `ProblemDetails` lorsqu'elle sera rejetée. | ||
*/ | ||
export interface ProblemDetails { | ||
/** | ||
* A URI reference [RFC3986] that identifies the problem type. | ||
* | ||
* This specification encourages that, when dereferenced, it provide human-readable documentation for the problem type (e.g., using HTML [W3C.REC-html5-20141028]). | ||
* | ||
* When this member is not present, its value is assumed to be "type" (string) - "about:blank". | ||
*/ | ||
type?: string | "about:blank"; | ||
|
||
/** | ||
* The HTTP status code ([RFC7231], Section 6) generated by the origin server for this occurrence of the problem. | ||
*/ | ||
status: number; | ||
} | ||
|
||
/** Erreur JSON issue du serveur, à laquelle on a ajouté des infos issues du parsing. */ | ||
export interface ManagedErrorResponse { | ||
/** | ||
* A short, human-readable summary of the problem type. | ||
* | ||
* It SHOULD NOT change from occurrence to occurrence of the problem, except for purposes of localization (e.g., using proactive content negotiation; see [RFC7231], Section 3.4). | ||
* | ||
* Si aucun message n'a été enregistré dans le `messageStore` à partir de cette instance, alors ce `title` sera enregistré comme `error` dans le `messageStore`. | ||
*/ | ||
title?: string; | ||
|
||
/** | ||
* A human-readable explanation specific to this occurrence of the problem. | ||
* | ||
* Si renseigné, sera enregistré en premier comme `error` dans le `messageStore`. | ||
*/ | ||
detail?: string; | ||
|
||
/** | ||
* A URI reference that identifies the specific occurrence of the problem. | ||
* | ||
* It may or may not yield further information if dereferenced. | ||
*/ | ||
instance?: string; | ||
|
||
/** | ||
* Détails supplémentaires sur le problème (erreurs de validation par exemple). | ||
* | ||
* Chaque message d'erreur supplémentaire sera enregistré comme `error` dans le `messageStore`, préfixé par sa clé (sauf si c'est `global` ou `globals`). | ||
*/ | ||
errors?: string | string[] | Record<string, string> | Record<string, string[]>; | ||
|
||
/** | ||
* Problem type definitions MAY extend the problem details object with additional members. | ||
* | ||
* Si la clé correspond à un type de message enregistré dans le `messageStore` et que la valeur est un `string`, `string[]`, `Record<string, string>` ou `Record<string, string[]>`, | ||
* alors les messages seront enregistrés comme du type de leur clé dans le `messageStore` (selon les mêmes règles que `errors`). | ||
*/ | ||
[key: string]: any; | ||
/** Erreurs détectées dans l'erreur serveur. */ | ||
$parsedErrors: { | ||
/** Erreurs globales. */ | ||
globals: string[]; | ||
}; | ||
/** Statut HTTP de la réponse. */ | ||
$status: number; | ||
} | ||
|
||
/** | ||
* Parse une réponse du serveur pour enregistrer les erreurs. | ||
* @param $status Statut HTTP de la réponse. | ||
* @param response Corps de la réponse. | ||
* ProblemDetails traité par Focus, avec les messages qui ont été ajoutés dans le MessageStore. | ||
*/ | ||
export function manageResponseErrors($status: number, response: ErrorResponse): ManagedErrorResponse { | ||
export interface HandledProblemDetails extends ProblemDetails { | ||
/** Messages enregistrés dans le MessageStore, dans l'ordre. */ | ||
$messages: {type: string; message: string}[]; | ||
} | ||
|
||
export function createProblemDetails(status: number, jsonResponse: object): ProblemDetails { | ||
return { | ||
...response, | ||
$status, | ||
$parsedErrors: { | ||
globals: messageStore.addMessages(response) | ||
} | ||
...jsonResponse, | ||
type: "about:blank", | ||
status | ||
}; | ||
} | ||
|
||
export function handleProblemDetails(problemDetails: ProblemDetails): HandledProblemDetails { | ||
const messages: Record<string, string[]> = {}; | ||
|
||
function add(type: string, ...newMessages: string[]) { | ||
messages[type] ??= []; | ||
messages[type].push(...newMessages); | ||
} | ||
|
||
if (problemDetails.detail) { | ||
add("errors", problemDetails.detail); | ||
} | ||
|
||
for (const key in problemDetails) { | ||
if (["type", "status", "title", "detail", "instance"].includes(key)) { | ||
continue; | ||
} | ||
|
||
if (typeof problemDetails[key] === "string") { | ||
add(key, problemDetails[key]); | ||
} else if ( | ||
Array.isArray(problemDetails[key]) && | ||
problemDetails[key].length > 0 && | ||
typeof problemDetails[key][0] === "string" | ||
) { | ||
add(key, ...problemDetails[key]); | ||
} else if (typeof problemDetails[key] === "object") { | ||
for (const subkey in problemDetails[key]) { | ||
if (typeof problemDetails[key][subkey] === "string") { | ||
add( | ||
key, | ||
subkey === "global" || subkey === "globals" | ||
? problemDetails[key][subkey] | ||
: `${subkey}: ${problemDetails[key][subkey]}` | ||
); | ||
} else if ( | ||
Array.isArray(problemDetails[key][subkey]) && | ||
problemDetails[key][subkey].length > 0 && | ||
typeof problemDetails[key][subkey][0] === "string" | ||
) { | ||
add( | ||
key, | ||
...problemDetails[key][subkey].map(m => | ||
subkey === "global" || subkey === "globals" ? m : `${subkey}: ${m}` | ||
) | ||
); | ||
} | ||
} | ||
} | ||
} | ||
|
||
const $messages = messageStore.addMessages(messages); | ||
|
||
if ($messages.length === 0 && problemDetails.title) { | ||
messageStore.addErrorMessage(problemDetails.title); | ||
$messages.push({type: "error", message: problemDetails.title}); | ||
} | ||
|
||
return {...problemDetails, $messages}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
export {manageResponseErrors} from "./error-parsing"; | ||
export {coreFetch, downloadFile, getFileObjectUrl} from "./fetch"; | ||
export {RequestStore, requestStore} from "./store"; | ||
|
||
export type {HandledProblemDetails, ProblemDetails} from "./error-parsing"; | ||
export type {HttpMethod, Request} from "./store"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters