* new api models * use new PackageResource * do not error out on missing default exports (esmodules auto-execute) * do not check for js extensions (they might have been registered on the client without a js file) * prepend the api baseurl to any relataive server JS dependencies * ignore tsbuildinfo * create base file for tsconfig * extend from base config and optimise include/exclude paths * install rollup plugin to handle json files * use plugin to bundle json files * call script for cms builds that builds libs * add rollup config to utils lib * add a context token to the extension registry instance itself and provide it through BackofficeElement * add rollup node resolve * add node resolve * only include element mixin in element library * add error description to module load error * add types to UmbExtensionRegistry token * set UmbNotificationService as string in its token to avoid minification * correct comment * reverse order of checks * add host to server extensions and support life-cycle check * add imports * use lit rather than lit-html * correct comment * add PackageManifestModel * add import * run libs build for cms * revert reorder * use string name for NotificationContext token * make alias public readonly of UmbContextToken * remove TODO * use UmbContextToken::toString() for all stores * use string alias for contexts * move default data so we avoid importing a big lit library just to get default data interface * add rollup to two extra libraries * make sure we build uui and lit into our libraries for the few cases we import something * add lockfile * add separate options for .js files * add function to install types of module * add types output * remove unused tsconfig-base file for now
112 lines
3.1 KiB
TypeScript
112 lines
3.1 KiB
TypeScript
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
import {
|
|
UmbNotificationOptions,
|
|
UmbNotificationContext,
|
|
UMB_NOTIFICATION_CONTEXT_TOKEN,
|
|
} from '@umbraco-cms/notification';
|
|
import { ApiError, CancelablePromise, ProblemDetailsModel } from '@umbraco-cms/backend-api';
|
|
import { UmbController, UmbControllerHostInterface } from '@umbraco-cms/controller';
|
|
import { UmbContextConsumerController } from '@umbraco-cms/context-api';
|
|
import type { DataSourceResponse } from '@umbraco-cms/models';
|
|
|
|
export class UmbResourceController extends UmbController {
|
|
#promise: Promise<any>;
|
|
|
|
#notificationContext?: UmbNotificationContext;
|
|
|
|
constructor(host: UmbControllerHostInterface, promise: Promise<any>, alias?: string) {
|
|
super(host, alias);
|
|
|
|
this.#promise = promise;
|
|
|
|
new UmbContextConsumerController(host, UMB_NOTIFICATION_CONTEXT_TOKEN, (_instance) => {
|
|
this.#notificationContext = _instance;
|
|
});
|
|
}
|
|
|
|
hostConnected(): void {
|
|
// Do nothing
|
|
}
|
|
|
|
hostDisconnected(): void {
|
|
this.cancel();
|
|
}
|
|
|
|
/**
|
|
* Extract the ProblemDetailsModel object from an ApiError.
|
|
*
|
|
* This assumes that all ApiErrors contain a ProblemDetailsModel object in their body.
|
|
*/
|
|
static toProblemDetailsModel(error: unknown): ProblemDetailsModel | undefined {
|
|
if (error instanceof ApiError) {
|
|
const errorDetails = error.body as ProblemDetailsModel;
|
|
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.
|
|
*/
|
|
static async tryExecute<T>(promise: Promise<T>): Promise<DataSourceResponse<T>> {
|
|
try {
|
|
return {data: await promise};
|
|
} catch (e) {
|
|
return {error: UmbResourceController.toProblemDetailsModel(e)};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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): Promise<DataSourceResponse<T>> {
|
|
const {data, error} = await UmbResourceController.tryExecute<T>(this.#promise);
|
|
|
|
if (error) {
|
|
if (this.#notificationContext) {
|
|
this.#notificationContext?.peek('danger', {
|
|
data: {
|
|
headline: error.title ?? 'Server Error',
|
|
message: error.detail ?? 'Something went wrong'
|
|
}, ...options
|
|
});
|
|
} else {
|
|
console.group('UmbResourceController');
|
|
console.error(error);
|
|
console.groupEnd();
|
|
}
|
|
}
|
|
|
|
return {data, error};
|
|
}
|
|
|
|
/**
|
|
* 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();
|
|
this.cancel();
|
|
}
|
|
}
|