mirror of
https://github.com/cozystack/cozystack.git
synced 2026-11-12 23:54:01 +09:00
fix(dashboard): materialise an immutable leaf whose ancestor is gone
overlayImmutable stopped walking as soon as the submitted body had nothing at a path segment, so a YAML edit that dropped a whole parent object also dropped the immutable leaf under it -- reachable through foundationdb storage.storageClass and kafka kafka.storageClass. Create the missing ancestor when the persisted spec has one, and turn the pinned FIXME test into a test of the fixed behaviour. Fixes #3102 Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
This commit is contained in:
1 parent
4a5e18b6e1
commit
2da6594466
2 files changed
+25
-10
No files matched your search
+20
-9
@@ -377,20 +377,31 @@ describe("overlayImmutable", () => {
|
||||
})
|
||||
})
|
||||
|
||||
it("does NOT materialise an immutable leaf when its ancestor is missing in target (pinned current behaviour)", () => {
|
||||
// Tracked in cozystack/cozystack-ui#9.
|
||||
// Defence-in-depth gap: if the YAML editor strips the parent object
|
||||
// entirely, overlay cannot reach the leaf to copy the original value.
|
||||
// Pin behaviour so a future contributor fixing the gap notices the
|
||||
// test and revises it.
|
||||
it("materialises an immutable leaf when its ancestor is missing in target", () => {
|
||||
// A YAML edit that strips the parent object must not strip the immutable
|
||||
// leaf with it. foundationdb's storage.storageClass is a shipped path of
|
||||
// this shape.
|
||||
const submitted = { spec: {} } as Record<string, unknown>
|
||||
const original = {
|
||||
spec: { backup: { storageClass: "slow" } },
|
||||
spec: { storage: { storageClass: "replicated", size: "10Gi" } },
|
||||
}
|
||||
const result = overlayImmutable(submitted, original, [
|
||||
["spec", "backup", "storageClass"],
|
||||
["spec", "storage", "storageClass"],
|
||||
])
|
||||
expect(result).toEqual({ spec: {} })
|
||||
expect(result).toEqual({ spec: { storage: { storageClass: "replicated" } } })
|
||||
})
|
||||
|
||||
it("materialises an immutable leaf when its ancestor is null in target", () => {
|
||||
// `storage:` with nothing under it parses to null, not to a missing key,
|
||||
// so the walker has to treat it the same as absent.
|
||||
const submitted = { spec: { storage: null } } as Record<string, unknown>
|
||||
const original = {
|
||||
spec: { storage: { storageClass: "replicated", size: "10Gi" } },
|
||||
}
|
||||
const result = overlayImmutable(submitted, original, [
|
||||
["spec", "storage", "storageClass"],
|
||||
])
|
||||
expect(result).toEqual({ spec: { storage: { storageClass: "replicated" } } })
|
||||
})
|
||||
|
||||
it("array reordering by the user with index-aligned overlay re-anchors source values to the new index (pinned current behaviour)", () => {
|
||||
|
||||
@@ -174,7 +174,11 @@ function overlayPath(
|
||||
const sourceVal = sourceObj ? sourceObj[seg] : undefined
|
||||
const targetObj = isPlainObject(target)
|
||||
? (target as Record<string, unknown>)
|
||||
: null
|
||||
: // A YAML edit can drop the whole intermediate object; materialise it so
|
||||
// the immutable leaf underneath is still restored from source.
|
||||
sourceVal !== undefined && (target === undefined || target === null)
|
||||
? {}
|
||||
: null
|
||||
if (!targetObj) return target
|
||||
if (
|
||||
sourceVal === undefined &&
|
||||
|
||||
Reference in new issue
Block a user