split item into its own module

This commit is contained in:
Mads Rasmussen
2023-11-13 19:25:36 +01:00
parent f5772a8fc9
commit 3ba51bac02
8 changed files with 186 additions and 159 deletions

View File

@@ -1,36 +0,0 @@
import { DataTypeItemResponseModel } from '@umbraco-cms/backoffice/backend-api';
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api';
import { UmbItemStore, UmbStoreBase } from '@umbraco-cms/backoffice/store';
import { UmbArrayState } from '@umbraco-cms/backoffice/observable-api';
/**
* @export
* @class UmbDataTypeItemStore
* @extends {UmbStoreBase}
* @description - Data Store for Data Type items
*/
export class UmbDataTypeItemStore
extends UmbStoreBase<DataTypeItemResponseModel>
implements UmbItemStore<DataTypeItemResponseModel>
{
/**
* Creates an instance of UmbDataTypeItemStore.
* @param {UmbControllerHostElement} host
* @memberof UmbDataTypeItemStore
*/
constructor(host: UmbControllerHostElement) {
super(
host,
UMB_DATA_TYPE_ITEM_STORE_CONTEXT_TOKEN.toString(),
new UmbArrayState<DataTypeItemResponseModel>([], (x) => x.id)
);
}
items(ids: Array<string>) {
return this._data.asObservablePart((items) => items.filter((item) => ids.includes(item.id ?? '')));
}
}
export const UMB_DATA_TYPE_ITEM_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbDataTypeItemStore>('UmbDataTypeItemStore');

View File

@@ -0,0 +1,38 @@
import { UMB_DATA_TYPE_ITEM_STORE_CONTEXT_TOKEN, UmbDataTypeItemStore } from './item/data-type-item.store.js';
import { UMB_DATA_TYPE_TREE_STORE_CONTEXT_TOKEN, UmbDataTypeTreeStore } from './data-type.tree.store.js';
import { UMB_DATA_TYPE_STORE_CONTEXT_TOKEN, UmbDataTypeStore } from './data-type.store.js';
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
import { UMB_NOTIFICATION_CONTEXT_TOKEN, UmbNotificationContext } from '@umbraco-cms/backoffice/notification';
import { UmbRepositoryBase } from '@umbraco-cms/backoffice/repository';
export class UmbDataTypeRepositoryBase extends UmbRepositoryBase {
protected _init: Promise<unknown>;
protected _detailStore?: UmbDataTypeStore;
protected _treeStore?: UmbDataTypeTreeStore;
protected _itemStore?: UmbDataTypeItemStore;
protected _notificationContext?: UmbNotificationContext;
constructor(host: UmbControllerHost) {
super(host);
this._init = Promise.all([
this.consumeContext(UMB_DATA_TYPE_STORE_CONTEXT_TOKEN, (instance) => {
this._detailStore = instance;
}).asPromise(),
this.consumeContext(UMB_DATA_TYPE_TREE_STORE_CONTEXT_TOKEN, (instance) => {
this._treeStore = instance;
}).asPromise(),
this.consumeContext(UMB_DATA_TYPE_ITEM_STORE_CONTEXT_TOKEN, (instance) => {
this._itemStore = instance;
}).asPromise(),
this.consumeContext(UMB_NOTIFICATION_CONTEXT_TOKEN, (instance) => {
this._notificationContext = instance;
}).asPromise(),
]);
}
}

View File

