Skip to content

API RESTful em Node.js que simula uma loja online, usa o MySQL para gestão de dados e CHAI para testes

Notifications You must be signed in to change notification settings

WalmirLucena/store-manager

Repository files navigation

Projeto Store Manager

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


Sumário


Habilidades

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.

Dependências necessárias

Antes de começar a desenvolver

  1. Instale as dependências
  • npm install

Atenção ⚠️ Não rode o comando npm audit fix! Ele atualiza várias dependências do projeto, e essa atualização gera conflitos com o avaliador.

Conexão com o Banco:

⚠️ IMPORTANTE! ⚠️

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=3000

Nota: 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.

Tabelas

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:

Tabela Produtos

(O id será gerado automaticamente)

A tabela sales tem o seguinte formato:

Tabela Vendas

(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:

Tabela Vendas-Produtos

Requisitos do projeto

Linter

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.


Lista de requisitos

1 - Crie um endpoint para o cadastro de produtos

  • O endpoint deve ser acessível através do caminho (/products);

  • Os produtos enviados devem ser salvos na tabela products do 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 campo name deve 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 400 e o seguinte body:
      { "message": "\"name\" is required" }          
  • Quando a requisição é feita e contém o seguinte body:

      { "name": "pro", "quantity": 100 }
    • sua API deve responder com status http 422 e o seguinte body:
      { "message": "\"name\" length must be at least 5 characters long" }          
  • Quando a requisição é feita com o atributo name igual um já cadastrado:

      { "name": "produto", "quantity": 100 }
    • sua API deve responder com status http 409 e o seguinte body:
      { "message": "Product already exists" }          

👉 Para o endpoint POST /products, o campoquantity deve 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 400 e o seguinte body:
        { "message": "\"quantity\" is required" }          
  • 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 422 e o seguinte body:
      { "message": "\"quantity\" must be a number larger than or equal to 1" }           

👉 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 201 e o seguinte body:
      { "id": 1, "name": "produto", "quantity": 10 }          

2 - Crie um endpoint para listar os produtos

  • 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 o id presente 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 200 e o seguinte body:
  [
    {
      "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 200 e o seguinte body:
      {
        "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 404 e o seguinte body:
      { "message": "Product not found" }

3 - Crie um endpoint para atualizar um produto

  • 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 id presente 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 campo name deve 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 422 e o seguinte body:
      { "message": "\"name\" length must be at least 5 characters long" }          

👉 Para o endpoint PUT /products/:id, o campoquantity deve 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 422 e o seguinte body:
      { "message": "\"quantity\" must be a number larger than or equal to 1" }           

👉 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 200 e o seguinte body:
      { "id": 1, "name": "produto", "quantity": 15 }          

👉 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 404 e o seguinte body:
      { "message": "Product not found" }

4 - Crie um endpoint para deletar um produto

  • O endpoint deve ser acessível através do caminho (/products/:id);

  • Apenas o produto com o id presente 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 200 e o seguinte body:
  {
    "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 404 e o seguinte body:
      { "message": "Product not found" }

5 - Crie um endpoint para cadastrar vendas

  • O endpoint deve ser acessível através do caminho (/sales);

  • As vendas enviadas devem ser salvas na tabela sales e sales_products do 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 campo product_id deve 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 400 e o seguinte body:
      { "message": "\"product_id\" is required" }          

👉 Para o endpoint POST /sales, o campoquantity deve 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 400 e o seguinte body:
        { "message": "\"quantity\" is required" }          
  • 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 422 e o seguinte body:
      { "message": "\"quantity\" must be a number larger than or equal to 1" }           

👉 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 201 e o seguinte body:
      {
        "id": 1,
        "itemsSold": [
          {
            "product_id": 1,
            "quantity": 3
          }
        ]
      }          

👉 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 201 e o seguinte body:
      {
        "id": 1,
        "itemsSold": [
          {
            "product_id": 1,
            "quantity": 2
          },
          {
            "product_id": 2,
            "quantity": 5
          }
        ]
      }          

6 - Crie um endpoint para listar as vendas

  • 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 o id presente 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 200 e o seguinte body:
  [
    {
      "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 200 e o seguinte body:
      [
        { 
          "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 404 e o seguinte body:
      { "message": "Sale not found" }

7 - Crie um endpoint para atualizar uma venda

  • O endpoint deve ser acessível através do caminho (/sales/:id);

  • quantity deve ser um número inteiro maior que 0;

  • Apenas a venda com o id presente 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 campo product_id deve 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 400 e o seguinte body:
      { "message": "\"product_id\" is required" }          

👉 Para o endpoint PUT /sales/:id, o campo quantity deve 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 400 e o seguinte body:
      { "message": "\"quantity\" is required" }          
  • 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 422 e o seguinte body:
      { "message": "\"quantity\" must be a number larger than or equal to 1" }           

👉 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 200 e o seguinte body:
      {
        "saleId": 1,
        "itemUpdated": [
          {
            "product_id": 1,
            "quantity": 6
          }
        ]
      }        

8 - Escreva testes para cobrir 35% das camadas da sua aplicação

  • Seus arquivos de teste devem ficar no diretório test/unit, como citado aqui;

  • Seus testes da model devem fazer mock do banco de dados obrigatóriamente;

  • Opcionalmente você pode parar o serviço do MYSQL em sua máquina. Para rodar seus teste utilize npm run test:mocha;

O que será validado

👉 Será validado que a cobertura total das linhas dos arquivos nas pastas models, services e controllers é maior ou igual a 35%.

👉 Será validado que ao menos 24 linhas são cobertas pelos testes.


9 - Escreva testes para cobrir 40% das camadas da sua aplicação

  • Seus arquivos de teste devem ficar no diretório test/unit, como citado aqui

  • Seus testes da model devem fazer mock do banco de dados obrigatóriamente;

  • Opcionalmente você pode parar o serviço do MYSQL em sua máquina. Para rodar seus teste utilize npm run test:mocha;

O que será validado

👉 Será validado que a cobertura total das linhas dos arquivos nas pastas models, services e controllers é maior ou igual a 40%.

👉 Será validado que ao menos 24 linhas são cobertas pelos testes.


10 - Crie um endpoint para deletar uma venda

  • O endpoint deve ser acessível através do caminho (/sales/:id);

  • Apenas a venda com o id presente 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 200 e o seguinte body:
  [
    { 
      "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 404 e o seguinte body:
  { "message": "Sale not found" }          

11 - Atualize a quantidade de produtos

  • 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 quantity tenha 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 quantity e seu valor voltará a 10, pois 2 + 8 = 10;
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;.


12 - Valide a quantidade de 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 422 e o seguinte body:
      { "message": "Such amount is not permitted to sell" }          

13 - Escreva testes para cobrir 50% das camadas da sua aplicação

  • Seus arquivos de teste devem ficar no diretório test/unit, como citado aqui;

  • Seus testes da model devem fazer mock do banco de dados obrigatóriamente;

  • Opcionalmente você pode parar o serviço do MYSQL em sua máquina. Para rodar seus teste utilize npm run test:mocha;

O que será validado

👉 Será validado que a cobertura total das linhas dos arquivos nas pastas models, services e controllers é maior ou igual a 50%.

👉 Será validado que ao menos 24 linhas são cobertas pelos testes.


14 - Escreva testes para cobrir 60% das camadas da sua aplicação

  • Seus arquivos de teste devem ficar no diretório test/unit, como citado aqui;

  • Seus testes da model devem fazer mock do banco de dados obrigatóriamente;

  • Opcionalmente você pode parar o serviço do MYSQL em sua máquina. Para rodar seus teste utilize npm run test:mocha;

O que será validado

👉 Será validado que a cobertura total das linhas dos arquivos nas pastas models, services e controllers é maior ou igual a 60%.

👉 Será validado que ao menos 24 linhas são cobertas pelos testes.


About

API RESTful em Node.js que simula uma loja online, usa o MySQL para gestão de dados e CHAI para testes

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published