diff --git a/cli/backup/alertnotifications.go b/cli/backup/alertnotifications.go deleted file mode 100644 index ff4e1881..00000000 --- a/cli/backup/alertnotifications.go +++ /dev/null @@ -1,154 +0,0 @@ -package backup - -import ( - "context" - "fmt" - "github.com/bep/simplecobra" - "github.com/esnet/gdg/cli/support" - "github.com/esnet/gdg/internal/config" - "github.com/esnet/gdg/internal/service" - "github.com/jedib0t/go-pretty/v6/table" - "github.com/spf13/cobra" - "log/slog" -) - -func newAlertNotificationsCommand() simplecobra.Commander { - description := "Manage alert notification channels" - return &support.SimpleCommand{ - NameP: "alertnotifications", - Short: description, - Long: description, - WithCFunc: func(cmd *cobra.Command, r *support.RootCommand) { - cmd.Aliases = []string{"an", "alertnotifications"} - }, - CommandsList: []simplecobra.Commander{ - newListAlertNotificationsCmd(), - newDownloadAlertNotificationsCmd(), - newUploadAlertNotificationsCmd(), - newClearAlertNotificationsCmd(), - }, - RunFunc: func(ctx context.Context, cd *simplecobra.Commandeer, rootCmd *support.RootCommand, args []string) error { - return cd.CobraCommand.Help() - }, - } - -} - -func newClearAlertNotificationsCmd() simplecobra.Commander { - description := "delete all alert notification channels from grafana" - return &support.SimpleCommand{ - NameP: "clear", - Short: description, - Long: description, - WithCFunc: func(cmd *cobra.Command, r *support.RootCommand) { - cmd.Aliases = []string{"c"} - }, - RunFunc: func(ctx context.Context, cd *simplecobra.Commandeer, rootCmd *support.RootCommand, args []string) error { - slog.Warn("Alert Notifications will be deprecated as of Grafana 9.0, this API may no longer work soon") - rootCmd.TableObj.AppendHeader(table.Row{"type", "filename"}) - - slog.Info("Clearing all alert notification channels for context", - "context", config.Config().GetGDGConfig().GetContext()) - deleted := rootCmd.GrafanaSvc().DeleteAllAlertNotifications() - for _, item := range deleted { - rootCmd.TableObj.AppendRow(table.Row{"alertnotification", item}) - } - if len(deleted) == 0 { - slog.Info("No alert notification channels were found. 0 removed") - } else { - slog.Info("alert notification channels were deleted", "count", len(deleted)) - rootCmd.TableObj.Render() - } - return nil - }, - } -} - -func newUploadAlertNotificationsCmd() simplecobra.Commander { - description := "upload all alert notification channels to grafana" - return &support.SimpleCommand{ - NameP: "upload", - Short: description, - Long: description, - WithCFunc: func(cmd *cobra.Command, r *support.RootCommand) { - cmd.Aliases = []string{"u"} - }, - RunFunc: func(ctx context.Context, cd *simplecobra.Commandeer, rootCmd *support.RootCommand, args []string) error { - slog.Warn("Alert Notifications will be deprecated as of Grafana 9.0, this API may no longer work soon") - rootCmd.TableObj.AppendHeader(table.Row{"name", "id", "UID"}) - - slog.Info("Exporting alert notification channels for context", - "context", config.Config().GetGDGConfig().GetContext()) - rootCmd.GrafanaSvc().UploadAlertNotifications() - items := rootCmd.GrafanaSvc().ListAlertNotifications() - for _, item := range items { - rootCmd.TableObj.AppendRow(table.Row{item.Name, item.ID, item.UID}) - } - if len(items) > 0 { - rootCmd.TableObj.Render() - } else { - slog.Info("No alert notification channels found") - } - return nil - }, - } -} - -func newDownloadAlertNotificationsCmd() simplecobra.Commander { - description := "download all alert notification channels from grafana to local filesystem" - return &support.SimpleCommand{ - NameP: "download", - Short: description, - Long: description, - WithCFunc: func(cmd *cobra.Command, r *support.RootCommand) { - cmd.Aliases = []string{"d"} - }, - RunFunc: func(ctx context.Context, cd *simplecobra.Commandeer, rootCmd *support.RootCommand, args []string) error { - slog.Warn("Alert Notifications will be deprecated as of Grafana 9.0, this API may no longer work soon") - rootCmd.TableObj.AppendHeader(table.Row{"type", "filename"}) - - slog.Info("Downloading alert notification channels for context", - "context", config.Config().GetGDGConfig().GetContext()) - - savedFiles := rootCmd.GrafanaSvc().DownloadAlertNotifications() - for _, file := range savedFiles { - rootCmd.TableObj.AppendRow(table.Row{"alertnotification", file}) - } - rootCmd.TableObj.Render() - return nil - }, - } -} - -func newListAlertNotificationsCmd() simplecobra.Commander { - description := "List all alert notification channels from grafana" - return &support.SimpleCommand{ - NameP: "list", - Short: description, - Long: description, - WithCFunc: func(cmd *cobra.Command, r *support.RootCommand) { - cmd.Aliases = []string{"l"} - }, - RunFunc: func(ctx context.Context, cd *simplecobra.Commandeer, rootCmd *support.RootCommand, args []string) error { - slog.Warn("Alert Notifications will be deprecated as of Grafana 9.0, this API may no longer work soon") - - rootCmd.TableObj.AppendHeader(table.Row{"id", "name", "slug", "type", "default", "url"}) - alertnotifications := rootCmd.GrafanaSvc().ListAlertNotifications() - - slog.Info("Listing alert notifications channels for context", - "context", config.Config().GetGDGConfig().GetContext()) - - if len(alertnotifications) == 0 { - slog.Info("No alert notifications found") - } else { - for _, link := range alertnotifications { - url := fmt.Sprintf("%s/alerting/notification/%d/edit", config.Config().GetDefaultGrafanaConfig().URL, link.ID) - rootCmd.TableObj.AppendRow(table.Row{link.ID, link.Name, service.GetSlug(link.Name), link.Type, link.IsDefault, url}) - } - rootCmd.TableObj.Render() - } - - return nil - }, - } -} diff --git a/cli/backup/backup.go b/cli/backup/backup.go index 2a740cca..b74ae0fe 100644 --- a/cli/backup/backup.go +++ b/cli/backup/backup.go @@ -4,6 +4,7 @@ import ( "context" "github.com/bep/simplecobra" "github.com/esnet/gdg/cli/support" + "github.com/esnet/gdg/internal/config" "github.com/spf13/cobra" ) @@ -27,7 +28,6 @@ limited to clear/delete, list, download and upload. Any other functionality wil }, CommandsList: []simplecobra.Commander{ newDashboardCommand(), - newAlertNotificationsCommand(), newConnectionsCommand(), newFolderCommand(), newLibraryElementsCommand(), @@ -38,3 +38,13 @@ limited to clear/delete, list, download and upload. Any other functionality wil } } + +// GetOrganizationName wrapper for verbose version below. +func GetOrganizationName() string { + return config.Config().GetDefaultGrafanaConfig().GetOrganizationName() +} + +// GetContext wrapper for verbose version below. +func GetContext() string { + return config.Config().GetGDGConfig().GetContext() +} diff --git a/cli/backup/connection_permissions.go b/cli/backup/connection_permissions.go index 54521491..00c65e4b 100644 --- a/cli/backup/connection_permissions.go +++ b/cli/backup/connection_permissions.go @@ -113,7 +113,7 @@ func newConnectionsPermissionDownloadCmd() simplecobra.Commander { cmd.Aliases = []string{"d"} }, RunFunc: func(ctx context.Context, cd *simplecobra.Commandeer, rootCmd *support.RootCommand, args []string) error { - slog.Info("import Connections for context", + slog.Info("Download Connections for context", "context", config.Config().GetGDGConfig().GetContext()) rootCmd.TableObj.AppendHeader(table.Row{"filename"}) connectionFilter, _ := cd.CobraCommand.Flags().GetString("connection") diff --git a/cli/backup/connections.go b/cli/backup/connections.go index e21b51f5..96ca1978 100644 --- a/cli/backup/connections.go +++ b/cli/backup/connections.go @@ -47,7 +47,7 @@ func newClearConnectionsCmd() simplecobra.Commander { cmd.Aliases = []string{"c"} }, RunFunc: func(ctx context.Context, cd *simplecobra.Commandeer, rootCmd *support.RootCommand, args []string) error { - slog.Info("Delete connections") + slog.Info("Delete connections", slog.String("Organization", GetOrganizationName())) dashboardFilter, _ := cd.CobraCommand.Flags().GetString("datasource") filters := service.NewConnectionFilter(dashboardFilter) savedFiles := rootCmd.GrafanaSvc().DeleteAllConnections(filters) @@ -71,7 +71,7 @@ func newUploadConnectionsCmd() simplecobra.Commander { cmd.Aliases = []string{"u"} }, RunFunc: func(ctx context.Context, cd *simplecobra.Commandeer, rootCmd *support.RootCommand, args []string) error { - slog.Info("Uploading connections") + slog.Info("Uploading connections", slog.String("Organization", GetOrganizationName())) dashboardFilter, _ := cd.CobraCommand.Flags().GetString("connection") filters := service.NewConnectionFilter(dashboardFilter) exportedList := rootCmd.GrafanaSvc().UploadConnections(filters) @@ -96,6 +96,7 @@ func newDownloadConnectionsCmd() simplecobra.Commander { }, RunFunc: func(ctx context.Context, cd *simplecobra.Commandeer, rootCmd *support.RootCommand, args []string) error { slog.Info("Importing connections for context", + slog.String("Organization", GetOrganizationName()), "context", config.Config().GetGDGConfig().GetContext()) dashboardFilter, _ := cd.CobraCommand.Flags().GetString("connection") filters := service.NewConnectionFilter(dashboardFilter) @@ -123,7 +124,9 @@ func newListConnectionsCmd() simplecobra.Commander { dashboardFilter, _ := cd.CobraCommand.Flags().GetString("connection") filters := service.NewConnectionFilter(dashboardFilter) dsListing := rootCmd.GrafanaSvc().ListConnections(filters) - slog.Info("Listing connections for context", "context", config.Config().GetGDGConfig().GetContext()) + slog.Info("Listing connections for context", + slog.String("Organization", GetOrganizationName()), + slog.String("context", GetContext())) if len(dsListing) == 0 { slog.Info("No connections found") } else { diff --git a/cli/backup/dashboard.go b/cli/backup/dashboard.go index 3ba4019a..3bec0ceb 100644 --- a/cli/backup/dashboard.go +++ b/cli/backup/dashboard.go @@ -40,11 +40,10 @@ func newDashboardCommand() simplecobra.Commander { Long: description, WithCFunc: func(cmd *cobra.Command, r *support.RootCommand) { cmd.Aliases = []string{"dash", "dashboard"} - dashboard := cmd - dashboard.PersistentFlags().BoolVarP(&skipConfirmAction, "skip-confirmation", "", false, "when set to true, bypass confirmation prompts") - dashboard.PersistentFlags().StringP("dashboard", "d", "", "filter by dashboard slug") - dashboard.PersistentFlags().StringP("folder", "f", "", "Filter by Folder Name (Quotes in names not supported)") - dashboard.PersistentFlags().StringArrayP("tags", "t", []string{}, "Filter by list of comma delimited tags") + cmd.PersistentFlags().BoolVarP(&skipConfirmAction, "skip-confirmation", "", false, "when set to true, bypass confirmation prompts") + cmd.PersistentFlags().StringP("dashboard", "d", "", "filter by dashboard slug") + cmd.PersistentFlags().StringP("folder", "f", "", "Filter by Folder Name (Quotes in names not supported)") + cmd.PersistentFlags().StringArrayP("tags", "t", []string{}, "Filter by list of comma delimited tags") }, CommandsList: []simplecobra.Commander{ newListDashboardsCmd(), @@ -110,7 +109,9 @@ func newUploadDashboardsCmd() simplecobra.Commander { rootCmd.TableObj.AppendHeader(table.Row{"Title", "id", "folder", "UID"}) boards := rootCmd.GrafanaSvc().ListDashboards(filter) - slog.Info(fmt.Sprintf("%d dashboards have been uploaded", len(boards))) + slog.Info("dashboards have been uploaded", slog.Any("count", len(boards)), + slog.String("context", GetContext()), + slog.String("Organization", GetOrganizationName())) for _, link := range boards { rootCmd.TableObj.AppendRow(table.Row{link.Title, link.ID, link.FolderTitle, link.UID}) } @@ -138,7 +139,8 @@ func newDownloadDashboardsCmd() simplecobra.Commander { filter := service.NewDashboardFilter(parseDashboardGlobalFlags(cd.CobraCommand)...) savedFiles := rootCmd.GrafanaSvc().DownloadDashboards(filter) slog.Info("Downloading dashboards for context", - "context", config.Config().GetGDGConfig().GetContext()) + slog.String("Organization", GetOrganizationName()), + "context", GetContext()) rootCmd.TableObj.AppendHeader(table.Row{"type", "filename"}) for _, file := range savedFiles { rootCmd.TableObj.AppendRow(table.Row{"dashboard", file}) @@ -165,7 +167,10 @@ func newListDashboardsCmd() simplecobra.Commander { filters := service.NewDashboardFilter(parseDashboardGlobalFlags(cd.CobraCommand)...) boards := rootCmd.GrafanaSvc().ListDashboards(filters) - slog.Info("Listing dashboards for context", slog.String("context", config.Config().GetGDGConfig().GetContext()), slog.Any("count", len(boards))) + slog.Info("Listing dashboards for context", + slog.String("context", GetContext()), + slog.String("orgName", GetOrganizationName()), + slog.Any("count", len(boards))) for _, link := range boards { base, err := url.Parse(config.Config().GetDefaultGrafanaConfig().URL) var baseHost string diff --git a/cli/backup/organizations.go b/cli/backup/organizations.go index 581ecd33..56e19ccf 100644 --- a/cli/backup/organizations.go +++ b/cli/backup/organizations.go @@ -5,12 +5,19 @@ import ( "github.com/bep/simplecobra" "github.com/esnet/gdg/cli/support" "github.com/esnet/gdg/internal/config" + "github.com/esnet/gdg/internal/service" + "github.com/gosimple/slug" "github.com/jedib0t/go-pretty/v6/table" "github.com/spf13/cobra" "log/slog" "sort" ) +func parseOrganizationGlobalFlags(command *cobra.Command) []string { + orgName, _ := command.Flags().GetString("org-name") + return []string{orgName} +} + func newOrganizationsCommand() simplecobra.Commander { description := "Manage Grafana Organizations." return &support.SimpleCommand{ @@ -19,6 +26,12 @@ func newOrganizationsCommand() simplecobra.Commander { Long: description, WithCFunc: func(cmd *cobra.Command, r *support.RootCommand) { cmd.Aliases = []string{"org", "orgs"} + cmd.PersistentFlags().StringP("org-name", "o", "", "when set to true, bypass confirmation prompts") + }, + + InitCFunc: func(cd *simplecobra.Commandeer, r *support.RootCommand) error { + r.GrafanaSvc().InitOrganizations() + return nil }, RunFunc: func(ctx context.Context, cd *simplecobra.Commandeer, rootCmd *support.RootCommand, args []string) error { return cd.CobraCommand.Help() @@ -42,9 +55,10 @@ func newOrganizationsListCmd() simplecobra.Commander { cmd.Aliases = []string{"l"} }, RunFunc: func(ctx context.Context, cd *simplecobra.Commandeer, rootCmd *support.RootCommand, args []string) error { + filter := service.NewOrganizationFilter(parseOrganizationGlobalFlags(cd.CobraCommand)...) slog.Info("Listing organizations for context", "context", config.Config().GetGDGConfig().GetContext()) - rootCmd.TableObj.AppendHeader(table.Row{"id", "org"}) - listOrganizations := rootCmd.GrafanaSvc().ListOrganizations() + rootCmd.TableObj.AppendHeader(table.Row{"id", "organization Name", "org slug ID"}) + listOrganizations := rootCmd.GrafanaSvc().ListOrganizations(filter) sort.Slice(listOrganizations, func(a, b int) bool { return listOrganizations[a].ID < listOrganizations[b].ID }) @@ -52,7 +66,7 @@ func newOrganizationsListCmd() simplecobra.Commander { slog.Info("No organizations found") } else { for _, org := range listOrganizations { - rootCmd.TableObj.AppendRow(table.Row{org.ID, org.Name}) + rootCmd.TableObj.AppendRow(table.Row{org.ID, org.Name, slug.Make(org.Name)}) } rootCmd.TableObj.Render() } @@ -73,7 +87,8 @@ func newOrganizationsDownloadCmd() simplecobra.Commander { RunFunc: func(ctx context.Context, cd *simplecobra.Commandeer, rootCmd *support.RootCommand, args []string) error { slog.Info("Downloading organizations for context", "context", config.Config().GetGDGConfig().GetContext()) rootCmd.TableObj.AppendHeader(table.Row{"file"}) - listOrganizations := rootCmd.GrafanaSvc().DownloadOrganizations() + filter := service.NewOrganizationFilter(parseOrganizationGlobalFlags(cd.CobraCommand)...) + listOrganizations := rootCmd.GrafanaSvc().DownloadOrganizations(filter) if len(listOrganizations) == 0 { slog.Info("No organizations found") } else { @@ -97,9 +112,10 @@ func newOrganizationsUploadCmd() simplecobra.Commander { cmd.Aliases = []string{"u"} }, RunFunc: func(ctx context.Context, cd *simplecobra.Commandeer, rootCmd *support.RootCommand, args []string) error { - slog.Info("Uploading Folders for context: '%s'", "context", config.Config().GetGDGConfig().GetContext()) + slog.Info("Uploading Folders for context: ", "context", config.Config().GetGDGConfig().GetContext()) rootCmd.TableObj.AppendHeader(table.Row{"file"}) - folders := rootCmd.GrafanaSvc().UploadOrganizations() + filter := service.NewOrganizationFilter(parseOrganizationGlobalFlags(cd.CobraCommand)...) + folders := rootCmd.GrafanaSvc().UploadOrganizations(filter) if len(folders) == 0 { slog.Info("No Orgs were uploaded") } else { diff --git a/cli/tools/organizations.go b/cli/tools/organizations.go index eeba60d1..294a46c5 100644 --- a/cli/tools/organizations.go +++ b/cli/tools/organizations.go @@ -43,22 +43,32 @@ func newOrgCommand() simplecobra.Commander { func newSetOrgCmd() simplecobra.Commander { return &support.SimpleCommand{ NameP: "set", - Short: "Set , 0 removes filter", - Long: "Set , 0 removes filter", + Short: "Set --orgId --orgName to set user Org", + Long: "Set --orgId --orgName to set user Org", + WithCFunc: func(cmd *cobra.Command, r *support.RootCommand) { + cmd.PersistentFlags().StringP("orgName", "o", "", "Set user Org by Name (not slug)") + cmd.PersistentFlags().StringP("orgSlugName", "", "", "Set user Org by slug name") + + }, RunFunc: func(ctx context.Context, cd *simplecobra.Commandeer, rootCmd *support.RootCommand, args []string) error { - if len(args) < 1 { - return errors.New("requires an Org ID and name") - } - OrgId := args[0] - orgId, err := strconv.ParseInt(OrgId, 10, 64) - if err != nil { - log.Fatal("invalid Org ID, could not parse value to a numeric value") - } - err = rootCmd.GrafanaSvc().SetOrganization(orgId) - if err != nil { - log.Fatal("unable to set Org ID", "err", err) + orgName, _ := cd.CobraCommand.Flags().GetString("orgName") + slugName, _ := cd.CobraCommand.Flags().GetString("orgSlugName") + if orgName != "" || slugName != "" { + var useSlug = false + if slugName != "" { + useSlug = true + orgName = slugName + } + err := rootCmd.GrafanaSvc().SetOrganizationByName(orgName, useSlug) + if err != nil { + log.Fatal("unable to set Org ID, ", err.Error()) + } } - slog.Info("Successfully set Org ID for context", "context", config.Config().GetGDGConfig().GetContext()) + + rootCmd.GrafanaSvc().InitOrganizations() + userOrg := rootCmd.GrafanaSvc().GetUserOrganization() + slog.Info("New Org is now set to", slog.String("orgName", userOrg.Name)) + return nil }, diff --git a/config/importer-example.yml b/config/importer-example.yml index eda705b2..593acaee 100644 --- a/config/importer-example.yml +++ b/config/importer-example.yml @@ -75,7 +75,6 @@ contexts: url: https://grafana.com user_name: admin password: admin - organization_id: 1 filter_override: ignore_dashboard_filters: false # When set to true all Watched filtered folders will be ignored and ALL folders will be acted on watched: @@ -95,14 +94,13 @@ contexts: regex: ".*" secure_data: "default.json" url: https://staging.grafana.com - organization_id: 1 filter_override: ignore_dashboard_filters: false # When set to true all Watched filtered folders will be ignored and ALL folders will be acted on watched: - Folder1 - Folder2 watched_folders_override: - - organization_id: 0 + - organization_name: "Some Other Org" folders: - General - SpecialFolder diff --git a/config/templates-example.yml b/config/templates-example.yml index 309bc988..e530fe0f 100644 --- a/config/templates-example.yml +++ b/config/templates-example.yml @@ -3,7 +3,7 @@ entities: - template_name: template_example output: - folder: "General" - org_id: 2 + organization_name: "Main Org." dashboard_name: "Testing Foobar" template_data: Title: Bob Loves Candy @@ -14,7 +14,7 @@ entities: - lightbulb - office lights - folder: "Testing" - org_id: 3 + organization_name: Some Other Org dashboard_name: "" template_data: Title: Uncle McDonalds diff --git a/config/testing.yml b/config/testing.yml index c61d38d1..22808194 100644 --- a/config/testing.yml +++ b/config/testing.yml @@ -40,7 +40,7 @@ contexts: url: https://grafana.com user_name: admin password: admin - organization: your-org + organization_name: Your Org ignore_filters: False # When set to true all Watched filtered folders will be ignored and ALL folders will be acted on watched: - General @@ -63,7 +63,7 @@ contexts: regex: ".*" secure_data: "default.json" url: https://staging.grafana.com - organization: your-org + organization_name: Your Org ignore_filters: False # When set to true all Watched filtered folders will be ignored and ALL folders will be acted on watched: - Folder1 diff --git a/internal/api/extended.go b/internal/api/extended.go index b48bbcca..23c57ed4 100644 --- a/internal/api/extended.go +++ b/internal/api/extended.go @@ -18,7 +18,6 @@ func NewExtendedApi() *ExtendedApi { o := ExtendedApi{ grafanaCfg: cfg, } - return &o } diff --git a/internal/api/orgs.go b/internal/api/orgs.go new file mode 100644 index 00000000..8054a027 --- /dev/null +++ b/internal/api/orgs.go @@ -0,0 +1,28 @@ +package api + +import ( + "context" + "errors" + "github.com/grafana/grafana-openapi-client-go/models" + "net/http" +) + +// GetConfiguredOrgId needed to call grafana API in order to configure the Grafana API correctly. Invoking +// this endpoint manually to avoid a circular dependency. +func (extended *ExtendedApi) GetConfiguredOrgId(orgName string) (int64, error) { + var result []*models.UserOrgDTO + err := extended.getRequestBuilder(). + Path("api/user/orgs"). + ToJSON(&result). + Method(http.MethodGet). + Fetch(context.Background()) + if err != nil { + return 0, err + } + for _, entity := range result { + if entity.Name == orgName { + return entity.OrgID, nil + } + } + return 0, errors.New("org not found") +} diff --git a/internal/config/config_model.go b/internal/config/config_model.go index ee82a9db..966f718d 100644 --- a/internal/config/config_model.go +++ b/internal/config/config_model.go @@ -4,11 +4,10 @@ import ( "encoding/json" "errors" "fmt" + "github.com/gosimple/slug" "github.com/grafana/grafana-openapi-client-go/models" "github.com/tidwall/gjson" - "log" "log/slog" - "os" "path" "regexp" ) @@ -16,10 +15,8 @@ import ( type ResourceType string const ( - AlertNotificationResource = "alertnotifications" ConnectionPermissionResource = "connections-permissions" ConnectionResource = "connections" - LegacyConnections = "datasources" DashboardResource = "dashboards" FolderPermissionResource = "folders-permissions" FolderResource = "folders" @@ -33,7 +30,6 @@ const ( ) var orgNamespacedResource = map[ResourceType]bool{ - AlertNotificationResource: true, ConnectionPermissionResource: true, ConnectionResource: true, DashboardResource: true, @@ -56,8 +52,8 @@ func (s *ResourceType) String() string { // GetPath returns the path of the resource type, if Namespaced, will delimit the path by org Id func (s *ResourceType) GetPath(basePath string) string { if s.isNamespaced() { - orgId := Config().GetDefaultGrafanaConfig().GetOrganizationId() - return path.Join(basePath, fmt.Sprintf("%s_%d", OrganizationMetaResource, orgId), s.String()) + orgName := slug.Make(Config().GetDefaultGrafanaConfig().GetOrganizationName()) + return path.Join(basePath, fmt.Sprintf("%s_%s", OrganizationMetaResource, orgName), s.String()) } return path.Join(basePath, s.String()) @@ -167,9 +163,9 @@ func (s *GrafanaConfig) GetPath(r ResourceType) string { } // GetOrgMonitoredFolders return the OrganizationMonitoredFolders that override a given Org -func (s *GrafanaConfig) GetOrgMonitoredFolders(orgId int64) []string { +func (s *GrafanaConfig) GetOrgMonitoredFolders(orgName string) []string { for _, item := range s.MonitoredFoldersOverride { - if item.OrganizationId == orgId && len(item.Folders) > 0 { + if item.OrganizationName == orgName && len(item.Folders) > 0 { return item.Folders } } @@ -179,7 +175,7 @@ func (s *GrafanaConfig) GetOrgMonitoredFolders(orgId int64) []string { // GetMonitoredFolders return a list of the monitored folders alternatively returns the "General" folder. func (s *GrafanaConfig) GetMonitoredFolders() []string { - orgFolders := s.GetOrgMonitoredFolders(s.OrganizationId) + orgFolders := s.GetOrgMonitoredFolders(s.GetOrganizationName()) if len(orgFolders) > 0 { return orgFolders } @@ -192,16 +188,6 @@ func (s *GrafanaConfig) GetMonitoredFolders() []string { // Validate will return terminate if any deprecated configuration is found. func (s *GrafanaConfig) Validate() { - if len(s.LegacyConnectionSettings) > 0 { - log.Fatal("Using 'datasources' is now deprecated, please use 'connections' instead") - } - //Validate Connections - //TODO: remove code after next release - legacyCheck := s.GetPath(LegacyConnections) - if _, err := os.Stat(legacyCheck); !os.IsNotExist(err) { - log.Fatalf("Your export contains a datasource directry which is deprecated. Please remove or "+ - "rename directory to '%s'", ConnectionResource) - } } diff --git a/internal/config/config_test.go b/internal/config/config_test.go index 8cfc89c2..78a14c85 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -83,16 +83,16 @@ func TestWatchedFoldersConfig(t *testing.T) { grafanaConf := config.Config().GetDefaultGrafanaConfig() assert.NotNil(t, grafanaConf) grafanaConf.MonitoredFoldersOverride = []config.MonitoredOrgFolders{{ - OrganizationId: 0, - Folders: []string{"General", "SpecialFolder"}, + OrganizationName: "Your Org", + Folders: []string{"General", "SpecialFolder"}, }} folders := grafanaConf.GetMonitoredFolders() assert.True(t, slices.Contains(folders, "SpecialFolder")) - grafanaConf.OrganizationId = 2 + grafanaConf.OrganizationName = "DumbDumb" folders = grafanaConf.GetMonitoredFolders() assert.False(t, slices.Contains(folders, "SpecialFolder")) assert.True(t, slices.Contains(folders, "Folder2")) - grafanaConf.OrganizationId = 0 + grafanaConf.OrganizationName = "Main Org." grafanaConf.MonitoredFoldersOverride = nil folders = grafanaConf.GetMonitoredFolders() assert.False(t, slices.Contains(folders, "SpecialFolder")) @@ -140,8 +140,8 @@ func validateGrafanaQA(t *testing.T, grafana *config.GrafanaConfig) { folders := grafana.GetMonitoredFolders() assert.True(t, funk.Contains(folders, "Folder1")) assert.True(t, funk.Contains(folders, "Folder2")) - assert.Equal(t, "test/data/org_1/connections", grafana.GetPath(config.ConnectionResource)) - assert.Equal(t, "test/data/org_1/dashboards", grafana.GetPath(config.DashboardResource)) + assert.Equal(t, "test/data/org_your-org/connections", grafana.GetPath(config.ConnectionResource)) + assert.Equal(t, "test/data/org_your-org/dashboards", grafana.GetPath(config.DashboardResource)) dsSettings := grafana.ConnectionSettings request := models.AddDataSourceCommand{} assert.Equal(t, len(grafana.ConnectionSettings.MatchingRules), 3) diff --git a/internal/config/types.go b/internal/config/types.go index 4af44256..dc57bbb2 100644 --- a/internal/config/types.go +++ b/internal/config/types.go @@ -10,8 +10,10 @@ import ( ) const ( - ViperGdgConfig = "gdg" - ViperTemplateConfig = "template" + ViperGdgConfig = "gdg" + ViperTemplateConfig = "template" + DefaultOrganizationName = "Main Org." + DefaultOrganizationId = 1 ) type Configuration struct { @@ -34,10 +36,10 @@ type TemplateDashboards struct { } type TemplateDashboardEntity struct { - Folder string `mapstructure:"folder"` - OrgId int64 `mapstructure:"org_id"` - DashboardName string `mapstructure:"dashboard_name"` - TemplateData map[string]interface{} `mapstructure:"template_data"` + Folder string `mapstructure:"folder"` + OrganizationName string `mapstructure:"organization_name"` + DashboardName string `mapstructure:"dashboard_name"` + TemplateData map[string]interface{} `mapstructure:"template_data"` } // AppGlobals is the global configuration for the application @@ -63,27 +65,25 @@ type GrafanaConfig struct { APIToken string `mapstructure:"token" yaml:"token"` UserName string `mapstructure:"user_name" yaml:"user_name"` Password string `mapstructure:"password" yaml:"password"` - OrganizationId int64 `mapstructure:"organization_id" yaml:"organization_id"` + OrganizationName string `mapstructure:"organization_name" yaml:"organization_name"` MonitoredFoldersOverride []MonitoredOrgFolders `mapstructure:"watched_folders_override" yaml:"watched_folders_override"` MonitoredFolders []string `mapstructure:"watched" yaml:"watched"` ConnectionSettings *ConnectionSettings `mapstructure:"connections" yaml:"connections"` - //Datasources are deprecated, please use Connections - LegacyConnectionSettings map[string]interface{} `mapstructure:"datasources" yaml:"datasources"` - FilterOverrides *FilterOverrides `mapstructure:"filter_override" yaml:"filter_override"` - OutputPath string `mapstructure:"output_path" yaml:"output_path"` + FilterOverrides *FilterOverrides `mapstructure:"filter_override" yaml:"filter_override"` + OutputPath string `mapstructure:"output_path" yaml:"output_path"` } type MonitoredOrgFolders struct { - OrganizationId int64 `json:"organization_id" yaml:"organization_id"` - Folders []string `json:"folders" yaml:"folders"` + OrganizationName string `json:"organization_name" yaml:"organization_name"` + Folders []string `json:"folders" yaml:"folders"` } -// GetOrganizationId returns the id of the organization (defaults to 1 if unset) -func (s *GrafanaConfig) GetOrganizationId() int64 { - if s.OrganizationId > 1 { - return s.OrganizationId +// GetOrganizationName returns the id of the organization (defaults to 1 if unset) +func (s *GrafanaConfig) GetOrganizationName() string { + if s.OrganizationName != "" { + return s.OrganizationName } - return 1 + return DefaultOrganizationName } // SetAdmin sets true if user has admin permissions diff --git a/internal/service/alertnotifications.go b/internal/service/alertnotifications.go deleted file mode 100644 index b3f31b4f..00000000 --- a/internal/service/alertnotifications.go +++ /dev/null @@ -1,121 +0,0 @@ -package service - -import ( - "encoding/json" - "github.com/esnet/gdg/internal/config" - "github.com/grafana/grafana-openapi-client-go/models" - "log/slog" - "strings" - - "github.com/gosimple/slug" - "log" -) - -// AlertNotificationsApi Contract definition -// Deprecated: Marked as Deprecated as of Grafana 9.0, Moving to ContactPoints is recommended -type AlertNotificationsApi interface { - ListAlertNotifications() []*models.AlertNotification - DownloadAlertNotifications() []string - UploadAlertNotifications() []string - DeleteAllAlertNotifications() []string -} - -//ListAlertNotifications: list all currently configured notification channels - -func (s *DashNGoImpl) ListAlertNotifications() []*models.AlertNotification { - channels, err := s.GetClient().LegacyAlertsNotificationChannels.GetAlertNotificationChannels() - if err != nil { - log.Panic(err) - } - return channels.Payload -} - -// ImportAlertNotifications: will read in all the configured alert notification channels. -func (s *DashNGoImpl) DownloadAlertNotifications() []string { - var ( - alertnotifications []*models.AlertNotification - anPacked []byte - err error - dataFiles []string - ) - alertnotifications = s.ListAlertNotifications() - for _, an := range alertnotifications { - if anPacked, err = json.Marshal(an); err != nil { - slog.Error("error marshalling to json", "filename", an.Name, "err", err.Error()) - continue - } - anPath := buildResourcePath(slug.Make(an.Name), config.AlertNotificationResource) - if err = s.storage.WriteFile(anPath, anPacked); err != nil { - slog.Error("error writing to file", "filename", slug.Make(an.Name), "err", err.Error()) - } else { - dataFiles = append(dataFiles, anPath) - } - } - return dataFiles -} - -// Removes all current alert notification channels -func (s *DashNGoImpl) DeleteAllAlertNotifications() []string { - var an = make([]string, 0) - items := s.ListAlertNotifications() - for _, item := range items { - _, err := s.GetClient().LegacyAlertsNotificationChannels.DeleteAlertNotificationChannel(item.ID) - if err != nil { - slog.Error("Failed to delete notification") - continue - } - an = append(an, item.Name) - } - return an -} - -// ExportAlertNotifications: exports all alert notification channels to grafana. -// NOTE: credentials will be missing and need to be set manually after export -// TODO implement configuring sensitive fields for different kinds of alert notification channels -func (s *DashNGoImpl) UploadAlertNotifications() []string { - var ( - alertnotifications []*models.AlertNotification - exported []string - filesInDir []string - err error - ) - - dirPath := config.Config().GetDefaultGrafanaConfig().GetPath(config.AlertNotificationResource) - filesInDir, err = s.storage.FindAllFiles(dirPath, true) - if err != nil { - log.Fatalf("Unable to find Alert data in Storage System %s, err: %s", s.storage.Name(), err.Error()) - } - alertnotifications = s.ListAlertNotifications() - - var raw []byte - for _, file := range filesInDir { - if strings.HasSuffix(file, ".json") { - if raw, err = s.storage.ReadFile(file); err != nil { - slog.Error("error reading file", "file", file, "err", err) - continue - } - - var newAlertNotification models.CreateAlertNotificationCommand - if err = json.Unmarshal(raw, &newAlertNotification); err != nil { - slog.Error("error unmarshalling json", "err", err) - continue - } - - for _, existing := range alertnotifications { - if existing.Name == newAlertNotification.Name { - if _, err := s.GetClient().LegacyAlertsNotificationChannels.DeleteAlertNotificationChannelByUID(existing.UID); err != nil { - slog.Error("error on deleting datasource", "datasource", newAlertNotification.Name, "err", err) - } - break - } - } - - if _, err = s.GetClient().LegacyAlertsNotificationChannels.CreateAlertNotificationChannel(&newAlertNotification); err != nil { - slog.Error("error on importing datasource", "datasource", newAlertNotification.Name, "err", err) - continue - } - exported = append(exported, file) - } - } - return exported -} diff --git a/internal/service/common_test.go b/internal/service/common_test.go index ecae0cb3..ae4dab55 100644 --- a/internal/service/common_test.go +++ b/internal/service/common_test.go @@ -45,21 +45,16 @@ func TestUserPath(t *testing.T) { } func TestBuildDashboardPath(t *testing.T) { result := BuildResourceFolder("General", config.DashboardResource) - assert.Equal(t, "test/data/org_1/dashboards/General", result) + assert.Equal(t, "test/data/org_your-org/dashboards/General", result) } func TestBuildFolderSourcePath(t *testing.T) { result := buildResourcePath(slug.Make("Some Folder"), config.FolderResource) - assert.Equal(t, "test/data/org_1/folders/some-folder.json", result) + assert.Equal(t, "test/data/org_your-org/folders/some-folder.json", result) } func TestBuildDataSourcePath(t *testing.T) { result := buildResourcePath(slug.Make("My DS"), config.ConnectionResource) - assert.Equal(t, "test/data/org_1/connections/my-ds.json", result) -} - -func TestBuildAlertNotificationPath(t *testing.T) { - result := buildResourcePath("SomeNotification", config.AlertNotificationResource) - assert.Equal(t, "test/data/org_1/alertnotifications/SomeNotification.json", result) + assert.Equal(t, "test/data/org_your-org/connections/my-ds.json", result) } diff --git a/internal/service/connections.go b/internal/service/connections.go index 29e333f4..91e7dcc1 100644 --- a/internal/service/connections.go +++ b/internal/service/connections.go @@ -43,9 +43,9 @@ func NewConnectionFilter(name string) filters.Filter { // ListConnections list all the currently configured datasources func (s *DashNGoImpl) ListConnections(filter filters.Filter) []models.DataSourceListItemDTO { - err := s.SwitchOrganization(s.grafanaConf.GetOrganizationId()) + err := s.SwitchOrganizationByName(s.grafanaConf.GetOrganizationName()) if err != nil { - log.Fatalf("Failed to switch organization ID %d: ", s.grafanaConf.OrganizationId) + log.Fatalf("Failed to switch organization ID %s: ", s.grafanaConf.OrganizationName) } ds, err := s.GetClient().Datasources.GetDataSources() diff --git a/internal/service/contract.go b/internal/service/contract.go index 9b5e094d..3928e7bb 100644 --- a/internal/service/contract.go +++ b/internal/service/contract.go @@ -13,7 +13,6 @@ type GrafanaService interface { OrganizationsApi DashboardsApi ConnectionsApi - AlertNotificationsApi UsersApi FoldersApi LibraryElementsApi diff --git a/internal/service/filters/filters.go b/internal/service/filters/filters.go index e8f64b28..9b4baf23 100644 --- a/internal/service/filters/filters.go +++ b/internal/service/filters/filters.go @@ -17,10 +17,10 @@ const ( TagsFilter FilterType = "TagsFilter" DashFilter FilterType = "DashFilter" FolderFilter FilterType = "FolderFilter" - TeamFilter FilterType = "TeamFilter" DefaultFilter FilterType = "default" Name FilterType = "Name" AuthLabel FilterType = "AuthLabel" + OrgFilter FilterType = "OrgFilter" ) func (s FilterType) String() string { diff --git a/internal/service/login.go b/internal/service/login.go index d469df00..f0058921 100644 --- a/internal/service/login.go +++ b/internal/service/login.go @@ -3,6 +3,7 @@ package service import ( "crypto/tls" "log" + "log/slog" "net/http" "net/url" @@ -56,10 +57,19 @@ func (s *DashNGoImpl) getNewClient(opts ...NewClientOpts) (*client.GrafanaHTTPAP Schemes: []string{u.Scheme}, // NumRetries: 3, } - // Sets Organization one client if one is configured - if s.grafanaConf.OrganizationId != 0 { + + if s.grafanaConf.OrganizationName != "" { + orgId, err := api.NewExtendedApi().GetConfiguredOrgId(s.grafanaConf.OrganizationName) + if err != nil { + slog.Info("unable to determine org ID, falling back") + orgId = 1 + } + opts = append(opts, func(clientCfg *client.TransportConfig) { + clientCfg.OrgID = orgId + }) + } else { opts = append(opts, func(clientCfg *client.TransportConfig) { - clientCfg.OrgID = s.grafanaConf.OrganizationId + clientCfg.OrgID = config.DefaultOrganizationId }) } for _, opt := range opts { diff --git a/internal/service/mocks/AlertNotificationsApi.go b/internal/service/mocks/AlertNotificationsApi.go deleted file mode 100644 index aba1e429..00000000 --- a/internal/service/mocks/AlertNotificationsApi.go +++ /dev/null @@ -1,207 +0,0 @@ -// Code generated by mockery v2.36.0. DO NOT EDIT. - -package mocks - -import ( - models "github.com/grafana/grafana-openapi-client-go/models" - mock "github.com/stretchr/testify/mock" -) - -// AlertNotificationsApi is an autogenerated mock type for the AlertNotificationsApi type -type AlertNotificationsApi struct { - mock.Mock -} - -type AlertNotificationsApi_Expecter struct { - mock *mock.Mock -} - -func (_m *AlertNotificationsApi) EXPECT() *AlertNotificationsApi_Expecter { - return &AlertNotificationsApi_Expecter{mock: &_m.Mock} -} - -// DeleteAllAlertNotifications provides a mock function with given fields: -func (_m *AlertNotificationsApi) DeleteAllAlertNotifications() []string { - ret := _m.Called() - - var r0 []string - if rf, ok := ret.Get(0).(func() []string); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]string) - } - } - - return r0 -} - -// AlertNotificationsApi_DeleteAllAlertNotifications_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DeleteAllAlertNotifications' -type AlertNotificationsApi_DeleteAllAlertNotifications_Call struct { - *mock.Call -} - -// DeleteAllAlertNotifications is a helper method to define mock.On call -func (_e *AlertNotificationsApi_Expecter) DeleteAllAlertNotifications() *AlertNotificationsApi_DeleteAllAlertNotifications_Call { - return &AlertNotificationsApi_DeleteAllAlertNotifications_Call{Call: _e.mock.On("DeleteAllAlertNotifications")} -} - -func (_c *AlertNotificationsApi_DeleteAllAlertNotifications_Call) Run(run func()) *AlertNotificationsApi_DeleteAllAlertNotifications_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *AlertNotificationsApi_DeleteAllAlertNotifications_Call) Return(_a0 []string) *AlertNotificationsApi_DeleteAllAlertNotifications_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *AlertNotificationsApi_DeleteAllAlertNotifications_Call) RunAndReturn(run func() []string) *AlertNotificationsApi_DeleteAllAlertNotifications_Call { - _c.Call.Return(run) - return _c -} - -// DownloadAlertNotifications provides a mock function with given fields: -func (_m *AlertNotificationsApi) DownloadAlertNotifications() []string { - ret := _m.Called() - - var r0 []string - if rf, ok := ret.Get(0).(func() []string); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]string) - } - } - - return r0 -} - -// AlertNotificationsApi_DownloadAlertNotifications_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DownloadAlertNotifications' -type AlertNotificationsApi_DownloadAlertNotifications_Call struct { - *mock.Call -} - -// DownloadAlertNotifications is a helper method to define mock.On call -func (_e *AlertNotificationsApi_Expecter) DownloadAlertNotifications() *AlertNotificationsApi_DownloadAlertNotifications_Call { - return &AlertNotificationsApi_DownloadAlertNotifications_Call{Call: _e.mock.On("DownloadAlertNotifications")} -} - -func (_c *AlertNotificationsApi_DownloadAlertNotifications_Call) Run(run func()) *AlertNotificationsApi_DownloadAlertNotifications_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *AlertNotificationsApi_DownloadAlertNotifications_Call) Return(_a0 []string) *AlertNotificationsApi_DownloadAlertNotifications_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *AlertNotificationsApi_DownloadAlertNotifications_Call) RunAndReturn(run func() []string) *AlertNotificationsApi_DownloadAlertNotifications_Call { - _c.Call.Return(run) - return _c -} - -// ListAlertNotifications provides a mock function with given fields: -func (_m *AlertNotificationsApi) ListAlertNotifications() []*models.AlertNotification { - ret := _m.Called() - - var r0 []*models.AlertNotification - if rf, ok := ret.Get(0).(func() []*models.AlertNotification); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]*models.AlertNotification) - } - } - - return r0 -} - -// AlertNotificationsApi_ListAlertNotifications_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListAlertNotifications' -type AlertNotificationsApi_ListAlertNotifications_Call struct { - *mock.Call -} - -// ListAlertNotifications is a helper method to define mock.On call -func (_e *AlertNotificationsApi_Expecter) ListAlertNotifications() *AlertNotificationsApi_ListAlertNotifications_Call { - return &AlertNotificationsApi_ListAlertNotifications_Call{Call: _e.mock.On("ListAlertNotifications")} -} - -func (_c *AlertNotificationsApi_ListAlertNotifications_Call) Run(run func()) *AlertNotificationsApi_ListAlertNotifications_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *AlertNotificationsApi_ListAlertNotifications_Call) Return(_a0 []*models.AlertNotification) *AlertNotificationsApi_ListAlertNotifications_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *AlertNotificationsApi_ListAlertNotifications_Call) RunAndReturn(run func() []*models.AlertNotification) *AlertNotificationsApi_ListAlertNotifications_Call { - _c.Call.Return(run) - return _c -} - -// UploadAlertNotifications provides a mock function with given fields: -func (_m *AlertNotificationsApi) UploadAlertNotifications() []string { - ret := _m.Called() - - var r0 []string - if rf, ok := ret.Get(0).(func() []string); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]string) - } - } - - return r0 -} - -// AlertNotificationsApi_UploadAlertNotifications_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UploadAlertNotifications' -type AlertNotificationsApi_UploadAlertNotifications_Call struct { - *mock.Call -} - -// UploadAlertNotifications is a helper method to define mock.On call -func (_e *AlertNotificationsApi_Expecter) UploadAlertNotifications() *AlertNotificationsApi_UploadAlertNotifications_Call { - return &AlertNotificationsApi_UploadAlertNotifications_Call{Call: _e.mock.On("UploadAlertNotifications")} -} - -func (_c *AlertNotificationsApi_UploadAlertNotifications_Call) Run(run func()) *AlertNotificationsApi_UploadAlertNotifications_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *AlertNotificationsApi_UploadAlertNotifications_Call) Return(_a0 []string) *AlertNotificationsApi_UploadAlertNotifications_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *AlertNotificationsApi_UploadAlertNotifications_Call) RunAndReturn(run func() []string) *AlertNotificationsApi_UploadAlertNotifications_Call { - _c.Call.Return(run) - return _c -} - -// NewAlertNotificationsApi creates a new instance of AlertNotificationsApi. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewAlertNotificationsApi(t interface { - mock.TestingT - Cleanup(func()) -}) *AlertNotificationsApi { - mock := &AlertNotificationsApi{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/internal/service/mocks/GrafanaService.go b/internal/service/mocks/GrafanaService.go index 552c46b0..edae33f8 100644 --- a/internal/service/mocks/GrafanaService.go +++ b/internal/service/mocks/GrafanaService.go @@ -236,49 +236,6 @@ func (_c *GrafanaService_CreateServiceAccountToken_Call) RunAndReturn(run func(i return _c } -// DeleteAllAlertNotifications provides a mock function with given fields: -func (_m *GrafanaService) DeleteAllAlertNotifications() []string { - ret := _m.Called() - - var r0 []string - if rf, ok := ret.Get(0).(func() []string); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]string) - } - } - - return r0 -} - -// GrafanaService_DeleteAllAlertNotifications_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DeleteAllAlertNotifications' -type GrafanaService_DeleteAllAlertNotifications_Call struct { - *mock.Call -} - -// DeleteAllAlertNotifications is a helper method to define mock.On call -func (_e *GrafanaService_Expecter) DeleteAllAlertNotifications() *GrafanaService_DeleteAllAlertNotifications_Call { - return &GrafanaService_DeleteAllAlertNotifications_Call{Call: _e.mock.On("DeleteAllAlertNotifications")} -} - -func (_c *GrafanaService_DeleteAllAlertNotifications_Call) Run(run func()) *GrafanaService_DeleteAllAlertNotifications_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *GrafanaService_DeleteAllAlertNotifications_Call) Return(_a0 []string) *GrafanaService_DeleteAllAlertNotifications_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *GrafanaService_DeleteAllAlertNotifications_Call) RunAndReturn(run func() []string) *GrafanaService_DeleteAllAlertNotifications_Call { - _c.Call.Return(run) - return _c -} - // DeleteAllConnectionPermissions provides a mock function with given fields: filter func (_m *GrafanaService) DeleteAllConnectionPermissions(filter filters.Filter) []string { ret := _m.Called(filter) @@ -770,49 +727,6 @@ func (_c *GrafanaService_DeleteUserFromOrg_Call) RunAndReturn(run func(int64, in return _c } -// DownloadAlertNotifications provides a mock function with given fields: -func (_m *GrafanaService) DownloadAlertNotifications() []string { - ret := _m.Called() - - var r0 []string - if rf, ok := ret.Get(0).(func() []string); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]string) - } - } - - return r0 -} - -// GrafanaService_DownloadAlertNotifications_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DownloadAlertNotifications' -type GrafanaService_DownloadAlertNotifications_Call struct { - *mock.Call -} - -// DownloadAlertNotifications is a helper method to define mock.On call -func (_e *GrafanaService_Expecter) DownloadAlertNotifications() *GrafanaService_DownloadAlertNotifications_Call { - return &GrafanaService_DownloadAlertNotifications_Call{Call: _e.mock.On("DownloadAlertNotifications")} -} - -func (_c *GrafanaService_DownloadAlertNotifications_Call) Run(run func()) *GrafanaService_DownloadAlertNotifications_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *GrafanaService_DownloadAlertNotifications_Call) Return(_a0 []string) *GrafanaService_DownloadAlertNotifications_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *GrafanaService_DownloadAlertNotifications_Call) RunAndReturn(run func() []string) *GrafanaService_DownloadAlertNotifications_Call { - _c.Call.Return(run) - return _c -} - // DownloadConnectionPermissions provides a mock function with given fields: filter func (_m *GrafanaService) DownloadConnectionPermissions(filter filters.Filter) []string { ret := _m.Called(filter) @@ -1077,13 +991,13 @@ func (_c *GrafanaService_DownloadLibraryElements_Call) RunAndReturn(run func(fil return _c } -// DownloadOrganizations provides a mock function with given fields: -func (_m *GrafanaService) DownloadOrganizations() []string { - ret := _m.Called() +// DownloadOrganizations provides a mock function with given fields: filter +func (_m *GrafanaService) DownloadOrganizations(filter filters.Filter) []string { + ret := _m.Called(filter) var r0 []string - if rf, ok := ret.Get(0).(func() []string); ok { - r0 = rf() + if rf, ok := ret.Get(0).(func(filters.Filter) []string); ok { + r0 = rf(filter) } else { if ret.Get(0) != nil { r0 = ret.Get(0).([]string) @@ -1099,13 +1013,14 @@ type GrafanaService_DownloadOrganizations_Call struct { } // DownloadOrganizations is a helper method to define mock.On call -func (_e *GrafanaService_Expecter) DownloadOrganizations() *GrafanaService_DownloadOrganizations_Call { - return &GrafanaService_DownloadOrganizations_Call{Call: _e.mock.On("DownloadOrganizations")} +// - filter filters.Filter +func (_e *GrafanaService_Expecter) DownloadOrganizations(filter interface{}) *GrafanaService_DownloadOrganizations_Call { + return &GrafanaService_DownloadOrganizations_Call{Call: _e.mock.On("DownloadOrganizations", filter)} } -func (_c *GrafanaService_DownloadOrganizations_Call) Run(run func()) *GrafanaService_DownloadOrganizations_Call { +func (_c *GrafanaService_DownloadOrganizations_Call) Run(run func(filter filters.Filter)) *GrafanaService_DownloadOrganizations_Call { _c.Call.Run(func(args mock.Arguments) { - run() + run(args[0].(filters.Filter)) }) return _c } @@ -1115,7 +1030,7 @@ func (_c *GrafanaService_DownloadOrganizations_Call) Return(_a0 []string) *Grafa return _c } -func (_c *GrafanaService_DownloadOrganizations_Call) RunAndReturn(run func() []string) *GrafanaService_DownloadOrganizations_Call { +func (_c *GrafanaService_DownloadOrganizations_Call) RunAndReturn(run func(filters.Filter) []string) *GrafanaService_DownloadOrganizations_Call { _c.Call.Return(run) return _c } @@ -1465,49 +1380,6 @@ func (_c *GrafanaService_ListAPIKeys_Call) RunAndReturn(run func() []*models.API return _c } -// ListAlertNotifications provides a mock function with given fields: -func (_m *GrafanaService) ListAlertNotifications() []*models.AlertNotification { - ret := _m.Called() - - var r0 []*models.AlertNotification - if rf, ok := ret.Get(0).(func() []*models.AlertNotification); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]*models.AlertNotification) - } - } - - return r0 -} - -// GrafanaService_ListAlertNotifications_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListAlertNotifications' -type GrafanaService_ListAlertNotifications_Call struct { - *mock.Call -} - -// ListAlertNotifications is a helper method to define mock.On call -func (_e *GrafanaService_Expecter) ListAlertNotifications() *GrafanaService_ListAlertNotifications_Call { - return &GrafanaService_ListAlertNotifications_Call{Call: _e.mock.On("ListAlertNotifications")} -} - -func (_c *GrafanaService_ListAlertNotifications_Call) Run(run func()) *GrafanaService_ListAlertNotifications_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *GrafanaService_ListAlertNotifications_Call) Return(_a0 []*models.AlertNotification) *GrafanaService_ListAlertNotifications_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *GrafanaService_ListAlertNotifications_Call) RunAndReturn(run func() []*models.AlertNotification) *GrafanaService_ListAlertNotifications_Call { - _c.Call.Return(run) - return _c -} - // ListConnectionPermissions provides a mock function with given fields: filter func (_m *GrafanaService) ListConnectionPermissions(filter filters.Filter) map[*models.DataSourceListItemDTO]*models.DataSourcePermissionsDTO { ret := _m.Called(filter) @@ -1861,13 +1733,13 @@ func (_c *GrafanaService_ListOrgUsers_Call) RunAndReturn(run func(int64) []*mode return _c } -// ListOrganizations provides a mock function with given fields: -func (_m *GrafanaService) ListOrganizations() []*models.OrgDTO { - ret := _m.Called() +// ListOrganizations provides a mock function with given fields: filter +func (_m *GrafanaService) ListOrganizations(filter filters.Filter) []*models.OrgDTO { + ret := _m.Called(filter) var r0 []*models.OrgDTO - if rf, ok := ret.Get(0).(func() []*models.OrgDTO); ok { - r0 = rf() + if rf, ok := ret.Get(0).(func(filters.Filter) []*models.OrgDTO); ok { + r0 = rf(filter) } else { if ret.Get(0) != nil { r0 = ret.Get(0).([]*models.OrgDTO) @@ -1883,13 +1755,14 @@ type GrafanaService_ListOrganizations_Call struct { } // ListOrganizations is a helper method to define mock.On call -func (_e *GrafanaService_Expecter) ListOrganizations() *GrafanaService_ListOrganizations_Call { - return &GrafanaService_ListOrganizations_Call{Call: _e.mock.On("ListOrganizations")} +// - filter filters.Filter +func (_e *GrafanaService_Expecter) ListOrganizations(filter interface{}) *GrafanaService_ListOrganizations_Call { + return &GrafanaService_ListOrganizations_Call{Call: _e.mock.On("ListOrganizations", filter)} } -func (_c *GrafanaService_ListOrganizations_Call) Run(run func()) *GrafanaService_ListOrganizations_Call { +func (_c *GrafanaService_ListOrganizations_Call) Run(run func(filter filters.Filter)) *GrafanaService_ListOrganizations_Call { _c.Call.Run(func(args mock.Arguments) { - run() + run(args[0].(filters.Filter)) }) return _c } @@ -1899,7 +1772,7 @@ func (_c *GrafanaService_ListOrganizations_Call) Return(_a0 []*models.OrgDTO) *G return _c } -func (_c *GrafanaService_ListOrganizations_Call) RunAndReturn(run func() []*models.OrgDTO) *GrafanaService_ListOrganizations_Call { +func (_c *GrafanaService_ListOrganizations_Call) RunAndReturn(run func(filters.Filter) []*models.OrgDTO) *GrafanaService_ListOrganizations_Call { _c.Call.Return(run) return _c } @@ -2045,6 +1918,59 @@ func (_c *GrafanaService_ListTeams_Call) RunAndReturn(run func(filters.Filter) m return _c } +// ListUserOrganizations provides a mock function with given fields: +func (_m *GrafanaService) ListUserOrganizations() ([]*models.UserOrgDTO, error) { + ret := _m.Called() + + var r0 []*models.UserOrgDTO + var r1 error + if rf, ok := ret.Get(0).(func() ([]*models.UserOrgDTO, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() []*models.UserOrgDTO); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*models.UserOrgDTO) + } + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GrafanaService_ListUserOrganizations_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListUserOrganizations' +type GrafanaService_ListUserOrganizations_Call struct { + *mock.Call +} + +// ListUserOrganizations is a helper method to define mock.On call +func (_e *GrafanaService_Expecter) ListUserOrganizations() *GrafanaService_ListUserOrganizations_Call { + return &GrafanaService_ListUserOrganizations_Call{Call: _e.mock.On("ListUserOrganizations")} +} + +func (_c *GrafanaService_ListUserOrganizations_Call) Run(run func()) *GrafanaService_ListUserOrganizations_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *GrafanaService_ListUserOrganizations_Call) Return(_a0 []*models.UserOrgDTO, _a1 error) *GrafanaService_ListUserOrganizations_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *GrafanaService_ListUserOrganizations_Call) RunAndReturn(run func() ([]*models.UserOrgDTO, error)) *GrafanaService_ListUserOrganizations_Call { + _c.Call.Return(run) + return _c +} + // ListUsers provides a mock function with given fields: filter func (_m *GrafanaService) ListUsers(filter filters.Filter) []*models.UserSearchHitDTO { ret := _m.Called(filter) @@ -2173,13 +2099,13 @@ func (_c *GrafanaService_PromoteUser_Call) RunAndReturn(run func(string) (string return _c } -// SetOrganization provides a mock function with given fields: id -func (_m *GrafanaService) SetOrganization(id int64) error { - ret := _m.Called(id) +// SetOrganizationByName provides a mock function with given fields: name, useSlug +func (_m *GrafanaService) SetOrganizationByName(name string, useSlug bool) error { + ret := _m.Called(name, useSlug) var r0 error - if rf, ok := ret.Get(0).(func(int64) error); ok { - r0 = rf(id) + if rf, ok := ret.Get(0).(func(string, bool) error); ok { + r0 = rf(name, useSlug) } else { r0 = ret.Error(0) } @@ -2187,30 +2113,31 @@ func (_m *GrafanaService) SetOrganization(id int64) error { return r0 } -// GrafanaService_SetOrganization_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetOrganization' -type GrafanaService_SetOrganization_Call struct { +// GrafanaService_SetOrganizationByName_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetOrganizationByName' +type GrafanaService_SetOrganizationByName_Call struct { *mock.Call } -// SetOrganization is a helper method to define mock.On call -// - id int64 -func (_e *GrafanaService_Expecter) SetOrganization(id interface{}) *GrafanaService_SetOrganization_Call { - return &GrafanaService_SetOrganization_Call{Call: _e.mock.On("SetOrganization", id)} +// SetOrganizationByName is a helper method to define mock.On call +// - name string +// - useSlug bool +func (_e *GrafanaService_Expecter) SetOrganizationByName(name interface{}, useSlug interface{}) *GrafanaService_SetOrganizationByName_Call { + return &GrafanaService_SetOrganizationByName_Call{Call: _e.mock.On("SetOrganizationByName", name, useSlug)} } -func (_c *GrafanaService_SetOrganization_Call) Run(run func(id int64)) *GrafanaService_SetOrganization_Call { +func (_c *GrafanaService_SetOrganizationByName_Call) Run(run func(name string, useSlug bool)) *GrafanaService_SetOrganizationByName_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(int64)) + run(args[0].(string), args[1].(bool)) }) return _c } -func (_c *GrafanaService_SetOrganization_Call) Return(_a0 error) *GrafanaService_SetOrganization_Call { +func (_c *GrafanaService_SetOrganizationByName_Call) Return(_a0 error) *GrafanaService_SetOrganizationByName_Call { _c.Call.Return(_a0) return _c } -func (_c *GrafanaService_SetOrganization_Call) RunAndReturn(run func(int64) error) *GrafanaService_SetOrganization_Call { +func (_c *GrafanaService_SetOrganizationByName_Call) RunAndReturn(run func(string, bool) error) *GrafanaService_SetOrganizationByName_Call { _c.Call.Return(run) return _c } @@ -2301,49 +2228,6 @@ func (_c *GrafanaService_UpdateUserInOrg_Call) RunAndReturn(run func(string, int return _c } -// UploadAlertNotifications provides a mock function with given fields: -func (_m *GrafanaService) UploadAlertNotifications() []string { - ret := _m.Called() - - var r0 []string - if rf, ok := ret.Get(0).(func() []string); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]string) - } - } - - return r0 -} - -// GrafanaService_UploadAlertNotifications_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UploadAlertNotifications' -type GrafanaService_UploadAlertNotifications_Call struct { - *mock.Call -} - -// UploadAlertNotifications is a helper method to define mock.On call -func (_e *GrafanaService_Expecter) UploadAlertNotifications() *GrafanaService_UploadAlertNotifications_Call { - return &GrafanaService_UploadAlertNotifications_Call{Call: _e.mock.On("UploadAlertNotifications")} -} - -func (_c *GrafanaService_UploadAlertNotifications_Call) Run(run func()) *GrafanaService_UploadAlertNotifications_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *GrafanaService_UploadAlertNotifications_Call) Return(_a0 []string) *GrafanaService_UploadAlertNotifications_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *GrafanaService_UploadAlertNotifications_Call) RunAndReturn(run func() []string) *GrafanaService_UploadAlertNotifications_Call { - _c.Call.Return(run) - return _c -} - // UploadConnectionPermissions provides a mock function with given fields: filter func (_m *GrafanaService) UploadConnectionPermissions(filter filters.Filter) []string { ret := _m.Called(filter) @@ -2597,13 +2481,13 @@ func (_c *GrafanaService_UploadLibraryElements_Call) RunAndReturn(run func(filte return _c } -// UploadOrganizations provides a mock function with given fields: -func (_m *GrafanaService) UploadOrganizations() []string { - ret := _m.Called() +// UploadOrganizations provides a mock function with given fields: filter +func (_m *GrafanaService) UploadOrganizations(filter filters.Filter) []string { + ret := _m.Called(filter) var r0 []string - if rf, ok := ret.Get(0).(func() []string); ok { - r0 = rf() + if rf, ok := ret.Get(0).(func(filters.Filter) []string); ok { + r0 = rf(filter) } else { if ret.Get(0) != nil { r0 = ret.Get(0).([]string) @@ -2619,13 +2503,14 @@ type GrafanaService_UploadOrganizations_Call struct { } // UploadOrganizations is a helper method to define mock.On call -func (_e *GrafanaService_Expecter) UploadOrganizations() *GrafanaService_UploadOrganizations_Call { - return &GrafanaService_UploadOrganizations_Call{Call: _e.mock.On("UploadOrganizations")} +// - filter filters.Filter +func (_e *GrafanaService_Expecter) UploadOrganizations(filter interface{}) *GrafanaService_UploadOrganizations_Call { + return &GrafanaService_UploadOrganizations_Call{Call: _e.mock.On("UploadOrganizations", filter)} } -func (_c *GrafanaService_UploadOrganizations_Call) Run(run func()) *GrafanaService_UploadOrganizations_Call { +func (_c *GrafanaService_UploadOrganizations_Call) Run(run func(filter filters.Filter)) *GrafanaService_UploadOrganizations_Call { _c.Call.Run(func(args mock.Arguments) { - run() + run(args[0].(filters.Filter)) }) return _c } @@ -2635,7 +2520,7 @@ func (_c *GrafanaService_UploadOrganizations_Call) Return(_a0 []string) *Grafana return _c } -func (_c *GrafanaService_UploadOrganizations_Call) RunAndReturn(run func() []string) *GrafanaService_UploadOrganizations_Call { +func (_c *GrafanaService_UploadOrganizations_Call) RunAndReturn(run func(filters.Filter) []string) *GrafanaService_UploadOrganizations_Call { _c.Call.Return(run) return _c } diff --git a/internal/service/mocks/NewClientOpts.go b/internal/service/mocks/NewClientOpts.go new file mode 100644 index 00000000..63b98331 --- /dev/null +++ b/internal/service/mocks/NewClientOpts.go @@ -0,0 +1,68 @@ +// Code generated by mockery v2.36.0. DO NOT EDIT. + +package mocks + +import ( + client "github.com/grafana/grafana-openapi-client-go/client" + mock "github.com/stretchr/testify/mock" +) + +// NewClientOpts is an autogenerated mock type for the NewClientOpts type +type NewClientOpts struct { + mock.Mock +} + +type NewClientOpts_Expecter struct { + mock *mock.Mock +} + +func (_m *NewClientOpts) EXPECT() *NewClientOpts_Expecter { + return &NewClientOpts_Expecter{mock: &_m.Mock} +} + +// Execute provides a mock function with given fields: transportConfig +func (_m *NewClientOpts) Execute(transportConfig *client.TransportConfig) { + _m.Called(transportConfig) +} + +// NewClientOpts_Execute_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Execute' +type NewClientOpts_Execute_Call struct { + *mock.Call +} + +// Execute is a helper method to define mock.On call +// - transportConfig *client.TransportConfig +func (_e *NewClientOpts_Expecter) Execute(transportConfig interface{}) *NewClientOpts_Execute_Call { + return &NewClientOpts_Execute_Call{Call: _e.mock.On("Execute", transportConfig)} +} + +func (_c *NewClientOpts_Execute_Call) Run(run func(transportConfig *client.TransportConfig)) *NewClientOpts_Execute_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*client.TransportConfig)) + }) + return _c +} + +func (_c *NewClientOpts_Execute_Call) Return() *NewClientOpts_Execute_Call { + _c.Call.Return() + return _c +} + +func (_c *NewClientOpts_Execute_Call) RunAndReturn(run func(*client.TransportConfig)) *NewClientOpts_Execute_Call { + _c.Call.Return(run) + return _c +} + +// NewNewClientOpts creates a new instance of NewClientOpts. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewNewClientOpts(t interface { + mock.TestingT + Cleanup(func()) +}) *NewClientOpts { + mock := &NewClientOpts{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/internal/service/mocks/OrganizationsApi.go b/internal/service/mocks/OrganizationsApi.go index aaff266a..7b13ce1a 100644 --- a/internal/service/mocks/OrganizationsApi.go +++ b/internal/service/mocks/OrganizationsApi.go @@ -3,8 +3,10 @@ package mocks import ( - models "github.com/grafana/grafana-openapi-client-go/models" + filters "github.com/esnet/gdg/internal/service/filters" mock "github.com/stretchr/testify/mock" + + models "github.com/grafana/grafana-openapi-client-go/models" ) // OrganizationsApi is an autogenerated mock type for the OrganizationsApi type @@ -107,13 +109,13 @@ func (_c *OrganizationsApi_DeleteUserFromOrg_Call) RunAndReturn(run func(int64, return _c } -// DownloadOrganizations provides a mock function with given fields: -func (_m *OrganizationsApi) DownloadOrganizations() []string { - ret := _m.Called() +// DownloadOrganizations provides a mock function with given fields: filter +func (_m *OrganizationsApi) DownloadOrganizations(filter filters.Filter) []string { + ret := _m.Called(filter) var r0 []string - if rf, ok := ret.Get(0).(func() []string); ok { - r0 = rf() + if rf, ok := ret.Get(0).(func(filters.Filter) []string); ok { + r0 = rf(filter) } else { if ret.Get(0) != nil { r0 = ret.Get(0).([]string) @@ -129,13 +131,14 @@ type OrganizationsApi_DownloadOrganizations_Call struct { } // DownloadOrganizations is a helper method to define mock.On call -func (_e *OrganizationsApi_Expecter) DownloadOrganizations() *OrganizationsApi_DownloadOrganizations_Call { - return &OrganizationsApi_DownloadOrganizations_Call{Call: _e.mock.On("DownloadOrganizations")} +// - filter filters.Filter +func (_e *OrganizationsApi_Expecter) DownloadOrganizations(filter interface{}) *OrganizationsApi_DownloadOrganizations_Call { + return &OrganizationsApi_DownloadOrganizations_Call{Call: _e.mock.On("DownloadOrganizations", filter)} } -func (_c *OrganizationsApi_DownloadOrganizations_Call) Run(run func()) *OrganizationsApi_DownloadOrganizations_Call { +func (_c *OrganizationsApi_DownloadOrganizations_Call) Run(run func(filter filters.Filter)) *OrganizationsApi_DownloadOrganizations_Call { _c.Call.Run(func(args mock.Arguments) { - run() + run(args[0].(filters.Filter)) }) return _c } @@ -145,7 +148,7 @@ func (_c *OrganizationsApi_DownloadOrganizations_Call) Return(_a0 []string) *Org return _c } -func (_c *OrganizationsApi_DownloadOrganizations_Call) RunAndReturn(run func() []string) *OrganizationsApi_DownloadOrganizations_Call { +func (_c *OrganizationsApi_DownloadOrganizations_Call) RunAndReturn(run func(filters.Filter) []string) *OrganizationsApi_DownloadOrganizations_Call { _c.Call.Return(run) return _c } @@ -312,13 +315,13 @@ func (_c *OrganizationsApi_ListOrgUsers_Call) RunAndReturn(run func(int64) []*mo return _c } -// ListOrganizations provides a mock function with given fields: -func (_m *OrganizationsApi) ListOrganizations() []*models.OrgDTO { - ret := _m.Called() +// ListOrganizations provides a mock function with given fields: filter +func (_m *OrganizationsApi) ListOrganizations(filter filters.Filter) []*models.OrgDTO { + ret := _m.Called(filter) var r0 []*models.OrgDTO - if rf, ok := ret.Get(0).(func() []*models.OrgDTO); ok { - r0 = rf() + if rf, ok := ret.Get(0).(func(filters.Filter) []*models.OrgDTO); ok { + r0 = rf(filter) } else { if ret.Get(0) != nil { r0 = ret.Get(0).([]*models.OrgDTO) @@ -334,13 +337,14 @@ type OrganizationsApi_ListOrganizations_Call struct { } // ListOrganizations is a helper method to define mock.On call -func (_e *OrganizationsApi_Expecter) ListOrganizations() *OrganizationsApi_ListOrganizations_Call { - return &OrganizationsApi_ListOrganizations_Call{Call: _e.mock.On("ListOrganizations")} +// - filter filters.Filter +func (_e *OrganizationsApi_Expecter) ListOrganizations(filter interface{}) *OrganizationsApi_ListOrganizations_Call { + return &OrganizationsApi_ListOrganizations_Call{Call: _e.mock.On("ListOrganizations", filter)} } -func (_c *OrganizationsApi_ListOrganizations_Call) Run(run func()) *OrganizationsApi_ListOrganizations_Call { +func (_c *OrganizationsApi_ListOrganizations_Call) Run(run func(filter filters.Filter)) *OrganizationsApi_ListOrganizations_Call { _c.Call.Run(func(args mock.Arguments) { - run() + run(args[0].(filters.Filter)) }) return _c } @@ -350,18 +354,71 @@ func (_c *OrganizationsApi_ListOrganizations_Call) Return(_a0 []*models.OrgDTO) return _c } -func (_c *OrganizationsApi_ListOrganizations_Call) RunAndReturn(run func() []*models.OrgDTO) *OrganizationsApi_ListOrganizations_Call { +func (_c *OrganizationsApi_ListOrganizations_Call) RunAndReturn(run func(filters.Filter) []*models.OrgDTO) *OrganizationsApi_ListOrganizations_Call { _c.Call.Return(run) return _c } -// SetOrganization provides a mock function with given fields: id -func (_m *OrganizationsApi) SetOrganization(id int64) error { - ret := _m.Called(id) +// ListUserOrganizations provides a mock function with given fields: +func (_m *OrganizationsApi) ListUserOrganizations() ([]*models.UserOrgDTO, error) { + ret := _m.Called() + + var r0 []*models.UserOrgDTO + var r1 error + if rf, ok := ret.Get(0).(func() ([]*models.UserOrgDTO, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() []*models.UserOrgDTO); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*models.UserOrgDTO) + } + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// OrganizationsApi_ListUserOrganizations_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListUserOrganizations' +type OrganizationsApi_ListUserOrganizations_Call struct { + *mock.Call +} + +// ListUserOrganizations is a helper method to define mock.On call +func (_e *OrganizationsApi_Expecter) ListUserOrganizations() *OrganizationsApi_ListUserOrganizations_Call { + return &OrganizationsApi_ListUserOrganizations_Call{Call: _e.mock.On("ListUserOrganizations")} +} + +func (_c *OrganizationsApi_ListUserOrganizations_Call) Run(run func()) *OrganizationsApi_ListUserOrganizations_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *OrganizationsApi_ListUserOrganizations_Call) Return(_a0 []*models.UserOrgDTO, _a1 error) *OrganizationsApi_ListUserOrganizations_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *OrganizationsApi_ListUserOrganizations_Call) RunAndReturn(run func() ([]*models.UserOrgDTO, error)) *OrganizationsApi_ListUserOrganizations_Call { + _c.Call.Return(run) + return _c +} + +// SetOrganizationByName provides a mock function with given fields: name, useSlug +func (_m *OrganizationsApi) SetOrganizationByName(name string, useSlug bool) error { + ret := _m.Called(name, useSlug) var r0 error - if rf, ok := ret.Get(0).(func(int64) error); ok { - r0 = rf(id) + if rf, ok := ret.Get(0).(func(string, bool) error); ok { + r0 = rf(name, useSlug) } else { r0 = ret.Error(0) } @@ -369,30 +426,31 @@ func (_m *OrganizationsApi) SetOrganization(id int64) error { return r0 } -// OrganizationsApi_SetOrganization_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetOrganization' -type OrganizationsApi_SetOrganization_Call struct { +// OrganizationsApi_SetOrganizationByName_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetOrganizationByName' +type OrganizationsApi_SetOrganizationByName_Call struct { *mock.Call } -// SetOrganization is a helper method to define mock.On call -// - id int64 -func (_e *OrganizationsApi_Expecter) SetOrganization(id interface{}) *OrganizationsApi_SetOrganization_Call { - return &OrganizationsApi_SetOrganization_Call{Call: _e.mock.On("SetOrganization", id)} +// SetOrganizationByName is a helper method to define mock.On call +// - name string +// - useSlug bool +func (_e *OrganizationsApi_Expecter) SetOrganizationByName(name interface{}, useSlug interface{}) *OrganizationsApi_SetOrganizationByName_Call { + return &OrganizationsApi_SetOrganizationByName_Call{Call: _e.mock.On("SetOrganizationByName", name, useSlug)} } -func (_c *OrganizationsApi_SetOrganization_Call) Run(run func(id int64)) *OrganizationsApi_SetOrganization_Call { +func (_c *OrganizationsApi_SetOrganizationByName_Call) Run(run func(name string, useSlug bool)) *OrganizationsApi_SetOrganizationByName_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(int64)) + run(args[0].(string), args[1].(bool)) }) return _c } -func (_c *OrganizationsApi_SetOrganization_Call) Return(_a0 error) *OrganizationsApi_SetOrganization_Call { +func (_c *OrganizationsApi_SetOrganizationByName_Call) Return(_a0 error) *OrganizationsApi_SetOrganizationByName_Call { _c.Call.Return(_a0) return _c } -func (_c *OrganizationsApi_SetOrganization_Call) RunAndReturn(run func(int64) error) *OrganizationsApi_SetOrganization_Call { +func (_c *OrganizationsApi_SetOrganizationByName_Call) RunAndReturn(run func(string, bool) error) *OrganizationsApi_SetOrganizationByName_Call { _c.Call.Return(run) return _c } @@ -483,13 +541,13 @@ func (_c *OrganizationsApi_UpdateUserInOrg_Call) RunAndReturn(run func(string, i return _c } -// UploadOrganizations provides a mock function with given fields: -func (_m *OrganizationsApi) UploadOrganizations() []string { - ret := _m.Called() +// UploadOrganizations provides a mock function with given fields: filter +func (_m *OrganizationsApi) UploadOrganizations(filter filters.Filter) []string { + ret := _m.Called(filter) var r0 []string - if rf, ok := ret.Get(0).(func() []string); ok { - r0 = rf() + if rf, ok := ret.Get(0).(func(filters.Filter) []string); ok { + r0 = rf(filter) } else { if ret.Get(0) != nil { r0 = ret.Get(0).([]string) @@ -505,13 +563,14 @@ type OrganizationsApi_UploadOrganizations_Call struct { } // UploadOrganizations is a helper method to define mock.On call -func (_e *OrganizationsApi_Expecter) UploadOrganizations() *OrganizationsApi_UploadOrganizations_Call { - return &OrganizationsApi_UploadOrganizations_Call{Call: _e.mock.On("UploadOrganizations")} +// - filter filters.Filter +func (_e *OrganizationsApi_Expecter) UploadOrganizations(filter interface{}) *OrganizationsApi_UploadOrganizations_Call { + return &OrganizationsApi_UploadOrganizations_Call{Call: _e.mock.On("UploadOrganizations", filter)} } -func (_c *OrganizationsApi_UploadOrganizations_Call) Run(run func()) *OrganizationsApi_UploadOrganizations_Call { +func (_c *OrganizationsApi_UploadOrganizations_Call) Run(run func(filter filters.Filter)) *OrganizationsApi_UploadOrganizations_Call { _c.Call.Run(func(args mock.Arguments) { - run() + run(args[0].(filters.Filter)) }) return _c } @@ -521,7 +580,7 @@ func (_c *OrganizationsApi_UploadOrganizations_Call) Return(_a0 []string) *Organ return _c } -func (_c *OrganizationsApi_UploadOrganizations_Call) RunAndReturn(run func() []string) *OrganizationsApi_UploadOrganizations_Call { +func (_c *OrganizationsApi_UploadOrganizations_Call) RunAndReturn(run func(filters.Filter) []string) *OrganizationsApi_UploadOrganizations_Call { _c.Call.Return(run) return _c } diff --git a/internal/service/organizations.go b/internal/service/organizations.go index fa2c512b..26a37e55 100644 --- a/internal/service/organizations.go +++ b/internal/service/organizations.go @@ -5,10 +5,12 @@ import ( "errors" "fmt" "github.com/esnet/gdg/internal/config" + "github.com/esnet/gdg/internal/service/filters" "github.com/gosimple/slug" "github.com/grafana/grafana-openapi-client-go/client" "github.com/grafana/grafana-openapi-client-go/client/orgs" "github.com/grafana/grafana-openapi-client-go/models" + "github.com/tidwall/gjson" "log" "log/slog" "path/filepath" @@ -17,14 +19,15 @@ import ( // OrganizationsApi Contract definition type OrganizationsApi interface { - ListOrganizations() []*models.OrgDTO - DownloadOrganizations() []string - UploadOrganizations() []string - SetOrganization(id int64) error + ListOrganizations(filter filters.Filter) []*models.OrgDTO + DownloadOrganizations(filter filters.Filter) []string + UploadOrganizations(filter filters.Filter) []string + SetOrganizationByName(name string, useSlug bool) error //Manage Active Organization GetUserOrganization() *models.OrgDetailsDTO GetTokenOrganization() *models.OrgDetailsDTO SetUserOrganizations(id int64) error + ListUserOrganizations() ([]*models.UserOrgDTO, error) InitOrganizations() //Org Users ListOrgUsers(orgId int64) []*models.OrgUserDTO @@ -33,17 +36,47 @@ type OrganizationsApi interface { UpdateUserInOrg(role string, userId, orgId int64) error } +func NewOrganizationFilter(args ...string) filters.Filter { + filterObj := filters.NewBaseFilter() + if len(args) == 0 || args[0] == "" { + return filterObj + } + + filterObj.AddFilter(filters.OrgFilter, args[0]) + return filterObj +} + // InitOrganizations will context switch to configured organization and invoke a different call depending on the access level. func (s *DashNGoImpl) InitOrganizations() { var orgInfo *models.OrgDetailsDTO + var orgEntity models.OrgDetailsDTO if s.grafanaConf.IsAdminEnabled() || s.grafanaConf.IsBasicAuth() { orgInfo = s.GetUserOrganization() if orgInfo == nil { log.Fatal("Unable to retrieve requested user's org") } - if orgInfo.ID != s.grafanaConf.GetOrganizationId() { - err := s.SetUserOrganizations(s.grafanaConf.GetOrganizationId()) + if orgInfo.Name != s.grafanaConf.GetOrganizationName() { + userOrgs, err := s.ListUserOrganizations() + if err != nil { + log.Fatal("Unable to switch user's Org") + } + found := false + for _, org := range userOrgs { + if org.Name == s.grafanaConf.GetOrganizationName() { + orgEntity.ID = org.OrgID + orgEntity.Name = org.Name + found = true + break + } + } + if !found { + log.Fatalf("User does not have access to org: '%s', Unable to switch user's Org", s.grafanaConf.GetOrganizationName()) + } + + } + if orgInfo.Name != s.grafanaConf.GetOrganizationName() { + err := s.SetUserOrganizations(orgEntity.ID) if err != nil { log.Fatal("Unable to switch user's Org") } @@ -51,50 +84,52 @@ func (s *DashNGoImpl) InitOrganizations() { } else { orgInfo = &models.OrgDetailsDTO{ - ID: s.grafanaConf.GetOrganizationId(), + Name: s.grafanaConf.GetOrganizationName(), } } } -// getOrganizations returns organization for a given id. -func (s *DashNGoImpl) getOrganization(id int64) (*models.OrgDetailsDTO, error) { - data, err := s.GetClient().Orgs.GetOrgByID(id) - if err != nil { - return nil, err - } - - return data.GetPayload(), nil - -} - -// SetOrganization sets organization for a given id. -func (s *DashNGoImpl) SetOrganization(id int64) error { - //Removes Org filter - if id <= 1 { - slog.Warn("organization is not a valid value, resetting to default value of 1.") - s.grafanaConf.OrganizationId = 1 - } +func (s *DashNGoImpl) SetOrganizationByName(name string, useSlug bool) error { if s.grafanaConf.IsAdminEnabled() || s.grafanaConf.IsBasicAuth() { - organization, err := s.getOrganization(id) + payload, err := s.ListUserOrganizations() if err != nil { - return errors.New("invalid org Id, org is not found") + return err } - s.grafanaConf.OrganizationId = organization.ID + var requestOrg *models.UserOrgDTO + + for ndx, orgEntity := range payload { + orgName := orgEntity.Name + if useSlug { + orgName = slug.Make(orgName) + } + if orgName == name { + requestOrg = payload[ndx] + break + } + } + if requestOrg == nil { + log.Fatalf("unable to set org. Please ensure you have the correct permissions and the org name is correct") + } + s.grafanaConf.OrganizationName = requestOrg.Name } else { tokenOrg := s.GetTokenOrganization() - if tokenOrg.ID != id { - log.Fatalf("you have no BasicAuth configured, and token org are non-changeable. Please configure a different token associated with Org %d, OR configure basic auth.", id) + orgName := tokenOrg.Name + if useSlug { + orgName = slug.Make(orgName) + } + if orgName != name { + log.Fatalf("you have no BasicAuth configured, and token org are non-changeable. Please configure a different token associated with Org %s, OR configure basic auth.", orgName) } - s.grafanaConf.OrganizationId = id } return config.Config().SaveToDisk(false) + } // ListOrganizations List all dashboards -func (s *DashNGoImpl) ListOrganizations() []*models.OrgDTO { +func (s *DashNGoImpl) ListOrganizations(filter filters.Filter) []*models.OrgDTO { if !s.grafanaConf.IsAdminEnabled() { slog.Error("No valid Grafana Admin configured, cannot retrieve Organizations List") return nil @@ -113,11 +148,17 @@ func (s *DashNGoImpl) ListOrganizations() []*models.OrgDTO { log.Fatalf("%s, err: %v", msg, err) } } - return orgList.GetPayload() + var result []*models.OrgDTO + for _, org := range orgList.Payload { + if filter.GetFilter(filters.OrgFilter) == "" || filter.GetFilter(filters.OrgFilter) == org.Name { + result = append(result, org) + } + } + return result } // DownloadOrganizations Download organizations -func (s *DashNGoImpl) DownloadOrganizations() []string { +func (s *DashNGoImpl) DownloadOrganizations(filter filters.Filter) []string { if !s.grafanaConf.IsAdminEnabled() { slog.Error("No valid Grafana Admin configured, cannot retrieve Organizations") return nil @@ -128,7 +169,7 @@ func (s *DashNGoImpl) DownloadOrganizations() []string { dataFiles []string ) - orgsListing := s.ListOrganizations() + orgsListing := s.ListOrganizations(filter) for _, organisation := range orgsListing { if dsPacked, err = json.MarshalIndent(organisation, "", " "); err != nil { slog.Error("Unable to serialize organization object", "err", err, "organization", organisation.Name) @@ -146,7 +187,7 @@ func (s *DashNGoImpl) DownloadOrganizations() []string { } // UploadOrganizations Upload organizations to Grafana -func (s *DashNGoImpl) UploadOrganizations() []string { +func (s *DashNGoImpl) UploadOrganizations(filter filters.Filter) []string { if !s.grafanaConf.IsAdminEnabled() { slog.Error("No valid Grafana Admin configured, cannot upload Organizations") return nil @@ -159,7 +200,7 @@ func (s *DashNGoImpl) UploadOrganizations() []string { if err != nil { log.Fatalf("Failed to read folders imports, err: %v", err) } - orgListing := s.ListOrganizations() + orgListing := s.ListOrganizations(filter) orgMap := map[string]bool{} for _, entry := range orgListing { orgMap[entry.Name] = true @@ -178,6 +219,10 @@ func (s *DashNGoImpl) UploadOrganizations() []string { slog.Warn("failed to unmarshall folder", "err", err) continue } + rawOrgName := gjson.GetBytes(rawFolder, "name").String() + if filter.GetFilter(filters.OrgFilter) != "" && rawOrgName != filter.GetFilter(filters.OrgFilter) { + continue + } if _, ok := orgMap[newOrg.Name]; ok { slog.Info("Organization already exists, skipping", "organization", newOrg.Name) continue @@ -185,7 +230,7 @@ func (s *DashNGoImpl) UploadOrganizations() []string { _, err = s.GetBasicAuthClient().Orgs.CreateOrg(&newOrg) if err != nil { - slog.Error("failed to create folder", "organization", newOrg.Name) + slog.Error("failed to create organization", "organization", newOrg.Name) continue } result = append(result, newOrg.Name) @@ -194,14 +239,15 @@ func (s *DashNGoImpl) UploadOrganizations() []string { return result } -// SwitchOrganization switch organization context -func (s *DashNGoImpl) SwitchOrganization(id int64) error { +// SwitchOrganizationByName switch organization context +func (s *DashNGoImpl) SwitchOrganizationByName(orgName string) error { if !s.grafanaConf.IsBasicAuth() { slog.Warn("Basic auth required for Org switching. Ignoring Org setting and continuing") return nil } valid := false - if id > 1 { + var orgId int64 = 1 + if orgName != "" { var orgsPayload []*models.OrgDTO orgList, err := s.GetBasicAuthClient().Orgs.SearchOrgs(orgs.NewSearchOrgsParams()) if err != nil { @@ -213,17 +259,17 @@ func (s *DashNGoImpl) SwitchOrganization(id int64) error { } for _, orgEntry := range orgsPayload { slog.Debug("", "orgID", orgEntry.ID, "OrgName", orgEntry.Name) - if orgEntry.ID == s.grafanaConf.GetOrganizationId() { + if orgEntry.Name == s.grafanaConf.GetOrganizationName() { valid = true + orgId = orgEntry.ID break } } - } - //Fallback on default - if id < 2 { - id = 1 // DefaultOrgID + } else { + //Fallback on default valid = true + orgId = config.DefaultOrganizationId } //We retrieved all the orgs successfully and none of them matched the requested ID @@ -231,7 +277,7 @@ func (s *DashNGoImpl) SwitchOrganization(id int64) error { log.Fatalf("The Specified OrgId does not match any existing organization. Please check your configuration and try again.") } - status, err := s.GetBasicAuthClient().SignedInUser.UserSetUsingOrg(id) + status, err := s.GetBasicAuthClient().SignedInUser.UserSetUsingOrg(orgId) if err != nil { log.Fatalf("%s for %v\n", err, status) return err @@ -259,6 +305,16 @@ func (s *DashNGoImpl) getAssociatedActiveOrg(apiClient *client.GrafanaHTTPAPI) * return payload.GetPayload() } +func (s *DashNGoImpl) ListUserOrganizations() ([]*models.UserOrgDTO, error) { + payload, err := s.GetBasicAuthClient().SignedInUser.GetSignedInUserOrgList() + if err != nil { + return nil, err + } + + return payload.GetPayload(), nil + +} + func (s *DashNGoImpl) SetUserOrganizations(id int64) error { payload, err := s.GetBasicAuthClient().SignedInUser.UserSetUsingOrg(id) if err == nil { diff --git a/internal/templating/templating.go b/internal/templating/templating.go index cf044638..f542ef06 100644 --- a/internal/templating/templating.go +++ b/internal/templating/templating.go @@ -83,8 +83,12 @@ func (t *templateImpl) Generate(templateName string) (map[string][]string, error } for _, outputEntity := range entity.DashboardEntities { grafana := cfg.GetDefaultGrafanaConfig() - slog.Debug("Creating a new template", slog.String("folder", outputEntity.Folder), slog.Int64("orgId", outputEntity.OrgId), slog.Any("data", outputEntity.TemplateData)) - grafana.OrganizationId = outputEntity.OrgId + slog.Debug("Creating a new template", + slog.String("folder", outputEntity.Folder), + slog.String("orgName", outputEntity.OrganizationName), + slog.Any("data", outputEntity.TemplateData), + ) + grafana.OrganizationName = outputEntity.OrganizationName outputPath := service.BuildResourceFolder(outputEntity.Folder, config.DashboardResource) //Merge two maps. tmpl, err := template.New("").Funcs(fns).Parse(string(templateData)) diff --git a/internal/templating/templating_test.go b/internal/templating/templating_test.go index 14d3ea4a..01681cdf 100644 --- a/internal/templating/templating_test.go +++ b/internal/templating/templating_test.go @@ -25,8 +25,8 @@ func TestGenerate(t *testing.T) { assert.Nil(t, err) assert.Equal(t, len(data), 1) generatedFiles := data["template_example"] - assert.True(t, slices.Contains(generatedFiles, "test/data/org_2/dashboards/General/testing-foobar.json")) - assert.True(t, slices.Contains(generatedFiles, "test/data/org_3/dashboards/Testing/template_example.json")) + assert.True(t, slices.Contains(generatedFiles, "test/data/org_main-org/dashboards/General/testing-foobar.json")) + assert.True(t, slices.Contains(generatedFiles, "test/data/org_some-other-org/dashboards/Testing/template_example.json")) //Remove output to avoid conflicting with other tests defer func() { os.Remove(generatedFiles[0]) @@ -36,7 +36,7 @@ func TestGenerate(t *testing.T) { //Obtain first Config and validate output. cfg := config.Config().GetTemplateConfig() templateCfg := cfg.Entities.Dashboards[0].DashboardEntities[0] - rawData, err := os.ReadFile("test/data/org_2/dashboards/General/testing-foobar.json") + rawData, err := os.ReadFile("test/data/org_main-org/dashboards/General/testing-foobar.json") assert.Nil(t, err) parser := gjson.ParseBytes(rawData) val := parser.Get("annotations.list.0.hashKey") diff --git a/test/dashboard_integration_test.go b/test/dashboard_integration_test.go index ed2015b6..7cb410e5 100644 --- a/test/dashboard_integration_test.go +++ b/test/dashboard_integration_test.go @@ -252,9 +252,9 @@ func validateGeneralBoard(t *testing.T, board *models.Hit) { func validateTags(t *testing.T, board *models.Hit) { assert.True(t, board.UID != "") assert.True(t, len(board.Tags) > 0) - all_tags := []string{"netsage", "flow"} + allTags := []string{"netsage", "flow"} for _, tag := range board.Tags { - assert.True(t, slices.Contains(all_tags, tag)) + assert.True(t, slices.Contains(allTags, tag)) } } diff --git a/test/data/org_1/connections/google-sheets.json b/test/data/org_main-org/connections/google-sheets.json similarity index 100% rename from test/data/org_1/connections/google-sheets.json rename to test/data/org_main-org/connections/google-sheets.json diff --git a/test/data/org_1/connections/netsage-tsds.json b/test/data/org_main-org/connections/netsage-tsds.json similarity index 100% rename from test/data/org_1/connections/netsage-tsds.json rename to test/data/org_main-org/connections/netsage-tsds.json diff --git a/test/data/org_1/connections/netsage.json b/test/data/org_main-org/connections/netsage.json similarity index 100% rename from test/data/org_1/connections/netsage.json rename to test/data/org_main-org/connections/netsage.json diff --git a/test/data/org_1/dashboards/General/bandwidth-dashboard.json b/test/data/org_main-org/dashboards/General/bandwidth-dashboard.json similarity index 100% rename from test/data/org_1/dashboards/General/bandwidth-dashboard.json rename to test/data/org_main-org/dashboards/General/bandwidth-dashboard.json diff --git a/test/data/org_1/dashboards/General/bandwidth-patterns.json b/test/data/org_main-org/dashboards/General/bandwidth-patterns.json similarity index 100% rename from test/data/org_1/dashboards/General/bandwidth-patterns.json rename to test/data/org_main-org/dashboards/General/bandwidth-patterns.json diff --git a/test/data/org_1/dashboards/General/individual-flows-per-country.json b/test/data/org_main-org/dashboards/General/individual-flows-per-country.json similarity index 100% rename from test/data/org_1/dashboards/General/individual-flows-per-country.json rename to test/data/org_main-org/dashboards/General/individual-flows-per-country.json diff --git a/test/data/org_1/dashboards/General/individual-flows.json b/test/data/org_main-org/dashboards/General/individual-flows.json similarity index 100% rename from test/data/org_1/dashboards/General/individual-flows.json rename to test/data/org_main-org/dashboards/General/individual-flows.json diff --git a/test/data/org_1/dashboards/General/loss-patterns.json b/test/data/org_main-org/dashboards/General/loss-patterns.json similarity index 100% rename from test/data/org_1/dashboards/General/loss-patterns.json rename to test/data/org_main-org/dashboards/General/loss-patterns.json diff --git a/test/data/org_1/dashboards/General/other-flow-stats.json b/test/data/org_main-org/dashboards/General/other-flow-stats.json similarity index 100% rename from test/data/org_1/dashboards/General/other-flow-stats.json rename to test/data/org_main-org/dashboards/General/other-flow-stats.json diff --git a/test/data/org_1/dashboards/General/science-discipline-patterns.json b/test/data/org_main-org/dashboards/General/science-discipline-patterns.json similarity index 100% rename from test/data/org_1/dashboards/General/science-discipline-patterns.json rename to test/data/org_main-org/dashboards/General/science-discipline-patterns.json diff --git a/test/data/org_1/dashboards/General/top-talkers-over-time.json b/test/data/org_main-org/dashboards/General/top-talkers-over-time.json similarity index 100% rename from test/data/org_1/dashboards/General/top-talkers-over-time.json rename to test/data/org_main-org/dashboards/General/top-talkers-over-time.json diff --git a/test/data/org_1/dashboards/Ignored/latency-patterns.json b/test/data/org_main-org/dashboards/Ignored/latency-patterns.json similarity index 100% rename from test/data/org_1/dashboards/Ignored/latency-patterns.json rename to test/data/org_main-org/dashboards/Ignored/latency-patterns.json diff --git a/test/data/org_1/dashboards/Other/dashboard-makeover-challenge.json b/test/data/org_main-org/dashboards/Other/dashboard-makeover-challenge.json similarity index 100% rename from test/data/org_1/dashboards/Other/dashboard-makeover-challenge.json rename to test/data/org_main-org/dashboards/Other/dashboard-makeover-challenge.json diff --git a/test/data/org_1/dashboards/Other/flow-analysis.json b/test/data/org_main-org/dashboards/Other/flow-analysis.json similarity index 100% rename from test/data/org_1/dashboards/Other/flow-analysis.json rename to test/data/org_main-org/dashboards/Other/flow-analysis.json diff --git a/test/data/org_1/dashboards/Other/flow-data-for-circuits.json b/test/data/org_main-org/dashboards/Other/flow-data-for-circuits.json similarity index 100% rename from test/data/org_1/dashboards/Other/flow-data-for-circuits.json rename to test/data/org_main-org/dashboards/Other/flow-data-for-circuits.json diff --git a/test/data/org_1/dashboards/Other/flow-data-for-projects.json b/test/data/org_main-org/dashboards/Other/flow-data-for-projects.json similarity index 100% rename from test/data/org_1/dashboards/Other/flow-data-for-projects.json rename to test/data/org_main-org/dashboards/Other/flow-data-for-projects.json diff --git a/test/data/org_1/dashboards/Other/flow-data-per-country.json b/test/data/org_main-org/dashboards/Other/flow-data-per-country.json similarity index 100% rename from test/data/org_1/dashboards/Other/flow-data-per-country.json rename to test/data/org_main-org/dashboards/Other/flow-data-per-country.json diff --git a/test/data/org_1/dashboards/Other/flow-data-per-organization.json b/test/data/org_main-org/dashboards/Other/flow-data-per-organization.json similarity index 100% rename from test/data/org_1/dashboards/Other/flow-data-per-organization.json rename to test/data/org_main-org/dashboards/Other/flow-data-per-organization.json diff --git a/test/data/org_1/dashboards/Other/flow-information.json b/test/data/org_main-org/dashboards/Other/flow-information.json similarity index 100% rename from test/data/org_1/dashboards/Other/flow-information.json rename to test/data/org_main-org/dashboards/Other/flow-information.json diff --git a/test/data/org_1/dashboards/Other/flows-by-science-discipline.json b/test/data/org_main-org/dashboards/Other/flows-by-science-discipline.json similarity index 100% rename from test/data/org_1/dashboards/Other/flows-by-science-discipline.json rename to test/data/org_main-org/dashboards/Other/flows-by-science-discipline.json diff --git a/test/data/org_1/folders/ignored.json b/test/data/org_main-org/folders/ignored.json similarity index 100% rename from test/data/org_1/folders/ignored.json rename to test/data/org_main-org/folders/ignored.json diff --git a/test/data/org_1/folders/other.json b/test/data/org_main-org/folders/other.json similarity index 100% rename from test/data/org_1/folders/other.json rename to test/data/org_main-org/folders/other.json diff --git a/test/data/org_1/libraryelements/General/dashboard-makeover-extra-cleaning-duty-assignment-today.json b/test/data/org_main-org/libraryelements/General/dashboard-makeover-extra-cleaning-duty-assignment-today.json similarity index 100% rename from test/data/org_1/libraryelements/General/dashboard-makeover-extra-cleaning-duty-assignment-today.json rename to test/data/org_main-org/libraryelements/General/dashboard-makeover-extra-cleaning-duty-assignment-today.json diff --git a/test/data/org_1/libraryelements/General/dashboard-makeover-lighting-status.json b/test/data/org_main-org/libraryelements/General/dashboard-makeover-lighting-status.json similarity index 100% rename from test/data/org_1/libraryelements/General/dashboard-makeover-lighting-status.json rename to test/data/org_main-org/libraryelements/General/dashboard-makeover-lighting-status.json diff --git a/test/data/org_1/libraryelements/General/dashboard-makeover-side-dish-prep-times-past-7-days.json b/test/data/org_main-org/libraryelements/General/dashboard-makeover-side-dish-prep-times-past-7-days.json similarity index 100% rename from test/data/org_1/libraryelements/General/dashboard-makeover-side-dish-prep-times-past-7-days.json rename to test/data/org_main-org/libraryelements/General/dashboard-makeover-side-dish-prep-times-past-7-days.json diff --git a/test/data/org_1/libraryelements/General/dashboard-makeover-time-since-we-purchased-these-spices.json b/test/data/org_main-org/libraryelements/General/dashboard-makeover-time-since-we-purchased-these-spices.json similarity index 100% rename from test/data/org_1/libraryelements/General/dashboard-makeover-time-since-we-purchased-these-spices.json rename to test/data/org_main-org/libraryelements/General/dashboard-makeover-time-since-we-purchased-these-spices.json diff --git a/test/data/org_1/libraryelements/General/extreme-dashboard-makeover-grill.json b/test/data/org_main-org/libraryelements/General/extreme-dashboard-makeover-grill.json similarity index 100% rename from test/data/org_1/libraryelements/General/extreme-dashboard-makeover-grill.json rename to test/data/org_main-org/libraryelements/General/extreme-dashboard-makeover-grill.json diff --git a/test/data/org_1/libraryelements/General/extreme-dashboard-makeover-mac-oven.json b/test/data/org_main-org/libraryelements/General/extreme-dashboard-makeover-mac-oven.json similarity index 100% rename from test/data/org_1/libraryelements/General/extreme-dashboard-makeover-mac-oven.json rename to test/data/org_main-org/libraryelements/General/extreme-dashboard-makeover-mac-oven.json diff --git a/test/data/org_1/libraryelements/General/extreme-dashboard-makeover-refrigerator-temperature-f.json b/test/data/org_main-org/libraryelements/General/extreme-dashboard-makeover-refrigerator-temperature-f.json similarity index 100% rename from test/data/org_1/libraryelements/General/extreme-dashboard-makeover-refrigerator-temperature-f.json rename to test/data/org_main-org/libraryelements/General/extreme-dashboard-makeover-refrigerator-temperature-f.json diff --git a/test/data/org_1/libraryelements/General/extreme-dashboard-makeover-room-temperature-f.json b/test/data/org_main-org/libraryelements/General/extreme-dashboard-makeover-room-temperature-f.json similarity index 100% rename from test/data/org_1/libraryelements/General/extreme-dashboard-makeover-room-temperature-f.json rename to test/data/org_main-org/libraryelements/General/extreme-dashboard-makeover-room-temperature-f.json diff --git a/test/data/org_1/libraryelements/General/extreme-dashboard-makeover-salmon-cooking-times-past-7-days.json b/test/data/org_main-org/libraryelements/General/extreme-dashboard-makeover-salmon-cooking-times-past-7-days.json similarity index 100% rename from test/data/org_1/libraryelements/General/extreme-dashboard-makeover-salmon-cooking-times-past-7-days.json rename to test/data/org_main-org/libraryelements/General/extreme-dashboard-makeover-salmon-cooking-times-past-7-days.json diff --git a/test/data/org_1/teams/engineers/members.json b/test/data/org_main-org/teams/engineers/members.json similarity index 100% rename from test/data/org_1/teams/engineers/members.json rename to test/data/org_main-org/teams/engineers/members.json diff --git a/test/data/org_1/teams/engineers/team.json b/test/data/org_main-org/teams/engineers/team.json similarity index 100% rename from test/data/org_1/teams/engineers/team.json rename to test/data/org_main-org/teams/engineers/team.json diff --git a/test/data/org_1/teams/musicians/members.json b/test/data/org_main-org/teams/musicians/members.json similarity index 100% rename from test/data/org_1/teams/musicians/members.json rename to test/data/org_main-org/teams/musicians/members.json diff --git a/test/data/org_1/teams/musicians/team.json b/test/data/org_main-org/teams/musicians/team.json similarity index 100% rename from test/data/org_1/teams/musicians/team.json rename to test/data/org_main-org/teams/musicians/team.json diff --git a/test/organizations_integration_test.go b/test/organizations_integration_test.go index bc3c3066..2ecd6521 100644 --- a/test/organizations_integration_test.go +++ b/test/organizations_integration_test.go @@ -11,7 +11,7 @@ import ( "github.com/stretchr/testify/assert" ) -func TestOrgsCrud(t *testing.T) { +func TestOrganizationCrud(t *testing.T) { if testing.Short() { t.Skip("skipping integration test") } @@ -20,19 +20,23 @@ func TestOrgsCrud(t *testing.T) { } apiClient, _, cleanup := initTest(t, nil) defer cleanup() - orgs := apiClient.ListOrganizations() + orgs := apiClient.ListOrganizations(service.NewOrganizationFilter()) assert.Equal(t, len(orgs), 1) mainOrg := orgs[0] assert.Equal(t, mainOrg.ID, int64(1)) assert.Equal(t, mainOrg.Name, "Main Org.") - newOrgs := apiClient.UploadOrganizations() + newOrgs := apiClient.UploadOrganizations(service.NewOrganizationFilter()) assert.Equal(t, len(newOrgs), 2) assert.True(t, slices.Contains(newOrgs, "DumbDumb")) assert.True(t, slices.Contains(newOrgs, "Moo")) + //Filter Org + orgs = apiClient.ListOrganizations(service.NewOrganizationFilter("DumbDumb")) + assert.Equal(t, len(orgs), 1) + assert.Equal(t, orgs[0].Name, "DumbDumb") } -func TestOrgUserMembership(t *testing.T) { +func TestOrganizationUserMembership(t *testing.T) { if testing.Short() { t.Skip("skipping integration test") } @@ -42,8 +46,8 @@ func TestOrgUserMembership(t *testing.T) { apiClient, _, cleanup := initTest(t, nil) defer cleanup() //Create Orgs in case they aren't already present. - apiClient.UploadOrganizations() - orgs := apiClient.ListOrganizations() + apiClient.UploadOrganizations(service.NewOrganizationFilter()) + orgs := apiClient.ListOrganizations(service.NewOrganizationFilter()) sort.Slice(orgs, func(a, b int) bool { return orgs[a].ID < orgs[b].ID })