Skip to content

Commit 87e4bef

Browse files
committed
Add support for team notification configurations
1 parent f9d7888 commit 87e4bef

File tree

5 files changed

+899
-64
lines changed

5 files changed

+899
-64
lines changed

helper_test.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -615,6 +615,54 @@ func createNotificationConfiguration(t *testing.T, client *Client, w *Workspace,
615615
}
616616
}
617617

618+
func createTeamNotificationConfiguration(t *testing.T, client *Client, team *Team, options *TeamNotificationConfigurationCreateOptions) (*TeamNotificationConfiguration, func()) {
619+
var tCleanup func()
620+
621+
if team == nil {
622+
team, tCleanup = createTeam(t, client, nil)
623+
}
624+
625+
// Team notification configurations do not actually require a run task, but we'll
626+
// reuse this as a URL that returns a 200.
627+
runTaskURL := os.Getenv("TFC_RUN_TASK_URL")
628+
if runTaskURL == "" {
629+
t.Error("You must set TFC_RUN_TASK_URL for run task related tests.")
630+
}
631+
632+
if options == nil {
633+
options = &TeamNotificationConfigurationCreateOptions{
634+
DestinationType: NotificationDestination(NotificationDestinationTypeGeneric),
635+
Enabled: Bool(false),
636+
Name: String(randomString(t)),
637+
Token: String(randomString(t)),
638+
URL: String(runTaskURL),
639+
Triggers: []NotificationTriggerType{NotificationTriggerChangeRequestCreated},
640+
}
641+
}
642+
643+
ctx := context.Background()
644+
nc, err := client.TeamNotificationConfigurations.Create(
645+
ctx,
646+
team.ID,
647+
*options,
648+
)
649+
if err != nil {
650+
t.Fatal(err)
651+
}
652+
653+
return nc, func() {
654+
if err := client.TeamNotificationConfigurations.Delete(ctx, nc.ID); err != nil {
655+
t.Errorf("Error destroying team notification configuration! WARNING: Dangling\n"+
656+
"resources may exist! The full error is shown below.\n\n"+
657+
"TeamNotificationConfiguration: %s\nError: %s", nc.ID, err)
658+
}
659+
660+
if tCleanup != nil {
661+
tCleanup()
662+
}
663+
}
664+
}
665+
618666
func createPolicySetParameter(t *testing.T, client *Client, ps *PolicySet) (*PolicySetParameter, func()) {
619667
var psCleanup func()
620668

notification_configuration.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ const (
5959
NotificationTriggerAssessmentCheckFailed NotificationTriggerType = "assessment:check_failure"
6060
NotificationTriggerWorkspaceAutoDestroyReminder NotificationTriggerType = "workspace:auto_destroy_reminder"
6161
NotificationTriggerWorkspaceAutoDestroyRunResults NotificationTriggerType = "workspace:auto_destroy_run_results"
62+
NotificationTriggerChangeRequestCreated NotificationTriggerType = "change_request:created"
6263
)
6364

6465
// NotificationDestinationType represents the destination type of the

team_notification_configuration.go

Lines changed: 327 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,327 @@
1+
// Copyright (c) HashiCorp, Inc.
2+
// SPDX-License-Identifier: MPL-2.0
3+
4+
package tfe
5+
6+
import (
7+
"context"
8+
"fmt"
9+
"net/url"
10+
"time"
11+
)
12+
13+
// Compile-time proof of interface implementation.
14+
var _ TeamNotificationConfigurations = (*teamNotificationConfigurations)(nil)
15+
16+
// TeamNotificationConfigurations describes all the Team Notification Configuration
17+
// related methods that the Terraform Enterprise API supports.
18+
//
19+
// TFE API docs:
20+
// https://developer.hashicorp.com/terraform/cloud-docs/api-docs/notification-configurations#team-notification-configuration
21+
type TeamNotificationConfigurations interface {
22+
// List all the notification configurations within a team.
23+
List(ctx context.Context, teamID string, options *TeamNotificationConfigurationListOptions) (*TeamNotificationConfigurationList, error)
24+
25+
// Create a new team notification configuration with the given options.
26+
Create(ctx context.Context, teamID string, options TeamNotificationConfigurationCreateOptions) (*TeamNotificationConfiguration, error)
27+
28+
// Read a notification configuration by its ID.
29+
Read(ctx context.Context, teamNotificationConfigurationID string) (*TeamNotificationConfiguration, error)
30+
31+
// Update an existing team notification configuration.
32+
Update(ctx context.Context, teamNotificationConfigurationID string, options TeamNotificationConfigurationUpdateOptions) (*TeamNotificationConfiguration, error)
33+
34+
// Delete a team notification configuration by its ID.
35+
Delete(ctx context.Context, teamNotificationConfigurationID string) error
36+
37+
// Verify a team notification configuration by its ID.
38+
Verify(ctx context.Context, teamNotificationConfigurationID string) (*TeamNotificationConfiguration, error)
39+
}
40+
41+
// teamNotificationConfigurations implements TeamNotificationConfigurations.
42+
type teamNotificationConfigurations struct {
43+
client *Client
44+
}
45+
46+
// TeamNotificationConfigurationList represents a list of team notification
47+
// configurations.
48+
type TeamNotificationConfigurationList struct {
49+
*Pagination
50+
Items []*TeamNotificationConfiguration
51+
}
52+
53+
// TeamNotificationConfiguration represents a team notification configuration.
54+
type TeamNotificationConfiguration struct {
55+
ID string `jsonapi:"primary,notification-configurations"`
56+
CreatedAt time.Time `jsonapi:"attr,created-at,iso8601"`
57+
DeliveryResponses []*DeliveryResponse `jsonapi:"attr,delivery-responses"`
58+
DestinationType NotificationDestinationType `jsonapi:"attr,destination-type"`
59+
Enabled bool `jsonapi:"attr,enabled"`
60+
Name string `jsonapi:"attr,name"`
61+
Token string `jsonapi:"attr,token"`
62+
Triggers []string `jsonapi:"attr,triggers"`
63+
UpdatedAt time.Time `jsonapi:"attr,updated-at,iso8601"`
64+
URL string `jsonapi:"attr,url"`
65+
66+
// EmailAddresses is only available for TFE users. It is not available in HCP Terraform.
67+
EmailAddresses []string `jsonapi:"attr,email-addresses"`
68+
69+
// Relations
70+
Subscribable *Team `jsonapi:"relation,subscribable"`
71+
EmailUsers []*User `jsonapi:"relation,users"`
72+
}
73+
74+
// TeamNotificationConfigurationListOptions represents the options for listing
75+
// notification configurations.
76+
type TeamNotificationConfigurationListOptions struct {
77+
ListOptions
78+
}
79+
80+
// TeamNotificationConfigurationCreateOptions represents the options for
81+
// creating a new team notification configuration.
82+
type TeamNotificationConfigurationCreateOptions struct {
83+
// Type is a public field utilized by JSON:API to
84+
// set the resource type via the field tag.
85+
// It is not a user-defined value and does not need to be set.
86+
// https://jsonapi.org/format/#crud-creating
87+
Type string `jsonapi:"primary,notification-configurations"`
88+
89+
// Required: The destination type of the team notification configuration
90+
DestinationType *NotificationDestinationType `jsonapi:"attr,destination-type"`
91+
92+
// Required: Whether the team notification configuration should be enabled or not
93+
Enabled *bool `jsonapi:"attr,enabled"`
94+
95+
// Required: The name of the team notification configuration
96+
Name *string `jsonapi:"attr,name"`
97+
98+
// Optional: The token of the team notification configuration
99+
Token *string `jsonapi:"attr,token,omitempty"`
100+
101+
// Optional: The list of events that will trigger team notifications
102+
Triggers []NotificationTriggerType `jsonapi:"attr,triggers,omitempty"`
103+
104+
// Optional: The URL of the team notification configuration
105+
URL *string `jsonapi:"attr,url,omitempty"`
106+
107+
// Optional: The list of email addresses that will receive team notification emails.
108+
// EmailAddresses is only available for TFE users. It is not available in HCP Terraform.
109+
EmailAddresses []string `jsonapi:"attr,email-addresses,omitempty"`
110+
111+
// Optional: The list of users belonging to the organization that will receive
112+
// team notification emails.
113+
EmailUsers []*User `jsonapi:"relation,users,omitempty"`
114+
}
115+
116+
// TeamNotificationConfigurationUpdateOptions represents the options for
117+
// updating a existing team notification configuration.
118+
type TeamNotificationConfigurationUpdateOptions struct {
119+
// Type is a public field utilized by JSON:API to
120+
// set the resource type via the field tag.
121+
// It is not a user-defined value and does not need to be set.
122+
// https://jsonapi.org/format/#crud-creating
123+
Type string `jsonapi:"primary,notification-configurations"`
124+
125+
// Optional: Whether the team notification configuration should be enabled or not
126+
Enabled *bool `jsonapi:"attr,enabled,omitempty"`
127+
128+
// Optional: The name of the team notification configuration
129+
Name *string `jsonapi:"attr,name,omitempty"`
130+
131+
// Optional: The token of the team notification configuration
132+
Token *string `jsonapi:"attr,token,omitempty"`
133+
134+
// Optional: The list of events that will trigger team notifications
135+
Triggers []NotificationTriggerType `jsonapi:"attr,triggers,omitempty"`
136+
137+
// Optional: The URL of the team notification configuration
138+
URL *string `jsonapi:"attr,url,omitempty"`
139+
140+
// Optional: The list of email addresses that will receive team notification emails.
141+
// EmailAddresses is only available for TFE users. It is not available in HCP Terraform.
142+
EmailAddresses []string `jsonapi:"attr,email-addresses,omitempty"`
143+
144+
// Optional: The list of users belonging to the organization that will receive
145+
// team notification emails.
146+
EmailUsers []*User `jsonapi:"relation,users,omitempty"`
147+
}
148+
149+
// List all the notification configurations associated with a team.
150+
func (s *teamNotificationConfigurations) List(ctx context.Context, teamID string, options *TeamNotificationConfigurationListOptions) (*TeamNotificationConfigurationList, error) {
151+
if !validStringID(&teamID) {
152+
return nil, ErrInvalidTeamID
153+
}
154+
155+
u := fmt.Sprintf("teams/%s/notification-configurations", url.PathEscape(teamID))
156+
req, err := s.client.NewRequest("GET", u, options)
157+
if err != nil {
158+
return nil, err
159+
}
160+
161+
ncl := &TeamNotificationConfigurationList{}
162+
err = req.Do(ctx, ncl)
163+
if err != nil {
164+
return nil, err
165+
}
166+
167+
return ncl, nil
168+
}
169+
170+
// Create a team notification configuration with the given options.
171+
func (s *teamNotificationConfigurations) Create(ctx context.Context, teamID string, options TeamNotificationConfigurationCreateOptions) (*TeamNotificationConfiguration, error) {
172+
if !validStringID(&teamID) {
173+
return nil, ErrInvalidTeamID
174+
}
175+
if err := options.valid(); err != nil {
176+
return nil, err
177+
}
178+
179+
u := fmt.Sprintf("teams/%s/notification-configurations", url.PathEscape(teamID))
180+
req, err := s.client.NewRequest("POST", u, &options)
181+
if err != nil {
182+
return nil, err
183+
}
184+
185+
nc := &TeamNotificationConfiguration{}
186+
err = req.Do(ctx, nc)
187+
if err != nil {
188+
return nil, err
189+
}
190+
191+
return nc, nil
192+
}
193+
194+
// Read a team notification configuration by its ID.
195+
func (s *teamNotificationConfigurations) Read(ctx context.Context, teamNotificationConfigurationID string) (*TeamNotificationConfiguration, error) {
196+
if !validStringID(&teamNotificationConfigurationID) {
197+
return nil, ErrInvalidNotificationConfigID
198+
}
199+
200+
u := fmt.Sprintf("notification-configurations/%s", url.PathEscape(teamNotificationConfigurationID))
201+
req, err := s.client.NewRequest("GET", u, nil)
202+
if err != nil {
203+
return nil, err
204+
}
205+
206+
nc := &TeamNotificationConfiguration{}
207+
err = req.Do(ctx, nc)
208+
if err != nil {
209+
return nil, err
210+
}
211+
212+
return nc, nil
213+
}
214+
215+
// Updates a team notification configuration with the given options.
216+
func (s *teamNotificationConfigurations) Update(ctx context.Context, teamNotificationConfigurationID string, options TeamNotificationConfigurationUpdateOptions) (*TeamNotificationConfiguration, error) {
217+
if !validStringID(&teamNotificationConfigurationID) {
218+
return nil, ErrInvalidNotificationConfigID
219+
}
220+
221+
if err := options.valid(); err != nil {
222+
return nil, err
223+
}
224+
225+
u := fmt.Sprintf("notification-configurations/%s", url.PathEscape(teamNotificationConfigurationID))
226+
req, err := s.client.NewRequest("PATCH", u, &options)
227+
if err != nil {
228+
return nil, err
229+
}
230+
231+
nc := &TeamNotificationConfiguration{}
232+
err = req.Do(ctx, nc)
233+
if err != nil {
234+
return nil, err
235+
}
236+
237+
return nc, nil
238+
}
239+
240+
// Delete a team notification configuration by its ID.
241+
func (s *teamNotificationConfigurations) Delete(ctx context.Context, teamNotificationConfigurationID string) error {
242+
if !validStringID(&teamNotificationConfigurationID) {
243+
return ErrInvalidNotificationConfigID
244+
}
245+
246+
u := fmt.Sprintf("notification-configurations/%s", url.PathEscape(teamNotificationConfigurationID))
247+
req, err := s.client.NewRequest("DELETE", u, nil)
248+
if err != nil {
249+
return err
250+
}
251+
252+
return req.Do(ctx, nil)
253+
}
254+
255+
// Verify a team notification configuration by delivering a verification payload
256+
// to the configured URL.
257+
func (s *teamNotificationConfigurations) Verify(ctx context.Context, teamNotificationConfigurationID string) (*TeamNotificationConfiguration, error) {
258+
if !validStringID(&teamNotificationConfigurationID) {
259+
return nil, ErrInvalidNotificationConfigID
260+
}
261+
262+
u := fmt.Sprintf(
263+
"notification-configurations/%s/actions/verify", url.PathEscape(teamNotificationConfigurationID))
264+
req, err := s.client.NewRequest("POST", u, nil)
265+
if err != nil {
266+
return nil, err
267+
}
268+
269+
nc := &TeamNotificationConfiguration{}
270+
err = req.Do(ctx, nc)
271+
if err != nil {
272+
return nil, err
273+
}
274+
275+
return nc, nil
276+
}
277+
278+
func (o TeamNotificationConfigurationCreateOptions) valid() error {
279+
if o.DestinationType == nil {
280+
return ErrRequiredDestinationType
281+
}
282+
if o.Enabled == nil {
283+
return ErrRequiredEnabled
284+
}
285+
if !validString(o.Name) {
286+
return ErrRequiredName
287+
}
288+
289+
if !validTeamNotificationTriggerType(o.Triggers) {
290+
return ErrInvalidNotificationTrigger
291+
}
292+
293+
if *o.DestinationType == NotificationDestinationTypeGeneric ||
294+
*o.DestinationType == NotificationDestinationTypeSlack ||
295+
*o.DestinationType == NotificationDestinationTypeMicrosoftTeams {
296+
if o.URL == nil {
297+
return ErrRequiredURL
298+
}
299+
}
300+
return nil
301+
}
302+
303+
func (o TeamNotificationConfigurationUpdateOptions) valid() error {
304+
if o.Name != nil && !validString(o.Name) {
305+
return ErrRequiredName
306+
}
307+
308+
if !validTeamNotificationTriggerType(o.Triggers) {
309+
return ErrInvalidNotificationTrigger
310+
}
311+
312+
return nil
313+
}
314+
315+
func validTeamNotificationTriggerType(triggers []NotificationTriggerType) bool {
316+
for _, t := range triggers {
317+
switch t {
318+
case
319+
NotificationTriggerChangeRequestCreated:
320+
continue
321+
default:
322+
return false
323+
}
324+
}
325+
326+
return true
327+
}

0 commit comments

Comments
 (0)