From 7741e5711aa14655c13a9eeac26e44f24e84c2c5 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 4 Feb 2020 16:43:38 +0100 Subject: [PATCH] Parse and test logs --- packages/sdk/src/logs.spec.ts | 124 ++++++++++++++++++++++++++++ packages/sdk/src/logs.ts | 59 +++++++++++++ packages/sdk/src/restclient.spec.ts | 9 ++ packages/sdk/types/logs.d.ts | 17 ++++ 4 files changed, 209 insertions(+) create mode 100644 packages/sdk/src/logs.spec.ts create mode 100644 packages/sdk/src/logs.ts create mode 100644 packages/sdk/types/logs.d.ts diff --git a/packages/sdk/src/logs.spec.ts b/packages/sdk/src/logs.spec.ts new file mode 100644 index 00000000..a34efd66 --- /dev/null +++ b/packages/sdk/src/logs.spec.ts @@ -0,0 +1,124 @@ +/* eslint-disable @typescript-eslint/camelcase */ +import { parseAttribute, parseEvent, parseLog, parseLogs } from "./logs"; + +describe("logs", () => { + describe("parseAttribute", () => { + it("works", () => { + const attr = parseAttribute({ key: "a", value: "b" }); + expect(attr).toEqual({ key: "a", value: "b" }); + }); + }); + + describe("parseEvent", () => { + it("works", () => { + const original = { + type: "message", + attributes: [ + { + key: "action", + value: "store-code", + }, + { + key: "module", + value: "wasm", + }, + { + key: "action", + value: "store-code", + }, + { + key: "sender", + value: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6", + }, + { + key: "code_id", + value: "1", + }, + ], + } as const; + + const event = parseEvent(original); + expect(event).toEqual(original); + }); + }); + + describe("parseLog", () => { + it("works", () => { + const original = { + msg_index: 0, + log: "", + events: [ + { + type: "message", + attributes: [ + { + key: "action", + value: "store-code", + }, + { + key: "module", + value: "wasm", + }, + { + key: "action", + value: "store-code", + }, + { + key: "sender", + value: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6", + }, + { + key: "code_id", + value: "1", + }, + ], + }, + ], + } as const; + + const log = parseLog(original); + expect(log).toEqual(original); + }); + }); + + describe("parseLogs", () => { + it("works", () => { + const original = [ + { + msg_index: 0, + log: "", + events: [ + { + type: "message", + attributes: [ + { + key: "action", + value: "store-code", + }, + { + key: "module", + value: "wasm", + }, + { + key: "action", + value: "store-code", + }, + { + key: "sender", + value: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6", + }, + { + key: "code_id", + value: "1", + }, + ], + }, + ], + }, + ] as const; + + const logs = parseLogs(original); + expect(logs).toEqual(original); + }); + }); +}); diff --git a/packages/sdk/src/logs.ts b/packages/sdk/src/logs.ts new file mode 100644 index 00000000..4c822dc4 --- /dev/null +++ b/packages/sdk/src/logs.ts @@ -0,0 +1,59 @@ +/* eslint-disable @typescript-eslint/camelcase */ +import { isNonNullObject } from "@iov/encoding"; + +export interface Attribute { + readonly key: string; + readonly value: string; +} + +export interface Event { + readonly type: "message"; + readonly attributes: readonly Attribute[]; +} + +export interface Log { + readonly msg_index: number; + readonly log: string; + readonly events: readonly Event[]; +} + +export function parseAttribute(input: unknown): Attribute { + if (!isNonNullObject(input)) throw new Error("Attribute must be a non-null object"); + const { key, value } = input as any; + if (typeof key !== "string" || typeof value !== "string") { + throw new Error("Attribute is not a key/value pair"); + } + return { + key: key, + value: value, + }; +} + +export function parseEvent(input: unknown): Event { + if (!isNonNullObject(input)) throw new Error("Event must be a non-null object"); + const { type, attributes } = input as any; + if (type !== "message") throw new Error("Event must be of type message"); + if (!Array.isArray(attributes)) throw new Error("Event's attributes must be an array"); + return { + type: type, + attributes: attributes.map(parseAttribute), + }; +} + +export function parseLog(input: unknown): Log { + if (!isNonNullObject(input)) throw new Error("Log must be a non-null object"); + const { msg_index, log, events } = input as any; + if (typeof msg_index !== "number") throw new Error("Log's msg_index must be a number"); + if (typeof log !== "string") throw new Error("Log's log must be a string"); + if (!Array.isArray(events)) throw new Error("Log's events must be an array"); + return { + msg_index: msg_index, + log: log, + events: events.map(parseEvent), + }; +} + +export function parseLogs(input: unknown): readonly Log[] { + if (!Array.isArray(input)) throw new Error("Logs must be an array"); + return input.map(parseLog); +} diff --git a/packages/sdk/src/restclient.spec.ts b/packages/sdk/src/restclient.spec.ts index ade652b4..5b516e7c 100644 --- a/packages/sdk/src/restclient.spec.ts +++ b/packages/sdk/src/restclient.spec.ts @@ -4,6 +4,7 @@ import { Encoding } from "@iov/encoding"; import { HdPaths, Secp256k1HdWallet } from "@iov/keycontrol"; import { encodeSecp256k1Signature, makeSignBytes, marshalTx } from "./encoding"; +import { Log, parseLogs } from "./logs"; import { RestClient } from "./restclient"; import contract from "./testdata/contract.json"; import data from "./testdata/cosmoshub.json"; @@ -25,6 +26,11 @@ function pendingWithoutCosmos(): void { } } +function parseSuccess(rawLog?: string): readonly Log[] { + if (!rawLog) throw new Error("Log missing"); + return parseLogs(JSON.parse(rawLog)); +} + describe("RestClient", () => { it("can be constructed", () => { const client = new RestClient(httpUrl); @@ -153,6 +159,9 @@ describe("RestClient", () => { const result = await client.postTx(postableBytes); // console.log("Raw log:", result.raw_log); expect(result.code).toBeFalsy(); + const [firstLog] = parseSuccess(result.raw_log); + const codeIdAttr = firstLog.events[0].attributes.find(attr => attr.key === "code_id"); + expect(codeIdAttr).toEqual({ key: "code_id", value: "1" }); }); }); }); diff --git a/packages/sdk/types/logs.d.ts b/packages/sdk/types/logs.d.ts new file mode 100644 index 00000000..159ce322 --- /dev/null +++ b/packages/sdk/types/logs.d.ts @@ -0,0 +1,17 @@ +export interface Attribute { + readonly key: string; + readonly value: string; +} +export interface Event { + readonly type: "message"; + readonly attributes: readonly Attribute[]; +} +export interface Log { + readonly msg_index: number; + readonly log: string; + readonly events: readonly Event[]; +} +export declare function parseAttribute(input: unknown): Attribute; +export declare function parseEvent(input: unknown): Event; +export declare function parseLog(input: unknown): Log; +export declare function parseLogs(input: unknown): readonly Log[];