diff --git a/public/swagger.json b/public/swagger.json index bcec842..c52f73a 100644 --- a/public/swagger.json +++ b/public/swagger.json @@ -63,6 +63,59 @@ ] } }, + "/api/users": { + "get": { + "operationId": "UsersController_getUsers", + "parameters": [ + { + "name": "nameLike", + "required": false, + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "page", + "required": false, + "in": "query", + "schema": { + "default": 1, + "type": "number" + } + }, + { + "name": "limit", + "required": false, + "in": "query", + "schema": { + "default": 10, + "type": "number" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserOutputDTO" + } + } + } + } + }, + "tags": [ + "Users" + ], + "security": [ + { + "AccessToken": [] + } + ] + } + }, "/api/users/me": { "get": { "operationId": "UsersController_getMe", diff --git a/src/api/controllers/users.controller.ts b/src/api/controllers/users.controller.ts index 0a3a818..eb69d5f 100644 --- a/src/api/controllers/users.controller.ts +++ b/src/api/controllers/users.controller.ts @@ -18,6 +18,7 @@ import { Pagination } from 'nestjs-typeorm-paginate'; import { UserInventoryEntriesUseCase } from 'src/core/use-cases/user-inventory-entries.use-case'; import { QuickSoldUserInventoryEntryEntity } from 'src/infra/postgres/entities/quick-sold-user-inventory-entry.entity'; import { QuickSoldUserInventoryEntryOutputDTO } from '../dtos/user-inventory-entries/quick-sold-user-inventory-entry.output.dto'; +import { GetUsersInputDTO } from '../dtos/users/get-users.input.dto'; @ApiTags('Users') @Controller('users') @@ -26,10 +27,29 @@ export class UsersController { @InjectMapper() private readonly mapper: Mapper, + private readonly usersUseCase: UsersUseCase, private readonly userInventoryEntriesUseCase: UserInventoryEntriesUseCase, private readonly dataSource: DataSource, ) {} + @ApiOkResponse({ type: UserOutputDTO }) + @ApiSecurity('AccessToken') + @Get() + @UseGuards(JwtAuthGuard) + public async getUsers( + @Query() dto: GetUsersInputDTO, + @Query() paginationDTO: PaginationInputDTO, + ): Promise> { + const users = await this.usersUseCase.findUsers(dto, paginationDTO); + + return mapArrayWithPagination( + this.mapper, + users, + UserEntity, + UserOutputDTO, + ) + } + @ApiOkResponse({ type: UserOutputDTO }) @ApiSecurity('AccessToken') @Get('me') diff --git a/src/api/dtos/users/get-users.input.dto.ts b/src/api/dtos/users/get-users.input.dto.ts new file mode 100644 index 0000000..735d325 --- /dev/null +++ b/src/api/dtos/users/get-users.input.dto.ts @@ -0,0 +1,9 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsOptional, IsString } from 'class-validator'; + +export class GetUsersInputDTO { + @ApiPropertyOptional() + @IsString() + @IsOptional() + public readonly nameLike?: string; +} diff --git a/src/core/services/users.service.ts b/src/core/services/users.service.ts index c11890f..20e67f3 100644 --- a/src/core/services/users.service.ts +++ b/src/core/services/users.service.ts @@ -1,5 +1,6 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; +import { IPaginationOptions, paginate, Pagination } from 'nestjs-typeorm-paginate'; import { Nullable } from 'src/common/types'; import { UserEntity, UserModel, CreateUserEntityFields, UpdateUserEntityFields } from 'src/infra/postgres/entities/user.entity'; import { FindEntityRelationsOptions } from 'src/infra/postgres/other/types'; @@ -33,6 +34,20 @@ export class UsersService { }) as Promise>>; } + public async findManyWithPagination< + T extends FindEntityRelationsOptions = {}, + >( + paginationOptions: IPaginationOptions, + where?: FindOptionsWhere, + relations?: T, + ): Promise>> { + return paginate( + this.usersRepository, + paginationOptions, + { where, relations }, + ) as unknown as Promise>>; + } + public async createOne(fields: CreateUserEntityFields): Promise { const user = this.usersRepository.create(fields); diff --git a/src/core/use-cases/users.use-case.ts b/src/core/use-cases/users.use-case.ts index d19b959..e6096a4 100644 --- a/src/core/use-cases/users.use-case.ts +++ b/src/core/use-cases/users.use-case.ts @@ -5,7 +5,9 @@ import { CreateUserInputDTO } from 'src/api/dtos/users/create-user.input.dto'; import { UpdateUserInputDTO } from 'src/api/dtos/users/update-user.input.dto'; import { UUIDv4 } from 'src/common/types'; import { FindEntityRelationsOptions } from 'src/infra/postgres/other/types'; -import { FindOptionsWhere } from 'typeorm'; +import { FindOptionsWhere, Like } from 'typeorm'; +import { IPaginationOptions, Pagination } from 'nestjs-typeorm-paginate'; +import { GetUsersInputDTO } from 'src/api/dtos/users/get-users.input.dto'; @Injectable() export class UsersUseCase { @@ -13,6 +15,7 @@ export class UsersUseCase { private readonly usersService: UsersService, ) {} + // TODO: Rename all methods in use cases starting with find to starting with get public async findUser( where: FindOptionsWhere, errorMessage?: string, @@ -33,6 +36,20 @@ export class UsersUseCase { return this.findUser({ id }, errorMessageFn?.(id) ?? `User (\`${id}\`) not found`); } + public async findUsers( + dto: GetUsersInputDTO, + paginationOptions: IPaginationOptions + ): Promise> { + const where: FindOptionsWhere = { + ...(dto.nameLike && { name: Like(`%${dto.nameLike}%`) }), + }; + + return this.usersService.findManyWithPagination( + paginationOptions, + where, + ); + } + public async checkIfUserExistsByName(name: string) { return this.usersService.exist({ name }); }