-
Notifications
You must be signed in to change notification settings - Fork 2
Feature/participates e rotas #543
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
EduTiyo
merged 17 commits into
532-cadastro-expedicoes
from
feature/Participates-e-rotas
Sep 27, 2026
Merged
Changes from all commits
Commits
Show all changes
17 commits
Select commit
Hold shift + click to select a range
8abfacf
feat: interfaces detalhadas para expedicao, participante, e rota
DimitriGaraluz 1e33c0d
feat: adiciona contratos de participantes e rotas em ExpedicaoCollection
DimitriGaraluz f700fde
refactor: update nas interfaces de ExpedicaoCollection por consistencia
DimitriGaraluz cb332b9
feat: adiciona use cases para adicionar e remover participantes, e, s…
DimitriGaraluz c8c09e7
feat: adciona ConflictError para http 409
DimitriGaraluz 7de2280
feat: adiciona factory para ExpedicaoCollection
DimitriGaraluz b2dacef
feat: adiciona controllers e rotas para participantes e rotas
DimitriGaraluz 8ee9b9b
feat: adciona metodos para participantes e rotas, e, ajusta findId e …
DimitriGaraluz 6909a15
test: adiciona testes de integracao para participantes e rotas
DimitriGaraluz b45e95f
test: atualiza teste para refletir interface de retorno paginado
DimitriGaraluz cee01ea
fix: corrige formatacao e erros do lint
DimitriGaraluz f534410
fix: resolve conflitos de merge
DimitriGaraluz ed1d519
fix: lint reafctor
DimitriGaraluz 54724fb
fix: corrige inconsistencia com "SubstitiuiRotasController"
DimitriGaraluz 2f9f3d6
fix: remove interface duplicada ExpedicaoListItem
DimitriGaraluz 3efcf58
refactor: implementa DuplicateParticipantError para tratamento do 409
DimitriGaraluz 66466f3
refactor: altera mapeamento de erro para usar error.name e corrige o …
DimitriGaraluz File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
67 changes: 67 additions & 0 deletions
67
src/application/expedicao/AdicionaParticipanteController.ts
This file contains hidden or 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,67 @@ | ||
| import { AdicionaParticipanteUseCase } from '@/domain/expedicao/AdcionaParticipanteUseCase' | ||
| import { | ||
| HttpRequest, HttpResponse, StatusCode | ||
| } from '@/library/http/common' | ||
| import { BadRequestError } from '@/library/http/error/BadRequestError' | ||
| import { ConflictError } from '@/library/http/error/ConflictError' | ||
| import { HttpError } from '@/library/http/error/HttpError' | ||
| import { InternalServerError } from '@/library/http/error/InternalServerError' | ||
| import { NextHandler, RequestHandler } from '@/library/http/Server' | ||
|
|
||
| interface Dependencies { | ||
| adicionaParticipanteUseCase: AdicionaParticipanteUseCase | ||
| } | ||
|
|
||
| interface CustomHttpRequest extends HttpRequest { | ||
| params: Record<string, string | undefined> | ||
| body: unknown | ||
| usuario?: { | ||
| id: number | ||
| tipo_usuario_id: number | ||
| } | ||
| } | ||
|
|
||
| export class AdicionaParticipanteController implements RequestHandler { | ||
| private readonly adicionaParticipanteUseCase: AdicionaParticipanteUseCase | ||
|
|
||
| constructor(dependencies: Dependencies) { | ||
| this.adicionaParticipanteUseCase = dependencies.adicionaParticipanteUseCase | ||
| } | ||
|
|
||
| async handle(request: CustomHttpRequest, _next: NextHandler): Promise<HttpResponse | HttpError> { | ||
| try { | ||
| // TODO: AUTENTICAÇÃO TEMPORARIAMENTE DESABILITADA | ||
|
|
||
| const { expedicaoId } = request.params | ||
| const { usuarioId } = request.body as { usuarioId?: number } | ||
| if (expedicaoId === undefined || expedicaoId === null || expedicaoId === '' || !/^\d+$/.test(expedicaoId)) { | ||
| return new BadRequestError({ message: 'expedicaoId inválido' }) | ||
| } | ||
|
|
||
| if (usuarioId === undefined || typeof usuarioId !== 'number' || usuarioId <= 0) { | ||
| return new BadRequestError({ message: 'usuarioId inválido' }) | ||
| } | ||
|
|
||
| const result = await this.adicionaParticipanteUseCase.execute(Number(expedicaoId), usuarioId) | ||
|
|
||
| if (result.left()) { | ||
| const error = result.value | ||
| if (error.name === 'DuplicateParticipantError') { | ||
| return new ConflictError({ message: error.message }) | ||
| } | ||
| if (error.name === 'CollectionError') { | ||
| return new InternalServerError({ message: 'Falha interna ao adicionar participante' }) | ||
| } | ||
| return new BadRequestError({ message: error.message }) | ||
| } | ||
|
|
||
| return { | ||
| statusCode: StatusCode.Created, | ||
| body: { message: 'Participante adicionado com sucesso.' } | ||
| } | ||
| } catch (error) { | ||
| const errorMessage = error instanceof Error ? error.message : 'Erro inesperado ao adicionar participante' | ||
| return new InternalServerError({ message: errorMessage }) | ||
| } | ||
| } | ||
| } |
This file contains hidden or 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,58 @@ | ||
| import { RemoveParticipanteUseCase } from '@/domain/expedicao/RemoveParticipanteUseCase' | ||
| import { | ||
| HttpRequest, HttpResponse, StatusCode | ||
| } from '@/library/http/common' | ||
| import { BadRequestError } from '@/library/http/error/BadRequestError' | ||
| import { HttpError } from '@/library/http/error/HttpError' | ||
| import { InternalServerError } from '@/library/http/error/InternalServerError' | ||
| import { NextHandler, RequestHandler } from '@/library/http/Server' | ||
|
|
||
| interface Dependencies { | ||
| removeParticipanteUseCase: RemoveParticipanteUseCase | ||
| } | ||
|
|
||
| interface CustomHttpRequest extends HttpRequest { | ||
| usuario?: { | ||
| id: number | ||
| tipo_usuario_id: number | ||
| } | ||
| } | ||
|
|
||
| export class RemoveParticipanteController implements RequestHandler { | ||
| private readonly removeParticipanteUseCase: RemoveParticipanteUseCase | ||
|
|
||
| constructor(dependencies: Dependencies) { | ||
| this.removeParticipanteUseCase = dependencies.removeParticipanteUseCase | ||
| } | ||
|
|
||
| async handle(request: CustomHttpRequest, _next: NextHandler): Promise<HttpResponse | HttpError> { | ||
| try { | ||
| const { expedicaoId, usuarioId } = request.params | ||
|
|
||
| if (expedicaoId === undefined || expedicaoId === null || expedicaoId === '' || typeof expedicaoId !== 'string' || !/^\d+$/.test(expedicaoId)) { | ||
| return new BadRequestError({ message: 'expedicaoId inválido' }) | ||
| } | ||
|
|
||
| if (usuarioId === undefined || usuarioId === null || usuarioId === '' || typeof usuarioId !== 'string' || !/^\d+$/.test(usuarioId)) { | ||
| return new BadRequestError({ message: 'usuarioId inválido' }) | ||
| } | ||
|
|
||
| const result = await this.removeParticipanteUseCase.execute(Number(expedicaoId), Number(usuarioId)) | ||
|
|
||
| if (result.left()) { | ||
| if (result.value.name === 'CollectionError' || result.value.message.includes('Falha')) { | ||
| return new InternalServerError({ message: 'Falha interna ao remover participante' }) | ||
| } | ||
| return new BadRequestError({ message: result.value.message }) | ||
| } | ||
|
|
||
| return { | ||
| statusCode: StatusCode.NoContent, | ||
| body: undefined | ||
| } | ||
| } catch (error) { | ||
| const errorMessage = error instanceof Error ? error.message : 'Erro inesperado ao remover participante' | ||
| return new InternalServerError({ message: errorMessage }) | ||
| } | ||
| } | ||
| } | ||
This file contains hidden or 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,60 @@ | ||
| import { SubstituiRotasUseCase } from '@/domain/expedicao/SubstituiRotaUseCase' | ||
| import { | ||
| HttpRequest, HttpResponse, StatusCode | ||
| } from '@/library/http/common' | ||
| import { BadRequestError } from '@/library/http/error/BadRequestError' | ||
| import { HttpError } from '@/library/http/error/HttpError' | ||
| import { InternalServerError } from '@/library/http/error/InternalServerError' | ||
| import { NextHandler, RequestHandler } from '@/library/http/Server' | ||
|
|
||
| interface Dependencies { | ||
| SubstituiRotasUseCase: SubstituiRotasUseCase | ||
| } | ||
|
|
||
| interface CustomHttpRequest extends HttpRequest { | ||
| usuario?: { | ||
| id: number | ||
| tipo_usuario_id: number | ||
| } | ||
| } | ||
|
|
||
| export class SubstituiRotasController implements RequestHandler { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Standards] Nome do arquivo não bate com o da classe (smell). O arquivo é |
||
| private readonly SubstituiRotasUseCase: SubstituiRotasUseCase | ||
|
|
||
| constructor(dependencies: Dependencies) { | ||
| this.SubstituiRotasUseCase = dependencies.SubstituiRotasUseCase | ||
| } | ||
|
|
||
| async handle(request: CustomHttpRequest, _next: NextHandler): Promise<HttpResponse | HttpError> { | ||
| try { | ||
| const { expedicaoId } = request.params | ||
| const { rotas } = request.body as { rotas?: number[] } | ||
|
|
||
| if (expedicaoId === undefined || expedicaoId === null || expedicaoId === '' || typeof expedicaoId !== 'string' || !/^\d+$/.test(expedicaoId)) { | ||
| return new BadRequestError({ message: 'expedicaoId inválido' }) | ||
| } | ||
|
|
||
| if (!rotas || !Array.isArray(rotas) || rotas.some(id => typeof id !== 'number' || id <= 0)) { | ||
| return new BadRequestError({ message: 'rotas inválidas' }) | ||
| } | ||
|
|
||
| const result = await this.SubstituiRotasUseCase.execute(Number(expedicaoId), rotas) | ||
|
|
||
| if (result.left()) { | ||
| const error = result.value | ||
| if (error.name === 'CollectionError') { | ||
| return new InternalServerError({ message: 'Falha interna ao substituir rotas' }) | ||
| } | ||
| return new BadRequestError({ message: error.message }) | ||
| } | ||
|
|
||
| return { | ||
| statusCode: StatusCode.NoContent, | ||
| body: undefined | ||
| } | ||
| } catch (error) { | ||
| const errorMessage = error instanceof Error ? error.message : 'Erro inesperado ao substituir rotas' | ||
| return new InternalServerError({ message: errorMessage }) | ||
| } | ||
| } | ||
| } | ||
This file contains hidden or 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
This file contains hidden or 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 { Either } from '@/library/either/Either' | ||
|
|
||
| import { ExpedicaoCollection } from './ExpedicaoCollection' | ||
|
|
||
| interface Dependencies { | ||
| expedicaoCollection: ExpedicaoCollection | ||
| } | ||
|
|
||
| export class AdicionaParticipanteUseCase { | ||
| private readonly expedicaoCollection: ExpedicaoCollection | ||
|
|
||
| constructor(dependencies: Dependencies) { | ||
| this.expedicaoCollection = dependencies.expedicaoCollection | ||
| } | ||
|
|
||
| execute(expedicaoId: number, usuarioId: number): Promise<Either<Error, void>> { | ||
| return this.expedicaoCollection.addParticipant(expedicaoId, usuarioId) | ||
| } | ||
| } |
This file contains hidden or 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
This file contains hidden or 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
This file contains hidden or 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 { Either } from '@/library/either/Either' | ||
|
|
||
| import { ExpedicaoCollection } from './ExpedicaoCollection' | ||
|
|
||
| interface Dependencies { | ||
| expedicaoCollection: ExpedicaoCollection | ||
| } | ||
|
|
||
| export class RemoveParticipanteUseCase { | ||
| private readonly expedicaoCollection: ExpedicaoCollection | ||
|
|
||
| constructor(dependencies: Dependencies) { | ||
| this.expedicaoCollection = dependencies.expedicaoCollection | ||
| } | ||
|
|
||
| execute(expedicaoId: number, usuarioId: number): Promise<Either<Error, void>> { | ||
| return this.expedicaoCollection.removeParticipant(expedicaoId, usuarioId) | ||
| } | ||
| } |
This file contains hidden or 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 { Either } from '@/library/either/Either' | ||
|
|
||
| import { ExpedicaoCollection } from './ExpedicaoCollection' | ||
|
|
||
| interface Dependencies { | ||
| expedicaoCollection: ExpedicaoCollection | ||
| } | ||
|
|
||
| export class SubstituiRotasUseCase { | ||
| private readonly expedicaoCollection: ExpedicaoCollection | ||
|
|
||
| constructor(dependencies: Dependencies) { | ||
| this.expedicaoCollection = dependencies.expedicaoCollection | ||
| } | ||
|
|
||
| execute(expedicaoId: number, rotas: number[]): Promise<Either<Error, void>> { | ||
| return this.expedicaoCollection.substituteRoute(expedicaoId, rotas) | ||
| } | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[Standards] Mapeamento por string, mesmo padrão frágil.
result.value.name === 'CollectionError' || result.value.message.includes('Falha')— funciona hoje porque o adapter só produzCollectionErrorou sucesso, mas o branch "resto vira 400" é código morto: não há nenhuma condição de domínio real (expedição inexistente, participante não encontrado) modelada aqui. Mesmo comentário vale paraSubstituirRotaController.ts:43.