Repository: PagerDuty/go-pagerduty
Pull request #228 (2020-06-15 16:58:47)

adding business_service and service_dependency
Adding support for Business Services and Service Dependencies. Test results are here:

```=== RUN   TestBusinessService_List
--- PASS: TestBusinessService_List (0.00s)
=== RUN   TestBusinessService_Create
--- PASS: TestBusinessService_Create (0.00s)
=== RUN   TestBusinessService_Get
--- PASS: TestBusinessService_Get (0.00s)
=== RUN   TestBusinessService_Update
--- PASS: TestBusinessService_Update (0.00s)
=== RUN   TestBusinessService_Delete
--- PASS: TestBusinessService_Delete (0.00s)
=== RUN   TestBusinessServiceDependency_List
--- PASS: TestBusinessServiceDependency_List (0.00s)
=== RUN   TestTechnicalServiceDependency_List
--- PASS: TestTechnicalServiceDependency_List (0.00s)
=== RUN   TestServiceDependency_Associate
--- PASS: TestServiceDependency_Associate (0.00s)
=== RUN   TestServiceDependency_Disassociate
--- PASS: TestServiceDependency_Disassociate (0.00s)
```



Patch:
diff --git a/business_service.go b/business_service.go
new file mode 100644
index 0000000..bdf8f65
--- /dev/null
+++ b/business_service.go
@@ -0,0 +1,127 @@
+package pagerduty
+
+import (
+	"fmt"
+	"net/http"
+
+	"github.com/google/go-querystring/query"
+)
+
+// BusinessService represents a business service.
+type BusinessService struct {
+	ID             string               `json:"id,omitempty"`
+	Name           string               `json:"name,omitempty"`
+	Type           string               `json:"type,omitempty"`
+	Summary        string               `json:"summary,omitempty"`
+	Self           string               `json:"self,omitempty"`
+	PointOfContact string               `json:"point_of_contact,omitempty"`
+	HTMLUrl        string               `json:"html_url,omitempty"`
+	Description    string               `json:"description,omitempty"`
+	Team           *BusinessServiceTeam `json:"team,omitempty"`
+}
+
+// BusinessServiceTeam represents a team object in a business service
+type BusinessServiceTeam struct {
+	ID   string `json:"id,omitempty"`
+	Type string `json:"type,omitempty"`
+	Self string `json:"self,omitempty"`
+}
+
+// BusinessServicePayload represents payload with a business service object
+type BusinessServicePayload struct {
+	BusinessService *BusinessService `json:"business_service,omitempty"`
+}
+
+// ListBusinessServicesResponse represents a list response of business services.
+type ListBusinessServicesResponse struct {
+	Total            uint               `json:"total,omitempty"`
+	BusinessServices []*BusinessService `json:"business_services,omitempty"`
+	Offset           uint               `json:"offset,omitempty"`
+	More             bool               `json:"more,omitempty"`
+	Limit            uint               `json:"limit,omitempty"`
+}
+
+// ListBusinessServiceOptions is the data structure used when calling the ListBusinessServices API endpoint.
+type ListBusinessServiceOptions struct {
+	APIListObject
+}
+
+// ListBusinessServices lists existing business services.
+func (c *Client) ListBusinessServices(o ListBusinessServiceOptions) (*ListBusinessServicesResponse, error) {
+	queryParms, err := query.Values(o)
+	if err != nil {
+		return nil, err
+	}
+	businessServiceResponse := new(ListBusinessServicesResponse)
+	businessServices := make([]*BusinessService, 0)
+
+	// Create a handler closure capable of parsing data from the business_services endpoint
+	// and appending resultant business_services to the return slice.
+	responseHandler := func(response *http.Response) (APIListObject, error) {
+		var result ListBusinessServicesResponse
+		if err := c.decodeJSON(response, &result); err != nil {
+			return APIListObject{}, err
+		}
+
+		businessServices = append(businessServices, result.BusinessServices...)
+
+		// Return stats on the current page. Caller can use this information to
+		// adjust for requesting additional pages.
+		return APIListObject{
+			More:   result.More,
+			Offset: result.Offset,
+			Limit:  result.Limit,
+		}, nil
+	}
+
+	// Make call to get all pages associated with the base endpoint.
+	if err := c.pagedGet("/business_services"+queryParms.Encode(), responseHandler); err != nil {
+		return nil, err
+	}
+	businessServiceResponse.BusinessServices = businessServices
+
+	return businessServiceResponse, nil
+}
+
+// CreateBusinessService creates a new business service.
+func (c *Client) CreateBusinessService(b *BusinessService) (*BusinessService, *http.Response, error) {
+	data := make(map[string]*BusinessService)
+	data["business_service"] = b
+	resp, err := c.post("/business_services", data, nil)
+	return getBusinessServiceFromResponse(c, resp, err)
+}
+
+// GetBusinessService gets details about a business service.
+func (c *Client) GetBusinessService(ID string) (*BusinessService, *http.Response, error) {
+	resp, err := c.get("/business_services/" + ID)
+	return getBusinessServiceFromResponse(c, resp, err)
+}
+
+// DeleteBusinessService deletes a business_service.
+func (c *Client) DeleteBusinessService(ID string) error {
+	_, err := c.delete("/business_services/" + ID)
+	return err
+}
+
+// UpdateBusinessService updates a business_service.
+func (c *Client) UpdateBusinessService(b *BusinessService) (*BusinessService, *http.Response, error) {
+	v := make(map[string]*BusinessService)
+	v["business_service"] = b
+	resp, err := c.put("/business_services/"+b.ID, v, nil)
+	return getBusinessServiceFromResponse(c, resp, err)
+}
+
+func getBusinessServiceFromResponse(c *Client, resp *http.Response, err error) (*BusinessService, *http.Response, error) {
+	if err != nil {
+		return nil, nil, err
+	}
+	var target map[string]BusinessService
+	if dErr := c.decodeJSON(resp, &target); dErr != nil {
+		return nil, nil, fmt.Errorf("Could not decode JSON response: %v", dErr)
+	}
+	t, nodeOK := target["business_service"]
+	if !nodeOK {
+		return nil, nil, fmt.Errorf("JSON response does not have business_service field")
+	}
+	return &t, resp, nil
+}
diff --git a/ruleset.go b/ruleset.go
index 0fb9dd7..e0c188f 100644
--- a/ruleset.go
+++ b/ruleset.go
@@ -173,7 +173,7 @@ func (c *Client) CreateRuleset(r *Ruleset) (*Ruleset, *http.Response, error) {
 	return getRulesetFromResponse(c, resp, err)
 }
 
