Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,18 @@
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
</dependency>
</dependencies>

<build>
Expand All @@ -57,6 +69,14 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>9</source>
<target>9</target>
</configuration>
</plugin>
</plugins>
</build>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package com.zoomcare.candidatechallenge.controllers;

import com.zoomcare.candidatechallenge.models.Employee;
import com.zoomcare.candidatechallenge.services.EmployeeService;
import org.springframework.http.HttpStatus;
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;

/**
* Endpoints for obtaining information about an employee or collections of employees.
*/
@RestController
@RequestMapping("/employees")
public class EmployeeController {

private final EmployeeService employeeService;

public EmployeeController(EmployeeService employeeService) {
this.employeeService = employeeService;
}

/**
* Retrieves the employee with the given ID.
*
* @param id the ID of the employee to retrieve
* @return a {@code ResponseEntity} representing the requested employee. The entity will contain the
* employee data, or a {@code NOT_FOUND} status if an employee with the given ID could not be found.
*/
@GetMapping("/{id}")
public ResponseEntity<Employee> getEmployeeById(@PathVariable long id) {
Employee employee = employeeService.getEmployeeById(id);
if (employee == null) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
return new ResponseEntity<>(employee, HttpStatus.OK);
}

/**
* Retrieves all top-level employees (employees with no managers).
*
* @return a {@code ResponseEntity} representing the collection of top-level employees. The entity will contain
* the employee data.
*/
@GetMapping("/top-level-employees")
public ResponseEntity<List<Employee>> getAllTopLevelEmployees() {
List<Employee> topLevelEmployees = employeeService.getAllTopLevelEmployees();
return new ResponseEntity<>(topLevelEmployees, HttpStatus.OK);
}
}
61 changes: 61 additions & 0 deletions src/main/java/com/zoomcare/candidatechallenge/models/Employee.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package com.zoomcare.candidatechallenge.models;

import org.springframework.data.annotation.Transient;

import java.util.ArrayList;
import java.util.List;

/**
* Domain representation of an employee as it is stored in the database.
*/
public class Employee {
private Long id;
private Long supervisorId;
@Transient private List<Property> properties;
@Transient private List<Employee> directReports;

public Employee(Long id, Long supervisorId) {
this.id = id;
this.supervisorId = supervisorId;
}

public Employee() {
this.properties = new ArrayList<>();
}

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<Property> getProperties() {
return properties;
}

public void setProperties(List<Property> properties) {
this.properties = properties;
}

public void addEmployeeProperty(Property property) {
this.properties.add(property);
}

public List<Employee> getDirectReports() {
return directReports;
}

public void setDirectReports(List<Employee> directReports) {
this.directReports = directReports;
}
}
42 changes: 42 additions & 0 deletions src/main/java/com/zoomcare/candidatechallenge/models/Property.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.zoomcare.candidatechallenge.models;

/**
* Domain representation of a property of an employee as it is stored in the database.
*/
public class Property {
private Long employeeId;
private String key;
private String value;

public Property(Long employeeId, String key, String value) {
this.employeeId = employeeId;
this.key = key;
this.value = value;
}

public Property() {}

public Long getEmployeeId() {
return employeeId;
}

public void setEmployeeId(Long employeeId) {
this.employeeId = employeeId;
}

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;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.zoomcare.candidatechallenge.repositories;

import com.zoomcare.candidatechallenge.models.Employee;
import org.springframework.data.jdbc.repository.query.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;

import java.util.List;
import java.util.Optional;

/**
* Access layer for the employee table.
*/
@Repository
public interface EmployeeRepository extends CrudRepository<Employee, Long> {
@Query("SELECT * FROM Employee WHERE id = :id")
Optional<Employee> findById(Long id);

@Query("SELECT * FROM Employee WHERE supervisor_id = :supervisorId")
Optional<List<Employee>> findDirectReports(Long supervisorId);

@Query("SELECT * FROM Employee WHERE supervisor_id IS NULL")
Optional<List<Employee>> findBySupervisorIdIsNull();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.zoomcare.candidatechallenge.repositories;

import com.zoomcare.candidatechallenge.models.Property;
import org.springframework.data.jdbc.repository.query.Query;
import org.springframework.data.repository.CrudRepository;

import java.util.List;
import java.util.Optional;

/**
* Access layer for the property table.
*/
public interface PropertyRepository extends CrudRepository<Property, Long> {
@Query("SELECT * FROM Property WHERE employee_id = :employeeId")
Optional<List<Property>> findByEmployeeId(Long employeeId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package com.zoomcare.candidatechallenge.services;

import com.zoomcare.candidatechallenge.models.Employee;
import com.zoomcare.candidatechallenge.models.Property;
import com.zoomcare.candidatechallenge.repositories.EmployeeRepository;
import com.zoomcare.candidatechallenge.repositories.PropertyRepository;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
* Handles employee information requests.
*/
@Service
public class EmployeeService {

private final EmployeeRepository employeeRepository;
private final PropertyRepository propertyRepository;

@Autowired
public EmployeeService(EmployeeRepository employeeRepository, PropertyRepository propertyRepository) {
this.employeeRepository = employeeRepository;
this.propertyRepository = propertyRepository;
}

public Employee getEmployeeById(Long id) {
Employee employee = employeeRepository.findById(id).orElse(null);
if (employee == null) {
return null;
}

List<Property> properties = propertyRepository.findByEmployeeId(id).orElse(Collections.emptyList());
employee.setProperties(properties);

List<Employee> directReports = employeeRepository.findDirectReports(id).orElse(Collections.emptyList());
employee.setDirectReports(directReports);

List<Employee> employees = new ArrayList<>(directReports);
while (!employees.isEmpty()) {
Employee currentEmployee = employees.remove(0);
List<Property> currentProperties = propertyRepository.findByEmployeeId(currentEmployee.getId()).orElse(Collections.emptyList());
currentEmployee.setProperties(currentProperties);
directReports = employeeRepository.findDirectReports(currentEmployee.getId()).orElse(Collections.emptyList());
currentEmployee.setDirectReports(directReports);
employees.addAll(directReports);
}

return employee;
}

public List<Employee> getAllTopLevelEmployees() {
List<Employee> topLevelEmployees = employeeRepository.findBySupervisorIdIsNull().orElse(Collections.emptyList());
return topLevelEmployees.stream()
.map(employee -> getEmployeeById(employee.getId()))
.collect(Collectors.toList());
}
}
Loading