Skip to content

EDM-2646: Revert to manually generating the PATCHes for applications#387

Merged
celdrake merged 1 commit into
flightctl:mainfrom
celdrake:EDM-2646-fix-application-patching
Nov 26, 2025
Merged

EDM-2646: Revert to manually generating the PATCHes for applications#387
celdrake merged 1 commit into
flightctl:mainfrom
celdrake:EDM-2646-fix-application-patching

Conversation

@celdrake

@celdrake celdrake commented Nov 26, 2025

Copy link
Copy Markdown
Collaborator

Attempting to PATCH the applications using "json patch" library didn't work well, it was failing to handle cases such as adding the first application or having empty resource limits (it would try to patch to an empty string which the API rejects)

  • Reverted to having custom functions that check if the application has changed.
  • When we actually PATCH an application, we now will only patch the ones that changed. Previously we were patching all the applications.

Summary by CodeRabbit

  • Refactor
    • More granular detection of changes to device applications, producing targeted per-item updates instead of wholesale replacements.
    • Correct handling of added, removed, and reordered applications to minimize unnecessary updates.
    • More detailed equality checks for volumes, ports, resource limits and environment variables for precise diffs.
    • Expanded support for compose/quadlet and inline application formats during change detection.
    • Replaced the previous generic patching dependency with a bespoke diff mechanism; public API remains unchanged.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai

coderabbitai Bot commented Nov 26, 2025

Copy link
Copy Markdown

Walkthrough

The patch generation in deviceSpecUtils.ts was refactored to remove fast-json-patch and implement a custom, granular diff that detects fine-grained changes across applications (inline/image/single-container), volumes, ports, resources, and environment variables, emitting precise per-index add/remove/replace patches as appropriate.

Changes

Cohort / File(s) Summary
Patch generation refactor
libs/ui-components/src/components/Device/EditDeviceWizard/deviceSpecUtils.ts
Removed fast-json-patch (compare) usage and implemented a bespoke granular diff. Added helpers: hasInlineApplicationChanged, areVolumesEqual, arePortsEqual, areResourceLimitsEqual, areEnvVariablesEqual, hasSingleContainerAppChanged, and hasApplicationChanged. getApplicationPatches now emits targeted add/remove/replace patches (including per-index replaces) and handles Compose/Quadlet image and inline app forms. Kept toAPIApplication/getApiConfig logic but routed through new change detectors.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant Caller
    participant getApplicationPatches as getApplicationPatches()
    participant Detectors as Change Detectors
    participant Patches as Patch Array

    Caller->>getApplicationPatches: call(currentApps, updatedApps)
    alt currentApps empty
        getApplicationPatches->>Patches: create add-all patches
    else updatedApps empty
        getApplicationPatches->>Patches: create remove-all patches
    else lengths differ
        getApplicationPatches->>Patches: replace entire applications array
    else same length
        loop for each index i
            getApplicationPatches->>Detectors: hasApplicationChanged(current[i], updated[i])
            Detectors->>Detectors: check inline/image/single-container, volumes, ports, resources, envs
            Detectors-->>getApplicationPatches: changed? (true/false)
            alt changed
                getApplicationPatches->>Patches: add replace patch at index i
            end
        end
    end
    getApplicationPatches-->>Caller: return Patches
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Review comparison helpers for correctness and edge cases (volumes, ports, resource limits, env vars).
  • Verify per-index replace semantics vs full-array replace when lengths differ.
  • Confirm Compose/Quadlet and inline/image/single-container scenarios are fully covered by tests.

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: reverting from the json-patch library to manual PATCH generation for applications, which directly aligns with the file modifications and PR objectives.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
libs/ui-components/src/components/Device/EditDeviceWizard/deviceSpecUtils.ts (1)

392-404: Redundant environment variable check for single container apps.

hasApplicationChanged already checks envVars at lines 422-433 before calling hasSingleContainerAppChanged, which performs the same check again at lines 392-404. Consider removing the duplicate check from hasSingleContainerAppChanged to avoid redundant computation.

Also applies to: 436-438

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7c0cf98 and 479bb2f.

