diff --git a/go.mod b/go.mod index 77b3d5d19..a6c3985cf 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.16 require ( cloud.google.com/go v0.71.0 // indirect github.com/hashicorp/terraform-plugin-sdk/v2 v2.10.1 - github.com/heimweh/go-pagerduty v0.0.0-20211210233744-b65de43109c1 + github.com/heimweh/go-pagerduty v0.0.0-20220208023456-83fe435832fb golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd // indirect google.golang.org/api v0.35.0 // indirect google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb // indirect diff --git a/go.sum b/go.sum index f8b73d3b8..ba73b62d9 100644 --- a/go.sum +++ b/go.sum @@ -282,6 +282,8 @@ github.com/heimweh/go-pagerduty v0.0.0-20211119212911-31ef1eea0d0f h1:/sgh3L7adJ github.com/heimweh/go-pagerduty v0.0.0-20211119212911-31ef1eea0d0f/go.mod h1:JtJGtgN0y9KOCaqFMZFaBCWskpO/KK3Ro9TwjP9ss6w= github.com/heimweh/go-pagerduty v0.0.0-20211210233744-b65de43109c1 h1:49zl3n/g+ff/7/CpxiuqnenyxId5iAjbhgoE9iDHULc= github.com/heimweh/go-pagerduty v0.0.0-20211210233744-b65de43109c1/go.mod h1:JtJGtgN0y9KOCaqFMZFaBCWskpO/KK3Ro9TwjP9ss6w= +github.com/heimweh/go-pagerduty v0.0.0-20220208023456-83fe435832fb h1:p3faOVCU8L4wab9mpxN9Cm/VNVkPD8GMEfD0sPHw9nY= +github.com/heimweh/go-pagerduty v0.0.0-20220208023456-83fe435832fb/go.mod h1:JtJGtgN0y9KOCaqFMZFaBCWskpO/KK3Ro9TwjP9ss6w= github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= diff --git a/pagerduty/resource_pagerduty_schedule.go b/pagerduty/resource_pagerduty_schedule.go index d2fec6451..b3df4f741 100644 --- a/pagerduty/resource_pagerduty_schedule.go +++ b/pagerduty/resource_pagerduty_schedule.go @@ -304,7 +304,8 @@ func resourcePagerDutyScheduleUpdate(d *schema.ResourceData, meta interface{}) e if err != nil { return err } - o.End = end.String() + endStr := end.String() + o.End = &endStr schedule.ScheduleLayers = append(schedule.ScheduleLayers, o) } } @@ -372,11 +373,13 @@ func expandScheduleLayers(v interface{}) ([]*pagerduty.ScheduleLayer, error) { return nil, err } + // The type of layer.*.end is schema.TypeString. If the end is an empty string, it means the layer does not end. + // A client should send a payload including `"end": null` to unset the end of layer. scheduleLayer := &pagerduty.ScheduleLayer{ ID: rsl["id"].(string), Name: rsl["name"].(string), Start: rsl["start"].(string), - End: rsl["end"].(string), + End: stringTypeToStringPtr(rsl["end"].(string)), RotationVirtualStart: rvs.String(), RotationTurnLengthSeconds: rsl["rotation_turn_length_seconds"].(int), } @@ -417,8 +420,9 @@ func flattenScheduleLayers(v []*pagerduty.ScheduleLayer) ([]map[string]interface // A schedule layer can never be removed but it can be ended. // Here we check each layer and if it has been ended we don't read it back // because it's not relevant anymore. - if sl.End != "" { - end, err := timeToUTC(sl.End) + endStr := stringPtrToStringType(sl.End) + if endStr != "" { + end, err := timeToUTC(endStr) if err != nil { return nil, err } @@ -430,7 +434,7 @@ func flattenScheduleLayers(v []*pagerduty.ScheduleLayer) ([]map[string]interface scheduleLayer := map[string]interface{}{ "id": sl.ID, "name": sl.Name, - "end": sl.End, + "end": endStr, "start": sl.Start, "rotation_virtual_start": sl.RotationVirtualStart, "rotation_turn_length_seconds": sl.RotationTurnLengthSeconds, diff --git a/pagerduty/util.go b/pagerduty/util.go index 878eed813..e8cb33ea9 100644 --- a/pagerduty/util.go +++ b/pagerduty/util.go @@ -123,3 +123,21 @@ func flattenSlice(v []interface{}) interface{} { } return string(b) } + +// stringTypeToStringPtr is a helper that returns a pointer to +// the string value passed in or nil if the string is empty. +func stringTypeToStringPtr(v string) *string { + if v == "" { + return nil + } + return &v +} + +// stringPtrToStringType is a helper that returns the string value passed in +// or an empty string if the given pointer is nil. +func stringPtrToStringType(v *string) string { + if v == nil { + return "" + } + return *v +} diff --git a/vendor/github.com/heimweh/go-pagerduty/pagerduty/schedule.go b/vendor/github.com/heimweh/go-pagerduty/pagerduty/schedule.go index f526aaf30..bcf5f3e17 100644 --- a/vendor/github.com/heimweh/go-pagerduty/pagerduty/schedule.go +++ b/vendor/github.com/heimweh/go-pagerduty/pagerduty/schedule.go @@ -58,7 +58,8 @@ type ScheduleLayerEntry struct { // ScheduleLayer represents a schedule layer in a schedule type ScheduleLayer struct { - End string `json:"end,omitempty"` + // End should be nullable because if it's null, it means the layer does not end. + End *string `json:"end"` ID string `json:"id,omitempty"` Name string `json:"name,omitempty"` RenderedCoveragePercentage float64 `json:"rendered_coverage_percentage,omitempty"` diff --git a/vendor/github.com/heimweh/go-pagerduty/pagerduty/user.go b/vendor/github.com/heimweh/go-pagerduty/pagerduty/user.go index a50971588..b6314510a 100644 --- a/vendor/github.com/heimweh/go-pagerduty/pagerduty/user.go +++ b/vendor/github.com/heimweh/go-pagerduty/pagerduty/user.go @@ -3,6 +3,7 @@ package pagerduty import ( "fmt" "log" + "strings" ) // UserService handles the communication with user @@ -289,13 +290,31 @@ func (s *UserService) ListContactMethods(userID string) (*ListContactMethodsResp } // CreateContactMethod creates a new contact method for a user. +// If the same contact method already exists, it will fetch the existing one, return a 200 instead of fail. This feature is useful in terraform +// provider, as when the desired user contact method already exists, terraform will be able to sync it to the state automatically. Otherwise, +// we need to manually fix the conflicts. func (s *UserService) CreateContactMethod(userID string, contactMethod *ContactMethod) (*ContactMethod, *Response, error) { u := fmt.Sprintf("/users/%s/contact_methods", userID) v := new(ContactMethodPayload) resp, err := s.client.newRequestDo("POST", u, nil, &ContactMethodPayload{ContactMethod: contactMethod}, &v) if err != nil { - return nil, nil, err + if e, ok := err.(*Error); ok && strings.Compare(fmt.Sprintf("%v", e.Errors), "[User Contact method must be unique]") == 0 { + resp, _, lErr := s.ListContactMethods(userID) + if lErr != nil { + return nil, nil, fmt.Errorf("user contact method is not unique and failed to fetch existing ones: %w", lErr) + } + + for _, contact := range resp.ContactMethods { + if isSameContactMethod(contact, contactMethod) { + return s.GetContactMethod(userID, contact.ID) + } + } + + return nil, nil, fmt.Errorf("user contact method address is used with different attributes (possibly label)") + } else { + return nil, nil, err + } } if err = cachePutContactMethod(v.ContactMethod); err != nil { @@ -307,6 +326,16 @@ func (s *UserService) CreateContactMethod(userID string, contactMethod *ContactM return v.ContactMethod, resp, nil } +// isSameContactMethod checks if an existing contact method should be taken as the same as a new one users want to create. +// note new contact method misses some fields like Self, HTMLURL. +func isSameContactMethod(existingContact, newContact *ContactMethod) bool { + return existingContact.Type == newContact.Type && + existingContact.Address == newContact.Address && + existingContact.Label == newContact.Label && + existingContact.CountryCode == newContact.CountryCode && + existingContact.Summary == newContact.Summary +} + // GetContactMethod retrieves a contact method for a user. func (s *UserService) GetContactMethod(userID string, contactMethodID string) (*ContactMethod, *Response, error) { u := fmt.Sprintf("/users/%s/contact_methods/%s", userID, contactMethodID) diff --git a/vendor/github.com/heimweh/go-pagerduty/pagerduty/webhook_subscription.go b/vendor/github.com/heimweh/go-pagerduty/pagerduty/webhook_subscription.go index 2f17c17af..8390be7db 100644 --- a/vendor/github.com/heimweh/go-pagerduty/pagerduty/webhook_subscription.go +++ b/vendor/github.com/heimweh/go-pagerduty/pagerduty/webhook_subscription.go @@ -18,9 +18,15 @@ type WebhookSubscription struct { // DeliveryMethod represents a webhook delivery method type DeliveryMethod struct { - TemporarilyDisabled bool `json:"temporarily_disabled,omitempty"` - Type string `json:"type,omitempty"` - URL string `json:"url,omitempty"` + TemporarilyDisabled bool `json:"temporarily_disabled,omitempty"` + Type string `json:"type,omitempty"` + URL string `json:"url,omitempty"` + CustomHeaders []*CustomHeaders `json:"custom_headers,omitempty"` +} + +type CustomHeaders struct { + Name string `json:"name,omitempty"` + Value string `json:"value,omitempty"` } // Filter represents a webhook subscription filter diff --git a/vendor/modules.txt b/vendor/modules.txt index e28f13ce8..00587e7be 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -124,7 +124,7 @@ github.com/hashicorp/terraform-registry-address github.com/hashicorp/terraform-svchost # github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d github.com/hashicorp/yamux -# github.com/heimweh/go-pagerduty v0.0.0-20211210233744-b65de43109c1 +# github.com/heimweh/go-pagerduty v0.0.0-20220208023456-83fe435832fb ## explicit github.com/heimweh/go-pagerduty/pagerduty # github.com/klauspost/compress v1.11.2