From d422e289435f9123230d649d89d4dfe27d7fa8a7 Mon Sep 17 00:00:00 2001 From: Jesse Wierzbinski Date: Sun, 25 Aug 2024 16:01:06 +0200 Subject: [PATCH] refactor(federation): :alien: Update rest of federation code to Working Draft 4 --- federation/http.test.ts | 8 ++-- federation/http.ts | 104 +++++++++++++++++++++++++++------------- federation/schemas.ts | 19 +++++++- federation/validator.ts | 91 +++++++++++++++++++++++++++++++++++ 4 files changed, 182 insertions(+), 40 deletions(-) diff --git a/federation/http.test.ts b/federation/http.test.ts index 1e7a9c7..08fcf77 100644 --- a/federation/http.test.ts +++ b/federation/http.test.ts @@ -79,7 +79,7 @@ describe("RequestParserHandler", () => { expect(noteCallback).not.toHaveBeenCalled(); }); - test("Throw on incorrect body type property", async () => { + test("Correctly handle unknown body type property", async () => { const handler = new RequestParserHandler( { type: "DoesntExist", @@ -89,9 +89,7 @@ describe("RequestParserHandler", () => { ); const noteCallback = jest.fn(); - await expect( - handler.parseBody({ note: noteCallback }), - ).rejects.toThrow(); - expect(noteCallback).not.toHaveBeenCalled(); + await handler.parseBody({ unknown: noteCallback }); + expect(noteCallback).toHaveBeenCalled(); }); }); diff --git a/federation/http.ts b/federation/http.ts index f204e8b..05da75b 100644 --- a/federation/http.ts +++ b/federation/http.ts @@ -1,15 +1,18 @@ import type { + Delete, Dislike, - Extension, Follow, FollowAccept, FollowReject, + Group, + InstanceMetadata, Like, Note, - Patch, - ServerMetadata, - Undo, + Reaction, + Share, + Unfollow, User, + Vote, } from "./schemas"; import type { EntityValidator } from "./validator"; @@ -23,10 +26,14 @@ type ParserCallbacks = { user: (user: User) => MaybePromise; like: (like: Like) => MaybePromise; dislike: (dislike: Dislike) => MaybePromise; - undo: (undo: Undo) => MaybePromise; - serverMetadata: (serverMetadata: ServerMetadata) => MaybePromise; - extension: (extension: Extension) => MaybePromise; - patch: (patch: Patch) => MaybePromise; + delete: (undo: Delete) => MaybePromise; + instanceMetadata: (instanceMetadata: InstanceMetadata) => MaybePromise; + group: (group: Group) => MaybePromise; + reaction: (reaction: Reaction) => MaybePromise; + share: (share: Share) => MaybePromise; + vote: (vote: Vote) => MaybePromise; + unfollow: (unfollow: Unfollow) => MaybePromise; + unknown: (data: unknown) => MaybePromise; }; /** @@ -92,15 +99,6 @@ export class RequestParserHandler { break; } - case "Patch": { - const patch = await this.validator.Patch(this.body); - - if (callbacks.patch) { - return await callbacks.patch(patch); - } - - break; - } case "Follow": { const follow = await this.validator.Follow(this.body); @@ -159,41 +157,79 @@ export class RequestParserHandler { break; } - case "Undo": { - const undo = await this.validator.Undo(this.body); + case "Delete": { + // "delete" isn't an allowed variable name + const delete_ = await this.validator.Delete(this.body); - if (callbacks.undo) { - return await callbacks.undo(undo); + if (callbacks.delete) { + return await callbacks.delete(delete_); } break; } - case "ServerMetadata": { - const serverMetadata = await this.validator.ServerMetadata( + case "InstanceMetadata": { + const instanceMetadata = await this.validator.InstanceMetadata( this.body, ); - if (callbacks.serverMetadata) { - return await callbacks.serverMetadata(serverMetadata); + if (callbacks.instanceMetadata) { + return await callbacks.instanceMetadata(instanceMetadata); } break; } - case "Extension": { - const extension = await this.validator.Extension(this.body); + case "Group": { + const group = await this.validator.Group(this.body); - if (callbacks.extension) { - return await callbacks.extension(extension); + if (callbacks.group) { + return await callbacks.group(group); } break; } - default: - throw new Error( - `Invalid type field in body: ${this.body.type}`, - ); + case "Reaction": { + const reaction = await this.validator.Reaction(this.body); + + if (callbacks.reaction) { + return await callbacks.reaction(reaction); + } + + break; + } + case "Share": { + const share = await this.validator.Share(this.body); + + if (callbacks.share) { + return await callbacks.share(share); + } + + break; + } + case "Vote": { + const vote = await this.validator.Vote(this.body); + + if (callbacks.vote) { + return await callbacks.vote(vote); + } + + break; + } + case "Unfollow": { + const unfollow = await this.validator.Unfollow(this.body); + + if (callbacks.unfollow) { + return await callbacks.unfollow(unfollow); + } + + break; + } + default: { + if (callbacks.unknown) { + return await callbacks.unknown(this.body); + } + } } - throw new Error(`Invalid type field in body: ${this.body.type}`); + return undefined as ReturnType; } } diff --git a/federation/schemas.ts b/federation/schemas.ts index 2159ac0..1e29375 100644 --- a/federation/schemas.ts +++ b/federation/schemas.ts @@ -17,9 +17,18 @@ import type { UnfollowSchema, UserSchema, } from "./schemas/base"; -import type { ContentFormatSchema } from "./schemas/content_format"; +import type { + AudioOnlyContentFormatSchema, + ContentFormatSchema, + ImageOnlyContentFormatSchema, + TextOnlyContentFormatSchema, +} from "./schemas/content_format"; import type { ExtensionPropertySchema } from "./schemas/extensions"; import type { CustomEmojiExtensionSchema } from "./schemas/extensions/custom_emojis"; +import type { LikeSchema } from "./schemas/extensions/likes"; +import type { VoteSchema } from "./schemas/extensions/polls"; +import type { ReactionSchema } from "./schemas/extensions/reactions"; +import type { ShareSchema } from "./schemas/extensions/share"; import type { VanityExtensionSchema } from "./schemas/extensions/vanity"; // biome-ignore lint/suspicious/noExplicitAny: Used only as a base type @@ -36,9 +45,17 @@ export type Follow = InferType; export type FollowAccept = InferType; export type FollowReject = InferType; export type ContentFormat = InferType; +export type ImageContentFormat = InferType; +export type TextContentFormat = InferType; +export type AudioContentFormat = InferType; export type CustomEmojiExtension = InferType; export type Entity = InferType; export type Delete = InferType; export type Group = InferType; export type InstanceMetadata = InferType; export type Unfollow = InferType; +export type Like = InferType; +export type Dislike = InferType; +export type Vote = InferType; +export type Reaction = InferType; +export type Share = InferType; diff --git a/federation/validator.ts b/federation/validator.ts index f93e26d..35b1594 100644 --- a/federation/validator.ts +++ b/federation/validator.ts @@ -1,17 +1,25 @@ import type { z } from "zod"; import { fromError } from "zod-validation-error"; import { + DeleteSchema, EntitySchema, FollowAcceptSchema, FollowRejectSchema, FollowSchema, + GroupSchema, + InstanceMetadataSchema, NoteSchema, PublicKeyDataSchema, + UnfollowSchema, UserSchema, } from "./schemas/base"; import { ContentFormatSchema } from "./schemas/content_format"; import { ExtensionPropertySchema } from "./schemas/extensions"; import { CustomEmojiExtensionSchema } from "./schemas/extensions/custom_emojis"; +import { LikeSchema } from "./schemas/extensions/likes"; +import { VoteSchema } from "./schemas/extensions/polls"; +import { ReactionSchema } from "./schemas/extensions/reactions"; +import { ShareSchema } from "./schemas/extensions/share"; import { VanityExtensionSchema } from "./schemas/extensions/vanity"; // biome-ignore lint/suspicious/noExplicitAny: Used only as a base type @@ -174,4 +182,87 @@ export class EntityValidator { ): Promise> { return this.validate(ExtensionPropertySchema, data); } + + /** + * Validates a Delete entity. + * @param data - The data to validate + * @returns A promise that resolves to the validated data. + */ + public Delete(data: unknown): Promise> { + return this.validate(DeleteSchema, data); + } + + /** + * Validates a Group entity. + * @param data - The data to validate + * @returns A promise that resolves to the validated data. + */ + public Group(data: unknown): Promise> { + return this.validate(GroupSchema, data); + } + + /** + * Validates an InstanceMetadata entity. + * @param data - The data to validate + * @returns A promise that resolves to the validated data. + */ + public InstanceMetadata( + data: unknown, + ): Promise> { + return this.validate(InstanceMetadataSchema, data); + } + + /** + * Validates an Unfollow entity. + * @param data - The data to validate + * @returns A promise that resolves to the validated data. + */ + public Unfollow(data: unknown): Promise> { + return this.validate(UnfollowSchema, data); + } + + /** + * Validates a Like entity. + * @param data - The data to validate + * @returns A promise that resolves to the validated data. + */ + public Like(data: unknown): Promise> { + return this.validate(LikeSchema, data); + } + + /** + * Validates a Dislike entity. + * @param data - The data to validate + * @returns A promise that resolves to the validated data. + */ + public Dislike(data: unknown): Promise> { + return this.validate(LikeSchema, data); + } + + /** + * Validates a Vote entity. + * @param data - The data to validate + * @returns A promise that resolves to the validated data. + */ + public Vote(data: unknown): Promise> { + return this.validate(VoteSchema, data); + } + + /** + * Validates a Reaction entity. + * @param data - The data to validate + * @returns A promise that resolves to the validated data. + */ + public Reaction(data: unknown): Promise> { + return this.validate(ReactionSchema, data); + } + + /** + * Validates a Share entity. + * @param data - The data to validate + * @returns A promise that resolves to the validated data. + */ + public Share(data: unknown): Promise> { + return this.validate(ShareSchema, data); + } }