SEBSERV-140 springdoc-openapi integration
This commit is contained in:
parent
dae6ff1df6
commit
9d9b4a949c
6 changed files with 104 additions and 10 deletions
9
pom.xml
9
pom.xml
|
@ -288,6 +288,15 @@
|
||||||
<artifactId>spring-boot-starter-validation</artifactId>
|
<artifactId>spring-boot-starter-validation</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Springdoc-openapi -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springdoc</groupId>
|
||||||
|
<artifactId>springdoc-openapi-ui</artifactId>
|
||||||
|
<version>1.5.10</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- Flyway -->
|
<!-- Flyway -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.flywaydb</groupId>
|
<groupId>org.flywaydb</groupId>
|
||||||
|
|
|
@ -16,9 +16,27 @@ import org.springframework.context.annotation.Lazy;
|
||||||
|
|
||||||
import ch.ethz.seb.sebserver.gbl.Constants;
|
import ch.ethz.seb.sebserver.gbl.Constants;
|
||||||
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
||||||
|
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
|
||||||
|
import io.swagger.v3.oas.annotations.enums.SecuritySchemeType;
|
||||||
|
import io.swagger.v3.oas.annotations.info.Info;
|
||||||
|
import io.swagger.v3.oas.annotations.security.OAuthFlow;
|
||||||
|
import io.swagger.v3.oas.annotations.security.OAuthFlows;
|
||||||
|
import io.swagger.v3.oas.annotations.security.SecurityScheme;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@WebServiceProfile
|
@WebServiceProfile
|
||||||
|
@OpenAPIDefinition(info = @Info(title = "SEB Server", version = "v1"))
|
||||||
|
@SecurityScheme(
|
||||||
|
name = "oauth2",
|
||||||
|
type = SecuritySchemeType.OAUTH2,
|
||||||
|
bearerFormat = "JWT",
|
||||||
|
flows = @OAuthFlows(
|
||||||
|
password = @OAuthFlow(
|
||||||
|
tokenUrl = "/oauth/token"
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
))
|
||||||
public class WebserviceConfig {
|
public class WebserviceConfig {
|
||||||
|
|
||||||
@Lazy
|
@Lazy
|
||||||
|
|
|
@ -25,7 +25,6 @@ import org.springframework.util.MultiValueMap;
|
||||||
import org.springframework.web.bind.WebDataBinder;
|
import org.springframework.web.bind.WebDataBinder;
|
||||||
import org.springframework.web.bind.annotation.InitBinder;
|
import org.springframework.web.bind.annotation.InitBinder;
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMethod;
|
import org.springframework.web.bind.annotation.RequestMethod;
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
@ -54,12 +53,18 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.dao.EntityDAO;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationService;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationService;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Content;
|
||||||
|
import io.swagger.v3.oas.annotations.parameters.RequestBody;
|
||||||
|
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||||
|
|
||||||
/** Abstract Entity-Controller that defines generic Entity rest API endpoints that are supported
|
/** Abstract Entity-Controller that defines generic Entity rest API endpoints that are supported
|
||||||
* by all entity types.
|
* by all entity types.
|
||||||
*
|
*
|
||||||
* @param <T> The concrete Entity domain-model type used on all GET, PUT
|
* @param <T> The concrete Entity domain-model type used on all GET, PUT
|
||||||
* @param <M> The concrete Entity domain-model type used for POST methods (new) */
|
* @param <M> The concrete Entity domain-model type used for POST methods (new) */
|
||||||
|
@SecurityRequirement(name = "oauth2")
|
||||||
public abstract class EntityController<T extends Entity, M extends Entity> {
|
public abstract class EntityController<T extends Entity, M extends Entity> {
|
||||||
|
|
||||||
protected final AuthorizationService authorization;
|
protected final AuthorizationService authorization;
|
||||||
|
@ -129,6 +134,39 @@ public abstract class EntityController<T extends Entity, M extends Entity> {
|
||||||
* descending sort order.
|
* descending sort order.
|
||||||
* @param allRequestParams a MultiValueMap of all request parameter that is used for filtering.
|
* @param allRequestParams a MultiValueMap of all request parameter that is used for filtering.
|
||||||
* @return Page of domain-model-entities of specified type */
|
* @return Page of domain-model-entities of specified type */
|
||||||
|
@Operation(
|
||||||
|
summary = "Get a page of the specific domain entity. Sorting and filtering is applied before paging",
|
||||||
|
requestBody = @RequestBody(
|
||||||
|
content = { @Content(mediaType = MediaType.APPLICATION_FORM_URLENCODED_VALUE) }),
|
||||||
|
parameters = {
|
||||||
|
@Parameter(
|
||||||
|
name = Page.ATTR_PAGE_NUMBER,
|
||||||
|
description = "The number of the page to get from the whole list. If the page does not exists, the API retruns with the first page."),
|
||||||
|
@Parameter(
|
||||||
|
name = Page.ATTR_PAGE_SIZE,
|
||||||
|
description = "The size of the page to get."),
|
||||||
|
@Parameter(
|
||||||
|
name = Page.ATTR_SORT,
|
||||||
|
description = "the sort parameter to sort the list of entities before paging\n"
|
||||||
|
+ "the sort parameter is the name of the entity-model attribute to sort with a leading '-' sign for\n"
|
||||||
|
+ "descending sort order. Note that not all entity-model attribute are suited for sorting while the most are.",
|
||||||
|
example = "-name"),
|
||||||
|
@Parameter(
|
||||||
|
name = API.PARAM_INSTITUTION_ID,
|
||||||
|
description = "The institution identifier of the request.\n"
|
||||||
|
+ "Default is the institution identifier of the institution of the current user"),
|
||||||
|
@Parameter(
|
||||||
|
name = "filterCriteria",
|
||||||
|
description = "Additional filter criterias \n" +
|
||||||
|
"The filter criteria attributes accepted by this API depend on the actual entity model (domain entity)\n"
|
||||||
|
+ "and are query of the form [domain-attribute-name]=[filter-value]. E.g.: name=abc or type=EXAM.\n"
|
||||||
|
+ "For OpenAPI 3 input please use the form: {\"columnName\":\"filterValue\"}\n"
|
||||||
|
+ "Usually filter attributes of text type are treated as SQL wildcard with %[text]% to filter all text containing\n"
|
||||||
|
+ "a given text-snippet.",
|
||||||
|
example = "{\"name\":\"ethz\"}",
|
||||||
|
required = false,
|
||||||
|
allowEmptyValue = true)
|
||||||
|
})
|
||||||
@RequestMapping(
|
@RequestMapping(
|
||||||
method = RequestMethod.GET,
|
method = RequestMethod.GET,
|
||||||
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
|
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
|
||||||
|
@ -141,13 +179,13 @@ public abstract class EntityController<T extends Entity, M extends Entity> {
|
||||||
@RequestParam(name = Page.ATTR_PAGE_NUMBER, required = false) final Integer pageNumber,
|
@RequestParam(name = Page.ATTR_PAGE_NUMBER, required = false) final Integer pageNumber,
|
||||||
@RequestParam(name = Page.ATTR_PAGE_SIZE, required = false) final Integer pageSize,
|
@RequestParam(name = Page.ATTR_PAGE_SIZE, required = false) final Integer pageSize,
|
||||||
@RequestParam(name = Page.ATTR_SORT, required = false) final String sort,
|
@RequestParam(name = Page.ATTR_SORT, required = false) final String sort,
|
||||||
@RequestParam final MultiValueMap<String, String> allRequestParams,
|
@RequestParam final MultiValueMap<String, String> filterCriteria,
|
||||||
final HttpServletRequest request) {
|
final HttpServletRequest request) {
|
||||||
|
|
||||||
// at least current user must have read access for specified entity type within its own institution
|
// at least current user must have read access for specified entity type within its own institution
|
||||||
checkReadPrivilege(institutionId);
|
checkReadPrivilege(institutionId);
|
||||||
|
|
||||||
final FilterMap filterMap = new FilterMap(allRequestParams, request.getQueryString());
|
final FilterMap filterMap = new FilterMap(filterCriteria, request.getQueryString());
|
||||||
populateFilterMap(filterMap, institutionId, sort);
|
populateFilterMap(filterMap, institutionId, sort);
|
||||||
|
|
||||||
return this.paginationService.getPage(
|
return this.paginationService.getPage(
|
||||||
|
@ -176,6 +214,28 @@ public abstract class EntityController<T extends Entity, M extends Entity> {
|
||||||
// * GET (names)
|
// * GET (names)
|
||||||
// ******************
|
// ******************
|
||||||
|
|
||||||
|
@Operation(
|
||||||
|
summary = "Get a filtered list of specific entity name keys.\n" +
|
||||||
|
"An entity name key is a minimal entity data object with the entity-type, modelId and the name of the entity.",
|
||||||
|
requestBody = @RequestBody(
|
||||||
|
content = { @Content(mediaType = MediaType.APPLICATION_FORM_URLENCODED_VALUE) }),
|
||||||
|
parameters = {
|
||||||
|
@Parameter(
|
||||||
|
name = API.PARAM_INSTITUTION_ID,
|
||||||
|
description = "The institution identifier of the request.\n"
|
||||||
|
+ "Default is the institution identifier of the institution of the current user"),
|
||||||
|
@Parameter(
|
||||||
|
name = "filterCriteria",
|
||||||
|
description = "Additional filter criterias \n" +
|
||||||
|
"The filter criteria attributes accepted by this API depend on the actual entity model (domain entity)\n"
|
||||||
|
+ "and are query of the form [domain-attribute-name]=[filter-value]. E.g.: name=abc or type=EXAM.\n"
|
||||||
|
+ "For OpenAPI 3 input please use the form: {\"columnName\":\"filterValue\"}\n"
|
||||||
|
+ "Usually filter attributes of text type are treated as SQL wildcard with %[text]% to filter all text containing\n"
|
||||||
|
+ "a given text-snippet.",
|
||||||
|
example = "{\"name\":\"ethz\"}",
|
||||||
|
required = false,
|
||||||
|
allowEmptyValue = true)
|
||||||
|
})
|
||||||
@RequestMapping(
|
@RequestMapping(
|
||||||
path = API.NAMES_PATH_SEGMENT,
|
path = API.NAMES_PATH_SEGMENT,
|
||||||
method = RequestMethod.GET,
|
method = RequestMethod.GET,
|
||||||
|
@ -186,13 +246,13 @@ public abstract class EntityController<T extends Entity, M extends Entity> {
|
||||||
name = API.PARAM_INSTITUTION_ID,
|
name = API.PARAM_INSTITUTION_ID,
|
||||||
required = true,
|
required = true,
|
||||||
defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId,
|
defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId,
|
||||||
@RequestParam final MultiValueMap<String, String> allRequestParams,
|
@RequestParam final MultiValueMap<String, String> filterCriteria,
|
||||||
final HttpServletRequest request) {
|
final HttpServletRequest request) {
|
||||||
|
|
||||||
// at least current user must have read access for specified entity type within its own institution
|
// at least current user must have read access for specified entity type within its own institution
|
||||||
checkReadPrivilege(institutionId);
|
checkReadPrivilege(institutionId);
|
||||||
|
|
||||||
final FilterMap filterMap = new FilterMap(allRequestParams, request.getQueryString());
|
final FilterMap filterMap = new FilterMap(filterCriteria, request.getQueryString());
|
||||||
|
|
||||||
// if current user has no read access for specified entity type within other institution then its own institution,
|
// if current user has no read access for specified entity type within other institution then its own institution,
|
||||||
// then the current users institutionId is put as a SQL filter criteria attribute to extends query performance
|
// then the current users institutionId is put as a SQL filter criteria attribute to extends query performance
|
||||||
|
@ -285,7 +345,7 @@ public abstract class EntityController<T extends Entity, M extends Entity> {
|
||||||
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
|
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
|
||||||
produces = MediaType.APPLICATION_JSON_VALUE)
|
produces = MediaType.APPLICATION_JSON_VALUE)
|
||||||
public T create(
|
public T create(
|
||||||
@RequestParam final MultiValueMap<String, String> allRequestParams,
|
@RequestParam final MultiValueMap<String, String> formParams,
|
||||||
@RequestParam(
|
@RequestParam(
|
||||||
name = API.PARAM_INSTITUTION_ID,
|
name = API.PARAM_INSTITUTION_ID,
|
||||||
required = true,
|
required = true,
|
||||||
|
@ -295,7 +355,7 @@ public abstract class EntityController<T extends Entity, M extends Entity> {
|
||||||
// check write privilege for requested institution and concrete entityType
|
// check write privilege for requested institution and concrete entityType
|
||||||
this.checkWritePrivilege(institutionId);
|
this.checkWritePrivilege(institutionId);
|
||||||
|
|
||||||
final POSTMapper postMap = new POSTMapper(allRequestParams, request.getQueryString())
|
final POSTMapper postMap = new POSTMapper(formParams, request.getQueryString())
|
||||||
.putIfAbsent(API.PARAM_INSTITUTION_ID, String.valueOf(institutionId));
|
.putIfAbsent(API.PARAM_INSTITUTION_ID, String.valueOf(institutionId));
|
||||||
|
|
||||||
final M requestModel = this.createNew(postMap);
|
final M requestModel = this.createNew(postMap);
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
spring.profiles.include=dev-ws,dev-gui
|
spring.profiles.include=dev-ws
|
||||||
|
|
||||||
sebserver.test.property=This is the development Setup
|
sebserver.test.property=This is the development Setup
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,14 @@ sebserver.webservice.distributed=false
|
||||||
sebserver.webservice.http.external.scheme=https
|
sebserver.webservice.http.external.scheme=https
|
||||||
sebserver.webservice.http.external.servername=
|
sebserver.webservice.http.external.servername=
|
||||||
sebserver.webservice.http.external.port=
|
sebserver.webservice.http.external.port=
|
||||||
sebserver.webservice.http.redirect.gui=/gui
|
sebserver.webservice.http.redirect.gui=/
|
||||||
|
|
||||||
|
### Open API Documentation
|
||||||
|
springdoc.swagger-ui.oauth.clientId=guiClient
|
||||||
|
springdoc.swagger-ui.oauth.clientSecret=${sebserver.password}
|
||||||
|
#springdoc.consumes-to-match=application/json,application/x-www-form-urlencoded
|
||||||
|
#springdoc.default-consumes-media-type=application/x-www-form-urlencoded
|
||||||
|
springdoc.paths-to-exclude=/exam-api,/exam-api/discovery,/sebserver/error,/sebserver/check,/oauth,/exam-api/v1/*
|
||||||
|
|
||||||
### webservice API
|
### webservice API
|
||||||
sebserver.webservice.api.admin.clientId=guiClient
|
sebserver.webservice.api.admin.clientId=guiClient
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
spring.application.name=SEB Server
|
spring.application.name=SEB Server
|
||||||
spring.profiles.active=ws,gui,dev
|
spring.profiles.active=ws,dev
|
||||||
sebserver.version=@sebserver-version@
|
sebserver.version=@sebserver-version@
|
||||||
|
|
||||||
##########################################################
|
##########################################################
|
||||||
|
|
Loading…
Add table
Reference in a new issue