From 366f98590183d52c678e441306c0ac287582bbc5 Mon Sep 17 00:00:00 2001 From: David Bryant Date: Fri, 14 Oct 2022 15:48:31 -0400 Subject: [PATCH 1/3] submission - David Bryant --- pom.xml | 13 +++- .../controller/EmployeeController.java | 31 +++++++++ .../candidatechallenge/model/Employee.java | 29 +++++++++ .../model/EmployeeWithReports.java | 16 +++++ .../repository/EmployeeRepository.java | 11 ++++ .../service/EmployeeService.java | 63 +++++++++++++++++++ 6 files changed, 162 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/zoomcare/candidatechallenge/controller/EmployeeController.java create mode 100644 src/main/java/com/zoomcare/candidatechallenge/model/Employee.java create mode 100644 src/main/java/com/zoomcare/candidatechallenge/model/EmployeeWithReports.java create mode 100644 src/main/java/com/zoomcare/candidatechallenge/repository/EmployeeRepository.java create mode 100644 src/main/java/com/zoomcare/candidatechallenge/service/EmployeeService.java diff --git a/pom.xml b/pom.xml index 17fd7ad..6f37b19 100644 --- a/pom.xml +++ b/pom.xml @@ -23,10 +23,21 @@ org.springframework.boot spring-boot-starter-actuator + + + + org.springframework.boot - spring-boot-starter-data-jdbc + spring-boot-starter-data-jpa + + org.projectlombok + lombok + 1.18.24 + provided + + org.springframework.boot spring-boot-starter-web diff --git a/src/main/java/com/zoomcare/candidatechallenge/controller/EmployeeController.java b/src/main/java/com/zoomcare/candidatechallenge/controller/EmployeeController.java new file mode 100644 index 0000000..c1b5e6e --- /dev/null +++ b/src/main/java/com/zoomcare/candidatechallenge/controller/EmployeeController.java @@ -0,0 +1,31 @@ +package com.zoomcare.candidatechallenge.controller; + +import com.zoomcare.candidatechallenge.model.EmployeeWithReports; +import com.zoomcare.candidatechallenge.service.EmployeeService; +import lombok.AllArgsConstructor; +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.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +@RestController +@RequestMapping("/employees") +@AllArgsConstructor +public class EmployeeController { + + EmployeeService employeeService; + + @GetMapping("/{id}") + public ResponseEntity getById(@PathVariable Long id) { + return ResponseEntity.ok(employeeService.getById(id)); + } + + @GetMapping("/all") + public ResponseEntity> getAll() { + return ResponseEntity.ok(employeeService.getAll()); + } +} + diff --git a/src/main/java/com/zoomcare/candidatechallenge/model/Employee.java b/src/main/java/com/zoomcare/candidatechallenge/model/Employee.java new file mode 100644 index 0000000..5db4351 --- /dev/null +++ b/src/main/java/com/zoomcare/candidatechallenge/model/Employee.java @@ -0,0 +1,29 @@ +package com.zoomcare.candidatechallenge.model; + +import lombok.Data; + +import javax.persistence.CollectionTable; +import javax.persistence.Column; +import javax.persistence.ElementCollection; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.MapKeyColumn; +import java.util.HashMap; +import java.util.Map; + +@Entity +@Data +public class Employee { + + @Id + private Long id; + + private Long supervisorId; + + @ElementCollection + @MapKeyColumn(name = "key") + @Column(name = "value") + @CollectionTable(name="property", joinColumns = @JoinColumn(name = "employee_id")) + Map properties = new HashMap<>(); +} diff --git a/src/main/java/com/zoomcare/candidatechallenge/model/EmployeeWithReports.java b/src/main/java/com/zoomcare/candidatechallenge/model/EmployeeWithReports.java new file mode 100644 index 0000000..b0baaf0 --- /dev/null +++ b/src/main/java/com/zoomcare/candidatechallenge/model/EmployeeWithReports.java @@ -0,0 +1,16 @@ +package com.zoomcare.candidatechallenge.model; + +import lombok.Data; + +import java.util.List; +import java.util.Map; + +@Data +public class EmployeeWithReports { + + private Long id; + private Long supervisorId; + private Map properties; + private List reports; + +} 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..6f2cf36 --- /dev/null +++ b/src/main/java/com/zoomcare/candidatechallenge/repository/EmployeeRepository.java @@ -0,0 +1,11 @@ +package com.zoomcare.candidatechallenge.repository; + +import com.zoomcare.candidatechallenge.model.Employee; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.List; + +public interface EmployeeRepository extends JpaRepository { + List findBySupervisorId(Long supervisorId); + List findBySupervisorIdIsNull(); +} 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..e54394d --- /dev/null +++ b/src/main/java/com/zoomcare/candidatechallenge/service/EmployeeService.java @@ -0,0 +1,63 @@ +package com.zoomcare.candidatechallenge.service; + +import com.zoomcare.candidatechallenge.model.Employee; +import com.zoomcare.candidatechallenge.model.EmployeeWithReports; +import com.zoomcare.candidatechallenge.repository.EmployeeRepository; +import lombok.AllArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; + +import java.util.ArrayList; +import java.util.List; + +@Service +@AllArgsConstructor +public class EmployeeService { + + EmployeeRepository employeeRepository; + + @GetMapping("/{id}") + public EmployeeWithReports getById(@PathVariable Long id) { + Employee employee = employeeRepository.findById(id).orElse(null); + if (employee == null) { + return null; + } + EmployeeWithReports result = new EmployeeWithReports(); + result.setId(employee.getId()); + result.setSupervisorId(employee.getSupervisorId()); + result.setProperties(employee.getProperties()); + result.setReports(collectReportsFor(employee.getId())); + return result; + } + + @GetMapping("/all") + public List getAll() { + List result = new ArrayList<>(); + List topLevel = employeeRepository.findBySupervisorIdIsNull(); + topLevel.forEach(employee -> { + EmployeeWithReports employeeWithReports = new EmployeeWithReports(); + employeeWithReports.setId(employee.getId()); + employeeWithReports.setSupervisorId(employee.getSupervisorId()); + employeeWithReports.setProperties(employee.getProperties()); + employeeWithReports.setReports(collectReportsFor(employee.getId())); + result.add(employeeWithReports); + }); + return result; + } + + + private List collectReportsFor(Long id) { + List result = new ArrayList<>(); + List reports = employeeRepository.findBySupervisorId(id); + reports.forEach(employee -> { + EmployeeWithReports employeeWithReports = new EmployeeWithReports(); + employeeWithReports.setId(employee.getId()); + employeeWithReports.setSupervisorId(employee.getSupervisorId()); + employeeWithReports.setProperties(employee.getProperties()); + employeeWithReports.setReports(collectReportsFor(employee.getId())); + result.add(employeeWithReports); + }); + return result; + } +} From 592656a32e3b8b290450ba72f434ee7a0b83ba99 Mon Sep 17 00:00:00 2001 From: David Bryant Date: Thu, 20 Oct 2022 04:56:25 -0400 Subject: [PATCH 2/3] Clean up a bit --- pom.xml | 1 - .../controller/EmployeeController.java | 20 ++---- .../candidatechallenge/model/Employee.java | 16 ++--- .../model/EmployeeWithReports.java | 4 +- .../repository/EmployeeRepository.java | 2 + .../service/EmployeeService.java | 56 +--------------- .../service/EmployeeServiceImpl.java | 65 +++++++++++++++++++ 7 files changed, 85 insertions(+), 79 deletions(-) create mode 100644 src/main/java/com/zoomcare/candidatechallenge/service/EmployeeServiceImpl.java diff --git a/pom.xml b/pom.xml index 6f37b19..f2ff7f1 100644 --- a/pom.xml +++ b/pom.xml @@ -34,7 +34,6 @@ org.projectlombok lombok - 1.18.24 provided diff --git a/src/main/java/com/zoomcare/candidatechallenge/controller/EmployeeController.java b/src/main/java/com/zoomcare/candidatechallenge/controller/EmployeeController.java index c1b5e6e..f395445 100644 --- a/src/main/java/com/zoomcare/candidatechallenge/controller/EmployeeController.java +++ b/src/main/java/com/zoomcare/candidatechallenge/controller/EmployeeController.java @@ -1,31 +1,25 @@ package com.zoomcare.candidatechallenge.controller; -import com.zoomcare.candidatechallenge.model.EmployeeWithReports; import com.zoomcare.candidatechallenge.service.EmployeeService; import lombok.AllArgsConstructor; 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.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import java.util.List; - @RestController @RequestMapping("/employees") @AllArgsConstructor public class EmployeeController { - EmployeeService employeeService; - - @GetMapping("/{id}") - public ResponseEntity getById(@PathVariable Long id) { - return ResponseEntity.ok(employeeService.getById(id)); - } + private EmployeeService employeeService; - @GetMapping("/all") - public ResponseEntity> getAll() { - return ResponseEntity.ok(employeeService.getAll()); + @GetMapping + public ResponseEntity getById(@RequestParam(required = false) Long id) { + return id == null ? + ResponseEntity.ok(employeeService.getAll()) : + ResponseEntity.ok(employeeService.getById(id)); } } diff --git a/src/main/java/com/zoomcare/candidatechallenge/model/Employee.java b/src/main/java/com/zoomcare/candidatechallenge/model/Employee.java index 5db4351..729a22e 100644 --- a/src/main/java/com/zoomcare/candidatechallenge/model/Employee.java +++ b/src/main/java/com/zoomcare/candidatechallenge/model/Employee.java @@ -2,13 +2,7 @@ import lombok.Data; -import javax.persistence.CollectionTable; -import javax.persistence.Column; -import javax.persistence.ElementCollection; -import javax.persistence.Entity; -import javax.persistence.Id; -import javax.persistence.JoinColumn; -import javax.persistence.MapKeyColumn; +import javax.persistence.*; import java.util.HashMap; import java.util.Map; @@ -17,13 +11,13 @@ public class Employee { @Id - private Long id; + public Long id; - private Long supervisorId; + public Long supervisorId; @ElementCollection @MapKeyColumn(name = "key") @Column(name = "value") - @CollectionTable(name="property", joinColumns = @JoinColumn(name = "employee_id")) - Map properties = new HashMap<>(); + @CollectionTable(name = "property", joinColumns = @JoinColumn(name = "employee_id")) + public Map properties = new HashMap<>(); } diff --git a/src/main/java/com/zoomcare/candidatechallenge/model/EmployeeWithReports.java b/src/main/java/com/zoomcare/candidatechallenge/model/EmployeeWithReports.java index b0baaf0..66cdc50 100644 --- a/src/main/java/com/zoomcare/candidatechallenge/model/EmployeeWithReports.java +++ b/src/main/java/com/zoomcare/candidatechallenge/model/EmployeeWithReports.java @@ -9,8 +9,10 @@ public class EmployeeWithReports { private Long id; + private Long supervisorId; + private Map properties; - private List reports; + private List reports; } diff --git a/src/main/java/com/zoomcare/candidatechallenge/repository/EmployeeRepository.java b/src/main/java/com/zoomcare/candidatechallenge/repository/EmployeeRepository.java index 6f2cf36..a96ea75 100644 --- a/src/main/java/com/zoomcare/candidatechallenge/repository/EmployeeRepository.java +++ b/src/main/java/com/zoomcare/candidatechallenge/repository/EmployeeRepository.java @@ -6,6 +6,8 @@ import java.util.List; public interface EmployeeRepository extends JpaRepository { + List findBySupervisorId(Long supervisorId); + List findBySupervisorIdIsNull(); } diff --git a/src/main/java/com/zoomcare/candidatechallenge/service/EmployeeService.java b/src/main/java/com/zoomcare/candidatechallenge/service/EmployeeService.java index e54394d..b98b458 100644 --- a/src/main/java/com/zoomcare/candidatechallenge/service/EmployeeService.java +++ b/src/main/java/com/zoomcare/candidatechallenge/service/EmployeeService.java @@ -1,63 +1,13 @@ package com.zoomcare.candidatechallenge.service; -import com.zoomcare.candidatechallenge.model.Employee; import com.zoomcare.candidatechallenge.model.EmployeeWithReports; -import com.zoomcare.candidatechallenge.repository.EmployeeRepository; -import lombok.AllArgsConstructor; -import org.springframework.stereotype.Service; -import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; -import java.util.ArrayList; import java.util.List; -@Service -@AllArgsConstructor -public class EmployeeService { +public interface EmployeeService { - EmployeeRepository employeeRepository; + EmployeeWithReports getById(@PathVariable Long id); - @GetMapping("/{id}") - public EmployeeWithReports getById(@PathVariable Long id) { - Employee employee = employeeRepository.findById(id).orElse(null); - if (employee == null) { - return null; - } - EmployeeWithReports result = new EmployeeWithReports(); - result.setId(employee.getId()); - result.setSupervisorId(employee.getSupervisorId()); - result.setProperties(employee.getProperties()); - result.setReports(collectReportsFor(employee.getId())); - return result; - } - - @GetMapping("/all") - public List getAll() { - List result = new ArrayList<>(); - List topLevel = employeeRepository.findBySupervisorIdIsNull(); - topLevel.forEach(employee -> { - EmployeeWithReports employeeWithReports = new EmployeeWithReports(); - employeeWithReports.setId(employee.getId()); - employeeWithReports.setSupervisorId(employee.getSupervisorId()); - employeeWithReports.setProperties(employee.getProperties()); - employeeWithReports.setReports(collectReportsFor(employee.getId())); - result.add(employeeWithReports); - }); - return result; - } - - - private List collectReportsFor(Long id) { - List result = new ArrayList<>(); - List reports = employeeRepository.findBySupervisorId(id); - reports.forEach(employee -> { - EmployeeWithReports employeeWithReports = new EmployeeWithReports(); - employeeWithReports.setId(employee.getId()); - employeeWithReports.setSupervisorId(employee.getSupervisorId()); - employeeWithReports.setProperties(employee.getProperties()); - employeeWithReports.setReports(collectReportsFor(employee.getId())); - result.add(employeeWithReports); - }); - return result; - } + List getAll(); } 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..c2fc87f --- /dev/null +++ b/src/main/java/com/zoomcare/candidatechallenge/service/EmployeeServiceImpl.java @@ -0,0 +1,65 @@ +package com.zoomcare.candidatechallenge.service; + +import com.zoomcare.candidatechallenge.model.Employee; +import com.zoomcare.candidatechallenge.model.EmployeeWithReports; +import com.zoomcare.candidatechallenge.repository.EmployeeRepository; +import lombok.AllArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.web.bind.annotation.PathVariable; + +import java.util.List; +import java.util.stream.Collectors; + +@Service +@AllArgsConstructor +public class EmployeeServiceImpl implements EmployeeService { + + private EmployeeRepository employeeRepository; + + public EmployeeWithReports getById(@PathVariable Long id) { + if (!employeeRepository.existsById(id)) { + return null; + } + return employeeRepository + .findById(id) + .map(this::getEmployeeWithReports) + .orElse(null); + } + + public List getAll() { + return employeeRepository + .findBySupervisorIdIsNull() + .stream() + .map(this::getEmployeeWithReports) + .collect(Collectors.toList()); + } + + /** + * Recursively collects reports via findBySupervisorId. + * + * @param id the id of the superior employee. + * @return a list of reports. + */ + private List collectReportsFor(Long id) { + return employeeRepository + .findBySupervisorId(id) + .stream() + .map(this::getEmployeeWithReports) + .collect(Collectors.toList()); + } + + /** + * Creates a new EmployeeWithReports object. + * + * @param employee the DAU instance to copy. + * @return an EmployeeWithReports object. + */ + private EmployeeWithReports getEmployeeWithReports(Employee employee) { + EmployeeWithReports result = new EmployeeWithReports(); + result.setId(employee.getId()); + result.setSupervisorId(employee.getSupervisorId()); + result.setProperties(employee.getProperties()); + result.setReports(collectReportsFor(employee.getId())); + return result; + } +} From 5c54b7ebf6655ccbfa32d12086b6400d6460f525 Mon Sep 17 00:00:00 2001 From: David Bryant Date: Thu, 20 Oct 2022 14:31:51 -0400 Subject: [PATCH 3/3] API format change + comments --- .../controller/EmployeeController.java | 35 ++++++++-------- .../controller/EmployeeControllerImpl.java | 35 ++++++++++++++++ .../service/EmployeeService.java | 15 ++++++- .../service/EmployeeServiceImpl.java | 40 +++++++++---------- 4 files changed, 85 insertions(+), 40 deletions(-) create mode 100644 src/main/java/com/zoomcare/candidatechallenge/controller/EmployeeControllerImpl.java diff --git a/src/main/java/com/zoomcare/candidatechallenge/controller/EmployeeController.java b/src/main/java/com/zoomcare/candidatechallenge/controller/EmployeeController.java index f395445..e2c792b 100644 --- a/src/main/java/com/zoomcare/candidatechallenge/controller/EmployeeController.java +++ b/src/main/java/com/zoomcare/candidatechallenge/controller/EmployeeController.java @@ -1,25 +1,24 @@ package com.zoomcare.candidatechallenge.controller; -import com.zoomcare.candidatechallenge.service.EmployeeService; -import lombok.AllArgsConstructor; +import com.zoomcare.candidatechallenge.model.EmployeeWithReports; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; -@RestController -@RequestMapping("/employees") -@AllArgsConstructor -public class EmployeeController { +import java.util.List; - private EmployeeService employeeService; +public interface EmployeeController { - @GetMapping - public ResponseEntity getById(@RequestParam(required = false) Long id) { - return id == null ? - ResponseEntity.ok(employeeService.getAll()) : - ResponseEntity.ok(employeeService.getById(id)); - } -} + /** + * Returns a list of all top-level employees. + * + * @return the list of employees + */ + ResponseEntity> getAll(); + /** + * Returns the employee with the given ID or a 404 response if no such employee exists. + * + * @param id the employee ID + * @return the employee + */ + ResponseEntity getById(Long id); +} diff --git a/src/main/java/com/zoomcare/candidatechallenge/controller/EmployeeControllerImpl.java b/src/main/java/com/zoomcare/candidatechallenge/controller/EmployeeControllerImpl.java new file mode 100644 index 0000000..a1232a3 --- /dev/null +++ b/src/main/java/com/zoomcare/candidatechallenge/controller/EmployeeControllerImpl.java @@ -0,0 +1,35 @@ +package com.zoomcare.candidatechallenge.controller; + +import com.zoomcare.candidatechallenge.model.EmployeeWithReports; +import com.zoomcare.candidatechallenge.service.EmployeeService; +import lombok.AllArgsConstructor; +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.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; +import java.util.Optional; + + +@RestController +@RequestMapping("/employees") +@AllArgsConstructor +public class EmployeeControllerImpl implements EmployeeController { + + private EmployeeService employeeService; + + @GetMapping("/all") + public ResponseEntity> getAll() { + return ResponseEntity.ok(employeeService.getAll()); + } + + @GetMapping("/id/{id}") + public ResponseEntity getById(@PathVariable Long id) { + return Optional.ofNullable(employeeService.getById(id)) + .map(ResponseEntity::ok) // if exists, return 200 with body + .orElse(ResponseEntity.notFound().build()); // otherwise return a 404 + } +} + diff --git a/src/main/java/com/zoomcare/candidatechallenge/service/EmployeeService.java b/src/main/java/com/zoomcare/candidatechallenge/service/EmployeeService.java index b98b458..3165b84 100644 --- a/src/main/java/com/zoomcare/candidatechallenge/service/EmployeeService.java +++ b/src/main/java/com/zoomcare/candidatechallenge/service/EmployeeService.java @@ -7,7 +7,18 @@ public interface EmployeeService { - EmployeeWithReports getById(@PathVariable Long id); - + /** + * Returns all top-level employees. + * + * @return the list of employees + */ List getAll(); + + /** + * Returns the employee with the given ID, or null if no such employee exists. + * + * @param id the employee ID + * @return the employee + */ + EmployeeWithReports getById(@PathVariable Long id); } diff --git a/src/main/java/com/zoomcare/candidatechallenge/service/EmployeeServiceImpl.java b/src/main/java/com/zoomcare/candidatechallenge/service/EmployeeServiceImpl.java index c2fc87f..c907d68 100644 --- a/src/main/java/com/zoomcare/candidatechallenge/service/EmployeeServiceImpl.java +++ b/src/main/java/com/zoomcare/candidatechallenge/service/EmployeeServiceImpl.java @@ -16,16 +16,6 @@ public class EmployeeServiceImpl implements EmployeeService { private EmployeeRepository employeeRepository; - public EmployeeWithReports getById(@PathVariable Long id) { - if (!employeeRepository.existsById(id)) { - return null; - } - return employeeRepository - .findById(id) - .map(this::getEmployeeWithReports) - .orElse(null); - } - public List getAll() { return employeeRepository .findBySupervisorIdIsNull() @@ -34,18 +24,14 @@ public List getAll() { .collect(Collectors.toList()); } - /** - * Recursively collects reports via findBySupervisorId. - * - * @param id the id of the superior employee. - * @return a list of reports. - */ - private List collectReportsFor(Long id) { + public EmployeeWithReports getById(@PathVariable Long id) { + if (!employeeRepository.existsById(id)) { + return null; + } return employeeRepository - .findBySupervisorId(id) - .stream() + .findById(id) .map(this::getEmployeeWithReports) - .collect(Collectors.toList()); + .orElse(null); } /** @@ -62,4 +48,18 @@ private EmployeeWithReports getEmployeeWithReports(Employee employee) { result.setReports(collectReportsFor(employee.getId())); return result; } + + /** + * Recursively collects reports via findBySupervisorId. + * + * @param id the id of the superior employee. + * @return a list of reports. + */ + private List collectReportsFor(Long id) { + return employeeRepository + .findBySupervisorId(id) + .stream() + .map(this::getEmployeeWithReports) + .collect(Collectors.toList()); + } }