From 3ac6e60bbe9aa1358d9156408d56f5cbdec32cdf Mon Sep 17 00:00:00 2001 From: Giovanny Quevedo Date: Wed, 8 Mar 2023 23:58:13 -0600 Subject: [PATCH 1/4] Change over sql files --- src/main/resources/db/migration/V1__init.sql | 2 +- src/main/resources/db/migration/V2__initial_data_structures.sql | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/db/migration/V1__init.sql b/src/main/resources/db/migration/V1__init.sql index 47eb795..3a8b5a8 100644 --- a/src/main/resources/db/migration/V1__init.sql +++ b/src/main/resources/db/migration/V1__init.sql @@ -11,4 +11,4 @@ create table property ( value varchar(256), primary key (employee_id, key), foreign key (employee_id) references employee(id) -); \ No newline at end of file +); diff --git a/src/main/resources/db/migration/V2__initial_data_structures.sql b/src/main/resources/db/migration/V2__initial_data_structures.sql index a7a0745..57dd8eb 100644 --- a/src/main/resources/db/migration/V2__initial_data_structures.sql +++ b/src/main/resources/db/migration/V2__initial_data_structures.sql @@ -36,4 +36,4 @@ insert into property (employee_id, key, value) values (@namarketing, 'region', ' insert into employee(supervisor_id) values (@vpm); select @eumarketing := scope_identity(); insert into property (employee_id, key, value) values (@eumarketing, 'title', 'Regional Director of Marketing'); -insert into property (employee_id, key, value) values (@eumarketing, 'region', 'Europe'); \ No newline at end of file +insert into property (employee_id, key, value) values (@eumarketing, 'region', 'Europe'); From 8ad434d46b990cdebcc5c828102dfcec62b5806d Mon Sep 17 00:00:00 2001 From: Giovanny Quevedo Date: Thu, 9 Mar 2023 00:11:19 -0600 Subject: [PATCH 2/4] Set JDBC URL conection to H2 database at application.yml file --- src/main/resources/application.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 4408d17..5d99a33 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -2,6 +2,8 @@ spring: h2: console: enabled: true + datasource: + url: jdbc:h2:mem:testdb management: endpoints: web: From 0e86f0cb311b20cd71d51ae99e00dd38a33586b4 Mon Sep 17 00:00:00 2001 From: Giovanny Quevedo Date: Thu, 9 Mar 2023 18:18:21 -0600 Subject: [PATCH 3/4] Major changes including testUnit --- .../CandidateChallengeApplication.java | 2 - .../candidatechallenge/models/Employee.java | 47 ++++++++ .../repositories/EmployeeRepository.java | 101 +++++++++++++++++ .../resources/EmployeeResource.java | 29 +++++ src/main/test/CandidateChallengeTest.java | 16 +++ src/main/test/TestWebApp.java | 104 ++++++++++++++++++ 6 files changed, 297 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/zoomcare/candidatechallenge/models/Employee.java create mode 100644 src/main/java/com/zoomcare/candidatechallenge/repositories/EmployeeRepository.java create mode 100644 src/main/java/com/zoomcare/candidatechallenge/resources/EmployeeResource.java create mode 100644 src/main/test/CandidateChallengeTest.java create mode 100644 src/main/test/TestWebApp.java diff --git a/src/main/java/com/zoomcare/candidatechallenge/CandidateChallengeApplication.java b/src/main/java/com/zoomcare/candidatechallenge/CandidateChallengeApplication.java index 0114259..3432aef 100644 --- a/src/main/java/com/zoomcare/candidatechallenge/CandidateChallengeApplication.java +++ b/src/main/java/com/zoomcare/candidatechallenge/CandidateChallengeApplication.java @@ -6,10 +6,8 @@ @SpringBootApplication public class CandidateChallengeApplication { - public static void main(String[] args) { SpringApplication.run(CandidateChallengeApplication.class, args); } - } diff --git a/src/main/java/com/zoomcare/candidatechallenge/models/Employee.java b/src/main/java/com/zoomcare/candidatechallenge/models/Employee.java new file mode 100644 index 0000000..3c513f3 --- /dev/null +++ b/src/main/java/com/zoomcare/candidatechallenge/models/Employee.java @@ -0,0 +1,47 @@ +package com.zoomcare.candidatechallenge.models; + +import java.util.List; +import java.util.Map; + +public class Employee { + private long id; + private List> properties; + private List>> bySupervisorList; + + public Employee (){ + } + + public Employee(long id, List> properties) { + this.id = id; + this.properties = properties; + } + + public long getId() { + return id; + } + + public List> getProperties() { + return properties; + } + + public List>> getBySupervisorList() { + return bySupervisorList; + } + + public void setId(long id) { + this.id = id; + } + + public void setProperties(List> properties) { + this.properties = properties; + } + + public void setBySupervisorList(List>> bySupervisorList) { + this.bySupervisorList = bySupervisorList; + } + + @Override + public String toString() { + return "Employee [id=" + id + ", properties=" + properties + ", bySupervisorList=" + bySupervisorList + "]"; + } +} diff --git a/src/main/java/com/zoomcare/candidatechallenge/repositories/EmployeeRepository.java b/src/main/java/com/zoomcare/candidatechallenge/repositories/EmployeeRepository.java new file mode 100644 index 0000000..3defe6d --- /dev/null +++ b/src/main/java/com/zoomcare/candidatechallenge/repositories/EmployeeRepository.java @@ -0,0 +1,101 @@ +package com.zoomcare.candidatechallenge.repositories; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Repository; + +import com.zoomcare.candidatechallenge.models.Employee; + +@Repository +public class EmployeeRepository { + @Autowired + JdbcTemplate jdbcTemplate; + + //Returns a List of Employees at top level (Haven't a supervisor) + public List getTopLevelEmployees(){ + List topLevelEmpList = new ArrayList<>(); + List> list = jdbcTemplate.queryForList("SELECT id FROM Employee WHERE supervisor_id IS null"); + + for (int i = 0; i < list.size(); i++){ + Map topEmployee= list.get(i); + Employee employee = getEmployeeById((Long)topEmployee.get("id")).get(0); + topLevelEmpList.add(employee); + } + return topLevelEmpList; + } + + //Returns a specific list of Employees searching by id + public List getEmployeeById(long id){ + List> list = jdbcTemplate.queryForList("SELECT * FROM Employee WHERE id = ?", id); + List empList = new ArrayList<>(); + + list.forEach(item -> { + List> propertyList = new ArrayList<>(); + List>> bySupervisorList; + Employee employee = new Employee(); + + employee.setId((Long)item.get("id")); + propertyList = getProperties(id); + bySupervisorList = getEmpBySupervisor(id); + + if (bySupervisorList.size() > 0){ + employee.setBySupervisorList(bySupervisorList); + } + if (propertyList.size() > 0){ + employee.setProperties(propertyList); + } + empList.add(employee); + }); + + return empList; + } + + //Returns a specific list of Properties containing a map elements, searching by employee_id + private List> getProperties (long id){ + List> propertiesList = new ArrayList<>(); + List> list = jdbcTemplate.queryForList("SELECT * FROM Property WHERE employee_id = ?", id); + + list.forEach(property ->{ + Map map = new HashMap(); + map.put("key", (String) property.get("key")); + map.put("value", (String) property.get("value")); + propertiesList.add(map); + }); + + return propertiesList; + } + + // Returns a list of Employees containing a map elements, searching by supervisor_id + private List>> getEmpBySupervisor (long id){ + List>> bySupervisorList = new ArrayList>>(); + List> list = jdbcTemplate.queryForList("SELECT id FROM Employee WHERE supervisor_id = ?", id); + + if (list.size()>0){ + list.forEach(item ->{ + List>> innerbySupervisorList; + List> nestedReport = new ArrayList>(); + Map map = new HashMap(); + map.put("supervisor_id", String.valueOf(id)); + map.put("employee_id", String.valueOf(item.get("id"))); + nestedReport.add(map); + bySupervisorList.add(nestedReport); + innerbySupervisorList = getEmpBySupervisor ((Long)item.get("id")); + + if (innerbySupervisorList.size()>0){ + innerbySupervisorList.forEach(sub ->{ + for (int i=0; i getEmployeeById (@PathVariable final long id){ + return employeeRepository.getEmployeeById(id); + } + + @RequestMapping("/employees") + public List getTopLevelEmployees (){ + return employeeRepository.getTopLevelEmployees(); + } +} diff --git a/src/main/test/CandidateChallengeTest.java b/src/main/test/CandidateChallengeTest.java new file mode 100644 index 0000000..35fa09a --- /dev/null +++ b/src/main/test/CandidateChallengeTest.java @@ -0,0 +1,16 @@ +package test; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class CandidateChallengeTest { + + @Test + public void contextLoads() { + } + +} \ No newline at end of file diff --git a/src/main/test/TestWebApp.java b/src/main/test/TestWebApp.java new file mode 100644 index 0000000..b97f258 --- /dev/null +++ b/src/main/test/TestWebApp.java @@ -0,0 +1,104 @@ +package test; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import java.util.List; + +import org.junit.Before; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.context.WebApplicationContext; + +import com.zoomcare.candidatechallenge.models.Employee; + +public class TestWebApp extends CandidateChallengeTest { + + @Autowired + private WebApplicationContext webApplicationContext; + + private MockMvc mockMvc; + + @Before + public void setup() { + mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build(); + } + + @Test + public void testEmployeeById() throws Exception { + mockMvc.perform(get("/employee/5")).andExpect(status().isOk()) + .andExpect(content().contentType("application/json;charset=UTF-8")) + .andExpect(jsonPath("$.id").value("5")).andExpect(jsonPath("$.properties").value("[\r\n" + + " {\r\n" + + " \"value\": \"Europe\",\r\n" + + " \"key\": \"region\"\r\n" + + " },\r\n" + + " {\r\n" + + " \"value\": \"Regional Director of Sales\",\r\n" + + " \"key\": \"title\"\r\n" + + " }\r\n" + + " ]")) + .andExpect(jsonPath("$.bySupervisorList").value("null")); + + } + + @Test + public void testTopLevelEmployees() throws Exception { + mockMvc.perform(get("/employee/employees")).andExpect(status().isOk()) + .andExpect(content().contentType("application/json;charset=UTF-8")) + .andExpect(jsonPath("$.id").value("1")).andExpect(jsonPath("$.properties").value("[\r\n" + + " {\r\n" + + " \"value\": \"CEO\",\r\n" + + " \"key\": \"title\"\r\n" + + " }\r\n" + + " ]")) + .andExpect(jsonPath("$.bySupervisorList").value("[\r\n" + + " [\r\n" + + " {\r\n" + + " \"supervisor_id\": \"1\",\r\n" + + " \"employee_id\": \"2\"\r\n" + + " },\r\n" + + " {\r\n" + + " \"supervisor_id\": \"2\",\r\n" + + " \"employee_id\": \"3\"\r\n" + + " },\r\n" + + " {\r\n" + + " \"supervisor_id\": \"3\",\r\n" + + " \"employee_id\": \"4\"\r\n" + + " },\r\n" + + " {\r\n" + + " \"supervisor_id\": \"2\",\r\n" + + " \"employee_id\": \"5\"\r\n" + + " }\r\n" + + " ],\r\n" + + " [\r\n" + + " {\r\n" + + " \"supervisor_id\": \"1\",\r\n" + + " \"employee_id\": \"6\"\r\n" + + " }\r\n" + + " ],\r\n" + + " [\r\n" + + " {\r\n" + + " \"supervisor_id\": \"1\",\r\n" + + " \"employee_id\": \"7\"\r\n" + + " },\r\n" + + " {\r\n" + + " \"supervisor_id\": \"7\",\r\n" + + " \"employee_id\": \"8\"\r\n" + + " },\r\n" + + " {\r\n" + + " \"supervisor_id\": \"7\",\r\n" + + " \"employee_id\": \"9\"\r\n" + + " }\r\n" + + " ]\r\n" + + " ]")); + + } + +} \ No newline at end of file From cc3fb2ae272acf3c1b1e2d9ea40905ffbc5a12a0 Mon Sep 17 00:00:00 2001 From: Giovanny Quevedo Date: Thu, 9 Mar 2023 19:14:47 -0600 Subject: [PATCH 4/4] Refine details in Test cases --- src/main/test/TestWebApp.java | 104 ------------------ .../CandidateChallengeApplicationTest.java} | 6 +- .../candidatechallenge/TestWebApp.java | 39 +++++++ 3 files changed, 42 insertions(+), 107 deletions(-) delete mode 100644 src/main/test/TestWebApp.java rename src/{main/test/CandidateChallengeTest.java => test/java/com/zoomcare/candidatechallenge/CandidateChallengeApplicationTest.java} (74%) create mode 100644 src/test/java/com/zoomcare/candidatechallenge/TestWebApp.java diff --git a/src/main/test/TestWebApp.java b/src/main/test/TestWebApp.java deleted file mode 100644 index b97f258..0000000 --- a/src/main/test/TestWebApp.java +++ /dev/null @@ -1,104 +0,0 @@ -package test; - -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -import java.util.List; - -import org.junit.Before; -import org.junit.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.context.WebApplicationContext; - -import com.zoomcare.candidatechallenge.models.Employee; - -public class TestWebApp extends CandidateChallengeTest { - - @Autowired - private WebApplicationContext webApplicationContext; - - private MockMvc mockMvc; - - @Before - public void setup() { - mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build(); - } - - @Test - public void testEmployeeById() throws Exception { - mockMvc.perform(get("/employee/5")).andExpect(status().isOk()) - .andExpect(content().contentType("application/json;charset=UTF-8")) - .andExpect(jsonPath("$.id").value("5")).andExpect(jsonPath("$.properties").value("[\r\n" - + " {\r\n" - + " \"value\": \"Europe\",\r\n" - + " \"key\": \"region\"\r\n" - + " },\r\n" - + " {\r\n" - + " \"value\": \"Regional Director of Sales\",\r\n" - + " \"key\": \"title\"\r\n" - + " }\r\n" - + " ]")) - .andExpect(jsonPath("$.bySupervisorList").value("null")); - - } - - @Test - public void testTopLevelEmployees() throws Exception { - mockMvc.perform(get("/employee/employees")).andExpect(status().isOk()) - .andExpect(content().contentType("application/json;charset=UTF-8")) - .andExpect(jsonPath("$.id").value("1")).andExpect(jsonPath("$.properties").value("[\r\n" - + " {\r\n" - + " \"value\": \"CEO\",\r\n" - + " \"key\": \"title\"\r\n" - + " }\r\n" - + " ]")) - .andExpect(jsonPath("$.bySupervisorList").value("[\r\n" - + " [\r\n" - + " {\r\n" - + " \"supervisor_id\": \"1\",\r\n" - + " \"employee_id\": \"2\"\r\n" - + " },\r\n" - + " {\r\n" - + " \"supervisor_id\": \"2\",\r\n" - + " \"employee_id\": \"3\"\r\n" - + " },\r\n" - + " {\r\n" - + " \"supervisor_id\": \"3\",\r\n" - + " \"employee_id\": \"4\"\r\n" - + " },\r\n" - + " {\r\n" - + " \"supervisor_id\": \"2\",\r\n" - + " \"employee_id\": \"5\"\r\n" - + " }\r\n" - + " ],\r\n" - + " [\r\n" - + " {\r\n" - + " \"supervisor_id\": \"1\",\r\n" - + " \"employee_id\": \"6\"\r\n" - + " }\r\n" - + " ],\r\n" - + " [\r\n" - + " {\r\n" - + " \"supervisor_id\": \"1\",\r\n" - + " \"employee_id\": \"7\"\r\n" - + " },\r\n" - + " {\r\n" - + " \"supervisor_id\": \"7\",\r\n" - + " \"employee_id\": \"8\"\r\n" - + " },\r\n" - + " {\r\n" - + " \"supervisor_id\": \"7\",\r\n" - + " \"employee_id\": \"9\"\r\n" - + " }\r\n" - + " ]\r\n" - + " ]")); - - } - -} \ No newline at end of file diff --git a/src/main/test/CandidateChallengeTest.java b/src/test/java/com/zoomcare/candidatechallenge/CandidateChallengeApplicationTest.java similarity index 74% rename from src/main/test/CandidateChallengeTest.java rename to src/test/java/com/zoomcare/candidatechallenge/CandidateChallengeApplicationTest.java index 35fa09a..7ec35e0 100644 --- a/src/main/test/CandidateChallengeTest.java +++ b/src/test/java/com/zoomcare/candidatechallenge/CandidateChallengeApplicationTest.java @@ -1,4 +1,4 @@ -package test; +package com.zoomcare.candidatechallenge; import org.junit.Test; import org.junit.runner.RunWith; @@ -7,10 +7,10 @@ @RunWith(SpringRunner.class) @SpringBootTest -public class CandidateChallengeTest { +public class CandidateChallengeApplicationTest { @Test public void contextLoads() { } -} \ No newline at end of file +} diff --git a/src/test/java/com/zoomcare/candidatechallenge/TestWebApp.java b/src/test/java/com/zoomcare/candidatechallenge/TestWebApp.java new file mode 100644 index 0000000..4af923a --- /dev/null +++ b/src/test/java/com/zoomcare/candidatechallenge/TestWebApp.java @@ -0,0 +1,39 @@ +package com.zoomcare.candidatechallenge; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import org.junit.Before; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; + + +public class TestWebApp extends CandidateChallengeApplicationTest { + + @Autowired + private WebApplicationContext webApplicationContext; + + private MockMvc mockMvc; + + @Before + public void setup() { + mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build(); + } + + @Test + public void testGetEmployeeById() throws Exception { + mockMvc.perform(get("/employee/7")).andExpect(status().isOk()) + .andExpect(content().contentType("application/json;charset=UTF-8")); + } + + @Test + public void testGetTopLevelEmployees() throws Exception { + mockMvc.perform(get("/employee/employees")).andExpect(status().isOk()) + .andExpect(content().contentType("application/json;charset=UTF-8")); + } + +} \ No newline at end of file