Unverified Commit 47e9826f authored by Hong Minhee's avatar Hong Minhee
Browse files

Merge pull request #223 from revathskumar/handle-kvstore-exception

fix(docLoader): handle exception from kvstore and log error message
parents 75372a15 a9930cad
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -73,6 +73,9 @@ To be released.
 -  Updated *uri-template-router* to 0.0.17 which fixes bundler errors on
    Rollup.  [[#221]]

 -  Improved error handling and logging for document loader when KV store
    operations fail.  [[#223] by Revath S Kumar]

 -  Fixed a bug of the `fedify inbox` command where it had failed to render
    the web interface when the `fedify` command was installed using
    `deno install` command from JSR.
@@ -98,6 +101,7 @@ To be released.
[#215]: https://github.com/fedify-dev/fedify/pull/215
[#220]: https://github.com/fedify-dev/fedify/issues/220
[#221]: https://github.com/fedify-dev/fedify/issues/221
[#223]: https://github.com/fedify-dev/fedify/pull/223
[multibase]: https://github.com/multiformats/js-multibase


+44 −0
Original line number Diff line number Diff line
@@ -3,6 +3,7 @@ import { assertEquals, assertRejects, assertThrows } from "@std/assert";
import * as mf from "mock_fetch";
import process from "node:process";
import metadata from "../deno.json" with { type: "json" };
import type { KvKey, KvStore, KvStoreSetOptions } from "../federation/kv.ts";
import { MemoryKvStore } from "../federation/kv.ts";
import { verifyRequest } from "../sig/http.ts";
import { mockDocumentLoader } from "../testing/docloader.ts";
@@ -548,6 +549,49 @@ test("kvCache()", async (t) => {
      "The maximum cache duration is 30 days",
    );
  });

  await t.step("on kv store exception", async () => {
    class KvStoreThrowsException implements KvStore {
      get<T = unknown>(_key: KvKey): Promise<T | undefined> {
        throw new Error("Failed to get key");
      }
      set(
        _key: KvKey,
        _value: unknown,
        _options?: KvStoreSetOptions,
      ): Promise<void> {
        throw new Error("Failed to set key");
      }
      delete(_key: KvKey): Promise<void> {
        throw new Error("Failed to delete key");
      }
    }

    const loader = kvCache({
      kv: new KvStoreThrowsException(),
      loader: mockDocumentLoader,
      rules: [
        ["https://example.org/", Temporal.Duration.from({ days: 1 })],
        [new URL("https://example.net/"), Temporal.Duration.from({ days: 1 })],
        [
          new URLPattern("https://example.com/*"),
          Temporal.Duration.from({ days: 30 }),
        ],
      ],
      prefix: ["_test", "not cached"],
    });
    const result = await loader("https://example.com/object");
    assertEquals(result, {
      contextUrl: null,
      documentUrl: "https://example.com/object",
      document: {
        "@context": "https://www.w3.org/ns/activitystreams",
        id: "https://example.com/object",
        name: "Fetched object",
        type: "Object",
      },
    });
  });
});

test("getUserAgent()", () => {
+19 −2
Original line number Diff line number Diff line
@@ -495,10 +495,27 @@ export function kvCache(
    const match = matchRule(url);
    if (match == null) return await loader(url);
    const key: KvKey = [...keyPrefix, url];
    const cache = await kv.get<RemoteDocument>(key);
    let cache: RemoteDocument | undefined = undefined;
    try {
      cache = await kv.get<RemoteDocument>(key);
    } catch (error) {
      if (error instanceof Error) {
        logger.warn(
          "Failed to get the document of {url} from the KV cache: {error}",
          { url, error },
        );
      }
    }
    if (cache == null) {
      const remoteDoc = await loader(url);
      try {
        await kv.set(key, remoteDoc, { ttl: match });
      } catch (error) {
        logger.warn(
          "Failed to save the document of {url} to the KV cache: {error}",
          { url, error },
        );
      }
      return remoteDoc;
    }
    return cache;