Skip to content
This repository was archived by the owner on Jun 22, 2021. It is now read-only.

Commit 7ed13ea

Browse files
committed
feat(pagination): Adds pagination utils.
1 parent e8effe1 commit 7ed13ea

12 files changed

+125
-2
lines changed

.circleci/config.yml

+3
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ jobs:
3030
- run:
3131
name: Compiling Code
3232
command: npm run build
33+
- run:
34+
name: Testing Code
35+
command: npm run cover
3336
- run:
3437
name: Linting Code
3538
command: npm run lint
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"/Applications/AMPPS/www/js-entity-repos-core/dist/tests/utils/testEntity.js":{"path":"/Applications/AMPPS/www/js-entity-repos-core/dist/tests/utils/testEntity.js","statementMap":{"0":{"start":{"line":2,"column":15},"end":{"line":9,"column":1}},"1":{"start":{"line":3,"column":4},"end":{"line":7,"column":5}},"2":{"start":{"line":4,"column":8},"end":{"line":4,"column":25}},"3":{"start":{"line":5,"column":8},"end":{"line":6,"column":24}},"4":{"start":{"line":5,"column":25},"end":{"line":6,"column":24}},"5":{"start":{"line":6,"column":12},"end":{"line":6,"column":24}},"6":{"start":{"line":8,"column":4},"end":{"line":8,"column":13}},"7":{"start":{"line":10,"column":0},"end":{"line":10,"column":62}},"8":{"start":{"line":11,"column":0},"end":{"line":13,"column":2}},"9":{"start":{"line":14,"column":0},"end":{"line":14,"column":120}},"10":{"start":{"line":15,"column":0},"end":{"line":15,"column":37}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":2,"column":59},"end":{"line":2,"column":60}},"loc":{"start":{"line":2,"column":71},"end":{"line":9,"column":1}},"line":2}},"branchMap":{"0":{"loc":{"start":{"line":2,"column":15},"end":{"line":9,"column":1}},"type":"binary-expr","locations":[{"start":{"line":2,"column":16},"end":{"line":2,"column":20}},{"start":{"line":2,"column":24},"end":{"line":2,"column":37}},{"start":{"line":2,"column":42},"end":{"line":2,"column":55}},{"start":{"line":2,"column":59},"end":{"line":9,"column":1}}],"line":2},"1":{"loc":{"start":{"line":5,"column":25},"end":{"line":6,"column":24}},"type":"if","locations":[{"start":{"line":5,"column":25},"end":{"line":6,"column":24}},{"start":{"line":5,"column":25},"end":{"line":6,"column":24}}],"line":5}},"s":{"0":1,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":1,"8":1,"9":1,"10":1},"f":{"0":0},"b":{"0":[1,1,1,0],"1":[0,0]},"_coverageSchema":"332fd63041d2c1bcb487cc26dd0d5f7d97098a6c","hash":"296308cde61c29a2c2f14e548e18eba73251e6e4","contentHash":"f0bf9f7257bfabdd397dd539d1bd93eb_11.3.0"},"/Applications/AMPPS/www/js-entity-repos-core/dist/utils/createCursorFromEntity/createCursorFromEntity.js":{"path":"/Applications/AMPPS/www/js-entity-repos-core/dist/utils/createCursorFromEntity/createCursorFromEntity.js","statementMap":{"0":{"start":{"line":2,"column":0},"end":{"line":2,"column":62}},"1":{"start":{"line":3,"column":11},"end":{"line":3,"column":26}},"2":{"start":{"line":4,"column":15},"end":{"line":4,"column":32}},"3":{"start":{"line":5,"column":0},"end":{"line":14,"column":2}},"4":{"start":{"line":6,"column":4},"end":{"line":8,"column":5}},"5":{"start":{"line":7,"column":8},"end":{"line":7,"column":25}},"6":{"start":{"line":9,"column":19},"end":{"line":9,"column":36}},"7":{"start":{"line":10,"column":23},"end":{"line":12,"column":10}},"8":{"start":{"line":11,"column":8},"end":{"line":11,"column":76}},"9":{"start":{"line":13,"column":4},"end":{"line":13,"column":46}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":5,"column":18},"end":{"line":5,"column":19}},"loc":{"start":{"line":5,"column":42},"end":{"line":14,"column":1}},"line":5},"1":{"name":"(anonymous_1)","decl":{"start":{"line":10,"column":39},"end":{"line":10,"column":40}},"loc":{"start":{"line":10,"column":66},"end":{"line":12,"column":5}},"line":10}},"branchMap":{"0":{"loc":{"start":{"line":6,"column":4},"end":{"line":8,"column":5}},"type":"if","locations":[{"start":{"line":6,"column":4},"end":{"line":8,"column":5}},{"start":{"line":6,"column":4},"end":{"line":8,"column":5}}],"line":6}},"s":{"0":1,"1":1,"2":1,"3":1,"4":4,"5":1,"6":3,"7":3,"8":3,"9":3},"f":{"0":4,"1":3},"b":{"0":[1,3]},"_coverageSchema":"332fd63041d2c1bcb487cc26dd0d5f7d97098a6c","hash":"4feb5a9ab0062ad6b42c4a37cfaf6e9db404ed76","contentHash":"c9827a668e5c6fed5d7462633f9fa3a3_11.3.0"},"/Applications/AMPPS/www/js-entity-repos-core/dist/utils/createPaginationFilter/createPaginationFilter.js":{"path":"/Applications/AMPPS/www/js-entity-repos-core/dist/utils/createPaginationFilter/createPaginationFilter.js","statementMap":{"0":{"start":{"line":2,"column":0},"end":{"line":2,"column":62}},"1":{"start":{"line":3,"column":11},"end":{"line":3,"column":26}},"2":{"start":{"line":4,"column":15},"end":{"line":4,"column":32}},"3":{"start":{"line":5,"column":10},"end":{"line":7,"column":1}},"4":{"start":{"line":6,"column":4},"end":{"line":6,"column":70}},"5":{"start":{"line":8,"column":0},"end":{"line":23,"column":2}},"6":{"start":{"line":9,"column":4},"end":{"line":11,"column":5}},"7":{"start":{"line":10,"column":8},"end":{"line":10,"column":18}},"8":{"start":{"line":12,"column":20},"end":{"line":12,"column":55}},"9":{"start":{"line":13,"column":17},"end":{"line":21,"column":6}},"10":{"start":{"line":14,"column":22},"end":{"line":14,"column":75}},"11":{"start":{"line":15,"column":8},"end":{"line":20,"column":9}},"12":{"start":{"line":16,"column":12},"end":{"line":16,"column":40}},"13":{"start":{"line":19,"column":12},"end":{"line":19,"column":40}},"14":{"start":{"line":22,"column":4},"end":{"line":22,"column":18}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":5,"column":10},"end":{"line":5,"column":11}},"loc":{"start":{"line":5,"column":44},"end":{"line":7,"column":1}},"line":5},"1":{"name":"(anonymous_1)","decl":{"start":{"line":8,"column":18},"end":{"line":8,"column":19}},"loc":{"start":{"line":8,"column":46},"end":{"line":23,"column":1}},"line":8},"2":{"name":"(anonymous_2)","decl":{"start":{"line":13,"column":47},"end":{"line":13,"column":48}},"loc":{"start":{"line":13,"column":79},"end":{"line":21,"column":5}},"line":13}},"branchMap":{"0":{"loc":{"start":{"line":6,"column":11},"end":{"line":6,"column":69}},"type":"binary-expr","locations":[{"start":{"line":6,"column":12},"end":{"line":6,"column":22}},{"start":{"line":6,"column":26},"end":{"line":6,"column":37}},{"start":{"line":6,"column":43},"end":{"line":6,"column":54}},{"start":{"line":6,"column":58},"end":{"line":6,"column":68}}],"line":6},"1":{"loc":{"start":{"line":9,"column":4},"end":{"line":11,"column":5}},"type":"if","locations":[{"start":{"line":9,"column":4},"end":{"line":11,"column":5}},{"start":{"line":9,"column":4},"end":{"line":11,"column":5}}],"line":9},"2":{"loc":{"start":{"line":15,"column":8},"end":{"line":20,"column":9}},"type":"if","locations":[{"start":{"line":15,"column":8},"end":{"line":20,"column":9}},{"start":{"line":15,"column":8},"end":{"line":20,"column":9}}],"line":15}},"s":{"0":1,"1":1,"2":1,"3":1,"4":2,"5":1,"6":3,"7":1,"8":2,"9":2,"10":2,"11":2,"12":1,"13":1,"14":2},"f":{"0":2,"1":3,"2":2},"b":{"0":[2,2,1,0],"1":[1,2],"2":[1,1]},"_coverageSchema":"332fd63041d2c1bcb487cc26dd0d5f7d97098a6c","hash":"1e96b7d8625194e0c090be43b9e6d7069c2f2c80","contentHash":"092d8b30b5ebd0f69207596206e58ed4_11.3.0"}}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{}

