Skip to content

Commit bbe53c2

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

File tree

5 files changed

+927
-13
lines changed

5 files changed

+927
-13
lines changed

api/filters/replacement/replacement.go

Lines changed: 153 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,146 @@ 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+
serializedData, err := serializeStructuredData(structuredData, scalarValue)
342+
if err != nil {
343+
return fmt.Errorf("%w", err)
344+
}
345+
346+
// Update the original scalar field
347+
scalarField.YNode().Value = serializedData
348+
349+
return nil
350+
}
351+
352+
// serializeStructuredData handles the serialization of structured data back to string format
353+
// preserving the original format (JSON vs YAML) and style (pretty vs compact)
354+
func serializeStructuredData(structuredData *yaml.RNode, originalValue string) (string, error) {
355+
firstChar := rune(strings.TrimSpace(originalValue)[0])
356+
if firstChar == '{' || firstChar == '[' {
357+
return serializeAsJSON(structuredData, originalValue)
358+
}
359+
360+
// Fallback to YAML format
361+
return serializeAsYAML(structuredData)
362+
}
363+
364+
// serializeAsJSON converts structured data back to JSON format
365+
func serializeAsJSON(structuredData *yaml.RNode, originalValue string) (string, error) {
366+
modifiedData, err := structuredData.String()
367+
if err != nil {
368+
return "", fmt.Errorf("failed to serialize structured data: %w", err)
369+
}
370+
371+
// Parse the YAML output as JSON
372+
var jsonData interface{}
373+
if err := yaml.Unmarshal([]byte(modifiedData), &jsonData); err != nil {
374+
return "", fmt.Errorf("failed to unmarshal YAML data: %w", err)
375+
}
376+
377+
// Check if original was pretty-printed by looking for newlines and indentation
378+
if strings.Contains(originalValue, "\n") && strings.Contains(originalValue, " ") {
379+
// Pretty-print the JSON to match original formatting
380+
if prettyJSON, err := json.MarshalIndent(jsonData, "", " "); err == nil {
381+
return string(prettyJSON), nil
382+
}
383+
}
384+
385+
// Compact JSON
386+
if compactJSON, err := json.Marshal(jsonData); err == nil {
387+
return string(compactJSON), nil
388+
}
389+
390+
return "", fmt.Errorf("failed to marshal JSON data")
391+
}
392+
393+
// serializeAsYAML converts structured data back to YAML format
394+
func serializeAsYAML(structuredData *yaml.RNode) (string, error) {
395+
modifiedData, err := structuredData.String()
396+
if err != nil {
397+
return "", fmt.Errorf("failed to serialize YAML data: %w", err)
398+
}
399+
400+
return strings.TrimSpace(modifiedData), nil
401+
}

0 commit comments

Comments
 (0)