From de27d99f7a64fd650daa8a7b7af0fc93ce9e1e5b Mon Sep 17 00:00:00 2001 From: L-Steinmacher Date: Mon, 19 Apr 2021 13:52:36 -0700 Subject: [PATCH 1/3] first Commit --- .../exceptions/CustomErrorDetails.java | 38 ++++++++ .../exceptions/ResourceNotFoundExeption.java | 9 ++ .../handelers/RestExceptionHandler.java | 49 ++++++++++ .../schools/models/ErrorDetail.java | 96 +++++++++++++++++++ .../schools/models/ValidationError.java | 39 ++++++++ .../schools/services/HelperFunctions.java | 10 ++ .../schools/services/HelperFunctionsImpl.java | 36 +++++++ .../src/main/resources/application.properties | 5 + 8 files changed, 282 insertions(+) create mode 100644 schools/src/main/java/com/lambdaschool/schools/exceptions/CustomErrorDetails.java create mode 100644 schools/src/main/java/com/lambdaschool/schools/exceptions/ResourceNotFoundExeption.java create mode 100644 schools/src/main/java/com/lambdaschool/schools/handelers/RestExceptionHandler.java create mode 100644 schools/src/main/java/com/lambdaschool/schools/models/ErrorDetail.java create mode 100644 schools/src/main/java/com/lambdaschool/schools/models/ValidationError.java create mode 100644 schools/src/main/java/com/lambdaschool/schools/services/HelperFunctions.java create mode 100644 schools/src/main/java/com/lambdaschool/schools/services/HelperFunctionsImpl.java diff --git a/schools/src/main/java/com/lambdaschool/schools/exceptions/CustomErrorDetails.java b/schools/src/main/java/com/lambdaschool/schools/exceptions/CustomErrorDetails.java new file mode 100644 index 00000000..d4cce63a --- /dev/null +++ b/schools/src/main/java/com/lambdaschool/schools/exceptions/CustomErrorDetails.java @@ -0,0 +1,38 @@ +package com.lambdaschool.schools.exceptions; + +import com.lambdaschool.schools.services.HelperFunctions; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.web.servlet.error.DefaultErrorAttributes; +import org.springframework.stereotype.Component; +import org.springframework.web.context.request.WebRequest; + +import java.util.Date; +import java.util.LinkedHashMap; +import java.util.Map; + +@Component +public class CustomErrorDetails extends DefaultErrorAttributes +{ + @Autowired + HelperFunctions helperFunctions; + + @Override + public Map getErrorAttributes( + WebRequest webRequest, + boolean includeStackTrace) + { + Map errorAttributes = super.getErrorAttributes(webRequest, includeStackTrace); + + Map rtnAttributes = new LinkedHashMap<>(); + + rtnAttributes.put("title", errorAttributes.get("error")); + rtnAttributes.put("status", errorAttributes.get("status")); + rtnAttributes.put("detail", errorAttributes.get("message")); + rtnAttributes.put("timestamp", new Date()); + rtnAttributes.put("developer", "path: " + errorAttributes.get("path")); + + rtnAttributes.put("errors", helperFunctions.getValidationErrors(this.getError(webRequest))); + + return rtnAttributes; + } +} diff --git a/schools/src/main/java/com/lambdaschool/schools/exceptions/ResourceNotFoundExeption.java b/schools/src/main/java/com/lambdaschool/schools/exceptions/ResourceNotFoundExeption.java new file mode 100644 index 00000000..3891ffa9 --- /dev/null +++ b/schools/src/main/java/com/lambdaschool/schools/exceptions/ResourceNotFoundExeption.java @@ -0,0 +1,9 @@ +package com.lambdaschool.schools.exceptions; + +public class ResourceNotFoundExeption extends RuntimeException +{ + public ResourceNotFoundExeption(String message) + { + super("Found an issue with School: " + message); + } +} diff --git a/schools/src/main/java/com/lambdaschool/schools/handelers/RestExceptionHandler.java b/schools/src/main/java/com/lambdaschool/schools/handelers/RestExceptionHandler.java new file mode 100644 index 00000000..8fa35c71 --- /dev/null +++ b/schools/src/main/java/com/lambdaschool/schools/handelers/RestExceptionHandler.java @@ -0,0 +1,49 @@ +package com.lambdaschool.schools.handelers; + +import com.lambdaschool.schools.exceptions.ResourceNotFoundExeption; +import com.lambdaschool.schools.models.ErrorDetail; +import com.lambdaschool.schools.services.HelperFunctions; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.web.servlet.error.DefaultErrorAttributes; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.context.request.WebRequest; +import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; + +import java.util.Date; +import java.util.Map; + +@RestControllerAdvice +@Order(Ordered.HIGHEST_PRECEDENCE) +public class RestExceptionHandler extends ResponseEntityExceptionHandler +{ + @Autowired + private HelperFunctions helperFunctions; + /* + title + status + detail + timestamp + developerMessage + */ + + @ExceptionHandler(ResourceNotFoundExeption.class) + public ResponseEntity handleResourceNotFoundException(ResourceNotFoundExeption rnfe) + { + ErrorDetail errorDetail = new ErrorDetail(); + errorDetail.setTimestamp(new Date()); + errorDetail.setTitle("Resource Not Found Bubba!"); + errorDetail.setStatus(HttpStatus.NOT_FOUND.value()); + errorDetail.setDetail(rnfe.getMessage()); + errorDetail.setDeveloper(rnfe.getClass().getName()); + errorDetail.setErrors(helperFunctions.getValidationErrors(rnfe)); + return new ResponseEntity<>(errorDetail, null, HttpStatus.NOT_FOUND); + } + + +} diff --git a/schools/src/main/java/com/lambdaschool/schools/models/ErrorDetail.java b/schools/src/main/java/com/lambdaschool/schools/models/ErrorDetail.java new file mode 100644 index 00000000..f5478f7b --- /dev/null +++ b/schools/src/main/java/com/lambdaschool/schools/models/ErrorDetail.java @@ -0,0 +1,96 @@ +package com.lambdaschool.schools.models; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +public class ErrorDetail +{ + + private String title; + private int status; + private String detail; + private Date timestamp; + private String developer; + private List errors = new ArrayList<>(); + + public ErrorDetail() + { + } + + public ErrorDetail( + String title, + int status, + String detail, + Date timestamp, + String developer, + List errors) + { + this.title = title; + this.status = status; + this.detail = detail; + this.timestamp = timestamp; + this.developer = developer; + this.errors = errors; + } + + public String getTitle() + { + return title; + } + + public void setTitle(String title) + { + this.title = title; + } + + public int getStatus() + { + return status; + } + + public void setStatus(int status) + { + this.status = status; + } + + public String getDetail() + { + return detail; + } + + public void setDetail(String detail) + { + this.detail = detail; + } + + public Date getTimestamp() + { + return timestamp; + } + + public void setTimestamp(Date timestamp) + { + this.timestamp = timestamp; + } + + public String getDeveloper() + { + return developer; + } + + public void setDeveloper(String developer) + { + this.developer = developer; + } + + public List getErrors() + { + return errors; + } + + public void setErrors(List errors) + { + this.errors = errors; + } +} diff --git a/schools/src/main/java/com/lambdaschool/schools/models/ValidationError.java b/schools/src/main/java/com/lambdaschool/schools/models/ValidationError.java new file mode 100644 index 00000000..483a5e57 --- /dev/null +++ b/schools/src/main/java/com/lambdaschool/schools/models/ValidationError.java @@ -0,0 +1,39 @@ +package com.lambdaschool.schools.models; + +public class ValidationError +{ + private String fieldname; + private String message; + + public ValidationError() + { + } + + public ValidationError( + String fieldname, + String message) + { + this.fieldname = fieldname; + this.message = message; + } + + public String getFieldname() + { + return fieldname; + } + + public void setFieldname(String fieldname) + { + this.fieldname = fieldname; + } + + public String getMessage() + { + return message; + } + + public void setMessage(String message) + { + this.message = message; + } +} diff --git a/schools/src/main/java/com/lambdaschool/schools/services/HelperFunctions.java b/schools/src/main/java/com/lambdaschool/schools/services/HelperFunctions.java new file mode 100644 index 00000000..fa82e506 --- /dev/null +++ b/schools/src/main/java/com/lambdaschool/schools/services/HelperFunctions.java @@ -0,0 +1,10 @@ +package com.lambdaschool.schools.services; + +import com.lambdaschool.schools.models.ValidationError; + +import java.util.List; + +public interface HelperFunctions +{ + List getValidationErrors(Throwable cause); +} diff --git a/schools/src/main/java/com/lambdaschool/schools/services/HelperFunctionsImpl.java b/schools/src/main/java/com/lambdaschool/schools/services/HelperFunctionsImpl.java new file mode 100644 index 00000000..b85a0690 --- /dev/null +++ b/schools/src/main/java/com/lambdaschool/schools/services/HelperFunctionsImpl.java @@ -0,0 +1,36 @@ +package com.lambdaschool.schools.services; + +import com.lambdaschool.schools.models.ValidationError; +import org.springframework.stereotype.Service; +import org.springframework.validation.FieldError; +import org.springframework.web.bind.MethodArgumentNotValidException; + +import javax.validation.ConstraintViolationException; +import java.util.ArrayList; +import java.util.List; + +@Service(value = "helperFunctions") +public class HelperFunctionsImpl implements HelperFunctions +{ + @Override + public List getValidationErrors(Throwable cause) + { + List validationErrorList = new ArrayList<>(); + + while (cause != null && !(cause instanceof ConstraintViolationException || cause instanceof MethodArgumentNotValidException)) + { + cause = cause.getCause(); + } + + if (cause != null) + { + if (cause instanceof ConstraintViolationException) + { + ConstraintViolationException ex = (ConstraintViolationException)cause; + + ValidationError newVE = new ValidationError(); + newVE.setMessage(); + } + } + } +} diff --git a/schools/src/main/resources/application.properties b/schools/src/main/resources/application.properties index 9758fe0c..4da5be49 100644 --- a/schools/src/main/resources/application.properties +++ b/schools/src/main/resources/application.properties @@ -23,3 +23,8 @@ spring.datasource.initialization-mode=always # spring.jpa.hibernate.ddl-auto=update # since we have our data in SeedData, do not also load it from data.sql # spring.datasource.initialization-mode=never +# +server.error.whitelabel.enabled=false +# +spring.mvc.throw-exception-if-no-handler-found=true +spring.resources.add-mappings=false From 1c564cab57a88405319fc82027ff48fe9c1e4855 Mon Sep 17 00:00:00 2001 From: L-Steinmacher Date: Mon, 19 Apr 2021 14:37:23 -0700 Subject: [PATCH 2/3] MVP? --- .../zoos/services/ZooServiceImpl.html | 8 +++--- .../handelers/RestExceptionHandler.java | 26 +++++++++++++------ .../schools/services/CoursesServiceImpl.java | 13 +++++----- .../schools/services/HelperFunctionsImpl.java | 23 +++++++++++++--- .../schools/services/StudentServiceImpl.java | 8 +++--- 5 files changed, 53 insertions(+), 25 deletions(-) diff --git a/schools/javadocs/com/lambdaschool/zoos/services/ZooServiceImpl.html b/schools/javadocs/com/lambdaschool/zoos/services/ZooServiceImpl.html index 10bef294..2f7c278e 100644 --- a/schools/javadocs/com/lambdaschool/zoos/services/ZooServiceImpl.html +++ b/schools/javadocs/com/lambdaschool/zoos/services/ZooServiceImpl.html @@ -376,7 +376,7 @@

