From 7e4705b703d32508dd2232930178a659d1926ca7 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Mon, 10 Aug 2020 06:14:52 +0200 Subject: [PATCH] Create and use omitDefaults --- packages/proto-signing/src/adr27.spec.ts | 116 ++++++++++++++++++--- packages/proto-signing/src/adr27.ts | 24 +++++ packages/proto-signing/src/signing.spec.ts | 18 ++-- packages/proto-signing/types/adr27.d.ts | 4 + 4 files changed, 141 insertions(+), 21 deletions(-) diff --git a/packages/proto-signing/src/adr27.spec.ts b/packages/proto-signing/src/adr27.spec.ts index f8221496..5d7c55e5 100644 --- a/packages/proto-signing/src/adr27.spec.ts +++ b/packages/proto-signing/src/adr27.spec.ts @@ -1,7 +1,7 @@ import { fromHex } from "@cosmjs/encoding"; import { parse } from "protobufjs"; -import { omitDefault } from "./adr27"; +import { omitDefault, omitDefaults } from "./adr27"; describe("adr27", () => { describe("omitDefault", () => { @@ -46,6 +46,94 @@ describe("adr27", () => { expect(omitDefault(Review.values["ACCEPTED"])).toEqual(Review.values["ACCEPTED"]); expect(omitDefault(Review.values["UNSPECIFIED"])).toEqual(null); }); + }); + + describe("omitDefaults", () => { + it("works for scalars", () => { + expect(omitDefaults("abc")).toEqual("abc"); + expect(omitDefaults("")).toEqual(null); + + expect(omitDefaults(fromHex("ab"))).toEqual(fromHex("ab")); + expect(omitDefaults(fromHex(""))).toEqual(null); + + expect(omitDefaults(123)).toEqual(123); + expect(omitDefaults(0)).toEqual(null); + + expect(omitDefaults(1.234)).toEqual(1.234); + expect(omitDefaults(0.0)).toEqual(null); + }); + + it("works for repeaded", () => { + expect(omitDefaults(["a", "b", "c"])).toEqual(["a", "b", "c"]); + expect(omitDefaults([])).toEqual(null); + }); + + it("works for enums", () => { + const proto = ` + package blog; + syntax = "proto3"; + + enum Review { + UNSPECIFIED = 0; + ACCEPTED = 1; + REJECTED = 2; + }; + `; + // eslint-disable-next-line @typescript-eslint/naming-convention + const Review = parse(proto).root.lookupEnum("blog.Review"); + expect(omitDefaults(Review.values["ACCEPTED"])).toEqual(Review.values["ACCEPTED"]); + expect(omitDefaults(Review.values["UNSPECIFIED"])).toEqual(null); + }); + + it("works for objects", () => { + // empty + expect(omitDefaults({})).toEqual({}); + + // simple + expect( + omitDefaults({ + a: "foo", + b: "", + c: 100, + d: 0, + }), + ).toEqual({ + a: "foo", + b: null, + c: 100, + d: null, + }); + + // nested + + expect( + omitDefaults({ + a: { + x: "foo", + y: "", + }, + b: { + x: { + o: 1.2, + p: false, + q: 0, + }, + }, + }), + ).toEqual({ + a: { + x: "foo", + y: null, + }, + b: { + x: { + o: 1.2, + p: null, + q: null, + }, + }, + }); + }); it("can be used to reproduce ADR 027 test vector", () => { const proto = ` @@ -93,18 +181,20 @@ describe("adr27", () => { ); const serialization = Uint8Array.from( - Article.encode({ - title: omitDefault("The world needs change"), - description: omitDefault(""), - created: omitDefault(1596806111080), - updated: omitDefault(0), - public: omitDefault(true), - promoted: omitDefault(false), - type: omitDefault(Type.values["NEWS"]), - review: omitDefault(Review.values["UNSPECIFIED"]), - comments: omitDefault(["Nice one", "Thank you"]), - backlinks: omitDefault([]), - }).finish(), + Article.encode( + omitDefaults({ + title: "The world needs change", + description: "", + created: 1596806111080, + updated: 0, + public: true, + promoted: false, + type: Type.values["NEWS"], + review: Review.values["UNSPECIFIED"], + comments: ["Nice one", "Thank you"], + backlinks: [], + }), + ).finish(), ); expect(serialization).toEqual(expected); }); diff --git a/packages/proto-signing/src/adr27.ts b/packages/proto-signing/src/adr27.ts index 22a57df9..7183fdf3 100644 --- a/packages/proto-signing/src/adr27.ts +++ b/packages/proto-signing/src/adr27.ts @@ -17,3 +17,27 @@ export function omitDefault(input: T): T | null { throw new Error("Input type not supported"); } + +/** + * Walks through a potentially nested object and calls omitDefault on each element. + */ +// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types +export function omitDefaults(input: any): any { + if ( + typeof input === "number" || + typeof input === "boolean" || + typeof input === "string" || + Array.isArray(input) || + isUint8Array(input) + ) { + return omitDefault(input); + } + + return Object.keys(input).reduce( + (accumulator, key) => ({ + ...accumulator, + [key]: omitDefaults(input[key]), + }), + {}, + ); +} diff --git a/packages/proto-signing/src/signing.spec.ts b/packages/proto-signing/src/signing.spec.ts index fd3f2493..2e9ebcc0 100644 --- a/packages/proto-signing/src/signing.spec.ts +++ b/packages/proto-signing/src/signing.spec.ts @@ -2,7 +2,7 @@ import { Bech32, fromBase64, fromHex, toHex } from "@cosmjs/encoding"; import { Secp256k1Wallet } from "@cosmjs/launchpad"; -import { omitDefault } from "./adr27"; +import { omitDefaults } from "./adr27"; import { cosmos } from "./generated/codecimpl"; import { defaultRegistry } from "./msgs"; import { Registry, TxBodyValue } from "./registry"; @@ -149,13 +149,15 @@ describe("signing demo", () => { await Promise.all( testVectors.map(async ({ sequenceNumber, signBytes, signedTxBytes }) => { - const signDoc = SignDoc.create({ - bodyBytes: omitDefault(txBodyBytes), - authInfoBytes: omitDefault(authInfoBytes), - chainId: omitDefault(chainId), - accountNumber: omitDefault(accountNumber), - accountSequence: omitDefault(sequenceNumber), - }); + const signDoc = SignDoc.create( + omitDefaults({ + bodyBytes: txBodyBytes, + authInfoBytes: authInfoBytes, + chainId: chainId, + accountNumber: accountNumber, + accountSequence: sequenceNumber, + }), + ); const signDocBytes = Uint8Array.from(SignDoc.encode(signDoc).finish()); expect(toHex(signDocBytes)).toEqual(signBytes); diff --git a/packages/proto-signing/types/adr27.d.ts b/packages/proto-signing/types/adr27.d.ts index dba1b48b..89e45c68 100644 --- a/packages/proto-signing/types/adr27.d.ts +++ b/packages/proto-signing/types/adr27.d.ts @@ -5,3 +5,7 @@ * @see https://github.com/cosmos/cosmos-sdk/pull/6979 */ export declare function omitDefault(input: T): T | null; +/** + * Walks through a potentially nested object and calls omitDefault on each element. + */ +export declare function omitDefaults(input: any): any;