Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,8 @@ var (

ErrInvalidNotificationConfigID = errors.New("invalid value for notification configuration ID")

ErrInvalidNotificationConfigSubscribableChoice = errors.New("invalid value for notification configuration subscribable choice")

ErrInvalidMembership = errors.New("invalid value for membership")

ErrInvalidMembershipIDs = errors.New("invalid value for organization membership ids")
Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,5 @@ require (
golang.org/x/sys v0.25.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

replace github.com/hashicorp/jsonapi => github.com/notchairmk/jsonapi v0.0.0-20241223221631-b0c6a5b7edd8
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/C
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY=
github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/jsonapi v1.3.1 h1:GtPvnmcWgYwCuDGvYT5VZBHcUyFdq9lSyCzDjn1DdPo=
github.com/hashicorp/jsonapi v1.3.1/go.mod h1:kWfdn49yCjQvbpnvY1dxxAuAFzISwrrMDQOcu6NsFoM=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/notchairmk/jsonapi v0.0.0-20241223221631-b0c6a5b7edd8 h1:Nll3UptyKamtMP60oCHnRKI3l/kgadZHKQ6/uLYPyVM=
github.com/notchairmk/jsonapi v0.0.0-20241223221631-b0c6a5b7edd8/go.mod h1:kWfdn49yCjQvbpnvY1dxxAuAFzISwrrMDQOcu6NsFoM=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
Expand Down
49 changes: 49 additions & 0 deletions helper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,55 @@ func createNotificationConfiguration(t *testing.T, client *Client, w *Workspace,
}
}

func createTeamNotificationConfiguration(t *testing.T, client *Client, team *Team, options *NotificationConfigurationCreateOptions) (*NotificationConfiguration, func()) {
var tCleanup func()

if team == nil {
team, tCleanup = createTeam(t, client, nil)
}

// Team notification configurations do not actually require a run task, but we'll
// reuse this as a URL that returns a 200.
runTaskURL := os.Getenv("TFC_RUN_TASK_URL")
if runTaskURL == "" {
t.Error("You must set TFC_RUN_TASK_URL for run task related tests.")
}

if options == nil {
options = &NotificationConfigurationCreateOptions{
DestinationType: NotificationDestination(NotificationDestinationTypeGeneric),
Enabled: Bool(false),
Name: String(randomString(t)),
Token: String(randomString(t)),
URL: String(runTaskURL),
Triggers: []NotificationTriggerType{NotificationTriggerChangeRequestCreated},
SubscribableChoice: &NotificationConfigurationSubscribableChoice{Team: team},
}
}

ctx := context.Background()
nc, err := client.NotificationConfigurations.Create(
ctx,
team.ID,
*options,
)
if err != nil {
t.Fatal(err)
}

return nc, func() {
if err := client.NotificationConfigurations.Delete(ctx, nc.ID); err != nil {
t.Errorf("Error destroying team notification configuration! WARNING: Dangling\n"+
"resources may exist! The full error is shown below.\n\n"+
"NotificationConfiguration: %s\nError: %s", nc.ID, err)
}

if tCleanup != nil {
tCleanup()
}
}
}

func createPolicySetParameter(t *testing.T, client *Client, ps *PolicySet) (*PolicySetParameter, func()) {
var psCleanup func()

Expand Down
16 changes: 8 additions & 8 deletions mocks/notification_configuration_mocks.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

103 changes: 89 additions & 14 deletions notification_configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ var _ NotificationConfigurations = (*notificationConfigurations)(nil)
// https://developer.hashicorp.com/terraform/cloud-docs/api-docs/notification-configurations
type NotificationConfigurations interface {
// List all the notification configurations within a workspace.
List(ctx context.Context, workspaceID string, options *NotificationConfigurationListOptions) (*NotificationConfigurationList, error)
List(ctx context.Context, subscribableID string, options *NotificationConfigurationListOptions) (*NotificationConfigurationList, error)

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

// Read a notification configuration by its ID.
Read(ctx context.Context, notificationConfigurationID string) (*NotificationConfiguration, error)
Expand Down Expand Up @@ -59,6 +59,7 @@ const (
NotificationTriggerAssessmentCheckFailed NotificationTriggerType = "assessment:check_failure"
NotificationTriggerWorkspaceAutoDestroyReminder NotificationTriggerType = "workspace:auto_destroy_reminder"
NotificationTriggerWorkspaceAutoDestroyRunResults NotificationTriggerType = "workspace:auto_destroy_run_results"
NotificationTriggerChangeRequestCreated NotificationTriggerType = "change_request:created"
)

// NotificationDestinationType represents the destination type of the
Expand All @@ -80,6 +81,14 @@ type NotificationConfigurationList struct {
Items []*NotificationConfiguration
}

// NotificationConfigurationSubscribableChoice is a choice type struct that represents the possible values
// within a polymorphic relation. If a value is available, exactly one field
// will be non-nil.
type NotificationConfigurationSubscribableChoice struct {
Team *Team
Workspace *Workspace
}

// NotificationConfiguration represents a Notification Configuration.
type NotificationConfiguration struct {
ID string `jsonapi:"primary,notification-configurations"`
Expand All @@ -97,8 +106,11 @@ type NotificationConfiguration struct {
EmailAddresses []string `jsonapi:"attr,email-addresses"`

// Relations
Subscribable *Workspace `jsonapi:"relation,subscribable"`
EmailUsers []*User `jsonapi:"relation,users"`
// DEPRECATED. The subscribable field is polymorphic. Use NotificationConfigurationSubscribableChoice instead.
Subscribable *Workspace `jsonapi:"relation,subscribable,omitempty"`
SubscribableChoice *NotificationConfigurationSubscribableChoice `jsonapi:"polyrelation,subscribable"`

EmailUsers []*User `jsonapi:"relation,users"`
}

// DeliveryResponse represents a notification configuration delivery response.
Expand All @@ -115,6 +127,8 @@ type DeliveryResponse struct {
// notification configurations.
type NotificationConfigurationListOptions struct {
ListOptions

SubscribableChoice *NotificationConfigurationSubscribableChoice
}

// NotificationConfigurationCreateOptions represents the options for
Expand Down Expand Up @@ -150,6 +164,9 @@ type NotificationConfigurationCreateOptions struct {

// Optional: The list of users belonging to the organization that will receive notification emails.
EmailUsers []*User `jsonapi:"relation,users,omitempty"`

// Required: The workspace or team that the notification configuration is associated with.
SubscribableChoice *NotificationConfigurationSubscribableChoice `jsonapi:"polyrelation,subscribable"`
}

// NotificationConfigurationUpdateOptions represents the options for
Expand Down Expand Up @@ -185,12 +202,32 @@ type NotificationConfigurationUpdateOptions struct {
}

// 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, ErrInvalidWorkspaceID
func (s *notificationConfigurations) List(ctx context.Context, subscribableID string, options *NotificationConfigurationListOptions) (*NotificationConfigurationList, error) {
var u string
if options == nil {
options = &NotificationConfigurationListOptions{
SubscribableChoice: &NotificationConfigurationSubscribableChoice{
Workspace: &Workspace{ID: subscribableID},
},
}
} else if options.SubscribableChoice == nil {
options.SubscribableChoice = &NotificationConfigurationSubscribableChoice{
Workspace: &Workspace{ID: subscribableID},
}
}

if options.SubscribableChoice.Team != nil {
if !validStringID(&subscribableID) {
return nil, ErrInvalidTeamID
}
u = fmt.Sprintf("teams/%s/notification-configurations", url.PathEscape(subscribableID))
} else {
if !validStringID(&subscribableID) {
return nil, ErrInvalidWorkspaceID
}
u = fmt.Sprintf("workspaces/%s/notification-configurations", url.PathEscape(subscribableID))
}

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

for i := range ncl.Items {
backfillDeprecatedSubscribable(ncl.Items[i])
}

return ncl, nil
}

// Create 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, ErrInvalidWorkspaceID
}
func (s *notificationConfigurations) Create(ctx context.Context, subscribableID string, options NotificationConfigurationCreateOptions) (*NotificationConfiguration, error) {
if err := options.valid(); err != nil {
return nil, err
}

u := fmt.Sprintf("workspaces/%s/notification-configurations", url.PathEscape(workspaceID))
var u string
var subscribableChoice *NotificationConfigurationSubscribableChoice
if options.SubscribableChoice == nil || options.SubscribableChoice.Workspace != nil {
if !validStringID(&subscribableID) {
return nil, ErrInvalidWorkspaceID
}

u = fmt.Sprintf("workspaces/%s/notification-configurations", url.PathEscape(subscribableID))
subscribableChoice = &NotificationConfigurationSubscribableChoice{Workspace: &Workspace{ID: subscribableID}}
} else if options.SubscribableChoice != nil && options.SubscribableChoice.Team != nil {
if !validStringID(&subscribableID) {
return nil, ErrInvalidTeamID
}

u = fmt.Sprintf("teams/%s/notification-configurations", url.PathEscape(subscribableID))
subscribableChoice = &NotificationConfigurationSubscribableChoice{Team: &Team{ID: subscribableID}}
} else {
return nil, ErrInvalidNotificationConfigSubscribableChoice
}

req, err := s.client.NewRequest("POST", u, &options)
if err != nil {
return nil, err
}

nc := &NotificationConfiguration{}
nc := &NotificationConfiguration{SubscribableChoice: subscribableChoice}
err = req.Do(ctx, nc)

if err != nil {
return nil, err
}

backfillDeprecatedSubscribable(nc)

return nc, nil
}

Expand All @@ -247,6 +307,8 @@ func (s *notificationConfigurations) Read(ctx context.Context, notificationConfi
return nil, err
}

backfillDeprecatedSubscribable(nc)

return nc, nil
}

Expand All @@ -272,6 +334,8 @@ func (s *notificationConfigurations) Update(ctx context.Context, notificationCon
return nil, err
}

backfillDeprecatedSubscribable(nc)

return nc, nil
}

Expand Down Expand Up @@ -350,6 +414,16 @@ func (o NotificationConfigurationUpdateOptions) valid() error {
return nil
}

func backfillDeprecatedSubscribable(notification *NotificationConfiguration) {
if notification.Subscribable != nil || notification.SubscribableChoice == nil {
return
}

if notification.SubscribableChoice.Workspace != nil {
notification.Subscribable = notification.SubscribableChoice.Workspace
}
}

func validNotificationTriggerType(triggers []NotificationTriggerType) bool {
for _, t := range triggers {
switch t {
Expand All @@ -363,6 +437,7 @@ func validNotificationTriggerType(triggers []NotificationTriggerType) bool {
NotificationTriggerAssessmentFailed,
NotificationTriggerWorkspaceAutoDestroyReminder,
NotificationTriggerWorkspaceAutoDestroyRunResults,
NotificationTriggerChangeRequestCreated,
NotificationTriggerAssessmentCheckFailed:
continue
default:
Expand Down
Loading
Loading