Skip to content

Latest commit

 

History

History
665 lines (553 loc) · 22.1 KB

README.md

File metadata and controls

665 lines (553 loc) · 22.1 KB

✨ Projeto Tech Futebol Clube ⚽

O TFC é um site informativo sobre partidas e classificações de futebol.

Sumário


Sobre o projeto

👨‍💻 Contexto

O desafio deste projeto foi desenvolver uma API (utilizando o método TDD) e integrar, através do docker-compose, as aplicações para que elas funcionem consumindo um banco de dados relacional.

Minha responsabilidade foi construir o back-end dockerizado (dockerfile do back e front) utilizando modelagem de dados através do Sequelize. E aplicar os conceitos de Programação Orientada a Objeto (POO) e os princípios SOLID.

O desenvolvimento teve que respeitar as regras de negócio para que a API seja consumida adequadamente pelo front-end disponibilizado, que exibe as informações tabeladas para a pessoa usuária do sistema.

Para algumas rotas, como adicionar uma partida, é necessário ter um token, portanto a pessoa usuária deverá estar logada para fazer as alterações, para tanto foi utilizado JWT para autenticação.

Habilidades

  • Dockerização dos apps, network, volume e compose;
  • Modelagem de dados com MySQL através do Sequelize;
  • Criação e associação de tabelas usando models do sequelize;
  • Construção de uma API REST com endpoints para consumir os models criados;
  • Construção de um CRUD com TypeScript, utilizando ORM;
  • Autenticação de rotas utilizando JWT.
  • Programação Orientada a Objetos e princípios SOLID:
    • Implementar, em TypeScript: Classes, Instâncias, Atributos e Métodos.

Demo

Deploy: Em breve

tfc.webm

(De volta ao topo)

Tecnologias utilizadas

  • Docker

Front-end

Fornecido pronto pela instituição

  • React js

Back-end

  • TypeScript
  • Node.js
  • Express.js
  • Sequelize
  • JSON Web Token (JWT)
  • Bcrypt.js

    Testes

    • Mocha
    • Chai
    • Sinon

Banco de dados

  • MySQL

(De volta ao topo)

Documentação da API

Visão geral

Endpoint Método HTTP Descrição
/login POST Faz o login com usuários do banco de dados
/login/role GET 🔐 Retorna o role do usuário logado (user ou adm)
/teams GET Retorna todos os times do campeonato
/teams/:id GET Retorna o time especificado no id
/matches GET Retorna todas as partidas
/matches POST 🔐 Insere uma nova partida em andamento.
/matches?inProgress=true GET Retorna as partidas em andamento.
/matches?inProgress=false GET Retorna as partidas finalizadas.
/matches/:id PATCH 🔐 Atualiza a partida de acordo com seu id.
/matches/:id/finish PATCH 🔐 Finaliza uma partida em andamento.
/leaderboard GET Retorna a classificação geral do campeonato.
/leaderboar/home GET Retorna a classificação dos times mandantes.
/leaderboard/away GET Retorna a classificação dos times visitantes.

🔐 : Necessário que o token gerado no login seja enviado no headers como "Authorization".

(De volta ao topo)

Corpo das requisições e respostas

Clique nas setas para ver mais

Endpoint: /login

  • Método POST Exemplo de corpo da requisção válido
    {
      "email": "[email protected]",
      "password": "secret_user", 
    }

    Respostas

    • Status: 200 OK

      {
        "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwicm9sZSI6ImFkbWluIiwiaWF0IjoxNjU0NTI3MTg5fQ.XS_9AA82iNoiVaASi0NtJpqOQ_gHSHhxrpIdigiT-fc" // jsonwebtoken gerado
      }
    • Status: 400 Bad Request

      { "message": "All fields must be filled" }
    • Status: 401 Unauthorized

      { "message": "Invalid email or password" }

Endpoint: /login/role

  • 🔐 Método GET Respostas - Status: 200 OK ```json { "role": "admin" } ```
    • Status: 401 Unauthorized

      { "message": "Token not found" }
      { "message": "Token must be a valid token" }

Endpoint: /teams

  • Método GET

    Resposta

    • Status: 200 OK
      [
        {
          "id": 1,
          "teamName": "Avaí/Kindermann"
        },
        {
          "id": 2,
          "teamName": "Bahia"
        },
        {
          "id": 3,
          "teamName": "Botafogo"
        },
        ...
      ]

Parâmetro id: /teams/:id

  • Método GET

    Resposta

    • Status: 200 OK
      {
        "id": 5,
        "teamName": "Cruzeiro"
      }

