Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(348)

Side by Side Diff: go/src/infra/monorail/endpoints.go

Issue 2037143002: Monorail client in Go (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git@master
Patch Set: more tests Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « go/src/infra/monorail/doc.go ('k') | go/src/infra/monorail/endpoints_test.go » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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 "strings"
15
16 "golang.org/x/net/context"
17 "golang.org/x/net/context/ctxhttp"
18 "google.golang.org/grpc"
19
20 "github.com/luci/luci-go/common/clock"
21 "github.com/luci/luci-go/common/errors"
22 "github.com/luci/luci-go/common/logging"
23 )
24
25 // epClient implements MonorailClient by sending requests to Monorail's
26 // Cloud Endpoints API.
27 // See https://monorail-staging.appspot.com/_ah/api/explorer#p/monorail/v1/
28 type epClient struct {
29 HTTP *http.Client
30 // url is root of API without trailing slash,
31 // e.g. "https://monorail-staging.appspot.com/_ah/api/monorail/v1"
32 url string
33 }
34
35 // NewEndpointsClient creates a MonorailClient that send requests to
36 // Monorail Cloud Endpoints API at url.
37 //
38 // Example of url: "https://monorail-staging.appspot.com/_ah/api/monorail/v1"
39 //
40 // Methods do not implement retries.
41 // Use "github.com/luci/luci-go/common/errors".IsTransient to check
42 // if an error is transient.
43 //
44 // Client methods return an error on any grpc.CallOption.
45 func NewEndpointsClient(client *http.Client, url string) MonorailClient {
46 return &epClient{HTTP: client, url: strings.TrimSuffix(url, "/")}
47 }
48
49 func (c *epClient) call(ctx context.Context, urlSuffix string, request, response interface{}) error {
50 client := c.HTTP
51 if client == nil {
52 client = http.DefaultClient
53 }
54
55 // Limit ctx deadline to timeout set in client.
56 if client.Timeout > 0 {
57 clientDeadline := clock.Now(ctx).Add(client.Timeout)
58 if deadline, ok := ctx.Deadline(); !ok || deadline.After(clientD eadline) {
59 ctx, _ = context.WithDeadline(ctx, clientDeadline)
60 }
61 }
62
63 // Convert request object to JSON.
64 reqBuf := &bytes.Buffer{}
65 if request != nil {
66 if err := json.NewEncoder(reqBuf).Encode(request); err != nil {
67 return err
68 }
69 }
70
71 // Make an HTTP request.
72 req, err := http.NewRequest("POST", c.url + urlSuffix, reqBuf)
73 if err != nil {
74 return fmt.Errorf("could not make a request to %s: %s", req.URL, err)
75 }
76 req.Header.Set("Content-Type", "application/json")
77 req.Header.Set("Accept", "application/json")
78
79 // Send the request.
80 logging.Debugf(ctx, "POST %s %s", req.URL, reqBuf.Bytes())
81 res, err := ctxhttp.Do(ctx, client, req)
82 if err != nil {
83 return errors.WrapTransient(err)
84 }
85 defer res.Body.Close()
86
87 // Check HTTP status code.
88 if res.StatusCode != http.StatusOK {
89 text, _ := ioutil.ReadAll(io.LimitReader(res.Body, 1024))
90 err := fmt.Errorf("unexpected status %q. Response: %s", res.Stat us, text)
91 if res.StatusCode == http.StatusNotFound || res.StatusCode > 500 {
92 // Cloud Endpoints often flake with HTTP 404.
93 // Treat such responses transient errors.
94 err = errors.WrapTransient(err)
95 }
96 return err
97 }
98
99 if response == nil {
100 return nil
101 }
102 return json.NewDecoder(res.Body).Decode(response)
103 }
104
105 func (c *epClient) InsertIssue(ctx context.Context, req *InsertIssueRequest, opt ions ...grpc.CallOption) (*InsertIssueResponse, error) {
106 if err := checkOptions(options); err != nil {
107 return nil, err
108 }
109 if err := req.Validate(); err != nil {
110 return nil, err
111 }
112 url := fmt.Sprintf("/projects/%s/issues?sendEmail=%v", req.ProjectId, re q.SendEmail)
113 res := &InsertIssueResponse{&Issue{}}
114 return res, c.call(ctx, url, &req.Issue, res.Issue)
115 }
116
117 func (c *epClient) InsertComment(ctx context.Context, req *InsertCommentRequest, options ...grpc.CallOption) (*InsertCommentResponse, error) {
118 if err := checkOptions(options); err != nil {
119 return nil, err
120 }
121 url := fmt.Sprintf("/projects/%s/issues/%d/comments", req.Issue.ProjectI d, req.Issue.IssueId)
122 return &InsertCommentResponse{}, c.call(ctx, url, req.Comment, nil)
123 }
124
125 func checkOptions(options []grpc.CallOption) error {
126 if len(options) > 0 {
127 return errGrpcOptions
128 }
129 return nil
130 }
OLDNEW
« no previous file with comments | « go/src/infra/monorail/doc.go ('k') | go/src/infra/monorail/endpoints_test.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698