From e89e18f5bae5903b55faafc6389e650c0c0e81db Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 12 Jun 2025 12:59:11 +0200 Subject: [PATCH] V16: Item and Detail Base Repository should use correct typings for return types (#19447) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: add a catcher to most `asPromise` for stores to prevent cascading errors * fix: remove conditional instances - they should be able to be undefined * fix: check for missing store and extract UmbProblemDetails * fix: only append data if no error * fix: adds error handling to missing stores and to extract the ProblemDetails object * revert commit * fix: ignore errors completely instead of unsetting stores * revert commit * chore: cleanup imports * fix: do not unset store * stop observation in a proper way * stop observation of for document-user-permissions * check for manager twice * save action * save action optional * fix: ensure the right types are used for base stores --------- Co-authored-by: Niels Lyngsø --- .../content-type-structure-manager.class.ts | 2 +- .../detail/detail-repository-base.ts | 4 +-- .../read/read-detail-repository.interface.ts | 2 +- .../repository/item/item-repository-base.ts | 14 +++----- .../item/item-repository.interface.ts | 8 ++--- .../repository/repository-items.manager.ts | 3 +- .../src/packages/core/repository/types.ts | 9 +++-- .../core/tree/data/tree-repository-base.ts | 33 +++++++------------ .../tree/data/tree-repository.interface.ts | 32 ++++++++---------- 9 files changed, 45 insertions(+), 62 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/content/content-type/structure/content-type-structure-manager.class.ts b/src/Umbraco.Web.UI.Client/src/packages/content/content-type/structure/content-type-structure-manager.class.ts index 1a71834059..eed0caeedb 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/content/content-type/structure/content-type-structure-manager.class.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/content/content-type/structure/content-type-structure-manager.class.ts @@ -175,7 +175,7 @@ export class UmbContentTypeStructureManager< * @param {string} unique - The unique of the ContentType to load. * @returns {Promise} - Promise resolved */ - public async loadType(unique: string): Promise> { + public async loadType(unique: string): Promise> { if (this.#ownerContentTypeUnique === unique) { // Its the same, but we do not know if its done loading jet, so we will wait for the load promise to finish. [NL] await this.#init; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/repository/detail/detail-repository-base.ts b/src/Umbraco.Web.UI.Client/src/packages/core/repository/detail/detail-repository-base.ts index 418d95f0f4..af37a4fa1b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/repository/detail/detail-repository-base.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/repository/detail/detail-repository-base.ts @@ -60,7 +60,7 @@ export abstract class UmbDetailRepositoryBase< * @returns {*} * @memberof UmbDetailRepositoryBase */ - async requestByUnique(unique: string): Promise> { + async requestByUnique(unique: string): Promise> { if (!unique) throw new Error('Unique is missing'); await this.#init; @@ -73,7 +73,7 @@ export abstract class UmbDetailRepositoryBase< return { data, error, - asObservable: () => this.#detailStore!.byUnique(unique), + asObservable: () => this.#detailStore?.byUnique(unique), }; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/repository/detail/read/read-detail-repository.interface.ts b/src/Umbraco.Web.UI.Client/src/packages/core/repository/detail/read/read-detail-repository.interface.ts index 702dfb9a1f..a19f530b19 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/repository/detail/read/read-detail-repository.interface.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/repository/detail/read/read-detail-repository.interface.ts @@ -3,6 +3,6 @@ import type { UmbApi } from '@umbraco-cms/backoffice/extension-api'; import type { Observable } from '@umbraco-cms/backoffice/external/rxjs'; export interface UmbReadDetailRepository extends UmbApi { - requestByUnique(unique: string): Promise>; + requestByUnique(unique: string): Promise>; byUnique(unique: string): Promise>; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/repository/item/item-repository-base.ts b/src/Umbraco.Web.UI.Client/src/packages/core/repository/item/item-repository-base.ts index 1f502778cd..15134e562e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/repository/item/item-repository-base.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/repository/item/item-repository-base.ts @@ -41,22 +41,18 @@ export class UmbItemRepositoryBase try { await this._init; } catch { - return {}; + return { + asObservable: () => undefined, + }; } const { data, error } = await this.#itemSource.getItems(uniques); - if (!this._itemStore) { - // If store is gone, then we are most likely in a disassembled state. - return {}; - } - if (data) { - this._itemStore.appendItems(data); + this._itemStore?.appendItems(data); } - // TODO: Fix the type of error, it should be UmbApiError, but currently it is any. - return { data, error: error as any, asObservable: () => this._itemStore!.items(uniques) }; + return { data, error, asObservable: () => this._itemStore?.items(uniques) }; } /** diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/repository/item/item-repository.interface.ts b/src/Umbraco.Web.UI.Client/src/packages/core/repository/item/item-repository.interface.ts index 1c4bcfe070..71e25909a2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/repository/item/item-repository.interface.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/repository/item/item-repository.interface.ts @@ -1,12 +1,8 @@ +import type { UmbRepositoryResponseWithAsObservable } from '../types.js'; import type { Observable } from '@umbraco-cms/backoffice/external/rxjs'; import type { UmbApi } from '@umbraco-cms/backoffice/extension-api'; -import type { UmbProblemDetails } from '@umbraco-cms/backoffice/resources'; export interface UmbItemRepository extends UmbApi { - requestItems: (uniques: string[]) => Promise<{ - data?: Array | undefined; - error?: UmbProblemDetails | undefined; - asObservable?: () => Observable>; - }>; + requestItems: (uniques: string[]) => Promise>; items: (uniques: string[]) => Promise> | undefined>; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/repository/repository-items.manager.ts b/src/Umbraco.Web.UI.Client/src/packages/core/repository/repository-items.manager.ts index d5fac8fabb..bc3536a3e9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/repository/repository-items.manager.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/repository/repository-items.manager.ts @@ -250,7 +250,8 @@ export class UmbRepositoryItemsManager exte } } - #sortByUniques(data: Array): Array { + #sortByUniques(data?: Array): Array { + if (!data) return []; const uniques = this.getUniques(); return [...data].sort((a, b) => { const aIndex = uniques.indexOf(this.#getUnique(a) ?? ''); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/repository/types.ts b/src/Umbraco.Web.UI.Client/src/packages/core/repository/types.ts index c75d422538..32d7a55020 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/repository/types.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/repository/types.ts @@ -11,8 +11,13 @@ export interface UmbRepositoryResponse extends UmbDataSourceResponse {} // eslint-disable-next-line @typescript-eslint/no-empty-object-type export interface UmbRepositoryErrorResponse extends UmbDataSourceErrorResponse {} -export interface UmbRepositoryResponseWithAsObservable extends UmbRepositoryResponse { - asObservable: () => Observable; +/** + * Interface for a repository that can return a paged model. + * @template T - The type of items in the paged model. + * @template T$ - The type of items returned by the asObservable method, defaults to T. You should only use this if you want to return a different type from the asObservable method. + */ +export interface UmbRepositoryResponseWithAsObservable extends UmbRepositoryResponse { + asObservable: () => Observable | undefined; } export type * from './data-mapper/mapping/types.js'; 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 e9b4b8ba58..84d8fac131 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 @@ -7,11 +7,10 @@ import type { UmbTreeChildrenOfRequestArgs, UmbTreeRootItemsRequestArgs, } from './types.js'; -import { UmbRepositoryBase } from '@umbraco-cms/backoffice/repository'; +import { UmbRepositoryBase, type UmbRepositoryResponse } from '@umbraco-cms/backoffice/repository'; 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'; import { of } from '@umbraco-cms/backoffice/external/rxjs'; /** @@ -74,7 +73,7 @@ export abstract class UmbTreeRepositoryBase< * @returns {*} * @memberof UmbTreeRepositoryBase */ - abstract requestTreeRoot(): Promise<{ data?: TreeRootType; error?: UmbProblemDetails }>; + abstract requestTreeRoot(): Promise>; /** * Requests root items of a tree @@ -89,15 +88,16 @@ export abstract class UmbTreeRepositoryBase< if (!this._treeStore) { // If the tree store is not available, then we most likely are in a destructed setting. - return {}; + return { + asObservable: () => undefined, + }; } if (data) { this._treeStore.appendItems(data.items); } - // TODO: Fix the type of error, it should be UmbApiError, but currently it is any. - return { data, error: error as any, asObservable: () => this._treeStore!.rootItems }; + return { data, error, asObservable: () => this._treeStore?.rootItems }; } /** @@ -117,15 +117,16 @@ export abstract class UmbTreeRepositoryBase< if (!this._treeStore) { // If the tree store is not available, then we most likely are in a destructed setting. - return {}; + return { + asObservable: () => undefined, + }; } if (data) { this._treeStore.appendItems(data.items); } - // TODO: Fix the type of error, it should be UmbApiError, but currently it is any. - return { data, error: error as any, asObservable: () => this._treeStore!.childrenOf(args.parent.unique) }; + return { data, error, asObservable: () => this._treeStore?.childrenOf(args.parent.unique) }; } /** @@ -153,12 +154,7 @@ export abstract class UmbTreeRepositoryBase< async rootTreeItems() { await this._init; - if (!this._treeStore) { - // If the tree store is not available, then we most likely are in a destructed setting. - return of([]); - } - - return this._treeStore.rootItems; + return this._treeStore?.rootItems ?? of([]); } /** @@ -171,11 +167,6 @@ export abstract class UmbTreeRepositoryBase< if (parentUnique === undefined) throw new Error('Parent unique is missing'); await this._init; - if (!this._treeStore) { - // If the tree store is not available, then we most likely are in a destructed setting. - return of([]); - } - - return this._treeStore.childrenOf(parentUnique); + return this._treeStore?.childrenOf(parentUnique) ?? of([]); } } 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 afdb7226b2..221db148ba 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 @@ -4,10 +4,13 @@ import type { UmbTreeAncestorsOfRequestArgs, UmbTreeRootItemsRequestArgs, } from './types.js'; -import type { UmbPagedModel } from '@umbraco-cms/backoffice/repository'; +import type { + UmbPagedModel, + UmbRepositoryResponse, + UmbRepositoryResponseWithAsObservable, +} from '@umbraco-cms/backoffice/repository'; import type { Observable } from '@umbraco-cms/backoffice/external/rxjs'; import type { UmbApi } from '@umbraco-cms/backoffice/extension-api'; -import type { UmbProblemDetails } from '@umbraco-cms/backoffice/resources'; /** * Interface for a tree repository. @@ -27,41 +30,32 @@ export interface UmbTreeRepository< * Requests the root of the tree. * @memberof UmbTreeRepository */ - requestTreeRoot: () => Promise<{ - data?: TreeRootType; - error?: UmbProblemDetails; - }>; + requestTreeRoot: () => Promise>; /** * Requests the root items of the tree. * @param {UmbTreeRootItemsRequestArgs} args * @memberof UmbTreeRepository */ - requestTreeRootItems: (args: TreeRootItemsRequestArgsType) => Promise<{ - data?: UmbPagedModel; - error?: UmbProblemDetails; - asObservable?: () => Observable; - }>; + requestTreeRootItems: ( + args: TreeRootItemsRequestArgsType, + ) => Promise, TreeItemType[]>>; /** * Requests the children of the given parent item. * @param {UmbTreeChildrenOfRequestArgs} args * @memberof UmbTreeRepository */ - requestTreeItemsOf: (args: TreeChildrenOfRequestArgsType) => Promise<{ - data?: UmbPagedModel; - error?: UmbProblemDetails; - asObservable?: () => Observable; - }>; + requestTreeItemsOf: ( + args: TreeChildrenOfRequestArgsType, + ) => Promise, TreeItemType[]>>; /** * Requests the ancestors of the given item. * @param {UmbTreeAncestorsOfRequestArgs} args * @memberof UmbTreeRepository */ - requestTreeItemAncestors: ( - args: TreeAncestorsOfRequestArgsType, - ) => Promise<{ data?: TreeItemType[]; error?: UmbProblemDetails; asObservable?: () => Observable }>; + requestTreeItemAncestors: (args: TreeAncestorsOfRequestArgsType) => Promise>; /** * Returns an observable of the root items of the tree.