Skip to content

Commit 780becf

Browse files
committed
add service template
1 parent 7c0ead6 commit 780becf

File tree

2 files changed

+275
-0
lines changed

2 files changed

+275
-0
lines changed

schema/templates/service.tmpl

Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
const (
2+
Version = "{{.Version}}"
3+
DefaultAPIURL = "{{.URL}}"
4+
DefaultUserAgent = "{{.Name}}/" + Version + " (" + runtime.GOOS + "; " + runtime.GOARCH + ")"
5+
)
6+
7+
// Client is the interface that wraps HTTP request creation
8+
// and execution.
9+
type Client interface {
10+
Do(req *http.Request, v interface{}) error
11+
NewRequest(method, path string, body interface{}) (*http.Request, error)
12+
}
13+
14+
// Service represents your API.
15+
type Service struct {
16+
Client Client
17+
}
18+
19+
// Create a Service using the given, if none is provided
20+
// it uses DefaultClient.
21+
func NewService(client Client) *Service {
22+
if client == nil {
23+
client = &DefaultClient{}
24+
}
25+
return &Service{
26+
Client: client,
27+
}
28+
}
29+
30+
// Generates an HTTP request, but does not perform the request.
31+
func (s *Service) NewRequest(method, path string, body interface{}) (*http.Request, error) {
32+
return s.Client.NewRequest(method, path, body)
33+
}
34+
35+
// Sends a request and decodes the response into v.
36+
func (s *Service) Do(v interface{}, method, path string, body interface{}) error {
37+
req, err := s.Client.NewRequest(method, path, body)
38+
if err != nil {
39+
return err
40+
}
41+
return s.Client.Do(req, v)
42+
}
43+
44+
func (s *Service) Get(v interface{}, path string) error {
45+
return s.Do(v, "GET", path, nil)
46+
}
47+
48+
func (s *Service) Patch(v interface{}, path string, body interface{}) error {
49+
return s.Do(v, "PATCH", path, body)
50+
}
51+
52+
func (s *Service) Post(v interface{}, path string, body interface{}) error {
53+
return s.Do(v, "POST", path, body)
54+
}
55+
56+
func (s *Service) Put(v interface{}, path string, body interface{}) error {
57+
return s.Do(v, "PUT", path, body)
58+
}
59+
60+
func (s *Service) Delete(path string) error {
61+
return s.Do(nil, "DELETE", path, nil)
62+
}
63+
64+
// DefaultClient implements Client interface.
65+
// DefaultClient has an internal HTTP client (HTTP) which defaults to http.DefaultClient.
66+
type DefaultClient struct {
67+
// HTTP is the Client's internal http.Client, handling HTTP requests.
68+
HTTP *http.Client
69+
70+
// The URL of the API to communicate with.
71+
URL string
72+
73+
// Username is the HTTP basic auth username for API calls made by this Client.
74+
Username string
75+
76+
// Password is the HTTP basic auth password for API calls made by this Client.
77+
Password string
78+
79+
// UserAgent to be provided in API requests. Set to DefaultUserAgent if not
80+
// specified.
81+
UserAgent string
82+
83+
// Debug mode can be used to dump the full request and response to stdout.
84+
Debug bool
85+
86+
// AdditionalHeaders are extra headers to add to each HTTP request sent by
87+
// this Client.
88+
AdditionalHeaders http.Header
89+
}
90+
91+
// Generates an HTTP request, but does not perform the request.
92+
// The request's Accept header field will be set to:
93+
//
94+
// Accept: application/json
95+
//
96+
// The Request-Id header will be set to a random UUID. The User-Agent header
97+
// will be set to the Client's UserAgent, or DefaultUserAgent if UserAgent is
98+
// not set.
99+
//
100+
// The type of body determines how to encode the request:
101+
//
102+
// nil no body
103+
// io.Reader body is sent verbatim
104+
// else body is encoded as application/json
105+
//
106+
func (c *DefaultClient) NewRequest(method, path string, body interface{}) (*http.Request, error) {
107+
var ctype string
108+
var rbody io.Reader
109+
110+
switch t := body.(type) {
111+
case nil:
112+
case string:
113+
rbody = bytes.NewBufferString(t)
114+
case io.Reader:
115+
rbody = t
116+
default:
117+
v := reflect.ValueOf(body)
118+
if !v.IsValid() {
119+
break
120+
}
121+
if v.Type().Kind() == reflect.Ptr {
122+
v = reflect.Indirect(v)
123+
if !v.IsValid() {
124+
break
125+
}
126+
}
127+
128+
j, err := json.Marshal(body)
129+
if err != nil {
130+
log.Fatal(err)
131+
}
132+
rbody = bytes.NewReader(j)
133+
ctype = "application/json"
134+
}
135+
apiURL := strings.TrimRight(c.URL, "/")
136+
if apiURL == "" {
137+
apiURL = DefaultAPIURL
138+
}
139+
req, err := http.NewRequest(method, apiURL+path, rbody)
140+
if err != nil {
141+
return nil, err
142+
}
143+
req.Header.Set("Accept", "application/json")
144+
req.Header.Set("Request-Id", uuid.New())
145+
useragent := c.UserAgent
146+
if useragent == "" {
147+
useragent = DefaultUserAgent
148+
}
149+
req.Header.Set("User-Agent", useragent)
150+
if ctype != "" {
151+
req.Header.Set("Content-Type", ctype)
152+
}
153+
req.SetBasicAuth(c.Username, c.Password)
154+
for k, v := range c.AdditionalHeaders {
155+
req.Header[k] = v
156+
}
157+
return req, nil
158+
}
159+
160+
// Submits an HTTP request, checks its response, and deserializes
161+
// the response into v. The type of v determines how to handle
162+
// the response body:
163+
//
164+
// nil body is discarded
165+
// io.Writer body is copied directly into v
166+
// else body is decoded into v as json
167+
//
168+
func (c *DefaultClient) Do(req *http.Request, v interface{}) error {
169+
if c.Debug {
170+
dump, err := httputil.DumpRequestOut(req, true)
171+
if err != nil {
172+
log.Println(err)
173+
} else {
174+
log.Println(string(dump))
175+
}
176+
}
177+
178+
httpClient := c.HTTP
179+
if httpClient == nil {
180+
httpClient = http.DefaultClient
181+
}
182+
183+
res, err := httpClient.Do(req)
184+
if err != nil {
185+
return err
186+
}
187+
defer res.Body.Close()
188+
if c.Debug {
189+
dump, err := httputil.DumpResponse(res, true)
190+
if err != nil {
191+
log.Println(err)
192+
} else {
193+
log.Println(string(dump))
194+
}
195+
}
196+
if err = checkResponse(res); err != nil {
197+
return err
198+
}
199+
switch t := v.(type) {
200+
case nil:
201+
case io.Writer:
202+
_, err = io.Copy(t, res.Body)
203+
default:
204+
err = json.NewDecoder(res.Body).Decode(v)
205+
}
206+
return err
207+
}
208+
209+
func checkResponse(res *http.Response) error {
210+
if res.StatusCode/100 != 2 { // 200, 201, 202, etc
211+
// FIXME: Try to handle error json in a generic way.
212+
return fmt.Errorf("Encountered an error : %s", res.Status)
213+
}
214+
return nil
215+
}
216+
217+
type ListRange struct {
218+
Field string
219+
Max int
220+
Descending bool
221+
FirstId string
222+
LastId string
223+
}
224+
225+
func (lr *ListRange) SetHeader(req *http.Request) {
226+
var hdrval string
227+
if lr.Field != "" {
228+
hdrval += lr.Field + " "
229+
}
230+
hdrval += lr.FirstId + ".." + lr.LastId
231+
if lr.Max != 0 {
232+
hdrval += fmt.Sprintf("; max=%d", lr.Max)
233+
if lr.Descending {
234+
hdrval += ", "
235+
}
236+
}
237+
238+
if lr.Descending {
239+
hdrval += ", order=desc"
240+
}
241+
242+
req.Header.Set("Range", hdrval)
243+
return
244+
}

