diff --git a/internal/VERSION b/internal/VERSION index 5712157..1a96df1 100644 --- a/internal/VERSION +++ b/internal/VERSION @@ -1 +1 @@ -0.10.1 +0.11.3 diff --git a/internal/budgetadjustments/events/delete.go b/internal/budgetadjustments/events/delete.go index 686f02f..43d198f 100644 --- a/internal/budgetadjustments/events/delete.go +++ b/internal/budgetadjustments/events/delete.go @@ -43,24 +43,26 @@ func NewDeleteCmd(clientProvider sdkclient.SdkClientProvider) *cobra.Command { } func (g *DeleteCmd) run(cmd *cobra.Command) error { - data, err := readFile(g.filepath) + docs, err := getEventsStringsFromFile(g.filepath) if err != nil { - return errors.Wrap(err, "failed to read input data") - } - body, err := yaml.YAMLToJSON(data) - if err != nil { - return errors.Wrap(err, "failed to convert input data to JSON") + return errors.Wrap(err, "failed to read events form file") } - if _, err = DoRequest( - g.client, - cmd.Context(), - http.MethodPost, - fmt.Sprintf("%s/%s/events/delete", BudgetAdjustmentAPI, g.adjustment), - nil, - bytes.NewReader(body), - ); err != nil { - return err + for _, doc := range docs { + jsonBytes, err := yaml.YAMLToJSON([]byte(doc)) + if err != nil { + return errors.Wrap(err, "failed to convert input data to JSON") + } + if _, err = DoRequest( + g.client, + cmd.Context(), + http.MethodPost, + fmt.Sprintf("%s/%s/events/delete", BudgetAdjustmentAPI, g.adjustment), + nil, + bytes.NewReader(jsonBytes), + ); err != nil { + return err + } } return nil diff --git a/internal/budgetadjustments/events/inputreader.go b/internal/budgetadjustments/events/inputreader.go index 6d78994..7f669f5 100644 --- a/internal/budgetadjustments/events/inputreader.go +++ b/internal/budgetadjustments/events/inputreader.go @@ -4,6 +4,9 @@ import ( "io" "os" "path/filepath" + "regexp" + + "github.com/pkg/errors" ) func readFile(path string) (data []byte, err error) { @@ -13,3 +16,24 @@ func readFile(path string) (data []byte, err error) { path = filepath.Clean(path) return os.ReadFile(path) // #nosec G304 } + +func getEventsStringsFromFile(path string) ([]string, error) { + data, err := readFile(path) + if err != nil { + return nil, errors.Wrap(err, "failed to read input data") + } + return splitYAMLDocs(data), nil +} + +func splitYAMLDocs(data []byte) []string { + re := regexp.MustCompile("(?m)^---$\n?") + split := re.Split(string(data), -1) + docs := make([]string, 0, len(split)) + for _, docStr := range split { + if len(docStr) < 1 { + continue + } + docs = append(docs, docStr) + } + return docs +} diff --git a/internal/budgetadjustments/events/inputreader_test.go b/internal/budgetadjustments/events/inputreader_test.go new file mode 100644 index 0000000..c4c7c8a --- /dev/null +++ b/internal/budgetadjustments/events/inputreader_test.go @@ -0,0 +1,160 @@ +package events + +import ( + "reflect" + "strings" + "testing" +) + +func TestSplitYAMLDocuments(t *testing.T) { + tests := []struct { + name string + input string + expected []string + }{ + { + name: "Basic YAML Split", + input: `--- +name: doc1 +value: 1 +--- +name: doc2 +value: 2 +--- +name: doc3 +value: 3 + `, + expected: []string{ + "name: doc1\nvalue: 1", + "name: doc2\nvalue: 2", + "name: doc3\nvalue: 3", + }, + }, + { + name: "Basic YAML Split with additional separators", + input: `--- +--- +--- +name: doc1 +value: 1 +--- +--- +name: doc2 +value: 2 +--- +name: doc3 +value: 3 +--- +---`, + expected: []string{ + "name: doc1\nvalue: 1", + "name: doc2\nvalue: 2", + "name: doc3\nvalue: 3", + }, + }, + { + name: "YAML with Lists", + input: `--- +list: + - item1 + - item2 +--- +list: + - item3 + - item4 +`, + expected: []string{ + "list:\n - item1\n - item2", + "list:\n - item3\n - item4", + }, + }, + { + name: "YAML with Nested Structures", + input: `--- +parent: + child: value1 +--- +parent: + child: value2 +`, + expected: []string{ + "parent:\n child: value1", + "parent:\n child: value2", + }, + }, + { + name: "invalid YAML", + input: `--- +foo bar +baz: bob +`, + expected: []string{"foo bar\nbaz: bob"}, + }, + { + name: "just YAML", + input: "YAML", + expected: []string{"YAML"}, + }, + { + name: "YAML with doc separators found in content", + input: `--- +parent: + child: "foo---bar" +--- +parent: + child: value2 +--- +----nt: + child: value3 +---- +--- + --- + --- +`, + expected: []string{ + "parent:\n child: \"foo---bar\"", + "parent:\n child: value2", + "----nt:\n child: value3\n----", + "---\n ---", + }, + }, + { + name: "YAML with correct event format", + input: ` +- eventStart: 2024-12-04T06:37:00Z + eventEnd: 2024-12-04T06:59:00Z + slos: + - project: test-project + name: sample-slo-1-578b974d-8e27-43cf-85a3-7751a774f13d + update: + eventStart: 2024-12-04T06:37:00Z + eventEnd: 2024-12-04T06:59:00Z +`, + expected: []string{ + `- eventStart: 2024-12-04T06:37:00Z + eventEnd: 2024-12-04T06:59:00Z + slos: + - project: test-project + name: sample-slo-1-578b974d-8e27-43cf-85a3-7751a774f13d + update: + eventStart: 2024-12-04T06:37:00Z + eventEnd: 2024-12-04T06:59:00Z`, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := splitYAMLDocs([]byte(tt.input)) + + // Trim whitespace from results for better comparison + for i := range result { + result[i] = strings.TrimSpace(result[i]) + } + + if !reflect.DeepEqual(result, tt.expected) { + t.Errorf("Test %s failed. Expected %v, but got %v", tt.name, tt.expected, result) + } + }) + } +} diff --git a/internal/budgetadjustments/events/update.go b/internal/budgetadjustments/events/update.go index 89f7cc4..f4dbc05 100644 --- a/internal/budgetadjustments/events/update.go +++ b/internal/budgetadjustments/events/update.go @@ -43,25 +43,27 @@ func NewUpdateCmd(clientProvider sdkclient.SdkClientProvider) *cobra.Command { } func (g *UpdateCmd) run(cmd *cobra.Command) error { - data, err := readFile(g.filepath) + docs, err := getEventsStringsFromFile(g.filepath) if err != nil { - return errors.Wrap(err, "failed to read input data") - } - body, err := yaml.YAMLToJSON(data) - if err != nil { - return errors.Wrap(err, "failed to convert input data to JSON") + return errors.Wrap(err, "failed to read events form file") } - _, err = DoRequest( - g.client, - cmd.Context(), - http.MethodPut, - fmt.Sprintf("%s/%s/events/update", BudgetAdjustmentAPI, g.adjustment), - nil, - bytes.NewReader(body), - ) - if err != nil { - return err + for _, doc := range docs { + jsonBytes, err := yaml.YAMLToJSON([]byte(doc)) + if err != nil { + return errors.Wrap(err, "failed to convert input data to JSON") + } + _, err = DoRequest( + g.client, + cmd.Context(), + http.MethodPut, + fmt.Sprintf("%s/%s/events/update", BudgetAdjustmentAPI, g.adjustment), + nil, + bytes.NewReader(jsonBytes), + ) + if err != nil { + return err + } } return nil