Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bruno Carreira C Silva #56

Open
wants to merge 26 commits into
base: master
Choose a base branch
from
Open
4 changes: 4 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
FROM openjdk:8
ADD target/user-search.jar user-search.jar
EXPOSE 8086
ENTRYPOINT ["java", "-jar", "user-search.jar"]
42 changes: 42 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,45 @@ Faça um ***Fork*** deste repositório e abra um ***Pull Request***, **com seu n
- Ter um desempenho elevado num conjunto de dados muito grande
- Utilizar o Docker

-----

# Solução

O backend foi desenvolvido com a utilização do framework Spring Data REST, com banco H2 embedded.
Uma UI simples foi construída em ReactJS para listar o resultado da busca com paginação.

### UI

A aplicação ReactJS utiliza webpack para compilar todos os arquivos num bundle. Usa rest.js para comunicação com a API REST. E usa babel para compilar ES6 para ES5.

### Segurança

A API está protegida por BASIC AUTHENTICATION, via Spring Security (usuário e senha: bruno).

### Docker

Um arquivo Dockerfile foi adicionado ao projeto permitindo a criação de uma imagem baseada no JDK8.
Para gerar a imagem, basta executar do diretório do projeto:

`docker build . -t user-search`

Depois de baixar a imagem, basta executá-la:

`docker run image user-search`

### Executando na máquina local

Não é necessário ter npm e nodeJS na máquina local para executar a UI.
Basta executar:

`mvn spring-boot:run`

Esta operação demandará um bom tempo porque, além de instalar npm e nodeJS embedded, vai fazer o download do arquivo users.csv.gz, descompactá-lo e carregar o banco de dados H2.
Após a execução correta do comando acima, basta acessar a URL (`http://localhost:8086/`) no browser.
Caso queira acessar diretamente a API, basta acessar `http://localhost:8086/api/users` ou `http://localhost:8086/api/users/search/listUsers?nome=nome`

## Melhorias

- Otimizar o banco de dados H2 para melhor performance de LOAD e SEARCH
- Utilizar um banco de dados noSQL
- Criar mais unit tests e integration tests
44 changes: 44 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{
"name": "spring-data-rest-and-reactjs",
"version": "0.1.0",
"description": "Demo of ReactJS + Spring Data REST",
"repository": {
"type": "git",
"url": "[email protected]:spring-guides/tut-react-and-spring-data-rest.git"
},
"keywords": [
"rest",
"hateoas",
"spring",
"data",
"react"
],
"author": "Greg L. Turnquist",
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/spring-guides/tut-react-and-spring-data-rest/issues"
},
"homepage": "https://github.com/spring-guides/tut-react-and-spring-data-rest",
"dependencies": {
"bootstrap": "^4.1.3",
"bootstrap-less": "^3.3.8",
"less": "^3.8.1",
"react": "^15.3.2",
"react-data-grid": "^4.0.8",
"react-dom": "^15.3.2",
"react-js-pagination": "^3.0.2",
"rest": "^1.3.1",
"webpack": "^1.12.2"
},
"scripts": {
"watch": "webpack --watch -d"
},
"devDependencies": {
"babel-core": "^6.18.2",
"babel-loader": "^6.2.7",
"babel-polyfill": "^6.16.0",
"babel-preset-es2015": "^6.18.0",
"babel-preset-react": "^6.16.0",
"less-loader": "^4.1.0"
}
}
126 changes: 126 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.picpay</groupId>
<artifactId>trabalhe-conosco-backend-dev</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>

<name>PicPay User Manager Service</name>
<description>User Manager Service using Spring Boot</description>

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

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<!--<scope>runtime</scope>-->
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-csv</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-batch</artifactId>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<finalName>user-search</finalName>
</configuration>
</plugin>