Endpoint: /matches

  • Método GET

    Resposta

    • Status: 200 OK
        [
          {
            "id": 1,
            "homeTeamId": 16,
            "homeTeamGoals": 1,
            "awayTeamId": 8,
            "awayTeamGoals": 1,
            "inProgress": false,
            "homeTeam": {
              "teamName": "São Paulo"
            },
            "awayTeam": {
              "teamName": "Grêmio"
            }
          },
          ...
          {
            "id": 41,
            "homeTeamId": 16,
            "homeTeamGoals": 2,
            "awayTeamId": 9,
            "awayTeamGoals": 0,
            "inProgress": true,
            "homeTeam": {
              "teamName": "São Paulo"
            },
            "awayTeam": {
              "teamName": "Internacional"
            }
          }
        ]
  • 🔐 Método POST Requisição
    {
      "homeTeamId": 16, // O valor deve ser o id do time
      "awayTeamId": 8, // O valor deve ser o id do time
      "homeTeamGoals": 2,
      "awayTeamGoals": 1,
    }

    Respostas

    • Status: 201 Created

      {
        "id": 1,
        "homeTeamId": 16,
        "homeTeamGoals": 2,
        "awayTeamId": 8,
        "awayTeamGoals": 1,
        "inProgress": true,
      }
    • Status: 401 Unauthorized

      { "message": "Token not found" }
      { "message": "Token must be a valid token" }
    • Status: 404 Not Found

      { "message": "There is no team with such id!" }
    • Status: 422 Unprocessable Entity

      { "message": "It is not possible to create a match with two equal teams" }

Parâmetro inProgress: /matches?inProgress=

  • Método GET Opções de query: true ou false

    Ex:

    matches?inProgress=true
    

    Resposta

    • Status: 200 OK
      [
        {
          "id": 41,
          "homeTeamId": 16,
          "homeTeamGoals": 2,
          "awayTeamId": 9,
          "awayTeamGoals": 0,
          "inProgress": true,
          "homeTeam": {
            "teamName": "São Paulo"
          },
          "awayTeam": {
            "teamName": "Internacional"
          }
        },
        {
          "id": 42,
          "homeTeamId": 6,
          "homeTeamGoals": 1,
          "awayTeamId": 1,
          "awayTeamGoals": 0,
          "inProgress": true,
          "homeTeam": {
            "teamName": "Ferroviária"
          },
          "awayTeam": {
            "teamName": "Avaí/Kindermann"
          }
        }
      ]

Parâmetro id: /matches/:id

  • 🔐 Método PATCH

    Requisição:

    {
      "homeTeamGoals": 3,
      "awayTeamGoals": 1
    }

    Respostas

    • Status: 200 OK

      { "message": "Updated match!" } 
    • Status: 401 Unauthorized

      { "message": "Token not found" }
      { "message": "Token must be a valid token" }

Endpoint: /matches/:id/finish

  • 🔐 Método PATCH Respostas
    • Status: 200 OK

        { "message": "Finished" }
    • Status: 401 Unauthorized

        { "message": "Token not found" }
        { "message": "Token must be a valid token" }

Endpoint: /leaderboard

  • Método GET

    Resposta

    • Status: 200 OK
      [
        {
          "name": "Palmeiras",
          "totalPoints": 13,
          "totalGames": 5,
          "totalVictories": 4,
          "totalDraws": 1,
          "totalLosses": 0,
          "goalsFavor": 17,
          "goalsOwn": 5,
          "goalsBalance": 12,
          "efficiency": "86.67"
        },
          ...
        {
          "name": "Napoli-SC",
          "totalPoints": 2,
          "totalGames": 6,
          "totalVictories": 0,
          "totalDraws": 2,
          "totalLosses": 4,
          "goalsFavor": 3,
          "goalsOwn": 15,
          "goalsBalance": -12,
          "efficiency": "11.11"
        }
      ]

Endpoint: /leaderboard/home

  • Método GET

    Resposta

    • Status: 200 OK
      [
        {
          "name": "Santos",
          "totalPoints": 9,
          "totalGames": 3,
          "totalVictories": 3,
          "totalDraws": 0,
          "totalLosses": 0,
          "goalsFavor": 9,
          "goalsOwn": 3,
          "goalsBalance": 6,
          "efficiency": "100.00"
        },
        ...
        {
          "name": "Bahia",
          "totalPoints": 0,
          "totalGames": 3,
          "totalVictories": 0,
          "totalDraws": 0,
          "totalLosses": 3,
          "goalsFavor": 0,
          "goalsOwn": 4,
          "goalsBalance": -4,
          "efficiency": "0.00"
        }
      ]

