diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..82eca336
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,25 @@
+/target/
+!.mvn/wrapper/maven-wrapper.jar
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/build/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 00000000..4e8a6ffd
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,19 @@
+language: java
+
+
+sudo: required
+
+
+script:
+
+
+ - java -version
+
+
+ - echo $JAVA_HOME
+
+
+jdk:
+
+
+ - oraclejdk8
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 00000000..8a50b558
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2019 Andrews Lima
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
index 15d8f685..ac7211c0 100644
--- a/README.md
+++ b/README.md
@@ -1,76 +1,132 @@
-# Show me the code
-
-### # DESAFIO:
-
-API REST para Gestão de Gastos!
-
-```
-Funcionalidade: Integração de gastos por cartão
- Apenas sistemas credenciados poderão incluir novos gastos
- É esperado um volume de 100.000 inclusões por segundo
- Os gastos, serão informados atraves do protoloco JSON, seguindo padrão:
- { "descricao": "alfanumerico", "valor": double americano, "codigousuario": numerico, "data": Data dem formato UTC }
-```
-```
-Funcionalidade: Listagem de gastos*
- Dado que acesso como um cliente autenticado que pode visualizar os gastos do cartão
- Quando acesso a interface de listagem de gastos
- Então gostaria de ver meus gastos mais atuais.
+[](https://travis-ci.org/adslima/TestBackJava)
+
+
+# Micros serviços com spring boot, eureka, zuul, oauth, RabbitMq, solr e Axon Framework.
+
+
+## Microservices architecture
+
+Contém 5 componentes, todos eles são aplicativos implantáveis independentemente.
+
+
+### Service Registry (registro de serviços):
+
+ Este serviço mantém o registro de todos os microservices que foram implantados.
+Foi feito uso do netflix eureka neste projeto.
+
+### Service Gateway (roteamento de serviços):
+
+ Pensando na questão do client realizar suas chamadas, diretamente para um único ponto de entrada
+ que encaminharia a solicitação para o serviço de back-end apropriado.
+Com base nessa questão, usei o netflix zuul, o configurando para rotear as solicitações especificando rotas.
+
+### Auth Service(serviço de autenticação):
+
+ Para acessar qualquer recurso de autenticação é necessário,
+ em vez de usar as credenciais do proprietário do recurso para acessar recursos protegidos,
+ o cliente obtém um token de acesso.
+
+
+### Expense Management Command & Expense Management Query
+
+ Com base no que o CQRS propõe, para que separemos a aplicação em modelos diferentes para atualização e exibição,
+ que de acordo com o padrão do CQRS estariamos falando dos Comandos e Consultas.
+ No momento que o usuário realiza um compra com cartão, isso é roteado para o modelo de comando
+ que por sua vez, irá realizar operações necessárias e informará ao modelo de consulta
+ para que as novas informações sejam exibidas para o usuário.
+ Ainda com o uso do Axon Framework foi possivel a implementação do padrão arquitetural CQRS.
+
+ Para comunicação de nossas aplicações. ocorrerá através de barramento de eventos fazendo uso do RabbitMq.
+ Para a persistência dos gastos com cartões, será feito uso do Mysql.
+ E para indexação das categotorias, será feito uso do SOLR.
+
+
+# Pré requisito
+
+ - Maven 3
+ - Java 8
+ - Git
+ - Docker/Docker Compose
+
+## No Docker serão criados os serviços listados abaixo:
+
+ - Mysql
+ - Solr
+ - RabbitMq
+
+ - Service Registry
+ - Auth Service
+ - Service Gateway
+
+ - Expense Management Command
+ - Expense Management Query
+
+# Preparando ambiente
+
+ Em cada serviço (registry, auth, gateway, command e query)
+
+### Executar o seguinte commando:
+ - mvn clean compile package -DskipTests
+
+# Executando
+
+### Após esse procedimento, dentro da pasta raiz do projeto executar
+ - docker-compose up --no-start
+
+
+ Ao termino desse processo, executar os seguintes comandos:
+
+ - docker start mysql solr rabbit
+ - docker start eureka oauth
+ - docker start command query
+ - docker start zuul
+
+ *Obs. Devemos considerar um intervalo entre entre os start's dos serviços.
+
+
+## Com tudo rodando, devemos realizar a solicitação do token para acesso dos serviços;
+para isso faremos o seguinte request:
+
+ POST
+ http://localhost:8765/auth-api/oauth/token?grant_type=password&username=demo&password=password
+
+ Authorization: Basic dHJ1c3RlZC1hcHA6cGFzc3dvcmQ=
+
+ Content-Type: application/json
+
+com o token em 'mãos',vamos realizar um cadastro de gastos com cartão.
+
+POST
+ http://localhost:8765/commands/api-command
+
+ {
+ "userCode": 12345,
+ "description": "RecargaCel",
+ "date": "2019-03-01T01:45:55.031",
+ "value": 15.00
+ }
+
-*Para esta funcionalidade é esperado 2.000 acessos por segundo.
-*O cliente espera ver gastos realizados a 5 segundos atrás.
-```
-```
-Funcionalidade: Filtro de gastos
- Dado que acesso como um cliente autenticado
- E acessei a interface de listagem de gastos
- E configure o filtro de data igual a 27/03/1992
- Então gostaria de ver meus gastos apenas deste dia.
-```
-```
-Funcionalidade: Categorização de gastos
- Dado que acesso como um cliente autenticado
- Quando acesso o detalhe de um gasto
- E este não possui uma categoria
- Então devo conseguir incluir uma categoria para este
-```
-```
-Funcionalidade: Sugestão de categoria
- Dado que acesso como um cliente autenticado
- Quando acesso o detalhe do gasto que não possui categoria
- E começo a digitar a categoria que desejo
- Então uma lista de sugestões de categoria deve ser exibida, estas baseadas em categorias já informadas por outro usuários.
-```
-```
-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
-```
-### # 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.
-
-* 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.
-
-### # Observações gerais
-
-Adicione um arquivo [README.md](http://README.md) com os procedimentos para executar o projeto.
-Pedimos que trabalhe sozinho e não divulgue o resultado na internet.
-
-Faça um fork desse desse repositório em seu Github e nos envie um Pull Request com o resultado, por favor informe por qual empresa você esta se candidatando.
-
-### # Importante: não há prazo de entrega, faça com qualidade!
-
-# BOA SORTE!
+## Para edição de categorias
+
+PUT
+ http://localhost:8765/commands/api-command/{id}/categories
+
+ {"category": "Serviços"}
+
+## Para Listarmos os gastos:
+
+GET
+ http://localhost:8765/queries/api-queries/12345/expense-menagement
+
+## Para a listagem por data;
+
+GET
+ http://localhost:8765/queries/api-queries/expense-menagement?userCode=12345&date=2019-03-01T00:00:00
+
+
+## Para sugestão de categotorias:
+
+GET
+ http://localhost:8765/queries/api-queries/{Texto_a_Pesquisar}/categories
+
diff --git a/auth-service/Dockerfile b/auth-service/Dockerfile
new file mode 100644
index 00000000..1012364a
--- /dev/null
+++ b/auth-service/Dockerfile
@@ -0,0 +1,9 @@
+FROM java:8-jre-alpine
+
+RUN mkdir -p /auth-service
+ADD target/auth-service-0.0.1-SNAPSHOT.jar /auth-service
+ADD target/classes/application.properties /auth-service
+
+WORKDIR /auth-service
+
+ENTRYPOINT ["java", "-jar", "auth-service-0.0.1-SNAPSHOT.jar"]
\ No newline at end of file
diff --git a/auth-service/pom.xml b/auth-service/pom.xml
new file mode 100644
index 00000000..0b9dff1f
--- /dev/null
+++ b/auth-service/pom.xml
@@ -0,0 +1,101 @@
+
+
+ 4.0.0
+
+ br.com.adslima
+ auth-service
+ 0.0.1-SNAPSHOT
+ jar
+
+ auth-service
+ Oauth Server
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 1.5.4.RELEASE
+
+
+
+
+ UTF-8
+ UTF-8
+ 1.8
+ Dalston.SR2
+
+
+
+
+
+
+
+
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-starter-eureka
+
+
+
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-starter-oauth2
+
+
+
+ org.springframework.boot
+ spring-boot-starter-security
+
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+
+ com.h2database
+ h2
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-dependencies
+ ${spring-cloud.version}
+ pom
+ import
+
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
+
+
diff --git a/auth-service/src/main/java/br/com/adslima/ExpenseManagementAuthServerApplication.java b/auth-service/src/main/java/br/com/adslima/ExpenseManagementAuthServerApplication.java
new file mode 100644
index 00000000..e54de5cd
--- /dev/null
+++ b/auth-service/src/main/java/br/com/adslima/ExpenseManagementAuthServerApplication.java
@@ -0,0 +1,20 @@
+package br.com.adslima;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.domain.EntityScan;
+import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
+
+@SpringBootApplication
+@EnableDiscoveryClient
+@ComponentScan("br.com.adslima")
+@EnableJpaRepositories(basePackages = { "br.com.adslima.repository" })
+@EntityScan(basePackages = { "br.com.adslima.entity" })
+public class ExpenseManagementAuthServerApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(ExpenseManagementAuthServerApplication.class, args);
+ }
+}
diff --git a/auth-service/src/main/java/br/com/adslima/config/AuthorizationServerConfiguration.java b/auth-service/src/main/java/br/com/adslima/config/AuthorizationServerConfiguration.java
new file mode 100644
index 00000000..00ca1df4
--- /dev/null
+++ b/auth-service/src/main/java/br/com/adslima/config/AuthorizationServerConfiguration.java
@@ -0,0 +1,90 @@
+package br.com.adslima.config;
+
+import javax.sql.DataSource;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Primary;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+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.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
+import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
+import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
+import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
+import org.springframework.security.oauth2.provider.token.TokenEnhancer;
+import org.springframework.security.oauth2.provider.token.TokenStore;
+
+import br.com.adslima.service.impl.UserAuthenticationServiceImpl;
+
+
+
+@Configuration
+@EnableAuthorizationServer
+public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
+
+ private static String REALM = "MICRO_SERVICE_OAUTH";
+
+ @Autowired
+ private TokenStore tokenStore;
+
+ @Autowired
+ private AuthorizationCodeServices authorizationCodeServices;
+
+
+ @Autowired
+ private UserAuthenticationServiceImpl userAuthenticationServiceImpl;
+
+ @Autowired
+ @Qualifier("authenticationManagerBean")
+ private AuthenticationManager authenticationManager;
+
+ @Autowired
+ private DataSource dataSource;
+
+ @Autowired
+ private BCryptPasswordEncoder passwordEncoder;
+
+ @Autowired
+ private TokenEnhancer tokenEnhancer;
+
+ final static Logger logger = LoggerFactory.getLogger(AuthorizationServerConfiguration.class);
+
+
+ @Override
+ public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
+ clients.jdbc(dataSource).passwordEncoder(passwordEncoder);
+
+ }
+
+ @Override
+ public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
+
+ endpoints.tokenStore(tokenStore).authorizationCodeServices(authorizationCodeServices)
+ .userDetailsService(userAuthenticationServiceImpl)
+ .authenticationManager(authenticationManager).tokenEnhancer(tokenEnhancer)
+ .approvalStoreDisabled();
+ }
+
+ @Override
+ public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
+ security.realm(REALM + "/client").passwordEncoder(passwordEncoder);
+ }
+
+ @Bean
+ @Primary
+ public AuthorizationServerTokenServices tokenServices() {
+ DefaultTokenServices tokenServices = new DefaultTokenServices();
+ tokenServices.setTokenStore(tokenStore);
+ tokenServices.setSupportRefreshToken(true);
+ tokenServices.setTokenEnhancer(tokenEnhancer);
+ return tokenServices;
+ }
+}
diff --git a/auth-service/src/main/java/br/com/adslima/config/OAuth2SecurityConfiguration.java b/auth-service/src/main/java/br/com/adslima/config/OAuth2SecurityConfiguration.java
new file mode 100644
index 00000000..ef80fcb9
--- /dev/null
+++ b/auth-service/src/main/java/br/com/adslima/config/OAuth2SecurityConfiguration.java
@@ -0,0 +1,71 @@
+package br.com.adslima.config;
+
+ import javax.sql.DataSource;
+
+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.authentication.dao.DaoAuthenticationProvider;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
+import org.springframework.security.oauth2.provider.code.JdbcAuthorizationCodeServices;
+import org.springframework.security.oauth2.provider.token.TokenStore;
+import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;
+
+import br.com.adslima.service.impl.UserAuthenticationServiceImpl;
+
+
+
+ @Configuration
+ @EnableWebSecurity
+ public class OAuth2SecurityConfiguration extends WebSecurityConfigurerAdapter {
+
+ @Autowired
+ private UserAuthenticationServiceImpl userAuthenticationServiceImpl;
+
+ @Autowired
+ private DataSource dataSource;
+
+ @Override
+ protected void configure(HttpSecurity http) throws Exception {
+ http.csrf().disable().anonymous().disable().authorizeRequests().antMatchers("/oauth/token/**")
+ .permitAll();
+ }
+
+ @Override
+ @Bean
+ public AuthenticationManager authenticationManagerBean() throws Exception {
+ return super.authenticationManagerBean();
+ }
+
+ @Bean
+ public TokenStore tokenStore() {
+ return new JdbcTokenStore(dataSource);
+ }
+
+ @Bean
+ public AuthorizationCodeServices authorizationCodeServices() {
+ return new JdbcAuthorizationCodeServices(dataSource);
+ }
+
+ @Bean
+ public DaoAuthenticationProvider authenticationProvider() {
+ DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
+ authenticationProvider.setUserDetailsService(userAuthenticationServiceImpl);
+ authenticationProvider.setPasswordEncoder(passwordEncoder());
+ return authenticationProvider;
+ }
+
+ @Bean
+ public BCryptPasswordEncoder passwordEncoder() {
+ return new BCryptPasswordEncoder(8);
+ }
+ public static void main(String[] args) {
+ System.out.println(new BCryptPasswordEncoder(8).encode("password"));
+ }
+ }
+
diff --git a/auth-service/src/main/java/br/com/adslima/config/OauthTokenEnhancer.java b/auth-service/src/main/java/br/com/adslima/config/OauthTokenEnhancer.java
new file mode 100644
index 00000000..ef2fe6ce
--- /dev/null
+++ b/auth-service/src/main/java/br/com/adslima/config/OauthTokenEnhancer.java
@@ -0,0 +1,32 @@
+package br.com.adslima.config;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
+import org.springframework.security.oauth2.common.OAuth2AccessToken;
+import org.springframework.security.oauth2.provider.OAuth2Authentication;
+import org.springframework.security.oauth2.provider.token.TokenEnhancer;
+import org.springframework.stereotype.Component;
+
+import br.com.adslima.entity.Users;
+import br.com.adslima.service.impl.UserServiceImpl;
+
+@Component
+public class OauthTokenEnhancer implements TokenEnhancer {
+
+ @Autowired
+ private UserServiceImpl userServiceImpl;
+
+ @Override
+ public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
+ String userName = authentication.getName();
+ Users user = userServiceImpl.findByUsername(userName);
+ final Map additionalInfo = new HashMap<>();
+ additionalInfo.put("name", user.getFirstName());
+ ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
+ return accessToken;
+ }
+
+}
diff --git a/auth-service/src/main/java/br/com/adslima/config/ResourceServerConfiguration.java b/auth-service/src/main/java/br/com/adslima/config/ResourceServerConfiguration.java
new file mode 100644
index 00000000..5a23940c
--- /dev/null
+++ b/auth-service/src/main/java/br/com/adslima/config/ResourceServerConfiguration.java
@@ -0,0 +1,23 @@
+package br.com.adslima.config;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+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
+@EnableResourceServer
+public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
+ private static final String RESOURCE_ID = "auth-service";
+
+ @Override
+ public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
+ resources.resourceId(RESOURCE_ID).stateless(false);
+ }
+
+ @Override
+ public void configure(HttpSecurity http) throws Exception {
+ http.anonymous().disable().authorizeRequests().anyRequest().authenticated();
+ }
+}
diff --git a/auth-service/src/main/java/br/com/adslima/controller/UserController.java b/auth-service/src/main/java/br/com/adslima/controller/UserController.java
new file mode 100644
index 00000000..f7743116
--- /dev/null
+++ b/auth-service/src/main/java/br/com/adslima/controller/UserController.java
@@ -0,0 +1,32 @@
+package br.com.adslima.controller;
+
+import java.security.Principal;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import br.com.adslima.entity.Users;
+import br.com.adslima.service.impl.UserServiceImpl;
+
+
+@RestController
+@RequestMapping(value = "/users")
+public class UserController {
+
+ @Autowired
+ private UserServiceImpl userServiceImpl;
+
+ @GetMapping("/authenticate")
+ public ResponseEntity user(Principal user) {
+ return ResponseEntity.ok(user);
+ }
+
+ @GetMapping
+ public ResponseEntity getUserByUsername(Principal principal){
+ Users user = userServiceImpl.findByUsername(principal.getName());
+ return ResponseEntity.ok(user);
+ }
+}
diff --git a/auth-service/src/main/java/br/com/adslima/entity/Roles.java b/auth-service/src/main/java/br/com/adslima/entity/Roles.java
new file mode 100644
index 00000000..3647862f
--- /dev/null
+++ b/auth-service/src/main/java/br/com/adslima/entity/Roles.java
@@ -0,0 +1,58 @@
+package br.com.adslima.entity;
+
+import java.io.Serializable;
+import java.util.Set;
+
+import javax.persistence.CascadeType;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.ManyToMany;
+import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+
+@Entity
+@Table(name = "roles",
+ uniqueConstraints = @UniqueConstraint(columnNames = {"role_id", "role_name"}))
+public class Roles implements Serializable {
+
+ private static final long serialVersionUID = -3090658385114905175L;
+
+ @Id
+ @Column(name = "role_id", nullable = false, unique = true, columnDefinition = "smallint unsigned")
+ private Integer roleId;
+
+ @Column(name = "role_name", nullable = false, unique = true)
+ private String roleName;
+
+ @JsonIgnore
+ @ManyToMany(cascade = {CascadeType.MERGE}, mappedBy = "roles")
+ private Set users;
+
+ public Integer getRoleId() {
+ return roleId;
+ }
+
+ public void setRoleId(Integer roleId) {
+ this.roleId = roleId;
+ }
+
+ public String getRoleName() {
+ return roleName;
+ }
+
+ public void setRoleName(String roleName) {
+ this.roleName = roleName;
+ }
+
+ public Set getUsers() {
+ return users;
+ }
+
+ public void setUsers(Set users) {
+ this.users = users;
+ }
+
+}
diff --git a/auth-service/src/main/java/br/com/adslima/entity/Users.java b/auth-service/src/main/java/br/com/adslima/entity/Users.java
new file mode 100644
index 00000000..0e64b309
--- /dev/null
+++ b/auth-service/src/main/java/br/com/adslima/entity/Users.java
@@ -0,0 +1,117 @@
+package br.com.adslima.entity;
+
+import java.io.Serializable;
+import java.util.Set;
+
+import javax.persistence.CascadeType;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.JoinTable;
+import javax.persistence.ManyToMany;
+import javax.persistence.Table;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+
+@Entity
+@Table(name = "users")
+public class Users implements Serializable {
+
+ private static final long serialVersionUID = -3788391832239645648L;
+
+ @Id
+ @Column(name = "user_id", nullable = false, unique = true, columnDefinition = "int unsigned")
+ private Integer userId;
+
+ @Column(name = "user_name", nullable = false, unique = true, length = 45)
+ private String userName;
+
+ @JsonIgnore
+ @Column(name = "password", nullable = false)
+ private String password;
+
+ @Column(name = "first_name", length = 45)
+ private String firstName;
+
+ @Column(name = "last_name", length = 45)
+ private String lastName;
+
+ @Column(name = "email", length = 45, unique =true)
+ private String email;
+
+ @Column(name = "mobile", length = 15, unique=true)
+ private String mobile;
+
+ @JsonIgnore
+ @ManyToMany(fetch = FetchType.LAZY, cascade = {CascadeType.MERGE, CascadeType.REFRESH})
+ @JoinTable(name = "user_roles", joinColumns = @JoinColumn(name = "user_id"),
+ inverseJoinColumns = @JoinColumn(name = "role_id"))
+ private Set roles;
+
+ public Integer getUserId() {
+ return userId;
+ }
+
+ public void setUserId(Integer userId) {
+ this.userId = userId;
+ }
+
+ public String getUserName() {
+ return userName;
+ }
+
+ public void setUserName(String userName) {
+ this.userName = userName;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ public String getFirstName() {
+ return firstName;
+ }
+
+ public void setFirstName(String firstName) {
+ this.firstName = firstName;
+ }
+
+ public String getLastName() {
+ return lastName;
+ }
+
+ public void setLastName(String lastName) {
+ this.lastName = lastName;
+ }
+
+ public String getEmail() {
+ return email;
+ }
+
+ public void setEmail(String email) {
+ this.email = email;
+ }
+
+ public String getMobile() {
+ return mobile;
+ }
+
+ public void setMobile(String mobile) {
+ this.mobile = mobile;
+ }
+
+ public Set getRoles() {
+ return roles;
+ }
+
+ public void setRoles(Set roles) {
+ this.roles = roles;
+ }
+
+}
diff --git a/auth-service/src/main/java/br/com/adslima/exception/BaseException.java b/auth-service/src/main/java/br/com/adslima/exception/BaseException.java
new file mode 100644
index 00000000..5a603e43
--- /dev/null
+++ b/auth-service/src/main/java/br/com/adslima/exception/BaseException.java
@@ -0,0 +1,26 @@
+package br.com.adslima.exception;
+
+/**
+ *
+ * @author andrews.silva
+ *
+ */
+public class BaseException extends RuntimeException {
+
+ private static final long serialVersionUID = 2827308907740826575L;
+
+ private String code;
+
+ public BaseException(String message) {
+ super(message);
+ }
+
+ public BaseException(String message, String code) {
+ super(message);
+ this.code = code;
+ }
+
+ public String getCode() {
+ return code;
+ }
+}
diff --git a/auth-service/src/main/java/br/com/adslima/exception/NotFoundException.java b/auth-service/src/main/java/br/com/adslima/exception/NotFoundException.java
new file mode 100644
index 00000000..94473ca2
--- /dev/null
+++ b/auth-service/src/main/java/br/com/adslima/exception/NotFoundException.java
@@ -0,0 +1,15 @@
+package br.com.adslima.exception;
+
+public class NotFoundException extends BaseException {
+
+ private static final long serialVersionUID = 7156525748893002528L;
+
+ public NotFoundException(String message) {
+ super(message);
+ }
+
+ public NotFoundException(String message, String code) {
+ super(message, code);
+ }
+
+}
diff --git a/auth-service/src/main/java/br/com/adslima/exception/handler/GlobalExceptionHandler.java b/auth-service/src/main/java/br/com/adslima/exception/handler/GlobalExceptionHandler.java
new file mode 100644
index 00000000..0ae702e7
--- /dev/null
+++ b/auth-service/src/main/java/br/com/adslima/exception/handler/GlobalExceptionHandler.java
@@ -0,0 +1,82 @@
+package br.com.adslima.exception.handler;
+
+import java.io.IOException;
+import java.sql.SQLException;
+
+import javax.persistence.EntityNotFoundException;
+import javax.validation.ConstraintViolationException;
+
+import org.hibernate.HibernateException;
+import org.hibernate.exception.DataException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.dao.DataIntegrityViolationException;
+import org.springframework.dao.EmptyResultDataAccessException;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.converter.HttpMessageNotReadableException;
+import org.springframework.http.converter.HttpMessageNotWritableException;
+import org.springframework.orm.jpa.JpaSystemException;
+import org.springframework.web.HttpRequestMethodNotSupportedException;
+import org.springframework.web.bind.ServletRequestBindingException;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
+import org.springframework.web.servlet.NoHandlerFoundException;
+
+import com.fasterxml.jackson.core.JsonParseException;
+import com.fasterxml.jackson.databind.JsonMappingException;
+
+import br.com.adslima.exception.NotFoundException;
+import br.com.adslima.model.ErrorMessage;
+
+@RestControllerAdvice
+public class GlobalExceptionHandler {
+
+ private static Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
+
+ public static String getMessage(Exception e) {
+ logger.error("Exception", e);
+ return e.getCause() == null ? e.getMessage() : e.getCause().getMessage();
+ }
+
+ @ResponseStatus(HttpStatus.NOT_FOUND)
+ @ExceptionHandler(value = { NoHandlerFoundException.class, NotFoundException.class })
+ public ErrorMessage resourceNotFoundExceptionHandler(Exception e) {
+ return new ErrorMessage(getMessage(e), "404");
+ }
+
+ @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
+ @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
+ public ErrorMessage methodNotSupportedException(Exception e) {
+ return new ErrorMessage("Method Not Supported :" + getMessage(e), "405");
+ }
+
+ @ResponseStatus(HttpStatus.BAD_REQUEST)
+ @ExceptionHandler(value = { HttpMessageNotReadableException.class, MethodArgumentTypeMismatchException.class,
+ ServletRequestBindingException.class })
+ public ErrorMessage badRequestExceptionHandler(Exception e) {
+ return new ErrorMessage(getMessage(e), "400");
+ }
+
+ @ResponseStatus(HttpStatus.CONFLICT)
+ @ExceptionHandler(value = { DataIntegrityViolationException.class, ConstraintViolationException.class })
+ public ErrorMessage dataIntegrityExceptionHandler(Exception e) {
+ return new ErrorMessage("Constraint voilation : " + getMessage(e), "409");
+ }
+
+ @ResponseStatus(HttpStatus.BAD_REQUEST)
+ @ExceptionHandler(value = { EmptyResultDataAccessException.class, EntityNotFoundException.class,
+ HttpMessageNotWritableException.class })
+ public ErrorMessage emptyResultDataAccessExceptionHandler(Exception e) {
+ return new ErrorMessage("Entity doesnot exist : " + getMessage(e), "400");
+ }
+
+ @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
+ @ExceptionHandler(value = { DataException.class, SQLException.class, HibernateException.class,
+ JpaSystemException.class, JsonMappingException.class, JsonParseException.class, IOException.class })
+ public ErrorMessage systemException(Exception e) {
+ return new ErrorMessage("System error : " + getMessage(e), "500");
+ }
+
+}
diff --git a/auth-service/src/main/java/br/com/adslima/model/ErrorMessage.java b/auth-service/src/main/java/br/com/adslima/model/ErrorMessage.java
new file mode 100644
index 00000000..89b56aa9
--- /dev/null
+++ b/auth-service/src/main/java/br/com/adslima/model/ErrorMessage.java
@@ -0,0 +1,37 @@
+package br.com.adslima.model;
+
+import java.io.Serializable;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class ErrorMessage implements Serializable{
+
+ private static final long serialVersionUID = -1325725054820828328L;
+
+ @JsonProperty(value = "error")
+ private String error;
+ @JsonProperty(value = "error_description")
+ private String errorDescription;
+
+ public ErrorMessage(String error, String errorDescription) {
+ this.error = error;
+ this.errorDescription = errorDescription;
+ }
+
+ public String getError() {
+ return error;
+ }
+
+ public void setError(String error) {
+ this.error = error;
+ }
+
+ public String getErrorDescription() {
+ return errorDescription;
+ }
+
+ public void setErrorDescription(String errorDescription) {
+ this.errorDescription = errorDescription;
+ }
+
+}
diff --git a/auth-service/src/main/java/br/com/adslima/repository/BaseRepository.java b/auth-service/src/main/java/br/com/adslima/repository/BaseRepository.java
new file mode 100644
index 00000000..92c046c3
--- /dev/null
+++ b/auth-service/src/main/java/br/com/adslima/repository/BaseRepository.java
@@ -0,0 +1,11 @@
+package br.com.adslima.repository;
+
+import java.io.Serializable;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.repository.NoRepositoryBean;
+
+@NoRepositoryBean
+public interface BaseRepository extends JpaRepository {
+
+}
diff --git a/auth-service/src/main/java/br/com/adslima/repository/RolesRepository.java b/auth-service/src/main/java/br/com/adslima/repository/RolesRepository.java
new file mode 100644
index 00000000..98209ecf
--- /dev/null
+++ b/auth-service/src/main/java/br/com/adslima/repository/RolesRepository.java
@@ -0,0 +1,7 @@
+package br.com.adslima.repository;
+
+import br.com.adslima.entity.Roles;
+
+public interface RolesRepository extends BaseRepository {
+
+}
diff --git a/auth-service/src/main/java/br/com/adslima/repository/UsersRepository.java b/auth-service/src/main/java/br/com/adslima/repository/UsersRepository.java
new file mode 100644
index 00000000..24ab1164
--- /dev/null
+++ b/auth-service/src/main/java/br/com/adslima/repository/UsersRepository.java
@@ -0,0 +1,7 @@
+package br.com.adslima.repository;
+
+import br.com.adslima.entity.Users;
+
+public interface UsersRepository extends BaseRepository {
+ Users findByUserName(String username);
+}
diff --git a/auth-service/src/main/java/br/com/adslima/service/UserAuthenticationService.java b/auth-service/src/main/java/br/com/adslima/service/UserAuthenticationService.java
new file mode 100644
index 00000000..1c757528
--- /dev/null
+++ b/auth-service/src/main/java/br/com/adslima/service/UserAuthenticationService.java
@@ -0,0 +1,7 @@
+package br.com.adslima.service;
+
+import org.springframework.security.core.userdetails.UserDetailsService;
+
+public interface UserAuthenticationService extends UserDetailsService {
+
+}
diff --git a/auth-service/src/main/java/br/com/adslima/service/UserService.java b/auth-service/src/main/java/br/com/adslima/service/UserService.java
new file mode 100644
index 00000000..7f2ac037
--- /dev/null
+++ b/auth-service/src/main/java/br/com/adslima/service/UserService.java
@@ -0,0 +1,9 @@
+package br.com.adslima.service;
+
+import br.com.adslima.entity.Users;
+
+public interface UserService {
+ Users findByUsername(String username);
+
+ Users findByUserId(Integer userId);
+}
diff --git a/auth-service/src/main/java/br/com/adslima/service/impl/UserAuthenticationServiceImpl.java b/auth-service/src/main/java/br/com/adslima/service/impl/UserAuthenticationServiceImpl.java
new file mode 100644
index 00000000..177f0535
--- /dev/null
+++ b/auth-service/src/main/java/br/com/adslima/service/impl/UserAuthenticationServiceImpl.java
@@ -0,0 +1,50 @@
+package br.com.adslima.service.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.core.userdetails.User;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import br.com.adslima.entity.Roles;
+import br.com.adslima.entity.Users;
+import br.com.adslima.service.UserAuthenticationService;
+import br.com.adslima.service.UserService;
+
+
+@Service
+@Transactional
+public class UserAuthenticationServiceImpl implements UserAuthenticationService {
+
+ private static Logger logger = LoggerFactory.getLogger(UserAuthenticationServiceImpl.class);
+
+ @Autowired
+ private UserService userService;
+
+ @Override
+ public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
+ Users user = userService.findByUsername(username);
+ if (user == null) {
+ throw new UsernameNotFoundException("User not found");
+ }
+ String password = user.getPassword();
+ Set roles = user.getRoles();
+ List userGrants = new ArrayList<>();
+ for (Roles role : roles) {
+ GrantedAuthority userGrant = new SimpleGrantedAuthority(role.getRoleName());
+ userGrants.add(userGrant);
+ }
+ logger.info("user roles : {}", roles);
+ return new User(username, password, userGrants);
+ }
+
+}
diff --git a/auth-service/src/main/java/br/com/adslima/service/impl/UserServiceImpl.java b/auth-service/src/main/java/br/com/adslima/service/impl/UserServiceImpl.java
new file mode 100644
index 00000000..6f48ddaa
--- /dev/null
+++ b/auth-service/src/main/java/br/com/adslima/service/impl/UserServiceImpl.java
@@ -0,0 +1,35 @@
+package br.com.adslima.service.impl;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import br.com.adslima.entity.Users;
+import br.com.adslima.exception.NotFoundException;
+import br.com.adslima.repository.UsersRepository;
+import br.com.adslima.service.UserService;
+
+
+@Service
+public class UserServiceImpl implements UserService {
+
+ @Autowired
+ private UsersRepository userRepository;
+
+ @Override
+ public Users findByUsername(String username) {
+ Users user = userRepository.findByUserName(username);
+ if (user == null) {
+ throw new NotFoundException("User not found : " + username);
+ }
+ return user;
+ }
+
+ @Override
+ public Users findByUserId(Integer userId) {
+ Users user = userRepository.findOne(userId);
+ if (user == null) {
+ throw new NotFoundException("User not found : " + userId);
+ }
+ return user;
+ }
+}
diff --git a/auth-service/src/main/resources/application.properties b/auth-service/src/main/resources/application.properties
new file mode 100644
index 00000000..b54f4d8e
--- /dev/null
+++ b/auth-service/src/main/resources/application.properties
@@ -0,0 +1,30 @@
+server.port=8787
+server.context-path=/auth-api
+spring.application.name=auth-service
+
+################ DB Connection #################
+spring.datasource.url=jdbc:h2:mem:auth_service
+spring.datasource.username=
+spring.datasource.password=
+spring.datasource.dataSourceClassName=org.h2.jdbcx.JdbcDataSource
+spring.datasource.schema=classpath:/schema.sql
+spring.datasource.data=classpath:/data.sql
+
+# JPA properties
+spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
+spring.jpa.database=H2
+spring.jpa.openInView=false
+spring.jpa.show_sql=true
+spring.jpa.generate-ddl=false
+spring.jpa.hibernate.ddl-auto=none
+
+# Discovery Server Access
+eureka.client.serviceUrl.defaultZone=http://${EUREKA_IP:localhost}:8761/eureka/
+eureka.instance.preferIpAddress=true
+eureka.instance.leaseRenewalIntervalInSeconds: 1
+eureka.instance.leaseExpirationDurationInSeconds: 2
+
+security.basic.enabled=false
+
+#To maintain oauth filter ordering
+security.oauth2.resource.filter-order=3
diff --git a/auth-service/src/main/resources/data.sql b/auth-service/src/main/resources/data.sql
new file mode 100644
index 00000000..4fc93c9a
--- /dev/null
+++ b/auth-service/src/main/resources/data.sql
@@ -0,0 +1,11 @@
+INSERT INTO oauth_client_details(client_id, client_secret, scope, authorized_grant_types, authorities, access_token_validity, refresh_token_validity)
+VALUES ('trusted-app', '$2a$08$V0jF.bqEvkO9EhpSEfXi5OZ2JAygrAyz/8X3BLCKHgc2pPyBi4Spy', 'read,write,trust', 'client_credentials,authorization_code,implicit,password,refresh_token','ROLE_CLIENT,ROLE_TRUSTED_CLIENT','45000', '45000');
+
+INSERT INTO `users` (`user_id`, `email`, `first_name`, `last_name`, `mobile`, `password`, `user_name`)
+VALUES(123456,'demo@example.com', 'Demo', 'Example', '1234578632', '$2a$08$V0jF.bqEvkO9EhpSEfXi5OZ2JAygrAyz/8X3BLCKHgc2pPyBi4Spy', 'demo');
+
+INSERT INTO `roles` (`role_id`, `role_name`)
+VALUES(1234, 'ROLE_DEMO');
+
+INSERT INTO `user_roles` (`role_id`, `user_id`)
+VALUES(1234, 123456);
\ No newline at end of file
diff --git a/auth-service/src/main/resources/schema.sql b/auth-service/src/main/resources/schema.sql
new file mode 100644
index 00000000..f856f289
--- /dev/null
+++ b/auth-service/src/main/resources/schema.sql
@@ -0,0 +1,69 @@
+CREATE TABLE oauth_client_details (
+ client_id VARCHAR(255) PRIMARY KEY,
+ resource_ids VARCHAR(255),
+ client_secret VARCHAR(255),
+ scope VARCHAR(255),
+ authorized_grant_types VARCHAR(255),
+ web_server_redirect_uri VARCHAR(255),
+ authorities VARCHAR(255),
+ access_token_validity INTEGER,
+ refresh_token_validity INTEGER,
+ additional_information VARCHAR(255),
+ autoapprove VARCHAR(255)
+ );
+
+CREATE TABLE oauth_client_token (
+ token_id VARCHAR(255),
+ token BLOB,
+ authentication_id VARCHAR(255),
+ user_name VARCHAR(255),
+ client_id VARCHAR(255)
+);
+
+CREATE TABLE oauth_access_token (
+ token_id VARCHAR(255),
+ token BLOB,
+ authentication_id VARCHAR(255),
+ user_name VARCHAR(255),
+ client_id VARCHAR(255),
+ authentication BLOB,
+ refresh_token VARCHAR(255)
+);
+CREATE TABLE oauth_refresh_token (
+ token_id VARCHAR(255),
+ token BLOB,
+ authentication BLOB
+);
+CREATE TABLE oauth_code (
+ code VARCHAR(255), authentication BLOB
+);
+
+CREATE TABLE `users` (
+ `user_id` int(10) unsigned NOT NULL,
+ `email` varchar(45) DEFAULT NULL,
+ `first_name` varchar(45) DEFAULT NULL,
+ `last_name` varchar(45) DEFAULT NULL,
+ `mobile` varchar(15) DEFAULT NULL,
+ `password` varchar(255) NOT NULL,
+ `user_name` varchar(45) NOT NULL,
+ PRIMARY KEY (`user_id`),
+ UNIQUE KEY `UK_k8d0f2n7n88w1a16yhua64onx` (`user_name`),
+ UNIQUE KEY `UK_6dotkott2kjsp8vw4d0m25fb7` (`email`),
+ UNIQUE KEY `UK_63cf888pmqtt5tipcne79xsbm` (`mobile`)
+);
+
+CREATE TABLE `roles` (
+ `role_id` smallint(5) unsigned NOT NULL,
+ `role_name` varchar(45) NOT NULL,
+ PRIMARY KEY (`role_id`),
+ UNIQUE KEY `UK3cyq9kgtpbol1tlouij82oufa` (`role_id`,`role_name`),
+ UNIQUE KEY `UK_716hgxp60ym1lifrdgp67xt5k` (`role_name`)
+);
+
+CREATE TABLE `user_roles` (
+ `user_id` int(10) unsigned NOT NULL,
+ `role_id` smallint(5) unsigned NOT NULL,
+ PRIMARY KEY (`user_id`,`role_id`),
+ CONSTRAINT `FKh8ciramu9cc9q3qcqiv4ue8a6` FOREIGN KEY (`role_id`) REFERENCES `roles` (`role_id`),
+ CONSTRAINT `FKhfh9dx7w3ubf1co1vdev94g3f` FOREIGN KEY (`user_id`) REFERENCES `users` (`user_id`)
+);
diff --git a/auth-service/src/main/resources/sql/data.sql b/auth-service/src/main/resources/sql/data.sql
new file mode 100644
index 00000000..d725ce2f
--- /dev/null
+++ b/auth-service/src/main/resources/sql/data.sql
@@ -0,0 +1,32 @@
+
+DELETE FROM oauth_client_details;
+
+INSERT INTO user (username, email, password, activated)
+SELECT * FROM (SELECT 'admin', 'admin@admin.com', '$2a$10$r0RFDmpneBVryx.ihHK9gu6FFJQi4nTxQUqzdSTvrPpaKZMxigqpy', true) AS tmp
+WHERE NOT EXISTS (
+ SELECT username FROM user WHERE username = 'admin'
+) LIMIT 1;
+
+INSERT INTO authority (name)
+SELECT * FROM (SELECT 'ROLE_USER') AS tmp
+WHERE NOT EXISTS (
+ SELECT name FROM authority WHERE name = 'ROLE_USER'
+) LIMIT 1;
+
+INSERT INTO authority (name)
+SELECT * FROM (SELECT 'ROLE_ADMIN') AS tmp
+WHERE NOT EXISTS (
+ SELECT name FROM authority WHERE name = 'ROLE_ADMIN'
+) LIMIT 1;
+
+INSERT INTO user_authority (username, authority)
+SELECT * FROM (SELECT 'admin', 'ROLE_USER') AS tmp
+WHERE NOT EXISTS (
+ SELECT username, authority FROM user_authority WHERE username = 'admin' and authority = 'ROLE_USER'
+) LIMIT 1;
+
+INSERT INTO user_authority (username, authority)
+SELECT * FROM (SELECT 'admin', 'ROLE_ADMIN') AS tmp
+WHERE NOT EXISTS (
+ SELECT username, authority FROM user_authority WHERE username = 'admin' and authority = 'ROLE_ADMIN'
+) LIMIT 1;
\ No newline at end of file
diff --git a/auth-service/src/main/resources/sql/schema.sql b/auth-service/src/main/resources/sql/schema.sql
new file mode 100644
index 00000000..ac56e40f
--- /dev/null
+++ b/auth-service/src/main/resources/sql/schema.sql
@@ -0,0 +1,50 @@
+CREATE TABLE IF NOT EXISTS user (
+ username VARCHAR(50) NOT NULL PRIMARY KEY,
+ email VARCHAR(50),
+ password VARCHAR(500),
+ activated BOOLEAN DEFAULT FALSE,
+ activationkey VARCHAR(50) DEFAULT NULL,
+ resetpasswordkey VARCHAR(50) DEFAULT NULL
+);
+
+CREATE TABLE IF NOT EXISTS authority (
+ name VARCHAR(50) NOT NULL PRIMARY KEY
+);
+
+CREATE TABLE IF NOT EXISTS user_authority (
+ username VARCHAR(50) NOT NULL,
+ authority VARCHAR(50) NOT NULL,
+ FOREIGN KEY (username) REFERENCES user (username),
+ FOREIGN KEY (authority) REFERENCES authority (name),
+ UNIQUE INDEX user_authority_idx_1 (username, authority)
+);
+
+CREATE TABLE IF NOT EXISTS oauth_access_token (
+ token_id VARCHAR(256) DEFAULT NULL,
+ token BLOB,
+ authentication_id VARCHAR(256) DEFAULT NULL,
+ user_name VARCHAR(256) DEFAULT NULL,
+ client_id VARCHAR(256) DEFAULT NULL,
+ authentication BLOB,
+ refresh_token VARCHAR(256) DEFAULT NULL
+);
+
+CREATE TABLE IF NOT EXISTS oauth_refresh_token (
+ token_id VARCHAR(256) DEFAULT NULL,
+ token BLOB,
+ authentication BLOB
+);
+
+CREATE TABLE IF NOT EXISTS oauth_client_details (
+ client_id VARCHAR(255) PRIMARY KEY,
+ resource_ids VARCHAR(255),
+ client_secret VARCHAR(255),
+ scope VARCHAR(255),
+ authorized_grant_types VARCHAR(255),
+ web_server_redirect_uri VARCHAR(255),
+ authorities VARCHAR(255),
+ access_token_validity INTEGER,
+ refresh_token_validity INTEGER,
+ additional_information VARCHAR(4096),
+ autoapprove VARCHAR(255)
+);
\ No newline at end of file
diff --git a/auth-service/src/test/java/br/com/adslima/ExpenseManagementAuthServerApplicationTests.java b/auth-service/src/test/java/br/com/adslima/ExpenseManagementAuthServerApplicationTests.java
new file mode 100644
index 00000000..a94db150
--- /dev/null
+++ b/auth-service/src/test/java/br/com/adslima/ExpenseManagementAuthServerApplicationTests.java
@@ -0,0 +1,16 @@
+package br.com.adslima;
+
+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 ExpenseManagementAuthServerApplicationTests {
+
+ @Test
+ public void contextLoads() {
+ }
+
+}
diff --git a/auth-service/target/classes/META-INF/MANIFEST.MF b/auth-service/target/classes/META-INF/MANIFEST.MF
new file mode 100644
index 00000000..e6b1f5b8
--- /dev/null
+++ b/auth-service/target/classes/META-INF/MANIFEST.MF
@@ -0,0 +1,11 @@
+Manifest-Version: 1.0
+Implementation-Title: auth-service
+Implementation-Version: 0.0.1-SNAPSHOT
+Built-By: andrews.silva
+Implementation-Vendor-Id: br.com.adslima
+Build-Jdk: 1.8.0_201
+Implementation-URL: http://projects.spring.io/spring-boot/auth-service
+ /
+Created-By: Maven Integration for Eclipse
+Implementation-Vendor: Pivotal Software, Inc.
+
diff --git a/auth-service/target/classes/META-INF/maven/br.com.adslima/auth-service/pom.properties b/auth-service/target/classes/META-INF/maven/br.com.adslima/auth-service/pom.properties
new file mode 100644
index 00000000..009ca2f4
--- /dev/null
+++ b/auth-service/target/classes/META-INF/maven/br.com.adslima/auth-service/pom.properties
@@ -0,0 +1,7 @@
+#Generated by Maven Integration for Eclipse
+#Thu Mar 14 19:46:13 GMT-03:00 2019
+version=0.0.1-SNAPSHOT
+groupId=br.com.adslima
+m2e.projectName=auth-service
+m2e.projectLocation=C\:\\Users\\andrews.silva\\Documents\\santander_bkp\\TestBackJava\\auth-service
+artifactId=auth-service
diff --git a/auth-service/target/classes/META-INF/maven/br.com.adslima/auth-service/pom.xml b/auth-service/target/classes/META-INF/maven/br.com.adslima/auth-service/pom.xml
new file mode 100644
index 00000000..0b9dff1f
--- /dev/null
+++ b/auth-service/target/classes/META-INF/maven/br.com.adslima/auth-service/pom.xml
@@ -0,0 +1,101 @@
+
+
+ 4.0.0
+
+ br.com.adslima
+ auth-service
+ 0.0.1-SNAPSHOT
+ jar
+
+ auth-service
+ Oauth Server
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 1.5.4.RELEASE
+
+
+
+
+ UTF-8
+ UTF-8
+ 1.8
+ Dalston.SR2
+
+
+
+
+
+
+
+
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-starter-eureka
+
+
+
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-starter-oauth2
+
+
+
+ org.springframework.boot
+ spring-boot-starter-security
+
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+
+ com.h2database
+ h2
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-dependencies
+ ${spring-cloud.version}
+ pom
+ import
+
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
+
+
diff --git a/auth-service/target/classes/application.properties b/auth-service/target/classes/application.properties
new file mode 100644
index 00000000..b54f4d8e
--- /dev/null
+++ b/auth-service/target/classes/application.properties
@@ -0,0 +1,30 @@
+server.port=8787
+server.context-path=/auth-api
+spring.application.name=auth-service
+
+################ DB Connection #################
+spring.datasource.url=jdbc:h2:mem:auth_service
+spring.datasource.username=
+spring.datasource.password=
+spring.datasource.dataSourceClassName=org.h2.jdbcx.JdbcDataSource
+spring.datasource.schema=classpath:/schema.sql
+spring.datasource.data=classpath:/data.sql
+
+# JPA properties
+spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
+spring.jpa.database=H2
+spring.jpa.openInView=false
+spring.jpa.show_sql=true
+spring.jpa.generate-ddl=false
+spring.jpa.hibernate.ddl-auto=none
+
+# Discovery Server Access
+eureka.client.serviceUrl.defaultZone=http://${EUREKA_IP:localhost}:8761/eureka/
+eureka.instance.preferIpAddress=true
+eureka.instance.leaseRenewalIntervalInSeconds: 1
+eureka.instance.leaseExpirationDurationInSeconds: 2
+
+security.basic.enabled=false
+
+#To maintain oauth filter ordering
+security.oauth2.resource.filter-order=3
diff --git a/auth-service/target/classes/data.sql b/auth-service/target/classes/data.sql
new file mode 100644
index 00000000..4fc93c9a
--- /dev/null
+++ b/auth-service/target/classes/data.sql
@@ -0,0 +1,11 @@
+INSERT INTO oauth_client_details(client_id, client_secret, scope, authorized_grant_types, authorities, access_token_validity, refresh_token_validity)
+VALUES ('trusted-app', '$2a$08$V0jF.bqEvkO9EhpSEfXi5OZ2JAygrAyz/8X3BLCKHgc2pPyBi4Spy', 'read,write,trust', 'client_credentials,authorization_code,implicit,password,refresh_token','ROLE_CLIENT,ROLE_TRUSTED_CLIENT','45000', '45000');
+
+INSERT INTO `users` (`user_id`, `email`, `first_name`, `last_name`, `mobile`, `password`, `user_name`)
+VALUES(123456,'demo@example.com', 'Demo', 'Example', '1234578632', '$2a$08$V0jF.bqEvkO9EhpSEfXi5OZ2JAygrAyz/8X3BLCKHgc2pPyBi4Spy', 'demo');
+
+INSERT INTO `roles` (`role_id`, `role_name`)
+VALUES(1234, 'ROLE_DEMO');
+
+INSERT INTO `user_roles` (`role_id`, `user_id`)
+VALUES(1234, 123456);
\ No newline at end of file
diff --git a/auth-service/target/classes/schema.sql b/auth-service/target/classes/schema.sql
new file mode 100644
index 00000000..f856f289
--- /dev/null
+++ b/auth-service/target/classes/schema.sql
@@ -0,0 +1,69 @@
+CREATE TABLE oauth_client_details (
+ client_id VARCHAR(255) PRIMARY KEY,
+ resource_ids VARCHAR(255),
+ client_secret VARCHAR(255),
+ scope VARCHAR(255),
+ authorized_grant_types VARCHAR(255),
+ web_server_redirect_uri VARCHAR(255),
+ authorities VARCHAR(255),
+ access_token_validity INTEGER,
+ refresh_token_validity INTEGER,
+ additional_information VARCHAR(255),
+ autoapprove VARCHAR(255)
+ );
+
+CREATE TABLE oauth_client_token (
+ token_id VARCHAR(255),
+ token BLOB,
+ authentication_id VARCHAR(255),
+ user_name VARCHAR(255),
+ client_id VARCHAR(255)
+);
+
+CREATE TABLE oauth_access_token (
+ token_id VARCHAR(255),
+ token BLOB,
+ authentication_id VARCHAR(255),
+ user_name VARCHAR(255),
+ client_id VARCHAR(255),
+ authentication BLOB,
+ refresh_token VARCHAR(255)
+);
+CREATE TABLE oauth_refresh_token (
+ token_id VARCHAR(255),
+ token BLOB,
+ authentication BLOB
+);
+CREATE TABLE oauth_code (
+ code VARCHAR(255), authentication BLOB
+);
+
+CREATE TABLE `users` (
+ `user_id` int(10) unsigned NOT NULL,
+ `email` varchar(45) DEFAULT NULL,
+ `first_name` varchar(45) DEFAULT NULL,
+ `last_name` varchar(45) DEFAULT NULL,
+ `mobile` varchar(15) DEFAULT NULL,
+ `password` varchar(255) NOT NULL,
+ `user_name` varchar(45) NOT NULL,
+ PRIMARY KEY (`user_id`),
+ UNIQUE KEY `UK_k8d0f2n7n88w1a16yhua64onx` (`user_name`),
+ UNIQUE KEY `UK_6dotkott2kjsp8vw4d0m25fb7` (`email`),
+ UNIQUE KEY `UK_63cf888pmqtt5tipcne79xsbm` (`mobile`)
+);
+
+CREATE TABLE `roles` (
+ `role_id` smallint(5) unsigned NOT NULL,
+ `role_name` varchar(45) NOT NULL,
+ PRIMARY KEY (`role_id`),
+ UNIQUE KEY `UK3cyq9kgtpbol1tlouij82oufa` (`role_id`,`role_name`),
+ UNIQUE KEY `UK_716hgxp60ym1lifrdgp67xt5k` (`role_name`)
+);
+
+CREATE TABLE `user_roles` (
+ `user_id` int(10) unsigned NOT NULL,
+ `role_id` smallint(5) unsigned NOT NULL,
+ PRIMARY KEY (`user_id`,`role_id`),
+ CONSTRAINT `FKh8ciramu9cc9q3qcqiv4ue8a6` FOREIGN KEY (`role_id`) REFERENCES `roles` (`role_id`),
+ CONSTRAINT `FKhfh9dx7w3ubf1co1vdev94g3f` FOREIGN KEY (`user_id`) REFERENCES `users` (`user_id`)
+);
diff --git a/auth-service/target/classes/sql/data.sql b/auth-service/target/classes/sql/data.sql
new file mode 100644
index 00000000..d725ce2f
--- /dev/null
+++ b/auth-service/target/classes/sql/data.sql
@@ -0,0 +1,32 @@
+
+DELETE FROM oauth_client_details;
+
+INSERT INTO user (username, email, password, activated)
+SELECT * FROM (SELECT 'admin', 'admin@admin.com', '$2a$10$r0RFDmpneBVryx.ihHK9gu6FFJQi4nTxQUqzdSTvrPpaKZMxigqpy', true) AS tmp
+WHERE NOT EXISTS (
+ SELECT username FROM user WHERE username = 'admin'
+) LIMIT 1;
+
+INSERT INTO authority (name)
+SELECT * FROM (SELECT 'ROLE_USER') AS tmp
+WHERE NOT EXISTS (
+ SELECT name FROM authority WHERE name = 'ROLE_USER'
+) LIMIT 1;
+
+INSERT INTO authority (name)
+SELECT * FROM (SELECT 'ROLE_ADMIN') AS tmp
+WHERE NOT EXISTS (
+ SELECT name FROM authority WHERE name = 'ROLE_ADMIN'
+) LIMIT 1;
+
+INSERT INTO user_authority (username, authority)
+SELECT * FROM (SELECT 'admin', 'ROLE_USER') AS tmp
+WHERE NOT EXISTS (
+ SELECT username, authority FROM user_authority WHERE username = 'admin' and authority = 'ROLE_USER'
+) LIMIT 1;
+
+INSERT INTO user_authority (username, authority)
+SELECT * FROM (SELECT 'admin', 'ROLE_ADMIN') AS tmp
+WHERE NOT EXISTS (
+ SELECT username, authority FROM user_authority WHERE username = 'admin' and authority = 'ROLE_ADMIN'
+) LIMIT 1;
\ No newline at end of file
diff --git a/auth-service/target/classes/sql/schema.sql b/auth-service/target/classes/sql/schema.sql
new file mode 100644
index 00000000..ac56e40f
--- /dev/null
+++ b/auth-service/target/classes/sql/schema.sql
@@ -0,0 +1,50 @@
+CREATE TABLE IF NOT EXISTS user (
+ username VARCHAR(50) NOT NULL PRIMARY KEY,
+ email VARCHAR(50),
+ password VARCHAR(500),
+ activated BOOLEAN DEFAULT FALSE,
+ activationkey VARCHAR(50) DEFAULT NULL,
+ resetpasswordkey VARCHAR(50) DEFAULT NULL
+);
+
+CREATE TABLE IF NOT EXISTS authority (
+ name VARCHAR(50) NOT NULL PRIMARY KEY
+);
+
+CREATE TABLE IF NOT EXISTS user_authority (
+ username VARCHAR(50) NOT NULL,
+ authority VARCHAR(50) NOT NULL,
+ FOREIGN KEY (username) REFERENCES user (username),
+ FOREIGN KEY (authority) REFERENCES authority (name),
+ UNIQUE INDEX user_authority_idx_1 (username, authority)
+);
+
+CREATE TABLE IF NOT EXISTS oauth_access_token (
+ token_id VARCHAR(256) DEFAULT NULL,
+ token BLOB,
+ authentication_id VARCHAR(256) DEFAULT NULL,
+ user_name VARCHAR(256) DEFAULT NULL,
+ client_id VARCHAR(256) DEFAULT NULL,
+ authentication BLOB,
+ refresh_token VARCHAR(256) DEFAULT NULL
+);
+
+CREATE TABLE IF NOT EXISTS oauth_refresh_token (
+ token_id VARCHAR(256) DEFAULT NULL,
+ token BLOB,
+ authentication BLOB
+);
+
+CREATE TABLE IF NOT EXISTS oauth_client_details (
+ client_id VARCHAR(255) PRIMARY KEY,
+ resource_ids VARCHAR(255),
+ client_secret VARCHAR(255),
+ scope VARCHAR(255),
+ authorized_grant_types VARCHAR(255),
+ web_server_redirect_uri VARCHAR(255),
+ authorities VARCHAR(255),
+ access_token_validity INTEGER,
+ refresh_token_validity INTEGER,
+ additional_information VARCHAR(4096),
+ autoapprove VARCHAR(255)
+);
\ No newline at end of file
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 00000000..77895966
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,105 @@
+version: '3'
+services:
+
+ mysql-expenseManagement:
+ image: mysql:5.7
+ container_name: mysql
+ expose:
+ - 3306
+ ports:
+ - 3306:3306
+ environment:
+ - MYSQL_ROOT_PASSWORD=root
+ volumes:
+ - /data/mysql
+
+ solr:
+ image: adslima/solr:1.1
+ container_name: solr
+ expose:
+ - '8983'
+ entrypoint:
+ - docker-entrypoint.sh
+ - solr-precreate
+ - categories-core
+ ports:
+ - 8983:8983
+
+ rabbitmq:
+ image: rabbitmq:management
+ container_name: rabbit
+ expose:
+ - '15672'
+ ports:
+ - 5672:5672
+ - 15672:15672
+
+ eureka:
+ build: ./service-registry
+ container_name: eureka
+ ports:
+ - 8761:8761
+
+ zuul:
+ build: ./service-gateway
+ container_name: zuul
+ ports:
+ - 8765:8765
+ expose:
+ - 8765
+ environment:
+ EUREKA_IP: eureka
+ depends_on:
+ - eureka
+
+ oauth:
+ build: ./auth-service
+ container_name: oauth
+ ports:
+ - 8787:8787
+ expose:
+ - '8787'
+ environment:
+ EUREKA_IP: eureka
+ depends_on:
+ - eureka
+
+ expense-management-command:
+ build: ./expense-management-command
+ container_name: command
+ ports:
+ - 8081:8081
+ expose:
+ - '8081'
+ environment:
+ MYSQL_IP: mysql-expenseManagement
+ EUREKA_IP: eureka
+ OAUTH_IP: oauth
+ SOLR_IP: solr
+ RABBITMQ_IP: rabbitmq
+ depends_on:
+ - eureka
+ - mysql-expenseManagement
+ - solr
+ - rabbitmq
+ - oauth
+
+ expense-management-query:
+ build: ./expense-management-query
+ container_name: query
+ ports:
+ - 8082:8082
+ expose:
+ - '8082'
+ environment:
+ MYSQL_IP: mysql-expenseManagement
+ EUREKA_IP: eureka
+ OAUTH_IP: oauth
+ SOLR_IP: solr
+ RABBITMQ_IP: rabbitmq
+ depends_on:
+ - eureka
+ - mysql-expenseManagement
+ - solr
+ - rabbitmq
+ - oauth
\ No newline at end of file
diff --git a/expense-management-command/.gitignore b/expense-management-command/.gitignore
new file mode 100644
index 00000000..82eca336
--- /dev/null
+++ b/expense-management-command/.gitignore
@@ -0,0 +1,25 @@
+/target/
+!.mvn/wrapper/maven-wrapper.jar
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/build/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
\ No newline at end of file
diff --git a/expense-management-command/Dockerfile b/expense-management-command/Dockerfile
new file mode 100644
index 00000000..2767dfcc
--- /dev/null
+++ b/expense-management-command/Dockerfile
@@ -0,0 +1,9 @@
+FROM java:8-jre-alpine
+
+RUN mkdir -p /expense-management-command
+ADD target/expense-management-command-0.0.1-SNAPSHOT.jar /expense-management-command
+ADD target/classes/bootstrap.yml /expense-management-command
+
+WORKDIR /expense-management-command
+
+ENTRYPOINT ["java", "-jar", "expense-management-command-0.0.1-SNAPSHOT.jar"]
\ No newline at end of file
diff --git a/expense-management-command/pom.xml b/expense-management-command/pom.xml
new file mode 100644
index 00000000..3a129d52
--- /dev/null
+++ b/expense-management-command/pom.xml
@@ -0,0 +1,177 @@
+
+
+ 4.0.0
+
+ br.com.adslima
+ expense-management-command
+ 0.0.1-SNAPSHOT
+ jar
+
+ expense-management-command
+ Expense-Management-Command
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.0.4.RELEASE
+
+
+
+
+
+ UTF-8
+ UTF-8
+ 1.8
+ 3.3.4
+ 1.18.2
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+ org.springframework.cloud
+ spring-cloud-starter-oauth2
+ 2.0.0.RELEASE
+
+
+
+ org.springframework.cloud
+ spring-cloud-commons
+ 2.0.0.RELEASE
+
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+
+ org.springframework.boot
+ spring-boot-starter-jetty
+
+
+
+
+
+ com.h2database
+ h2
+
+
+
+ mysql
+ mysql-connector-java
+
+
+
+ org.springframework.boot
+ spring-boot-starter-amqp
+
+
+
+ org.axonframework
+ axon-spring-boot-starter
+ ${axon.version}
+
+
+ org.axonframework
+ axon-amqp
+ ${axon.version}
+
+
+
+ org.projectlombok
+ lombok
+ provided
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+ org.springframework.security
+ spring-security-test
+ test
+
+
+
+ org.springframework.cloud
+ spring-cloud-starter-netflix-eureka-client
+ 2.0.0.RELEASE
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-starter-netflix-zuul
+ 2.0.0.RELEASE
+
+
+
+
+ com.google.guava
+ guava
+ 19.0
+
+
+
+
+
+
+
+
+
+ org.springframework.retry
+ spring-retry
+
+
+
+ com.fasterxml.jackson.datatype
+ jackson-datatype-jdk8
+
+
+
+ org.axonframework
+ axon-test
+ ${axon.version}
+ test
+
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+ 1.8
+ 1.8
+
+
+
+
+
diff --git a/expense-management-command/src/main/java/br/com/adslima/ExpenseManagementCommandApplication.java b/expense-management-command/src/main/java/br/com/adslima/ExpenseManagementCommandApplication.java
new file mode 100644
index 00000000..12f597e2
--- /dev/null
+++ b/expense-management-command/src/main/java/br/com/adslima/ExpenseManagementCommandApplication.java
@@ -0,0 +1,60 @@
+package br.com.adslima;
+
+import java.util.List;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cloud.client.ServiceInstance;
+import org.springframework.cloud.client.discovery.DiscoveryClient;
+import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
+import org.springframework.cloud.context.config.annotation.RefreshScope;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ *
+ * @author andrews.silva
+ *
+ */
+@EnableDiscoveryClient
+@SpringBootApplication
+@EnableResourceServer
+@ComponentScan("br.com.adslima")
+public class ExpenseManagementCommandApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(ExpenseManagementCommandApplication.class, args);
+ }
+
+ @RestController
+ class ServiceInstanceRestController {
+
+ @Autowired
+ private DiscoveryClient discoveryClient;
+
+ @Value("${spring.application.name}")
+ private String appName;
+
+ @RequestMapping("/instances")
+ public List serviceInstancesByApplicationName() {
+ return this.discoveryClient.getInstances(appName);
+ }
+ }
+
+ @RefreshScope
+ @RestController
+ class MessageRestController {
+
+ @Value("${message}")
+ private String message;
+
+ @RequestMapping("/message")
+ String getMessage() {
+ return this.message;
+ }
+ }
+}
diff --git a/expense-management-command/src/main/java/br/com/adslima/aggregate/ExpenseManagementAggregate.java b/expense-management-command/src/main/java/br/com/adslima/aggregate/ExpenseManagementAggregate.java
new file mode 100644
index 00000000..3ed58282
--- /dev/null
+++ b/expense-management-command/src/main/java/br/com/adslima/aggregate/ExpenseManagementAggregate.java
@@ -0,0 +1,91 @@
+package br.com.adslima.aggregate;
+
+import static org.axonframework.commandhandling.model.AggregateLifecycle.apply;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+import org.axonframework.commandhandling.CommandHandler;
+import org.axonframework.commandhandling.model.AggregateIdentifier;
+import org.axonframework.eventsourcing.EventSourcingHandler;
+import org.axonframework.spring.stereotype.Aggregate;
+import org.springframework.util.Assert;
+
+import br.com.adslima.command.AddExpenseManagementCommand;
+import br.com.adslima.command.UpdateCategoryExpenseManagementCommand;
+import br.com.adslima.events.ExpenseManagementAddedEvent;
+import br.com.adslima.events.ExpenseManagementCategoryUpdatedEvent;
+import br.com.adslima.model.ExpenseCategory;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ *
+ * @author andrews.silva
+ *
+ */
+@Slf4j
+@Getter
+@Aggregate
+@NoArgsConstructor
+public class ExpenseManagementAggregate {
+
+ @AggregateIdentifier
+ private String id;
+ private Integer userCode;
+ private String description;
+ private LocalDateTime date;
+ private BigDecimal value;
+ private ExpenseCategory category;
+
+ @CommandHandler
+ public ExpenseManagementAggregate(AddExpenseManagementCommand cmd) {
+ log.info("Handling {} command: {}", cmd.getClass().getSimpleName(), cmd);
+
+ Assert.hasLength(cmd.getId(), "Id não deve estar vazio ou nulo.");
+ Assert.notNull(cmd.getUserCode(), "UserCode não deve estar vazio ou nulo.");
+ Assert.hasLength(cmd.getDescription(), "Description não deve estar vazio ou nulo.");
+ Assert.notNull(cmd.getDate(), "Date não deve estar vazio ou nulo.");
+ Assert.notNull(cmd.getValue(), "Value não deve estar vazio ou nulo.");
+
+ apply(new ExpenseManagementAddedEvent(cmd.getId(), cmd.getUserCode(), cmd.getDescription(), cmd.getDate(),
+ cmd.getValue(), cmd.getCategory()));
+
+ log.info("Done handling {} command: {}", cmd.getClass().getSimpleName(), cmd);
+ }
+
+ @CommandHandler
+ public void handle(UpdateCategoryExpenseManagementCommand cmd) {
+ log.info("Handling {} command: {}", cmd.getClass().getSimpleName(), cmd);
+
+ Assert.hasLength(cmd.getCardExpenseId(), "ExpenseCardId não deve estar vazio ou nulo.");
+ Assert.notNull(cmd.getCategory(), "Category não deve estar vazio ou nulo.");
+
+ apply(new ExpenseManagementCategoryUpdatedEvent(cmd.getCardExpenseId(), cmd.getCategory()));
+
+ log.info("Done handling {} command: {}", cmd.getClass().getSimpleName(), cmd);
+ }
+
+ @EventSourcingHandler
+ public void on(ExpenseManagementAddedEvent event) {
+ log.info("Handling {} event: {}", event.getClass().getSimpleName(), event);
+
+ this.id = event.getId();
+ this.userCode = event.getUserCode();
+ this.description = event.getDescription();
+ this.date = event.getDate();
+ this.value = event.getValue();
+
+ log.info("Done handling {} event: {}", event.getClass().getSimpleName(), event);
+ }
+
+ @EventSourcingHandler
+ public void on(ExpenseManagementCategoryUpdatedEvent event) {
+
+ log.info("Handling {} event: {}", event.getClass().getSimpleName(), event);
+ this.category = event.getCategory();
+ log.info("Done handling {} event: {}", event.getClass().getSimpleName(), event);
+ }
+
+}
\ No newline at end of file
diff --git a/expense-management-command/src/main/java/br/com/adslima/command/AddExpenseManagementCommand.java b/expense-management-command/src/main/java/br/com/adslima/command/AddExpenseManagementCommand.java
new file mode 100644
index 00000000..7fc50a65
--- /dev/null
+++ b/expense-management-command/src/main/java/br/com/adslima/command/AddExpenseManagementCommand.java
@@ -0,0 +1,30 @@
+package br.com.adslima.command;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+import org.axonframework.commandhandling.TargetAggregateIdentifier;
+
+import br.com.adslima.model.ExpenseCategory;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.ToString;
+
+/**
+ *
+ * @author andrews.silva
+ *
+ */
+@Getter
+@ToString
+@AllArgsConstructor
+public class AddExpenseManagementCommand {
+
+ @TargetAggregateIdentifier
+ private String id;
+ private Integer userCode;
+ private String description;
+ private LocalDateTime date;
+ private BigDecimal value;
+ private ExpenseCategory category;
+}
\ No newline at end of file
diff --git a/expense-management-command/src/main/java/br/com/adslima/command/UpdateCategoryExpenseManagementCommand.java b/expense-management-command/src/main/java/br/com/adslima/command/UpdateCategoryExpenseManagementCommand.java
new file mode 100644
index 00000000..0916b686
--- /dev/null
+++ b/expense-management-command/src/main/java/br/com/adslima/command/UpdateCategoryExpenseManagementCommand.java
@@ -0,0 +1,23 @@
+package br.com.adslima.command;
+
+import org.axonframework.commandhandling.TargetAggregateIdentifier;
+
+import br.com.adslima.model.ExpenseCategory;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.ToString;
+
+/**
+ *
+ * @author andrews.silva
+ *
+ */
+@Getter
+@ToString
+@AllArgsConstructor
+public class UpdateCategoryExpenseManagementCommand {
+
+ @TargetAggregateIdentifier
+ private String cardExpenseId;
+ private ExpenseCategory category;
+}
diff --git a/expense-management-command/src/main/java/br/com/adslima/configuration/AmqpEventPublicationConfiguration.java b/expense-management-command/src/main/java/br/com/adslima/configuration/AmqpEventPublicationConfiguration.java
new file mode 100644
index 00000000..0998dc93
--- /dev/null
+++ b/expense-management-command/src/main/java/br/com/adslima/configuration/AmqpEventPublicationConfiguration.java
@@ -0,0 +1,47 @@
+package br.com.adslima.configuration;
+
+import org.springframework.amqp.core.AmqpAdmin;
+import org.springframework.amqp.core.Binding;
+import org.springframework.amqp.core.BindingBuilder;
+import org.springframework.amqp.core.Exchange;
+import org.springframework.amqp.core.ExchangeBuilder;
+import org.springframework.amqp.core.Queue;
+import org.springframework.amqp.core.QueueBuilder;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ *
+ * @author andrews.silva
+ *
+ */
+@Configuration
+public class AmqpEventPublicationConfiguration {
+
+ @Value("${axon.amqp.exchange:expense-management.events}")
+ String exchangeName;
+
+ @Bean
+ public Exchange exchange() {
+ return ExchangeBuilder.fanoutExchange(exchangeName).build();
+ }
+
+ @Bean
+ public Queue queue() {
+ return QueueBuilder.durable(exchangeName).build();
+ }
+
+ @Bean
+ public Binding binding(Queue queue, Exchange exchange) {
+ return BindingBuilder.bind(queue).to(exchange).with("*").noargs();
+ }
+
+ @Autowired
+ public void configure(AmqpAdmin amqpAdmin, Exchange exchange, Queue queue, Binding binding) {
+ amqpAdmin.declareExchange(exchange);
+ amqpAdmin.declareQueue(queue);
+ amqpAdmin.declareBinding(binding);
+ }
+}
diff --git a/expense-management-command/src/main/java/br/com/adslima/controller/EventController.java b/expense-management-command/src/main/java/br/com/adslima/controller/EventController.java
new file mode 100644
index 00000000..7227379b
--- /dev/null
+++ b/expense-management-command/src/main/java/br/com/adslima/controller/EventController.java
@@ -0,0 +1,33 @@
+package br.com.adslima.controller;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.axonframework.eventsourcing.eventstore.EventStore;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import lombok.AllArgsConstructor;
+
+/**
+ *
+ * @author andrews.silva
+ *
+ */
+@RestController
+@AllArgsConstructor
+@RequestMapping("/events")
+public class EventController {
+
+ private EventStore eventStore;
+
+ @GetMapping
+ @RequestMapping("/{aggregateId}")
+ @Transactional(readOnly = true)
+ public List