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..02f2909 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
org.springframework.boot
spring-boot-starter-parent
- 2.1.3.RELEASE
+ 2.3.0.RELEASE
com.zoomcare
@@ -16,6 +16,7 @@
1.8
+ 1.5.5.Final
@@ -49,6 +50,21 @@
spring-boot-starter-test
test
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+ org.projectlombok
+ lombok
+ 1.18.26
+ provided
+
+
+ org.mapstruct
+ mapstruct
+ ${org.mapstruct.version}
+
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..073c16c
--- /dev/null
+++ b/src/main/java/com/zoomcare/candidatechallenge/controller/EmployeeController.java
@@ -0,0 +1,33 @@
+package com.zoomcare.candidatechallenge.controller;
+
+import com.zoomcare.candidatechallenge.entity.EmployeeEntity;
+import com.zoomcare.candidatechallenge.model.EmployeeModel;
+import com.zoomcare.candidatechallenge.service.EmployeeService;
+import lombok.RequiredArgsConstructor;
+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;
+
+@RestController
+@RequiredArgsConstructor
+@RequestMapping("/employees")
+public class EmployeeController {
+ private EmployeeService service;
+
+ @GetMapping("/{id}")
+ public ResponseEntity getEmployeeById(@PathVariable Long id){
+ EmployeeModel model = service.getEmployee(id);
+ return new ResponseEntity<>(model, HttpStatus.OK);
+ }
+
+ @GetMapping("/")
+ public ResponseEntity> getTopEmployees(){
+ List topEmployees = service.getTopEmployees();
+ return new ResponseEntity<>(topEmployees, HttpStatus.OK);
+ }
+}
diff --git a/src/main/java/com/zoomcare/candidatechallenge/entity/EmployeeEntity.java b/src/main/java/com/zoomcare/candidatechallenge/entity/EmployeeEntity.java
new file mode 100644
index 0000000..27f61f6
--- /dev/null
+++ b/src/main/java/com/zoomcare/candidatechallenge/entity/EmployeeEntity.java
@@ -0,0 +1,29 @@
+package com.zoomcare.candidatechallenge.entity;
+
+import lombok.*;
+
+import javax.persistence.*;
+import java.util.ArrayList;
+import java.util.List;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@AllArgsConstructor
+@Entity
+@Table(name="EMPLOYEE")
+public class EmployeeEntity {
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "SUPERVISOR_ID")
+ private EmployeeEntity supervisor;
+
+ @OneToMany(mappedBy = "supervisor", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
+ private List reports = new ArrayList<>();
+
+ @OneToMany(mappedBy = "employee", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
+ private List properties = new ArrayList<>();
+}
diff --git a/src/main/java/com/zoomcare/candidatechallenge/entity/PropertyEntity.java b/src/main/java/com/zoomcare/candidatechallenge/entity/PropertyEntity.java
new file mode 100644
index 0000000..78480ce
--- /dev/null
+++ b/src/main/java/com/zoomcare/candidatechallenge/entity/PropertyEntity.java
@@ -0,0 +1,24 @@
+package com.zoomcare.candidatechallenge.entity;
+
+import lombok.*;
+
+import javax.persistence.*;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@AllArgsConstructor
+@Entity
+@Table(name="PROPERTY")
+public class PropertyEntity {
+
+ @EmbeddedId
+ private PropertyIdEntity id;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "EMPLOYEE_ID", insertable = false, updatable = false)
+ private EmployeeEntity employee;
+
+ @Column(name = "VALUE")
+ private String value;
+}
diff --git a/src/main/java/com/zoomcare/candidatechallenge/entity/PropertyIdEntity.java b/src/main/java/com/zoomcare/candidatechallenge/entity/PropertyIdEntity.java
new file mode 100644
index 0000000..f7ae984
--- /dev/null
+++ b/src/main/java/com/zoomcare/candidatechallenge/entity/PropertyIdEntity.java
@@ -0,0 +1,20 @@
+package com.zoomcare.candidatechallenge.entity;
+
+import lombok.*;
+
+import javax.persistence.Column;
+import javax.persistence.Embeddable;
+import java.io.Serializable;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@EqualsAndHashCode
+@AllArgsConstructor
+@Embeddable
+public class PropertyIdEntity implements Serializable {
+ @Column(name = "EMPLOYEE_ID")
+ private Long id;
+ @Column(name = "KEY")
+ private String key;
+}
diff --git a/src/main/java/com/zoomcare/candidatechallenge/model/EmployeeModel.java b/src/main/java/com/zoomcare/candidatechallenge/model/EmployeeModel.java
new file mode 100644
index 0000000..ac28a83
--- /dev/null
+++ b/src/main/java/com/zoomcare/candidatechallenge/model/EmployeeModel.java
@@ -0,0 +1,45 @@
+package com.zoomcare.candidatechallenge.model;
+
+import com.zoomcare.candidatechallenge.entity.EmployeeEntity;
+import com.zoomcare.candidatechallenge.entity.PropertyEntity;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class EmployeeModel {
+ private Long id;
+ private Long supervisor;
+
+ private List reports = new ArrayList<>();
+ private List properties = new ArrayList<>();
+
+ public static final EmployeeModel EMPTY = new EmployeeModel();
+
+ public static final EmployeeModel parseFrom(EmployeeEntity entity){
+ EmployeeModel model = new EmployeeModel();
+ model.setId(entity.getId());
+
+ if(entity.getSupervisor()!= null && entity.getSupervisor().getId()!=null){
+ model.setSupervisor(entity.getSupervisor().getId());
+ }
+
+ if(entity.getReports()!=null && !entity.getReports().isEmpty()){
+ for(EmployeeEntity report : entity.getReports()){
+ model.getReports().add(report.getId());
+ }
+ }
+
+ if(entity.getProperties()!=null && !entity.getProperties().isEmpty()){
+ for(PropertyEntity property : entity.getProperties()){
+ model.getProperties().add(PropertyModel.parseFrom(property));
+ }
+ }
+ return model;
+ }
+}
diff --git a/src/main/java/com/zoomcare/candidatechallenge/model/PropertyModel.java b/src/main/java/com/zoomcare/candidatechallenge/model/PropertyModel.java
new file mode 100644
index 0000000..abd9176
--- /dev/null
+++ b/src/main/java/com/zoomcare/candidatechallenge/model/PropertyModel.java
@@ -0,0 +1,22 @@
+package com.zoomcare.candidatechallenge.model;
+
+import com.zoomcare.candidatechallenge.entity.PropertyEntity;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class PropertyModel {
+
+ private String key;
+ private String value;
+
+ public static PropertyModel parseFrom(PropertyEntity entity){
+ PropertyModel model = new PropertyModel();
+ model.setKey(entity.getId().getKey());
+ model.setValue(entity.getValue());
+ return model;
+ }
+}
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..d668f79
--- /dev/null
+++ b/src/main/java/com/zoomcare/candidatechallenge/repository/EmployeeRepository.java
@@ -0,0 +1,12 @@
+package com.zoomcare.candidatechallenge.repository;
+
+import com.zoomcare.candidatechallenge.entity.EmployeeEntity;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+
+@Repository
+public interface EmployeeRepository extends JpaRepository {
+ List findBySupervisorIsNull();
+}
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..fc0165c
--- /dev/null
+++ b/src/main/java/com/zoomcare/candidatechallenge/service/EmployeeService.java
@@ -0,0 +1,40 @@
+package com.zoomcare.candidatechallenge.service;
+
+import com.zoomcare.candidatechallenge.entity.EmployeeEntity;
+import com.zoomcare.candidatechallenge.model.EmployeeModel;
+import com.zoomcare.candidatechallenge.repository.EmployeeRepository;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+@Component
+@RequiredArgsConstructor
+public class EmployeeService {
+ private final EmployeeRepository employeeRepository;
+
+ public EmployeeModel getEmployee(Long id) {
+ Optional employee = employeeRepository.findById(id);
+ if (employee.isPresent()) {
+ EmployeeEntity e = employee.get();
+ EmployeeModel model = EmployeeModel.parseFrom(e);
+ return model;
+ } else {
+ return EmployeeModel.EMPTY;
+ }
+ }
+
+ public List getTopEmployees(){
+ List topEmployees = new ArrayList<>();
+ List topEntityEmployees = employeeRepository.findBySupervisorIsNull();
+
+ if(topEntityEmployees!=null && !topEntityEmployees.isEmpty()){
+ for(EmployeeEntity entity : topEntityEmployees){
+ topEmployees.add(EmployeeModel.parseFrom(entity));
+ }
+ }
+ return topEmployees;
+ }
+}
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
new file mode 100644
index 0000000..4d9ef04
--- /dev/null
+++ b/src/main/resources/application.properties
@@ -0,0 +1,16 @@
+server.port=8890
+spring.datasource.url=jdbc:h2:mem:testdb
+spring.datasource.driverClassName=org.h2.Driver
+spring.datasource.username=sa
+spring.datasource.password=
+
+spring.jpa.show-sql=true
+spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect
+spring.jpa.hibernate.ddl-auto=create-drop
+spring.jpa.defer-datasource-initialization=true
+
+spring.h2.console.enabled=true
+spring.h2.console.path=/h2-console
+
+#http://localhost:8080/h2-console
+#http://127.0.0.1:8080/h2-console
\ No newline at end of file
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
deleted file mode 100644
index 4408d17..0000000
--- a/src/main/resources/application.yml
+++ /dev/null
@@ -1,12 +0,0 @@
-spring:
- h2:
- console:
- enabled: true
-management:
- endpoints:
- web:
- exposure:
- include: "*"
- endpoint:
- health:
- show-details: always
\ No newline at end of file
diff --git a/src/main/test/com/zoomcare/candidatechallenge/ApplicationContextTest.java b/src/main/test/com/zoomcare/candidatechallenge/ApplicationContextTest.java
new file mode 100644
index 0000000..72c946c
--- /dev/null
+++ b/src/main/test/com/zoomcare/candidatechallenge/ApplicationContextTest.java
@@ -0,0 +1,19 @@
+package com.zoomcare.candidatechallenge;
+
+import com.zoomcare.candidatechallenge.controller.EmployeeController;
+import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+
+import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
+
+@SpringBootTest
+public class ApplicationContextTest {
+ @Autowired
+ private EmployeeController controller;
+
+ @Test
+ public void testContext_whenApplicationRuns_thenContextLoads(){
+ assertThat(controller).isNull();
+ }
+}
diff --git a/src/main/test/com/zoomcare/candidatechallenge/service/EmployeeServiceTest.java b/src/main/test/com/zoomcare/candidatechallenge/service/EmployeeServiceTest.java
new file mode 100644
index 0000000..79830f7
--- /dev/null
+++ b/src/main/test/com/zoomcare/candidatechallenge/service/EmployeeServiceTest.java
@@ -0,0 +1,60 @@
+package com.zoomcare.candidatechallenge.service;
+
+
+import com.zoomcare.candidatechallenge.entity.EmployeeEntity;
+import com.zoomcare.candidatechallenge.model.EmployeeModel;
+import com.zoomcare.candidatechallenge.repository.EmployeeRepository;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.Mockito.when;
+
+public class EmployeeServiceTest {
+
+ @Mock
+ private EmployeeRepository repository;
+
+ private EmployeeService service;
+
+ @BeforeEach
+ public void setup(){
+ MockitoAnnotations.initMocks(this);
+ service = new EmployeeService(repository);
+ }
+
+ @Test
+ public void testTopEmployees_whenTopRequired_thenReturnTop(){
+
+ EmployeeEntity employeeEntity = new EmployeeEntity();
+ employeeEntity.setId(Long.valueOf(1));
+ employeeEntity.setSupervisor(null);
+ List entities = new ArrayList<>();
+ entities.add(employeeEntity);
+
+ when(repository.findBySupervisorIsNull()).thenReturn(entities);
+ List topEmployees = service.getTopEmployees();
+ assertEquals(1,topEmployees.size());
+ assertEquals(EmployeeModel.parseFrom(employeeEntity),topEmployees.get(0));
+ }
+
+ @Test
+ public void testEmployee_whenEmployeeIdIsGiven_thenEmployeeReturned(){
+ EmployeeEntity employeeEntity = new EmployeeEntity();
+ employeeEntity.setId(Long.valueOf(5));
+
+ when(repository.findById(Long.valueOf(5))).thenReturn(Optional.of(employeeEntity));
+ EmployeeModel employeeModel = service.getEmployee(Long.valueOf(5));
+
+ assertNotNull(employeeModel);
+ assertEquals(employeeEntity.getId(),employeeModel.getId());
+ assertEquals(EmployeeModel.parseFrom(employeeEntity),employeeModel);
+ }
+}