Skip to content

Commit e5ccc17

Browse files
committed
feat(services): add services api support to sdk.
create structures and API for calling Service REST API of controller
1 parent 511ea0e commit e5ccc17

3 files changed

Lines changed: 265 additions & 0 deletions

File tree

api/services.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package api
2+
3+
// Service is the structure of the service object.
4+
type Service struct {
5+
ProcfileType string `json:"procfile_type"`
6+
PathPattern string `json:"path_pattern"`
7+
}
8+
9+
// Services defines a collection of service objects.
10+
type Services []Service
11+
12+
func (s Services) Len() int { return len(s) }
13+
func (s Services) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
14+
func (s Services) Less(i, j int) bool { return s[i].ProcfileType < s[j].ProcfileType }
15+
16+
// ServiceCreateUpdateRequest is the structure of POST /v2/app/<app id>/services/.
17+
type ServiceCreateUpdateRequest struct {
18+
ProcfileType string `json:"procfile_type"`
19+
PathPattern string `json:"path_pattern"`
20+
}
21+
22+
// ServiceDeleteRequest is the structure of DELETE /v2/app/<app id>/services/.
23+
type ServiceDeleteRequest struct {
24+
ProcfileType string `json:"procfile_type"`
25+
}

services/services.go

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
// Package services provides methods for managing an app's services.
2+
package services
3+
4+
import (
5+
"encoding/json"
6+
"fmt"
7+
"io/ioutil"
8+
9+
deis "github.com/workato/deis-controller-sdk-go"
10+
"github.com/workato/deis-controller-sdk-go/api"
11+
)
12+
13+
// List services registered with an app.
14+
func List(c *deis.Client, appID string) (api.Services, error) {
15+
u := fmt.Sprintf("/v2/apps/%s/services/", appID)
16+
res, reqErr := c.Request("GET", u, nil)
17+
18+
if reqErr != nil && !deis.IsErrAPIMismatch(reqErr) {
19+
return []api.Service{}, reqErr
20+
}
21+
22+
defer res.Body.Close()
23+
24+
body, err := ioutil.ReadAll(res.Body)
25+
if err != nil {
26+
return []api.Service{}, err
27+
}
28+
29+
r := make(map[string]interface{})
30+
if err = json.Unmarshal([]byte(body), &r); err != nil {
31+
return []api.Service{}, err
32+
}
33+
34+
out, err := json.Marshal(r["services"].([]interface{}))
35+
if err != nil {
36+
return []api.Service{}, err
37+
}
38+
39+
var services []api.Service
40+
if err := json.Unmarshal([]byte(out), &services); err != nil {
41+
return []api.Service{}, err
42+
}
43+
44+
return services, reqErr
45+
}
46+
47+
// New adds a service to an app.
48+
func New(c *deis.Client, appID string, procfileType string, pathPattern string) (api.Service, error) {
49+
u := fmt.Sprintf("/v2/apps/%s/services/", appID)
50+
51+
req := api.ServiceCreateUpdateRequest{ProcfileType: procfileType, PathPattern: pathPattern}
52+
53+
body, err := json.Marshal(req)
54+
55+
if err != nil {
56+
return api.Service{}, err
57+
}
58+
59+
res, reqErr := c.Request("POST", u, body)
60+
if reqErr != nil && !deis.IsErrAPIMismatch(reqErr) {
61+
return api.Service{}, reqErr
62+
}
63+
defer res.Body.Close()
64+
65+
d := api.Service{ProcfileType: procfileType, PathPattern: pathPattern}
66+
return d, reqErr
67+
}
68+
69+
// Delete service from app
70+
func Delete(c *deis.Client, appID string, procfileType string) error {
71+
u := fmt.Sprintf("/v2/apps/%s/services/", appID)
72+
73+
req := api.ServiceDeleteRequest{ProcfileType: procfileType}
74+
75+
body, err := json.Marshal(req)
76+
77+
if err != nil {
78+
return err
79+
}
80+
81+
_, err = c.Request("DELETE", u, body)
82+
83+
return err
84+
}

