Skip to content

Edson-Mendes/finances-api

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Finances API

Badge Concluído Badge Java Badge Spring Badge Maven Badge JUnit5 Badge Postgres Badge Heroku

📖 Resumo do projeto

Finances API é uma REST API para controle de orçamento familiar que visa auxiliar o usuário a gerenciar suas receitas e despesas de forma clara e segura.

A aplicação possui endpoints para gerenciar e manipular os recursos Usuário (User), Receitas (Income), Despesas (Expense), Autenticação (Auth), Resumos (Summary), Categorias (Category), que são protegidos e requerem autenticação por JWT (Json Web Token) para serem manipulados.

O projeto foi proposto pela Alura no Challenge Backend 2ª edição.

🧰 Tecnologias

🔨 Funcionalidades

🔒 API de gerenciamento de autenticação

  • Cadastrar usuário: Cadastro de usuários através de um POST /api/auth/signup com as informações name, email, password e confirm em um JSON no corpo da requisição.

    • O password é salvo criptografado no banco de dados usando BCryp.

    Segue abaixo um exemplo do corpo da requisição.

    {
      "name": "John Doe",
      "email": "john.doe@email.com",
      "password": "1234567890",
      "confirmPassword": "1234567890"
    }

    Em caso de sucesso a resposta tem status 201 com um JSON no corpo da resposta contendo id, name, email, status e createdAt do usuário cadastrado.

    Segue abaixo um exemplo do corpo da resposta.

    {
      "id": 150,
      "name": "John Doe",
      "email": "john.doe@email.com"
    }
  • Logar usuário: Sign in de usuários através de um POST /api/auth/signin com as informações de autenticação do usuário (email e senha) em um JSON no corpo da requisição.

    Segue abaixo um exemplo do corpo da requisição.

    {
      "email": "john.doe@email.com",
      "password": "1234567890"
    }

    Em caso de sucesso a resposta tem status 200 com um JSON no corpo da resposta contendo token e type, onde token é um JWT que deve ser enviado em todas as requisições que requerem usuário autenticado, e type é o tipo do token, no caso desse sistema é o tipo Bearer.

    Segue abaixo um exemplo do corpo da resposta.

    {
      "token": "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiI1IiwiaWF0IjoxNzE4MTk1OTQ5LCJleHAiOjE3MTgyODIzNDksImlzcyI6IkZpbmFuY2VzIEFQSSJ9.MEN5dWRe5XZ1F1j6A24loW9lzD3U_SvRMFlOG5TtLuM",
      "type": "Bearer"
    }

👤 API de gerenciamento de usuário

  • Buscar usuários: Busca paginada de usuários através de um GET /api/users, apenas usuário ADMIN tem permissão para usar essa funcionalidade.

    • Caso o cliente queira outra página da pesquisa deve passar a query param page, o valor padrão de page é 0.
    • Caso o cliente queira outro tamanho de página deve passar a query param size, o valor padrão de size é 10.
    • Os usuários vem ordenados por id em ordem crescente.

    Em caso de sucesso a resposta tem status 200 e um JSON no corpo da resposta com as informações paginadas dos usuários cadastrados.

    Segue abaixo um exemplo do corpo da resposta.

    {
      "totalElements": 2,
      "totalPages": 1,
      "pageable": {
        "pageNumber": 0,
        "pageSize": 10,
        "sort": {
          "sorted": true,
          "empty": false,
          "unsorted": false
        },
        "offset": 0,
        "paged": true,
        "unpaged": false
      },
      "size": 10,
      "content": [
        {
          "id": 1,
          "name": "John Doe",
          "email": "john@email.com"
        },
        {
          "id": 3,
          "name": "Jane Doe",
          "email": "jane@email.com"
        }
      ],
      "number": 0,
      "sort": {
        "sorted": true,
        "empty": false,
        "unsorted": false
      },
      "numberOfElements": 2,
      "first": true,
      "last": true,
      "empty": false
    }
  • Atualizar senha: Atualizar password através de um POST /api/users/password com as informações oldPassword, newPassword e confirm em um JSON no corpo da requisição.

    Segue abaixo um exemplo do corpo da requisição.

    {
      "oldPassword": "1234567890",
      "newPassword": "0987654321",
      "confirm": "0987654321"
    }

    Em caso de sucesso a resposta tem status 204.

  • Deletar usuário: Deletar usuário através de um DELETE /api/users/{id}, apenas usuário ADMIN tem permissão para usar essa funcionalidade.

    Em caso de sucesso a resposta tem status 204.

