Skip to content

Commit ccc7f8f

Browse files
committed
implements nested structure replacements
1 parent 2dc0d0d commit ccc7f8f

File tree

4 files changed

+902
-8
lines changed

4 files changed

+902
-8
lines changed

api/filters/replacement/replacement.go

Lines changed: 134 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
package replacement
55

66
import (
7+
"encoding/json"
78
"fmt"
89
"strings"
910

@@ -188,6 +189,14 @@ func copyValueToTarget(target *yaml.RNode, value *yaml.RNode, selector *types.Ta
188189
if selector.Options != nil && selector.Options.Create {
189190
createKind = value.YNode().Kind
190191
}
192+
193+
// Check if this fieldPath contains structured data access
194+
if err := setValueInStructuredData(target, value, fp, createKind); err == nil {
195+
// Successfully handled as structured data
196+
continue
197+
}
198+
199+
// Fall back to normal path handling
191200
targetFieldList, err := target.Pipe(&yaml.PathMatcher{
192201
Path: kyaml_utils.SmarterPathSplitter(fp, "."),
193202
Create: createKind})
@@ -204,7 +213,7 @@ func copyValueToTarget(target *yaml.RNode, value *yaml.RNode, selector *types.Ta
204213

205214
for _, t := range targetFields {
206215
if err := setFieldValue(selector.Options, t, value); err != nil {
207-
return err
216+
return fmt.Errorf("%w", err)
208217
}
209218
}
210219
}
@@ -247,3 +256,127 @@ func setFieldValue(options *types.FieldOptions, targetField *yaml.RNode, value *
247256

248257
return nil
249258
}
259+
260+
// setValueInStructuredData handles setting values within structured data (JSON/YAML) in scalar fields
261+
func setValueInStructuredData(target *yaml.RNode, value *yaml.RNode, fieldPath string, createKind yaml.Kind) error {
262+
pathParts := kyaml_utils.SmarterPathSplitter(fieldPath, ".")
263+
if len(pathParts) < 2 {
264+
return fmt.Errorf("not a structured data path")
265+
}
266+
267+
// Find the potential scalar field that might contain structured data
268+
var scalarFieldPath []string
269+
var structuredDataPath []string
270+
var foundScalar = false
271+
272+
// Try to find where the scalar field ends and structured data begins
273+
for i := 1; i <= len(pathParts); i++ {
274+
potentialScalarPath := pathParts[:i]
275+
scalarField, err := target.Pipe(yaml.Lookup(potentialScalarPath...))
276+
if err != nil {
277+
continue
278+
}
279+
if scalarField != nil && scalarField.YNode().Kind == yaml.ScalarNode && i < len(pathParts) {
280+
// Try to parse the scalar value as structured data
281+
scalarValue := scalarField.YNode().Value
282+
var parsedNode yaml.Node
283+
if err := yaml.Unmarshal([]byte(scalarValue), &parsedNode); err == nil {
284+
// Successfully parsed - this is structured data
285+
scalarFieldPath = potentialScalarPath
286+
structuredDataPath = pathParts[i:]
287+
foundScalar = true
288+
break
289+
}
290+
}
291+
}
292+
293+
if !foundScalar {
294+
return fmt.Errorf("no structured data found in path")
295+
}
296+
297+
// Get the scalar field containing structured data
298+
scalarField, err := target.Pipe(yaml.Lookup(scalarFieldPath...))
299+
if err != nil {
300+
return fmt.Errorf("%w", err)
301+
}
302+
303+
// Parse the structured data
304+
scalarValue := scalarField.YNode().Value
305+
var parsedNode yaml.Node
306+
if err := yaml.Unmarshal([]byte(scalarValue), &parsedNode); err != nil {
307+
return fmt.Errorf("%w", err)
308+
}
309+
310+
structuredData := yaml.NewRNode(&parsedNode)
311+
312+
// Navigate to the target location within the structured data
313+
targetInStructured, err := structuredData.Pipe(&yaml.PathMatcher{
314+
Path: structuredDataPath,
315+
Create: createKind,
316+
})
317+
if err != nil {
318+
return fmt.Errorf("%w", err)
319+
}
320+
321+
targetFields, err := targetInStructured.Elements()
322+
if err != nil {
323+
return fmt.Errorf("%w", err)
324+
}
325+
326+
if len(targetFields) == 0 {
327+
return fmt.Errorf("unable to find field in structured data")
328+
}
329+
330+
// Set the value in the structured data
331+
for _, t := range targetFields {
332+
if t.YNode().Kind == yaml.ScalarNode {
333+
t.YNode().Value = value.YNode().Value
334+
} else {
335+
t.SetYNode(value.YNode())
336+
}
337+
}
338+
339+
// Serialize the modified structured data back to the scalar field
340+
// Try to detect if original was JSON or YAML and preserve formatting
341+
// if strings.TrimSpace(scalarValue)[0] == '{' || strings.TrimSpace(scalarValue)[0] == '[' {
342+
firstChar := strings.TrimSpace(scalarValue)[0]
343+
if firstChar == '{' || firstChar == '[' {
344+
// Original was JSON - convert back to JSON
345+
var jsonData interface{}
346+
modifiedData, err := structuredData.String()
347+
if err != nil {
348+
return fmt.Errorf("%w", err)
349+
}
350+
351+
// Parse the YAML output as JSON
352+
if err := yaml.Unmarshal([]byte(modifiedData), &jsonData); err != nil {
353+
return fmt.Errorf("%w", err)
354+
}
355+
356+
// Check if original was pretty-printed by looking for newlines and indentation
357+
if strings.Contains(scalarValue, "\n") && strings.Contains(scalarValue, " ") {
358+
// Pretty-print the JSON to match original formatting
359+
if prettyJSON, err := json.MarshalIndent(jsonData, "", " "); err == nil {
360+
scalarField.YNode().Value = string(prettyJSON)
361+
return nil
362+
}
363+
} else {
364+
// Compact JSON
365+
if compactJSON, err := json.Marshal(jsonData); err == nil {
366+
scalarField.YNode().Value = string(compactJSON)
367+
return nil
368+
}
369+
}
370+
}
371+
372+
// Fallback to YAML format
373+
modifiedData, err := structuredData.String()
374+
if err != nil {
375+
return fmt.Errorf("%w", err)
376+
}
377+
378+
// Update the original scalar field
379+
scalarField.YNode().Value = strings.TrimSpace(modifiedData)
380+
381+
return nil
382+
}

0 commit comments

Comments
 (0)