Files
Umbraco-CMS/src/Umbraco.Web.UI.Client/libs/resources/resource.controller.ts

112 lines
3.1 KiB
TypeScript
Raw Normal View History

2022-12-20 07:44:45 +01:00
/* eslint-disable @typescript-eslint/no-explicit-any */
import {
UmbNotificationOptions,
UmbNotificationService,
UmbNotificationDefaultData,
UMB_NOTIFICATION_SERVICE_CONTEXT_TOKEN,
2023-01-23 15:12:48 +01:00
} from '@umbraco-cms/notification';
2022-12-20 07:44:45 +01:00
import { ApiError, CancelablePromise, ProblemDetails } from '@umbraco-cms/backend-api';
import { UmbController, UmbControllerHostInterface } from '@umbraco-cms/controller';
import { UmbContextConsumerController } from '@umbraco-cms/context-api';
2023-01-02 14:59:29 +01:00
export class UmbResourceController extends UmbController {
2023-01-02 13:27:35 +01:00
#promise: Promise<any>;
2022-12-20 07:44:45 +01:00
#notificationService?: UmbNotificationService;
constructor(host: UmbControllerHostInterface, promise: Promise<any>, alias?: string) {
super(host, alias);
2023-01-02 13:27:35 +01:00
this.#promise = promise;
new UmbContextConsumerController(host, UMB_NOTIFICATION_SERVICE_CONTEXT_TOKEN, (_instance) => {
this.#notificationService = _instance;
});
2022-12-20 07:44:45 +01:00
}
hostConnected(): void {
// Do nothing
2022-12-20 07:44:45 +01:00
}
hostDisconnected(): void {
2023-01-02 13:27:35 +01:00
this.cancel();
2022-12-20 07:44:45 +01:00
}
/**
2023-01-04 10:23:22 +01:00
* Extract the ProblemDetails object from an ApiError.
*
* This assumes that all ApiErrors contain a ProblemDetails object in their body.
*/
static toProblemDetails(error: unknown): ProblemDetails | undefined {
if (error instanceof ApiError) {
const errorDetails = error.body as ProblemDetails;
return errorDetails;
} else if (error instanceof Error) {
return {
title: error.name,
detail: error.message,
};
}
return undefined;
}
/**
* Base execute function with a try/catch block and return a tuple with the result and the error.
2022-12-20 07:44:45 +01:00
*/
2023-01-04 10:23:22 +01:00
static async tryExecute<T>(promise: Promise<T>): Promise<{ data?: T; error?: ProblemDetails }> {
2022-12-20 07:44:45 +01:00
try {
2023-01-04 10:23:22 +01:00
return { data: await promise };
2022-12-20 07:44:45 +01:00
} catch (e) {
2023-01-04 10:23:22 +01:00
return { error: UmbResourceController.toProblemDetails(e) };
2022-12-20 07:44:45 +01:00
}
}
/**
* Wrap the {execute} function in a try/catch block and return the result.
* If the executor function throws an error, then show the details in a notification.
*/
async tryExecuteAndNotify<T>(options?: UmbNotificationOptions<any>): Promise<{ data?: T; error?: ProblemDetails }> {
2023-01-04 10:23:22 +01:00
const { data, error } = await UmbResourceController.tryExecute<T>(this.#promise);
2022-12-20 07:44:45 +01:00
if (error) {
const data: UmbNotificationDefaultData = {
headline: error.title ?? 'Server Error',
message: error.detail ?? 'Something went wrong',
};
if (this.#notificationService) {
this.#notificationService?.peek('danger', { data, ...options });
} else {
console.group('UmbResourceController');
console.error(error);
console.groupEnd();
}
}
return { data, error };
2022-12-20 07:44:45 +01:00
}
2023-01-02 13:27:35 +01:00
/**
* Cancel all resources that are currently being executed by this controller if they are cancelable.
*
* This works by checking if the promise is a CancelablePromise and if so, it will call the cancel method.
*
* This is useful when the controller is being disconnected from the DOM.
*
* @see CancelablePromise
* @see https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal
* @see https://developer.mozilla.org/en-US/docs/Web/API/AbortController
*/
cancel() {
if (this.#promise instanceof CancelablePromise) {
this.#promise.cancel();
}
}
destroy() {
super.destroy();
2023-01-02 13:27:35 +01:00
this.cancel();
}
2022-12-20 07:44:45 +01:00
}