feat: main branch for the existing keys, tasks, imports, language stats#3430
feat: main branch for the existing keys, tasks, imports, language stats#3430
Conversation
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughWalkthroughProject-wide enforcement of non-null branch associations: default/main branch creation and migration added; branch fields made non-null in models and services; builders and import flows updated to use resolved branches; introduced DefaultBranchNotFoundException; DB migration backfills and enforces NOT NULL on branch foreign keys. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant Client
participant SingleStepImportService
participant BranchService
participant Database
Client->>SingleStepImportService: request import (projectId, branchName?)
SingleStepImportService->>BranchService: getActiveOrDefault(projectId, branchName)
BranchService->>Database: query branch by project+name or default
Database-->>BranchService: Branch or none
alt Branch found
BranchService-->>SingleStepImportService: Branch (non-null)
SingleStepImportService->>Database: create Import with branch_id (non-null)
Database-->>SingleStepImportService: Import persisted
SingleStepImportService-->>Client: success
else No branch
BranchService-->>SingleStepImportService: throws DefaultBranchNotFoundException
SingleStepImportService-->>Client: error (exception)
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
backend/data/src/main/kotlin/io/tolgee/component/demoProject/DemoProjectCreator.kt
Show resolved
Hide resolved
5d67e55 to
4c8a46c
Compare
58d0858 to
84062af
Compare
a6ad058 to
e9037a6
Compare
84062af to
d4a618e
Compare
594f365 to
29f7614
Compare
- Add DefaultBranchNotFoundException for consistent error handling - Make Key.branch, Task.branch, and Import.branch non-nullable (JPA optional=false) - Update BranchService methods to return non-null Branch - Add automatic default branch creation in test data builders - Simplify SecurityService and BranchVersionedEntity branch access Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
d4a618e to
e89afe3
Compare
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
backend/data/src/main/kotlin/io/tolgee/development/DbPopulatorReal.kt (1)
296-321:⚠️ Potential issue | 🔴 CriticalSet
key.branchbefore persisting the KeyThe
createTranslationmethod creates aKey(line 303) and persists it (line 314) without assigningkey.branch. SinceKey.branchis a non-nullablelateinitproperty with a database NOT NULL constraint, this will fail at persist time.Assign the default branch from the project:
Proposed fix
val key = Key() key.name = "sampleApp." + english .replace(" ", "_") .lowercase(Locale.getDefault()) .replace("\\.+$".toRegex(), "") key.project = project + key.branch = project.getDefaultBranch()backend/data/src/main/kotlin/io/tolgee/model/key/Key.kt (1)
106-113:⚠️ Potential issue | 🟠 MajorSecondary constructor is missing required
branchfield initialization.This constructor sets
projectandtranslationsbut notbranch. Although all current call sites inKeyService.ktandKeysImporter.ktimmediately assignbranchafter construction, the constructor signature is unsafe. Addbranchas a parameter to this constructor or refactor to prevent accidental misuse.
🤖 Fix all issues with AI agents
In `@backend/data/src/main/kotlin/io/tolgee/model/key/Key.kt`:
- Around line 80-84: The comment above the branch property is stale and
contradicts the current non-null constraint; update or remove the misleading
comment so it reflects that in class Key the property branch (annotated
`@ManyToOne`(fetch = FetchType.LAZY, optional = false), `@JoinColumn`(name =
"branch_id"), declared as lateinit var branch: Branch and annotated
`@ActivityLoggedProp`) is non-nullable and legacy nulls are no longer
represented—replace the old note about "Nullable for backward compatibility"
with a brief accurate comment about the required non-null branch or simply
remove the comment.
In
`@backend/data/src/main/kotlin/io/tolgee/service/branching/AbstractBranchService.kt`:
- Around line 14-17: getActiveBranch currently throws
DefaultBranchNotFoundException which is semantically incorrect for arbitrary
branch lookups; change getActiveBranch to throw a generic
BranchNotFoundException (or NotFoundException) instead of
DefaultBranchNotFoundException, while keeping DefaultBranchNotFoundException
reserved for getDefaultBranch; update the throw site that uses
branchRepository.findActiveByProjectIdAndName(projectId, branchName) to
instantiate/throw the new BranchNotFoundException (or reuse an existing
NotFoundException) so the exception name matches the lookup intent.
In `@backend/data/src/main/resources/db/changelog/schema.xml`:
- Around line 5060-5128: The migration inserts a 'main' default branch
unconditionally and then backfills branch_id, which can create a conflicting
second is_default=true or leave NULLs; fix by first renaming any existing
default branch to 'main' where no 'main' exists, then run the INSERT and
backfills. Concretely, add an earlier step (before changeSet id
"1769500000000-1") that runs: UPDATE branch b SET name = 'main' WHERE
b.is_default = true AND NOT EXISTS (SELECT 1 FROM branch b2 WHERE b2.project_id
= b.project_id AND b2.name = 'main'); keep the existing INSERT in
"1769500000000-1" but guard it with WHERE NOT EXISTS (SELECT 1 FROM branch b
WHERE b.project_id = p.id AND b.name = 'main') (already present), and ensure the
subsequent updates in changeSets "1769500000000-2".."1769500000000-5" continue
to target branch_id via the SELECT FROM branch b to avoid NULLs.
🧹 Nitpick comments (5)
backend/data/src/main/kotlin/io/tolgee/service/security/SecurityService.kt (1)
547-554: Consider tighteningBranch?toBranchnow that both callers supply non-null values.With
key.branchnow non-null andbranchService.getActiveOrDefaultalso returning a non-nullBranch(per this PR), thebranchparameter here is never actually null. Keeping it nullable meansbranch?.isProtectedsilently skips the permission check if a future caller passesnull—a potential security gap. Changing the parameter to non-nullBranchwould make the compiler enforce the invariant.Suggested diff
- private fun checkProtectedBranchModify( - branch: Branch?, - projectId: Long, - ) { - if (branch?.isProtected == true) { + private fun checkProtectedBranchModify( + branch: Branch, + projectId: Long, + ) { + if (branch.isProtected) { checkProjectPermission(projectId, Scope.BRANCH_PROTECTED_MODIFY) } }backend/data/src/main/kotlin/io/tolgee/development/testDataBuilder/builders/BranchBuilder.kt (1)
16-31: Factory method creates and immediately discards a branch — consider a cleaner approach.
forExistingBranchinstantiates aBranchBuilder(which adds an auto-created branch toproject.branchesin the constructor), then immediately removes it and replacesself. This works but is somewhat wasteful. A private constructor or an init flag could avoid the throwaway branch creation. That said, for a test data builder this is perfectly acceptable.ee/backend/app/src/main/kotlin/io/tolgee/ee/service/TaskService.kt (2)
495-511:getOnlyProjectKeysparameterbranchis still nullable, but callers now always pass non-null.Since
getBranchForTasknow returnsBranch(non-null), thebranch: Branch?parameter on line 501 and the safe-callbranch?.nameon line 510 are no longer necessary. Consider tightening the signature tobranch: Branchfor consistency with the new non-null contract.Proposed fix
private fun getOnlyProjectKeys( projectId: Long, languageId: Long, type: TaskType, keys: Collection<Long>, filters: TranslationScopeFilters, - branch: Branch?, + branch: Branch, ): MutableSet<Long> { return taskRepository .getKeysWithoutConflicts( projectId, languageId, type.toString(), keys, filters, - branch?.name, + branch.name, ).toMutableSet() }
423-440: Unnecessary safe-call operators on non-nullbranchincalculateScope.
getBranchForTasknow returnsBranch(non-null), sobranch?.nameon lines 430 and 439 can be simplified tobranch.name.Proposed fix
val keysIncludingConflicts = taskRepository.getKeysIncludingConflicts( projectEntity.id, language.id, dto.keys, filters, - branch?.name, + branch.name, ) val relevantKeys = taskRepository.getKeysWithoutConflicts( projectEntity.id, language.id, dto.type.toString(), dto.keys, filters, - branch?.name, + branch.name, )backend/data/src/main/kotlin/io/tolgee/model/dataImport/Import.kt (1)
38-40: Missing@field:NotNullannotation — inconsistent with sibling fields.Both
project(line 26) andauthor(line 31) use@field:NotNullalongside@ManyToOne(optional = false). The newbranchfield omits it. Whileoptional = falseandnullable = falseenforce the constraint at the JPA/DDL level, adding@field:NotNullkeeps bean-validation behaviour consistent across the entity.Suggested fix
+ `@field`:NotNull `@ManyToOne`(targetEntity = Branch::class, optional = false) `@JoinColumn`(name = "branch_id", nullable = false) lateinit var branch: Branch
backend/data/src/main/kotlin/io/tolgee/service/branching/AbstractBranchService.kt
Show resolved
Hide resolved
The test creates 11 keys via BranchMergeTestData, but other tests (KeyUsageReportingTest, etc.) may leave a cached subscription with keysLimit=10. This clears any existing subscription and cache before saving test data to prevent PlanLimitExceededKeysException. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
a44a1da to
e6201cb
Compare
e6201cb to
98b8b29
Compare
Key.branch is now non-null, so createTranslation() must assign the project's default branch to each key it creates. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
TaskRedirect: only pass task.branchName to withBranchLink when branching is active, preventing /tree/main from being added to translation URLs on non-branching projects. TaskTranslationsLink: skip the branch mismatch check when branching is not enabled, so inactive task links remain clickable. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… non-null branch The branch filter in findImportTranslationsView was filtering out all import translations when no existing key matched (e.g. fresh project), because the LEFT JOIN on Key produced NULL branch_id which never equals the import's non-null branch_id. Adding `ek is null` allows translations without a matching existing key to pass the filter. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Filter ActivityTestUtil.getLastRevision() by projectId to avoid picking up async BranchMetaUpdater revisions - Update AllKeysControllerTest branch assertions from null to "main" - Update BigMetaControllerTest expected distance counts - Increase N+1 thresholds in LanguageServiceTest and TaskControllerActivityTest to account for global async statistics - Fix ProjectBranchingMigrationService to use existing default branch instead of early-returning when one already exists Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Avoid duplicate default branch constraint violation when generateBranchedData is called with isBranchDefault=true after the project's defaultBranch has already been lazily initialized. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
BranchMetaUpdater.snapshot() runs @async and increments branch.revision, which triggered activity recording (INSERT into activity_revision) from a separate thread. This deadlocked with the main thread's concurrent activity recording for the original entity change. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Summary by CodeRabbit
New Features
Bug Fixes
Chores