From b608433a4db72616ef2d84bd761654a747594261 Mon Sep 17 00:00:00 2001 From: ranim-n Date: Wed, 28 Aug 2024 15:40:32 +0200 Subject: [PATCH 1/7] feat: add support for jsonldContext member in subscriptions --- .../config/detekt/baseline.xml | 2 + .../subscription/model/Subscription.kt | 4 +- .../service/NotificationService.kt | 4 +- .../service/SubscriptionService.kt | 22 ++++++---- .../V0_30__add_jsonld_context_column.sql | 2 + .../service/NotificationServiceTests.kt | 43 ++++++++++++++++++- .../service/SubscriptionServiceTests.kt | 3 +- .../resources/ngsild/subscription_full.json | 3 +- 8 files changed, 69 insertions(+), 14 deletions(-) create mode 100644 subscription-service/src/main/resources/db/migration/V0_30__add_jsonld_context_column.sql diff --git a/subscription-service/config/detekt/baseline.xml b/subscription-service/config/detekt/baseline.xml index eab1783a5..c8160769b 100644 --- a/subscription-service/config/detekt/baseline.xml +++ b/subscription-service/config/detekt/baseline.xml @@ -6,6 +6,8 @@ LongMethod:EntityEventListenerService.kt$EntityEventListenerService$internal suspend fun dispatchEntityEvent(content: String) LongMethod:SubscriptionService.kt$SubscriptionService$@Transactional suspend fun update( subscriptionId: URI, input: Map<String, Any>, contexts: List<String> ): Either<APIException, Unit> LongParameterList:FixtureUtils.kt$( withQueryAndGeoQuery: Pair<Boolean, Boolean> = Pair(true, true), withEndpointReceiverInfo: Boolean = true, withNotifParams: Pair<FormatType, List<String>> = Pair(FormatType.NORMALIZED, emptyList()), withModifiedAt: Boolean = false, georel: String = "within", geometry: String = "Polygon", coordinates: String = "[[[100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0]]]", timeInterval: Int? = null, contexts: List<String> = listOf(NGSILD_TEST_CORE_CONTEXT) ) + MaxLineLength:NotificationServiceTests.kt$NotificationServiceTests$fun + MaximumLineLength:NotificationServiceTests.kt$NotificationServiceTests$ TooGenericExceptionCaught:SubscriptionService.kt$SubscriptionService$e: Exception TooManyFunctions:SubscriptionService.kt$SubscriptionService diff --git a/subscription-service/src/main/kotlin/com/egm/stellio/subscription/model/Subscription.kt b/subscription-service/src/main/kotlin/com/egm/stellio/subscription/model/Subscription.kt index 7e477ba51..6f63a1e3f 100644 --- a/subscription-service/src/main/kotlin/com/egm/stellio/subscription/model/Subscription.kt +++ b/subscription-service/src/main/kotlin/com/egm/stellio/subscription/model/Subscription.kt @@ -53,7 +53,9 @@ data class Subscription( val throttling: Int? = null, val lang: String? = null, @JsonInclude(value = JsonInclude.Include.NON_EMPTY) - val datasetId: List? = null + val datasetId: List? = null, + @JsonInclude(value = JsonInclude.Include.NON_EMPTY) + val jsonldContext: URI? = null ) { @Transient diff --git a/subscription-service/src/main/kotlin/com/egm/stellio/subscription/service/NotificationService.kt b/subscription-service/src/main/kotlin/com/egm/stellio/subscription/service/NotificationService.kt index 17b0488e2..0ba1b0eac 100644 --- a/subscription-service/src/main/kotlin/com/egm/stellio/subscription/service/NotificationService.kt +++ b/subscription-service/src/main/kotlin/com/egm/stellio/subscription/service/NotificationService.kt @@ -49,9 +49,11 @@ class NotificationService( AttributeRepresentation.SIMPLIFIED else AttributeRepresentation.NORMALIZED + val context = it.jsonldContext?.toString()?.let { listOf(it) } ?: it.contexts + val compactedEntity = compactEntity( ExpandedEntity(filteredEntity), - it.contexts + context ).toFinalRepresentation( NgsiLdDataRepresentation( entityRepresentation, diff --git a/subscription-service/src/main/kotlin/com/egm/stellio/subscription/service/SubscriptionService.kt b/subscription-service/src/main/kotlin/com/egm/stellio/subscription/service/SubscriptionService.kt index e24b5f433..0aaa10771 100644 --- a/subscription-service/src/main/kotlin/com/egm/stellio/subscription/service/SubscriptionService.kt +++ b/subscription-service/src/main/kotlin/com/egm/stellio/subscription/service/SubscriptionService.kt @@ -149,11 +149,11 @@ class SubscriptionService( INSERT INTO subscription(id, type, subscription_name, created_at, description, watched_attributes, notification_trigger, time_interval, q, scope_q, notif_attributes, notif_format, endpoint_uri, endpoint_accept, endpoint_receiver_info, endpoint_notifier_info, times_sent, is_active, - expires_at, sub, contexts, throttling, sys_attrs, lang, datasetId) + expires_at, sub, contexts, throttling, sys_attrs, lang, datasetId, jsonld_context) VALUES(:id, :type, :subscription_name, :created_at, :description, :watched_attributes, :notification_trigger, :time_interval, :q, :scope_q, :notif_attributes, :notif_format, :endpoint_uri, :endpoint_accept, :endpoint_receiver_info, :endpoint_notifier_info, :times_sent, :is_active, - :expires_at, :sub, :contexts, :throttling, :sys_attrs, :lang, :datasetId) + :expires_at, :sub, :contexts, :throttling, :sys_attrs, :lang, :datasetId, :jsonld_context) """.trimIndent() databaseClient.sql(insertStatement) @@ -182,6 +182,7 @@ class SubscriptionService( .bind("sys_attrs", subscription.notification.sysAttrs) .bind("lang", subscription.lang) .bind("datasetId", subscription.datasetId?.toTypedArray()) + .bind("jsonld_context", subscription.jsonldContext) .execute().bind() geoQuery?.let { @@ -252,7 +253,7 @@ class SubscriptionService( notif_format, endpoint_uri, endpoint_accept, endpoint_receiver_info, endpoint_notifier_info, status, times_sent, is_active, last_notification, last_failure, last_success, entity_selector.id as entity_id, id_pattern, entity_selector.type_selection as type_selection, georel, geometry, coordinates, - pgis_geometry, geoproperty, scope_q, expires_at, contexts, throttling, sys_attrs, lang, datasetId + pgis_geometry, geoproperty, scope_q, expires_at, contexts, throttling, sys_attrs, lang, datasetId, jsonld_context FROM subscription LEFT JOIN entity_selector ON entity_selector.subscription_id = :id LEFT JOIN geometry_query ON geometry_query.subscription_id = :id @@ -359,7 +360,8 @@ class SubscriptionService( "modifiedAt", "throttling", "lang", - "datasetId" + "datasetId", + "jsonldContext" ).contains(it.key) -> { val columnName = it.key.toSqlColumnName() val value = it.value.toSqlValue(it.key) @@ -502,7 +504,7 @@ class SubscriptionService( notif_format, endpoint_uri, endpoint_accept, endpoint_receiver_info, endpoint_notifier_info, status, times_sent, is_active, last_notification, last_failure, last_success, entity_selector.id as entity_id, id_pattern, entity_selector.type_selection as type_selection, georel, geometry, coordinates, - pgis_geometry, geoproperty, scope_q, expires_at, contexts, throttling, sys_attrs, lang, datasetId + pgis_geometry, geoproperty, scope_q, expires_at, contexts, throttling, sys_attrs, lang, datasetId, jsonld_context FROM subscription LEFT JOIN entity_selector ON entity_selector.subscription_id = subscription.id LEFT JOIN geometry_query ON geometry_query.subscription_id = subscription.id @@ -544,7 +546,7 @@ class SubscriptionService( entity_selector.id as entity_id, entity_selector.id_pattern as id_pattern, entity_selector.type_selection as type_selection, georel, geometry, coordinates, pgis_geometry, geoproperty, scope_q, notif_attributes, notif_format, endpoint_uri, endpoint_accept, times_sent, - endpoint_receiver_info, endpoint_notifier_info, contexts, throttling, sys_attrs, lang, datasetId + endpoint_receiver_info, endpoint_notifier_info, contexts, throttling, sys_attrs, lang, datasetId, jsonld_context FROM subscription LEFT JOIN entity_selector on subscription.id = entity_selector.subscription_id LEFT JOIN geometry_query on subscription.id = geometry_query.subscription_id @@ -699,7 +701,8 @@ class SubscriptionService( contexts = toList(row["contexts"]!!), throttling = toNullableInt(row["throttling"]), lang = row["lang"] as? String, - datasetId = toNullableList(row["datasetId"]) + datasetId = toNullableList(row["datasetId"]), + jsonldContext = toNullableUri(row["jsonld_context"]) ) } @@ -732,7 +735,8 @@ class SubscriptionService( contexts = toList(row["contexts"]!!), throttling = toNullableInt(row["throttling"]), lang = row["lang"] as? String, - datasetId = toNullableList(row["datasetId"]) + datasetId = toNullableList(row["datasetId"]), + jsonldContext = toNullableUri(row["jsonld_context"]) ) } @@ -768,7 +772,7 @@ class SubscriptionService( scope_q, notif_attributes, notif_format, endpoint_uri, endpoint_accept, endpoint_receiver_info, endpoint_notifier_info, status, times_sent, last_notification, last_failure, last_success, is_active, entity_selector.id as entity_id, id_pattern, entity_selector.type_selection as type_selection, georel, - geometry, coordinates, pgis_geometry, geoproperty, contexts, throttling, sys_attrs, lang, datasetId + geometry, coordinates, pgis_geometry, geoproperty, contexts, throttling, sys_attrs, lang, datasetId, jsonld_context FROM subscription LEFT JOIN entity_selector ON entity_selector.subscription_id = subscription.id LEFT JOIN geometry_query ON geometry_query.subscription_id = subscription.id diff --git a/subscription-service/src/main/resources/db/migration/V0_30__add_jsonld_context_column.sql b/subscription-service/src/main/resources/db/migration/V0_30__add_jsonld_context_column.sql new file mode 100644 index 000000000..da10d53f6 --- /dev/null +++ b/subscription-service/src/main/resources/db/migration/V0_30__add_jsonld_context_column.sql @@ -0,0 +1,2 @@ +ALTER TABLE subscription + ADD jsonld_context text; \ No newline at end of file diff --git a/subscription-service/src/test/kotlin/com/egm/stellio/subscription/service/NotificationServiceTests.kt b/subscription-service/src/test/kotlin/com/egm/stellio/subscription/service/NotificationServiceTests.kt index 86296cd4b..c4aaed31b 100644 --- a/subscription-service/src/test/kotlin/com/egm/stellio/subscription/service/NotificationServiceTests.kt +++ b/subscription-service/src/test/kotlin/com/egm/stellio/subscription/service/NotificationServiceTests.kt @@ -176,7 +176,7 @@ class NotificationServiceTests { } @Test - fun `it should notify the subscriber and use the contexts of the subscription to compact`() = runTest { + fun `it should notify the subscriber and use subscription contexts to compact when no jsonldContext is provided`() = runTest { val subscription = gimmeRawSubscription().copy( notification = NotificationParams( attributes = emptyList(), @@ -215,6 +215,47 @@ class NotificationServiceTests { } } + @Test + fun `it should notify the subscriber and use jsonldContext to compact when it is provided`() = runTest { + val subscription = gimmeRawSubscription().copy( + notification = NotificationParams( + attributes = emptyList(), + endpoint = Endpoint( + uri = "http://localhost:8089/notification".toUri(), + accept = Endpoint.AcceptType.JSONLD + ) + ), + contexts = listOf(NGSILD_TEST_CORE_CONTEXT), + jsonldContext = APIC_COMPOUND_CONTEXT.toUri() + ) + val expandedEntity = expandJsonLdEntity(rawEntity) + + coEvery { + subscriptionService.getMatchingSubscriptions(any(), any(), any()) + } returns listOf(subscription).right() + coEvery { subscriptionService.updateSubscriptionNotification(any(), any(), any()) } returns 1 + + stubFor( + post(urlMatching("/notification")) + .willReturn(ok()) + ) + + notificationService.notifyMatchingSubscribers( + expandedEntity, + setOf(NGSILD_NAME_TERM), + ATTRIBUTE_UPDATED + ).shouldSucceedWith { notificationResults -> + val notificationResult = notificationResults[0] + assertEquals(subscription.id, notificationResult.first.id) + assertEquals(subscription.id, notificationResult.second.subscriptionId) + assertEquals(1, notificationResult.second.data.size) + assertTrue(notificationResult.second.data[0].containsKey(NGSILD_NAME_TERM)) + assertTrue(notificationResult.second.data[0].containsKey(MANAGED_BY_COMPACT_RELATIONSHIP)) + assertEquals(APIC_COMPOUND_CONTEXT, notificationResult.second.data[0][JsonLdUtils.JSONLD_CONTEXT]) + assertTrue(notificationResult.third) + } + } + @Test fun `it should send a simplified payload when format is keyValues and include only the specified attributes`() = runTest { diff --git a/subscription-service/src/test/kotlin/com/egm/stellio/subscription/service/SubscriptionServiceTests.kt b/subscription-service/src/test/kotlin/com/egm/stellio/subscription/service/SubscriptionServiceTests.kt index 11be07e16..04ef59990 100644 --- a/subscription-service/src/test/kotlin/com/egm/stellio/subscription/service/SubscriptionServiceTests.kt +++ b/subscription-service/src/test/kotlin/com/egm/stellio/subscription/service/SubscriptionServiceTests.kt @@ -325,7 +325,8 @@ class SubscriptionServiceTests : WithTimescaleContainer, WithKafkaContainer { it.notification.sysAttrs && it.expiresAt == ZonedDateTime.parse("2100-01-01T00:00:00Z") && it.throttling == 60 && - it.lang == "fr,en" + it.lang == "fr,en" && + it.jsonldContext == APIC_COMPOUND_CONTEXT.toUri() } } diff --git a/subscription-service/src/test/resources/ngsild/subscription_full.json b/subscription-service/src/test/resources/ngsild/subscription_full.json index d77a991b6..3e0eaca14 100644 --- a/subscription-service/src/test/resources/ngsild/subscription_full.json +++ b/subscription-service/src/test/resources/ngsild/subscription_full.json @@ -38,5 +38,6 @@ }, "expiresAt": "2100-01-01T00:00:00Z", "throttling": 60, - "lang": "fr,en" + "lang": "fr,en", + "jsonldContext":"http://localhost:8093/jsonld-contexts/apic-compound.jsonld" } From 427eb5972ad8ecc0192e2de346c068a79e419df3 Mon Sep 17 00:00:00 2001 From: ranim-n Date: Fri, 30 Aug 2024 17:19:15 +0200 Subject: [PATCH 2/7] added jsonldContext initialization and validation --- shared/config/detekt/baseline.xml | 4 + .../egm/stellio/shared/model/ApiExceptions.kt | 2 +- .../egm/stellio/shared/util/JsonLdUtils.kt | 15 ++++ .../config/detekt/baseline.xml | 5 ++ .../service/NotificationService.kt | 4 +- .../service/SubscriptionService.kt | 20 ++++- .../service/NotificationServiceTests.kt | 3 +- .../service/SubscriptionServiceTests.kt | 81 +++++++++++++++++++ .../subscription/support/FixtureUtils.kt | 4 +- 9 files changed, 131 insertions(+), 7 deletions(-) diff --git a/shared/config/detekt/baseline.xml b/shared/config/detekt/baseline.xml index c3deb4468..3a8360f1f 100644 --- a/shared/config/detekt/baseline.xml +++ b/shared/config/detekt/baseline.xml @@ -7,8 +7,12 @@ 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> ) + MaxLineLength:ApiExceptions.kt$if + MaximumLineLength:ApiExceptions.kt$ SpreadOperator:EntityEvent.kt$EntityEvent$( *[ JsonSubTypes.Type(value = EntityCreateEvent::class), JsonSubTypes.Type(value = EntityReplaceEvent::class), JsonSubTypes.Type(value = EntityDeleteEvent::class), JsonSubTypes.Type(value = AttributeAppendEvent::class), JsonSubTypes.Type(value = AttributeReplaceEvent::class), JsonSubTypes.Type(value = AttributeUpdateEvent::class), JsonSubTypes.Type(value = AttributeDeleteEvent::class), JsonSubTypes.Type(value = AttributeDeleteAllInstancesEvent::class) ] ) + SwallowedException:JsonLdUtils.kt$JsonLdUtils$e: Exception SwallowedException:JsonLdUtils.kt$JsonLdUtils$e: JsonLdError + TooGenericExceptionCaught:JsonLdUtils.kt$JsonLdUtils$e: Exception TooManyFunctions:JsonLdUtils.kt$JsonLdUtils diff --git a/shared/src/main/kotlin/com/egm/stellio/shared/model/ApiExceptions.kt b/shared/src/main/kotlin/com/egm/stellio/shared/model/ApiExceptions.kt index e96b51108..93bd6abda 100644 --- a/shared/src/main/kotlin/com/egm/stellio/shared/model/ApiExceptions.kt +++ b/shared/src/main/kotlin/com/egm/stellio/shared/model/ApiExceptions.kt @@ -25,7 +25,7 @@ fun Throwable.toAPIException(specificMessage: String? = null): APIException = when (this) { is APIException -> this is JsonLdError -> - if (this.code == JsonLdErrorCode.LOADING_REMOTE_CONTEXT_FAILED) + if (this.code == JsonLdErrorCode.LOADING_REMOTE_CONTEXT_FAILED || this.code == JsonLdErrorCode.LOADING_DOCUMENT_FAILED) LdContextNotAvailableException(specificMessage ?: "Unable to load remote context (cause was: $this)") else BadRequestDataException("Unexpected error while parsing payload (cause was: $this)") else -> BadRequestDataException(specificMessage ?: this.localizedMessage) 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 6e66bc8bd..c7214ec6c 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 @@ -8,6 +8,9 @@ import com.apicatalog.jsonld.JsonLdError import com.apicatalog.jsonld.JsonLdOptions import com.apicatalog.jsonld.context.cache.LruCache import com.apicatalog.jsonld.document.JsonDocument +import com.apicatalog.jsonld.http.DefaultHttpClient +import com.apicatalog.jsonld.loader.DocumentLoaderOptions +import com.apicatalog.jsonld.loader.HttpLoader import com.egm.stellio.shared.model.* import com.egm.stellio.shared.util.JsonUtils.deserializeAs import com.egm.stellio.shared.util.JsonUtils.deserializeAsList @@ -121,6 +124,7 @@ object JsonLdUtils { contextCache = LruCache(CONTEXT_CACHE_CAPACITY) documentCache = LruCache(DOCUMENT_CACHE_CAPACITY) } + private val loader = HttpLoader(DefaultHttpClient.defaultInstance()) private fun buildContextDocument(contexts: List): JsonStructure { val contextsArray = Json.createArrayBuilder() @@ -244,6 +248,17 @@ object JsonLdUtils { } } + fun checkJsonldContext(context: URI) { + try { + val options = DocumentLoaderOptions() + loader.loadDocument(context, options) + } catch (e: JsonLdError) { + throw e.toAPIException(e.cause?.cause?.message) + } catch (e: Exception) { + throw e.toAPIException(e.cause?.cause?.message) + } + } + private fun transformGeoPropertyToWKT(): (Map.Entry) -> Any = { if (NGSILD_GEO_PROPERTIES_TERMS.contains(it.key)) { when (it.value) { diff --git a/subscription-service/config/detekt/baseline.xml b/subscription-service/config/detekt/baseline.xml index c8160769b..f1cedcb20 100644 --- a/subscription-service/config/detekt/baseline.xml +++ b/subscription-service/config/detekt/baseline.xml @@ -2,12 +2,17 @@ + ArgumentListWrapping:SubscriptionServiceTests.kt$SubscriptionServiceTests$(rawSubscription.deserializeAsMap(), emptyList()) CyclomaticComplexMethod:SubscriptionServiceTests.kt$SubscriptionServiceTests$@Test fun `it should load a subscription with all possible members`() + Indentation:SubscriptionServiceTests.kt$SubscriptionServiceTests$ LongMethod:EntityEventListenerService.kt$EntityEventListenerService$internal suspend fun dispatchEntityEvent(content: String) LongMethod:SubscriptionService.kt$SubscriptionService$@Transactional suspend fun update( subscriptionId: URI, input: Map<String, Any>, contexts: List<String> ): Either<APIException, Unit> LongParameterList:FixtureUtils.kt$( withQueryAndGeoQuery: Pair<Boolean, Boolean> = Pair(true, true), withEndpointReceiverInfo: Boolean = true, withNotifParams: Pair<FormatType, List<String>> = Pair(FormatType.NORMALIZED, emptyList()), withModifiedAt: Boolean = false, georel: String = "within", geometry: String = "Polygon", coordinates: String = "[[[100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0]]]", timeInterval: Int? = null, contexts: List<String> = listOf(NGSILD_TEST_CORE_CONTEXT) ) MaxLineLength:NotificationServiceTests.kt$NotificationServiceTests$fun + MaxLineLength:SubscriptionServiceTests.kt$SubscriptionServiceTests$val subscription = ParsingUtils.parseSubscription(rawSubscription.deserializeAsMap(), emptyList()).shouldSucceedAndResult() MaximumLineLength:NotificationServiceTests.kt$NotificationServiceTests$ + MaximumLineLength:SubscriptionServiceTests.kt$SubscriptionServiceTests$ + NoUnusedImports:SubscriptionService.kt$com.egm.stellio.subscription.service.SubscriptionService.kt TooGenericExceptionCaught:SubscriptionService.kt$SubscriptionService$e: Exception TooManyFunctions:SubscriptionService.kt$SubscriptionService diff --git a/subscription-service/src/main/kotlin/com/egm/stellio/subscription/service/NotificationService.kt b/subscription-service/src/main/kotlin/com/egm/stellio/subscription/service/NotificationService.kt index 0ba1b0eac..ca3246b7f 100644 --- a/subscription-service/src/main/kotlin/com/egm/stellio/subscription/service/NotificationService.kt +++ b/subscription-service/src/main/kotlin/com/egm/stellio/subscription/service/NotificationService.kt @@ -49,11 +49,9 @@ class NotificationService( AttributeRepresentation.SIMPLIFIED else AttributeRepresentation.NORMALIZED - val context = it.jsonldContext?.toString()?.let { listOf(it) } ?: it.contexts - val compactedEntity = compactEntity( ExpandedEntity(filteredEntity), - context + listOf(it.jsonldContext.toString()) ).toFinalRepresentation( NgsiLdDataRepresentation( entityRepresentation, diff --git a/subscription-service/src/main/kotlin/com/egm/stellio/subscription/service/SubscriptionService.kt b/subscription-service/src/main/kotlin/com/egm/stellio/subscription/service/SubscriptionService.kt index 0aaa10771..2cb02082b 100644 --- a/subscription-service/src/main/kotlin/com/egm/stellio/subscription/service/SubscriptionService.kt +++ b/subscription-service/src/main/kotlin/com/egm/stellio/subscription/service/SubscriptionService.kt @@ -2,14 +2,17 @@ package com.egm.stellio.subscription.service import arrow.core.Either import arrow.core.Option +import arrow.core.computations.ResultEffect.bind import arrow.core.left import arrow.core.raise.either import arrow.core.right +import com.apicatalog.jsonld.JsonLdError import com.egm.stellio.shared.model.* import com.egm.stellio.shared.util.* import com.egm.stellio.shared.util.JsonLdUtils.JSONLD_TYPE_TERM import com.egm.stellio.shared.util.JsonLdUtils.NGSILD_LOCATION_PROPERTY import com.egm.stellio.shared.util.JsonLdUtils.NGSILD_SUBSCRIPTION_TERM +import com.egm.stellio.shared.util.JsonLdUtils.checkJsonldContext import com.egm.stellio.shared.util.JsonLdUtils.expandJsonLdTerm import com.egm.stellio.subscription.config.SubscriptionProperties import com.egm.stellio.subscription.model.* @@ -56,6 +59,7 @@ class SubscriptionService( checkExpiresAtInTheFuture(subscription).bind() checkIdPatternIsValid(subscription).bind() checkNotificationTriggersAreValid(subscription).bind() + checkJsonLdContextIsValid(subscription).bind() } private fun checkTypeIsSubscription(subscription: Subscription): Either = @@ -134,6 +138,18 @@ class SubscriptionService( else BadRequestDataException("Unknown notification trigger in ${subscription.notificationTrigger}").left() } + private suspend fun checkJsonLdContextIsValid(subscription: Subscription): Either { + return try { + val jsonldContext = subscription.jsonldContext + if (jsonldContext != null) { + checkJsonldContext(jsonldContext) + } + Unit.right() + } catch (e: APIException) { + e.left() + } + } + @Transactional suspend fun create(subscription: Subscription, sub: Option): Either = either { validateNewSubscription(subscription).bind() @@ -143,6 +159,8 @@ class SubscriptionService( parseGeoQueryParameters(subscription.geoQ.toMap(), subscription.contexts).bind() else null val endpoint = subscription.notification.endpoint + val jsonldContext = + subscription.jsonldContext ?: subscription.contexts.first() val insertStatement = """ @@ -182,7 +200,7 @@ class SubscriptionService( .bind("sys_attrs", subscription.notification.sysAttrs) .bind("lang", subscription.lang) .bind("datasetId", subscription.datasetId?.toTypedArray()) - .bind("jsonld_context", subscription.jsonldContext) + .bind("jsonld_context", jsonldContext) .execute().bind() geoQuery?.let { diff --git a/subscription-service/src/test/kotlin/com/egm/stellio/subscription/service/NotificationServiceTests.kt b/subscription-service/src/test/kotlin/com/egm/stellio/subscription/service/NotificationServiceTests.kt index c4aaed31b..201513898 100644 --- a/subscription-service/src/test/kotlin/com/egm/stellio/subscription/service/NotificationServiceTests.kt +++ b/subscription-service/src/test/kotlin/com/egm/stellio/subscription/service/NotificationServiceTests.kt @@ -558,7 +558,8 @@ class NotificationServiceTests { ) ), lang = "fr", - contexts = APIC_COMPOUND_CONTEXTS + contexts = APIC_COMPOUND_CONTEXTS, + jsonldContext = APIC_COMPOUND_CONTEXT.toUri() ) val expandedEntity = expandJsonLdEntity( diff --git a/subscription-service/src/test/kotlin/com/egm/stellio/subscription/service/SubscriptionServiceTests.kt b/subscription-service/src/test/kotlin/com/egm/stellio/subscription/service/SubscriptionServiceTests.kt index 04ef59990..5be07a480 100644 --- a/subscription-service/src/test/kotlin/com/egm/stellio/subscription/service/SubscriptionServiceTests.kt +++ b/subscription-service/src/test/kotlin/com/egm/stellio/subscription/service/SubscriptionServiceTests.kt @@ -3,12 +3,14 @@ package com.egm.stellio.subscription.service import arrow.core.Some import com.egm.stellio.shared.model.BadRequestDataException import com.egm.stellio.shared.model.EntitySelector +import com.egm.stellio.shared.model.LdContextNotAvailableException import com.egm.stellio.shared.model.NotImplementedException import com.egm.stellio.shared.util.* import com.egm.stellio.shared.util.JsonLdUtils.NGSILD_LOCATION_PROPERTY import com.egm.stellio.shared.util.JsonLdUtils.NGSILD_LOCATION_TERM import com.egm.stellio.shared.util.JsonLdUtils.NGSILD_SUBSCRIPTION_TERM import com.egm.stellio.shared.util.JsonLdUtils.expandJsonLdEntity +import com.egm.stellio.shared.util.JsonUtils.deserializeAsMap import com.egm.stellio.subscription.model.Endpoint import com.egm.stellio.subscription.model.EndpointInfo import com.egm.stellio.subscription.model.Notification @@ -240,6 +242,62 @@ class SubscriptionServiceTests : WithTimescaleContainer, WithKafkaContainer { } } + @Test + fun `it should throw a BadRequestData exception when jsonldContext is not a URI`() = runTest { + val rawSubscription = + """ + { + "id": "urn:ngsi-ld:Subscription:1234567890", + "type": "Subscription", + "entities": [ + { + "type": "BeeHive" + } + ], + "notification": { + "endpoint": { + "uri": "http://localhost:8084" + } + }, + "jsonldContext": "unknownContext" + } + """.trimIndent() + + val subscription = ParsingUtils.parseSubscription(rawSubscription.deserializeAsMap(), emptyList()).shouldSucceedAndResult() + subscriptionService.validateNewSubscription(subscription) + .shouldFailWith { + it is BadRequestDataException + } + } + + @Test + fun `it should throw a LdContextNotAvailable exception when jsonldContext is not available`() = runTest { + val rawSubscription = + """ + { + "id": "urn:ngsi-ld:Subscription:1234567890", + "type": "Subscription", + "entities": [ + { + "type": "BeeHive" + } + ], + "notification": { + "endpoint": { + "uri": "http://localhost:8084" + } + }, + "jsonldContext": "https://uri.etsi.org/ngsi-ld/v1/ngsi-ld-core-context-non-existing.jsonld" + } + """.trimIndent() + + val subscription = ParsingUtils.parseSubscription(rawSubscription.deserializeAsMap(), emptyList()).shouldSucceedAndResult() + subscriptionService.validateNewSubscription(subscription) + .shouldFailWith { + it is LdContextNotAvailableException + } + } + @Test fun `it should load a subscription with minimal required info - entities`() = runTest { val subscription = loadAndDeserializeSubscription("subscription_minimal_entities.json") @@ -330,6 +388,29 @@ class SubscriptionServiceTests : WithTimescaleContainer, WithKafkaContainer { } } + @Test + fun `it should initialize jsonldContext with subscription @context if jsonldContext is not provided`() = runTest { + val subscription = loadAndDeserializeSubscription("subscription_minimal_entities.json") + subscriptionService.create(subscription, mockUserSub).shouldSucceed() + + val persistedSubscription = subscriptionService.getById(subscription.id) + assertThat(persistedSubscription) + .matches { + it.id == "urn:ngsi-ld:Subscription:1".toUri() && + it.notification.format == FormatType.NORMALIZED && + it.notification.endpoint.uri == URI("http://localhost:8084") && + it.notification.endpoint.accept == Endpoint.AcceptType.JSON && + ( + it.entities != null && + it.entities!!.size == 1 && + it.entities!!.all { entitySelector -> entitySelector.typeSelection == BEEHIVE_TYPE } + ) && + it.watchedAttributes == null && + it.isActive + it.jsonldContext == APIC_COMPOUND_CONTEXT.toUri() + } + } + @Test fun `it should load a subscription with extra info on last notification`() = runTest { val subscription = gimmeSubscriptionFromMembers( diff --git a/subscription-service/src/test/kotlin/com/egm/stellio/subscription/support/FixtureUtils.kt b/subscription-service/src/test/kotlin/com/egm/stellio/subscription/support/FixtureUtils.kt index ffcdd3f71..fd126df36 100644 --- a/subscription-service/src/test/kotlin/com/egm/stellio/subscription/support/FixtureUtils.kt +++ b/subscription-service/src/test/kotlin/com/egm/stellio/subscription/support/FixtureUtils.kt @@ -63,6 +63,7 @@ fun gimmeRawSubscription( null val modifiedAtValue = if (withModifiedAt) Instant.now().atZone(ZoneOffset.UTC) else null + val jsonldContext = contexts.first().toUri() return Subscription( type = NGSILD_SUBSCRIPTION_TERM, subscriptionName = "My Subscription", @@ -81,6 +82,7 @@ fun gimmeRawSubscription( receiverInfo = endpointReceiverInfo ) ), - contexts = contexts + contexts = contexts, + jsonldContext = jsonldContext ) } From 240616b97d120d2de4d0ea25984ea34e8089c0df Mon Sep 17 00:00:00 2001 From: ranim-n Date: Tue, 3 Sep 2024 14:09:46 +0200 Subject: [PATCH 3/7] re-implementing jsonldContext without initialization --- shared/config/detekt/baseline.xml | 4 -- .../egm/stellio/shared/model/ApiExceptions.kt | 4 +- .../egm/stellio/shared/util/JsonLdUtils.kt | 2 - .../config/detekt/baseline.xml | 7 --- .../subscription/model/Subscription.kt | 2 +- .../service/NotificationService.kt | 4 +- .../service/SubscriptionService.kt | 12 ++--- .../V0_30__add_jsonld_context_column.sql | 2 +- .../service/NotificationServiceTests.kt | 6 +-- .../service/SubscriptionServiceTests.kt | 47 +++++++------------ .../subscription/support/FixtureUtils.kt | 2 +- .../resources/ngsild/subscription_full.json | 4 +- 12 files changed, 36 insertions(+), 60 deletions(-) diff --git a/shared/config/detekt/baseline.xml b/shared/config/detekt/baseline.xml index 3a8360f1f..c3deb4468 100644 --- a/shared/config/detekt/baseline.xml +++ b/shared/config/detekt/baseline.xml @@ -7,12 +7,8 @@ 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> ) - MaxLineLength:ApiExceptions.kt$if - MaximumLineLength:ApiExceptions.kt$ SpreadOperator:EntityEvent.kt$EntityEvent$( *[ JsonSubTypes.Type(value = EntityCreateEvent::class), JsonSubTypes.Type(value = EntityReplaceEvent::class), JsonSubTypes.Type(value = EntityDeleteEvent::class), JsonSubTypes.Type(value = AttributeAppendEvent::class), JsonSubTypes.Type(value = AttributeReplaceEvent::class), JsonSubTypes.Type(value = AttributeUpdateEvent::class), JsonSubTypes.Type(value = AttributeDeleteEvent::class), JsonSubTypes.Type(value = AttributeDeleteAllInstancesEvent::class) ] ) - SwallowedException:JsonLdUtils.kt$JsonLdUtils$e: Exception SwallowedException:JsonLdUtils.kt$JsonLdUtils$e: JsonLdError - TooGenericExceptionCaught:JsonLdUtils.kt$JsonLdUtils$e: Exception TooManyFunctions:JsonLdUtils.kt$JsonLdUtils diff --git a/shared/src/main/kotlin/com/egm/stellio/shared/model/ApiExceptions.kt b/shared/src/main/kotlin/com/egm/stellio/shared/model/ApiExceptions.kt index 93bd6abda..6a9269335 100644 --- a/shared/src/main/kotlin/com/egm/stellio/shared/model/ApiExceptions.kt +++ b/shared/src/main/kotlin/com/egm/stellio/shared/model/ApiExceptions.kt @@ -25,7 +25,9 @@ fun Throwable.toAPIException(specificMessage: String? = null): APIException = when (this) { is APIException -> this is JsonLdError -> - if (this.code == JsonLdErrorCode.LOADING_REMOTE_CONTEXT_FAILED || this.code == JsonLdErrorCode.LOADING_DOCUMENT_FAILED) + if (this.code == JsonLdErrorCode.LOADING_REMOTE_CONTEXT_FAILED || + this.code == JsonLdErrorCode.LOADING_DOCUMENT_FAILED + ) LdContextNotAvailableException(specificMessage ?: "Unable to load remote context (cause was: $this)") else BadRequestDataException("Unexpected error while parsing payload (cause was: $this)") else -> BadRequestDataException(specificMessage ?: this.localizedMessage) 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 c7214ec6c..889f078ec 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 @@ -254,8 +254,6 @@ object JsonLdUtils { loader.loadDocument(context, options) } catch (e: JsonLdError) { throw e.toAPIException(e.cause?.cause?.message) - } catch (e: Exception) { - throw e.toAPIException(e.cause?.cause?.message) } } diff --git a/subscription-service/config/detekt/baseline.xml b/subscription-service/config/detekt/baseline.xml index f1cedcb20..eab1783a5 100644 --- a/subscription-service/config/detekt/baseline.xml +++ b/subscription-service/config/detekt/baseline.xml @@ -2,17 +2,10 @@ - ArgumentListWrapping:SubscriptionServiceTests.kt$SubscriptionServiceTests$(rawSubscription.deserializeAsMap(), emptyList()) CyclomaticComplexMethod:SubscriptionServiceTests.kt$SubscriptionServiceTests$@Test fun `it should load a subscription with all possible members`() - Indentation:SubscriptionServiceTests.kt$SubscriptionServiceTests$ LongMethod:EntityEventListenerService.kt$EntityEventListenerService$internal suspend fun dispatchEntityEvent(content: String) LongMethod:SubscriptionService.kt$SubscriptionService$@Transactional suspend fun update( subscriptionId: URI, input: Map<String, Any>, contexts: List<String> ): Either<APIException, Unit> LongParameterList:FixtureUtils.kt$( withQueryAndGeoQuery: Pair<Boolean, Boolean> = Pair(true, true), withEndpointReceiverInfo: Boolean = true, withNotifParams: Pair<FormatType, List<String>> = Pair(FormatType.NORMALIZED, emptyList()), withModifiedAt: Boolean = false, georel: String = "within", geometry: String = "Polygon", coordinates: String = "[[[100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0]]]", timeInterval: Int? = null, contexts: List<String> = listOf(NGSILD_TEST_CORE_CONTEXT) ) - MaxLineLength:NotificationServiceTests.kt$NotificationServiceTests$fun - MaxLineLength:SubscriptionServiceTests.kt$SubscriptionServiceTests$val subscription = ParsingUtils.parseSubscription(rawSubscription.deserializeAsMap(), emptyList()).shouldSucceedAndResult() - MaximumLineLength:NotificationServiceTests.kt$NotificationServiceTests$ - MaximumLineLength:SubscriptionServiceTests.kt$SubscriptionServiceTests$ - NoUnusedImports:SubscriptionService.kt$com.egm.stellio.subscription.service.SubscriptionService.kt TooGenericExceptionCaught:SubscriptionService.kt$SubscriptionService$e: Exception TooManyFunctions:SubscriptionService.kt$SubscriptionService diff --git a/subscription-service/src/main/kotlin/com/egm/stellio/subscription/model/Subscription.kt b/subscription-service/src/main/kotlin/com/egm/stellio/subscription/model/Subscription.kt index 6f63a1e3f..81c17af6d 100644 --- a/subscription-service/src/main/kotlin/com/egm/stellio/subscription/model/Subscription.kt +++ b/subscription-service/src/main/kotlin/com/egm/stellio/subscription/model/Subscription.kt @@ -55,7 +55,7 @@ data class Subscription( @JsonInclude(value = JsonInclude.Include.NON_EMPTY) val datasetId: List? = null, @JsonInclude(value = JsonInclude.Include.NON_EMPTY) - val jsonldContext: URI? = null + val jsonldContext: List? = null ) { @Transient diff --git a/subscription-service/src/main/kotlin/com/egm/stellio/subscription/service/NotificationService.kt b/subscription-service/src/main/kotlin/com/egm/stellio/subscription/service/NotificationService.kt index ca3246b7f..6fb8a3828 100644 --- a/subscription-service/src/main/kotlin/com/egm/stellio/subscription/service/NotificationService.kt +++ b/subscription-service/src/main/kotlin/com/egm/stellio/subscription/service/NotificationService.kt @@ -49,9 +49,11 @@ class NotificationService( AttributeRepresentation.SIMPLIFIED else AttributeRepresentation.NORMALIZED + val context = it.jsonldContext ?: it.contexts + val compactedEntity = compactEntity( ExpandedEntity(filteredEntity), - listOf(it.jsonldContext.toString()) + context ).toFinalRepresentation( NgsiLdDataRepresentation( entityRepresentation, diff --git a/subscription-service/src/main/kotlin/com/egm/stellio/subscription/service/SubscriptionService.kt b/subscription-service/src/main/kotlin/com/egm/stellio/subscription/service/SubscriptionService.kt index 2cb02082b..84f1318fe 100644 --- a/subscription-service/src/main/kotlin/com/egm/stellio/subscription/service/SubscriptionService.kt +++ b/subscription-service/src/main/kotlin/com/egm/stellio/subscription/service/SubscriptionService.kt @@ -2,11 +2,9 @@ package com.egm.stellio.subscription.service import arrow.core.Either import arrow.core.Option -import arrow.core.computations.ResultEffect.bind import arrow.core.left import arrow.core.raise.either import arrow.core.right -import com.apicatalog.jsonld.JsonLdError import com.egm.stellio.shared.model.* import com.egm.stellio.shared.util.* import com.egm.stellio.shared.util.JsonLdUtils.JSONLD_TYPE_TERM @@ -142,7 +140,7 @@ class SubscriptionService( return try { val jsonldContext = subscription.jsonldContext if (jsonldContext != null) { - checkJsonldContext(jsonldContext) + checkJsonldContext(jsonldContext.first().toUri()) } Unit.right() } catch (e: APIException) { @@ -159,8 +157,6 @@ class SubscriptionService( parseGeoQueryParameters(subscription.geoQ.toMap(), subscription.contexts).bind() else null val endpoint = subscription.notification.endpoint - val jsonldContext = - subscription.jsonldContext ?: subscription.contexts.first() val insertStatement = """ @@ -200,7 +196,7 @@ class SubscriptionService( .bind("sys_attrs", subscription.notification.sysAttrs) .bind("lang", subscription.lang) .bind("datasetId", subscription.datasetId?.toTypedArray()) - .bind("jsonld_context", jsonldContext) + .bind("jsonld_context", subscription.jsonldContext?.toTypedArray()) .execute().bind() geoQuery?.let { @@ -720,7 +716,7 @@ class SubscriptionService( throttling = toNullableInt(row["throttling"]), lang = row["lang"] as? String, datasetId = toNullableList(row["datasetId"]), - jsonldContext = toNullableUri(row["jsonld_context"]) + jsonldContext = toNullableList(row["jsonld_context"]) ) } @@ -754,7 +750,7 @@ class SubscriptionService( throttling = toNullableInt(row["throttling"]), lang = row["lang"] as? String, datasetId = toNullableList(row["datasetId"]), - jsonldContext = toNullableUri(row["jsonld_context"]) + jsonldContext = toNullableList(row["jsonld_context"]) ) } diff --git a/subscription-service/src/main/resources/db/migration/V0_30__add_jsonld_context_column.sql b/subscription-service/src/main/resources/db/migration/V0_30__add_jsonld_context_column.sql index da10d53f6..ab486d38e 100644 --- a/subscription-service/src/main/resources/db/migration/V0_30__add_jsonld_context_column.sql +++ b/subscription-service/src/main/resources/db/migration/V0_30__add_jsonld_context_column.sql @@ -1,2 +1,2 @@ ALTER TABLE subscription - ADD jsonld_context text; \ No newline at end of file + ADD jsonld_context text[]; \ No newline at end of file diff --git a/subscription-service/src/test/kotlin/com/egm/stellio/subscription/service/NotificationServiceTests.kt b/subscription-service/src/test/kotlin/com/egm/stellio/subscription/service/NotificationServiceTests.kt index 201513898..efdcef2e9 100644 --- a/subscription-service/src/test/kotlin/com/egm/stellio/subscription/service/NotificationServiceTests.kt +++ b/subscription-service/src/test/kotlin/com/egm/stellio/subscription/service/NotificationServiceTests.kt @@ -176,7 +176,7 @@ class NotificationServiceTests { } @Test - fun `it should notify the subscriber and use subscription contexts to compact when no jsonldContext is provided`() = runTest { + fun `it should notify the subscriber and use subscription contexts to compact`() = runTest { val subscription = gimmeRawSubscription().copy( notification = NotificationParams( attributes = emptyList(), @@ -226,7 +226,7 @@ class NotificationServiceTests { ) ), contexts = listOf(NGSILD_TEST_CORE_CONTEXT), - jsonldContext = APIC_COMPOUND_CONTEXT.toUri() + jsonldContext = APIC_COMPOUND_CONTEXTS ) val expandedEntity = expandJsonLdEntity(rawEntity) @@ -559,7 +559,7 @@ class NotificationServiceTests { ), lang = "fr", contexts = APIC_COMPOUND_CONTEXTS, - jsonldContext = APIC_COMPOUND_CONTEXT.toUri() + jsonldContext = APIC_COMPOUND_CONTEXTS ) val expandedEntity = expandJsonLdEntity( diff --git a/subscription-service/src/test/kotlin/com/egm/stellio/subscription/service/SubscriptionServiceTests.kt b/subscription-service/src/test/kotlin/com/egm/stellio/subscription/service/SubscriptionServiceTests.kt index 5be07a480..dce632984 100644 --- a/subscription-service/src/test/kotlin/com/egm/stellio/subscription/service/SubscriptionServiceTests.kt +++ b/subscription-service/src/test/kotlin/com/egm/stellio/subscription/service/SubscriptionServiceTests.kt @@ -259,11 +259,16 @@ class SubscriptionServiceTests : WithTimescaleContainer, WithKafkaContainer { "uri": "http://localhost:8084" } }, - "jsonldContext": "unknownContext" + "jsonldContext": [ + "unknownContext" + ] } - """.trimIndent() + """.trimIndent() - val subscription = ParsingUtils.parseSubscription(rawSubscription.deserializeAsMap(), emptyList()).shouldSucceedAndResult() + val subscription = ParsingUtils.parseSubscription( + rawSubscription.deserializeAsMap(), + emptyList() + ).shouldSucceedAndResult() subscriptionService.validateNewSubscription(subscription) .shouldFailWith { it is BadRequestDataException @@ -287,11 +292,16 @@ class SubscriptionServiceTests : WithTimescaleContainer, WithKafkaContainer { "uri": "http://localhost:8084" } }, - "jsonldContext": "https://uri.etsi.org/ngsi-ld/v1/ngsi-ld-core-context-non-existing.jsonld" + "jsonldContext": [ + "https://uri.etsi.org/ngsi-ld/v1/ngsi-ld-core-context-non-existing.jsonld" + ] } - """.trimIndent() + """.trimIndent() - val subscription = ParsingUtils.parseSubscription(rawSubscription.deserializeAsMap(), emptyList()).shouldSucceedAndResult() + val subscription = ParsingUtils.parseSubscription( + rawSubscription.deserializeAsMap(), + emptyList() + ).shouldSucceedAndResult() subscriptionService.validateNewSubscription(subscription) .shouldFailWith { it is LdContextNotAvailableException @@ -384,30 +394,7 @@ class SubscriptionServiceTests : WithTimescaleContainer, WithKafkaContainer { it.expiresAt == ZonedDateTime.parse("2100-01-01T00:00:00Z") && it.throttling == 60 && it.lang == "fr,en" && - it.jsonldContext == APIC_COMPOUND_CONTEXT.toUri() - } - } - - @Test - fun `it should initialize jsonldContext with subscription @context if jsonldContext is not provided`() = runTest { - val subscription = loadAndDeserializeSubscription("subscription_minimal_entities.json") - subscriptionService.create(subscription, mockUserSub).shouldSucceed() - - val persistedSubscription = subscriptionService.getById(subscription.id) - assertThat(persistedSubscription) - .matches { - it.id == "urn:ngsi-ld:Subscription:1".toUri() && - it.notification.format == FormatType.NORMALIZED && - it.notification.endpoint.uri == URI("http://localhost:8084") && - it.notification.endpoint.accept == Endpoint.AcceptType.JSON && - ( - it.entities != null && - it.entities!!.size == 1 && - it.entities!!.all { entitySelector -> entitySelector.typeSelection == BEEHIVE_TYPE } - ) && - it.watchedAttributes == null && - it.isActive - it.jsonldContext == APIC_COMPOUND_CONTEXT.toUri() + it.jsonldContext == APIC_COMPOUND_CONTEXTS } } diff --git a/subscription-service/src/test/kotlin/com/egm/stellio/subscription/support/FixtureUtils.kt b/subscription-service/src/test/kotlin/com/egm/stellio/subscription/support/FixtureUtils.kt index fd126df36..e1c0137dc 100644 --- a/subscription-service/src/test/kotlin/com/egm/stellio/subscription/support/FixtureUtils.kt +++ b/subscription-service/src/test/kotlin/com/egm/stellio/subscription/support/FixtureUtils.kt @@ -63,7 +63,7 @@ fun gimmeRawSubscription( null val modifiedAtValue = if (withModifiedAt) Instant.now().atZone(ZoneOffset.UTC) else null - val jsonldContext = contexts.first().toUri() + val jsonldContext = contexts return Subscription( type = NGSILD_SUBSCRIPTION_TERM, subscriptionName = "My Subscription", diff --git a/subscription-service/src/test/resources/ngsild/subscription_full.json b/subscription-service/src/test/resources/ngsild/subscription_full.json index 3e0eaca14..4bcf7b279 100644 --- a/subscription-service/src/test/resources/ngsild/subscription_full.json +++ b/subscription-service/src/test/resources/ngsild/subscription_full.json @@ -39,5 +39,7 @@ "expiresAt": "2100-01-01T00:00:00Z", "throttling": 60, "lang": "fr,en", - "jsonldContext":"http://localhost:8093/jsonld-contexts/apic-compound.jsonld" + "jsonldContext": [ + "http://localhost:8093/jsonld-contexts/apic-compound.jsonld" + ] } From a1e06e804b27e58ffa542c88b5a3f37f5ed3d0c3 Mon Sep 17 00:00:00 2001 From: ranim-n Date: Wed, 4 Sep 2024 14:20:42 +0200 Subject: [PATCH 4/7] exceptions handling + some fixes --- .../egm/stellio/shared/util/JsonLdUtils.kt | 11 +++++-- .../subscription/model/Subscription.kt | 2 +- .../service/NotificationService.kt | 2 +- .../service/SubscriptionService.kt | 32 +++++++++---------- .../V0_30__add_jsonld_context_column.sql | 2 +- .../service/NotificationServiceTests.kt | 4 +-- .../service/SubscriptionServiceTests.kt | 19 +++++------ .../subscription/support/FixtureUtils.kt | 4 +-- .../resources/ngsild/subscription_full.json | 4 +-- 9 files changed, 39 insertions(+), 41 deletions(-) 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 889f078ec..0ca39e080 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 @@ -2,6 +2,7 @@ package com.egm.stellio.shared.util import arrow.core.Either import arrow.core.left +import arrow.core.raise.either import arrow.core.right import com.apicatalog.jsonld.JsonLd import com.apicatalog.jsonld.JsonLdError @@ -248,12 +249,16 @@ object JsonLdUtils { } } - fun checkJsonldContext(context: URI) { - try { + fun checkJsonldContext(context: URI): Either = either { + return try { + context.toString().toUri() val options = DocumentLoaderOptions() loader.loadDocument(context, options) + Unit.right() } catch (e: JsonLdError) { - throw e.toAPIException(e.cause?.cause?.message) + e.toAPIException(e.cause?.cause?.message).left() + } catch (e: BadRequestDataException) { + e.left() } } diff --git a/subscription-service/src/main/kotlin/com/egm/stellio/subscription/model/Subscription.kt b/subscription-service/src/main/kotlin/com/egm/stellio/subscription/model/Subscription.kt index 81c17af6d..6f63a1e3f 100644 --- a/subscription-service/src/main/kotlin/com/egm/stellio/subscription/model/Subscription.kt +++ b/subscription-service/src/main/kotlin/com/egm/stellio/subscription/model/Subscription.kt @@ -55,7 +55,7 @@ data class Subscription( @JsonInclude(value = JsonInclude.Include.NON_EMPTY) val datasetId: List? = null, @JsonInclude(value = JsonInclude.Include.NON_EMPTY) - val jsonldContext: List? = null + val jsonldContext: URI? = null ) { @Transient diff --git a/subscription-service/src/main/kotlin/com/egm/stellio/subscription/service/NotificationService.kt b/subscription-service/src/main/kotlin/com/egm/stellio/subscription/service/NotificationService.kt index 6fb8a3828..0ba1b0eac 100644 --- a/subscription-service/src/main/kotlin/com/egm/stellio/subscription/service/NotificationService.kt +++ b/subscription-service/src/main/kotlin/com/egm/stellio/subscription/service/NotificationService.kt @@ -49,7 +49,7 @@ class NotificationService( AttributeRepresentation.SIMPLIFIED else AttributeRepresentation.NORMALIZED - val context = it.jsonldContext ?: it.contexts + val context = it.jsonldContext?.toString()?.let { listOf(it) } ?: it.contexts val compactedEntity = compactEntity( ExpandedEntity(filteredEntity), diff --git a/subscription-service/src/main/kotlin/com/egm/stellio/subscription/service/SubscriptionService.kt b/subscription-service/src/main/kotlin/com/egm/stellio/subscription/service/SubscriptionService.kt index 84f1318fe..d7959ad0b 100644 --- a/subscription-service/src/main/kotlin/com/egm/stellio/subscription/service/SubscriptionService.kt +++ b/subscription-service/src/main/kotlin/com/egm/stellio/subscription/service/SubscriptionService.kt @@ -136,15 +136,11 @@ class SubscriptionService( else BadRequestDataException("Unknown notification trigger in ${subscription.notificationTrigger}").left() } - private suspend fun checkJsonLdContextIsValid(subscription: Subscription): Either { - return try { - val jsonldContext = subscription.jsonldContext - if (jsonldContext != null) { - checkJsonldContext(jsonldContext.first().toUri()) - } - Unit.right() - } catch (e: APIException) { - e.left() + suspend fun checkJsonLdContextIsValid(subscription: Subscription): Either = either { + val jsonldContext = subscription.jsonldContext + + if (jsonldContext != null) { + checkJsonldContext(jsonldContext).bind() } } @@ -196,7 +192,7 @@ class SubscriptionService( .bind("sys_attrs", subscription.notification.sysAttrs) .bind("lang", subscription.lang) .bind("datasetId", subscription.datasetId?.toTypedArray()) - .bind("jsonld_context", subscription.jsonldContext?.toTypedArray()) + .bind("jsonld_context", subscription.jsonldContext) .execute().bind() geoQuery?.let { @@ -267,7 +263,8 @@ class SubscriptionService( notif_format, endpoint_uri, endpoint_accept, endpoint_receiver_info, endpoint_notifier_info, status, times_sent, is_active, last_notification, last_failure, last_success, entity_selector.id as entity_id, id_pattern, entity_selector.type_selection as type_selection, georel, geometry, coordinates, - pgis_geometry, geoproperty, scope_q, expires_at, contexts, throttling, sys_attrs, lang, datasetId, jsonld_context + pgis_geometry, geoproperty, scope_q, expires_at, contexts, throttling, sys_attrs, lang, + datasetId, jsonld_context FROM subscription LEFT JOIN entity_selector ON entity_selector.subscription_id = :id LEFT JOIN geometry_query ON geometry_query.subscription_id = :id @@ -518,7 +515,8 @@ class SubscriptionService( notif_format, endpoint_uri, endpoint_accept, endpoint_receiver_info, endpoint_notifier_info, status, times_sent, is_active, last_notification, last_failure, last_success, entity_selector.id as entity_id, id_pattern, entity_selector.type_selection as type_selection, georel, geometry, coordinates, - pgis_geometry, geoproperty, scope_q, expires_at, contexts, throttling, sys_attrs, lang, datasetId, jsonld_context + pgis_geometry, geoproperty, scope_q, expires_at, contexts, throttling, sys_attrs, lang, + datasetId, jsonld_context FROM subscription LEFT JOIN entity_selector ON entity_selector.subscription_id = subscription.id LEFT JOIN geometry_query ON geometry_query.subscription_id = subscription.id @@ -560,7 +558,8 @@ class SubscriptionService( entity_selector.id as entity_id, entity_selector.id_pattern as id_pattern, entity_selector.type_selection as type_selection, georel, geometry, coordinates, pgis_geometry, geoproperty, scope_q, notif_attributes, notif_format, endpoint_uri, endpoint_accept, times_sent, - endpoint_receiver_info, endpoint_notifier_info, contexts, throttling, sys_attrs, lang, datasetId, jsonld_context + endpoint_receiver_info, endpoint_notifier_info, contexts, throttling, sys_attrs, lang, + datasetId, jsonld_context FROM subscription LEFT JOIN entity_selector on subscription.id = entity_selector.subscription_id LEFT JOIN geometry_query on subscription.id = geometry_query.subscription_id @@ -716,7 +715,7 @@ class SubscriptionService( throttling = toNullableInt(row["throttling"]), lang = row["lang"] as? String, datasetId = toNullableList(row["datasetId"]), - jsonldContext = toNullableList(row["jsonld_context"]) + jsonldContext = toNullableUri(row["jsonld_context"]) ) } @@ -750,7 +749,7 @@ class SubscriptionService( throttling = toNullableInt(row["throttling"]), lang = row["lang"] as? String, datasetId = toNullableList(row["datasetId"]), - jsonldContext = toNullableList(row["jsonld_context"]) + jsonldContext = toNullableUri(row["jsonld_context"]) ) } @@ -786,7 +785,8 @@ class SubscriptionService( scope_q, notif_attributes, notif_format, endpoint_uri, endpoint_accept, endpoint_receiver_info, endpoint_notifier_info, status, times_sent, last_notification, last_failure, last_success, is_active, entity_selector.id as entity_id, id_pattern, entity_selector.type_selection as type_selection, georel, - geometry, coordinates, pgis_geometry, geoproperty, contexts, throttling, sys_attrs, lang, datasetId, jsonld_context + geometry, coordinates, pgis_geometry, geoproperty, contexts, throttling, sys_attrs, lang, + datasetId, jsonld_context FROM subscription LEFT JOIN entity_selector ON entity_selector.subscription_id = subscription.id LEFT JOIN geometry_query ON geometry_query.subscription_id = subscription.id diff --git a/subscription-service/src/main/resources/db/migration/V0_30__add_jsonld_context_column.sql b/subscription-service/src/main/resources/db/migration/V0_30__add_jsonld_context_column.sql index ab486d38e..da10d53f6 100644 --- a/subscription-service/src/main/resources/db/migration/V0_30__add_jsonld_context_column.sql +++ b/subscription-service/src/main/resources/db/migration/V0_30__add_jsonld_context_column.sql @@ -1,2 +1,2 @@ ALTER TABLE subscription - ADD jsonld_context text[]; \ No newline at end of file + ADD jsonld_context text; \ No newline at end of file diff --git a/subscription-service/src/test/kotlin/com/egm/stellio/subscription/service/NotificationServiceTests.kt b/subscription-service/src/test/kotlin/com/egm/stellio/subscription/service/NotificationServiceTests.kt index efdcef2e9..e2625f62e 100644 --- a/subscription-service/src/test/kotlin/com/egm/stellio/subscription/service/NotificationServiceTests.kt +++ b/subscription-service/src/test/kotlin/com/egm/stellio/subscription/service/NotificationServiceTests.kt @@ -226,7 +226,7 @@ class NotificationServiceTests { ) ), contexts = listOf(NGSILD_TEST_CORE_CONTEXT), - jsonldContext = APIC_COMPOUND_CONTEXTS + jsonldContext = APIC_COMPOUND_CONTEXT.toUri() ) val expandedEntity = expandJsonLdEntity(rawEntity) @@ -559,7 +559,7 @@ class NotificationServiceTests { ), lang = "fr", contexts = APIC_COMPOUND_CONTEXTS, - jsonldContext = APIC_COMPOUND_CONTEXTS + jsonldContext = APIC_COMPOUND_CONTEXT.toUri() ) val expandedEntity = expandJsonLdEntity( diff --git a/subscription-service/src/test/kotlin/com/egm/stellio/subscription/service/SubscriptionServiceTests.kt b/subscription-service/src/test/kotlin/com/egm/stellio/subscription/service/SubscriptionServiceTests.kt index dce632984..9c57582c4 100644 --- a/subscription-service/src/test/kotlin/com/egm/stellio/subscription/service/SubscriptionServiceTests.kt +++ b/subscription-service/src/test/kotlin/com/egm/stellio/subscription/service/SubscriptionServiceTests.kt @@ -29,6 +29,7 @@ import kotlinx.coroutines.test.runTest import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertInstanceOf import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.CsvSource @@ -259,9 +260,7 @@ class SubscriptionServiceTests : WithTimescaleContainer, WithKafkaContainer { "uri": "http://localhost:8084" } }, - "jsonldContext": [ - "unknownContext" - ] + "jsonldContext": "unknownContext" } """.trimIndent() @@ -270,8 +269,8 @@ class SubscriptionServiceTests : WithTimescaleContainer, WithKafkaContainer { emptyList() ).shouldSucceedAndResult() subscriptionService.validateNewSubscription(subscription) - .shouldFailWith { - it is BadRequestDataException + .shouldFail { + assertInstanceOf(BadRequestDataException::class.java, it) } } @@ -292,9 +291,7 @@ class SubscriptionServiceTests : WithTimescaleContainer, WithKafkaContainer { "uri": "http://localhost:8084" } }, - "jsonldContext": [ - "https://uri.etsi.org/ngsi-ld/v1/ngsi-ld-core-context-non-existing.jsonld" - ] + "jsonldContext": "https://uri.etsi.org/ngsi-ld/v1/ngsi-ld-core-context-non-existing.jsonld" } """.trimIndent() @@ -303,8 +300,8 @@ class SubscriptionServiceTests : WithTimescaleContainer, WithKafkaContainer { emptyList() ).shouldSucceedAndResult() subscriptionService.validateNewSubscription(subscription) - .shouldFailWith { - it is LdContextNotAvailableException + .shouldFail { + assertInstanceOf(LdContextNotAvailableException::class.java, it) } } @@ -394,7 +391,7 @@ class SubscriptionServiceTests : WithTimescaleContainer, WithKafkaContainer { it.expiresAt == ZonedDateTime.parse("2100-01-01T00:00:00Z") && it.throttling == 60 && it.lang == "fr,en" && - it.jsonldContext == APIC_COMPOUND_CONTEXTS + it.jsonldContext == APIC_COMPOUND_CONTEXT.toUri() } } diff --git a/subscription-service/src/test/kotlin/com/egm/stellio/subscription/support/FixtureUtils.kt b/subscription-service/src/test/kotlin/com/egm/stellio/subscription/support/FixtureUtils.kt index e1c0137dc..ffcdd3f71 100644 --- a/subscription-service/src/test/kotlin/com/egm/stellio/subscription/support/FixtureUtils.kt +++ b/subscription-service/src/test/kotlin/com/egm/stellio/subscription/support/FixtureUtils.kt @@ -63,7 +63,6 @@ fun gimmeRawSubscription( null val modifiedAtValue = if (withModifiedAt) Instant.now().atZone(ZoneOffset.UTC) else null - val jsonldContext = contexts return Subscription( type = NGSILD_SUBSCRIPTION_TERM, subscriptionName = "My Subscription", @@ -82,7 +81,6 @@ fun gimmeRawSubscription( receiverInfo = endpointReceiverInfo ) ), - contexts = contexts, - jsonldContext = jsonldContext + contexts = contexts ) } diff --git a/subscription-service/src/test/resources/ngsild/subscription_full.json b/subscription-service/src/test/resources/ngsild/subscription_full.json index 4bcf7b279..fb27cb2b1 100644 --- a/subscription-service/src/test/resources/ngsild/subscription_full.json +++ b/subscription-service/src/test/resources/ngsild/subscription_full.json @@ -39,7 +39,5 @@ "expiresAt": "2100-01-01T00:00:00Z", "throttling": 60, "lang": "fr,en", - "jsonldContext": [ - "http://localhost:8093/jsonld-contexts/apic-compound.jsonld" - ] + "jsonldContext": "http://localhost:8093/jsonld-contexts/apic-compound.jsonld" } From cb2945d23d7bec8a6b80a6daed1d55ef2c0b7780 Mon Sep 17 00:00:00 2001 From: ranim-n Date: Fri, 6 Sep 2024 11:05:02 +0200 Subject: [PATCH 5/7] minor fixes --- .../kotlin/com/egm/stellio/shared/util/JsonLdUtils.kt | 8 +++----- .../stellio/subscription/service/NotificationService.kt | 4 ++-- .../subscription/service/NotificationServiceTests.kt | 4 ---- 3 files changed, 5 insertions(+), 11 deletions(-) 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 0ca39e080..c0945de02 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 @@ -251,14 +251,12 @@ object JsonLdUtils { fun checkJsonldContext(context: URI): Either = either { return try { - context.toString().toUri() - val options = DocumentLoaderOptions() - loader.loadDocument(context, options) + loader.loadDocument(context, DocumentLoaderOptions()) Unit.right() } catch (e: JsonLdError) { e.toAPIException(e.cause?.cause?.message).left() - } catch (e: BadRequestDataException) { - e.left() + } catch (e: IllegalArgumentException) { + BadRequestDataException(e.cause?.message ?: "Provided context is invalid: $context").left() } } diff --git a/subscription-service/src/main/kotlin/com/egm/stellio/subscription/service/NotificationService.kt b/subscription-service/src/main/kotlin/com/egm/stellio/subscription/service/NotificationService.kt index 0ba1b0eac..05e15a248 100644 --- a/subscription-service/src/main/kotlin/com/egm/stellio/subscription/service/NotificationService.kt +++ b/subscription-service/src/main/kotlin/com/egm/stellio/subscription/service/NotificationService.kt @@ -49,11 +49,11 @@ class NotificationService( AttributeRepresentation.SIMPLIFIED else AttributeRepresentation.NORMALIZED - val context = it.jsonldContext?.toString()?.let { listOf(it) } ?: it.contexts + val contexts = it.jsonldContext?.let { listOf(it.toString()) } ?: it.contexts val compactedEntity = compactEntity( ExpandedEntity(filteredEntity), - context + contexts ).toFinalRepresentation( NgsiLdDataRepresentation( entityRepresentation, diff --git a/subscription-service/src/test/kotlin/com/egm/stellio/subscription/service/NotificationServiceTests.kt b/subscription-service/src/test/kotlin/com/egm/stellio/subscription/service/NotificationServiceTests.kt index e2625f62e..ec2bf35f4 100644 --- a/subscription-service/src/test/kotlin/com/egm/stellio/subscription/service/NotificationServiceTests.kt +++ b/subscription-service/src/test/kotlin/com/egm/stellio/subscription/service/NotificationServiceTests.kt @@ -246,13 +246,9 @@ class NotificationServiceTests { ATTRIBUTE_UPDATED ).shouldSucceedWith { notificationResults -> val notificationResult = notificationResults[0] - assertEquals(subscription.id, notificationResult.first.id) - assertEquals(subscription.id, notificationResult.second.subscriptionId) - assertEquals(1, notificationResult.second.data.size) assertTrue(notificationResult.second.data[0].containsKey(NGSILD_NAME_TERM)) assertTrue(notificationResult.second.data[0].containsKey(MANAGED_BY_COMPACT_RELATIONSHIP)) assertEquals(APIC_COMPOUND_CONTEXT, notificationResult.second.data[0][JsonLdUtils.JSONLD_CONTEXT]) - assertTrue(notificationResult.third) } } From 5236964525cc1076b776b77d6630748a9d60aad9 Mon Sep 17 00:00:00 2001 From: ranim-n Date: Fri, 6 Sep 2024 17:32:11 +0200 Subject: [PATCH 6/7] adapting link header when sending notification to jsonldContext --- .../service/SubscriptionService.kt | 23 ++++++++------ .../service/NotificationServiceTests.kt | 31 +++++++++++++++++++ 2 files changed, 45 insertions(+), 9 deletions(-) diff --git a/subscription-service/src/main/kotlin/com/egm/stellio/subscription/service/SubscriptionService.kt b/subscription-service/src/main/kotlin/com/egm/stellio/subscription/service/SubscriptionService.kt index d7959ad0b..b2246fe21 100644 --- a/subscription-service/src/main/kotlin/com/egm/stellio/subscription/service/SubscriptionService.kt +++ b/subscription-service/src/main/kotlin/com/egm/stellio/subscription/service/SubscriptionService.kt @@ -281,7 +281,7 @@ class SubscriptionService( suspend fun getContextsForSubscription(id: URI): Either> { val selectStatement = """ - SELECT contexts + SELECT contexts, jsonld_context FROM subscription WHERE id = :id """.trimIndent() @@ -289,17 +289,22 @@ class SubscriptionService( return databaseClient.sql(selectStatement) .bind("id", id) .oneToResult { - toList(it["contexts"]!!) + it["jsonld_context"]?.let { listOf(it as String) } ?: toList(it["contexts"]!!) } } - fun getContextsLink(subscription: Subscription): String = - if (subscription.contexts.size > 1) { - val linkToRetrieveContexts = subscriptionProperties.stellioUrl + - "/ngsi-ld/v1/subscriptions/${subscription.id}/context" - buildContextLinkHeader(linkToRetrieveContexts) - } else - buildContextLinkHeader(subscription.contexts[0]) + fun getContextsLink(subscription: Subscription): String { + val contextLink = when { + subscription.contexts.size > 1 && subscription.jsonldContext == null -> { + val linkToRetrieveContexts = subscriptionProperties.stellioUrl + + "/ngsi-ld/v1/subscriptions/${subscription.id}/context" + linkToRetrieveContexts + } + subscription.jsonldContext != null -> subscription.jsonldContext.toString() + else -> subscription.contexts[0] + } + return buildContextLinkHeader(contextLink) + } suspend fun isCreatorOf(subscriptionId: URI, sub: Option): Either { val selectStatement = diff --git a/subscription-service/src/test/kotlin/com/egm/stellio/subscription/service/NotificationServiceTests.kt b/subscription-service/src/test/kotlin/com/egm/stellio/subscription/service/NotificationServiceTests.kt index ec2bf35f4..4b2050420 100644 --- a/subscription-service/src/test/kotlin/com/egm/stellio/subscription/service/NotificationServiceTests.kt +++ b/subscription-service/src/test/kotlin/com/egm/stellio/subscription/service/NotificationServiceTests.kt @@ -409,6 +409,37 @@ class NotificationServiceTests { ) } + @Test + fun `it should add a Link header containing the jsonldContext of the subscription when provided`() = runTest { + val subscription = gimmeRawSubscription().copy( + notification = NotificationParams( + attributes = emptyList(), + endpoint = Endpoint( + uri = "http://localhost:8089/notification".toUri(), + accept = Endpoint.AcceptType.JSON + ) + ), + jsonldContext = APIC_COMPOUND_CONTEXT.toUri() + ) + + coEvery { subscriptionService.getContextsLink(any()) } returns buildContextLinkHeader(APIC_COMPOUND_CONTEXT) + coEvery { subscriptionService.updateSubscriptionNotification(any(), any(), any()) } returns 1 + + stubFor( + post(urlMatching("/notification")) + .willReturn(ok()) + ) + + notificationService.callSubscriber(subscription, rawEntity.deserializeAsMap()) + + val link = buildContextLinkHeader(subscription.jsonldContext.toString()) + verify( + 1, + postRequestedFor(urlPathEqualTo("/notification")) + .withHeader(HttpHeaders.LINK, equalTo(link)) + ) + } + @Test fun `it should add an NGSILD-Tenant header if the subscription is not from the default context`() = runTest { val subscription = gimmeRawSubscription().copy( From d928b624a1235a6f5a7de09f747748c1a04d9710 Mon Sep 17 00:00:00 2001 From: ranim-n Date: Fri, 6 Sep 2024 17:43:58 +0200 Subject: [PATCH 7/7] minor fix --- .../egm/stellio/subscription/service/SubscriptionService.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/subscription-service/src/main/kotlin/com/egm/stellio/subscription/service/SubscriptionService.kt b/subscription-service/src/main/kotlin/com/egm/stellio/subscription/service/SubscriptionService.kt index b2246fe21..5063d3383 100644 --- a/subscription-service/src/main/kotlin/com/egm/stellio/subscription/service/SubscriptionService.kt +++ b/subscription-service/src/main/kotlin/com/egm/stellio/subscription/service/SubscriptionService.kt @@ -295,12 +295,12 @@ class SubscriptionService( fun getContextsLink(subscription: Subscription): String { val contextLink = when { - subscription.contexts.size > 1 && subscription.jsonldContext == null -> { + subscription.jsonldContext != null -> subscription.jsonldContext.toString() + subscription.contexts.size > 1 -> { val linkToRetrieveContexts = subscriptionProperties.stellioUrl + "/ngsi-ld/v1/subscriptions/${subscription.id}/context" linkToRetrieveContexts } - subscription.jsonldContext != null -> subscription.jsonldContext.toString() else -> subscription.contexts[0] } return buildContextLinkHeader(contextLink)