Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,38 @@ package io.tolgee.util
import io.tolgee.events.EntityPreCommitEvent
import io.tolgee.events.OnEntityPreDelete
import io.tolgee.events.OnEntityPrePersist
import io.tolgee.events.OnEntityPreUpdate
import io.tolgee.model.translation.Translation

fun EntityPreCommitEvent.getUsageIncreaseAmount(): Long {
return when (this) {
is OnEntityPrePersist -> 1
is OnEntityPreDelete -> -1
else -> 0
is OnEntityPrePersist -> {
1
}

is OnEntityPreDelete -> {
-1
}

is OnEntityPreUpdate -> {
val entity = this.entity
if (entity is Translation) {
val textIndex = this.propertyNames?.indexOf("text") ?: -1
if (textIndex != -1) {
val previousText = this.previousState?.get(textIndex) as? String
val currentText = entity.text
if (previousText.isNullOrEmpty() && !currentText.isNullOrEmpty()) {
Comment thread
dkrizan marked this conversation as resolved.
return 1
} else if (!previousText.isNullOrEmpty() && currentText.isNullOrEmpty()) {
return -1
}
}
}
0
}

else -> {
0
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,7 @@ class EeKeyCountLimitListener(
return
}

KeysLimitChecker(
required = keyCount,
limits = limits,
).check()
KeysLimitChecker(limits = limits).check(keyCount)
}

private val limits by lazy {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,11 @@ class SelfHostedKeysSeatsLimitsChecker(
}

private fun checkKeysLimits() {
keys ?: return
KeysLimitChecker(required = keys, limits = limits).check()
KeysLimitChecker(limits = limits).check(keys)
}

private fun checkSeatLimits() {
seats ?: return
SeatsLimitChecker(required = seats, limits = limits).check()
SeatsLimitChecker(limits = limits).check(seats)
}

private val limits by lazy {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,36 @@ package io.tolgee.ee.component.limitsAndReporting.generic

import io.tolgee.dtos.UsageLimits

class GenericLimitChecker(
private val required: Long,
open class GenericLimitChecker(
private val limit: UsageLimits.Limit,
private val isPayAsYouGo: Boolean,
/**
* When plan is fixed (the opposite of pay-as-you-go), this exception will be thrown
*/
private val includedUsageExceededExceptionProvider: () -> Exception,
private val includedUsageExceededExceptionProvider: (Long) -> Exception,
/**
* When plan is pay-as-you-go, this exception will be thrown
*/
private val spendingLimitExceededExceptionProvider: () -> Exception,
private val spendingLimitExceededExceptionProvider: (Long) -> Exception,
) {
fun checkLimit() {
fun check(required: Long?) = check { required }

fun check(requiredProvider: () -> Long?) {
if (limit.limit < 0) {
return
}

val required = requiredProvider() ?: return

if (!isPayAsYouGo) {
if (required > limit.included) {
throw includedUsageExceededExceptionProvider()
throw includedUsageExceededExceptionProvider(required)
}
return
}

if (required > limit.limit) {
throw spendingLimitExceededExceptionProvider()
throw spendingLimitExceededExceptionProvider(required)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,14 @@ import io.tolgee.exceptions.limits.PlanLimitExceededKeysException
import io.tolgee.exceptions.limits.PlanSpendingLimitExceededKeysException

class KeysLimitChecker(
private val required: Long?,
private val limits: UsageLimits,
) {
fun check() {
required ?: return

GenericLimitChecker(
required,
limit = limits.keys,
isPayAsYouGo = limits.isPayAsYouGo,
includedUsageExceededExceptionProvider = {
PlanLimitExceededKeysException(
required = required,
limit = limits.keys.limit,
)
},
spendingLimitExceededExceptionProvider = {
PlanSpendingLimitExceededKeysException(required = required, limit = limits.keys.limit)
},
).checkLimit()
}
}
limits: UsageLimits,
) : GenericLimitChecker(
limit = limits.keys,
isPayAsYouGo = limits.isPayAsYouGo,
includedUsageExceededExceptionProvider = { req ->
PlanLimitExceededKeysException(required = req, limit = limits.keys.limit)
},
spendingLimitExceededExceptionProvider = { req ->
PlanSpendingLimitExceededKeysException(required = req, limit = limits.keys.limit)
},
)
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,17 @@ import io.tolgee.exceptions.BadRequestException
import io.tolgee.exceptions.limits.PlanLimitExceededSeatsException
import io.tolgee.exceptions.limits.PlanSpendingLimitExceededSeatsException

open class SeatsLimitChecker(
private val required: Long?,
private val limits: UsageLimits,
) {
fun check() {
required ?: return

GenericLimitChecker(
required = required,
limit = limits.seats,
isPayAsYouGo = limits.isPayAsYouGo,
includedUsageExceededExceptionProvider = {
getIncludedUsageExceededException()
},
spendingLimitExceededExceptionProvider = {
getSpendingLimitExceededException()
},
).checkLimit()
}

open fun getIncludedUsageExceededException(): BadRequestException {
return PlanLimitExceededSeatsException(required!!, limit = limits.seats.limit)
}

open fun getSpendingLimitExceededException(): BadRequestException {
return PlanSpendingLimitExceededSeatsException(required!!, limit = limits.seats.limit)
}
}
class SeatsLimitChecker(
limits: UsageLimits,
includedUsageExceededExceptionProvider: (Long) -> BadRequestException = { req ->
PlanLimitExceededSeatsException(req, limit = limits.seats.limit)
},
spendingLimitExceededExceptionProvider: (Long) -> BadRequestException = { req ->
PlanSpendingLimitExceededSeatsException(req, limit = limits.seats.limit)
},
) : GenericLimitChecker(
limit = limits.seats,
isPayAsYouGo = limits.isPayAsYouGo,
includedUsageExceededExceptionProvider = includedUsageExceededExceptionProvider,
spendingLimitExceededExceptionProvider = spendingLimitExceededExceptionProvider,
)
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,14 @@ import io.tolgee.exceptions.limits.PlanLimitExceededStringsException
import io.tolgee.exceptions.limits.PlanSpendingLimitExceededStringsException

class StringsLimitChecker(
private val required: Long?,
private val limits: UsageLimits,
) {
fun check() {
required ?: return

GenericLimitChecker(
required,
limit = limits.strings,
isPayAsYouGo = limits.isPayAsYouGo,
includedUsageExceededExceptionProvider = {
PlanLimitExceededStringsException(
required = required,
limit = limits.strings.limit,
)
},
spendingLimitExceededExceptionProvider = {
PlanSpendingLimitExceededStringsException(required = required, limit = limits.strings.limit)
},
).checkLimit()
}
}
limits: UsageLimits,
) : GenericLimitChecker(
limit = limits.strings,
isPayAsYouGo = limits.isPayAsYouGo,
includedUsageExceededExceptionProvider = { req ->
PlanLimitExceededStringsException(required = req, limit = limits.strings.limit)
},
spendingLimitExceededExceptionProvider = { req ->
PlanSpendingLimitExceededStringsException(required = req, limit = limits.strings.limit)
},
)
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import io.tolgee.ee.model.EeSubscription
import io.tolgee.ee.repository.EeSubscriptionRepository
import io.tolgee.ee.service.eeSubscription.cloudClient.TolgeeCloudLicencingClient
import io.tolgee.exceptions.BadRequestException
import io.tolgee.exceptions.limits.PlanLimitExceededSeatsException
import io.tolgee.hateoas.ee.PrepareSetEeLicenceKeyModel
import io.tolgee.hateoas.ee.SelfHostedEeSubscriptionModel
import io.tolgee.service.InstanceIdService
Expand Down Expand Up @@ -169,16 +170,16 @@ class EeSubscriptionServiceImpl(
return
}

object : SeatsLimitChecker(
required = seats,
limits = selfHostedLimitsProvider.getLimits(),
) {
override fun getIncludedUsageExceededException(): BadRequestException {
self.findSubscriptionDto()
?: return BadRequestException(Message.FREE_SELF_HOSTED_SEAT_LIMIT_EXCEEDED)
return super.getIncludedUsageExceededException()
}
}.check()
val limits = selfHostedLimitsProvider.getLimits()
SeatsLimitChecker(
limits = limits,
includedUsageExceededExceptionProvider = { req ->
self
.findSubscriptionDto()
?.let { PlanLimitExceededSeatsException(req, limit = limits.seats.limit) }
?: BadRequestException(Message.FREE_SELF_HOSTED_SEAT_LIMIT_EXCEEDED)
},
).check(seats)
}

@CacheEvict(Caches.Companion.EE_SUBSCRIPTION, key = "1")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package io.tolgee.ee.unit.limitsAndReporting

import io.tolgee.dtos.UsageLimits
import io.tolgee.ee.component.limitsAndReporting.generic.GenericLimitChecker
import org.junit.jupiter.api.Assertions.assertDoesNotThrow
import org.junit.jupiter.api.Assertions.assertFalse
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows

class GenericLimitCheckerTest {
private fun fixedLimit(
included: Long,
total: Long = included,
) = UsageLimits.Limit(included = included, limit = total)

private fun unlimitedLimit() = UsageLimits.Limit(included = -1, limit = -1)

private fun zeroLimit() = UsageLimits.Limit(included = 0, limit = 0)

private fun checker(
limit: UsageLimits.Limit,
isPayAsYouGo: Boolean = false,
includedProvider: (Long) -> Exception = { req -> RuntimeException("included: $req") },
spendingProvider: (Long) -> Exception = { req -> RuntimeException("spending: $req") },
) = GenericLimitChecker(
limit = limit,
isPayAsYouGo = isPayAsYouGo,
includedUsageExceededExceptionProvider = includedProvider,
spendingLimitExceededExceptionProvider = spendingProvider,
)

@Test
fun `zero limit - throws included exception (not treated as unlimited)`() {
val checker = checker(zeroLimit())

val ex = assertThrows<RuntimeException> { checker.check { 999L } }

assert(ex.message == "included: 999")
}

@Test
fun `minus one limit - no exception and provider not called`() {
var providerCalled = false
val checker =
checker(
unlimitedLimit(),
includedProvider = {
providerCalled = true
RuntimeException()
},
)

assertDoesNotThrow { checker.check { 999L } }
assertFalse(providerCalled)
}

@Test
fun `provider returns null - no exception`() {
val checker = checker(fixedLimit(100))
assertDoesNotThrow { checker.check { null } }
}

@Test
fun `fixed plan - required within included - no exception`() {
val checker = checker(fixedLimit(100))
assertDoesNotThrow { checker.check { 100L } }
}

@Test
fun `fixed plan - required exceeds included - throws included exception`() {
val checker = checker(fixedLimit(included = 100, total = 200))

val ex = assertThrows<RuntimeException> { checker.check { 150L } }

assert(ex.message == "included: 150")
}
Comment thread
dkrizan marked this conversation as resolved.

@Test
fun `payg - required exceeds included but within limit - no exception`() {
val checker = checker(fixedLimit(included = 100, total = 200), isPayAsYouGo = true)
assertDoesNotThrow { checker.check { 150L } }
}

@Test
fun `payg - required equals limit - no exception`() {
val checker = checker(fixedLimit(included = 100, total = 200), isPayAsYouGo = true)
assertDoesNotThrow { checker.check { 200L } }
}

@Test
fun `payg - required exceeds limit - throws spending exception`() {
val checker = checker(fixedLimit(included = 100, total = 200), isPayAsYouGo = true)

val ex = assertThrows<RuntimeException> { checker.check { 201L } }

assert(ex.message == "spending: 201")
}

@Test
fun `check nullable overload with value - behaves same as lambda overload`() {
val checker = checker(fixedLimit(included = 100, total = 200))

val ex = assertThrows<RuntimeException> { checker.check(150L) }

assert(ex.message == "included: 150")
}

@Test
fun `check nullable overload with null - no exception`() {
val checker = checker(fixedLimit(100))
assertDoesNotThrow { checker.check(null) }
}
}
Loading
Loading