/trybesmith

API Node Express utilizando o Typescript

Primary LanguageTypeScript

Termos e acordos

Ao iniciar este projeto, você concorda com as diretrizes do Código de Ética e Conduta e do Manual da Pessoa Estudante da Trybe.

Boas vindas ao repositório do projeto Trybesmith!

Você já usa o GitHub diariamente para desenvolver os exercícios, certo? Agora, para desenvolver os projetos, você deverá seguir as instruções a seguir. Tenha atenção a cada passo, e se tiver qualquer dúvida, nos envie por Slack! #vqv 🚀

Aqui você vai encontrar os detalhes de como estruturar o desenvolvimento do seu projeto a partir deste repositório, utilizando uma branch específica e um Pull Request para colocar seus códigos.


Sumário


Habilidades

Neste projeto, você será capaz de:

  • Declarar variáveis e funções com tipagens Typescript;

  • Construir uma API Node Express utilizando o Typescript.


Entregáveis

Para entregar o seu projeto você deverá criar um Pull Request neste repositório.

Lembre-se que você pode consultar nosso conteúdo sobre Git & GitHub sempre que precisar!


O que deverá ser desenvolvido

Para este projeto, você vai desenvolver um CRUD (Create, Read, Update e Delete) de itens medievais, no formato de uma API, utilizando Typescript.

Você irá criar alguns endpoints que irão ler e escrever em um banco de dados, utilizando o MySQL.


Desenvolvimento

Você vai desenvolver todas as camadas da aplicação (Models, Service e Controllers) em seu código e, por meio dessa aplicação, será possível realizar as operações básicas que se pode fazer em um determinado banco de dados: Criação, Leitura, Atualização e Exclusão (ou CRUD, para as pessoas mais íntimas 😜).

O código para cadastro de pessoas usuárias deve ser criado por você utilizando os conhecimentos adquiridos nesse bloco.

⚠️ Dicas Importantes ⚠️:

  • Não haverá front-end neste projeto, portanto não se preocupe com a visualização, apenas com as funcionalidades e organização do código.

  • Sua API deve ser desenvolvida dentro da pasta ./src;


Data de Entrega

  • Serão 2 dias de projeto.
  • Data de entrega para avaliação final do projeto: 04/03/2022 14:00.

Instruções para entregar seu projeto

Antes de começar a desenvolver

  1. Clone o repositório
  • git clone https://github.com/tryber/sd-014-a-project-trybesmith.git.
  • Entre na pasta do repositório que você acabou de clonar:
    • cd sd-014-a-project-trybesmith
  1. Instale as dependências [Caso existam]
  • npm install
  1. Crie uma branch a partir da branch main
  • Verifique que você está na branch main
    • Exemplo: git branch
  • Se não estiver, mude para a branch main
    • Exemplo: git checkout main
  • Agora crie uma branch à qual você vai submeter os commits do seu projeto
    • Você deve criar uma branch no seguinte formato: nome-de-usuario-nome-do-projeto
    • Exemplo: git checkout -b joaozinho-sd-014-a-project-trybesmith
  1. Adicione as mudanças ao stage do Git e faça um commit
  • Verifique que as mudanças ainda não estão no stage
    • Exemplo: git status (deve aparecer listada a pasta joaozinho em vermelho)
  • Adicione o novo arquivo ao stage do Git
    • Exemplo:
      • git add . (adicionando todas as mudanças - que estavam em vermelho - ao stage do Git)
      • git status (deve aparecer listado o arquivo joaozinho/README.md em verde)
  • Faça o commit inicial
    • Exemplo:
      • git commit -m 'iniciando o projeto x' (fazendo o primeiro commit)
      • git status (deve aparecer uma mensagem tipo nothing to commit )
  1. Adicione a sua branch com o novo commit ao repositório remoto
  • Usando o exemplo anterior: git push -u origin joaozinho-sd-014-a-project-trybesmith
  1. Crie um novo Pull Request (PR)
  • Vá até a página de Pull Requests do repositório no GitHub
  • Clique no botão verde "New pull request"
  • Clique na caixa de seleção "Compare" e escolha a sua branch com atenção
  • Clique no botão verde "Create pull request"
  • Adicione uma descrição para o Pull Request e clique no botão verde "Create pull request"
  • Não se preocupe em preencher mais nada por enquanto!
  • Volte até a página de Pull Requests do repositório e confira que o seu Pull Request está criado

