Skip to content

Commit 3bdc591

Browse files
committed
feat(endpoints): add common behaviors
1 parent 9c0cc8b commit 3bdc591

2 files changed

Lines changed: 81 additions & 0 deletions

File tree

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import { Observable, shareReplay } from 'rxjs';
2+
3+
import { EndpointInvokeConfig, EndpointInvoker } from './invoker';
4+
5+
/**
6+
* Decorator of an {@link EndpointInvoker} instance that
7+
* prepends a prefix to the path of each invocation.
8+
*
9+
* @usageNotes
10+
* ```ts
11+
* provide({
12+
* token: EndpointInvoker,
13+
* useFactory: (
14+
* invoker: EndpointInvoker = inject(HttpClientEndpointInvoker),
15+
* ) => {
16+
* // behaviors are applied in reverse order (last -> first)
17+
* invoker = new PrefixEndpointPath(invoker, "https://my.api/");
18+
* invoker = new CacheEndpointResponse(invoker, c => c.method === 'GET');
19+
* invoker = // other behaviors here...
20+
* return invoker;
21+
* },
22+
* }),
23+
* ```
24+
*
25+
*/
26+
export class PrefixEndpointPath implements EndpointInvoker {
27+
constructor(
28+
private kernel: EndpointInvoker,
29+
private prefix: string,
30+
) {}
31+
32+
invoke<T>(config: EndpointInvokeConfig): Observable<T> {
33+
config.path = this.prefix + config.path;
34+
return this.kernel.invoke(config);
35+
}
36+
}
37+
38+
/**
39+
* Decorator of an {@link EndpointInvoker} instance that
40+
* caches the responses of endpoint invocations when a given criteria is met.
41+
*
42+
* @usageNotes
43+
* ```ts
44+
* provide({
45+
* token: EndpointInvoker,
46+
* useFactory: (
47+
* invoker: EndpointInvoker = inject(HttpClientEndpointInvoker),
48+
* ) => {
49+
* // behaviors are applied in reverse order (last -> first)
50+
* invoker = new PrefixEndpointPath(invoker, "https://my.api/");
51+
* invoker = new CacheEndpointResponse(invoker, c => c.method === 'GET');
52+
* invoker = // other behaviors here...
53+
* return invoker;
54+
* },
55+
* }),
56+
* ```
57+
*/
58+
export class CacheEndpointResponse implements EndpointInvoker {
59+
private cache = new Map<string, Observable<unknown>>();
60+
61+
/**
62+
* @param criteria a function that determines whether
63+
* an endpoint invocation should be cached.
64+
*/
65+
constructor(
66+
private kernel: EndpointInvoker,
67+
private criteria: (config: EndpointInvokeConfig) => boolean,
68+
) {}
69+
70+
invoke<T>(config: EndpointInvokeConfig): Observable<T> {
71+
if (this.criteria(config)) {
72+
const cached = this.cache.get(config.path);
73+
if (cached) return cached as Observable<T>;
74+
const observable = this.kernel.invoke<T>(config).pipe(shareReplay(1));
75+
this.cache.set(config.path, observable);
76+
return observable;
77+
}
78+
return this.kernel.invoke(config);
79+
}
80+
}

packages/endpoints/src/public-api.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
export * from './behaviors';
12
export * from './convenience';
23
export * from './core';
34
export * from './generators';

0 commit comments

Comments
 (0)