add unique to document type data

This commit is contained in:
Mads Rasmussen
2024-01-15 20:11:07 +01:00
parent 468dd04b96
commit e483d3da11
20 changed files with 449 additions and 501 deletions

View File

@@ -1,14 +1,18 @@
import {
ContentTypeCompositionTypeModel,
DocumentTypeItemResponseModel,
DocumentTypeResponseModel,
DocumentTypeTreeItemResponseModel,
} from '@umbraco-cms/backoffice/backend-api';
export type UmbMockDocumentTypeModel = DocumentTypeResponseModel & DocumentTypeTreeItemResponseModel;
export type UmbMockDocumentTypeModelHack = DocumentTypeResponseModel &
DocumentTypeTreeItemResponseModel &
DocumentTypeItemResponseModel;
export interface UmbMockDocumentTypeModel extends Omit<UmbMockDocumentTypeModelHack, 'type'> {}
export const data: Array<UmbMockDocumentTypeModel> = [
{
type: 'document-type',
allowedTemplateIds: [],
defaultTemplateId: null,
id: 'all-property-editors-document-type-id',
@@ -675,7 +679,6 @@ export const data: Array<UmbMockDocumentTypeModel> = [
variesByCulture: true,
variesBySegment: false,
isElement: false,
type: 'document-type',
hasChildren: false,
isContainer: false,
parentId: null,
@@ -732,7 +735,6 @@ export const data: Array<UmbMockDocumentTypeModel> = [
variesByCulture: true,
variesBySegment: false,
isElement: false,
type: 'document-type',
hasChildren: false,
isContainer: false,
parentId: null,
@@ -903,7 +905,6 @@ export const data: Array<UmbMockDocumentTypeModel> = [
variesByCulture: false,
variesBySegment: false,
isElement: false,
type: 'document-type',
hasChildren: false,
isContainer: false,
parentId: null,
@@ -959,7 +960,6 @@ export const data: Array<UmbMockDocumentTypeModel> = [
variesByCulture: false,
variesBySegment: false,
isElement: true,
type: 'document-type',
hasChildren: false,
isContainer: false,
parentId: null,
@@ -1015,7 +1015,6 @@ export const data: Array<UmbMockDocumentTypeModel> = [
variesByCulture: false,
variesBySegment: false,
isElement: true,
type: 'document-type',
hasChildren: false,
isContainer: false,
parentId: null,
@@ -1075,7 +1074,6 @@ export const data: Array<UmbMockDocumentTypeModel> = [
variesByCulture: false,
variesBySegment: false,
isElement: false,
type: 'document-type',
hasChildren: false,
isContainer: false,
parentId: null,
@@ -1134,7 +1132,6 @@ export const data: Array<UmbMockDocumentTypeModel> = [
variesByCulture: false,
variesBySegment: false,
isElement: false,
type: 'document-type',
hasChildren: false,
isContainer: false,
parentId: null,
@@ -1199,7 +1196,6 @@ export const data: Array<UmbMockDocumentTypeModel> = [
},
},
{
type: 'document-type',
allowedTemplateIds: [],
defaultTemplateId: null,
id: 'folder-umbraco-demo-blocks-id',
@@ -1226,7 +1222,6 @@ export const data: Array<UmbMockDocumentTypeModel> = [
containers: [],
},
{
type: 'document-type',
allowedTemplateIds: [],
defaultTemplateId: null,
id: 'coffee-umbraco-demo-block-id',
@@ -1302,7 +1297,6 @@ export const data: Array<UmbMockDocumentTypeModel> = [
],
},
{
type: 'document-type',
allowedTemplateIds: [],
defaultTemplateId: null,
id: 'headline-umbraco-demo-block-id',
@@ -1358,7 +1352,6 @@ export const data: Array<UmbMockDocumentTypeModel> = [
],
},
{
type: 'document-type',
allowedTemplateIds: [],
defaultTemplateId: null,
id: 'image-umbraco-demo-block-id',
@@ -1414,7 +1407,6 @@ export const data: Array<UmbMockDocumentTypeModel> = [
],
},
{
type: 'document-type',
allowedTemplateIds: [],
defaultTemplateId: null,
id: 'rich-text-umbraco-demo-block-id',
@@ -1470,7 +1462,6 @@ export const data: Array<UmbMockDocumentTypeModel> = [
],
},
{
type: 'document-type',
allowedTemplateIds: [],
defaultTemplateId: null,
id: 'two-column-layout-umbraco-demo-block-id',

View File

@@ -1,81 +1,131 @@
import { UmbEntityData } from '../entity.data.js';
import { createEntityTreeItem } from '../utils.js';
import { UmbEntityMockDbBase } from '../entity/entity-base.js';
import { UmbMockEntityFolderManager } from '../entity/entity-folder.manager.js';
import { UmbMockEntityTreeManager } from '../entity/entity-tree.manager.js';
import { UmbMockEntityItemManager } from '../entity/entity-item.manager.js';
import { UmbMockEntityDetailManager } from '../entity/entity-detail.manager.js';
import { UmbMockDocumentTypeModel, data } from './document-type.data.js';
import { UmbId } from '@umbraco-cms/backoffice/id';
import {
DocumentTypeTreeItemResponseModel,
DocumentTypeResponseModel,
CreateDocumentTypeRequestModel,
CreateFolderRequestModel,
DocumentTypeItemResponseModel,
DocumentTypeResponseModel,
DocumentTypeTreeItemResponseModel,
} from '@umbraco-cms/backoffice/backend-api';
class UmbDocumentTypeData extends UmbEntityData<UmbMockDocumentTypeModel> {
constructor() {
class UmbDocumentTypeMockDB extends UmbEntityMockDbBase<UmbMockDocumentTypeModel> {
tree = new UmbMockEntityTreeManager<UmbMockDocumentTypeModel>(this, documentTypeTreeItemMapper);
folder = new UmbMockEntityFolderManager<UmbMockDocumentTypeModel>(this, createMockDocumentTypeFolderMapper);
item = new UmbMockEntityItemManager<UmbMockDocumentTypeModel>(this, documentTypeItemMapper);
detail = new UmbMockEntityDetailManager<UmbMockDocumentTypeModel>(
this,
createMockDocumentTypeMapper,
documentTypeDetailMapper,
);
constructor(data: Array<UmbMockDocumentTypeModel>) {
super(data);
}
// TODO: Can we do this smarter so we don't need to make this for each mock data:
insert(item: DocumentTypeResponseModel) {
const mockItem: UmbMockDocumentTypeModel = {
...item,
type: 'document-type',
isContainer: false,
hasChildren: false,
isFolder: false,
};
super.insert(mockItem);
}
update(id: string, item: DocumentTypeResponseModel) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
super.save(id, item);
}
getTreeRoot(): Array<DocumentTypeTreeItemResponseModel> {
const rootItems = this.data.filter((item) => item.parentId === null);
return rootItems.map((item) => createDocumentTypeTreeItem(item));
}
getTreeItemChildren(id: string): Array<DocumentTypeTreeItemResponseModel> {
const childItems = this.data.filter((item) => item.parentId === id);
return childItems.map((item) => createDocumentTypeTreeItem(item));
}
getTreeItem(ids: Array<string>): Array<DocumentTypeTreeItemResponseModel> {
const items = this.data.filter((item) => ids.includes(item.id ?? ''));
return items.map((item) => createDocumentTypeTreeItem(item));
}
getAllowedTypesOf(id: string): Array<DocumentTypeTreeItemResponseModel> {
const documentType = this.getById(id);
const allowedTypeKeys = documentType?.allowedContentTypes?.map((documentType) => documentType.id) ?? [];
const items = this.data.filter((item) => allowedTypeKeys.includes(item.id ?? ''));
return items.map((item) => createDocumentTypeTreeItem(item));
}
getItems(ids: Array<string>): Array<DocumentTypeItemResponseModel> {
const items = this.data.filter((item) => ids.includes(item.id ?? ''));
return items.map((item) => createDocumentTypeItem(item));
}
}
export const createDocumentTypeTreeItem = (item: DocumentTypeResponseModel): DocumentTypeTreeItemResponseModel => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const createMockDocumentTypeFolderMapper = (request: CreateFolderRequestModel): UmbMockDocumentTypeModel => {
return {
...createEntityTreeItem(item),
type: 'document-type',
name: request.name,
id: request.id ? request.id : UmbId.new(),
parentId: request.parentId,
description: '',
alias: '',
icon: '',
properties: [],
containers: [],
allowedAsRoot: false,
variesByCulture: false,
variesBySegment: false,
isElement: false,
allowedContentTypes: [],
compositions: [],
isFolder: true,
hasChildren: false,
isContainer: false,
allowedTemplateIds: [],
cleanup: {
preventCleanup: false,
keepAllVersionsNewerThanDays: null,
keepLatestVersionPerDayForDays: null,
},
};
};
const createMockDocumentTypeMapper = (request: CreateDocumentTypeRequestModel): UmbMockDocumentTypeModel => {
return {
name: request.name,
id: request.id ? request.id : UmbId.new(),
description: request.description,
alias: request.alias,
icon: request.icon,
properties: request.properties,
containers: request.containers,
allowedAsRoot: request.allowedAsRoot,
variesByCulture: request.variesByCulture,
variesBySegment: request.variesBySegment,
isElement: request.isElement,
allowedContentTypes: request.allowedContentTypes,
compositions: request.compositions,
parentId: request.containerId,
isFolder: false,
hasChildren: false,
isContainer: false,
allowedTemplateIds: [],
cleanup: {
preventCleanup: false,
keepAllVersionsNewerThanDays: null,
keepLatestVersionPerDayForDays: null,
},
};
};
const documentTypeDetailMapper = (item: UmbMockDocumentTypeModel): DocumentTypeResponseModel => {
return {
name: item.name,
id: item.id,
description: item.description,
alias: item.alias,
icon: item.icon,
properties: item.properties,
containers: item.containers,
allowedAsRoot: item.allowedAsRoot,
variesByCulture: item.variesByCulture,
variesBySegment: item.variesBySegment,
isElement: item.isElement,
allowedContentTypes: item.allowedContentTypes,
compositions: item.compositions,
allowedTemplateIds: item.allowedTemplateIds,
cleanup: item.cleanup,
};
};
const documentTypeTreeItemMapper = (
item: UmbMockDocumentTypeModel,
): Omit<DocumentTypeTreeItemResponseModel, 'type'> => {
return {
name: item.name,
hasChildren: item.hasChildren,
id: item.id,
isContainer: item.isContainer,
parentId: item.parentId,
isFolder: item.isFolder,
icon: item.icon,
isElement: item.isElement,
};
};
const createDocumentTypeItem = (item: DocumentTypeResponseModel): DocumentTypeItemResponseModel => {
const documentTypeItemMapper = (item: UmbMockDocumentTypeModel): DocumentTypeItemResponseModel => {
return {
id: item.id,
name: item.name,
isElement: item.isElement,
icon: item.icon,
isElement: item.isElement,
};
};
export const umbDocumentTypeData = new UmbDocumentTypeData();
export const umbDocumentTypeMockDb = new UmbDocumentTypeMockDB(data);

View File

@@ -1,4 +1,4 @@
import { umbDocumentTypeData } from './document-type/document-type.db.js';
import { umbDocumentTypeMockDb } from './document-type/document-type.db.js';
import { umbUserPermissionData } from './user-permission.data.js';
import { UmbEntityData } from './entity.data.js';
import { createDocumentTreeItem } from './utils.js';
@@ -815,12 +815,12 @@ class UmbDocumentData extends UmbEntityData<DocumentResponseModel> {
getDocumentByIdAllowedDocumentTypes(id: string): PagedDocumentTypeResponseModel {
const item = this.getById(id);
if (item?.contentTypeId) {
const docType = umbDocumentTypeData.getById(item.contentTypeId);
const docType = umbDocumentTypeMockDb.read(item.contentTypeId);
if (docType) {
const allowedTypes = docType?.allowedContentTypes ?? [];
const mockedTypes = allowedTypes
.map((allowedType) => umbDocumentTypeData.getById(allowedType.id))
.map((allowedType) => umbDocumentTypeMockDb.read(allowedType.id))
.filter((item) => item !== undefined) as Array<UmbMockDocumentTypeModel>;
const items = mockedTypes.map((item) => mapToDocumentType(item));
const total = items.length;
@@ -831,7 +831,8 @@ class UmbDocumentData extends UmbEntityData<DocumentResponseModel> {
}
getAllowedDocumentTypesAtRoot(): PagedDocumentTypeResponseModel {
return umbDocumentTypeData.getAll(); //.filter((docType) => docType.allowedAsRoot);
const items = umbDocumentTypeMockDb.getData(); //.filter((docType) => docType.allowedAsRoot);
return { items, total: items.length };
}
getRecycleBinRoot(): PagedRecycleBinItemResponseModel {

View File

@@ -1,36 +1,44 @@
const { rest } = window.MockServiceWorker;
import { umbDocumentTypeData } from '../../data/document-type/document-type.db.js';
import { umbDocumentTypeMockDb } from '../../data/document-type/document-type.db.js';
import { UMB_SLUG } from './slug.js';
import { CreateMediaTypeRequestModel, UpdateMediaTypeRequestModel } from '@umbraco-cms/backoffice/backend-api';
import { umbracoPath } from '@umbraco-cms/backoffice/utils';
export const handlers = [
rest.get(umbracoPath(`${UMB_SLUG}/:id`), (req, res, ctx) => {
const id = req.params.id as string;
if (!id) return;
const document = umbDocumentTypeData.getById(id);
return res(ctx.status(200), ctx.json(document));
export const detailHandlers = [
rest.post(umbracoPath(`${UMB_SLUG}`), async (req, res, ctx) => {
const requestBody = (await req.json()) as CreateMediaTypeRequestModel;
if (!requestBody) return res(ctx.status(400, 'no body found'));
const id = umbDocumentTypeMockDb.detail.create(requestBody);
return res(
ctx.status(201),
ctx.set({
Location: id,
}),
);
}),
rest.delete(umbracoPath(`${UMB_SLUG}/:id`), (req, res, ctx) => {
rest.get(umbracoPath(`${UMB_SLUG}/:id`), (req, res, ctx) => {
const id = req.params.id as string;
if (!id) return;
umbDocumentTypeData.delete([id]);
return res(ctx.status(200));
if (!id) return res(ctx.status(400));
const response = umbDocumentTypeMockDb.detail.read(id);
return res(ctx.status(200), ctx.json(response));
}),
rest.put(umbracoPath(`${UMB_SLUG}/:id`), async (req, res, ctx) => {
const id = req.params.id as string;
if (!id) return;
const data = await req.json();
if (!data) return;
umbDocumentTypeData.save(id, data);
if (!id) return res(ctx.status(400));
const requestBody = (await req.json()) as UpdateMediaTypeRequestModel;
if (!requestBody) return res(ctx.status(400, 'no body found'));
umbDocumentTypeMockDb.detail.update(id, requestBody);
return res(ctx.status(200));
}),
rest.post(umbracoPath(`${UMB_SLUG}`), async (req, res, ctx) => {
const data = await req.json();
if (!data) return;
umbDocumentTypeData.insert(data);
rest.delete(umbracoPath(`${UMB_SLUG}/:id`), (req, res, ctx) => {
const id = req.params.id as string;
if (!id) return res(ctx.status(400));
umbDocumentTypeMockDb.detail.delete(id);
return res(ctx.status(200));
}),
];

View File

@@ -1,5 +1,5 @@
import { handlers as treeHandlers } from './tree.handlers.js';
import { handlers as detailHandlers } from './detail.handlers.js';
import { handlers as itemHandlers } from './item.handlers.js';
import { treeHandlers } from './tree.handlers.js';
import { detailHandlers } from './detail.handlers.js';
import { itemHandlers } from './item.handlers.js';
export const handlers = [...treeHandlers, ...itemHandlers, ...detailHandlers];

View File

@@ -1,13 +1,13 @@
const { rest } = window.MockServiceWorker;
import { umbDocumentTypeData } from '../../data/document-type/document-type.db.js';
import { umbDocumentTypeMockDb } from '../../data/document-type/document-type.db.js';
import { UMB_SLUG } from './slug.js';
import { umbracoPath } from '@umbraco-cms/backoffice/utils';
export const handlers = [
export const itemHandlers = [
rest.get(umbracoPath(`${UMB_SLUG}/item`), (req, res, ctx) => {
const ids = req.url.searchParams.getAll('id');
if (!ids) return;
const items = umbDocumentTypeData.getItems(ids);
const items = umbDocumentTypeMockDb.item.getItems(ids);
return res(ctx.status(200), ctx.json(items));
}),
];

View File

@@ -1,29 +1,18 @@
const { rest } = window.MockServiceWorker;
import { umbDocumentTypeData } from '../../data/document-type/document-type.db.js';
import { umbDocumentTypeMockDb } from '../../data/document-type/document-type.db.js';
import { UMB_SLUG } from './slug.js';
import { umbracoPath } from '@umbraco-cms/backoffice/utils';
export const handlers = [
export const treeHandlers = [
rest.get(umbracoPath(`/tree${UMB_SLUG}/root`), (req, res, ctx) => {
const rootItems = umbDocumentTypeData.getTreeRoot();
const response = {
total: rootItems.length,
items: rootItems,
};
const response = umbDocumentTypeMockDb.tree.getRoot();
return res(ctx.status(200), ctx.json(response));
}),
rest.get(umbracoPath(`/tree${UMB_SLUG}/children`), (req, res, ctx) => {
const parentId = req.url.searchParams.get('parentId');
if (!parentId) return;
const children = umbDocumentTypeData.getTreeItemChildren(parentId);
const response = {
total: children.length,
items: children,
};
const response = umbDocumentTypeMockDb.tree.getChildrenOf(parentId);
return res(ctx.status(200), ctx.json(response));
}),
];

View File

@@ -0,0 +1,7 @@
export const UMB_DOCUMENT_TYPE_ENTITY_TYPE = 'document-type';
export const UMB_DOCUMENT_TYPE_ROOT_ENTITY_TYPE = 'document-type-root';
export const UMB_DOCUMENT_TYPE_FOLDER_ENTITY_TYPE = 'document-type-folder';
export type UmbDocumentTypeEntityType = typeof UMB_DOCUMENT_TYPE_ENTITY_TYPE;
export type UmbDocumentTypeRootEntityType = typeof UMB_DOCUMENT_TYPE_ROOT_ENTITY_TYPE;
export type UmbDocumentTypeFolderEntityType = typeof UMB_DOCUMENT_TYPE_FOLDER_ENTITY_TYPE;

View File

@@ -1,175 +1,10 @@
import { UMB_DOCUMENT_TYPE_TREE_STORE_CONTEXT, UmbDocumentTypeTreeStore } from '../../tree/document-type.tree.store.js';
import { UMB_DOCUMENT_TYPE_ITEM_STORE_CONTEXT, UmbDocumentTypeItemStore } from '../item/document-type-item.store.js';
import { UmbDocumentTypeServerDataSource } from './document-type.server.data.js';
import { UmbDocumentTypeDetailStore, UMB_DOCUMENT_TYPE_DETAIL_STORE_CONTEXT } from './document-type-detail.store.js';
import { type UmbDetailRepository } from '@umbraco-cms/backoffice/repository';
import { UmbDocumentTypeDetailModel } from '../../types.js';
import { UmbDocumentTypeServerDataSource } from './document-type-detail.server.data-source.js';
import { UMB_DOCUMENT_TYPE_DETAIL_STORE_CONTEXT } from './document-type-detail.store.js';
import { type UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
import { UmbBaseController } from '@umbraco-cms/backoffice/class-api';
import {
CreateDocumentTypeRequestModel,
DocumentTypeResponseModel,
UpdateDocumentTypeRequestModel,
} 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';
import { UmbEntityTreeItemModel } from '@umbraco-cms/backoffice/tree';
type ItemType = DocumentTypeResponseModel;
export class UmbDocumentTypeDetailRepository
extends UmbBaseController
implements
UmbDetailRepository<CreateDocumentTypeRequestModel, any, UpdateDocumentTypeRequestModel, DocumentTypeResponseModel>,
UmbApi
{
#init!: Promise<unknown>;
#treeStore?: UmbDocumentTypeTreeStore;
#detailDataSource: UmbDocumentTypeServerDataSource;
#detailStore?: UmbDocumentTypeDetailStore;
#itemStore?: UmbDocumentTypeItemStore;
#notificationContext?: UmbNotificationContext;
import { UmbDetailRepositoryBase } from '@umbraco-cms/backoffice/repository';
export class UmbDocumentTypeDetailRepository extends UmbDetailRepositoryBase<UmbDocumentTypeDetailModel> {
constructor(host: UmbControllerHost) {
super(host);
// TODO: figure out how spin up get the correct data source
this.#detailDataSource = new UmbDocumentTypeServerDataSource(this);
this.#init = Promise.all([
this.consumeContext(UMB_DOCUMENT_TYPE_TREE_STORE_CONTEXT, (instance) => {
this.#treeStore = instance;
}),
this.consumeContext(UMB_DOCUMENT_TYPE_DETAIL_STORE_CONTEXT, (instance) => {
this.#detailStore = instance;
}),
this.consumeContext(UMB_DOCUMENT_TYPE_ITEM_STORE_CONTEXT, (instance) => {
this.#itemStore = instance;
}),
this.consumeContext(UMB_NOTIFICATION_CONTEXT_TOKEN, (instance) => {
this.#notificationContext = instance;
}),
]);
}
// DETAILS:
async createScaffold(parentId: string | null) {
if (parentId === undefined) throw new Error('Parent id is missing');
await this.#init;
const { data } = await this.#detailDataSource.createScaffold(parentId);
if (data) {
this.#detailStore?.append(data);
}
return { data };
}
async requestById(id: string) {
if (!id) throw new Error('Id is missing');
await this.#init;
const { data, error } = await this.#detailDataSource.read(id);
if (data) {
this.#detailStore?.append(data);
}
return { data, error, asObservable: () => this.#detailStore!.byId(id) };
}
async byId(id: string) {
if (!id) throw new Error('Id is missing');
await this.#init;
return this.#detailStore!.byId(id);
}
// TODO: we need to figure out where to put this
async requestAllowedChildTypesOf(id: string) {
if (!id) throw new Error('Id is missing');
await this.#init;
return this.#detailDataSource.getAllowedChildrenOf(id);
}
// Could potentially be general methods:
async create(documentType: ItemType) {
if (!documentType || !documentType.id) throw new Error('Document Type is missing');
await this.#init;
const { error } = await this.#detailDataSource.create(documentType);
if (!error) {
this.#detailStore?.append(documentType);
const treeItem = createTreeItem(documentType);
this.#treeStore?.appendItems([treeItem]);
}
return { error };
}
async save(id: string, item: UpdateDocumentTypeRequestModel) {
if (!id) throw new Error('Id is missing');
if (!item) throw new Error('Item is missing');
await this.#init;
const { error } = await this.#detailDataSource.update(id, item);
if (!error) {
this.#detailStore?.updateItem(id, item);
this.#treeStore?.updateItem(id, item);
const notification = { data: { message: `Document Type saved` } };
this.#notificationContext?.peek('positive', notification);
}
return { error };
}
// General:
async delete(id: string) {
if (!id) throw new Error('Document Type id is missing');
await this.#init;
const { error } = await this.#detailDataSource.delete(id);
if (!error) {
const notification = { data: { message: `Document Type deleted` } };
this.#notificationContext?.peek('positive', notification);
// TODO: we currently don't use the detail store for anything.
// 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.
this.#detailStore?.removeItem(id);
this.#treeStore?.removeItem(id);
this.#itemStore?.removeItem(id);
}
return { error };
super(host, UmbDocumentTypeServerDataSource, UMB_DOCUMENT_TYPE_DETAIL_STORE_CONTEXT);
}
}
export const createTreeItem = (item: ItemType): UmbEntityTreeItemModel => {
if (!item) throw new Error('item is null or undefined');
if (!item.id) throw new Error('item.id is null or undefined');
// TODO: needs parentID, this is missing in the current model. Should be good when updated to a createModel.
return {
entityType: 'document-type',
parentId: null,
name: item.name,
id: item.id,
isFolder: false,
isContainer: false,
hasChildren: false,
};
};

View File

@@ -0,0 +1,215 @@
import { UmbDocumentTypeDetailModel } from '../../types.js';
import { UMB_DOCUMENT_TYPE_ENTITY_TYPE } from '../../entity.js';
import { UmbId } from '@umbraco-cms/backoffice/id';
import { UmbDetailDataSource } from '@umbraco-cms/backoffice/repository';
import {
CreateDocumentTypeRequestModel,
DocumentTypeResource,
UpdateDocumentTypeRequestModel,
} from '@umbraco-cms/backoffice/backend-api';
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources';
/**
* A data source for the Document Type that fetches data from the server
* @export
* @class UmbDocumentTypeServerDataSource
* @implements {RepositoryDetailDataSource}
*/
export class UmbDocumentTypeServerDataSource implements UmbDetailDataSource<UmbDocumentTypeDetailModel> {
#host: UmbControllerHost;
/**
* Creates an instance of UmbDocumentTypeServerDataSource.
* @param {UmbControllerHost} host
* @memberof UmbDocumentTypeServerDataSource
*/
constructor(host: UmbControllerHost) {
this.#host = host;
}
/**
* Creates a new Document Type scaffold
* @param {(string | null)} parentUnique
* @return { CreateDocumentTypeRequestModel }
* @memberof UmbDocumentTypeServerDataSource
*/
async createScaffold(parentUnique: string | null) {
const data: UmbDocumentTypeDetailModel = {
entityType: UMB_DOCUMENT_TYPE_ENTITY_TYPE,
unique: UmbId.new(),
parentUnique,
name: '',
alias: '',
description: '',
icon: '',
allowedAsRoot: false,
variesByCulture: false,
variesBySegment: false,
isElement: false,
properties: [],
containers: [],
allowedContentTypes: [],
compositions: [],
allowedTemplateIds: [],
defaultTemplateId: null,
cleanup: {
preventCleanup: false,
keepAllVersionsNewerThanDays: null,
keepLatestVersionPerDayForDays: null,
},
};
return { data };
}
/**
* Fetches a Media Type with the given id from the server
* @param {string} unique
* @return {*}
* @memberof UmbDocumentTypeServerDataSource
*/
async read(unique: string) {
if (!unique) throw new Error('Unique is missing');
const { data, error } = await tryExecuteAndNotify(
this.#host,
DocumentTypeResource.getDocumentTypeById({ id: unique }),
);
if (error || !data) {
return { error };
}
// TODO: make data mapper to prevent errors
const DocumentType: UmbDocumentTypeDetailModel = {
entityType: UMB_DOCUMENT_TYPE_ENTITY_TYPE,
unique: data.id,
parentUnique: null, // TODO: map to parent/folder id
name: data.name,
alias: data.alias,
description: data.description || null,
icon: data.icon,
allowedAsRoot: data.allowedAsRoot,
variesByCulture: data.variesByCulture,
variesBySegment: data.variesBySegment,
isElement: data.isElement,
properties: data.properties,
containers: data.containers,
allowedContentTypes: data.allowedContentTypes,
compositions: data.compositions,
allowedTemplateIds: data.allowedTemplateIds,
defaultTemplateId: data.defaultTemplateId || null,
cleanup: data.cleanup,
};
return { data: DocumentType };
}
/**
* Inserts a new Media Type on the server
* @param {UmbDocumentTypeDetailModel} documentType
* @return {*}
* @memberof UmbDocumentTypeServerDataSource
*/
async create(documentType: UmbDocumentTypeDetailModel) {
if (!documentType) throw new Error('Media Type is missing');
if (!documentType.unique) throw new Error('Media Type unique is missing');
// TODO: make data mapper to prevent errors
const requestBody: CreateDocumentTypeRequestModel = {
alias: documentType.alias,
name: documentType.name,
description: documentType.description,
icon: documentType.icon,
allowedAsRoot: documentType.allowedAsRoot,
variesByCulture: documentType.variesByCulture,
variesBySegment: documentType.variesBySegment,
isElement: documentType.isElement,
properties: documentType.properties,
containers: documentType.containers,
allowedContentTypes: documentType.allowedContentTypes,
compositions: documentType.compositions,
id: documentType.unique,
containerId: documentType.parentUnique,
allowedTemplateIds: documentType.allowedTemplateIds,
defaultTemplateId: documentType.defaultTemplateId || null,
cleanup: documentType.cleanup,
};
const { error: createError } = await tryExecuteAndNotify(
this.#host,
DocumentTypeResource.postDocumentType({
requestBody,
}),
);
if (createError) {
return { error: createError };
}
// We have to fetch the data type again. The server can have modified the data after creation
return this.read(documentType.unique);
}
/**
* Updates a DocumentType on the server
* @param {UmbDocumentTypeDetailModel} DocumentType
* @return {*}
* @memberof UmbDocumentTypeServerDataSource
*/
async update(data: UmbDocumentTypeDetailModel) {
if (!data.unique) throw new Error('Unique is missing');
// TODO: make data mapper to prevent errors
const requestBody: UpdateDocumentTypeRequestModel = {
alias: data.alias,
name: data.name,
description: data.description,
icon: data.icon,
allowedAsRoot: data.allowedAsRoot,
variesByCulture: data.variesByCulture,
variesBySegment: data.variesBySegment,
isElement: data.isElement,
properties: data.properties,
containers: data.containers,
allowedContentTypes: data.allowedContentTypes,
compositions: data.compositions,
allowedTemplateIds: data.allowedTemplateIds,
defaultTemplateId: data.defaultTemplateId || null,
cleanup: data.cleanup,
};
const { error } = await tryExecuteAndNotify(
this.#host,
DocumentTypeResource.putDocumentTypeById({
id: data.unique,
requestBody,
}),
);
if (error) {
return { error };
}
// We have to fetch the data type again. The server can have modified the data after update
return this.read(data.unique);
}
/**
* Deletes a Media Type on the server
* @param {string} unique
* @return {*}
* @memberof UmbDocumentTypeServerDataSource
*/
async delete(unique: string) {
if (!unique) throw new Error('Unique is missing');
return tryExecuteAndNotify(
this.#host,
DocumentTypeResource.deleteDocumentTypeById({
id: unique,
}),
);
}
}

View File

@@ -1,7 +1,6 @@
import { DocumentTypeResponseModel } from '@umbraco-cms/backoffice/backend-api';
import { UmbDocumentTypeDetailModel } from '../../types.js';
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
import { UmbArrayState } from '@umbraco-cms/backoffice/observable-api';
import { UmbStoreBase } from '@umbraco-cms/backoffice/store';
import { UmbDetailStoreBase } from '@umbraco-cms/backoffice/store';
import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api';
/**
@@ -10,27 +9,14 @@ import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api
* @extends {UmbStoreBase}
* @description - Data Store for Document Types
*/
export class UmbDocumentTypeDetailStore extends UmbStoreBase<DocumentTypeResponseModel> {
export class UmbDocumentTypeDetailStore extends UmbDetailStoreBase<UmbDocumentTypeDetailModel> {
/**
* Creates an instance of UmbDocumentTypeStore.
* @param {UmbControllerHostElement} host
* @memberof UmbDocumentTypeStore
*/
constructor(host: UmbControllerHostElement) {
super(
host,
UMB_DOCUMENT_TYPE_DETAIL_STORE_CONTEXT.toString(),
new UmbArrayState<DocumentTypeResponseModel>([], (x) => x.id),
);
}
/**
* @param {DocumentTypeResponseModel['id']} id
* @return {*}
* @memberof UmbDocumentTypeDetailStore
*/
byId(id: DocumentTypeResponseModel['id']) {
return this._data.asObservablePart((x) => x.find((y) => y.id === id));
super(host, UMB_DOCUMENT_TYPE_DETAIL_STORE_CONTEXT.toString());
}
}

View File

@@ -1,155 +0,0 @@
import type { UmbDataSource } from '@umbraco-cms/backoffice/repository';
import {
CreateDocumentTypeRequestModel,
DocumentTypeResource,
DocumentTypeResponseModel,
UpdateDocumentTypeRequestModel,
} from '@umbraco-cms/backoffice/backend-api';
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources';
import { UmbId } from '@umbraco-cms/backoffice/id';
/**
* A data source for the Document Type that fetches data from the server
* @export
* @class UmbDocumentTypeServerDataSource
* @implements {RepositoryDetailDataSource}
*/
export class UmbDocumentTypeServerDataSource
implements
UmbDataSource<CreateDocumentTypeRequestModel, any, UpdateDocumentTypeRequestModel, DocumentTypeResponseModel>
{
#host: UmbControllerHost;
/**
* Creates an instance of UmbDocumentServerDataSource.
* @param {UmbControllerHost} host
* @memberof UmbDocumentServerDataSource
*/
constructor(host: UmbControllerHost) {
this.#host = host;
}
/**
* Fetches a Document with the given id from the server
* @param {string} id
* @return {*}
* @memberof UmbDocumentTypeServerDataSource
*/
async read(id: string) {
if (!id) {
throw new Error('Id is missing');
}
return tryExecuteAndNotify(
this.#host,
DocumentTypeResource.getDocumentTypeById({
id: id,
}),
);
}
/**
* Creates a new Document scaffold
* @param {(string | null)} parentId
* @return {*}
* @memberof UmbDocumentTypeServerDataSource
*/
async createScaffold(parentId: string | null) {
//, parentId: string | null
const data: DocumentTypeResponseModel = {
id: UmbId.new(),
//parentId: parentId,
name: '',
alias: '',
description: '',
icon: 'icon-document',
allowedAsRoot: false,
variesByCulture: false,
variesBySegment: false,
isElement: false,
allowedContentTypes: [],
compositions: [],
allowedTemplateIds: [],
defaultTemplateId: null,
cleanup: {
preventCleanup: false,
keepAllVersionsNewerThanDays: null,
keepLatestVersionPerDayForDays: null,
},
properties: [],
containers: [],
};
return { data };
}
/**
* Inserts a new Document Type on the server
* @param {CreateDocumentTypeRequestModel} documentType
* @return {*}
* @memberof UmbDocumentTypeServerDataSource
*/
async create(documentType: CreateDocumentTypeRequestModel) {
if (!documentType) throw new Error('Document Type is missing');
return tryExecuteAndNotify(
this.#host,
DocumentTypeResource.postDocumentType({
requestBody: documentType,
}),
);
}
/**
* Updates a Document Type on the server
* @param {string} id
* @param {Document} documentType
* @return {*}
* @memberof UmbDocumentTypeServerDataSource
*/
async update(id: string, documentType: UpdateDocumentTypeRequestModel) {
if (!id) throw new Error('Id is missing');
documentType = { ...documentType };
// TODO: Hack to remove some props that ruins the document-type post end-point.
(documentType as any).id = undefined;
return tryExecuteAndNotify(this.#host, DocumentTypeResource.putDocumentTypeById({ id, requestBody: documentType }));
}
/**
* Deletes a Template on the server
* @param {string} id
* @return {*}
* @memberof UmbDocumentTypeServerDataSource
*/
async delete(id: string) {
if (!id) {
throw new Error('Id is missing');
}
// TODO: Hack the type to avoid type-error here:
return tryExecuteAndNotify(this.#host, DocumentTypeResource.deleteDocumentTypeById({ id })) as any;
}
/**
* Get the allowed document types for a given parent id
* @param {string} id
* @return {*}
* @memberof UmbDocumentTypeServerDataSource
*/
async getAllowedChildrenOf(id: string) {
if (!id) throw new Error('Id is missing');
return tryExecuteAndNotify(
this.#host,
fetch(`/umbraco/management/api/v1/document-type/allowed-children-of/${id}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
}).then((res) => res.json()),
);
}
}

View File

@@ -15,8 +15,8 @@ export class UmbDocumentTypeTreeRepository
}
async requestTreeRoot() {
const data = {
id: null,
const data: UmbDocumentTypeTreeRootModel = {
unique: null,
entityType: UMB_DOCUMENT_TYPE_ROOT_ENTITY_TYPE,
name: 'Document Types',
icon: 'icon-folder',

View File

@@ -1,3 +1,4 @@
import { UMB_DOCUMENT_TYPE_ENTITY_TYPE, UMB_DOCUMENT_TYPE_FOLDER_ENTITY_TYPE } from '../entity.js';
import { UmbDocumentTypeTreeItemModel } from './types.js';
import { UmbTreeServerDataSourceBase } from '@umbraco-cms/backoffice/tree';
import { DocumentTypeResource, DocumentTypeTreeItemResponseModel } from '@umbraco-cms/backoffice/backend-api';
@@ -43,10 +44,10 @@ const getChildrenOf = (parentUnique: string | null) => {
const mapper = (item: DocumentTypeTreeItemResponseModel): UmbDocumentTypeTreeItemModel => {
return {
id: item.id,
parentId: item.parentId || null,
unique: item.id,
parentUnique: item.parentId || null,
name: item.name,
entityType: 'document-type',
entityType: item.isFolder ? UMB_DOCUMENT_TYPE_FOLDER_ENTITY_TYPE : UMB_DOCUMENT_TYPE_ENTITY_TYPE,
isContainer: item.isContainer,
hasChildren: item.hasChildren,
isFolder: item.isFolder,

View File

@@ -1,14 +1,14 @@
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api';
import { UmbEntityTreeStore } from '@umbraco-cms/backoffice/tree';
import { UmbUniqueTreeStore } from '@umbraco-cms/backoffice/tree';
/**
* @export
* @class UmbDocumentTypeTreeStore
* @extends {UmbStoreBase}
* @extends {UmbUniqueTreeStore}
* @description - Tree Data Store for Document Types
*/
export class UmbDocumentTypeTreeStore extends UmbEntityTreeStore {
export class UmbDocumentTypeTreeStore extends UmbUniqueTreeStore {
/**
* Creates an instance of UmbDocumentTypeTreeStore.
* @param {UmbControllerHostElement} host

View File

@@ -1,3 +1,4 @@
import { UMB_DOCUMENT_TYPE_ENTITY_TYPE, UMB_DOCUMENT_TYPE_ROOT_ENTITY_TYPE } from '../entity.js';
import { UmbDocumentTypeTreeRepository } from './document-type-tree.repository.js';
import { UmbDocumentTypeTreeStore } from './document-type.tree.store.js';
import type {
@@ -36,11 +37,11 @@ const tree: ManifestTree = {
const treeItem: ManifestTreeItem = {
type: 'treeItem',
kind: 'entity',
kind: 'unique',
alias: 'Umb.TreeItem.DocumentType',
name: 'Document Type Tree Item',
meta: {
entityTypes: ['document-type-root', 'document-type'],
entityTypes: [UMB_DOCUMENT_TYPE_ROOT_ENTITY_TYPE, UMB_DOCUMENT_TYPE_ENTITY_TYPE],
},
};

View File

@@ -1,8 +1,15 @@
import type { UmbEntityTreeItemModel, UmbEntityTreeRootModel } from '@umbraco-cms/backoffice/tree';
import {
UmbDocumentTypeEntityType,
UmbDocumentTypeFolderEntityType,
UmbDocumentTypeRootEntityType,
} from '../entity.js';
import { UmbUniqueTreeItemModel, UmbUniqueTreeRootModel } from '@umbraco-cms/backoffice/tree';
export interface UmbDocumentTypeTreeItemModel extends UmbEntityTreeItemModel {
export interface UmbDocumentTypeTreeItemModel extends UmbUniqueTreeItemModel {
entityType: UmbDocumentTypeEntityType | UmbDocumentTypeFolderEntityType;
isElement: boolean;
icon?: string | null;
}
// TODO: TREE STORE TYPE PROBLEM:
export interface UmbDocumentTypeTreeRootModel extends UmbEntityTreeRootModel {}
export interface UmbDocumentTypeTreeRootModel extends UmbUniqueTreeRootModel {
entityType: UmbDocumentTypeRootEntityType;
}

View File

@@ -0,0 +1,30 @@
import { UmbDocumentTypeEntityType } from './entity.js';
import {
ContentTypeCleanupModel,
ContentTypeCompositionModel,
ContentTypeSortModel,
MediaTypePropertyTypeContainerResponseModel,
MediaTypePropertyTypeResponseModel,
} from '@umbraco-cms/backoffice/backend-api';
export interface UmbDocumentTypeDetailModel {
entityType: UmbDocumentTypeEntityType;
unique: string;
parentUnique: string | null;
name: string;
alias: string;
description: string | null;
icon: string;
allowedAsRoot: boolean;
variesByCulture: boolean;
variesBySegment: boolean;
isElement: boolean;
allowedTemplateIds: Array<string>;
defaultTemplateId: string | null;
// TODO: investigate if we need our own model for these
properties: Array<MediaTypePropertyTypeResponseModel>;
containers: Array<MediaTypePropertyTypeContainerResponseModel>;
allowedContentTypes: Array<ContentTypeSortModel>;
compositions: Array<ContentTypeCompositionModel>;
cleanup: ContentTypeCleanupModel;
}

View File

@@ -1,36 +1,22 @@
import { MediaTypeResponseModel } from '@umbraco-cms/backoffice/backend-api';
import { UmbMediaTypeDetailModel } from '../../types.js';
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
import { UmbArrayState } from '@umbraco-cms/backoffice/observable-api';
import { UmbStoreBase } from '@umbraco-cms/backoffice/store';
import { UmbDetailStoreBase } from '@umbraco-cms/backoffice/store';
import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api';
/**
* @export
* @class UmbMediaTypeStore
* @extends {UmbStoreBase}
* @extends {UmbDetailStoreBase}
* @description - Data Store for Media Types
*/
export class UmbMediaTypeDetailStore extends UmbStoreBase<MediaTypeResponseModel> {
export class UmbMediaTypeDetailStore extends UmbDetailStoreBase<UmbMediaTypeDetailModel> {
/**
* Creates an instance of UmbMediaTypeStore.
* @param {UmbControllerHostElement} host
* @memberof UmbMediaTypeStore
*/
constructor(host: UmbControllerHostElement) {
super(
host,
UMB_MEDIA_TYPE_DETAIL_STORE_CONTEXT.toString(),
new UmbArrayState<MediaTypeResponseModel>([], (x) => x.id),
);
}
/**
* @param {MediaTypeResponseModel['id']} id
* @return {*}
* @memberof UmbMediaTypeDetailStore
*/
byId(id: MediaTypeResponseModel['id']) {
return this._data.asObservablePart((x) => x.find((y) => y.id === id));
super(host, UMB_MEDIA_TYPE_DETAIL_STORE_CONTEXT.toString());
}
}

View File

@@ -1,4 +0,0 @@
import { UMB_MEDIA_TYPE_ENTITY_TYPE } from '../../entity.js';
import { MediaTypeResponseModel } from '@umbraco-cms/backoffice/backend-api';
export type UmbMediaTypeDetailModel = MediaTypeResponseModel & { entityType: typeof UMB_MEDIA_TYPE_ENTITY_TYPE };