Durante o desenvolvimento

  • Faça commits das alterações que você fizer no código regularmente.

  • Lembre-se de sempre após um (ou alguns) commits atualizar o repositório remoto.

  • Os comandos que você utilizará com mais frequência são:

    1. git status (para verificar o que está em vermelho - fora do stage - e o que está em verde - no stage)
    2. git add (para adicionar arquivos ao stage do Git)
    3. git commit (para criar um commit com os arquivos que estão no stage do Git)
    4. git push -u nome-da-branch (para enviar o commit para o repositório remoto na primeira vez que fizer o push de uma nova branch)
    5. git push (para enviar o commit para o repositório remoto após o passo anterior)

Como desenvolver

⚠️ Leia-os atentamente e siga à risca o que for pedido. ⚠️

👀 Observações importantes:

  • O não cumprimento de um requisito, total ou parcialmente, impactará em sua avaliação;
  • O projeto deve rodar na porta 3000;
  • O arquivo index.ts existe para rodar corretamente os testes. Todo o projeto (incluindo as rotas) deverá ser feito dentro do arquivo app.ts;
  • Você pode utilizar as funções json.parse e json.stringify nos models;

Todos os seus endpoints devem estar no padrão REST

  • Use os verbos HTTP adequados para cada operação.

  • Agrupe e padronize suas URL em cada recurso.

  • Garanta que seus endpoints sempre retornem uma resposta, havendo sucesso nas operações ou não.

  • Retorne os códigos de status corretos (recurso criado, erro de validação, etc).


Há dois arquivos no diretório ./src/: index.ts e app.ts, ambos não devem ser renomeados ou apagados.

Você poderá fazer modificações em ambos os arquivos, porém no arquivo app.ts o seguinte trecho de código não deve ser removido:

import express from 'express';

const app = express();

app.use(express.json());

export default app;

Isso está configurado para o avaliador funcionar corretamente.

Conexão com o Banco

A conexão do banco local deverá conter os seguintes parâmetros:

import dotenv from 'dotenv';
import mysql from 'mysql2/promise';

dotenv.config();

const connection = mysql.createPool({
  host: process.env.MYSQL_HOST,
  user: process.env.MYSQL_USER,
  password: process.env.MYSQL_PASSWORD,
});

export default connection;

⚠️ É essencial configurar essas 3 variáveis de ambiente para testar o projeto localmente: ⚠️

  host: process.env.MYSQL_HOST
  user: process.env.MYSQL_USER
  password: process.env.MYSQL_PASSWORD

⚠️ É essencial que seu arquivo tenha o nome de connection.ts e esteja no diretório src/models ⚠️

Tabelas

O banco terá três tabelas: pessoas usuárias, produtos e pedidos.

DROP SCHEMA IF EXISTS Trybesmith;
CREATE SCHEMA Trybesmith;

CREATE TABLE Trybesmith.Users (
  id INTEGER AUTO_INCREMENT PRIMARY KEY NOT NULL,
  username TEXT NOT NULL,
  classe TEXT NOT NULL,
  level INTEGER NOT NULL,
  password TEXT NOT NULL
);

CREATE TABLE Trybesmith.Orders (
  id INTEGER AUTO_INCREMENT PRIMARY KEY NOT NULL,
  userId INTEGER,
  FOREIGN KEY (userId) REFERENCES Trybesmith.Users (id)
);

CREATE TABLE Trybesmith.Products (
  id INTEGER AUTO_INCREMENT PRIMARY KEY NOT NULL,
  name TEXT NOT NULL,
  amount TEXT NOT NULL,
  orderId INTEGER,
  FOREIGN KEY (orderId) REFERENCES Trybesmith.Orders (id)
);

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.

