From a4bee94f29ae569460eaa358e1ea8e3624c4c00b Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Mon, 30 Jan 2023 14:10:44 +0100 Subject: [PATCH] add template server data source --- .../templating/templates/data/index.ts | 22 ++++ .../templates/template.repository.ts | 121 ++++++------------ .../templating/templates/template.server.ts | 106 +++++++++++++++ .../workspace/template-workspace.context.ts | 2 +- 4 files changed, 171 insertions(+), 80 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/backoffice/templating/templates/data/index.ts create mode 100644 src/Umbraco.Web.UI.Client/src/backoffice/templating/templates/template.server.ts diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/templating/templates/data/index.ts b/src/Umbraco.Web.UI.Client/src/backoffice/templating/templates/data/index.ts new file mode 100644 index 0000000000..da130c02f7 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/backoffice/templating/templates/data/index.ts @@ -0,0 +1,22 @@ +import { EntityTreeItem, PagedEntityTreeItem, ProblemDetails, Template } from '@umbraco-cms/backend-api'; + +export interface DataOrErrorResponse { + data?: T; + error?: ProblemDetails; +} + +export interface ErrorResponse { + data?: undefined; + error?: ProblemDetails; +} + +export interface TemplateDataSource { + createScaffold(parentKey: string | null): Promise>; + get(key: string): Promise>; + insert(template: Template): Promise; + update(template: Template): Promise; + delete(key: string): Promise; + getTreeRoot(): Promise>; + getTreeItemChildren(parentKey: string): Promise>; + getTreeItems(key: Array): Promise>; +} diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/templating/templates/template.repository.ts b/src/Umbraco.Web.UI.Client/src/backoffice/templating/templates/template.repository.ts index 5591a9dec1..dbb5364eae 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/templating/templates/template.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/templating/templates/template.repository.ts @@ -1,23 +1,17 @@ -import { v4 as uuid } from 'uuid'; import { Observable } from 'rxjs'; import { UmbTemplateDetailStore, UMB_TEMPLATE_DETAIL_STORE_CONTEXT_TOKEN } from './template.detail.store'; import { UmbTemplateTreeStore, UMB_TEMPLATE_TREE_STORE_CONTEXT_TOKEN } from './tree/template.tree.store'; -import { EntityTreeItem, Template, TemplateResource } from '@umbraco-cms/backend-api'; +import { TemplateServerDataSource } from './template.server'; +import { EntityTreeItem, PagedEntityTreeItem, ProblemDetails, Template } from '@umbraco-cms/backend-api'; import { UmbContextConsumerController } from '@umbraco-cms/context-api'; import { UmbControllerHostInterface } from '@umbraco-cms/controller'; -import { tryExecuteAndNotify } from '@umbraco-cms/resources'; import { UmbNotificationService, UMB_NOTIFICATION_SERVICE_CONTEXT_TOKEN } from '@umbraco-cms/notification'; -/* we need to new up the repository from within the element context. We want the correct context for -the notifications to be displayed in the correct place. */ - +/* We need to create a new instance of the repository from within the element context. We want the notifications to be displayed in the right context. */ // element -> context -> repository -> (store) -> data source - -/* TODO: don't call the server directly from the repository. -We need to be able to swap it out with a local database in the future. -Implement data sources */ export class UmbTemplateRepository { #host: UmbControllerHostInterface; + #dataSource: TemplateServerDataSource; #detailStore?: UmbTemplateDetailStore; #treeStore!: UmbTemplateTreeStore; #notificationService?: UmbNotificationService; @@ -26,6 +20,8 @@ export class UmbTemplateRepository { constructor(host: UmbControllerHostInterface) { this.#host = host; + // TODO: figure out how spin up get the correct data source + this.#dataSource = new TemplateServerDataSource(this.#host); new UmbContextConsumerController(this.#host, UMB_TEMPLATE_DETAIL_STORE_CONTEXT_TOKEN, (instance) => { this.#detailStore = instance; @@ -56,55 +52,31 @@ export class UmbTemplateRepository { } } - async new(parentKey: string | null): Promise<{ data?: Template; error?: unknown }> { - let masterTemplateAlias: string | undefined = undefined; - let error = undefined; - let data = undefined; - - // TODO: can we do something so we don't have to call two endpoints? - if (parentKey) { - const { data: parentData, error: parentError } = await tryExecuteAndNotify( - this.#host, - TemplateResource.getTemplateByKey({ key: parentKey }) - ); - masterTemplateAlias = parentData?.alias; - error = parentError; + async createScaffold(parentKey: string | null): Promise<{ data?: Template; error?: ProblemDetails }> { + if (!parentKey) { + const error: ProblemDetails = { title: 'Parent key is missing' }; + return { error }; } - const { data: scaffoldData, error: scaffoldError } = await tryExecuteAndNotify( - this.#host, - TemplateResource.getTemplateScaffold({ masterTemplateAlias }) - ); - error = scaffoldError; - - if (scaffoldData?.content) { - data = { - key: uuid(), - name: '', - alias: '', - content: scaffoldData?.content, - }; - } - - return { data, error }; + return this.#dataSource.createScaffold(parentKey); } - async get(key: string): Promise<{ data?: Template; error?: unknown }> { + async get(key: string): Promise<{ data?: Template; error?: ProblemDetails }> { if (!key) { - return { error: 'key is missing' }; + const error: ProblemDetails = { title: 'Key is missing' }; + return { error }; } - const { data, error } = await tryExecuteAndNotify(this.#host, TemplateResource.getTemplateByKey({ key })); - return { data, error }; + return this.#dataSource.get(key); } - async insert(template: Template): Promise<{ error?: unknown }> { + async insert(template: Template): Promise<{ error?: ProblemDetails }> { if (!template) { - return { error: 'Template is missing' }; + const error: ProblemDetails = { title: 'Template is missing' }; + return { error }; } - const payload = { requestBody: template }; - const { error } = await tryExecuteAndNotify(this.#host, TemplateResource.postTemplate(payload)); + const { error } = await this.#dataSource.insert(template); if (!error) { const notification = { data: { message: `Template created` } }; @@ -118,13 +90,13 @@ export class UmbTemplateRepository { return { error }; } - async update(template: Template): Promise<{ error?: unknown }> { - if (!template.key) { - return { error: 'Template key is missing' }; + async update(template: Template): Promise<{ error?: ProblemDetails }> { + if (!template) { + const error: ProblemDetails = { title: 'Template is missing' }; + return { error }; } - const payload = { key: template.key, requestBody: template }; - const { error } = await tryExecuteAndNotify(this.#host, TemplateResource.putTemplateByKey(payload)); + const { error } = await this.#dataSource.update(template); if (!error) { const notification = { data: { message: `Template saved` } }; @@ -138,15 +110,16 @@ export class UmbTemplateRepository { return { error }; } - async delete(key: string): Promise<{ error?: unknown }> { - if (key) { - return { error: 'Key is missing' }; + async delete(key: string): Promise<{ error?: ProblemDetails }> { + if (!key) { + const error: ProblemDetails = { title: 'Key is missing' }; + return { error }; } - const { error } = await tryExecuteAndNotify(this.#host, TemplateResource.deleteTemplateByKey({ key })); + const { error } = await this.#dataSource.delete(key); if (!error) { - const notification = { data: { message: `Template saved` } }; + const notification = { data: { message: `Template deleted` } }; this.#notificationService?.peek('positive', notification); } @@ -155,11 +128,9 @@ export class UmbTemplateRepository { return { error }; } - // TODO: add delete // TODO: split into multiple repositories - - async getTreeRoot(): Promise<{ data?: unknown; error?: unknown }> { - const { data, error } = await tryExecuteAndNotify(this.#host, TemplateResource.getTreeTemplateRoot({})); + async getTreeRoot(): Promise<{ data?: PagedEntityTreeItem; error?: ProblemDetails }> { + const { data, error } = await this.#dataSource.getTreeRoot(); if (data) { this.#treeStore?.appendTreeItems(data.items); @@ -168,17 +139,13 @@ export class UmbTemplateRepository { return { data, error }; } - async getTreeItemChildren(key: string): Promise<{ data?: unknown; error?: unknown }> { - if (key) { - return { error: 'Key is missing' }; + async getTreeItemChildren(parentKey: string): Promise<{ data?: PagedEntityTreeItem; error?: ProblemDetails }> { + if (!parentKey) { + const error: ProblemDetails = { title: 'Parent key is missing' }; + return { error }; } - const { data, error } = await tryExecuteAndNotify( - this.#host, - TemplateResource.getTreeTemplateChildren({ - parentKey: key, - }) - ); + const { data, error } = await this.#dataSource.getTreeItemChildren(parentKey); if (data) { this.#treeStore?.appendTreeItems(data.items); @@ -187,17 +154,13 @@ export class UmbTemplateRepository { return { data, error }; } - async getTreeItems(keys: Array): Promise<{ data?: unknown; error?: unknown }> { - if (keys) { - return { error: 'Keys are missing' }; + async getTreeItems(keys: Array): Promise<{ data?: EntityTreeItem[]; error?: ProblemDetails }> { + if (!keys) { + const error: ProblemDetails = { title: 'Keys are missing' }; + return { error }; } - const { data, error } = await tryExecuteAndNotify( - this.#host, - TemplateResource.getTreeTemplateItem({ - key: keys, - }) - ); + const { data, error } = await this.#dataSource.getTreeItems(keys); if (data) { this.#treeStore?.appendTreeItems(data); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/templating/templates/template.server.ts b/src/Umbraco.Web.UI.Client/src/backoffice/templating/templates/template.server.ts new file mode 100644 index 0000000000..740b9b29df --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/backoffice/templating/templates/template.server.ts @@ -0,0 +1,106 @@ +import { v4 as uuid } from 'uuid'; +import { TemplateDataSource } from './data'; +import { ProblemDetails, Template, TemplateResource } from '@umbraco-cms/backend-api'; +import { UmbControllerHostInterface } from '@umbraco-cms/controller'; +import { tryExecuteAndNotify } from '@umbraco-cms/resources'; + +export class TemplateServerDataSource implements TemplateDataSource { + #host: UmbControllerHostInterface; + constructor(host: UmbControllerHostInterface) { + this.#host = host; + } + + get(key: string) { + return tryExecuteAndNotify(this.#host, TemplateResource.getTemplateByKey({ key })); + } + + async createScaffold(parentKey: string | null) { + let masterTemplateAlias: string | undefined = undefined; + let error = undefined; + let data = undefined; + + // TODO: update when backend is updated so we don't have to do two calls + if (parentKey) { + const { data: parentData, error: parentError } = await tryExecuteAndNotify( + this.#host, + TemplateResource.getTemplateByKey({ key: parentKey }) + ); + masterTemplateAlias = parentData?.alias; + error = parentError; + } + + const { data: scaffoldData, error: scaffoldError } = await tryExecuteAndNotify( + this.#host, + TemplateResource.getTemplateScaffold({ masterTemplateAlias }) + ); + + error = scaffoldError; + + if (scaffoldData?.content) { + data = { + key: uuid(), + name: '', + alias: '', + content: scaffoldData?.content, + }; + } + + return { data, error }; + } + + async insert(template: Template) { + const payload = { requestBody: template }; + return tryExecuteAndNotify(this.#host, TemplateResource.postTemplate(payload)); + } + + async update(template: Template) { + if (!template.key) { + const error: ProblemDetails = { title: 'Template key is missing' }; + return { error }; + } + + const payload = { key: template.key, requestBody: template }; + return tryExecuteAndNotify(this.#host, TemplateResource.putTemplateByKey(payload)); + } + + async delete(key: string) { + if (!key) { + const error: ProblemDetails = { title: 'Key is missing' }; + return { error }; + } + + return await tryExecuteAndNotify(this.#host, TemplateResource.deleteTemplateByKey({ key })); + } + + async getTreeRoot() { + return tryExecuteAndNotify(this.#host, TemplateResource.getTreeTemplateRoot({})); + } + + async getTreeItemChildren(parentKey: string | null) { + if (!parentKey) { + const error: ProblemDetails = { title: 'Parent key is missing' }; + return { error }; + } + + return tryExecuteAndNotify( + this.#host, + TemplateResource.getTreeTemplateChildren({ + parentKey, + }) + ); + } + + async getTreeItems(keys: Array) { + if (keys) { + const error: ProblemDetails = { title: 'Keys are missing' }; + return { error }; + } + + return tryExecuteAndNotify( + this.#host, + TemplateResource.getTreeTemplateItem({ + key: keys, + }) + ); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/templating/templates/workspace/template-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/templating/templates/workspace/template-workspace.context.ts index fd53505e34..7f5bf71b0d 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/templating/templates/workspace/template-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/templating/templates/workspace/template-workspace.context.ts @@ -33,7 +33,7 @@ export class UmbTemplateWorkspaceContext { } async createScaffold(parentKey: string | null) { - const { data } = await this.#templateRepository.new(parentKey); + const { data } = await this.#templateRepository.createScaffold(parentKey); if (!data) return; this.#data.next(data); }