1package tfe 2 3import ( 4 "context" 5 "errors" 6 "fmt" 7 "net/http" 8 "net/url" 9 "time" 10) 11 12// Compile-time proof of interface implementation. 13var _ NotificationConfigurations = (*notificationConfigurations)(nil) 14 15// NotificationConfigurations describes all the Notification Configuration 16// related methods that the Terraform Enterprise API supports. 17// 18// TFE API docs: 19// https://www.terraform.io/docs/enterprise/api/notification-configurations.html 20type NotificationConfigurations interface { 21 // List all the notification configurations within a workspace. 22 List(ctx context.Context, workspaceID string, options NotificationConfigurationListOptions) (*NotificationConfigurationList, error) 23 24 // Create a new notification configuration with the given options. 25 Create(ctx context.Context, workspaceID string, options NotificationConfigurationCreateOptions) (*NotificationConfiguration, error) 26 27 // Read a notification configuration by its ID. 28 Read(ctx context.Context, notificationConfigurationID string) (*NotificationConfiguration, error) 29 30 // Update an existing notification configuration. 31 Update(ctx context.Context, notificationConfigurationID string, options NotificationConfigurationUpdateOptions) (*NotificationConfiguration, error) 32 33 // Delete a notification configuration by its ID. 34 Delete(ctx context.Context, notificationConfigurationID string) error 35 36 // Verify a notification configuration by its ID. 37 Verify(ctx context.Context, notificationConfigurationID string) (*NotificationConfiguration, error) 38} 39 40// notificationConfigurations implements NotificationConfigurations. 41type notificationConfigurations struct { 42 client *Client 43} 44 45// List of available notification triggers. 46const ( 47 NotificationTriggerCreated string = "run:created" 48 NotificationTriggerPlanning string = "run:planning" 49 NotificationTriggerNeedsAttention string = "run:needs_attention" 50 NotificationTriggerApplying string = "run:applying" 51 NotificationTriggerCompleted string = "run:completed" 52 NotificationTriggerErrored string = "run:errored" 53) 54 55// NotificationDestinationType represents the destination type of the 56// notification configuration. 57type NotificationDestinationType string 58 59// List of available notification destination types. 60const ( 61 NotificationDestinationTypeEmail NotificationDestinationType = "email" 62 NotificationDestinationTypeGeneric NotificationDestinationType = "generic" 63 NotificationDestinationTypeSlack NotificationDestinationType = "slack" 64) 65 66// NotificationConfigurationList represents a list of Notification 67// Configurations. 68type NotificationConfigurationList struct { 69 *Pagination 70 Items []*NotificationConfiguration 71} 72 73// NotificationConfiguration represents a Notification Configuration. 74type NotificationConfiguration struct { 75 ID string `jsonapi:"primary,notification-configurations"` 76 CreatedAt time.Time `jsonapi:"attr,created-at,iso8601"` 77 DeliveryResponses []*DeliveryResponse `jsonapi:"attr,delivery-responses"` 78 DestinationType NotificationDestinationType `jsonapi:"attr,destination-type"` 79 Enabled bool `jsonapi:"attr,enabled"` 80 Name string `jsonapi:"attr,name"` 81 Token string `jsonapi:"attr,token"` 82 Triggers []string `jsonapi:"attr,triggers"` 83 UpdatedAt time.Time `jsonapi:"attr,updated-at,iso8601"` 84 URL string `jsonapi:"attr,url"` 85 86 // EmailAddresses is only available for TFE users. It is not available in TFC. 87 EmailAddresses []string `jsonapi:"attr,email-addresses"` 88 89 // Relations 90 Subscribable *Workspace `jsonapi:"relation,subscribable"` 91 EmailUsers []*User `jsonapi:"relation,users"` 92} 93 94// DeliveryResponse represents a notification configuration delivery response. 95type DeliveryResponse struct { 96 Body string `json:"body"` 97 Code int `json:"code"` 98 Headers http.Header `json:"headers"` 99 SentAt time.Time `json:"sent-at,iso8601"` 100 Successful bool `json:"successful"` 101 URL string `json:"url"` 102} 103 104// NotificationConfigurationListOptions represents the options for listing 105// notification configurations. 106type NotificationConfigurationListOptions struct { 107 ListOptions 108} 109 110// List all the notification configurations associated with a workspace. 111func (s *notificationConfigurations) List(ctx context.Context, workspaceID string, options NotificationConfigurationListOptions) (*NotificationConfigurationList, error) { 112 if !validStringID(&workspaceID) { 113 return nil, errors.New("invalid value for workspace ID") 114 } 115 116 u := fmt.Sprintf("workspaces/%s/notification-configurations", url.QueryEscape(workspaceID)) 117 req, err := s.client.newRequest("GET", u, options) 118 if err != nil { 119 return nil, err 120 } 121 122 ncl := &NotificationConfigurationList{} 123 err = s.client.do(ctx, req, ncl) 124 if err != nil { 125 return nil, err 126 } 127 128 return ncl, nil 129} 130 131// NotificationConfigurationCreateOptions represents the options for 132// creating a new notification configuration. 133type NotificationConfigurationCreateOptions struct { 134 // For internal use only! 135 ID string `jsonapi:"primary,notification-configurations"` 136 137 // The destination type of the notification configuration 138 DestinationType *NotificationDestinationType `jsonapi:"attr,destination-type"` 139 140 // Whether the notification configuration should be enabled or not 141 Enabled *bool `jsonapi:"attr,enabled"` 142 143 // The name of the notification configuration 144 Name *string `jsonapi:"attr,name"` 145 146 // The token of the notification configuration 147 Token *string `jsonapi:"attr,token,omitempty"` 148 149 // The list of run events that will trigger notifications. 150 Triggers []string `jsonapi:"attr,triggers,omitempty"` 151 152 // The url of the notification configuration 153 URL *string `jsonapi:"attr,url,omitempty"` 154 155 // The list of email addresses that will receive notification emails. 156 // EmailAddresses is only available for TFE users. It is not available in TFC. 157 EmailAddresses []string `jsonapi:"attr,email-addresses,omitempty"` 158 159 // The list of users belonging to the organization that will receive notification emails. 160 EmailUsers []*User `jsonapi:"relation,users,omitempty"` 161} 162 163func (o NotificationConfigurationCreateOptions) valid() error { 164 if o.DestinationType == nil { 165 return errors.New("destination type is required") 166 } 167 if o.Enabled == nil { 168 return errors.New("enabled is required") 169 } 170 if !validString(o.Name) { 171 return errors.New("name is required") 172 } 173 174 if *o.DestinationType == NotificationDestinationTypeGeneric || *o.DestinationType == NotificationDestinationTypeSlack { 175 if o.URL == nil { 176 return errors.New("url is required") 177 } 178 } 179 return nil 180} 181 182// Creates a notification configuration with the given options. 183func (s *notificationConfigurations) Create(ctx context.Context, workspaceID string, options NotificationConfigurationCreateOptions) (*NotificationConfiguration, error) { 184 if !validStringID(&workspaceID) { 185 return nil, errors.New("invalid value for workspace ID") 186 } 187 if err := options.valid(); err != nil { 188 return nil, err 189 } 190 191 // Make sure we don't send a user provided ID. 192 options.ID = "" 193 194 u := fmt.Sprintf("workspaces/%s/notification-configurations", url.QueryEscape(workspaceID)) 195 req, err := s.client.newRequest("POST", u, &options) 196 if err != nil { 197 return nil, err 198 } 199 200 nc := &NotificationConfiguration{} 201 err = s.client.do(ctx, req, nc) 202 if err != nil { 203 return nil, err 204 } 205 206 return nc, nil 207} 208 209// Read a notification configuration by its ID. 210func (s *notificationConfigurations) Read(ctx context.Context, notificationConfigurationID string) (*NotificationConfiguration, error) { 211 if !validStringID(¬ificationConfigurationID) { 212 return nil, errors.New("invalid value for notification configuration ID") 213 } 214 215 u := fmt.Sprintf("notification-configurations/%s", url.QueryEscape(notificationConfigurationID)) 216 req, err := s.client.newRequest("GET", u, nil) 217 if err != nil { 218 return nil, err 219 } 220 221 nc := &NotificationConfiguration{} 222 err = s.client.do(ctx, req, nc) 223 if err != nil { 224 return nil, err 225 } 226 227 return nc, nil 228} 229 230// NotificationConfigurationUpdateOptions represents the options for 231// updating a existing notification configuration. 232type NotificationConfigurationUpdateOptions struct { 233 // For internal use only! 234 ID string `jsonapi:"primary,notification-configurations"` 235 236 // Whether the notification configuration should be enabled or not 237 Enabled *bool `jsonapi:"attr,enabled,omitempty"` 238 239 // The name of the notification configuration 240 Name *string `jsonapi:"attr,name,omitempty"` 241 242 // The token of the notification configuration 243 Token *string `jsonapi:"attr,token,omitempty"` 244 245 // The list of run events that will trigger notifications. 246 Triggers []string `jsonapi:"attr,triggers,omitempty"` 247 248 // The url of the notification configuration 249 URL *string `jsonapi:"attr,url,omitempty"` 250 251 // The list of email addresses that will receive notification emails. 252 // EmailAddresses is only available for TFE users. It is not available in TFC. 253 EmailAddresses []string `jsonapi:"attr,email-addresses,omitempty"` 254 255 // The list of users belonging to the organization that will receive notification emails. 256 EmailUsers []*User `jsonapi:"relation,users,omitempty"` 257} 258 259// Updates a notification configuration with the given options. 260func (s *notificationConfigurations) Update(ctx context.Context, notificationConfigurationID string, options NotificationConfigurationUpdateOptions) (*NotificationConfiguration, error) { 261 if !validStringID(¬ificationConfigurationID) { 262 return nil, errors.New("invalid value for notification configuration ID") 263 } 264 265 // Make sure we don't send a user provided ID. 266 options.ID = "" 267 268 u := fmt.Sprintf("notification-configurations/%s", url.QueryEscape(notificationConfigurationID)) 269 req, err := s.client.newRequest("PATCH", u, &options) 270 if err != nil { 271 return nil, err 272 } 273 274 nc := &NotificationConfiguration{} 275 err = s.client.do(ctx, req, nc) 276 if err != nil { 277 return nil, err 278 } 279 280 return nc, nil 281} 282 283// Delete a notifications configuration by its ID. 284func (s *notificationConfigurations) Delete(ctx context.Context, notificationConfigurationID string) error { 285 if !validStringID(¬ificationConfigurationID) { 286 return errors.New("invalid value for notification configuration ID") 287 } 288 289 u := fmt.Sprintf("notification-configurations/%s", url.QueryEscape(notificationConfigurationID)) 290 req, err := s.client.newRequest("DELETE", u, nil) 291 if err != nil { 292 return err 293 } 294 295 return s.client.do(ctx, req, nil) 296} 297 298// Verifies a notification configuration by delivering a verification 299// payload to the configured url. 300func (s *notificationConfigurations) Verify(ctx context.Context, notificationConfigurationID string) (*NotificationConfiguration, error) { 301 if !validStringID(¬ificationConfigurationID) { 302 return nil, errors.New("invalid value for notification configuration ID") 303 } 304 305 u := fmt.Sprintf( 306 "notification-configurations/%s/actions/verify", url.QueryEscape(notificationConfigurationID)) 307 req, err := s.client.newRequest("POST", u, nil) 308 if err != nil { 309 return nil, err 310 } 311 312 nc := &NotificationConfiguration{} 313 err = s.client.do(ctx, req, nc) 314 if err != nil { 315 return nil, err 316 } 317 318 return nc, nil 319} 320