schema/templates/templates.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,15 +58,20 @@ var templates = map[string]string{"astruct.tmpl": `{{$Root := .Root}} struct {
5858
DefaultUserAgent = "{{.Name}}/" + Version + " (" + runtime.GOOS + "; " + runtime.GOARCH + ")"
5959
)
6060
61+
// Client is the interface that wraps HTTP request creation
62+
// and execution.
6163
type Client interface {
6264
Do(req *http.Request, v interface{}) error
6365
NewRequest(method, path string, body interface{}) (*http.Request, error)
6466
}
6567
68+
// Service represents your API.
6669
type Service struct {
6770
Client Client
6871
}
6972
73+
// Create a Service using the given, if none is provided
74+
// it uses DefaultClient.
7075
func NewService(client Client) *Service {
7176
if client == nil {
7277
client = &DefaultClient{}
@@ -76,10 +81,12 @@ func NewService(client Client) *Service {
7681
}
7782
}
7883
84+
// Generates an HTTP request, but does not perform the request.
7985
func (s *Service) NewRequest(method, path string, body interface{}) (*http.Request, error) {
8086
return s.Client.NewRequest(method, path, body)
8187
}
8288
89+
// Sends a request and decodes the response into v.
8390
func (s *Service) Do(v interface{}, method, path string, body interface{}) error {
8491
req, err := s.Client.NewRequest(method, path, body)
8592
if err != nil {
@@ -135,6 +142,21 @@ type DefaultClient struct {
135142
AdditionalHeaders http.Header
136143
}
137144
145+
// Generates an HTTP request, but does not perform the request.
146+
// The request's Accept header field will be set to:
147+
//
148+
// Accept: application/json
149+
//
150+
// The Request-Id header will be set to a random UUID. The User-Agent header
151+
// will be set to the Client's UserAgent, or DefaultUserAgent if UserAgent is
152+
// not set.
153+
//
154+
// The type of body determines how to encode the request:
155+
//
156+
// nil no body
157+
// io.Reader body is sent verbatim
158+
// else body is encoded as application/json
159+
//
138160
func (c *DefaultClient) NewRequest(method, path string, body interface{}) (*http.Request, error) {
139161
var ctype string
140162
var rbody io.Reader
@@ -189,6 +211,14 @@ func (c *DefaultClient) NewRequest(method, path string, body interface{}) (*http
189211
return req, nil
190212
}
191213
214+
// Submits an HTTP request, checks its response, and deserializes
215+
// the response into v. The type of v determines how to handle
216+
// the response body:
217+
//
218+
// nil body is discarded
219+
// io.Writer body is copied directly into v
220+
// else body is decoded into v as json
221+
//
192222
func (c *DefaultClient) Do(req *http.Request, v interface{}) error {
193223
if c.Debug {
194224
dump, err := httputil.DumpRequestOut(req, true)
@@ -289,3 +319,4 @@ func Parse(t *template.Template) (*template.Template, error) {
289319
}
290320
return t, nil
291321
}
322+

0 commit comments

Comments
 (0)