📈 API de gerenciamento de receita

  • Cadastrar receita: Cadastrar receitas através de um POST /api/incomes com as informações description, date e value em um JSON no corpo da requisição.

    Segue abaixo um exemplo do corpo da requisição.

    {
      "description": "Salário",
      "date": "2024-01-04",
      "value": 3240.59
    }

    Em caso de sucesso a resposta tem status 201 e um JSON no corpo da resposta.

    Segue abaixo um exemplo do corpo da resposta.

    {
      "id": 1
      "description": "Salário",
      "date": "2024-01-04",
      "value": 3240.59
    }
  • Buscar receitas: Busca paginada de receitas através de um GET /api/incomes, opcional buscar por description.

    • Caso o cliente queira outra página deve passar a query param page, o valor padrão de page é 0.
    • Caso o cliente queira outro tamanho de página deve passar a query param size, o valor padrão de size é 10.
    • Caso o cliente queira receitas com uma dada description deve passar a query param description, esse parâmetro é opcional.
    • As receitas vem ordenados por date da mais recente para menos recente.

    Em caso de sucesso a resposta tem status 200 com um JSON no corpo da resposta.

    Segue abaixo um exemplo do corpo da resposta para a requisição GET /api/incomes.

    {
      "totalElements": 1,
      "totalPages": 1,
      "pageable": {
        "pageNumber": 0,
        "pageSize": 10,
        "sort": {
          "sorted": true,
          "empty": false,
          "unsorted": false
        },
        "offset": 0,
        "paged": true,
        "unpaged": false
      },
      "size": 10,
      "content": [
        {
          "id": 2,
          "description": "Salário",
          "date": "2024-01-04",
          "value": 3240.59
        }
      ],
      "number": 0,
      "sort": {
        "sorted": true,
        "empty": false,
        "unsorted": false
      },
      "numberOfElements": 1,
      "first": true,
      "last": true,
      "empty": false
    }
  • Buscar receita por id: Busca receita por id através de um GET /api/incomes/{id}, onde id é o identificador da receita.

    • Usuário só tem permissão para buscar receitas que pertencem a si mesmo.

    Em caso de sucesso a resposta tem status 200 com um JSON no corpo da resposta.

    Segue abaixo um exemplo do corpo da resposta para a requisição GET /api/incomes/2

    {
      "id": 2,
      "description": "Salário",
      "date": "2024-01-04",
      "value": 3240.59
    }
  • Buscar receitas por ano e mês: Busca paginada de receitas por ano e mês, através de um GET /api/incomes{year}/{month}.

    • Caso o cliente queira outra página deve passar a query param page, o valor padrão de page é 0.
    • Caso o cliente queira outro tamanho de página deve passar a query param size, o valor padrão de size é 10.
    • As receitas vem ordenadas por date da mais recente para menos recente.

    Em caso de sucesso a resposta tem status 200 com um JSON no corpo da resposta.

    Segue abaixo um exemplo do corpo da resposta para a requisição GET /api/incomes/2024/1.

    {
      "totalElements": 1,
      "totalPages": 1,
      "pageable": {
        "pageNumber": 0,
        "pageSize": 10,
        "sort": {
          "sorted": true,
          "empty": false,
          "unsorted": false
        },
        "offset": 0,
        "paged": true,
        "unpaged": false
      },
      "size": 10,
      "content": [
        {
          "id": 2,
          "description": "Salário",
          "date": "2024-01-04",
          "value": 3240.59
        }
      ],
      "number": 0,
      "sort": {
        "sorted": true,
        "empty": false,
        "unsorted": false
      },
      "numberOfElements": 1,
      "first": true,
      "last": true,
      "empty": false
    }
  • Atualizar receita: Atualizar receita por id através de um PUT /api/incomes/{id}, onde id é o identificador da receita, e enviando as novas informações da receita (description, date e value) em um JSON no corpo da requisição.

    • Usuário só tem permissão de atualizar receitas que pertencem a si mesmo.

    Segue abaixo um exemplo do corpo da requisição.

    {
      "description": "Salário",
      "date": "2024-01-05",
      "value": 3800.00
    }

    Em caso de sucesso a resposta tem status 200, com um JSON no corpo da resposta.

    Segue abaixo um exemplo do corpo da resposta.

    {
      "id": 2,
      "description": "Salário",
      "date": "2024-01-05",
      "value": 3800.00
    }
  • Deletar receita: Deletar receita por id através de um DELETE /api/incomes/{id}, onde id é o identificador da receita a ser deletada.

    • Usuário só tem permissão de deletar receitas que pertencem a si mesmo.

    Em caso de sucesso a resposta tem status 204.

