diff --git a/backend/api/src/main/kotlin/io/tolgee/hateoas/ee/SelfHostedEePlanModel.kt b/backend/api/src/main/kotlin/io/tolgee/hateoas/ee/SelfHostedEePlanModel.kt index 2545eaee2e..894f538fd8 100644 --- a/backend/api/src/main/kotlin/io/tolgee/hateoas/ee/SelfHostedEePlanModel.kt +++ b/backend/api/src/main/kotlin/io/tolgee/hateoas/ee/SelfHostedEePlanModel.kt @@ -1,5 +1,8 @@ package io.tolgee.hateoas.ee +import com.fasterxml.jackson.annotation.JsonGetter +import com.fasterxml.jackson.annotation.JsonIgnore +import com.fasterxml.jackson.annotation.JsonSetter import io.tolgee.constants.Feature import org.springframework.hateoas.RepresentationModel import org.springframework.hateoas.server.core.Relation @@ -10,10 +13,26 @@ open class SelfHostedEePlanModel( val id: Long = 0, var name: String = "", val public: Boolean = true, - val enabledFeatures: Array = arrayOf(), + @JsonIgnore + var enabledFeatures: Array = arrayOf(), val prices: PlanPricesModel, val includedUsage: PlanIncludedUsageModel = PlanIncludedUsageModel(), val hasYearlyPrice: Boolean = false, val free: Boolean, val nonCommercial: Boolean, -) : RepresentationModel() +) : RepresentationModel() { + /** + * We need to provide this setter so unrecognized features are ignored in situation + * that self-hosted instance is not upgraded to version containing the new features introduced + * in Tolgee cloud. + */ + @JsonSetter("enabledFeatures") + fun setJsonEnabledFeatures(features: Set) { + this.enabledFeatures = features.mapNotNull { Feature.findByName(it) }.toTypedArray() + } + + @JsonGetter("enabledFeatures") + fun getJsonEnabledFeatures(): Array { + return this.enabledFeatures + } +} diff --git a/backend/data/src/main/kotlin/io/tolgee/component/SentryBeforeSendCallback.kt b/backend/data/src/main/kotlin/io/tolgee/component/SentryBeforeSendCallback.kt index c1ca10d58e..910a8f76b5 100644 --- a/backend/data/src/main/kotlin/io/tolgee/component/SentryBeforeSendCallback.kt +++ b/backend/data/src/main/kotlin/io/tolgee/component/SentryBeforeSendCallback.kt @@ -18,6 +18,7 @@ class SentryBeforeSendCallback : SentryOptions.BeforeSendCallback { listOf( "FailedDontRequeueException", "ClientAbortException", + "AsyncRequestNotUsableException", ) } diff --git a/backend/data/src/main/kotlin/io/tolgee/constants/Feature.kt b/backend/data/src/main/kotlin/io/tolgee/constants/Feature.kt index 5d142193c4..948729d617 100644 --- a/backend/data/src/main/kotlin/io/tolgee/constants/Feature.kt +++ b/backend/data/src/main/kotlin/io/tolgee/constants/Feature.kt @@ -21,4 +21,12 @@ enum class Feature { TASKS, SSO, ORDER_TRANSLATION, + + ; + + companion object { + fun findByName(name: String): Feature? { + return entries.find { it.name == name } + } + } } diff --git a/backend/data/src/main/kotlin/io/tolgee/service/security/ApiKeyService.kt b/backend/data/src/main/kotlin/io/tolgee/service/security/ApiKeyService.kt index 93e5a4038e..39a2645585 100644 --- a/backend/data/src/main/kotlin/io/tolgee/service/security/ApiKeyService.kt +++ b/backend/data/src/main/kotlin/io/tolgee/service/security/ApiKeyService.kt @@ -243,11 +243,13 @@ class ApiKeyService( } private fun logTransactionIsolation() { + val isolationLevel = + entityManager.createNativeQuery("show transaction_isolation") + .singleResult as String + val message = "Transaction isolation level: $isolationLevel" + Sentry.addBreadcrumb(message) if (logger.isDebugEnabled) { - val isolationLevel = - entityManager.createNativeQuery("show transaction_isolation") - .singleResult as String - logger.debug("Transaction isolation level: $isolationLevel") + logger.debug(message) } } diff --git a/ee/backend/tests/src/test/kotlin/io/tolgee/ee/unit/SelfHostedEePlanModelTest.kt b/ee/backend/tests/src/test/kotlin/io/tolgee/ee/unit/SelfHostedEePlanModelTest.kt new file mode 100644 index 0000000000..0677eded37 --- /dev/null +++ b/ee/backend/tests/src/test/kotlin/io/tolgee/ee/unit/SelfHostedEePlanModelTest.kt @@ -0,0 +1,39 @@ +package io.tolgee.ee.unit + +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.module.kotlin.readValue +import io.tolgee.constants.Feature +import io.tolgee.hateoas.ee.PlanPricesModel +import io.tolgee.hateoas.ee.SelfHostedEePlanModel +import io.tolgee.testing.assert +import org.junit.jupiter.api.Test +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.context.SpringBootTest + +@SpringBootTest +class SelfHostedEePlanModelTest { + @Autowired + private lateinit var objectMapper: ObjectMapper + + @Test + fun `parses plan response with unknown features`() { + val parsed = + objectMapper.readValue( + """ + { + "enabledFeatures": ["I've made this up", "ASSISTED_UPDATES"], + "prices": $pricesJson, + "free": false, + "nonCommercial": false + } + """.trimIndent(), + ) + + parsed.enabledFeatures.toList().assert.containsExactly(Feature.ASSISTED_UPDATES) + } + + val pricesJson: String + get() { + return objectMapper.writeValueAsString(PlanPricesModel()) + } +}