⚠ PULL REQUESTS COM ISSUES DE LINTER NÃO SERÃO AVALIADAS. ATENTE-SE PARA RESOLVÊ-LAS ANTES DE FINALIZAR O DESENVOLVIMENTO! ⚠

Você pode também instalar o plugin do ESLint no VSCode, bastar ir em extensions e baixar o plugin ESLint.

Testes

Todos os requisitos do projeto serão testados automaticamente. Cada endpoint possui vários requisitos e os testes para cada requisito de um endpoint estão no arquivo de teste.

Para executar os testes localmente, digite no terminal o comando npm test.

Dica: desativando testes

Especialmente no início, quando a maioria dos testes está falhando, a saída após executar os testes é bastante poluída. Você pode desabilitar temporariamente um teste utilizando a função skip junto à função it. Como o nome indica, esta função "pula" um teste:

  it.skip('Será validado que o campo "username" é obrigatório', async () => {
    const result = await request(app).post("/users").send({
      level: 2,
      classe: "classe",
      password: "senha",
    });
    expect(result.statusCode).toEqual(400);
    expect(result.body.error).toEqual("Username is required");
  });

Uma estratégia é pular todos os testes no início e ir implementando um teste de cada vez, removendo dele a função skip.

Testando um arquivo específico

⚠️ Lembre-se de não entregar o projeto com nenhum teste ignorado. Testes ignorados serão tratados como testes falhando. ⚠️

⚠️ Não apague, em hipótese alguma, qualquer teste ou arquivo deste repositório. ⚠️


Requisitos do projeto

Requisitos Obrigatórios

1 - Crie um endpoint para o cadastro de pessoas usuárias

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

  • As informações de pessoas usuárias cadastradas devem ser salvas na tabela Users do banco de dados;

  • Um token JWT deve ser gerado e retornado caso haja sucesso no cadastro. No seu payload deve estar presente o id e username.

  • O endpoint deve receber a seguinte estrutura:

{
  "username": "string",
  "classe": "string",
  "level": 1,
  "password": "string"
}
Além disso, as seguintes verificações serão feitas:

👉 Para username

  • [Será validado que o campo "username" é obrigatório]

    • Se a requisição não tiver o campo "username", o resultado retornado deverá ser um status http 400 e
      { "error": "Username is required" }
  • [Será validado que o campo "username" tem o tipo string]

    • Se o campo "username" não for do tipo string, o resultado retornado deverá ser um status http 422 e
      { "error": "Username must be a string" }
  • [Será validado que o campo "username" é uma string com mais de 2 caracteres]

    • Se o campo "username" não for do tipo string com mais de 2 caracteres, o resultado retornado deverá ser um status http 422 e
      { "error": "Username must be longer than 2 characters" }

👉 Para classe

  • [Será validado que o campo "classe" é obrigatório]

    • Se a requisição não tiver o campo "classe", o resultado retornado deverá ser um status http 400 e
      { "error": "classe is required" }
  • [Será validado que o campo "classe" tem o tipo string]

    • Se o campo "classe" não for do tipo string, o resultado retornado deverá ser um status http 422 e
      { "error": "Classe must be a string" }
  • [Será validado que o campo "classe" é uma string com mais de 2 caracteres]

    • Se o campo "classe" não for do tipo string com mais de 2 caracteres, o resultado retornado deverá ser um status http 422 e
      { "error": "Classe must be longer than 2 characters" }

👉 Para level

  • [Será validado que o campo "level" é obrigatório]

    • Se a pessoa usuária não tiver o campo "level", o resultado retornado deverá ser um status http 400 e
      { "error": "Level is required" }
  • [Será validado que o campo "level" tem o tipo number]

    • Se o campo "level" não for do tipo number, o resultado retornado deverá ser um status http 422 e
      { "error": "Level must be a number" }
  • [Será validado que o campo "level" deve ser um número maior que 0]

    • Se o campo "level" não for do tipo number maior que 0, o resultado retornado deverá ser um status http 422 e
      { "error": "Level must be greater than 0" }

