Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(core-database): properly sort BigNumber values #2144

Merged
merged 13 commits into from
Feb 25, 2019
30 changes: 29 additions & 1 deletion packages/core-api/__tests__/v2/handlers/delegates.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { utils } from "../utils";

import { blocks2to100 } from "../../../../core-test-utils/src/fixtures/testnet/blocks2to100";

import { models } from "@arkecosystem/crypto";
import { Bignum, models } from "@arkecosystem/crypto";
const { Block } = models;

import { app } from "@arkecosystem/core-container";
Expand Down Expand Up @@ -38,6 +38,34 @@ describe("API 2.0 - Delegates", () => {
response.data.data.forEach(utils.expectDelegate);
expect(response.data.data.sort((a, b) => a.rank < b.rank)).toEqual(response.data.data);
});

it("should GET all the delegates sorted by votes,asc", async () => {
const wm = app.resolvePlugin("database").walletManager;
const wallet = wm.findByUsername("genesis_51");
wallet.voteBalance = new Bignum(1);
wm.reindex(wallet);

const response = await utils[request]("GET", "delegates", { orderBy: "votes:asc" });
expect(response).toBeSuccessfulResponse();
expect(response.data.data).toBeArray();

expect(response.data.data[0].username).toBe(wallet.username);
expect(response.data.data[0].votes).toBe(+wallet.voteBalance.toFixed());
});

it("should GET all the delegates sorted by votes,desc", async () => {
const wm = app.resolvePlugin("database").walletManager;
const wallet = wm.findByUsername("genesis_1");
wallet.voteBalance = new Bignum(12500000000000000);
wm.reindex(wallet);

const response = await utils[request]("GET", "delegates", { orderBy: "votes:desc" });
expect(response).toBeSuccessfulResponse();
expect(response.data.data).toBeArray();

expect(response.data.data[0].username).toBe(wallet.username);
expect(response.data.data[0].votes).toBe(+wallet.voteBalance.toFixed());
});
},
);

Expand Down
19 changes: 19 additions & 0 deletions packages/core-api/__tests__/v2/handlers/wallets.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import "@arkecosystem/core-test-utils";
import { Bignum } from "@arkecosystem/crypto";
import { setUp, tearDown } from "../../__support__/setup";
import { utils } from "../utils";

Expand Down Expand Up @@ -28,6 +29,24 @@ describe("API 2.0 - Wallets", () => {

utils.expectWallet(response.data.data[0]);
});

it("should GET all the wallets sorted by balance,asc", async () => {
const response = await utils[request]("GET", "wallets", { orderBy: "balance:asc" });
expect(response).toBeSuccessfulResponse();
expect(response.data.data).toBeArray();

expect(response.data.data[0].address).toBe("APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn");
expect(response.data.data[0].balance).toBe(-12500000000000000);
});

it("should GET all the wallets sorted by balance,desc", async () => {
const response = await utils[request]("GET", "wallets", { orderBy: "balance:desc" });
expect(response).toBeSuccessfulResponse();
expect(response.data.data).toBeArray();

expect(response.data.data[0].address).toBe("ANBkoGqWeTSiaEVgVzSKZd3jS7UWzv9PSo");
expect(response.data.data[0].balance).toBe(245100000000000);
});
},
);
});
Expand Down
10 changes: 3 additions & 7 deletions packages/core-api/__tests__/v2/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,17 @@ class Helpers {
"Content-Type": "application/json",
};

const server = app.resolvePlugin("api");

return ApiHelpers.request(server.http, method, url, headers, params);
return ApiHelpers.request(app.resolvePlugin("api").http, method, url, headers, params);
}

public async requestWithAcceptHeader(method, path, params = {}) {
const url = `http://localhost:4003/api/${path}`;
const headers = {
Accept: "application/vnd.core-api.v2+json",
"API-Version": 2,
"Content-Type": "application/json",
};

const server = app.resolvePlugin("api");

return ApiHelpers.request(server.http, method, url, headers, params);
return ApiHelpers.request(app.resolvePlugin("api").http, method, url, headers, params);
}

public expectJson(response) {
Expand Down
2 changes: 1 addition & 1 deletion packages/core-api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
"@arkecosystem/core-transaction-pool": "^2.2.0-beta.7",
"@arkecosystem/core-utils": "^2.2.0-beta.7",
"@arkecosystem/crypto": "^2.2.0-beta.7",
"@arkecosystem/utils": "^0.2.3",
"@arkecosystem/utils": "^0.2.4",
"@faustbrian/hapi-version": "^0.2.11",
"@types/lodash.orderby": "^4.6.4",
"@types/lodash.partition": "^4.6.4",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,8 @@ describe("Wallet Repository", () => {
if (i < 17) {
wallet.vote = vote;
}

wallet.balance = new Bignum(0);
});
walletManager.index(wallets);
});
Expand Down
2 changes: 1 addition & 1 deletion packages/core-database/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
"@arkecosystem/core-interfaces": "^2.2.0-beta.7",
"@arkecosystem/core-utils": "^2.2.0-beta.7",
"@arkecosystem/crypto": "^2.2.0-beta.7",
"@arkecosystem/utils": "^0.2.3",
"@arkecosystem/utils": "^0.2.4",
"@types/lodash.clonedeep": "^4.5.4",
"@types/lodash.compact": "^3.0.4",
"@types/lodash.uniq": "^4.5.4",
Expand Down
16 changes: 13 additions & 3 deletions packages/core-database/src/database-service-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,20 @@ import { DelegatesRepository } from "./repositories/delegates";
import { WalletsRepository } from "./repositories/wallets";

