Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 package monorail | |
| 6 | |
| 7 import ( | |
| 8 "bytes" | |
| 9 "encoding/json" | |
| 10 "fmt" | |
| 11 "io" | |
| 12 "io/ioutil" | |
| 13 "net/http" | |
| 14 | |
| 15 "golang.org/x/net/context" | |
| 16 "google.golang.org/grpc" | |
| 17 | |
| 18 "github.com/luci/luci-go/common/errors" | |
| 19 "github.com/luci/luci-go/common/logging" | |
| 20 | |
| 21 "infra/httputil" | |
| 22 ) | |
| 23 | |
| 24 // epClient implements MonorailClient by sending requests to Monorail's | |
| 25 // Cloud Endpoints API. | |
| 26 // See https://monorail-staging.appspot.com/_ah/api/explorer#p/monorail/v1/ | |
| 27 type epClient struct { | |
| 28 HTTP *http.Client | |
| 29 Host string | |
| 30 } | |
| 31 | |
| 32 // NewEndpointsClient creates a MonorailClient that send requests to Monorail's | |
| 33 // Cloud Endpoints. | |
| 34 func NewEndpointsClient(client *http.Client, host string) MonorailClient { | |
| 35 return &epClient{HTTP: client, Host: host} | |
| 36 } | |
| 37 | |
| 38 func (c *epClient) call(ctx context.Context, urlSuffix string, request, response interface{}) error { | |
| 39 var buf bytes.Buffer | |
| 40 if err := json.NewEncoder(&buf).Encode(request); err != nil { | |
| 41 return err | |
| 42 } | |
| 43 reqBytes := buf.Bytes() | |
| 44 | |
| 45 u := fmt.Sprintf("https://%s/_ah/api/monorail/v1/%s", c.Host, urlSuffix) | |
| 46 req, err := http.NewRequest("POST", u, nil) | |
| 47 if err != nil { | |
| 48 return err | |
| 49 } | |
| 50 req.Header.Set("Content-Type", "application/json") | |
| 51 req.Header.Set("Accept", "application/json") | |
| 52 | |
| 53 logging.Debugf(ctx, "POST %s %s", u, reqBytes) | |
| 54 | |
| 55 return httputil.RetryRequest(ctx, c.HTTP, req, reqBytes, func(res *http. Response) error { | |
| 56 if res.StatusCode != http.StatusOK { | |
| 57 text, _ := ioutil.ReadAll(io.LimitReader(res.Body, 1024) ) | |
| 58 err := fmt.Errorf("unexpected status %q. Response: %s", res.Status, text) | |
| 59 if res.StatusCode == http.StatusNotFound { | |
| 60 // Occasionally Cloud Endpoints returns 404. | |
|
seanmccullough1
2016/06/03 18:45:33
Even when it really should be a 500 :)
nodir
2016/06/03 18:56:12
yes!
| |
| 61 err = errors.WrapTransient(err) | |
| 62 } | |
| 63 return err | |
| 64 } | |
| 65 if response == nil { | |
| 66 return nil | |
| 67 } | |
| 68 return json.NewDecoder(res.Body).Decode(response) | |
| 69 }) | |
| 70 } | |
| 71 | |
| 72 func (c *epClient) InsertIssue(ctx context.Context, req *InsertIssueRequest, opt ions ...grpc.CallOption) (*InsertIssueResponse, error) { | |
|
seanmccullough1
2016/06/03 18:45:33
Godoc here and below. Mention that even though the
nodir
2016/06/03 18:56:12
these functions are internal, so nobody will see t
| |
| 73 if err := checkOptions(options); err != nil { | |
| 74 return nil, err | |
| 75 } | |
| 76 if err := req.Validate(); err != nil { | |
| 77 return nil, err | |
| 78 } | |
| 79 url := fmt.Sprintf("projects/%s/issues?sendEmail=%v", req.ProjectId, req .SendEmail) | |
| 80 res := &InsertIssueResponse{&Issue{}} | |
| 81 return res, c.call(ctx, url, &req.Issue, res.Issue) | |
| 82 } | |
| 83 | |
| 84 func (c *epClient) InsertComment(ctx context.Context, req *InsertCommentRequest, options ...grpc.CallOption) (*InsertCommentResponse, error) { | |
| 85 if err := checkOptions(options); err != nil { | |
| 86 return nil, err | |
| 87 } | |
| 88 url := fmt.Sprintf("projects/%s/issues/%d/comments", req.Issue.ProjectId , req.Issue.IssueId) | |
| 89 return &InsertCommentResponse{}, c.call(ctx, url, req.Comment, nil) | |
| 90 } | |
| 91 | |
| 92 func checkOptions(options []grpc.CallOption) error { | |
| 93 if len(options) > 0 { | |
| 94 return errGrpcOptions | |
| 95 } | |
| 96 return nil | |
| 97 } | |
| OLD | NEW |