diff --git a/search-service/config/detekt/baseline.xml b/search-service/config/detekt/baseline.xml
index f467d3c62..de5d3b9fd 100644
--- a/search-service/config/detekt/baseline.xml
+++ b/search-service/config/detekt/baseline.xml
@@ -5,6 +5,7 @@
ClassNaming:V0_29_JsonLd_migrationTests.kt$V0_29_JsonLd_migrationTests
ClassNaming:V0_29__JsonLd_migration.kt$V0_29__JsonLd_migration : BaseJavaMigration
ComplexCondition:EntitiesQueryUtils.kt$geoQuery == null && q.isNullOrEmpty() && typeSelection.isNullOrEmpty() && attrs.isEmpty()
+ ComplexCondition:EntityQueryService.kt$EntityQueryService$it && !inverse || !it && inverse
Filename:V0_29__JsonLd_migration.kt$db.migration.V0_29__JsonLd_migration.kt
LongMethod:AttributeInstanceService.kt$AttributeInstanceService$@Transactional suspend fun create(attributeInstance: AttributeInstance): Either<APIException, Unit>
LongMethod:EnabledAuthorizationServiceTests.kt$EnabledAuthorizationServiceTests$@Test fun `it should return serialized access control entities with other rigths if user is owner`()
diff --git a/search-service/src/main/kotlin/com/egm/stellio/search/entity/sources/EntitySourceService.kt b/search-service/src/main/kotlin/com/egm/stellio/search/entity/compaction/EntityCompactionService.kt
similarity index 94%
rename from search-service/src/main/kotlin/com/egm/stellio/search/entity/sources/EntitySourceService.kt
rename to search-service/src/main/kotlin/com/egm/stellio/search/entity/compaction/EntityCompactionService.kt
index b061681a7..b425d28f4 100644
--- a/search-service/src/main/kotlin/com/egm/stellio/search/entity/sources/EntitySourceService.kt
+++ b/search-service/src/main/kotlin/com/egm/stellio/search/entity/compaction/EntityCompactionService.kt
@@ -1,4 +1,4 @@
-package com.egm.stellio.search.entity.sources
+package com.egm.stellio.search.entity.compaction
import arrow.core.Either
import arrow.core.left
@@ -27,16 +27,13 @@ import org.springframework.stereotype.Service
import org.springframework.util.MultiValueMap
import java.net.URI
-// todo find better package name and file name
@Service
-class EntitySourceService(
+class EntityCompactionService(
private val entityQueryService: EntityQueryService,
private val contextSourceRegistrationService: ContextSourceRegistrationService,
private val linkedEntityService: LinkedEntityService
) : BaseHandler() {
- // todo could also return Pair, List>
- // + more consistent with getEntity and let combined ApiException with warnings (no case for now)
- // - can't use .bind() easilly
+
suspend fun getEntitiesFromSources(
sub: Sub?,
contexts: List,
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 933e8bc44..4375be60f 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
@@ -5,8 +5,8 @@ import arrow.core.left
import arrow.core.raise.either
import arrow.core.right
import com.egm.stellio.search.csr.model.addWarnings
+import com.egm.stellio.search.entity.compaction.EntityCompactionService
import com.egm.stellio.search.entity.service.EntityService
-import com.egm.stellio.search.entity.sources.EntitySourceService
import com.egm.stellio.search.entity.util.composeEntitiesQueryFromGet
import com.egm.stellio.search.entity.util.validateMinimalQueryEntitiesParameters
import com.egm.stellio.shared.config.ApplicationProperties
@@ -63,7 +63,7 @@ import java.net.URI
class EntityHandler(
private val applicationProperties: ApplicationProperties,
private val entityService: EntityService,
- private val entitySourceService: EntitySourceService
+ private val entityCompactionService: EntityCompactionService
) : BaseHandler() {
/**
@@ -199,7 +199,7 @@ class EntityHandler(
).bind()
.validateMinimalQueryEntitiesParameters().bind()
- val (entities, count, warnings) = entitySourceService.getEntitiesFromSources(
+ val (entities, count, warnings) = entityCompactionService.getEntitiesFromSources(
sub = sub.getOrNull(),
contexts = contexts,
entitiesQuery = entitiesQuery,
@@ -248,7 +248,7 @@ class EntityHandler(
contexts
).bind()
- val (entityOrException, warnings) = entitySourceService.getEntityFromSources(
+ val (entityOrException, warnings) = entityCompactionService.getEntityFromSources(
sub = sub.getOrNull(),
contexts = contexts,
entitiesQuery = entitiesQuery,
diff --git a/search-service/src/test/kotlin/com/egm/stellio/search/entity/compaction/EntityCompactionServiceTests.kt b/search-service/src/test/kotlin/com/egm/stellio/search/entity/compaction/EntityCompactionServiceTests.kt
new file mode 100644
index 000000000..2f62109ff
--- /dev/null
+++ b/search-service/src/test/kotlin/com/egm/stellio/search/entity/compaction/EntityCompactionServiceTests.kt
@@ -0,0 +1,511 @@
+// package com.egm.stellio.search.entity.compaction
+//
+// import com.egm.stellio.search.common.config.SearchProperties
+// import com.egm.stellio.search.csr.service.ContextSourceRegistrationService
+// import com.egm.stellio.search.entity.service.EntityQueryService
+// import com.egm.stellio.search.entity.service.EntityService
+// import com.egm.stellio.search.entity.service.LinkedEntityService
+// import com.egm.stellio.shared.config.ApplicationProperties
+// import com.egm.stellio.shared.util.toUri
+// import com.ninjasquad.springmockk.MockkBean
+// import io.mockk.coEvery
+// import org.junit.jupiter.api.BeforeEach
+// import org.springframework.beans.factory.annotation.Autowired
+// import org.springframework.boot.context.properties.EnableConfigurationProperties
+// import org.springframework.test.context.ActiveProfiles
+//
+// // DO NOT REVIEW WIP
+// // DO NOT REVIEW WIP
+// // DO NOT REVIEW WIP
+// // DO NOT REVIEW WIP
+// // DO NOT REVIEW WIP
+//
+// @ActiveProfiles("test")
+// @EnableConfigurationProperties(ApplicationProperties::class, SearchProperties::class)
+// class EntityCompactionServiceTests {
+//
+// @Autowired
+// private lateinit var applicationProperties: ApplicationProperties
+//
+// @MockkBean
+// private lateinit var entitySourceService: EntityCompactionService
+//
+// @MockkBean
+// private lateinit var entityService: EntityService
+//
+// @MockkBean
+// private lateinit var entityQueryService: EntityQueryService
+//
+// @MockkBean
+// private lateinit var contextSourceRegistrationService: ContextSourceRegistrationService
+//
+// @MockkBean(relaxed = true)
+// private lateinit var linkedEntityService: LinkedEntityService
+//
+// @BeforeEach
+// fun mockCSR() {
+// coEvery {
+// contextSourceRegistrationService
+// .getContextSourceRegistrations(any(), any(), any())
+// } returns listOf()
+// }
+//
+// private val beehiveId = "urn:ngsi-ld:BeeHive:TESTC".toUri()
+// private val fishNumberAttribute = "https://ontology.eglobalmark.com/aquac#fishNumber"
+// private val fishSizeAttribute = "https://ontology.eglobalmark.com/aquac#fishSize"
+//
+// // // todo in entitySourceServiceTEst ?
+// // @Test
+// // fun `get entity by id should correctly serialize multi-attribute relationship having more than one instance`(){
+// // coEvery { entityQueryService.queryEntity(any(), MOCK_USER_SUB) } returns ExpandedEntity(
+// // mapOf(
+// // "https://uri.etsi.org/ngsi-ld/default-context/managedBy" to
+// // listOf(
+// // mapOf(
+// // JSONLD_TYPE to "https://uri.etsi.org/ngsi-ld/Relationship",
+// // NGSILD_RELATIONSHIP_OBJECT to mapOf(
+// // JSONLD_ID to "urn:ngsi-ld:Beekeeper:1229"
+// // )
+// // ),
+// // mapOf(
+// // JSONLD_TYPE to "https://uri.etsi.org/ngsi-ld/Relationship",
+// // NGSILD_RELATIONSHIP_OBJECT to mapOf(
+// // JSONLD_ID to "urn:ngsi-ld:Beekeeper:1230"
+// // ),
+// // NGSILD_DATASET_ID_PROPERTY to mapOf(
+// // JSONLD_ID to "urn:ngsi-ld:Dataset:managedBy:0215"
+// // )
+// // )
+// // ),
+// // JSONLD_ID to "urn:ngsi-ld:Beehive:4567",
+// // JSONLD_TYPE to listOf("Beehive")
+// // )
+// // ).right()
+// //
+// // webClient.get()
+// // .uri("/ngsi-ld/v1/entities/$beehiveId")
+// // .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
+// // .exchange()
+// // .expectStatus().isOk
+// // .expectBody().json(
+// // """
+// // {
+// // "id":"urn:ngsi-ld:Beehive:4567",
+// // "type": "Beehive",
+// // "managedBy":[
+// // {
+// // "type": "Relationship",
+// // "object":"urn:ngsi-ld:Beekeeper:1229"
+// // },
+// // {
+// // "type": "Relationship",
+// // "datasetId":"urn:ngsi-ld:Dataset:managedBy:0215",
+// // "object":"urn:ngsi-ld:Beekeeper:1230"
+// // }
+// // ],
+// // "@context": "${applicationProperties.contexts.core}"
+// // }
+// // """.trimIndent()
+// // )
+// // }
+//
+// // // todo put on EntitySource Test
+// // @Test
+// // fun `get entity by id should correctly filter the asked attributes`() = runTest {
+// // val compactedEntity = """
+// // {
+// // "id": "$beehiveId",
+// // "type": "Beehive",
+// // "attr1": {
+// // "type": "Property",
+// // "value": "some value 1"
+// // },
+// // "attr2": {
+// // "type": "Property",
+// // "value": "some value 2"
+// // },
+// // "@context" : [
+// // "http://localhost:8093/jsonld-contexts/apic-compound.jsonld"
+// // ]
+// // }
+// // """.trimIndent().deserializeAsMap()
+// //
+// // mockEntitySourceSuccess(compactedEntity)
+// //
+// // webClient.get()
+// // .uri("/ngsi-ld/v1/entities/$beehiveId?attrs=attr2")
+// // .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
+// // .exchange()
+// // .expectStatus().isOk
+// // .expectBody()
+// // .jsonPath("$.attr1").doesNotExist()
+// // .jsonPath("$.attr2").isNotEmpty
+// // }
+// //
+// // // todo move to CompactedServiceTest
+// // @Test
+// // fun `get entity by id should return 404 if the entity has none of the requested attributes`() {
+// // coEvery { entityQueryService.queryEntity(any(), MOCK_USER_SUB) } returns ExpandedEntity(
+// // mapOf(
+// // "@id" to beehiveId.toString(),
+// // "@type" to listOf(BEEHIVE_TYPE)
+// // )
+// // ).right()
+// //
+// // val expectedMessage = entityOrAttrsNotFoundMessage(
+// // beehiveId.toString(),
+// // setOf("https://uri.etsi.org/ngsi-ld/default-context/attr2")
+// // )
+// // webClient.get()
+// // .uri("/ngsi-ld/v1/entities/$beehiveId?attrs=attr2")
+// // .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
+// // .exchange()
+// // .expectStatus().isNotFound
+// // .expectBody().json(
+// // """
+// // {
+// // "type": "https://uri.etsi.org/ngsi-ld/errors/ResourceNotFound",
+// // "title": "$expectedMessage",
+// // "detail": "$DEFAULT_DETAIL"
+// // }
+// // """.trimIndent()
+// // )
+// // }
+// //
+// // // todo in entitySourceServiceTest and for dateTime
+// // @Test
+// // fun `get entity by id should correctly serialize properties of type DateTime `() {
+// // coEvery { entityQueryService.queryEntity(any(), MOCK_USER_SUB) } returns ExpandedEntity(
+// // mapOf(
+// // NGSILD_CREATED_AT_PROPERTY to
+// // mapOf(
+// // "@type" to NGSILD_DATE_TIME_TYPE,
+// // "@value" to Instant.parse("2015-10-18T11:20:30.000001Z").atZone(ZoneOffset.UTC)
+// // ),
+// // "https://uri.etsi.org/ngsi-ld/default-context/testedAt" to mapOf(
+// // "@type" to "https://uri.etsi.org/ngsi-ld/Property",
+// // NGSILD_PROPERTY_VALUE to mapOf(
+// // "@type" to NGSILD_DATE_TIME_TYPE,
+// // "@value" to Instant.parse("2015-10-18T11:20:30.000001Z").atZone(ZoneOffset.UTC)
+// // ),
+// // NGSILD_CREATED_AT_PROPERTY to
+// // mapOf(
+// // "@type" to NGSILD_DATE_TIME_TYPE,
+// // "@value" to Instant.parse("2015-10-18T11:20:30.000001Z").atZone(ZoneOffset.UTC)
+// // ),
+// // NGSILD_MODIFIED_AT_PROPERTY to
+// // mapOf(
+// // "@type" to NGSILD_DATE_TIME_TYPE,
+// // "@value" to Instant.parse("2015-10-18T12:20:30.000001Z").atZone(ZoneOffset.UTC)
+// // )
+// // ),
+// // "@id" to beehiveId.toString(),
+// // "@type" to listOf("Beehive")
+// // )
+// // ).right()
+// //
+// // webClient.get()
+// // .uri("/ngsi-ld/v1/entities/$beehiveId?options=sysAttrs")
+// // .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
+// // .exchange()
+// // .expectStatus().isOk
+// // .expectBody().json(
+// // """
+// // {
+// // "createdAt":"2015-10-18T11:20:30.000001Z",
+// // "testedAt":{
+// // "type": "Property",
+// // "value":{
+// // "type": "DateTime",
+// // "@value":"2015-10-18T11:20:30.000001Z"
+// // },
+// // "createdAt":"2015-10-18T11:20:30.000001Z",
+// // "modifiedAt":"2015-10-18T12:20:30.000001Z"
+// // },
+// // "@context": "${applicationProperties.contexts.core}"
+// // }
+// // """.trimIndent()
+// // )
+// // }
+// //
+// // // todo in entitySourceServiceTest
+// // @Test
+// // fun `get entity by id should correctly serialize properties of type Date`() {
+// // coEvery { entityQueryService.queryEntity(any(), MOCK_USER_SUB) } returns ExpandedEntity(
+// // mapOf(
+// // "https://uri.etsi.org/ngsi-ld/default-context/testedAt" to mapOf(
+// // "@type" to "https://uri.etsi.org/ngsi-ld/Property",
+// // NGSILD_PROPERTY_VALUE to mapOf(
+// // "@type" to NGSILD_DATE_TYPE,
+// // "@value" to LocalDate.of(2015, 10, 18)
+// // )
+// // ),
+// // "@id" to beehiveId.toString(),
+// // "@type" to listOf("Beehive")
+// // )
+// // ).right()
+// //
+// // webClient.get()
+// // .uri("/ngsi-ld/v1/entities/$beehiveId")
+// // .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
+// // .exchange()
+// // .expectStatus().isOk
+// // .expectBody().json(
+// // """
+// // {
+// // "testedAt":{
+// // "type": "Property",
+// // "value":{
+// // "type": "Date",
+// // "@value":"2015-10-18"
+// // }
+// // },
+// // "@context": "${applicationProperties.contexts.core}"
+// // }
+// // """.trimIndent()
+// // )
+// // }
+// //
+// // // todo in entitySourceServiceTEst
+// // @Test
+// // fun `get entity by id should correctly serialize properties of type Time`() {
+// // coEvery { entityQueryService.queryEntity(any(), MOCK_USER_SUB) } returns ExpandedEntity(
+// // mapOf(
+// // "https://uri.etsi.org/ngsi-ld/default-context/testedAt" to mapOf(
+// // "@type" to "https://uri.etsi.org/ngsi-ld/Property",
+// // NGSILD_PROPERTY_VALUE to mapOf(
+// // "@type" to NGSILD_TIME_TYPE,
+// // "@value" to LocalTime.of(11, 20, 30)
+// // )
+// // ),
+// // "@id" to "urn:ngsi-ld:Beehive:4567",
+// // "@type" to listOf("Beehive")
+// // )
+// // ).right()
+// //
+// // webClient.get()
+// // .uri("/ngsi-ld/v1/entities/$beehiveId")
+// // .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
+// // .exchange()
+// // .expectStatus().isOk
+// // .expectBody().json(
+// // """
+// // {
+// // "testedAt":{
+// // "type": "Property",
+// // "value":{
+// // "type": "Time",
+// // "@value":"11:20:30"
+// // }
+// // },
+// // "@context": "${applicationProperties.contexts.core}"
+// // }
+// // """.trimIndent()
+// // )
+// // }
+// //
+// // // todo in entitySourceServiceTEst ?
+// // @Test
+// // fun `get entity by id should correctly serialize multi-attribute property having one instance`() {
+// // coEvery { entityQueryService.queryEntity(any(), MOCK_USER_SUB) } returns ExpandedEntity(
+// // mapOf(
+// // "https://uri.etsi.org/ngsi-ld/default-context/name" to
+// // mapOf(
+// // JSONLD_TYPE to "https://uri.etsi.org/ngsi-ld/Property",
+// // NGSILD_PROPERTY_VALUE to "ruche",
+// // NGSILD_DATASET_ID_PROPERTY to mapOf(
+// // JSONLD_ID to "urn:ngsi-ld:Property:french-name"
+// // )
+// // ),
+// // JSONLD_ID to "urn:ngsi-ld:Beehive:4567",
+// // JSONLD_TYPE to listOf("Beehive")
+// // )
+// // ).right()
+// //
+// // webClient.get()
+// // .uri("/ngsi-ld/v1/entities/$beehiveId")
+// // .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
+// // .exchange()
+// // .expectStatus().isOk
+// // .expectBody().json(
+// // """
+// // {
+// // "id":"urn:ngsi-ld:Beehive:4567",
+// // "type": "Beehive",
+// // "name":{"type": "Property","datasetId":"urn:ngsi-ld:Property:french-name","value":"ruche"},
+// // "@context": "${applicationProperties.contexts.core}"
+// // }
+// // """.trimIndent()
+// // )
+// // }
+// //
+// // // todo in entitySourceServiceTEst ?
+// // @Test
+// // fun `get entity by id should correctly serialize multi-attribute property having more than one instance`() {
+// // coEvery { entityQueryService.queryEntity(any(), MOCK_USER_SUB) } returns ExpandedEntity(
+// // mapOf(
+// // "https://uri.etsi.org/ngsi-ld/default-context/name" to
+// // listOf(
+// // mapOf(
+// // JSONLD_TYPE to "https://uri.etsi.org/ngsi-ld/Property",
+// // NGSILD_PROPERTY_VALUE to "beehive",
+// // NGSILD_DATASET_ID_PROPERTY to mapOf(
+// // JSONLD_ID to "urn:ngsi-ld:Property:english-name"
+// // )
+// // ),
+// // mapOf(
+// // JSONLD_TYPE to "https://uri.etsi.org/ngsi-ld/Property",
+// // NGSILD_PROPERTY_VALUE to "ruche",
+// // NGSILD_DATASET_ID_PROPERTY to mapOf(
+// // JSONLD_ID to "urn:ngsi-ld:Property:french-name"
+// // )
+// // )
+// // ),
+// // JSONLD_ID to "urn:ngsi-ld:Beehive:4567",
+// // JSONLD_TYPE to listOf("Beehive")
+// // )
+// // ).right()
+// //
+// // webClient.get()
+// // .uri("/ngsi-ld/v1/entities/$beehiveId")
+// // .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
+// // .exchange()
+// // .expectStatus().isOk
+// // .expectBody().json(
+// // """
+// // {
+// // "id":"urn:ngsi-ld:Beehive:4567",
+// // "type": "Beehive",
+// // "name":[
+// // {
+// // "type": "Property","datasetId":"urn:ngsi-ld:Property:english-name","value":"beehive"
+// // },
+// // {
+// // "type": "Property","datasetId":"urn:ngsi-ld:Property:french-name","value":"ruche"
+// // }
+// // ],
+// // "@context": "${applicationProperties.contexts.core}"
+// // }
+// // """.trimIndent()
+// // )
+// // }
+// //
+// // // todo in entitySourceServiceTEst ?
+// // @Test
+// // fun `get entity by id should correctly serialize multi-attribute relationship having one instance`() {
+// // coEvery { entityQueryService.queryEntity(any(), MOCK_USER_SUB) } returns ExpandedEntity(
+// // mapOf(
+// // "https://uri.etsi.org/ngsi-ld/default-context/managedBy" to
+// // mapOf(
+// // JSONLD_TYPE to "https://uri.etsi.org/ngsi-ld/Relationship",
+// // NGSILD_RELATIONSHIP_OBJECT to mapOf(
+// // JSONLD_ID to "urn:ngsi-ld:Beekeeper:1230"
+// // ),
+// // NGSILD_DATASET_ID_PROPERTY to mapOf(
+// // JSONLD_ID to "urn:ngsi-ld:Dataset:managedBy:0215"
+// // )
+// // ),
+// // JSONLD_ID to "urn:ngsi-ld:Beehive:4567",
+// // JSONLD_TYPE to listOf("Beehive")
+// // )
+// // ).right()
+// //
+// // webClient.get()
+// // .uri("/ngsi-ld/v1/entities/$beehiveId")
+// // .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
+// // .exchange()
+// // .expectStatus().isOk
+// // .expectBody().json(
+// // """
+// // {
+// // "id":"urn:ngsi-ld:Beehive:4567",
+// // "type": "Beehive",
+// // "managedBy": {
+// // "type": "Relationship",
+// // "datasetId":"urn:ngsi-ld:Dataset:managedBy:0215",
+// // "object":"urn:ngsi-ld:Beekeeper:1230"
+// // },
+// // "@context": "${applicationProperties.contexts.core}"
+// // }
+// // """.trimIndent()
+// // )
+// // }
+//
+// // DO NOT REVIEW WIP
+// //
+// // @Test
+// // fun `get entity by id should return the warnings sent by the CSRs and update the CSRs statuses`() {
+// // val csr = gimmeRawCSR()
+// // coEvery {
+// // entityQueryService.queryEntity("urn:ngsi-ld:BeeHive:TEST".toUri(), sub.getOrNull())
+// // } returns ResourceNotFoundException("no entity").left()
+// //
+// // coEvery {
+// // contextSourceRegistrationService
+// // .getContextSourceRegistrations(any(), any(), any())
+// // } returns listOf(csr, csr)
+// //
+// // mockkObject(ContextSourceCaller) {
+// // coEvery {
+// // ContextSourceCaller.retrieveContextSourceEntity(any(), any(), any(), any())
+// // } returns MiscellaneousWarning(
+// // "message with\nline\nbreaks",
+// // csr
+// // ).left() andThen
+// // MiscellaneousWarning("message", csr).left()
+// //
+// // coEvery { contextSourceRegistrationService.updateContextSourceStatus(any(), any()) } returns Unit
+// // webClient.get()
+// // .uri("/ngsi-ld/v1/entities/urn:ngsi-ld:BeeHive:TEST")
+// // .header(HttpHeaders.LINK, AQUAC_HEADER_LINK)
+// // .exchange()
+// // .expectStatus().isNotFound
+// // .expectHeader().valueEquals(
+// // NGSILDWarning.HEADER_NAME,
+// // "199 urn:ngsi-ld:ContextSourceRegistration:test \"message with line breaks\"",
+// // "199 urn:ngsi-ld:ContextSourceRegistration:test \"message\""
+// // )
+// //
+// // coVerify(exactly = 2) { contextSourceRegistrationService.updateContextSourceStatus(any(), false) }
+// // }
+// // }
+// //
+// //
+// // @Test
+// // fun `get entities should return the warnings sent by the CSRs and update the CSRs statuses`() {
+// // val csr = gimmeRawCSR()
+// //
+// // coEvery {
+// // entityQueryService.queryEntities(any(), sub.getOrNull())
+// // } returns (emptyList() to 0).right()
+// //
+// // coEvery {
+// // contextSourceRegistrationService
+// // .getContextSourceRegistrations(any(), any(), any())
+// // } returns listOf(csr, csr)
+// //
+// // mockkObject(ContextSourceCaller) {
+// // coEvery {
+// // ContextSourceCaller.queryContextSourceEntities(any(), any(), any())
+// // } returns MiscellaneousWarning(
+// // "message with\nline\nbreaks",
+// // csr
+// // ).left() andThen
+// // MiscellaneousWarning("message", csr).left()
+// //
+// // coEvery { contextSourceRegistrationService.updateContextSourceStatus(any(), any()) } returns Unit
+// // webClient.get()
+// // .uri("/ngsi-ld/v1/entities?type=$BEEHIVE_COMPACT_TYPE&count=true")
+// // .header(HttpHeaders.LINK, AQUAC_HEADER_LINK)
+// // .exchange()
+// // .expectStatus().isOk
+// // .expectHeader().valueEquals(
+// // NGSILDWarning.HEADER_NAME,
+// // "199 urn:ngsi-ld:ContextSourceRegistration:test \"message with line breaks\"",
+// // "199 urn:ngsi-ld:ContextSourceRegistration:test \"message\""
+// // ).expectHeader().valueEquals(RESULTS_COUNT_HEADER, "0",)
+// //
+// // coVerify(exactly = 2) { contextSourceRegistrationService.updateContextSourceStatus(any(), false) }
+// // }
+// // }
+// }
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..7167e00e9 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
@@ -6,69 +6,54 @@ import com.egm.stellio.search.common.config.SearchProperties
import com.egm.stellio.search.csr.CsrUtils.gimmeRawCSR
import com.egm.stellio.search.csr.model.MiscellaneousWarning
import com.egm.stellio.search.csr.model.NGSILDWarning
-import com.egm.stellio.search.csr.service.ContextSourceCaller
import com.egm.stellio.search.csr.service.ContextSourceRegistrationService
-import com.egm.stellio.search.entity.model.EntitiesQueryFromGet
+import com.egm.stellio.search.entity.compaction.EntityCompactionService
import com.egm.stellio.search.entity.model.NotUpdatedDetails
import com.egm.stellio.search.entity.model.UpdateResult
-import com.egm.stellio.search.entity.service.EntityQueryService
+import com.egm.stellio.search.entity.model.UpdatedDetails
import com.egm.stellio.search.entity.service.EntityService
-import com.egm.stellio.search.entity.service.LinkedEntityService
import com.egm.stellio.shared.config.ApplicationProperties
import com.egm.stellio.shared.model.AccessDeniedException
import com.egm.stellio.shared.model.AlreadyExistsException
import com.egm.stellio.shared.model.BadRequestDataException
import com.egm.stellio.shared.model.CompactedEntity
import com.egm.stellio.shared.model.DEFAULT_DETAIL
-import com.egm.stellio.shared.model.ExpandedEntity
import com.egm.stellio.shared.model.InternalErrorException
import com.egm.stellio.shared.model.NgsiLdEntity
import com.egm.stellio.shared.model.ResourceNotFoundException
-import com.egm.stellio.shared.queryparameter.PaginationQuery
import com.egm.stellio.shared.util.APIC_COMPOUND_CONTEXT
-import com.egm.stellio.shared.util.APIC_COMPOUND_CONTEXTS
import com.egm.stellio.shared.util.APIC_HEADER_LINK
import com.egm.stellio.shared.util.AQUAC_HEADER_LINK
import com.egm.stellio.shared.util.BEEHIVE_COMPACT_TYPE
-import com.egm.stellio.shared.util.BEEHIVE_TYPE
import com.egm.stellio.shared.util.INCOMING_COMPACT_PROPERTY
import com.egm.stellio.shared.util.INCOMING_PROPERTY
import com.egm.stellio.shared.util.JSON_LD_MEDIA_TYPE
-import com.egm.stellio.shared.util.JsonLdUtils.JSONLD_ID
+import com.egm.stellio.shared.util.JsonLdUtils.JSONLD_ID_TERM
+import com.egm.stellio.shared.util.JsonLdUtils.JSONLD_OBJECT
import com.egm.stellio.shared.util.JsonLdUtils.JSONLD_TYPE
-import com.egm.stellio.shared.util.JsonLdUtils.JSONLD_VALUE
-import com.egm.stellio.shared.util.JsonLdUtils.NGSILD_CREATED_AT_PROPERTY
-import com.egm.stellio.shared.util.JsonLdUtils.NGSILD_DATASET_ID_PROPERTY
-import com.egm.stellio.shared.util.JsonLdUtils.NGSILD_DATE_TIME_TYPE
-import com.egm.stellio.shared.util.JsonLdUtils.NGSILD_DATE_TYPE
-import com.egm.stellio.shared.util.JsonLdUtils.NGSILD_MODIFIED_AT_PROPERTY
-import com.egm.stellio.shared.util.JsonLdUtils.NGSILD_PROPERTY_TYPE
-import com.egm.stellio.shared.util.JsonLdUtils.NGSILD_PROPERTY_VALUE
-import com.egm.stellio.shared.util.JsonLdUtils.NGSILD_RELATIONSHIP_OBJECT
-import com.egm.stellio.shared.util.JsonLdUtils.NGSILD_RELATIONSHIP_TYPE
-import com.egm.stellio.shared.util.JsonLdUtils.NGSILD_TIME_TYPE
+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_CREATED_AT_TERM
+import com.egm.stellio.shared.util.JsonLdUtils.NGSILD_DATASET_ID_TERM
+import com.egm.stellio.shared.util.JsonLdUtils.NGSILD_MODIFIED_AT_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.JsonUtils.deserializeAsMap
import com.egm.stellio.shared.util.MOCK_USER_SUB
import com.egm.stellio.shared.util.RESULTS_COUNT_HEADER
-import com.egm.stellio.shared.util.Sub
import com.egm.stellio.shared.util.TEMPERATURE_COMPACT_PROPERTY
import com.egm.stellio.shared.util.TEMPERATURE_PROPERTY
import com.egm.stellio.shared.util.buildContextLinkHeader
import com.egm.stellio.shared.util.entityNotFoundMessage
-import com.egm.stellio.shared.util.entityOrAttrsNotFoundMessage
-import com.egm.stellio.shared.util.expandJsonLdEntity
import com.egm.stellio.shared.util.loadSampleData
+import com.egm.stellio.shared.util.ngsiLdDateTime
import com.egm.stellio.shared.util.sub
import com.egm.stellio.shared.util.toUri
import com.ninjasquad.springmockk.MockkBean
import io.mockk.called
import io.mockk.coEvery
import io.mockk.coVerify
-import io.mockk.every
-import io.mockk.mockkClass
-import io.mockk.mockkObject
-import io.mockk.slot
import io.mockk.verify
-import kotlinx.coroutines.test.runTest
import org.hamcrest.core.Is
import org.junit.jupiter.api.BeforeAll
import org.junit.jupiter.api.BeforeEach
@@ -87,8 +72,6 @@ import org.springframework.test.context.ActiveProfiles
import org.springframework.test.web.reactive.server.WebTestClient
import java.lang.reflect.UndeclaredThrowableException
import java.time.Instant
-import java.time.LocalDate
-import java.time.LocalTime
import java.time.ZoneOffset
import java.time.ZonedDateTime
@@ -104,17 +87,14 @@ class EntityHandlerTests {
private lateinit var applicationProperties: ApplicationProperties
@MockkBean
- private lateinit var entityService: EntityService
+ private lateinit var entitySourceService: EntityCompactionService
@MockkBean
- private lateinit var entityQueryService: EntityQueryService
+ private lateinit var entityService: EntityService
@MockkBean
private lateinit var contextSourceRegistrationService: ContextSourceRegistrationService
- @MockkBean(relaxed = true)
- private lateinit var linkedEntityService: LinkedEntityService
-
@BeforeAll
fun configureWebClientDefaults() {
webClient = webClient.mutate()
@@ -349,22 +329,29 @@ class EntityHandlerTests {
)
}
- fun initializeRetrieveEntityMocks() {
- val compactedEntity = slot()
-
- coEvery {
- linkedEntityService.processLinkedEntities(capture(compactedEntity), any(), any())
- } answers {
- listOf(compactedEntity.captured).right()
- }
+ private fun mockEntitySourceSuccess(entity: CompactedEntity) {
+ coEvery { entitySourceService.getEntityFromSources(MOCK_USER_SUB, any(), any(), any(), any(), any()) } returns
+ (listOf(entity).right() to emptyList())
}
@Test
fun `get entity by id should return 200 when entity exists`() {
- initializeRetrieveEntityMocks()
- val returnedExpandedEntity = mockkClass(ExpandedEntity::class, relaxed = true)
- coEvery { entityQueryService.queryEntity(any(), MOCK_USER_SUB) } returns returnedExpandedEntity.right()
- every { returnedExpandedEntity.checkContainsAnyOf(any()) } returns Unit.right()
+ val compactedEntity = """
+ {
+ "id": "urn:ngsi-ld:Entity:01",
+ "type": "Entity",
+ "languageProperty": {
+ "type": "LanguageProperty",
+ "languageMap": {
+ "fr": "Grand Place",
+ "nl": "Grote Markt",
+ "@none": "Big Place"
+ }
+ }
+ }
+ """.trimIndent()
+ .deserializeAsMap()
+ mockEntitySourceSuccess(compactedEntity)
webClient.get()
.uri("/ngsi-ld/v1/entities/$beehiveId")
@@ -375,18 +362,13 @@ class EntityHandlerTests {
@Test
fun `get entity by id should correctly serialize temporal properties`() {
- initializeRetrieveEntityMocks()
- coEvery { entityQueryService.queryEntity(any(), MOCK_USER_SUB) } returns ExpandedEntity(
+ mockEntitySourceSuccess(
mapOf(
- NGSILD_CREATED_AT_PROPERTY to
- mapOf(
- "@type" to NGSILD_DATE_TIME_TYPE,
- "@value" to Instant.parse("2015-10-18T11:20:30.000001Z").atZone(ZoneOffset.UTC)
- ),
- "@id" to beehiveId.toString(),
- "@type" to listOf("Beehive")
+ NGSILD_CREATED_AT_TERM to Instant.parse("2015-10-18T11:20:30.000001Z").atZone(ZoneOffset.UTC),
+ "id" to beehiveId.toString(),
+ "type" to listOf("Beehive")
)
- ).right()
+ )
webClient.get()
.uri("/ngsi-ld/v1/entities/$beehiveId?options=sysAttrs")
@@ -397,67 +379,28 @@ class EntityHandlerTests {
"""
{
"createdAt": "2015-10-18T11:20:30.000001Z",
- "@context": "${applicationProperties.contexts.core}"
}
""".trimIndent()
)
}
- @Test
- fun `get entity by id should correctly filter the asked attributes`() = runTest {
- initializeRetrieveEntityMocks()
- val entity = """
- {
- "id": "$beehiveId",
- "type": "Beehive",
- "attr1": {
- "type": "Property",
- "value": "some value 1"
- },
- "attr2": {
- "type": "Property",
- "value": "some value 2"
- },
- "@context" : [
- "http://localhost:8093/jsonld-contexts/apic-compound.jsonld"
- ]
- }
- """.trimIndent()
- val expandedEntity = expandJsonLdEntity(entity)
-
- coEvery { entityQueryService.queryEntity(any(), MOCK_USER_SUB) } returns expandedEntity.right()
-
- webClient.get()
- .uri("/ngsi-ld/v1/entities/$beehiveId?attrs=attr2")
- .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
- .exchange()
- .expectStatus().isOk
- .expectBody()
- .jsonPath("$.attr1").doesNotExist()
- .jsonPath("$.attr2").isNotEmpty
- }
-
@Test
fun `get entity by id should correctly return the simplified representation of an entity`() {
- initializeRetrieveEntityMocks()
- coEvery { entityQueryService.queryEntity(any(), MOCK_USER_SUB) } returns ExpandedEntity(
+ mockEntitySourceSuccess(
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"
- )
+ "id" to beehiveId.toString(),
+ "type" to "Beehive",
+ "prop1" to mapOf(
+ JSONLD_TYPE_TERM to NGSILD_PROPERTY_TERM,
+ JSONLD_VALUE_TERM 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"
- )
- )
+ "rel1" to mapOf(
+ JSONLD_TYPE_TERM to NGSILD_RELATIONSHIP_TERM,
+ JSONLD_OBJECT to "urn:ngsi-ld:Entity:1234"
+ ),
+ "@context" to applicationProperties.contexts.core
)
- ).right()
+ )
webClient.get()
.uri("/ngsi-ld/v1/entities/$beehiveId?options=keyValues")
@@ -478,342 +421,47 @@ class EntityHandlerTests {
)
}
- @Test
- fun `get entity by id should return 404 if the entity has none of the requested attributes`() {
- initializeRetrieveEntityMocks()
- coEvery { entityQueryService.queryEntity(any(), MOCK_USER_SUB) } returns ExpandedEntity(
- mapOf(
- "@id" to beehiveId.toString(),
- "@type" to listOf(BEEHIVE_TYPE)
- )
- ).right()
-
- val expectedMessage = entityOrAttrsNotFoundMessage(
- beehiveId.toString(),
- setOf("https://uri.etsi.org/ngsi-ld/default-context/attr2")
- )
- webClient.get()
- .uri("/ngsi-ld/v1/entities/$beehiveId?attrs=attr2")
- .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
- .exchange()
- .expectStatus().isNotFound
- .expectBody().json(
- """
- {
- "type": "https://uri.etsi.org/ngsi-ld/errors/ResourceNotFound",
- "title": "$expectedMessage",
- "detail": "$DEFAULT_DETAIL"
- }
- """.trimIndent()
- )
- }
-
@Test
fun `get entity by id should not include temporal properties if optional query param sysAttrs is not present`() {
- initializeRetrieveEntityMocks()
- coEvery { entityQueryService.queryEntity(any(), MOCK_USER_SUB) } returns ExpandedEntity(
+ mockEntitySourceSuccess(
mapOf(
- "@id" to beehiveId.toString(),
- "@type" to listOf("Beehive")
+ "id" to beehiveId.toString(),
+ "type" to BEEHIVE_COMPACT_TYPE,
+ "createdAt" to ngsiLdDateTime(),
+ "modifiedAt" to ngsiLdDateTime()
)
- ).right()
+ )
webClient.get()
.uri("/ngsi-ld/v1/entities/$beehiveId")
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.exchange()
.expectStatus().isOk
- .expectBody().json("""{"@context":"${applicationProperties.contexts.core}"}""")
+ .expectBody().json("""{"type":"$BEEHIVE_COMPACT_TYPE"}""")
.jsonPath("$.createdAt").doesNotExist()
.jsonPath("$.modifiedAt").doesNotExist()
}
- @Test
- fun `get entity by id should correctly serialize properties of type DateTime and display sysAttrs asked`() {
- initializeRetrieveEntityMocks()
- coEvery { entityQueryService.queryEntity(any(), MOCK_USER_SUB) } returns ExpandedEntity(
- mapOf(
- NGSILD_CREATED_AT_PROPERTY to
- mapOf(
- "@type" to NGSILD_DATE_TIME_TYPE,
- "@value" to Instant.parse("2015-10-18T11:20:30.000001Z").atZone(ZoneOffset.UTC)
- ),
- "https://uri.etsi.org/ngsi-ld/default-context/testedAt" to mapOf(
- "@type" to "https://uri.etsi.org/ngsi-ld/Property",
- NGSILD_PROPERTY_VALUE to mapOf(
- "@type" to NGSILD_DATE_TIME_TYPE,
- "@value" to Instant.parse("2015-10-18T11:20:30.000001Z").atZone(ZoneOffset.UTC)
- ),
- NGSILD_CREATED_AT_PROPERTY to
- mapOf(
- "@type" to NGSILD_DATE_TIME_TYPE,
- "@value" to Instant.parse("2015-10-18T11:20:30.000001Z").atZone(ZoneOffset.UTC)
- ),
- NGSILD_MODIFIED_AT_PROPERTY to
- mapOf(
- "@type" to NGSILD_DATE_TIME_TYPE,
- "@value" to Instant.parse("2015-10-18T12:20:30.000001Z").atZone(ZoneOffset.UTC)
- )
- ),
- "@id" to beehiveId.toString(),
- "@type" to listOf("Beehive")
- )
- ).right()
-
- webClient.get()
- .uri("/ngsi-ld/v1/entities/$beehiveId?options=sysAttrs")
- .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
- .exchange()
- .expectStatus().isOk
- .expectBody().json(
- """
- {
- "createdAt":"2015-10-18T11:20:30.000001Z",
- "testedAt":{
- "type": "Property",
- "value":{
- "type": "DateTime",
- "@value":"2015-10-18T11:20:30.000001Z"
- },
- "createdAt":"2015-10-18T11:20:30.000001Z",
- "modifiedAt":"2015-10-18T12:20:30.000001Z"
- },
- "@context": "${applicationProperties.contexts.core}"
- }
- """.trimIndent()
- )
- }
-
- @Test
- fun `get entity by id should correctly serialize properties of type Date`() {
- initializeRetrieveEntityMocks()
- coEvery { entityQueryService.queryEntity(any(), MOCK_USER_SUB) } returns ExpandedEntity(
- mapOf(
- "https://uri.etsi.org/ngsi-ld/default-context/testedAt" to mapOf(
- "@type" to "https://uri.etsi.org/ngsi-ld/Property",
- NGSILD_PROPERTY_VALUE to mapOf(
- "@type" to NGSILD_DATE_TYPE,
- "@value" to LocalDate.of(2015, 10, 18)
- )
- ),
- "@id" to beehiveId.toString(),
- "@type" to listOf("Beehive")
- )
- ).right()
-
- webClient.get()
- .uri("/ngsi-ld/v1/entities/$beehiveId")
- .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
- .exchange()
- .expectStatus().isOk
- .expectBody().json(
- """
- {
- "testedAt":{
- "type": "Property",
- "value":{
- "type": "Date",
- "@value":"2015-10-18"
- }
- },
- "@context": "${applicationProperties.contexts.core}"
- }
- """.trimIndent()
- )
- }
-
- @Test
- fun `get entity by id should correctly serialize properties of type Time`() {
- initializeRetrieveEntityMocks()
- coEvery { entityQueryService.queryEntity(any(), MOCK_USER_SUB) } returns ExpandedEntity(
- mapOf(
- "https://uri.etsi.org/ngsi-ld/default-context/testedAt" to mapOf(
- "@type" to "https://uri.etsi.org/ngsi-ld/Property",
- NGSILD_PROPERTY_VALUE to mapOf(
- "@type" to NGSILD_TIME_TYPE,
- "@value" to LocalTime.of(11, 20, 30)
- )
- ),
- "@id" to "urn:ngsi-ld:Beehive:4567",
- "@type" to listOf("Beehive")
- )
- ).right()
-
- webClient.get()
- .uri("/ngsi-ld/v1/entities/$beehiveId")
- .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
- .exchange()
- .expectStatus().isOk
- .expectBody().json(
- """
- {
- "testedAt":{
- "type": "Property",
- "value":{
- "type": "Time",
- "@value":"11:20:30"
- }
- },
- "@context": "${applicationProperties.contexts.core}"
- }
- """.trimIndent()
- )
- }
-
- @Test
- fun `get entity by id should correctly serialize multi-attribute property having one instance`() {
- initializeRetrieveEntityMocks()
- coEvery { entityQueryService.queryEntity(any(), MOCK_USER_SUB) } returns ExpandedEntity(
- mapOf(
- "https://uri.etsi.org/ngsi-ld/default-context/name" to
- mapOf(
- JSONLD_TYPE to "https://uri.etsi.org/ngsi-ld/Property",
- NGSILD_PROPERTY_VALUE to "ruche",
- NGSILD_DATASET_ID_PROPERTY to mapOf(
- JSONLD_ID to "urn:ngsi-ld:Property:french-name"
- )
- ),
- JSONLD_ID to "urn:ngsi-ld:Beehive:4567",
- JSONLD_TYPE to listOf("Beehive")
- )
- ).right()
-
- webClient.get()
- .uri("/ngsi-ld/v1/entities/$beehiveId")
- .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
- .exchange()
- .expectStatus().isOk
- .expectBody().json(
- """
- {
- "id":"urn:ngsi-ld:Beehive:4567",
- "type": "Beehive",
- "name":{"type": "Property","datasetId":"urn:ngsi-ld:Property:french-name","value":"ruche"},
- "@context": "${applicationProperties.contexts.core}"
- }
- """.trimIndent()
- )
- }
-
- @Test
- fun `get entity by id should correctly serialize multi-attribute property having more than one instance`() {
- initializeRetrieveEntityMocks()
- coEvery { entityQueryService.queryEntity(any(), MOCK_USER_SUB) } returns ExpandedEntity(
- mapOf(
- "https://uri.etsi.org/ngsi-ld/default-context/name" to
- listOf(
- mapOf(
- JSONLD_TYPE to "https://uri.etsi.org/ngsi-ld/Property",
- NGSILD_PROPERTY_VALUE to "beehive",
- NGSILD_DATASET_ID_PROPERTY to mapOf(
- JSONLD_ID to "urn:ngsi-ld:Property:english-name"
- )
- ),
- mapOf(
- JSONLD_TYPE to "https://uri.etsi.org/ngsi-ld/Property",
- NGSILD_PROPERTY_VALUE to "ruche",
- NGSILD_DATASET_ID_PROPERTY to mapOf(
- JSONLD_ID to "urn:ngsi-ld:Property:french-name"
- )
- )
- ),
- JSONLD_ID to "urn:ngsi-ld:Beehive:4567",
- JSONLD_TYPE to listOf("Beehive")
- )
- ).right()
-
- webClient.get()
- .uri("/ngsi-ld/v1/entities/$beehiveId")
- .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
- .exchange()
- .expectStatus().isOk
- .expectBody().json(
- """
- {
- "id":"urn:ngsi-ld:Beehive:4567",
- "type": "Beehive",
- "name":[
- {
- "type": "Property","datasetId":"urn:ngsi-ld:Property:english-name","value":"beehive"
- },
- {
- "type": "Property","datasetId":"urn:ngsi-ld:Property:french-name","value":"ruche"
- }
- ],
- "@context": "${applicationProperties.contexts.core}"
- }
- """.trimIndent()
- )
- }
-
- @Test
- fun `get entity by id should correctly serialize multi-attribute relationship having one instance`() {
- initializeRetrieveEntityMocks()
- coEvery { entityQueryService.queryEntity(any(), MOCK_USER_SUB) } returns ExpandedEntity(
- mapOf(
- "https://uri.etsi.org/ngsi-ld/default-context/managedBy" to
- mapOf(
- JSONLD_TYPE to "https://uri.etsi.org/ngsi-ld/Relationship",
- NGSILD_RELATIONSHIP_OBJECT to mapOf(
- JSONLD_ID to "urn:ngsi-ld:Beekeeper:1230"
- ),
- NGSILD_DATASET_ID_PROPERTY to mapOf(
- JSONLD_ID to "urn:ngsi-ld:Dataset:managedBy:0215"
- )
- ),
- JSONLD_ID to "urn:ngsi-ld:Beehive:4567",
- JSONLD_TYPE to listOf("Beehive")
- )
- ).right()
-
- webClient.get()
- .uri("/ngsi-ld/v1/entities/$beehiveId")
- .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
- .exchange()
- .expectStatus().isOk
- .expectBody().json(
- """
- {
- "id":"urn:ngsi-ld:Beehive:4567",
- "type": "Beehive",
- "managedBy": {
- "type": "Relationship",
- "datasetId":"urn:ngsi-ld:Dataset:managedBy:0215",
- "object":"urn:ngsi-ld:Beekeeper:1230"
- },
- "@context": "${applicationProperties.contexts.core}"
- }
- """.trimIndent()
- )
- }
-
@Test
fun `get entity by id should include createdAt & modifiedAt if query param sysAttrs is present`() {
- initializeRetrieveEntityMocks()
- coEvery { entityQueryService.queryEntity(any(), MOCK_USER_SUB) } returns ExpandedEntity(
+ mockEntitySourceSuccess(
mapOf(
- "https://uri.etsi.org/ngsi-ld/default-context/managedBy" to
+ "managedBy" to
mapOf(
- JSONLD_TYPE to "https://uri.etsi.org/ngsi-ld/Relationship",
- NGSILD_RELATIONSHIP_OBJECT to mapOf(
- JSONLD_ID to "urn:ngsi-ld:Beekeeper:1230"
+ JSONLD_TYPE_TERM to "Relationship",
+ JSONLD_OBJECT to "urn:ngsi-ld:Beekeeper:1230",
+ NGSILD_DATASET_ID_TERM to mapOf(
+ JSONLD_ID_TERM to "urn:ngsi-ld:Dataset:managedBy:0215"
),
- NGSILD_DATASET_ID_PROPERTY to mapOf(
- JSONLD_ID to "urn:ngsi-ld:Dataset:managedBy:0215"
- ),
- NGSILD_CREATED_AT_PROPERTY to
- mapOf(
- "@type" to NGSILD_DATE_TIME_TYPE,
- "@value" to Instant.parse("2015-10-18T11:20:30.000001Z").atZone(ZoneOffset.UTC)
- ),
- NGSILD_MODIFIED_AT_PROPERTY to
- mapOf(
- "@type" to NGSILD_DATE_TIME_TYPE,
- "@value" to Instant.parse("2015-10-18T12:20:30.000001Z").atZone(ZoneOffset.UTC)
- )
+ NGSILD_CREATED_AT_TERM to
+ Instant.parse("2015-10-18T13:20:30.000001Z").atZone(ZoneOffset.UTC),
+ NGSILD_MODIFIED_AT_TERM to
+ Instant.parse("2015-10-18T14:20:30.000001Z").atZone(ZoneOffset.UTC)
),
- JSONLD_ID to "urn:ngsi-ld:Beehive:4567",
- JSONLD_TYPE to listOf("Beehive")
+ JSONLD_ID_TERM to "urn:ngsi-ld:Beehive:4567",
+ JSONLD_TYPE_TERM to "Beehive",
+ NGSILD_CREATED_AT_TERM to Instant.parse("2015-10-18T11:20:30.000001Z").atZone(ZoneOffset.UTC),
+ NGSILD_MODIFIED_AT_TERM to Instant.parse("2015-10-18T12:20:30.000001Z").atZone(ZoneOffset.UTC)
)
).right()
@@ -823,71 +471,26 @@ class EntityHandlerTests {
.exchange()
.expectStatus().isOk
.expectBody()
- .jsonPath("$..createdAt").isEqualTo("2015-10-18T11:20:30.000001Z")
- .jsonPath("$..modifiedAt").isEqualTo("2015-10-18T12:20:30.000001Z")
- }
-
- @Test
- fun `get entity by id should correctly serialize multi-attribute relationship having more than one instance`() {
- initializeRetrieveEntityMocks()
- coEvery { entityQueryService.queryEntity(any(), MOCK_USER_SUB) } returns ExpandedEntity(
- mapOf(
- "https://uri.etsi.org/ngsi-ld/default-context/managedBy" to
- listOf(
- mapOf(
- JSONLD_TYPE to "https://uri.etsi.org/ngsi-ld/Relationship",
- NGSILD_RELATIONSHIP_OBJECT to mapOf(
- JSONLD_ID to "urn:ngsi-ld:Beekeeper:1229"
- )
- ),
- mapOf(
- JSONLD_TYPE to "https://uri.etsi.org/ngsi-ld/Relationship",
- NGSILD_RELATIONSHIP_OBJECT to mapOf(
- JSONLD_ID to "urn:ngsi-ld:Beekeeper:1230"
- ),
- NGSILD_DATASET_ID_PROPERTY to mapOf(
- JSONLD_ID to "urn:ngsi-ld:Dataset:managedBy:0215"
- )
- )
- ),
- JSONLD_ID to "urn:ngsi-ld:Beehive:4567",
- JSONLD_TYPE to listOf("Beehive")
- )
- ).right()
-
- webClient.get()
- .uri("/ngsi-ld/v1/entities/$beehiveId")
- .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
- .exchange()
- .expectStatus().isOk
- .expectBody().json(
+ .json(
"""
- {
- "id":"urn:ngsi-ld:Beehive:4567",
- "type": "Beehive",
- "managedBy":[
- {
- "type": "Relationship",
- "object":"urn:ngsi-ld:Beekeeper:1229"
- },
- {
- "type": "Relationship",
- "datasetId":"urn:ngsi-ld:Dataset:managedBy:0215",
- "object":"urn:ngsi-ld:Beekeeper:1230"
- }
- ],
- "@context": "${applicationProperties.contexts.core}"
+ {
+ createdAt: "2015-10-18T11:20:30.000001Z",
+ modifiedAt: "2015-10-18T12:20:30.000001Z",
+ managedBy: {
+ createdAt: "2015-10-18T13:20:30.000001Z",
+ modifiedAt: "2015-10-18T14:20:30.000001Z"
+ }
}
""".trimIndent()
)
}
@Test
- fun `get entity by id should return 404 when entity does not exist`() {
- initializeRetrieveEntityMocks()
+ fun `get entity by id should return the received error`() {
coEvery {
- entityQueryService.queryEntity(any(), MOCK_USER_SUB)
- } returns ResourceNotFoundException(entityNotFoundMessage("urn:ngsi-ld:BeeHive:TEST")).left()
+ entitySourceService.getEntityFromSources(MOCK_USER_SUB, any(), any(), any(), any(), any())
+ } returns
+ (ResourceNotFoundException(entityNotFoundMessage("urn:ngsi-ld:BeeHive:TEST")).left() to emptyList())
webClient.get()
.uri("/ngsi-ld/v1/entities/urn:ngsi-ld:BeeHive:TEST")
@@ -906,85 +509,79 @@ class EntityHandlerTests {
}
@Test
- fun `get entity by id should return 403 if user is not authorized to read an entity`() {
- initializeRetrieveEntityMocks()
+ fun `get entity by id should return the warnings for success and errors`() {
+ val csr = gimmeRawCSR()
coEvery {
- entityQueryService.queryEntity("urn:ngsi-ld:BeeHive:TEST".toUri(), sub.getOrNull())
- } returns AccessDeniedException("User forbidden read access to entity urn:ngsi-ld:BeeHive:TEST").left()
+ entitySourceService.getEntityFromSources(
+ MOCK_USER_SUB,
+ any(),
+ any(),
+ any(),
+ any(),
+ any()
+ )
+ } returns
+ (
+ ResourceNotFoundException("no entity").left() to listOf(
+ MiscellaneousWarning(
+ "message with\nline\nbreaks",
+ csr
+ ),
+ MiscellaneousWarning("message", csr)
+ )
+ ) andThen (
+ emptyList().right() to listOf(
+ MiscellaneousWarning(
+ "message with\nline\nbreaks",
+ csr
+ ),
+ MiscellaneousWarning("message", csr)
+ )
+ )
webClient.get()
.uri("/ngsi-ld/v1/entities/urn:ngsi-ld:BeeHive:TEST")
.header(HttpHeaders.LINK, AQUAC_HEADER_LINK)
.exchange()
- .expectStatus().isForbidden
- .expectBody().json(
- """
- {
- "type": "https://uri.etsi.org/ngsi-ld/errors/AccessDenied",
- "title": "User forbidden read access to entity urn:ngsi-ld:BeeHive:TEST",
- "detail": "$DEFAULT_DETAIL"
- }
- """.trimIndent()
+ .expectStatus().isNotFound
+ .expectHeader().valueEquals(
+ NGSILDWarning.HEADER_NAME,
+ "199 urn:ngsi-ld:ContextSourceRegistration:test \"message with line breaks\"",
+ "199 urn:ngsi-ld:ContextSourceRegistration:test \"message\""
)
- }
- @Test
- fun `get entity by id should return the warnings sent by the CSRs and update the CSRs statuses`() {
- val csr = gimmeRawCSR()
- coEvery {
- entityQueryService.queryEntity("urn:ngsi-ld:BeeHive:TEST".toUri(), sub.getOrNull())
- } returns ResourceNotFoundException("no entity").left()
-
- coEvery {
- contextSourceRegistrationService
- .getContextSourceRegistrations(any(), any(), any())
- } returns listOf(csr, csr)
-
- mockkObject(ContextSourceCaller) {
- coEvery {
- ContextSourceCaller.retrieveContextSourceEntity(any(), any(), any(), any())
- } returns MiscellaneousWarning(
- "message with\nline\nbreaks",
- csr
- ).left() andThen
- MiscellaneousWarning("message", csr).left()
-
- coEvery { contextSourceRegistrationService.updateContextSourceStatus(any(), any()) } returns Unit
- webClient.get()
- .uri("/ngsi-ld/v1/entities/urn:ngsi-ld:BeeHive:TEST")
- .header(HttpHeaders.LINK, AQUAC_HEADER_LINK)
- .exchange()
- .expectStatus().isNotFound
- .expectHeader().valueEquals(
- NGSILDWarning.HEADER_NAME,
- "199 urn:ngsi-ld:ContextSourceRegistration:test \"message with line breaks\"",
- "199 urn:ngsi-ld:ContextSourceRegistration:test \"message\""
- )
-
- coVerify(exactly = 2) { contextSourceRegistrationService.updateContextSourceStatus(any(), false) }
- }
+ webClient.get()
+ .uri("/ngsi-ld/v1/entities/urn:ngsi-ld:BeeHive:TEST")
+ .header(HttpHeaders.LINK, AQUAC_HEADER_LINK)
+ .exchange()
+ .expectStatus().isOk
+ .expectHeader().valueEquals(
+ NGSILDWarning.HEADER_NAME,
+ "199 urn:ngsi-ld:ContextSourceRegistration:test \"message with line breaks\"",
+ "199 urn:ngsi-ld:ContextSourceRegistration:test \"message\""
+ )
}
- fun initializeQueryEntitiesMocks() {
- val compactedEntities = slot>()
-
+ fun mockGetEntitiesFromSourceSuccess(
+ entities: List,
+ count: Int = 0,
+ warnings: List = emptyList()
+ ) {
coEvery {
- linkedEntityService.processLinkedEntities(capture(compactedEntities), any(), any())
- } answers {
- compactedEntities.captured.right()
- }
+ entitySourceService.getEntitiesFromSources(any(), any(), any(), any(), any())
+ } returns Triple(entities, count, warnings).right()
}
@Test
fun `get entities by type should not include temporal properties if query param sysAttrs is not present`() {
- initializeQueryEntitiesMocks()
- coEvery { entityQueryService.queryEntities(any(), any()) } returns Pair(
+ mockGetEntitiesFromSourceSuccess(
listOf(
- ExpandedEntity(
- mapOf(
- "@id" to beehiveId.toString(),
- "@type" to listOf("Beehive")
- )
+ mapOf(
+ "id" to beehiveId.toString(),
+ "type" to "Beehive",
+ NGSILD_CREATED_AT_TERM to ngsiLdDateTime(),
+ NGSILD_MODIFIED_AT_TERM to ngsiLdDateTime(),
+ "@context" to applicationProperties.contexts.core
)
),
1
@@ -1011,29 +608,15 @@ class EntityHandlerTests {
}
@Test
- fun `get entities by type should include temporal properties if optional query param sysAttrs is present`() {
- initializeQueryEntitiesMocks()
- coEvery {
- entityQueryService.queryEntities(
- EntitiesQueryFromGet(
- typeSelection = "https://uri.etsi.org/ngsi-ld/default-context/Beehive",
- paginationQuery = PaginationQuery(offset = 0, limit = 30),
- contexts = listOf(applicationProperties.contexts.core)
- ),
- any()
- )
- } returns Pair(
+ fun `get entities by type should include temporal properties if query param sysAttrs is present`() {
+ mockGetEntitiesFromSourceSuccess(
listOf(
- ExpandedEntity(
- mapOf(
- NGSILD_CREATED_AT_PROPERTY to
- mapOf(
- "@type" to NGSILD_DATE_TIME_TYPE,
- "@value" to Instant.parse("2015-10-18T11:20:30.000001Z").atZone(ZoneOffset.UTC)
- ),
- "@id" to beehiveId.toString(),
- "@type" to listOf("Beehive")
- )
+ mapOf(
+ "id" to beehiveId.toString(),
+ "type" to "Beehive",
+ NGSILD_CREATED_AT_TERM to Instant.parse("2015-10-18T11:20:30.000001Z").atZone(ZoneOffset.UTC),
+ NGSILD_MODIFIED_AT_TERM to Instant.parse("2015-10-18T11:20:30.000001Z").atZone(ZoneOffset.UTC),
+ "@context" to applicationProperties.contexts.core
)
),
1
@@ -1051,6 +634,7 @@ class EntityHandlerTests {
"id": "$beehiveId",
"type": "Beehive",
"createdAt":"2015-10-18T11:20:30.000001Z",
+ "modifiedAt":"2015-10-18T11:20:30.000001Z",
"@context": "${applicationProperties.contexts.core}"
}
]
@@ -1060,15 +644,16 @@ class EntityHandlerTests {
@Test
fun `get entities should return 200 with prev and next link header if exists`() {
- initializeQueryEntitiesMocks()
- coEvery { entityQueryService.queryEntities(any(), any()) } returns Pair(
+ mockGetEntitiesFromSourceSuccess(
listOf(
- ExpandedEntity(
- mapOf("@id" to "urn:ngsi-ld:Beehive:TESTC", "@type" to listOf("Beehive"))
+ mapOf(
+ "id" to "urn:ngsi-ld:Beehive:TESTC",
+ "type" to "Beehive",
+ "@context" to applicationProperties.contexts.core
)
),
3
- ).right()
+ )
webClient.get()
.uri(
@@ -1101,10 +686,7 @@ class EntityHandlerTests {
@Test
fun `get entities should return 200 and empty response if requested offset does not exists`() {
- initializeQueryEntitiesMocks()
- coEvery {
- entityQueryService.queryEntities(any(), any())
- } returns Pair(emptyList(), 0).right()
+ mockGetEntitiesFromSourceSuccess(emptyList())
webClient.get()
.uri("/ngsi-ld/v1/entities/?type=Beehive&limit=1&offset=9")
@@ -1149,24 +731,12 @@ class EntityHandlerTests {
@Test
fun `get entities with id and type should return 200`() {
- initializeQueryEntitiesMocks()
- coEvery {
- entityQueryService.queryEntities(
- EntitiesQueryFromGet(
- ids = setOf(beehiveId),
- typeSelection = BEEHIVE_TYPE,
- paginationQuery = PaginationQuery(offset = 0, limit = 30),
- contexts = APIC_COMPOUND_CONTEXTS
- ),
- any()
- )
- } returns Pair(
+ mockGetEntitiesFromSourceSuccess(
listOf(
- ExpandedEntity(
- mapOf(
- "@id" to beehiveId.toString(),
- "@type" to listOf("Beehive")
- )
+ mapOf(
+ "id" to beehiveId.toString(),
+ "type" to "Beehive",
+ "@context" to APIC_COMPOUND_CONTEXT
)
),
1
@@ -1193,11 +763,7 @@ class EntityHandlerTests {
@Test
fun `get entities should return 200 and the number of results`() {
- initializeQueryEntitiesMocks()
- coEvery {
- entityQueryService.queryEntities(any(), any())
- } returns Pair(emptyList(), 3).right()
-
+ mockGetEntitiesFromSourceSuccess(emptyList(), 3)
webClient.get()
.uri("/ngsi-ld/v1/entities/?type=Beehive&limit=0&offset=1&count=true")
.exchange()
@@ -1225,10 +791,7 @@ class EntityHandlerTests {
@Test
fun `get entities should allow a query not including a type request parameter`() {
- initializeQueryEntitiesMocks()
- coEvery {
- entityQueryService.queryEntities(any(), any())
- } returns Pair(emptyList(), 0).right()
+ mockGetEntitiesFromSourceSuccess(emptyList())
webClient.get()
.uri("/ngsi-ld/v1/entities/?attrs=myProp")
@@ -1256,42 +819,31 @@ class EntityHandlerTests {
}
@Test
- fun `get entities should return the warnings sent by the CSRs and update the CSRs statuses`() {
+ fun `get entities should return the received warnings`() {
val csr = gimmeRawCSR()
- initializeQueryEntitiesMocks()
- coEvery {
- entityQueryService.queryEntities(any(), sub.getOrNull())
- } returns (emptyList() to 0).right()
+ mockGetEntitiesFromSourceSuccess(
+ emptyList(),
+ warnings = listOf(
+ MiscellaneousWarning(
+ "message with\nline\nbreaks",
+ csr
+ ),
+ MiscellaneousWarning("message", csr)
+ )
+ )
- coEvery {
- contextSourceRegistrationService
- .getContextSourceRegistrations(any(), any(), any())
- } returns listOf(csr, csr)
-
- mockkObject(ContextSourceCaller) {
- coEvery {
- ContextSourceCaller.queryContextSourceEntities(any(), any(), any())
- } returns MiscellaneousWarning(
- "message with\nline\nbreaks",
- csr
- ).left() andThen
- MiscellaneousWarning("message", csr).left()
-
- coEvery { contextSourceRegistrationService.updateContextSourceStatus(any(), any()) } returns Unit
- webClient.get()
- .uri("/ngsi-ld/v1/entities?type=$BEEHIVE_COMPACT_TYPE&count=true")
- .header(HttpHeaders.LINK, AQUAC_HEADER_LINK)
- .exchange()
- .expectStatus().isOk
- .expectHeader().valueEquals(
- NGSILDWarning.HEADER_NAME,
- "199 urn:ngsi-ld:ContextSourceRegistration:test \"message with line breaks\"",
- "199 urn:ngsi-ld:ContextSourceRegistration:test \"message\""
- ).expectHeader().valueEquals(RESULTS_COUNT_HEADER, "0")
-
- coVerify(exactly = 2) { contextSourceRegistrationService.updateContextSourceStatus(any(), false) }
- }
+ coEvery { contextSourceRegistrationService.updateContextSourceStatus(any(), any()) } returns Unit
+ webClient.get()
+ .uri("/ngsi-ld/v1/entities?type=$BEEHIVE_COMPACT_TYPE&count=true")
+ .header(HttpHeaders.LINK, AQUAC_HEADER_LINK)
+ .exchange()
+ .expectStatus().isOk
+ .expectHeader().valueEquals(
+ NGSILDWarning.HEADER_NAME,
+ "199 urn:ngsi-ld:ContextSourceRegistration:test \"message with line breaks\"",
+ "199 urn:ngsi-ld:ContextSourceRegistration:test \"message\""
+ ).expectHeader().valueEquals(RESULTS_COUNT_HEADER, "0")
}
@Test
@@ -1503,7 +1055,8 @@ class EntityHandlerTests {
@Test
fun `append entity attribute should return a 207 if types or attributes could not be appended`() {
- val jsonLdFile = ClassPathResource("/ngsild/aquac/fragments/BreedingService_newInvalidTypeAndAttribute.json")
+ val jsonLdFile =
+ ClassPathResource("/ngsild/aquac/fragments/BreedingService_newInvalidTypeAndAttribute.json")
val entityId = "urn:ngsi-ld:BreedingService:0214".toUri()
coEvery {
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 58681c694..83d4468d8 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
@@ -98,26 +98,6 @@ object JsonLdUtils {
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)
- val JSONLD_EXPANDED_ENTITY_CORE_MEMBERS =
- setOf(
- JSONLD_ID,
- JSONLD_TYPE,
- JSONLD_CONTEXT,
- NGSILD_SCOPE_PROPERTY,
- NGSILD_CREATED_AT_PROPERTY,
- NGSILD_MODIFIED_AT_PROPERTY
- )
- val JSONLD_COMPACTED_ENTITY_CORE_MEMBERS =
- setOf(
- JSONLD_ID_TERM,
- JSONLD_TYPE_TERM,
- JSONLD_CONTEXT,
- NGSILD_SCOPE_TERM,
- NGSILD_CREATED_AT_TERM,
- NGSILD_MODIFIED_AT_TERM
- )
-
const val NGSILD_CREATED_AT_TERM = "createdAt"
const val NGSILD_MODIFIED_AT_TERM = "modifiedAt"
val NGSILD_SYSATTRS_TERMS = setOf(NGSILD_CREATED_AT_TERM, NGSILD_MODIFIED_AT_TERM, NGSILD_DELETED_AT_TERM)
@@ -148,9 +128,31 @@ object JsonLdUtils {
const val NGSILD_CSR_TERM = "ContextSourceRegistration"
const val NGSILD_DATE_TIME_TYPE = "https://uri.etsi.org/ngsi-ld/DateTime"
+ const val NGSILD_DATE_TIME_TERM = "DateTime"
+
const val NGSILD_DATE_TYPE = "https://uri.etsi.org/ngsi-ld/Date"
const val NGSILD_TIME_TYPE = "https://uri.etsi.org/ngsi-ld/Time"
+ // List of members that are part of a core entity base definition (i.e., without attributes)
+ val JSONLD_EXPANDED_ENTITY_CORE_MEMBERS =
+ setOf(
+ JSONLD_ID,
+ JSONLD_TYPE,
+ JSONLD_CONTEXT,
+ NGSILD_SCOPE_PROPERTY,
+ NGSILD_CREATED_AT_PROPERTY,
+ NGSILD_MODIFIED_AT_PROPERTY
+ )
+ val JSONLD_COMPACTED_ENTITY_CORE_MEMBERS =
+ setOf(
+ JSONLD_ID_TERM,
+ JSONLD_TYPE_TERM,
+ JSONLD_CONTEXT,
+ NGSILD_SCOPE_TERM,
+ NGSILD_CREATED_AT_TERM,
+ NGSILD_MODIFIED_AT_TERM
+ )
+
val logger: Logger = LoggerFactory.getLogger(javaClass)
private const val CONTEXT_CACHE_CAPACITY = 128