Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add support for the format parameter #1299

Draft
wants to merge 10 commits into
base: develop
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, String>
): ResponseEntity<*> = either {
Expand Down Expand Up @@ -264,7 +264,7 @@ class EntityHandler(
mergedEntities ?: emptyList()
}

val ngsiLdDataRepresentation = parseRepresentations(queryParams, mediaType)
val ngsiLdDataRepresentation = parseRepresentations(queryParams, mediaType).bind()
buildQueryResponse(
mergedEntities.toFinalRepresentation(ngsiLdDataRepresentation),
maxCount,
Expand All @@ -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<String, String>
): ResponseEntity<*> = either {
Expand Down Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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)
Expand Down Expand Up @@ -215,7 +216,7 @@ class ScopeService(
row: Map<String, Any>,
temporalEntitiesQuery: TemporalEntitiesQuery
): ScopeInstanceResult =
if (temporalEntitiesQuery.withAggregatedValues) {
if (temporalEntitiesQuery.temporalRepresentation == TemporalRepresentation.AGGREGATED_VALUES) {
val startDateTime = toZonedDateTime(row["start"])
val endDateTime =
if (!temporalEntitiesQuery.isAggregatedWithDefinedDuration())
Expand All @@ -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"]),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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<String>())
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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,19 @@ 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

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"

Expand All @@ -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)
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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())
Expand Down Expand Up @@ -319,7 +320,7 @@ class AttributeInstanceService(
row: Map<String, Any>,
temporalEntitiesQuery: TemporalEntitiesQuery
): AttributeInstanceResult =
if (temporalEntitiesQuery.withAggregatedValues) {
if (temporalEntitiesQuery.temporalRepresentation == TemporalRepresentation.AGGREGATED_VALUES) {
val startDateTime = toZonedDateTime(row["start"])
val endDateTime =
if (!temporalEntitiesQuery.isAggregatedWithDefinedDuration())
Expand All @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<ZonedDateTime, ZonedDateTime>
Expand Down Expand Up @@ -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()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -93,8 +94,8 @@ 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)
null
if (!(temporalEntitiesQuery.temporalRepresentation == TemporalRepresentation.AGGREGATED_VALUES))
return null
else if (temporalQuery.timeAt != null)
temporalQuery.timeAt
else {
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,10 @@ object TemporalEntityBuilder {
attributeAndResultsMap: AttributesWithInstances,
temporalEntitiesQuery: TemporalEntitiesQuery,
): Map<String, Any> =
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!!
Expand Down
Loading
Loading