Endpoint: /leaderboard/away

  • Método GET

    Resposta

    • Status: 200 OK
      [
        {
          "name": "Palmeiras",
          "totalPoints": 6,
          "totalGames": 2,
          "totalVictories": 2,
          "totalDraws": 0,
          "totalLosses": 0,
          "goalsFavor": 7,
          "goalsOwn": 0,
          "goalsBalance": 7,
          "efficiency": "100.00"
        },
          ...
        {
          "name": "Napoli-SC",
          "totalPoints": 0,
          "totalGames": 3,
          "totalVictories": 0,
          "totalDraws": 0,
          "totalLosses": 3,
          "goalsFavor": 1,
          "goalsOwn": 10,
          "goalsBalance": -9,
          "efficiency": "0.00"
        }
      ]

(De volta ao topo)

Banco de Dados

  1. Diagrama ER

diagram-er

  1. Seeders

O banco de dados contém:

  • tabela users com usuários válidos com hash das senhas e alguns inválidos, estes útimos utilizados para os testes avaliativos.
  • tabela teams com a lista de todos os times que estão participando do campeonato.
  • tabela matches com algumas partidadas finalizadas e outras em andamento.
  1. Exemplo de um arquivo .env

Se estiver utilizar Docker as informações do DB_* vem do docker-compose, caso contrário se utilizar o MySql instalado na sua máquina.

  JWT_SECRET=jwt_secret
  APP_PORT=3001
  DB_USER=seu_user
  DB_PASS=sua_senha
  DB_HOST=localhost
  DB_PORT=3002

(De volta ao topo)

Instalando localmente

Caso deseje contribuir ou simplesmente rodar o projeto na sua máquina, siga as orientações:

⚠️ Configurações mínimas para execução do projeto

  • Preferencialmente Sistema Operacional Distribuição Unix
  • Node versão 16
  • Docker
  • Docker-compose versão >=1.29.2
  1. Clone o repositório
    git clone [email protected]:ligiabicalho/Tech-Futebol-Clube.git
  2. Navegue até a pasta do repositório clonado
    cd Tech-Futebol-Clube
  3. Instale as dependências no diretório raiz
    npm install

Docker

  1. Na raíz do projeto rode o comando:
    npm run compose:up
  2. Em seguida abra o terminal interativo do container:
    docker exec -it app_backend sh
  3. Instale as dependências dentro do container:
    npm install

Rodando os testes

Para rodar os testes de integração desenvolvidos por mim, entre na pasta backend e rode o comando:

  • npm test

(De volta ao topo)

Requisitos do projeto

Clique na seta para ver a lista de requisitos que recebemos para desenvolver durante o processo avaliativo.

Docker

Configuração dos dockerfiles referente ao front e back-end, para integrar as aplicações através do docker-compose, para que elas funcionem consumido o banco de dados.

Fluxo Teams
  1. Desenvolva uma migration e um model para a tabela de times, utilizando Sequelize.
  2. (TDD) Desenvolva testes de integração do back-end referente a implementação do requisito seguinte.
  3. Desenvolva o endpoint /teams no back-end de forma que ele possa retornar a lista com todos os times corretamente.
  4. (TDD) Evolua os testes de integração da sua rota /teams, agora considerando o contrato do próximo requisito.
  5. Desenvolva o endpoint /teams/:id no back-end de forma que ele possa retornar dados de um time específico.
Fluxo User e Login
  1. Desenvolva uma migration e um model para a tabela de pessoas usuárias, utilizando Sequelize.
  2. (TDD) Desenvolva testes baseando-se no contrato do endpoint /login do próximo requisito.
  3. Desenvolva o endpoint /login no back-end de maneira que ele permita o acesso com preenchimento obrigatório de email e password no front-end e retorne um token.
  4. (TDD) Evolua os testes de integração da sua rota /login, agora considerando o contrato do próximo requisito.
  5. Desenvolva o endpoint /login no back-end de maneira que ele não permita o acesso com dados inválidos ou não cadastrados no banco de dados, considerando:
    • As senhas que existem no banco de dados estão encriptadas.
  6. (TDD) Desenvolva testes baseando-se no contrato do endpoint /login/role do próximo requisito.
  7. Desenvolva um middleware de validação para o token, verificando se ele é válido, e desenvolva o endpoint /login/role no back-end de maneira que ele retorne os dados corretamente no front-end.
    • ⚠️ A rota deve recebe um header com parâmetro authorization, onde ficará armazenado o token gerado no login;
