Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
9bcbd64
feat: add image field in Load Guardian By Id Repository Result
andersonnaz Mar 9, 2026
332e159
feat: add update guardian repository protocol
andersonnaz Mar 9, 2026
99ed15a
feat: create update guardian use case
andersonnaz Mar 9, 2026
954cd1a
feat: add update method in guardian repository
andersonnaz Mar 9, 2026
16defed
feat: implemented update guardian use case
andersonnaz Mar 9, 2026
4d8133e
feat: add update guardian controller
andersonnaz Mar 9, 2026
0cb95ee
feat: add update guardian use case factory
andersonnaz Mar 9, 2026
19b52a7
feat: add update guardian controller factory
andersonnaz Mar 9, 2026
6753295
feat: add update guardian validations factory
andersonnaz Mar 9, 2026
1e38e01
feat: create update guardian route
andersonnaz Mar 9, 2026
2ab865b
test: add image field in guardian mock
andersonnaz Mar 9, 2026
4103c48
docs: add update guardian route in documentation
andersonnaz Mar 9, 2026
9815d9e
test: ensure return a guardian when image id updated
andersonnaz Mar 9, 2026
3201b21
refactor: add a trycatch on updateImage method in guardian account re…
andersonnaz Mar 9, 2026
b815011
test: ensure return undefined if a invalid guardian id is provided
andersonnaz Mar 9, 2026
e918e39
refactor: removed unused field on result
andersonnaz Mar 9, 2026
c36a27c
refactor: add trycatch on updateImage and update methods
andersonnaz Mar 9, 2026
692a69e
test: ensure return a guardian when update success
andersonnaz Mar 9, 2026
b8f2a3d
test: ensure call loadById method with correct values
andersonnaz Mar 9, 2026
5d2ea12
test: ensure return Not Acceptable Error if incorrect guardianId is p…
andersonnaz Mar 9, 2026
68a69e9
test: ensure throw if loadById method throws
andersonnaz Mar 9, 2026
11f3852
test: ensure call save method with correct values
andersonnaz Mar 9, 2026
ca349a3
test: ensure throw if save method throws
andersonnaz Mar 9, 2026
c3d4297
test: ensure call delete method with correct values
andersonnaz Mar 9, 2026
00bb7fe
test: ensure reutrn 406 (NotAcceptable) if invalid data is provided
andersonnaz Mar 9, 2026
76e3d3a
test: ensure return 500 (ServerError) if update throws
andersonnaz Mar 9, 2026
c38e70f
test: ensure call update with correct values
andersonnaz Mar 9, 2026
66d2a57
test: ensure return 400(BadRequest) if validation returns an error
andersonnaz Mar 9, 2026
6ba4ff2
test: ensure call validation with correct values
andersonnaz Mar 9, 2026
a0987e5
test: ensure reutrn 200 (success) if empty data is provided
andersonnaz Mar 9, 2026
682af86
test: ensure return 200 (success) if valid data is provided
andersonnaz Mar 9, 2026
1648611
fix: change class to validate phone
andersonnaz Mar 12, 2026
db2b510
test: ensure return a updated guardian in route PUT guardian
andersonnaz Mar 12, 2026
bc70e12
test: ensure return 400 if no access token is provided
andersonnaz Mar 12, 2026
5df8245
test: ensure return 406 (NotAcceptable) if invalid guardianId is prov…
andersonnaz Mar 12, 2026
1a3377c
test: removed unused timeout
andersonnaz Mar 12, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/application/controllers/guardian/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './update-guardian'
45 changes: 45 additions & 0 deletions src/application/controllers/guardian/update-guardian.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { badRequest, notAcceptable, serverError, success, type HttpRequest, type HttpResponse } from '@/application/helpers'
import { type Controller, type Validation } from '@/application/protocols'
import { type UpdateGuardian } from '@/domain/use-cases'

export class UpdateGuardianController implements Controller {
private readonly validation: Validation
private readonly UpdateGuardian: UpdateGuardian

constructor ({ validation, updateGuardian }: UpdateGuardianController.Dependencies) {
this.validation = validation
this.UpdateGuardian = updateGuardian
}

async handle (httpRequest: HttpRequest): Promise<HttpResponse> {
try {
const error = this.validation.validate({ ...httpRequest.body, ...httpRequest.params })
if (error) {
return badRequest(error)
}
const { guardianId } = httpRequest.params
const image = httpRequest.file ?? null
const updateData = { ...httpRequest.body, guardianId, image }
const result = await this.UpdateGuardian.update(updateData)
if (!result.isSuccess) {
return notAcceptable(result.error as Error)
}
return success({
id: result.data?.id,
firstName: result.data?.firstName,
lastName: result.data?.lastName,
phone: result.data?.phone,
image: result.data?.image
})
} catch (error) {
return serverError(error as Error)
}
}
}

