feat!: drop keys with nil values when not required by scheme spec#1889
Conversation
On-behalf-of: SAP <matthias.bruns@sap.com> Signed-off-by: Matthias Bruns <git@matthiasbruns.com>
On-behalf-of: SAP <matthias.bruns@sap.com> Signed-off-by: Matthias Bruns <git@matthiasbruns.com>
On-behalf-of: SAP <matthias.bruns@sap.com> Signed-off-by: Matthias Bruns <git@matthiasbruns.com>
c93c79b to
edc67c0
Compare
Signed-off-by: Matthias Bruns <git@matthiasbruns.com>
e238ca4 to
517e6ef
Compare
On-behalf-of: SAP <matthias.bruns@sap.com> Signed-off-by: Matthias Bruns <git@matthiasbruns.com>
On-behalf-of: SAP <matthias.bruns@sap.com> Signed-off-by: Matthias Bruns <git@matthiasbruns.com>
Skarlso
left a comment
There was a problem hiding this comment.
Couple of questions and remarks.
On-behalf-of: SAP <matthias.bruns@sap.com> Signed-off-by: Matthias Bruns <git@matthiasbruns.com>
On-behalf-of: SAP <matthias.bruns@sap.com> Signed-off-by: Matthias Bruns <git@matthiasbruns.com>
…ctions On-behalf-of: SAP <matthias.bruns@sap.com> Signed-off-by: Matthias Bruns <git@matthiasbruns.com>
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughResolver now accepts an optional jsonschema.Schema (spec-level) and uses it to detect optional fields; nil values for optional fields are deleted from resources instead of being written. Deletions are tracked on ResolutionResult/ResolutionSummary, tests updated, and runtime now passes the spec sub-schema to the resolver. Changes
Sequence Diagram(s)sequenceDiagram
participant Runtime as Runtime
participant Resolver as Resolver
participant Schema as jsonschema.Schema
participant Resource as Resource (map[string]interface{})
Runtime->>Resolver: NewResolver(resource, data, specSubSchema)
Resolver->>Schema: Inspect path/schema (schemaProperty / isOptionalField / schemaItems)
Resolver->>Resolver: Resolve expression -> value
alt Field is optional and resolved value == nil
Resolver->>Resource: deleteValueAtPath(path)
Resolver-->>Runtime: ResolutionResult{Deleted:true}
Resolver->>Resolver: increment DeletedExpressions
else Field is required or value non-nil
Resolver->>Resource: write resolved value
Resolver-->>Runtime: ResolutionResult{Deleted:false}
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
bindings/go/transform/graph/builder/builder_test.go (1)
160-177: Consider asserting the resulting object shape, not justNoErrorFor this case, it would be valuable to also verify that
versionis actually absent in the processed output, so the test checks the semantic goal (drop optional nil field), not only execution success.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@bindings/go/transform/graph/builder/builder_test.go` around lines 160 - 177, The test currently only asserts staticAnalysisErr and runtimeProcessingErr are NoError; add an assertion that the processed output for the second transformation does not contain a version field (i.e. the optional cel reference ${add1.spec.object.?version} produced no value). After the processing step that yields the result, locate the resolved object for "add2" (or the structure populated from transformationSpec) and assert that add2.spec.object.version is absent/nil (use the existing test helpers like require.Nil/require.NotContains or equivalent) so the test verifies the semantic goal of dropping the optional nil field.bindings/go/transform/graph/runtime/resolver/resolver_test.go (1)
634-844: Please add one indexed-path optional deletion testThe new optional-field coverage is solid, but it would help to add a case like
items[0].optionalFieldresolving tonilto lock behavior for array-nested optional fields.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@bindings/go/transform/graph/runtime/resolver/resolver_test.go` around lines 634 - 844, Add a new t.Run inside TestResolveField_NilOptionalPointer_SpecSchema that exercises an indexed-path optional deletion: create a resource with an array property (e.g., "items": []interface{}{ map[string]interface{}{"optionalField": "${optExpr}", "other": "keep"} }), set data map with "optExpr": nil, call r := NewResolver(resource, data, specSchema) and result := r.resolveField(variable.FieldDescriptor{Path: fieldpath.MustParse("items[0].optionalField"), Expressions: []variable.Expression{{Value: "optExpr"}}, StandaloneExpression: true}), then require.NoError(result.Error), require.True(result.Resolved), require.True(result.Deleted) and assert the first item no longer contains "optionalField" but still contains "other"; use the same specSchema and jsonschema refs as the surrounding tests and mirror assertions style used in other t.Run cases.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@bindings/go/transform/graph/builder/builder_test.go`:
- Around line 160-177: The test currently only asserts staticAnalysisErr and
runtimeProcessingErr are NoError; add an assertion that the processed output for
the second transformation does not contain a version field (i.e. the optional
cel reference ${add1.spec.object.?version} produced no value). After the
processing step that yields the result, locate the resolved object for "add2"
(or the structure populated from transformationSpec) and assert that
add2.spec.object.version is absent/nil (use the existing test helpers like
require.Nil/require.NotContains or equivalent) so the test verifies the semantic
goal of dropping the optional nil field.
In `@bindings/go/transform/graph/runtime/resolver/resolver_test.go`:
- Around line 634-844: Add a new t.Run inside
TestResolveField_NilOptionalPointer_SpecSchema that exercises an indexed-path
optional deletion: create a resource with an array property (e.g., "items":
[]interface{}{ map[string]interface{}{"optionalField": "${optExpr}", "other":
"keep"} }), set data map with "optExpr": nil, call r := NewResolver(resource,
data, specSchema) and result := r.resolveField(variable.FieldDescriptor{Path:
fieldpath.MustParse("items[0].optionalField"), Expressions:
[]variable.Expression{{Value: "optExpr"}}, StandaloneExpression: true}), then
require.NoError(result.Error), require.True(result.Resolved),
require.True(result.Deleted) and assert the first item no longer contains
"optionalField" but still contains "other"; use the same specSchema and
jsonschema refs as the surrounding tests and mirror assertions style used in
other t.Run cases.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 583b021f-d34e-4da1-81d2-e375a8ce3d6e
📒 Files selected for processing (4)
bindings/go/transform/graph/builder/builder_test.gobindings/go/transform/graph/runtime/resolver/resolver.gobindings/go/transform/graph/runtime/resolver/resolver_test.gobindings/go/transform/graph/runtime/runtime.go
On-behalf-of: SAP <matthias.bruns@sap.com> Signed-off-by: Matthias Bruns <git@matthiasbruns.com>
c21318f to
b58f60e
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
bindings/go/transform/graph/runtime/resolver/resolver_test.go (1)
891-915: Add an array-item$refregression case to hardenisOptionalField.The array test currently covers
Items2020with inline object schema, but notItems2020pointing to a$refschema. Adding that case will lock behavior for common generated-schema patterns.Proposed test addition
func TestIsOptionalField_NestedRefs(t *testing.T) { @@ rArr := NewResolver(nil, nil, arrayTopSchema) assert.False(t, rArr.isOptionalField(fieldpath.MustParse("servers[0].host")), "host is required inside array item (Items2020)") assert.True(t, rArr.isOptionalField(fieldpath.MustParse("servers[0].timeout")), "timeout is optional inside array item (Items2020)") assert.True(t, rArr.isOptionalField(fieldpath.MustParse("servers[2].timeout")), "timeout is optional regardless of array index") + + // array items via $ref + arrayTopSchemaWithRefItems := &jsonschema.Schema{ + Properties: map[string]*jsonschema.Schema{ + "servers": {Items2020: &jsonschema.Schema{Ref: itemSchema}}, + }, + Required: []string{"servers"}, + } + rArrRef := NewResolver(nil, nil, arrayTopSchemaWithRefItems) + assert.False(t, rArrRef.isOptionalField(fieldpath.MustParse("servers[0].host")), + "host is required inside array item ($ref)") + assert.True(t, rArrRef.isOptionalField(fieldpath.MustParse("servers[0].timeout")), + "timeout is optional inside array item ($ref)") }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@bindings/go/transform/graph/runtime/resolver/resolver_test.go` around lines 891 - 915, Add a regression test that mirrors the existing array Items2020 case but where the array item schema is a $ref instead of an inline object: create a referenced schema (e.g., "Server" schema with Required ["host"]) and have arrayTopSchema.Properties["servers"].Items2020 point to a schema with Ref set to that reference; then construct the resolver with NewResolver(nil, nil, arrayTopSchema) and assert via rArr.isOptionalField(fieldpath.MustParse("servers[0].host")) and rArr.isOptionalField(fieldpath.MustParse("servers[0].timeout")) (and an index like "servers[2].timeout") to ensure required/optional behavior matches the inline Items2020 case.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@bindings/go/transform/graph/runtime/resolver/resolver.go`:
- Around line 333-339: Array item branch in Resolve traversal sets current =
itemSchema (from schemaItems(current)) without dereferencing a possible Ref,
unlike the property branch that checks propSchema.Ref; update the array-item
handling in the resolver (the segment.Index branch that uses schemaItems and
itemSchema) to detect and follow itemSchema.Ref (similar to how propSchema.Ref
is handled) before assigning/continuing traversal so array items are
consistently dereferenced.
---
Nitpick comments:
In `@bindings/go/transform/graph/runtime/resolver/resolver_test.go`:
- Around line 891-915: Add a regression test that mirrors the existing array
Items2020 case but where the array item schema is a $ref instead of an inline
object: create a referenced schema (e.g., "Server" schema with Required
["host"]) and have arrayTopSchema.Properties["servers"].Items2020 point to a
schema with Ref set to that reference; then construct the resolver with
NewResolver(nil, nil, arrayTopSchema) and assert via
rArr.isOptionalField(fieldpath.MustParse("servers[0].host")) and
rArr.isOptionalField(fieldpath.MustParse("servers[0].timeout")) (and an index
like "servers[2].timeout") to ensure required/optional behavior matches the
inline Items2020 case.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 340223bb-7a32-430a-9367-11e64055e76a
📒 Files selected for processing (2)
bindings/go/transform/graph/runtime/resolver/resolver.gobindings/go/transform/graph/runtime/resolver/resolver_test.go
There was a problem hiding this comment.
🧹 Nitpick comments (2)
bindings/go/transform/graph/runtime/resolver/resolver_test.go (1)
891-915: Consider adding a test case for array items withRef.The current array tests use
Items2020pointing directly to a schema with properties. Consider adding a test case whereItems2020itself contains aRef(e.g.,{Items2020: &jsonschema.Schema{Ref: itemSchema}}) to ensure consistent traversal behavior with the property-ref pattern tested elsewhere.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@bindings/go/transform/graph/runtime/resolver/resolver_test.go` around lines 891 - 915, Add a new test variant that mirrors the existing array item checks but sets Items2020 to a schema containing a Ref instead of inline properties: create itemSchema (same as existing), then create arrayTopSchema where "servers" has Items2020: &jsonschema.Schema{Ref: itemSchema} and instantiate resolver via NewResolver(nil, nil, arrayTopSchema); assert using rArr.isOptionalField(fieldpath.MustParse("servers[0].host")) and "servers[0].timeout" (and a different index like "servers[2].timeout") to verify required/optional behavior matches the inline-Items2020 case. Ensure you reference the same symbols (itemSchema, arrayTopSchema, Items2020, Ref, NewResolver, isOptionalField, fieldpath.MustParse) so traversal with a Ref is covered.bindings/go/transform/graph/runtime/resolver/resolver.go (1)
396-403: Final segment handling assumes map key without validation.The code at line 402 uses
segment.Nameunconditionally, but doesn't verify that the final segment is indeed a map key (not an array index). While current usage viaresolveFieldensures this is always true (sinceisOptionalFieldreturns false for array indices), the function signature doesn't enforce this constraint.Consider adding a guard for robustness:
Defensive check
if i == len(path)-1 { - // final segment: always a map key deletion + // final segment: only map key deletion is supported + if segment.Index != nil { + return fmt.Errorf("cannot delete array element at path segment: %v", segment) + } currentMap, ok := current.(map[string]interface{}) if !ok { return fmt.Errorf("expected map at path segment: %v", segment) } delete(currentMap, segment.Name) return nil }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@bindings/go/transform/graph/runtime/resolver/resolver.go` around lines 396 - 403, The final-segment deletion assumes the segment is a map key and uses segment.Name unconditionally; add a defensive check before treating it as a map key (e.g., verify the segment represents a key rather than an array index — using whatever discriminator the Segment type exposes, such as a boolean like IsIndex or by checking an empty Name) and return a clear error if it's not valid; update the block in the resolver function where currentMap is asserted (the same code path referenced by resolveField and isOptionalField) to validate the segment kind before calling delete(currentMap, segment.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 `@bindings/go/transform/graph/runtime/resolver/resolver_test.go`:
- Around line 891-915: Add a new test variant that mirrors the existing array
item checks but sets Items2020 to a schema containing a Ref instead of inline
properties: create itemSchema (same as existing), then create arrayTopSchema
where "servers" has Items2020: &jsonschema.Schema{Ref: itemSchema} and
instantiate resolver via NewResolver(nil, nil, arrayTopSchema); assert using
rArr.isOptionalField(fieldpath.MustParse("servers[0].host")) and
"servers[0].timeout" (and a different index like "servers[2].timeout") to verify
required/optional behavior matches the inline-Items2020 case. Ensure you
reference the same symbols (itemSchema, arrayTopSchema, Items2020, Ref,
NewResolver, isOptionalField, fieldpath.MustParse) so traversal with a Ref is
covered.
In `@bindings/go/transform/graph/runtime/resolver/resolver.go`:
- Around line 396-403: The final-segment deletion assumes the segment is a map
key and uses segment.Name unconditionally; add a defensive check before treating
it as a map key (e.g., verify the segment represents a key rather than an array
index — using whatever discriminator the Segment type exposes, such as a boolean
like IsIndex or by checking an empty Name) and return a clear error if it's not
valid; update the block in the resolver function where currentMap is asserted
(the same code path referenced by resolveField and isOptionalField) to validate
the segment kind before calling delete(currentMap, segment.Name).
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: ce33f583-cded-45b7-ab50-ccfa1180e121
📒 Files selected for processing (2)
bindings/go/transform/graph/runtime/resolver/resolver.gobindings/go/transform/graph/runtime/resolver/resolver_test.go
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@bindings/go/transform/graph/runtime/resolver/resolver.go`:
- Around line 380-390: schemaItems currently ignores tuple-style Items (slice
[]*jsonschema.Schema), causing nil returns during indexed array traversal in the
resolver; update schemaItems to detect when s.Items is a []*jsonschema.Schema
and return the first element (to match the wrapper Schema.Items() behavior) or
the indexed element when available during traversal, so optional-field detection
at the resolver's indexed array handling (around where schemaItems is used at
the indexed traversal) no longer gets nil; ensure you check for non-empty slice
before returning.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: f785ff12-1a1b-43da-bfe3-3c35f83bc659
📒 Files selected for processing (2)
bindings/go/transform/graph/runtime/resolver/resolver.gobindings/go/transform/graph/runtime/resolver/resolver_test.go
930257e to
c01b1f6
Compare
On-behalf-of: SAP <matthias.bruns@sap.com> Signed-off-by: Matthias Bruns <git@matthiasbruns.com>
c01b1f6 to
62a2254
Compare
There was a problem hiding this comment.
🧹 Nitpick comments (1)
bindings/go/transform/graph/runtime/resolver/resolver.go (1)
323-360: Add defensive dereferencing for root-level schema refs inisOptionalField.The current implementation doesn't handle cases where the passed schema is itself a
$refwrapper (particularly in Ref chains). WhilespecSubSchemadereferences one level, if the resulting target itself has a Ref, traversal begins without dereferencing it, causingschemaPropertyto fail to find properties. Add thederefSchemahelper function and apply it at the root and all intermediate levels as proposed to handle Ref chains defensively.♻️ Proposed hardening
+func derefSchema(s *jsonschema.Schema) *jsonschema.Schema { + for s != nil && s.Ref != nil { + s = s.Ref + } + return s +} + func (r *Resolver) isOptionalField(path fieldpath.Path) bool { if r.schema == nil || len(path) == 0 { return false } - current := r.schema + current := derefSchema(r.schema) // Walk the segments and either check a property or the ref schema until we get to the last segment, where we check if it's required or not. for i, segment := range path { + current = derefSchema(current) if current == nil { return false } if segment.Index != nil { itemSchema := current.Items2020 if itemSchema == nil { return false } - if itemSchema.Ref != nil { - current = itemSchema.Ref - } else { - current = itemSchema - } + current = derefSchema(itemSchema) continue } propSchema := schemaProperty(current, segment.Name) if propSchema == nil { return false } @@ - if propSchema.Ref != nil { - // if we have a ref, we need to resolve it before continuing to the next segment - current = propSchema.Ref - } else { - current = propSchema - } + current = derefSchema(propSchema) } return false }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@bindings/go/transform/graph/runtime/resolver/resolver.go` around lines 323 - 360, The isOptionalField implementation must defensively dereference $ref wrapper schemas to avoid failing property lookups when the root or intermediate target is a Ref; add and use a helper derefSchema (or reuse specSubSchema behavior) to replace current = propSchema.Ref / itemSchema.Ref / r.schema with derefSchema(current) at the start and whenever you follow a Ref (including the root r.schema and after resolving itemSchema.Ref or propSchema.Ref) so schemaProperty always receives the actual target schema rather than a Ref wrapper; update isOptionalField to call derefSchema on r.schema initially and after each Ref resolution before continuing traversal.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@bindings/go/transform/graph/runtime/resolver/resolver.go`:
- Around line 323-360: The isOptionalField implementation must defensively
dereference $ref wrapper schemas to avoid failing property lookups when the root
or intermediate target is a Ref; add and use a helper derefSchema (or reuse
specSubSchema behavior) to replace current = propSchema.Ref / itemSchema.Ref /
r.schema with derefSchema(current) at the start and whenever you follow a Ref
(including the root r.schema and after resolving itemSchema.Ref or
propSchema.Ref) so schemaProperty always receives the actual target schema
rather than a Ref wrapper; update isOptionalField to call derefSchema on
r.schema initially and after each Ref resolution before continuing traversal.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: c8b2d120-4575-472b-a7ba-16d5fd7dc57d
📒 Files selected for processing (2)
bindings/go/transform/graph/runtime/resolver/resolver.gobindings/go/transform/graph/runtime/resolver/resolver_test.go
Co-authored-by: Fabian Burth <fabian.burth@sap.com> Signed-off-by: Matthias Bruns <github@matthiasbruns.com>
8fea505
…en-component-model#1889) #### What this PR does / why we need it This PR solves the issue that happens when you want to pass nil values for optional pointers through the transformation steps. If in a spec a type is annotated by `omitempty` and resolved to `nil` one would suspect that `nil` is a valid value. The problem is, that our schemagen does not generate a rule that allows `null` to be passed in such cases. Since we have no way to omitting keys based on the fact that values exist or not, we have to circumvent that in the transformer graph. This PR does that. Here is an example of what we are trying to solve with this change: ```go convertToOCITransform := transformv1alpha1.GenericTransformation{ TransformationMeta: meta.TransformationMeta{ Type: ociv1alpha1.ConvertHelmToOCIV1alpha1, ID: convertResourceID, }, Spec: &runtime.Unstructured{Data: map[string]any{ "resource": fmt.Sprintf("${%s.output.resource}", getResourceID), "chartFile": fmt.Sprintf("${%s.output.chartFile}", getResourceID), "provFile": fmt.Sprintf("${%[1]s.output.?provFile}", getResourceID), }}, } ``` `provFile` might be null, if not provided by the helm chart. We can only run checks on the values with CEL here. If we pass `nil` to the `provFile` json field, the schemavalidation will fail. open-component-model#1846 #### Which issue(s) this PR fixes Contributes: open-component-model/ocm-project#883 #### Testing ##### How to test the changes Fully tested in https://github.com/open-component-model/open-component-model/pull/1846/changes#diff-85d55581ce565d132e8a8b027169c162a024c7e25f6df4c5cc2ddbbfb1294d2fR356 ##### Verification - [x] I have tested the changes locally by running `ocm` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Schema-driven resolution: resolvers can accept optional schemas (including spec sub-schemas) to detect optional fields and guide processing. * **Bug Fixes** * Optional-field removal: optional fields that resolve to nil are removed from resources (no null retained). * Runtime processing no longer treats optional nil values as errors. * **Tests** * Expanded coverage for nil handling, nested/referenced schemas, array items, and targeted deletions. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: Matthias Bruns <git@matthiasbruns.com> Signed-off-by: Matthias Bruns <github@matthiasbruns.com> Co-authored-by: Fabian Burth <fabian.burth@sap.com>
…en-component-model#1889) #### What this PR does / why we need it This PR solves the issue that happens when you want to pass nil values for optional pointers through the transformation steps. If in a spec a type is annotated by `omitempty` and resolved to `nil` one would suspect that `nil` is a valid value. The problem is, that our schemagen does not generate a rule that allows `null` to be passed in such cases. Since we have no way to omitting keys based on the fact that values exist or not, we have to circumvent that in the transformer graph. This PR does that. Here is an example of what we are trying to solve with this change: ```go convertToOCITransform := transformv1alpha1.GenericTransformation{ TransformationMeta: meta.TransformationMeta{ Type: ociv1alpha1.ConvertHelmToOCIV1alpha1, ID: convertResourceID, }, Spec: &runtime.Unstructured{Data: map[string]any{ "resource": fmt.Sprintf("${%s.output.resource}", getResourceID), "chartFile": fmt.Sprintf("${%s.output.chartFile}", getResourceID), "provFile": fmt.Sprintf("${%[1]s.output.?provFile}", getResourceID), }}, } ``` `provFile` might be null, if not provided by the helm chart. We can only run checks on the values with CEL here. If we pass `nil` to the `provFile` json field, the schemavalidation will fail. open-component-model#1846 #### Which issue(s) this PR fixes Contributes: open-component-model/ocm-project#883 #### Testing ##### How to test the changes Fully tested in https://github.com/open-component-model/open-component-model/pull/1846/changes#diff-85d55581ce565d132e8a8b027169c162a024c7e25f6df4c5cc2ddbbfb1294d2fR356 ##### Verification - [x] I have tested the changes locally by running `ocm` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Schema-driven resolution: resolvers can accept optional schemas (including spec sub-schemas) to detect optional fields and guide processing. * **Bug Fixes** * Optional-field removal: optional fields that resolve to nil are removed from resources (no null retained). * Runtime processing no longer treats optional nil values as errors. * **Tests** * Expanded coverage for nil handling, nested/referenced schemas, array items, and targeted deletions. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: Matthias Bruns <git@matthiasbruns.com> Signed-off-by: Matthias Bruns <github@matthiasbruns.com> Co-authored-by: Fabian Burth <fabian.burth@sap.com> Signed-off-by: Gerald Morrison (SAP) <gerald.morrison@sap.com>
What this PR does / why we need it
This PR solves the issue that happens when you want to pass nil values for optional pointers through the transformation steps.
If in a spec a type is annotated by
omitemptyand resolved tonilone would suspect that
nilis a valid value. The problem is, that our schemagen does not generate a rule that allowsnullto be passed in such cases.Since we have no way to omitting keys based on the fact that values exist or not, we have to circumvent that in the transformer graph.
This PR does that.
Here is an example of what we are trying to solve with this change:
provFilemight be null, if not provided by the helm chart. We can only run checks on the values with CEL here. If we passnilto theprovFilejson field, the schemavalidation will fail.#1846
Which issue(s) this PR fixes
Contributes: open-component-model/ocm-project#883
Testing
How to test the changes
Fully tested in https://github.com/open-component-model/open-component-model/pull/1846/changes#diff-85d55581ce565d132e8a8b027169c162a024c7e25f6df4c5cc2ddbbfb1294d2fR356
Verification
ocmSummary by CodeRabbit
New Features
Bug Fixes
Tests