-
-
Notifications
You must be signed in to change notification settings - Fork 4
/
backstage.go
138 lines (110 loc) · 3.1 KB
/
backstage.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
package backstage
import (
"bytes"
"context"
"encoding/json"
"io"
"net/http"
"net/url"
"strings"
)
type service struct {
client *Client
apiPath string
}
const (
DefaultNamespaceName = "default"
userAgent = "go-backstage"
contentTypeJSON = "application/json"
)
// Client manages communication with the Backstage API.
type Client struct {
// Client is an HTTP client used to communicate with the API.
client *http.Client
// BaseURL for API requests, e.g. http://localhost:7007/api/.
BaseURL *url.URL
// User agent used when communicating with the Backstage API.
UserAgent string
// Name of the namespace to use by default when communicating with the Backstage API.
DefaultNamespace string
// Catalog service to handle communication with the Backstage Catalog API.
Catalog *catalogService
}
// NewClient returns a new Backstage API client. If a nil httpClient is provided, a new http.Client will be used.
// To use API methods which require authentication, provide a http.Client that will perform the authentication.
func NewClient(baseURL string, defaultNamespace string, httpClient *http.Client) (*Client, error) {
const apiPath = "/api"
baseURL = strings.TrimSuffix(baseURL, "/")
if !strings.HasSuffix(baseURL, apiPath) {
baseURL += apiPath
}
baseEndpoint, err := url.Parse(baseURL)
if err != nil {
return nil, err
}
if httpClient == nil {
httpClient = &http.Client{}
}
ns := defaultNamespace
if defaultNamespace == "" {
ns = DefaultNamespaceName
}
c := &Client{
client: httpClient,
BaseURL: baseEndpoint,
UserAgent: userAgent,
DefaultNamespace: ns,
}
c.Catalog = newCatalogService(c)
return c, nil
}
// newRequest creates an API request. A relative URL can be provided in urlStr, in which case it is resolved relative to the BaseURL.
func (c *Client) newRequest(method string, urlStr string, body interface{}) (*http.Request, error) {
u, err := url.Parse(urlStr)
if err != nil {
return nil, err
}
var resolvedURL string
if c.BaseURL != nil {
u.Path, _ = url.JoinPath(c.BaseURL.Path, u.Path)
resolvedURL = c.BaseURL.ResolveReference(u).String()
} else {
resolvedURL = u.String()
}
var buf io.ReadWriter
if body != nil {
buf = &bytes.Buffer{}
enc := json.NewEncoder(buf)
err = enc.Encode(body)
if err != nil {
return nil, err
}
}
req, err := http.NewRequest(method, resolvedURL, buf)
if err != nil {
return nil, err
}
if body != nil {
req.Header.Set("Content-Type", contentTypeJSON)
}
req.Header.Set("Accept", contentTypeJSON)
if c.UserAgent != "" {
req.Header.Set("User-Agent", c.UserAgent)
}
return req, nil
}
// do send an API request and returns the API response. The API response is JSON decoded and stored in the value pointed to by v.
func (c *Client) do(ctx context.Context, req *http.Request, v interface{}) (*http.Response, error) {
resp, err := c.client.Do(req.WithContext(ctx))
if err != nil {
return nil, err
}
defer func(Body io.ReadCloser) {
_ = Body.Close()
}(resp.Body)
decErr := json.NewDecoder(resp.Body).Decode(v)
if decErr == io.EOF {
decErr = nil
}
return resp, decErr
}