Skip to content

Commit 7b0ffc9

Browse files
authored
Merge pull request #1016 from hashicorp/jfreda/add-team-notification-configs
Add support for team notification configurations
2 parents f215e07 + bb7b35f commit 7b0ffc9

File tree

4 files changed

+560
-25
lines changed

4 files changed

+560
-25
lines changed

helper_test.go

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

618+
func createTeamNotificationConfiguration(t *testing.T, client *Client, team *Team, options *NotificationConfigurationCreateOptions) (*NotificationConfiguration, 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 = &NotificationConfigurationCreateOptions{
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+
SubscribableChoice: &NotificationConfigurationSubscribableChoice{Team: team},
641+
}
642+
}
643+
644+
ctx := context.Background()
645+
nc, err := client.NotificationConfigurations.Create(
646+
ctx,
647+
team.ID,
648+
*options,
649+
)
650+
if err != nil {
651+
t.Fatal(err)
652+
}
653+
654+
return nc, func() {
655+
if err := client.NotificationConfigurations.Delete(ctx, nc.ID); err != nil {
656+
t.Errorf("Error destroying team notification configuration! WARNING: Dangling\n"+
657+
"resources may exist! The full error is shown below.\n\n"+
658+
"NotificationConfiguration: %s\nError: %s", nc.ID, err)
659+
}
660+
661+
if tCleanup != nil {
662+
tCleanup()
663+
}
664+
}
665+
}
666+
618667
func createPolicySetParameter(t *testing.T, client *Client, ps *PolicySet) (*PolicySetParameter, func()) {
619668
var psCleanup func()
620669

mocks/notification_configuration_mocks.go

Lines changed: 8 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

notification_configuration.go

Lines changed: 88 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@ var _ NotificationConfigurations = (*notificationConfigurations)(nil)
2020
// https://developer.hashicorp.com/terraform/cloud-docs/api-docs/notification-configurations
2121
type NotificationConfigurations interface {
2222
// List all the notification configurations within a workspace.
23-
List(ctx context.Context, workspaceID string, options *NotificationConfigurationListOptions) (*NotificationConfigurationList, error)
23+
List(ctx context.Context, subscribableID string, options *NotificationConfigurationListOptions) (*NotificationConfigurationList, error)
2424

2525
// Create a new notification configuration with the given options.
26-
Create(ctx context.Context, workspaceID string, options NotificationConfigurationCreateOptions) (*NotificationConfiguration, error)
26+
Create(ctx context.Context, subscribableID string, options NotificationConfigurationCreateOptions) (*NotificationConfiguration, error)
2727

2828
// Read a notification configuration by its ID.
2929
Read(ctx context.Context, notificationConfigurationID string) (*NotificationConfiguration, error)
@@ -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
@@ -80,6 +81,14 @@ type NotificationConfigurationList struct {
8081
Items []*NotificationConfiguration
8182
}
8283

84+
// NotificationConfigurationSubscribableChoice is a choice type struct that represents the possible values
85+
// within a polymorphic relation. If a value is available, exactly one field
86+
// will be non-nil.
87+
type NotificationConfigurationSubscribableChoice struct {
88+
Team *Team
89+
Workspace *Workspace
90+
}
91+
8392
// NotificationConfiguration represents a Notification Configuration.
8493
type NotificationConfiguration struct {
8594
ID string `jsonapi:"primary,notification-configurations"`
@@ -97,8 +106,11 @@ type NotificationConfiguration struct {
97106
EmailAddresses []string `jsonapi:"attr,email-addresses"`
98107

99108
// Relations
100-
Subscribable *Workspace `jsonapi:"relation,subscribable"`
101-
EmailUsers []*User `jsonapi:"relation,users"`
109+
// DEPRECATED. The subscribable field is polymorphic. Use NotificationConfigurationSubscribableChoice instead.
110+
Subscribable *Workspace `jsonapi:"relation,subscribable,omitempty"`
111+
SubscribableChoice *NotificationConfigurationSubscribableChoice `jsonapi:"polyrelation,subscribable"`
112+
113+
EmailUsers []*User `jsonapi:"relation,users"`
102114
}
103115

104116
// DeliveryResponse represents a notification configuration delivery response.
@@ -115,6 +127,8 @@ type DeliveryResponse struct {
115127
// notification configurations.
116128
type NotificationConfigurationListOptions struct {
117129
ListOptions
130+
131+
SubscribableChoice *NotificationConfigurationSubscribableChoice
118132
}
119133

120134
// NotificationConfigurationCreateOptions represents the options for
@@ -150,6 +164,9 @@ type NotificationConfigurationCreateOptions struct {
150164

151165
// Optional: The list of users belonging to the organization that will receive notification emails.
152166
EmailUsers []*User `jsonapi:"relation,users,omitempty"`
167+
168+
// Required: The workspace or team that the notification configuration is associated with.
169+
SubscribableChoice *NotificationConfigurationSubscribableChoice `jsonapi:"polyrelation,subscribable,omitempty"`
153170
}
154171

155172
// NotificationConfigurationUpdateOptions represents the options for
@@ -185,12 +202,32 @@ type NotificationConfigurationUpdateOptions struct {
185202
}
186203

187204
// List all the notification configurations associated with a workspace.
188-
func (s *notificationConfigurations) List(ctx context.Context, workspaceID string, options *NotificationConfigurationListOptions) (*NotificationConfigurationList, error) {
189-
if !validStringID(&workspaceID) {
190-
return nil, ErrInvalidWorkspaceID
205+
func (s *notificationConfigurations) List(ctx context.Context, subscribableID string, options *NotificationConfigurationListOptions) (*NotificationConfigurationList, error) {
206+
var u string
207+
if options == nil {
208+
options = &NotificationConfigurationListOptions{
209+
SubscribableChoice: &NotificationConfigurationSubscribableChoice{
210+
Workspace: &Workspace{ID: subscribableID},
211+
},
212+
}
213+
} else if options.SubscribableChoice == nil {
214+
options.SubscribableChoice = &NotificationConfigurationSubscribableChoice{
215+
Workspace: &Workspace{ID: subscribableID},
216+
}
217+
}
218+
219+
if options.SubscribableChoice.Team != nil {
220+
if !validStringID(&subscribableID) {
221+
return nil, ErrInvalidTeamID
222+
}
223+
u = fmt.Sprintf("teams/%s/notification-configurations", url.PathEscape(subscribableID))
224+
} else {
225+
if !validStringID(&subscribableID) {
226+
return nil, ErrInvalidWorkspaceID
227+
}
228+
u = fmt.Sprintf("workspaces/%s/notification-configurations", url.PathEscape(subscribableID))
191229
}
192230

193-
u := fmt.Sprintf("workspaces/%s/notification-configurations", url.PathEscape(workspaceID))
194231
req, err := s.client.NewRequest("GET", u, options)
195232
if err != nil {
196233
return nil, err
@@ -202,30 +239,43 @@ func (s *notificationConfigurations) List(ctx context.Context, workspaceID strin
202239
return nil, err
203240
}
204241

242+
for i := range ncl.Items {
243+
backfillDeprecatedSubscribable(ncl.Items[i])
244+
}
245+
205246
return ncl, nil
206247
}
207248

208249
// Create a notification configuration with the given options.
209-
func (s *notificationConfigurations) Create(ctx context.Context, workspaceID string, options NotificationConfigurationCreateOptions) (*NotificationConfiguration, error) {
210-
if !validStringID(&workspaceID) {
211-
return nil, ErrInvalidWorkspaceID
250+
func (s *notificationConfigurations) Create(ctx context.Context, subscribableID string, options NotificationConfigurationCreateOptions) (*NotificationConfiguration, error) {
251+
var u string
252+
var subscribableChoice *NotificationConfigurationSubscribableChoice
253+
if options.SubscribableChoice == nil || options.SubscribableChoice.Team == nil {
254+
u = fmt.Sprintf("workspaces/%s/notification-configurations", url.PathEscape(subscribableID))
255+
options.SubscribableChoice = &NotificationConfigurationSubscribableChoice{Workspace: &Workspace{ID: subscribableID}}
256+
} else {
257+
u = fmt.Sprintf("teams/%s/notification-configurations", url.PathEscape(subscribableID))
258+
options.SubscribableChoice = &NotificationConfigurationSubscribableChoice{Team: &Team{ID: subscribableID}}
212259
}
260+
213261
if err := options.valid(); err != nil {
214262
return nil, err
215263
}
216264

217-
u := fmt.Sprintf("workspaces/%s/notification-configurations", url.PathEscape(workspaceID))
218265
req, err := s.client.NewRequest("POST", u, &options)
219266
if err != nil {
220267
return nil, err
221268
}
222269

223-
nc := &NotificationConfiguration{}
270+
nc := &NotificationConfiguration{SubscribableChoice: subscribableChoice}
224271
err = req.Do(ctx, nc)
272+
225273
if err != nil {
226274
return nil, err
227275
}
228276

277+
backfillDeprecatedSubscribable(nc)
278+
229279
return nc, nil
230280
}
231281

@@ -247,6 +297,8 @@ func (s *notificationConfigurations) Read(ctx context.Context, notificationConfi
247297
return nil, err
248298
}
249299

300+
backfillDeprecatedSubscribable(nc)
301+
250302
return nc, nil
251303
}
252304

@@ -272,6 +324,8 @@ func (s *notificationConfigurations) Update(ctx context.Context, notificationCon
272324
return nil, err
273325
}
274326

327+
backfillDeprecatedSubscribable(nc)
328+
275329
return nc, nil
276330
}
277331

@@ -314,6 +368,16 @@ func (s *notificationConfigurations) Verify(ctx context.Context, notificationCon
314368
}
315369

316370
func (o NotificationConfigurationCreateOptions) valid() error {
371+
if o.SubscribableChoice == nil || o.SubscribableChoice.Workspace != nil {
372+
if !validStringID(&o.SubscribableChoice.Workspace.ID) {
373+
return ErrInvalidWorkspaceID
374+
}
375+
} else {
376+
if !validStringID(&o.SubscribableChoice.Team.ID) {
377+
return ErrInvalidTeamID
378+
}
379+
}
380+
317381
if o.DestinationType == nil {
318382
return ErrRequiredDestinationType
319383
}
@@ -350,6 +414,16 @@ func (o NotificationConfigurationUpdateOptions) valid() error {
350414
return nil
351415
}
352416

417+
func backfillDeprecatedSubscribable(notification *NotificationConfiguration) {
418+
if notification.Subscribable != nil || notification.SubscribableChoice == nil {
419+
return
420+
}
421+
422+
if notification.SubscribableChoice.Workspace != nil {
423+
notification.Subscribable = notification.SubscribableChoice.Workspace
424+
}
425+
}
426+
353427
func validNotificationTriggerType(triggers []NotificationTriggerType) bool {
354428
for _, t := range triggers {
355429
switch t {
@@ -363,6 +437,7 @@ func validNotificationTriggerType(triggers []NotificationTriggerType) bool {
363437
NotificationTriggerAssessmentFailed,
364438
NotificationTriggerWorkspaceAutoDestroyReminder,
365439
NotificationTriggerWorkspaceAutoDestroyRunResults,
440+
NotificationTriggerChangeRequestCreated,
366441
NotificationTriggerAssessmentCheckFailed:
367442
continue
368443
default:

0 commit comments

Comments
 (0)