Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 3 additions & 1 deletion src/application/create-app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { assets, upload } from '../config/directory'
import legacyErrors from '../middlewares/erros-middleware'
import { generatePreview, reportPreview } from '../reports/controller'
import { routes as createEstadoRoutes } from './estado'
import { routes as createFaseSucessionalRoutes } from './fase-sucessional'
import { routes as createPaisRoutes } from './pais'

interface CorsParameters {
Expand Down Expand Up @@ -55,7 +56,8 @@ export function createApp({
}: Parameters) {
const routes: Route[] = [
...createPaisRoutes(knex),
...createEstadoRoutes(knex)
...createEstadoRoutes(knex),
...createFaseSucessionalRoutes(knex)
]
const application = new ExpressApplication({ logger })

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { BuscarFaseSucessionalUseCase } from '@/domain/faseSucessional/BuscarFaseSucessionalUseCase'
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 { NotFoundError } from '@/library/http/error/NotFoundError'
import { NextHandler, RequestHandler } from '@/library/http/Server'

interface Dependencies {
buscarFaseSucessionalUseCase: BuscarFaseSucessionalUseCase
}

export class BuscarFaseSucessionalController implements RequestHandler {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

O restante da v2 usa Lista (não Listar). Renomear BuscarFaseSucessionalController, BuscarFaseSucessionalUseCase e os arquivos correspondentes para Busca*.

private readonly buscarFaseSucessionalUseCase: BuscarFaseSucessionalUseCase

constructor(dependencies: Dependencies) {
this.buscarFaseSucessionalUseCase = dependencies.buscarFaseSucessionalUseCase
}

async handle(request: HttpRequest, _next: NextHandler): Promise<HttpResponse | HttpError> {
const { faseSucessionalId } = request.params as { faseSucessionalId?: string }

if (!faseSucessionalId || Number.isNaN(Number(faseSucessionalId)) || Number(faseSucessionalId) <= 0) {
return new BadRequestError({ message: 'faseSucessionalId inválido' })
}

const id = Number(faseSucessionalId)
const result = await this.buscarFaseSucessionalUseCase.execute({ id })

if (result.left()) {
return new InternalServerError({ message: result.value.message })
}

if (!result.value) {
return new NotFoundError({ message: 'Fase sucessional não encontrada' })
}

return { statusCode: StatusCode.Ok, body: result.value }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { ListaFasesSucessionaisUseCase } from '@/domain/faseSucessional/ListaFasesSucessionaisUseCase'
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 {
listaFasesSucessionaisUseCase: ListaFasesSucessionaisUseCase
}

export class ListaFasesSucessionaisController implements RequestHandler {
private readonly listaFasesSucessionaisUseCase: ListaFasesSucessionaisUseCase

constructor(dependencies: Dependencies) {
this.listaFasesSucessionaisUseCase = dependencies.listaFasesSucessionaisUseCase
}

async handle(request: HttpRequest, _next: NextHandler): Promise<HttpResponse | HttpError> {
const { nome, order } = request.params as {
nome?: string
order?: string
}

const parsedOrder = parseOrder(order)
if (parsedOrder instanceof Error) {
return new BadRequestError({ message: parsedOrder.message })
}

const result = await this.listaFasesSucessionaisUseCase.execute({
nome,
order: parsedOrder
})

if (result.left()) {
return new InternalServerError({ message: result.value.message })
}

return { statusCode: StatusCode.Ok, body: result.value }
}
}

function parseOrder(order?: string): { column: 'id' | 'nome'; direction: 'asc' | 'desc' } | Error | undefined {
if (!order) return undefined

const pieces = order.split(':')
if (pieces.length !== 2) {
return new Error('order inválido. Use o formato "id:asc", "id:desc", "nome:asc" ou "nome:desc"')
}

const [rawColumn, rawDirection] = pieces
const column = rawColumn.trim().toLowerCase()
const direction = rawDirection.trim().toLowerCase()

if ((column !== 'id' && column !== 'nome') || (direction !== 'asc' && direction !== 'desc')) {
return new Error('order inválido. Use o formato "id:asc", "id:desc", "nome:asc" ou "nome:desc"')
}

return {
column,
direction
}
}
35 changes: 35 additions & 0 deletions src/application/fase-sucessional/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { type Knex } from 'knex'

import { BuscarFaseSucessionalUseCase } from '@/domain/faseSucessional/BuscarFaseSucessionalUseCase'
import { ListaFasesSucessionaisUseCase } from '@/domain/faseSucessional/ListaFasesSucessionaisUseCase'
import { FaseSucessionalCollectionKnexAdapter } from '@/infrastructure/FaseSucessionalCollectionKnexAdapter'
import { Method } from '@/library/http/common'
import { Route } from '@/library/http/Router'

import { BuscarFaseSucessionalController } from './BuscarFaseSucessionalController'
import { ListaFasesSucessionaisController } from './ListaFasesSucessionaisController'

export function routes(knex: Knex): Route[] {
const faseSucessionalCollection = new FaseSucessionalCollectionKnexAdapter({ knex })

return [
{
handlers: [
new ListaFasesSucessionaisController({
listaFasesSucessionaisUseCase: new ListaFasesSucessionaisUseCase({ faseSucessionalCollection })
})
],
method: Method.Get,
path: '/v2/fases-sucessionais'
},
{
handlers: [
new BuscarFaseSucessionalController({
buscarFaseSucessionalUseCase: new BuscarFaseSucessionalUseCase({ faseSucessionalCollection })
})
],
method: Method.Get,
path: '/v2/fases-sucessionais/:faseSucessionalId'
}
]
}
20 changes: 20 additions & 0 deletions src/domain/faseSucessional/BuscarFaseSucessionalUseCase.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Either } from '@/library/either/Either'

import { Attributes } from './FaseSucessional'
import { FaseSucessionalCollection } from './FaseSucessionalCollection'

interface Dependencies {
faseSucessionalCollection: FaseSucessionalCollection
}

export class BuscarFaseSucessionalUseCase {
private readonly faseSucessionalCollection: FaseSucessionalCollection

constructor(dependencies: Dependencies) {
this.faseSucessionalCollection = dependencies.faseSucessionalCollection
}

async execute({ id }: { id: number }): Promise<Either<Error, Attributes | null>> {
return this.faseSucessionalCollection.findById(id)
}
}
24 changes: 24 additions & 0 deletions src/domain/faseSucessional/FaseSucessional.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Either } from '@/library/either/Either'

export interface Attributes {
id: number
nome: string
}

export class FaseSucessional {
readonly id: number
readonly nome: string

private constructor(attributes: Attributes) {
this.id = attributes.id
this.nome = attributes.nome
}

static create(attributes: Attributes): Either<Error, FaseSucessional> {
if (!attributes.nome.trim()) {
return Either.left(new Error('Nome da fase sucessional não pode ser vazio'))
}

return Either.right(new FaseSucessional(attributes))
}
}
16 changes: 16 additions & 0 deletions src/domain/faseSucessional/FaseSucessionalCollection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Either } from '@/library/either/Either'

import { Attributes } from './FaseSucessional'

export interface FaseSucessionalFilters {
nome?: string
order?: {
column: 'id' | 'nome'
direction: 'asc' | 'desc'
}
}

export interface FaseSucessionalCollection {
findAll(filters: FaseSucessionalFilters): Promise<Either<Error, Attributes[]>>
findById(id: number): Promise<Either<Error, Attributes | null>>
}
20 changes: 20 additions & 0 deletions src/domain/faseSucessional/ListaFasesSucessionaisUseCase.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Either } from '@/library/either/Either'

