Procházet zdrojové kódy

docs(core): Document the SelfRefreshingCache (#3666)

Drayke před 6 měsíci
rodič
revize
03eb7fb2c9

+ 67 - 0
docs/docs/guides/developer-guide/cache/index.mdx

@@ -258,3 +258,70 @@ which internally just uses whatever the current `CacheStrategy` is to store the
 This means that in most cases you don't need to worry about the session cache, but if you have specific
 This means that in most cases you don't need to worry about the session cache, but if you have specific
 requirements, you can create a custom session cache strategy and set it via the `authOptions.sessionCacheStrategy`
 requirements, you can create a custom session cache strategy and set it via the `authOptions.sessionCacheStrategy`
 config property.
 config property.
+
+## SelfRefreshingCache
+The [SelfRefreshingCache](/reference/typescript-api/cache/self-refreshing-cache) is a specialized in-memory cache which automatically
+refreshes itself if the value is found to be stale. This is useful to cache a single frequently-accessed value, that don't change often.
+
+It is created using the [createSelfRefreshingCache](/reference/typescript-api/cache/self-refreshing-cache#createselfrefreshingcache) function, which takes a configuration object that specifies the
+name of the cache, the time-to-live (TTL) for the cache entries, and a refresh function that will be called to update the value when it is stale.
+
+```ts title="SelfRefreshingCache Example"
+import {
+  Channel,
+  createSelfRefreshingCache,
+  EventBus,
+  InitializerEvent,
+  InternalServerError,
+  Logger,
+  RequestContext
+  SelfRefreshingCache,
+  TransactionalConnection,
+} from '@vendure/core';
+
+@Injectable()
+export class PublicChannelService {
+// highlight-start
+  private publicChannel: SelfRefreshingCache<Channel, [RequestContext]>;
+// highlight-end
+  private readonly logCtx = 'PublicChannelService';
+
+  constructor(
+    private connection: TransactionalConnection,
+    private eventBus: EventBus,
+  ) {
+    this.eventBus.ofType(InitializerEvent).subscribe(async () => {
+      // highlight-start
+      this.publicChannel = await createSelfRefreshingCache({
+        name: 'PublicChannelService.publicChannel',
+        ttl: 1000 * 60 * 5, // 5min
+        refresh: { fn: ctx => this.findPublicChannel(ctx), defaultArgs: [RequestContext.empty()] },
+      });
+      // highlight-end
+    });
+  }
+
+  async getPublicChannel(): Promise<Channel> {
+    // highlight-start
+    const publicChannel = await this.publicChannel.value();
+    // highlight-end
+    if (!publicChannel) {
+      throw new InternalServerError(`error.public-channel-not-found`);
+    }
+    return publicChannel;
+  }
+
+  private async findPublicChannel(ctx: RequestContext): Promise<Channel> {
+    const publicChannel = await this.connection.getRepository(ctx, Channel).findOne({
+      where: { code: DEFAULT_PUBLIC_CHANNEL_CODE },
+      relations: ['defaultShippingZone', 'defaultTaxZone'],
+    });
+
+    if (!publicChannel) {
+      Logger.error('Could not find public channel!', this.logCtx);
+      throw new InternalServerError(`error.public-channel-not-found`);
+    }
+    return publicChannel;
+  }
+}
+```

+ 51 - 2
packages/core/src/common/self-refreshing-cache.ts

@@ -1,10 +1,11 @@
-import { Json } from '@vendure/common/lib/shared-types';
-
 import { Logger } from '../config/logger/vendure-logger';
 import { Logger } from '../config/logger/vendure-logger';
 
 
 /**
 /**
  * @description
  * @description
  * A cache which automatically refreshes itself if the value is found to be stale.
  * A cache which automatically refreshes itself if the value is found to be stale.
+ *
+ * @docsCategory cache
+ * @docsPage SelfRefreshingCache
  */
  */
 export interface SelfRefreshingCache<V, RefreshArgs extends any[] = []> {
 export interface SelfRefreshingCache<V, RefreshArgs extends any[] = []> {
     /**
     /**
@@ -35,9 +36,31 @@ export interface SelfRefreshingCache<V, RefreshArgs extends any[] = []> {
     refresh(...args: RefreshArgs): Promise<V>;
     refresh(...args: RefreshArgs): Promise<V>;
 }
 }
 
 
+/**
+ * @description
+ * Configuration options for creating a {@link SelfRefreshingCache}.
+ *
+ * @docsCategory cache
+ * @docsPage SelfRefreshingCache
+ */
 export interface SelfRefreshingCacheConfig<V, RefreshArgs extends any[]> {
 export interface SelfRefreshingCacheConfig<V, RefreshArgs extends any[]> {
+    /**
+     * @description
+     * The name of the cache, used for logging purposes.
+     * e.g. `'MyService.cachedValue'`.
+     */
     name: string;
     name: string;
+    /**
+     * @description
+     * The time-to-live (ttl) in milliseconds for the cache. After this time, the value will be considered stale
+     * and will be refreshed the next time it is accessed.
+     */
     ttl: number;
     ttl: number;
+    /**
+     * @description
+     * The function which is used to refresh the value of the cache.
+     * This function should return a Promise which resolves to the new value.
+     */
     refresh: {
     refresh: {
         fn: (...args: RefreshArgs) => Promise<V>;
         fn: (...args: RefreshArgs) => Promise<V>;
         /**
         /**
@@ -46,6 +69,7 @@ export interface SelfRefreshingCacheConfig<V, RefreshArgs extends any[]> {
         defaultArgs: RefreshArgs;
         defaultArgs: RefreshArgs;
     };
     };
     /**
     /**
+     * @description
      * Intended for unit testing the SelfRefreshingCache only.
      * Intended for unit testing the SelfRefreshingCache only.
      * By default uses `() => new Date().getTime()`
      * By default uses `() => new Date().getTime()`
      */
      */
@@ -61,6 +85,31 @@ export interface SelfRefreshingCacheConfig<V, RefreshArgs extends any[]> {
  * From there, when the `.value` property is accessed, it will return a value from the cache, and if the
  * From there, when the `.value` property is accessed, it will return a value from the cache, and if the
  * value has expired, it will automatically run the `refreshFn` to update the value and then return the
  * value has expired, it will automatically run the `refreshFn` to update the value and then return the
  * fresh value.
  * fresh value.
+ *
+ * @example
+ * ```ts title="Example of creating a SelfRefreshingCache"
+ * import { createSelfRefreshingCache } from '@vendure/core';
+ *
+ * \@Injectable()
+ * export class PublicChannelService {
+ *   private publicChannel: SelfRefreshingCache<Channel, [RequestContext]>;
+ *
+ *   async init() {
+ *     this.publicChannel = await createSelfRefreshingCache<Channel, [RequestContext]>({
+ *      name: 'PublicChannelService.publicChannel',
+ *      ttl: 1000 * 60 * 60, // 1 hour
+ *      refresh: {
+ *        fn: async (ctx: RequestContext) => {
+ *         return this.channelService.getPublicChannel(ctx);
+ *       },
+ *      defaultArgs: [RequestContext.empty()],
+ *     },
+ *   });
+ * }
+ * ```
+ *
+ * @docsCategory cache
+ * @docsPage SelfRefreshingCache
  */
  */
 export async function createSelfRefreshingCache<V, RefreshArgs extends any[]>(
 export async function createSelfRefreshingCache<V, RefreshArgs extends any[]>(
     config: SelfRefreshingCacheConfig<V, RefreshArgs>,
     config: SelfRefreshingCacheConfig<V, RefreshArgs>,