Skip to content

Commit c44253d

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

File tree

4 files changed

+812
-15
lines changed

4 files changed

+812
-15
lines changed

api/filters/replacement/replacement.go

Lines changed: 131 additions & 0 deletions
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})
@@ -247,3 +256,125 @@ 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 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 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 err
319+
}
320+
321+
targetFields, err := targetInStructured.Elements()
322+
if err != nil {
323+
return 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+
// Original was JSON - convert back to JSON
343+
var jsonData interface{}
344+
modifiedData, err := structuredData.String()
345+
if err != nil {
346+
return err
347+
}
348+
349+
// Parse the YAML output as JSON
350+
if err := yaml.Unmarshal([]byte(modifiedData), &jsonData); err != nil {
351+
return err
352+
}
353+
354+
// Check if original was pretty-printed by looking for newlines and indentation
355+
if strings.Contains(scalarValue, "\n") && strings.Contains(scalarValue, " ") {
356+
// Pretty-print the JSON to match original formatting
357+
if prettyJSON, err := json.MarshalIndent(jsonData, "", " "); err == nil {
358+
scalarField.YNode().Value = string(prettyJSON)
359+
return nil
360+
}
361+
} else {
362+
// Compact JSON
363+
if compactJSON, err := json.Marshal(jsonData); err == nil {
364+
scalarField.YNode().Value = string(compactJSON)
365+
return nil
366+
}
367+
}
368+
}
369+
370+
// Fallback to YAML format
371+
modifiedData, err := structuredData.String()
372+
if err != nil {
373+
return err
374+
}
375+
376+
// Update the original scalar field
377+
scalarField.YNode().Value = strings.TrimSpace(modifiedData)
378+
379+
return nil
380+
}

0 commit comments

Comments
 (0)