Skip to content

fix: allow editing content delivery config on projects with branching disabled - #3622

Merged
bdshadow merged 1 commit into
mainfrom
dkrizan/fix-cdn-edit-feature-not-enabled
Apr 27, 2026
Merged

fix: allow editing content delivery config on projects with branching disabled#3622
bdshadow merged 1 commit into
mainfrom
dkrizan/fix-cdn-edit-feature-not-enabled

Conversation

@dkrizan

@dkrizan dkrizan commented Apr 24, 2026

Copy link
Copy Markdown
Member

Summary

  • The backend feature guard on the CDN update endpoint fired whenever filterBranch was non-null in the request body. Because this endpoint uses PUT replace semantics (every field in the DTO is unconditionally reassigned), the frontend always echoes the stored branchName back on save. As a result, editing any CDN config that referenced a branch would 400 with FEATURE_NOT_ENABLED_FOR_PROJECT once branching got disabled on the project (either the org's BRANCHING feature flag or project.useBranching).
  • Refine the guard in ContentDeliveryConfigService.update() so it only fires when the config is — or would end up — pointing at a non-default branch. Echoing the default branch back on an already-default config is now a no-op.

Guard semantics

Current config branch dto.filterBranch Guard fires? Rationale
default (main) null no no feature usage
default (main) "main" no echo of default, no change
default (main) "feature" yes user is activating the feature
non-default (feature) "feature" yes config is still using the feature
non-default (feature) "main" yes was using the feature — guard fires
non-default (feature) null yes currently on non-default — guard fires

Test plan

  • Existing backend tests in ContentDeliveryConfigBranchingTest still pass, including update CDN config with branch fails when branching not enabled and create CDN config with branch fails when branching not enabled.
  • New backend coverage:
    • update default-branch CDN succeeds when branching not enabled and default is echoed
    • update default-branch CDN succeeds when branching not enabled and filterBranch is null
    • update non-default-branch CDN fails when branching not enabled even if branch is unchanged
  • New e2e regression edits branched content delivery when branching gets disabled in contentDeliveryBranching.cy.ts — reproduces the original 400 via the UI.

Summary by CodeRabbit

Release Notes

  • Tests

    • Added end-to-end test for editing content delivery records when branching is disabled
    • Added controller tests for content delivery configuration updates with different branching feature states
  • Bug Fixes

    • Improved branching validation for content delivery configurations to correctly handle default branch updates

@coderabbitai

coderabbitai Bot commented Apr 24, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

This PR refines the BRANCHING feature validation in content delivery configuration updates. Rather than checking feature enablement upfront based on the filterBranch parameter, the service now conditionally validates the feature only when transitioning to or retaining a non-default branch. The change includes new E2E and controller tests to ensure correct behavior when branching is disabled.

Changes

Cohort / File(s) Summary
E2E Test Coverage
e2e/cypress/e2e/branching/contentDeliveryBranching.cy.ts
New test validates that the branch selector is hidden in the edit dialog when BRANCHING feature is disabled, and confirms rename/save operations succeed after feature toggle and UI reload.
Backend Service Logic
backend/data/src/main/kotlin/io/tolgee/service/contentDelivery/ContentDeliveryConfigService.kt
Modified update method to defer BRANCHING feature validation until after fetching the target branch, checking enablement only when transitioning to or retaining a non-default branch rather than upfront.
Backend Controller Tests
ee/backend/tests/src/test/kotlin/io/tolgee/ee/api/v2/controllers/ContentDeliveryConfigBranchingTest.kt
Added three tests covering update scenarios when BRANCHING is disabled: default-branch updates with explicit/omitted filterBranch succeed, while non-default-branch updates fail with FEATURE_NOT_ENABLED error.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant Service as ContentDeliveryConfigService
    participant Guard as ProjectFeatureGuard
    participant BranchRepo as Branch Repository

    User->>Service: update(config, dto)
    Service->>BranchRepo: fetch active-or-default branch
    BranchRepo-->>Service: newBranch
    
    rect rgba(100, 150, 200, 0.5)
    Note over Service: Conditional Feature Check
    alt newBranch is non-default OR config.branch is non-default
        Service->>Guard: checkEnabled(BRANCHING)
        Guard-->>Service: validation result
    else both branches are default
        Note over Service: Skip BRANCHING check
    end
    end
    
    Service->>Service: config.branch = newBranch
    Service->>Service: proceed with standard update
    Service-->>User: success
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • bdshadow
  • JanCizmar

Poem

🐰 Branches bloom when features hide,
Default paths can now reside,
Guards check only when they should,
Tests confirm it's working good!
No branching, yet still we glide! 🌿

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title accurately describes the main fix: allowing editing of content delivery configs when branching is disabled, which is the core objective of this changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dkrizan/fix-cdn-edit-feature-not-enabled

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
e2e/cypress/e2e/branching/contentDeliveryBranching.cy.ts (1)

111-128: Nit: keep the gcy call style consistent within the suite.

Lines 122–123 use cy.gcy(...) while the rest of this file (including lines 118 and 120 of the same test) uses the bare gcy(...) helper imported from ../../common/shared. Functionally equivalent, but worth aligning for consistency.

Proposed tweak
-    cy.gcy('content-delivery-form-name').find('input').clear().type('Renamed');
-    cy.gcy('content-delivery-form-save').click();
+    gcy('content-delivery-form-name').find('input').clear().type('Renamed');
+    gcy('content-delivery-form-save').click();

Otherwise the regression coverage is solid: seeding with BRANCHING on, flipping it off, reloading, and asserting a successful update accurately reproduces the bug this PR fixes.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/cypress/e2e/branching/contentDeliveryBranching.cy.ts` around lines 111 -
128, The test uses the helper gcy imported from ../../common/shared but two
assertions use cy.gcy(...) which is inconsistent; replace
cy.gcy('content-delivery-form-name') and cy.gcy('content-delivery-form-save')
with the bare gcy(...) calls so the suite consistently uses the gcy helper (same
style as openEditDialog and the other gcy usages) and run the test to ensure
behavior is unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@e2e/cypress/e2e/branching/contentDeliveryBranching.cy.ts`:
- Around line 111-128: The test uses the helper gcy imported from
../../common/shared but two assertions use cy.gcy(...) which is inconsistent;
replace cy.gcy('content-delivery-form-name') and
cy.gcy('content-delivery-form-save') with the bare gcy(...) calls so the suite
consistently uses the gcy helper (same style as openEditDialog and the other gcy
usages) and run the test to ensure behavior is unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 7be98009-87d3-46df-9de6-ee90cf8f1551

📥 Commits

Reviewing files that changed from the base of the PR and between 599d5fa and dbc71d7.

📒 Files selected for processing (2)
  • e2e/cypress/e2e/branching/contentDeliveryBranching.cy.ts
  • webapp/src/views/projects/developer/contentDelivery/useCdActions.tsx

@dkrizan
dkrizan marked this pull request as draft April 24, 2026 20:23
… disabled

The backend feature guard on the CDN update endpoint used to fire
whenever filterBranch was non-null in the request body. Because this
endpoint uses replace semantics, the frontend always echoes the stored
branchName back on save, so editing any CDN config that referenced a
branch would 400 with FEATURE_NOT_ENABLED_FOR_PROJECT once branching
got disabled on the project (feature flag or project.useBranching).

Refine the guard so it fires only when the config is — or would end up
— pointing at a non-default branch. Echoing the default branch back on
an already-default config is now a no-op.
@dkrizan
dkrizan force-pushed the dkrizan/fix-cdn-edit-feature-not-enabled branch from dbc71d7 to f29748f Compare April 24, 2026 21:16
@dkrizan dkrizan changed the title fix: do not send filterBranch when branching is disabled fix: allow editing content delivery config on projects with branching disabled Apr 24, 2026
@dkrizan
dkrizan marked this pull request as ready for review April 27, 2026 08:33
@dkrizan
dkrizan requested a review from bdshadow April 27, 2026 08:33

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
e2e/cypress/e2e/branching/contentDeliveryBranching.cy.ts (1)

118-123: Style nit: mix of gcy() and cy.gcy() within the same test.

Lines 118 and 120 use gcy(...) while lines 122–123 use cy.gcy(...). The rest of this file consistently uses the imported gcy helper; aligning here keeps the test readable.

Proposed tweak
-    cy.gcy('content-delivery-form-name').find('input').clear().type('Renamed');
-    cy.gcy('content-delivery-form-save').click();
+    gcy('content-delivery-form-name').find('input').clear().type('Renamed');
+    gcy('content-delivery-form-save').click();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/cypress/e2e/branching/contentDeliveryBranching.cy.ts` around lines 118 -
123, The test mixes two helper usages—call sites using gcy(...) and cy.gcy(...);
update the occurrences of cy.gcy (used for 'content-delivery-form-name' and
'content-delivery-form-save') to use the imported gcy helper instead so the test
consistently uses gcy across openEditDialog('Main CDN'), the branch selector
assertion, and the input/save steps; this keeps style consistent and matches the
rest of the file.
backend/data/src/main/kotlin/io/tolgee/service/contentDelivery/ContentDeliveryConfigService.kt (1)

147-151: Guard logic is correct; the asymmetry between create() and update() is real and worth documenting.

The conditional correctly fires when either the new branch or existing config branch is on a non-default branch, matching the test matrix. When both are default (e.g., echoing the default branch on a default-branch config), the guard does not fire—a no-op as intended.

However, create() at line 49 uses checkIfUsed(Feature.BRANCHING, dto.filterBranch), which fires whenever filterBranch is any non-empty string—including when it equals the default branch name (e.g., "main"). A client that always sends filterBranch for default-branch creations would fail unnecessarily, unlike the update path which is now lenient. Consider whether the UI omits filterBranch on create for default-branch scenarios, or align create() to match update()'s logic by checking if the resolved branch is actually non-default.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@backend/data/src/main/kotlin/io/tolgee/service/contentDelivery/ContentDeliveryConfigService.kt`
around lines 147 - 151, The create() path should mirror update() by resolving
the branch before enforcing the branching guard: instead of calling
checkIfUsed(Feature.BRANCHING, dto.filterBranch) directly, call
branchService.getActiveOrDefault(projectId, dto.filterBranch) to get the
resolved branch and only invoke
projectFeatureGuard.checkEnabled(Feature.BRANCHING) (or checkIfUsed) when that
resolved branch is non-default (resolvedBranch?.isDefault == false); update the
create() implementation to use the same isDefault-based check as update() to
avoid rejecting creations where dto.filterBranch equals the project's default
branch name.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In
`@backend/data/src/main/kotlin/io/tolgee/service/contentDelivery/ContentDeliveryConfigService.kt`:
- Around line 147-151: The create() path should mirror update() by resolving the
branch before enforcing the branching guard: instead of calling
checkIfUsed(Feature.BRANCHING, dto.filterBranch) directly, call
branchService.getActiveOrDefault(projectId, dto.filterBranch) to get the
resolved branch and only invoke
projectFeatureGuard.checkEnabled(Feature.BRANCHING) (or checkIfUsed) when that
resolved branch is non-default (resolvedBranch?.isDefault == false); update the
create() implementation to use the same isDefault-based check as update() to
avoid rejecting creations where dto.filterBranch equals the project's default
branch name.

In `@e2e/cypress/e2e/branching/contentDeliveryBranching.cy.ts`:
- Around line 118-123: The test mixes two helper usages—call sites using
gcy(...) and cy.gcy(...); update the occurrences of cy.gcy (used for
'content-delivery-form-name' and 'content-delivery-form-save') to use the
imported gcy helper instead so the test consistently uses gcy across
openEditDialog('Main CDN'), the branch selector assertion, and the input/save
steps; this keeps style consistent and matches the rest of the file.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 503250bb-bde9-4290-b2b6-06d357235ac9

📥 Commits

Reviewing files that changed from the base of the PR and between dbc71d7 and f29748f.

📒 Files selected for processing (3)
  • backend/data/src/main/kotlin/io/tolgee/service/contentDelivery/ContentDeliveryConfigService.kt
  • e2e/cypress/e2e/branching/contentDeliveryBranching.cy.ts
  • ee/backend/tests/src/test/kotlin/io/tolgee/ee/api/v2/controllers/ContentDeliveryConfigBranchingTest.kt

@bdshadow
bdshadow merged commit 1da8d67 into main Apr 27, 2026
78 of 80 checks passed
@bdshadow
bdshadow deleted the dkrizan/fix-cdn-edit-feature-not-enabled branch April 27, 2026 10:26
TolgeeMachine added a commit that referenced this pull request Apr 27, 2026
## [3.181.3](v3.181.2...v3.181.3) (2026-04-27)

### Bug Fixes

* allow editing content delivery config on projects with branching disabled ([#3622](#3622)) ([1da8d67](1da8d67))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants