From 1c384f95ae694bdff584e3aab3daaac52bcd6f29 Mon Sep 17 00:00:00 2001 From: Soumyadeep Bera Date: Sat, 21 Jan 2023 00:10:01 +0530 Subject: [PATCH] created 5 API, added few of the validation, project level exception handling, few JUNit test --- java-spring-api/build.gradle | 5 + .../controller/PatientsController.java | 57 ++++++++++ .../exception/PatientNotFoundException.java | 7 ++ .../MainExceptionHandler.java | 33 ++++++ .../javaspringapi/models/Patient.java | 102 +++++++++++++++++- .../repositories/PatientRepository.java | 50 +++++++-- .../javaspringapi/service/PatientService.java | 19 ++++ .../service/PatientServiceImpl.java | 55 ++++++++++ .../javaspringapi/util/PatientUtils.java | 16 +++ .../controller/PatientControllerTest.java | 89 +++++++++++++++ .../service/PatientServiceTest.java | 57 ++++++++++ 11 files changed, 480 insertions(+), 10 deletions(-) create mode 100644 java-spring-api/src/main/java/com/elsevier/javaspringapi/controller/PatientsController.java create mode 100644 java-spring-api/src/main/java/com/elsevier/javaspringapi/exception/PatientNotFoundException.java create mode 100644 java-spring-api/src/main/java/com/elsevier/javaspringapi/exceptionhandler/MainExceptionHandler.java create mode 100644 java-spring-api/src/main/java/com/elsevier/javaspringapi/service/PatientService.java create mode 100644 java-spring-api/src/main/java/com/elsevier/javaspringapi/service/PatientServiceImpl.java create mode 100644 java-spring-api/src/main/java/com/elsevier/javaspringapi/util/PatientUtils.java create mode 100644 java-spring-api/src/test/java/com/elsevier/javaspringapi/controller/PatientControllerTest.java create mode 100644 java-spring-api/src/test/java/com/elsevier/javaspringapi/service/PatientServiceTest.java diff --git a/java-spring-api/build.gradle b/java-spring-api/build.gradle index 7187b55..5807340 100644 --- a/java-spring-api/build.gradle +++ b/java-spring-api/build.gradle @@ -13,10 +13,15 @@ repositories { dependencies { implementation 'org.springframework.boot:spring-boot-starter-web:2.7.2' + implementation 'org.springframework.boot:spring-boot-starter-validation:2.7.2' runtimeOnly 'org.springframework.boot:spring-boot-devtools:2.7.2' testImplementation 'org.springframework.boot:spring-boot-starter-test:2.7.2' } +test { + useJUnitPlatform() +} + group = 'com.elsevier' version = '1.0-SNAPSHOT' description = 'java-sprin-api' \ No newline at end of file diff --git a/java-spring-api/src/main/java/com/elsevier/javaspringapi/controller/PatientsController.java b/java-spring-api/src/main/java/com/elsevier/javaspringapi/controller/PatientsController.java new file mode 100644 index 0000000..b9192d3 --- /dev/null +++ b/java-spring-api/src/main/java/com/elsevier/javaspringapi/controller/PatientsController.java @@ -0,0 +1,57 @@ +package com.elsevier.javaspringapi.controller; + +import com.elsevier.javaspringapi.exception.PatientNotFoundException; +import com.elsevier.javaspringapi.models.Patient; +import com.elsevier.javaspringapi.service.PatientService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.util.CollectionUtils; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; +import java.util.List; +import java.util.UUID; + +@RestController +@RequestMapping("/patients") +public class PatientsController { + + @Autowired + private PatientService patientService; + + + @GetMapping() + public ResponseEntity> fetchPatient() { + List patientList = patientService.fetchPatientList(); + if (!CollectionUtils.isEmpty(patientList)) { + return new ResponseEntity<>(patientService.fetchPatientList(), HttpStatus.OK); + } else { + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } + } + + @GetMapping("/{id}") + public ResponseEntity fetchPatientByID(@PathVariable("id") UUID id) throws PatientNotFoundException { + return new ResponseEntity<>(patientService.fetchPatientById(id), HttpStatus.OK); + } + + @PostMapping() + public ResponseEntity savePatient(@Valid @RequestBody Patient patient) { + patientService.savePatient(patient); + return new ResponseEntity<>("Patient saved Successfully!!", HttpStatus.OK); + } + + @PatchMapping("/{id}") + public ResponseEntity updatePatients(@Valid @RequestBody Patient patient, @PathVariable("id") UUID id) throws PatientNotFoundException { + patientService.updatePatients(patient, id); + return new ResponseEntity<>("Patient Updated Successfully!!", HttpStatus.OK); + } + + @DeleteMapping("/{id}") + public ResponseEntity deletePatientById(@PathVariable("id") UUID id) throws PatientNotFoundException { + patientService.deletePatientById(id); + return new ResponseEntity<>("Patient deleted Successfully!!", HttpStatus.OK); + } + +} \ No newline at end of file diff --git a/java-spring-api/src/main/java/com/elsevier/javaspringapi/exception/PatientNotFoundException.java b/java-spring-api/src/main/java/com/elsevier/javaspringapi/exception/PatientNotFoundException.java new file mode 100644 index 0000000..c7b1868 --- /dev/null +++ b/java-spring-api/src/main/java/com/elsevier/javaspringapi/exception/PatientNotFoundException.java @@ -0,0 +1,7 @@ +package com.elsevier.javaspringapi.exception; + +public class PatientNotFoundException extends Exception { + public PatientNotFoundException(String message) { + super(message); + } +} diff --git a/java-spring-api/src/main/java/com/elsevier/javaspringapi/exceptionhandler/MainExceptionHandler.java b/java-spring-api/src/main/java/com/elsevier/javaspringapi/exceptionhandler/MainExceptionHandler.java new file mode 100644 index 0000000..e1bf950 --- /dev/null +++ b/java-spring-api/src/main/java/com/elsevier/javaspringapi/exceptionhandler/MainExceptionHandler.java @@ -0,0 +1,33 @@ +package com.elsevier.javaspringapi.exceptionhandler; + +import com.elsevier.javaspringapi.exception.PatientNotFoundException; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +import java.util.HashMap; +import java.util.Map; + +@RestControllerAdvice +public class MainExceptionHandler { + + @ResponseStatus(HttpStatus.BAD_REQUEST) + @ExceptionHandler(MethodArgumentNotValidException.class) + public Map handleInvalidArgument(MethodArgumentNotValidException ex) { + Map errorMap = new HashMap<>(); + ex.getBindingResult().getFieldErrors().forEach(error -> { + errorMap.put(error.getField(), error.getDefaultMessage()); + }); + return errorMap; + } + + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) + @ExceptionHandler(PatientNotFoundException.class) + public Map handleBusinessException(PatientNotFoundException ex) { + Map errorMap = new HashMap<>(); + errorMap.put("errorMessage", ex.getMessage()); + return errorMap; + } +} diff --git a/java-spring-api/src/main/java/com/elsevier/javaspringapi/models/Patient.java b/java-spring-api/src/main/java/com/elsevier/javaspringapi/models/Patient.java index 59dc727..777208e 100644 --- a/java-spring-api/src/main/java/com/elsevier/javaspringapi/models/Patient.java +++ b/java-spring-api/src/main/java/com/elsevier/javaspringapi/models/Patient.java @@ -1,6 +1,106 @@ package com.elsevier.javaspringapi.models; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Past; +import javax.validation.constraints.Pattern; +import java.util.Date; +import java.util.UUID; + // TODO: implement this model + public class Patient { - public Patient() {} + + //id* (uuid) Cannot have 2 ids equal + @NotNull(message = "id shouldn't be null") + private UUID id; + + //firstName* (string) + @NotBlank(message = "firstName shouldn't be null or empty") + private String firstName; + + //lastName* (string - dID) + @NotBlank(message = "lastName shouldn't be null or empty") + private String lastName; + @NotNull(message = "DOB shouldn't be null") + @Past(message = "The date of birth must be in the past.") + private Date dob; + + @NotBlank(message = "SSN shouldn't be null or empty") + private String ssn; + + @Pattern(regexp = "^\\(?([0-9]{3})\\)?[-.\\s]?([0-9]{3})[-.\\s]?([0-9]{4})$", message = "Enter a valid phone number") + private String phoneNumber; + + public Patient() { + } + + public UUID getId() { + return id; + } + + public void setId(UUID id) { + this.id = id; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public Date getDob() { + return dob; + } + + public void setDob(Date dob) { + this.dob = dob; + } + + public String getSsn() { + return ssn; + } + + public void setSsn(String ssn) { + this.ssn = ssn; + } + + public String getPhoneNumber() { + return phoneNumber; + } + + public void setPhoneNumber(String phoneNumber) { + this.phoneNumber = phoneNumber; + } + + public Patient(UUID id, String firstName, String lastName, Date dob, String ssn, String phoneNumber) { + this.id = id; + this.firstName = firstName; + this.lastName = lastName; + this.dob = dob; + this.ssn = ssn; + this.phoneNumber = phoneNumber; + } + + @Override + public String toString() { + return "Patient{" + + "id=" + id + + ", firstName='" + firstName + '\'' + + ", lastName='" + lastName + '\'' + + ", dob=" + dob + + ", ssn='" + ssn + '\'' + + ", phoneNumber='" + phoneNumber + '\'' + + '}'; + } } diff --git a/java-spring-api/src/main/java/com/elsevier/javaspringapi/repositories/PatientRepository.java b/java-spring-api/src/main/java/com/elsevier/javaspringapi/repositories/PatientRepository.java index 5be370b..8e35d97 100644 --- a/java-spring-api/src/main/java/com/elsevier/javaspringapi/repositories/PatientRepository.java +++ b/java-spring-api/src/main/java/com/elsevier/javaspringapi/repositories/PatientRepository.java @@ -1,35 +1,67 @@ package com.elsevier.javaspringapi.repositories; import com.elsevier.javaspringapi.models.Patient; -import jdk.jshell.spi.ExecutionControl; +import com.elsevier.javaspringapi.util.PatientUtils; +import org.springframework.stereotype.Repository; +import java.text.ParseException; import java.util.ArrayList; import java.util.List; import java.util.UUID; + +@Repository public class PatientRepository { + // TODO: Once the patient model is complete add some test patients here, 3 should be fine. - private List patients = new ArrayList<>(){{ - add(new Patient()); - add(new Patient()); - add(new Patient()); + + private List patients = new ArrayList<>() {{ + try { + add(new Patient(UUID.fromString("4ced7ec1-0048-4907-a871-d93bfb933ece"), "Rakesh", "Singha", PatientUtils.generateDate("01-01-1900"), "SSN", "123-321-3456")); + add(new Patient(UUID.fromString("2742971e-175f-41cd-a7d7-70d74eb70f30"), "Amit", "Kar", PatientUtils.generateDate("01-01-1980"), "SSN", "123-321-3456")); + add(new Patient(UUID.fromString("fb61d092-953c-4c72-b382-205377d4c9ea"), "Pramod", "Das", PatientUtils.generateDate("01-01-2020"), "SSN", "123-321-3456")); + + } catch (ParseException e) { + throw new RuntimeException(e); + } }}; // TODO: Implement these methods public List getPatients() { - return null; + return patients; } public Patient getPatient(UUID id) { - return null; + return patients.stream().filter(patient -> patient.getId().equals(id)).findFirst().get(); } public void save(Patient patient) { - + Patient newPatient = new Patient(); + newPatient.setId(patient.getId()); + newPatient.setFirstName(patient.getFirstName()); + newPatient.setLastName(patient.getLastName()); + newPatient.setDob(patient.getDob()); + newPatient.setSsn(patient.getSsn()); + newPatient.setPhoneNumber(patient.getPhoneNumber()); + patients.add(newPatient); } - public void delete(UUID id) { + public boolean delete(UUID id) { + return patients.removeIf(patient -> patient.getId().equals(id)); + } + public Patient updatePatients(Patient patient, UUID id) { + for (Patient patients : patients) { + if (patients.getId().equals(id)) { + patients.setFirstName(patient.getFirstName()); + patients.setId(patient.getId()); + patients.setFirstName(patient.getLastName()); + return patients; + } + } + return null; } + + } diff --git a/java-spring-api/src/main/java/com/elsevier/javaspringapi/service/PatientService.java b/java-spring-api/src/main/java/com/elsevier/javaspringapi/service/PatientService.java new file mode 100644 index 0000000..0597ce6 --- /dev/null +++ b/java-spring-api/src/main/java/com/elsevier/javaspringapi/service/PatientService.java @@ -0,0 +1,19 @@ +package com.elsevier.javaspringapi.service; + +import com.elsevier.javaspringapi.exception.PatientNotFoundException; +import com.elsevier.javaspringapi.models.Patient; + +import java.util.List; +import java.util.UUID; + +public interface PatientService { + public List fetchPatientList(); + + public void savePatient(Patient patient); + + void deletePatientById(UUID id) throws PatientNotFoundException; + + Patient fetchPatientById(UUID id) throws PatientNotFoundException; + + void updatePatients(Patient patient, UUID id) throws PatientNotFoundException; +} diff --git a/java-spring-api/src/main/java/com/elsevier/javaspringapi/service/PatientServiceImpl.java b/java-spring-api/src/main/java/com/elsevier/javaspringapi/service/PatientServiceImpl.java new file mode 100644 index 0000000..a253d88 --- /dev/null +++ b/java-spring-api/src/main/java/com/elsevier/javaspringapi/service/PatientServiceImpl.java @@ -0,0 +1,55 @@ +package com.elsevier.javaspringapi.service; + +import com.elsevier.javaspringapi.exception.PatientNotFoundException; +import com.elsevier.javaspringapi.models.Patient; +import com.elsevier.javaspringapi.repositories.PatientRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.UUID; + +@Service +public class PatientServiceImpl implements PatientService { + + @Autowired + private PatientRepository patientRepository; + + @Override + public List fetchPatientList() { + return patientRepository.getPatients(); + } + + @Override + public void savePatient(Patient patient) { + patientRepository.save(patient); + } + + @Override + public void deletePatientById(UUID id) throws PatientNotFoundException { + if (!patientRepository.delete(id)) { + throw new PatientNotFoundException("Patient Not Available to delete with Id:" + id); + } + } + + @Override + public Patient fetchPatientById(UUID id) throws PatientNotFoundException { + Optional patient = null; + try { + patient = Optional.ofNullable(patientRepository.getPatient(id)); + } catch (Exception ex) { + throw new PatientNotFoundException("Patient Not Available with Id:" + id); + } + return patient.get(); + } + + @Override + public void updatePatients(Patient patient, UUID id) throws PatientNotFoundException { + Patient newPatient = patientRepository.updatePatients(patient, id); + if (!Objects.nonNull(newPatient)) { + throw new PatientNotFoundException("Patient Not Available to update with Id:" + id); + } + } +} diff --git a/java-spring-api/src/main/java/com/elsevier/javaspringapi/util/PatientUtils.java b/java-spring-api/src/main/java/com/elsevier/javaspringapi/util/PatientUtils.java new file mode 100644 index 0000000..2a60448 --- /dev/null +++ b/java-spring-api/src/main/java/com/elsevier/javaspringapi/util/PatientUtils.java @@ -0,0 +1,16 @@ +package com.elsevier.javaspringapi.util; + +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; + +public class PatientUtils { + + + //This method is created to generate Date based upon Input + public static Date generateDate(String dateValue) throws ParseException { + DateFormat df = new SimpleDateFormat("MM-dd-yyyy"); + return df.parse(dateValue); + } +} diff --git a/java-spring-api/src/test/java/com/elsevier/javaspringapi/controller/PatientControllerTest.java b/java-spring-api/src/test/java/com/elsevier/javaspringapi/controller/PatientControllerTest.java new file mode 100644 index 0000000..2474067 --- /dev/null +++ b/java-spring-api/src/test/java/com/elsevier/javaspringapi/controller/PatientControllerTest.java @@ -0,0 +1,89 @@ +package com.elsevier.javaspringapi.controller; + +import com.elsevier.javaspringapi.models.Patient; +import com.elsevier.javaspringapi.service.PatientService; +import com.elsevier.javaspringapi.util.PatientUtils; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; + +import java.text.ParseException; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +import static org.mockito.BDDMockito.given; +import static org.mockito.BDDMockito.willDoNothing; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.hamcrest.CoreMatchers.is; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +@WebMvcTest(PatientsController.class) + class PatientControllerTest { + + @Autowired + private MockMvc mockMvc; + + + @MockBean + private PatientService patientService; + + private List patientList; + + @BeforeEach + void setUp() { + patientList = new ArrayList<>() {{ + try { + add(new Patient(UUID.fromString("4ced7ec1-0048-4907-a871-d93bfb933ece"), "Rakesh", "Singha", PatientUtils.generateDate("01-01-1900"), "SSN", "123-321-3456")); + add(new Patient(UUID.fromString("2742971e-175f-41cd-a7d7-70d74eb70f30"), "Amit", "Kar", PatientUtils.generateDate("01-01-1980"), "SSN", "123-321-3456")); + add(new Patient(UUID.fromString("fb61d092-953c-4c72-b382-205377d4c9ea"), "Pramod", "Das", PatientUtils.generateDate("01-01-2020"), "SSN", "123-321-3456")); + } catch (ParseException e) { + throw new RuntimeException(e); + } + }}; + } + + // JUnit test for Get All employees REST API + @Test + public void whenGetAllPatient_thenReturnPatientList() throws Exception{ + given(patientService.fetchPatientList()).willReturn(patientList); + ResultActions response = mockMvc.perform(get("/patients")); + + response.andExpect(status().isOk()) + .andDo(print()) + .andExpect(jsonPath("$.size()", + is(patientList.size()))); + + } + + // JUnit test for Get patients by ID REST API + @Test + public void GetPatientByIdTest() throws Exception{ + Mockito.when(patientService.fetchPatientById(UUID.fromString("4ced7ec1-0048-4907-a871-d93bfb933ece"))) + .thenReturn(patientList.get(0)); + + mockMvc.perform(get("/patients/4ced7ec1-0048-4907-a871-d93bfb933ece") + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()).andExpect(jsonPath("$.firstName"). + value(patientList.get(0).getFirstName())); + } + + @Test + public void deletePatientByIdTest() throws Exception{ + UUID patientID = UUID.fromString("4ced7ec1-0048-4907-a871-d93bfb933ece"); + willDoNothing().given(patientService).deletePatientById(patientID); + ResultActions response = mockMvc.perform(delete("/patients/{id}", patientID)); + response.andExpect(status().isOk()) + .andDo(print()); + } + + +} diff --git a/java-spring-api/src/test/java/com/elsevier/javaspringapi/service/PatientServiceTest.java b/java-spring-api/src/test/java/com/elsevier/javaspringapi/service/PatientServiceTest.java new file mode 100644 index 0000000..dd0048b --- /dev/null +++ b/java-spring-api/src/test/java/com/elsevier/javaspringapi/service/PatientServiceTest.java @@ -0,0 +1,57 @@ +package com.elsevier.javaspringapi.service; + +import com.elsevier.javaspringapi.exception.PatientNotFoundException; +import com.elsevier.javaspringapi.models.Patient; +import com.elsevier.javaspringapi.repositories.PatientRepository; +import com.elsevier.javaspringapi.util.PatientUtils; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; + +import java.text.ParseException; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.when; + +@SpringBootTest +public class PatientServiceTest { + + @MockBean + private PatientRepository patientRepository; + + @Autowired + PatientService patientService; + + private List patientList; + + @BeforeEach + void setUp() { + patientList = new ArrayList<>() {{ + try { + add(new Patient(UUID.fromString("4ced7ec1-0048-4907-a871-d93bfb933ece"), "Rakesh", "Singha", PatientUtils.generateDate("01-01-1900"), "SSN", "123-321-3456")); + add(new Patient(UUID.fromString("2742971e-175f-41cd-a7d7-70d74eb70f30"), "Amit", "Kar", PatientUtils.generateDate("01-01-1980"), "SSN", "123-321-3456")); + add(new Patient(UUID.fromString("fb61d092-953c-4c72-b382-205377d4c9ea"), "Pramod", "Das", PatientUtils.generateDate("01-01-2020"), "SSN", "123-321-3456")); + } catch (ParseException e) { + throw new RuntimeException(e); + } + }}; + } + //Junit to fetch patient by ID + @Test + public void fetchPatientById() throws PatientNotFoundException { + String firstName = "Rakesh"; + String lastName = "Singha"; + when(patientRepository.getPatient(UUID.fromString("4ced7ec1-0048-4907-a871-d93bfb933ece"))).thenReturn(patientList.get(0)); + Patient patient = + patientService.fetchPatientById(UUID.fromString("4ced7ec1-0048-4907-a871-d93bfb933ece")); + + assertEquals(firstName, patient.getFirstName()); + assertEquals(lastName, patient.getLastName()); + + } +}