github.com/newrelic/newrelic-client-go@v1.1.0/pkg/alerts/policies.go (about)

     1  package alerts
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  
     7  	"github.com/newrelic/newrelic-client-go/pkg/errors"
     8  
     9  	"github.com/newrelic/newrelic-client-go/internal/http"
    10  	"github.com/newrelic/newrelic-client-go/internal/serialization"
    11  )
    12  
    13  // IncidentPreferenceType specifies rollup settings for alert policies.
    14  type IncidentPreferenceType string
    15  
    16  var (
    17  	// IncidentPreferenceTypes specifies the possible incident preferenece types for an alert policy.
    18  	IncidentPreferenceTypes = struct {
    19  		PerPolicy             IncidentPreferenceType
    20  		PerCondition          IncidentPreferenceType
    21  		PerConditionAndTarget IncidentPreferenceType
    22  	}{
    23  		PerPolicy:             "PER_POLICY",
    24  		PerCondition:          "PER_CONDITION",
    25  		PerConditionAndTarget: "PER_CONDITION_AND_TARGET",
    26  	}
    27  )
    28  
    29  // Policy represents a New Relic alert policy.
    30  type Policy struct {
    31  	ID                 int                      `json:"id,omitempty"`
    32  	IncidentPreference IncidentPreferenceType   `json:"incident_preference,omitempty"`
    33  	Name               string                   `json:"name,omitempty"`
    34  	CreatedAt          *serialization.EpochTime `json:"created_at,omitempty"`
    35  	UpdatedAt          *serialization.EpochTime `json:"updated_at,omitempty"`
    36  }
    37  
    38  // ListPoliciesParams represents a set of filters to be used when querying New
    39  // Relic alert policies.
    40  type ListPoliciesParams struct {
    41  	Name string `url:"filter[name],omitempty"`
    42  }
    43  
    44  // ListPolicies returns a list of Alert Policies for a given account.
    45  func (a *Alerts) ListPolicies(params *ListPoliciesParams) ([]Policy, error) {
    46  	return a.ListPoliciesWithContext(context.Background(), params)
    47  }
    48  
    49  // ListPoliciesWithContext returns a list of Alert Policies for a given account.
    50  func (a *Alerts) ListPoliciesWithContext(ctx context.Context, params *ListPoliciesParams) ([]Policy, error) {
    51  	alertPolicies := []Policy{}
    52  
    53  	nextURL := a.config.Region().RestURL("/alerts_policies.json")
    54  
    55  	for nextURL != "" {
    56  		response := alertPoliciesResponse{}
    57  		resp, err := a.client.GetWithContext(ctx, nextURL, &params, &response)
    58  
    59  		if err != nil {
    60  			return nil, err
    61  		}
    62  
    63  		alertPolicies = append(alertPolicies, response.Policies...)
    64  
    65  		paging := a.pager.Parse(resp)
    66  		nextURL = paging.Next
    67  	}
    68  
    69  	return alertPolicies, nil
    70  }
    71  
    72  // GetPolicy returns a specific alert policy by ID for a given account.
    73  func (a *Alerts) GetPolicy(id int) (*Policy, error) {
    74  	return a.GetPolicyWithContext(context.Background(), id)
    75  }
    76  
    77  // GetPolicyWithContext returns a specific alert policy by ID for a given account.
    78  func (a *Alerts) GetPolicyWithContext(ctx context.Context, id int) (*Policy, error) {
    79  	policies, err := a.ListPoliciesWithContext(ctx, nil)
    80  	if err != nil {
    81  		return nil, err
    82  	}
    83  
    84  	for _, policy := range policies {
    85  		if policy.ID == id {
    86  			return &policy, nil
    87  		}
    88  	}
    89  
    90  	return nil, errors.NewNotFoundf("no alert policy found for id %d", id)
    91  }
    92  
    93  // CreatePolicy creates a new alert policy for a given account.
    94  func (a *Alerts) CreatePolicy(policy Policy) (*Policy, error) {
    95  	return a.CreatePolicyWithContext(context.Background(), policy)
    96  }
    97  
    98  // CreatePolicyWithContext creates a new alert policy for a given account.
    99  func (a *Alerts) CreatePolicyWithContext(ctx context.Context, policy Policy) (*Policy, error) {
   100  	reqBody := alertPolicyRequestBody{
   101  		Policy: policy,
   102  	}
   103  	resp := alertPolicyResponse{}
   104  
   105  	_, err := a.client.PostWithContext(ctx, a.config.Region().RestURL("/alerts_policies.json"), nil, &reqBody, &resp)
   106  
   107  	if err != nil {
   108  		return nil, err
   109  	}
   110  
   111  	return &resp.Policy, nil
   112  }
   113  
   114  // UpdatePolicy update an alert policy for a given account.
   115  func (a *Alerts) UpdatePolicy(policy Policy) (*Policy, error) {
   116  	return a.UpdatePolicyWithContext(context.Background(), policy)
   117  }
   118  
   119  // UpdatePolicyWithContext update an alert policy for a given account.
   120  func (a *Alerts) UpdatePolicyWithContext(ctx context.Context, policy Policy) (*Policy, error) {
   121  	reqBody := alertPolicyRequestBody{
   122  		Policy: policy,
   123  	}
   124  	resp := alertPolicyResponse{}
   125  	url := fmt.Sprintf("/alerts_policies/%d.json", policy.ID)
   126  
   127  	_, err := a.client.PutWithContext(ctx, a.config.Region().RestURL(url), nil, &reqBody, &resp)
   128  	if err != nil {
   129  		return nil, err
   130  	}
   131  
   132  	return &resp.Policy, nil
   133  }
   134  
   135  // DeletePolicy deletes an existing alert policy for a given account.
   136  func (a *Alerts) DeletePolicy(id int) (*Policy, error) {
   137  	return a.DeletePolicyWithContext(context.Background(), id)
   138  }
   139  
   140  // DeletePolicyWithContext deletes an existing alert policy for a given account.
   141  func (a *Alerts) DeletePolicyWithContext(ctx context.Context, id int) (*Policy, error) {
   142  	resp := alertPolicyResponse{}
   143  	url := fmt.Sprintf("/alerts_policies/%d.json", id)
   144  
   145  	_, err := a.client.DeleteWithContext(ctx, a.config.Region().RestURL(url), nil, &resp)
   146  	if err != nil {
   147  		return nil, err
   148  	}
   149  
   150  	return &resp.Policy, nil
   151  }
   152  
   153  func (a *Alerts) CreatePolicyMutation(accountID int, policy AlertsPolicyInput) (*AlertsPolicy, error) {
   154  	return a.CreatePolicyMutationWithContext(context.Background(), accountID, policy)
   155  }
   156  
   157  func (a *Alerts) CreatePolicyMutationWithContext(ctx context.Context, accountID int, policy AlertsPolicyInput) (*AlertsPolicy, error) {
   158  	vars := map[string]interface{}{
   159  		"accountID": accountID,
   160  		"policy":    policy,
   161  	}
   162  
   163  	resp := alertQueryPolicyCreateResponse{}
   164  
   165  	if err := a.client.NerdGraphQueryWithContext(ctx, alertsPolicyCreatePolicy, vars, &resp); err != nil {
   166  		return nil, err
   167  	}
   168  
   169  	return &resp.AlertsPolicy, nil
   170  }
   171  
   172  func (a *Alerts) UpdatePolicyMutation(accountID int, policyID string, policy AlertsPolicyUpdateInput) (*AlertsPolicy, error) {
   173  	return a.UpdatePolicyMutationWithContext(context.Background(), accountID, policyID, policy)
   174  }
   175  
   176  func (a *Alerts) UpdatePolicyMutationWithContext(ctx context.Context, accountID int, policyID string, policy AlertsPolicyUpdateInput) (*AlertsPolicy, error) {
   177  	vars := map[string]interface{}{
   178  		"accountID": accountID,
   179  		"policyID":  policyID,
   180  		"policy":    policy,
   181  	}
   182  
   183  	resp := alertQueryPolicyUpdateResponse{}
   184  
   185  	if err := a.client.NerdGraphQueryWithContext(ctx, alertsPolicyUpdatePolicy, vars, &resp); err != nil {
   186  		return nil, err
   187  	}
   188  
   189  	return &resp.AlertsPolicy, nil
   190  }
   191  
   192  // QueryPolicy queries NerdGraph for a policy matching the given account ID and
   193  // policy ID.
   194  func (a *Alerts) QueryPolicy(accountID int, id string) (*AlertsPolicy, error) {
   195  	return a.QueryPolicyWithContext(context.Background(), accountID, id)
   196  }
   197  
   198  // QueryPolicyWithContext queries NerdGraph for a policy matching the given account ID and
   199  // policy ID.
   200  func (a *Alerts) QueryPolicyWithContext(ctx context.Context, accountID int, id string) (*AlertsPolicy, error) {
   201  	resp := alertQueryPolicyResponse{}
   202  	vars := map[string]interface{}{
   203  		"accountID": accountID,
   204  		"policyID":  id,
   205  	}
   206  
   207  	req, err := a.client.NewNerdGraphRequest(alertPolicyQueryPolicy, vars, &resp)
   208  	if err != nil {
   209  		return nil, err
   210  	}
   211  
   212  	req.WithContext(ctx)
   213  
   214  	var errorResponse alertPoliciesErrorResponse
   215  	req.SetErrorValue(&errorResponse)
   216  
   217  	if _, err := a.client.Do(req); err != nil {
   218  		return nil, err
   219  	}
   220  
   221  	return &resp.Actor.Account.Alerts.Policy, nil
   222  }
   223  
   224  // QueryPolicySearch searches NerdGraph for policies.
   225  func (a *Alerts) QueryPolicySearch(accountID int, params AlertsPoliciesSearchCriteriaInput) ([]*AlertsPolicy, error) {
   226  	return a.QueryPolicySearchWithContext(context.Background(), accountID, params)
   227  }
   228  
   229  // QueryPolicySearchWithContext searches NerdGraph for policies.
   230  func (a *Alerts) QueryPolicySearchWithContext(ctx context.Context, accountID int, params AlertsPoliciesSearchCriteriaInput) ([]*AlertsPolicy, error) {
   231  	policies := []*AlertsPolicy{}
   232  	var nextCursor *string
   233  
   234  	for ok := true; ok; ok = nextCursor != nil {
   235  		resp := alertQueryPolicySearchResponse{}
   236  		vars := map[string]interface{}{
   237  			"accountID":      accountID,
   238  			"cursor":         nextCursor,
   239  			"searchCriteria": params,
   240  		}
   241  
   242  		if err := a.client.NerdGraphQueryWithContext(ctx, alertsPolicyQuerySearch, vars, &resp); err != nil {
   243  			return nil, err
   244  		}
   245  
   246  		policies = append(policies, resp.Actor.Account.Alerts.PoliciesSearch.Policies...)
   247  
   248  		nextCursor = resp.Actor.Account.Alerts.PoliciesSearch.NextCursor
   249  	}
   250  
   251  	return policies, nil
   252  }
   253  
   254  // DeletePolicyMutation is the NerdGraph mutation to delete a policy given the
   255  // account ID and the policy ID.
   256  func (a *Alerts) DeletePolicyMutation(accountID int, id string) (*AlertsPolicy, error) {
   257  	return a.DeletePolicyMutationWithContext(context.Background(), accountID, id)
   258  }
   259  
   260  // DeletePolicyMutationWithContext is the NerdGraph mutation to delete a policy given the
   261  // account ID and the policy ID.
   262  func (a *Alerts) DeletePolicyMutationWithContext(ctx context.Context, accountID int, id string) (*AlertsPolicy, error) {
   263  	policy := &AlertsPolicy{}
   264  
   265  	resp := alertQueryPolicyDeleteRespose{}
   266  	vars := map[string]interface{}{
   267  		"accountID": accountID,
   268  		"policyID":  id,
   269  	}
   270  
   271  	if err := a.client.NerdGraphQueryWithContext(ctx, alertPolicyDeletePolicy, vars, &resp); err != nil {
   272  		return nil, err
   273  	}
   274  
   275  	return policy, nil
   276  }
   277  
   278  type alertPoliciesErrorResponse struct {
   279  	http.GraphQLErrorResponse
   280  }
   281  
   282  func (r *alertPoliciesErrorResponse) IsNotFound() bool {
   283  	if len(r.Errors) == 0 {
   284  		return false
   285  	}
   286  
   287  	for _, err := range r.Errors {
   288  		if err.Message == "Not Found" &&
   289  			// TODO: When the alerts API begins using `errorClass`
   290  			// instead of `code` to specify error type, the conditional
   291  			// checking the `code` field can be removed.
   292  			//
   293  			// https://newrelic.atlassian.net/browse/AINTER-7746
   294  			(err.Extensions.Code == "BAD_USER_INPUT" || err.Extensions.ErrorClass == "BAD_USER_INPUT") {
   295  			return true
   296  		}
   297  	}
   298  
   299  	return false
   300  }
   301  
   302  func (r *alertPoliciesErrorResponse) Error() string {
   303  	return r.GraphQLErrorResponse.Error()
   304  }
   305  
   306  func (r *alertPoliciesErrorResponse) New() http.ErrorResponse {
   307  	return &alertPoliciesErrorResponse{}
   308  }
   309  
   310  type alertPoliciesResponse struct {
   311  	Policies []Policy `json:"policies,omitempty"`
   312  }
   313  
   314  type alertPolicyResponse struct {
   315  	Policy Policy `json:"policy,omitempty"`
   316  }
   317  
   318  type alertPolicyRequestBody struct {
   319  	Policy Policy `json:"policy"`
   320  }
   321  
   322  type alertQueryPolicySearchResponse struct {
   323  	Actor struct {
   324  		Account struct {
   325  			Alerts struct {
   326  				PoliciesSearch struct {
   327  					NextCursor *string         `json:"nextCursor"`
   328  					Policies   []*AlertsPolicy `json:"policies"`
   329  					TotalCount int             `json:"totalCount"`
   330  				} `json:"policiesSearch"`
   331  			} `json:"alerts"`
   332  		} `json:"account"`
   333  	} `json:"actor"`
   334  }
   335  
   336  type alertQueryPolicyCreateResponse struct {
   337  	AlertsPolicy AlertsPolicy `json:"alertsPolicyCreate"`
   338  }
   339  
   340  type alertQueryPolicyUpdateResponse struct {
   341  	AlertsPolicy AlertsPolicy `json:"alertsPolicyUpdate"`
   342  }
   343  
   344  type alertQueryPolicyResponse struct {
   345  	Actor struct {
   346  		Account struct {
   347  			Alerts struct {
   348  				Policy AlertsPolicy `json:"policy"`
   349  			} `json:"alerts"`
   350  		} `json:"account"`
   351  	} `json:"actor"`
   352  }
   353  
   354  type alertQueryPolicyDeleteRespose struct {
   355  	AlertsPolicyDelete struct {
   356  		ID int `json:"id,string"`
   357  	} `json:"alertsPolicyDelete"`
   358  }
   359  
   360  const (
   361  	graphqlAlertPolicyFields = `
   362  						id
   363  						name
   364  						incidentPreference
   365  						accountId
   366  	`
   367  	alertPolicyQueryPolicy = `query($accountID: Int!, $policyID: ID!) {
   368  		actor {
   369  			account(id: $accountID) {
   370  				alerts {
   371  					policy(id: $policyID) {` + graphqlAlertPolicyFields + `
   372  					}
   373  				}
   374  			}
   375  		}
   376  	}`
   377  
   378  	alertsPolicyQuerySearch = `query($accountID: Int!, $cursor: String, $criteria: AlertsPoliciesSearchCriteriaInput) {
   379  		actor {
   380  			account(id: $accountID) {
   381  				alerts {
   382  					policiesSearch(cursor: $cursor, searchCriteria: $criteria) {
   383  						nextCursor
   384  						totalCount
   385  						policies {
   386  							accountId
   387  							id
   388  							incidentPreference
   389  							name
   390  						}
   391  					}
   392  				}
   393  			}
   394  		}
   395  	}`
   396  
   397  	alertsPolicyCreatePolicy = `mutation CreatePolicy($accountID: Int!, $policy: AlertsPolicyInput!){
   398  		alertsPolicyCreate(accountId: $accountID, policy: $policy) {` + graphqlAlertPolicyFields + `
   399  		} }`
   400  
   401  	alertsPolicyUpdatePolicy = `mutation UpdatePolicy($accountID: Int!, $policyID: ID!, $policy: AlertsPolicyUpdateInput!){
   402  			alertsPolicyUpdate(accountId: $accountID, id: $policyID, policy: $policy) {` + graphqlAlertPolicyFields + `
   403  			}
   404  		}`
   405  
   406  	alertPolicyDeletePolicy = `mutation DeletePolicy($accountID: Int!, $policyID: ID!){
   407  		alertsPolicyDelete(accountId: $accountID, id: $policyID) {
   408  			id
   409  		} }`
   410  )