Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Enable big-meta (context storing) in key create / edit endpoints & optimize #2005

Merged
merged 7 commits into from
Dec 22, 2023
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 @@ -10,6 +10,7 @@ import io.tolgee.security.ProjectHolder
import io.tolgee.security.authentication.AllowApiAccess
import io.tolgee.security.authorization.RequiresProjectPermissions
import io.tolgee.service.bigMeta.BigMetaService
import io.tolgee.util.Logging
import jakarta.validation.Valid
import org.springframework.hateoas.CollectionModel
import org.springframework.web.bind.annotation.GetMapping
Expand All @@ -29,18 +30,18 @@ class BigMetaController(
private val bigMetaService: BigMetaService,
private val projectHolder: ProjectHolder,
private val keyWithBaseTranslationModelAssembler: KeyWithBaseTranslationModelAssembler,
) {
) : Logging {
@PostMapping("/big-meta")
@Operation(summary = "Stores a bigMeta for a project")
@RequiresProjectPermissions([ Scope.TRANSLATIONS_EDIT ])
@RequiresProjectPermissions([Scope.TRANSLATIONS_EDIT])
@AllowApiAccess
fun store(@RequestBody @Valid data: BigMetaDto) {
bigMetaService.store(data, projectHolder.projectEntity)
}

@GetMapping("/keys/{id}/big-meta")
@Operation(summary = "Returns a bigMeta for given key")
@RequiresProjectPermissions([ Scope.TRANSLATIONS_VIEW ])
@RequiresProjectPermissions([Scope.TRANSLATIONS_VIEW])
@AllowApiAccess
fun getBigMeta(
@PathVariable("id") id: Long
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,24 +95,41 @@ class KeyController(
@RequiresProjectPermissions([Scope.KEYS_CREATE])
@AllowApiAccess
fun create(@RequestBody @Valid dto: CreateKeyDto): ResponseEntity<KeyWithDataModel> {
if (dto.screenshotUploadedImageIds != null || !dto.screenshots.isNullOrEmpty()) {
projectHolder.projectEntity.checkScreenshotsUploadPermission()
checkScrenshotUploadPermissions(dto)
checkTranslatePermission(dto)
checkCanStoreBigMeta(dto)
checkStateChangePermission(dto)

val key = keyService.create(projectHolder.projectEntity, dto)
return ResponseEntity(keyWithDataModelAssembler.toModel(key), HttpStatus.CREATED)
}

private fun checkCanStoreBigMeta(dto: CreateKeyDto) {
if (!dto.relatedKeysInOrder.isNullOrEmpty()) {
securityService.checkBigMetaUploadPermission(projectHolder.project.id)
}
}

private fun checkTranslatePermission(dto: CreateKeyDto) {
dto.translations?.filterValues { !it.isNullOrEmpty() }?.keys?.let { languageTags ->
if (languageTags.isNotEmpty()) {
securityService.checkLanguageTranslatePermissionByTag(projectHolder.project.id, languageTags)
}
}
}

private fun checkStateChangePermission(dto: CreateKeyDto) {
dto.states?.filterValues { it != AssignableTranslationState.TRANSLATED }?.keys?.let { languageTags ->
if (languageTags.isNotEmpty()) {
securityService.checkLanguageStateChangePermissionsByTag(projectHolder.project.id, languageTags)
}
}
}

val key = keyService.create(projectHolder.projectEntity, dto)
return ResponseEntity(keyWithDataModelAssembler.toModel(key), HttpStatus.CREATED)
private fun checkScrenshotUploadPermissions(dto: CreateKeyDto) {
if (dto.screenshotUploadedImageIds != null || !dto.screenshots.isNullOrEmpty()) {
projectHolder.projectEntity.checkScreenshotsUploadPermission()
}
}

@PutMapping(value = ["/{id}/complex-update"])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import io.tolgee.model.key.Key
import io.tolgee.model.translation.Translation
import io.tolgee.security.ProjectHolder
import io.tolgee.service.LanguageService
import io.tolgee.service.bigMeta.BigMetaService
import io.tolgee.service.key.KeyService
import io.tolgee.service.key.ScreenshotService
import io.tolgee.service.key.TagService
Expand Down Expand Up @@ -42,6 +43,7 @@ class KeyComplexEditHelper(
private val activityHolder: ActivityHolder = applicationContext.getBean(ActivityHolder::class.java)
private val transactionManager: PlatformTransactionManager =
applicationContext.getBean(PlatformTransactionManager::class.java)
private val bigMetaService = applicationContext.getBean(BigMetaService::class.java)

private lateinit var key: Key
private var modifiedTranslations: Map<Long, String?>? = null
Expand All @@ -54,6 +56,7 @@ class KeyComplexEditHelper(
private var isKeyModified by Delegates.notNull<Boolean>()
private var isScreenshotDeleted by Delegates.notNull<Boolean>()
private var isScreenshotAdded by Delegates.notNull<Boolean>()
private var isBigMetaProvided by Delegates.notNull<Boolean>()

private val languages by lazy {
val translationLanguages = dto.translations?.keys ?: setOf()
Expand Down Expand Up @@ -84,7 +87,16 @@ class KeyComplexEditHelper(
doStateUpdate()
doUpdateTags()
doUpdateScreenshots()
doUpdateKey()
val result = doUpdateKey()
storeBigMeta()
result
}
}

private fun storeBigMeta() {
if (isBigMetaProvided) {
securityService.checkBigMetaUploadPermission(projectHolder.project.id)
bigMetaService.store(dto.relatedKeysInOrder!!, projectHolder.projectEntity)
}
}

Expand Down Expand Up @@ -225,6 +237,7 @@ class KeyComplexEditHelper(
isKeyModified = key.name != dto.name || getSafeNamespace(key.namespace?.name) != getSafeNamespace(dto.namespace)
isScreenshotDeleted = !dto.screenshotIdsToDelete.isNullOrEmpty()
isScreenshotAdded = !dto.screenshotUploadedImageIds.isNullOrEmpty() || !dto.screenshotsToAdd.isNullOrEmpty()
isBigMetaProvided = !dto.relatedKeysInOrder.isNullOrEmpty()
}

private fun areTagsModified(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import com.zaxxer.hikari.HikariDataSource
import io.tolgee.development.testDataBuilder.data.TranslationsTestData
import io.tolgee.fixtures.andIsOk
import io.tolgee.fixtures.retry
import io.tolgee.fixtures.waitForNotThrowing
import io.tolgee.testing.annotations.ProjectJWTAuthTestMethod
import io.tolgee.testing.assert
import org.junit.jupiter.api.BeforeEach
Expand Down Expand Up @@ -73,7 +74,9 @@ class StreamingBodyDatabasePoolHealthTest : ProjectAuthControllerTest("/v2/proje
performProjectAuthGet("export").andIsOk
Thread.sleep(sleepBetweenMs)
}
pool.idleConnections.assert.isGreaterThan(90)
waitForNotThrowing(pollTime = 50, timeout = 5000) {
pool.idleConnections.assert.isGreaterThan(90)
}
} finally {
sleepBetweenMs += 10
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,20 @@ import io.tolgee.ProjectAuthControllerTest
import io.tolgee.development.testDataBuilder.data.BigMetaTestData
import io.tolgee.fixtures.andAssertThatJson
import io.tolgee.fixtures.andIsOk
import io.tolgee.model.key.Key
import io.tolgee.service.bigMeta.BigMetaService
import io.tolgee.testing.annotations.ProjectJWTAuthTestMethod
import io.tolgee.testing.assert
import io.tolgee.util.Logging
import io.tolgee.util.infoMeasureTime
import io.tolgee.util.logger
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import kotlin.time.ExperimentalTime
import kotlin.time.measureTime

class BigMetaControllerTest : ProjectAuthControllerTest("/v2/projects/") {
class BigMetaControllerTest : ProjectAuthControllerTest("/v2/projects/"), Logging {

lateinit var testData: BigMetaTestData

Expand Down Expand Up @@ -60,22 +64,41 @@ class BigMetaControllerTest : ProjectAuthControllerTest("/v2/projects/") {
val keys = testData.addLotOfData()
saveTestDataAndPrepare()

val time = measureTime {
performProjectAuthPost(
"big-meta",
mapOf(
"relatedKeysInOrder" to keys.take(100).map {
mapOf(
"namespace" to it.namespace,
"keyName" to it.name
)
}
)
).andIsOk
logger.infoMeasureTime("it performs well time 1") {
storeLogOfBigMeta(keys, 500, 100)
}

logger.infoMeasureTime("it performs well time 2") {
storeLogOfBigMeta(keys, 500, 100)
}

logger.infoMeasureTime("it performs well time 3") {
storeLogOfBigMeta(keys, 10, 200)
}

logger.infoMeasureTime("it performs well time 4") {
storeLogOfBigMeta(keys, 800, 50)
}

time.inWholeSeconds.assert.isLessThan(10)
bigMetaService.findExistingKeysDistancesByIds(keys.map { it.id }).assert.hasSize(20790)
measureTime {
storeLogOfBigMeta(keys, 800, 50)
}.inWholeSeconds.assert.isLessThan(10)

bigMetaService.findExistingKeysDistancesByIds(keys.map { it.id }).assert.hasSize(104790)
}

private fun storeLogOfBigMeta(keys: List<Key>, drop: Int, take: Int) {
performProjectAuthPost(
"big-meta",
mapOf(
"relatedKeysInOrder" to keys.drop(drop).take(take).map {
mapOf(
"namespace" to it.namespace,
"keyName" to it.name
)
}
)
).andIsOk
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package io.tolgee.api.v2.controllers.v2KeyController

import io.tolgee.ProjectAuthControllerTest
import io.tolgee.development.testDataBuilder.data.KeysTestData
import io.tolgee.dtos.RelatedKeyDto
import io.tolgee.dtos.request.KeyInScreenshotPositionDto
import io.tolgee.dtos.request.key.ComplexEditKeyDto
import io.tolgee.dtos.request.key.KeyScreenshotDto
Expand All @@ -16,12 +17,15 @@ import io.tolgee.model.enums.AssignableTranslationState
import io.tolgee.model.enums.Scope
import io.tolgee.model.enums.TranslationState
import io.tolgee.service.ImageUploadService
import io.tolgee.service.bigMeta.BigMetaService
import io.tolgee.testing.annotations.ProjectApiKeyAuthTestMethod
import io.tolgee.testing.assert
import io.tolgee.testing.assertions.Assertions.assertThat
import io.tolgee.util.generateImage
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.core.io.InputStreamSource
Expand All @@ -33,6 +37,9 @@ class KeyControllerComplexUpdateTest : ProjectAuthControllerTest("/v2/projects/"

lateinit var testData: KeysTestData

@Autowired
lateinit var bigMetaService: BigMetaService

val screenshotFile: InputStreamSource by lazy {
generateImage(2000, 3000)
}
Expand Down Expand Up @@ -278,6 +285,28 @@ class KeyControllerComplexUpdateTest : ProjectAuthControllerTest("/v2/projects/"
).andIsOk
}

@ProjectApiKeyAuthTestMethod(
scopes = [
Scope.TRANSLATIONS_EDIT,
]
)
@Test
fun `stores big meta`() {
this.userAccount = testData.enOnlyUserAccount
performProjectAuthPut(
"keys/${testData.firstKey.id}/complex-update",
ComplexEditKeyDto(
name = testData.firstKey.name,
relatedKeysInOrder = mutableListOf(
RelatedKeyDto(null, "first_key"),
RelatedKeyDto(null, testData.firstKey.name)
)
)
).andIsOk

bigMetaService.getCloseKeyIds(testData.firstKey.id).assert.hasSize(1)
}

@Test
@ProjectApiKeyAuthTestMethod(
scopes = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package io.tolgee.api.v2.controllers.v2KeyController
import io.tolgee.ProjectAuthControllerTest
import io.tolgee.development.testDataBuilder.data.KeysTestData
import io.tolgee.development.testDataBuilder.data.PermissionsTestData
import io.tolgee.dtos.RelatedKeyDto
import io.tolgee.dtos.request.KeyInScreenshotPositionDto
import io.tolgee.dtos.request.key.CreateKeyDto
import io.tolgee.dtos.request.key.KeyScreenshotDto
Expand All @@ -17,6 +18,7 @@ import io.tolgee.model.enums.AssignableTranslationState
import io.tolgee.model.enums.Scope
import io.tolgee.model.enums.TranslationState
import io.tolgee.service.ImageUploadService
import io.tolgee.service.bigMeta.BigMetaService
import io.tolgee.testing.annotations.ProjectApiKeyAuthTestMethod
import io.tolgee.testing.annotations.ProjectJWTAuthTestMethod
import io.tolgee.testing.assert
Expand All @@ -25,6 +27,7 @@ import io.tolgee.util.generateImage
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.core.io.InputStreamSource
Expand All @@ -36,6 +39,9 @@ class KeyControllerCreationTest : ProjectAuthControllerTest("/v2/projects/") {

lateinit var testData: KeysTestData

@Autowired
lateinit var bigMetaService: BigMetaService

val screenshotFile: InputStreamSource by lazy {
generateImage(2000, 3000)
}
Expand Down Expand Up @@ -234,6 +240,28 @@ class KeyControllerCreationTest : ProjectAuthControllerTest("/v2/projects/") {
}
}

@ProjectJWTAuthTestMethod
@Test
fun `creates key with big meta`() {
val keyName = "super_key"

performProjectAuthPost(
"keys",
CreateKeyDto(
name = keyName,
translations = mapOf("en" to "EN", "de" to "DE"),
relatedKeysInOrder = mutableListOf(
RelatedKeyDto(null, "first_key"),
RelatedKeyDto(null, "super_key")
)
)
).andIsCreated.andAssertThatJson {
node("id").isNumber.satisfies { it ->
bigMetaService.getCloseKeyIds(it.toLong()).assert.hasSize(1)
}
}
}

@ProjectJWTAuthTestMethod
@Test
fun `creates key with translation state`() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package io.tolgee.component.demoProject

import io.tolgee.activity.ActivityHolder
import io.tolgee.activity.data.ActivityType
import io.tolgee.dtos.BigMetaDto
import io.tolgee.dtos.RelatedKeyDto
import io.tolgee.dtos.request.KeyInScreenshotPositionDto
import io.tolgee.dtos.request.ScreenshotInfoDto
Expand Down Expand Up @@ -62,16 +61,15 @@ class DemoProjectCreator(
}

private fun addBigMeta() {
val bigMetaDto = BigMetaDto().apply {
keys.forEach { (keyName, _) ->
relatedKeysInOrder.add(
RelatedKeyDto().apply {
this.keyName = keyName
}
)
}
val relatedKeysInOrder = mutableListOf<RelatedKeyDto>()
keys.forEach { (keyName, _) ->
relatedKeysInOrder.add(
RelatedKeyDto().apply {
this.keyName = keyName
}
)
}
bigMetaService.store(bigMetaDto, project)
bigMetaService.store(relatedKeysInOrder, project)
}

private fun setStates() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class BigMetaTestData {
}

fun addLotOfData(): List<Key> {
val keys = (0..1000).map {
val keys = (0..5000).map {
projectBuilder.addKey(null, "key$it").self
}

Expand Down
Loading
Loading