👉 Para password

  • [Será validado que o campo "password" é obrigatório]

    • Se a requisição não tiver o campo "password", o resultado retornado deverá ser um status http 400 e
      { "error": "Password is required" }
  • [Será validado que o campo "password" tem o tipo string]

    • Se o campo "password" não for do tipo string, o resultado retornado deverá ser um status http 422 e
      { "error": "Password must be a string" }
  • [Será validado que o campo "password" é uma string com 8 ou mais caracteres]

    • Se o campo "password" não for do tipo string com mais de 8 caracteres, o resultado retornado deverá ser um status http 422 e
      { "error": "Password must be longer than 7 characters" }

👉 Para caso os dados sejam enviados corretamente

  • [Será validado que é possível cadastrar a pessoa usuária com sucesso]
    • Se a pessoa usuária for cadastrada com sucesso, o resultado deverá ser conforme o exibido abaixo, com um status http 201 e retornando um token:
    {
      "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
    }

2 - Crie um endpoint para o login de pessoas usuárias

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

  • A rota deve receber os campos username e password, e esses campos devem ser validados no banco de dados.

  • Um token JWT deve ser gerado e retornado caso haja sucesso no login. No seu payload deve estar presente o id e username.

  • O endpoint deve receber a seguinte estrutura:

  {
    "username": "string",
    "password": "string"
  }

⚠️ Na configuração do JWT não use variáveis de ambientes para não ter conflito com o avaliador.

Além disso, as seguintes verificações serão feitas:

👉 Para caso haja problemas no login

  • [Será validado que o campo "username" é enviado]

    • Se o login não tiver o campo "username", o resultado retornado deverá ser um status http 400 e
      { "error": "Username is required" }
  • [Será validado que o campo "password" é enviado]

    • Se o login não tiver o campo "password", o resultado retornado deverá ser um status http 400
      { "error": "Password is required" }
  • [Será validado que não é possível fazer login com um username inválido]

    • Se o login tiver o username inválido, o resultado retornado deverá ser um status http 401 e
      { "error": "Username or password invalid" }
  • [Será validado que não é possível fazer login com uma senha inválida]

    • Se o login tiver a senha inválida, o resultado retornado deverá ser um status http 401 e
      { "error": "Username or password invalid" }

👉 Para caso os dados sejam enviados corretamente

  • [Será validado que é possível fazer login com sucesso]
    • Se o login foi feito com sucesso, o resultado deverá ser um status http 200 e deverá retornar um token:
    {
      "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
    }

3 - Crie um endpoint para o cadastro de produtos

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

  • Um produto só pode ser criado caso a pessoa usuária esteja logada e o token JWT validado.

  • Os produtos enviados devem ser salvos na tabela Products do banco de dados;

  • O endpoint deve receber a seguinte estrutura:

  {
    "name": "Espada longa",
    "amount": "30 peças de ouro"
  }
Além disso, as seguintes verificações serão feitas:

👉 Para token

  • [Será validado que não é possível cadastrar um produto sem token]

    • Se o token não for informado, o resultado retornado deverá ser um status http 401 e
      { "error": "Token not found" }
  • [Será validado que não é possível cadastrar um produto com um token inválido]

    • Se o token informado não for válido, o resultado retornado deverá ser um status http 401 e
      { "error": "Invalid token" }

👉 Para name

  • [Será validado que o campo "name" é obrigatório]

    • Se o campo "name" não for informado, o resultado retornado deverá ser um status http 400 e
      { "error": "Name is required" }
  • [Será validado que o campo "name" tem o tipo string]

    • Se o campo "name" não for do tipo string, o resultado retornado deverá ser um status http 422 e
      { "error": "Name must be a string" }
  • [Será validado que o campo "name" é uma string com mais de 2 caracteres]

    • Se o campo "name" não for uma string com mais de 2 caracteres, o resultado retornado deverá ser um status http 422 e
      { "error": "Name must be longer than 2 characters" }