import { Attributes } from './FaseSucessional'
import { FaseSucessionalCollection, FaseSucessionalFilters } from './FaseSucessionalCollection'

interface Dependencies {
faseSucessionalCollection: FaseSucessionalCollection
}

export class ListaFasesSucessionaisUseCase {
private readonly faseSucessionalCollection: FaseSucessionalCollection

constructor(dependencies: Dependencies) {
this.faseSucessionalCollection = dependencies.faseSucessionalCollection
}

async execute(filters: FaseSucessionalFilters): Promise<Either<Error, Attributes[]>> {
return this.faseSucessionalCollection.findAll(filters)
}
}
53 changes: 53 additions & 0 deletions src/infrastructure/FaseSucessionalCollectionKnexAdapter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { Knex } from 'knex'

import { Attributes } from '@/domain/faseSucessional/FaseSucessional'
import { FaseSucessionalCollection, FaseSucessionalFilters } from '@/domain/faseSucessional/FaseSucessionalCollection'
import { Either } from '@/library/either/Either'

import { CollectionError } from './error/CollectionError'

interface Dependencies {
knex: Knex
}

export class FaseSucessionalCollectionKnexAdapter implements FaseSucessionalCollection {
private readonly knex: Knex

constructor(dependencies: Dependencies) {
this.knex = dependencies.knex
}

async findAll(filters: FaseSucessionalFilters): Promise<Either<Error, Attributes[]>> {
try {
const query = this.knex<Attributes>('fase_sucessional')
.select(['id', 'nome'])

if (filters.nome) {
query.whereILike('nome', `%${filters.nome}%`)
}

if (filters.order) {
query.orderBy(filters.order.column, filters.order.direction)
} else {
query.orderBy('id', 'desc')
}

return Either.right(await query)
} catch (error) {
return Either.left(new CollectionError({ message: 'Failed to list fases sucessionais', cause: error }))
}
}

async findById(id: number): Promise<Either<Error, Attributes | null>> {
try {
const faseSucessional = await this.knex<Attributes>('fase_sucessional')
.select(['id', 'nome'])
.where({ id })
.first()

return Either.right(faseSucessional ?? null)
} catch (error) {
return Either.left(new CollectionError({ message: 'Failed to find fase sucessional by id', cause: error }))
}
}
}
Loading
Loading