Skip to content

Commit 0322363

Browse files
Feature: Inflight Request Cache for Document Types and Data Types (#19956)
* extend controller base * extend controller base * add package for management api * add signalr as external package * connect to server event hub * do no act on undefined * add event subject * correct alias * export token * add helper methods * cache server responses * fix import * use helpers * add detail request manager * implement for document type * implement for data type * add method for update * add support for create method * align code * Update detail-request.manager.ts * move explicit naming * move into folder * collect server code in folder * add implementation for data type request manager * implement for document type * only cache when we have connection to the server events * poc inflight request cache * clean up * update * add management api inflight request cache * Update document-type-detail.server.request-manager.ts * Update src/Umbraco.Web.UI.Client/src/packages/management-api/detail/detail-data.request-manager.ts Co-authored-by: Copilot <[email protected]> --------- Co-authored-by: Copilot <[email protected]>
1 parent 7425d10 commit 0322363

File tree

6 files changed

+104
-9
lines changed

6 files changed

+104
-9
lines changed

src/Umbraco.Web.UI.Client/src/packages/data-type/repository/detail/server-data-source/data-type-detail.server.request-manager.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,26 @@ import {
77
type DataTypeResponseModel,
88
type UpdateDataTypeRequestModel,
99
} from '@umbraco-cms/backoffice/external/backend-api';
10-
import { UmbManagementApiDetailDataRequestManager } from '@umbraco-cms/backoffice/management-api';
10+
import {
11+
UmbManagementApiDetailDataRequestManager,
12+
UmbManagementApiInflightRequestCache,
13+
} from '@umbraco-cms/backoffice/management-api';
1114

1215
export class UmbManagementApiDataTypeDetailDataRequestManager extends UmbManagementApiDetailDataRequestManager<
1316
DataTypeResponseModel,
1417
UpdateDataTypeRequestModel,
1518
CreateDataTypeRequestModel
1619
> {
20+
static #inflightRequestCache = new UmbManagementApiInflightRequestCache<DataTypeResponseModel>();
21+
1722
constructor(host: UmbControllerHost) {
1823
super(host, {
1924
create: (body: CreateDataTypeRequestModel) => DataTypeService.postDataType({ body }),
2025
read: (id: string) => DataTypeService.getDataTypeById({ path: { id } }),
2126
update: (id: string, body: UpdateDataTypeRequestModel) => DataTypeService.putDataTypeById({ path: { id }, body }),
2227
delete: (id: string) => DataTypeService.deleteDataTypeById({ path: { id } }),
2328
dataCache: dataTypeDetailCache,
29+
inflightRequestCache: UmbManagementApiDataTypeDetailDataRequestManager.#inflightRequestCache,
2430
});
2531
}
2632
}

src/Umbraco.Web.UI.Client/src/packages/documents/document-types/repository/detail/server-data-source/document-type-detail.server.request-manager.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,18 @@ import {
77
type DocumentTypeResponseModel,
88
type UpdateDocumentTypeRequestModel,
99
} from '@umbraco-cms/backoffice/external/backend-api';
10-
import { UmbManagementApiDetailDataRequestManager } from '@umbraco-cms/backoffice/management-api';
10+
import {
11+
UmbManagementApiDetailDataRequestManager,
12+
UmbManagementApiInflightRequestCache,
13+
} from '@umbraco-cms/backoffice/management-api';
1114

1215
export class UmbManagementApiDocumentTypeDetailDataRequestManager extends UmbManagementApiDetailDataRequestManager<
1316
DocumentTypeResponseModel,
1417
UpdateDocumentTypeRequestModel,
1518
CreateDocumentTypeRequestModel
1619
> {
20+
static #inflightRequestCache = new UmbManagementApiInflightRequestCache<DocumentTypeResponseModel>();
21+
1722
constructor(host: UmbControllerHost) {
1823
super(host, {
1924
create: (body: CreateDocumentTypeRequestModel) => DocumentTypeService.postDocumentType({ body }),
@@ -22,6 +27,7 @@ export class UmbManagementApiDocumentTypeDetailDataRequestManager extends UmbMan
2227
DocumentTypeService.putDocumentTypeById({ path: { id }, body }),
2328
delete: (id: string) => DocumentTypeService.deleteDocumentTypeById({ path: { id } }),
2429
dataCache: documentTypeDetailCache,
30+
inflightRequestCache: UmbManagementApiDocumentTypeDetailDataRequestManager.#inflightRequestCache,
2531
});
2632
}
2733
}

src/Umbraco.Web.UI.Client/src/packages/management-api/detail/detail-data.request-manager.ts

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { UMB_MANAGEMENT_API_SERVER_EVENT_CONTEXT } from '../server-event/constants.js';
2+
import type { UmbManagementApiInflightRequestCache } from '../inflight-request/cache.js';
23
import type { UmbManagementApiDetailDataCache } from './cache.js';
34
import {
45
tryExecute,
@@ -20,6 +21,7 @@ export interface UmbManagementApiDetailDataRequestManagerArgs<
2021
update: (id: string, data: UpdateRequestModelType) => Promise<UmbApiResponse<{ data: unknown }>>;
2122
delete: (id: string) => Promise<UmbApiResponse<{ data: unknown }>>;
2223
dataCache: UmbManagementApiDetailDataCache<DetailResponseModelType>;
24+
inflightRequestCache: UmbManagementApiInflightRequestCache<DetailResponseModelType>;
2325
}
2426

2527
export class UmbManagementApiDetailDataRequestManager<
@@ -28,6 +30,7 @@ export class UmbManagementApiDetailDataRequestManager<
2830
UpdateRequestModelType,
2931
> extends UmbControllerBase {
3032
#dataCache: UmbManagementApiDetailDataCache<DetailResponseModelType>;
33+
#inflightRequestCache: UmbManagementApiInflightRequestCache<DetailResponseModelType>;
3134

3235
#create;
3336
#read;
@@ -52,6 +55,7 @@ export class UmbManagementApiDetailDataRequestManager<
5255
this.#delete = args.delete;
5356

5457
this.#dataCache = args.dataCache;
58+
this.#inflightRequestCache = args.inflightRequestCache;
5559

5660
this.consumeContext(UMB_MANAGEMENT_API_SERVER_EVENT_CONTEXT, (context) => {
5761
this.#serverEventContext = context;
@@ -73,18 +77,36 @@ export class UmbManagementApiDetailDataRequestManager<
7377
let data: DetailResponseModelType | undefined;
7478
let error: UmbApiError | UmbCancelError | undefined;
7579

80+
const inflightCacheKey = `read:${id}`;
81+
7682
// Only read from the cache when we are connected to the server events
7783
if (this.#isConnectedToServerEvents && this.#dataCache.has(id)) {
7884
data = this.#dataCache.get(id);
7985
} else {
80-
const { data: serverData, error: serverError } = await tryExecute(this, this.#read(id));
86+
const hasInflightRequest = this.#inflightRequestCache.has(inflightCacheKey);
87+
88+
const request = hasInflightRequest
89+
? this.#inflightRequestCache.get(inflightCacheKey)
90+
: tryExecute(this, this.#read(id));
8191

82-
if (this.#isConnectedToServerEvents && serverData) {
83-
this.#dataCache.set(id, serverData);
92+
if (!request) {
93+
throw new Error(`Failed to create or retrieve 'read' request for ID: ${id} (cache key: ${inflightCacheKey}). Aborting read.`);
8494
}
8595

86-
data = serverData;
87-
error = serverError;
96+
this.#inflightRequestCache.set(inflightCacheKey, request);
97+
98+
try {
99+
const { data: serverData, error: serverError } = await request;
100+
101+
if (this.#isConnectedToServerEvents && serverData) {
102+
this.#dataCache.set(id, serverData);
103+
}
104+
105+
data = serverData;
106+
error = serverError;
107+
} finally {
108+
this.#inflightRequestCache.delete(inflightCacheKey);
109+
}
88110
}
89111

90112
return { data, error };
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
export * from './detail-data.request-manager.js';
2-
export * from './cache.js';
31
export * from './cache-invalidation.manager.js';
2+
export * from './cache.js';
3+
export * from './detail-data.request-manager.js';
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
export * from './detail/index.js';
22
export * from './item/index.js';
33
export * from './server-event/constants.js';
4+
export * from './inflight-request/cache.js';
45
export type * from './types.js';
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import type { UmbApiResponse } from '@umbraco-cms/backoffice/resources';
2+
3+
// Keep internal
4+
type RequestResolvedType<ResponseModelType> = UmbApiResponse<{ data?: ResponseModelType }>;
5+
6+
/**
7+
* A cache for inflight requests to the Management Api. Use this class to cache requests and avoid duplicate calls.
8+
* @class UmbManagementApiInflightRequestCache
9+
* @template ResponseModelType
10+
*/
11+
export class UmbManagementApiInflightRequestCache<ResponseModelType> {
12+
#entries = new Map<string, Promise<RequestResolvedType<ResponseModelType>>>();
13+
14+
/**
15+
* Checks if an entry exists in the cache
16+
* @param {string} key - The ID of the entry to check
17+
* @returns {boolean} - True if the entry exists, false otherwise
18+
* @memberof UmbManagementApiInflightRequestCache
19+
*/
20+
has(key: string): boolean {
21+
return this.#entries.has(key);
22+
}
23+
24+
/**
25+
* Adds an entry to the cache
26+
* @param {string} key - A unique key representing the promise
27+
* @param {Promise<UmbApiResponse<RequestResolvedType<ResponseModelType>>>} promise - The promise to cache
28+
* @memberof UmbManagementApiInflightRequestCache
29+
*/
30+
set(key: string, promise: Promise<RequestResolvedType<ResponseModelType>>): void {
31+
this.#entries.set(key, promise);
32+
}
33+
34+
/**
35+
* Retrieves an entry from the cache
36+
* @param {string} key - The ID of the entry to retrieve
37+
* @returns {Promise<RequestResolvedType<ResponseModelType>> | undefined} - The cached promise or undefined if not found
38+
* @memberof UmbManagementApiInflightRequestCache
39+
*/
40+
get(key: string): Promise<RequestResolvedType<ResponseModelType>> | undefined {
41+
return this.#entries.get(key);
42+
}
43+
44+
/**
45+
* Deletes an entry from the cache
46+
* @param {string} key - The ID of the entry to delete
47+
* @memberof UmbManagementApiInflightRequestCache
48+
*/
49+
delete(key: string): void {
50+
this.#entries.delete(key);
51+
}
52+
53+
/**
54+
* Clears all entries from the cache
55+
* @memberof UmbManagementApiInflightRequestCache
56+
*/
57+
clear(): void {
58+
this.#entries.clear();
59+
}
60+
}

0 commit comments

Comments
 (0)