Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 76 additions & 63 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,76 +1,89 @@
# Show me the code
# Springboot + Eureka + Zuul + Feign

### # DESAFIO:
API REST for Expense Management!

API REST para Gestão de Gastos!
###Enviroment Configuration
<b>Redis: </b> https://redis.io/download
<b>Lombok: </b> https://projectlombok.org/setup/eclipse
<i>Lombok Tutorial: </i> https://projectlombok.org/setup/eclipse
<b>MySQL: </b> https://dev.mysql.com/downloads/
<b>Postman: </b> https://www.getpostman.com/downloads/

Order to run the applications: 1. eurekaserver / 2. category-management / 3. expense-management / 4. zuul

<b>Note:</b> Before you use all endpoints, I personally suggest to add some expenses first and use the same as well to search for what you need.
I didn't populated the base and all of the values below are merely illustrative.

How to use cURL code:
```
Funcionalidade: Integração de gastos por cartão
Apenas sistemas credenciados poderão incluir novos gastos
É esperado um volume de 100.000 inclusões por segundo
Os gastos, serão informados atraves do protoloco JSON, seguindo padrão:
{ "descricao": "alfanumerico", "valor": double americano, "codigousuario": numerico, "data": Data dem formato UTC }
```
```
Funcionalidade: Listagem de gastos*
Dado que acesso como um cliente autenticado que pode visualizar os gastos do cartão
Quando acesso a interface de listagem de gastos
Então gostaria de ver meus gastos mais atuais.

*Para esta funcionalidade é esperado 2.000 acessos por segundo.
*O cliente espera ver gastos realizados a 5 segundos atrás.
```
```
Funcionalidade: Filtro de gastos
Dado que acesso como um cliente autenticado
E acessei a interface de listagem de gastos
E configure o filtro de data igual a 27/03/1992
Então gostaria de ver meus gastos apenas deste dia.
```
```
Funcionalidade: Categorização de gastos
Dado que acesso como um cliente autenticado
Quando acesso o detalhe de um gasto
E este não possui uma categoria
Então devo conseguir incluir uma categoria para este
```
```
Funcionalidade: Sugestão de categoria
Dado que acesso como um cliente autenticado
Quando acesso o detalhe do gasto que não possui categoria
E começo a digitar a categoria que desejo
Então uma lista de sugestões de categoria deve ser exibida, estas baseadas em categorias já informadas por outro usuários.
```
1. Open postman.exe
2. CTRL + O
3. Choose: Past Raw Text
4. Paste the cURL code below.
5. Click on button send to run the endpoint.
```
Funcionalidade: Categorização automatica de gasto
No processo de integração de gastos, a categoria deve ser incluida automaticamente
caso a descrição de um gasto seja igual a descrição de qualquer outro gasto já categorizado pelo cliente
o mesmo deve receber esta categoria no momento da inclusão do mesmo

Endpoints:
Expense:
```
### # Avaliação
* Add a new expense
POST http:localhost:8762/expense-management/expenses
cURL:
curl -X POST \
http://http:localhost:8762/expense-management/expenses \
-H 'Postman-Token: c79ad816-5fe0-4ea2-8647-cb4251931543' \
-H 'cache-control: no-cache' \
-H 'content-type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW' \
-F 'description=O homem mais rico da babilonia' \
-F cost=30.4 \
-F category=Leitura

Você será avaliado pela usabilidade, por respeitar o design e pela arquitetura da API.
É esperado que você consiga explicar as decisões que tomou durante o desenvolvimento através de commits.
* Update a new expense
PUT http:localhost:8762/expense-management/expenses/1
cURL:
curl -X POST \
http://http:localhost:8762/expense-management/expenses/1 \
-H 'Postman-Token: de311b6c-9101-4133-ae76-05f9efc6578c' \
-H 'cache-control: no-cache' \
-H 'content-type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW' \
-F codUser=1 \
-F 'description=O homem mais rico da babilonia' \
-F cost=30.4 \
-F category=Leitura

