From e59cd6e3ca26ed63a00c306708b9a47fed69b315 Mon Sep 17 00:00:00 2001 From: zhumeisongsong Date: Mon, 25 Nov 2024 18:08:51 +0900 Subject: [PATCH 01/15] =?UTF-8?q?feat:=20=E2=9C=A8=20update=20user=20entit?= =?UTF-8?q?y=20in=20domain?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- libs/users/domain/src/lib/user.entity.spec.ts | 25 +++++++++++++------ libs/users/domain/src/lib/user.entity.ts | 4 ++- .../domain/src/lib/users.repository.spec.ts | 16 +++++++++--- 3 files changed, 33 insertions(+), 12 deletions(-) diff --git a/libs/users/domain/src/lib/user.entity.spec.ts b/libs/users/domain/src/lib/user.entity.spec.ts index 4274304..8525925 100644 --- a/libs/users/domain/src/lib/user.entity.spec.ts +++ b/libs/users/domain/src/lib/user.entity.spec.ts @@ -1,15 +1,24 @@ import { User } from './user.entity'; describe('User Entity', () => { - it('should create a user with id and name', () => { - const user = new User('1', 'John Doe'); - expect(user.id).toBe('1'); - expect(user.name).toBe('John Doe'); + it('should create a user with all required fields', () => { + const user = new User('123', 'test@example.com', 'John', 'Doe'); + + expect(user.id).toBe('123'); + expect(user.email).toBe('test@example.com'); + expect(user.firstName).toBe('John'); + expect(user.lastName).toBe('Doe'); + }); + + it('should allow updating first name', () => { + const user = new User('123', 'test@example.com', 'John', 'Doe'); + user.firstName = 'Jane'; + expect(user.firstName).toBe('Jane'); }); - it('should allow updating the name', () => { - const user = new User('1', 'John Doe'); - user.name = 'Jane Doe'; - expect(user.name).toBe('Jane Doe'); + it('should allow updating last name', () => { + const user = new User('123', 'test@example.com', 'John', 'Doe'); + user.lastName = 'Smith'; + expect(user.lastName).toBe('Smith'); }); }); diff --git a/libs/users/domain/src/lib/user.entity.ts b/libs/users/domain/src/lib/user.entity.ts index 202554e..b86c28d 100644 --- a/libs/users/domain/src/lib/user.entity.ts +++ b/libs/users/domain/src/lib/user.entity.ts @@ -1,6 +1,8 @@ export class User { constructor( public readonly id: string, - public name: string, + public readonly email: string, + public firstName: string, + public lastName: string, ) {} } diff --git a/libs/users/domain/src/lib/users.repository.spec.ts b/libs/users/domain/src/lib/users.repository.spec.ts index 93f4ec3..ba5e3b8 100644 --- a/libs/users/domain/src/lib/users.repository.spec.ts +++ b/libs/users/domain/src/lib/users.repository.spec.ts @@ -3,8 +3,13 @@ import { User } from './user.entity'; class MockUsersRepository implements UsersRepository { private users: User[] = [ - { id: '1', name: 'John Doe' }, - { id: '2', name: 'Jane Doe' }, + { id: '1', email: 'john@example.com', firstName: 'John', lastName: 'Doe' }, + { + id: '2', + email: 'jane@example.com', + firstName: 'Jane', + lastName: 'Smith', + }, ]; async findById(id: string): Promise { @@ -21,7 +26,12 @@ describe('UsersRepository', () => { test('findById should return a user by id', async () => { const user = await usersRepository.findById('1'); - expect(user).toEqual({ id: '1', name: 'John Doe' }); + expect(user).toEqual({ + id: '1', + email: 'john@example.com', + firstName: 'John', + lastName: 'Doe' + }); }); test('findById should return null if user not found', async () => { From 5699cd55a805c08bf6971d7f214fcdbaa4e23b5a Mon Sep 17 00:00:00 2001 From: zhumeisongsong Date: Mon, 25 Nov 2024 18:18:58 +0900 Subject: [PATCH 02/15] =?UTF-8?q?feat:=20=E2=9C=A8=20update=20UserDocument?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/lib/mongoose-users.repository.spec.ts | 14 ++++++++++---- .../mongoose/src/lib/mongoose-users.repository.ts | 8 ++++++-- .../infrastructure/mongoose/src/lib/user.schema.ts | 10 ++++++++-- 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/libs/users/infrastructure/mongoose/src/lib/mongoose-users.repository.spec.ts b/libs/users/infrastructure/mongoose/src/lib/mongoose-users.repository.spec.ts index ae36827..0a4bc93 100644 --- a/libs/users/infrastructure/mongoose/src/lib/mongoose-users.repository.spec.ts +++ b/libs/users/infrastructure/mongoose/src/lib/mongoose-users.repository.spec.ts @@ -23,13 +23,17 @@ describe('MongooseUsersRepository', () => { }).compile(); repository = module.get(MongooseUsersRepository); - userModel = module.get>(getModelToken(UserDocument.name)); + userModel = module.get>( + getModelToken(UserDocument.name), + ); }); it('should return a user when found', async () => { const mockUser = { id: '507f1f77bcf86cd799439011', - name: 'Test User', + email: 'john@example.com', + firstName: 'John', + lastName: 'Doe', }; jest.spyOn(userModel, 'findById').mockReturnValue({ @@ -39,7 +43,9 @@ describe('MongooseUsersRepository', () => { const user = await repository.findById(mockUser.id); expect(user).toBeInstanceOf(User); expect(user?.id).toBe(mockUser.id); - expect(user?.name).toBe(mockUser.name); + expect(user?.email).toBe(mockUser.email); + expect(user?.firstName).toBe(mockUser.firstName); + expect(user?.lastName).toBe(mockUser.lastName); }); it('should return null when user is not found', async () => { @@ -50,4 +56,4 @@ describe('MongooseUsersRepository', () => { const user = await repository.findById('507f1f77bcf86cd799439011'); expect(user).toBeNull(); }); -}); \ No newline at end of file +}); diff --git a/libs/users/infrastructure/mongoose/src/lib/mongoose-users.repository.ts b/libs/users/infrastructure/mongoose/src/lib/mongoose-users.repository.ts index 9da4285..9a560ed 100644 --- a/libs/users/infrastructure/mongoose/src/lib/mongoose-users.repository.ts +++ b/libs/users/infrastructure/mongoose/src/lib/mongoose-users.repository.ts @@ -12,13 +12,17 @@ export class MongooseUsersRepository implements UsersRepository { ) {} async findById(id: string): Promise { - const _id = new Types.ObjectId(id); const userDocument = await this.userModel.findById(_id).exec(); if (!userDocument) { return null; } - return new User(userDocument.id, userDocument.name); + return new User( + userDocument.id, + userDocument.email, + userDocument.firstName, + userDocument.lastName, + ); } } diff --git a/libs/users/infrastructure/mongoose/src/lib/user.schema.ts b/libs/users/infrastructure/mongoose/src/lib/user.schema.ts index ff6527f..b3e998d 100644 --- a/libs/users/infrastructure/mongoose/src/lib/user.schema.ts +++ b/libs/users/infrastructure/mongoose/src/lib/user.schema.ts @@ -4,7 +4,13 @@ import { Document } from 'mongoose'; @Schema() export class UserDocument extends Document { @Prop({ required: true }) - name!: string; + email!: string; + + @Prop({ required: true, nullable: true }) + firstName!: string; + + @Prop({ required: true, nullable: true }) + lastName!: string; } -export const UserSchema = SchemaFactory.createForClass(UserDocument); \ No newline at end of file +export const UserSchema = SchemaFactory.createForClass(UserDocument); From ca27362cc225186b62126ace8f3540a0727429cd Mon Sep 17 00:00:00 2001 From: zhumeisongsong Date: Mon, 25 Nov 2024 18:27:10 +0900 Subject: [PATCH 03/15] =?UTF-8?q?feat:=20=E2=9C=A8=20update=20user=20attri?= =?UTF-8?q?butes=20to=20nullable?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- libs/users/domain/src/lib/user.entity.spec.ts | 22 +++++++++++++++++++ libs/users/domain/src/lib/user.entity.ts | 4 ++-- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/libs/users/domain/src/lib/user.entity.spec.ts b/libs/users/domain/src/lib/user.entity.spec.ts index 8525925..e3ff279 100644 --- a/libs/users/domain/src/lib/user.entity.spec.ts +++ b/libs/users/domain/src/lib/user.entity.spec.ts @@ -21,4 +21,26 @@ describe('User Entity', () => { user.lastName = 'Smith'; expect(user.lastName).toBe('Smith'); }); + + it('should allow updating first name and last name', () => { + const user = new User('123', 'test@example.com', 'John', 'Doe'); + user.firstName = 'Jane'; + user.lastName = 'Smith'; + expect(user.firstName).toBe('Jane'); + expect(user.lastName).toBe('Smith'); + }); + + it('should allow null values for first name and last name', () => { + const user = new User('123', 'test@example.com', null, null); + expect(user.firstName).toBeNull(); + expect(user.lastName).toBeNull(); + }); + + it('should allow setting first name and last name to null', () => { + const user = new User('123', 'test@example.com', 'John', 'Doe'); + user.firstName = null; + user.lastName = null; + expect(user.firstName).toBeNull(); + expect(user.lastName).toBeNull(); + }); }); diff --git a/libs/users/domain/src/lib/user.entity.ts b/libs/users/domain/src/lib/user.entity.ts index b86c28d..2f82d96 100644 --- a/libs/users/domain/src/lib/user.entity.ts +++ b/libs/users/domain/src/lib/user.entity.ts @@ -2,7 +2,7 @@ export class User { constructor( public readonly id: string, public readonly email: string, - public firstName: string, - public lastName: string, + public firstName: string | null, + public lastName: string | null, ) {} } From b66a109e7b1260a7c5a4249b6c0d7e178046d4c9 Mon Sep 17 00:00:00 2001 From: zhumeisongsong Date: Mon, 25 Nov 2024 18:27:44 +0900 Subject: [PATCH 04/15] =?UTF-8?q?feat:=20=E2=9C=A8=20update=20UserDto=20in?= =?UTF-8?q?=20resolver=20layer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/lib/dto/user.dto.spec.ts | 8 ++++---- .../src/lib/dto/user.dto.ts | 19 ++++++++++++++++--- .../src/lib/resolver/users.resolver.spec.ts | 1 - .../src/lib/resolver/users.resolver.ts | 5 +++-- 4 files changed, 23 insertions(+), 10 deletions(-) diff --git a/libs/users/interface-adapters/src/lib/dto/user.dto.spec.ts b/libs/users/interface-adapters/src/lib/dto/user.dto.spec.ts index aa7e379..a2acd6a 100644 --- a/libs/users/interface-adapters/src/lib/dto/user.dto.spec.ts +++ b/libs/users/interface-adapters/src/lib/dto/user.dto.spec.ts @@ -3,11 +3,11 @@ import { UserDto } from './user.dto'; describe('UserDto', () => { it('should create a new UserDto instance', () => { const id = '123'; - const name = 'Test User'; - const userDto = new UserDto(id, name); + const email = 'john@example.com'; + const firstName = 'John'; + const lastName = 'Doe'; + const userDto = new UserDto(id, email, firstName, lastName); expect(userDto).toBeDefined(); - expect(userDto.id).toBe(id); - expect(userDto.name).toBe(name); }); }); diff --git a/libs/users/interface-adapters/src/lib/dto/user.dto.ts b/libs/users/interface-adapters/src/lib/dto/user.dto.ts index 764738c..50b2677 100644 --- a/libs/users/interface-adapters/src/lib/dto/user.dto.ts +++ b/libs/users/interface-adapters/src/lib/dto/user.dto.ts @@ -6,10 +6,23 @@ export class UserDto { id: string; @Field() - name: string; + email: string; - constructor(id: string, name: string) { + @Field() + firstName: string | null; + + @Field() + lastName: string | null; + + constructor( + id: string, + email: string, + firstName: string | null, + lastName: string | null, + ) { this.id = id; - this.name = name; + this.email = email; + this.firstName = firstName; + this.lastName = lastName; } } diff --git a/libs/users/interface-adapters/src/lib/resolver/users.resolver.spec.ts b/libs/users/interface-adapters/src/lib/resolver/users.resolver.spec.ts index 1bbf9ec..1b71a8c 100644 --- a/libs/users/interface-adapters/src/lib/resolver/users.resolver.spec.ts +++ b/libs/users/interface-adapters/src/lib/resolver/users.resolver.spec.ts @@ -1,6 +1,5 @@ import { Test, TestingModule } from '@nestjs/testing'; import { UsersService } from '@users/application'; -import { User } from '@users/domain'; import { UsersResolver } from './users.resolver'; diff --git a/libs/users/interface-adapters/src/lib/resolver/users.resolver.ts b/libs/users/interface-adapters/src/lib/resolver/users.resolver.ts index 5619dc7..5e665db 100644 --- a/libs/users/interface-adapters/src/lib/resolver/users.resolver.ts +++ b/libs/users/interface-adapters/src/lib/resolver/users.resolver.ts @@ -1,5 +1,6 @@ import { Args, ID, Query, Resolver } from '@nestjs/graphql'; import { UsersService } from '@users/application'; +import { User } from '@users/domain'; import { UserDto } from '../dto/user.dto'; @@ -9,12 +10,12 @@ export class UsersResolver { @Query(() => UserDto, { nullable: true }) async getUser(@Args({ name: 'id', type: () => ID }) id: string): Promise { - const user = await this.usersService.findById(id); + const user: User| null = await this.usersService.findById(id); if (!user) { return null; } - return new UserDto(user.id, user.name); + return new UserDto(user.id, user.email, user.firstName, user.lastName); } } From 330dca5e433457517f0d31d2899020854067ba82 Mon Sep 17 00:00:00 2001 From: zhumeisongsong Date: Mon, 25 Nov 2024 18:31:19 +0900 Subject: [PATCH 05/15] =?UTF-8?q?test:=20=F0=9F=A7=AA=20update=20mock=20da?= =?UTF-8?q?ta=20in=20test=20cases?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/lib/use-cases/get-user.use-case.spec.ts | 9 +++++++-- libs/users/application/src/lib/users.service.spec.ts | 7 ++++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/libs/users/application/src/lib/use-cases/get-user.use-case.spec.ts b/libs/users/application/src/lib/use-cases/get-user.use-case.spec.ts index 7debc7a..5426856 100644 --- a/libs/users/application/src/lib/use-cases/get-user.use-case.spec.ts +++ b/libs/users/application/src/lib/use-cases/get-user.use-case.spec.ts @@ -14,7 +14,12 @@ describe('GetUserUseCase', () => { describe('execute', () => { it('should return user when found', async () => { - const mockUser = { id: '1', name: 'John Doe' }; + const mockUser = { + id: '1', + email: 'test@example.com', + firstName: 'John', + lastName: 'Doe', + }; (usersRepository.findById as jest.Mock).mockResolvedValue(mockUser); const result = await getUserUseCase.execute('1'); @@ -32,4 +37,4 @@ describe('GetUserUseCase', () => { expect(usersRepository.findById).toHaveBeenCalledWith('1'); }); }); -}); \ No newline at end of file +}); diff --git a/libs/users/application/src/lib/users.service.spec.ts b/libs/users/application/src/lib/users.service.spec.ts index c3879e0..8e7a651 100644 --- a/libs/users/application/src/lib/users.service.spec.ts +++ b/libs/users/application/src/lib/users.service.spec.ts @@ -30,7 +30,12 @@ describe('UsersService', () => { describe('findById', () => { it('should return a user if found', async () => { - const user: User = { id: '1', name: 'John Doe' }; + const user: User = { + id: '1', + email: 'test@example.com', + firstName: 'John', + lastName: 'Doe', + }; jest.spyOn(getUserUseCase, 'execute').mockResolvedValue(user); const result = await service.findById('1'); From 46259d38786ab42620c853b724c23e045ea9c13f Mon Sep 17 00:00:00 2001 From: zhumeisongsong Date: Mon, 25 Nov 2024 18:49:46 +0900 Subject: [PATCH 06/15] =?UTF-8?q?test:=20=F0=9F=A7=AA=20add=20more=20expec?= =?UTF-8?q?t=20to=20UserDto=20test?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- libs/users/interface-adapters/src/lib/dto/user.dto.spec.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libs/users/interface-adapters/src/lib/dto/user.dto.spec.ts b/libs/users/interface-adapters/src/lib/dto/user.dto.spec.ts index a2acd6a..c7f486f 100644 --- a/libs/users/interface-adapters/src/lib/dto/user.dto.spec.ts +++ b/libs/users/interface-adapters/src/lib/dto/user.dto.spec.ts @@ -9,5 +9,8 @@ describe('UserDto', () => { const userDto = new UserDto(id, email, firstName, lastName); expect(userDto).toBeDefined(); + expect(userDto.id).toBe(id); + expect(userDto.email).toBe(email); + expect(userDto.firstName).toBe(firstName); }); }); From 382e302ee6ab7ae12b787d7e8654b62adec36e25 Mon Sep 17 00:00:00 2001 From: zhumeisongsong Date: Mon, 25 Nov 2024 18:50:28 +0900 Subject: [PATCH 07/15] =?UTF-8?q?feat:=20=E2=9C=A8=20Add=20email=20format?= =?UTF-8?q?=20validation=20and=20uniqueness=20constraint?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 8 ++++---- libs/users/infrastructure/mongoose/src/lib/user.schema.ts | 3 ++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index a26ae1b..e8529e8 100644 --- a/README.md +++ b/README.md @@ -59,13 +59,13 @@ You can use `npx nx list` to get a list of installed plugins. Then, run `npx nx | interface-adapters | It should not contain complex business logic.
Responsible for receiving and processing requests from clients. These requests are interactions with external systems that need to be converted into operations that conform to the business logic through adapters.
Relies on the Application layer for core business logic, avoiding direct interaction with databases or other external systems. | | resolver(interface-adapters) | Handles GraphQL queries and mutations by converting them into calls to the application layer.
Responsible for input validation and response formatting specific to GraphQL. | | dto(interface-adapters) | Define DTOs for GraphQL schema. | -| infrastructure | Implements the technical capabilities needed to support the higher layers of the application.
Handles database connections, external service integrations, and other technical concerns.
Contains concrete implementations of repository interfaces defined in the domain layer. | +| infrastructure | Implements the technical capabilities needed to support the higher layers of the application.
Handles database connections, external service integrations, and other technical concerns.
Contains concrete implementations of repository interfaces defined in the domain layer. | -| mongoose(infrastructure) | Implements the repository interfaces defined in the domain layer using Mongoose as the ODM (Object Document Mapper).
Includes Mongoose Schema definitions, database connection management, and concrete implementations of repository interfaces (e.g., MongooseUsersRepository). | +| mongoose(infrastructure) | Implements the repository interfaces defined in the domain layer using Mongoose as the ODM (Object Document Mapper).
Includes Mongoose Schema definitions, database connection management, and concrete implementations of repository interfaces (e.g., MongooseUsersRepository).
Adding validation in the Mongoose schema ensures that any data persisted to the database adheres to the required constraints. This helps maintain data integrity and prevents invalid or duplicate entries at the database level. | | service(application) | As the core of the application layer, it mainly interacts with the domain layer and interface-adapter layer.
If you migrate to a non-NestJS architecture in the future (e.g. other frameworks or microservices), the application tier code can be left unaffected. | -| use-case(application) | Define business use cases and encapsulate business logic. | +| use-case(application) | Define business use cases and encapsulate business logic.
Implementing validation in the use-case layer allows you to enforce business logic and provide immediate feedback to users or calling services. This is where you can handle complex validation rules and provide detailed error messages.| | entity(domain) | Define core business entities and business rules.
Maintain entity independence from database and framework. | -| repository(domain) | Interfaces (or abstract classes), which define methods for manipulating data without concern for specific database implementations.
By defining this interface, we can decouple database access: the specific details of data access will be done by implementation classes, such as specific implementations using tools like Mongoose, TypeORM, Prisma, and so on. | +| repository(domain) | Interfaces (or abstract classes), which define methods for manipulating data without concern for specific database implementations.
By defining this interface, we can decouple database access: the specific details of data access will be done by implementation classes, such as specific implementations using tools like Mongoose, TypeORM, Prisma, and so on. | ## Useful links diff --git a/libs/users/infrastructure/mongoose/src/lib/user.schema.ts b/libs/users/infrastructure/mongoose/src/lib/user.schema.ts index b3e998d..21a2b06 100644 --- a/libs/users/infrastructure/mongoose/src/lib/user.schema.ts +++ b/libs/users/infrastructure/mongoose/src/lib/user.schema.ts @@ -3,7 +3,8 @@ import { Document } from 'mongoose'; @Schema() export class UserDocument extends Document { - @Prop({ required: true }) + // refactor: move match regex to a shared lib + @Prop({ required: true, unique: true, match: /^[^\s@]+@[^\s@]+\.[^\s@]+$/ }) email!: string; @Prop({ required: true, nullable: true }) From 656402ce9025a249f17b160c89f3379ca75b3f96 Mon Sep 17 00:00:00 2001 From: zhumeisongsong Date: Mon, 25 Nov 2024 18:58:44 +0900 Subject: [PATCH 08/15] =?UTF-8?q?fix:=20=F0=9F=90=9B=20same=20contradictor?= =?UTF-8?q?y=20constraints=20as=20firstName=20field?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- libs/users/infrastructure/mongoose/src/lib/user.schema.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/libs/users/infrastructure/mongoose/src/lib/user.schema.ts b/libs/users/infrastructure/mongoose/src/lib/user.schema.ts index 21a2b06..1cc6d43 100644 --- a/libs/users/infrastructure/mongoose/src/lib/user.schema.ts +++ b/libs/users/infrastructure/mongoose/src/lib/user.schema.ts @@ -7,10 +7,9 @@ export class UserDocument extends Document { @Prop({ required: true, unique: true, match: /^[^\s@]+@[^\s@]+\.[^\s@]+$/ }) email!: string; - @Prop({ required: true, nullable: true }) + @Prop({ required: false}) firstName!: string; - - @Prop({ required: true, nullable: true }) + @Prop({ required: false }) lastName!: string; } From 342e407f80bb30303d054021448d44fcf877b9b7 Mon Sep 17 00:00:00 2001 From: zhumeisongsong Date: Wed, 27 Nov 2024 15:13:32 +0900 Subject: [PATCH 09/15] =?UTF-8?q?chore:=20=F0=9F=A4=96=20add=20class-valid?= =?UTF-8?q?ator?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 1 + pnpm-lock.yaml | 101 ++++++++++++++++++++++++++++++++----------------- 2 files changed, 68 insertions(+), 34 deletions(-) diff --git a/package.json b/package.json index ef8b6ee..0ec6acf 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "@nestjs/mongoose": "^10.1.0", "@nestjs/platform-express": "^10.4.7", "axios": "^1.7.7", + "class-validator": "^0.14.1", "graphql": "^16.9.0", "graphql-tools": "^9.0.2", "inlineTrace": "link:@apollo/server/plugin/inlineTrace", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 16d0d57..db97da1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -19,28 +19,31 @@ importers: version: 2.9.3(graphql@16.9.0) '@nestjs/apollo': specifier: ^12.2.1 - version: 12.2.1(@apollo/gateway@2.9.3(encoding@0.1.13)(graphql@16.9.0))(@apollo/server@4.11.2(encoding@0.1.13)(graphql@16.9.0))(@apollo/subgraph@2.9.3(graphql@16.9.0))(@nestjs/common@10.4.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.8)(@nestjs/graphql@12.2.1(@apollo/subgraph@2.9.3(graphql@16.9.0))(@nestjs/common@10.4.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.8)(graphql@16.9.0)(reflect-metadata@0.2.2)(ts-morph@24.0.0))(graphql@16.9.0) + version: 12.2.1(@apollo/gateway@2.9.3(encoding@0.1.13)(graphql@16.9.0))(@apollo/server@4.11.2(encoding@0.1.13)(graphql@16.9.0))(@apollo/subgraph@2.9.3(graphql@16.9.0))(@nestjs/common@10.4.8(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.8)(@nestjs/graphql@12.2.1(@apollo/subgraph@2.9.3(graphql@16.9.0))(@nestjs/common@10.4.8(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.8)(class-validator@0.14.1)(graphql@16.9.0)(reflect-metadata@0.2.2)(ts-morph@24.0.0))(graphql@16.9.0) '@nestjs/common': specifier: ^10.4.7 - version: 10.4.8(reflect-metadata@0.2.2)(rxjs@7.8.1) + version: 10.4.8(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1) '@nestjs/config': specifier: ^3.3.0 - version: 3.3.0(@nestjs/common@10.4.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(rxjs@7.8.1) + version: 3.3.0(@nestjs/common@10.4.8(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(rxjs@7.8.1) '@nestjs/core': specifier: ^10.4.7 - version: 10.4.8(@nestjs/common@10.4.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.8)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1) + version: 10.4.8(@nestjs/common@10.4.8(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.8)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1) '@nestjs/graphql': specifier: ^12.2.1 - version: 12.2.1(@apollo/subgraph@2.9.3(graphql@16.9.0))(@nestjs/common@10.4.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.8)(graphql@16.9.0)(reflect-metadata@0.2.2)(ts-morph@24.0.0) + version: 12.2.1(@apollo/subgraph@2.9.3(graphql@16.9.0))(@nestjs/common@10.4.8(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.8)(class-validator@0.14.1)(graphql@16.9.0)(reflect-metadata@0.2.2)(ts-morph@24.0.0) '@nestjs/mongoose': specifier: ^10.1.0 - version: 10.1.0(@nestjs/common@10.4.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.8)(mongoose@8.8.2(socks@2.8.3))(rxjs@7.8.1) + version: 10.1.0(@nestjs/common@10.4.8(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.8)(mongoose@8.8.2(socks@2.8.3))(rxjs@7.8.1) '@nestjs/platform-express': specifier: ^10.4.7 - version: 10.4.8(@nestjs/common@10.4.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.8) + version: 10.4.8(@nestjs/common@10.4.8(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.8) axios: specifier: ^1.7.7 version: 1.7.7 + class-validator: + specifier: ^0.14.1 + version: 0.14.1 graphql: specifier: ^16.9.0 version: 16.9.0 @@ -74,7 +77,7 @@ importers: version: 10.2.3(chokidar@4.0.1)(typescript@5.7.2) '@nestjs/testing': specifier: ^10.4.7 - version: 10.4.8(@nestjs/common@10.4.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.8)(@nestjs/platform-express@10.4.8) + version: 10.4.8(@nestjs/common@10.4.8(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.8)(@nestjs/platform-express@10.4.8) '@nx/eslint': specifier: 20.1.3 version: 20.1.3(@babel/traverse@7.25.9)(@swc-node/register@1.10.9(@swc/core@1.9.3(@swc/helpers@0.5.15))(@swc/types@0.1.17)(typescript@5.7.2))(@swc/core@1.9.3(@swc/helpers@0.5.15))(@types/node@22.9.3)(@zkochan/js-yaml@0.0.7)(eslint@9.15.0)(nx@20.1.3(@swc-node/register@1.10.9(@swc/core@1.9.3(@swc/helpers@0.5.15))(@swc/types@0.1.17)(typescript@5.7.2))(@swc/core@1.9.3(@swc/helpers@0.5.15))) @@ -2155,6 +2158,9 @@ packages: '@types/uuid@9.0.8': resolution: {integrity: sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==} + '@types/validator@13.12.2': + resolution: {integrity: sha512-6SlHBzUW8Jhf3liqrGGXyTJSIFe4nqlJ5A5KaMZ2l/vbM3Wh3KSybots/wfWVzNLK4D1NZluDlSQIbIEPx6oyA==} + '@types/webidl-conversions@7.0.3': resolution: {integrity: sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==} @@ -2782,6 +2788,9 @@ packages: cjs-module-lexer@1.4.1: resolution: {integrity: sha512-cuSVIHi9/9E/+821Qjdvngor+xpnlwnuwIyZOaLmHBVdXL+gP+I6QQB9VkO7RI77YIcTV+S1W9AreJ5eN63JBA==} + class-validator@0.14.1: + resolution: {integrity: sha512-2VEG9JICxIqTpoK1eMzZqaV+u/EiwEJkMGzTrZf6sU/fwsnOITVgYJ8yojSy6CaXtO9V0Cc6ZQZ8h8m4UBuLwQ==} + clean-stack@2.2.0: resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} engines: {node: '>=6'} @@ -4699,6 +4708,9 @@ packages: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} + libphonenumber-js@1.11.15: + resolution: {integrity: sha512-M7+rtYi9l5RvMmHyjyoF3BHHUpXTYdJ0PezZGHNs0GyW1lO+K7jxlXpbdIb7a56h0nqLYdjIw+E+z0ciGaJP7g==} + license-webpack-plugin@4.0.2: resolution: {integrity: sha512-771TFWFD70G1wLTC4oU2Cw4qvtmNrIw+wRvBtn+okgHl7slJVi7zfNcdmqDL72BojM30VNJ2UHylr1o77U37Jw==} peerDependencies: @@ -6890,6 +6902,10 @@ packages: resolution: {integrity: sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + validator@13.12.0: + resolution: {integrity: sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==} + engines: {node: '>= 0.10'} + value-or-promise@1.0.12: resolution: {integrity: sha512-Z6Uz+TYwEqE7ZN50gwn+1LCVo9ZVrpxRPOhOLnncYkY1ZzOYtrX8Fwf/rFktZ8R5mJms6EZf5TqNOMeZmnPq9Q==} engines: {node: '>=12'} @@ -9350,13 +9366,13 @@ snapshots: '@emnapi/runtime': 1.3.1 '@tybys/wasm-util': 0.9.0 - '@nestjs/apollo@12.2.1(@apollo/gateway@2.9.3(encoding@0.1.13)(graphql@16.9.0))(@apollo/server@4.11.2(encoding@0.1.13)(graphql@16.9.0))(@apollo/subgraph@2.9.3(graphql@16.9.0))(@nestjs/common@10.4.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.8)(@nestjs/graphql@12.2.1(@apollo/subgraph@2.9.3(graphql@16.9.0))(@nestjs/common@10.4.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.8)(graphql@16.9.0)(reflect-metadata@0.2.2)(ts-morph@24.0.0))(graphql@16.9.0)': + '@nestjs/apollo@12.2.1(@apollo/gateway@2.9.3(encoding@0.1.13)(graphql@16.9.0))(@apollo/server@4.11.2(encoding@0.1.13)(graphql@16.9.0))(@apollo/subgraph@2.9.3(graphql@16.9.0))(@nestjs/common@10.4.8(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.8)(@nestjs/graphql@12.2.1(@apollo/subgraph@2.9.3(graphql@16.9.0))(@nestjs/common@10.4.8(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.8)(class-validator@0.14.1)(graphql@16.9.0)(reflect-metadata@0.2.2)(ts-morph@24.0.0))(graphql@16.9.0)': dependencies: '@apollo/server': 4.11.2(encoding@0.1.13)(graphql@16.9.0) '@apollo/server-plugin-landing-page-graphql-playground': 4.0.0(@apollo/server@4.11.2(encoding@0.1.13)(graphql@16.9.0)) - '@nestjs/common': 10.4.8(reflect-metadata@0.2.2)(rxjs@7.8.1) - '@nestjs/core': 10.4.8(@nestjs/common@10.4.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.8)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1) - '@nestjs/graphql': 12.2.1(@apollo/subgraph@2.9.3(graphql@16.9.0))(@nestjs/common@10.4.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.8)(graphql@16.9.0)(reflect-metadata@0.2.2)(ts-morph@24.0.0) + '@nestjs/common': 10.4.8(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1) + '@nestjs/core': 10.4.8(@nestjs/common@10.4.8(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.8)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1) + '@nestjs/graphql': 12.2.1(@apollo/subgraph@2.9.3(graphql@16.9.0))(@nestjs/common@10.4.8(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.8)(class-validator@0.14.1)(graphql@16.9.0)(reflect-metadata@0.2.2)(ts-morph@24.0.0) graphql: 16.9.0 iterall: 1.3.0 lodash.omit: 4.5.0 @@ -9365,25 +9381,27 @@ snapshots: '@apollo/gateway': 2.9.3(encoding@0.1.13)(graphql@16.9.0) '@apollo/subgraph': 2.9.3(graphql@16.9.0) - '@nestjs/common@10.4.8(reflect-metadata@0.2.2)(rxjs@7.8.1)': + '@nestjs/common@10.4.8(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1)': dependencies: iterare: 1.2.1 reflect-metadata: 0.2.2 rxjs: 7.8.1 tslib: 2.7.0 uid: 2.0.2 + optionalDependencies: + class-validator: 0.14.1 - '@nestjs/config@3.3.0(@nestjs/common@10.4.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(rxjs@7.8.1)': + '@nestjs/config@3.3.0(@nestjs/common@10.4.8(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(rxjs@7.8.1)': dependencies: - '@nestjs/common': 10.4.8(reflect-metadata@0.2.2)(rxjs@7.8.1) + '@nestjs/common': 10.4.8(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1) dotenv: 16.4.5 dotenv-expand: 10.0.0 lodash: 4.17.21 rxjs: 7.8.1 - '@nestjs/core@10.4.8(@nestjs/common@10.4.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.8)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1)': + '@nestjs/core@10.4.8(@nestjs/common@10.4.8(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.8)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1)': dependencies: - '@nestjs/common': 10.4.8(reflect-metadata@0.2.2)(rxjs@7.8.1) + '@nestjs/common': 10.4.8(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1) '@nuxtjs/opencollective': 0.3.2(encoding@0.1.13) fast-safe-stringify: 2.1.1 iterare: 1.2.1 @@ -9393,18 +9411,18 @@ snapshots: tslib: 2.7.0 uid: 2.0.2 optionalDependencies: - '@nestjs/platform-express': 10.4.8(@nestjs/common@10.4.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.8) + '@nestjs/platform-express': 10.4.8(@nestjs/common@10.4.8(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.8) transitivePeerDependencies: - encoding - '@nestjs/graphql@12.2.1(@apollo/subgraph@2.9.3(graphql@16.9.0))(@nestjs/common@10.4.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.8)(graphql@16.9.0)(reflect-metadata@0.2.2)(ts-morph@24.0.0)': + '@nestjs/graphql@12.2.1(@apollo/subgraph@2.9.3(graphql@16.9.0))(@nestjs/common@10.4.8(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.8)(class-validator@0.14.1)(graphql@16.9.0)(reflect-metadata@0.2.2)(ts-morph@24.0.0)': dependencies: '@graphql-tools/merge': 9.0.8(graphql@16.9.0) '@graphql-tools/schema': 10.0.7(graphql@16.9.0) '@graphql-tools/utils': 10.5.5(graphql@16.9.0) - '@nestjs/common': 10.4.8(reflect-metadata@0.2.2)(rxjs@7.8.1) - '@nestjs/core': 10.4.8(@nestjs/common@10.4.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.8)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1) - '@nestjs/mapped-types': 2.0.5(@nestjs/common@10.4.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(reflect-metadata@0.2.2) + '@nestjs/common': 10.4.8(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1) + '@nestjs/core': 10.4.8(@nestjs/common@10.4.8(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.8)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1) + '@nestjs/mapped-types': 2.0.5(@nestjs/common@10.4.8(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(class-validator@0.14.1)(reflect-metadata@0.2.2) chokidar: 4.0.1 fast-glob: 3.3.2 graphql: 16.9.0 @@ -9419,27 +9437,30 @@ snapshots: ws: 8.18.0 optionalDependencies: '@apollo/subgraph': 2.9.3(graphql@16.9.0) + class-validator: 0.14.1 ts-morph: 24.0.0 transitivePeerDependencies: - bufferutil - utf-8-validate - '@nestjs/mapped-types@2.0.5(@nestjs/common@10.4.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(reflect-metadata@0.2.2)': + '@nestjs/mapped-types@2.0.5(@nestjs/common@10.4.8(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(class-validator@0.14.1)(reflect-metadata@0.2.2)': dependencies: - '@nestjs/common': 10.4.8(reflect-metadata@0.2.2)(rxjs@7.8.1) + '@nestjs/common': 10.4.8(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1) reflect-metadata: 0.2.2 + optionalDependencies: + class-validator: 0.14.1 - '@nestjs/mongoose@10.1.0(@nestjs/common@10.4.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.8)(mongoose@8.8.2(socks@2.8.3))(rxjs@7.8.1)': + '@nestjs/mongoose@10.1.0(@nestjs/common@10.4.8(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.8)(mongoose@8.8.2(socks@2.8.3))(rxjs@7.8.1)': dependencies: - '@nestjs/common': 10.4.8(reflect-metadata@0.2.2)(rxjs@7.8.1) - '@nestjs/core': 10.4.8(@nestjs/common@10.4.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.8)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1) + '@nestjs/common': 10.4.8(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1) + '@nestjs/core': 10.4.8(@nestjs/common@10.4.8(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.8)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1) mongoose: 8.8.2(socks@2.8.3) rxjs: 7.8.1 - '@nestjs/platform-express@10.4.8(@nestjs/common@10.4.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.8)': + '@nestjs/platform-express@10.4.8(@nestjs/common@10.4.8(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.8)': dependencies: - '@nestjs/common': 10.4.8(reflect-metadata@0.2.2)(rxjs@7.8.1) - '@nestjs/core': 10.4.8(@nestjs/common@10.4.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.8)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1) + '@nestjs/common': 10.4.8(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1) + '@nestjs/core': 10.4.8(@nestjs/common@10.4.8(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.8)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1) body-parser: 1.20.3 cors: 2.8.5 express: 4.21.1 @@ -9469,13 +9490,13 @@ snapshots: transitivePeerDependencies: - chokidar - '@nestjs/testing@10.4.8(@nestjs/common@10.4.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.8)(@nestjs/platform-express@10.4.8)': + '@nestjs/testing@10.4.8(@nestjs/common@10.4.8(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.8)(@nestjs/platform-express@10.4.8)': dependencies: - '@nestjs/common': 10.4.8(reflect-metadata@0.2.2)(rxjs@7.8.1) - '@nestjs/core': 10.4.8(@nestjs/common@10.4.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.8)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1) + '@nestjs/common': 10.4.8(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1) + '@nestjs/core': 10.4.8(@nestjs/common@10.4.8(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.8)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1) tslib: 2.7.0 optionalDependencies: - '@nestjs/platform-express': 10.4.8(@nestjs/common@10.4.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.8) + '@nestjs/platform-express': 10.4.8(@nestjs/common@10.4.8(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.8) '@nodelib/fs.scandir@2.1.5': dependencies: @@ -10438,6 +10459,8 @@ snapshots: '@types/uuid@9.0.8': {} + '@types/validator@13.12.2': {} + '@types/webidl-conversions@7.0.3': {} '@types/whatwg-url@11.0.5': @@ -11199,6 +11222,12 @@ snapshots: cjs-module-lexer@1.4.1: {} + class-validator@0.14.1: + dependencies: + '@types/validator': 13.12.2 + libphonenumber-js: 1.11.15 + validator: 13.12.0 + clean-stack@2.2.0: {} clean-stack@5.2.0: @@ -13413,6 +13442,8 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 + libphonenumber-js@1.11.15: {} + license-webpack-plugin@4.0.2(webpack@5.96.1): dependencies: webpack-sources: 3.2.3 @@ -15556,6 +15587,8 @@ snapshots: validate-npm-package-name@5.0.1: {} + validator@13.12.0: {} + value-or-promise@1.0.12: {} vary@1.1.2: {} From 4e7f601b0e16da4c0798487272d43345154a2e55 Mon Sep 17 00:00:00 2001 From: zhumeisongsong Date: Wed, 27 Nov 2024 15:16:32 +0900 Subject: [PATCH 10/15] =?UTF-8?q?docs:=20=F0=9F=93=9D=20add=20comments=20o?= =?UTF-8?q?f=20validaiton=20related?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- libs/users/domain/src/lib/user.entity.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libs/users/domain/src/lib/user.entity.ts b/libs/users/domain/src/lib/user.entity.ts index 2f82d96..1e64b9e 100644 --- a/libs/users/domain/src/lib/user.entity.ts +++ b/libs/users/domain/src/lib/user.entity.ts @@ -1,4 +1,8 @@ +// Ensure that the values of all attributes entering the domain layer are valid. +// The constructor is private to prevent direct instantiation of the class. + export class User { + // Class attributes can be declared and initialized directly in the constructor arguments constructor( public readonly id: string, public readonly email: string, From 624c562ba2fa6d2fcaae898f7b64f4f3655c9063 Mon Sep 17 00:00:00 2001 From: zhumeisongsong Date: Wed, 27 Nov 2024 15:24:13 +0900 Subject: [PATCH 11/15] =?UTF-8?q?docs:=20=F0=9F=93=9D=20Fix=20inconsistenc?= =?UTF-8?q?y=20between=20comment=20and=20implementation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- libs/users/domain/src/lib/user.entity.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/libs/users/domain/src/lib/user.entity.ts b/libs/users/domain/src/lib/user.entity.ts index 1e64b9e..8c71c83 100644 --- a/libs/users/domain/src/lib/user.entity.ts +++ b/libs/users/domain/src/lib/user.entity.ts @@ -1,5 +1,4 @@ // Ensure that the values of all attributes entering the domain layer are valid. -// The constructor is private to prevent direct instantiation of the class. export class User { // Class attributes can be declared and initialized directly in the constructor arguments From a4e2dd6509478e7f5db804bbd9b2329091c8fcab Mon Sep 17 00:00:00 2001 From: zhumeisongsong Date: Wed, 27 Nov 2024 15:30:29 +0900 Subject: [PATCH 12/15] =?UTF-8?q?feat:=20=E2=9C=A8=20Add=20validation=20de?= =?UTF-8?q?corators=EF=BC=8Cclass-validator=20to=20DTO?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/lib/dto/user.dto.spec.ts | 44 +++++++++++++++++++ .../src/lib/dto/user.dto.ts | 6 ++- 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/libs/users/interface-adapters/src/lib/dto/user.dto.spec.ts b/libs/users/interface-adapters/src/lib/dto/user.dto.spec.ts index c7f486f..0f7cc82 100644 --- a/libs/users/interface-adapters/src/lib/dto/user.dto.spec.ts +++ b/libs/users/interface-adapters/src/lib/dto/user.dto.spec.ts @@ -13,4 +13,48 @@ describe('UserDto', () => { expect(userDto.email).toBe(email); expect(userDto.firstName).toBe(firstName); }); + + it('should handle empty firstName and lastName', () => { + const id = '456'; + const email = 'jane@example.com'; + const userDto = new UserDto(id, email, null, null); + + expect(userDto).toBeDefined(); + expect(userDto.id).toBe(id); + expect(userDto.email).toBe(email); + expect(userDto.firstName).toBeUndefined(); + expect(userDto.lastName).toBeUndefined(); + }); + + it('should validate email format', () => { + const id = '789'; + const invalidEmail = 'invalid-email'; + + expect(() => { + new UserDto(id, invalidEmail, null, null); + }).toThrow(); + }); + + it('should require id and email', () => { + expect(() => { + // @ts-expect-error testing invalid constructor args + new UserDto(); + }).toThrow(); + + expect(() => { + // @ts-expect-error testing invalid constructor args + new UserDto('123'); + }).toThrow(); + }); + + it('should handle special characters in names', () => { + const id = '101'; + const email = 'test@example.com'; + const firstName = 'Jean-Pierre'; + const lastName = "O'Connor"; + const userDto = new UserDto(id, email, firstName, lastName); + + expect(userDto.firstName).toBe(firstName); + expect(userDto.lastName).toBe(lastName); + }); }); diff --git a/libs/users/interface-adapters/src/lib/dto/user.dto.ts b/libs/users/interface-adapters/src/lib/dto/user.dto.ts index 50b2677..fd61646 100644 --- a/libs/users/interface-adapters/src/lib/dto/user.dto.ts +++ b/libs/users/interface-adapters/src/lib/dto/user.dto.ts @@ -1,3 +1,4 @@ +import { IsEmail, IsString } from 'class-validator'; import { Field, ID, ObjectType } from '@nestjs/graphql'; @ObjectType() @@ -6,12 +7,15 @@ export class UserDto { id: string; @Field() + @IsEmail() email: string; @Field() - firstName: string | null; + @IsString() + firstName?: string | null; @Field() + @IsString() lastName: string | null; constructor( From 9e3b040cc4173c8be0d453d04f21c323741e6829 Mon Sep 17 00:00:00 2001 From: zhumeisongsong Date: Wed, 27 Nov 2024 15:35:20 +0900 Subject: [PATCH 13/15] =?UTF-8?q?style:=20=F0=9F=92=84=20format=20readme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e8529e8..8ad1d41 100644 --- a/README.md +++ b/README.md @@ -56,8 +56,8 @@ You can use `npx nx list` to get a list of installed plugins. Then, run `npx nx | Layer | Description | | ---------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| interface-adapters | It should not contain complex business logic.
Responsible for receiving and processing requests from clients. These requests are interactions with external systems that need to be converted into operations that conform to the business logic through adapters.
Relies on the Application layer for core business logic, avoiding direct interaction with databases or other external systems. | -| resolver(interface-adapters) | Handles GraphQL queries and mutations by converting them into calls to the application layer.
Responsible for input validation and response formatting specific to GraphQL. | +| interface-adapters | It should not contain complex business logic.
Responsible for receiving and processing requests from clients. These requests are interactions with external systems that need to be converted into operations that conform to the business logic through adapters.
Relies on the Application layer for core business logic, avoiding direct interaction with databases or other external systems. | +| resolver(interface-adapters) | Handles GraphQL queries and mutations by converting them into calls to the application layer.
Responsible for input validation and response formatting specific to GraphQL. | | dto(interface-adapters) | Define DTOs for GraphQL schema. | | infrastructure | Implements the technical capabilities needed to support the higher layers of the application.
Handles database connections, external service integrations, and other technical concerns.
Contains concrete implementations of repository interfaces defined in the domain layer. | From 0b33d93c4fca4d977382bbc11bf0476f93757270 Mon Sep 17 00:00:00 2001 From: zhumeisongsong Date: Wed, 27 Nov 2024 15:39:39 +0900 Subject: [PATCH 14/15] =?UTF-8?q?test:=20=F0=9F=A7=AA=20remove=20throw=20e?= =?UTF-8?q?rror=20test=20cases?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/lib/dto/user.dto.spec.ts | 21 ------------------- 1 file changed, 21 deletions(-) diff --git a/libs/users/interface-adapters/src/lib/dto/user.dto.spec.ts b/libs/users/interface-adapters/src/lib/dto/user.dto.spec.ts index 0f7cc82..b7b062f 100644 --- a/libs/users/interface-adapters/src/lib/dto/user.dto.spec.ts +++ b/libs/users/interface-adapters/src/lib/dto/user.dto.spec.ts @@ -26,27 +26,6 @@ describe('UserDto', () => { expect(userDto.lastName).toBeUndefined(); }); - it('should validate email format', () => { - const id = '789'; - const invalidEmail = 'invalid-email'; - - expect(() => { - new UserDto(id, invalidEmail, null, null); - }).toThrow(); - }); - - it('should require id and email', () => { - expect(() => { - // @ts-expect-error testing invalid constructor args - new UserDto(); - }).toThrow(); - - expect(() => { - // @ts-expect-error testing invalid constructor args - new UserDto('123'); - }).toThrow(); - }); - it('should handle special characters in names', () => { const id = '101'; const email = 'test@example.com'; From c537c87ddbb1e2518bd29265074e572056c01a47 Mon Sep 17 00:00:00 2001 From: zhumeisongsong Date: Wed, 27 Nov 2024 15:40:56 +0900 Subject: [PATCH 15/15] =?UTF-8?q?test:=20=F0=9F=A7=AA=20Test=20assertions?= =?UTF-8?q?=20don't=20match=20type=20definitions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- libs/users/interface-adapters/src/lib/dto/user.dto.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/users/interface-adapters/src/lib/dto/user.dto.spec.ts b/libs/users/interface-adapters/src/lib/dto/user.dto.spec.ts index b7b062f..b8276cf 100644 --- a/libs/users/interface-adapters/src/lib/dto/user.dto.spec.ts +++ b/libs/users/interface-adapters/src/lib/dto/user.dto.spec.ts @@ -22,8 +22,8 @@ describe('UserDto', () => { expect(userDto).toBeDefined(); expect(userDto.id).toBe(id); expect(userDto.email).toBe(email); - expect(userDto.firstName).toBeUndefined(); - expect(userDto.lastName).toBeUndefined(); + expect(userDto.firstName).toBeNull(); + expect(userDto.lastName).toBeNull(); }); it('should handle special characters in names', () => {