Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
4442154
Adicionadas dependências ao projeto
leonardomunsa Mar 10, 2022
153477b
Criada lógica de extração das infos necessárias do site utilizado
leonardomunsa Mar 10, 2022
5386073
Apropriando código ao prettier
leonardomunsa Mar 10, 2022
32ce74e
Criado documento de configuração para conectar ao sql
leonardomunsa Mar 10, 2022
f065e9f
Criado modelo do banco de dados com tabela para armazenamento das inf…
leonardomunsa Mar 10, 2022
077af2b
Criado arquivo de migração para criação dos campos que armazenam as i…
leonardomunsa Mar 10, 2022
4cc329e
Criando ambiente do node
leonardomunsa Mar 10, 2022
650aba0
Retirando interpretador de json
leonardomunsa Mar 10, 2022
46c5d60
Tentando adicionar ponto e vírgula automaticamente ao final das expre…
leonardomunsa Mar 10, 2022
ba4e39e
Ajustando ambiente Node
leonardomunsa Mar 10, 2022
c43e311
Criado middleware para tratamento de erros
leonardomunsa Mar 10, 2022
69190b5
Utilitário de dicionário com expressões de rota
leonardomunsa Mar 10, 2022
690eff5
Função para middleware de tratamento de erros
leonardomunsa Mar 10, 2022
040656b
Criado arquivo da camada de serviços que possui funções de scrapping …
leonardomunsa Mar 10, 2022
895100a
Criado arquivo na camada de controle que possui função de rota para b…
leonardomunsa Mar 10, 2022
f6dbee4
Adicionada rota ao index
leonardomunsa Mar 10, 2022
0b4dbe9
Erros corrigidos
leonardomunsa Mar 10, 2022
340bb5c
Variável PORT de ambiente adicionada ao index
leonardomunsa Mar 11, 2022
7c75a8d
Apagaos arquivos sequelize que estavam na pasta database pois o seque…
leonardomunsa Mar 11, 2022
61abf23
Arquivo de migração de bancoo de dados criado
leonardomunsa Mar 11, 2022
8c794ae
Index de arquivo da camada de modelo criado
leonardomunsa Mar 11, 2022
4aa74a8
Arquivo da camada de modeloo da tabela Infos Gov criado
leonardomunsa Mar 11, 2022
836b2db
Alterações na função de busca de informações para funcionar da maneir…
leonardomunsa Mar 11, 2022
5ce71b7
NotFound retirado pois não será usado
leonardomunsa Mar 11, 2022
55bc7b1
arquivo de configuração para conexão com banco de dados criado
leonardomunsa Mar 11, 2022
a4d9a60
Comentados links que me ajudaram no código
leonardomunsa Mar 12, 2022
e58dac4
Função de scrapping dos dados chamada quando o servidor inicia
leonardomunsa Mar 20, 2022
f4206d2
Arquivo de configuração json setado para usar banco de dados 'portal_…
leonardomunsa Mar 20, 2022
dc9c91e
Criada nova função para deixar cada qual com uma única responsabilida…
leonardomunsa Mar 20, 2022
1b1285a
Retirando dependências não utilizadas
leonardomunsa Mar 20, 2022
fd26fa0
Arquivo para documentação do projeto e explicação de como rodá-lo e o…
leonardomunsa Mar 20, 2022
75046ec
Merge branch 'main' of https://github.com/leonardomunsa/backend-chall…
leonardomunsa Mar 20, 2022
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
17 changes: 17 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"env": {
"es2021": true,
"node": true
},
"extends": "eslint:recommended",
"parserOptions": {
"ecmaVersion": "latest",
"sourceType": "module"
},
"plugins": [
"prettier"
],
"rules": {
"prettier/prettier": "error"
}
}
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
.idea/
.env
node_modules
package-lock.json
6 changes: 6 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"semi": false,
"singleQuote": true,
"semiColon": true,
"trailingComma": "es5"
}
57 changes: 57 additions & 0 deletions PROJETO.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
## Aplicação back end de raspagem de dados

Uma aplicação back end para raspagem de dados, coletando informações da tabela do Portal da Transparência do Governo Federal e inserindo em um banco
de dados relacional.

