diff --git a/pom.xml b/pom.xml index 79cd452..38886b1 100644 --- a/pom.xml +++ b/pom.xml @@ -12,11 +12,13 @@ net.jwierzbo.rest REST-movie-service 0.1 + jar Movie app - Spring Boot REST with CRUD example UTF-8 1.8 + 2.8.0 @@ -28,6 +30,20 @@ com.fasterxml.jackson.datatype jackson-datatype-jsr310 + + + + io.springfox + springfox-swagger2 + ${swagger.lib.version} + compile + + + io.springfox + springfox-swagger-ui + ${swagger.lib.version} + compile + diff --git a/src/main/java/net/jwierzbo/rest/controller/MovieV1RestController.java b/src/main/java/net/jwierzbo/rest/api/MovieV1RestController.java similarity index 96% rename from src/main/java/net/jwierzbo/rest/controller/MovieV1RestController.java rename to src/main/java/net/jwierzbo/rest/api/MovieV1RestController.java index 4f5f696..3bfb66b 100644 --- a/src/main/java/net/jwierzbo/rest/controller/MovieV1RestController.java +++ b/src/main/java/net/jwierzbo/rest/api/MovieV1RestController.java @@ -1,4 +1,4 @@ -package net.jwierzbo.rest.controller; +package net.jwierzbo.rest.api; import java.util.List; @@ -21,6 +21,7 @@ import org.springframework.web.bind.annotation.RestController; @RestController +@RequestMapping("/v1") // prefix for api methods public class MovieV1RestController { @Autowired diff --git a/src/main/java/net/jwierzbo/rest/api/MovieV2SwaggerController.java b/src/main/java/net/jwierzbo/rest/api/MovieV2SwaggerController.java new file mode 100644 index 0000000..1b7e0db --- /dev/null +++ b/src/main/java/net/jwierzbo/rest/api/MovieV2SwaggerController.java @@ -0,0 +1,90 @@ +package net.jwierzbo.rest.api; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; +import net.jwierzbo.rest.dao.MovieDAO; +import net.jwierzbo.rest.exception.MovieNotFoundException; +import net.jwierzbo.rest.model.Movie; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +/* This is example Controller class with extends description for SwaggerApi +* These @Api*** annotations are not necessary if we use @ResponseStatus +* and return Model objects instead of generic response */ + +@Api(value="MovieController", description="List of favourite Movies") +@RestController +@RequestMapping("/v2") +public class MovieV2SwaggerController { + + @Autowired + private MovieDAO movieDAO; + + @ApiOperation(value = "View a list of Movies", response = Movie.class, responseContainer = "List") + @GetMapping("/movies") + public List getMovies() { + return movieDAO.list(); + } + + @ApiOperation(value = "Search a Movie by ID",response = Movie.class) + @ApiResponses(value = {@ApiResponse(code = 404, message = "Not Found")}) + @GetMapping("/movies/{id}") + public Movie getMovie(@PathVariable("id") Long id) { + Movie movie = checkIfMovieExist(id); + return movie; + } + + @ApiOperation(value = "Add new Movie",response = Movie.class) + @ApiResponses(value = { + @ApiResponse(code = 400, message = "Invalid input"), + @ApiResponse(code = 404, message = "Not Found")}) + @ApiImplicitParams({ + // This can be write also as inline @ApiParam - example below in "deleteMovie" + @ApiImplicitParam(name = "movie", value = "Movie object to add", required = true, dataType = "Movie", + paramType = "body") + }) + @PostMapping(value = "/movies") + @ResponseStatus(value = HttpStatus.CREATED) + public Movie createMovie(@RequestBody Movie movie) { + return movieDAO.create(movie); + } + + @ApiOperation(value = "Delete specific movie") + @ApiResponses(value = {@ApiResponse(code = 404, message = "Not Found")}) + @DeleteMapping("/movies/{id}") + @ResponseStatus(value = HttpStatus.NO_CONTENT) + public void deleteMovie(@ApiParam(value = "Movie ID", required = true) @PathVariable Long id) { + checkIfMovieExist(id); + movieDAO.delete(id); + } + + @ApiResponses(value = { + @ApiResponse(code = 400, message = "Invalid input"), + @ApiResponse(code = 404, message = "Not Found")}) + @PutMapping("/movies/{id}") + public Movie updateMovie(@PathVariable Long id, @RequestBody Movie movie) { + checkIfMovieExist(id); + return movieDAO.update(id, movie); + } + + private Movie checkIfMovieExist(Long id) { + return movieDAO.get(id).orElseThrow(() -> new MovieNotFoundException(id)); + } + +} \ No newline at end of file diff --git a/src/main/java/net/jwierzbo/rest/config/SwaggerConfig.java b/src/main/java/net/jwierzbo/rest/config/SwaggerConfig.java new file mode 100644 index 0000000..3f45d60 --- /dev/null +++ b/src/main/java/net/jwierzbo/rest/config/SwaggerConfig.java @@ -0,0 +1,46 @@ +package net.jwierzbo.rest.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.bind.annotation.RequestMethod; +import springfox.documentation.builders.ApiInfoBuilder; +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.builders.ResponseMessageBuilder; +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; + +import java.util.Arrays; + +@Configuration +@EnableSwagger2 +public class SwaggerConfig { + @Bean + public Docket api() { + return new Docket(DocumentationType.SWAGGER_2) + .useDefaultResponseMessages(false) + .apiInfo(apiInfo()) + .globalResponseMessage(RequestMethod.GET, + Arrays.asList(new ResponseMessageBuilder() + .code(500) + .message("Server Error") + .build())) + .select() + .apis(RequestHandlerSelectors.basePackage("net.jwierzbo.rest")) + .paths(PathSelectors.any()) + .build(); + } + + private ApiInfo apiInfo() { + return new ApiInfoBuilder() + .title("Favourites movies API") + .description("CRUD example REST API to manage list of movies") + .contact(new Contact("jwierzbo", "url", "jwierzbo@jwierzbo.net")) + .license("MIT") + .version("0.1") + .build(); + } +} \ No newline at end of file diff --git a/src/main/java/net/jwierzbo/rest/model/Movie.java b/src/main/java/net/jwierzbo/rest/model/Movie.java index 9beb687..3de6174 100644 --- a/src/main/java/net/jwierzbo/rest/model/Movie.java +++ b/src/main/java/net/jwierzbo/rest/model/Movie.java @@ -1,58 +1,65 @@ package net.jwierzbo.rest.model; import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; import java.time.LocalDate; +@ApiModel(value = "Movie") public class Movie { - private Long id; - private String title; - private String director; + @ApiModelProperty(notes = "The database generated product ID") + private Long id; - @JsonFormat(pattern = "yyyy-MM-dd") - private LocalDate releaseDate; + private String title; - public Movie(long id, String title, String director, LocalDate releaseDate) { - this.id = id; - this.title = title; - this.director = director; - this.releaseDate = releaseDate; - } + private String director; - public Movie() { - } + @JsonFormat(pattern = "yyyy-MM-dd") + @ApiModelProperty(notes = "Movie premiere date", example = "2018-02-28") + private LocalDate releaseDate; - public Long getId() { - return id; - } + public Movie(long id, String title, String director, LocalDate releaseDate) { + this.id = id; + this.title = title; + this.director = director; + this.releaseDate = releaseDate; + } - public void setId(Long id) { - this.id = id; - } + public Movie() { + } - public String getTitle() { - return title; - } + public Long getId() { + return id; + } - public void setTitle(String title) { - this.title = title; - } + public void setId(Long id) { + this.id = id; + } - public String getDirector() { - return director; - } + public String getTitle() { + return title; + } - public void setDirector(String director) { - this.director = director; - } + public void setTitle(String title) { + this.title = title; + } - public LocalDate getReleaseDate() { - return releaseDate; - } + public String getDirector() { + return director; + } - public void setReleaseDate(LocalDate releaseDate) { - this.releaseDate = releaseDate; - } + public void setDirector(String director) { + this.director = director; + } + + public LocalDate getReleaseDate() { + return releaseDate; + } + + public void setReleaseDate(LocalDate releaseDate) { + this.releaseDate = releaseDate; + } } \ No newline at end of file