From d8e23e4dad504599a3e1ac3cd317d33d41953f50 Mon Sep 17 00:00:00 2001 From: Quoc-Hao Tran Date: Sun, 12 Dec 2021 10:55:46 -0500 Subject: [PATCH 1/2] Use parser's API support for BigInt - Use parser's API support instead of normalizing bigint/number ourself. Signed-off-by: Quoc-Hao Tran --- tsp-typescript-client/package.json | 5 +- .../src/models/annotation.ts | 22 +- tsp-typescript-client/src/models/bookmark.ts | 11 +- tsp-typescript-client/src/models/entry.ts | 16 +- .../src/models/experiment.ts | 15 +- .../src/models/output-descriptor.ts | 12 +- .../src/models/response/responses.ts | 10 +- tsp-typescript-client/src/models/styles.ts | 6 - tsp-typescript-client/src/models/table.ts | 25 ++- tsp-typescript-client/src/models/timegraph.ts | 42 ++-- tsp-typescript-client/src/models/trace.ts | 11 +- tsp-typescript-client/src/models/xy.ts | 19 +- .../src/protocol/rest-client.ts | 64 ++---- .../src/protocol/serialization.ts | 205 +----------------- .../src/protocol/tsp-client.ts | 56 ++--- yarn.lock | 21 +- 16 files changed, 151 insertions(+), 389 deletions(-) diff --git a/tsp-typescript-client/package.json b/tsp-typescript-client/package.json index d8c55ec..e242d8d 100644 --- a/tsp-typescript-client/package.json +++ b/tsp-typescript-client/package.json @@ -11,7 +11,6 @@ }, "devDependencies": { "@types/jest": "^27.0.1", - "@types/json-bigint": "^1.0.1", "@types/node": "^11.13.8", "@types/node-fetch": "^2.3.3", "jest": "^27.1.0", @@ -22,9 +21,9 @@ "typescript": "latest" }, "dependencies": { - "json-bigint": "sidorares/json-bigint#2c0a5f896d7888e68e5f4ae3c7ea5cd42fd54473", "node-fetch": "^2.5.0", - "rimraf": "latest" + "rimraf": "latest", + "when-json-met-bigint": "^0.21.0" }, "scripts": { "prepare": "yarn run clean && yarn run build", diff --git a/tsp-typescript-client/src/models/annotation.ts b/tsp-typescript-client/src/models/annotation.ts index 83cf453..1948cd2 100644 --- a/tsp-typescript-client/src/models/annotation.ts +++ b/tsp-typescript-client/src/models/annotation.ts @@ -1,4 +1,5 @@ -import { array, assertNumber, createNormalizer, record } from '../protocol/serialization'; +import { Schema } from 'when-json-met-bigint'; +import { assertNumber, bigint } from '../protocol/serialization'; import { OutputElementStyle } from './styles'; export enum Type { @@ -10,12 +11,15 @@ export interface AnnotationCategoriesModel { annotationCategories: string[]; } -export const Annotation = createNormalizer({ - duration: BigInt, - entryId: assertNumber, - time: BigInt, - style: OutputElementStyle, -}); +export const AnnotationSchema: Schema = { + annotations: { + [Symbol.for(`any`)]: [{ + duration: bigint, + entryId: assertNumber, + time: bigint, + }] + } +}; /** * Model for annotation @@ -53,10 +57,6 @@ export interface Annotation { style?: OutputElementStyle; } -export const AnnotationModel = createNormalizer({ - annotations: record(array(Annotation)), -}); - export interface AnnotationModel { annotations: { [category: string]: Annotation[] }; } diff --git a/tsp-typescript-client/src/models/bookmark.ts b/tsp-typescript-client/src/models/bookmark.ts index fd8452e..2db5a94 100644 --- a/tsp-typescript-client/src/models/bookmark.ts +++ b/tsp-typescript-client/src/models/bookmark.ts @@ -1,9 +1,10 @@ -import { createNormalizer } from '../protocol/serialization'; +import { Schema } from 'when-json-met-bigint'; +import { bigint } from '../protocol/serialization'; -export const Bookmark = createNormalizer({ - endTime: BigInt, - startTime: BigInt, -}); +export const BookmarkSchema: Schema = { + endTime: bigint, + startTime: bigint, +}; /** * Model for bookmark diff --git a/tsp-typescript-client/src/models/entry.ts b/tsp-typescript-client/src/models/entry.ts index ec7a3b5..f9ad1c7 100644 --- a/tsp-typescript-client/src/models/entry.ts +++ b/tsp-typescript-client/src/models/entry.ts @@ -1,13 +1,11 @@ -import { array, assertNumber, createNormalizer, Normalizer } from '../protocol/serialization'; +import { Schema } from 'when-json-met-bigint'; +import { assertNumber } from '../protocol/serialization'; import { OutputElementStyle } from './styles'; -export const Entry = createNormalizer({ +export const EntrySchema: Schema = { id: assertNumber, parentId: assertNumber, - style: { - values: undefined, - }, -}); +}; /** * Basic entry interface @@ -54,11 +52,7 @@ export interface EntryHeader { tooltip: string } -export function EntryModel(normalizer: Normalizer): Normalizer> { - return createNormalizer>({ - entries: array(normalizer), - }); -} +export const EntryModelSchema = (schema: Schema): Schema => ({ entries: [schema] }); /** * Entry model that will be returned by the server diff --git a/tsp-typescript-client/src/models/experiment.ts b/tsp-typescript-client/src/models/experiment.ts index ddfc9ae..cff921d 100644 --- a/tsp-typescript-client/src/models/experiment.ts +++ b/tsp-typescript-client/src/models/experiment.ts @@ -1,12 +1,13 @@ -import { array, assertNumber, createNormalizer } from '../protocol/serialization'; -import { Trace } from './trace'; +import { Schema } from 'when-json-met-bigint'; +import { assertNumber, bigint } from '../protocol/serialization'; +import { TraceSchema, Trace } from './trace'; -export const Experiment = createNormalizer({ - end: BigInt, +export const ExperimentSchema: Schema = { + end: bigint, nbEvents: assertNumber, - start: BigInt, - traces: array(Trace), -}); + start: bigint, + traces: [TraceSchema], +}; /** * Model of an experiment that contain one or more traces diff --git a/tsp-typescript-client/src/models/output-descriptor.ts b/tsp-typescript-client/src/models/output-descriptor.ts index 2e703b3..ffe44d7 100644 --- a/tsp-typescript-client/src/models/output-descriptor.ts +++ b/tsp-typescript-client/src/models/output-descriptor.ts @@ -1,10 +1,10 @@ -import { createNormalizer } from '../protocol/serialization'; +import { Schema } from 'when-json-met-bigint'; +import { bigint } from '../protocol/serialization'; -export const OutputDescriptor = createNormalizer({ - end: BigInt, - queryParameters: undefined, - start: BigInt, -}); +export const OutputDescriptorSchema: Schema = { + end: bigint, + start: bigint, +}; /** * Descriptor of a specific output provider diff --git a/tsp-typescript-client/src/models/response/responses.ts b/tsp-typescript-client/src/models/response/responses.ts index 152feaf..0a050ad 100644 --- a/tsp-typescript-client/src/models/response/responses.ts +++ b/tsp-typescript-client/src/models/response/responses.ts @@ -1,4 +1,4 @@ -import { Deserialized, createNormalizer, Normalizer } from '../../protocol/serialization'; +import { Schema } from 'when-json-met-bigint'; /** * Response status @@ -24,13 +24,7 @@ export enum ResponseStatus { CANCELLED = 'CANCELLED' } -export function GenericResponse(): Normalizer>>; -export function GenericResponse(normalizer: Normalizer): Normalizer>; -export function GenericResponse(normalizer?: Normalizer): Normalizer> | Normalizer>> { - return createNormalizer>({ - model: normalizer, - }); -} +export const GenericResponseSchema = (schema: Schema): Schema => ({ model: schema }); /** * Generic response that contains a model diff --git a/tsp-typescript-client/src/models/styles.ts b/tsp-typescript-client/src/models/styles.ts index c5ae19e..eaf271b 100644 --- a/tsp-typescript-client/src/models/styles.ts +++ b/tsp-typescript-client/src/models/styles.ts @@ -1,9 +1,3 @@ -import { createNormalizer } from '../protocol/serialization'; - -export const OutputElementStyle = createNormalizer({ - values: undefined, -}); - /** * Output element style object for one style key. It supports style * inheritance. To avoid creating new styles the element style can have a parent diff --git a/tsp-typescript-client/src/models/table.ts b/tsp-typescript-client/src/models/table.ts index b0c0c0a..6808df5 100644 --- a/tsp-typescript-client/src/models/table.ts +++ b/tsp-typescript-client/src/models/table.ts @@ -1,8 +1,9 @@ -import { array, assertNumber, createNormalizer } from '../protocol/serialization'; +import { Schema } from 'when-json-met-bigint'; +import { assertNumber } from '../protocol/serialization'; -export const ColumnHeaderEntry = createNormalizer({ +export const ColumnHeaderEntrySchema: Schema = { id: assertNumber, -}); +}; /** * Column header @@ -29,9 +30,9 @@ export interface ColumnHeaderEntry { type: string; } -export const Cell = createNormalizer({ +export const CellSchema: Schema = { tags: assertNumber, -}); +}; /** * Cell inside a table line @@ -48,11 +49,11 @@ export interface Cell { tags?: number; } -export const Line = createNormalizer({ - cells: array(Cell), +export const LineSchema = { + cells: [CellSchema], index: assertNumber, tags: assertNumber, -}); +}; /** * Line of a table @@ -74,12 +75,12 @@ export interface Line { tags?: number; } -export const TableModel = createNormalizer({ - columnIds: array(assertNumber), - lines: array(Line), +export const TableModelSchema = { + columnIds: [assertNumber], + lines: [LineSchema], lowIndex: assertNumber, size: assertNumber, -}); +}; /** * Model of a table diff --git a/tsp-typescript-client/src/models/timegraph.ts b/tsp-typescript-client/src/models/timegraph.ts index 856f07f..802468d 100644 --- a/tsp-typescript-client/src/models/timegraph.ts +++ b/tsp-typescript-client/src/models/timegraph.ts @@ -1,14 +1,14 @@ -import { array, assertNumber, createNormalizer } from '../protocol/serialization'; +import { Schema } from 'when-json-met-bigint'; +import { assertNumber, bigint } from '../protocol/serialization'; import { Entry } from './entry'; import { OutputElementStyle } from './styles'; -export const TimeGraphEntry = createNormalizer({ - end: BigInt, +export const TimeGraphEntrySchema: Schema = { + end: bigint, id: assertNumber, parentId: assertNumber, - start: BigInt, - style: OutputElementStyle, -}); + start: bigint, +}; /** * Entry in a time graph @@ -25,12 +25,11 @@ export interface TimeGraphEntry extends Entry { end: bigint; } -const TimeGraphState = createNormalizer({ - end: BigInt, - start: BigInt, +const TimeGraphStateSchema: Schema = { + end: bigint, + start: bigint, tags: assertNumber, - style: OutputElementStyle, -}); +}; /** * Time graph state @@ -62,10 +61,10 @@ export interface TimeGraphState { style?: OutputElementStyle; } -export const TimeGraphRow = createNormalizer({ +export const TimeGraphRowSchema: Schema = { entryId: assertNumber, - states: array(TimeGraphState), -}); + states: [TimeGraphStateSchema], +}; /** * Time graph row described by an array of states for a specific entry @@ -82,9 +81,9 @@ export interface TimeGraphRow { states: TimeGraphState[]; } -export const TimeGraphModel = createNormalizer({ - rows: array(TimeGraphRow), -}); +export const TimeGraphModelSchema: Schema = { + rows: [TimeGraphRowSchema], +}; /** * Time Graph model that will be returned by the server @@ -93,13 +92,12 @@ export interface TimeGraphModel { rows: TimeGraphRow[]; } -export const TimeGraphArrow = createNormalizer({ - end: BigInt, +export const TimeGraphArrowSchema: Schema = { + end: bigint, sourceId: assertNumber, - start: BigInt, + start: bigint, targetId: assertNumber, - style: OutputElementStyle, -}); +}; /** * Arrow for time graph diff --git a/tsp-typescript-client/src/models/trace.ts b/tsp-typescript-client/src/models/trace.ts index ec1d4ba..350ec12 100644 --- a/tsp-typescript-client/src/models/trace.ts +++ b/tsp-typescript-client/src/models/trace.ts @@ -1,10 +1,11 @@ -import { assertNumber, createNormalizer } from '../protocol/serialization'; +import { Schema } from 'when-json-met-bigint'; +import { assertNumber, bigint } from '../protocol/serialization'; -export const Trace = createNormalizer({ - end: BigInt, +export const TraceSchema: Schema = { + end: bigint, nbEvents: assertNumber, - start: BigInt, -}); + start: bigint, +}; /** * Model of a single trace diff --git a/tsp-typescript-client/src/models/xy.ts b/tsp-typescript-client/src/models/xy.ts index 8d2ae66..6996ea8 100644 --- a/tsp-typescript-client/src/models/xy.ts +++ b/tsp-typescript-client/src/models/xy.ts @@ -1,11 +1,12 @@ -import { array, assertNumber, createNormalizer } from '../protocol/serialization'; +import { Schema } from 'when-json-met-bigint'; +import { assertNumber, number } from '../protocol/serialization'; -export const XYSeries = createNormalizer({ +export const XYSeriesSchema: Schema = { seriesId: assertNumber, - xValues: array(Number), // lossy conversion if too big - yValues: array(assertNumber), - tags: array(assertNumber), -}); + xValues: [number], // lossy conversion if too big + yValues: [assertNumber], + tags: [assertNumber], +}; /** * Represent a XY series and its values @@ -47,9 +48,9 @@ export interface XYSeries { tags?: number[]; } -export const XYModel = createNormalizer({ - series: array(XYSeries), -}); +export const XYModelSchema: Schema = { + series: [XYSeriesSchema], +}; /** * Model of a XY chart, contains at least one XY series diff --git a/tsp-typescript-client/src/protocol/rest-client.ts b/tsp-typescript-client/src/protocol/rest-client.ts index c1e1f30..09698f5 100644 --- a/tsp-typescript-client/src/protocol/rest-client.ts +++ b/tsp-typescript-client/src/protocol/rest-client.ts @@ -1,11 +1,6 @@ import fetch from 'node-fetch'; -import { Deserialized, Normalizer } from './serialization'; import { TspClientResponse } from './tsp-client-response'; -import JSONBigConfig = require('json-bigint'); - -const JSONBig = JSONBigConfig({ - useNativeBigInt: true, -}); +import { JSONB as JSONBig, Schema } from "when-json-met-bigint"; export interface HttpRequest { method: string, @@ -27,70 +22,70 @@ export interface HttpResponse { */ export class RestClient { - static get(url: string, parameters?: Map): Promise>>; - static get(url: string, parameters: Map | undefined, normalizer: Normalizer): Promise>; + static get(url: string, parameters?: Map): Promise>; + static get(url: string, parameters: Map | undefined, schema: Schema): Promise>; /** * Perform GET * @template T is the expected type of the json object returned by this request * @param url URL to query without query parameters * @param parameters Query parameters. Mapped keys and values are used to build the final URL */ - static async get(url: string, parameters?: Map, normalizer?: Normalizer) { + static async get(url: string, parameters?: Map, schema?: Schema) { let getUrl = url; if (parameters) { const urlParameters = this.encodeURLParameters(parameters); getUrl = getUrl.concat(urlParameters); } - return this.performRequest('get', getUrl, undefined, normalizer); + return this.performRequest('get', getUrl, undefined, schema); } - static post(url: string, body?: any): Promise>>; - static post(url: string, body: any, normalizer: Normalizer): Promise>; + static post(url: string, body?: any): Promise>; + static post(url: string, body: any, schema: Schema): Promise>; /** * Perform POST * @template T is the expected type of the json object returned by this request * @param url URL to query * @param body Query object as defined by the Query interface */ - static async post(url: string, body?: any, normalizer?: Normalizer) { - return this.performRequest('post', url, body, normalizer); + static async post(url: string, body?: any, schema?: Schema) { + return this.performRequest('post', url, body, schema); } - static put(url: string, body?: any): Promise>>; - static put(url: string, body: any, normalizer: Normalizer): Promise>; + static put(url: string, body?: any): Promise>; + static put(url: string, body: any, schema: Schema): Promise>; /** * Perform PUT * @template T is the expected type of the json object returned by this request * @param url URL to query * @param body Query object as defined by the Query interface */ - static async put(url: string, body?: any, normalizer?: Normalizer) { - return this.performRequest('put', url, body, normalizer); + static async put(url: string, body?: any, schema?: Schema) { + return this.performRequest('put', url, body, schema); } - static delete(url: string, parameters?: Map): Promise>>; - static delete(url: string, parameters: Map | undefined, normalizer: Normalizer): Promise>; + static delete(url: string, parameters?: Map): Promise>; + static delete(url: string, parameters: Map | undefined, schema: Schema): Promise>; /** * Perform DELETE * @template T is the expected type of the json object returned by this request * @param url URL to query without query parameters * @param parameters Query parameters. Mapped keys and values are used to build the final URL */ - static async delete(url: string, parameters?: Map, normalizer?: Normalizer) { + static async delete(url: string, parameters?: Map, schema?: Schema) { let deleteUrl = url; if (parameters) { const urlParameters = this.encodeURLParameters(parameters); deleteUrl = deleteUrl.concat(urlParameters); } - return this.performRequest('delete', deleteUrl, undefined, normalizer); + return this.performRequest('delete', deleteUrl, undefined, schema); } protected static async performRequest( method: string, url: string, body?: any, - normalizer?: Normalizer, - ): Promise>> { + schema?: Schema, + ): Promise> { const response = await this.httpRequest({ url, method, @@ -98,11 +93,9 @@ export class RestClient { 'Accept': 'application/json', 'Content-Type': 'application/json' }, - body: this.jsonStringify(body) + body: JSONBig.stringify(body) }); - const parsed = this.jsonParse(response.text); - const responseModel = normalizer ? normalizer(parsed) : parsed; - return new TspClientResponse(response.text, response.status, response.statusText, responseModel); + return new TspClientResponse(response.text, response.status, response.statusText, JSONBig.parse(response.text, null, schema)); } protected static async httpRequest(req: HttpRequest): Promise { @@ -125,19 +118,4 @@ export class RestClient { } return ''; } - - /** - * Stringify JS objects. Can stringify `BigInt` values. - */ - protected static jsonStringify(data: any): string { - return JSONBig.stringify(data); - } - - /** - * Parse JSON-encoded data. If a number is too large to fit into a regular - * `number` then it will be deserialized as `BigInt`. - */ - protected static jsonParse(text: string): any { - return JSONBig.parse(text); - } } diff --git a/tsp-typescript-client/src/protocol/serialization.ts b/tsp-typescript-client/src/protocol/serialization.ts index c9d0e48..c40cf32 100644 --- a/tsp-typescript-client/src/protocol/serialization.ts +++ b/tsp-typescript-client/src/protocol/serialization.ts @@ -1,202 +1,13 @@ -/** - * @file The whole purpose of this file is to encode the special logic - * required to handle BigInt values in the various datatypes defined - * by the TSP. Node.js doesn't handle BigInt and numbers the same way. - * - * Here are the cases we need to keep in mind: - * 1. If what's supposed to be a BigInt value is too low, it will get - * deserialized as a number. It needs to be converted back into BigInt. - * 2. If what's supposed to be a number value is too high, it will get - * deserialized as a BigInt. We need to throw in this case. - * - * To address (1) we define functions that will force the type of - * fields to be converted into a BigInt. - * - * To address (2) we define functions that will throw an exception - * if we get a BigInt instead of a number when deserializing. - */ - -/** - * Whenever a protocol message contains a numeric field, it may - * be deserialized as `bigint` or as `number` depending on its size. - * `Deserialized` is a mapped type that reflects that behavior. - */ -export type Deserialized = - bigint extends T - ? T | number - : number extends T - ? T | bigint - : T extends object - ? { [K in keyof T]: Deserialized } - : T - ; - -/** - * Given a possibly altered input, get a normalized output. - */ -export type Normalizer = (input: Deserialized) => T; - -/** - * `true` if `bigint` or `number` can be assigned to `T`, `false` otherwise. - */ -export type IsBigIntOrNumber = - bigint extends T - ? true - : number extends T - ? true - : false - ; - -/** - * For `T`, replace by `V` all types that can be assigned `U`. - */ -export type Replace = - U extends T - ? V - : T extends object - ? { [K in keyof T]: Replace } - : T - ; - -/** - * `true` if `T` must be normalized, `false` otherwise. - */ -export type MustBeNormalized = - // Only `any` and `unknown` can be assigned to `unknown`. - // Here we ensure that if an object contains `any` or `unknown` this type will coerce to `true`. - Replace, unknown, 0> extends Replace ? false : true; - -/** - * Mapped type that only keeps properties that need to be normalized. - */ -export type OnlyMustBeNormalized = - MustBeNormalized extends false - ? never // Discard - : T extends any[] // Is T an array? - ? T // Keep - : T extends object // Is T an object? - ? { - // Only keep fields that must be normalized - [K in keyof T as MustBeNormalized extends true ? K : never]-?: OnlyMustBeNormalized; - } - : T // Keep - ; - -/** - * Remove the `undefined` variant from `T`. - */ -export type NonUndefined = T extends undefined ? never : T; - -/** - * Object passed to `createNormalizer` that acts as a template. - */ -export type NormalizerDescriptor< - T, - U = OnlyMustBeNormalized, - V = NonUndefined, - > = - // Any - unknown extends U // Is U any? - ? Normalizer | undefined // U is any. - // BigInt or Number - : IsBigIntOrNumber extends true // Is U bigint or number? - ? Normalizer // U is bigint or number. - // Array - : U extends (infer Z)[] // Is U an array? - ? unknown extends Z // Is U any[]? - ? Normalizer | undefined // U is any[]. - : Normalizer // U is an array that doesn't contain any. - // Object - : U extends object // Is U an object? - ? string extends keyof U // Is U a record? - ? U extends Record // Is U a record of any? - ? Normalizer | undefined // U is a record of any. - : Normalizer // U is a record that doesn't contain any. - // Object with known keys - : Normalizer | { - [K in keyof U]: NormalizerDescriptor - } - // Fall-through - : never // U is none of the above. - ; - -/** - * Create a normalizer function based on `descriptor` for `T` . - * - * General rules for a descriptor: - * - If a field is `any`-like then you should either define a generic normalizer - * for the value or explicitly use `undefined`. - * - Record objects (`{ [key: string]: any }`) are considered `any`-like. - * - Any field that directly or indirectly contains `bigint` or `number` must - * have a normalizer function attached to it. - */ -export function createNormalizer(descriptor: NormalizerDescriptor): Normalizer { - return input => normalize(input, descriptor); -} +import { Schema } from "when-json-met-bigint"; +export const bigint = `bigint`; +export const number = `number`; /** - * Create a deep-copy of `input` while applying normalizers from `descriptor`. + * Throw if `input` is not a `number`. */ -export function normalize(input: Deserialized, descriptor?: NormalizerDescriptor): T { - if (input === undefined) { - // Undefined - return input as T; - } - if (typeof input === 'object') { - if (input === null) { - // Null - return input as T; - } else if (Array.isArray(input)) { - // Array - return typeof descriptor === 'function' - ? descriptor(input as any) // Normalize - : input.map(element => normalize(element)); // Deep-copy - } else { - // Object - if (typeof descriptor === 'function') { - return descriptor(input as unknown as Deserialized>); // Normalize - } - const output: Partial = {}; - for (const [key, value] of Object.entries(input)) { - output[key] = normalize(value, descriptor?.[key]); - } - return output as T; - } +export const assertNumber: Schema = (num) => { + if (typeof num === bigint) { + throw new TypeError(`Expected ${num} to be ${number}, found ${bigint}!`); } - // Primitive - return typeof descriptor === 'function' - ? descriptor(input as unknown as Deserialized>) // Normalize (bigint or number or any) - : input; + return number; } - -/** - * Create a normalizer that operates on JS Array objects. - */ -export function array(normalizer: Normalizer): Normalizer { - return input => input.map(element => normalizer(element)); -} - -/** - * Create a normalizer that converts the values of a JS Object Map (aka Record). - */ -export function record(normalizer: Normalizer): Normalizer> { - return input => { - const output: Record = {}; - for (const [key, value] of Object.entries(input)) { - output[key] = normalizer(value); - } - return output; - }; -} - -/** - * Throw if `input` is not a `number`. - */ -export const assertNumber: Normalizer = - // tslint:disable-next-line: no-shadowed-variable - function assertNumber(input: unknown): number { - if (typeof input !== 'number') { - throw new TypeError(`"${input}" is not a number!`); - } - return input; - }; diff --git a/tsp-typescript-client/src/protocol/tsp-client.ts b/tsp-typescript-client/src/protocol/tsp-client.ts index 3544f74..e8ab230 100644 --- a/tsp-typescript-client/src/protocol/tsp-client.ts +++ b/tsp-typescript-client/src/protocol/tsp-client.ts @@ -1,19 +1,18 @@ import { Query } from '../models/query/query'; -import { GenericResponse } from '../models/response/responses'; -import { XYModel } from '../models/xy'; -import { TimeGraphEntry, TimeGraphArrow, TimeGraphModel } from '../models/timegraph'; -import { AnnotationCategoriesModel, AnnotationModel } from '../models/annotation'; -import { TableModel, ColumnHeaderEntry } from '../models/table'; -import { Trace } from '../models/trace'; +import { GenericResponseSchema, GenericResponse } from '../models/response/responses'; +import { XYModelSchema, XYModel } from '../models/xy'; +import { TimeGraphEntrySchema, TimeGraphArrowSchema, TimeGraphModelSchema, TimeGraphModel, TimeGraphEntry, TimeGraphArrow } from '../models/timegraph'; +import { AnnotationCategoriesModel, AnnotationSchema, AnnotationModel } from '../models/annotation'; +import { TableModelSchema, ColumnHeaderEntrySchema, TableModel, ColumnHeaderEntry } from '../models/table'; +import { TraceSchema, Trace } from '../models/trace'; import { RestClient } from './rest-client'; -import { Experiment } from '../models/experiment'; -import { OutputDescriptor } from '../models/output-descriptor'; -import { EntryModel, Entry } from '../models/entry'; +import { ExperimentSchema, Experiment } from '../models/experiment'; +import { OutputDescriptorSchema, OutputDescriptor } from '../models/output-descriptor'; +import { EntryModelSchema, EntrySchema, EntryModel, Entry } from '../models/entry'; import { TspClientResponse } from './tsp-client-response'; import { OutputStyleModel } from '../models/styles'; import { HealthStatus } from '../models/health'; import { MarkerSet } from '../models/markerset'; -import { array } from './serialization'; /** * Trace Server Protocol client @@ -35,7 +34,7 @@ export class TspClient { */ public async fetchTraces(): Promise> { const url = this.baseUrl + '/traces'; - return RestClient.get(url, undefined, array(Trace)); + return RestClient.get(url, undefined, [TraceSchema]); } /** @@ -44,7 +43,7 @@ export class TspClient { */ public async fetchTrace(traceUUID: string): Promise> { const url = this.baseUrl + '/traces/' + traceUUID; - return RestClient.get(url, undefined, Trace); + return RestClient.get(url, undefined, TraceSchema); } /** @@ -54,7 +53,7 @@ export class TspClient { */ public async openTrace(parameters: Query): Promise> { const url = this.baseUrl + '/traces'; - return RestClient.post(url, parameters, Trace); + return RestClient.post(url, parameters, TraceSchema); } /** @@ -73,7 +72,7 @@ export class TspClient { if (removeCache) { deleteParameters.set('removeCache', removeCache.toString()); } - return RestClient.delete(url, deleteParameters, Trace); + return RestClient.delete(url, deleteParameters, TraceSchema); } /** @@ -82,7 +81,7 @@ export class TspClient { */ public async fetchExperiments(): Promise> { const url = this.baseUrl + '/experiments'; - return RestClient.get(url, undefined, array(Experiment)); + return RestClient.get(url, undefined, [ExperimentSchema]); } /** @@ -92,7 +91,7 @@ export class TspClient { */ public async fetchExperiment(expUUID: string): Promise> { const url = this.baseUrl + '/experiments/' + expUUID; - return RestClient.get(url, undefined, Experiment); + return RestClient.get(url, undefined, ExperimentSchema); } /** @@ -102,7 +101,7 @@ export class TspClient { */ public async createExperiment(parameters: Query): Promise> { const url = this.baseUrl + '/experiments'; - return RestClient.post(url, parameters, Experiment); + return RestClient.post(url, parameters, ExperimentSchema); } /** @@ -113,7 +112,7 @@ export class TspClient { */ public async updateExperiment(expUUID: string, parameters: Query): Promise> { const url = this.baseUrl + '/experiments/' + expUUID; - return RestClient.put(url, parameters, Experiment); + return RestClient.put(url, parameters, ExperimentSchema); } /** @@ -123,7 +122,7 @@ export class TspClient { */ public async deleteExperiment(expUUID: string): Promise> { const url = this.baseUrl + '/experiments/' + expUUID; - return RestClient.delete(url, undefined, Experiment); + return RestClient.delete(url, undefined, ExperimentSchema); } /** @@ -133,7 +132,7 @@ export class TspClient { */ public async experimentOutputs(expUUID: string): Promise> { const url = this.baseUrl + '/experiments/' + expUUID + '/outputs'; - return RestClient.get(url, undefined, array(OutputDescriptor)); + return RestClient.get(url, undefined, [OutputDescriptorSchema]); } /** @@ -149,7 +148,7 @@ export class TspClient { parameters: Query, ): Promise>>> { const url = this.baseUrl + '/experiments/' + expUUID + '/outputs/XY/' + outputID + '/tree'; - return RestClient.post(url, parameters, GenericResponse(EntryModel(Entry))); + return RestClient.post(url, parameters, GenericResponseSchema(EntryModelSchema(EntrySchema))); } /** @@ -165,7 +164,8 @@ export class TspClient { parameters: Query, ): Promise>> { const url = this.baseUrl + '/experiments/' + expUUID + '/outputs/XY/' + outputID + '/xy'; - return RestClient.post(url, parameters, GenericResponse(XYModel)); + console.log(JSON.stringify(GenericResponseSchema(XYModelSchema))); + return RestClient.post(url, parameters, GenericResponseSchema(XYModelSchema)); } /** @@ -210,7 +210,7 @@ export class TspClient { parameters: Query ): Promise>>> { const url = this.baseUrl + '/experiments/' + expUUID + '/outputs/timeGraph/' + outputID + '/tree'; - return RestClient.post(url, parameters, GenericResponse(EntryModel(TimeGraphEntry))); + return RestClient.post(url, parameters, GenericResponseSchema(EntryModelSchema(TimeGraphEntrySchema))); } /** @@ -226,7 +226,7 @@ export class TspClient { parameters: Query ): Promise>> { const url = this.baseUrl + '/experiments/' + expUUID + '/outputs/timeGraph/' + outputID + '/states'; - return RestClient.post(url, parameters, GenericResponse(TimeGraphModel)); + return RestClient.post(url, parameters, GenericResponseSchema(TimeGraphModelSchema)); } /** @@ -242,7 +242,7 @@ export class TspClient { parameters: Query ): Promise>> { const url = this.baseUrl + '/experiments/' + expUUID + '/outputs/timeGraph/' + outputID + '/arrows'; - return RestClient.post(url, parameters, GenericResponse(array(TimeGraphArrow))); + return RestClient.post(url, parameters, GenericResponseSchema([TimeGraphArrowSchema])); } /** @@ -288,7 +288,7 @@ export class TspClient { parameters: Query ): Promise>> { const url = this.baseUrl + '/experiments/' + expUUID + '/outputs/' + outputID + '/annotations'; - return RestClient.post(url, parameters, GenericResponse(AnnotationModel)); + return RestClient.post(url, parameters, GenericResponseSchema(AnnotationSchema)); } /** @@ -320,7 +320,7 @@ export class TspClient { parameters: Query ): Promise>> { const url = this.baseUrl + '/experiments/' + expUUID + '/outputs/table/' + outputID + '/columns'; - return RestClient.post(url, parameters, GenericResponse(array(ColumnHeaderEntry))); + return RestClient.post(url, parameters, GenericResponseSchema([ColumnHeaderEntrySchema])); } /** @@ -336,7 +336,7 @@ export class TspClient { parameters: Query ): Promise>> { const url = this.baseUrl + '/experiments/' + expUUID + '/outputs/table/' + outputID + '/lines'; - return RestClient.post(url, parameters, GenericResponse(TableModel)); + return RestClient.post(url, parameters, GenericResponseSchema(TableModelSchema)); } /** diff --git a/yarn.lock b/yarn.lock index 364ebc0..39f66b0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1467,11 +1467,6 @@ jest-diff "^27.0.0" pretty-format "^27.0.0" -"@types/json-bigint@^1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@types/json-bigint/-/json-bigint-1.0.1.tgz#201062a6990119a8cc18023cfe1fed12fc2fc8a7" - integrity sha512-zpchZLNsNuzJHi6v64UBoFWAvQlPhch7XAi36FkH6tL1bbbmimIF+cS7vwkzY4u5RaSWMoflQfu+TshMPPw8uw== - "@types/minimatch@^3.0.3": version "3.0.5" resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.5.tgz#1001cc5e6a3704b83c236027e77f2f58ea010f40" @@ -1831,11 +1826,6 @@ before-after-hook@^2.2.0: resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.2.tgz#a6e8ca41028d90ee2c24222f201c90956091613e" integrity sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ== -bignumber.js@^9.0.0: - version "9.0.1" - resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.0.1.tgz#8d7ba124c882bfd8e43260c67475518d0689e4e5" - integrity sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA== - brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -3987,12 +3977,6 @@ jsesc@^2.5.1: resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== -json-bigint@sidorares/json-bigint#2c0a5f896d7888e68e5f4ae3c7ea5cd42fd54473: - version "1.0.0" - resolved "https://codeload.github.com/sidorares/json-bigint/tar.gz/2c0a5f896d7888e68e5f4ae3c7ea5cd42fd54473" - dependencies: - bignumber.js "^9.0.0" - json-parse-better-errors@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" @@ -6293,6 +6277,11 @@ whatwg-url@^8.0.0, whatwg-url@^8.4.0, whatwg-url@^8.5.0: tr46 "^2.1.0" webidl-conversions "^6.1.0" +when-json-met-bigint@^0.21.0: + version "0.21.0" + resolved "https://registry.yarnpkg.com/when-json-met-bigint/-/when-json-met-bigint-0.21.0.tgz#cf2d1e78690a84767b073d8c05f2a419df7117b7" + integrity sha512-RZwbeIBslSCWR7oXT03mV2ojwy5A+iO7k55mNsmqrsWnvUqJtmimv4M9wULd8I9jHZj7HnYlW1ZRaQzj2FRjFg== + which-boxed-primitive@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" From 9adc68ec274d51ff177a582452daf1cb3c328531 Mon Sep 17 00:00:00 2001 From: Quoc-Hao Tran Date: Tue, 14 Dec 2021 23:01:53 -0500 Subject: [PATCH 2/2] Add type to schemas Signed-off-by: Quoc-Hao Tran --- tsp-typescript-client/package.json | 2 +- tsp-typescript-client/src/models/annotation.ts | 2 +- tsp-typescript-client/src/models/bookmark.ts | 2 +- tsp-typescript-client/src/models/entry.ts | 4 ++-- tsp-typescript-client/src/models/experiment.ts | 2 +- tsp-typescript-client/src/models/output-descriptor.ts | 2 +- tsp-typescript-client/src/models/response/responses.ts | 2 +- tsp-typescript-client/src/models/table.ts | 8 ++++---- tsp-typescript-client/src/models/timegraph.ts | 10 +++++----- tsp-typescript-client/src/models/trace.ts | 2 +- tsp-typescript-client/src/models/xy.ts | 4 ++-- tsp-typescript-client/src/protocol/serialization.ts | 2 +- tsp-typescript-client/src/protocol/tsp-client.ts | 1 - yarn.lock | 8 ++++---- 14 files changed, 25 insertions(+), 26 deletions(-) diff --git a/tsp-typescript-client/package.json b/tsp-typescript-client/package.json index e242d8d..4dffc3f 100644 --- a/tsp-typescript-client/package.json +++ b/tsp-typescript-client/package.json @@ -23,7 +23,7 @@ "dependencies": { "node-fetch": "^2.5.0", "rimraf": "latest", - "when-json-met-bigint": "^0.21.0" + "when-json-met-bigint": "^0.27.0" }, "scripts": { "prepare": "yarn run clean && yarn run build", diff --git a/tsp-typescript-client/src/models/annotation.ts b/tsp-typescript-client/src/models/annotation.ts index 1948cd2..581ad6a 100644 --- a/tsp-typescript-client/src/models/annotation.ts +++ b/tsp-typescript-client/src/models/annotation.ts @@ -11,7 +11,7 @@ export interface AnnotationCategoriesModel { annotationCategories: string[]; } -export const AnnotationSchema: Schema = { +export const AnnotationSchema: Schema = { annotations: { [Symbol.for(`any`)]: [{ duration: bigint, diff --git a/tsp-typescript-client/src/models/bookmark.ts b/tsp-typescript-client/src/models/bookmark.ts index 2db5a94..d0e6257 100644 --- a/tsp-typescript-client/src/models/bookmark.ts +++ b/tsp-typescript-client/src/models/bookmark.ts @@ -1,7 +1,7 @@ import { Schema } from 'when-json-met-bigint'; import { bigint } from '../protocol/serialization'; -export const BookmarkSchema: Schema = { +export const BookmarkSchema: Schema = { endTime: bigint, startTime: bigint, }; diff --git a/tsp-typescript-client/src/models/entry.ts b/tsp-typescript-client/src/models/entry.ts index f9ad1c7..bd0093d 100644 --- a/tsp-typescript-client/src/models/entry.ts +++ b/tsp-typescript-client/src/models/entry.ts @@ -2,7 +2,7 @@ import { Schema } from 'when-json-met-bigint'; import { assertNumber } from '../protocol/serialization'; import { OutputElementStyle } from './styles'; -export const EntrySchema: Schema = { +export const EntrySchema: Schema = { id: assertNumber, parentId: assertNumber, }; @@ -52,7 +52,7 @@ export interface EntryHeader { tooltip: string } -export const EntryModelSchema = (schema: Schema): Schema => ({ entries: [schema] }); +export const EntryModelSchema = (schema: Schema): Schema> => ({ entries: [schema] }); /** * Entry model that will be returned by the server diff --git a/tsp-typescript-client/src/models/experiment.ts b/tsp-typescript-client/src/models/experiment.ts index cff921d..5825569 100644 --- a/tsp-typescript-client/src/models/experiment.ts +++ b/tsp-typescript-client/src/models/experiment.ts @@ -2,7 +2,7 @@ import { Schema } from 'when-json-met-bigint'; import { assertNumber, bigint } from '../protocol/serialization'; import { TraceSchema, Trace } from './trace'; -export const ExperimentSchema: Schema = { +export const ExperimentSchema: Schema = { end: bigint, nbEvents: assertNumber, start: bigint, diff --git a/tsp-typescript-client/src/models/output-descriptor.ts b/tsp-typescript-client/src/models/output-descriptor.ts index ffe44d7..9bd7e75 100644 --- a/tsp-typescript-client/src/models/output-descriptor.ts +++ b/tsp-typescript-client/src/models/output-descriptor.ts @@ -1,7 +1,7 @@ import { Schema } from 'when-json-met-bigint'; import { bigint } from '../protocol/serialization'; -export const OutputDescriptorSchema: Schema = { +export const OutputDescriptorSchema: Schema = { end: bigint, start: bigint, }; diff --git a/tsp-typescript-client/src/models/response/responses.ts b/tsp-typescript-client/src/models/response/responses.ts index 0a050ad..5ae9a01 100644 --- a/tsp-typescript-client/src/models/response/responses.ts +++ b/tsp-typescript-client/src/models/response/responses.ts @@ -24,7 +24,7 @@ export enum ResponseStatus { CANCELLED = 'CANCELLED' } -export const GenericResponseSchema = (schema: Schema): Schema => ({ model: schema }); +export const GenericResponseSchema = (schema: Schema): Schema> => ({ model: schema }); /** * Generic response that contains a model diff --git a/tsp-typescript-client/src/models/table.ts b/tsp-typescript-client/src/models/table.ts index 6808df5..4611553 100644 --- a/tsp-typescript-client/src/models/table.ts +++ b/tsp-typescript-client/src/models/table.ts @@ -1,7 +1,7 @@ import { Schema } from 'when-json-met-bigint'; import { assertNumber } from '../protocol/serialization'; -export const ColumnHeaderEntrySchema: Schema = { +export const ColumnHeaderEntrySchema: Schema = { id: assertNumber, }; @@ -30,7 +30,7 @@ export interface ColumnHeaderEntry { type: string; } -export const CellSchema: Schema = { +export const CellSchema: Schema = { tags: assertNumber, }; @@ -49,7 +49,7 @@ export interface Cell { tags?: number; } -export const LineSchema = { +export const LineSchema: Schema = { cells: [CellSchema], index: assertNumber, tags: assertNumber, @@ -75,7 +75,7 @@ export interface Line { tags?: number; } -export const TableModelSchema = { +export const TableModelSchema: Schema = { columnIds: [assertNumber], lines: [LineSchema], lowIndex: assertNumber, diff --git a/tsp-typescript-client/src/models/timegraph.ts b/tsp-typescript-client/src/models/timegraph.ts index 802468d..abf062f 100644 --- a/tsp-typescript-client/src/models/timegraph.ts +++ b/tsp-typescript-client/src/models/timegraph.ts @@ -3,7 +3,7 @@ import { assertNumber, bigint } from '../protocol/serialization'; import { Entry } from './entry'; import { OutputElementStyle } from './styles'; -export const TimeGraphEntrySchema: Schema = { +export const TimeGraphEntrySchema: Schema = { end: bigint, id: assertNumber, parentId: assertNumber, @@ -25,7 +25,7 @@ export interface TimeGraphEntry extends Entry { end: bigint; } -const TimeGraphStateSchema: Schema = { +const TimeGraphStateSchema: Schema = { end: bigint, start: bigint, tags: assertNumber, @@ -61,7 +61,7 @@ export interface TimeGraphState { style?: OutputElementStyle; } -export const TimeGraphRowSchema: Schema = { +export const TimeGraphRowSchema: Schema = { entryId: assertNumber, states: [TimeGraphStateSchema], }; @@ -81,7 +81,7 @@ export interface TimeGraphRow { states: TimeGraphState[]; } -export const TimeGraphModelSchema: Schema = { +export const TimeGraphModelSchema: Schema = { rows: [TimeGraphRowSchema], }; @@ -92,7 +92,7 @@ export interface TimeGraphModel { rows: TimeGraphRow[]; } -export const TimeGraphArrowSchema: Schema = { +export const TimeGraphArrowSchema: Schema = { end: bigint, sourceId: assertNumber, start: bigint, diff --git a/tsp-typescript-client/src/models/trace.ts b/tsp-typescript-client/src/models/trace.ts index 350ec12..fb6d9dd 100644 --- a/tsp-typescript-client/src/models/trace.ts +++ b/tsp-typescript-client/src/models/trace.ts @@ -1,7 +1,7 @@ import { Schema } from 'when-json-met-bigint'; import { assertNumber, bigint } from '../protocol/serialization'; -export const TraceSchema: Schema = { +export const TraceSchema: Schema = { end: bigint, nbEvents: assertNumber, start: bigint, diff --git a/tsp-typescript-client/src/models/xy.ts b/tsp-typescript-client/src/models/xy.ts index 6996ea8..bcad95c 100644 --- a/tsp-typescript-client/src/models/xy.ts +++ b/tsp-typescript-client/src/models/xy.ts @@ -1,7 +1,7 @@ import { Schema } from 'when-json-met-bigint'; import { assertNumber, number } from '../protocol/serialization'; -export const XYSeriesSchema: Schema = { +export const XYSeriesSchema: Schema = { seriesId: assertNumber, xValues: [number], // lossy conversion if too big yValues: [assertNumber], @@ -48,7 +48,7 @@ export interface XYSeries { tags?: number[]; } -export const XYModelSchema: Schema = { +export const XYModelSchema: Schema = { series: [XYSeriesSchema], }; diff --git a/tsp-typescript-client/src/protocol/serialization.ts b/tsp-typescript-client/src/protocol/serialization.ts index c40cf32..b50d912 100644 --- a/tsp-typescript-client/src/protocol/serialization.ts +++ b/tsp-typescript-client/src/protocol/serialization.ts @@ -5,7 +5,7 @@ export const number = `number`; /** * Throw if `input` is not a `number`. */ -export const assertNumber: Schema = (num) => { +export const assertNumber: Schema = (num) => { if (typeof num === bigint) { throw new TypeError(`Expected ${num} to be ${number}, found ${bigint}!`); } diff --git a/tsp-typescript-client/src/protocol/tsp-client.ts b/tsp-typescript-client/src/protocol/tsp-client.ts index e8ab230..3430e2a 100644 --- a/tsp-typescript-client/src/protocol/tsp-client.ts +++ b/tsp-typescript-client/src/protocol/tsp-client.ts @@ -164,7 +164,6 @@ export class TspClient { parameters: Query, ): Promise>> { const url = this.baseUrl + '/experiments/' + expUUID + '/outputs/XY/' + outputID + '/xy'; - console.log(JSON.stringify(GenericResponseSchema(XYModelSchema))); return RestClient.post(url, parameters, GenericResponseSchema(XYModelSchema)); } diff --git a/yarn.lock b/yarn.lock index 39f66b0..7f1ce97 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6277,10 +6277,10 @@ whatwg-url@^8.0.0, whatwg-url@^8.4.0, whatwg-url@^8.5.0: tr46 "^2.1.0" webidl-conversions "^6.1.0" -when-json-met-bigint@^0.21.0: - version "0.21.0" - resolved "https://registry.yarnpkg.com/when-json-met-bigint/-/when-json-met-bigint-0.21.0.tgz#cf2d1e78690a84767b073d8c05f2a419df7117b7" - integrity sha512-RZwbeIBslSCWR7oXT03mV2ojwy5A+iO7k55mNsmqrsWnvUqJtmimv4M9wULd8I9jHZj7HnYlW1ZRaQzj2FRjFg== +when-json-met-bigint@^0.27.0: + version "0.27.0" + resolved "https://registry.yarnpkg.com/when-json-met-bigint/-/when-json-met-bigint-0.27.0.tgz#0d28e02fd8af53c83ccf0f403b85e33698e6f878" + integrity sha512-0YsgwxDNDD0WHZvCm4MCCZtO42584C3onB2YY6ujdP4inaJm3vh7ZZnXIb2hQeDinq+sEfDsVL75Lf1CpxsBow== which-boxed-primitive@^1.0.2: version "1.0.2"