From 89aca6cf46328bc8de0f2644a46eb1cf5f0dfb8f Mon Sep 17 00:00:00 2001 From: Azbel Trinidad Date: Fri, 10 Mar 2023 09:19:33 -0600 Subject: [PATCH] Candidate code challege --- pom.xml | 20 ++++ .../controllers/EmployeeController.java | 24 ++++ .../controllers/EmployeeControllerImpl.java | 103 +++++++++++++++++ .../candidatechallenge/entity/Employee.java | 58 ++++++++++ .../candidatechallenge/entity/EmployeeId.java | 44 ++++++++ .../candidatechallenge/entity/Property.java | 51 +++++++++ .../exception/EmployeeNotFoundException.java | 7 ++ .../EmployeeProductExceptionController.java | 16 +++ .../candidatechallenge/model/EmployeeDto.java | 40 +++++++ .../candidatechallenge/model/PropertyDto.java | 21 ++++ .../repository/EmployeeRepository.java | 20 ++++ .../repository/PropertyRepository.java | 12 ++ .../service/EmployeeService.java | 17 +++ .../service/EmployeeServiceImpl.java | 38 +++++++ src/main/resources/application.yml | 12 ++ src/main/resources/mock/add.json | 13 +++ .../controllers/EmployeeControllerTest.java | 104 ++++++++++++++++++ 17 files changed, 600 insertions(+) create mode 100644 src/main/java/com/zoomcare/candidatechallenge/controllers/EmployeeController.java create mode 100644 src/main/java/com/zoomcare/candidatechallenge/controllers/EmployeeControllerImpl.java create mode 100644 src/main/java/com/zoomcare/candidatechallenge/entity/Employee.java create mode 100644 src/main/java/com/zoomcare/candidatechallenge/entity/EmployeeId.java create mode 100644 src/main/java/com/zoomcare/candidatechallenge/entity/Property.java create mode 100644 src/main/java/com/zoomcare/candidatechallenge/exception/EmployeeNotFoundException.java create mode 100644 src/main/java/com/zoomcare/candidatechallenge/exception/EmployeeProductExceptionController.java create mode 100644 src/main/java/com/zoomcare/candidatechallenge/model/EmployeeDto.java create mode 100644 src/main/java/com/zoomcare/candidatechallenge/model/PropertyDto.java create mode 100644 src/main/java/com/zoomcare/candidatechallenge/repository/EmployeeRepository.java create mode 100644 src/main/java/com/zoomcare/candidatechallenge/repository/PropertyRepository.java create mode 100644 src/main/java/com/zoomcare/candidatechallenge/service/EmployeeService.java create mode 100644 src/main/java/com/zoomcare/candidatechallenge/service/EmployeeServiceImpl.java create mode 100644 src/main/resources/mock/add.json create mode 100644 src/test/java/com/zoomcare/candidatechallenge/controllers/EmployeeControllerTest.java diff --git a/pom.xml b/pom.xml index 17fd7ad..2128d9d 100644 --- a/pom.xml +++ b/pom.xml @@ -49,6 +49,15 @@ spring-boot-starter-test test + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.junit.jupiter + junit-jupiter-engine + test + @@ -57,6 +66,17 @@ org.springframework.boot spring-boot-maven-plugin + + org.apache.maven.plugins + maven-surefire-plugin + + + org.junit.jupiter + junit-jupiter-engine + 5.3.2 + + + diff --git a/src/main/java/com/zoomcare/candidatechallenge/controllers/EmployeeController.java b/src/main/java/com/zoomcare/candidatechallenge/controllers/EmployeeController.java new file mode 100644 index 0000000..54acae4 --- /dev/null +++ b/src/main/java/com/zoomcare/candidatechallenge/controllers/EmployeeController.java @@ -0,0 +1,24 @@ +package com.zoomcare.candidatechallenge.controllers; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; + +import com.zoomcare.candidatechallenge.model.EmployeeDto; + +@RequestMapping("/employee") +public interface EmployeeController { + + @GetMapping("/") + ResponseEntity getAll(); + + @GetMapping("/{id}") + ResponseEntity getById(@PathVariable Long id); + + @PostMapping("/") + ResponseEntity addEmployee(@RequestBody EmployeeDto employee); + +} diff --git a/src/main/java/com/zoomcare/candidatechallenge/controllers/EmployeeControllerImpl.java b/src/main/java/com/zoomcare/candidatechallenge/controllers/EmployeeControllerImpl.java new file mode 100644 index 0000000..e0e6956 --- /dev/null +++ b/src/main/java/com/zoomcare/candidatechallenge/controllers/EmployeeControllerImpl.java @@ -0,0 +1,103 @@ +package com.zoomcare.candidatechallenge.controllers; + +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.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.zoomcare.candidatechallenge.entity.Employee; +import com.zoomcare.candidatechallenge.entity.Property; +import com.zoomcare.candidatechallenge.model.EmployeeDto; +import com.zoomcare.candidatechallenge.service.EmployeeService; + +@RestController +@RequestMapping("/employee") +public class EmployeeControllerImpl implements EmployeeController { + + @Autowired + private EmployeeService employeeService; + + @Override + public ResponseEntity getAll() { + List> response = new ArrayList>(); + + List employees = employeeService.getAllEmployees(); + + employees.forEach(employee -> { + Map resp = new HashMap(); + resp.putAll(getSupervisorData(employee)); + response.add(resp); + }); + + return new ResponseEntity<>(response, HttpStatus.OK); + } + + @Override + public ResponseEntity getById(Long id) { + //First fetch the employee properties + Employee employee = employeeService.findEmployeeById(id); + + Map response = new HashMap(); + response.putAll(getSupervisorData(employee)); + + return new ResponseEntity<>(response, HttpStatus.OK); + } + + private Map getSupervisorData(Employee employee) { + Map resp = new HashMap(); + + //EmployeeResponse response = new EmployeeResponse(); + resp.putAll(getEmployeeData(employee)); + List> listEmployees = new ArrayList>(); + + //then fetch the employees under supervision of current employee + List supervised = employeeService.findBySupervisorId(employee.getId()); + if (!supervised.isEmpty()) { + supervised.forEach(emp -> { + listEmployees.add(getEmployeeData(emp)); + }); + + resp.put("employees", listEmployees); + } + return resp; + } + + private Map getEmployeeData(Employee employee) { + Map response = new HashMap(); + + response.put("id", employee.getId()); + Map properties = new HashMap(); + employee.getProperties().forEach(prop -> { + properties.put(prop.getKey(), prop.getValue()); + }); + + response.putAll(properties); + + return response; + } + + @Override + public ResponseEntity addEmployee(EmployeeDto empl) { + System.out.println("Empl" + empl); + Employee employee = new Employee(); + employee.setSupervisorId(empl.getSupervisorId()); + List properties = new ArrayList(); + empl.getProperties().forEach(prop -> { + Property property = new Property(); + property.setKey(prop.getKey()); + property.setValue(prop.getValue()); + property.setEmployee(employee); + properties.add(property); + }); + employee.setProperties(properties); + + Employee added = employeeService.save(employee); + + return new ResponseEntity<>("Added succesfully with ID: " + added.getId(), HttpStatus.OK); + } + +} diff --git a/src/main/java/com/zoomcare/candidatechallenge/entity/Employee.java b/src/main/java/com/zoomcare/candidatechallenge/entity/Employee.java new file mode 100644 index 0000000..7cf8f91 --- /dev/null +++ b/src/main/java/com/zoomcare/candidatechallenge/entity/Employee.java @@ -0,0 +1,58 @@ +package com.zoomcare.candidatechallenge.entity; + +import java.util.List; + +import javax.persistence.CascadeType; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.OneToMany; + +@Entity +public class Employee { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(name = "supervisor_id") + private Long supervisorId; + + @JoinColumn(name = "employee_id") + @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL) + private List properties; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getSupervisorId() { + return supervisorId; + } + + public void setSupervisorId(Long supervisorId) { + this.supervisorId = supervisorId; + } + + public List getProperties() { + return properties; + } + + public void setProperties(List properties) { + this.properties = properties; + } + + @Override + public String toString() { + return "Employee [id=" + id + ", supervisorId=" + supervisorId + ", properties=" + properties + "]"; + } + +} diff --git a/src/main/java/com/zoomcare/candidatechallenge/entity/EmployeeId.java b/src/main/java/com/zoomcare/candidatechallenge/entity/EmployeeId.java new file mode 100644 index 0000000..4827216 --- /dev/null +++ b/src/main/java/com/zoomcare/candidatechallenge/entity/EmployeeId.java @@ -0,0 +1,44 @@ +package com.zoomcare.candidatechallenge.entity; + +import java.io.Serializable; + +public class EmployeeId implements Serializable { + + private static final long serialVersionUID = 1268563792164235735L; + + private String key; + + private Employee employee; + + public EmployeeId() { + } + + public Employee getEmployee() { + return employee; + } + + + public void setEmployee(Employee employee) { + this.employee = employee; + } + + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + @Override + public boolean equals(Object obj) { + return super.equals(obj); + } + + @Override + public int hashCode() { + return super.hashCode(); + } + +} diff --git a/src/main/java/com/zoomcare/candidatechallenge/entity/Property.java b/src/main/java/com/zoomcare/candidatechallenge/entity/Property.java new file mode 100644 index 0000000..46b31c9 --- /dev/null +++ b/src/main/java/com/zoomcare/candidatechallenge/entity/Property.java @@ -0,0 +1,51 @@ +package com.zoomcare.candidatechallenge.entity; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.IdClass; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; + +import com.fasterxml.jackson.annotation.JsonIgnore; + +@Entity +@IdClass(value = EmployeeId.class) +public class Property { + + @Id + @Column(name = "key") + private String key; + + @ManyToOne + @JoinColumn(name = "employee_id") + @JsonIgnore + private Employee employee; + + @Column(name = "value") + private String value; + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + public Employee getEmployee() { + return employee; + } + + public void setEmployee(Employee employee) { + this.employee = employee; + } + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } +} diff --git a/src/main/java/com/zoomcare/candidatechallenge/exception/EmployeeNotFoundException.java b/src/main/java/com/zoomcare/candidatechallenge/exception/EmployeeNotFoundException.java new file mode 100644 index 0000000..9ea0892 --- /dev/null +++ b/src/main/java/com/zoomcare/candidatechallenge/exception/EmployeeNotFoundException.java @@ -0,0 +1,7 @@ +package com.zoomcare.candidatechallenge.exception; + +public class EmployeeNotFoundException extends RuntimeException { + + private static final long serialVersionUID = 8826523028693639120L; + +} diff --git a/src/main/java/com/zoomcare/candidatechallenge/exception/EmployeeProductExceptionController.java b/src/main/java/com/zoomcare/candidatechallenge/exception/EmployeeProductExceptionController.java new file mode 100644 index 0000000..fdfa789 --- /dev/null +++ b/src/main/java/com/zoomcare/candidatechallenge/exception/EmployeeProductExceptionController.java @@ -0,0 +1,16 @@ +package com.zoomcare.candidatechallenge.exception; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; + +@ControllerAdvice +public class EmployeeProductExceptionController { + + @ExceptionHandler(value = EmployeeNotFoundException.class) + public ResponseEntity exception(EmployeeNotFoundException exception) { + return new ResponseEntity<>("Employee not found", HttpStatus.NOT_FOUND); + } + +} diff --git a/src/main/java/com/zoomcare/candidatechallenge/model/EmployeeDto.java b/src/main/java/com/zoomcare/candidatechallenge/model/EmployeeDto.java new file mode 100644 index 0000000..0bc3aef --- /dev/null +++ b/src/main/java/com/zoomcare/candidatechallenge/model/EmployeeDto.java @@ -0,0 +1,40 @@ +package com.zoomcare.candidatechallenge.model; + +import java.util.List; + +public class EmployeeDto { + + private Long id; + private Long supervisorId; + private List properties; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getSupervisorId() { + return supervisorId; + } + + public void setSupervisorId(Long supervisorId) { + this.supervisorId = supervisorId; + } + + public List getProperties() { + return properties; + } + + public void setProperties(List properties) { + this.properties = properties; + } + + @Override + public String toString() { + return "EmployeeDto [id=" + id + ", supervisorId=" + supervisorId + ", properties=" + properties + "]"; + } + +} diff --git a/src/main/java/com/zoomcare/candidatechallenge/model/PropertyDto.java b/src/main/java/com/zoomcare/candidatechallenge/model/PropertyDto.java new file mode 100644 index 0000000..b4b554e --- /dev/null +++ b/src/main/java/com/zoomcare/candidatechallenge/model/PropertyDto.java @@ -0,0 +1,21 @@ +package com.zoomcare.candidatechallenge.model; + +public class PropertyDto { + + private String key; + private String value; + + public String getKey() { + return key; + } + public void setKey(String key) { + this.key = key; + } + public String getValue() { + return value; + } + public void setValue(String value) { + this.value = value; + } + +} diff --git a/src/main/java/com/zoomcare/candidatechallenge/repository/EmployeeRepository.java b/src/main/java/com/zoomcare/candidatechallenge/repository/EmployeeRepository.java new file mode 100644 index 0000000..ead2321 --- /dev/null +++ b/src/main/java/com/zoomcare/candidatechallenge/repository/EmployeeRepository.java @@ -0,0 +1,20 @@ +package com.zoomcare.candidatechallenge.repository; + +import java.util.List; +import java.util.Optional; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import com.zoomcare.candidatechallenge.entity.Employee; + +@Repository +public interface EmployeeRepository extends JpaRepository { + + List findAll(); + + Optional findById(Long id); + + List findBySupervisorId(Long id); + +} diff --git a/src/main/java/com/zoomcare/candidatechallenge/repository/PropertyRepository.java b/src/main/java/com/zoomcare/candidatechallenge/repository/PropertyRepository.java new file mode 100644 index 0000000..e565225 --- /dev/null +++ b/src/main/java/com/zoomcare/candidatechallenge/repository/PropertyRepository.java @@ -0,0 +1,12 @@ +package com.zoomcare.candidatechallenge.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import com.zoomcare.candidatechallenge.entity.EmployeeId; +import com.zoomcare.candidatechallenge.entity.Property; + +@Repository +public interface PropertyRepository extends JpaRepository { + +} diff --git a/src/main/java/com/zoomcare/candidatechallenge/service/EmployeeService.java b/src/main/java/com/zoomcare/candidatechallenge/service/EmployeeService.java new file mode 100644 index 0000000..b54b771 --- /dev/null +++ b/src/main/java/com/zoomcare/candidatechallenge/service/EmployeeService.java @@ -0,0 +1,17 @@ +package com.zoomcare.candidatechallenge.service; + +import java.util.List; + +import com.zoomcare.candidatechallenge.entity.Employee; + +public interface EmployeeService { + + List getAllEmployees(); + + Employee findEmployeeById(Long id); + + List findBySupervisorId(Long id); + + Employee save(Employee employee); + +} diff --git a/src/main/java/com/zoomcare/candidatechallenge/service/EmployeeServiceImpl.java b/src/main/java/com/zoomcare/candidatechallenge/service/EmployeeServiceImpl.java new file mode 100644 index 0000000..cdb9a89 --- /dev/null +++ b/src/main/java/com/zoomcare/candidatechallenge/service/EmployeeServiceImpl.java @@ -0,0 +1,38 @@ +package com.zoomcare.candidatechallenge.service; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.zoomcare.candidatechallenge.entity.Employee; +import com.zoomcare.candidatechallenge.exception.EmployeeNotFoundException; +import com.zoomcare.candidatechallenge.repository.EmployeeRepository; + +@Service +public class EmployeeServiceImpl implements EmployeeService { + + @Autowired + private EmployeeRepository employeeRepository; + + @Override + public List getAllEmployees() { + return employeeRepository.findAll(); + } + + @Override + public Employee findEmployeeById(Long id) { + //return employeeRepository.findById(id).orElse(null); + return employeeRepository.findById(id) + .orElseThrow(() -> new EmployeeNotFoundException()); + } + + @Override + public List findBySupervisorId(Long id) { + return employeeRepository.findBySupervisorId(id); + } + + @Override + public Employee save(Employee employee) { + return employeeRepository.save(employee); + } + +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 4408d17..5a16c54 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -2,6 +2,18 @@ spring: h2: console: enabled: true + datasource: + url: jdbc:h2:mem:testdb + username: sa + password: + driverClassName: org.h2.Driver + jpa: + database-platform: org.hibernate.dialect.H2Dialect + repositories: + enabled: true + #show-sql: true + main: + allow-bean-definition-overriding: true management: endpoints: web: diff --git a/src/main/resources/mock/add.json b/src/main/resources/mock/add.json new file mode 100644 index 0000000..4aec5ce --- /dev/null +++ b/src/main/resources/mock/add.json @@ -0,0 +1,13 @@ +{ + "supervisorId": 1, + "properties": [ + { + "key": "key", + "value": "value" + }, + { + "key": "key1", + "value": "value1" + } + ] +} \ No newline at end of file diff --git a/src/test/java/com/zoomcare/candidatechallenge/controllers/EmployeeControllerTest.java b/src/test/java/com/zoomcare/candidatechallenge/controllers/EmployeeControllerTest.java new file mode 100644 index 0000000..21055a7 --- /dev/null +++ b/src/test/java/com/zoomcare/candidatechallenge/controllers/EmployeeControllerTest.java @@ -0,0 +1,104 @@ +package com.zoomcare.candidatechallenge.controllers; + +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.doReturn; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import org.junit.jupiter.api.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Bean; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringRunner; + +import com.zoomcare.candidatechallenge.entity.Employee; +import com.zoomcare.candidatechallenge.entity.Property; +import com.zoomcare.candidatechallenge.repository.EmployeeRepository; +import com.zoomcare.candidatechallenge.service.EmployeeService; +import com.zoomcare.candidatechallenge.service.EmployeeServiceImpl; + +@RunWith( SpringRunner.class ) +@SpringBootTest +@ContextConfiguration +public class EmployeeControllerTest { + + @TestConfiguration + static class EmployeeServiceImplTestContextConfiguration { + + @Bean + public EmployeeService employeeService() { + return new EmployeeServiceImpl(); + } + } + + @Autowired + private EmployeeService employeeService; + + @MockBean + private EmployeeRepository employeeRepository; + + @Test + public void testFindById() { + Optional opt = Optional.of(getEmployee()); + doReturn(opt).when(employeeRepository).findById(2L); + Employee employee = employeeService.findEmployeeById(2L); + assertEquals(employee.getId().intValue(), 10); + } + + @Test + public void testFindByIdNegative() { + Optional opt = Optional.of(getEmployee()); + doReturn(opt).when(employeeRepository).findById(2L); + Employee employee = employeeService.findEmployeeById(2L); + assertNotEquals(employee.getId().intValue(), 1); + } + + @Test + public void testFindBySupervisorId() { + List empls = new ArrayList(); + empls.add(getEmployee()); + doReturn(empls).when(employeeRepository).findBySupervisorId(1L); + List employees = employeeService.findBySupervisorId(1L); + assertTrue(employees.size() > 0); + } + + @Test + public void testFindBySupervisorIdNegative() { + List empls = new ArrayList(); + empls.add(getEmployee()); + doReturn(empls).when(employeeRepository).findBySupervisorId(1L); + List employees = employeeService.findBySupervisorId(2L); + assertTrue(employees.isEmpty()); + } + + @Test + public void testSaveEmployee() { + Employee emp = getEmployee(); + Employee emp1 = getEmployee(); + doReturn(emp).when(employeeRepository).save(emp1); + Employee savedEmp = employeeService.save(emp1); + assertEquals(savedEmp.getId().intValue(), 10); + } + + private Employee getEmployee() { + Employee employee = new Employee(); + employee.setId(10L); + employee.setSupervisorId(1L); + Property property = new Property(); + property.setKey("title"); + property.setValue("Regional Director of Sales"); + List properties = new ArrayList(); + properties.add(property); + employee.setProperties(properties); + return employee; + } + +}