Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | 54x 54x 14x 14x 11x 17x 9x 26x 15x 26x 12x 2x 26x 18x 10x 9x | import { escape, escapeRegExp, uniq } from 'lodash-es';
import { ErrorFeedback, HttpError } from './http-error.model';
export class HttpErrorHelper {
/**
* Formats all messages of an HTTP error for display: the main messages and their causes are rendered as separate lines.
* ICM messages are trusted and may contain HTML, but the values of their parameters (e.g. a SKU entered by the user) are HTML-escaped.
*
* The result has to be rendered as sanitized HTML, e.g. in a toast or with the DomSanitizer.
*/
static formatMessage(error: Partial<HttpError>): string {
const lines = error?.errors?.flatMap(feedback => HttpErrorHelper.getLines(feedback)) ?? [];
return lines.length ? HttpErrorHelper.joinLines(lines) : error?.message;
}
/**
* Formats the message of a single error feedback and its causes, see {@link formatMessage}.
*/
static formatFeedback(feedback: ErrorFeedback): string {
return HttpErrorHelper.joinLines(HttpErrorHelper.getLines(feedback));
}
private static getLines(feedback: ErrorFeedback): string[] {
return [
HttpErrorHelper.escapeParameters(feedback?.message, feedback?.parameters),
...(feedback?.causes ?? []).map(cause =>
HttpErrorHelper.escapeParameters(cause.message, { ...feedback.parameters, ...cause.parameters })
),
].filter(line => !!line);
}
private static joinLines(lines: string[]): string {
return lines.length > 1 ? lines.map(line => `<div>${line}</div>`).join('') : lines[0];
}
private static escapeParameters(message: string, parameters: Record<string, unknown>): string {
const values = uniq(Object.values(parameters ?? {}))
.filter((value): value is string => typeof value === 'string' && !!value)
// match longer values first, so that values contained in other values are escaped completely
.sort((a, b) => b.length - a.length);
if (!message || !values.length) {
return message;
}
// escape all values in one pass with a callback, so replacement tokens like '$&' in values are not interpreted and nothing is escaped twice
const pattern = new RegExp(values.map(value => escapeRegExp(value)).join('|'), 'g');
return message.replace(pattern, match => escape(match));
}
}
|