find all users by ids
This commit is contained in:
		
							parent
							
								
									ad8b98da40
								
							
						
					
					
						commit
						3f4236f34f
					
				
							
								
								
									
										12
									
								
								README.md
								
								
								
								
							
							
						
						
									
										12
									
								
								README.md
								
								
								
								
							| 
						 | 
					@ -56,6 +56,18 @@ The app exposes the following [gRPC](https://grpc.io/) services :
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    ```
 | 
					    ```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-   **FindAllByIds** : find all users for the given ids
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ```json
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        "ids": [
 | 
				
			||||||
 | 
					            "80126a61-d128-4f96-afdb-92e33c75a3e1",
 | 
				
			||||||
 | 
					            "80126a61-d128-4f96-afdb-92e33c75a3e2",
 | 
				
			||||||
 | 
					            "80126a61-d128-4f96-afdb-92e33c75a3e3"
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    ```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-   **Create** : create a user
 | 
					-   **Create** : create a user
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ```json
 | 
					    ```json
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										49
									
								
								package.json
								
								
								
								
							
							
						
						
									
										49
									
								
								package.json
								
								
								
								
							| 
						 | 
					@ -30,27 +30,26 @@
 | 
				
			||||||
    "migrate:deploy": "npx prisma migrate deploy"
 | 
					    "migrate:deploy": "npx prisma migrate deploy"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "dependencies": {
 | 
					  "dependencies": {
 | 
				
			||||||
    "@grpc/grpc-js": "^1.9.6",
 | 
					    "@grpc/grpc-js": "^1.9.11",
 | 
				
			||||||
    "@grpc/proto-loader": "^0.7.10",
 | 
					    "@grpc/proto-loader": "^0.7.10",
 | 
				
			||||||
    "@songkeys/nestjs-redis": "^10.0.0",
 | 
					    "@songkeys/nestjs-redis": "^10.0.0",
 | 
				
			||||||
    "@mobicoop/configuration-module": "^3.0.0",
 | 
					    "@mobicoop/ddd-library": "^2.4.0",
 | 
				
			||||||
    "@mobicoop/ddd-library": "^2.0.0",
 | 
					 | 
				
			||||||
    "@mobicoop/health-module": "^2.3.1",
 | 
					    "@mobicoop/health-module": "^2.3.1",
 | 
				
			||||||
    "@mobicoop/message-broker-module": "^2.1.1",
 | 
					    "@mobicoop/message-broker-module": "^2.1.1",
 | 
				
			||||||
    "@nestjs/cache-manager": "^2.1.0",
 | 
					    "@nestjs/cache-manager": "^2.1.1",
 | 
				
			||||||
    "@nestjs/common": "^10.2.7",
 | 
					    "@nestjs/common": "^10.2.10",
 | 
				
			||||||
    "@nestjs/config": "^3.1.1",
 | 
					    "@nestjs/config": "^3.1.1",
 | 
				
			||||||
    "@nestjs/core": "^10.2.7",
 | 
					    "@nestjs/core": "^10.2.10",
 | 
				
			||||||
    "@nestjs/cqrs": "^10.2.6",
 | 
					    "@nestjs/cqrs": "^10.2.6",
 | 
				
			||||||
    "@nestjs/event-emitter": "^2.0.2",
 | 
					    "@nestjs/event-emitter": "^2.0.3",
 | 
				
			||||||
    "@nestjs/microservices": "^10.2.7",
 | 
					    "@nestjs/microservices": "^10.2.10",
 | 
				
			||||||
    "@nestjs/platform-express": "^10.2.7",
 | 
					    "@nestjs/platform-express": "^10.2.10",
 | 
				
			||||||
    "@nestjs/terminus": "^10.1.1",
 | 
					    "@nestjs/terminus": "^10.1.1",
 | 
				
			||||||
    "@prisma/client": "^5.4.2",
 | 
					    "@prisma/client": "^5.6.0",
 | 
				
			||||||
    "@types/supertest": "^2.0.14",
 | 
					    "@types/supertest": "^2.0.16",
 | 
				
			||||||
    "@typescript-eslint/eslint-plugin": "^6.8.0",
 | 
					    "@typescript-eslint/eslint-plugin": "^6.12.0",
 | 
				
			||||||
    "@typescript-eslint/parser": "^6.8.0",
 | 
					    "@typescript-eslint/parser": "^6.12.0",
 | 
				
			||||||
    "cache-manager": "^5.2.4",
 | 
					    "cache-manager": "^5.3.1",
 | 
				
			||||||
    "cache-manager-ioredis-yet": "^1.2.2",
 | 
					    "cache-manager-ioredis-yet": "^1.2.2",
 | 
				
			||||||
    "class-transformer": "^0.5.1",
 | 
					    "class-transformer": "^0.5.1",
 | 
				
			||||||
    "class-validator": "^0.14.0",
 | 
					    "class-validator": "^0.14.0",
 | 
				
			||||||
| 
						 | 
					@ -58,25 +57,25 @@
 | 
				
			||||||
    "ioredis": "^5.3.2"
 | 
					    "ioredis": "^5.3.2"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "devDependencies": {
 | 
					  "devDependencies": {
 | 
				
			||||||
    "@nestjs/cli": "^10.1.18",
 | 
					    "@nestjs/cli": "^10.2.1",
 | 
				
			||||||
    "@nestjs/schematics": "^10.0.2",
 | 
					    "@nestjs/schematics": "^10.0.3",
 | 
				
			||||||
    "@nestjs/testing": "^10.2.7",
 | 
					    "@nestjs/testing": "^10.2.10",
 | 
				
			||||||
    "@types/jest": "^29.5.6",
 | 
					    "@types/jest": "^29.5.10",
 | 
				
			||||||
    "@types/node": "^20.8.6",
 | 
					    "@types/node": "^20.9.4",
 | 
				
			||||||
    "@types/uuid": "^9.0.5",
 | 
					    "@types/uuid": "^9.0.7",
 | 
				
			||||||
    "eslint": "^8.51.0",
 | 
					    "eslint": "^8.54.0",
 | 
				
			||||||
    "eslint-config-prettier": "^9.0.0",
 | 
					    "eslint-config-prettier": "^9.0.0",
 | 
				
			||||||
    "eslint-plugin-prettier": "^5.0.1",
 | 
					    "eslint-plugin-prettier": "^5.0.1",
 | 
				
			||||||
    "jest": "29.7.0",
 | 
					    "jest": "29.7.0",
 | 
				
			||||||
    "prettier": "^3.0.3",
 | 
					    "prettier": "^3.1.0",
 | 
				
			||||||
    "prisma": "^5.4.2",
 | 
					    "prisma": "^5.6.0",
 | 
				
			||||||
    "source-map-support": "^0.5.21",
 | 
					    "source-map-support": "^0.5.21",
 | 
				
			||||||
    "supertest": "^6.3.3",
 | 
					    "supertest": "^6.3.3",
 | 
				
			||||||
    "ts-jest": "29.1.1",
 | 
					    "ts-jest": "29.1.1",
 | 
				
			||||||
    "ts-loader": "^9.5.0",
 | 
					    "ts-loader": "^9.5.1",
 | 
				
			||||||
    "ts-node": "^10.9.1",
 | 
					    "ts-node": "^10.9.1",
 | 
				
			||||||
    "tsconfig-paths": "4.2.0",
 | 
					    "tsconfig-paths": "4.2.0",
 | 
				
			||||||
    "typescript": "^5.2.2"
 | 
					    "typescript": "^5.3.2"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "jest": {
 | 
					  "jest": {
 | 
				
			||||||
    "moduleFileExtensions": [
 | 
					    "moduleFileExtensions": [
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,12 +5,6 @@ export const SERVICE_NAME = 'user';
 | 
				
			||||||
export const GRPC_PACKAGE_NAME = 'user';
 | 
					export const GRPC_PACKAGE_NAME = 'user';
 | 
				
			||||||
export const GRPC_SERVICE_NAME = 'UserService';
 | 
					export const GRPC_SERVICE_NAME = 'UserService';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// configuration
 | 
					 | 
				
			||||||
export const SERVICE_CONFIGURATION_SET_QUEUE = 'user-configuration-set';
 | 
					 | 
				
			||||||
export const SERVICE_CONFIGURATION_DELETE_QUEUE = 'user-configuration-delete';
 | 
					 | 
				
			||||||
export const SERVICE_CONFIGURATION_PROPAGATE_QUEUE =
 | 
					 | 
				
			||||||
  'user-configuration-propagate';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// health
 | 
					// health
 | 
				
			||||||
export const GRPC_HEALTH_PACKAGE_NAME = 'health';
 | 
					export const GRPC_HEALTH_PACKAGE_NAME = 'health';
 | 
				
			||||||
export const HEALTH_USER_REPOSITORY = 'UserRepository';
 | 
					export const HEALTH_USER_REPOSITORY = 'UserRepository';
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,10 +1,6 @@
 | 
				
			||||||
import { Module } from '@nestjs/common';
 | 
					import { Module } from '@nestjs/common';
 | 
				
			||||||
import { ConfigModule, ConfigService } from '@nestjs/config';
 | 
					import { ConfigModule } from '@nestjs/config';
 | 
				
			||||||
import { UserModule } from './modules/user/user.module';
 | 
					import { UserModule } from './modules/user/user.module';
 | 
				
			||||||
import {
 | 
					 | 
				
			||||||
  ConfigurationModule,
 | 
					 | 
				
			||||||
  ConfigurationModuleOptions,
 | 
					 | 
				
			||||||
} from '@mobicoop/configuration-module';
 | 
					 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  HealthModule,
 | 
					  HealthModule,
 | 
				
			||||||
  HealthModuleOptions,
 | 
					  HealthModuleOptions,
 | 
				
			||||||
| 
						 | 
					@ -18,9 +14,6 @@ import { EventEmitterModule } from '@nestjs/event-emitter';
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  HEALTH_CRITICAL_LOGGING_KEY,
 | 
					  HEALTH_CRITICAL_LOGGING_KEY,
 | 
				
			||||||
  HEALTH_USER_REPOSITORY,
 | 
					  HEALTH_USER_REPOSITORY,
 | 
				
			||||||
  SERVICE_CONFIGURATION_DELETE_QUEUE,
 | 
					 | 
				
			||||||
  SERVICE_CONFIGURATION_PROPAGATE_QUEUE,
 | 
					 | 
				
			||||||
  SERVICE_CONFIGURATION_SET_QUEUE,
 | 
					 | 
				
			||||||
  SERVICE_NAME,
 | 
					  SERVICE_NAME,
 | 
				
			||||||
} from './app.constants';
 | 
					} from './app.constants';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -28,36 +21,6 @@ import {
 | 
				
			||||||
  imports: [
 | 
					  imports: [
 | 
				
			||||||
    ConfigModule.forRoot({ isGlobal: true }),
 | 
					    ConfigModule.forRoot({ isGlobal: true }),
 | 
				
			||||||
    EventEmitterModule.forRoot(),
 | 
					    EventEmitterModule.forRoot(),
 | 
				
			||||||
    ConfigurationModule.forRootAsync({
 | 
					 | 
				
			||||||
      imports: [ConfigModule],
 | 
					 | 
				
			||||||
      inject: [ConfigService],
 | 
					 | 
				
			||||||
      useFactory: async (
 | 
					 | 
				
			||||||
        configService: ConfigService,
 | 
					 | 
				
			||||||
      ): Promise<ConfigurationModuleOptions> => ({
 | 
					 | 
				
			||||||
        domain: configService.get<string>(
 | 
					 | 
				
			||||||
          'SERVICE_CONFIGURATION_DOMAIN',
 | 
					 | 
				
			||||||
        ) as string,
 | 
					 | 
				
			||||||
        messageBroker: {
 | 
					 | 
				
			||||||
          uri: configService.get<string>('MESSAGE_BROKER_URI') as string,
 | 
					 | 
				
			||||||
          exchange: {
 | 
					 | 
				
			||||||
            name: configService.get<string>(
 | 
					 | 
				
			||||||
              'MESSAGE_BROKER_EXCHANGE',
 | 
					 | 
				
			||||||
            ) as string,
 | 
					 | 
				
			||||||
            durable: configService.get<boolean>(
 | 
					 | 
				
			||||||
              'MESSAGE_BROKER_EXCHANGE',
 | 
					 | 
				
			||||||
            ) as boolean,
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        redis: {
 | 
					 | 
				
			||||||
          host: configService.get<string>('REDIS_HOST') as string,
 | 
					 | 
				
			||||||
          password: configService.get<string>('REDIS_PASSWORD'),
 | 
					 | 
				
			||||||
          port: configService.get<number>('REDIS_PORT') as number,
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        setConfigurationQueue: SERVICE_CONFIGURATION_SET_QUEUE,
 | 
					 | 
				
			||||||
        deleteConfigurationQueue: SERVICE_CONFIGURATION_DELETE_QUEUE,
 | 
					 | 
				
			||||||
        propagateConfigurationQueue: SERVICE_CONFIGURATION_PROPAGATE_QUEUE,
 | 
					 | 
				
			||||||
      }),
 | 
					 | 
				
			||||||
    }),
 | 
					 | 
				
			||||||
    HealthModule.forRootAsync({
 | 
					    HealthModule.forRootAsync({
 | 
				
			||||||
      imports: [UserModule, MessagerModule],
 | 
					      imports: [UserModule, MessagerModule],
 | 
				
			||||||
      inject: [USER_REPOSITORY, MESSAGE_PUBLISHER],
 | 
					      inject: [USER_REPOSITORY, MESSAGE_PUBLISHER],
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,17 @@
 | 
				
			||||||
 | 
					import { IQueryHandler, QueryHandler } from '@nestjs/cqrs';
 | 
				
			||||||
 | 
					import { Inject } from '@nestjs/common';
 | 
				
			||||||
 | 
					import { USER_REPOSITORY } from '@modules/user/user.di-tokens';
 | 
				
			||||||
 | 
					import { UserRepositoryPort } from '../../ports/user.repository.port';
 | 
				
			||||||
 | 
					import { UserEntity } from '@modules/user/core/domain/user.entity';
 | 
				
			||||||
 | 
					import { FindUsersByIdsQuery } from './find-users-by-ids.query';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@QueryHandler(FindUsersByIdsQuery)
 | 
				
			||||||
 | 
					export class FindUsersByIdsQueryHandler implements IQueryHandler {
 | 
				
			||||||
 | 
					  constructor(
 | 
				
			||||||
 | 
					    @Inject(USER_REPOSITORY)
 | 
				
			||||||
 | 
					    private readonly userRepository: UserRepositoryPort,
 | 
				
			||||||
 | 
					  ) {}
 | 
				
			||||||
 | 
					  async execute(query: FindUsersByIdsQuery): Promise<UserEntity[]> {
 | 
				
			||||||
 | 
					    return await this.userRepository.findAllByIds(query.ids);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,10 @@
 | 
				
			||||||
 | 
					import { QueryBase } from '@mobicoop/ddd-library';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class FindUsersByIdsQuery extends QueryBase {
 | 
				
			||||||
 | 
					  readonly ids: string[];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  constructor(ids: string[]) {
 | 
				
			||||||
 | 
					    super();
 | 
				
			||||||
 | 
					    this.ids = ids;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,5 @@
 | 
				
			||||||
 | 
					import { UserResponseDto } from './user.response.dto';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class UsersResponseDto {
 | 
				
			||||||
 | 
					  readonly users: readonly UserResponseDto[];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,7 @@
 | 
				
			||||||
 | 
					import { ArrayMinSize, IsArray } from 'class-validator';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class FindUsersByIdsRequestDto {
 | 
				
			||||||
 | 
					  @IsArray()
 | 
				
			||||||
 | 
					  @ArrayMinSize(1)
 | 
				
			||||||
 | 
					  ids: string[];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,44 @@
 | 
				
			||||||
 | 
					import { Controller, UsePipes } from '@nestjs/common';
 | 
				
			||||||
 | 
					import { QueryBus } from '@nestjs/cqrs';
 | 
				
			||||||
 | 
					import { GrpcMethod, RpcException } from '@nestjs/microservices';
 | 
				
			||||||
 | 
					import { RpcExceptionCode } from '@mobicoop/ddd-library';
 | 
				
			||||||
 | 
					import { RpcValidationPipe } from '@mobicoop/ddd-library';
 | 
				
			||||||
 | 
					import { UserMapper } from '@modules/user/user.mapper';
 | 
				
			||||||
 | 
					import { UserEntity } from '@modules/user/core/domain/user.entity';
 | 
				
			||||||
 | 
					import { GRPC_SERVICE_NAME } from '@src/app.constants';
 | 
				
			||||||
 | 
					import { FindUsersByIdsRequestDto } from './dtos/find-users-by-ids.request.dto';
 | 
				
			||||||
 | 
					import { FindUsersByIdsQuery } from '@modules/user/core/application/queries/find-users-by-ids/find-users-by-ids.query';
 | 
				
			||||||
 | 
					import { UsersResponseDto } from '../dtos/users.response.dto';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@UsePipes(
 | 
				
			||||||
 | 
					  new RpcValidationPipe({
 | 
				
			||||||
 | 
					    whitelist: false,
 | 
				
			||||||
 | 
					    forbidUnknownValues: false,
 | 
				
			||||||
 | 
					  }),
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					@Controller()
 | 
				
			||||||
 | 
					export class FindUsersByIdsGrpcController {
 | 
				
			||||||
 | 
					  constructor(
 | 
				
			||||||
 | 
					    protected readonly mapper: UserMapper,
 | 
				
			||||||
 | 
					    private readonly queryBus: QueryBus,
 | 
				
			||||||
 | 
					  ) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @GrpcMethod(GRPC_SERVICE_NAME, 'FindAllByIds')
 | 
				
			||||||
 | 
					  async findAllbyIds(
 | 
				
			||||||
 | 
					    data: FindUsersByIdsRequestDto,
 | 
				
			||||||
 | 
					  ): Promise<UsersResponseDto> {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      const users: UserEntity[] = await this.queryBus.execute(
 | 
				
			||||||
 | 
					        new FindUsersByIdsQuery(data.ids),
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					      return {
 | 
				
			||||||
 | 
					        users: users.map((user: UserEntity) => this.mapper.toResponse(user)),
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					    } catch (e) {
 | 
				
			||||||
 | 
					      throw new RpcException({
 | 
				
			||||||
 | 
					        code: RpcExceptionCode.UNKNOWN,
 | 
				
			||||||
 | 
					        message: e.message,
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -4,7 +4,7 @@ package user;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
service UserService {
 | 
					service UserService {
 | 
				
			||||||
  rpc FindOneById(UserById) returns (User);
 | 
					  rpc FindOneById(UserById) returns (User);
 | 
				
			||||||
  rpc FindAll(UserFilter) returns (Users);
 | 
					  rpc FindAllByIds(UsersById) returns (Users);
 | 
				
			||||||
  rpc Create(User) returns (UserById);
 | 
					  rpc Create(User) returns (UserById);
 | 
				
			||||||
  rpc Update(User) returns (UserById);
 | 
					  rpc Update(User) returns (UserById);
 | 
				
			||||||
  rpc Delete(UserById) returns (Empty);
 | 
					  rpc Delete(UserById) returns (Empty);
 | 
				
			||||||
| 
						 | 
					@ -14,6 +14,10 @@ message UserById {
 | 
				
			||||||
  string id = 1;
 | 
					  string id = 1;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					message UsersById {
 | 
				
			||||||
 | 
					  repeated string ids = 1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
message User {
 | 
					message User {
 | 
				
			||||||
  string id = 1;
 | 
					  string id = 1;
 | 
				
			||||||
  string firstName = 2;
 | 
					  string firstName = 2;
 | 
				
			||||||
| 
						 | 
					@ -22,14 +26,8 @@ message User {
 | 
				
			||||||
  string phone = 5;
 | 
					  string phone = 5;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
message UserFilter {
 | 
					 | 
				
			||||||
  optional int32 page = 1;
 | 
					 | 
				
			||||||
  optional int32 perPage = 2;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
message Users {
 | 
					message Users {
 | 
				
			||||||
  repeated User data = 1;
 | 
					  repeated User users = 1;
 | 
				
			||||||
  int32 total = 2;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
message Empty {}
 | 
					message Empty {}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,82 @@
 | 
				
			||||||
 | 
					import { Test, TestingModule } from '@nestjs/testing';
 | 
				
			||||||
 | 
					import { UserEntity } from '@modules/user/core/domain/user.entity';
 | 
				
			||||||
 | 
					import { USER_REPOSITORY } from '@modules/user/user.di-tokens';
 | 
				
			||||||
 | 
					import { FindUsersByIdsQueryHandler } from '@modules/user/core/application/queries/find-users-by-ids/find-users-by-ids.query-handler';
 | 
				
			||||||
 | 
					import { FindUsersByIdsQuery } from '@modules/user/core/application/queries/find-users-by-ids/find-users-by-ids.query';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const now = new Date('2023-06-21 06:00:00');
 | 
				
			||||||
 | 
					const user1: UserEntity = new UserEntity({
 | 
				
			||||||
 | 
					  id: 'c160cf8c-f057-4962-841f-3ad68346df44',
 | 
				
			||||||
 | 
					  props: {
 | 
				
			||||||
 | 
					    firstName: 'John',
 | 
				
			||||||
 | 
					    lastName: 'Doe',
 | 
				
			||||||
 | 
					    email: 'john.doe@email.com',
 | 
				
			||||||
 | 
					    phone: '+33611223344',
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  createdAt: now,
 | 
				
			||||||
 | 
					  updatedAt: now,
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					const user2: UserEntity = new UserEntity({
 | 
				
			||||||
 | 
					  id: 'c160cf8c-f057-4962-841f-3ad68346df55',
 | 
				
			||||||
 | 
					  props: {
 | 
				
			||||||
 | 
					    firstName: 'Jane',
 | 
				
			||||||
 | 
					    lastName: 'Doe',
 | 
				
			||||||
 | 
					    email: 'jane.doe@email.com',
 | 
				
			||||||
 | 
					    phone: '+33611223355',
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  createdAt: now,
 | 
				
			||||||
 | 
					  updatedAt: now,
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					const user3: UserEntity = new UserEntity({
 | 
				
			||||||
 | 
					  id: 'c160cf8c-f057-4962-841f-3ad68346df66',
 | 
				
			||||||
 | 
					  props: {
 | 
				
			||||||
 | 
					    firstName: 'Jimmy',
 | 
				
			||||||
 | 
					    lastName: 'Doe',
 | 
				
			||||||
 | 
					    email: 'jimmy.doe@email.com',
 | 
				
			||||||
 | 
					    phone: '+33611223366',
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  createdAt: now,
 | 
				
			||||||
 | 
					  updatedAt: now,
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const mockUserRepository = {
 | 
				
			||||||
 | 
					  findAllByIds: jest.fn().mockImplementation(() => [user1, user2, user3]),
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					describe('Find Users By Ids Query Handler', () => {
 | 
				
			||||||
 | 
					  let findUsersByIdsQueryHandler: FindUsersByIdsQueryHandler;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  beforeAll(async () => {
 | 
				
			||||||
 | 
					    const module: TestingModule = await Test.createTestingModule({
 | 
				
			||||||
 | 
					      providers: [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          provide: USER_REPOSITORY,
 | 
				
			||||||
 | 
					          useValue: mockUserRepository,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        FindUsersByIdsQueryHandler,
 | 
				
			||||||
 | 
					      ],
 | 
				
			||||||
 | 
					    }).compile();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    findUsersByIdsQueryHandler = module.get<FindUsersByIdsQueryHandler>(
 | 
				
			||||||
 | 
					      FindUsersByIdsQueryHandler,
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  it('should be defined', () => {
 | 
				
			||||||
 | 
					    expect(findUsersByIdsQueryHandler).toBeDefined();
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  describe('execution', () => {
 | 
				
			||||||
 | 
					    it('should return users', async () => {
 | 
				
			||||||
 | 
					      const findUsersByIdsQuery = new FindUsersByIdsQuery([
 | 
				
			||||||
 | 
					        'c160cf8c-f057-4962-841f-3ad68346df44',
 | 
				
			||||||
 | 
					        'c160cf8c-f057-4962-841f-3ad68346df55',
 | 
				
			||||||
 | 
					        'c160cf8c-f057-4962-841f-3ad68346df66',
 | 
				
			||||||
 | 
					      ]);
 | 
				
			||||||
 | 
					      const users: UserEntity[] =
 | 
				
			||||||
 | 
					        await findUsersByIdsQueryHandler.execute(findUsersByIdsQuery);
 | 
				
			||||||
 | 
					      expect(users).toHaveLength(3);
 | 
				
			||||||
 | 
					      expect(users[1].getProps().firstName).toBe('Jane');
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,95 @@
 | 
				
			||||||
 | 
					import { RpcExceptionCode } from '@mobicoop/ddd-library';
 | 
				
			||||||
 | 
					import { FindUsersByIdsGrpcController } from '@modules/user/interface/grpc-controllers/find-users-by-ids.grpc.controller';
 | 
				
			||||||
 | 
					import { UserMapper } from '@modules/user/user.mapper';
 | 
				
			||||||
 | 
					import { QueryBus } from '@nestjs/cqrs';
 | 
				
			||||||
 | 
					import { RpcException } from '@nestjs/microservices';
 | 
				
			||||||
 | 
					import { Test, TestingModule } from '@nestjs/testing';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const mockQueryBus = {
 | 
				
			||||||
 | 
					  execute: jest
 | 
				
			||||||
 | 
					    .fn()
 | 
				
			||||||
 | 
					    .mockImplementationOnce(() => [
 | 
				
			||||||
 | 
					      '6dcf093c-c7db-4dae-8e9c-c715cebf83c7',
 | 
				
			||||||
 | 
					      '6dcf093c-c7db-4dae-8e9c-c715cebf83c8',
 | 
				
			||||||
 | 
					      '6dcf093c-c7db-4dae-8e9c-c715cebf83c9',
 | 
				
			||||||
 | 
					    ])
 | 
				
			||||||
 | 
					    .mockImplementationOnce(() => {
 | 
				
			||||||
 | 
					      throw new Error();
 | 
				
			||||||
 | 
					    }),
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const mockUserMapper = {
 | 
				
			||||||
 | 
					  toResponse: jest.fn().mockImplementationOnce(() => ({
 | 
				
			||||||
 | 
					    firstName: 'John',
 | 
				
			||||||
 | 
					    lastName: 'Doe',
 | 
				
			||||||
 | 
					    email: 'john.doe@email.com',
 | 
				
			||||||
 | 
					    phone: '+33611223344',
 | 
				
			||||||
 | 
					  })),
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					describe('Find Users By Ids Grpc Controller', () => {
 | 
				
			||||||
 | 
					  let findUsersbyIdsGrpcController: FindUsersByIdsGrpcController;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  beforeAll(async () => {
 | 
				
			||||||
 | 
					    const module: TestingModule = await Test.createTestingModule({
 | 
				
			||||||
 | 
					      providers: [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          provide: QueryBus,
 | 
				
			||||||
 | 
					          useValue: mockQueryBus,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          provide: UserMapper,
 | 
				
			||||||
 | 
					          useValue: mockUserMapper,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        FindUsersByIdsGrpcController,
 | 
				
			||||||
 | 
					      ],
 | 
				
			||||||
 | 
					    }).compile();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    findUsersbyIdsGrpcController = module.get<FindUsersByIdsGrpcController>(
 | 
				
			||||||
 | 
					      FindUsersByIdsGrpcController,
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  afterEach(async () => {
 | 
				
			||||||
 | 
					    jest.clearAllMocks();
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  it('should be defined', () => {
 | 
				
			||||||
 | 
					    expect(findUsersbyIdsGrpcController).toBeDefined();
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  it('should return users', async () => {
 | 
				
			||||||
 | 
					    jest.spyOn(mockQueryBus, 'execute');
 | 
				
			||||||
 | 
					    jest.spyOn(mockUserMapper, 'toResponse');
 | 
				
			||||||
 | 
					    const response = await findUsersbyIdsGrpcController.findAllbyIds({
 | 
				
			||||||
 | 
					      ids: [
 | 
				
			||||||
 | 
					        '6dcf093c-c7db-4dae-8e9c-c715cebf83c7',
 | 
				
			||||||
 | 
					        '6dcf093c-c7db-4dae-8e9c-c715cebf83c8',
 | 
				
			||||||
 | 
					        '6dcf093c-c7db-4dae-8e9c-c715cebf83c9',
 | 
				
			||||||
 | 
					      ],
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    expect(response.users).toHaveLength(3);
 | 
				
			||||||
 | 
					    expect(mockQueryBus.execute).toHaveBeenCalledTimes(1);
 | 
				
			||||||
 | 
					    expect(mockUserMapper.toResponse).toHaveBeenCalledTimes(3);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  it('should throw a generic RpcException', async () => {
 | 
				
			||||||
 | 
					    jest.spyOn(mockQueryBus, 'execute');
 | 
				
			||||||
 | 
					    jest.spyOn(mockUserMapper, 'toResponse');
 | 
				
			||||||
 | 
					    expect.assertions(4);
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      await findUsersbyIdsGrpcController.findAllbyIds({
 | 
				
			||||||
 | 
					        ids: [
 | 
				
			||||||
 | 
					          '6dcf093c-c7db-4dae-8e9c-c715cebf83c7',
 | 
				
			||||||
 | 
					          '6dcf093c-c7db-4dae-8e9c-c715cebf83c8',
 | 
				
			||||||
 | 
					          '6dcf093c-c7db-4dae-8e9c-c715cebf83c9',
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    } catch (e: any) {
 | 
				
			||||||
 | 
					      expect(e).toBeInstanceOf(RpcException);
 | 
				
			||||||
 | 
					      expect(e.error.code).toBe(RpcExceptionCode.UNKNOWN);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    expect(mockQueryBus.execute).toHaveBeenCalledTimes(1);
 | 
				
			||||||
 | 
					    expect(mockUserMapper.toResponse).toHaveBeenCalledTimes(0);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
| 
						 | 
					@ -20,6 +20,8 @@ import { PublishMessageWhenUserIsCreatedDomainEventHandler } from './core/applic
 | 
				
			||||||
import { PublishMessageWhenUserIsUpdatedDomainEventHandler } from './core/application/event-handlers/publish-message-when-user-is-updated.domain-event-handler';
 | 
					import { PublishMessageWhenUserIsUpdatedDomainEventHandler } from './core/application/event-handlers/publish-message-when-user-is-updated.domain-event-handler';
 | 
				
			||||||
import { PublishMessageWhenUserIsDeletedDomainEventHandler } from './core/application/event-handlers/publish-message-when-user-is-deleted.domain-event-handler';
 | 
					import { PublishMessageWhenUserIsDeletedDomainEventHandler } from './core/application/event-handlers/publish-message-when-user-is-deleted.domain-event-handler';
 | 
				
			||||||
import { RedisClientOptions } from '@songkeys/nestjs-redis';
 | 
					import { RedisClientOptions } from '@songkeys/nestjs-redis';
 | 
				
			||||||
 | 
					import { FindUsersByIdsGrpcController } from './interface/grpc-controllers/find-users-by-ids.grpc.controller';
 | 
				
			||||||
 | 
					import { FindUsersByIdsQueryHandler } from './core/application/queries/find-users-by-ids/find-users-by-ids.query-handler';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const imports = [
 | 
					const imports = [
 | 
				
			||||||
  CqrsModule,
 | 
					  CqrsModule,
 | 
				
			||||||
| 
						 | 
					@ -42,6 +44,7 @@ const grpcControllers = [
 | 
				
			||||||
  UpdateUserGrpcController,
 | 
					  UpdateUserGrpcController,
 | 
				
			||||||
  DeleteUserGrpcController,
 | 
					  DeleteUserGrpcController,
 | 
				
			||||||
  FindUserByIdGrpcController,
 | 
					  FindUserByIdGrpcController,
 | 
				
			||||||
 | 
					  FindUsersByIdsGrpcController,
 | 
				
			||||||
];
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const eventHandlers: Provider[] = [
 | 
					const eventHandlers: Provider[] = [
 | 
				
			||||||
| 
						 | 
					@ -56,7 +59,10 @@ const commandHandlers: Provider[] = [
 | 
				
			||||||
  DeleteUserService,
 | 
					  DeleteUserService,
 | 
				
			||||||
];
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const queryHandlers: Provider[] = [FindUserByIdQueryHandler];
 | 
					const queryHandlers: Provider[] = [
 | 
				
			||||||
 | 
					  FindUserByIdQueryHandler,
 | 
				
			||||||
 | 
					  FindUsersByIdsQueryHandler,
 | 
				
			||||||
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const mappers: Provider[] = [UserMapper];
 | 
					const mappers: Provider[] = [UserMapper];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue