diff --git a/packages/webiny-api-cms/src/dataSource/schemas/pageResolvers/listPages.js b/packages/webiny-api-cms/src/dataSource/schemas/pageResolvers/listPages.js index 4e9db6e49f8..29bfc7b7faa 100644 --- a/packages/webiny-api-cms/src/dataSource/schemas/pageResolvers/listPages.js +++ b/packages/webiny-api-cms/src/dataSource/schemas/pageResolvers/listPages.js @@ -1,5 +1,6 @@ // @flow import type { Entity, EntityCollection } from "webiny-entity"; +import { createPaginationMeta } from "webiny-entity"; import { ListResponse } from "webiny-api/graphql/responses"; type EntityFetcher = (context: Object) => Class; @@ -39,19 +40,12 @@ export default (entityFetcher: EntityFetcher) => async ( }; const pages: EntityCollection = await entityClass.find({ sql }); - - const meta: Object = { - page, - perPage, - count: pages.length, - totalCount: pages.getMeta().totalCount - }; - - meta.totalPages = Math.ceil(meta.totalCount / meta.perPage); - meta.to = (meta.page - 1) * meta.perPage + meta.count; - meta.from = meta.to - meta.count + 1; - meta.nextPage = meta.page < meta.totalPages ? meta.page + 1 : null; - meta.previousPage = meta.page === 1 ? null : meta.page - 1; - - return new ListResponse(pages, meta); + return new ListResponse( + pages, + createPaginationMeta({ + page, + perPage, + totalCount: pages.getMeta().totalCount + }) + ); }; diff --git a/packages/webiny-api/src/graphql/crudResolvers.js b/packages/webiny-api/src/graphql/crudResolvers.js index 0bdd7ee65b2..2818b721fb0 100644 --- a/packages/webiny-api/src/graphql/crudResolvers.js +++ b/packages/webiny-api/src/graphql/crudResolvers.js @@ -61,16 +61,7 @@ export const resolveList = (entityFetcher: EntityFetcher) => async ( sort: args.sort }); - const meta = data.getParams(); - meta.count = data.length; - meta.totalCount = data.getMeta().totalCount; - meta.totalPages = Math.ceil(meta.totalCount / meta.perPage); - meta.to = (meta.page - 1) * meta.perPage + meta.count; - meta.from = meta.to - meta.count + 1; - meta.nextPage = meta.page < meta.totalPages ? meta.page + 1 : null; - meta.previousPage = meta.page === 1 ? null : meta.page - 1; - - return new ListResponse(data, meta); + return new ListResponse(data, data.getMeta()); }; export const resolveCreate = (entityFetcher: EntityFetcher) => async ( diff --git a/packages/webiny-api/src/graphql/schema/genericTypes.js b/packages/webiny-api/src/graphql/schema/genericTypes.js index a8c873f840c..ba244fb13e2 100644 --- a/packages/webiny-api/src/graphql/schema/genericTypes.js +++ b/packages/webiny-api/src/graphql/schema/genericTypes.js @@ -8,7 +8,6 @@ export const genericTypes = () => { } type ListMeta { - count: Int totalCount: Int totalPages: Int page: Int @@ -30,4 +29,4 @@ export const genericTypes = () => { data: JSON } `; -}; \ No newline at end of file +}; diff --git a/packages/webiny-app-admin/src/views/ApiTokens/graphql.js b/packages/webiny-app-admin/src/views/ApiTokens/graphql.js index 268ddfe3c21..b5adf69e73f 100644 --- a/packages/webiny-app-admin/src/views/ApiTokens/graphql.js +++ b/packages/webiny-app-admin/src/views/ApiTokens/graphql.js @@ -1,3 +1,4 @@ +// @flow import gql from "graphql-tag"; const fields = ` @@ -5,7 +6,13 @@ const fields = ` `; export const loadApiTokens = gql` - query LoadApiTokens($where: JSON, $sort: JSON, $page: Int, $perPage: Int, $search: SearchInput) { + query LoadApiTokens( + $where: JSON + $sort: JSON + $page: Int + $perPage: Int + $search: SearchInput + ) { security { tokens: listApiTokens( where: $where @@ -21,9 +28,7 @@ export const loadApiTokens = gql` createdOn } meta { - count totalCount - totalPages to from nextPage diff --git a/packages/webiny-app-admin/src/views/Groups/graphql.js b/packages/webiny-app-admin/src/views/Groups/graphql.js index e8539b8a43f..6a0dabe622a 100644 --- a/packages/webiny-app-admin/src/views/Groups/graphql.js +++ b/packages/webiny-app-admin/src/views/Groups/graphql.js @@ -26,9 +26,7 @@ export const loadGroups = gql` createdOn } meta { - count totalCount - totalPages to from nextPage diff --git a/packages/webiny-app-admin/src/views/Roles/graphql.js b/packages/webiny-app-admin/src/views/Roles/graphql.js index 7be34c80b07..f5c2618eea7 100644 --- a/packages/webiny-app-admin/src/views/Roles/graphql.js +++ b/packages/webiny-app-admin/src/views/Roles/graphql.js @@ -1,3 +1,4 @@ +// @flow import gql from "graphql-tag"; const fields = ` @@ -33,9 +34,7 @@ export const loadRoles = gql` createdOn } meta { - count totalCount - totalPages to from nextPage diff --git a/packages/webiny-app-admin/src/views/Users/graphql.js b/packages/webiny-app-admin/src/views/Users/graphql.js index 0fd6ae036c9..9732d7725ce 100644 --- a/packages/webiny-app-admin/src/views/Users/graphql.js +++ b/packages/webiny-app-admin/src/views/Users/graphql.js @@ -1,3 +1,4 @@ +// @flow import gql from "graphql-tag"; const fields = ` @@ -26,9 +27,7 @@ export const loadUsers = gql` createdOn } meta { - count totalCount - totalPages to from nextPage diff --git a/packages/webiny-app-cms/src/admin/graphql/pages.js b/packages/webiny-app-cms/src/admin/graphql/pages.js index d46af158f08..39dc7d3d13c 100644 --- a/packages/webiny-app-cms/src/admin/graphql/pages.js +++ b/packages/webiny-app-cms/src/admin/graphql/pages.js @@ -1,3 +1,4 @@ +// @flow import gql from "graphql-tag"; const error = ` @@ -45,13 +46,9 @@ export const listPages = gql` } } meta { - count totalCount - from to - page - totalPages - perPage + from nextPage previousPage } diff --git a/packages/webiny-app-cms/src/admin/views/Categories/graphql.js b/packages/webiny-app-cms/src/admin/views/Categories/graphql.js index ba5507b6ccc..cc4579ed0a0 100644 --- a/packages/webiny-app-cms/src/admin/views/Categories/graphql.js +++ b/packages/webiny-app-cms/src/admin/views/Categories/graphql.js @@ -1,3 +1,4 @@ +// @flow import gql from "graphql-tag"; const fields = ` @@ -9,7 +10,13 @@ const fields = ` `; export const loadCategories = gql` - query LoadCategories($where: JSON, $sort: JSON, $page: Int, $perPage: Int, $search: SearchInput) { + query LoadCategories( + $where: JSON + $sort: JSON + $page: Int + $perPage: Int + $search: SearchInput + ) { cms { categories: listCategories( where: $where @@ -26,9 +33,7 @@ export const loadCategories = gql` createdOn } meta { - count totalCount - totalPages to from nextPage diff --git a/packages/webiny-entity-mysql/src/mysqlDriver.js b/packages/webiny-entity-mysql/src/mysqlDriver.js index f3d834b600c..709f357828f 100644 --- a/packages/webiny-entity-mysql/src/mysqlDriver.js +++ b/packages/webiny-entity-mysql/src/mysqlDriver.js @@ -2,7 +2,7 @@ import _ from "lodash"; import mdbid from "mdbid"; import type { Connection, Pool } from "mysql"; -import { Entity, Driver, QueryResult } from "webiny-entity"; +import { Entity, Driver, QueryResult, createPaginationMeta } from "webiny-entity"; import { MySQLConnection } from "webiny-mysql-connection"; import { Attribute } from "webiny-model"; import type { @@ -173,7 +173,13 @@ class MySQLDriver extends Driver { const sql = new Select(clonedOptions, entity).generate(); const results = await this.getConnection().query([sql, "SELECT FOUND_ROWS() as count"]); - return new QueryResult(results[0], { totalCount: results[1][0].count }); + const meta = createPaginationMeta({ + totalCount: results[1][0].count, + page: options.page, + perPage: options.perPage + }); + + return new QueryResult(results[0], meta); } async findOne( diff --git a/packages/webiny-entity/__tests__/createPaginationMeta.test.js b/packages/webiny-entity/__tests__/createPaginationMeta.test.js new file mode 100644 index 00000000000..dfe088b48c6 --- /dev/null +++ b/packages/webiny-entity/__tests__/createPaginationMeta.test.js @@ -0,0 +1,90 @@ +import { createPaginationMeta } from "webiny-entity"; + +describe("createPaginationMeta test", () => { + test("should return correct pagination meta data", async () => { + let meta = createPaginationMeta({ + page: 1, + perPage: 10, + totalCount: 100 + }); + + expect(meta).toEqual({ + from: 1, + nextPage: 2, + page: 1, + perPage: 10, + previousPage: null, + to: 10, + totalCount: 100, + totalPages: 10 + }); + + meta = createPaginationMeta({ + totalCount: 100, + page: 3, + perPage: 7 + }); + + expect(meta).toEqual({ + page: 3, + perPage: 7, + totalCount: 100, + from: 15, + nextPage: 4, + previousPage: 2, + to: 21, + totalPages: 15 + }); + + meta = createPaginationMeta({ + totalCount: 100, + page: 3, + perPage: 10 + }); + + expect(meta).toEqual({ + from: 21, + nextPage: 4, + page: 3, + perPage: 10, + previousPage: 2, + to: 30, + totalCount: 100, + totalPages: 10 + }); + + meta = createPaginationMeta({ + totalCount: 15, + page: 3, + perPage: 6 + }); + + expect(meta).toEqual({ + from: 13, + nextPage: null, + page: 3, + perPage: 6, + previousPage: 2, + to: 15, + totalCount: 15, + totalPages: 3 + }); + + meta = createPaginationMeta({ + totalCount: 0, + page: 1, + perPage: 10 + }); + + expect(meta).toEqual({ + page: 1, + perPage: 10, + nextPage: null, + previousPage: null, + to: 0, + from: 0, + totalCount: 0, + totalPages: 0 + }); + }); +}); diff --git a/packages/webiny-entity/__tests__/entityCollection.test.js b/packages/webiny-entity/__tests__/entityCollection.test.js index d7ea01a0bb5..99a79407b67 100644 --- a/packages/webiny-entity/__tests__/entityCollection.test.js +++ b/packages/webiny-entity/__tests__/entityCollection.test.js @@ -41,12 +41,6 @@ describe("EntityCollection test", () => { expect(collection.getParams().a).toEqual(123); }); - test("setTotalCount/getTotalCount methods must work correctly", async () => { - const collection = new EntityCollection(); - collection.setTotalCount(444); - expect(collection.getTotalCount()).toEqual(444); - }); - test("setMeta/getMeta methods must work correctly", async () => { const collection = new EntityCollection(); collection.setMeta({ a: 123 }); diff --git a/packages/webiny-entity/index.js b/packages/webiny-entity/index.js index 97c3b56b720..3aaa1627bbc 100644 --- a/packages/webiny-entity/index.js +++ b/packages/webiny-entity/index.js @@ -7,6 +7,7 @@ export { default as EntityPoolEntry } from "./src/entityPoolEntry"; export { default as EntityCollection } from "./src/entityCollection"; export { default as EntityAttributesContainer } from "./src/entityAttributesContainer"; export { default as QueryResult } from "./src/queryResult"; +export { default as createPaginationMeta } from "./src/createPaginationMeta"; export { default as Driver } from "./src/driver"; export { default as EventHandler } from "./src/eventHandler"; diff --git a/packages/webiny-entity/src/createPaginationMeta.js b/packages/webiny-entity/src/createPaginationMeta.js new file mode 100644 index 00000000000..65dd784ec8d --- /dev/null +++ b/packages/webiny-entity/src/createPaginationMeta.js @@ -0,0 +1,52 @@ +// @flow +type PaginationMeta = { + page: number, + perPage: number, + totalCount: number, + + totalPages: ?number, + from: ?number, + to: ?number, + nextPage: ?number, + previousPage: ?number +}; + +type Params = { + page: number, + perPage: number, + totalCount: number +}; + +export default (params: ?Params): PaginationMeta => { + const meta: PaginationMeta = { + page: 0, + perPage: 0, + totalCount: 0, + totalPages: null, + from: null, + to: null, + nextPage: null, + previousPage: null, + ...params + }; + + if (meta.page && meta.perPage) { + meta.totalPages = Math.ceil(meta.totalCount / meta.perPage); + + if (meta.totalCount) { + meta.from = 1 + meta.perPage * (meta.page - 1); + } else { + meta.from = 0; + } + + meta.to = meta.perPage * meta.page; + if (meta.to > meta.totalCount) { + meta.to = meta.totalCount; + } + + meta.nextPage = meta.page < meta.totalPages ? meta.page + 1 : null; + meta.previousPage = meta.page === 1 ? null : meta.page - 1; + } + + return meta; +}; diff --git a/packages/webiny-entity/src/entity.js b/packages/webiny-entity/src/entity.js index 789587fc00c..2aa0ec7c8a9 100644 --- a/packages/webiny-entity/src/entity.js +++ b/packages/webiny-entity/src/entity.js @@ -3,6 +3,7 @@ import _ from "lodash"; import { Attribute } from "webiny-model"; import Driver from "./driver"; import EntityPool from "./entityPool"; +import createPaginationMeta from "./createPaginationMeta"; import EventHandler from "./eventHandler"; import EntityCollection from "./entityCollection"; import EntityModel from "./entityModel"; @@ -229,7 +230,7 @@ class Entity { * Used when populating entity with data from storage. * @param data */ - populateFromStorage(data: Object): this { + populateFromStorage(data: Object): Entity { this.getModel().populateFromStorage(data); return this; } @@ -496,9 +497,11 @@ class Entity { await this.emit("query", prepared); const queryResult: QueryResult = await this.getDriver().find(this, prepared); + const entityCollection = new EntityCollection() .setParams(prepared) - .setMeta(queryResult.getMeta()); + .setMeta({ ...createPaginationMeta(), ...queryResult.getMeta() }); + const result: Array = (queryResult.getResult(): any); if (result instanceof Array) { for (let i = 0; i < result.length; i++) { diff --git a/packages/webiny-entity/src/index.js b/packages/webiny-entity/src/index.js index 30c850d19d8..d84d781f397 100644 --- a/packages/webiny-entity/src/index.js +++ b/packages/webiny-entity/src/index.js @@ -5,6 +5,7 @@ export { default as EntityModel } from "./entityModel"; export { default as EntityPool } from "./entityPool"; export { default as EntityPoolEntry } from "./entityPoolEntry"; export { default as EntityCollection } from "./entityCollection"; +export { default as createPaginationMeta } from "./createPaginationMeta"; export { default as EntityAttributesContainer } from "./entityAttributesContainer"; export { default as QueryResult } from "./queryResult"; export { default as Driver } from "./driver";