Repository: PagerDuty/go-pagerduty
Pull request #498 (2023-10-03 16:56:01)

Updated README.md replacing deprecated calls with corresponding new ones
Current version of README.md uses:

- client.ListEscalationPolicies (deprecated)
- client.GetUser (deprecated)

This MR updates examples with
- client.ListEscalationPoliciesWithContext
- client.GetUserWithContext


Patch:
diff --git a/README.md b/README.md
index e664ffe..3321387 100644
--- a/README.md
+++ b/README.md
@@ -70,9 +70,11 @@ import (
 var	authtoken = "" // Set your auth token here
 
 func main() {
-	var opts pagerduty.ListEscalationPoliciesOptions
+	ctx := context.Background()
 	client := pagerduty.NewClient(authtoken)
-	eps, err := client.ListEscalationPolicies(opts)
+
+	var opts pagerduty.ListEscalationPoliciesOptions
+	eps, err := client.ListEscalationPoliciesWithContext(ctx, opts)
 	if err != nil {
 		panic(err)
 	}
@@ -105,8 +107,9 @@ import (
 var	authtoken = "" // Set your auth token here
 
 func main() {
+	ctx := context.Background()
 	client := pagerduty.NewClient(authtoken)
-	user, err := client.GetUser("NOTREAL", pagerduty.GetUserOptions{})
+	user, err := client.GetUserWithContext(ctx, "NOTREAL", pagerduty.GetUserOptions{})
 	if err != nil {
 		var aerr pagerduty.APIError
 
diff --git a/command/main.go b/command/main.go
index ce6215b..340d7b2 100644
--- a/command/main.go
+++ b/command/main.go
@@ -82,6 +82,11 @@ func loadCommands() map[string]cli.CommandFactory {
 		"service rule show":          ServiceRuleShowCommand,
 		"service rule update":        ServiceRuleUpdateCommand,
 
+		"standard list":                        StandardListCommand,
+		"standard update":                      StandardUpdateCommand,
+		"standard resource-scores list":        StandardListResourceScoresCommand,
+		"standard multi-resources-scores list": StandardListMultiResourcesScoresCommand,
+
 		"team list":                     TeamListCommand,
 		"team create":                   TeamShowCommand,
 		"team delete":                   TeamDeleteCommand,
diff --git a/command/standard_list.go b/command/standard_list.go
new file mode 100644
index 0000000..85dedda
--- /dev/null
+++ b/command/standard_list.go
@@ -0,0 +1,78 @@
+package main
+
+import (
+	"context"
+	"fmt"
+	"strings"
+
+	"github.com/PagerDuty/go-pagerduty"
+	"github.com/mitchellh/cli"
+	log "github.com/sirupsen/logrus"
+	"gopkg.in/yaml.v2"
+)
+
+type StandardList struct {
+	Meta
+}
+
+func (c *StandardList) Help() string {
+	helpText := `
+	pd standard list List all of the existing standards
+
+	Options:
+
+    -active
+    -resource-type    Filter by resource type. Allowed values: technical_service
+
+	` + c.Meta.Help()
+	return strings.TrimSpace(helpText)
+}
+
+func (c *StandardList) Synopsis() string {
+	return "List all of the existing standards"
+}
+
+func StandardListCommand() (cli.Command, error) {
+	return &StandardList{}, nil
+}
+
+func (c *StandardList) Run(args []string) int {
+	var active bool
+	var resourceType string
+
+	flags := c.Meta.FlagSet("standard list")
+	flags.Usage = func() { fmt.Println(c.Help()) }
+	flags.BoolVar(&active, "active", false, "")
+	flags.StringVar(&resourceType, "resource-type", "", "Filter by resource type. Allowed values: technical_service")
+
+	if err := flags.Parse(args); err != nil {
+		log.Error(err)
+		return -1
+	}
+	if err := c.Meta.Setup(); err != nil {
+		log.Error(err)
+		return -1
+	}
+	client := c.Meta.Client()
+	opts := pagerduty.ListStandardsOptions{
+		Active:       active,
+		ResourceType: resourceType,
+	}
+	if res, err := client.ListStandards(context.Background(), opts); err != nil {
+		log.Error(err)
+		return -1
+	} else {
+		for i, r := range res.Standards {
+			if i > 0 {
+				fmt.Println("---")
+			}
+			data, err := yaml.Marshal(r)
+			if err != nil {
+				log.Error(err)
+				return -1
+			}
+			fmt.Println(string(data))
+		}
+	}
+	return 0
+}
diff --git a/command/standard_multi_resources_scores_list.go b/command/standard_multi_resources_scores_list.go
new file mode 100644
index 0000000..1e6359b
--- /dev/null
+++ b/command/standard_multi_resources_scores_list.go
@@ -0,0 +1,77 @@
+package main
+
+import (
+	"context"
+	"fmt"
+	"strings"
+
+	"github.com/PagerDuty/go-pagerduty"
+	"github.com/mitchellh/cli"
+	log "github.com/sirupsen/logrus"
+	"gopkg.in/yaml.v2"
+)
+
+type StandardListMultiResourcesScores struct {
+	Meta
+}
+
+func (c *StandardListMultiResourcesScores) Help() string {
+	helpText := `
+	pd standard multi-resources-scores list List all of the existing standards scores for multiple resources
+
+	Options:
+
+    -id    List standard scores applied to resource (can be specified multiple times)
+    -resource-type    List scores applied to resource type (default: technical_services). Allowed values: technical_services
+
+	` + c.Meta.Help()
+	return strings.TrimSpace(helpText)
+}
+
+func (c *StandardListMultiResourcesScores) Synopsis() string {
+	return "List all of the existing standards scores applied to multiple resources"
+}
+
+func StandardListMultiResourcesScoresCommand() (cli.Command, error) {
+	return &StandardListMultiResourcesScores{}, nil
+}
+
+func (c *StandardListMultiResourcesScores) Run(args []string) int {
+	var resIDs []string
+	var resourceType string
+
+	flags := c.Meta.FlagSet("standard multi-resources-scores list")
+	flags.Usage = func() { fmt.Println(c.Help()) }
+	flags.Var((*ArrayFlags)(&resIDs), "id", "Display scores for standard applied to resource (can be specified multiple times)")
+	flags.StringVar(&resourceType, "resource-type", "technical_services", "Scores applied to resource type (default: technical_services). Allowed values: technical_services")
+
+	if err := flags.Parse(args); err != nil {
+		log.Error(err)
+		return -1
+	}
+	if err := c.Meta.Setup(); err != nil {
+		log.Error(err)
+		return -1
+	}
+	client := c.Meta.Client()
+	opts := pagerduty.ListMultiResourcesStandardScoresOptions{
+		IDs: resIDs,
+	}
+	if res, err := client.ListMultiResourcesStandardScores(context.Background(), resourceType, opts); err != nil {
+		log.Error(err)
+		return -1
+	} else {
+		for i, r := range res.Resources {
+			if i > 0 {
+				fmt.Println("---")
+			}
+			data, err := yaml.Marshal(r)
+			if err != nil {
+				log.Error(err)
+				return -1
+			}
+			fmt.Println(string(data))
+		}
+	}
+	return 0
+}
diff --git a/command/standard_resource_scores_list.go b/command/standard_resource_scores_list.go
new file mode 100644
index 0000000..2026665
--- /dev/null
+++ b/command/standard_resource_scores_list.go
@@ -0,0 +1,68 @@
+package main
+
+import (
+	"context"
+	"fmt"
+	"strings"
+
+	"github.com/mitchellh/cli"
+	log "github.com/sirupsen/logrus"
+	"gopkg.in/yaml.v2"
+)
+
+type StandardListResourceScores struct {
+	Meta
+}
+
+func (c *StandardListResourceScores) Help() string {
+	helpText := `
+	pd standard resource-scores list List all of the existing standards scores for a specific resource
+
+	Options:
+
+    -id
+    -resource-type    List scores applied to resource type (default: technical_services). Allowed values: technical_services
+
+	` + c.Meta.Help()
+	return strings.TrimSpace(helpText)
+}
+
+func (c *StandardListResourceScores) Synopsis() string {
+	return "List all of the existing standards scores applied to a specific resource"
+}
+
+func StandardListResourceScoresCommand() (cli.Command, error) {
+	return &StandardListResourceScores{}, nil
+}
+
+func (c *StandardListResourceScores) Run(args []string) int {
+	var resID string
+	var resourceType string
+
+	flags := c.Meta.FlagSet("standard resource-scores list")
+	flags.Usage = func() { fmt.Println(c.Help()) }
+	flags.StringVar(&resID, "id", "", "Resource id")
+	flags.StringVar(&resourceType, "resource-type", "technical_services", "Scores applied to resource type (default: technical_services). Allowed values: technical_services")
+
+	if err := flags.Parse(args); err != nil {
+		log.Error(err)
+		return -1
+	}
+	if err := c.Meta.Setup(); err != nil {
+		log.Error(err)
+		return -1
+	}
+	client := c.Meta.Client()
+	if res, err := client.ListResourceStandardScores(context.Background(), resID, resourceType); err != nil {
+		log.Error(err)
+		return -1
+	} else {
+		data, err := yaml.Marshal(res)
+		if err != nil {
+			log.Error(err)
+			return -1
+		}
+		fmt.Println(string(data))
+	}
+	return 0
+}
diff --git a/command/standard_update.go b/command/standard_update.go
new file mode 100644
index 0000000..02e8171
--- /dev/null
+++ b/command/standard_update.go
@@ -0,0 +1,84 @@
+package main
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"os"
+	"strings"
+
+	"github.com/PagerDuty/go-pagerduty"
+	log "github.com/sirupsen/logrus"
+
+	"github.com/mitchellh/cli"
+)
+
+type StandardUpdate struct {
+	Meta
+}
+
+func StandardUpdateCommand() (cli.Command, error) {
+	return &StandardUpdate{}, nil
+}
+
+func (c *StandardUpdate) Help() string {
+	helpText := `
+	pd standard update <FILE> Update an standard from json file
+	Options:
+
+		 -id
+
+	` + c.Meta.Help()
+	return strings.TrimSpace(helpText)
+}
+
+func (c *StandardUpdate) Synopsis() string {
+	return "Update an existing standard"
+}
+
+func (c *StandardUpdate) Run(args []string) int {
+	var standardID string
+
+	flags := c.Meta.FlagSet("standard update")
+	flags.Usage = func() { fmt.Println(c.Help()) }
+	flags.StringVar(&standardID, "id", "", "standard id")
+	if err := flags.Parse(args); err != nil {
+		log.Error(err)
+		return -1
+	}
+	if err := c.Meta.Setup(); err != nil {
+		log.Error(err)
+		return -1
+	}
+	if standardID == "" {
+		log.Error("You must provide standard id")
+		return -1
+	}
+
+	client := c.Meta.Client()
+	var standard pagerduty.Standard
+	if len(flags.Args()) != 1 {
+		log.Error("Please specify input json file")
+		return -1
+	}
+
+	log.Info("Input file is:", flags.Arg(0))
+	f, err := os.ReadFile(flags.Arg(0))
+	if err != nil {
+		log.Error(err)
+		return -1
+	}
+
+	if err := json.Unmarshal(f, &standard); err != nil {
+		log.Errorln("Failed to decode json. Error:", err)
+		return -1
+	}
+
+	log.Debugf("%#v", standard)
+	if _, err := client.UpdateStandard(context.Background(), standardID, standard); err != nil {
+		log.Error(err)
+		return -1
+	}
+
+	return 0
+}
diff --git a/standard.go b/standard.go
new file mode 100644
index 0000000..792dde8
--- /dev/null
+++ b/standard.go
@@ -0,0 +1,148 @@
+package pagerduty
+
+import (
+	"context"
+
+	"github.com/google/go-querystring/query"
+)
+
+const (
+	standardPath = "/standards"
+)
+
+// Standard defines a PagerDuty resource standard.
+type Standard struct {
+	Active       bool                         `json:"active"`
+	Description  string                       `json:"description,omitempty"`
+	Exclusions   []StandardInclusionExclusion `json:"exclusions,omitempty"`
+	ID           string                       `json:"id,omitempty"`
+	Inclusions   []StandardInclusionExclusion `json:"inclusions,omitempty"`
+	Name         string                       `json:"name,omitempty"`
+	ResourceType string                       `json:"resource_type,omitempty"`
+	Type         string                       `json:"type,omitempty"`
+}
+
+type StandardInclusionExclusion struct {
+	Type string `json:"type,omitempty"`
+	ID   string `json:"id,omitempty"`
+}
+
+// ListStandardsResponse is the data structure returned from calling the ListStandards API endpoint.
+type ListStandardsResponse struct {
+	Standards []Standard `json:"standards"`
+}
+
+// ListStandardsOptions is the data structure used when calling the ListStandards API endpoint.
+type ListStandardsOptions struct {
+	Active bool `url:"active,omitempty"`
+
+	// ResourceType query for a specific resource type.
+	//  Allowed value: technical_service
+	ResourceType string `url:"resource_type,omitempty"`
+}
+
+type ResourceStandardScore struct {
+	ResourceID   string             `json:"resource_id,omitempty"`
+	ResourceType string             `json:"resource_type,omitempty"`
+	Score        *ResourceScore     `json:"score,omitempty"`
+	Standards    []ResourceStandard `json:"standards,omitempty"`
+}
+
+type ResourceScore struct {
+	Passing int `json:"passing,omitempty"`
+	Total   int `json:"total,omitempty"`
+}
+
+type ResourceStandard struct {
+	Active      bool   `json:"active"`
+	Description string `json:"description,omitempty"`
+	ID          string `json:"id,omitempty"`
+	Name        string `json:"name,omitempty"`
+	Pass        bool   `json:"pass"`
+	Type        string `json:"type,omitempty"`
+}
+
+type ListMultiResourcesStandardScoresResponse struct {
+	Resources []ResourceStandardScore `json:"resources,omitempty"`
+}
+
+type ListMultiResourcesStandardScoresOptions struct {
+	// Ids of resources to apply the standards. Maximum of 100 items
+	IDs []string `url:"ids,omitempty,brackets"`
+}
+
+// ListStandards lists all the existing standards.
+func (c *Client) ListStandards(ctx context.Context, o ListStandardsOptions) (*ListStandardsResponse, error) {
+	v, err := query.Values(o)
+	if err != nil {
+		return nil, err
+	}
+
+	resp, err := c.get(ctx, standardPath+"?"+v.Encode())
+	if err != nil {
+		return nil, err
+	}
+
+	var result ListStandardsResponse
+	if err = c.decodeJSON(resp, &result); err != nil {
+		return nil, err
+	}
+
+	return &result, nil
+}
+
+// UpdateStandard updates an existing standard.
+func (c *Client) UpdateStandard(ctx context.Context, id string, s Standard) (*Standard, error) {
+	resp, err := c.put(ctx, standardPath+"/"+id, s, nil)
+	if err != nil {
+		return nil, err
+	}
+
+	var result Standard
+	if err = c.decodeJSON(resp, &result); err != nil {
+		return nil, err
+	}
+
+	return &result, nil
+}
+
+// ListResourceStandardScores
+//
+//	rt - Resource type
+//	Allowed values: technical_services
+func (c *Client) ListResourceStandardScores(ctx context.Context, id string, rt string) (*ResourceStandardScore, error) {
+	resp, err := c.get(ctx, standardPath+"/scores/"+rt+"/"+id)
+	if err != nil {
+		return nil, err
+	}
+
+	var result ResourceStandardScore
+	if err = c.decodeJSON(resp, &result); err != nil {
+		return nil, err
+	}
+
+	return &result, nil
+}
+
+// ListMultiResourcesStandardScores
+//
+//	rt - Resource type
+//	Allowed values: technical_services
+func (c *Client) ListMultiResourcesStandardScores(ctx context.Context, rt string, o ListMultiResourcesStandardScoresOptions) (*ListMultiResourcesStandardScoresResponse, error) {
+	v, err := query.Values(o)
+	if err != nil {
+		return nil, err
+	}
+
+	resp, err := c.get(ctx, standardPath+"/scores/"+rt+"?"+v.Encode())
+	if err != nil {
+		return nil, err
+	}
+
+	var result ListMultiResourcesStandardScoresResponse
+	if err = c.decodeJSON(resp, &result); err != nil {
+		return nil, err
+	}
+
+	return &result, nil
+}


Test patch:
diff --git a/standard_test.go b/standard_test.go
new file mode 100644
index 0000000..f83a1f1
--- /dev/null
+++ b/standard_test.go
@@ -0,0 +1,158 @@
+package pagerduty
+
+import (
+	"context"
+	"net/http"
+	"testing"
+)
+
+func TestStandard_List(t *testing.T) {
+	setup()
+	defer teardown()
+
+	mux.HandleFunc("/standards", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		_, _ = w.Write([]byte(`{"standards":[{"active":true,"description":"A description provides critical context about what a service represents or is used for to inform team members and responders. The description should be kept concise and understandable by those without deep knowledge of the service.","id":"1","inclusions":[{"type":"technical_service_reference","id":"1"}],"name":"Service has a description","resource_type":"technical_service","type":"has_technical_service_description"}]}`))
+	})
+
+	var opts ListStandardsOptions
+	client := defaultTestClient(server.URL, standardPath)
+
+	res, err := client.ListStandards(context.Background(), opts)
+
+	want := &ListStandardsResponse{
+		Standards: []Standard{
+			{
+				Active:      true,
+				ID:          "1",
+				Description: "A description provides critical context about what a service represents or is used for to inform team members and responders. The description should be kept concise and understandable by those without deep knowledge of the service.",
+				Inclusions: []StandardInclusionExclusion{
+					{
+						ID:   "1",
+						Type: "technical_service_reference",
+					},
+				},
+				Name:         "Service has a description",
+				ResourceType: "technical_service",
+				Type:         "has_technical_service_description",
+			},
+		},
+	}
+	if err != nil {
+		t.Fatal(err)
+	}
+	testEqual(t, want, res)
+}
+
+func TestStandard_Update(t *testing.T) {
+	setup()
+	defer teardown()
+
+	mux.HandleFunc("/standards/1", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "PUT")
+		_, _ = w.Write([]byte(`{"active":true,"description":"A description provides critical context about what a service represents or is used for to inform team members and responders. The description should be kept concise and understandable by those without deep knowledge of the service.","id":"1","inclusions":[{"type":"technical_service_reference","id":"1"}],"name":"Service has a description","resource_type":"technical_service","type":"has_technical_service_description"}`))
+	})
+
+	client := defaultTestClient(server.URL, "foo")
+	input := &Standard{Active: true}
+	want := &Standard{
+		Active:      true,
+		ID:          "1",
+		Description: "A description provides critical context about what a service represents or is used for to inform team members and responders. The description should be kept concise and understandable by those without deep knowledge of the service.",
+		Inclusions: []StandardInclusionExclusion{
+			{
+				ID:   "1",
+				Type: "technical_service_reference",
+			},
+		},
+		Name:         "Service has a description",
+		ResourceType: "technical_service",
+		Type:         "has_technical_service_description",
+	}
+	res, err := client.UpdateStandard(context.Background(), "1", *input)
+	if err != nil {
+		t.Fatal(err)
+	}
+	testEqual(t, want, res)
+}
+
+func TestStandard_ListTechniServiceStandardScores(t *testing.T) {
+	setup()
+	defer teardown()
+
+	mux.HandleFunc("/standards/scores/technical_services/1", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		_, _ = w.Write([]byte(`{"resource_id":"1","resource_type":"technical_service","score":{"passing":1,"total":1},"standards":[{"active":true,"description":"A description provides critical context about what a service represents or is used for to inform team members and responders. The description should be kept concise and understandable by those without deep knowledge of the service.","id":"01CXX38Q0U8XKHO4LNKXUJTBFG","pass":true,"name":"Service has a description","type":"has_technical_service_description"}]}`))
+	})
+
+	client := defaultTestClient(server.URL, standardPath)
+
+	res, err := client.ListResourceStandardScores(context.Background(), "1", "technical_services")
+
+	want := &ResourceStandardScore{
+		ResourceID:   "1",
+		ResourceType: "technical_service",
+		Score: &ResourceScore{
+			Passing: 1,
+			Total:   1,
+		},
+		Standards: []ResourceStandard{
+			{
+				Active:      true,
+				Description: "A description provides critical context about what a service represents or is used for to inform team members and responders. The description should be kept concise and understandable by those without deep knowledge of the service.",
+				ID:          "01CXX38Q0U8XKHO4LNKXUJTBFG",
+				Pass:        true,
+				Name:        "Service has a description",
+				Type:        "has_technical_service_description",
+			},
+		},
+	}
+	if err != nil {
+		t.Fatal(err)
+	}
+	testEqual(t, want, res)
+}
+
+func TestStandard_ListManyTechniServiceStandardScores(t *testing.T) {
+	setup()
+	defer teardown()
+
+	mux.HandleFunc("/standards/scores/technical_services", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		_, _ = w.Write([]byte(`{"resources":[{"resource_id":"1","resource_type":"technical_service","score":{"passing":1,"total":1},"standards":[{"active":true,"description":"A description provides critical context about what a service represents or is used for to inform team members and responders. The description should be kept concise and understandable by those without deep knowledge of the service.","id":"01CXX38Q0U8XKHO4LNKXUJTBFG","pass":true,"name":"Service has a description","type":"has_technical_service_description"}]}]}`))
+	})
+
+	opts := ListMultiResourcesStandardScoresOptions{
+		IDs: []string{"1"},
+	}
+	client := defaultTestClient(server.URL, standardPath)
+
+	res, err := client.ListMultiResourcesStandardScores(context.Background(), "technical_services", opts)
+
+	want := &ListMultiResourcesStandardScoresResponse{
+		Resources: []ResourceStandardScore{
+			{
+				ResourceID:   "1",
+				ResourceType: "technical_service",
+				Score: &ResourceScore{
+					Passing: 1,
+					Total:   1,
+				},
+				Standards: []ResourceStandard{
+					{
+						Active:      true,
+						Description: "A description provides critical context about what a service represents or is used for to inform team members and responders. The description should be kept concise and understandable by those without deep knowledge of the service.",
+						ID:          "01CXX38Q0U8XKHO4LNKXUJTBFG",
+						Pass:        true,
+						Name:        "Service has a description",
+						Type:        "has_technical_service_description",
+					},
+				},
+			},
+		},
+	}
+	if err != nil {
+		t.Fatal(err)
+	}
+	testEqual(t, want, res)
+}