<plugin>
<groupId>com.github.eirslett</groupId>
<artifactId>frontend-maven-plugin</artifactId>
<version>1.2</version>
<configuration>
<installDirectory>target</installDirectory>
</configuration>
<executions>
<execution>
<id>install node and npm</id>
<goals>
<goal>install-node-and-npm</goal>
</goals>
<configuration>
<nodeVersion>v4.4.5</nodeVersion>
<npmVersion>3.9.2</npmVersion>
</configuration>
</execution>
<execution>
<id>npm install</id>
<goals>
<goal>npm</goal>
</goals>
<configuration>
<arguments>install</arguments>
</configuration>
</execution>
<execution>
<id>webpack build</id>
<goals>
<goal>webpack</goal>
</goals>
</execution>
</executions>
</plugin>

</plugins>
</build>
</project>
40 changes: 40 additions & 0 deletions src/main/java/com/picpay/ApplicationStartup.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.picpay;

import com.picpay.repositories.UserRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

import java.util.List;


/**
* @author Bruno Carreira
*/
// tag::code[]
@Component
public class ApplicationStartup implements CommandLineRunner {
@Autowired
private List<String> listRelevancia1;

@Autowired
private List<String> listRelevancia2;

@Autowired
private UserRepository repo;

private static final Logger LOG =
LoggerFactory.getLogger(ApplicationStartup.class);

@Override
public void run(String... strings) throws Exception {
LOG.info("Updating priority....");
repo.updatePriorityByIds(listRelevancia1, 1);
repo.updatePriorityByIds(listRelevancia2, 2);
LOG.info("Priority updated!");
}

}
// end::code[]
58 changes: 58 additions & 0 deletions src/main/java/com/picpay/DatasourceConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.picpay;


import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.h2.jdbcx.JdbcDataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;
import java.io.File;
import java.io.InputStream;
import java.net.URL;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.zip.GZIPInputStream;


@Configuration
public class DatasourceConfig {

@Value("${com.picpay.csv.database.url}")
private final String csvDatabaseUrl = null;

private static final Logger LOG =
LoggerFactory.getLogger(DatasourceConfig.class);

private static final String CSV_PATH = "src/main/resources/users.csv";

@Bean(name = "mainDataSource")
public DataSource createMainDataSource() throws Exception {
LOG.info("Importing and extracting CSV file...");

if (!java.nio.file.Files.exists(Paths.get(CSV_PATH))) {

InputStream zipFileInputStream = new URL(csvDatabaseUrl).openStream();
GZIPInputStream is = new GZIPInputStream(zipFileInputStream);

File targetFile = new File(CSV_PATH);

java.nio.file.Files.copy(
is,
targetFile.toPath(),
StandardCopyOption.REPLACE_EXISTING);

IOUtils.closeQuietly(is);
LOG.info("CSV imported and extracted...");
}

JdbcDataSource ds = new JdbcDataSource();
ds.setURL("jdbc:h2:file:~/testdb;LOG=0;CACHE_SIZE=65536;LOCK_MODE=0;UNDO_LOG=0");
ds.setUser("sa");
ds.setPassword("");
return ds;
}
}
42 changes: 42 additions & 0 deletions src/main/java/com/picpay/UserManagerApplication.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.picpay;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
import java.util.stream.Collectors;

/**
* @author Bruno Carreira
*/
// tag::code[]
@SpringBootApplication
public class UserManagerApplication {

@Value("${com.picpay.relevancia1}")
private String relevancia1_txt = null;

@Value("${com.picpay.relevancia2}")
private String relevancia2_txt = null;

public static void main(String[] args){
SpringApplication.run(UserManagerApplication.class, args);
}

@Bean
public List<String> listRelevancia1() throws IOException {
return Files.lines(Paths.get(relevancia1_txt)).collect(Collectors.toList());
}

@Bean
public List<String> listRelevancia2() throws IOException {
return Files.lines(Paths.get(relevancia2_txt)).collect(Collectors.toList());
}

}
// end::code[]
28 changes: 28 additions & 0 deletions src/main/java/com/picpay/WebSecurityConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.picpay;

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.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("bruno").password("bruno")
.authorities("ROLE_USER");
}

@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/api/**").authenticated()
.and()
.httpBasic();
}
}
Loading