diff --git a/shared/config/detekt/baseline.xml b/shared/config/detekt/baseline.xml index 7676e8f70..c3deb4468 100644 --- a/shared/config/detekt/baseline.xml +++ b/shared/config/detekt/baseline.xml @@ -3,7 +3,7 @@ CyclomaticComplexMethod:ExceptionHandler.kt$ExceptionHandler$@ExceptionHandler fun transformErrorResponse(throwable: Throwable): ResponseEntity<ProblemDetail> - LongMethod:ExpandedEntityTests.kt$ExpandedEntityTests$@Test fun `it should return simplified GeoJSON entities`() + LongMethod:CompactedEntityTests.kt$CompactedEntityTests$@Test fun `it should return the GeoJSON representation of simplified entities`() LongMethod:QueryUtils.kt$private fun transformQQueryToSqlJsonPath( mainAttributePath: List<ExpandedTerm>, trailingAttributePath: List<ExpandedTerm>, operator: String, value: String ) LongParameterList:ApiResponses.kt$( body: String, count: Int, resourceUrl: String, paginationQuery: PaginationQuery, requestParams: MultiValueMap<String, String>, mediaType: MediaType, contexts: List<String> ) LongParameterList:ApiResponses.kt$( entities: Any, count: Int, resourceUrl: String, paginationQuery: PaginationQuery, requestParams: MultiValueMap<String, String>, mediaType: MediaType, contexts: List<String> ) diff --git a/shared/src/main/kotlin/com/egm/stellio/shared/model/CompactedEntity.kt b/shared/src/main/kotlin/com/egm/stellio/shared/model/CompactedEntity.kt index 4ff3bdaae..781701e73 100644 --- a/shared/src/main/kotlin/com/egm/stellio/shared/model/CompactedEntity.kt +++ b/shared/src/main/kotlin/com/egm/stellio/shared/model/CompactedEntity.kt @@ -7,10 +7,9 @@ import com.egm.stellio.shared.util.JsonLdUtils.JSONLD_JSON_TERM import com.egm.stellio.shared.util.JsonLdUtils.JSONLD_OBJECT import com.egm.stellio.shared.util.JsonLdUtils.JSONLD_TYPE_TERM import com.egm.stellio.shared.util.JsonLdUtils.JSONLD_VALUE_TERM -import com.egm.stellio.shared.util.JsonLdUtils.NGSILD_GEOPROPERTY_TERM -import com.egm.stellio.shared.util.JsonLdUtils.NGSILD_JSONPROPERTY_TERM -import com.egm.stellio.shared.util.JsonLdUtils.NGSILD_PROPERTY_TERM -import com.egm.stellio.shared.util.JsonLdUtils.NGSILD_RELATIONSHIP_TERM +import com.egm.stellio.shared.util.JsonLdUtils.NGSILD_DATASET_ID_TERM +import com.egm.stellio.shared.util.JsonLdUtils.NGSILD_DATASET_TERM +import com.egm.stellio.shared.util.JsonLdUtils.NGSILD_NONE_TERM import com.egm.stellio.shared.util.JsonLdUtils.NGSILD_SYSATTRS_TERMS typealias CompactedEntity = Map @@ -23,24 +22,36 @@ private fun simplifyRepresentation(value: Any): Any = // an attribute with a single instance is Map<*, *> -> simplifyValue(value as Map) // an attribute with multiple instances - is List<*> -> value.map { - when (it) { - is Map<*, *> -> simplifyValue(it as Map) + is List<*> -> { + when (value.first()) { + is Map<*, *> -> simplifyMultiInstanceAttribute(value as List>) // we keep @context value as it is (List) - else -> it + else -> value } } // keep id, type and other non-reified properties as they are (typically string or list) else -> value } -private fun simplifyValue(value: Map): Any = - when (value[JSONLD_TYPE_TERM]) { - NGSILD_PROPERTY_TERM, NGSILD_GEOPROPERTY_TERM -> value.getOrDefault(JSONLD_VALUE_TERM, value) - NGSILD_JSONPROPERTY_TERM -> mapOf(JSONLD_JSON_TERM to value.getOrDefault(JSONLD_JSON_TERM, value)) - NGSILD_RELATIONSHIP_TERM -> value.getOrDefault(JSONLD_OBJECT, value) - else -> value +private fun simplifyMultiInstanceAttribute(value: List>): Map> { + val datasetIds = value.map { + val datasetId = (it[NGSILD_DATASET_ID_TERM] as? String) ?: NGSILD_NONE_TERM + val datasetValue: Any = simplifyValue(it) + Pair(datasetId, datasetValue) + } + return mapOf(NGSILD_DATASET_TERM to datasetIds.toMap()) +} + +private fun simplifyValue(value: Map): Any { + val attributeCompactedType = AttributeCompactedType.forKey(value[JSONLD_TYPE_TERM] as String)!! + return when (attributeCompactedType) { + AttributeCompactedType.PROPERTY, AttributeCompactedType.GEOPROPERTY -> { + value.getOrDefault(JSONLD_VALUE_TERM, value) + } + AttributeCompactedType.JSONPROPERTY -> mapOf(JSONLD_JSON_TERM to value.getOrDefault(JSONLD_JSON_TERM, value)) + AttributeCompactedType.RELATIONSHIP -> value.getOrDefault(JSONLD_OBJECT, value) } +} fun CompactedEntity.toGeoJson(geometryProperty: String): Map { val geometryAttributeContent = this[geometryProperty] as? Map @@ -120,3 +131,15 @@ fun List.toFinalRepresentation( ) } else it } + +enum class AttributeCompactedType(val key: String) { + PROPERTY("Property"), + RELATIONSHIP("Relationship"), + GEOPROPERTY("GeoProperty"), + JSONPROPERTY("JsonProperty"); + + companion object { + fun forKey(key: String): AttributeCompactedType? = + entries.find { it.key == key } + } +} diff --git a/shared/src/main/kotlin/com/egm/stellio/shared/util/JsonLdUtils.kt b/shared/src/main/kotlin/com/egm/stellio/shared/util/JsonLdUtils.kt index 87e953369..b4a9ca95a 100644 --- a/shared/src/main/kotlin/com/egm/stellio/shared/util/JsonLdUtils.kt +++ b/shared/src/main/kotlin/com/egm/stellio/shared/util/JsonLdUtils.kt @@ -64,7 +64,8 @@ object JsonLdUtils { const val JSONLD_CONTEXT = "@context" const val NGSILD_SCOPE_TERM = "scope" const val NGSILD_SCOPE_PROPERTY = "https://uri.etsi.org/ngsi-ld/$NGSILD_SCOPE_TERM" - + const val NGSILD_NONE_TERM = "@none" + const val NGSILD_DATASET_TERM = "dataset" val JSONLD_EXPANDED_ENTITY_SPECIFIC_MEMBERS = setOf(JSONLD_TYPE, NGSILD_SCOPE_PROPERTY) // List of members that are part of a core entity base definition (i.e., without attributes) @@ -90,6 +91,8 @@ object JsonLdUtils { val NGSILD_GEO_PROPERTIES_TERMS = setOf(NGSILD_LOCATION_TERM, NGSILD_OBSERVATION_SPACE_TERM, NGSILD_OPERATION_SPACE_TERM) const val NGSILD_DATASET_ID_PROPERTY = "https://uri.etsi.org/ngsi-ld/datasetId" + const val NGSILD_DATASET_ID_TERM = "datasetId" + const val NGSILD_INSTANCE_ID_PROPERTY = "https://uri.etsi.org/ngsi-ld/instanceId" const val NGSILD_SUBSCRIPTION_TERM = "Subscription" diff --git a/shared/src/test/kotlin/com/egm/stellio/shared/model/CompactedEntityTests.kt b/shared/src/test/kotlin/com/egm/stellio/shared/model/CompactedEntityTests.kt index b49c61199..245bfebd4 100644 --- a/shared/src/test/kotlin/com/egm/stellio/shared/model/CompactedEntityTests.kt +++ b/shared/src/test/kotlin/com/egm/stellio/shared/model/CompactedEntityTests.kt @@ -1,12 +1,147 @@ package com.egm.stellio.shared.model +import com.egm.stellio.shared.util.JsonLdUtils import com.egm.stellio.shared.util.JsonUtils.deserializeAsMap import com.egm.stellio.shared.util.JsonUtils.serializeObject import com.egm.stellio.shared.util.assertJsonPayloadsAreEqual +import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test +import org.springframework.http.MediaType class CompactedEntityTests { + private val normalizedEntity = + """ + { + "id": "urn:ngsi-ld:Vehicle:A4567", + "type": "Vehicle", + "brandName": { + "type": "Property", + "value": "Mercedes" + }, + "isParked": { + "type": "Relationship", + "object": "urn:ngsi-ld:OffStreetParking:Downtown1", + "observedAt": "2017-07-29T12:00:04Z", + "providedBy": { + "type": "Relationship", + "object": "urn:ngsi-ld:Person:Bob" + } + }, + "location": { + "type": "GeoProperty", + "value": { + "type": "Point", + "coordinates": [ + 24.30623, + 60.07966 + ] + } + }, + "@context": [ + "https://example.org/ngsi-ld/latest/commonTerms.jsonld", + "https://example.org/ngsi-ld/latest/vehicle.jsonld", + "https://example.org/ngsi-ld/latest/parking.jsonld", + "https://uri.etsi.org/ngsi-ld/v1/ngsi-ld-core-context-v1.7.jsonld" + ] + } + """.trimIndent() + + private val simplifiedEntity = + """ + { + "id": "urn:ngsi-ld:Vehicle:A4567", + "type": "Vehicle", + "brandName": "Mercedes", + "isParked": "urn:ngsi-ld:OffStreetParking:Downtown1", + "location": { + "type": "Point", + "coordinates": [ + 24.30623, + 60.07966 + ] + }, + "@context": [ + "https://example.org/ngsi-ld/latest/commonTerms.jsonld", + "https://example.org/ngsi-ld/latest/vehicle.jsonld", + "https://example.org/ngsi-ld/latest/parking.jsonld", + "https://uri.etsi.org/ngsi-ld/v1/ngsi-ld-core-context-v1.7.jsonld" + ] + } + """.trimIndent() + + private val normalizedMultiAttributeEntity = + """ + { + "id": "urn:ngsi-ld:Vehicle:A4567", + "type": "Vehicle", + "speed": [ + { + "type": "Property", + "datasetId": "urn:ngsi-ld:Dataset:01", + "value": 10 + }, + { + "type": "Property", + "datasetId": "urn:ngsi-ld:Dataset:02", + "value": 11 + } + ], + "hasOwner": [ + { + "type": "Relationship", + "datasetId": "urn:ngsi-ld:Dataset:01", + "object": "urn:ngsi-ld:Person:John" + }, + { + "type": "Relationship", + "datasetId": "urn:ngsi-ld:Dataset:02", + "object": "urn:ngsi-ld:Person:Jane" + } + ] + } + """.trimIndent() + + private val simplifiedMultiAttributeEntity = + """ + { + "id": "urn:ngsi-ld:Vehicle:A4567", + "type": "Vehicle", + "speed": { + "dataset": { + "urn:ngsi-ld:Dataset:01": 10, + "urn:ngsi-ld:Dataset:02": 11 + } + }, + "hasOwner": { + "dataset": { + "urn:ngsi-ld:Dataset:01": "urn:ngsi-ld:Person:John", + "urn:ngsi-ld:Dataset:02": "urn:ngsi-ld:Person:Jane" + } + } + } + """.trimIndent() + + @Test + fun `it should simplify a compacted entity`() { + val normalizedMap = normalizedEntity.deserializeAsMap() + val simplifiedMap = simplifiedEntity.deserializeAsMap() + + val resultMap = normalizedMap.toKeyValues() + + assertEquals(simplifiedMap, resultMap) + } + + @Test + fun `it should simplify a compacted entity with multi-attributes`() { + val normalizedMap = normalizedMultiAttributeEntity.deserializeAsMap() + val simplifiedMap = simplifiedMultiAttributeEntity.deserializeAsMap() + + val resultMap = normalizedMap.toKeyValues() + + assertEquals(simplifiedMap, resultMap) + } + @Test fun `it should return the simplified representation of a JsonProperty having an object as value`() { val compactedEntity = """ @@ -76,4 +211,543 @@ class CompactedEntityTests { """.trimIndent() assertJsonPayloadsAreEqual(expectedSimplifiedRepresentation, serializeObject(simplifiedRepresentation)) } + + @Test + fun `it should return a simplified entity with sysAttrs`() { + val inputEntity = + """ + { + "id": "urn:ngsi-ld:Vehicle:A4567", + "type": "Vehicle", + "createdAt": "2023-11-25T08:00:00Z", + "modifiedAt": "2023-11-25T09:00:00Z", + "brandName": { + "type": "Property", + "value": "Mercedes" + } + } + """.trimIndent().deserializeAsMap() + val expectedEntity = + """ + { + "id": "urn:ngsi-ld:Vehicle:A4567", + "type": "Vehicle", + "createdAt": "2023-11-25T08:00:00Z", + "modifiedAt": "2023-11-25T09:00:00Z", + "brandName": "Mercedes" + } + """.trimIndent().deserializeAsMap() + + val simplifiedEntity = inputEntity.toFinalRepresentation( + NgsiLdDataRepresentation( + EntityRepresentation.JSON, + AttributeRepresentation.SIMPLIFIED, + includeSysAttrs = true + ) + ) + + assertEquals(expectedEntity, simplifiedEntity) + } + + @Test + fun `it should return a simplified entity without sysAttrs`() { + val inputEntity = + """ + { + "id": "urn:ngsi-ld:Vehicle:A4567", + "type": "Vehicle", + "createdAt": "2023-11-25T08:00:00Z", + "modifiedAt": "2023-11-25T09:00:00Z", + "brandName": { + "type": "Property", + "value": "Mercedes" + } + } + """.trimIndent().deserializeAsMap() + val expectedEntity = + """ + { + "id": "urn:ngsi-ld:Vehicle:A4567", + "type": "Vehicle", + "brandName": "Mercedes" + } + """.trimIndent().deserializeAsMap() + + val simplifiedEntity = inputEntity.toFinalRepresentation( + NgsiLdDataRepresentation( + EntityRepresentation.forMediaType(MediaType.APPLICATION_JSON), + AttributeRepresentation.SIMPLIFIED, + includeSysAttrs = false + ) + ) + + assertEquals(expectedEntity, simplifiedEntity) + } + + @Test + fun `it should return a simplified entity with a multi-attribute Property`() { + val inputEntity = + """ + { + "id": "urn:ngsi-ld:Vehicle:A4567", + "speed": [ + { + "type": "Property", + "value": 55, + "datasetId": "urn:ngsi-ld:Property:speedometerA4567-speed" + }, + { + "type": "Property", + "value": 54.5, + "datasetId": "urn:ngsi-ld:Property:gpsBxyz123-speed" + }] + } + """.trimIndent().deserializeAsMap() + val expectedEntity = + """ + { + "id": "urn:ngsi-ld:Vehicle:A4567", + "speed": { + "dataset": { + "urn:ngsi-ld:Property:speedometerA4567-speed": 55, + "urn:ngsi-ld:Property:gpsBxyz123-speed": 54.5 + } + } + } + """.trimIndent().deserializeAsMap() + + val simplifiedEntity = inputEntity.toFinalRepresentation( + NgsiLdDataRepresentation( + EntityRepresentation.forMediaType(MediaType.APPLICATION_JSON), + AttributeRepresentation.SIMPLIFIED, + includeSysAttrs = false + ) + ) + + assertEquals(expectedEntity, simplifiedEntity) + } + + @Test + fun `it should return a simplified entity with a multi-attribute Relationship`() { + val inputEntity = + """ + { + "id": "urn:ngsi-ld:Vehicle:A4567", + "hasOwner": [ + { + "type": "Relationship", + "datasetId": "urn:ngsi-ld:Dataset:01", + "object": "urn:ngsi-ld:Person:John" + }, + { + "type": "Relationship", + "datasetId": "urn:ngsi-ld:Dataset:02", + "object": "urn:ngsi-ld:Person:Jane" + } + ] + } + """.trimIndent().deserializeAsMap() + val expectedEntity = + """ + { + "id": "urn:ngsi-ld:Vehicle:A4567", + "hasOwner": { + "dataset": { + "urn:ngsi-ld:Dataset:01": "urn:ngsi-ld:Person:John", + "urn:ngsi-ld:Dataset:02": "urn:ngsi-ld:Person:Jane" + } + } + } + """.trimIndent().deserializeAsMap() + + val simplifiedEntity = inputEntity.toFinalRepresentation( + NgsiLdDataRepresentation( + EntityRepresentation.forMediaType(MediaType.APPLICATION_JSON), + AttributeRepresentation.SIMPLIFIED, + includeSysAttrs = false + ) + ) + + assertEquals(expectedEntity, simplifiedEntity) + } + + @Test + fun `it should return a simplified entity with a multi-attribute JsonProperty`() { + val inputEntity = + """ + { + "id": "urn:ngsi-ld:Vehicle:A4567", + "speed": [ + { + "type": "JsonProperty", + "json": { + "anId": "id" + }, + "datasetId": "urn:ngsi-ld:JsonProperty:1" + }, + { + "type": "JsonProperty", + "json": { + "anArray": [1, 2] + }, + "datasetId": "urn:ngsi-ld:JsonProperty:2" + }] + } + """.trimIndent().deserializeAsMap() + val expectedEntity = + """ + { + "id": "urn:ngsi-ld:Vehicle:A4567", + "speed": { + "dataset": { + "urn:ngsi-ld:JsonProperty:1": { + "json": { + "anId": "id" + } + }, + "urn:ngsi-ld:JsonProperty:2": { + "json": { + "anArray": [1, 2] + } + } + } + } + } + """.trimIndent().deserializeAsMap() + + val simplifiedEntity = inputEntity.toFinalRepresentation( + NgsiLdDataRepresentation( + EntityRepresentation.forMediaType(MediaType.APPLICATION_JSON), + AttributeRepresentation.SIMPLIFIED, + includeSysAttrs = false + ) + ) + + assertEquals(expectedEntity, simplifiedEntity) + } + + @Test + fun `it should return a simplified entity with a multi-attribute GeoProperty`() { + val inputEntity = + """ + { + "id": "urn:ngsi-ld:Vehicle:A4567", + "location": [ + { + "type": "GeoProperty", + "value": { + "type": "Point", + "coordinates": [ 24.30623, 60.07966 ] + }, + "datasetId": "urn:ngsi-ld:GeoProperty:1" + }, + { + "type": "GeoProperty", + "value": { + "type": "Point", + "coordinates": [ 25.30623, 60.08066 ] + }, + "datasetId": "urn:ngsi-ld:GeoProperty:2" + }] + } + """.trimIndent().deserializeAsMap() + val expectedEntity = + """ + { + "id": "urn:ngsi-ld:Vehicle:A4567", + "location": + { + "dataset": { + "urn:ngsi-ld:GeoProperty:1": { + "type": "Point", + "coordinates": [ 24.30623, 60.07966 ] + }, + "urn:ngsi-ld:GeoProperty:2": { + "type": "Point", + "coordinates": [ 25.30623, 60.08066 ] + } + } + } + } + """.trimIndent().deserializeAsMap() + + val simplifiedEntity = inputEntity.toFinalRepresentation( + NgsiLdDataRepresentation( + EntityRepresentation.forMediaType(MediaType.APPLICATION_JSON), + AttributeRepresentation.SIMPLIFIED, + includeSysAttrs = false + ) + ) + + assertEquals(expectedEntity, simplifiedEntity) + } + + @Test + fun `it should return a simplified entity with a multi-Attribute having a default instance`() { + val inputEntity = + """ + { + "id": "urn:ngsi-ld:Vehicle:A4567", + "type": "Vehicle", + "speed": [ + { + "type": "Property", + "value": 10 + }, + { + "type": "Property", + "datasetId": "urn:ngsi-ld:Dataset:01", + "value": 11 + } + ], + "hasOwner": [ + { + "type": "Relationship", + "object": "urn:ngsi-ld:Person:John" + }, + { + "type": "Relationship", + "datasetId": "urn:ngsi-ld:Dataset:01", + "object": "urn:ngsi-ld:Person:Jane" + } + ] + } + """.trimIndent().deserializeAsMap() + val expectedEntity = + """ + { + "id": "urn:ngsi-ld:Vehicle:A4567", + "type": "Vehicle", + "speed": { + "dataset": { + "@none": 10, + "urn:ngsi-ld:Dataset:01": 11 + } + }, + "hasOwner": { + "dataset": { + "@none": "urn:ngsi-ld:Person:John", + "urn:ngsi-ld:Dataset:01": "urn:ngsi-ld:Person:Jane" + } + } + } + """.trimIndent().deserializeAsMap() + + val simplifiedEntity = inputEntity.toFinalRepresentation( + NgsiLdDataRepresentation( + EntityRepresentation.forMediaType(MediaType.APPLICATION_JSON), + AttributeRepresentation.SIMPLIFIED, + includeSysAttrs = false + ) + ) + + assertEquals(expectedEntity, simplifiedEntity) + } + + @Test + fun `it should return the GeoJSON representation of a normalized entity on location attribute`() { + val inputEntity = normalizedEntity.deserializeAsMap() + + val expectedEntity = + """ + { + "id": "urn:ngsi-ld:Vehicle:A4567", + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [ 24.30623, 60.07966 ] + }, + "properties": { + "type": "Vehicle", + "brandName": { + "type": "Property", + "value": "Mercedes" + }, + "isParked": { + "type": "Relationship", + "object": "urn:ngsi-ld:OffStreetParking:Downtown1", + "observedAt": "2017-07-29T12:00:04Z", + "providedBy": { + "type": "Relationship", + "object": "urn:ngsi-ld:Person:Bob" + } + }, + "location": { + "type": "GeoProperty", + "value": { + "type": "Point", + "coordinates": [ + 24.30623, + 60.07966 + ] + } + }, + "@context": [ + "https://example.org/ngsi-ld/latest/commonTerms.jsonld", + "https://example.org/ngsi-ld/latest/vehicle.jsonld", + "https://example.org/ngsi-ld/latest/parking.jsonld", + "https://uri.etsi.org/ngsi-ld/v1/ngsi-ld-core-context-v1.7.jsonld" + ] + } + } + """.trimIndent().deserializeAsMap() + + val actualEntity = inputEntity.toFinalRepresentation( + NgsiLdDataRepresentation( + EntityRepresentation.GEO_JSON, + AttributeRepresentation.NORMALIZED, + includeSysAttrs = false, + geometryProperty = JsonLdUtils.NGSILD_LOCATION_TERM + ) + ) + + assertEquals(expectedEntity, actualEntity) + } + + @Test + fun `it should return the GeoJSON representation of a simplified entity on location attribute`() { + val inputEntity = normalizedEntity.deserializeAsMap() + + val expectedEntity = + """ + { + "id": "urn:ngsi-ld:Vehicle:A4567", + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [ 24.30623, 60.07966 ] + }, + "properties": { + "type": "Vehicle", + "brandName": "Mercedes", + "isParked": "urn:ngsi-ld:OffStreetParking:Downtown1", + "location": { + "type": "Point", + "coordinates": [ 24.30623, 60.07966 ] + }, + "@context": [ + "https://example.org/ngsi-ld/latest/commonTerms.jsonld", + "https://example.org/ngsi-ld/latest/vehicle.jsonld", + "https://example.org/ngsi-ld/latest/parking.jsonld", + "https://uri.etsi.org/ngsi-ld/v1/ngsi-ld-core-context-v1.7.jsonld" + ] + } + } + """.trimIndent().deserializeAsMap() + + val simplifiedEntity = inputEntity.toFinalRepresentation( + NgsiLdDataRepresentation( + EntityRepresentation.GEO_JSON, + AttributeRepresentation.SIMPLIFIED, + includeSysAttrs = false, + geometryProperty = JsonLdUtils.NGSILD_LOCATION_TERM + ) + ) + + assertEquals(expectedEntity, simplifiedEntity) + } + + @Test + fun `it should return the GeoJSON representation of an entity with a null geometry if it does not exist`() { + val inputEntity = + """ + { + "id": "urn:ngsi-ld:Vehicle:A4567", + "type": "Vehicle", + "brandName": { + "type": "Property", + "value": "Mercedes" + } + } + """.trimIndent().deserializeAsMap() + + val expectedEntity = + """ + { + "id": "urn:ngsi-ld:Vehicle:A4567", + "type": "Feature", + "geometry": null, + "properties": { + "type": "Vehicle", + "brandName": "Mercedes" + } + } + """.trimIndent().deserializeAsMap() + + val simplifiedEntity = inputEntity.toFinalRepresentation( + NgsiLdDataRepresentation( + EntityRepresentation.GEO_JSON, + AttributeRepresentation.SIMPLIFIED, + includeSysAttrs = false, + geometryProperty = JsonLdUtils.NGSILD_LOCATION_TERM + ) + ) + + assertEquals(expectedEntity, simplifiedEntity) + } + + @Test + fun `it should return the GeoJSON representation of simplified entities`() { + val inputEntity = + """ + { + "id": "urn:ngsi-ld:Vehicle:A4567", + "type": "Vehicle", + "location": { + "type": "GeoProperty", + "value": { + "type": "Point", + "coordinates": [ 24.30623, 60.07966 ] + } + } + } + """.trimIndent().deserializeAsMap() + + val expectedEntities = + """ + { + "type": "FeatureCollection", + "features": [{ + "id": "urn:ngsi-ld:Vehicle:A4567", + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [ 24.30623, 60.07966 ] + }, + "properties": { + "type": "Vehicle", + "location": { + "type": "Point", + "coordinates": [ 24.30623, 60.07966 ] + } + } + }, { + "id": "urn:ngsi-ld:Vehicle:A4567", + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [ 24.30623, 60.07966 ] + }, + "properties": { + "type": "Vehicle", + "location": { + "type": "Point", + "coordinates": [ 24.30623, 60.07966 ] + } + } + }] + } + """.trimIndent().deserializeAsMap() + + val actualEntities = listOf(inputEntity, inputEntity).toFinalRepresentation( + NgsiLdDataRepresentation( + EntityRepresentation.GEO_JSON, + AttributeRepresentation.SIMPLIFIED, + includeSysAttrs = false, + JsonLdUtils.NGSILD_LOCATION_TERM + ) + ) + + assertEquals(expectedEntities, actualEntities) + } } diff --git a/shared/src/test/kotlin/com/egm/stellio/shared/model/ExpandedEntityTests.kt b/shared/src/test/kotlin/com/egm/stellio/shared/model/ExpandedEntityTests.kt index 1c70d3cc8..e935f63af 100644 --- a/shared/src/test/kotlin/com/egm/stellio/shared/model/ExpandedEntityTests.kt +++ b/shared/src/test/kotlin/com/egm/stellio/shared/model/ExpandedEntityTests.kt @@ -3,119 +3,12 @@ package com.egm.stellio.shared.model import com.egm.stellio.shared.util.* import com.egm.stellio.shared.util.JsonLdUtils.JSONLD_ID import com.egm.stellio.shared.util.JsonLdUtils.NGSILD_CREATED_AT_PROPERTY -import com.egm.stellio.shared.util.JsonLdUtils.NGSILD_LOCATION_TERM -import com.egm.stellio.shared.util.JsonUtils.deserializeAsMap import kotlinx.coroutines.test.runTest import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test import org.junit.jupiter.api.fail -import org.springframework.http.MediaType - class ExpandedEntityTests { - - private val normalizedJson = - """ - { - "id": "urn:ngsi-ld:Vehicle:A4567", - "type": "Vehicle", - "brandName": { - "type": "Property", - "value": "Mercedes" - }, - "isParked": { - "type": "Relationship", - "object": "urn:ngsi-ld:OffStreetParking:Downtown1", - "observedAt": "2017-07-29T12:00:04Z", - "providedBy": { - "type": "Relationship", - "object": "urn:ngsi-ld:Person:Bob" - } - }, - "location": { - "type": "GeoProperty", - "value": { - "type": "Point", - "coordinates": [ - 24.30623, - 60.07966 - ] - } - }, - "@context": [ - "https://example.org/ngsi-ld/latest/commonTerms.jsonld", - "https://example.org/ngsi-ld/latest/vehicle.jsonld", - "https://example.org/ngsi-ld/latest/parking.jsonld", - "https://uri.etsi.org/ngsi-ld/v1/ngsi-ld-core-context-v1.7.jsonld" - ] - } - """.trimIndent() - - private val simplifiedJson = - """ - { - "id": "urn:ngsi-ld:Vehicle:A4567", - "type": "Vehicle", - "brandName": "Mercedes", - "isParked": "urn:ngsi-ld:OffStreetParking:Downtown1", - "location": { - "type": "Point", - "coordinates": [ - 24.30623, - 60.07966 - ] - }, - "@context": [ - "https://example.org/ngsi-ld/latest/commonTerms.jsonld", - "https://example.org/ngsi-ld/latest/vehicle.jsonld", - "https://example.org/ngsi-ld/latest/parking.jsonld", - "https://uri.etsi.org/ngsi-ld/v1/ngsi-ld-core-context-v1.7.jsonld" - ] - } - """.trimIndent() - - private val normalizedMultiAttributeJson = - """ - { - "id": "urn:ngsi-ld:Vehicle:A4567", - "type": "Vehicle", - "speed": [ - { - "type": "Property", - "datasetId": "urn:ngsi-ld:Dataset:01", - "value": 10 - }, - { - "type": "Property", - "datasetId": "urn:ngsi-ld:Dataset:02", - "value": 11 - } - ], - "hasOwner": [ - { - "type": "Relationship", - "datasetId": "urn:ngsi-ld:Dataset:01", - "object": "urn:ngsi-ld:Person:John" - }, - { - "type": "Relationship", - "datasetId": "urn:ngsi-ld:Dataset:02", - "object": "urn:ngsi-ld:Person:Jane" - } - ] - } - """.trimIndent() - - private val simplifiedMultiAttributeJson = - """ - { - "id": "urn:ngsi-ld:Vehicle:A4567", - "type": "Vehicle", - "speed": [ 10, 11 ], - "hasOwner": [ "urn:ngsi-ld:Person:John", "urn:ngsi-ld:Person:Jane" ] - } - """.trimIndent() - @Test fun `it should find an expanded attribute contained in the entity`() { val expandedEntity = ExpandedEntity( @@ -207,303 +100,4 @@ class ExpandedEntityTests { assertThat(nameAttributeInstances).hasSize(1) assertThat(nameAttributeInstances[0]).containsKey(NGSILD_CREATED_AT_PROPERTY) } - - @Test - fun `it should simplify a compacted entity`() { - val normalizedMap = normalizedJson.deserializeAsMap() - val simplifiedMap = simplifiedJson.deserializeAsMap() - - val resultMap = normalizedMap.toKeyValues() - - assertEquals(simplifiedMap, resultMap) - } - - @Test - fun `it should simplify a compacted entity with multi-attributes`() { - val normalizedMap = normalizedMultiAttributeJson.deserializeAsMap() - val simplifiedMap = simplifiedMultiAttributeJson.deserializeAsMap() - - val resultMap = normalizedMap.toKeyValues() - - assertEquals(simplifiedMap, resultMap) - } - - @Test - fun `it should return a simplified entity with sysAttrs`() { - val inputEntity = - """ - { - "id": "urn:ngsi-ld:Vehicle:A4567", - "type": "Vehicle", - "createdAt": "2023-11-25T08:00:00Z", - "modifiedAt": "2023-11-25T09:00:00Z", - "brandName": { - "type": "Property", - "value": "Mercedes" - } - } - """.trimIndent().deserializeAsMap() - val expectedEntity = - """ - { - "id": "urn:ngsi-ld:Vehicle:A4567", - "type": "Vehicle", - "createdAt": "2023-11-25T08:00:00Z", - "modifiedAt": "2023-11-25T09:00:00Z", - "brandName": "Mercedes" - } - """.trimIndent().deserializeAsMap() - - val simplifiedEntity = inputEntity.toFinalRepresentation( - NgsiLdDataRepresentation( - EntityRepresentation.JSON, - AttributeRepresentation.SIMPLIFIED, - includeSysAttrs = true - ) - ) - - assertEquals(expectedEntity, simplifiedEntity) - } - - @Test - fun `it should return a simplified entity without sysAttrs`() { - val inputEntity = - """ - { - "id": "urn:ngsi-ld:Vehicle:A4567", - "type": "Vehicle", - "createdAt": "2023-11-25T08:00:00Z", - "modifiedAt": "2023-11-25T09:00:00Z", - "brandName": { - "type": "Property", - "value": "Mercedes" - } - } - """.trimIndent().deserializeAsMap() - val expectedEntity = - """ - { - "id": "urn:ngsi-ld:Vehicle:A4567", - "type": "Vehicle", - "brandName": "Mercedes" - } - """.trimIndent().deserializeAsMap() - - val simplifiedEntity = inputEntity.toFinalRepresentation( - NgsiLdDataRepresentation( - EntityRepresentation.forMediaType(MediaType.APPLICATION_JSON), - AttributeRepresentation.SIMPLIFIED, - includeSysAttrs = false - ) - ) - - assertEquals(expectedEntity, simplifiedEntity) - } - - @Test - fun `it should return a normalized GeoJSON entity on location attribute`() { - val inputEntity = normalizedJson.deserializeAsMap() - - val expectedEntity = - """ - { - "id": "urn:ngsi-ld:Vehicle:A4567", - "type": "Feature", - "geometry": { - "type": "Point", - "coordinates": [ 24.30623, 60.07966 ] - }, - "properties": { - "type": "Vehicle", - "brandName": { - "type": "Property", - "value": "Mercedes" - }, - "isParked": { - "type": "Relationship", - "object": "urn:ngsi-ld:OffStreetParking:Downtown1", - "observedAt": "2017-07-29T12:00:04Z", - "providedBy": { - "type": "Relationship", - "object": "urn:ngsi-ld:Person:Bob" - } - }, - "location": { - "type": "GeoProperty", - "value": { - "type": "Point", - "coordinates": [ - 24.30623, - 60.07966 - ] - } - }, - "@context": [ - "https://example.org/ngsi-ld/latest/commonTerms.jsonld", - "https://example.org/ngsi-ld/latest/vehicle.jsonld", - "https://example.org/ngsi-ld/latest/parking.jsonld", - "https://uri.etsi.org/ngsi-ld/v1/ngsi-ld-core-context-v1.7.jsonld" - ] - } - } - """.trimIndent().deserializeAsMap() - - val actualEntity = inputEntity.toFinalRepresentation( - NgsiLdDataRepresentation( - EntityRepresentation.GEO_JSON, - AttributeRepresentation.NORMALIZED, - includeSysAttrs = false, - geometryProperty = NGSILD_LOCATION_TERM - ) - ) - - assertEquals(expectedEntity, actualEntity) - } - - @Test - fun `it should return a simplified GeoJSON entity on location attribute`() { - val inputEntity = normalizedJson.deserializeAsMap() - - val expectedEntity = - """ - { - "id": "urn:ngsi-ld:Vehicle:A4567", - "type": "Feature", - "geometry": { - "type": "Point", - "coordinates": [ 24.30623, 60.07966 ] - }, - "properties": { - "type": "Vehicle", - "brandName": "Mercedes", - "isParked": "urn:ngsi-ld:OffStreetParking:Downtown1", - "location": { - "type": "Point", - "coordinates": [ 24.30623, 60.07966 ] - }, - "@context": [ - "https://example.org/ngsi-ld/latest/commonTerms.jsonld", - "https://example.org/ngsi-ld/latest/vehicle.jsonld", - "https://example.org/ngsi-ld/latest/parking.jsonld", - "https://uri.etsi.org/ngsi-ld/v1/ngsi-ld-core-context-v1.7.jsonld" - ] - } - } - """.trimIndent().deserializeAsMap() - - val simplifiedEntity = inputEntity.toFinalRepresentation( - NgsiLdDataRepresentation( - EntityRepresentation.GEO_JSON, - AttributeRepresentation.SIMPLIFIED, - includeSysAttrs = false, - geometryProperty = NGSILD_LOCATION_TERM - ) - ) - - assertEquals(expectedEntity, simplifiedEntity) - } - - @Test - fun `it should return a GeoJSON entity with a null geometry if the GeoProperty does not exist`() { - val inputEntity = - """ - { - "id": "urn:ngsi-ld:Vehicle:A4567", - "type": "Vehicle", - "brandName": { - "type": "Property", - "value": "Mercedes" - } - } - """.trimIndent().deserializeAsMap() - - val expectedEntity = - """ - { - "id": "urn:ngsi-ld:Vehicle:A4567", - "type": "Feature", - "geometry": null, - "properties": { - "type": "Vehicle", - "brandName": "Mercedes" - } - } - """.trimIndent().deserializeAsMap() - - val simplifiedEntity = inputEntity.toFinalRepresentation( - NgsiLdDataRepresentation( - EntityRepresentation.GEO_JSON, - AttributeRepresentation.SIMPLIFIED, - includeSysAttrs = false, - geometryProperty = NGSILD_LOCATION_TERM - ) - ) - - assertEquals(expectedEntity, simplifiedEntity) - } - - @Test - fun `it should return simplified GeoJSON entities`() { - val inputEntity = - """ - { - "id": "urn:ngsi-ld:Vehicle:A4567", - "type": "Vehicle", - "location": { - "type": "GeoProperty", - "value": { - "type": "Point", - "coordinates": [ 24.30623, 60.07966 ] - } - } - } - """.trimIndent().deserializeAsMap() - - val expectedEntities = - """ - { - "type": "FeatureCollection", - "features": [{ - "id": "urn:ngsi-ld:Vehicle:A4567", - "type": "Feature", - "geometry": { - "type": "Point", - "coordinates": [ 24.30623, 60.07966 ] - }, - "properties": { - "type": "Vehicle", - "location": { - "type": "Point", - "coordinates": [ 24.30623, 60.07966 ] - } - } - }, { - "id": "urn:ngsi-ld:Vehicle:A4567", - "type": "Feature", - "geometry": { - "type": "Point", - "coordinates": [ 24.30623, 60.07966 ] - }, - "properties": { - "type": "Vehicle", - "location": { - "type": "Point", - "coordinates": [ 24.30623, 60.07966 ] - } - } - }] - } - """.trimIndent().deserializeAsMap() - - val actualEntities = listOf(inputEntity, inputEntity).toFinalRepresentation( - NgsiLdDataRepresentation( - EntityRepresentation.GEO_JSON, - AttributeRepresentation.SIMPLIFIED, - includeSysAttrs = false, - NGSILD_LOCATION_TERM - ) - ) - - assertEquals(expectedEntities, actualEntities) - } }