Skip to content

Commit

Permalink
fix(core-api): limit parameter (rollback #3940) (#3953)
Browse files Browse the repository at this point in the history
  • Loading branch information
rainydio authored Aug 12, 2020
1 parent 37ac35e commit 39894a4
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 118 deletions.
9 changes: 9 additions & 0 deletions __tests__/integration/core-api/handlers/transactions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,15 @@ describe("API 2.0 - Transactions", () => {
});
});

describe("GET /transactions?limit=5", () => {
it("should GET few transactions", async () => {
const response = await api.request("GET", "transactions", { limit: 5 });
expect(response).toBeSuccessfulResponse();
expect(response.data.data).toBeArray();
expect(response.data.data.length).toBe(5);
});
});

describe("GET /transactions/:id", () => {
it("should GET a transaction by the given identifier", async () => {
const response = await api.request("GET", `transactions/${transactionId}`);
Expand Down
229 changes: 127 additions & 102 deletions packages/core-api/src/schemas.ts
Original file line number Diff line number Diff line change
@@ -1,113 +1,138 @@
import Joi from "@hapi/joi";

export const pagination = {
page: Joi.number().integer().positive().default(1),
offset: Joi.number().integer().min(0),
limit: Joi.number().integer().min(1).default(100).max(Joi.ref("$configuration.plugins.pagination.limit")),
type SchemaSettings = {
pagination: {
limit: number;
};
};

export const blockId = Joi.alternatives().try(
Joi.string()
export const createSchemas = (settings: SchemaSettings) => {
const pagination = {
page: Joi.number().integer().positive().default(1),
offset: Joi.number().integer().min(0),
limit: Joi.number().integer().min(1).default(100).max(settings.pagination.limit),
};

const blockId = Joi.alternatives().try(
Joi.string()
.min(1)
.max(20)
.regex(/^[0-9]+$/, "decimal non-negative integer"),
Joi.string().length(64).hex(),
);

const address = Joi.string().alphanum().length(34);

const delegateIdentifier = Joi.string()
.regex(/^[a-zA-Z0-9!@$&_.]+$/)
.min(1)
.max(20)
.regex(/^[0-9]+$/, "decimal non-negative integer"),
Joi.string().length(64).hex(),
);

export const address = Joi.string().alphanum().length(34);

export const delegateIdentifier = Joi.string()
.regex(/^[a-zA-Z0-9!@$&_.]+$/)
.min(1)
.max(66);

export const username = Joi.string()
.regex(/^[a-z0-9!@$&_.]+$/)
.min(1)
.max(20);

export const integerBetween = Joi.object().keys({
from: Joi.number().integer().min(0),
to: Joi.number().integer().min(0),
});

export const percentage = Joi.object().keys({
from: Joi.number().precision(2).min(0).max(100),
to: Joi.number().precision(2).min(0).max(100),
});

export const numberFixedOrBetween = Joi.alternatives().try(
Joi.number().integer().min(0),
Joi.object().keys({
from: Joi.number().integer().min(0),
to: Joi.number().integer().min(0),
}),
);
.max(66);

export const walletId = Joi.alternatives().try(
Joi.string()
const username = Joi.string()
.regex(/^[a-z0-9!@$&_.]+$/)
.min(1)
.max(20),
Joi.string().alphanum().length(34),
Joi.string().hex().length(66),
);

export const orderBy = Joi.string().regex(
/^[a-z._]{1,40}:(asc|desc)$/i,
"orderBy query parameter (<iteratee>:<direction>)",
);

export const blocksOrderBy = orderBy.default("height:desc");
export const transactionsOrderBy = orderBy.default("timestamp:desc,sequence:desc");

const equalCriteria = (value: any) => value;
const numericCriteria = (value: any) =>
Joi.alternatives().try(
value,
Joi.object().keys({ from: value }),
Joi.object().keys({ to: value }),
Joi.object().keys({ from: value, to: value }),
.max(20);

const integerBetween = Joi.object().keys({
from: Joi.number().integer().min(0),
to: Joi.number().integer().min(0),
});

const percentage = Joi.object().keys({
from: Joi.number().precision(2).min(0).max(100),
to: Joi.number().precision(2).min(0).max(100),
});

const numberFixedOrBetween = Joi.alternatives().try(
Joi.number().integer().min(0),
Joi.object().keys({
from: Joi.number().integer().min(0),
to: Joi.number().integer().min(0),
}),
);
const likeCriteria = (value: any) => value;
const containsCriteria = (value: any) => value;
const orCriteria = (criteria: any) => Joi.alternatives().try(criteria, Joi.array().items(criteria));
const orEqualCriteria = (value: any) => orCriteria(equalCriteria(value));
const orNumericCriteria = (value: any) => orCriteria(numericCriteria(value));
const orLikeCriteria = (value: any) => orCriteria(likeCriteria(value));
const orContainsCriteria = (value: any) => orCriteria(containsCriteria(value));

export const blockCriteriaSchemas = {
id: orEqualCriteria(blockId),
version: orEqualCriteria(Joi.number().integer().min(0)),
timestamp: orNumericCriteria(Joi.number().integer().min(0)),
previousBlock: orEqualCriteria(blockId),
height: orNumericCriteria(Joi.number().integer().min(0)),
numberOfTransactions: orNumericCriteria(Joi.number().integer().min(0)),
totalAmount: orNumericCriteria(Joi.number().integer().min(0)),
totalFee: orNumericCriteria(Joi.number().integer().min(0)),
reward: orNumericCriteria(Joi.number().integer().min(0)),
payloadLength: orNumericCriteria(Joi.number().integer().min(0)),
payloadHash: orEqualCriteria(Joi.string().hex()),
generatorPublicKey: orEqualCriteria(Joi.string().hex().length(66)),
blockSignature: orEqualCriteria(Joi.string().hex()),
};

export const transactionCriteriaSchemas = {
address: orEqualCriteria(address),
senderId: orEqualCriteria(address),
recipientId: orEqualCriteria(address),
id: orEqualCriteria(Joi.string().hex().length(64)),
version: orEqualCriteria(Joi.number().integer().positive()),
blockId: orEqualCriteria(blockId),
sequence: orNumericCriteria(Joi.number().integer().positive()),
timestamp: orNumericCriteria(Joi.number().integer().min(0)),
nonce: orNumericCriteria(Joi.number().integer().positive()),
senderPublicKey: orEqualCriteria(Joi.string().hex().length(66)),
type: orEqualCriteria(Joi.number().integer().min(0)),
typeGroup: orEqualCriteria(Joi.number().integer().min(0)),
vendorField: orLikeCriteria(Joi.string().max(255, "utf8")),
amount: orNumericCriteria(Joi.number().integer().min(0)),
fee: orNumericCriteria(Joi.number().integer().min(0)),
asset: orContainsCriteria(Joi.object()),
const walletId = Joi.alternatives().try(
Joi.string()
.regex(/^[a-z0-9!@$&_.]+$/)
.min(1)
.max(20),
Joi.string().alphanum().length(34),
Joi.string().hex().length(66),
);

const orderBy = Joi.string().regex(
/^[a-z._]{1,40}:(asc|desc)$/i,
"orderBy query parameter (<iteratee>:<direction>)",
);

const blocksOrderBy = orderBy.default("height:desc");
const transactionsOrderBy = orderBy.default("timestamp:desc,sequence:desc");

const equalCriteria = (value: any) => value;
const numericCriteria = (value: any) =>
Joi.alternatives().try(
value,
Joi.object().keys({ from: value }),
Joi.object().keys({ to: value }),
Joi.object().keys({ from: value, to: value }),
);
const likeCriteria = (value: any) => value;
const containsCriteria = (value: any) => value;
const orCriteria = (criteria: any) => Joi.alternatives().try(criteria, Joi.array().items(criteria));
const orEqualCriteria = (value: any) => orCriteria(equalCriteria(value));
const orNumericCriteria = (value: any) => orCriteria(numericCriteria(value));
const orLikeCriteria = (value: any) => orCriteria(likeCriteria(value));
const orContainsCriteria = (value: any) => orCriteria(containsCriteria(value));

const blockCriteriaSchemas = {
id: orEqualCriteria(blockId),
version: orEqualCriteria(Joi.number().integer().min(0)),
timestamp: orNumericCriteria(Joi.number().integer().min(0)),
previousBlock: orEqualCriteria(blockId),
height: orNumericCriteria(Joi.number().integer().min(0)),
numberOfTransactions: orNumericCriteria(Joi.number().integer().min(0)),
totalAmount: orNumericCriteria(Joi.number().integer().min(0)),
totalFee: orNumericCriteria(Joi.number().integer().min(0)),
reward: orNumericCriteria(Joi.number().integer().min(0)),
payloadLength: orNumericCriteria(Joi.number().integer().min(0)),
payloadHash: orEqualCriteria(Joi.string().hex()),
generatorPublicKey: orEqualCriteria(Joi.string().hex().length(66)),
blockSignature: orEqualCriteria(Joi.string().hex()),
};

const transactionCriteriaSchemas = {
address: orEqualCriteria(address),
senderId: orEqualCriteria(address),
recipientId: orEqualCriteria(address),
id: orEqualCriteria(Joi.string().hex().length(64)),
version: orEqualCriteria(Joi.number().integer().positive()),
blockId: orEqualCriteria(blockId),
sequence: orNumericCriteria(Joi.number().integer().positive()),
timestamp: orNumericCriteria(Joi.number().integer().min(0)),
nonce: orNumericCriteria(Joi.number().integer().positive()),
senderPublicKey: orEqualCriteria(Joi.string().hex().length(66)),
type: orEqualCriteria(Joi.number().integer().min(0)),
typeGroup: orEqualCriteria(Joi.number().integer().min(0)),
vendorField: orLikeCriteria(Joi.string().max(255, "utf8")),
amount: orNumericCriteria(Joi.number().integer().min(0)),
fee: orNumericCriteria(Joi.number().integer().min(0)),
asset: orContainsCriteria(Joi.object()),
};

return {
pagination,
blockId,
address,
delegateIdentifier,
username,
integerBetween,
percentage,
numberFixedOrBetween,
walletId,
orderBy,
blocksOrderBy,
transactionsOrderBy,
blockCriteriaSchemas,
transactionCriteriaSchemas,
};
};
22 changes: 6 additions & 16 deletions packages/core-api/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { badData } from "@hapi/boom";
import { Server as HapiServer, ServerInjectOptions, ServerInjectResponse, ServerRoute } from "@hapi/hapi";
import { readFileSync } from "fs";

import * as Schemas from "./schemas";
import { createSchemas } from "./schemas";

// todo: review the implementation
@Container.injectable()
Expand Down Expand Up @@ -63,7 +63,11 @@ export class Server {
this.server.listener.headersTimeout = timeout;

this.server.app.app = this.app;
this.server.app.schemas = Schemas;
this.server.app.schemas = createSchemas({
pagination: {
limit: this.configuration.getRequired<number>("plugins.pagination.limit"),
},
});

this.server.ext("onPreHandler", (request, h) => {
request.headers["content-type"] = "application/json";
Expand Down Expand Up @@ -162,16 +166,6 @@ export class Server {
options.tls.cert = readFileSync(options.tls.cert).toString();
}

const validateContext = {
configuration: {
plugins: {
pagination: {
limit: this.configuration.getRequired<number>("plugins.pagination.limit"),
},
},
},
};

return {
...{
router: {
Expand All @@ -185,10 +179,6 @@ export class Server {
},
},
validate: {
options: {
context: validateContext,
},

/* istanbul ignore next */
async failAction(request, h, err) {
return badData(err.message);
Expand Down

0 comments on commit 39894a4

Please sign in to comment.