diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..5008ddf Binary files /dev/null and b/.DS_Store differ diff --git a/pom.xml b/pom.xml index 17fd7ad..2d416d4 100644 --- a/pom.xml +++ b/pom.xml @@ -16,6 +16,8 @@ 1.8 + 1.8 + 1.8 @@ -25,12 +27,17 @@ org.springframework.boot - spring-boot-starter-data-jdbc + spring-boot-starter-data-jpa org.springframework.boot spring-boot-starter-web + + org.springframework.boot + spring-boot-starter-test + test + org.flywaydb @@ -49,6 +56,22 @@ spring-boot-starter-test test + + + com.fasterxml.jackson.core + jackson-core + + + + com.fasterxml.jackson.dataformat + jackson-dataformat-yaml + + + + com.fasterxml.jackson.core + jackson-databind + + @@ -57,6 +80,14 @@ org.springframework.boot spring-boot-maven-plugin + + org.apache.maven.plugins + maven-compiler-plugin + + 1.8 + 1.8 + + diff --git a/src/main/java/com/zoomcare/candidatechallenge/constants/ApplicationConstants.java b/src/main/java/com/zoomcare/candidatechallenge/constants/ApplicationConstants.java new file mode 100644 index 0000000..d67e094 --- /dev/null +++ b/src/main/java/com/zoomcare/candidatechallenge/constants/ApplicationConstants.java @@ -0,0 +1,14 @@ +package com.zoomcare.candidatechallenge.constants; + +/** + * Message values for application + */ +public final class ApplicationConstants { + + /** Personalized message for employee not found error **/ + public static final String EMPLOYEE_NOT_FOUND = "Employee ID %d not found"; + + /** Personalized message for employee ID required **/ + public static final String EMPLOYEE_ID_REQUIRED = "Employee ID required"; + +} diff --git a/src/main/java/com/zoomcare/candidatechallenge/dao/EmployeeDAO.java b/src/main/java/com/zoomcare/candidatechallenge/dao/EmployeeDAO.java new file mode 100644 index 0000000..83e8c7b --- /dev/null +++ b/src/main/java/com/zoomcare/candidatechallenge/dao/EmployeeDAO.java @@ -0,0 +1,12 @@ +package com.zoomcare.candidatechallenge.dao; + +import org.springframework.data.repository.CrudRepository; + +import com.zoomcare.candidatechallenge.model.Employee; + +/** + * DAO for employee + */ +public interface EmployeeDAO extends CrudRepository { + +} diff --git a/src/main/java/com/zoomcare/candidatechallenge/dto/EmployeeDTO.java b/src/main/java/com/zoomcare/candidatechallenge/dto/EmployeeDTO.java new file mode 100644 index 0000000..4a8804a --- /dev/null +++ b/src/main/java/com/zoomcare/candidatechallenge/dto/EmployeeDTO.java @@ -0,0 +1,46 @@ +package com.zoomcare.candidatechallenge.dto; + +import java.util.HashMap; +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonInclude.Include; + +/** + * DTO to return employee + */ +@JsonInclude(Include.NON_NULL) +public abstract class EmployeeDTO { + + /** Employee ID */ + private Long id; + + /** Properties map **/ + private Map properties; + + public EmployeeDTO() { + properties = new HashMap<>(); + } + + public EmployeeDTO(Long id, Map properties) { + this.id = id; + this.properties = properties; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Map getProperties() { + return properties; + } + + public void setProperties(Map properties) { + this.properties = properties; + } + +} diff --git a/src/main/java/com/zoomcare/candidatechallenge/dto/EmployeeInfoDTO.java b/src/main/java/com/zoomcare/candidatechallenge/dto/EmployeeInfoDTO.java new file mode 100644 index 0000000..b7e6689 --- /dev/null +++ b/src/main/java/com/zoomcare/candidatechallenge/dto/EmployeeInfoDTO.java @@ -0,0 +1,50 @@ +package com.zoomcare.candidatechallenge.dto; + +import java.util.Map; + +/** + * DTO to return employee with supervisor information + */ +public class EmployeeInfoDTO extends EmployeeDTO { + + /** Supervisor information **/ + private EmployeeInfoDTO supervisor; + + /** Error message **/ + private String error; + + public EmployeeInfoDTO() { + super(); + } + + public EmployeeInfoDTO(String error) { + this.error = error; + this.setProperties(null); + } + + public EmployeeInfoDTO(Long id, EmployeeInfoDTO supervisor, Map properties) { + super(id, properties); + this.supervisor = supervisor; + } + + public String toString() { + return String.format("EmployeeInfo[id: %d, supervisor: %d]", getId(), null != supervisor ? supervisor.getId() : null); + } + + public EmployeeInfoDTO getSupervisor() { + return supervisor; + } + + public void setSupervisor(EmployeeInfoDTO supervisor) { + this.supervisor = supervisor; + } + + public String getError() { + return error; + } + + public void setError(String error) { + this.error = error; + } + +} diff --git a/src/main/java/com/zoomcare/candidatechallenge/dto/OrganizationStructureDTO.java b/src/main/java/com/zoomcare/candidatechallenge/dto/OrganizationStructureDTO.java new file mode 100644 index 0000000..c230858 --- /dev/null +++ b/src/main/java/com/zoomcare/candidatechallenge/dto/OrganizationStructureDTO.java @@ -0,0 +1,35 @@ +package com.zoomcare.candidatechallenge.dto; + +import java.util.Map; + +/** + * DTO to create organization estructure + */ +public class OrganizationStructureDTO extends EmployeeDTO { + + /** Supervisor ID **/ + private Long supervisor; + + public OrganizationStructureDTO() { + super(); + } + + public OrganizationStructureDTO(Long id, Map properties, Long supervisor) { + super(id, properties); + this.supervisor = supervisor; + } + + @Override + public String toString() { + return String.format("OrganizationStructure[id; %d, supervidor: %d]", getId(), supervisor); + } + + public Long getSupervisor() { + return supervisor; + } + + public void setSupervisor(Long supervisor) { + this.supervisor = null != supervisor ? supervisor : -1l; + } + +} diff --git a/src/main/java/com/zoomcare/candidatechallenge/dto/TopLevelListDTO.java b/src/main/java/com/zoomcare/candidatechallenge/dto/TopLevelListDTO.java new file mode 100644 index 0000000..0e83d57 --- /dev/null +++ b/src/main/java/com/zoomcare/candidatechallenge/dto/TopLevelListDTO.java @@ -0,0 +1,37 @@ +package com.zoomcare.candidatechallenge.dto; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * DTO to create top level list organization + */ +public class TopLevelListDTO extends EmployeeDTO { + + /** underling employees **/ + private List underling; + + public TopLevelListDTO() { + super(); + underling = new ArrayList<>(); + } + + public TopLevelListDTO(Long id, Map properties, List underling) { + super(id, properties); + this.underling = underling; + } + + public String toString() { + return String.format("TopLevelLis[id: %d, underling: %d]", getId(), underling.size()); + } + + public List getUnderling() { + return underling; + } + + public void setUnderling(List underling) { + this.underling = underling; + } + +} diff --git a/src/main/java/com/zoomcare/candidatechallenge/exception/DataRequiredException.java b/src/main/java/com/zoomcare/candidatechallenge/exception/DataRequiredException.java new file mode 100644 index 0000000..6786e78 --- /dev/null +++ b/src/main/java/com/zoomcare/candidatechallenge/exception/DataRequiredException.java @@ -0,0 +1,18 @@ +package com.zoomcare.candidatechallenge.exception; + +import org.springframework.http.HttpStatus; + +/** + * Exception to report data required + */ +public class DataRequiredException extends EmployeeException { + + private static final long serialVersionUID = 1L; + + public DataRequiredException(String message) { + super(); + this.message = message; + this.httpStatus = HttpStatus.BAD_REQUEST; + } + +} diff --git a/src/main/java/com/zoomcare/candidatechallenge/exception/EmployeeException.java b/src/main/java/com/zoomcare/candidatechallenge/exception/EmployeeException.java new file mode 100644 index 0000000..1374c6c --- /dev/null +++ b/src/main/java/com/zoomcare/candidatechallenge/exception/EmployeeException.java @@ -0,0 +1,36 @@ +package com.zoomcare.candidatechallenge.exception; + +import org.springframework.http.HttpStatus; + +/** + * Basic exception for employee operations + */ +public class EmployeeException extends Exception { + + private static final long serialVersionUID = 1L; + + /** Error message **/ + protected String message; + + /** Status response **/ + protected HttpStatus httpStatus; + + public EmployeeException() { + super(); + } + + public EmployeeException(String message, HttpStatus httpStatus) { + super(); + this.message = message; + this.httpStatus = httpStatus; + } + + public String getMessage() { + return message; + } + + public HttpStatus getHttpStatus() { + return httpStatus; + } + +} 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..4dee046 --- /dev/null +++ b/src/main/java/com/zoomcare/candidatechallenge/exception/EmployeeNotFoundException.java @@ -0,0 +1,17 @@ +package com.zoomcare.candidatechallenge.exception; + +import org.springframework.http.HttpStatus; + +import com.zoomcare.candidatechallenge.constants.ApplicationConstants; + +public class EmployeeNotFoundException extends EmployeeException { + + private static final long serialVersionUID = 1L; + + public EmployeeNotFoundException(Long emplooyeeId) { + super(); + message = String.format(ApplicationConstants.EMPLOYEE_NOT_FOUND, emplooyeeId); + httpStatus = HttpStatus.NOT_FOUND; + } + +} 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..efc820e --- /dev/null +++ b/src/main/java/com/zoomcare/candidatechallenge/model/Employee.java @@ -0,0 +1,67 @@ +package com.zoomcare.candidatechallenge.model; + +import java.io.Serializable; +import java.util.List; + +import javax.persistence.CascadeType; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.Id; +import javax.persistence.OneToMany; +import javax.persistence.Table; + +/** + * Entity for table Employee + */ +@Entity +@Table(name="EMPLOYEE") +public class Employee implements Serializable { + + private static final long serialVersionUID = 1L; + + @Id + private Long id; + + private Long supervisorId; + + @OneToMany(mappedBy = "employee", fetch = FetchType.EAGER, cascade = CascadeType.ALL) + private List properties; + + public Employee() { } + + public Employee(Long id, Long supervisorId, List properties) { + this.id = id; + this.supervisorId = supervisorId; + this.properties = properties; + } + + @Override + public String toString() { + return String.format("Employee[id: %d, supervisorId: %d]", id, supervisorId); + } + + 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; + } + +} \ No newline at end of file diff --git a/src/main/java/com/zoomcare/candidatechallenge/model/Properties.java b/src/main/java/com/zoomcare/candidatechallenge/model/Properties.java new file mode 100644 index 0000000..293e998 --- /dev/null +++ b/src/main/java/com/zoomcare/candidatechallenge/model/Properties.java @@ -0,0 +1,75 @@ +package com.zoomcare.candidatechallenge.model; + +import java.io.Serializable; + +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; + +/** + * Entity for table Property + */ +@Entity +@Table(name="PROPERTY") +public class Properties implements Serializable { + + private static final long serialVersionUID = 1L; + + @Id + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "employeeId") + private Employee employee; + + @Id + private String key; + + @Id + private String value; + + public Properties() { } + + public Properties(String key, String value) { + super(); + this.key = key; + this.value = value; + } + + public Properties(Employee employee, String key, String value) { + this.employee = employee; + this.key = key; + this.value = value; + } + + @Override + public String toString() { + return String.format("Properties[employeeId: %d, key: %s, value: %s]", employee.getId(), key, 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; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + +} \ No newline at end of file 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..b9cdfbc --- /dev/null +++ b/src/main/java/com/zoomcare/candidatechallenge/repository/EmployeeRepository.java @@ -0,0 +1,55 @@ +package com.zoomcare.candidatechallenge.repository; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Repository; + +import com.zoomcare.candidatechallenge.constants.ApplicationConstants; +import com.zoomcare.candidatechallenge.dao.EmployeeDAO; +import com.zoomcare.candidatechallenge.exception.DataRequiredException; +import com.zoomcare.candidatechallenge.exception.EmployeeException; +import com.zoomcare.candidatechallenge.model.Employee; + +/** + * Repository for employees + */ +@Repository +public class EmployeeRepository { + + private static final Logger LOG = LoggerFactory.getLogger(EmployeeRepository.class); + + private final EmployeeDAO employeeDAO; + + @Autowired + public EmployeeRepository(EmployeeDAO employeeDAO) { + this.employeeDAO = employeeDAO; + } + + /** + * Get employee information + * @param id - Employee ID + * @return Employee information + * @throws EmployeeException + */ + public Employee getEmployeeById(Long id) throws EmployeeException { + + if (null == id) { + LOG.error("Employee ID is null"); + throw new DataRequiredException(ApplicationConstants.EMPLOYEE_ID_REQUIRED); + } + + LOG.info(String.format("Getting Employee by Id: %d", id)); + return employeeDAO.findById(id).orElse(null); + } + + /** + * Get all employees + * @return Employees + */ + public Iterable getAllEmployees(){ + LOG.info("Get all employees"); + return employeeDAO.findAll(); + } + +} diff --git a/src/main/java/com/zoomcare/candidatechallenge/resource/EmployeeResource.java b/src/main/java/com/zoomcare/candidatechallenge/resource/EmployeeResource.java new file mode 100644 index 0000000..add9d50 --- /dev/null +++ b/src/main/java/com/zoomcare/candidatechallenge/resource/EmployeeResource.java @@ -0,0 +1,70 @@ +package com.zoomcare.candidatechallenge.resource; + +import java.util.List; + +import javax.validation.constraints.NotBlank; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +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 org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; + +import com.zoomcare.candidatechallenge.dto.EmployeeInfoDTO; +import com.zoomcare.candidatechallenge.dto.TopLevelListDTO; +import com.zoomcare.candidatechallenge.exception.EmployeeException; +import com.zoomcare.candidatechallenge.service.EmployeeService; + +/** + * Employee APIs + */ +@RestController +@RequestMapping("/v1") +public class EmployeeResource { + + private static final Logger LOG = LoggerFactory.getLogger(EmployeeResource.class); + + private final EmployeeService employeeService; + + @Autowired + public EmployeeResource(EmployeeService employeeService) { + this.employeeService = employeeService; + } + + /** + * Return employee information + * @param id - Employee ID + * @return Employee information + */ + @GetMapping(value = "/employee/{id}", produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity getEmployeeInformation(@PathVariable("id") @NotBlank Long id) { + + LOG.info(String.format("Get employee information for employee ID: %d", id)); + try { + EmployeeInfoDTO employee = employeeService.getEmployeeInformation(id); + LOG.info(String.format("Employee found: %s", employee.toString())); + return ResponseEntity.status(HttpStatus.OK).body(employee); + + } catch (EmployeeException e) { + LOG.error(e.getMessage()); + return ResponseEntity.status(e.getHttpStatus()).body(new EmployeeInfoDTO(e.getMessage())); + } + } + + /** + * Get all employees + * @return Employee list + */ + @GetMapping(value = "/employee/all", produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity> getAllEmployees() { + List employeeList = employeeService.getAllEmployees(); + return ResponseEntity.status(HttpStatus.OK).body(employeeList); + } + + +} 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..335e087 --- /dev/null +++ b/src/main/java/com/zoomcare/candidatechallenge/service/EmployeeService.java @@ -0,0 +1,186 @@ +package com.zoomcare.candidatechallenge.service; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import com.zoomcare.candidatechallenge.dto.EmployeeInfoDTO; +import com.zoomcare.candidatechallenge.dto.OrganizationStructureDTO; +import com.zoomcare.candidatechallenge.dto.TopLevelListDTO; +import com.zoomcare.candidatechallenge.exception.EmployeeException; +import com.zoomcare.candidatechallenge.exception.EmployeeNotFoundException; +import com.zoomcare.candidatechallenge.model.Employee; +import com.zoomcare.candidatechallenge.repository.EmployeeRepository; + +/** + * Service for employees + */ +@Service +public class EmployeeService { + + private static final Logger LOG = LoggerFactory.getLogger(EmployeeService.class); + + private final EmployeeRepository employeeRepository; + + @Autowired + public EmployeeService(EmployeeRepository employeeRepository) { + this.employeeRepository = employeeRepository; + } + + /** + * Get employee information + * @param id - Employee ID + * @return Employee information + * @throws EmployeeException + */ + public EmployeeInfoDTO getEmployeeInformation(Long id) throws EmployeeException { + LOG.info("Get employee information"); + Employee employee = getEmployeeById(id); + + if (null == employee) { + throw new EmployeeNotFoundException(id); + } + + EmployeeInfoDTO employeeDTO = convertEmployeeToEmployeeDTO(employee); + + if (null != employee.getSupervisorId()) { + LOG.info("Get supervisor id: %d", employee.getSupervisorId()); + Employee supervisor = getEmployeeById(employee.getSupervisorId()); + employeeDTO.setSupervisor(convertEmployeeToEmployeeDTO(supervisor)); + } + return employeeDTO; + } + + /** + * Get all employees + * @return Employee list + */ + public List getAllEmployees() { + + Iterable employeeIterable = employeeRepository.getAllEmployees(); + List organizationList = new ArrayList<>(); + Map> employeeOrg = new HashMap<>(); + + employeeIterable.forEach(employee -> { + organizationList.add(convertEmployeeToOrganizationDTO(employee)); + }); + + organizationList.stream() + .collect(Collectors.groupingBy(OrganizationStructureDTO::getSupervisor)) + .values() + .stream() + .forEach(emp -> { + employeeOrg.put(emp.get(0).getSupervisor(), emp); + }); + + + return mapOrganization(employeeOrg); + } + + /** + * Get employee by ID + * @param id - Employee ID + * @return Employee + * @throws EmployeeException + */ + private Employee getEmployeeById(Long id) throws EmployeeException { + return employeeRepository.getEmployeeById(id); + } + + /** + * Convert employee to EmployeeDTO + * @param employee - Employee + * @return Employee DTO + */ + private EmployeeInfoDTO convertEmployeeToEmployeeDTO (Employee employee) { + if (null != employee) { + LOG.info("Information found"); + EmployeeInfoDTO employeeDTO = new EmployeeInfoDTO(); + + employeeDTO.setId(employee.getId()); + employee.getProperties().forEach(emp -> { + employeeDTO.getProperties().put(emp.getKey(), emp.getValue()); + }); + + return employeeDTO; + } + + return null; + } + + /** + * Convert employee to OrganizationDTO + * @param employee - Employee + * @return OrganizationStructureDTO + */ + private OrganizationStructureDTO convertEmployeeToOrganizationDTO(Employee employee) { + if (null != employee) { + OrganizationStructureDTO organizationDTO = new OrganizationStructureDTO(); + organizationDTO.setId(employee.getId()); + organizationDTO.setSupervisor(employee.getSupervisorId()); + employee.getProperties().forEach(emp -> { + organizationDTO.getProperties().put(emp.getKey(), emp.getValue()); + }); + + return organizationDTO; + } + + return null; + } + + /** + * Create map organization for all employees + * @param organizationMap - map by supervisors + * @return Organization map nested by supervisor + */ + private List mapOrganization(Map> organizationMap) { + List topLevelList = new ArrayList<>(); + List firstLevel = organizationMap.get(-1l); + + firstLevel.forEach(first -> { + TopLevelListDTO topLevelListDTO = new TopLevelListDTO(); + topLevelListDTO.setId(first.getId()); + topLevelListDTO.setProperties(first.getProperties()); + topLevelListDTO.getUnderling().addAll(mapNextLevels(first.getId(), organizationMap)); + topLevelList.add(topLevelListDTO); + }); + + return topLevelList; + } + + /** + * Create map organization for nested levels + * @param supervisorId - Supervisor ID + * @param organizationMap - map by supervisors + * @return Organization map nested by supervisor + */ + private List mapNextLevels(Long supervisorId, + Map> organizationMap) { + + List topLevelList = new ArrayList<>(); + List nextLevel = organizationMap.get(supervisorId); + + nextLevel.forEach(next -> { + TopLevelListDTO topLevelListDTO = new TopLevelListDTO(); + topLevelListDTO.setId(next.getId()); + topLevelListDTO.setProperties(next.getProperties()); + + if (null != organizationMap.get(next.getId())) { + topLevelListDTO.getUnderling().addAll(mapNextLevels(next.getId(), organizationMap)); + } else { + topLevelListDTO.setUnderling(null); + } + topLevelList.add(topLevelListDTO); + }); + + return topLevelList; + } + +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 4408d17..fb76e0b 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -2,6 +2,15 @@ spring: h2: console: enabled: true + datasource: + url: jdbc:h2:mem:testdb + username: sa + driverClassName: org.h2.Driver + jpa: + show-sql: true + properties: + hibernate: + format_sql: true management: endpoints: web: diff --git a/src/main/resources/log4j.yml b/src/main/resources/log4j.yml new file mode 100644 index 0000000..89f3a07 --- /dev/null +++ b/src/main/resources/log4j.yml @@ -0,0 +1,23 @@ +Configutation: + name: Default + Properties: + Property: + name: log_pattern + value: "%d{yyyy-MM-dd HH:mm:ss.SSS} %5p ${hostName} --- [%15.15t] %-40.40c{1.} : %m%n%ex" + Appenders: + Console: + name: Console_Appender + target: SYSTEM_OUT + PatternLayout: + pattern: ${log_pattern} + Loggers: + Logger: + - name: com.zoomcare.candidatechallenge + level: debug + additivity: false + AppenderRef: + - ref: Console_Appender + Root: + level: info + AppenderRef: + - ref: Console_Appender \ No newline at end of file diff --git a/src/test/java/com/zoomcare/candidatechallenge/repository/EmployeeRepositoryTest.java b/src/test/java/com/zoomcare/candidatechallenge/repository/EmployeeRepositoryTest.java new file mode 100644 index 0000000..93f751f --- /dev/null +++ b/src/test/java/com/zoomcare/candidatechallenge/repository/EmployeeRepositoryTest.java @@ -0,0 +1,101 @@ +package com.zoomcare.candidatechallenge.repository; + +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + +import org.assertj.core.api.ThrowableAssert.ThrowingCallable; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.springframework.test.context.junit4.SpringRunner; + +import com.zoomcare.candidatechallenge.dao.EmployeeDAO; +import com.zoomcare.candidatechallenge.exception.EmployeeException; +import com.zoomcare.candidatechallenge.model.Employee; +import com.zoomcare.candidatechallenge.model.Properties; + +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +@RunWith(SpringRunner.class) +public class EmployeeRepositoryTest { + + @Mock + private EmployeeDAO employeeDAO; + + @InjectMocks + private EmployeeRepository employeeRepository; + + // POSITIVE TEST + + @Test + public void givenValidIdThenshouldReturnEmployeeInformationWithSupervisor() throws EmployeeException { + Optional employeeOptional = Optional.of(new Employee(2l, 1l, Arrays.asList( + new Properties("region", "North America"), + new Properties("title", "Regional Director of Sales")))); + + when(employeeDAO.findById(any())).thenReturn(employeeOptional); + + Employee employee = employeeRepository.getEmployeeById(2l); + + assertNotNull(employee); + assertNotNull(employee.getSupervisorId()); + } + + @Test + public void givenValidIdThenshouldReturnEmployeeInformationWithoutSupervisor() throws EmployeeException { + Optional employeeOptional = Optional.of(new Employee(1l, null, Arrays.asList( + new Properties("title", "CEO")))); + + when(employeeDAO.findById(any())).thenReturn(employeeOptional); + + Employee employee = employeeRepository.getEmployeeById(2l); + + assertNotNull(employee); + assertNull(employee.getSupervisorId()); + } + + @Test + public void withExistingEmployeesThenShoulReturnEmployeesList() { + List employeeList = Arrays.asList( + new Employee(1l, null, Arrays.asList( + new Properties("title", "CEO"))), + new Employee(2l, 1l, Arrays.asList( + new Properties("region", "North America"), + new Properties("title", "Regional Director of Sales"))) + ); + + when(employeeDAO.findAll()).thenReturn((Iterable)employeeList); + + Iterable employeeIterable = employeeRepository.getAllEmployees(); + + assertNotNull(employeeIterable); + assertTrue(employeeIterable.iterator().hasNext()); + } + + // NEGATIVE TEST + + @Test + public void givenNotExistingIdThenShouldReturnNull() throws EmployeeException { + when(employeeDAO.findById(any())).thenReturn(Optional.empty()); + Employee employee = employeeRepository.getEmployeeById(1l); + assertNull(employee); + } + + @Test + public void givenNullIdThenShouldReturnException() { + + ThrowingCallable fail = () -> { + employeeRepository.getEmployeeById(null); + }; + + assertThatCode(fail).isInstanceOf(EmployeeException.class); + } + +} diff --git a/src/test/java/com/zoomcare/candidatechallenge/resource/EmployeeResourceTest.java b/src/test/java/com/zoomcare/candidatechallenge/resource/EmployeeResourceTest.java new file mode 100644 index 0000000..ec7e289 --- /dev/null +++ b/src/test/java/com/zoomcare/candidatechallenge/resource/EmployeeResourceTest.java @@ -0,0 +1,65 @@ +package com.zoomcare.candidatechallenge.resource; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.test.web.servlet.result.MockMvcResultHandlers; +import org.springframework.test.web.servlet.result.MockMvcResultMatchers; + +@RunWith(SpringRunner.class) +@SpringBootTest +@AutoConfigureMockMvc +public class EmployeeResourceTest { + + @Autowired + private MockMvc webTestClient; + + @Test + public void givenValidIdThenshouldReturnEmployeeInformationWithoutSupervisor() throws Exception { + + webTestClient.perform(MockMvcRequestBuilders + .get("/v1/employee/1") + .accept(MediaType.APPLICATION_JSON)) + .andExpect(MockMvcResultMatchers.status().isOk()) + .andExpect(MockMvcResultMatchers.jsonPath("$.id").value(1)) + .andDo(MockMvcResultHandlers.print());; + } + + @Test + public void givenValidIdThenshouldReturnEmployeeInformationWithSupervisor() throws Exception { + + webTestClient.perform(MockMvcRequestBuilders + .get("/v1/employee/2") + .accept(MediaType.APPLICATION_JSON)) + .andExpect(MockMvcResultMatchers.status().isOk()) + .andExpect(MockMvcResultMatchers.jsonPath("$.id").value(2)) + .andDo(MockMvcResultHandlers.print());; + } + + @Test + public void givenInvalidIdThenshouldReturnNotFoundMessage() throws Exception { + + webTestClient.perform(MockMvcRequestBuilders + .get("/v1/employee/100") + .accept(MediaType.APPLICATION_JSON)) + .andExpect(MockMvcResultMatchers.status().isNotFound()) + .andExpect(MockMvcResultMatchers.jsonPath("$.error").value("Employee ID 100 not found")) + .andDo(MockMvcResultHandlers.print()); + } + + @Test + public void withExistingEmployeesThenShoulReturnEmployeesList() throws Exception { + webTestClient.perform(MockMvcRequestBuilders + .get("/v1/employee/all") + .accept(MediaType.APPLICATION_JSON)) + .andExpect(MockMvcResultMatchers.status().isOk()) + .andDo(MockMvcResultHandlers.print()); + } + +} diff --git a/src/test/java/com/zoomcare/candidatechallenge/service/EmployeeServiceTest.java b/src/test/java/com/zoomcare/candidatechallenge/service/EmployeeServiceTest.java new file mode 100644 index 0000000..e54fefa --- /dev/null +++ b/src/test/java/com/zoomcare/candidatechallenge/service/EmployeeServiceTest.java @@ -0,0 +1,104 @@ +package com.zoomcare.candidatechallenge.service; + +import java.util.Arrays; +import java.util.List; + +import org.assertj.core.api.ThrowableAssert.ThrowingCallable; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.springframework.test.context.junit4.SpringRunner; + +import com.zoomcare.candidatechallenge.dto.EmployeeInfoDTO; +import com.zoomcare.candidatechallenge.dto.TopLevelListDTO; +import com.zoomcare.candidatechallenge.exception.EmployeeException; +import com.zoomcare.candidatechallenge.model.Employee; +import com.zoomcare.candidatechallenge.model.Properties; +import com.zoomcare.candidatechallenge.repository.EmployeeRepository; + +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +@RunWith(SpringRunner.class) +public class EmployeeServiceTest { + + @Mock + private EmployeeRepository employeeRepository; + + @InjectMocks + private EmployeeService employeeService; + + // POSITIVE TEST + + @Test + public void givenValidIdThenshouldReturnEmployeeInformationWithSupervisor() throws EmployeeException { + Employee employeeResult = new Employee(2l, 1l, Arrays.asList( + new Properties("region", "North America"), + new Properties("title", "Regional Director of Sales"))); + + when(employeeRepository.getEmployeeById(any())).thenReturn(employeeResult); + EmployeeInfoDTO employeeInfo = employeeService.getEmployeeInformation(2l); + + assertNotNull(employeeInfo); + assertNotNull(employeeInfo.getSupervisor()); + } + + @Test + public void givenValidIdThenshouldReturnEmployeeInformationWithoutSupervisor() throws EmployeeException { + Employee employeeResult = new Employee(1l, null, Arrays.asList( + new Properties("title", "CEO"))); + + when(employeeRepository.getEmployeeById(any())).thenReturn(employeeResult); + EmployeeInfoDTO employeeInfo = employeeService.getEmployeeInformation(1l); + + assertNotNull(employeeInfo); + assertNull(employeeInfo.getSupervisor()); + } + + @Test + public void withExistingEmployeesThenShoulReturnEmployeesList() { + List employeeList = Arrays.asList( + new Employee(1l, null, Arrays.asList( + new Properties("title", "CEO"))), + new Employee(2l, 1l, Arrays.asList( + new Properties("region", "North America"), + new Properties("title", "Regional Director of Sales"))) + ); + + when(employeeRepository.getAllEmployees()).thenReturn((Iterable)employeeList); + + List topLevelList = employeeService.getAllEmployees(); + + assertNotNull(topLevelList); + assertEquals(topLevelList.size(), 1); + assertNotNull(topLevelList.get(0).getUnderling()); + assertEquals(topLevelList.get(0).getUnderling().size(), 1); + } + + // NEGATIVE TEST + + @Test + public void givenNotExistingIdThenShouldReturnException() throws EmployeeException { + when(employeeRepository.getEmployeeById(any())).thenReturn(null); + ThrowingCallable fail = () -> { + employeeService.getEmployeeInformation(20l); + }; + + assertThatCode(fail).isInstanceOf(EmployeeException.class); + } + + @Test + public void givenNullIdThenShouldReturnException() { + + ThrowingCallable fail = () -> { + employeeService.getEmployeeInformation(null); + }; + + assertThatCode(fail).isInstanceOf(EmployeeException.class); + } +}