Skip to content

Commit

Permalink
feat: Add more generic serialization
Browse files Browse the repository at this point in the history
Generalize type branding
Add `Date` object serialization and tests
  • Loading branch information
petarvujovic98 committed Sep 23, 2022
1 parent b10bb1d commit d843af6
Show file tree
Hide file tree
Showing 6 changed files with 131 additions and 15 deletions.
28 changes: 22 additions & 6 deletions lib/utils.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

29 changes: 23 additions & 6 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,12 @@ export type PromiseIndex = number | bigint;
export type NearAmount = number | bigint;
export type Register = number | bigint;

const BIGINT_KEY = "bigint";
const BIGINT_BRAND = "tnigib";
const TYPE_KEY = "typeInfo";
enum TypeBrand {
BIGINT = "bigint",
DATE = "date",
}

export const ERR_INCONSISTENT_STATE =
"The collection is an inconsistent state. Did previous smart contract execution terminate unexpectedly?";
export const ERR_INDEX_OUT_OF_BOUNDS = "Index out of bounds";
Expand Down Expand Up @@ -84,11 +88,20 @@ export function serializeValueWithOptions<DataType>(
}

export function serialize(valueToSerialize: unknown): string {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
Date.prototype.toJSON = function () {
return {
value: this.toISOString(),
[TYPE_KEY]: TypeBrand.DATE,
};
};

return JSON.stringify(valueToSerialize, (_, value) => {
if (typeof value === "bigint") {
return {
value: value.toString(),
[BIGINT_KEY]: BIGINT_BRAND,
[TYPE_KEY]: TypeBrand.BIGINT,
};
}

Expand All @@ -102,10 +115,14 @@ export function deserialize(valueToDeserialize: string): unknown {
value !== null &&
typeof value === "object" &&
Object.keys(value).length === 2 &&
Object.keys(value).every((key) => ["value", BIGINT_KEY].includes(key)) &&
value[BIGINT_KEY] === BIGINT_BRAND
Object.keys(value).every((key) => ["value", TYPE_KEY].includes(key))
) {
return BigInt(value["value"]);
switch (value[TYPE_KEY]) {
case TypeBrand.BIGINT:
return BigInt(value["value"]);
case TypeBrand.DATE:
return new Date(value["value"]);
}
}

return value;
Expand Down
51 changes: 51 additions & 0 deletions tests/__tests__/test-date-serialization.ava.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { Worker } from "near-workspaces";
import test from "ava";

test.beforeEach(async (t) => {
// Init the worker and start a Sandbox server
const worker = await Worker.init();

// Prepare sandbox for tests, create accounts, deploy contracts, etx.
const root = worker.rootAccount;

// Create and deploy test contract
const dsContract = await root.devDeploy("build/date-serialization.wasm");

// Save state for test runs
t.context.worker = worker;
t.context.accounts = { root, dsContract };
});

test.afterEach.always(async (t) => {
await t.context.worker.tearDown().catch((error) => {
console.log("Failed to tear down the worker:", error);
});
});

test("get initial date field value", async (t) => {
const { dsContract } = t.context.accounts;
const dateField = await dsContract.view("getDateField");
t.is(dateField, new Date(0).toISOString());
});

test("get date field after set", async (t) => {
const { dsContract } = t.context.accounts;
const dateField = await dsContract.view("getDateField");
t.is(dateField, new Date(0).toISOString());

const newDate = new Date();
await dsContract.call(dsContract, "setDateField", { dateField: newDate });
const afterSet = await dsContract.view("getDateField");
t.is(afterSet, newDate.toISOString());
});

test("get date field in milliseconds", async (t) => {
const { dsContract } = t.context.accounts;
const dateField = await dsContract.view("getDateFieldAsMilliseconds");
t.is(dateField, new Date(0).getTime());

const newDate = new Date();
await dsContract.call(dsContract, "setDateField", { dateField: newDate });
const afterIncrement = await dsContract.view("getDateFieldAsMilliseconds");
t.is(afterIncrement, newDate.getTime());
});
7 changes: 5 additions & 2 deletions tests/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"type": "module",
"scripts": {
"postinstall": "cd .. && yarn link && cd tests && yarn link near-sdk-js",
"build": "yarn build:context-api && yarn build:math-api && yarn build:storage-api && yarn build:log-panic-api && yarn build:promise-api && yarn build:promise-batch-api && yarn build:function-params && yarn build:lookup-map && yarn build:lookup-set && yarn build:unordered-map && yarn build:unordered-set && yarn build:vector && yarn build:bytes && yarn build:typescript && yarn build:public-key && yarn build:near-bindgen && yarn build:payable && yarn build:private && yarn build:highlevel-promise && yarn build:bigint-serialization",
"build": "yarn build:context-api && yarn build:math-api && yarn build:storage-api && yarn build:log-panic-api && yarn build:promise-api && yarn build:promise-batch-api && yarn build:function-params && yarn build:lookup-map && yarn build:lookup-set && yarn build:unordered-map && yarn build:unordered-set && yarn build:vector && yarn build:bytes && yarn build:typescript && yarn build:public-key && yarn build:near-bindgen && yarn build:payable && yarn build:private && yarn build:highlevel-promise && yarn build:bigint-serialization && yarn build:date-serialization",
"build:context-api": "near-sdk-js build src/context_api.js build/context_api.wasm",
"build:math-api": "near-sdk-js build src/math_api.js build/math_api.wasm",
"build:storage-api": "near-sdk-js build src/storage_api.js build/storage_api.wasm",
Expand All @@ -27,6 +27,7 @@
"build:payable": "near-sdk-js build src/decorators/payable.ts build/payable.wasm",
"build:private": "near-sdk-js build src/decorators/private.ts build/private.wasm",
"build:bigint-serialization": "near-sdk-js build src/bigint-serialization.ts build/bigint-serialization.wasm",
"build:date-serialization": "near-sdk-js build src/date-serialization.ts build/date-serialization.wasm",
"test": "ava",
"test:context-api": "ava __tests__/test_context_api.ava.js",
"test:math-api": "ava __tests__/test_math_api.ava.js",
Expand All @@ -46,7 +47,9 @@
"test:near-bindgen": "ava __tests__/decorators/near_bindgen.ava.js",
"test:payable": "ava __tests__/decorators/payable.ava.js",
"test:private": "ava __tests__/decorators/private.ava.js",
"test:bigint-serialization": "ava __tests__/test-bigint-serialization.ava.js"
"test:bigint-serialization": "ava __tests__/test-bigint-serialization.ava.js",
"test:date-serialization": "ava __tests__/test-date-serialization.ava.js",
"test:serialization": "ava __tests__/test-serialization.ava.js"
},
"author": "Near Inc <hello@nearprotocol.com>",
"license": "Apache-2.0",
Expand Down
2 changes: 1 addition & 1 deletion tests/src/bigint-serialization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export class BigIntSerializationTest {

@view({})
getBigintField(): bigint {
near.log(`getStatus: ${this.bigintField}`);
near.log(`getBigintField: ${this.bigintField}`);
return this.bigintField;
}

Expand Down
29 changes: 29 additions & 0 deletions tests/src/date-serialization.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { near, NearBindgen, call, view } from "near-sdk-js";

@NearBindgen({})
export class DateSerializationTest {
dateField: Date;

constructor() {
this.dateField = new Date(0);
}

@view({})
getDateField(): Date {
near.log(`getDateField: ${this.dateField}`);
return this.dateField;
}

@call({})
setDateField(args: { dateField: Date }): void {
const dateField = new Date(args.dateField);
near.log(`setDateField: ${dateField}`);
this.dateField = dateField;
}

@view({})
getDateFieldAsMilliseconds(): number {
near.log(`getDateFieldAsMilliseconds: ${this.dateField.getTime()}`);
return this.dateField.getTime();
}
}

0 comments on commit d843af6

Please sign in to comment.