diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..5ecf1cf2
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,6 @@
+.settings
+.DS_Store
+.classpath
+.factorypath
+target/
+.project
\ No newline at end of file
diff --git a/README.md b/README.md
index 15d8f685..5f465b56 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,43 @@
# Show me the code
+### # Instruções para rodar a Api.
+
+
1- Primeiramente, ir na pasta raiz do projeto e compile com o maven
+
+ ./mvnw clean install
+
+2- Na pasta Targat Clique 2x no TestBackJava-1.jar
+3- A api está usando segurança Oauth 2
+ Então é necessario fazer um POST primeiro no endpoint:
+
+ http://localhost:8000/gasto/oauth/token
+
+ Como Authorization basic, deve-se passar o username e passaword que nesse caso é "cliente".
+ No Body da requisição deve se passar;
+ chave Valor
+ client cliente
+ username admin
+ password admin
+ grant_type password
+ na resposta existe um access_token o qual deve ser copiado e passado no header do http para as outras requisições.
+4- A api responde os seguinte endPoonts;
+
+ ```
+ POST http://localhost:8000/gasto/ --> Para a inclusão de gastos, exemplo.
+ {
+ "descricao" : "teste",
+ "codigoUsuario" : 1,
+ "valor" : "200.00",
+ "data" : "2019-09-20T18:54:10",
+ "categoria": ""
+ }
+ GET http://localhost:8000/gasto/{idUsuario}/listagemGasto e http://localhost:8000/gasto/{idUsuario}/listagemGasto?data={2019-09-20} --> Para consulta de gastos
+ PUT http://localhost:8000/gasto/{idGasto}/categoria --> Altera uma categoria caso ela possa ser alterada
+ GET http://localhost:8000/gasto/categoria/filtro?categoria={caracter} --> lista categorias que iniciam com um caracter
+ ```
+*O banco de Dados ultilizado foi o H2, um banco em memoria.
+
+
### # DESAFIO:
API REST para Gestão de Gastos!
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 00000000..6300c62b
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,75 @@
+
+
+ 4.0.0
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.1.7.RELEASE
+
+
+ com.santander
+ TestBackJava
+ 1
+ TestBackJava
+ Demo project for Spring Boot
+
+
+ 1.8
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+ com.h2database
+ h2
+ runtime
+
+
+ org.projectlombok
+ lombok
+ true
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+ com.fasterxml.jackson.datatype
+ jackson-datatype-jsr310
+
+
+
+ org.springframework.security.oauth
+ spring-security-oauth2
+ 2.3.5.RELEASE
+
+
+
+ org.springframework.security
+ spring-security-jwt
+ 1.0.10.RELEASE
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
+
diff --git a/src/main/java/com/santander/TestBackJavaApplication.java b/src/main/java/com/santander/TestBackJavaApplication.java
new file mode 100644
index 00000000..a669448c
--- /dev/null
+++ b/src/main/java/com/santander/TestBackJavaApplication.java
@@ -0,0 +1,13 @@
+package com.santander;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class TestBackJavaApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(TestBackJavaApplication.class, args);
+ }
+
+}
diff --git a/src/main/java/com/santander/config/AuthorizationServerConfig.java b/src/main/java/com/santander/config/AuthorizationServerConfig.java
new file mode 100644
index 00000000..a9f14c47
--- /dev/null
+++ b/src/main/java/com/santander/config/AuthorizationServerConfig.java
@@ -0,0 +1,46 @@
+package com.santander.config;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
+import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
+import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
+import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
+import org.springframework.security.oauth2.provider.token.TokenStore;
+import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
+import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
+
+@Configuration
+@EnableAuthorizationServer
+public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
+
+ @Autowired
+ private AuthenticationManager authenticationManager;
+
+ @Override
+ public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
+ clients.inMemory().withClient("cliente").secret("cliente").scopes("read", "write")
+ .authorizedGrantTypes("password").accessTokenValiditySeconds(1800);
+ }
+
+ @Override
+ public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
+ endpoints.tokenStore(tokenStore()).accessTokenConverter(accessTokenConverter())
+ .authenticationManager(authenticationManager);
+ }
+
+ @Bean
+ public JwtAccessTokenConverter accessTokenConverter() {
+ JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter();
+ accessTokenConverter.setSigningKey("algaworks");
+ return accessTokenConverter;
+ }
+
+ @Bean
+ public TokenStore tokenStore() {
+ return new JwtTokenStore(accessTokenConverter());
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/santander/config/OAuthSecurityConfig.java b/src/main/java/com/santander/config/OAuthSecurityConfig.java
new file mode 100644
index 00000000..70c5b66a
--- /dev/null
+++ b/src/main/java/com/santander/config/OAuthSecurityConfig.java
@@ -0,0 +1,25 @@
+package com.santander.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.crypto.password.NoOpPasswordEncoder;
+import org.springframework.security.crypto.password.PasswordEncoder;
+
+@Configuration
+@EnableWebSecurity
+public class OAuthSecurityConfig extends WebSecurityConfigurerAdapter {
+
+ @Bean
+ @Override
+ public AuthenticationManager authenticationManager() throws Exception {
+ return super.authenticationManager();
+ }
+
+ @Bean
+ public PasswordEncoder passwordEncoder() {
+ return NoOpPasswordEncoder.getInstance();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/santander/config/ResourceServerConfig.java b/src/main/java/com/santander/config/ResourceServerConfig.java
new file mode 100644
index 00000000..7f82090b
--- /dev/null
+++ b/src/main/java/com/santander/config/ResourceServerConfig.java
@@ -0,0 +1,39 @@
+package com.santander.config;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Configuration;
+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.configuration.EnableWebSecurity;
+import org.springframework.security.config.http.SessionCreationPolicy;
+import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
+import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
+import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
+
+@Configuration
+@EnableWebSecurity
+@EnableResourceServer
+public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
+
+ @Autowired
+ public void configure(AuthenticationManagerBuilder auth) throws Exception {
+ auth.inMemoryAuthentication()
+ .withUser("admin").password("admin").roles("ROLE");
+ }
+
+ @Override
+ public void configure(HttpSecurity http) throws Exception {
+ http.authorizeRequests()
+ .antMatchers("/categorias").permitAll()
+ .anyRequest().authenticated()
+ .and()
+ .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
+ .csrf().disable();
+ }
+
+ @Override
+ public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
+ resources.stateless(true);
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/santander/model/Gasto.java b/src/main/java/com/santander/model/Gasto.java
new file mode 100644
index 00000000..3b404f6f
--- /dev/null
+++ b/src/main/java/com/santander/model/Gasto.java
@@ -0,0 +1,122 @@
+package com.santander.model;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.Size;
+
+@Entity
+public class Gasto {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+
+ @NotBlank
+ @Size(min = 3, max = 250)
+ @Column(name = "descricao")
+ private String descricao;
+
+ @Column(name = "valor")
+ private BigDecimal valor;
+
+ @Column(name = "codigoUsuario")
+ private int codigoUsuario;
+
+ @Column(name = "data")
+ private LocalDateTime data;
+
+ @Column(name = "categoria")
+ private String categoria;
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getDescricao() {
+ return descricao;
+ }
+
+ public void setDescricao(String descricao) {
+ this.descricao = descricao;
+ }
+
+ public BigDecimal getValor() {
+ return valor;
+ }
+
+ public void setValor(BigDecimal valor) {
+ this.valor = valor;
+ }
+
+ public int getCodigoUsuario() {
+ return codigoUsuario;
+ }
+
+ public void setCodigoUsuario(int codigoUsuario) {
+ this.codigoUsuario = codigoUsuario;
+ }
+
+ public LocalDateTime getData() {
+ return data;
+ }
+
+ public void setData(LocalDateTime data) {
+ this.data = data;
+ }
+
+ public boolean temCategoria() {
+ return false;
+
+ }
+
+ public String getCategoria() {
+ return categoria;
+ }
+
+ public void setCategoria(String categoria) {
+ this.categoria = categoria;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((data == null) ? 0 : data.hashCode());
+ result = prime * result + ((id == null) ? 0 : id.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ Gasto other = (Gasto) obj;
+ if (data == null) {
+ if (other.data != null)
+ return false;
+ } else if (!data.equals(other.data))
+ return false;
+ if (id == null) {
+ if (other.id != null)
+ return false;
+ } else if (!id.equals(other.id))
+ return false;
+ return true;
+ }
+
+}
diff --git a/src/main/java/com/santander/model/dto/GastoDTO.java b/src/main/java/com/santander/model/dto/GastoDTO.java
new file mode 100644
index 00000000..6eb2bc04
--- /dev/null
+++ b/src/main/java/com/santander/model/dto/GastoDTO.java
@@ -0,0 +1,18 @@
+package com.santander.model.dto;
+
+public class GastoDTO {
+ private String categoria;
+
+ public GastoDTO(String categoria) {
+ this.setCategoria(categoria);
+ }
+
+ public String getCategoria() {
+ return categoria;
+ }
+
+ public void setCategoria(String categoria) {
+ this.categoria = categoria;
+ }
+
+}
diff --git a/src/main/java/com/santander/repository/GastoRepositoy.java b/src/main/java/com/santander/repository/GastoRepositoy.java
new file mode 100644
index 00000000..f4680514
--- /dev/null
+++ b/src/main/java/com/santander/repository/GastoRepositoy.java
@@ -0,0 +1,16 @@
+package com.santander.repository;
+
+import java.util.Optional;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+import com.santander.model.Gasto;
+import com.santander.repository.gasto.GastoRepositoyQuery;
+
+@Repository
+public interface GastoRepositoy extends JpaRepository, GastoRepositoyQuery {
+
+ Optional findByDescricao(String descricao);
+
+}
diff --git a/src/main/java/com/santander/repository/Gastos.java b/src/main/java/com/santander/repository/Gastos.java
new file mode 100644
index 00000000..d7e6f08d
--- /dev/null
+++ b/src/main/java/com/santander/repository/Gastos.java
@@ -0,0 +1,9 @@
+package com.santander.repository;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import com.santander.model.Gasto;
+
+public interface Gastos extends JpaRepository {
+
+}
diff --git a/src/main/java/com/santander/repository/filter/GastoFilter.java b/src/main/java/com/santander/repository/filter/GastoFilter.java
new file mode 100644
index 00000000..94e7047f
--- /dev/null
+++ b/src/main/java/com/santander/repository/filter/GastoFilter.java
@@ -0,0 +1,19 @@
+package com.santander.repository.filter;
+
+import java.time.LocalDate;
+
+import org.springframework.format.annotation.DateTimeFormat;
+
+public class GastoFilter {
+ @DateTimeFormat(pattern = "yyyy-MM-dd")
+ private LocalDate data;
+
+ public LocalDate getData() {
+ return data;
+ }
+
+ public void setData(LocalDate data) {
+ this.data = data;
+ }
+
+}
diff --git a/src/main/java/com/santander/repository/gasto/GastoRepositoyImpl.java b/src/main/java/com/santander/repository/gasto/GastoRepositoyImpl.java
new file mode 100644
index 00000000..5a45f394
--- /dev/null
+++ b/src/main/java/com/santander/repository/gasto/GastoRepositoyImpl.java
@@ -0,0 +1,73 @@
+package com.santander.repository.gasto;
+
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceContext;
+import javax.persistence.TypedQuery;
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Predicate;
+import javax.persistence.criteria.Root;
+
+import org.springframework.util.StringUtils;
+
+import com.santander.model.Gasto;
+import com.santander.model.dto.GastoDTO;
+import com.santander.repository.filter.GastoFilter;
+
+public class GastoRepositoyImpl implements GastoRepositoyQuery {
+
+ @PersistenceContext
+ private EntityManager manager;
+
+ @Override
+ public List filtrar(GastoFilter gastoFilter, int codigoUsuario) {
+
+ CriteriaBuilder builder = manager.getCriteriaBuilder();
+ CriteriaQuery criteria = builder.createQuery(Gasto.class);
+ Root root = criteria.from(Gasto.class);
+
+ Predicate[] predicates = criarRestricoes(gastoFilter, codigoUsuario, builder, root);
+ criteria.where(predicates);
+
+ TypedQuery query = manager.createQuery(criteria);
+ return query.getResultList();
+ }
+
+ @Override
+ public List buscarPorCategoria(String categoria) {
+ String jpql = "select new com.santander.model.dto.GastoDTO(categoria) "
+ + "from Gasto where lower(categoria) like lower(:categoria)";
+
+ List categoriasFiltradas = manager.createQuery(jpql, GastoDTO.class)
+ .setParameter("categoria", categoria + "%").getResultList();
+ return categoriasFiltradas;
+ }
+
+ private Predicate[] criarRestricoes(GastoFilter gastoFilter, int codigoUsuario, CriteriaBuilder builder,
+ Root root) {
+
+ List predicates = new ArrayList<>();
+
+ if (gastoFilter.getData() != null) {
+ predicates.add(builder.greaterThanOrEqualTo(root.get("data"), gastoFilter.getData().atStartOfDay()));
+ predicates.add(builder.lessThanOrEqualTo(root.get("data"), gastoFilter.getData().atTime(LocalTime.MAX)));
+ } else {
+ LocalDateTime now = LocalDateTime.now();
+ predicates.add(builder.lessThanOrEqualTo(root.get("data"), now));
+
+ LocalDateTime second = now.minusSeconds(5);
+ predicates.add(builder.greaterThanOrEqualTo(root.get("data"), second));
+ }
+
+ if (!StringUtils.isEmpty(codigoUsuario)) {
+ predicates.add(builder.equal(root.get("codigoUsuario"), codigoUsuario));
+ }
+ return predicates.toArray(new Predicate[predicates.size()]);
+ }
+
+}
diff --git a/src/main/java/com/santander/repository/gasto/GastoRepositoyQuery.java b/src/main/java/com/santander/repository/gasto/GastoRepositoyQuery.java
new file mode 100644
index 00000000..1d01c939
--- /dev/null
+++ b/src/main/java/com/santander/repository/gasto/GastoRepositoyQuery.java
@@ -0,0 +1,13 @@
+package com.santander.repository.gasto;
+
+import java.util.List;
+
+import com.santander.model.Gasto;
+import com.santander.model.dto.GastoDTO;
+import com.santander.repository.filter.GastoFilter;
+
+public interface GastoRepositoyQuery {
+ public List filtrar(GastoFilter gastoFilter, int codigoUsuario);
+
+ public List buscarPorCategoria(String categoria);
+}
diff --git a/src/main/java/com/santander/resource/GastoResouce.java b/src/main/java/com/santander/resource/GastoResouce.java
new file mode 100644
index 00000000..8a47409c
--- /dev/null
+++ b/src/main/java/com/santander/resource/GastoResouce.java
@@ -0,0 +1,63 @@
+package com.santander.resource;
+
+import java.net.URI;
+import java.util.List;
+
+import javax.validation.Valid;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+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.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
+
+import com.santander.model.Gasto;
+import com.santander.model.dto.GastoDTO;
+import com.santander.repository.GastoRepositoy;
+import com.santander.repository.filter.GastoFilter;
+import com.santander.service.GastoService;
+
+@RestController
+@RequestMapping("/")
+public class GastoResouce {
+
+ @Autowired
+ private GastoService gastoService;
+ @Autowired
+ private GastoRepositoy gastoRepository;
+
+ @PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE)
+ public ResponseEntity criarGasto(@Valid @RequestBody Gasto gasto) {
+
+ Gasto gastoSalvo = gastoService.salvar(gasto);
+
+ URI uri = ServletUriComponentsBuilder.fromCurrentRequestUri().path("/usuario/{codigoUsuario}")
+ .buildAndExpand(gastoSalvo.getCodigoUsuario()).toUri();
+
+ return ResponseEntity.created(uri).body(gastoSalvo);
+ }
+
+ @GetMapping("/{codigoUsuario}/listagemGasto")
+ public ResponseEntity> buscarGastoPor(GastoFilter filter, @PathVariable int codigoUsuario) {
+ List gasto = gastoService.filtrar(filter, codigoUsuario);
+ return !gasto.isEmpty() ? ResponseEntity.ok(gasto) : ResponseEntity.notFound().build();
+ }
+
+ @PutMapping("/{codigo}/categoria")
+ public void alterarCategoria(@PathVariable Long codigo, @RequestBody String categoria) {
+ gastoService.alterarCategoria(codigo, categoria);
+ }
+
+ @GetMapping("/categoria/filtro")
+ public @ResponseBody List pesquisarCategoria(String categoria) {
+ return gastoRepository.buscarPorCategoria(categoria);
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/santander/resource/exceptionhandler/ApiExceptionHandler.java b/src/main/java/com/santander/resource/exceptionhandler/ApiExceptionHandler.java
new file mode 100644
index 00000000..39894b0c
--- /dev/null
+++ b/src/main/java/com/santander/resource/exceptionhandler/ApiExceptionHandler.java
@@ -0,0 +1,96 @@
+package com.santander.resource.exceptionhandler;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.MessageSource;
+import org.springframework.context.i18n.LocaleContextHolder;
+import org.springframework.dao.EmptyResultDataAccessException;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.http.converter.HttpMessageNotReadableException;
+import org.springframework.validation.BindingResult;
+import org.springframework.validation.FieldError;
+import org.springframework.web.bind.MethodArgumentNotValidException;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.context.request.WebRequest;
+import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
+
+public class ApiExceptionHandler extends ResponseEntityExceptionHandler {
+
+ @Autowired
+ private MessageSource messageSource;
+
+ @Override
+ protected ResponseEntity handleHttpMessageNotReadable(HttpMessageNotReadableException ex,
+ HttpHeaders headers, HttpStatus status, WebRequest request) {
+
+ String mensagemUsuario = messageSource.getMessage("mensagem.invalida", null, LocaleContextHolder.getLocale());
+ String mensagemDesenvolvedor = ex.getCause().toString();
+ return handleExceptionInternal(ex, new Erro(mensagemUsuario, mensagemDesenvolvedor), headers,
+ HttpStatus.BAD_REQUEST, request);
+ }
+
+ @Override
+ protected ResponseEntity handleMethodArgumentNotValid(MethodArgumentNotValidException ex,
+ HttpHeaders headers, HttpStatus status, WebRequest request) {
+
+ List erros = criarListaDeErros(ex.getBindingResult());
+ return handleExceptionInternal(ex, erros, headers, HttpStatus.BAD_REQUEST, request);
+ }
+
+ @ExceptionHandler({ EmptyResultDataAccessException.class })
+ public ResponseEntity handleEmptyResultDataAccessException(EmptyResultDataAccessException ex,
+ WebRequest request) {
+ String mensagemUsuario = messageSource.getMessage("recurso.nao-encontrado", null,
+ LocaleContextHolder.getLocale());
+ String mensagemDesenvolvedor = ex.toString();
+ List erros = Arrays.asList(new Erro(mensagemUsuario, mensagemDesenvolvedor));
+ return handleExceptionInternal(ex, erros, new HttpHeaders(), HttpStatus.NOT_FOUND, request);
+ }
+
+ @ExceptionHandler({ IllegalArgumentException.class })
+ public ResponseEntity handleIllegalArgumentException(IllegalArgumentException ex, WebRequest request) {
+ String mensagemUsuario = messageSource.getMessage("categoria.nao-pode-ser-alterada", null,
+ LocaleContextHolder.getLocale());
+ String mensagemDesenvolvedor = ex.toString();
+ List erros = Arrays.asList(new Erro(mensagemUsuario, mensagemDesenvolvedor));
+ return handleExceptionInternal(ex, erros, new HttpHeaders(), HttpStatus.BAD_REQUEST, request);
+ }
+
+ private List criarListaDeErros(BindingResult bindingResult) {
+ List erros = new ArrayList<>();
+
+ for (FieldError fieldError : bindingResult.getFieldErrors()) {
+ String mensagemUsuario = messageSource.getMessage(fieldError, LocaleContextHolder.getLocale());
+ String mensagemDesenvolvedor = fieldError.toString();
+ erros.add(new Erro(mensagemUsuario, mensagemDesenvolvedor));
+ }
+
+ return erros;
+ }
+
+ public static class Erro {
+
+ private String mensagemUsuario;
+ private String mensagemDesenvolvedor;
+
+ public Erro(String mensagemUsuario, String mensagemDesenvolvedor) {
+ this.mensagemUsuario = mensagemUsuario;
+ this.mensagemDesenvolvedor = mensagemDesenvolvedor;
+ }
+
+ public String getMensagemUsuario() {
+ return mensagemUsuario;
+ }
+
+ public String getMensagemDesenvolvedor() {
+ return mensagemDesenvolvedor;
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/santander/service/GastoService.java b/src/main/java/com/santander/service/GastoService.java
new file mode 100644
index 00000000..53a3a42e
--- /dev/null
+++ b/src/main/java/com/santander/service/GastoService.java
@@ -0,0 +1,57 @@
+package com.santander.service;
+
+import java.util.List;
+import java.util.Optional;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.dao.EmptyResultDataAccessException;
+import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
+
+import com.santander.model.Gasto;
+import com.santander.repository.GastoRepositoy;
+import com.santander.repository.filter.GastoFilter;
+
+@Service
+public class GastoService implements IGastoService {
+
+ @Autowired
+ private GastoRepositoy gastosRepository;
+
+ public GastoService(GastoRepositoy gastoRepository) {
+ this.gastosRepository = gastoRepository;
+ }
+
+ @Override
+ public Gasto salvar(Gasto gasto) {
+
+ if (StringUtils.isEmpty(gasto.getCategoria())) {
+ Optional descricaoOptional = gastosRepository.findByDescricao(gasto.getDescricao());
+ if (descricaoOptional.isPresent()) {
+ Gasto gastoSalvo = descricaoOptional.get();
+ gasto.setCategoria(gastoSalvo.getCategoria());
+ }
+ }
+ return gastosRepository.save(gasto);
+ }
+
+ @Override
+ public List filtrar(GastoFilter filter, int codigoUsuario) {
+ return gastosRepository.filtrar(filter, codigoUsuario);
+ }
+
+ @Override
+ public Gasto alterarCategoria(Long idGasto, String categoria) {
+
+ Gasto gastoSalvo = this.gastosRepository.findById(idGasto)
+ .orElseThrow(() -> new EmptyResultDataAccessException(1));
+
+ if (!StringUtils.isEmpty(gastoSalvo.getCategoria())) {
+ throw new IllegalArgumentException("voce nao pode alterar esse campo");
+ }
+
+ gastoSalvo.setCategoria(categoria);
+ return gastosRepository.save(gastoSalvo);
+ }
+
+}
diff --git a/src/main/java/com/santander/service/IGastoService.java b/src/main/java/com/santander/service/IGastoService.java
new file mode 100644
index 00000000..8fd1c53e
--- /dev/null
+++ b/src/main/java/com/santander/service/IGastoService.java
@@ -0,0 +1,16 @@
+package com.santander.service;
+
+import java.util.List;
+
+import com.santander.model.Gasto;
+import com.santander.repository.filter.GastoFilter;
+
+public interface IGastoService {
+
+ public Gasto salvar(Gasto gasto);
+
+ public List filtrar(GastoFilter filter, int codigoUsuario);
+
+ public Gasto alterarCategoria(Long idGasto, String categoria);
+
+}
diff --git a/src/main/resources/ValidationMessages.properties b/src/main/resources/ValidationMessages.properties
new file mode 100644
index 00000000..3db868cf
--- /dev/null
+++ b/src/main/resources/ValidationMessages.properties
@@ -0,0 +1,3 @@
+javax.validation.constraints.NotNull.message={0} \u00e9 obrigat\u00f3rio(a)
+javax.validation.constraints.Size.message={0} deve ter o tamanho entre {min} e {max}
+javax.validation.constraints.NotBlank.message={0} não pode ser embranco
\ No newline at end of file
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
new file mode 100644
index 00000000..8af64327
--- /dev/null
+++ b/src/main/resources/application.properties
@@ -0,0 +1,16 @@
+spring.datasource.url=jdbc:h2:mem:testdb
+spring.datasource.driverClassName=org.h2.Driver
+spring.datasource.username=sa
+spring.datasource.password=12345
+spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
+spring.h2.console.enabled=true
+
+server.port=8000
+server.servlet.context-path= /gasto
+
+spring.jpa.show-sql=true
+
+spring.jackson.deserialization.fail-on-unknown-properties= true
+spring.jackson.serialization.write-dates-as-timestamps= false
+
+spring.jackson.date-format=yyyy-MM-dd
diff --git a/src/main/resources/messages.properties b/src/main/resources/messages.properties
new file mode 100644
index 00000000..7cc87108
--- /dev/null
+++ b/src/main/resources/messages.properties
@@ -0,0 +1,4 @@
+mensagem.invalida=Mensagem inv\u00E1lida
+recurso.nao-encontrado=Recurso n\u00E3o encontrado
+
+categoria.nao-pode-ser-alterada= N\u00E3o pode alterar categora já cadastrada
\ No newline at end of file
diff --git a/src/test/java/com/santander/IntegracaoGastosTest.java b/src/test/java/com/santander/IntegracaoGastosTest.java
new file mode 100644
index 00000000..6b9b269d
--- /dev/null
+++ b/src/test/java/com/santander/IntegracaoGastosTest.java
@@ -0,0 +1,102 @@
+package com.santander;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import com.santander.model.Gasto;
+import com.santander.repository.GastoRepositoy;
+import com.santander.repository.filter.GastoFilter;
+import com.santander.service.GastoService;
+import com.santander.service.IGastoService;
+
+@RunWith(SpringRunner.class)
+public class IntegracaoGastosTest {
+
+ @MockBean
+ private GastoRepositoy gastoRepository;
+
+ private IGastoService gastoService;
+
+ private Gasto gasto;
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ @Before
+ public void setUp() {
+
+ gasto = new Gasto();
+ gasto.setDescricao("Primeiro Gasto");
+ gasto.setValor(new BigDecimal(1.0));
+ gasto.setCodigoUsuario(1);
+ gasto.setData(LocalDateTime.now());
+ gasto.setCategoria("teste");
+ gastoService = new GastoService(gastoRepository);
+ }
+
+ @Test
+ public void deveSalvarGastosNoRepositorio() {
+ gastoService.salvar(gasto);
+ verify(gastoRepository).save(gasto);
+ }
+
+ @Test
+ public void deveSalvar2GastosNoRepositorio() throws Exception {
+
+ Gasto gasto2 = new Gasto();
+ gasto2.setCategoria(null);
+ gasto2.setDescricao("Segundo Gsato");
+ gasto2.setValor(new BigDecimal(10.00));
+ gasto2.setCodigoUsuario(2);
+ gasto2.setData(LocalDateTime.now());
+
+ gastoService.salvar(gasto);
+
+ gastoService.salvar(gasto2);
+ verify(gastoRepository, times(2)).save(gasto);
+
+ }
+
+ @Test
+ public void deveSalvarCemMilGastosPorSegundoNoRepositorio() {
+
+ long inicio = System.currentTimeMillis();
+
+ for (int i = 0; i <= 100000 - 1; i++) {
+ gastoService.salvar(gasto);
+ }
+
+ long fim = System.currentTimeMillis();
+ long tempo = fim - inicio;
+
+ verify(gastoRepository, times(100000)).save(gasto);
+ assertTrue(tempo <= 1000);
+
+ }
+
+ @Test
+ public void deveTrazerGastoParaDia() throws Exception {
+ GastoFilter filter = new GastoFilter();
+
+ filter.setData(LocalDate.now());
+ when(gastoRepository.filtrar(filter, gasto.getCodigoUsuario())).thenReturn(new ArrayList());
+ gastoService.filtrar(filter, gasto.getCodigoUsuario());
+ verify(gastoRepository).filtrar(filter, gasto.getCodigoUsuario());
+ }
+
+}
diff --git a/src/test/java/com/santander/TestBackJavaApplicationTests.java b/src/test/java/com/santander/TestBackJavaApplicationTests.java
new file mode 100644
index 00000000..d49dd237
--- /dev/null
+++ b/src/test/java/com/santander/TestBackJavaApplicationTests.java
@@ -0,0 +1,16 @@
+package com.santander;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest
+public class TestBackJavaApplicationTests {
+
+ @Test
+ public void contextLoads() {
+ }
+
+}