Skip to content

Commit

Permalink
Scheduled searches (#165)
Browse files Browse the repository at this point in the history
  • Loading branch information
fjerlov-cs committed Jun 28, 2024
1 parent 284f2be commit 517aa79
Show file tree
Hide file tree
Showing 12 changed files with 686 additions and 7 deletions.
16 changes: 12 additions & 4 deletions api/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ import (
type EntityType string

const (
EntityTypeParser EntityType = "parser"
EntityTypeAction EntityType = "action"
EntityTypeAlert EntityType = "alert"
EntityTypeFilterAlert EntityType = "filter-alert"
EntityTypeParser EntityType = "parser"
EntityTypeAction EntityType = "action"
EntityTypeAlert EntityType = "alert"
EntityTypeFilterAlert EntityType = "filter-alert"
EntityTypeScheduledSearch EntityType = "scheduled-search"
)

func (e EntityType) String() string {
Expand Down Expand Up @@ -61,3 +62,10 @@ func FilterAlertNotFound(name string) error {
key: name,
}
}

func ScheduledSearchNotFound(name string) error {
return EntityNotFound{
entityType: EntityTypeScheduledSearch,
key: name,
}
}
4 changes: 1 addition & 3 deletions api/internal/humiographql/alerts.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,7 @@ type Alert struct {
Labels []graphql.String `graphql:"labels"`
LastError graphql.String `graphql:"lastError"`
QueryOwnership QueryOwnership `graphql:"queryOwnership"`
RunAsUser struct {
ID graphql.String `graphql:"id"`
} `graphql:"runAsUser"`
RunAsUser User `graphql:"runAsUser"`
}

type CreateAlert struct {
Expand Down
56 changes: 56 additions & 0 deletions api/internal/humiographql/scheduled-search.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package humiographql

import graphql "github.com/cli/shurcooL-graphql"

type ScheduledSearch struct {
ID graphql.String `json:"id"`
Name graphql.String `json:"name"`
Description graphql.String `json:"description,omitempty"`
QueryString graphql.String `json:"queryString"`
Start graphql.String `json:"start"`
End graphql.String `json:"end"`
TimeZone graphql.String `json:"timezone"`
Schedule graphql.String `json:"schedule"`
BackfillLimit graphql.Int `json:"backfillLimit"`
Enabled graphql.Boolean `json:"enabled"`
ActionsV2 []Action `json:"actionsV2"`
RunAsUser User `json:"runAsUser,omitempty"`
TimeOfNextPlannedExecution Long `json:"timeOfNextPlannedExecution"`
Labels []graphql.String `json:"labels"`
QueryOwnership QueryOwnership `json:"queryOwnership"`
}

type CreateScheduledSearch struct {
ViewName graphql.String `json:"viewName"`
Name graphql.String `json:"name"`
Description graphql.String `json:"description,omitempty"`
QueryString graphql.String `json:"queryString"`
QueryStart graphql.String `json:"queryStart"`
QueryEnd graphql.String `json:"queryEnd"`
Schedule graphql.String `json:"schedule"`
TimeZone graphql.String `json:"timeZone"`
BackfillLimit graphql.Int `json:"backfillLimit"`
Enabled graphql.Boolean `json:"enabled"`
ActionsIdsOrNames []graphql.String `json:"actions"`
Labels []graphql.String `json:"labels,omitempty"`
RunAsUserID graphql.String `json:"runAsUserId,omitempty"`
QueryOwnership QueryOwnershipType `json:"queryOwnershipType,omitempty"`
}

type UpdateScheduledSearch struct {
ViewName graphql.String `json:"viewName"`
ID graphql.String `json:"id"`
Name graphql.String `json:"name"`
Description graphql.String `json:"description,omitempty"`
QueryString graphql.String `json:"queryString"`
QueryStart graphql.String `json:"queryStart"`
QueryEnd graphql.String `json:"queryEnd"`
Schedule graphql.String `json:"schedule"`
TimeZone graphql.String `json:"timeZone"`
BackfillLimit graphql.Int `json:"backfillLimit"`
Enabled graphql.Boolean `json:"enabled"`
ActionsIdsOrNames []graphql.String `json:"actions"`
Labels []graphql.String `json:"labels"`
RunAsUserID graphql.String `json:"runAsUserId,omitempty"`
QueryOwnership QueryOwnershipType `json:"queryOwnershipType"`
}
7 changes: 7 additions & 0 deletions api/internal/humiographql/user.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package humiographql

import graphql "github.com/cli/shurcooL-graphql"

type User struct {
ID graphql.String `graphql:"id"`
}
242 changes: 242 additions & 0 deletions api/scheduled-search.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
package api

import (
"fmt"
graphql "github.com/cli/shurcooL-graphql"
"github.com/humio/cli/api/internal/humiographql"
)

type ScheduledSearch struct {
ID string `graphql:"id" yaml:"-" json:"id"`
Name string `graphql:"name" yaml:"name" json:"name"`
Description string `graphql:"description" yaml:"description,omitempty" json:"description,omitempty"`
QueryString string `graphql:"queryString" yaml:"queryString" json:"queryString"`
QueryStart string `graphql:"queryStart" yaml:"queryStart" json:"queryStart"`
QueryEnd string `graphql:"queryEnd" yaml:"queryEnd" json:"queryEnd"`
TimeZone string `graphql:"timeZone" yaml:"timeZone" json:"timeZone"`
Schedule string `graphql:"schedule" yaml:"schedule" json:"schedule"`
BackfillLimit int `graphql:"backfillLimit" yaml:"backfillLimit" json:"backfillLimit"`
Enabled bool `graphql:"enabled" yaml:"enabled" json:"enabled"`
ActionNames []string `graphql:"actionNames" yaml:"actionNames" json:"actionNames"`
RunAsUserID string `graphql:"runAsUserId" yaml:"runAsUserId,omitempty" json:"runAsUserId,omitempty"`
Labels []string `graphql:"labels" yaml:"labels" json:"labels"`
QueryOwnershipType string `graphql:"queryOwnership" yaml:"queryOwnershipType" json:"queryOwnershipType"`
}

type ScheduledSearches struct {
client *Client
}

func (c *Client) ScheduledSearches() *ScheduledSearches { return &ScheduledSearches{client: c} }

func (a *ScheduledSearches) List(viewName string) ([]ScheduledSearch, error) {
var query struct {
SearchDomain struct {
ScheduledSearches []humiographql.ScheduledSearch `graphql:"scheduledSearches"`
} `graphql:"searchDomain(name: $viewName)"`
}

variables := map[string]any{
"viewName": graphql.String(viewName),
}

err := a.client.Query(&query, variables)
if err != nil {
return nil, err
}

var scheduledSearches = make([]ScheduledSearch, len(query.SearchDomain.ScheduledSearches))
for i := range query.SearchDomain.ScheduledSearches {
scheduledSearches[i] = mapHumioGraphqlScheduledSearchToScheduledSearch(query.SearchDomain.ScheduledSearches[i])
}

return scheduledSearches, err
}

func (a *ScheduledSearches) Update(viewName string, updateScheduledSearch *ScheduledSearch) (*ScheduledSearch, error) {
if updateScheduledSearch == nil {
return nil, fmt.Errorf("updateScheduledSearch must not be nil")
}

if updateScheduledSearch.ID == "" {
return nil, fmt.Errorf("updateScheduledSearch must have non-empty ID")
}

var mutation struct {
humiographql.ScheduledSearch `graphql:"updateScheduledSearch(input: $input)"`
}

actionNames := make([]graphql.String, len(updateScheduledSearch.ActionNames))
for i, actionName := range updateScheduledSearch.ActionNames {
actionNames[i] = graphql.String(actionName)
}

labels := make([]graphql.String, len(updateScheduledSearch.Labels))
for i, label := range updateScheduledSearch.Labels {
labels[i] = graphql.String(label)
}

updateAlert := humiographql.UpdateScheduledSearch{
ViewName: graphql.String(viewName),
ID: graphql.String(updateScheduledSearch.ID),
Name: graphql.String(updateScheduledSearch.Name),
Description: graphql.String(updateScheduledSearch.Description),
QueryString: graphql.String(updateScheduledSearch.QueryString),
QueryStart: graphql.String(updateScheduledSearch.QueryStart),
QueryEnd: graphql.String(updateScheduledSearch.QueryEnd),
Schedule: graphql.String(updateScheduledSearch.Schedule),
TimeZone: graphql.String(updateScheduledSearch.TimeZone),
BackfillLimit: graphql.Int(updateScheduledSearch.BackfillLimit),
Enabled: graphql.Boolean(updateScheduledSearch.Enabled),
ActionsIdsOrNames: actionNames,
Labels: labels,
RunAsUserID: graphql.String(updateScheduledSearch.RunAsUserID),
QueryOwnership: humiographql.QueryOwnershipType(updateScheduledSearch.QueryOwnershipType),
}

variables := map[string]any{
"input": updateAlert,
}

err := a.client.Mutate(&mutation, variables)
if err != nil {
return nil, err
}

scheduledSearch := mapHumioGraphqlScheduledSearchToScheduledSearch(mutation.ScheduledSearch)

return &scheduledSearch, nil
}

func (a *ScheduledSearches) Create(viewName string, newScheduledSearch *ScheduledSearch) (*ScheduledSearch, error) {
if newScheduledSearch == nil {
return nil, fmt.Errorf("newScheduledSearch must not be nil")
}

var mutation struct {
humiographql.ScheduledSearch `graphql:"createScheduledSearch(input: $input)"`
}

actionNames := make([]graphql.String, len(newScheduledSearch.ActionNames))
for i, actionName := range newScheduledSearch.ActionNames {
actionNames[i] = graphql.String(actionName)
}

labels := make([]graphql.String, len(newScheduledSearch.Labels))
for i, label := range newScheduledSearch.Labels {
labels[i] = graphql.String(label)
}

createScheduledSearch := humiographql.CreateScheduledSearch{
ViewName: graphql.String(viewName),
Name: graphql.String(newScheduledSearch.Name),
Description: graphql.String(newScheduledSearch.Description),
QueryString: graphql.String(newScheduledSearch.QueryString),
QueryStart: graphql.String(newScheduledSearch.QueryStart),
QueryEnd: graphql.String(newScheduledSearch.QueryEnd),
Schedule: graphql.String(newScheduledSearch.Schedule),
TimeZone: graphql.String(newScheduledSearch.TimeZone),
BackfillLimit: graphql.Int(newScheduledSearch.BackfillLimit),
Enabled: graphql.Boolean(newScheduledSearch.Enabled),
ActionsIdsOrNames: actionNames,
Labels: labels,
RunAsUserID: graphql.String(newScheduledSearch.RunAsUserID),
QueryOwnership: humiographql.QueryOwnershipType(newScheduledSearch.QueryOwnershipType),
}

variables := map[string]any{
"input": createScheduledSearch,
}

err := a.client.Mutate(&mutation, variables)
if err != nil {
return nil, err
}

scheduledSearch := mapHumioGraphqlScheduledSearchToScheduledSearch(mutation.ScheduledSearch)

return &scheduledSearch, nil
}

func (a *ScheduledSearches) Delete(viewName, scheduledSearchID string) error {
if scheduledSearchID == "" {
return fmt.Errorf("scheduledSearchID is empty")
}

var mutation struct {
DidDelete bool `graphql:"deleteScheduledSearch(input: { viewName: $viewName, id: $id })"`
}

variables := map[string]any{
"viewName": graphql.String(viewName),
"id": graphql.String(scheduledSearchID),
}

err := a.client.Mutate(&mutation, variables)

if !mutation.DidDelete {
return fmt.Errorf("unable to remove scheduled search in repo/view '%s' with id '%s'", viewName, scheduledSearchID)
}

return err
}

func (a *ScheduledSearches) Get(viewName string, scheduledSearchId string) (*ScheduledSearch, error) {
var query struct {
SearchDomain struct {
ScheduledSearch humiographql.ScheduledSearch `graphql:"scheduledSearch(id: $scheduledSearchId)"`
} `graphql:"searchDomain(name: $viewName) "`
}

variables := map[string]any{
"viewName": graphql.String(viewName),
"scheduledSearchId": graphql.String(scheduledSearchId),
}

err := a.client.Query(&query, variables)
if err != nil {
return nil, err
}

scheduledSearch := mapHumioGraphqlScheduledSearchToScheduledSearch(query.SearchDomain.ScheduledSearch)

return &scheduledSearch, nil
}

func mapHumioGraphqlScheduledSearchToScheduledSearch(input humiographql.ScheduledSearch) ScheduledSearch {
var queryOwnershipType, runAsUserID string
switch input.QueryOwnership.QueryOwnershipTypeName {
case humiographql.QueryOwnershipTypeNameOrganization:
queryOwnershipType = QueryOwnershipTypeOrganization
case humiographql.QueryOwnershipTypeNameUser:
queryOwnershipType = QueryOwnershipTypeUser
runAsUserID = string(input.QueryOwnership.ID)
}

var actionNames = make([]string, len(input.ActionsV2))
for i := range input.ActionsV2 {
actionNames[i] = string(input.ActionsV2[i].Name)
}

var labels = make([]string, len(input.Labels))
for i := range input.Labels {
labels[i] = string(input.Labels[i])
}

return ScheduledSearch{
ID: string(input.ID),
Name: string(input.Name),
Description: string(input.Description),
QueryString: string(input.QueryString),
QueryStart: string(input.Start),
QueryEnd: string(input.End),
TimeZone: string(input.TimeZone),
Schedule: string(input.Schedule),
BackfillLimit: int(input.BackfillLimit),
ActionNames: actionNames,
Labels: labels,
Enabled: bool(input.Enabled),
QueryOwnershipType: queryOwnershipType,
RunAsUserID: runAsUserID,
}
}
1 change: 1 addition & 0 deletions cmd/humioctl/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ Common Management Commands:
rootCmd.AddCommand(newActionsCmd())
rootCmd.AddCommand(newAlertsCmd())
rootCmd.AddCommand(newFilterAlertsCmd())
rootCmd.AddCommand(newScheduledSearchesCmd())
rootCmd.AddCommand(newPackagesCmd())
rootCmd.AddCommand(newGroupsCmd())
rootCmd.AddCommand(newFilesCmd())
Expand Down
34 changes: 34 additions & 0 deletions cmd/humioctl/scheduled_searches.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright © 2024 CrowdStrike
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
"github.com/spf13/cobra"
)

func newScheduledSearchesCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "scheduled-searches",
Short: "Manage scheduled searches",
}

cmd.AddCommand(newScheduledSearchesListCmd())
cmd.AddCommand(newScheduledSearchesInstallCmd())
cmd.AddCommand(newScheduledSearchesExportCmd())
cmd.AddCommand(newScheduledSearchesRemoveCmd())
cmd.AddCommand(newScheduledSearchesShowCmd())

return cmd
}
Loading

0 comments on commit 517aa79

Please sign in to comment.