export namespace UpdateGuardianController {
export interface Dependencies {
validation: Validation
updateGuardian: UpdateGuardian
}
}
1 change: 1 addition & 0 deletions src/application/controllers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ export * from './email-confirmation'
export * from './scheduler'
export * from './tasks'
export * from './settings'
export * from './guardian'
1 change: 1 addition & 0 deletions src/data/protocols/db/guardian/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ export * from './update-guardian-password-repository'
export * from './save-token-repository'
export * from './update-email-confirmation-repository'
export * from './update-guardian-image-repository'
export * from './update-guardian-repository'
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,6 @@ export namespace LoadGuardianByIdRepository {
verificationToken: string
verificationTokenCreatedAt: Date
emailConfirmation: boolean
image: string
} | null
}
22 changes: 22 additions & 0 deletions src/data/protocols/db/guardian/update-guardian-repository.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
export interface UpdateGuardianRepository {
update: (params: UpdateGuardianRepository.Params) => Promise<UpdateGuardianRepository.Result>
}

export namespace UpdateGuardianRepository {
export type Params = {
guardianId: string
firstName?: string
lastName?: string
phone?: string
image?: string
}

export type Result = {
id: string
firstName: string
lastName: string
phone: string
image: string
} | undefined

}
55 changes: 55 additions & 0 deletions src/data/use-cases/guardian/db-update-guardian.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { NotAcceptableError } from '@/application/errors'
import { type DeleteFileStorage, type FileStorage, type LoadGuardianByIdRepository, type UpdateGuardianRepository } from '@/data/protocols'
import { type UpdateGuardian } from '@/domain/use-cases'

export class DbUpdateGuardian implements UpdateGuardian {
private readonly guardianRepository: LoadGuardianByIdRepository & UpdateGuardianRepository
private readonly fileStorage: FileStorage & DeleteFileStorage

constructor ({ guardianRepository, fileStorage }: UpdateGuardian.Dependencies) {
this.guardianRepository = guardianRepository
this.fileStorage = fileStorage
}

async update (guardianData: UpdateGuardian.Params): Promise<UpdateGuardian.Result> {
const guardian = await this.guardianRepository.loadById(guardianData.guardianId)
if (!guardian) {
return {
isSuccess: false,
error: new NotAcceptableError('userId')
}
}

let finalImage: string = ''
if (guardianData.image) {
const currentImage = guardian.image
const updatedImage = await this.fileStorage.save({ file: guardianData.image, fileName: `images/guardian-${guardian?.id}-${Math.trunc(Date.now() / 1000)}` })
if (!updatedImage) {
return {
isSuccess: false,
error: new NotAcceptableError('update image failed')
}
}
if (currentImage) {
await this.fileStorage.delete({ fileUrlOrPath: currentImage })
}
finalImage = updatedImage
}
if (!guardianData.image) {
finalImage = guardian.image
}

const guardianUpdateResult = await this.guardianRepository.update({
guardianId: guardian.id,
firstName: guardianData.firstName ? guardianData.firstName : guardian.firstName,
lastName: guardianData.lastName ? guardianData.lastName : guardian.lastName,
phone: guardianData.phone ? guardianData.phone : guardian.phone,
image: finalImage
})

return {
isSuccess: true,
data: guardianUpdateResult
}
}
}
1 change: 1 addition & 0 deletions src/data/use-cases/guardian/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './db-update-guardian'
1 change: 1 addition & 0 deletions src/data/use-cases/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ export * from './db-send-email'
export * from './scheduler'
export * from './tasks'
export * from './settings'
export * from './guardian'
1 change: 1 addition & 0 deletions src/domain/use-cases/guardian/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './update-guardian'
26 changes: 26 additions & 0 deletions src/domain/use-cases/guardian/update-guardian.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { type DeleteFileStorage, type FileStorage, type LoadGuardianByIdRepository, type UpdateGuardianRepository } from '@/data/protocols'