👉 Para amount

  • [Será validado que o campo "amount" é obrigatório]

    • Se o campo "amount" não for informado, o resultado retornado deverá ser um status http 400 e
      { "error": "Amount is required" }
  • [Será validado que o campo "amount" tem o tipo string]

    • Se o campo "amount" não for do tipo string, o resultado retornado deverá ser um status http 422 e
      { "error": "Amount must be a string" }
  • [Será validado que o campo "amount" é uma string com mais de 2 caracteres]

    • Se o campo "amount" não for uma string com mais de 2 caracteres, o resultado retornado deverá ser um status http 422 e
      { "error": "Amount must be longer than 2 characters" }

👉 Para caso os dados sejam enviados corretamente

  • [Será validado que é possível cadastrar um produto com sucesso]
    • O resultado retornado para cadastrar o produto com sucesso deverá ser conforme exibido abaixo, com um status http 201:
      {
        "item": {
          "id": 1,
          "name": "Poção de cura",
          "amount": "20 gold",
        }
      }

4 - Crie um endpoint para a listagem de produtos

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

  • A rota pode ser acessada apenas por pessoas logadas e com token JWT válido;

Além disso, as seguintes verificações serão feitas:

👉 Para token

  • [Será validado que não é possível listar todos os produtos sem token]

    • Se o token não for informado, o resultado retornado deverá ser um status http 401 e
      { "error": "Token not found" }
  • [Será validado que não é possível listar todos os produtos com um token inválido]

    • Se o token informado não for válido, o resultado retornado deverá ser um status http 401 e
      { "error": "Invalid token" }

👉 Para caso os dados sejam enviados corretamente

  • [Será validado que é possível listar todos os produtos com sucesso]
    • O resultado retornado para listar produtos com sucesso deverá ser conforme exibido abaixo, com um status http 200:
    [
      {
        "id": 1,
        "name": "Poção de cura",
        "amount": "20 gold",
        "orderId": null
      },
      {
        "id": 2,
        "name": "Escudo do Herói",
        "amount": "100 diamond",
        "orderId": 1
      }
    ]

5 - Crie um endpoint para o cadastro de um pedido

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

  • Um pedido só pode ser criado caso a pessoa usuária esteja logada e o token JWT validado.

  • Os pedidos enviados devem ser salvos na tabela Orders do banco de dados. A tabela Products também deve ser alterada;

  • O endpoint deve receber a seguinte estrutura:

  {
    "products": [1, 2]
  }

⚠️ Ao cadastrar um pedido, lembre-se de atualizar os respectivos produtos no banco de dados, incluindo neles o número do pedido criado.

Além disso, as seguintes verificações serão feitas:

👉 Para token

  • [Será validado que não é possível cadastrar pedidos sem token]

    • Se o token não for informado, o resultado retornado deverá ser um status http 401 e
      { "error": "Token not found" }
  • [Será validado que não é possível cadastrar um pedido com token inválido]

    • Se o token informado não for válido, o resultado retornado deverá ser um status http 401 e
      { "error": "Invalid token" }

👉 Para products

  • [Será validado que o campo "products" é obrigatório]

    • Se o corpo da requisição não possuir o campo "products", o resultado retornado deverá ser um status http 400 e
      { "error": "Products is required" }
  • [Será validado que não é possível criar um pedido com o campo "products" não sendo um array]

    • Se o valor do campo "products" não for um array, o resultado retornado deverá ser um status http 422 e
      { "error": "Products must be an array of numbers" }
  • [Será validado que não é possível cadastrar um pedido se o campo "products" for um array vazio]

    • Se o campo "products" possuir um array vazio, o resultado retornado deverá ser um status http 422 e
      { "error": "Products can't be empty" }

👉 Para caso os dados sejam enviados corretamente

  • [Será validado que é possível criar um pedido com sucesso com 1 item]

    • O resultado retornado para cadastrar um pedido com sucesso deverá ser conforme exibido abaixo, com um status http 201:
      {
        "order": {
          "userId": 1,
          "products": [1],
        }
      }
  • [Será validado que é possível criar um pedido com sucesso com vários itens]

    • O resultado retornado para cadastrar um pedido com sucesso deverá ser conforme exibido abaixo, com um status http 201:
      {
        "order": {
          "userId": 1,
          "products": [1, 2]
        }
      }

