Merge branch 'dmp-refactoring' of https://code-repo.d4science.org/MaDgiK-CITE/argos into dmp-refactoring

This commit is contained in:
Sofia Papacharalampous 2024-06-03 16:27:53 +03:00
commit 2d8217155c
2 changed files with 283 additions and 16 deletions

View File

@ -13,6 +13,9 @@ import gr.cite.tools.logging.MapLogEntry;
import gr.cite.tools.validation.ValidationFilterAnnotation; import gr.cite.tools.validation.ValidationFilterAnnotation;
import io.swagger.v3.oas.annotations.Hidden; import io.swagger.v3.oas.annotations.Hidden;
import io.swagger.v3.oas.annotations.Operation; 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 io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.xml.bind.JAXBException; import jakarta.xml.bind.JAXBException;
import org.opencdmp.audit.AuditableAction; import org.opencdmp.audit.AuditableAction;
@ -146,7 +149,24 @@ public class DescriptionController {
} }
@PostMapping("query") @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<Description> query(@RequestBody DescriptionLookup lookup) throws MyApplicationException, MyForbiddenException { public QueryResult<Description> query(@RequestBody DescriptionLookup lookup) throws MyApplicationException, MyForbiddenException {
logger.debug("querying {}", Description.class.getSimpleName()); logger.debug("querying {}", Description.class.getSimpleName());
@ -162,7 +182,10 @@ public class DescriptionController {
@GetMapping("{id}") @GetMapping("{id}")
@Operation(summary = "Fetch a specific description by 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)); logger.debug(new MapLogEntry("retrieving" + Description.class.getSimpleName()).And("id", id).And("fields", fieldSet));
fieldSet = this.fieldSetExpanderService.expand(fieldSet); fieldSet = this.fieldSetExpanderService.expand(fieldSet);
@ -342,6 +365,10 @@ public class DescriptionController {
""" """
This endpoint is used to fetch all the available descriptions.<br/> This endpoint is used to fetch all the available descriptions.<br/>
It also allows to restrict the results using a query object passed in the request body.<br/> It also allows to restrict the results using a query object passed in the request body.<br/>
""";
static final String endpoint_query_request_body =
"""
Let's explore the options this object gives us. Let's explore the options this object gives us.
### <u>General query parameters:</u> ### <u>General query parameters:</u>
@ -451,7 +478,60 @@ public class DescriptionController {
</ul> </ul>
"""; """;
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 =
""" """
"""; """;

View File

@ -12,6 +12,8 @@ import gr.cite.tools.logging.LoggerService;
import gr.cite.tools.logging.MapLogEntry; import gr.cite.tools.logging.MapLogEntry;
import gr.cite.tools.validation.ValidationFilterAnnotation; import gr.cite.tools.validation.ValidationFilterAnnotation;
import io.swagger.v3.oas.annotations.Operation; 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 io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.xml.bind.JAXBException; import jakarta.xml.bind.JAXBException;
import org.opencdmp.audit.AuditableAction; import org.opencdmp.audit.AuditableAction;
@ -75,16 +77,16 @@ public class DmpController {
private final QueryFactory queryFactory; private final QueryFactory queryFactory;
private final MessageSource messageSource; private final MessageSource messageSource;
private final ElasticQueryHelperService elasticQueryHelperService; private final ElasticQueryHelperService elasticQueryHelperService;
public DmpController( public DmpController(
BuilderFactory builderFactory, BuilderFactory builderFactory,
AuditService auditService, AuditService auditService,
DmpService dmpService, DmpService dmpService,
CensorFactory censorFactory, CensorFactory censorFactory,
QueryFactory queryFactory, QueryFactory queryFactory,
MessageSource messageSource, MessageSource messageSource,
ElasticQueryHelperService elasticQueryHelperService) { ElasticQueryHelperService elasticQueryHelperService) {
this.builderFactory = builderFactory; this.builderFactory = builderFactory;
this.auditService = auditService; this.auditService = auditService;
@ -92,7 +94,7 @@ public class DmpController {
this.censorFactory = censorFactory; this.censorFactory = censorFactory;
this.queryFactory = queryFactory; this.queryFactory = queryFactory;
this.messageSource = messageSource; this.messageSource = messageSource;
this.elasticQueryHelperService = elasticQueryHelperService; this.elasticQueryHelperService = elasticQueryHelperService;
} }
@PostMapping("public/query") @PostMapping("public/query")
@ -117,10 +119,11 @@ public class DmpController {
this.censorFactory.censor(PublicDmpCensor.class).censor(fieldSet); 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)); 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( this.auditService.track(AuditableAction.Dmp_PublicLookup, Map.ofEntries(
new AbstractMap.SimpleEntry<String, Object>("id", id), new AbstractMap.SimpleEntry<String, Object>("id", id),
@ -131,7 +134,24 @@ public class DmpController {
} }
@PostMapping("query") @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<Dmp> Query(@RequestBody DmpLookup lookup) throws MyApplicationException, MyForbiddenException { public QueryResult<Dmp> Query(@RequestBody DmpLookup lookup) throws MyApplicationException, MyForbiddenException {
logger.debug("querying {}", Dmp.class.getSimpleName()); logger.debug("querying {}", Dmp.class.getSimpleName());
@ -401,4 +421,171 @@ public class DmpController {
return model; return model;
} }
protected static class SwaggerHelpers {
static final String endpoint_query =
"""
This endpoint is used to fetch all the available plans.<br/>
It also allows to restrict the results using a query object passed in the request body.<br/>
""";
static final String endpoint_query_request_body =
"""
Let's explore the options this object gives us.
### <u>General query parameters:</u>
<ul>
<li><b>page:</b>
This is an object controlling the pagination of the results. It contains two properties.
</li>
<ul>
<li><b>offset:</b>
How many records to omit.
</li>
<li><b>size:</b>
How many records to include in each page.
</li>
</ul>
</ul>
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
}
```
<ul>
<li><b>order:</b>
This is an object controlling the ordering of the results.
It contains a list of strings called <i>items</i> with the names of the properties to use.
<br/>If the name of the property is prefixed with a <b>'-'</b>, the ordering direction is <b>DESC</b>. Otherwise, it is <b>ASC</b>.
</li>
</ul>
For example, if we wanted to order based on the field 'createdAt' in descending order, we would pass the following object:
```JSON
{
"items": [
"-createdAt"
],
}
```
<ul>
<li><b>metadata:</b>
This is an object containing metadata for the request. There is only one available option.
<ul>
<li><b>countAll:</b>
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.
<br/>The first option is useful for the UI clients to calculate how many result pages are available.
</li>
</ul>
</li>
<li><b>project:</b>
This is an object controlling the data projection of the results.
It contains a list of strings called <i>fields</i> with the names of the properties to project.
<br/>You can also include properties that are deeper in the object tree by prefixing them with dots.
</li>
</ul>
### <u>Plan specific query parameters:</u>
<ul>
<li><b>like:</b>
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.
</li>
<li><b>ids:</b>
This is a list and contains the ids we want to include in the response. <br/>If empty, every record is included.
</li>
<li><b>excludedIds:</b>
This is a list and contains the ids we want to exclude from the response. <br/>If empty, no record gets excluded.
</li>
<li><b>groupIds:</b>
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. <br/>If empty, every record is included.
</li>
<li><b>isActive:</b>
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].
<br/>If not present or if we pass [0,1], every record is included.
</li>
<li><b>statuses:</b>
This is a list and determines which records we want to include in the response, based on their status.
The status can be <i>Draft</i> or <i>Finalized</i>. We add 0 or 1 to the list respectively.
<br/>If not present, every record is included.
</li>
<li><b>versionStatuses:</b>
This is a list and determines which records we want to include in the response, based on their version status.
The status can be <i>Current</i>, <i>Previous</i> or <i>NotFinalized</i>. We add 0, 1 or 2 to the list respectively.
<br/>If not present, every record is included.
</li>
<li><b>accessTypes:</b>
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 <i>Public</i> or <i>Restricted</i>. We add 0 or 1 to the list respectively.
<br/>If not present, every record is included.
</li>
</ul>
""";
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_ =
"""
""";
}
} }