generator: support required parameters with no parameterOrder key.
If a method has required parameters but no "parameterOrder" key, collect
the required params and make them args.
Since the JSON data is a map, the order of the parameters may change
from run to run, so sort them by name.
Change-Id: I2ce901b115e1821cbdf25b7cfc34b0445e7953cc
Reviewed-on: https://code-review.googlesource.com/32210
Reviewed-by: Jonathan Amsterdam <jba@google.com>
diff --git a/google-api-go-generator/gen.go b/google-api-go-generator/gen.go
index bc07142..48a8d71 100644
--- a/google-api-go-generator/gen.go
+++ b/google-api-go-generator/gen.go
@@ -1060,7 +1060,7 @@
case disco.StructKind:
addSubStruct(subApiName, p.Type())
default:
- panicf("Unknown type for %s: %v", subApiName, p.Type())
+ panicf("Unknown type for %q: %v", subApiName, p.Type())
}
}
case disco.ArrayKind:
@@ -1081,7 +1081,7 @@
case disco.StructKind:
addSubStruct(subApiName, at)
default:
- panicf("Unknown array type for %s: %v", subApiName, at)
+ panicf("Unknown array type for %q: %v", subApiName, at)
}
case disco.AnyStructKind, disco.MapKind, disco.SimpleKind, disco.ReferenceKind:
// Do nothing.
@@ -1440,7 +1440,7 @@
r *disco.Resource // or nil if a API-level (top-level) method
m *disco.Method
- params []*Param // all Params, of each type, lazily set by first access to Parameters
+ params []*Param // all Params, of each type, lazily set by first call of Params method.
}
func (m *Method) Id() string {
@@ -2090,22 +2090,27 @@
return u.String()
}
-func (meth *Method) NewArguments() (args *arguments) {
- args = &arguments{
+func (meth *Method) NewArguments() *arguments {
+ args := &arguments{
method: meth,
m: make(map[string]*argument),
}
- po := meth.m.ParameterOrder
- if len(po) > 0 {
- for _, pname := range po {
- arg := meth.NewArg(pname, meth.NamedParam(pname))
- args.AddArg(arg)
+ pnames := meth.m.ParameterOrder
+ if len(pnames) == 0 {
+ // No parameterOrder; collect required parameters and sort by name.
+ for _, reqParam := range meth.grepParams(func(p *Param) bool { return p.p.Required }) {
+ pnames = append(pnames, reqParam.p.Name)
}
+ sort.Strings(pnames)
+ }
+ for _, pname := range pnames {
+ arg := meth.NewArg(pname, meth.NamedParam(pname))
+ args.AddArg(arg)
}
if rs := meth.m.Request; rs != nil {
args.AddArg(meth.NewBodyArg(rs))
}
- return
+ return args
}
func (meth *Method) NewBodyArg(ds *disco.Schema) *argument {
diff --git a/google-api-go-generator/gen_test.go b/google-api-go-generator/gen_test.go
index b6f3850..3349650 100644
--- a/google-api-go-generator/gen_test.go
+++ b/google-api-go-generator/gen_test.go
@@ -43,6 +43,7 @@
"param-rename",
"quotednum",
"repeated",
+ "required-query",
"resource-named-service", // appengine/v1/appengine-api.json
"unfortunatedefaults",
"variants",
diff --git a/google-api-go-generator/testdata/required-query.json b/google-api-go-generator/testdata/required-query.json
new file mode 100644
index 0000000..537a4f5
--- /dev/null
+++ b/google-api-go-generator/testdata/required-query.json
@@ -0,0 +1,43 @@
+{
+ "name": "tshealth",
+ "discoveryVersion": "v1",
+ "id": "tshealth:v1",
+ "basePath": "/_ah/api/tshealth/v1/",
+ "baseUrl": "https://ths-prod.googleplex.com/_ah/api/tshealth/v1/",
+ "servicePath": "tshealth/v1/",
+ "resources": {
+ "techs": {
+ "methods": {
+ "count": {
+ "description": "Counts the number of techs matching the constraints.",
+ "httpMethod": "GET",
+ "id": "tshealth.techs.count",
+ "parameters": {
+ "manager": {
+ "location": "query",
+ "required": true,
+ "type": "string"
+ }
+ },
+ "path": "techs/count",
+ "response": {
+ "$ref": "Google3CorpSupportToolsTshealthServiceApiV1TechsMessagesTechsCountResponse"
+ }
+ }
+ }
+ }
+ },
+ "schemas": {
+ "Google3CorpSupportToolsTshealthServiceApiV1TechsMessagesTechsCountResponse": {
+ "description": "Response for a tech count request.",
+ "id": "Google3CorpSupportToolsTshealthServiceApiV1TechsMessagesTechsCountResponse",
+ "properties": {
+ "count": {
+ "format": "int64",
+ "type": "string"
+ }
+ },
+ "type": "object"
+ }
+ }
+}
diff --git a/google-api-go-generator/testdata/required-query.want b/google-api-go-generator/testdata/required-query.want
new file mode 100644
index 0000000..760c55e
--- /dev/null
+++ b/google-api-go-generator/testdata/required-query.want
@@ -0,0 +1,239 @@
+// Package tshealth provides access to the .
+//
+// Usage example:
+//
+// import "google.golang.org/api/tshealth/"
+// ...
+// tshealthService, err := tshealth.New(oauthHttpClient)
+package tshealth // import "google.golang.org/api/tshealth/"
+
+import (
+ "bytes"
+ "encoding/json"
+ "errors"
+ "fmt"
+ context "golang.org/x/net/context"
+ ctxhttp "golang.org/x/net/context/ctxhttp"
+ gensupport "google.golang.org/api/gensupport"
+ googleapi "google.golang.org/api/googleapi"
+ "io"
+ "net/http"
+ "net/url"
+ "strconv"
+ "strings"
+)
+
+// 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
+var _ = ctxhttp.Do
+
+const apiId = "tshealth:v1"
+const apiName = "tshealth"
+const apiVersion = ""
+const basePath = "https://www.googleapis.com/_ah/api/tshealth/v1/"
+
+func New(client *http.Client) (*Service, error) {
+ if client == nil {
+ return nil, errors.New("client is nil")
+ }
+ s := &Service{client: client, BasePath: basePath}
+ s.Techs = NewTechsService(s)
+ return s, nil
+}
+
+type Service struct {
+ client *http.Client
+ BasePath string // API endpoint base URL
+ UserAgent string // optional additional User-Agent fragment
+
+ Techs *TechsService
+}
+
+func (s *Service) userAgent() string {
+ if s.UserAgent == "" {
+ return googleapi.UserAgent
+ }
+ return googleapi.UserAgent + " " + s.UserAgent
+}
+
+func NewTechsService(s *Service) *TechsService {
+ rs := &TechsService{s: s}
+ return rs
+}
+
+type TechsService struct {
+ s *Service
+}
+
+// Google3CorpSupportToolsTshealthServiceApiV1TechsMessagesTechsCountResp
+// onse: Response for a tech count request.
+type Google3CorpSupportToolsTshealthServiceApiV1TechsMessagesTechsCountResponse struct {
+ Count int64 `json:"count,omitempty,string"`
+
+ // ServerResponse contains the HTTP response code and headers from the
+ // server.
+ googleapi.ServerResponse `json:"-"`
+
+ // ForceSendFields is a list of field names (e.g. "Count") 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. "Count") 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 *Google3CorpSupportToolsTshealthServiceApiV1TechsMessagesTechsCountResponse) MarshalJSON() ([]byte, error) {
+ type NoMethod Google3CorpSupportToolsTshealthServiceApiV1TechsMessagesTechsCountResponse
+ raw := NoMethod(*s)
+ return gensupport.MarshalJSON(raw, s.ForceSendFields, s.NullFields)
+}
+
+// method id "tshealth.techs.count":
+
+type TechsCountCall struct {
+ s *Service
+ urlParams_ gensupport.URLParams
+ ifNoneMatch_ string
+ ctx_ context.Context
+ header_ http.Header
+}
+
+// Count: Counts the number of techs matching the constraints.
+func (r *TechsService) Count(manager string) *TechsCountCall {
+ c := &TechsCountCall{s: r.s, urlParams_: make(gensupport.URLParams)}
+ c.urlParams_.Set("manager", manager)
+ 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 *TechsCountCall) Fields(s ...googleapi.Field) *TechsCountCall {
+ 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 *TechsCountCall) IfNoneMatch(entityTag string) *TechsCountCall {
+ 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 *TechsCountCall) Context(ctx context.Context) *TechsCountCall {
+ 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 *TechsCountCall) Header() http.Header {
+ if c.header_ == nil {
+ c.header_ = make(http.Header)
+ }
+ return c.header_
+}
+
+func (c *TechsCountCall) doRequest(alt string) (*http.Response, error) {
+ reqHeaders := make(http.Header)
+ 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, "techs/count")
+ urls += "?" + c.urlParams_.Encode()
+ req, _ := http.NewRequest("GET", urls, body)
+ req.Header = reqHeaders
+ return gensupport.SendRequest(c.ctx_, c.s.client, req)
+}
+
+// Do executes the "tshealth.techs.count" call.
+// Exactly one of
+// *Google3CorpSupportToolsTshealthServiceApiV1TechsMessagesTechsCountRes
+// ponse or error will be non-nil. Any non-2xx status code is an error.
+// Response headers are in either
+// *Google3CorpSupportToolsTshealthServiceApiV1TechsMessagesTechsCountRes
+// ponse.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 *TechsCountCall) Do(opts ...googleapi.CallOption) (*Google3CorpSupportToolsTshealthServiceApiV1TechsMessagesTechsCountResponse, 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 := &Google3CorpSupportToolsTshealthServiceApiV1TechsMessagesTechsCountResponse{
+ 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": "Counts the number of techs matching the constraints.",
+ // "httpMethod": "GET",
+ // "id": "tshealth.techs.count",
+ // "parameters": {
+ // "manager": {
+ // "location": "query",
+ // "required": true,
+ // "type": "string"
+ // }
+ // },
+ // "path": "techs/count",
+ // "response": {
+ // "$ref": "Google3CorpSupportToolsTshealthServiceApiV1TechsMessagesTechsCountResponse"
+ // }
+ // }
+
+}