Skip to content

Commit a71861f

Browse files
committed
add cache functionality
1 parent 6563b3f commit a71861f

File tree

13 files changed

+411
-18
lines changed

13 files changed

+411
-18
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
],
4242
"dependencies": {
4343
"@ember-nexus/web-sdk": "^0.1.1",
44-
"lru-cache": "^10.0.1",
44+
"lru-cache": "^11.1.0",
4545
"luxon": "^3.4.4",
4646
"reflect-metadata": "^0.2",
4747
"tslib": "^2.8.1",

src/Cache/Cache.ts

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import { LRUCache } from 'lru-cache';
2+
3+
import { CacheEntry } from './CacheEntry.js';
4+
import { LogicError } from '../Error/index.js';
5+
import { ParsedResponse } from '../Type/Definition/Response/index.js';
6+
7+
class Cache<T> {
8+
protected cache: LRUCache<string, CacheEntry<T>>;
9+
10+
public has(key: string): boolean {
11+
return this.cache.has(key);
12+
}
13+
14+
public get(key: string): CacheEntry<T> | undefined {
15+
return this.cache.get(key);
16+
}
17+
18+
public set(key: string, value: CacheEntry<T>): this {
19+
this.cache.set(key, value);
20+
return this;
21+
}
22+
23+
/**
24+
* Sets a cache entry with the given data and optional ETag.
25+
* If no ETag is provided, reuses the previous one if available.
26+
*
27+
* Reusing a known ETag can improve API performance when the data
28+
* is unchanged but the new source lacks ETag metadata.
29+
*/
30+
public setFromDataEtag(key: string, data: T, etag?: string | undefined): this {
31+
const previousCacheEntry = this.cache.get(key);
32+
if (previousCacheEntry && etag === undefined) {
33+
etag = previousCacheEntry.etag;
34+
}
35+
const cacheEntry = {
36+
data: data,
37+
etag: etag,
38+
};
39+
this.cache.set(key, cacheEntry);
40+
return this;
41+
}
42+
43+
public setFromParsedResponse(key: string, parsedResponse: ParsedResponse<T>): this {
44+
const etag = parsedResponse.response.headers.get('ETag');
45+
if (etag === null) {
46+
throw new LogicError('Expected parsedResponse to contain ETag header.');
47+
}
48+
const cacheEntry = {
49+
data: parsedResponse.data,
50+
etag: etag,
51+
};
52+
this.cache.set(key, cacheEntry);
53+
return this;
54+
}
55+
56+
public refresh(_key: string): this {
57+
// todo implement refresh
58+
return this;
59+
}
60+
}
61+
62+
export { Cache };

src/Cache/CacheEntry.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
type CacheEntry<T> = {
2+
data: T;
3+
etag: string | undefined;
4+
};
5+
6+
export { CacheEntry };

src/Cache/ElementCache.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { LRUCache } from 'lru-cache';
2+
3+
import { Cache } from './Cache.js';
4+
import { CacheEntry } from './CacheEntry.js';
5+
import { Collection, Node, Relation, Uuid } from '../Type/Definition/index.js';
6+
import { ServiceIdentifier } from '../Type/Enum/index.js';
7+
8+
class ElementCache extends Cache<Node | Relation> {
9+
static identifier: ServiceIdentifier = ServiceIdentifier.cacheElement;
10+
11+
constructor() {
12+
super();
13+
this.cache = new LRUCache<string, CacheEntry<Node | Relation>>({
14+
max: 500,
15+
});
16+
}
17+
18+
static constructFromServiceResolver(): ElementCache {
19+
return new ElementCache();
20+
}
21+
22+
static createCacheKey(elementId: Uuid): string {
23+
return `${elementId}`;
24+
}
25+
26+
setFromCollection(collection: Collection): this {
27+
for (let i = 0; i < collection.nodes.length; i++) {
28+
this.setFromDataEtag(ElementCache.createCacheKey(collection.nodes[i].id), collection.nodes[i]);
29+
}
30+
for (let i = 0; i < collection.relations.length; i++) {
31+
this.setFromDataEtag(ElementCache.createCacheKey(collection.relations[i].id), collection.relations[i]);
32+
}
33+
return this;
34+
}
35+
}
36+
37+
export { ElementCache };

src/Cache/ElementChildrenCache.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { LRUCache } from 'lru-cache';
2+
3+
import { Cache } from './Cache.js';
4+
import { CacheEntry } from './CacheEntry.js';
5+
import { Collection, Uuid } from '../Type/Definition/index.js';
6+
import { ServiceIdentifier } from '../Type/Enum/index.js';
7+
8+
class ElementChildrenCache extends Cache<Collection> {
9+
static identifier: ServiceIdentifier = ServiceIdentifier.cacheElementChildren;
10+
11+
constructor() {
12+
super();
13+
this.cache = new LRUCache<string, CacheEntry<Collection>>({
14+
max: 50,
15+
});
16+
}
17+
18+
static constructFromServiceResolver(): ElementChildrenCache {
19+
return new ElementChildrenCache();
20+
}
21+
22+
static createCacheKey(parentId: Uuid, page: number, pageSize: number): string {
23+
return `${parentId}-${page}-${pageSize}`;
24+
}
25+
}
26+
27+
export { ElementChildrenCache };

src/Cache/ElementParentsCache.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { LRUCache } from 'lru-cache';
2+
3+
import { Cache } from './Cache.js';
4+
import { CacheEntry } from './CacheEntry.js';
5+
import { Collection, Uuid } from '../Type/Definition/index.js';
6+
import { ServiceIdentifier } from '../Type/Enum/index.js';
7+
8+
class ElementParentsCache extends Cache<Collection> {
9+
static identifier: ServiceIdentifier = ServiceIdentifier.cacheElementParents;
10+
11+
constructor() {
12+
super();
13+
this.cache = new LRUCache<string, CacheEntry<Collection>>({
14+
max: 50,
15+
});
16+
}
17+
18+
static constructFromServiceResolver(): ElementParentsCache {
19+
return new ElementParentsCache();
20+
}
21+
22+
static createCacheKey(childId: Uuid, page: number, pageSize: number): string {
23+
return `${childId}-${page}-${pageSize}`;
24+
}
25+
}
26+
27+
export { ElementParentsCache };

src/Cache/ElementRelatedCache.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { LRUCache } from 'lru-cache';
2+
3+
import { Cache } from './Cache.js';
4+
import { CacheEntry } from './CacheEntry.js';
5+
import { Collection, Uuid } from '../Type/Definition/index.js';
6+
import { ServiceIdentifier } from '../Type/Enum/index.js';
7+
8+
class ElementRelatedCache extends Cache<Collection> {
9+
static identifier: ServiceIdentifier = ServiceIdentifier.cacheElementRelated;
10+
11+
constructor() {
12+
super();
13+
this.cache = new LRUCache<string, CacheEntry<Collection>>({
14+
max: 50,
15+
});
16+
}
17+
18+
static constructFromServiceResolver(): ElementRelatedCache {
19+
return new ElementRelatedCache();
20+
}
21+
22+
static createCacheKey(centerId: Uuid, page: number, pageSize: number): string {
23+
return `${centerId}-${page}-${pageSize}`;
24+
}
25+
}
26+
27+
export { ElementRelatedCache };

src/Cache/IndexCache.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { LRUCache } from 'lru-cache';
2+
3+
import { Cache } from './Cache.js';
4+
import { CacheEntry } from './CacheEntry.js';
5+
import { Collection } from '../Type/Definition/index.js';
6+
import { ServiceIdentifier } from '../Type/Enum/index.js';
7+
8+
class IndexCache extends Cache<Collection> {
9+
static identifier: ServiceIdentifier = ServiceIdentifier.cacheIndex;
10+
11+
constructor() {
12+
super();
13+
this.cache = new LRUCache<string, CacheEntry<Collection>>({
14+
max: 50,
15+
});
16+
}
17+
18+
static constructFromServiceResolver(): IndexCache {
19+
return new IndexCache();
20+
}
21+
22+
static createCacheKey(page: number, pageSize: number): string {
23+
return `${page}-${pageSize}`;
24+
}
25+
}
26+
27+
export { IndexCache };

src/Cache/index.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export * from './Cache.js';
2+
export * from './CacheEntry.js';
3+
export * from './ElementCache.js';
4+
export * from './ElementChildrenCache.js';
5+
export * from './ElementParentsCache.js';
6+
export * from './ElementRelatedCache.js';
7+
export * from './IndexCache.js';

src/Init.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@ import { EmberNexus } from '@ember-nexus/web-sdk/Service';
22
import { Logger } from 'tslog';
33

44
import { GetServiceResolverEvent } from './BrowserEvent/index.js';
5+
import {
6+
ElementCache,
7+
ElementChildrenCache,
8+
ElementParentsCache,
9+
ElementRelatedCache,
10+
IndexCache,
11+
} from './Cache/index.js';
512
import {
613
DeleteElementEndpoint,
714
GetElementChildrenEndpoint,
@@ -89,6 +96,13 @@ function init(rootNode: HTMLElement, emberNexus: EmberNexus | null = null): Serv
8996
PostChangePasswordEndpoint,
9097
PostRegisterEndpoint,
9198
PostTokenEndpoint,
99+
100+
// caches
101+
ElementCache,
102+
ElementChildrenCache,
103+
ElementParentsCache,
104+
ElementRelatedCache,
105+
IndexCache,
92106
];
93107
for (let i = 0; i < services.length; i++) {
94108
serviceResolver.setService(services[i].identifier, services[i].constructFromServiceResolver(serviceResolver));

0 commit comments

Comments
 (0)