* Springboot - Java - Maven (preferêncialmente) ([https://projects.spring.io/spring-boot/](https://projects.spring.io/spring-boot/))
* 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/))
* DDD ([https://airbrake.io/blog/software-design/domain-driven-design](https://airbrake.io/blog/software-design/domain-driven-design))
* Microservices ([https://martinfowler.com/microservices/](https://martinfowler.com/microservices/))
* 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.
* SOAPUI para testes de carga ([https://www.soapui.org/load-testing/concept.html](https://www.soapui.org/load-testing/concept.html))
* Uso de diferentes formas de armazenamento de dados (REDIS, Cassandra, Solr/Lucene)
* Uso do git
* Diferencial: Criptografia de comunicação, com troca de chaves. ([http://noiseprotocol.org/](http://noiseprotocol.org/))
* Diferencial: CQRS ([https://martinfowler.com/bliki/CQRS.html](https://martinfowler.com/bliki/CQRS.html))
* Diferencial: Docker File + Docker Compose (com dbs) para rodar seus jars.
* Search for expenses by user
GET http:localhost:8762/expense-management/expenses/1

* Search for expenses by user in a specific date
http:localhost:8762/expense-management/expenses/1/2019-04-06
```

### # Observações gerais

Adicione um arquivo [README.md](http://README.md) com os procedimentos para executar o projeto.
Pedimos que trabalhe sozinho e não divulgue o resultado na internet.

Faça um fork desse desse repositório em seu Github e nos envie um Pull Request com o resultado, por favor informe por qual empresa você esta se candidatando.
Category:
```
* Searching for similar name: Autocomplete usability
GET http:localhost:8762/category-management/categories/suggest?name=Roup
* Search by exact name
GET http:localhost:8762/category-management/category/Comida
* Add Category
POST http:localhost:8762/category-management/categories
cURL:
curl -X POST \
http://http:localhost:8762/category-management/categories \
-H 'Postman-Token: 87f38883-4711-4d4a-a185-87425fa7b2ac' \
-H 'cache-control: no-cache' \
-H 'content-type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW' \
-F name=Saques

### # Importante: não há prazo de entrega, faça com qualidade!
* Update category
http:localhost:8762/category-management/categories/1
cURL:
curl -X POST \
http://http:localhost:8762/category-management/categories/1 \
-H 'Postman-Token: 86de2127-2c50-4e35-9094-af4c07bbf4e8' \
-H 'cache-control: no-cache' \
-H 'content-type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW' \
-F name=Saques
```

# BOA SORTE!
4 changes: 4 additions & 0 deletions category-management/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/target/
*.classpath
*.project
.settings/
98 changes: 98 additions & 0 deletions category-management/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>br.com.camaroti.alex.rest.api</groupId>
<artifactId>category-management</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>category-management</name>


<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
</parent>

<properties>
<java.version>1.8</java.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
</dependency>

<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<type>jar</type>
</dependency>

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
<scope>runtime</scope>
</dependency>

<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.10.19</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>2.1.1.RELEASE</version>
</dependency>


</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package br.com.camaroti.alex.rest.api.category.configuration;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.repository.configuration.EnableRedisRepositories;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@SpringBootApplication
@EnableAutoConfiguration
@EntityScan(basePackages = { "br.com.camaroti.alex.rest.api.category.domain" })
@ComponentScan(basePackages = { "br.com.camaroti.alex.rest.api.category.controller",
"br.com.camaroti.alex.rest.api.category.service", "class br.com.camaroti.alex.rest.api.category.domain" })
@EnableJpaRepositories("br.com.camaroti.alex.rest.api.category.repository")
@EnableRedisRepositories("br.com.camaroti.alex.rest.api.category.service")
@EnableDiscoveryClient
public class Application {

@Bean
JedisConnectionFactory jedisConnectionFactory() {
RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();
return new JedisConnectionFactory(config);
}

@Bean
public RedisTemplate<String, String> redisTemplate() {
final RedisTemplate<String, String> template = new RedisTemplate<String, String>();
template.setConnectionFactory(jedisConnectionFactory());
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new StringRedisSerializer());
template.setHashValueSerializer(new StringRedisSerializer());
template.afterPropertiesSet();
return template;

}

public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package br.com.camaroti.alex.rest.api.category.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import br.com.camaroti.alex.rest.api.category.domain.Category;
import br.com.camaroti.alex.rest.api.category.service.CategoryService;

@RestController
public class CategoryController {

@Autowired
private CategoryService categoryService;

@GetMapping(path="/categories/suggest")
public @ResponseBody List<Category> suggestCategory(@RequestParam(value="name", required = false, defaultValue = "") String name) throws Exception {
return categoryService.findByNameContaining(name);
}

@GetMapping(path="/category/{name}")
public @ResponseBody Category findByNameIgnoreCase(@PathVariable(value="name", required = true) String name) throws Exception {
return categoryService.findByNameIgnoreCase(name);
}

@PostMapping(path="/categories")
public @ResponseBody Category addCategory(@RequestParam(value="name") String name) throws Exception {
Category category = new Category();
category.setName(name);
return categoryService.save(category);
}

}
Loading