// Allow extenders of core-database to provide, optionally, a IWalletManager concrete in addition to a IDatabaseConnection, but keep the business repos common
export const databaseServiceFactory = async (opts: any, walletManager: Database.IWalletManager, connection: Database.IDatabaseConnection): Promise<Database.IDatabaseService> => {
export const databaseServiceFactory = async (
opts: any,
walletManager: Database.IWalletManager,
connection: Database.IDatabaseConnection,
): Promise<Database.IDatabaseService> => {
let databaseService: DatabaseService;
databaseService = new DatabaseService(opts, connection, walletManager, new WalletsRepository(() => databaseService), new DelegatesRepository(() => databaseService));
databaseService = new DatabaseService(
opts,
connection,
walletManager,
// @ts-ignore
new WalletsRepository(() => databaseService),
new DelegatesRepository(() => databaseService),
);
await databaseService.init();
return databaseService;
};

22 changes: 14 additions & 8 deletions packages/core-database/src/repositories/delegates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Database } from "@arkecosystem/core-interfaces";
import { delegateCalculator } from "@arkecosystem/core-utils";
import { orderBy } from "@arkecosystem/utils";
import limitRows from "./utils/limit-rows";
import { sortEntries } from "./utils/sort-entries";

export class DelegatesRepository implements Database.IDelegatesBusinessRepository {
/**
Expand All @@ -26,12 +27,12 @@ export class DelegatesRepository implements Database.IDelegatesBusinessRepositor
* @return {Object}
*/
public findAll(params: Database.IParameters = {}) {
const delegates = this.getLocalDelegates();
this.applyOrder(params);

const [iteratee, order] = this.__orderBy(params);
const delegates = sortEntries(params, this.getLocalDelegates(), ["rate", "asc"]);

return {
rows: limitRows(orderBy(delegates, [iteratee], [order as "desc" | "asc"]), params),
rows: limitRows(delegates, params),
count: delegates.length,
};
}
Expand Down Expand Up @@ -99,21 +100,26 @@ export class DelegatesRepository implements Database.IDelegatesBusinessRepositor
});
}

