Skip to content

Commit

Permalink
Add errors on process pages
Browse files Browse the repository at this point in the history
  • Loading branch information
Quetzacoalt91 committed Feb 4, 2025
1 parent 58f0e19 commit b81f5f0
Show file tree
Hide file tree
Showing 9 changed files with 75 additions and 27 deletions.
20 changes: 8 additions & 12 deletions _dev/src/ts/api/RequestHandler.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import baseApi from './baseApi';
import { ApiResponse, ApiResponseAction, ApiResponseUnknown } from '../types/apiTypes';
import { ApiError, ApiResponse, ApiResponseAction, ApiResponseUnknown } from '../types/apiTypes';
import Hydration from '../utils/Hydration';
import { AxiosError } from 'axios';
import { toApiError, toApiResponseAction } from './axiosError';

export class RequestHandler {
#currentRequestAbortController: AbortController | null = null;
Expand Down Expand Up @@ -50,19 +51,19 @@ export class RequestHandler {
* @description Sends a POST request to the API with the specified action.
* Automatically includes the `admin_dir` required by the backend.
*/
public async postAction(action: string): Promise<ApiResponseAction | void> {
public async postAction(action: string): Promise<ApiResponseAction> {
const data = new FormData();
data.append('action', action);

try {
const response = await baseApi.post<ApiResponseAction>('', data);
return response.data;
} catch (error: unknown) {
if (error instanceof AxiosError && error?.response?.data?.error) {
return error.response.data as ApiResponseAction;
if (error instanceof AxiosError) {
return toApiResponseAction(error);
}
// TODO: catch errors
console.error(error);

throw error;
}
}

Expand All @@ -83,12 +84,7 @@ export class RequestHandler {
}

async #handleError(error: AxiosError<ApiResponseUnknown, XMLHttpRequest>): Promise<void> {
new Hydration().hydrateError({
code: error.status,
type: error.code,
requestParams: error.request,
additionalContents: error.response?.data
});
new Hydration().hydrateError(toApiError(error));
}
}

Expand Down
34 changes: 34 additions & 0 deletions _dev/src/ts/api/axiosError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { AxiosError } from "axios";
import { ApiError, ApiResponseAction } from "../types/apiTypes";

export const toApiError = (error: AxiosError): ApiError => ({
code: error.status,
type: error.code,
requestParams: error.request,
additionalContents: formatResponseContents(error)
});

export const toApiResponseAction = (error: AxiosError): ApiResponseAction => ({
kind: 'action',
error: true,
next: 'Error',
stepDone: null,
status: 'error',
next_desc: 'Error',
nextParams: {
progressPercentage: 0
},
nextQuickInfo: [],
apiError: toApiError(error)
});

export const isHttpErrorCode = (code?: number): boolean => {
return typeof code === 'number' && code >= 300 && code.toString().length === 3;
}
const formatResponseContents = (error: AxiosError): string|undefined => {
return typeof error.response?.data === 'string'
? error.response?.data
: JSON.stringify(error.response?.data);
};


9 changes: 3 additions & 6 deletions _dev/src/ts/components/ErrorPageBuilder.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { isHttpErrorCode } from '../api/axiosError';
import { ApiError } from '../types/apiTypes';

export default class ErrorPageBuilder {
Expand All @@ -17,7 +18,7 @@ export default class ErrorPageBuilder {
* If code is a HTTP error number (i.e 404, 500 etc.), let's change the text in the left column with it.
*/
public updateLeftColumn(code: ApiError['code']): void {
if (this.#isHttpErrorCode(code)) {
if (isHttpErrorCode(code)) {
const stringifiedCode = (code as number).toString().replaceAll('0', 'O');
const errorCodeSlotElements = this.errorElement.querySelectorAll('.error-page__code-char');
errorCodeSlotElements.forEach((element: Element, index: number) => {
Expand All @@ -34,16 +35,12 @@ export default class ErrorPageBuilder {
public updateDescriptionBlock(errorDetails: Pick<ApiError, 'code' | 'type'>): void {
const errorDescriptionElement = this.errorElement.querySelector('.error-page__desc');
const userFriendlyDescriptionElement = errorDescriptionElement?.querySelector(
`.error-page__desc-${this.#isHttpErrorCode(errorDetails.code) ? errorDetails.code : errorDetails.type}`
`.error-page__desc-${isHttpErrorCode(errorDetails.code) ? errorDetails.code : errorDetails.type}`
);
if (userFriendlyDescriptionElement) {
userFriendlyDescriptionElement.classList.remove('hidden');
} else if (errorDescriptionElement && errorDetails.type) {
errorDescriptionElement.innerHTML = errorDetails.type;
}
}

#isHttpErrorCode(code?: number): boolean {
return typeof code === 'number' && code >= 300 && code.toString().length === 3;
}
}
23 changes: 23 additions & 0 deletions _dev/src/ts/components/LogsViewer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import { parseLogWithSeverity, debounce } from '../utils/logsUtils';
import DomLifecycle from '../types/DomLifecycle';
import { logStore } from '../store/LogStore';
import api from '../api/RequestHandler';
import { ApiError } from '../types/apiTypes';
import { isHttpErrorCode } from '../api/axiosError';
import ErrorPage from '../pages/ErrorPage';

// TODO: clear the debounce on beforeDestroy
export default class LogsViewer extends ComponentAbstract implements DomLifecycle {
Expand Down Expand Up @@ -40,6 +43,11 @@ export default class LogsViewer extends ComponentAbstract implements DomLifecycl
'Logs scroll not found'
);

#additionalContentsPanel = this.queryElement<HTMLPreElement>(
'#log-additional-contents',
'Panel of additional contents not found'
);

get #logsList() {
return this.queryElement<HTMLDivElement>('[data-slot-component="list"]', 'Logs list not found');
}
Expand Down Expand Up @@ -93,6 +101,21 @@ export default class LogsViewer extends ComponentAbstract implements DomLifecycl
this.#scrollToBottom();
};

public addError = (error: ApiError): void => {
const detailedError = document.getElementById(ErrorPage.templateId)?.content?.querySelector(`.error-page__desc .error-page__desc-${isHttpErrorCode(error.code) ? error.code : error.type}`);
if (detailedError) {
this.addLogs([`ERROR - ${detailedError.textContent}`]);
}
this.addLogs([`ERROR - HTTP request failed: Type: ${error.type || 'N/A'} - HTTP Code ${error.code || 'N/A'}`]);

// Contents is added on the DOM in a hidden panel in order to:
// - Display it if requested in the future
// - Have a place to store it for the error report without displaying random contents in the logs
if (error.additionalContents) {
this.#additionalContentsPanel.innerText = error.additionalContents;
}
}

/**
* @private
* @param {string} log - A single log string to be parsed, stored, and added to the DOM.
Expand Down
3 changes: 3 additions & 0 deletions _dev/src/ts/components/ProgressTracker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ export default class ProgressTracker extends ComponentAbstract implements DomLif
this.#logsSummary?.setLogsSummaryText(data.next_desc ?? '');
this.#progressBar?.setProgressPercentage(data.nextParams?.progressPercentage || 0);
this.#logsViewer.addLogs(data.nextQuickInfo);
if (data.apiError) {
this.#logsViewer.addError(data.apiError);
}
};

/**
Expand Down
7 changes: 1 addition & 6 deletions _dev/src/ts/pages/ErrorPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,16 +68,11 @@ export default class ErrorPage extends PageAbstract {

// Store the contents in the logs so it can be used in the error reporting modal
if (event.detail.additionalContents) {
const logsContents =
typeof event.detail.additionalContents === 'object'
? JSON.stringify(event.detail.additionalContents)
: event.detail.additionalContents;

logStore.addLog({
severity: Severity.SUCCESS,
height: 0,
offsetTop: 0,
message: logsContents
message: event.detail.additionalContents
});
}

Expand Down
4 changes: 2 additions & 2 deletions _dev/src/ts/types/apiTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,18 @@ interface ApiResponseAction {
status: string;
next_desc: null | string;
nextQuickInfo: string[];
nextErrors: string[];
nextParams: {
progressPercentage: number;
[key: string]: unknown;
};
apiError?: ApiError;
}

export interface ApiError {
code?: number;
type?: string;
requestParams?: XMLHttpRequest;
additionalContents?: string | object;
additionalContents?: string;
}
export class SilencedApiError extends Error {}

Expand Down
1 change: 0 additions & 1 deletion _dev/tests/api/RequestHandler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@ describe('RequestHandler', () => {
status: 'ok',
next_desc: 'description step',
nextQuickInfo: [],
nextErrors: [],
nextParams: {
progressPercentage: 80
}
Expand Down
1 change: 1 addition & 0 deletions views/templates/components/logs-viewer.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@
</div>
<div data-slot-component="summary" class="logs__summaries"></div>
<div id="{{ download_logs_parent_id }}"></div>
<pre id="log-additional-contents"></pre>
</div>

0 comments on commit b81f5f0

Please sign in to comment.