Skip to content
This repository has been archived by the owner on Jun 10, 2022. It is now read-only.

Commit

Permalink
Allows blocks to be sorted by height (related sort/offset filters)
Browse files Browse the repository at this point in the history
  • Loading branch information
Vektrat authored Jun 15, 2020
1 parent 3674100 commit b8535f5
Show file tree
Hide file tree
Showing 12 changed files with 123 additions and 106 deletions.
23 changes: 13 additions & 10 deletions rest/src/db/CatapultDb.js
Original file line number Diff line number Diff line change
Expand Up @@ -214,24 +214,27 @@ class CatapultDb {
* @param {Uint8Array} signerPublicKey Filters by signer public key
* @param {Uint8Array} beneficiaryAddress Filters by beneficiary address
* @param {object} options Options for ordering and pagination. Can have an `offset`, and must contain the `sortField`, `sortDirection`,
* `pageSize` and `pageNumber`.
* `pageSize` and `pageNumber`. 'sortField' must be within allowed 'sortingOptions'.
* @returns {Promise.<object>} Blocks page.
*/
blocks(signerPublicKey, beneficiaryAddress, options) {
const sortingOptions = {
id: '_id',
height: 'block.height'
};

const conditions = [];

// it is assumed that sortField will always be an `id` for now - this will need to be redesigned when it gets upgraded
// in fact, offset logic should be moved to `queryPagedDocuments`
if (options.offset)
conditions.push({ [options.sortField]: { [1 === options.sortDirection ? '$gt' : '$lt']: new ObjectId(options.offset) } });
conditions.push({ [sortingOptions[options.sortField]]: { [1 === options.sortDirection ? '$gt' : '$lt']: options.offset } });

if (signerPublicKey)
conditions.push({ 'block.signerPublicKey': Buffer.from(signerPublicKey) });

if (beneficiaryAddress)
conditions.push({ 'block.beneficiaryAddress': Buffer.from(beneficiaryAddress) });

const sortConditions = { $sort: { [options.sortField]: options.sortDirection } };
const sortConditions = { $sort: { [sortingOptions[options.sortField]]: options.sortDirection } };

return this.queryPagedDocuments_2(conditions, [], sortConditions, 'blocks', options);
}
Expand Down Expand Up @@ -367,10 +370,12 @@ class CatapultDb {
* @param {object} filters Filters to be applied: `address` for an involved address in the query, `signerPublicKey`, `recipientAddress`,
* `group`, `height`, `embedded`, `transactionTypes` array of uint. If `address` is provided, other account related filters are omitted.
* @param {object} options Options for ordering and pagination. Can have an `offset`, and must contain the `sortField`, `sortDirection`,
* `pageSize` and `pageNumber`.
* `pageSize` and `pageNumber`. 'sortField' must be within allowed 'sortingOptions'.
* @returns {Promise.<object>} Transactions page.
*/
transactions(filters, options) {
const sortingOptions = { id: '_id' };

const getCollectionName = (transactionStatus = 'confirmed') => {
const collectionNames = {
confirmed: 'transactions',
Expand Down Expand Up @@ -405,10 +410,8 @@ class CatapultDb {
const buildConditions = () => {
const conditions = [];

// it is assumed that sortField will always be an `id` for now - this will need to be redesigned when it gets upgraded
// in fact, offset logic should be moved to `queryPagedDocuments`
if (options.offset)
conditions.push({ [options.sortField]: { [1 === options.sortDirection ? '$gt' : '$lt']: new ObjectId(options.offset) } });
conditions.push({ [sortingOptions[options.sortField]]: { [1 === options.sortDirection ? '$gt' : '$lt']: options.offset } });

if (filters.height)
conditions.push({ 'meta.height': convertToLong(filters.height) });
Expand All @@ -427,7 +430,7 @@ class CatapultDb {
};

const removedFields = ['meta.addresses'];
const sortConditions = { $sort: { [options.sortField]: options.sortDirection } };
const sortConditions = { $sort: { [sortingOptions[options.sortField]]: options.sortDirection } };
const conditions = buildConditions();

return this.queryPagedDocuments_2(conditions, removedFields, sortConditions, collectionName, options);
Expand Down
12 changes: 6 additions & 6 deletions rest/src/plugins/mosaic/MosaicDb.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

const MongoDb = require('mongodb');

const { Long, ObjectId } = MongoDb;
const { Long } = MongoDb;


class MosaicDb {
Expand All @@ -36,21 +36,21 @@ class MosaicDb {
* Retrieves filtered and paginated mosaics.
* @param {Uint8Array} ownerAddress Mosaic owner address
* @param {object} options Options for ordering and pagination. Can have an `offset`, and must contain the `sortField`, `sortDirection`,
* `pageSize` and `pageNumber`.
* `pageSize` and `pageNumber`. 'sortField' must be within allowed 'sortingOptions'.
* @returns {Promise.<object>} Mosaics page.
*/
mosaics(ownerAddress, options) {
const sortingOptions = { id: '_id' };

const conditions = [];

// it is assumed that sortField will always be an `id` for now - this will need to be redesigned when it gets upgraded
// in fact, offset logic should be moved to `queryPagedDocuments`
if (options.offset)
conditions.push({ [options.sortField]: { [1 === options.sortDirection ? '$gt' : '$lt']: new ObjectId(options.offset) } });
conditions.push({ [sortingOptions[options.sortField]]: { [1 === options.sortDirection ? '$gt' : '$lt']: options.offset } });

if (ownerAddress)
conditions.push({ 'mosaic.ownerAddress': Buffer.from(ownerAddress) });

const sortConditions = { $sort: { [options.sortField]: options.sortDirection } };
const sortConditions = { $sort: { [sortingOptions[options.sortField]]: options.sortDirection } };
return this.catapultDb.queryPagedDocuments_2(conditions, [], sortConditions, 'mosaics', options);
}

Expand Down
2 changes: 1 addition & 1 deletion rest/src/plugins/mosaic/mosaicRoutes.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ module.exports = {
server.get('/mosaics', (req, res, next) => {
const ownerAddress = req.params.ownerAddress ? routeUtils.parseArgument(req.params, 'ownerAddress', 'address') : undefined;

const options = routeUtils.parsePaginationArguments(req.params, services.config.pageSize, ['_id']);
const options = routeUtils.parsePaginationArguments(req.params, services.config.pageSize, { id: 'objectId' });

return db.mosaics(ownerAddress, options)
.then(result => mosaicSender.sendPage(res, next)(result));
Expand Down
6 changes: 5 additions & 1 deletion rest/src/routes/blockRoutes.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,11 @@ module.exports = {
? routeUtils.parseArgument(params, 'beneficiaryAddress', 'address')
: undefined;

const options = routeUtils.parsePaginationArguments(params, services.config.pageSize, ['_id']);
const offsetParsers = {
id: 'objectId',
height: 'uint64'
};
const options = routeUtils.parsePaginationArguments(params, services.config.pageSize, offsetParsers);

return db.blocks(signerPublicKey, beneficiaryAddress, options)
.then(result => routeUtils.createSender(routeResultTypes.block).sendPage(res, next)(result));
Expand Down
18 changes: 9 additions & 9 deletions rest/src/routes/routeUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -161,17 +161,17 @@ const routeUtils = {
* Parses pagination arguments and throws an invalid argument error if any is invalid.
* @param {object} args Arguments to parse.
* @param {object} optionsPageSize Page size options.
* @param {object} allowedSortFields Sort fields this endpoint allows, will match provided `sortField` and throw if invalid. Must have
* at least one value, and the first is treated as default if no `sortField` is provided.
* @param {object} offsetParsers Sort fields with the related offset parser this endpoint allows, will match provided `sortField` and
* throw if invalid. Must have at least one entry, and `id` is treated as default if no `sortField` is provided.
* @returns {object} Parsed pagination options.
*/
parsePaginationArguments: (args, optionsPageSize, allowedSortFields) => {
if (args.sortField && !allowedSortFields.includes(args.sortField))
throw errors.createInvalidArgumentError(`sorting by ${args.sortField} is not allowed`);
parsePaginationArguments: (args, optionsPageSize, offsetParsers) => {
const allowedSortFields = Object.keys(offsetParsers);
if (args.orderBy && !allowedSortFields.includes(args.orderBy))
throw errors.createInvalidArgumentError(`sorting by ${args.orderBy} is not allowed`);

const parsedArgs = {
offset: args.offset,
sortField: allowedSortFields.includes(args.sortField) ? args.sortField : allowedSortFields[0],
sortField: allowedSortFields.includes(args.orderBy) ? args.orderBy : 'id',
sortDirection: 'desc' === args.order ? -1 : 1
};

Expand All @@ -194,8 +194,8 @@ const routeUtils = {
}
parsedArgs.pageNumber = 0 < parsedArgs.pageNumber ? parsedArgs.pageNumber : 1;

if (args.offset && !isObjectId(args.offset))
throw errors.createInvalidArgumentError('offset is not a valid object id');
if (args.offset)
parsedArgs.offset = routeUtils.parseArgument(args, 'offset', offsetParsers[parsedArgs.sortField]);

return parsedArgs;
},
Expand Down
2 changes: 1 addition & 1 deletion rest/src/routes/transactionRoutes.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ module.exports = {
group: params.group
};

const options = routeUtils.parsePaginationArguments(params, services.config.pageSize, ['_id']);
const options = routeUtils.parsePaginationArguments(params, services.config.pageSize, { id: 'objectId' });

return db.transactions(filters, options)
.then(result => routeUtils.createSender(routeResultTypes.transaction).sendPage(res, next)(result));
Expand Down
44 changes: 24 additions & 20 deletions rest/test/db/CatapultDb_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const CatapultDb = require('../../src/db/CatapultDb');
const catapult = require('catapult-sdk');
const { expect } = require('chai');
const MongoDb = require('mongodb');
const sinon = require('sinon');

const { address, EntityType } = catapult.model;

Expand Down Expand Up @@ -175,8 +176,9 @@ describe('catapult db', () => {

const createBlock = (objectId, height, signerPublicKey, beneficiaryAddress) => ({
_id: createObjectId(objectId),
meta: { height },
meta: {},
block: {
height,
signerPublicKey: signerPublicKey ? Buffer.from(signerPublicKey) : undefined,
beneficiaryAddress: beneficiaryAddress ? Buffer.from(beneficiaryAddress) : undefined
}
Expand Down Expand Up @@ -247,9 +249,9 @@ describe('catapult db', () => {
const options = {
pageSize: 10,
pageNumber: 1,
sortField: '_id',
sortField: 'id',
sortDirection: 1,
offset: createObjectId(20).toString()
offset: createObjectId(20)
};

it('gt', () => {
Expand Down Expand Up @@ -279,7 +281,7 @@ describe('catapult db', () => {
const options = {
pageSize: 10,
pageNumber: 1,
sortField: '_id',
sortField: 'id',
sortDirection: 1
};

Expand All @@ -299,7 +301,7 @@ describe('catapult db', () => {
const options = {
pageSize: 10,
pageNumber: 1,
sortField: '_id',
sortField: 'id',
sortDirection: -1
};

Expand All @@ -316,21 +318,22 @@ describe('catapult db', () => {
});

it('sort field', () => {
const queryPagedDocumentsSpy = sinon.spy(CatapultDb.prototype, 'queryPagedDocuments_2');
const options = {
pageSize: 10,
pageNumber: 1,
sortField: 'meta.height',
sortField: 'height',
sortDirection: 1
};

// Act + Assert:
return runDbTest(
{ blocks: dbBlocks() },
db => db.blocks(undefined, undefined, options),
blocksPage => {
expect(blocksPage.data[0].id).to.deep.equal(createObjectId(30));
expect(blocksPage.data[1].id).to.deep.equal(createObjectId(10));
expect(blocksPage.data[2].id).to.deep.equal(createObjectId(20));
() => {
expect(queryPagedDocumentsSpy.calledOnce).to.equal(true);
expect(Object.keys(queryPagedDocumentsSpy.firstCall.args[2].$sort)[0]).to.equal('block.height');
queryPagedDocumentsSpy.restore();
}
);
});
Expand Down Expand Up @@ -1330,7 +1333,7 @@ describe('catapult db', () => {
const paginationOptions = {
pageSize: 10,
pageNumber: 1,
sortField: '_id',
sortField: 'id',
sortDirection: -1
};

Expand Down Expand Up @@ -1495,9 +1498,9 @@ describe('catapult db', () => {
const options = {
pageSize: 10,
pageNumber: 1,
sortField: '_id',
sortField: 'id',
sortDirection: 1,
offset: createObjectId(20).toString()
offset: createObjectId(20)
};

it('gt', () => {
Expand Down Expand Up @@ -1527,7 +1530,7 @@ describe('catapult db', () => {
const options = {
pageSize: 10,
pageNumber: 1,
sortField: '_id',
sortField: 'id',
sortDirection: 1
};

Expand All @@ -1547,7 +1550,7 @@ describe('catapult db', () => {
const options = {
pageSize: 10,
pageNumber: 1,
sortField: '_id',
sortField: 'id',
sortDirection: -1
};

Expand All @@ -1564,21 +1567,22 @@ describe('catapult db', () => {
});

it('sort field', () => {
const queryPagedDocumentsSpy = sinon.spy(CatapultDb.prototype, 'queryPagedDocuments_2');
const options = {
pageSize: 10,
pageNumber: 1,
sortField: 'meta.height',
sortField: 'id',
sortDirection: 1
};

// Act + Assert:
return runDbTest(
{ transactions: dbTransactions() },
db => db.transactions([], options),
transactionsPage => {
expect(transactionsPage.data[0].id).to.deep.equal(createObjectId(30));
expect(transactionsPage.data[1].id).to.deep.equal(createObjectId(10));
expect(transactionsPage.data[2].id).to.deep.equal(createObjectId(20));
() => {
expect(queryPagedDocumentsSpy.calledOnce).to.equal(true);
expect(Object.keys(queryPagedDocumentsSpy.firstCall.args[2].$sort)[0]).to.equal('_id');
queryPagedDocumentsSpy.restore();
}
);
});
Expand Down
23 changes: 13 additions & 10 deletions rest/test/plugins/mosaic/MosaicDb_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@
* along with Catapult. If not, see <http://www.gnu.org/licenses/>.
*/

const CatapultDb = require('../../../src/db/CatapultDb');
const MosaicDb = require('../../../src/plugins/mosaic/MosaicDb');
const test = require('../../db/utils/dbTestUtils');
const catapult = require('catapult-sdk');
const { expect } = require('chai');
const MongoDb = require('mongodb');
const sinon = require('sinon');

const { Binary, Long } = MongoDb;
const { address } = catapult.model;
Expand All @@ -40,7 +42,7 @@ describe('mosaic db', () => {
const paginationOptions = {
pageSize: 10,
pageNumber: 1,
sortField: '_id',
sortField: 'id',
sortDirection: -1
};

Expand Down Expand Up @@ -123,7 +125,7 @@ describe('mosaic db', () => {
const options = {
pageSize: 10,
pageNumber: 1,
sortField: '_id',
sortField: 'id',
sortDirection: 1
};

Expand All @@ -143,7 +145,7 @@ describe('mosaic db', () => {
const options = {
pageSize: 10,
pageNumber: 1,
sortField: '_id',
sortField: 'id',
sortDirection: -1
};

Expand All @@ -160,21 +162,22 @@ describe('mosaic db', () => {
});

it('sort field', () => {
const queryPagedDocumentsSpy = sinon.spy(CatapultDb.prototype, 'queryPagedDocuments_2');
const options = {
pageSize: 10,
pageNumber: 1,
sortField: 'mosaic.id',
sortField: 'id',
sortDirection: 1
};

// Act + Assert:
return runMosaicsDbTest(
dbMosaics(),
db => db.mosaics(undefined, options),
page => {
expect(page.data[0].id).to.deep.equal(createObjectId(30));
expect(page.data[1].id).to.deep.equal(createObjectId(10));
expect(page.data[2].id).to.deep.equal(createObjectId(20));
() => {
expect(queryPagedDocumentsSpy.calledOnce).to.equal(true);
expect(Object.keys(queryPagedDocumentsSpy.firstCall.args[2].$sort)[0]).to.equal('_id');
queryPagedDocumentsSpy.restore();
}
);
});
Expand All @@ -190,9 +193,9 @@ describe('mosaic db', () => {
const options = {
pageSize: 10,
pageNumber: 1,
sortField: '_id',
sortField: 'id',
sortDirection: 1,
offset: createObjectId(20).toString()
offset: createObjectId(20)
};

it('gt', () => {
Expand Down
Loading

0 comments on commit b8535f5

Please sign in to comment.