diff --git a/search-service/config/detekt/baseline.xml b/search-service/config/detekt/baseline.xml
index 8dbac235f..7f380d5b4 100644
--- a/search-service/config/detekt/baseline.xml
+++ b/search-service/config/detekt/baseline.xml
@@ -32,7 +32,6 @@
LongParameterList:TemporalEntityHandler.kt$TemporalEntityHandler$( @RequestHeader httpHeaders: HttpHeaders, @PathVariable entityId: URI, @PathVariable attrId: String, @PathVariable instanceId: URI, @RequestBody requestBody: Mono<String>, @AllowedParameters(notImplemented = [QP.LOCAL, QP.VIA]) @RequestParam queryParams: MultiValueMap<String, String> )
LongParameterList:V0_29__JsonLd_migration.kt$V0_29__JsonLd_migration$( entityId: URI, attributeName: ExpandedTerm, datasetId: URI?, attributePayload: ExpandedAttributeInstance, ngsiLdAttributeInstance: NgsiLdAttributeInstance, defaultCreatedAt: ZonedDateTime )
NestedBlockDepth:V0_29__JsonLd_migration.kt$V0_29__JsonLd_migration$override fun migrate(context: Context)
- SpacingBetweenPackageAndImports:ContextSourceCaller.kt$
SwallowedException:TemporalQueryUtils.kt$e: IllegalArgumentException
diff --git a/search-service/src/main/kotlin/com/egm/stellio/search/csr/service/ContextSourceCaller.kt b/search-service/src/main/kotlin/com/egm/stellio/search/csr/service/ContextSourceCaller.kt
index fc48408a9..c97d17ee4 100644
--- a/search-service/src/main/kotlin/com/egm/stellio/search/csr/service/ContextSourceCaller.kt
+++ b/search-service/src/main/kotlin/com/egm/stellio/search/csr/service/ContextSourceCaller.kt
@@ -26,7 +26,9 @@ import org.springframework.web.reactive.function.client.WebClient
import org.springframework.web.reactive.function.client.awaitBodyOrNull
import org.springframework.web.reactive.function.client.awaitExchange
import java.net.URI
+
typealias QueryEntityResponse = Pair, Int?>
+
object ContextSourceCaller {
val logger: Logger = LoggerFactory.getLogger(javaClass)
diff --git a/search-service/src/main/kotlin/com/egm/stellio/search/csr/service/ContextSourceUtils.kt b/search-service/src/main/kotlin/com/egm/stellio/search/csr/service/ContextSourceUtils.kt
index f35257dd6..54979973e 100644
--- a/search-service/src/main/kotlin/com/egm/stellio/search/csr/service/ContextSourceUtils.kt
+++ b/search-service/src/main/kotlin/com/egm/stellio/search/csr/service/ContextSourceUtils.kt
@@ -35,14 +35,15 @@ object ContextSourceUtils {
localEntities: List,
remoteEntitiesWithCSR: List
): IorNel> {
- val mergedEntityMap = localEntities.associateBy { it[JSONLD_ID_TERM] }.toMutableMap()
+ val mergedEntityMap = localEntities.map { it.toMutableMap() }.associateBy { it[JSONLD_ID_TERM] }.toMutableMap()
- val warnings = remoteEntitiesWithCSR.mapNotNull { (entities, csr) ->
+ val warnings = remoteEntitiesWithCSR.sortedBy { (_, csr) -> csr.isAuxiliary() }.mapNotNull { (entities, csr) ->
val test = either {
entities.forEach { entity ->
val id = entity[JSONLD_ID_TERM]
- mergedEntityMap[id] = mergedEntityMap[id]
- ?.let { getMergeNewValues(it, entity, csr).bind() } ?: entity
+ mergedEntityMap[id]
+ ?.let { it.putAll(getMergeNewValues(it, entity, csr).bind()) }
+ ?: run { mergedEntityMap[id] = entity.toMutableMap() }
}
null
}
diff --git a/search-service/src/test/kotlin/com/egm/stellio/search/csr/service/ContextSourceCallerTests.kt b/search-service/src/test/kotlin/com/egm/stellio/search/csr/service/ContextSourceCallerTests.kt
index 086ef0fd6..66dc453a9 100644
--- a/search-service/src/test/kotlin/com/egm/stellio/search/csr/service/ContextSourceCallerTests.kt
+++ b/search-service/src/test/kotlin/com/egm/stellio/search/csr/service/ContextSourceCallerTests.kt
@@ -9,6 +9,7 @@ import com.egm.stellio.shared.util.APIC_COMPOUND_CONTEXT
import com.egm.stellio.shared.util.GEO_JSON_CONTENT_TYPE
import com.egm.stellio.shared.util.GEO_JSON_MEDIA_TYPE
import com.egm.stellio.shared.util.JsonUtils.serializeObject
+import com.egm.stellio.shared.util.RESULTS_COUNT_HEADER
import com.egm.stellio.shared.util.assertJsonPayloadsAreEqual
import com.egm.stellio.shared.util.toUri
import com.github.tomakehurst.wiremock.client.WireMock.get
@@ -23,7 +24,9 @@ import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo
import com.github.tomakehurst.wiremock.client.WireMock.verify
import com.github.tomakehurst.wiremock.junit5.WireMockTest
import kotlinx.coroutines.test.runTest
+import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertInstanceOf
+import org.junit.jupiter.api.Assertions.assertNotNull
import org.junit.jupiter.api.Assertions.assertNull
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test
@@ -71,6 +74,55 @@ class ContextSourceCallerTests {
}
""".trimIndent()
+ @Test
+ fun `queryContextSourceEntities should return the count and the entities when the request succeed`() = runTest {
+ val csr = gimmeRawCSR()
+ val path = "/ngsi-ld/v1/entities"
+ val count = 222
+ val payload = "[$entityWithSysAttrs, $entityWithSysAttrs]"
+ stubFor(
+ get(urlMatching(path))
+ .willReturn(
+ ok()
+ .withHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE)
+ .withHeader(RESULTS_COUNT_HEADER, count.toString()).withBody(payload)
+ )
+ )
+
+ val response = ContextSourceCaller.queryContextSourceEntities(
+ HttpHeaders.EMPTY,
+ csr,
+ true,
+ emptyParams
+ ).getOrNull()
+ assertNotNull(response)
+ assertEquals(count, response!!.second)
+ assertJsonPayloadsAreEqual(entityWithSysAttrs, serializeObject(response.first.first()))
+ }
+
+ @Test
+ fun `queryContextSourceEntities should return a RevalidationFailedWarning when receiving bad payload`() = runTest {
+ val csr = gimmeRawCSR()
+ val path = "/ngsi-ld/v1/entities"
+ stubFor(
+ get(urlMatching(path))
+ .willReturn(
+ ok()
+ .withHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE).withBody(entityWithSysAttrs)
+ )
+ )
+
+ val response = ContextSourceCaller.queryContextSourceEntities(
+ HttpHeaders.EMPTY,
+ csr,
+ false,
+ emptyParams
+ )
+
+ assertTrue(response.isLeft())
+ assertInstanceOf(RevalidationFailedWarning::class.java, response.leftOrNull())
+ }
+
@Test
fun `retrieveContextSourceEntity should return the entity when the request succeed`() = runTest {
val csr = gimmeRawCSR()
diff --git a/search-service/src/test/kotlin/com/egm/stellio/search/csr/service/ContextSourceUtilsTests.kt b/search-service/src/test/kotlin/com/egm/stellio/search/csr/service/ContextSourceUtilsTests.kt
index f2ff972b1..c78ba6771 100644
--- a/search-service/src/test/kotlin/com/egm/stellio/search/csr/service/ContextSourceUtilsTests.kt
+++ b/search-service/src/test/kotlin/com/egm/stellio/search/csr/service/ContextSourceUtilsTests.kt
@@ -7,6 +7,7 @@ import com.egm.stellio.search.csr.model.MiscellaneousWarning
import com.egm.stellio.search.csr.model.Mode
import com.egm.stellio.shared.model.CompactedAttributeInstance
import com.egm.stellio.shared.model.CompactedEntity
+import com.egm.stellio.shared.util.JsonLdUtils.JSONLD_ID_TERM
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
@@ -230,4 +231,73 @@ class ContextSourceUtilsTests {
assertEquals(entityWithNameAndSurname, entity)
}
}
+
+ @Test
+ fun `merge entitiesList should merge entities with the same id`() = runTest {
+ val mergedEntity = ContextSourceUtils.mergeEntitiesLists(
+ listOf(entityWithName),
+ listOf(listOf(entityWithLastName) to auxiliaryCSR, listOf(entityWithSurName) to inclusiveCSR)
+ ).getOrNull()
+ assertEquals(1, mergedEntity!!.size)
+ assertEquals(entityWithName + entityWithLastName + entityWithSurName, mergedEntity.first())
+ }
+
+ @Test
+ fun `merge entitiesList should not keep conflicting data from an auxiliary csr`() = runTest {
+ val mergedEntity = ContextSourceUtils.mergeEntitiesLists(
+ listOf(moreRecentEntity),
+ listOf(listOf(evenMoreRecentEntity) to auxiliaryCSR, listOf(entityWithName) to inclusiveCSR)
+ ).getOrNull()
+ assertEquals(1, mergedEntity!!.size)
+ assertEquals(moreRecentEntity, mergedEntity.first())
+ }
+
+ @Test
+ fun `merge entitiesList should add entities with the different ids`() = runTest {
+ val entityWithDifferentId = minimalEntity.toMutableMap() + (JSONLD_ID_TERM to "differentId")
+ val entityWithAnotherDifferentId = minimalEntity.toMutableMap() + (JSONLD_ID_TERM to "anotherDifferentId")
+ val mergedEntity = ContextSourceUtils.mergeEntitiesLists(
+ listOf(entityWithName),
+ listOf(listOf(entityWithDifferentId) to inclusiveCSR, listOf(entityWithAnotherDifferentId) to inclusiveCSR)
+ ).getOrNull()
+ assertThat(mergedEntity)
+ .hasSize(3)
+ .contains(entityWithName, entityWithDifferentId, entityWithAnotherDifferentId)
+ }
+
+ @Test
+ fun `merge entitiesList should merge using getMergeNewValues and return the received warnings`() = runTest {
+ val warning1 = MiscellaneousWarning("1", inclusiveCSR)
+ val warning2 = MiscellaneousWarning("2", inclusiveCSR)
+ mockkObject(ContextSourceUtils) {
+ every { ContextSourceUtils.getMergeNewValues(any(), any(), any()) } returns
+ warning1.left() andThen warning2.left()
+
+ val (warnings, entity) = ContextSourceUtils.mergeEntitiesLists(
+ listOf(entityWithName),
+ listOf(listOf(entityWithName) to inclusiveCSR, listOf(entityWithName) to inclusiveCSR)
+ ).toPair()
+ verify(exactly = 2) { ContextSourceUtils.getMergeNewValues(any(), any(), any()) }
+ assertThat(warnings).hasSize(2).contains(warning1, warning2)
+ assertEquals(listOf(entityWithName), entity)
+ }
+ }
+
+ @Test
+ fun `merge entitiesList should not merge List in error`() = runTest {
+ mockkObject(ContextSourceUtils) {
+ val (warnings, entity) = ContextSourceUtils.mergeEntitiesLists(
+ listOf(entityWithName),
+ listOf(
+ listOf(invalidEntityWithLastName) to inclusiveCSR,
+ listOf(entityWithSurName) to inclusiveCSR
+ )
+ ).toPair()
+
+ verify(exactly = 2) { ContextSourceUtils.getMergeNewValues(any(), any(), any()) }
+ assertThat(warnings).hasSize(1)
+ val entityWithNameAndSurname = entityWithName.plus("surName" to nameAttribute)
+ assertEquals(listOf(entityWithNameAndSurname), entity)
+ }
+ }
}