services/services_test.go

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
package services
2+
3+
import (
4+
"fmt"
5+
"io/ioutil"
6+
"net/http"
7+
"net/http/httptest"
8+
"reflect"
9+
"testing"
10+
11+
deis "github.com/workato/deis-controller-sdk-go"
12+
"github.com/workato/deis-controller-sdk-go/api"
13+
)
14+
15+
const servicesFixture string = `
16+
{
17+
"services": [
18+
{
19+
"procfile_type": "web",
20+
"path_pattern": "/abc/xyz"
21+
},
22+
{
23+
"procfile_type": "worker",
24+
"path_pattern": "/x1z/a2c"
25+
}
26+
]
27+
}`
28+
29+
const serviceFixture string = `
30+
{
31+
"procfile_type": "web",
32+
"path_pattern": "/abc/xyz"
33+
}`
34+
35+
const serviceCreateExpected string = `{"procfile_type":"web","path_pattern":"/abc/xyz"}`
36+
37+
type fakeHTTPServer struct{}
38+
39+
func (fakeHTTPServer) ServeHTTP(res http.ResponseWriter, req *http.Request) {
40+
res.Header().Add("DEIS_API_VERSION", deis.APIVersion)
41+
42+
if req.URL.Path == "/v2/apps/example-go/services/" && req.Method == "GET" {
43+
res.Write([]byte(servicesFixture))
44+
return
45+
}
46+
47+
if req.URL.Path == "/v2/apps/example-go/services/" && req.Method == "POST" {
48+
body, err := ioutil.ReadAll(req.Body)
49+
50+
if err != nil {
51+
fmt.Println(err)
52+
res.WriteHeader(http.StatusInternalServerError)
53+
res.Write(nil)
54+
}
55+
56+
if string(body) != serviceCreateExpected {
57+
fmt.Printf("Expected '%s', Got '%s'\n", serviceCreateExpected, body)
58+
res.WriteHeader(http.StatusInternalServerError)
59+
res.Write(nil)
60+
return
61+
}
62+
63+
res.WriteHeader(http.StatusCreated)
64+
res.Write([]byte(serviceFixture))
65+
return
66+
}
67+
68+
if req.URL.Path == "/v2/apps/example-go/services/" && req.Method == "DELETE" {
69+
res.WriteHeader(http.StatusNoContent)
70+
res.Write([]byte(servicesFixture))
71+
return
72+
}
73+
74+
fmt.Printf("Unrecognized URL %s\n", req.URL)
75+
res.WriteHeader(http.StatusNotFound)
76+
res.Write(nil)
77+
}
78+
79+
func TestServicesList(t *testing.T) {
80+
t.Parallel()
81+
82+
expected := api.Services{
83+
{
84+
ProcfileType: "web",
85+
PathPattern: "/abc/xyz",
86+
},
87+
{
88+
ProcfileType: "worker",
89+
PathPattern: "/x1z/a2c",
90+
},
91+
}
92+
93+
handler := fakeHTTPServer{}
94+
server := httptest.NewServer(handler)
95+
defer server.Close()
96+
97+
deis, err := deis.New(false, server.URL, "abc")
98+
if err != nil {
99+
t.Fatal(err)
100+
}
101+
102+
actual, err := List(deis, "example-go")
103+
104+
if err != nil {
105+
t.Fatal(err)
106+
}
107+
108+
if !reflect.DeepEqual(expected, actual) {
109+
t.Error(fmt.Errorf("Expected %v, Got %v", expected, actual))
110+
}
111+
}
112+
113+
func TestServicesAdd(t *testing.T) {
114+
t.Parallel()
115+
116+
expected := api.Service{
117+
ProcfileType: "web",
118+
PathPattern: "/abc/xyz",
119+
}
120+
121+
handler := fakeHTTPServer{}
122+
server := httptest.NewServer(handler)
123+
defer server.Close()
124+
125+
deis, err := deis.New(false, server.URL, "abc")
126+
if err != nil {
127+
t.Fatal(err)
128+
}
129+
130+
actual, err := New(deis, "example-go", "web", "/abc/xyz")
131+
132+
if err != nil {
133+
t.Fatal(err)
134+
}
135+
136+
if !reflect.DeepEqual(expected, actual) {
137+
t.Error(fmt.Errorf("Expected %v, Got %v", expected, actual))
138+
}
139+
}
140+
141+
func TestServicesRemove(t *testing.T) {
142+
t.Parallel()
143+
144+
handler := fakeHTTPServer{}
145+
server := httptest.NewServer(handler)
146+
defer server.Close()
147+
148+
deis, err := deis.New(false, server.URL, "abc")
149+
if err != nil {
150+
t.Fatal(err)
151+
}
152+
153+
if err = Delete(deis, "example-go", "web"); err != nil {
154+
t.Fatal(err)
155+
}
156+
}

0 commit comments

Comments
 (0)