From 9803a58a1122dc48ed9bf010bcad25e1be49cef0 Mon Sep 17 00:00:00 2001 From: Thomas BOUSSELIN Date: Thu, 28 Nov 2024 11:42:22 +0100 Subject: [PATCH] feat: rename QueryParameter add allowed parameters to entityhandler --- config/detekt/detekt.yml | 2 +- .../search/csr/service/ContextSourceCaller.kt | 8 +- .../web/ContextSourceRegistrationHandler.kt | 8 +- .../search/entity/util/EntitiesQueryUtils.kt | 22 ++-- .../search/entity/web/EntityHandler.kt | 118 +++++++++++++++--- .../entity/web/EntityOperationHandler.kt | 4 +- .../egm/stellio/search/scope/ScopeService.kt | 2 +- .../service/AttributeInstanceService.kt | 2 +- .../temporal/util/TemporalQueryUtils.kt | 45 ++++--- .../csr/service/ContextSourceCallerTests.kt | 6 +- .../service/LinkedEntityServiceTests.kt | 2 +- .../entity/util/EntitiesQueryUtilsTests.kt | 6 +- .../stellio/shared/model/CompactedEntity.kt | 6 +- .../egm/stellio/shared/model/GeoQueryUtils.kt | 18 +++ .../shared/model/NgsiLdDataRepresentation.kt | 12 +- .../stellio/shared/model/PaginationQuery.kt | 10 +- .../model/parameter/AllowedParameters.kt | 9 +- .../shared/model/parameter/GeoQuery.kt | 17 ++- .../model/parameter/GeoQueryParameter.kt | 69 ---------- .../stellio/shared/model/parameter/Georel.kt | 42 +++++++ .../model/parameter/PaginationParameter.kt | 10 -- .../shared/model/parameter/QueryParameter.kt | 68 ++++++---- .../model/parameter/TemporalQueryParameter.kt | 17 --- .../com/egm/stellio/shared/util/ApiUtils.kt | 4 +- .../com/egm/stellio/shared/util/QueryUtils.kt | 6 +- .../job/TimeIntervalNotificationJob.kt | 14 ++- .../egm/stellio/subscription/model/GeoQ.kt | 10 +- .../subscription/web/SubscriptionHandler.kt | 8 +- 28 files changed, 308 insertions(+), 237 deletions(-) create mode 100644 shared/src/main/kotlin/com/egm/stellio/shared/model/GeoQueryUtils.kt delete mode 100644 shared/src/main/kotlin/com/egm/stellio/shared/model/parameter/GeoQueryParameter.kt create mode 100644 shared/src/main/kotlin/com/egm/stellio/shared/model/parameter/Georel.kt delete mode 100644 shared/src/main/kotlin/com/egm/stellio/shared/model/parameter/PaginationParameter.kt delete mode 100644 shared/src/main/kotlin/com/egm/stellio/shared/model/parameter/TemporalQueryParameter.kt diff --git a/config/detekt/detekt.yml b/config/detekt/detekt.yml index e29567ca7..aa1b8e0c4 100644 --- a/config/detekt/detekt.yml +++ b/config/detekt/detekt.yml @@ -733,7 +733,7 @@ style: active: true UnusedParameter: active: true - allowedNames: 'ignored|expected' + allowedNames: 'ignored|expected|params' UnusedPrivateClass: active: true UnusedPrivateMember: diff --git a/search-service/src/main/kotlin/com/egm/stellio/search/csr/service/ContextSourceCaller.kt b/search-service/src/main/kotlin/com/egm/stellio/search/csr/service/ContextSourceCaller.kt index b7533fcb8..041ba9994 100644 --- a/search-service/src/main/kotlin/com/egm/stellio/search/csr/service/ContextSourceCaller.kt +++ b/search-service/src/main/kotlin/com/egm/stellio/search/csr/service/ContextSourceCaller.kt @@ -10,7 +10,7 @@ import com.egm.stellio.search.csr.model.MiscellaneousWarning import com.egm.stellio.search.csr.model.NGSILDWarning import com.egm.stellio.search.csr.model.RevalidationFailedWarning import com.egm.stellio.shared.model.CompactedEntity -import com.egm.stellio.shared.model.parameter.QueryParam +import com.egm.stellio.shared.model.parameter.QueryParameter import org.slf4j.Logger import org.slf4j.LoggerFactory import org.springframework.core.codec.DecodingException @@ -36,9 +36,9 @@ object ContextSourceCaller { val uri = URI("${csr.endpoint}$path") val queryParams = CollectionUtils.toMultiValueMap(params.toMutableMap()) - queryParams.remove(QueryParam.GEOMETRY_PROPERTY.key) - queryParams.remove(QueryParam.OPTIONS.key) // only normalized request - queryParams.remove(QueryParam.LANG.key) + queryParams.remove(QueryParameter.GEOMETRY_PROPERTY.key) + queryParams.remove(QueryParameter.OPTIONS.key) // only normalized request + queryParams.remove(QueryParameter.LANG.key) val request = WebClient.create() .method(HttpMethod.GET) diff --git a/search-service/src/main/kotlin/com/egm/stellio/search/csr/web/ContextSourceRegistrationHandler.kt b/search-service/src/main/kotlin/com/egm/stellio/search/csr/web/ContextSourceRegistrationHandler.kt index ee6446ca8..698e5bfe5 100644 --- a/search-service/src/main/kotlin/com/egm/stellio/search/csr/web/ContextSourceRegistrationHandler.kt +++ b/search-service/src/main/kotlin/com/egm/stellio/search/csr/web/ContextSourceRegistrationHandler.kt @@ -14,7 +14,7 @@ import com.egm.stellio.shared.config.ApplicationProperties import com.egm.stellio.shared.model.APIException import com.egm.stellio.shared.model.AccessDeniedException import com.egm.stellio.shared.model.PaginationQuery.Companion.parsePaginationParameters -import com.egm.stellio.shared.model.parameter.QueryParam +import com.egm.stellio.shared.model.parameter.QueryParameter import com.egm.stellio.shared.util.JSON_LD_CONTENT_TYPE import com.egm.stellio.shared.util.JsonUtils.deserializeAsMap import com.egm.stellio.shared.util.Sub @@ -86,8 +86,8 @@ class ContextSourceRegistrationHandler( val mediaType = getApplicableMediaType(httpHeaders).bind() val sub = getSubFromSecurityContext() - val includeSysAttrs = params.getOrDefault(QueryParam.OPTIONS.key, emptyList()) - .contains(QueryParam.OptionValue.SYS_ATTRS.value) + val includeSysAttrs = params.getOrDefault(QueryParameter.OPTIONS.key, emptyList()) + .contains(QueryParameter.SYS_ATTRS.key) val paginationQuery = parsePaginationParameters( params, applicationProperties.pagination.limitDefault, @@ -124,7 +124,7 @@ class ContextSourceRegistrationHandler( @PathVariable contextSourceRegistrationId: URI, @RequestParam options: String? ): ResponseEntity<*> = either { - val includeSysAttrs = options == QueryParam.OptionValue.SYS_ATTRS.value + val includeSysAttrs = options == QueryParameter.SYS_ATTRS.key val contexts = getContextFromLinkHeaderOrDefault(httpHeaders, applicationProperties.contexts.core).bind() val mediaType = getApplicableMediaType(httpHeaders).bind() diff --git a/search-service/src/main/kotlin/com/egm/stellio/search/entity/util/EntitiesQueryUtils.kt b/search-service/src/main/kotlin/com/egm/stellio/search/entity/util/EntitiesQueryUtils.kt index 0da49bf10..80c589ee9 100644 --- a/search-service/src/main/kotlin/com/egm/stellio/search/entity/util/EntitiesQueryUtils.kt +++ b/search-service/src/main/kotlin/com/egm/stellio/search/entity/util/EntitiesQueryUtils.kt @@ -13,7 +13,7 @@ import com.egm.stellio.shared.model.EntitySelector import com.egm.stellio.shared.model.LinkedEntityQuery.Companion.parseLinkedEntityQueryParameters import com.egm.stellio.shared.model.PaginationQuery.Companion.parsePaginationParameters import com.egm.stellio.shared.model.parameter.GeoQuery.Companion.parseGeoQueryParameters -import com.egm.stellio.shared.model.parameter.QueryParam +import com.egm.stellio.shared.model.parameter.QueryParameter import com.egm.stellio.shared.util.JsonLdUtils import com.egm.stellio.shared.util.decode import com.egm.stellio.shared.util.expandTypeSelection @@ -28,18 +28,18 @@ fun composeEntitiesQueryFromGet( requestParams: MultiValueMap, contexts: List ): Either = either { - val ids = requestParams.getFirst(QueryParam.ID.key)?.split(",").orEmpty().toListOfUri().toSet() - val typeSelection = expandTypeSelection(requestParams.getFirst(QueryParam.TYPE.key), contexts) - val idPattern = validateIdPattern(requestParams.getFirst(QueryParam.ID_PATTERN.key)).bind() + val ids = requestParams.getFirst(QueryParameter.ID.key)?.split(",").orEmpty().toListOfUri().toSet() + val typeSelection = expandTypeSelection(requestParams.getFirst(QueryParameter.TYPE.key), contexts) + val idPattern = validateIdPattern(requestParams.getFirst(QueryParameter.ID_PATTERN.key)).bind() /** * Decoding query parameters is not supported by default so a call to a decode function was added query * with the right parameters values */ - val q = requestParams.getFirst(QueryParam.Q.key)?.decode() - val scopeQ = requestParams.getFirst(QueryParam.SCOPEQ.key) - val attrs = parseAndExpandRequestParameter(requestParams.getFirst(QueryParam.ATTRS.key), contexts) - val datasetId = parseRequestParameter(requestParams.getFirst(QueryParam.DATASET_ID.key)) + val q = requestParams.getFirst(QueryParameter.Q.key)?.decode() + val scopeQ = requestParams.getFirst(QueryParameter.SCOPEQ.key) + val attrs = parseAndExpandRequestParameter(requestParams.getFirst(QueryParameter.ATTRS.key), contexts) + val datasetId = parseRequestParameter(requestParams.getFirst(QueryParameter.DATASET_ID.key)) val paginationQuery = parsePaginationParameters( requestParams, defaultPagination.limitDefault, @@ -48,9 +48,9 @@ fun composeEntitiesQueryFromGet( val geoQuery = parseGeoQueryParameters(requestParams.toSingleValueMap(), contexts).bind() val linkedEntityQuery = parseLinkedEntityQueryParameters( - requestParams.getFirst(QueryParam.JOIN.key), - requestParams.getFirst(QueryParam.JOIN_LEVEL.key), - requestParams.getFirst(QueryParam.CONTAINED_BY.key) + requestParams.getFirst(QueryParameter.JOIN.key), + requestParams.getFirst(QueryParameter.JOIN_LEVEL.key), + requestParams.getFirst(QueryParameter.CONTAINED_BY.key) ).bind() EntitiesQueryFromGet( diff --git a/search-service/src/main/kotlin/com/egm/stellio/search/entity/web/EntityHandler.kt b/search-service/src/main/kotlin/com/egm/stellio/search/entity/web/EntityHandler.kt index e6c0c40fd..0107d4d7e 100644 --- a/search-service/src/main/kotlin/com/egm/stellio/search/entity/web/EntityHandler.kt +++ b/search-service/src/main/kotlin/com/egm/stellio/search/entity/web/EntityHandler.kt @@ -24,7 +24,7 @@ import com.egm.stellio.shared.model.NgsiLdDataRepresentation.Companion.parseRepr import com.egm.stellio.shared.model.ResourceNotFoundException import com.egm.stellio.shared.model.filterAttributes import com.egm.stellio.shared.model.parameter.AllowedParameters -import com.egm.stellio.shared.model.parameter.QueryParam +import com.egm.stellio.shared.model.parameter.QueryParameter import com.egm.stellio.shared.model.toFinalRepresentation import com.egm.stellio.shared.model.toNgsiLdEntity import com.egm.stellio.shared.util.GEO_JSON_CONTENT_TYPE @@ -66,7 +66,6 @@ import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.RestController import reactor.core.publisher.Mono import java.net.URI -import java.util.Optional @RestController @RequestMapping("/ngsi-ld/v1/entities") @@ -85,7 +84,9 @@ class EntityHandler( @PostMapping(consumes = [APPLICATION_JSON_VALUE, JSON_LD_CONTENT_TYPE]) suspend fun create( @RequestHeader httpHeaders: HttpHeaders, - @RequestBody requestBody: Mono + @RequestBody requestBody: Mono, + @AllowedParameters(implemented = [], notImplemented = [QueryParameter.LOCAL, QueryParameter.VIA]) + @RequestParam params: MultiValueMap ): ResponseEntity<*> = either { val sub = getSubFromSecurityContext() val (body, contexts) = @@ -110,13 +111,23 @@ class EntityHandler( suspend fun merge( @RequestHeader httpHeaders: HttpHeaders, @PathVariable entityId: URI, - @RequestParam options: MultiValueMap, + @RequestParam + @AllowedParameters( + implemented = [ + QueryParameter.OPTIONS, + QueryParameter.TYPE, + QueryParameter.OBSERVED_AT, + QueryParameter.LANG + ], + notImplemented = [QueryParameter.FORMAT, QueryParameter.LOCAL, QueryParameter.VIA] + ) + options: MultiValueMap, @RequestBody requestBody: Mono ): ResponseEntity<*> = either { val sub = getSubFromSecurityContext() val (body, contexts) = extractPayloadAndContexts(requestBody, httpHeaders, applicationProperties.contexts.core).bind() - val observedAt = options.getFirst(QueryParam.OptionValue.OBSERVED_AT.value) + val observedAt = options.getFirst(QueryParameter.OBSERVED_AT.key) ?.parseTimeParameter("'observedAt' parameter is not a valid date") ?.getOrElse { return@either BadRequestDataException(it).left().bind>() } val expandedAttributes = expandAttributes(body, contexts) @@ -148,7 +159,9 @@ class EntityHandler( suspend fun replace( @RequestHeader httpHeaders: HttpHeaders, @PathVariable entityId: URI, - @RequestBody requestBody: Mono + @RequestBody requestBody: Mono, + @AllowedParameters(implemented = [], notImplemented = [QueryParameter.LOCAL, QueryParameter.VIA]) + @RequestParam params: MultiValueMap ): ResponseEntity<*> = either { val sub = getSubFromSecurityContext() val (body, contexts) = @@ -185,8 +198,38 @@ class EntityHandler( @RequestHeader httpHeaders: HttpHeaders, @RequestParam @AllowedParameters( - implemented = [QueryParam.Q, QueryParam.TYPE, QueryParam.ID_PATTERN], - notImplemented = [QueryParam.ATTRS] + implemented = [ + QueryParameter.OPTIONS, + QueryParameter.FORMAT, + QueryParameter.COUNT, + QueryParameter.OFFSET, + QueryParameter.LIMIT, + QueryParameter.ID, + QueryParameter.TYPE, + QueryParameter.ID_PATTERN, + QueryParameter.ATTRS, + QueryParameter.Q, + QueryParameter.GEOMETRY, + QueryParameter.GEOREL, + QueryParameter.COORDINATES, + QueryParameter.GEOPROPERTY, + QueryParameter.GEOMETRY_PROPERTY, + QueryParameter.LANG, + QueryParameter.SCOPEQ, + QueryParameter.CONTAINED_BY, + QueryParameter.JOIN, + QueryParameter.JOIN_LEVEL, + QueryParameter.DATASET_ID, + ], + notImplemented = [ + QueryParameter.PICK, + QueryParameter.OMIT, + QueryParameter.EXPAND_VALUES, + QueryParameter.CSF, + QueryParameter.ENTITY_MAP, + QueryParameter.LOCAL, + QueryParameter.VIA + ] ) params: MultiValueMap ): ResponseEntity<*> = either { @@ -232,7 +275,28 @@ class EntityHandler( suspend fun getByURI( @RequestHeader httpHeaders: HttpHeaders, @PathVariable entityId: URI, - @RequestParam params: MultiValueMap + @RequestParam + @AllowedParameters( + implemented = [ + QueryParameter.OPTIONS, + QueryParameter.FORMAT, + QueryParameter.TYPE, + QueryParameter.ATTRS, + QueryParameter.GEOMETRY_PROPERTY, + QueryParameter.LANG, + QueryParameter.CONTAINED_BY, + QueryParameter.JOIN, + QueryParameter.JOIN_LEVEL, + QueryParameter.DATASET_ID, + ], + notImplemented = [ + QueryParameter.PICK, + QueryParameter.OMIT, + QueryParameter.ENTITY_MAP, + QueryParameter.LOCAL, QueryParameter.VIA + ] + ) + params: MultiValueMap ): ResponseEntity<*> = either { val mediaType = getApplicableMediaType(httpHeaders).bind() val sub = getSubFromSecurityContext() @@ -310,7 +374,9 @@ class EntityHandler( */ @DeleteMapping("/{entityId}") suspend fun delete( - @PathVariable entityId: URI + @PathVariable entityId: URI, + @AllowedParameters(implemented = [], notImplemented = [QueryParameter.LOCAL, QueryParameter.VIA]) + @RequestParam params: MultiValueMap ): ResponseEntity<*> = either { val sub = getSubFromSecurityContext() @@ -330,11 +396,17 @@ class EntityHandler( suspend fun appendEntityAttributes( @RequestHeader httpHeaders: HttpHeaders, @PathVariable entityId: URI, - @RequestParam options: Optional, - @RequestBody requestBody: Mono + @RequestBody requestBody: Mono, + @RequestParam + @AllowedParameters( + implemented = [QueryParameter.OPTIONS], // todo type no implemented? + notImplemented = [QueryParameter.TYPE, QueryParameter.LOCAL, QueryParameter.VIA] + ) + params: MultiValueMap ): ResponseEntity<*> = either { + val options = params.getFirst(QueryParameter.OPTIONS.key) val sub = getSubFromSecurityContext() - val disallowOverwrite = options.map { it == QueryParam.OptionValue.NO_OVERWRITE.value }.orElse(false) + val disallowOverwrite = options?.let { it == QueryParameter.NO_OVERWRITE.key } ?: false val (body, contexts) = extractPayloadAndContexts(requestBody, httpHeaders, applicationProperties.contexts.core).bind() @@ -371,7 +443,9 @@ class EntityHandler( suspend fun updateEntityAttributes( @RequestHeader httpHeaders: HttpHeaders, @PathVariable entityId: URI, - @RequestBody requestBody: Mono + @RequestBody requestBody: Mono, + @AllowedParameters(implemented = [], notImplemented = [QueryParameter.LOCAL, QueryParameter.VIA]) + @RequestParam params: MultiValueMap ): ResponseEntity<*> = either { val sub = getSubFromSecurityContext() val (body, contexts) = @@ -409,7 +483,9 @@ class EntityHandler( @RequestHeader httpHeaders: HttpHeaders, @PathVariable entityId: URI, @PathVariable attrId: String, - @RequestBody requestBody: Mono + @RequestBody requestBody: Mono, + @AllowedParameters(implemented = [], notImplemented = [QueryParameter.LOCAL, QueryParameter.VIA]) + @RequestParam params: MultiValueMap ): ResponseEntity<*> = either { val sub = getSubFromSecurityContext() @@ -448,11 +524,15 @@ class EntityHandler( @RequestHeader httpHeaders: HttpHeaders, @PathVariable entityId: URI, @PathVariable attrId: String, + @AllowedParameters( + implemented = [QueryParameter.DELETE_ALL, QueryParameter.TYPE, QueryParameter.DATASET_ID], + notImplemented = [QueryParameter.LOCAL, QueryParameter.VIA] + ) @RequestParam params: MultiValueMap ): ResponseEntity<*> = either { val sub = getSubFromSecurityContext() - val deleteAll = params.getFirst("deleteAll")?.toBoolean() ?: false - val datasetId = params.getFirst("datasetId")?.toUri() + val deleteAll = params.getFirst(QueryParameter.DELETE_ALL.key)?.toBoolean() ?: false + val datasetId = params.getFirst(QueryParameter.DATASET_ID.key)?.toUri() val contexts = getContextFromLinkHeaderOrDefault(httpHeaders, applicationProperties.contexts.core).bind() val expandedAttrId = expandJsonLdTerm(attrId, contexts) @@ -483,7 +563,9 @@ class EntityHandler( @RequestHeader httpHeaders: HttpHeaders, @PathVariable entityId: URI, @PathVariable attrId: String, - @RequestBody requestBody: Mono + @RequestBody requestBody: Mono, + @AllowedParameters(implemented = [], notImplemented = [QueryParameter.LOCAL, QueryParameter.VIA]) + @RequestParam params: MultiValueMap ): ResponseEntity<*> = either { val sub = getSubFromSecurityContext() val (body, contexts) = diff --git a/search-service/src/main/kotlin/com/egm/stellio/search/entity/web/EntityOperationHandler.kt b/search-service/src/main/kotlin/com/egm/stellio/search/entity/web/EntityOperationHandler.kt index 3e9f12d56..a4c9de4be 100644 --- a/search-service/src/main/kotlin/com/egm/stellio/search/entity/web/EntityOperationHandler.kt +++ b/search-service/src/main/kotlin/com/egm/stellio/search/entity/web/EntityOperationHandler.kt @@ -15,7 +15,7 @@ import com.egm.stellio.shared.model.BadRequestDataException import com.egm.stellio.shared.model.LdContextNotAvailableException import com.egm.stellio.shared.model.NgsiLdDataRepresentation.Companion.parseRepresentations import com.egm.stellio.shared.model.filterAttributes -import com.egm.stellio.shared.model.parameter.QueryParam +import com.egm.stellio.shared.model.parameter.QueryParameter import com.egm.stellio.shared.model.toFinalRepresentation import com.egm.stellio.shared.model.toNgsiLdEntity import com.egm.stellio.shared.util.GEO_JSON_CONTENT_TYPE @@ -144,7 +144,7 @@ class EntityOperationHandler( val (parsedEntities, unparsableEntities) = prepareEntitiesFromRequestBody(requestBody, httpHeaders).bind() - val disallowOverwrite = options.map { it == QueryParam.OptionValue.NO_OVERWRITE.value }.orElse(false) + val disallowOverwrite = options.map { it == QueryParameter.NO_OVERWRITE.key }.orElse(false) val batchOperationResult = BatchOperationResult().apply { addEntitiesToErrors(unparsableEntities) diff --git a/search-service/src/main/kotlin/com/egm/stellio/search/scope/ScopeService.kt b/search-service/src/main/kotlin/com/egm/stellio/search/scope/ScopeService.kt index 535b6cb07..500ce6db3 100644 --- a/search-service/src/main/kotlin/com/egm/stellio/search/scope/ScopeService.kt +++ b/search-service/src/main/kotlin/com/egm/stellio/search/scope/ScopeService.kt @@ -31,12 +31,12 @@ import com.egm.stellio.shared.model.ExpandedAttributeInstances import com.egm.stellio.shared.model.NgsiLdEntity import com.egm.stellio.shared.model.OperationNotSupportedException import com.egm.stellio.shared.model.getScopes -import com.egm.stellio.shared.model.parameter.TemporalQueryParameter.Companion.WHOLE_TIME_RANGE_DURATION import com.egm.stellio.shared.util.INCONSISTENT_VALUES_IN_AGGREGATION_MESSAGE import com.egm.stellio.shared.util.JsonLdUtils import com.egm.stellio.shared.util.JsonLdUtils.NGSILD_SCOPE_PROPERTY import com.egm.stellio.shared.util.JsonUtils.serializeObject import com.egm.stellio.shared.util.Sub +import com.egm.stellio.shared.util.WHOLE_TIME_RANGE_DURATION import io.r2dbc.postgresql.codec.Json import org.springframework.r2dbc.core.DatabaseClient import org.springframework.r2dbc.core.bind diff --git a/search-service/src/main/kotlin/com/egm/stellio/search/temporal/service/AttributeInstanceService.kt b/search-service/src/main/kotlin/com/egm/stellio/search/temporal/service/AttributeInstanceService.kt index 1f410c871..15afb8c85 100644 --- a/search-service/src/main/kotlin/com/egm/stellio/search/temporal/service/AttributeInstanceService.kt +++ b/search-service/src/main/kotlin/com/egm/stellio/search/temporal/service/AttributeInstanceService.kt @@ -32,9 +32,9 @@ import com.egm.stellio.shared.model.ExpandedAttributeInstances import com.egm.stellio.shared.model.ExpandedTerm import com.egm.stellio.shared.model.OperationNotSupportedException import com.egm.stellio.shared.model.ResourceNotFoundException -import com.egm.stellio.shared.model.parameter.TemporalQueryParameter.Companion.WHOLE_TIME_RANGE_DURATION import com.egm.stellio.shared.model.toNgsiLdAttribute import com.egm.stellio.shared.util.INCONSISTENT_VALUES_IN_AGGREGATION_MESSAGE +import com.egm.stellio.shared.util.WHOLE_TIME_RANGE_DURATION import com.egm.stellio.shared.util.attributeOrInstanceNotFoundMessage import com.egm.stellio.shared.util.ngsiLdDateTime import org.springframework.r2dbc.core.DatabaseClient diff --git a/search-service/src/main/kotlin/com/egm/stellio/search/temporal/util/TemporalQueryUtils.kt b/search-service/src/main/kotlin/com/egm/stellio/search/temporal/util/TemporalQueryUtils.kt index 8c6d8b50e..8187c3564 100644 --- a/search-service/src/main/kotlin/com/egm/stellio/search/temporal/util/TemporalQueryUtils.kt +++ b/search-service/src/main/kotlin/com/egm/stellio/search/temporal/util/TemporalQueryUtils.kt @@ -20,10 +20,9 @@ import com.egm.stellio.search.temporal.model.TemporalQuery.Timerel import com.egm.stellio.shared.config.ApplicationProperties import com.egm.stellio.shared.model.APIException import com.egm.stellio.shared.model.BadRequestDataException -import com.egm.stellio.shared.model.parameter.QueryParam -import com.egm.stellio.shared.model.parameter.TemporalQueryParameter -import com.egm.stellio.shared.model.parameter.TemporalQueryParameter.Companion.WHOLE_TIME_RANGE_DURATION +import com.egm.stellio.shared.model.parameter.QueryParameter import com.egm.stellio.shared.util.OptionsParamValue +import com.egm.stellio.shared.util.WHOLE_TIME_RANGE_DURATION import com.egm.stellio.shared.util.hasValueInOptionsParam import com.egm.stellio.shared.util.parseTimeParameter import org.springframework.util.MultiValueMap @@ -47,15 +46,15 @@ fun composeTemporalEntitiesQueryFromGet( entitiesQueryFromGet.validateMinimalQueryEntitiesParameters().bind() val withTemporalValues = hasValueInOptionsParam( - Optional.ofNullable(requestParams.getFirst(QueryParam.OPTIONS.key)), + Optional.ofNullable(requestParams.getFirst(QueryParameter.OPTIONS.key)), OptionsParamValue.TEMPORAL_VALUES ) val withAudit = hasValueInOptionsParam( - Optional.ofNullable(requestParams.getFirst(QueryParam.OPTIONS.key)), + Optional.ofNullable(requestParams.getFirst(QueryParameter.OPTIONS.key)), OptionsParamValue.AUDIT ) val withAggregatedValues = hasValueInOptionsParam( - Optional.ofNullable(requestParams.getFirst(QueryParam.OPTIONS.key)), + Optional.ofNullable(requestParams.getFirst(QueryParameter.OPTIONS.key)), OptionsParamValue.AGGREGATED_VALUES ) val temporalQuery = @@ -84,26 +83,26 @@ fun composeTemporalEntitiesQueryFromPost( ).bind() val withTemporalValues = hasValueInOptionsParam( - Optional.ofNullable(requestParams.getFirst(QueryParam.OPTIONS.key)), + Optional.ofNullable(requestParams.getFirst(QueryParameter.OPTIONS.key)), OptionsParamValue.TEMPORAL_VALUES ) val withAudit = hasValueInOptionsParam( - Optional.ofNullable(requestParams.getFirst(QueryParam.OPTIONS.key)), + Optional.ofNullable(requestParams.getFirst(QueryParameter.OPTIONS.key)), OptionsParamValue.AUDIT ) val withAggregatedValues = hasValueInOptionsParam( - Optional.ofNullable(requestParams.getFirst(QueryParam.OPTIONS.key)), + Optional.ofNullable(requestParams.getFirst(QueryParameter.OPTIONS.key)), OptionsParamValue.AGGREGATED_VALUES ) val temporalParams = mapOf( - TemporalQueryParameter.TIMEREL.key to listOf(query.temporalQ?.timerel), - TemporalQueryParameter.TIMEAT.key to listOf(query.temporalQ?.timeAt), - TemporalQueryParameter.ENDTIMEAT.key to listOf(query.temporalQ?.endTimeAt), - TemporalQueryParameter.AGGRPERIODDURATION.key to listOf(query.temporalQ?.aggrPeriodDuration), - TemporalQueryParameter.AGGRMETHODS.key to query.temporalQ?.aggrMethods, - TemporalQueryParameter.LASTN.key to listOf(query.temporalQ?.lastN.toString()), - TemporalQueryParameter.TIMEPROPERTY.key to listOf(query.temporalQ?.timeproperty) + QueryParameter.TIMEREL.key to listOf(query.temporalQ?.timerel), + QueryParameter.TIMEAT.key to listOf(query.temporalQ?.timeAt), + QueryParameter.ENDTIMEAT.key to listOf(query.temporalQ?.endTimeAt), + QueryParameter.AGGRPERIODDURATION.key to listOf(query.temporalQ?.aggrPeriodDuration), + QueryParameter.AGGRMETHODS.key to query.temporalQ?.aggrMethods, + QueryParameter.LASTN.key to listOf(query.temporalQ?.lastN.toString()), + QueryParameter.TIMEPROPERTY.key to listOf(query.temporalQ?.timeproperty) ) val temporalQuery = buildTemporalQuery( MultiValueMapAdapter(temporalParams), @@ -128,16 +127,16 @@ fun buildTemporalQuery( inQueryEntities: Boolean = false, withAggregatedValues: Boolean = false, ): Either { - val timerelParam = params.getFirst(TemporalQueryParameter.TIMEREL.key) - val timeAtParam = params.getFirst(TemporalQueryParameter.TIMEAT.key) - val endTimeAtParam = params.getFirst(TemporalQueryParameter.ENDTIMEAT.key) + val timerelParam = params.getFirst(QueryParameter.TIMEREL.key) + val timeAtParam = params.getFirst(QueryParameter.TIMEAT.key) + val endTimeAtParam = params.getFirst(QueryParameter.ENDTIMEAT.key) val aggrPeriodDurationParam = if (withAggregatedValues) - params.getFirst(TemporalQueryParameter.AGGRPERIODDURATION.key) ?: WHOLE_TIME_RANGE_DURATION + params.getFirst(QueryParameter.AGGRPERIODDURATION.key) ?: WHOLE_TIME_RANGE_DURATION else null - val aggrMethodsParam = params.getFirst(TemporalQueryParameter.AGGRMETHODS.key) - val lastNParam = params.getFirst(TemporalQueryParameter.LASTN.key) - val timeproperty = params.getFirst(TemporalQueryParameter.TIMEPROPERTY.key)?.let { + val aggrMethodsParam = params.getFirst(QueryParameter.AGGRMETHODS.key) + val lastNParam = params.getFirst(QueryParameter.LASTN.key) + val timeproperty = params.getFirst(QueryParameter.TIMEPROPERTY.key)?.let { AttributeInstance.TemporalProperty.forPropertyName(it) } ?: AttributeInstance.TemporalProperty.OBSERVED_AT diff --git a/search-service/src/test/kotlin/com/egm/stellio/search/csr/service/ContextSourceCallerTests.kt b/search-service/src/test/kotlin/com/egm/stellio/search/csr/service/ContextSourceCallerTests.kt index aa0e1df75..092a662c5 100644 --- a/search-service/src/test/kotlin/com/egm/stellio/search/csr/service/ContextSourceCallerTests.kt +++ b/search-service/src/test/kotlin/com/egm/stellio/search/csr/service/ContextSourceCallerTests.kt @@ -4,7 +4,7 @@ import com.egm.stellio.search.csr.CsrUtils.gimmeRawCSR import com.egm.stellio.search.csr.model.MiscellaneousPersistentWarning import com.egm.stellio.search.csr.model.MiscellaneousWarning import com.egm.stellio.search.csr.model.RevalidationFailedWarning -import com.egm.stellio.shared.model.parameter.QueryParam +import com.egm.stellio.shared.model.parameter.QueryParameter import com.egm.stellio.shared.util.APIC_COMPOUND_CONTEXT import com.egm.stellio.shared.util.GEO_JSON_CONTENT_TYPE import com.egm.stellio.shared.util.GEO_JSON_MEDIA_TYPE @@ -175,7 +175,7 @@ class ContextSourceCallerTests { get(urlMatching(path)) .willReturn(notFound()) ) - val params = LinkedMultiValueMap(mapOf(QueryParam.OPTIONS.key to listOf("simplified"))) + val params = LinkedMultiValueMap(mapOf(QueryParameter.OPTIONS.key to listOf("simplified"))) ContextSourceCaller.getDistributedInformation( HttpHeaders.EMPTY, csr, @@ -184,7 +184,7 @@ class ContextSourceCallerTests { ) verify( getRequestedFor(urlPathEqualTo(path)) - .withQueryParam(QueryParam.OPTIONS.key, notContaining("simplified")) + .withQueryParam(QueryParameter.OPTIONS.key, notContaining("simplified")) ) } } diff --git a/search-service/src/test/kotlin/com/egm/stellio/search/entity/service/LinkedEntityServiceTests.kt b/search-service/src/test/kotlin/com/egm/stellio/search/entity/service/LinkedEntityServiceTests.kt index d20dcc68c..961c50bbf 100644 --- a/search-service/src/test/kotlin/com/egm/stellio/search/entity/service/LinkedEntityServiceTests.kt +++ b/search-service/src/test/kotlin/com/egm/stellio/search/entity/service/LinkedEntityServiceTests.kt @@ -4,8 +4,8 @@ import arrow.core.right import com.egm.stellio.search.entity.model.EntitiesQueryFromGet import com.egm.stellio.shared.model.CompactedEntity import com.egm.stellio.shared.model.ExpandedEntity +import com.egm.stellio.shared.model.JoinType import com.egm.stellio.shared.model.LinkedEntityQuery -import com.egm.stellio.shared.model.LinkedEntityQuery.JoinType import com.egm.stellio.shared.model.PaginationQuery import com.egm.stellio.shared.util.JsonUtils.deserializeAsMap import com.egm.stellio.shared.util.JsonUtils.serializeObject diff --git a/search-service/src/test/kotlin/com/egm/stellio/search/entity/util/EntitiesQueryUtilsTests.kt b/search-service/src/test/kotlin/com/egm/stellio/search/entity/util/EntitiesQueryUtilsTests.kt index 6f4da694a..bc062cacc 100644 --- a/search-service/src/test/kotlin/com/egm/stellio/search/entity/util/EntitiesQueryUtilsTests.kt +++ b/search-service/src/test/kotlin/com/egm/stellio/search/entity/util/EntitiesQueryUtilsTests.kt @@ -9,9 +9,9 @@ import com.egm.stellio.shared.config.ApplicationProperties import com.egm.stellio.shared.model.APIException import com.egm.stellio.shared.model.BadRequestDataException import com.egm.stellio.shared.model.EntitySelector -import com.egm.stellio.shared.model.LinkedEntityQuery.JoinType +import com.egm.stellio.shared.model.JoinType import com.egm.stellio.shared.model.parameter.GeoQuery -import com.egm.stellio.shared.model.parameter.GeoQueryParameter +import com.egm.stellio.shared.model.parameter.Georel import com.egm.stellio.shared.util.APIARY_TYPE import com.egm.stellio.shared.util.APIC_COMPOUND_CONTEXTS import com.egm.stellio.shared.util.BEEHIVE_TYPE @@ -239,7 +239,7 @@ class EntitiesQueryUtilsTests { assertEquals("temperature>32", it.q) assertEquals(GeoQuery.GeometryType.POINT, it.geoQuery?.geometry) assertEquals("[1.0, 1.0]", it.geoQuery?.coordinates) - assertEquals(GeoQueryParameter.Georel.EQUALS.key, it.geoQuery?.georel) + assertEquals(Georel.EQUALS.key, it.geoQuery?.georel) assertEquals(NGSILD_OBSERVATION_SPACE_PROPERTY, it.geoQuery?.geoproperty) assertEquals("/Nantes", it.scopeQ) assertEquals(setOf("urn:ngsi-ld:Dataset:Test1", "urn:ngsi-ld:Dataset:Test2"), it.datasetId) diff --git a/shared/src/main/kotlin/com/egm/stellio/shared/model/CompactedEntity.kt b/shared/src/main/kotlin/com/egm/stellio/shared/model/CompactedEntity.kt index 01355ece0..83cc7d78f 100644 --- a/shared/src/main/kotlin/com/egm/stellio/shared/model/CompactedEntity.kt +++ b/shared/src/main/kotlin/com/egm/stellio/shared/model/CompactedEntity.kt @@ -6,7 +6,7 @@ import com.egm.stellio.shared.model.AttributeCompactedType.LANGUAGEPROPERTY import com.egm.stellio.shared.model.AttributeCompactedType.PROPERTY import com.egm.stellio.shared.model.AttributeCompactedType.RELATIONSHIP import com.egm.stellio.shared.model.AttributeCompactedType.VOCABPROPERTY -import com.egm.stellio.shared.model.parameter.QueryParam +import com.egm.stellio.shared.model.parameter.QueryParameter import com.egm.stellio.shared.util.FEATURES_PROPERTY_TERM import com.egm.stellio.shared.util.FEATURE_COLLECTION_TYPE import com.egm.stellio.shared.util.FEATURE_TYPE @@ -117,7 +117,7 @@ private fun simplifyAttribute(value: Map): Any { } fun CompactedEntity.toFilteredLanguageProperties(languageFilter: String): CompactedEntity { - val transformationParameters = mapOf(QueryParam.LANG.key to languageFilter) + val transformationParameters = mapOf(QueryParameter.LANG.key to languageFilter) return this.mapValues { entry -> applyAttributeTransformation( entry, @@ -136,7 +136,7 @@ private fun filterMultiInstanceLanguageProperty( } private fun filterLanguageProperty(value: Map, transformationParameters: Map?): Any { - val languageFilter = transformationParameters?.get(QueryParam.LANG.key)!! + val languageFilter = transformationParameters?.get(QueryParameter.LANG.key)!! val attributeCompactedType = value[JSONLD_TYPE_TERM]?.let { AttributeCompactedType.forKey(value[JSONLD_TYPE_TERM] as String) } diff --git a/shared/src/main/kotlin/com/egm/stellio/shared/model/GeoQueryUtils.kt b/shared/src/main/kotlin/com/egm/stellio/shared/model/GeoQueryUtils.kt new file mode 100644 index 000000000..edfe7bdb9 --- /dev/null +++ b/shared/src/main/kotlin/com/egm/stellio/shared/model/GeoQueryUtils.kt @@ -0,0 +1,18 @@ +package com.egm.stellio.shared.model + +import arrow.core.Either +import com.egm.stellio.shared.model.parameter.GeoQuery +import com.egm.stellio.shared.util.geoJsonToWkt + +fun stringifyCoordinates(coordinates: Any): String = + when (coordinates) { + is String -> coordinates + is List<*> -> coordinates.toString() + else -> coordinates.toString() + } + +fun parseGeometryToWKT( + geometryType: GeoQuery.GeometryType, + coordinates: String +): Either = + geoJsonToWkt(geometryType, coordinates) diff --git a/shared/src/main/kotlin/com/egm/stellio/shared/model/NgsiLdDataRepresentation.kt b/shared/src/main/kotlin/com/egm/stellio/shared/model/NgsiLdDataRepresentation.kt index f3ffde0f6..87d1d8c18 100644 --- a/shared/src/main/kotlin/com/egm/stellio/shared/model/NgsiLdDataRepresentation.kt +++ b/shared/src/main/kotlin/com/egm/stellio/shared/model/NgsiLdDataRepresentation.kt @@ -1,6 +1,6 @@ package com.egm.stellio.shared.model -import com.egm.stellio.shared.model.parameter.QueryParam +import com.egm.stellio.shared.model.parameter.QueryParameter import com.egm.stellio.shared.util.GEO_JSON_MEDIA_TYPE import com.egm.stellio.shared.util.JSON_LD_MEDIA_TYPE import com.egm.stellio.shared.util.JsonLdUtils.NGSILD_LOCATION_TERM @@ -26,15 +26,15 @@ data class NgsiLdDataRepresentation( requestParams: MultiValueMap, acceptMediaType: MediaType ): NgsiLdDataRepresentation { - val optionsParam = requestParams.getOrDefault(QueryParam.OPTIONS.key, emptyList()) - val includeSysAttrs = optionsParam.contains(QueryParam.OptionValue.SYS_ATTRS.value) - val attributeRepresentation = optionsParam.contains(QueryParam.OptionValue.KEY_VALUES.value) + val optionsParam = requestParams.getOrDefault(QueryParameter.OPTIONS.key, emptyList()) + val includeSysAttrs = optionsParam.contains(QueryParameter.SYS_ATTRS.key) + val attributeRepresentation = optionsParam.contains(QueryParameter.KEY_VALUES.key) .let { if (it) AttributeRepresentation.SIMPLIFIED else AttributeRepresentation.NORMALIZED } - val languageFilter = requestParams.getFirst(QueryParam.LANG.key) + val languageFilter = requestParams.getFirst(QueryParameter.LANG.key) val entityRepresentation = EntityRepresentation.forMediaType(acceptMediaType) val geometryProperty = if (entityRepresentation == EntityRepresentation.GEO_JSON) - requestParams.getFirst(QueryParam.GEOMETRY_PROPERTY.key) ?: NGSILD_LOCATION_TERM + requestParams.getFirst(QueryParameter.GEOMETRY_PROPERTY.key) ?: NGSILD_LOCATION_TERM else null val timeproperty = requestParams.getFirst("timeproperty") diff --git a/shared/src/main/kotlin/com/egm/stellio/shared/model/PaginationQuery.kt b/shared/src/main/kotlin/com/egm/stellio/shared/model/PaginationQuery.kt index 7dc37ec61..5ad0e7722 100644 --- a/shared/src/main/kotlin/com/egm/stellio/shared/model/PaginationQuery.kt +++ b/shared/src/main/kotlin/com/egm/stellio/shared/model/PaginationQuery.kt @@ -3,9 +3,7 @@ package com.egm.stellio.shared.model import arrow.core.Either import arrow.core.left import arrow.core.right -import com.egm.stellio.shared.model.parameter.PaginationParameter.COUNT -import com.egm.stellio.shared.model.parameter.PaginationParameter.LIMIT -import com.egm.stellio.shared.model.parameter.PaginationParameter.OFFSET +import com.egm.stellio.shared.model.parameter.QueryParameter import org.springframework.util.MultiValueMap data class PaginationQuery( @@ -20,9 +18,9 @@ data class PaginationQuery( limitDefault: Int, limitMax: Int ): Either { - val count = queryParams.getFirst(COUNT.key)?.toBoolean() ?: false - val offset = queryParams.getFirst(OFFSET.key)?.toIntOrNull() ?: 0 - val limit = queryParams.getFirst(LIMIT.key)?.toIntOrNull() ?: limitDefault + val count = queryParams.getFirst(QueryParameter.COUNT.key)?.toBoolean() ?: false + val offset = queryParams.getFirst(QueryParameter.OFFSET.key)?.toIntOrNull() ?: 0 + val limit = queryParams.getFirst(QueryParameter.LIMIT.key)?.toIntOrNull() ?: limitDefault if (!count && (limit <= 0 || offset < 0)) return BadRequestDataException( "Offset must be greater than zero and limit must be strictly greater than zero" diff --git a/shared/src/main/kotlin/com/egm/stellio/shared/model/parameter/AllowedParameters.kt b/shared/src/main/kotlin/com/egm/stellio/shared/model/parameter/AllowedParameters.kt index 253e351ce..f408ab42c 100644 --- a/shared/src/main/kotlin/com/egm/stellio/shared/model/parameter/AllowedParameters.kt +++ b/shared/src/main/kotlin/com/egm/stellio/shared/model/parameter/AllowedParameters.kt @@ -9,15 +9,14 @@ import kotlin.reflect.KClass @Target( AnnotationTarget.VALUE_PARAMETER, - AnnotationTarget.FUNCTION, ) @Retention( AnnotationRetention.RUNTIME ) @Constraint(validatedBy = [ AllowedParameters.ParamValidator::class]) annotation class AllowedParameters( - val implemented: Array, - val notImplemented: Array = [], + val implemented: Array, + val notImplemented: Array = [], val message: String = "Invalid parameter received", val groups: Array> = [], val payload: Array> = [], @@ -28,8 +27,8 @@ annotation class AllowedParameters( private var notImplemented: List = listOf() override fun initialize(requiredIfChecked: AllowedParameters) { - this.implemented = requiredIfChecked.implemented.map(QueryParam::key) - this.notImplemented = requiredIfChecked.notImplemented.map(QueryParam::key) + this.implemented = requiredIfChecked.implemented.map(QueryParameter::key) + this.notImplemented = requiredIfChecked.notImplemented.map(QueryParameter::key) } override fun isValid(value: MultiValueMap?, context: ConstraintValidatorContext): Boolean { diff --git a/shared/src/main/kotlin/com/egm/stellio/shared/model/parameter/GeoQuery.kt b/shared/src/main/kotlin/com/egm/stellio/shared/model/parameter/GeoQuery.kt index 7d37be046..54fa0274a 100644 --- a/shared/src/main/kotlin/com/egm/stellio/shared/model/parameter/GeoQuery.kt +++ b/shared/src/main/kotlin/com/egm/stellio/shared/model/parameter/GeoQuery.kt @@ -9,9 +9,8 @@ import com.egm.stellio.shared.model.BadRequestDataException import com.egm.stellio.shared.model.ExpandedEntity import com.egm.stellio.shared.model.ExpandedTerm import com.egm.stellio.shared.model.WKTCoordinates -import com.egm.stellio.shared.model.parameter.GeoQueryParameter.Companion.parseGeometryToWKT -import com.egm.stellio.shared.model.parameter.GeoQueryParameter.Companion.stringifyCoordinates -import com.egm.stellio.shared.model.parameter.GeoQueryParameter.Georel +import com.egm.stellio.shared.model.parseGeometryToWKT +import com.egm.stellio.shared.model.stringifyCoordinates import com.egm.stellio.shared.util.JsonLdUtils import com.egm.stellio.shared.util.JsonLdUtils.JSONLD_VALUE import com.egm.stellio.shared.util.JsonLdUtils.NGSILD_GEOPROPERTY_VALUE @@ -50,19 +49,19 @@ data class GeoQuery( requestParams: Map, contexts: List ): Either = either { - val georel = requestParams[GeoQueryParameter.GEOREL.key]?.decode()?.also { + val georel = requestParams[QueryParameter.GEOREL.key]?.decode()?.also { Georel.verify(it).bind() } - val geometry = requestParams[GeoQueryParameter.GEOMETRY.key]?.let { + val geometry = requestParams[QueryParameter.GEOMETRY.key]?.let { if (GeoQuery.GeometryType.isSupportedType(it)) GeoQuery.GeometryType.forType(it).right() else BadRequestDataException("$it is not a recognized value for 'geometry' parameter").left() }?.bind() - val coordinates = requestParams[GeoQueryParameter.COORDINATES.key]?.decode()?.let { + val coordinates = requestParams[QueryParameter.COORDINATES.key]?.decode()?.let { stringifyCoordinates(it) } - val geoproperty = requestParams[GeoQueryParameter.GEOPROPERTY.key]?.let { + val geoproperty = requestParams[QueryParameter.GEOPROPERTY.key]?.let { expandJsonLdTerm(it, contexts) } ?: JsonLdUtils.NGSILD_LOCATION_PROPERTY @@ -90,10 +89,10 @@ data class GeoQuery( """ (select jsonb_path_query_first(#{TARGET}#, '$."${geoQuery.geoproperty}"."$NGSILD_GEOPROPERTY_VALUE"[0]')->>'$JSONLD_VALUE') """.trimIndent() - val georelQuery = GeoQueryParameter.Georel.prepareQuery(geoQuery.georel) + val georelQuery = Georel.prepareQuery(geoQuery.georel) return ( - if (georelQuery.first == GeoQueryParameter.Georel.NEAR_DISTANCE_MODIFIER) + if (georelQuery.first == Georel.NEAR_DISTANCE_MODIFIER) """ public.ST_Distance( cast('SRID=4326;${geoQuery.wktCoordinates.value}' as public.geography), diff --git a/shared/src/main/kotlin/com/egm/stellio/shared/model/parameter/GeoQueryParameter.kt b/shared/src/main/kotlin/com/egm/stellio/shared/model/parameter/GeoQueryParameter.kt deleted file mode 100644 index d69accaa2..000000000 --- a/shared/src/main/kotlin/com/egm/stellio/shared/model/parameter/GeoQueryParameter.kt +++ /dev/null @@ -1,69 +0,0 @@ -package com.egm.stellio.shared.model.parameter - -import arrow.core.Either -import arrow.core.left -import arrow.core.right -import com.egm.stellio.shared.model.APIException -import com.egm.stellio.shared.model.BadRequestDataException -import com.egm.stellio.shared.model.WKTCoordinates -import com.egm.stellio.shared.util.geoJsonToWkt - -enum class GeoQueryParameter( - override val key: String, - override val implemented: Boolean = true, -) : QueryParameter { - GEOREL("georel"), - GEOMETRY("geometry"), - COORDINATES("coordinates"), - GEOPROPERTY("geoproperty"); - enum class Georel(val key: String) { - NEAR("near"), - WITHIN("within"), - CONTAINS("contains"), - INTERSECTS("intersects"), - EQUALS("equals"), - DISJOINT("disjoint"), - OVERLAPS("overlaps"); - companion object { - val ALL = Georel.entries.map { it.key } - - const val NEAR_DISTANCE_MODIFIER = "distance" - const val NEAR_MAXDISTANCE_MODIFIER = "maxDistance" - private val nearRegex = "^near;(?:minDistance|maxDistance)==\\d+$".toRegex() - - fun verify(georel: String): Either { - if (georel.startsWith(GeoQueryParameter.Georel.NEAR.key)) { - if (!georel.matches(nearRegex)) - return BadRequestDataException("Invalid expression for 'near' georel: $georel").left() - return Unit.right() - } else if (GeoQueryParameter.Georel.ALL.any { georel == it }) - return Unit.right() - else return BadRequestDataException("Invalid 'georel' parameter provided: $georel").left() - } - - fun prepareQuery(georel: String): Triple = - if (georel.startsWith(GeoQueryParameter.Georel.NEAR.key)) { - val comparisonParams = georel.split(";")[1].split("==") - if (comparisonParams[0] == NEAR_MAXDISTANCE_MODIFIER) - Triple(NEAR_DISTANCE_MODIFIER, "<=", comparisonParams[1]) - else Triple(NEAR_DISTANCE_MODIFIER, ">=", comparisonParams[1]) - } else Triple(georel, null, null) - } - } - - companion object { - - fun stringifyCoordinates(coordinates: Any): String = - when (coordinates) { - is String -> coordinates - is List<*> -> coordinates.toString() - else -> coordinates.toString() - } - - fun parseGeometryToWKT( - geometryType: GeoQuery.GeometryType, - coordinates: String - ): Either = - geoJsonToWkt(geometryType, coordinates) - } -} diff --git a/shared/src/main/kotlin/com/egm/stellio/shared/model/parameter/Georel.kt b/shared/src/main/kotlin/com/egm/stellio/shared/model/parameter/Georel.kt new file mode 100644 index 000000000..973ff3e81 --- /dev/null +++ b/shared/src/main/kotlin/com/egm/stellio/shared/model/parameter/Georel.kt @@ -0,0 +1,42 @@ +package com.egm.stellio.shared.model.parameter + +import arrow.core.Either +import arrow.core.left +import arrow.core.right +import com.egm.stellio.shared.model.APIException +import com.egm.stellio.shared.model.BadRequestDataException + +enum class Georel(val key: String) { + NEAR("near"), + WITHIN("within"), + CONTAINS("contains"), + INTERSECTS("intersects"), + EQUALS("equals"), + DISJOINT("disjoint"), + OVERLAPS("overlaps"); + companion object { + val ALL = entries.map { it.key } + + const val NEAR_DISTANCE_MODIFIER = "distance" + const val NEAR_MAXDISTANCE_MODIFIER = "maxDistance" + private val nearRegex = "^near;(?:minDistance|maxDistance)==\\d+$".toRegex() + + fun verify(georel: String): Either { + if (georel.startsWith(NEAR.key)) { + if (!georel.matches(nearRegex)) + return BadRequestDataException("Invalid expression for 'near' georel: $georel").left() + return Unit.right() + } else if (ALL.any { georel == it }) + return Unit.right() + else return BadRequestDataException("Invalid 'georel' parameter provided: $georel").left() + } + + fun prepareQuery(georel: String): Triple = + if (georel.startsWith(NEAR.key)) { + val comparisonParams = georel.split(";")[1].split("==") + if (comparisonParams[0] == NEAR_MAXDISTANCE_MODIFIER) + Triple(NEAR_DISTANCE_MODIFIER, "<=", comparisonParams[1]) + else Triple(NEAR_DISTANCE_MODIFIER, ">=", comparisonParams[1]) + } else Triple(georel, null, null) + } +} diff --git a/shared/src/main/kotlin/com/egm/stellio/shared/model/parameter/PaginationParameter.kt b/shared/src/main/kotlin/com/egm/stellio/shared/model/parameter/PaginationParameter.kt deleted file mode 100644 index e2495011c..000000000 --- a/shared/src/main/kotlin/com/egm/stellio/shared/model/parameter/PaginationParameter.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.egm.stellio.shared.model.parameter - -enum class PaginationParameter( - override val key: String, - override val implemented: Boolean = true, -) : QueryParameter { - COUNT("count",), - OFFSET("offset"), - LIMIT("limit") -} diff --git a/shared/src/main/kotlin/com/egm/stellio/shared/model/parameter/QueryParameter.kt b/shared/src/main/kotlin/com/egm/stellio/shared/model/parameter/QueryParameter.kt index 787240cb5..53b69da5e 100644 --- a/shared/src/main/kotlin/com/egm/stellio/shared/model/parameter/QueryParameter.kt +++ b/shared/src/main/kotlin/com/egm/stellio/shared/model/parameter/QueryParameter.kt @@ -2,15 +2,9 @@ package com.egm.stellio.shared.model.parameter import java.util.regex.Pattern -sealed interface QueryParameter { - val key: String - val implemented: Boolean -} - -enum class QueryParam( - override val key: String, - override val implemented: Boolean = true, -) : QueryParameter { +enum class QueryParameter( + val key: String, +) { ID("id"), TYPE("type"), ID_PATTERN("idPattern"), @@ -20,21 +14,53 @@ enum class QueryParam( GEOMETRY_PROPERTY("geometryProperty"), LANG("lang"), DATASET_ID("datasetId"), - OPTIONS("options"), CONTAINED_BY("containedBy"), JOIN("join"), JOIN_LEVEL("joinLevel"), - PICK("pick", false), - OMIT("omit", false); // todo other not implemented parameters - - enum class OptionValue(val value: String) { - SYS_ATTRS("sysAttrs"), - KEY_VALUES("keyValues"), - NO_OVERWRITE("noOverwrite"), - OBSERVED_AT("observedAt"); - companion object { - val ALL = OptionValue.entries.map { it.value } - } + OPTIONS("options"), + + // options + SYS_ATTRS("sysAttrs"), + KEY_VALUES("keyValues"), + NO_OVERWRITE("noOverwrite"), + OBSERVED_AT("observedAt"), + + // geoQuery + GEOREL("georel"), + GEOMETRY("geometry"), + COORDINATES("coordinates"), + GEOPROPERTY("geoproperty"), + + // temporal + TIMEREL("timerel"), + TIMEAT("timeAt"), + ENDTIMEAT("endTimeAt"), + AGGRPERIODDURATION("aggrPeriodDuration"), + AGGRMETHODS("aggrMethods"), + LASTN("lastN"), + TIMEPROPERTY("timeproperty"), + + // pagination + COUNT("count",), + OFFSET("offset"), + LIMIT("limit"), + + DELETE_ALL("deleteAll"), + + // not implemented yet + FORMAT("format"), + PICK("pick"), + OMIT("omit"), + EXPAND_VALUES("expandValues"), + CSF("csf"), + ENTITY_MAP("entityMap"), + + // 6.3.18 limiting distributed operations + LOCAL("local"), // 6.3.18 + VIA("Via"); // 6.3.18 + + override fun toString(): String { + return key } object Query { diff --git a/shared/src/main/kotlin/com/egm/stellio/shared/model/parameter/TemporalQueryParameter.kt b/shared/src/main/kotlin/com/egm/stellio/shared/model/parameter/TemporalQueryParameter.kt deleted file mode 100644 index c87e52035..000000000 --- a/shared/src/main/kotlin/com/egm/stellio/shared/model/parameter/TemporalQueryParameter.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.egm.stellio.shared.model.parameter - -enum class TemporalQueryParameter( - override val key: String, - override val implemented: Boolean = true, -) : QueryParameter { - TIMEREL("timerel"), - TIMEAT("timeAt"), - ENDTIMEAT("endTimeAt"), - AGGRPERIODDURATION("aggrPeriodDuration"), - AGGRMETHODS("aggrMethods"), - LASTN("lastN"), - TIMEPROPERTY("timeproperty"); - companion object { - const val WHOLE_TIME_RANGE_DURATION = "PT0S" - } -} diff --git a/shared/src/main/kotlin/com/egm/stellio/shared/util/ApiUtils.kt b/shared/src/main/kotlin/com/egm/stellio/shared/util/ApiUtils.kt index efed011ce..efce5dec3 100644 --- a/shared/src/main/kotlin/com/egm/stellio/shared/util/ApiUtils.kt +++ b/shared/src/main/kotlin/com/egm/stellio/shared/util/ApiUtils.kt @@ -11,7 +11,7 @@ import com.egm.stellio.shared.model.BadRequestDataException import com.egm.stellio.shared.model.CompactedEntity import com.egm.stellio.shared.model.EntityTypeSelection import com.egm.stellio.shared.model.NotAcceptableException -import com.egm.stellio.shared.model.parameter.QueryParam.Query.typeSelectionRegex +import com.egm.stellio.shared.model.parameter.QueryParameter.Query.typeSelectionRegex import com.egm.stellio.shared.util.JsonLdUtils.JSONLD_CONTEXT import com.egm.stellio.shared.util.JsonLdUtils.NGSILD_DATASET_ID_PROPERTY import com.egm.stellio.shared.util.JsonUtils.deserializeAsMap @@ -36,6 +36,8 @@ val GEO_JSON_MEDIA_TYPE = MediaType.valueOf(GEO_JSON_CONTENT_TYPE) val linkHeaderRegex: Regex = """<(.*)>;rel="http://www.w3.org/ns/json-ld#context";type="application/ld\+json"""".toRegex() +const val WHOLE_TIME_RANGE_DURATION = "PT0S" + /** * As per 6.3.5, If the request verb is GET or DELETE, then the associated JSON-LD "@context" shall be obtained from a * Link header as mandated by JSON-LD, section 6.2.extract @context from Link header. diff --git a/shared/src/main/kotlin/com/egm/stellio/shared/util/QueryUtils.kt b/shared/src/main/kotlin/com/egm/stellio/shared/util/QueryUtils.kt index f170a2c74..bc7957952 100644 --- a/shared/src/main/kotlin/com/egm/stellio/shared/util/QueryUtils.kt +++ b/shared/src/main/kotlin/com/egm/stellio/shared/util/QueryUtils.kt @@ -2,9 +2,9 @@ package com.egm.stellio.shared.util import com.egm.stellio.shared.model.ExpandedEntity import com.egm.stellio.shared.model.ExpandedTerm -import com.egm.stellio.shared.model.parameter.QueryParam.Query.qPattern -import com.egm.stellio.shared.model.parameter.QueryParam.Query.scopeSelectionRegex -import com.egm.stellio.shared.model.parameter.QueryParam.Query.typeSelectionRegex +import com.egm.stellio.shared.model.parameter.QueryParameter.Query.qPattern +import com.egm.stellio.shared.model.parameter.QueryParameter.Query.scopeSelectionRegex +import com.egm.stellio.shared.model.parameter.QueryParameter.Query.typeSelectionRegex import com.egm.stellio.shared.util.JsonLdUtils.JSONLD_ID import com.egm.stellio.shared.util.JsonLdUtils.JSONLD_VALUE import com.egm.stellio.shared.util.JsonLdUtils.NGSILD_PROPERTY_VALUE diff --git a/subscription-service/src/main/kotlin/com/egm/stellio/subscription/job/TimeIntervalNotificationJob.kt b/subscription-service/src/main/kotlin/com/egm/stellio/subscription/job/TimeIntervalNotificationJob.kt index 66971bb3f..b3548ef3e 100644 --- a/subscription-service/src/main/kotlin/com/egm/stellio/subscription/job/TimeIntervalNotificationJob.kt +++ b/subscription-service/src/main/kotlin/com/egm/stellio/subscription/job/TimeIntervalNotificationJob.kt @@ -4,7 +4,7 @@ import arrow.core.flatten import com.egm.stellio.shared.config.ApplicationProperties import com.egm.stellio.shared.model.CompactedEntity import com.egm.stellio.shared.model.EntitySelector -import com.egm.stellio.shared.model.parameter.QueryParam +import com.egm.stellio.shared.model.parameter.QueryParameter import com.egm.stellio.shared.util.JsonUtils import com.egm.stellio.shared.util.encode import com.egm.stellio.shared.web.NGSILD_TENANT_HEADER @@ -53,12 +53,14 @@ class TimeIntervalNotificationJob( fun prepareQueryParams(entitySelector: EntitySelector, q: String?, attributes: List?): String { val param = java.lang.StringBuilder() - param.append("?${QueryParam.TYPE.key}=${entitySelector.typeSelection.encode()}") - if (entitySelector.id != null) param.append("&${QueryParam.ID.key}=${entitySelector.id}") - if (entitySelector.idPattern != null) param.append("&${QueryParam.ID_PATTERN.key}=${entitySelector.idPattern}") - if (q != null) param.append("&${QueryParam.Q.key}=${q.encode()}") + param.append("?${QueryParameter.TYPE.key}=${entitySelector.typeSelection.encode()}") + if (entitySelector.id != null) param.append("&${QueryParameter.ID.key}=${entitySelector.id}") + if (entitySelector.idPattern != null) param.append( + "&${QueryParameter.ID_PATTERN.key}=${entitySelector.idPattern}" + ) + if (q != null) param.append("&${QueryParameter.Q.key}=${q.encode()}") if (!attributes.isNullOrEmpty()) - param.append("&${QueryParam.ATTRS.key}=${attributes.joinToString(",") { it.encode() }}") + param.append("&${QueryParameter.ATTRS.key}=${attributes.joinToString(",") { it.encode() }}") return param.toString() } diff --git a/subscription-service/src/main/kotlin/com/egm/stellio/subscription/model/GeoQ.kt b/subscription-service/src/main/kotlin/com/egm/stellio/subscription/model/GeoQ.kt index 4d5fc06a7..3c0149aca 100644 --- a/subscription-service/src/main/kotlin/com/egm/stellio/subscription/model/GeoQ.kt +++ b/subscription-service/src/main/kotlin/com/egm/stellio/subscription/model/GeoQ.kt @@ -1,6 +1,6 @@ package com.egm.stellio.subscription.model -import com.egm.stellio.shared.model.parameter.GeoQueryParameter +import com.egm.stellio.shared.model.parameter.QueryParameter import com.egm.stellio.shared.util.JsonLdUtils.NGSILD_LOCATION_PROPERTY import com.fasterxml.jackson.annotation.JsonIgnore import org.springframework.data.relational.core.mapping.Table @@ -17,9 +17,9 @@ data class GeoQ( // representation passed to function checking for the correctness of geo-queries fun toMap(): Map = mapOf( - GeoQueryParameter.GEOREL.key to georel, - GeoQueryParameter.GEOMETRY.key to geometry, - GeoQueryParameter.COORDINATES.key to coordinates, - GeoQueryParameter.GEOPROPERTY.key to geoproperty + QueryParameter.GEOREL.key to georel, + QueryParameter.GEOMETRY.key to geometry, + QueryParameter.COORDINATES.key to coordinates, + QueryParameter.GEOPROPERTY.key to geoproperty ) } diff --git a/subscription-service/src/main/kotlin/com/egm/stellio/subscription/web/SubscriptionHandler.kt b/subscription-service/src/main/kotlin/com/egm/stellio/subscription/web/SubscriptionHandler.kt index df6c717f5..bd6b0b355 100644 --- a/subscription-service/src/main/kotlin/com/egm/stellio/subscription/web/SubscriptionHandler.kt +++ b/subscription-service/src/main/kotlin/com/egm/stellio/subscription/web/SubscriptionHandler.kt @@ -12,7 +12,7 @@ import com.egm.stellio.shared.model.AccessDeniedException import com.egm.stellio.shared.model.AlreadyExistsException import com.egm.stellio.shared.model.PaginationQuery.Companion.parsePaginationParameters import com.egm.stellio.shared.model.ResourceNotFoundException -import com.egm.stellio.shared.model.parameter.QueryParam +import com.egm.stellio.shared.model.parameter.QueryParameter import com.egm.stellio.shared.util.JSON_LD_CONTENT_TYPE import com.egm.stellio.shared.util.JSON_MERGE_PATCH_CONTENT_TYPE import com.egm.stellio.shared.util.JsonLdUtils.JSONLD_CONTEXT @@ -94,8 +94,8 @@ class SubscriptionHandler( val mediaType = getApplicableMediaType(httpHeaders).bind() val sub = getSubFromSecurityContext() - val includeSysAttrs = params.getOrDefault(QueryParam.OPTIONS.key, emptyList()) - .contains(QueryParam.OptionValue.SYS_ATTRS.value) + val includeSysAttrs = params.getOrDefault(QueryParameter.OPTIONS.key, emptyList()) + .contains(QueryParameter.SYS_ATTRS.key) val paginationQuery = parsePaginationParameters( params, applicationProperties.pagination.limitDefault, @@ -128,7 +128,7 @@ class SubscriptionHandler( @PathVariable subscriptionId: URI, @RequestParam options: Optional ): ResponseEntity<*> = either { - val includeSysAttrs = options.filter { it.contains(QueryParam.OptionValue.SYS_ATTRS.value) }.isPresent + val includeSysAttrs = options.filter { it.contains(QueryParameter.SYS_ATTRS.key) }.isPresent val contexts = getContextFromLinkHeaderOrDefault(httpHeaders, applicationProperties.contexts.core).bind() val mediaType = getApplicableMediaType(httpHeaders).bind()