📉 API de gerenciamento de despesas

  • Cadastrar despesa: Cadastrar despesas através de um POST /api/expenses com as informações description, date, value e category (opcional), em um JSON no corpo da requisição.

    • O campo category é opcional e caso não seja enviado, a despesa é salva com a category OUTRAS.

    Segue abaixo um exemplo do corpo da requisição.

    {
      "description": "Mercado",
      "date": "2024-01-15",
      "value": 271.94,
      "category": "ALIMENTACAO"
    }

    Em caso de sucesso a resposta tem status 201, e um JSON no corpo da resposta.

    Segue abaixo um exemplo do corpo da resposta.

    {
      "id": 5,
      "description": "Mercado",
      "date": "2024-01-15",
      "value": 271.94,
      "category": "ALIMENTACAO"
    }
  • Buscar despesas: Busca paginada de despesas através de um GET /api/expenses, opcional buscar por description.

    • Caso o cliente queira outra página deve passar a query param page, o valor padrão de page é 0.
    • Caso o cliente queira outro tamanho de página deve passar a query param size, o valor padrão de size é 10.
    • Caso o cliente queira despesas com uma dada description deve passar a query param description, esse parâmetro é opcional.
    • As despesas vem ordenadas por date da mais recente para menos recente.

    Em caso de sucesso a resposta tem status 200, e um JSON no corpo da resposta.

    Segue abaixo um exemplo do corpo da resposta.

    {
      "totalElements": 1,
      "totalPages": 1,
      "pageable": {
        "pageNumber": 0,
        "pageSize": 10,
        "sort": {
          "sorted": true,
          "empty": false,
          "unsorted": false
        },
        "offset": 0,
        "paged": true,
        "unpaged": false
      },
      "size": 10,
      "content": [
        {
          "id": 5,
          "description": "Mercado",
          "date": "2024-01-15",
          "value": 271.94,
          "category": "ALIMENTACAO"
        }
      ],
      "number": 0,
      "sort": {
        "sorted": true,
        "empty": false,
        "unsorted": false
      },
      "numberOfElements": 1,
      "first": true,
      "last": true,
      "empty": false
    }
  • Buscar despesa por id: Busca de despesa por id através de um GET para /api/expenses/{id}, onde id é o identificador da despesa.

    • Usuário só tem permissão para buscar despesas que pertencem a si mesmo.

    Em caso de sucesso a resposta tem status 200 com um JSON no corpo da resposta.

    Segue abaixo um exemplo do corpo da resposta para a requisição GET /api/expenses/5

    {
      "id": 5,
      "description": "Mercado",
      "date": "2024-01-15",
      "value": 271.94,
      "category": "ALIMENTACAO"
    }
  • Buscar despesas por ano e mês: Busca paginada de despesas por ano e mês através de um GET /api/expenses/{year}/{month}.

    • Caso o cliente queira outra página deve passar a query param page, o valor padrão de page é 0.
    • Caso o cliente queira outro tamanho de página deve passar a query param size, o valor padrão de size é 10.
    • As despesas vem ordenados por date da mais recente para menos recente.

    Em caso de sucesso a resposta tem status 200 com um JSON no corpo da resposta.

    Segue abaixo um exemplo do corpo da resposta para a requisição GET /api/expenses/2024/1.

    {
      "totalElements": 1,
      "totalPages": 1,
      "pageable": {
        "pageNumber": 0,
        "pageSize": 10,
        "sort": {
          "sorted": true,
          "empty": false,
          "unsorted": false
        },
        "offset": 0,
        "paged": true,
        "unpaged": false
      },
      "size": 10,
      "content": [
        {
          "id": 5,
          "description": "Mercado",
          "date": "2024-01-15",
          "value": 271.94,
          "category": "ALIMENTACAO"
        }
      ],
      "number": 0,
      "sort": {
        "sorted": true,
        "empty": false,
        "unsorted": false
      },
      "numberOfElements": 1,
      "first": true,
      "last": true,
      "empty": false
    }
  • Atualizar despesa: Atualizar despesa por id através de um PUT /api/expenses/{id}, onde id é o identificador da despesa e enviando as novas informações da despesa (description, date, value e category) em um JSON no corpo da requisição.

    • Usuário só tem permissão de atualizar despesas que pertencem a si mesmo.

    Segue abaixo um exemplo do corpo da requisição.

    {
      "description": "Mercado",
      "date": "2024-01-20",
      "value": 300.00,
      "category": "ALIMENTACAO"
    }

    Em caso de sucesso a resposta tem status 201, e um JSON no corpo da resposta.

    Segue abaixo um exemplo do corpo da resposta.

    {
      "id": 5,
      "description": "Mercado",
      "date": "2024-01-20",
      "value": 300.00,
      "category": "ALIMENTACAO"
    }
  • Deletar despesa: Deletar despesa por id através de um DELETE /api/expenses/{id}, onde id é o identificador da despesa a ser deletada.

    • Usuário só tem permissão de deletar despesas que pertencem a si mesmo.

    Em caso de sucesso a resposta tem status 204.