update

delete

@Transactional
 public void delete​(long id)
-            throws javax.persistence.EntityNotFoundException
+ throws javax.persistence.ResourceNotFoundExeption
Description copied from interface: ZooService
Deletes the course record, it student course combinations, and its telephone items from the database based off of the provided primary key
@@ -385,7 +385,7 @@

delete

Parameters:
id - id The primary key (long) of the course you seek.
Throws:
-
javax.persistence.EntityNotFoundException
+
javax.persistence.ResourceNotFoundExeption
@@ -438,7 +438,7 @@

saveZooAnimalCombo

  • findZooByLikeName

    public java.util.ArrayList<Zoo> findZooByLikeName​(java.lang.String name)
    -                                           throws javax.persistence.EntityNotFoundException
    + throws javax.persistence.ResourceNotFoundExeption
    Description copied from interface: ZooService
    A list of all zoos whose name contains the given substring A Stretch Goal
    @@ -450,7 +450,7 @@

    findZooByLikeName

    Returns:
    List of zoos whose name contains the given substring
    Throws:
    -
    javax.persistence.EntityNotFoundException
    +
    javax.persistence.ResourceNotFoundExeption
  • diff --git a/schools/src/main/java/com/lambdaschool/schools/handelers/RestExceptionHandler.java b/schools/src/main/java/com/lambdaschool/schools/handelers/RestExceptionHandler.java index 8fa35c71..d4957ddb 100644 --- a/schools/src/main/java/com/lambdaschool/schools/handelers/RestExceptionHandler.java +++ b/schools/src/main/java/com/lambdaschool/schools/handelers/RestExceptionHandler.java @@ -7,6 +7,7 @@ import org.springframework.boot.web.servlet.error.DefaultErrorAttributes; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; +import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; @@ -24,13 +25,6 @@ public class RestExceptionHandler extends ResponseEntityExceptionHandler { @Autowired private HelperFunctions helperFunctions; - /* - title - status - detail - timestamp - developerMessage - */ @ExceptionHandler(ResourceNotFoundExeption.class) public ResponseEntity handleResourceNotFoundException(ResourceNotFoundExeption rnfe) @@ -45,5 +39,21 @@ public ResponseEntity handleResourceNotFoundException(ResourceNotFoundExeptio return new ResponseEntity<>(errorDetail, null, HttpStatus.NOT_FOUND); } - + @Override + protected ResponseEntity handleExceptionInternal( + Exception ex, + Object body, + HttpHeaders headers, + HttpStatus status, + WebRequest request) + { + ErrorDetail errorDetail = new ErrorDetail(); + errorDetail.setTimestamp(new Date()); + errorDetail.setTitle("REST Internal Exception"); + errorDetail.setStatus(status.value()); + errorDetail.setDetail(ex.getMessage()); + errorDetail.setDeveloper(ex.getClass().getName()); + errorDetail.setErrors(helperFunctions.getValidationErrors(ex)); + return new ResponseEntity<>(errorDetail,null,status); + } } diff --git a/schools/src/main/java/com/lambdaschool/schools/services/CoursesServiceImpl.java b/schools/src/main/java/com/lambdaschool/schools/services/CoursesServiceImpl.java index 77861321..8440f9e9 100644 --- a/schools/src/main/java/com/lambdaschool/schools/services/CoursesServiceImpl.java +++ b/schools/src/main/java/com/lambdaschool/schools/services/CoursesServiceImpl.java @@ -1,5 +1,6 @@ package com.lambdaschool.schools.services; +import com.lambdaschool.schools.exceptions.ResourceNotFoundExeption; import com.lambdaschool.schools.models.Course; import com.lambdaschool.schools.models.Instructor; import com.lambdaschool.schools.models.StudCourses; @@ -11,7 +12,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import javax.persistence.EntityNotFoundException; + import java.util.ArrayList; import java.util.List; @@ -58,7 +59,7 @@ public List findAll() public Course findCourseById(long id) { return courserepos.findById(id) - .orElseThrow(() -> new EntityNotFoundException("Course id " + id + " not found!")); + .orElseThrow(() -> new ResourceNotFoundExeption("Course id " + id + " not found!")); } @Transactional @@ -66,7 +67,7 @@ public Course findCourseById(long id) public void delete(long id) { courserepos.findById(id) - .orElseThrow(() -> new EntityNotFoundException("Course id " + id + " not found!")); + .orElseThrow(() -> new ResourceNotFoundExeption("Course id " + id + " not found!")); courserepos.deleteById(id); } @@ -79,7 +80,7 @@ public Course save(Course course) if (course.getCourseid() != 0) { Course oldCourse = courserepos.findById(course.getCourseid()) - .orElseThrow(() -> new EntityNotFoundException("Course id " + course.getCourseid() + " not found!")); + .orElseThrow(() -> new ResourceNotFoundExeption("Course id " + course.getCourseid() + " not found!")); newCourse.setCourseid(course.getCourseid()); } @@ -87,7 +88,7 @@ public Course save(Course course) newCourse.setCoursename(course.getCoursename()); Instructor newInstructor = instructorrepos.findById(course.getInstructor() .getInstructorid()) - .orElseThrow(() -> new EntityNotFoundException("Instructor id " + course.getInstructor() + .orElseThrow(() -> new ResourceNotFoundExeption("Instructor id " + course.getInstructor() .getInstructorid() + " not found!")); newCourse.setInstructor(newInstructor); @@ -97,7 +98,7 @@ public Course save(Course course) { Student newStudent = studentrepos.findById(sc.getStudent() .getStudentid()) - .orElseThrow(() -> new EntityNotFoundException("Instructor id " + sc.getStudent() + .orElseThrow(() -> new ResourceNotFoundExeption("Instructor id " + sc.getStudent() .getStudentid() + " not found!")); newCourse.getStudents() diff --git a/schools/src/main/java/com/lambdaschool/schools/services/HelperFunctionsImpl.java b/schools/src/main/java/com/lambdaschool/schools/services/HelperFunctionsImpl.java index b85a0690..6b53f7d1 100644 --- a/schools/src/main/java/com/lambdaschool/schools/services/HelperFunctionsImpl.java +++ b/schools/src/main/java/com/lambdaschool/schools/services/HelperFunctionsImpl.java @@ -1,11 +1,11 @@ package com.lambdaschool.schools.services; import com.lambdaschool.schools.models.ValidationError; +import org.hibernate.exception.ConstraintViolationException; import org.springframework.stereotype.Service; import org.springframework.validation.FieldError; import org.springframework.web.bind.MethodArgumentNotValidException; -import javax.validation.ConstraintViolationException; import java.util.ArrayList; import java.util.List; @@ -17,7 +17,7 @@ public List getValidationErrors(Throwable cause) { List validationErrorList = new ArrayList<>(); - while (cause != null && !(cause instanceof ConstraintViolationException || cause instanceof MethodArgumentNotValidException)) + while (cause != null && !(cause instanceof org.hibernate.exception.ConstraintViolationException || cause instanceof MethodArgumentNotValidException)) { cause = cause.getCause(); } @@ -29,8 +29,25 @@ public List getValidationErrors(Throwable cause) ConstraintViolationException ex = (ConstraintViolationException)cause; ValidationError newVE = new ValidationError(); - newVE.setMessage(); + newVE.setMessage(ex.getConstraintName()); + newVE.setFieldname(ex.getMessage()); + + validationErrorList.add(newVE); + }else + { + MethodArgumentNotValidException ex = (MethodArgumentNotValidException)cause; + + List fieldErrors = ex.getBindingResult().getFieldErrors(); + for (FieldError fe : fieldErrors) + { + ValidationError newVE = new ValidationError(); + newVE.setFieldname(fe.getField()); + newVE.setMessage(fe.getDefaultMessage()); + + validationErrorList.add(newVE); + } } } + return validationErrorList; } } diff --git a/schools/src/main/java/com/lambdaschool/schools/services/StudentServiceImpl.java b/schools/src/main/java/com/lambdaschool/schools/services/StudentServiceImpl.java index 1ad495cd..f5243783 100644 --- a/schools/src/main/java/com/lambdaschool/schools/services/StudentServiceImpl.java +++ b/schools/src/main/java/com/lambdaschool/schools/services/StudentServiceImpl.java @@ -1,5 +1,6 @@ package com.lambdaschool.schools.services; +import com.lambdaschool.schools.exceptions.ResourceNotFoundExeption; import com.lambdaschool.schools.models.Course; import com.lambdaschool.schools.models.StudCourses; import com.lambdaschool.schools.models.Student; @@ -8,7 +9,6 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import javax.persistence.EntityNotFoundException; import java.util.ArrayList; import java.util.List; @@ -49,7 +49,7 @@ public List findAll() public Student findStudentById(long id) { return studentrepos.findById(id) - .orElseThrow(() -> new EntityNotFoundException("Student id " + id + " not found!")); + .orElseThrow(() -> new ResourceNotFoundExeption("Student id " + id + " not found!")); } @Transactional @@ -57,7 +57,7 @@ public Student findStudentById(long id) public void delete(long id) { studentrepos.findById(id) - .orElseThrow(() -> new EntityNotFoundException("Student id " + id + " not found!")); + .orElseThrow(() -> new ResourceNotFoundExeption("Student id " + id + " not found!")); studentrepos.deleteById(id); } @@ -70,7 +70,7 @@ public Student save(Student student) if (student.getStudentid() != 0) { Student oldStudent = studentrepos.findById(student.getStudentid()) - .orElseThrow(() -> new EntityNotFoundException("Student id " + student.getStudentid() + " not found!")); + .orElseThrow(() -> new ResourceNotFoundExeption("Student id " + student.getStudentid() + " not found!")); newStudent.setStudentid(student.getStudentid()); } From ef2d1957af07f37d885a614c22ab5ddfde52b65d Mon Sep 17 00:00:00 2001 From: L-Steinmacher Date: Mon, 19 Apr 2021 18:24:42 -0700 Subject: [PATCH 3/3] Swagger Working and working on advice --- schools/pom.xml | 23 ++++++++ .../schools/SchoolsApplication.java | 39 ++++++++++++- .../schools/config/Swagger2Config.java | 58 +++++++++++++++++++ .../schools/config/SwaggerWebMVC.java | 36 ++++++++++++ .../controllers/InstructorController.java | 29 ++++++++++ .../controllers/StudentController.java | 13 +++++ .../lambdaschool/schools/models/Course.java | 2 +- .../schools/models/Instructor.java | 13 +++++ .../lambdaschool/schools/models/Student.java | 6 ++ .../com/lambdaschool/schools/models/slip.java | 35 +++++++++++ .../repositories/InstructorRepository.java | 3 +- .../schools/services/InstructorService.java | 8 +++ .../services/InstructorServiceImpl.java | 22 +++++++ 13 files changed, 282 insertions(+), 5 deletions(-) create mode 100644 schools/src/main/java/com/lambdaschool/schools/config/Swagger2Config.java create mode 100644 schools/src/main/java/com/lambdaschool/schools/config/SwaggerWebMVC.java create mode 100644 schools/src/main/java/com/lambdaschool/schools/controllers/InstructorController.java create mode 100644 schools/src/main/java/com/lambdaschool/schools/models/slip.java create mode 100644 schools/src/main/java/com/lambdaschool/schools/services/InstructorService.java create mode 100644 schools/src/main/java/com/lambdaschool/schools/services/InstructorServiceImpl.java diff --git a/schools/pom.xml b/schools/pom.xml index e68d108f..236c3d85 100644 --- a/schools/pom.xml +++ b/schools/pom.xml @@ -43,6 +43,29 @@ spring-boot-starter-test test + + + + + io.springfox + springfox-swagger2 + 2.9.2 + + + + + io.springfox + springfox-swagger-ui + 2.9.2 + + + + io.springfox + springfox-bean-validators + 2.9.2 + + + diff --git a/schools/src/main/java/com/lambdaschool/schools/SchoolsApplication.java b/schools/src/main/java/com/lambdaschool/schools/SchoolsApplication.java index ae94d2dc..b88a8bcd 100644 --- a/schools/src/main/java/com/lambdaschool/schools/SchoolsApplication.java +++ b/schools/src/main/java/com/lambdaschool/schools/SchoolsApplication.java @@ -1,8 +1,17 @@ package com.lambdaschool.schools; +import com.lambdaschool.schools.models.slip; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.core.ParameterizedTypeReference; import org.springframework.data.jpa.repository.config.EnableJpaAuditing; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.web.client.RestTemplate; + +import java.util.Collections; /** * Main class to start the application. @@ -17,10 +26,36 @@ public class SchoolsApplication * * @param args Not used in this application. */ + public static void main(String[] args) { - SpringApplication.run(SchoolsApplication.class, - args); + /* + * Creates the object that is needed to do a client side Rest API call. + * We are the client getting data from a remote API. + */ + RestTemplate restTemplate = new RestTemplate(); + + // we need to tell our RestTemplate what format to expect + MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(); + // a couple of common formats + // converter.setSupportedMediaTypes(Collections.singletonList(MediaType.TEXT_HTML)); + // converter.setSupportedMediaTypes(Collections.singletonList(MediaType.APPLICATION_JSON)); + // or we can accept all formats! Easiest but least secure + converter.setSupportedMediaTypes(Collections.singletonList(MediaType.ALL)); + restTemplate.getMessageConverters().add(converter); + + // create the url to access the API + String requestURL = "https://api.adviceslip.com/advice"; + // create the responseType expected. Notice the YearFact is the data type we are expecting back from the API! + ParameterizedTypeReference responseType = new ParameterizedTypeReference() + { + }; + + ResponseEntity responseEntity = restTemplate.exchange(requestURL, HttpMethod.GET, null, responseType); + slip ourslip = responseEntity.getBody(); + System.out.println(ourslip); + + SpringApplication.run(SchoolsApplication.class, args); } } diff --git a/schools/src/main/java/com/lambdaschool/schools/config/Swagger2Config.java b/schools/src/main/java/com/lambdaschool/schools/config/Swagger2Config.java new file mode 100644 index 00000000..29854a39 --- /dev/null +++ b/schools/src/main/java/com/lambdaschool/schools/config/Swagger2Config.java @@ -0,0 +1,58 @@ +package com.lambdaschool.schools.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import springfox.bean.validators.configuration.BeanValidatorPluginsConfiguration; +import springfox.documentation.builders.ApiInfoBuilder; +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.service.ApiInfo; +import springfox.documentation.service.Contact; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spring.web.plugins.Docket; +import springfox.documentation.swagger2.annotations.EnableSwagger2; + +/** + * Configures the default Swagger Documentation + */ +@Configuration +@EnableSwagger2 +@Import(BeanValidatorPluginsConfiguration.class) +public class Swagger2Config +{ + /** + * Configures what to document using Swagger + * + * @return A Docket which is the primary interface for Swagger configuration + */ + @Bean + public Docket api() + { + return new Docket(DocumentationType.SWAGGER_2) + .select() + .apis(RequestHandlerSelectors + .basePackage("com.lambdaschool.schools")) + .paths(PathSelectors.regex("/.*")) + .build() + .apiInfo(apiEndPointsInfo()); + } + + /** + * Configures some information related to the Application for Swagger + * + * @return ApiInfo a Swagger object containing identification information for this application + */ + private ApiInfo apiEndPointsInfo() + { + return new ApiInfoBuilder().title("User Model Example") + .description("User Model Example") + .contact(new Contact("Lucas Steinmacher", + "http://www.lambdaschool.com", + "john@lambdaschool.com")) + .license("MIT") + .licenseUrl("https://github.com/LambdaSchool/java-usermodel/blob/master/LICENSE") + .version("1.0.0") + .build(); + } +} \ No newline at end of file diff --git a/schools/src/main/java/com/lambdaschool/schools/config/SwaggerWebMVC.java b/schools/src/main/java/com/lambdaschool/schools/config/SwaggerWebMVC.java new file mode 100644 index 00000000..d668f98f --- /dev/null +++ b/schools/src/main/java/com/lambdaschool/schools/config/SwaggerWebMVC.java @@ -0,0 +1,36 @@ +package com.lambdaschool.schools.config; + + +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +/** + * The application turns off any automatic web page generate done by Spring. This is done to improve exception handling. + * However, we do need some web page generate done for Swagger, so we do that here. + */ +@Configuration +public class SwaggerWebMVC + implements WebMvcConfigurer +{ + /** + * Adds the Swagger web pages to Spring. + * This still gives the following warning + *

    + * No mapping for GET / + * No mapping for GET /csrf + *

    + * All works though + * + * @param registry the place that holds the web pages for Spring + */ + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) + { + registry.addResourceHandler("swagger-ui.html") + .addResourceLocations("classpath:/META-INF/resources/"); + + registry.addResourceHandler("/webjars/**") + .addResourceLocations("classpath:/META-INF/resources/webjars/"); + } +} \ No newline at end of file diff --git a/schools/src/main/java/com/lambdaschool/schools/controllers/InstructorController.java b/schools/src/main/java/com/lambdaschool/schools/controllers/InstructorController.java new file mode 100644 index 00000000..0fce5f8b --- /dev/null +++ b/schools/src/main/java/com/lambdaschool/schools/controllers/InstructorController.java @@ -0,0 +1,29 @@ +package com.lambdaschool.schools.controllers; + +import com.lambdaschool.schools.models.Instructor; +import com.lambdaschool.schools.services.InstructorService; +import org.springframework.beans.factory.annotation.Autowired; +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; + +@RestController +@RequestMapping(value = "instructors") +public class InstructorController +{ + @Autowired + private InstructorService instructorService; + + // http://localhost:2019/instructors/instructor/3/advice + @GetMapping(value = "/instructor/{instructorid}/advice", produces = "application/json") + public ResponseEntity getInstructorAdvice( + @PathVariable Long instructorid) + { + Instructor i = instructorService.addAdvice(instructorid); + return new ResponseEntity<>(i, HttpStatus.OK); + } + +} diff --git a/schools/src/main/java/com/lambdaschool/schools/controllers/StudentController.java b/schools/src/main/java/com/lambdaschool/schools/controllers/StudentController.java index 83c41946..c11a4890 100644 --- a/schools/src/main/java/com/lambdaschool/schools/controllers/StudentController.java +++ b/schools/src/main/java/com/lambdaschool/schools/controllers/StudentController.java @@ -1,7 +1,12 @@ package com.lambdaschool.schools.controllers; +import com.lambdaschool.schools.models.ErrorDetail; import com.lambdaschool.schools.models.Student; import com.lambdaschool.schools.services.StudentService; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; @@ -34,6 +39,7 @@ public class StudentController * @return JSON list of all students with a status of OK * @see StudentService#findAll() StudentService.findAll() */ + @ApiOperation(value = "returns all students", response = Student.class, responseContainer = "List") @GetMapping(value = "/students", produces = {"application/json"}) public ResponseEntity listAllStudents() @@ -51,9 +57,16 @@ public ResponseEntity listAllStudents() * @return JSON object of the student you seek * @see StudentService#findStudentById(long) StudentService.findStudentById(long) */ + @ApiOperation(value = "Returns a student by id", response = Student.class) + @ApiResponses(value = {@ApiResponse(code = 200, + message = "Student found", + response = Student.class), @ApiResponse(code = 404, + message = "Student not found", + response = ErrorDetail.class)}) @GetMapping(value = "/student/{studentId}", produces = {"application/json"}) public ResponseEntity getStudentById( + @ApiParam(value = "Student id", required = true, example = "4") @PathVariable Long studentId) { diff --git a/schools/src/main/java/com/lambdaschool/schools/models/Course.java b/schools/src/main/java/com/lambdaschool/schools/models/Course.java index ccaeb75f..01f9fc61 100644 --- a/schools/src/main/java/com/lambdaschool/schools/models/Course.java +++ b/schools/src/main/java/com/lambdaschool/schools/models/Course.java @@ -25,7 +25,7 @@ public class Course /** * Name (String) of this Course. Cannot be null and must be unique */ - @Column(nullable = true, + @Column(nullable = false, unique = true) private String coursename; diff --git a/schools/src/main/java/com/lambdaschool/schools/models/Instructor.java b/schools/src/main/java/com/lambdaschool/schools/models/Instructor.java index 19da1bd9..636b3d0b 100644 --- a/schools/src/main/java/com/lambdaschool/schools/models/Instructor.java +++ b/schools/src/main/java/com/lambdaschool/schools/models/Instructor.java @@ -38,6 +38,9 @@ public class Instructor allowSetters = true) private List courses = new ArrayList<>(); + @Transient + private String advice; + /** * Default Constructor used primarily by the JPA. */ @@ -115,4 +118,14 @@ public void setCourses(List courses) { this.courses = courses; } + + public String getAdvice() + { + return advice; + } + + public void setAdvice(String advice) + { + this.advice = advice; + } } diff --git a/schools/src/main/java/com/lambdaschool/schools/models/Student.java b/schools/src/main/java/com/lambdaschool/schools/models/Student.java index 6dc4bfab..cee3c5a0 100644 --- a/schools/src/main/java/com/lambdaschool/schools/models/Student.java +++ b/schools/src/main/java/com/lambdaschool/schools/models/Student.java @@ -1,6 +1,8 @@ package com.lambdaschool.schools.models; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; import javax.persistence.*; import java.util.HashSet; @@ -9,6 +11,7 @@ /** * The Entity allowing interaction with the students table */ +@ApiModel(value = "Students,", description = "Student record") @Entity @Table(name = "students") public class Student @@ -17,6 +20,7 @@ public class Student /** * The primary key (long) of the students table */ + @ApiModelProperty(name = "student id", value = "primary key of student", required = true, example = "1") @Id @GeneratedValue(strategy = GenerationType.AUTO) private long studentid; @@ -24,6 +28,7 @@ public class Student /** * The name student (String) */ + @ApiModelProperty(name = "Student name", value = "Students actual name", required = true, example = "Bucky Barnes") @Column(nullable = false, unique = true) private String name; @@ -32,6 +37,7 @@ public class Student * Part of the join relationship between student and course * connects students to the student course combination */ + @ApiModelProperty(name = "Student Classes", value = "name of the class", required = false, example = "Java Back End") @OneToMany(mappedBy = "student", cascade = CascadeType.ALL, orphanRemoval = true) diff --git a/schools/src/main/java/com/lambdaschool/schools/models/slip.java b/schools/src/main/java/com/lambdaschool/schools/models/slip.java new file mode 100644 index 00000000..b232bc80 --- /dev/null +++ b/schools/src/main/java/com/lambdaschool/schools/models/slip.java @@ -0,0 +1,35 @@ +package com.lambdaschool.schools.models; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +@JsonIgnoreProperties(ignoreUnknown = true) +public class slip +{ + private long id; + private String advice; + + public long getId() + { + return id; + } + + public void setId(long id) + { + this.id = id; + } + + public String getAdvice() + { + return advice; + } + + public void setAdvice(String advice) + { + this.advice = advice; + } + + public String toString() + { + return "{slip: {" + "id:" + id + ", advice: " + advice + "}}"; + } +} diff --git a/schools/src/main/java/com/lambdaschool/schools/repositories/InstructorRepository.java b/schools/src/main/java/com/lambdaschool/schools/repositories/InstructorRepository.java index b91b5963..89c2a311 100644 --- a/schools/src/main/java/com/lambdaschool/schools/repositories/InstructorRepository.java +++ b/schools/src/main/java/com/lambdaschool/schools/repositories/InstructorRepository.java @@ -6,8 +6,7 @@ /** * The CRUD repository connecting Instructors to the rest of the application */ -public interface InstructorRepository - extends CrudRepository +public interface InstructorRepository extends CrudRepository { } diff --git a/schools/src/main/java/com/lambdaschool/schools/services/InstructorService.java b/schools/src/main/java/com/lambdaschool/schools/services/InstructorService.java new file mode 100644 index 00000000..7535ac55 --- /dev/null +++ b/schools/src/main/java/com/lambdaschool/schools/services/InstructorService.java @@ -0,0 +1,8 @@ +package com.lambdaschool.schools.services; + +import com.lambdaschool.schools.models.Instructor; + +public interface InstructorService +{ + Instructor addAdvice(long id); +} diff --git a/schools/src/main/java/com/lambdaschool/schools/services/InstructorServiceImpl.java b/schools/src/main/java/com/lambdaschool/schools/services/InstructorServiceImpl.java new file mode 100644 index 00000000..67303f69 --- /dev/null +++ b/schools/src/main/java/com/lambdaschool/schools/services/InstructorServiceImpl.java @@ -0,0 +1,22 @@ +package com.lambdaschool.schools.services; + +import com.lambdaschool.schools.exceptions.ResourceNotFoundExeption; +import com.lambdaschool.schools.models.Instructor; +import com.lambdaschool.schools.repositories.InstructorRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service(value = "instructorService") +public class InstructorServiceImpl implements InstructorService +{ + @Autowired + private InstructorRepository instructorRepository; + + + @Override + public Instructor addAdvice(long id) + { + return instructorRepository.findById(id) + .orElseThrow(()-> new ResourceNotFoundExeption("Instructor " + id + " Not Found!")); + } +}