Skip to content

Commit 1a5acff

Browse files
committed
Alteracoes
1 parent a5c012b commit 1a5acff

34 files changed

Lines changed: 746 additions & 87 deletions

README.md

Lines changed: 79 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,62 @@
1+
# Apresentação do teste
2+
3+
## Para rodar o projeto localmente
4+
5+
**(importante) ter o docker instalado na maquina**
6+
7+
Faça o clone do projeto
8+
9+
Dentro da pasta "docker" rode os seguintes comandos:
10+
11+
- **docker-compose build** (para buildar o docker-compose e verificar se está tudo ok)
12+
- **docker-compose up -d** (para rodar o docker-compose em background)
13+
14+
O docker-compose irá subir o **MongoDB** e o **MongoExpress** em um workspace chamado test-safra como na imagem abaixo:
15+
16+
Para acessar o **MongoExpress** basta inserir no browser o seguinte endereço: http://localhost:8081
17+
18+
O MongoDB vai estar rodando na porta 27017 padrão.
19+
20+
## Para rodar os projetos de API .NET CORE CORE (V.6)
21+
22+
Temos dois projetos:
23+
24+
ExpenseManagement.Api - na pasta "back", este é responsável for fornecer os dados de gasto do cliente e operações de update e insert de dados
25+
26+
ExpenseManagement.Authentication.Api - na pasta "auth", este é responsável por fornecer autenticação para o projeto, temos dois perfis aqui cadastrados:
27+
28+
- role "system" para sistemas que podem cadastrar gastos para um cliente
29+
- role "client" para clientes aptos a visualizar gastos e alterar categorias
30+
31+
O sistema de autenticação fornece um token JWT que deve ser inserido nas chamadas a API de backend.
32+
33+
Todas as informações da API de autenticação nesta primeira versão estão "mockadas" na aplicação.
34+
35+
Colocando as duas aplicações para rodar teremos as duas API´s nos seguintes endereços:
36+
37+
- http://localhost:5226/swagger/index.html (API principal)
38+
- http://localhost:5167/swagger/index.html (API autenticação)
39+
40+
## Para rodar o projeto de front Angular (V.15)
41+
42+
Dentro da pasta "front" acesse a pasta "spent-app" e rode os comandos:
43+
44+
- npm install (para instalar as dependências)
45+
- ng serve (para rodar a aplicação)
46+
47+
A aplicação deverá ser consumida no endereço: http://localhost:4200/log-in
48+
49+
## Collection do Postman
50+
51+
Deixei dentro da pasta "others" as coleções do Postman utilizado neste projeto:
52+
53+
- API - ExpenseManagement - Auth.postman_collection.json
54+
- API - ExpenseManagement.postman_collection.json
55+
56+
Basta abrir o Postman e importar a collection
57+
58+
## Telas da aplicação
59+
160
# Show me the code
261

