-
Notifications
You must be signed in to change notification settings - Fork 28
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
b1475fc
commit 9c91aa1
Showing
31 changed files
with
467 additions
and
88 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import { Module } from '@nestjs/common'; | ||
|
||
import { DatabaseModule } from '@infra/database/database.module'; | ||
|
||
import { GetUserByEmailUseCase } from './user/get-user-by-email.use-case'; | ||
|
||
@Module({ | ||
imports: [DatabaseModule], | ||
providers: [GetUserByEmailUseCase], | ||
exports: [GetUserByEmailUseCase], | ||
}) | ||
export class UseCasesModule {} |
8 changes: 8 additions & 0 deletions
8
src/application/use-cases/user/errors/user-by-email-not-found.error.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import { DomainError } from '@core/domain/errors/DomainError'; | ||
|
||
export class UserByEmailNotFoundError extends Error implements DomainError { | ||
constructor(email: string) { | ||
super(`User with email '${email}' was not found.`); | ||
this.name = 'UserNotFound'; | ||
} | ||
} |
50 changes: 50 additions & 0 deletions
50
src/application/use-cases/user/get-user-by-email.use-case.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
import { EmailBadFormattedError } from '@domain/value-objects/errors/email-bad-formatted-error'; | ||
|
||
import { UsersRepository } from '@infra/database/repositories/users.repository'; | ||
|
||
import { makeFakeUser } from '@test/factories/users.factory'; | ||
import { InMemoryUsersRepository } from '@test/repositories/in-memory-users.repository'; | ||
|
||
import { UserByEmailNotFoundError } from './errors/user-by-email-not-found.error'; | ||
import { GetUserByEmailUseCase } from './get-user-by-email.use-case'; | ||
|
||
describe('GetUserByEmailUseCase', () => { | ||
let usersRepository: UsersRepository; | ||
|
||
let getUserByEmailUseCase: GetUserByEmailUseCase; | ||
|
||
beforeEach(() => { | ||
usersRepository = new InMemoryUsersRepository(); | ||
|
||
getUserByEmailUseCase = new GetUserByEmailUseCase(usersRepository); | ||
}); | ||
|
||
it('should be able to get user by email', async () => { | ||
const user = makeFakeUser(); | ||
|
||
await usersRepository.create(user); | ||
|
||
const output = await getUserByEmailUseCase.handle(user.email); | ||
|
||
expect(output.isRight()).toBeTruthy(); | ||
expect(output.value).toEqual(user); | ||
}); | ||
|
||
it('should be able an error is returned in case an invalid email address is provided', async () => { | ||
const invalidEmail = 'invalid_email'; | ||
|
||
const output = await getUserByEmailUseCase.handle(invalidEmail); | ||
|
||
expect(output.isLeft()).toBeTruthy(); | ||
expect(output.value).toBeInstanceOf(EmailBadFormattedError); | ||
}); | ||
|
||
it('should be able to return user not found error', async () => { | ||
const email = '[email protected]'; | ||
|
||
const output = await getUserByEmailUseCase.handle(email); | ||
|
||
expect(output.isLeft()).toBeTruthy(); | ||
expect(output.value).toBeInstanceOf(UserByEmailNotFoundError); | ||
}); | ||
}); |
37 changes: 37 additions & 0 deletions
37
src/application/use-cases/user/get-user-by-email.use-case.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import { Injectable } from '@nestjs/common'; | ||
|
||
import { Either, left, right } from '@core/logic/Either'; | ||
|
||
import { User } from '@domain/entities/user.entity'; | ||
import { Email } from '@domain/value-objects/email'; | ||
import { EmailBadFormattedError } from '@domain/value-objects/errors/email-bad-formatted-error'; | ||
|
||
import { UsersRepository } from '@infra/database/repositories/users.repository'; | ||
|
||
import { UserByEmailNotFoundError } from './errors/user-by-email-not-found.error'; | ||
|
||
type GetUserByEmailResponse = Either< | ||
EmailBadFormattedError | UserByEmailNotFoundError, | ||
User | ||
>; | ||
|
||
@Injectable() | ||
export class GetUserByEmailUseCase { | ||
constructor(private readonly usersRepository: UsersRepository) {} | ||
|
||
async handle(email: string): Promise<GetUserByEmailResponse> { | ||
const isInvalidEmail = !Email.validate(email); | ||
|
||
if (isInvalidEmail) { | ||
return left(new EmailBadFormattedError(email)); | ||
} | ||
|
||
const user = await this.usersRepository.findByEmail(email); | ||
|
||
if (!user) { | ||
return left(new UserByEmailNotFoundError(email)); | ||
} | ||
|
||
return right(user); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,7 @@ | ||
import 'dotenv/config'; | ||
|
||
export const USER = process.env.DATABASE_USER ?? 'docker'; | ||
export const PASSWORD = process.env.DATABASE_PASS ?? 'docker'; | ||
export const USER = process.env.DATABASE_USER ?? 'root'; | ||
export const PASSWORD = process.env.DATABASE_PASS ?? 'toor'; | ||
export const HOST = process.env.DATABASE_HOST ?? 'localhost'; | ||
export const PORT = process.env.DATABASE_PORT ?? '3306'; | ||
export const NAME = process.env.DATABASE_NAME ?? 'app'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
/** | ||
* Replaces the property definitions from a given type | ||
* @example | ||
* ```typescript | ||
* type Post { | ||
* id: string; | ||
* name: string; | ||
* } | ||
* | ||
* Replace<Post, {id: number}> | ||
* ``` | ||
**/ | ||
|
||
export type Replace<T, R> = Omit<T, keyof R> & R; |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import { Entity } from '@core/domain/Entity'; | ||
import { Replace } from '@core/logic/Replace'; | ||
|
||
export type UserProps = { | ||
email: string; | ||
createdAt: Date; | ||
}; | ||
|
||
export class User extends Entity<UserProps> { | ||
get email() { | ||
return this.props.email; | ||
} | ||
|
||
get createdAt() { | ||
return this.props.createdAt; | ||
} | ||
|
||
static create( | ||
props: Replace< | ||
UserProps, | ||
{ | ||
createdAt?: Date; | ||
} | ||
>, | ||
id?: string, | ||
) { | ||
const user = new User( | ||
{ | ||
...props, | ||
createdAt: props.createdAt ?? new Date(), | ||
}, | ||
id, | ||
); | ||
|
||
return user; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import { Either, left, right } from '@core/logic/Either'; | ||
|
||
import { EmailBadFormattedError } from './errors/email-bad-formatted-error'; | ||
|
||
export class Email { | ||
protected constructor(private readonly email: string) {} | ||
|
||
get value(): string { | ||
return this.email; | ||
} | ||
|
||
static validate(email: string): boolean { | ||
const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/; | ||
|
||
return emailRegex.test(email); | ||
} | ||
|
||
static create(email: string): Either<EmailBadFormattedError, Email> { | ||
const isValidEmail = this.validate(email); | ||
|
||
if (!isValidEmail) { | ||
return left(new EmailBadFormattedError(email)); | ||
} | ||
|
||
return right(new Email(email)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import { DomainError } from '@core/domain/errors/DomainError'; | ||
|
||
export class EmailBadFormattedError extends Error implements DomainError { | ||
constructor(email: string) { | ||
super(`The email '${email}' is bad formatted.`); | ||
this.name = 'EmailBadFormatted'; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,17 @@ | ||
import { Module } from '@nestjs/common'; | ||
|
||
import { PrismaService } from './prisma/prisma.service'; | ||
import { PrismaUsersRepository } from './prisma/repositories/prisma-users-repository'; | ||
import { UsersRepository } from './repositories/users.repository'; | ||
|
||
@Module({ | ||
providers: [PrismaService], | ||
exports: [PrismaService], | ||
providers: [ | ||
PrismaService, | ||
{ | ||
provide: UsersRepository, | ||
useClass: PrismaUsersRepository, | ||
}, | ||
], | ||
exports: [PrismaService, UsersRepository], | ||
}) | ||
export class DatabaseModule {} |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { Prisma, User as RawUser } from '@prisma/client'; | ||
|
||
import { User } from '@domain/entities/user.entity'; | ||
|
||
export class UserMapper { | ||
static toDomain(raw: RawUser): User { | ||
const user = User.create({ | ||
email: raw.email, | ||
createdAt: raw.created_at, | ||
}); | ||
|
||
return user; | ||
} | ||
|
||
static toPersistence(user: User): Prisma.UserCreateInput { | ||
return { | ||
id: user.id, | ||
email: user.email, | ||
created_at: user.createdAt, | ||
}; | ||
} | ||
} |
Empty file.
37 changes: 37 additions & 0 deletions
37
src/infra/database/prisma/repositories/prisma-users-repository.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import { Injectable } from '@nestjs/common'; | ||
|
||
import { AsyncMaybe } from '@core/logic/Maybe'; | ||
|
||
import { User } from '@domain/entities/user.entity'; | ||
|
||
import { UsersRepository } from '@infra/database/repositories/users.repository'; | ||
|
||
import { UserMapper } from '../mappers/user.mapper'; | ||
import { PrismaService } from '../prisma.service'; | ||
|
||
@Injectable() | ||
export class PrismaUsersRepository implements UsersRepository { | ||
constructor(private readonly prisma: PrismaService) {} | ||
|
||
async create(user: User): Promise<User> { | ||
await this.prisma.user.create({ | ||
data: UserMapper.toPersistence(user), | ||
}); | ||
|
||
return user; | ||
} | ||
|
||
async findByEmail(email: string): AsyncMaybe<User> { | ||
const user = await this.prisma.user.findUnique({ | ||
where: { | ||
email, | ||
}, | ||
}); | ||
|
||
if (!user) { | ||
return null; | ||
} | ||
|
||
return UserMapper.toDomain(user); | ||
} | ||
} |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import { Injectable } from '@nestjs/common'; | ||
|
||
import { AsyncMaybe } from '@core/logic/Maybe'; | ||
|
||
import { User } from '@domain/entities/user.entity'; | ||
|
||
@Injectable() | ||
export abstract class UsersRepository { | ||
abstract create(user: User): Promise<User>; | ||
abstract findByEmail(email: string): AsyncMaybe<User>; | ||
} |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import { Injectable } from '@nestjs/common'; | ||
import { Field, ID, ObjectType } from '@nestjs/graphql'; | ||
|
||
import { Paginated } from '../../common/dto/models/paginated'; | ||
|
||
@ObjectType() | ||
export class User { | ||
@Field(() => ID) | ||
id: string; | ||
|
||
@Field(() => String) | ||
email: string; | ||
|
||
@Field(() => Date) | ||
createdAt: Date; | ||
} | ||
|
||
@Injectable() | ||
export class PaginatedUsers extends Paginated(User) {} |
36 changes: 0 additions & 36 deletions
36
src/infra/http/graphql/resolvers/example.resolver.e2e-spec.ts
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.