Este projeto consiste em desenvolver todas as camadas de uma API na arquitetura MSC (Models, Services e Controllers).
A API a ser construída trata-se de um sistema de gerenciamento de vendas, onde será possível criar, visualizar, deletar e atualizar produtos e vendas.
Será utilizardo o banco MySQL para a gestão de dados. Além disso, a API sserá RESTful.
Além disso a aplicação irá contar com testes unitários via Chai e Sinon
- Habilidades
- Dependências Necessárias
- Requisitos do projeto
- Linter
- Lista de requisitos
- 1 - Crie um endpoint para o cadastro de produtos
- 2 - Crie um endpoint para listar os produtos
- 3 - Crie um endpoint para atualizar um produto
- 4 - Crie um endpoint para deletar um produto
- 5 - Crie um endpoint para cadastrar vendas
- 6 - Crie um endpoint para listar as vendas
- 7 - Crie um endpoint para atualizar uma venda
- 8 - Escreva testes para cobrir 35% das camadas da sua aplicação
- 9 - Escreva testes para cobrir 40% das camadas da sua aplicação S - 10 - Crie um endpoint para deletar uma venda
- 11 - Atualize a quantidade de produtos
- 12 - Valide a quantidade de produtos
- 13 - Escreva testes para cobrir 50% das camadas da sua aplicação
- 14 - Escreva testes para cobrir 60% das camadas da sua aplicação
Nesse projeto, você será capaz de:
- Entender o funcionamento da camada de Model;
- Delegar responsabilidades específicas para essa camada;
- Conectar sua aplicação com diferentes bancos de dados;
- Estruturar uma aplicação em camadas;
- Delegar responsabilidades específicas para cada parte do seu app;
- Melhorar manutenibilidade e reusabilidade do seu código;
- Entender e aplicar os padrões REST;
- Escrever assinaturas para APIs intuitivas e facilmente entendíveis.
- Instale as dependências
npm install
Atenção
const connection = mysql.createPool({
host: process.env.MYSQL_HOST,
user: process.env.MYSQL_USER,
password: process.env.MYSQL_PASSWORD,
});Para os testes rodarem corretamente, na raiz do projeto renomeie o arquivo .env.example para .env com as variáveis de ambiente. Por exemplo, caso o seu usuário SQL seja nome e a senha 1234 seu arquivo ficará desta forma:
MYSQL_HOST=localhost
MYSQL_USER=nome
MYSQL_PASSWORD=1234
PORT=3000Nota: A variável PORT do arquivo .env deve ser utilizada para a conexão com o servidor. É importante utilizar essa variável para os testes serem executados corretamente tanto na máquina local quanto no avaliador.
Com essas configurações, enquanto estiver na máquina local, o banco será executado normalmente via localhost (possibilitando os testes via npm test).
Como o arquivo .env não será enviado para o GitHub (não se preocupe com isso, pois já está configurado no .gitignore), o avaliador utilizará as suas próprias variáveis de ambiente.
Na raiz do projeto existe o arquivo StoreManager.sql que será usado para rodar os testes. Você pode importá-lo localmente para testar o comportamento da sua aplicação durante o desenvolvimento.
O banco terá três tabelas: products, sales e sales_products.
A tabela products tem o seguinte formato:
(O id será gerado automaticamente)
A tabela sales tem o seguinte formato:
(O id e date são gerados automaticamente)
A tabela sales_products, é a tabela que faz o relacionamento N:N entre products e sales e tem o seguinte formato:
Usaremos o ESLint para fazer a análise estática do seu código.
Este projeto já vem com as dependências relacionadas ao linter configuradas no arquivos package.json.
Para poder rodar os ESLint em um projeto basta executar o comando npm install dentro do projeto e depois npm run lint. Se a análise do ESLint encontrar problemas no seu código, tais problemas serão mostrados no seu terminal. Se não houver problema no seu código, nada será impresso no seu terminal.
Você pode também instalar o plugin do ESLint no VSCode, bastar ir em extensions e baixar o plugin ESLint.
-
O endpoint deve ser acessível através do caminho (
/products); -
Os produtos enviados devem ser salvos na tabela
productsdo Banco de Dados; -
O endpoint deve receber a seguinte estrutura:
{
"name": "product_name",
"quantity": "product_quantity"
}O que será validado
👉 Para o endpoint
POST /products, o camponamedeve ser uma string com 5 ou mais caracteres e deve ser único.
-
Quando a requisição é feita sem o atributo
name:{ "quantity": 100 }- sua API deve responder com status http
400e o seguintebody:
{ "message": "\"name\" is required" } - sua API deve responder com status http
-
Quando a requisição é feita e contém o seguinte
body:{ "name": "pro", "quantity": 100 }- sua API deve responder com status http
422e o seguintebody:
{ "message": "\"name\" length must be at least 5 characters long" } - sua API deve responder com status http
-
Quando a requisição é feita com o atributo
nameigual um já cadastrado:{ "name": "produto", "quantity": 100 }- sua API deve responder com status http
409e o seguintebody:
{ "message": "Product already exists" } - sua API deve responder com status http
👉 Para o endpoint
POST /products, o campoquantitydeve ser um número inteiro maior que 0.
-
Quando a requisição é feita sem o atributo
quantity:{ "name": "produto" }- sua API deve responder com status http
400e o seguintebody:{ "message": "\"quantity\" is required" }
- sua API deve responder com status http
-
Quando a requisição é feita e contém os seguintes
body:{ "name": "produto", "quantity": "string" }{ "name": "produto", "quantity": -1 }{ "name": "produto", "quantity": 0 }- sua API deve responder com status http
422e o seguintebody:
{ "message": "\"quantity\" must be a number larger than or equal to 1" } - sua API deve responder com status http
👉 Para o endpoint
POST /products, quando a requisição é feita corretamente, o produto deve ser cadastrado.
- Quando a requisição é feita e contém o seguinte
body:{ "name": "produto", "quantity": 10 }- sua API deve responder com status http
201e o seguintebody:
{ "id": 1, "name": "produto", "quantity": 10 } - sua API deve responder com status http
-
O endpoint deve ser acessível através do caminho (
/products) ou (/products/:id); -
Através do caminho
/products, todos os produtos devem ser retornados; -
Através do caminho
/products/:id, apenas o produto com oidpresente na URL deve ser retornado;
O que será validado
👉 Para o endpoint
GET /products, será validado que todos produtos estão sendo retornados.
- sua API deve responder com status http
200e o seguintebody:
[
{
"id": 1,
"name": "produto A",
"quantity": 10
},
{
"id": 2,
"name": "produto B",
"quantity": 20
}
]👉 Para o endpoint
GET /products/:id, será validado que é possível listar um determinado produto.
- sua API deve responder com status http
200e o seguintebody:{ "id": 1, "name": "produto A", "quantity": 10 }
👉 Para o endpoint
GET /products/:id, será validado que não é possível listar um produto que não existe.
- sua API deve responder com status http
404e o seguintebody:{ "message": "Product not found" }
-
O endpoint deve ser acessível através do caminho (
/products/:id); -
O corpo da requisição deve seguir a mesma estrutura do método responsável por adicionar um produto;
-
Apenas o produto com o
idpresente na URL deve ser atualizado; -
O corpo da requisição deve receber a seguinte estrutura:
{
"name": "new_product_name",
"quantity": "new_product_quantity"
}O que será validado
👉 Para o endpoint
PUT /products/:id, o camponamedeve ser uma string com 5 ou mais caracteres e deve ser único.
- Quando a requisição é feita e contém o seguinte
body:{ "name": "pro", "quantity": 15 }- sua API deve responder com status http
422e o seguintebody:
{ "message": "\"name\" length must be at least 5 characters long" } - sua API deve responder com status http
👉 Para o endpoint
PUT /products/:id, o campoquantitydeve ser um número inteiro maior que 0.
- Quando a requisição é feita e contém os seguintes
body:{ "name": "produto", "quantity": "string" }{ "name": "produto", "quantity": -1 }{ "name": "produto", "quantity": 0 }- sua API deve responder com status http
422e o seguintebody:
{ "message": "\"quantity\" must be a number larger than or equal to 1" } - sua API deve responder com status http
👉 Para o endpoint
PUT /products/:id, quando a requisição é feita corretamente, o produto deve ser alterado.
- Quando a requisição é feita e contém o seguinte
body:{ "name": "produto", "quantity": 15 }- sua API deve responder com status http
200e o seguintebody:
{ "id": 1, "name": "produto", "quantity": 15 } - sua API deve responder com status http
👉 Para o endpoint
PUT /products/:id, será validado que não é possível alterar um produto que não existe.
- sua API deve responder com status http
404e o seguintebody:{ "message": "Product not found" }
-
O endpoint deve ser acessível através do caminho (
/products/:id); -
Apenas o produto com o
idpresente na URL deve ser deletado;
O que será validado
👉 Para o endpoint
DELETE /products/:id, será validado que é possível deletar um produto com sucesso.
- sua API deve responder com status http
200e o seguintebody:
{
"id": 1,
"name": "produto A",
"quantity": 10
}👉 Para o endpoint
DELETE /products/:id, será validado que não é possível deletar um produto que não existe.
- sua API deve responder com status http
404e o seguintebody:{ "message": "Product not found" }
-
O endpoint deve ser acessível através do caminho (
/sales); -
As vendas enviadas devem ser salvas na tabela
salesesales_productsdo Banco de dados; -
Deve ser possível cadastrar a venda de vários produtos através da uma mesma requisição;
-
O endpoint deve receber a seguinte estrutura:
[
{
"product_id": "product_id",
"quantity": "product_quantity",
}
]O que será validado
👉 Para o endpoint
POST /sales, o campoproduct_iddeve ser um id de um produto anteriormente cadastrado.
- Quando a requisição é feita sem o atributo
product_id:[ { "quantity": 1 } ]- sua API deve responder com status http
400e o seguintebody:
{ "message": "\"product_id\" is required" } - sua API deve responder com status http
👉 Para o endpoint
POST /sales, o campoquantitydeve ser um número inteiro maior que 0.
-
Quando a requisição é feita sem o atributo
quantity:[ { "product_id": 1 } ]- sua API deve responder com status http
400e o seguintebody:{ "message": "\"quantity\" is required" }
- sua API deve responder com status http
-
Quando a requisição é feita e contém os seguintes
body:[ { "product_id": 1, "quantity": -1 } ][ { "product_id": 1, "quantity": 0 } ][ { "product_id": 1, "quantity": "string" } ]- sua API deve responder com status http
422e o seguintebody:
{ "message": "\"quantity\" must be a number larger than or equal to 1" } - sua API deve responder com status http
👉 Para o endpoint
POST /sales, quando a requisição é feita corretamente, o produto deve ser cadastrado.
- Quando a requisição é feita e contém o seguinte
body:[ { "product_id": 1, "quantity": 3 } ]- sua API deve responder com status http
201e o seguintebody:
{ "id": 1, "itemsSold": [ { "product_id": 1, "quantity": 3 } ] } - sua API deve responder com status http
👉 Para o endpoint
POST /sales, quando a requisição é feita corretamente, a venda deve ser cadastrada.
- Quando a requisição é feita e contém o seguinte
body:[ { "product_id": 1, "quantity": 2 }, { "product_id": 2, "quantity": 5 } ]- sua API deve responder com status http
201e o seguintebody:
{ "id": 1, "itemsSold": [ { "product_id": 1, "quantity": 2 }, { "product_id": 2, "quantity": 5 } ] } - sua API deve responder com status http
-
O endpoint deve ser acessível através do caminho (
/sales) ou (/sales/:id); -
Através do caminho
/sales, todas as vendas devem ser retornadas; -
Através do caminho
/sales/:id, apenas a venda com oidpresente na URL deve ser retornada;
O que será validado
👉 Para o endpoint
GET /sales, será validado que todas vendas estão sendo retornados.
- sua API deve responder com status http
200e o seguintebody:
[
{
"saleId": 1,
"date": "2021-09-09T04:54:29.000Z",
"product_id": 1,
"quantity": 2
},
{
"saleId": 1,
"date": "2021-09-09T04:54:54.000Z",
"product_id": 2,
"quantity": 2
}
]👉 Para o endpoint
GET /sales/:id, será validado que é possível listar uma determinada venda.
- sua API deve responder com status http
200e o seguintebody:[ { "date": "2021-09-09T04:54:29.000Z", "product_id": 1, "quantity": 2 }, { "date": "2021-09-09T04:54:54.000Z", "product_id": 2, "quantity": 2 } ]
👉 Para o endpoint
GET /sales/:id, será validado que não é possível listar uma venda que não existe.
- sua API deve responder com status http
404e o seguintebody:{ "message": "Sale not found" }
-
O endpoint deve ser acessível através do caminho (
/sales/:id); -
quantitydeve ser um número inteiro maior que 0; -
Apenas a venda com o
idpresente na URL deve ser atualizada; -
O corpo da requisição deve receber a seguinte estrutura:
[
{
"product_id": "id_change",
"quantity": "new_quantity"
}
]O que será validado
👉 Para o endpoint
PUT /sales/:id, o campoproduct_iddeve ser um id de um produto anteriormente cadastrado.
- Quando a requisição é feita sem o atributo
product_id:[ { "quantity": 10 } ]- sua API deve responder com status http
400e o seguintebody:
{ "message": "\"product_id\" is required" } - sua API deve responder com status http
👉 Para o endpoint
PUT /sales/:id, o campoquantitydeve ser um número inteiro maior que 0.
-
Quando a requisição é feita sem o atributo
quantity:[ { "product_id": 1 } ]- sua API deve responder com status http
400e o seguintebody:
{ "message": "\"quantity\" is required" } - sua API deve responder com status http
-
Quando a requisição é feita e contém os seguintes
body:[ { "product_id": 1, "quantity": -1 } ][ { "product_id": 1, "quantity": 0 } ][ { "product_id": 1, "quantity": "string" } ]- sua API deve responder com status http
422e o seguintebody:
{ "message": "\"quantity\" must be a number larger than or equal to 1" } - sua API deve responder com status http
👉 Para o endpoint
PUT /sales/:id, quando a requisição é feita corretamente, a venda deve ser alterada.
- Quando a requisição é feita e contém o seguinte
body:[ { "product_id": 1, "quantity": 6 } ]- sua API deve responder com status http
200e o seguintebody:
{ "saleId": 1, "itemUpdated": [ { "product_id": 1, "quantity": 6 } ] } - sua API deve responder com status http
-
Seus arquivos de teste devem ficar no diretório
test/unit, como citado aqui; -
Seus testes da
modeldevem fazer mock do banco de dados obrigatóriamente; -
Opcionalmente você pode parar o serviço do
MYSQLem sua máquina. Para rodar seus teste utilizenpm run test:mocha;
O que será validado
👉 Será validado que a cobertura total das linhas dos arquivos nas pastas
models,servicesecontrollersé maior ou igual a 35%.
👉 Será validado que ao menos 24 linhas são cobertas pelos testes.
-
Seus arquivos de teste devem ficar no diretório
test/unit, como citado aqui -
Seus testes da
modeldevem fazer mock do banco de dados obrigatóriamente; -
Opcionalmente você pode parar o serviço do
MYSQLem sua máquina. Para rodar seus teste utilizenpm run test:mocha;
O que será validado
👉 Será validado que a cobertura total das linhas dos arquivos nas pastas
models,servicesecontrollersé maior ou igual a 40%.
👉 Será validado que ao menos 24 linhas são cobertas pelos testes.
-
O endpoint deve ser acessível através do caminho (
/sales/:id); -
Apenas a venda com o
idpresente na URL deve ser deletado;
O que será validado
👉 Para o endpoint
DELETE /sales/:id, será validado que é possível deletar uma venda com sucesso.
- sua API deve responder com status http
200e o seguintebody:
[
{
"date": "2021-09-09T04:54:29.000Z",
"product_id": 1,
"quantity": 2
},
{
"date": "2021-09-09T04:54:54.000Z",
"product_id": 2,
"quantity": 2
}
] 👉 Para o endpoint
DELETE /sales/:id, será validado que não é possível deletar uma venda que não existe.
- sua API deve responder com status http
404e o seguintebody:
{ "message": "Sale not found" } -
Ao realizar uma venda, atualizá-la ou deletá-la, você deve também atualizar a quantidade do produto em questão presente na tabela responsável pelos produtos;
- Exemplo 1: suponha que haja um produto chamado Bola de Futebol e a sua propriedade
quantitytenha o valor 10. Caso seja feita uma venda com 8 unidades desse produto, a quantidade do produto deve ser atualizada para 2 , pois 10 - 8 = 2; - Exemplo 2: Suponha que esta venda tenha sido deletada, logo estas 8 unidades devem voltar ao
quantitye seu valor voltará a 10, pois 2 + 8 = 10;
- Exemplo 1: suponha que haja um produto chamado Bola de Futebol e a sua propriedade
O que será validado
👉 Será validado que ao fazer uma determinada venda, a quantidade do produto deverá ser atualizada também na tabela responsável pelos produtos.
👉 Será validado que ao deletar uma determinada venda, a quantidade do produto deverá ser atualizada também na tabela responsável pelos produtos;.
-
Um produto nunca deve ter a quantidade em estoque menor que 0;
-
Quando uma venda for realizada, garanta que a quantidade sendo vendida está disponível no estoque
O que será validado
👉 Para o endpoint
POST /sales, será validado que a quantidade de produtos em estoque nunca seja menor que 0 (zero).
- Quando a requisição é feita com uma quantidade superior a quantidade em estoque:
[ { "product_id": 1, "quantity": 100 } ]- sua API deve responder com status http
422e o seguintebody:
{ "message": "Such amount is not permitted to sell" } - sua API deve responder com status http
-
Seus arquivos de teste devem ficar no diretório
test/unit, como citado aqui; -
Seus testes da
modeldevem fazer mock do banco de dados obrigatóriamente; -
Opcionalmente você pode parar o serviço do
MYSQLem sua máquina. Para rodar seus teste utilizenpm run test:mocha;
O que será validado
👉 Será validado que a cobertura total das linhas dos arquivos nas pastas
models,servicesecontrollersé maior ou igual a 50%.
👉 Será validado que ao menos 24 linhas são cobertas pelos testes.
-
Seus arquivos de teste devem ficar no diretório
test/unit, como citado aqui; -
Seus testes da
modeldevem fazer mock do banco de dados obrigatóriamente; -
Opcionalmente você pode parar o serviço do
MYSQLem sua máquina. Para rodar seus teste utilizenpm run test:mocha;
O que será validado
👉 Será validado que a cobertura total das linhas dos arquivos nas pastas
models,servicesecontrollersé maior ou igual a 60%.
👉 Será validado que ao menos 24 linhas são cobertas pelos testes.


