diff --git a/search-service/config/detekt/baseline.xml b/search-service/config/detekt/baseline.xml
index f467d3c62..9137ecca1 100644
--- a/search-service/config/detekt/baseline.xml
+++ b/search-service/config/detekt/baseline.xml
@@ -10,14 +10,13 @@
LongMethod:EnabledAuthorizationServiceTests.kt$EnabledAuthorizationServiceTests$@Test fun `it should return serialized access control entities with other rigths if user is owner`()
LongMethod:EntityAccessControlHandler.kt$EntityAccessControlHandler$@PostMapping("/{subjectId}/attrs", consumes = [MediaType.APPLICATION_JSON_VALUE, JSON_LD_CONTENT_TYPE]) suspend fun addRightsOnEntities( @RequestHeader httpHeaders: HttpHeaders, @PathVariable subjectId: String, @RequestBody requestBody: Mono<String>, @AllowedParameters @RequestParam queryParams: MultiValueMap<String, String> ): ResponseEntity<*>
LongMethod:EntityEventService.kt$EntityEventService$private fun publishAttributeChangeEvent( sub: String?, tenantName: String, entityId: URI, entityTypesAndPayload: Pair<List<ExpandedTerm>, String>, attributeOperationResult: SucceededAttributeOperationResult )
- LongMethod:EntityHandler.kt$EntityHandler$@GetMapping("/{entityId}", produces = [APPLICATION_JSON_VALUE, JSON_LD_CONTENT_TYPE, GEO_JSON_CONTENT_TYPE]) suspend fun getByURI( @RequestHeader httpHeaders: HttpHeaders, @PathVariable entityId: URI, @AllowedParameters( implemented = [ QP.OPTIONS, QP.TYPE, QP.ATTRS, QP.GEOMETRY_PROPERTY, QP.LANG, QP.CONTAINED_BY, QP.JOIN, QP.JOIN_LEVEL, QP.DATASET_ID, ], notImplemented = [QP.FORMAT, QP.PICK, QP.OMIT, QP.ENTITY_MAP, QP.LOCAL, QP.VIA] ) @RequestParam queryParams: MultiValueMap<String, String> ): ResponseEntity<*>
- LongMethod:EntityHandler.kt$EntityHandler$@GetMapping(produces = [APPLICATION_JSON_VALUE, JSON_LD_CONTENT_TYPE, GEO_JSON_CONTENT_TYPE]) suspend fun getEntities( @RequestHeader httpHeaders: HttpHeaders, @AllowedParameters( implemented = [ QP.OPTIONS, QP.COUNT, QP.OFFSET, QP.LIMIT, QP.ID, QP.TYPE, QP.ID_PATTERN, QP.ATTRS, QP.Q, QP.GEOMETRY, QP.GEOREL, QP.COORDINATES, QP.GEOPROPERTY, QP.GEOMETRY_PROPERTY, QP.LANG, QP.SCOPEQ, QP.CONTAINED_BY, QP.JOIN, QP.JOIN_LEVEL, QP.DATASET_ID, ], notImplemented = [QP.FORMAT, QP.PICK, QP.OMIT, QP.EXPAND_VALUES, QP.CSF, QP.ENTITY_MAP, QP.LOCAL, QP.VIA] ) @RequestParam queryParams: MultiValueMap<String, String> ): ResponseEntity<*>
+ LongMethod:EntityHandler.kt$EntityHandler$@GetMapping("/{entityId}", produces = [APPLICATION_JSON_VALUE, JSON_LD_CONTENT_TYPE, GEO_JSON_CONTENT_TYPE]) suspend fun getByURI( @RequestHeader httpHeaders: HttpHeaders, @PathVariable entityId: URI, @AllowedParameters( implemented = [ QP.OPTIONS, QP.FORMAT, QP.TYPE, QP.ATTRS, QP.GEOMETRY_PROPERTY, QP.LANG, QP.CONTAINED_BY, QP.JOIN, QP.JOIN_LEVEL, QP.DATASET_ID, ], notImplemented = [QP.PICK, QP.OMIT, QP.ENTITY_MAP, QP.LOCAL, QP.VIA] ) @RequestParam queryParams: MultiValueMap<String, String> ): ResponseEntity<*>
+ LongMethod:EntityHandler.kt$EntityHandler$@GetMapping(produces = [APPLICATION_JSON_VALUE, JSON_LD_CONTENT_TYPE, GEO_JSON_CONTENT_TYPE]) suspend fun getEntities( @RequestHeader httpHeaders: HttpHeaders, @AllowedParameters( implemented = [ QP.OPTIONS, QP.FORMAT, QP.COUNT, QP.OFFSET, QP.LIMIT, QP.ID, QP.TYPE, QP.ID_PATTERN, QP.ATTRS, QP.Q, QP.GEOMETRY, QP.GEOREL, QP.COORDINATES, QP.GEOPROPERTY, QP.GEOMETRY_PROPERTY, QP.LANG, QP.SCOPEQ, QP.CONTAINED_BY, QP.JOIN, QP.JOIN_LEVEL, QP.DATASET_ID, ], notImplemented = [QP.PICK, QP.OMIT, QP.EXPAND_VALUES, QP.CSF, QP.ENTITY_MAP, QP.LOCAL, QP.VIA] ) @RequestParam queryParams: MultiValueMap<String, String> ): ResponseEntity<*>
LongMethod:LinkedEntityServiceTests.kt$LinkedEntityServiceTests$@Test fun `it should inline entities up to the asked 2nd level`()
LongMethod:PatchAttributeTests.kt$PatchAttributeTests.Companion$@JvmStatic fun mergePatchProvider(): Stream<Arguments>
LongMethod:PatchAttributeTests.kt$PatchAttributeTests.Companion$@JvmStatic fun partialUpdatePatchProvider(): Stream<Arguments>
LongMethod:TemporalQueryServiceTests.kt$TemporalQueryServiceTests$@Test fun `it should query temporal entities as requested by query params`()
LongMethod:TemporalQueryServiceTests.kt$TemporalQueryServiceTests$@Test fun `it should return an empty list for an attribute if it has no temporal values`()
- LongMethod:TemporalScopeBuilderTests.kt$TemporalScopeBuilderTests$@Test fun `it should build an aggregated temporal representation of scopes`()
LongMethod:V0_29__JsonLd_migration.kt$V0_29__JsonLd_migration$override fun migrate(context: Context)
LongParameterList:AttributeInstance.kt$AttributeInstance.Companion$( attributeUuid: UUID, instanceId: URI = generateRandomInstanceId(), timeAndProperty: Pair<ZonedDateTime, TemporalProperty>, value: Triple<String?, Double?, WKTCoordinates?>, payload: ExpandedAttributeInstance, sub: String? )
LongParameterList:AttributeInstance.kt$AttributeInstance.Companion$( attributeUuid: UUID, instanceId: URI = generateRandomInstanceId(), timeProperty: TemporalProperty? = TemporalProperty.OBSERVED_AT, modifiedAt: ZonedDateTime? = null, attributeMetadata: AttributeMetadata, payload: ExpandedAttributeInstance, time: ZonedDateTime, sub: String? = null )
diff --git a/search-service/src/main/kotlin/com/egm/stellio/search/authorization/web/EntityAccessControlHandler.kt b/search-service/src/main/kotlin/com/egm/stellio/search/authorization/web/EntityAccessControlHandler.kt
index cbbdf667c..048862f44 100644
--- a/search-service/src/main/kotlin/com/egm/stellio/search/authorization/web/EntityAccessControlHandler.kt
+++ b/search-service/src/main/kotlin/com/egm/stellio/search/authorization/web/EntityAccessControlHandler.kt
@@ -105,7 +105,7 @@ class EntityAccessControlHandler(
val compactedEntities = compactEntities(entities, contexts)
- val ngsiLdDataRepresentation = parseRepresentations(queryParams, mediaType)
+ val ngsiLdDataRepresentation = parseRepresentations(queryParams, mediaType).bind()
buildQueryResponse(
compactedEntities.toFinalRepresentation(ngsiLdDataRepresentation),
count,
@@ -150,7 +150,7 @@ class EntityAccessControlHandler(
val compactedEntities = compactEntities(entities, contexts)
- val ngsiLdDataRepresentation = parseRepresentations(params, mediaType)
+ val ngsiLdDataRepresentation = parseRepresentations(params, mediaType).bind()
buildQueryResponse(
compactedEntities.toFinalRepresentation(ngsiLdDataRepresentation),
count,
@@ -196,7 +196,7 @@ class EntityAccessControlHandler(
val compactedEntities = compactEntities(entities, contexts)
- val ngsiLdDataRepresentation = parseRepresentations(params, mediaType)
+ val ngsiLdDataRepresentation = parseRepresentations(params, mediaType).bind()
buildQueryResponse(
compactedEntities.toFinalRepresentation(ngsiLdDataRepresentation),
count,
diff --git a/search-service/src/main/kotlin/com/egm/stellio/search/entity/service/EntityAttributeService.kt b/search-service/src/main/kotlin/com/egm/stellio/search/entity/service/EntityAttributeService.kt
index 223cec0e4..f020f47b0 100644
--- a/search-service/src/main/kotlin/com/egm/stellio/search/entity/service/EntityAttributeService.kt
+++ b/search-service/src/main/kotlin/com/egm/stellio/search/entity/service/EntityAttributeService.kt
@@ -95,7 +95,7 @@ class EntityAttributeService(
private val logger = LoggerFactory.getLogger(javaClass)
@Transactional
- suspend fun create(attribute: Attribute): Either =
+ suspend fun create(attribute: Attribute): Either =
databaseClient.sql(
"""
INSERT INTO temporal_entity_attribute
@@ -110,6 +110,7 @@ class EntityAttributeService(
attribute_value_type = :attribute_value_type,
modified_at = :created_at,
payload = :payload
+ RETURNING id
""".trimIndent()
)
.bind("id", attribute.id)
@@ -120,7 +121,7 @@ class EntityAttributeService(
.bind("created_at", attribute.createdAt)
.bind("dataset_id", attribute.datasetId)
.bind("payload", attribute.payload)
- .execute()
+ .oneToResult { row -> toUuid(row["id"]) }
@Transactional
suspend fun updateOnUpdate(
@@ -215,10 +216,10 @@ class EntityAttributeService(
createdAt = createdAt,
payload = Json.of(serializeObject(attributePayload))
)
- create(attribute).bind()
+ val attributeUuid = create(attribute).bind()
val attributeInstance = AttributeInstance(
- attributeUuid = attribute.id,
+ attributeUuid = attributeUuid,
timeProperty = AttributeInstance.TemporalProperty.CREATED_AT,
time = createdAt,
attributeMetadata = attributeMetadata,
@@ -229,7 +230,7 @@ class EntityAttributeService(
if (attributeMetadata.observedAt != null) {
val attributeObservedAtInstance = AttributeInstance(
- attributeUuid = attribute.id,
+ attributeUuid = attributeUuid,
time = attributeMetadata.observedAt,
attributeMetadata = attributeMetadata,
payload = attributePayload
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 5dfb34e01..f35763175 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
@@ -194,11 +194,11 @@ class EntityHandler(
@RequestHeader httpHeaders: HttpHeaders,
@AllowedParameters(
implemented = [
- QP.OPTIONS, QP.COUNT, QP.OFFSET, QP.LIMIT, QP.ID, QP.TYPE, QP.ID_PATTERN, QP.ATTRS, QP.Q,
+ QP.OPTIONS, QP.FORMAT, QP.COUNT, QP.OFFSET, QP.LIMIT, QP.ID, QP.TYPE, QP.ID_PATTERN, QP.ATTRS, QP.Q,
QP.GEOMETRY, QP.GEOREL, QP.COORDINATES, QP.GEOPROPERTY, QP.GEOMETRY_PROPERTY,
QP.LANG, QP.SCOPEQ, QP.CONTAINED_BY, QP.JOIN, QP.JOIN_LEVEL, QP.DATASET_ID,
],
- notImplemented = [QP.FORMAT, QP.PICK, QP.OMIT, QP.EXPAND_VALUES, QP.CSF, QP.ENTITY_MAP, QP.LOCAL, QP.VIA]
+ notImplemented = [QP.PICK, QP.OMIT, QP.EXPAND_VALUES, QP.CSF, QP.ENTITY_MAP, QP.LOCAL, QP.VIA]
)
@RequestParam queryParams: MultiValueMap
): ResponseEntity<*> = either {
@@ -264,7 +264,7 @@ class EntityHandler(
mergedEntities ?: emptyList()
}
- val ngsiLdDataRepresentation = parseRepresentations(queryParams, mediaType)
+ val ngsiLdDataRepresentation = parseRepresentations(queryParams, mediaType).bind()
buildQueryResponse(
mergedEntities.toFinalRepresentation(ngsiLdDataRepresentation),
maxCount,
@@ -288,10 +288,10 @@ class EntityHandler(
@PathVariable entityId: URI,
@AllowedParameters(
implemented = [
- QP.OPTIONS, QP.TYPE, QP.ATTRS, QP.GEOMETRY_PROPERTY,
+ QP.OPTIONS, QP.FORMAT, QP.TYPE, QP.ATTRS, QP.GEOMETRY_PROPERTY,
QP.LANG, QP.CONTAINED_BY, QP.JOIN, QP.JOIN_LEVEL, QP.DATASET_ID,
],
- notImplemented = [QP.FORMAT, QP.PICK, QP.OMIT, QP.ENTITY_MAP, QP.LOCAL, QP.VIA]
+ notImplemented = [QP.PICK, QP.OMIT, QP.ENTITY_MAP, QP.LOCAL, QP.VIA]
)
@RequestParam queryParams: MultiValueMap
): ResponseEntity<*> = either {
@@ -358,7 +358,7 @@ class EntityHandler(
val mergedEntityWithLinkedEntities =
linkedEntityService.processLinkedEntities(mergedEntity, entitiesQuery, sub.getOrNull()).bind()
- val ngsiLdDataRepresentation = parseRepresentations(queryParams, mediaType)
+ val ngsiLdDataRepresentation = parseRepresentations(queryParams, mediaType).bind()
prepareGetSuccessResponseHeaders(mediaType, contexts)
.let {
val body = if (mergedEntityWithLinkedEntities.size == 1)
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 3d5c9913d..5c1d49247 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
@@ -281,7 +281,7 @@ class EntityOperationHandler(
val compactedEntities = compactEntities(filteredEntities, contexts)
- val ngsiLdDataRepresentation = parseRepresentations(queryParams, mediaType)
+ val ngsiLdDataRepresentation = parseRepresentations(queryParams, mediaType).bind()
.copy(languageFilter = query.lang)
buildQueryResponse(
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 9eacfef87..0310a7949 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
@@ -24,6 +24,7 @@ import com.egm.stellio.search.entity.model.SucceededAttributeOperationResult
import com.egm.stellio.search.temporal.model.AttributeInstance.TemporalProperty
import com.egm.stellio.search.temporal.model.TemporalEntitiesQuery
import com.egm.stellio.search.temporal.model.TemporalQuery
+import com.egm.stellio.search.temporal.util.TemporalRepresentation
import com.egm.stellio.search.temporal.util.WHOLE_TIME_RANGE_DURATION
import com.egm.stellio.search.temporal.util.composeAggregationSelectClause
import com.egm.stellio.shared.model.APIException
@@ -136,7 +137,7 @@ class ScopeService(
if (temporalEntitiesQuery.isAggregatedWithDefinedDuration())
sqlQueryBuilder.append(" GROUP BY entity_id, start")
- else if (temporalEntitiesQuery.withAggregatedValues)
+ else if (temporalEntitiesQuery.temporalRepresentation == TemporalRepresentation.AGGREGATED_VALUES)
sqlQueryBuilder.append(" GROUP BY entity_id")
if (temporalQuery.hasLastN())
// in order to get first or last instances, need to order by time
@@ -161,7 +162,7 @@ class ScopeService(
temporalEntitiesQuery: TemporalEntitiesQuery,
origin: ZonedDateTime?
): String = when {
- temporalEntitiesQuery.withAggregatedValues -> {
+ temporalEntitiesQuery.temporalRepresentation == TemporalRepresentation.AGGREGATED_VALUES -> {
val temporalQuery = temporalEntitiesQuery.temporalQuery
val aggrPeriodDuration = temporalQuery.aggrPeriodDuration
val allAggregates = temporalQuery.aggrMethods?.composeAggregationSelectClause(AttributeValueType.ARRAY)
@@ -215,7 +216,7 @@ class ScopeService(
row: Map,
temporalEntitiesQuery: TemporalEntitiesQuery
): ScopeInstanceResult =
- if (temporalEntitiesQuery.withAggregatedValues) {
+ if (temporalEntitiesQuery.temporalRepresentation == TemporalRepresentation.AGGREGATED_VALUES) {
val startDateTime = toZonedDateTime(row["start"])
val endDateTime =
if (!temporalEntitiesQuery.isAggregatedWithDefinedDuration())
@@ -231,7 +232,7 @@ class ScopeService(
entityId = toUri(row["entity_id"]),
values = values
)
- } else if (temporalEntitiesQuery.withTemporalValues) {
+ } else if (temporalEntitiesQuery.temporalRepresentation == TemporalRepresentation.TEMPORAL_VALUES) {
SimplifiedScopeInstanceResult(
entityId = toUri(row["entity_id"]),
scopes = toList(row["value"]),
diff --git a/search-service/src/main/kotlin/com/egm/stellio/search/scope/TemporalScopeBuilder.kt b/search-service/src/main/kotlin/com/egm/stellio/search/scope/TemporalScopeBuilder.kt
index 0ab5b7113..f6aefa286 100644
--- a/search-service/src/main/kotlin/com/egm/stellio/search/scope/TemporalScopeBuilder.kt
+++ b/search-service/src/main/kotlin/com/egm/stellio/search/scope/TemporalScopeBuilder.kt
@@ -3,6 +3,7 @@ package com.egm.stellio.search.scope
import com.egm.stellio.search.entity.model.Entity
import com.egm.stellio.search.temporal.model.TemporalEntitiesQuery
import com.egm.stellio.search.temporal.model.TemporalQuery
+import com.egm.stellio.search.temporal.util.TemporalRepresentation
import com.egm.stellio.shared.util.JsonLdUtils.JSONLD_LIST
import com.egm.stellio.shared.util.JsonLdUtils.JSONLD_TYPE
import com.egm.stellio.shared.util.JsonLdUtils.JSONLD_VALUE
@@ -27,12 +28,12 @@ object TemporalScopeBuilder {
// if no history but entity has a scope, add an empty scope list (no history in the given time range)
else if (scopeInstances.isEmpty())
mapOf(NGSILD_SCOPE_PROPERTY to emptyList())
- else if (temporalEntitiesQuery.withAggregatedValues)
+ else if (temporalEntitiesQuery.temporalRepresentation == TemporalRepresentation.AGGREGATED_VALUES)
buildScopeAggregatedRepresentation(
scopeInstances,
temporalEntitiesQuery.temporalQuery.aggrMethods!!
)
- else if (temporalEntitiesQuery.withTemporalValues)
+ else if (temporalEntitiesQuery.temporalRepresentation == TemporalRepresentation.TEMPORAL_VALUES)
buildScopeSimplifiedRepresentation(scopeInstances)
else
buildScopeFullRepresentation(scopeInstances)
diff --git a/search-service/src/main/kotlin/com/egm/stellio/search/temporal/model/TemporalEntitiesQuery.kt b/search-service/src/main/kotlin/com/egm/stellio/search/temporal/model/TemporalEntitiesQuery.kt
index 5843cf52a..cb85b85c1 100644
--- a/search-service/src/main/kotlin/com/egm/stellio/search/temporal/model/TemporalEntitiesQuery.kt
+++ b/search-service/src/main/kotlin/com/egm/stellio/search/temporal/model/TemporalEntitiesQuery.kt
@@ -3,6 +3,7 @@ package com.egm.stellio.search.temporal.model
import com.egm.stellio.search.entity.model.EntitiesQuery
import com.egm.stellio.search.entity.model.EntitiesQueryFromGet
import com.egm.stellio.search.entity.model.EntitiesQueryFromPost
+import com.egm.stellio.search.temporal.util.TemporalRepresentation
import java.time.Duration
import java.time.Period
import java.time.temporal.TemporalAmount
@@ -10,12 +11,11 @@ import java.time.temporal.TemporalAmount
sealed class TemporalEntitiesQuery(
open val entitiesQuery: EntitiesQuery,
open val temporalQuery: TemporalQuery,
- open val withTemporalValues: Boolean,
- open val withAudit: Boolean,
- open val withAggregatedValues: Boolean
+ open val temporalRepresentation: TemporalRepresentation,
+ open val withAudit: Boolean
) {
fun isAggregatedWithDefinedDuration(): Boolean =
- withAggregatedValues &&
+ temporalRepresentation == TemporalRepresentation.AGGREGATED_VALUES &&
temporalQuery.aggrPeriodDuration != null &&
temporalQuery.aggrPeriodDuration != "PT0S"
@@ -36,15 +36,13 @@ sealed class TemporalEntitiesQuery(
data class TemporalEntitiesQueryFromGet(
override val entitiesQuery: EntitiesQueryFromGet,
override val temporalQuery: TemporalQuery,
- override val withTemporalValues: Boolean,
- override val withAudit: Boolean,
- override val withAggregatedValues: Boolean
-) : TemporalEntitiesQuery(entitiesQuery, temporalQuery, withTemporalValues, withAudit, withAggregatedValues)
+ override val temporalRepresentation: TemporalRepresentation,
+ override val withAudit: Boolean
+) : TemporalEntitiesQuery(entitiesQuery, temporalQuery, temporalRepresentation, withAudit)
data class TemporalEntitiesQueryFromPost(
override val entitiesQuery: EntitiesQueryFromPost,
override val temporalQuery: TemporalQuery,
- override val withTemporalValues: Boolean,
- override val withAudit: Boolean,
- override val withAggregatedValues: Boolean
-) : TemporalEntitiesQuery(entitiesQuery, temporalQuery, withTemporalValues, withAudit, withAggregatedValues)
+ override val temporalRepresentation: TemporalRepresentation,
+ override val withAudit: Boolean
+) : TemporalEntitiesQuery(entitiesQuery, temporalQuery, temporalRepresentation, withAudit)
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 d718bd9ee..6e189f61e 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
@@ -28,6 +28,7 @@ import com.egm.stellio.search.temporal.model.SimplifiedAttributeInstanceResult
import com.egm.stellio.search.temporal.model.TemporalEntitiesQuery
import com.egm.stellio.search.temporal.model.TemporalQuery
import com.egm.stellio.search.temporal.model.TemporalQuery.Timerel
+import com.egm.stellio.search.temporal.util.TemporalRepresentation
import com.egm.stellio.search.temporal.util.WHOLE_TIME_RANGE_DURATION
import com.egm.stellio.search.temporal.util.composeAggregationSelectClause
import com.egm.stellio.shared.model.APIException
@@ -174,7 +175,7 @@ class AttributeInstanceService(
sqlQueryBuilder.append(composeSearchSelectStatement(temporalQuery, attributes, origin))
- if (!temporalEntitiesQuery.withTemporalValues && !temporalEntitiesQuery.withAggregatedValues)
+ if (temporalEntitiesQuery.temporalRepresentation == TemporalRepresentation.NORMALIZED)
sqlQueryBuilder.append(", payload")
if (temporalQuery.timeproperty == OBSERVED_AT)
@@ -204,7 +205,7 @@ class AttributeInstanceService(
if (temporalEntitiesQuery.isAggregatedWithDefinedDuration())
sqlQueryBuilder.append(" GROUP BY temporal_entity_attribute, start")
- else if (temporalEntitiesQuery.withAggregatedValues)
+ else if (temporalEntitiesQuery.temporalRepresentation == TemporalRepresentation.AGGREGATED_VALUES)
sqlQueryBuilder.append(" GROUP BY temporal_entity_attribute")
if (temporalQuery.hasLastN())
@@ -319,7 +320,7 @@ class AttributeInstanceService(
row: Map,
temporalEntitiesQuery: TemporalEntitiesQuery
): AttributeInstanceResult =
- if (temporalEntitiesQuery.withAggregatedValues) {
+ if (temporalEntitiesQuery.temporalRepresentation == TemporalRepresentation.AGGREGATED_VALUES) {
val startDateTime = toZonedDateTime(row["start"])
val endDateTime =
if (!temporalEntitiesQuery.isAggregatedWithDefinedDuration())
@@ -335,7 +336,7 @@ class AttributeInstanceService(
attributeUuid = toUuid(row["temporal_entity_attribute"]),
values = values
)
- } else if (temporalEntitiesQuery.withTemporalValues)
+ } else if (temporalEntitiesQuery.temporalRepresentation == TemporalRepresentation.TEMPORAL_VALUES)
SimplifiedAttributeInstanceResult(
attributeUuid = toUuid(row["temporal_entity_attribute"]),
// the type of the value of a property may have changed in the history (e.g., from number to string)
diff --git a/search-service/src/main/kotlin/com/egm/stellio/search/temporal/service/TemporalPaginationService.kt b/search-service/src/main/kotlin/com/egm/stellio/search/temporal/service/TemporalPaginationService.kt
index 1b9d166f1..96d002601 100644
--- a/search-service/src/main/kotlin/com/egm/stellio/search/temporal/service/TemporalPaginationService.kt
+++ b/search-service/src/main/kotlin/com/egm/stellio/search/temporal/service/TemporalPaginationService.kt
@@ -5,6 +5,7 @@ import com.egm.stellio.search.temporal.model.AttributeInstanceResult
import com.egm.stellio.search.temporal.model.TemporalEntitiesQuery
import com.egm.stellio.search.temporal.model.TemporalQuery
import com.egm.stellio.search.temporal.util.AttributesWithInstances
+import com.egm.stellio.search.temporal.util.TemporalRepresentation
import java.time.ZonedDateTime
typealias Range = Pair
@@ -47,12 +48,13 @@ object TemporalPaginationService {
val temporalQuery = query.temporalQuery
val attributesTimeRanges = attributeInstancesWhoReachedLimit.map {
- it.first().getComparableTime() to if (query.withAggregatedValues) {
- val lastInstance = it.last() as AggregatedAttributeInstanceResult
- lastInstance.values.first().endDateTime
- } else {
- it.last().getComparableTime()
- }
+ it.first().getComparableTime() to
+ if (query.temporalRepresentation == TemporalRepresentation.AGGREGATED_VALUES) {
+ val lastInstance = it.last() as AggregatedAttributeInstanceResult
+ lastInstance.values.first().endDateTime
+ } else {
+ it.last().getComparableTime()
+ }
}
if (temporalQuery.hasLastN()) {
diff --git a/search-service/src/main/kotlin/com/egm/stellio/search/temporal/service/TemporalQueryService.kt b/search-service/src/main/kotlin/com/egm/stellio/search/temporal/service/TemporalQueryService.kt
index 8f0e742ef..57535e597 100644
--- a/search-service/src/main/kotlin/com/egm/stellio/search/temporal/service/TemporalQueryService.kt
+++ b/search-service/src/main/kotlin/com/egm/stellio/search/temporal/service/TemporalQueryService.kt
@@ -17,6 +17,7 @@ import com.egm.stellio.search.temporal.model.TemporalEntitiesQuery
import com.egm.stellio.search.temporal.service.TemporalPaginationService.getPaginatedAttributeWithInstancesAndRange
import com.egm.stellio.search.temporal.util.AttributesWithInstances
import com.egm.stellio.search.temporal.util.TemporalEntityBuilder
+import com.egm.stellio.search.temporal.util.TemporalRepresentation
import com.egm.stellio.search.temporal.web.Range
import com.egm.stellio.shared.model.APIException
import com.egm.stellio.shared.model.ExpandedEntity
@@ -93,7 +94,7 @@ class TemporalQueryService(
// - timeAt if it is provided
// - the oldest value if not (timeAt is optional if querying a temporal entity by id)
- return if (!temporalEntitiesQuery.withAggregatedValues)
+ return if (temporalEntitiesQuery.temporalRepresentation != TemporalRepresentation.AGGREGATED_VALUES)
null
else if (temporalQuery.timeAt != null)
temporalQuery.timeAt
@@ -198,7 +199,7 @@ class TemporalQueryService(
// when retrieved from DB, values of geo-properties are encoded in WKT and won't be automatically
// transformed during compaction as it is not done for temporal values, so it is done now
if (it.key == Attribute.AttributeValueType.GEOMETRY &&
- temporalEntitiesQuery.withTemporalValues
+ temporalEntitiesQuery.temporalRepresentation == TemporalRepresentation.TEMPORAL_VALUES
) {
it.value.map { attributeInstanceResult ->
attributeInstanceResult as SimplifiedAttributeInstanceResult
diff --git a/search-service/src/main/kotlin/com/egm/stellio/search/temporal/util/TemporalEntityBuilder.kt b/search-service/src/main/kotlin/com/egm/stellio/search/temporal/util/TemporalEntityBuilder.kt
index 4800e42fb..d5e96b895 100644
--- a/search-service/src/main/kotlin/com/egm/stellio/search/temporal/util/TemporalEntityBuilder.kt
+++ b/search-service/src/main/kotlin/com/egm/stellio/search/temporal/util/TemporalEntityBuilder.kt
@@ -68,10 +68,10 @@ object TemporalEntityBuilder {
attributeAndResultsMap: AttributesWithInstances,
temporalEntitiesQuery: TemporalEntitiesQuery,
): Map =
- if (temporalEntitiesQuery.withTemporalValues) {
+ if (temporalEntitiesQuery.temporalRepresentation == TemporalRepresentation.TEMPORAL_VALUES) {
val attributes = buildAttributesSimplifiedRepresentation(attributeAndResultsMap)
mergeSimplifiedTemporalAttributesOnAttributeName(attributes)
- } else if (temporalEntitiesQuery.withAggregatedValues) {
+ } else if (temporalEntitiesQuery.temporalRepresentation == TemporalRepresentation.AGGREGATED_VALUES) {
val attributes = buildAttributesAggregatedRepresentation(
attributeAndResultsMap,
temporalEntitiesQuery.temporalQuery.aggrMethods!!
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 494a8a5ee..5855d2193 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,8 +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.InvalidRequestException
+import com.egm.stellio.shared.queryparameter.OptionsValue
import com.egm.stellio.shared.queryparameter.QueryParameter
-import com.egm.stellio.shared.util.OptionsParamValue
import com.egm.stellio.shared.util.hasValueInOptionsParam
import com.egm.stellio.shared.util.parseTimeParameter
import org.springframework.util.MultiValueMap
@@ -42,31 +43,21 @@ fun composeTemporalEntitiesQueryFromGet(
requestParams,
contexts
).bind()
-
if (inQueryEntities)
entitiesQueryFromGet.validateMinimalQueryEntitiesParameters().bind()
-
- val withTemporalValues = hasValueInOptionsParam(
- Optional.ofNullable(requestParams.getFirst(QueryParameter.OPTIONS.key)),
- OptionsParamValue.TEMPORAL_VALUES
- )
+ val temporalRepresentation = extractTemporalRepresentation(requestParams).bind()
val withAudit = hasValueInOptionsParam(
Optional.ofNullable(requestParams.getFirst(QueryParameter.OPTIONS.key)),
- OptionsParamValue.AUDIT
- )
- val withAggregatedValues = hasValueInOptionsParam(
- Optional.ofNullable(requestParams.getFirst(QueryParameter.OPTIONS.key)),
- OptionsParamValue.AGGREGATED_VALUES
- )
+ OptionsValue.AUDIT
+ ).bind()
val temporalQuery =
- buildTemporalQuery(requestParams, defaultPagination, inQueryEntities, withAggregatedValues).bind()
+ buildTemporalQuery(requestParams, defaultPagination, inQueryEntities, temporalRepresentation).bind()
TemporalEntitiesQueryFromGet(
entitiesQuery = entitiesQueryFromGet,
temporalQuery = temporalQuery,
- withTemporalValues = withTemporalValues,
- withAudit = withAudit,
- withAggregatedValues = withAggregatedValues
+ temporalRepresentation = temporalRepresentation,
+ withAudit = withAudit
)
}
@@ -82,20 +73,12 @@ fun composeTemporalEntitiesQueryFromPost(
requestParams,
contexts
).bind()
+ val temporalRepresentation = extractTemporalRepresentation(requestParams).bind()
- val withTemporalValues = hasValueInOptionsParam(
- Optional.ofNullable(requestParams.getFirst(QueryParameter.OPTIONS.key)),
- OptionsParamValue.TEMPORAL_VALUES
- )
val withAudit = hasValueInOptionsParam(
Optional.ofNullable(requestParams.getFirst(QueryParameter.OPTIONS.key)),
- OptionsParamValue.AUDIT
- )
- val withAggregatedValues = hasValueInOptionsParam(
- Optional.ofNullable(requestParams.getFirst(QueryParameter.OPTIONS.key)),
- OptionsParamValue.AGGREGATED_VALUES
- )
-
+ OptionsValue.AUDIT
+ ).bind()
val temporalParams = mapOf(
QueryParameter.TIMEREL.key to listOf(query.temporalQ?.timerel),
QueryParameter.TIMEAT.key to listOf(query.temporalQ?.timeAt),
@@ -109,15 +92,14 @@ fun composeTemporalEntitiesQueryFromPost(
MultiValueMapAdapter(temporalParams),
defaultPagination,
true,
- withAggregatedValues
+ temporalRepresentation
).bind()
TemporalEntitiesQueryFromPost(
entitiesQuery = entitiesQueryFromPost,
temporalQuery = temporalQuery,
- withTemporalValues = withTemporalValues,
- withAudit = withAudit,
- withAggregatedValues = withAggregatedValues
+ temporalRepresentation = temporalRepresentation,
+ withAudit = withAudit
)
}
@@ -126,11 +108,12 @@ fun buildTemporalQuery(
params: MultiValueMap,
pagination: ApplicationProperties.Pagination,
inQueryEntities: Boolean = false,
- withAggregatedValues: Boolean = false,
+ temporalRepresentation: TemporalRepresentation,
): Either {
val timerelParam = params.getFirst(QueryParameter.TIMEREL.key)
val timeAtParam = params.getFirst(QueryParameter.TIMEAT.key)
val endTimeAtParam = params.getFirst(QueryParameter.ENDTIMEAT.key)
+ val withAggregatedValues = temporalRepresentation == TemporalRepresentation.AGGREGATED_VALUES
val aggrPeriodDurationParam =
if (withAggregatedValues)
params.getFirst(QueryParameter.AGGRPERIODDURATION.key) ?: WHOLE_TIME_RANGE_DURATION
@@ -206,3 +189,38 @@ fun buildTimerelAndTime(
} else {
"'timerel' and 'time' must be used in conjunction".left()
}
+
+fun extractTemporalRepresentation(
+ queryParams: MultiValueMap
+): Either = either {
+ val optionsParam = Optional.ofNullable(queryParams.getFirst(QueryParameter.OPTIONS.key))
+ val formatParam = queryParams.getFirst(QueryParameter.FORMAT.key)
+ if (formatParam != null) {
+ return TemporalRepresentation.fromString(formatParam)
+ } else {
+ if (!optionsParam.isEmpty) {
+ val hasTemporal = hasValueInOptionsParam(optionsParam, OptionsValue.TEMPORAL_VALUES).bind()
+ val hasAggregated = hasValueInOptionsParam(optionsParam, OptionsValue.AGGREGATED_VALUES).bind()
+ when {
+ hasTemporal && hasAggregated ->
+ return BadRequestDataException("Only one temporal representation can be present").left()
+ hasTemporal -> return TemporalRepresentation.TEMPORAL_VALUES.right()
+ hasAggregated -> return TemporalRepresentation.AGGREGATED_VALUES.right()
+ else -> return TemporalRepresentation.NORMALIZED.right()
+ }
+ } else
+ return TemporalRepresentation.NORMALIZED.right()
+ }
+}
+
+enum class TemporalRepresentation(val key: String) {
+ TEMPORAL_VALUES("temporalValues"),
+ AGGREGATED_VALUES("aggregatedValues"),
+ NORMALIZED("normalized");
+ companion object {
+ fun fromString(key: String): Either = either {
+ TemporalRepresentation.entries.find { it.key == key }
+ ?: return InvalidRequestException("'$key' is not a valid temporal representation").left()
+ }
+ }
+}
diff --git a/search-service/src/main/kotlin/com/egm/stellio/search/temporal/web/TemporalApiResponses.kt b/search-service/src/main/kotlin/com/egm/stellio/search/temporal/web/TemporalApiResponses.kt
index 3804985e8..6047cf8e0 100644
--- a/search-service/src/main/kotlin/com/egm/stellio/search/temporal/web/TemporalApiResponses.kt
+++ b/search-service/src/main/kotlin/com/egm/stellio/search/temporal/web/TemporalApiResponses.kt
@@ -1,5 +1,6 @@
package com.egm.stellio.search.temporal.web
+import arrow.core.raise.either
import com.egm.stellio.search.temporal.model.TemporalEntitiesQuery
import com.egm.stellio.search.temporal.model.TemporalQuery
import com.egm.stellio.shared.model.CompactedEntity
@@ -30,8 +31,8 @@ object TemporalApiResponses {
contexts: List,
range: Range?,
lang: String? = null,
- ): ResponseEntity {
- val baseRepresentation = parseRepresentations(requestParams, mediaType)
+ ): ResponseEntity<*> = either {
+ val baseRepresentation = parseRepresentations(requestParams, mediaType).bind()
// this is needed for queryEntitiesViaPost where the properties are not in the query parameters
val representation = lang?.let {
baseRepresentation.copy(languageFilter = it, timeproperty = query.temporalQuery.timeproperty.propertyName)
@@ -48,7 +49,7 @@ object TemporalApiResponses {
contexts
)
- return if (range == null)
+ if (range == null)
successResponse
else ResponseEntity.status(HttpStatus.PARTIAL_CONTENT).apply {
this.headers(
@@ -59,7 +60,10 @@ object TemporalApiResponses {
getHeaderRange(range, query.temporalQuery)
)
}.body(serializeObject(entities.toFinalRepresentation(representation)))
- }
+ }.fold(
+ { it.toErrorResponse() },
+ { it }
+ )
fun buildEntityTemporalResponse(
mediaType: MediaType,
diff --git a/search-service/src/main/kotlin/com/egm/stellio/search/temporal/web/TemporalEntityHandler.kt b/search-service/src/main/kotlin/com/egm/stellio/search/temporal/web/TemporalEntityHandler.kt
index 2b176ef34..e804bd922 100644
--- a/search-service/src/main/kotlin/com/egm/stellio/search/temporal/web/TemporalEntityHandler.kt
+++ b/search-service/src/main/kotlin/com/egm/stellio/search/temporal/web/TemporalEntityHandler.kt
@@ -141,11 +141,11 @@ class TemporalEntityHandler(
@RequestHeader httpHeaders: HttpHeaders,
@AllowedParameters(
implemented = [
- QP.OPTIONS, QP.COUNT, QP.OFFSET, QP.LIMIT, QP.ID, QP.TYPE, QP.ID_PATTERN, QP.ATTRS, QP.Q,
+ QP.OPTIONS, QP.FORMAT, QP.COUNT, QP.OFFSET, QP.LIMIT, QP.ID, QP.TYPE, QP.ID_PATTERN, QP.ATTRS, QP.Q,
QP.GEOMETRY, QP.GEOREL, QP.COORDINATES, QP.GEOPROPERTY, QP.TIMEPROPERTY, QP.TIMEREL, QP.TIMEAT,
QP.ENDTIMEAT, QP.LASTN, QP.LANG, QP.AGGRMETHODS, QP.AGGRPERIODDURATION, QP.SCOPEQ, QP.DATASET_ID
],
- notImplemented = [QP.FORMAT, QP.LOCAL, QP.VIA, QP.PICK, QP.OMIT, QP.EXPAND_VALUES, QP.CSF]
+ notImplemented = [QP.LOCAL, QP.VIA, QP.PICK, QP.OMIT, QP.EXPAND_VALUES, QP.CSF]
)
@RequestParam queryParams: MultiValueMap
): ResponseEntity<*> = either {
@@ -187,10 +187,10 @@ class TemporalEntityHandler(
@PathVariable entityId: URI,
@AllowedParameters(
implemented = [
- QP.OPTIONS, QP.ATTRS, QP.TIMEPROPERTY, QP.TIMEREL, QP.TIMEAT, QP.ENDTIMEAT, QP.LASTN,
+ QP.OPTIONS, QP.FORMAT, QP.ATTRS, QP.TIMEPROPERTY, QP.TIMEREL, QP.TIMEAT, QP.ENDTIMEAT, QP.LASTN,
QP.LANG, QP.AGGRMETHODS, QP.AGGRPERIODDURATION, QP.DATASET_ID
],
- notImplemented = [QP.FORMAT, QP.LOCAL, QP.VIA, QP.PICK, QP.OMIT]
+ notImplemented = [QP.LOCAL, QP.VIA, QP.PICK, QP.OMIT]
)
@RequestParam queryParams: MultiValueMap
): ResponseEntity<*> = either {
@@ -209,7 +209,7 @@ class TemporalEntityHandler(
val compactedEntity = compactEntity(temporalEntity, contexts)
- val ngsiLdDataRepresentation = parseRepresentations(queryParams, mediaType)
+ val ngsiLdDataRepresentation = parseRepresentations(queryParams, mediaType).bind()
buildEntityTemporalResponse(mediaType, contexts, temporalEntitiesQuery, range)
.body(serializeObject(compactedEntity.toFinalRepresentation(ngsiLdDataRepresentation)))
}.fold(
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 b4e467a5a..27370df17 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
@@ -183,7 +183,7 @@ class EntitiesQueryUtilsTests {
requestParams.add("count", "true")
requestParams.add("offset", "1")
requestParams.add("limit", "10")
- requestParams.add("options", "keyValues")
+ requestParams.add("format", "keyValues")
requestParams.add("containedBy", "urn:ngsi-ld:Beekeper:A,urn:ngsi-ld:Beekeeper:B")
requestParams.add("join", "inline")
requestParams.add("joinLevel", "1")
diff --git a/search-service/src/test/kotlin/com/egm/stellio/search/entity/web/EntityHandlerTests.kt b/search-service/src/test/kotlin/com/egm/stellio/search/entity/web/EntityHandlerTests.kt
index 3b89c8169..24d07d80d 100644
--- a/search-service/src/test/kotlin/com/egm/stellio/search/entity/web/EntityHandlerTests.kt
+++ b/search-service/src/test/kotlin/com/egm/stellio/search/entity/web/EntityHandlerTests.kt
@@ -478,6 +478,47 @@ class EntityHandlerTests {
)
}
+ @Test
+ fun `get entity by id should correctly return the representation asked in the format parameter`() {
+ initializeRetrieveEntityMocks()
+ coEvery { entityQueryService.queryEntity(any(), MOCK_USER_SUB) } returns ExpandedEntity(
+ mapOf(
+ "@id" to beehiveId.toString(),
+ "@type" to listOf("Beehive"),
+ "https://uri.etsi.org/ngsi-ld/default-context/prop1" to mapOf(
+ JSONLD_TYPE to NGSILD_PROPERTY_TYPE.uri,
+ NGSILD_PROPERTY_VALUE to mapOf(
+ JSONLD_VALUE to "some value"
+ )
+ ),
+ "https://uri.etsi.org/ngsi-ld/default-context/rel1" to mapOf(
+ JSONLD_TYPE to NGSILD_RELATIONSHIP_TYPE.uri,
+ NGSILD_RELATIONSHIP_OBJECT to mapOf(
+ JSONLD_ID to "urn:ngsi-ld:Entity:1234"
+ )
+ )
+ )
+ ).right()
+
+ webClient.get()
+ .uri("/ngsi-ld/v1/entities/$beehiveId?format=keyValues&options=normalized")
+ .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
+ .exchange()
+ .expectStatus().isOk
+ .expectBody()
+ .json(
+ """
+ {
+ "id": "$beehiveId",
+ "type": "Beehive",
+ "prop1": "some value",
+ "rel1": "urn:ngsi-ld:Entity:1234",
+ "@context": "${applicationProperties.contexts.core}"
+ }
+ """.trimIndent()
+ )
+ }
+
@Test
fun `get entity by id should return 404 if the entity has none of the requested attributes`() {
initializeRetrieveEntityMocks()
diff --git a/search-service/src/test/kotlin/com/egm/stellio/search/scope/ScopeServiceTests.kt b/search-service/src/test/kotlin/com/egm/stellio/search/scope/ScopeServiceTests.kt
index 24bc39f48..a607428c8 100644
--- a/search-service/src/test/kotlin/com/egm/stellio/search/scope/ScopeServiceTests.kt
+++ b/search-service/src/test/kotlin/com/egm/stellio/search/scope/ScopeServiceTests.kt
@@ -12,6 +12,7 @@ import com.egm.stellio.search.support.buildDefaultTestTemporalQuery
import com.egm.stellio.search.temporal.model.AttributeInstance.TemporalProperty
import com.egm.stellio.search.temporal.model.TemporalEntitiesQueryFromGet
import com.egm.stellio.search.temporal.model.TemporalQuery
+import com.egm.stellio.search.temporal.util.TemporalRepresentation
import com.egm.stellio.shared.model.getScopes
import com.egm.stellio.shared.queryparameter.PaginationQuery
import com.egm.stellio.shared.util.APIC_COMPOUND_CONTEXTS
@@ -154,9 +155,8 @@ class ScopeServiceTests : WithTimescaleContainer, WithKafkaContainer() {
contexts = APIC_COMPOUND_CONTEXTS
),
buildDefaultTestTemporalQuery(timeproperty = TemporalProperty.MODIFIED_AT),
- withTemporalValues = false,
- withAudit = false,
- withAggregatedValues = false
+ temporalRepresentation = TemporalRepresentation.NORMALIZED,
+ withAudit = false
)
).shouldSucceedAndResult()
@@ -184,9 +184,8 @@ class ScopeServiceTests : WithTimescaleContainer, WithKafkaContainer() {
timerel = TemporalQuery.Timerel.BEFORE,
timeAt = ngsiLdDateTime()
),
- withTemporalValues = false,
- withAudit = false,
- withAggregatedValues = false
+ temporalRepresentation = TemporalRepresentation.NORMALIZED,
+ withAudit = false
)
).shouldSucceedAndResult()
@@ -216,9 +215,8 @@ class ScopeServiceTests : WithTimescaleContainer, WithKafkaContainer() {
aggrMethods = listOf(TemporalQuery.Aggregate.SUM),
aggrPeriodDuration = "PT1S"
),
- withTemporalValues = false,
- withAudit = false,
- withAggregatedValues = true
+ temporalRepresentation = TemporalRepresentation.AGGREGATED_VALUES,
+ withAudit = false
),
ngsiLdDateTime().minusHours(1)
).shouldSucceedAndResult()
@@ -250,9 +248,8 @@ class ScopeServiceTests : WithTimescaleContainer, WithKafkaContainer() {
aggrMethods = listOf(TemporalQuery.Aggregate.SUM),
aggrPeriodDuration = "PT0S"
),
- withTemporalValues = false,
- withAudit = false,
- withAggregatedValues = true
+ temporalRepresentation = TemporalRepresentation.AGGREGATED_VALUES,
+ withAudit = false
),
ngsiLdDateTime().minusHours(1)
).shouldSucceedAndResult()
@@ -283,9 +280,8 @@ class ScopeServiceTests : WithTimescaleContainer, WithKafkaContainer() {
aggrMethods = listOf(TemporalQuery.Aggregate.SUM),
aggrPeriodDuration = "PT0S"
),
- withTemporalValues = false,
- withAudit = false,
- withAggregatedValues = true
+ temporalRepresentation = TemporalRepresentation.AGGREGATED_VALUES,
+ withAudit = false
),
ngsiLdDateTime().minusHours(1)
).shouldSucceedAndResult()
@@ -319,9 +315,8 @@ class ScopeServiceTests : WithTimescaleContainer, WithKafkaContainer() {
instanceLimit = 1,
lastN = 1
),
- withTemporalValues = false,
- withAudit = false,
- withAggregatedValues = true
+ temporalRepresentation = TemporalRepresentation.AGGREGATED_VALUES,
+ withAudit = false
),
ngsiLdDateTime().minusHours(1)
).shouldSucceedAndResult()
@@ -365,9 +360,8 @@ class ScopeServiceTests : WithTimescaleContainer, WithKafkaContainer() {
timeAt = ZonedDateTime.parse("2024-08-13T00:00:00Z"),
instanceLimit = 5
),
- withTemporalValues = false,
- withAudit = false,
- withAggregatedValues = false
+ temporalRepresentation = TemporalRepresentation.NORMALIZED,
+ withAudit = false
),
ngsiLdDateTime().minusHours(1)
).shouldSucceedWith {
@@ -413,9 +407,8 @@ class ScopeServiceTests : WithTimescaleContainer, WithKafkaContainer() {
endTimeAt = ZonedDateTime.parse("2024-08-15T00:00:00Z"),
instanceLimit = 5
),
- withTemporalValues = false,
- withAudit = false,
- withAggregatedValues = false
+ temporalRepresentation = TemporalRepresentation.NORMALIZED,
+ withAudit = false
),
ngsiLdDateTime().minusHours(1)
).shouldSucceedWith {
@@ -447,9 +440,8 @@ class ScopeServiceTests : WithTimescaleContainer, WithKafkaContainer() {
contexts = APIC_COMPOUND_CONTEXTS
),
buildDefaultTestTemporalQuery(),
- withTemporalValues = false,
- withAudit = false,
- withAggregatedValues = false
+ temporalRepresentation = TemporalRepresentation.NORMALIZED,
+ withAudit = false
)
).shouldSucceedAndResult()
assertTrue(scopeHistoryEntries.isEmpty())
diff --git a/search-service/src/test/kotlin/com/egm/stellio/search/scope/TemporalScopeBuilderTests.kt b/search-service/src/test/kotlin/com/egm/stellio/search/scope/TemporalScopeBuilderTests.kt
index 94df0ba54..cd1703094 100644
--- a/search-service/src/test/kotlin/com/egm/stellio/search/scope/TemporalScopeBuilderTests.kt
+++ b/search-service/src/test/kotlin/com/egm/stellio/search/scope/TemporalScopeBuilderTests.kt
@@ -6,6 +6,7 @@ import com.egm.stellio.search.support.gimmeEntityPayload
import com.egm.stellio.search.temporal.model.AttributeInstance.TemporalProperty
import com.egm.stellio.search.temporal.model.TemporalEntitiesQueryFromGet
import com.egm.stellio.search.temporal.model.TemporalQuery
+import com.egm.stellio.search.temporal.util.TemporalRepresentation
import com.egm.stellio.shared.util.JsonUtils
import com.egm.stellio.shared.util.assertJsonPayloadsAreEqual
import com.egm.stellio.shared.util.loadSampleData
@@ -73,9 +74,8 @@ class TemporalScopeBuilderTests {
TemporalEntitiesQueryFromGet(
entitiesQuery = buildDefaultQueryParams(),
temporalQuery = temporalQuery,
- withTemporalValues = false,
- withAudit = false,
- withAggregatedValues = true
+ temporalRepresentation = TemporalRepresentation.AGGREGATED_VALUES,
+ withAudit = false
)
)
@@ -111,9 +111,8 @@ class TemporalScopeBuilderTests {
TemporalEntitiesQueryFromGet(
entitiesQuery = buildDefaultQueryParams(),
temporalQuery = temporalQuery,
- withTemporalValues = true,
- withAudit = false,
- withAggregatedValues = false
+ temporalRepresentation = TemporalRepresentation.TEMPORAL_VALUES,
+ withAudit = false
)
)
@@ -151,9 +150,8 @@ class TemporalScopeBuilderTests {
TemporalEntitiesQueryFromGet(
entitiesQuery = buildDefaultQueryParams(),
temporalQuery = temporalQuery,
- withTemporalValues = false,
- withAudit = false,
- withAggregatedValues = false
+ temporalRepresentation = TemporalRepresentation.NORMALIZED,
+ withAudit = false
)
)
diff --git a/search-service/src/test/kotlin/com/egm/stellio/search/support/BusinessObjectsFactory.kt b/search-service/src/test/kotlin/com/egm/stellio/search/support/BusinessObjectsFactory.kt
index 24e0b5d71..0181e98a1 100644
--- a/search-service/src/test/kotlin/com/egm/stellio/search/support/BusinessObjectsFactory.kt
+++ b/search-service/src/test/kotlin/com/egm/stellio/search/support/BusinessObjectsFactory.kt
@@ -7,6 +7,7 @@ import com.egm.stellio.search.entity.model.Entity
import com.egm.stellio.search.temporal.model.AttributeInstance
import com.egm.stellio.search.temporal.model.TemporalEntitiesQueryFromGet
import com.egm.stellio.search.temporal.model.TemporalQuery
+import com.egm.stellio.search.temporal.util.TemporalRepresentation
import com.egm.stellio.shared.model.ExpandedTerm
import com.egm.stellio.shared.model.addNonReifiedTemporalProperty
import com.egm.stellio.shared.model.getSingleEntry
@@ -153,9 +154,8 @@ fun gimmeVocabPropertyAttributeInstance(
fun gimmeTemporalEntitiesQuery(
temporalQuery: TemporalQuery,
- withTemporalValues: Boolean = false,
- withAudit: Boolean = false,
- withAggregatedValues: Boolean = false
+ temporalRepresentation: TemporalRepresentation = TemporalRepresentation.NORMALIZED,
+ withAudit: Boolean = false
): TemporalEntitiesQueryFromGet =
TemporalEntitiesQueryFromGet(
entitiesQuery = EntitiesQueryFromGet(
@@ -163,9 +163,8 @@ fun gimmeTemporalEntitiesQuery(
contexts = APIC_COMPOUND_CONTEXTS
),
temporalQuery = temporalQuery,
- withTemporalValues = withTemporalValues,
- withAudit = withAudit,
- withAggregatedValues = withAggregatedValues
+ temporalRepresentation = temporalRepresentation,
+ withAudit = withAudit
)
fun buildDefaultQueryParams(): EntitiesQueryFromGet =
diff --git a/search-service/src/test/kotlin/com/egm/stellio/search/temporal/service/AggregatedTemporalQueryServiceTests.kt b/search-service/src/test/kotlin/com/egm/stellio/search/temporal/service/AggregatedTemporalQueryServiceTests.kt
index 3efe15704..3f53b35e2 100644
--- a/search-service/src/test/kotlin/com/egm/stellio/search/temporal/service/AggregatedTemporalQueryServiceTests.kt
+++ b/search-service/src/test/kotlin/com/egm/stellio/search/temporal/service/AggregatedTemporalQueryServiceTests.kt
@@ -14,6 +14,7 @@ import com.egm.stellio.search.temporal.model.AttributeInstance
import com.egm.stellio.search.temporal.model.AttributeInstanceResult
import com.egm.stellio.search.temporal.model.TemporalEntitiesQueryFromGet
import com.egm.stellio.search.temporal.model.TemporalQuery
+import com.egm.stellio.search.temporal.util.TemporalRepresentation
import com.egm.stellio.shared.model.OperationNotSupportedException
import com.egm.stellio.shared.util.INCOMING_PROPERTY
import com.egm.stellio.shared.util.ngsiLdDateTime
@@ -508,7 +509,7 @@ class AggregatedTemporalQueryServiceTests : WithTimescaleContainer, WithKafkaCon
aggrPeriodDuration = aggrPeriodDuration,
aggrMethods = listOfNotNull(TemporalQuery.Aggregate.forMethod(aggrMethod))
),
- withAggregatedValues = true
+ temporalRepresentation = TemporalRepresentation.AGGREGATED_VALUES
)
private fun assertAggregatedResult(
diff --git a/search-service/src/test/kotlin/com/egm/stellio/search/temporal/service/AttributeInstanceServiceTests.kt b/search-service/src/test/kotlin/com/egm/stellio/search/temporal/service/AttributeInstanceServiceTests.kt
index 37f9eef69..02083fb4b 100644
--- a/search-service/src/test/kotlin/com/egm/stellio/search/temporal/service/AttributeInstanceServiceTests.kt
+++ b/search-service/src/test/kotlin/com/egm/stellio/search/temporal/service/AttributeInstanceServiceTests.kt
@@ -21,6 +21,7 @@ import com.egm.stellio.search.temporal.model.FullAttributeInstanceResult
import com.egm.stellio.search.temporal.model.SimplifiedAttributeInstanceResult
import com.egm.stellio.search.temporal.model.TemporalQuery
import com.egm.stellio.search.temporal.model.TemporalQuery.Timerel
+import com.egm.stellio.search.temporal.util.TemporalRepresentation
import com.egm.stellio.shared.model.ExpandedAttributes
import com.egm.stellio.shared.model.ResourceNotFoundException
import com.egm.stellio.shared.model.addNonReifiedTemporalProperty
@@ -199,7 +200,8 @@ class AttributeInstanceServiceTests : WithTimescaleContainer, WithKafkaContainer
buildDefaultTestTemporalQuery(
timerel = Timerel.AFTER,
timeAt = now.minusHours(1)
- )
+ ),
+ TemporalRepresentation.NORMALIZED
)
attributeInstanceService.search(temporalEntitiesQuery, incomingAttribute)
.shouldSucceedWith {
@@ -224,7 +226,8 @@ class AttributeInstanceServiceTests : WithTimescaleContainer, WithKafkaContainer
timerel = Timerel.AFTER,
timeAt = now.minusHours(1),
timeproperty = AttributeInstance.TemporalProperty.CREATED_AT
- )
+ ),
+ TemporalRepresentation.NORMALIZED
)
attributeInstanceService.search(temporalEntitiesQuery, incomingAttribute)
.shouldSucceedWith {
@@ -249,7 +252,8 @@ class AttributeInstanceServiceTests : WithTimescaleContainer, WithKafkaContainer
timerel = Timerel.AFTER,
timeAt = now.minusHours(1),
timeproperty = AttributeInstance.TemporalProperty.CREATED_AT
- )
+ ),
+ TemporalRepresentation.NORMALIZED
)
attributeInstanceService.search(temporalEntitiesQuery, incomingAttribute)
.shouldSucceedWith {
@@ -274,7 +278,8 @@ class AttributeInstanceServiceTests : WithTimescaleContainer, WithKafkaContainer
timerel = Timerel.AFTER,
timeAt = now.minusHours(1),
timeproperty = AttributeInstance.TemporalProperty.MODIFIED_AT
- )
+ ),
+ TemporalRepresentation.NORMALIZED
)
attributeInstanceService.search(temporalEntitiesQuery, incomingAttribute)
.shouldSucceedWith {
@@ -293,7 +298,8 @@ class AttributeInstanceServiceTests : WithTimescaleContainer, WithKafkaContainer
buildDefaultTestTemporalQuery(
timerel = Timerel.AFTER,
timeAt = now.minusHours(1)
- )
+ ),
+ TemporalRepresentation.NORMALIZED
)
attributeInstanceService.search(temporalEntitiesQuery, incomingAttribute)
.shouldSucceedWith {
@@ -309,7 +315,7 @@ class AttributeInstanceServiceTests : WithTimescaleContainer, WithKafkaContainer
}
attributeInstanceService.search(
- gimmeTemporalEntitiesQuery(buildDefaultTestTemporalQuery()),
+ gimmeTemporalEntitiesQuery(buildDefaultTestTemporalQuery(), TemporalRepresentation.NORMALIZED),
incomingAttribute
)
.shouldSucceedWith {
@@ -353,7 +359,7 @@ class AttributeInstanceServiceTests : WithTimescaleContainer, WithKafkaContainer
}
attributeInstanceService.search(
- gimmeTemporalEntitiesQuery(buildDefaultTestTemporalQuery(), withTemporalValues = true),
+ gimmeTemporalEntitiesQuery(buildDefaultTestTemporalQuery(), TemporalRepresentation.TEMPORAL_VALUES),
attribute2
).shouldSucceedWith { results ->
assertThat(results)
@@ -381,7 +387,7 @@ class AttributeInstanceServiceTests : WithTimescaleContainer, WithKafkaContainer
aggrPeriodDuration = "P30D",
aggrMethods = listOf(TemporalQuery.Aggregate.MAX)
),
- withAggregatedValues = true
+ TemporalRepresentation.AGGREGATED_VALUES
)
val origin = attributeInstanceService.selectOldestDate(
@@ -409,7 +415,8 @@ class AttributeInstanceServiceTests : WithTimescaleContainer, WithKafkaContainer
timerel = Timerel.AFTER,
timeAt = ZonedDateTime.parse("2022-07-01T00:00:00Z"),
instanceLimit = 5
- )
+ ),
+ TemporalRepresentation.NORMALIZED
)
attributeInstanceService.search(temporalEntitiesQuery, incomingAttribute)
@@ -435,7 +442,8 @@ class AttributeInstanceServiceTests : WithTimescaleContainer, WithKafkaContainer
timeAt = ZonedDateTime.parse("2022-07-01T00:00:00Z"),
endTimeAt = ZonedDateTime.parse("2022-07-05T00:00:00Z"),
instanceLimit = 5
- )
+ ),
+ TemporalRepresentation.NORMALIZED
)
attributeInstanceService.search(temporalEntitiesQuery, incomingAttribute)
@@ -462,7 +470,8 @@ class AttributeInstanceServiceTests : WithTimescaleContainer, WithKafkaContainer
timeAt = now.minusHours(1),
instanceLimit = 5,
lastN = 5
- )
+ ),
+ TemporalRepresentation.NORMALIZED
)
attributeInstanceService.search(temporalEntitiesQuery, incomingAttribute)
.shouldSucceedWith {
@@ -492,7 +501,7 @@ class AttributeInstanceServiceTests : WithTimescaleContainer, WithKafkaContainer
aggrMethods = listOf(TemporalQuery.Aggregate.SUM),
instanceLimit = 5,
),
- withAggregatedValues = true
+ TemporalRepresentation.AGGREGATED_VALUES
)
attributeInstanceService.search(temporalEntitiesQuery, incomingAttribute)
.shouldSucceedWith {
@@ -524,7 +533,8 @@ class AttributeInstanceServiceTests : WithTimescaleContainer, WithKafkaContainer
buildDefaultTestTemporalQuery(
timerel = Timerel.AFTER,
timeAt = now.minusHours(1)
- )
+ ),
+ TemporalRepresentation.NORMALIZED
)
attributeInstanceService.search(temporalEntitiesQuery, incomingAttribute)
.shouldSucceedWith { results ->
@@ -546,7 +556,8 @@ class AttributeInstanceServiceTests : WithTimescaleContainer, WithKafkaContainer
buildDefaultTestTemporalQuery(
timerel = Timerel.AFTER,
timeAt = now.minusHours(1)
- )
+ ),
+ TemporalRepresentation.NORMALIZED
)
attributeInstanceService.search(
temporalEntitiesQuery,
@@ -567,7 +578,8 @@ class AttributeInstanceServiceTests : WithTimescaleContainer, WithKafkaContainer
buildDefaultTestTemporalQuery(
timerel = Timerel.AFTER,
timeAt = now.plusHours(1)
- )
+ ),
+ TemporalRepresentation.NORMALIZED
)
attributeInstanceService.search(temporalEntitiesQuery, incomingAttribute)
.shouldSucceedWith {
@@ -600,7 +612,7 @@ class AttributeInstanceServiceTests : WithTimescaleContainer, WithKafkaContainer
timerel = Timerel.AFTER,
timeAt = now.minusHours(1)
),
- withTemporalValues = true
+ TemporalRepresentation.TEMPORAL_VALUES
),
incomingAttribute
).shouldSucceedWith { results ->
@@ -808,7 +820,8 @@ class AttributeInstanceServiceTests : WithTimescaleContainer, WithKafkaContainer
buildDefaultTestTemporalQuery(
timerel = Timerel.AFTER,
timeAt = ZonedDateTime.parse("1970-01-01T00:00:00Z")
- )
+ ),
+ TemporalRepresentation.NORMALIZED
)
attributeInstanceService.modifyAttributeInstance(
@@ -846,7 +859,8 @@ class AttributeInstanceServiceTests : WithTimescaleContainer, WithKafkaContainer
buildDefaultTestTemporalQuery(
timerel = Timerel.AFTER,
timeAt = ZonedDateTime.parse("1970-01-01T00:00:00Z")
- )
+ ),
+ TemporalRepresentation.NORMALIZED
)
attributeInstanceService.modifyAttributeInstance(
@@ -885,7 +899,8 @@ class AttributeInstanceServiceTests : WithTimescaleContainer, WithKafkaContainer
buildDefaultTestTemporalQuery(
timerel = Timerel.AFTER,
timeAt = ZonedDateTime.parse("1970-01-01T00:00:00Z")
- )
+ ),
+ TemporalRepresentation.NORMALIZED
)
attributeInstanceService.modifyAttributeInstance(
@@ -929,7 +944,8 @@ class AttributeInstanceServiceTests : WithTimescaleContainer, WithKafkaContainer
buildDefaultTestTemporalQuery(
timerel = Timerel.AFTER,
timeAt = ZonedDateTime.parse("1970-01-01T00:00:00Z")
- )
+ ),
+ TemporalRepresentation.NORMALIZED
)
attributeInstanceService.modifyAttributeInstance(
@@ -968,7 +984,7 @@ class AttributeInstanceServiceTests : WithTimescaleContainer, WithKafkaContainer
).shouldSucceed()
attributeInstanceService.search(
- gimmeTemporalEntitiesQuery(buildDefaultTestTemporalQuery()),
+ gimmeTemporalEntitiesQuery(buildDefaultTestTemporalQuery(), TemporalRepresentation.NORMALIZED),
incomingAttribute
)
.shouldSucceedWith {
@@ -1042,7 +1058,8 @@ class AttributeInstanceServiceTests : WithTimescaleContainer, WithKafkaContainer
buildDefaultTestTemporalQuery(
timerel = Timerel.AFTER,
timeAt = now.minusHours(1)
- )
+ ),
+ TemporalRepresentation.NORMALIZED
)
attributeInstanceService.search(temporalEntitiesQuery, incomingAttribute)
.shouldSucceedWith { assertThat(it).isEmpty() }
@@ -1052,7 +1069,8 @@ class AttributeInstanceServiceTests : WithTimescaleContainer, WithKafkaContainer
timerel = Timerel.AFTER,
timeAt = now.minusHours(1),
timeproperty = AttributeInstance.TemporalProperty.CREATED_AT
- )
+ ),
+ TemporalRepresentation.NORMALIZED
)
attributeInstanceService.search(temporalEntitiesAuditQuery, incomingAttribute)
@@ -1078,7 +1096,8 @@ class AttributeInstanceServiceTests : WithTimescaleContainer, WithKafkaContainer
buildDefaultTestTemporalQuery(
timerel = Timerel.AFTER,
timeAt = now.minusHours(1)
- )
+ ),
+ TemporalRepresentation.NORMALIZED
)
attributeInstanceService.search(temporalEntitiesQuery, outgoingAttribute)
.shouldSucceedWith { assertThat(it).hasSize(5) }
@@ -1105,7 +1124,8 @@ class AttributeInstanceServiceTests : WithTimescaleContainer, WithKafkaContainer
buildDefaultTestTemporalQuery(
timerel = Timerel.AFTER,
timeAt = now.minusHours(1)
- )
+ ),
+ TemporalRepresentation.NORMALIZED
)
attributeInstanceService.search(temporalEntitiesQuery, outgoingAttribute)
.shouldSucceedWith { assertThat(it).hasSize(5) }
diff --git a/search-service/src/test/kotlin/com/egm/stellio/search/temporal/service/TemporalPaginationServiceTests.kt b/search-service/src/test/kotlin/com/egm/stellio/search/temporal/service/TemporalPaginationServiceTests.kt
index 2d7bf7c20..035927876 100644
--- a/search-service/src/test/kotlin/com/egm/stellio/search/temporal/service/TemporalPaginationServiceTests.kt
+++ b/search-service/src/test/kotlin/com/egm/stellio/search/temporal/service/TemporalPaginationServiceTests.kt
@@ -13,6 +13,7 @@ import com.egm.stellio.search.temporal.model.TemporalEntitiesQueryFromGet
import com.egm.stellio.search.temporal.model.TemporalQuery
import com.egm.stellio.search.temporal.service.TemporalPaginationService.getPaginatedAttributeWithInstancesAndRange
import com.egm.stellio.search.temporal.util.AttributesWithInstances
+import com.egm.stellio.search.temporal.util.TemporalRepresentation
import com.egm.stellio.shared.queryparameter.PaginationQuery
import com.egm.stellio.shared.util.APIC_COMPOUND_CONTEXTS
import com.egm.stellio.shared.util.INCOMING_PROPERTY
@@ -138,9 +139,8 @@ class TemporalPaginationServiceTests {
attrs = setOf(INCOMING_PROPERTY, OUTGOING_PROPERTY),
contexts = APIC_COMPOUND_CONTEXTS
),
- withTemporalValues = false,
- withAudit = false,
- withAggregatedValues = false
+ temporalRepresentation = TemporalRepresentation.NORMALIZED,
+ withAudit = false
)
@Test
@@ -282,9 +282,8 @@ class TemporalPaginationServiceTests {
attrs = setOf(INCOMING_PROPERTY, OUTGOING_PROPERTY),
contexts = APIC_COMPOUND_CONTEXTS
),
- withTemporalValues = false,
- withAudit = false,
- withAggregatedValues = true
+ temporalRepresentation = TemporalRepresentation.AGGREGATED_VALUES,
+ withAudit = false
)
val attributesWithInstances: AttributesWithInstances =
diff --git a/search-service/src/test/kotlin/com/egm/stellio/search/temporal/service/TemporalQueryServiceTests.kt b/search-service/src/test/kotlin/com/egm/stellio/search/temporal/service/TemporalQueryServiceTests.kt
index 2a3cc15d9..c34028380 100644
--- a/search-service/src/test/kotlin/com/egm/stellio/search/temporal/service/TemporalQueryServiceTests.kt
+++ b/search-service/src/test/kotlin/com/egm/stellio/search/temporal/service/TemporalQueryServiceTests.kt
@@ -20,6 +20,7 @@ import com.egm.stellio.search.temporal.model.FullAttributeInstanceResult
import com.egm.stellio.search.temporal.model.SimplifiedAttributeInstanceResult
import com.egm.stellio.search.temporal.model.TemporalEntitiesQueryFromGet
import com.egm.stellio.search.temporal.model.TemporalQuery
+import com.egm.stellio.search.temporal.util.TemporalRepresentation
import com.egm.stellio.shared.model.ResourceNotFoundException
import com.egm.stellio.shared.queryparameter.PaginationQuery
import com.egm.stellio.shared.util.APIARY_TYPE
@@ -91,9 +92,8 @@ class TemporalQueryServiceTests {
TemporalEntitiesQueryFromGet(
entitiesQuery = buildDefaultQueryParams(),
temporalQuery = buildDefaultTestTemporalQuery(),
- withTemporalValues = true,
- withAudit = false,
- withAggregatedValues = false
+ temporalRepresentation = TemporalRepresentation.TEMPORAL_VALUES,
+ withAudit = false
)
).fold({
assertInstanceOf(ResourceNotFoundException::class.java, it)
@@ -139,9 +139,8 @@ class TemporalQueryServiceTests {
paginationQuery = PaginationQuery(limit = 0, offset = 50),
contexts = APIC_COMPOUND_CONTEXTS
),
- withTemporalValues = false,
- withAudit = false,
- withAggregatedValues = false
+ temporalRepresentation = TemporalRepresentation.NORMALIZED,
+ withAudit = false
)
)
@@ -172,9 +171,8 @@ class TemporalQueryServiceTests {
paginationQuery = PaginationQuery(limit = 0, offset = 50),
contexts = APIC_COMPOUND_CONTEXTS
),
- withTemporalValues = false,
- withAudit = false,
- withAggregatedValues = false
+ temporalRepresentation = TemporalRepresentation.NORMALIZED,
+ withAudit = false
),
emptyList()
)
@@ -195,9 +193,8 @@ class TemporalQueryServiceTests {
paginationQuery = PaginationQuery(limit = 0, offset = 50),
contexts = APIC_COMPOUND_CONTEXTS
),
- withTemporalValues = false,
- withAudit = false,
- withAggregatedValues = true
+ temporalRepresentation = TemporalRepresentation.AGGREGATED_VALUES,
+ withAudit = false
),
emptyList()
)
@@ -223,9 +220,8 @@ class TemporalQueryServiceTests {
paginationQuery = PaginationQuery(limit = 0, offset = 50),
contexts = APIC_COMPOUND_CONTEXTS
),
- withTemporalValues = false,
- withAudit = false,
- withAggregatedValues = true
+ temporalRepresentation = TemporalRepresentation.AGGREGATED_VALUES,
+ withAudit = false
),
emptyList()
)
@@ -274,9 +270,8 @@ class TemporalQueryServiceTests {
timerel = TemporalQuery.Timerel.BEFORE,
timeAt = ZonedDateTime.parse("2019-10-17T07:31:39Z")
),
- withTemporalValues = true,
- withAudit = false,
- withAggregatedValues = false
+ temporalRepresentation = TemporalRepresentation.TEMPORAL_VALUES,
+ withAudit = false
)
)
@@ -345,9 +340,8 @@ class TemporalQueryServiceTests {
timeAt = ZonedDateTime.parse("2019-10-17T07:31:39Z"),
aggrMethods = listOf(TemporalQuery.Aggregate.AVG)
),
- withTemporalValues = false,
- withAudit = false,
- withAggregatedValues = true
+ temporalRepresentation = TemporalRepresentation.AGGREGATED_VALUES,
+ withAudit = false
)
)
.fold({
diff --git a/search-service/src/test/kotlin/com/egm/stellio/search/temporal/util/TemporalEntitiesParameterizedSource.kt b/search-service/src/test/kotlin/com/egm/stellio/search/temporal/util/TemporalEntitiesParameterizedSource.kt
index 2c74de4b7..f1f6f6a86 100644
--- a/search-service/src/test/kotlin/com/egm/stellio/search/temporal/util/TemporalEntitiesParameterizedSource.kt
+++ b/search-service/src/test/kotlin/com/egm/stellio/search/temporal/util/TemporalEntitiesParameterizedSource.kt
@@ -227,19 +227,19 @@ class TemporalEntitiesParameterizedSource {
return Stream.of(
Arguments.arguments(
resultOfTwoEntitiesWithOneProperty,
- false,
+ TemporalRepresentation.NORMALIZED,
true,
loadSampleData("expectations/query/two_beehives.json")
),
Arguments.arguments(
simplifiedResultOfTwoEntitiesWithOneProperty,
- true,
+ TemporalRepresentation.TEMPORAL_VALUES,
false,
loadSampleData("expectations/query/two_beehives_temporal_values.json")
),
Arguments.arguments(
simplifiedResultOfTwoEntitiesWithOnePropertyAndOneRelationship,
- true,
+ TemporalRepresentation.TEMPORAL_VALUES,
false,
loadSampleData("expectations/query/two_entities_temporal_values_property_and_relationship.json")
)
diff --git a/search-service/src/test/kotlin/com/egm/stellio/search/temporal/util/TemporalEntityBuilderTests.kt b/search-service/src/test/kotlin/com/egm/stellio/search/temporal/util/TemporalEntityBuilderTests.kt
index 5fe3d43e3..4bf5e7473 100644
--- a/search-service/src/test/kotlin/com/egm/stellio/search/temporal/util/TemporalEntityBuilderTests.kt
+++ b/search-service/src/test/kotlin/com/egm/stellio/search/temporal/util/TemporalEntityBuilderTests.kt
@@ -56,9 +56,8 @@ class TemporalEntityBuilderTests {
TemporalEntitiesQueryFromGet(
entitiesQuery = buildDefaultQueryParams(),
temporalQuery = buildDefaultTestTemporalQuery(),
- withTemporalValues = false,
- withAudit = false,
- withAggregatedValues = false
+ temporalRepresentation = TemporalRepresentation.NORMALIZED,
+ withAudit = false
)
)
assertJsonPayloadsAreEqual(
@@ -73,7 +72,7 @@ class TemporalEntityBuilderTests {
fun `it should correctly build a temporal entity`(
scopeHistory: List,
attributeAndResultsMap: AttributesWithInstances,
- withTemporalValues: Boolean,
+ temporalRepresentation: TemporalRepresentation,
withAudit: Boolean,
expectation: String
) {
@@ -89,9 +88,8 @@ class TemporalEntityBuilderTests {
TemporalEntitiesQueryFromGet(
entitiesQuery = buildDefaultQueryParams(),
temporalQuery = buildDefaultTestTemporalQuery(),
- withTemporalValues,
- withAudit,
- false
+ temporalRepresentation,
+ withAudit
)
)
assertJsonPayloadsAreEqual(
@@ -105,7 +103,7 @@ class TemporalEntityBuilderTests {
@MethodSource("com.egm.stellio.search.temporal.util.TemporalEntitiesParameterizedSource#rawResultsProvider")
fun `it should correctly build temporal entities`(
entityTemporalResults: List,
- withTemporalValues: Boolean,
+ temporalRepresentation: TemporalRepresentation,
withAudit: Boolean,
expectation: String
) {
@@ -114,9 +112,8 @@ class TemporalEntityBuilderTests {
TemporalEntitiesQueryFromGet(
entitiesQuery = buildDefaultQueryParams(),
temporalQuery = buildDefaultTestTemporalQuery(),
- withTemporalValues,
- withAudit,
- false
+ temporalRepresentation,
+ withAudit
)
)
assertJsonPayloadsAreEqual(
@@ -193,9 +190,8 @@ class TemporalEntityBuilderTests {
TemporalEntitiesQueryFromGet(
entitiesQuery = buildDefaultQueryParams(),
temporalQuery = temporalQuery,
- withTemporalValues = false,
- withAudit = false,
- withAggregatedValues = true
+ temporalRepresentation = TemporalRepresentation.AGGREGATED_VALUES,
+ withAudit = false
)
)
diff --git a/search-service/src/test/kotlin/com/egm/stellio/search/temporal/util/TemporalEntityParameterizedSource.kt b/search-service/src/test/kotlin/com/egm/stellio/search/temporal/util/TemporalEntityParameterizedSource.kt
index 133701db2..b822787ce 100644
--- a/search-service/src/test/kotlin/com/egm/stellio/search/temporal/util/TemporalEntityParameterizedSource.kt
+++ b/search-service/src/test/kotlin/com/egm/stellio/search/temporal/util/TemporalEntityParameterizedSource.kt
@@ -69,7 +69,7 @@ class TemporalEntityParameterizedSource {
)
)
),
- false,
+ TemporalRepresentation.NORMALIZED,
false,
loadSampleData("expectations/beehive_incoming_multi_instances_without_datasetId.jsonld")
)
@@ -115,7 +115,7 @@ class TemporalEntityParameterizedSource {
)
)
),
- false,
+ TemporalRepresentation.NORMALIZED,
false,
loadSampleData("expectations/beehive_relationship_multi_instances_without_datasetId.jsonld")
)
@@ -193,7 +193,7 @@ class TemporalEntityParameterizedSource {
)
)
),
- false,
+ TemporalRepresentation.NORMALIZED,
false,
loadSampleData("expectations/beehive_incoming_multi_instances.jsonld")
)
@@ -237,7 +237,7 @@ class TemporalEntityParameterizedSource {
)
)
),
- false,
+ TemporalRepresentation.NORMALIZED,
false,
loadSampleData("expectations/beehive_incoming_multi_instances_string_values.jsonld")
)
@@ -281,7 +281,7 @@ class TemporalEntityParameterizedSource {
)
)
),
- false,
+ TemporalRepresentation.NORMALIZED,
true,
loadSampleData("expectations/beehive_incoming_multi_instances_string_values_with_audit.jsonld")
)
@@ -324,7 +324,7 @@ class TemporalEntityParameterizedSource {
)
)
),
- false,
+ TemporalRepresentation.NORMALIZED,
false,
loadSampleData(
"expectations/beehive_incoming_multi_instances_without_datasetId_string_values.jsonld"
@@ -355,7 +355,7 @@ class TemporalEntityParameterizedSource {
)
)
),
- true,
+ TemporalRepresentation.TEMPORAL_VALUES,
false,
loadSampleData(
"expectations/beehive_incoming_multi_instances_without_datasetId_temporal_values.jsonld"
@@ -407,7 +407,7 @@ class TemporalEntityParameterizedSource {
)
)
),
- true,
+ TemporalRepresentation.TEMPORAL_VALUES,
false,
loadSampleData("expectations/beehive_incoming_multi_instances_temporal_values.jsonld")
)
@@ -437,7 +437,7 @@ class TemporalEntityParameterizedSource {
)
)
),
- true,
+ TemporalRepresentation.TEMPORAL_VALUES,
false,
loadSampleData("expectations/beehive_incoming_multi_instances_string_temporal_values.jsonld")
)
@@ -466,7 +466,7 @@ class TemporalEntityParameterizedSource {
)
)
),
- true,
+ TemporalRepresentation.TEMPORAL_VALUES,
false,
loadSampleData(
"expectations/beehive_incoming_multi_instances_without_datasetId_string_temporal_values.jsonld"
@@ -499,7 +499,7 @@ class TemporalEntityParameterizedSource {
)
)
),
- true,
+ TemporalRepresentation.TEMPORAL_VALUES,
false,
loadSampleData("expectations/beehive_relationship_multi_instances_temporal_values.jsonld")
)
@@ -519,7 +519,7 @@ class TemporalEntityParameterizedSource {
)
),
emptyMap>(),
- true,
+ TemporalRepresentation.TEMPORAL_VALUES,
false,
loadSampleData("expectations/beehive_scope_multi_instances_temporal_values.jsonld")
)
@@ -541,7 +541,7 @@ class TemporalEntityParameterizedSource {
)
),
emptyMap>(),
- false,
+ TemporalRepresentation.NORMALIZED,
false,
loadSampleData("expectations/beehive_scope_multi_instances.jsonld")
)
@@ -576,7 +576,7 @@ class TemporalEntityParameterizedSource {
)
)
),
- true,
+ TemporalRepresentation.TEMPORAL_VALUES,
false,
loadSampleData("expectations/beehive_json_property_temporal_values.jsonld")
)
@@ -625,7 +625,7 @@ class TemporalEntityParameterizedSource {
)
)
),
- true,
+ TemporalRepresentation.TEMPORAL_VALUES,
false,
loadSampleData("expectations/beehive_language_property_temporal_values.jsonld")
)
@@ -667,7 +667,7 @@ class TemporalEntityParameterizedSource {
)
)
),
- true,
+ TemporalRepresentation.TEMPORAL_VALUES,
false,
loadSampleData("expectations/beehive_vocab_property_temporal_values.jsonld")
)
diff --git a/search-service/src/test/kotlin/com/egm/stellio/search/temporal/util/TemporalQueryUtilsTests.kt b/search-service/src/test/kotlin/com/egm/stellio/search/temporal/util/TemporalQueryUtilsTests.kt
index c701f8413..977aa8ebc 100644
--- a/search-service/src/test/kotlin/com/egm/stellio/search/temporal/util/TemporalQueryUtilsTests.kt
+++ b/search-service/src/test/kotlin/com/egm/stellio/search/temporal/util/TemporalQueryUtilsTests.kt
@@ -1,7 +1,6 @@
package com.egm.stellio.search.temporal.util
import com.egm.stellio.search.common.model.Query
-import com.egm.stellio.search.entity.model.EntitiesQueryFromGet
import com.egm.stellio.search.entity.model.EntitiesQueryFromPost
import com.egm.stellio.search.support.buildDefaultPagination
import com.egm.stellio.search.support.buildDefaultTestTemporalQuery
@@ -10,6 +9,7 @@ import com.egm.stellio.search.temporal.model.TemporalQuery
import com.egm.stellio.shared.config.ApplicationProperties
import com.egm.stellio.shared.model.BadRequestDataException
import com.egm.stellio.shared.model.EntitySelector
+import com.egm.stellio.shared.model.InvalidRequestException
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
@@ -94,6 +94,67 @@ class TemporalQueryUtilsTests {
}
}
+ @Test
+ fun `it shouldn't validate the temporal query if both temporalValues and aggregatedValues are present`() = runTest {
+ val queryParams = gimmeTemporalEntitiesQueryParams()
+ queryParams.replace("options", listOf("aggregatedValues,temporalValues"))
+ queryParams.add("aggrMethods", "sum")
+ val pagination = mockkClass(ApplicationProperties.Pagination::class)
+ every { pagination.limitDefault } returns 30
+ every { pagination.limitMax } returns 100
+ every { pagination.temporalLimit } returns 100
+
+ composeTemporalEntitiesQueryFromGet(
+ pagination,
+ queryParams,
+ APIC_COMPOUND_CONTEXTS,
+ true
+ ).shouldFail {
+ assertInstanceOf(BadRequestDataException::class.java, it)
+ assertEquals("Only one temporal representation can be present", it.message)
+ }
+ }
+
+ @Test
+ fun `it shouldn't validate the temporal query if format contains an invalid value`() = runTest {
+ val queryParams = gimmeTemporalEntitiesQueryParams()
+ queryParams.add("format", "invalid")
+ val pagination = mockkClass(ApplicationProperties.Pagination::class)
+ every { pagination.limitDefault } returns 30
+ every { pagination.limitMax } returns 100
+ every { pagination.temporalLimit } returns 100
+
+ composeTemporalEntitiesQueryFromGet(
+ pagination,
+ queryParams,
+ APIC_COMPOUND_CONTEXTS,
+ true
+ ).shouldFail {
+ assertInstanceOf(InvalidRequestException::class.java, it)
+ assertEquals("'invalid' is not a valid temporal representation", it.message)
+ }
+ }
+
+ @Test
+ fun `it shouldn't validate the temporal query if options contains an invalid value`() = runTest {
+ val queryParams = gimmeTemporalEntitiesQueryParams()
+ queryParams.replace("options", listOf("invalidOptions"))
+ val pagination = mockkClass(ApplicationProperties.Pagination::class)
+ every { pagination.limitDefault } returns 30
+ every { pagination.limitMax } returns 100
+ every { pagination.temporalLimit } returns 100
+
+ composeTemporalEntitiesQueryFromGet(
+ pagination,
+ queryParams,
+ APIC_COMPOUND_CONTEXTS,
+ true
+ ).shouldFail {
+ assertInstanceOf(InvalidRequestException::class.java, it)
+ assertEquals("'invalidOptions' is not a valid options value", it.message)
+ }
+ }
+
@Test
fun `it should parse a valid temporal query`() = runTest {
val queryParams = gimmeTemporalEntitiesQueryParams()
@@ -109,11 +170,11 @@ class TemporalQueryUtilsTests {
assertEquals(
setOf("urn:ngsi-ld:BeeHive:TESTC".toUri(), "urn:ngsi-ld:BeeHive:TESTB".toUri()),
- (temporalEntitiesQuery.entitiesQuery as EntitiesQueryFromGet).ids
+ temporalEntitiesQuery.entitiesQuery.ids
)
assertEquals(
"$BEEHIVE_TYPE,$APIARY_TYPE",
- (temporalEntitiesQuery.entitiesQuery as EntitiesQueryFromGet).typeSelection
+ temporalEntitiesQuery.entitiesQuery.typeSelection
)
assertEquals(setOf(INCOMING_PROPERTY, OUTGOING_PROPERTY), temporalEntitiesQuery.entitiesQuery.attrs)
assertEquals(
@@ -124,7 +185,7 @@ class TemporalQueryUtilsTests {
),
temporalEntitiesQuery.temporalQuery
)
- assertTrue(temporalEntitiesQuery.withTemporalValues)
+ assertTrue(temporalEntitiesQuery.temporalRepresentation == TemporalRepresentation.TEMPORAL_VALUES)
assertFalse(temporalEntitiesQuery.withAudit)
assertEquals(10, temporalEntitiesQuery.entitiesQuery.paginationQuery.limit)
assertEquals(2, temporalEntitiesQuery.entitiesQuery.paginationQuery.offset)
@@ -228,7 +289,12 @@ class TemporalQueryUtilsTests {
queryParams.add("timeAt", "2019-10-17T07:31:39Z")
queryParams.add("lastN", "2")
- val temporalQuery = buildTemporalQuery(queryParams, buildDefaultPagination()).shouldSucceedAndResult()
+ val temporalQuery = buildTemporalQuery(
+ queryParams,
+ buildDefaultPagination(),
+ false,
+ TemporalRepresentation.NORMALIZED
+ ).shouldSucceedAndResult()
assertEquals(2, temporalQuery.instanceLimit)
assertEquals(2, temporalQuery.lastN)
@@ -241,7 +307,12 @@ class TemporalQueryUtilsTests {
queryParams.add("timeAt", "2019-10-17T07:31:39Z")
queryParams.add("lastN", "A")
val pagination = buildDefaultPagination()
- val temporalQuery = buildTemporalQuery(queryParams, pagination).shouldSucceedAndResult()
+ val temporalQuery = buildTemporalQuery(
+ queryParams,
+ pagination,
+ false,
+ TemporalRepresentation.NORMALIZED
+ ).shouldSucceedAndResult()
assertEquals(pagination.temporalLimit, temporalQuery.instanceLimit)
assertNull(temporalQuery.lastN)
@@ -255,7 +326,12 @@ class TemporalQueryUtilsTests {
queryParams.add("lastN", "-2")
val pagination = buildDefaultPagination()
- val temporalQuery = buildTemporalQuery(queryParams, pagination).shouldSucceedAndResult()
+ val temporalQuery = buildTemporalQuery(
+ queryParams,
+ pagination,
+ false,
+ TemporalRepresentation.NORMALIZED
+ ).shouldSucceedAndResult()
assertEquals(pagination.temporalLimit, temporalQuery.instanceLimit)
assertNull(temporalQuery.lastN)
@@ -265,7 +341,12 @@ class TemporalQueryUtilsTests {
fun `it should treat time and timerel properties as optional in a temporal query`() = runTest {
val queryParams = LinkedMultiValueMap()
- val temporalQuery = buildTemporalQuery(queryParams, buildDefaultPagination()).shouldSucceedAndResult()
+ val temporalQuery = buildTemporalQuery(
+ queryParams,
+ buildDefaultPagination(),
+ false,
+ TemporalRepresentation.NORMALIZED
+ ).shouldSucceedAndResult()
assertNull(temporalQuery.timeAt)
assertNull(temporalQuery.timerel)
@@ -276,7 +357,12 @@ class TemporalQueryUtilsTests {
val queryParams = LinkedMultiValueMap()
queryParams.add("timeproperty", "createdAt")
- val temporalQuery = buildTemporalQuery(queryParams, buildDefaultPagination()).shouldSucceedAndResult()
+ val temporalQuery = buildTemporalQuery(
+ queryParams,
+ buildDefaultPagination(),
+ false,
+ TemporalRepresentation.NORMALIZED
+ ).shouldSucceedAndResult()
assertEquals(AttributeInstance.TemporalProperty.CREATED_AT, temporalQuery.timeproperty)
}
@@ -285,7 +371,12 @@ class TemporalQueryUtilsTests {
fun `it should set timeproperty to observedAt if no value is provided in query parameters`() = runTest {
val queryParams = LinkedMultiValueMap()
- val temporalQuery = buildTemporalQuery(queryParams, buildDefaultPagination()).shouldSucceedAndResult()
+ val temporalQuery = buildTemporalQuery(
+ queryParams,
+ buildDefaultPagination(),
+ false,
+ TemporalRepresentation.NORMALIZED
+ ).shouldSucceedAndResult()
assertEquals(AttributeInstance.TemporalProperty.OBSERVED_AT, temporalQuery.timeproperty)
}
diff --git a/search-service/src/test/kotlin/com/egm/stellio/search/temporal/web/TemporalEntityHandlerTests.kt b/search-service/src/test/kotlin/com/egm/stellio/search/temporal/web/TemporalEntityHandlerTests.kt
index 73d2f7541..ed1d3c901 100644
--- a/search-service/src/test/kotlin/com/egm/stellio/search/temporal/web/TemporalEntityHandlerTests.kt
+++ b/search-service/src/test/kotlin/com/egm/stellio/search/temporal/web/TemporalEntityHandlerTests.kt
@@ -8,6 +8,7 @@ import com.egm.stellio.search.entity.model.EntitiesQueryFromGet
import com.egm.stellio.search.support.buildDefaultTestTemporalQuery
import com.egm.stellio.search.temporal.model.TemporalQuery
import com.egm.stellio.search.temporal.service.TemporalService.CreateOrUpdateResult
+import com.egm.stellio.search.temporal.util.TemporalRepresentation
import com.egm.stellio.shared.config.ApplicationProperties
import com.egm.stellio.shared.model.AccessDeniedException
import com.egm.stellio.shared.model.BadRequestDataException
@@ -425,6 +426,69 @@ class TemporalEntityHandlerTests : TemporalEntityHandlerTestCommon() {
)
}
+ @Test
+ fun `it should raise a 400 if temporalValues and aggregatedValues exist in options query param`() {
+ webClient.get()
+ .uri(
+ "/ngsi-ld/v1/temporal/entities/urn:ngsi-ld:Entity:01?" +
+ "timerel=after&timeAt=2020-01-31T07:31:39Z&aggrPeriodDuration=P1D&" +
+ "options=aggregatedValues,temporalValues"
+ )
+ .exchange()
+ .expectStatus().isBadRequest
+ .expectBody().json(
+ """
+ {
+ "type": "https://uri.etsi.org/ngsi-ld/errors/BadRequestData",
+ "title": "Only one temporal representation can be present",
+ "detail": "$DEFAULT_DETAIL"
+ }
+ """
+ )
+ }
+
+ @Test
+ fun `it should raise a 400 if format query param has an invalid value`() {
+ webClient.get()
+ .uri(
+ "/ngsi-ld/v1/temporal/entities/urn:ngsi-ld:Entity:01?" +
+ "timerel=after&timeAt=2020-01-31T07:31:39Z&" +
+ "format=invalid"
+ )
+ .exchange()
+ .expectStatus().isBadRequest
+ .expectBody().json(
+ """
+ {
+ "type": "https://uri.etsi.org/ngsi-ld/errors/InvalidRequest",
+ "title": "'invalid' is not a valid temporal representation",
+ "detail": "$DEFAULT_DETAIL"
+ }
+ """
+ )
+ }
+
+ @Test
+ fun `it should raise a 400 if options query param has an invalid value`() {
+ webClient.get()
+ .uri(
+ "/ngsi-ld/v1/temporal/entities/urn:ngsi-ld:Entity:01?" +
+ "timerel=after&timeAt=2020-01-31T07:31:39Z&" +
+ "options=invalidOptions"
+ )
+ .exchange()
+ .expectStatus().isBadRequest
+ .expectBody().json(
+ """
+ {
+ "type": "https://uri.etsi.org/ngsi-ld/errors/InvalidRequest",
+ "title": "'invalidOptions' is not a valid options value",
+ "detail": "$DEFAULT_DETAIL"
+ }
+ """
+ )
+ }
+
@Test
fun `it should return a 404 if temporal entity attribute does not exist`() {
coEvery {
@@ -473,7 +537,7 @@ class TemporalEntityHandlerTests : TemporalEntityHandlerTestCommon() {
temporalEntitiesQuery.temporalQuery.timeAt!!.isEqual(
ZonedDateTime.parse("2019-10-17T07:31:39Z")
) &&
- !temporalEntitiesQuery.withTemporalValues &&
+ temporalEntitiesQuery.temporalRepresentation == TemporalRepresentation.NORMALIZED &&
!temporalEntitiesQuery.withAudit
},
eq(sub.value)
@@ -604,7 +668,7 @@ class TemporalEntityHandlerTests : TemporalEntityHandlerTestCommon() {
entitiesQueryFromGet.ids.isEmpty() &&
entitiesQueryFromGet.typeSelection == BEEHIVE_TYPE &&
temporalEntitiesQuery.temporalQuery == temporalQuery &&
- !temporalEntitiesQuery.withTemporalValues
+ temporalEntitiesQuery.temporalRepresentation == TemporalRepresentation.NORMALIZED
},
any()
)
diff --git a/search-service/src/test/kotlin/com/egm/stellio/search/temporal/web/TemporalEntityOperationsHandlerTests.kt b/search-service/src/test/kotlin/com/egm/stellio/search/temporal/web/TemporalEntityOperationsHandlerTests.kt
index 7f4ff8e9b..5f04f17d2 100644
--- a/search-service/src/test/kotlin/com/egm/stellio/search/temporal/web/TemporalEntityOperationsHandlerTests.kt
+++ b/search-service/src/test/kotlin/com/egm/stellio/search/temporal/web/TemporalEntityOperationsHandlerTests.kt
@@ -6,6 +6,7 @@ import com.egm.stellio.search.entity.model.EntitiesQueryFromPost
import com.egm.stellio.search.support.buildDefaultTestTemporalQuery
import com.egm.stellio.search.temporal.model.TemporalQuery
import com.egm.stellio.search.temporal.service.TemporalQueryService
+import com.egm.stellio.search.temporal.util.TemporalRepresentation
import com.egm.stellio.shared.config.ApplicationProperties
import com.egm.stellio.shared.model.DEFAULT_DETAIL
import com.egm.stellio.shared.util.APIARY_COMPACT_TYPE
@@ -101,7 +102,7 @@ class TemporalEntityOperationsHandlerTests {
entitiesQueryFromPost.entitySelectors!![0].typeSelection == "$BEEHIVE_TYPE,$APIARY_TYPE" &&
temporalEntitiesQuery.entitiesQuery.attrs == setOf(INCOMING_PROPERTY, OUTGOING_PROPERTY) &&
temporalEntitiesQuery.temporalQuery == temporalQuery &&
- temporalEntitiesQuery.withTemporalValues
+ temporalEntitiesQuery.temporalRepresentation == TemporalRepresentation.TEMPORAL_VALUES
},
any()
)
@@ -155,7 +156,7 @@ class TemporalEntityOperationsHandlerTests {
temporalEntitiesQuery.entitiesQuery.attrs == setOf(INCOMING_PROPERTY, OUTGOING_PROPERTY) &&
temporalEntitiesQuery.entitiesQuery.paginationQuery.count &&
temporalEntitiesQuery.temporalQuery == temporalQuery &&
- temporalEntitiesQuery.withTemporalValues
+ temporalEntitiesQuery.temporalRepresentation == TemporalRepresentation.TEMPORAL_VALUES
},
any()
)
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 ba2dbcfca..8ae1a3363 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,5 +1,10 @@
package com.egm.stellio.shared.model
+import arrow.core.Either
+import arrow.core.left
+import arrow.core.raise.either
+import arrow.core.right
+import com.egm.stellio.shared.queryparameter.FormatValue
import com.egm.stellio.shared.queryparameter.OptionsValue
import com.egm.stellio.shared.queryparameter.QueryParameter
import com.egm.stellio.shared.util.GEO_JSON_MEDIA_TYPE
@@ -26,11 +31,26 @@ data class NgsiLdDataRepresentation(
fun parseRepresentations(
queryParams: MultiValueMap,
acceptMediaType: MediaType
- ): NgsiLdDataRepresentation {
+ ): Either = either {
val optionsParam = queryParams.getOrDefault(QueryParameter.OPTIONS.key, emptyList())
+ .flatMap { it.split(",") }
+ val formatParam = queryParams.getFirst(QueryParameter.FORMAT.key)
+ if (formatParam != null && FormatValue.fromString(formatParam) == null) {
+ return InvalidRequestException("'$formatParam' is not a valid format value").left()
+ }
+
+ optionsParam.forEach { option ->
+ OptionsValue.fromString(option).bind()
+ }
+ val attributeRepresentation = when {
+ formatParam == FormatValue.KEY_VALUES.value ||
+ formatParam == FormatValue.SIMPLIFIED.value -> AttributeRepresentation.SIMPLIFIED
+ formatParam == FormatValue.NORMALIZED.value -> AttributeRepresentation.NORMALIZED
+ optionsParam.contains(FormatValue.KEY_VALUES.value) ||
+ optionsParam.contains(FormatValue.SIMPLIFIED.value) -> AttributeRepresentation.SIMPLIFIED
+ else -> AttributeRepresentation.NORMALIZED
+ }
val includeSysAttrs = optionsParam.contains(OptionsValue.SYS_ATTRS.value)
- val attributeRepresentation = optionsParam.contains(OptionsValue.KEY_VALUES.value)
- .let { if (it) AttributeRepresentation.SIMPLIFIED else AttributeRepresentation.NORMALIZED }
val languageFilter = queryParams.getFirst(QueryParameter.LANG.key)
val entityRepresentation = EntityRepresentation.forMediaType(acceptMediaType)
val geometryProperty =
@@ -46,7 +66,7 @@ data class NgsiLdDataRepresentation(
languageFilter,
geometryProperty,
timeproperty
- )
+ ).right()
}
}
}
diff --git a/shared/src/main/kotlin/com/egm/stellio/shared/queryparameter/FormatValue.kt b/shared/src/main/kotlin/com/egm/stellio/shared/queryparameter/FormatValue.kt
new file mode 100644
index 000000000..0565a3c61
--- /dev/null
+++ b/shared/src/main/kotlin/com/egm/stellio/shared/queryparameter/FormatValue.kt
@@ -0,0 +1,13 @@
+package com.egm.stellio.shared.queryparameter
+
+enum class FormatValue(val value: String) {
+ KEY_VALUES("keyValues"),
+ SIMPLIFIED("simplified"),
+ NORMALIZED("normalized"),
+ TEMPORAL_VALUES("temporalValues"),
+ AGGREGATED_VALUES("aggregatedValues");
+ companion object {
+ fun fromString(key: String): FormatValue? =
+ FormatValue.entries.find { it.value == key }
+ }
+}
diff --git a/shared/src/main/kotlin/com/egm/stellio/shared/queryparameter/OptionsValue.kt b/shared/src/main/kotlin/com/egm/stellio/shared/queryparameter/OptionsValue.kt
index 05072d108..25fa030b7 100644
--- a/shared/src/main/kotlin/com/egm/stellio/shared/queryparameter/OptionsValue.kt
+++ b/shared/src/main/kotlin/com/egm/stellio/shared/queryparameter/OptionsValue.kt
@@ -1,9 +1,26 @@
package com.egm.stellio.shared.queryparameter
+import arrow.core.Either
+import arrow.core.left
+import arrow.core.raise.either
+import com.egm.stellio.shared.model.APIException
+import com.egm.stellio.shared.model.InvalidRequestException
+
enum class OptionsValue(val value: String) {
SYS_ATTRS("sysAttrs"),
- KEY_VALUES("keyValues"),
NO_OVERWRITE("noOverwrite"),
UPDATE_MODE("update"),
- REPLACE_MODE("replace")
+ REPLACE_MODE("replace"),
+ TEMPORAL_VALUES("temporalValues"),
+ AGGREGATED_VALUES("aggregatedValues"),
+ AUDIT("audit"),
+ NORMALIZED("normalized"),
+ KEY_VALUES("keyValues"),
+ SIMPLIFIED("simplified");
+ companion object {
+ fun fromString(key: String): Either = either {
+ OptionsValue.entries.find { it.value == key }
+ ?: return InvalidRequestException("'$key' is not a valid options value").left()
+ }
+ }
}
diff --git a/shared/src/main/kotlin/com/egm/stellio/shared/queryparameter/QueryParameter.kt b/shared/src/main/kotlin/com/egm/stellio/shared/queryparameter/QueryParameter.kt
index 3762001e6..f8ac16d8e 100644
--- a/shared/src/main/kotlin/com/egm/stellio/shared/queryparameter/QueryParameter.kt
+++ b/shared/src/main/kotlin/com/egm/stellio/shared/queryparameter/QueryParameter.kt
@@ -18,6 +18,7 @@ enum class QueryParameter(
JOIN("join"),
JOIN_LEVEL("joinLevel"),
OPTIONS("options"),
+ FORMAT("format"),
OBSERVED_AT("observedAt"),
// geoQuery
@@ -46,7 +47,6 @@ enum class QueryParameter(
DELETE_ALL("deleteAll"),
// not implemented yet
- FORMAT("format"),
PICK("pick"),
OMIT("omit"),
EXPAND_VALUES("expandValues"),
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 8f8c8176d..37c48968f 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,6 +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.queryparameter.OptionsValue
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
@@ -168,17 +169,19 @@ internal fun canExpandJsonLdKeyFromCore(contexts: List): Boolean {
return expandedType == NGSILD_DATASET_ID_PROPERTY
}
-enum class OptionsParamValue(val value: String) {
- TEMPORAL_VALUES("temporalValues"),
- AUDIT("audit"),
- AGGREGATED_VALUES("aggregatedValues")
-}
-
-fun hasValueInOptionsParam(options: Optional, optionValue: OptionsParamValue): Boolean =
- options
+fun hasValueInOptionsParam(
+ queryParam: Optional,
+ optionsParamValue: OptionsValue
+): Either = either {
+ val optionsValue = queryParam
.map { it.split(",") }
- .filter { it.any { option -> option == optionValue.value } }
- .isPresent
+ .orElse(emptyList())
+
+ optionsValue.forEach { option ->
+ OptionsValue.fromString(option).bind()
+ }
+ optionsValue.any { option -> option == optionsParamValue.value }
+}
fun parseQueryParameter(queryParam: String?): Set =
queryParam
diff --git a/shared/src/test/kotlin/com/egm/stellio/shared/model/NgsiLdDataRepresentationTests.kt b/shared/src/test/kotlin/com/egm/stellio/shared/model/NgsiLdDataRepresentationTests.kt
new file mode 100644
index 000000000..bb29c9ddd
--- /dev/null
+++ b/shared/src/test/kotlin/com/egm/stellio/shared/model/NgsiLdDataRepresentationTests.kt
@@ -0,0 +1,110 @@
+package com.egm.stellio.shared.model
+
+import com.egm.stellio.shared.model.NgsiLdDataRepresentation.Companion.parseRepresentations
+import com.egm.stellio.shared.util.shouldFail
+import com.egm.stellio.shared.util.shouldSucceedAndResult
+import org.junit.jupiter.api.Assertions.assertEquals
+import org.junit.jupiter.api.Assertions.assertInstanceOf
+import org.junit.jupiter.api.Assertions.assertTrue
+import org.junit.jupiter.api.Test
+import org.springframework.http.MediaType
+import org.springframework.test.context.ActiveProfiles
+import org.springframework.util.LinkedMultiValueMap
+
+@ActiveProfiles("test")
+class NgsiLdDataRepresentationTests {
+
+ @Test
+ fun `it should return the attribute representation in the format query param when options exist`() {
+ val queryParams = LinkedMultiValueMap()
+ queryParams.add("timerel", "after")
+ queryParams.add("timeAt", "2025-01-03T07:45:24Z")
+ queryParams.add("options", "simplified")
+ queryParams.add("format", "normalized")
+
+ val parsedRepresentation = parseRepresentations(queryParams, MediaType.APPLICATION_JSON)
+ .shouldSucceedAndResult()
+
+ assertEquals(AttributeRepresentation.NORMALIZED, parsedRepresentation.attributeRepresentation)
+ }
+
+ @Test
+ fun `it should return the attribute representation in the options query param when no format is given`() {
+ val queryParams = LinkedMultiValueMap()
+ queryParams.add("timerel", "after")
+ queryParams.add("timeAt", "2025-01-03T07:45:24Z")
+ queryParams.add("options", "simplified")
+
+ val parsedRepresentation = parseRepresentations(queryParams, MediaType.APPLICATION_JSON)
+ .shouldSucceedAndResult()
+
+ assertEquals(AttributeRepresentation.SIMPLIFIED, parsedRepresentation.attributeRepresentation)
+ }
+
+ @Test
+ fun `it should correctly parse the options query param when it contains more than one value`() {
+ val queryParams = LinkedMultiValueMap()
+ queryParams.add("timerel", "after")
+ queryParams.add("timeAt", "2025-01-03T07:45:24Z")
+ queryParams.add("options", "simplified")
+ queryParams.add("options", "sysAttrs,audit")
+ val parsedRepresentation = parseRepresentations(queryParams, MediaType.APPLICATION_JSON)
+ .shouldSucceedAndResult()
+
+ assertEquals(AttributeRepresentation.SIMPLIFIED, parsedRepresentation.attributeRepresentation)
+ assertTrue(parsedRepresentation.includeSysAttrs)
+ }
+
+ @Test
+ fun `it should return the attribute representation in the first format query param`() {
+ val queryParams = LinkedMultiValueMap()
+ queryParams.add("timerel", "after")
+ queryParams.add("timeAt", "2025-01-03T07:45:24Z")
+ queryParams.add("format", "simplified")
+ queryParams.add("format", "normalized")
+
+ val parsedRepresentation = parseRepresentations(queryParams, MediaType.APPLICATION_JSON)
+ .shouldSucceedAndResult()
+
+ assertEquals(AttributeRepresentation.SIMPLIFIED, parsedRepresentation.attributeRepresentation)
+ }
+
+ @Test
+ fun `it should return an exception if format query param is invalid`() {
+ val queryParams = LinkedMultiValueMap()
+ queryParams.add("timerel", "after")
+ queryParams.add("timeAt", "2025-01-03T07:45:24Z")
+ queryParams.add("format", "invalid")
+
+ parseRepresentations(queryParams, MediaType.APPLICATION_JSON).shouldFail {
+ assertInstanceOf(InvalidRequestException::class.java, it)
+ assertEquals("'invalid' is not a valid format value", it.message)
+ }
+ }
+
+ @Test
+ fun `it should return an exception if options query param is invalid`() {
+ val queryParams = LinkedMultiValueMap()
+ queryParams.add("timerel", "after")
+ queryParams.add("timeAt", "2025-01-03T07:45:24Z")
+ queryParams.add("options", "invalidOptions")
+
+ parseRepresentations(queryParams, MediaType.APPLICATION_JSON).shouldFail {
+ assertInstanceOf(InvalidRequestException::class.java, it)
+ assertEquals("'invalidOptions' is not a valid options value", it.message)
+ }
+ }
+
+ @Test
+ fun `it should return an exception if at least one value in options query param is invalid`() {
+ val queryParams = LinkedMultiValueMap()
+ queryParams.add("timerel", "after")
+ queryParams.add("timeAt", "2025-01-03T07:45:24Z")
+ queryParams.add("options", "simplified,invalidOptions")
+
+ parseRepresentations(queryParams, MediaType.APPLICATION_JSON).shouldFail {
+ assertInstanceOf(InvalidRequestException::class.java, it)
+ assertEquals("'invalidOptions' is not a valid options value", it.message)
+ }
+ }
+}
diff --git a/shared/src/test/kotlin/com/egm/stellio/shared/util/ApiUtilsTests.kt b/shared/src/test/kotlin/com/egm/stellio/shared/util/ApiUtilsTests.kt
index 00a9da49a..fce6ec97c 100644
--- a/shared/src/test/kotlin/com/egm/stellio/shared/util/ApiUtilsTests.kt
+++ b/shared/src/test/kotlin/com/egm/stellio/shared/util/ApiUtilsTests.kt
@@ -2,7 +2,8 @@ package com.egm.stellio.shared.util
import com.egm.stellio.shared.config.ApplicationProperties
import com.egm.stellio.shared.model.BadRequestDataException
-import com.egm.stellio.shared.util.OptionsParamValue.TEMPORAL_VALUES
+import com.egm.stellio.shared.model.InvalidRequestException
+import com.egm.stellio.shared.queryparameter.OptionsValue.TEMPORAL_VALUES
import com.egm.stellio.shared.web.CustomWebFilter
import io.mockk.every
import io.mockk.mockk
@@ -33,27 +34,38 @@ class ApiUtilsTests {
@Test
fun `it should not find a value if there is no options query param`() {
- assertFalse(hasValueInOptionsParam(Optional.empty(), TEMPORAL_VALUES))
+ val result = hasValueInOptionsParam(Optional.empty(), TEMPORAL_VALUES).shouldSucceedAndResult()
+ assertFalse(result)
}
@Test
- fun `it should not find a value if it is not in a single value options query param`() {
- assertFalse(hasValueInOptionsParam(Optional.of("one"), TEMPORAL_VALUES))
+ fun `it should return an exception if it is given an invalid options query param`() {
+ hasValueInOptionsParam(Optional.of("one"), TEMPORAL_VALUES).shouldFail {
+ assertInstanceOf(InvalidRequestException::class.java, it)
+ assertEquals("'one' is not a valid options value", it.message)
+ }
}
@Test
- fun `it should not find a value if it is not in a multi value options query param`() {
- assertFalse(hasValueInOptionsParam(Optional.of("one,two"), TEMPORAL_VALUES))
+ fun `it should return an exception if it is given an invalid multi value options query param`() {
+ hasValueInOptionsParam(Optional.of("one,two"), TEMPORAL_VALUES).shouldFail {
+ assertInstanceOf(InvalidRequestException::class.java, it)
+ assertEquals("'one' is not a valid options value", it.message)
+ }
}
@Test
fun `it should find a value if it is in a single value options query param`() {
- assertTrue(hasValueInOptionsParam(Optional.of("temporalValues"), TEMPORAL_VALUES))
+ val result = hasValueInOptionsParam(Optional.of("temporalValues"), TEMPORAL_VALUES).shouldSucceedAndResult()
+ assertTrue(result)
}
@Test
- fun `it should find a value if it is in a multi value options query param`() {
- assertTrue(hasValueInOptionsParam(Optional.of("one,temporalValues"), TEMPORAL_VALUES))
+ fun `it should return an exception if it is given at least one invalid value in options query param`() {
+ hasValueInOptionsParam(Optional.of("one,temporalValues"), TEMPORAL_VALUES).shouldFail {
+ assertInstanceOf(InvalidRequestException::class.java, it)
+ assertEquals("'one' is not a valid options value", it.message)
+ }
}
@Test