362
### # DESAFIO:
@@ -11,58 +70,64 @@ Funcionalidade: Integração de gastos por cartão
1170
Os gastos, serão informados atraves do protoloco JSON, seguindo padrão:
1271
{ "descricao": "alfanumerico", "valor": double americano, "codigousuario": numerico, "data": Data dem formato UTC }
1372
```
73+
1474
```
1575
Funcionalidade: Listagem de gastos*
1676
Dado que acesso como um cliente autenticado que pode visualizar os gastos do cartão
1777
Quando acesso a interface de listagem de gastos
1878
Então gostaria de ver meus gastos mais atuais.
19-
79+
2080
*Para esta funcionalidade é esperado 2.000 acessos por segundo.
2181
*O cliente espera ver gastos realizados a 5 segundos atrás.
2282
```
83+
2384
```
2485
Funcionalidade: Filtro de gastos
2586
Dado que acesso como um cliente autenticado
2687
E acessei a interface de listagem de gastos
2788
E configure o filtro de data igual a 27/03/1992
2889
Então gostaria de ver meus gastos apenas deste dia.
2990
```
91+
3092
```
3193
Funcionalidade: Categorização de gastos
3294
Dado que acesso como um cliente autenticado
3395
Quando acesso o detalhe de um gasto
3496
E este não possui uma categoria
3597
Então devo conseguir incluir uma categoria para este
3698
```
99+
37100
```
38101
Funcionalidade: Sugestão de categoria
39102
Dado que acesso como um cliente autenticado
40103
Quando acesso o detalhe do gasto que não possui categoria
41104
E começo a digitar a categoria que desejo
42105
Então uma lista de sugestões de categoria deve ser exibida, estas baseadas em categorias já informadas por outro usuários.
43106
```
107+
44108
```
45109
Funcionalidade: Categorização automatica de gasto
46-
No processo de integração de gastos, a categoria deve ser incluida automaticamente
110+
No processo de integração de gastos, a categoria deve ser incluida automaticamente
47111
caso a descrição de um gasto seja igual a descrição de qualquer outro gasto já categorizado pelo cliente
48112
o mesmo deve receber esta categoria no momento da inclusão do mesmo
49113
```
114+
50115
### # Avaliação
51116

52-
Você será avaliado pela usabilidade, por respeitar o design e pela arquitetura da API.
117+
Você será avaliado pela usabilidade, por respeitar o design e pela arquitetura da API.
53118
É esperado que você consiga explicar as decisões que tomou durante o desenvolvimento através de commits.
54119

55-
* Springboot - Java - Maven (preferêncialmente) ([https://projects.spring.io/spring-boot/](https://projects.spring.io/spring-boot/))
56-
* RESTFul ([https://blog.mwaysolutions.com/2014/06/05/10-best-practices-for-better-restful-api/](https://blog.mwaysolutions.com/2014/06/05/10-best-practices-for-better-restful-api/))
57-
* DDD ([https://airbrake.io/blog/software-design/domain-driven-design](https://airbrake.io/blog/software-design/domain-driven-design))
58-
* Microservices ([https://martinfowler.com/microservices/](https://martinfowler.com/microservices/))
59-
* Testes unitários, teste o que achar importante (De preferência JUnit + Mockito). Mas pode usar o que você tem mais experiência, só nos explique o que ele tem de bom.
60-
* SOAPUI para testes de carga ([https://www.soapui.org/load-testing/concept.html](https://www.soapui.org/load-testing/concept.html))
61-
* Uso de diferentes formas de armazenamento de dados (REDIS, Cassandra, Solr/Lucene)
62-
* Uso do git
63-
* Diferencial: Criptografia de comunicação, com troca de chaves. ([http://noiseprotocol.org/](http://noiseprotocol.org/))
64-
* Diferencial: CQRS ([https://martinfowler.com/bliki/CQRS.html](https://martinfowler.com/bliki/CQRS.html))
65-
* Diferencial: Docker File + Docker Compose (com dbs) para rodar seus jars.
120+
- Springboot - Java - Maven (preferêncialmente) ([https://projects.spring.io/spring-boot/](https://projects.spring.io/spring-boot/))
121+
- RESTFul ([https://blog.mwaysolutions.com/2014/06/05/10-best-practices-for-better-restful-api/](https://blog.mwaysolutions.com/2014/06/05/10-best-practices-for-better-restful-api/))
122+
- DDD ([https://airbrake.io/blog/software-design/domain-driven-design](https://airbrake.io/blog/software-design/domain-driven-design))
123+
- Microservices ([https://martinfowler.com/microservices/](https://martinfowler.com/microservices/))
124+
- Testes unitários, teste o que achar importante (De preferência JUnit + Mockito). Mas pode usar o que você tem mais experiência, só nos explique o que ele tem de bom.
125+
- SOAPUI para testes de carga ([https://www.soapui.org/load-testing/concept.html](https://www.soapui.org/load-testing/concept.html))
126+
- Uso de diferentes formas de armazenamento de dados (REDIS, Cassandra, Solr/Lucene)
127+
- Uso do git
128+
- Diferencial: Criptografia de comunicação, com troca de chaves. ([http://noiseprotocol.org/](http://noiseprotocol.org/))
129+
- Diferencial: CQRS ([https://martinfowler.com/bliki/CQRS.html](https://martinfowler.com/bliki/CQRS.html))
130+
- Diferencial: Docker File + Docker Compose (com dbs) para rodar seus jars.
66131

67132
### # Observações gerais
68133

auth/ExpenseManagement.Authentication.Api/Repositories/UserRepository.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ public class UserRepository
77
public static User Get(string email, string password)
88
{
99
var users = new List<User>();
10-
users.Add(new User { Id = 1, Email= "empresa-xpto@gmail.com", Username = "EMPRESA XPTO", Password = "123", Role = "system" });
11-
users.Add(new User { Id = 1234, Email="marcelo@gmail.com", Username = "Marcelo", Password = "123", Role = "client" });
10+
users.Add(new User { Id = 1, Email= "empresa-teste@gmail.com", Username = "Empresa Teste", Password = "123", Role = "system" });
11+
users.Add(new User { Id = 1234, Email="john.doe@gmail.com", Username = "Marcelo", Password = "123", Role = "client" });
1212
return users.Where(x => x.Email.ToLower() == email.ToLower() && x.Password == password).FirstOrDefault();
1313
}
1414
}

back/ExpenseManagement.Api.Application/ApplicationModule.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ public static IServiceCollection AddApplication(this IServiceCollection services
1919
private static IServiceCollection AddApplicationServices(this IServiceCollection services)
2020
{
2121
services.AddScoped<IExpenseManagementService, ExpenseManagementService>();
22+
services.AddScoped<ICategoryService, CategoryService>();
2223

2324
return services;
2425
}

back/ExpenseManagement.Api.Application/Dto/SpentDto.cs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -59,13 +59,16 @@ public static SpentDto FromEntity(Spent entity)
5959
/// <returns>Spent</returns>
6060
public static Spent FromDto(SpentDto dto)
6161
{
62-
return new Spent(
63-
dto.CodeUser,
64-
dto.Description,
65-
dto.Value,
66-
dto.PostedAt,
67-
dto.Category
68-
);
62+
Spent spent = new Spent()
63+
{
64+
CodeUser = dto.CodeUser,
65+
Description = dto.Description,
66+
Value = dto.Value,
67+
Category = dto.Category,
68+
PostedAt = dto.PostedAt
69+
};
70+
71+
return spent;
6972
}
7073
}
7174
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
using ExpenseManagement.Core.Entities;
2+
using ExpenseManagement.Core.Repositories;
3+
4+
namespace ExpenseManagement.Api.Application.Services
5+
{
6+
public class CategoryService : ICategoryService
7+
{
8+
private readonly ICategoryRepository _repository;
9+
10+
/// <summary>
11+
/// Construtor
12+
/// </summary>
13+
/// <param name="repository">repositório</param>
14+
public CategoryService(ICategoryRepository repository)
15+
{
16+
_repository = repository;
17+
}
18+
19+
/// <inheritdoc />
20+
public async Task<List<Category>> GetAll()
21+
{
22+
var listCategoriesFromDb = await _repository.GetAll();
23+
return listCategoriesFromDb;
24+
}
25+
26+
public List<Category> GetByFilter(string search)
27+
{
28+
var listCategoriesFromDb = _repository.GetByFilter(search);
29+
return listCategoriesFromDb;
30+
}
31+
}
32+
}

back/ExpenseManagement.Api.Application/Services/ExpenseManagementService.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ public async Task<string> AddAsync(SpentDto dto)
2727

2828
if (string.IsNullOrEmpty(spent.Category))
2929
spent.Category = await FindCategoryContentDescription(spent.CodeUser, spent.Description);
30+
31+
spent.PostedAt = DateTime.Now.Date;
3032

3133
await _repository.AddAsync(spent);
3234

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
using ExpenseManagement.Core.Entities;
2+
3+
namespace ExpenseManagement.Api.Application.Services
4+
{
5+
public interface ICategoryService
6+
{
7+
/// <summary>
8+
/// Recupera todas as categorias cadastradas.
9+
/// </summary>
10+
/// <returns>Lista de strings</returns>
11+
Task<List<Category>> GetAll();
12+
13+
/// <summary>
14+
/// TODO
15+
/// </summary>
16+
/// <param name="search"></param>
17+
/// <returns></returns>
18+
List<Category> GetByFilter(string search);
19+
}
20+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
using ExpenseManagement.Api.Application.Services;
2+
using Microsoft.AspNetCore.Authorization;
3+
using Microsoft.AspNetCore.Mvc;
4+
5+
namespace ExpenseManagement.Api.Controllers
6+
{
7+
[ApiController]
8+
[Route("api/category")]
9+
public class CategoryController : ControllerBase
10+
{
11+
private readonly ICategoryService _service;
12+
/// <summary>
13+
/// Construtor
14+
/// </summary>
15+
/// <param name="service">service</param>
16+
public CategoryController(ICategoryService service)
17+
{
18+
_service = service;
19+
}
20+
21+
/// <summary>
22+
/// Retorna os gastos efetuados no cartão de crédito dado o código do cliente.
23+
/// </summary>
24+
/// <param name="usercode">Código do cliente</param>
25+
/// <returns>Listagem de gastos efetuados no cartão do cliente.</returns>
26+
[HttpGet("getall")]
27+
[Authorize(Roles = "client")]
28+
public async Task<IActionResult> GetAll()
29+
{
30+
var categories = await _service.GetAll();
31+
32+
if (categories == null)
33+
return NotFound();
34+
35+
return Ok(categories);
36+
}
37+
38+
/// <summary>
39+
/// Retorna os gastos efetuados no cartão de crédito dado o código do cliente.
40+
/// </summary>
41+
/// <param name="usercode">Código do cliente</param>
42+
/// <returns>Listagem de gastos efetuados no cartão do cliente.</returns>
43+
[HttpGet("getbyfilter/{search}")]
44+
[Authorize(Roles = "client")]
45+
public IActionResult GetByFilter(string search)
46+
{
47+
var categories = _service.GetByFilter(search);
48+
49+
if (categories == null)
50+
return NotFound();
51+
52+
return Ok(categories);
53+
}
54+
}
55+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using MongoDB.Bson;
2+
using MongoDB.Bson.Serialization.Attributes;
3+
using Newtonsoft.Json;
4+
5+
namespace ExpenseManagement.Core.Entities
6+
{
7+
public class Category
8+
{
9+
[BsonId]
10+
[BsonRepresentation(BsonType.ObjectId)]
11+
[JsonProperty("id")]
12+
public string Id { get; set; }
13+
public string Description { get; set; }
14+
}
15+
}
Lines changed: 6 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,19 @@
11
using MongoDB.Bson;
22
using MongoDB.Bson.Serialization.Attributes;
33
using Newtonsoft.Json;
4-
using System;
5-
using System.Text.Json.Serialization;
64

75
namespace ExpenseManagement.Core.Entities
86
{
9-
public class Spent
7+
public class Spent
108
{
11-
/// <summary>
12-
///
13-
/// </summary>
14-
/// <param name="codeUser"></param>
15-
/// <param name="description"></param>
16-
/// <param name="value"></param>
17-
/// <param name="postedAt"></param>
18-
/// <param name="category"></param>
19-
public Spent(long codeUser, string description, double value, DateTime postedAt, string category)
20-
{
21-
CodeUser = codeUser;
22-
Description = description;
23-
Value = value;
24-
PostedAt = postedAt;
25-
Category = category;
26-
}
27-
28-
299
[BsonId]
30-
//[BsonRepresentation(BsonType.String)]
3110
[BsonRepresentation(BsonType.ObjectId)]
3211
[JsonProperty("id")]
33-
//public Guid Id { get; set; }
3412
public string Id { get; set; }
35-
public long CodeUser { get; private set; }
36-
public string? Description { get; private set; }
37-
public double Value { get; private set; }
38-
public DateTime PostedAt { get; private set; }
13+
public long CodeUser { get; set; }
14+
public string? Description { get; set; }
15+
public double Value { get; set; }
16+
public DateTime PostedAt { get; set; }
3917
public string? Category { get; set; }
4018
}
41-
}
19+
}

0 commit comments

Comments
 (0)