From 70a7e6f8ace1bda87217656e62806727a12423db Mon Sep 17 00:00:00 2001 From: Mike Rostermund Date: Mon, 12 Aug 2024 15:04:04 +0200 Subject: [PATCH 1/3] Add EntityNotFound for ingest tokens --- api/actions.go | 2 +- api/error.go | 8 ++++++++ api/ingest-tokens.go | 3 +-- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/api/actions.go b/api/actions.go index cf80a8d5..95c71293 100644 --- a/api/actions.go +++ b/api/actions.go @@ -832,7 +832,7 @@ func (n *Actions) Delete(viewName, actionName string) error { } } if actionID == "" { - return fmt.Errorf("unable to find action") + return ActionNotFound(actionID) } var mutation struct { diff --git a/api/error.go b/api/error.go index a33c2ba6..7432abb0 100644 --- a/api/error.go +++ b/api/error.go @@ -7,6 +7,7 @@ import ( type EntityType string const ( + EntityTypeIngestToken EntityType = "ingest-token" EntityTypeParser EntityType = "parser" EntityTypeAction EntityType = "action" EntityTypeAlert EntityType = "alert" @@ -36,6 +37,13 @@ func (e EntityNotFound) Error() string { return fmt.Sprintf("%s %q not found", e.entityType.String(), e.key) } +func IngestTokenNotFound(name string) error { + return EntityNotFound{ + entityType: EntityTypeIngestToken, + key: name, + } +} + func ParserNotFound(name string) error { return EntityNotFound{ entityType: EntityTypeParser, diff --git a/api/ingest-tokens.go b/api/ingest-tokens.go index 470db50e..28da72ac 100644 --- a/api/ingest-tokens.go +++ b/api/ingest-tokens.go @@ -1,7 +1,6 @@ package api import ( - "fmt" graphql "github.com/cli/shurcooL-graphql" ) @@ -61,7 +60,7 @@ func (i *IngestTokens) Get(repoName, tokenName string) (*IngestToken, error) { } } - return nil, fmt.Errorf("could not find an ingest token with name '%s' in repo '%s'", tokenName, repoName) + return nil, IngestTokenNotFound(tokenName) } func toIngestToken(data ingestTokenData) *IngestToken { From eafdd46e889a4b4a0cd2d1eaca2365095f4e3644 Mon Sep 17 00:00:00 2001 From: Mike Rostermund Date: Mon, 12 Aug 2024 16:23:40 +0200 Subject: [PATCH 2/3] Add RepositoryNotFound and ViewNotFound responses to Get() calls --- api/error.go | 16 ++++++++++++++++ api/repositories.go | 3 +-- api/views.go | 2 +- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/api/error.go b/api/error.go index 7432abb0..1c249d10 100644 --- a/api/error.go +++ b/api/error.go @@ -7,6 +7,8 @@ import ( type EntityType string const ( + EntityTypeRepository EntityType = "repository" + EntityTypeView EntityType = "view" EntityTypeIngestToken EntityType = "ingest-token" EntityTypeParser EntityType = "parser" EntityTypeAction EntityType = "action" @@ -37,6 +39,20 @@ func (e EntityNotFound) Error() string { return fmt.Sprintf("%s %q not found", e.entityType.String(), e.key) } +func RepositoryNotFound(name string) error { + return EntityNotFound{ + entityType: EntityTypeRepository, + key: name, + } +} + +func ViewNotFound(name string) error { + return EntityNotFound{ + entityType: EntityTypeView, + key: name, + } +} + func IngestTokenNotFound(name string) error { return EntityNotFound{ entityType: EntityTypeIngestToken, diff --git a/api/repositories.go b/api/repositories.go index a37b70d6..351bda3a 100644 --- a/api/repositories.go +++ b/api/repositories.go @@ -38,8 +38,7 @@ func (r *Repositories) Get(name string) (Repository, error) { err := r.client.Query(&query, variables) if err != nil { - // The graphql error message is vague if the repo already exists, so add a hint. - return query.Repository, fmt.Errorf("%w. Does the repo already exist?", err) + return query.Repository, RepositoryNotFound(name) } return query.Repository, nil diff --git a/api/views.go b/api/views.go index 7f058b9b..7f0dc73c 100644 --- a/api/views.go +++ b/api/views.go @@ -48,7 +48,7 @@ func (c *Views) Get(name string) (*View, error) { err := c.client.Query(&query, variables) if err != nil { - return nil, err + return nil, ViewNotFound(name) } connections := make([]ViewConnection, len(query.Result.ViewInfo.Connections)) From aacdf44666ce34e1340b0576ec4c5abb74003924 Mon Sep 17 00:00:00 2001 From: Mike Rostermund Date: Wed, 14 Aug 2024 12:39:29 +0200 Subject: [PATCH 3/3] Ensure view actions validate type --- api/error.go | 8 +++ api/internal/humiographql/searchdomains.go | 7 +++ api/repositories.go | 15 +++++ api/searchdomains.go | 70 ++++++++++++++++++++++ api/views.go | 33 +++++++++- 5 files changed, 130 insertions(+), 3 deletions(-) create mode 100644 api/internal/humiographql/searchdomains.go create mode 100644 api/searchdomains.go diff --git a/api/error.go b/api/error.go index 1c249d10..9a78cb34 100644 --- a/api/error.go +++ b/api/error.go @@ -7,6 +7,7 @@ import ( type EntityType string const ( + EntityTypeSearchDomain EntityType = "search-domain" EntityTypeRepository EntityType = "repository" EntityTypeView EntityType = "view" EntityTypeIngestToken EntityType = "ingest-token" @@ -39,6 +40,13 @@ func (e EntityNotFound) Error() string { return fmt.Sprintf("%s %q not found", e.entityType.String(), e.key) } +func SearchDomainNotFound(name string) error { + return EntityNotFound{ + entityType: EntityTypeSearchDomain, + key: name, + } +} + func RepositoryNotFound(name string) error { return EntityNotFound{ entityType: EntityTypeRepository, diff --git a/api/internal/humiographql/searchdomains.go b/api/internal/humiographql/searchdomains.go new file mode 100644 index 00000000..a0dc9b95 --- /dev/null +++ b/api/internal/humiographql/searchdomains.go @@ -0,0 +1,7 @@ +package humiographql + +import graphql "github.com/cli/shurcooL-graphql" + +const ( + SearchDomainTypeView graphql.String = "View" +) diff --git a/api/repositories.go b/api/repositories.go index 351bda3a..753107bd 100644 --- a/api/repositories.go +++ b/api/repositories.go @@ -80,6 +80,11 @@ func (r *Repositories) Create(name string) error { } func (r *Repositories) Delete(name, reason string, allowDataDeletion bool) error { + _, err := r.Get(name) + if err != nil { + return err + } + if !allowDataDeletion { return fmt.Errorf("repository may contain data and data deletion not enabled") } @@ -231,6 +236,11 @@ func (r *Repositories) UpdateIngestBasedRetention(name string, ingestInGB float6 } func (r *Repositories) UpdateDescription(name, description string) error { + _, err := r.Get(name) + if err != nil { + return err + } + var mutation struct { UpdateDescription struct { // We have to make a selection, so just take __typename @@ -331,6 +341,11 @@ func (r *Repositories) UpdateS3ArchivingConfiguration(name string, bucket string } func (r *Repositories) UpdateAutomaticSearch(name string, automaticSearch bool) error { + _, err := r.Get(name) + if err != nil { + return err + } + var mutation struct { SetAutomaticSearching struct { // We have to make a selection, so just take __typename diff --git a/api/searchdomains.go b/api/searchdomains.go new file mode 100644 index 00000000..2dcb8e11 --- /dev/null +++ b/api/searchdomains.go @@ -0,0 +1,70 @@ +package api + +import ( + "sort" + "strings" + + graphql "github.com/cli/shurcooL-graphql" +) + +type SearchDomains struct { + client *Client +} + +type SearchDomainsQueryData struct { + Name string + Description string + AutomaticSearch bool + Typename graphql.String `graphql:"__typename"` +} + +type SearchDomain struct { + Name string + Description string + AutomaticSearch bool +} + +func (s *Client) SearchDomains() *SearchDomains { return &SearchDomains{client: s} } + +func (s *SearchDomains) Get(name string) (*SearchDomain, error) { + var query struct { + Result SearchDomainsQueryData `graphql:"searchDomain(name: $name)"` + } + + variables := map[string]interface{}{ + "name": graphql.String(name), + } + + err := s.client.Query(&query, variables) + if err != nil { + return nil, SearchDomainNotFound(name) + } + + searchDomain := SearchDomain{ + Name: query.Result.Name, + Description: query.Result.Description, + AutomaticSearch: query.Result.AutomaticSearch, + } + + return &searchDomain, nil +} + +type SearchDomainListItem struct { + Name string + Typename string `graphql:"__typename"` + AutomaticSearch bool +} + +func (s *SearchDomains) List() ([]SearchDomainListItem, error) { + var query struct { + SearchDomain []SearchDomainListItem `graphql:"searchDomains"` + } + + err := s.client.Query(&query, nil) + + sort.Slice(query.SearchDomain, func(i, j int) bool { + return strings.ToLower(query.SearchDomain[i].Name) < strings.ToLower(query.SearchDomain[j].Name) + }) + + return query.SearchDomain, err +} diff --git a/api/views.go b/api/views.go index 7f0dc73c..6a9c5ed4 100644 --- a/api/views.go +++ b/api/views.go @@ -1,6 +1,7 @@ package api import ( + "github.com/humio/cli/api/internal/humiographql" "sort" "strings" @@ -26,6 +27,7 @@ type ViewQueryData struct { Filter string } } `graphql:"... on View"` + Typename graphql.String `graphql:"__typename"` } type View struct { @@ -50,6 +52,9 @@ func (c *Views) Get(name string) (*View, error) { if err != nil { return nil, ViewNotFound(name) } + if query.Result.Typename != "View" { + return nil, ViewNotFound("name") + } connections := make([]ViewConnection, len(query.Result.ViewInfo.Connections)) for i, data := range query.Result.ViewInfo.Connections { @@ -82,11 +87,18 @@ func (c *Views) List() ([]ViewListItem, error) { err := c.client.Query(&query, nil) - sort.Slice(query.View, func(i, j int) bool { - return strings.ToLower(query.View[i].Name) < strings.ToLower(query.View[j].Name) + viewsList := []ViewListItem{} + for k, v := range query.View { + if v.Typename == string(humiographql.SearchDomainTypeView) { + viewsList = append(viewsList, query.View[k]) + } + } + + sort.Slice(viewsList, func(i, j int) bool { + return strings.ToLower(viewsList[i].Name) < strings.ToLower(viewsList[j].Name) }) - return query.View, err + return viewsList, err } type ViewConnectionInput struct { @@ -112,6 +124,11 @@ func (c *Views) Create(name, description string, connections []ViewConnectionInp } func (c *Views) Delete(name, reason string) error { + _, err := c.Get(name) + if err != nil { + return err + } + var mutation struct { DeleteSearchDomain struct { // We have to make a selection, so just take __typename @@ -142,6 +159,11 @@ func (c *Views) UpdateConnections(name string, connections []ViewConnectionInput } func (c *Views) UpdateDescription(name string, description string) error { + _, err := c.Get(name) + if err != nil { + return err + } + var mutation struct { UpdateDescriptionMutation struct { // We have to make a selection, so just take __typename @@ -158,6 +180,11 @@ func (c *Views) UpdateDescription(name string, description string) error { } func (c *Views) UpdateAutomaticSearch(name string, automaticSearch bool) error { + _, err := c.Get(name) + if err != nil { + return err + } + var mutation struct { SetAutomaticSearching struct { // We have to make a selection, so just take __typename