Requisito Bônus

6 - Crie um endpoint para consultar um pedido

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

  • O pedido só poderá ser acessado caso uma pessoa usuária esteja logada e o token JWT validado;

  • É necessário passar o id correspondente ao pedido na rota;

Além disso, as seguintes verificações serão feitas:

👉 Para token

  • [Será validado que não é possível exibir um pedido sem token]

    • Se o token não for informado, o resultado retornado deverá ser um status http 401 e
      { "error": "Token not found" }
  • [Será validado que não é possível exibir um pedido com token inválido]

    • Se o token informado não for válido, o resultado retornado deverá ser um status http 401 e
      { "error": "Invalid token" }

👉 Para order

  • [Será validado que não é possível exibir um pedido inexistente]
    • Se o pedido não existir, o resultado retornado deverá ser um status http 404 e
      { "error": "Order not found" }

👉 Para caso os dados sejam enviados corretamente

  • [Será validado que é possível exibir um pedido com sucesso buscando pelo seu id]
    • O resultado retornado para consultar um pedido com sucesso deverá ser conforme exibido abaixo, com um status http 200:
      {
        "id": 1,
        "userId": 2,
        "products": [1, 2]
      }

7 - Crie um endpoint para listar todos os pedidos

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

  • A lista só poderá ser acessada caso a pessoa usuária esteja logada e o token JWT validado.

Além disso, as seguintes verificações serão feitas:

👉 Para token

  • [Será validado que não é possível listar pedidos sem token]

    • Se o token não for informado, o resultado retornado deverá ser um status http 401 e
      { "error": "Token not found" }
  • [Será validado que não é possível listar pedidos com token inválido]

    • Se o token informado não for válido, o resultado retornado deverá ser um status http 401 e
      { "error": "Invalid token" }

👉 Para orders

  • [Será validado que é possível listar todos os pedidos com sucesso quando houver apenas um pedido]

    • Quando houver apenas um pedido, o resultado retornado para listar pedidos com sucesso deverá ser conforme exibido abaixo, com um status http 200:
      [
        {
          "id": 1,
          "userId": 2,
          "products": [1, 2]
        }
      ]
  • [Será validado que é possível listar todos os pedidos com sucesso quando hover mais de um pedido]

    • Quando houver mais de um pedido, o resultado retornado para listar pedidos com sucesso deverá ser conforme exibido abaixo, com um status http 200:
      [
        {
          "id": 1,
          "userId": 2,
          "products": [1, 2]
        },
        {
          "id": 2,
          "userId": 2,
          "products": [3, 1, 4]
        }
      ]

Depois de terminar o desenvolvimento (opcional)

Para sinalizar que o seu projeto está pronto para o "Code Review" dos seus colegas, faça o seguinte:

  • Vá até a página DO SEU Pull Request, adicione a label de "code-review" e marque seus colegas:

    • No menu à direita, clique no link "Labels" e escolha a label code-review;

    • No menu à direita, clique no link "Assignees" e escolha o seu usuário;

    • No menu à direita, clique no link "Reviewers" e digite students, selecione o time tryber/students-sd-00.

Caso tenha alguma dúvida, aqui tem um video explicativo.


Revisando um pull request

Use o conteúdo sobre Code Review para te ajudar a revisar os Pull Requests.

#VQV


Avisos finais

Ao finalizar e submeter o projeto, não se esqueça de avaliar sua experiência preenchendo o formulário. Leva menos de 3 minutos!

Link: FORMULÁRIO DE AVALIAÇÃO DE PROJETO

O avaliador automático não necessariamente avalia seu projeto na ordem em que os requisitos aparecem no README. Isso acontece para deixar o processo de avaliação mais rápido. Então, não se assuste se isso acontecer, ok?