From 82310c63142e9b00051050fc789cd29d9703c51f Mon Sep 17 00:00:00 2001 From: Gabriel Rosa Date: Mon, 18 Jul 2022 14:04:35 -0300 Subject: [PATCH] FEAT: Users roles id = seqid --- package-lock.json | 14 +++--- package.json | 2 +- src/modules/permissions/dto/entities.ts | 2 +- src/modules/roles/dto/entities.ts | 27 +++++----- src/modules/roles/roles.controller.ts | 4 +- src/modules/roles/roles.service.ts | 22 ++++++--- src/modules/users/dtos/entities.ts | 26 +++++++--- src/modules/users/users.controller.ts | 65 ++++++++++++++++++------- src/modules/users/users.service.ts | 49 ++++++++++++++++++- src/utils/ErrorBuilder.ts | 21 ++++++++ src/utils/errorCodes.ts | 15 ++++++ 11 files changed, 193 insertions(+), 54 deletions(-) diff --git a/package-lock.json b/package-lock.json index 59f61ba..c99b3fe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -36,7 +36,7 @@ "passport-hubspot-oauth2": "^1.0.3", "passport-mailchimp": "^1.1.0", "protospack": "2.5.1", - "protospack-v2": "3.5.1", + "protospack-v2": "file:../protospack-v2/protospack-v2-0.0.0.tgz", "reflect-metadata": "^0.1.13", "rimraf": "^3.0.2", "rxjs": "^7.5.5", @@ -9015,9 +9015,10 @@ } }, "node_modules/protospack-v2": { - "version": "3.5.1", - "resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/protospack-v2/-/protospack-v2-3.5.1.tgz", - "integrity": "sha512-qSD5D9p1f/FtPMovfwHG3nbnvpD8c2V01ifK2QB2V2SMPc/Qxe+IYiv3zhXu/KUQZGI82nYznXyMnmiJ9Jo9aw==", + "version": "0.0.0", + "resolved": "file:../protospack-v2/protospack-v2-0.0.0.tgz", + "integrity": "sha512-e9xqk0tm7AQcd4tZa1PbMsGRDHSbwJ6dUHAePL4myVGRH+j/g4K7K1dg5igd9daKVNIrJaTwZb5RmgoFaU6T4Q==", + "license": "ISC", "dependencies": { "@grpc/grpc-js": "^1.6.7", "rxjs": "^7.5.5", @@ -17245,9 +17246,8 @@ } }, "protospack-v2": { - "version": "3.5.1", - "resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com:443/npm/dadosfera-npm/protospack-v2/-/protospack-v2-3.5.1.tgz", - "integrity": "sha512-qSD5D9p1f/FtPMovfwHG3nbnvpD8c2V01ifK2QB2V2SMPc/Qxe+IYiv3zhXu/KUQZGI82nYznXyMnmiJ9Jo9aw==", + "version": "file:../protospack-v2/protospack-v2-0.0.0.tgz", + "integrity": "sha512-e9xqk0tm7AQcd4tZa1PbMsGRDHSbwJ6dUHAePL4myVGRH+j/g4K7K1dg5igd9daKVNIrJaTwZb5RmgoFaU6T4Q==", "requires": { "@grpc/grpc-js": "^1.6.7", "rxjs": "^7.5.5", diff --git a/package.json b/package.json index c02610b..ed44165 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "passport-hubspot-oauth2": "^1.0.3", "passport-mailchimp": "^1.1.0", "protospack": "2.5.1", - "protospack-v2": "3.5.1", + "protospack-v2": "file:../protospack-v2/protospack-v2-0.0.0.tgz", "reflect-metadata": "^0.1.13", "rimraf": "^3.0.2", "rxjs": "^7.5.5", diff --git a/src/modules/permissions/dto/entities.ts b/src/modules/permissions/dto/entities.ts index 4fee93f..f4dc174 100644 --- a/src/modules/permissions/dto/entities.ts +++ b/src/modules/permissions/dto/entities.ts @@ -1,4 +1,4 @@ -import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { ApiProperty } from '@nestjs/swagger'; export class PermissionDto { @ApiProperty() diff --git a/src/modules/roles/dto/entities.ts b/src/modules/roles/dto/entities.ts index a5cbaa2..b7ce791 100644 --- a/src/modules/roles/dto/entities.ts +++ b/src/modules/roles/dto/entities.ts @@ -1,18 +1,21 @@ import { ApiProperty, ApiPropertyOptional, OmitType } from '@nestjs/swagger'; -import { Permission } from 'src/modules/permissions/dto/entities'; +import { + Permission, + PermissionDto, +} from 'src/modules/permissions/dto/entities'; import { Customer, UserNoRolesAndCustomer, } from 'src/modules/users/dtos/entities'; -export class Role { +export class RoleDto { @ApiProperty() id: string; @ApiProperty() name: string; @ApiProperty() description: string; - @ApiPropertyOptional({ type: () => [Permission] }) - permissions: Permission[]; + @ApiPropertyOptional({ type: () => [PermissionDto] }) + permissions: PermissionDto[]; @ApiPropertyOptional({ type: () => [UserNoRolesAndCustomer] }) users: UserNoRolesAndCustomer[]; @ApiProperty() @@ -23,7 +26,7 @@ export class Role { customer?: Customer; } -export class CustomerRole extends OmitType(Role, ['customer']) {} +export class CustomerRole extends OmitType(RoleDto, ['customer']) {} export enum TrueOrFalseEnum { true = 'true', @@ -37,8 +40,8 @@ export class GetRolesByCustomerReq { } export class GetRolesByCustomerRes { - @ApiProperty({ type: [Role] }) - roles: Role[]; + @ApiProperty({ type: [RoleDto] }) + roles: RoleDto[]; } export class CreateRoleReq { @ApiProperty() @@ -63,7 +66,7 @@ export class GrantPermissionsToRoleReq { } export class GrantPermissionsToRoleRes { @ApiProperty() - role: Role; + role: RoleDto; } export class RevokePermissionsFromRoleReq { @@ -74,7 +77,7 @@ export class RevokePermissionsFromRoleReq { } export class RevokePermissionsFromRoleRes { @ApiProperty() - role: Role; + role: RoleDto; } export class UpdateRoleReq { @@ -86,7 +89,7 @@ export class UpdateRoleReq { export class UpdateRoleRes { @ApiProperty() - role: Role; + role: RoleDto; } export class SetRolePermissionsReq { @@ -97,7 +100,7 @@ export class SetRolePermissionsReq { } export class SetRolePermissionsRes { @ApiProperty() - role: Role; + role: RoleDto; } export class SetRoleUsersReq { @@ -108,5 +111,5 @@ export class SetRoleUsersReq { } export class SetRoleUsersRes { @ApiProperty() - role: Role; + role: RoleDto; } diff --git a/src/modules/roles/roles.controller.ts b/src/modules/roles/roles.controller.ts index d2c2289..fd904ad 100644 --- a/src/modules/roles/roles.controller.ts +++ b/src/modules/roles/roles.controller.ts @@ -32,7 +32,7 @@ import { RevokePermissionsFromRoleRes, UpdateRoleReq, UpdateRoleRes, - Role, + RoleDto, SetRolePermissionsReq, SetRolePermissionsRes, SetRoleUsersRes, @@ -144,7 +144,7 @@ export class RolesController { } @Get(':id') - @ApiOkResponse({ type: Role }) + @ApiOkResponse({ type: RoleDto }) @RequireAllPermissions(PERMISSIONS.USERS.permissions.ADMIN) async getRoleById( @User() user: RequestUser, diff --git a/src/modules/roles/roles.service.ts b/src/modules/roles/roles.service.ts index 1aebc5c..9ef20f7 100644 --- a/src/modules/roles/roles.service.ts +++ b/src/modules/roles/roles.service.ts @@ -6,7 +6,7 @@ import { GetRolesByCustomerReq, GrantPermissionsToRoleReq, RevokePermissionsFromRoleReq, - Role, + RoleDto, SetRolePermissionsReq, SetRoleUsersReq, UpdateRoleReq, @@ -16,6 +16,7 @@ import { ClientGrpc } from '@nestjs/microservices'; import { lastValueFrom } from 'rxjs'; import { PermissionsService } from '../permissions/permissions.service'; import { LanguageEnum } from 'src/authentication/permissions.enum'; +import { Role } from 'protospack-v2/dist/lib/Duc/interfaces/entities'; @Injectable() export class RolesService { @@ -164,16 +165,23 @@ export class RolesService { const [roleTreated] = this.getRolesPermissionsName([role.role]); return { role: roleTreated }; } - getRolesPermissionsName(roles: Role[]) { + getRolesPermissionsName(roles: Role[]): RoleDto[] { + const newRoles: RoleDto[] = []; for (const role of roles) { - const permissions = this.permissionsService.getAllPermissions( + const allPermissions = this.permissionsService.getAllPermissions( this.language, ); - role.permissions.forEach((p) => { - const permission = permissions.find((per) => per.seqid === p.seqid); - p.name = permission.name; + const newPermissions = role.permissions.map((p) => { + const permission = allPermissions.find((per) => per.seqid === p.seqid); + return { + ...p, + name: permission.name, + id: p.seqid, + }; }); + const newRole: RoleDto = { ...role, permissions: newPermissions }; + newRoles.push(newRole); } - return roles; + return newRoles; } } diff --git a/src/modules/users/dtos/entities.ts b/src/modules/users/dtos/entities.ts index 3664d05..46170c3 100644 --- a/src/modules/users/dtos/entities.ts +++ b/src/modules/users/dtos/entities.ts @@ -1,5 +1,5 @@ import { ApiProperty, ApiPropertyOptional, OmitType } from '@nestjs/swagger'; -import { Role } from 'src/modules/roles/dto/entities'; +import { RoleDto } from 'src/modules/roles/dto/entities'; export class Customer { @ApiProperty() id: string; @@ -23,8 +23,8 @@ export class User { name: string; @ApiProperty() email: string; - @ApiProperty({ type: [Role] }) - roles: Role[]; + @ApiProperty({ type: [RoleDto] }) + roles: RoleDto[]; @ApiProperty() jobTitle?: string; @ApiProperty() @@ -45,8 +45,8 @@ export class UserNoRolesAndCustomer extends OmitType(UserNoRoles, [ 'customer', ]) {} export class UserByCustomer { - @ApiProperty({ type: [Role] }) - roles: Role[]; + @ApiProperty({ type: [RoleDto] }) + roles: RoleDto[]; @ApiProperty() id: string; @ApiProperty() @@ -72,7 +72,7 @@ export class CreateUserReq { @ApiPropertyOptional() department?: string; @ApiPropertyOptional() - hierarchy?: string; + hierarchy?: number; } export class CreateUserRes { @ApiProperty() @@ -87,6 +87,19 @@ export class BatchCreateUserRes { users: User[]; } +export class UpdateUserReq { + @ApiPropertyOptional() + name?: string; + @ApiPropertyOptional() + roles?: string[]; + @ApiPropertyOptional() + jobTitle?: string; + @ApiPropertyOptional() + department?: string; + @ApiPropertyOptional() + hierarchy?: number; +} + export class GetAllUsersByCustomerIdReq { @ApiPropertyOptional() s?: string; @@ -117,4 +130,5 @@ export class SearchUserReq { @ApiProperty() s: string; } + export class SearchUserRes extends GetAllUsersByCustomerIdRes {} diff --git a/src/modules/users/users.controller.ts b/src/modules/users/users.controller.ts index 7d7b3ed..ac3e021 100644 --- a/src/modules/users/users.controller.ts +++ b/src/modules/users/users.controller.ts @@ -5,27 +5,35 @@ import { Get, Inject, Param, + Patch, Post, Put, - Query, + Req, + UseFilters, } from '@nestjs/common'; -import { ApiOkResponse, ApiTags } from '@nestjs/swagger'; +import { ApiCreatedResponse, ApiOkResponse, ApiTags } from '@nestjs/swagger'; import { DadosferaLogger } from 'dadosfera-logs'; +import { RequireAllPermissions } from 'src/authentication/authentication.decorator'; +import { PERMISSIONS } from 'src/authentication/permissions.enum'; import { RequestUser, User } from 'src/authentication/user.decorator'; +import { GrpcToHttpExceptionFilter } from 'src/error/grpc-to-http-exception.filter'; import { AssignRoleToUserReq, + BatchCreateUserReq, + BatchCreateUserRes, CreateUserReq, CreateUserRes, DeleteUserRes, - GetAllUsersByCustomerIdReq, GetAllUsersByCustomerIdRes, UnassignRoleToUserReq, + UserByCustomer, } from './dtos/entities'; import { UsersService } from './users.service'; // TODO GET de hierarquias e do PATCH em usuário @ApiTags('Users') @Controller('users') +@UseFilters(GrpcToHttpExceptionFilter) export class UsersController { logger: any; @@ -38,37 +46,48 @@ export class UsersController { } @Get() + @RequireAllPermissions(PERMISSIONS.USERS.permissions.ADMIN) @ApiOkResponse({ type: GetAllUsersByCustomerIdRes, description: 'List of users of a customer', }) - async getAllUsersByCustomerId( - @User() user: RequestUser, - @Query('s') searchQuery: GetAllUsersByCustomerIdReq, - ) { + async getAllUsersByCustomerId(@User() user: RequestUser) { this.logger.info('getAllUsersByCustomerId', { user }); - return await this.userService.findAllUsersByCustomerId( - user.customer_id, - searchQuery, - ); + return await this.userService.findAllUsersByCustomerId(user.customer_id); } - @Post() - @ApiOkResponse({ type: CreateUserRes }) + @RequireAllPermissions(PERMISSIONS.USERS.permissions.ADMIN) + @ApiCreatedResponse({ type: CreateUserRes }) async createUser(@User() user: RequestUser, @Body() body: CreateUserReq) { this.logger.info('createUser', { user }); + return await this.userService.createUser(body); + } + + @Patch(':id') + @RequireAllPermissions(PERMISSIONS.USERS.permissions.ADMIN) + @ApiCreatedResponse({ type: CreateUserRes }) + async updateUser( + @User() user: RequestUser, + @Body() body: CreateUserReq, + @Param('id') id: string, + ) { + this.logger.info('createUser', { user }); + return await this.userService.updateUser(body, id); } @Post('batch') - @ApiOkResponse({ type: CreateUserRes }) + @RequireAllPermissions(PERMISSIONS.USERS.permissions.ADMIN) + @ApiCreatedResponse({ type: BatchCreateUserRes }) async batchCreateUser( @User() user: RequestUser, - @Body() body: CreateUserReq, + @Body() body: BatchCreateUserReq, ) { this.logger.info('batchCreateUser', { user }); + return await this.userService.batchCreateUser(body); } @Delete(':id') + @RequireAllPermissions(PERMISSIONS.USERS.permissions.ADMIN) @ApiOkResponse({ type: DeleteUserRes }) async deleteUser(@User() user: RequestUser, @Param('id') id: string) { this.logger.info('deleteUser', { user }); @@ -76,6 +95,7 @@ export class UsersController { } @Put('role') + @RequireAllPermissions(PERMISSIONS.USERS.permissions.ADMIN) async assignRoleToUser( @User() user: RequestUser, @Body() body: AssignRoleToUserReq, @@ -85,11 +105,22 @@ export class UsersController { } @Delete('role') + @RequireAllPermissions(PERMISSIONS.USERS.permissions.ADMIN) async unassignRoleToUser( @User() user: RequestUser, - @Body() body: UnassignRoleToUserReq, + @Req() request: UnassignRoleToUserReq, ) { this.logger.info('unassignRoleToUser', { user }); - return await this.userService.unassignRoleToUser(body); + return await this.userService.unassignRoleToUser(request); + } + + @Get(':id') + @RequireAllPermissions(PERMISSIONS.USERS.permissions.ADMIN) + @ApiOkResponse({ + type: UserByCustomer, + }) + async findOneById(@User() user: RequestUser, @Param('id') id: string) { + this.logger.info('findOneById', { user }); + return await this.userService.findOneById(id); } } diff --git a/src/modules/users/users.service.ts b/src/modules/users/users.service.ts index 86263ac..6a69216 100644 --- a/src/modules/users/users.service.ts +++ b/src/modules/users/users.service.ts @@ -10,6 +10,11 @@ import { UnassignRoleFromUserRequest, } from 'protospack-v2/dist/lib/Duc/interfaces/messages'; import { lastValueFrom } from 'rxjs'; +import { + BatchCreateUserReq, + CreateUserReq, + UpdateUserReq, +} from './dtos/entities'; @Injectable() export class UsersService implements OnModuleInit { @@ -30,12 +35,54 @@ export class UsersService implements OnModuleInit { ); } - async findAllUsersByCustomerId(customerId, searchQuery) { + async findAllUsersByCustomerId(customerId: string) { return lastValueFrom( this.usersService.UserFindAllByCustomerId({ customerId }), ); } + async findOneById(id: string) { + return lastValueFrom(this.usersService.UserFindOneById({ id })); + } + + async createUser(user: CreateUserReq) { + const { email, department, hierarchy, jobTitle, name, roles } = user; + return lastValueFrom( + this.usersService.UserCreate({ + email, + hierarchy, + name, + roleIds: roles, + department, + jobTitle, + }), + ); + } + + async updateUser(user: UpdateUserReq, id: string) { + const { department, hierarchy, jobTitle, name, roles } = user; + return lastValueFrom( + this.usersService.UserUpdate({ + name, + customerId: '', + id, + }), + ); + } + + async batchCreateUser({ users }: BatchCreateUserReq) { + const usersToCreate = users.map((u) => ({ + ...u, + roleIds: u.roles, + name: u.name || '', + hierarchy: u.hierarchy || null, + })); + return lastValueFrom( + this.usersService.UserBatchCreate({ + users: usersToCreate, + }), + ); + } async assignRoleToUser(body: AssignRoleToUserRequest) { return lastValueFrom(this.usersService.AssignRoleToUser(body)); } diff --git a/src/utils/ErrorBuilder.ts b/src/utils/ErrorBuilder.ts index 4555b44..ec353be 100644 --- a/src/utils/ErrorBuilder.ts +++ b/src/utils/ErrorBuilder.ts @@ -142,6 +142,27 @@ export function EnrichErrorCode(code: string) { message: 'Grupo solicitado não encontrado', code, }; + case ErrorCodes.USER.NOT_FOUND: + return { + statusCode: HttpStatus.NOT_FOUND, + error: 'Usuário não encontrado', + message: 'Usuário solicitado não encontrado', + code, + }; + case ErrorCodes.USER.ALREADY_EXISTS: + return { + statusCode: HttpStatus.NOT_FOUND, + error: 'Usuário encontrado', + message: 'Nome de usuário já existente', + code, + }; + case ErrorCodes.USER.INVALID_ID: + return { + statusCode: HttpStatus.NOT_FOUND, + error: 'Id inválido', + message: 'Id encontra-se num formato inválido', + code, + }; case ErrorCodes.INTERNAL: case ErrorCodes.UNKNOWN: default: diff --git a/src/utils/errorCodes.ts b/src/utils/errorCodes.ts index f8d976a..04a9eb0 100644 --- a/src/utils/errorCodes.ts +++ b/src/utils/errorCodes.ts @@ -18,6 +18,19 @@ export const ROLE = { NOT_FOUND: 'ROLE.NOT_FOUND', }; +export const USER = { + INVALID_ID: 'USER.INVALID_ID', + NOT_FOUND: 'USER.NOT_FOUND', + ALREADY_EXISTS: 'USER.ALREADY_EXISTS', + ROLE_ALREADY_ASSIGNED: 'USER.ROLE_ALREADY_ASSIGNED', + ROLE_NOT_ASSIGNED: 'USER.ROLE_NOT_ASSIGNED', +}; + +export const CUSTOMER = { + INVALID_ID: 'CUSTOMER.INVALID_ID', + NOT_FOUND: 'CUSTOMER.NOT_FOUND', + ALREADY_EXISTS: 'CUSTOMER.ALREADY_EXISTS', +}; const TERMS_OF_USE = { UP_TO_DATE: 'TERMS_OF_USE.UP_TO_DATE', REQUIRED: 'TERMS_OF_USE.REQUIRED', @@ -30,6 +43,8 @@ const ErrorCodes = { AUTH, ROLE, TERMS_OF_USE, + USER, + CUSTOMER, }; export default ErrorCodes;