export interface UpdateGuardian {
update: (params: UpdateGuardian.Params) => Promise<UpdateGuardian.Result>
}

export namespace UpdateGuardian {
export type Params = {
guardianId: string
firstName?: string
lastName?: string
phone?: string
image?: Buffer | null
}

export type Result = {
isSuccess: boolean
error?: Error
data?: UpdateGuardianRepository.Result
}

export type Dependencies = {
guardianRepository: LoadGuardianByIdRepository & UpdateGuardianRepository
fileStorage: FileStorage & DeleteFileStorage
}
}
1 change: 1 addition & 0 deletions src/domain/use-cases/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ export * from './send-email'
export * from './scheduler'
export * from './tasks'
export * from './settings'
export * from './guardian'
52 changes: 40 additions & 12 deletions src/infra/repos/postgresql/guardian-account-repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
type UpdateVerificationTokenRepository,
type UpdateEmailConfirmationRepository
} from '@/data/protocols'
import { type UpdateGuardianRepository } from '@/data/protocols/db/guardian/update-guardian-repository'

export class GuardianAccountRepository
implements AddGuardianRepository, LoadGuardianByEmailRepository,
Expand All @@ -17,7 +18,8 @@ implements AddGuardianRepository, LoadGuardianByEmailRepository,
UpdateGuardianPasswordRepository,
UpdateVerificationTokenRepository,
UpdateEmailConfirmationRepository,
UpdateGuardianImageRepository {
UpdateGuardianImageRepository,
UpdateGuardianRepository {
async add (
guardianData: AddGuardianRepository.Params
): Promise<AddGuardianRepository.Result> {
Expand Down Expand Up @@ -140,17 +142,43 @@ implements AddGuardianRepository, LoadGuardianByEmailRepository,
}

async updateImage (params: UpdateGuardianImageRepository.Params): Promise<UpdateGuardianImageRepository.Result> {
const { guardianId, image } = params
const result = await db.guardian.update({
where: { id: guardianId },
data: { image },
omit: {
password: true,
verificationToken: true,
verificationTokenCreatedAt: true
}
})
try {
const { guardianId, image } = params
const result = await db.guardian.update({
where: { id: guardianId },
data: { image },
omit: {
password: true,
verificationToken: true,
verificationTokenCreatedAt: true,
emailConfirmation: true,
accessToken: true
}
})
return result
} catch (error) {
return undefined
}
}

return result
async update (params: UpdateGuardianRepository.Params): Promise<UpdateGuardianRepository.Result> {
try {
const { guardianId, ...updateData } = params
const result = await db.guardian.update({
where: { id: guardianId },
data: updateData,
omit: {
password: true,
verificationToken: true,
verificationTokenCreatedAt: true,
accessToken: true,
email: true,
emailConfirmation: true
}
})
return result
} catch (error) {
return undefined
}
}
}
4 changes: 3 additions & 1 deletion src/main/docs/paths.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ import {
loadPetByIdPath,
loadNextTasksByPetIdPath,
loadPreviousTasksByPetIdPath,
loadNextTasksByPetIdAndTagIdPath
loadNextTasksByPetIdAndTagIdPath,
updateGuardianPath
} from './paths/'

export default {
Expand All @@ -37,6 +38,7 @@ export default {
'/forget-password': forgetPasswordPath,
'/guardian/change-password': changePasswordPath,
'/guardian/email-confirmation/{userId}': emailConfirmationPath,
'/guardian/{guardianId}': updateGuardianPath,
'/waiting-code': waitingCodePath,
'/pet': { ...petRegistryPath, ...loadPetsPath },
'/pet/{petId}': { ...loadPetByIdPath, ...updatePetPath, ...deletePetPath },
Expand Down
1 change: 1 addition & 0 deletions src/main/docs/paths/guardian/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './update-guardian-path'
42 changes: 42 additions & 0 deletions src/main/docs/paths/guardian/update-guardian-path.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { DocBuilder } from '../../utils/doc-builder'

export const updateGuardianPath = DocBuilder.putBuilder()
.addTags(['guardian'])
.addSummary('Update an existing guardian')
.addJwtAuthSecurity()
.addPathParameter('guardianId', 'Guardian ID', 'string')
.addMultipartFormDataBody({
type: 'object',
properties: {
firstName: {
type: 'string',
example: 'John'
},
lastName: {
type: 'string',
example: 'Doe'
},
phone: {
type: 'string',
example: '11987654321'
},
image: {
type: 'string',
format: 'binary'
}
}
})
.addResponse(200, {
description: 'Success',
content: {
'application/json': {
schema: {
$ref: '#/schemas/guardian'
}
}
}
})
.addBadRequestResponse()
.addNotAcceptableResponse()
.addServerErrorResponse()
.build()
1 change: 1 addition & 0 deletions src/main/docs/paths/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ export * from './email-confirmation-path'
export * from './scheduler'
export * from './tasks'
export * from './settings'
export * from './guardian'
1 change: 1 addition & 0 deletions src/main/factories/controllers/guardian/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './update-guardian-factory'
19 changes: 19 additions & 0 deletions src/main/factories/controllers/guardian/update-guardian-factory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { UpdateGuardianController } from '@/application/controllers'
import { type Controller } from '@/application/protocols'
import { makeDbUpdateGuardian } from '../../usecases'
import { makeUpdateGuardianValidation } from '../../validations'
import { LoggerPgRepository } from '@/infra/repos/postgresql'
import { DevLoggerControllerDecorator, LoggerControllerDecorator } from '@/main/decorators'

export const makeUpdateGuardianController = (): Controller => {
const updateGuardian = makeDbUpdateGuardian()
const validation = makeUpdateGuardianValidation()
const dependencies: UpdateGuardianController.Dependencies = {
updateGuardian,
validation
}
const updateGuardianController = new UpdateGuardianController(dependencies)
const loggerPgRepository = new LoggerPgRepository()
const loggerControllerDecorator = new LoggerControllerDecorator(updateGuardianController, loggerPgRepository)
return new DevLoggerControllerDecorator(loggerControllerDecorator)
}
1 change: 1 addition & 0 deletions src/main/factories/controllers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ export * from './email-confirmation-factory'
export * from './scheduler'
export * from './tasks'
export * from './settings'
export * from './guardian'
15 changes: 15 additions & 0 deletions src/main/factories/usecases/guardian/db-update-guardian-factory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { DbUpdateGuardian } from '@/data/use-cases'
import { type UpdateGuardian } from '@/domain/use-cases'
import { FirebaseStorageAdapter } from '@/infra/repos/firebase'
import { GuardianAccountRepository } from '@/infra/repos/postgresql'
import env from '@/main/config/env'

export const makeDbUpdateGuardian = (): UpdateGuardian => {
const guardianRepository = new GuardianAccountRepository()
const fileStorage = new FirebaseStorageAdapter(env.firebase.projectId, env.firebase.storageBucket, env.firebase.serviceAccount)
const dependencies: UpdateGuardian.Dependencies = {
guardianRepository,
fileStorage
}
return new DbUpdateGuardian(dependencies)
}
1 change: 1 addition & 0 deletions src/main/factories/usecases/guardian/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './db-update-guardian-factory'
1 change: 1 addition & 0 deletions src/main/factories/usecases/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ export * from './db-send-email-factory'
export * from './scheduler'
export * from './tasks'
export * from './settings'
export * from './guardian'
1 change: 1 addition & 0 deletions src/main/factories/validations/guardian/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './update-guardian-factory'
13 changes: 13 additions & 0 deletions src/main/factories/validations/guardian/update-guardian-factory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { type Validation } from '@/application/protocols'
import { NameValidation, OptionalFieldValidation, PhoneValidation, ValidationComposite } from '@/application/validation'
import { NameValidatorAdapter, PhoneValidatorAdapter } from '@/infra/validators'

export const makeUpdateGuardianValidation = (): ValidationComposite => {
const validations: Validation[] = []

validations.push(new OptionalFieldValidation('firstName', new NameValidation('firstName', new NameValidatorAdapter())))
validations.push(new OptionalFieldValidation('lastName', new NameValidation('lastName', new NameValidatorAdapter())))
validations.push(new OptionalFieldValidation('phone', new PhoneValidation('phone', new PhoneValidatorAdapter())))

return new ValidationComposite(validations)
}
1 change: 1 addition & 0 deletions src/main/factories/validations/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ export * from './change-password-validation-factory'
export * from './waiting-code-validation-factory'
export * from './pet'
export * from './scheduler'
export * from './guardian'
Loading