@types/atob/index.d.ts

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
declare module 'atob' {
2+
const x: (stringToDecode: string) => string;
3+
export = x;
4+
}

@types/btoa/index.d.ts

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
declare module 'btoa' {
2+
const x: (stringToEncode: string) => string;
3+
export = x;
4+
}

package-lock.json

+12-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+5
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
"scripts": {
1212
"build": "tsc",
1313
"lint": "tslint --project ./tsconfig.json",
14+
"test": "mocha $(find dist -name '*.test.js') --exit",
15+
"cover": "nyc npm test",
1416
"duplication": "jscpd",
1517
"clean": "rimraf dist",
1618
"semantic-release": "ht2-release-private-circleci"
@@ -20,11 +22,14 @@
2022
"check-coverage": true
2123
},
2224
"dependencies": {
25+
"btoa": "^1.1.2",
26+
"lodash": "^4.17.4",
2327
"make-error": "^1.3.0"
2428
},
2529
"devDependencies": {
2630
"@ht2-labs/semantic-release": "1.0.9",
2731
"@ht2-labs/typescript-project": "1.0.0",
32+
"@types/lodash": "^4.14.91",
2833
"@types/mocha": "2.2.44",
2934
"@types/power-assert": "1.4.29",
3035
"assert-rejects": "0.1.1",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import 'mocha'; // tslint:disable-line:no-import-side-effect
2+
import * as assert from 'power-assert';
3+
import { TestEntity, testEntity } from '../../tests/utils/testEntity';
4+
import createCursorFromEntity from './createCursorFromEntity';
5+
6+
describe('createCursorFromEntity', () => {
7+
it('should return undefined when the entity is undefined', () => {
8+
const actualResult = createCursorFromEntity<TestEntity>(undefined, { id: true });
9+
assert.equal(actualResult, undefined);
10+
});
11+
12+
it('should return the correct cursor when the entity is defined', () => {
13+
const actualResult = createCursorFromEntity<TestEntity>(testEntity, { id: true });
14+
assert.equal(actualResult, 'eyJpZCI6InRlc3RfaWQifQ==');
15+
});
16+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import * as btoa from 'btoa';
2+
import { get, set } from 'lodash';
3+
import Cursor from '../../types/Cursor';
4+
import Sort from '../../types/Sort';
5+
6+
export default <Entity>(entity: Entity | undefined, sort: Sort<Entity>): Cursor => {
7+
if (entity === undefined) {
8+
return undefined;
9+
}
10+
const sortKeys = Object.keys(sort);
11+
const cursorResult = sortKeys.reduce<Partial<Entity>>((result, sortKey) => {
12+
return set(result, sortKey, get(entity, sortKey));
13+
}, {});
14+
return btoa(JSON.stringify(cursorResult));
15+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import 'mocha'; // tslint:disable-line:no-import-side-effect
2+
import * as assert from 'power-assert';
3+
import { TestEntity, testEntity } from '../../tests/utils/testEntity';
4+
import Pagination from '../../types/Pagination';
5+
import createCursorFromEntity from '../createCursorFromEntity/createCursorFromEntity';
6+
import createPaginationFilter from './createPaginationFilter';
7+
8+
describe('createCursorFromEntity', () => {
9+
const sort = { id: true };
10+
11+
it('should return empty filter when the cursor is undefined', () => {
12+
const pagination: Pagination = { cursor: undefined, forward: true, limit: 1 };
13+
const actualResult = createPaginationFilter<TestEntity>(pagination, sort);
14+
const expectedResult = {};
15+
assert.deepEqual(actualResult, expectedResult);
16+
});
17+
18+
it('should return the correct filter when the cursor is defined and going forward', () => {
19+
const cursor = createCursorFromEntity<TestEntity>(testEntity, sort);
20+
const pagination: Pagination = { cursor, forward: true, limit: 1 };
21+
const actualResult = createPaginationFilter<TestEntity>(pagination, sort);
22+
const expectedResult = {
23+
id: { $gt: testEntity.id },
24+
};
25+
assert.deepEqual(actualResult, expectedResult);
26+
});
27+
28+
it('should return the correct filter when the cursor is defined and going backward', () => {
29+
const cursor = createCursorFromEntity<TestEntity>(testEntity, sort);
30+
const pagination: Pagination = { cursor, forward: false, limit: 1 };
31+
const actualResult = createPaginationFilter<TestEntity>(pagination, sort);
32+
const expectedResult = {
33+
id: { $lt: testEntity.id },
34+
};
35+
assert.deepEqual(actualResult, expectedResult);
36+
});
37+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import * as atob from 'atob';
2+
import { get, mapValues } from 'lodash';
3+
// tslint:disable-next-line:no-unused
4+
import Filter, { AndFilter, NotFilter, OrFilter } from '../../types/Filter';
5+
import Pagination from '../../types/Pagination';
6+
import Sort from '../../types/Sort';
7+
8+
const xor = (conditionA: boolean, conditionB: boolean) => {
9+
return (conditionA && !conditionB) || (!conditionA && conditionB);
10+
};
11+
12+
export default <Entity>(pagination: Pagination, sort: Sort<Entity>): Filter<Entity> => {
13+
if (pagination.cursor === undefined) {
14+
return {};
15+
}
16+
const cursorObj = JSON.parse(atob(pagination.cursor));
17+
const filter = mapValues(cursorObj, (cursorValue, sortKey) => {
18+
const forward = !xor(get(sort, sortKey), pagination.forward);
19+
if (forward) {
20+
return { $gt: cursorValue };
21+
} else {
22+
return { $lt: cursorValue };
23+
}
24+
});
25+
return filter as any as Filter<Entity>;
26+
};

0 commit comments

Comments
 (0)