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() { + } + +}