package tfe import ( "context" "errors" "fmt" "net/http" "net/url" "time" ) // Compile-time proof of interface implementation. var _ NotificationConfigurations = (*notificationConfigurations)(nil) // NotificationConfigurations describes all the Notification Configuration // related methods that the Terraform Enterprise API supports. // // TFE API docs: // https://www.terraform.io/docs/enterprise/api/notification-configurations.html type NotificationConfigurations interface { // List all the notification configurations within a workspace. List(ctx context.Context, workspaceID string, options NotificationConfigurationListOptions) (*NotificationConfigurationList, error) // Create a new notification configuration with the given options. Create(ctx context.Context, workspaceID string, options NotificationConfigurationCreateOptions) (*NotificationConfiguration, error) // Read a notification configuration by its ID. Read(ctx context.Context, notificationConfigurationID string) (*NotificationConfiguration, error) // Update an existing notification configuration. Update(ctx context.Context, notificationConfigurationID string, options NotificationConfigurationUpdateOptions) (*NotificationConfiguration, error) // Delete a notification configuration by its ID. Delete(ctx context.Context, notificationConfigurationID string) error // Verify a notification configuration by its ID. Verify(ctx context.Context, notificationConfigurationID string) (*NotificationConfiguration, error) } // notificationConfigurations implements NotificationConfigurations. type notificationConfigurations struct { client *Client } // List of available notification triggers. const ( NotificationTriggerCreated string = "run:created" NotificationTriggerPlanning string = "run:planning" NotificationTriggerNeedsAttention string = "run:needs_attention" NotificationTriggerApplying string = "run:applying" NotificationTriggerCompleted string = "run:completed" NotificationTriggerErrored string = "run:errored" ) // NotificationDestinationType represents the destination type of the // notification configuration. type NotificationDestinationType string // List of available notification destination types. const ( NotificationDestinationTypeEmail NotificationDestinationType = "email" NotificationDestinationTypeGeneric NotificationDestinationType = "generic" NotificationDestinationTypeSlack NotificationDestinationType = "slack" ) // NotificationConfigurationList represents a list of Notification // Configurations. type NotificationConfigurationList struct { *Pagination Items []*NotificationConfiguration } // NotificationConfiguration represents a Notification Configuration. type NotificationConfiguration struct { ID string `jsonapi:"primary,notification-configurations"` CreatedAt time.Time `jsonapi:"attr,created-at,iso8601"` DeliveryResponses []*DeliveryResponse `jsonapi:"attr,delivery-responses"` DestinationType NotificationDestinationType `jsonapi:"attr,destination-type"` Enabled bool `jsonapi:"attr,enabled"` Name string `jsonapi:"attr,name"` Token string `jsonapi:"attr,token"` Triggers []string `jsonapi:"attr,triggers"` UpdatedAt time.Time `jsonapi:"attr,updated-at,iso8601"` URL string `jsonapi:"attr,url"` // EmailAddresses is only available for TFE users. It is not available in TFC. EmailAddresses []string `jsonapi:"attr,email-addresses"` // Relations Subscribable *Workspace `jsonapi:"relation,subscribable"` EmailUsers []*User `jsonapi:"relation,users"` } // DeliveryResponse represents a notification configuration delivery response. type DeliveryResponse struct { Body string `json:"body"` Code int `json:"code"` Headers http.Header `json:"headers"` SentAt time.Time `json:"sent-at,iso8601"` Successful bool `json:"successful"` URL string `json:"url"` } // NotificationConfigurationListOptions represents the options for listing // notification configurations. type NotificationConfigurationListOptions struct { ListOptions } // List all the notification configurations associated with a workspace. func (s *notificationConfigurations) List(ctx context.Context, workspaceID string, options NotificationConfigurationListOptions) (*NotificationConfigurationList, error) { if !validStringID(&workspaceID) { return nil, errors.New("invalid value for workspace ID") } u := fmt.Sprintf("workspaces/%s/notification-configurations", url.QueryEscape(workspaceID)) req, err := s.client.newRequest("GET", u, options) if err != nil { return nil, err } ncl := &NotificationConfigurationList{} err = s.client.do(ctx, req, ncl) if err != nil { return nil, err } return ncl, nil } // NotificationConfigurationCreateOptions represents the options for // creating a new notification configuration. type NotificationConfigurationCreateOptions struct { // For internal use only! ID string `jsonapi:"primary,notification-configurations"` // The destination type of the notification configuration DestinationType *NotificationDestinationType `jsonapi:"attr,destination-type"` // Whether the notification configuration should be enabled or not Enabled *bool `jsonapi:"attr,enabled"` // The name of the notification configuration Name *string `jsonapi:"attr,name"` // The token of the notification configuration Token *string `jsonapi:"attr,token,omitempty"` // The list of run events that will trigger notifications. Triggers []string `jsonapi:"attr,triggers,omitempty"` // The url of the notification configuration URL *string `jsonapi:"attr,url,omitempty"` // The list of email addresses that will receive notification emails. // EmailAddresses is only available for TFE users. It is not available in TFC. EmailAddresses []string `jsonapi:"attr,email-addresses,omitempty"` // The list of users belonging to the organization that will receive notification emails. EmailUsers []*User `jsonapi:"relation,users,omitempty"` } func (o NotificationConfigurationCreateOptions) valid() error { if o.DestinationType == nil { return errors.New("destination type is required") } if o.Enabled == nil { return errors.New("enabled is required") } if !validString(o.Name) { return errors.New("name is required") } if *o.DestinationType == NotificationDestinationTypeGeneric || *o.DestinationType == NotificationDestinationTypeSlack { if o.URL == nil { return errors.New("url is required") } } return nil } // Creates a notification configuration with the given options. func (s *notificationConfigurations) Create(ctx context.Context, workspaceID string, options NotificationConfigurationCreateOptions) (*NotificationConfiguration, error) { if !validStringID(&workspaceID) { return nil, errors.New("invalid value for workspace ID") } if err := options.valid(); err != nil { return nil, err } // Make sure we don't send a user provided ID. options.ID = "" u := fmt.Sprintf("workspaces/%s/notification-configurations", url.QueryEscape(workspaceID)) req, err := s.client.newRequest("POST", u, &options) if err != nil { return nil, err } nc := &NotificationConfiguration{} err = s.client.do(ctx, req, nc) if err != nil { return nil, err } return nc, nil } // Read a notification configuration by its ID. func (s *notificationConfigurations) Read(ctx context.Context, notificationConfigurationID string) (*NotificationConfiguration, error) { if !validStringID(¬ificationConfigurationID) { return nil, errors.New("invalid value for notification configuration ID") } u := fmt.Sprintf("notification-configurations/%s", url.QueryEscape(notificationConfigurationID)) req, err := s.client.newRequest("GET", u, nil) if err != nil { return nil, err } nc := &NotificationConfiguration{} err = s.client.do(ctx, req, nc) if err != nil { return nil, err } return nc, nil } // NotificationConfigurationUpdateOptions represents the options for // updating a existing notification configuration. type NotificationConfigurationUpdateOptions struct { // For internal use only! ID string `jsonapi:"primary,notification-configurations"` // Whether the notification configuration should be enabled or not Enabled *bool `jsonapi:"attr,enabled,omitempty"` // The name of the notification configuration Name *string `jsonapi:"attr,name,omitempty"` // The token of the notification configuration Token *string `jsonapi:"attr,token,omitempty"` // The list of run events that will trigger notifications. Triggers []string `jsonapi:"attr,triggers,omitempty"` // The url of the notification configuration URL *string `jsonapi:"attr,url,omitempty"` // The list of email addresses that will receive notification emails. // EmailAddresses is only available for TFE users. It is not available in TFC. EmailAddresses []string `jsonapi:"attr,email-addresses,omitempty"` // The list of users belonging to the organization that will receive notification emails. EmailUsers []*User `jsonapi:"relation,users,omitempty"` } // Updates a notification configuration with the given options. func (s *notificationConfigurations) Update(ctx context.Context, notificationConfigurationID string, options NotificationConfigurationUpdateOptions) (*NotificationConfiguration, error) { if !validStringID(¬ificationConfigurationID) { return nil, errors.New("invalid value for notification configuration ID") } // Make sure we don't send a user provided ID. options.ID = "" u := fmt.Sprintf("notification-configurations/%s", url.QueryEscape(notificationConfigurationID)) req, err := s.client.newRequest("PATCH", u, &options) if err != nil { return nil, err } nc := &NotificationConfiguration{} err = s.client.do(ctx, req, nc) if err != nil { return nil, err } return nc, nil } // Delete a notifications configuration by its ID. func (s *notificationConfigurations) Delete(ctx context.Context, notificationConfigurationID string) error { if !validStringID(¬ificationConfigurationID) { return errors.New("invalid value for notification configuration ID") } u := fmt.Sprintf("notification-configurations/%s", url.QueryEscape(notificationConfigurationID)) req, err := s.client.newRequest("DELETE", u, nil) if err != nil { return err } return s.client.do(ctx, req, nil) } // Verifies a notification configuration by delivering a verification // payload to the configured url. func (s *notificationConfigurations) Verify(ctx context.Context, notificationConfigurationID string) (*NotificationConfiguration, error) { if !validStringID(¬ificationConfigurationID) { return nil, errors.New("invalid value for notification configuration ID") } u := fmt.Sprintf( "notification-configurations/%s/actions/verify", url.QueryEscape(notificationConfigurationID)) req, err := s.client.newRequest("POST", u, nil) if err != nil { return nil, err } nc := &NotificationConfiguration{} err = s.client.do(ctx, req, nc) if err != nil { return nil, err } return nc, nil }