Foram usadas as tecnologias:
- Node JS
- MySQL
- Express
- Sequelize
- Puppeteer
- Thunder Client

## Funcionamento da aplicação

A aplicação funciona com o bot do Puppeteer para raspagem de dados, ou seja, para coleta de informações de algum site. Nesse caso, foi escolhida a página https://www.transparencia.gov.br/despesas/orgao?ordenarPor=orgaoSuperior&direcao=asc para alvo da raspagem. O servidor é iniciado e o bot é ativado para fazer a raspagem dos dados da tabela alvo. Se as informações foram coletadas com sucesso, elas são adicionadas ao banco de dados MySQL por meio do Sequelize. Caso algo tenha dado errado, o cliente receberá tal aviso e será guiado para que o erro seja solucionado. Ao fazer a requisição para a rota /api/dados, caso o banco de dados esteja vazio por conta de algum erro na raspagem ao início da aplicação, essa requisição fará a inserção dos dados no banco de dados. Caso esteja tudo certo, a requisição entregará os dados ao usuário.

## Requisitos para rodar o projeto

- Node JS instalado
- MySQL instalado

## Como clonar o repositório e testar o projeto

Clone o projeto do Github :

```sh
$ git clone [email protected]:leonardomunsa/backend-challenge.gitt or git clone https://github.com/leonardomunsa/backend-challenge.git
$ cd backend-challenge
```

#### Para que o banco de dados funcione:

```
- abra o arquivo config.json da pasta config
- edite a variável de senha no objeto development para sua senha usada no usuário do MySQL*
```
A partir disso o projeto está configurado para conectar com o bando de dados a partir do sequelize
*nunca commite esse documento com sua senha, apenas salve para uso do banco de dados

#### Instale as dependências e inicie a aplicação para fazer as requisições:

```sh
$ npm install
$ npx sequelize-cli db:create
$ npx sequelize-cli db:migrate
$ npm start
```

Se estiver tudo ok, a aplicação deve rodar no link:

```bash
http://localhost:8090
```

Teste para ver se está correto, adicione ingredientes ao bando de dados e entre no link http://localhost:8090/api/dados,
o resultado deve ser, em formado json, as informações da tabela do site do Portal de Transparência do Governo.
23 changes: 23 additions & 0 deletions config/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"development": {
"username": "root",
"password": null,
"database": "portal_gov",
"host": "127.0.0.1",
"dialect": "mysql"
},
"test": {
"username": "root",
"password": null,
"database": "portal_gov",
"host": "127.0.0.1",
"dialect": "mysql"
},
"production": {
"username": "root",
"password": null,
"database": "database_production",
"host": "127.0.0.1",
"dialect": "mysql"
}
}
17 changes: 17 additions & 0 deletions controllers/infosgovController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
const { getInfosGov } = require('../services/infosgovService');
const { success } = require('../utils/dictionary');

const getInfosGovController = async (req, res, next) => {
try {
const infos = await getInfosGov();

return res.status(success).json(infos);
} catch (error) {
console.log(`GET INFOS -> ${error.message}`);
next(error);
}
};

module.exports = {
getInfosGovController,
};
27 changes: 27 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
const express = require('express');
require('dotenv').config();
const { getInfosGovController } = require('./controllers/infosgovController');
const { createInfosGov } = require('./services/infosgovService');
const errorHandling = require('./utils/errorHandling');
const { badRequest } = require('./utils/dictionary');

const errorHandler = require('./middlewares/errorHandler');

const app = express();
const PORT = process.env.PORT || 8090;

app.get('/api/dados', getInfosGovController);

app.listen(
PORT,
() => console.log(`App running on port ${PORT}!`),
async () => {
await createInfosGov().catch(() => {
throw errorHandling(badRequest, 'Scrapping failed. Try restarting the server.');
});
}
);

app.use(errorHandler);

module.exports = app;
8 changes: 8 additions & 0 deletions middlewares/errorHandler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module.exports = (err, _req, res, _next) => {
if (err.status) {
return res.status(err.status).json({ message: err.message });
}

console.log(err)
return res.status(500).json({ message: 'Internal Server Error' });
};
52 changes: 52 additions & 0 deletions migrations/20220310204522-create-infosgov.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
'use strict';

