diff --git a/.github/workflows/anchore.yml b/.github/workflows/anchore.yml index 49f529e52..212ac25cb 100644 --- a/.github/workflows/anchore.yml +++ b/.github/workflows/anchore.yml @@ -11,17 +11,17 @@ jobs: steps: - name: Checkout project uses: actions/checkout@v4 - - name: Set up JDK 17 + - name: Set up JDK 21 uses: actions/setup-java@v4 with: distribution: 'temurin' - java-version: '17' + java-version: '21' - name: Assemble project uses: gradle/gradle-build-action@v2 with: arguments: assemble - name: Upload jars artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: jars path: | @@ -37,7 +37,7 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Download jars artifacts - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: jars path: build @@ -90,19 +90,19 @@ jobs: image: "localbuild/subscription-service:latest-scan" severity-cutoff: critical - name: Upload Anchore scan SARIF report for API Gateway - uses: github/codeql-action/upload-sarif@v2 + uses: github/codeql-action/upload-sarif@v3 if: always() with: sarif_file: ${{ steps.scan-api-gateway.outputs.sarif }} category: anchore-api-gateway - name: Upload Anchore scan SARIF report for Search Service - uses: github/codeql-action/upload-sarif@v2 + uses: github/codeql-action/upload-sarif@v3 if: always() with: sarif_file: ${{ steps.scan-search-service.outputs.sarif }} category: anchore-search-service - name: Upload Anchore scan SARIF report for Subscription Service - uses: github/codeql-action/upload-sarif@v2 + uses: github/codeql-action/upload-sarif@v3 if: always() with: sarif_file: ${{ steps.scan-subscription-service.outputs.sarif }} diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 722bd9afd..07721d2a7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,11 +17,11 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis - - name: Set up JDK 17 + - name: Set up JDK 21 uses: actions/setup-java@v4 with: distribution: 'temurin' - java-version: '17' + java-version: '21' cache: 'gradle' - name: Cache SonarCloud packages uses: actions/cache@v3 diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 418429212..52d35305b 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -31,15 +31,15 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 - - name: Set up JDK 17 + - name: Set up JDK 21 uses: actions/setup-java@v4 with: distribution: 'temurin' - java-version: '17' + java-version: '21' # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -53,7 +53,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v2 + uses: github/codeql-action/autobuild@v3 # ℹ️ Command-line programs to run using the OS shell. # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun @@ -66,6 +66,6 @@ jobs: # ./location_of_script_within_repo/buildscript.sh - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 with: category: "/language:${{matrix.language}}" diff --git a/.sdkmanrc b/.sdkmanrc index 272da7dcf..bc36ae5d2 100644 --- a/.sdkmanrc +++ b/.sdkmanrc @@ -1,3 +1,3 @@ # Enable auto-env through the sdkman_auto_env config # Add key=value pairs of SDKs to use below -java=17.0.8-tem +java=21-tem diff --git a/Jenkinsfile b/Jenkinsfile index 54be3cf45..35fc704da 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,7 +1,7 @@ pipeline { agent any tools { - jdk 'JDK 17' + jdk 'JDK 21' } environment { EGM_CI_DH = credentials('egm-ci-dh') diff --git a/README.md b/README.md index aebba3fc5..9c212fb81 100644 --- a/README.md +++ b/README.md @@ -81,7 +81,7 @@ The version number is obtained during the build process by using the `version` i ### Developing on a service Requirements: -* Java 17 (we recommend using [sdkman!](https://sdkman.io/) to install and manage versions of the JDK) +* Java 21 (we recommend using [sdkman!](https://sdkman.io/) to install and manage versions of the JDK) To develop on a specific service, you can use the provided `docker-compose.yml` file inside each service's directory, for instance: diff --git a/api-gateway/Dockerfile b/api-gateway/Dockerfile index 9489adb55..b0ca8a5d4 100644 --- a/api-gateway/Dockerfile +++ b/api-gateway/Dockerfile @@ -1,12 +1,12 @@ # You can build a Docker image of the module with the following command: # docker build --build-arg JAR_FILE=build/libs/api-gateway-{version}.jar -t stellio-api-gateway:{version} . -FROM eclipse-temurin:17-jre as builder +FROM eclipse-temurin:21-jre as builder WORKDIR application ARG JAR_FILE=build/libs/*.jar COPY ${JAR_FILE} application.jar RUN java -Djarmode=layertools -jar application.jar extract -FROM eclipse-temurin:17-jre +FROM eclipse-temurin:21-jre WORKDIR application COPY --from=builder application/dependencies/ ./ COPY --from=builder application/spring-boot-loader/ ./ diff --git a/api-gateway/build.gradle.kts b/api-gateway/build.gradle.kts index c559fed4f..82e5a33f9 100644 --- a/api-gateway/build.gradle.kts +++ b/api-gateway/build.gradle.kts @@ -7,9 +7,13 @@ plugins { dependencies { implementation("org.springframework.cloud:spring-cloud-starter-gateway") - implementation("org.zalando:logbook-spring-boot-webflux-autoconfigure:3.6.0") + implementation("org.zalando:logbook-spring-boot-webflux-autoconfigure:3.7.2") - detektPlugins("io.gitlab.arturbosch.detekt:detekt-formatting:1.23.3") + // https://docs.gradle.org/8.4/userguide/upgrading_version_8.html#test_framework_implementation_dependencies + testImplementation("org.springframework.boot:spring-boot-starter-test") + testRuntimeOnly("org.junit.platform:junit-platform-launcher") + + detektPlugins("io.gitlab.arturbosch.detekt:detekt-formatting:1.23.4") } springBoot { diff --git a/build.gradle.kts b/build.gradle.kts index c2daf6399..d0cf99b30 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,4 +1,3 @@ - import com.google.cloud.tools.jib.gradle.PlatformParameters import io.gitlab.arturbosch.detekt.Detekt import io.gitlab.arturbosch.detekt.DetektCreateBaselineTask @@ -11,7 +10,7 @@ buildscript { } } -extra["springCloudVersion"] = "2022.0.2" +extra["springCloudVersion"] = "2023.0.0" plugins { // https://docs.spring.io/spring-boot/docs/current/gradle-plugin/reference/htmlsingle/#reacting-to-other-plugins.java @@ -19,13 +18,13 @@ plugins { `kotlin-dsl` // only apply the plugin in the subprojects requiring it because it expects a Spring Boot app // and the shared lib is obviously not one - id("org.springframework.boot") version "3.1.5" apply false + id("org.springframework.boot") version "3.2.1" apply false id("io.spring.dependency-management") version "1.1.4" apply false id("org.graalvm.buildtools.native") version "0.9.28" - kotlin("jvm") version "1.9.21" apply false - kotlin("plugin.spring") version "1.9.21" apply false + kotlin("jvm") version "1.9.22" apply false + kotlin("plugin.spring") version "1.9.22" apply false id("com.google.cloud.tools.jib") version "3.4.0" apply false - id("io.gitlab.arturbosch.detekt") version "1.23.3" apply false + id("io.gitlab.arturbosch.detekt") version "1.23.4" apply false id("org.sonarqube") version "4.4.1.3373" jacoco } @@ -42,7 +41,7 @@ subprojects { apply(plugin = "io.gitlab.arturbosch.detekt") apply(plugin = "jacoco") - java.sourceCompatibility = JavaVersion.VERSION_17 + java.sourceCompatibility = JavaVersion.VERSION_21 the().apply { imports { @@ -72,7 +71,7 @@ subprojects { annotationProcessor("org.springframework.boot:spring-boot-configuration-processor") - runtimeOnly("de.siegmar:logback-gelf:5.0.0") + runtimeOnly("de.siegmar:logback-gelf:5.0.1") runtimeOnly("io.micrometer:micrometer-registry-prometheus") testImplementation("org.springframework.boot:spring-boot-starter-test") { @@ -87,8 +86,8 @@ subprojects { tasks.withType { kotlinOptions { - freeCompilerArgs = listOf("-Xjsr305=strict", "-opt-in=kotlin.RequiresOptIn") - jvmTarget = "${JavaVersion.VERSION_17}" + freeCompilerArgs = listOf("-Xjsr305=strict") + jvmTarget = "${JavaVersion.VERSION_21}" } } tasks.withType { @@ -103,7 +102,7 @@ subprojects { configurations.matching { it.name == "detekt" }.all { resolutionStrategy.eachDependency { if (requested.group == "org.jetbrains.kotlin") { - useVersion("1.9.10") + useVersion("1.9.21") } } } @@ -127,7 +126,7 @@ subprojects { // see https://docs.gradle.org/current/userguide/jacoco_plugin.html for configuration instructions jacoco { - toolVersion = "0.8.7" + toolVersion = "0.8.9" } tasks.test { finalizedBy(tasks.jacocoTestReport) // report is always generated after tests run @@ -140,7 +139,7 @@ subprojects { } } - project.ext.set("jibFromImage", "eclipse-temurin:17-jre") + project.ext.set("jibFromImage", "eclipse-temurin:21-jre") project.ext.set( "jibFromPlatforms", listOf( @@ -164,7 +163,7 @@ subprojects { NGSI-LD is an Open API and data model specification for context management published by ETSI. """.trimIndent(), "org.opencontainers.image.source" to "https://github.com/stellio-hub/stellio-context-broker", - "com.java.version" to "${JavaVersion.VERSION_17}" + "com.java.version" to "${JavaVersion.VERSION_21}" ) ) } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index bdc9a83b1..744c64d12 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/search-service/Dockerfile b/search-service/Dockerfile index 0e4c15d6b..5a2770219 100644 --- a/search-service/Dockerfile +++ b/search-service/Dockerfile @@ -1,12 +1,12 @@ # You can build a Docker image of the module with the following command: # docker build --build-arg JAR_FILE=build/libs/search-service-{version}.jar -t stellio-search-service:{version} . -FROM eclipse-temurin:17-jre as builder +FROM eclipse-temurin:21-jre as builder WORKDIR application ARG JAR_FILE=build/libs/*.jar COPY ${JAR_FILE} application.jar RUN java -Djarmode=layertools -jar application.jar extract -FROM eclipse-temurin:17-jre +FROM eclipse-temurin:21-jre WORKDIR application COPY --from=builder application/dependencies/ ./ COPY --from=builder application/spring-boot-loader/ ./ diff --git a/search-service/build.gradle.kts b/search-service/build.gradle.kts index 52f1e8d92..cd0eb8202 100644 --- a/search-service/build.gradle.kts +++ b/search-service/build.gradle.kts @@ -23,7 +23,7 @@ dependencies { implementation("org.json:json:20231013") implementation(project(":shared")) - detektPlugins("io.gitlab.arturbosch.detekt:detekt-formatting:1.23.3") + detektPlugins("io.gitlab.arturbosch.detekt:detekt-formatting:1.23.4") developmentOnly("org.springframework.boot:spring-boot-devtools") @@ -33,6 +33,9 @@ dependencies { testImplementation("org.testcontainers:kafka") testImplementation("org.testcontainers:r2dbc") testImplementation(testFixtures(project(":shared"))) + // https://docs.gradle.org/8.4/userguide/upgrading_version_8.html#test_framework_implementation_dependencies + testImplementation("org.springframework.boot:spring-boot-starter-test") + testRuntimeOnly("org.junit.platform:junit-platform-launcher") } defaultTasks("bootRun") diff --git a/search-service/src/main/kotlin/com/egm/stellio/search/config/WebConfig.kt b/search-service/src/main/kotlin/com/egm/stellio/search/config/WebConfig.kt index 49c9a591e..b55387b80 100644 --- a/search-service/src/main/kotlin/com/egm/stellio/search/config/WebConfig.kt +++ b/search-service/src/main/kotlin/com/egm/stellio/search/config/WebConfig.kt @@ -3,7 +3,6 @@ package com.egm.stellio.search.config import org.springframework.context.annotation.Configuration import org.springframework.http.codec.ServerCodecConfigurer import org.springframework.web.reactive.config.EnableWebFlux -import org.springframework.web.reactive.config.PathMatchConfigurer import org.springframework.web.reactive.config.WebFluxConfigurer @Configuration @@ -14,8 +13,4 @@ class WebConfig(private val searchProperties: SearchProperties) : WebFluxConfigu configurer.defaultCodecs().enableLoggingRequestDetails(true) configurer.defaultCodecs().maxInMemorySize(searchProperties.payloadMaxBodySize) } - - override fun configurePathMatching(configurer: PathMatchConfigurer) { - configurer.setUseTrailingSlashMatch(true) - } } diff --git a/search-service/src/main/kotlin/com/egm/stellio/search/model/TemporalEntitiesQuery.kt b/search-service/src/main/kotlin/com/egm/stellio/search/model/TemporalEntitiesQuery.kt index a3d04dde6..6063f3a10 100644 --- a/search-service/src/main/kotlin/com/egm/stellio/search/model/TemporalEntitiesQuery.kt +++ b/search-service/src/main/kotlin/com/egm/stellio/search/model/TemporalEntitiesQuery.kt @@ -1,5 +1,9 @@ package com.egm.stellio.search.model +import java.time.Duration +import java.time.Period +import java.time.temporal.TemporalAmount + data class TemporalEntitiesQuery( val entitiesQuery: EntitiesQuery, val temporalQuery: TemporalQuery, @@ -10,4 +14,16 @@ data class TemporalEntitiesQuery( fun isAggregatedWithDefinedDuration(): Boolean = withAggregatedValues && (temporalQuery.aggrPeriodDuration != null && temporalQuery.aggrPeriodDuration != "PT0S") + + fun computeAggrPeriodDuration(): TemporalAmount { + val splitted = temporalQuery.aggrPeriodDuration!!.split("T") + return if (splitted.size == 1) // has only date-based fields + Period.parse(temporalQuery.aggrPeriodDuration) + else { + val duration = Duration.parse("PT" + splitted[1]) + if ("P" == splitted[0]) // has only time-based fields + duration + else Period.parse(splitted[0]).plus(duration) + } + } } diff --git a/search-service/src/main/kotlin/com/egm/stellio/search/model/UpdateResult.kt b/search-service/src/main/kotlin/com/egm/stellio/search/model/UpdateResult.kt index 4127d15bf..ddb61f626 100644 --- a/search-service/src/main/kotlin/com/egm/stellio/search/model/UpdateResult.kt +++ b/search-service/src/main/kotlin/com/egm/stellio/search/model/UpdateResult.kt @@ -20,6 +20,10 @@ data class UpdateResult( updated = this.updated.plus(other.updated), notUpdated = this.notUpdated.plus(other.notUpdated) ) + + @JsonIgnore + fun hasSuccessfulUpdate(): Boolean = + this.updated.isNotEmpty() } val EMPTY_UPDATE_RESULT: UpdateResult = UpdateResult(emptyList(), emptyList()) @@ -48,7 +52,8 @@ data class UpdateAttributeResult( this.updateOperationResult in listOf( UpdateOperationResult.APPENDED, UpdateOperationResult.REPLACED, - UpdateOperationResult.UPDATED + UpdateOperationResult.UPDATED, + UpdateOperationResult.IGNORED ) } @@ -62,9 +67,6 @@ enum class UpdateOperationResult { fun isSuccessResult(): Boolean = listOf(APPENDED, REPLACED, UPDATED).contains(this) } -fun UpdateResult.hasSuccessfulUpdate(): Boolean = - this.updated.isNotEmpty() - fun updateResultFromDetailedResult(updateStatuses: List): UpdateResult { val updated = updateStatuses.filter { it.isSuccessfullyUpdated() } .map { UpdatedDetails(it.attributeName, it.datasetId, it.updateOperationResult) } diff --git a/search-service/src/main/kotlin/com/egm/stellio/search/scope/ScopeService.kt b/search-service/src/main/kotlin/com/egm/stellio/search/scope/ScopeService.kt index 95d3753be..6badab4f6 100644 --- a/search-service/src/main/kotlin/com/egm/stellio/search/scope/ScopeService.kt +++ b/search-service/src/main/kotlin/com/egm/stellio/search/scope/ScopeService.kt @@ -177,7 +177,7 @@ class ScopeService( ) .bind("entity_id", entityId) .bind("time_property", timeproperty.name) - .oneToResult { toZonedDateTime(it["first"]) } + .oneToResult { toOptionalZonedDateTime(it["first"]) } .getOrNull() private fun Json.replaceScopeValue(newScopeValue: Any): Map = diff --git a/search-service/src/main/kotlin/com/egm/stellio/search/service/AttributeInstanceService.kt b/search-service/src/main/kotlin/com/egm/stellio/search/service/AttributeInstanceService.kt index 10cd1582c..f1d5831d1 100644 --- a/search-service/src/main/kotlin/com/egm/stellio/search/service/AttributeInstanceService.kt +++ b/search-service/src/main/kotlin/com/egm/stellio/search/service/AttributeInstanceService.kt @@ -18,7 +18,6 @@ import org.springframework.r2dbc.core.bind import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional import java.net.URI -import java.time.Duration import java.time.ZonedDateTime import java.util.UUID @@ -174,7 +173,11 @@ class AttributeInstanceService( this.allToMappedList { rowToAttributeInstanceResult(it, temporalEntitiesQuery) } }.fold( { it.right() }, - { OperationNotSupportedException(INCONSISTENT_VALUES_IN_AGGREGATION_MESSAGE).left() } + { + OperationNotSupportedException( + it.cause?.message ?: INCONSISTENT_VALUES_IN_AGGREGATION_MESSAGE + ).left() + } ) } @@ -275,7 +278,7 @@ class AttributeInstanceService( if (!temporalEntitiesQuery.isAggregatedWithDefinedDuration()) toZonedDateTime(row["endTime"]) else - startDateTime.plus(Duration.parse(temporalEntitiesQuery.temporalQuery.aggrPeriodDuration)) + startDateTime.plus(temporalEntitiesQuery.computeAggrPeriodDuration()) // in a row, there is the result for each requested aggregation method val values = temporalEntitiesQuery.temporalQuery.aggrMethods!!.map { val value = row["${it.method}_value"] ?: "" diff --git a/search-service/src/main/kotlin/com/egm/stellio/search/service/EntityPayloadService.kt b/search-service/src/main/kotlin/com/egm/stellio/search/service/EntityPayloadService.kt index 541406daf..2f5f33552 100644 --- a/search-service/src/main/kotlin/com/egm/stellio/search/service/EntityPayloadService.kt +++ b/search-service/src/main/kotlin/com/egm/stellio/search/service/EntityPayloadService.kt @@ -492,33 +492,32 @@ class EntityPayloadService( expandedAttributes: ExpandedAttributes, disallowOverwrite: Boolean, sub: Sub? - ): Either = - either { - val (coreAttrs, otherAttrs) = - expandedAttributes.toList().partition { JSONLD_EXPANDED_ENTITY_SPECIFIC_MEMBERS.contains(it.first) } - val createdAt = ZonedDateTime.now(ZoneOffset.UTC) + ): Either = either { + val (coreAttrs, otherAttrs) = + expandedAttributes.toList().partition { JSONLD_EXPANDED_ENTITY_SPECIFIC_MEMBERS.contains(it.first) } + val createdAt = ZonedDateTime.now(ZoneOffset.UTC) - val operationType = - if (disallowOverwrite) APPEND_ATTRIBUTES - else APPEND_ATTRIBUTES_OVERWRITE_ALLOWED - val coreUpdateResult = updateCoreAttributes(entityUri, coreAttrs, createdAt, operationType).bind() - val attrsUpdateResult = temporalEntityAttributeService.appendEntityAttributes( - entityUri, - otherAttrs.toMap().toNgsiLdAttributes().bind(), - expandedAttributes, - disallowOverwrite, - createdAt, - sub - ).bind() + val operationType = + if (disallowOverwrite) APPEND_ATTRIBUTES + else APPEND_ATTRIBUTES_OVERWRITE_ALLOWED + val coreUpdateResult = updateCoreAttributes(entityUri, coreAttrs, createdAt, operationType).bind() + val attrsUpdateResult = temporalEntityAttributeService.appendEntityAttributes( + entityUri, + otherAttrs.toMap().toNgsiLdAttributes().bind(), + expandedAttributes, + disallowOverwrite, + createdAt, + sub + ).bind() - val updateResult = coreUpdateResult.mergeWith(attrsUpdateResult) - // update modifiedAt in entity if at least one attribute has been added - if (updateResult.hasSuccessfulUpdate()) { - val teas = temporalEntityAttributeService.getForEntity(entityUri, emptySet()) - updateState(entityUri, createdAt, teas).bind() - } - updateResult + val updateResult = coreUpdateResult.mergeWith(attrsUpdateResult) + // update modifiedAt in entity if at least one attribute has been added + if (updateResult.hasSuccessfulUpdate()) { + val teas = temporalEntityAttributeService.getForEntity(entityUri, emptySet()) + updateState(entityUri, createdAt, teas).bind() } + updateResult + } @Transactional suspend fun updateAttributes( diff --git a/search-service/src/main/kotlin/com/egm/stellio/search/service/TemporalEntityAttributeService.kt b/search-service/src/main/kotlin/com/egm/stellio/search/service/TemporalEntityAttributeService.kt index b450a38d1..96a6e4130 100644 --- a/search-service/src/main/kotlin/com/egm/stellio/search/service/TemporalEntityAttributeService.kt +++ b/search-service/src/main/kotlin/com/egm/stellio/search/service/TemporalEntityAttributeService.kt @@ -689,7 +689,7 @@ class TemporalEntityAttributeService( UpdateAttributeResult( attributeName, datasetId, - UpdateOperationResult.IGNORED, + UpdateOperationResult.FAILED, "Unknown attribute $attributeName with datasetId $datasetId in entity $entityId" ) } @@ -819,7 +819,7 @@ class TemporalEntityAttributeService( UpdateAttributeResult( attributeName, datasetId, - UpdateOperationResult.IGNORED, + UpdateOperationResult.FAILED, "Unknown attribute $attributeName with datasetId $datasetId in entity $entityId" ) } else { diff --git a/search-service/src/main/kotlin/com/egm/stellio/search/web/EntityHandler.kt b/search-service/src/main/kotlin/com/egm/stellio/search/web/EntityHandler.kt index fc15e68d9..5a3d8fe0e 100644 --- a/search-service/src/main/kotlin/com/egm/stellio/search/web/EntityHandler.kt +++ b/search-service/src/main/kotlin/com/egm/stellio/search/web/EntityHandler.kt @@ -5,7 +5,6 @@ import arrow.core.left import arrow.core.raise.either import arrow.core.right import com.egm.stellio.search.authorization.AuthorizationService -import com.egm.stellio.search.model.hasSuccessfulUpdate import com.egm.stellio.search.service.EntityEventService import com.egm.stellio.search.service.EntityPayloadService import com.egm.stellio.search.service.QueryService @@ -288,10 +287,6 @@ class EntityHandler( { it } ) - @DeleteMapping("/", "") - fun handleMissingEntityIdOnDelete(): ResponseEntity<*> = - missingPathErrorResponse("Missing entity id when trying to delete an entity") - /** * Implements 6.6.3.1 - Append Entity Attributes * @@ -489,7 +484,7 @@ class EntityHandler( { it } ) - @DeleteMapping("/attrs/{attrId}", "/{entityId}/attrs") + @DeleteMapping("/attrs/{attrId}") fun handleMissingEntityIdOrAttributeOnDeleteAttribute(): ResponseEntity<*> = missingPathErrorResponse("Missing entity id or attribute id when trying to delete an attribute") diff --git a/search-service/src/main/kotlin/com/egm/stellio/search/web/TemporalEntityHandler.kt b/search-service/src/main/kotlin/com/egm/stellio/search/web/TemporalEntityHandler.kt index d56a883fc..c8b636549 100644 --- a/search-service/src/main/kotlin/com/egm/stellio/search/web/TemporalEntityHandler.kt +++ b/search-service/src/main/kotlin/com/egm/stellio/search/web/TemporalEntityHandler.kt @@ -247,14 +247,11 @@ class TemporalEntityHandler( @PatchMapping( "/attrs/{attrId}/{instanceId}", - "/{entityId}/attrs/{instanceId}", "/attrs/{instanceId}", - "/{entityId}/attrs", - "/attrs" ) fun handleMissingParametersOnModifyInstanceTemporal(): ResponseEntity<*> = missingPathErrorResponse( - "Missing some parameter(entity id, attribute id, instance id) when trying to modify temporal entity" + "Missing some parameter (entity id, attribute id, instance id) when trying to modify temporal entity" ) /** @@ -277,10 +274,6 @@ class TemporalEntityHandler( { it } ) - @DeleteMapping("/", "") - fun handleMissingEntityIdOnDeleteTemporalEntity(): ResponseEntity<*> = - missingPathErrorResponse("Missing entity id when trying to delete temporal entity") - /** * Implements 6.21.3.1 - Delete Attribute from Temporal Representation of an Entity */ diff --git a/search-service/src/test/kotlin/com/egm/stellio/search/authorization/AuthUtilsTests.kt b/search-service/src/test/kotlin/com/egm/stellio/search/authorization/AuthUtilsTests.kt index 4f03b3416..76672376a 100644 --- a/search-service/src/test/kotlin/com/egm/stellio/search/authorization/AuthUtilsTests.kt +++ b/search-service/src/test/kotlin/com/egm/stellio/search/authorization/AuthUtilsTests.kt @@ -6,13 +6,11 @@ import com.egm.stellio.shared.util.* import com.egm.stellio.shared.util.AuthContextModel.AUTHORIZATION_API_DEFAULT_CONTEXTS import com.egm.stellio.shared.util.AuthContextModel.AUTH_TERM_SAP import com.egm.stellio.shared.util.JsonLdUtils.expandAttribute -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Test -@OptIn(ExperimentalCoroutinesApi::class) class AuthUtilsTests { @Test diff --git a/search-service/src/test/kotlin/com/egm/stellio/search/authorization/AuthorizationServiceTests.kt b/search-service/src/test/kotlin/com/egm/stellio/search/authorization/AuthorizationServiceTests.kt index d1dd29e44..3b2b68514 100644 --- a/search-service/src/test/kotlin/com/egm/stellio/search/authorization/AuthorizationServiceTests.kt +++ b/search-service/src/test/kotlin/com/egm/stellio/search/authorization/AuthorizationServiceTests.kt @@ -8,13 +8,11 @@ import com.egm.stellio.shared.util.JsonLdUtils.NGSILD_CORE_CONTEXT import com.egm.stellio.shared.util.shouldSucceedWith import com.egm.stellio.shared.util.toUri import io.mockk.spyk -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test import org.junit.jupiter.api.fail -@OptIn(ExperimentalCoroutinesApi::class) class AuthorizationServiceTests { private val authorizationService = spyk(DisabledAuthorizationService()) diff --git a/search-service/src/test/kotlin/com/egm/stellio/search/authorization/EnabledAuthorizationServiceTests.kt b/search-service/src/test/kotlin/com/egm/stellio/search/authorization/EnabledAuthorizationServiceTests.kt index e9f5a8182..f1b3508c9 100644 --- a/search-service/src/test/kotlin/com/egm/stellio/search/authorization/EnabledAuthorizationServiceTests.kt +++ b/search-service/src/test/kotlin/com/egm/stellio/search/authorization/EnabledAuthorizationServiceTests.kt @@ -20,7 +20,6 @@ import com.ninjasquad.springmockk.MockkBean import io.mockk.coEvery import io.mockk.coVerify import io.mockk.coVerifyAll -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.Test @@ -30,7 +29,6 @@ import org.springframework.test.context.ActiveProfiles import java.net.URI import java.util.UUID -@OptIn(ExperimentalCoroutinesApi::class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = [EnabledAuthorizationService::class]) @ActiveProfiles("test") class EnabledAuthorizationServiceTests { diff --git a/search-service/src/test/kotlin/com/egm/stellio/search/authorization/EntityAccessRightsServiceTests.kt b/search-service/src/test/kotlin/com/egm/stellio/search/authorization/EntityAccessRightsServiceTests.kt index 2b392a9fa..83c03c1ea 100644 --- a/search-service/src/test/kotlin/com/egm/stellio/search/authorization/EntityAccessRightsServiceTests.kt +++ b/search-service/src/test/kotlin/com/egm/stellio/search/authorization/EntityAccessRightsServiceTests.kt @@ -19,7 +19,6 @@ import io.mockk.Called import io.mockk.coEvery import io.mockk.coVerify import io.r2dbc.postgresql.codec.Json -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.AfterEach @@ -36,7 +35,6 @@ import org.springframework.test.context.ActiveProfiles import java.net.URI import java.util.UUID -@OptIn(ExperimentalCoroutinesApi::class) @SpringBootTest @ActiveProfiles("test") class EntityAccessRightsServiceTests : WithTimescaleContainer { diff --git a/search-service/src/test/kotlin/com/egm/stellio/search/authorization/SubjectReferentialServiceTests.kt b/search-service/src/test/kotlin/com/egm/stellio/search/authorization/SubjectReferentialServiceTests.kt index 9c305e6e1..c27cac98e 100644 --- a/search-service/src/test/kotlin/com/egm/stellio/search/authorization/SubjectReferentialServiceTests.kt +++ b/search-service/src/test/kotlin/com/egm/stellio/search/authorization/SubjectReferentialServiceTests.kt @@ -8,7 +8,6 @@ import com.egm.stellio.shared.util.* import com.egm.stellio.shared.util.GlobalRole.STELLIO_ADMIN import com.egm.stellio.shared.util.GlobalRole.STELLIO_CREATOR import io.r2dbc.postgresql.codec.Json -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.AfterEach @@ -21,7 +20,6 @@ import org.springframework.data.r2dbc.core.R2dbcEntityTemplate import org.springframework.test.context.ActiveProfiles import java.util.UUID -@OptIn(ExperimentalCoroutinesApi::class) @SpringBootTest @ActiveProfiles("test") class SubjectReferentialServiceTests : WithTimescaleContainer { diff --git a/search-service/src/test/kotlin/com/egm/stellio/search/config/WebSecurityTestConfig.kt b/search-service/src/test/kotlin/com/egm/stellio/search/config/WebSecurityTestConfig.kt new file mode 100644 index 000000000..3049b3a95 --- /dev/null +++ b/search-service/src/test/kotlin/com/egm/stellio/search/config/WebSecurityTestConfig.kt @@ -0,0 +1,17 @@ +package com.egm.stellio.search.config + +import com.egm.stellio.shared.config.ApplicationProperties +import org.springframework.boot.test.context.TestConfiguration +import org.springframework.context.annotation.Bean +import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder +import org.springframework.security.oauth2.jwt.ReactiveJwtDecoders + +@TestConfiguration +class WebSecurityTestConfig( + private val applicationProperties: ApplicationProperties +) { + + @Bean + fun jwtDecoder(): ReactiveJwtDecoder = + ReactiveJwtDecoders.fromOidcIssuerLocation(applicationProperties.tenants[0].issuer) +} diff --git a/search-service/src/test/kotlin/com/egm/stellio/search/listener/IAMListenerTests.kt b/search-service/src/test/kotlin/com/egm/stellio/search/listener/IAMListenerTests.kt index 6194e764b..2432aae42 100644 --- a/search-service/src/test/kotlin/com/egm/stellio/search/listener/IAMListenerTests.kt +++ b/search-service/src/test/kotlin/com/egm/stellio/search/listener/IAMListenerTests.kt @@ -8,14 +8,12 @@ import com.egm.stellio.shared.util.loadSampleData import com.ninjasquad.springmockk.MockkBean import io.mockk.coEvery import io.mockk.coVerify -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.Test import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.context.SpringBootTest import org.springframework.test.context.ActiveProfiles -@OptIn(ExperimentalCoroutinesApi::class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = [IAMListener::class]) @ActiveProfiles("test") class IAMListenerTests { diff --git a/search-service/src/test/kotlin/com/egm/stellio/search/listener/ObservationEventListenerTests.kt b/search-service/src/test/kotlin/com/egm/stellio/search/listener/ObservationEventListenerTests.kt index c8349e553..da6ca7edb 100644 --- a/search-service/src/test/kotlin/com/egm/stellio/search/listener/ObservationEventListenerTests.kt +++ b/search-service/src/test/kotlin/com/egm/stellio/search/listener/ObservationEventListenerTests.kt @@ -11,7 +11,6 @@ import com.egm.stellio.shared.model.JsonLdEntity import com.egm.stellio.shared.util.* import com.ninjasquad.springmockk.MockkBean import io.mockk.* -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.Job import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.Test @@ -20,7 +19,6 @@ import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.context.SpringBootTest import org.springframework.test.context.ActiveProfiles -@OptIn(ExperimentalCoroutinesApi::class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = [ObservationEventListener::class]) @ActiveProfiles("test") class ObservationEventListenerTests { diff --git a/search-service/src/test/kotlin/com/egm/stellio/search/scope/ScopeServiceTests.kt b/search-service/src/test/kotlin/com/egm/stellio/search/scope/ScopeServiceTests.kt index e39b4ec5c..f343e1d02 100644 --- a/search-service/src/test/kotlin/com/egm/stellio/search/scope/ScopeServiceTests.kt +++ b/search-service/src/test/kotlin/com/egm/stellio/search/scope/ScopeServiceTests.kt @@ -9,7 +9,6 @@ import com.egm.stellio.search.util.deserializeAsMap import com.egm.stellio.shared.model.PaginationQuery import com.egm.stellio.shared.model.getScopes import com.egm.stellio.shared.util.* -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.runTest import org.assertj.core.api.Assertions.assertThat @@ -25,7 +24,6 @@ import org.springframework.data.r2dbc.core.R2dbcEntityTemplate import org.springframework.test.context.ActiveProfiles import java.util.stream.Stream -@OptIn(ExperimentalCoroutinesApi::class) @SpringBootTest @ActiveProfiles("test") class ScopeServiceTests : WithTimescaleContainer, WithKafkaContainer { diff --git a/search-service/src/test/kotlin/com/egm/stellio/search/service/AggregatedQueryServiceTests.kt b/search-service/src/test/kotlin/com/egm/stellio/search/service/AggregatedQueryServiceTests.kt index 173c1aca4..ea80463b1 100644 --- a/search-service/src/test/kotlin/com/egm/stellio/search/service/AggregatedQueryServiceTests.kt +++ b/search-service/src/test/kotlin/com/egm/stellio/search/service/AggregatedQueryServiceTests.kt @@ -4,7 +4,6 @@ import com.egm.stellio.search.model.* import com.egm.stellio.search.support.* import com.egm.stellio.shared.model.OperationNotSupportedException import com.egm.stellio.shared.util.* -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.assertj.core.api.AbstractObjectAssert import org.assertj.core.api.Assertions @@ -24,7 +23,6 @@ import java.time.OffsetTime import java.time.ZonedDateTime import java.util.UUID -@OptIn(ExperimentalCoroutinesApi::class) @SpringBootTest @ActiveProfiles("test") class AggregatedQueryServiceTests : WithTimescaleContainer, WithKafkaContainer { @@ -351,7 +349,7 @@ class AggregatedQueryServiceTests : WithTimescaleContainer, WithKafkaContainer { } @Test - fun `ìt should aggregate on the whole time range if no aggrPeriodDuration is given`() = runTest { + fun `it should aggregate on the whole time range if no aggrPeriodDuration is given`() = runTest { val temporalEntityAttribute = createTemporalEntityAttribute(TemporalEntityAttribute.AttributeValueType.NUMBER) (1..10).forEach { i -> val attributeInstance = gimmeAttributeInstance(teaUuid) @@ -374,6 +372,35 @@ class AggregatedQueryServiceTests : WithTimescaleContainer, WithKafkaContainer { } } + @ParameterizedTest + @CsvSource( + "P1D, 10", + "PT48H, 6", + "PT24H, 10", + "PT12H, 10", + "P1W, 2", + "P2W, 1", + "P1M, 2" + ) + fun `it should aggregate on the asked aggrPeriodDuration`( + aggrPeriodDuration: String, + expectedNumberOfBuckets: Int + ) = runTest { + val temporalEntityAttribute = createTemporalEntityAttribute(TemporalEntityAttribute.AttributeValueType.NUMBER) + val startTimestamp = ZonedDateTime.parse("2023-12-28T12:00:00Z") + (1..10).forEach { i -> + val attributeInstance = gimmeAttributeInstance(teaUuid) + .copy(time = startTimestamp.plusDays(i.toLong())) + attributeInstanceService.create(attributeInstance) + } + + val temporalEntitiesQuery = createTemporalEntitiesQuery("avg", aggrPeriodDuration) + attributeInstanceService.search(temporalEntitiesQuery, temporalEntityAttribute, startTimestamp) + .shouldSucceedWith { results -> + assertEquals(expectedNumberOfBuckets, results.size) + } + } + @Test fun `it should handle aggregates for an attribute having different types of values in history`() = runTest { val temporalEntityAttribute = createTemporalEntityAttribute(TemporalEntityAttribute.AttributeValueType.ARRAY) @@ -390,7 +417,7 @@ class AggregatedQueryServiceTests : WithTimescaleContainer, WithKafkaContainer { attributeInstanceService.search(temporalEntitiesQuery, temporalEntityAttribute, now) .shouldFail { assertInstanceOf(OperationNotSupportedException::class.java, it) - assertEquals(INCONSISTENT_VALUES_IN_AGGREGATION_MESSAGE, it.message) + assertEquals("cannot get array length of a scalar", it.message) } } @@ -409,12 +436,15 @@ class AggregatedQueryServiceTests : WithTimescaleContainer, WithKafkaContainer { return temporalEntityAttribute } - private fun createTemporalEntitiesQuery(aggrMethod: String): TemporalEntitiesQuery = + private fun createTemporalEntitiesQuery( + aggrMethod: String, + aggrPeriodDuration: String = "P1D" + ): TemporalEntitiesQuery = gimmeTemporalEntitiesQuery( TemporalQuery( timerel = TemporalQuery.Timerel.AFTER, timeAt = now.minusHours(1), - aggrPeriodDuration = "P1D", + aggrPeriodDuration = aggrPeriodDuration, aggrMethods = listOfNotNull(TemporalQuery.Aggregate.forMethod(aggrMethod)) ), withAggregatedValues = true diff --git a/search-service/src/test/kotlin/com/egm/stellio/search/service/AttributeInstanceServiceTests.kt b/search-service/src/test/kotlin/com/egm/stellio/search/service/AttributeInstanceServiceTests.kt index e309b89fa..733d27c3a 100644 --- a/search-service/src/test/kotlin/com/egm/stellio/search/service/AttributeInstanceServiceTests.kt +++ b/search-service/src/test/kotlin/com/egm/stellio/search/service/AttributeInstanceServiceTests.kt @@ -17,7 +17,6 @@ import com.egm.stellio.shared.util.JsonUtils.deserializeAsList import com.egm.stellio.shared.util.JsonUtils.deserializeAsMap import io.mockk.spyk import io.mockk.verify -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.runTest import org.assertj.core.api.Assertions.assertThat @@ -35,7 +34,6 @@ import java.time.ZoneOffset import java.time.ZonedDateTime import java.util.UUID -@OptIn(ExperimentalCoroutinesApi::class) @SpringBootTest @ActiveProfiles("test") class AttributeInstanceServiceTests : WithTimescaleContainer, WithKafkaContainer { diff --git a/search-service/src/test/kotlin/com/egm/stellio/search/service/AttributeServiceTests.kt b/search-service/src/test/kotlin/com/egm/stellio/search/service/AttributeServiceTests.kt index 0bf2eca95..16c039ffb 100644 --- a/search-service/src/test/kotlin/com/egm/stellio/search/service/AttributeServiceTests.kt +++ b/search-service/src/test/kotlin/com/egm/stellio/search/service/AttributeServiceTests.kt @@ -13,7 +13,6 @@ import com.egm.stellio.shared.model.ResourceNotFoundException 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 kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.AfterEach @@ -28,7 +27,6 @@ import org.springframework.test.context.ActiveProfiles import java.time.Instant import java.time.ZoneOffset -@OptIn(ExperimentalCoroutinesApi::class) @SpringBootTest @ActiveProfiles("test") class AttributeServiceTests : WithTimescaleContainer, WithKafkaContainer { diff --git a/search-service/src/test/kotlin/com/egm/stellio/search/service/EntityEventServiceTests.kt b/search-service/src/test/kotlin/com/egm/stellio/search/service/EntityEventServiceTests.kt index 061c8a34e..1dff12870 100644 --- a/search-service/src/test/kotlin/com/egm/stellio/search/service/EntityEventServiceTests.kt +++ b/search-service/src/test/kotlin/com/egm/stellio/search/service/EntityEventServiceTests.kt @@ -15,7 +15,6 @@ import com.egm.stellio.shared.web.DEFAULT_TENANT_URI import com.ninjasquad.springmockk.MockkBean import com.ninjasquad.springmockk.SpykBean import io.mockk.* -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.Test import org.springframework.boot.test.context.SpringBootTest @@ -23,7 +22,6 @@ import org.springframework.kafka.core.KafkaTemplate import org.springframework.test.context.ActiveProfiles import java.util.concurrent.CompletableFuture -@OptIn(ExperimentalCoroutinesApi::class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = [EntityEventService::class]) @ActiveProfiles("test") class EntityEventServiceTests { diff --git a/search-service/src/test/kotlin/com/egm/stellio/search/service/EntityOperationServiceTests.kt b/search-service/src/test/kotlin/com/egm/stellio/search/service/EntityOperationServiceTests.kt index df5985f15..fb0cc6bf6 100644 --- a/search-service/src/test/kotlin/com/egm/stellio/search/service/EntityOperationServiceTests.kt +++ b/search-service/src/test/kotlin/com/egm/stellio/search/service/EntityOperationServiceTests.kt @@ -19,7 +19,6 @@ import io.mockk.coEvery import io.mockk.coVerify import io.mockk.every import io.mockk.mockkClass -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertTrue @@ -29,7 +28,6 @@ import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.context.SpringBootTest import org.springframework.test.context.ActiveProfiles -@OptIn(ExperimentalCoroutinesApi::class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = [EntityOperationService::class]) @ActiveProfiles("test") class EntityOperationServiceTests { diff --git a/search-service/src/test/kotlin/com/egm/stellio/search/service/EntityPayloadServiceTests.kt b/search-service/src/test/kotlin/com/egm/stellio/search/service/EntityPayloadServiceTests.kt index 4fd42d6b1..c5e6b83ca 100644 --- a/search-service/src/test/kotlin/com/egm/stellio/search/service/EntityPayloadServiceTests.kt +++ b/search-service/src/test/kotlin/com/egm/stellio/search/service/EntityPayloadServiceTests.kt @@ -23,7 +23,6 @@ import com.egm.stellio.shared.util.JsonUtils.deserializeExpandedPayload import com.ninjasquad.springmockk.MockkBean import io.mockk.coEvery import io.mockk.coVerify -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatList @@ -38,7 +37,6 @@ import org.springframework.data.r2dbc.core.R2dbcEntityTemplate import org.springframework.test.context.ActiveProfiles import java.time.ZonedDateTime -@OptIn(ExperimentalCoroutinesApi::class) @SpringBootTest @ActiveProfiles("test") class EntityPayloadServiceTests : WithTimescaleContainer, WithKafkaContainer { diff --git a/search-service/src/test/kotlin/com/egm/stellio/search/service/EntityQueryServiceTests.kt b/search-service/src/test/kotlin/com/egm/stellio/search/service/EntityQueryServiceTests.kt index f993cd170..beee42708 100644 --- a/search-service/src/test/kotlin/com/egm/stellio/search/service/EntityQueryServiceTests.kt +++ b/search-service/src/test/kotlin/com/egm/stellio/search/service/EntityQueryServiceTests.kt @@ -11,7 +11,6 @@ import com.egm.stellio.shared.util.* import com.egm.stellio.shared.util.JsonLdUtils.NGSILD_NAME_PROPERTY import com.ninjasquad.springmockk.MockkBean import io.mockk.coEvery -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.runTest import org.assertj.core.api.Assertions.assertThat @@ -30,7 +29,6 @@ import org.springframework.data.relational.core.query.Update import org.springframework.test.context.ActiveProfiles import java.net.URI -@OptIn(ExperimentalCoroutinesApi::class) @SpringBootTest @ActiveProfiles("test") class EntityQueryServiceTests : WithTimescaleContainer, WithKafkaContainer { diff --git a/search-service/src/test/kotlin/com/egm/stellio/search/service/EntityTypeServiceTests.kt b/search-service/src/test/kotlin/com/egm/stellio/search/service/EntityTypeServiceTests.kt index fc2c0ad03..5e68807e3 100644 --- a/search-service/src/test/kotlin/com/egm/stellio/search/service/EntityTypeServiceTests.kt +++ b/search-service/src/test/kotlin/com/egm/stellio/search/service/EntityTypeServiceTests.kt @@ -13,7 +13,6 @@ import com.egm.stellio.shared.model.ResourceNotFoundException 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 kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.AfterEach @@ -29,7 +28,6 @@ import org.springframework.test.context.ActiveProfiles import java.time.Instant import java.time.ZoneOffset -@OptIn(ExperimentalCoroutinesApi::class) @SpringBootTest @ActiveProfiles("test") class EntityTypeServiceTests : WithTimescaleContainer, WithKafkaContainer { diff --git a/search-service/src/test/kotlin/com/egm/stellio/search/service/TemporalEntityAttributeServiceTests.kt b/search-service/src/test/kotlin/com/egm/stellio/search/service/TemporalEntityAttributeServiceTests.kt index cd0746676..c46bb84cc 100644 --- a/search-service/src/test/kotlin/com/egm/stellio/search/service/TemporalEntityAttributeServiceTests.kt +++ b/search-service/src/test/kotlin/com/egm/stellio/search/service/TemporalEntityAttributeServiceTests.kt @@ -16,7 +16,6 @@ import com.ninjasquad.springmockk.MockkBean import com.ninjasquad.springmockk.SpykBean import io.mockk.coEvery import io.mockk.coVerify -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.Assertions.* @@ -33,7 +32,6 @@ import java.time.Instant import java.time.ZoneOffset.UTC import java.time.ZonedDateTime -@OptIn(ExperimentalCoroutinesApi::class) @SpringBootTest @ActiveProfiles("test") class TemporalEntityAttributeServiceTests : WithTimescaleContainer, WithKafkaContainer { diff --git a/search-service/src/test/kotlin/com/egm/stellio/search/util/AttributeInstanceUtilsTests.kt b/search-service/src/test/kotlin/com/egm/stellio/search/util/AttributeInstanceUtilsTests.kt index fa5b12821..ebe8938aa 100644 --- a/search-service/src/test/kotlin/com/egm/stellio/search/util/AttributeInstanceUtilsTests.kt +++ b/search-service/src/test/kotlin/com/egm/stellio/search/util/AttributeInstanceUtilsTests.kt @@ -3,7 +3,6 @@ package com.egm.stellio.search.util import com.egm.stellio.search.model.TemporalEntityAttribute import com.egm.stellio.shared.util.DEFAULT_CONTEXTS import com.egm.stellio.shared.util.JsonLdUtils.expandAttribute -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test @@ -11,7 +10,6 @@ import org.springframework.test.context.ActiveProfiles import java.net.URI import java.time.LocalTime -@OptIn(ExperimentalCoroutinesApi::class) @ActiveProfiles("test") class AttributeInstanceUtilsTests { diff --git a/search-service/src/test/kotlin/com/egm/stellio/search/util/EntitiesQueryUtilsTests.kt b/search-service/src/test/kotlin/com/egm/stellio/search/util/EntitiesQueryUtilsTests.kt index d8fc25342..6027f7b76 100644 --- a/search-service/src/test/kotlin/com/egm/stellio/search/util/EntitiesQueryUtilsTests.kt +++ b/search-service/src/test/kotlin/com/egm/stellio/search/util/EntitiesQueryUtilsTests.kt @@ -11,7 +11,6 @@ import com.egm.stellio.shared.util.JsonLdUtils.NGSILD_DEFAULT_VOCAB import com.egm.stellio.shared.util.JsonLdUtils.NGSILD_OBSERVATION_SPACE_PROPERTY import io.mockk.every import io.mockk.mockkClass -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.Test @@ -20,7 +19,6 @@ import org.springframework.util.LinkedMultiValueMap import java.net.URI import java.time.ZonedDateTime -@OptIn(ExperimentalCoroutinesApi::class) @ActiveProfiles("test") class EntitiesQueryUtilsTests { diff --git a/search-service/src/test/kotlin/com/egm/stellio/search/web/AnonymousUserHandlerTests.kt b/search-service/src/test/kotlin/com/egm/stellio/search/web/AnonymousUserHandlerTests.kt index 7dd146eb0..82eed369e 100644 --- a/search-service/src/test/kotlin/com/egm/stellio/search/web/AnonymousUserHandlerTests.kt +++ b/search-service/src/test/kotlin/com/egm/stellio/search/web/AnonymousUserHandlerTests.kt @@ -2,6 +2,7 @@ package com.egm.stellio.search.web import com.egm.stellio.search.authorization.AuthorizationService import com.egm.stellio.search.config.SearchProperties +import com.egm.stellio.search.config.WebSecurityTestConfig import com.egm.stellio.search.service.EntityEventService import com.egm.stellio.search.service.EntityPayloadService import com.egm.stellio.search.service.QueryService @@ -14,6 +15,7 @@ import org.junit.jupiter.api.Test import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.context.properties.EnableConfigurationProperties import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest +import org.springframework.context.annotation.Import import org.springframework.http.HttpHeaders import org.springframework.security.test.context.support.WithAnonymousUser import org.springframework.test.context.ActiveProfiles @@ -23,6 +25,7 @@ import org.springframework.test.web.reactive.server.WebTestClient @WebFluxTest(EntityHandler::class) @EnableConfigurationProperties(ApplicationProperties::class, SearchProperties::class) @Suppress("unused") +@Import(WebSecurityTestConfig::class) class AnonymousUserHandlerTests { private val aquacHeaderLink = buildContextLinkHeader(AQUAC_COMPOUND_CONTEXT) diff --git a/search-service/src/test/kotlin/com/egm/stellio/search/web/AttributeHandlerTests.kt b/search-service/src/test/kotlin/com/egm/stellio/search/web/AttributeHandlerTests.kt index b2aed52c9..5e56e10fc 100644 --- a/search-service/src/test/kotlin/com/egm/stellio/search/web/AttributeHandlerTests.kt +++ b/search-service/src/test/kotlin/com/egm/stellio/search/web/AttributeHandlerTests.kt @@ -3,6 +3,7 @@ package com.egm.stellio.search.web import arrow.core.left import arrow.core.right import com.egm.stellio.search.config.SearchProperties +import com.egm.stellio.search.config.WebSecurityTestConfig import com.egm.stellio.search.model.AttributeDetails import com.egm.stellio.search.model.AttributeList import com.egm.stellio.search.model.AttributeType @@ -21,6 +22,7 @@ import org.junit.jupiter.api.Test import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.context.properties.EnableConfigurationProperties import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest +import org.springframework.context.annotation.Import import org.springframework.http.HttpHeaders import org.springframework.http.MediaType import org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.csrf @@ -31,6 +33,7 @@ import org.springframework.test.web.reactive.server.WebTestClient @ActiveProfiles("test") @WebFluxTest(AttributeHandler::class) @EnableConfigurationProperties(ApplicationProperties::class, SearchProperties::class) +@Import(WebSecurityTestConfig::class) class AttributeHandlerTests { @Autowired diff --git a/search-service/src/test/kotlin/com/egm/stellio/search/web/EntityAccessControlHandlerTests.kt b/search-service/src/test/kotlin/com/egm/stellio/search/web/EntityAccessControlHandlerTests.kt index 7ad3e3841..92f195f6c 100644 --- a/search-service/src/test/kotlin/com/egm/stellio/search/web/EntityAccessControlHandlerTests.kt +++ b/search-service/src/test/kotlin/com/egm/stellio/search/web/EntityAccessControlHandlerTests.kt @@ -7,6 +7,7 @@ import com.egm.stellio.search.authorization.EntityAccessRights import com.egm.stellio.search.authorization.EntityAccessRightsService import com.egm.stellio.search.authorization.User import com.egm.stellio.search.config.SearchProperties +import com.egm.stellio.search.config.WebSecurityTestConfig import com.egm.stellio.search.service.EntityPayloadService import com.egm.stellio.shared.config.ApplicationProperties import com.egm.stellio.shared.model.* @@ -34,13 +35,13 @@ import com.ninjasquad.springmockk.MockkBean import io.mockk.Called import io.mockk.coEvery import io.mockk.coVerify -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.BeforeAll import org.junit.jupiter.api.Test import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.context.properties.EnableConfigurationProperties import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest +import org.springframework.context.annotation.Import import org.springframework.http.HttpHeaders import org.springframework.http.HttpStatus import org.springframework.http.MediaType @@ -51,10 +52,10 @@ import org.springframework.test.web.reactive.server.WebTestClient import java.net.URI import java.time.Duration -@OptIn(ExperimentalCoroutinesApi::class) @ActiveProfiles("test") @WebFluxTest(EntityAccessControlHandler::class) @EnableConfigurationProperties(ApplicationProperties::class, SearchProperties::class) +@Import(WebSecurityTestConfig::class) class EntityAccessControlHandlerTests { private val authzHeaderLink = buildContextLinkHeader(AUTHORIZATION_CONTEXT) diff --git a/search-service/src/test/kotlin/com/egm/stellio/search/web/EntityHandlerTests.kt b/search-service/src/test/kotlin/com/egm/stellio/search/web/EntityHandlerTests.kt index 403a545a4..ee690c6e7 100644 --- a/search-service/src/test/kotlin/com/egm/stellio/search/web/EntityHandlerTests.kt +++ b/search-service/src/test/kotlin/com/egm/stellio/search/web/EntityHandlerTests.kt @@ -4,6 +4,7 @@ import arrow.core.left import arrow.core.right import com.egm.stellio.search.authorization.AuthorizationService import com.egm.stellio.search.config.SearchProperties +import com.egm.stellio.search.config.WebSecurityTestConfig import com.egm.stellio.search.model.* import com.egm.stellio.search.service.EntityEventService import com.egm.stellio.search.service.EntityPayloadService @@ -34,6 +35,7 @@ import org.junit.jupiter.api.Test import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.context.properties.EnableConfigurationProperties import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest +import org.springframework.context.annotation.Import import org.springframework.core.io.ClassPathResource import org.springframework.http.HttpHeaders import org.springframework.http.HttpMethod @@ -50,6 +52,7 @@ import java.time.* @ActiveProfiles("test") @WebFluxTest(EntityHandler::class) @EnableConfigurationProperties(ApplicationProperties::class, SearchProperties::class) +@Import(WebSecurityTestConfig::class) class EntityHandlerTests { private val aquacHeaderLink = buildContextLinkHeader(AQUAC_COMPOUND_CONTEXT) diff --git a/search-service/src/test/kotlin/com/egm/stellio/search/web/EntityOperationHandlerTests.kt b/search-service/src/test/kotlin/com/egm/stellio/search/web/EntityOperationHandlerTests.kt index 2359225eb..4a19fc35f 100644 --- a/search-service/src/test/kotlin/com/egm/stellio/search/web/EntityOperationHandlerTests.kt +++ b/search-service/src/test/kotlin/com/egm/stellio/search/web/EntityOperationHandlerTests.kt @@ -4,6 +4,7 @@ import arrow.core.left import arrow.core.right import com.egm.stellio.search.authorization.AuthorizationService import com.egm.stellio.search.config.SearchProperties +import com.egm.stellio.search.config.WebSecurityTestConfig import com.egm.stellio.search.model.EMPTY_UPDATE_RESULT import com.egm.stellio.search.model.EntityPayload import com.egm.stellio.search.model.UpdateResult @@ -19,7 +20,6 @@ import com.egm.stellio.shared.util.* import com.egm.stellio.shared.util.JsonLdUtils.NGSILD_DEFAULT_VOCAB import com.ninjasquad.springmockk.MockkBean import io.mockk.* -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.Job import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.Assertions.assertEquals @@ -31,6 +31,7 @@ import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.context.properties.EnableConfigurationProperties import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest +import org.springframework.context.annotation.Import import org.springframework.core.io.ClassPathResource import org.springframework.http.HttpStatus import org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.csrf @@ -39,11 +40,11 @@ import org.springframework.test.context.ActiveProfiles import org.springframework.test.web.reactive.server.WebTestClient import java.net.URI -@OptIn(ExperimentalCoroutinesApi::class) @AutoConfigureWebTestClient(timeout = "30000") @ActiveProfiles("test") @WebFluxTest(EntityOperationHandler::class) @EnableConfigurationProperties(ApplicationProperties::class, SearchProperties::class) +@Import(WebSecurityTestConfig::class) class EntityOperationHandlerTests { @Autowired diff --git a/search-service/src/test/kotlin/com/egm/stellio/search/web/EntityTypeHandlerTests.kt b/search-service/src/test/kotlin/com/egm/stellio/search/web/EntityTypeHandlerTests.kt index 95d263a76..2847bae64 100644 --- a/search-service/src/test/kotlin/com/egm/stellio/search/web/EntityTypeHandlerTests.kt +++ b/search-service/src/test/kotlin/com/egm/stellio/search/web/EntityTypeHandlerTests.kt @@ -3,6 +3,7 @@ package com.egm.stellio.search.web import arrow.core.left import arrow.core.right import com.egm.stellio.search.config.SearchProperties +import com.egm.stellio.search.config.WebSecurityTestConfig import com.egm.stellio.search.model.* import com.egm.stellio.search.model.AttributeType import com.egm.stellio.search.service.EntityTypeService @@ -19,6 +20,7 @@ import org.junit.jupiter.api.Test import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.context.properties.EnableConfigurationProperties import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest +import org.springframework.context.annotation.Import import org.springframework.http.HttpHeaders import org.springframework.http.MediaType import org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.csrf @@ -29,6 +31,7 @@ import org.springframework.test.web.reactive.server.WebTestClient @ActiveProfiles("test") @WebFluxTest(EntityTypeHandler::class) @EnableConfigurationProperties(ApplicationProperties::class, SearchProperties::class) +@Import(WebSecurityTestConfig::class) class EntityTypeHandlerTests { @Autowired diff --git a/search-service/src/test/kotlin/com/egm/stellio/search/web/TemporalEntityHandlerTests.kt b/search-service/src/test/kotlin/com/egm/stellio/search/web/TemporalEntityHandlerTests.kt index 4e494bb5c..738a02b21 100644 --- a/search-service/src/test/kotlin/com/egm/stellio/search/web/TemporalEntityHandlerTests.kt +++ b/search-service/src/test/kotlin/com/egm/stellio/search/web/TemporalEntityHandlerTests.kt @@ -6,6 +6,7 @@ import arrow.core.left import arrow.core.right import com.egm.stellio.search.authorization.AuthorizationService import com.egm.stellio.search.config.SearchProperties +import com.egm.stellio.search.config.WebSecurityTestConfig import com.egm.stellio.search.model.SimplifiedAttributeInstanceResult import com.egm.stellio.search.model.TemporalEntityAttribute import com.egm.stellio.search.model.TemporalQuery @@ -28,7 +29,6 @@ import io.mockk.Called import io.mockk.coEvery import io.mockk.coVerify import io.mockk.confirmVerified -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.hamcrest.core.Is import org.junit.jupiter.api.BeforeAll @@ -36,6 +36,7 @@ import org.junit.jupiter.api.Test import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.context.properties.EnableConfigurationProperties import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest +import org.springframework.context.annotation.Import import org.springframework.core.io.ClassPathResource import org.springframework.http.HttpHeaders import org.springframework.http.HttpMethod @@ -50,10 +51,10 @@ import java.time.ZoneOffset import java.time.ZonedDateTime import java.util.UUID -@OptIn(ExperimentalCoroutinesApi::class) @ActiveProfiles("test") @WebFluxTest(TemporalEntityHandler::class) @EnableConfigurationProperties(ApplicationProperties::class, SearchProperties::class) +@Import(WebSecurityTestConfig::class) class TemporalEntityHandlerTests { private lateinit var apicHeaderLink: String @@ -1255,20 +1256,11 @@ class TemporalEntityHandlerTests { } @Test - fun `delete temporal entity should return a 400 if entity id is missing`() { + fun `delete temporal entity should return a 405 if entity id is missing`() { webClient.delete() .uri("/ngsi-ld/v1/temporal/entities/") .exchange() - .expectStatus().isBadRequest - .expectBody().json( - """ - { - "type":"https://uri.etsi.org/ngsi-ld/errors/BadRequestData", - "title":"The request includes input data which does not meet the requirements of the operation", - "detail":"Missing entity id when trying to delete temporal entity" - } - """.trimIndent() - ) + .expectStatus().isEqualTo(HttpStatus.METHOD_NOT_ALLOWED) } @Test diff --git a/search-service/src/test/kotlin/com/egm/stellio/search/web/TemporalEntityOperationsHandlerTests.kt b/search-service/src/test/kotlin/com/egm/stellio/search/web/TemporalEntityOperationsHandlerTests.kt index 18589d8d0..d853c43e3 100644 --- a/search-service/src/test/kotlin/com/egm/stellio/search/web/TemporalEntityOperationsHandlerTests.kt +++ b/search-service/src/test/kotlin/com/egm/stellio/search/web/TemporalEntityOperationsHandlerTests.kt @@ -3,6 +3,7 @@ package com.egm.stellio.search.web import arrow.core.Either import com.egm.stellio.search.authorization.AuthorizationService import com.egm.stellio.search.config.SearchProperties +import com.egm.stellio.search.config.WebSecurityTestConfig import com.egm.stellio.search.model.TemporalQuery import com.egm.stellio.search.service.QueryService import com.egm.stellio.shared.config.ApplicationProperties @@ -16,6 +17,7 @@ import org.junit.jupiter.api.Test import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.context.properties.EnableConfigurationProperties import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest +import org.springframework.context.annotation.Import import org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.csrf import org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.mockJwt import org.springframework.test.context.ActiveProfiles @@ -25,6 +27,7 @@ import java.time.ZonedDateTime @ActiveProfiles("test") @WebFluxTest(TemporalEntityOperationsHandler::class) @EnableConfigurationProperties(ApplicationProperties::class, SearchProperties::class) +@Import(WebSecurityTestConfig::class) class TemporalEntityOperationsHandlerTests { private lateinit var apicHeaderLink: String diff --git a/search-service/src/test/kotlin/db/migration/V0_29_JsonLd_migrationTests.kt b/search-service/src/test/kotlin/db/migration/V0_29_JsonLd_migrationTests.kt index 22a0c4c2f..645011f5e 100644 --- a/search-service/src/test/kotlin/db/migration/V0_29_JsonLd_migrationTests.kt +++ b/search-service/src/test/kotlin/db/migration/V0_29_JsonLd_migrationTests.kt @@ -4,13 +4,11 @@ import com.egm.stellio.shared.util.APIC_COMPOUND_CONTEXT import com.egm.stellio.shared.util.JsonLdUtils import com.egm.stellio.shared.util.JsonUtils.deserializeAsMap import com.egm.stellio.shared.util.loadSampleData -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test import org.springframework.test.context.ActiveProfiles -@OptIn(ExperimentalCoroutinesApi::class) @ActiveProfiles("test") class V0_29_JsonLd_migrationTests { diff --git a/shared/build.gradle.kts b/shared/build.gradle.kts index fc91ba502..0b186007d 100644 --- a/shared/build.gradle.kts +++ b/shared/build.gradle.kts @@ -16,6 +16,10 @@ the().apply { } dependencies { + // https://docs.gradle.org/8.4/userguide/upgrading_version_8.html#test_framework_implementation_dependencies + testImplementation("org.springframework.boot:spring-boot-starter-test") + testRuntimeOnly("org.junit.platform:junit-platform-launcher") + testFixturesImplementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") testFixturesImplementation("com.fasterxml.jackson.module:jackson-module-kotlin") testFixturesImplementation("org.springframework:spring-core") @@ -28,7 +32,7 @@ dependencies { exclude(module = "mockito-core") } - detektPlugins("io.gitlab.arturbosch.detekt:detekt-formatting:1.23.3") + detektPlugins("io.gitlab.arturbosch.detekt:detekt-formatting:1.23.4") testFixturesApi("org.testcontainers:testcontainers") testFixturesApi("org.testcontainers:junit-jupiter") diff --git a/shared/src/main/kotlin/com/egm/stellio/shared/config/TenantAuthenticationManagerResolver.kt b/shared/src/main/kotlin/com/egm/stellio/shared/config/TenantAuthenticationManagerResolver.kt index 402f5a924..9490e21e5 100644 --- a/shared/src/main/kotlin/com/egm/stellio/shared/config/TenantAuthenticationManagerResolver.kt +++ b/shared/src/main/kotlin/com/egm/stellio/shared/config/TenantAuthenticationManagerResolver.kt @@ -4,6 +4,7 @@ import com.egm.stellio.shared.model.NonexistentTenantException import com.egm.stellio.shared.web.DEFAULT_TENANT_URI import com.egm.stellio.shared.web.NGSILD_TENANT_HEADER import jakarta.annotation.PostConstruct +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty import org.springframework.security.authentication.ReactiveAuthenticationManager import org.springframework.security.authentication.ReactiveAuthenticationManagerResolver import org.springframework.security.oauth2.jwt.ReactiveJwtDecoders @@ -13,6 +14,7 @@ import org.springframework.web.server.ServerWebExchange import reactor.core.publisher.Mono @Component +@ConditionalOnProperty("application.authentication.enabled") class TenantAuthenticationManagerResolver( private val applicationProperties: ApplicationProperties ) : ReactiveAuthenticationManagerResolver { diff --git a/shared/src/main/kotlin/com/egm/stellio/shared/config/TrailingSlashRedirectFilter.kt b/shared/src/main/kotlin/com/egm/stellio/shared/config/TrailingSlashRedirectFilter.kt new file mode 100644 index 000000000..0d24a8f43 --- /dev/null +++ b/shared/src/main/kotlin/com/egm/stellio/shared/config/TrailingSlashRedirectFilter.kt @@ -0,0 +1,23 @@ +package com.egm.stellio.shared.config + +import org.springframework.http.server.reactive.ServerHttpRequest +import org.springframework.stereotype.Component +import org.springframework.web.server.ServerWebExchange +import org.springframework.web.server.WebFilter +import org.springframework.web.server.WebFilterChain +import reactor.core.publisher.Mono + +@Component +class TrailingSlashRedirectFilter : WebFilter { + + override fun filter(exchange: ServerWebExchange, chain: WebFilterChain): Mono { + val request: ServerHttpRequest = exchange.request + val path: String = request.path.value() + if (path.endsWith("/")) { + val newPath = path.removeSuffix("/") + val newRequest: ServerHttpRequest = request.mutate().path(newPath).build() + return chain.filter(exchange.mutate().request(newRequest).build()) + } + return chain.filter(exchange) + } +} diff --git a/shared/src/main/kotlin/com/egm/stellio/shared/config/WebSecurityConfig.kt b/shared/src/main/kotlin/com/egm/stellio/shared/config/WebSecurityConfig.kt index baa4044fd..187ef5e8e 100644 --- a/shared/src/main/kotlin/com/egm/stellio/shared/config/WebSecurityConfig.kt +++ b/shared/src/main/kotlin/com/egm/stellio/shared/config/WebSecurityConfig.kt @@ -7,12 +7,12 @@ import org.springframework.security.config.web.server.ServerHttpSecurity import org.springframework.security.web.server.SecurityWebFilterChain @Configuration +@ConditionalOnProperty("application.authentication.enabled") class WebSecurityConfig( private val tenantAuthenticationManagerResolver: TenantAuthenticationManagerResolver ) { @Bean - @ConditionalOnProperty("application.authentication.enabled") fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain { http // disable CSRF as it does not fit with an HTTP REST API @@ -29,18 +29,4 @@ class WebSecurityConfig( return http.build() } - - @Bean - @ConditionalOnProperty("application.authentication.enabled", havingValue = "false") - fun springNoSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain { - http - // disable CSRF as it does not fit with an HTTP REST API - .csrf { csrf -> csrf.disable() } - // explicitly disable authentication to override Spring Security defaults - .authorizeExchange { exchanges -> - exchanges.pathMatchers("/**").permitAll() - } - - return http.build() - } } diff --git a/shared/src/main/kotlin/com/egm/stellio/shared/config/WebSecurityNoAuthConfig.kt b/shared/src/main/kotlin/com/egm/stellio/shared/config/WebSecurityNoAuthConfig.kt new file mode 100644 index 000000000..6b0b56793 --- /dev/null +++ b/shared/src/main/kotlin/com/egm/stellio/shared/config/WebSecurityNoAuthConfig.kt @@ -0,0 +1,25 @@ +package com.egm.stellio.shared.config + +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.security.config.web.server.ServerHttpSecurity +import org.springframework.security.web.server.SecurityWebFilterChain + +@Configuration +@ConditionalOnProperty("application.authentication.enabled", havingValue = "false") +class WebSecurityNoAuthConfig { + + @Bean + fun springNoSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain { + http + // disable CSRF as it does not fit with an HTTP REST API + .csrf { csrf -> csrf.disable() } + // explicitly disable authentication to override Spring Security defaults + .authorizeExchange { exchanges -> + exchanges.pathMatchers("/**").permitAll() + } + + return http.build() + } +} diff --git a/shared/src/main/kotlin/com/egm/stellio/shared/util/ApiResponses.kt b/shared/src/main/kotlin/com/egm/stellio/shared/util/ApiResponses.kt index 09612a8dd..b2ef5c67f 100644 --- a/shared/src/main/kotlin/com/egm/stellio/shared/util/ApiResponses.kt +++ b/shared/src/main/kotlin/com/egm/stellio/shared/util/ApiResponses.kt @@ -76,6 +76,8 @@ fun APIException.toErrorResponse(): ResponseEntity<*> = generateErrorResponse(HttpStatus.BAD_REQUEST, InvalidRequestResponse(this.message)) is BadRequestDataException -> generateErrorResponse(HttpStatus.BAD_REQUEST, BadRequestDataResponse(this.message)) + is OperationNotSupportedException -> + generateErrorResponse(HttpStatus.BAD_REQUEST, OperationNotSupportedResponse(this.message)) is AccessDeniedException -> generateErrorResponse(HttpStatus.FORBIDDEN, AccessDeniedResponse(this.message)) is NotImplementedException -> diff --git a/shared/src/main/kotlin/com/egm/stellio/shared/web/ExceptionHandler.kt b/shared/src/main/kotlin/com/egm/stellio/shared/web/ExceptionHandler.kt index 77a0ff227..412d791bf 100644 --- a/shared/src/main/kotlin/com/egm/stellio/shared/web/ExceptionHandler.kt +++ b/shared/src/main/kotlin/com/egm/stellio/shared/web/ExceptionHandler.kt @@ -10,6 +10,7 @@ import org.springframework.http.ProblemDetail import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.ExceptionHandler import org.springframework.web.bind.annotation.RestControllerAdvice +import org.springframework.web.server.MethodNotAllowedException import org.springframework.web.server.NotAcceptableStatusException import org.springframework.web.server.UnsupportedMediaTypeStatusException @@ -65,6 +66,8 @@ class ExceptionHandler { HttpStatus.NOT_ACCEPTABLE, NotAcceptableResponse(cause.message) ) + is MethodNotAllowedException -> + ResponseEntity.status(HttpStatus.METHOD_NOT_ALLOWED).body(cause.body) is NonexistentTenantException -> generateErrorResponse( HttpStatus.NOT_FOUND, NonexistentTenantResponse(cause.message) diff --git a/shared/src/test/kotlin/com/egm/stellio/shared/model/JsonLdEntityTests.kt b/shared/src/test/kotlin/com/egm/stellio/shared/model/JsonLdEntityTests.kt index 5021a4c27..a2a3a283a 100644 --- a/shared/src/test/kotlin/com/egm/stellio/shared/model/JsonLdEntityTests.kt +++ b/shared/src/test/kotlin/com/egm/stellio/shared/model/JsonLdEntityTests.kt @@ -7,7 +7,6 @@ import com.egm.stellio.shared.util.JsonLdUtils.NGSILD_LOCATION_TERM import com.egm.stellio.shared.util.JsonLdUtils.NGSILD_NAME_PROPERTY import com.egm.stellio.shared.util.JsonLdUtils.expandJsonLdEntity import com.egm.stellio.shared.util.JsonUtils.deserializeAsMap -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Assertions.assertEquals @@ -15,7 +14,6 @@ import org.junit.jupiter.api.Test import org.junit.jupiter.api.fail import org.springframework.http.MediaType -@OptIn(ExperimentalCoroutinesApi::class) class JsonLdEntityTests { private val normalizedJson = diff --git a/shared/src/test/kotlin/com/egm/stellio/shared/model/NgsiLdEntityTests.kt b/shared/src/test/kotlin/com/egm/stellio/shared/model/NgsiLdEntityTests.kt index 93c98642b..2bb23bf72 100644 --- a/shared/src/test/kotlin/com/egm/stellio/shared/model/NgsiLdEntityTests.kt +++ b/shared/src/test/kotlin/com/egm/stellio/shared/model/NgsiLdEntityTests.kt @@ -8,13 +8,11 @@ import com.egm.stellio.shared.util.JsonLdUtils.expandJsonLdEntity import com.egm.stellio.shared.util.shouldFail import com.egm.stellio.shared.util.shouldSucceedAndResult import com.egm.stellio.shared.util.toUri -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.Test import java.time.ZonedDateTime -@OptIn(ExperimentalCoroutinesApi::class) class NgsiLdEntityTests { @Test diff --git a/shared/src/test/kotlin/com/egm/stellio/shared/util/ApiUtilsTests.kt b/shared/src/test/kotlin/com/egm/stellio/shared/util/ApiUtilsTests.kt index 2785a8c78..d734bd0ac 100644 --- a/shared/src/test/kotlin/com/egm/stellio/shared/util/ApiUtilsTests.kt +++ b/shared/src/test/kotlin/com/egm/stellio/shared/util/ApiUtilsTests.kt @@ -4,7 +4,6 @@ import com.egm.stellio.shared.model.BadRequestDataException import com.egm.stellio.shared.util.JsonLdUtils.NGSILD_CORE_CONTEXT import com.egm.stellio.shared.util.OptionsParamValue.TEMPORAL_VALUES import com.egm.stellio.shared.web.CustomWebFilter -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.Test @@ -15,7 +14,6 @@ import org.springframework.test.context.ActiveProfiles import org.springframework.test.web.reactive.server.WebTestClient import java.util.Optional -@OptIn(ExperimentalCoroutinesApi::class) @ActiveProfiles("test") class ApiUtilsTests { private val webClient = WebTestClient.bindToController(MockkedHandler()).webFilter( diff --git a/shared/src/test/kotlin/com/egm/stellio/shared/util/DataRepresentationUtilsTests.kt b/shared/src/test/kotlin/com/egm/stellio/shared/util/DataRepresentationUtilsTests.kt index 9157c7345..990a06f4a 100644 --- a/shared/src/test/kotlin/com/egm/stellio/shared/util/DataRepresentationUtilsTests.kt +++ b/shared/src/test/kotlin/com/egm/stellio/shared/util/DataRepresentationUtilsTests.kt @@ -2,7 +2,6 @@ package com.egm.stellio.shared.util import com.egm.stellio.shared.model.BadRequestDataException import com.egm.stellio.shared.util.JsonUtils.deserializeAsMap -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertInstanceOf @@ -10,7 +9,6 @@ import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.CsvSource -@OptIn(ExperimentalCoroutinesApi::class) class DataRepresentationUtilsTests { @Test diff --git a/shared/src/test/kotlin/com/egm/stellio/shared/util/GeoQueryUtilsTests.kt b/shared/src/test/kotlin/com/egm/stellio/shared/util/GeoQueryUtilsTests.kt index 68b743eea..3426ad86f 100644 --- a/shared/src/test/kotlin/com/egm/stellio/shared/util/GeoQueryUtilsTests.kt +++ b/shared/src/test/kotlin/com/egm/stellio/shared/util/GeoQueryUtilsTests.kt @@ -6,14 +6,12 @@ import com.egm.stellio.shared.model.GeoQuery.GeometryType import com.egm.stellio.shared.util.JsonLdUtils.NGSILD_CORE_CONTEXT import com.egm.stellio.shared.util.JsonLdUtils.NGSILD_LOCATION_PROPERTY import com.egm.stellio.shared.util.JsonLdUtils.NGSILD_OPERATION_SPACE_PROPERTY -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertInstanceOf import org.junit.jupiter.api.Test import org.springframework.test.context.ActiveProfiles -@OptIn(ExperimentalCoroutinesApi::class) @ActiveProfiles("test") class GeoQueryUtilsTests { diff --git a/shared/src/test/kotlin/com/egm/stellio/shared/util/JsonLdUtilsTests.kt b/shared/src/test/kotlin/com/egm/stellio/shared/util/JsonLdUtilsTests.kt index a7742f756..35fc29ce3 100644 --- a/shared/src/test/kotlin/com/egm/stellio/shared/util/JsonLdUtilsTests.kt +++ b/shared/src/test/kotlin/com/egm/stellio/shared/util/JsonLdUtilsTests.kt @@ -14,14 +14,12 @@ import com.egm.stellio.shared.util.JsonLdUtils.extractContextFromInput import com.egm.stellio.shared.util.JsonLdUtils.extractRelationshipObject import com.egm.stellio.shared.util.JsonLdUtils.getAttributeFromExpandedAttributes import com.egm.stellio.shared.util.JsonUtils.deserializeAsMap -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows import org.springframework.http.MediaType -@OptIn(ExperimentalCoroutinesApi::class) class JsonLdUtilsTests { private val normalizedJson = diff --git a/shared/src/test/kotlin/com/egm/stellio/shared/util/JsonUtilsTests.kt b/shared/src/test/kotlin/com/egm/stellio/shared/util/JsonUtilsTests.kt index 49ae03062..ae61a4fc0 100644 --- a/shared/src/test/kotlin/com/egm/stellio/shared/util/JsonUtilsTests.kt +++ b/shared/src/test/kotlin/com/egm/stellio/shared/util/JsonUtilsTests.kt @@ -6,13 +6,11 @@ import com.egm.stellio.shared.util.JsonUtils.deserializeAs import com.egm.stellio.shared.util.JsonUtils.deserializeAsMap import com.egm.stellio.shared.util.JsonUtils.serializeObject import com.egm.stellio.shared.web.DEFAULT_TENANT_URI -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows -@OptIn(ExperimentalCoroutinesApi::class) class JsonUtilsTests { private val entityId = "urn:ngsi-ld:BeeHive:01".toUri() diff --git a/shared/src/test/kotlin/com/egm/stellio/shared/util/UriUtilsTests.kt b/shared/src/test/kotlin/com/egm/stellio/shared/util/UriUtilsTests.kt index 25cc3e15a..bf1aa85d3 100644 --- a/shared/src/test/kotlin/com/egm/stellio/shared/util/UriUtilsTests.kt +++ b/shared/src/test/kotlin/com/egm/stellio/shared/util/UriUtilsTests.kt @@ -29,7 +29,7 @@ class UriUtilsTests { } Assertions.assertEquals( "The supplied identifier was expected to be an URI but it is not: https://just\\AString " + - "(cause was: java.net.URISyntaxException: Illegal character in authority at index 8: " + + "(cause was: java.net.URISyntaxException: Illegal character in authority at index 12: " + "https://just\\AString)", exception.message ) diff --git a/subscription-service/Dockerfile b/subscription-service/Dockerfile index b0b229a42..5f41d609b 100644 --- a/subscription-service/Dockerfile +++ b/subscription-service/Dockerfile @@ -1,12 +1,12 @@ # You can build a Docker image of the module with the following command: # docker build --build-arg JAR_FILE=build/libs/subscription-service-{version}.jar -t stellio-subscription-service:{version} . -FROM eclipse-temurin:17-jre as builder +FROM eclipse-temurin:21-jre as builder WORKDIR application ARG JAR_FILE=build/libs/*.jar COPY ${JAR_FILE} application.jar RUN java -Djarmode=layertools -jar application.jar extract -FROM eclipse-temurin:17-jre +FROM eclipse-temurin:21-jre WORKDIR application COPY --from=builder application/dependencies/ ./ COPY --from=builder application/spring-boot-loader/ ./ diff --git a/subscription-service/build.gradle.kts b/subscription-service/build.gradle.kts index 08057ad3d..9e3f8e619 100644 --- a/subscription-service/build.gradle.kts +++ b/subscription-service/build.gradle.kts @@ -21,7 +21,7 @@ dependencies { implementation("com.jayway.jsonpath:json-path:2.8.0") implementation(project(":shared")) - detektPlugins("io.gitlab.arturbosch.detekt:detekt-formatting:1.23.3") + detektPlugins("io.gitlab.arturbosch.detekt:detekt-formatting:1.23.4") developmentOnly("org.springframework.boot:spring-boot-devtools") @@ -31,6 +31,9 @@ dependencies { testImplementation("org.testcontainers:postgresql") testImplementation("org.testcontainers:r2dbc") testImplementation(testFixtures(project(":shared"))) + // https://docs.gradle.org/8.4/userguide/upgrading_version_8.html#test_framework_implementation_dependencies + testImplementation("org.springframework.boot:spring-boot-starter-test") + testRuntimeOnly("org.junit.platform:junit-platform-launcher") } defaultTasks("bootRun") diff --git a/subscription-service/src/main/kotlin/com/egm/stellio/subscription/config/WebConfig.kt b/subscription-service/src/main/kotlin/com/egm/stellio/subscription/config/WebConfig.kt index 31011253d..10b0d1b39 100644 --- a/subscription-service/src/main/kotlin/com/egm/stellio/subscription/config/WebConfig.kt +++ b/subscription-service/src/main/kotlin/com/egm/stellio/subscription/config/WebConfig.kt @@ -3,7 +3,6 @@ package com.egm.stellio.subscription.config import org.springframework.context.annotation.Configuration import org.springframework.http.codec.ServerCodecConfigurer import org.springframework.web.reactive.config.EnableWebFlux -import org.springframework.web.reactive.config.PathMatchConfigurer import org.springframework.web.reactive.config.WebFluxConfigurer @Configuration @@ -13,8 +12,4 @@ class WebConfig : WebFluxConfigurer { override fun configureHttpMessageCodecs(configurer: ServerCodecConfigurer) { configurer.defaultCodecs().enableLoggingRequestDetails(true) } - - override fun configurePathMatching(configurer: PathMatchConfigurer) { - configurer.setUseTrailingSlashMatch(true) - } } diff --git a/subscription-service/src/main/kotlin/com/egm/stellio/subscription/web/SubscriptionHandler.kt b/subscription-service/src/main/kotlin/com/egm/stellio/subscription/web/SubscriptionHandler.kt index b095949d9..c5f09d59d 100644 --- a/subscription-service/src/main/kotlin/com/egm/stellio/subscription/web/SubscriptionHandler.kt +++ b/subscription-service/src/main/kotlin/com/egm/stellio/subscription/web/SubscriptionHandler.kt @@ -164,10 +164,6 @@ class SubscriptionHandler( { it } ) - @PatchMapping("/", "") - fun handleMissingIdOnUpdate(): ResponseEntity<*> = - missingPathErrorResponse("Missing id when trying to update a subscription") - /** * Implements 6.11.3.3 - Delete Subscription */ @@ -185,10 +181,6 @@ class SubscriptionHandler( { it } ) - @DeleteMapping("/", "") - fun handleMissingIdOnDelete(): ResponseEntity<*> = - missingPathErrorResponse("Missing id when trying to delete a subscription") - private suspend fun checkSubscriptionExists(subscriptionId: URI): Either = subscriptionService.exists(subscriptionId) .flatMap { diff --git a/subscription-service/src/test/kotlin/com/egm/stellio/subscription/job/TimeIntervalNotificationJobTest.kt b/subscription-service/src/test/kotlin/com/egm/stellio/subscription/job/TimeIntervalNotificationJobTest.kt index ad061c249..928347c3e 100644 --- a/subscription-service/src/test/kotlin/com/egm/stellio/subscription/job/TimeIntervalNotificationJobTest.kt +++ b/subscription-service/src/test/kotlin/com/egm/stellio/subscription/job/TimeIntervalNotificationJobTest.kt @@ -15,12 +15,11 @@ import com.egm.stellio.subscription.model.Notification import com.egm.stellio.subscription.model.Subscription import com.egm.stellio.subscription.service.NotificationService import com.egm.stellio.subscription.service.SubscriptionService -import com.egm.stellio.subscription.utils.gimmeRawSubscription +import com.egm.stellio.subscription.support.gimmeRawSubscription import com.github.tomakehurst.wiremock.client.WireMock.* import com.github.tomakehurst.wiremock.junit5.WireMockTest import com.ninjasquad.springmockk.MockkBean import io.mockk.* -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.Assertions.assertEquals @@ -32,7 +31,6 @@ import org.springframework.context.annotation.Import import org.springframework.test.context.ActiveProfiles import org.springframework.test.context.TestPropertySource -@OptIn(ExperimentalCoroutinesApi::class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = [TimeIntervalNotificationJob::class]) @WireMockTest(httpPort = 8089) @Import(WebClientConfig::class) diff --git a/subscription-service/src/test/kotlin/com/egm/stellio/subscription/listener/EntityEventListenerServiceTests.kt b/subscription-service/src/test/kotlin/com/egm/stellio/subscription/listener/EntityEventListenerServiceTests.kt index dbcf98709..65b59272e 100644 --- a/subscription-service/src/test/kotlin/com/egm/stellio/subscription/listener/EntityEventListenerServiceTests.kt +++ b/subscription-service/src/test/kotlin/com/egm/stellio/subscription/listener/EntityEventListenerServiceTests.kt @@ -12,14 +12,12 @@ import io.mockk.coEvery import io.mockk.coVerify import io.mockk.confirmVerified import io.mockk.mockkClass -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.Test import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.context.SpringBootTest import org.springframework.test.context.ActiveProfiles -@OptIn(ExperimentalCoroutinesApi::class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = [EntityEventListenerService::class]) @ActiveProfiles("test") class EntityEventListenerServiceTests { 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 e42b84926..8851824b3 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 @@ -15,14 +15,13 @@ import com.egm.stellio.subscription.model.Endpoint import com.egm.stellio.subscription.model.NotificationParams import com.egm.stellio.subscription.model.NotificationParams.FormatType import com.egm.stellio.subscription.model.NotificationTrigger.* -import com.egm.stellio.subscription.utils.gimmeRawSubscription +import com.egm.stellio.subscription.support.gimmeRawSubscription import com.github.tomakehurst.wiremock.client.WireMock.* import com.github.tomakehurst.wiremock.junit5.WireMockTest import com.ninjasquad.springmockk.MockkBean import io.mockk.coEvery import io.mockk.coVerify import io.mockk.confirmVerified -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.reactor.awaitSingle import kotlinx.coroutines.reactor.mono import kotlinx.coroutines.test.runTest @@ -34,7 +33,6 @@ import org.springframework.boot.test.context.SpringBootTest import org.springframework.http.HttpHeaders import org.springframework.test.context.ActiveProfiles -@OptIn(ExperimentalCoroutinesApi::class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = [NotificationService::class]) @WireMockTest(httpPort = 8089) @ActiveProfiles("test") 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 ef8a9edbf..04190a2ce 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 @@ -17,11 +17,11 @@ import com.egm.stellio.subscription.model.NotificationParams.FormatType import com.egm.stellio.subscription.model.NotificationParams.StatusType import com.egm.stellio.subscription.model.NotificationTrigger.* import com.egm.stellio.subscription.model.Subscription +import com.egm.stellio.subscription.support.WithKafkaContainer import com.egm.stellio.subscription.support.WithTimescaleContainer +import com.egm.stellio.subscription.support.gimmeSubscriptionFromMembers +import com.egm.stellio.subscription.support.loadAndDeserializeSubscription import com.egm.stellio.subscription.utils.ParsingUtils -import com.egm.stellio.subscription.utils.gimmeSubscriptionFromMembers -import com.egm.stellio.subscription.utils.loadAndDeserializeSubscription -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.delay import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.runTest @@ -43,11 +43,10 @@ import java.time.ZonedDateTime import java.util.UUID import kotlin.time.Duration -@OptIn(ExperimentalCoroutinesApi::class) @SpringBootTest @ActiveProfiles("test") @TestPropertySource(properties = ["application.authentication.enabled=false"]) -class SubscriptionServiceTests : WithTimescaleContainer { +class SubscriptionServiceTests : WithTimescaleContainer, WithKafkaContainer { @Autowired private lateinit var subscriptionService: SubscriptionService diff --git a/subscription-service/src/test/kotlin/com/egm/stellio/subscription/utils/FixtureUtils.kt b/subscription-service/src/test/kotlin/com/egm/stellio/subscription/support/FixtureUtils.kt similarity index 97% rename from subscription-service/src/test/kotlin/com/egm/stellio/subscription/utils/FixtureUtils.kt rename to subscription-service/src/test/kotlin/com/egm/stellio/subscription/support/FixtureUtils.kt index 457911431..a7041dec6 100644 --- a/subscription-service/src/test/kotlin/com/egm/stellio/subscription/utils/FixtureUtils.kt +++ b/subscription-service/src/test/kotlin/com/egm/stellio/subscription/support/FixtureUtils.kt @@ -1,4 +1,4 @@ -package com.egm.stellio.subscription.utils +package com.egm.stellio.subscription.support import com.egm.stellio.shared.util.APIC_COMPOUND_CONTEXT import com.egm.stellio.shared.util.JsonLdUtils.NGSILD_CORE_CONTEXT @@ -9,6 +9,7 @@ import com.egm.stellio.shared.util.shouldSucceedAndResult import com.egm.stellio.shared.util.toUri import com.egm.stellio.subscription.model.* import com.egm.stellio.subscription.model.NotificationParams.FormatType +import com.egm.stellio.subscription.utils.ParsingUtils import java.time.Instant import java.time.ZoneOffset diff --git a/subscription-service/src/test/kotlin/com/egm/stellio/subscription/support/WithKafkaContainer.kt b/subscription-service/src/test/kotlin/com/egm/stellio/subscription/support/WithKafkaContainer.kt new file mode 100644 index 000000000..1c4d9cb3a --- /dev/null +++ b/subscription-service/src/test/kotlin/com/egm/stellio/subscription/support/WithKafkaContainer.kt @@ -0,0 +1,29 @@ +package com.egm.stellio.subscription.support + +import org.springframework.test.context.DynamicPropertyRegistry +import org.springframework.test.context.DynamicPropertySource +import org.testcontainers.containers.KafkaContainer +import org.testcontainers.utility.DockerImageName + +interface WithKafkaContainer { + + companion object { + + private val kafkaImage: DockerImageName = + DockerImageName.parse("confluentinc/cp-kafka:7.3.1") + + private val kafkaContainer = KafkaContainer(kafkaImage).apply { + withReuse(true) + } + + @JvmStatic + @DynamicPropertySource + fun properties(registry: DynamicPropertyRegistry) { + registry.add("spring.kafka.bootstrap-servers") { kafkaContainer.bootstrapServers } + } + + init { + kafkaContainer.start() + } + } +} diff --git a/subscription-service/src/test/kotlin/com/egm/stellio/subscription/utils/ParsingUtilsTests.kt b/subscription-service/src/test/kotlin/com/egm/stellio/subscription/utils/ParsingUtilsTests.kt index 63ffe5d04..1ce8e3765 100644 --- a/subscription-service/src/test/kotlin/com/egm/stellio/subscription/utils/ParsingUtilsTests.kt +++ b/subscription-service/src/test/kotlin/com/egm/stellio/subscription/utils/ParsingUtilsTests.kt @@ -6,7 +6,6 @@ import com.egm.stellio.shared.util.JsonLdUtils.NGSILD_SUBSCRIPTION_TERM import com.egm.stellio.shared.util.shouldSucceedWith import com.egm.stellio.shared.util.toUri import com.egm.stellio.subscription.model.EndpointInfo -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.Test @@ -14,7 +13,6 @@ import org.junit.jupiter.api.fail import org.springframework.boot.test.context.SpringBootTest import org.springframework.test.context.ActiveProfiles -@OptIn(ExperimentalCoroutinesApi::class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = [ParsingUtils::class]) @ActiveProfiles("test") class ParsingUtilsTests { diff --git a/subscription-service/src/test/kotlin/com/egm/stellio/subscription/web/SubscriptionHandlerTests.kt b/subscription-service/src/test/kotlin/com/egm/stellio/subscription/web/SubscriptionHandlerTests.kt index 723b5ea82..d82ca7adf 100644 --- a/subscription-service/src/test/kotlin/com/egm/stellio/subscription/web/SubscriptionHandlerTests.kt +++ b/subscription-service/src/test/kotlin/com/egm/stellio/subscription/web/SubscriptionHandlerTests.kt @@ -9,13 +9,12 @@ import com.egm.stellio.shared.model.InternalErrorException import com.egm.stellio.shared.util.* import com.egm.stellio.shared.util.JsonUtils.deserializeAsMap import com.egm.stellio.subscription.service.SubscriptionService -import com.egm.stellio.subscription.utils.gimmeRawSubscription +import com.egm.stellio.subscription.support.gimmeRawSubscription import com.ninjasquad.springmockk.MockkBean import io.mockk.Called import io.mockk.coEvery import io.mockk.coVerify import io.mockk.confirmVerified -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.hamcrest.core.Is import org.junit.jupiter.api.BeforeAll @@ -32,7 +31,6 @@ import org.springframework.security.test.web.reactive.server.SecurityMockServerC import org.springframework.test.context.ActiveProfiles import org.springframework.test.web.reactive.server.WebTestClient -@OptIn(ExperimentalCoroutinesApi::class) @AutoConfigureWebTestClient(timeout = "30000") @ActiveProfiles("test") @WebFluxTest(SubscriptionHandler::class) @@ -415,7 +413,7 @@ class SubscriptionHandlerTests { coEvery { subscriptionService.getSubscriptions(any(), any(), any()) } returns listOf(subscription) webClient.get() - .uri("/ngsi-ld/v1/subscriptions?${subscription.id}&limit=0&offset=1&count=true") + .uri("/ngsi-ld/v1/subscriptions?limit=0&offset=1&count=true") .exchange() .expectStatus().isOk .expectHeader().valueEquals(RESULTS_COUNT_HEADER, "3")