From 8e2dcc855a50a12d77abc5ca4aca76ca6ebb3b9a Mon Sep 17 00:00:00 2001 From: Thomas Georgios Giannos Date: Mon, 3 Jun 2024 15:58:46 +0300 Subject: [PATCH] Refactored swagger docs contents to reflect better the relevant parts of the docs, added query examples for dmp and description queries. --- .../controllers/DescriptionController.java | 86 ++++++- .../opencdmp/controllers/DmpController.java | 213 ++++++++++++++++-- 2 files changed, 283 insertions(+), 16 deletions(-) diff --git a/backend/web/src/main/java/org/opencdmp/controllers/DescriptionController.java b/backend/web/src/main/java/org/opencdmp/controllers/DescriptionController.java index 0591d8c15..0b8909da3 100644 --- a/backend/web/src/main/java/org/opencdmp/controllers/DescriptionController.java +++ b/backend/web/src/main/java/org/opencdmp/controllers/DescriptionController.java @@ -13,6 +13,9 @@ import gr.cite.tools.logging.MapLogEntry; import gr.cite.tools.validation.ValidationFilterAnnotation; import io.swagger.v3.oas.annotations.Hidden; 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.media.ExampleObject; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.xml.bind.JAXBException; import org.opencdmp.audit.AuditableAction; @@ -146,7 +149,24 @@ public class DescriptionController { } @PostMapping("query") - @Operation(summary = "Query all descriptions", description = SwaggerHelpers.endpoint_query) + @Operation( + summary = "Query all descriptions", + description = SwaggerHelpers.endpoint_query, + requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody( + description = SwaggerHelpers.endpoint_query_request_body, + content = { + @Content( + examples = { + @ExampleObject( + name = "Pagination and projection", + description = "Simple paginated request using a property projection list and pagination info", + value = SwaggerHelpers.endpoint_query_request_body_example + ) + } + ) + } + ) + ) public QueryResult query(@RequestBody DescriptionLookup lookup) throws MyApplicationException, MyForbiddenException { logger.debug("querying {}", Description.class.getSimpleName()); @@ -162,7 +182,10 @@ public class DescriptionController { @GetMapping("{id}") @Operation(summary = "Fetch a specific description by id") - public Description get(@PathVariable("id") UUID id, FieldSet fieldSet) throws MyApplicationException, MyForbiddenException, MyNotFoundException { + public Description get( + @Parameter(name = "id", description = "The id of a description to fetch", example = "c0c163dc-2965-45a5-9608-f76030578609", required = true) @PathVariable("id") UUID id, + @Parameter(name = "fieldSet", description = "This is an object containing a list of the properties you wish to include in the response, similar to the 'project' attribute on queries.", required = true) FieldSet fieldSet) + throws MyApplicationException, MyForbiddenException, MyNotFoundException { logger.debug(new MapLogEntry("retrieving" + Description.class.getSimpleName()).And("id", id).And("fields", fieldSet)); fieldSet = this.fieldSetExpanderService.expand(fieldSet); @@ -342,6 +365,10 @@ public class DescriptionController { """ This endpoint is used to fetch all the available descriptions.
It also allows to restrict the results using a query object passed in the request body.
+ """; + + static final String endpoint_query_request_body = + """ Let's explore the options this object gives us. ### General query parameters: @@ -451,7 +478,60 @@ public class DescriptionController { """; - static final String endpoint_ = + static final String endpoint_query_request_body_example = + """ + { + "project":{ + "fields":[ + "id", + "label", + "status", + "updatedAt", + "belongsToCurrentTenant", + "finalizedAt", + "descriptionTemplate.id", + "descriptionTemplate.label", + "descriptionTemplate.groupId", + "dmp.id", + "dmp.label", + "dmp.status", + "dmp.accessType", + "dmp.blueprint.id", + "dmp.blueprint.label", + "dmp.blueprint.definition.sections.id", + "dmp.blueprint.definition.sections.label", + "dmp.blueprint.definition.sections.hasTemplates", + "dmp.dmpReferences.id", + "dmp.dmpReferences.reference.id", + "dmp.dmpReferences.reference.label", + "dmp.dmpReferences.reference.type.id", + "dmp.dmpReferences.reference.reference", + "dmp.dmpReferences.isActive", + "dmpDescriptionTemplate.id", + "dmpDescriptionTemplate.dmp.id", + "dmpDescriptionTemplate.descriptionTemplateGroupId", + "dmpDescriptionTemplate.sectionId" + ] + }, + "page":{ + "size":5, + "offset":0 + }, + "order":{ + "items":[ + "-updatedAt" + ] + }, + "metadata":{ + "countAll":true + }, + "isActive":[ + 1 + ] + } + """; + + static final String endpoint_get = """ """; diff --git a/backend/web/src/main/java/org/opencdmp/controllers/DmpController.java b/backend/web/src/main/java/org/opencdmp/controllers/DmpController.java index c63958482..99c15180f 100644 --- a/backend/web/src/main/java/org/opencdmp/controllers/DmpController.java +++ b/backend/web/src/main/java/org/opencdmp/controllers/DmpController.java @@ -12,6 +12,8 @@ import gr.cite.tools.logging.LoggerService; import gr.cite.tools.logging.MapLogEntry; import gr.cite.tools.validation.ValidationFilterAnnotation; import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.ExampleObject; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.xml.bind.JAXBException; import org.opencdmp.audit.AuditableAction; @@ -75,16 +77,16 @@ public class DmpController { private final QueryFactory queryFactory; private final MessageSource messageSource; + private final ElasticQueryHelperService elasticQueryHelperService; - public DmpController( - BuilderFactory builderFactory, - AuditService auditService, - DmpService dmpService, - CensorFactory censorFactory, - QueryFactory queryFactory, - MessageSource messageSource, + BuilderFactory builderFactory, + AuditService auditService, + DmpService dmpService, + CensorFactory censorFactory, + QueryFactory queryFactory, + MessageSource messageSource, ElasticQueryHelperService elasticQueryHelperService) { this.builderFactory = builderFactory; this.auditService = auditService; @@ -92,9 +94,9 @@ public class DmpController { this.censorFactory = censorFactory; this.queryFactory = queryFactory; this.messageSource = messageSource; - this.elasticQueryHelperService = elasticQueryHelperService; - } - + this.elasticQueryHelperService = elasticQueryHelperService; + } + @PostMapping("public/query") @Operation(summary = "Query public published plans") public QueryResult publicQuery(@RequestBody DmpLookup lookup) throws MyApplicationException, MyForbiddenException { @@ -117,10 +119,11 @@ public class DmpController { this.censorFactory.censor(PublicDmpCensor.class).censor(fieldSet); - DmpQuery query = this.queryFactory.query(DmpQuery.class).disableTracking().authorize(EnumSet.of(Public)).ids(id).isActive(IsActive.Active).statuses(DmpStatus.Finalized).accessTypes(DmpAccessType.Public); + DmpQuery query = this.queryFactory.query(DmpQuery.class).disableTracking().authorize(EnumSet.of(Public)).ids(id).isActive(IsActive.Active).statuses(DmpStatus.Finalized).accessTypes(DmpAccessType.Public); PublicDmp model = this.builderFactory.builder(PublicDmpBuilder.class).authorize(EnumSet.of(Public)).build(fieldSet, query.firstAs(fieldSet)); - if (model == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{id, Dmp.class.getSimpleName()}, LocaleContextHolder.getLocale())); + if (model == null) + throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{id, Dmp.class.getSimpleName()}, LocaleContextHolder.getLocale())); this.auditService.track(AuditableAction.Dmp_PublicLookup, Map.ofEntries( new AbstractMap.SimpleEntry("id", id), @@ -131,7 +134,24 @@ public class DmpController { } @PostMapping("query") - @Operation(summary = "Query all plans") + @Operation( + summary = "Query all plans", + description = SwaggerHelpers.endpoint_query, + requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody( + description = SwaggerHelpers.endpoint_query_request_body, + content = { + @Content( + examples = { + @ExampleObject( + name = "Pagination and projection", + description = "Simple paginated request using a property projection list and pagination info", + value = SwaggerHelpers.endpoint_query_request_body_example + ) + } + ) + } + ) + ) public QueryResult Query(@RequestBody DmpLookup lookup) throws MyApplicationException, MyForbiddenException { logger.debug("querying {}", Dmp.class.getSimpleName()); @@ -401,4 +421,171 @@ public class DmpController { return model; } + protected static class SwaggerHelpers { + + static final String endpoint_query = + """ + This endpoint is used to fetch all the available plans.
+ It also allows to restrict the results using a query object passed in the request body.
+ """; + + static final String endpoint_query_request_body = + """ + Let's explore the options this object gives us. + + ### General query parameters: + +
    +
  • page: + This is an object controlling the pagination of the results. It contains two properties. +
  • +
      +
    • offset: + How many records to omit. +
    • +
    • size: + How many records to include in each page. +
    • +
    +
+ + For example, if we want the third page, and our pages to contain 15 elements, we would pass the following object: + + ```JSON + { + "offset": 30, + "size": 15 + } + ``` + +
    +
  • order: + This is an object controlling the ordering of the results. + It contains a list of strings called items with the names of the properties to use. +
    If the name of the property is prefixed with a '-', the ordering direction is DESC. Otherwise, it is ASC. +
  • +
+ + For example, if we wanted to order based on the field 'createdAt' in descending order, we would pass the following object: + + ```JSON + { + "items": [ + "-createdAt" + ], + } + ``` + +
    +
  • metadata: + This is an object containing metadata for the request. There is only one available option. +
      +
    • countAll: + If this is set to true, the count property included in the response will account for all the records regardless the pagination, + with all the rest of filtering options applied of course. + Otherwise, if it is set to false or not present, only the returned results will be counted. +
      The first option is useful for the UI clients to calculate how many result pages are available. +
    • +
    +
  • +
  • project: + This is an object controlling the data projection of the results. + It contains a list of strings called fields with the names of the properties to project. +
    You can also include properties that are deeper in the object tree by prefixing them with dots. +
  • +
+ + ### Plan specific query parameters: + +
    +
  • like: + If there is a like parameter present in the query, only the description entities that include the contents of the parameter either in their labels or the descriptions will be in the response. +
  • +
  • ids: + This is a list and contains the ids we want to include in the response.
    If empty, every record is included. +
  • +
  • excludedIds: + This is a list and contains the ids we want to exclude from the response.
    If empty, no record gets excluded. +
  • +
  • groupIds: + This is a list and contains the group ids we want the plans to have. Every plan and all its versions, have the same groupId.
    If empty, every record is included. +
  • +
  • isActive: + This is a list and determines which records we want to include in the response, based on if they are deleted or not. + This filter works like this. If we want to view only the active records we pass [1] and for only the deleted records we pass [0]. +
    If not present or if we pass [0,1], every record is included. +
  • +
  • statuses: + This is a list and determines which records we want to include in the response, based on their status. + The status can be Draft or Finalized. We add 0 or 1 to the list respectively. +
    If not present, every record is included. +
  • +
  • versionStatuses: + This is a list and determines which records we want to include in the response, based on their version status. + The status can be Current, Previous or NotFinalized. We add 0, 1 or 2 to the list respectively. +
    If not present, every record is included. +
  • +
  • accessTypes: + This is a list and determines which records we want to include in the response, based on their access type. + The access type can be Public or Restricted. We add 0 or 1 to the list respectively. +
    If not present, every record is included. +
  • +
+ """; + + static final String endpoint_query_request_body_example = + """ + { + "project":{ + "fields":[ + "id", + "label", + "description", + "status", + "accessType", + "version", + "versionStatus", + "groupId", + "updatedAt", + "belongsToCurrentTenant", + "finalizedAt", + "hash", + "descriptions.id", + "descriptions.label", + "descriptions.status", + "descriptions.descriptionTemplate.groupId", + "descriptions.isActive", + "blueprint.id", + "blueprint.label", + "blueprint.definition.sections.id" + ] + }, + "page":{ + "size":5, + "offset":0 + }, + "order":{ + "items":[ + "-updatedAt" + ] + }, + "metadata":{ + "countAll":true + }, + "isActive":[ + 1 + ], + "versionStatuses":[ + 0, + 2 + ] + } + """; + + static final String endpoint_ = + """ + + """; + } + }