Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DT-1254 update swagger to use OpenAPI Spec v3 #748

Merged
merged 5 commits into from
Oct 27, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.MD
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@


[![CircleCI](https://circleci.com/gh/ministryofjustice/prison-api/tree/main.svg?style=svg)](https://circleci.com/gh/ministryofjustice/prison-api)
[![API docs](https://img.shields.io/badge/API_docs-view-85EA2D.svg?logo=swagger)](https://api-dev.prison.service.justice.gov.uk/swagger-ui.html)
[![API docs](https://img.shields.io/badge/API_docs-view-85EA2D.svg?logo=swagger)](https://api-dev.prison.service.justice.gov.uk/swagger-ui/)

### How do I get set up? ###

Expand Down Expand Up @@ -192,7 +192,7 @@ aws --endpoint-url=http://localhost:4576 sqs receive-message \
### Authorize with Swagger UI ###
* Without correct authorisation calling endpoints will return http 401 - Unauthorized
* Obtain a correct JWT Token from 'Auth' service
* Go to http://<`host:port`>/swagger-ui.html
* Go to http://<`host:port`>/swagger-ui/
* Click on top right button 'Authorize'
* In the 'Authorize' dialog enter
* `Bearer <Your Token>`
Expand Down
6 changes: 2 additions & 4 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,7 @@ dependencies {
implementation 'net.sf.ehcache:ehcache:2.10.6'
implementation 'com.zaxxer:HikariCP:3.4.5'

implementation 'io.springfox:springfox-swagger-ui:2.9.2'
implementation 'io.springfox:springfox-swagger2:2.9.2'
implementation 'io.springfox:springfox-bean-validators:2.9.2'
implementation 'io.springfox:springfox-boot-starter:3.0.0'
implementation 'io.swagger:swagger-core:1.6.2'

implementation 'org.apache.commons:commons-lang3:3.11'
Expand Down Expand Up @@ -86,8 +84,8 @@ dependencies {
testImplementation 'org.slf4j:slf4j-api:1.7.30'
testImplementation 'com.github.tomakehurst:wiremock-standalone:2.27.2'
testImplementation 'io.jsonwebtoken:jjwt:0.9.1'
testImplementation 'io.swagger.parser.v3:swagger-parser-v2-converter:2.0.22'
testImplementation 'org.glassfish:javax.el:3.0.0'
testImplementation 'io.swagger.parser.v3:swagger-parser:2.0.20'
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

required to downgrade due to json deserialization bug introduced here swagger-api/swagger-parser#1400 and discussed here javalin/javalin#1079 and here swagger-api/swagger-core#3637


testCompileOnly 'org.projectlombok:lombok:1.18.16'
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public interface AccessRoleResource {
@ResponseStatus(HttpStatus.CREATED)
@ApiOperation(value = "Create new access role.", notes = "Create new access role.", nickname = "createAccessRole")
@ApiResponses(value = {
@ApiResponse(code = 201, message = ""),
@ApiResponse(code = 201, message = "Created"),
@ApiResponse(code = 400, message = "Invalid request - e.g. role code not provided.", response = ErrorResponse.class),
@ApiResponse(code = 403, message = "Forbidden - user not authorised to create an access role.", response = ErrorResponse.class),
@ApiResponse(code = 404, message = "Parent access role not found.", response = ErrorResponse.class),
Expand All @@ -44,7 +44,7 @@ public interface AccessRoleResource {
@PutMapping
@ApiOperation(value = "Update the access role.", notes = "Update the access role.", nickname = "updateAccessRole")
@ApiResponses(value = {
@ApiResponse(code = 200, message = ""),
@ApiResponse(code = 200, message = "OK"),
@ApiResponse(code = 400, message = "Invalid request - e.g. role code not provided.", response = ErrorResponse.class),
@ApiResponse(code = 403, message = "Forbidden - user not authorised to update an access role.", response = ErrorResponse.class),
@ApiResponse(code = 404, message = "Access role not found.", response = ErrorResponse.class)})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ List<OffenderCategorise> getOffenderCategorisationsSystem(@ApiParam(value = "The
@PostMapping("/category/categorise")
@ApiOperation(value = "Record new offender categorisation.", notes = "Create new categorisation record. The booking id and new sequence number is returned.")
@ApiResponses(value = {
@ApiResponse(code = 201, message = ""),
@ApiResponse(code = 201, message = "Created"),
@ApiResponse(code = 400, message = "Invalid request - e.g. category does not exist.", response = ErrorResponse.class)})
ResponseEntity<Map<String, Long>> createCategorisation(@ApiParam(value = "Categorisation details", required = true) @RequestBody @Valid CategorisationDetail detail);

Expand All @@ -114,28 +114,28 @@ List<OffenderCategorise> getOffenderCategorisationsSystem(@ApiParam(value = "The
notes = "This is intended for use by the categoriser to correct any problems with a pending (in-progress) categorisation." +
" Fields left as null will be left unchanged")
@ApiResponses(value = {
@ApiResponse(code = 200, message = ""),
@ApiResponse(code = 200, message = "OK"),
@ApiResponse(code = 400, message = "Invalid request - e.g. category does not exist.", response = ErrorResponse.class)})
ResponseEntity<Void> updateCategorisation(@ApiParam(value = "Categorisation details", required = true) @RequestBody @Valid CategorisationUpdateDetail detail);

@PutMapping("/category/approve")
@ApiOperation(value = "Approve a pending offender categorisation.", notes = "Update categorisation record with approval.")
@ApiResponses(value = {
@ApiResponse(code = 201, message = ""),
@ApiResponse(code = 201, message = "Created"),
@ApiResponse(code = 400, message = "Validation error - e.g. category does not exist.", response = ErrorResponse.class)})
ResponseEntity<Void> approveCategorisation(@ApiParam(value = "Approval details", required = true) @RequestBody @Valid CategoryApprovalDetail detail);

@PutMapping("/category/reject")
@ApiOperation(value = "Reject a pending offender categorisation.", notes = "Update categorisation record with rejection.")
@ApiResponses(value = {
@ApiResponse(code = 201, message = ""),
@ApiResponse(code = 201, message = "Created"),
@ApiResponse(code = 400, message = "Validation error - e.g. comment too long or committee code does not exist.", response = ErrorResponse.class)})
ResponseEntity<Void> rejectCategorisation(@ApiParam(value = "Rejection details", required = true) @RequestBody @Valid CategoryRejectionDetail detail);

@PutMapping("/category/{bookingId}/inactive")
@ApiOperation(value = "Set all active or pending (status A or P) categorisations inactive", notes = "This endpoint should only be used with edge case categorisations.")
@ApiResponses(value = {
@ApiResponse(code = 200, message = ""),
@ApiResponse(code = 200, message = "OK"),
@ApiResponse(code = 400, message = "Invalid request - e.g. invalid status.", response = ErrorResponse.class),
@ApiResponse(code = 403, message = "Forbidden - user not authorised to update categorisations.", response = ErrorResponse.class)})
ResponseEntity<Void> setCategorisationInactive(
Expand All @@ -147,7 +147,7 @@ ResponseEntity<Void> setCategorisationInactive(
@PutMapping("/category/{bookingId}/nextReviewDate/{nextReviewDate}")
@ApiOperation(value = "Update the next review date on the latest active categorisation", notes = "Update categorisation record with new next review date.", nickname = "updateCategorisationNextReviewDate")
@ApiResponses(value = {
@ApiResponse(code = 200, message = ""),
@ApiResponse(code = 200, message = "OK"),
@ApiResponse(code = 404, message = "Active categorisation not found.", response = ErrorResponse.class),
@ApiResponse(code = 403, message = "Forbidden - user not authorised to update the categorisation.", response = ErrorResponse.class)})
ResponseEntity<Void> updateCategorisationNextReviewDate(@ApiParam(value = "The booking id of offender", required = true) @PathVariable("bookingId") Long bookingId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ ResponseEntity<List<String>> getAlertCandidates(@ApiParam(value = "A recent tim
@GetMapping("/{offenderNo}/case-notes")
@ApiOperation(value = "Offender case notes", notes = "Retrieve an offenders case notes for latest booking", nickname = "getOffenderCaseNotes")
@ApiResponses(value = {
@ApiResponse(code = 200, message = "", response = CaseNote.class, responseContainer = "List")})
@ApiResponse(code = 200, message = "OK", response = CaseNote.class, responseContainer = "List")})
ResponseEntity<List<CaseNote>> getOffenderCaseNotes(@ApiParam(value = "Noms ID or Prisoner number (also called offenderNo)", required = true, example = "A1234AA") @PathVariable("offenderNo") String offenderNo,
@ApiParam(value = "start contact date to search from") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) @RequestParam(value = "from", required = false) LocalDate from,
@ApiParam(value = "end contact date to search up to (including this date)") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) @RequestParam(value = "to", required = false) LocalDate to,
Expand All @@ -155,14 +155,14 @@ ResponseEntity<List<CaseNote>> getOffenderCaseNotes(@ApiParam(value = "Noms ID
@GetMapping("/{offenderNo}/case-notes/{caseNoteId}")
@ApiOperation(value = "Offender case note detail.", notes = "Retrieve an single offender case note", nickname = "getOffenderCaseNote")
@ApiResponses(value = {
@ApiResponse(code = 200, message = "", response = CaseNote.class)})
@ApiResponse(code = 200, message = "OK", response = CaseNote.class)})
CaseNote getOffenderCaseNote(@ApiParam(value = "Noms ID or Prisoner number (also called offenderNo)", required = true) @PathVariable("offenderNo") String offenderNo,
@ApiParam(value = "The case note id", required = true) @PathVariable("caseNoteId") Long caseNoteId);

@GetMapping("/{offenderNo}/sentences")
@ApiOperation(value = "Offender Sentence Details", notes = "Retrieve an single offender sentence details", nickname = "getOffenderSentenceDetails")
@ApiResponses(value = {
@ApiResponse(code = 200, message = "", response = OffenderSentenceDetail.class)})
@ApiResponse(code = 200, message = "OK", response = OffenderSentenceDetail.class)})
OffenderSentenceDetail getOffenderSentenceDetail(@ApiParam(value = "Noms ID or Prisoner number (also called offenderNo)", required = true) @PathVariable("offenderNo") String offenderNo);

@PostMapping("/{offenderNo}/case-notes")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ public void configure(final HttpSecurity http) throws Exception {
.authorizeRequests(auth ->
auth.antMatchers("/webjars/**", "/favicon.ico", "/csrf",
"/health/**", "/info", "/ping", "/h2-console/**",
"/v2/api-docs", "/api/swagger.json",
"/swagger-ui.html", "/swagger-resources", "/swagger-resources/configuration/ui",
"/api/swagger.json", "/swagger-ui/**",
"/swagger-resources", "/swagger-resources/configuration/ui",
"/swagger-resources/configuration/security").permitAll()
.anyRequest().authenticated()
).oauth2ResourceServer()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.json.JacksonModuleRegistrar;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

import java.time.LocalDate;
import java.time.LocalDateTime;
Expand All @@ -30,7 +29,6 @@
import java.util.Optional;

@Configuration
@EnableSwagger2
@Import(BeanValidatorPluginsConfiguration.class)
public class SwaggerConfig {

Expand All @@ -56,7 +54,7 @@ public JacksonModuleRegistrar swaggerJacksonModuleRegistrar() {
@Bean
public Docket docket() {
var apiKey = new ApiKey(SECURITY_SCHEME_REF, AUTHORIZATION_HEADER, PassAs.header.name());
return new Docket(DocumentationType.SWAGGER_2)
return new Docket(DocumentationType.OAS_30)
.select()
.apis(RequestHandlerSelectors.withClassAnnotation(RestController.class))
.paths(PathSelectors.any())
Expand All @@ -67,7 +65,8 @@ public Docket docket() {
.directModelSubstitute(LocalDateTime.class, Date.class)
.directModelSubstitute(LocalDate.class, java.sql.Date.class)
.securityContexts(Lists.newArrayList(securityContext()))
.securitySchemes(Lists.newArrayList(apiKey));
.securitySchemes(Lists.newArrayList(apiKey))
.forCodeGeneration(true);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

required to stop classes using inheritance being named with invalid characters

e.g. before we would have Page«PrisonerInformation»

after we have PageOfPrisonerInformation

}

private SecurityContext securityContext() {
Expand Down
2 changes: 1 addition & 1 deletion src/main/resources/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ datasource.jdbc.fetch_size=100
oracle.tag.role.name=TAG_USER
oracle.default.schema=

springfox.documentation.swagger.v2.path=/api/swagger.json
springfox.documentation.open-api.v3.path=/api/swagger.json

# Configuration settings that govern API functionality

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package uk.gov.justice.hmpps.prison.api.resource;

import io.swagger.v3.parser.converter.SwaggerConverter;
import io.swagger.v3.parser.OpenAPIV3Parser;
import org.junit.jupiter.api.Test;
import uk.gov.justice.hmpps.prison.api.resource.impl.ResourceTest;

Expand All @@ -10,7 +10,7 @@ public class SwaggerValidator extends ResourceTest {
@Test
public void test() {
final var rootUri = testRestTemplate.getRootUri();
final var result = new SwaggerConverter().readLocation(rootUri + "/api/swagger.json", null, null);
final var result = new OpenAPIV3Parser().readLocation(rootUri + "/api/swagger.json", null, null);
assertThat(result.getMessages()).isEmpty();
}
}