From ae64489b0dbc9a73a9f48aa42b22b77e0b168d19 Mon Sep 17 00:00:00 2001 From: Daniel McCartney Date: Tue, 5 Sep 2023 16:52:45 -0400 Subject: [PATCH] fix: normalize reaction encoding, support legacy form --- .../src/Reaction.test.ts | 40 +++++++++++++++++ .../content-type-reaction/src/Reaction.ts | 45 ++++++++++++------- packages/content-type-reaction/src/index.ts | 2 +- 3 files changed, 69 insertions(+), 18 deletions(-) diff --git a/packages/content-type-reaction/src/Reaction.test.ts b/packages/content-type-reaction/src/Reaction.test.ts index 676fb0f..e0c52a4 100644 --- a/packages/content-type-reaction/src/Reaction.test.ts +++ b/packages/content-type-reaction/src/Reaction.test.ts @@ -11,6 +11,46 @@ describe("ReactionContentType", () => { expect(ContentTypeReaction.versionMinor).toBe(0); }); + it("supports canonical and legacy form", async () => { + let codec = new ReactionCodec(); + + // This is how clients send reactions now. + let canonicalEncoded = { + type: ContentTypeReaction, + content: new TextEncoder().encode( + JSON.stringify({ + action: "added", + content: "smile", + reference: "abc123", + schema: "shortcode", + }), + ), + }; + + // Previously, some clients sent reactions like this. + // So we test here to make sure we can still decode them. + let legacyEncoded = { + type: ContentTypeReaction, + parameters: { + action: "added", + reference: "abc123", + schema: "shortcode", + }, + content: new TextEncoder().encode("smile"), + }; + + let canonical = codec.decode(canonicalEncoded as any); + let legacy = codec.decode(legacyEncoded as any); + expect(canonical.action).toBe("added"); + expect(legacy.action).toBe("added"); + expect(canonical.content).toBe("smile"); + expect(legacy.content).toBe("smile"); + expect(canonical.reference).toBe("abc123"); + expect(legacy.reference).toBe("abc123"); + expect(canonical.schema).toBe("shortcode"); + expect(legacy.schema).toBe("shortcode"); + }); + it("can send a reaction", async () => { const aliceWallet = Wallet.createRandom(); const aliceClient = await Client.create(aliceWallet, { env: "local" }); diff --git a/packages/content-type-reaction/src/Reaction.ts b/packages/content-type-reaction/src/Reaction.ts index 27abe92..f81930d 100644 --- a/packages/content-type-reaction/src/Reaction.ts +++ b/packages/content-type-reaction/src/Reaction.ts @@ -27,9 +27,9 @@ export type Reaction = { schema: "unicode" | "shortcode" | "custom"; }; -export type ReactionParameters = Pick< - Reaction, - "action" | "reference" | "schema" +type LegacyReactionParameters = Pick< + Reaction, + "action" | "reference" | "schema" > & { encoding: "UTF-8"; }; @@ -39,29 +39,40 @@ export class ReactionCodec implements ContentCodec { return ContentTypeReaction; } - encode(content: Reaction): EncodedContent { + encode(reaction: Reaction): EncodedContent { + const { action, reference, schema, content } = reaction; return { type: ContentTypeReaction, - parameters: { - encoding: "UTF-8", - action: content.action, - reference: content.reference, - schema: content.schema, - }, - content: new TextEncoder().encode(content.content), + parameters: {}, + content: new TextEncoder().encode( + JSON.stringify({ action, reference, schema, content }), + ), }; } - decode(content: EncodedContent): Reaction { - const { encoding } = content.parameters; + decode(content: EncodedContent): Reaction { + let text = new TextDecoder().decode(content.content); + + // First try to decode it in the canonical form. + try { + const reaction = JSON.parse(text); + const { action, reference, schema, content } = reaction; + return { action, reference, schema, content }; + } catch (e) { + // ignore, fall through to legacy decoding + } + + // If that fails, try to decode it in the legacy form. + let parameters = content.parameters as LegacyReactionParameters; + const { encoding } = parameters; if (encoding && encoding !== "UTF-8") { throw new Error(`unrecognized encoding ${encoding as string}`); } return { - action: content.parameters.action, - reference: content.parameters.reference, - schema: content.parameters.schema, - content: new TextDecoder().decode(content.content), + action: parameters.action, + reference: parameters.reference, + schema: parameters.schema, + content: text, }; } } diff --git a/packages/content-type-reaction/src/index.ts b/packages/content-type-reaction/src/index.ts index 1b80abf..edc7e20 100644 --- a/packages/content-type-reaction/src/index.ts +++ b/packages/content-type-reaction/src/index.ts @@ -1,2 +1,2 @@ export { ReactionCodec, ContentTypeReaction } from "./Reaction"; -export type { Reaction, ReactionParameters } from "./Reaction"; +export type { Reaction } from "./Reaction";