Skip to content

Commit 7dfb378

Browse files
authored
Merge pull request #34 from alissonpef/custom-erros
Padronização dos Controllers
2 parents 4078f4f + 753cf01 commit 7dfb378

8 files changed

Lines changed: 203 additions & 80 deletions

File tree

infra/controller.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { InternalServerError, MethodNotAllowedError } from "infra/errors";
2+
3+
function onNoMatchHandler(request, response) {
4+
const publicErrorObject = new MethodNotAllowedError();
5+
response.status(publicErrorObject.statusCode).json(publicErrorObject);
6+
}
7+
8+
function onErrorHandler(error, request, response) {
9+
const publicErrorObject = new InternalServerError({
10+
statusCode: error.statusCode,
11+
cause: error,
12+
});
13+
14+
console.error(publicErrorObject);
15+
16+
response.status(publicErrorObject.statusCode).json(publicErrorObject);
17+
}
18+
19+
const controller = {
20+
errorHandlers: {
21+
onNoMatch: onNoMatchHandler,
22+
onError: onErrorHandler,
23+
},
24+
};
25+
26+
export default controller;

infra/database.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { Client } from "pg";
2+
import { ServiceError } from "./errors.js";
23

34
async function query(queryObject) {
45
let client;
@@ -7,9 +8,11 @@ async function query(queryObject) {
78
const result = await client.query(queryObject);
89
return result;
910
} catch (error) {
10-
console.log("\n Erro dentro do catch do database.js:");
11-
console.error(error);
12-
throw error;
11+
const serviceErrorObject = new ServiceError({
12+
message: "Erro na conexão com Banco ou na Query.",
13+
cause: error,
14+
});
15+
throw serviceErrorObject;
1316
} finally {
1417
await client?.end();
1518
}

infra/errors.js

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,50 @@
11
export class InternalServerError extends Error {
2-
constructor({ cause }) {
2+
constructor({ cause, statusCode }) {
33
super("Um erro interno não esperado aconteceu.", {
44
cause,
55
});
66
this.name = "InternalServerError";
77
this.action = "Entre em contato com o suporte.";
8-
this.statusCode = 500;
8+
this.statusCode = statusCode || 500;
9+
}
10+
11+
toJSON() {
12+
return {
13+
name: this.name,
14+
message: this.message,
15+
action: this.action,
16+
status_code: this.statusCode,
17+
};
18+
}
19+
}
20+
21+
export class ServiceError extends Error {
22+
constructor({ cause, message }) {
23+
super(message || "Serviço indisponível no momento.", {
24+
cause,
25+
});
26+
this.name = "ServiceError";
27+
this.action = "Verifique se o serviço está disponível.";
28+
this.statusCode = 503;
29+
}
30+
31+
toJSON() {
32+
return {
33+
name: this.name,
34+
message: this.message,
35+
action: this.action,
36+
status_code: this.statusCode,
37+
};
38+
}
39+
}
40+
41+
export class MethodNotAllowedError extends Error {
42+
constructor() {
43+
super("Método não permitido para este endpoint.");
44+
this.name = "MethodNotAllowedError";
45+
this.action =
46+
"Verifique se o método HTTP enviado é válido para este endpoint.";
47+
this.statusCode = 405;
948
}
1049

1150
toJSON() {

package-lock.json

Lines changed: 26 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
"dotenv": "16.4.4",
2828
"dotenv-expand": "11.0.6",
2929
"next": "13.1.6",
30+
"next-connect": "1.0.0",
3031
"node-pg-migrate": "6.2.2",
3132
"pg": "8.11.3",
3233
"react": "18.2.0",

pages/api/v1/migrations/index.js

Lines changed: 40 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,57 @@
1+
import { createRouter } from "next-connect";
12
import migrationRunner from "node-pg-migrate";
23
import { resolve } from "node:path";
34
import database from "infra/database.js";
5+
import controller from "infra/controller.js";
46

5-
export default async function migrations(request, response) {
6-
const allowedMethods = ["GET", "POST"];
7-
if (!allowedMethods.includes(request.method)) {
8-
return response.status(405).json({
9-
error: `Method "${request.method}" not allowed`,
10-
});
11-
}
7+
const router = createRouter();
8+
9+
router.get(getHandler);
10+
router.post(postHandler);
11+
12+
export default router.handler(controller.errorHandlers);
13+
14+
const defaultMigrationOptions = {
15+
dryRun: true,
16+
dir: resolve("infra", "migrations"),
17+
direction: "up",
18+
verbose: true,
19+
migrationsTable: "pgmigrations",
20+
};
1221

22+
async function getHandler(request, response) {
1323
let dbClient;
1424

1525
try {
1626
dbClient = await database.getNewClient();
1727

18-
const defaultMigrationOptions = {
19-
dbClient: dbClient,
20-
dryRun: true,
21-
dir: resolve("infra", "migrations"),
22-
direction: "up",
23-
verbose: true,
24-
migrationsTable: "pgmigrations",
25-
};
26-
27-
if (request.method === "GET") {
28-
const pendingMigrations = await migrationRunner(defaultMigrationOptions);
29-
return response.status(200).json(pendingMigrations);
30-
}
28+
const pendingMigrations = await migrationRunner({
29+
...defaultMigrationOptions,
30+
dbClient,
31+
});
32+
return response.status(200).json(pendingMigrations);
33+
} finally {
34+
await dbClient.end();
35+
}
36+
}
37+
38+
async function postHandler(request, response) {
39+
let dbClient;
3140

32-
if (request.method === "POST") {
33-
const migratedMigrations = await migrationRunner({
34-
...defaultMigrationOptions,
35-
dryRun: false,
36-
});
41+
try {
42+
dbClient = await database.getNewClient();
3743

38-
if (migratedMigrations.length > 0) {
39-
return response.status(201).json(migratedMigrations);
40-
}
44+
const migratedMigrations = await migrationRunner({
45+
...defaultMigrationOptions,
46+
dbClient,
47+
dryRun: false,
48+
});
4149

42-
return response.status(200).json(migratedMigrations);
50+
if (migratedMigrations.length > 0) {
51+
return response.status(201).json(migratedMigrations);
4352
}
44-
} catch (error) {
45-
console.error(error);
46-
throw error;
53+
54+
return response.status(200).json(migratedMigrations);
4755
} finally {
4856
await dbClient.end();
4957
}

pages/api/v1/status/index.js

Lines changed: 37 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,41 @@
1+
import { createRouter } from "next-connect";
12
import database from "infra/database";
2-
import { InternalServerError } from "infra/errors";
3-
4-
async function status(request, response) {
5-
try {
6-
const updatedAt = new Date().toISOString();
7-
8-
const databaseVersionResult = await database.query("SHOW server_version;");
9-
const databaseVersionValue = databaseVersionResult.rows[0].server_version;
10-
11-
const databaseMaxConnectionsResult = await database.query(
12-
"SHOW max_connections;"
13-
);
14-
const databaseMaxConnectionsValue =
15-
databaseMaxConnectionsResult.rows[0].max_connections;
16-
17-
const databaseName = process.env.POSTGRES_DB;
18-
const databaseOpenedConnectionsResult = await database.query({
19-
text: "SELECT count(*)::int FROM pg_stat_activity WHERE datname = $1;",
20-
values: [databaseName],
21-
});
22-
const databaseOpenedConnectionsValue =
23-
databaseOpenedConnectionsResult.rows[0].count;
24-
25-
response.status(200).json({
26-
updated_at: updatedAt,
27-
dependencies: {
28-
database: {
29-
version: databaseVersionValue,
30-
max_connections: parseInt(databaseMaxConnectionsValue),
31-
opened_connections: databaseOpenedConnectionsValue,
32-
},
33-
},
34-
});
35-
} catch (error) {
36-
const publicErrorObject = new InternalServerError({
37-
cause: error,
38-
});
3+
import controller from "infra/controller.js";
394

40-
console.log("\n Erro dentro do catch do controller:");
41-
console.error(publicErrorObject);
5+
const router = createRouter();
426

43-
response.status(500).json(publicErrorObject);
44-
}
45-
}
7+
router.get(getHandler);
8+
9+
export default router.handler(controller.errorHandlers);
10+
11+
async function getHandler(request, response) {
12+
const updatedAt = new Date().toISOString();
13+
14+
const databaseVersionResult = await database.query("SHOW server_version;");
15+
const databaseVersionValue = databaseVersionResult.rows[0].server_version;
4616

47-
export default status;
17+
const databaseMaxConnectionsResult = await database.query(
18+
"SHOW max_connections;"
19+
);
20+
const databaseMaxConnectionsValue =
21+
databaseMaxConnectionsResult.rows[0].max_connections;
22+
23+
const databaseName = process.env.POSTGRES_DB;
24+
const databaseOpenedConnectionsResult = await database.query({
25+
text: "SELECT count(*)::int FROM pg_stat_activity WHERE datname = $1;",
26+
values: [databaseName],
27+
});
28+
const databaseOpenedConnectionsValue =
29+
databaseOpenedConnectionsResult.rows[0].count;
30+
31+
response.status(200).json({
32+
updated_at: updatedAt,
33+
dependencies: {
34+
database: {
35+
version: databaseVersionValue,
36+
max_connections: parseInt(databaseMaxConnectionsValue),
37+
opened_connections: databaseOpenedConnectionsValue,
38+
},
39+
},
40+
});
41+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import orchestrator from "tests/orchestrator.js";
2+
3+
beforeAll(async () => {
4+
await orchestrator.waitForAllServices();
5+
});
6+
7+
describe("POST /api/v1/status", () => {
8+
describe("Anonymous user", () => {
9+
test("Retrieving current system status", async () => {
10+
const response = await fetch("http://localhost:3000/api/v1/status", {
11+
method: "POST",
12+
});
13+
expect(response.status).toBe(405);
14+
15+
const responseBody = await response.json();
16+
17+
expect(responseBody).toEqual({
18+
name: "MethodNotAllowedError",
19+
message: "Método não permitido para este endpoint.",
20+
action:
21+
"Verifique se o método HTTP enviado é válido para este endpoint.",
22+
status_code: 405,
23+
});
24+
});
25+
});
26+
});

0 commit comments

Comments
 (0)