Fluxo Matches
  1. Desenvolva uma migration e um model para a tabela de partidas, utilizando Sequelize.
  2. (TDD) Desenvolva teste de integração, agora da sua rota /matches, considerando os contratos dos próximos requisitos.
  3. Desenvolva o endpoint /matches de forma retorna uma lista de partidas e que todos os dados de partidas sem nenhum filtro apareçam corretamente na tela de partidas no front-end.
  4. Desenvolva o endpoint /matches de forma que seja possível filtrar somente as partidas em andamento, e também filtrar somente as partidas finalizadas, na tela de partidas do front-end.
    • Essa requisição deverá usar query string para definir o parâmetro.
  5. Desenvolva o endpoint /matches/:id/finish de modo que seja possível finalizar uma partida no banco de dados.
    • ⚠️ Não é possível alterar uma partida sem um token;
  6. Desenvolva o endpoint /matches/:id de forma que seja possível atualizar partidas em andamento.
    • ⚠️ Não é possível atualizar uma partida sem um token;
  7. (TDD) Desenvolva testes de integração, agora da sua rota /matches, considerando os contratos dos próximos requisitos.
  8. Desenvolva o endpoint /matches de modo que seja possível cadastrar uma nova partida em andamento no banco de dados e retornar os dados inserida no banco de dados.
    • ⚠️ Não é possível atualizar uma partida sem um token;
  9. Desenvolva o endpoint /matches de forma que não seja possível inserir uma partida com times iguais nem com um time que não existe na tabela de times.
Fluxo Leaderboards
  • Regras de negócio para classificação dos times

    Todas as regras de negócio e cálculos necessários deverão ser realizados no back-end. A aplicação front-end apenas renderizará essas informações.

    • A tabela deverá renderizar somente as partidas que já foram FINALIZADAS.
    Classificação: Posição na classificação;  
    Time: Nome do time;  
    P: Total de Pontos;  
    J: Total de Jogos;  
    V: Total de Vitórias;  
    E: Total de Empates;  
    D: Total de Derrotas;  
    GP: Gols marcados a favor;  
    GC: Gols sofridos;  
    SG: Saldo total de gols;  
    %: Aproveitamento do time.  
    

    O resultado deverá ser ordenado sempre de forma decrescente, levando em consideração a quantidade de pontos que o time acumulou.
    Em caso de empate no Total de Pontos, você deve levar em consideração os seguintes critérios para desempate:

    • 1º Total de Vitórias;
    • 2º Saldo de gols;
    • 3º Gols a favor;
  1. (Bônus TDD) Desenvolva testes de integração para a rota /leaderboard, considerando o contrato dos próximos requisitos.
  • Leaderboard Home
    1. Desenvolva o endpoint /leaderboard/home de forma que retorne as informações do desempenho dos times da casa com as seguintes propriedades: name, totalPoints, totalGames, totalVictories, totalDraws, totalLosses, goalsFavor e goalsOwn.
    2. Desenvolva o endpoint /leaderboard/home de forma que seja possível filtrar as classificações dos times da casa na tela de classificação do front-end com os dados iniciais do banco de dados, incluindo as propriedades goalsBalance e efficiency, além das propriedades do requisito anterior.
    3. Desenvolva o endpoint /leaderboard/home de forma que seja possível filtrar as classificações dos times da casa na tela de classificação do front-end, e atualizar a tabela ao inserir a partida Corinthians 2 X 1 Internacional.
  • Leaderboard away
    1. Desenvolva o endpoint /leaderboard/away de forma que retorne as informações do desempenho dos times visitantes com as mesmas propriedades do req. 23.
    2. Desenvolva o endpoint /leaderboard/away, de forma que seja possível filtrar as classificações dos times quando visitantes na tela de classificação do front-end, com os dados iniciais do banco de dados, incluindo as propriedades goalsBalance e efficiency, além das propriedades do requisito anterior.
    3. Desenvolva o endpoint /leaderboard/away de forma que seja possível filtrar as classificações dos times quando visitantes na tela de classificação do front-end e atualizar a tabela ao inserir a partida Corinthians 2 X 1 Internacional.
  • Leaderboard
    1. Desenvolva o endpoint /leaderboard de forma que seja possível filtrar a classificação geral dos times na tela de classificação do front-end com os dados iniciais do banco de dados.
    2. (Bônus) Desenvolva o endpoint /leaderboard de forma que seja possível filtrar a classificação geral dos times na tela de classificação do front-end e atualizar a tabela ao inserir a partida Flamengo 3 X 0 Napoli-SC.

(De volta ao topo)

Status de desenvolvimento

100% dos requisitos solicitados foram desenvolvidos.

No entanto, podemos pensar em ampliar a API criando rotas para criação e edição do perfil do usuário.

Desenvolvedora

Ligia Bicalho

ℹ️ Lígia Bicalho

Agradecimentos

A Trybe e seus instrutures pelos ensinamentos, elaboração do desafio e mentorias técnicas. Aos colegas de turma pelas valiosas discussões, devidamente referenciadas nos comentários do código.

(De volta ao topo)