module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.createTable('infos_govs', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
mesAno: {
allowNull: false,
type: Sequelize.STRING,
field: 'mes_ano'
},
programaOrcamentario: {
allowNull: false,
type: Sequelize.STRING,
field: 'programa_orcamentario'
},
acaoOrcamentaria: {
allowNull: false,
type: Sequelize.STRING,
field: 'acao_orcamentaria'
},
valorEmpenhado: {
allowNull: false,
type: Sequelize.STRING,
field: 'valor_empenhado'
},
valorLiquidado: {
allowNull: false,
type: Sequelize.STRING,
field: 'valor_liquidado'
},
valorPago: {
allowNull: false,
type: Sequelize.STRING,
field: 'valor_pago'
},
valorRestosAPagarPagos: {
allowNull: false,
type: Sequelize.STRING,
field: 'valor_restos_a_pagar_pagos'
},
});
},
down: async (queryInterface, _Sequelize) => {
await queryInterface.dropTable('infos_gov');
},
};
37 changes: 37 additions & 0 deletions models/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
'use strict';

const fs = require('fs');
const path = require('path');
const Sequelize = require('sequelize');
const basename = path.basename(__filename);
const env = process.env.NODE_ENV || 'development';
const config = require(__dirname + '/../config/config.json')[env];
const db = {};

let sequelize;
if (config.use_env_variable) {
sequelize = new Sequelize(process.env[config.use_env_variable], config);
} else {
sequelize = new Sequelize(config.database, config.username, config.password, config);
}

fs
.readdirSync(__dirname)
.filter(file => {
return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js');
})
.forEach(file => {
const model = require(path.join(__dirname, file))(sequelize, Sequelize.DataTypes);
db[model.name] = model;
});

Object.keys(db).forEach(modelName => {
if (db[modelName].associate) {
db[modelName].associate(db);
}
});

db.sequelize = sequelize;
db.Sequelize = Sequelize;

module.exports = db;
42 changes: 42 additions & 0 deletions models/infosgov.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
module.exports = (sequelize, DataTypes) => {
const InfosGov = sequelize.define(
'InfosGov',
{
mesAno: {
type: DataTypes.STRING,
mesAno: 'mes_ano',
},
programaOrcamentario: {
type: DataTypes.STRING,
programaOrcamentario: 'programa_orcamentario',
},
acaoOrcamentaria: {
type: DataTypes.STRING,
acaoOrcamentaria: 'acao_orcamentaria',
},
valorEmpenhado: {
type: DataTypes.STRING,
valorEmpenhado: 'valor_empenhado'
},
valorLiquidado: {
type: DataTypes.STRING,
valorLiquidado: 'valor_liquidado'
},
valorPago: {
type: DataTypes.STRING,
valorPago: 'valor_pago'
},
valorRestosAPagarPagos: {
type: DataTypes.STRING,
valorRestosAPagarPagos: 'valor_restos_a_pagar_pagos'
},
},
{
timestamps: false,
tablename: 'infos_govs',
underscored: true,
}
);

return InfosGov;
}
33 changes: 33 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"name": "backend-challenge",
"version": "1.0.0",
"description": "O web scraping (raspagem de rede, em tradução livre), também conhecido como extração de dados da web,\r é o nome dado ao processo de coleta de dados estruturados da web de maneira automatizada. Em geral,\r esse método é usado por pessoas, empresas que desejam usar a vasta quantidade de dados da web disponíveis\r publicamente para tomar decisões mais inteligentes.",
"main": "index.js",
"scripts": {
"test": "",
"start": "node index.js"
},
"repository": {
"type": "git",
"url": "git+https://github.com/agilize/backend-challenge.git"
},
"keywords": [],
"author": "",
"license": "ISC",
"bugs": {
"url": "https://github.com/agilize/backend-challenge/issues"
},
"homepage": "https://github.com/agilize/backend-challenge#readme",
"dependencies": {
"dotenv": "^16.0.0",
"express": "^4.17.3",
"mysql2": "^2.3.3",
"puppeteer": "^13.5.0",
"sequelize": "^6.17.0",
"sequelize-cli": "^6.4.1"
},
"devDependencies": {
"eslint": "^8.10.0",
"prettier": "2.5.1"
}
}
Loading