diff --git a/cmd/apply.go b/cmd/apply.go index 0d12164..069cbc4 100644 --- a/cmd/apply.go +++ b/cmd/apply.go @@ -6,8 +6,9 @@ import ( ) var ( - applyFile string - applyProject string + applyFile string + applyProject string + applyRecursive bool applyCmd = &cobra.Command{ Use: "apply", @@ -20,7 +21,7 @@ var ( } r := rpdac.NewReportPortal(c) - return r.Apply(applyProject, applyFile) + return r.Apply(applyProject, applyFile, applyRecursive) }, } ) @@ -28,6 +29,7 @@ var ( func init() { applyCmd.Flags().StringVarP(&applyFile, "file", "f", "", "YAML file") applyCmd.Flags().StringVarP(&applyProject, "project", "p", "", "ReportPortal Project") + applyCmd.Flags().BoolVarP(&applyRecursive, "recursive", "r", false, "If file is a directory it will recusive apply all objects in it") applyCmd.MarkFlagRequired("file") applyCmd.MarkFlagRequired("project") diff --git a/pkg/rpdac/filter.go b/pkg/rpdac/filter.go index e9d5b9f..8ec1152 100644 --- a/pkg/rpdac/filter.go +++ b/pkg/rpdac/filter.go @@ -80,8 +80,6 @@ func (s *FilterService) Update(project string, current, target Object) error { if err != nil { return fmt.Errorf("error updating filter \"%s\": %w", targetFilter.Name, err) } - - log.Printf("update \"%s\" filter", targetFilter.Name) return nil } diff --git a/pkg/rpdac/reportportal.go b/pkg/rpdac/reportportal.go index 74267c2..dc6f67d 100644 --- a/pkg/rpdac/reportportal.go +++ b/pkg/rpdac/reportportal.go @@ -1,9 +1,14 @@ package rpdac import ( + "errors" "fmt" + "io/fs" "io/ioutil" "log" + "os" + "path/filepath" + "strings" "github.com/b1zzu/reportportal-dashboards-as-code/pkg/reportportal" "gopkg.in/yaml.v2" @@ -122,7 +127,59 @@ func (r *ReportPortal) Create(project, file string) error { return nil } -func (r *ReportPortal) Apply(project, file string) error { +func (r *ReportPortal) Apply(project, file string, recursive bool) error { + + info, err := os.Stat(file) + if os.IsNotExist(err) { + return fmt.Errorf("error '%s' is not a vailid file or directory: %w", file, err) + } else if err != nil { + return err + } + + if info.IsDir() { + + if !recursive { + return fmt.Errorf("error '%s' is a directory, use the `-r` option if you want to recursive apply all object in the directory", file) + } + + failed := false + err = filepath.WalkDir(file, func(path string, d fs.DirEntry, err error) error { + if err != nil { + log.Printf("Unknow error: %s", err) + return nil + } + + if d.IsDir() { + // skip directories + return nil + } + + if !strings.HasSuffix(d.Name(), ".yml") && !strings.HasSuffix(d.Name(), ".yaml") { + log.Printf("Ignore file '%s' because only .yml|.yaml are supported", path) + return nil + } + + if err := r.ApplyFile(project, path); err != nil { + failed = true + log.Printf("Failed to apply file '%s': %s", path, err) + } + return nil + }) + if err != nil { + return err + } + + if failed { + return errors.New("error applying one or more objects") + } + return nil + + } else { + return r.ApplyFile(project, file) + } +} + +func (r *ReportPortal) ApplyFile(project, file string) error { fileBytes, err := ioutil.ReadFile(file) if err != nil { diff --git a/pkg/rpdac/reportportal_test.go b/pkg/rpdac/reportportal_test.go index 07fd547..be253c2 100644 --- a/pkg/rpdac/reportportal_test.go +++ b/pkg/rpdac/reportportal_test.go @@ -1,6 +1,7 @@ package rpdac import ( + "fmt" "io/ioutil" "os" "testing" @@ -268,7 +269,7 @@ description: Test desc r := NewReportPortal(nil) r.Dashboard = mockService - err := r.Apply("test_project", file) + err := r.Apply("test_project", file, false) if err != nil { t.Errorf("Apply retunred error: %s", err) } @@ -317,7 +318,7 @@ description: Test new desc r := NewReportPortal(nil) r.Dashboard = mockService - err := r.Apply("test_project", file) + err := r.Apply("test_project", file, false) if err != nil { t.Errorf("Apply retunred error: %s", err) } @@ -355,10 +356,150 @@ description: Test desc r := NewReportPortal(nil) r.Dashboard = mockService - err := r.Apply("test_project", file) + err := r.Apply("test_project", file, false) if err != nil { t.Errorf("Apply retunred error: %s", err) } testDeepEqual(t, mockService.Counter, MockServiceCounter{GetByName: 1, Create: 1}) } + +func tempDir(t *testing.T) (string, func()) { + t.Helper() + + dir, err := ioutil.TempDir("", "") + if err != nil { + t.Fatalf("failed to create tmp directory: %s", err) + } + return dir, func() { os.RemoveAll(dir) } +} + +func mkdir(t *testing.T, name string) { + t.Helper() + + err := os.Mkdir(name, 0755) + if err != nil { + t.Fatalf("failed to create dir '%s': %s", name, err) + } +} + +func writeFile(t *testing.T, filename, data string) { + t.Helper() + + err := ioutil.WriteFile(filename, []byte(data), 0644) + if err != nil { + t.Fatalf("failed to write file '%s': %s", filename, err) + } +} + +func TestApply_Directory(t *testing.T) { + + dir, clean := tempDir(t) + defer clean() + + writeFile(t, dir+"/dashboard.yml", `kind: Dashboard +name: Test +`) + writeFile(t, dir+"/skipme.xml", `Some randome stuff`) + mkdir(t, dir+"/subfolder") + writeFile(t, dir+"/subfolder/filter.yaml", `kind: Filter +name: Test +`) + + mockDashboardService := &MockService{ + GetByNameM: func(project, name string) (Object, error) { + testEqual(t, project, "test_project") + testEqual(t, name, "Test") + return nil, nil + }, + CreateM: func(project string, o Object) error { + testEqual(t, project, "test_project") + testDeepEqual(t, o, &Dashboard{ + Kind: DashboardKind, + Name: "Test", + }, cmpopts.IgnoreUnexported(Dashboard{})) + return nil + }, + } + mockFilterService := &MockService{ + GetByNameM: func(project, name string) (Object, error) { + testEqual(t, project, "test_project") + testEqual(t, name, "Test") + return &Filter{ + Kind: FilterKind, + Name: "Test", + }, nil + }, + } + r := NewReportPortal(nil) + r.Dashboard = mockDashboardService + r.Filter = mockFilterService + + err := r.Apply("test_project", dir, true) + if err != nil { + t.Errorf("Apply retunred error: %s", err) + } + + testDeepEqual(t, mockDashboardService.Counter, MockServiceCounter{GetByName: 1, Create: 1}) + testDeepEqual(t, mockFilterService.Counter, MockServiceCounter{GetByName: 1}) +} + +func TestApply_DirectoryWithoutRecursive(t *testing.T) { + + dir, clean := tempDir(t) + defer clean() + r := NewReportPortal(nil) + + err := r.Apply("test_project", dir, false) + if err == nil { + t.Errorf("Want err but got nil") + } else { + want := fmt.Sprintf("error '%s' is a directory, use the `-r` option if you want to recursive apply all object in the directory", dir) + if err.Error() != want { + t.Errorf("Want err '%s' but got '%s'", want, err) + } + } +} + +func TestApply_DirectoryWithWrongObject(t *testing.T) { + + dir, clean := tempDir(t) + defer clean() + + writeFile(t, dir+"/dashboard.yml", `kind: Something +name: Test +`) + writeFile(t, dir+"/skipme.xml", `Some randome stuff`) + mkdir(t, dir+"/subfolder") + writeFile(t, dir+"/subfolder/filter.yaml", `kind: Filter +name: Test +`) + + mockDashboardService := &MockService{} + mockFilterService := &MockService{ + GetByNameM: func(project, name string) (Object, error) { + testEqual(t, project, "test_project") + testEqual(t, name, "Test") + return &Filter{ + Kind: FilterKind, + Name: "Test", + }, nil + }, + } + r := NewReportPortal(nil) + r.Dashboard = mockDashboardService + r.Filter = mockFilterService + + err := r.Apply("test_project", dir, true) + if err == nil { + t.Errorf("Want err but got nil") + } else { + want := "error applying one or more objects" + if err.Error() != want { + t.Errorf("Want err '%s' but got '%s'", want, err) + } + } + + testDeepEqual(t, mockDashboardService.Counter, MockServiceCounter{}) + testDeepEqual(t, mockFilterService.Counter, MockServiceCounter{GetByName: 1}) +}