From d64fe25963b60376b7fa399c808a0390e982045a Mon Sep 17 00:00:00 2001 From: "jiho.park" Date: Wed, 16 Aug 2023 18:48:52 +0900 Subject: [PATCH] refactor: each `pagination dto` manages response`s metadata --- spec/base/base.controller.read-one.spec.ts | 2 +- spec/base/base.controller.spec.ts | 2 +- .../custom-entity.controller.read-many.spec.ts | 7 ++----- .../custom-entity.controller.read-one.spec.ts | 2 +- .../custom-entity.controller.search.spec.ts | 6 ++---- ...ple-primary-key.controller.read-one.spec.ts | 2 +- spec/pagination/pagination.interceptor.spec.ts | 12 ++---------- spec/pagination/pagination.spec.ts | 6 ++---- spec/read-many/read-many.controller.spec.ts | 7 +------ .../response-interceptor.spec.ts | 2 +- spec/search/search-cursor-pagination.spec.ts | 10 ++++------ spec/search/search-query-operator.spec.ts | 10 ++++------ src/lib/abstract/abstract.pagination.spec.ts | 4 ++++ src/lib/abstract/abstract.pagination.ts | 3 ++- src/lib/dto/pagination-cursor.dto.ts | 10 +++++++++- src/lib/dto/pagination-offset.dto.ts | 12 +++++++++++- src/lib/request/read-many.request.ts | 18 +++++------------- 17 files changed, 53 insertions(+), 62 deletions(-) diff --git a/spec/base/base.controller.read-one.spec.ts b/spec/base/base.controller.read-one.spec.ts index 21f7ec6..f3d1134 100644 --- a/spec/base/base.controller.read-one.spec.ts +++ b/spec/base/base.controller.read-one.spec.ts @@ -18,7 +18,7 @@ describe('BaseController', () => { app = moduleFixture.createNestApplication(); service = moduleFixture.get(BaseService); - await Promise.all(['name1', 'name2'].map((name: string) => service.repository.save(service.repository.create({ name })))); + await service.repository.save(['name1', 'name2'].map((name: string) => service.repository.create({ name }))); await app.init(); }); diff --git a/spec/base/base.controller.spec.ts b/spec/base/base.controller.spec.ts index 5f80fb8..3e58c44 100644 --- a/spec/base/base.controller.spec.ts +++ b/spec/base/base.controller.spec.ts @@ -20,7 +20,7 @@ describe('BaseController', () => { controller = moduleFixture.get(BaseController); service = moduleFixture.get(BaseService); - await Promise.all(['name1', 'name2'].map((name: string) => service.repository.save(service.repository.create({ name })))); + await service.repository.save(['name1', 'name2'].map((name: string) => service.repository.create({ name }))); await app.init(); }); diff --git a/spec/custom-entity/custom-entity.controller.read-many.spec.ts b/spec/custom-entity/custom-entity.controller.read-many.spec.ts index 7698dc3..66dab4c 100644 --- a/spec/custom-entity/custom-entity.controller.read-many.spec.ts +++ b/spec/custom-entity/custom-entity.controller.read-many.spec.ts @@ -18,12 +18,9 @@ describe('CustomEntity - ReadMany', () => { app = moduleFixture.createNestApplication(); service = moduleFixture.get(CustomEntityService); - await Promise.all( - Array.from({ length: 100 }, (_, index) => index).map((number) => - service.repository.save(service.repository.create({ uuid: `${number}`, name: `name-${number}` })), - ), + await service.repository.save( + Array.from({ length: 100 }, (_, index) => index).map((number) => ({ uuid: `${number}`, name: `name-${number}` })), ); - await app.init(); }); diff --git a/spec/custom-entity/custom-entity.controller.read-one.spec.ts b/spec/custom-entity/custom-entity.controller.read-one.spec.ts index 2255776..f58118a 100644 --- a/spec/custom-entity/custom-entity.controller.read-one.spec.ts +++ b/spec/custom-entity/custom-entity.controller.read-one.spec.ts @@ -18,7 +18,7 @@ describe('CustomEntity - ReadOne', () => { app = moduleFixture.createNestApplication(); service = moduleFixture.get(CustomEntityService); - await Promise.all(['name1', 'name2'].map((name: string) => service.repository.save(service.repository.create({ name })))); + await service.repository.save(['name1', 'name2'].map((name: string) => service.repository.create({ name }))); await app.init(); }); diff --git a/spec/custom-entity/custom-entity.controller.search.spec.ts b/spec/custom-entity/custom-entity.controller.search.spec.ts index 64ba92d..f2b230b 100644 --- a/spec/custom-entity/custom-entity.controller.search.spec.ts +++ b/spec/custom-entity/custom-entity.controller.search.spec.ts @@ -18,10 +18,8 @@ describe('CustomEntity - Search', () => { app = moduleFixture.createNestApplication(); service = moduleFixture.get(CustomEntityService); - await Promise.all( - Array.from({ length: 100 }, (_, index) => index).map((number) => - service.repository.save(service.repository.create({ uuid: `${number}`, name: `name-${number}` })), - ), + await service.repository.save( + Array.from({ length: 100 }, (_, index) => index).map((number) => ({ uuid: `${number}`, name: `name-${number}` })), ); await service.repository.save(service.repository.create({ uuid: 'test' })); diff --git a/spec/multiple-primary-key/multiple-primary-key.controller.read-one.spec.ts b/spec/multiple-primary-key/multiple-primary-key.controller.read-one.spec.ts index 0b9233b..e828c12 100644 --- a/spec/multiple-primary-key/multiple-primary-key.controller.read-one.spec.ts +++ b/spec/multiple-primary-key/multiple-primary-key.controller.read-one.spec.ts @@ -18,7 +18,7 @@ describe('MultiplePrimaryKey - ReadOne', () => { app = moduleFixture.createNestApplication(); service = moduleFixture.get(MultiplePrimaryKeyService); - await Promise.all(['name1', 'name2'].map((name: string) => service.repository.save(service.repository.create({ name })))); + await service.repository.save(['name1', 'name2'].map((name: string) => service.repository.create({ name }))); await app.init(); }); diff --git a/spec/pagination/pagination.interceptor.spec.ts b/spec/pagination/pagination.interceptor.spec.ts index 042d4b1..527655a 100644 --- a/spec/pagination/pagination.interceptor.spec.ts +++ b/spec/pagination/pagination.interceptor.spec.ts @@ -35,11 +35,7 @@ describe('Pagination with interceptor', () => { app = moduleFixture.createNestApplication(); const service: BaseService = moduleFixture.get(BaseService); - await Promise.all( - Array.from({ length: 100 }, (_, index) => index).map((number) => - service.repository.save(service.repository.create({ name: `name-${number}` })), - ), - ); + await service.repository.save(Array.from({ length: 100 }, (_, index) => index).map((number) => ({ name: `name-${number}` }))); await app.init(); }); @@ -141,11 +137,7 @@ describe('Pagination with interceptor', () => { app = moduleFixture.createNestApplication(); const service: BaseService = moduleFixture.get(BaseService); - await Promise.all( - Array.from({ length: 100 }, (_, index) => index).map((number) => - service.repository.save(service.repository.create({ name: `name-${number}` })), - ), - ); + await service.repository.save(Array.from({ length: 100 }, (_, index) => index).map((number) => ({ name: `name-${number}` }))); await app.init(); }); diff --git a/spec/pagination/pagination.spec.ts b/spec/pagination/pagination.spec.ts index f7b47b7..9e0724d 100644 --- a/spec/pagination/pagination.spec.ts +++ b/spec/pagination/pagination.spec.ts @@ -34,10 +34,8 @@ describe('Pagination', () => { beforeEach(async () => { await service.repository.delete({}); - await Promise.all( - Array.from({ length: totalCount }, (_, index) => index).map((number) => - service.repository.save(service.repository.create({ name: `name-${number}` })), - ), + await service.repository.save( + Array.from({ length: totalCount }, (_, index) => index).map((number) => ({ name: `name-${number}` })), ); }); diff --git a/spec/read-many/read-many.controller.spec.ts b/spec/read-many/read-many.controller.spec.ts index 152920f..43d754a 100644 --- a/spec/read-many/read-many.controller.spec.ts +++ b/spec/read-many/read-many.controller.spec.ts @@ -23,12 +23,7 @@ describe('ReadMany - Options', () => { app = moduleFixture.createNestApplication(); service = moduleFixture.get(BaseService); - await Promise.all( - Array.from({ length: 100 }, (_, index) => index).map((number) => - service.repository.save(service.repository.create({ name: `name-${number}` })), - ), - ); - + await service.repository.save(Array.from({ length: 100 }, (_, index) => index).map((number) => ({ name: `name-${number}` }))); await app.init(); }); diff --git a/spec/response-interceptor/response-interceptor.spec.ts b/spec/response-interceptor/response-interceptor.spec.ts index c6d2d89..1a9bdcf 100644 --- a/spec/response-interceptor/response-interceptor.spec.ts +++ b/spec/response-interceptor/response-interceptor.spec.ts @@ -18,7 +18,7 @@ describe('Response Interceptor', () => { app = moduleFixture.createNestApplication(); service = moduleFixture.get(BaseService); - await Promise.all(['name1', 'name2'].map((name: string) => service.repository.save(service.repository.create({ name })))); + await service.repository.save(['name1', 'name2'].map((name: string) => service.repository.create({ name }))); await app.init(); }); diff --git a/spec/search/search-cursor-pagination.spec.ts b/spec/search/search-cursor-pagination.spec.ts index 1fd0f63..5e46322 100644 --- a/spec/search/search-cursor-pagination.spec.ts +++ b/spec/search/search-cursor-pagination.spec.ts @@ -36,16 +36,14 @@ describe('Search Cursor Pagination', () => { * - when index is [0-24], it has [50-26] * - when index is [25-49], is has null */ - await Promise.all( + await service.repository.save( Array.from({ length: 25 }, (_, index) => index).map((no: number) => - service.repository.save( - service.repository.create({ col1: `col${no % 2 === 0 ? '0' : '1'}_${no}`, col2: no, col3: 50 - no }), - ), + service.repository.create({ col1: `col${no % 2 === 0 ? '0' : '1'}_${no}`, col2: no, col3: 50 - no }), ), ); - await Promise.all( + await service.repository.save( Array.from({ length: 25 }, (_, index) => index + 25).map((no: number) => - service.repository.save(service.repository.create({ col1: `col${no % 2 === 0 ? '0' : '1'}_${no}`, col2: no })), + service.repository.create({ col1: `col${no % 2 === 0 ? '0' : '1'}_${no}`, col2: no }), ), ); expect(await TestEntity.count()).toEqual(50); diff --git a/spec/search/search-query-operator.spec.ts b/spec/search/search-query-operator.spec.ts index fbdc75d..4c1eaa4 100644 --- a/spec/search/search-query-operator.spec.ts +++ b/spec/search/search-query-operator.spec.ts @@ -31,16 +31,14 @@ describe('Search Query Operator', () => { * - when index is [0-4], it has [10-6] * - when index is [5-9], is has null */ - await Promise.all( + await service.repository.save( Array.from({ length: 5 }, (_, index) => index).map((no: number) => - service.repository.save( - service.repository.create({ col1: `col${no % 2 === 0 ? '0' : '1'}_${no}`, col2: no, col3: 10 - no }), - ), + service.repository.create({ col1: `col${no % 2 === 0 ? '0' : '1'}_${no}`, col2: no, col3: 10 - no }), ), ); - await Promise.all( + await service.repository.save( Array.from({ length: 5 }, (_, index) => index + 5).map((no: number) => - service.repository.save(service.repository.create({ col1: `col${no % 2 === 0 ? '0' : '1'}_${no}`, col2: no })), + service.repository.create({ col1: `col${no % 2 === 0 ? '0' : '1'}_${no}`, col2: no }), ), ); diff --git a/src/lib/abstract/abstract.pagination.spec.ts b/src/lib/abstract/abstract.pagination.spec.ts index 7989032..f513c60 100644 --- a/src/lib/abstract/abstract.pagination.spec.ts +++ b/src/lib/abstract/abstract.pagination.spec.ts @@ -1,10 +1,14 @@ import { AbstractPaginationRequest } from './abstract.pagination'; +import { PaginationResponse } from '../interface'; describe('AbstractPaginationRequest', () => { class PaginationRequest extends AbstractPaginationRequest { nextTotal(_dataLength?: number | undefined): number { throw new Error('Method not implemented.'); } + metadata(_take: number, _dataLength: number, _total: number, _nextCursor: string): PaginationResponse['metadata'] { + throw new Error('Method not implemented.'); + } } it('should do nothing when where is undefined', () => { const paginationRequest = new PaginationRequest(); diff --git a/src/lib/abstract/abstract.pagination.ts b/src/lib/abstract/abstract.pagination.ts index 8fe0d21..06ed93b 100644 --- a/src/lib/abstract/abstract.pagination.ts +++ b/src/lib/abstract/abstract.pagination.ts @@ -1,7 +1,7 @@ import { Expose } from 'class-transformer'; import { IsString, IsOptional } from 'class-validator'; -import { PaginationType } from '../interface'; +import { PaginationResponse, PaginationType } from '../interface'; interface PaginationQuery { where: string; @@ -74,4 +74,5 @@ export abstract class AbstractPaginationRequest { } abstract nextTotal(dataLength?: number): number; + abstract metadata(take: number, dataLength: number, total: number, nextCursor: string): PaginationResponse['metadata']; } diff --git a/src/lib/dto/pagination-cursor.dto.ts b/src/lib/dto/pagination-cursor.dto.ts index 969bd38..0bf6906 100644 --- a/src/lib/dto/pagination-cursor.dto.ts +++ b/src/lib/dto/pagination-cursor.dto.ts @@ -1,5 +1,5 @@ import { AbstractPaginationRequest } from '../abstract'; -import { PaginationType } from '../interface'; +import { CursorPaginationResponse, PaginationType } from '../interface'; export class PaginationCursorDto extends AbstractPaginationRequest { type: PaginationType.CURSOR = PaginationType.CURSOR; @@ -7,4 +7,12 @@ export class PaginationCursorDto extends AbstractPaginationRequest { nextTotal(dataLength: number): number { return this.total - dataLength; } + + metadata(take: number, _dataLength: number, total: number, nextCursor: string): CursorPaginationResponse['metadata'] { + return { + limit: take, + total, + nextCursor: this.makeQuery(total, nextCursor), + }; + } } diff --git a/src/lib/dto/pagination-offset.dto.ts b/src/lib/dto/pagination-offset.dto.ts index e84b082..4f5cb9b 100644 --- a/src/lib/dto/pagination-offset.dto.ts +++ b/src/lib/dto/pagination-offset.dto.ts @@ -2,7 +2,7 @@ import { Expose, Transform, Type } from 'class-transformer'; import { IsNumber, IsOptional, IsPositive, Max } from 'class-validator'; import { AbstractPaginationRequest } from '../abstract'; -import { PaginationType } from '../interface'; +import { OffsetPaginationResponse, PaginationType } from '../interface'; export class PaginationOffsetDto extends AbstractPaginationRequest { type: PaginationType.OFFSET = PaginationType.OFFSET; @@ -24,4 +24,14 @@ export class PaginationOffsetDto extends AbstractPaginationRequest { nextTotal(): number { return this.total; } + + metadata(take: number, dataLength: number, total: number, nextCursor: string): OffsetPaginationResponse['metadata'] { + return { + page: this.offset ? Math.floor(this.offset / take) + 1 : 1, + pages: total ? Math.ceil(total / take) : 1, + offset: (this.offset ?? 0) + dataLength, + total, + nextCursor: this.makeQuery(total, nextCursor), + }; + } } diff --git a/src/lib/request/read-many.request.ts b/src/lib/request/read-many.request.ts index 03cd450..4e032cc 100644 --- a/src/lib/request/read-many.request.ts +++ b/src/lib/request/read-many.request.ts @@ -111,32 +111,24 @@ export class CrudReadManyRequest { } toResponse(data: T[], total: number): PaginationResponse { + const take = this.findOptions.take; + const dataLength = data.length; const nextCursor = PaginationHelper.serialize( _.pick( data.at(-1), this.primaryKeys.map(({ name }) => name), ) as FindOptionsWhere, ); + if (this.pagination.type === PaginationType.OFFSET) { return { data, - metadata: { - page: this.pagination.offset ? Math.floor(this.pagination.offset / this.findOptions.take) + 1 : 1, - pages: total ? Math.ceil(total / this.findOptions.take) : 1, - offset: (this.pagination.offset ?? 0) + data.length, - total, - nextCursor: this.pagination.makeQuery(total, nextCursor), - }, + metadata: this.pagination.metadata(take, dataLength, total, nextCursor), }; } - return { data, - metadata: { - limit: this.findOptions.take, - total, - nextCursor: this.pagination.makeQuery(total, nextCursor), - }, + metadata: this.pagination.metadata(take, dataLength, total, nextCursor), }; } }