hashtag - Initial commit of hashtag code.

Bug: skia:9559
Change-Id: I1ee3ebd7ac620d6ccdd2640e3c6733c63ea3daed
Reviewed-on: https://skia-review.googlesource.com/c/buildbot/+/247300
Commit-Queue: Joe Gregorio <jcgregorio@google.com>
Reviewed-by: Ben Wagner aka dogben <benjaminwagner@google.com>
diff --git a/go.mod b/go.mod
index 6008b70..476da6b 100644
--- a/go.mod
+++ b/go.mod
@@ -70,6 +70,7 @@
 	github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 // indirect
 	github.com/spf13/cobra v0.0.5
 	github.com/spf13/pflag v1.0.5 // indirect
+	github.com/spf13/viper v1.3.2
 	github.com/stretchr/objx v0.2.0 // indirect
 	github.com/stretchr/testify v1.4.0
 	github.com/syndtr/goleveldb v1.0.0
diff --git a/go.sum b/go.sum
index 748e152..7009f18 100644
--- a/go.sum
+++ b/go.sum
@@ -202,6 +202,7 @@
 github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk=
 github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
+github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
 github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
 github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
 github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
@@ -250,6 +251,7 @@
 github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
 github.com/luci/gtreap v0.0.0-20161228054646-35df89791e8f h1:Kkxfmkf53vnIADWIhzvJ0GvwVR/gz9U7F7Wqofqd7dU=
 github.com/luci/gtreap v0.0.0-20161228054646-35df89791e8f/go.mod h1:OjKOY0UvVOOH5nWXSIWTbQWESn8dDiGlaEZx6IAsWhU=
+github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
 github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
 github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
 github.com/mailru/easyjson v0.0.0-20180730094502-03f2033d19d5/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
@@ -261,6 +263,7 @@
 github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
 github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
 github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
+github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
 github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
 github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY=
 github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
@@ -295,6 +298,7 @@
 github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
 github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
 github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
+github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
 github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
 github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
 github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
@@ -347,10 +351,13 @@
 github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 h1:WN9BUFbdyOsSH/XohnWpXOlq9NBD5sGAB2FciQMUEe8=
 github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
 github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
+github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc=
 github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
+github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
 github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
 github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s=
 github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
+github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
 github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
 github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 github.com/spf13/pflag v1.0.1 h1:aCvUg6QPl3ibpQUxyLkrEkCHtPqYJL4x9AuhqVqFis4=
@@ -359,6 +366,7 @@
 github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
 github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+github.com/spf13/viper v1.3.2 h1:VUFqw5KcqRf7i70GOzW7N+Q7+gxVBkSSqiXB12+JQ4M=
 github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
diff --git a/go/androidbuildinternal/v2beta1/rebuild.sh b/go/androidbuildinternal/v2beta1/rebuild.sh
index 63e560b..97b0f23 100755
--- a/go/androidbuildinternal/v2beta1/rebuild.sh
+++ b/go/androidbuildinternal/v2beta1/rebuild.sh
@@ -2,8 +2,8 @@
 
 # Script to rebuild the androidbuildinternal.go file.
 
-# Get an updated generator.
-go get -u google.golang.org/api/google-api-go-generator
+# Get an updated generator. Pin to v0.10.0 because https://github.com/googleapis/google-api-go-client/issues/416.
+go get -u google.golang.org/api/google-api-go-generator@v0.10.0
 
 # Retrieve the discovery document for the API.
 wget https://www.googleapis.com/discovery/v1/apis/androidbuildinternal/v2beta1/rest
