Unverified Commit 3542d06a authored by Hong Minhee's avatar Hong Minhee
Browse files

respondWithObject() function

parent b9913c70
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -13,6 +13,12 @@ Version 0.3.0

To be released.

 -  Utility functions for responding with an ActivityPub object.

     -  Added `respondWithObject()` function.
     -  Added `respondWithObjectIfAcceptable()` function.
     -  Added `RespondWithObjectOptions` interface.


Version 0.2.0
-------------
+6 −25
Original line number Diff line number Diff line
import { Handler, PageProps } from "$fresh/server.ts";
import { Head } from "$fresh/runtime.ts";
import { accepts } from "$std/http/mod.ts";
import { respondWithObjectIfAcceptable } from "fedify/federation";
import Comment from "../../components/Comment.tsx";
import Post from "../../components/Post.tsx";
import { federation } from "../../federation/mod.ts";
@@ -30,30 +30,11 @@ export const handler: Handler<PostPageData> = async (req, ctx) => {
  const post = await getPost(ctx.params.uuid);
  if (post == null) return await ctx.renderNotFound();
  const comments = await getComments(post.uuid);
  const accept = accepts(
    req,
    "application/activity+json",
    "application/ld+json",
    "application/json",
    "text/html",
    "application/xhtml+xml",
  );
  if (
    accept === "application/activity+json" ||
    accept === "application/ld+json" || accept === "application/json"
  ) {
  const fedCtx = federation.createContext(req);
  const article = toArticle(fedCtx, blog, post, comments);
    const jsonLd = await article.toJsonLd(fedCtx);
    return new Response(JSON.stringify(jsonLd), {
      headers: {
        "Content-Type": "application/activity+json",
        Link:
          `<${article.id}>; rel="alternate"; type="application/activity+json"`,
        Vary: "Accept",
      },
    });
  }
  const response = await respondWithObjectIfAcceptable(article, req, fedCtx);
  if (response != null) return response;

  const followers = await countFollowers();
  const data: PostPageData = {
    blog,
+69 −2
Original line number Diff line number Diff line
import { assert, assertEquals, assertFalse } from "jsr:@std/assert@^0.218.2";
import { createRequestContext } from "../testing/context.ts";
import { Activity, Create, Person } from "../vocab/vocab.ts";
import { Activity, Create, Note, Person } from "../vocab/vocab.ts";
import {
  ActorDispatcher,
  CollectionCounter,
  CollectionCursor,
  CollectionDispatcher,
} from "./callback.ts";
import { acceptsJsonLd, handleActor, handleCollection } from "./handler.ts";
import {
  acceptsJsonLd,
  handleActor,
  handleCollection,
  respondWithObject,
  respondWithObjectIfAcceptable,
} from "./handler.ts";
import { mockDocumentLoader } from "../testing/docloader.ts";

Deno.test("acceptsJsonLd()", () => {
  assert(acceptsJsonLd(
@@ -390,3 +397,63 @@ Deno.test("handleCollection()", async () => {
  assertEquals(onNotFoundCalled, null);
  assertEquals(onNotAcceptableCalled, null);
});

Deno.test("respondWithObject()", async () => {
  const response = await respondWithObject(
    new Note({
      id: new URL("https://example.com/notes/1"),
      content: "Hello, world!",
    }),
    { documentLoader: mockDocumentLoader },
  );
  assert(response.ok);
  assertEquals(
    response.headers.get("Content-Type"),
    "application/activity+json",
  );
  assertEquals(await response.json(), {
    "@context": "https://www.w3.org/ns/activitystreams",
    id: "https://example.com/notes/1",
    type: "Note",
    content: "Hello, world!",
  });
});

Deno.test("respondWithObjectIfAcceptable", async () => {
  let request = new Request("https://example.com/", {
    headers: { Accept: "application/activity+json" },
  });
  let response = await respondWithObjectIfAcceptable(
    new Note({
      id: new URL("https://example.com/notes/1"),
      content: "Hello, world!",
    }),
    request,
    { documentLoader: mockDocumentLoader },
  );
  assert(response != null);
  assert(response.ok);
  assertEquals(
    response.headers.get("Content-Type"),
    "application/activity+json",
  );
  assertEquals(await response.json(), {
    "@context": "https://www.w3.org/ns/activitystreams",
    id: "https://example.com/notes/1",
    type: "Note",
    content: "Hello, world!",
  });

  request = new Request("https://example.com/", {
    headers: { Accept: "text/html" },
  });
  response = await respondWithObjectIfAcceptable(
    new Note({
      id: new URL("https://example.com/notes/1"),
      content: "Hello, world!",
    }),
    request,
    { documentLoader: mockDocumentLoader },
  );
  assertEquals(response, null);
});
+49 −0
Original line number Diff line number Diff line
import { accepts } from "jsr:@std/http@^0.218.2";
import { doesActorOwnKey, verify } from "../httpsig/mod.ts";
import { DocumentLoader } from "../runtime/docloader.ts";
import {
  Activity,
  Link,
@@ -336,3 +337,51 @@ export async function handleInbox<TContextData>(
    headers: { "Content-Type": "text/plain; charset=utf-8" },
  });
}

/**
 * Options for the {@link respondWithObject} and
 * {@link respondWithObjectIfAcceptable} functions.
 */
export interface RespondWithObjectOptions {
  /**
   * The document loader to use for compacting JSON-LD.
   */
  documentLoader: DocumentLoader;
}

/**
 * Responds with the given object in JSON-LD format.
 *
 * @param object The object to respond with.
 * @param options Options.
 */
export async function respondWithObject(
  object: Object,
  options?: RespondWithObjectOptions,
): Promise<Response> {
  const jsonLd = await object.toJsonLd(options);
  return new Response(JSON.stringify(jsonLd), {
    headers: {
      "Content-Type": "application/activity+json",
    },
  });
}

/**
 * Responds with the given object in JSON-LD format if the request accepts
 * JSON-LD.
 *
 * @param object The object to respond with.
 * @param request The request to check for JSON-LD acceptability.
 * @param options Options.
 */
export async function respondWithObjectIfAcceptable(
  object: Object,
  request: Request,
  options?: RespondWithObjectOptions,
): Promise<Response | null> {
  if (!acceptsJsonLd(request)) return null;
  const response = await respondWithObject(object, options);
  response.headers.set("Vary", "Accept");
  return response;
}
+5 −0
Original line number Diff line number Diff line
@@ -6,5 +6,10 @@
export * from "./callback.ts";
export * from "./collection.ts";
export * from "./context.ts";
export {
  respondWithObject,
  respondWithObjectIfAcceptable,
  type RespondWithObjectOptions,
} from "./handler.ts";
export * from "./middleware.ts";
export * from "./router.ts";