From 6b5510771e07f4748351025c0239a60db3aaddda Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 3 Apr 2025 15:52:47 +0200 Subject: [PATCH 01/45] build(deps-dev): bump @hey-api/openapi-ts from 0.61.3 to 0.66.1 --- .../devops/openapi-ts/openapi-ts.config.js | 2 +- src/Umbraco.Web.UI.Client/package-lock.json | 18 +++++++++--------- src/Umbraco.Web.UI.Client/package.json | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/devops/openapi-ts/openapi-ts.config.js b/src/Umbraco.Web.UI.Client/devops/openapi-ts/openapi-ts.config.js index a86fd9d42689..0e27431a11bc 100644 --- a/src/Umbraco.Web.UI.Client/devops/openapi-ts/openapi-ts.config.js +++ b/src/Umbraco.Web.UI.Client/devops/openapi-ts/openapi-ts.config.js @@ -1,7 +1,6 @@ import { defineConfig } from '@hey-api/openapi-ts'; export default defineConfig({ - client: 'legacy/fetch', debug: true, input: '../Umbraco.Cms.Api.Management/OpenApi.json', output: { @@ -10,6 +9,7 @@ export default defineConfig({ lint: 'eslint', }, plugins: [ + 'legacy/fetch', { name: '@hey-api/typescript', enums: 'typescript' diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index 03e0d8873fa1..775e79b7e98f 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/package-lock.json @@ -42,7 +42,7 @@ "devDependencies": { "@babel/core": "^7.26.9", "@eslint/js": "^9.20.0", - "@hey-api/openapi-ts": "^0.61.3", + "@hey-api/openapi-ts": "^0.66.1", "@open-wc/testing": "^4.0.0", "@playwright/test": "^1.49.1", "@rollup/plugin-json": "^6.1.0", @@ -1032,9 +1032,9 @@ "license": "BSD-3-Clause" }, "node_modules/@hey-api/json-schema-ref-parser": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@hey-api/json-schema-ref-parser/-/json-schema-ref-parser-1.0.1.tgz", - "integrity": "sha512-dBt0A7op9kf4BcK++x6HBYDmvCvnJUZEGe5QytghPFHnMXPyKwDKomwL/v5e9ERk6E0e1GzL/e/y6pWUso9zrQ==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@hey-api/json-schema-ref-parser/-/json-schema-ref-parser-1.0.4.tgz", + "integrity": "sha512-IaJ4yFgU5r63KZyeySHRKSM1bavFIda8KdwCFi5BxQCIklltzEByBksNOPms+yHXpWWfR+OopIusVZV8roycYg==", "dev": true, "license": "MIT", "dependencies": { @@ -1050,13 +1050,13 @@ } }, "node_modules/@hey-api/openapi-ts": { - "version": "0.61.3", - "resolved": "https://registry.npmjs.org/@hey-api/openapi-ts/-/openapi-ts-0.61.3.tgz", - "integrity": "sha512-Ls9MBRa5+vg7UHw6fIcfdgcCyZ9vKtRw63nWxwF9zjJIPlzVOZO6xKuzGmDc6o0Pb6XCdTz6lPV5hcV0R4b/ag==", + "version": "0.66.1", + "resolved": "https://registry.npmjs.org/@hey-api/openapi-ts/-/openapi-ts-0.66.1.tgz", + "integrity": "sha512-Mol3vuM76d8F7xLvcZi502UxM3taXy7TDZzZfFpUMT+VLMdPiGd9cTWfKfQde0kgi+SZPjejvqmad1zk4EIu7A==", "dev": true, "license": "MIT", "dependencies": { - "@hey-api/json-schema-ref-parser": "1.0.1", + "@hey-api/json-schema-ref-parser": "1.0.4", "c12": "2.0.1", "commander": "13.0.0", "handlebars": "4.7.8" @@ -1065,7 +1065,7 @@ "openapi-ts": "bin/index.cjs" }, "engines": { - "node": "^18.20.5 || ^20.11.1 || >=22.11.0" + "node": "^18.18.0 || ^20.9.0 || >=22.10.0" }, "funding": { "url": "https://github.com/sponsors/hey-api" diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index dd899bb926d4..b280f2de3d5d 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/package.json @@ -230,7 +230,7 @@ "devDependencies": { "@babel/core": "^7.26.9", "@eslint/js": "^9.20.0", - "@hey-api/openapi-ts": "^0.61.3", + "@hey-api/openapi-ts": "^0.66.1", "@open-wc/testing": "^4.0.0", "@playwright/test": "^1.49.1", "@rollup/plugin-json": "^6.1.0", From 4d0f64a31e3c677c46649257d9e6bbf4c9e21d00 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 3 Apr 2025 15:53:03 +0200 Subject: [PATCH 02/45] docs: adds information on how to configure new fetch-client --- .../src/packages/core/auth/auth.context.ts | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/auth/auth.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/auth/auth.context.ts index d4f68271205d..470f1f776cdf 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/auth/auth.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/auth/auth.context.ts @@ -233,17 +233,18 @@ export class UmbAuthContext extends UmbContextBase { /** * Get the default OpenAPI configuration, which is set up to communicate with the Management API. - * @remark This is useful if you want to communicate with your own resources generated by the [openapi-typescript-codegen](https://github.com/ferdikoomen/openapi-typescript-codegen) library. + * @remark This is useful if you want to communicate with your own resources generated by the [@hey-api/openapi-ts](https://github.com/hey-api/openapi-ts) library. * @memberof UmbAuthContext * @example Using the default OpenAPI configuration * ```js - * const defaultOpenApi = authContext.getOpenApiConfiguration(); - * OpenAPI.BASE = defaultOpenApi.base; - * OpenAPI.WITH_CREDENTIALS = defaultOpenApi.withCredentials; - * OpenAPI.CREDENTIALS = defaultOpenApi.credentials; - * OpenAPI.TOKEN = defaultOpenApi.token; + * const defaultOpenApi = authContext.getOpenApiConfiguration(); + * client.setConfig({ + * base: defaultOpenApi.base, + * credentials: defaultOpenApi.credentials, + * auth: defaultOpenApi.token, + * }); * ``` - * @returns The default OpenAPI configuration + * @returns {UmbOpenApiConfiguration} The default OpenAPI configuration */ getOpenApiConfiguration(): UmbOpenApiConfiguration { return { From 6181682e7d95eb09625388a0714bc3faa21bfa51 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 3 Apr 2025 16:43:42 +0200 Subject: [PATCH 03/45] feat: adds preliminary umb-prefixed error types --- .../data-source-response.interface.ts | 8 +- .../resources/apiTypeValidators.function.ts | 28 ++- .../core/resources/resource.controller.ts | 205 +++++++++++++----- .../src/packages/core/resources/types.ts | 11 + .../src/packages/core/resources/umb-error.ts | 46 ++++ 5 files changed, 234 insertions(+), 64 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/resources/umb-error.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/repository/data-source-response.interface.ts b/src/Umbraco.Web.UI.Client/src/packages/core/repository/data-source-response.interface.ts index acfe5fb5c374..181b80fb4656 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/repository/data-source-response.interface.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/repository/data-source-response.interface.ts @@ -1,16 +1,12 @@ -import type { ApiError, CancelError } from '@umbraco-cms/backoffice/external/backend-api'; +import type { UmbApiError, UmbCancelError, UmbError } from '../resources/umb-error.js'; export interface UmbDataSourceResponse extends UmbDataSourceErrorResponse { data?: T; } export interface UmbDataSourceErrorResponse { - // TODO: we should not rely on the ApiError and CancelError types from the backend-api package - // We need to be able to return a generic error type that can be used in the frontend - // Example: the clipboard is getting is data from local storage, so it should not use the ApiError type /** * The error that occurred when fetching the data. - * The {ApiError} and {CancelError} types will change in the future to be a more generic error type. */ - error?: ApiError | CancelError; + error?: UmbError | UmbApiError | UmbCancelError | Error; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/resources/apiTypeValidators.function.ts b/src/Umbraco.Web.UI.Client/src/packages/core/resources/apiTypeValidators.function.ts index bb5ba98d230e..b8a381fd074f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/resources/apiTypeValidators.function.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/resources/apiTypeValidators.function.ts @@ -1,25 +1,35 @@ -import type { ApiError, CancelError, CancelablePromise } from '@umbraco-cms/backoffice/external/backend-api'; +import type { ApiError, CancelError } from '@umbraco-cms/backoffice/external/backend-api'; +import { UmbDeprecation } from '../utils'; /** - * - * @param error + * Checks if the given error is an instance of ApiError. + * @deprecated Use {UmbApiError.isUmbApiError} instead and map your object to {UmbApiError} if needed. */ export function isApiError(error: unknown): error is ApiError { + new UmbDeprecation({ + deprecated: 'isApiError', + removeInVersion: '18.0.0', + solution: 'Use UmbApiError.isUmbApiError instead', + }); return (error as ApiError).name === 'ApiError'; } /** - * - * @param error + * Checks if the given error is an instance of CancelError. + * @deprecated Use {UmbApiCancelError.isUmbApiCancelError}` instead and map your object to {UmbApiCancelError} if needed. */ export function isCancelError(error: unknown): error is CancelError { + new UmbDeprecation({ + deprecated: 'isCancelError', + removeInVersion: '18.0.0', + solution: 'Use UmbApiCancelError.isUmbApiCancelError instead', + }); return (error as CancelError).name === 'CancelError'; } /** - * - * @param promise + * Checks if the given promise is cancelable, i.e. if it has a cancel method. */ -export function isCancelablePromise(promise: unknown): promise is CancelablePromise { - return (promise as CancelablePromise)?.cancel !== undefined; +export function isCancelablePromise(promise: unknown): promise is Promise & { cancel: () => void } { + return typeof (promise as Promise & { cancel: () => void }).cancel === 'function'; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/resources/resource.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/resources/resource.controller.ts index dc728b4291de..7125bdb5de03 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/resources/resource.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/resources/resource.controller.ts @@ -1,6 +1,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { UMB_AUTH_CONTEXT } from '../auth/index.js'; import { UmbDeprecation } from '../utils/deprecation/deprecation.js'; +import { UmbApiError, UmbCancelError, type UmbError } from './umb-error.js'; import { isApiError, isCancelError, isCancelablePromise } from './apiTypeValidators.function.js'; import type { XhrRequestOptions } from './types.js'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; @@ -23,17 +24,149 @@ export class UmbResourceController extends UmbControllerBase { this.#promise = promise; } + /** + * Handle errors + * @internal + * @param {any} error The error to handle. + * @returns {UmbDataSourceResponse} The response object with the error. + * @template T The type of the data returned from the API. + */ + async handleUmbErrors(error: UmbError | UmbApiError | UmbCancelError | Error): Promise> { + if (!error) return {}; + + /** + * Determine if we want to show a notification or just log the error to the console. + * If the error is not a recognizable system error (i.e. a HttpError), then we will show a notification + * with the error details using the default notification options. + */ + if (UmbCancelError.isUmbCancelError(error)) { + // Cancelled - do nothing + return { error }; + } else if (UmbApiError.isUmbApiError(error)) { + // UmbApiError - handle it + return this.handleUmbApiError(error); + } else { + // UmbError or unknown error - log it to the console + console.error('Error occured', error); + return { error }; + } + } + + /** + * Handle errors from the API and show a notification if needed. + * @internal + * @param {UmbApiError} error The error to handle. + * @param {boolean} shouldNotify Whether to show a notification or not. Default is false. + * @returns {UmbDataSourceResponse} The response object with the error. + * @template T The type of the data returned from the API. + * @see {@link UmbResourceController.handleUmbErrors} + */ + async handleUmbApiError(error: UmbApiError, shouldNotify: boolean = false): Promise> { + console.groupCollapsed('UmbError caught in UmbResourceController'); + console.error('Request failed', error.request); + console.error('Error', error); + console.groupEnd(); + + /** + * Check if the operation status ends with `ByNotification` and if so, don't show a notification + * This is a special case where the operation was cancelled by the server and the client gets a notification header instead. + */ + if ( + error.problemDetails.operationStatus && + typeof error.problemDetails.operationStatus === 'string' && + error.problemDetails.operationStatus.endsWith('ByNotification') + ) { + shouldNotify = false; + } + + // Go through the error status codes and act accordingly + switch (error.status ?? 0) { + case 401: { + // See if we can get the UmbAuthContext and let it know the user is timed out + const authContext = await this.getContext(UMB_AUTH_CONTEXT, { preventTimeout: true }); + if (!authContext) { + throw new Error('Could not get the auth context'); + } + authContext.timeOut(); + break; + } + case 500: + // Server Error + + if (shouldNotify) { + let headline = error.problemDetails.title ?? error.name ?? 'Server Error'; + let message = 'A fatal server error occurred. If this continues, please reach out to your administrator.'; + + // Special handling for ObjectCacheAppCache corruption errors, which we are investigating + if ( + error.problemDetails.detail?.includes('ObjectCacheAppCache') || + error.problemDetails.detail?.includes('Umbraco.Cms.Infrastructure.Scoping.Scope.DisposeLastScope()') + ) { + headline = 'Please restart the server'; + message = + 'The Umbraco object cache is corrupt, but your action may still have been executed. Please restart the server to reset the cache. This is a work in progress.'; + } + + this.#peekError(headline, message, error.problemDetails.errors ?? error.problemDetails.detail); + } + break; + default: + // Other errors + if (shouldNotify) { + const headline = error.problemDetails.title ?? error.name ?? 'Server Error'; + this.#peekError('', headline, error.problemDetails.errors ?? error.problemDetails.detail); + } + } + + return { error }; + } + + async #peekError(headline: string, message: string, details: unknown) { + // This late importing is done to avoid circular reference [NL] + (await import('@umbraco-cms/backoffice/notification')).umbPeekError(this, { + headline, + message, + details, + }); + } + + /** + * 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(): void { + if (isCancelablePromise(this.#promise)) { + this.#promise.cancel(); + } + } + override hostDisconnected(): void { super.hostDisconnected(); this.cancel(); } + override destroy(): void { + super.destroy(); + this.cancel(); + } + /** * Base execute function with a try/catch block and return a tuple with the result and the error. + * @deprecated This method is deprecated and will be removed in Umbraco 18. Use the {UmbTryExecuteController} instead. * @param promise */ static async tryExecute(promise: Promise): Promise> { - // TODO: tryExecute should not take a promise as argument, but should utilize the class property `#promise` instead. (In this way the promise can be cancelled when disconnected) + new UmbDeprecation({ + deprecated: 'UmbResourceController.tryExecute', + removeInVersion: '18.0.0', + solution: 'Use the UmbTryExecuteController instead.', + }); try { return { data: await promise }; } catch (error) { @@ -49,11 +182,17 @@ export class UmbResourceController extends UmbControllerBase { /** * Wrap the {tryExecute} function in a try/catch block and return the result. * If the executor function throws an error, then show the details in a notification. - * @param _options + * @deprecated This method is deprecated and will be removed in Umbraco 18. Use the {UmbTryExecuteAndNotifyController} instead. * @param options */ async tryExecuteAndNotify(options?: UmbNotificationOptions): Promise> { + new UmbDeprecation({ + deprecated: 'UmbResourceController.tryExecuteAndNotify', + removeInVersion: '18.0.0', + solution: 'Use the UmbTryExecuteAndNotifyController instead.', + }); + const { data, error } = await UmbResourceController.tryExecute(this.#promise); if (options) { @@ -72,8 +211,8 @@ export class UmbResourceController extends UmbControllerBase { */ if (isCancelError(error)) { // Cancelled - do nothing - return { error }; - } else { + return { error: new UmbCancelError(error.message) }; + } else if (isApiError(error)) { console.groupCollapsed('ApiError caught in UmbResourceController'); console.error('Request failed', error.request); console.error('Request body', error.body); @@ -132,38 +271,20 @@ export class UmbResourceController extends UmbControllerBase { 'The Umbraco object cache is corrupt, but your action may still have been executed. Please restart the server to reset the cache. This is a work in progress.'; } - // This late importing is done to avoid circular reference [NL] - (await import('@umbraco-cms/backoffice/notification')).umbPeekError(this, { - headline: headline, - message: message, - details: problemDetails?.errors ?? problemDetails?.detail, - }); + this.#peekError(headline, message, problemDetails?.errors ?? problemDetails?.detail); } break; default: // Other errors if (!isCancelledByNotification) { - /* - const notificationContext = await this.getContext(UMB_NOTIFICATION_CONTEXT); - notificationContext.peek('danger', { - data: { - headline: problemDetails?.title ?? error.name ?? 'Server Error', - message: problemDetails?.detail ?? error.message ?? 'Something went wrong', - structuredList: problemDetails?.errors - ? (problemDetails.errors as Record>) - : undefined, - }, - ...options, - }); - */ const headline = problemDetails?.title ?? error.name ?? 'Server Error'; - // This late importing is done to avoid circular reference [NL] - (await import('@umbraco-cms/backoffice/notification')).umbPeekError(this, { - message: headline, - details: problemDetails?.errors ?? problemDetails?.detail, - }); + this.#peekError('', headline, problemDetails?.errors ?? problemDetails?.detail); } } + } else { + // UmbError or unknown error - log it to the console + console.error('Unknown error', error); + this.#peekError('Error', error.message ?? 'Unknown error', []); } } @@ -172,10 +293,17 @@ export class UmbResourceController extends UmbControllerBase { /** * Make an XHR request. + * @deprecated This method is deprecated and will be removed in Umbraco 18. Use the {UmbXhrRequestController} instead. * @param host The controller host for this controller to be appended to. * @param options The options for the XHR request. */ static xhrRequest(options: XhrRequestOptions): CancelablePromise { + new UmbDeprecation({ + deprecated: 'UmbResourceController.xhrRequest', + removeInVersion: '18.0.0', + solution: 'Use the UmbXhrRequestController instead.', + }); + const baseUrl = options.baseUrl || '/umbraco'; const promise = new CancelablePromise(async (resolve, reject, onCancel) => { @@ -286,25 +414,4 @@ export class UmbResourceController extends UmbControllerBase { return promise; } - - /** - * 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(): void { - if (isCancelablePromise(this.#promise)) { - this.#promise.cancel(); - } - } - - override destroy(): void { - super.destroy(); - this.cancel(); - } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/resources/types.ts b/src/Umbraco.Web.UI.Client/src/packages/core/resources/types.ts index 8b5c34c3f18a..d07b39731b13 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/resources/types.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/resources/types.ts @@ -9,3 +9,14 @@ export interface XhrRequestOptions { onProgress?: (event: ProgressEvent) => void; abortSignal?: AbortSignal; } + +export interface UmbProblemDetails { + type: string; + title: string; + status: number; + detail?: string; + instance?: string; + operationStatus?: string; + extensions?: Record; + errors?: Record; +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/resources/umb-error.ts b/src/Umbraco.Web.UI.Client/src/packages/core/resources/umb-error.ts new file mode 100644 index 000000000000..b4f6e76f8fa4 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/resources/umb-error.ts @@ -0,0 +1,46 @@ +import type { UmbProblemDetails } from './types.js'; + +export class UmbError extends Error { + public override name = 'UmbError'; + + public static isUmbError(error: unknown): error is UmbError { + return error instanceof UmbError || (error as UmbError).name === 'UmbError'; + } +} + +export class UmbCancelError extends UmbError { + public override name = 'UmbCancelError'; + + public static isUmbCancelError(error: unknown): error is UmbCancelError { + return error instanceof UmbCancelError || (error as UmbCancelError).name === 'UmbCancelError'; + } +} + +/** + * UmbApiError is a class that extends UmbError and represents an error that occurs during an API call. + */ +export class UmbApiError extends UmbError { + public override name = 'UmbApiError'; + public status: number; + public response: unknown; + public request: unknown; + public problemDetails: UmbProblemDetails; + + public constructor( + message: string, + status: number, + response: unknown, + request: unknown, + problemDetails: UmbProblemDetails, + ) { + super(message); + this.status = status; + this.response = response; + this.request = request; + this.problemDetails = problemDetails; + } + + public static isUmbApiError(error: unknown): error is UmbApiError { + return error instanceof UmbApiError || (error as UmbApiError).name === 'UmbApiError'; + } +} From 5c13befb635c841e5efcdf3af7848269efdc14cf Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 3 Apr 2025 16:45:59 +0200 Subject: [PATCH 04/45] fix: uses correct import path --- .../src/packages/core/resources/apiTypeValidators.function.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/resources/apiTypeValidators.function.ts b/src/Umbraco.Web.UI.Client/src/packages/core/resources/apiTypeValidators.function.ts index b8a381fd074f..99461e3ede19 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/resources/apiTypeValidators.function.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/resources/apiTypeValidators.function.ts @@ -1,5 +1,5 @@ import type { ApiError, CancelError } from '@umbraco-cms/backoffice/external/backend-api'; -import { UmbDeprecation } from '../utils'; +import { UmbDeprecation } from '@umbraco-cms/backoffice/utils'; /** * Checks if the given error is an instance of ApiError. From b98d06b06a3e3e13d939a33c99b958d5eebbcfdb Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 3 Apr 2025 16:46:30 +0200 Subject: [PATCH 05/45] docs: jsdocs --- .../src/packages/core/resources/resource.controller.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/resources/resource.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/resources/resource.controller.ts index 7125bdb5de03..a942fabfa593 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/resources/resource.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/resources/resource.controller.ts @@ -59,7 +59,7 @@ export class UmbResourceController extends UmbControllerBase { * @param {boolean} shouldNotify Whether to show a notification or not. Default is false. * @returns {UmbDataSourceResponse} The response object with the error. * @template T The type of the data returned from the API. - * @see {@link UmbResourceController.handleUmbErrors} + * @see {@link UmbResourceController.handleUmbErrors} */ async handleUmbApiError(error: UmbApiError, shouldNotify: boolean = false): Promise> { console.groupCollapsed('UmbError caught in UmbResourceController'); From 42daeeebf4b44522bca25be68fa7b709803fa724 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 3 Apr 2025 16:46:59 +0200 Subject: [PATCH 06/45] feat: optimises error reporting --- .../src/packages/core/resources/resource.controller.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/resources/resource.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/resources/resource.controller.ts index a942fabfa593..fe0e28358815 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/resources/resource.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/resources/resource.controller.ts @@ -62,10 +62,7 @@ export class UmbResourceController extends UmbControllerBase { * @see {@link UmbResourceController.handleUmbErrors} */ async handleUmbApiError(error: UmbApiError, shouldNotify: boolean = false): Promise> { - console.groupCollapsed('UmbError caught in UmbResourceController'); - console.error('Request failed', error.request); - console.error('Error', error); - console.groupEnd(); + console.error('UmbError caught in UmbResourceController', error); /** * Check if the operation status ends with `ByNotification` and if so, don't show a notification From b385c5bd8b2e5768cd0b63a4fbd6c5cb3ffa5cba Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 3 Apr 2025 17:32:12 +0200 Subject: [PATCH 07/45] feat: maps functions into separate controllers --- .../core/resources/cancelable-promise.ts | 117 ++++++++++++++++++ .../core/resources/resource.controller.ts | 36 ++++-- .../try-execute-and-notify.controller.ts | 17 +++ .../core/resources/try-execute.controller.ts | 17 +++ .../resources/try-xhr-request.controller.ts | 115 +++++++++++++++++ .../core/resources/tryExecute.function.ts | 10 +- .../resources/tryExecuteAndNotify.function.ts | 11 +- .../core/resources/tryXhrRequest.function.ts | 19 ++- .../src/packages/core/resources/umb-error.ts | 39 ++++-- 9 files changed, 348 insertions(+), 33 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/resources/cancelable-promise.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/resources/try-execute-and-notify.controller.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/resources/try-execute.controller.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/resources/try-xhr-request.controller.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/resources/cancelable-promise.ts b/src/Umbraco.Web.UI.Client/src/packages/core/resources/cancelable-promise.ts new file mode 100644 index 000000000000..a3454d7c6adf --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/resources/cancelable-promise.ts @@ -0,0 +1,117 @@ +import { UmbCancelError } from './umb-error.js'; + +export interface OnCancel { + readonly isResolved: boolean; + readonly isRejected: boolean; + readonly isCancelled: boolean; + + (cancelHandler: () => void): void; +} + +export class UmbCancelablePromise implements Promise { + private _isResolved: boolean; + private _isRejected: boolean; + private _isCancelled: boolean; + readonly cancelHandlers: (() => void)[]; + readonly promise: Promise; + private _resolve?: (value: T | PromiseLike) => void; + private _reject?: (reason?: unknown) => void; + + constructor( + executor: ( + resolve: (value: T | PromiseLike) => void, + reject: (reason?: unknown) => void, + onCancel: OnCancel, + ) => void, + ) { + this._isResolved = false; + this._isRejected = false; + this._isCancelled = false; + this.cancelHandlers = []; + this.promise = new Promise((resolve, reject) => { + this._resolve = resolve; + this._reject = reject; + + const onResolve = (value: T | PromiseLike): void => { + if (this._isResolved || this._isRejected || this._isCancelled) { + return; + } + this._isResolved = true; + if (this._resolve) this._resolve(value); + }; + + const onReject = (reason?: unknown): void => { + if (this._isResolved || this._isRejected || this._isCancelled) { + return; + } + this._isRejected = true; + if (this._reject) this._reject(reason); + }; + + const onCancel = (cancelHandler: () => void): void => { + if (this._isResolved || this._isRejected || this._isCancelled) { + return; + } + this.cancelHandlers.push(cancelHandler); + }; + + Object.defineProperty(onCancel, 'isResolved', { + get: (): boolean => this._isResolved, + }); + + Object.defineProperty(onCancel, 'isRejected', { + get: (): boolean => this._isRejected, + }); + + Object.defineProperty(onCancel, 'isCancelled', { + get: (): boolean => this._isCancelled, + }); + + return executor(onResolve, onReject, onCancel as OnCancel); + }); + } + + get [Symbol.toStringTag]() { + return 'Cancellable Promise'; + } + + public then( + onFulfilled?: ((value: T) => TResult1 | PromiseLike) | null, + onRejected?: ((reason: unknown) => TResult2 | PromiseLike) | null, + ): Promise { + return this.promise.then(onFulfilled, onRejected); + } + + public catch( + onRejected?: ((reason: unknown) => TResult | PromiseLike) | null, + ): Promise { + return this.promise.catch(onRejected); + } + + public finally(onFinally?: (() => void) | null): Promise { + return this.promise.finally(onFinally); + } + + public cancel(): void { + if (this._isResolved || this._isRejected || this._isCancelled) { + return; + } + this._isCancelled = true; + if (this.cancelHandlers.length) { + try { + for (const cancelHandler of this.cancelHandlers) { + cancelHandler(); + } + } catch (error) { + console.warn('Cancellation threw an error', error); + return; + } + } + this.cancelHandlers.length = 0; + if (this._reject) this._reject(new UmbCancelError('Request aborted')); + } + + public get isCancelled(): boolean { + return this._isCancelled; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/resources/resource.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/resources/resource.controller.ts index fe0e28358815..3b1575fb6736 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/resources/resource.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/resources/resource.controller.ts @@ -1,9 +1,10 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { UMB_AUTH_CONTEXT } from '../auth/index.js'; import { UmbDeprecation } from '../utils/deprecation/deprecation.js'; -import { UmbApiError, UmbCancelError, type UmbError } from './umb-error.js'; +import { UmbApiError, UmbCancelError, UmbError } from './umb-error.js'; import { isApiError, isCancelError, isCancelablePromise } from './apiTypeValidators.function.js'; import type { XhrRequestOptions } from './types.js'; +import type { UmbCancelablePromise } from './cancelable-promise.js'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; import type { UmbNotificationOptions } from '@umbraco-cms/backoffice/notification'; @@ -16,22 +17,27 @@ import { } from '@umbraco-cms/backoffice/external/backend-api'; export class UmbResourceController extends UmbControllerBase { - #promise: Promise; + /** + * The promise that is being executed. + * @protected + */ + protected _promise: UmbCancelablePromise | CancelablePromise | Promise; - constructor(host: UmbControllerHost, promise: Promise, alias?: string) { + constructor(host: UmbControllerHost, promise: Promise, alias?: string) { super(host, alias); - this.#promise = promise; + this._promise = promise; } /** * Handle errors * @internal * @param {any} error The error to handle. + * @param {boolean} shouldNotify Whether to show a notification or not. Default is false. * @returns {UmbDataSourceResponse} The response object with the error. * @template T The type of the data returned from the API. */ - async handleUmbErrors(error: UmbError | UmbApiError | UmbCancelError | Error): Promise> { + async handleUmbErrors(error: unknown, shouldNotify: boolean = false): Promise> { if (!error) return {}; /** @@ -44,11 +50,21 @@ export class UmbResourceController extends UmbControllerBase { return { error }; } else if (UmbApiError.isUmbApiError(error)) { // UmbApiError - handle it - return this.handleUmbApiError(error); - } else { + return this.handleUmbApiError(error, shouldNotify); + } else if (error instanceof Error) { // UmbError or unknown error - log it to the console console.error('Error occured', error); + if (shouldNotify) { + this.#peekError('Error', error.message ?? 'Unknown error', []); + } return { error }; + } else { + // Unknown error - log it to the console + console.error('Unknown error', error); + if (shouldNotify) { + this.#peekError('Error', 'Unknown error', []); + } + return { error: new UmbError('Unknown error') }; } } @@ -138,8 +154,8 @@ export class UmbResourceController extends UmbControllerBase { * @see https://developer.mozilla.org/en-US/docs/Web/API/AbortController */ cancel(): void { - if (isCancelablePromise(this.#promise)) { - this.#promise.cancel(); + if (isCancelablePromise(this._promise)) { + this._promise.cancel(); } } @@ -190,7 +206,7 @@ export class UmbResourceController extends UmbControllerBase { solution: 'Use the UmbTryExecuteAndNotifyController instead.', }); - const { data, error } = await UmbResourceController.tryExecute(this.#promise); + const { data, error } = await UmbResourceController.tryExecute(this._promise); if (options) { new UmbDeprecation({ diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/resources/try-execute-and-notify.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/resources/try-execute-and-notify.controller.ts new file mode 100644 index 000000000000..da4be68b49b2 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/resources/try-execute-and-notify.controller.ts @@ -0,0 +1,17 @@ +import { isApiError, isCancelError } from './apiTypeValidators.function.js'; +import { UmbResourceController } from './resource.controller.js'; +import { UmbApiError, UmbCancelError } from './umb-error.js'; +import type { UmbDataSourceResponse } from '@umbraco-cms/backoffice/repository'; + +export class UmbTryExecuteAndNotifyController extends UmbResourceController { + override async tryExecuteAndNotify(): Promise> { + try { + return { data: await this._promise }; + } catch (error) { + // Error might be a legacy error, so we need to check if it is an UmbError + let umbError = isApiError(error) ? UmbApiError.fromLegacyApiError(error) : error; + umbError = isCancelError(umbError) ? UmbCancelError.fromLegacyCancelError(umbError) : umbError; + return this.handleUmbErrors(umbError, true); + } + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/resources/try-execute.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/resources/try-execute.controller.ts new file mode 100644 index 000000000000..c2db39ccb81e --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/resources/try-execute.controller.ts @@ -0,0 +1,17 @@ +import { UmbResourceController } from './resource.controller.js'; +import { UmbApiError, UmbCancelError } from './umb-error.js'; +import { isApiError, isCancelError } from './apiTypeValidators.function.js'; +import type { UmbDataSourceResponse } from '@umbraco-cms/backoffice/repository'; + +export class UmbTryExecuteController extends UmbResourceController { + async tryExecute(): Promise> { + try { + return { data: await this._promise }; + } catch (error) { + // Error might be a legacy error, so we need to check if it is an UmbError + let umbError = isApiError(error) ? UmbApiError.fromLegacyApiError(error) : error; + umbError = isCancelError(umbError) ? UmbCancelError.fromLegacyCancelError(umbError) : umbError; + return this.handleUmbErrors(umbError); + } + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/resources/try-xhr-request.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/resources/try-xhr-request.controller.ts new file mode 100644 index 000000000000..b48031b55ed7 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/resources/try-xhr-request.controller.ts @@ -0,0 +1,115 @@ +import { isApiError, isCancelError } from './apiTypeValidators.function.js'; +import { UmbCancelablePromise } from './cancelable-promise.js'; +import { UmbResourceController } from './resource.controller.js'; +import type { XhrRequestOptions } from './types.js'; +import { UmbApiError, UmbCancelError } from './umb-error.js'; +import type { UmbDataSourceResponse } from '@umbraco-cms/backoffice/repository'; + +export class UmbTryXhrRequestController extends UmbResourceController { + #abortSignal?: AbortSignal; + + async tryXhrRequest(abortSignal?: AbortSignal): Promise> { + try { + if (abortSignal) { + this.#abortSignal = abortSignal; + this.#abortSignal.addEventListener('abort', this.cancel, { once: true }); + } + return { data: await this._promise }; + } catch (error) { + // Error might be a legacy error, so we need to check if it is an UmbError + let umbError = isApiError(error) ? UmbApiError.fromLegacyApiError(error) : error; + umbError = isCancelError(umbError) ? UmbCancelError.fromLegacyCancelError(umbError) : umbError; + return this.handleUmbErrors(umbError); + } + } + + static createXhrRequest(options: XhrRequestOptions): UmbCancelablePromise { + const baseUrl = options.baseUrl || '/umbraco'; + + return new UmbCancelablePromise(async (resolve, reject, onCancel) => { + const xhr = new XMLHttpRequest(); + xhr.open(options.method, `${baseUrl}${options.url}`, true); + + // Set default headers + if (options.token) { + const token = typeof options.token === 'function' ? await options.token() : options.token; + if (token) { + xhr.setRequestHeader('Authorization', `Bearer ${token}`); + } + } + + // Infer Content-Type header based on body type + if (options.body instanceof FormData) { + // Note: 'multipart/form-data' is automatically set by the browser for FormData + } else { + xhr.setRequestHeader('Content-Type', 'application/json'); + } + + // Set custom headers + if (options.headers) { + for (const [key, value] of Object.entries(options.headers)) { + xhr.setRequestHeader(key, value); + } + } + + xhr.upload.onprogress = (event) => { + if (options.onProgress) { + options.onProgress(event); + } + }; + + xhr.onload = () => { + try { + if (xhr.status >= 200 && xhr.status < 300) { + if (options.responseHeader) { + const response = xhr.getResponseHeader(options.responseHeader); + resolve(response as T); + } else { + resolve(JSON.parse(xhr.responseText)); + } + } else { + const error = new UmbApiError(xhr.statusText, xhr.status, xhr, { + title: xhr.statusText, + type: 'ApiError', + status: xhr.status, + }); + reject(error); + } + } catch { + // This most likely happens when the response is not JSON + reject(new Error(`Failed to make request: ${xhr.statusText}`)); + } + }; + + xhr.onerror = () => { + const error = new UmbApiError(xhr.statusText, xhr.status, xhr, { + title: xhr.statusText, + type: 'ApiError', + status: xhr.status, + }); + reject(error); + }; + + if (!onCancel.isCancelled) { + // Handle body based on Content-Type + if (options.body instanceof FormData) { + xhr.send(options.body); + } else { + xhr.send(JSON.stringify(options.body)); + } + } + + onCancel(() => { + xhr.abort(); + }); + }); + } + + public override destroy(): void { + super.destroy(); + if (this.#abortSignal) { + this.#abortSignal.removeEventListener('abort', this.cancel); + } + this.#abortSignal = undefined; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/resources/tryExecute.function.ts b/src/Umbraco.Web.UI.Client/src/packages/core/resources/tryExecute.function.ts index c4b8b60adf26..d08cbb0feb99 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/resources/tryExecute.function.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/resources/tryExecute.function.ts @@ -1,10 +1,14 @@ -import { UmbResourceController } from './resource.controller.js'; +import { UmbTryExecuteController } from './try-execute.controller.js'; import type { UmbDataSourceResponse } from '@umbraco-cms/backoffice/repository'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; /** * * @param promise */ -export function tryExecute(promise: Promise): Promise> { - return UmbResourceController.tryExecute(promise); +export async function tryExecute(host: UmbControllerHost, promise: Promise): Promise> { + const controller = new UmbTryExecuteController(host, promise); + const response = await controller.tryExecute(); + controller.destroy(); + return response as UmbDataSourceResponse; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/resources/tryExecuteAndNotify.function.ts b/src/Umbraco.Web.UI.Client/src/packages/core/resources/tryExecuteAndNotify.function.ts index 2e2b41670478..0c72fa92be15 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/resources/tryExecuteAndNotify.function.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/resources/tryExecuteAndNotify.function.ts @@ -1,6 +1,5 @@ -import { UmbResourceController } from './resource.controller.js'; +import { UmbTryExecuteAndNotifyController } from './try-execute-and-notify.controller.js'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import type { UmbNotificationOptions } from '@umbraco-cms/backoffice/notification'; import type { UmbDataSourceResponse } from '@umbraco-cms/backoffice/repository'; /** @@ -9,10 +8,12 @@ import type { UmbDataSourceResponse } from '@umbraco-cms/backoffice/repository'; * @param resource * @param options */ -export function tryExecuteAndNotify( +export async function tryExecuteAndNotify( host: UmbControllerHost, resource: Promise, - options?: UmbNotificationOptions, ): Promise> { - return new UmbResourceController(host, resource).tryExecuteAndNotify(options); + const controller = new UmbTryExecuteAndNotifyController(host, resource); + const response = await controller.tryExecuteAndNotify(); + controller.destroy(); + return response as UmbDataSourceResponse; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/resources/tryXhrRequest.function.ts b/src/Umbraco.Web.UI.Client/src/packages/core/resources/tryXhrRequest.function.ts index 749fafc7296e..08bb3bacc5f6 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/resources/tryXhrRequest.function.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/resources/tryXhrRequest.function.ts @@ -1,16 +1,23 @@ import type { XhrRequestOptions } from './types.js'; -import { UmbResourceController } from './resource.controller.js'; -import { OpenAPI, type CancelablePromise } from '@umbraco-cms/backoffice/external/backend-api'; +import { UmbTryXhrRequestController } from './try-xhr-request.controller.js'; +import { OpenAPI } from '@umbraco-cms/backoffice/external/backend-api'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import type { UmbDataSourceResponse } from '@umbraco-cms/backoffice/repository'; /** * Make an XHR request. - * @param {XhrRequestOptions} options The options for the XHR request. - * @returns {CancelablePromise} A promise that can be cancelled. */ -export function tryXhrRequest(options: XhrRequestOptions): CancelablePromise { - return UmbResourceController.xhrRequest({ +export async function tryXhrRequest( + host: UmbControllerHost, + options: XhrRequestOptions, +): Promise> { + const promise = UmbTryXhrRequestController.createXhrRequest({ ...options, baseUrl: OpenAPI.BASE, token: OpenAPI.TOKEN as never, }); + const controller = new UmbTryXhrRequestController(host, promise); + const response = await controller.tryXhrRequest(options.abortSignal); + controller.destroy(); + return response as UmbDataSourceResponse; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/resources/umb-error.ts b/src/Umbraco.Web.UI.Client/src/packages/core/resources/umb-error.ts index b4f6e76f8fa4..63fbf6d13029 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/resources/umb-error.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/resources/umb-error.ts @@ -1,3 +1,4 @@ +import type { ApiError, CancelError } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbProblemDetails } from './types.js'; export class UmbError extends Error { @@ -14,6 +15,13 @@ export class UmbCancelError extends UmbError { public static isUmbCancelError(error: unknown): error is UmbCancelError { return error instanceof UmbCancelError || (error as UmbCancelError).name === 'UmbCancelError'; } + + /** + * @deprecated Use `UmbCancelError.isUmbCancelError` instead and map your object to `UmbCancelError` if needed. + */ + public static fromLegacyCancelError(error: CancelError): UmbCancelError { + return new UmbCancelError(error.message); + } } /** @@ -22,20 +30,12 @@ export class UmbCancelError extends UmbError { export class UmbApiError extends UmbError { public override name = 'UmbApiError'; public status: number; - public response: unknown; public request: unknown; public problemDetails: UmbProblemDetails; - public constructor( - message: string, - status: number, - response: unknown, - request: unknown, - problemDetails: UmbProblemDetails, - ) { + public constructor(message: string, status: number, request: unknown, problemDetails: UmbProblemDetails) { super(message); this.status = status; - this.response = response; this.request = request; this.problemDetails = problemDetails; } @@ -43,4 +43,25 @@ export class UmbApiError extends UmbError { public static isUmbApiError(error: unknown): error is UmbApiError { return error instanceof UmbApiError || (error as UmbApiError).name === 'UmbApiError'; } + + /** + * @deprecated Use `UmbCancelError.isUmbApiError` instead and map your object to `UmbApiError` if needed. + */ + public static fromLegacyApiError(error: ApiError): UmbApiError { + // ApiError - body could hold a ProblemDetails from the server + let problemDetails: UmbProblemDetails | null = null; + if (typeof error.body !== 'undefined' && !!error.body) { + try { + problemDetails = typeof error.body === 'string' ? JSON.parse(error.body) : error.body; + } catch (e) { + console.error('Error parsing error body (expected JSON)', e); + } + } + return new UmbApiError( + error.message, + error.status, + error.request, + problemDetails ?? { title: error.message, type: 'ApiError', status: error.status }, + ); + } } From ce47867c74c9002e2ddecb7cc9d46ac0bab7bbb4 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Fri, 4 Apr 2025 11:48:08 +0200 Subject: [PATCH 08/45] feat: adds color to peek notification --- .../controllers/peek-error/peek-error.controller.ts | 2 +- .../src/packages/core/notification/types.ts | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/notification/controllers/peek-error/peek-error.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/notification/controllers/peek-error/peek-error.controller.ts index 2506371e27f4..cf102fa9c5f0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/notification/controllers/peek-error/peek-error.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/notification/controllers/peek-error/peek-error.controller.ts @@ -12,7 +12,7 @@ export class UmbPeekErrorController extends UmbControllerBase { throw new Error('Could not get notification context'); } - context.peek('danger', { + context.peek(args.color ?? 'danger', { elementName: 'umb-peek-error-notification', data: args, }); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/notification/types.ts b/src/Umbraco.Web.UI.Client/src/packages/core/notification/types.ts index 3ec0daea78ab..08f800994106 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/notification/types.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/notification/types.ts @@ -26,5 +26,6 @@ export interface UmbNotificationOptions Date: Fri, 4 Apr 2025 11:48:45 +0200 Subject: [PATCH 09/45] feat: moves the internal api interceptors controller and adds more interceptors --- .../apps/app/api-interceptor.controller.ts | 57 -------- .../src/apps/app/app.element.ts | 7 +- .../resources/api-interceptor.controller.ts | 125 ++++++++++++++++++ .../src/packages/core/resources/index.ts | 1 + 4 files changed, 130 insertions(+), 60 deletions(-) delete mode 100644 src/Umbraco.Web.UI.Client/src/apps/app/api-interceptor.controller.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/resources/api-interceptor.controller.ts diff --git a/src/Umbraco.Web.UI.Client/src/apps/app/api-interceptor.controller.ts b/src/Umbraco.Web.UI.Client/src/apps/app/api-interceptor.controller.ts deleted file mode 100644 index 8c7bbcf6e838..000000000000 --- a/src/Umbraco.Web.UI.Client/src/apps/app/api-interceptor.controller.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; -import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { OpenAPI } from '@umbraco-cms/backoffice/external/backend-api'; -import { - extractUmbNotificationColor, - isUmbNotifications, - UMB_NOTIFICATION_CONTEXT, - UMB_NOTIFICATION_HEADER, -} from '@umbraco-cms/backoffice/notification'; - -/** - * Controller that adds interceptors to the OpenAPI client - */ -export class UmbApiInterceptorController extends UmbControllerBase { - constructor(host: UmbControllerHost) { - super(host); - this.#addUmbNotificationsInterceptor(); - } - - /** - * Interceptor which checks responses for the umb-notifications header and displays them as a notification if any. Removes the umb-notifications from the headers. - */ - #addUmbNotificationsInterceptor() { - OpenAPI.interceptors.response.use((response) => { - const umbNotifications = response.headers.get(UMB_NOTIFICATION_HEADER); - if (!umbNotifications) return response; - - const notifications = JSON.parse(umbNotifications); - if (!isUmbNotifications(notifications)) return response; - - this.getContext(UMB_NOTIFICATION_CONTEXT).then((notificationContext) => { - if (notificationContext === undefined) { - throw new Error('Notification context is not available'); - } - for (const notification of notifications) { - notificationContext.peek(extractUmbNotificationColor(notification.type), { - data: { headline: notification.category, message: notification.message }, - }); - } - }); - - const newHeader = new Headers(); - for (const header of response.headers.entries()) { - const [key, value] = header; - if (key !== UMB_NOTIFICATION_HEADER) newHeader.set(key, value); - } - - const newResponse = new Response(response.body, { - headers: newHeader, - status: response.status, - statusText: response.statusText, - }); - - return newResponse; - }); - } -} diff --git a/src/Umbraco.Web.UI.Client/src/apps/app/app.element.ts b/src/Umbraco.Web.UI.Client/src/apps/app/app.element.ts index e77efa374bb3..40d1c3e3df09 100644 --- a/src/Umbraco.Web.UI.Client/src/apps/app/app.element.ts +++ b/src/Umbraco.Web.UI.Client/src/apps/app/app.element.ts @@ -3,7 +3,6 @@ import type { UmbAppErrorElement } from './app-error.element.js'; import { UmbAppContext } from './app.context.js'; import { UmbServerConnection } from './server-connection.js'; import { UmbAppAuthController } from './app-auth.controller.js'; -import { UmbApiInterceptorController } from './api-interceptor.controller.js'; import type { UmbAppOauthElement } from './app-oauth.element.js'; import type { UMB_AUTH_CONTEXT } from '@umbraco-cms/backoffice/auth'; import { UmbAuthContext } from '@umbraco-cms/backoffice/auth'; @@ -21,6 +20,7 @@ import { } from '@umbraco-cms/backoffice/extension-registry'; import { filter, first, firstValueFrom } from '@umbraco-cms/backoffice/external/rxjs'; import { hasOwnOpener, retrieveStoredPath } from '@umbraco-cms/backoffice/utils'; +import { UmbApiInterceptorController } from '@umbraco-cms/backoffice/resources'; import './app-oauth.element.js'; @@ -139,13 +139,14 @@ export class UmbAppElement extends UmbLitElement { #authContext?: typeof UMB_AUTH_CONTEXT.TYPE; #serverConnection?: UmbServerConnection; #authController = new UmbAppAuthController(this); + #apiInterceptorController = new UmbApiInterceptorController(this); constructor() { super(); OpenAPI.BASE = window.location.origin; - new UmbApiInterceptorController(this); + this.#apiInterceptorController.bindDefaultInterceptors(OpenAPI); new UmbBundleExtensionInitializer(this, umbExtensionsRegistry); @@ -160,7 +161,7 @@ export class UmbAppElement extends UmbLitElement { } async #setup() { - this.#serverConnection = await new UmbServerConnection(this.serverUrl).connect(); + this.#serverConnection = await new UmbServerConnection(this, this.serverUrl).connect(); this.#authContext = new UmbAuthContext(this, this.serverUrl, this.backofficePath, this.bypassAuth); new UmbAppContext(this, { diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/resources/api-interceptor.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/resources/api-interceptor.controller.ts new file mode 100644 index 000000000000..73c183ca0899 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/resources/api-interceptor.controller.ts @@ -0,0 +1,125 @@ +import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; +import type { OpenAPIConfig } from '@umbraco-cms/backoffice/external/backend-api'; +import { UMB_AUTH_CONTEXT } from '@umbraco-cms/backoffice/auth'; +import { + extractUmbNotificationColor, + isUmbNotifications, + UMB_NOTIFICATION_HEADER, + type UmbNotificationColor, +} from '@umbraco-cms/backoffice/notification'; + +export class UmbApiInterceptorController extends UmbControllerBase { + public bindDefaultInterceptors(client: OpenAPIConfig) { + this.addAuthResponseInterceptor(client); + this.addUmbNotificationsInterceptor(client); + this.addErrorInterceptor(client); + } + + /** + * @internal + */ + addAuthResponseInterceptor(client: OpenAPIConfig) { + client.interceptors.response.use(async (response) => { + if (response.status === 401) { + // See if we can get the UmbAuthContext and let it know the user is timed out + const authContext = await this.getContext(UMB_AUTH_CONTEXT, { preventTimeout: true }); + if (!authContext) { + throw new Error('Could not get the auth context'); + } + authContext.timeOut(); + } + return response; + }); + } + + /** + * @internal + */ + addErrorInterceptor(client: OpenAPIConfig) { + client.interceptors.response.use(async (response) => { + if (response.ok) return response; + + const error = await response.json(); + if (!error) return response; + + // If the error is not an UmbError, we check if it is a problem details object + // Check if the error is a problem details object + if (!('type' in error) || !('title' in error) || !('status' in error)) { + // If not, we just return the response + return response; + } + + // Handle 500 errors - we need to show a notification + if (response.status === 500) { + let headline = error.title ?? error.name ?? 'Server Error'; + let message = 'A fatal server error occurred. If this continues, please reach out to your administrator.'; + + // Special handling for ObjectCacheAppCache corruption errors, which we are investigating + if ( + error.detail?.includes('ObjectCacheAppCache') || + error.detail?.includes('Umbraco.Cms.Infrastructure.Scoping.Scope.DisposeLastScope()') + ) { + headline = 'Please restart the server'; + message = + 'The Umbraco object cache is corrupt, but your action may still have been executed. Please restart the server to reset the cache. This is a work in progress.'; + } + + this.#peekError(headline, message, error.errors ?? error.detail); + } + + // Create a new response with the error + return response; + }); + } + + /** + * Interceptor which checks responses for the umb-notifications header and displays them as a notification if any. Removes the umb-notifications from the headers. + * @internal + */ + addUmbNotificationsInterceptor(client: OpenAPIConfig) { + client.interceptors.response.use((response) => { + const umbNotifications = response.headers.get(UMB_NOTIFICATION_HEADER); + if (!umbNotifications) return response; + + const notifications = JSON.parse(umbNotifications); + if (!isUmbNotifications(notifications)) return response; + + for (const notification of notifications) { + this.#peekError( + notification.category, + notification.message, + null, + extractUmbNotificationColor(notification.type), + ); + } + + const newHeader = new Headers(); + for (const header of response.headers.entries()) { + const [key, value] = header; + if (key !== UMB_NOTIFICATION_HEADER) newHeader.set(key, value); + } + + const newResponse = response.clone(); + newResponse.headers.delete(UMB_NOTIFICATION_HEADER); + return newResponse; + + /*const newResponse = new Response(response.body, { + headers: newHeader, + status: response.status, + statusText: response.statusText, + }); + + return newResponse;*/ + }); + } + + async #peekError(headline: string, message: string, details: unknown, color?: UmbNotificationColor) { + // This late importing is done to avoid circular reference [NL] + (await import('@umbraco-cms/backoffice/notification')).umbPeekError(this, { + headline, + message, + details, + color, + }); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/resources/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/resources/index.ts index 6de800cccbc1..d499a6c3c201 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/resources/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/resources/index.ts @@ -1,3 +1,4 @@ +export * from './api-interceptor.controller.js'; export * from './resource.controller.js'; export * from './tryExecute.function.js'; export * from './tryExecuteAndNotify.function.js'; From 112c9b42f38ad158bffa6b59d43fcd9770f81946 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Fri, 4 Apr 2025 11:49:05 +0200 Subject: [PATCH 10/45] feat: adds host to params --- .../src/apps/app/server-connection.ts | 11 +++++++---- .../src/apps/backoffice/backoffice.context.ts | 2 +- .../document-validation.server.data-source.ts | 10 ++++------ 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/apps/app/server-connection.ts b/src/Umbraco.Web.UI.Client/src/apps/app/server-connection.ts index 4365b643f00f..0eb426eac68c 100644 --- a/src/Umbraco.Web.UI.Client/src/apps/app/server-connection.ts +++ b/src/Umbraco.Web.UI.Client/src/apps/app/server-connection.ts @@ -1,8 +1,10 @@ +import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { RuntimeLevelModel, ServerService } from '@umbraco-cms/backoffice/external/backend-api'; import { UmbBooleanState, UmbNumberState } from '@umbraco-cms/backoffice/observable-api'; import { tryExecute } from '@umbraco-cms/backoffice/resources'; -export class UmbServerConnection { +export class UmbServerConnection extends UmbControllerBase { #url: string; #status: RuntimeLevelModel = RuntimeLevelModel.UNKNOWN; @@ -18,7 +20,8 @@ export class UmbServerConnection { #allowPasswordReset = new UmbBooleanState(false); allowPasswordReset = this.#allowPasswordReset.asObservable(); - constructor(serverUrl: string) { + constructor(host: UmbControllerHost, serverUrl: string) { + super(host); this.#url = serverUrl; } @@ -61,7 +64,7 @@ export class UmbServerConnection { } async #setStatus() { - const { data, error } = await tryExecute(ServerService.getServerStatus()); + const { data, error } = await tryExecute(this, ServerService.getServerStatus()); if (error) { throw error; } @@ -71,7 +74,7 @@ export class UmbServerConnection { } async #setServerConfiguration() { - const { data, error } = await tryExecute(ServerService.getServerConfiguration()); + const { data, error } = await tryExecute(this, ServerService.getServerConfiguration()); if (error) { throw error; } diff --git a/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.context.ts b/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.context.ts index ff5086c83c18..a11bc1235753 100644 --- a/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.context.ts +++ b/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.context.ts @@ -57,7 +57,7 @@ export class UmbBackofficeContext extends UmbContextBase { } async #getVersion() { - const { data } = await tryExecute(ServerService.getServerInformation()); + const { data } = await tryExecute(this, ServerService.getServerInformation()); if (!data) return; // A quick semver parser (to remove the unwanted bits) [LK] diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/repository/validation/document-validation.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/repository/validation/document-validation.server.data-source.ts index 85ab9d7550f1..8df17d34ba86 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/repository/validation/document-validation.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/repository/validation/document-validation.server.data-source.ts @@ -13,12 +13,10 @@ import type { UmbEntityUnique } from '@umbraco-cms/backoffice/entity'; * A server data source for Document Validation */ export class UmbDocumentValidationServerDataSource { - //#host: UmbControllerHost; + #host: UmbControllerHost; - // TODO: [v15]: ignoring unused var here here to prevent a breaking change - // eslint-disable-next-line @typescript-eslint/no-unused-vars constructor(host: UmbControllerHost) { - //this.#host = host; + this.#host = host; } /** @@ -44,7 +42,7 @@ export class UmbDocumentValidationServerDataSource { // Maybe use: tryExecuteAndNotify return tryExecute( - //this.#host, + this.#host, DocumentService.postDocumentValidate({ requestBody, }), @@ -72,7 +70,7 @@ export class UmbDocumentValidationServerDataSource { // Maybe use: tryExecuteAndNotify return tryExecute( - //this.#host, + this.#host, DocumentService.putUmbracoManagementApiV11DocumentByIdValidate11({ id: model.unique, requestBody, From 991a44bf565eee9d64685abdd976aceec19b2975 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Fri, 4 Apr 2025 11:51:01 +0200 Subject: [PATCH 11/45] feat: marks certain functions as deprecated --- .../src/packages/core/resources/tryExecute.function.ts | 4 ---- .../packages/core/resources/tryExecuteAndNotify.function.ts | 6 ++---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/resources/tryExecute.function.ts b/src/Umbraco.Web.UI.Client/src/packages/core/resources/tryExecute.function.ts index d08cbb0feb99..56544f2a0426 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/resources/tryExecute.function.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/resources/tryExecute.function.ts @@ -2,10 +2,6 @@ import { UmbTryExecuteController } from './try-execute.controller.js'; import type { UmbDataSourceResponse } from '@umbraco-cms/backoffice/repository'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -/** - * - * @param promise - */ export async function tryExecute(host: UmbControllerHost, promise: Promise): Promise> { const controller = new UmbTryExecuteController(host, promise); const response = await controller.tryExecute(); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/resources/tryExecuteAndNotify.function.ts b/src/Umbraco.Web.UI.Client/src/packages/core/resources/tryExecuteAndNotify.function.ts index 0c72fa92be15..4c19977e42e0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/resources/tryExecuteAndNotify.function.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/resources/tryExecuteAndNotify.function.ts @@ -3,10 +3,8 @@ import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import type { UmbDataSourceResponse } from '@umbraco-cms/backoffice/repository'; /** - * - * @param host - * @param resource - * @param options + * @deprecated Use the {tryExecute} function instead and handle the error in the caller. + * This function is kept for backwards compatibility and will be removed in a future version. */ export async function tryExecuteAndNotify( host: UmbControllerHost, From fbf70840b907daf1b98bef18815410333c064e23 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Fri, 4 Apr 2025 11:51:14 +0200 Subject: [PATCH 12/45] feat: maps api errors to UmbErrors --- .../src/packages/core/resources/index.ts | 1 + .../core/resources/resource.controller.ts | 131 +++--------------- .../try-execute-and-notify.controller.ts | 19 ++- .../core/resources/try-execute.controller.ts | 8 +- 4 files changed, 41 insertions(+), 118 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/resources/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/resources/index.ts index d499a6c3c201..140e5716737a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/resources/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/resources/index.ts @@ -5,4 +5,5 @@ export * from './tryExecuteAndNotify.function.js'; export * from './tryXhrRequest.function.js'; export * from './extractUmbColorVariable.function.js'; export * from './apiTypeValidators.function.js'; +export * from './umb-error.js'; export type * from './types.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/resources/resource.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/resources/resource.controller.ts index 3b1575fb6736..748e14deb3d8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/resources/resource.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/resources/resource.controller.ts @@ -30,117 +30,23 @@ export class UmbResourceController extends UmbControllerBase { } /** - * Handle errors + * Maps any error to an UmbError. * @internal - * @param {any} error The error to handle. - * @param {boolean} shouldNotify Whether to show a notification or not. Default is false. - * @returns {UmbDataSourceResponse} The response object with the error. - * @template T The type of the data returned from the API. + * @param {*} error The error to map + * @returns {*} The mapped error */ - async handleUmbErrors(error: unknown, shouldNotify: boolean = false): Promise> { - if (!error) return {}; - - /** - * Determine if we want to show a notification or just log the error to the console. - * If the error is not a recognizable system error (i.e. a HttpError), then we will show a notification - * with the error details using the default notification options. - */ - if (UmbCancelError.isUmbCancelError(error)) { - // Cancelled - do nothing - return { error }; - } else if (UmbApiError.isUmbApiError(error)) { - // UmbApiError - handle it - return this.handleUmbApiError(error, shouldNotify); - } else if (error instanceof Error) { - // UmbError or unknown error - log it to the console - console.error('Error occured', error); - if (shouldNotify) { - this.#peekError('Error', error.message ?? 'Unknown error', []); - } - return { error }; - } else { - // Unknown error - log it to the console - console.error('Unknown error', error); - if (shouldNotify) { - this.#peekError('Error', 'Unknown error', []); - } - return { error: new UmbError('Unknown error') }; + mapToUmbError(error: unknown): UmbApiError | UmbCancelError | UmbError { + if (isApiError(error)) { + return UmbApiError.fromLegacyApiError(error); + } else if (isCancelError(error)) { + return UmbCancelError.fromLegacyCancelError(error); + } else if (error instanceof UmbApiError) { + return error; + } else if (error instanceof UmbCancelError) { + return error; } - } - - /** - * Handle errors from the API and show a notification if needed. - * @internal - * @param {UmbApiError} error The error to handle. - * @param {boolean} shouldNotify Whether to show a notification or not. Default is false. - * @returns {UmbDataSourceResponse} The response object with the error. - * @template T The type of the data returned from the API. - * @see {@link UmbResourceController.handleUmbErrors} - */ - async handleUmbApiError(error: UmbApiError, shouldNotify: boolean = false): Promise> { - console.error('UmbError caught in UmbResourceController', error); - - /** - * Check if the operation status ends with `ByNotification` and if so, don't show a notification - * This is a special case where the operation was cancelled by the server and the client gets a notification header instead. - */ - if ( - error.problemDetails.operationStatus && - typeof error.problemDetails.operationStatus === 'string' && - error.problemDetails.operationStatus.endsWith('ByNotification') - ) { - shouldNotify = false; - } - - // Go through the error status codes and act accordingly - switch (error.status ?? 0) { - case 401: { - // See if we can get the UmbAuthContext and let it know the user is timed out - const authContext = await this.getContext(UMB_AUTH_CONTEXT, { preventTimeout: true }); - if (!authContext) { - throw new Error('Could not get the auth context'); - } - authContext.timeOut(); - break; - } - case 500: - // Server Error - - if (shouldNotify) { - let headline = error.problemDetails.title ?? error.name ?? 'Server Error'; - let message = 'A fatal server error occurred. If this continues, please reach out to your administrator.'; - - // Special handling for ObjectCacheAppCache corruption errors, which we are investigating - if ( - error.problemDetails.detail?.includes('ObjectCacheAppCache') || - error.problemDetails.detail?.includes('Umbraco.Cms.Infrastructure.Scoping.Scope.DisposeLastScope()') - ) { - headline = 'Please restart the server'; - message = - 'The Umbraco object cache is corrupt, but your action may still have been executed. Please restart the server to reset the cache. This is a work in progress.'; - } - - this.#peekError(headline, message, error.problemDetails.errors ?? error.problemDetails.detail); - } - break; - default: - // Other errors - if (shouldNotify) { - const headline = error.problemDetails.title ?? error.name ?? 'Server Error'; - this.#peekError('', headline, error.problemDetails.errors ?? error.problemDetails.detail); - } - } - - return { error }; - } - - async #peekError(headline: string, message: string, details: unknown) { - // This late importing is done to avoid circular reference [NL] - (await import('@umbraco-cms/backoffice/notification')).umbPeekError(this, { - headline, - message, - details, - }); + // If the error is not an UmbError, we will just return it as is + return new UmbError(error instanceof Error ? error.message : 'Unknown error'); } /** @@ -427,4 +333,13 @@ export class UmbResourceController extends UmbControllerBase { return promise; } + + async #peekError(headline: string, message: string, details: unknown) { + // This late importing is done to avoid circular reference [NL] + (await import('@umbraco-cms/backoffice/notification')).umbPeekError(this, { + headline, + message, + details, + }); + } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/resources/try-execute-and-notify.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/resources/try-execute-and-notify.controller.ts index da4be68b49b2..17c2d1ff8f9c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/resources/try-execute-and-notify.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/resources/try-execute-and-notify.controller.ts @@ -1,17 +1,26 @@ -import { isApiError, isCancelError } from './apiTypeValidators.function.js'; import { UmbResourceController } from './resource.controller.js'; -import { UmbApiError, UmbCancelError } from './umb-error.js'; import type { UmbDataSourceResponse } from '@umbraco-cms/backoffice/repository'; +/** + * A controller that tries to execute a promise and returns the result. + * @internal + * @deprecated Use the {UmbTryExecuteController} instead and handle the error in the caller. + * This class is kept for backwards compatibility and will be removed in a future version. + */ export class UmbTryExecuteAndNotifyController extends UmbResourceController { + /** + * @internal + * @deprecated Use the {UmbTryExecuteController.tryExecute} instead and handle the error in the caller. + * This method is kept for backwards compatibility and will be removed in a future version. + */ override async tryExecuteAndNotify(): Promise> { try { return { data: await this._promise }; } catch (error) { // Error might be a legacy error, so we need to check if it is an UmbError - let umbError = isApiError(error) ? UmbApiError.fromLegacyApiError(error) : error; - umbError = isCancelError(umbError) ? UmbCancelError.fromLegacyCancelError(umbError) : umbError; - return this.handleUmbErrors(umbError, true); + return { + error: this.mapToUmbError(error), + }; } } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/resources/try-execute.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/resources/try-execute.controller.ts index c2db39ccb81e..1ff94b3aba8c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/resources/try-execute.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/resources/try-execute.controller.ts @@ -1,6 +1,4 @@ import { UmbResourceController } from './resource.controller.js'; -import { UmbApiError, UmbCancelError } from './umb-error.js'; -import { isApiError, isCancelError } from './apiTypeValidators.function.js'; import type { UmbDataSourceResponse } from '@umbraco-cms/backoffice/repository'; export class UmbTryExecuteController extends UmbResourceController { @@ -9,9 +7,9 @@ export class UmbTryExecuteController extends UmbResourceController { return { data: await this._promise }; } catch (error) { // Error might be a legacy error, so we need to check if it is an UmbError - let umbError = isApiError(error) ? UmbApiError.fromLegacyApiError(error) : error; - umbError = isCancelError(umbError) ? UmbCancelError.fromLegacyCancelError(umbError) : umbError; - return this.handleUmbErrors(umbError); + return { + error: this.mapToUmbError(error), + }; } } } From 00d5c60e95ed5c03cc4b3e5b67ba31f34816bbef Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Fri, 4 Apr 2025 11:52:48 +0200 Subject: [PATCH 13/45] chore: removes deprecation console logs --- .../core/resources/apiTypeValidators.function.ts | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/resources/apiTypeValidators.function.ts b/src/Umbraco.Web.UI.Client/src/packages/core/resources/apiTypeValidators.function.ts index 99461e3ede19..d551dbf4f45d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/resources/apiTypeValidators.function.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/resources/apiTypeValidators.function.ts @@ -1,16 +1,10 @@ import type { ApiError, CancelError } from '@umbraco-cms/backoffice/external/backend-api'; -import { UmbDeprecation } from '@umbraco-cms/backoffice/utils'; /** * Checks if the given error is an instance of ApiError. * @deprecated Use {UmbApiError.isUmbApiError} instead and map your object to {UmbApiError} if needed. */ export function isApiError(error: unknown): error is ApiError { - new UmbDeprecation({ - deprecated: 'isApiError', - removeInVersion: '18.0.0', - solution: 'Use UmbApiError.isUmbApiError instead', - }); return (error as ApiError).name === 'ApiError'; } @@ -19,11 +13,6 @@ export function isApiError(error: unknown): error is ApiError { * @deprecated Use {UmbApiCancelError.isUmbApiCancelError}` instead and map your object to {UmbApiCancelError} if needed. */ export function isCancelError(error: unknown): error is CancelError { - new UmbDeprecation({ - deprecated: 'isCancelError', - removeInVersion: '18.0.0', - solution: 'Use UmbApiCancelError.isUmbApiCancelError instead', - }); return (error as CancelError).name === 'CancelError'; } From d527c2e9b9c4a6b3dcd42cd4a48ac07a71c4b4f3 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Fri, 4 Apr 2025 11:54:32 +0200 Subject: [PATCH 14/45] chore: allows any --- .../src/packages/core/notification/types.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/notification/types.ts b/src/Umbraco.Web.UI.Client/src/packages/core/notification/types.ts index 08f800994106..7e47cd51829e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/notification/types.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/notification/types.ts @@ -26,6 +26,7 @@ export interface UmbNotificationOptions Date: Fri, 4 Apr 2025 11:56:08 +0200 Subject: [PATCH 15/45] feat: maps xhr errors to umb errors --- .../resources/try-xhr-request.controller.ts | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/resources/try-xhr-request.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/resources/try-xhr-request.controller.ts index b48031b55ed7..f3206927bd66 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/resources/try-xhr-request.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/resources/try-xhr-request.controller.ts @@ -1,8 +1,7 @@ -import { isApiError, isCancelError } from './apiTypeValidators.function.js'; import { UmbCancelablePromise } from './cancelable-promise.js'; import { UmbResourceController } from './resource.controller.js'; import type { XhrRequestOptions } from './types.js'; -import { UmbApiError, UmbCancelError } from './umb-error.js'; +import { UmbApiError } from './umb-error.js'; import type { UmbDataSourceResponse } from '@umbraco-cms/backoffice/repository'; export class UmbTryXhrRequestController extends UmbResourceController { @@ -17,9 +16,9 @@ export class UmbTryXhrRequestController extends UmbResourceController { return { data: await this._promise }; } catch (error) { // Error might be a legacy error, so we need to check if it is an UmbError - let umbError = isApiError(error) ? UmbApiError.fromLegacyApiError(error) : error; - umbError = isCancelError(umbError) ? UmbCancelError.fromLegacyCancelError(umbError) : umbError; - return this.handleUmbErrors(umbError); + return { + error: this.mapToUmbError(error), + }; } } @@ -77,7 +76,13 @@ export class UmbTryXhrRequestController extends UmbResourceController { } } catch { // This most likely happens when the response is not JSON - reject(new Error(`Failed to make request: ${xhr.statusText}`)); + reject( + new UmbApiError(`Failed to make request: ${xhr.statusText}`, xhr.status, xhr, { + title: xhr.statusText, + type: 'ApiError', + status: xhr.status, + }), + ); } }; From 46a6cbe7db19ba53fbe899e4a8eeeefc557d8f77 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Fri, 4 Apr 2025 12:01:33 +0200 Subject: [PATCH 16/45] feat: adds host to tryExecute --- .../installer/database/installer-database.element.ts | 2 ++ .../src/apps/installer/installer.context.ts | 2 +- .../src/apps/upgrader/upgrader.element.ts | 4 ++-- .../temporary-file.server.data-source.ts | 4 ++-- .../repository/preview/document-preview.repository.ts | 4 ++-- .../validation/media-validation.server.data-source.ts | 10 ++++------ .../repository/public-access.server.data.ts | 2 +- .../validation/member-validation.server.data-source.ts | 10 ++++------ .../packages/sysinfo/repository/sysinfo.repository.ts | 2 +- .../repository/current-user.server.data-source.ts | 6 +++++- .../src/packages/user/current-user/types.ts | 5 ++--- .../repository/sources/user-mfa.server.data-source.ts | 1 + 12 files changed, 27 insertions(+), 25 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/apps/installer/database/installer-database.element.ts b/src/Umbraco.Web.UI.Client/src/apps/installer/database/installer-database.element.ts index c09facb816fa..a9ea13e4d6ae 100644 --- a/src/Umbraco.Web.UI.Client/src/apps/installer/database/installer-database.element.ts +++ b/src/Umbraco.Web.UI.Client/src/apps/installer/database/installer-database.element.ts @@ -165,6 +165,7 @@ export class UmbInstallerDatabaseElement extends UmbLitElement { }; const { error } = await tryExecute( + this, InstallService.postInstallValidateDatabase({ requestBody: databaseDetails }), ); @@ -196,6 +197,7 @@ export class UmbInstallerDatabaseElement extends UmbLitElement { this._installerContext.nextStep(); const { error: _error } = await tryExecute( + this, InstallService.postInstallSetup({ requestBody: this._installerContext.getData() }), ); const error = _error as ProblemDetails | undefined; diff --git a/src/Umbraco.Web.UI.Client/src/apps/installer/installer.context.ts b/src/Umbraco.Web.UI.Client/src/apps/installer/installer.context.ts index 019399941641..381436fa0904 100644 --- a/src/Umbraco.Web.UI.Client/src/apps/installer/installer.context.ts +++ b/src/Umbraco.Web.UI.Client/src/apps/installer/installer.context.ts @@ -121,7 +121,7 @@ export class UmbInstallerContext extends UmbContextBase({ + const xhrRequest = tryXhrRequest(this.#host, { url: '/umbraco/management/api/v1/temporary-file', method: 'POST', responseHeader: 'Umb-Generated-Resource', @@ -44,7 +44,7 @@ export class UmbTemporaryFileServerDataSource { onProgress, abortSignal, }); - return tryExecuteAndNotify(this.#host, xhrRequest); + return xhrRequest; } /** diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/repository/preview/document-preview.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/repository/preview/document-preview.repository.ts index ed04e8634c7f..02dc7ade3e4f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/repository/preview/document-preview.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/repository/preview/document-preview.repository.ts @@ -14,7 +14,7 @@ export class UmbDocumentPreviewRepository extends UmbRepositoryBase { * @memberof UmbDocumentPreviewRepository */ async enter(): Promise { - await tryExecute(PreviewService.postPreview()); + await tryExecute(this, PreviewService.postPreview()); return; } @@ -24,7 +24,7 @@ export class UmbDocumentPreviewRepository extends UmbRepositoryBase { * @memberof UmbDocumentPreviewRepository */ async exit(): Promise { - await tryExecute(PreviewService.deletePreview()); + await tryExecute(this, PreviewService.deletePreview()); return; } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/repository/validation/media-validation.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/repository/validation/media-validation.server.data-source.ts index ef4d12057b87..04243d21a846 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/repository/validation/media-validation.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/repository/validation/media-validation.server.data-source.ts @@ -13,12 +13,10 @@ import type { UmbEntityUnique } from '@umbraco-cms/backoffice/entity'; * A server data source for Media Validation */ export class UmbMediaValidationServerDataSource { - //#host: UmbControllerHost; + #host: UmbControllerHost; - // TODO: [v15]: ignoring unused var here here to prevent a breaking change - // eslint-disable-next-line @typescript-eslint/no-unused-vars constructor(host: UmbControllerHost) { - //this.#host = host; + this.#host = host; } /** @@ -43,7 +41,7 @@ export class UmbMediaValidationServerDataSource { // Maybe use: tryExecuteAndNotify return tryExecute( - //this.#host, + this.#host, MediaService.postMediaValidate({ requestBody, }), @@ -70,7 +68,7 @@ export class UmbMediaValidationServerDataSource { // Maybe use: tryExecuteAndNotify return tryExecute( - //this.#host, + this.#host, MediaService.putMediaByIdValidate({ id: model.unique, requestBody, diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member-public-access/repository/public-access.server.data.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member-public-access/repository/public-access.server.data.ts index 7221bc40d072..fff9b0028f1d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member-public-access/repository/public-access.server.data.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member-public-access/repository/public-access.server.data.ts @@ -43,7 +43,7 @@ export class UmbDocumentPublicAccessServerDataSource { if (!unique) throw new Error('unique is missing'); // NOTE: The entity will not be present, when fetching Public Access for a descendant of a protected Document. // This is a perfectly valid scenario, which is handled in the view. In other words, just use tryExecute here. - return tryExecute(DocumentService.getDocumentByIdPublicAccess({ id: unique })); + return tryExecute(this.#host, DocumentService.getDocumentByIdPublicAccess({ id: unique })); } /** diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member/repository/validation/member-validation.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member/repository/validation/member-validation.server.data-source.ts index 6dac610d9c98..c62f1642731a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member/repository/validation/member-validation.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member/repository/validation/member-validation.server.data-source.ts @@ -13,12 +13,10 @@ import type { UmbEntityUnique } from '@umbraco-cms/backoffice/entity'; * A server data source for Member Validation */ export class UmbMemberValidationServerDataSource { - //#host: UmbControllerHost; + #host: UmbControllerHost; - // TODO: [v15]: ignoring unused var here here to prevent a breaking change - // eslint-disable-next-line @typescript-eslint/no-unused-vars constructor(host: UmbControllerHost) { - //this.#host = host; + this.#host = host; } /** @@ -47,7 +45,7 @@ export class UmbMemberValidationServerDataSource { // Maybe use: tryExecuteAndNotify return tryExecute( - //this.#host, + this.#host, MemberService.postMemberValidate({ requestBody, }), @@ -79,7 +77,7 @@ export class UmbMemberValidationServerDataSource { // Maybe use: tryExecuteAndNotify return tryExecute( - //this.#host, + this.#host, MemberService.putMemberByIdValidate({ id: model.unique, requestBody, diff --git a/src/Umbraco.Web.UI.Client/src/packages/sysinfo/repository/sysinfo.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/sysinfo/repository/sysinfo.repository.ts index 5f5970d6a037..042a02ed5813 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/sysinfo/repository/sysinfo.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/sysinfo/repository/sysinfo.repository.ts @@ -108,7 +108,7 @@ export class UmbSysinfoRepository extends UmbRepositoryBase { currentVersion: string, ): Promise { // Check the server for an upgrade because we have no stored check or the stored check is invalid - const { data } = await tryExecute(ServerService.getServerUpgradeCheck()); + const { data } = await tryExecute(this, ServerService.getServerUpgradeCheck()); if (data) { // Save the last check date including the data received diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/repository/current-user.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/repository/current-user.server.data-source.ts index 55a12a7824c8..2024f9bbf194 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/repository/current-user.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/repository/current-user.server.data-source.ts @@ -92,6 +92,7 @@ export class UmbCurrentUserServerDataSource { */ async enableMfaProvider(providerName: string, code: string, secret: string) { const { error } = await tryExecute( + this.#host, UserService.postUserCurrent2FaByProviderName({ providerName, requestBody: { code, secret } }), ); @@ -108,7 +109,10 @@ export class UmbCurrentUserServerDataSource { * @param code */ async disableMfaProvider(providerName: string, code: string) { - const { error } = await tryExecute(UserService.deleteUserCurrent2FaByProviderName({ providerName, code })); + const { error } = await tryExecute( + this.#host, + UserService.deleteUserCurrent2FaByProviderName({ providerName, code }), + ); if (error) { return { error }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/types.ts b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/types.ts index e199d687806a..97bc30ab3ed0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/types.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/types.ts @@ -1,12 +1,11 @@ import type { - ApiError, - CancelError, DocumentPermissionPresentationModel, UnknownTypePermissionPresentationModel, UserExternalLoginProviderModel, UserTwoFactorProviderModel, } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbReferenceByUnique } from '@umbraco-cms/backoffice/models'; +import type { UmbApiError, UmbCancelError } from '@umbraco-cms/backoffice/resources'; export type * from './user-profile-app.extension.js'; export type * from './current-user-action.extension.js'; @@ -37,7 +36,7 @@ export type UmbCurrentUserExternalLoginProviderModel = UserExternalLoginProvider export type UmbCurrentUserMfaProviderModel = UserTwoFactorProviderModel; -export type UmbMfaProviderConfigurationCallback = Promise<{ error?: ApiError | CancelError }>; +export type UmbMfaProviderConfigurationCallback = Promise<{ error?: UmbApiError | UmbCancelError }>; export interface UmbMfaProviderConfigurationElementProps { /** diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/sources/user-mfa.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/sources/user-mfa.server.data-source.ts index b7ff6ab6566a..b3411e30188e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/sources/user-mfa.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/sources/user-mfa.server.data-source.ts @@ -45,6 +45,7 @@ export class UmbUserMfaServerDataSource { if (!providerName) throw new Error('Provider is missing'); return tryExecute( + this.#host, UserService.deleteUserById2FaByProviderName({ id: unique, providerName, From 84ecfcce82289e0dfd08c187520d53062f2bff7d Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Fri, 4 Apr 2025 12:05:35 +0200 Subject: [PATCH 17/45] feat: adjusts deprecation notifices and checks --- .../src/packages/core/resources/resource.controller.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/resources/resource.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/resources/resource.controller.ts index 748e14deb3d8..115d7a600501 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/resources/resource.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/resources/resource.controller.ts @@ -40,9 +40,11 @@ export class UmbResourceController extends UmbControllerBase { return UmbApiError.fromLegacyApiError(error); } else if (isCancelError(error)) { return UmbCancelError.fromLegacyCancelError(error); - } else if (error instanceof UmbApiError) { + } else if (UmbCancelError.isUmbCancelError(error)) { return error; - } else if (error instanceof UmbCancelError) { + } else if (UmbApiError.isUmbApiError(error)) { + return error; + } else if (UmbError.isUmbError(error)) { return error; } // If the error is not an UmbError, we will just return it as is @@ -109,7 +111,7 @@ export class UmbResourceController extends UmbControllerBase { new UmbDeprecation({ deprecated: 'UmbResourceController.tryExecuteAndNotify', removeInVersion: '18.0.0', - solution: 'Use the UmbTryExecuteAndNotifyController instead.', + solution: 'Use the UmbTryExecuteController instead.', }); const { data, error } = await UmbResourceController.tryExecute(this._promise); @@ -220,7 +222,7 @@ export class UmbResourceController extends UmbControllerBase { new UmbDeprecation({ deprecated: 'UmbResourceController.xhrRequest', removeInVersion: '18.0.0', - solution: 'Use the UmbXhrRequestController instead.', + solution: 'Use the UmbTryXhrRequestController instead.', }); const baseUrl = options.baseUrl || '/umbraco'; From 0b0d40d7a614fc982867011bc9c6d6e36f304cf1 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Fri, 4 Apr 2025 12:09:14 +0200 Subject: [PATCH 18/45] chore: adjusts deprecation notices --- .../src/packages/core/resources/resource.controller.ts | 10 +++++----- .../core/resources/tryExecuteAndNotify.function.ts | 8 +++++++- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/resources/resource.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/resources/resource.controller.ts index 115d7a600501..a80f7e029786 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/resources/resource.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/resources/resource.controller.ts @@ -79,7 +79,7 @@ export class UmbResourceController extends UmbControllerBase { /** * Base execute function with a try/catch block and return a tuple with the result and the error. - * @deprecated This method is deprecated and will be removed in Umbraco 18. Use the {UmbTryExecuteController} instead. + * @deprecated This method is deprecated and will be removed in Umbraco 18. Use the {@link UmbTryExecuteController} instead. * @param promise */ static async tryExecute(promise: Promise): Promise> { @@ -87,7 +87,7 @@ export class UmbResourceController extends UmbControllerBase { deprecated: 'UmbResourceController.tryExecute', removeInVersion: '18.0.0', solution: 'Use the UmbTryExecuteController instead.', - }); + }).warn(); try { return { data: await promise }; } catch (error) { @@ -103,7 +103,7 @@ export class UmbResourceController extends UmbControllerBase { /** * Wrap the {tryExecute} function in a try/catch block and return the result. * If the executor function throws an error, then show the details in a notification. - * @deprecated This method is deprecated and will be removed in Umbraco 18. Use the {UmbTryExecuteAndNotifyController} instead. + * @deprecated This method is deprecated and will be removed in Umbraco 18. Use the {@link UmbTryExecuteAndNotifyController} instead. * @param options */ @@ -112,7 +112,7 @@ export class UmbResourceController extends UmbControllerBase { deprecated: 'UmbResourceController.tryExecuteAndNotify', removeInVersion: '18.0.0', solution: 'Use the UmbTryExecuteController instead.', - }); + }).warn(); const { data, error } = await UmbResourceController.tryExecute(this._promise); @@ -214,7 +214,7 @@ export class UmbResourceController extends UmbControllerBase { /** * Make an XHR request. - * @deprecated This method is deprecated and will be removed in Umbraco 18. Use the {UmbXhrRequestController} instead. + * @deprecated This method is deprecated and will be removed in Umbraco 18. Use the {@link UmbXhrRequestController} instead. * @param host The controller host for this controller to be appended to. * @param options The options for the XHR request. */ diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/resources/tryExecuteAndNotify.function.ts b/src/Umbraco.Web.UI.Client/src/packages/core/resources/tryExecuteAndNotify.function.ts index 4c19977e42e0..bc2894965d50 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/resources/tryExecuteAndNotify.function.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/resources/tryExecuteAndNotify.function.ts @@ -1,15 +1,21 @@ import { UmbTryExecuteAndNotifyController } from './try-execute-and-notify.controller.js'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import type { UmbDataSourceResponse } from '@umbraco-cms/backoffice/repository'; +import { UmbDeprecation } from '@umbraco-cms/backoffice/utils'; /** - * @deprecated Use the {tryExecute} function instead and handle the error in the caller. + * @deprecated Use the {@link tryExecute} function instead and handle the error in the caller. * This function is kept for backwards compatibility and will be removed in a future version. */ export async function tryExecuteAndNotify( host: UmbControllerHost, resource: Promise, ): Promise> { + new UmbDeprecation({ + deprecated: 'The tryExecuteAndNotify function is deprecated.', + removeInVersion: '18.0.0', + solution: 'Use the tryExecute function instead.', + }).warn(); const controller = new UmbTryExecuteAndNotifyController(host, resource); const response = await controller.tryExecuteAndNotify(); controller.destroy(); From 4c8f90cc23ee18dfe0aabba8aec12ad28dc7984f Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Fri, 4 Apr 2025 13:38:11 +0200 Subject: [PATCH 19/45] chore: add .warn() to deprecation --- .../src/packages/core/resources/resource.controller.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/resources/resource.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/resources/resource.controller.ts index a80f7e029786..3e220c588ceb 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/resources/resource.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/resources/resource.controller.ts @@ -88,6 +88,7 @@ export class UmbResourceController extends UmbControllerBase { removeInVersion: '18.0.0', solution: 'Use the UmbTryExecuteController instead.', }).warn(); + try { return { data: await promise }; } catch (error) { @@ -223,7 +224,7 @@ export class UmbResourceController extends UmbControllerBase { deprecated: 'UmbResourceController.xhrRequest', removeInVersion: '18.0.0', solution: 'Use the UmbTryXhrRequestController instead.', - }); + }).warn(); const baseUrl = options.baseUrl || '/umbraco'; From 871fb1acae5bd262d60c5772e280cc25c5789bf4 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Fri, 4 Apr 2025 14:41:42 +0200 Subject: [PATCH 20/45] feat: updates login app repository --- .../src/contexts/auth.repository.ts | 470 +++++++++--------- 1 file changed, 246 insertions(+), 224 deletions(-) diff --git a/src/Umbraco.Web.UI.Login/src/contexts/auth.repository.ts b/src/Umbraco.Web.UI.Login/src/contexts/auth.repository.ts index 6bbc9edce4c8..62933575b119 100644 --- a/src/Umbraco.Web.UI.Login/src/contexts/auth.repository.ts +++ b/src/Umbraco.Web.UI.Login/src/contexts/auth.repository.ts @@ -1,229 +1,251 @@ import { - LoginRequestModel, - LoginResponse, MfaCodeResponse, - NewPasswordResponse, - PasswordConfigurationModel, - ResetPasswordResponse, - ValidateInviteCodeResponse, - ValidatePasswordResetCodeResponse -} from "../types.js"; -import { UmbRepositoryBase } from "@umbraco-cms/backoffice/repository"; -import { UmbLocalizationController } from "@umbraco-cms/backoffice/localization-api"; + LoginRequestModel, + LoginResponse, + MfaCodeResponse, + NewPasswordResponse, + PasswordConfigurationModel, + ResetPasswordResponse, + ValidateInviteCodeResponse, + ValidatePasswordResetCodeResponse, +} from '../types.js'; +import { UmbRepositoryBase } from '@umbraco-cms/backoffice/repository'; +import { UmbLocalizationController } from '@umbraco-cms/backoffice/localization-api'; import { - ApiError, - CancelError, - ProblemDetails, - SecurityService, - UserService -} from "@umbraco-cms/backoffice/external/backend-api"; -import { tryExecute } from "@umbraco-cms/backoffice/resources"; + ApiError, + CancelError, + ProblemDetails, + SecurityService, + UserService, +} from '@umbraco-cms/backoffice/external/backend-api'; +import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; export class UmbAuthRepository extends UmbRepositoryBase { - #localize = new UmbLocalizationController(this); - - public async login(data: LoginRequestModel): Promise { - try { - const request = new Request('management/api/v1/security/back-office/login', { - method: 'POST', - body: JSON.stringify({ - username: data.username, - password: data.password, - }), - headers: { - 'Content-Type': 'application/json', - }, - }); - - const response = await fetch(request); - - if (!response.ok) { - // If the response code is 402, it means that the user has enabled 2-factor authentication - if (response.status === 402) { - const responseData = await response.json(); - return { - status: response.status, - twoFactorView: responseData.twoFactorLoginView ?? '', - twoFactorProviders: responseData.enabledTwoFactorProviderNames ?? [], - }; - } - - return { - status: response.status, - error: await this.#getErrorText(response), - }; - } - - return { - status: response.status, - data: { - username: data.username, - }, - }; - } catch (error) { - return { - status: 500, - error: error instanceof Error ? error.message : this.#localize.term('auth_receivedErrorFromServer'), - }; - } - } - - public async validateMfaCode(code: string, provider: string): Promise { - try { - const requestData = new Request('management/api/v1/security/back-office/verify-2fa', { - method: 'POST', - body: JSON.stringify({ - code, - provider, - }), - headers: { - 'Content-Type': 'application/json', - }, - }); - - const response = await fetch(requestData); - - if (!response.ok) { - return { - error: response.status === 400 ? this.#localize.term('auth_mfaInvalidCode') : await this.#getErrorText(response), - }; - } - - return {}; - } catch (error) { - return { - error: error instanceof Error ? error.message : this.#localize.term('auth_receivedErrorFromServer'), - }; - } - } - - public async resetPassword(email: string): Promise { - const response = await tryExecute(SecurityService.postSecurityForgotPassword({ - requestBody: { - email - } - })) - - if (response.error) { - return { - error: this.#getApiErrorDetailText(response.error, 'Could not reset the password'), - }; - } - - return {}; - } - - public async validatePasswordResetCode(userId: string, resetCode: string): Promise { - const { data, error } = await tryExecute(SecurityService.postSecurityForgotPasswordVerify({ - requestBody: { - user: { - id: userId - }, - resetCode - } - })); - - if (error || !data) { - return { - error: this.#getApiErrorDetailText(error, 'Could not validate the password reset code') - }; - } - - return { - passwordConfiguration: (data as unknown as {passwordConfiguration: PasswordConfigurationModel}).passwordConfiguration // TODO: Fix this when the API schema has been updated - }; - } - - public async newPassword(password: string, resetCode: string, userId: string): Promise { - const response = await tryExecute(SecurityService.postSecurityForgotPasswordReset({ - requestBody: { - password, - resetCode, - user: { - id: userId - } - } - })); - - if (response.error) { - return { - error: this.#getApiErrorDetailText(response.error, 'Could not reset the password'), - }; - } - - return {}; - } - - public async validateInviteCode(token: string, userId: string): Promise { - const { data, error } = await tryExecute(UserService.postUserInviteVerify({ - requestBody: { - token, - user: { - id: userId - } - } - })); - - if (error || !data) { - return { - error: this.#getApiErrorDetailText(error, 'Could not validate the invite code') - }; - } - - return { - passwordConfiguration: (data as unknown as {passwordConfiguration: PasswordConfigurationModel}).passwordConfiguration // TODO: Fix this when the API schema has been updated - }; - } - - public async newInvitedUserPassword(password: string, token: string, userId: string): Promise { - const response = await tryExecute(UserService.postUserInviteCreatePassword({ - requestBody: { - password, - token, - user: { - id: userId - } - } - })); - - if (response.error) { - return { - error: this.#getApiErrorDetailText(response.error, 'Could not create a password for the invited user') - }; - } - - return {}; - } - - #getApiErrorDetailText(error: ApiError | CancelError | undefined, fallbackText?: string): string | undefined { - if (error instanceof ApiError) { - // Try to parse the body - return typeof error.body === 'object' ? (error.body as ProblemDetails).title ?? fallbackText : fallbackText ?? 'An unknown error occurred.'; - } - - // Ignore cancel errors (user cancelled the request) - if (error instanceof CancelError) { - return undefined; - } - - return fallbackText ?? 'An unknown error occurred.'; - } - - async #getErrorText(response: Response): Promise { - switch (response.status) { - case 400: - case 401: - return this.#localize.term('auth_userFailedLogin'); - - case 402: - return this.#localize.term('auth_mfaText'); - - case 403: - return this.#localize.term('auth_userLockedOut'); - - default: - return ( - this.#localize.term('auth_receivedErrorFromServer') - ); - } - } + #localize = new UmbLocalizationController(this); + + public async login(data: LoginRequestModel): Promise { + try { + const request = new Request('management/api/v1/security/back-office/login', { + method: 'POST', + body: JSON.stringify({ + username: data.username, + password: data.password, + }), + headers: { + 'Content-Type': 'application/json', + }, + }); + + const response = await fetch(request); + + if (!response.ok) { + // If the response code is 402, it means that the user has enabled 2-factor authentication + if (response.status === 402) { + const responseData = await response.json(); + return { + status: response.status, + twoFactorView: responseData.twoFactorLoginView ?? '', + twoFactorProviders: responseData.enabledTwoFactorProviderNames ?? [], + }; + } + + return { + status: response.status, + error: await this.#getErrorText(response), + }; + } + + return { + status: response.status, + data: { + username: data.username, + }, + }; + } catch (error) { + return { + status: 500, + error: error instanceof Error ? error.message : this.#localize.term('auth_receivedErrorFromServer'), + }; + } + } + + public async validateMfaCode(code: string, provider: string): Promise { + try { + const requestData = new Request('management/api/v1/security/back-office/verify-2fa', { + method: 'POST', + body: JSON.stringify({ + code, + provider, + }), + headers: { + 'Content-Type': 'application/json', + }, + }); + + const response = await fetch(requestData); + + if (!response.ok) { + return { + error: + response.status === 400 ? this.#localize.term('auth_mfaInvalidCode') : await this.#getErrorText(response), + }; + } + + return {}; + } catch (error) { + return { + error: error instanceof Error ? error.message : this.#localize.term('auth_receivedErrorFromServer'), + }; + } + } + + public async resetPassword(email: string): Promise { + const response = await tryExecuteAndNotify( + this, + SecurityService.postSecurityForgotPassword({ + requestBody: { + email, + }, + }) + ); + + if (response.error) { + return { + error: this.#getApiErrorDetailText(response.error, 'Could not reset the password'), + }; + } + + return {}; + } + + public async validatePasswordResetCode( + userId: string, + resetCode: string + ): Promise { + const { data, error } = await tryExecuteAndNotify( + this, + SecurityService.postSecurityForgotPasswordVerify({ + requestBody: { + user: { + id: userId, + }, + resetCode, + }, + }) + ); + + if (error || !data) { + return { + error: this.#getApiErrorDetailText(error, 'Could not validate the password reset code'), + }; + } + + return { + passwordConfiguration: (data as unknown as { passwordConfiguration: PasswordConfigurationModel }) + .passwordConfiguration, // TODO: Fix this when the API schema has been updated + }; + } + + public async newPassword(password: string, resetCode: string, userId: string): Promise { + const response = await tryExecuteAndNotify( + this, + SecurityService.postSecurityForgotPasswordReset({ + requestBody: { + password, + resetCode, + user: { + id: userId, + }, + }, + }) + ); + + if (response.error) { + return { + error: this.#getApiErrorDetailText(response.error, 'Could not reset the password'), + }; + } + + return {}; + } + + public async validateInviteCode(token: string, userId: string): Promise { + const { data, error } = await tryExecuteAndNotify( + this, + UserService.postUserInviteVerify({ + requestBody: { + token, + user: { + id: userId, + }, + }, + }) + ); + + if (error || !data) { + return { + error: this.#getApiErrorDetailText(error, 'Could not validate the invite code'), + }; + } + + return { + passwordConfiguration: (data as unknown as { passwordConfiguration: PasswordConfigurationModel }) + .passwordConfiguration, // TODO: Fix this when the API schema has been updated + }; + } + + public async newInvitedUserPassword(password: string, token: string, userId: string): Promise { + const response = await tryExecuteAndNotify( + this, + UserService.postUserInviteCreatePassword({ + requestBody: { + password, + token, + user: { + id: userId, + }, + }, + }) + ); + + if (response.error) { + return { + error: this.#getApiErrorDetailText(response.error, 'Could not create a password for the invited user'), + }; + } + + return {}; + } + + #getApiErrorDetailText(error: ApiError | CancelError | undefined, fallbackText?: string): string | undefined { + if (error instanceof ApiError) { + // Try to parse the body + return typeof error.body === 'object' + ? (error.body as ProblemDetails).title ?? fallbackText + : fallbackText ?? 'An unknown error occurred.'; + } + + // Ignore cancel errors (user cancelled the request) + if (error instanceof CancelError) { + return undefined; + } + + return fallbackText ?? 'An unknown error occurred.'; + } + + async #getErrorText(response: Response): Promise { + switch (response.status) { + case 400: + case 401: + return this.#localize.term('auth_userFailedLogin'); + + case 402: + return this.#localize.term('auth_mfaText'); + + case 403: + return this.#localize.term('auth_userLockedOut'); + + default: + return this.#localize.term('auth_receivedErrorFromServer'); + } + } } From 4ac607efb6fb50faf1697b68d9ce1c25766a622e Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Fri, 4 Apr 2025 14:49:07 +0200 Subject: [PATCH 21/45] feat: changes all `tryExecuteAndNotify` calls to `tryExecute` --- .../eslint/rules/no-direct-api-import.cjs | 6 ++--- ...server-extension-registrator.controller.ts | 8 +++---- ...-type-structure-server-data-source-base.ts | 7 ++---- .../repository/sources/culture.server.data.ts | 4 ++-- .../object-type/object-type.repository.ts | 4 ++-- .../item/item-server-data-source-base.ts | 4 ++-- .../config/config.server.data-source.ts | 4 ++-- .../temporary-file.server.data-source.ts | 6 ++--- .../tree/data/tree-server-data-source-base.ts | 8 +++---- ...data-type-collection.server.data-source.ts | 4 ++-- .../data-type-duplicate.server.data-source.ts | 4 ++-- .../data-type-move.server.data-source.ts | 4 ++-- .../data-type-reference.server.data.ts | 4 ++-- .../data-type-detail.server.data-source.ts | 10 ++++---- .../data-type-search.server.data-source.ts | 4 ++-- .../data-type-folder.server.data-source.ts | 10 ++++---- ...ictionary-collection.server.data-source.ts | 4 ++-- .../dictionary-move.server.data-source.ts | 4 ++-- .../dictionary-detail.server.data-source.ts | 10 ++++---- .../dictionary-export.server.data-source.ts | 7 ++---- .../dictionary-import.server.data-source.ts | 4 ++-- .../dictionary-search.server.data-source.ts | 4 ++-- ...ument-blueprint-move.server.data-source.ts | 4 ++-- ...ent-blueprint-detail.server.data-source.ts | 10 ++++---- ...ument-blueprint-item.server.data-source.ts | 4 ++-- ...ent-blueprint-folder.server.data-source.ts | 10 ++++---- .../dashboard-redirect-management.element.ts | 13 ++++------- ...ument-type-duplicate.server.data-source.ts | 4 ++-- ...document-type-export.server.data-source.ts | 4 ++-- ...document-type-import.server.data-source.ts | 4 ++-- .../document-type-move.server.data-source.ts | 4 ++-- ...ent-type-composition.server.data-source.ts | 6 ++--- ...document-type-detail.server.data-source.ts | 13 ++++------- ...document-type-search.server.data-source.ts | 4 ++-- ...document-type-folder.server.data-source.ts | 10 ++++---- .../document-audit-log.server.data-source.ts | 4 ++-- .../document-collection.server.data-source.ts | 4 ++-- ...ent-create-blueprint.server.data-source.ts | 4 ++-- .../culture-and-hostnames.server.data.ts | 6 ++--- .../document-duplicate.server.data-source.ts | 4 ++-- .../document-move.server.data-source.ts | 4 ++-- .../document-notifications.server.data.ts | 9 +++----- .../sort-children-of.server.data.ts | 4 ++-- .../document-configuration.context.ts | 4 ++-- .../document-publishing.server.data-source.ts | 18 +++++---------- ...document-recycle-bin.server.data-source.ts | 10 ++++---- .../document-reference.server.data.ts | 8 +++---- .../document-detail.server.data-source.ts | 10 ++++---- .../repository/rollback.server.data-source.ts | 13 ++++------- .../document-search.server.data-source.ts | 4 ++-- .../document-permission.server.data.ts | 4 ++-- .../repository/oembed.server.data.ts | 4 ++-- .../dashboard-health-check.element.ts | 4 ++-- .../health-check/health-check.context.ts | 6 ++--- .../views/health-check-action.element.ts | 4 ++-- .../language-collection.server.data-source.ts | 4 ++-- .../language-detail.server.data-source.ts | 13 ++++------- .../sources/log-viewer.server.data.ts | 23 ++++++++----------- .../media/imaging/imaging.server.data.ts | 4 ++-- ...media-type-duplicate.server.data-source.ts | 4 ++-- .../media-type-export.server.data-source.ts | 4 ++-- .../media-type-import.server.data-source.ts | 4 ++-- .../media-type-move.server.data-source.ts | 4 ++-- ...dia-type-composition.server.data-source.ts | 9 +++----- .../media-type-detail.server.data-source.ts | 10 ++++---- .../media-type-search.server.data-source.ts | 4 ++-- .../media-type-folder.server.data-source.ts | 10 ++++---- .../media-audit-log.server.data-source.ts | 4 ++-- .../media-collection.server.data-source.ts | 4 ++-- .../media-move.server.data-source.ts | 4 ++-- .../sort-children-of.server.data.ts | 4 ++-- .../media-recycle-bin.server.data-source.ts | 10 ++++---- .../repository/media-reference.server.data.ts | 14 ++++------- .../detail/media-detail.server.data-source.ts | 10 ++++---- .../item/media-item.server.data-source.ts | 7 ++---- .../search/media-search.server.data-source.ts | 4 ++-- ...ber-group-collection.server.data-source.ts | 4 ++-- .../member-group-detail.server.data-source.ts | 13 ++++------- .../repository/public-access.server.data.ts | 9 +++----- ...ember-type-duplicate.server.data-source.ts | 4 ++-- ...ber-type-composition.server.data-source.ts | 6 ++--- .../member-type-detail.server.data-source.ts | 10 ++++---- .../member-type-search.server.data-source.ts | 4 ++-- .../member-collection.server.data-source.ts | 4 ++-- .../member-reference.server.data.ts | 14 ++++------- .../member-detail.server.data-source.ts | 10 ++++---- .../member-search.server.data-source.ts | 4 ++-- .../models-builder-dashboard.element.ts | 6 ++--- ...lled-packages-section-view-item.element.ts | 7 ++---- .../repository/sources/package.server.data.ts | 22 +++++++++--------- ...dashboard-performance-profiling.element.ts | 6 ++--- .../repository/dynamic-root.server.data.ts | 4 ++-- .../dashboard-published-status.element.ts | 8 +++---- ...tion-type-collection.server.data-source.ts | 4 ++-- ...relation-type-detail.server.data-source.ts | 7 ++---- .../relation-collection.server.data-source.ts | 7 ++---- .../views/section-view-examine-indexers.ts | 9 +++----- .../views/section-view-examine-overview.ts | 6 ++--- .../views/section-view-examine-searchers.ts | 4 ++-- .../sysinfo/repository/sysinfo.repository.ts | 4 ++-- .../repository/sources/tag.server.data.ts | 4 ++-- .../telemetry/dashboard-telemetry.element.ts | 8 +++---- .../create-from-snippet-modal.ts | 4 ++-- .../rename-partial-view.server.data-source.ts | 4 ++-- .../partial-view-item.server.data-source.ts | 4 ++-- .../partial-view-detail.server.data-source.ts | 10 ++++---- .../partial-view-folder.server.data-source.ts | 8 +++---- .../partial-view-workspace.context.ts | 4 ++-- .../rename-script.server.data-source.ts | 4 ++-- .../item/script-item.server.data-source.ts | 4 ++-- .../script-detail.server.data-source.ts | 10 ++++---- .../script-folder.server.data-source.ts | 8 +++---- .../rename-stylesheet.server.data-source.ts | 4 ++-- .../stylesheet-item.server.data-source.ts | 4 ++-- .../stylesheet-detail.server.data-source.ts | 10 ++++---- .../stylesheet-folder.server.data-source.ts | 8 +++---- .../template-detail.server.data-source.ts | 10 ++++---- .../template-query.server.data-source.ts | 6 ++--- .../template-search.server.data-source.ts | 4 ++-- .../mfa-provider-default.element.ts | 8 +++---- .../current-user.server.data-source.ts | 8 +++---- ...ser-group-collection.server.data-source.ts | 4 ++-- .../user-group-detail.server.data-source.ts | 10 ++++---- ...er-client-credential.server.data-source.ts | 8 +++---- .../user-collection.server.data-source.ts | 4 ++-- .../invite-user-server.data-source.ts | 6 ++--- .../avatar/user-avatar.server.data-source.ts | 6 ++--- ...change-user-password.server.data-source.ts | 4 ++-- .../current-user-config.server.data-source.ts | 4 ++-- .../config/user-config.server.data-source.ts | 6 ++--- .../detail/user-detail.server.data-source.ts | 12 +++++----- .../disable-user.server.data-source.ts | 4 ++-- .../enable/enable-user.server.data-source.ts | 4 ++-- .../new-user-password.server.data-source.ts | 4 ++-- .../sources/user-mfa.server.data-source.ts | 2 +- .../user-set-group.server.data-source.ts | 4 ++-- .../unlock/unlock-user.server.data-source.ts | 4 ++-- ...-delivery-collection.server.data-source.ts | 4 ++-- .../webhook-event.server.data-source.ts | 4 ++-- .../webhook-collection.server.data-source.ts | 4 ++-- .../webhook-detail.server.data-source.ts | 10 ++++---- 141 files changed, 424 insertions(+), 490 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/devops/eslint/rules/no-direct-api-import.cjs b/src/Umbraco.Web.UI.Client/devops/eslint/rules/no-direct-api-import.cjs index 9f3a3912dc4b..bd0b06de69fc 100644 --- a/src/Umbraco.Web.UI.Client/devops/eslint/rules/no-direct-api-import.cjs +++ b/src/Umbraco.Web.UI.Client/devops/eslint/rules/no-direct-api-import.cjs @@ -12,7 +12,7 @@ module.exports = { }, create: function (context) { return { - // If methods called on *Service classes are not already wrapped with `await tryExecuteAndNotify()`, then we should suggest to wrap them. + // If methods called on *Service classes are not already wrapped with `await tryExecute()`, then we should suggest to wrap them. CallExpression: function (node) { if ( node.callee.type === 'MemberExpression' && @@ -28,9 +28,9 @@ module.exports = { if (!hasTryExecuteAndNotify) { context.report({ node, - message: 'Wrap this call with `tryExecuteAndNotify()`. Make sure to `await` the result.', + message: 'Wrap this call with `tryExecute()`. Make sure to `await` the result.', fix: (fixer) => [ - fixer.insertTextBefore(node, 'tryExecuteAndNotify(this, '), + fixer.insertTextBefore(node, 'tryExecute(this, '), fixer.insertTextAfter(node, ')'), ], }); diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/controller/server-extension-registrator.controller.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/controller/server-extension-registrator.controller.ts index d582f2f3077e..b0f77cffe704 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/extension-api/controller/server-extension-registrator.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/extension-api/controller/server-extension-registrator.controller.ts @@ -4,7 +4,7 @@ import { OpenAPI, ManifestService, type ManifestResponseModel } from '@umbraco-c import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import type { UmbBackofficeExtensionRegistry } from '@umbraco-cms/backoffice/extension-registry'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; // TODO: consider if this can be replaced by the new extension controllers export class UmbServerExtensionRegistrator extends UmbControllerBase { @@ -22,7 +22,7 @@ export class UmbServerExtensionRegistrator extends UmbControllerBase { * @remark Users must have the BACKOFFICE_ACCESS permission to access this method. */ public async registerAllExtensions() { - const { data: packages } = await tryExecuteAndNotify(this, ManifestService.getManifestManifest()); + const { data: packages } = await tryExecute(this, ManifestService.getManifestManifest()); if (packages) { await this.#loadServerPackages(packages); } @@ -34,7 +34,7 @@ export class UmbServerExtensionRegistrator extends UmbControllerBase { * @remark Users must have the BACKOFFICE_ACCESS permission to access this method. */ public async registerPrivateExtensions() { - const { data: packages } = await tryExecuteAndNotify(this, ManifestService.getManifestManifestPrivate()); + const { data: packages } = await tryExecute(this, ManifestService.getManifestManifestPrivate()); if (packages) { await this.#loadServerPackages(packages); } @@ -46,7 +46,7 @@ export class UmbServerExtensionRegistrator extends UmbControllerBase { * @remark Any user can access this method without any permissions. */ public async registerPublicExtensions() { - const { data: packages } = await tryExecuteAndNotify(this, ManifestService.getManifestManifestPublic()); + const { data: packages } = await tryExecute(this, ManifestService.getManifestManifestPublic()); if (packages) { await this.#loadServerPackages(packages); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/repository/structure/content-type-structure-server-data-source-base.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/repository/structure/content-type-structure-server-data-source-base.ts index 4991c103b33d..b370ae568142 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/repository/structure/content-type-structure-server-data-source-base.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/repository/structure/content-type-structure-server-data-source-base.ts @@ -2,7 +2,7 @@ import type { UmbPagedModel } from '../../../repository/types.js'; import type { UmbContentTypeStructureDataSource } from './content-type-structure-data-source.interface.js'; import type { UmbEntityModel } from '@umbraco-cms/backoffice/entity'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; // Keep this type internal type AllowedContentTypeBaseModel = { @@ -55,10 +55,7 @@ export abstract class UmbContentTypeStructureServerDataSourceBase< * @memberof UmbContentTypeStructureServerDataSourceBase */ async getAllowedChildrenOf(unique: string | null, parentContentUnique: string | null) { - const { data, error } = await tryExecuteAndNotify( - this.#host, - this.#getAllowedChildrenOf(unique, parentContentUnique), - ); + const { data, error } = await tryExecute(this.#host, this.#getAllowedChildrenOf(unique, parentContentUnique)); if (data) { const items = data.items.map((item) => this.#mapper(item)); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/culture/repository/sources/culture.server.data.ts b/src/Umbraco.Web.UI.Client/src/packages/core/culture/repository/sources/culture.server.data.ts index 14a3574c59b6..99343287d7e3 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/culture/repository/sources/culture.server.data.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/culture/repository/sources/culture.server.data.ts @@ -1,7 +1,7 @@ import type { UmbCultureDataSource } from './index.js'; import { CultureService } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; /** * A data source for the Language that fetches data from the server @@ -29,6 +29,6 @@ export class UmbCultureServerDataSource implements UmbCultureDataSource { * @memberof UmbLanguageServerDataSource */ async getCollection({ skip, take }: { skip: number; take: number }) { - return tryExecuteAndNotify(this.#host, CultureService.getCulture({ skip, take })); + return tryExecute(this.#host, CultureService.getCulture({ skip, take })); } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/object-type/object-type.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/core/object-type/object-type.repository.ts index ab9fa73ee5ad..779db8b3381b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/object-type/object-type.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/object-type/object-type.repository.ts @@ -2,7 +2,7 @@ import { ObjectTypesService } from '@umbraco-cms/backoffice/external/backend-api import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import type { UmbApi } from '@umbraco-cms/backoffice/extension-api'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; export class UmbObjectTypeRepository extends UmbControllerBase implements UmbApi { #host: UmbControllerHost; @@ -14,7 +14,7 @@ export class UmbObjectTypeRepository extends UmbControllerBase implements UmbApi } async #read() { - return tryExecuteAndNotify(this.#host, ObjectTypesService.getObjectTypes({})); + return tryExecute(this.#host, ObjectTypesService.getObjectTypes({})); } async read() { diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/repository/item/item-server-data-source-base.ts b/src/Umbraco.Web.UI.Client/src/packages/core/repository/item/item-server-data-source-base.ts index 5f50233b78e8..9ae47938eb5f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/repository/item/item-server-data-source-base.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/repository/item/item-server-data-source-base.ts @@ -1,6 +1,6 @@ import type { UmbItemDataSource } from '@umbraco-cms/backoffice/repository'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; export interface UmbItemServerDataSourceBaseArgs { getItems: (uniques: Array) => Promise>; @@ -39,7 +39,7 @@ export abstract class UmbItemServerDataSourceBase) { if (!uniques) throw new Error('Uniques are missing'); - const { data, error } = await tryExecuteAndNotify(this.#host, this.#getItems(uniques)); + const { data, error } = await tryExecute(this.#host, this.#getItems(uniques)); if (data) { const items = data.map((item) => this.#mapper(item)); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/temporary-file/config/config.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/core/temporary-file/config/config.server.data-source.ts index 10d2db7a04e6..3f3ac52aa9b6 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/temporary-file/config/config.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/temporary-file/config/config.server.data-source.ts @@ -1,6 +1,6 @@ import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { TemporaryFileService } from '@umbraco-cms/backoffice/external/backend-api'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; export class UmbTemporaryFileConfigServerDataSource { #host; @@ -13,6 +13,6 @@ export class UmbTemporaryFileConfigServerDataSource { * Get the temporary file configuration. */ getConfig() { - return tryExecuteAndNotify(this.#host, TemporaryFileService.getTemporaryFileConfiguration()); + return tryExecute(this.#host, TemporaryFileService.getTemporaryFileConfiguration()); } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/temporary-file/temporary-file.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/core/temporary-file/temporary-file.server.data-source.ts index b155ac59f4bc..ff3642c385d2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/temporary-file/temporary-file.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/temporary-file/temporary-file.server.data-source.ts @@ -1,7 +1,7 @@ import type { UmbDataSourceResponse } from '../repository/index.js'; import { TemporaryFileService, type PostTemporaryFileResponse } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { tryExecuteAndNotify, tryXhrRequest } from '@umbraco-cms/backoffice/resources'; +import { tryExecute, tryXhrRequest } from '@umbraco-cms/backoffice/resources'; /** * A data source to upload temporary files to the server @@ -55,7 +55,7 @@ export class UmbTemporaryFileServerDataSource { */ read(id: string) { if (!id) throw new Error('Id is missing'); - return tryExecuteAndNotify(this.#host, TemporaryFileService.getTemporaryFileById({ id })); + return tryExecute(this.#host, TemporaryFileService.getTemporaryFileById({ id })); } /** @@ -66,6 +66,6 @@ export class UmbTemporaryFileServerDataSource { */ delete(id: string) { if (!id) throw new Error('Id is missing'); - return tryExecuteAndNotify(this.#host, TemporaryFileService.deleteTemporaryFileById({ id })); + return tryExecute(this.#host, TemporaryFileService.deleteTemporaryFileById({ id })); } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/tree/data/tree-server-data-source-base.ts b/src/Umbraco.Web.UI.Client/src/packages/core/tree/data/tree-server-data-source-base.ts index ff035537a10f..d8069603f207 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/tree/data/tree-server-data-source-base.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/tree/data/tree-server-data-source-base.ts @@ -5,7 +5,7 @@ import type { UmbTreeChildrenOfRequestArgs, UmbTreeRootItemsRequestArgs, } from './types.js'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import type { UmbPagedModel } from '@umbraco-cms/backoffice/repository'; @@ -77,7 +77,7 @@ export abstract class UmbTreeServerDataSourceBase< * @memberof UmbTreeServerDataSourceBase */ async getRootItems(args: TreeRootItemsRequestArgsType) { - const { data, error } = await tryExecuteAndNotify(this.#host, this.#getRootItems(args)); + const { data, error } = await tryExecute(this.#host, this.#getRootItems(args)); if (data) { const items = data?.items.map((item) => this.#mapper(item)); @@ -96,7 +96,7 @@ export abstract class UmbTreeServerDataSourceBase< async getChildrenOf(args: TreeChildrenOfRequestArgsType) { if (args.parent.unique === undefined) throw new Error('Parent unique is missing'); - const { data, error } = await tryExecuteAndNotify(this.#host, this.#getChildrenOf(args)); + const { data, error } = await tryExecute(this.#host, this.#getChildrenOf(args)); if (data) { const items = data?.items.map((item: ServerTreeItemType) => this.#mapper(item)); @@ -115,7 +115,7 @@ export abstract class UmbTreeServerDataSourceBase< async getAncestorsOf(args: TreeAncestorsOfRequestArgsType) { if (!args.treeItem.entityType) throw new Error('Parent unique is missing'); - const { data, error } = await tryExecuteAndNotify(this.#host, this.#getAncestorsOf(args)); + const { data, error } = await tryExecute(this.#host, this.#getAncestorsOf(args)); if (data) { const items = data?.map((item: ServerTreeItemType) => this.#mapper(item)); diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/collection/repository/data-type-collection.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/collection/repository/data-type-collection.server.data-source.ts index b986141d01f6..637f8dde0e57 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/collection/repository/data-type-collection.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/collection/repository/data-type-collection.server.data-source.ts @@ -1,7 +1,7 @@ import type { UmbDataTypeCollectionFilterModel } from '../types.js'; import type { UmbDataTypeItemModel } from '../../repository/index.js'; import { UMB_DATA_TYPE_ENTITY_TYPE } from '../../entity.js'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; import { DataTypeService } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbCollectionDataSource } from '@umbraco-cms/backoffice/collection'; import type { DataTypeItemResponseModel } from '@umbraco-cms/backoffice/external/backend-api'; @@ -40,7 +40,7 @@ export class UmbDataTypeCollectionServerDataSource implements UmbCollectionDataS * @DataTypeof UmbDataTypeCollectionServerDataSource */ async getCollection(filter: UmbDataTypeCollectionFilterModel) { - const { data, error } = await tryExecuteAndNotify(this.#host, DataTypeService.getFilterDataType(filter)); + const { data, error } = await tryExecute(this.#host, DataTypeService.getFilterDataType(filter)); if (error) { return { error }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/entity-actions/duplicate/repository/data-type-duplicate.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/entity-actions/duplicate/repository/data-type-duplicate.server.data-source.ts index c95604577e78..73899fb42a2d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/entity-actions/duplicate/repository/data-type-duplicate.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/entity-actions/duplicate/repository/data-type-duplicate.server.data-source.ts @@ -1,6 +1,6 @@ import { DataTypeService } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; import type { UmbDuplicateToDataSource, UmbDuplicateToRequestArgs } from '@umbraco-cms/backoffice/tree'; /** @@ -29,7 +29,7 @@ export class UmbDuplicateDataTypeServerDataSource implements UmbDuplicateToDataS if (!args.unique) throw new Error('Unique is missing'); if (args.destination.unique === undefined) throw new Error('Destination unique is missing'); - return tryExecuteAndNotify( + return tryExecute( this.#host, DataTypeService.postDataTypeByIdCopy({ id: args.unique, diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/entity-actions/move-to/repository/data-type-move.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/entity-actions/move-to/repository/data-type-move.server.data-source.ts index 524b71b54145..22982c86c1d7 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/entity-actions/move-to/repository/data-type-move.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/entity-actions/move-to/repository/data-type-move.server.data-source.ts @@ -1,6 +1,6 @@ import { DataTypeService } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; import type { UmbMoveDataSource, UmbMoveToRequestArgs } from '@umbraco-cms/backoffice/tree'; /** @@ -31,7 +31,7 @@ export class UmbMoveDataTypeServerDataSource implements UmbMoveDataSource { if (!args.unique) throw new Error('Unique is missing'); if (args.destination.unique === undefined) throw new Error('Destination unique is missing'); - return tryExecuteAndNotify( + return tryExecute( this.#host, DataTypeService.putDataTypeByIdMove({ id: args.unique, diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/reference/repository/data-type-reference.server.data.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/reference/repository/data-type-reference.server.data.ts index b94f270e188b..e6539a72942d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/reference/repository/data-type-reference.server.data.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/reference/repository/data-type-reference.server.data.ts @@ -1,4 +1,4 @@ -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; import { DataTypeService } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; @@ -25,6 +25,6 @@ export class UmbDataTypeReferenceServerDataSource { * @memberof UmbDataTypeReferenceServerDataSource */ async getReferencedBy(id: string) { - return await tryExecuteAndNotify(this.#host, DataTypeService.getDataTypeByIdReferences({ id })); + return await tryExecute(this.#host, DataTypeService.getDataTypeByIdReferences({ id })); } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/repository/detail/data-type-detail.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/repository/detail/data-type-detail.server.data-source.ts index 3eb91ed61f4b..d085fcfedd1a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/repository/detail/data-type-detail.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/repository/detail/data-type-detail.server.data-source.ts @@ -8,7 +8,7 @@ import type { } from '@umbraco-cms/backoffice/external/backend-api'; import { DataTypeService } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; /** * A data source for the Data Type that fetches data from the server @@ -57,7 +57,7 @@ export class UmbDataTypeServerDataSource implements UmbDetailDataSource { diff --git a/src/Umbraco.Web.UI.Client/src/packages/dictionary/entity-action/move-to/repository/dictionary-move.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/dictionary/entity-action/move-to/repository/dictionary-move.server.data-source.ts index a18f0f991646..247a15f6c4db 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/dictionary/entity-action/move-to/repository/dictionary-move.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/dictionary/entity-action/move-to/repository/dictionary-move.server.data-source.ts @@ -1,6 +1,6 @@ import { DictionaryService } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; import type { UmbMoveDataSource, UmbMoveToRequestArgs } from '@umbraco-cms/backoffice/tree'; /** @@ -31,7 +31,7 @@ export class UmbMoveDictionaryServerDataSource implements UmbMoveDataSource { if (!args.unique) throw new Error('Unique is missing'); if (args.destination.unique === undefined) throw new Error('Destination unique is missing'); - return tryExecuteAndNotify( + return tryExecute( this.#host, DictionaryService.putDictionaryByIdMove({ id: args.unique, diff --git a/src/Umbraco.Web.UI.Client/src/packages/dictionary/repository/detail/dictionary-detail.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/dictionary/repository/detail/dictionary-detail.server.data-source.ts index de6a91d59db2..d7f66fd676b5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/dictionary/repository/detail/dictionary-detail.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/dictionary/repository/detail/dictionary-detail.server.data-source.ts @@ -8,7 +8,7 @@ import type { } from '@umbraco-cms/backoffice/external/backend-api'; import { DictionaryService } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; /** * A data source for the Dictionary that fetches data from the server @@ -52,7 +52,7 @@ export class UmbDictionaryServerDataSource implements UmbDetailDataSource x.id !== id); @@ -127,10 +127,7 @@ export class UmbDashboardRedirectManagementElement extends UmbLitElement { async #trackerToggle() { const status = this._trackerEnabled ? RedirectStatusModel.DISABLED : RedirectStatusModel.ENABLED; - const { error } = await tryExecuteAndNotify( - this, - RedirectManagementService.postRedirectManagementStatus({ status }), - ); + const { error } = await tryExecute(this, RedirectManagementService.postRedirectManagementStatus({ status })); if (error) return; this._trackerEnabled = !this._trackerEnabled; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/duplicate/repository/document-type-duplicate.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/duplicate/repository/document-type-duplicate.server.data-source.ts index 25c78481f381..afc6c0a7050e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/duplicate/repository/document-type-duplicate.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/duplicate/repository/document-type-duplicate.server.data-source.ts @@ -1,6 +1,6 @@ import { DocumentTypeService } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; import type { UmbDuplicateToDataSource, UmbDuplicateToRequestArgs } from '@umbraco-cms/backoffice/tree'; /** @@ -29,7 +29,7 @@ export class UmbDuplicateDocumentTypeServerDataSource implements UmbDuplicateToD if (!args.unique) throw new Error('Unique is missing'); if (args.destination.unique === undefined) throw new Error('Destination unique is missing'); - return tryExecuteAndNotify( + return tryExecute( this.#host, DocumentTypeService.postDocumentTypeByIdCopy({ id: args.unique, diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/export/repository/document-type-export.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/export/repository/document-type-export.server.data-source.ts index c67e9bf0c58b..abe26a3cc6db 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/export/repository/document-type-export.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/export/repository/document-type-export.server.data-source.ts @@ -1,6 +1,6 @@ import { DocumentTypeService } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; /** * Export Document Server Data Source @@ -27,6 +27,6 @@ export class UmbExportDocumentTypeServerDataSource { async export(unique: string) { if (!unique) throw new Error('Unique is missing'); - return tryExecuteAndNotify(this.#host, DocumentTypeService.getDocumentTypeByIdExport({ id: unique })); + return tryExecute(this.#host, DocumentTypeService.getDocumentTypeByIdExport({ id: unique })); } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/import/repository/document-type-import.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/import/repository/document-type-import.server.data-source.ts index bfba81e1ed39..5a2f602f189a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/import/repository/document-type-import.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/import/repository/document-type-import.server.data-source.ts @@ -1,6 +1,6 @@ import { DocumentTypeService, type PostDocumentTypeImportData } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; /** * Document Type Import Server Data Source @@ -32,6 +32,6 @@ export class UmbDocumentTypeImportServerDataSource { requestBody: { file: { id: temporaryUnique } }, }; - return tryExecuteAndNotify(this.#host, DocumentTypeService.postDocumentTypeImport(requestBody)); + return tryExecute(this.#host, DocumentTypeService.postDocumentTypeImport(requestBody)); } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/move-to/repository/document-type-move.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/move-to/repository/document-type-move.server.data-source.ts index 16200f0089f0..ba8ba22bbbf7 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/move-to/repository/document-type-move.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/move-to/repository/document-type-move.server.data-source.ts @@ -1,6 +1,6 @@ import { DocumentTypeService } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; import type { UmbMoveDataSource, UmbMoveToRequestArgs } from '@umbraco-cms/backoffice/tree'; /** @@ -31,7 +31,7 @@ export class UmbMoveDocumentTypeServerDataSource implements UmbMoveDataSource { if (!args.unique) throw new Error('Unique is missing'); if (args.destination.unique === undefined) throw new Error('Destination unique is missing'); - return tryExecuteAndNotify( + return tryExecute( this.#host, DocumentTypeService.putDocumentTypeByIdMove({ id: args.unique, diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/repository/composition/document-type-composition.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/repository/composition/document-type-composition.server.data-source.ts index 0609f5427f38..6a257dc16b2b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/repository/composition/document-type-composition.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/repository/composition/document-type-composition.server.data-source.ts @@ -8,7 +8,7 @@ import { DocumentTypeService, } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; import type { UmbContentTypeCompositionDataSource } from '@umbraco-cms/backoffice/content-type'; /** @@ -40,7 +40,7 @@ export class UmbDocumentTypeCompositionServerDataSource * @memberof UmbDocumentTypeCompositionServerDataSource */ async getReferences(unique: string) { - const response = await tryExecuteAndNotify( + const response = await tryExecute( this.#host, DocumentTypeService.getDocumentTypeByIdCompositionReferences({ id: unique }), ); @@ -70,7 +70,7 @@ export class UmbDocumentTypeCompositionServerDataSource currentPropertyAliases: args.currentPropertyAliases, }; - const response = await tryExecuteAndNotify( + const response = await tryExecute( this.#host, DocumentTypeService.postDocumentTypeAvailableCompositions({ requestBody }), ); diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/repository/detail/document-type-detail.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/repository/detail/document-type-detail.server.data-source.ts index 77f3ede54807..aaf83ef1770f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/repository/detail/document-type-detail.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/repository/detail/document-type-detail.server.data-source.ts @@ -8,7 +8,7 @@ import type { } from '@umbraco-cms/backoffice/external/backend-api'; import { DocumentTypeService } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; import type { UmbPropertyContainerTypes, UmbPropertyTypeContainerModel } from '@umbraco-cms/backoffice/content-type'; /** @@ -74,10 +74,7 @@ export class UmbDocumentTypeDetailServerDataSource implements UmbDetailDataSourc async read(unique: string) { if (!unique) throw new Error('Unique is missing'); - const { data, error } = await tryExecuteAndNotify( - this.#host, - DocumentTypeService.getDocumentTypeById({ id: unique }), - ); + const { data, error } = await tryExecute(this.#host, DocumentTypeService.getDocumentTypeById({ id: unique })); if (error || !data) { return { error }; @@ -189,7 +186,7 @@ export class UmbDocumentTypeDetailServerDataSource implements UmbDetailDataSourc collection: model.collection?.unique ? { id: model.collection?.unique } : null, }; - const { data, error } = await tryExecuteAndNotify( + const { data, error } = await tryExecute( this.#host, DocumentTypeService.postDocumentType({ requestBody, @@ -265,7 +262,7 @@ export class UmbDocumentTypeDetailServerDataSource implements UmbDetailDataSourc collection: model.collection?.unique ? { id: model.collection?.unique } : null, }; - const { error } = await tryExecuteAndNotify( + const { error } = await tryExecute( this.#host, DocumentTypeService.putDocumentTypeById({ id: model.unique, @@ -289,7 +286,7 @@ export class UmbDocumentTypeDetailServerDataSource implements UmbDetailDataSourc async delete(unique: string) { if (!unique) throw new Error('Unique is missing'); - return tryExecuteAndNotify( + return tryExecute( this.#host, DocumentTypeService.deleteDocumentTypeById({ id: unique, diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/search/document-type-search.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/search/document-type-search.server.data-source.ts index 162b97fd31f1..784945fb454c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/search/document-type-search.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/search/document-type-search.server.data-source.ts @@ -3,7 +3,7 @@ import type { UmbDocumentTypeSearchItemModel } from './document-type.search-prov import type { UmbSearchDataSource, UmbSearchRequestArgs } from '@umbraco-cms/backoffice/search'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { DocumentTypeService } from '@umbraco-cms/backoffice/external/backend-api'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; /** * A data source for the Rollback that fetches data from the server @@ -29,7 +29,7 @@ export class UmbDocumentTypeSearchServerDataSource implements UmbSearchDataSourc * @memberof UmbDocumentTypeSearchServerDataSource */ async search(args: UmbSearchRequestArgs) { - const { data, error } = await tryExecuteAndNotify( + const { data, error } = await tryExecute( this.#host, DocumentTypeService.getItemDocumentTypeSearch({ query: args.query, diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/tree/folder/repository/document-type-folder.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/tree/folder/repository/document-type-folder.server.data-source.ts index 0c30b4d911e5..ae37bfdf95bf 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/tree/folder/repository/document-type-folder.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/tree/folder/repository/document-type-folder.server.data-source.ts @@ -2,7 +2,7 @@ import { UMB_DOCUMENT_TYPE_FOLDER_ENTITY_TYPE } from '../entity.js'; import type { UmbFolderModel } from '@umbraco-cms/backoffice/tree'; import { DocumentTypeService } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; import { UmbId } from '@umbraco-cms/backoffice/id'; import type { UmbDetailDataSource } from '@umbraco-cms/backoffice/repository'; @@ -49,7 +49,7 @@ export class UmbDocumentTypeFolderServerDataSource implements UmbDetailDataSourc async read(unique: string) { if (!unique) throw new Error('Unique is missing'); - const { data, error } = await tryExecuteAndNotify( + const { data, error } = await tryExecute( this.#host, DocumentTypeService.getDocumentTypeFolderById({ id: unique, @@ -86,7 +86,7 @@ export class UmbDocumentTypeFolderServerDataSource implements UmbDetailDataSourc name: model.name, }; - const { error } = await tryExecuteAndNotify( + const { error } = await tryExecute( this.#host, DocumentTypeService.postDocumentTypeFolder({ requestBody, @@ -110,7 +110,7 @@ export class UmbDocumentTypeFolderServerDataSource implements UmbDetailDataSourc if (!model.unique) throw new Error('Unique is missing'); if (!model.name) throw new Error('Folder name is missing'); - const { error } = await tryExecuteAndNotify( + const { error } = await tryExecute( this.#host, DocumentTypeService.putDocumentTypeFolderById({ id: model.unique, @@ -133,7 +133,7 @@ export class UmbDocumentTypeFolderServerDataSource implements UmbDetailDataSourc */ async delete(unique: string) { if (!unique) throw new Error('Unique is missing'); - return tryExecuteAndNotify( + return tryExecute( this.#host, DocumentTypeService.deleteDocumentTypeFolderById({ id: unique, diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/audit-log/repository/document-audit-log.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/audit-log/repository/document-audit-log.server.data-source.ts index 08e2c765cc45..6553e8ebd487 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/audit-log/repository/document-audit-log.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/audit-log/repository/document-audit-log.server.data-source.ts @@ -4,7 +4,7 @@ import type { UmbAuditLogDataSource, UmbAuditLogRequestArgs } from '@umbraco-cms import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import type { DirectionModel } from '@umbraco-cms/backoffice/external/backend-api'; import { DocumentService } from '@umbraco-cms/backoffice/external/backend-api'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; /** * Server data source for the document audit log @@ -29,7 +29,7 @@ export class UmbDocumentAuditLogServerDataSource implements UmbAuditLogDataSourc * @memberof UmbDocumentAuditLogServerDataSource */ async getAuditLog(args: UmbAuditLogRequestArgs) { - const { data, error } = await tryExecuteAndNotify( + const { data, error } = await tryExecute( this.#host, DocumentService.getDocumentByIdAuditLog({ id: args.unique, diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/collection/repository/document-collection.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/collection/repository/document-collection.server.data-source.ts index 04977651d4a7..3085ee363b92 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/collection/repository/document-collection.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/collection/repository/document-collection.server.data-source.ts @@ -1,7 +1,7 @@ import type { UmbDocumentCollectionFilterModel, UmbDocumentCollectionItemModel } from '../types.js'; import { UMB_DOCUMENT_ENTITY_TYPE } from '../../entity.js'; import { DirectionModel, DocumentService } from '@umbraco-cms/backoffice/external/backend-api'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; import type { DocumentCollectionResponseModel } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbCollectionDataSource } from '@umbraco-cms/backoffice/collection'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; @@ -29,7 +29,7 @@ export class UmbDocumentCollectionServerDataSource implements UmbCollectionDataS take: query.take || 100, }; - const { data, error } = await tryExecuteAndNotify(this.#host, DocumentService.getCollectionDocumentById(params)); + const { data, error } = await tryExecute(this.#host, DocumentService.getCollectionDocumentById(params)); if (data) { const items = data.items.map((item: DocumentCollectionResponseModel) => { diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/create-blueprint/repository/document-create-blueprint.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/create-blueprint/repository/document-create-blueprint.server.data-source.ts index e01030eed5fa..3485ccfe05e1 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/create-blueprint/repository/document-create-blueprint.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/create-blueprint/repository/document-create-blueprint.server.data-source.ts @@ -1,4 +1,4 @@ -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; import { DocumentBlueprintService } from '@umbraco-cms/backoffice/external/backend-api'; import type { CreateDocumentBlueprintFromDocumentRequestModel } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; @@ -27,6 +27,6 @@ export class UmbDocumentCreateBlueprintServerDataSource { * @memberof UmbDocumentCreateBlueprintServerDataSource */ async create(requestBody: CreateDocumentBlueprintFromDocumentRequestModel) { - return tryExecuteAndNotify(this.#host, DocumentBlueprintService.postDocumentBlueprintFromDocument({ requestBody })); + return tryExecute(this.#host, DocumentBlueprintService.postDocumentBlueprintFromDocument({ requestBody })); } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/culture-and-hostnames/repository/culture-and-hostnames.server.data.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/culture-and-hostnames/repository/culture-and-hostnames.server.data.ts index 652dcec6a117..bcc8d00871e8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/culture-and-hostnames/repository/culture-and-hostnames.server.data.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/culture-and-hostnames/repository/culture-and-hostnames.server.data.ts @@ -1,7 +1,7 @@ import { DocumentService } from '@umbraco-cms/backoffice/external/backend-api'; import type { UpdateDomainsRequestModel } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; /** * A data source for the Document Culture and Hostnames that fetches data from the server @@ -27,7 +27,7 @@ export class UmbDocumentCultureAndHostnamesServerDataSource { */ async read(unique: string) { if (!unique) throw new Error('Unique is missing'); - return tryExecuteAndNotify(this.#host, DocumentService.getDocumentByIdDomains({ id: unique })); + return tryExecute(this.#host, DocumentService.getDocumentByIdDomains({ id: unique })); } /** @@ -38,6 +38,6 @@ export class UmbDocumentCultureAndHostnamesServerDataSource { */ async update(unique: string, data: UpdateDomainsRequestModel) { if (!unique) throw new Error('Unique is missing'); - return tryExecuteAndNotify(this.#host, DocumentService.putDocumentByIdDomains({ id: unique, requestBody: data })); + return tryExecute(this.#host, DocumentService.putDocumentByIdDomains({ id: unique, requestBody: data })); } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/duplicate/repository/document-duplicate.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/duplicate/repository/document-duplicate.server.data-source.ts index e6c084e1b3f6..68e2002f6ec5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/duplicate/repository/document-duplicate.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/duplicate/repository/document-duplicate.server.data-source.ts @@ -1,7 +1,7 @@ import type { UmbDuplicateDocumentRequestArgs } from './types.js'; import { DocumentService } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; /** * Duplicate Document Server Data Source @@ -29,7 +29,7 @@ export class UmbDuplicateDocumentServerDataSource { if (!args.unique) throw new Error('Unique is missing'); if (args.destination.unique === undefined) throw new Error('Destination unique is missing'); - return tryExecuteAndNotify( + return tryExecute( this.#host, DocumentService.postDocumentByIdCopy({ id: args.unique, diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/move-to/repository/document-move.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/move-to/repository/document-move.server.data-source.ts index 865dcef132a1..eb62d9e7b3d4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/move-to/repository/document-move.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/move-to/repository/document-move.server.data-source.ts @@ -1,6 +1,6 @@ import { DocumentService } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; import type { UmbMoveDataSource, UmbMoveToRequestArgs } from '@umbraco-cms/backoffice/tree'; /** @@ -31,7 +31,7 @@ export class UmbMoveDocumentServerDataSource implements UmbMoveDataSource { if (!args.unique) throw new Error('Unique is missing'); if (args.destination.unique === undefined) throw new Error('Destination unique is missing'); - return tryExecuteAndNotify( + return tryExecute( this.#host, DocumentService.putDocumentByIdMove({ id: args.unique, diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/notifications/repository/document-notifications.server.data.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/notifications/repository/document-notifications.server.data.ts index be7cd0511c2d..21e76efb3e24 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/notifications/repository/document-notifications.server.data.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/notifications/repository/document-notifications.server.data.ts @@ -1,7 +1,7 @@ import { DocumentService } from '@umbraco-cms/backoffice/external/backend-api'; import type { UpdateDocumentNotificationsRequestModel } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; /** * A data source for the Document Culture and Hostnames that fetches data from the server @@ -26,7 +26,7 @@ export class UmbDocumentNotificationsServerDataSource { */ async read(unique: string) { if (!unique) throw new Error('Unique is missing'); - return tryExecuteAndNotify(this.#host, DocumentService.getDocumentByIdNotifications({ id: unique })); + return tryExecute(this.#host, DocumentService.getDocumentByIdNotifications({ id: unique })); } /** @@ -37,9 +37,6 @@ export class UmbDocumentNotificationsServerDataSource { */ async update(unique: string, data: UpdateDocumentNotificationsRequestModel) { if (!unique) throw new Error('Unique is missing'); - return tryExecuteAndNotify( - this.#host, - DocumentService.putDocumentByIdNotifications({ id: unique, requestBody: data }), - ); + return tryExecute(this.#host, DocumentService.putDocumentByIdNotifications({ id: unique, requestBody: data })); } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/sort-children-of/repository/sort-children-of.server.data.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/sort-children-of/repository/sort-children-of.server.data.ts index 6b51854ac408..2d93f989829d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/sort-children-of/repository/sort-children-of.server.data.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/sort-children-of/repository/sort-children-of.server.data.ts @@ -1,6 +1,6 @@ import { DocumentService } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; import type { UmbSortChildrenOfArgs, UmbSortChildrenOfDataSource } from '@umbraco-cms/backoffice/tree'; /** @@ -30,7 +30,7 @@ export class UmbSortChildrenOfDocumentServerDataSource implements UmbSortChildre const sortingMapping = args.sorting.map((item) => ({ id: item.unique, sortOrder: item.sortOrder })); - return tryExecuteAndNotify( + return tryExecute( this.#host, DocumentService.putDocumentSort({ requestBody: { diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/global-contexts/document-configuration.context.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/global-contexts/document-configuration.context.ts index 8829599ee039..60fece6d0d73 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/global-contexts/document-configuration.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/global-contexts/document-configuration.context.ts @@ -3,7 +3,7 @@ import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import type { UmbApi } from '@umbraco-cms/backoffice/extension-api'; import { DocumentService, type DocumentConfigurationResponseModel } from '@umbraco-cms/backoffice/external/backend-api'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; // TODO: Turn this into a Repository with a Store that holds the cache [NL] /** @@ -36,7 +36,7 @@ export class UmbDocumentConfigurationContext * @returns A promise that resolves to the document configuration, or null if the configuration could not be fetched. */ async fetchDocumentConfiguration() { - const { data } = await tryExecuteAndNotify(this, DocumentService.getDocumentConfiguration()); + const { data } = await tryExecute(this, DocumentService.getDocumentConfiguration()); return data ?? null; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/publishing/repository/document-publishing.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/publishing/repository/document-publishing.server.data-source.ts index add5a266fa1a..95d5d25204f8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/publishing/repository/document-publishing.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/publishing/repository/document-publishing.server.data-source.ts @@ -8,7 +8,7 @@ import type { } from '@umbraco-cms/backoffice/external/backend-api'; import { DocumentService } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; import type { UmbVariantId } from '@umbraco-cms/backoffice/variant'; import type { UmbDataSourceResponse } from '@umbraco-cms/backoffice/repository'; @@ -54,7 +54,7 @@ export class UmbDocumentPublishingServerDataSource { publishSchedules, }; - return tryExecuteAndNotify(this.#host, DocumentService.putDocumentByIdPublish({ id: unique, requestBody })); + return tryExecute(this.#host, DocumentService.putDocumentByIdPublish({ id: unique, requestBody })); } /** @@ -77,14 +77,14 @@ export class UmbDocumentPublishingServerDataSource { cultures: null, }; - return tryExecuteAndNotify(this.#host, DocumentService.putDocumentByIdUnpublish({ id: unique, requestBody })); + return tryExecute(this.#host, DocumentService.putDocumentByIdUnpublish({ id: unique, requestBody })); } const requestBody: UnpublishDocumentRequestModel = { cultures: variantIds.map((variant) => variant.toCultureString()), }; - return tryExecuteAndNotify(this.#host, DocumentService.putDocumentByIdUnpublish({ id: unique, requestBody })); + return tryExecute(this.#host, DocumentService.putDocumentByIdUnpublish({ id: unique, requestBody })); } /** @@ -106,10 +106,7 @@ export class UmbDocumentPublishingServerDataSource { includeUnpublishedDescendants, }; - return tryExecuteAndNotify( - this.#host, - DocumentService.putDocumentByIdPublishWithDescendants({ id: unique, requestBody }), - ); + return tryExecute(this.#host, DocumentService.putDocumentByIdPublishWithDescendants({ id: unique, requestBody })); } /** @@ -121,10 +118,7 @@ export class UmbDocumentPublishingServerDataSource { async published(unique: string): Promise> { if (!unique) throw new Error('Unique is missing'); - const { data, error } = await tryExecuteAndNotify( - this.#host, - DocumentService.getDocumentByIdPublished({ id: unique }), - ); + const { data, error } = await tryExecute(this.#host, DocumentService.getDocumentByIdPublished({ id: unique })); if (error || !data) { return { error }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/recycle-bin/repository/document-recycle-bin.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/recycle-bin/repository/document-recycle-bin.server.data-source.ts index 4db3dbe660d8..c4518908054a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/recycle-bin/repository/document-recycle-bin.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/recycle-bin/repository/document-recycle-bin.server.data-source.ts @@ -6,7 +6,7 @@ import type { } from '@umbraco-cms/backoffice/recycle-bin'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { DocumentService } from '@umbraco-cms/backoffice/external/backend-api'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; export class UmbDocumentRecycleBinServerDataSource implements UmbRecycleBinDataSource { #host: UmbControllerHost; @@ -16,11 +16,11 @@ export class UmbDocumentRecycleBinServerDataSource implements UmbRecycleBinDataS } trash(args: UmbRecycleBinTrashRequestArgs) { - return tryExecuteAndNotify(this.#host, DocumentService.putDocumentByIdMoveToRecycleBin({ id: args.unique })); + return tryExecute(this.#host, DocumentService.putDocumentByIdMoveToRecycleBin({ id: args.unique })); } restore(args: UmbRecycleBinRestoreRequestArgs) { - return tryExecuteAndNotify( + return tryExecute( this.#host, DocumentService.putRecycleBinDocumentByIdRestore({ id: args.unique, @@ -32,11 +32,11 @@ export class UmbDocumentRecycleBinServerDataSource implements UmbRecycleBinDataS } empty() { - return tryExecuteAndNotify(this.#host, DocumentService.deleteRecycleBinDocument()); + return tryExecute(this.#host, DocumentService.deleteRecycleBinDocument()); } async getOriginalParent(args: UmbRecycleBinOriginalParentRequestArgs) { - const { data, error } = await tryExecuteAndNotify( + const { data, error } = await tryExecute( this.#host, DocumentService.getRecycleBinDocumentByIdOriginalParent({ id: args.unique }), ); diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/reference/repository/document-reference.server.data.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/reference/repository/document-reference.server.data.ts index 3b2f7d3de705..94b37df7c637 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/reference/repository/document-reference.server.data.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/reference/repository/document-reference.server.data.ts @@ -1,6 +1,6 @@ import { UMB_DOCUMENT_ENTITY_TYPE } from '../../entity.js'; import { DocumentService } from '@umbraco-cms/backoffice/external/backend-api'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; import type { UmbEntityModel } from '@umbraco-cms/backoffice/entity'; import type { UmbEntityReferenceDataSource, UmbReferenceItemModel } from '@umbraco-cms/backoffice/relations'; @@ -27,7 +27,7 @@ export class UmbDocumentReferenceServerDataSource extends UmbControllerBase impl skip = 0, take = 20, ): Promise>> { - const { data, error } = await tryExecuteAndNotify( + const { data, error } = await tryExecute( this, DocumentService.getDocumentByIdReferencedBy({ id: unique, skip, take }), ); @@ -68,7 +68,7 @@ export class UmbDocumentReferenceServerDataSource extends UmbControllerBase impl skip: number = 0, take: number = 20, ): Promise>> { - const { data, error } = await tryExecuteAndNotify( + const { data, error } = await tryExecute( this, DocumentService.getDocumentAreReferenced({ id: uniques, skip, take }), ); @@ -100,7 +100,7 @@ export class UmbDocumentReferenceServerDataSource extends UmbControllerBase impl skip: number = 0, take: number = 20, ): Promise>> { - const { data, error } = await tryExecuteAndNotify( + const { data, error } = await tryExecute( this, DocumentService.getDocumentByIdReferencedDescendants({ id: unique, skip, take }), ); diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/repository/detail/document-detail.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/repository/detail/document-detail.server.data-source.ts index 1b96b586c547..c6c3162b0341 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/repository/detail/document-detail.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/repository/detail/document-detail.server.data-source.ts @@ -8,7 +8,7 @@ import type { } from '@umbraco-cms/backoffice/external/backend-api'; import { DocumentService } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; /** * A data source for the Document that fetches data from the server @@ -81,7 +81,7 @@ export class UmbDocumentServerDataSource implements UmbDetailDataSource { - const { data } = await tryExecuteAndNotify(this, HealthCheckService.getHealthCheckGroup({ skip: 0, take: 9999 })); + const { data } = await tryExecute(this, HealthCheckService.getHealthCheckGroup({ skip: 0, take: 9999 })); if (!data) return; const manifests = this.#createManifests(data.items); this.#register(manifests); diff --git a/src/Umbraco.Web.UI.Client/src/packages/health-check/health-check.context.ts b/src/Umbraco.Web.UI.Client/src/packages/health-check/health-check.context.ts index 44316bc0053b..94efcc5863de 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/health-check/health-check.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/health-check/health-check.context.ts @@ -3,7 +3,7 @@ import type { HealthCheckGroupWithResultResponseModel, } from '@umbraco-cms/backoffice/external/backend-api'; import { HealthCheckService } from '@umbraco-cms/backoffice/external/backend-api'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; import type { UmbApi } from '@umbraco-cms/backoffice/extension-api'; import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; import { UmbBasicState } from '@umbraco-cms/backoffice/observable-api'; @@ -17,7 +17,7 @@ export class UmbHealthCheckContext extends UmbControllerBase implements UmbApi { public readonly results = this.#results.asObservable(); async getGroupChecks(name: string) { - const { data } = await tryExecuteAndNotify(this, HealthCheckService.getHealthCheckGroupByName({ name })); + const { data } = await tryExecute(this, HealthCheckService.getHealthCheckGroupByName({ name })); if (data) { this.#checks.setValue(data); @@ -27,7 +27,7 @@ export class UmbHealthCheckContext extends UmbControllerBase implements UmbApi { } async checkGroup(name: string) { - const { data } = await tryExecuteAndNotify(this, HealthCheckService.postHealthCheckGroupByNameCheck({ name })); + const { data } = await tryExecute(this, HealthCheckService.postHealthCheckGroupByNameCheck({ name })); if (data) { this.#results.setValue(data); diff --git a/src/Umbraco.Web.UI.Client/src/packages/health-check/views/health-check-action.element.ts b/src/Umbraco.Web.UI.Client/src/packages/health-check/views/health-check-action.element.ts index d5efa4c102c3..8a4e753c2bdc 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/health-check/views/health-check-action.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/health-check/views/health-check-action.element.ts @@ -4,7 +4,7 @@ import { css, html, nothing, customElement, property, state, ifDefined } from '@ import type { HealthCheckActionRequestModel } from '@umbraco-cms/backoffice/external/backend-api'; import { HealthCheckService } from '@umbraco-cms/backoffice/external/backend-api'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; @customElement('umb-dashboard-health-check-action') export class UmbDashboardHealthCheckActionElement extends UmbLitElement { @@ -17,7 +17,7 @@ export class UmbDashboardHealthCheckActionElement extends UmbLitElement { private async _onActionClick(e: SubmitEvent) { e.preventDefault(); this._buttonState = 'waiting'; - const { error } = await tryExecuteAndNotify( + const { error } = await tryExecute( this, HealthCheckService.postHealthCheckExecuteAction({ requestBody: this.action }), ); diff --git a/src/Umbraco.Web.UI.Client/src/packages/language/collection/repository/language-collection.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/language/collection/repository/language-collection.server.data-source.ts index 1ed9cc3e9ab0..e6be8d18f0b1 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/language/collection/repository/language-collection.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/language/collection/repository/language-collection.server.data-source.ts @@ -4,7 +4,7 @@ import { UMB_LANGUAGE_ENTITY_TYPE } from '../../entity.js'; import type { UmbCollectionDataSource } from '@umbraco-cms/backoffice/collection'; import { LanguageService } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; /** * A data source that fetches the language collection data from the server. @@ -30,7 +30,7 @@ export class UmbLanguageCollectionServerDataSource implements UmbCollectionDataS * @memberof UmbLanguageCollectionServerDataSource */ async getCollection(filter: UmbLanguageCollectionFilterModel) { - const { data, error } = await tryExecuteAndNotify(this.#host, LanguageService.getLanguage(filter)); + const { data, error } = await tryExecute(this.#host, LanguageService.getLanguage(filter)); if (data) { const items = data.items.map((item) => { diff --git a/src/Umbraco.Web.UI.Client/src/packages/language/repository/detail/language-detail.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/language/repository/detail/language-detail.server.data-source.ts index 975323b62bd3..e354bd8e551c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/language/repository/detail/language-detail.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/language/repository/detail/language-detail.server.data-source.ts @@ -7,7 +7,7 @@ import type { } from '@umbraco-cms/backoffice/external/backend-api'; import { LanguageService } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; /** * A data source for the Language that fetches data from the server @@ -55,10 +55,7 @@ export class UmbLanguageServerDataSource implements UmbDetailDataSource, imagingModel?: UmbImagingResizeModel) { if (!uniques.length) throw new Error('Uniques are missing'); - const { data, error } = await tryExecuteAndNotify( + const { data, error } = await tryExecute( this.#host, ImagingService.getImagingResizeUrls({ id: uniques, ...imagingModel }), ); diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/entity-actions/duplicate/repository/media-type-duplicate.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/entity-actions/duplicate/repository/media-type-duplicate.server.data-source.ts index 08282ec7579b..c9113e05454a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/entity-actions/duplicate/repository/media-type-duplicate.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/entity-actions/duplicate/repository/media-type-duplicate.server.data-source.ts @@ -1,6 +1,6 @@ import { MediaTypeService } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; import type { UmbDuplicateToDataSource, UmbDuplicateToRequestArgs } from '@umbraco-cms/backoffice/tree'; /** @@ -29,7 +29,7 @@ export class UmbDuplicateMediaTypeServerDataSource implements UmbDuplicateToData if (!args.unique) throw new Error('Unique is missing'); if (args.destination.unique === undefined) throw new Error('Destination unique is missing'); - return tryExecuteAndNotify( + return tryExecute( this.#host, MediaTypeService.postMediaTypeByIdCopy({ id: args.unique, diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/entity-actions/export/repository/media-type-export.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/entity-actions/export/repository/media-type-export.server.data-source.ts index 7a23bc2fe494..fd111609f8ab 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/entity-actions/export/repository/media-type-export.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/entity-actions/export/repository/media-type-export.server.data-source.ts @@ -1,6 +1,6 @@ import { MediaTypeService } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; /** * Export Media Server Data Source @@ -28,6 +28,6 @@ export class UmbExportMediaTypeServerDataSource { async export(unique: string) { if (!unique) throw new Error('Unique is missing'); - return tryExecuteAndNotify(this.#host, MediaTypeService.getMediaTypeByIdExport({ id: unique })); + return tryExecute(this.#host, MediaTypeService.getMediaTypeByIdExport({ id: unique })); } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/entity-actions/import/repository/media-type-import.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/entity-actions/import/repository/media-type-import.server.data-source.ts index c71e44051d12..716dd3ae1f33 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/entity-actions/import/repository/media-type-import.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/entity-actions/import/repository/media-type-import.server.data-source.ts @@ -1,6 +1,6 @@ import { MediaTypeService, type PostMediaTypeImportData } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; /** * Media Type Import Server Data Source @@ -32,6 +32,6 @@ export class UmbMediaTypeImportServerDataSource { requestBody: { file: { id: temporaryUnique } }, }; - return tryExecuteAndNotify(this.#host, MediaTypeService.postMediaTypeImport(requestBody)); + return tryExecute(this.#host, MediaTypeService.postMediaTypeImport(requestBody)); } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/entity-actions/move-to/repository/media-type-move.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/entity-actions/move-to/repository/media-type-move.server.data-source.ts index a48ce5662c3b..104b6f64b5d0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/entity-actions/move-to/repository/media-type-move.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/entity-actions/move-to/repository/media-type-move.server.data-source.ts @@ -1,6 +1,6 @@ import { MediaTypeService } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; import type { UmbMoveDataSource, UmbMoveToRequestArgs } from '@umbraco-cms/backoffice/tree'; /** @@ -31,7 +31,7 @@ export class UmbMoveMediaTypeServerDataSource implements UmbMoveDataSource { if (!args.unique) throw new Error('Unique is missing'); if (args.destination.unique === undefined) throw new Error('Destination unique is missing'); - return tryExecuteAndNotify( + return tryExecute( this.#host, MediaTypeService.putMediaTypeByIdMove({ id: args.unique, diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/repository/composition/media-type-composition.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/repository/composition/media-type-composition.server.data-source.ts index 81f353a70bd8..858a12680478 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/repository/composition/media-type-composition.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/repository/composition/media-type-composition.server.data-source.ts @@ -5,7 +5,7 @@ import type { } from '../../types.js'; import { type MediaTypeCompositionRequestModel, MediaTypeService } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; import type { UmbContentTypeCompositionDataSource } from '@umbraco-cms/backoffice/content-type'; /** @@ -37,7 +37,7 @@ export class UmbMediaTypeCompositionServerDataSource * @memberof UmbMediaTypeCompositionServerDataSource */ async getReferences(unique: string) { - const response = await tryExecuteAndNotify( + const response = await tryExecute( this.#host, MediaTypeService.getMediaTypeByIdCompositionReferences({ id: unique }), ); @@ -66,10 +66,7 @@ export class UmbMediaTypeCompositionServerDataSource currentPropertyAliases: args.currentPropertyAliases, }; - const response = await tryExecuteAndNotify( - this.#host, - MediaTypeService.postMediaTypeAvailableCompositions({ requestBody }), - ); + const response = await tryExecute(this.#host, MediaTypeService.postMediaTypeAvailableCompositions({ requestBody })); const error = response.error; const data: Array | undefined = response.data?.map((composition) => { return { diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/repository/detail/media-type-detail.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/repository/detail/media-type-detail.server.data-source.ts index 7fc5078ce846..19b01c623dd1 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/repository/detail/media-type-detail.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/repository/detail/media-type-detail.server.data-source.ts @@ -8,7 +8,7 @@ import type { } from '@umbraco-cms/backoffice/external/backend-api'; import { MediaTypeService } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; import type { UmbPropertyContainerTypes } from '@umbraco-cms/backoffice/content-type'; /** @@ -66,7 +66,7 @@ export class UmbMediaTypeServerDataSource implements UmbDetailDataSource { diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/entity-actions/move-to/repository/media-move.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/entity-actions/move-to/repository/media-move.server.data-source.ts index 8108f4e73c21..8d24101fca08 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/entity-actions/move-to/repository/media-move.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/entity-actions/move-to/repository/media-move.server.data-source.ts @@ -1,6 +1,6 @@ import { MediaService } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; import type { UmbMoveDataSource, UmbMoveToRequestArgs } from '@umbraco-cms/backoffice/tree'; /** @@ -31,7 +31,7 @@ export class UmbMoveMediaServerDataSource implements UmbMoveDataSource { if (!args.unique) throw new Error('Unique is missing'); if (args.destination.unique === undefined) throw new Error('Destination unique is missing'); - return tryExecuteAndNotify( + return tryExecute( this.#host, MediaService.putMediaByIdMove({ id: args.unique, diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/entity-actions/sort-children-of/repository/sort-children-of.server.data.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/entity-actions/sort-children-of/repository/sort-children-of.server.data.ts index 0dbf057bae66..6b3f8acb9bcd 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/entity-actions/sort-children-of/repository/sort-children-of.server.data.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/entity-actions/sort-children-of/repository/sort-children-of.server.data.ts @@ -1,6 +1,6 @@ import { MediaService } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; import type { UmbSortChildrenOfArgs, UmbSortChildrenOfDataSource } from '@umbraco-cms/backoffice/tree'; /** @@ -30,7 +30,7 @@ export class UmbSortChildrenOfMediaServerDataSource implements UmbSortChildrenOf const sortingMapping = args.sorting.map((item) => ({ id: item.unique, sortOrder: item.sortOrder })); - return tryExecuteAndNotify( + return tryExecute( this.#host, MediaService.putMediaSort({ requestBody: { diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/recycle-bin/repository/media-recycle-bin.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/recycle-bin/repository/media-recycle-bin.server.data-source.ts index 6348f879fa2e..058d73469e46 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/recycle-bin/repository/media-recycle-bin.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/recycle-bin/repository/media-recycle-bin.server.data-source.ts @@ -6,7 +6,7 @@ import type { } from '@umbraco-cms/backoffice/recycle-bin'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { MediaService } from '@umbraco-cms/backoffice/external/backend-api'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; export class UmbMediaRecycleBinServerDataSource implements UmbRecycleBinDataSource { #host: UmbControllerHost; @@ -16,11 +16,11 @@ export class UmbMediaRecycleBinServerDataSource implements UmbRecycleBinDataSour } trash(args: UmbRecycleBinTrashRequestArgs) { - return tryExecuteAndNotify(this.#host, MediaService.putMediaByIdMoveToRecycleBin({ id: args.unique })); + return tryExecute(this.#host, MediaService.putMediaByIdMoveToRecycleBin({ id: args.unique })); } restore(args: UmbRecycleBinRestoreRequestArgs) { - return tryExecuteAndNotify( + return tryExecute( this.#host, MediaService.putRecycleBinMediaByIdRestore({ id: args.unique, @@ -32,11 +32,11 @@ export class UmbMediaRecycleBinServerDataSource implements UmbRecycleBinDataSour } empty() { - return tryExecuteAndNotify(this.#host, MediaService.deleteRecycleBinMedia()); + return tryExecute(this.#host, MediaService.deleteRecycleBinMedia()); } async getOriginalParent(args: UmbRecycleBinOriginalParentRequestArgs) { - const { data, error } = await tryExecuteAndNotify( + const { data, error } = await tryExecute( this.#host, MediaService.getRecycleBinMediaByIdOriginalParent({ id: args.unique }), ); diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/reference/repository/media-reference.server.data.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/reference/repository/media-reference.server.data.ts index 6f169f00c41a..d7b8418cb2d8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/reference/repository/media-reference.server.data.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/reference/repository/media-reference.server.data.ts @@ -1,7 +1,7 @@ import { UMB_MEDIA_ENTITY_TYPE } from '../../entity.js'; import { MediaService } from '@umbraco-cms/backoffice/external/backend-api'; import { UmbManagementApiDataMapper } from '@umbraco-cms/backoffice/repository'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; import type { UmbEntityModel } from '@umbraco-cms/backoffice/entity'; import type { UmbEntityReferenceDataSource, UmbReferenceItemModel } from '@umbraco-cms/backoffice/relations'; @@ -27,10 +27,7 @@ export class UmbMediaReferenceServerDataSource extends UmbControllerBase impleme skip: number = 0, take: number = 20, ): Promise>> { - const { data, error } = await tryExecuteAndNotify( - this, - MediaService.getMediaByIdReferencedBy({ id: unique, skip, take }), - ); + const { data, error } = await tryExecute(this, MediaService.getMediaByIdReferencedBy({ id: unique, skip, take })); if (data) { const promises = data.items.map(async (item) => { @@ -68,10 +65,7 @@ export class UmbMediaReferenceServerDataSource extends UmbControllerBase impleme skip: number = 0, take: number = 20, ): Promise>> { - const { data, error } = await tryExecuteAndNotify( - this, - MediaService.getMediaAreReferenced({ id: uniques, skip, take }), - ); + const { data, error } = await tryExecute(this, MediaService.getMediaAreReferenced({ id: uniques, skip, take })); if (data) { const items: Array = data.items.map((item) => { @@ -100,7 +94,7 @@ export class UmbMediaReferenceServerDataSource extends UmbControllerBase impleme skip: number = 0, take: number = 20, ): Promise>> { - const { data, error } = await tryExecuteAndNotify( + const { data, error } = await tryExecute( this, MediaService.getMediaByIdReferencedDescendants({ id: unique, skip, take }), ); diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/repository/detail/media-detail.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/repository/detail/media-detail.server.data-source.ts index 34cdee7872bb..8dc85a8b0c6d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/repository/detail/media-detail.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/repository/detail/media-detail.server.data-source.ts @@ -5,7 +5,7 @@ import type { UmbDetailDataSource } from '@umbraco-cms/backoffice/repository'; import type { CreateMediaRequestModel, UpdateMediaRequestModel } from '@umbraco-cms/backoffice/external/backend-api'; import { MediaService } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; /** * A data source for the Media that fetches data from the server @@ -66,7 +66,7 @@ export class UmbMediaServerDataSource implements UmbDetailDataSource mapper(item)); return { data: mapped, error }; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/search/media-search.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/search/media-search.server.data-source.ts index 207406449d22..fd782a1856aa 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/search/media-search.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/search/media-search.server.data-source.ts @@ -3,7 +3,7 @@ import type { UmbMediaSearchItemModel, UmbMediaSearchRequestArgs } from './types import type { UmbSearchDataSource } from '@umbraco-cms/backoffice/search'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { MediaService } from '@umbraco-cms/backoffice/external/backend-api'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; /** * A data source for the Rollback that fetches data from the server @@ -31,7 +31,7 @@ export class UmbMediaSearchServerDataSource * @memberof UmbMediaSearchServerDataSource */ async search(args: UmbMediaSearchRequestArgs) { - const { data, error } = await tryExecuteAndNotify( + const { data, error } = await tryExecute( this.#host, MediaService.getItemMediaSearch({ query: args.query, diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member-group/collection/repository/member-group-collection.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member-group/collection/repository/member-group-collection.server.data-source.ts index 726bac656cc5..0433001568b1 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member-group/collection/repository/member-group-collection.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member-group/collection/repository/member-group-collection.server.data-source.ts @@ -3,7 +3,7 @@ import type { UmbMemberGroupDetailModel } from '../../types.js'; import { UMB_MEMBER_GROUP_ENTITY_TYPE } from '../../entity.js'; import type { UmbCollectionDataSource } from '@umbraco-cms/backoffice/collection'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; import type { MemberGroupResponseModel } from '@umbraco-cms/backoffice/external/backend-api'; import { MemberGroupService } from '@umbraco-cms/backoffice/external/backend-api'; @@ -31,7 +31,7 @@ export class UmbMemberGroupCollectionServerDataSource implements UmbCollectionDa * @memberof UmbMemberGroupCollectionServerDataSource */ async getCollection(filter: UmbMemberGroupCollectionFilterModel) { - const { data, error } = await tryExecuteAndNotify(this.#host, MemberGroupService.getMemberGroup(filter)); + const { data, error } = await tryExecute(this.#host, MemberGroupService.getMemberGroup(filter)); if (error) { return { error }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member-group/repository/detail/member-group-detail.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member-group/repository/detail/member-group-detail.server.data-source.ts index c5336bbab0ec..4e14ee3624a5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member-group/repository/detail/member-group-detail.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member-group/repository/detail/member-group-detail.server.data-source.ts @@ -3,7 +3,7 @@ import { UMB_MEMBER_GROUP_ENTITY_TYPE } from '../../entity.js'; import { UmbId } from '@umbraco-cms/backoffice/id'; import type { UmbDetailDataSource } from '@umbraco-cms/backoffice/repository'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; import type { CreateMemberGroupRequestModel } from '@umbraco-cms/backoffice/external/backend-api'; import { MemberGroupService } from '@umbraco-cms/backoffice/external/backend-api'; @@ -49,10 +49,7 @@ export class UmbMemberGroupServerDataSource implements UmbDetailDataSource>> { - const { data, error } = await tryExecuteAndNotify( - this, - MemberService.getMemberByIdReferencedBy({ id: unique, skip, take }), - ); + const { data, error } = await tryExecute(this, MemberService.getMemberByIdReferencedBy({ id: unique, skip, take })); if (data) { const promises = data.items.map(async (item) => { @@ -68,10 +65,7 @@ export class UmbMemberReferenceServerDataSource extends UmbControllerBase implem skip: number = 0, take: number = 20, ): Promise>> { - const { data, error } = await tryExecuteAndNotify( - this, - MemberService.getMemberAreReferenced({ id: uniques, skip, take }), - ); + const { data, error } = await tryExecute(this, MemberService.getMemberAreReferenced({ id: uniques, skip, take })); if (data) { const items: Array = data.items.map((item) => { @@ -100,7 +94,7 @@ export class UmbMemberReferenceServerDataSource extends UmbControllerBase implem skip: number = 0, take: number = 20, ): Promise>> { - const { data, error } = await tryExecuteAndNotify( + const { data, error } = await tryExecute( this, MemberService.getMemberByIdReferencedDescendants({ id: unique, skip, take }), ); diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member/repository/detail/member-detail.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member/repository/detail/member-detail.server.data-source.ts index 30d4bddac99c..1a2296ed581b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member/repository/detail/member-detail.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member/repository/detail/member-detail.server.data-source.ts @@ -6,7 +6,7 @@ import type { UmbDetailDataSource } from '@umbraco-cms/backoffice/repository'; import type { CreateMemberRequestModel, UpdateMemberRequestModel } from '@umbraco-cms/backoffice/external/backend-api'; import { MemberService } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; /** * A data source for the Member that fetches data from the server @@ -75,7 +75,7 @@ export class UmbMemberServerDataSource implements UmbDetailDataSource setTimeout(resolve, this.#isFirstRebuildStatusPoll ? 1000 : 5000)); this.#isFirstRebuildStatusPoll = false; - const { data, error } = await tryExecuteAndNotify(this, PublishedCacheService.getPublishedCacheRebuildStatus()); + const { data, error } = await tryExecute(this, PublishedCacheService.getPublishedCacheRebuildStatus()); if (error || !data) { this._buttonStateRebuild = 'failed'; return; diff --git a/src/Umbraco.Web.UI.Client/src/packages/relations/relation-types/collection/repository/relation-type-collection.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/relations/relation-types/collection/repository/relation-type-collection.server.data-source.ts index d35c1861efa7..5a9e7d88453b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/relations/relation-types/collection/repository/relation-type-collection.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/relations/relation-types/collection/repository/relation-type-collection.server.data-source.ts @@ -4,7 +4,7 @@ import { UMB_RELATION_TYPE_ENTITY_TYPE } from '../../entity.js'; import type { UmbCollectionDataSource } from '@umbraco-cms/backoffice/collection'; import { RelationTypeService } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; /** * A data source that fetches the relation type collection data from the server. @@ -30,7 +30,7 @@ export class UmbRelationTypeCollectionServerDataSource implements UmbCollectionD * @memberof UmbRelationTypeCollectionServerDataSource */ async getCollection(filter: UmbRelationTypeCollectionFilterModel) { - const { data, error } = await tryExecuteAndNotify(this.#host, RelationTypeService.getRelationType(filter)); + const { data, error } = await tryExecute(this.#host, RelationTypeService.getRelationType(filter)); if (data) { const items = data.items.map((item) => { diff --git a/src/Umbraco.Web.UI.Client/src/packages/relations/relation-types/repository/detail/relation-type-detail.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/relations/relation-types/repository/detail/relation-type-detail.server.data-source.ts index 86715b1f78bb..180dd186f6b2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/relations/relation-types/repository/detail/relation-type-detail.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/relations/relation-types/repository/detail/relation-type-detail.server.data-source.ts @@ -2,7 +2,7 @@ import type { UmbRelationTypeDetailModel } from '../../types.js'; import { UMB_RELATION_TYPE_ENTITY_TYPE } from '../../entity.js'; import { RelationTypeService } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; import type { UmbReadDetailDataSource } from '@umbraco-cms/backoffice/repository'; /** @@ -31,10 +31,7 @@ export class UmbRelationTypeDetailServerDataSource implements UmbReadDetailDataS async read(unique: string) { if (!unique) throw new Error('Unique is missing'); - const { data, error } = await tryExecuteAndNotify( - this.#host, - RelationTypeService.getRelationTypeById({ id: unique }), - ); + const { data, error } = await tryExecute(this.#host, RelationTypeService.getRelationTypeById({ id: unique })); if (error || !data) { return { error }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/relations/relations/collection/repository/relation-collection.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/relations/relations/collection/repository/relation-collection.server.data-source.ts index dc1506c73867..f88f87ce9f98 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/relations/relations/collection/repository/relation-collection.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/relations/relations/collection/repository/relation-collection.server.data-source.ts @@ -4,7 +4,7 @@ import { UMB_RELATION_ENTITY_TYPE } from '../../entity.js'; import type { UmbCollectionDataSource } from '@umbraco-cms/backoffice/collection'; import { RelationService } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; /** * A data source that fetches the relation collection data from the server. @@ -36,10 +36,7 @@ export class UmbRelationCollectionServerDataSource implements UmbCollectionDataS id: filter.relationType.unique, }; - const { data, error } = await tryExecuteAndNotify( - this.#host, - RelationService.getRelationByRelationTypeId(requestBody), - ); + const { data, error } = await tryExecute(this.#host, RelationService.getRelationByRelationTypeId(requestBody)); if (data) { const items = data.items.map((item) => { diff --git a/src/Umbraco.Web.UI.Client/src/packages/search/examine-management-dashboard/views/section-view-examine-indexers.ts b/src/Umbraco.Web.UI.Client/src/packages/search/examine-management-dashboard/views/section-view-examine-indexers.ts index 0c16cd657de8..16085ebee4e0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/search/examine-management-dashboard/views/section-view-examine-indexers.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/search/examine-management-dashboard/views/section-view-examine-indexers.ts @@ -4,7 +4,7 @@ import { umbConfirmModal } from '@umbraco-cms/backoffice/modal'; import type { HealthStatusResponseModel, IndexResponseModel } from '@umbraco-cms/backoffice/external/backend-api'; import { HealthStatusModel, IndexerService } from '@umbraco-cms/backoffice/external/backend-api'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; import './section-view-examine-searchers.js'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; @@ -40,10 +40,7 @@ export class UmbDashboardExamineIndexElement extends UmbLitElement { } async #getIndexData() { - const { data } = await tryExecuteAndNotify( - this, - IndexerService.getIndexerByIndexName({ indexName: this.indexName }), - ); + const { data } = await tryExecute(this, IndexerService.getIndexerByIndexName({ indexName: this.indexName })); return data; } @@ -76,7 +73,7 @@ export class UmbDashboardExamineIndexElement extends UmbLitElement { } private async _rebuild() { this._buttonState = 'waiting'; - const { error } = await tryExecuteAndNotify( + const { error } = await tryExecute( this, IndexerService.postIndexerByIndexNameRebuild({ indexName: this.indexName }), ); diff --git a/src/Umbraco.Web.UI.Client/src/packages/search/examine-management-dashboard/views/section-view-examine-overview.ts b/src/Umbraco.Web.UI.Client/src/packages/search/examine-management-dashboard/views/section-view-examine-overview.ts index a375374906c4..34af67599ac0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/search/examine-management-dashboard/views/section-view-examine-overview.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/search/examine-management-dashboard/views/section-view-examine-overview.ts @@ -4,7 +4,7 @@ import { css, html, nothing, customElement, state } from '@umbraco-cms/backoffic import type { IndexResponseModel, SearcherResponseModel } from '@umbraco-cms/backoffice/external/backend-api'; import { HealthStatusModel, IndexerService, SearcherService } from '@umbraco-cms/backoffice/external/backend-api'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; @customElement('umb-dashboard-examine-overview') export class UmbDashboardExamineOverviewElement extends UmbLitElement { @@ -28,14 +28,14 @@ export class UmbDashboardExamineOverviewElement extends UmbLitElement { private async _getIndexers() { this._loadingIndexers = true; - const { data } = await tryExecuteAndNotify(this, IndexerService.getIndexer({ take: 9999, skip: 0 })); + const { data } = await tryExecute(this, IndexerService.getIndexer({ take: 9999, skip: 0 })); this._indexers = data?.items ?? []; this._loadingIndexers = false; } private async _getSearchers() { this._loadingSearchers = true; - const { data } = await tryExecuteAndNotify(this, SearcherService.getSearcher({ take: 9999, skip: 0 })); + const { data } = await tryExecute(this, SearcherService.getSearcher({ take: 9999, skip: 0 })); this._searchers = data?.items ?? []; this._loadingSearchers = false; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/search/examine-management-dashboard/views/section-view-examine-searchers.ts b/src/Umbraco.Web.UI.Client/src/packages/search/examine-management-dashboard/views/section-view-examine-searchers.ts index cb3837558be7..181f2f76fa01 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/search/examine-management-dashboard/views/section-view-examine-searchers.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/search/examine-management-dashboard/views/section-view-examine-searchers.ts @@ -7,7 +7,7 @@ import { UmbModalRouteRegistrationController } from '@umbraco-cms/backoffice/rou import type { SearchResultResponseModel, FieldPresentationModel } from '@umbraco-cms/backoffice/external/backend-api'; import { SearcherService } from '@umbraco-cms/backoffice/external/backend-api'; import { UmbLitElement, umbFocus } from '@umbraco-cms/backoffice/lit-element'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; interface ExposedSearchResultField { name: string; @@ -58,7 +58,7 @@ export class UmbDashboardExamineSearcherElement extends UmbLitElement { if (!this._searchInput.value.length) return; this._searchLoading = true; - const { data } = await tryExecuteAndNotify( + const { data } = await tryExecute( this, SearcherService.getSearcherBySearcherNameQuery({ searcherName: this.searcherName, diff --git a/src/Umbraco.Web.UI.Client/src/packages/sysinfo/repository/sysinfo.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/sysinfo/repository/sysinfo.repository.ts index 042a02ed5813..70b277638bad 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/sysinfo/repository/sysinfo.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/sysinfo/repository/sysinfo.repository.ts @@ -11,12 +11,12 @@ export class UmbSysinfoRepository extends UmbRepositoryBase { } async requestTroubleShooting() { - const { data } = await tryExecuteAndNotify(this, ServerService.getServerTroubleshooting()); + const { data } = await tryExecute(this, ServerService.getServerTroubleshooting()); return data; } async requestServerInformation() { - const { data } = await tryExecuteAndNotify(this, ServerService.getServerInformation()); + const { data } = await tryExecute(this, ServerService.getServerInformation()); return data; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/tags/repository/sources/tag.server.data.ts b/src/Umbraco.Web.UI.Client/src/packages/tags/repository/sources/tag.server.data.ts index 9f697497a4a1..dfef822fb74f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tags/repository/sources/tag.server.data.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/tags/repository/sources/tag.server.data.ts @@ -1,6 +1,6 @@ import { TagService } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; /** * A data source for the Tag that fetches data from the server @@ -43,6 +43,6 @@ export class UmbTagServerDataSource { tagGroup?: string; culture?: string; }) { - return tryExecuteAndNotify(this.#host, TagService.getTag({ query, skip, take, tagGroup, culture })); + return tryExecute(this.#host, TagService.getTag({ query, skip, take, tagGroup, culture })); } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/telemetry/dashboard-telemetry.element.ts b/src/Umbraco.Web.UI.Client/src/packages/telemetry/dashboard-telemetry.element.ts index 08f885388a4e..6b5a33ea4a86 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/telemetry/dashboard-telemetry.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/telemetry/dashboard-telemetry.element.ts @@ -3,7 +3,7 @@ import type { UUIButtonState } from '@umbraco-cms/backoffice/external/uui'; import type { TelemetryResponseModel } from '@umbraco-cms/backoffice/external/backend-api'; import { TelemetryLevelModel, TelemetryService, ApiError } from '@umbraco-cms/backoffice/external/backend-api'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; @customElement('umb-dashboard-telemetry') @@ -26,10 +26,10 @@ export class UmbDashboardTelemetryElement extends UmbLitElement { } private async _setup() { - const telemetryLevels = await tryExecuteAndNotify(this, TelemetryService.getTelemetry({ skip: 0, take: 3 })); + const telemetryLevels = await tryExecute(this, TelemetryService.getTelemetry({ skip: 0, take: 3 })); this._telemetryLevels = telemetryLevels.data?.items ?? []; - const telemetryLevel = await tryExecuteAndNotify(this, TelemetryService.getTelemetryLevel()); + const telemetryLevel = await tryExecute(this, TelemetryService.getTelemetryLevel()); this._telemetryFormData = telemetryLevel.data?.telemetryLevel ?? TelemetryLevelModel.BASIC; } @@ -38,7 +38,7 @@ export class UmbDashboardTelemetryElement extends UmbLitElement { this._buttonState = 'waiting'; - const { error } = await tryExecuteAndNotify( + const { error } = await tryExecute( this, TelemetryService.postTelemetryLevel({ requestBody: { telemetryLevel: this._telemetryFormData }, diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/entity-actions/create/snippet-modal/create-from-snippet-modal.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/entity-actions/create/snippet-modal/create-from-snippet-modal.ts index 5b12e3d58dce..396b2e920881 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/entity-actions/create/snippet-modal/create-from-snippet-modal.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/entity-actions/create/snippet-modal/create-from-snippet-modal.ts @@ -1,7 +1,7 @@ import type { UmbCreatePartialViewFromSnippetModalData } from './index.js'; import { html, customElement, state } from '@umbraco-cms/backoffice/external/lit'; import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; import type { PartialViewSnippetItemResponseModel } from '@umbraco-cms/backoffice/external/backend-api'; import { PartialViewService } from '@umbraco-cms/backoffice/external/backend-api'; @@ -25,7 +25,7 @@ export class UmbPartialViewCreateFromSnippetModalElement extends UmbModalBaseEle } protected override async firstUpdated() { - const { data } = await tryExecuteAndNotify(this, PartialViewService.getPartialViewSnippet({ take: 10000 })); + const { data } = await tryExecute(this, PartialViewService.getPartialViewSnippet({ take: 10000 })); if (data) { this._snippets = data.items.map((snippet) => { diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/entity-actions/rename/rename-partial-view.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/entity-actions/rename/rename-partial-view.server.data-source.ts index 92965afabb42..5128a36559f8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/entity-actions/rename/rename-partial-view.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/entity-actions/rename/rename-partial-view.server.data-source.ts @@ -6,7 +6,7 @@ import { import type { RenameStylesheetRequestModel } from '@umbraco-cms/backoffice/external/backend-api'; import { PartialViewService } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; export class UmbRenamePartialViewServerDataSource { #host: UmbControllerHost; @@ -36,7 +36,7 @@ export class UmbRenamePartialViewServerDataSource { name: appendFileExtensionIfNeeded(name, '.cshtml'), }; - const { data, error } = await tryExecuteAndNotify( + const { data, error } = await tryExecute( this.#host, PartialViewService.putPartialViewByPathRename({ path: encodeURIComponent(path), diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/repository/item/partial-view-item.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/repository/item/partial-view-item.server.data-source.ts index 55e91bf34db1..46997b774708 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/repository/item/partial-view-item.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/repository/item/partial-view-item.server.data-source.ts @@ -3,7 +3,7 @@ import type { UmbPartialViewItemModel } from '../../types.js'; import { UmbServerFilePathUniqueSerializer } from '@umbraco-cms/backoffice/server-file-system'; import type { UmbItemDataSource } from '@umbraco-cms/backoffice/repository'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; import { PartialViewService } from '@umbraco-cms/backoffice/external/backend-api'; /** @@ -40,7 +40,7 @@ export class UmbPartialViewItemServerDataSource implements UmbItemDataSource x !== null) as string[]; - const { data, error } = await tryExecuteAndNotify( + const { data, error } = await tryExecute( this.#host, PartialViewService.getItemPartialView({ path: paths, diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/repository/partial-view-detail.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/repository/partial-view-detail.server.data-source.ts index f884bab984b5..8cbe60d9ddc5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/repository/partial-view-detail.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/repository/partial-view-detail.server.data-source.ts @@ -11,7 +11,7 @@ import type { import { PartialViewService } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import type { UmbDetailDataSource } from '@umbraco-cms/backoffice/repository'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; export class UmbPartialViewDetailServerDataSource implements UmbDetailDataSource { #host: UmbControllerHost; @@ -46,7 +46,7 @@ export class UmbPartialViewDetailServerDataSource implements UmbDetailDataSource content: model.content, }; - const { data, error } = await tryExecuteAndNotify( + const { data, error } = await tryExecute( this.#host, PartialViewService.postPartialView({ requestBody, @@ -68,7 +68,7 @@ export class UmbPartialViewDetailServerDataSource implements UmbDetailDataSource const path = this.#serverFilePathUniqueSerializer.toServerPath(unique); if (!path) throw new Error('Path is missing'); - const { data, error } = await tryExecuteAndNotify( + const { data, error } = await tryExecute( this.#host, PartialViewService.getPartialViewByPath({ path: encodeURIComponent(path) }), ); @@ -97,7 +97,7 @@ export class UmbPartialViewDetailServerDataSource implements UmbDetailDataSource content: model.content, }; - const { error } = await tryExecuteAndNotify( + const { error } = await tryExecute( this.#host, PartialViewService.putPartialViewByPath({ path: encodeURIComponent(path), @@ -118,7 +118,7 @@ export class UmbPartialViewDetailServerDataSource implements UmbDetailDataSource const path = this.#serverFilePathUniqueSerializer.toServerPath(unique); if (!path) throw new Error('Path is missing'); - return tryExecuteAndNotify( + return tryExecute( this.#host, PartialViewService.deletePartialViewByPath({ path: encodeURIComponent(path), diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/tree/folder/repository/partial-view-folder.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/tree/folder/repository/partial-view-folder.server.data-source.ts index e56ae5b048f7..60593bfb68f4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/tree/folder/repository/partial-view-folder.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/tree/folder/repository/partial-view-folder.server.data-source.ts @@ -4,7 +4,7 @@ import type { UmbFolderModel } from '@umbraco-cms/backoffice/tree'; import type { CreatePartialViewFolderRequestModel } from '@umbraco-cms/backoffice/external/backend-api'; import { PartialViewService } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; import { UmbId } from '@umbraco-cms/backoffice/id'; import type { UmbDetailDataSource } from '@umbraco-cms/backoffice/repository'; @@ -55,7 +55,7 @@ export class UmbPartialViewFolderServerDataSource implements UmbDetailDataSource const path = this.#serverFilePathUniqueSerializer.toServerPath(unique); if (!path) throw new Error('Cannot read partial view folder without a path'); - const { data, error } = await tryExecuteAndNotify( + const { data, error } = await tryExecute( this.#host, PartialViewService.getPartialViewFolderByPath({ path: encodeURIComponent(path), @@ -94,7 +94,7 @@ export class UmbPartialViewFolderServerDataSource implements UmbDetailDataSource name: model.name, }; - const { data, error } = await tryExecuteAndNotify( + const { data, error } = await tryExecute( this.#host, PartialViewService.postPartialViewFolder({ requestBody, @@ -122,7 +122,7 @@ export class UmbPartialViewFolderServerDataSource implements UmbDetailDataSource const path = this.#serverFilePathUniqueSerializer.toServerPath(unique); if (!path) throw new Error('Cannot delete partial view folder without a path'); - return tryExecuteAndNotify( + return tryExecute( this.#host, PartialViewService.deletePartialViewFolderByPath({ path: encodeURIComponent(path), diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/workspace/partial-view-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/workspace/partial-view-workspace.context.ts index a38bf0de2ae7..a9948c74c827 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/workspace/partial-view-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/workspace/partial-view-workspace.context.ts @@ -14,7 +14,7 @@ import { UmbEntityNamedDetailWorkspaceContextBase, UmbWorkspaceIsNewRedirectController, } from '@umbraco-cms/backoffice/workspace'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; import { PartialViewService } from '@umbraco-cms/backoffice/external/backend-api'; import type { IRoutingInfo, PageComponent } from '@umbraco-cms/backoffice/router'; import { UmbServerFileRenameWorkspaceRedirectController } from '@umbraco-cms/backoffice/server-file-system'; @@ -112,7 +112,7 @@ export class UmbPartialViewWorkspaceContext } #getSnippet(unique: string) { - return tryExecuteAndNotify( + return tryExecute( this, PartialViewService.getPartialViewSnippetById({ id: unique, diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/scripts/entity-actions/rename/rename-script.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/scripts/entity-actions/rename/rename-script.server.data-source.ts index 6aae06b1d42d..13202d266645 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/scripts/entity-actions/rename/rename-script.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/scripts/entity-actions/rename/rename-script.server.data-source.ts @@ -6,7 +6,7 @@ import { import type { RenameStylesheetRequestModel } from '@umbraco-cms/backoffice/external/backend-api'; import { ScriptService } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; export class UmbRenameScriptServerDataSource { #host: UmbControllerHost; @@ -36,7 +36,7 @@ export class UmbRenameScriptServerDataSource { name: appendFileExtensionIfNeeded(name, '.js'), }; - const { data, error } = await tryExecuteAndNotify( + const { data, error } = await tryExecute( this.#host, ScriptService.putScriptByPathRename({ path: encodeURIComponent(path), diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/scripts/repository/item/script-item.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/scripts/repository/item/script-item.server.data-source.ts index 257e195196f8..247a1b068059 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/scripts/repository/item/script-item.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/scripts/repository/item/script-item.server.data-source.ts @@ -3,7 +3,7 @@ import type { UmbScriptItemModel } from '../../types.js'; import { UmbServerFilePathUniqueSerializer } from '@umbraco-cms/backoffice/server-file-system'; import type { UmbItemDataSource } from '@umbraco-cms/backoffice/repository'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; import { ScriptService } from '@umbraco-cms/backoffice/external/backend-api'; /** @@ -40,7 +40,7 @@ export class UmbScriptItemServerDataSource implements UmbItemDataSource x !== null) as string[]; - const { data, error } = await tryExecuteAndNotify( + const { data, error } = await tryExecute( this.#host, ScriptService.getItemScript({ path: paths, diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/scripts/repository/script-detail.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/scripts/repository/script-detail.server.data-source.ts index 5fe28babb8df..0cbcb8e4d78c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/scripts/repository/script-detail.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/scripts/repository/script-detail.server.data-source.ts @@ -8,7 +8,7 @@ import type { CreateScriptRequestModel, UpdateScriptRequestModel } from '@umbrac import { ScriptService } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import type { UmbDetailDataSource } from '@umbraco-cms/backoffice/repository'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; export class UmbScriptDetailServerDataSource implements UmbDetailDataSource { #host: UmbControllerHost; @@ -43,7 +43,7 @@ export class UmbScriptDetailServerDataSource implements UmbDetailDataSource x !== null) as string[]; - const { data, error } = await tryExecuteAndNotify( + const { data, error } = await tryExecute( this.#host, StylesheetService.getItemStylesheet({ path: paths, diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/repository/stylesheet-detail.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/repository/stylesheet-detail.server.data-source.ts index ecfa5313460e..cc0ff55563ea 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/repository/stylesheet-detail.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/repository/stylesheet-detail.server.data-source.ts @@ -11,7 +11,7 @@ import type { import { StylesheetService } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import type { UmbDetailDataSource } from '@umbraco-cms/backoffice/repository'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; export class UmbStylesheetDetailServerDataSource implements UmbDetailDataSource { #host: UmbControllerHost; @@ -46,7 +46,7 @@ export class UmbStylesheetDetailServerDataSource implements UmbDetailDataSource< content: model.content, }; - const { data, error } = await tryExecuteAndNotify( + const { data, error } = await tryExecute( this.#host, StylesheetService.postStylesheet({ requestBody, @@ -68,7 +68,7 @@ export class UmbStylesheetDetailServerDataSource implements UmbDetailDataSource< const path = this.#serverFilePathUniqueSerializer.toServerPath(unique); if (!path) throw new Error('Path is missing'); - const { data, error } = await tryExecuteAndNotify( + const { data, error } = await tryExecute( this.#host, StylesheetService.getStylesheetByPath({ path: encodeURIComponent(path) }), ); @@ -97,7 +97,7 @@ export class UmbStylesheetDetailServerDataSource implements UmbDetailDataSource< content: model.content, }; - const { error } = await tryExecuteAndNotify( + const { error } = await tryExecute( this.#host, StylesheetService.putStylesheetByPath({ path: encodeURIComponent(path), @@ -118,7 +118,7 @@ export class UmbStylesheetDetailServerDataSource implements UmbDetailDataSource< const path = this.#serverFilePathUniqueSerializer.toServerPath(unique); if (!path) throw new Error('Path is missing'); - return tryExecuteAndNotify( + return tryExecute( this.#host, StylesheetService.deleteStylesheetByPath({ path: encodeURIComponent(path), diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/tree/folder/repository/stylesheet-folder.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/tree/folder/repository/stylesheet-folder.server.data-source.ts index aae714461fa4..6eaaada8e8f5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/tree/folder/repository/stylesheet-folder.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/tree/folder/repository/stylesheet-folder.server.data-source.ts @@ -4,7 +4,7 @@ import type { UmbFolderModel } from '@umbraco-cms/backoffice/tree'; import type { CreateStylesheetFolderRequestModel } from '@umbraco-cms/backoffice/external/backend-api'; import { StylesheetService } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; import { UmbId } from '@umbraco-cms/backoffice/id'; import type { UmbDetailDataSource } from '@umbraco-cms/backoffice/repository'; @@ -49,7 +49,7 @@ export class UmbStylesheetFolderServerDataSource implements UmbDetailDataSource< const path = this.#serverFilePathUniqueSerializer.toServerPath(unique); if (!path) throw new Error('Cannot read stylesheet folder without a path'); - const { data, error } = await tryExecuteAndNotify( + const { data, error } = await tryExecute( this.#host, StylesheetService.getStylesheetFolderByPath({ path: encodeURIComponent(path), @@ -88,7 +88,7 @@ export class UmbStylesheetFolderServerDataSource implements UmbDetailDataSource< name: model.name, }; - const { data, error } = await tryExecuteAndNotify( + const { data, error } = await tryExecute( this.#host, StylesheetService.postStylesheetFolder({ requestBody, @@ -116,7 +116,7 @@ export class UmbStylesheetFolderServerDataSource implements UmbDetailDataSource< const path = this.#serverFilePathUniqueSerializer.toServerPath(unique); if (!path) throw new Error('Cannot delete stylesheet folder without a path'); - return tryExecuteAndNotify( + return tryExecute( this.#host, StylesheetService.deleteStylesheetFolderByPath({ path: encodeURIComponent(path), diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/templates/repository/detail/template-detail.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/templates/repository/detail/template-detail.server.data-source.ts index 299da0b1ac77..7bcbeb500aa0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/templates/repository/detail/template-detail.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/templates/repository/detail/template-detail.server.data-source.ts @@ -8,7 +8,7 @@ import type { } from '@umbraco-cms/backoffice/external/backend-api'; import { TemplateService } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; /** * A data source for the Template that fetches data from the server @@ -59,7 +59,7 @@ export class UmbTemplateServerDataSource implements UmbDetailDataSource { - return tryExecuteAndNotify(this.#host, UserService.deleteUserAvatarById({ id: unique })); + return tryExecute(this.#host, UserService.deleteUserAvatarById({ id: unique })); } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/change-password/change-user-password.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/change-password/change-user-password.server.data-source.ts index ebe00ebe02df..0dcb1c63f1f8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/change-password/change-user-password.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/change-password/change-user-password.server.data-source.ts @@ -1,6 +1,6 @@ import { UserService } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; /** * A server data source for changing the password of a user @@ -28,7 +28,7 @@ export class UmbChangeUserPasswordServerDataSource { async changePassword(id: string, newPassword: string) { if (!id) throw new Error('User Id is missing'); - return tryExecuteAndNotify( + return tryExecute( this.#host, UserService.postUserByIdChangePassword({ id, diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/config/current-user-config.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/config/current-user-config.server.data-source.ts index 5054a2b4b9f9..2ce7ef04d5ab 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/config/current-user-config.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/config/current-user-config.server.data-source.ts @@ -1,6 +1,6 @@ import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { UserService } from '@umbraco-cms/backoffice/external/backend-api'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; export class UmbCurrentUserConfigServerDataSource { #host; @@ -14,6 +14,6 @@ export class UmbCurrentUserConfigServerDataSource { * @memberof UmbCurrentUserConfigServerDataSource */ getCurrentUserConfig() { - return tryExecuteAndNotify(this.#host, UserService.getUserCurrentConfiguration()); + return tryExecute(this.#host, UserService.getUserCurrentConfiguration()); } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/config/user-config.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/config/user-config.server.data-source.ts index ced6f1cc52c1..27cd73eed86b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/config/user-config.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/config/user-config.server.data-source.ts @@ -1,6 +1,6 @@ import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { UserService } from '@umbraco-cms/backoffice/external/backend-api'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; export class UmbUserConfigServerDataSource { #host; @@ -14,7 +14,7 @@ export class UmbUserConfigServerDataSource { * @memberof UmbUserConfigServerDataSource */ getUserConfig() { - return tryExecuteAndNotify(this.#host, UserService.getUserConfiguration()); + return tryExecute(this.#host, UserService.getUserConfiguration()); } /** @@ -22,6 +22,6 @@ export class UmbUserConfigServerDataSource { * @memberof UmbUserConfigServerDataSource */ getCurrentUserConfig() { - return tryExecuteAndNotify(this.#host, UserService.getUserCurrentConfiguration()); + return tryExecute(this.#host, UserService.getUserCurrentConfiguration()); } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/detail/user-detail.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/detail/user-detail.server.data-source.ts index 6dfb70223784..f33bebed5b25 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/detail/user-detail.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/detail/user-detail.server.data-source.ts @@ -10,7 +10,7 @@ import type { } from '@umbraco-cms/backoffice/external/backend-api'; import { UserService } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; /** * A data source for the User that fetches data from the server @@ -72,7 +72,7 @@ export class UmbUserServerDataSource implements UmbDetailDataSource Date: Fri, 4 Apr 2025 15:07:26 +0200 Subject: [PATCH 22/45] feat: copies helper functions to resources package and deprecates in notification package --- .../src/packages/core/notification/index.ts | 2 +- .../isUmbNotifications.function.ts | 12 ++++++--- .../resources/api-interceptor.controller.ts | 9 +++---- .../extractUmbNotificationColor.function.ts | 5 ++-- .../src/packages/core/resources/index.ts | 2 ++ .../resources/isUmbNotifications.function.ts | 26 +++++++++++++++++++ 6 files changed, 42 insertions(+), 14 deletions(-) rename src/Umbraco.Web.UI.Client/src/packages/core/{notification => resources}/extractUmbNotificationColor.function.ts (78%) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/resources/isUmbNotifications.function.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/notification/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/notification/index.ts index 1d57e22f7db6..526e0028f401 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/notification/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/notification/index.ts @@ -1,6 +1,6 @@ import './layouts/default/index.js'; export * from './controllers/peek-error/index.js'; -export * from './extractUmbNotificationColor.function.js'; +export * from '../resources/extractUmbNotificationColor.function.js'; export * from './isUmbNotifications.function.js'; export * from './notification-handler.js'; export * from './notification.context.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/notification/isUmbNotifications.function.ts b/src/Umbraco.Web.UI.Client/src/packages/core/notification/isUmbNotifications.function.ts index 6c8b9b2895e6..ab861df6c2e4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/notification/isUmbNotifications.function.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/notification/isUmbNotifications.function.ts @@ -1,8 +1,7 @@ import { EventMessageTypeModel } from '@umbraco-cms/backoffice/external/backend-api'; /** - * - * @param notification + * @deprecated Import from `@umbraco-cms/backoffice/resources` instead. */ function objectIsUmbNotification(notification: unknown): notification is UmbNotificationsEventModel { if (typeof notification !== 'object' || notification === null) { @@ -17,6 +16,9 @@ function objectIsUmbNotification(notification: unknown): notification is UmbNoti ); } +/** + * @deprecated Import from `@umbraco-cms/backoffice/resources` instead. + */ export interface UmbNotificationsEventModel { category: string; message: string; @@ -24,11 +26,13 @@ export interface UmbNotificationsEventModel { } /** - * - * @param notifications + * @deprecated Import from `@umbraco-cms/backoffice/resources` instead. */ export function isUmbNotifications(notifications: Array): notifications is Array { return notifications.every(objectIsUmbNotification); } +/** + * @deprecated Import from `@umbraco-cms/backoffice/resources` instead. + */ export const UMB_NOTIFICATION_HEADER = 'umb-notifications'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/resources/api-interceptor.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/resources/api-interceptor.controller.ts index 73c183ca0899..126d7006983c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/resources/api-interceptor.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/resources/api-interceptor.controller.ts @@ -1,12 +1,9 @@ +import { extractUmbNotificationColor } from './extractUmbNotificationColor.function.js'; +import { isUmbNotifications, UMB_NOTIFICATION_HEADER } from './isUmbNotifications.function.js'; import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; import type { OpenAPIConfig } from '@umbraco-cms/backoffice/external/backend-api'; import { UMB_AUTH_CONTEXT } from '@umbraco-cms/backoffice/auth'; -import { - extractUmbNotificationColor, - isUmbNotifications, - UMB_NOTIFICATION_HEADER, - type UmbNotificationColor, -} from '@umbraco-cms/backoffice/notification'; +import type { UmbNotificationColor } from '@umbraco-cms/backoffice/notification'; export class UmbApiInterceptorController extends UmbControllerBase { public bindDefaultInterceptors(client: OpenAPIConfig) { diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/notification/extractUmbNotificationColor.function.ts b/src/Umbraco.Web.UI.Client/src/packages/core/resources/extractUmbNotificationColor.function.ts similarity index 78% rename from src/Umbraco.Web.UI.Client/src/packages/core/notification/extractUmbNotificationColor.function.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/resources/extractUmbNotificationColor.function.ts index 492f7c76b74d..efeb246e1e07 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/notification/extractUmbNotificationColor.function.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/resources/extractUmbNotificationColor.function.ts @@ -1,9 +1,8 @@ -import type { UmbNotificationColor } from './types.js'; +import type { UmbNotificationColor } from '../notification/types.js'; import { EventMessageTypeModel } from '@umbraco-cms/backoffice/external/backend-api'; /** - * - * @param type + * @deprecated Import from `@umbraco-cms/backoffice/resources` instead. */ export function extractUmbNotificationColor(type: EventMessageTypeModel): UmbNotificationColor { switch (type) { diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/resources/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/resources/index.ts index 140e5716737a..f79ef6599a4e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/resources/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/resources/index.ts @@ -3,7 +3,9 @@ export * from './resource.controller.js'; export * from './tryExecute.function.js'; export * from './tryExecuteAndNotify.function.js'; export * from './tryXhrRequest.function.js'; +export * from './extractUmbNotificationColor.function.js'; export * from './extractUmbColorVariable.function.js'; +export * from './isUmbNotifications.function.js'; export * from './apiTypeValidators.function.js'; export * from './umb-error.js'; export type * from './types.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/resources/isUmbNotifications.function.ts b/src/Umbraco.Web.UI.Client/src/packages/core/resources/isUmbNotifications.function.ts new file mode 100644 index 000000000000..036185b35542 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/resources/isUmbNotifications.function.ts @@ -0,0 +1,26 @@ +import { EventMessageTypeModel } from '@umbraco-cms/backoffice/external/backend-api'; + +function objectIsUmbNotification(notification: unknown): notification is UmbNotificationsEventModel { + if (typeof notification !== 'object' || notification === null) { + return false; + } + const object = notification as UmbNotificationsEventModel; + return ( + typeof object.category === 'string' && + typeof object.message === 'string' && + typeof object.type === 'string' && + Object.values(EventMessageTypeModel).includes(object.type) + ); +} + +export interface UmbNotificationsEventModel { + category: string; + message: string; + type: EventMessageTypeModel; +} + +export function isUmbNotifications(notifications: Array): notifications is Array { + return notifications.every(objectIsUmbNotification); +} + +export const UMB_NOTIFICATION_HEADER = 'umb-notifications'; From 78ad5bc08f61fa2c4a20ab6d3bb2c91c8ec3d1b9 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Fri, 4 Apr 2025 15:13:47 +0200 Subject: [PATCH 23/45] chore: removes unused imports --- .../repository/public-access.server.data.ts | 2 +- .../src/packages/sysinfo/repository/sysinfo.repository.ts | 2 +- .../current-user/repository/current-user.server.data-source.ts | 2 +- .../user/user/repository/sources/user-mfa.server.data-source.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member-public-access/repository/public-access.server.data.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member-public-access/repository/public-access.server.data.ts index 47c12660b26f..5dcdf0d15e3f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member-public-access/repository/public-access.server.data.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member-public-access/repository/public-access.server.data.ts @@ -1,7 +1,7 @@ import { DocumentService } from '@umbraco-cms/backoffice/external/backend-api'; import type { PublicAccessRequestModel } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { tryExecute, tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; /** * A data source for the Document Public Access that fetches data from the server diff --git a/src/Umbraco.Web.UI.Client/src/packages/sysinfo/repository/sysinfo.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/sysinfo/repository/sysinfo.repository.ts index 70b277638bad..cbe214b66b88 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/sysinfo/repository/sysinfo.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/sysinfo/repository/sysinfo.repository.ts @@ -1,7 +1,7 @@ import type { UmbServerUpgradeCheck } from '../types.js'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { UmbRepositoryBase } from '@umbraco-cms/backoffice/repository'; -import { tryExecute, tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; import { ServerService } from '@umbraco-cms/backoffice/external/backend-api'; import { UMB_APP_CONTEXT } from '@umbraco-cms/backoffice/app'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/repository/current-user.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/repository/current-user.server.data-source.ts index d663e5151038..1a091c124aa3 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/repository/current-user.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/repository/current-user.server.data-source.ts @@ -1,7 +1,7 @@ import type { UmbCurrentUserModel } from '../types.js'; import { UserService } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { tryExecute, tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; /** * A data source for the current user that fetches data from the server diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/sources/user-mfa.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/sources/user-mfa.server.data-source.ts index e113a29931c8..391749558bee 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/sources/user-mfa.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/sources/user-mfa.server.data-source.ts @@ -1,6 +1,6 @@ import { UserService } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { tryExecute, tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; /** * A data source for User MFA items that fetches data from the server From 6991ad5213a40d658d9268be7f5549005619168c Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Mon, 7 Apr 2025 09:48:46 +0200 Subject: [PATCH 24/45] feat: adds exports --- src/Umbraco.Web.UI.Client/src/packages/core/resources/index.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/resources/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/resources/index.ts index f79ef6599a4e..40088cc35ff8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/resources/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/resources/index.ts @@ -1,5 +1,7 @@ export * from './api-interceptor.controller.js'; export * from './resource.controller.js'; +export * from './try-execute.controller.js'; +export * from './try-xhr-request.controller.js'; export * from './tryExecute.function.js'; export * from './tryExecuteAndNotify.function.js'; export * from './tryXhrRequest.function.js'; From 2da122ee786fd617eaa38e905ed468cede206c49 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Mon, 7 Apr 2025 09:49:05 +0200 Subject: [PATCH 25/45] chore: removes controller that is no longer useful --- .../try-execute-and-notify.controller.ts | 26 ------------------- 1 file changed, 26 deletions(-) delete mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/resources/try-execute-and-notify.controller.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/resources/try-execute-and-notify.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/resources/try-execute-and-notify.controller.ts deleted file mode 100644 index 17c2d1ff8f9c..000000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/core/resources/try-execute-and-notify.controller.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { UmbResourceController } from './resource.controller.js'; -import type { UmbDataSourceResponse } from '@umbraco-cms/backoffice/repository'; - -/** - * A controller that tries to execute a promise and returns the result. - * @internal - * @deprecated Use the {UmbTryExecuteController} instead and handle the error in the caller. - * This class is kept for backwards compatibility and will be removed in a future version. - */ -export class UmbTryExecuteAndNotifyController extends UmbResourceController { - /** - * @internal - * @deprecated Use the {UmbTryExecuteController.tryExecute} instead and handle the error in the caller. - * This method is kept for backwards compatibility and will be removed in a future version. - */ - override async tryExecuteAndNotify(): Promise> { - try { - return { data: await this._promise }; - } catch (error) { - // Error might be a legacy error, so we need to check if it is an UmbError - return { - error: this.mapToUmbError(error), - }; - } - } -} From be5b7fbee65c82a7280271ea03a1a0c75a82b270 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Mon, 7 Apr 2025 09:49:15 +0200 Subject: [PATCH 26/45] feat: marks _peekError as protected --- .../src/packages/core/resources/resource.controller.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/resources/resource.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/resources/resource.controller.ts index 3e220c588ceb..81be56ee5114 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/resources/resource.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/resources/resource.controller.ts @@ -193,20 +193,20 @@ export class UmbResourceController extends UmbControllerBase { 'The Umbraco object cache is corrupt, but your action may still have been executed. Please restart the server to reset the cache. This is a work in progress.'; } - this.#peekError(headline, message, problemDetails?.errors ?? problemDetails?.detail); + this._peekError(headline, message, problemDetails?.errors ?? problemDetails?.detail); } break; default: // Other errors if (!isCancelledByNotification) { const headline = problemDetails?.title ?? error.name ?? 'Server Error'; - this.#peekError('', headline, problemDetails?.errors ?? problemDetails?.detail); + this._peekError('', headline, problemDetails?.errors ?? problemDetails?.detail); } } } else { // UmbError or unknown error - log it to the console console.error('Unknown error', error); - this.#peekError('Error', error.message ?? 'Unknown error', []); + this._peekError('Error', error.message ?? 'Unknown error', []); } } @@ -337,7 +337,7 @@ export class UmbResourceController extends UmbControllerBase { return promise; } - async #peekError(headline: string, message: string, details: unknown) { + protected async _peekError(headline: string, message: string, details: unknown) { // This late importing is done to avoid circular reference [NL] (await import('@umbraco-cms/backoffice/notification')).umbPeekError(this, { headline, From 07c0266b294e7ff429e9da0a30d4edfcb765b651 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Mon, 7 Apr 2025 09:49:55 +0200 Subject: [PATCH 27/45] feat: adds support for error notifications (and to ignore them) and to cancel an ongoing request --- .../core/resources/try-execute.controller.ts | 50 ++++++++++++++++++- .../core/resources/tryExecute.function.ts | 9 +++- .../resources/tryExecuteAndNotify.function.ts | 8 +-- .../src/packages/core/resources/types.ts | 16 +++++- 4 files changed, 73 insertions(+), 10 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/resources/try-execute.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/resources/try-execute.controller.ts index 1ff94b3aba8c..2df496efbe93 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/resources/try-execute.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/resources/try-execute.controller.ts @@ -1,15 +1,61 @@ import { UmbResourceController } from './resource.controller.js'; +import type { UmbTryExecuteOptions } from './types.js'; +import { UmbApiError, UmbCancelError } from './umb-error.js'; import type { UmbDataSourceResponse } from '@umbraco-cms/backoffice/repository'; export class UmbTryExecuteController extends UmbResourceController { - async tryExecute(): Promise> { + #abortSignal?: AbortSignal; + + async tryExecute(opts?: UmbTryExecuteOptions): Promise> { try { + if (opts?.abortSignal) { + this.#abortSignal = opts.abortSignal; + this.#abortSignal.addEventListener('abort', () => this.cancel(), { once: true }); + } + return { data: await this._promise }; } catch (error) { // Error might be a legacy error, so we need to check if it is an UmbError + const umbError = this.mapToUmbError(error); + + if (!opts?.disableNotifications) { + this.#notifyOnError(umbError); + } + return { - error: this.mapToUmbError(error), + error: umbError, }; } } + + override destroy(): void { + if (this.#abortSignal) { + this.#abortSignal.removeEventListener('abort', this.cancel); + } + super.destroy(); + } + + #notifyOnError(error: unknown) { + if (UmbCancelError.isUmbCancelError(error)) { + // Cancel error, do not show notification + return; + } + + let headline = 'An error occurred'; + let message = 'An error occurred while trying to execute the request.'; + let details: Record = {}; + + if (UmbApiError.isUmbApiError(error)) { + // UmbApiError, show notification + headline = error.problemDetails.title ?? error.name; + message = error.problemDetails.detail ?? error.message; + details = error.problemDetails.errors ?? {}; + } else { + // Unknown error, show notification + headline = ''; + message = error instanceof Error ? error.message : 'An unknown error occurred.'; + } + + this._peekError(headline, message, details); + } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/resources/tryExecute.function.ts b/src/Umbraco.Web.UI.Client/src/packages/core/resources/tryExecute.function.ts index 56544f2a0426..293dd16363ce 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/resources/tryExecute.function.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/resources/tryExecute.function.ts @@ -1,10 +1,15 @@ import { UmbTryExecuteController } from './try-execute.controller.js'; +import type { UmbTryExecuteOptions } from './types.js'; import type { UmbDataSourceResponse } from '@umbraco-cms/backoffice/repository'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -export async function tryExecute(host: UmbControllerHost, promise: Promise): Promise> { +export async function tryExecute( + host: UmbControllerHost, + promise: Promise, + opts?: UmbTryExecuteOptions, +): Promise> { const controller = new UmbTryExecuteController(host, promise); - const response = await controller.tryExecute(); + const response = await controller.tryExecute(opts); controller.destroy(); return response as UmbDataSourceResponse; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/resources/tryExecuteAndNotify.function.ts b/src/Umbraco.Web.UI.Client/src/packages/core/resources/tryExecuteAndNotify.function.ts index bc2894965d50..d8f83d7fc51a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/resources/tryExecuteAndNotify.function.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/resources/tryExecuteAndNotify.function.ts @@ -1,4 +1,4 @@ -import { UmbTryExecuteAndNotifyController } from './try-execute-and-notify.controller.js'; +import { UmbTryExecuteController } from './try-execute.controller.js'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import type { UmbDataSourceResponse } from '@umbraco-cms/backoffice/repository'; import { UmbDeprecation } from '@umbraco-cms/backoffice/utils'; @@ -14,10 +14,10 @@ export async function tryExecuteAndNotify( new UmbDeprecation({ deprecated: 'The tryExecuteAndNotify function is deprecated.', removeInVersion: '18.0.0', - solution: 'Use the tryExecute function instead.', + solution: 'Use the tryExecute function with options instead.', }).warn(); - const controller = new UmbTryExecuteAndNotifyController(host, resource); - const response = await controller.tryExecuteAndNotify(); + const controller = new UmbTryExecuteController(host, resource); + const response = await controller.tryExecute({ disableNotifications: false }); controller.destroy(); return response as UmbDataSourceResponse; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/resources/types.ts b/src/Umbraco.Web.UI.Client/src/packages/core/resources/types.ts index d07b39731b13..48b03c4eb252 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/resources/types.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/resources/types.ts @@ -1,4 +1,4 @@ -export interface XhrRequestOptions { +export interface XhrRequestOptions extends UmbTryExecuteOptions { baseUrl?: string; method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS'; url: string; @@ -7,7 +7,6 @@ export interface XhrRequestOptions { headers?: Record; responseHeader?: string; onProgress?: (event: ProgressEvent) => void; - abortSignal?: AbortSignal; } export interface UmbProblemDetails { @@ -20,3 +19,16 @@ export interface UmbProblemDetails { extensions?: Record; errors?: Record; } + +export interface UmbTryExecuteOptions { + /** + * If set to true, the controller will not show any notifications at all. + * @default false + */ + disableNotifications?: boolean; + + /** + * Signal object to cancel the request. + */ + abortSignal?: AbortSignal; +} From dd4574a28ed17f96f9650dfb6cf0e0f2cae93739 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Mon, 7 Apr 2025 09:50:10 +0200 Subject: [PATCH 28/45] feat: eliminates duplicated logic in xhr controller --- .../resources/try-xhr-request.controller.ts | 30 ++----------------- .../core/resources/tryXhrRequest.function.ts | 6 ++-- 2 files changed, 5 insertions(+), 31 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/resources/try-xhr-request.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/resources/try-xhr-request.controller.ts index f3206927bd66..8141e1663c2d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/resources/try-xhr-request.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/resources/try-xhr-request.controller.ts @@ -1,27 +1,9 @@ import { UmbCancelablePromise } from './cancelable-promise.js'; -import { UmbResourceController } from './resource.controller.js'; +import { UmbTryExecuteController } from './try-execute.controller.js'; import type { XhrRequestOptions } from './types.js'; import { UmbApiError } from './umb-error.js'; -import type { UmbDataSourceResponse } from '@umbraco-cms/backoffice/repository'; - -export class UmbTryXhrRequestController extends UmbResourceController { - #abortSignal?: AbortSignal; - - async tryXhrRequest(abortSignal?: AbortSignal): Promise> { - try { - if (abortSignal) { - this.#abortSignal = abortSignal; - this.#abortSignal.addEventListener('abort', this.cancel, { once: true }); - } - return { data: await this._promise }; - } catch (error) { - // Error might be a legacy error, so we need to check if it is an UmbError - return { - error: this.mapToUmbError(error), - }; - } - } +export class UmbTryXhrRequestController extends UmbTryExecuteController { static createXhrRequest(options: XhrRequestOptions): UmbCancelablePromise { const baseUrl = options.baseUrl || '/umbraco'; @@ -109,12 +91,4 @@ export class UmbTryXhrRequestController extends UmbResourceController { }); }); } - - public override destroy(): void { - super.destroy(); - if (this.#abortSignal) { - this.#abortSignal.removeEventListener('abort', this.cancel); - } - this.#abortSignal = undefined; - } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/resources/tryXhrRequest.function.ts b/src/Umbraco.Web.UI.Client/src/packages/core/resources/tryXhrRequest.function.ts index 08bb3bacc5f6..2084d30d09e3 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/resources/tryXhrRequest.function.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/resources/tryXhrRequest.function.ts @@ -1,4 +1,4 @@ -import type { XhrRequestOptions } from './types.js'; +import type { UmbTryExecuteOptions, XhrRequestOptions } from './types.js'; import { UmbTryXhrRequestController } from './try-xhr-request.controller.js'; import { OpenAPI } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; @@ -9,7 +9,7 @@ import type { UmbDataSourceResponse } from '@umbraco-cms/backoffice/repository'; */ export async function tryXhrRequest( host: UmbControllerHost, - options: XhrRequestOptions, + options: XhrRequestOptions & UmbTryExecuteOptions, ): Promise> { const promise = UmbTryXhrRequestController.createXhrRequest({ ...options, @@ -17,7 +17,7 @@ export async function tryXhrRequest( token: OpenAPI.TOKEN as never, }); const controller = new UmbTryXhrRequestController(host, promise); - const response = await controller.tryXhrRequest(options.abortSignal); + const response = await controller.tryExecute(options); controller.destroy(); return response as UmbDataSourceResponse; } From 86bcfedd4b270f69ea7d16c03345bf242f6be925 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Mon, 7 Apr 2025 10:12:50 +0200 Subject: [PATCH 29/45] feat: touches only the cloned response to allow interceptors downstream to unwrap the body --- .../packages/core/resources/api-interceptor.controller.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/resources/api-interceptor.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/resources/api-interceptor.controller.ts index 126d7006983c..ed305123be15 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/resources/api-interceptor.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/resources/api-interceptor.controller.ts @@ -36,7 +36,9 @@ export class UmbApiInterceptorController extends UmbControllerBase { client.interceptors.response.use(async (response) => { if (response.ok) return response; - const error = await response.json(); + // Clones the response to read the body + const origResponse = response.clone(); + const error = await origResponse.json(); if (!error) return response; // If the error is not an UmbError, we check if it is a problem details object @@ -47,7 +49,7 @@ export class UmbApiInterceptorController extends UmbControllerBase { } // Handle 500 errors - we need to show a notification - if (response.status === 500) { + if (origResponse.status === 500) { let headline = error.title ?? error.name ?? 'Server Error'; let message = 'A fatal server error occurred. If this continues, please reach out to your administrator.'; @@ -64,7 +66,7 @@ export class UmbApiInterceptorController extends UmbControllerBase { this.#peekError(headline, message, error.errors ?? error.detail); } - // Create a new response with the error + // Return original response return response; }); } From 09df917e1d2faffd447bc48b6d246b8107e2f27f Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Mon, 7 Apr 2025 10:12:59 +0200 Subject: [PATCH 30/45] feat: stores the host for async context --- .../src/packages/core/resources/resource.controller.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/resources/resource.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/resources/resource.controller.ts index 81be56ee5114..0480314d1b68 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/resources/resource.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/resources/resource.controller.ts @@ -338,8 +338,11 @@ export class UmbResourceController extends UmbControllerBase { } protected async _peekError(headline: string, message: string, details: unknown) { - // This late importing is done to avoid circular reference [NL] - (await import('@umbraco-cms/backoffice/notification')).umbPeekError(this, { + // Store the host for usage in the following async context + const host = this._host; + + // This late importing is done to avoid circular reference + (await import('@umbraco-cms/backoffice/notification')).umbPeekError(host, { headline, message, details, From ec44c9d44fd21b254ebd315ea2115010b3e2472a Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Mon, 7 Apr 2025 10:15:50 +0200 Subject: [PATCH 31/45] feat: disables automatic notifications for validation data source --- .../validation/document-validation.server.data-source.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/repository/validation/document-validation.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/repository/validation/document-validation.server.data-source.ts index 8df17d34ba86..b8808d1e7ae1 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/repository/validation/document-validation.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/repository/validation/document-validation.server.data-source.ts @@ -46,6 +46,9 @@ export class UmbDocumentValidationServerDataSource { DocumentService.postDocumentValidate({ requestBody, }), + { + disableNotifications: true, + }, ); } @@ -75,6 +78,9 @@ export class UmbDocumentValidationServerDataSource { id: model.unique, requestBody, }), + { + disableNotifications: true, + }, ); } } From 75db1e6cddc6c878b6c3e4f00290bc6febfa33d1 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Mon, 7 Apr 2025 10:23:22 +0200 Subject: [PATCH 32/45] feat: disables notifications where they are otherwise handled or ignored --- .../src/apps/app/server-connection.ts | 8 ++++++-- .../src/apps/backoffice/backoffice.context.ts | 2 +- .../repository/preview/document-preview.repository.ts | 4 ++-- .../src/packages/sysinfo/repository/sysinfo.repository.ts | 6 +++--- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/apps/app/server-connection.ts b/src/Umbraco.Web.UI.Client/src/apps/app/server-connection.ts index 0eb426eac68c..1f22522350d2 100644 --- a/src/Umbraco.Web.UI.Client/src/apps/app/server-connection.ts +++ b/src/Umbraco.Web.UI.Client/src/apps/app/server-connection.ts @@ -64,7 +64,9 @@ export class UmbServerConnection extends UmbControllerBase { } async #setStatus() { - const { data, error } = await tryExecute(this, ServerService.getServerStatus()); + const { data, error } = await tryExecute(this._host, ServerService.getServerStatus(), { + disableNotifications: true, + }); if (error) { throw error; } @@ -74,7 +76,9 @@ export class UmbServerConnection extends UmbControllerBase { } async #setServerConfiguration() { - const { data, error } = await tryExecute(this, ServerService.getServerConfiguration()); + const { data, error } = await tryExecute(this._host, ServerService.getServerConfiguration(), { + disableNotifications: true, + }); if (error) { throw error; } diff --git a/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.context.ts b/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.context.ts index a11bc1235753..ee79a30bff74 100644 --- a/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.context.ts +++ b/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.context.ts @@ -57,7 +57,7 @@ export class UmbBackofficeContext extends UmbContextBase { } async #getVersion() { - const { data } = await tryExecute(this, ServerService.getServerInformation()); + const { data } = await tryExecute(this._host, ServerService.getServerInformation(), { disableNotifications: true }); if (!data) return; // A quick semver parser (to remove the unwanted bits) [LK] diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/repository/preview/document-preview.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/repository/preview/document-preview.repository.ts index 02dc7ade3e4f..3b4319e2437b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/repository/preview/document-preview.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/repository/preview/document-preview.repository.ts @@ -14,7 +14,7 @@ export class UmbDocumentPreviewRepository extends UmbRepositoryBase { * @memberof UmbDocumentPreviewRepository */ async enter(): Promise { - await tryExecute(this, PreviewService.postPreview()); + await tryExecute(this, PreviewService.postPreview(), { disableNotifications: true }); return; } @@ -24,7 +24,7 @@ export class UmbDocumentPreviewRepository extends UmbRepositoryBase { * @memberof UmbDocumentPreviewRepository */ async exit(): Promise { - await tryExecute(this, PreviewService.deletePreview()); + await tryExecute(this, PreviewService.deletePreview(), { disableNotifications: true }); return; } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/sysinfo/repository/sysinfo.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/sysinfo/repository/sysinfo.repository.ts index cbe214b66b88..9108a962bec2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/sysinfo/repository/sysinfo.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/sysinfo/repository/sysinfo.repository.ts @@ -11,12 +11,12 @@ export class UmbSysinfoRepository extends UmbRepositoryBase { } async requestTroubleShooting() { - const { data } = await tryExecute(this, ServerService.getServerTroubleshooting()); + const { data } = await tryExecute(this, ServerService.getServerTroubleshooting(), { disableNotifications: true }); return data; } async requestServerInformation() { - const { data } = await tryExecute(this, ServerService.getServerInformation()); + const { data } = await tryExecute(this, ServerService.getServerInformation(), { disableNotifications: true }); return data; } @@ -108,7 +108,7 @@ export class UmbSysinfoRepository extends UmbRepositoryBase { currentVersion: string, ): Promise { // Check the server for an upgrade because we have no stored check or the stored check is invalid - const { data } = await tryExecute(this, ServerService.getServerUpgradeCheck()); + const { data } = await tryExecute(this, ServerService.getServerUpgradeCheck(), { disableNotifications: true }); if (data) { // Save the last check date including the data received From 282831c9df80b2e283fa843c8a16c209b77244c9 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Mon, 7 Apr 2025 10:36:26 +0200 Subject: [PATCH 33/45] feat: removes deprecated code --- .../core/resources/resource.controller.ts | 278 +----------------- 1 file changed, 4 insertions(+), 274 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/resources/resource.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/resources/resource.controller.ts index 0480314d1b68..9de099ed4171 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/resources/resource.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/resources/resource.controller.ts @@ -1,20 +1,10 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { UMB_AUTH_CONTEXT } from '../auth/index.js'; -import { UmbDeprecation } from '../utils/deprecation/deprecation.js'; -import { UmbApiError, UmbCancelError, UmbError } from './umb-error.js'; -import { isApiError, isCancelError, isCancelablePromise } from './apiTypeValidators.function.js'; -import type { XhrRequestOptions } from './types.js'; +import { isApiError, isCancelablePromise, isCancelError } from './apiTypeValidators.function.js'; import type { UmbCancelablePromise } from './cancelable-promise.js'; -import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import { UmbApiError, UmbCancelError, UmbError } from './umb-error.js'; import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; -import type { UmbNotificationOptions } from '@umbraco-cms/backoffice/notification'; -import type { UmbDataSourceResponse } from '@umbraco-cms/backoffice/repository'; -import { - ApiError, - CancelablePromise, - CancelError, - type ProblemDetails, -} from '@umbraco-cms/backoffice/external/backend-api'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import type { CancelablePromise } from '@umbraco-cms/backoffice/external/backend-api'; export class UmbResourceController extends UmbControllerBase { /** @@ -77,266 +67,6 @@ export class UmbResourceController extends UmbControllerBase { this.cancel(); } - /** - * Base execute function with a try/catch block and return a tuple with the result and the error. - * @deprecated This method is deprecated and will be removed in Umbraco 18. Use the {@link UmbTryExecuteController} instead. - * @param promise - */ - static async tryExecute(promise: Promise): Promise> { - new UmbDeprecation({ - deprecated: 'UmbResourceController.tryExecute', - removeInVersion: '18.0.0', - solution: 'Use the UmbTryExecuteController instead.', - }).warn(); - - try { - return { data: await promise }; - } catch (error) { - if (isApiError(error) || isCancelError(error)) { - return { error }; - } - - console.error('Unknown error', error); - throw new Error('Unknown error'); - } - } - - /** - * Wrap the {tryExecute} function in a try/catch block and return the result. - * If the executor function throws an error, then show the details in a notification. - * @deprecated This method is deprecated and will be removed in Umbraco 18. Use the {@link UmbTryExecuteAndNotifyController} instead. - * @param options - */ - - async tryExecuteAndNotify(options?: UmbNotificationOptions): Promise> { - new UmbDeprecation({ - deprecated: 'UmbResourceController.tryExecuteAndNotify', - removeInVersion: '18.0.0', - solution: 'Use the UmbTryExecuteController instead.', - }).warn(); - - const { data, error } = await UmbResourceController.tryExecute(this._promise); - - if (options) { - new UmbDeprecation({ - deprecated: 'tryExecuteAndNotify `options` argument is deprecated.', - removeInVersion: '17.0.0', - solution: 'Use the method without arguments.', - }).warn(); - } - - if (error) { - /** - * Determine if we want to show a notification or just log the error to the console. - * If the error is not a recognizable system error (i.e. a HttpError), then we will show a notification - * with the error details using the default notification options. - */ - if (isCancelError(error)) { - // Cancelled - do nothing - return { error: new UmbCancelError(error.message) }; - } else if (isApiError(error)) { - console.groupCollapsed('ApiError caught in UmbResourceController'); - console.error('Request failed', error.request); - console.error('Request body', error.body); - console.error('Error', error); - console.groupEnd(); - - let problemDetails: ProblemDetails | null = null; - - // ApiError - body could hold a ProblemDetails from the server - if (typeof error.body !== 'undefined' && !!error.body) { - try { - (error as any).body = problemDetails = typeof error.body === 'string' ? JSON.parse(error.body) : error.body; - } catch (e) { - console.error('Error parsing error body (expected JSON)', e); - } - } - - /** - * Check if the operation status ends with `ByNotification` and if so, don't show a notification - * This is a special case where the operation was cancelled by the server and the client gets a notification header instead. - */ - let isCancelledByNotification = false; - if ( - problemDetails?.operationStatus && - typeof problemDetails.operationStatus === 'string' && - problemDetails.operationStatus.endsWith('ByNotification') - ) { - isCancelledByNotification = true; - } - - // Go through the error status codes and act accordingly - switch (error.status ?? 0) { - case 401: { - // See if we can get the UmbAuthContext and let it know the user is timed out - const authContext = await this.getContext(UMB_AUTH_CONTEXT, { preventTimeout: true }); - if (!authContext) { - throw new Error('Could not get the auth context'); - } - authContext.timeOut(); - break; - } - case 500: - // Server Error - - if (!isCancelledByNotification) { - let headline = problemDetails?.title ?? error.name ?? 'Server Error'; - let message = 'A fatal server error occurred. If this continues, please reach out to your administrator.'; - - // Special handling for ObjectCacheAppCache corruption errors, which we are investigating - if ( - problemDetails?.detail?.includes('ObjectCacheAppCache') || - problemDetails?.detail?.includes('Umbraco.Cms.Infrastructure.Scoping.Scope.DisposeLastScope()') - ) { - headline = 'Please restart the server'; - message = - 'The Umbraco object cache is corrupt, but your action may still have been executed. Please restart the server to reset the cache. This is a work in progress.'; - } - - this._peekError(headline, message, problemDetails?.errors ?? problemDetails?.detail); - } - break; - default: - // Other errors - if (!isCancelledByNotification) { - const headline = problemDetails?.title ?? error.name ?? 'Server Error'; - this._peekError('', headline, problemDetails?.errors ?? problemDetails?.detail); - } - } - } else { - // UmbError or unknown error - log it to the console - console.error('Unknown error', error); - this._peekError('Error', error.message ?? 'Unknown error', []); - } - } - - return { data, error }; - } - - /** - * Make an XHR request. - * @deprecated This method is deprecated and will be removed in Umbraco 18. Use the {@link UmbXhrRequestController} instead. - * @param host The controller host for this controller to be appended to. - * @param options The options for the XHR request. - */ - static xhrRequest(options: XhrRequestOptions): CancelablePromise { - new UmbDeprecation({ - deprecated: 'UmbResourceController.xhrRequest', - removeInVersion: '18.0.0', - solution: 'Use the UmbTryXhrRequestController instead.', - }).warn(); - - const baseUrl = options.baseUrl || '/umbraco'; - - const promise = new CancelablePromise(async (resolve, reject, onCancel) => { - const xhr = new XMLHttpRequest(); - xhr.open(options.method, `${baseUrl}${options.url}`, true); - - // Set default headers - if (options.token) { - const token = typeof options.token === 'function' ? await options.token() : options.token; - if (token) { - xhr.setRequestHeader('Authorization', `Bearer ${token}`); - } - } - - // Infer Content-Type header based on body type - if (options.body instanceof FormData) { - // Note: 'multipart/form-data' is automatically set by the browser for FormData - } else { - xhr.setRequestHeader('Content-Type', 'application/json'); - } - - // Set custom headers - if (options.headers) { - for (const [key, value] of Object.entries(options.headers)) { - xhr.setRequestHeader(key, value); - } - } - - xhr.upload.onprogress = (event) => { - if (options.onProgress) { - options.onProgress(event); - } - }; - - xhr.onload = () => { - try { - if (xhr.status >= 200 && xhr.status < 300) { - if (options.responseHeader) { - const response = xhr.getResponseHeader(options.responseHeader); - resolve(response as T); - } else { - resolve(JSON.parse(xhr.responseText)); - } - } else { - // TODO: [JOV] This has to be changed into our own error type, when we have a chance to introduce a breaking change in the future. - const error = new ApiError( - { - method: options.method, - url: `${baseUrl}${options.url}`, - }, - { - body: xhr.responseText, - ok: false, - status: xhr.status, - statusText: xhr.statusText, - url: xhr.responseURL, - }, - xhr.statusText, - ); - reject(error); - } - } catch { - // This most likely happens when the response is not JSON - reject(new Error(`Failed to make request: ${xhr.statusText}`)); - } - }; - - xhr.onerror = () => { - // TODO: [JOV] This has to be changed into our own error type, when we have a chance to introduce a breaking change in the future. - const error = new ApiError( - { - method: options.method, - url: `${baseUrl}${options.url}`, - }, - { - body: xhr.responseText, - ok: false, - status: xhr.status, - statusText: xhr.statusText, - url: xhr.responseURL, - }, - xhr.statusText, - ); - reject(error); - }; - - if (!onCancel.isCancelled) { - // Handle body based on Content-Type - if (options.body instanceof FormData) { - xhr.send(options.body); - } else { - xhr.send(JSON.stringify(options.body)); - } - } - - onCancel(() => { - xhr.abort(); - // TODO: [JOV] This has to be changed into our own error type, when we have a chance to introduce a breaking change in the future. - reject(new CancelError('Request was cancelled.')); - }); - }); - - if (options.abortSignal) { - options.abortSignal.addEventListener('abort', () => { - promise.cancel(); - }); - } - - return promise; - } - protected async _peekError(headline: string, message: string, details: unknown) { // Store the host for usage in the following async context const host = this._host; From 6c357f66007ec8a031d00969c73473d13876b944 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Mon, 7 Apr 2025 10:38:15 +0200 Subject: [PATCH 34/45] feat: eliminates a controller that only had a static method --- .../src/packages/core/resources/index.ts | 1 - .../resources/try-xhr-request.controller.ts | 94 ------------------ .../core/resources/tryXhrRequest.function.ts | 96 ++++++++++++++++++- 3 files changed, 93 insertions(+), 98 deletions(-) delete mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/resources/try-xhr-request.controller.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/resources/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/resources/index.ts index 40088cc35ff8..e1e175611104 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/resources/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/resources/index.ts @@ -1,7 +1,6 @@ export * from './api-interceptor.controller.js'; export * from './resource.controller.js'; export * from './try-execute.controller.js'; -export * from './try-xhr-request.controller.js'; export * from './tryExecute.function.js'; export * from './tryExecuteAndNotify.function.js'; export * from './tryXhrRequest.function.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/resources/try-xhr-request.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/resources/try-xhr-request.controller.ts deleted file mode 100644 index 8141e1663c2d..000000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/core/resources/try-xhr-request.controller.ts +++ /dev/null @@ -1,94 +0,0 @@ -import { UmbCancelablePromise } from './cancelable-promise.js'; -import { UmbTryExecuteController } from './try-execute.controller.js'; -import type { XhrRequestOptions } from './types.js'; -import { UmbApiError } from './umb-error.js'; - -export class UmbTryXhrRequestController extends UmbTryExecuteController { - static createXhrRequest(options: XhrRequestOptions): UmbCancelablePromise { - const baseUrl = options.baseUrl || '/umbraco'; - - return new UmbCancelablePromise(async (resolve, reject, onCancel) => { - const xhr = new XMLHttpRequest(); - xhr.open(options.method, `${baseUrl}${options.url}`, true); - - // Set default headers - if (options.token) { - const token = typeof options.token === 'function' ? await options.token() : options.token; - if (token) { - xhr.setRequestHeader('Authorization', `Bearer ${token}`); - } - } - - // Infer Content-Type header based on body type - if (options.body instanceof FormData) { - // Note: 'multipart/form-data' is automatically set by the browser for FormData - } else { - xhr.setRequestHeader('Content-Type', 'application/json'); - } - - // Set custom headers - if (options.headers) { - for (const [key, value] of Object.entries(options.headers)) { - xhr.setRequestHeader(key, value); - } - } - - xhr.upload.onprogress = (event) => { - if (options.onProgress) { - options.onProgress(event); - } - }; - - xhr.onload = () => { - try { - if (xhr.status >= 200 && xhr.status < 300) { - if (options.responseHeader) { - const response = xhr.getResponseHeader(options.responseHeader); - resolve(response as T); - } else { - resolve(JSON.parse(xhr.responseText)); - } - } else { - const error = new UmbApiError(xhr.statusText, xhr.status, xhr, { - title: xhr.statusText, - type: 'ApiError', - status: xhr.status, - }); - reject(error); - } - } catch { - // This most likely happens when the response is not JSON - reject( - new UmbApiError(`Failed to make request: ${xhr.statusText}`, xhr.status, xhr, { - title: xhr.statusText, - type: 'ApiError', - status: xhr.status, - }), - ); - } - }; - - xhr.onerror = () => { - const error = new UmbApiError(xhr.statusText, xhr.status, xhr, { - title: xhr.statusText, - type: 'ApiError', - status: xhr.status, - }); - reject(error); - }; - - if (!onCancel.isCancelled) { - // Handle body based on Content-Type - if (options.body instanceof FormData) { - xhr.send(options.body); - } else { - xhr.send(JSON.stringify(options.body)); - } - } - - onCancel(() => { - xhr.abort(); - }); - }); - } -} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/resources/tryXhrRequest.function.ts b/src/Umbraco.Web.UI.Client/src/packages/core/resources/tryXhrRequest.function.ts index 2084d30d09e3..1651819e2fa0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/resources/tryXhrRequest.function.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/resources/tryXhrRequest.function.ts @@ -1,5 +1,7 @@ +import { UmbTryExecuteController } from './try-execute.controller.js'; +import { UmbCancelablePromise } from './cancelable-promise.js'; +import { UmbApiError } from './umb-error.js'; import type { UmbTryExecuteOptions, XhrRequestOptions } from './types.js'; -import { UmbTryXhrRequestController } from './try-xhr-request.controller.js'; import { OpenAPI } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import type { UmbDataSourceResponse } from '@umbraco-cms/backoffice/repository'; @@ -11,13 +13,101 @@ export async function tryXhrRequest( host: UmbControllerHost, options: XhrRequestOptions & UmbTryExecuteOptions, ): Promise> { - const promise = UmbTryXhrRequestController.createXhrRequest({ + const promise = createXhrRequest({ ...options, baseUrl: OpenAPI.BASE, token: OpenAPI.TOKEN as never, }); - const controller = new UmbTryXhrRequestController(host, promise); + const controller = new UmbTryExecuteController(host, promise); const response = await controller.tryExecute(options); controller.destroy(); return response as UmbDataSourceResponse; } + +function createXhrRequest(options: XhrRequestOptions): UmbCancelablePromise { + const baseUrl = options.baseUrl || '/umbraco'; + + return new UmbCancelablePromise(async (resolve, reject, onCancel) => { + const xhr = new XMLHttpRequest(); + xhr.open(options.method, `${baseUrl}${options.url}`, true); + + // Set default headers + if (options.token) { + const token = typeof options.token === 'function' ? await options.token() : options.token; + if (token) { + xhr.setRequestHeader('Authorization', `Bearer ${token}`); + } + } + + // Infer Content-Type header based on body type + if (options.body instanceof FormData) { + // Note: 'multipart/form-data' is automatically set by the browser for FormData + } else { + xhr.setRequestHeader('Content-Type', 'application/json'); + } + + // Set custom headers + if (options.headers) { + for (const [key, value] of Object.entries(options.headers)) { + xhr.setRequestHeader(key, value); + } + } + + xhr.upload.onprogress = (event) => { + if (options.onProgress) { + options.onProgress(event); + } + }; + + xhr.onload = () => { + try { + if (xhr.status >= 200 && xhr.status < 300) { + if (options.responseHeader) { + const response = xhr.getResponseHeader(options.responseHeader); + resolve(response as T); + } else { + resolve(JSON.parse(xhr.responseText)); + } + } else { + const error = new UmbApiError(xhr.statusText, xhr.status, xhr, { + title: xhr.statusText, + type: 'ApiError', + status: xhr.status, + }); + reject(error); + } + } catch { + // This most likely happens when the response is not JSON + reject( + new UmbApiError(`Failed to make request: ${xhr.statusText}`, xhr.status, xhr, { + title: xhr.statusText, + type: 'ApiError', + status: xhr.status, + }), + ); + } + }; + + xhr.onerror = () => { + const error = new UmbApiError(xhr.statusText, xhr.status, xhr, { + title: xhr.statusText, + type: 'ApiError', + status: xhr.status, + }); + reject(error); + }; + + if (!onCancel.isCancelled) { + // Handle body based on Content-Type + if (options.body instanceof FormData) { + xhr.send(options.body); + } else { + xhr.send(JSON.stringify(options.body)); + } + } + + onCancel(() => { + xhr.abort(); + }); + }); +} From 3c873c3ced54c84280eef67fbfe0ab62c7cf8d40 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Mon, 7 Apr 2025 10:55:19 +0200 Subject: [PATCH 35/45] docs: adds jsdocs --- .../core/resources/api-interceptor.controller.ts | 15 ++++++++++++++- .../extractUmbNotificationColor.function.ts | 6 +++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/resources/api-interceptor.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/resources/api-interceptor.controller.ts index ed305123be15..7d8c2cda8470 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/resources/api-interceptor.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/resources/api-interceptor.controller.ts @@ -6,6 +6,11 @@ import { UMB_AUTH_CONTEXT } from '@umbraco-cms/backoffice/auth'; import type { UmbNotificationColor } from '@umbraco-cms/backoffice/notification'; export class UmbApiInterceptorController extends UmbControllerBase { + /** + * Binds the default interceptors to the client. + * This includes the auth response interceptor, the error interceptor and the umb-notifications interceptor. + * @param {OpenAPIConfig} client The OpenAPI client to add the interceptor to. It can be any client supporting Response and Request interceptors. + */ public bindDefaultInterceptors(client: OpenAPIConfig) { this.addAuthResponseInterceptor(client); this.addUmbNotificationsInterceptor(client); @@ -13,6 +18,8 @@ export class UmbApiInterceptorController extends UmbControllerBase { } /** + * Interceptor which checks responses for 401 errors and lets the UmbAuthContext know the user is timed out. + * @param {OpenAPIConfig} client The OpenAPI client to add the interceptor to. It can be any client supporting Response and Request interceptors. * @internal */ addAuthResponseInterceptor(client: OpenAPIConfig) { @@ -30,6 +37,8 @@ export class UmbApiInterceptorController extends UmbControllerBase { } /** + * Interceptor which checks responses for 500 errors and displays them as a notification if any. + * @param {OpenAPIConfig} client The OpenAPI client to add the interceptor to. It can be any client supporting Response and Request interceptors. * @internal */ addErrorInterceptor(client: OpenAPIConfig) { @@ -73,6 +82,7 @@ export class UmbApiInterceptorController extends UmbControllerBase { /** * Interceptor which checks responses for the umb-notifications header and displays them as a notification if any. Removes the umb-notifications from the headers. + * @param {OpenAPIConfig} client The OpenAPI client to add the interceptor to. It can be any client supporting Response and Request interceptors. * @internal */ addUmbNotificationsInterceptor(client: OpenAPIConfig) { @@ -113,8 +123,11 @@ export class UmbApiInterceptorController extends UmbControllerBase { } async #peekError(headline: string, message: string, details: unknown, color?: UmbNotificationColor) { + // Store the host for usage in the following async context + const host = this._host; + // This late importing is done to avoid circular reference [NL] - (await import('@umbraco-cms/backoffice/notification')).umbPeekError(this, { + (await import('@umbraco-cms/backoffice/notification')).umbPeekError(host, { headline, message, details, diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/resources/extractUmbNotificationColor.function.ts b/src/Umbraco.Web.UI.Client/src/packages/core/resources/extractUmbNotificationColor.function.ts index efeb246e1e07..c787e44e231f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/resources/extractUmbNotificationColor.function.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/resources/extractUmbNotificationColor.function.ts @@ -2,7 +2,11 @@ import type { UmbNotificationColor } from '../notification/types.js'; import { EventMessageTypeModel } from '@umbraco-cms/backoffice/external/backend-api'; /** - * @deprecated Import from `@umbraco-cms/backoffice/resources` instead. + * Extracts the UmbNotificationColor from the EventMessageTypeModel. + * @param {EventMessageTypeModel} type The EventMessageTypeModel to extract the color from. + * @returns {UmbNotificationColor} The corresponding UmbNotificationColor. + * @example + * const color = extractUmbNotificationColor(EventMessageTypeModel.ERROR); // color will be 'danger' */ export function extractUmbNotificationColor(type: EventMessageTypeModel): UmbNotificationColor { switch (type) { From ec17d9aec776073862ba4abd1b247a0f816699e7 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Mon, 7 Apr 2025 10:57:47 +0200 Subject: [PATCH 36/45] docs: adds jsdocs --- .../resources/tryExecuteAndNotify.function.ts | 7 +++++++ .../core/resources/tryXhrRequest.function.ts | 17 +++++++++++++++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/resources/tryExecuteAndNotify.function.ts b/src/Umbraco.Web.UI.Client/src/packages/core/resources/tryExecuteAndNotify.function.ts index d8f83d7fc51a..46f9d3882c84 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/resources/tryExecuteAndNotify.function.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/resources/tryExecuteAndNotify.function.ts @@ -4,6 +4,13 @@ import type { UmbDataSourceResponse } from '@umbraco-cms/backoffice/repository'; import { UmbDeprecation } from '@umbraco-cms/backoffice/utils'; /** + * Make a request and notify the user of any errors. + * This function is a wrapper around the {@link tryExecute} function and will notify the user of any errors. + * It is useful for making requests where you want to handle errors in a consistent way. + * @param {UmbControllerHost} host The host to use for the request. + * @param {Promise} resource The resource to request. + * @returns {Promise>} A promise that resolves with the response data or rejects with an error. + * @template T The type of the response data. * @deprecated Use the {@link tryExecute} function instead and handle the error in the caller. * This function is kept for backwards compatibility and will be removed in a future version. */ diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/resources/tryXhrRequest.function.ts b/src/Umbraco.Web.UI.Client/src/packages/core/resources/tryXhrRequest.function.ts index 1651819e2fa0..68f2f222eb6e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/resources/tryXhrRequest.function.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/resources/tryXhrRequest.function.ts @@ -1,17 +1,23 @@ import { UmbTryExecuteController } from './try-execute.controller.js'; import { UmbCancelablePromise } from './cancelable-promise.js'; import { UmbApiError } from './umb-error.js'; -import type { UmbTryExecuteOptions, XhrRequestOptions } from './types.js'; +import type { XhrRequestOptions } from './types.js'; import { OpenAPI } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import type { UmbDataSourceResponse } from '@umbraco-cms/backoffice/repository'; /** * Make an XHR request. + * This function is a wrapper around XMLHttpRequest to provide a consistent API for making requests. + * It supports cancelable promises, progress events, and custom headers. + * @param {UmbControllerHost} host The host to use for the request. + * @param {XhrRequestOptions} options The options for the request. + * @returns {Promise>} A promise that resolves with the response data or rejects with an error. + * @template T The type of the response data. */ export async function tryXhrRequest( host: UmbControllerHost, - options: XhrRequestOptions & UmbTryExecuteOptions, + options: XhrRequestOptions, ): Promise> { const promise = createXhrRequest({ ...options, @@ -24,6 +30,13 @@ export async function tryXhrRequest( return response as UmbDataSourceResponse; } +/** + * Create an XHR request. + * @param {XhrRequestOptions} options The options for the request. + * @returns {UmbCancelablePromise} A cancelable promise that resolves with the response data or rejects with an error. + * @template T The type of the response data. + * @internal + */ function createXhrRequest(options: XhrRequestOptions): UmbCancelablePromise { const baseUrl = options.baseUrl || '/umbraco'; From 6862fd0100a8af1cdb3bd2bee12d26476534cadb Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Mon, 7 Apr 2025 10:59:34 +0200 Subject: [PATCH 37/45] docs: adds jsdocs --- .../core/resources/tryExecute.function.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/resources/tryExecute.function.ts b/src/Umbraco.Web.UI.Client/src/packages/core/resources/tryExecute.function.ts index 293dd16363ce..ab96539486d5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/resources/tryExecute.function.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/resources/tryExecute.function.ts @@ -3,6 +3,22 @@ import type { UmbTryExecuteOptions } from './types.js'; import type { UmbDataSourceResponse } from '@umbraco-cms/backoffice/repository'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +/** + * Make a request and handle errors. + * @param {UmbControllerHost} host The host to use for the request and where notifications will be shown. + * @param {Promise} promise The promise to execute. + * @param {UmbTryExecuteOptions} opts Options for the request. + * @returns {Promise>} A promise that resolves with the response data or rejects with an error. + * @template T The type of the response data. + * @example + * const { data, error } = await tryExecute(this, myPromise, { + * abortSignal: myAbortSignal, + * disableNotifications: false, + * }); + * if (!error) { + * console.log('Success:', data); + * } + */ export async function tryExecute( host: UmbControllerHost, promise: Promise, From b146ffc619b78102f5e167dcf441a800f2750f8e Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Mon, 7 Apr 2025 11:06:08 +0200 Subject: [PATCH 38/45] feat: returns umb-notifications response without modifying it --- .../resources/api-interceptor.controller.ts | 46 ++++++++----------- 1 file changed, 19 insertions(+), 27 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/resources/api-interceptor.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/resources/api-interceptor.controller.ts index 7d8c2cda8470..aa54c5f841df 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/resources/api-interceptor.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/resources/api-interceptor.controller.ts @@ -87,38 +87,30 @@ export class UmbApiInterceptorController extends UmbControllerBase { */ addUmbNotificationsInterceptor(client: OpenAPIConfig) { client.interceptors.response.use((response) => { + // Check if the response has the umb-notifications header + // If not, we just return the response const umbNotifications = response.headers.get(UMB_NOTIFICATION_HEADER); if (!umbNotifications) return response; - const notifications = JSON.parse(umbNotifications); - if (!isUmbNotifications(notifications)) return response; - - for (const notification of notifications) { - this.#peekError( - notification.category, - notification.message, - null, - extractUmbNotificationColor(notification.type), - ); - } - - const newHeader = new Headers(); - for (const header of response.headers.entries()) { - const [key, value] = header; - if (key !== UMB_NOTIFICATION_HEADER) newHeader.set(key, value); + // Parse the notifications from the header + // If the header is not a valid JSON, we just return the response + try { + const notifications = JSON.parse(umbNotifications); + if (!isUmbNotifications(notifications)) return response; + + for (const notification of notifications) { + this.#peekError( + notification.category, + notification.message, + null, + extractUmbNotificationColor(notification.type), + ); + } + } catch { + // Ignore JSON parse errors } - const newResponse = response.clone(); - newResponse.headers.delete(UMB_NOTIFICATION_HEADER); - return newResponse; - - /*const newResponse = new Response(response.body, { - headers: newHeader, - status: response.status, - statusText: response.statusText, - }); - - return newResponse;*/ + return response; }); } From 9ebe9c5f37f57898be9b0a509439e35186c3bab1 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Mon, 7 Apr 2025 11:18:24 +0200 Subject: [PATCH 39/45] feat: eliminates dependency on generated `ProblemDetails` type --- .../src/apps/app/app-error.element.ts | 8 +++--- .../database/installer-database.element.ts | 12 ++++----- .../error/installer-error.element.ts | 8 +++--- .../src/apps/installer/installer.context.ts | 27 +++++++++---------- .../src/apps/upgrader/upgrader.element.ts | 15 ++++++----- .../item/item-repository.interface.ts | 4 +-- .../src/packages/core/resources/types.ts | 1 + .../core/tree/data/tree-repository-base.ts | 4 +-- .../tree/data/tree-repository.interface.ts | 10 +++---- .../modals/external-login-modal.element.ts | 8 +++--- 10 files changed, 49 insertions(+), 48 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/apps/app/app-error.element.ts b/src/Umbraco.Web.UI.Client/src/apps/app/app-error.element.ts index fc34f7156bab..7d2b06a629e7 100644 --- a/src/Umbraco.Web.UI.Client/src/apps/app/app-error.element.ts +++ b/src/Umbraco.Web.UI.Client/src/apps/app/app-error.element.ts @@ -1,6 +1,6 @@ import { css, html, nothing, customElement, property } from '@umbraco-cms/backoffice/external/lit'; -import type { ProblemDetails } from '@umbraco-cms/backoffice/external/backend-api'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; +import type { UmbProblemDetails } from '@umbraco-cms/backoffice/resources'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; /** @@ -26,7 +26,7 @@ export class UmbAppErrorElement extends UmbLitElement { * The error to display * @attr */ - @property() + @property({ attribute: false }) error?: unknown; /** @@ -133,7 +133,7 @@ export class UmbAppErrorElement extends UmbLitElement { } } - #renderProblemDetails = (problemDetails: ProblemDetails) => html` + #renderProblemDetails = (problemDetails: UmbProblemDetails) => html`

${problemDetails.title}

${problemDetails.detail}

${problemDetails.stack}
@@ -145,7 +145,7 @@ export class UmbAppErrorElement extends UmbLitElement {
${error.stack}
`; - #isProblemDetails(error: unknown): error is ProblemDetails { + #isProblemDetails(error: unknown): error is UmbProblemDetails { return typeof error === 'object' && error !== null && 'detail' in error && 'title' in error; } diff --git a/src/Umbraco.Web.UI.Client/src/apps/installer/database/installer-database.element.ts b/src/Umbraco.Web.UI.Client/src/apps/installer/database/installer-database.element.ts index a9ea13e4d6ae..6ff723350f31 100644 --- a/src/Umbraco.Web.UI.Client/src/apps/installer/database/installer-database.element.ts +++ b/src/Umbraco.Web.UI.Client/src/apps/installer/database/installer-database.element.ts @@ -7,11 +7,10 @@ import { css, html, nothing, customElement, property, query, state } from '@umbr import type { DatabaseInstallRequestModel, DatabaseSettingsPresentationModel, - ProblemDetails, } from '@umbraco-cms/backoffice/external/backend-api'; import { ApiError, InstallService } from '@umbraco-cms/backoffice/external/backend-api'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import { tryExecute } from '@umbraco-cms/backoffice/resources'; +import { tryExecute, UmbApiError, type UmbProblemDetails } from '@umbraco-cms/backoffice/resources'; @customElement('umb-installer-database') export class UmbInstallerDatabaseElement extends UmbLitElement { @@ -167,6 +166,7 @@ export class UmbInstallerDatabaseElement extends UmbLitElement { const { error } = await tryExecute( this, InstallService.postInstallValidateDatabase({ requestBody: databaseDetails }), + { disableNotifications: true }, ); if (error) { @@ -196,13 +196,13 @@ export class UmbInstallerDatabaseElement extends UmbLitElement { this._installerContext.nextStep(); - const { error: _error } = await tryExecute( + const { error } = await tryExecute( this, InstallService.postInstallSetup({ requestBody: this._installerContext.getData() }), + { disableNotifications: true }, ); - const error = _error as ProblemDetails | undefined; if (error) { - this._handleRejected(error); + if (UmbApiError.isUmbApiError(error)) this._handleRejected(error.problemDetails); } else { this._handleFulfilled(); } @@ -215,7 +215,7 @@ export class UmbInstallerDatabaseElement extends UmbLitElement { history.replaceState(null, '', 'section/content'); } - private _handleRejected(e: ProblemDetails) { + private _handleRejected(e: UmbProblemDetails) { this._installerContext?.setInstallStatus(e); } diff --git a/src/Umbraco.Web.UI.Client/src/apps/installer/error/installer-error.element.ts b/src/Umbraco.Web.UI.Client/src/apps/installer/error/installer-error.element.ts index f5fa4e2cec2f..a7aad8e47003 100644 --- a/src/Umbraco.Web.UI.Client/src/apps/installer/error/installer-error.element.ts +++ b/src/Umbraco.Web.UI.Client/src/apps/installer/error/installer-error.element.ts @@ -2,13 +2,13 @@ import type { UmbInstallerContext } from '../installer.context.js'; import { UMB_INSTALLER_CONTEXT } from '../installer.context.js'; import type { CSSResultGroup } from '@umbraco-cms/backoffice/external/lit'; import { css, html, nothing, customElement, state } from '@umbraco-cms/backoffice/external/lit'; -import type { ProblemDetails } from '@umbraco-cms/backoffice/external/backend-api'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; +import type { UmbProblemDetails } from '@umbraco-cms/backoffice/resources'; @customElement('umb-installer-error') export class UmbInstallerErrorElement extends UmbLitElement { @state() - _error?: ProblemDetails; + _error?: UmbProblemDetails; private _installerContext?: UmbInstallerContext; @@ -36,10 +36,10 @@ export class UmbInstallerErrorElement extends UmbLitElement { this._installerContext?.reset(); } - private _renderError(error: ProblemDetails) { + private _renderError(error: UmbProblemDetails) { return html`

Description: ${error.title}

- ${error.errors ? this._renderErrors(error.errors as any) : nothing} + ${error.errors ? this._renderErrors(error.errors) : nothing}

Details:

${error.detail ?? 'Unknown error'}

diff --git a/src/Umbraco.Web.UI.Client/src/apps/installer/installer.context.ts b/src/Umbraco.Web.UI.Client/src/apps/installer/installer.context.ts index 381436fa0904..cf51df93cb0c 100644 --- a/src/Umbraco.Web.UI.Client/src/apps/installer/installer.context.ts +++ b/src/Umbraco.Web.UI.Client/src/apps/installer/installer.context.ts @@ -1,11 +1,7 @@ import type { Observable } from '@umbraco-cms/backoffice/external/rxjs'; -import type { - InstallSettingsResponseModel, - ProblemDetails, - InstallRequestModel, -} from '@umbraco-cms/backoffice/external/backend-api'; +import type { InstallSettingsResponseModel, InstallRequestModel } from '@umbraco-cms/backoffice/external/backend-api'; import { InstallService, TelemetryLevelModel } from '@umbraco-cms/backoffice/external/backend-api'; -import { tryExecute } from '@umbraco-cms/backoffice/resources'; +import { tryExecute, UmbApiError, type UmbProblemDetails } from '@umbraco-cms/backoffice/resources'; import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; import { UmbObjectState, UmbNumberState } from '@umbraco-cms/backoffice/observable-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; @@ -29,7 +25,7 @@ export class UmbInstallerContext extends UmbContextBase(undefined); public readonly settings = this._settings.asObservable(); - private _installStatus = new UmbObjectState(null); + private _installStatus = new UmbObjectState(null); public readonly installStatus = this._installStatus.asObservable(); constructor(host: UmbControllerHost) { @@ -50,10 +46,10 @@ export class UmbInstallerContext extends UmbContextBase)} + * @returns {*} {(Observable)} * @memberof UmbInstallerContext */ - public installStatusChanges(): Observable { + public installStatusChanges(): Observable { return this.installStatus; } @@ -108,10 +104,10 @@ export class UmbInstallerContext extends UmbContextBase extends UmbApi { requestItems: (uniques: string[]) => Promise<{ data?: Array | undefined; - error?: ProblemDetails | undefined; + error?: UmbProblemDetails | undefined; asObservable?: () => Observable>; }>; items: (uniques: string[]) => Promise>>; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/resources/types.ts b/src/Umbraco.Web.UI.Client/src/packages/core/resources/types.ts index 48b03c4eb252..bbdebf1d1e80 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/resources/types.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/resources/types.ts @@ -13,6 +13,7 @@ export interface UmbProblemDetails { type: string; title: string; status: number; + stack?: unknown; detail?: string; instance?: string; operationStatus?: string; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/tree/data/tree-repository-base.ts b/src/Umbraco.Web.UI.Client/src/packages/core/tree/data/tree-repository-base.ts index 18baee61cec4..ef62e28205a3 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/tree/data/tree-repository-base.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/tree/data/tree-repository-base.ts @@ -8,10 +8,10 @@ import type { UmbTreeRootItemsRequestArgs, } from './types.js'; import { UmbRepositoryBase } from '@umbraco-cms/backoffice/repository'; -import type { ProblemDetails } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import type { UmbApi } from '@umbraco-cms/backoffice/extension-api'; import type { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; +import type { UmbProblemDetails } from '@umbraco-cms/backoffice/resources'; /** * Base class for a tree repository. @@ -70,7 +70,7 @@ export abstract class UmbTreeRepositoryBase< * @returns {*} * @memberof UmbTreeRepositoryBase */ - abstract requestTreeRoot(): Promise<{ data?: TreeRootType; error?: ProblemDetails }>; + abstract requestTreeRoot(): Promise<{ data?: TreeRootType; error?: UmbProblemDetails }>; /** * Requests root items of a tree diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/tree/data/tree-repository.interface.ts b/src/Umbraco.Web.UI.Client/src/packages/core/tree/data/tree-repository.interface.ts index 0331e90c77c0..afdb7226b261 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/tree/data/tree-repository.interface.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/tree/data/tree-repository.interface.ts @@ -6,8 +6,8 @@ import type { } from './types.js'; import type { UmbPagedModel } from '@umbraco-cms/backoffice/repository'; import type { Observable } from '@umbraco-cms/backoffice/external/rxjs'; -import type { ProblemDetails } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbApi } from '@umbraco-cms/backoffice/extension-api'; +import type { UmbProblemDetails } from '@umbraco-cms/backoffice/resources'; /** * Interface for a tree repository. @@ -29,7 +29,7 @@ export interface UmbTreeRepository< */ requestTreeRoot: () => Promise<{ data?: TreeRootType; - error?: ProblemDetails; + error?: UmbProblemDetails; }>; /** @@ -39,7 +39,7 @@ export interface UmbTreeRepository< */ requestTreeRootItems: (args: TreeRootItemsRequestArgsType) => Promise<{ data?: UmbPagedModel; - error?: ProblemDetails; + error?: UmbProblemDetails; asObservable?: () => Observable; }>; @@ -50,7 +50,7 @@ export interface UmbTreeRepository< */ requestTreeItemsOf: (args: TreeChildrenOfRequestArgsType) => Promise<{ data?: UmbPagedModel; - error?: ProblemDetails; + error?: UmbProblemDetails; asObservable?: () => Observable; }>; @@ -61,7 +61,7 @@ export interface UmbTreeRepository< */ requestTreeItemAncestors: ( args: TreeAncestorsOfRequestArgsType, - ) => Promise<{ data?: TreeItemType[]; error?: ProblemDetails; asObservable?: () => Observable }>; + ) => Promise<{ data?: TreeItemType[]; error?: UmbProblemDetails; asObservable?: () => Observable }>; /** * Returns an observable of the root items of the tree. diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/external-login/modals/external-login-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/external-login/modals/external-login-modal.element.ts index 2167dfe016dc..daef3e32a4f6 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/external-login/modals/external-login-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/external-login/modals/external-login-modal.element.ts @@ -8,7 +8,7 @@ import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registr import { mergeObservables } from '@umbraco-cms/backoffice/observable-api'; import { UMB_AUTH_CONTEXT } from '@umbraco-cms/backoffice/auth'; import { UMB_NOTIFICATION_CONTEXT } from '@umbraco-cms/backoffice/notification'; -import type { ProblemDetails } from '@umbraco-cms/backoffice/external/backend-api'; +import { UmbApiError } from '@umbraco-cms/backoffice/resources'; type UmbExternalLoginProviderOption = UmbCurrentUserExternalLoginProviderModel & { displayName: string; @@ -204,10 +204,10 @@ export class UmbCurrentUserExternalLoginModalElement extends UmbLitElement { await authContext.unlinkLogin(item.providerSchemeName, item.providerKey); } catch (error) { let message = this.localize.term('errors_receivedErrorFromServer'); - if (error instanceof Error) { + if (UmbApiError.isUmbApiError(error)) { + message = error.problemDetails.detail ?? message; + } else if (error instanceof Error) { message = error.message; - } else if (typeof error === 'object' && (error as ProblemDetails).title) { - message = (error as ProblemDetails).title ?? message; } console.error('[External Login] Error unlinking provider: ', error); this.#notificationContext?.peek('danger', { From 2d1c739058548a9813a96354c5fc4531b69951d0 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Mon, 7 Apr 2025 11:35:21 +0200 Subject: [PATCH 40/45] feat: eliminates dependence on generated `ApiError` type --- .../database/installer-database.element.ts | 4 +- ...-entry-detail.local-storage.data-source.ts | 125 ++---------------- .../resources/apiTypeValidators.function.ts | 16 ++- .../src/packages/core/resources/umb-error.ts | 15 ++- .../telemetry/dashboard-telemetry.element.ts | 7 +- 5 files changed, 35 insertions(+), 132 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/apps/installer/database/installer-database.element.ts b/src/Umbraco.Web.UI.Client/src/apps/installer/database/installer-database.element.ts index 6ff723350f31..59fc0d0131c5 100644 --- a/src/Umbraco.Web.UI.Client/src/apps/installer/database/installer-database.element.ts +++ b/src/Umbraco.Web.UI.Client/src/apps/installer/database/installer-database.element.ts @@ -8,7 +8,7 @@ import type { DatabaseInstallRequestModel, DatabaseSettingsPresentationModel, } from '@umbraco-cms/backoffice/external/backend-api'; -import { ApiError, InstallService } from '@umbraco-cms/backoffice/external/backend-api'; +import { InstallService } from '@umbraco-cms/backoffice/external/backend-api'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { tryExecute, UmbApiError, type UmbProblemDetails } from '@umbraco-cms/backoffice/resources'; @@ -171,7 +171,7 @@ export class UmbInstallerDatabaseElement extends UmbLitElement { if (error) { this._validationErrorMessage = `The server could not validate the database connection. Details: ${ - error instanceof ApiError ? (error.body as any).detail : error.message + UmbApiError.isUmbApiError(error) ? error.problemDetails.detail : error.message }`; this._installButton.state = 'failed'; return; diff --git a/src/Umbraco.Web.UI.Client/src/packages/clipboard/clipboard-entry/detail/clipboard-entry-detail.local-storage.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/clipboard/clipboard-entry/detail/clipboard-entry-detail.local-storage.data-source.ts index 43f21e714a67..e661a9df8298 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/clipboard/clipboard-entry/detail/clipboard-entry-detail.local-storage.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/clipboard/clipboard-entry/detail/clipboard-entry-detail.local-storage.data-source.ts @@ -7,11 +7,8 @@ import type { UmbDataSourceResponse, UmbDetailDataSource, } from '@umbraco-cms/backoffice/repository'; -import { ApiError } from '@umbraco-cms/backoffice/external/backend-api'; import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; - -// TODO: these are temp solutions to comply to the ApiError interface -const localstorageFakeUrl = 'localstorage'; +import { UmbError } from '@umbraco-cms/backoffice/resources'; /** * Manage clipboard entries in local storage @@ -56,20 +53,7 @@ export class UmbClipboardEntryDetailLocalStorageDataSource async create(model: UmbClipboardEntryDetailModel): Promise> { if (!model) { return { - error: new ApiError( - { - method: 'POST', - url: localstorageFakeUrl, - }, - { - ok: false, - status: 400, - statusText: 'Bad Request', - url: localstorageFakeUrl, - body: {}, - }, - 'Clipboard entry is missing', - ), + error: new UmbError('Clipboard entry is missing'), }; } @@ -78,20 +62,7 @@ export class UmbClipboardEntryDetailLocalStorageDataSource if (entry) { return { - error: new ApiError( - { - method: 'POST', - url: localstorageFakeUrl, - }, - { - ok: false, - status: 400, - statusText: 'Bad Request', - url: localstorageFakeUrl, - body: {}, - }, - 'Clipboard entry already exists', - ), + error: new UmbError('Clipboard entry already exists'), }; } @@ -117,20 +88,7 @@ export class UmbClipboardEntryDetailLocalStorageDataSource async read(unique: string): Promise> { if (!unique) { return { - error: new ApiError( - { - method: 'GET', - url: localstorageFakeUrl, - }, - { - ok: false, - status: 400, - statusText: 'Bad Request', - url: localstorageFakeUrl, - body: {}, - }, - 'Unique is missing', - ), + error: new UmbError('Unique is missing'), }; } @@ -139,20 +97,7 @@ export class UmbClipboardEntryDetailLocalStorageDataSource if (!entry) { return { - error: new ApiError( - { - method: 'GET', - url: localstorageFakeUrl, - }, - { - ok: false, - status: 404, - statusText: 'Not Found', - url: localstorageFakeUrl, - body: {}, - }, - 'Entry not found', - ), + error: new UmbError('Entry not found'), }; } @@ -168,20 +113,7 @@ export class UmbClipboardEntryDetailLocalStorageDataSource async update(model: UmbClipboardEntryDetailModel): Promise> { if (!model) { return { - error: new ApiError( - { - method: 'PUT', - url: localstorageFakeUrl, - }, - { - ok: false, - status: 400, - statusText: 'Bad Request', - url: localstorageFakeUrl, - body: {}, - }, - 'Clipboard entry is missing', - ), + error: new UmbError('Clipboard entry is missing'), }; } @@ -189,20 +121,7 @@ export class UmbClipboardEntryDetailLocalStorageDataSource const entry = await this.#localStorageManager.getEntry(model.unique); if (!entry) { return { - error: new ApiError( - { - method: 'GET', - url: localstorageFakeUrl, - }, - { - ok: false, - status: 404, - statusText: 'Not Found', - url: localstorageFakeUrl, - body: {}, - }, - 'Entry not found', - ), + error: new UmbError('Entry not found'), }; } @@ -234,20 +153,7 @@ export class UmbClipboardEntryDetailLocalStorageDataSource async delete(unique: string): Promise { if (!unique) { return { - error: new ApiError( - { - method: 'DELETE', - url: localstorageFakeUrl, - }, - { - ok: false, - status: 400, - statusText: 'Bad Request', - url: localstorageFakeUrl, - body: {}, - }, - 'Unique is missing', - ), + error: new UmbError('Unique is missing'), }; } @@ -256,20 +162,7 @@ export class UmbClipboardEntryDetailLocalStorageDataSource if (!entry) { return { - error: new ApiError( - { - method: 'GET', - url: localstorageFakeUrl, - }, - { - ok: false, - status: 404, - statusText: 'Not Found', - url: localstorageFakeUrl, - body: {}, - }, - 'Entry not found', - ), + error: new UmbError('Entry not found'), }; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/resources/apiTypeValidators.function.ts b/src/Umbraco.Web.UI.Client/src/packages/core/resources/apiTypeValidators.function.ts index d551dbf4f45d..4668d8c1421c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/resources/apiTypeValidators.function.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/resources/apiTypeValidators.function.ts @@ -1,23 +1,27 @@ -import type { ApiError, CancelError } from '@umbraco-cms/backoffice/external/backend-api'; - /** * Checks if the given error is an instance of ApiError. + * @param {*} error The error to check + * @returns {boolean} True if the error is an instance of ApiError, false otherwise * @deprecated Use {UmbApiError.isUmbApiError} instead and map your object to {UmbApiError} if needed. */ -export function isApiError(error: unknown): error is ApiError { - return (error as ApiError).name === 'ApiError'; +export function isApiError(error: unknown): error is { body?: string; status?: number; request?: unknown } { + return typeof error === 'object' && error !== null && 'body' in error && 'status' in error && 'request' in error; } /** * Checks if the given error is an instance of CancelError. + * @param {*} error The error to check + * @returns {boolean} True if the error is an instance of CancelError, false otherwise * @deprecated Use {UmbApiCancelError.isUmbApiCancelError}` instead and map your object to {UmbApiCancelError} if needed. */ -export function isCancelError(error: unknown): error is CancelError { - return (error as CancelError).name === 'CancelError'; +export function isCancelError(error: unknown): error is Error { + return error instanceof Error && 'name' in error && error.name === 'CancelError'; } /** * Checks if the given promise is cancelable, i.e. if it has a cancel method. + * @param {*} promise The promise to check + * @returns {boolean} True if the promise is cancelable, false otherwise */ export function isCancelablePromise(promise: unknown): promise is Promise & { cancel: () => void } { return typeof (promise as Promise & { cancel: () => void }).cancel === 'function'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/resources/umb-error.ts b/src/Umbraco.Web.UI.Client/src/packages/core/resources/umb-error.ts index 63fbf6d13029..0dd787b67f3a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/resources/umb-error.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/resources/umb-error.ts @@ -1,4 +1,3 @@ -import type { ApiError, CancelError } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbProblemDetails } from './types.js'; export class UmbError extends Error { @@ -17,9 +16,12 @@ export class UmbCancelError extends UmbError { } /** + * Transforms a CancelError into an UmbCancelError. + * @param {*} error The CancelError to transform. + * @returns {UmbCancelError} The transformed UmbCancelError. * @deprecated Use `UmbCancelError.isUmbCancelError` instead and map your object to `UmbCancelError` if needed. */ - public static fromLegacyCancelError(error: CancelError): UmbCancelError { + public static fromLegacyCancelError(error: Error): UmbCancelError { return new UmbCancelError(error.message); } } @@ -45,9 +47,12 @@ export class UmbApiError extends UmbError { } /** + * Transforms an ApiError into an UmbApiError. + * @param {*} error The ApiError to transform. + * @returns {UmbApiError} The transformed UmbApiError. * @deprecated Use `UmbCancelError.isUmbApiError` instead and map your object to `UmbApiError` if needed. */ - public static fromLegacyApiError(error: ApiError): UmbApiError { + public static fromLegacyApiError(error: Error & { body?: string; status?: number; request?: unknown }): UmbApiError { // ApiError - body could hold a ProblemDetails from the server let problemDetails: UmbProblemDetails | null = null; if (typeof error.body !== 'undefined' && !!error.body) { @@ -59,9 +64,9 @@ export class UmbApiError extends UmbError { } return new UmbApiError( error.message, - error.status, + error.status ?? 0, error.request, - problemDetails ?? { title: error.message, type: 'ApiError', status: error.status }, + problemDetails ?? { title: error.message, type: 'ApiError', status: error.status ?? 0 }, ); } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/telemetry/dashboard-telemetry.element.ts b/src/Umbraco.Web.UI.Client/src/packages/telemetry/dashboard-telemetry.element.ts index 6b5a33ea4a86..2b16da0871b3 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/telemetry/dashboard-telemetry.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/telemetry/dashboard-telemetry.element.ts @@ -1,9 +1,9 @@ import { css, html, customElement, state, unsafeHTML } from '@umbraco-cms/backoffice/external/lit'; import type { UUIButtonState } from '@umbraco-cms/backoffice/external/uui'; import type { TelemetryResponseModel } from '@umbraco-cms/backoffice/external/backend-api'; -import { TelemetryLevelModel, TelemetryService, ApiError } from '@umbraco-cms/backoffice/external/backend-api'; +import { TelemetryLevelModel, TelemetryService } from '@umbraco-cms/backoffice/external/backend-api'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import { tryExecute } from '@umbraco-cms/backoffice/resources'; +import { tryExecute, UmbApiError } from '@umbraco-cms/backoffice/resources'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; @customElement('umb-dashboard-telemetry') @@ -47,7 +47,8 @@ export class UmbDashboardTelemetryElement extends UmbLitElement { if (error) { this._buttonState = 'failed'; - this._errorMessage = error instanceof ApiError ? (error.body as any).detail : error.message; + this._errorMessage = + (UmbApiError.isUmbApiError(error) ? error.problemDetails.detail : error.message) ?? 'Unknown error'; return; } From f928ee513f1bc6c05bfdf48315aed7b750bfdeb6 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Mon, 7 Apr 2025 11:41:16 +0200 Subject: [PATCH 41/45] feat: eliminates dependence on generated `CancelError` type --- .../src/packages/core/resources/apiTypeValidators.function.ts | 2 +- .../src/packages/core/resources/resource.controller.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/resources/apiTypeValidators.function.ts b/src/Umbraco.Web.UI.Client/src/packages/core/resources/apiTypeValidators.function.ts index 4668d8c1421c..94a003fee462 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/resources/apiTypeValidators.function.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/resources/apiTypeValidators.function.ts @@ -15,7 +15,7 @@ export function isApiError(error: unknown): error is { body?: string; status?: n * @deprecated Use {UmbApiCancelError.isUmbApiCancelError}` instead and map your object to {UmbApiCancelError} if needed. */ export function isCancelError(error: unknown): error is Error { - return error instanceof Error && 'name' in error && error.name === 'CancelError'; + return error instanceof Error && (error.name === 'CancelError' || (error as Error).message === 'Request aborted'); } /** diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/resources/resource.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/resources/resource.controller.ts index 9de099ed4171..c2946ca358b8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/resources/resource.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/resources/resource.controller.ts @@ -27,7 +27,7 @@ export class UmbResourceController extends UmbControllerBase { */ mapToUmbError(error: unknown): UmbApiError | UmbCancelError | UmbError { if (isApiError(error)) { - return UmbApiError.fromLegacyApiError(error); + return UmbApiError.fromLegacyApiError(error as any); } else if (isCancelError(error)) { return UmbCancelError.fromLegacyCancelError(error); } else if (UmbCancelError.isUmbCancelError(error)) { From 6d1c08286f86925491c8d31267f8ebf00cd99cc6 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Mon, 7 Apr 2025 12:00:02 +0200 Subject: [PATCH 42/45] fix: removes dependency on CancelablePromise --- .../src/packages/core/resources/resource.controller.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/resources/resource.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/resources/resource.controller.ts index c2946ca358b8..7b22a4a41c76 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/resources/resource.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/resources/resource.controller.ts @@ -4,14 +4,13 @@ import type { UmbCancelablePromise } from './cancelable-promise.js'; import { UmbApiError, UmbCancelError, UmbError } from './umb-error.js'; import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import type { CancelablePromise } from '@umbraco-cms/backoffice/external/backend-api'; export class UmbResourceController extends UmbControllerBase { /** * The promise that is being executed. * @protected */ - protected _promise: UmbCancelablePromise | CancelablePromise | Promise; + protected _promise: UmbCancelablePromise | Promise; constructor(host: UmbControllerHost, promise: Promise, alias?: string) { super(host, alias); From 50a228ab5241f805e4f64fe8efea189139050377 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Tue, 8 Apr 2025 08:43:55 +0200 Subject: [PATCH 43/45] fix: resolves an unintended circular reference based on files being moved around introduced in #18939 --- .../src/packages/core/notification/index.ts | 2 - .../isUmbNotifications.function.ts | 38 ------------------- 2 files changed, 40 deletions(-) delete mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/notification/isUmbNotifications.function.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/notification/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/notification/index.ts index 526e0028f401..40980d113511 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/notification/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/notification/index.ts @@ -1,7 +1,5 @@ import './layouts/default/index.js'; export * from './controllers/peek-error/index.js'; -export * from '../resources/extractUmbNotificationColor.function.js'; -export * from './isUmbNotifications.function.js'; export * from './notification-handler.js'; export * from './notification.context.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/notification/isUmbNotifications.function.ts b/src/Umbraco.Web.UI.Client/src/packages/core/notification/isUmbNotifications.function.ts deleted file mode 100644 index ab861df6c2e4..000000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/core/notification/isUmbNotifications.function.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { EventMessageTypeModel } from '@umbraco-cms/backoffice/external/backend-api'; - -/** - * @deprecated Import from `@umbraco-cms/backoffice/resources` instead. - */ -function objectIsUmbNotification(notification: unknown): notification is UmbNotificationsEventModel { - if (typeof notification !== 'object' || notification === null) { - return false; - } - const object = notification as UmbNotificationsEventModel; - return ( - typeof object.category === 'string' && - typeof object.message === 'string' && - typeof object.type === 'string' && - Object.values(EventMessageTypeModel).includes(object.type) - ); -} - -/** - * @deprecated Import from `@umbraco-cms/backoffice/resources` instead. - */ -export interface UmbNotificationsEventModel { - category: string; - message: string; - type: EventMessageTypeModel; -} - -/** - * @deprecated Import from `@umbraco-cms/backoffice/resources` instead. - */ -export function isUmbNotifications(notifications: Array): notifications is Array { - return notifications.every(objectIsUmbNotification); -} - -/** - * @deprecated Import from `@umbraco-cms/backoffice/resources` instead. - */ -export const UMB_NOTIFICATION_HEADER = 'umb-notifications'; From beff453f1f64843950a10e2cc132d57a0a30b2f2 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Tue, 8 Apr 2025 08:44:27 +0200 Subject: [PATCH 44/45] fix: improves error handling to return early if interceptor does not catch a 500 --- .../resources/api-interceptor.controller.ts | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/resources/api-interceptor.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/resources/api-interceptor.controller.ts index aa54c5f841df..757de02d1c69 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/resources/api-interceptor.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/resources/api-interceptor.controller.ts @@ -45,20 +45,21 @@ export class UmbApiInterceptorController extends UmbControllerBase { client.interceptors.response.use(async (response) => { if (response.ok) return response; - // Clones the response to read the body - const origResponse = response.clone(); - const error = await origResponse.json(); - if (!error) return response; - - // If the error is not an UmbError, we check if it is a problem details object - // Check if the error is a problem details object - if (!('type' in error) || !('title' in error) || !('status' in error)) { - // If not, we just return the response - return response; - } - // Handle 500 errors - we need to show a notification - if (origResponse.status === 500) { + if (response.status === 500) { + // Clones the response to read the body + const origResponse = response.clone(); + const error = await origResponse.json(); + + if (!error) return response; + + // If the error is not an UmbError, we check if it is a problem details object + // Check if the error is a problem details object + if (!('type' in error) || !('title' in error) || !('status' in error)) { + // If not, we just return the response + return response; + } + let headline = error.title ?? error.name ?? 'Server Error'; let message = 'A fatal server error occurred. If this continues, please reach out to your administrator.'; From f30335f70d817c8a9a09157648b5a85cb8660a13 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Tue, 8 Apr 2025 08:52:33 +0200 Subject: [PATCH 45/45] fix: adds try/catch around json parsing --- .../resources/api-interceptor.controller.ts | 57 ++++++++++--------- 1 file changed, 31 insertions(+), 26 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/resources/api-interceptor.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/resources/api-interceptor.controller.ts index 757de02d1c69..b4043cd5ea16 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/resources/api-interceptor.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/resources/api-interceptor.controller.ts @@ -47,33 +47,38 @@ export class UmbApiInterceptorController extends UmbControllerBase { // Handle 500 errors - we need to show a notification if (response.status === 500) { - // Clones the response to read the body - const origResponse = response.clone(); - const error = await origResponse.json(); - - if (!error) return response; - - // If the error is not an UmbError, we check if it is a problem details object - // Check if the error is a problem details object - if (!('type' in error) || !('title' in error) || !('status' in error)) { - // If not, we just return the response - return response; + try { + // Clones the response to read the body + const origResponse = response.clone(); + const error = await origResponse.json(); + + if (!error) return response; + + // If the error is not an UmbError, we check if it is a problem details object + // Check if the error is a problem details object + if (!('type' in error) || !('title' in error) || !('status' in error)) { + // If not, we just return the response + return response; + } + + let headline = error.title ?? error.name ?? 'Server Error'; + let message = 'A fatal server error occurred. If this continues, please reach out to your administrator.'; + + // Special handling for ObjectCacheAppCache corruption errors, which we are investigating + if ( + error.detail?.includes('ObjectCacheAppCache') || + error.detail?.includes('Umbraco.Cms.Infrastructure.Scoping.Scope.DisposeLastScope()') + ) { + headline = 'Please restart the server'; + message = + 'The Umbraco object cache is corrupt, but your action may still have been executed. Please restart the server to reset the cache. This is a work in progress.'; + } + + this.#peekError(headline, message, error.errors ?? error.detail); + } catch (e) { + // Ignore JSON parse error + console.error('[Interceptor] Caught a 500 Error, but failed parsing error body (expected JSON)', e); } - - let headline = error.title ?? error.name ?? 'Server Error'; - let message = 'A fatal server error occurred. If this continues, please reach out to your administrator.'; - - // Special handling for ObjectCacheAppCache corruption errors, which we are investigating - if ( - error.detail?.includes('ObjectCacheAppCache') || - error.detail?.includes('Umbraco.Cms.Infrastructure.Scoping.Scope.DisposeLastScope()') - ) { - headline = 'Please restart the server'; - message = - 'The Umbraco object cache is corrupt, but your action may still have been executed. Please restart the server to reset the cache. This is a work in progress.'; - } - - this.#peekError(headline, message, error.errors ?? error.detail); } // Return original response