diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..a2a3040a
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,31 @@
+HELP.md
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**
+!**/src/test/**
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+
+### VS Code ###
+.vscode/
diff --git a/README.md b/README.md
index 15d8f685..cf5c3bd5 100644
--- a/README.md
+++ b/README.md
@@ -1,76 +1,88 @@
-# Show me the code
+# Santander Way
+Gerenciamento de Gastos Way
-### # DESAFIO:
+Aplicação feita com Spring Boot, Spring Data com Banco Postgress (Permissão) e Solr(Gasto), Rest com swagger.
-API REST para Gestão de Gastos!
+
Subindo Aplicação Completa
+
+Para rodar a aplicação deve-se criar um banco Postgress(Usuario:psotgres Senha:admin)
+
+Criar um database com nome way
+
+Deve-se também criar um banco Solr (8.1.1)
+
+Entrar na pasta bin do Solr e rodar o comando
```
-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 }
-```
+.\solr start
```
-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.
+
+Para que o mesmo seja iniciado. Então rodar o comando :
+
```
+.\solr create -c GastoSolr
```
-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.
+
+Assim será adicionado um core chamado GastoSolr
+
+Então deve-se copiar o arquivo ./resources/Solr/managed-schema.xml e substituir o mesmo na pasta
+
+%SOLAR_INSTALATION_DIR%\server\solr\GastoSolr\conf
+
+pode ser necessário restartar o Solr
+
+```
+.\solr stop -p 8983
```
+
+Depois
+
```
-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
+.\solr start
```
+
+Logo após entrar na pasta do projeto rodar o comando
+
```
-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.
+mvn spring-boot:run
```
+
+O flyWay irá criar as tabelas necessárias
+
+Criará também 3 usuarios
+
+Admin (Senha : admin - Id = 1) - usuario admin
+
+User (Senha : user - Id = 2 ) - Usuário do tipo cliente que pode receber inclusão de gastos
+
+Sys (Senha : sys - Id = 3) - Usuário do tipo sistema que pode incluir gastos
+
+aplicação estará com os endpoints disponíveis em :
+
```
-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
+http://localhost:8080/swagger-ui.html
```
-### # Avaliação
-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.
+
Testes com Mokito
-* 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.
+Para rodar a classe de testes configurada com as conexão de banco mokadas (Mokito)
-### # Observações gerais
+Deve-se executar o seguinte comando:
-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.
+```
+mvn clean test -Dtest=br.com.zup.way.gasto.GastoApplicationTests
+```
+
+Com isso os testes dos serviços irão rodar sem necessidade de conexão com o banco.
+
+
Testes de Integração
-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.
+Deve-se executar o seguinte comando:
+
+```
+mvn clean test -Dtest=br.com.zup.way.gasto.GastoIntegrationTests
+```
-### # Importante: não há prazo de entrega, faça com qualidade!
+Irá ser criado um banco do Solr em mémoria e configurado alguns registros bases para o teste.
-# BOA SORTE!
+O banco postgres já deve ter sido criado para que o mesmo possa validar as informações de usuário.
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 00000000..7c7768f6
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,226 @@
+
+
+ 4.0.0
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.1.6.RELEASE
+
+
+ br.com.zup
+ way
+ 0.0.1-SNAPSHOT
+ way
+ Teste Java BackEnd
+
+
+ 1.8
+ 1.3.0.Final
+ 1.18.8
+ 2.3.5.RELEASE
+ 1.0.10.RELEASE
+
+
+
+
+ dev
+
+ true
+
+
+ dev
+
+
+
+ test
+
+ test
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-data-rest
+
+
+
+ org.springframework.boot
+ spring-boot-starter-security
+
+
+
+ io.dropwizard.metrics
+ metrics-core
+ 3.2.6
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+ org.springframework.boot
+ spring-boot-devtools
+ runtime
+ true
+
+
+
+ org.projectlombok
+ lombok
+ true
+
+
+
+ org.mapstruct
+ mapstruct
+ ${org.mapstruct.version}
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+ org.springframework.security
+ spring-security-test
+ test
+
+
+
+ io.springfox
+ springfox-swagger2
+ 2.9.2
+
+
+
+ org.springframework.boot
+ spring-boot-starter-data-solr
+
+
+
+ org.apache.solr
+ solr-solrj
+
+
+
+ org.apache.solr
+ solr-core
+
+
+
+ org.springframework.data
+ spring-data-solr
+
+
+
+
+ org.springframework.security.oauth
+ spring-security-oauth2
+ ${oauth.version}
+
+
+
+ org.springframework.security
+ spring-security-jwt
+ ${jwt.version}
+
+
+
+ io.springfox
+ springfox-swagger-ui
+ 2.9.2
+
+
+
+ org.mockito
+ mockito-core
+ 2.11.0
+ compile
+
+
+
+ org.springframework.data
+ spring-data-jpa
+
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+
+ org.postgresql
+ postgresql
+ runtime
+
+
+
+ org.apache.commons
+ commons-lang3
+
+
+
+
+
+ org.flywaydb
+ flyway-core
+ 5.2.4
+
+
+
+ com.h2database
+ h2
+ runtime
+
+
+
+ io.jsonwebtoken
+ jjwt
+ 0.9.1
+
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+ true
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.5.1
+
+ ${java.version}
+ ${java.version}
+
+
+ org.mapstruct
+ mapstruct-processor
+ ${org.mapstruct.version}
+
+
+ org.projectlombok
+ lombok
+ ${org.projectlombok.version}
+
+
+
+
+
+
+
+
+
diff --git a/src/main/java/br/com/zup/way/FlywaySlaveInitializer.java b/src/main/java/br/com/zup/way/FlywaySlaveInitializer.java
new file mode 100644
index 00000000..38b55ccc
--- /dev/null
+++ b/src/main/java/br/com/zup/way/FlywaySlaveInitializer.java
@@ -0,0 +1,27 @@
+package br.com.zup.way;
+
+import org.flywaydb.core.Flyway;
+import org.springframework.context.annotation.Configuration;
+
+import javax.annotation.PostConstruct;
+import javax.sql.DataSource;
+
+@Configuration
+public class FlywaySlaveInitializer {
+
+ private DataSource dataSource;
+
+ public FlywaySlaveInitializer(DataSource dataSource) {
+ this.dataSource = dataSource;
+ }
+
+ @PostConstruct
+ public void migrateFlyway() {
+ Flyway flyway = new Flyway();
+
+ flyway.setDataSource(dataSource);
+ flyway.setLocations("db/migration");
+ flyway.migrate();
+
+ }
+}
diff --git a/src/main/java/br/com/zup/way/WayApplication.java b/src/main/java/br/com/zup/way/WayApplication.java
new file mode 100644
index 00000000..4c85ac2b
--- /dev/null
+++ b/src/main/java/br/com/zup/way/WayApplication.java
@@ -0,0 +1,13 @@
+package br.com.zup.way;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class WayApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(WayApplication.class, args);
+ }
+
+}
diff --git a/src/main/java/br/com/zup/way/WayDbConfig.java b/src/main/java/br/com/zup/way/WayDbConfig.java
new file mode 100644
index 00000000..036ccac5
--- /dev/null
+++ b/src/main/java/br/com/zup/way/WayDbConfig.java
@@ -0,0 +1,56 @@
+package br.com.zup.way;
+
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.boot.jdbc.DataSourceBuilder;
+import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Primary;
+import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
+import org.springframework.orm.jpa.JpaTransactionManager;
+import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
+import org.springframework.transaction.PlatformTransactionManager;
+import org.springframework.transaction.annotation.EnableTransactionManagement;
+
+import javax.persistence.EntityManagerFactory;
+import javax.sql.DataSource;
+
+@Configuration
+@EnableTransactionManagement
+@EnableJpaRepositories(
+ entityManagerFactoryRef = "entityManagerFactory",
+ basePackages = {"br.com.zup.way.db.postgres.repository"}
+)
+public class WayDbConfig {
+
+ @Primary
+ @Bean(name = "dataSource")
+ @ConfigurationProperties(prefix = "spring.datasource")
+ public DataSource dataSource() {
+ return DataSourceBuilder.create().build();
+ }
+
+ @Primary
+ @Bean(name = "entityManagerFactory")
+ public LocalContainerEntityManagerFactoryBean
+ entityManagerFactory(
+ EntityManagerFactoryBuilder builder,
+ @Qualifier("dataSource") DataSource dataSource
+ ) {
+ return builder
+ .dataSource(dataSource)
+ .packages("br.com.zup.way.db.postgres.model")
+ .persistenceUnit("way")
+ .build();
+ }
+
+ @Primary
+ @Bean(name = "transactionManager")
+ public PlatformTransactionManager transactionManager(
+ @Qualifier("entityManagerFactory") EntityManagerFactory
+ entityManagerFactory
+ ) {
+ return new JpaTransactionManager(entityManagerFactory);
+ }
+}
diff --git a/src/main/java/br/com/zup/way/WaySolrDbConfig.java b/src/main/java/br/com/zup/way/WaySolrDbConfig.java
new file mode 100644
index 00000000..c92f45b8
--- /dev/null
+++ b/src/main/java/br/com/zup/way/WaySolrDbConfig.java
@@ -0,0 +1,35 @@
+package br.com.zup.way;
+
+import org.apache.solr.client.solrj.SolrClient;
+import org.apache.solr.client.solrj.impl.HttpSolrClient;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Profile;
+import org.springframework.data.solr.core.SolrTemplate;
+import org.springframework.data.solr.repository.config.EnableSolrRepositories;
+import org.springframework.transaction.annotation.EnableTransactionManagement;
+
+@Configuration
+@EnableTransactionManagement
+@EnableSolrRepositories(basePackages = "br.com.zup.way.db.solr.repository")
+@ComponentScan
+@Profile("dev")
+public class WaySolrDbConfig {
+
+ static final String CONFIGSET_DIR = "src/test/resources/configsets";
+
+ @Value("${host.solr}")
+ private String hostSolr;
+
+ @Bean
+ public SolrClient solrClient() {
+ return new HttpSolrClient.Builder().withBaseSolrUrl(hostSolr).build();
+ }
+
+ @Bean
+ public SolrTemplate solrTemplate(SolrClient client) {
+ return new SolrTemplate(client);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/br/com/zup/way/controllers/AutenticationController.java b/src/main/java/br/com/zup/way/controllers/AutenticationController.java
new file mode 100644
index 00000000..5f60cbce
--- /dev/null
+++ b/src/main/java/br/com/zup/way/controllers/AutenticationController.java
@@ -0,0 +1,35 @@
+package br.com.zup.way.controllers;
+
+import br.com.zup.way.db.postgres.model.dto.LoginDTO;
+import br.com.zup.way.db.postgres.model.dto.TokenDTO;
+import br.com.zup.way.service.security.JWTTokenService;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.validation.Valid;
+
+@RestController
+@RequestMapping("/auth")
+public class AutenticationController {
+
+ private AuthenticationManager authenticationManager;
+ private JWTTokenService tokenService;
+
+ public AutenticationController(AuthenticationManager authenticationManager, JWTTokenService tokenService) {
+ this.authenticationManager = authenticationManager;
+ this.tokenService = tokenService;
+ }
+
+ @PostMapping(path = "/")
+ public ResponseEntity authenticate(@RequestBody @Valid LoginDTO login) {
+ UsernamePasswordAuthenticationToken userNameToken = login.converter();
+ Authentication authentication = authenticationManager.authenticate(userNameToken);
+ return ResponseEntity.ok(new TokenDTO(tokenService.generateToken(authentication), "Bearer"));
+ }
+}
diff --git a/src/main/java/br/com/zup/way/controllers/GastoController.java b/src/main/java/br/com/zup/way/controllers/GastoController.java
new file mode 100644
index 00000000..18f43a52
--- /dev/null
+++ b/src/main/java/br/com/zup/way/controllers/GastoController.java
@@ -0,0 +1,51 @@
+package br.com.zup.way.controllers;
+
+import br.com.zup.way.db.solr.model.dto.FiltroCategorizarGastoDTO;
+import br.com.zup.way.db.solr.model.dto.FiltroGastoDTO;
+import br.com.zup.way.db.solr.model.dto.GastoDTO;
+import br.com.zup.way.db.solr.model.dto.IntegrateGastoDTO;
+import br.com.zup.way.security.JwtHelper;
+import br.com.zup.way.service.GastoService;
+import br.com.zup.way.util.Exception.WayBusinessException;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.Valid;
+import java.util.List;
+import java.util.Set;
+
+@RestController
+@RequestMapping
+public class GastoController {
+
+ private GastoService gastoService;
+
+ public GastoController(GastoService gastoService) {
+ this.gastoService = gastoService;
+ }
+
+ @PutMapping(path = "/sys/api/gasto/integracao")
+ public GastoDTO integrate(@RequestBody @Valid IntegrateGastoDTO gastoDTO) throws WayBusinessException {
+ return gastoService.integrateGasto(gastoDTO);
+ }
+
+ @PutMapping(path = "/user/api/gasto/categorizar")
+ public GastoDTO categorizarGasto(@RequestBody @Valid FiltroCategorizarGastoDTO filtro) throws WayBusinessException {
+ return gastoService.categorizarGasto(filtro);
+ }
+
+ @GetMapping(path = "/user/api/gasto/last")
+ public List listLast() {
+ return gastoService.findLastGastos(JwtHelper.getCodigoUsuario());
+ }
+
+ @PostMapping(path = "/user/api/gasto/find")
+ public List find(@RequestBody @Valid FiltroGastoDTO filtroData) {
+ return gastoService.findGastosByDate(JwtHelper.getCodigoUsuario(), filtroData);
+ }
+
+ @GetMapping(path = "/user/api/gasto/categoria/{nomeCategoria}")
+ public Set suggestCategoria(@PathVariable("nomeCategoria") String nomeCategoria) {
+ return gastoService.findCategoria(nomeCategoria);
+ }
+
+}
diff --git a/src/main/java/br/com/zup/way/db/postgres/model/Perfil.java b/src/main/java/br/com/zup/way/db/postgres/model/Perfil.java
new file mode 100644
index 00000000..0a6411a7
--- /dev/null
+++ b/src/main/java/br/com/zup/way/db/postgres/model/Perfil.java
@@ -0,0 +1,29 @@
+package br.com.zup.way.db.postgres.model;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.springframework.security.core.GrantedAuthority;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+
+@Entity
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+class Perfil implements GrantedAuthority {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+
+ private String nome;
+
+ @Override
+ public String getAuthority() {
+ return this.nome;
+ }
+}
diff --git a/src/main/java/br/com/zup/way/db/postgres/model/Usuario.java b/src/main/java/br/com/zup/way/db/postgres/model/Usuario.java
new file mode 100644
index 00000000..cfaa3711
--- /dev/null
+++ b/src/main/java/br/com/zup/way/db/postgres/model/Usuario.java
@@ -0,0 +1,67 @@
+package br.com.zup.way.db.postgres.model;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.userdetails.UserDetails;
+
+import javax.persistence.*;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+@Entity
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class Usuario implements UserDetails {
+
+
+ @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+ private String nome;
+ private String login;
+ private String senha;
+
+ private Integer tipo;
+
+ @ManyToMany(fetch = FetchType.EAGER)
+ private List perfis = new ArrayList();
+
+ @Override
+ public Collection extends GrantedAuthority> getAuthorities() {
+ return this.perfis;
+ }
+
+ @Override
+ public String getPassword() {
+ return this.senha;
+ }
+
+ @Override
+ public String getUsername() {
+ return this.login;
+ }
+
+ @Override
+ public boolean isAccountNonExpired() {
+ return true;
+ }
+
+ @Override
+ public boolean isAccountNonLocked() {
+ return true;
+ }
+
+ @Override
+ public boolean isCredentialsNonExpired() {
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return true;
+ }
+
+}
diff --git a/src/main/java/br/com/zup/way/db/postgres/model/dto/ErrorDTO.java b/src/main/java/br/com/zup/way/db/postgres/model/dto/ErrorDTO.java
new file mode 100644
index 00000000..55e4d5d6
--- /dev/null
+++ b/src/main/java/br/com/zup/way/db/postgres/model/dto/ErrorDTO.java
@@ -0,0 +1,15 @@
+package br.com.zup.way.db.postgres.model.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+
+@Getter
+@NoArgsConstructor
+@AllArgsConstructor
+public class ErrorDTO {
+
+ private String field;
+ private String error;
+
+}
\ No newline at end of file
diff --git a/src/main/java/br/com/zup/way/db/postgres/model/dto/LoginDTO.java b/src/main/java/br/com/zup/way/db/postgres/model/dto/LoginDTO.java
new file mode 100644
index 00000000..4a690feb
--- /dev/null
+++ b/src/main/java/br/com/zup/way/db/postgres/model/dto/LoginDTO.java
@@ -0,0 +1,20 @@
+package br.com.zup.way.db.postgres.model.dto;
+
+import lombok.Data;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+
+import javax.validation.constraints.NotNull;
+
+@Data
+public class LoginDTO {
+
+ @NotNull
+ private String login;
+
+ @NotNull
+ private String senha;
+
+ public UsernamePasswordAuthenticationToken converter() {
+ return new UsernamePasswordAuthenticationToken(login, senha);
+ }
+}
diff --git a/src/main/java/br/com/zup/way/db/postgres/model/dto/TokenDTO.java b/src/main/java/br/com/zup/way/db/postgres/model/dto/TokenDTO.java
new file mode 100644
index 00000000..31572896
--- /dev/null
+++ b/src/main/java/br/com/zup/way/db/postgres/model/dto/TokenDTO.java
@@ -0,0 +1,14 @@
+package br.com.zup.way.db.postgres.model.dto;
+
+import lombok.Getter;
+
+@Getter
+public class TokenDTO {
+ private String token;
+ private String type;
+
+ public TokenDTO(String token, String type) {
+ this.token = token;
+ this.type = type;
+ }
+}
diff --git a/src/main/java/br/com/zup/way/db/postgres/repository/UsuarioRepository.java b/src/main/java/br/com/zup/way/db/postgres/repository/UsuarioRepository.java
new file mode 100644
index 00000000..ae69479b
--- /dev/null
+++ b/src/main/java/br/com/zup/way/db/postgres/repository/UsuarioRepository.java
@@ -0,0 +1,13 @@
+package br.com.zup.way.db.postgres.repository;
+
+import br.com.zup.way.db.postgres.model.Usuario;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import java.util.Optional;
+
+public interface UsuarioRepository extends JpaRepository {
+
+ Optional findByLogin(String login);
+
+ Optional findByIdAndTipo(Long codUsuario, Integer tipo);
+}
diff --git a/src/main/java/br/com/zup/way/db/solr/model/Categoria.java b/src/main/java/br/com/zup/way/db/solr/model/Categoria.java
new file mode 100644
index 00000000..ce0fc1b7
--- /dev/null
+++ b/src/main/java/br/com/zup/way/db/solr/model/Categoria.java
@@ -0,0 +1,34 @@
+package br.com.zup.way.db.solr.model;
+
+import br.com.zup.way.db.solr.model.dto.CategoriaDTO;
+import br.com.zup.way.util.mapper.CategoriaMapper;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.apache.solr.client.solrj.beans.Field;
+import org.springframework.data.solr.core.mapping.SolrDocument;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+
+@Entity
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@SolrDocument(collection = "CategoriaSolr")
+public class Categoria {
+
+ @Id
+ @Field
+ private String id;
+
+ @Field("nome_text_pt")
+ private String nome;
+
+ @Field
+ private Long codigoUsuario;
+
+ public static Categoria convert(CategoriaDTO categoriaDTO) {
+ return CategoriaMapper.INSTANCE.categoriaDTOtoCategoria(categoriaDTO);
+ }
+}
diff --git a/src/main/java/br/com/zup/way/db/solr/model/Gasto.java b/src/main/java/br/com/zup/way/db/solr/model/Gasto.java
new file mode 100644
index 00000000..c1131a47
--- /dev/null
+++ b/src/main/java/br/com/zup/way/db/solr/model/Gasto.java
@@ -0,0 +1,46 @@
+package br.com.zup.way.db.solr.model;
+
+import br.com.zup.way.db.solr.model.dto.GastoDTO;
+import br.com.zup.way.db.solr.model.dto.IntegrateGastoDTO;
+import br.com.zup.way.util.mapper.GastoMapper;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.apache.solr.client.solrj.beans.Field;
+import org.springframework.data.solr.core.mapping.SolrDocument;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import java.time.LocalDateTime;
+
+@Entity
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@SolrDocument(collection = "GastoSolr")
+public class Gasto {
+
+ @Id
+ @Field
+ private String id;
+
+ @Field("descricao_text_pt")
+ private String descricao;
+
+ @Field
+ private Double valor;
+
+ @Field
+ private Long codigoUsuario;
+
+ @Field("categoria_text_pt")
+ private String categoria;
+
+ @Field("data_cadastro_dt")
+ private LocalDateTime dataCadastro;
+
+ public static Gasto convert(IntegrateGastoDTO gastoDTO) {
+ return GastoMapper.INSTANCE.gastoDTOtoGasto(gastoDTO);
+ }
+
+}
diff --git a/src/main/java/br/com/zup/way/db/solr/model/dto/CategoriaDTO.java b/src/main/java/br/com/zup/way/db/solr/model/dto/CategoriaDTO.java
new file mode 100644
index 00000000..5962b20d
--- /dev/null
+++ b/src/main/java/br/com/zup/way/db/solr/model/dto/CategoriaDTO.java
@@ -0,0 +1,27 @@
+package br.com.zup.way.db.solr.model.dto;
+
+import br.com.zup.way.db.solr.model.Categoria;
+import br.com.zup.way.util.mapper.CategoriaMapper;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class CategoriaDTO {
+
+ private String nome;
+
+ private Long codigoUsuario;
+
+ public static List convert(List categorias) {
+ return CategoriaMapper.INSTANCE.categoriaToCategoriaDTO(categorias);
+ }
+
+ public static CategoriaDTO convert(Categoria categoria) {
+ return CategoriaMapper.INSTANCE.categoriaToCategoriaDTO(categoria);
+ }
+}
diff --git a/src/main/java/br/com/zup/way/db/solr/model/dto/FiltroCategorizarGastoDTO.java b/src/main/java/br/com/zup/way/db/solr/model/dto/FiltroCategorizarGastoDTO.java
new file mode 100644
index 00000000..60c6d57c
--- /dev/null
+++ b/src/main/java/br/com/zup/way/db/solr/model/dto/FiltroCategorizarGastoDTO.java
@@ -0,0 +1,19 @@
+package br.com.zup.way.db.solr.model.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import javax.validation.constraints.NotNull;
+
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class FiltroCategorizarGastoDTO {
+
+ @NotNull
+ private String categoria;
+
+ @NotNull
+ private String idGasto;
+}
diff --git a/src/main/java/br/com/zup/way/db/solr/model/dto/FiltroGastoDTO.java b/src/main/java/br/com/zup/way/db/solr/model/dto/FiltroGastoDTO.java
new file mode 100644
index 00000000..38aa0b6a
--- /dev/null
+++ b/src/main/java/br/com/zup/way/db/solr/model/dto/FiltroGastoDTO.java
@@ -0,0 +1,20 @@
+package br.com.zup.way.db.solr.model.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import javax.validation.constraints.NotNull;
+import java.time.LocalDateTime;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class FiltroGastoDTO {
+
+ @NotNull
+ @DateTimeFormat(pattern = "dd/MM/yyyy")
+ private LocalDateTime filtroData;
+}
diff --git a/src/main/java/br/com/zup/way/db/solr/model/dto/GastoDTO.java b/src/main/java/br/com/zup/way/db/solr/model/dto/GastoDTO.java
new file mode 100644
index 00000000..e66b1dfe
--- /dev/null
+++ b/src/main/java/br/com/zup/way/db/solr/model/dto/GastoDTO.java
@@ -0,0 +1,43 @@
+package br.com.zup.way.db.solr.model.dto;
+
+import br.com.zup.way.db.solr.model.Gasto;
+import br.com.zup.way.util.mapper.GastoMapper;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+
+import javax.validation.constraints.NotNull;
+import java.time.LocalDateTime;
+import java.util.List;
+
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@EqualsAndHashCode(exclude = "id")
+public class GastoDTO {
+
+ private String id;
+
+ @NotNull
+ private String descricao;
+
+ @NotNull
+ private Double valor;
+
+ @NotNull
+ private Long codigoUsuario;
+
+ private LocalDateTime dataCadastro;
+
+ private String categoria;
+
+ public static GastoDTO convert(Gasto gasto) {
+ return GastoMapper.INSTANCE.gastoToGastoDTO(gasto);
+ }
+
+ public static List convert(List gasto) {
+ return GastoMapper.INSTANCE.gastoToGastoDTO(gasto);
+ }
+
+}
diff --git a/src/main/java/br/com/zup/way/db/solr/model/dto/IntegrateGastoDTO.java b/src/main/java/br/com/zup/way/db/solr/model/dto/IntegrateGastoDTO.java
new file mode 100644
index 00000000..27ee9810
--- /dev/null
+++ b/src/main/java/br/com/zup/way/db/solr/model/dto/IntegrateGastoDTO.java
@@ -0,0 +1,25 @@
+package br.com.zup.way.db.solr.model.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import javax.validation.constraints.NotNull;
+import java.time.LocalDateTime;
+
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class IntegrateGastoDTO {
+
+ @NotNull
+ private Long codigoUsuario;
+
+ @NotNull
+ private String descricao;
+
+ @NotNull
+ private Double valor;
+
+ private LocalDateTime dataCadastro;
+}
diff --git a/src/main/java/br/com/zup/way/db/solr/repository/GastoRepository.java b/src/main/java/br/com/zup/way/db/solr/repository/GastoRepository.java
new file mode 100644
index 00000000..c1f17d35
--- /dev/null
+++ b/src/main/java/br/com/zup/way/db/solr/repository/GastoRepository.java
@@ -0,0 +1,20 @@
+package br.com.zup.way.db.solr.repository;
+
+import br.com.zup.way.db.solr.model.Gasto;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Sort;
+import org.springframework.data.solr.repository.SolrCrudRepository;
+
+import java.util.List;
+
+public interface GastoRepository extends SolrCrudRepository {
+
+ List findGastoByDescricaoIgnoreCaseAndCodigoUsuario(String descricao, Long codigoUsuario);
+
+ List findGastoByCodigoUsuarioAndDataCadastroLessThanEqual(Long codigoUsuario, String dataLimite, PageRequest dataCadastro);
+
+ List findGastoByCodigoUsuarioAndDataCadastroBetween(Long codigoUsuario, String dataLimiteInicio, String dataLimiteFim, Sort dataCadastro);
+
+ List findGastoByCategoriaContainingIgnoreCaseAndCodigoUsuarioOrderByCategoria(String nomeCategoria, Long codigoUsuario);
+
+}
diff --git a/src/main/java/br/com/zup/way/security/JWTAutenticationFilter.java b/src/main/java/br/com/zup/way/security/JWTAutenticationFilter.java
new file mode 100644
index 00000000..3cc99047
--- /dev/null
+++ b/src/main/java/br/com/zup/way/security/JWTAutenticationFilter.java
@@ -0,0 +1,52 @@
+package br.com.zup.way.security;
+
+import br.com.zup.way.db.postgres.model.Usuario;
+import br.com.zup.way.service.UsuarioService;
+import br.com.zup.way.service.security.JWTTokenService;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+class JWTAutenticationFilter extends OncePerRequestFilter {
+
+ private static final String HEADER_STRING = "Authorization";
+
+ private JWTTokenService tokenService;
+
+ private UsuarioService usuarioService;
+
+ public JWTAutenticationFilter(JWTTokenService tokenService, UsuarioService usuarioService) {
+ this.tokenService = tokenService;
+ this.usuarioService = usuarioService;
+ }
+
+ @Override
+ protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
+ String token = getToken(httpServletRequest);
+ if (tokenService.isValidToken(token)) {
+ doAutentication(token);
+ }
+ filterChain.doFilter(httpServletRequest, httpServletResponse);
+ }
+
+ private void doAutentication(String token) {
+ Usuario user = usuarioService.findById(tokenService.getIdByToken(token));
+ SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities()));
+ }
+
+
+ private String getToken(HttpServletRequest httpServletRequest) {
+ String token = httpServletRequest.getHeader(HEADER_STRING);
+ if (StringUtils.isNotBlank(token) && token.startsWith("Bearer ")) {
+ return token.substring(7);
+ }
+ return null;
+ }
+}
diff --git a/src/main/java/br/com/zup/way/security/JwtHelper.java b/src/main/java/br/com/zup/way/security/JwtHelper.java
new file mode 100644
index 00000000..9456be60
--- /dev/null
+++ b/src/main/java/br/com/zup/way/security/JwtHelper.java
@@ -0,0 +1,10 @@
+package br.com.zup.way.security;
+
+import br.com.zup.way.db.postgres.model.Usuario;
+import org.springframework.security.core.context.SecurityContextHolder;
+
+public class JwtHelper {
+ public static Long getCodigoUsuario() {
+ return ((Usuario) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getId();
+ }
+}
diff --git a/src/main/java/br/com/zup/way/security/WebSecurityConfig.java b/src/main/java/br/com/zup/way/security/WebSecurityConfig.java
new file mode 100644
index 00000000..aad3262c
--- /dev/null
+++ b/src/main/java/br/com/zup/way/security/WebSecurityConfig.java
@@ -0,0 +1,64 @@
+package br.com.zup.way.security;
+
+import br.com.zup.way.service.UsuarioService;
+import br.com.zup.way.service.security.AutenticationService;
+import br.com.zup.way.service.security.JWTTokenService;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.HttpMethod;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.builders.WebSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.config.http.SessionCreationPolicy;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
+
+@EnableWebSecurity
+@Configuration
+public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
+
+ private AutenticationService authenticationService;
+
+ private JWTTokenService tokenService;
+
+ private UsuarioService usuarioService;
+
+ public WebSecurityConfig(AutenticationService authenticationService, JWTTokenService tokenService, UsuarioService usuarioService) {
+ this.authenticationService = authenticationService;
+ this.tokenService = tokenService;
+ this.usuarioService = usuarioService;
+ }
+
+ @Override
+ protected void configure(AuthenticationManagerBuilder auth) throws Exception {
+ auth.userDetailsService(authenticationService).passwordEncoder(new BCryptPasswordEncoder());
+ }
+
+ @Override
+ @Bean
+ protected AuthenticationManager authenticationManager() throws Exception {
+ return super.authenticationManager();
+ }
+
+ @Override
+ protected void configure(HttpSecurity http) throws Exception {
+ http.authorizeRequests()
+ .antMatchers(HttpMethod.POST, "/auth/*").permitAll()
+ .antMatchers("/admin/api/*").hasRole("ADMIN")
+ .antMatchers("/user/api/*").hasRole("USER")
+ .antMatchers("/sys/api/*").hasRole("SYS")
+ .anyRequest().authenticated()
+ .and().csrf().disable()
+ .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
+ .and().addFilterBefore(new JWTAutenticationFilter(tokenService, usuarioService), UsernamePasswordAuthenticationFilter.class);
+ }
+
+ @Override
+ public void configure(WebSecurity web) {
+ web.ignoring().antMatchers("/v2/api-docs", "/configuration/ui", "/swagger-resources/**", "/configuration/**", "/swagger-ui.html", "/webjars/**");
+ }
+
+}
diff --git a/src/main/java/br/com/zup/way/service/GastoService.java b/src/main/java/br/com/zup/way/service/GastoService.java
new file mode 100644
index 00000000..a3a360c0
--- /dev/null
+++ b/src/main/java/br/com/zup/way/service/GastoService.java
@@ -0,0 +1,24 @@
+package br.com.zup.way.service;
+
+import br.com.zup.way.db.solr.model.dto.FiltroCategorizarGastoDTO;
+import br.com.zup.way.db.solr.model.dto.FiltroGastoDTO;
+import br.com.zup.way.db.solr.model.dto.GastoDTO;
+import br.com.zup.way.db.solr.model.dto.IntegrateGastoDTO;
+import br.com.zup.way.util.Exception.WayBusinessException;
+
+import java.util.List;
+import java.util.Set;
+
+public interface GastoService {
+
+ GastoDTO categorizarGasto(FiltroCategorizarGastoDTO filtro) throws WayBusinessException;
+
+ GastoDTO integrateGasto(IntegrateGastoDTO gastoDTO) throws WayBusinessException;
+
+ List findLastGastos(Long codigoUsuario);
+
+ List findGastosByDate(Long codigoUsuario, FiltroGastoDTO filtroData);
+
+ Set findCategoria(String nomeCategoria);
+
+}
diff --git a/src/main/java/br/com/zup/way/service/UsuarioService.java b/src/main/java/br/com/zup/way/service/UsuarioService.java
new file mode 100644
index 00000000..f01d3b90
--- /dev/null
+++ b/src/main/java/br/com/zup/way/service/UsuarioService.java
@@ -0,0 +1,11 @@
+package br.com.zup.way.service;
+
+import br.com.zup.way.db.postgres.model.Usuario;
+import br.com.zup.way.util.Exception.WayBusinessException;
+
+public interface UsuarioService {
+
+ Usuario findById(Long idUsuario);
+
+ Usuario findClienteByCodigoUsuario(Long codigoUsuario) throws WayBusinessException;
+}
diff --git a/src/main/java/br/com/zup/way/service/impl/GastoServiceImpl.java b/src/main/java/br/com/zup/way/service/impl/GastoServiceImpl.java
new file mode 100644
index 00000000..6565bac2
--- /dev/null
+++ b/src/main/java/br/com/zup/way/service/impl/GastoServiceImpl.java
@@ -0,0 +1,104 @@
+package br.com.zup.way.service.impl;
+
+import br.com.zup.way.db.solr.model.Gasto;
+import br.com.zup.way.db.solr.model.dto.FiltroCategorizarGastoDTO;
+import br.com.zup.way.db.solr.model.dto.FiltroGastoDTO;
+import br.com.zup.way.db.solr.model.dto.GastoDTO;
+import br.com.zup.way.db.solr.model.dto.IntegrateGastoDTO;
+import br.com.zup.way.db.solr.repository.GastoRepository;
+import br.com.zup.way.security.JwtHelper;
+import br.com.zup.way.service.GastoService;
+import br.com.zup.way.service.UsuarioService;
+import br.com.zup.way.util.DateUtil;
+import br.com.zup.way.util.Exception.WayBusinessException;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Sort;
+import org.springframework.stereotype.Service;
+
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+@Service
+public class GastoServiceImpl implements GastoService {
+
+ private GastoRepository gastoRepository;
+
+ private UsuarioService usuarioService;
+
+ public GastoServiceImpl(GastoRepository gastoRepository, UsuarioService usuarioService) {
+ this.gastoRepository = gastoRepository;
+ this.usuarioService = usuarioService;
+ }
+
+ @Override
+ public GastoDTO integrateGasto(IntegrateGastoDTO gastoDTO) throws WayBusinessException {
+ validateCodigoUsuario(gastoDTO.getCodigoUsuario());
+ Gasto gasto = Gasto.convert(gastoDTO);
+ if (gasto.getDataCadastro() == null) {
+ gasto.setDataCadastro(DateUtil.setZone(LocalDateTime.now()));
+ }
+ gasto.setCategoria(findCategoriaByGasto(gastoDTO));
+ return GastoDTO.convert(gastoRepository.save(gasto));
+ }
+
+ @Override
+ public GastoDTO categorizarGasto(FiltroCategorizarGastoDTO filtro) throws WayBusinessException {
+ Gasto gasto = findById(filtro.getIdGasto());
+ if (StringUtils.isNotBlank(gasto.getCategoria())) {
+ throw new WayBusinessException("Esse gasto já foi categorizado");
+ }
+ gasto.setCategoria(filtro.getCategoria());
+ return GastoDTO.convert(gastoRepository.save(gasto));
+ }
+
+ @Override
+ public List findLastGastos(Long codigoUsuario) {
+ return GastoDTO.convert(
+ gastoRepository.findGastoByCodigoUsuarioAndDataCadastroLessThanEqual(
+ codigoUsuario, DateUtil.format(LocalDateTime.now().minusSeconds(5)),
+ PageRequest.of(0, 15,
+ Sort.by(Sort.Order.desc("dataCadastro"), Sort.Order.asc("descricao")))
+ )
+ );
+ }
+
+ @Override
+ public List findGastosByDate(Long codigoUsuario, FiltroGastoDTO filtroData) {
+ return GastoDTO.convert(
+ gastoRepository.findGastoByCodigoUsuarioAndDataCadastroBetween(
+ codigoUsuario,
+ DateUtil.format(DateUtil.getStartDate(filtroData.getFiltroData())),
+ DateUtil.format(DateUtil.getEndDate(filtroData.getFiltroData())),
+ Sort.by(Sort.Order.desc("dataCadastro"))
+ )
+ );
+ }
+
+ @Override
+ public Set findCategoria(String nomeCategoria) {
+ List gastos =
+ gastoRepository.findGastoByCategoriaContainingIgnoreCaseAndCodigoUsuarioOrderByCategoria(nomeCategoria, JwtHelper.getCodigoUsuario());
+ return gastos.stream().map(Gasto::getCategoria).collect(Collectors.toSet());
+ }
+
+ /**
+ * Private Methods
+ */
+
+ private void validateCodigoUsuario(Long codigoUsuario) throws WayBusinessException {
+ usuarioService.findClienteByCodigoUsuario(codigoUsuario);
+ }
+
+ private String findCategoriaByGasto(IntegrateGastoDTO gastoDTO) {
+ List gastos =
+ gastoRepository.findGastoByDescricaoIgnoreCaseAndCodigoUsuario(gastoDTO.getDescricao(), gastoDTO.getCodigoUsuario());
+ return gastos.stream().findFirst().orElse(new Gasto()).getCategoria();
+ }
+
+ private Gasto findById(String idGasto) throws WayBusinessException {
+ return gastoRepository.findById(idGasto).orElseThrow(() -> new WayBusinessException("Gasto não Encontrado."));
+ }
+}
diff --git a/src/main/java/br/com/zup/way/service/impl/UsuarioServiceImpl.java b/src/main/java/br/com/zup/way/service/impl/UsuarioServiceImpl.java
new file mode 100644
index 00000000..3830cdf1
--- /dev/null
+++ b/src/main/java/br/com/zup/way/service/impl/UsuarioServiceImpl.java
@@ -0,0 +1,31 @@
+package br.com.zup.way.service.impl;
+
+import br.com.zup.way.db.postgres.model.Usuario;
+import br.com.zup.way.db.postgres.repository.UsuarioRepository;
+import br.com.zup.way.service.UsuarioService;
+import br.com.zup.way.util.Exception.WayBusinessException;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.stereotype.Service;
+
+@Service
+public class UsuarioServiceImpl implements UsuarioService {
+
+ private static final Integer CLIENTE = 1;
+
+ private UsuarioRepository usuarioRepository;
+
+ public UsuarioServiceImpl(UsuarioRepository usuarioRepository) {
+ this.usuarioRepository = usuarioRepository;
+ }
+
+ public Usuario findById(Long idUsuario) {
+ return usuarioRepository.findById(idUsuario)
+ .orElseThrow(() -> new UsernameNotFoundException("Usuario não Encontrado"));
+ }
+
+ @Override
+ public Usuario findClienteByCodigoUsuario(Long codigoUsuario) throws WayBusinessException {
+ return usuarioRepository.findByIdAndTipo(codigoUsuario, CLIENTE)
+ .orElseThrow(() -> new WayBusinessException("Usuario não Encontrado"));
+ }
+}
diff --git a/src/main/java/br/com/zup/way/service/security/AutenticationService.java b/src/main/java/br/com/zup/way/service/security/AutenticationService.java
new file mode 100644
index 00000000..5dc8e382
--- /dev/null
+++ b/src/main/java/br/com/zup/way/service/security/AutenticationService.java
@@ -0,0 +1,24 @@
+package br.com.zup.way.service.security;
+
+import br.com.zup.way.db.postgres.repository.UsuarioRepository;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.stereotype.Service;
+
+@Service
+public class AutenticationService implements UserDetailsService {
+
+ private UsuarioRepository usuarioRepository;
+
+ public AutenticationService(UsuarioRepository usuarioRepository) {
+ this.usuarioRepository = usuarioRepository;
+ }
+
+ @Override
+ public UserDetails loadUserByUsername(String login) throws UsernameNotFoundException {
+ return usuarioRepository.findByLogin(login)
+ .orElseThrow(() -> new UsernameNotFoundException("Username: " + login + " não encontrado"));
+ }
+
+}
diff --git a/src/main/java/br/com/zup/way/service/security/JWTTokenService.java b/src/main/java/br/com/zup/way/service/security/JWTTokenService.java
new file mode 100644
index 00000000..3f373c40
--- /dev/null
+++ b/src/main/java/br/com/zup/way/service/security/JWTTokenService.java
@@ -0,0 +1,49 @@
+package br.com.zup.way.service.security;
+
+import br.com.zup.way.db.postgres.model.Usuario;
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.SignatureAlgorithm;
+import io.jsonwebtoken.SignatureException;
+import org.apache.commons.lang3.time.DateUtils;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.security.core.Authentication;
+import org.springframework.stereotype.Service;
+
+import java.util.Date;
+
+@Service
+public class JWTTokenService {
+
+ @Value("${way.jwt.expiration}")
+ private String expiration;
+
+ @Value("${way.jwt.secret}")
+ private String secret;
+
+
+ public String generateToken(Authentication authentication) {
+ return Jwts.builder()
+ .setIssuer("API Way")
+ .setSubject(((Usuario) authentication.getPrincipal()).getId().toString())
+ .setIssuedAt(new Date())
+ .setExpiration(DateUtils.addMilliseconds(new Date(), Integer.valueOf(expiration)))
+ .signWith(SignatureAlgorithm.HS256, secret)
+ .compact();
+ }
+
+ public boolean isValidToken(String token) {
+ try {
+ if (token != null) {
+ Jwts.parser().setSigningKey(secret).parseClaimsJws(token);
+ return true;
+ }
+ } catch (SignatureException sign) {
+ return false;
+ }
+ return false;
+ }
+
+ public Long getIdByToken(String token) {
+ return Long.parseLong(Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody().getSubject());
+ }
+}
diff --git a/src/main/java/br/com/zup/way/util/DateUtil.java b/src/main/java/br/com/zup/way/util/DateUtil.java
new file mode 100644
index 00000000..0a9034ac
--- /dev/null
+++ b/src/main/java/br/com/zup/way/util/DateUtil.java
@@ -0,0 +1,29 @@
+package br.com.zup.way.util;
+
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+
+public class DateUtil {
+
+ private static final String PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";
+
+ public static LocalDateTime getStartDate(LocalDateTime dateToConvert) {
+ return setZone((dateToConvert.toLocalDate()).atStartOfDay());
+ }
+
+ public static LocalDateTime getEndDate(LocalDateTime dateToConvert) {
+ return setZone((dateToConvert.toLocalDate()).atTime(23, 59, 59));
+ }
+
+ public static LocalDateTime setZone(LocalDateTime localDateTime) {
+ return localDateTime
+ .atZone(ZoneId.of("UTC"))
+ .toLocalDateTime();
+ }
+
+ public static String format(LocalDateTime dateTime) {
+ return ZonedDateTime.of(dateTime, ZoneId.of("UTC")).format(DateTimeFormatter.ofPattern(PATTERN));
+ }
+}
diff --git a/src/main/java/br/com/zup/way/util/Exception/WayBusinessException.java b/src/main/java/br/com/zup/way/util/Exception/WayBusinessException.java
new file mode 100644
index 00000000..d0cb3897
--- /dev/null
+++ b/src/main/java/br/com/zup/way/util/Exception/WayBusinessException.java
@@ -0,0 +1,11 @@
+package br.com.zup.way.util.Exception;
+
+public class WayBusinessException extends Exception {
+
+ private static final long serialVersionUID = 1L;
+
+ public WayBusinessException(String msg) {
+ super(msg);
+ }
+
+}
diff --git a/src/main/java/br/com/zup/way/util/mapper/CategoriaMapper.java b/src/main/java/br/com/zup/way/util/mapper/CategoriaMapper.java
new file mode 100644
index 00000000..d0770baf
--- /dev/null
+++ b/src/main/java/br/com/zup/way/util/mapper/CategoriaMapper.java
@@ -0,0 +1,22 @@
+package br.com.zup.way.util.mapper;
+
+import br.com.zup.way.db.solr.model.Categoria;
+import br.com.zup.way.db.solr.model.dto.CategoriaDTO;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.factory.Mappers;
+
+import java.util.List;
+
+@Mapper
+public interface CategoriaMapper {
+
+ CategoriaMapper INSTANCE = Mappers.getMapper(CategoriaMapper.class);
+
+ CategoriaDTO categoriaToCategoriaDTO(Categoria categoria);
+
+ List categoriaToCategoriaDTO(List categoria);
+
+ @Mapping(target = "id", ignore = true)
+ Categoria categoriaDTOtoCategoria(CategoriaDTO categoria);
+}
diff --git a/src/main/java/br/com/zup/way/util/mapper/GastoMapper.java b/src/main/java/br/com/zup/way/util/mapper/GastoMapper.java
new file mode 100644
index 00000000..3729ff6a
--- /dev/null
+++ b/src/main/java/br/com/zup/way/util/mapper/GastoMapper.java
@@ -0,0 +1,23 @@
+package br.com.zup.way.util.mapper;
+
+import br.com.zup.way.db.solr.model.Gasto;
+import br.com.zup.way.db.solr.model.dto.GastoDTO;
+import br.com.zup.way.db.solr.model.dto.IntegrateGastoDTO;
+import org.mapstruct.Mapper;
+import org.mapstruct.factory.Mappers;
+
+import java.util.List;
+
+@Mapper
+public interface GastoMapper {
+
+ GastoMapper INSTANCE = Mappers.getMapper(GastoMapper.class);
+
+ GastoDTO gastoToGastoDTO(Gasto gasto);
+
+ List gastoToGastoDTO(List gasto);
+
+ Gasto gastoDTOtoGasto(GastoDTO gasto);
+
+ Gasto gastoDTOtoGasto(IntegrateGastoDTO gastoDTO);
+}
diff --git a/src/main/java/br/com/zup/way/util/swagger/SwaggerConfig.java b/src/main/java/br/com/zup/way/util/swagger/SwaggerConfig.java
new file mode 100644
index 00000000..6ce45e1f
--- /dev/null
+++ b/src/main/java/br/com/zup/way/util/swagger/SwaggerConfig.java
@@ -0,0 +1,50 @@
+package br.com.zup.way.util.swagger;
+
+import br.com.zup.way.db.postgres.model.Usuario;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import springfox.documentation.builders.ApiInfoBuilder;
+import springfox.documentation.builders.ParameterBuilder;
+import springfox.documentation.builders.RequestHandlerSelectors;
+import springfox.documentation.schema.ModelRef;
+import springfox.documentation.service.ApiInfo;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spring.web.plugins.Docket;
+import springfox.documentation.swagger2.annotations.EnableSwagger2;
+
+import java.util.Arrays;
+
+@Configuration
+@EnableSwagger2
+public class SwaggerConfig {
+
+ @Bean
+ public Docket api() {
+ return new Docket(DocumentationType.SWAGGER_2)
+ .select()
+ .apis(RequestHandlerSelectors.basePackage("br.com.zup.way.controller"))
+ .build()
+ .apiInfo(apiInfo())
+ .ignoredParameterTypes(Usuario.class)
+ .globalOperationParameters(
+ Arrays.asList(new ParameterBuilder()
+ .name("Authorization")
+ .description("Header for Token JWT")
+ .modelRef(new ModelRef("string"))
+ .parameterType("header")
+ .required(false).build()
+ )
+ );
+ }
+
+ private ApiInfo apiInfo() {
+ return new ApiInfoBuilder()
+ .title("Gestão de Gastos")
+ .description("\"API REST para Gestão de Gastos\"")
+ .version("1.0.0")
+ .license("Apache License Version 2.0")
+ .licenseUrl("https://www.apache.org/licenses/LICENSE-2.0\"")
+ .build();
+ }
+}
+
diff --git a/src/main/resources/Solr/managed-schema.xml b/src/main/resources/Solr/managed-schema.xml
new file mode 100644
index 00000000..7f68b504
--- /dev/null
+++ b/src/main/resources/Solr/managed-schema.xml
@@ -0,0 +1,1018 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ id
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
new file mode 100644
index 00000000..f30c8236
--- /dev/null
+++ b/src/main/resources/application.properties
@@ -0,0 +1,24 @@
+way.jwt.expiration=86400000
+way.jwt.secret=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE1NjE0NzExMDksImV4cCI6MTU2MTQ3MTEwNywiYXVkIjoid3d3LmV4YW1wbGUuY29tIiwic3ViIjoianJvY2tldEBleGFtcGxlLmNvbSIsIkdpdmVuTmFtZSI6IkpvaG5ueSIsIlN1cm5hbWUiOiJSb2NrZXQiLCJFbWFpbCI6Impyb2NrZXRAZXhhbXBsZS5jb20iLCJSb2xlIjpbIk1hbmFnZXIiLCJQcm9qZWN0IEFkbWluaXN0cmF0b3IiXX0.9ZnHxfOJs9ikez4ms8ZVNQHkaieQ-BON40mrLEKQGwU
+
+spring.main.allow-bean-definition-overriding=true
+
+# DATASOURCE
+
+spring.profiles.active=@spring.profiles.active@
+spring.jpa.generate-ddl=false
+spring.jpa.hibernate.ddl-auto=none
+spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
+spring.jpa.properties.hibernate.default_schema=PUBLIC
+spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true
+spring.flyway.clean-on-validation-error=true
+spring.jpa.database=default
+
+# POSTGRES DATASOURCE
+spring.datasource.jdbcUrl=jdbc:postgresql://localhost:5432/way
+spring.datasource.username=postgres
+spring.datasource.password=admin
+spring.datasource.driver-class-name=org.postgresql.Driver
+
+# Solar Datasource
+host.solr=http://localhost:8983/solr
\ No newline at end of file
diff --git a/src/main/resources/db/migration/V1__Create.sql b/src/main/resources/db/migration/V1__Create.sql
new file mode 100644
index 00000000..ab444ca1
--- /dev/null
+++ b/src/main/resources/db/migration/V1__Create.sql
@@ -0,0 +1,54 @@
+CREATE TABLE "usuario"
+(
+ "id" SERIAL NOT NULL,
+ "nome" TEXT NOT NULL,
+ "login" TEXT NOT NULL,
+ "senha" TEXT NOT NULL,
+ "tipo" INTEGER NOT NULL,
+ CONSTRAINT usuario_pk PRIMARY KEY ("id")
+) WITH (
+ OIDS=FALSE
+);
+
+CREATE TABLE "perfil"
+(
+ "id" SERIAL NOT NULL,
+ "nome" TEXT NOT NULL,
+ CONSTRAINT perfil_pk PRIMARY KEY ("id")
+) WITH (
+ OIDS=FALSE
+);
+
+CREATE TABLE "usuario_perfil"
+(
+ "usuario_id" SERIAL NOT NULL,
+ "perfis_id" SERIAL NOT NULL,
+ FOREIGN KEY (usuario_id) REFERENCES "usuario" (id),
+ FOREIGN KEY ("perfis_id") REFERENCES "perfil" (id)
+) WITH (
+ OIDS=FALSE
+);
+
+CREATE TABLE "categoria"
+(
+ "id" SERIAL NOT NULL,
+ "nome" TEXT NOT NULL,
+ CONSTRAINT categoria_pk PRIMARY KEY ("id")
+) WITH (
+ OIDS=FALSE
+);
+
+INSERT INTO "perfil" ("nome") VALUES ( 'ROLE_ADMIN');
+INSERT INTO "perfil" ("nome") VALUES ( 'ROLE_USER');
+INSERT INTO "perfil" ("nome") VALUES ( 'ROLE_SYS');
+
+INSERT INTO "usuario" ("nome", "login", "senha", "tipo") VALUES ( 'Administrador','admin','$2a$10$.ltOnHeuxrc2JLpiG8Ex0.1MT/L2Ah2faemk6DCR.yZAhK6yt2fve',2);
+INSERT INTO "usuario" ("nome", "login", "senha", "tipo") VALUES ( 'Usuario','user','$2a$10$oLSlGuWbD43fYrw.7DzkQubr2Q8Ronbnw8r1zpkjTHzJsQ6z5oXPq',1);
+INSERT INTO "usuario" ("nome", "login", "senha", "tipo") VALUES ( 'Sistema','sys','$2a$10$opBp5R5Cvje.VC/jwNTuLOucdUbKpue9kEzA5gSdYEYzZ6Eb/M.Cq',2);
+
+
+INSERT INTO "usuario_perfil" ("usuario_id","perfis_id") VALUES (1,1);
+INSERT INTO "usuario_perfil" ("usuario_id","perfis_id") VALUES (2,2);
+INSERT INTO "usuario_perfil" ("usuario_id","perfis_id") VALUES (3,3);
+
+
diff --git a/src/main/resources/db/testdat/V1__CreateTest.sql b/src/main/resources/db/testdat/V1__CreateTest.sql
new file mode 100644
index 00000000..dffe16b5
--- /dev/null
+++ b/src/main/resources/db/testdat/V1__CreateTest.sql
@@ -0,0 +1,19 @@
+CREATE TABLE "USUARIO"
+(
+ "ID" BIGINT AUTO_INCREMENT PRIMARY KEY,
+ "NOME" TEXT NOT NULL,
+ "LOGIN" TEXT NOT NULL,
+ "SENHA" TEXT NOT NULL,
+ "TIPO" INTEGER NOT NULL,
+);
+
+CREATE TABLE "PERFIL"
+(
+ "ID" BIGINT AUTO_INCREMENT PRIMARY KEY,
+ "NOME" TEXT NOT NULL
+);
+
+
+INSERT INTO "USUARIO" ("NOME", "LOGIN", "SENHA", "TIPO") VALUES ( 'Administrador','admin','$2a$10$.ltOnHeuxrc2JLpiG8Ex0.1MT/L2Ah2faemk6DCR.yZAhK6yt2fve',1);
+INSERT INTO "USUARIO" ("NOME", "LOGIN", "SENHA", "TIPO") VALUES ( 'Usuario','user','$2a$10$oLSlGuWbD43fYrw.7DzkQubr2Q8Ronbnw8r1zpkjTHzJsQ6z5oXPq',2);
+INSERT INTO "USUARIO" ("NOME", "LOGIN", "SENHA", "TIPO") VALUES ( 'Sistema','sys','$2a$10$opBp5R5Cvje.VC/jwNTuLOucdUbKpue9kEzA5gSdYEYzZ6Eb/M.Cq',1);
diff --git a/src/test/java/br/com/zup/way/EmbeddedWaySolrServerFactory.java b/src/test/java/br/com/zup/way/EmbeddedWaySolrServerFactory.java
new file mode 100644
index 00000000..2e03e47b
--- /dev/null
+++ b/src/test/java/br/com/zup/way/EmbeddedWaySolrServerFactory.java
@@ -0,0 +1,71 @@
+package br.com.zup.way;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.solr.client.solrj.SolrClient;
+import org.apache.solr.client.solrj.SolrServerException;
+import org.apache.solr.client.solrj.embedded.EmbeddedSolrServer;
+import org.apache.solr.client.solrj.request.CoreAdminRequest;
+import org.apache.solr.core.NodeConfig;
+import org.apache.solr.core.SolrResourceLoader;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+/**
+ * @author bbende
+ */
+public class EmbeddedWaySolrServerFactory {
+
+ /**
+ * Cleans the given solrHome directory and creates a new EmbeddedSolrServer.
+ *
+ * @param solrHome the Solr home directory to use
+ * @param configSetHome the directory containing config sets
+ * @param coreName the name of the core, must have a matching directory in configHome
+ * @return an EmbeddedSolrServer with a core created for the given coreName
+ * @throws IOException
+ */
+ public static SolrClient create(final String solrHome, final String configSetHome, final String coreName)
+ throws IOException, SolrServerException {
+ return create(solrHome, configSetHome, coreName, true);
+ }
+
+ /**
+ * @param solrHome the Solr home directory to use
+ * @param configSetHome the directory containing config sets
+ * @param coreName the name of the core, must have a matching directory in configHome
+ * @param cleanSolrHome if true the directory for solrHome will be deleted and re-created if it already exists
+ * @return an EmbeddedSolrServer with a core created for the given coreName
+ * @throws IOException
+ */
+ public static SolrClient create(final String solrHome, final String configSetHome, final String coreName, final boolean cleanSolrHome)
+ throws IOException, SolrServerException {
+
+ final File solrHomeDir = new File(solrHome);
+ if (solrHomeDir.exists()) {
+ FileUtils.deleteDirectory(solrHomeDir);
+ solrHomeDir.mkdirs();
+ } else {
+ solrHomeDir.mkdirs();
+ }
+
+ final SolrResourceLoader loader = new SolrResourceLoader(solrHomeDir.toPath());
+ final Path configSetPath = Paths.get(configSetHome).toAbsolutePath();
+
+ final NodeConfig config = new NodeConfig.NodeConfigBuilder("embeddedSolrServerNode", loader)
+ .setConfigSetBaseDirectory(configSetPath.toString())
+ .build();
+
+ final EmbeddedSolrServer embeddedSolrServer = new EmbeddedSolrServer(config, coreName);
+
+ final CoreAdminRequest.Create createRequest = new CoreAdminRequest.Create();
+ createRequest.setCoreName(coreName);
+ createRequest.setConfigSet(coreName);
+ embeddedSolrServer.request(createRequest);
+
+ return embeddedSolrServer;
+ }
+
+}
diff --git a/src/test/java/br/com/zup/way/WayApplicationTests.java b/src/test/java/br/com/zup/way/WayApplicationTests.java
new file mode 100644
index 00000000..3a00de64
--- /dev/null
+++ b/src/test/java/br/com/zup/way/WayApplicationTests.java
@@ -0,0 +1,17 @@
+package br.com.zup.way;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Profile;
+import org.springframework.test.context.junit4.SpringRunner;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest
+public class WayApplicationTests {
+
+ @Test
+ public void contextLoads() {
+ }
+
+}
diff --git a/src/test/java/br/com/zup/way/WaySolrDbConfig.java b/src/test/java/br/com/zup/way/WaySolrDbConfig.java
new file mode 100644
index 00000000..c05f3991
--- /dev/null
+++ b/src/test/java/br/com/zup/way/WaySolrDbConfig.java
@@ -0,0 +1,86 @@
+package br.com.zup.way;
+
+import br.com.zup.way.util.DateUtil;
+import org.apache.solr.client.solrj.SolrClient;
+import org.apache.solr.client.solrj.SolrServerException;
+import org.apache.solr.common.SolrInputDocument;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Profile;
+import org.springframework.data.solr.core.SolrTemplate;
+import org.springframework.data.solr.repository.config.EnableSolrRepositories;
+import org.springframework.transaction.annotation.EnableTransactionManagement;
+
+import java.io.IOException;
+import java.time.LocalDateTime;
+import java.util.Arrays;
+
+@Configuration
+@EnableTransactionManagement
+@EnableSolrRepositories(basePackages = "br.com.zup.way.db.solr.repository")
+@ComponentScan
+@Profile("test")
+public class WaySolrDbConfig {
+
+ static final String CONFIGSET_DIR = "src/test/resources/configsets";
+
+ private LocalDateTime dataCadastro = DateUtil.setZone(LocalDateTime.of(2019, 01, 01, 8, 00));
+
+ @Bean
+ public SolrClient solrClient() {
+ return getEmbeddedSolr();
+ }
+
+ private SolrClient getEmbeddedSolr() {
+ String targetLocation = EmbeddedWaySolrServerFactory.class
+ .getProtectionDomain().getCodeSource().getLocation().getFile() + "/..";
+
+ String solrHome = targetLocation + "/solr";
+
+ try {
+ SolrClient solrClient = EmbeddedWaySolrServerFactory.create(solrHome, CONFIGSET_DIR, "GastoSolr");
+
+ LocalDateTime dataCadastro = DateUtil.setZone(LocalDateTime.of(2019, 01, 01, 8, 00));
+
+ SolrInputDocument doc1 = new SolrInputDocument();
+
+ doc1.setField("id", "513b08d9-b286-4da4-a09f-33d15687e972");
+ doc1.addField("descricao_text_pt", "TAM SITE");
+ doc1.addField("valor", 300);
+ doc1.addField("codigoUsuario", 2);
+ doc1.addField("data_cadastro_dt", DateUtil.format(dataCadastro));
+
+ SolrInputDocument doc2 = new SolrInputDocument();
+
+ doc2.addField("descricao_text_pt", "Padaria No Céu");
+ doc2.addField("valor", 50D);
+ doc2.addField("codigoUsuario", 2);
+ doc2.addField("categoria_text_pt", "Restaurante");
+ doc2.addField("data_cadastro_dt", DateUtil.format(dataCadastro));
+
+ SolrInputDocument doc3 = new SolrInputDocument();
+
+ doc3.addField("descricao_text_pt", "Uber Do Brasil");
+ doc3.addField("valor", 10D);
+ doc3.addField("codigoUsuario", 2);
+ doc3.addField("categoria_text_pt", "Transporte");
+ doc3.addField("data_cadastro_dt", DateUtil.format(dataCadastro));
+
+ solrClient.add(Arrays.asList(doc1, doc2, doc3));
+ solrClient.commit();
+ return solrClient;
+ } catch (IOException e) {
+ e.printStackTrace();
+ } catch (SolrServerException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+
+ @Bean
+ public SolrTemplate solrTemplate(SolrClient client) {
+ return new SolrTemplate(client);
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/br/com/zup/way/gasto/GastoApplicationTests.java b/src/test/java/br/com/zup/way/gasto/GastoApplicationTests.java
new file mode 100644
index 00000000..f9bf5b04
--- /dev/null
+++ b/src/test/java/br/com/zup/way/gasto/GastoApplicationTests.java
@@ -0,0 +1,50 @@
+package br.com.zup.way.gasto;
+
+import br.com.zup.way.db.solr.model.Gasto;
+import br.com.zup.way.db.solr.model.dto.FiltroCategorizarGastoDTO;
+import br.com.zup.way.db.solr.repository.GastoRepository;
+import br.com.zup.way.service.impl.GastoServiceImpl;
+import br.com.zup.way.util.DateUtil;
+import br.com.zup.way.util.Exception.WayBusinessException;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.time.LocalDateTime;
+import java.util.Optional;
+
+import static org.mockito.Mockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+public class GastoApplicationTests {
+
+ @Mock
+ GastoRepository gastoRepositoryMock;
+
+ @InjectMocks
+ GastoServiceImpl gastoService;
+
+ @Test(expected = WayBusinessException.class)
+ public void categorizarGasto() throws WayBusinessException {
+ LocalDateTime data = DateUtil.setZone(LocalDateTime.now());
+ when(gastoRepositoryMock.findById("7bd92702-ddc2-4286-a2ec-1e362c5d77b3"))
+ .thenReturn(returnGasto("7bd92702-ddc2-4286-a2ec-1e362c5d77b3", "Supermercado Extra", 200D, 2L, "SuperMercado", data));
+ gastoService.categorizarGasto(returnFiltroCategorizarGastoDTO("Supermercado Extra", "7bd92702-ddc2-4286-a2ec-1e362c5d77b3"));
+ }
+
+ private FiltroCategorizarGastoDTO returnFiltroCategorizarGastoDTO(String categoria, String idGasto) {
+ return new FiltroCategorizarGastoDTO(categoria, idGasto);
+ }
+
+ /**
+ * Private Methods
+ *
+ * @return
+ */
+ private Optional returnGasto(String id, String descricao, Double valor, Long codigoUsuario, String categoria, LocalDateTime dataCadastro) {
+ return Optional.of(new Gasto(id, descricao, valor, codigoUsuario, categoria, dataCadastro));
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/java/br/com/zup/way/gasto/GastoIntegrationTests.java b/src/test/java/br/com/zup/way/gasto/GastoIntegrationTests.java
new file mode 100644
index 00000000..fc9f795f
--- /dev/null
+++ b/src/test/java/br/com/zup/way/gasto/GastoIntegrationTests.java
@@ -0,0 +1,97 @@
+package br.com.zup.way.gasto;
+
+import br.com.zup.way.controllers.GastoController;
+import br.com.zup.way.db.solr.model.dto.FiltroCategorizarGastoDTO;
+import br.com.zup.way.db.solr.model.dto.FiltroGastoDTO;
+import br.com.zup.way.db.solr.model.dto.GastoDTO;
+import br.com.zup.way.db.solr.model.dto.IntegrateGastoDTO;
+import br.com.zup.way.util.DateUtil;
+import br.com.zup.way.util.Exception.WayBusinessException;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Profile;
+import org.springframework.security.test.context.support.WithUserDetails;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import java.time.LocalDateTime;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest
+@WithUserDetails
+@Profile("test")
+public class GastoIntegrationTests {
+
+ @Autowired
+ GastoController gastoController;
+ private LocalDateTime dataCadastro = DateUtil.setZone(LocalDateTime.of(2019, 01, 01, 8, 00));
+
+ @Test
+ public void testIntegrateGastoWithouCategoria() throws WayBusinessException {
+ GastoDTO gasto = gastoController.integrate(new IntegrateGastoDTO(2L, "SuperMercado Santana", 200.20D, dataCadastro));
+ Assert.assertEquals(createGasto("SuperMercado Santana", 200.20D, null), gasto);
+ }
+
+ @Test
+ public void testIntegrateGastoWithCategoria() throws WayBusinessException {
+ GastoDTO gasto = gastoController.integrate(new IntegrateGastoDTO(2L, "TAM SITE", 400D, dataCadastro));
+ Assert.assertEquals(createGasto("TAM SITE", 400D, "Viagem"), gasto);
+ }
+
+ @Test(expected = WayBusinessException.class)
+ public void testIntegrateGastoWithoutValidUser() throws WayBusinessException {
+ gastoController.integrate(new IntegrateGastoDTO(3L, "Inclusão Cliente Invalido", 200.20D, dataCadastro));
+ }
+
+ @Test
+ public void testCategorizarGasto() throws WayBusinessException {
+ GastoDTO gasto = gastoController.categorizarGasto(new FiltroCategorizarGastoDTO("Viagem", "513b08d9-b286-4da4-a09f-33d15687e972"));
+ Assert.assertEquals(createGasto("TAM SITE", 300D, "Viagem"), gasto);
+ }
+
+ @Test
+ public void testSuggestCategoria() {
+ Set categorias = gastoController.suggestCategoria("agem");
+ Assert.assertEquals(new HashSet<>(Arrays.asList("Viagem")), categorias);
+ }
+
+ @Test
+ public void testFind() {
+ List gasto = gastoController.find(new FiltroGastoDTO(dataCadastro));
+ Assert.assertEquals(
+ Arrays.asList(
+ createGasto("TAM SITE", 300D, null),
+ createGasto("Padaria No Céu", 50D, "Restaurante"),
+ createGasto("Uber Do Brasil", 10D, "Transporte")
+ ), gasto);
+
+ }
+
+ @Test
+ public void testlistLast() {
+ List gasto = gastoController.listLast();
+ Assert.assertEquals(
+ Arrays.asList(
+ createGasto("Uber Do Brasil", 10D, "Transporte"),
+ createGasto("Padaria No Céu", 50D, "Restaurante"),
+ createGasto("SuperMercado Santana", 200.2D, null),
+ createGasto("TAM SITE", 300D, "Viagem"),
+ createGasto("TAM SITE", 400D, "Viagem")
+ ), gasto);
+ }
+
+
+ /**
+ * Private Methods
+ */
+
+ private GastoDTO createGasto(String descricao, double valor, String categoria) {
+ return new GastoDTO(null, descricao, valor, 2L, dataCadastro, categoria);
+ }
+}
diff --git a/src/test/resources/configsets/GastoSolr/conf/lang/stopwords_en.txt b/src/test/resources/configsets/GastoSolr/conf/lang/stopwords_en.txt
new file mode 100644
index 00000000..2c164c0b
--- /dev/null
+++ b/src/test/resources/configsets/GastoSolr/conf/lang/stopwords_en.txt
@@ -0,0 +1,54 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# a couple of test stopwords to test that the words are really being
+# configured from this file:
+stopworda
+stopwordb
+
+# Standard english stop words taken from Lucene's StopAnalyzer
+a
+an
+and
+are
+as
+at
+be
+but
+by
+for
+if
+in
+into
+is
+it
+no
+not
+of
+on
+or
+such
+that
+the
+their
+then
+there
+these
+they
+this
+to
+was
+will
+with
diff --git a/src/test/resources/configsets/GastoSolr/conf/lang/stopwords_pt.txt b/src/test/resources/configsets/GastoSolr/conf/lang/stopwords_pt.txt
new file mode 100644
index 00000000..acfeb01a
--- /dev/null
+++ b/src/test/resources/configsets/GastoSolr/conf/lang/stopwords_pt.txt
@@ -0,0 +1,253 @@
+ | From svn.tartarus.org/snowball/trunk/website/algorithms/portuguese/stop.txt
+ | This file is distributed under the BSD License.
+ | See http://snowball.tartarus.org/license.php
+ | Also see http://www.opensource.org/licenses/bsd-license.html
+ | - Encoding was converted to UTF-8.
+ | - This notice was added.
+ |
+ | NOTE: To use this file with StopFilterFactory, you must specify format="snowball"
+
+ | A Portuguese stop word list. Comments begin with vertical bar. Each stop
+ | word is at the start of a line.
+
+
+ | The following is a ranked list (commonest to rarest) of stopwords
+ | deriving from a large sample of text.
+
+ | Extra words have been added at the end.
+
+de | of, from
+a | the; to, at; her
+o | the; him
+que | who, that
+e | and
+do | de + o
+da | de + a
+em | in
+um | a
+para | for
+ | é from SER
+com | with
+não | not, no
+uma | a
+os | the; them
+no | em + o
+se | himself etc
+na | em + a
+por | for
+mais | more
+as | the; them
+dos | de + os
+como | as, like
+mas | but
+ | foi from SER
+ao | a + o
+ele | he
+das | de + as
+ | tem from TER
+à | a + a
+seu | his
+sua | her
+ou | or
+ | ser from SER
+quando | when
+muito | much
+ | há from HAV
+nos | em + os; us
+já | already, now
+ | está from EST
+eu | I
+também | also
+só | only, just
+pelo | per + o
+pela | per + a
+até | up to
+isso | that
+ela | he
+entre | between
+ | era from SER
+depois | after
+sem | without
+mesmo | same
+aos | a + os
+ | ter from TER
+seus | his
+quem | whom
+nas | em + as
+me | me
+esse | that
+eles | they
+ | estão from EST
+você | you
+ | tinha from TER
+ | foram from SER
+essa | that
+num | em + um
+nem | nor
+suas | her
+meu | my
+às | a + as
+minha | my
+ | têm from TER
+numa | em + uma
+pelos | per + os
+elas | they
+ | havia from HAV
+ | seja from SER
+qual | which
+ | será from SER
+nós | we
+ | tenho from TER
+lhe | to him, her
+deles | of them
+essas | those
+esses | those
+pelas | per + as
+este | this
+ | fosse from SER
+dele | of him
+
+ | other words. There are many contractions such as naquele = em+aquele,
+ | mo = me+o, but they are rare.
+ | Indefinite article plural forms are also rare.
+
+tu | thou
+te | thee
+vocês | you (plural)
+vos | you
+lhes | to them
+meus | my
+minhas
+teu | thy
+tua
+teus
+tuas
+nosso | our
+nossa
+nossos
+nossas
+
+dela | of her
+delas | of them
+
+esta | this
+estes | these
+estas | these
+aquele | that
+aquela | that
+aqueles | those
+aquelas | those
+isto | this
+aquilo | that
+
+ | forms of estar, to be (not including the infinitive):
+estou
+está
+estamos
+estão
+estive
+esteve
+estivemos
+estiveram
+estava
+estávamos
+estavam
+estivera
+estivéramos
+esteja
+estejamos
+estejam
+estivesse
+estivéssemos
+estivessem
+estiver
+estivermos
+estiverem
+
+ | forms of haver, to have (not including the infinitive):
+hei
+há
+havemos
+hão
+houve
+houvemos
+houveram
+houvera
+houvéramos
+haja
+hajamos
+hajam
+houvesse
+houvéssemos
+houvessem
+houver
+houvermos
+houverem
+houverei
+houverá
+houveremos
+houverão
+houveria
+houveríamos
+houveriam
+
+ | forms of ser, to be (not including the infinitive):
+sou
+somos
+são
+era
+éramos
+eram
+fui
+foi
+fomos
+foram
+fora
+fôramos
+seja
+sejamos
+sejam
+fosse
+fôssemos
+fossem
+for
+formos
+forem
+serei
+será
+seremos
+serão
+seria
+seríamos
+seriam
+
+ | forms of ter, to have (not including the infinitive):
+tenho
+tem
+temos
+tém
+tinha
+tínhamos
+tinham
+tive
+teve
+tivemos
+tiveram
+tivera
+tivéramos
+tenha
+tenhamos
+tenham
+tivesse
+tivéssemos
+tivessem
+tiver
+tivermos
+tiverem
+terei
+terá
+teremos
+terão
+teria
+teríamos
+teriam
diff --git a/src/test/resources/configsets/GastoSolr/conf/params.json b/src/test/resources/configsets/GastoSolr/conf/params.json
new file mode 100644
index 00000000..06114ef2
--- /dev/null
+++ b/src/test/resources/configsets/GastoSolr/conf/params.json
@@ -0,0 +1,20 @@
+{"params":{
+ "query":{
+ "defType":"edismax",
+ "q.alt":"*:*",
+ "rows":"10",
+ "fl":"*,score",
+ "":{"v":0}
+ },
+ "facets":{
+ "facet":"on",
+ "facet.mincount": "1",
+ "":{"v":0}
+ },
+ "velocity":{
+ "wt": "velocity",
+ "v.template":"browse",
+ "v.layout": "layout",
+ "":{"v":0}
+ }
+}}
\ No newline at end of file
diff --git a/src/test/resources/configsets/GastoSolr/conf/protwords.txt b/src/test/resources/configsets/GastoSolr/conf/protwords.txt
new file mode 100644
index 00000000..1dfc0abe
--- /dev/null
+++ b/src/test/resources/configsets/GastoSolr/conf/protwords.txt
@@ -0,0 +1,21 @@
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#-----------------------------------------------------------------------
+# Use a protected word file to protect against the stemmer reducing two
+# unrelated words to the same base word.
+
+# Some non-words that normally won't be encountered,
+# just to test that they won't be stemmed.
+dontstems
+zwhacky
+
diff --git a/src/test/resources/configsets/GastoSolr/conf/schema.xml b/src/test/resources/configsets/GastoSolr/conf/schema.xml
new file mode 100644
index 00000000..93115691
--- /dev/null
+++ b/src/test/resources/configsets/GastoSolr/conf/schema.xml
@@ -0,0 +1,147 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ id
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/test/resources/configsets/GastoSolr/conf/solrconfig.xml b/src/test/resources/configsets/GastoSolr/conf/solrconfig.xml
new file mode 100644
index 00000000..fcfb9bb3
--- /dev/null
+++ b/src/test/resources/configsets/GastoSolr/conf/solrconfig.xml
@@ -0,0 +1,44 @@
+
+
+
+ 6.2.1
+
+ ${solr.data.dir:}
+
+
+
+
+ single
+
+
+
+
+
+
+
+
+
+
+
+ id
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/test/resources/configsets/GastoSolr/conf/stopwords.txt b/src/test/resources/configsets/GastoSolr/conf/stopwords.txt
new file mode 100644
index 00000000..ae1e83ee
--- /dev/null
+++ b/src/test/resources/configsets/GastoSolr/conf/stopwords.txt
@@ -0,0 +1,14 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
diff --git a/src/test/resources/configsets/GastoSolr/conf/synonyms.txt b/src/test/resources/configsets/GastoSolr/conf/synonyms.txt
new file mode 100644
index 00000000..7f721283
--- /dev/null
+++ b/src/test/resources/configsets/GastoSolr/conf/synonyms.txt
@@ -0,0 +1,29 @@
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#-----------------------------------------------------------------------
+#some test synonym mappings unlikely to appear in real input text
+aaafoo => aaabar
+bbbfoo => bbbfoo bbbbar
+cccfoo => cccbar cccbaz
+fooaaa,baraaa,bazaaa
+
+# Some synonym groups specific to this example
+GB,gib,gigabyte,gigabytes
+MB,mib,megabyte,megabytes
+Television, Televisions, TV, TVs
+#notice we use "gib" instead of "GiB" so any WordDelimiterFilter coming
+#after us won't split it into two words.
+
+# Synonym mappings can be used for spelling correction too
+pixima => pixma
+