📒 Files selected for processing (1)
  • libs/ui-components/src/components/Device/EditDeviceWizard/deviceSpecUtils.ts (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: integration-tests
  • GitHub Check: Build
  • GitHub Check: Build ocp plugin
  • GitHub Check: Lint
🔇 Additional comments (3)
libs/ui-components/src/components/Device/EditDeviceWizard/deviceSpecUtils.ts (3)

297-313: LGTM!

The inline application change detection logic correctly compares file counts, encoding, paths, and content using index-based comparison.


488-500: LGTM - Index-based granular patching.

The approach correctly emits targeted replace patches only for applications that have actually changed. The index-based comparison works correctly because array length changes already trigger a full array replacement. For same-length arrays with reordering, individual replacements achieve the correct end state.


468-474: LGTM - Correctly handles adding first application(s).

This addresses the PR objective of fixing the issue where adding the first application failed with the json-patch library.

Comment thread libs/ui-components/src/components/Device/EditDeviceWizard/deviceSpecUtils.ts Outdated
Comment thread libs/ui-components/src/components/Device/EditDeviceWizard/deviceSpecUtils.ts Outdated
@celdrake celdrake force-pushed the EDM-2646-fix-application-patching branch from 479bb2f to 1ab39bd Compare November 26, 2025 10:28

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 479bb2f and 1ab39bd.

📒 Files selected for processing (1)
  • libs/ui-components/src/components/Device/EditDeviceWizard/deviceSpecUtils.ts (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
libs/ui-components/src/components/Device/EditDeviceWizard/deviceSpecUtils.ts (2)
libs/types/index.ts (8)
  • InlineApplicationProviderSpec (126-126)
  • EncodingType (81-81)
  • ApplicationVolume (16-16)
  • ImageMountVolumeProviderSpec (122-122)
  • ImagePullPolicy (123-123)
  • ApplicationProviderSpec (11-11)
  • ImageApplicationProviderSpec (121-121)
  • PatchRequest (147-147)
libs/ui-components/src/types/deviceSpec.ts (8)
  • QuadletInlineAppForm (76-81)
  • ComposeInlineAppForm (89-94)
  • ApplicationVolumeForm (143-149)
  • PortMapping (56-59)
  • AppForm (113-118)
  • isSingleContainerAppForm (134-135)
  • QuadletImageAppForm (70-74)
  • ComposeImageAppForm (83-87)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: integration-tests
  • GitHub Check: Build ocp plugin
  • GitHub Check: Build
  • GitHub Check: Lint
🔇 Additional comments (6)
libs/ui-components/src/components/Device/EditDeviceWizard/deviceSpecUtils.ts (6)

297-313: LGTM!

The inline file comparison logic correctly handles undefined base64 by defaulting to false.


315-348: LGTM!

Volume comparison covers all relevant properties with sensible defaults for optional fields.


350-360: LGTM!

Port comparison is straightforward with helpful documentation about ordering behavior.


362-372: LGTM!

Resource limits comparison correctly handles undefined values by defaulting to empty strings.


374-387: Good fix for the falsy value issue.

Using variable.name in envVars correctly handles environment variables with falsy values like empty strings or 0. This addresses the concerns from the past review.


454-499: LGTM!

The refactored approach correctly handles all edge cases (adding first apps, removing all, length changes) and generates precise per-index patches when the array length is unchanged. This is a solid improvement over the previous library-based approach.

@celdrake celdrake force-pushed the EDM-2646-fix-application-patching branch from 1ab39bd to 7f0d550 Compare November 26, 2025 13:13
@celdrake celdrake merged commit 90ae3b4 into flightctl:main Nov 26, 2025
5 of 6 checks passed
@celdrake celdrake deleted the EDM-2646-fix-application-patching branch November 26, 2025 13:18

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1ab39bd and 7f0d550.

📒 Files selected for processing (1)
  • libs/ui-components/src/components/Device/EditDeviceWizard/deviceSpecUtils.ts (2 hunks)
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: celdrake
Repo: flightctl/flightctl-ui PR: 371
File: libs/types/models/AppType.ts:10-10
Timestamp: 2025-10-29T16:47:29.614Z
Learning: PR #371 (flightctl/flightctl-ui) adds the AppTypeQuadlet enum member and related types as a preparatory change. Full implementation of quadlet application support in the UI will be added later, after backend support is available.
📚 Learning: 2025-11-26T10:44:58.434Z
Learnt from: celdrake
Repo: flightctl/flightctl-ui PR: 387
File: libs/ui-components/src/components/Device/EditDeviceWizard/deviceSpecUtils.ts:447-451
Timestamp: 2025-11-26T10:44:58.434Z
Learning: In the flightctl-ui codebase, the UI only allows users to modify volumes for Single Container applications. Volumes cannot be modified through the UI for Quadlet/Compose image applications or inline applications, even though the backend API supports volumes for these application types.

Applied to files:

  • libs/ui-components/src/components/Device/EditDeviceWizard/deviceSpecUtils.ts
📚 Learning: 2025-06-04T06:41:15.969Z
Learnt from: celdrake
Repo: flightctl/flightctl-ui PR: 308
File: libs/ui-components/src/components/Device/EditDeviceWizard/deviceSpecUtils.ts:204-204
Timestamp: 2025-06-04T06:41:15.969Z
Learning: In the flightctl-ui codebase, volume functionality is not yet implemented in the backend. TODO comments related to volume preservation (like in toAPIApplication function) are intentional placeholders for when backend volume support is added.

Applied to files:

  • libs/ui-components/src/components/Device/EditDeviceWizard/deviceSpecUtils.ts
📚 Learning: 2025-10-29T16:47:29.614Z
Learnt from: celdrake
Repo: flightctl/flightctl-ui PR: 371
File: libs/types/models/AppType.ts:10-10
Timestamp: 2025-10-29T16:47:29.614Z
Learning: PR #371 (flightctl/flightctl-ui) adds the AppTypeQuadlet enum member and related types as a preparatory change. Full implementation of quadlet application support in the UI will be added later, after backend support is available.

Applied to files:

  • libs/ui-components/src/components/Device/EditDeviceWizard/deviceSpecUtils.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: integration-tests
  • GitHub Check: Build ocp plugin
  • GitHub Check: Build
  • GitHub Check: Lint
🔇 Additional comments (6)
libs/ui-components/src/components/Device/EditDeviceWizard/deviceSpecUtils.ts (6)

21-49: New application form imports are consistent and used.

ComposeImageAppForm, PortMapping, and QuadletImageAppForm are all used in the new helpers (arePortsEqual, hasApplicationChanged), and the import surface stays aligned with the existing AppForm/type‑guard model. No issues here.


315-348: Volume comparison logic matches form conversion semantics.

areVolumesEqual compares name, image reference, pull policy (defaulting to PullIfNotPresent), and mount path, using the same defaults as convertVolumesToForm. Order-sensitive comparison is reasonable here since the API models volumes as an ordered array. This helper looks sound.


350-372: Ports and resource limits equality checks behave as expected.

  • arePortsEqual normalizes back to the "host:container" string and is intentionally order-sensitive.
  • areResourceLimitsEqual normalizes missing values to '', so { cpu: undefined, memory: undefined } and undefined are treated the same, while real values (including '0') are still distinguished.

Both helpers are consistent with getApplicationValues/toAPIApplication and should avoid spurious changes.


374-387: Environment variable equality correctly handles falsy values and order.

areEnvVariablesEqual fixes the earlier falsy‑value bug by:

  • Comparing counts (Object.keys(envVars).length) against the form array length.
  • Requiring that each variable.name exists in the map and matches by strict equality.

This ignores ordering differences (good) and no longer treats empty strings or other falsy values as implicit changes.


414-451: Overall application change detection is coherent; inline volumes intentionally ignored.

The structure of hasApplicationChanged cleanly separates cases:

  • Early exit on specType/appType/name changes and env var changes.
  • Delegation to hasSingleContainerAppChanged for single-container apps.
  • Direct image + volume comparison for Quadlet/Compose image apps.
  • Fallback to inline file comparison for inline apps.

Per the recorded learnings, the UI only exposes volume editing for single-container applications, not for Quadlet/Compose or inline apps, so omitting a volume comparison in the inline branch is intentional and avoids chasing backend capabilities the UI doesn’t surface. Based on learnings, this split looks appropriate.


454-499: Manual application patch generation meets PR goals and avoids json-patch edge cases.

The new getApplicationPatches logic:

  • Handles the “no apps → some apps” and “some apps → no apps” transitions with add/remove at ${basePath}/applications.
  • Replaces the entire array when lengths differ.
  • When lengths are equal, only emits replace operations for indices where hasApplicationChanged returns true.

This should fix the previous issues with the third-party json-patch library (e.g., empty string resource limits and first-app additions) while also reducing unnecessary PATCHes by only touching genuinely changed applications. Behavior for reordering (same length, different ordering) is to emit per-index replaces, which is acceptable given the array-based API model.

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