Skip to content
Merged
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
26 changes: 26 additions & 0 deletions infra/controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { InternalServerError, MethodNotAllowedError } from './errors'

function onNoMatchHandler(request, response) {
const publicErrorObject = new MethodNotAllowedError()
console.error(publicErrorObject)
response.status(publicErrorObject.statusCode).json(publicErrorObject)
}

function onErrorHandler(error, request, response) {
const publicErrorObject = new InternalServerError({
statusCode: error.statusCode,
cause: error
})

console.error(publicErrorObject)
response.status(publicErrorObject.statusCode).json(publicErrorObject)
}

const controller = {
errorHandlers: {
onNoMatch: onNoMatchHandler,
onError: onErrorHandler
}
}

export default controller
9 changes: 6 additions & 3 deletions infra/database.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Client } from 'pg'
import { ServiceError } from './errors.js'

async function query(queryObject) {
let client
Expand All @@ -8,9 +9,11 @@ async function query(queryObject) {
const result = await client.query(queryObject)
return result
} catch (error) {
console.log('\n Erro dentro do catch do database.js:')
console.error(error)
throw error
const serviceErrorObject = new ServiceError({
message: 'Erro na conexão com Banco ou na Query.',
cause: error
})
throw serviceErrorObject
} finally {
await client?.end()
}
Expand Down
43 changes: 41 additions & 2 deletions infra/errors.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,50 @@
export class InternalServerError extends Error {
constructor({ cause }) {
constructor({ cause, statusCode }) {
super('Um erro interno não esperado aconteceu.', {
cause
})
this.name = 'InternalServerError'
this.action = 'Entre em contato com o suporte.'
this.statusCode = 500
this.statusCode = statusCode || 500
}

toJSON() {
return {
name: this.name,
message: this.message,
action: this.action,
status_code: this.statusCode
}
}
}

export class MethodNotAllowedError extends Error {
constructor() {
super('Método não permitido para este endpoint.')
this.name = 'MethodNotAllowedError'
this.action =
'Verifique se o método HTTP enviado é valido para este endpoint.'
this.statusCode = 405
}

toJSON() {
return {
name: this.name,
message: this.message,
action: this.action,
status_code: this.statusCode
}
}
}

export class ServiceError extends Error {
constructor({ cause, message }) {
super(message || 'Serviço indisponível no momento.', {
cause
})
this.name = 'ServiceError'
this.action = 'Verifique se o serviço está disponível.'
this.statusCode = 503
}

toJSON() {
Expand Down
29 changes: 29 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
"version": "1.0.0",
"description": "",
"main": "index.js",
"imports": {
"@/*": "./src/*"
},
"scripts": {
"dev": "node infra/scripts/dev.js",
"test": "npm run services:up && concurrently --names next,jest --hide next --kill-others --success command-jest \"next dev\" \"jest --runInBand --verbose\"",
Expand All @@ -28,6 +31,7 @@
"dotenv": "16.4.5",
"dotenv-expand": "11.0.6",
"next": "14.2.5",
"next-connect": "1.0.0",
"node-pg-migrate": "7.6.1",
"pg": "8.12.0",
"react": "18.3.1",
Expand Down
76 changes: 43 additions & 33 deletions pages/api/v1/migrations/index.js
Original file line number Diff line number Diff line change
@@ -1,48 +1,58 @@
import migrationRunner from 'node-pg-migrate'
import { resolve } from 'node:path'
import database from 'infra/database'
import { createRouter } from 'next-connect'
import controller from 'infra/controller'

export default async function migrations(request, response) {
const allowedMethods = ['GET', 'POST']
if (!allowedMethods.includes(request.method)) {
return response.status(405).json({
error: `Method '${request.method}' not allowed`
})
const router = createRouter()

router.get(getHandler)
router.post(postHandler)

export default router.handler(controller.errorHandlers)

async function defineMigrationsClientConfig() {
const dbClient = await database.getNewClient()

const defaultMigrationOptions = {
dbClient: dbClient,
dryRun: true,
dir: resolve('infra', 'migrations'),
direction: 'up',
verbose: true,
migrationsTable: 'pgmigrations'
}

let dbClient
return { dbClient, defaultMigrationOptions }
}

async function getHandler(request, response) {
const { dbClient, defaultMigrationOptions } =
await defineMigrationsClientConfig()

try {
dbClient = await database.getNewClient()

const defaultMigrationOptions = {
dbClient: dbClient,
dryRun: true,
dir: resolve('infra', 'migrations'),
direction: 'up',
verbose: true,
migrationsTable: 'pgmigrations'
}
if (request.method === 'GET') {
const pendingMigrations = await migrationRunner(defaultMigrationOptions)
return response.status(200).send(pendingMigrations)
}
const pendingMigrations = await migrationRunner(defaultMigrationOptions)
return response.status(200).send(pendingMigrations)
} finally {
await dbClient.end()
}
}

if (request.method === 'POST') {
const migratedMigrations = await migrationRunner({
...defaultMigrationOptions,
dryRun: false
})
async function postHandler(request, response) {
const { dbClient, defaultMigrationOptions } =
await defineMigrationsClientConfig()

if (migratedMigrations.length > 0) {
return response.status(201).send(migratedMigrations)
}
try {
const migratedMigrations = await migrationRunner({
...defaultMigrationOptions,
dryRun: false
})

return response.status(200).send(migratedMigrations)
if (migratedMigrations.length > 0) {
return response.status(201).send(migratedMigrations)
}
} catch (error) {
console.error(error)
throw error

return response.status(200).send(migratedMigrations)
} finally {
await dbClient.end()
}
Expand Down
89 changes: 42 additions & 47 deletions pages/api/v1/status/index.js
Original file line number Diff line number Diff line change
@@ -1,51 +1,46 @@
import { createRouter } from 'next-connect'
import database from 'infra/database.js'
import { InternalServerError } from 'infra/errors.js'

async function status(request, response) {
try {
const updatedAt = new Date().toISOString()

// Server version
const databaseVersionResult = await database.query('SHOW server_version;')
const databaseVersionValue = databaseVersionResult.rows[0].server_version

// Max connections
const databaseMaxConnectionsResult = await database.query(
'SHOW max_connections;'
)
const databaseMaxConnectionsValue =
databaseMaxConnectionsResult.rows[0].max_connections

// Opened connections
const databaseName = process.env.POSTGRES_DB
const databaseOpenedConnectionsResult = await database.query({
text: 'SELECT COUNT(*)::int FROM pg_stat_activity WHERE datname = $1;',
values: [databaseName]
})
const databaseOpenedConnectionsValue =
databaseOpenedConnectionsResult.rows[0].count

const dependencies = {
database: {
version: databaseVersionValue,
max_connections: parseInt(databaseMaxConnectionsValue),
opened_connections: databaseOpenedConnectionsValue
}
import controller from 'infra/controller'

const router = createRouter()

router.get(getHandler)

export default router.handler(controller.errorHandlers)

async function getHandler(request, response) {
const updatedAt = new Date().toISOString()

// Server version
const databaseVersionResult = await database.query('SHOW server_version;')
const databaseVersionValue = databaseVersionResult.rows[0].server_version

// Max connections
const databaseMaxConnectionsResult = await database.query(
'SHOW max_connections;'
)
const databaseMaxConnectionsValue =
databaseMaxConnectionsResult.rows[0].max_connections

// Opened connections
const databaseName = process.env.POSTGRES_DB
const databaseOpenedConnectionsResult = await database.query({
text: 'SELECT COUNT(*)::int FROM pg_stat_activity WHERE datname = $1;',
values: [databaseName]
})
const databaseOpenedConnectionsValue =
databaseOpenedConnectionsResult.rows[0].count

const dependencies = {
database: {
version: databaseVersionValue,
max_connections: parseInt(databaseMaxConnectionsValue),
opened_connections: databaseOpenedConnectionsValue
}

response.status(200).send({
updated_at: updatedAt,
dependencies
})
} catch (error) {
const publicErrorObject = new InternalServerError({
cause: error
})

console.log('\n Erro dentro do catch do controller:')
console.error(publicErrorObject)
response.status(500).json(publicErrorObject)
}
}

export default status
response.status(200).send({
updated_at: updatedAt,
dependencies
})
}
Loading
Loading