@@ -1,42 +1,35 @@
import { DATA_TYPE_ROOT_ENTITY_TYPE } from '../entities.js';
import { UmbDataTypeTreeServerDataSource } from './sources/data-type.tree.server.data.js';
import { UmbDataTypeMoveServerDataSource } from './sources/data-type-move.server.data.js';
import { UmbDataTypeStore, UMB_DATA_TYPE_STORE_CONTEXT_TOKEN } from './data-type.store.js';
import { UmbDataTypeServerDataSource } from './sources/data-type.server.data.js';
import { UmbDataTypeTreeStore, UMB_DATA_TYPE_TREE_STORE_CONTEXT_TOKEN } from './data-type.tree.store.js';
import { UmbDataTypeFolderServerDataSource } from './sources/data-type-folder.server.data.js';
import { UmbDataTypeItemServerDataSource } from './sources/data-type-item.server.data.js';
import { UMB_DATA_TYPE_ITEM_STORE_CONTEXT_TOKEN, UmbDataTypeItemStore } from './data-type-item.store.js';
import { UmbDataTypeCopyServerDataSource } from './sources/data-type-copy.server.data.js';
import { UmbDataTypeRepositoryBase } from './data-type-repository-base.js';
import type {
UmbTreeRepository,
UmbDetailRepository,
UmbItemRepository,
UmbFolderRepository,
UmbMoveRepository,
UmbCopyRepository,
UmbTreeDataSource,
UmbDataSource,
UmbFolderDataSource,
UmbItemDataSource,
UmbMoveDataSource,
UmbCopyDataSource,
} from '@umbraco-cms/backoffice/repository';
import { UmbBaseController, type UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
import { type UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
import {
CreateDataTypeRequestModel,
CreateFolderRequestModel,
DataTypeItemResponseModel,
DataTypeResponseModel,
FolderModelBaseModel,
FolderTreeItemResponseModel,
UpdateDataTypeRequestModel,
} from '@umbraco-cms/backoffice/backend-api';
import { UmbNotificationContext, UMB_NOTIFICATION_CONTEXT_TOKEN } from '@umbraco-cms/backoffice/notification';
import { UmbApi } from '@umbraco-cms/backoffice/extension-api';
export class UmbDataTypeRepository extends UmbBaseController
export class UmbDataTypeRepository
extends UmbDataTypeRepositoryBase
implements
UmbItemRepository<DataTypeItemResponseModel>,
UmbTreeRepository<FolderTreeItemResponseModel>,
UmbDetailRepository<CreateDataTypeRequestModel, any, UpdateDataTypeRequestModel, DataTypeResponseModel>,
UmbFolderRepository,
@@ -44,21 +37,12 @@ export class UmbDataTypeRepository extends UmbBaseController
UmbCopyRepository,
UmbApi
{
#init: Promise<unknown>;
#treeSource: UmbTreeDataSource<FolderTreeItemResponseModel>;
#detailSource: UmbDataSource<CreateDataTypeRequestModel, any, UpdateDataTypeRequestModel, DataTypeResponseModel>;
#folderSource: UmbFolderDataSource;
#itemSource: UmbItemDataSource<DataTypeItemResponseModel>;
#moveSource: UmbMoveDataSource;
#copySource: UmbCopyDataSource;
#detailStore?: UmbDataTypeStore;
#treeStore?: UmbDataTypeTreeStore;
#itemStore?: UmbDataTypeItemStore;
#notificationContext?: UmbNotificationContext;
constructor(host: UmbControllerHost) {
super(host);
@@ -66,33 +50,13 @@ export class UmbDataTypeRepository extends UmbBaseController
this.#treeSource = new UmbDataTypeTreeServerDataSource(this);
this.#detailSource = new UmbDataTypeServerDataSource(this);
this.#folderSource = new UmbDataTypeFolderServerDataSource(this);
this.#itemSource = new UmbDataTypeItemServerDataSource(this);
this.#moveSource = new UmbDataTypeMoveServerDataSource(this);
this.#copySource = new UmbDataTypeCopyServerDataSource(this);
// TODO: Make a method that takes the controllers and returns a promise, just to simplify this:
this.#init = Promise.all([
this.consumeContext(UMB_DATA_TYPE_STORE_CONTEXT_TOKEN, (instance) => {
this.#detailStore = instance;
}).asPromise(),
this.consumeContext(UMB_DATA_TYPE_TREE_STORE_CONTEXT_TOKEN, (instance) => {
this.#treeStore = instance;
}).asPromise(),
this.consumeContext(UMB_DATA_TYPE_ITEM_STORE_CONTEXT_TOKEN, (instance) => {
this.#itemStore = instance;
}).asPromise(),
this.consumeContext(UMB_NOTIFICATION_CONTEXT_TOKEN, (instance) => {
this.#notificationContext = instance;
}).asPromise(),
]);
}
// TREE:
async requestTreeRoot() {
await this.#init;
await this._init;
const data = {
id: null,
@@ -106,108 +70,89 @@ export class UmbDataTypeRepository extends UmbBaseController
}
async requestRootTreeItems() {
await this.#init;
await this._init;
const { data, error } = await this.#treeSource.getRootItems();
if (data) {
this.#treeStore?.appendItems(data.items);
this._treeStore!.appendItems(data.items);
}
return { data, error, asObservable: () => this.#treeStore!.rootItems };
return { data, error, asObservable: () => this._treeStore!.rootItems };
}
async requestTreeItemsOf(parentId: string | null) {
await this.#init;
await this._init;
if (parentId === undefined) throw new Error('Parent id is missing');
const { data, error } = await this.#treeSource.getChildrenOf(parentId);
if (data) {
this.#treeStore?.appendItems(data.items);
this._treeStore!.appendItems(data.items);
}
return { data, error, asObservable: () => this.#treeStore!.childrenOf(parentId) };
return { data, error, asObservable: () => this._treeStore!.childrenOf(parentId) };
}
async rootTreeItems() {
await this.#init;
return this.#treeStore!.rootItems;
await this._init;
return this._treeStore!.rootItems;
}
async treeItemsOf(parentId: string | null) {
if (parentId === undefined) throw new Error('Parent id is missing');
await this.#init;
return this.#treeStore!.childrenOf(parentId);
}
// ITEMS:
async requestItems(ids: Array<string>) {
if (!ids) throw new Error('Keys are missing');
await this.#init;
const { data, error } = await this.#itemSource.getItems(ids);
if (data) {
this.#itemStore?.appendItems(data);
}
return { data, error, asObservable: () => this.#itemStore!.items(ids) };
}
async items(ids: Array<string>) {
await this.#init;
return this.#itemStore!.items(ids);
await this._init;
return this._treeStore!.childrenOf(parentId);
}
// DETAILS:
async createScaffold(parentId: string | null) {
if (parentId === undefined) throw new Error('Parent id is missing');
await this.#init;
await this._init;
return this.#detailSource.createScaffold(parentId);
}
async requestById(id: string) {
if (!id) throw new Error('Key is missing');
await this.#init;
await this._init;
const { data, error } = await this.#detailSource.get(id);
if (data) {
this.#detailStore?.append(data);
this._detailStore!.append(data);
}
return { data, error, asObservable: () => this.#detailStore!.byId(id) };
return { data, error, asObservable: () => this._detailStore!.byId(id) };
}
async byId(id: string) {
if (!id) throw new Error('Key is missing');
await this.#init;
return this.#detailStore!.byId(id);
await this._init;
return this._detailStore!.byId(id);
}
async byPropertyEditorUiAlias(propertyEditorUiAlias: string) {
if (!propertyEditorUiAlias) throw new Error('propertyEditorUiAlias is missing');
await this.#init;
return this.#detailStore!.withPropertyEditorUiAlias(propertyEditorUiAlias);
await this._init;
return this._detailStore!.withPropertyEditorUiAlias(propertyEditorUiAlias);
}
async create(dataType: CreateDataTypeRequestModel) {
if (!dataType) throw new Error('Data Type is missing');
if (!dataType.id) throw new Error('Data Type id is missing');
await this.#init;
await this._init;
const { error } = await this.#detailSource.insert(dataType);
if (!error) {
// TODO: We need to push a new item to the tree store to update the tree. How do we want to create the tree items?
const treeItem = createTreeItem(dataType);
this.#treeStore?.appendItems([treeItem]);
this._treeStore!.appendItems([treeItem]);
//this.#detailStore?.append(dataType);
const notification = { data: { message: `Data Type created` } };
this.#notificationContext?.peek('positive', notification);
this._notificationContext!.peek('positive', notification);
}
return { error };
@@ -216,7 +161,7 @@ export class UmbDataTypeRepository extends UmbBaseController
async save(id: string, updatedDataType: UpdateDataTypeRequestModel) {
if (!id) throw new Error('Data Type id is missing');
if (!updatedDataType) throw new Error('Data Type is missing');
await this.#init;
await this._init;
const { error } = await this.#detailSource.update(id, updatedDataType);
@@ -225,13 +170,13 @@ export class UmbDataTypeRepository extends UmbBaseController
// Consider to look up the data before fetching from the server
// Consider notify a workspace if a template is updated in the store while someone is editing it.
// TODO: would be nice to align the stores on methods/methodNames.
this.#detailStore?.updateItem(id, updatedDataType);
this._detailStore!.updateItem(id, updatedDataType);
// TODO: This is parsing on the full models to the tree and item store. Those should only contain the data they need. I don't know, at this point, if thats a repository or store responsibility.
this.#treeStore?.updateItem(id, updatedDataType);
this.#itemStore?.updateItem(id, updatedDataType);
this._treeStore!.updateItem(id, updatedDataType);
this._itemStore!.updateItem(id, updatedDataType);
const notification = { data: { message: `Data Type saved` } };
this.#notificationContext?.peek('positive', notification);
this._notificationContext!.peek('positive', notification);
}
return { error };
@@ -239,7 +184,7 @@ export class UmbDataTypeRepository extends UmbBaseController
async delete(id: string) {
if (!id) throw new Error('Data Type id is missing');
await this.#init;
await this._init;
const { error } = await this.#detailSource.delete(id);
@@ -248,12 +193,12 @@ export class UmbDataTypeRepository extends UmbBaseController
// Consider to look up the data before fetching from the server.
// Consider notify a workspace if a template is deleted from the store while someone is editing it.
// TODO: would be nice to align the stores on methods/methodNames.
this.#detailStore?.remove([id]);
this.#treeStore?.removeItem(id);
this.#itemStore?.removeItem(id);
this._detailStore!.remove([id]);
this._treeStore!.removeItem(id);
this._itemStore!.removeItem(id);
const notification = { data: { message: `Data Type deleted` } };
this.#notificationContext?.peek('positive', notification);
this._notificationContext!.peek('positive', notification);
}
return { error };
@@ -262,21 +207,21 @@ export class UmbDataTypeRepository extends UmbBaseController
// Folder:
async createFolderScaffold(parentId: string | null) {
if (parentId === undefined) throw new Error('Parent id is missing');
await this.#init;
await this._init;
return this.#folderSource.createScaffold(parentId);
}
// TODO: temp create type until backend is ready. Remove the id addition when new types are generated.
async createFolder(folderRequest: CreateFolderRequestModel & { id?: string | undefined }) {
if (!folderRequest) throw new Error('folder request is missing');
await this.#init;
await this._init;
const { error } = await this.#folderSource.insert(folderRequest);
if (!error) {
// TODO: We need to push a new item to the tree store to update the tree. How do we want to create the tree items?
const folderTreeItem = createFolderTreeItem(folderRequest);
this.#treeStore?.appendItems([folderTreeItem]);
this._treeStore!.appendItems([folderTreeItem]);
}
return { error };
@@ -284,12 +229,12 @@ export class UmbDataTypeRepository extends UmbBaseController
async deleteFolder(id: string) {
if (!id) throw new Error('Key is missing');
await this.#init;
await this._init;
const { error } = await this.#folderSource.delete(id);
if (!error) {
this.#treeStore?.removeItem(id);
this._treeStore!.removeItem(id);
}
return { error };
@@ -298,12 +243,12 @@ export class UmbDataTypeRepository extends UmbBaseController
async updateFolder(id: string, folder: FolderModelBaseModel) {
if (!id) throw new Error('Key is missing');
if (!folder) throw new Error('Folder data is missing');
await this.#init;
await this._init;
const { error } = await this.#folderSource.update(id, folder);
if (!error) {
this.#treeStore?.updateItem(id, { name: folder.name });
this._treeStore!.updateItem(id, { name: folder.name });
}
return { error };
@@ -311,32 +256,32 @@ export class UmbDataTypeRepository extends UmbBaseController
async requestFolder(id: string) {
if (!id) throw new Error('Key is missing');
await this.#init;
await this._init;
return await this.#folderSource.get(id);
}
// Actions
async move(id: string, targetId: string | null) {
await this.#init;
await this._init;
const { error } = await this.#moveSource.move(id, targetId);
if (!error) {
// TODO: Be aware about this responsibility.
this.#treeStore?.updateItem(id, { parentId: targetId });
this._treeStore!.updateItem(id, { parentId: targetId });
// only update the target if its not the root
if (targetId) {
this.#treeStore?.updateItem(targetId, { hasChildren: true });
this._treeStore!.updateItem(targetId, { hasChildren: true });
}
const notification = { data: { message: `Data type moved` } };
this.#notificationContext?.peek('positive', notification);
this._notificationContext!.peek('positive', notification);
}
return { error };
}
async copy(id: string, targetId: string | null) {
await this.#init;
await this._init;
const { data: dataTypeCopyId, error } = await this.#copySource.copy(id, targetId);
if (error) return { error };
@@ -345,14 +290,14 @@ export class UmbDataTypeRepository extends UmbBaseController
if (!dataTypeCopy) throw new Error('Could not find copied data type');
// TODO: Be aware about this responsibility.
this.#treeStore?.appendItems([dataTypeCopy]);
this._treeStore!.appendItems([dataTypeCopy]);
// only update the target if its not the root
if (targetId) {
this.#treeStore?.updateItem(targetId, { hasChildren: true });
this._treeStore!.updateItem(targetId, { hasChildren: true });
}
const notification = { data: { message: `Data type copied` } };
this.#notificationContext?.peek('positive', notification);
this._notificationContext!.peek('positive', notification);
}
return { data: dataTypeCopyId };

View File

@@ -0,0 +1,47 @@
import { UmbDataTypeRepositoryBase } from '../data-type-repository-base.js';
import { UmbDataTypeItemServerDataSource } from './data-type-item.server.data.js';
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
import { UmbItemDataSource, UmbItemRepository } from '@umbraco-cms/backoffice/repository';
import { DataTypeItemResponseModel } from '@umbraco-cms/backoffice/backend-api';
export class UmbDataTypeItemRepository
extends UmbDataTypeRepositoryBase
implements UmbItemRepository<DataTypeItemResponseModel>
{
#itemSource: UmbItemDataSource<DataTypeItemResponseModel>;
constructor(host: UmbControllerHost) {
super(host);
this.#itemSource = new UmbDataTypeItemServerDataSource(host);
}
/**
* Requests the Data Type items for the given ids
* @param {Array<string>} ids
* @return {*}
* @memberof UmbDataTypeItemRepository
*/
async requestItems(ids: Array<string>) {
if (!ids) throw new Error('Ids are missing');
await this._init;
const { data, error } = await this.#itemSource.getItems(ids);
if (data) {
this._itemStore!.appendItems(data);
}
return { data, error, asObservable: () => this._itemStore!.items(ids) };
}
/**
* Returns a promise with an observable of the Data Type items for the given ids
* @param {Array<string>} ids
* @return {Promise<Observable<DataTypeItemResponseModel[]>>}
* @memberof UmbDataTypeItemRepository
*/
async items(ids: Array<string>) {
await this._init;
return this._itemStore!.items(ids);
}
}

View File

@@ -0,0 +1,24 @@
import { DataTypeItemResponseModel } from '@umbraco-cms/backoffice/backend-api';
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
import { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
import { UmbEntityItemStore } from '@umbraco-cms/backoffice/store';
/**
* @export
* @class UmbDataTypeItemStore
* @extends {UmbStoreBase}
* @description - Data Store for Data Type items
*/
export class UmbDataTypeItemStore extends UmbEntityItemStore<DataTypeItemResponseModel> {
/**
* Creates an instance of UmbDataTypeItemStore.
* @param {UmbControllerHost} host
* @memberof UmbDataTypeItemStore
*/
constructor(host: UmbControllerHost) {
super(host, UMB_DATA_TYPE_ITEM_STORE_CONTEXT_TOKEN.toString());
}
}
export const UMB_DATA_TYPE_ITEM_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbDataTypeItemStore>('UmbDataTypeItemStore');

View File

@@ -0,0 +1,22 @@
import { UmbDataTypeItemStore } from './data-type-item.store.js';
import { UmbDataTypeItemRepository } from './data-type-item.repository.js';
import type { ManifestRepository, ManifestItemStore } from '@umbraco-cms/backoffice/extension-registry';
export const DATA_TYPE_ITEM_REPOSITORY_ALIAS = 'Umb.Repository.DataTypeItem';
export const DATA_TYPE_STORE_ALIAS = 'Umb.Store.DataTypeItem';
const itemRepository: ManifestRepository = {
type: 'repository',
alias: DATA_TYPE_ITEM_REPOSITORY_ALIAS,
name: 'Data Type Item Repository',
api: UmbDataTypeItemRepository,
};
const itemStore: ManifestItemStore = {
type: 'itemStore',
alias: DATA_TYPE_STORE_ALIAS,
name: 'Data Type Item Store',
api: UmbDataTypeItemStore,
};
export const manifests = [itemRepository, itemStore];

View File

@@ -1,13 +1,8 @@
import { UmbDataTypeRepository } from './data-type.repository.js';
import { UmbDataTypeItemStore } from './data-type-item.store.js';
import { UmbDataTypeStore } from './data-type.store.js';
import { UmbDataTypeTreeStore } from './data-type.tree.store.js';
import type {
ManifestStore,
ManifestTreeStore,
ManifestRepository,
ManifestItemStore,
} from '@umbraco-cms/backoffice/extension-registry';
import { manifests as itemManifests } from './item/manifests.js';
import type { ManifestStore, ManifestTreeStore, ManifestRepository } from '@umbraco-cms/backoffice/extension-registry';
export const DATA_TYPE_REPOSITORY_ALIAS = 'Umb.Repository.DataType';
@@ -20,7 +15,6 @@ const repository: ManifestRepository = {
export const DATA_TYPE_STORE_ALIAS = 'Umb.Store.DataType';
export const DATA_TYPE_TREE_STORE_ALIAS = 'Umb.Store.DataTypeTree';
export const DATA_TYPE_ITEM_STORE_ALIAS = 'Umb.Store.DataTypeItem';
const store: ManifestStore = {
type: 'store',
@@ -36,11 +30,4 @@ const treeStore: ManifestTreeStore = {
api: UmbDataTypeTreeStore,
};
const itemStore: ManifestItemStore = {
type: 'itemStore',
alias: DATA_TYPE_ITEM_STORE_ALIAS,
name: 'Data Type Item Store',
api: UmbDataTypeItemStore,
};
export const manifests = [repository, store, treeStore, itemStore];
export const manifests = [repository, store, treeStore, ...itemManifests];