🔣 API de gerenciamento de categorias

  • Buscar categorias: Buscar categorias através de um GET /api/categories.

    Em caso de sucesso a resposta tem status 200, e um JSON no corpo da resposta.

    Segue abaixo um exemplo do corpo da resposta.

    [
      {
        "category": "ALIMENTACAO"
      },
      {
        "category": "SAUDE"
      },
      {
        "category": "MORADIA"
      },
      {
        "category": "TRANSPORTE"
      },
      {
        "category": "EDUCACAO"
      },
      {
        "category": "LAZER"
      },
      {
        "category": "IMPREVISTOS"
      },
      {
        "category": "OUTRAS"
      }
    ]

📊 API de gerenciamento de resumo

  • Buscar resumo do mês: Buscar resumo do mês através de um GET /api/summaries/{year}/{month}, o usuário tem acesso ao total de receitas e despesas de um dado ano e mês, assim como o saldo do mês e o total de despesas por categoria.

    Em caso de sucesso a resposta tem status 200 e um JSON no corpo da resposta.

    Segue abaixo um exemplo do corpo da resposta.

    {
      "incomeTotalValue": 3240.59,
      "expenseTotalValue": 271.94,
      "finalBalance": 2968.65,
      "valuesByCategory": [
        {
          "category": "ALIMENTACAO",
          "value": 271.94
        }
      ]
    }

⬜ Diagramas

Diagrama entidade relacionamento

---
    title: Database Schema
---
    erDiagram
        USER {
            bigserial id PK
            varchar(100) name
            varchar(150) email UK
            varchar(255) password
        }
        ROLE {
            serial id PK
            varchar(50) name UK
        }
        USER_ROLES {
            bigint user_id FK
            int roles_id FK
        }
        INCOME {
            bigserial id PK
            varchar(255) description
            numeric value
            date date
            bigint user_id FK
        }
        EXPENSE {
            bigserial id PK
            varchar(255) description
            numeric value
            varchar(25) category
            date date
            bigint user_id FK
        }
        
        USER }o--o{ USER_ROLES : has
        ROLE }o--o{ USER_ROLES : allows
        USER ||--o{ INCOME : has
        USER ||--o{ EXPENSE : has

🛠️ Deploy

O deploy da aplicação foi realizado no Heroku, você pode testar/brincar/usar aqui

OBS: As aplicações que usam conta gratuita do heroku adormecem se ficarem inativas, então pode ser que a primeira requisição demore um pouco (até uns 60 segundos), apenas seja paciente 😉.

🗃️ Documentação

Documentação feita com Springdoc openapi, gerando Swagger UI e Api docs

⚙️ Atualizações futuras

  • Configuração de CORS
  • Mais relatórios, tipo relatório anual, ou por categoria em dados mês e ano.
  • Adicionar algum recurso para monitorar e gerenciar a aplicação, tipo o Actuator
  • Endpoint para listar todas as categorias.
  • Remoção da regra de negócio que não permitia receitas/despesas com a mesmo DESCRIÇÃO no mesmo MÊS e ANO.
  • Atualizar versão do Spring boot (2.7.* ou 3.3.*)