public __orderBy(params): string[] {
private applyOrder(params): string {
const assignOrder = (params, value) => (params.orderBy = value.join(":"));

if (!params.orderBy) {
return ["rate", "asc"];
return assignOrder(params, ["rate", "asc"]);
}

const orderByMapped = params.orderBy.split(":").map(p => p.toLowerCase());

if (orderByMapped.length !== 2 || ["desc", "asc"].includes(orderByMapped[1]) !== true) {
return ["rate", "asc"];
return assignOrder(params, ["rate", "asc"]);
}

return [this.__manipulateIteratee(orderByMapped[0]), orderByMapped[1]];
return assignOrder(params, [this.manipulateIteratee(orderByMapped[0]), orderByMapped[1]]);
}

public __manipulateIteratee(iteratee): any {
private manipulateIteratee(iteratee): any {
switch (iteratee) {
case "votes":
return "voteBalance";
case "rank":
return "rate";
case "productivity":
Expand Down
16 changes: 16 additions & 0 deletions packages/core-database/src/repositories/utils/sort-entries.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Database } from "@arkecosystem/core-interfaces";
import { orderBy } from "@arkecosystem/utils";

export function sortEntries(params: Database.IParameters, entries: any[], defaultValue) {
const [iteratee, order] = params.orderBy ? params.orderBy.split(":") : defaultValue;

if (["balance", "voteBalance"].includes(iteratee)) {
return Object.values(entries).sort((a: any, b: any) => {
return order === "asc"
? +a[iteratee].minus(b[iteratee]).toFixed()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use https://mikemcl.github.io/bignumber.js/#cmp for comparing BigNumbers.

: +b[iteratee].minus(a[iteratee]).toFixed();
});
}

return orderBy(entries, [iteratee], [order as "desc" | "asc"]);
}
13 changes: 5 additions & 8 deletions packages/core-database/src/repositories/wallets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Database } from "@arkecosystem/core-interfaces";
import { orderBy } from "@arkecosystem/utils";
import filterRows from "./utils/filter-rows";
import limitRows from "./utils/limit-rows";
import { sortEntries } from "./utils/sort-entries";

export class WalletsRepository implements Database.IWalletsBusinessRepository {
/**
Expand All @@ -24,12 +25,10 @@ export class WalletsRepository implements Database.IWalletsBusinessRepository {
* @return {Object}
*/
public findAll(params: Database.IParameters = {}) {
const wallets = this.all();

const [iteratee, order] = params.orderBy ? params.orderBy.split(":") : ["rate", "asc"];
const wallets = sortEntries(params, this.all(), ["rate", "asc"]);

return {
rows: limitRows(orderBy(wallets, [iteratee], [order as "desc" | "asc"]), params),
rows: limitRows(wallets, params),
count: wallets.length,
};
}
Expand All @@ -43,10 +42,8 @@ export class WalletsRepository implements Database.IWalletsBusinessRepository {
public findAllByVote(publicKey: string, params: Database.IParameters = {}) {
const wallets = this.all().filter(wallet => wallet.vote === publicKey);

const [iteratee, order] = params.orderBy ? params.orderBy.split(":") : ["balance", "desc"];

return {
rows: limitRows(orderBy(wallets, [iteratee], [order as "desc" | "asc"]), params),
rows: limitRows(sortEntries(params, wallets, ["balance", "desc"]), params),
count: wallets.length,
};
}
Expand All @@ -69,7 +66,7 @@ export class WalletsRepository implements Database.IWalletsBusinessRepository {
* Find all wallets sorted by balance.
*/
public top(params: Database.IParameters = {}) {
const wallets = Object.values(this.all()).sort((a: any, b: any) => +b.balance.minus(a.balance).toFixed());
const wallets = sortEntries(params, this.all(), ["balance", "desc"]);

return {
rows: limitRows(wallets, params),
Expand Down
4 changes: 1 addition & 3 deletions packages/core-http-utils/src/server/create-secure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import expandHomeDir from "expand-home-dir";
import { readFileSync } from "fs";
import { createServer } from "./create";

async function createSecureServer(options, callback, secure) {
export async function createSecureServer(options, callback, secure) {
options.host = secure.host;
options.port = secure.port;
options.tls = {
Expand All @@ -12,5 +12,3 @@ async function createSecureServer(options, callback, secure) {

return createServer(options, callback);
}

export { createSecureServer };
4 changes: 1 addition & 3 deletions packages/core-http-utils/src/server/create.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Hapi from "hapi";
import { monitorServer } from "./monitor";

async function createServer(options, callback: any = null) {
export async function createServer(options, callback: any = null) {
const server = new Hapi.Server(options);

await server.register([require("vision"), require("inert"), require("lout")]);
Expand All @@ -21,5 +21,3 @@ async function createServer(options, callback: any = null) {

return server;
}

export { createServer };
4 changes: 1 addition & 3 deletions packages/core-http-utils/src/server/monitor.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
async function monitorServer(server) {
export async function monitorServer(server) {
return server.register({
plugin: require("good"),
options: {
Expand All @@ -18,5 +18,3 @@ async function monitorServer(server) {
},
});
}

export { monitorServer };
4 changes: 1 addition & 3 deletions packages/core-http-utils/src/server/mount.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { app } from "@arkecosystem/core-container";

async function mountServer(name, server) {
export async function mountServer(name, server) {
try {
await server.start();

Expand All @@ -11,5 +11,3 @@ async function mountServer(name, server) {
app.forceExit(`Could not start ${name} Server!`, error);
}
}

export { mountServer };
2 changes: 1 addition & 1 deletion packages/core-jest-matchers/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
"dependencies": {
"@arkecosystem/core-container": "^2.2.0-beta.7",
"@arkecosystem/crypto": "^2.2.0-beta.7",
"@arkecosystem/utils": "^0.2.3",
"@arkecosystem/utils": "^0.2.4",
"@types/bip39": "^2.4.1",
"@types/lodash.get": "^4.4.4",
"@types/lodash.isequal": "^4.5.3",
Expand Down
8 changes: 4 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -135,10 +135,10 @@
webpack-node-externals "^1.7.2"
wif "^2.0.6"

"@arkecosystem/utils@^0.2.3":
version "0.2.3"
resolved "https://registry.yarnpkg.com/@arkecosystem/utils/-/utils-0.2.3.tgz#8e8cac978cd892b6e8d9cd956d60f2e6c4312fd0"
integrity sha512-7DrnQlrPWxIUQA/C1c1Grry0aAkxIwt4uN7aKd0afSkqpwg+rIyByT9N+rwqwYyhSxEZtN6og+iOHXnbNs0epQ==
"@arkecosystem/utils@^0.2.4":
version "0.2.4"
resolved "https://registry.yarnpkg.com/@arkecosystem/utils/-/utils-0.2.4.tgz#1458a1046141adb55e9b8af0a355b00920d35332"
integrity sha512-24+DQLJ42KOJywFiq6oWd7oUJwbUZMRrVUkec8HprwXXza5MjmcIYf62cHqSfikTCxWJdXXKujUaKNd9ZJpPoA==
dependencies:
"@flatten/array" "^1.1.7"
dottie "^2.0.1"
Expand Down