From 74285ef4687fe9d4efcac0705624aa4ebed65dc9 Mon Sep 17 00:00:00 2001 From: Thiago Sato Date: Mon, 2 Sep 2019 23:30:17 -0300 Subject: [PATCH 01/30] Criando o gitignore --- .gitignore | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..62464086 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +/target/ +.idea/ +bin +.classpath +.project +.settings/* From 88f9bcc89405fc41129cccbb7227c6aa83ad626e Mon Sep 17 00:00:00 2001 From: Thiago Sato Date: Mon, 2 Sep 2019 23:30:42 -0300 Subject: [PATCH 02/30] Iniciando o projeto --- pom.xml | 63 +++++++++++++++++++ .../interview/ExpenseManagement.java | 20 ++++++ .../interview/config/MongoConfig.java | 30 +++++++++ 3 files changed, 113 insertions(+) create mode 100644 pom.xml create mode 100644 src/main/java/com/santander/interview/ExpenseManagement.java create mode 100644 src/main/java/com/santander/interview/config/MongoConfig.java diff --git a/pom.xml b/pom.xml new file mode 100644 index 00000000..bb014c66 --- /dev/null +++ b/pom.xml @@ -0,0 +1,63 @@ + + + 4.0.0 + + com.santander.interview + TestBackJava + 1.0-SNAPSHOT + + + org.springframework.boot + spring-boot-starter-parent + 2.1.6.RELEASE + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.data + spring-data-mongodb + 2.1.9.RELEASE + + + + + 1.8 + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + 1.8 + 1.8 + UTF-8 + + + + org.springframework.boot + spring-boot-maven-plugin + + com.santander.interview.ExpenseManagement + JAR + + + + + repackage + + + + + + + + \ No newline at end of file diff --git a/src/main/java/com/santander/interview/ExpenseManagement.java b/src/main/java/com/santander/interview/ExpenseManagement.java new file mode 100644 index 00000000..3f305c80 --- /dev/null +++ b/src/main/java/com/santander/interview/ExpenseManagement.java @@ -0,0 +1,20 @@ +package com.santander.interview; + +import com.mongodb.Mongo; +import com.mongodb.MongoClient; +import com.mongodb.MongoClientURI; +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 org.springframework.context.annotation.ComponentScan; + +@SpringBootApplication +@ComponentScan(basePackages = {"com.santander.interview"}) +public class ExpenseManagement { + + public static void main(String[] args) { + SpringApplication.run(ExpenseManagement.class, args); + } + +} diff --git a/src/main/java/com/santander/interview/config/MongoConfig.java b/src/main/java/com/santander/interview/config/MongoConfig.java new file mode 100644 index 00000000..1a270baf --- /dev/null +++ b/src/main/java/com/santander/interview/config/MongoConfig.java @@ -0,0 +1,30 @@ +package com.santander.interview.config; + +import com.mongodb.MongoClient; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.mongodb.config.AbstractMongoConfiguration; + +@Configuration +public class MongoConfig extends AbstractMongoConfiguration { + @Value("#{environment['MONGO_HOST']}") + private String MONGO_HOST; + + @Value("#{environment['MONGO_PORT']}") + private int MONGO_PORT; + + @Value("#{environment['MONGO_DATABASE']}") + private String MONGO_DATABASE; + + @Bean + @Override + public MongoClient mongoClient() { + return new MongoClient(MONGO_HOST, MONGO_PORT); + } + + @Override + protected String getDatabaseName() { + return MONGO_DATABASE; + } +} From 5158726369e247f8534abd7dc7cd545eefb57768 Mon Sep 17 00:00:00 2001 From: Thiago Sato Date: Mon, 2 Sep 2019 23:41:04 -0300 Subject: [PATCH 03/30] Criando classes domain: Expense e Category para armazenamento dos dados --- .../santander/interview/domain/Category.java | 25 +++++++++++++ .../santander/interview/domain/Expense.java | 36 +++++++++++++++++++ .../santander/interview/domain/Response.java | 21 +++++++++++ 3 files changed, 82 insertions(+) create mode 100644 src/main/java/com/santander/interview/domain/Category.java create mode 100644 src/main/java/com/santander/interview/domain/Expense.java create mode 100644 src/main/java/com/santander/interview/domain/Response.java diff --git a/src/main/java/com/santander/interview/domain/Category.java b/src/main/java/com/santander/interview/domain/Category.java new file mode 100644 index 00000000..f14543a7 --- /dev/null +++ b/src/main/java/com/santander/interview/domain/Category.java @@ -0,0 +1,25 @@ +package com.santander.interview.domain; + +import org.springframework.data.annotation.Id; + +public class Category { + @Id + private String id; + private String detail; + + public Category() { } + + public Category(String id, String detail) { + this.id = id; + this.detail = detail; + } + + public String getId() { return id; } + + public void setId(String id) { this.id = id; } + + public String getDetail() { return detail; } + + public void setDetail(String detail) { this.detail = detail; } + +} diff --git a/src/main/java/com/santander/interview/domain/Expense.java b/src/main/java/com/santander/interview/domain/Expense.java new file mode 100644 index 00000000..d96ffb62 --- /dev/null +++ b/src/main/java/com/santander/interview/domain/Expense.java @@ -0,0 +1,36 @@ +package com.santander.interview.domain; + +import org.springframework.data.annotation.Id; + +import java.util.Date; + +public class Expense { + @Id + private String id; + private String descricao; + private double valor; + private int codigousuario; + private Date data; + + public Expense() { } + + public String getId() { return id; } + + public void setId(String id) { this.id = id; } + + public String getDescricao() { return descricao; } + + public void setDescricao(String descricao) { this.descricao = descricao; } + + public double getValor() { return valor; } + + public void setValor(double valor) { this.valor = valor; } + + public int getCodigousuario() { return codigousuario; } + + public void setCodigousuario(int codigousuario) { this.codigousuario = codigousuario; } + + public Date getData() { return data; } + + public void setData(Date data) { this.data = data; } +} diff --git a/src/main/java/com/santander/interview/domain/Response.java b/src/main/java/com/santander/interview/domain/Response.java new file mode 100644 index 00000000..a8c2e417 --- /dev/null +++ b/src/main/java/com/santander/interview/domain/Response.java @@ -0,0 +1,21 @@ +package com.santander.interview.domain; + +public class Response { + private String statusCode; + private String message; + private Object data; + + public Response() { } + + public String getStatusCode() { return statusCode; } + + public void setStatusCode(String statusCode) { this.statusCode = statusCode; } + + public String getMessage() { return message; } + + public void setMessage(String message) { this.message = message; } + + public Object getData() { return data; } + + public void setData(Object data) { this.data = data; } +} From 32f736a09c356a814aabda8e870230e82e55ab83 Mon Sep 17 00:00:00 2001 From: Thiago Sato Date: Tue, 3 Sep 2019 00:13:33 -0300 Subject: [PATCH 04/30] =?UTF-8?q?Integra=C3=A7=C3=A3o=20de=20gastos=20por?= =?UTF-8?q?=20cart=C3=A3o?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../santander/interview/domain/Expense.java | 14 ++++-- .../santander/interview/domain/Response.java | 12 +++-- .../repository/ExpenseRepository.java | 14 ++++++ .../interview/service/ExpenseService.java | 13 ++++++ .../service/impl/ExpenseServiceImpl.java | 44 +++++++++++++++++++ .../interview/webservice/ExpenseApi.java | 26 +++++++++++ 6 files changed, 117 insertions(+), 6 deletions(-) create mode 100644 src/main/java/com/santander/interview/repository/ExpenseRepository.java create mode 100644 src/main/java/com/santander/interview/service/ExpenseService.java create mode 100644 src/main/java/com/santander/interview/service/impl/ExpenseServiceImpl.java create mode 100644 src/main/java/com/santander/interview/webservice/ExpenseApi.java diff --git a/src/main/java/com/santander/interview/domain/Expense.java b/src/main/java/com/santander/interview/domain/Expense.java index d96ffb62..89e54409 100644 --- a/src/main/java/com/santander/interview/domain/Expense.java +++ b/src/main/java/com/santander/interview/domain/Expense.java @@ -9,7 +9,7 @@ public class Expense { private String id; private String descricao; private double valor; - private int codigousuario; + private long codigoUsuario; private Date data; public Expense() { } @@ -26,11 +26,19 @@ public Expense() { } public void setValor(double valor) { this.valor = valor; } - public int getCodigousuario() { return codigousuario; } + public long getCodigoUsuario() { return codigoUsuario; } - public void setCodigousuario(int codigousuario) { this.codigousuario = codigousuario; } + public void setCodigoUsuario(long codigoUsuario) { this.codigoUsuario = codigoUsuario; } public Date getData() { return data; } public void setData(Date data) { this.data = data; } + + @Override + public String toString() { + return String.format( + "Expense[id=%s, descricao=%s, valor=%f, codigoUsuario=%d, data=%s]", + this.id, this.descricao, this.valor, this.codigoUsuario, this.data.toString() + ); + } } diff --git a/src/main/java/com/santander/interview/domain/Response.java b/src/main/java/com/santander/interview/domain/Response.java index a8c2e417..46af5175 100644 --- a/src/main/java/com/santander/interview/domain/Response.java +++ b/src/main/java/com/santander/interview/domain/Response.java @@ -1,15 +1,21 @@ package com.santander.interview.domain; public class Response { - private String statusCode; + private long statusCode; private String message; private Object data; public Response() { } - public String getStatusCode() { return statusCode; } + public Response(long statusCode, String message, Object data) { + this.statusCode = statusCode; + this.message = message; + this.data = data; + } - public void setStatusCode(String statusCode) { this.statusCode = statusCode; } + public long getStatusCode() { return statusCode; } + + public void setStatusCode(long statusCode) { this.statusCode = statusCode; } public String getMessage() { return message; } diff --git a/src/main/java/com/santander/interview/repository/ExpenseRepository.java b/src/main/java/com/santander/interview/repository/ExpenseRepository.java new file mode 100644 index 00000000..a6d3534e --- /dev/null +++ b/src/main/java/com/santander/interview/repository/ExpenseRepository.java @@ -0,0 +1,14 @@ +package com.santander.interview.repository; + +import com.santander.interview.domain.Expense; +import org.springframework.data.mongodb.repository.MongoRepository; +import org.springframework.stereotype.Repository; + +import java.util.Date; +import java.util.List; + +@Repository +public interface ExpenseRepository extends MongoRepository { + public List findByCodigoUsuario(String codigoUsuario); + public List findByCodigoUsuarioAndData(long codigoUsuario, Date date); +} diff --git a/src/main/java/com/santander/interview/service/ExpenseService.java b/src/main/java/com/santander/interview/service/ExpenseService.java new file mode 100644 index 00000000..fcaab119 --- /dev/null +++ b/src/main/java/com/santander/interview/service/ExpenseService.java @@ -0,0 +1,13 @@ +package com.santander.interview.service; + +import com.santander.interview.domain.Expense; + +import java.util.List; + +public interface ExpenseService { + public void addNewExpense(Expense expense); + public List getListExpenses(); + public List findExpenseByCodigoUsuarioAndData(); + public List findExpenseByCodigoUsuario(); + public void updateExpense(Expense expense); +} diff --git a/src/main/java/com/santander/interview/service/impl/ExpenseServiceImpl.java b/src/main/java/com/santander/interview/service/impl/ExpenseServiceImpl.java new file mode 100644 index 00000000..8fe13ce6 --- /dev/null +++ b/src/main/java/com/santander/interview/service/impl/ExpenseServiceImpl.java @@ -0,0 +1,44 @@ +package com.santander.interview.service.impl; + +import com.santander.interview.domain.Expense; +import com.santander.interview.repository.ExpenseRepository; +import com.santander.interview.service.ExpenseService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.UUID; + +@Service +public class ExpenseServiceImpl implements ExpenseService { + @Autowired + ExpenseRepository expenseRepository; + + private String generateUuid() { return UUID.randomUUID().toString(); } + + @Override + public void addNewExpense(Expense expense) { + expense.setId(this.generateUuid()); + expenseRepository.save(expense); + } + + @Override + public List getListExpenses() { + return expenseRepository.findAll(); + } + + @Override + public List findExpenseByCodigoUsuarioAndData() { + return null; + } + + @Override + public List findExpenseByCodigoUsuario() { + return null; + } + + @Override + public void updateExpense(Expense expense) { + + } +} diff --git a/src/main/java/com/santander/interview/webservice/ExpenseApi.java b/src/main/java/com/santander/interview/webservice/ExpenseApi.java new file mode 100644 index 00000000..1e602a2e --- /dev/null +++ b/src/main/java/com/santander/interview/webservice/ExpenseApi.java @@ -0,0 +1,26 @@ +package com.santander.interview.webservice; + +import com.santander.interview.domain.Expense; +import com.santander.interview.domain.Response; +import com.santander.interview.service.ExpenseService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class ExpenseApi { + @Autowired + ExpenseService expenseService; + + @PostMapping("/expenses") + public ResponseEntity addExpense(@RequestBody Expense expense) { + this.expenseService.addNewExpense(expense); + return new ResponseEntity<>( + new Response(HttpStatus.OK.value(), "Criado com sucesso", null), + HttpStatus.OK + ); + } +} From a025adcf0831852f93be9533f193b2e4d1095539 Mon Sep 17 00:00:00 2001 From: Thiago Sato Date: Tue, 3 Sep 2019 00:31:57 -0300 Subject: [PATCH 05/30] =?UTF-8?q?Listagem=20de=20gastos=20para=20o=20usu?= =?UTF-8?q?=C3=A1rio?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../santander/interview/ExpenseManagement.java | 5 ----- .../repository/ExpenseRepository.java | 2 +- .../interview/service/ExpenseService.java | 3 +-- .../service/impl/ExpenseServiceImpl.java | 18 +++++++++++------- .../interview/webservice/ExpenseApi.java | 17 ++++++++++++++--- 5 files changed, 27 insertions(+), 18 deletions(-) diff --git a/src/main/java/com/santander/interview/ExpenseManagement.java b/src/main/java/com/santander/interview/ExpenseManagement.java index 3f305c80..38f1c95d 100644 --- a/src/main/java/com/santander/interview/ExpenseManagement.java +++ b/src/main/java/com/santander/interview/ExpenseManagement.java @@ -1,12 +1,7 @@ package com.santander.interview; -import com.mongodb.Mongo; -import com.mongodb.MongoClient; -import com.mongodb.MongoClientURI; -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 org.springframework.context.annotation.ComponentScan; @SpringBootApplication diff --git a/src/main/java/com/santander/interview/repository/ExpenseRepository.java b/src/main/java/com/santander/interview/repository/ExpenseRepository.java index a6d3534e..9df8ed69 100644 --- a/src/main/java/com/santander/interview/repository/ExpenseRepository.java +++ b/src/main/java/com/santander/interview/repository/ExpenseRepository.java @@ -9,6 +9,6 @@ @Repository public interface ExpenseRepository extends MongoRepository { - public List findByCodigoUsuario(String codigoUsuario); + public List findByCodigoUsuario(long codigoUsuario); public List findByCodigoUsuarioAndData(long codigoUsuario, Date date); } diff --git a/src/main/java/com/santander/interview/service/ExpenseService.java b/src/main/java/com/santander/interview/service/ExpenseService.java index fcaab119..d2ac1d84 100644 --- a/src/main/java/com/santander/interview/service/ExpenseService.java +++ b/src/main/java/com/santander/interview/service/ExpenseService.java @@ -6,8 +6,7 @@ public interface ExpenseService { public void addNewExpense(Expense expense); - public List getListExpenses(); + public List findExpenseByCodigoUsuario(long codigoUsuario); public List findExpenseByCodigoUsuarioAndData(); - public List findExpenseByCodigoUsuario(); public void updateExpense(Expense expense); } diff --git a/src/main/java/com/santander/interview/service/impl/ExpenseServiceImpl.java b/src/main/java/com/santander/interview/service/impl/ExpenseServiceImpl.java index 8fe13ce6..da34f1f0 100644 --- a/src/main/java/com/santander/interview/service/impl/ExpenseServiceImpl.java +++ b/src/main/java/com/santander/interview/service/impl/ExpenseServiceImpl.java @@ -6,6 +6,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import java.util.Collections; +import java.util.Comparator; import java.util.List; import java.util.UUID; @@ -23,8 +25,15 @@ public void addNewExpense(Expense expense) { } @Override - public List getListExpenses() { - return expenseRepository.findAll(); + public List findExpenseByCodigoUsuario(long codigoUsuario) { + List expenses = expenseRepository.findByCodigoUsuario(codigoUsuario); + Collections.sort(expenses, new Comparator() { + @Override + public int compare(Expense expense1, Expense expense2) { + return expense2.getData().compareTo(expense1.getData()); + } + }); + return expenses; } @Override @@ -32,11 +41,6 @@ public List findExpenseByCodigoUsuarioAndData() { return null; } - @Override - public List findExpenseByCodigoUsuario() { - return null; - } - @Override public void updateExpense(Expense expense) { diff --git a/src/main/java/com/santander/interview/webservice/ExpenseApi.java b/src/main/java/com/santander/interview/webservice/ExpenseApi.java index 1e602a2e..2ddd27ec 100644 --- a/src/main/java/com/santander/interview/webservice/ExpenseApi.java +++ b/src/main/java/com/santander/interview/webservice/ExpenseApi.java @@ -6,9 +6,9 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; + +import java.util.List; @RestController public class ExpenseApi { @@ -23,4 +23,15 @@ public ResponseEntity addExpense(@RequestBody Expense expense) { HttpStatus.OK ); } + + @GetMapping("/expense/userCode/{userCode}") + public ResponseEntity getExpenseByUserCode(@PathVariable long userCode) { + List expensesByUserCode = this.expenseService.findExpenseByCodigoUsuario(userCode); + return new ResponseEntity<>( + new Response(HttpStatus.OK.value(), "Criado com sucesso", expensesByUserCode), + HttpStatus.OK + ); + } + + } From c0c40e40e12c2c85bd83eb663e5089a57b6b7ba6 Mon Sep 17 00:00:00 2001 From: Thiago Sato Date: Tue, 3 Sep 2019 06:22:08 -0300 Subject: [PATCH 06/30] =?UTF-8?q?Filtro=20de=20gastos=20por=20usu=C3=A1rio?= =?UTF-8?q?=20e=20por=20data?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/ExpenseRepository.java | 2 +- .../interview/service/ExpenseService.java | 3 ++- .../service/impl/ExpenseServiceImpl.java | 16 +++++++++------ .../interview/webservice/ExpenseApi.java | 20 ++++++++++++++++++- 4 files changed, 32 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/santander/interview/repository/ExpenseRepository.java b/src/main/java/com/santander/interview/repository/ExpenseRepository.java index 9df8ed69..f3c09c9d 100644 --- a/src/main/java/com/santander/interview/repository/ExpenseRepository.java +++ b/src/main/java/com/santander/interview/repository/ExpenseRepository.java @@ -10,5 +10,5 @@ @Repository public interface ExpenseRepository extends MongoRepository { public List findByCodigoUsuario(long codigoUsuario); - public List findByCodigoUsuarioAndData(long codigoUsuario, Date date); + public List findByCodigoUsuarioAndDataBetween(long codigoUsuario, Date startData, Date endData); } diff --git a/src/main/java/com/santander/interview/service/ExpenseService.java b/src/main/java/com/santander/interview/service/ExpenseService.java index d2ac1d84..7aa02cee 100644 --- a/src/main/java/com/santander/interview/service/ExpenseService.java +++ b/src/main/java/com/santander/interview/service/ExpenseService.java @@ -2,11 +2,12 @@ import com.santander.interview.domain.Expense; +import java.text.ParseException; import java.util.List; public interface ExpenseService { public void addNewExpense(Expense expense); public List findExpenseByCodigoUsuario(long codigoUsuario); - public List findExpenseByCodigoUsuarioAndData(); + public List findExpenseByCodigoUsuarioAndData(long codigoUsuario, String data) throws ParseException; public void updateExpense(Expense expense); } diff --git a/src/main/java/com/santander/interview/service/impl/ExpenseServiceImpl.java b/src/main/java/com/santander/interview/service/impl/ExpenseServiceImpl.java index da34f1f0..94fdb7cc 100644 --- a/src/main/java/com/santander/interview/service/impl/ExpenseServiceImpl.java +++ b/src/main/java/com/santander/interview/service/impl/ExpenseServiceImpl.java @@ -6,10 +6,12 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; -import java.util.UUID; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.*; @Service public class ExpenseServiceImpl implements ExpenseService { @@ -37,8 +39,10 @@ public int compare(Expense expense1, Expense expense2) { } @Override - public List findExpenseByCodigoUsuarioAndData() { - return null; + public List findExpenseByCodigoUsuarioAndData(long codigoUsuario, String data) throws ParseException { + Date startDate = new SimpleDateFormat("ddMMyyyy").parse(data); + Date endDate = new Date(startDate.getTime() + (1000 * 60 * 60 * 24)); + return expenseRepository.findByCodigoUsuarioAndDataBetween(codigoUsuario, startDate, endDate); } @Override diff --git a/src/main/java/com/santander/interview/webservice/ExpenseApi.java b/src/main/java/com/santander/interview/webservice/ExpenseApi.java index 2ddd27ec..fb10e4fb 100644 --- a/src/main/java/com/santander/interview/webservice/ExpenseApi.java +++ b/src/main/java/com/santander/interview/webservice/ExpenseApi.java @@ -8,6 +8,7 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import java.text.ParseException; import java.util.List; @RestController @@ -28,10 +29,27 @@ public ResponseEntity addExpense(@RequestBody Expense expense) { public ResponseEntity getExpenseByUserCode(@PathVariable long userCode) { List expensesByUserCode = this.expenseService.findExpenseByCodigoUsuario(userCode); return new ResponseEntity<>( - new Response(HttpStatus.OK.value(), "Criado com sucesso", expensesByUserCode), + new Response(HttpStatus.OK.value(), "Busca por código do usuário realizada com sucesso", + expensesByUserCode), HttpStatus.OK ); } + @GetMapping("/expense/userCode/{userCode}/date/{date}") + public ResponseEntity getExpenseByUserCodeAndDate(@PathVariable long userCode, @PathVariable String date) { + try { + List expensesByUserCodeAndDate = this.expenseService.findExpenseByCodigoUsuarioAndData(userCode, date); + return new ResponseEntity<>( + new Response(HttpStatus.OK.value(), "Busca por código do usuário e pela data realizada com sucesso", + expensesByUserCodeAndDate), + HttpStatus.OK + ); + } catch (ParseException pe){ + return new ResponseEntity<>( + new Response(HttpStatus.BAD_REQUEST.value(), "Data mal formatada", null), + HttpStatus.BAD_REQUEST + ); + } + } } From 7cba83b5a3519e8e5520081769c289ccc85b6074 Mon Sep 17 00:00:00 2001 From: Thiago Sato Date: Tue, 3 Sep 2019 22:45:52 -0300 Subject: [PATCH 07/30] =?UTF-8?q?Funcionalidade:=20Categoriza=C3=A7=C3=A3o?= =?UTF-8?q?=20de=20gastos?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/CategoryRepository.java | 10 +++++++ .../interview/service/CategoryService.java | 7 +++++ .../interview/service/ExpenseService.java | 2 +- .../service/impl/CategoryServiceImpl.java | 23 +++++++++++++++ .../service/impl/ExpenseServiceImpl.java | 13 ++++++--- .../interview/webservice/CategoryApi.java | 29 +++++++++++++++++++ .../interview/webservice/ExpenseApi.java | 10 +++++++ 7 files changed, 89 insertions(+), 5 deletions(-) create mode 100644 src/main/java/com/santander/interview/repository/CategoryRepository.java create mode 100644 src/main/java/com/santander/interview/service/CategoryService.java create mode 100644 src/main/java/com/santander/interview/service/impl/CategoryServiceImpl.java create mode 100644 src/main/java/com/santander/interview/webservice/CategoryApi.java diff --git a/src/main/java/com/santander/interview/repository/CategoryRepository.java b/src/main/java/com/santander/interview/repository/CategoryRepository.java new file mode 100644 index 00000000..1542db99 --- /dev/null +++ b/src/main/java/com/santander/interview/repository/CategoryRepository.java @@ -0,0 +1,10 @@ +package com.santander.interview.repository; + +import com.santander.interview.domain.Category; +import org.springframework.data.mongodb.repository.MongoRepository; + +import java.util.List; + +public interface CategoryRepository extends MongoRepository { + public List findByDetailLike(String detail); +} diff --git a/src/main/java/com/santander/interview/service/CategoryService.java b/src/main/java/com/santander/interview/service/CategoryService.java new file mode 100644 index 00000000..004c36b2 --- /dev/null +++ b/src/main/java/com/santander/interview/service/CategoryService.java @@ -0,0 +1,7 @@ +package com.santander.interview.service; + +import com.santander.interview.domain.Category; + +public interface CategoryService { + public void saveCategory(Category category); +} diff --git a/src/main/java/com/santander/interview/service/ExpenseService.java b/src/main/java/com/santander/interview/service/ExpenseService.java index 7aa02cee..e945abe0 100644 --- a/src/main/java/com/santander/interview/service/ExpenseService.java +++ b/src/main/java/com/santander/interview/service/ExpenseService.java @@ -9,5 +9,5 @@ public interface ExpenseService { public void addNewExpense(Expense expense); public List findExpenseByCodigoUsuario(long codigoUsuario); public List findExpenseByCodigoUsuarioAndData(long codigoUsuario, String data) throws ParseException; - public void updateExpense(Expense expense); + public void updateExpense(String id, Expense expense); } diff --git a/src/main/java/com/santander/interview/service/impl/CategoryServiceImpl.java b/src/main/java/com/santander/interview/service/impl/CategoryServiceImpl.java new file mode 100644 index 00000000..ab00b30b --- /dev/null +++ b/src/main/java/com/santander/interview/service/impl/CategoryServiceImpl.java @@ -0,0 +1,23 @@ +package com.santander.interview.service.impl; + +import com.santander.interview.domain.Category; +import com.santander.interview.repository.CategoryRepository; +import com.santander.interview.service.CategoryService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.UUID; + +@Service +public class CategoryServiceImpl implements CategoryService { + @Autowired + CategoryRepository categoryRepository; + + private String generateUuid() { return UUID.randomUUID().toString(); } + + @Override + public void saveCategory(Category category) { + category.setId(generateUuid()); + categoryRepository.save(category); + } +} diff --git a/src/main/java/com/santander/interview/service/impl/ExpenseServiceImpl.java b/src/main/java/com/santander/interview/service/impl/ExpenseServiceImpl.java index 94fdb7cc..a1a8aa76 100644 --- a/src/main/java/com/santander/interview/service/impl/ExpenseServiceImpl.java +++ b/src/main/java/com/santander/interview/service/impl/ExpenseServiceImpl.java @@ -23,7 +23,7 @@ public class ExpenseServiceImpl implements ExpenseService { @Override public void addNewExpense(Expense expense) { expense.setId(this.generateUuid()); - expenseRepository.save(expense); + this.expenseRepository.save(expense); } @Override @@ -42,11 +42,16 @@ public int compare(Expense expense1, Expense expense2) { public List findExpenseByCodigoUsuarioAndData(long codigoUsuario, String data) throws ParseException { Date startDate = new SimpleDateFormat("ddMMyyyy").parse(data); Date endDate = new Date(startDate.getTime() + (1000 * 60 * 60 * 24)); - return expenseRepository.findByCodigoUsuarioAndDataBetween(codigoUsuario, startDate, endDate); + return this.expenseRepository.findByCodigoUsuarioAndDataBetween(codigoUsuario, startDate, endDate); } @Override - public void updateExpense(Expense expense) { - + public void updateExpense(String id, Expense expense) { + this.expenseRepository.findById(id).ifPresent( + searchResult -> { + expense.setId(searchResult.getId()); + this.expenseRepository.save(expense); + } + ); } } diff --git a/src/main/java/com/santander/interview/webservice/CategoryApi.java b/src/main/java/com/santander/interview/webservice/CategoryApi.java new file mode 100644 index 00000000..248b39fa --- /dev/null +++ b/src/main/java/com/santander/interview/webservice/CategoryApi.java @@ -0,0 +1,29 @@ +package com.santander.interview.webservice; + +import com.santander.interview.domain.Category; +import com.santander.interview.domain.Response; +import com.santander.interview.service.CategoryService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class CategoryApi { + + @Autowired + CategoryService categoryService; + + @PostMapping("/categories") + public ResponseEntity addCategory(@RequestBody Category category) { + categoryService.saveCategory(category); + + return new ResponseEntity<>( + new Response(HttpStatus.OK.value(), "Categoria criada.", null), + HttpStatus.OK + ); + } + +} diff --git a/src/main/java/com/santander/interview/webservice/ExpenseApi.java b/src/main/java/com/santander/interview/webservice/ExpenseApi.java index fb10e4fb..025c53df 100644 --- a/src/main/java/com/santander/interview/webservice/ExpenseApi.java +++ b/src/main/java/com/santander/interview/webservice/ExpenseApi.java @@ -13,6 +13,7 @@ @RestController public class ExpenseApi { + @Autowired ExpenseService expenseService; @@ -50,6 +51,15 @@ public ResponseEntity getExpenseByUserCodeAndDate(@PathVariable long u HttpStatus.BAD_REQUEST ); } + } + + @PutMapping("/expense/{id}") + public ResponseEntity updateExpense(@PathVariable String id, @RequestBody Expense expense) { + this.expenseService.updateExpense(id, expense); + return new ResponseEntity<>( + new Response(HttpStatus.OK.value(), "Dado atualizado", null), + HttpStatus.OK + ); } } From 3b9f8b4604f7c11d1c737a4ba4c47d8e257a13dd Mon Sep 17 00:00:00 2001 From: Thiago Sato Date: Tue, 3 Sep 2019 22:52:02 -0300 Subject: [PATCH 08/30] =?UTF-8?q?Funcionalidade:=20Sugest=C3=A3o=20de=20ca?= =?UTF-8?q?tegoria?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../interview/service/CategoryService.java | 3 +++ .../service/impl/CategoryServiceImpl.java | 8 +++++++- .../interview/webservice/CategoryApi.java | 16 +++++++++++++--- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/santander/interview/service/CategoryService.java b/src/main/java/com/santander/interview/service/CategoryService.java index 004c36b2..ae935ab2 100644 --- a/src/main/java/com/santander/interview/service/CategoryService.java +++ b/src/main/java/com/santander/interview/service/CategoryService.java @@ -2,6 +2,9 @@ import com.santander.interview.domain.Category; +import java.util.List; + public interface CategoryService { public void saveCategory(Category category); + public List searchCategoryByDetailPrefix(String detailPrefix); } diff --git a/src/main/java/com/santander/interview/service/impl/CategoryServiceImpl.java b/src/main/java/com/santander/interview/service/impl/CategoryServiceImpl.java index ab00b30b..9cb37cba 100644 --- a/src/main/java/com/santander/interview/service/impl/CategoryServiceImpl.java +++ b/src/main/java/com/santander/interview/service/impl/CategoryServiceImpl.java @@ -6,6 +6,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import java.util.List; import java.util.UUID; @Service @@ -18,6 +19,11 @@ public class CategoryServiceImpl implements CategoryService { @Override public void saveCategory(Category category) { category.setId(generateUuid()); - categoryRepository.save(category); + this.categoryRepository.save(category); + } + + @Override + public List searchCategoryByDetailPrefix(String detailPrefix) { + return this.categoryRepository.findByDetailLike(detailPrefix); } } diff --git a/src/main/java/com/santander/interview/webservice/CategoryApi.java b/src/main/java/com/santander/interview/webservice/CategoryApi.java index 248b39fa..dd1ffba9 100644 --- a/src/main/java/com/santander/interview/webservice/CategoryApi.java +++ b/src/main/java/com/santander/interview/webservice/CategoryApi.java @@ -6,9 +6,9 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; + +import java.util.List; @RestController public class CategoryApi { @@ -26,4 +26,14 @@ public ResponseEntity addCategory(@RequestBody Category category) { ); } + @GetMapping("/category/detail/{detailPrefix}") + public ResponseEntity suggestionCategory(@PathVariable String detailPrefix) { + List categories = this.categoryService.searchCategoryByDetailPrefix(detailPrefix); + + return new ResponseEntity<>( + new Response(HttpStatus.OK.value(), "Resultado da busca de categorias", categories), + HttpStatus.OK + ); + } + } From 29641107f7aa2404a026a2ed6020273efc9be068 Mon Sep 17 00:00:00 2001 From: Thiago Sato Date: Tue, 3 Sep 2019 23:13:44 -0300 Subject: [PATCH 09/30] =?UTF-8?q?Funcionalidade:=20Categoriza=C3=A7=C3=A3o?= =?UTF-8?q?=20automatica=20de=20gasto?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../santander/interview/domain/Expense.java | 5 +++++ .../repository/CategoryRepository.java | 1 + .../service/impl/ExpenseServiceImpl.java | 18 +++++++++++++++++- 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/santander/interview/domain/Expense.java b/src/main/java/com/santander/interview/domain/Expense.java index 89e54409..49ec7d8d 100644 --- a/src/main/java/com/santander/interview/domain/Expense.java +++ b/src/main/java/com/santander/interview/domain/Expense.java @@ -11,6 +11,7 @@ public class Expense { private double valor; private long codigoUsuario; private Date data; + private Category category; public Expense() { } @@ -34,6 +35,10 @@ public Expense() { } public void setData(Date data) { this.data = data; } + public Category getCategory() { return category; } + + public void setCategory(Category category) { this.category = category; } + @Override public String toString() { return String.format( diff --git a/src/main/java/com/santander/interview/repository/CategoryRepository.java b/src/main/java/com/santander/interview/repository/CategoryRepository.java index 1542db99..2f032f1a 100644 --- a/src/main/java/com/santander/interview/repository/CategoryRepository.java +++ b/src/main/java/com/santander/interview/repository/CategoryRepository.java @@ -6,5 +6,6 @@ import java.util.List; public interface CategoryRepository extends MongoRepository { + public List findByDetail(String detail); public List findByDetailLike(String detail); } diff --git a/src/main/java/com/santander/interview/service/impl/ExpenseServiceImpl.java b/src/main/java/com/santander/interview/service/impl/ExpenseServiceImpl.java index a1a8aa76..44181edd 100644 --- a/src/main/java/com/santander/interview/service/impl/ExpenseServiceImpl.java +++ b/src/main/java/com/santander/interview/service/impl/ExpenseServiceImpl.java @@ -1,6 +1,8 @@ package com.santander.interview.service.impl; +import com.santander.interview.domain.Category; import com.santander.interview.domain.Expense; +import com.santander.interview.repository.CategoryRepository; import com.santander.interview.repository.ExpenseRepository; import com.santander.interview.service.ExpenseService; import org.springframework.beans.factory.annotation.Autowired; @@ -18,10 +20,23 @@ public class ExpenseServiceImpl implements ExpenseService { @Autowired ExpenseRepository expenseRepository; + @Autowired + CategoryRepository categoryRepository; + private String generateUuid() { return UUID.randomUUID().toString(); } + private Category automaticCategorization(Expense expense) { + Category category = expense.getCategory(); + if(category != null && category.getDetail() != null) { + return categoryRepository.findByDetail(category.getDetail()).get(0); + } + + return null; + } + @Override public void addNewExpense(Expense expense) { + expense.setCategory(this.automaticCategorization(expense)); expense.setId(this.generateUuid()); this.expenseRepository.save(expense); } @@ -40,8 +55,9 @@ public int compare(Expense expense1, Expense expense2) { @Override public List findExpenseByCodigoUsuarioAndData(long codigoUsuario, String data) throws ParseException { + long oneDayInMilliseconds = 1000 * 60 * 60 * 24; Date startDate = new SimpleDateFormat("ddMMyyyy").parse(data); - Date endDate = new Date(startDate.getTime() + (1000 * 60 * 60 * 24)); + Date endDate = new Date(startDate.getTime() + oneDayInMilliseconds); return this.expenseRepository.findByCodigoUsuarioAndDataBetween(codigoUsuario, startDate, endDate); } From 20c6af4d175e78d7538895afb4d5f9eb1240ce5f Mon Sep 17 00:00:00 2001 From: Thiago Sato Date: Thu, 5 Sep 2019 01:53:30 -0300 Subject: [PATCH 10/30] =?UTF-8?q?Testes=20unit=C3=A1rios?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../interview/ExpenseManagementTest.java | 12 ++ .../interview/config/MongoConfigTest.java | 21 ++++ .../interview/domain/CategoryTest.java | 30 +++++ .../interview/domain/ExpenseTest.java | 43 +++++++ .../interview/domain/ResponseTest.java | 33 +++++ .../service/CategoryServiceTest.java | 57 +++++++++ .../interview/service/ExpenseServiceTest.java | 117 ++++++++++++++++++ .../interview/webservice/CategoryApiTest.java | 50 ++++++++ .../interview/webservice/ExpenseApiTest.java | 105 ++++++++++++++++ 9 files changed, 468 insertions(+) create mode 100644 src/test/java/com/santander/interview/ExpenseManagementTest.java create mode 100644 src/test/java/com/santander/interview/config/MongoConfigTest.java create mode 100644 src/test/java/com/santander/interview/domain/CategoryTest.java create mode 100644 src/test/java/com/santander/interview/domain/ExpenseTest.java create mode 100644 src/test/java/com/santander/interview/domain/ResponseTest.java create mode 100644 src/test/java/com/santander/interview/service/CategoryServiceTest.java create mode 100644 src/test/java/com/santander/interview/service/ExpenseServiceTest.java create mode 100644 src/test/java/com/santander/interview/webservice/CategoryApiTest.java create mode 100644 src/test/java/com/santander/interview/webservice/ExpenseApiTest.java diff --git a/src/test/java/com/santander/interview/ExpenseManagementTest.java b/src/test/java/com/santander/interview/ExpenseManagementTest.java new file mode 100644 index 00000000..bf0f20e7 --- /dev/null +++ b/src/test/java/com/santander/interview/ExpenseManagementTest.java @@ -0,0 +1,12 @@ +package com.santander.interview; + +import org.junit.Test; + +public class ExpenseManagementTest { + + @Test(expected = Exception.class) + public void mainStartDispatcherTest() { + String[] args = new String[]{}; + ExpenseManagement.main(args); + } +} diff --git a/src/test/java/com/santander/interview/config/MongoConfigTest.java b/src/test/java/com/santander/interview/config/MongoConfigTest.java new file mode 100644 index 00000000..fe3dff85 --- /dev/null +++ b/src/test/java/com/santander/interview/config/MongoConfigTest.java @@ -0,0 +1,21 @@ +package com.santander.interview.config; + +import org.junit.Assert; +import org.junit.Test; +import org.mockito.InjectMocks; + +public class MongoConfigTest { + @InjectMocks + MongoConfig mongoConfig = new MongoConfig(); + + @Test + public void mongoClientTest() { + Assert.assertNotNull(mongoConfig.mongoClient()); + } + + @Test + public void mongoDatabaseTest() { + Assert.assertNull(mongoConfig.getDatabaseName()); + } + +} diff --git a/src/test/java/com/santander/interview/domain/CategoryTest.java b/src/test/java/com/santander/interview/domain/CategoryTest.java new file mode 100644 index 00000000..c39135b1 --- /dev/null +++ b/src/test/java/com/santander/interview/domain/CategoryTest.java @@ -0,0 +1,30 @@ +package com.santander.interview.domain; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class CategoryTest { + private static final String ID = "123"; + private static final String DETAIL = "teste"; + + @Test + public void CategoryTest() { + Category category = new Category(); + category.setId(ID); + category.setDetail(DETAIL); + + Assert.assertEquals(category.getId(), ID); + Assert.assertEquals(category.getDetail(), DETAIL); + } + + @Test + public void CategoryConstructorWithParamsTest() { + Category category = new Category(ID, DETAIL); + Assert.assertEquals(category.getId(), ID); + Assert.assertEquals(category.getDetail(), DETAIL); + } +} diff --git a/src/test/java/com/santander/interview/domain/ExpenseTest.java b/src/test/java/com/santander/interview/domain/ExpenseTest.java new file mode 100644 index 00000000..5837f4fc --- /dev/null +++ b/src/test/java/com/santander/interview/domain/ExpenseTest.java @@ -0,0 +1,43 @@ +package com.santander.interview.domain; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; + +import java.util.Date; + +@RunWith(MockitoJUnitRunner.class) +public class ExpenseTest { + private static final String ID = "123"; + private static final String DESCRICAO = "descricao"; + private static final double VALOR = 124.2; + private static final long CODIGO_USUARIO = 142; + private static final Date DATA = new Date(); + private static final String CATEGORY_ID = "32"; + private static final String CATEGORY_DETAIL = "teste"; + private static final Category CATEGORY = new Category(CATEGORY_ID, CATEGORY_DETAIL); + + @Test + public void ExpenseTest() { + String expectedToString = String.format( + "Expense[id=%s, descricao=%s, valor=%f, codigoUsuario=%d, data=%s]", + ID, DESCRICAO, VALOR, CODIGO_USUARIO, DATA + ); + Expense expense = new Expense(); + expense.setId(ID); + expense.setCategory(CATEGORY); + expense.setDescricao(DESCRICAO); + expense.setValor(VALOR); + expense.setCodigoUsuario(CODIGO_USUARIO); + expense.setData(DATA); + + Assert.assertEquals(expense.getId(), ID); + Assert.assertEquals(expense.getCategory(), CATEGORY); + Assert.assertEquals(expense.getCodigoUsuario(), CODIGO_USUARIO); + Assert.assertEquals(expense.getData(), DATA); + Assert.assertEquals(expense.getDescricao(), DESCRICAO); + Assert.assertTrue(expense.getValor() == VALOR); + Assert.assertEquals(expense.toString(), expectedToString); + } +} diff --git a/src/test/java/com/santander/interview/domain/ResponseTest.java b/src/test/java/com/santander/interview/domain/ResponseTest.java new file mode 100644 index 00000000..c2275e1a --- /dev/null +++ b/src/test/java/com/santander/interview/domain/ResponseTest.java @@ -0,0 +1,33 @@ +package com.santander.interview.domain; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class ResponseTest { + private static final long STATUS_CODE = 123; + private static final String MESSAGE = "Teste"; + private static final Object DATA = new Object(); + + @Test + public void ResponseTest() { + Response response = new Response(); + response.setData(DATA); + response.setMessage(MESSAGE); + response.setStatusCode(STATUS_CODE); + + Assert.assertEquals(response.getData(), DATA); + Assert.assertEquals(response.getMessage(), MESSAGE); + Assert.assertEquals(response.getStatusCode(), STATUS_CODE); + } + + @Test + public void ResponseConstructorWithParamsTest() { + Response response = new Response(STATUS_CODE, MESSAGE, DATA); + Assert.assertEquals(response.getStatusCode(), STATUS_CODE); + Assert.assertEquals(response.getMessage(), MESSAGE); + Assert.assertEquals(response.getData(), DATA); + } +} diff --git a/src/test/java/com/santander/interview/service/CategoryServiceTest.java b/src/test/java/com/santander/interview/service/CategoryServiceTest.java new file mode 100644 index 00000000..45c875de --- /dev/null +++ b/src/test/java/com/santander/interview/service/CategoryServiceTest.java @@ -0,0 +1,57 @@ +package com.santander.interview.service; + +import com.santander.interview.domain.Category; +import com.santander.interview.repository.CategoryRepository; +import com.santander.interview.service.impl.CategoryServiceImpl; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.mockito.junit.MockitoJUnitRunner; + +import java.util.ArrayList; +import java.util.List; + +@RunWith(MockitoJUnitRunner.class) +public class CategoryServiceTest { + @InjectMocks + CategoryServiceImpl categoryService = new CategoryServiceImpl(); + + @Mock + CategoryRepository categoryRepository; + + Category category; + List categoriesResult; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + this.category = new Category("123", "Detalhe"); + this.categoriesResult = new ArrayList<>(); + this.categoriesResult.add(this.category); + } + + @Test + public void saveCategoryTest() { + boolean isOk = true; + try { + this.categoryService.saveCategory(this.category); + } catch (Exception e) { + isOk = false; + } + Assert.assertTrue(isOk); + } + + @Test + public void searchCategoryByDetailPrefixTest() { + Mockito.when(categoryRepository.findByDetailLike(this.category.getDetail())).thenReturn(this.categoriesResult); + + String prefix = this.category.getDetail(); + List result = this.categoryService.searchCategoryByDetailPrefix(prefix); + Assert.assertEquals(result, categoriesResult); + } +} diff --git a/src/test/java/com/santander/interview/service/ExpenseServiceTest.java b/src/test/java/com/santander/interview/service/ExpenseServiceTest.java new file mode 100644 index 00000000..f9a59c1e --- /dev/null +++ b/src/test/java/com/santander/interview/service/ExpenseServiceTest.java @@ -0,0 +1,117 @@ +package com.santander.interview.service; + +import com.santander.interview.domain.Category; +import com.santander.interview.domain.Expense; +import com.santander.interview.repository.CategoryRepository; +import com.santander.interview.repository.ExpenseRepository; +import com.santander.interview.service.impl.ExpenseServiceImpl; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; + +import java.text.ParseException; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.UUID; + +public class ExpenseServiceTest { + private static final long CODIGO_USUARIO = 1232; + private static final Date DATA = new Date(); + private static final String DATA_STRING = "01102019"; + private static final double VALOR = 124.2; + private static final String DESCRICAO = "Teste"; + private static final String DETAIL = "Detalhes"; + + @InjectMocks + ExpenseServiceImpl expenseService = new ExpenseServiceImpl(); + + @Mock + ExpenseRepository expenseRepository; + + @Mock + CategoryRepository categoryRepository; + + @Before + public void init() { + MockitoAnnotations.initMocks(this); + } + + @Test + public void addNewExpenseWithCategoryNullTest() { + List expenses = new ArrayList<>(); + Expense expense = new Expense(); + expense.setCodigoUsuario(CODIGO_USUARIO); + expense.setData(DATA); + expense.setValor(VALOR); + expense.setDescricao(DESCRICAO); + expenses.add(expense); + + Mockito.when(this.expenseRepository.findByCodigoUsuario(CODIGO_USUARIO)) + .thenReturn(expenses); + + this.expenseService.addNewExpense(expense); + List result = this.expenseService.findExpensesByCodigoUsuario(CODIGO_USUARIO); + Assert.assertEquals(result, expenses); + } + + @Test + public void addNewExpenseWithCategoryTest() { + boolean isOk = true; + Expense expense = new Expense(); + Category category = new Category(); + List categories = new ArrayList<>(); + String uuid = UUID.randomUUID().toString(); + + expense.setCodigoUsuario(CODIGO_USUARIO); + expense.setData(DATA); + expense.setValor(VALOR); + expense.setDescricao(DESCRICAO); + category.setDetail(DETAIL); + expense.setCategory(category); + categories.add(new Category(uuid, DETAIL)); + + Mockito.when(categoryRepository.findByDetail(category.getDetail())) + .thenReturn(categories); + try { + this.expenseService.addNewExpense(expense); + } catch (Exception e) { + isOk = false; + } + + Assert.assertTrue(isOk); + } + + @Test + public void findExpensesByCodigoUsuarioAndDataTest() throws ParseException { + List expenses = new ArrayList<>(); + Expense expense = new Expense(); + expense.setCodigoUsuario(CODIGO_USUARIO); + expense.setData(DATA); + expense.setValor(VALOR); + expense.setDescricao(DESCRICAO); + expenses.add(expense); + + Assert.assertNotNull(this.expenseService.findExpensesByCodigoUsuarioAndData(CODIGO_USUARIO, DATA_STRING)); + } + + @Test + public void updateExpenseTest() { + boolean isOk = true; + String uuid = UUID.randomUUID().toString(); + Expense expense = new Expense(); + try { + this.expenseService.updateExpense(uuid, expense); + } catch (Exception e) { + isOk = false; + } + + Assert.assertTrue(isOk); + } + + +} diff --git a/src/test/java/com/santander/interview/webservice/CategoryApiTest.java b/src/test/java/com/santander/interview/webservice/CategoryApiTest.java new file mode 100644 index 00000000..912b47c1 --- /dev/null +++ b/src/test/java/com/santander/interview/webservice/CategoryApiTest.java @@ -0,0 +1,50 @@ +package com.santander.interview.webservice; + +import com.santander.interview.domain.Category; +import com.santander.interview.domain.Response; +import com.santander.interview.service.CategoryService; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; + + +public class CategoryApiTest { + private static final String CATEGORY_ID = "12asd"; + private static final String CATEGORY_DETAIL = "Detail"; + private Category category; + + @InjectMocks + CategoryApi categoryApi = new CategoryApi(); + + @Mock + CategoryService categoryService; + + @Before + public void init() { + category = new Category(CATEGORY_ID, CATEGORY_DETAIL); + MockitoAnnotations.initMocks(this); + } + + @Test + public void suggestionCategoryTest() { + String detailPrefix = "teste"; + Mockito.when(categoryService.searchCategoryByDetailPrefix(detailPrefix)).thenReturn(null); + ResponseEntity response = categoryApi.suggestionCategory(detailPrefix); + Assert.assertEquals(response.getStatusCode(), HttpStatus.OK); + Assert.assertNull(response.getBody().getData()); + } + + @Test + public void addCategoryTest() { + ResponseEntity response = categoryApi.addCategory(category); + Assert.assertEquals(response.getStatusCode(), HttpStatus.OK); + Assert.assertNull(response.getBody().getData()); + } + +} diff --git a/src/test/java/com/santander/interview/webservice/ExpenseApiTest.java b/src/test/java/com/santander/interview/webservice/ExpenseApiTest.java new file mode 100644 index 00000000..5fa0235b --- /dev/null +++ b/src/test/java/com/santander/interview/webservice/ExpenseApiTest.java @@ -0,0 +1,105 @@ +package com.santander.interview.webservice; + +import com.santander.interview.domain.Expense; +import com.santander.interview.domain.Response; +import com.santander.interview.service.ExpenseService; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; + +import java.text.ParseException; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.UUID; + +public class ExpenseApiTest { + private static final String DESCRICAO = "descricao"; + private static final Double VALOR = 198.23; + private static final long CODIGO_USUARIO = 129; + private static final Date DATA = new Date(); + + @InjectMocks + ExpenseApi expenseApi = new ExpenseApi(); + + @Mock + ExpenseService expenseService; + + Expense expense; + + String uuid; + + @Before + public void init() { + MockitoAnnotations.initMocks(this); + this.uuid = UUID.randomUUID().toString(); + this.expense = new Expense(); + this.expense.setId(uuid); + this.expense.setDescricao(DESCRICAO); + this.expense.setValor(VALOR); + this.expense.setCodigoUsuario(CODIGO_USUARIO); + this.expense.setData(DATA); + this.expense.setCategory(null); + } + + @Test + public void addExpenseTest() { + ResponseEntity response = this.expenseApi.addExpense(this.expense); + Assert.assertEquals(response.getStatusCode(), HttpStatus.OK); + Assert.assertEquals(response.getBody().getStatusCode(), HttpStatus.OK.value()); + Assert.assertNull(response.getBody().getData()); + } + + @Test + public void getExpenseByUserCodeTest() { + long userCode = CODIGO_USUARIO; + List list = new ArrayList<>(); + list.add(this.expense); + + Mockito.when(expenseService.findExpensesByCodigoUsuario(userCode)).thenReturn(list); + + ResponseEntity response = this.expenseApi.getExpenseByUserCode(userCode); + Assert.assertEquals(response.getStatusCode(), HttpStatus.OK); + Assert.assertEquals(response.getBody().getStatusCode(), HttpStatus.OK.value()); + Assert.assertEquals(response.getBody().getData(), list); + } + + @Test + public void getExpenseByUserCodeAndDateTest() throws ParseException { + long userCode = CODIGO_USUARIO; + String date = DATA.toString(); + List list = new ArrayList<>(); + list.add(this.expense); + + Mockito.when(this.expenseService.findExpensesByCodigoUsuarioAndData(userCode, date)).thenReturn(list); + ResponseEntity response = this.expenseApi.getExpenseByUserCodeAndDate(userCode, date); + Assert.assertEquals(response.getStatusCode(), HttpStatus.OK); + Assert.assertEquals(response.getBody().getStatusCode(), HttpStatus.OK.value()); + Assert.assertEquals(response.getBody().getData(), list); + } + + @Test + public void getExpenseByUserCodeAndDate_WithDataIncorrectTest() throws ParseException { + long userCode = CODIGO_USUARIO; + String date = "121251"; + Mockito.when(this.expenseService.findExpensesByCodigoUsuarioAndData(userCode, date)) + .thenThrow(ParseException.class); + ResponseEntity response = this.expenseApi.getExpenseByUserCodeAndDate(userCode, date); + Assert.assertEquals(response.getStatusCode(), HttpStatus.BAD_REQUEST); + Assert.assertEquals(response.getBody().getStatusCode(), HttpStatus.BAD_REQUEST.value()); + } + + @Test + public void updateExpenseTest() { + String id = this.uuid; + ResponseEntity response = this.expenseApi.updateExpense(id, expense); + Assert.assertEquals(response.getStatusCode(), HttpStatus.OK); + Assert.assertEquals(response.getBody().getStatusCode(), HttpStatus.OK.value()); + } +} From 6a581d854e25992f674a355cdeeec86a6f081cec Mon Sep 17 00:00:00 2001 From: Thiago Sato Date: Thu, 5 Sep 2019 01:53:56 -0300 Subject: [PATCH 11/30] Adicionando mensagens de retorno em um enum --- .../interview/enums/ResponseMessageEnum.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 src/main/java/com/santander/interview/enums/ResponseMessageEnum.java diff --git a/src/main/java/com/santander/interview/enums/ResponseMessageEnum.java b/src/main/java/com/santander/interview/enums/ResponseMessageEnum.java new file mode 100644 index 00000000..bda41bb4 --- /dev/null +++ b/src/main/java/com/santander/interview/enums/ResponseMessageEnum.java @@ -0,0 +1,19 @@ +package com.santander.interview.enums; + +public enum ResponseMessageEnum { + ADD_EXPENSE_SUCCESS("Gasto criado com sucesso"), + SEARCH_EXPENSE_BY_USER_CODE_SUCCESS("Busca pelo código do usuário realizada com sucesso"), + SEARCH_EXPENSE_BY_USER_CODE_AND_DATE_SUCCESS("Busca por código do usuário e pela data realizada com sucesso"), + UPDATE_EXPENSE_SUCCESS("Gasto atualizado com sucesso"), + ERROR_BADLY_FORMATTED_DATE("Data mal formatada"), + ADD_CATEGORY_SUCCESS("Categoria criada com sucesso"), + SUGGESTION_CATEGORY_SUCCESS("Busca por categorias realizada com sucesso"); + + private String message; + + private ResponseMessageEnum(String message) { + this.message = message; + } + + public String getMessage() { return this.message; } +} From 7861c33764f214ba259a8f5e379a6d0c4d87d80f Mon Sep 17 00:00:00 2001 From: Thiago Sato Date: Thu, 5 Sep 2019 01:54:39 -0300 Subject: [PATCH 12/30] =?UTF-8?q?Adicionando=20depend=C3=AAncias=20de=20te?= =?UTF-8?q?stes=20no=20pom?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/pom.xml b/pom.xml index bb014c66..dab30ee8 100644 --- a/pom.xml +++ b/pom.xml @@ -24,6 +24,17 @@ spring-data-mongodb 2.1.9.RELEASE + + + org.springframework.boot + spring-boot-starter-test + test + + + junit + junit + test + From 829778d49f84580117511cc9a418f29161521e98 Mon Sep 17 00:00:00 2001 From: Thiago Sato Date: Thu, 5 Sep 2019 01:55:30 -0300 Subject: [PATCH 13/30] Enum para o envio das mensagens de retorno --- .../interview/service/ExpenseService.java | 4 ++-- .../service/impl/ExpenseServiceImpl.java | 4 ++-- .../interview/webservice/CategoryApi.java | 6 ++++-- .../interview/webservice/ExpenseApi.java | 16 +++++++++------- 4 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/main/java/com/santander/interview/service/ExpenseService.java b/src/main/java/com/santander/interview/service/ExpenseService.java index e945abe0..c227e780 100644 --- a/src/main/java/com/santander/interview/service/ExpenseService.java +++ b/src/main/java/com/santander/interview/service/ExpenseService.java @@ -7,7 +7,7 @@ public interface ExpenseService { public void addNewExpense(Expense expense); - public List findExpenseByCodigoUsuario(long codigoUsuario); - public List findExpenseByCodigoUsuarioAndData(long codigoUsuario, String data) throws ParseException; + public List findExpensesByCodigoUsuario(long codigoUsuario); + public List findExpensesByCodigoUsuarioAndData(long codigoUsuario, String data) throws ParseException; public void updateExpense(String id, Expense expense); } diff --git a/src/main/java/com/santander/interview/service/impl/ExpenseServiceImpl.java b/src/main/java/com/santander/interview/service/impl/ExpenseServiceImpl.java index 44181edd..916a4b0a 100644 --- a/src/main/java/com/santander/interview/service/impl/ExpenseServiceImpl.java +++ b/src/main/java/com/santander/interview/service/impl/ExpenseServiceImpl.java @@ -42,7 +42,7 @@ public void addNewExpense(Expense expense) { } @Override - public List findExpenseByCodigoUsuario(long codigoUsuario) { + public List findExpensesByCodigoUsuario(long codigoUsuario) { List expenses = expenseRepository.findByCodigoUsuario(codigoUsuario); Collections.sort(expenses, new Comparator() { @Override @@ -54,7 +54,7 @@ public int compare(Expense expense1, Expense expense2) { } @Override - public List findExpenseByCodigoUsuarioAndData(long codigoUsuario, String data) throws ParseException { + public List findExpensesByCodigoUsuarioAndData(long codigoUsuario, String data) throws ParseException { long oneDayInMilliseconds = 1000 * 60 * 60 * 24; Date startDate = new SimpleDateFormat("ddMMyyyy").parse(data); Date endDate = new Date(startDate.getTime() + oneDayInMilliseconds); diff --git a/src/main/java/com/santander/interview/webservice/CategoryApi.java b/src/main/java/com/santander/interview/webservice/CategoryApi.java index dd1ffba9..46f52861 100644 --- a/src/main/java/com/santander/interview/webservice/CategoryApi.java +++ b/src/main/java/com/santander/interview/webservice/CategoryApi.java @@ -1,5 +1,7 @@ package com.santander.interview.webservice; +import static com.santander.interview.enums.ResponseMessageEnum.*; + import com.santander.interview.domain.Category; import com.santander.interview.domain.Response; import com.santander.interview.service.CategoryService; @@ -21,7 +23,7 @@ public ResponseEntity addCategory(@RequestBody Category category) { categoryService.saveCategory(category); return new ResponseEntity<>( - new Response(HttpStatus.OK.value(), "Categoria criada.", null), + new Response(HttpStatus.OK.value(), ADD_CATEGORY_SUCCESS.getMessage(), null), HttpStatus.OK ); } @@ -31,7 +33,7 @@ public ResponseEntity suggestionCategory(@PathVariable String detailPr List categories = this.categoryService.searchCategoryByDetailPrefix(detailPrefix); return new ResponseEntity<>( - new Response(HttpStatus.OK.value(), "Resultado da busca de categorias", categories), + new Response(HttpStatus.OK.value(), SUGGESTION_CATEGORY_SUCCESS.getMessage(), categories), HttpStatus.OK ); } diff --git a/src/main/java/com/santander/interview/webservice/ExpenseApi.java b/src/main/java/com/santander/interview/webservice/ExpenseApi.java index 025c53df..4893a77e 100644 --- a/src/main/java/com/santander/interview/webservice/ExpenseApi.java +++ b/src/main/java/com/santander/interview/webservice/ExpenseApi.java @@ -1,5 +1,7 @@ package com.santander.interview.webservice; +import static com.santander.interview.enums.ResponseMessageEnum.*; + import com.santander.interview.domain.Expense; import com.santander.interview.domain.Response; import com.santander.interview.service.ExpenseService; @@ -21,16 +23,16 @@ public class ExpenseApi { public ResponseEntity addExpense(@RequestBody Expense expense) { this.expenseService.addNewExpense(expense); return new ResponseEntity<>( - new Response(HttpStatus.OK.value(), "Criado com sucesso", null), + new Response(HttpStatus.OK.value(), ADD_EXPENSE_SUCCESS.getMessage(), null), HttpStatus.OK ); } @GetMapping("/expense/userCode/{userCode}") public ResponseEntity getExpenseByUserCode(@PathVariable long userCode) { - List expensesByUserCode = this.expenseService.findExpenseByCodigoUsuario(userCode); + List expensesByUserCode = this.expenseService.findExpensesByCodigoUsuario(userCode); return new ResponseEntity<>( - new Response(HttpStatus.OK.value(), "Busca por código do usuário realizada com sucesso", + new Response(HttpStatus.OK.value(), SEARCH_EXPENSE_BY_USER_CODE_SUCCESS.getMessage(), expensesByUserCode), HttpStatus.OK ); @@ -39,15 +41,15 @@ public ResponseEntity getExpenseByUserCode(@PathVariable long userCode @GetMapping("/expense/userCode/{userCode}/date/{date}") public ResponseEntity getExpenseByUserCodeAndDate(@PathVariable long userCode, @PathVariable String date) { try { - List expensesByUserCodeAndDate = this.expenseService.findExpenseByCodigoUsuarioAndData(userCode, date); + List expensesByUserCodeAndDate = this.expenseService.findExpensesByCodigoUsuarioAndData(userCode, date); return new ResponseEntity<>( - new Response(HttpStatus.OK.value(), "Busca por código do usuário e pela data realizada com sucesso", + new Response(HttpStatus.OK.value(), SEARCH_EXPENSE_BY_USER_CODE_AND_DATE_SUCCESS.getMessage(), expensesByUserCodeAndDate), HttpStatus.OK ); } catch (ParseException pe){ return new ResponseEntity<>( - new Response(HttpStatus.BAD_REQUEST.value(), "Data mal formatada", null), + new Response(HttpStatus.BAD_REQUEST.value(), ERROR_BADLY_FORMATTED_DATE.getMessage(), null), HttpStatus.BAD_REQUEST ); } @@ -58,7 +60,7 @@ public ResponseEntity updateExpense(@PathVariable String id, @RequestB this.expenseService.updateExpense(id, expense); return new ResponseEntity<>( - new Response(HttpStatus.OK.value(), "Dado atualizado", null), + new Response(HttpStatus.OK.value(), UPDATE_EXPENSE_SUCCESS.getMessage(), null), HttpStatus.OK ); } From bec7264bac2a1d5c1579f11c77ac7773b9862f5d Mon Sep 17 00:00:00 2001 From: Thiago Sato Date: Sat, 7 Sep 2019 12:32:12 -0300 Subject: [PATCH 14/30] =?UTF-8?q?Cria=C3=A7=C3=A3o=20da=20classe=20utils?= =?UTF-8?q?=20para=20convers=C3=A3o=20de=20uma=20String=20para=20Int?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../santander/interview/config/ThreadConfig.java | 4 ++++ .../CategoryController.java} | 0 .../ExpenseController.java} | 0 .../interview/utils/ExpenseManagementUtils.java | 13 +++++++++++++ .../CategoryControllerTest.java} | 0 .../ExpenseControllerTest.java} | 0 6 files changed, 17 insertions(+) create mode 100644 src/main/java/com/santander/interview/config/ThreadConfig.java rename src/main/java/com/santander/interview/{webservice/CategoryApi.java => controller/CategoryController.java} (100%) rename src/main/java/com/santander/interview/{webservice/ExpenseApi.java => controller/ExpenseController.java} (100%) create mode 100644 src/main/java/com/santander/interview/utils/ExpenseManagementUtils.java rename src/test/java/com/santander/interview/{webservice/CategoryApiTest.java => controller/CategoryControllerTest.java} (100%) rename src/test/java/com/santander/interview/{webservice/ExpenseApiTest.java => controller/ExpenseControllerTest.java} (100%) diff --git a/src/main/java/com/santander/interview/config/ThreadConfig.java b/src/main/java/com/santander/interview/config/ThreadConfig.java new file mode 100644 index 00000000..3491a767 --- /dev/null +++ b/src/main/java/com/santander/interview/config/ThreadConfig.java @@ -0,0 +1,4 @@ +package com.santander.interview.config; + +public class ThredConfig { +} diff --git a/src/main/java/com/santander/interview/webservice/CategoryApi.java b/src/main/java/com/santander/interview/controller/CategoryController.java similarity index 100% rename from src/main/java/com/santander/interview/webservice/CategoryApi.java rename to src/main/java/com/santander/interview/controller/CategoryController.java diff --git a/src/main/java/com/santander/interview/webservice/ExpenseApi.java b/src/main/java/com/santander/interview/controller/ExpenseController.java similarity index 100% rename from src/main/java/com/santander/interview/webservice/ExpenseApi.java rename to src/main/java/com/santander/interview/controller/ExpenseController.java diff --git a/src/main/java/com/santander/interview/utils/ExpenseManagementUtils.java b/src/main/java/com/santander/interview/utils/ExpenseManagementUtils.java new file mode 100644 index 00000000..4f650df3 --- /dev/null +++ b/src/main/java/com/santander/interview/utils/ExpenseManagementUtils.java @@ -0,0 +1,13 @@ +package com.santander.interview.utils; + +public class ExpenseManagementUtils { + + public static int expenseManagementUtils(int defaultValue, String value) { + try { + return Integer.valueOf(value); + } catch (Exception e) { + return defaultValue; + } + } + +} diff --git a/src/test/java/com/santander/interview/webservice/CategoryApiTest.java b/src/test/java/com/santander/interview/controller/CategoryControllerTest.java similarity index 100% rename from src/test/java/com/santander/interview/webservice/CategoryApiTest.java rename to src/test/java/com/santander/interview/controller/CategoryControllerTest.java diff --git a/src/test/java/com/santander/interview/webservice/ExpenseApiTest.java b/src/test/java/com/santander/interview/controller/ExpenseControllerTest.java similarity index 100% rename from src/test/java/com/santander/interview/webservice/ExpenseApiTest.java rename to src/test/java/com/santander/interview/controller/ExpenseControllerTest.java From 6648577047aa2ce27ea9a6a7a933898765810e5f Mon Sep 17 00:00:00 2001 From: Thiago Sato Date: Sat, 7 Sep 2019 12:38:05 -0300 Subject: [PATCH 15/30] =?UTF-8?q?Adicionando=20configura=C3=A7=C3=B5es=20p?= =?UTF-8?q?ara=20execu=C3=A7=C3=A3o=20com=20threads?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../interview/ExpenseManagement.java | 2 ++ .../interview/config/ThreadConfig.java | 34 ++++++++++++++++++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/santander/interview/ExpenseManagement.java b/src/main/java/com/santander/interview/ExpenseManagement.java index 38f1c95d..d28ea1ec 100644 --- a/src/main/java/com/santander/interview/ExpenseManagement.java +++ b/src/main/java/com/santander/interview/ExpenseManagement.java @@ -3,7 +3,9 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.ComponentScan; +import org.springframework.scheduling.annotation.EnableAsync; +@EnableAsync @SpringBootApplication @ComponentScan(basePackages = {"com.santander.interview"}) public class ExpenseManagement { diff --git a/src/main/java/com/santander/interview/config/ThreadConfig.java b/src/main/java/com/santander/interview/config/ThreadConfig.java index 3491a767..4a292d5d 100644 --- a/src/main/java/com/santander/interview/config/ThreadConfig.java +++ b/src/main/java/com/santander/interview/config/ThreadConfig.java @@ -1,4 +1,36 @@ package com.santander.interview.config; -public class ThredConfig { +import com.santander.interview.utils.ExpenseManagementUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +import java.util.concurrent.Executor; + +@Configuration +public class ThreadConfig { + public static final String THREAD_NAME_PREFIX = "interview"; + + @Value("#{environment['ASYNC_CORE_POOL_SIZE']}") + private String ASYNC_CORE_POOL_SIZE; + + @Value("#{environment['ASYNC_MAX_POOL_SIZE']}") + private String ASYNC_MAX_POOL_SIZE; + + @Bean + public Executor asyncExecutor() { + ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor(); + threadPoolTaskExecutor.setCorePoolSize( + ExpenseManagementUtils.expenseManagementUtils(10, ASYNC_CORE_POOL_SIZE) + ); + threadPoolTaskExecutor.setMaxPoolSize( + ExpenseManagementUtils.expenseManagementUtils(100, ASYNC_MAX_POOL_SIZE) + ); + threadPoolTaskExecutor.setQueueCapacity(Integer.MAX_VALUE); + threadPoolTaskExecutor.setThreadNamePrefix(THREAD_NAME_PREFIX); + threadPoolTaskExecutor.initialize(); + + return threadPoolTaskExecutor; + } } From 72261b4d3f728d50adf0d6127210ea049a420b14 Mon Sep 17 00:00:00 2001 From: Thiago Sato Date: Sat, 7 Sep 2019 12:40:17 -0300 Subject: [PATCH 16/30] Renomeando o nome das classes para controller --- .../interview/controller/CategoryController.java | 4 ++-- .../interview/controller/ExpenseController.java | 4 ++-- .../interview/repository/CategoryRepository.java | 2 ++ .../service/impl/ExpenseServiceImpl.java | 3 --- .../controller/CategoryControllerTest.java | 10 +++++----- .../controller/ExpenseControllerTest.java | 16 ++++++++-------- 6 files changed, 19 insertions(+), 20 deletions(-) diff --git a/src/main/java/com/santander/interview/controller/CategoryController.java b/src/main/java/com/santander/interview/controller/CategoryController.java index 46f52861..3a965d9c 100644 --- a/src/main/java/com/santander/interview/controller/CategoryController.java +++ b/src/main/java/com/santander/interview/controller/CategoryController.java @@ -1,4 +1,4 @@ -package com.santander.interview.webservice; +package com.santander.interview.controller; import static com.santander.interview.enums.ResponseMessageEnum.*; @@ -13,7 +13,7 @@ import java.util.List; @RestController -public class CategoryApi { +public class CategoryController { @Autowired CategoryService categoryService; diff --git a/src/main/java/com/santander/interview/controller/ExpenseController.java b/src/main/java/com/santander/interview/controller/ExpenseController.java index 4893a77e..1f625841 100644 --- a/src/main/java/com/santander/interview/controller/ExpenseController.java +++ b/src/main/java/com/santander/interview/controller/ExpenseController.java @@ -1,4 +1,4 @@ -package com.santander.interview.webservice; +package com.santander.interview.controller; import static com.santander.interview.enums.ResponseMessageEnum.*; @@ -14,7 +14,7 @@ import java.util.List; @RestController -public class ExpenseApi { +public class ExpenseController { @Autowired ExpenseService expenseService; diff --git a/src/main/java/com/santander/interview/repository/CategoryRepository.java b/src/main/java/com/santander/interview/repository/CategoryRepository.java index 2f032f1a..be18bdc6 100644 --- a/src/main/java/com/santander/interview/repository/CategoryRepository.java +++ b/src/main/java/com/santander/interview/repository/CategoryRepository.java @@ -2,9 +2,11 @@ import com.santander.interview.domain.Category; import org.springframework.data.mongodb.repository.MongoRepository; +import org.springframework.stereotype.Repository; import java.util.List; +@Repository public interface CategoryRepository extends MongoRepository { public List findByDetail(String detail); public List findByDetailLike(String detail); diff --git a/src/main/java/com/santander/interview/service/impl/ExpenseServiceImpl.java b/src/main/java/com/santander/interview/service/impl/ExpenseServiceImpl.java index 916a4b0a..39c0564e 100644 --- a/src/main/java/com/santander/interview/service/impl/ExpenseServiceImpl.java +++ b/src/main/java/com/santander/interview/service/impl/ExpenseServiceImpl.java @@ -10,9 +10,6 @@ import java.text.ParseException; import java.text.SimpleDateFormat; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.ZoneId; import java.util.*; @Service diff --git a/src/test/java/com/santander/interview/controller/CategoryControllerTest.java b/src/test/java/com/santander/interview/controller/CategoryControllerTest.java index 912b47c1..830751f6 100644 --- a/src/test/java/com/santander/interview/controller/CategoryControllerTest.java +++ b/src/test/java/com/santander/interview/controller/CategoryControllerTest.java @@ -1,4 +1,4 @@ -package com.santander.interview.webservice; +package com.santander.interview.controller; import com.santander.interview.domain.Category; import com.santander.interview.domain.Response; @@ -14,13 +14,13 @@ import org.springframework.http.ResponseEntity; -public class CategoryApiTest { +public class CategoryControllerTest { private static final String CATEGORY_ID = "12asd"; private static final String CATEGORY_DETAIL = "Detail"; private Category category; @InjectMocks - CategoryApi categoryApi = new CategoryApi(); + CategoryController categoryController = new CategoryController(); @Mock CategoryService categoryService; @@ -35,14 +35,14 @@ public void init() { public void suggestionCategoryTest() { String detailPrefix = "teste"; Mockito.when(categoryService.searchCategoryByDetailPrefix(detailPrefix)).thenReturn(null); - ResponseEntity response = categoryApi.suggestionCategory(detailPrefix); + ResponseEntity response = categoryController.suggestionCategory(detailPrefix); Assert.assertEquals(response.getStatusCode(), HttpStatus.OK); Assert.assertNull(response.getBody().getData()); } @Test public void addCategoryTest() { - ResponseEntity response = categoryApi.addCategory(category); + ResponseEntity response = categoryController.addCategory(category); Assert.assertEquals(response.getStatusCode(), HttpStatus.OK); Assert.assertNull(response.getBody().getData()); } diff --git a/src/test/java/com/santander/interview/controller/ExpenseControllerTest.java b/src/test/java/com/santander/interview/controller/ExpenseControllerTest.java index 5fa0235b..c5833258 100644 --- a/src/test/java/com/santander/interview/controller/ExpenseControllerTest.java +++ b/src/test/java/com/santander/interview/controller/ExpenseControllerTest.java @@ -1,4 +1,4 @@ -package com.santander.interview.webservice; +package com.santander.interview.controller; import com.santander.interview.domain.Expense; import com.santander.interview.domain.Response; @@ -19,14 +19,14 @@ import java.util.List; import java.util.UUID; -public class ExpenseApiTest { +public class ExpenseControllerTest { private static final String DESCRICAO = "descricao"; private static final Double VALOR = 198.23; private static final long CODIGO_USUARIO = 129; private static final Date DATA = new Date(); @InjectMocks - ExpenseApi expenseApi = new ExpenseApi(); + ExpenseController expenseController = new ExpenseController(); @Mock ExpenseService expenseService; @@ -50,7 +50,7 @@ public void init() { @Test public void addExpenseTest() { - ResponseEntity response = this.expenseApi.addExpense(this.expense); + ResponseEntity response = this.expenseController.addExpense(this.expense); Assert.assertEquals(response.getStatusCode(), HttpStatus.OK); Assert.assertEquals(response.getBody().getStatusCode(), HttpStatus.OK.value()); Assert.assertNull(response.getBody().getData()); @@ -64,7 +64,7 @@ public void getExpenseByUserCodeTest() { Mockito.when(expenseService.findExpensesByCodigoUsuario(userCode)).thenReturn(list); - ResponseEntity response = this.expenseApi.getExpenseByUserCode(userCode); + ResponseEntity response = this.expenseController.getExpenseByUserCode(userCode); Assert.assertEquals(response.getStatusCode(), HttpStatus.OK); Assert.assertEquals(response.getBody().getStatusCode(), HttpStatus.OK.value()); Assert.assertEquals(response.getBody().getData(), list); @@ -78,7 +78,7 @@ public void getExpenseByUserCodeAndDateTest() throws ParseException { list.add(this.expense); Mockito.when(this.expenseService.findExpensesByCodigoUsuarioAndData(userCode, date)).thenReturn(list); - ResponseEntity response = this.expenseApi.getExpenseByUserCodeAndDate(userCode, date); + ResponseEntity response = this.expenseController.getExpenseByUserCodeAndDate(userCode, date); Assert.assertEquals(response.getStatusCode(), HttpStatus.OK); Assert.assertEquals(response.getBody().getStatusCode(), HttpStatus.OK.value()); Assert.assertEquals(response.getBody().getData(), list); @@ -90,7 +90,7 @@ public void getExpenseByUserCodeAndDate_WithDataIncorrectTest() throws ParseExce String date = "121251"; Mockito.when(this.expenseService.findExpensesByCodigoUsuarioAndData(userCode, date)) .thenThrow(ParseException.class); - ResponseEntity response = this.expenseApi.getExpenseByUserCodeAndDate(userCode, date); + ResponseEntity response = this.expenseController.getExpenseByUserCodeAndDate(userCode, date); Assert.assertEquals(response.getStatusCode(), HttpStatus.BAD_REQUEST); Assert.assertEquals(response.getBody().getStatusCode(), HttpStatus.BAD_REQUEST.value()); } @@ -98,7 +98,7 @@ public void getExpenseByUserCodeAndDate_WithDataIncorrectTest() throws ParseExce @Test public void updateExpenseTest() { String id = this.uuid; - ResponseEntity response = this.expenseApi.updateExpense(id, expense); + ResponseEntity response = this.expenseController.updateExpense(id, expense); Assert.assertEquals(response.getStatusCode(), HttpStatus.OK); Assert.assertEquals(response.getBody().getStatusCode(), HttpStatus.OK.value()); } From 3fe38360d381fd3096f8b418e6838fbe9fe10780 Mon Sep 17 00:00:00 2001 From: Thiago Sato Date: Sun, 8 Sep 2019 22:21:00 -0300 Subject: [PATCH 17/30] SoapUI para testes --- InterviewSantanderJava-soapui-project.xml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 InterviewSantanderJava-soapui-project.xml diff --git a/InterviewSantanderJava-soapui-project.xml b/InterviewSantanderJava-soapui-project.xml new file mode 100644 index 00000000..051cc9d2 --- /dev/null +++ b/InterviewSantanderJava-soapui-project.xml @@ -0,0 +1,5 @@ + +http://localhost:8080application/json;charset=UTF-8200exp:Responseapplication/json<xml-fragment/>http://localhost:8080{ "description": "asd1231", "value": 12783.23, "userCode": 12283, "date": "2019-09-02T20:21:42.026Z" }http://localhost/expense-management/expensesadminmyadminBasicBasicGlobal HTTP SettingsuserCodeuserCodeTEMPLATEuserCodeapplication/json;charset=UTF-8401ns:Faultapplication/json;charset=UTF-8200ns:Response<xml-fragment/>http://localhost:8080http://localhost/expense-management/expense/userCode/12283clienttheclientBasicBasicGlobal HTTP SettingsuserCodeuserCodeuserCodeTEMPLATEuserCodedatedateTEMPLATEdateapplication/json;charset=UTF-8400ns:Faultapplication/json;charset=UTF-8200ns:Response<xml-fragment/>http://localhost:8080http://localhost/expense-management/expense/userCode/12283/date/02092019clienttheclientBasicBasicGlobal HTTP Settings + + +userCodedateididTEMPLATEidapplication/jsonapplication/json;charset=UTF-8200afa7:Response0data0data<xml-fragment/>http://localhost:8080{ "description": "2134asd", "value": 12351.23, "userCode": 1227883, "date": "2019-09-01T17:21:42.026Z" }http://localhost/expense-management/expense/6655afa7-7679-4098-9927-6c975f67fbd5clienttheclientBasicBasicGlobal HTTP Settingsidapplication/jsonapplication/json;charset=UTF-8200cat:Response<xml-fragment/>http://localhost:8080{ "detail": "teste" }http://localhost/expense-management/categoriesclienttheclientBasicBasicGlobal HTTP SettingsdetailPrefixdetailPrefixTEMPLATEdetailPrefixapplication/json;charset=UTF-8200tes:Response<xml-fragment/>http://localhost:8080http://localhost/expense-management/category/detail/testeclienttheclientBasicBasicGlobal HTTP SettingsdetailPrefixPARALLELLfalse1000250truetrue-1100000COUNTSimple00.5100true500interviewadminmyadmin \ No newline at end of file From 5aac3ca1ca5c7dba102f3b3d11a48b6f4c453b8a Mon Sep 17 00:00:00 2001 From: Thiago Sato Date: Sun, 8 Sep 2019 22:24:23 -0300 Subject: [PATCH 18/30] =?UTF-8?q?Cria=C3=A7=C3=A3o=20da=20autentica=C3=A7?= =?UTF-8?q?=C3=A3o=20e=20da=20autoriza=C3=A7=C3=A3o?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/santander/interview/SecurityInit.java | 21 ++++++++++ .../interview/config/SecurityConfig.java | 40 +++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 src/main/java/com/santander/interview/SecurityInit.java create mode 100644 src/main/java/com/santander/interview/config/SecurityConfig.java diff --git a/src/main/java/com/santander/interview/SecurityInit.java b/src/main/java/com/santander/interview/SecurityInit.java new file mode 100644 index 00000000..9f467bc3 --- /dev/null +++ b/src/main/java/com/santander/interview/SecurityInit.java @@ -0,0 +1,21 @@ +package com.santander.interview; + +import com.santander.interview.config.SecurityConfig; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer; + +@Configuration +public class SecurityInit extends AbstractSecurityWebApplicationInitializer { + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + + public SecurityInit() { + super(SecurityConfig.class); + } +} diff --git a/src/main/java/com/santander/interview/config/SecurityConfig.java b/src/main/java/com/santander/interview/config/SecurityConfig.java new file mode 100644 index 00000000..7ee4f1e1 --- /dev/null +++ b/src/main/java/com/santander/interview/config/SecurityConfig.java @@ -0,0 +1,40 @@ +package com.santander.interview.config; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpMethod; +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; +import org.springframework.security.crypto.password.PasswordEncoder; + +@Configuration +@EnableWebSecurity +public class SecurityConfig extends WebSecurityConfigurerAdapter { + + @Autowired + PasswordEncoder passwordEncoder; + + @Override + protected void configure(HttpSecurity http) throws Exception { + http.csrf().disable().authorizeRequests() + .antMatchers(HttpMethod.POST, "/expense-management/expenses").hasRole("ADMIN") + .antMatchers("/expense-management/expense/**").hasRole("CLIENT") + .antMatchers("/expense-management/categories").hasRole("CLIENT") + .antMatchers("/expense-management/category/**").hasRole("CLIENT") + .anyRequest().authenticated() + .and().httpBasic(); + } + + @Override + protected void configure(AuthenticationManagerBuilder auth) throws Exception { + auth.inMemoryAuthentication() +// .passwordEncoder(passwordEncoder) + .withUser("admin").password(passwordEncoder.encode("myadmin")).roles("ADMIN") +// .withUser("admin").password("{noop}myadmin").roles("ADMIN") + .and() + .withUser("client").password(passwordEncoder.encode("theclient")).roles("CLIENT"); + + } +} From dfc11a965b28a76dff2f79921575bc25f5fa757a Mon Sep 17 00:00:00 2001 From: Thiago Sato Date: Sun, 8 Sep 2019 22:25:44 -0300 Subject: [PATCH 19/30] =?UTF-8?q?Adicionando=20Swagger=20para=20documenta?= =?UTF-8?q?=C3=A7=C3=A3o=20das=20APIs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 26 +++++++++++++++++ .../interview/config/SwaggerConfig.java | 22 ++++++++++++++ .../controller/CategoryController.java | 15 ++++++++-- .../controller/ExpenseController.java | 29 +++++++++++++++---- 4 files changed, 84 insertions(+), 8 deletions(-) create mode 100644 src/main/java/com/santander/interview/config/SwaggerConfig.java diff --git a/pom.xml b/pom.xml index dab30ee8..1012a353 100644 --- a/pom.xml +++ b/pom.xml @@ -24,6 +24,32 @@ spring-data-mongodb 2.1.9.RELEASE + + org.springframework.boot + spring-boot-starter-security + + + + io.springfox + springfox-swagger2 + 2.9.2 + + + io.swagger + swagger-models + + + + + io.springfox + springfox-swagger-ui + 2.9.2 + + + io.swagger + swagger-models + 1.5.21 + org.springframework.boot diff --git a/src/main/java/com/santander/interview/config/SwaggerConfig.java b/src/main/java/com/santander/interview/config/SwaggerConfig.java new file mode 100644 index 00000000..0588a003 --- /dev/null +++ b/src/main/java/com/santander/interview/config/SwaggerConfig.java @@ -0,0 +1,22 @@ +package com.santander.interview.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spring.web.plugins.Docket; +import springfox.documentation.swagger2.annotations.EnableSwagger2; + +@Configuration +@EnableSwagger2 +public class SwaggerConfig { + @Bean + public Docket api () { + return new Docket(DocumentationType.SWAGGER_2) + .select() + .apis(RequestHandlerSelectors.any()) + .paths(PathSelectors.ant("/expense-management/**")) + .build(); + } +} diff --git a/src/main/java/com/santander/interview/controller/CategoryController.java b/src/main/java/com/santander/interview/controller/CategoryController.java index 3a965d9c..1d7df8a4 100644 --- a/src/main/java/com/santander/interview/controller/CategoryController.java +++ b/src/main/java/com/santander/interview/controller/CategoryController.java @@ -5,6 +5,9 @@ import com.santander.interview.domain.Category; import com.santander.interview.domain.Response; import com.santander.interview.service.CategoryService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -13,13 +16,18 @@ import java.util.List; @RestController +@RequestMapping("/expense-management") +@Api(value = "Categoria") public class CategoryController { @Autowired CategoryService categoryService; + @ApiOperation("Adicionar uma nova categoria") @PostMapping("/categories") - public ResponseEntity addCategory(@RequestBody Category category) { + public ResponseEntity addCategory( + @ApiParam(value = "Nova categoria", required = true) @RequestBody Category category + ) { categoryService.saveCategory(category); return new ResponseEntity<>( @@ -28,8 +36,11 @@ public ResponseEntity addCategory(@RequestBody Category category) { ); } + @ApiOperation("Sugestão de categoria") @GetMapping("/category/detail/{detailPrefix}") - public ResponseEntity suggestionCategory(@PathVariable String detailPrefix) { + public ResponseEntity suggestionCategory( + @ApiParam(value = "Prefixo da categoria", required = true) @PathVariable String detailPrefix + ) { List categories = this.categoryService.searchCategoryByDetailPrefix(detailPrefix); return new ResponseEntity<>( diff --git a/src/main/java/com/santander/interview/controller/ExpenseController.java b/src/main/java/com/santander/interview/controller/ExpenseController.java index 1f625841..dcabb883 100644 --- a/src/main/java/com/santander/interview/controller/ExpenseController.java +++ b/src/main/java/com/santander/interview/controller/ExpenseController.java @@ -5,6 +5,7 @@ import com.santander.interview.domain.Expense; import com.santander.interview.domain.Response; import com.santander.interview.service.ExpenseService; +import io.swagger.annotations.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -14,13 +15,18 @@ import java.util.List; @RestController +@RequestMapping("/expense-management") +@Api(value = "Gasto") public class ExpenseController { @Autowired ExpenseService expenseService; + @ApiOperation(value = "Adicionar gasto") @PostMapping("/expenses") - public ResponseEntity addExpense(@RequestBody Expense expense) { + public ResponseEntity addExpense( + @ApiParam(value = "Novo gasto", required = true) @RequestBody Expense expense + ) { this.expenseService.addNewExpense(expense); return new ResponseEntity<>( new Response(HttpStatus.OK.value(), ADD_EXPENSE_SUCCESS.getMessage(), null), @@ -28,9 +34,12 @@ public ResponseEntity addExpense(@RequestBody Expense expense) { ); } + @ApiOperation("Buscar gastos por usuário") @GetMapping("/expense/userCode/{userCode}") - public ResponseEntity getExpenseByUserCode(@PathVariable long userCode) { - List expensesByUserCode = this.expenseService.findExpensesByCodigoUsuario(userCode); + public ResponseEntity getExpenseByUserCode( + @ApiParam(value = "Código do cliente", required = true) @PathVariable long userCode + ) { + List expensesByUserCode = this.expenseService.findExpensesByUserCode(userCode); return new ResponseEntity<>( new Response(HttpStatus.OK.value(), SEARCH_EXPENSE_BY_USER_CODE_SUCCESS.getMessage(), expensesByUserCode), @@ -38,10 +47,14 @@ public ResponseEntity getExpenseByUserCode(@PathVariable long userCode ); } + @ApiOperation("Buscar gastos por usuário e data") @GetMapping("/expense/userCode/{userCode}/date/{date}") - public ResponseEntity getExpenseByUserCodeAndDate(@PathVariable long userCode, @PathVariable String date) { + public ResponseEntity getExpenseByUserCodeAndDate( + @ApiParam(value = "Código do cliente", required = true) @PathVariable long userCode, + @ApiParam(value = "Data a ser pesquisada", required = true) @PathVariable String date + ) { try { - List expensesByUserCodeAndDate = this.expenseService.findExpensesByCodigoUsuarioAndData(userCode, date); + List expensesByUserCodeAndDate = this.expenseService.findExpensesByUserCodeAndDate(userCode, date); return new ResponseEntity<>( new Response(HttpStatus.OK.value(), SEARCH_EXPENSE_BY_USER_CODE_AND_DATE_SUCCESS.getMessage(), expensesByUserCodeAndDate), @@ -55,8 +68,12 @@ public ResponseEntity getExpenseByUserCodeAndDate(@PathVariable long u } } + @ApiOperation("Atualizar gasto") @PutMapping("/expense/{id}") - public ResponseEntity updateExpense(@PathVariable String id, @RequestBody Expense expense) { + public ResponseEntity updateExpense( + @ApiParam(value = "ID do gasto a ser atualizado", required = true) @PathVariable String id, + @ApiParam(value = "Gasto atualizado", required = true) @RequestBody Expense expense + ) { this.expenseService.updateExpense(id, expense); return new ResponseEntity<>( From f2f1f8dbae03a3aa5727fc40595c31f5937dabb1 Mon Sep 17 00:00:00 2001 From: Thiago Sato Date: Sun, 8 Sep 2019 22:47:39 -0300 Subject: [PATCH 20/30] =?UTF-8?q?Adicionando=20arquivo=20com=20as=20vari?= =?UTF-8?q?=C3=A1veis=20de=20ambiente?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/santander/interview/config/MongoConfig.java | 9 ++++++--- .../com/santander/interview/config/ThreadConfig.java | 6 ++++-- src/main/resources/application.properties | 6 ++++++ 3 files changed, 16 insertions(+), 5 deletions(-) create mode 100644 src/main/resources/application.properties diff --git a/src/main/java/com/santander/interview/config/MongoConfig.java b/src/main/java/com/santander/interview/config/MongoConfig.java index 1a270baf..78a905f7 100644 --- a/src/main/java/com/santander/interview/config/MongoConfig.java +++ b/src/main/java/com/santander/interview/config/MongoConfig.java @@ -8,13 +8,16 @@ @Configuration public class MongoConfig extends AbstractMongoConfiguration { - @Value("#{environment['MONGO_HOST']}") +// @Value("#{environment['MONGO_HOST']}") + @Value("${mongo.host}") private String MONGO_HOST; - @Value("#{environment['MONGO_PORT']}") +// @Value("#{environment['MONGO_PORT']}") + @Value("${mongo.port}") private int MONGO_PORT; - @Value("#{environment['MONGO_DATABASE']}") +// @Value("#{environment['MONGO_DATABASE']}") + @Value("${mongo.database}") private String MONGO_DATABASE; @Bean diff --git a/src/main/java/com/santander/interview/config/ThreadConfig.java b/src/main/java/com/santander/interview/config/ThreadConfig.java index 4a292d5d..a12a8150 100644 --- a/src/main/java/com/santander/interview/config/ThreadConfig.java +++ b/src/main/java/com/santander/interview/config/ThreadConfig.java @@ -12,10 +12,12 @@ public class ThreadConfig { public static final String THREAD_NAME_PREFIX = "interview"; - @Value("#{environment['ASYNC_CORE_POOL_SIZE']}") +// @Value("#{environment['ASYNC_CORE_POOL_SIZE']}") + @Value("${thread.async_core_pool_size}") private String ASYNC_CORE_POOL_SIZE; - @Value("#{environment['ASYNC_MAX_POOL_SIZE']}") +// @Value("#{environment['ASYNC_MAX_POOL_SIZE']}") + @Value("${thread.async_max_pool_size}") private String ASYNC_MAX_POOL_SIZE; @Bean diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties new file mode 100644 index 00000000..893fd39e --- /dev/null +++ b/src/main/resources/application.properties @@ -0,0 +1,6 @@ +mongo.host=localhost +mongo.port=27018 +mongo.database=app1 +thread.async_core_pool_size=5 +thread.async_max_pool_size=50 +info.app.version=@project.version@ \ No newline at end of file From d0938608c9603f3cc16f149d21ebfce79d112264 Mon Sep 17 00:00:00 2001 From: Thiago Sato Date: Mon, 9 Sep 2019 00:48:17 -0300 Subject: [PATCH 21/30] =?UTF-8?q?Adicionando=20m=C3=A9todos=20para=20o=20r?= =?UTF-8?q?esponse?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../interview/config/ThreadConfig.java | 4 +- .../santander/interview/domain/Response.java | 27 ----------- .../interview/domain/ResponseObject.java | 33 ++++++++++++++ .../utils/ExpenseManagementUtils.java | 45 ++++++++++++++++++- .../interview/domain/ResponseObjectTest.java | 33 ++++++++++++++ .../interview/domain/ResponseTest.java | 33 -------------- 6 files changed, 112 insertions(+), 63 deletions(-) delete mode 100644 src/main/java/com/santander/interview/domain/Response.java create mode 100644 src/main/java/com/santander/interview/domain/ResponseObject.java create mode 100644 src/test/java/com/santander/interview/domain/ResponseObjectTest.java delete mode 100644 src/test/java/com/santander/interview/domain/ResponseTest.java diff --git a/src/main/java/com/santander/interview/config/ThreadConfig.java b/src/main/java/com/santander/interview/config/ThreadConfig.java index a12a8150..8b7959ca 100644 --- a/src/main/java/com/santander/interview/config/ThreadConfig.java +++ b/src/main/java/com/santander/interview/config/ThreadConfig.java @@ -24,10 +24,10 @@ public class ThreadConfig { public Executor asyncExecutor() { ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor(); threadPoolTaskExecutor.setCorePoolSize( - ExpenseManagementUtils.expenseManagementUtils(10, ASYNC_CORE_POOL_SIZE) + ExpenseManagementUtils.convertStringtoInt(10, ASYNC_CORE_POOL_SIZE) ); threadPoolTaskExecutor.setMaxPoolSize( - ExpenseManagementUtils.expenseManagementUtils(100, ASYNC_MAX_POOL_SIZE) + ExpenseManagementUtils.convertStringtoInt(100, ASYNC_MAX_POOL_SIZE) ); threadPoolTaskExecutor.setQueueCapacity(Integer.MAX_VALUE); threadPoolTaskExecutor.setThreadNamePrefix(THREAD_NAME_PREFIX); diff --git a/src/main/java/com/santander/interview/domain/Response.java b/src/main/java/com/santander/interview/domain/Response.java deleted file mode 100644 index 46af5175..00000000 --- a/src/main/java/com/santander/interview/domain/Response.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.santander.interview.domain; - -public class Response { - private long statusCode; - private String message; - private Object data; - - public Response() { } - - public Response(long statusCode, String message, Object data) { - this.statusCode = statusCode; - this.message = message; - this.data = data; - } - - public long getStatusCode() { return statusCode; } - - public void setStatusCode(long statusCode) { this.statusCode = statusCode; } - - public String getMessage() { return message; } - - public void setMessage(String message) { this.message = message; } - - public Object getData() { return data; } - - public void setData(Object data) { this.data = data; } -} diff --git a/src/main/java/com/santander/interview/domain/ResponseObject.java b/src/main/java/com/santander/interview/domain/ResponseObject.java new file mode 100644 index 00000000..26b673dc --- /dev/null +++ b/src/main/java/com/santander/interview/domain/ResponseObject.java @@ -0,0 +1,33 @@ +package com.santander.interview.domain; + +public class ResponsePost { + private long statusCode; + private String userMessage; + private String internalMessage; + private Object data; + + public ResponsePost() { } + + public ResponsePost(long statusCode, String userMessage, String internalMessage, Object data) { + this.statusCode = statusCode; + this.userMessage = userMessage; + this.internalMessage = internalMessage; + this.data = data; + } + + public long getStatusCode() { return statusCode; } + + public void setStatusCode(long statusCode) { this.statusCode = statusCode; } + + public String getUserMessage() { return userMessage; } + + public void setUserMessage(String userMessage) { this.userMessage = userMessage; } + + public String getInternalMessage() { return internalMessage; } + + public void setInternalMessage(String internalMessage) { this.internalMessage = internalMessage; } + + public Object getData() { return data; } + + public void setData(Object data) { this.data = data; } +} diff --git a/src/main/java/com/santander/interview/utils/ExpenseManagementUtils.java b/src/main/java/com/santander/interview/utils/ExpenseManagementUtils.java index 4f650df3..2b03205c 100644 --- a/src/main/java/com/santander/interview/utils/ExpenseManagementUtils.java +++ b/src/main/java/com/santander/interview/utils/ExpenseManagementUtils.java @@ -1,8 +1,15 @@ package com.santander.interview.utils; +import com.santander.interview.domain.Response; +import com.santander.interview.domain.ResponseError; +import com.santander.interview.domain.ResponseObject; +import com.santander.interview.enums.ResponseMessageEnum; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; + public class ExpenseManagementUtils { - public static int expenseManagementUtils(int defaultValue, String value) { + public static int convertStringtoInt(int defaultValue, String value) { try { return Integer.valueOf(value); } catch (Exception e) { @@ -10,4 +17,40 @@ public static int expenseManagementUtils(int defaultValue, String value) { } } + public static ResponseEntity responseWithData (ResponseMessageEnum responseMessageEnum, + HttpStatus httpStatus, + Object data) { + return new ResponseEntity<>( + new ResponseObject( + httpStatus.value(), + responseMessageEnum.getUserMessage(), + responseMessageEnum.getInternalMessage(), + data + ), + httpStatus); + } + + public static ResponseEntity responseWithoutData (ResponseMessageEnum responseMessageEnum, + HttpStatus httpStatus) { + return new ResponseEntity<>( + new Response( + httpStatus.value(), + responseMessageEnum.getUserMessage(), + responseMessageEnum.getInternalMessage() + ), + httpStatus); + } + + public static ResponseEntity responseWithError (ResponseMessageEnum responseMessageEnum, + HttpStatus httpStatus) { + return new ResponseEntity<>( + new ResponseError( + httpStatus.value(), + responseMessageEnum.getUserMessage(), + responseMessageEnum.getInternalMessage(), + responseMessageEnum.getCode() + ), + httpStatus); + } + } diff --git a/src/test/java/com/santander/interview/domain/ResponseObjectTest.java b/src/test/java/com/santander/interview/domain/ResponseObjectTest.java new file mode 100644 index 00000000..443253cb --- /dev/null +++ b/src/test/java/com/santander/interview/domain/ResponseObjectTest.java @@ -0,0 +1,33 @@ +package com.santander.interview.domain; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class ResponsePostTest { + private static final long STATUS_CODE = 123; + private static final String MESSAGE = "Teste"; + private static final Object DATA = new Object(); + + @Test + public void ResponseTest() { + ResponsePost responsePost = new ResponsePost(); + responsePost.setData(DATA); + responsePost.setUserMessage(MESSAGE); + responsePost.setStatusCode(STATUS_CODE); + + Assert.assertEquals(responsePost.getData(), DATA); + Assert.assertEquals(responsePost.getUserMessage(), MESSAGE); + Assert.assertEquals(responsePost.getStatusCode(), STATUS_CODE); + } + + @Test + public void ResponseConstructorWithParamsTest() { + ResponsePost responsePost = new ResponsePost(STATUS_CODE, MESSAGE, DATA); + Assert.assertEquals(responsePost.getStatusCode(), STATUS_CODE); + Assert.assertEquals(responsePost.getUserMessage(), MESSAGE); + Assert.assertEquals(responsePost.getData(), DATA); + } +} diff --git a/src/test/java/com/santander/interview/domain/ResponseTest.java b/src/test/java/com/santander/interview/domain/ResponseTest.java deleted file mode 100644 index c2275e1a..00000000 --- a/src/test/java/com/santander/interview/domain/ResponseTest.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.santander.interview.domain; - -import org.junit.Assert; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.junit.MockitoJUnitRunner; - -@RunWith(MockitoJUnitRunner.class) -public class ResponseTest { - private static final long STATUS_CODE = 123; - private static final String MESSAGE = "Teste"; - private static final Object DATA = new Object(); - - @Test - public void ResponseTest() { - Response response = new Response(); - response.setData(DATA); - response.setMessage(MESSAGE); - response.setStatusCode(STATUS_CODE); - - Assert.assertEquals(response.getData(), DATA); - Assert.assertEquals(response.getMessage(), MESSAGE); - Assert.assertEquals(response.getStatusCode(), STATUS_CODE); - } - - @Test - public void ResponseConstructorWithParamsTest() { - Response response = new Response(STATUS_CODE, MESSAGE, DATA); - Assert.assertEquals(response.getStatusCode(), STATUS_CODE); - Assert.assertEquals(response.getMessage(), MESSAGE); - Assert.assertEquals(response.getData(), DATA); - } -} From daa25b1af8f33b3c21c3a7864653853677d5ed28 Mon Sep 17 00:00:00 2001 From: Thiago Sato Date: Mon, 9 Sep 2019 00:50:32 -0300 Subject: [PATCH 22/30] =?UTF-8?q?Tratar=20exce=C3=A7=C3=B5es=20ao=20execut?= =?UTF-8?q?ar=20os=20comandos?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../santander/interview/domain/Response.java | 28 +++++++++++ .../interview/domain/ResponseError.java | 14 ++++++ .../interview/domain/ResponseObject.java | 25 ++-------- .../interview/enums/ResponseMessageEnum.java | 42 +++++++++++----- .../interview/exception/ExpenseException.java | 48 +++++++++++++++++++ 5 files changed, 125 insertions(+), 32 deletions(-) create mode 100644 src/main/java/com/santander/interview/domain/Response.java create mode 100644 src/main/java/com/santander/interview/domain/ResponseError.java create mode 100644 src/main/java/com/santander/interview/exception/ExpenseException.java diff --git a/src/main/java/com/santander/interview/domain/Response.java b/src/main/java/com/santander/interview/domain/Response.java new file mode 100644 index 00000000..87291829 --- /dev/null +++ b/src/main/java/com/santander/interview/domain/Response.java @@ -0,0 +1,28 @@ +package com.santander.interview.domain; + +public class Response { + private long statusCode; + private String userMessage; + private String internalMessage; + + public Response() { } + + public Response(long statusCode, String userMessage, String internalMessage) { + this.statusCode = statusCode; + this.userMessage = userMessage; + this.internalMessage = internalMessage; + } + + public long getStatusCode() { return statusCode; } + + public void setStatusCode(long statusCode) { this.statusCode = statusCode; } + + public String getUserMessage() { return userMessage; } + + public void setUserMessage(String userMessage) { this.userMessage = userMessage; } + + public String getInternalMessage() { return internalMessage; } + + public void setInternalMessage(String internalMessage) { this.internalMessage = internalMessage; } + +} diff --git a/src/main/java/com/santander/interview/domain/ResponseError.java b/src/main/java/com/santander/interview/domain/ResponseError.java new file mode 100644 index 00000000..9f7d70b2 --- /dev/null +++ b/src/main/java/com/santander/interview/domain/ResponseError.java @@ -0,0 +1,14 @@ +package com.santander.interview.domain; + +public class ResponseError extends Response { + int internalCode; + + public ResponseError(long statusCode, String userMessage, String internalMessage, int internalCode) { + super(statusCode, userMessage, internalMessage); + this.internalCode = internalCode; + } + + public int getInternalCode() { return internalCode; } + + public void setInternalCode(int internalCode) { this.internalCode = internalCode; } +} diff --git a/src/main/java/com/santander/interview/domain/ResponseObject.java b/src/main/java/com/santander/interview/domain/ResponseObject.java index 26b673dc..59377419 100644 --- a/src/main/java/com/santander/interview/domain/ResponseObject.java +++ b/src/main/java/com/santander/interview/domain/ResponseObject.java @@ -1,32 +1,15 @@ package com.santander.interview.domain; -public class ResponsePost { - private long statusCode; - private String userMessage; - private String internalMessage; +public class ResponseObject extends Response { private Object data; - public ResponsePost() { } + public ResponseObject() { } - public ResponsePost(long statusCode, String userMessage, String internalMessage, Object data) { - this.statusCode = statusCode; - this.userMessage = userMessage; - this.internalMessage = internalMessage; + public ResponseObject(long statusCode, String userMessage, String internalMessage, Object data) { + super(statusCode, userMessage, internalMessage); this.data = data; } - public long getStatusCode() { return statusCode; } - - public void setStatusCode(long statusCode) { this.statusCode = statusCode; } - - public String getUserMessage() { return userMessage; } - - public void setUserMessage(String userMessage) { this.userMessage = userMessage; } - - public String getInternalMessage() { return internalMessage; } - - public void setInternalMessage(String internalMessage) { this.internalMessage = internalMessage; } - public Object getData() { return data; } public void setData(Object data) { this.data = data; } diff --git a/src/main/java/com/santander/interview/enums/ResponseMessageEnum.java b/src/main/java/com/santander/interview/enums/ResponseMessageEnum.java index bda41bb4..8571da37 100644 --- a/src/main/java/com/santander/interview/enums/ResponseMessageEnum.java +++ b/src/main/java/com/santander/interview/enums/ResponseMessageEnum.java @@ -1,19 +1,39 @@ package com.santander.interview.enums; public enum ResponseMessageEnum { - ADD_EXPENSE_SUCCESS("Gasto criado com sucesso"), - SEARCH_EXPENSE_BY_USER_CODE_SUCCESS("Busca pelo código do usuário realizada com sucesso"), - SEARCH_EXPENSE_BY_USER_CODE_AND_DATE_SUCCESS("Busca por código do usuário e pela data realizada com sucesso"), - UPDATE_EXPENSE_SUCCESS("Gasto atualizado com sucesso"), - ERROR_BADLY_FORMATTED_DATE("Data mal formatada"), - ADD_CATEGORY_SUCCESS("Categoria criada com sucesso"), - SUGGESTION_CATEGORY_SUCCESS("Busca por categorias realizada com sucesso"); + UNKNOWN_ERROR(0, "", ""), + ADD_EXPENSE_SUCCESS(1,"Gasto criado com sucesso", "Gasto criado"), +// EXPENSE_NOT_FOUND_TO_USER_CODE(2, "Esse cliente não possui gastos", +// "Não foram encontrados gastos para esse código de usuário"), + SEARCH_EXPENSE_BY_USER_CODE_SUCCESS(3, "Busca pelo cliente realizada com sucesso", + "Gastos do usuário encontrados"), + EXPENSE_BADLY_FORMATTED_DATE(4, "Data mal formatada", + "A data deve estar no formato ddMMyyyy"), + EXPENSE_NOT_FOUND(5, "Gasto não encontrado", + "Não foi encontrado gasto para o ID informado"), + SEARCH_EXPENSE_BY_USER_CODE_AND_DATE_SUCCESS(6, + "Busca realizada com sucesso", + "Busca por código do usuário e pela data realizada com sucesso"), + UPDATE_EXPENSE_SUCCESS(7, "Gasto atualizado com sucesso", + "Gasto encontrado e atualizado"), + ADD_CATEGORY_SUCCESS(8, "Categoria adicionada com sucesso", + "Categoria criada"), + SUGGESTION_CATEGORY_SUCCESS(9, "Busca da categoria realizada com sucesso", + "Lista com categorias"); - private String message; + private int code; + private String userMessage; + private String internalMessage; - private ResponseMessageEnum(String message) { - this.message = message; + private ResponseMessageEnum(int code, String userMessage, String internalMessage) { + this.code = code; + this.userMessage = userMessage; + this.internalMessage = internalMessage; } - public String getMessage() { return this.message; } + public int getCode() { return this.code; } + + public String getUserMessage() { return this.userMessage; } + + public String getInternalMessage() { return this.internalMessage; } } diff --git a/src/main/java/com/santander/interview/exception/ExpenseException.java b/src/main/java/com/santander/interview/exception/ExpenseException.java new file mode 100644 index 00000000..b6a54fec --- /dev/null +++ b/src/main/java/com/santander/interview/exception/ExpenseException.java @@ -0,0 +1,48 @@ +package com.santander.interview.exception; + +import static com.santander.interview.enums.ResponseMessageEnum.*; + +import com.santander.interview.enums.ResponseMessageEnum; +import org.springframework.http.HttpStatus; + +public class ExpenseException extends Exception{ + private HttpStatus statusCode; + private ResponseMessageEnum responseMessageEnum; + private String message; + + public ExpenseException() { + super(); + this.statusCode = HttpStatus.INTERNAL_SERVER_ERROR; + this.message = super.getMessage(); + this.responseMessageEnum = UNKNOWN_ERROR; + } + + public ExpenseException(HttpStatus statusCode, String message, ResponseMessageEnum responseMessageEnum) { + super(message); + this.statusCode = statusCode; + this.message = message; + this.responseMessageEnum = responseMessageEnum; + } + + public ExpenseException(HttpStatus statusCode, ResponseMessageEnum responseMessageEnum) { + super(); + this.statusCode = statusCode; + this.message = super.getMessage(); + this.responseMessageEnum = responseMessageEnum; + } + + public HttpStatus getStatusCode() { return statusCode; } + + public void setStatusCode(HttpStatus statusCode) { this.statusCode = statusCode; } + + @Override + public String getMessage() { return message; } + + public void setMessage(String message) { this.message = message; } + + public ResponseMessageEnum getResponseMessageEnum() { return responseMessageEnum; } + + public void setResponseMessageEnum(ResponseMessageEnum responseMessageEnum) { + this.responseMessageEnum = responseMessageEnum; + } +} From c68aeef8ecc5f3c54a15412a48450d206ddd90b2 Mon Sep 17 00:00:00 2001 From: Thiago Sato Date: Mon, 9 Sep 2019 00:52:00 -0300 Subject: [PATCH 23/30] =?UTF-8?q?Tratar=20as=20exce=C3=A7=C3=B5es=20ao=20b?= =?UTF-8?q?uscar/atualizar=20um=20dado=20do=20banco=20de=20dados?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/CategoryController.java | 24 ++++----- .../controller/ExpenseController.java | 52 ++++++++----------- .../santander/interview/domain/Expense.java | 48 ++++++++++++----- .../repository/ExpenseRepository.java | 4 +- .../interview/service/CategoryService.java | 2 +- .../interview/service/ExpenseService.java | 7 +-- .../service/impl/CategoryServiceImpl.java | 4 +- .../service/impl/ExpenseServiceImpl.java | 52 ++++++++++++------- 8 files changed, 109 insertions(+), 84 deletions(-) diff --git a/src/main/java/com/santander/interview/controller/CategoryController.java b/src/main/java/com/santander/interview/controller/CategoryController.java index 1d7df8a4..3d9f238d 100644 --- a/src/main/java/com/santander/interview/controller/CategoryController.java +++ b/src/main/java/com/santander/interview/controller/CategoryController.java @@ -3,8 +3,10 @@ import static com.santander.interview.enums.ResponseMessageEnum.*; import com.santander.interview.domain.Category; -import com.santander.interview.domain.Response; +import com.santander.interview.domain.ResponseObject; +import com.santander.interview.enums.ResponseMessageEnum; import com.santander.interview.service.CategoryService; +import com.santander.interview.utils.ExpenseManagementUtils; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; @@ -25,28 +27,22 @@ public class CategoryController { @ApiOperation("Adicionar uma nova categoria") @PostMapping("/categories") - public ResponseEntity addCategory( + public ResponseEntity addCategory( @ApiParam(value = "Nova categoria", required = true) @RequestBody Category category ) { categoryService.saveCategory(category); - return new ResponseEntity<>( - new Response(HttpStatus.OK.value(), ADD_CATEGORY_SUCCESS.getMessage(), null), - HttpStatus.OK - ); + return ExpenseManagementUtils.responseWithoutData(ADD_CATEGORY_SUCCESS, HttpStatus.OK); } @ApiOperation("Sugestão de categoria") - @GetMapping("/category/detail/{detailPrefix}") - public ResponseEntity suggestionCategory( - @ApiParam(value = "Prefixo da categoria", required = true) @PathVariable String detailPrefix + @GetMapping("/category/detail/{detailSubstring}") + public ResponseEntity suggestionCategory( + @ApiParam(value = "Substring da categoria", required = true) @PathVariable String detailSubstring ) { - List categories = this.categoryService.searchCategoryByDetailPrefix(detailPrefix); + List categories = this.categoryService.searchCategoryByDetailSubstring(detailSubstring); - return new ResponseEntity<>( - new Response(HttpStatus.OK.value(), SUGGESTION_CATEGORY_SUCCESS.getMessage(), categories), - HttpStatus.OK - ); + return ExpenseManagementUtils.responseWithData(SUGGESTION_CATEGORY_SUCCESS, HttpStatus.OK, categories); } } diff --git a/src/main/java/com/santander/interview/controller/ExpenseController.java b/src/main/java/com/santander/interview/controller/ExpenseController.java index dcabb883..ecd34dee 100644 --- a/src/main/java/com/santander/interview/controller/ExpenseController.java +++ b/src/main/java/com/santander/interview/controller/ExpenseController.java @@ -4,14 +4,15 @@ import com.santander.interview.domain.Expense; import com.santander.interview.domain.Response; +import com.santander.interview.exception.ExpenseException; import com.santander.interview.service.ExpenseService; +import com.santander.interview.utils.ExpenseManagementUtils; import io.swagger.annotations.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import java.text.ParseException; import java.util.List; @RestController @@ -28,57 +29,48 @@ public ResponseEntity addExpense( @ApiParam(value = "Novo gasto", required = true) @RequestBody Expense expense ) { this.expenseService.addNewExpense(expense); - return new ResponseEntity<>( - new Response(HttpStatus.OK.value(), ADD_EXPENSE_SUCCESS.getMessage(), null), - HttpStatus.OK - ); + + return ExpenseManagementUtils.responseWithoutData(ADD_EXPENSE_SUCCESS, HttpStatus.OK); } @ApiOperation("Buscar gastos por usuário") @GetMapping("/expense/userCode/{userCode}") - public ResponseEntity getExpenseByUserCode( + public ResponseEntity getExpenseByUserCode( @ApiParam(value = "Código do cliente", required = true) @PathVariable long userCode ) { List expensesByUserCode = this.expenseService.findExpensesByUserCode(userCode); - return new ResponseEntity<>( - new Response(HttpStatus.OK.value(), SEARCH_EXPENSE_BY_USER_CODE_SUCCESS.getMessage(), - expensesByUserCode), - HttpStatus.OK - ); + + return ExpenseManagementUtils.responseWithData(SEARCH_EXPENSE_BY_USER_CODE_SUCCESS, + HttpStatus.OK, expensesByUserCode); } @ApiOperation("Buscar gastos por usuário e data") @GetMapping("/expense/userCode/{userCode}/date/{date}") - public ResponseEntity getExpenseByUserCodeAndDate( + public ResponseEntity getExpenseByUserCodeAndDate( @ApiParam(value = "Código do cliente", required = true) @PathVariable long userCode, @ApiParam(value = "Data a ser pesquisada", required = true) @PathVariable String date ) { + List expensesByUserCodeAndDate; try { - List expensesByUserCodeAndDate = this.expenseService.findExpensesByUserCodeAndDate(userCode, date); - return new ResponseEntity<>( - new Response(HttpStatus.OK.value(), SEARCH_EXPENSE_BY_USER_CODE_AND_DATE_SUCCESS.getMessage(), - expensesByUserCodeAndDate), - HttpStatus.OK - ); - } catch (ParseException pe){ - return new ResponseEntity<>( - new Response(HttpStatus.BAD_REQUEST.value(), ERROR_BADLY_FORMATTED_DATE.getMessage(), null), - HttpStatus.BAD_REQUEST - ); + expensesByUserCodeAndDate = this.expenseService.findExpensesByUserCodeAndDate(userCode, date); + return ExpenseManagementUtils.responseWithData(SEARCH_EXPENSE_BY_USER_CODE_AND_DATE_SUCCESS, + HttpStatus.OK, expensesByUserCodeAndDate); + } catch (ExpenseException ee){ + return ExpenseManagementUtils.responseWithError(ee.getResponseMessageEnum(), ee.getStatusCode()); } } @ApiOperation("Atualizar gasto") @PutMapping("/expense/{id}") - public ResponseEntity updateExpense( + public ResponseEntity updateExpense( @ApiParam(value = "ID do gasto a ser atualizado", required = true) @PathVariable String id, @ApiParam(value = "Gasto atualizado", required = true) @RequestBody Expense expense ) { - this.expenseService.updateExpense(id, expense); - - return new ResponseEntity<>( - new Response(HttpStatus.OK.value(), UPDATE_EXPENSE_SUCCESS.getMessage(), null), - HttpStatus.OK - ); + try { + this.expenseService.updateExpense(id, expense); + return ExpenseManagementUtils.responseWithoutData(UPDATE_EXPENSE_SUCCESS, HttpStatus.OK); + } catch (ExpenseException ee) { + return ExpenseManagementUtils.responseWithError(ee.getResponseMessageEnum(), ee.getStatusCode()); + } } } diff --git a/src/main/java/com/santander/interview/domain/Expense.java b/src/main/java/com/santander/interview/domain/Expense.java index 49ec7d8d..62e480a5 100644 --- a/src/main/java/com/santander/interview/domain/Expense.java +++ b/src/main/java/com/santander/interview/domain/Expense.java @@ -3,37 +3,57 @@ import org.springframework.data.annotation.Id; import java.util.Date; +import java.util.UUID; public class Expense { @Id private String id; - private String descricao; - private double valor; - private long codigoUsuario; - private Date data; + private String description; + private double value; + private long userCode; + private Date date; private Category category; - public Expense() { } + private String generateID() { return UUID.randomUUID().toString(); } + + public Expense() { this.id = this.generateID(); } + + public Expense(String description, double value, long userCode, Date date) { + this.id = UUID.randomUUID().toString(); + this.description = description; + this.value = value; + this.userCode = userCode; + this.date = date; + } + + public Expense(String description, double value, long userCode, Date date, Category category) { + this.id = UUID.randomUUID().toString(); + this.description = description; + this.value = value; + this.userCode = userCode; + this.date = date; + this.category = category; + } public String getId() { return id; } public void setId(String id) { this.id = id; } - public String getDescricao() { return descricao; } + public String getDescription() { return description; } - public void setDescricao(String descricao) { this.descricao = descricao; } + public void setDescription(String description) { this.description = description; } - public double getValor() { return valor; } + public double getValue() { return value; } - public void setValor(double valor) { this.valor = valor; } + public void setValue(double value) { this.value = value; } - public long getCodigoUsuario() { return codigoUsuario; } + public long getUserCode() { return userCode; } - public void setCodigoUsuario(long codigoUsuario) { this.codigoUsuario = codigoUsuario; } + public void setUserCode(long userCode) { this.userCode = userCode; } - public Date getData() { return data; } + public Date getDate() { return date; } - public void setData(Date data) { this.data = data; } + public void setDate(Date date) { this.date = date; } public Category getCategory() { return category; } @@ -43,7 +63,7 @@ public Expense() { } public String toString() { return String.format( "Expense[id=%s, descricao=%s, valor=%f, codigoUsuario=%d, data=%s]", - this.id, this.descricao, this.valor, this.codigoUsuario, this.data.toString() + this.id, this.description, this.value, this.userCode, this.date.toString() ); } } diff --git a/src/main/java/com/santander/interview/repository/ExpenseRepository.java b/src/main/java/com/santander/interview/repository/ExpenseRepository.java index f3c09c9d..a494c648 100644 --- a/src/main/java/com/santander/interview/repository/ExpenseRepository.java +++ b/src/main/java/com/santander/interview/repository/ExpenseRepository.java @@ -9,6 +9,6 @@ @Repository public interface ExpenseRepository extends MongoRepository { - public List findByCodigoUsuario(long codigoUsuario); - public List findByCodigoUsuarioAndDataBetween(long codigoUsuario, Date startData, Date endData); + public List findByUserCode(long userCode); + public List findByUserCodeAndDateBetween(long userCode, Date startData, Date endData); } diff --git a/src/main/java/com/santander/interview/service/CategoryService.java b/src/main/java/com/santander/interview/service/CategoryService.java index ae935ab2..0ca95838 100644 --- a/src/main/java/com/santander/interview/service/CategoryService.java +++ b/src/main/java/com/santander/interview/service/CategoryService.java @@ -6,5 +6,5 @@ public interface CategoryService { public void saveCategory(Category category); - public List searchCategoryByDetailPrefix(String detailPrefix); + public List searchCategoryByDetailSubstring(String detailSubstring); } diff --git a/src/main/java/com/santander/interview/service/ExpenseService.java b/src/main/java/com/santander/interview/service/ExpenseService.java index c227e780..df7e3c3e 100644 --- a/src/main/java/com/santander/interview/service/ExpenseService.java +++ b/src/main/java/com/santander/interview/service/ExpenseService.java @@ -1,13 +1,14 @@ package com.santander.interview.service; import com.santander.interview.domain.Expense; +import com.santander.interview.exception.ExpenseException; import java.text.ParseException; import java.util.List; public interface ExpenseService { public void addNewExpense(Expense expense); - public List findExpensesByCodigoUsuario(long codigoUsuario); - public List findExpensesByCodigoUsuarioAndData(long codigoUsuario, String data) throws ParseException; - public void updateExpense(String id, Expense expense); + public List findExpensesByUserCode(long userCode); + public List findExpensesByUserCodeAndDate(long userCode, String date) throws ExpenseException; + public void updateExpense(String id, Expense expense) throws ExpenseException; } diff --git a/src/main/java/com/santander/interview/service/impl/CategoryServiceImpl.java b/src/main/java/com/santander/interview/service/impl/CategoryServiceImpl.java index 9cb37cba..df0c62c4 100644 --- a/src/main/java/com/santander/interview/service/impl/CategoryServiceImpl.java +++ b/src/main/java/com/santander/interview/service/impl/CategoryServiceImpl.java @@ -23,7 +23,7 @@ public void saveCategory(Category category) { } @Override - public List searchCategoryByDetailPrefix(String detailPrefix) { - return this.categoryRepository.findByDetailLike(detailPrefix); + public List searchCategoryByDetailSubstring(String detailSubstring) { + return this.categoryRepository.findByDetailLike(detailSubstring); } } diff --git a/src/main/java/com/santander/interview/service/impl/ExpenseServiceImpl.java b/src/main/java/com/santander/interview/service/impl/ExpenseServiceImpl.java index 39c0564e..83e636dd 100644 --- a/src/main/java/com/santander/interview/service/impl/ExpenseServiceImpl.java +++ b/src/main/java/com/santander/interview/service/impl/ExpenseServiceImpl.java @@ -1,11 +1,15 @@ package com.santander.interview.service.impl; +import static com.santander.interview.enums.ResponseMessageEnum.*; + import com.santander.interview.domain.Category; import com.santander.interview.domain.Expense; +import com.santander.interview.exception.ExpenseException; import com.santander.interview.repository.CategoryRepository; import com.santander.interview.repository.ExpenseRepository; import com.santander.interview.service.ExpenseService; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; import java.text.ParseException; @@ -20,7 +24,7 @@ public class ExpenseServiceImpl implements ExpenseService { @Autowired CategoryRepository categoryRepository; - private String generateUuid() { return UUID.randomUUID().toString(); } +// private String generateUuid() { return UUID.randomUUID().toString(); } private Category automaticCategorization(Expense expense) { Category category = expense.getCategory(); @@ -33,38 +37,50 @@ private Category automaticCategorization(Expense expense) { @Override public void addNewExpense(Expense expense) { - expense.setCategory(this.automaticCategorization(expense)); - expense.setId(this.generateUuid()); - this.expenseRepository.save(expense); + Expense newExpense = new Expense( + expense.getDescription(), + expense.getValue(), + expense.getUserCode(), + expense.getDate() + ); + newExpense.setCategory(this.automaticCategorization(expense)); + this.expenseRepository.save(newExpense); } @Override - public List findExpensesByCodigoUsuario(long codigoUsuario) { - List expenses = expenseRepository.findByCodigoUsuario(codigoUsuario); + public List findExpensesByUserCode(long userCode) { + List expenses = expenseRepository.findByUserCode(userCode); Collections.sort(expenses, new Comparator() { @Override public int compare(Expense expense1, Expense expense2) { - return expense2.getData().compareTo(expense1.getData()); + return expense2.getDate().compareTo(expense1.getDate()); } }); return expenses; } @Override - public List findExpensesByCodigoUsuarioAndData(long codigoUsuario, String data) throws ParseException { + public List findExpensesByUserCodeAndDate(long userCode, String date) throws ExpenseException { long oneDayInMilliseconds = 1000 * 60 * 60 * 24; - Date startDate = new SimpleDateFormat("ddMMyyyy").parse(data); - Date endDate = new Date(startDate.getTime() + oneDayInMilliseconds); - return this.expenseRepository.findByCodigoUsuarioAndDataBetween(codigoUsuario, startDate, endDate); + try { + Date startDate = new SimpleDateFormat("ddMMyyyy").parse(date); + Date endDate = new Date(startDate.getTime() + oneDayInMilliseconds); + return this.expenseRepository.findByUserCodeAndDateBetween(userCode, startDate, endDate); + } catch(ParseException pe) { + throw new ExpenseException(HttpStatus.BAD_REQUEST, EXPENSE_BADLY_FORMATTED_DATE); + } } @Override - public void updateExpense(String id, Expense expense) { - this.expenseRepository.findById(id).ifPresent( - searchResult -> { - expense.setId(searchResult.getId()); - this.expenseRepository.save(expense); - } - ); + public void updateExpense(String id, Expense expense) throws ExpenseException { + Optional existExpense = this.expenseRepository.findById(id); + if(existExpense.isPresent()) { + Expense expenseFound = existExpense.get(); + expense.setId(expenseFound.getId()); + expense.setCategory(this.automaticCategorization(expense)); + this.expenseRepository.save(expense); + } else { + throw new ExpenseException(HttpStatus.NOT_FOUND, EXPENSE_NOT_FOUND); + } } } From fdb5e7d3c1ea602571c163063203d1069aaa2b6e Mon Sep 17 00:00:00 2001 From: Thiago Sato Date: Mon, 9 Sep 2019 02:38:40 -0300 Subject: [PATCH 24/30] =?UTF-8?q?Testes=20unit=C3=A1rios?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/CategoryController.java | 3 +- .../controller/ExpenseController.java | 3 +- .../santander/interview/domain/Category.java | 8 +- .../service/impl/CategoryServiceImpl.java | 7 +- .../interview/ExpenseManagementTest.java | 12 --- .../controller/CategoryControllerTest.java | 22 +++-- .../controller/ExpenseControllerTest.java | 78 ++++++++------- .../interview/domain/CategoryTest.java | 7 +- .../interview/domain/ExpenseTest.java | 52 +++++++--- .../interview/domain/ResponseErrorTest.java | 24 +++++ .../interview/domain/ResponseObjectTest.java | 34 ++++--- .../interview/domain/ResponseTest.java | 31 ++++++ .../enums/ResponseMessageEnumTest.java | 15 +++ .../exception/ExpenseExceptionTest.java | 41 ++++++++ .../service/CategoryServiceTest.java | 8 +- .../interview/service/ExpenseServiceTest.java | 97 ++++++++++++------- .../utils/ExpenseManagementUtilsTest.java | 58 +++++++++++ 17 files changed, 362 insertions(+), 138 deletions(-) delete mode 100644 src/test/java/com/santander/interview/ExpenseManagementTest.java create mode 100644 src/test/java/com/santander/interview/domain/ResponseErrorTest.java create mode 100644 src/test/java/com/santander/interview/domain/ResponseTest.java create mode 100644 src/test/java/com/santander/interview/enums/ResponseMessageEnumTest.java create mode 100644 src/test/java/com/santander/interview/exception/ExpenseExceptionTest.java create mode 100644 src/test/java/com/santander/interview/utils/ExpenseManagementUtilsTest.java diff --git a/src/main/java/com/santander/interview/controller/CategoryController.java b/src/main/java/com/santander/interview/controller/CategoryController.java index 3d9f238d..24baf437 100644 --- a/src/main/java/com/santander/interview/controller/CategoryController.java +++ b/src/main/java/com/santander/interview/controller/CategoryController.java @@ -3,6 +3,7 @@ import static com.santander.interview.enums.ResponseMessageEnum.*; import com.santander.interview.domain.Category; +import com.santander.interview.domain.Response; import com.santander.interview.domain.ResponseObject; import com.santander.interview.enums.ResponseMessageEnum; import com.santander.interview.service.CategoryService; @@ -27,7 +28,7 @@ public class CategoryController { @ApiOperation("Adicionar uma nova categoria") @PostMapping("/categories") - public ResponseEntity addCategory( + public ResponseEntity addCategory( @ApiParam(value = "Nova categoria", required = true) @RequestBody Category category ) { categoryService.saveCategory(category); diff --git a/src/main/java/com/santander/interview/controller/ExpenseController.java b/src/main/java/com/santander/interview/controller/ExpenseController.java index ecd34dee..03d77e32 100644 --- a/src/main/java/com/santander/interview/controller/ExpenseController.java +++ b/src/main/java/com/santander/interview/controller/ExpenseController.java @@ -4,6 +4,7 @@ import com.santander.interview.domain.Expense; import com.santander.interview.domain.Response; +import com.santander.interview.domain.ResponseObject; import com.santander.interview.exception.ExpenseException; import com.santander.interview.service.ExpenseService; import com.santander.interview.utils.ExpenseManagementUtils; @@ -35,7 +36,7 @@ public ResponseEntity addExpense( @ApiOperation("Buscar gastos por usuário") @GetMapping("/expense/userCode/{userCode}") - public ResponseEntity getExpenseByUserCode( + public ResponseEntity getExpenseByUserCode( @ApiParam(value = "Código do cliente", required = true) @PathVariable long userCode ) { List expensesByUserCode = this.expenseService.findExpensesByUserCode(userCode); diff --git a/src/main/java/com/santander/interview/domain/Category.java b/src/main/java/com/santander/interview/domain/Category.java index f14543a7..a4cdf0b5 100644 --- a/src/main/java/com/santander/interview/domain/Category.java +++ b/src/main/java/com/santander/interview/domain/Category.java @@ -2,15 +2,19 @@ import org.springframework.data.annotation.Id; +import java.util.UUID; + public class Category { @Id private String id; private String detail; + private String generateID() { return UUID.randomUUID().toString(); } + public Category() { } - public Category(String id, String detail) { - this.id = id; + public Category(String detail) { + this.id = this.generateID(); this.detail = detail; } diff --git a/src/main/java/com/santander/interview/service/impl/CategoryServiceImpl.java b/src/main/java/com/santander/interview/service/impl/CategoryServiceImpl.java index df0c62c4..e90cc72d 100644 --- a/src/main/java/com/santander/interview/service/impl/CategoryServiceImpl.java +++ b/src/main/java/com/santander/interview/service/impl/CategoryServiceImpl.java @@ -7,19 +7,16 @@ import org.springframework.stereotype.Service; import java.util.List; -import java.util.UUID; @Service public class CategoryServiceImpl implements CategoryService { @Autowired CategoryRepository categoryRepository; - private String generateUuid() { return UUID.randomUUID().toString(); } - @Override public void saveCategory(Category category) { - category.setId(generateUuid()); - this.categoryRepository.save(category); + Category newCategory = new Category(category.getDetail()); + this.categoryRepository.save(newCategory); } @Override diff --git a/src/test/java/com/santander/interview/ExpenseManagementTest.java b/src/test/java/com/santander/interview/ExpenseManagementTest.java deleted file mode 100644 index bf0f20e7..00000000 --- a/src/test/java/com/santander/interview/ExpenseManagementTest.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.santander.interview; - -import org.junit.Test; - -public class ExpenseManagementTest { - - @Test(expected = Exception.class) - public void mainStartDispatcherTest() { - String[] args = new String[]{}; - ExpenseManagement.main(args); - } -} diff --git a/src/test/java/com/santander/interview/controller/CategoryControllerTest.java b/src/test/java/com/santander/interview/controller/CategoryControllerTest.java index 830751f6..a5283bed 100644 --- a/src/test/java/com/santander/interview/controller/CategoryControllerTest.java +++ b/src/test/java/com/santander/interview/controller/CategoryControllerTest.java @@ -1,7 +1,10 @@ package com.santander.interview.controller; +import static com.santander.interview.enums.ResponseMessageEnum.*; + import com.santander.interview.domain.Category; import com.santander.interview.domain.Response; +import com.santander.interview.domain.ResponseObject; import com.santander.interview.service.CategoryService; import org.junit.Assert; import org.junit.Before; @@ -13,9 +16,11 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import java.util.ArrayList; +import java.util.List; + public class CategoryControllerTest { - private static final String CATEGORY_ID = "12asd"; private static final String CATEGORY_DETAIL = "Detail"; private Category category; @@ -27,24 +32,27 @@ public class CategoryControllerTest { @Before public void init() { - category = new Category(CATEGORY_ID, CATEGORY_DETAIL); + category = new Category(CATEGORY_DETAIL); MockitoAnnotations.initMocks(this); } @Test public void suggestionCategoryTest() { - String detailPrefix = "teste"; - Mockito.when(categoryService.searchCategoryByDetailPrefix(detailPrefix)).thenReturn(null); - ResponseEntity response = categoryController.suggestionCategory(detailPrefix); + String detailSubstring = "teste"; + List categories = new ArrayList<>(); + Mockito.when(categoryService.searchCategoryByDetailSubstring(detailSubstring)).thenReturn(categories); + ResponseEntity response = categoryController.suggestionCategory(detailSubstring); Assert.assertEquals(response.getStatusCode(), HttpStatus.OK); - Assert.assertNull(response.getBody().getData()); + Assert.assertNotNull(response.getBody().getData()); } @Test public void addCategoryTest() { ResponseEntity response = categoryController.addCategory(category); Assert.assertEquals(response.getStatusCode(), HttpStatus.OK); - Assert.assertNull(response.getBody().getData()); + Assert.assertEquals(response.getBody().getStatusCode(), HttpStatus.OK.value()); + Assert.assertEquals(response.getBody().getUserMessage(), ADD_CATEGORY_SUCCESS.getUserMessage()); + Assert.assertEquals(response.getBody().getInternalMessage(), ADD_CATEGORY_SUCCESS.getInternalMessage()); } } diff --git a/src/test/java/com/santander/interview/controller/ExpenseControllerTest.java b/src/test/java/com/santander/interview/controller/ExpenseControllerTest.java index c5833258..7965cbf2 100644 --- a/src/test/java/com/santander/interview/controller/ExpenseControllerTest.java +++ b/src/test/java/com/santander/interview/controller/ExpenseControllerTest.java @@ -1,7 +1,11 @@ package com.santander.interview.controller; +import static com.santander.interview.enums.ResponseMessageEnum.*; + import com.santander.interview.domain.Expense; import com.santander.interview.domain.Response; +import com.santander.interview.domain.ResponseObject; +import com.santander.interview.exception.ExpenseException; import com.santander.interview.service.ExpenseService; import org.junit.Assert; import org.junit.Before; @@ -13,17 +17,15 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import java.text.ParseException; import java.util.ArrayList; import java.util.Date; import java.util.List; -import java.util.UUID; public class ExpenseControllerTest { - private static final String DESCRICAO = "descricao"; - private static final Double VALOR = 198.23; - private static final long CODIGO_USUARIO = 129; - private static final Date DATA = new Date(); + private static final String DESCRIPTION = "descricao"; + private static final Double VALUE = 198.23; + private static final long USER_CODE = 129; + private static final Date DATE = new Date(); @InjectMocks ExpenseController expenseController = new ExpenseController(); @@ -33,19 +35,10 @@ public class ExpenseControllerTest { Expense expense; - String uuid; - @Before public void init() { MockitoAnnotations.initMocks(this); - this.uuid = UUID.randomUUID().toString(); - this.expense = new Expense(); - this.expense.setId(uuid); - this.expense.setDescricao(DESCRICAO); - this.expense.setValor(VALOR); - this.expense.setCodigoUsuario(CODIGO_USUARIO); - this.expense.setData(DATA); - this.expense.setCategory(null); + this.expense = new Expense(DESCRIPTION, VALUE, USER_CODE, DATE); } @Test @@ -53,53 +46,66 @@ public void addExpenseTest() { ResponseEntity response = this.expenseController.addExpense(this.expense); Assert.assertEquals(response.getStatusCode(), HttpStatus.OK); Assert.assertEquals(response.getBody().getStatusCode(), HttpStatus.OK.value()); - Assert.assertNull(response.getBody().getData()); + Assert.assertEquals(response.getBody().getInternalMessage(), ADD_EXPENSE_SUCCESS.getInternalMessage()); + Assert.assertEquals(response.getBody().getUserMessage(), ADD_EXPENSE_SUCCESS.getUserMessage()); } @Test public void getExpenseByUserCodeTest() { - long userCode = CODIGO_USUARIO; + long userCode = USER_CODE; List list = new ArrayList<>(); list.add(this.expense); - Mockito.when(expenseService.findExpensesByCodigoUsuario(userCode)).thenReturn(list); + Mockito.when(expenseService.findExpensesByUserCode(userCode)).thenReturn(list); - ResponseEntity response = this.expenseController.getExpenseByUserCode(userCode); + ResponseEntity response = this.expenseController.getExpenseByUserCode(userCode); Assert.assertEquals(response.getStatusCode(), HttpStatus.OK); Assert.assertEquals(response.getBody().getStatusCode(), HttpStatus.OK.value()); Assert.assertEquals(response.getBody().getData(), list); + Assert.assertEquals(response.getBody().getInternalMessage(), SEARCH_EXPENSE_BY_USER_CODE_SUCCESS.getInternalMessage()); + Assert.assertEquals(response.getBody().getUserMessage(), SEARCH_EXPENSE_BY_USER_CODE_SUCCESS.getUserMessage()); } @Test - public void getExpenseByUserCodeAndDateTest() throws ParseException { - long userCode = CODIGO_USUARIO; - String date = DATA.toString(); + public void getExpenseByUserCodeAndDateTest() throws ExpenseException { + long userCode = USER_CODE; + String date = DATE.toString(); List list = new ArrayList<>(); list.add(this.expense); - Mockito.when(this.expenseService.findExpensesByCodigoUsuarioAndData(userCode, date)).thenReturn(list); - ResponseEntity response = this.expenseController.getExpenseByUserCodeAndDate(userCode, date); + Mockito.when(this.expenseService.findExpensesByUserCodeAndDate(userCode, date)).thenReturn(list); + + ResponseEntity response = this.expenseController.getExpenseByUserCodeAndDate(userCode, date); Assert.assertEquals(response.getStatusCode(), HttpStatus.OK); - Assert.assertEquals(response.getBody().getStatusCode(), HttpStatus.OK.value()); - Assert.assertEquals(response.getBody().getData(), list); + Assert.assertNotNull(response.getBody()); } @Test - public void getExpenseByUserCodeAndDate_WithDataIncorrectTest() throws ParseException { - long userCode = CODIGO_USUARIO; + public void getExpenseByUserCodeAndDate_WithDataIncorrectTest() throws ExpenseException { + long userCode = USER_CODE; String date = "121251"; - Mockito.when(this.expenseService.findExpensesByCodigoUsuarioAndData(userCode, date)) - .thenThrow(ParseException.class); - ResponseEntity response = this.expenseController.getExpenseByUserCodeAndDate(userCode, date); + Mockito.when(this.expenseService.findExpensesByUserCodeAndDate(userCode, date)) + .thenThrow(new ExpenseException(HttpStatus.BAD_REQUEST, EXPENSE_BADLY_FORMATTED_DATE)); + ResponseEntity response = this.expenseController.getExpenseByUserCodeAndDate(userCode, date); Assert.assertEquals(response.getStatusCode(), HttpStatus.BAD_REQUEST); - Assert.assertEquals(response.getBody().getStatusCode(), HttpStatus.BAD_REQUEST.value()); + Assert.assertNotNull(response.getBody()); } @Test public void updateExpenseTest() { - String id = this.uuid; - ResponseEntity response = this.expenseController.updateExpense(id, expense); + String id = expense.getId(); + ResponseEntity response = this.expenseController.updateExpense(id, expense); Assert.assertEquals(response.getStatusCode(), HttpStatus.OK); - Assert.assertEquals(response.getBody().getStatusCode(), HttpStatus.OK.value()); + Assert.assertNotNull(response.getBody()); + } + + @Test + public void updateExpense_ExpenseWithoutIdTest() throws ExpenseException { + String id = expense.getId(); + Mockito.doThrow(new ExpenseException(HttpStatus.NOT_FOUND, EXPENSE_NOT_FOUND)) + .when(this.expenseService).updateExpense(id, expense); + ResponseEntity response = this.expenseController.updateExpense(id, expense); + Assert.assertEquals(response.getStatusCode(), HttpStatus.NOT_FOUND); + Assert.assertNotNull(response.getBody()); } } diff --git a/src/test/java/com/santander/interview/domain/CategoryTest.java b/src/test/java/com/santander/interview/domain/CategoryTest.java index c39135b1..622aefd2 100644 --- a/src/test/java/com/santander/interview/domain/CategoryTest.java +++ b/src/test/java/com/santander/interview/domain/CategoryTest.java @@ -12,7 +12,7 @@ public class CategoryTest { private static final String DETAIL = "teste"; @Test - public void CategoryTest() { + public void categoryTest() { Category category = new Category(); category.setId(ID); category.setDetail(DETAIL); @@ -22,9 +22,8 @@ public void CategoryTest() { } @Test - public void CategoryConstructorWithParamsTest() { - Category category = new Category(ID, DETAIL); - Assert.assertEquals(category.getId(), ID); + public void categoryConstructorWithParamsTest() { + Category category = new Category(DETAIL); Assert.assertEquals(category.getDetail(), DETAIL); } } diff --git a/src/test/java/com/santander/interview/domain/ExpenseTest.java b/src/test/java/com/santander/interview/domain/ExpenseTest.java index 5837f4fc..55606703 100644 --- a/src/test/java/com/santander/interview/domain/ExpenseTest.java +++ b/src/test/java/com/santander/interview/domain/ExpenseTest.java @@ -10,34 +10,54 @@ @RunWith(MockitoJUnitRunner.class) public class ExpenseTest { private static final String ID = "123"; - private static final String DESCRICAO = "descricao"; - private static final double VALOR = 124.2; - private static final long CODIGO_USUARIO = 142; - private static final Date DATA = new Date(); - private static final String CATEGORY_ID = "32"; + private static final String DESCRIPTION = "descricao"; + private static final double VALUE = 124.2; + private static final long USER_CODE = 142; + private static final Date DATE = new Date(); private static final String CATEGORY_DETAIL = "teste"; - private static final Category CATEGORY = new Category(CATEGORY_ID, CATEGORY_DETAIL); + private static final Category CATEGORY = new Category(CATEGORY_DETAIL); @Test - public void ExpenseTest() { + public void expenseEmptyConstructorTest() { String expectedToString = String.format( "Expense[id=%s, descricao=%s, valor=%f, codigoUsuario=%d, data=%s]", - ID, DESCRICAO, VALOR, CODIGO_USUARIO, DATA + ID, DESCRIPTION, VALUE, USER_CODE, DATE ); Expense expense = new Expense(); expense.setId(ID); expense.setCategory(CATEGORY); - expense.setDescricao(DESCRICAO); - expense.setValor(VALOR); - expense.setCodigoUsuario(CODIGO_USUARIO); - expense.setData(DATA); + expense.setDescription(DESCRIPTION); + expense.setValue(VALUE); + expense.setUserCode(USER_CODE); + expense.setDate(DATE); Assert.assertEquals(expense.getId(), ID); Assert.assertEquals(expense.getCategory(), CATEGORY); - Assert.assertEquals(expense.getCodigoUsuario(), CODIGO_USUARIO); - Assert.assertEquals(expense.getData(), DATA); - Assert.assertEquals(expense.getDescricao(), DESCRICAO); - Assert.assertTrue(expense.getValor() == VALOR); + Assert.assertEquals(expense.getUserCode(), USER_CODE); + Assert.assertEquals(expense.getDate(), DATE); + Assert.assertEquals(expense.getDescription(), DESCRIPTION); + Assert.assertTrue(expense.getValue() == VALUE); Assert.assertEquals(expense.toString(), expectedToString); } + + @Test + public void expenseConstrutorWithAllAttrTest() { + Expense expense = new Expense(DESCRIPTION, VALUE, USER_CODE, DATE, CATEGORY); + + Assert.assertEquals(expense.getCategory(), CATEGORY); + Assert.assertEquals(expense.getUserCode(), USER_CODE); + Assert.assertEquals(expense.getDate(), DATE); + Assert.assertEquals(expense.getDescription(), DESCRIPTION); + Assert.assertTrue(expense.getValue() == VALUE); + } + + @Test + public void expenseConstrutorWithoutCategoryAttrTest() { + Expense expense = new Expense(DESCRIPTION, VALUE, USER_CODE, DATE); + + Assert.assertEquals(expense.getUserCode(), USER_CODE); + Assert.assertEquals(expense.getDate(), DATE); + Assert.assertEquals(expense.getDescription(), DESCRIPTION); + Assert.assertTrue(expense.getValue() == VALUE); + } } diff --git a/src/test/java/com/santander/interview/domain/ResponseErrorTest.java b/src/test/java/com/santander/interview/domain/ResponseErrorTest.java new file mode 100644 index 00000000..61113753 --- /dev/null +++ b/src/test/java/com/santander/interview/domain/ResponseErrorTest.java @@ -0,0 +1,24 @@ +package com.santander.interview.domain; + +import org.junit.Assert; +import org.junit.Test; + +public class ResponseErrorTest { + private static final long STATUS_CODE = 12; + private static final String USER_MESSAGE = "userMessage"; + private static final String INTERNAL_MESSAGE = "internalMessage"; + private static final int INTERNAL_CODE = 1; + private static final int INTERNAL_CODE_2 = 3; + + @Test + public void responseErrorTest(){ + ResponseError responseError = new ResponseError(STATUS_CODE, USER_MESSAGE, INTERNAL_MESSAGE, INTERNAL_CODE); + Assert.assertEquals(responseError.getStatusCode(), STATUS_CODE); + Assert.assertEquals(responseError.getUserMessage(), USER_MESSAGE); + Assert.assertEquals(responseError.getInternalMessage(), INTERNAL_MESSAGE); + Assert.assertEquals(responseError.getInternalCode(), INTERNAL_CODE); + + responseError.setInternalCode(INTERNAL_CODE_2); + Assert.assertEquals(responseError.getInternalCode(), INTERNAL_CODE_2); + } +} diff --git a/src/test/java/com/santander/interview/domain/ResponseObjectTest.java b/src/test/java/com/santander/interview/domain/ResponseObjectTest.java index 443253cb..91a86897 100644 --- a/src/test/java/com/santander/interview/domain/ResponseObjectTest.java +++ b/src/test/java/com/santander/interview/domain/ResponseObjectTest.java @@ -6,28 +6,32 @@ import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) -public class ResponsePostTest { +public class ResponseObjectTest { private static final long STATUS_CODE = 123; - private static final String MESSAGE = "Teste"; + private static final String USER_MESSAGE = "userMessage"; + private static final String INTERNAL_MESSAGE = "internalMessage"; private static final Object DATA = new Object(); @Test - public void ResponseTest() { - ResponsePost responsePost = new ResponsePost(); - responsePost.setData(DATA); - responsePost.setUserMessage(MESSAGE); - responsePost.setStatusCode(STATUS_CODE); + public void responseEmptyConstTest() { + ResponseObject responseObject = new ResponseObject(); + responseObject.setData(DATA); + responseObject.setUserMessage(USER_MESSAGE); + responseObject.setInternalMessage(INTERNAL_MESSAGE); + responseObject.setStatusCode(STATUS_CODE); - Assert.assertEquals(responsePost.getData(), DATA); - Assert.assertEquals(responsePost.getUserMessage(), MESSAGE); - Assert.assertEquals(responsePost.getStatusCode(), STATUS_CODE); + Assert.assertEquals(responseObject.getData(), DATA); + Assert.assertEquals(responseObject.getUserMessage(), USER_MESSAGE); + Assert.assertEquals(responseObject.getInternalMessage(), INTERNAL_MESSAGE); + Assert.assertEquals(responseObject.getStatusCode(), STATUS_CODE); } @Test - public void ResponseConstructorWithParamsTest() { - ResponsePost responsePost = new ResponsePost(STATUS_CODE, MESSAGE, DATA); - Assert.assertEquals(responsePost.getStatusCode(), STATUS_CODE); - Assert.assertEquals(responsePost.getUserMessage(), MESSAGE); - Assert.assertEquals(responsePost.getData(), DATA); + public void responseConstructorWithParamsTest() { + ResponseObject responseObject = new ResponseObject(STATUS_CODE, USER_MESSAGE, INTERNAL_MESSAGE, DATA); + Assert.assertEquals(responseObject.getStatusCode(), STATUS_CODE); + Assert.assertEquals(responseObject.getUserMessage(), USER_MESSAGE); + Assert.assertEquals(responseObject.getInternalMessage(), INTERNAL_MESSAGE); + Assert.assertEquals(responseObject.getData(), DATA); } } diff --git a/src/test/java/com/santander/interview/domain/ResponseTest.java b/src/test/java/com/santander/interview/domain/ResponseTest.java new file mode 100644 index 00000000..79e8945a --- /dev/null +++ b/src/test/java/com/santander/interview/domain/ResponseTest.java @@ -0,0 +1,31 @@ +package com.santander.interview.domain; + +import org.junit.Assert; +import org.junit.Test; + +public class ResponseTest { + private static final long STATUS_CODE = 12; + private static final String USER_MESSAGE = "userMessage"; + private static final String INTERNAL_MESSAGE = "internalMessage"; + + @Test + public void responseEmptyConstructorTest() { + Response response = new Response(); + response.setStatusCode(STATUS_CODE); + response.setInternalMessage(INTERNAL_MESSAGE); + response.setUserMessage(USER_MESSAGE); + + Assert.assertEquals(response.getStatusCode(), STATUS_CODE); + Assert.assertEquals(response.getUserMessage(), USER_MESSAGE); + Assert.assertEquals(response.getInternalMessage(), INTERNAL_MESSAGE); + } + + @Test + public void responseWithAllConstructorAttrTest() { + Response response = new Response(STATUS_CODE, USER_MESSAGE, INTERNAL_MESSAGE); + + Assert.assertEquals(response.getStatusCode(), STATUS_CODE); + Assert.assertEquals(response.getUserMessage(), USER_MESSAGE); + Assert.assertEquals(response.getInternalMessage(), INTERNAL_MESSAGE); + } +} diff --git a/src/test/java/com/santander/interview/enums/ResponseMessageEnumTest.java b/src/test/java/com/santander/interview/enums/ResponseMessageEnumTest.java new file mode 100644 index 00000000..1f01e500 --- /dev/null +++ b/src/test/java/com/santander/interview/enums/ResponseMessageEnumTest.java @@ -0,0 +1,15 @@ +package com.santander.interview.enums; + +import org.junit.Assert; +import org.junit.Test; + +import static com.santander.interview.enums.ResponseMessageEnum.*; + +public class ResponseMessageEnumTest { + @Test + public void responseMessageEnumTest() { + Assert.assertNotNull(UNKNOWN_ERROR.getUserMessage()); + Assert.assertNotNull(UNKNOWN_ERROR.getInternalMessage()); + Assert.assertNotNull(UNKNOWN_ERROR.getCode()); + } +} diff --git a/src/test/java/com/santander/interview/exception/ExpenseExceptionTest.java b/src/test/java/com/santander/interview/exception/ExpenseExceptionTest.java new file mode 100644 index 00000000..a68e496d --- /dev/null +++ b/src/test/java/com/santander/interview/exception/ExpenseExceptionTest.java @@ -0,0 +1,41 @@ +package com.santander.interview.exception; + +import static com.santander.interview.enums.ResponseMessageEnum.*; + +import org.junit.Assert; +import org.junit.Test; +import org.springframework.http.HttpStatus; + +public class ExpenseExceptionTest { + private static final String MESSAGE = "teste"; + + @Test + public void expenseExceptionConstructor1Test() { + ExpenseException ee = new ExpenseException(); + Assert.assertEquals(ee.getStatusCode(), HttpStatus.INTERNAL_SERVER_ERROR); + Assert.assertEquals(ee.getResponseMessageEnum(), UNKNOWN_ERROR); + } + + @Test + public void expenseExceptionConstructor2Test() { + ExpenseException ee = new ExpenseException(HttpStatus.OK, MESSAGE, EXPENSE_BADLY_FORMATTED_DATE); + Assert.assertEquals(ee.getStatusCode(), HttpStatus.OK); + Assert.assertEquals(ee.getResponseMessageEnum(), EXPENSE_BADLY_FORMATTED_DATE); + Assert.assertEquals(ee.getMessage(), MESSAGE); + } + + @Test + public void expenseExceptionConstructor3Test() { + ExpenseException ee = new ExpenseException(HttpStatus.OK, EXPENSE_BADLY_FORMATTED_DATE); + Assert.assertEquals(ee.getStatusCode(), HttpStatus.OK); + Assert.assertEquals(ee.getResponseMessageEnum(), EXPENSE_BADLY_FORMATTED_DATE); + + ee.setMessage(MESSAGE); + ee.setResponseMessageEnum(UNKNOWN_ERROR); + ee.setStatusCode(HttpStatus.BAD_GATEWAY); + + Assert.assertEquals(ee.getMessage(), MESSAGE); + Assert.assertEquals(ee.getResponseMessageEnum(), UNKNOWN_ERROR); + Assert.assertEquals(ee.getStatusCode(), HttpStatus.BAD_GATEWAY); + } +} diff --git a/src/test/java/com/santander/interview/service/CategoryServiceTest.java b/src/test/java/com/santander/interview/service/CategoryServiceTest.java index 45c875de..4221dd63 100644 --- a/src/test/java/com/santander/interview/service/CategoryServiceTest.java +++ b/src/test/java/com/santander/interview/service/CategoryServiceTest.java @@ -18,6 +18,8 @@ @RunWith(MockitoJUnitRunner.class) public class CategoryServiceTest { + private static final String CATEGORY_DETAIL = "Detalhe"; + @InjectMocks CategoryServiceImpl categoryService = new CategoryServiceImpl(); @@ -30,7 +32,7 @@ public class CategoryServiceTest { @Before public void setUp() { MockitoAnnotations.initMocks(this); - this.category = new Category("123", "Detalhe"); + this.category = new Category(CATEGORY_DETAIL); this.categoriesResult = new ArrayList<>(); this.categoriesResult.add(this.category); } @@ -50,8 +52,8 @@ public void saveCategoryTest() { public void searchCategoryByDetailPrefixTest() { Mockito.when(categoryRepository.findByDetailLike(this.category.getDetail())).thenReturn(this.categoriesResult); - String prefix = this.category.getDetail(); - List result = this.categoryService.searchCategoryByDetailPrefix(prefix); + String substring = this.category.getDetail(); + List result = this.categoryService.searchCategoryByDetailSubstring(substring); Assert.assertEquals(result, categoriesResult); } } diff --git a/src/test/java/com/santander/interview/service/ExpenseServiceTest.java b/src/test/java/com/santander/interview/service/ExpenseServiceTest.java index f9a59c1e..be7eda63 100644 --- a/src/test/java/com/santander/interview/service/ExpenseServiceTest.java +++ b/src/test/java/com/santander/interview/service/ExpenseServiceTest.java @@ -2,6 +2,8 @@ import com.santander.interview.domain.Category; import com.santander.interview.domain.Expense; +import com.santander.interview.enums.ResponseMessageEnum; +import com.santander.interview.exception.ExpenseException; import com.santander.interview.repository.CategoryRepository; import com.santander.interview.repository.ExpenseRepository; import com.santander.interview.service.impl.ExpenseServiceImpl; @@ -12,19 +14,20 @@ import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; +import org.springframework.http.HttpStatus; -import java.text.ParseException; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.UUID; +import java.util.*; + +import static com.santander.interview.enums.ResponseMessageEnum.EXPENSE_BADLY_FORMATTED_DATE; +import static com.santander.interview.enums.ResponseMessageEnum.EXPENSE_NOT_FOUND; public class ExpenseServiceTest { - private static final long CODIGO_USUARIO = 1232; - private static final Date DATA = new Date(); - private static final String DATA_STRING = "01102019"; - private static final double VALOR = 124.2; - private static final String DESCRICAO = "Teste"; + private static final long USER_CODE = 1232; + private static final Date DATE = new Date(); + private static final String DATE_STRING = "01102019"; + private static final String DATE_STRING_INVALID = "011"; + private static final double VALUE = 124.2; + private static final String DESCRIPTION = "Teste"; private static final String DETAIL = "Detalhes"; @InjectMocks @@ -44,39 +47,27 @@ public void init() { @Test public void addNewExpenseWithCategoryNullTest() { List expenses = new ArrayList<>(); - Expense expense = new Expense(); - expense.setCodigoUsuario(CODIGO_USUARIO); - expense.setData(DATA); - expense.setValor(VALOR); - expense.setDescricao(DESCRICAO); + Expense expense = new Expense(DESCRIPTION, VALUE, USER_CODE, DATE); expenses.add(expense); - Mockito.when(this.expenseRepository.findByCodigoUsuario(CODIGO_USUARIO)) - .thenReturn(expenses); + Mockito.when(this.expenseRepository.findByUserCode(USER_CODE)).thenReturn(expenses); this.expenseService.addNewExpense(expense); - List result = this.expenseService.findExpensesByCodigoUsuario(CODIGO_USUARIO); + List result = this.expenseService.findExpensesByUserCode(USER_CODE); Assert.assertEquals(result, expenses); } @Test public void addNewExpenseWithCategoryTest() { boolean isOk = true; - Expense expense = new Expense(); + Expense expense = new Expense(DESCRIPTION, VALUE, USER_CODE, DATE); Category category = new Category(); List categories = new ArrayList<>(); - String uuid = UUID.randomUUID().toString(); - - expense.setCodigoUsuario(CODIGO_USUARIO); - expense.setData(DATA); - expense.setValor(VALOR); - expense.setDescricao(DESCRICAO); category.setDetail(DETAIL); expense.setCategory(category); - categories.add(new Category(uuid, DETAIL)); + categories.add(new Category(DETAIL)); - Mockito.when(categoryRepository.findByDetail(category.getDetail())) - .thenReturn(categories); + Mockito.when(categoryRepository.findByDetail(category.getDetail())).thenReturn(categories); try { this.expenseService.addNewExpense(expense); } catch (Exception e) { @@ -87,23 +78,40 @@ public void addNewExpenseWithCategoryTest() { } @Test - public void findExpensesByCodigoUsuarioAndDataTest() throws ParseException { + public void findExpensesByCodigoUsuarioAndDataTest() throws ExpenseException { List expenses = new ArrayList<>(); - Expense expense = new Expense(); - expense.setCodigoUsuario(CODIGO_USUARIO); - expense.setData(DATA); - expense.setValor(VALOR); - expense.setDescricao(DESCRICAO); + Expense expense = new Expense(DESCRIPTION, VALUE, USER_CODE, DATE); + expenses.add(expense); + + Assert.assertNotNull(this.expenseService.findExpensesByUserCodeAndDate(USER_CODE, DATE_STRING)); + } + + @Test + public void findExpensesByCodigoUsuarioAndData_InvalidDataTest() { + List expenses = new ArrayList<>(); + ResponseMessageEnum responseMessageEnumExpected = EXPENSE_BADLY_FORMATTED_DATE; + HttpStatus httpStatusExpected = HttpStatus.BAD_REQUEST; + Expense expense = new Expense(DESCRIPTION, VALUE, USER_CODE, DATE); expenses.add(expense); - Assert.assertNotNull(this.expenseService.findExpensesByCodigoUsuarioAndData(CODIGO_USUARIO, DATA_STRING)); + try { + this.expenseService.findExpensesByUserCodeAndDate(USER_CODE, DATE_STRING_INVALID); + } catch (ExpenseException ee) { + ResponseMessageEnum responseMessageEnum = ee.getResponseMessageEnum(); + Assert.assertEquals(responseMessageEnum.getInternalMessage(), responseMessageEnumExpected.getInternalMessage()); + Assert.assertEquals(responseMessageEnum.getUserMessage(), responseMessageEnumExpected.getUserMessage()); + Assert.assertEquals(ee.getStatusCode(), httpStatusExpected); + } } @Test - public void updateExpenseTest() { + public void updateExpense_FoundExpenseTest() throws ExpenseException { boolean isOk = true; String uuid = UUID.randomUUID().toString(); Expense expense = new Expense(); + Optional optionalExpense = Optional.of(expense); + Mockito.when(this.expenseRepository.findById(uuid)).thenReturn(optionalExpense); + try { this.expenseService.updateExpense(uuid, expense); } catch (Exception e) { @@ -113,5 +121,22 @@ public void updateExpenseTest() { Assert.assertTrue(isOk); } + @Test + public void updateExpense_NotFoundExpenseTest() { + HttpStatus httpStatusExpected = HttpStatus.NOT_FOUND; + ResponseMessageEnum responseMessageEnum = EXPENSE_NOT_FOUND; + String uuid = UUID.randomUUID().toString(); + Expense expense = new Expense(); + Optional optionalExpense = Optional.empty(); + Mockito.when(this.expenseRepository.findById(uuid)).thenReturn(optionalExpense); + + try { + this.expenseService.updateExpense(uuid, expense); + } catch (ExpenseException ee) { + Assert.assertEquals(ee.getStatusCode(), httpStatusExpected); + Assert.assertEquals(ee.getResponseMessageEnum().getUserMessage(), responseMessageEnum.getUserMessage()); + } + + } } diff --git a/src/test/java/com/santander/interview/utils/ExpenseManagementUtilsTest.java b/src/test/java/com/santander/interview/utils/ExpenseManagementUtilsTest.java new file mode 100644 index 00000000..7b6780e0 --- /dev/null +++ b/src/test/java/com/santander/interview/utils/ExpenseManagementUtilsTest.java @@ -0,0 +1,58 @@ +package com.santander.interview.utils; + +import static com.santander.interview.enums.ResponseMessageEnum.*; + +import com.santander.interview.domain.Response; +import com.santander.interview.domain.ResponseError; +import com.santander.interview.domain.ResponseObject; +import com.santander.interview.enums.ResponseMessageEnum; +import org.junit.Assert; +import org.junit.Test; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; + +public class ExpenseManagementUtilsTest { + private static final ResponseMessageEnum RESPONSE_MESSAGE_ENUM = ADD_EXPENSE_SUCCESS; + private static final HttpStatus HTTP_STATUS = HttpStatus.OK; + private static final Object DATA = new Object(); + + @Test + public void convertStringToInt_InvalidStringTest() { + int intValue = ExpenseManagementUtils.convertStringtoInt(10, "asd"); + Assert.assertEquals(intValue, 10); + } + + @Test + public void convertStringToInt_ValidStringTest() { + int intValue = ExpenseManagementUtils.convertStringtoInt(10, "123"); + Assert.assertEquals(intValue, 123); + } + + @Test + public void responseWithDataTest() { + ResponseEntity response = ExpenseManagementUtils + .responseWithData(RESPONSE_MESSAGE_ENUM, HTTP_STATUS, DATA); + Assert.assertEquals(response.getStatusCode(), HTTP_STATUS); + Assert.assertEquals(response.getBody().getInternalMessage(), RESPONSE_MESSAGE_ENUM.getInternalMessage()); + Assert.assertEquals(response.getBody().getUserMessage(), RESPONSE_MESSAGE_ENUM.getUserMessage()); + Assert.assertEquals(response.getBody().getData(), DATA); + } + + @Test + public void responseWithoutData() { + ResponseEntity response = ExpenseManagementUtils + .responseWithoutData(RESPONSE_MESSAGE_ENUM, HTTP_STATUS); + Assert.assertEquals(response.getStatusCode(), HTTP_STATUS); + Assert.assertEquals(response.getBody().getInternalMessage(), RESPONSE_MESSAGE_ENUM.getInternalMessage()); + Assert.assertEquals(response.getBody().getUserMessage(), RESPONSE_MESSAGE_ENUM.getUserMessage()); + } + + @Test + public void responseWithError() { + ResponseEntity response = ExpenseManagementUtils + .responseWithError(RESPONSE_MESSAGE_ENUM, HTTP_STATUS); + Assert.assertEquals(response.getBody().getInternalMessage(), RESPONSE_MESSAGE_ENUM.getInternalMessage()); + Assert.assertEquals(response.getBody().getUserMessage(), RESPONSE_MESSAGE_ENUM.getUserMessage()); + } + +} From 3223d47f223d00a3493308fb2012d52e1e1819a5 Mon Sep 17 00:00:00 2001 From: Thiago Sato Date: Mon, 9 Sep 2019 02:54:33 -0300 Subject: [PATCH 25/30] =?UTF-8?q?Fixing:=20estava=20lan=C3=A7ando=20uma=20?= =?UTF-8?q?exce=C3=A7=C3=A3o=20devido=20ao=20n=C3=A3o=20tratamento=20do=20?= =?UTF-8?q?caso=20em=20a=20lista=20est=C3=A1=20vazia?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../santander/interview/service/impl/ExpenseServiceImpl.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/santander/interview/service/impl/ExpenseServiceImpl.java b/src/main/java/com/santander/interview/service/impl/ExpenseServiceImpl.java index 83e636dd..ef196df9 100644 --- a/src/main/java/com/santander/interview/service/impl/ExpenseServiceImpl.java +++ b/src/main/java/com/santander/interview/service/impl/ExpenseServiceImpl.java @@ -29,7 +29,9 @@ public class ExpenseServiceImpl implements ExpenseService { private Category automaticCategorization(Expense expense) { Category category = expense.getCategory(); if(category != null && category.getDetail() != null) { - return categoryRepository.findByDetail(category.getDetail()).get(0); + List list = categoryRepository.findByDetail(category.getDetail()); + if (list.size() > 0) + return list.get(0); } return null; From 2b9ac87a662f4a0d567aa54bced8b1df093cc422 Mon Sep 17 00:00:00 2001 From: Thiago Sato Date: Mon, 9 Sep 2019 03:20:59 -0300 Subject: [PATCH 26/30] README: procedimentos para executar o projeto --- InterviewSantanderJava-soapui-project.xml | 2 +- README.md | 83 ++++--------------- .../service/impl/ExpenseServiceImpl.java | 2 + 3 files changed, 20 insertions(+), 67 deletions(-) diff --git a/InterviewSantanderJava-soapui-project.xml b/InterviewSantanderJava-soapui-project.xml index 051cc9d2..f92393bd 100644 --- a/InterviewSantanderJava-soapui-project.xml +++ b/InterviewSantanderJava-soapui-project.xml @@ -1,5 +1,5 @@ -http://localhost:8080application/json;charset=UTF-8200exp:Responseapplication/json<xml-fragment/>http://localhost:8080{ "description": "asd1231", "value": 12783.23, "userCode": 12283, "date": "2019-09-02T20:21:42.026Z" }http://localhost/expense-management/expensesadminmyadminBasicBasicGlobal HTTP SettingsuserCodeuserCodeTEMPLATEuserCodeapplication/json;charset=UTF-8401ns:Faultapplication/json;charset=UTF-8200ns:Response<xml-fragment/>http://localhost:8080http://localhost/expense-management/expense/userCode/12283clienttheclientBasicBasicGlobal HTTP SettingsuserCodeuserCodeuserCodeTEMPLATEuserCodedatedateTEMPLATEdateapplication/json;charset=UTF-8400ns:Faultapplication/json;charset=UTF-8200ns:Response<xml-fragment/>http://localhost:8080http://localhost/expense-management/expense/userCode/12283/date/02092019clienttheclientBasicBasicGlobal HTTP Settings +http://localhost:8080application/json;charset=UTF-8200exp:Responseapplication/json<xml-fragment/>http://localhost:8080{ "description": "asd1231", "value": 12783.23, "userCode": 12283, "date": "2019-09-02T20:21:42.026Z" }http://localhost/expense-management/expensesadminmyadminBasicBasicGlobal HTTP SettingsuserCodeuserCodeTEMPLATEuserCodeapplication/json;charset=UTF-8401ns:Faultapplication/json;charset=UTF-8200ns:Response<xml-fragment/>http://localhost:8080http://localhost/expense-management/expense/userCode/12283clienttheclientBasicBasicGlobal HTTP SettingsuserCodeuserCodeuserCodeTEMPLATEuserCodedatedateTEMPLATEdateapplication/json;charset=UTF-8400ns:Faultapplication/json;charset=UTF-8200ns:Response<xml-fragment/>http://localhost:8080http://localhost/expense-management/expense/userCode/12283/date/02092019clienttheclientBasicBasicGlobal HTTP Settings userCodedateididTEMPLATEidapplication/jsonapplication/json;charset=UTF-8200afa7:Response0data0data<xml-fragment/>http://localhost:8080{ "description": "2134asd", "value": 12351.23, "userCode": 1227883, "date": "2019-09-01T17:21:42.026Z" }http://localhost/expense-management/expense/6655afa7-7679-4098-9927-6c975f67fbd5clienttheclientBasicBasicGlobal HTTP Settingsidapplication/jsonapplication/json;charset=UTF-8200cat:Response<xml-fragment/>http://localhost:8080{ "detail": "teste" }http://localhost/expense-management/categoriesclienttheclientBasicBasicGlobal HTTP SettingsdetailPrefixdetailPrefixTEMPLATEdetailPrefixapplication/json;charset=UTF-8200tes:Response<xml-fragment/>http://localhost:8080http://localhost/expense-management/category/detail/testeclienttheclientBasicBasicGlobal HTTP SettingsdetailPrefixPARALLELLfalse1000250truetrue-1100000COUNTSimple00.5100true500interviewadminmyadmin \ No newline at end of file diff --git a/README.md b/README.md index 15d8f685..050d2589 100644 --- a/README.md +++ b/README.md @@ -1,76 +1,27 @@ -# Show me the code ### # DESAFIO: API REST para Gestão de Gastos! +*** + +#### Configurando o Mongo ``` -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. - -*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. +docker pull mongo +docker run -d -p 27018:27017 mongo ``` + +#### Executando o projeto ``` -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 +mvn install +mvn clean spring-boot:run ``` -### # 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! +As variáveis de ambiente estão configuradas no arquivo *application.properties*. +``` +mongo.host=localhost +mongo.port=27018 +mongo.database=app1 +thread.async_core_pool_size=5 +thread.async_max_pool_size=50 +``` \ No newline at end of file diff --git a/src/main/java/com/santander/interview/service/impl/ExpenseServiceImpl.java b/src/main/java/com/santander/interview/service/impl/ExpenseServiceImpl.java index ef196df9..5fb72626 100644 --- a/src/main/java/com/santander/interview/service/impl/ExpenseServiceImpl.java +++ b/src/main/java/com/santander/interview/service/impl/ExpenseServiceImpl.java @@ -10,6 +10,7 @@ import com.santander.interview.service.ExpenseService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; +import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import java.text.ParseException; @@ -37,6 +38,7 @@ private Category automaticCategorization(Expense expense) { return null; } +// @Async @Override public void addNewExpense(Expense expense) { Expense newExpense = new Expense( From ba47e575bc963cf8c28f9da6a9ce0c56bdfa603b Mon Sep 17 00:00:00 2001 From: Thiago Sato Date: Mon, 9 Sep 2019 07:15:33 -0300 Subject: [PATCH 27/30] README: citando o arquivo XML do SOAPUI para realizar os requests --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 050d2589..184c2169 100644 --- a/README.md +++ b/README.md @@ -24,4 +24,6 @@ mongo.port=27018 mongo.database=app1 thread.async_core_pool_size=5 thread.async_max_pool_size=50 -``` \ No newline at end of file +``` + +O arquivo *InterviewSantanderJava-soapui-project.xml* contém exemplos de chamadas para este projeto. \ No newline at end of file From 82004dbfd20ea83fe0374f237983ec18b626a7d8 Mon Sep 17 00:00:00 2001 From: Thiago Sato Date: Fri, 13 Sep 2019 00:06:26 -0300 Subject: [PATCH 28/30] =?UTF-8?q?Refatorando=20o=20c=C3=B3digo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ExpenseController.java | 4 +- .../santander/interview/domain/Category.java | 17 +++++ .../santander/interview/domain/Expense.java | 70 ++++++++++++++++++- .../interview/service/CategoryService.java | 2 +- .../interview/service/ExpenseService.java | 9 ++- .../service/impl/CategoryServiceImpl.java | 10 ++- .../service/impl/ExpenseServiceImpl.java | 63 +++-------------- 7 files changed, 106 insertions(+), 69 deletions(-) diff --git a/src/main/java/com/santander/interview/controller/ExpenseController.java b/src/main/java/com/santander/interview/controller/ExpenseController.java index 03d77e32..3cb431f2 100644 --- a/src/main/java/com/santander/interview/controller/ExpenseController.java +++ b/src/main/java/com/santander/interview/controller/ExpenseController.java @@ -39,7 +39,7 @@ public ResponseEntity addExpense( public ResponseEntity getExpenseByUserCode( @ApiParam(value = "Código do cliente", required = true) @PathVariable long userCode ) { - List expensesByUserCode = this.expenseService.findExpensesByUserCode(userCode); + List expensesByUserCode = this.expenseService.searchExpensesByUserCode(userCode); return ExpenseManagementUtils.responseWithData(SEARCH_EXPENSE_BY_USER_CODE_SUCCESS, HttpStatus.OK, expensesByUserCode); @@ -53,7 +53,7 @@ public ResponseEntity getExpenseByUserCodeAndDate( ) { List expensesByUserCodeAndDate; try { - expensesByUserCodeAndDate = this.expenseService.findExpensesByUserCodeAndDate(userCode, date); + expensesByUserCodeAndDate = this.expenseService.searchExpensesByUserCodeAndDate(userCode, date); return ExpenseManagementUtils.responseWithData(SEARCH_EXPENSE_BY_USER_CODE_AND_DATE_SUCCESS, HttpStatus.OK, expensesByUserCodeAndDate); } catch (ExpenseException ee){ diff --git a/src/main/java/com/santander/interview/domain/Category.java b/src/main/java/com/santander/interview/domain/Category.java index a4cdf0b5..933231ac 100644 --- a/src/main/java/com/santander/interview/domain/Category.java +++ b/src/main/java/com/santander/interview/domain/Category.java @@ -1,14 +1,22 @@ package com.santander.interview.domain; +import com.santander.interview.repository.CategoryRepository; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.annotation.Id; +import org.springframework.stereotype.Component; +import java.util.List; import java.util.UUID; +@Component public class Category { @Id private String id; private String detail; + @Autowired + CategoryRepository categoryRepository; + private String generateID() { return UUID.randomUUID().toString(); } public Category() { } @@ -26,4 +34,13 @@ public Category(String detail) { public void setDetail(String detail) { this.detail = detail; } + public void save(Category category) { + Category newCategory = new Category(category.getDetail()); + this.categoryRepository.save(newCategory); + } + + public List searchByDetailSubstring(String detailSubstring) { + return this.categoryRepository.findByDetailLike(detailSubstring); + } + } diff --git a/src/main/java/com/santander/interview/domain/Expense.java b/src/main/java/com/santander/interview/domain/Expense.java index 62e480a5..a17c288b 100644 --- a/src/main/java/com/santander/interview/domain/Expense.java +++ b/src/main/java/com/santander/interview/domain/Expense.java @@ -1,10 +1,17 @@ package com.santander.interview.domain; +import com.santander.interview.repository.CategoryRepository; +import com.santander.interview.repository.ExpenseRepository; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.annotation.Id; +import org.springframework.stereotype.Component; -import java.util.Date; -import java.util.UUID; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.*; + +@Component public class Expense { @Id private String id; @@ -14,6 +21,12 @@ public class Expense { private Date date; private Category category; + @Autowired + ExpenseRepository expenseRepository; + + @Autowired + CategoryRepository categoryRepository; + private String generateID() { return UUID.randomUUID().toString(); } public Expense() { this.id = this.generateID(); } @@ -59,6 +72,59 @@ public Expense(String description, double value, long userCode, Date date, Categ public void setCategory(Category category) { this.category = category; } + private Category automaticCategorization(Expense expense) { + Category category = expense.getCategory(); + if(category != null && category.getDetail() != null) { + List list = categoryRepository.findByDetail(category.getDetail()); + if (list.size() > 0) + return list.get(0); + } + return null; + } + + public void add(Expense expense) { + Expense newExpense = new Expense( + expense.getDescription(), + expense.getValue(), + expense.getUserCode(), + expense.getDate() + ); + newExpense.setCategory(this.automaticCategorization(expense)); + this.expenseRepository.save(newExpense); + } + + public List searchByUserCode(long userCode) { + List expenses = expenseRepository.findByUserCode(userCode); + Collections.sort(expenses, new Comparator() { + @Override + public int compare(Expense expense1, Expense expense2) { + return expense2.getDate().compareTo(expense1.getDate()); + } + }); + return expenses; + } + + public List searchByUserCodeAndDate(long userCode, String date) throws ParseException { + long oneDayInMilliseconds = 1000 * 60 * 60 * 24; + + Date startDate = new SimpleDateFormat("ddMMyyyy").parse(date); + Date endDate = new Date(startDate.getTime() + oneDayInMilliseconds); + return this.expenseRepository.findByUserCodeAndDateBetween(userCode, startDate, endDate); + + } + + public boolean update(String id, Expense expense) { + Optional existExpense = this.expenseRepository.findById(id); + if(existExpense.isPresent()) { + Expense expenseFound = existExpense.get(); + expense.setId(expenseFound.getId()); + expense.setCategory(this.automaticCategorization(expense)); + this.expenseRepository.save(expense); + return true; + } + return false; + } + @Override public String toString() { return String.format( diff --git a/src/main/java/com/santander/interview/service/CategoryService.java b/src/main/java/com/santander/interview/service/CategoryService.java index 0ca95838..1969ba2c 100644 --- a/src/main/java/com/santander/interview/service/CategoryService.java +++ b/src/main/java/com/santander/interview/service/CategoryService.java @@ -5,6 +5,6 @@ import java.util.List; public interface CategoryService { - public void saveCategory(Category category); + public void saveCategory(Category newCategory); public List searchCategoryByDetailSubstring(String detailSubstring); } diff --git a/src/main/java/com/santander/interview/service/ExpenseService.java b/src/main/java/com/santander/interview/service/ExpenseService.java index df7e3c3e..58143f51 100644 --- a/src/main/java/com/santander/interview/service/ExpenseService.java +++ b/src/main/java/com/santander/interview/service/ExpenseService.java @@ -3,12 +3,11 @@ import com.santander.interview.domain.Expense; import com.santander.interview.exception.ExpenseException; -import java.text.ParseException; import java.util.List; public interface ExpenseService { - public void addNewExpense(Expense expense); - public List findExpensesByUserCode(long userCode); - public List findExpensesByUserCodeAndDate(long userCode, String date) throws ExpenseException; - public void updateExpense(String id, Expense expense) throws ExpenseException; + public void addNewExpense(Expense newExpense); + public List searchExpensesByUserCode(long userCode); + public List searchExpensesByUserCodeAndDate(long userCode, String date) throws ExpenseException; + public void updateExpense(String id, Expense newExpense) throws ExpenseException; } diff --git a/src/main/java/com/santander/interview/service/impl/CategoryServiceImpl.java b/src/main/java/com/santander/interview/service/impl/CategoryServiceImpl.java index e90cc72d..7d27ef7e 100644 --- a/src/main/java/com/santander/interview/service/impl/CategoryServiceImpl.java +++ b/src/main/java/com/santander/interview/service/impl/CategoryServiceImpl.java @@ -1,7 +1,6 @@ package com.santander.interview.service.impl; import com.santander.interview.domain.Category; -import com.santander.interview.repository.CategoryRepository; import com.santander.interview.service.CategoryService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -11,16 +10,15 @@ @Service public class CategoryServiceImpl implements CategoryService { @Autowired - CategoryRepository categoryRepository; + Category category; @Override - public void saveCategory(Category category) { - Category newCategory = new Category(category.getDetail()); - this.categoryRepository.save(newCategory); + public void saveCategory(Category newCategory) { + this.category.save(newCategory); } @Override public List searchCategoryByDetailSubstring(String detailSubstring) { - return this.categoryRepository.findByDetailLike(detailSubstring); + return this.category.searchByDetailSubstring(detailSubstring); } } diff --git a/src/main/java/com/santander/interview/service/impl/ExpenseServiceImpl.java b/src/main/java/com/santander/interview/service/impl/ExpenseServiceImpl.java index 5fb72626..d0cc29b1 100644 --- a/src/main/java/com/santander/interview/service/impl/ExpenseServiceImpl.java +++ b/src/main/java/com/santander/interview/service/impl/ExpenseServiceImpl.java @@ -2,11 +2,8 @@ import static com.santander.interview.enums.ResponseMessageEnum.*; -import com.santander.interview.domain.Category; import com.santander.interview.domain.Expense; import com.santander.interview.exception.ExpenseException; -import com.santander.interview.repository.CategoryRepository; -import com.santander.interview.repository.ExpenseRepository; import com.santander.interview.service.ExpenseService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; @@ -14,76 +11,36 @@ import org.springframework.stereotype.Service; import java.text.ParseException; -import java.text.SimpleDateFormat; import java.util.*; @Service public class ExpenseServiceImpl implements ExpenseService { @Autowired - ExpenseRepository expenseRepository; + Expense expense; - @Autowired - CategoryRepository categoryRepository; - -// private String generateUuid() { return UUID.randomUUID().toString(); } - - private Category automaticCategorization(Expense expense) { - Category category = expense.getCategory(); - if(category != null && category.getDetail() != null) { - List list = categoryRepository.findByDetail(category.getDetail()); - if (list.size() > 0) - return list.get(0); - } - - return null; - } - -// @Async + @Async @Override - public void addNewExpense(Expense expense) { - Expense newExpense = new Expense( - expense.getDescription(), - expense.getValue(), - expense.getUserCode(), - expense.getDate() - ); - newExpense.setCategory(this.automaticCategorization(expense)); - this.expenseRepository.save(newExpense); + public void addNewExpense(Expense newExpense) { + this.expense.add(newExpense); } @Override - public List findExpensesByUserCode(long userCode) { - List expenses = expenseRepository.findByUserCode(userCode); - Collections.sort(expenses, new Comparator() { - @Override - public int compare(Expense expense1, Expense expense2) { - return expense2.getDate().compareTo(expense1.getDate()); - } - }); - return expenses; + public List searchExpensesByUserCode(long userCode) { + return this.expense.searchByUserCode(userCode); } @Override - public List findExpensesByUserCodeAndDate(long userCode, String date) throws ExpenseException { - long oneDayInMilliseconds = 1000 * 60 * 60 * 24; + public List searchExpensesByUserCodeAndDate(long userCode, String date) throws ExpenseException { try { - Date startDate = new SimpleDateFormat("ddMMyyyy").parse(date); - Date endDate = new Date(startDate.getTime() + oneDayInMilliseconds); - return this.expenseRepository.findByUserCodeAndDateBetween(userCode, startDate, endDate); + return this.expense.searchByUserCodeAndDate(userCode, date); } catch(ParseException pe) { throw new ExpenseException(HttpStatus.BAD_REQUEST, EXPENSE_BADLY_FORMATTED_DATE); } } @Override - public void updateExpense(String id, Expense expense) throws ExpenseException { - Optional existExpense = this.expenseRepository.findById(id); - if(existExpense.isPresent()) { - Expense expenseFound = existExpense.get(); - expense.setId(expenseFound.getId()); - expense.setCategory(this.automaticCategorization(expense)); - this.expenseRepository.save(expense); - } else { + public void updateExpense(String id, Expense newExpense) throws ExpenseException { + if (!this.expense.update(id, newExpense)) { throw new ExpenseException(HttpStatus.NOT_FOUND, EXPENSE_NOT_FOUND); } } From f3f18f606d1ff14b76b92f380649f522a6b54cdd Mon Sep 17 00:00:00 2001 From: Thiago Sato Date: Fri, 13 Sep 2019 00:06:47 -0300 Subject: [PATCH 29/30] =?UTF-8?q?Corrigindo=20testes=20unit=C3=A1rios?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ExpenseControllerTest.java | 9 +- .../interview/domain/CategoryTest.java | 49 +++++++- .../interview/domain/ExpenseTest.java | 113 +++++++++++++++++- .../service/CategoryServiceTest.java | 24 ++-- .../interview/service/ExpenseServiceTest.java | 77 ++++-------- 5 files changed, 199 insertions(+), 73 deletions(-) diff --git a/src/test/java/com/santander/interview/controller/ExpenseControllerTest.java b/src/test/java/com/santander/interview/controller/ExpenseControllerTest.java index 7965cbf2..f5d6b6c1 100644 --- a/src/test/java/com/santander/interview/controller/ExpenseControllerTest.java +++ b/src/test/java/com/santander/interview/controller/ExpenseControllerTest.java @@ -26,6 +26,7 @@ public class ExpenseControllerTest { private static final Double VALUE = 198.23; private static final long USER_CODE = 129; private static final Date DATE = new Date(); + private static final String INCORRECT_DATE = "121251"; @InjectMocks ExpenseController expenseController = new ExpenseController(); @@ -56,7 +57,7 @@ public void getExpenseByUserCodeTest() { List list = new ArrayList<>(); list.add(this.expense); - Mockito.when(expenseService.findExpensesByUserCode(userCode)).thenReturn(list); + Mockito.when(expenseService.searchExpensesByUserCode(userCode)).thenReturn(list); ResponseEntity response = this.expenseController.getExpenseByUserCode(userCode); Assert.assertEquals(response.getStatusCode(), HttpStatus.OK); @@ -73,7 +74,7 @@ public void getExpenseByUserCodeAndDateTest() throws ExpenseException { List list = new ArrayList<>(); list.add(this.expense); - Mockito.when(this.expenseService.findExpensesByUserCodeAndDate(userCode, date)).thenReturn(list); + Mockito.when(this.expenseService.searchExpensesByUserCodeAndDate(userCode, date)).thenReturn(list); ResponseEntity response = this.expenseController.getExpenseByUserCodeAndDate(userCode, date); Assert.assertEquals(response.getStatusCode(), HttpStatus.OK); @@ -83,8 +84,8 @@ public void getExpenseByUserCodeAndDateTest() throws ExpenseException { @Test public void getExpenseByUserCodeAndDate_WithDataIncorrectTest() throws ExpenseException { long userCode = USER_CODE; - String date = "121251"; - Mockito.when(this.expenseService.findExpensesByUserCodeAndDate(userCode, date)) + String date = INCORRECT_DATE; + Mockito.when(this.expenseService.searchExpensesByUserCodeAndDate(userCode, date)) .thenThrow(new ExpenseException(HttpStatus.BAD_REQUEST, EXPENSE_BADLY_FORMATTED_DATE)); ResponseEntity response = this.expenseController.getExpenseByUserCodeAndDate(userCode, date); Assert.assertEquals(response.getStatusCode(), HttpStatus.BAD_REQUEST); diff --git a/src/test/java/com/santander/interview/domain/CategoryTest.java b/src/test/java/com/santander/interview/domain/CategoryTest.java index 622aefd2..9f444a5c 100644 --- a/src/test/java/com/santander/interview/domain/CategoryTest.java +++ b/src/test/java/com/santander/interview/domain/CategoryTest.java @@ -1,18 +1,42 @@ package com.santander.interview.domain; +import com.santander.interview.repository.CategoryRepository; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; import org.mockito.junit.MockitoJUnitRunner; +import java.util.ArrayList; +import java.util.List; + @RunWith(MockitoJUnitRunner.class) public class CategoryTest { private static final String ID = "123"; private static final String DETAIL = "teste"; + List categoriesResult; + Category categoryObject; + + @InjectMocks + Category categoryDomain = new Category(); + + @Mock + CategoryRepository categoryRepository; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + this.categoryObject = new Category(DETAIL); + this.categoriesResult = new ArrayList<>(); + this.categoriesResult.add(this.categoryObject); + } @Test - public void categoryTest() { + public void category_EmptyConstructorTest() { Category category = new Category(); category.setId(ID); category.setDetail(DETAIL); @@ -22,8 +46,29 @@ public void categoryTest() { } @Test - public void categoryConstructorWithParamsTest() { + public void category_ConstructorWithParamsTest() { Category category = new Category(DETAIL); Assert.assertEquals(category.getDetail(), DETAIL); } + + @Test + public void saveTest() { + boolean isOk = true; + try { + this.categoryDomain.save(this.categoryObject); + } catch (Exception e) { + isOk = false; + } + Assert.assertTrue(isOk); + } + + @Test + public void searchByDetailSubstringTest() { + Mockito.when(categoryRepository.findByDetailLike(this.categoryObject.getDetail())) + .thenReturn(this.categoriesResult); + + String substring = this.categoryObject.getDetail(); + List result = this.categoryDomain.searchByDetailSubstring(substring); + Assert.assertEquals(result, categoriesResult); + } } diff --git a/src/test/java/com/santander/interview/domain/ExpenseTest.java b/src/test/java/com/santander/interview/domain/ExpenseTest.java index 55606703..0df77d2f 100644 --- a/src/test/java/com/santander/interview/domain/ExpenseTest.java +++ b/src/test/java/com/santander/interview/domain/ExpenseTest.java @@ -1,11 +1,21 @@ package com.santander.interview.domain; +import com.santander.interview.enums.ResponseMessageEnum; +import com.santander.interview.exception.ExpenseException; +import com.santander.interview.repository.CategoryRepository; +import com.santander.interview.repository.ExpenseRepository; import org.junit.Assert; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; import org.mockito.junit.MockitoJUnitRunner; -import java.util.Date; +import java.text.ParseException; +import java.util.*; @RunWith(MockitoJUnitRunner.class) public class ExpenseTest { @@ -16,6 +26,23 @@ public class ExpenseTest { private static final Date DATE = new Date(); private static final String CATEGORY_DETAIL = "teste"; private static final Category CATEGORY = new Category(CATEGORY_DETAIL); + private static final String DATE_STRING = "01102019"; + private static final String DATE_STRING_INVALID = "011"; + private static final String DETAIL = "Detalhes"; + + @InjectMocks + Expense expenseDomain = new Expense(); + + @Mock + ExpenseRepository expenseRepository; + + @Mock + CategoryRepository categoryRepository; + + @Before + public void init() { + MockitoAnnotations.initMocks(this); + } @Test public void expenseEmptyConstructorTest() { @@ -60,4 +87,88 @@ public void expenseConstrutorWithoutCategoryAttrTest() { Assert.assertEquals(expense.getDescription(), DESCRIPTION); Assert.assertTrue(expense.getValue() == VALUE); } + + + @Test + public void add_WithCategoryNullTest() { + List expenses = new ArrayList<>(); + Expense expense = new Expense(DESCRIPTION, VALUE, USER_CODE, DATE); + expenses.add(expense); + + Mockito.when(this.expenseRepository.findByUserCode(USER_CODE)).thenReturn(expenses); + + this.expenseDomain.add(expense); + List result = this.expenseDomain.searchByUserCode(USER_CODE); + Assert.assertEquals(result, expenses); + } + + @Test + public void add_WithExpenseWithCategoryTest() { + boolean isOk = true; + Expense expense = new Expense(DESCRIPTION, VALUE, USER_CODE, DATE); + Category category = new Category(); + List categories = new ArrayList<>(); + category.setDetail(DETAIL); + expense.setCategory(category); + categories.add(new Category(DETAIL)); + + Mockito.when(categoryRepository.findByDetail(category.getDetail())).thenReturn(categories); + try { + this.expenseDomain.add(expense); + } catch (Exception e) { + isOk = false; + } + + Assert.assertTrue(isOk); + } + + @Test + public void searchByUserCodeAndDateTest() throws ParseException { + List expenses = new ArrayList<>(); + Expense expense = new Expense(DESCRIPTION, VALUE, USER_CODE, DATE); + expenses.add(expense); + + Assert.assertNotNull(this.expenseDomain.searchByUserCodeAndDate(USER_CODE, DATE_STRING)); + } + + @Test + public void searchByUserCodeAndDate_InvalidDataTest() { + List expenses; + boolean parseException = false; + + try { + expenses = this.expenseDomain.searchByUserCodeAndDate(USER_CODE, DATE_STRING_INVALID); + } catch (ParseException pe) { + parseException = true; + } + + Assert.assertTrue(parseException); + } + + @Test + public void update_FoundExpenseTest() throws ExpenseException { + boolean isOk = true; + String uuid = UUID.randomUUID().toString(); + Expense expense = new Expense(); + Optional optionalExpense = Optional.of(expense); + Mockito.when(this.expenseRepository.findById(uuid)).thenReturn(optionalExpense); + + try { + this.expenseDomain.update(uuid, expense); + } catch (Exception e) { + isOk = false; + } + + Assert.assertTrue(isOk); + } + + @Test + public void update_NotFoundExpenseTest() { + String uuid = UUID.randomUUID().toString(); + Expense expense = new Expense(); + Optional optionalExpense = Optional.empty(); + Mockito.when(this.expenseRepository.findById(uuid)).thenReturn(optionalExpense); + + Assert.assertFalse(this.expenseDomain.update(uuid, expense)); + } } diff --git a/src/test/java/com/santander/interview/service/CategoryServiceTest.java b/src/test/java/com/santander/interview/service/CategoryServiceTest.java index 4221dd63..e34e0d9f 100644 --- a/src/test/java/com/santander/interview/service/CategoryServiceTest.java +++ b/src/test/java/com/santander/interview/service/CategoryServiceTest.java @@ -1,7 +1,6 @@ package com.santander.interview.service; import com.santander.interview.domain.Category; -import com.santander.interview.repository.CategoryRepository; import com.santander.interview.service.impl.CategoryServiceImpl; import org.junit.Assert; import org.junit.Before; @@ -18,30 +17,30 @@ @RunWith(MockitoJUnitRunner.class) public class CategoryServiceTest { - private static final String CATEGORY_DETAIL = "Detalhe"; + private static final String DETAIL = "teste"; @InjectMocks CategoryServiceImpl categoryService = new CategoryServiceImpl(); @Mock - CategoryRepository categoryRepository; + Category categoryDomain; - Category category; + Category categoryObject; List categoriesResult; @Before public void setUp() { MockitoAnnotations.initMocks(this); - this.category = new Category(CATEGORY_DETAIL); + this.categoryObject = new Category(DETAIL); this.categoriesResult = new ArrayList<>(); - this.categoriesResult.add(this.category); + this.categoriesResult.add(this.categoryObject); } @Test public void saveCategoryTest() { boolean isOk = true; try { - this.categoryService.saveCategory(this.category); + this.categoryService.saveCategory(categoryObject); } catch (Exception e) { isOk = false; } @@ -49,11 +48,12 @@ public void saveCategoryTest() { } @Test - public void searchCategoryByDetailPrefixTest() { - Mockito.when(categoryRepository.findByDetailLike(this.category.getDetail())).thenReturn(this.categoriesResult); + public void searchCategoryByDetailSubstringTest() { + Mockito.when(categoryDomain.searchByDetailSubstring(DETAIL)) + .thenReturn(this.categoriesResult); - String substring = this.category.getDetail(); - List result = this.categoryService.searchCategoryByDetailSubstring(substring); - Assert.assertEquals(result, categoriesResult); + List categories = this.categoryService.searchCategoryByDetailSubstring(DETAIL); + Assert.assertEquals(categories, this.categoriesResult); } + } diff --git a/src/test/java/com/santander/interview/service/ExpenseServiceTest.java b/src/test/java/com/santander/interview/service/ExpenseServiceTest.java index be7eda63..04111a35 100644 --- a/src/test/java/com/santander/interview/service/ExpenseServiceTest.java +++ b/src/test/java/com/santander/interview/service/ExpenseServiceTest.java @@ -16,6 +16,7 @@ import org.mockito.MockitoAnnotations; import org.springframework.http.HttpStatus; +import java.text.ParseException; import java.util.*; import static com.santander.interview.enums.ResponseMessageEnum.EXPENSE_BADLY_FORMATTED_DATE; @@ -29,73 +30,33 @@ public class ExpenseServiceTest { private static final double VALUE = 124.2; private static final String DESCRIPTION = "Teste"; private static final String DETAIL = "Detalhes"; + List expensesResult; @InjectMocks ExpenseServiceImpl expenseService = new ExpenseServiceImpl(); @Mock - ExpenseRepository expenseRepository; - - @Mock - CategoryRepository categoryRepository; + Expense expenseDomain; @Before public void init() { MockitoAnnotations.initMocks(this); + this.expensesResult = new ArrayList<>(); + this.expensesResult.add(new Expense()); } @Test - public void addNewExpenseWithCategoryNullTest() { - List expenses = new ArrayList<>(); - Expense expense = new Expense(DESCRIPTION, VALUE, USER_CODE, DATE); - expenses.add(expense); - - Mockito.when(this.expenseRepository.findByUserCode(USER_CODE)).thenReturn(expenses); - - this.expenseService.addNewExpense(expense); - List result = this.expenseService.findExpensesByUserCode(USER_CODE); - Assert.assertEquals(result, expenses); - } - - @Test - public void addNewExpenseWithCategoryTest() { - boolean isOk = true; - Expense expense = new Expense(DESCRIPTION, VALUE, USER_CODE, DATE); - Category category = new Category(); - List categories = new ArrayList<>(); - category.setDetail(DETAIL); - expense.setCategory(category); - categories.add(new Category(DETAIL)); - - Mockito.when(categoryRepository.findByDetail(category.getDetail())).thenReturn(categories); - try { - this.expenseService.addNewExpense(expense); - } catch (Exception e) { - isOk = false; - } - - Assert.assertTrue(isOk); - } - - @Test - public void findExpensesByCodigoUsuarioAndDataTest() throws ExpenseException { - List expenses = new ArrayList<>(); - Expense expense = new Expense(DESCRIPTION, VALUE, USER_CODE, DATE); - expenses.add(expense); - - Assert.assertNotNull(this.expenseService.findExpensesByUserCodeAndDate(USER_CODE, DATE_STRING)); - } - - @Test - public void findExpensesByCodigoUsuarioAndData_InvalidDataTest() { + public void searchExpensesByUserCodeAndDate_InvalidDataTest() throws ParseException { List expenses = new ArrayList<>(); ResponseMessageEnum responseMessageEnumExpected = EXPENSE_BADLY_FORMATTED_DATE; HttpStatus httpStatusExpected = HttpStatus.BAD_REQUEST; Expense expense = new Expense(DESCRIPTION, VALUE, USER_CODE, DATE); expenses.add(expense); + Mockito.when(this.expenseDomain.searchByUserCodeAndDate(USER_CODE, DATE_STRING_INVALID)) + .thenThrow(ParseException.class); try { - this.expenseService.findExpensesByUserCodeAndDate(USER_CODE, DATE_STRING_INVALID); + this.expenseService.searchExpensesByUserCodeAndDate(USER_CODE, DATE_STRING_INVALID); } catch (ExpenseException ee) { ResponseMessageEnum responseMessageEnum = ee.getResponseMessageEnum(); Assert.assertEquals(responseMessageEnum.getInternalMessage(), responseMessageEnumExpected.getInternalMessage()); @@ -105,16 +66,25 @@ public void findExpensesByCodigoUsuarioAndData_InvalidDataTest() { } @Test - public void updateExpense_FoundExpenseTest() throws ExpenseException { + public void searchExpensesByUserCodeTest() { + Mockito.when(this.expenseDomain.searchByUserCode(USER_CODE)).thenReturn(this.expensesResult); + Assert.assertEquals( + this.expenseService.searchExpensesByUserCode(USER_CODE), + this.expensesResult + ); + } + + + @Test + public void updateExpenseTest() { boolean isOk = true; String uuid = UUID.randomUUID().toString(); Expense expense = new Expense(); - Optional optionalExpense = Optional.of(expense); - Mockito.when(this.expenseRepository.findById(uuid)).thenReturn(optionalExpense); + Mockito.when(this.expenseDomain.update(uuid, expense)).thenReturn(true); try { this.expenseService.updateExpense(uuid, expense); - } catch (Exception e) { + } catch (ExpenseException e) { isOk = false; } @@ -127,8 +97,7 @@ public void updateExpense_NotFoundExpenseTest() { ResponseMessageEnum responseMessageEnum = EXPENSE_NOT_FOUND; String uuid = UUID.randomUUID().toString(); Expense expense = new Expense(); - Optional optionalExpense = Optional.empty(); - Mockito.when(this.expenseRepository.findById(uuid)).thenReturn(optionalExpense); + Mockito.when(this.expenseDomain.update(uuid, expense)).thenReturn(false); try { this.expenseService.updateExpense(uuid, expense); From a60ed2d953b0dc401f094ada99c6974e937737b6 Mon Sep 17 00:00:00 2001 From: Thiago Sato Date: Fri, 13 Sep 2019 00:12:17 -0300 Subject: [PATCH 30/30] =?UTF-8?q?Retirando=20imports=20n=C3=A3o=20utilizad?= =?UTF-8?q?os?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/santander/interview/service/ExpenseServiceTest.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/test/java/com/santander/interview/service/ExpenseServiceTest.java b/src/test/java/com/santander/interview/service/ExpenseServiceTest.java index 04111a35..dafac6ac 100644 --- a/src/test/java/com/santander/interview/service/ExpenseServiceTest.java +++ b/src/test/java/com/santander/interview/service/ExpenseServiceTest.java @@ -1,11 +1,8 @@ package com.santander.interview.service; -import com.santander.interview.domain.Category; import com.santander.interview.domain.Expense; import com.santander.interview.enums.ResponseMessageEnum; import com.santander.interview.exception.ExpenseException; -import com.santander.interview.repository.CategoryRepository; -import com.santander.interview.repository.ExpenseRepository; import com.santander.interview.service.impl.ExpenseServiceImpl; import org.junit.Assert; import org.junit.Before;