Skip to content

Commit

Permalink
U-4488 Support new Policies v3 API in Terraform (#137)
Browse files Browse the repository at this point in the history
  • Loading branch information
PetrHeinz authored Jan 30, 2025
1 parent 838897a commit f6e6c7f
Show file tree
Hide file tree
Showing 13 changed files with 469 additions and 140 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ GOLANGCI_LINT := golangci-lint run --disable-all \
-E staticcheck \
-E typecheck \
-E unused
VERSION := 0.15.0
VERSION := 0.16.0
.PHONY: test build

help:
Expand Down
14 changes: 14 additions & 0 deletions docs/data-sources/betteruptime_policy.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,10 @@ Read-Only:

- **days** (List of String)
- **metadata_key** (String)
- **metadata_value** (List of Object) (see [below for nested schema](#nestedobjatt--steps--metadata_value))
- **metadata_values** (List of String)
- **policy_id** (Number)
- **policy_metadata_key** (String)
- **step_members** (List of Object) (see [below for nested schema](#nestedobjatt--steps--step_members))
- **time_from** (String)
- **time_to** (String)
Expand All @@ -47,6 +49,18 @@ Read-Only:
- **wait_until_time** (String)
- **wait_until_timezone** (String)

<a id="nestedobjatt--steps--metadata_value"></a>
### Nested Schema for `steps.metadata_value`

Read-Only:

- **email** (String)
- **item_id** (String)
- **name** (String)
- **type** (String)
- **value** (String)


<a id="nestedobjatt--steps--step_members"></a>
### Nested Schema for `steps.step_members`

Expand Down
2 changes: 1 addition & 1 deletion docs/resources/betteruptime_catalog_record.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,13 @@ https://betterstack.com/docs/uptime/api/catalog-integrations-records/
Required:

- **attribute_id** (String) ID of the target Catalog attribute.
- **type** (String) Type of the value.

Optional:

- **email** (String) Email of the referenced user when type is User.
- **item_id** (String) ID of the referenced item when type is different than String.
- **name** (String) Human readable name of the referenced item when type is different than String and the item has a name.
- **type** (String) Type of the value. When left empty, the String type is used.
- **value** (String) Value when type is String.

Read-Only:
Expand Down
20 changes: 17 additions & 3 deletions docs/resources/betteruptime_policy.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
page_title: "betteruptime_policy Resource - terraform-provider-better-uptime"
subcategory: ""
description: |-
https://betterstack.com/docs/uptime/api/list-all-escalation-policies/
https://betterstack.com/docs/uptime/api/policies/
---

# betteruptime_policy (Resource)

https://betterstack.com/docs/uptime/api/list-all-escalation-policies/
https://betterstack.com/docs/uptime/api/policies/



Expand Down Expand Up @@ -43,8 +43,10 @@ Optional:

- **days** (List of String) An array of days during which the branching rule will be executed. Valid values are ["mon", "tue", "wed", "thu", "fri", "sat", "sun"]. Used when step type is branching.
- **metadata_key** (String) A metadata field key to check. Used when step type is metadata_branching.
- **metadata_values** (List of String) An array of metadata values which will cause the branching rule to be executed. Used when step type is metadata_branching.
- **metadata_value** (Block List) An array of typed metadata values which will cause the branching rule to be executed. Used when step type is metadata_branching. (see [below for nested schema](#nestedblock--steps--metadata_value))
- **metadata_values** (List of String, Deprecated) An array of metadata String values which will cause the branching rule to be executed. Used when step type is metadata_branching.
- **policy_id** (Number) A policy to executed if the branching rule matches the time of an incident. Used when step type is time_branching or metadata_branching.
- **policy_metadata_key** (String) A metadata key from which to extract the policy to executed if the branching rule matches the time of an incident. Used when step type is time_branching or metadata_branching.
- **step_members** (Block List) An array of escalation policy steps members. Used when step type is escalation. (see [below for nested schema](#nestedblock--steps--step_members))
- **time_from** (String) A time from which the branching rule will be executed. Use HH:MM format. Used when step type is time_branching.
- **time_to** (String) A time at which the branching rule will step being executed. Use HH:MM format. Used when step type is time_branching.
Expand All @@ -54,6 +56,18 @@ Optional:
- **wait_until_time** (String) Execute this step at the specified time. Use HH:MM format. Omit if wait_before is set.
- **wait_until_timezone** (String) Timezone to use when interpreting wait_until_time. Omit if wait_before is set.

<a id="nestedblock--steps--metadata_value"></a>
### Nested Schema for `steps.metadata_value`

Optional:

- **email** (String) Email of the referenced user when type is User.
- **item_id** (String) ID of the referenced item when type is different than String.
- **name** (String) Human readable name of the referenced item when type is different than String and the item has a name.
- **type** (String) Type of the value. When left empty, the String type is used.
- **value** (String) Value when type is String.


<a id="nestedblock--steps--step_members"></a>
### Nested Schema for `steps.step_members`

Expand Down
28 changes: 23 additions & 5 deletions examples/advanced/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -284,11 +284,29 @@ resource "betteruptime_policy" "this" {
policy_id = null
}
steps {
type = "metadata_branching"
wait_before = 0
metadata_key = "Description"
metadata_values = ["Low priority issue", "FYI", "Notice"]
policy_id = null
type = "metadata_branching"
wait_before = 0
metadata_key = "Description"
metadata_value {
value = "Low priority issue"
}
metadata_value {
value = "FYI"
}
metadata_value {
value = "Notice"
}
policy_id = null
}
steps {
type = "metadata_branching"
wait_before = 0
metadata_key = "Assigned User"
metadata_value {
type = "User"
email = "[email protected]"
}
policy_metadata_key = "Assigned Policy"
}
steps {
type = "escalation"
Expand Down
2 changes: 1 addition & 1 deletion examples/advanced/versions.tf
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ terraform {
required_providers {
betteruptime = {
source = "BetterStackHQ/better-uptime"
version = ">= 0.13.0"
version = ">= 0.16.0"
}
}
}
2 changes: 1 addition & 1 deletion internal/provider/data_policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ type policiesPageHTTPResponse struct {

func policyLookup(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
fetch := func(page int) (*policiesPageHTTPResponse, error) {
res, err := meta.(*client).Get(ctx, fmt.Sprintf("/api/v2/policies?page=%d", page))
res, err := meta.(*client).Get(ctx, fmt.Sprintf("/api/v3/policies?page=%d", page))
if err != nil {
return nil, err
}
Expand Down
2 changes: 1 addition & 1 deletion internal/provider/data_policy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ func TestDataPolicy(t *testing.T) {
t.Fail()
}

prefix := "/api/v2/policies"
prefix := "/api/v3/policies"

switch {
case r.Method == http.MethodGet && r.RequestURI == prefix+"?page=1":
Expand Down
94 changes: 10 additions & 84 deletions internal/provider/resource_catalog_record.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (

"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
)

var catalogRecordSchema = map[string]*schema.Schema{
Expand Down Expand Up @@ -42,40 +41,11 @@ var catalogRecordSchema = map[string]*schema.Schema{
Computed: true,
Optional: false,
},
"type": {
Description: "Type of the value.",
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.StringInSlice([]string{
"String", "User", "Team", "Policy", "Schedule",
"SlackIntegration", "LinearIntegration", "JiraIntegration",
"MicrosoftTeamsWebhook", "ZapierWebhook", "NativeWebhook",
"PagerDutyWebhook",
}, false),
},
"value": {
Description: "Value when type is String.",
Type: schema.TypeString,
Optional: true,
},
"item_id": {
Description: "ID of the referenced item when type is different than String.",
Type: schema.TypeString,
Optional: true,
Computed: true,
},
"name": {
Description: "Human readable name of the referenced item when type is different than String and the item has a name.",
Type: schema.TypeString,
Optional: true,
Computed: true,
},
"email": {
Description: "Email of the referenced user when type is User.",
Type: schema.TypeString,
Optional: true,
Computed: true,
},
"type": metadataValueSchema["type"],
"value": metadataValueSchema["value"],
"item_id": metadataValueSchema["item_id"],
"name": metadataValueSchema["name"],
"email": metadataValueSchema["email"],
},
},
},
Expand Down Expand Up @@ -106,20 +76,12 @@ func newCatalogRecordResource() *schema.Resource {
}
}

type catalogRecordValue struct {
Type string `json:"type"`
Value *string `json:"value,omitempty"`
ItemID json.Number `json:"item_id,omitempty"`
Name *string `json:"name,omitempty"`
Email *string `json:"email,omitempty"`
}

type catalogRecordAttribute struct {
Attribute struct {
ID json.Number `json:"id"`
Name string `json:"name,omitempty"`
} `json:"attribute"`
Values []catalogRecordValue `json:"values"`
Values []metadataValue `json:"values"`
}

type catalogRecord struct {
Expand All @@ -142,7 +104,7 @@ func expandCatalogRecordAttributes(d *schema.ResourceData) []catalogRecordAttrib
var attribute catalogRecordAttribute
attribute.Attribute.ID = json.Number(attrMap["attribute_id"].(string))

var value catalogRecordValue
var value metadataValue
value.Type = attrMap["type"].(string)

if v, ok := attrMap["value"].(string); ok && v != "" {
Expand All @@ -158,7 +120,7 @@ func expandCatalogRecordAttributes(d *schema.ResourceData) []catalogRecordAttrib
value.Email = &v
}

attribute.Values = []catalogRecordValue{value}
attribute.Values = []metadataValue{value}
attributes = append(attributes, attribute)
}

Expand Down Expand Up @@ -255,44 +217,8 @@ func validateCatalogRecordAttributes(ctx context.Context, d *schema.ResourceDiff

for i, attr := range attributes {
attrMap := attr.(map[string]interface{})
attrType := attrMap["type"].(string)

// Validation for String type
if attrType == "String" {
if value, ok := attrMap["value"].(string); !ok || value == "" {
return fmt.Errorf("attribute.%d: value must be set for String type attribute (for empty values omit the attirbute altogether)", i)
}
if itemID, ok := attrMap["item_id"].(string); ok && itemID != "" {
return fmt.Errorf("attribute.%d: item_id must not be set for String type attribute", i)
}
if email, ok := attrMap["email"].(string); ok && email != "" {
return fmt.Errorf("attribute.%d: email must not be set for String type attribute", i)
}
if name, ok := attrMap["name"].(string); ok && name != "" {
return fmt.Errorf("attribute.%d: name must not be set for String type attribute", i)
}
continue
}

// Validation for non-String types
if value, ok := attrMap["value"].(string); ok && value != "" {
return fmt.Errorf("attribute.%d: value must not be set for %s type attribute", i, attrType)
}

// At least one of item_id, email, or name must be set
hasIdentifier := false
if itemID, ok := attrMap["item_id"].(string); ok && itemID != "" {
hasIdentifier = true
}
if email, ok := attrMap["email"].(string); ok && email != "" {
hasIdentifier = true
}
if name, ok := attrMap["name"].(string); ok && name != "" {
hasIdentifier = true
}

if !hasIdentifier {
return fmt.Errorf("attribute.%d: at least one of item_id, email, or name must be set for %s type attribute", i, attrType)
if err := validateMetadataValue(attrMap, fmt.Sprintf("attribute.%d", i)); err != nil {
return err
}
}

Expand Down
8 changes: 4 additions & 4 deletions internal/provider/resource_catalog_record_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ func TestResourceCatalogRecordValidation(t *testing.T) {
}
}
`,
expectError: regexp.MustCompile("value must be set for String type attribute"),
expectError: regexp.MustCompile("value must be set for String type"),
},
{
name: "invalid string attribute - with item_id",
Expand All @@ -113,7 +113,7 @@ func TestResourceCatalogRecordValidation(t *testing.T) {
}
}
`,
expectError: regexp.MustCompile("item_id must not be set for String type attribute"),
expectError: regexp.MustCompile("item_id must not be set for String type"),
},
{
name: "valid user attribute",
Expand Down Expand Up @@ -150,7 +150,7 @@ func TestResourceCatalogRecordValidation(t *testing.T) {
}
}
`,
expectError: regexp.MustCompile("value must not be set for User type attribute"),
expectError: regexp.MustCompile("value must not be set for User type"),
},
{
name: "invalid user attribute - no identifier",
Expand All @@ -167,7 +167,7 @@ func TestResourceCatalogRecordValidation(t *testing.T) {
}
}
`,
expectError: regexp.MustCompile("at least one of item_id, email, or name must be set for User type attribute"),
expectError: regexp.MustCompile("at least one of item_id, email, or name must be set for User type"),
},
}

Expand Down
Loading

0 comments on commit f6e6c7f

Please sign in to comment.