diff --git a/go/codesearch/codesearch.go b/go/codesearch/codesearch.go
new file mode 100644
index 0000000..9d8dd2e
--- /dev/null
+++ b/go/codesearch/codesearch.go
@@ -0,0 +1,168 @@
+// Package codesearch wraps up the codesearch JSON API.
+//
+// Notes about the codesearch REST API:
+//
+// The query needs to be bracketed by search_request=[b|e] query parameters.
+//
+// For example:
+//    https://cs.chromium.org/codesearch/json/search_request:1
+//       ?search_request=b
+//       &query=file%3A.md+file%3A%5Esrc%2Fthird_party%2Fskia%2F+package%3A%5Echromium%24
+//       &max_num_results=20
+//       &results_offset=0
+//       &search_request=e
+//
+// The URL for a file.name of "src/third_party/skia/site/roles.md" is
+//
+//    https://cs.chromium.org/chromium/src/third_party/skia/site/roles.md
+//
+// To jump to a specific line:
+//
+//    https://cs.chromium.org/chromium/src/third_party/skia/site/roles.md?l=2
+//
+package codesearch
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"net/http"
+	"net/url"
+
+	"go.skia.org/infra/go/skerr"
+)
+
+const (
+	// origin is the site hosting codesearch.
+	origin = "https://cs.chromium.org"
+
+	// The location of the JSON codesearch API.
+	jsonQueryPath = "/codesearch/json/search_request"
+
+	// SkiaAllCode is the text to add to a search to restrict it to the Skia
+	// codebase.
+	SkiaAllCode = "file:^src/third_party/skia/ package:^chromium$"
+
+	// SkiaInfraBaseQuery is the text to add to a search to restrict it to the
+	// Skia Infra codebase.
+	SkiaInfraBaseQuery = "file:^skia/buildbot/ package:^chromium$"
+
+	// SkiaAllMarkdown is the text to add to a search to restrict it to Markdown
+	// files in either the buildbot or skia proper repo.
+	SkiaAllMarkdown = "lang:^markdown$ AND (file:^skia/buildbot/ OR file:^src/third_party/skia/) AND package:^chromium$ "
+)
+
+var (
+	defaultQueryParams = url.Values{
+		"search_request": []string{""},
+	}
+)
+
+// CodeSearch searches code.
+type CodeSearch struct {
+	c      *http.Client
+	origin string
+}
+
+// New creates a new CodeSearch instance.
+func New(client *http.Client) *CodeSearch {
+	return &CodeSearch{
+		c:      client,
+		origin: origin,
+	}
+}
+
+// Origin allows over-riding the default origin.
+func (cs *CodeSearch) Origin(origin string) {
+	cs.origin = origin
+}
+
+// File is a file in a search result.
+type File struct {
+	Name        string `json:"name"`
+	PackageName string `json:"package_name"`
+}
+
+// TopFile is the best matching file in a search result.
+type TopFile struct {
+	File File `json:"file"`
+	Size int  `json:"size"`
+}
+
+// Text is the text of a snippet.
+type Text struct {
+	Text string `json:"text"`
+}
+
+// Snippet is a matching snippet of code in a search result.
+type Snippet struct {
+	Text            Text `json:"text"`
+	FirstLineNumber int  `json:"first_line_number"`
+}
+
+// SearchResult is a single result from a search.
+type SearchResult struct {
+	TopFile                TopFile   `json:"top_file"`
+	NumDuplicates          int       `json:"num_duplicates"`
+	NumMatches             int       `json:"num_matches"`
+	Language               string    `json:"language"`
+	BestMatchingLineNumber int       `json:"best_matching_line_number"`
+	Snippet                []Snippet `json:"snippet"`
+}
+
+// SearchResponse is the response from CodeSearch.Query.
+type SearchResponse struct {
+	Status                        int            `json:"status"`
+	StatusMessage                 string         `json:"status_message"`
+	EstimatedTotalNumberOfResults int            `json:"estimated_total_number_of_results"`
+	ResultsOffset                 int            `json:"results_offset"`
+	NextPageToken                 string         `json:"next_page_token"`
+	SearchResult                  []SearchResult `json:"search_result"`
+}
+
+// CompoundSearchResponse represents multiple search responses.
+type CompoundSearchResponse struct {
+	Response []SearchResponse `json:"search_response"`
+}
+
+func (cs *CodeSearch) urlForQuery(q string, params url.Values) string {
+	if params == nil {
+		params = url.Values{}
+	}
+	params["query"] = []string{q}
+	encodedQuery := params.Encode()
+	return fmt.Sprintf("%s%s?search_request=b&%s&search_request=e", cs.origin, jsonQueryPath, encodedQuery)
+}
+
+// Query runs a search against the given search service.
+//
+// The query string should conform to any query you would use on
+// https://cs.chromium.org/.
+func (cs CodeSearch) Query(ctx context.Context, q string, params url.Values) (SearchResponse, error) {
+	req, err := http.NewRequest("GET", cs.urlForQuery(q, params), nil)
+	if err != nil {
+		return SearchResponse{}, err
+	}
+	req = req.WithContext(ctx)
+	resp, err := cs.c.Do(req)
+	if err != nil {
+		return SearchResponse{}, err
+	}
+	defer resp.Body.Close()
+	if resp.StatusCode != http.StatusOK {
+		return SearchResponse{}, skerr.Fmt("Bad status code: %d", resp.StatusCode)
+	}
+	var csr CompoundSearchResponse
+	if err := json.NewDecoder(resp.Body).Decode(&csr); err != nil {
+		return SearchResponse{}, err
+	}
+	if len(csr.Response) < 1 {
+		return SearchResponse{}, skerr.Fmt("No results body found")
+	}
+	return csr.Response[0], nil
+}
+
+// URL returns the link to display the TopFile in the SearchResult.
+func (cs CodeSearch) URL(r SearchResult) string {
+	return fmt.Sprintf("%s/%s/%s", cs.origin, r.TopFile.File.PackageName, r.TopFile.File.Name)
+}
diff --git a/go/codesearch/codesearch_test.go b/go/codesearch/codesearch_test.go
new file mode 100644
index 0000000..c48414e
--- /dev/null
+++ b/go/codesearch/codesearch_test.go
@@ -0,0 +1,103 @@
+package codesearch
+
+import (
+	"encoding/json"
+	"net/url"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"go.skia.org/infra/go/testutils/unittest"
+)
+
+const searchResponseBody = `{
+  "search_response": [
+	  {
+		"status": 0,
+		"estimated_total_number_of_results": 174,
+		"maybe_skipped_documents": false,
+		"results_offset": 0,
+		"hit_max_results": false,
+		"hit_max_to_score": false,
+		"status_message": "",
+		"percent_shards_skipped": 0,
+		"called_local_augmentation": false,
+		"next_page_token": "CJv...wE=",
+		"search_result": [
+		  {
+			"top_file": {
+			  "file": {
+				"name": "src/third_party/skia/site/roles.md",
+				"package_name": "chromium"
+			  }
+			},
+			"num_duplicates": 0,
+			"num_matches": 0,
+			"language": "markdown",
+			"docid": "svr-40oKURY",
+			"has_unshown_matches": false,
+			"is_augmented": false,
+			"match_reason": {},
+			"full_history_search": false
+		  },
+		  {
+			"top_file": {
+			  "file": {
+				"name": "src/third_party/skia/site/index.md",
+				"package_name": "chromium"
+			  }
+			},
+			"num_duplicates": 0,
+			"num_matches": 0,
+			"language": "markdown",
+			"docid": "vAHsr5oQ12k",
+			"has_unshown_matches": false,
+			"is_augmented": false,
+			"match_reason": {},
+			"full_history_search": false
+		  }
+		]
+     }
+  ]
+}`
+
+func TestParse(t *testing.T) {
+	unittest.SmallTest(t)
+
+	var resp CompoundSearchResponse
+	err := json.Unmarshal([]byte(searchResponseBody), &resp)
+	assert.NoError(t, err)
+	assert.Equal(t, 174, resp.Response[0].EstimatedTotalNumberOfResults)
+	assert.Equal(t, "src/third_party/skia/site/roles.md", resp.Response[0].SearchResult[0].TopFile.File.Name)
+	assert.Equal(t, "CJv...wE=", resp.Response[0].NextPageToken)
+}
+
+func TestCodeSearch_urlForQuery(t *testing.T) {
+	unittest.SmallTest(t)
+
+	tests := []struct {
+		name   string
+		q      string
+		params url.Values
+		want   string
+	}{
+		{
+			name:   "nil params",
+			q:      "file:.md",
+			params: nil,
+			want:   "https://cs.chromium.org/codesearch/json/search_request?search_request=b&query=file%3A.md&search_request=e",
+		},
+		{
+			name:   "extra params",
+			q:      "file:.md",
+			params: url.Values{"foo": []string{"bar"}},
+			want:   "https://cs.chromium.org/codesearch/json/search_request?search_request=b&foo=bar&query=file%3A.md&search_request=e",
+		},
+	}
+	cs := New(nil)
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			got := cs.urlForQuery(tt.q, tt.params)
+			assert.Equal(t, tt.want, got, tt.name)
+		})
+	}
+}
diff --git a/go/issues/issues.go b/go/issues/issues.go
index b52a7dc..1cf6d6b 100644
--- a/go/issues/issues.go
+++ b/go/issues/issues.go
@@ -130,7 +130,7 @@
 func get(client *http.Client, u string) ([]Issue, error) {
 	resp, err := client.Get(u)
 	if err != nil || resp == nil || resp.StatusCode != 200 {
-		return nil, fmt.Errorf("Failed to retrieve issue tracker response: %s", err)
+		return nil, fmt.Errorf("Failed to retrieve issue tracker response: %s Status Code: %d", err, resp.StatusCode)
 	}
 	defer util.Close(resp.Body)
 
diff --git a/go/monorail/v1/monorail.go b/go/monorail/v1/monorail.go
new file mode 100644
index 0000000..b53d089
--- /dev/null
+++ b/go/monorail/v1/monorail.go
@@ -0,0 +1,4691 @@
+// Copyright 2019 Google LLC.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Code generated file. DO NOT EDIT.
+
+// Package monorail provides access to the .
+//
+// For product documentation, see: https://chromium.googlesource.com/infra/infra/+/master/appengine/monorail/doc/api.md
+//
+// Creating a client
+//
+// Usage example:
+//
+//   import "go.skia.org/infra/go/monorail/v1"
+//   ...
+//   ctx := context.Background()
+//   monorailService, err := monorail.NewService(ctx)
+//
+// In this example, Google Application Default Credentials are used for authentication.
+//
+// For information on how to create and obtain Application Default Credentials, see https://developers.google.com/identity/protocols/application-default-credentials.
+//
+// Other authentication options
+//
+// To use an API key for authentication (note: some APIs do not support API keys), use option.WithAPIKey:
+//
+//   monorailService, err := monorail.NewService(ctx, option.WithAPIKey("AIza..."))
+//
+// To use an OAuth token (e.g., a user token obtained via a three-legged OAuth flow), use option.WithTokenSource:
+//
+//   config := &oauth2.Config{...}
+//   // ...
+//   token, err := config.Exchange(ctx, ...)
+//   monorailService, err := monorail.NewService(ctx, option.WithTokenSource(config.TokenSource(ctx, token)))
+//
+// See https://godoc.org/google.golang.org/api/option/ for details on options.
+package monorail // import "go.skia.org/infra/go/monorail/v1"
+
+import (
+	"bytes"
+	"context"
+	"encoding/json"
+	"errors"
+	"fmt"
+	"io"
+	"net/http"
+	"net/url"
+	"strconv"
+	"strings"
+
+	gensupport "google.golang.org/api/gensupport"
+	googleapi "google.golang.org/api/googleapi"
+	option "google.golang.org/api/option"
+	htransport "google.golang.org/api/transport/http"
+)
+
+// Always reference these packages, just in case the auto-generated code
+// below doesn't.
+var _ = bytes.NewBuffer
+var _ = strconv.Itoa
+var _ = fmt.Sprintf
+var _ = json.NewDecoder
+var _ = io.Copy
+var _ = url.Parse
+var _ = gensupport.MarshalJSON
+var _ = googleapi.Version
+var _ = errors.New
+var _ = strings.Replace
+var _ = context.Canceled
+
+const apiId = "monorail:v1"
+const apiName = "monorail"
+const apiVersion = "v1"
+const basePath = "https://monorail-prod.appspot.com/_ah/api/monorail/v1/"
+
+// OAuth2 scopes used by this API.
+const (
+	// https://www.googleapis.com/auth/userinfo.email
+	UserinfoEmailScope = "https://www.googleapis.com/auth/userinfo.email"
+)
+
+// NewService creates a new Service.
+func NewService(ctx context.Context, opts ...option.ClientOption) (*Service, error) {
+	scopesOption := option.WithScopes(
+		"https://www.googleapis.com/auth/userinfo.email",
+	)
+	// NOTE: prepend, so we don't override user-specified scopes.
+	opts = append([]option.ClientOption{scopesOption}, opts...)
+	client, endpoint, err := htransport.NewClient(ctx, opts...)
+	if err != nil {
+		return nil, err
+	}
+	s, err := New(client)
+	if err != nil {
+		return nil, err
+	}
+	if endpoint != "" {
+		s.BasePath = endpoint
+	}
+	return s, nil
+}
+
+// New creates a new Service. It uses the provided http.Client for requests.
+//
+// Deprecated: please use NewService instead.
+// To provide a custom HTTP client, use option.WithHTTPClient.
+// If you are using google.golang.org/api/googleapis/transport.APIKey, use option.WithAPIKey with NewService instead.
+func New(client *http.Client) (*Service, error) {
+	if client == nil {
+		return nil, errors.New("client is nil")
+	}
+	s := &Service{client: client, BasePath: basePath}
+	s.Approvals = NewApprovalsService(s)
+	s.Components = NewComponentsService(s)
+	s.Groups = NewGroupsService(s)
+	s.Issues = NewIssuesService(s)
+	s.Users = NewUsersService(s)
+	return s, nil
+}
+
+type Service struct {
+	client    *http.Client
+	BasePath  string // API endpoint base URL
+	UserAgent string // optional additional User-Agent fragment
+
+	Approvals *ApprovalsService
+
+	Components *ComponentsService
+
+	Groups *GroupsService
+
+	Issues *IssuesService
+
+	Users *UsersService
+}
+
+func (s *Service) userAgent() string {
+	if s.UserAgent == "" {
+		return googleapi.UserAgent
+	}
+	return googleapi.UserAgent + " " + s.UserAgent
+}
+
+func NewApprovalsService(s *Service) *ApprovalsService {
+	rs := &ApprovalsService{s: s}
+	rs.Comments = NewApprovalsCommentsService(s)
+	return rs
+}
+
+type ApprovalsService struct {
+	s *Service
+
+	Comments *ApprovalsCommentsService
+}
+
+func NewApprovalsCommentsService(s *Service) *ApprovalsCommentsService {
+	rs := &ApprovalsCommentsService{s: s}
+	return rs
+}
+
+type ApprovalsCommentsService struct {
+	s *Service
+}
+
+func NewComponentsService(s *Service) *ComponentsService {
+	rs := &ComponentsService{s: s}
+	return rs
+}
+
+type ComponentsService struct {
+	s *Service
+}
+
+func NewGroupsService(s *Service) *GroupsService {
+	rs := &GroupsService{s: s}
+	rs.Settings = NewGroupsSettingsService(s)
+	return rs
+}
+
+type GroupsService struct {
+	s *Service
+
+	Settings *GroupsSettingsService
+}
+
+func NewGroupsSettingsService(s *Service) *GroupsSettingsService {
+	rs := &GroupsSettingsService{s: s}
+	return rs
+}
+
+type GroupsSettingsService struct {
+	s *Service
+}
+
+func NewIssuesService(s *Service) *IssuesService {
+	rs := &IssuesService{s: s}
+	rs.Comments = NewIssuesCommentsService(s)
+	return rs
+}
+
+type IssuesService struct {
+	s *Service
+
+	Comments *IssuesCommentsService
+}
+
+func NewIssuesCommentsService(s *Service) *IssuesCommentsService {
+	rs := &IssuesCommentsService{s: s}
+	return rs
+}
+
+type IssuesCommentsService struct {
+	s *Service
+}
+
+func NewUsersService(s *Service) *UsersService {
+	rs := &UsersService{s: s}
+	return rs
+}
+
+type UsersService struct {
+	s *Service
+}
+
+// ProtoApiPb2V1Approval: Approval Value details
+type ProtoApiPb2V1Approval struct {
+	ApprovalName string `json:"approvalName,omitempty"`
+
+	// Approvers: Atomic person.
+	Approvers []*ProtoApiPb2V1AtomPerson `json:"approvers,omitempty"`
+
+	PhaseName string `json:"phaseName,omitempty"`
+
+	SetOn string `json:"setOn,omitempty"`
+
+	// Setter: Atomic person.
+	Setter *ProtoApiPb2V1AtomPerson `json:"setter,omitempty"`
+
+	// Possible values:
+	//   "approved"
+	//   "nA"
+	//   "needInfo"
+	//   "needsReview"
+	//   "notApproved"
+	//   "notSet"
+	//   "reviewRequested"
+	//   "reviewStarted"
+	Status string `json:"status,omitempty"`
+
+	// ForceSendFields is a list of field names (e.g. "ApprovalName") to
+	// unconditionally include in API requests. By default, fields with
+	// empty values are omitted from API requests. However, any non-pointer,
+	// non-interface field appearing in ForceSendFields will be sent to the
+	// server regardless of whether the field is empty or not. This may be
+	// used to include empty fields in Patch requests.
+	ForceSendFields []string `json:"-"`
+
+	// NullFields is a list of field names (e.g. "ApprovalName") to include
+	// in API requests with the JSON null value. By default, fields with
+	// empty values are omitted from API requests. However, any field with
+	// an empty value appearing in NullFields will be sent to the server as
+	// null. It is an error if a field in this list has a non-empty value.
+	// This may be used to include null fields in Patch requests.
+	NullFields []string `json:"-"`
+}
+
+func (s *ProtoApiPb2V1Approval) MarshalJSON() ([]byte, error) {
+	type NoMethod ProtoApiPb2V1Approval
+	raw := NoMethod(*s)
+	return gensupport.MarshalJSON(raw, s.ForceSendFields, s.NullFields)
+}
+
+// ProtoApiPb2V1ApprovalCommentWrapper: Approval comment details.
+type ProtoApiPb2V1ApprovalCommentWrapper struct {
+	// ApprovalUpdates: Approval update.
+	ApprovalUpdates *ProtoApiPb2V1ApprovalUpdate `json:"approvalUpdates,omitempty"`
+
+	// Attachments: Issue attachment.
+	Attachments []*ProtoApiPb2V1Attachment `json:"attachments,omitempty"`
+
+	// Author: Atomic person.
+	Author *ProtoApiPb2V1AtomPerson `json:"author,omitempty"`
+
+	CanDelete bool `json:"canDelete,omitempty"`
+
+	Content string `json:"content,omitempty"`
+
+	// DeletedBy: Atomic person.
+	DeletedBy *ProtoApiPb2V1AtomPerson `json:"deletedBy,omitempty"`
+
+	Id int64 `json:"id,omitempty"`
+
+	IsDescription bool `json:"is_description,omitempty"`
+
+	Kind string `json:"kind,omitempty"`
+
+	Published string `json:"published,omitempty"`
+
+	// ForceSendFields is a list of field names (e.g. "ApprovalUpdates") to
+	// unconditionally include in API requests. By default, fields with
+	// empty values are omitted from API requests. However, any non-pointer,
+	// non-interface field appearing in ForceSendFields will be sent to the
+	// server regardless of whether the field is empty or not. This may be
+	// used to include empty fields in Patch requests.
+	ForceSendFields []string `json:"-"`
+
+	// NullFields is a list of field names (e.g. "ApprovalUpdates") to
+	// include in API requests with the JSON null value. By default, fields
+	// with empty values are omitted from API requests. However, any field
+	// with an empty value appearing in NullFields will be sent to the
+	// server as null. It is an error if a field in this list has a
+	// non-empty value. This may be used to include null fields in Patch
+	// requests.
+	NullFields []string `json:"-"`
+}
+
+func (s *ProtoApiPb2V1ApprovalCommentWrapper) MarshalJSON() ([]byte, error) {
+	type NoMethod ProtoApiPb2V1ApprovalCommentWrapper
+	raw := NoMethod(*s)
+	return gensupport.MarshalJSON(raw, s.ForceSendFields, s.NullFields)
+}
+
+// ProtoApiPb2V1ApprovalUpdate: Approval update.
+type ProtoApiPb2V1ApprovalUpdate struct {
+	Approvers []string `json:"approvers,omitempty"`
+
+	// FieldValues: Custom field values.
+	FieldValues []*ProtoApiPb2V1FieldValue `json:"fieldValues,omitempty"`
+
+	Kind string `json:"kind,omitempty"`
+
+	Status string `json:"status,omitempty"`
+
+	// ForceSendFields is a list of field names (e.g. "Approvers") to
+	// unconditionally include in API requests. By default, fields with
+	// empty values are omitted from API requests. However, any non-pointer,
+	// non-interface field appearing in ForceSendFields will be sent to the
+	// server regardless of whether the field is empty or not. This may be
+	// used to include empty fields in Patch requests.
+	ForceSendFields []string `json:"-"`
+
+	// NullFields is a list of field names (e.g. "Approvers") to include in
+	// API requests with the JSON null value. By default, fields with empty
+	// values are omitted from API requests. However, any field with an
+	// empty value appearing in NullFields will be sent to the server as
+	// null. It is an error if a field in this list has a non-empty value.
+	// This may be used to include null fields in Patch requests.
+	NullFields []string `json:"-"`
+}
+
+func (s *ProtoApiPb2V1ApprovalUpdate) MarshalJSON() ([]byte, error) {
+	type NoMethod ProtoApiPb2V1ApprovalUpdate
+	raw := NoMethod(*s)
+	return gensupport.MarshalJSON(raw, s.ForceSendFields, s.NullFields)
+}
+
+// ProtoApiPb2V1ApprovalsCommentsInsertResponse: Response message of
+// request to insert an isuse's comments.
+type ProtoApiPb2V1ApprovalsCommentsInsertResponse struct {
+	ApprovalName string `json:"approvalName,omitempty"`
+
+	// ApprovalUpdates: Approval update.
+	ApprovalUpdates *ProtoApiPb2V1ApprovalUpdate `json:"approvalUpdates,omitempty"`
+
+	// Author: Atomic person.
+	Author *ProtoApiPb2V1AtomPerson `json:"author,omitempty"`
+
+	CanDelete bool `json:"canDelete,omitempty"`
+
+	Content string `json:"content,omitempty"`
+
+	// Error: Request error.
+	Error *ProtoApiPb2V1ErrorMessage `json:"error,omitempty"`
+
+	Id int64 `json:"id,omitempty"`
+
+	Kind string `json:"kind,omitempty"`
+
+	Published string `json:"published,omitempty"`
+
+	// ServerResponse contains the HTTP response code and headers from the
+	// server.
+	googleapi.ServerResponse `json:"-"`
+
+	// ForceSendFields is a list of field names (e.g. "ApprovalName") to
+	// unconditionally include in API requests. By default, fields with
+	// empty values are omitted from API requests. However, any non-pointer,
+	// non-interface field appearing in ForceSendFields will be sent to the
+	// server regardless of whether the field is empty or not. This may be
+	// used to include empty fields in Patch requests.
+	ForceSendFields []string `json:"-"`
+
+	// NullFields is a list of field names (e.g. "ApprovalName") to include
+	// in API requests with the JSON null value. By default, fields with
+	// empty values are omitted from API requests. However, any field with
+	// an empty value appearing in NullFields will be sent to the server as
+	// null. It is an error if a field in this list has a non-empty value.
+	// This may be used to include null fields in Patch requests.
+	NullFields []string `json:"-"`
+}
+
+func (s *ProtoApiPb2V1ApprovalsCommentsInsertResponse) MarshalJSON() ([]byte, error) {
+	type NoMethod ProtoApiPb2V1ApprovalsCommentsInsertResponse
+	raw := NoMethod(*s)
+	return gensupport.MarshalJSON(raw, s.ForceSendFields, s.NullFields)
+}
+
+// ProtoApiPb2V1ApprovalsCommentsListResponse: Response message of
+// request to list an approval's comments.
+type ProtoApiPb2V1ApprovalsCommentsListResponse struct {
+	// Error: Request error.
+	Error *ProtoApiPb2V1ErrorMessage `json:"error,omitempty"`
+
+	// Items: Approval comment details.
+	Items []*ProtoApiPb2V1ApprovalCommentWrapper `json:"items,omitempty"`
+
+	Kind string `json:"kind,omitempty"`
+
+	TotalResults int64 `json:"totalResults,omitempty"`
+
+	// ServerResponse contains the HTTP response code and headers from the
+	// server.
+	googleapi.ServerResponse `json:"-"`
+
+	// ForceSendFields is a list of field names (e.g. "Error") to
+	// unconditionally include in API requests. By default, fields with
+	// empty values are omitted from API requests. However, any non-pointer,
+	// non-interface field appearing in ForceSendFields will be sent to the
+	// server regardless of whether the field is empty or not. This may be
+	// used to include empty fields in Patch requests.
+	ForceSendFields []string `json:"-"`
+
+	// NullFields is a list of field names (e.g. "Error") to include in API
+	// requests with the JSON null value. By default, fields with empty
+	// values are omitted from API requests. However, any field with an
+	// empty value appearing in NullFields will be sent to the server as
+	// null. It is an error if a field in this list has a non-empty value.
+	// This may be used to include null fields in Patch requests.
+	NullFields []string `json:"-"`
+}
+
+func (s *ProtoApiPb2V1ApprovalsCommentsListResponse) MarshalJSON() ([]byte, error) {
+	type NoMethod ProtoApiPb2V1ApprovalsCommentsListResponse
+	raw := NoMethod(*s)
+	return gensupport.MarshalJSON(raw, s.ForceSendFields, s.NullFields)
+}
+
+// ProtoApiPb2V1AtomPerson: Atomic person.
+type ProtoApiPb2V1AtomPerson struct {
+	EmailBouncing bool `json:"email_bouncing,omitempty"`
+
+	HtmlLink string `json:"htmlLink,omitempty"`
+
+	Kind string `json:"kind,omitempty"`
+
+	LastVisitDaysAgo int64 `json:"last_visit_days_ago,omitempty,string"`
+
+	Name string `json:"name,omitempty"`
+
+	VacationMessage string `json:"vacation_message,omitempty"`
+
+	// ForceSendFields is a list of field names (e.g. "EmailBouncing") to
+	// unconditionally include in API requests. By default, fields with
+	// empty values are omitted from API requests. However, any non-pointer,
+	// non-interface field appearing in ForceSendFields will be sent to the
+	// server regardless of whether the field is empty or not. This may be
+	// used to include empty fields in Patch requests.
+	ForceSendFields []string `json:"-"`
+
+	// NullFields is a list of field names (e.g. "EmailBouncing") to include
+	// in API requests with the JSON null value. By default, fields with
+	// empty values are omitted from API requests. However, any field with
+	// an empty value appearing in NullFields will be sent to the server as
+	// null. It is an error if a field in this list has a non-empty value.
+	// This may be used to include null fields in Patch requests.
+	NullFields []string `json:"-"`
+}
+
+func (s *ProtoApiPb2V1AtomPerson) MarshalJSON() ([]byte, error) {
+	type NoMethod ProtoApiPb2V1AtomPerson
+	raw := NoMethod(*s)
+	return gensupport.MarshalJSON(raw, s.ForceSendFields, s.NullFields)
+}
+
+// ProtoApiPb2V1Attachment: Issue attachment.
+type ProtoApiPb2V1Attachment struct {
+	AttachmentId int64 `json:"attachmentId,omitempty,string"`
+
+	FileName string `json:"fileName,omitempty"`
+
+	FileSize int64 `json:"fileSize,omitempty"`
+
+	IsDeleted bool `json:"isDeleted,omitempty"`
+
+	Mimetype string `json:"mimetype,omitempty"`
+
+	// ForceSendFields is a list of field names (e.g. "AttachmentId") to
+	// unconditionally include in API requests. By default, fields with
+	// empty values are omitted from API requests. However, any non-pointer,
+	// non-interface field appearing in ForceSendFields will be sent to the
+	// server regardless of whether the field is empty or not. This may be
+	// used to include empty fields in Patch requests.
+	ForceSendFields []string `json:"-"`
+
+	// NullFields is a list of field names (e.g. "AttachmentId") to include
+	// in API requests with the JSON null value. By default, fields with
+	// empty values are omitted from API requests. However, any field with
+	// an empty value appearing in NullFields will be sent to the server as
+	// null. It is an error if a field in this list has a non-empty value.
+	// This may be used to include null fields in Patch requests.
+	NullFields []string `json:"-"`
+}
+
+func (s *ProtoApiPb2V1Attachment) MarshalJSON() ([]byte, error) {
+	type NoMethod ProtoApiPb2V1Attachment
+	raw := NoMethod(*s)
+	return gensupport.MarshalJSON(raw, s.ForceSendFields, s.NullFields)
+}
+
+// ProtoApiPb2V1Component: Component PB.
+type ProtoApiPb2V1Component struct {
+	Admin []string `json:"admin,omitempty"`
+
+	Cc []string `json:"cc,omitempty"`
+
+	ComponentId int64 `json:"componentId,omitempty"`
+
+	ComponentPath string `json:"componentPath,omitempty"`
+
+	Created string `json:"created,omitempty"`
+
+	Creator string `json:"creator,omitempty"`
+
+	Deprecated bool `json:"deprecated,omitempty"`
+
+	Description string `json:"description,omitempty"`
+
+	Modified string `json:"modified,omitempty"`
+
+	Modifier string `json:"modifier,omitempty"`
+
+	ProjectName string `json:"projectName,omitempty"`
+
+	// ServerResponse contains the HTTP response code and headers from the
+	// server.
+	googleapi.ServerResponse `json:"-"`
+
+	// ForceSendFields is a list of field names (e.g. "Admin") to
+	// unconditionally include in API requests. By default, fields with
+	// empty values are omitted from API requests. However, any non-pointer,
+	// non-interface field appearing in ForceSendFields will be sent to the
+	// server regardless of whether the field is empty or not. This may be
+	// used to include empty fields in Patch requests.
+	ForceSendFields []string `json:"-"`
+
+	// NullFields is a list of field names (e.g. "Admin") to include in API
+	// requests with the JSON null value. By default, fields with empty
+	// values are omitted from API requests. However, any field with an
+	// empty value appearing in NullFields will be sent to the server as
+	// null. It is an error if a field in this list has a non-empty value.
+	// This may be used to include null fields in Patch requests.
+	NullFields []string `json:"-"`
+}
+
+func (s *ProtoApiPb2V1Component) MarshalJSON() ([]byte, error) {
+	type NoMethod ProtoApiPb2V1Component
+	raw := NoMethod(*s)
+	return gensupport.MarshalJSON(raw, s.ForceSendFields, s.NullFields)
+}
+
+// ProtoApiPb2V1ComponentCreateRequestBody: Request body to create a
+// component.
+type ProtoApiPb2V1ComponentCreateRequestBody struct {
+	Admin []string `json:"admin,omitempty"`
+
+	Cc []string `json:"cc,omitempty"`
+
+	Deprecated bool `json:"deprecated,omitempty"`
+
+	Description string `json:"description,omitempty"`
+
+	ParentPath string `json:"parentPath,omitempty"`
+
+	// ForceSendFields is a list of field names (e.g. "Admin") to
+	// unconditionally include in API requests. By default, fields with
+	// empty values are omitted from API requests. However, any non-pointer,
+	// non-interface field appearing in ForceSendFields will be sent to the
+	// server regardless of whether the field is empty or not. This may be
+	// used to include empty fields in Patch requests.
+	ForceSendFields []string `json:"-"`
+
+	// NullFields is a list of field names (e.g. "Admin") to include in API
+	// requests with the JSON null value. By default, fields with empty
+	// values are omitted from API requests. However, any field with an
+	// empty value appearing in NullFields will be sent to the server as
+	// null. It is an error if a field in this list has a non-empty value.
+	// This may be used to include null fields in Patch requests.
+	NullFields []string `json:"-"`
+}
+
+func (s *ProtoApiPb2V1ComponentCreateRequestBody) MarshalJSON() ([]byte, error) {
+	type NoMethod ProtoApiPb2V1ComponentCreateRequestBody
+	raw := NoMethod(*s)
+	return gensupport.MarshalJSON(raw, s.ForceSendFields, s.NullFields)
+}
+
+// ProtoApiPb2V1ComponentUpdate: Component update.
+type ProtoApiPb2V1ComponentUpdate struct {
+	Admin []string `json:"admin,omitempty"`
+
+	Cc []string `json:"cc,omitempty"`
+
+	Deprecated bool `json:"deprecated,omitempty"`
+
+	Description string `json:"description,omitempty"`
+
+	// Possible values:
+	//   "ADMIN"
+	//   "CC"
+	//   "DEPRECATED"
+	//   "DESCRIPTION"
+	//   "LEAF_NAME"
+	Field string `json:"field,omitempty"`
+
+	LeafName string `json:"leafName,omitempty"`
+
+	// ForceSendFields is a list of field names (e.g. "Admin") to
+	// unconditionally include in API requests. By default, fields with
+	// empty values are omitted from API requests. However, any non-pointer,
+	// non-interface field appearing in ForceSendFields will be sent to the
+	// server regardless of whether the field is empty or not. This may be
+	// used to include empty fields in Patch requests.
+	ForceSendFields []string `json:"-"`
+
+	// NullFields is a list of field names (e.g. "Admin") to include in API
+	// requests with the JSON null value. By default, fields with empty
+	// values are omitted from API requests. However, any field with an
+	// empty value appearing in NullFields will be sent to the server as
+	// null. It is an error if a field in this list has a non-empty value.
+	// This may be used to include null fields in Patch requests.
+	NullFields []string `json:"-"`
+}
+
+func (s *ProtoApiPb2V1ComponentUpdate) MarshalJSON() ([]byte, error) {
+	type NoMethod ProtoApiPb2V1ComponentUpdate
+	raw := NoMethod(*s)
+	return gensupport.MarshalJSON(raw, s.ForceSendFields, s.NullFields)
+}
+
+// ProtoApiPb2V1ComponentUpdateRequestBody: Request body to update a
+// component.
+type ProtoApiPb2V1ComponentUpdateRequestBody struct {
+	// Updates: Component update.
+	Updates []*ProtoApiPb2V1ComponentUpdate `json:"updates,omitempty"`
+
+	// ForceSendFields is a list of field names (e.g. "Updates") to
+	// unconditionally include in API requests. By default, fields with
+	// empty values are omitted from API requests. However, any non-pointer,
+	// non-interface field appearing in ForceSendFields will be sent to the
+	// server regardless of whether the field is empty or not. This may be
+	// used to include empty fields in Patch requests.
+	ForceSendFields []string `json:"-"`
+
+	// NullFields is a list of field names (e.g. "Updates") to include in
+	// API requests with the JSON null value. By default, fields with empty
+	// values are omitted from API requests. However, any field with an
+	// empty value appearing in NullFields will be sent to the server as
+	// null. It is an error if a field in this list has a non-empty value.
+	// This may be used to include null fields in Patch requests.
+	NullFields []string `json:"-"`
+}
+
+func (s *ProtoApiPb2V1ComponentUpdateRequestBody) MarshalJSON() ([]byte, error) {
+	type NoMethod ProtoApiPb2V1ComponentUpdateRequestBody
+	raw := NoMethod(*s)
+	return gensupport.MarshalJSON(raw, s.ForceSendFields, s.NullFields)
+}
+
+// ProtoApiPb2V1ComponentsListResponse: Response to list components.
+type ProtoApiPb2V1ComponentsListResponse struct {
+	// Components: Component PB.
+	Components []*ProtoApiPb2V1Component `json:"components,omitempty"`
+
+	// ServerResponse contains the HTTP response code and headers from the
+	// server.
+	googleapi.ServerResponse `json:"-"`
+
+	// ForceSendFields is a list of field names (e.g. "Components") to
+	// unconditionally include in API requests. By default, fields with
+	// empty values are omitted from API requests. However, any non-pointer,
+	// non-interface field appearing in ForceSendFields will be sent to the
+	// server regardless of whether the field is empty or not. This may be
+	// used to include empty fields in Patch requests.
+	ForceSendFields []string `json:"-"`
+
+	// NullFields is a list of field names (e.g. "Components") to include in
+	// API requests with the JSON null value. By default, fields with empty
+	// values are omitted from API requests. However, any field with an
+	// empty value appearing in NullFields will be sent to the server as
+	// null. It is an error if a field in this list has a non-empty value.
+	// This may be used to include null fields in Patch requests.
+	NullFields []string `json:"-"`
+}
+
+func (s *ProtoApiPb2V1ComponentsListResponse) MarshalJSON() ([]byte, error) {
+	type NoMethod ProtoApiPb2V1ComponentsListResponse
+	raw := NoMethod(*s)
+	return gensupport.MarshalJSON(raw, s.ForceSendFields, s.NullFields)
+}
+
+// ProtoApiPb2V1ErrorMessage: Request error.
+type ProtoApiPb2V1ErrorMessage struct {
+	Code int64 `json:"code,omitempty"`
+
+	Message string `json:"message,omitempty"`
+
+	Reason string `json:"reason,omitempty"`
+
+	// ForceSendFields is a list of field names (e.g. "Code") to
+	// unconditionally include in API requests. By default, fields with
+	// empty values are omitted from API requests. However, any non-pointer,
+	// non-interface field appearing in ForceSendFields will be sent to the
+	// server regardless of whether the field is empty or not. This may be
+	// used to include empty fields in Patch requests.
+	ForceSendFields []string `json:"-"`
+
+	// NullFields is a list of field names (e.g. "Code") to include in API
+	// requests with the JSON null value. By default, fields with empty
+	// values are omitted from API requests. However, any field with an
+	// empty value appearing in NullFields will be sent to the server as
+	// null. It is an error if a field in this list has a non-empty value.
+	// This may be used to include null fields in Patch requests.
+	NullFields []string `json:"-"`
+}
+
+func (s *ProtoApiPb2V1ErrorMessage) MarshalJSON() ([]byte, error) {
+	type NoMethod ProtoApiPb2V1ErrorMessage
+	raw := NoMethod(*s)
+	return gensupport.MarshalJSON(raw, s.ForceSendFields, s.NullFields)
+}
+
+// ProtoApiPb2V1FieldValue: Custom field values.
+type ProtoApiPb2V1FieldValue struct {
+	ApprovalName string `json:"approvalName,omitempty"`
+
+	Derived bool `json:"derived,omitempty"`
+
+	FieldName string `json:"fieldName,omitempty"`
+
+	FieldValue string `json:"fieldValue,omitempty"`
+
+	// Possible values:
+	//   "add" (default)
+	//   "clear"
+	//   "remove"
+	Operator string `json:"operator,omitempty"`
+
+	PhaseName string `json:"phaseName,omitempty"`
+
+	// ForceSendFields is a list of field names (e.g. "ApprovalName") to
+	// unconditionally include in API requests. By default, fields with
+	// empty values are omitted from API requests. However, any non-pointer,
+	// non-interface field appearing in ForceSendFields will be sent to the
+	// server regardless of whether the field is empty or not. This may be
+	// used to include empty fields in Patch requests.
+	ForceSendFields []string `json:"-"`
+
+	// NullFields is a list of field names (e.g. "ApprovalName") to include
+	// in API requests with the JSON null value. By default, fields with
+	// empty values are omitted from API requests. However, any field with
+	// an empty value appearing in NullFields will be sent to the server as
+	// null. It is an error if a field in this list has a non-empty value.
+	// This may be used to include null fields in Patch requests.
+	NullFields []string `json:"-"`
+}
+
+func (s *ProtoApiPb2V1FieldValue) MarshalJSON() ([]byte, error) {
+	type NoMethod ProtoApiPb2V1FieldValue
+	raw := NoMethod(*s)
+	return gensupport.MarshalJSON(raw, s.ForceSendFields, s.NullFields)
+}
+
+// ProtoApiPb2V1GroupCitizens: Group members and owners.
+type ProtoApiPb2V1GroupCitizens struct {
+	GroupMembers []string `json:"groupMembers,omitempty"`
+
+	GroupOwners []string `json:"groupOwners,omitempty"`
+
+	// ForceSendFields is a list of field names (e.g. "GroupMembers") to
+	// unconditionally include in API requests. By default, fields with
+	// empty values are omitted from API requests. However, any non-pointer,
+	// non-interface field appearing in ForceSendFields will be sent to the
+	// server regardless of whether the field is empty or not. This may be
+	// used to include empty fields in Patch requests.
+	ForceSendFields []string `json:"-"`
+
+	// NullFields is a list of field names (e.g. "GroupMembers") to include
+	// in API requests with the JSON null value. By default, fields with
+	// empty values are omitted from API requests. However, any field with
+	// an empty value appearing in NullFields will be sent to the server as
+	// null. It is an error if a field in this list has a non-empty value.
+	// This may be used to include null fields in Patch requests.
+	NullFields []string `json:"-"`
+}
+
+func (s *ProtoApiPb2V1GroupCitizens) MarshalJSON() ([]byte, error) {
+	type NoMethod ProtoApiPb2V1GroupCitizens
+	raw := NoMethod(*s)
+	return gensupport.MarshalJSON(raw, s.ForceSendFields, s.NullFields)
+}
+
+// ProtoApiPb2V1GroupsCreateResponse: Response message of request to
+// create a group.
+type ProtoApiPb2V1GroupsCreateResponse struct {
+	// Error: Request error.
+	Error *ProtoApiPb2V1ErrorMessage `json:"error,omitempty"`
+
+	GroupID int64 `json:"groupID,omitempty"`
+
+	// ServerResponse contains the HTTP response code and headers from the
+	// server.
+	googleapi.ServerResponse `json:"-"`
+
+	// ForceSendFields is a list of field names (e.g. "Error") to
+	// unconditionally include in API requests. By default, fields with
+	// empty values are omitted from API requests. However, any non-pointer,
+	// non-interface field appearing in ForceSendFields will be sent to the
+	// server regardless of whether the field is empty or not. This may be
+	// used to include empty fields in Patch requests.
+	ForceSendFields []string `json:"-"`
+
+	// NullFields is a list of field names (e.g. "Error") to include in API
+	// requests with the JSON null value. By default, fields with empty
+	// values are omitted from API requests. However, any field with an
+	// empty value appearing in NullFields will be sent to the server as
+	// null. It is an error if a field in this list has a non-empty value.
+	// This may be used to include null fields in Patch requests.
+	NullFields []string `json:"-"`
+}
+
+func (s *ProtoApiPb2V1GroupsCreateResponse) MarshalJSON() ([]byte, error) {
+	type NoMethod ProtoApiPb2V1GroupsCreateResponse
+	raw := NoMethod(*s)
+	return gensupport.MarshalJSON(raw, s.ForceSendFields, s.NullFields)
+}
+
+// ProtoApiPb2V1GroupsGetResponse: Response message of request to create
+// a group.
+type ProtoApiPb2V1GroupsGetResponse struct {
+	// Error: Request error.
+	Error *ProtoApiPb2V1ErrorMessage `json:"error,omitempty"`
+
+	GroupID int64 `json:"groupID,omitempty"`
+
+	GroupMembers []string `json:"groupMembers,omitempty"`
+
+	GroupOwners []string `json:"groupOwners,omitempty"`
+
+	// GroupSettings: User group settings.
+	GroupSettings *ProtoApiPb2V1UserGroupSettingsWrapper `json:"groupSettings,omitempty"`
+
+	// ServerResponse contains the HTTP response code and headers from the
+	// server.
+	googleapi.ServerResponse `json:"-"`
+
+	// ForceSendFields is a list of field names (e.g. "Error") to
+	// unconditionally include in API requests. By default, fields with
+	// empty values are omitted from API requests. However, any non-pointer,
+	// non-interface field appearing in ForceSendFields will be sent to the
+	// server regardless of whether the field is empty or not. This may be
+	// used to include empty fields in Patch requests.
+	ForceSendFields []string `json:"-"`
+
+	// NullFields is a list of field names (e.g. "Error") to include in API
+	// requests with the JSON null value. By default, fields with empty
+	// values are omitted from API requests. However, any field with an
+	// empty value appearing in NullFields will be sent to the server as
+	// null. It is an error if a field in this list has a non-empty value.
+	// This may be used to include null fields in Patch requests.
+	NullFields []string `json:"-"`
+}
+
+func (s *ProtoApiPb2V1GroupsGetResponse) MarshalJSON() ([]byte, error) {
+	type NoMethod ProtoApiPb2V1GroupsGetResponse
+	raw := NoMethod(*s)
+	return gensupport.MarshalJSON(raw, s.ForceSendFields, s.NullFields)
+}
+
+// ProtoApiPb2V1GroupsSettingsListResponse: Response message of request
+// to list group settings.
+type ProtoApiPb2V1GroupsSettingsListResponse struct {
+	// Error: Request error.
+	Error *ProtoApiPb2V1ErrorMessage `json:"error,omitempty"`
+
+	// GroupSettings: User group settings.
+	GroupSettings []*ProtoApiPb2V1UserGroupSettingsWrapper `json:"groupSettings,omitempty"`
+
+	// ServerResponse contains the HTTP response code and headers from the
+	// server.
+	googleapi.ServerResponse `json:"-"`
+
+	// ForceSendFields is a list of field names (e.g. "Error") to
+	// unconditionally include in API requests. By default, fields with
+	// empty values are omitted from API requests. However, any non-pointer,
+	// non-interface field appearing in ForceSendFields will be sent to the
+	// server regardless of whether the field is empty or not. This may be
+	// used to include empty fields in Patch requests.
+	ForceSendFields []string `json:"-"`
+
+	// NullFields is a list of field names (e.g. "Error") to include in API
+	// requests with the JSON null value. By default, fields with empty
+	// values are omitted from API requests. However, any field with an
+	// empty value appearing in NullFields will be sent to the server as
+	// null. It is an error if a field in this list has a non-empty value.
+	// This may be used to include null fields in Patch requests.
+	NullFields []string `json:"-"`
+}
+
+func (s *ProtoApiPb2V1GroupsSettingsListResponse) MarshalJSON() ([]byte, error) {
+	type NoMethod ProtoApiPb2V1GroupsSettingsListResponse
+	raw := NoMethod(*s)
+	return gensupport.MarshalJSON(raw, s.ForceSendFields, s.NullFields)
+}
+
+// ProtoApiPb2V1GroupsUpdateResponse: Response message of request to
+// update a group.
+type ProtoApiPb2V1GroupsUpdateResponse struct {
+	// Error: Request error.
+	Error *ProtoApiPb2V1ErrorMessage `json:"error,omitempty"`
+
+	// ServerResponse contains the HTTP response code and headers from the
+	// server.
+	googleapi.ServerResponse `json:"-"`
+
+	// ForceSendFields is a list of field names (e.g. "Error") to
+	// unconditionally include in API requests. By default, fields with
+	// empty values are omitted from API requests. However, any non-pointer,
+	// non-interface field appearing in ForceSendFields will be sent to the
+	// server regardless of whether the field is empty or not. This may be
+	// used to include empty fields in Patch requests.
+	ForceSendFields []string `json:"-"`
+
+	// NullFields is a list of field names (e.g. "Error") to include in API
+	// requests with the JSON null value. By default, fields with empty
+	// values are omitted from API requests. However, any field with an
+	// empty value appearing in NullFields will be sent to the server as
+	// null. It is an error if a field in this list has a non-empty value.
+	// This may be used to include null fields in Patch requests.
+	NullFields []string `json:"-"`
+}
+
+func (s *ProtoApiPb2V1GroupsUpdateResponse) MarshalJSON() ([]byte, error) {
+	type NoMethod ProtoApiPb2V1GroupsUpdateResponse
+	raw := NoMethod(*s)
+	return gensupport.MarshalJSON(raw, s.ForceSendFields, s.NullFields)
+}
+
+// ProtoApiPb2V1IssueCommentWrapper: Issue comment details.
+type ProtoApiPb2V1IssueCommentWrapper struct {
+	// Attachments: Issue attachment.
+	Attachments []*ProtoApiPb2V1Attachment `json:"attachments,omitempty"`
+
+	// Author: Atomic person.
+	Author *ProtoApiPb2V1AtomPerson `json:"author,omitempty"`
+
+	CanDelete bool `json:"canDelete,omitempty"`
+
+	Content string `json:"content,omitempty"`
+
+	// DeletedBy: Atomic person.
+	DeletedBy *ProtoApiPb2V1AtomPerson `json:"deletedBy,omitempty"`
+
+	Id int64 `json:"id,omitempty"`
+
+	IsDescription bool `json:"is_description,omitempty"`
+
+	Kind string `json:"kind,omitempty"`
+
+	Published string `json:"published,omitempty"`
+
+	// Updates: Issue update.
+	Updates *ProtoApiPb2V1Update `json:"updates,omitempty"`
+
+	// ForceSendFields is a list of field names (e.g. "Attachments") to
+	// unconditionally include in API requests. By default, fields with
+	// empty values are omitted from API requests. However, any non-pointer,
+	// non-interface field appearing in ForceSendFields will be sent to the
+	// server regardless of whether the field is empty or not. This may be
+	// used to include empty fields in Patch requests.
+	ForceSendFields []string `json:"-"`
+
+	// NullFields is a list of field names (e.g. "Attachments") to include
+	// in API requests with the JSON null value. By default, fields with
+	// empty values are omitted from API requests. However, any field with
+	// an empty value appearing in NullFields will be sent to the server as
+	// null. It is an error if a field in this list has a non-empty value.
+	// This may be used to include null fields in Patch requests.
+	NullFields []string `json:"-"`
+}
+
+func (s *ProtoApiPb2V1IssueCommentWrapper) MarshalJSON() ([]byte, error) {
+	type NoMethod ProtoApiPb2V1IssueCommentWrapper
+	raw := NoMethod(*s)
+	return gensupport.MarshalJSON(raw, s.ForceSendFields, s.NullFields)
+}
+
+// ProtoApiPb2V1IssueRef: Issue reference.
+type ProtoApiPb2V1IssueRef struct {
+	IssueId int64 `json:"issueId,omitempty"`
+
+	Kind string `json:"kind,omitempty"`
+
+	ProjectId string `json:"projectId,omitempty"`
+
+	// ForceSendFields is a list of field names (e.g. "IssueId") to
+	// unconditionally include in API requests. By default, fields with
+	// empty values are omitted from API requests. However, any non-pointer,
+	// non-interface field appearing in ForceSendFields will be sent to the
+	// server regardless of whether the field is empty or not. This may be
+	// used to include empty fields in Patch requests.
+	ForceSendFields []string `json:"-"`
+
+	// NullFields is a list of field names (e.g. "IssueId") to include in
+	// API requests with the JSON null value. By default, fields with empty
+	// values are omitted from API requests. However, any field with an
+	// empty value appearing in NullFields will be sent to the server as
+	// null. It is an error if a field in this list has a non-empty value.
+	// This may be used to include null fields in Patch requests.
+	NullFields []string `json:"-"`
+}
+
+func (s *ProtoApiPb2V1IssueRef) MarshalJSON() ([]byte, error) {
+	type NoMethod ProtoApiPb2V1IssueRef
+	raw := NoMethod(*s)
+	return gensupport.MarshalJSON(raw, s.ForceSendFields, s.NullFields)
+}
+
+// ProtoApiPb2V1IssueWrapper: Issue details.
+type ProtoApiPb2V1IssueWrapper struct {
+	// ApprovalValues: Approval Value details
+	ApprovalValues []*ProtoApiPb2V1Approval `json:"approvalValues,omitempty"`
+
+	// Author: Atomic person.
+	Author *ProtoApiPb2V1AtomPerson `json:"author,omitempty"`
+
+	// BlockedOn: Issue reference.
+	BlockedOn []*ProtoApiPb2V1IssueRef `json:"blockedOn,omitempty"`
+
+	// Blocking: Issue reference.
+	Blocking []*ProtoApiPb2V1IssueRef `json:"blocking,omitempty"`
+
+	CanComment bool `json:"canComment,omitempty"`
+
+	CanEdit bool `json:"canEdit,omitempty"`
+
+	// Cc: Atomic person.
+	Cc []*ProtoApiPb2V1AtomPerson `json:"cc,omitempty"`
+
+	Closed string `json:"closed,omitempty"`
+
+	ComponentModified string `json:"component_modified,omitempty"`
+
+	Components []string `json:"components,omitempty"`
+
+	Description string `json:"description,omitempty"`
+
+	// FieldValues: Custom field values.
+	FieldValues []*ProtoApiPb2V1FieldValue `json:"fieldValues,omitempty"`
+
+	Id int64 `json:"id,omitempty"`
+
+	Kind string `json:"kind,omitempty"`
+
+	Labels []string `json:"labels,omitempty"`
+
+	// MergedInto: Issue reference.
+	MergedInto *ProtoApiPb2V1IssueRef `json:"mergedInto,omitempty"`
+
+	// Owner: Atomic person.
+	Owner *ProtoApiPb2V1AtomPerson `json:"owner,omitempty"`
+
+	OwnerModified string `json:"owner_modified,omitempty"`
+
+	// Phases: Issue phase details.
+	Phases []*ProtoApiPb2V1Phase `json:"phases,omitempty"`
+
+	ProjectId string `json:"projectId,omitempty"`
+
+	Published string `json:"published,omitempty"`
+
+	Starred bool `json:"starred,omitempty"`
+
+	Stars int64 `json:"stars,omitempty"`
+
+	// Possible values:
+	//   "closed"
+	//   "open"
+	State string `json:"state,omitempty"`
+
+	Status string `json:"status,omitempty"`
+
+	StatusModified string `json:"status_modified,omitempty"`
+
+	Summary string `json:"summary,omitempty"`
+
+	Title string `json:"title,omitempty"`
+
+	Updated string `json:"updated,omitempty"`
+
+	// ForceSendFields is a list of field names (e.g. "ApprovalValues") to
+	// unconditionally include in API requests. By default, fields with
+	// empty values are omitted from API requests. However, any non-pointer,
+	// non-interface field appearing in ForceSendFields will be sent to the
+	// server regardless of whether the field is empty or not. This may be
+	// used to include empty fields in Patch requests.
+	ForceSendFields []string `json:"-"`
+
+	// NullFields is a list of field names (e.g. "ApprovalValues") to
+	// include in API requests with the JSON null value. By default, fields
+	// with empty values are omitted from API requests. However, any field
+	// with an empty value appearing in NullFields will be sent to the
+	// server as null. It is an error if a field in this list has a
+	// non-empty value. This may be used to include null fields in Patch
+	// requests.
+	NullFields []string `json:"-"`
+}
+
+func (s *ProtoApiPb2V1IssueWrapper) MarshalJSON() ([]byte, error) {
+	type NoMethod ProtoApiPb2V1IssueWrapper
+	raw := NoMethod(*s)
+	return gensupport.MarshalJSON(raw, s.ForceSendFields, s.NullFields)
+}
+
+// ProtoApiPb2V1IssuesCommentsDeleteResponse: Response message of
+// request to delete/undelete an issue's comments.
+type ProtoApiPb2V1IssuesCommentsDeleteResponse struct {
+	// Error: Request error.
+	Error *ProtoApiPb2V1ErrorMessage `json:"error,omitempty"`
+
+	// ServerResponse contains the HTTP response code and headers from the
+	// server.
+	googleapi.ServerResponse `json:"-"`
+
+	// ForceSendFields is a list of field names (e.g. "Error") to
+	// unconditionally include in API requests. By default, fields with
+	// empty values are omitted from API requests. However, any non-pointer,
+	// non-interface field appearing in ForceSendFields will be sent to the
+	// server regardless of whether the field is empty or not. This may be
+	// used to include empty fields in Patch requests.
+	ForceSendFields []string `json:"-"`
+
+	// NullFields is a list of field names (e.g. "Error") to include in API
+	// requests with the JSON null value. By default, fields with empty
+	// values are omitted from API requests. However, any field with an
+	// empty value appearing in NullFields will be sent to the server as
+	// null. It is an error if a field in this list has a non-empty value.
+	// This may be used to include null fields in Patch requests.
+	NullFields []string `json:"-"`
+}
+
+func (s *ProtoApiPb2V1IssuesCommentsDeleteResponse) MarshalJSON() ([]byte, error) {
+	type NoMethod ProtoApiPb2V1IssuesCommentsDeleteResponse
+	raw := NoMethod(*s)
+	return gensupport.MarshalJSON(raw, s.ForceSendFields, s.NullFields)
+}
+
+// ProtoApiPb2V1IssuesCommentsInsertResponse: Response message of
+// request to insert an issue's comments.
+type ProtoApiPb2V1IssuesCommentsInsertResponse struct {
+	// Author: Atomic person.
+	Author *ProtoApiPb2V1AtomPerson `json:"author,omitempty"`
+
+	CanDelete bool `json:"canDelete,omitempty"`
+
+	Content string `json:"content,omitempty"`
+
+	// Error: Request error.
+	Error *ProtoApiPb2V1ErrorMessage `json:"error,omitempty"`
+
+	Id int64 `json:"id,omitempty"`
+
+	Kind string `json:"kind,omitempty"`
+
+	Published string `json:"published,omitempty"`
+
+	// Updates: Issue update.
+	Updates *ProtoApiPb2V1Update `json:"updates,omitempty"`
+
+	// ServerResponse contains the HTTP response code and headers from the
+	// server.
+	googleapi.ServerResponse `json:"-"`
+
+	// ForceSendFields is a list of field names (e.g. "Author") to
+	// unconditionally include in API requests. By default, fields with
+	// empty values are omitted from API requests. However, any non-pointer,
+	// non-interface field appearing in ForceSendFields will be sent to the
+	// server regardless of whether the field is empty or not. This may be
+	// used to include empty fields in Patch requests.
+	ForceSendFields []string `json:"-"`
+
+	// NullFields is a list of field names (e.g. "Author") to include in API
+	// requests with the JSON null value. By default, fields with empty
+	// values are omitted from API requests. However, any field with an
+	// empty value appearing in NullFields will be sent to the server as
+	// null. It is an error if a field in this list has a non-empty value.
+	// This may be used to include null fields in Patch requests.
+	NullFields []string `json:"-"`
+}
+
+func (s *ProtoApiPb2V1IssuesCommentsInsertResponse) MarshalJSON() ([]byte, error) {
+	type NoMethod ProtoApiPb2V1IssuesCommentsInsertResponse
+	raw := NoMethod(*s)
+	return gensupport.MarshalJSON(raw, s.ForceSendFields, s.NullFields)
+}
+
+// ProtoApiPb2V1IssuesCommentsListResponse: Response message of request
+// to list an issue's comments.
+type ProtoApiPb2V1IssuesCommentsListResponse struct {
+	// Error: Request error.
+	Error *ProtoApiPb2V1ErrorMessage `json:"error,omitempty"`
+
+	// Items: Issue comment details.
+	Items []*ProtoApiPb2V1IssueCommentWrapper `json:"items,omitempty"`
+
+	Kind string `json:"kind,omitempty"`
+
+	TotalResults int64 `json:"totalResults,omitempty"`
+
+	// ServerResponse contains the HTTP response code and headers from the
+	// server.
+	googleapi.ServerResponse `json:"-"`
+
+	// ForceSendFields is a list of field names (e.g. "Error") to
+	// unconditionally include in API requests. By default, fields with
+	// empty values are omitted from API requests. However, any non-pointer,
+	// non-interface field appearing in ForceSendFields will be sent to the
+	// server regardless of whether the field is empty or not. This may be
+	// used to include empty fields in Patch requests.
+	ForceSendFields []string `json:"-"`
+
+	// NullFields is a list of field names (e.g. "Error") to include in API
+	// requests with the JSON null value. By default, fields with empty
+	// values are omitted from API requests. However, any field with an
+	// empty value appearing in NullFields will be sent to the server as
+	// null. It is an error if a field in this list has a non-empty value.
+	// This may be used to include null fields in Patch requests.
+	NullFields []string `json:"-"`
+}
+
+func (s *ProtoApiPb2V1IssuesCommentsListResponse) MarshalJSON() ([]byte, error) {
+	type NoMethod ProtoApiPb2V1IssuesCommentsListResponse
+	raw := NoMethod(*s)
+	return gensupport.MarshalJSON(raw, s.ForceSendFields, s.NullFields)
+}
+
+// ProtoApiPb2V1IssuesGetInsertResponse: Response message of request to
+// get/insert an issue.
+type ProtoApiPb2V1IssuesGetInsertResponse struct {
+	// ApprovalValues: Approval Value details
+	ApprovalValues []*ProtoApiPb2V1Approval `json:"approvalValues,omitempty"`
+
+	// Author: Atomic person.
+	Author *ProtoApiPb2V1AtomPerson `json:"author,omitempty"`
+
+	// BlockedOn: Issue reference.
+	BlockedOn []*ProtoApiPb2V1IssueRef `json:"blockedOn,omitempty"`
+
+	// Blocking: Issue reference.
+	Blocking []*ProtoApiPb2V1IssueRef `json:"blocking,omitempty"`
+
+	CanComment bool `json:"canComment,omitempty"`
+
+	CanEdit bool `json:"canEdit,omitempty"`
+
+	// Cc: Atomic person.
+	Cc []*ProtoApiPb2V1AtomPerson `json:"cc,omitempty"`
+
+	Closed string `json:"closed,omitempty"`
+
+	ComponentModified string `json:"component_modified,omitempty"`
+
+	Components []string `json:"components,omitempty"`
+
+	// Error: Request error.
+	Error *ProtoApiPb2V1ErrorMessage `json:"error,omitempty"`
+
+	// FieldValues: Custom field values.
+	FieldValues []*ProtoApiPb2V1FieldValue `json:"fieldValues,omitempty"`
+
+	Id int64 `json:"id,omitempty"`
+
+	Kind string `json:"kind,omitempty"`
+
+	Labels []string `json:"labels,omitempty"`
+
+	// MergedInto: Issue reference.
+	MergedInto *ProtoApiPb2V1IssueRef `json:"mergedInto,omitempty"`
+
+	// Owner: Atomic person.
+	Owner *ProtoApiPb2V1AtomPerson `json:"owner,omitempty"`
+
+	OwnerModified string `json:"owner_modified,omitempty"`
+
+	// Phases: Issue phase details.
+	Phases []*ProtoApiPb2V1Phase `json:"phases,omitempty"`
+
+	ProjectId string `json:"projectId,omitempty"`
+
+	Published string `json:"published,omitempty"`
+
+	Starred bool `json:"starred,omitempty"`
+
+	Stars int64 `json:"stars,omitempty"`
+
+	// Possible values:
+	//   "closed"
+	//   "open"
+	State string `json:"state,omitempty"`
+
+	Status string `json:"status,omitempty"`
+
+	StatusModified string `json:"status_modified,omitempty"`
+
+	Summary string `json:"summary,omitempty"`
+
+	Title string `json:"title,omitempty"`
+
+	Updated string `json:"updated,omitempty"`
+
+	// ServerResponse contains the HTTP response code and headers from the
+	// server.
+	googleapi.ServerResponse `json:"-"`
+
+	// ForceSendFields is a list of field names (e.g. "ApprovalValues") to
+	// unconditionally include in API requests. By default, fields with
+	// empty values are omitted from API requests. However, any non-pointer,
+	// non-interface field appearing in ForceSendFields will be sent to the
+	// server regardless of whether the field is empty or not. This may be
+	// used to include empty fields in Patch requests.
+	ForceSendFields []string `json:"-"`
+
+	// NullFields is a list of field names (e.g. "ApprovalValues") to
+	// include in API requests with the JSON null value. By default, fields
+	// with empty values are omitted from API requests. However, any field
+	// with an empty value appearing in NullFields will be sent to the
+	// server as null. It is an error if a field in this list has a
+	// non-empty value. This may be used to include null fields in Patch
+	// requests.
+	NullFields []string `json:"-"`
+}
+
+func (s *ProtoApiPb2V1IssuesGetInsertResponse) MarshalJSON() ([]byte, error) {
+	type NoMethod ProtoApiPb2V1IssuesGetInsertResponse
+	raw := NoMethod(*s)
+	return gensupport.MarshalJSON(raw, s.ForceSendFields, s.NullFields)
+}
+
+// ProtoApiPb2V1IssuesListResponse: Response message of request to list
+// issues.
+type ProtoApiPb2V1IssuesListResponse struct {
+	// Error: Request error.
+	Error *ProtoApiPb2V1ErrorMessage `json:"error,omitempty"`
+
+	// Items: Issue details.
+	Items []*ProtoApiPb2V1IssueWrapper `json:"items,omitempty"`
+
+	Kind string `json:"kind,omitempty"`
+
+	TotalResults int64 `json:"totalResults,omitempty"`
+
+	// ServerResponse contains the HTTP response code and headers from the
+	// server.
+	googleapi.ServerResponse `json:"-"`
+
+	// ForceSendFields is a list of field names (e.g. "Error") to
+	// unconditionally include in API requests. By default, fields with
+	// empty values are omitted from API requests. However, any non-pointer,
+	// non-interface field appearing in ForceSendFields will be sent to the
+	// server regardless of whether the field is empty or not. This may be
+	// used to include empty fields in Patch requests.
+	ForceSendFields []string `json:"-"`
+
+	// NullFields is a list of field names (e.g. "Error") to include in API
+	// requests with the JSON null value. By default, fields with empty
+	// values are omitted from API requests. However, any field with an
+	// empty value appearing in NullFields will be sent to the server as
+	// null. It is an error if a field in this list has a non-empty value.
+	// This may be used to include null fields in Patch requests.
+	NullFields []string `json:"-"`
+}
+
+func (s *ProtoApiPb2V1IssuesListResponse) MarshalJSON() ([]byte, error) {
+	type NoMethod ProtoApiPb2V1IssuesListResponse
+	raw := NoMethod(*s)
+	return gensupport.MarshalJSON(raw, s.ForceSendFields, s.NullFields)
+}
+
+// ProtoApiPb2V1Label: Issue label.
+type ProtoApiPb2V1Label struct {
+	Description string `json:"description,omitempty"`
+
+	Label string `json:"label,omitempty"`
+
+	// ForceSendFields is a list of field names (e.g. "Description") to
+	// unconditionally include in API requests. By default, fields with
+	// empty values are omitted from API requests. However, any non-pointer,
+	// non-interface field appearing in ForceSendFields will be sent to the
+	// server regardless of whether the field is empty or not. This may be
+	// used to include empty fields in Patch requests.
+	ForceSendFields []string `json:"-"`
+
+	// NullFields is a list of field names (e.g. "Description") to include
+	// in API requests with the JSON null value. By default, fields with
+	// empty values are omitted from API requests. However, any field with
+	// an empty value appearing in NullFields will be sent to the server as
+	// null. It is an error if a field in this list has a non-empty value.
+	// This may be used to include null fields in Patch requests.
+	NullFields []string `json:"-"`
+}
+
+func (s *ProtoApiPb2V1Label) MarshalJSON() ([]byte, error) {
+	type NoMethod ProtoApiPb2V1Label
+	raw := NoMethod(*s)
+	return gensupport.MarshalJSON(raw, s.ForceSendFields, s.NullFields)
+}
+
+// ProtoApiPb2V1Phase: Issue phase details.
+type ProtoApiPb2V1Phase struct {
+	PhaseName string `json:"phaseName,omitempty"`
+
+	Rank int64 `json:"rank,omitempty,string"`
+
+	// ForceSendFields is a list of field names (e.g. "PhaseName") to
+	// unconditionally include in API requests. By default, fields with
+	// empty values are omitted from API requests. However, any non-pointer,
+	// non-interface field appearing in ForceSendFields will be sent to the
+	// server regardless of whether the field is empty or not. This may be
+	// used to include empty fields in Patch requests.
+	ForceSendFields []string `json:"-"`
+
+	// NullFields is a list of field names (e.g. "PhaseName") to include in
+	// API requests with the JSON null value. By default, fields with empty
+	// values are omitted from API requests. However, any field with an
+	// empty value appearing in NullFields will be sent to the server as
+	// null. It is an error if a field in this list has a non-empty value.
+	// This may be used to include null fields in Patch requests.
+	NullFields []string `json:"-"`
+}
+
+func (s *ProtoApiPb2V1Phase) MarshalJSON() ([]byte, error) {
+	type NoMethod ProtoApiPb2V1Phase
+	raw := NoMethod(*s)
+	return gensupport.MarshalJSON(raw, s.ForceSendFields, s.NullFields)
+}
+
+// ProtoApiPb2V1ProjectIssueConfig: Issue configuration of project.
+type ProtoApiPb2V1ProjectIssueConfig struct {
+	DefaultColumns []string `json:"defaultColumns,omitempty"`
+
+	DefaultPromptForMembers int64 `json:"defaultPromptForMembers,omitempty"`
+
+	DefaultPromptForNonMembers int64 `json:"defaultPromptForNonMembers,omitempty"`
+
+	DefaultSorting []string `json:"defaultSorting,omitempty"`
+
+	Kind string `json:"kind,omitempty"`
+
+	// Labels: Issue label.
+	Labels []*ProtoApiPb2V1Label `json:"labels,omitempty"`
+
+	// Prompts: Default issue template values.
+	Prompts []*ProtoApiPb2V1Prompt `json:"prompts,omitempty"`
+
+	RestrictToKnown bool `json:"restrictToKnown,omitempty"`
+
+	// Statuses: Issue status.
+	Statuses []*ProtoApiPb2V1Status `json:"statuses,omitempty"`
+
+	UsersCanSetLabels bool `json:"usersCanSetLabels,omitempty"`
+
+	// ForceSendFields is a list of field names (e.g. "DefaultColumns") to
+	// unconditionally include in API requests. By default, fields with
+	// empty values are omitted from API requests. However, any non-pointer,
+	// non-interface field appearing in ForceSendFields will be sent to the
+	// server regardless of whether the field is empty or not. This may be
+	// used to include empty fields in Patch requests.
+	ForceSendFields []string `json:"-"`
+
+	// NullFields is a list of field names (e.g. "DefaultColumns") to
+	// include in API requests with the JSON null value. By default, fields
+	// with empty values are omitted from API requests. However, any field
+	// with an empty value appearing in NullFields will be sent to the
+	// server as null. It is an error if a field in this list has a
+	// non-empty value. This may be used to include null fields in Patch
+	// requests.
+	NullFields []string `json:"-"`
+}
+
+func (s *ProtoApiPb2V1ProjectIssueConfig) MarshalJSON() ([]byte, error) {
+	type NoMethod ProtoApiPb2V1ProjectIssueConfig
+	raw := NoMethod(*s)
+	return gensupport.MarshalJSON(raw, s.ForceSendFields, s.NullFields)
+}
+
+// ProtoApiPb2V1ProjectWrapper: Project details.
+type ProtoApiPb2V1ProjectWrapper struct {
+	Description string `json:"description,omitempty"`
+
+	ExternalId string `json:"externalId,omitempty"`
+
+	HtmlLink string `json:"htmlLink,omitempty"`
+
+	// IssuesConfig: Issue configuration of project.
+	IssuesConfig *ProtoApiPb2V1ProjectIssueConfig `json:"issuesConfig,omitempty"`
+
+	Kind string `json:"kind,omitempty"`
+
+	// Members: Atomic person.
+	Members []*ProtoApiPb2V1AtomPerson `json:"members,omitempty"`
+
+	Name string `json:"name,omitempty"`
+
+	RepositoryUrls []string `json:"repositoryUrls,omitempty"`
+
+	// Possible values:
+	//   "contributor"
+	//   "member"
+	//   "owner"
+	Role string `json:"role,omitempty"`
+
+	Summary string `json:"summary,omitempty"`
+
+	VersionControlSystem string `json:"versionControlSystem,omitempty"`
+
+	// ForceSendFields is a list of field names (e.g. "Description") to
+	// unconditionally include in API requests. By default, fields with
+	// empty values are omitted from API requests. However, any non-pointer,
+	// non-interface field appearing in ForceSendFields will be sent to the
+	// server regardless of whether the field is empty or not. This may be
+	// used to include empty fields in Patch requests.
+	ForceSendFields []string `json:"-"`
+
+	// NullFields is a list of field names (e.g. "Description") to include
+	// in API requests with the JSON null value. By default, fields with
+	// empty values are omitted from API requests. However, any field with
+	// an empty value appearing in NullFields will be sent to the server as
+	// null. It is an error if a field in this list has a non-empty value.
+	// This may be used to include null fields in Patch requests.
+	NullFields []string `json:"-"`
+}
+
+func (s *ProtoApiPb2V1ProjectWrapper) MarshalJSON() ([]byte, error) {
+	type NoMethod ProtoApiPb2V1ProjectWrapper
+	raw := NoMethod(*s)
+	return gensupport.MarshalJSON(raw, s.ForceSendFields, s.NullFields)
+}
+
+// ProtoApiPb2V1Prompt: Default issue template values.
+type ProtoApiPb2V1Prompt struct {
+	ComponentRequired bool `json:"componentRequired,omitempty"`
+
+	DefaultToMember bool `json:"defaultToMember,omitempty"`
+
+	Description string `json:"description,omitempty"`
+
+	Labels []string `json:"labels,omitempty"`
+
+	MembersOnly bool `json:"membersOnly,omitempty"`
+
+	Name string `json:"name,omitempty"`
+
+	Status string `json:"status,omitempty"`
+
+	Title string `json:"title,omitempty"`
+
+	TitleMustBeEdited bool `json:"titleMustBeEdited,omitempty"`
+
+	// ForceSendFields is a list of field names (e.g. "ComponentRequired")
+	// to unconditionally include in API requests. By default, fields with
+	// empty values are omitted from API requests. However, any non-pointer,
+	// non-interface field appearing in ForceSendFields will be sent to the
+	// server regardless of whether the field is empty or not. This may be
+	// used to include empty fields in Patch requests.
+	ForceSendFields []string `json:"-"`
+
+	// NullFields is a list of field names (e.g. "ComponentRequired") to
+	// include in API requests with the JSON null value. By default, fields
+	// with empty values are omitted from API requests. However, any field
+	// with an empty value appearing in NullFields will be sent to the
+	// server as null. It is an error if a field in this list has a
+	// non-empty value. This may be used to include null fields in Patch
+	// requests.
+	NullFields []string `json:"-"`
+}
+
+func (s *ProtoApiPb2V1Prompt) MarshalJSON() ([]byte, error) {
+	type NoMethod ProtoApiPb2V1Prompt
+	raw := NoMethod(*s)
+	return gensupport.MarshalJSON(raw, s.ForceSendFields, s.NullFields)
+}
+
+// ProtoApiPb2V1Status: Issue status.
+type ProtoApiPb2V1Status struct {
+	Description string `json:"description,omitempty"`
+
+	MeansOpen bool `json:"meansOpen,omitempty"`
+
+	Status string `json:"status,omitempty"`
+
+	// ForceSendFields is a list of field names (e.g. "Description") to
+	// unconditionally include in API requests. By default, fields with
+	// empty values are omitted from API requests. However, any non-pointer,
+	// non-interface field appearing in ForceSendFields will be sent to the
+	// server regardless of whether the field is empty or not. This may be
+	// used to include empty fields in Patch requests.
+	ForceSendFields []string `json:"-"`
+
+	// NullFields is a list of field names (e.g. "Description") to include
+	// in API requests with the JSON null value. By default, fields with
+	// empty values are omitted from API requests. However, any field with
+	// an empty value appearing in NullFields will be sent to the server as
+	// null. It is an error if a field in this list has a non-empty value.
+	// This may be used to include null fields in Patch requests.
+	NullFields []string `json:"-"`
+}
+
+func (s *ProtoApiPb2V1Status) MarshalJSON() ([]byte, error) {
+	type NoMethod ProtoApiPb2V1Status
+	raw := NoMethod(*s)
+	return gensupport.MarshalJSON(raw, s.ForceSendFields, s.NullFields)
+}
+
+// ProtoApiPb2V1Update: Issue update.
+type ProtoApiPb2V1Update struct {
+	BlockedOn []string `json:"blockedOn,omitempty"`
+
+	Blocking []string `json:"blocking,omitempty"`
+
+	Cc []string `json:"cc,omitempty"`
+
+	Components []string `json:"components,omitempty"`
+
+	// FieldValues: Custom field values.
+	FieldValues []*ProtoApiPb2V1FieldValue `json:"fieldValues,omitempty"`
+
+	IsDescription bool `json:"is_description,omitempty"`
+
+	Kind string `json:"kind,omitempty"`
+
+	Labels []string `json:"labels,omitempty"`
+
+	MergedInto string `json:"mergedInto,omitempty"`
+
+	MoveToProject string `json:"moveToProject,omitempty"`
+
+	Owner string `json:"owner,omitempty"`
+
+	Status string `json:"status,omitempty"`
+
+	Summary string `json:"summary,omitempty"`
+
+	// ForceSendFields is a list of field names (e.g. "BlockedOn") to
+	// unconditionally include in API requests. By default, fields with
+	// empty values are omitted from API requests. However, any non-pointer,
+	// non-interface field appearing in ForceSendFields will be sent to the
+	// server regardless of whether the field is empty or not. This may be
+	// used to include empty fields in Patch requests.
+	ForceSendFields []string `json:"-"`
+
+	// NullFields is a list of field names (e.g. "BlockedOn") to include in
+	// API requests with the JSON null value. By default, fields with empty
+	// values are omitted from API requests. However, any field with an
+	// empty value appearing in NullFields will be sent to the server as
+	// null. It is an error if a field in this list has a non-empty value.
+	// This may be used to include null fields in Patch requests.
+	NullFields []string `json:"-"`
+}
+
+func (s *ProtoApiPb2V1Update) MarshalJSON() ([]byte, error) {
+	type NoMethod ProtoApiPb2V1Update
+	raw := NoMethod(*s)
+	return gensupport.MarshalJSON(raw, s.ForceSendFields, s.NullFields)
+}
+
+// ProtoApiPb2V1UserGroupSettingsWrapper: User group settings.
+type ProtoApiPb2V1UserGroupSettingsWrapper struct {
+	// Possible values:
+	//   "BAGGINS"
+	//   "CHROME_INFRA_AUTH"
+	//   "COMPUTED"
+	//   "MDB"
+	ExtGroupType string `json:"ext_group_type,omitempty"`
+
+	GroupName string `json:"groupName,omitempty"`
+
+	LastSyncTime int64 `json:"last_sync_time,omitempty"`
+
+	// Possible values:
+	//   "ANYONE"
+	//   "MEMBERS" (default)
+	//   "OWNERS"
+	WhoCanViewMembers string `json:"who_can_view_members,omitempty"`
+
+	// ForceSendFields is a list of field names (e.g. "ExtGroupType") to
+	// unconditionally include in API requests. By default, fields with
+	// empty values are omitted from API requests. However, any non-pointer,
+	// non-interface field appearing in ForceSendFields will be sent to the
+	// server regardless of whether the field is empty or not. This may be
+	// used to include empty fields in Patch requests.
+	ForceSendFields []string `json:"-"`
+
+	// NullFields is a list of field names (e.g. "ExtGroupType") to include
+	// in API requests with the JSON null value. By default, fields with
+	// empty values are omitted from API requests. However, any field with
+	// an empty value appearing in NullFields will be sent to the server as
+	// null. It is an error if a field in this list has a non-empty value.
+	// This may be used to include null fields in Patch requests.
+	NullFields []string `json:"-"`
+}
+
+func (s *ProtoApiPb2V1UserGroupSettingsWrapper) MarshalJSON() ([]byte, error) {
+	type NoMethod ProtoApiPb2V1UserGroupSettingsWrapper
+	raw := NoMethod(*s)
+	return gensupport.MarshalJSON(raw, s.ForceSendFields, s.NullFields)
+}
+
+// ProtoApiPb2V1UsersGetResponse: Response message of request to get a
+// user.
+type ProtoApiPb2V1UsersGetResponse struct {
+	// Error: Request error.
+	Error *ProtoApiPb2V1ErrorMessage `json:"error,omitempty"`
+
+	Id string `json:"id,omitempty"`
+
+	Kind string `json:"kind,omitempty"`
+
+	// Projects: Project details.
+	Projects []*ProtoApiPb2V1ProjectWrapper `json:"projects,omitempty"`
+
+	// ServerResponse contains the HTTP response code and headers from the
+	// server.
+	googleapi.ServerResponse `json:"-"`
+
+	// ForceSendFields is a list of field names (e.g. "Error") to
+	// unconditionally include in API requests. By default, fields with
+	// empty values are omitted from API requests. However, any non-pointer,
+	// non-interface field appearing in ForceSendFields will be sent to the
+	// server regardless of whether the field is empty or not. This may be
+	// used to include empty fields in Patch requests.
+	ForceSendFields []string `json:"-"`
+
+	// NullFields is a list of field names (e.g. "Error") to include in API
+	// requests with the JSON null value. By default, fields with empty
+	// values are omitted from API requests. However, any field with an
+	// empty value appearing in NullFields will be sent to the server as
+	// null. It is an error if a field in this list has a non-empty value.
+	// This may be used to include null fields in Patch requests.
+	NullFields []string `json:"-"`
+}
+
+func (s *ProtoApiPb2V1UsersGetResponse) MarshalJSON() ([]byte, error) {
+	type NoMethod ProtoApiPb2V1UsersGetResponse
+	raw := NoMethod(*s)
+	return gensupport.MarshalJSON(raw, s.ForceSendFields, s.NullFields)
+}
+
+// method id "monorail.approvals.comments.insert":
+
+type ApprovalsCommentsInsertCall struct {
+	s                                   *Service
+	projectId                           string
+	issueId                             int64
+	approvalName                        string
+	protoapipb2v1approvalcommentwrapper *ProtoApiPb2V1ApprovalCommentWrapper
+	urlParams_                          gensupport.URLParams
+	ctx_                                context.Context
+	header_                             http.Header
+}
+
+// Insert: Add an approval comment.
+func (r *ApprovalsCommentsService) Insert(projectId string, issueId int64, approvalName string, protoapipb2v1approvalcommentwrapper *ProtoApiPb2V1ApprovalCommentWrapper) *ApprovalsCommentsInsertCall {
+	c := &ApprovalsCommentsInsertCall{s: r.s, urlParams_: make(gensupport.URLParams)}
+	c.projectId = projectId
+	c.issueId = issueId
+	c.approvalName = approvalName
+	c.protoapipb2v1approvalcommentwrapper = protoapipb2v1approvalcommentwrapper
+	return c
+}
+
+// SendEmail sets the optional parameter "sendEmail":
+func (c *ApprovalsCommentsInsertCall) SendEmail(sendEmail bool) *ApprovalsCommentsInsertCall {
+	c.urlParams_.Set("sendEmail", fmt.Sprint(sendEmail))
+	return c
+}
+
+// Fields allows partial responses to be retrieved. See
+// https://developers.google.com/gdata/docs/2.0/basics#PartialResponse
+// for more information.
+func (c *ApprovalsCommentsInsertCall) Fields(s ...googleapi.Field) *ApprovalsCommentsInsertCall {
+	c.urlParams_.Set("fields", googleapi.CombineFields(s))
+	return c
+}
+
+// Context sets the context to be used in this call's Do method. Any
+// pending HTTP request will be aborted if the provided context is
+// canceled.
+func (c *ApprovalsCommentsInsertCall) Context(ctx context.Context) *ApprovalsCommentsInsertCall {
+	c.ctx_ = ctx
+	return c
+}
+
+// Header returns an http.Header that can be modified by the caller to
+// add HTTP headers to the request.
+func (c *ApprovalsCommentsInsertCall) Header() http.Header {
+	if c.header_ == nil {
+		c.header_ = make(http.Header)
+	}
+	return c.header_
+}
+
+func (c *ApprovalsCommentsInsertCall) doRequest(alt string) (*http.Response, error) {
+	reqHeaders := make(http.Header)
+	reqHeaders.Set("x-goog-api-client", "gl-go/1.12.7 gdcl/20190905")
+	for k, v := range c.header_ {
+		reqHeaders[k] = v
+	}
+	reqHeaders.Set("User-Agent", c.s.userAgent())
+	var body io.Reader = nil
+	body, err := googleapi.WithoutDataWrapper.JSONReader(c.protoapipb2v1approvalcommentwrapper)
+	if err != nil {
+		return nil, err
+	}
+	reqHeaders.Set("Content-Type", "application/json")
+	c.urlParams_.Set("alt", alt)
+	c.urlParams_.Set("prettyPrint", "false")
+	urls := googleapi.ResolveRelative(c.s.BasePath, "projects/{projectId}/issues/{issueId}/approvals/{approvalName}/comments")
+	urls += "?" + c.urlParams_.Encode()
+	req, err := http.NewRequest("POST", urls, body)
+	if err != nil {
+		return nil, err
+	}
+	req.Header = reqHeaders
+	googleapi.Expand(req.URL, map[string]string{
+		"projectId":    c.projectId,
+		"issueId":      strconv.FormatInt(c.issueId, 10),
+		"approvalName": c.approvalName,
+	})
+	return gensupport.SendRequest(c.ctx_, c.s.client, req)
+}
+
+// Do executes the "monorail.approvals.comments.insert" call.
+// Exactly one of *ProtoApiPb2V1ApprovalsCommentsInsertResponse or error
+// will be non-nil. Any non-2xx status code is an error. Response
+// headers are in either
+// *ProtoApiPb2V1ApprovalsCommentsInsertResponse.ServerResponse.Header
+// or (if a response was returned at all) in
+// error.(*googleapi.Error).Header. Use googleapi.IsNotModified to check
+// whether the returned error was because http.StatusNotModified was
+// returned.
+func (c *ApprovalsCommentsInsertCall) Do(opts ...googleapi.CallOption) (*ProtoApiPb2V1ApprovalsCommentsInsertResponse, error) {
+	gensupport.SetOptions(c.urlParams_, opts...)
+	res, err := c.doRequest("json")
+	if res != nil && res.StatusCode == http.StatusNotModified {
+		if res.Body != nil {
+			res.Body.Close()
+		}
+		return nil, &googleapi.Error{
+			Code:   res.StatusCode,
+			Header: res.Header,
+		}
+	}
+	if err != nil {
+		return nil, err
+	}
+	defer googleapi.CloseBody(res)
+	if err := googleapi.CheckResponse(res); err != nil {
+		return nil, err
+	}
+	ret := &ProtoApiPb2V1ApprovalsCommentsInsertResponse{
+		ServerResponse: googleapi.ServerResponse{
+			Header:         res.Header,
+			HTTPStatusCode: res.StatusCode,
+		},
+	}
+	target := &ret
+	if err := gensupport.DecodeResponse(target, res); err != nil {
+		return nil, err
+	}
+	return ret, nil
+	// {
+	//   "description": "Add an approval comment.",
+	//   "httpMethod": "POST",
+	//   "id": "monorail.approvals.comments.insert",
+	//   "parameterOrder": [
+	//     "projectId",
+	//     "issueId",
+	//     "approvalName"
+	//   ],
+	//   "parameters": {
+	//     "approvalName": {
+	//       "location": "path",
+	//       "required": true,
+	//       "type": "string"
+	//     },
+	//     "issueId": {
+	//       "format": "int32",
+	//       "location": "path",
+	//       "required": true,
+	//       "type": "integer"
+	//     },
+	//     "projectId": {
+	//       "location": "path",
+	//       "required": true,
+	//       "type": "string"
+	//     },
+	//     "sendEmail": {
+	//       "location": "query",
+	//       "type": "boolean"
+	//     }
+	//   },
+	//   "path": "projects/{projectId}/issues/{issueId}/approvals/{approvalName}/comments",
+	//   "request": {
+	//     "$ref": "ProtoApiPb2V1ApprovalCommentWrapper",
+	//     "parameterName": "resource"
+	//   },
+	//   "response": {
+	//     "$ref": "ProtoApiPb2V1ApprovalsCommentsInsertResponse"
+	//   },
+	//   "scopes": [
+	//     "https://www.googleapis.com/auth/userinfo.email"
+	//   ]
+	// }
+
+}
+
+// method id "monorail.approvals.comments.list":
+
+type ApprovalsCommentsListCall struct {
+	s            *Service
+	projectId    string
+	issueId      int64
+	approvalName string
+	urlParams_   gensupport.URLParams
+	ifNoneMatch_ string
+	ctx_         context.Context
+	header_      http.Header
+}
+
+// List: List all comments for an issue approval.
+func (r *ApprovalsCommentsService) List(projectId string, issueId int64, approvalName string) *ApprovalsCommentsListCall {
+	c := &ApprovalsCommentsListCall{s: r.s, urlParams_: make(gensupport.URLParams)}
+	c.projectId = projectId
+	c.issueId = issueId
+	c.approvalName = approvalName
+	return c
+}
+
+// MaxResults sets the optional parameter "maxResults":
+func (c *ApprovalsCommentsListCall) MaxResults(maxResults int64) *ApprovalsCommentsListCall {
+	c.urlParams_.Set("maxResults", fmt.Sprint(maxResults))
+	return c
+}
+
+// StartIndex sets the optional parameter "startIndex":
+func (c *ApprovalsCommentsListCall) StartIndex(startIndex int64) *ApprovalsCommentsListCall {
+	c.urlParams_.Set("startIndex", fmt.Sprint(startIndex))
+	return c
+}
+
+// Fields allows partial responses to be retrieved. See
+// https://developers.google.com/gdata/docs/2.0/basics#PartialResponse
+// for more information.
+func (c *ApprovalsCommentsListCall) Fields(s ...googleapi.Field) *ApprovalsCommentsListCall {
+	c.urlParams_.Set("fields", googleapi.CombineFields(s))
+	return c
+}
+
+// IfNoneMatch sets the optional parameter which makes the operation
+// fail if the object's ETag matches the given value. This is useful for
+// getting updates only after the object has changed since the last
+// request. Use googleapi.IsNotModified to check whether the response
+// error from Do is the result of In-None-Match.
+func (c *ApprovalsCommentsListCall) IfNoneMatch(entityTag string) *ApprovalsCommentsListCall {
+	c.ifNoneMatch_ = entityTag
+	return c
+}
+
+// Context sets the context to be used in this call's Do method. Any
+// pending HTTP request will be aborted if the provided context is
+// canceled.
+func (c *ApprovalsCommentsListCall) Context(ctx context.Context) *ApprovalsCommentsListCall {
+	c.ctx_ = ctx
+	return c
+}
+
+// Header returns an http.Header that can be modified by the caller to
+// add HTTP headers to the request.
+func (c *ApprovalsCommentsListCall) Header() http.Header {
+	if c.header_ == nil {
+		c.header_ = make(http.Header)
+	}
+	return c.header_
+}
+
+func (c *ApprovalsCommentsListCall) doRequest(alt string) (*http.Response, error) {
+	reqHeaders := make(http.Header)
+	reqHeaders.Set("x-goog-api-client", "gl-go/1.12.7 gdcl/20190905")
+	for k, v := range c.header_ {
+		reqHeaders[k] = v
+	}
+	reqHeaders.Set("User-Agent", c.s.userAgent())
+	if c.ifNoneMatch_ != "" {
+		reqHeaders.Set("If-None-Match", c.ifNoneMatch_)
+	}
+	var body io.Reader = nil
+	c.urlParams_.Set("alt", alt)
+	c.urlParams_.Set("prettyPrint", "false")
+	urls := googleapi.ResolveRelative(c.s.BasePath, "projects/{projectId}/issues/{issueId}/approvals/{approvalName}/comments")
+	urls += "?" + c.urlParams_.Encode()
+	req, err := http.NewRequest("GET", urls, body)
+	if err != nil {
+		return nil, err
+	}
+	req.Header = reqHeaders
+	googleapi.Expand(req.URL, map[string]string{
+		"projectId":    c.projectId,
+		"issueId":      strconv.FormatInt(c.issueId, 10),
+		"approvalName": c.approvalName,
+	})
+	return gensupport.SendRequest(c.ctx_, c.s.client, req)
+}
+
+// Do executes the "monorail.approvals.comments.list" call.
+// Exactly one of *ProtoApiPb2V1ApprovalsCommentsListResponse or error
+// will be non-nil. Any non-2xx status code is an error. Response
+// headers are in either
+// *ProtoApiPb2V1ApprovalsCommentsListResponse.ServerResponse.Header or
+// (if a response was returned at all) in
+// error.(*googleapi.Error).Header. Use googleapi.IsNotModified to check
+// whether the returned error was because http.StatusNotModified was
+// returned.
+func (c *ApprovalsCommentsListCall) Do(opts ...googleapi.CallOption) (*ProtoApiPb2V1ApprovalsCommentsListResponse, error) {
+	gensupport.SetOptions(c.urlParams_, opts...)
+	res, err := c.doRequest("json")
+	if res != nil && res.StatusCode == http.StatusNotModified {
+		if res.Body != nil {
+			res.Body.Close()
+		}
+		return nil, &googleapi.Error{
+			Code:   res.StatusCode,
+			Header: res.Header,
+		}
+	}
+	if err != nil {
+		return nil, err
+	}
+	defer googleapi.CloseBody(res)
+	if err := googleapi.CheckResponse(res); err != nil {
+		return nil, err
+	}
+	ret := &ProtoApiPb2V1ApprovalsCommentsListResponse{
+		ServerResponse: googleapi.ServerResponse{
+			Header:         res.Header,
+			HTTPStatusCode: res.StatusCode,
+		},
+	}
+	target := &ret
+	if err := gensupport.DecodeResponse(target, res); err != nil {
+		return nil, err
+	}
+	return ret, nil
+	// {
+	//   "description": "List all comments for an issue approval.",
+	//   "httpMethod": "GET",
+	//   "id": "monorail.approvals.comments.list",
+	//   "parameterOrder": [
+	//     "projectId",
+	//     "issueId",
+	//     "approvalName"
+	//   ],
+	//   "parameters": {
+	//     "approvalName": {
+	//       "location": "path",
+	//       "required": true,
+	//       "type": "string"
+	//     },
+	//     "issueId": {
+	//       "format": "int32",
+	//       "location": "path",
+	//       "required": true,
+	//       "type": "integer"
+	//     },
+	//     "maxResults": {
+	//       "default": "100",
+	//       "format": "int32",
+	//       "location": "query",
+	//       "type": "integer"
+	//     },
+	//     "projectId": {
+	//       "location": "path",
+	//       "required": true,
+	//       "type": "string"
+	//     },
+	//     "startIndex": {
+	//       "format": "int32",
+	//       "location": "query",
+	//       "type": "integer"
+	//     }
+	//   },
+	//   "path": "projects/{projectId}/issues/{issueId}/approvals/{approvalName}/comments",
+	//   "response": {
+	//     "$ref": "ProtoApiPb2V1ApprovalsCommentsListResponse"
+	//   },
+	//   "scopes": [
+	//     "https://www.googleapis.com/auth/userinfo.email"
+	//   ]
+	// }
+
+}
+
+// method id "monorail.components.create":
+
+type ComponentsCreateCall struct {
+	s                                       *Service
+	projectId                               string
+	protoapipb2v1componentcreaterequestbody *ProtoApiPb2V1ComponentCreateRequestBody
+	urlParams_                              gensupport.URLParams
+	ctx_                                    context.Context
+	header_                                 http.Header
+}
+
+// Create: Create a component.
+func (r *ComponentsService) Create(projectId string, componentName string, protoapipb2v1componentcreaterequestbody *ProtoApiPb2V1ComponentCreateRequestBody) *ComponentsCreateCall {
+	c := &ComponentsCreateCall{s: r.s, urlParams_: make(gensupport.URLParams)}
+	c.projectId = projectId
+	c.urlParams_.Set("componentName", componentName)
+	c.protoapipb2v1componentcreaterequestbody = protoapipb2v1componentcreaterequestbody
+	return c
+}
+
+// Fields allows partial responses to be retrieved. See
+// https://developers.google.com/gdata/docs/2.0/basics#PartialResponse
+// for more information.
+func (c *ComponentsCreateCall) Fields(s ...googleapi.Field) *ComponentsCreateCall {
+	c.urlParams_.Set("fields", googleapi.CombineFields(s))
+	return c
+}
+
+// Context sets the context to be used in this call's Do method. Any
+// pending HTTP request will be aborted if the provided context is
+// canceled.
+func (c *ComponentsCreateCall) Context(ctx context.Context) *ComponentsCreateCall {
+	c.ctx_ = ctx
+	return c
+}
+
+// Header returns an http.Header that can be modified by the caller to
+// add HTTP headers to the request.
+func (c *ComponentsCreateCall) Header() http.Header {
+	if c.header_ == nil {
+		c.header_ = make(http.Header)
+	}
+	return c.header_
+}
+
+func (c *ComponentsCreateCall) doRequest(alt string) (*http.Response, error) {
+	reqHeaders := make(http.Header)
+	reqHeaders.Set("x-goog-api-client", "gl-go/1.12.7 gdcl/20190905")
+	for k, v := range c.header_ {
+		reqHeaders[k] = v
+	}
+	reqHeaders.Set("User-Agent", c.s.userAgent())
+	var body io.Reader = nil
+	body, err := googleapi.WithoutDataWrapper.JSONReader(c.protoapipb2v1componentcreaterequestbody)
+	if err != nil {
+		return nil, err
+	}
+	reqHeaders.Set("Content-Type", "application/json")
+	c.urlParams_.Set("alt", alt)
+	c.urlParams_.Set("prettyPrint", "false")
+	urls := googleapi.ResolveRelative(c.s.BasePath, "projects/{projectId}/components")
+	urls += "?" + c.urlParams_.Encode()
+	req, err := http.NewRequest("POST", urls, body)
+	if err != nil {
+		return nil, err
+	}
+	req.Header = reqHeaders
+	googleapi.Expand(req.URL, map[string]string{
+		"projectId": c.projectId,
+	})
+	return gensupport.SendRequest(c.ctx_, c.s.client, req)
+}
+
+// Do executes the "monorail.components.create" call.
+// Exactly one of *ProtoApiPb2V1Component or error will be non-nil. Any
+// non-2xx status code is an error. Response headers are in either
+// *ProtoApiPb2V1Component.ServerResponse.Header or (if a response was
+// returned at all) in error.(*googleapi.Error).Header. Use
+// googleapi.IsNotModified to check whether the returned error was
+// because http.StatusNotModified was returned.
+func (c *ComponentsCreateCall) Do(opts ...googleapi.CallOption) (*ProtoApiPb2V1Component, error) {
+	gensupport.SetOptions(c.urlParams_, opts...)
+	res, err := c.doRequest("json")
+	if res != nil && res.StatusCode == http.StatusNotModified {
+		if res.Body != nil {
+			res.Body.Close()
+		}
+		return nil, &googleapi.Error{
+			Code:   res.StatusCode,
+			Header: res.Header,
+		}
+	}
+	if err != nil {
+		return nil, err
+	}
+	defer googleapi.CloseBody(res)
+	if err := googleapi.CheckResponse(res); err != nil {
+		return nil, err
+	}
+	ret := &ProtoApiPb2V1Component{
+		ServerResponse: googleapi.ServerResponse{
+			Header:         res.Header,
+			HTTPStatusCode: res.StatusCode,
+		},
+	}
+	target := &ret
+	if err := gensupport.DecodeResponse(target, res); err != nil {
+		return nil, err
+	}
+	return ret, nil
+	// {
+	//   "description": "Create a component.",
+	//   "httpMethod": "POST",
+	//   "id": "monorail.components.create",
+	//   "parameterOrder": [
+	//     "projectId",
+	//     "componentName"
+	//   ],
+	//   "parameters": {
+	//     "componentName": {
+	//       "location": "query",
+	//       "required": true,
+	//       "type": "string"
+	//     },
+	//     "projectId": {
+	//       "location": "path",
+	//       "required": true,
+	//       "type": "string"
+	//     }
+	//   },
+	//   "path": "projects/{projectId}/components",
+	//   "request": {
+	//     "$ref": "ProtoApiPb2V1ComponentCreateRequestBody",
+	//     "parameterName": "resource"
+	//   },
+	//   "response": {
+	//     "$ref": "ProtoApiPb2V1Component"
+	//   },
+	//   "scopes": [
+	//     "https://www.googleapis.com/auth/userinfo.email"
+	//   ]
+	// }
+
+}
+
+// method id "monorail.components.delete":
+
+type ComponentsDeleteCall struct {
+	s             *Service
+	projectId     string
+	componentPath string
+	urlParams_    gensupport.URLParams
+	ctx_          context.Context
+	header_       http.Header
+}
+
+// Delete: Delete a component.
+func (r *ComponentsService) Delete(projectId string, componentPath string) *ComponentsDeleteCall {
+	c := &ComponentsDeleteCall{s: r.s, urlParams_: make(gensupport.URLParams)}
+	c.projectId = projectId
+	c.componentPath = componentPath
+	return c
+}
+
+// Fields allows partial responses to be retrieved. See
+// https://developers.google.com/gdata/docs/2.0/basics#PartialResponse
+// for more information.
+func (c *ComponentsDeleteCall) Fields(s ...googleapi.Field) *ComponentsDeleteCall {
+	c.urlParams_.Set("fields", googleapi.CombineFields(s))
+	return c
+}
+
+// Context sets the context to be used in this call's Do method. Any
+// pending HTTP request will be aborted if the provided context is
+// canceled.
+func (c *ComponentsDeleteCall) Context(ctx context.Context) *ComponentsDeleteCall {
+	c.ctx_ = ctx
+	return c
+}
+
+// Header returns an http.Header that can be modified by the caller to
+// add HTTP headers to the request.
+func (c *ComponentsDeleteCall) Header() http.Header {
+	if c.header_ == nil {
+		c.header_ = make(http.Header)
+	}
+	return c.header_
+}
+
+func (c *ComponentsDeleteCall) doRequest(alt string) (*http.Response, error) {
+	reqHeaders := make(http.Header)
+	reqHeaders.Set("x-goog-api-client", "gl-go/1.12.7 gdcl/20190905")
+	for k, v := range c.header_ {
+		reqHeaders[k] = v
+	}
+	reqHeaders.Set("User-Agent", c.s.userAgent())
+	var body io.Reader = nil
+	c.urlParams_.Set("alt", alt)
+	c.urlParams_.Set("prettyPrint", "false")
+	urls := googleapi.ResolveRelative(c.s.BasePath, "projects/{projectId}/components/{componentPath}")
+	urls += "?" + c.urlParams_.Encode()
+	req, err := http.NewRequest("DELETE", urls, body)
+	if err != nil {
+		return nil, err
+	}
+	req.Header = reqHeaders
+	googleapi.Expand(req.URL, map[string]string{
+		"projectId":     c.projectId,
+		"componentPath": c.componentPath,
+	})
+	return gensupport.SendRequest(c.ctx_, c.s.client, req)
+}
+
+// Do executes the "monorail.components.delete" call.
+func (c *ComponentsDeleteCall) Do(opts ...googleapi.CallOption) error {
+	gensupport.SetOptions(c.urlParams_, opts...)
+	res, err := c.doRequest("json")
+	if err != nil {
+		return err
+	}
+	defer googleapi.CloseBody(res)
+	if err := googleapi.CheckResponse(res); err != nil {
+		return err
+	}
+	return nil
+	// {
+	//   "description": "Delete a component.",
+	//   "httpMethod": "DELETE",
+	//   "id": "monorail.components.delete",
+	//   "parameterOrder": [
+	//     "projectId",
+	//     "componentPath"
+	//   ],
+	//   "parameters": {
+	//     "componentPath": {
+	//       "location": "path",
+	//       "required": true,
+	//       "type": "string"
+	//     },
+	//     "projectId": {
+	//       "location": "path",
+	//       "required": true,
+	//       "type": "string"
+	//     }
+	//   },
+	//   "path": "projects/{projectId}/components/{componentPath}",
+	//   "scopes": [
+	//     "https://www.googleapis.com/auth/userinfo.email"
+	//   ]
+	// }
+
+}
+
+// method id "monorail.components.list":
+
+type ComponentsListCall struct {
+	s            *Service
+	projectId    string
+	urlParams_   gensupport.URLParams
+	ifNoneMatch_ string
+	ctx_         context.Context
+	header_      http.Header
+}
+
+// List: List all components of a given project.
+func (r *ComponentsService) List(projectId string) *ComponentsListCall {
+	c := &ComponentsListCall{s: r.s, urlParams_: make(gensupport.URLParams)}
+	c.projectId = projectId
+	return c
+}
+
+// Fields allows partial responses to be retrieved. See
+// https://developers.google.com/gdata/docs/2.0/basics#PartialResponse
+// for more information.
+func (c *ComponentsListCall) Fields(s ...googleapi.Field) *ComponentsListCall {
+	c.urlParams_.Set("fields", googleapi.CombineFields(s))
+	return c
+}
+
+// IfNoneMatch sets the optional parameter which makes the operation
+// fail if the object's ETag matches the given value. This is useful for
+// getting updates only after the object has changed since the last
+// request. Use googleapi.IsNotModified to check whether the response
+// error from Do is the result of In-None-Match.
+func (c *ComponentsListCall) IfNoneMatch(entityTag string) *ComponentsListCall {
+	c.ifNoneMatch_ = entityTag
+	return c
+}
+
+// Context sets the context to be used in this call's Do method. Any
+// pending HTTP request will be aborted if the provided context is
+// canceled.
+func (c *ComponentsListCall) Context(ctx context.Context) *ComponentsListCall {
+	c.ctx_ = ctx
+	return c
+}
+
+// Header returns an http.Header that can be modified by the caller to
+// add HTTP headers to the request.
+func (c *ComponentsListCall) Header() http.Header {
+	if c.header_ == nil {
+		c.header_ = make(http.Header)
+	}
+	return c.header_
+}
+
+func (c *ComponentsListCall) doRequest(alt string) (*http.Response, error) {
+	reqHeaders := make(http.Header)
+	reqHeaders.Set("x-goog-api-client", "gl-go/1.12.7 gdcl/20190905")
+	for k, v := range c.header_ {
+		reqHeaders[k] = v
+	}
+	reqHeaders.Set("User-Agent", c.s.userAgent())
+	if c.ifNoneMatch_ != "" {
+		reqHeaders.Set("If-None-Match", c.ifNoneMatch_)
+	}
+	var body io.Reader = nil
+	c.urlParams_.Set("alt", alt)
+	c.urlParams_.Set("prettyPrint", "false")
+	urls := googleapi.ResolveRelative(c.s.BasePath, "projects/{projectId}/components")
+	urls += "?" + c.urlParams_.Encode()
+	req, err := http.NewRequest("GET", urls, body)
+	if err != nil {
+		return nil, err
+	}
+	req.Header = reqHeaders
+	googleapi.Expand(req.URL, map[string]string{
+		"projectId": c.projectId,
+	})
+	return gensupport.SendRequest(c.ctx_, c.s.client, req)
+}
+
+// Do executes the "monorail.components.list" call.
+// Exactly one of *ProtoApiPb2V1ComponentsListResponse or error will be
+// non-nil. Any non-2xx status code is an error. Response headers are in
+// either *ProtoApiPb2V1ComponentsListResponse.ServerResponse.Header or
+// (if a response was returned at all) in
+// error.(*googleapi.Error).Header. Use googleapi.IsNotModified to check
+// whether the returned error was because http.StatusNotModified was
+// returned.
+func (c *ComponentsListCall) Do(opts ...googleapi.CallOption) (*ProtoApiPb2V1ComponentsListResponse, error) {
+	gensupport.SetOptions(c.urlParams_, opts...)
+	res, err := c.doRequest("json")
+	if res != nil && res.StatusCode == http.StatusNotModified {
+		if res.Body != nil {
+			res.Body.Close()
+		}
+		return nil, &googleapi.Error{
+			Code:   res.StatusCode,
+			Header: res.Header,
+		}
+	}
+	if err != nil {
+		return nil, err
+	}
+	defer googleapi.CloseBody(res)
+	if err := googleapi.CheckResponse(res); err != nil {
+		return nil, err
+	}
+	ret := &ProtoApiPb2V1ComponentsListResponse{
+		ServerResponse: googleapi.ServerResponse{
+			Header:         res.Header,
+			HTTPStatusCode: res.StatusCode,
+		},
+	}
+	target := &ret
+	if err := gensupport.DecodeResponse(target, res); err != nil {
+		return nil, err
+	}
+	return ret, nil
+	// {
+	//   "description": "List all components of a given project.",
+	//   "httpMethod": "GET",
+	//   "id": "monorail.components.list",
+	//   "parameterOrder": [
+	//     "projectId"
+	//   ],
+	//   "parameters": {
+	//     "projectId": {
+	//       "location": "path",
+	//       "required": true,
+	//       "type": "string"
+	//     }
+	//   },
+	//   "path": "projects/{projectId}/components",
+	//   "response": {
+	//     "$ref": "ProtoApiPb2V1ComponentsListResponse"
+	//   },
+	//   "scopes": [
+	//     "https://www.googleapis.com/auth/userinfo.email"
+	//   ]
+	// }
+
+}
+
+// method id "monorail.components.update":
+
+type ComponentsUpdateCall struct {
+	s                                       *Service
+	projectId                               string
+	componentPath                           string
+	protoapipb2v1componentupdaterequestbody *ProtoApiPb2V1ComponentUpdateRequestBody
+	urlParams_                              gensupport.URLParams
+	ctx_                                    context.Context
+	header_                                 http.Header
+}
+
+// Update: Update a component.
+func (r *ComponentsService) Update(projectId string, componentPath string, protoapipb2v1componentupdaterequestbody *ProtoApiPb2V1ComponentUpdateRequestBody) *ComponentsUpdateCall {
+	c := &ComponentsUpdateCall{s: r.s, urlParams_: make(gensupport.URLParams)}
+	c.projectId = projectId
+	c.componentPath = componentPath
+	c.protoapipb2v1componentupdaterequestbody = protoapipb2v1componentupdaterequestbody
+	return c
+}
+
+// Fields allows partial responses to be retrieved. See
+// https://developers.google.com/gdata/docs/2.0/basics#PartialResponse
+// for more information.
+func (c *ComponentsUpdateCall) Fields(s ...googleapi.Field) *ComponentsUpdateCall {
+	c.urlParams_.Set("fields", googleapi.CombineFields(s))
+	return c
+}
+
+// Context sets the context to be used in this call's Do method. Any
+// pending HTTP request will be aborted if the provided context is
+// canceled.
+func (c *ComponentsUpdateCall) Context(ctx context.Context) *ComponentsUpdateCall {
+	c.ctx_ = ctx
+	return c
+}
+
+// Header returns an http.Header that can be modified by the caller to
+// add HTTP headers to the request.
+func (c *ComponentsUpdateCall) Header() http.Header {
+	if c.header_ == nil {
+		c.header_ = make(http.Header)
+	}
+	return c.header_
+}
+
+func (c *ComponentsUpdateCall) doRequest(alt string) (*http.Response, error) {
+	reqHeaders := make(http.Header)
+	reqHeaders.Set("x-goog-api-client", "gl-go/1.12.7 gdcl/20190905")
+	for k, v := range c.header_ {
+		reqHeaders[k] = v
+	}
+	reqHeaders.Set("User-Agent", c.s.userAgent())
+	var body io.Reader = nil
+	body, err := googleapi.WithoutDataWrapper.JSONReader(c.protoapipb2v1componentupdaterequestbody)
+	if err != nil {
+		return nil, err
+	}
+	reqHeaders.Set("Content-Type", "application/json")
+	c.urlParams_.Set("alt", alt)
+	c.urlParams_.Set("prettyPrint", "false")
+	urls := googleapi.ResolveRelative(c.s.BasePath, "projects/{projectId}/components/{componentPath}")
+	urls += "?" + c.urlParams_.Encode()
+	req, err := http.NewRequest("POST", urls, body)
+	if err != nil {
+		return nil, err
+	}
+	req.Header = reqHeaders
+	googleapi.Expand(req.URL, map[string]string{
+		"projectId":     c.projectId,
+		"componentPath": c.componentPath,
+	})
+	return gensupport.SendRequest(c.ctx_, c.s.client, req)
+}
+
+// Do executes the "monorail.components.update" call.
+func (c *ComponentsUpdateCall) Do(opts ...googleapi.CallOption) error {
+	gensupport.SetOptions(c.urlParams_, opts...)
+	res, err := c.doRequest("json")
+	if err != nil {
+		return err
+	}
+	defer googleapi.CloseBody(res)
+	if err := googleapi.CheckResponse(res); err != nil {
+		return err
+	}
+	return nil
+	// {
+	//   "description": "Update a component.",
+	//   "httpMethod": "POST",
+	//   "id": "monorail.components.update",
+	//   "parameterOrder": [
+	//     "projectId",
+	//     "componentPath"
+	//   ],
+	//   "parameters": {
+	//     "componentPath": {
+	//       "location": "path",
+	//       "required": true,
+	//       "type": "string"
+	//     },
+	//     "projectId": {
+	//       "location": "path",
+	//       "required": true,
+	//       "type": "string"
+	//     }
+	//   },
+	//   "path": "projects/{projectId}/components/{componentPath}",
+	//   "request": {
+	//     "$ref": "ProtoApiPb2V1ComponentUpdateRequestBody",
+	//     "parameterName": "resource"
+	//   },
+	//   "scopes": [
+	//     "https://www.googleapis.com/auth/userinfo.email"
+	//   ]
+	// }
+
+}
+
+// method id "monorail.groups.create":
+
+type GroupsCreateCall struct {
+	s          *Service
+	urlParams_ gensupport.URLParams
+	ctx_       context.Context
+	header_    http.Header
+}
+
+// Create: Create a new user group.
+func (r *GroupsService) Create(groupName string, whoCanViewMembers string) *GroupsCreateCall {
+	c := &GroupsCreateCall{s: r.s, urlParams_: make(gensupport.URLParams)}
+	c.urlParams_.Set("groupName", groupName)
+	c.urlParams_.Set("who_can_view_members", whoCanViewMembers)
+	return c
+}
+
+// ExtGroupType sets the optional parameter "ext_group_type":
+//
+// Possible values:
+//   "BAGGINS"
+//   "CHROME_INFRA_AUTH"
+//   "COMPUTED"
+//   "MDB"
+func (c *GroupsCreateCall) ExtGroupType(extGroupType string) *GroupsCreateCall {
+	c.urlParams_.Set("ext_group_type", extGroupType)
+	return c
+}
+
+// Fields allows partial responses to be retrieved. See
+// https://developers.google.com/gdata/docs/2.0/basics#PartialResponse
+// for more information.
+func (c *GroupsCreateCall) Fields(s ...googleapi.Field) *GroupsCreateCall {
+	c.urlParams_.Set("fields", googleapi.CombineFields(s))
+	return c
+}
+
+// Context sets the context to be used in this call's Do method. Any
+// pending HTTP request will be aborted if the provided context is
+// canceled.
+func (c *GroupsCreateCall) Context(ctx context.Context) *GroupsCreateCall {
+	c.ctx_ = ctx
+	return c
+}
+
+// Header returns an http.Header that can be modified by the caller to
+// add HTTP headers to the request.
+func (c *GroupsCreateCall) Header() http.Header {
+	if c.header_ == nil {
+		c.header_ = make(http.Header)
+	}
+	return c.header_
+}
+
+func (c *GroupsCreateCall) doRequest(alt string) (*http.Response, error) {
+	reqHeaders := make(http.Header)
+	reqHeaders.Set("x-goog-api-client", "gl-go/1.12.7 gdcl/20190905")
+	for k, v := range c.header_ {
+		reqHeaders[k] = v
+	}
+	reqHeaders.Set("User-Agent", c.s.userAgent())
+	var body io.Reader = nil
+	c.urlParams_.Set("alt", alt)
+	c.urlParams_.Set("prettyPrint", "false")
+	urls := googleapi.ResolveRelative(c.s.BasePath, "groups")
+	urls += "?" + c.urlParams_.Encode()
+	req, err := http.NewRequest("POST", urls, body)
+	if err != nil {
+		return nil, err
+	}
+	req.Header = reqHeaders
+	return gensupport.SendRequest(c.ctx_, c.s.client, req)
+}
+
+// Do executes the "monorail.groups.create" call.
+// Exactly one of *ProtoApiPb2V1GroupsCreateResponse or error will be
+// non-nil. Any non-2xx status code is an error. Response headers are in
+// either *ProtoApiPb2V1GroupsCreateResponse.ServerResponse.Header or
+// (if a response was returned at all) in
+// error.(*googleapi.Error).Header. Use googleapi.IsNotModified to check
+// whether the returned error was because http.StatusNotModified was
+// returned.
+func (c *GroupsCreateCall) Do(opts ...googleapi.CallOption) (*ProtoApiPb2V1GroupsCreateResponse, error) {
+	gensupport.SetOptions(c.urlParams_, opts...)
+	res, err := c.doRequest("json")
+	if res != nil && res.StatusCode == http.StatusNotModified {
+		if res.Body != nil {
+			res.Body.Close()
+		}
+		return nil, &googleapi.Error{
+			Code:   res.StatusCode,
+			Header: res.Header,
+		}
+	}
+	if err != nil {
+		return nil, err
+	}
+	defer googleapi.CloseBody(res)
+	if err := googleapi.CheckResponse(res); err != nil {
+		return nil, err
+	}
+	ret := &ProtoApiPb2V1GroupsCreateResponse{
+		ServerResponse: googleapi.ServerResponse{
+			Header:         res.Header,
+			HTTPStatusCode: res.StatusCode,
+		},
+	}
+	target := &ret
+	if err := gensupport.DecodeResponse(target, res); err != nil {
+		return nil, err
+	}
+	return ret, nil
+	// {
+	//   "description": "Create a new user group.",
+	//   "httpMethod": "POST",
+	//   "id": "monorail.groups.create",
+	//   "parameterOrder": [
+	//     "groupName",
+	//     "who_can_view_members"
+	//   ],
+	//   "parameters": {
+	//     "ext_group_type": {
+	//       "enum": [
+	//         "BAGGINS",
+	//         "CHROME_INFRA_AUTH",
+	//         "COMPUTED",
+	//         "MDB"
+	//       ],
+	//       "enumDescriptions": [
+	//         "",
+	//         "",
+	//         "",
+	//         ""
+	//       ],
+	//       "location": "query",
+	//       "type": "string"
+	//     },
+	//     "groupName": {
+	//       "location": "query",
+	//       "required": true,
+	//       "type": "string"
+	//     },
+	//     "who_can_view_members": {
+	//       "default": "MEMBERS",
+	//       "enum": [
+	//         "ANYONE",
+	//         "MEMBERS",
+	//         "OWNERS"
+	//       ],
+	//       "enumDescriptions": [
+	//         "",
+	//         "",
+	//         ""
+	//       ],
+	//       "location": "query",
+	//       "required": true,
+	//       "type": "string"
+	//     }
+	//   },
+	//   "path": "groups",
+	//   "response": {
+	//     "$ref": "ProtoApiPb2V1GroupsCreateResponse"
+	//   },
+	//   "scopes": [
+	//     "https://www.googleapis.com/auth/userinfo.email"
+	//   ]
+	// }
+
+}
+
+// method id "monorail.groups.get":
+
+type GroupsGetCall struct {
+	s            *Service
+	groupName    string
+	urlParams_   gensupport.URLParams
+	ifNoneMatch_ string
+	ctx_         context.Context
+	header_      http.Header
+}
+
+// Get: Get a group's settings and users.
+func (r *GroupsService) Get(groupName string) *GroupsGetCall {
+	c := &GroupsGetCall{s: r.s, urlParams_: make(gensupport.URLParams)}
+	c.groupName = groupName
+	return c
+}
+
+// Fields allows partial responses to be retrieved. See
+// https://developers.google.com/gdata/docs/2.0/basics#PartialResponse
+// for more information.
+func (c *GroupsGetCall) Fields(s ...googleapi.Field) *GroupsGetCall {
+	c.urlParams_.Set("fields", googleapi.CombineFields(s))
+	return c
+}
+
+// IfNoneMatch sets the optional parameter which makes the operation
+// fail if the object's ETag matches the given value. This is useful for
+// getting updates only after the object has changed since the last
+// request. Use googleapi.IsNotModified to check whether the response
+// error from Do is the result of In-None-Match.
+func (c *GroupsGetCall) IfNoneMatch(entityTag string) *GroupsGetCall {
+	c.ifNoneMatch_ = entityTag
+	return c
+}
+
+// Context sets the context to be used in this call's Do method. Any
+// pending HTTP request will be aborted if the provided context is
+// canceled.
+func (c *GroupsGetCall) Context(ctx context.Context) *GroupsGetCall {
+	c.ctx_ = ctx
+	return c
+}
+
+// Header returns an http.Header that can be modified by the caller to
+// add HTTP headers to the request.
+func (c *GroupsGetCall) Header() http.Header {
+	if c.header_ == nil {
+		c.header_ = make(http.Header)
+	}
+	return c.header_
+}
+
+func (c *GroupsGetCall) doRequest(alt string) (*http.Response, error) {
+	reqHeaders := make(http.Header)
+	reqHeaders.Set("x-goog-api-client", "gl-go/1.12.7 gdcl/20190905")
+	for k, v := range c.header_ {
+		reqHeaders[k] = v
+	}
+	reqHeaders.Set("User-Agent", c.s.userAgent())
+	if c.ifNoneMatch_ != "" {
+		reqHeaders.Set("If-None-Match", c.ifNoneMatch_)
+	}
+	var body io.Reader = nil
+	c.urlParams_.Set("alt", alt)
+	c.urlParams_.Set("prettyPrint", "false")
+	urls := googleapi.ResolveRelative(c.s.BasePath, "groups/{groupName}")
+	urls += "?" + c.urlParams_.Encode()
+	req, err := http.NewRequest("GET", urls, body)
+	if err != nil {
+		return nil, err
+	}
+	req.Header = reqHeaders
+	googleapi.Expand(req.URL, map[string]string{
+		"groupName": c.groupName,
+	})
+	return gensupport.SendRequest(c.ctx_, c.s.client, req)
+}
+
+// Do executes the "monorail.groups.get" call.
+// Exactly one of *ProtoApiPb2V1GroupsGetResponse or error will be
+// non-nil. Any non-2xx status code is an error. Response headers are in
+// either *ProtoApiPb2V1GroupsGetResponse.ServerResponse.Header or (if a
+// response was returned at all) in error.(*googleapi.Error).Header. Use
+// googleapi.IsNotModified to check whether the returned error was
+// because http.StatusNotModified was returned.
+func (c *GroupsGetCall) Do(opts ...googleapi.CallOption) (*ProtoApiPb2V1GroupsGetResponse, error) {
+	gensupport.SetOptions(c.urlParams_, opts...)
+	res, err := c.doRequest("json")
+	if res != nil && res.StatusCode == http.StatusNotModified {
+		if res.Body != nil {
+			res.Body.Close()
+		}
+		return nil, &googleapi.Error{
+			Code:   res.StatusCode,
+			Header: res.Header,
+		}
+	}
+	if err != nil {
+		return nil, err
+	}
+	defer googleapi.CloseBody(res)
+	if err := googleapi.CheckResponse(res); err != nil {
+		return nil, err
+	}
+	ret := &ProtoApiPb2V1GroupsGetResponse{
+		ServerResponse: googleapi.ServerResponse{
+			Header:         res.Header,
+			HTTPStatusCode: res.StatusCode,
+		},
+	}
+	target := &ret
+	if err := gensupport.DecodeResponse(target, res); err != nil {
+		return nil, err
+	}
+	return ret, nil
+	// {
+	//   "description": "Get a group's settings and users.",
+	//   "httpMethod": "GET",
+	//   "id": "monorail.groups.get",
+	//   "parameterOrder": [
+	//     "groupName"
+	//   ],
+	//   "parameters": {
+	//     "groupName": {
+	//       "location": "path",
+	//       "required": true,
+	//       "type": "string"
+	//     }
+	//   },
+	//   "path": "groups/{groupName}",
+	//   "response": {
+	//     "$ref": "ProtoApiPb2V1GroupsGetResponse"
+	//   },
+	//   "scopes": [
+	//     "https://www.googleapis.com/auth/userinfo.email"
+	//   ]
+	// }
+
+}
+
+// method id "monorail.groups.update":
+
+type GroupsUpdateCall struct {
+	s                          *Service
+	groupName                  string
+	protoapipb2v1groupcitizens *ProtoApiPb2V1GroupCitizens
+	urlParams_                 gensupport.URLParams
+	ctx_                       context.Context
+	header_                    http.Header
+}
+
+// Update: Update a group's settings and users.
+func (r *GroupsService) Update(groupName string, protoapipb2v1groupcitizens *ProtoApiPb2V1GroupCitizens) *GroupsUpdateCall {
+	c := &GroupsUpdateCall{s: r.s, urlParams_: make(gensupport.URLParams)}
+	c.groupName = groupName
+	c.protoapipb2v1groupcitizens = protoapipb2v1groupcitizens
+	return c
+}
+
+// ExtGroupType sets the optional parameter "ext_group_type":
+//
+// Possible values:
+//   "BAGGINS"
+//   "CHROME_INFRA_AUTH"
+//   "COMPUTED"
+//   "MDB"
+func (c *GroupsUpdateCall) ExtGroupType(extGroupType string) *GroupsUpdateCall {
+	c.urlParams_.Set("ext_group_type", extGroupType)
+	return c
+}
+
+// FriendProjects sets the optional parameter "friend_projects":
+func (c *GroupsUpdateCall) FriendProjects(friendProjects ...string) *GroupsUpdateCall {
+	c.urlParams_.SetMulti("friend_projects", append([]string{}, friendProjects...))
+	return c
+}
+
+// LastSyncTime sets the optional parameter "last_sync_time":
+func (c *GroupsUpdateCall) LastSyncTime(lastSyncTime int64) *GroupsUpdateCall {
+	c.urlParams_.Set("last_sync_time", fmt.Sprint(lastSyncTime))
+	return c
+}
+
+// WhoCanViewMembers sets the optional parameter "who_can_view_members":
+//
+// Possible values:
+//   "ANYONE"
+//   "MEMBERS"
+//   "OWNERS"
+func (c *GroupsUpdateCall) WhoCanViewMembers(whoCanViewMembers string) *GroupsUpdateCall {
+	c.urlParams_.Set("who_can_view_members", whoCanViewMembers)
+	return c
+}
+
+// Fields allows partial responses to be retrieved. See
+// https://developers.google.com/gdata/docs/2.0/basics#PartialResponse
+// for more information.
+func (c *GroupsUpdateCall) Fields(s ...googleapi.Field) *GroupsUpdateCall {
+	c.urlParams_.Set("fields", googleapi.CombineFields(s))
+	return c
+}
+
+// Context sets the context to be used in this call's Do method. Any
+// pending HTTP request will be aborted if the provided context is
+// canceled.
+func (c *GroupsUpdateCall) Context(ctx context.Context) *GroupsUpdateCall {
+	c.ctx_ = ctx
+	return c
+}
+
+// Header returns an http.Header that can be modified by the caller to
+// add HTTP headers to the request.
+func (c *GroupsUpdateCall) Header() http.Header {
+	if c.header_ == nil {
+		c.header_ = make(http.Header)
+	}
+	return c.header_
+}
+
+func (c *GroupsUpdateCall) doRequest(alt string) (*http.Response, error) {
+	reqHeaders := make(http.Header)
+	reqHeaders.Set("x-goog-api-client", "gl-go/1.12.7 gdcl/20190905")
+	for k, v := range c.header_ {
+		reqHeaders[k] = v
+	}
+	reqHeaders.Set("User-Agent", c.s.userAgent())
+	var body io.Reader = nil
+	body, err := googleapi.WithoutDataWrapper.JSONReader(c.protoapipb2v1groupcitizens)
+	if err != nil {
+		return nil, err
+	}
+	reqHeaders.Set("Content-Type", "application/json")
+	c.urlParams_.Set("alt", alt)
+	c.urlParams_.Set("prettyPrint", "false")
+	urls := googleapi.ResolveRelative(c.s.BasePath, "groups/{groupName}")
+	urls += "?" + c.urlParams_.Encode()
+	req, err := http.NewRequest("POST", urls, body)
+	if err != nil {
+		return nil, err
+	}
+	req.Header = reqHeaders
+	googleapi.Expand(req.URL, map[string]string{
+		"groupName": c.groupName,
+	})
+	return gensupport.SendRequest(c.ctx_, c.s.client, req)
+}
+
+// Do executes the "monorail.groups.update" call.
+// Exactly one of *ProtoApiPb2V1GroupsUpdateResponse or error will be
+// non-nil. Any non-2xx status code is an error. Response headers are in
+// either *ProtoApiPb2V1GroupsUpdateResponse.ServerResponse.Header or
+// (if a response was returned at all) in
+// error.(*googleapi.Error).Header. Use googleapi.IsNotModified to check
+// whether the returned error was because http.StatusNotModified was
+// returned.
+func (c *GroupsUpdateCall) Do(opts ...googleapi.CallOption) (*ProtoApiPb2V1GroupsUpdateResponse, error) {
+	gensupport.SetOptions(c.urlParams_, opts...)
+	res, err := c.doRequest("json")
+	if res != nil && res.StatusCode == http.StatusNotModified {
+		if res.Body != nil {
+			res.Body.Close()
+		}
+		return nil, &googleapi.Error{
+			Code:   res.StatusCode,
+			Header: res.Header,
+		}
+	}
+	if err != nil {
+		return nil, err
+	}
+	defer googleapi.CloseBody(res)
+	if err := googleapi.CheckResponse(res); err != nil {
+		return nil, err
+	}
+	ret := &ProtoApiPb2V1GroupsUpdateResponse{
+		ServerResponse: googleapi.ServerResponse{
+			Header:         res.Header,
+			HTTPStatusCode: res.StatusCode,
+		},
+	}
+	target := &ret
+	if err := gensupport.DecodeResponse(target, res); err != nil {
+		return nil, err
+	}
+	return ret, nil
+	// {
+	//   "description": "Update a group's settings and users.",
+	//   "httpMethod": "POST",
+	//   "id": "monorail.groups.update",
+	//   "parameterOrder": [
+	//     "groupName"
+	//   ],
+	//   "parameters": {
+	//     "ext_group_type": {
+	//       "enum": [
+	//         "BAGGINS",
+	//         "CHROME_INFRA_AUTH",
+	//         "COMPUTED",
+	//         "MDB"
+	//       ],
+	//       "enumDescriptions": [
+	//         "",
+	//         "",
+	//         "",
+	//         ""
+	//       ],
+	//       "location": "query",
+	//       "type": "string"
+	//     },
+	//     "friend_projects": {
+	//       "location": "query",
+	//       "repeated": true,
+	//       "type": "string"
+	//     },
+	//     "groupName": {
+	//       "location": "path",
+	//       "required": true,
+	//       "type": "string"
+	//     },
+	//     "last_sync_time": {
+	//       "format": "int32",
+	//       "location": "query",
+	//       "type": "integer"
+	//     },
+	//     "who_can_view_members": {
+	//       "enum": [
+	//         "ANYONE",
+	//         "MEMBERS",
+	//         "OWNERS"
+	//       ],
+	//       "enumDescriptions": [
+	//         "",
+	//         "",
+	//         ""
+	//       ],
+	//       "location": "query",
+	//       "type": "string"
+	//     }
+	//   },
+	//   "path": "groups/{groupName}",
+	//   "request": {
+	//     "$ref": "ProtoApiPb2V1GroupCitizens",
+	//     "parameterName": "resource"
+	//   },
+	//   "response": {
+	//     "$ref": "ProtoApiPb2V1GroupsUpdateResponse"
+	//   },
+	//   "scopes": [
+	//     "https://www.googleapis.com/auth/userinfo.email"
+	//   ]
+	// }
+
+}
+
+// method id "monorail.groups.settings.list":
+
+type GroupsSettingsListCall struct {
+	s            *Service
+	urlParams_   gensupport.URLParams
+	ifNoneMatch_ string
+	ctx_         context.Context
+	header_      http.Header
+}
+
+// List: List all group settings.
+func (r *GroupsSettingsService) List() *GroupsSettingsListCall {
+	c := &GroupsSettingsListCall{s: r.s, urlParams_: make(gensupport.URLParams)}
+	return c
+}
+
+// ImportedGroupsOnly sets the optional parameter "importedGroupsOnly":
+func (c *GroupsSettingsListCall) ImportedGroupsOnly(importedGroupsOnly bool) *GroupsSettingsListCall {
+	c.urlParams_.Set("importedGroupsOnly", fmt.Sprint(importedGroupsOnly))
+	return c
+}
+
+// Fields allows partial responses to be retrieved. See
+// https://developers.google.com/gdata/docs/2.0/basics#PartialResponse
+// for more information.
+func (c *GroupsSettingsListCall) Fields(s ...googleapi.Field) *GroupsSettingsListCall {
+	c.urlParams_.Set("fields", googleapi.CombineFields(s))
+	return c
+}
+
+// IfNoneMatch sets the optional parameter which makes the operation
+// fail if the object's ETag matches the given value. This is useful for
+// getting updates only after the object has changed since the last
+// request. Use googleapi.IsNotModified to check whether the response
+// error from Do is the result of In-None-Match.
+func (c *GroupsSettingsListCall) IfNoneMatch(entityTag string) *GroupsSettingsListCall {
+	c.ifNoneMatch_ = entityTag
+	return c
+}
+
+// Context sets the context to be used in this call's Do method. Any
+// pending HTTP request will be aborted if the provided context is
+// canceled.
+func (c *GroupsSettingsListCall) Context(ctx context.Context) *GroupsSettingsListCall {
+	c.ctx_ = ctx
+	return c
+}
+
+// Header returns an http.Header that can be modified by the caller to
+// add HTTP headers to the request.
+func (c *GroupsSettingsListCall) Header() http.Header {
+	if c.header_ == nil {
+		c.header_ = make(http.Header)
+	}
+	return c.header_
+}
+
+func (c *GroupsSettingsListCall) doRequest(alt string) (*http.Response, error) {
+	reqHeaders := make(http.Header)
+	reqHeaders.Set("x-goog-api-client", "gl-go/1.12.7 gdcl/20190905")
+	for k, v := range c.header_ {
+		reqHeaders[k] = v
+	}
+	reqHeaders.Set("User-Agent", c.s.userAgent())
+	if c.ifNoneMatch_ != "" {
+		reqHeaders.Set("If-None-Match", c.ifNoneMatch_)
+	}
+	var body io.Reader = nil
+	c.urlParams_.Set("alt", alt)
+	c.urlParams_.Set("prettyPrint", "false")
+	urls := googleapi.ResolveRelative(c.s.BasePath, "groupsettings")
+	urls += "?" + c.urlParams_.Encode()
+	req, err := http.NewRequest("GET", urls, body)
+	if err != nil {
+		return nil, err
+	}
+	req.Header = reqHeaders
+	return gensupport.SendRequest(c.ctx_, c.s.client, req)
+}
+
+// Do executes the "monorail.groups.settings.list" call.
+// Exactly one of *ProtoApiPb2V1GroupsSettingsListResponse or error will
+// be non-nil. Any non-2xx status code is an error. Response headers are
+// in either
+// *ProtoApiPb2V1GroupsSettingsListResponse.ServerResponse.Header or (if
+// a response was returned at all) in error.(*googleapi.Error).Header.
+// Use googleapi.IsNotModified to check whether the returned error was
+// because http.StatusNotModified was returned.
+func (c *GroupsSettingsListCall) Do(opts ...googleapi.CallOption) (*ProtoApiPb2V1GroupsSettingsListResponse, error) {
+	gensupport.SetOptions(c.urlParams_, opts...)
+	res, err := c.doRequest("json")
+	if res != nil && res.StatusCode == http.StatusNotModified {
+		if res.Body != nil {
+			res.Body.Close()
+		}
+		return nil, &googleapi.Error{
+			Code:   res.StatusCode,
+			Header: res.Header,
+		}
+	}
+	if err != nil {
+		return nil, err
+	}
+	defer googleapi.CloseBody(res)
+	if err := googleapi.CheckResponse(res); err != nil {
+		return nil, err
+	}
+	ret := &ProtoApiPb2V1GroupsSettingsListResponse{
+		ServerResponse: googleapi.ServerResponse{
+			Header:         res.Header,
+			HTTPStatusCode: res.StatusCode,
+		},
+	}
+	target := &ret
+	if err := gensupport.DecodeResponse(target, res); err != nil {
+		return nil, err
+	}
+	return ret, nil
+	// {
+	//   "description": "List all group settings.",
+	//   "httpMethod": "GET",
+	//   "id": "monorail.groups.settings.list",
+	//   "parameters": {
+	//     "importedGroupsOnly": {
+	//       "location": "query",
+	//       "type": "boolean"
+	//     }
+	//   },
+	//   "path": "groupsettings",
+	//   "response": {
+	//     "$ref": "ProtoApiPb2V1GroupsSettingsListResponse"
+	//   },
+	//   "scopes": [
+	//     "https://www.googleapis.com/auth/userinfo.email"
+	//   ]
+	// }
+
+}
+
+// method id "monorail.issues.get":
+
+type IssuesGetCall struct {
+	s            *Service
+	projectId    string
+	issueId      int64
+	urlParams_   gensupport.URLParams
+	ifNoneMatch_ string
+	ctx_         context.Context
+	header_      http.Header
+}
+
+// Get: Get an issue.
+func (r *IssuesService) Get(projectId string, issueId int64) *IssuesGetCall {
+	c := &IssuesGetCall{s: r.s, urlParams_: make(gensupport.URLParams)}
+	c.projectId = projectId
+	c.issueId = issueId
+	return c
+}
+
+// Fields allows partial responses to be retrieved. See
+// https://developers.google.com/gdata/docs/2.0/basics#PartialResponse
+// for more information.
+func (c *IssuesGetCall) Fields(s ...googleapi.Field) *IssuesGetCall {
+	c.urlParams_.Set("fields", googleapi.CombineFields(s))
+	return c
+}
+
+// IfNoneMatch sets the optional parameter which makes the operation
+// fail if the object's ETag matches the given value. This is useful for
+// getting updates only after the object has changed since the last
+// request. Use googleapi.IsNotModified to check whether the response
+// error from Do is the result of In-None-Match.
+func (c *IssuesGetCall) IfNoneMatch(entityTag string) *IssuesGetCall {
+	c.ifNoneMatch_ = entityTag
+	return c
+}
+
+// Context sets the context to be used in this call's Do method. Any
+// pending HTTP request will be aborted if the provided context is
+// canceled.
+func (c *IssuesGetCall) Context(ctx context.Context) *IssuesGetCall {
+	c.ctx_ = ctx
+	return c
+}
+
+// Header returns an http.Header that can be modified by the caller to
+// add HTTP headers to the request.
+func (c *IssuesGetCall) Header() http.Header {
+	if c.header_ == nil {
+		c.header_ = make(http.Header)
+	}
+	return c.header_
+}
+
+func (c *IssuesGetCall) doRequest(alt string) (*http.Response, error) {
+	reqHeaders := make(http.Header)
+	reqHeaders.Set("x-goog-api-client", "gl-go/1.12.7 gdcl/20190905")
+	for k, v := range c.header_ {
+		reqHeaders[k] = v
+	}
+	reqHeaders.Set("User-Agent", c.s.userAgent())
+	if c.ifNoneMatch_ != "" {
+		reqHeaders.Set("If-None-Match", c.ifNoneMatch_)
+	}
+	var body io.Reader = nil
+	c.urlParams_.Set("alt", alt)
+	c.urlParams_.Set("prettyPrint", "false")
+	urls := googleapi.ResolveRelative(c.s.BasePath, "projects/{projectId}/issues/{issueId}")
+	urls += "?" + c.urlParams_.Encode()
+	req, err := http.NewRequest("GET", urls, body)
+	if err != nil {
+		return nil, err
+	}
+	req.Header = reqHeaders
+	googleapi.Expand(req.URL, map[string]string{
+		"projectId": c.projectId,
+		"issueId":   strconv.FormatInt(c.issueId, 10),
+	})
+	return gensupport.SendRequest(c.ctx_, c.s.client, req)
+}
+
+// Do executes the "monorail.issues.get" call.
+// Exactly one of *ProtoApiPb2V1IssuesGetInsertResponse or error will be
+// non-nil. Any non-2xx status code is an error. Response headers are in
+// either *ProtoApiPb2V1IssuesGetInsertResponse.ServerResponse.Header or
+// (if a response was returned at all) in
+// error.(*googleapi.Error).Header. Use googleapi.IsNotModified to check
+// whether the returned error was because http.StatusNotModified was
+// returned.
+func (c *IssuesGetCall) Do(opts ...googleapi.CallOption) (*ProtoApiPb2V1IssuesGetInsertResponse, error) {
+	gensupport.SetOptions(c.urlParams_, opts...)
+	res, err := c.doRequest("json")
+	if res != nil && res.StatusCode == http.StatusNotModified {
+		if res.Body != nil {
+			res.Body.Close()
+		}
+		return nil, &googleapi.Error{
+			Code:   res.StatusCode,
+			Header: res.Header,
+		}
+	}
+	if err != nil {
+		return nil, err
+	}
+	defer googleapi.CloseBody(res)
+	if err := googleapi.CheckResponse(res); err != nil {
+		return nil, err
+	}
+	ret := &ProtoApiPb2V1IssuesGetInsertResponse{
+		ServerResponse: googleapi.ServerResponse{
+			Header:         res.Header,
+			HTTPStatusCode: res.StatusCode,
+		},
+	}
+	target := &ret
+	if err := gensupport.DecodeResponse(target, res); err != nil {
+		return nil, err
+	}
+	return ret, nil
+	// {
+	//   "description": "Get an issue.",
+	//   "httpMethod": "GET",
+	//   "id": "monorail.issues.get",
+	//   "parameterOrder": [
+	//     "projectId",
+	//     "issueId"
+	//   ],
+	//   "parameters": {
+	//     "issueId": {
+	//       "format": "int32",
+	//       "location": "path",
+	//       "required": true,
+	//       "type": "integer"
+	//     },
+	//     "projectId": {
+	//       "location": "path",
+	//       "required": true,
+	//       "type": "string"
+	//     }
+	//   },
+	//   "path": "projects/{projectId}/issues/{issueId}",
+	//   "response": {
+	//     "$ref": "ProtoApiPb2V1IssuesGetInsertResponse"
+	//   },
+	//   "scopes": [
+	//     "https://www.googleapis.com/auth/userinfo.email"
+	//   ]
+	// }
+
+}
+
+// method id "monorail.issues.insert":
+
+type IssuesInsertCall struct {
+	s                         *Service
+	projectId                 string
+	protoapipb2v1issuewrapper *ProtoApiPb2V1IssueWrapper
+	urlParams_                gensupport.URLParams
+	ctx_                      context.Context
+	header_                   http.Header
+}
+
+// Insert: Add a new issue.
+func (r *IssuesService) Insert(projectId string, protoapipb2v1issuewrapper *ProtoApiPb2V1IssueWrapper) *IssuesInsertCall {
+	c := &IssuesInsertCall{s: r.s, urlParams_: make(gensupport.URLParams)}
+	c.projectId = projectId
+	c.protoapipb2v1issuewrapper = protoapipb2v1issuewrapper
+	return c
+}
+
+// SendEmail sets the optional parameter "sendEmail":
+func (c *IssuesInsertCall) SendEmail(sendEmail bool) *IssuesInsertCall {
+	c.urlParams_.Set("sendEmail", fmt.Sprint(sendEmail))
+	return c
+}
+
+// Fields allows partial responses to be retrieved. See
+// https://developers.google.com/gdata/docs/2.0/basics#PartialResponse
+// for more information.
+func (c *IssuesInsertCall) Fields(s ...googleapi.Field) *IssuesInsertCall {
+	c.urlParams_.Set("fields", googleapi.CombineFields(s))
+	return c
+}
+
+// Context sets the context to be used in this call's Do method. Any
+// pending HTTP request will be aborted if the provided context is
+// canceled.
+func (c *IssuesInsertCall) Context(ctx context.Context) *IssuesInsertCall {
+	c.ctx_ = ctx
+	return c
+}
+
+// Header returns an http.Header that can be modified by the caller to
+// add HTTP headers to the request.
+func (c *IssuesInsertCall) Header() http.Header {
+	if c.header_ == nil {
+		c.header_ = make(http.Header)
+	}
+	return c.header_
+}
+
+func (c *IssuesInsertCall) doRequest(alt string) (*http.Response, error) {
+	reqHeaders := make(http.Header)
+	reqHeaders.Set("x-goog-api-client", "gl-go/1.12.7 gdcl/20190905")
+	for k, v := range c.header_ {
+		reqHeaders[k] = v
+	}
+	reqHeaders.Set("User-Agent", c.s.userAgent())
+	var body io.Reader = nil
+	body, err := googleapi.WithoutDataWrapper.JSONReader(c.protoapipb2v1issuewrapper)
+	if err != nil {
+		return nil, err
+	}
+	reqHeaders.Set("Content-Type", "application/json")
+	c.urlParams_.Set("alt", alt)
+	c.urlParams_.Set("prettyPrint", "false")
+	urls := googleapi.ResolveRelative(c.s.BasePath, "projects/{projectId}/issues")
+	urls += "?" + c.urlParams_.Encode()
+	req, err := http.NewRequest("POST", urls, body)
+	if err != nil {
+		return nil, err
+	}
+	req.Header = reqHeaders
+	googleapi.Expand(req.URL, map[string]string{
+		"projectId": c.projectId,
+	})
+	return gensupport.SendRequest(c.ctx_, c.s.client, req)
+}
+
+// Do executes the "monorail.issues.insert" call.
+// Exactly one of *ProtoApiPb2V1IssuesGetInsertResponse or error will be
+// non-nil. Any non-2xx status code is an error. Response headers are in
+// either *ProtoApiPb2V1IssuesGetInsertResponse.ServerResponse.Header or
+// (if a response was returned at all) in
+// error.(*googleapi.Error).Header. Use googleapi.IsNotModified to check
+// whether the returned error was because http.StatusNotModified was
+// returned.
+func (c *IssuesInsertCall) Do(opts ...googleapi.CallOption) (*ProtoApiPb2V1IssuesGetInsertResponse, error) {
+	gensupport.SetOptions(c.urlParams_, opts...)
+	res, err := c.doRequest("json")
+	if res != nil && res.StatusCode == http.StatusNotModified {
+		if res.Body != nil {
+			res.Body.Close()
+		}
+		return nil, &googleapi.Error{
+			Code:   res.StatusCode,
+			Header: res.Header,
+		}
+	}
+	if err != nil {
+		return nil, err
+	}
+	defer googleapi.CloseBody(res)
+	if err := googleapi.CheckResponse(res); err != nil {
+		return nil, err
+	}
+	ret := &ProtoApiPb2V1IssuesGetInsertResponse{
+		ServerResponse: googleapi.ServerResponse{
+			Header:         res.Header,
+			HTTPStatusCode: res.StatusCode,
+		},
+	}
+	target := &ret
+	if err := gensupport.DecodeResponse(target, res); err != nil {
+		return nil, err
+	}
+	return ret, nil
+	// {
+	//   "description": "Add a new issue.",
+	//   "httpMethod": "POST",
+	//   "id": "monorail.issues.insert",
+	//   "parameterOrder": [
+	//     "projectId"
+	//   ],
+	//   "parameters": {
+	//     "projectId": {
+	//       "location": "path",
+	//       "required": true,
+	//       "type": "string"
+	//     },
+	//     "sendEmail": {
+	//       "default": "true",
+	//       "location": "query",
+	//       "type": "boolean"
+	//     }
+	//   },
+	//   "path": "projects/{projectId}/issues",
+	//   "request": {
+	//     "$ref": "ProtoApiPb2V1IssueWrapper",
+	//     "parameterName": "resource"
+	//   },
+	//   "response": {
+	//     "$ref": "ProtoApiPb2V1IssuesGetInsertResponse"
+	//   },
+	//   "scopes": [
+	//     "https://www.googleapis.com/auth/userinfo.email"
+	//   ]
+	// }
+
+}
+
+// method id "monorail.issues.list":
+
+type IssuesListCall struct {
+	s            *Service
+	projectId    string
+	urlParams_   gensupport.URLParams
+	ifNoneMatch_ string
+	ctx_         context.Context
+	header_      http.Header
+}
+
+// List: List issues for projects.
+func (r *IssuesService) List(projectId string) *IssuesListCall {
+	c := &IssuesListCall{s: r.s, urlParams_: make(gensupport.URLParams)}
+	c.projectId = projectId
+	return c
+}
+
+// AdditionalProject sets the optional parameter "additionalProject":
+func (c *IssuesListCall) AdditionalProject(additionalProject ...string) *IssuesListCall {
+	c.urlParams_.SetMulti("additionalProject", append([]string{}, additionalProject...))
+	return c
+}
+
+// Can sets the optional parameter "can":
+//
+// Possible values:
+//   "all" (default)
+//   "new"
+//   "open"
+//   "owned"
+//   "reported"
+//   "starred"
+//   "to_verify"
+func (c *IssuesListCall) Can(can string) *IssuesListCall {
+	c.urlParams_.Set("can", can)
+	return c
+}
+
+// Label sets the optional parameter "label":
+func (c *IssuesListCall) Label(label string) *IssuesListCall {
+	c.urlParams_.Set("label", label)
+	return c
+}
+
+// MaxResults sets the optional parameter "maxResults":
+func (c *IssuesListCall) MaxResults(maxResults int64) *IssuesListCall {
+	c.urlParams_.Set("maxResults", fmt.Sprint(maxResults))
+	return c
+}
+
+// Owner sets the optional parameter "owner":
+func (c *IssuesListCall) Owner(owner string) *IssuesListCall {
+	c.urlParams_.Set("owner", owner)
+	return c
+}
+
+// PublishedMax sets the optional parameter "publishedMax":
+func (c *IssuesListCall) PublishedMax(publishedMax int64) *IssuesListCall {
+	c.urlParams_.Set("publishedMax", fmt.Sprint(publishedMax))
+	return c
+}
+
+// PublishedMin sets the optional parameter "publishedMin":
+func (c *IssuesListCall) PublishedMin(publishedMin int64) *IssuesListCall {
+	c.urlParams_.Set("publishedMin", fmt.Sprint(publishedMin))
+	return c
+}
+
+// Q sets the optional parameter "q":
+func (c *IssuesListCall) Q(q string) *IssuesListCall {
+	c.urlParams_.Set("q", q)
+	return c
+}
+
+// Sort sets the optional parameter "sort":
+func (c *IssuesListCall) Sort(sort string) *IssuesListCall {
+	c.urlParams_.Set("sort", sort)
+	return c
+}
+
+// StartIndex sets the optional parameter "startIndex":
+func (c *IssuesListCall) StartIndex(startIndex int64) *IssuesListCall {
+	c.urlParams_.Set("startIndex", fmt.Sprint(startIndex))
+	return c
+}
+
+// Status sets the optional parameter "status":
+func (c *IssuesListCall) Status(status string) *IssuesListCall {
+	c.urlParams_.Set("status", status)
+	return c
+}
+
+// UpdatedMax sets the optional parameter "updatedMax":
+func (c *IssuesListCall) UpdatedMax(updatedMax int64) *IssuesListCall {
+	c.urlParams_.Set("updatedMax", fmt.Sprint(updatedMax))
+	return c
+}
+
+// UpdatedMin sets the optional parameter "updatedMin":
+func (c *IssuesListCall) UpdatedMin(updatedMin int64) *IssuesListCall {
+	c.urlParams_.Set("updatedMin", fmt.Sprint(updatedMin))
+	return c
+}
+
+// Fields allows partial responses to be retrieved. See
+// https://developers.google.com/gdata/docs/2.0/basics#PartialResponse
+// for more information.
+func (c *IssuesListCall) Fields(s ...googleapi.Field) *IssuesListCall {
+	c.urlParams_.Set("fields", googleapi.CombineFields(s))
+	return c
+}
+
+// IfNoneMatch sets the optional parameter which makes the operation
+// fail if the object's ETag matches the given value. This is useful for
+// getting updates only after the object has changed since the last
+// request. Use googleapi.IsNotModified to check whether the response
+// error from Do is the result of In-None-Match.
+func (c *IssuesListCall) IfNoneMatch(entityTag string) *IssuesListCall {
+	c.ifNoneMatch_ = entityTag
+	return c
+}
+
+// Context sets the context to be used in this call's Do method. Any
+// pending HTTP request will be aborted if the provided context is
+// canceled.
+func (c *IssuesListCall) Context(ctx context.Context) *IssuesListCall {
+	c.ctx_ = ctx
+	return c
+}
+
+// Header returns an http.Header that can be modified by the caller to
+// add HTTP headers to the request.
+func (c *IssuesListCall) Header() http.Header {
+	if c.header_ == nil {
+		c.header_ = make(http.Header)
+	}
+	return c.header_
+}
+
+func (c *IssuesListCall) doRequest(alt string) (*http.Response, error) {
+	reqHeaders := make(http.Header)
+	reqHeaders.Set("x-goog-api-client", "gl-go/1.12.7 gdcl/20190905")
+	for k, v := range c.header_ {
+		reqHeaders[k] = v
+	}
+	reqHeaders.Set("User-Agent", c.s.userAgent())
+	if c.ifNoneMatch_ != "" {
+		reqHeaders.Set("If-None-Match", c.ifNoneMatch_)
+	}
+	var body io.Reader = nil
+	c.urlParams_.Set("alt", alt)
+	c.urlParams_.Set("prettyPrint", "false")
+	urls := googleapi.ResolveRelative(c.s.BasePath, "projects/{projectId}/issues")
+	urls += "?" + c.urlParams_.Encode()
+	req, err := http.NewRequest("GET", urls, body)
+	if err != nil {
+		return nil, err
+	}
+	req.Header = reqHeaders
+	googleapi.Expand(req.URL, map[string]string{
+		"projectId": c.projectId,
+	})
+	return gensupport.SendRequest(c.ctx_, c.s.client, req)
+}
+
+// Do executes the "monorail.issues.list" call.
+// Exactly one of *ProtoApiPb2V1IssuesListResponse or error will be
+// non-nil. Any non-2xx status code is an error. Response headers are in
+// either *ProtoApiPb2V1IssuesListResponse.ServerResponse.Header or (if
+// a response was returned at all) in error.(*googleapi.Error).Header.
+// Use googleapi.IsNotModified to check whether the returned error was
+// because http.StatusNotModified was returned.
+func (c *IssuesListCall) Do(opts ...googleapi.CallOption) (*ProtoApiPb2V1IssuesListResponse, error) {
+	gensupport.SetOptions(c.urlParams_, opts...)
+	res, err := c.doRequest("json")
+	if res != nil && res.StatusCode == http.StatusNotModified {
+		if res.Body != nil {
+			res.Body.Close()
+		}
+		return nil, &googleapi.Error{
+			Code:   res.StatusCode,
+			Header: res.Header,
+		}
+	}
+	if err != nil {
+		return nil, err
+	}
+	defer googleapi.CloseBody(res)
+	if err := googleapi.CheckResponse(res); err != nil {
+		return nil, err
+	}
+	ret := &ProtoApiPb2V1IssuesListResponse{
+		ServerResponse: googleapi.ServerResponse{
+			Header:         res.Header,
+			HTTPStatusCode: res.StatusCode,
+		},
+	}
+	target := &ret
+	if err := gensupport.DecodeResponse(target, res); err != nil {
+		return nil, err
+	}
+	return ret, nil
+	// {
+	//   "description": "List issues for projects.",
+	//   "httpMethod": "GET",
+	//   "id": "monorail.issues.list",
+	//   "parameterOrder": [
+	//     "projectId"
+	//   ],
+	//   "parameters": {
+	//     "additionalProject": {
+	//       "location": "query",
+	//       "repeated": true,
+	//       "type": "string"
+	//     },
+	//     "can": {
+	//       "default": "all",
+	//       "enum": [
+	//         "all",
+	//         "new",
+	//         "open",
+	//         "owned",
+	//         "reported",
+	//         "starred",
+	//         "to_verify"
+	//       ],
+	//       "enumDescriptions": [
+	//         "",
+	//         "",
+	//         "",
+	//         "",
+	//         "",
+	//         "",
+	//         ""
+	//       ],
+	//       "location": "query",
+	//       "type": "string"
+	//     },
+	//     "label": {
+	//       "location": "query",
+	//       "type": "string"
+	//     },
+	//     "maxResults": {
+	//       "default": "100",
+	//       "format": "int32",
+	//       "location": "query",
+	//       "type": "integer"
+	//     },
+	//     "owner": {
+	//       "location": "query",
+	//       "type": "string"
+	//     },
+	//     "projectId": {
+	//       "location": "path",
+	//       "required": true,
+	//       "type": "string"
+	//     },
+	//     "publishedMax": {
+	//       "format": "int64",
+	//       "location": "query",
+	//       "type": "string"
+	//     },
+	//     "publishedMin": {
+	//       "format": "int64",
+	//       "location": "query",
+	//       "type": "string"
+	//     },
+	//     "q": {
+	//       "location": "query",
+	//       "type": "string"
+	//     },
+	//     "sort": {
+	//       "location": "query",
+	//       "type": "string"
+	//     },
+	//     "startIndex": {
+	//       "format": "int32",
+	//       "location": "query",
+	//       "type": "integer"
+	//     },
+	//     "status": {
+	//       "location": "query",
+	//       "type": "string"
+	//     },
+	//     "updatedMax": {
+	//       "format": "int64",
+	//       "location": "query",
+	//       "type": "string"
+	//     },
+	//     "updatedMin": {
+	//       "format": "int64",
+	//       "location": "query",
+	//       "type": "string"
+	//     }
+	//   },
+	//   "path": "projects/{projectId}/issues",
+	//   "response": {
+	//     "$ref": "ProtoApiPb2V1IssuesListResponse"
+	//   },
+	//   "scopes": [
+	//     "https://www.googleapis.com/auth/userinfo.email"
+	//   ]
+	// }
+
+}
+
+// method id "monorail.issues.comments.delete":
+
+type IssuesCommentsDeleteCall struct {
+	s          *Service
+	projectId  string
+	issueId    int64
+	commentId  int64
+	urlParams_ gensupport.URLParams
+	ctx_       context.Context
+	header_    http.Header
+}
+
+// Delete: Delete a comment.
+func (r *IssuesCommentsService) Delete(projectId string, issueId int64, commentId int64) *IssuesCommentsDeleteCall {
+	c := &IssuesCommentsDeleteCall{s: r.s, urlParams_: make(gensupport.URLParams)}
+	c.projectId = projectId
+	c.issueId = issueId
+	c.commentId = commentId
+	return c
+}
+
+// Fields allows partial responses to be retrieved. See
+// https://developers.google.com/gdata/docs/2.0/basics#PartialResponse
+// for more information.
+func (c *IssuesCommentsDeleteCall) Fields(s ...googleapi.Field) *IssuesCommentsDeleteCall {
+	c.urlParams_.Set("fields", googleapi.CombineFields(s))
+	return c
+}
+
+// Context sets the context to be used in this call's Do method. Any
+// pending HTTP request will be aborted if the provided context is
+// canceled.
+func (c *IssuesCommentsDeleteCall) Context(ctx context.Context) *IssuesCommentsDeleteCall {
+	c.ctx_ = ctx
+	return c
+}
+
+// Header returns an http.Header that can be modified by the caller to
+// add HTTP headers to the request.
+func (c *IssuesCommentsDeleteCall) Header() http.Header {
+	if c.header_ == nil {
+		c.header_ = make(http.Header)
+	}
+	return c.header_
+}
+
+func (c *IssuesCommentsDeleteCall) doRequest(alt string) (*http.Response, error) {
+	reqHeaders := make(http.Header)
+	reqHeaders.Set("x-goog-api-client", "gl-go/1.12.7 gdcl/20190905")
+	for k, v := range c.header_ {
+		reqHeaders[k] = v
+	}
+	reqHeaders.Set("User-Agent", c.s.userAgent())
+	var body io.Reader = nil
+	c.urlParams_.Set("alt", alt)
+	c.urlParams_.Set("prettyPrint", "false")
+	urls := googleapi.ResolveRelative(c.s.BasePath, "projects/{projectId}/issues/{issueId}/comments/{commentId}")
+	urls += "?" + c.urlParams_.Encode()
+	req, err := http.NewRequest("DELETE", urls, body)
+	if err != nil {
+		return nil, err
+	}
+	req.Header = reqHeaders
+	googleapi.Expand(req.URL, map[string]string{
+		"projectId": c.projectId,
+		"issueId":   strconv.FormatInt(c.issueId, 10),
+		"commentId": strconv.FormatInt(c.commentId, 10),
+	})
+	return gensupport.SendRequest(c.ctx_, c.s.client, req)
+}
+
+// Do executes the "monorail.issues.comments.delete" call.
+// Exactly one of *ProtoApiPb2V1IssuesCommentsDeleteResponse or error
+// will be non-nil. Any non-2xx status code is an error. Response
+// headers are in either
+// *ProtoApiPb2V1IssuesCommentsDeleteResponse.ServerResponse.Header or
+// (if a response was returned at all) in
+// error.(*googleapi.Error).Header. Use googleapi.IsNotModified to check
+// whether the returned error was because http.StatusNotModified was
+// returned.
+func (c *IssuesCommentsDeleteCall) Do(opts ...googleapi.CallOption) (*ProtoApiPb2V1IssuesCommentsDeleteResponse, error) {
+	gensupport.SetOptions(c.urlParams_, opts...)
+	res, err := c.doRequest("json")
+	if res != nil && res.StatusCode == http.StatusNotModified {
+		if res.Body != nil {
+			res.Body.Close()
+		}
+		return nil, &googleapi.Error{
+			Code:   res.StatusCode,
+			Header: res.Header,
+		}
+	}
+	if err != nil {
+		return nil, err
+	}
+	defer googleapi.CloseBody(res)
+	if err := googleapi.CheckResponse(res); err != nil {
+		return nil, err
+	}
+	ret := &ProtoApiPb2V1IssuesCommentsDeleteResponse{
+		ServerResponse: googleapi.ServerResponse{
+			Header:         res.Header,
+			HTTPStatusCode: res.StatusCode,
+		},
+	}
+	target := &ret
+	if err := gensupport.DecodeResponse(target, res); err != nil {
+		return nil, err
+	}
+	return ret, nil
+	// {
+	//   "description": "Delete a comment.",
+	//   "httpMethod": "DELETE",
+	//   "id": "monorail.issues.comments.delete",
+	//   "parameterOrder": [
+	//     "projectId",
+	//     "issueId",
+	//     "commentId"
+	//   ],
+	//   "parameters": {
+	//     "commentId": {
+	//       "format": "int32",
+	//       "location": "path",
+	//       "required": true,
+	//       "type": "integer"
+	//     },
+	//     "issueId": {
+	//       "format": "int32",
+	//       "location": "path",
+	//       "required": true,
+	//       "type": "integer"
+	//     },
+	//     "projectId": {
+	//       "location": "path",
+	//       "required": true,
+	//       "type": "string"
+	//     }
+	//   },
+	//   "path": "projects/{projectId}/issues/{issueId}/comments/{commentId}",
+	//   "response": {
+	//     "$ref": "ProtoApiPb2V1IssuesCommentsDeleteResponse"
+	//   },
+	//   "scopes": [
+	//     "https://www.googleapis.com/auth/userinfo.email"
+	//   ]
+	// }
+
+}
+
+// method id "monorail.issues.comments.insert":
+
+type IssuesCommentsInsertCall struct {
+	s                                *Service
+	projectId                        string
+	issueId                          int64
+	protoapipb2v1issuecommentwrapper *ProtoApiPb2V1IssueCommentWrapper
+	urlParams_                       gensupport.URLParams
+	ctx_                             context.Context
+	header_                          http.Header
+}
+
+// Insert: Add a comment.
+func (r *IssuesCommentsService) Insert(projectId string, issueId int64, protoapipb2v1issuecommentwrapper *ProtoApiPb2V1IssueCommentWrapper) *IssuesCommentsInsertCall {
+	c := &IssuesCommentsInsertCall{s: r.s, urlParams_: make(gensupport.URLParams)}
+	c.projectId = projectId
+	c.issueId = issueId
+	c.protoapipb2v1issuecommentwrapper = protoapipb2v1issuecommentwrapper
+	return c
+}
+
+// SendEmail sets the optional parameter "sendEmail":
+func (c *IssuesCommentsInsertCall) SendEmail(sendEmail bool) *IssuesCommentsInsertCall {
+	c.urlParams_.Set("sendEmail", fmt.Sprint(sendEmail))
+	return c
+}
+
+// Fields allows partial responses to be retrieved. See
+// https://developers.google.com/gdata/docs/2.0/basics#PartialResponse
+// for more information.
+func (c *IssuesCommentsInsertCall) Fields(s ...googleapi.Field) *IssuesCommentsInsertCall {
+	c.urlParams_.Set("fields", googleapi.CombineFields(s))
+	return c
+}
+
+// Context sets the context to be used in this call's Do method. Any
+// pending HTTP request will be aborted if the provided context is
+// canceled.
+func (c *IssuesCommentsInsertCall) Context(ctx context.Context) *IssuesCommentsInsertCall {
+	c.ctx_ = ctx
+	return c
+}
+
+// Header returns an http.Header that can be modified by the caller to
+// add HTTP headers to the request.
+func (c *IssuesCommentsInsertCall) Header() http.Header {
+	if c.header_ == nil {
+		c.header_ = make(http.Header)
+	}
+	return c.header_
+}
+
+func (c *IssuesCommentsInsertCall) doRequest(alt string) (*http.Response, error) {
+	reqHeaders := make(http.Header)
+	reqHeaders.Set("x-goog-api-client", "gl-go/1.12.7 gdcl/20190905")
+	for k, v := range c.header_ {
+		reqHeaders[k] = v
+	}
+	reqHeaders.Set("User-Agent", c.s.userAgent())
+	var body io.Reader = nil
+	body, err := googleapi.WithoutDataWrapper.JSONReader(c.protoapipb2v1issuecommentwrapper)
+	if err != nil {
+		return nil, err
+	}
+	reqHeaders.Set("Content-Type", "application/json")
+	c.urlParams_.Set("alt", alt)
+	c.urlParams_.Set("prettyPrint", "false")
+	urls := googleapi.ResolveRelative(c.s.BasePath, "projects/{projectId}/issues/{issueId}/comments")
+	urls += "?" + c.urlParams_.Encode()
+	req, err := http.NewRequest("POST", urls, body)
+	if err != nil {
+		return nil, err
+	}
+	req.Header = reqHeaders
+	googleapi.Expand(req.URL, map[string]string{
+		"projectId": c.projectId,
+		"issueId":   strconv.FormatInt(c.issueId, 10),
+	})
+	return gensupport.SendRequest(c.ctx_, c.s.client, req)
+}
+
+// Do executes the "monorail.issues.comments.insert" call.
+// Exactly one of *ProtoApiPb2V1IssuesCommentsInsertResponse or error
+// will be non-nil. Any non-2xx status code is an error. Response
+// headers are in either
+// *ProtoApiPb2V1IssuesCommentsInsertResponse.ServerResponse.Header or
+// (if a response was returned at all) in
+// error.(*googleapi.Error).Header. Use googleapi.IsNotModified to check
+// whether the returned error was because http.StatusNotModified was
+// returned.
+func (c *IssuesCommentsInsertCall) Do(opts ...googleapi.CallOption) (*ProtoApiPb2V1IssuesCommentsInsertResponse, error) {
+	gensupport.SetOptions(c.urlParams_, opts...)
+	res, err := c.doRequest("json")
+	if res != nil && res.StatusCode == http.StatusNotModified {
+		if res.Body != nil {
+			res.Body.Close()
+		}
+		return nil, &googleapi.Error{
+			Code:   res.StatusCode,
+			Header: res.Header,
+		}
+	}
+	if err != nil {
+		return nil, err
+	}
+	defer googleapi.CloseBody(res)
+	if err := googleapi.CheckResponse(res); err != nil {
+		return nil, err
+	}
+	ret := &ProtoApiPb2V1IssuesCommentsInsertResponse{
+		ServerResponse: googleapi.ServerResponse{
+			Header:         res.Header,
+			HTTPStatusCode: res.StatusCode,
+		},
+	}
+	target := &ret
+	if err := gensupport.DecodeResponse(target, res); err != nil {
+		return nil, err
+	}
+	return ret, nil
+	// {
+	//   "description": "Add a comment.",
+	//   "httpMethod": "POST",
+	//   "id": "monorail.issues.comments.insert",
+	//   "parameterOrder": [
+	//     "projectId",
+	//     "issueId"
+	//   ],
+	//   "parameters": {
+	//     "issueId": {
+	//       "format": "int32",
+	//       "location": "path",
+	//       "required": true,
+	//       "type": "integer"
+	//     },
+	//     "projectId": {
+	//       "location": "path",
+	//       "required": true,
+	//       "type": "string"
+	//     },
+	//     "sendEmail": {
+	//       "location": "query",
+	//       "type": "boolean"
+	//     }
+	//   },
+	//   "path": "projects/{projectId}/issues/{issueId}/comments",
+	//   "request": {
+	//     "$ref": "ProtoApiPb2V1IssueCommentWrapper",
+	//     "parameterName": "resource"
+	//   },
+	//   "response": {
+	//     "$ref": "ProtoApiPb2V1IssuesCommentsInsertResponse"
+	//   },
+	//   "scopes": [
+	//     "https://www.googleapis.com/auth/userinfo.email"
+	//   ]
+	// }
+
+}
+
+// method id "monorail.issues.comments.list":
+
+type IssuesCommentsListCall struct {
+	s            *Service
+	projectId    string
+	issueId      int64
+	urlParams_   gensupport.URLParams
+	ifNoneMatch_ string
+	ctx_         context.Context
+	header_      http.Header
+}
+
+// List: List all comments for an issue.
+func (r *IssuesCommentsService) List(projectId string, issueId int64) *IssuesCommentsListCall {
+	c := &IssuesCommentsListCall{s: r.s, urlParams_: make(gensupport.URLParams)}
+	c.projectId = projectId
+	c.issueId = issueId
+	return c
+}
+
+// MaxResults sets the optional parameter "maxResults":
+func (c *IssuesCommentsListCall) MaxResults(maxResults int64) *IssuesCommentsListCall {
+	c.urlParams_.Set("maxResults", fmt.Sprint(maxResults))
+	return c
+}
+
+// StartIndex sets the optional parameter "startIndex":
+func (c *IssuesCommentsListCall) StartIndex(startIndex int64) *IssuesCommentsListCall {
+	c.urlParams_.Set("startIndex", fmt.Sprint(startIndex))
+	return c
+}
+
+// Fields allows partial responses to be retrieved. See
+// https://developers.google.com/gdata/docs/2.0/basics#PartialResponse
+// for more information.
+func (c *IssuesCommentsListCall) Fields(s ...googleapi.Field) *IssuesCommentsListCall {
+	c.urlParams_.Set("fields", googleapi.CombineFields(s))
+	return c
+}
+
+// IfNoneMatch sets the optional parameter which makes the operation
+// fail if the object's ETag matches the given value. This is useful for
+// getting updates only after the object has changed since the last
+// request. Use googleapi.IsNotModified to check whether the response
+// error from Do is the result of In-None-Match.
+func (c *IssuesCommentsListCall) IfNoneMatch(entityTag string) *IssuesCommentsListCall {
+	c.ifNoneMatch_ = entityTag
+	return c
+}
+
+// Context sets the context to be used in this call's Do method. Any
+// pending HTTP request will be aborted if the provided context is
+// canceled.
+func (c *IssuesCommentsListCall) Context(ctx context.Context) *IssuesCommentsListCall {
+	c.ctx_ = ctx
+	return c
+}
+
+// Header returns an http.Header that can be modified by the caller to
+// add HTTP headers to the request.
+func (c *IssuesCommentsListCall) Header() http.Header {
+	if c.header_ == nil {
+		c.header_ = make(http.Header)
+	}
+	return c.header_
+}
+
+func (c *IssuesCommentsListCall) doRequest(alt string) (*http.Response, error) {
+	reqHeaders := make(http.Header)
+	reqHeaders.Set("x-goog-api-client", "gl-go/1.12.7 gdcl/20190905")
+	for k, v := range c.header_ {
+		reqHeaders[k] = v
+	}
+	reqHeaders.Set("User-Agent", c.s.userAgent())
+	if c.ifNoneMatch_ != "" {
+		reqHeaders.Set("If-None-Match", c.ifNoneMatch_)
+	}
+	var body io.Reader = nil
+	c.urlParams_.Set("alt", alt)
+	c.urlParams_.Set("prettyPrint", "false")
+	urls := googleapi.ResolveRelative(c.s.BasePath, "projects/{projectId}/issues/{issueId}/comments")
+	urls += "?" + c.urlParams_.Encode()
+	req, err := http.NewRequest("GET", urls, body)
+	if err != nil {
+		return nil, err
+	}
+	req.Header = reqHeaders
+	googleapi.Expand(req.URL, map[string]string{
+		"projectId": c.projectId,
+		"issueId":   strconv.FormatInt(c.issueId, 10),
+	})
+	return gensupport.SendRequest(c.ctx_, c.s.client, req)
+}
+
+// Do executes the "monorail.issues.comments.list" call.
+// Exactly one of *ProtoApiPb2V1IssuesCommentsListResponse or error will
+// be non-nil. Any non-2xx status code is an error. Response headers are
+// in either
+// *ProtoApiPb2V1IssuesCommentsListResponse.ServerResponse.Header or (if
+// a response was returned at all) in error.(*googleapi.Error).Header.
+// Use googleapi.IsNotModified to check whether the returned error was
+// because http.StatusNotModified was returned.
+func (c *IssuesCommentsListCall) Do(opts ...googleapi.CallOption) (*ProtoApiPb2V1IssuesCommentsListResponse, error) {
+	gensupport.SetOptions(c.urlParams_, opts...)
+	res, err := c.doRequest("json")
+	if res != nil && res.StatusCode == http.StatusNotModified {
+		if res.Body != nil {
+			res.Body.Close()
+		}
+		return nil, &googleapi.Error{
+			Code:   res.StatusCode,
+			Header: res.Header,
+		}
+	}
+	if err != nil {
+		return nil, err
+	}
+	defer googleapi.CloseBody(res)
+	if err := googleapi.CheckResponse(res); err != nil {
+		return nil, err
+	}
+	ret := &ProtoApiPb2V1IssuesCommentsListResponse{
+		ServerResponse: googleapi.ServerResponse{
+			Header:         res.Header,
+			HTTPStatusCode: res.StatusCode,
+		},
+	}
+	target := &ret
+	if err := gensupport.DecodeResponse(target, res); err != nil {
+		return nil, err
+	}
+	return ret, nil
+	// {
+	//   "description": "List all comments for an issue.",
+	//   "httpMethod": "GET",
+	//   "id": "monorail.issues.comments.list",
+	//   "parameterOrder": [
+	//     "projectId",
+	//     "issueId"
+	//   ],
+	//   "parameters": {
+	//     "issueId": {
+	//       "format": "int32",
+	//       "location": "path",
+	//       "required": true,
+	//       "type": "integer"
+	//     },
+	//     "maxResults": {
+	//       "default": "100",
+	//       "format": "int32",
+	//       "location": "query",
+	//       "type": "integer"
+	//     },
+	//     "projectId": {
+	//       "location": "path",
+	//       "required": true,
+	//       "type": "string"
+	//     },
+	//     "startIndex": {
+	//       "format": "int32",
+	//       "location": "query",
+	//       "type": "integer"
+	//     }
+	//   },
+	//   "path": "projects/{projectId}/issues/{issueId}/comments",
+	//   "response": {
+	//     "$ref": "ProtoApiPb2V1IssuesCommentsListResponse"
+	//   },
+	//   "scopes": [
+	//     "https://www.googleapis.com/auth/userinfo.email"
+	//   ]
+	// }
+
+}
+
+// method id "monorail.issues.comments.undelete":
+
+type IssuesCommentsUndeleteCall struct {
+	s          *Service
+	projectId  string
+	issueId    int64
+	commentId  int64
+	urlParams_ gensupport.URLParams
+	ctx_       context.Context
+	header_    http.Header
+}
+
+// Undelete: Restore a deleted comment.
+func (r *IssuesCommentsService) Undelete(projectId string, issueId int64, commentId int64) *IssuesCommentsUndeleteCall {
+	c := &IssuesCommentsUndeleteCall{s: r.s, urlParams_: make(gensupport.URLParams)}
+	c.projectId = projectId
+	c.issueId = issueId
+	c.commentId = commentId
+	return c
+}
+
+// Fields allows partial responses to be retrieved. See
+// https://developers.google.com/gdata/docs/2.0/basics#PartialResponse
+// for more information.
+func (c *IssuesCommentsUndeleteCall) Fields(s ...googleapi.Field) *IssuesCommentsUndeleteCall {
+	c.urlParams_.Set("fields", googleapi.CombineFields(s))
+	return c
+}
+
+// Context sets the context to be used in this call's Do method. Any
+// pending HTTP request will be aborted if the provided context is
+// canceled.
+func (c *IssuesCommentsUndeleteCall) Context(ctx context.Context) *IssuesCommentsUndeleteCall {
+	c.ctx_ = ctx
+	return c
+}
+
+// Header returns an http.Header that can be modified by the caller to
+// add HTTP headers to the request.
+func (c *IssuesCommentsUndeleteCall) Header() http.Header {
+	if c.header_ == nil {
+		c.header_ = make(http.Header)
+	}
+	return c.header_
+}
+
+func (c *IssuesCommentsUndeleteCall) doRequest(alt string) (*http.Response, error) {
+	reqHeaders := make(http.Header)
+	reqHeaders.Set("x-goog-api-client", "gl-go/1.12.7 gdcl/20190905")
+	for k, v := range c.header_ {
+		reqHeaders[k] = v
+	}
+	reqHeaders.Set("User-Agent", c.s.userAgent())
+	var body io.Reader = nil
+	c.urlParams_.Set("alt", alt)
+	c.urlParams_.Set("prettyPrint", "false")
+	urls := googleapi.ResolveRelative(c.s.BasePath, "projects/{projectId}/issues/{issueId}/comments/{commentId}")
+	urls += "?" + c.urlParams_.Encode()
+	req, err := http.NewRequest("POST", urls, body)
+	if err != nil {
+		return nil, err
+	}
+	req.Header = reqHeaders
+	googleapi.Expand(req.URL, map[string]string{
+		"projectId": c.projectId,
+		"issueId":   strconv.FormatInt(c.issueId, 10),
+		"commentId": strconv.FormatInt(c.commentId, 10),
+	})
+	return gensupport.SendRequest(c.ctx_, c.s.client, req)
+}
+
+// Do executes the "monorail.issues.comments.undelete" call.
+// Exactly one of *ProtoApiPb2V1IssuesCommentsDeleteResponse or error
+// will be non-nil. Any non-2xx status code is an error. Response
+// headers are in either
+// *ProtoApiPb2V1IssuesCommentsDeleteResponse.ServerResponse.Header or
+// (if a response was returned at all) in
+// error.(*googleapi.Error).Header. Use googleapi.IsNotModified to check
+// whether the returned error was because http.StatusNotModified was
+// returned.
+func (c *IssuesCommentsUndeleteCall) Do(opts ...googleapi.CallOption) (*ProtoApiPb2V1IssuesCommentsDeleteResponse, error) {
+	gensupport.SetOptions(c.urlParams_, opts...)
+	res, err := c.doRequest("json")
+	if res != nil && res.StatusCode == http.StatusNotModified {
+		if res.Body != nil {
+			res.Body.Close()
+		}
+		return nil, &googleapi.Error{
+			Code:   res.StatusCode,
+			Header: res.Header,
+		}
+	}
+	if err != nil {
+		return nil, err
+	}
+	defer googleapi.CloseBody(res)
+	if err := googleapi.CheckResponse(res); err != nil {
+		return nil, err
+	}
+	ret := &ProtoApiPb2V1IssuesCommentsDeleteResponse{
+		ServerResponse: googleapi.ServerResponse{
+			Header:         res.Header,
+			HTTPStatusCode: res.StatusCode,
+		},
+	}
+	target := &ret
+	if err := gensupport.DecodeResponse(target, res); err != nil {
+		return nil, err
+	}
+	return ret, nil
+	// {
+	//   "description": "Restore a deleted comment.",
+	//   "httpMethod": "POST",
+	//   "id": "monorail.issues.comments.undelete",
+	//   "parameterOrder": [
+	//     "projectId",
+	//     "issueId",
+	//     "commentId"
+	//   ],
+	//   "parameters": {
+	//     "commentId": {
+	//       "format": "int32",
+	//       "location": "path",
+	//       "required": true,
+	//       "type": "integer"
+	//     },
+	//     "issueId": {
+	//       "format": "int32",
+	//       "location": "path",
+	//       "required": true,
+	//       "type": "integer"
+	//     },
+	//     "projectId": {
+	//       "location": "path",
+	//       "required": true,
+	//       "type": "string"
+	//     }
+	//   },
+	//   "path": "projects/{projectId}/issues/{issueId}/comments/{commentId}",
+	//   "response": {
+	//     "$ref": "ProtoApiPb2V1IssuesCommentsDeleteResponse"
+	//   },
+	//   "scopes": [
+	//     "https://www.googleapis.com/auth/userinfo.email"
+	//   ]
+	// }
+
+}
+
+// method id "monorail.users.get":
+
+type UsersGetCall struct {
+	s            *Service
+	userId       string
+	urlParams_   gensupport.URLParams
+	ifNoneMatch_ string
+	ctx_         context.Context
+	header_      http.Header
+}
+
+// Get: Get a user.
+func (r *UsersService) Get(userId string) *UsersGetCall {
+	c := &UsersGetCall{s: r.s, urlParams_: make(gensupport.URLParams)}
+	c.userId = userId
+	return c
+}
+
+// OwnerProjectsOnly sets the optional parameter "ownerProjectsOnly":
+func (c *UsersGetCall) OwnerProjectsOnly(ownerProjectsOnly bool) *UsersGetCall {
+	c.urlParams_.Set("ownerProjectsOnly", fmt.Sprint(ownerProjectsOnly))
+	return c
+}
+
+// Fields allows partial responses to be retrieved. See
+// https://developers.google.com/gdata/docs/2.0/basics#PartialResponse
+// for more information.
+func (c *UsersGetCall) Fields(s ...googleapi.Field) *UsersGetCall {
+	c.urlParams_.Set("fields", googleapi.CombineFields(s))
+	return c
+}
+
+// IfNoneMatch sets the optional parameter which makes the operation
+// fail if the object's ETag matches the given value. This is useful for
+// getting updates only after the object has changed since the last
+// request. Use googleapi.IsNotModified to check whether the response
+// error from Do is the result of In-None-Match.
+func (c *UsersGetCall) IfNoneMatch(entityTag string) *UsersGetCall {
+	c.ifNoneMatch_ = entityTag
+	return c
+}
+
+// Context sets the context to be used in this call's Do method. Any
+// pending HTTP request will be aborted if the provided context is
+// canceled.
+func (c *UsersGetCall) Context(ctx context.Context) *UsersGetCall {
+	c.ctx_ = ctx
+	return c
+}
+
+// Header returns an http.Header that can be modified by the caller to
+// add HTTP headers to the request.
+func (c *UsersGetCall) Header() http.Header {
+	if c.header_ == nil {
+		c.header_ = make(http.Header)
+	}
+	return c.header_
+}
+
+func (c *UsersGetCall) doRequest(alt string) (*http.Response, error) {
+	reqHeaders := make(http.Header)
+	reqHeaders.Set("x-goog-api-client", "gl-go/1.12.7 gdcl/20190905")
+	for k, v := range c.header_ {
+		reqHeaders[k] = v
+	}
+	reqHeaders.Set("User-Agent", c.s.userAgent())
+	if c.ifNoneMatch_ != "" {
+		reqHeaders.Set("If-None-Match", c.ifNoneMatch_)
+	}
+	var body io.Reader = nil
+	c.urlParams_.Set("alt", alt)
+	c.urlParams_.Set("prettyPrint", "false")
+	urls := googleapi.ResolveRelative(c.s.BasePath, "users/{userId}")
+	urls += "?" + c.urlParams_.Encode()
+	req, err := http.NewRequest("GET", urls, body)
+	if err != nil {
+		return nil, err
+	}
+	req.Header = reqHeaders
+	googleapi.Expand(req.URL, map[string]string{
+		"userId": c.userId,
+	})
+	return gensupport.SendRequest(c.ctx_, c.s.client, req)
+}
+
+// Do executes the "monorail.users.get" call.
+// Exactly one of *ProtoApiPb2V1UsersGetResponse or error will be
+// non-nil. Any non-2xx status code is an error. Response headers are in
+// either *ProtoApiPb2V1UsersGetResponse.ServerResponse.Header or (if a
+// response was returned at all) in error.(*googleapi.Error).Header. Use
+// googleapi.IsNotModified to check whether the returned error was
+// because http.StatusNotModified was returned.
+func (c *UsersGetCall) Do(opts ...googleapi.CallOption) (*ProtoApiPb2V1UsersGetResponse, error) {
+	gensupport.SetOptions(c.urlParams_, opts...)
+	res, err := c.doRequest("json")
+	if res != nil && res.StatusCode == http.StatusNotModified {
+		if res.Body != nil {
+			res.Body.Close()
+		}
+		return nil, &googleapi.Error{
+			Code:   res.StatusCode,
+			Header: res.Header,
+		}
+	}
+	if err != nil {
+		return nil, err
+	}
+	defer googleapi.CloseBody(res)
+	if err := googleapi.CheckResponse(res); err != nil {
+		return nil, err
+	}
+	ret := &ProtoApiPb2V1UsersGetResponse{
+		ServerResponse: googleapi.ServerResponse{
+			Header:         res.Header,
+			HTTPStatusCode: res.StatusCode,
+		},
+	}
+	target := &ret
+	if err := gensupport.DecodeResponse(target, res); err != nil {
+		return nil, err
+	}
+	return ret, nil
+	// {
+	//   "description": "Get a user.",
+	//   "httpMethod": "GET",
+	//   "id": "monorail.users.get",
+	//   "parameterOrder": [
+	//     "userId"
+	//   ],
+	//   "parameters": {
+	//     "ownerProjectsOnly": {
+	//       "location": "query",
+	//       "type": "boolean"
+	//     },
+	//     "userId": {
+	//       "location": "path",
+	//       "required": true,
+	//       "type": "string"
+	//     }
+	//   },
+	//   "path": "users/{userId}",
+	//   "response": {
+	//     "$ref": "ProtoApiPb2V1UsersGetResponse"
+	//   },
+	//   "scopes": [
+	//     "https://www.googleapis.com/auth/userinfo.email"
+	//   ]
+	// }
+
+}
diff --git a/go/monorail/v1/rebuild.sh b/go/monorail/v1/rebuild.sh
new file mode 100755
index 0000000..fd6de88
--- /dev/null
+++ b/go/monorail/v1/rebuild.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+
+# Script to rebuild the monorail.go file.
+
+# Get an updated generator. Pin to v0.10.0 because https://github.com/googleapis/google-api-go-client/issues/416.
+go get -u google.golang.org/api/google-api-go-generator@v0.10.0
+
+# Retrieve the discovery document for the API.
+wget https://monorail-prod.appspot.com/_ah/api/discovery/v1/apis/monorail/v1/rest
+
+# Generate the Go file.
+google-api-go-generator \
+  -api_json_file=rest \
+  -api_pkg_base="go.skia.org/infra/go" \
+  -output="monorail.go"
+
+# Clean up the generated file.
+goimports -w .
+
+# Cleanup.
+rm rest
diff --git a/hashtag/Dockerfile b/hashtag/Dockerfile
new file mode 100644
index 0000000..36ad6c1
--- /dev/null
+++ b/hashtag/Dockerfile
@@ -0,0 +1,7 @@
+FROM gcr.io/skia-public/basealpine:3.8
+
+COPY . /
+
+USER skia
+
+ENTRYPOINT ["/usr/local/bin/hashtag"]
\ No newline at end of file
diff --git a/hashtag/Makefile b/hashtag/Makefile
new file mode 100644
index 0000000..33ff736
--- /dev/null
+++ b/hashtag/Makefile
@@ -0,0 +1,27 @@
+default: build package-lock.json
+	npx webpack --mode=development
+
+build:
+	go install ./go/hashtag
+
+release: package-lock.json
+	npx webpack --mode=production
+	CGO_ENABLED=0 GOOS=linux go install -a ./go/hashtag
+	./build_release
+
+push: release
+	pushk --cluster=skia-public hashtag
+
+watch: package-lock.json
+	npx webpack --mode=development --watch
+
+run: build
+	# To run locally download the whitelisted service account and point to it
+	# via the GOOGLE_APPLICATION_CREDENTIALS environment variable.
+	GOOGLE_APPLICATION_CREDENTIALS=${HOME}/.hashtag_creds hashtag --local --logtostderr
+
+test:
+	go test ./go/...
+
+package-lock.json: package.json
+	npm install
\ No newline at end of file
diff --git a/hashtag/README.md b/hashtag/README.md
new file mode 100644
index 0000000..0035aed
--- /dev/null
+++ b/hashtag/README.md
@@ -0,0 +1,10 @@
+# Project HashTag
+
+A system for gathering disparate data such as bugs, CLs, and Google Drive
+documents into a comprehensive view on project status.
+
+  * [Design Doc](http://go/skia-hashtag)
+
+## Configuration
+
+Configuration of the application is done via the `config.json` file.
\ No newline at end of file
diff --git a/hashtag/build_release b/hashtag/build_release
new file mode 100755
index 0000000..b9973d7
--- /dev/null
+++ b/hashtag/build_release
@@ -0,0 +1,17 @@
+#!/bin/bash
+APPNAME=hashtag
+
+set -x -e
+
+# Copy files into the right locations in ${ROOT}.
+copy_release_files()
+{
+INSTALL="install -D --verbose --backup=none"
+INSTALL_DIR="install -d --verbose --backup=none"
+${INSTALL} --mode=644 -T Dockerfile               ${ROOT}/Dockerfile
+${INSTALL} --mode=755 -T ${GOPATH}/bin/${APPNAME} ${ROOT}/usr/local/bin/${APPNAME}
+${INSTALL_DIR} --mode=755                         ${ROOT}/usr/local/share/${APPNAME}
+${INSTALL} --mode=644 ./dist/*                    ${ROOT}/usr/local/share/${APPNAME}
+}
+
+source ../bash/docker_build.sh
diff --git a/hashtag/config.json b/hashtag/config.json
new file mode 100644
index 0000000..792ba1b
--- /dev/null
+++ b/hashtag/config.json
@@ -0,0 +1,27 @@
+{
+    "sources": {
+        "codesearch": {
+            "prefix": "lang:^markdown$ AND (file:^skia/buildbot/ OR file:^src/third_party/skia/) AND package:^chromium$"
+        },
+        "gerrit": {
+            "url": "https://skia-review.googlesource.com",
+            "filters": [
+                "-owner:skia-autoroll@skia-public.iam.gserviceaccount.com",
+                "-owner:skia-lottie-ci-autoroll@skia-buildbots.google.com.iam.gserviceaccount.com"
+            ]
+        },
+        "monorail": {
+            "projectID": "skia",
+            "linkFormat": "https://bugs.skia.org/%d",
+            "sort": "-Id"
+        }
+    },
+    "hashtags": [
+        "Particles", "Skottie", "SkSL", "Metal", "Vulkan", "SkQP", "Hashtag", "Forklift"
+    ],
+    "auth": {
+        "client_id": "145247227042-9dc658m0dj9mbah94kn5i3nnejirvrgl.apps.googleusercontent.com",
+        "api_key": "AIzaSyB8YZ_bFpEVOyICD-pUutJ3mxm6c1to8q0"
+    },
+    "drive_id": "0AOGploz136NUUk9PVA"
+}
\ No newline at end of file
diff --git a/hashtag/create-hashtag-sa.sh b/hashtag/create-hashtag-sa.sh
new file mode 100755
index 0000000..79fbecc
--- /dev/null
+++ b/hashtag/create-hashtag-sa.sh
@@ -0,0 +1,29 @@
+#/bin/bash
+# Creates the service account for hashtag.
+
+set -e -x
+
+source ../kube/config.sh
+source ../bash/ramdisk.sh
+
+# New service account we will create.
+SA_NAME=skia-hashtag
+
+cd /tmp/ramdisk
+gcloud iam service-accounts create "${SA_NAME}" --display-name="Skia Hashtag Service Account"
+
+gcloud projects add-iam-policy-binding "${PROJECT_ID}" \
+  --member "serviceAccount:${SA_NAME}@${PROJECT_SUBDOMAIN}.iam.gserviceaccount.com" \
+  --role roles/datastore.user
+
+gcloud projects add-iam-policy-binding ${PROJECT_ID} \
+  --member serviceAccount:${SA_NAME}@${PROJECT_SUBDOMAIN}.iam.gserviceaccount.com \
+  --role roles/cloudtrace.agent
+
+gcloud beta iam service-accounts keys create ${SA_NAME}.json --iam-account="${SA_NAME}@${PROJECT_SUBDOMAIN}.iam.gserviceaccount.com"
+
+kubectl create secret generic "${SA_NAME}" --from-file=key.json=${SA_NAME}.json
+cd -
+
+# Should be added to Monorail whitelist:
+#    https://bugs.chromium.org/p/monorail/issues/detail?id=6529
\ No newline at end of file
diff --git a/hashtag/go/codesearchsource/codesearchsource.go b/hashtag/go/codesearchsource/codesearchsource.go
new file mode 100644
index 0000000..85929b6
--- /dev/null
+++ b/hashtag/go/codesearchsource/codesearchsource.go
@@ -0,0 +1,58 @@
+package codesearchsource
+
+import (
+	"context"
+
+	"github.com/spf13/viper"
+	"go.skia.org/infra/go/codesearch"
+	"go.skia.org/infra/go/httputils"
+	"go.skia.org/infra/go/sklog"
+	"go.skia.org/infra/hashtag/go/source"
+)
+
+// codesearchSource implements source.Source.
+type codesearchSource struct {
+	cs *codesearch.CodeSearch
+
+	// prefix is added to each query.
+	prefix string
+}
+
+// New returns a new Source.
+func New() (source.Source, error) {
+	c := httputils.DefaultClientConfig().With2xxOnly().Client()
+	cs := codesearch.New(c)
+	return &codesearchSource{
+		cs:     cs,
+		prefix: viper.GetString("sources.codesearch.prefix"),
+	}, nil
+}
+
+// See source.Source.
+func (cs *codesearchSource) ByHashtag(hashtag string) <-chan source.Artifact {
+	ret := make(chan source.Artifact)
+	go func() {
+		defer close(ret)
+		results, err := cs.cs.Query(context.Background(), cs.prefix+" "+hashtag, nil)
+		if err != nil {
+			sklog.Errorf("Failed to build code search: %s", err)
+			return
+		}
+		for _, r := range results.SearchResult {
+			ret <- source.Artifact{
+				Title: r.TopFile.File.Name,
+				URL:   cs.cs.URL(r),
+			}
+		}
+	}()
+
+	return ret
+
+}
+
+// See source.Source.
+func (cs *codesearchSource) ByUser(hashtag string) <-chan source.Artifact {
+	ret := make(chan source.Artifact)
+	close(ret)
+	return ret
+}
diff --git a/hashtag/go/gerritsource/gerritsource.go b/hashtag/go/gerritsource/gerritsource.go
new file mode 100644
index 0000000..4cf0b40
--- /dev/null
+++ b/hashtag/go/gerritsource/gerritsource.go
@@ -0,0 +1,79 @@
+package gerritsource
+
+import (
+	"context"
+	"strings"
+
+	"github.com/spf13/viper"
+	"go.skia.org/infra/go/gerrit"
+	"go.skia.org/infra/go/httputils"
+	"go.skia.org/infra/go/skerr"
+	"go.skia.org/infra/go/sklog"
+	"go.skia.org/infra/hashtag/go/source"
+)
+
+// gerritSource implements source.Source.
+type gerritSource struct {
+	g     *gerrit.Gerrit
+	terms []*gerrit.SearchTerm
+}
+
+// New returns a new Source.
+func New() (source.Source, error) {
+	c := httputils.DefaultClientConfig().With2xxOnly().Client()
+	g, err := gerrit.NewGerrit(viper.GetString("sources.gerrit.url"), "", c)
+	if err != nil {
+		return nil, skerr.Wrapf(err, "Failed to create gerrit instance")
+	}
+	terms := []*gerrit.SearchTerm{}
+	for _, filter := range viper.GetStringSlice("sources.gerrit.filters") {
+		parts := strings.SplitN(filter, ":", 2)
+		if len(parts) != 2 {
+			return nil, skerr.Fmt("All Gerrit filters must start with an operator, e.g. 'owner:'.")
+		}
+		terms = append(terms, &gerrit.SearchTerm{
+			Key:   parts[0],
+			Value: parts[1],
+		})
+	}
+	return &gerritSource{
+		g:     g,
+		terms: terms,
+	}, err
+}
+
+// See source.Source.
+func (g *gerritSource) ByHashtag(hashtag string) <-chan source.Artifact {
+	ret := make(chan source.Artifact)
+	go func() {
+		defer close(ret)
+		terms := []*gerrit.SearchTerm{
+			{
+				Key:   "message",
+				Value: hashtag,
+			},
+		}
+		terms = append(terms, g.terms...)
+		changes, err := g.g.Search(context.Background(), gerrit.MAX_GERRIT_LIMIT, terms...)
+		if err != nil {
+			sklog.Errorf("Failed to build Gerrit search: %s", err)
+			return
+		}
+		for _, c := range changes {
+			ret <- source.Artifact{
+				Title:        c.Subject,
+				URL:          g.g.Url(c.Issue),
+				LastModified: c.Updated,
+			}
+		}
+	}()
+
+	return ret
+}
+
+// See source.Source.
+func (g *gerritSource) ByUser(string) <-chan source.Artifact {
+	ret := make(chan source.Artifact)
+	close(ret)
+	return ret
+}
diff --git a/hashtag/go/hashtag/main.go b/hashtag/go/hashtag/main.go
new file mode 100644
index 0000000..ddf5af6
--- /dev/null
+++ b/hashtag/go/hashtag/main.go
@@ -0,0 +1,189 @@
+package main
+
+import (
+	"net/http"
+	"path/filepath"
+	"strings"
+	"sync"
+	"text/template"
+
+	"github.com/gorilla/mux"
+	"github.com/spf13/viper"
+	"github.com/unrolled/secure"
+	"go.skia.org/infra/go/allowed"
+	"go.skia.org/infra/go/baseapp"
+	"go.skia.org/infra/go/login"
+	"go.skia.org/infra/go/sklog"
+	"go.skia.org/infra/hashtag/go/codesearchsource"
+	"go.skia.org/infra/hashtag/go/gerritsource"
+	"go.skia.org/infra/hashtag/go/monorailsource"
+	"go.skia.org/infra/hashtag/go/source"
+)
+
+// sourceDescriptor describes a single source.Source.
+type sourceDescriptor struct {
+	displayName string
+	source      source.Source
+}
+
+// server implements baseapp.App.
+type server struct {
+	templates *template.Template
+	sources   []sourceDescriptor
+}
+
+func newServer() (baseapp.App, error) {
+	// Setup auth.
+	var allow allowed.Allow
+	if !*baseapp.Local {
+		allow = allowed.NewAllowedFromList([]string{"google.com"})
+	} else {
+		allow = allowed.NewAllowedFromList([]string{"fred@example.org", "barney@example.org", "wilma@example.org"})
+	}
+	login.SimpleInitWithAllow(*baseapp.Port, *baseapp.Local, nil, nil, allow)
+
+	viper.SetConfigName("config") // name of config file (without extension)
+	viper.AddConfigPath(*baseapp.ResourcesDir)
+	err := viper.ReadInConfig()
+	if err != nil {
+		return nil, err
+	}
+
+	// Create our Sources.
+	gs, err := gerritsource.New()
+	if err != nil {
+		return nil, err
+	}
+	ms, err := monorailsource.New()
+	if err != nil {
+		return nil, err
+	}
+	cs, err := codesearchsource.New()
+	if err != nil {
+		return nil, err
+	}
+
+	ret := &server{
+		sources: []sourceDescriptor{
+			{
+				displayName: "Documents",
+				source:      cs,
+			},
+			{
+				displayName: "Bugs",
+				source:      ms,
+			},
+			{
+				displayName: "CLs",
+				source:      gs,
+			},
+		},
+	}
+	ret.loadTemplates()
+
+	return ret, nil
+}
+
+func (srv *server) loadTemplates() {
+	srv.templates = template.Must(template.New("").Delims("{%", "%}").ParseFiles(
+		filepath.Join(*baseapp.ResourcesDir, "index.html"),
+	))
+}
+
+// result of a singe source search.
+type result struct {
+	DisplayName string
+	Artifacts   []source.Artifact
+}
+
+// TemplateContext is the context for the index.html template.
+type TemplateContext struct {
+	// Nonce is the CSP Nonce. Look in webpack.config.js for where the nonce
+	// templates are injected.
+	Nonce string
+
+	// IsSearch is true if we contain search results.
+	IsSearch bool
+
+	// HashTag is the search query made if IsSearch is true.
+	Hashtag string
+
+	// Hashtags is the list of "official" hashtags.
+	Hashtags []string
+
+	// Results of the search.
+	Results []result
+
+	// The Google Team Drive ID.
+	DriveID string
+
+	// OAuth 2.0 client id.
+	ClientID string
+
+	// JS API Key.
+	APIKey string
+}
+
+func (srv *server) indexHandler(w http.ResponseWriter, r *http.Request) {
+	w.Header().Set("Content-Type", "text/html")
+	if *baseapp.Local {
+		srv.loadTemplates()
+	}
+
+	templateContext := TemplateContext{
+		// Look in webpack.config.js for where the nonce templates are injected.
+		Nonce:    secure.CSPNonce(r.Context()),
+		Hashtags: viper.GetStringSlice("hashtags"),
+		DriveID:  viper.GetString("drive_id"),
+		ClientID: viper.GetString("auth.client_id"),
+		APIKey:   viper.GetString("auth.api_key"),
+	}
+
+	hashtag := strings.TrimSpace(r.FormValue("hashtag"))
+	if hashtag != "" {
+		templateContext.Hashtag = hashtag
+		templateContext.IsSearch = true
+		templateContext.Results = make([]result, len(srv.sources))
+
+		// Do searches in parallel.
+		var wg sync.WaitGroup
+		for i, s := range srv.sources {
+			wg.Add(1)
+			go func(i int, s sourceDescriptor) {
+				defer wg.Done()
+				results := []source.Artifact{}
+				for artifact := range s.source.ByHashtag(hashtag) {
+					results = append(results, artifact)
+				}
+				templateContext.Results[i] = result{
+					DisplayName: s.displayName,
+					Artifacts:   results,
+				}
+			}(i, s)
+		}
+		wg.Wait()
+	}
+
+	if err := srv.templates.ExecuteTemplate(w, "index.html", templateContext); err != nil {
+		sklog.Errorf("Failed to expand template: %s", err)
+	}
+}
+
+// See baseapp.App.
+func (srv *server) AddHandlers(r *mux.Router) {
+	r.HandleFunc("/", srv.indexHandler)
+	r.HandleFunc("/loginstatus/", login.StatusHandler).Methods("GET")
+}
+
+// See baseapp.App.
+func (srv *server) AddMiddleware() []mux.MiddlewareFunc {
+	ret := []mux.MiddlewareFunc{}
+	if !*baseapp.Local {
+		ret = append(ret, login.ForceAuthMiddleware(login.DEFAULT_REDIRECT_URL), login.RestrictViewer)
+	}
+	return ret
+}
+
+func main() {
+	baseapp.Serve(newServer, []string{"hashtag.skia.org"})
+}
diff --git a/hashtag/go/monorailsource/monorailsource.go b/hashtag/go/monorailsource/monorailsource.go
new file mode 100644
index 0000000..14166d2
--- /dev/null
+++ b/hashtag/go/monorailsource/monorailsource.go
@@ -0,0 +1,72 @@
+package monorailsource
+
+import (
+	"context"
+	"fmt"
+	"time"
+
+	"github.com/spf13/viper"
+	"go.skia.org/infra/go/monorail/v1"
+	"go.skia.org/infra/go/skerr"
+	"go.skia.org/infra/go/sklog"
+	"go.skia.org/infra/hashtag/go/source"
+)
+
+const monorailTimeFormat = "2006-01-02T15:04:05"
+
+// monorailSource implements source.Source.
+type monorailSource struct {
+	m          *monorail.Service
+	projectID  string
+	linkFormat string
+	sort       string
+}
+
+// New returns a new Source.
+func New() (source.Source, error) {
+	m, err := monorail.NewService(context.Background())
+	if err != nil {
+		return nil, skerr.Wrapf(err, "Unable to create monorail service.")
+	}
+
+	return &monorailSource{
+		m:          m,
+		projectID:  viper.GetString("sources.monorail.projectID"),
+		linkFormat: viper.GetString("sources.monorail.linkFormat"),
+		sort:       viper.GetString("sources.monorail.sort"),
+	}, nil
+}
+
+// See source.Source.
+func (m *monorailSource) ByHashtag(hashtag string) <-chan source.Artifact {
+	ret := make(chan source.Artifact)
+	go func() {
+		defer close(ret)
+		matchingIssues, err := m.m.Issues.List(m.projectID).Q(hashtag).Sort(m.sort).Do()
+		if err != nil {
+			sklog.Errorf("Failed to build Monorail search: %s", err)
+			return
+		}
+		for _, issue := range matchingIssues.Items {
+			ts, err := time.Parse(monorailTimeFormat, issue.StatusModified)
+			if err != nil {
+				sklog.Errorf("Can't parse %q at time: %s", issue.StatusModified, err)
+				ts = time.Now()
+			}
+			ret <- source.Artifact{
+				Title:        issue.Title,
+				URL:          fmt.Sprintf(m.linkFormat, issue.Id),
+				LastModified: ts,
+			}
+		}
+	}()
+
+	return ret
+}
+
+// See source.Source.
+func (m *monorailSource) ByUser(string) <-chan source.Artifact {
+	ret := make(chan source.Artifact)
+	close(ret)
+	return ret
+}
diff --git a/hashtag/go/source/source.go b/hashtag/go/source/source.go
new file mode 100644
index 0000000..8cbc84c
--- /dev/null
+++ b/hashtag/go/source/source.go
@@ -0,0 +1,23 @@
+package source
+
+import "time"
+
+// Artifact is a single item we found during a search.
+type Artifact struct {
+	Title        string    `json:"title"`
+	URL          string    `json:"url"`
+	Hashtags     []string  `json:"hashtags"`
+	LastModified time.Time `json:"last_modified"`
+}
+
+// Source is the interface each source of data must implement, i.e. Gerrit,
+// Monorail, Drive, etc.
+type Source interface {
+	// ByHashtag returns a channel that produces Artifacts that match the given
+	// hashtag.
+	ByHashtag(hashtag string) <-chan Artifact
+
+	// ByUSer returns a channel that produces Artifacts that match the given
+	// email.
+	ByUser(email string) <-chan Artifact
+}
diff --git a/hashtag/modules/team-drive-sk/index.js b/hashtag/modules/team-drive-sk/index.js
new file mode 100644
index 0000000..b19dfbd
--- /dev/null
+++ b/hashtag/modules/team-drive-sk/index.js
@@ -0,0 +1,2 @@
+import './team-drive-sk.js'
+import './team-drive-sk.scss'
\ No newline at end of file
diff --git a/hashtag/modules/team-drive-sk/team-drive-sk.js b/hashtag/modules/team-drive-sk/team-drive-sk.js
new file mode 100644
index 0000000..6b2bda0
--- /dev/null
+++ b/hashtag/modules/team-drive-sk/team-drive-sk.js
@@ -0,0 +1,134 @@
+/**
+ * @module modules/team-drive-sk
+ * @description <h2><code>team-drive-sk</code></h2>
+ *
+ * Lists all the files that match the query in the team drive.
+ *
+ * @attr query - The hashtag to search for.
+ *
+ * @attr client_id - The OAuth 2.0 Client ID.
+ *
+ * @attr api_key - The API Key.
+ *
+ * @attr drive_id - The Google Teams Drive ID.
+ *
+ */
+import { html, render } from 'lit-html'
+import { ElementSk } from '../../../infra-sk/modules/ElementSk'
+import { errorMessage } from 'elements-sk/errorMessage'
+import 'elements-sk/spinner-sk'
+import 'elements-sk/error-toast-sk'
+import 'elements-sk/styles/buttons'
+
+// Full list of mime-types: https://developers.google.com/drive/api/v3/mime-types
+const mimeTypesToPath = {
+    'application/vnd.google-apps.presentation': 'presentation',
+    'application/vnd.google-apps.spreadsheet': 'spreadsheets',
+    'application/vnd.google-apps.document': 'document',
+    'application/vnd.google-apps.drawing': 'drawings',
+};
+
+function linkFromFile(file) {
+    return `https://docs.google.com/${mimeTypesToPath[file.mimeType]}/d/${file.id}`
+}
+
+const template = (ele) => html`
+  ${ele._isSignedIn() ? html`` : html`<button @click=${() => ele._signIn()}>Authorize</button>`}
+  <spinner-sk ?active=${ele._loading}></spinner-sk>
+  <ul>
+    ${(!ele._loading && ele._files.length === 0) ? html`<p>None found.</p>` : html``}
+    ${ele._files.map((file) => html`<li><a href='${linkFromFile(file)}'>${file.name}</a></li>`)}
+  </ul>
+`;
+
+// Array of API discovery doc URLs for APIs used by the quickstart
+const DISCOVERY_DOCS = ["https://www.googleapis.com/discovery/v1/apis/drive/v3/rest"];
+
+// Authorization scopes required by the API; multiple scopes can be included,
+// separated by spaces.
+const SCOPES = 'https://www.googleapis.com/auth/drive.readonly';
+
+class TeamDriveSk extends ElementSk {
+    constructor() {
+        super(template);
+        this._files = [];
+        this._loading = true;
+    }
+
+    connectedCallback() {
+        super.connectedCallback();
+        this._render();
+        gapi.load('client:auth2', () => this._initClient());
+    }
+
+    _initClient() {
+        gapi.client.init({
+            // Client ID and API key from the Developer Console
+            apiKey: this.getAttribute("api_key"),
+            clientId:  this.getAttribute("client_id"),
+            discoveryDocs: DISCOVERY_DOCS,
+            scope: SCOPES
+        }).then(() => {
+            // Listen for sign-in state changes.
+            gapi.auth2.getAuthInstance().isSignedIn.listen(() => this._listFiles());
+
+            // Handle the initial sign-in state.
+            this._listFiles();
+        }).catch(errorMessage);
+    }
+
+    _signIn() {
+        gapi.auth2.getAuthInstance().signIn();
+    }
+
+    _isSignedIn() {
+        // Protect against being called before the gapi library is loaded.
+        // Default to true which will hide the login button and this avoids a
+        // flash of the button if the lib is slow to load but the user is logged
+        // in.
+        if (!gapi.auth2) {
+            return true;
+        }
+        return gapi.auth2.getAuthInstance().isSignedIn.get();
+    }
+
+    _listFiles() {
+        if (this._isSignedIn()) {
+            this._files = [];
+
+            // TODO(jcgregorio): iterate the pages of the response.
+            //'fields': "nextPageToken, files(id, name)"
+            gapi.client.drive.files.list({
+                'pageSize': 30,
+                'corpora': 'drive',
+                // The Google Team Drive ID.
+                'driveId': this.getAttribute("drive_id"),
+                'includeItemsFromAllDrives': true,
+                'q': `fullText contains \"${this.getAttribute('query')}\"`,
+                'supportsAllDrives': true,
+            }).then((response) => {
+                var files = response.result.files;
+                if (files && files.length > 0) {
+                    this._files = files;
+                } else {
+                    // None found.
+                }
+                this._loading = false;
+                this._render();
+            }).catch(errorMessage);
+        } else {
+            this._render();
+        }
+    }
+}
+
+// This element requires the Google JS API Client to be loaded, so don't
+// register the element until the library is loaded.
+if (window.gapi) {
+    window.customElements.define('team-drive-sk', TeamDriveSk);
+} else {
+    // Look in index.html for the code that generates the event.
+    document.addEventListener('gapi-loaded', () => {
+        window.customElements.define('team-drive-sk', TeamDriveSk);
+    });
+}
diff --git a/hashtag/modules/team-drive-sk/team-drive-sk.scss b/hashtag/modules/team-drive-sk/team-drive-sk.scss
new file mode 100644
index 0000000..37f744e
--- /dev/null
+++ b/hashtag/modules/team-drive-sk/team-drive-sk.scss
@@ -0,0 +1,5 @@
+spinner-sk {
+    width: 16px;
+    height: 16px;
+    margin: 1rem;
+}
\ No newline at end of file
diff --git a/hashtag/package.json b/hashtag/package.json
new file mode 100644
index 0000000..c5ae144
--- /dev/null
+++ b/hashtag/package.json
@@ -0,0 +1,32 @@
+{
+  "name": "hashtag",
+  "version": "1.0.0",
+  "description": "",
+  "main": "index.js",
+  "scripts": {
+    "test": "echo \"Error: no test specified\" && exit 1"
+  },
+  "author": "",
+  "license": "SEE LICENSE IN LICENSE",
+  "devDependencies": {
+    "chai": "~4.2.0",
+    "copy-webpack-plugin": "~5.0.4",
+    "elements-sk": "^3.0.1",
+    "html-webpack-inject-attributes-plugin": "^1.0.1",
+    "html-webpack-plugin": "^3.2.0",
+    "karma": "~4.4.0",
+    "karma-chai": "~0.1.0",
+    "karma-chrome-launcher": "~3.1.0",
+    "karma-firefox-launcher": "~1.2.0",
+    "karma-mocha": "~1.3.0",
+    "karma-sinon": "~1.0.5",
+    "karma-webpack": "~4.0.2",
+    "lit-html": "^1.1.2",
+    "mocha": "~6.2.1",
+    "pulito": "~4.4.0",
+    "sinon": "~7.5.0",
+    "webpack": "~4.41.2",
+    "webpack-cli": "~3.3.9",
+    "webpack-dev-server": "~3.8.2"
+  }
+}
diff --git a/hashtag/pages/index.html b/hashtag/pages/index.html
new file mode 100644
index 0000000..4e7c67f
--- /dev/null
+++ b/hashtag/pages/index.html
@@ -0,0 +1,75 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+  <title>Hashtag</title>
+  <meta charset="utf-8" />
+  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <script type="text/javascript" charset="utf-8" nonce='{%.Nonce%}'>
+    // Create a callback function that sends an event when gapi is loaded.
+    function sendGapiEvent() {
+      document.dispatchEvent(new CustomEvent('gapi-loaded'));
+    }
+  </script>
+  <script src="https://apis.google.com/js/api.js?onload=sendGapiEvent" async defer nonce='{% .Nonce %}'></script>
+</head>
+
+<body>
+  {% if not .IsSearch %}
+
+  <header>Hashtag
+    <form action="" method="get" accept-charset="utf-8">
+      <input type="text" name="hashtag" value="" placeholder="search">
+    </form>
+  </header>
+  <main>
+    <section>
+      <h2>Hashtags</h2>
+      <ul>
+        {% range .Hashtags %}
+        <li><a href='?hashtag={%.%}'>{%.%}</a></li>
+        {% end %}
+      </ul>
+    </section>
+  </main>
+
+  {% else %}
+
+  <header><a href='/'>Home</a>
+    <form action="" method="get" accept-charset="utf-8">
+      <input type="text" name="hashtag" value="{% .Hashtag %}" placeholder="search">
+    </form>
+  </header>
+  <main>
+    <section>
+      <h2>Drive</h2>
+      <team-drive-sk
+        query="{% .Hashtag %}"
+        client_id="{% .ClientID %}"
+        api_key="{% .APIKey %}"
+        drive_id="{% .DriveID %}">
+      </team-drive-sk>
+    </section>
+
+    {% range .Results %}
+    <section>
+      <h2>{% .DisplayName %}</h2>
+      <ul>
+        {% range .Artifacts %}
+        <li><a href='{% .URL %}'>{% .Title %}</a></li>
+        {% else %}
+        <p>None found.</p>
+        {% end %}
+      </ul>
+    </section>
+    {% end %}
+
+  </main>
+
+  {% end %}
+
+  <error-toast-sk></error-toast-sk>
+</body>
+
+</html>
\ No newline at end of file
diff --git a/hashtag/pages/index.js b/hashtag/pages/index.js
new file mode 100644
index 0000000..a0707ea
--- /dev/null
+++ b/hashtag/pages/index.js
@@ -0,0 +1,2 @@
+import './index.scss'
+import '../modules/team-drive-sk'
\ No newline at end of file
diff --git a/hashtag/pages/index.scss b/hashtag/pages/index.scss
new file mode 100644
index 0000000..f5bfab8
--- /dev/null
+++ b/hashtag/pages/index.scss
@@ -0,0 +1,61 @@
+
+@import '~elements-sk/colors.css';
+
+html {
+  --dark-gray: rgb(49, 49, 49);
+  --gray-text: rgb(204, 204, 204);
+  --border: rgb(120, 120, 120)
+}
+
+section {
+  margin: 1rem;
+}
+
+section li {
+  line-height: 1.4rem;
+  max-width: 30rem;
+  overflow-wrap: break-word;
+}
+
+section p {
+  font-style: italic;
+}
+
+section h2 {
+  color: var(--dark-white);
+}
+
+header,
+header a {
+  padding: 0.4rem;
+  color: var(--light-gray);
+  background: var(--gray);
+  font-size: 1.2rem;
+}
+
+header {
+  border-bottom: solid thin var(--border);
+}
+
+body {
+  background: var(--dark-gray);
+  color: var(--gray-text);
+  font-family: helvetica, 'helvetica neue', ubuntu, roboto, noto, 'segoe ui', arial, sans-serif;
+  margin: 0;
+  padding: 0;
+}
+
+a {
+  color: var(--gray-text);
+  text-decoration: underline;
+}
+
+form {
+  display: inline-block;
+}
+
+main {
+  display: flex;
+  flex-direction: row;
+  flex-wrap: wrap;
+}
\ No newline at end of file
diff --git a/hashtag/webpack.config.js b/hashtag/webpack.config.js
new file mode 100644
index 0000000..394891c
--- /dev/null
+++ b/hashtag/webpack.config.js
@@ -0,0 +1,26 @@
+const commonBuilder = require('pulito');
+const CopyWebpackPlugin = require('copy-webpack-plugin');
+const HtmlWebpackInjectAttributesPlugin = require('html-webpack-inject-attributes-plugin');
+const { resolve } = require('path')
+
+module.exports = (env, argv) => {
+  let config = commonBuilder(env, argv, __dirname);
+  config.output.publicPath='/static/';
+
+  config.plugins.push(
+    new CopyWebpackPlugin([
+      {
+        from: './config.json',
+        to: 'config.json'
+      },
+    ])
+  );
+  config.plugins.push(
+    new HtmlWebpackInjectAttributesPlugin({
+      nonce: "{% .Nonce %}",
+    }),
+  );
+  config.resolve = config.resolve || {};
+  config.resolve.modules = [resolve(__dirname, 'node_modules')];
+  return config;
+}
diff --git a/licenses/LICENSES.md b/licenses/LICENSES.md
index a8a3ab0..edb641a 100644
--- a/licenses/LICENSES.md
+++ b/licenses/LICENSES.md
@@ -21,6 +21,7 @@
 | github.com/davecgh/go-spew/spew     | ISC              |
 | github.com/fiorix/go-web/autogzip   | BSD-3-Clause     |
 | github.com/flynn/json5              | BSD-3-Clause/MIT |
+| github.com/fsnotify/fsnotify        | BSD-3-Clause     |
 | github.com/godbus/dbus              | BSD-2-Clause     |
 | github.com/golang/                  | Apache 2.0       |
 | github.com/golang/mock              | Apache 2.0       |
@@ -43,9 +44,12 @@
 | github.com/Masterminds/goutils      | Apache 2.0       |
 | github.com/Masterminds/semver       | MIT              |
 | github.com/Masterminds/sprig        | MIT              |
+| github.com/magiconair/properties    | BSD-2-Clause     |
 | github.com/mattheath/base62         | MIT              |
 | github.com/matttproud/golang\_protobuf\_extensions | Apache 2.0 |
+| github.com/mitchellh/mapstructure   | MIT              |
 | github.com/nfnt/resize              | ISC              |
+| github.com/pelletier/go-toml        | MIT              |
 | github.com/pkg/errors               | BSD-2-Clause     |
 | github.com/pmezard/go-difflib       | BSD-3-Clause     |
 | github.com/prometheus/              | Apache 2.0       |
@@ -54,6 +58,11 @@
 | github.com/skia-dev/go-systemd/dbus | Apache 2.0       |
 | github.com/spf13/cobra              | Apache 2.0       |
 | github.com/spf13/pflag              | BSD-3-Clause     |
+| github.com/spf13/afero              | Apache 2.0       |
+| github.com/spf13/afero/mem          | Apache 2.0       |
+| github.com/spf13/cast               | MIT              |
+| github.com/spf13/jwalterweatherman  | MIT              |
+| github.com/spf13/viper              | MIT              |
 | github.com/stretchr/testify         | MIT              |
 | github.com/syndtr/goleveldb         | BSD-2-Clause     |
 | github.com/texttheater/golang-levenshtein | MIT |
diff --git a/licenses/all_deps.txt b/licenses/all_deps.txt
index a1ae5bc..ab84490 100644
--- a/licenses/all_deps.txt
+++ b/licenses/all_deps.txt
@@ -43,6 +43,7 @@
 github.com/davecgh/go-spew/spew
 github.com/fiorix/go-web/autogzip
 github.com/flynn/json5
+github.com/fsnotify/fsnotify
 github.com/godbus/dbus
 github.com/gogo/protobuf/proto
 github.com/gogo/protobuf/sortkeys
@@ -77,10 +78,6 @@
 github.com/gorilla/csrf
 github.com/gorilla/mux
 github.com/gorilla/securecookie
-github.com/hashicorp/errwrap
-github.com/hashicorp/golang-lru
-github.com/hashicorp/golang-lru/simplelru
-github.com/hashicorp/go-multierror
 github.com/huandu/xstrings
 github.com/huin/goserial
 github.com/imdario/mergo
@@ -90,23 +87,31 @@
 github.com/jmespath/go-jmespath
 github.com/json-iterator/go
 github.com/julienschmidt/httprouter
+github.com/magiconair/properties
 github.com/Masterminds/goutils
 github.com/Masterminds/semver
 github.com/Masterminds/sprig
 github.com/matttproud/golang_protobuf_extensions/pbutil
 github.com/mitchellh/copystructure
+github.com/mitchellh/mapstructure
 github.com/mitchellh/reflectwalk
 github.com/modern-go/concurrent
 github.com/modern-go/reflect2
 github.com/nfnt/resize
 github.com/patrickmn/go-cache
+github.com/pelletier/go-toml
 github.com/pkg/errors
 github.com/pmezard/go-difflib/difflib
 github.com/russross/blackfriday/v2
 github.com/shurcooL/sanitized_anchor_name
 github.com/skia-dev/go-systemd/dbus
+github.com/spf13/afero
+github.com/spf13/afero/mem
+github.com/spf13/cast
 github.com/spf13/cobra
+github.com/spf13/jwalterweatherman
 github.com/spf13/pflag
+github.com/spf13/viper
 github.com/stretchr/objx
 github.com/stretchr/testify/assert
 github.com/stretchr/testify/mock
diff --git a/licenses/check.sh b/licenses/check.sh
index 8c85f18..0b944b4 100755
--- a/licenses/check.sh
+++ b/licenses/check.sh
@@ -3,7 +3,7 @@
 # Build the list of all recursive dependent packages, excluding the ones we
 # know are going to licensed correctly.
 DEPS=`go list -f '{{join .Deps "\n"}}' ../... | sort | uniq | grep "[a-zA-Z0-9]\+\." | \
-  egrep -v \(cloud.google.com\|github.com/prometheus\|go.chromium.org\|go.chromium.org\|golang.org\|google.golang.org\|go.opencensus.io\|go.skia.org\|k8s.io\)`
+  egrep -v \(cloud.google.com\|github.com/prometheus\|go.chromium.org\|go.chromium.org\|golang.org\|google.golang.org\|go.opencensus.io\|go.skia.org\|k8s.io\|github.com/hashicorp\)`
 
 if [ "$1" = "regenerate" ]; then
   echo "$DEPS" > all_deps.txt