-// DeleteRuleset deletes a user.
+// DeleteRuleset deletes a ruleset.
 func (c *Client) DeleteRuleset(id string) error {
 	_, err := c.delete("/rulesets/" + id)
 	return err
diff --git a/service_dependency.go b/service_dependency.go
new file mode 100644
index 0000000..7cf0fa2
--- /dev/null
+++ b/service_dependency.go
@@ -0,0 +1,68 @@
+package pagerduty
+
+import (
+	"net/http"
+)
+
+// ServiceDependency represents a relationship between a business and technical service
+type ServiceDependency struct {
+	ID                string      `json:"id,omitempty"`
+	Type              string      `json:"type,omitempty"`
+	SupportingService *ServiceObj `json:"supporting_service,omitempty"`
+	DependentService  *ServiceObj `json:"dependent_service,omitempty"`
+}
+
+// ServiceObj represents a service object in service relationship
+type ServiceObj struct {
+	ID   string `json:"id,omitempty"`
+	Type string `json:"type,omitempty"`
+}
+
+// ListServiceDependencies represents a list of dependencies for a service
+type ListServiceDependencies struct {
+	Relationships []*ServiceDependency `json:"relationships,omitempty"`
+}
+
+// ListBusinessServiceDependencies lists dependencies of a business service.
+func (c *Client) ListBusinessServiceDependencies(businessServiceID string) (*ListServiceDependencies, *http.Response, error) {
+	resp, err := c.get("/service_dependencies/business_services/" + businessServiceID)
+	if err != nil {
+		return nil, nil, err
+	}
+	var result ListServiceDependencies
+	return &result, resp, c.decodeJSON(resp, &result)
+}
+
+// ListTechnicalServiceDependencies lists dependencies of a technical service.
+func (c *Client) ListTechnicalServiceDependencies(serviceID string) (*ListServiceDependencies, *http.Response, error) {
+	resp, err := c.get("/service_dependencies/technical_services/" + serviceID)
+	if err != nil {
+		return nil, nil, err
+	}
+	var result ListServiceDependencies
+	return &result, resp, c.decodeJSON(resp, &result)
+}
+
+// AssociateServiceDependencies Create new dependencies between two services.
+func (c *Client) AssociateServiceDependencies(dependencies *ListServiceDependencies) (*ListServiceDependencies, *http.Response, error) {
+	data := make(map[string]*ListServiceDependencies)
+	data["relationships"] = dependencies
+	resp, err := c.post("/service_dependencies/associate", data, nil)
+	if err != nil {
+		return nil, nil, err
+	}
+	var result ListServiceDependencies
+	return &result, resp, c.decodeJSON(resp, &result)
+}
+
+// DisassociateServiceDependencies Disassociate dependencies between two services.
+func (c *Client) DisassociateServiceDependencies(dependencies *ListServiceDependencies) (*ListServiceDependencies, *http.Response, error) {
+	data := make(map[string]*ListServiceDependencies)
+	data["relationships"] = dependencies
+	resp, err := c.post("/service_dependencies/disassociate", data, nil)
+	if err != nil {
+		return nil, nil, err
+	}
+	var result ListServiceDependencies
+	return &result, resp, c.decodeJSON(resp, &result)
+}


Test patch:
diff --git a/business_service_test.go b/business_service_test.go
new file mode 100644
index 0000000..c8c5227
--- /dev/null
+++ b/business_service_test.go
@@ -0,0 +1,135 @@
+package pagerduty
+
+import (
+	"net/http"
+	"testing"
+)
+
+// List BusinessServices
+func TestBusinessService_List(t *testing.T) {
+	setup()
+	defer teardown()
+
+	mux.HandleFunc("/business_services/", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		w.Write([]byte(`{"business_services": [{"id": "1"}]}`))
+	})
+
+	var listObj = APIListObject{Limit: 0, Offset: 0, More: false, Total: 0}
+	var client = &Client{apiEndpoint: server.URL, authToken: "foo", HTTPClient: defaultHTTPClient}
+	var opts = ListBusinessServiceOptions{
+		APIListObject: listObj,
+	}
+	res, err := client.ListBusinessServices(opts)
+	if err != nil {
+		t.Fatal(err)
+	}
+	want := &ListBusinessServicesResponse{
+		BusinessServices: []*BusinessService{
+			{
+				ID: "1",
+			},
+		},
+	}
+
+	testEqual(t, want, res)
+}
+
+// Create BusinessService
+func TestBusinessService_Create(t *testing.T) {
+	setup()
+	defer teardown()
+
+	mux.HandleFunc("/business_services", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "POST")
+		w.Write([]byte(`{"business_service": {"id": "1", "name": "foo"}}`))
+	})
+
+	var client = &Client{apiEndpoint: server.URL, authToken: "foo", HTTPClient: defaultHTTPClient}
+	input := &BusinessService{
+		Name: "foo",
+	}
+	res, _, err := client.CreateBusinessService(input)
+
+	want := &BusinessService{
+		ID:   "1",
+		Name: "foo",
+	}
+
+	if err != nil {
+		t.Fatal(err)
+	}
+	testEqual(t, want, res)
+}
+
+// Get BusinessService
+func TestBusinessService_Get(t *testing.T) {
+	setup()
+	defer teardown()
+
+	mux.HandleFunc("/business_services/1", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		w.Write([]byte(`{"business_service": {"id": "1", "name":"foo"}}`))
+	})
+
+	var client = &Client{apiEndpoint: server.URL, authToken: "foo", HTTPClient: defaultHTTPClient}
+	ruleSetID := "1"
+
+	res, _, err := client.GetBusinessService(ruleSetID)
+
+	want := &BusinessService{
+		ID:   "1",
+		Name: "foo",
+	}
+
+	if err != nil {
+		t.Fatal(err)
+	}
+	testEqual(t, want, res)
+}
+
+// Update BusinessService
+func TestBusinessService_Update(t *testing.T) {
+	setup()
+	defer teardown()
+
+	mux.HandleFunc("/business_services/1", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "PUT")
+		w.Write([]byte(`{"business_service": {"id": "1", "name":"foo"}}`))
+	})
+
+	var client = &Client{apiEndpoint: server.URL, authToken: "foo", HTTPClient: defaultHTTPClient}
+	input := &BusinessService{
+		ID:   "1",
+		Name: "foo",
+	}
+	res, _, err := client.UpdateBusinessService(input)
+
+	want := &BusinessService{
+		ID:   "1",
+		Name: "foo",
+	}
+
+	if err != nil {
+		t.Fatal(err)
+	}
+	testEqual(t, want, res)
+}
+
+// Delete BusinessService
+func TestBusinessService_Delete(t *testing.T) {
+	setup()
+	defer teardown()
+
+	mux.HandleFunc("/business_services/1", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "DELETE")
+	})
+
+	var client = &Client{apiEndpoint: server.URL, authToken: "foo", HTTPClient: defaultHTTPClient}
+	ID := "1"
+	err := client.DeleteBusinessService(ID)
+
+	if err != nil {
+		t.Fatal(err)
+	}
+}
diff --git a/service_dependency_test.go b/service_dependency_test.go
new file mode 100644
index 0000000..7283f84
--- /dev/null
+++ b/service_dependency_test.go
@@ -0,0 +1,168 @@
+package pagerduty
+
+import (
+	"net/http"
+	"testing"
+)
+
+// List BusinessService Dependencies
+func TestBusinessServiceDependency_List(t *testing.T) {
+	setup()
+	defer teardown()
+
+	mux.HandleFunc("/service_dependencies/business_services/1", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		w.Write([]byte(`{"relationships": [{"id": "1","dependent_service":{"id":"1"},"supporting_service":{"id":"1"},"type":"service_dependency"}]}`))
+	})
+
+	var client = &Client{apiEndpoint: server.URL, authToken: "foo", HTTPClient: defaultHTTPClient}
+	bServeID := "1"
+	res, _, err := client.ListBusinessServiceDependencies(bServeID)
+	if err != nil {
+		t.Fatal(err)
+	}
+	want := &ListServiceDependencies{
+		Relationships: []*ServiceDependency{
+			{
+				ID:   "1",
+				Type: "service_dependency",
+				DependentService: &ServiceObj{
+					ID: "1",
+				},
+				SupportingService: &ServiceObj{
+					ID: "1",
+				},
+			},
+		},
+	}
+
+	testEqual(t, want, res)
+}
+
+// List TechnicalService Dependencies
+func TestTechnicalServiceDependency_List(t *testing.T) {
+	setup()
+	defer teardown()
+
+	mux.HandleFunc("/service_dependencies/technical_services/1", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		w.Write([]byte(`{"relationships": [{"id": "1","dependent_service":{"id":"1"},"supporting_service":{"id":"1"},"type":"service_dependency"}]}`))
+	})
+
+	var client = &Client{apiEndpoint: server.URL, authToken: "foo", HTTPClient: defaultHTTPClient}
+	bServeID := "1"
+	res, _, err := client.ListTechnicalServiceDependencies(bServeID)
+	if err != nil {
+		t.Fatal(err)
+	}
+	want := &ListServiceDependencies{
+		Relationships: []*ServiceDependency{
+			{
+				ID:   "1",
+				Type: "service_dependency",
+				DependentService: &ServiceObj{
+					ID: "1",
+				},
+				SupportingService: &ServiceObj{
+					ID: "1",
+				},
+			},
+		},
+	}
+
+	testEqual(t, want, res)
+}
+
+// AssociateServiceDependencies
+func TestServiceDependency_Associate(t *testing.T) {
+	setup()
+	defer teardown()
+
+	mux.HandleFunc("/service_dependencies/associate", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "POST")
+		w.Write([]byte(`{"relationships": [{"id": "1","dependent_service":{"id":"1"},"supporting_service":{"id":"1"},"type":"service_dependency"}]}`))
+	})
+
+	var client = &Client{apiEndpoint: server.URL, authToken: "foo", HTTPClient: defaultHTTPClient}
+	input := &ListServiceDependencies{
+		Relationships: []*ServiceDependency{
+			{
+				ID:   "1",
+				Type: "service_dependency",
+				DependentService: &ServiceObj{
+					ID: "1",
+				},
+				SupportingService: &ServiceObj{
+					ID: "1",
+				},
+			},
+		},
+	}
+	res, _, err := client.AssociateServiceDependencies(input)
+
+	if err != nil {
+		t.Fatal(err)
+	}
+	want := &ListServiceDependencies{
+		Relationships: []*ServiceDependency{
+			{
+				ID:   "1",
+				Type: "service_dependency",
+				DependentService: &ServiceObj{
+					ID: "1",
+				},
+				SupportingService: &ServiceObj{
+					ID: "1",
+				},
+			},
+		},
+	}
+	testEqual(t, want, res)
+}
+
+// DisassociateServiceDependencies
+func TestServiceDependency_Disassociate(t *testing.T) {
+	setup()
+	defer teardown()
+
+	mux.HandleFunc("/service_dependencies/disassociate", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "POST")
+		w.Write([]byte(`{"relationships": [{"id": "1","dependent_service":{"id":"1"},"supporting_service":{"id":"1"},"type":"service_dependency"}]}`))
+	})
+
+	var client = &Client{apiEndpoint: server.URL, authToken: "foo", HTTPClient: defaultHTTPClient}
+	input := &ListServiceDependencies{
+		Relationships: []*ServiceDependency{
+			{
+				ID:   "1",
+				Type: "service_dependency",
+				DependentService: &ServiceObj{
+					ID: "1",
+				},
+				SupportingService: &ServiceObj{
+					ID: "1",
+				},
+			},
+		},
+	}
+	res, _, err := client.DisassociateServiceDependencies(input)
+
+	if err != nil {
+		t.Fatal(err)
+	}
+	want := &ListServiceDependencies{
+		Relationships: []*ServiceDependency{
+			{
+				ID:   "1",
+				Type: "service_dependency",
+				DependentService: &ServiceObj{
+					ID: "1",
+				},
+				SupportingService: &ServiceObj{
+					ID: "1",
+				},
+			},
+		},
+	}
+	testEqual(t, want, res)
+}