1package cfclient
2
3import (
4	"bytes"
5	"encoding/json"
6	"fmt"
7	"io"
8	"io/ioutil"
9	"net/http"
10	"net/url"
11
12	"github.com/pkg/errors"
13)
14
15type ServiceInstancesResponse struct {
16	Count     int                       `json:"total_results"`
17	Pages     int                       `json:"total_pages"`
18	NextUrl   string                    `json:"next_url"`
19	Resources []ServiceInstanceResource `json:"resources"`
20}
21
22type ServiceInstanceRequest struct {
23	Name            string                 `json:"name"`
24	SpaceGuid       string                 `json:"space_guid"`
25	ServicePlanGuid string                 `json:"service_plan_guid"`
26	Parameters      map[string]interface{} `json:"parameters,omitempty"`
27	Tags            []string               `json:"tags,omitempty"`
28}
29
30type ServiceInstanceResource struct {
31	Meta   Meta            `json:"metadata"`
32	Entity ServiceInstance `json:"entity"`
33}
34
35type ServiceInstance struct {
36	Name               string                 `json:"name"`
37	CreatedAt          string                 `json:"created_at"`
38	UpdatedAt          string                 `json:"updated_at"`
39	Credentials        map[string]interface{} `json:"credentials"`
40	ServicePlanGuid    string                 `json:"service_plan_guid"`
41	SpaceGuid          string                 `json:"space_guid"`
42	DashboardUrl       string                 `json:"dashboard_url"`
43	Type               string                 `json:"type"`
44	LastOperation      LastOperation          `json:"last_operation"`
45	Tags               []string               `json:"tags"`
46	ServiceGuid        string                 `json:"service_guid"`
47	SpaceUrl           string                 `json:"space_url"`
48	ServicePlanUrl     string                 `json:"service_plan_url"`
49	ServiceBindingsUrl string                 `json:"service_bindings_url"`
50	ServiceKeysUrl     string                 `json:"service_keys_url"`
51	RoutesUrl          string                 `json:"routes_url"`
52	ServiceUrl         string                 `json:"service_url"`
53	Guid               string                 `json:"guid"`
54	c                  *Client
55}
56
57type LastOperation struct {
58	Type        string `json:"type"`
59	State       string `json:"state"`
60	Description string `json:"description"`
61	UpdatedAt   string `json:"updated_at"`
62	CreatedAt   string `json:"created_at"`
63}
64
65func (c *Client) ListServiceInstancesByQuery(query url.Values) ([]ServiceInstance, error) {
66	var instances []ServiceInstance
67
68	requestUrl := "/v2/service_instances?" + query.Encode()
69	for {
70		var sir ServiceInstancesResponse
71		r := c.NewRequest("GET", requestUrl)
72		resp, err := c.DoRequest(r)
73		if err != nil {
74			return nil, errors.Wrap(err, "Error requesting service instances")
75		}
76		resBody, err := ioutil.ReadAll(resp.Body)
77		if err != nil {
78			return nil, errors.Wrap(err, "Error reading service instances request:")
79		}
80
81		err = json.Unmarshal(resBody, &sir)
82		if err != nil {
83			return nil, errors.Wrap(err, "Error unmarshaling service instances")
84		}
85		for _, instance := range sir.Resources {
86			instances = append(instances, c.mergeServiceInstance(instance))
87		}
88
89		requestUrl = sir.NextUrl
90		if requestUrl == "" {
91			break
92		}
93	}
94	return instances, nil
95}
96
97func (c *Client) ListServiceInstances() ([]ServiceInstance, error) {
98	return c.ListServiceInstancesByQuery(nil)
99}
100
101func (c *Client) GetServiceInstanceByGuid(guid string) (ServiceInstance, error) {
102	var sir ServiceInstanceResource
103	req := c.NewRequest("GET", "/v2/service_instances/"+guid)
104	res, err := c.DoRequest(req)
105	if err != nil {
106		return ServiceInstance{}, errors.Wrap(err, "Error requesting service instance")
107	}
108
109	data, err := ioutil.ReadAll(res.Body)
110	if err != nil {
111		return ServiceInstance{}, errors.Wrap(err, "Error reading service instance response")
112	}
113	err = json.Unmarshal(data, &sir)
114	if err != nil {
115		return ServiceInstance{}, errors.Wrap(err, "Error JSON parsing service instance response")
116	}
117	return c.mergeServiceInstance(sir), nil
118}
119
120func (c *Client) ServiceInstanceByGuid(guid string) (ServiceInstance, error) {
121	return c.GetServiceInstanceByGuid(guid)
122}
123
124func (c *Client) mergeServiceInstance(instance ServiceInstanceResource) ServiceInstance {
125	instance.Entity.Guid = instance.Meta.Guid
126	instance.Entity.CreatedAt = instance.Meta.CreatedAt
127	instance.Entity.UpdatedAt = instance.Meta.UpdatedAt
128	instance.Entity.c = c
129	return instance.Entity
130}
131
132func (c *Client) CreateServiceInstance(req ServiceInstanceRequest) (ServiceInstance, error) {
133	var sir ServiceInstanceResource
134
135	buf := bytes.NewBuffer(nil)
136	err := json.NewEncoder(buf).Encode(req)
137	if err != nil {
138		return ServiceInstance{}, err
139	}
140
141	r := c.NewRequestWithBody("POST", "/v2/service_instances?accepts_incomplete=true", buf)
142
143	res, err := c.DoRequest(r)
144	if err != nil {
145		return ServiceInstance{}, err
146	}
147
148	if res.StatusCode != http.StatusAccepted && res.StatusCode != http.StatusCreated {
149		return ServiceInstance{}, errors.Wrapf(err, "Error creating service, response code: %d", res.StatusCode)
150	}
151
152	data, err := ioutil.ReadAll(res.Body)
153	if err != nil {
154		return ServiceInstance{}, errors.Wrap(err, "Error reading service instance response")
155	}
156
157	err = json.Unmarshal(data, &sir)
158	if err != nil {
159		return ServiceInstance{}, errors.Wrap(err, "Error JSON parsing service instance response")
160	}
161
162	return c.mergeServiceInstance(sir), nil
163}
164
165func (c *Client) UpdateServiceInstance(serviceInstanceGuid string, updatedConfiguration io.Reader, async bool) error {
166	u := fmt.Sprintf("/v2/service_instances/%s?accepts_incomplete=%t", serviceInstanceGuid, async)
167	resp, err := c.DoRequest(c.NewRequestWithBody("PUT", u, updatedConfiguration))
168	if err != nil {
169		return err
170	}
171	if resp.StatusCode != http.StatusAccepted {
172		return errors.Wrapf(err, "Error updating service instance %s, response code %d", serviceInstanceGuid, resp.StatusCode)
173	}
174	return nil
175}
176
177func (c *Client) DeleteServiceInstance(guid string, recursive, async bool) error {
178	resp, err := c.DoRequest(c.NewRequest("DELETE", fmt.Sprintf("/v2/service_instances/%s?recursive=%t&accepts_incomplete=%t&async=%t", guid, recursive, async, async)))
179	if err != nil {
180		return err
181	}
182	if resp.StatusCode != http.StatusAccepted {
183		return errors.Wrapf(err, "Error deleting service instance %s, response code %d", guid, resp.StatusCode)
184	}
185	return nil
186}
187