25'ten fazla konu seçemezsiniz Konular bir harf veya rakamla başlamalı, kısa çizgiler ('-') içerebilir ve en fazla 35 karakter uzunluğunda olabilir.
 
 
 

2482 satır
72 KiB

  1. // Copyright 2011 Google Inc. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package main
  5. import (
  6. "bytes"
  7. "encoding/json"
  8. "errors"
  9. "flag"
  10. "fmt"
  11. "go/format"
  12. "io"
  13. "io/ioutil"
  14. "log"
  15. "net/http"
  16. "net/url"
  17. "os"
  18. "os/exec"
  19. "path/filepath"
  20. "regexp"
  21. "sort"
  22. "strconv"
  23. "strings"
  24. "time"
  25. "unicode"
  26. "google.golang.org/api/google-api-go-generator/internal/disco"
  27. )
  28. const (
  29. googleDiscoveryURL = "https://www.googleapis.com/discovery/v1/apis"
  30. generatorVersion = "2018018"
  31. )
  32. var (
  33. apiToGenerate = flag.String("api", "*", "The API ID to generate, like 'tasks:v1'. A value of '*' means all.")
  34. useCache = flag.Bool("cache", true, "Use cache of discovered Google API discovery documents.")
  35. genDir = flag.String("gendir", "", "Directory to use to write out generated Go files")
  36. build = flag.Bool("build", false, "Compile generated packages.")
  37. install = flag.Bool("install", false, "Install generated packages.")
  38. apisURL = flag.String("discoveryurl", googleDiscoveryURL, "URL to root discovery document")
  39. publicOnly = flag.Bool("publiconly", true, "Only build public, released APIs. Only applicable for Google employees.")
  40. jsonFile = flag.String("api_json_file", "", "If non-empty, the path to a local file on disk containing the API to generate. Exclusive with setting --api.")
  41. output = flag.String("output", "", "(optional) Path to source output file. If not specified, the API name and version are used to construct an output path (e.g. tasks/v1).")
  42. apiPackageBase = flag.String("api_pkg_base", "google.golang.org/api", "Go package prefix to use for all generated APIs.")
  43. baseURL = flag.String("base_url", "", "(optional) Override the default service API URL. If empty, the service's root URL will be used.")
  44. headerPath = flag.String("header_path", "", "If non-empty, prepend the contents of this file to generated services.")
  45. gensupportPkg = flag.String("gensupport_pkg", "google.golang.org/api/gensupport", "Go package path of the 'api/gensupport' support package.")
  46. googleapiPkg = flag.String("googleapi_pkg", "google.golang.org/api/googleapi", "Go package path of the 'api/googleapi' support package.")
  47. optionPkg = flag.String("option_pkg", "google.golang.org/api/option", "Go package path of the 'api/option' support package.")
  48. htransportPkg = flag.String("htransport_pkg", "google.golang.org/api/transport/http", "Go package path of the 'api/transport/http' support package.")
  49. copyrightYear = flag.String("copyright_year", fmt.Sprintf("%d", time.Now().Year()), "Year for copyright.")
  50. serviceTypes = []string{"Service", "APIService"}
  51. )
  52. // API represents an API to generate, as well as its state while it's
  53. // generating.
  54. type API struct {
  55. // Fields needed before generating code, to select and find the APIs
  56. // to generate.
  57. // These fields usually come from the "directory item" JSON objects
  58. // that are provided by the googleDiscoveryURL. We unmarshal a directory
  59. // item directly into this struct.
  60. ID string `json:"id"`
  61. Name string `json:"name"`
  62. Version string `json:"version"`
  63. DiscoveryLink string `json:"discoveryRestUrl"` // absolute
  64. doc *disco.Document
  65. // TODO(jba): remove m when we've fully converted to using disco.
  66. m map[string]interface{}
  67. forceJSON []byte // if non-nil, the JSON schema file. else fetched.
  68. usedNames namePool
  69. schemas map[string]*Schema // apiName -> schema
  70. responseTypes map[string]bool
  71. p func(format string, args ...interface{}) // print raw
  72. pn func(format string, args ...interface{}) // print with newline
  73. }
  74. func (a *API) sortedSchemaNames() (names []string) {
  75. for name := range a.schemas {
  76. names = append(names, name)
  77. }
  78. sort.Strings(names)
  79. return
  80. }
  81. func (a *API) Schema(name string) *Schema {
  82. return a.schemas[name]
  83. }
  84. type generateError struct {
  85. api *API
  86. error error
  87. }
  88. func (e *generateError) Error() string {
  89. return fmt.Sprintf("API %s failed to generate code: %v", e.api.ID, e.error)
  90. }
  91. type compileError struct {
  92. api *API
  93. output string
  94. }
  95. func (e *compileError) Error() string {
  96. return fmt.Sprintf("API %s failed to compile:\n%v", e.api.ID, e.output)
  97. }
  98. func main() {
  99. flag.Parse()
  100. if *install {
  101. *build = true
  102. }
  103. var (
  104. apiIds = []string{}
  105. matches = []*API{}
  106. errors = []error{}
  107. )
  108. for _, api := range getAPIs() {
  109. apiIds = append(apiIds, api.ID)
  110. if !api.want() {
  111. continue
  112. }
  113. matches = append(matches, api)
  114. log.Printf("Generating API %s", api.ID)
  115. err := api.WriteGeneratedCode()
  116. if err != nil && err != errNoDoc {
  117. errors = append(errors, &generateError{api, err})
  118. continue
  119. }
  120. if *build && err == nil {
  121. var args []string
  122. if *install {
  123. args = append(args, "install")
  124. } else {
  125. args = append(args, "build")
  126. }
  127. args = append(args, api.Target())
  128. out, err := exec.Command("go", args...).CombinedOutput()
  129. if err != nil {
  130. errors = append(errors, &compileError{api, string(out)})
  131. }
  132. }
  133. }
  134. if len(matches) == 0 {
  135. log.Fatalf("No APIs matched %q; options are %v", *apiToGenerate, apiIds)
  136. }
  137. if len(errors) > 0 {
  138. log.Printf("%d API(s) failed to generate or compile:", len(errors))
  139. for _, ce := range errors {
  140. log.Println(ce.Error())
  141. }
  142. os.Exit(1)
  143. }
  144. }
  145. func (a *API) want() bool {
  146. if *jsonFile != "" {
  147. // Return true early, before calling a.JSONFile()
  148. // which will require a GOPATH be set. This is for
  149. // integration with Google's build system genrules
  150. // where there is no GOPATH.
  151. return true
  152. }
  153. // Skip this API if we're in cached mode and the files don't exist on disk.
  154. if *useCache {
  155. if _, err := os.Stat(a.JSONFile()); os.IsNotExist(err) {
  156. return false
  157. }
  158. }
  159. return *apiToGenerate == "*" || *apiToGenerate == a.ID
  160. }
  161. func getAPIs() []*API {
  162. if *jsonFile != "" {
  163. return getAPIsFromFile()
  164. }
  165. var bytes []byte
  166. var source string
  167. apiListFile := filepath.Join(genDirRoot(), "api-list.json")
  168. if *useCache {
  169. if !*publicOnly {
  170. log.Fatalf("-cache=true not compatible with -publiconly=false")
  171. }
  172. var err error
  173. bytes, err = ioutil.ReadFile(apiListFile)
  174. if err != nil {
  175. log.Fatal(err)
  176. }
  177. source = apiListFile
  178. } else {
  179. bytes = slurpURL(*apisURL)
  180. if *publicOnly {
  181. if err := writeFile(apiListFile, bytes); err != nil {
  182. log.Fatal(err)
  183. }
  184. }
  185. source = *apisURL
  186. }
  187. apis, err := unmarshalAPIs(bytes)
  188. if err != nil {
  189. log.Fatalf("error decoding JSON in %s: %v", source, err)
  190. }
  191. if !*publicOnly && *apiToGenerate != "*" {
  192. apis = append(apis, apiFromID(*apiToGenerate))
  193. }
  194. return apis
  195. }
  196. func unmarshalAPIs(bytes []byte) ([]*API, error) {
  197. var itemObj struct{ Items []*API }
  198. if err := json.Unmarshal(bytes, &itemObj); err != nil {
  199. return nil, err
  200. }
  201. return itemObj.Items, nil
  202. }
  203. func apiFromID(apiID string) *API {
  204. parts := strings.Split(apiID, ":")
  205. if len(parts) != 2 {
  206. log.Fatalf("malformed API name: %q", apiID)
  207. }
  208. return &API{
  209. ID: apiID,
  210. Name: parts[0],
  211. Version: parts[1],
  212. }
  213. }
  214. // getAPIsFromFile handles the case of generating exactly one API
  215. // from the flag given in --api_json_file
  216. func getAPIsFromFile() []*API {
  217. if *apiToGenerate != "*" {
  218. log.Fatalf("Can't set --api with --api_json_file.")
  219. }
  220. if !*publicOnly {
  221. log.Fatalf("Can't set --publiconly with --api_json_file.")
  222. }
  223. a, err := apiFromFile(*jsonFile)
  224. if err != nil {
  225. log.Fatal(err)
  226. }
  227. return []*API{a}
  228. }
  229. func apiFromFile(file string) (*API, error) {
  230. jsonBytes, err := ioutil.ReadFile(file)
  231. if err != nil {
  232. return nil, fmt.Errorf("Error reading %s: %v", file, err)
  233. }
  234. doc, err := disco.NewDocument(jsonBytes)
  235. if err != nil {
  236. return nil, fmt.Errorf("reading document from %q: %v", file, err)
  237. }
  238. a := &API{
  239. ID: doc.ID,
  240. Name: doc.Name,
  241. Version: doc.Version,
  242. forceJSON: jsonBytes,
  243. doc: doc,
  244. }
  245. return a, nil
  246. }
  247. func writeFile(file string, contents []byte) error {
  248. // Don't write it if the contents are identical.
  249. existing, err := ioutil.ReadFile(file)
  250. if err == nil && (bytes.Equal(existing, contents) || basicallyEqual(existing, contents)) {
  251. return nil
  252. }
  253. outdir := filepath.Dir(file)
  254. if err = os.MkdirAll(outdir, 0755); err != nil {
  255. return fmt.Errorf("failed to Mkdir %s: %v", outdir, err)
  256. }
  257. return ioutil.WriteFile(file, contents, 0644)
  258. }
  259. var ignoreLines = regexp.MustCompile(`(?m)^\s+"(?:etag|revision)": ".+\n`)
  260. // basicallyEqual reports whether a and b are equal except for boring
  261. // differences like ETag updates.
  262. func basicallyEqual(a, b []byte) bool {
  263. return ignoreLines.Match(a) && ignoreLines.Match(b) &&
  264. bytes.Equal(ignoreLines.ReplaceAll(a, nil), ignoreLines.ReplaceAll(b, nil))
  265. }
  266. func slurpURL(urlStr string) []byte {
  267. if *useCache {
  268. log.Fatalf("Invalid use of slurpURL in cached mode for URL %s", urlStr)
  269. }
  270. req, err := http.NewRequest("GET", urlStr, nil)
  271. if err != nil {
  272. log.Fatal(err)
  273. }
  274. if *publicOnly {
  275. req.Header.Add("X-User-IP", "0.0.0.0") // hack
  276. }
  277. res, err := http.DefaultClient.Do(req)
  278. if err != nil {
  279. log.Fatalf("Error fetching URL %s: %v", urlStr, err)
  280. }
  281. if res.StatusCode >= 300 {
  282. log.Printf("WARNING: URL %s served status code %d", urlStr, res.StatusCode)
  283. return nil
  284. }
  285. bs, err := ioutil.ReadAll(res.Body)
  286. if err != nil {
  287. log.Fatalf("Error reading body of URL %s: %v", urlStr, err)
  288. }
  289. return bs
  290. }
  291. func panicf(format string, args ...interface{}) {
  292. panic(fmt.Sprintf(format, args...))
  293. }
  294. // namePool keeps track of used names and assigns free ones based on a
  295. // preferred name
  296. type namePool struct {
  297. m map[string]bool // lazily initialized
  298. }
  299. // oddVersionRE matches unusual API names like directory_v1.
  300. var oddVersionRE = regexp.MustCompile(`^(.+)_(v[\d\.]+)$`)
  301. // renameVersion conditionally rewrites the provided version such
  302. // that the final path component of the import path doesn't look
  303. // like a Go identifier. This keeps the consistency that import paths
  304. // for the generated Go packages look like:
  305. // google.golang.org/api/NAME/v<version>
  306. // and have package NAME.
  307. // See https://github.com/google/google-api-go-client/issues/78
  308. func renameVersion(version string) string {
  309. if version == "alpha" || version == "beta" {
  310. return "v0." + version
  311. }
  312. if m := oddVersionRE.FindStringSubmatch(version); m != nil {
  313. return m[1] + "/" + m[2]
  314. }
  315. return version
  316. }
  317. func (p *namePool) Get(preferred string) string {
  318. if p.m == nil {
  319. p.m = make(map[string]bool)
  320. }
  321. name := preferred
  322. tries := 0
  323. for p.m[name] {
  324. tries++
  325. name = fmt.Sprintf("%s%d", preferred, tries)
  326. }
  327. p.m[name] = true
  328. return name
  329. }
  330. func genDirRoot() string {
  331. if *genDir != "" {
  332. return *genDir
  333. }
  334. paths := filepath.SplitList(os.Getenv("GOPATH"))
  335. if len(paths) == 0 {
  336. log.Fatalf("No GOPATH set.")
  337. }
  338. return filepath.Join(paths[0], "src", "google.golang.org", "api")
  339. }
  340. func (a *API) SourceDir() string {
  341. return filepath.Join(genDirRoot(), a.Package(), renameVersion(a.Version))
  342. }
  343. func (a *API) DiscoveryURL() string {
  344. if a.DiscoveryLink == "" {
  345. log.Fatalf("API %s has no DiscoveryLink", a.ID)
  346. }
  347. return a.DiscoveryLink
  348. }
  349. func (a *API) Package() string {
  350. return strings.ToLower(a.Name)
  351. }
  352. func (a *API) Target() string {
  353. return fmt.Sprintf("%s/%s/%s", *apiPackageBase, a.Package(), renameVersion(a.Version))
  354. }
  355. // ServiceType returns the name of the type to use for the root API struct
  356. // (typically "Service").
  357. func (a *API) ServiceType() string {
  358. switch a.Name {
  359. case "appengine", "content": // retained for historical compatibility.
  360. return "APIService"
  361. default:
  362. for _, t := range serviceTypes {
  363. if _, ok := a.schemas[t]; !ok {
  364. return t
  365. }
  366. }
  367. panic("all service types are used, please consider introducing a new type to serviceTypes.")
  368. }
  369. }
  370. // GetName returns a free top-level function/type identifier in the package.
  371. // It tries to return your preferred match if it's free.
  372. func (a *API) GetName(preferred string) string {
  373. return a.usedNames.Get(preferred)
  374. }
  375. func (a *API) apiBaseURL() string {
  376. var base, rel string
  377. switch {
  378. case *baseURL != "":
  379. base, rel = *baseURL, a.doc.BasePath
  380. case a.doc.RootURL != "":
  381. base, rel = a.doc.RootURL, a.doc.ServicePath
  382. default:
  383. base, rel = *apisURL, a.doc.BasePath
  384. }
  385. return resolveRelative(base, rel)
  386. }
  387. func (a *API) needsDataWrapper() bool {
  388. for _, feature := range a.doc.Features {
  389. if feature == "dataWrapper" {
  390. return true
  391. }
  392. }
  393. return false
  394. }
  395. func (a *API) jsonBytes() []byte {
  396. if a.forceJSON == nil {
  397. var slurp []byte
  398. var err error
  399. if *useCache {
  400. slurp, err = ioutil.ReadFile(a.JSONFile())
  401. if err != nil {
  402. log.Fatal(err)
  403. }
  404. } else {
  405. slurp = slurpURL(a.DiscoveryURL())
  406. if slurp != nil {
  407. // Make sure that keys are sorted by re-marshalling.
  408. d := make(map[string]interface{})
  409. json.Unmarshal(slurp, &d)
  410. if err != nil {
  411. log.Fatal(err)
  412. }
  413. var err error
  414. slurp, err = json.MarshalIndent(d, "", " ")
  415. if err != nil {
  416. log.Fatal(err)
  417. }
  418. }
  419. }
  420. a.forceJSON = slurp
  421. }
  422. return a.forceJSON
  423. }
  424. func (a *API) JSONFile() string {
  425. return filepath.Join(a.SourceDir(), a.Package()+"-api.json")
  426. }
  427. var errNoDoc = errors.New("could not read discovery doc")
  428. // WriteGeneratedCode generates code for a.
  429. // It returns errNoDoc if we couldn't read the discovery doc.
  430. func (a *API) WriteGeneratedCode() error {
  431. genfilename := *output
  432. jsonBytes := a.jsonBytes()
  433. // Skip generation if we don't have the discovery doc.
  434. if jsonBytes == nil {
  435. // No message here, because slurpURL printed one.
  436. return errNoDoc
  437. }
  438. if genfilename == "" {
  439. if err := writeFile(a.JSONFile(), jsonBytes); err != nil {
  440. return err
  441. }
  442. outdir := a.SourceDir()
  443. err := os.MkdirAll(outdir, 0755)
  444. if err != nil {
  445. return fmt.Errorf("failed to Mkdir %s: %v", outdir, err)
  446. }
  447. pkg := a.Package()
  448. genfilename = filepath.Join(outdir, pkg+"-gen.go")
  449. }
  450. code, err := a.GenerateCode()
  451. errw := writeFile(genfilename, code)
  452. if err == nil {
  453. err = errw
  454. }
  455. if err != nil {
  456. return err
  457. }
  458. return nil
  459. }
  460. var docsLink string
  461. func (a *API) GenerateCode() ([]byte, error) {
  462. pkg := a.Package()
  463. jsonBytes := a.jsonBytes()
  464. var err error
  465. if a.doc == nil {
  466. a.doc, err = disco.NewDocument(jsonBytes)
  467. if err != nil {
  468. return nil, err
  469. }
  470. }
  471. // Buffer the output in memory, for gofmt'ing later.
  472. var buf bytes.Buffer
  473. a.p = func(format string, args ...interface{}) {
  474. _, err := fmt.Fprintf(&buf, format, args...)
  475. if err != nil {
  476. panic(err)
  477. }
  478. }
  479. a.pn = func(format string, args ...interface{}) {
  480. a.p(format+"\n", args...)
  481. }
  482. wf := func(path string) error {
  483. f, err := os.Open(path)
  484. if err != nil {
  485. return err
  486. }
  487. defer f.Close()
  488. _, err = io.Copy(&buf, f)
  489. return err
  490. }
  491. p, pn := a.p, a.pn
  492. if *headerPath != "" {
  493. if err := wf(*headerPath); err != nil {
  494. return nil, err
  495. }
  496. }
  497. pn(`// Copyright %s Google LLC.
  498. // Use of this source code is governed by a BSD-style
  499. // license that can be found in the LICENSE file.
  500. // Code generated file. DO NOT EDIT.
  501. `, *copyrightYear)
  502. pn("// Package %s provides access to the %s.", pkg, a.doc.Title)
  503. if r := replacementPackage[pkg]; r != "" {
  504. pn("//")
  505. pn("// This package is DEPRECATED. Use package %s instead.", r)
  506. }
  507. docsLink = a.doc.DocumentationLink
  508. if docsLink != "" {
  509. pn("//")
  510. pn("// For product documentation, see: %s", docsLink)
  511. }
  512. pn("//")
  513. pn("// Creating a client")
  514. pn("//")
  515. pn("// Usage example:")
  516. pn("//")
  517. pn("// import %q", a.Target())
  518. pn("// ...")
  519. pn("// ctx := context.Background()")
  520. pn("// %sService, err := %s.NewService(ctx)", pkg, pkg)
  521. pn("//")
  522. pn("// In this example, Google Application Default Credentials are used for authentication.")
  523. pn("//")
  524. pn("// For information on how to create and obtain Application Default Credentials, see https://developers.google.com/identity/protocols/application-default-credentials.")
  525. pn("//")
  526. pn("// Other authentication options")
  527. pn("//")
  528. if len(a.doc.Auth.OAuth2Scopes) > 1 {
  529. pn(`// By default, all available scopes (see "Constants") are used to authenticate. To restrict scopes, use option.WithScopes:`)
  530. pn("//")
  531. // NOTE: the first scope tends to be the broadest. Use the last one to demonstrate restriction.
  532. pn("// %sService, err := %s.NewService(ctx, option.WithScopes(%s.%s))", pkg, pkg, pkg, scopeIdentifierFromURL(a.doc.Auth.OAuth2Scopes[len(a.doc.Auth.OAuth2Scopes)-1].URL))
  533. pn("//")
  534. }
  535. pn("// To use an API key for authentication (note: some APIs do not support API keys), use option.WithAPIKey:")
  536. pn("//")
  537. pn(`// %sService, err := %s.NewService(ctx, option.WithAPIKey("AIza..."))`, pkg, pkg)
  538. pn("//")
  539. pn("// To use an OAuth token (e.g., a user token obtained via a three-legged OAuth flow), use option.WithTokenSource:")
  540. pn("//")
  541. pn("// config := &oauth2.Config{...}")
  542. pn("// // ...")
  543. pn("// token, err := config.Exchange(ctx, ...)")
  544. pn("// %sService, err := %s.NewService(ctx, option.WithTokenSource(config.TokenSource(ctx, token)))", pkg, pkg)
  545. pn("//")
  546. pn("// See https://godoc.org/google.golang.org/api/option/ for details on options.")
  547. pn("package %s // import %q", pkg, a.Target())
  548. p("\n")
  549. pn("import (")
  550. for _, imp := range []string{
  551. "bytes",
  552. "context",
  553. "encoding/json",
  554. "errors",
  555. "fmt",
  556. "io",
  557. "net/http",
  558. "net/url",
  559. "strconv",
  560. "strings",
  561. } {
  562. pn(" %q", imp)
  563. }
  564. pn("")
  565. for _, imp := range []struct {
  566. pkg string
  567. lname string
  568. }{
  569. {*gensupportPkg, "gensupport"},
  570. {*googleapiPkg, "googleapi"},
  571. {*optionPkg, "option"},
  572. {*htransportPkg, "htransport"},
  573. } {
  574. pn(" %s %q", imp.lname, imp.pkg)
  575. }
  576. pn(")")
  577. pn("\n// Always reference these packages, just in case the auto-generated code")
  578. pn("// below doesn't.")
  579. pn("var _ = bytes.NewBuffer")
  580. pn("var _ = strconv.Itoa")
  581. pn("var _ = fmt.Sprintf")
  582. pn("var _ = json.NewDecoder")
  583. pn("var _ = io.Copy")
  584. pn("var _ = url.Parse")
  585. pn("var _ = gensupport.MarshalJSON")
  586. pn("var _ = googleapi.Version")
  587. pn("var _ = errors.New")
  588. pn("var _ = strings.Replace")
  589. pn("var _ = context.Canceled")
  590. pn("")
  591. pn("const apiId = %q", a.doc.ID)
  592. pn("const apiName = %q", a.doc.Name)
  593. pn("const apiVersion = %q", a.doc.Version)
  594. pn("const basePath = %q", a.apiBaseURL())
  595. a.generateScopeConstants()
  596. a.PopulateSchemas()
  597. service := a.ServiceType()
  598. // Reserve names (ignore return value; we're the first caller).
  599. a.GetName("New")
  600. a.GetName(service)
  601. pn("// NewService creates a new %s.", service)
  602. pn("func NewService(ctx context.Context, opts ...option.ClientOption) (*%s, error) {", service)
  603. if len(a.doc.Auth.OAuth2Scopes) != 0 {
  604. pn("scopesOption := option.WithScopes(")
  605. for _, scope := range a.doc.Auth.OAuth2Scopes {
  606. pn("%q,", scope.URL)
  607. }
  608. pn(")")
  609. pn("// NOTE: prepend, so we don't override user-specified scopes.")
  610. pn("opts = append([]option.ClientOption{scopesOption}, opts...)")
  611. }
  612. pn("client, endpoint, err := htransport.NewClient(ctx, opts...)")
  613. pn("if err != nil { return nil, err }")
  614. pn("s, err := New(client)")
  615. pn("if err != nil { return nil, err }")
  616. pn(`if endpoint != "" { s.BasePath = endpoint }`)
  617. pn("return s, nil")
  618. pn("}\n")
  619. pn("// New creates a new %s. It uses the provided http.Client for requests.", service)
  620. pn("//")
  621. pn("// Deprecated: please use NewService instead.")
  622. pn("// To provide a custom HTTP client, use option.WithHTTPClient.")
  623. pn("// If you are using google.golang.org/api/googleapis/transport.APIKey, use option.WithAPIKey with NewService instead.")
  624. pn("func New(client *http.Client) (*%s, error) {", service)
  625. pn("if client == nil { return nil, errors.New(\"client is nil\") }")
  626. pn("s := &%s{client: client, BasePath: basePath}", service)
  627. for _, res := range a.doc.Resources { // add top level resources.
  628. pn("s.%s = New%s(s)", resourceGoField(res, nil), resourceGoType(res))
  629. }
  630. pn("return s, nil")
  631. pn("}")
  632. pn("\ntype %s struct {", service)
  633. pn(" client *http.Client")
  634. pn(" BasePath string // API endpoint base URL")
  635. pn(" UserAgent string // optional additional User-Agent fragment")
  636. for _, res := range a.doc.Resources {
  637. pn("\n\t%s\t*%s", resourceGoField(res, nil), resourceGoType(res))
  638. }
  639. pn("}")
  640. pn("\nfunc (s *%s) userAgent() string {", service)
  641. pn(` if s.UserAgent == "" { return googleapi.UserAgent }`)
  642. pn(` return googleapi.UserAgent + " " + s.UserAgent`)
  643. pn("}\n")
  644. for _, res := range a.doc.Resources {
  645. a.generateResource(res)
  646. }
  647. a.responseTypes = make(map[string]bool)
  648. for _, meth := range a.APIMethods() {
  649. meth.cacheResponseTypes(a)
  650. }
  651. for _, res := range a.doc.Resources {
  652. a.cacheResourceResponseTypes(res)
  653. }
  654. for _, name := range a.sortedSchemaNames() {
  655. a.schemas[name].writeSchemaCode(a)
  656. }
  657. for _, meth := range a.APIMethods() {
  658. meth.generateCode()
  659. }
  660. for _, res := range a.doc.Resources {
  661. a.generateResourceMethods(res)
  662. }
  663. clean, err := format.Source(buf.Bytes())
  664. if err != nil {
  665. return buf.Bytes(), err
  666. }
  667. return clean, nil
  668. }
  669. func (a *API) generateScopeConstants() {
  670. scopes := a.doc.Auth.OAuth2Scopes
  671. if len(scopes) == 0 {
  672. return
  673. }
  674. a.pn("// OAuth2 scopes used by this API.")
  675. a.pn("const (")
  676. n := 0
  677. for _, scope := range scopes {
  678. if n > 0 {
  679. a.p("\n")
  680. }
  681. n++
  682. ident := scopeIdentifierFromURL(scope.URL)
  683. if scope.Description != "" {
  684. a.p("%s", asComment("\t", scope.Description))
  685. }
  686. a.pn("\t%s = %q", ident, scope.URL)
  687. }
  688. a.p(")\n\n")
  689. }
  690. func scopeIdentifierFromURL(urlStr string) string {
  691. const prefix = "https://www.googleapis.com/auth/"
  692. if !strings.HasPrefix(urlStr, prefix) {
  693. const https = "https://"
  694. if !strings.HasPrefix(urlStr, https) {
  695. log.Fatalf("Unexpected oauth2 scope %q doesn't start with %q", urlStr, https)
  696. }
  697. ident := validGoIdentifer(depunct(urlStr[len(https):], true)) + "Scope"
  698. return ident
  699. }
  700. ident := validGoIdentifer(initialCap(urlStr[len(prefix):])) + "Scope"
  701. return ident
  702. }
  703. // Schema is a disco.Schema that has been bestowed an identifier, whether by
  704. // having an "id" field at the top of the schema or with an
  705. // automatically generated one in populateSubSchemas.
  706. //
  707. // TODO: While sub-types shouldn't need to be promoted to schemas,
  708. // API.GenerateCode iterates over API.schemas to figure out what
  709. // top-level Go types to write. These should be separate concerns.
  710. type Schema struct {
  711. api *API
  712. typ *disco.Schema
  713. apiName string // the native API-defined name of this type
  714. goName string // lazily populated by GoName
  715. goReturnType string // lazily populated by GoReturnType
  716. props []*Property
  717. }
  718. type Property struct {
  719. s *Schema // the containing Schema
  720. p *disco.Property
  721. assignedGoName string
  722. }
  723. func (p *Property) Type() *disco.Schema {
  724. return p.p.Schema
  725. }
  726. func (p *Property) GoName() string {
  727. return initialCap(p.p.Name)
  728. }
  729. func (p *Property) Default() string {
  730. return p.p.Schema.Default
  731. }
  732. func (p *Property) Description() string {
  733. return p.p.Schema.Description
  734. }
  735. func (p *Property) Enum() ([]string, bool) {
  736. typ := p.p.Schema
  737. if typ.Enums != nil {
  738. return typ.Enums, true
  739. }
  740. // Check if this has an array of string enums.
  741. if typ.ItemSchema != nil {
  742. if enums := typ.ItemSchema.Enums; enums != nil && typ.ItemSchema.Type == "string" {
  743. return enums, true
  744. }
  745. }
  746. return nil, false
  747. }
  748. func (p *Property) EnumDescriptions() []string {
  749. if desc := p.p.Schema.EnumDescriptions; desc != nil {
  750. return desc
  751. }
  752. // Check if this has an array of string enum descriptions.
  753. if items := p.p.Schema.ItemSchema; items != nil {
  754. if desc := items.EnumDescriptions; desc != nil {
  755. return desc
  756. }
  757. }
  758. return nil
  759. }
  760. func (p *Property) Pattern() (string, bool) {
  761. return p.p.Schema.Pattern, (p.p.Schema.Pattern != "")
  762. }
  763. func (p *Property) TypeAsGo() string {
  764. return p.s.api.typeAsGo(p.Type(), false)
  765. }
  766. // A FieldName uniquely identifies a field within a Schema struct for an API.
  767. type fieldName struct {
  768. api string // The ID of an API.
  769. schema string // The Go name of a Schema struct.
  770. field string // The Go name of a field.
  771. }
  772. // pointerFields is a list of fields that should use a pointer type.
  773. // This makes it possible to distinguish between a field being unset vs having
  774. // an empty value.
  775. var pointerFields = []fieldName{
  776. {api: "androidpublisher:v1.1", schema: "InappPurchase", field: "PurchaseType"},
  777. {api: "androidpublisher:v2", schema: "ProductPurchase", field: "PurchaseType"},
  778. {api: "androidpublisher:v3", schema: "ProductPurchase", field: "PurchaseType"},
  779. {api: "androidpublisher:v2", schema: "SubscriptionPurchase", field: "CancelReason"},
  780. {api: "androidpublisher:v2", schema: "SubscriptionPurchase", field: "PaymentState"},
  781. {api: "androidpublisher:v2", schema: "SubscriptionPurchase", field: "PurchaseType"},
  782. {api: "androidpublisher:v3", schema: "SubscriptionPurchase", field: "PurchaseType"},
  783. {api: "cloudmonitoring:v2beta2", schema: "Point", field: "BoolValue"},
  784. {api: "cloudmonitoring:v2beta2", schema: "Point", field: "DoubleValue"},
  785. {api: "cloudmonitoring:v2beta2", schema: "Point", field: "Int64Value"},
  786. {api: "cloudmonitoring:v2beta2", schema: "Point", field: "StringValue"},
  787. {api: "compute:alpha", schema: "Scheduling", field: "AutomaticRestart"},
  788. {api: "compute:beta", schema: "MetadataItems", field: "Value"},
  789. {api: "compute:beta", schema: "Scheduling", field: "AutomaticRestart"},
  790. {api: "compute:v1", schema: "MetadataItems", field: "Value"},
  791. {api: "compute:v1", schema: "Scheduling", field: "AutomaticRestart"},
  792. {api: "content:v2", schema: "AccountUser", field: "Admin"},
  793. {api: "datastore:v1beta2", schema: "Property", field: "BlobKeyValue"},
  794. {api: "datastore:v1beta2", schema: "Property", field: "BlobValue"},
  795. {api: "datastore:v1beta2", schema: "Property", field: "BooleanValue"},
  796. {api: "datastore:v1beta2", schema: "Property", field: "DateTimeValue"},
  797. {api: "datastore:v1beta2", schema: "Property", field: "DoubleValue"},
  798. {api: "datastore:v1beta2", schema: "Property", field: "Indexed"},
  799. {api: "datastore:v1beta2", schema: "Property", field: "IntegerValue"},
  800. {api: "datastore:v1beta2", schema: "Property", field: "StringValue"},
  801. {api: "datastore:v1beta3", schema: "Value", field: "BlobValue"},
  802. {api: "datastore:v1beta3", schema: "Value", field: "BooleanValue"},
  803. {api: "datastore:v1beta3", schema: "Value", field: "DoubleValue"},
  804. {api: "datastore:v1beta3", schema: "Value", field: "IntegerValue"},
  805. {api: "datastore:v1beta3", schema: "Value", field: "StringValue"},
  806. {api: "datastore:v1beta3", schema: "Value", field: "TimestampValue"},
  807. {api: "genomics:v1beta2", schema: "Dataset", field: "IsPublic"},
  808. {api: "monitoring:v3", schema: "TypedValue", field: "BoolValue"},
  809. {api: "monitoring:v3", schema: "TypedValue", field: "DoubleValue"},
  810. {api: "monitoring:v3", schema: "TypedValue", field: "Int64Value"},
  811. {api: "monitoring:v3", schema: "TypedValue", field: "StringValue"},
  812. {api: "servicecontrol:v1", schema: "MetricValue", field: "BoolValue"},
  813. {api: "servicecontrol:v1", schema: "MetricValue", field: "DoubleValue"},
  814. {api: "servicecontrol:v1", schema: "MetricValue", field: "Int64Value"},
  815. {api: "servicecontrol:v1", schema: "MetricValue", field: "StringValue"},
  816. {api: "sqladmin:v1beta4", schema: "Settings", field: "StorageAutoResize"},
  817. {api: "storage:v1", schema: "BucketLifecycleRuleCondition", field: "IsLive"},
  818. {api: "storage:v1beta2", schema: "BucketLifecycleRuleCondition", field: "IsLive"},
  819. {api: "tasks:v1", schema: "Task", field: "Completed"},
  820. {api: "youtube:v3", schema: "ChannelSectionSnippet", field: "Position"},
  821. }
  822. // forcePointerType reports whether p should be represented as a pointer type in its parent schema struct.
  823. func (p *Property) forcePointerType() bool {
  824. if p.UnfortunateDefault() {
  825. return true
  826. }
  827. name := fieldName{api: p.s.api.ID, schema: p.s.GoName(), field: p.GoName()}
  828. for _, pf := range pointerFields {
  829. if pf == name {
  830. return true
  831. }
  832. }
  833. return false
  834. }
  835. // UnfortunateDefault reports whether p may be set to a zero value, but has a non-zero default.
  836. func (p *Property) UnfortunateDefault() bool {
  837. switch p.TypeAsGo() {
  838. default:
  839. return false
  840. case "bool":
  841. return p.Default() == "true"
  842. case "string":
  843. if p.Default() == "" {
  844. return false
  845. }
  846. // String fields are considered to "allow" a zero value if either:
  847. // (a) they are an enum, and one of the permitted enum values is the empty string, or
  848. // (b) they have a validation pattern which matches the empty string.
  849. pattern, hasPat := p.Pattern()
  850. enum, hasEnum := p.Enum()
  851. if hasPat && hasEnum {
  852. log.Printf("Encountered enum property which also has a pattern: %#v", p)
  853. return false // don't know how to handle this, so ignore.
  854. }
  855. return (hasPat && emptyPattern(pattern)) ||
  856. (hasEnum && emptyEnum(enum))
  857. case "float64", "int64", "uint64", "int32", "uint32":
  858. if p.Default() == "" {
  859. return false
  860. }
  861. if f, err := strconv.ParseFloat(p.Default(), 64); err == nil {
  862. return f != 0.0
  863. }
  864. // The default value has an unexpected form. Whatever it is, it's non-zero.
  865. return true
  866. }
  867. }
  868. // emptyPattern reports whether a pattern matches the empty string.
  869. func emptyPattern(pattern string) bool {
  870. if re, err := regexp.Compile(pattern); err == nil {
  871. return re.MatchString("")
  872. }
  873. log.Printf("Encountered bad pattern: %s", pattern)
  874. return false
  875. }
  876. // emptyEnum reports whether a property enum list contains the empty string.
  877. func emptyEnum(enum []string) bool {
  878. for _, val := range enum {
  879. if val == "" {
  880. return true
  881. }
  882. }
  883. return false
  884. }
  885. func (a *API) typeAsGo(s *disco.Schema, elidePointers bool) string {
  886. switch s.Kind {
  887. case disco.SimpleKind:
  888. return mustSimpleTypeConvert(s.Type, s.Format)
  889. case disco.ArrayKind:
  890. as := s.ElementSchema()
  891. if as.Type == "string" {
  892. switch as.Format {
  893. case "int64":
  894. return "googleapi.Int64s"
  895. case "uint64":
  896. return "googleapi.Uint64s"
  897. case "int32":
  898. return "googleapi.Int32s"
  899. case "uint32":
  900. return "googleapi.Uint32s"
  901. case "float64":
  902. return "googleapi.Float64s"
  903. }
  904. }
  905. return "[]" + a.typeAsGo(as, elidePointers)
  906. case disco.ReferenceKind:
  907. rs := s.RefSchema
  908. if rs.Kind == disco.SimpleKind {
  909. // Simple top-level schemas get named types (see writeSchemaCode).
  910. // Use the name instead of using the equivalent simple Go type.
  911. return a.schemaNamed(rs.Name).GoName()
  912. }
  913. return a.typeAsGo(rs, elidePointers)
  914. case disco.MapKind:
  915. es := s.ElementSchema()
  916. if es.Type == "string" {
  917. // If the element schema has a type "string", it's going to be
  918. // transmitted as a string, and the Go map type must reflect that.
  919. // This is true even if the format is, say, "int64". When type =
  920. // "string" and format = "int64" at top level, we can use the json
  921. // "string" tag option to unmarshal the string to an int64, but
  922. // inside a map we can't.
  923. return "map[string]string"
  924. }
  925. // Due to historical baggage (maps used to be a separate code path),
  926. // the element types of maps never have pointers in them. From this
  927. // level down, elide pointers in types.
  928. return "map[string]" + a.typeAsGo(es, true)
  929. case disco.AnyStructKind:
  930. return "googleapi.RawMessage"
  931. case disco.StructKind:
  932. tls := a.schemaNamed(s.Name)
  933. if elidePointers || s.Variant != nil {
  934. return tls.GoName()
  935. }
  936. return "*" + tls.GoName()
  937. default:
  938. panic(fmt.Sprintf("unhandled typeAsGo for %+v", s))
  939. }
  940. }
  941. func (a *API) schemaNamed(name string) *Schema {
  942. s := a.schemas[name]
  943. if s == nil {
  944. panicf("no top-level schema named %q", name)
  945. }
  946. return s
  947. }
  948. func (s *Schema) properties() []*Property {
  949. if s.props != nil {
  950. return s.props
  951. }
  952. if s.typ.Kind != disco.StructKind {
  953. panic("called properties on non-object schema")
  954. }
  955. for _, p := range s.typ.Properties {
  956. s.props = append(s.props, &Property{
  957. s: s,
  958. p: p,
  959. })
  960. }
  961. return s.props
  962. }
  963. func (s *Schema) HasContentType() bool {
  964. for _, p := range s.properties() {
  965. if p.GoName() == "ContentType" && p.TypeAsGo() == "string" {
  966. return true
  967. }
  968. }
  969. return false
  970. }
  971. func (s *Schema) populateSubSchemas() (outerr error) {
  972. defer func() {
  973. r := recover()
  974. if r == nil {
  975. return
  976. }
  977. outerr = fmt.Errorf("%v", r)
  978. }()
  979. addSubStruct := func(subApiName string, t *disco.Schema) {
  980. if s.api.schemas[subApiName] != nil {
  981. panic("dup schema apiName: " + subApiName)
  982. }
  983. if t.Name != "" {
  984. panic("subtype already has name: " + t.Name)
  985. }
  986. t.Name = subApiName
  987. subs := &Schema{
  988. api: s.api,
  989. typ: t,
  990. apiName: subApiName,
  991. }
  992. s.api.schemas[subApiName] = subs
  993. err := subs.populateSubSchemas()
  994. if err != nil {
  995. panicf("in sub-struct %q: %v", subApiName, err)
  996. }
  997. }
  998. switch s.typ.Kind {
  999. case disco.StructKind:
  1000. for _, p := range s.properties() {
  1001. subApiName := fmt.Sprintf("%s.%s", s.apiName, p.p.Name)
  1002. switch p.Type().Kind {
  1003. case disco.SimpleKind, disco.ReferenceKind, disco.AnyStructKind:
  1004. // Do nothing.
  1005. case disco.MapKind:
  1006. mt := p.Type().ElementSchema()
  1007. if mt.Kind == disco.SimpleKind || mt.Kind == disco.ReferenceKind {
  1008. continue
  1009. }
  1010. addSubStruct(subApiName, mt)
  1011. case disco.ArrayKind:
  1012. at := p.Type().ElementSchema()
  1013. if at.Kind == disco.SimpleKind || at.Kind == disco.ReferenceKind {
  1014. continue
  1015. }
  1016. addSubStruct(subApiName, at)
  1017. case disco.StructKind:
  1018. addSubStruct(subApiName, p.Type())
  1019. default:
  1020. panicf("Unknown type for %q: %v", subApiName, p.Type())
  1021. }
  1022. }
  1023. case disco.ArrayKind:
  1024. subApiName := fmt.Sprintf("%s.Item", s.apiName)
  1025. switch at := s.typ.ElementSchema(); at.Kind {
  1026. case disco.SimpleKind, disco.ReferenceKind, disco.AnyStructKind:
  1027. // Do nothing.
  1028. case disco.MapKind:
  1029. mt := at.ElementSchema()
  1030. if k := mt.Kind; k != disco.SimpleKind && k != disco.ReferenceKind {
  1031. addSubStruct(subApiName, mt)
  1032. }
  1033. case disco.ArrayKind:
  1034. at := at.ElementSchema()
  1035. if k := at.Kind; k != disco.SimpleKind && k != disco.ReferenceKind {
  1036. addSubStruct(subApiName, at)
  1037. }
  1038. case disco.StructKind:
  1039. addSubStruct(subApiName, at)
  1040. default:
  1041. panicf("Unknown array type for %q: %v", subApiName, at)
  1042. }
  1043. case disco.AnyStructKind, disco.MapKind, disco.SimpleKind, disco.ReferenceKind:
  1044. // Do nothing.
  1045. default:
  1046. fmt.Fprintf(os.Stderr, "in populateSubSchemas, schema is: %v", s.typ)
  1047. panicf("populateSubSchemas: unsupported type for schema %q", s.apiName)
  1048. panic("unreachable")
  1049. }
  1050. return nil
  1051. }
  1052. // GoName returns (or creates and returns) the bare Go name
  1053. // of the apiName, making sure that it's a proper Go identifier
  1054. // and doesn't conflict with an existing name.
  1055. func (s *Schema) GoName() string {
  1056. if s.goName == "" {
  1057. if s.typ.Kind == disco.MapKind {
  1058. s.goName = s.api.typeAsGo(s.typ, false)
  1059. } else {
  1060. base := initialCap(s.apiName)
  1061. s.goName = s.api.GetName(base)
  1062. if base == "Service" && s.goName != "Service" {
  1063. // Detect the case where a resource is going to clash with the
  1064. // root service object.
  1065. panicf("Clash on name Service")
  1066. }
  1067. }
  1068. }
  1069. return s.goName
  1070. }
  1071. // GoReturnType returns the Go type to use as the return type.
  1072. // If a type is a struct, it will return *StructType,
  1073. // for a map it will return map[string]ValueType,
  1074. // for (not yet supported) slices it will return []ValueType.
  1075. func (s *Schema) GoReturnType() string {
  1076. if s.goReturnType == "" {
  1077. if s.typ.Kind == disco.MapKind {
  1078. s.goReturnType = s.GoName()
  1079. } else {
  1080. s.goReturnType = "*" + s.GoName()
  1081. }
  1082. }
  1083. return s.goReturnType
  1084. }
  1085. func (s *Schema) writeSchemaCode(api *API) {
  1086. switch s.typ.Kind {
  1087. case disco.SimpleKind:
  1088. apitype := s.typ.Type
  1089. typ := mustSimpleTypeConvert(apitype, s.typ.Format)
  1090. s.api.pn("\ntype %s %s", s.GoName(), typ)
  1091. case disco.StructKind:
  1092. s.writeSchemaStruct(api)
  1093. case disco.MapKind, disco.AnyStructKind:
  1094. // Do nothing.
  1095. case disco.ArrayKind:
  1096. log.Printf("TODO writeSchemaCode for arrays for %s", s.GoName())
  1097. default:
  1098. fmt.Fprintf(os.Stderr, "in writeSchemaCode, schema is: %+v", s.typ)
  1099. panicf("writeSchemaCode: unsupported type for schema %q", s.apiName)
  1100. }
  1101. }
  1102. func (s *Schema) writeVariant(api *API, v *disco.Variant) {
  1103. s.api.p("\ntype %s map[string]interface{}\n\n", s.GoName())
  1104. // Write out the "Type" method that identifies the variant type.
  1105. s.api.pn("func (t %s) Type() string {", s.GoName())
  1106. s.api.pn(" return googleapi.VariantType(t)")
  1107. s.api.p("}\n\n")
  1108. // Write out helper methods to convert each possible variant.
  1109. for _, m := range v.Map {
  1110. if m.TypeValue == "" && m.Ref == "" {
  1111. log.Printf("TODO variant %s ref %s not yet supported.", m.TypeValue, m.Ref)
  1112. continue
  1113. }
  1114. s.api.pn("func (t %s) %s() (r %s, ok bool) {", s.GoName(), initialCap(m.TypeValue), m.Ref)
  1115. s.api.pn(" if t.Type() != %q {", initialCap(m.TypeValue))
  1116. s.api.pn(" return r, false")
  1117. s.api.pn(" }")
  1118. s.api.pn(" ok = googleapi.ConvertVariant(map[string]interface{}(t), &r)")
  1119. s.api.pn(" return r, ok")
  1120. s.api.p("}\n\n")
  1121. }
  1122. }
  1123. func (s *Schema) Description() string {
  1124. return s.typ.Description
  1125. }
  1126. func (s *Schema) writeSchemaStruct(api *API) {
  1127. if v := s.typ.Variant; v != nil {
  1128. s.writeVariant(api, v)
  1129. return
  1130. }
  1131. s.api.p("\n")
  1132. des := s.Description()
  1133. if des != "" {
  1134. s.api.p("%s", asComment("", fmt.Sprintf("%s: %s", s.GoName(), des)))
  1135. }
  1136. s.api.pn("type %s struct {", s.GoName())
  1137. np := new(namePool)
  1138. forceSendName := np.Get("ForceSendFields")
  1139. nullFieldsName := np.Get("NullFields")
  1140. if s.isResponseType() {
  1141. np.Get("ServerResponse") // reserve the name
  1142. }
  1143. firstFieldName := "" // used to store a struct field name for use in documentation.
  1144. for i, p := range s.properties() {
  1145. if i > 0 {
  1146. s.api.p("\n")
  1147. }
  1148. pname := np.Get(p.GoName())
  1149. if pname[0] == '@' {
  1150. // HACK(cbro): ignore JSON-LD special fields until we can figure out
  1151. // the correct Go representation for them.
  1152. continue
  1153. }
  1154. p.assignedGoName = pname
  1155. des := p.Description()
  1156. if des != "" {
  1157. s.api.p("%s", asComment("\t", fmt.Sprintf("%s: %s", pname, des)))
  1158. }
  1159. addFieldValueComments(s.api.p, p, "\t", des != "")
  1160. var extraOpt string
  1161. if p.Type().IsIntAsString() {
  1162. extraOpt += ",string"
  1163. }
  1164. typ := p.TypeAsGo()
  1165. if p.forcePointerType() {
  1166. typ = "*" + typ
  1167. }
  1168. s.api.pn(" %s %s `json:\"%s,omitempty%s\"`", pname, typ, p.p.Name, extraOpt)
  1169. if firstFieldName == "" {
  1170. firstFieldName = pname
  1171. }
  1172. }
  1173. if s.isResponseType() {
  1174. if firstFieldName != "" {
  1175. s.api.p("\n")
  1176. }
  1177. s.api.p("%s", asComment("\t", "ServerResponse contains the HTTP response code and headers from the server."))
  1178. s.api.pn(" googleapi.ServerResponse `json:\"-\"`")
  1179. }
  1180. if firstFieldName == "" {
  1181. // There were no fields in the struct, so there is no point
  1182. // adding any custom JSON marshaling code.
  1183. s.api.pn("}")
  1184. return
  1185. }
  1186. commentFmtStr := "%s is a list of field names (e.g. %q) to " +
  1187. "unconditionally include in API requests. By default, fields " +
  1188. "with empty values are omitted from API requests. However, " +
  1189. "any non-pointer, non-interface field appearing in %s will " +
  1190. "be sent to the server regardless of whether the field is " +
  1191. "empty or not. This may be used to include empty fields in " +
  1192. "Patch requests."
  1193. comment := fmt.Sprintf(commentFmtStr, forceSendName, firstFieldName, forceSendName)
  1194. s.api.p("\n")
  1195. s.api.p("%s", asComment("\t", comment))
  1196. s.api.pn("\t%s []string `json:\"-\"`", forceSendName)
  1197. commentFmtStr = "%s is a list of field names (e.g. %q) to " +
  1198. "include in API requests with the JSON null value. " +
  1199. "By default, fields with empty values are omitted from API requests. However, " +
  1200. "any field with an empty value appearing in %s will be sent to the server as null. " +
  1201. "It is an error if a field in this list has a non-empty value. This may be used to " +
  1202. "include null fields in Patch requests."
  1203. comment = fmt.Sprintf(commentFmtStr, nullFieldsName, firstFieldName, nullFieldsName)
  1204. s.api.p("\n")
  1205. s.api.p("%s", asComment("\t", comment))
  1206. s.api.pn("\t%s []string `json:\"-\"`", nullFieldsName)
  1207. s.api.pn("}")
  1208. s.writeSchemaMarshal(forceSendName, nullFieldsName)
  1209. s.writeSchemaUnmarshal()
  1210. }
  1211. // writeSchemaMarshal writes a custom MarshalJSON function for s, which allows
  1212. // fields to be explicitly transmitted by listing them in the field identified
  1213. // by forceSendFieldName, and allows fields to be transmitted with the null value
  1214. // by listing them in the field identified by nullFieldsName.
  1215. func (s *Schema) writeSchemaMarshal(forceSendFieldName, nullFieldsName string) {
  1216. s.api.pn("func (s *%s) MarshalJSON() ([]byte, error) {", s.GoName())
  1217. s.api.pn("\ttype NoMethod %s", s.GoName())
  1218. // pass schema as methodless type to prevent subsequent calls to MarshalJSON from recursing indefinitely.
  1219. s.api.pn("\traw := NoMethod(*s)")
  1220. s.api.pn("\treturn gensupport.MarshalJSON(raw, s.%s, s.%s)", forceSendFieldName, nullFieldsName)
  1221. s.api.pn("}")
  1222. }
  1223. func (s *Schema) writeSchemaUnmarshal() {
  1224. var floatProps []*Property
  1225. for _, p := range s.properties() {
  1226. if p.p.Schema.Type == "number" {
  1227. floatProps = append(floatProps, p)
  1228. }
  1229. }
  1230. if len(floatProps) == 0 {
  1231. return
  1232. }
  1233. pn := s.api.pn
  1234. pn("\nfunc (s *%s) UnmarshalJSON(data []byte) error {", s.GoName())
  1235. pn(" type NoMethod %s", s.GoName()) // avoid infinite recursion
  1236. pn(" var s1 struct {")
  1237. // Hide the float64 fields of the schema with fields that correctly
  1238. // unmarshal special values.
  1239. for _, p := range floatProps {
  1240. typ := "gensupport.JSONFloat64"
  1241. if p.forcePointerType() {
  1242. typ = "*" + typ
  1243. }
  1244. pn("%s %s `json:\"%s\"`", p.assignedGoName, typ, p.p.Name)
  1245. }
  1246. pn(" *NoMethod") // embed the schema
  1247. pn(" }")
  1248. // Set the schema value into the wrapper so its other fields are unmarshaled.
  1249. pn(" s1.NoMethod = (*NoMethod)(s)")
  1250. pn(" if err := json.Unmarshal(data, &s1); err != nil {")
  1251. pn(" return err")
  1252. pn(" }")
  1253. // Copy each shadowing field into the field it shadows.
  1254. for _, p := range floatProps {
  1255. n := p.assignedGoName
  1256. if p.forcePointerType() {
  1257. pn("if s1.%s != nil { s.%s = (*float64)(s1.%s) }", n, n, n)
  1258. } else {
  1259. pn("s.%s = float64(s1.%s)", n, n)
  1260. }
  1261. }
  1262. pn(" return nil")
  1263. pn("}")
  1264. }
  1265. // isResponseType returns true for all types that are used as a response.
  1266. func (s *Schema) isResponseType() bool {
  1267. return s.api.responseTypes["*"+s.goName]
  1268. }
  1269. // PopulateSchemas reads all the API types ("schemas") from the JSON file
  1270. // and converts them to *Schema instances, returning an identically
  1271. // keyed map, additionally containing subresources. For instance,
  1272. //
  1273. // A resource "Foo" of type "object" with a property "bar", also of type
  1274. // "object" (an anonymous sub-resource), will get a synthetic API name
  1275. // of "Foo.bar".
  1276. //
  1277. // A resource "Foo" of type "array" with an "items" of type "object"
  1278. // will get a synthetic API name of "Foo.Item".
  1279. func (a *API) PopulateSchemas() {
  1280. if a.schemas != nil {
  1281. panic("")
  1282. }
  1283. a.schemas = make(map[string]*Schema)
  1284. for name, ds := range a.doc.Schemas {
  1285. s := &Schema{
  1286. api: a,
  1287. apiName: name,
  1288. typ: ds,
  1289. }
  1290. a.schemas[name] = s
  1291. err := s.populateSubSchemas()
  1292. if err != nil {
  1293. panicf("Error populating schema with API name %q: %v", name, err)
  1294. }
  1295. }
  1296. }
  1297. func (a *API) generateResource(r *disco.Resource) {
  1298. pn := a.pn
  1299. t := resourceGoType(r)
  1300. pn(fmt.Sprintf("func New%s(s *%s) *%s {", t, a.ServiceType(), t))
  1301. pn("rs := &%s{s : s}", t)
  1302. for _, res := range r.Resources {
  1303. pn("rs.%s = New%s(s)", resourceGoField(res, r), resourceGoType(res))
  1304. }
  1305. pn("return rs")
  1306. pn("}")
  1307. pn("\ntype %s struct {", t)
  1308. pn(" s *%s", a.ServiceType())
  1309. for _, res := range r.Resources {
  1310. pn("\n\t%s\t*%s", resourceGoField(res, r), resourceGoType(res))
  1311. }
  1312. pn("}")
  1313. for _, res := range r.Resources {
  1314. a.generateResource(res)
  1315. }
  1316. }
  1317. func (a *API) cacheResourceResponseTypes(r *disco.Resource) {
  1318. for _, meth := range a.resourceMethods(r) {
  1319. meth.cacheResponseTypes(a)
  1320. }
  1321. for _, res := range r.Resources {
  1322. a.cacheResourceResponseTypes(res)
  1323. }
  1324. }
  1325. func (a *API) generateResourceMethods(r *disco.Resource) {
  1326. for _, meth := range a.resourceMethods(r) {
  1327. meth.generateCode()
  1328. }
  1329. for _, res := range r.Resources {
  1330. a.generateResourceMethods(res)
  1331. }
  1332. }
  1333. func resourceGoField(r, parent *disco.Resource) string {
  1334. // Avoid conflicts with method names.
  1335. und := ""
  1336. if parent != nil {
  1337. for _, m := range parent.Methods {
  1338. if m.Name == r.Name {
  1339. und = "_"
  1340. break
  1341. }
  1342. }
  1343. }
  1344. // Note: initialCap(r.Name + "_") doesn't work because initialCap calls depunct.
  1345. return initialCap(r.Name) + und
  1346. }
  1347. func resourceGoType(r *disco.Resource) string {
  1348. return initialCap(r.FullName + "Service")
  1349. }
  1350. func (a *API) resourceMethods(r *disco.Resource) []*Method {
  1351. ms := []*Method{}
  1352. for _, m := range r.Methods {
  1353. ms = append(ms, &Method{
  1354. api: a,
  1355. r: r,
  1356. m: m,
  1357. })
  1358. }
  1359. return ms
  1360. }
  1361. type Method struct {
  1362. api *API
  1363. r *disco.Resource // or nil if a API-level (top-level) method
  1364. m *disco.Method
  1365. params []*Param // all Params, of each type, lazily set by first call of Params method.
  1366. }
  1367. func (m *Method) Id() string {
  1368. return m.m.ID
  1369. }
  1370. func (m *Method) responseType() *Schema {
  1371. return m.api.schemas[m.m.Response.RefSchema.Name]
  1372. }
  1373. func (m *Method) supportsMediaUpload() bool {
  1374. return m.m.MediaUpload != nil
  1375. }
  1376. func (m *Method) mediaUploadPath() string {
  1377. return m.m.MediaUpload.Protocols["simple"].Path
  1378. }
  1379. func (m *Method) supportsMediaDownload() bool {
  1380. if m.supportsMediaUpload() {
  1381. // storage.objects.insert claims support for download in
  1382. // addition to upload but attempting to do so fails.
  1383. // This situation doesn't apply to any other methods.
  1384. return false
  1385. }
  1386. return m.m.SupportsMediaDownload
  1387. }
  1388. func (m *Method) supportsPaging() (*pageTokenGenerator, string, bool) {
  1389. ptg := m.pageTokenGenerator()
  1390. if ptg == nil {
  1391. return nil, "", false
  1392. }
  1393. // Check that the response type has the next page token.
  1394. s := m.responseType()
  1395. if s == nil || s.typ.Kind != disco.StructKind {
  1396. return nil, "", false
  1397. }
  1398. for _, prop := range s.properties() {
  1399. if isPageTokenName(prop.p.Name) && prop.Type().Type == "string" {
  1400. return ptg, prop.GoName(), true
  1401. }
  1402. }
  1403. return nil, "", false
  1404. }
  1405. type pageTokenGenerator struct {
  1406. isParam bool // is the page token a URL parameter?
  1407. name string // param or request field name
  1408. requestName string // empty for URL param
  1409. }
  1410. func (p *pageTokenGenerator) genGet() string {
  1411. if p.isParam {
  1412. return fmt.Sprintf("c.urlParams_.Get(%q)", p.name)
  1413. }
  1414. return fmt.Sprintf("c.%s.%s", p.requestName, p.name)
  1415. }
  1416. func (p *pageTokenGenerator) genSet(valueExpr string) string {
  1417. if p.isParam {
  1418. return fmt.Sprintf("c.%s(%s)", initialCap(p.name), valueExpr)
  1419. }
  1420. return fmt.Sprintf("c.%s.%s = %s", p.requestName, p.name, valueExpr)
  1421. }
  1422. func (p *pageTokenGenerator) genDeferBody() string {
  1423. if p.isParam {
  1424. return p.genSet(p.genGet())
  1425. }
  1426. return fmt.Sprintf("func (pt string) { %s }(%s)", p.genSet("pt"), p.genGet())
  1427. }
  1428. // pageTokenGenerator returns a pageTokenGenerator that will generate code to
  1429. // get/set the page token for a subsequent page in the context of the generated
  1430. // Pages method. It returns nil if there is no page token.
  1431. func (m *Method) pageTokenGenerator() *pageTokenGenerator {
  1432. matches := m.grepParams(func(p *Param) bool { return isPageTokenName(p.p.Name) })
  1433. switch len(matches) {
  1434. case 1:
  1435. if matches[0].p.Required {
  1436. // The page token is a required parameter (e.g. because there is
  1437. // a separate API call to start an iteration), and so the relevant
  1438. // call factory method takes the page token instead.
  1439. return nil
  1440. }
  1441. n := matches[0].p.Name
  1442. return &pageTokenGenerator{true, n, ""}
  1443. case 0: // No URL parameter, but maybe a request field.
  1444. if m.m.Request == nil {
  1445. return nil
  1446. }
  1447. rs := m.m.Request
  1448. if rs.RefSchema != nil {
  1449. rs = rs.RefSchema
  1450. }
  1451. for _, p := range rs.Properties {
  1452. if isPageTokenName(p.Name) {
  1453. return &pageTokenGenerator{false, initialCap(p.Name), validGoIdentifer(strings.ToLower(rs.Name))}
  1454. }
  1455. }
  1456. return nil
  1457. default:
  1458. panicf("too many page token parameters for method %s", m.m.Name)
  1459. return nil
  1460. }
  1461. }
  1462. func isPageTokenName(s string) bool {
  1463. return s == "pageToken" || s == "nextPageToken"
  1464. }
  1465. func (m *Method) Params() []*Param {
  1466. if m.params == nil {
  1467. for _, p := range m.m.Parameters {
  1468. m.params = append(m.params, &Param{
  1469. method: m,
  1470. p: p,
  1471. })
  1472. }
  1473. }
  1474. return m.params
  1475. }
  1476. func (m *Method) grepParams(f func(*Param) bool) []*Param {
  1477. matches := make([]*Param, 0)
  1478. for _, param := range m.Params() {
  1479. if f(param) {
  1480. matches = append(matches, param)
  1481. }
  1482. }
  1483. return matches
  1484. }
  1485. func (m *Method) NamedParam(name string) *Param {
  1486. matches := m.grepParams(func(p *Param) bool {
  1487. return p.p.Name == name
  1488. })
  1489. if len(matches) < 1 {
  1490. log.Panicf("failed to find named parameter %q", name)
  1491. }
  1492. if len(matches) > 1 {
  1493. log.Panicf("found multiple parameters for parameter name %q", name)
  1494. }
  1495. return matches[0]
  1496. }
  1497. func (m *Method) OptParams() []*Param {
  1498. return m.grepParams(func(p *Param) bool {
  1499. return !p.p.Required
  1500. })
  1501. }
  1502. func (meth *Method) cacheResponseTypes(api *API) {
  1503. if retType := responseType(api, meth.m); retType != "" && strings.HasPrefix(retType, "*") {
  1504. api.responseTypes[retType] = true
  1505. }
  1506. }
  1507. // convertMultiParams builds a []string temp variable from a slice
  1508. // of non-strings and returns the name of the temp variable.
  1509. func convertMultiParams(a *API, param string) string {
  1510. a.pn(" var %v_ []string", param)
  1511. a.pn(" for _, v := range %v {", param)
  1512. a.pn(" %v_ = append(%v_, fmt.Sprint(v))", param, param)
  1513. a.pn(" }")
  1514. return param + "_"
  1515. }
  1516. func (meth *Method) generateCode() {
  1517. res := meth.r // may be nil if a top-level method
  1518. a := meth.api
  1519. p, pn := a.p, a.pn
  1520. pn("\n// method id %q:", meth.Id())
  1521. retType := responseType(a, meth.m)
  1522. retTypeComma := retType
  1523. if retTypeComma != "" {
  1524. retTypeComma += ", "
  1525. }
  1526. args := meth.NewArguments()
  1527. methodName := initialCap(meth.m.Name)
  1528. prefix := ""
  1529. if res != nil {
  1530. prefix = initialCap(res.FullName)
  1531. }
  1532. callName := a.GetName(prefix + methodName + "Call")
  1533. pn("\ntype %s struct {", callName)
  1534. pn(" s *%s", a.ServiceType())
  1535. for _, arg := range args.l {
  1536. if arg.location != "query" {
  1537. pn(" %s %s", arg.goname, arg.gotype)
  1538. }
  1539. }
  1540. pn(" urlParams_ gensupport.URLParams")
  1541. httpMethod := meth.m.HTTPMethod
  1542. if httpMethod == "GET" {
  1543. pn(" ifNoneMatch_ string")
  1544. }
  1545. if meth.supportsMediaUpload() {
  1546. pn(" mediaInfo_ *gensupport.MediaInfo")
  1547. }
  1548. pn(" ctx_ context.Context")
  1549. pn(" header_ http.Header")
  1550. pn("}")
  1551. p("\n%s", asComment("", methodName+": "+meth.m.Description))
  1552. if res != nil {
  1553. if url := canonicalDocsURL[fmt.Sprintf("%v%v/%v", docsLink, res.Name, meth.m.Name)]; url != "" {
  1554. pn("// For details, see %v", url)
  1555. }
  1556. }
  1557. var servicePtr string
  1558. if res == nil {
  1559. pn("func (s *Service) %s(%s) *%s {", methodName, args, callName)
  1560. servicePtr = "s"
  1561. } else {
  1562. pn("func (r *%s) %s(%s) *%s {", resourceGoType(res), methodName, args, callName)
  1563. servicePtr = "r.s"
  1564. }
  1565. pn(" c := &%s{s: %s, urlParams_: make(gensupport.URLParams)}", callName, servicePtr)
  1566. for _, arg := range args.l {
  1567. // TODO(gmlewis): clean up and consolidate this section.
  1568. // See: https://code-review.googlesource.com/#/c/3520/18/google-api-go-generator/gen.go
  1569. if arg.location == "query" {
  1570. switch arg.gotype {
  1571. case "[]string":
  1572. pn(" c.urlParams_.SetMulti(%q, append([]string{}, %v...))", arg.apiname, arg.goname)
  1573. case "string":
  1574. pn(" c.urlParams_.Set(%q, %v)", arg.apiname, arg.goname)
  1575. default:
  1576. if strings.HasPrefix(arg.gotype, "[]") {
  1577. tmpVar := convertMultiParams(a, arg.goname)
  1578. pn(" c.urlParams_.SetMulti(%q, %v)", arg.apiname, tmpVar)
  1579. } else {
  1580. pn(" c.urlParams_.Set(%q, fmt.Sprint(%v))", arg.apiname, arg.goname)
  1581. }
  1582. }
  1583. continue
  1584. }
  1585. if arg.gotype == "[]string" {
  1586. pn(" c.%s = append([]string{}, %s...)", arg.goname, arg.goname) // Make a copy of the []string.
  1587. continue
  1588. }
  1589. pn(" c.%s = %s", arg.goname, arg.goname)
  1590. }
  1591. pn(" return c")
  1592. pn("}")
  1593. for _, opt := range meth.OptParams() {
  1594. if opt.p.Location != "query" {
  1595. panicf("optional parameter has unsupported location %q", opt.p.Location)
  1596. }
  1597. setter := initialCap(opt.p.Name)
  1598. des := opt.p.Description
  1599. des = strings.Replace(des, "Optional.", "", 1)
  1600. des = strings.TrimSpace(des)
  1601. p("\n%s", asComment("", fmt.Sprintf("%s sets the optional parameter %q: %s", setter, opt.p.Name, des)))
  1602. addFieldValueComments(p, opt, "", true)
  1603. np := new(namePool)
  1604. np.Get("c") // take the receiver's name
  1605. paramName := np.Get(validGoIdentifer(opt.p.Name))
  1606. typePrefix := ""
  1607. if opt.p.Repeated {
  1608. typePrefix = "..."
  1609. }
  1610. pn("func (c *%s) %s(%s %s%s) *%s {", callName, setter, paramName, typePrefix, opt.GoType(), callName)
  1611. if opt.p.Repeated {
  1612. if opt.GoType() == "string" {
  1613. pn("c.urlParams_.SetMulti(%q, append([]string{}, %v...))", opt.p.Name, paramName)
  1614. } else {
  1615. tmpVar := convertMultiParams(a, paramName)
  1616. pn(" c.urlParams_.SetMulti(%q, %v)", opt.p.Name, tmpVar)
  1617. }
  1618. } else {
  1619. if opt.GoType() == "string" {
  1620. pn("c.urlParams_.Set(%q, %v)", opt.p.Name, paramName)
  1621. } else {
  1622. pn("c.urlParams_.Set(%q, fmt.Sprint(%v))", opt.p.Name, paramName)
  1623. }
  1624. }
  1625. pn("return c")
  1626. pn("}")
  1627. }
  1628. if meth.supportsMediaUpload() {
  1629. comment := "Media specifies the media to upload in one or more chunks. " +
  1630. "The chunk size may be controlled by supplying a MediaOption generated by googleapi.ChunkSize. " +
  1631. "The chunk size defaults to googleapi.DefaultUploadChunkSize." +
  1632. "The Content-Type header used in the upload request will be determined by sniffing the contents of r, " +
  1633. "unless a MediaOption generated by googleapi.ContentType is supplied." +
  1634. "\nAt most one of Media and ResumableMedia may be set."
  1635. // TODO(mcgreevy): Ensure that r is always closed before Do returns, and document this.
  1636. // See comments on https://code-review.googlesource.com/#/c/3970/
  1637. p("\n%s", asComment("", comment))
  1638. pn("func (c *%s) Media(r io.Reader, options ...googleapi.MediaOption) *%s {", callName, callName)
  1639. // We check if the body arg, if any, has a content type and apply it here.
  1640. // In practice, this only happens for the storage API today.
  1641. // TODO(djd): check if we can cope with the developer setting the body's Content-Type field
  1642. // after they've made this call.
  1643. if ba := args.bodyArg(); ba != nil {
  1644. if ba.schema.HasContentType() {
  1645. pn(" if ct := c.%s.ContentType; ct != \"\" {", ba.goname)
  1646. pn(" options = append([]googleapi.MediaOption{googleapi.ContentType(ct)}, options...)")
  1647. pn(" }")
  1648. }
  1649. }
  1650. pn(" c.mediaInfo_ = gensupport.NewInfoFromMedia(r, options)")
  1651. pn(" return c")
  1652. pn("}")
  1653. comment = "ResumableMedia specifies the media to upload in chunks and can be canceled with ctx. " +
  1654. "\n\nDeprecated: use Media instead." +
  1655. "\n\nAt most one of Media and ResumableMedia may be set. " +
  1656. `mediaType identifies the MIME media type of the upload, such as "image/png". ` +
  1657. `If mediaType is "", it will be auto-detected. ` +
  1658. `The provided ctx will supersede any context previously provided to ` +
  1659. `the Context method.`
  1660. p("\n%s", asComment("", comment))
  1661. pn("func (c *%s) ResumableMedia(ctx context.Context, r io.ReaderAt, size int64, mediaType string) *%s {", callName, callName)
  1662. pn(" c.ctx_ = ctx")
  1663. pn(" c.mediaInfo_ = gensupport.NewInfoFromResumableMedia(r, size, mediaType)")
  1664. pn(" return c")
  1665. pn("}")
  1666. comment = "ProgressUpdater provides a callback function that will be called after every chunk. " +
  1667. "It should be a low-latency function in order to not slow down the upload operation. " +
  1668. "This should only be called when using ResumableMedia (as opposed to Media)."
  1669. p("\n%s", asComment("", comment))
  1670. pn("func (c *%s) ProgressUpdater(pu googleapi.ProgressUpdater) *%s {", callName, callName)
  1671. pn(`c.mediaInfo_.SetProgressUpdater(pu)`)
  1672. pn("return c")
  1673. pn("}")
  1674. }
  1675. comment := "Fields allows partial responses to be retrieved. " +
  1676. "See https://developers.google.com/gdata/docs/2.0/basics#PartialResponse " +
  1677. "for more information."
  1678. p("\n%s", asComment("", comment))
  1679. pn("func (c *%s) Fields(s ...googleapi.Field) *%s {", callName, callName)
  1680. pn(`c.urlParams_.Set("fields", googleapi.CombineFields(s))`)
  1681. pn("return c")
  1682. pn("}")
  1683. if httpMethod == "GET" {
  1684. // Note that non-GET responses are excluded from supporting If-None-Match.
  1685. // See https://github.com/google/google-api-go-client/issues/107 for more info.
  1686. comment := "IfNoneMatch sets the optional parameter which makes the operation fail if " +
  1687. "the object's ETag matches the given value. This is useful for getting updates " +
  1688. "only after the object has changed since the last request. " +
  1689. "Use googleapi.IsNotModified to check whether the response error from Do " +
  1690. "is the result of In-None-Match."
  1691. p("\n%s", asComment("", comment))
  1692. pn("func (c *%s) IfNoneMatch(entityTag string) *%s {", callName, callName)
  1693. pn(" c.ifNoneMatch_ = entityTag")
  1694. pn(" return c")
  1695. pn("}")
  1696. }
  1697. doMethod := "Do method"
  1698. if meth.supportsMediaDownload() {
  1699. doMethod = "Do and Download methods"
  1700. }
  1701. commentFmtStr := "Context sets the context to be used in this call's %s. " +
  1702. "Any pending HTTP request will be aborted if the provided context is canceled."
  1703. comment = fmt.Sprintf(commentFmtStr, doMethod)
  1704. p("\n%s", asComment("", comment))
  1705. if meth.supportsMediaUpload() {
  1706. comment = "This context will supersede any context previously provided to " +
  1707. "the ResumableMedia method."
  1708. p("%s", asComment("", comment))
  1709. }
  1710. pn("func (c *%s) Context(ctx context.Context) *%s {", callName, callName)
  1711. pn(`c.ctx_ = ctx`)
  1712. pn("return c")
  1713. pn("}")
  1714. comment = "Header returns an http.Header that can be modified by the caller to add " +
  1715. "HTTP headers to the request."
  1716. p("\n%s", asComment("", comment))
  1717. pn("func (c *%s) Header() http.Header {", callName)
  1718. pn(" if c.header_ == nil {")
  1719. pn(" c.header_ = make(http.Header)")
  1720. pn(" }")
  1721. pn(" return c.header_")
  1722. pn("}")
  1723. pn("\nfunc (c *%s) doRequest(alt string) (*http.Response, error) {", callName)
  1724. pn(`reqHeaders := make(http.Header)`)
  1725. pn("for k, v := range c.header_ {")
  1726. pn(" reqHeaders[k] = v")
  1727. pn("}")
  1728. pn(`reqHeaders.Set("User-Agent",c.s.userAgent())`)
  1729. if httpMethod == "GET" {
  1730. pn(`if c.ifNoneMatch_ != "" {`)
  1731. pn(` reqHeaders.Set("If-None-Match", c.ifNoneMatch_)`)
  1732. pn("}")
  1733. }
  1734. pn("var body io.Reader = nil")
  1735. if ba := args.bodyArg(); ba != nil && httpMethod != "GET" {
  1736. if meth.m.ID == "ml.projects.predict" {
  1737. // Skip JSONReader for APIs that require clients to pass in JSON already.
  1738. pn("body = strings.NewReader(c.%s.HttpBody.Data)", ba.goname)
  1739. } else {
  1740. style := "WithoutDataWrapper"
  1741. if a.needsDataWrapper() {
  1742. style = "WithDataWrapper"
  1743. }
  1744. pn("body, err := googleapi.%s.JSONReader(c.%s)", style, ba.goname)
  1745. pn("if err != nil { return nil, err }")
  1746. }
  1747. pn(`reqHeaders.Set("Content-Type", "application/json")`)
  1748. }
  1749. pn(`c.urlParams_.Set("alt", alt)`)
  1750. pn(`c.urlParams_.Set("prettyPrint", "false")`)
  1751. pn("urls := googleapi.ResolveRelative(c.s.BasePath, %q)", meth.m.Path)
  1752. if meth.supportsMediaUpload() {
  1753. pn("if c.mediaInfo_ != nil {")
  1754. // Hack guess, since we get a 404 otherwise:
  1755. //pn("urls = googleapi.ResolveRelative(%q, %q)", a.apiBaseURL(), meth.mediaUploadPath())
  1756. // Further hack. Discovery doc is wrong?
  1757. pn(" urls = strings.Replace(urls, %q, %q, 1)", "https://www.googleapis.com/", "https://www.googleapis.com/upload/")
  1758. pn(` c.urlParams_.Set("uploadType", c.mediaInfo_.UploadType())`)
  1759. pn("}")
  1760. pn("if body == nil {")
  1761. pn(" body = new(bytes.Buffer)")
  1762. pn(` reqHeaders.Set("Content-Type", "application/json")`)
  1763. pn("}")
  1764. pn("body, getBody, cleanup := c.mediaInfo_.UploadRequest(reqHeaders, body)")
  1765. pn("defer cleanup()")
  1766. }
  1767. pn("urls += \"?\" + c.urlParams_.Encode()")
  1768. pn("req, err := http.NewRequest(%q, urls, body)", httpMethod)
  1769. pn("if err != nil { return nil, err }")
  1770. pn("req.Header = reqHeaders")
  1771. if meth.supportsMediaUpload() {
  1772. pn("req.GetBody = getBody")
  1773. }
  1774. // Replace param values after NewRequest to avoid reencoding them.
  1775. // E.g. Cloud Storage API requires '%2F' in entity param to be kept, but url.Parse replaces it with '/'.
  1776. argsForLocation := args.forLocation("path")
  1777. if len(argsForLocation) > 0 {
  1778. pn(`googleapi.Expand(req.URL, map[string]string{`)
  1779. for _, arg := range argsForLocation {
  1780. pn(`"%s": %s,`, arg.apiname, arg.exprAsString("c."))
  1781. }
  1782. pn(`})`)
  1783. }
  1784. pn("return gensupport.SendRequest(c.ctx_, c.s.client, req)")
  1785. pn("}")
  1786. if meth.supportsMediaDownload() {
  1787. pn("\n// Download fetches the API endpoint's \"media\" value, instead of the normal")
  1788. pn("// API response value. If the returned error is nil, the Response is guaranteed to")
  1789. pn("// have a 2xx status code. Callers must close the Response.Body as usual.")
  1790. pn("func (c *%s) Download(opts ...googleapi.CallOption) (*http.Response, error) {", callName)
  1791. pn(`gensupport.SetOptions(c.urlParams_, opts...)`)
  1792. pn(`res, err := c.doRequest("media")`)
  1793. pn("if err != nil { return nil, err }")
  1794. pn("if err := googleapi.CheckMediaResponse(res); err != nil {")
  1795. pn("res.Body.Close()")
  1796. pn("return nil, err")
  1797. pn("}")
  1798. pn("return res, nil")
  1799. pn("}")
  1800. }
  1801. mapRetType := strings.HasPrefix(retTypeComma, "map[")
  1802. pn("\n// Do executes the %q call.", meth.m.ID)
  1803. if retTypeComma != "" && !mapRetType {
  1804. commentFmtStr := "Exactly one of %v or error will be non-nil. " +
  1805. "Any non-2xx status code is an error. " +
  1806. "Response headers are in either %v.ServerResponse.Header " +
  1807. "or (if a response was returned at all) in error.(*googleapi.Error).Header. " +
  1808. "Use googleapi.IsNotModified to check whether the returned error was because " +
  1809. "http.StatusNotModified was returned."
  1810. comment := fmt.Sprintf(commentFmtStr, retType, retType)
  1811. p("%s", asComment("", comment))
  1812. }
  1813. pn("func (c *%s) Do(opts ...googleapi.CallOption) (%serror) {", callName, retTypeComma)
  1814. nilRet := ""
  1815. if retTypeComma != "" {
  1816. nilRet = "nil, "
  1817. }
  1818. pn(`gensupport.SetOptions(c.urlParams_, opts...)`)
  1819. pn(`res, err := c.doRequest("json")`)
  1820. if retTypeComma != "" && !mapRetType {
  1821. pn("if res != nil && res.StatusCode == http.StatusNotModified {")
  1822. pn(" if res.Body != nil { res.Body.Close() }")
  1823. pn(" return nil, &googleapi.Error{")
  1824. pn(" Code: res.StatusCode,")
  1825. pn(" Header: res.Header,")
  1826. pn(" }")
  1827. pn("}")
  1828. }
  1829. pn("if err != nil { return %serr }", nilRet)
  1830. pn("defer googleapi.CloseBody(res)")
  1831. pn("if err := googleapi.CheckResponse(res); err != nil { return %serr }", nilRet)
  1832. if meth.supportsMediaUpload() {
  1833. pn(`rx := c.mediaInfo_.ResumableUpload(res.Header.Get("Location"))`)
  1834. pn("if rx != nil {")
  1835. pn(" rx.Client = c.s.client")
  1836. pn(" rx.UserAgent = c.s.userAgent()")
  1837. pn(" ctx := c.ctx_")
  1838. pn(" if ctx == nil {")
  1839. // TODO(mcgreevy): Require context when calling Media, or Do.
  1840. pn(" ctx = context.TODO()")
  1841. pn(" }")
  1842. pn(" res, err = rx.Upload(ctx)")
  1843. pn(" if err != nil { return %serr }", nilRet)
  1844. pn(" defer res.Body.Close()")
  1845. pn(" if err := googleapi.CheckResponse(res); err != nil { return %serr }", nilRet)
  1846. pn("}")
  1847. }
  1848. if retTypeComma == "" {
  1849. pn("return nil")
  1850. } else {
  1851. if mapRetType {
  1852. pn("var ret %s", responseType(a, meth.m))
  1853. } else {
  1854. pn("ret := &%s{", responseTypeLiteral(a, meth.m))
  1855. pn(" ServerResponse: googleapi.ServerResponse{")
  1856. pn(" Header: res.Header,")
  1857. pn(" HTTPStatusCode: res.StatusCode,")
  1858. pn(" },")
  1859. pn("}")
  1860. }
  1861. if a.needsDataWrapper() {
  1862. pn("target := &struct {")
  1863. pn(" Data %s `json:\"data\"`", responseType(a, meth.m))
  1864. pn("}{ret}")
  1865. } else {
  1866. pn("target := &ret")
  1867. }
  1868. if meth.m.ID == "ml.projects.predict" {
  1869. pn("var b bytes.Buffer")
  1870. pn("if _, err := io.Copy(&b, res.Body); err != nil { return nil, err }")
  1871. pn("if err := res.Body.Close(); err != nil { return nil, err }")
  1872. pn("if err := json.NewDecoder(bytes.NewReader(b.Bytes())).Decode(target); err != nil { return nil, err }")
  1873. pn("ret.Data = b.String()")
  1874. } else {
  1875. pn("if err := gensupport.DecodeResponse(target, res); err != nil { return nil, err }")
  1876. }
  1877. pn("return ret, nil")
  1878. }
  1879. bs, err := json.MarshalIndent(meth.m.JSONMap, "\t// ", " ")
  1880. if err != nil {
  1881. panic(err)
  1882. }
  1883. pn("// %s\n", string(bs))
  1884. pn("}")
  1885. if ptg, rname, ok := meth.supportsPaging(); ok {
  1886. // We can assume retType is non-empty.
  1887. pn("")
  1888. pn("// Pages invokes f for each page of results.")
  1889. pn("// A non-nil error returned from f will halt the iteration.")
  1890. pn("// The provided context supersedes any context provided to the Context method.")
  1891. pn("func (c *%s) Pages(ctx context.Context, f func(%s) error) error {", callName, retType)
  1892. pn(" c.ctx_ = ctx")
  1893. pn(` defer %s // reset paging to original point`, ptg.genDeferBody())
  1894. pn(" for {")
  1895. pn(" x, err := c.Do()")
  1896. pn(" if err != nil { return err }")
  1897. pn(" if err := f(x); err != nil { return err }")
  1898. pn(` if x.%s == "" { return nil }`, rname)
  1899. pn(ptg.genSet("x." + rname))
  1900. pn(" }")
  1901. pn("}")
  1902. }
  1903. }
  1904. // A Field provides methods that describe the characteristics of a Param or Property.
  1905. type Field interface {
  1906. Default() string
  1907. Enum() ([]string, bool)
  1908. EnumDescriptions() []string
  1909. UnfortunateDefault() bool
  1910. }
  1911. type Param struct {
  1912. method *Method
  1913. p *disco.Parameter
  1914. callFieldName string // empty means to use the default
  1915. }
  1916. func (p *Param) Default() string {
  1917. return p.p.Default
  1918. }
  1919. func (p *Param) Enum() ([]string, bool) {
  1920. if e := p.p.Enums; e != nil {
  1921. return e, true
  1922. }
  1923. return nil, false
  1924. }
  1925. func (p *Param) EnumDescriptions() []string {
  1926. return p.p.EnumDescriptions
  1927. }
  1928. func (p *Param) UnfortunateDefault() bool {
  1929. // We do not do anything special for Params with unfortunate defaults.
  1930. return false
  1931. }
  1932. func (p *Param) GoType() string {
  1933. typ, format := p.p.Type, p.p.Format
  1934. if typ == "string" && strings.Contains(format, "int") && p.p.Location != "query" {
  1935. panic("unexpected int parameter encoded as string, not in query: " + p.p.Name)
  1936. }
  1937. t, ok := simpleTypeConvert(typ, format)
  1938. if !ok {
  1939. panic("failed to convert parameter type " + fmt.Sprintf("type=%q, format=%q", typ, format))
  1940. }
  1941. return t
  1942. }
  1943. // goCallFieldName returns the name of this parameter's field in a
  1944. // method's "Call" struct.
  1945. func (p *Param) goCallFieldName() string {
  1946. if p.callFieldName != "" {
  1947. return p.callFieldName
  1948. }
  1949. return validGoIdentifer(p.p.Name)
  1950. }
  1951. // APIMethods returns top-level ("API-level") methods. They don't have an associated resource.
  1952. func (a *API) APIMethods() []*Method {
  1953. meths := []*Method{}
  1954. for _, m := range a.doc.Methods {
  1955. meths = append(meths, &Method{
  1956. api: a,
  1957. r: nil, // to be explicit
  1958. m: m,
  1959. })
  1960. }
  1961. return meths
  1962. }
  1963. func resolveRelative(basestr, relstr string) string {
  1964. u, err := url.Parse(basestr)
  1965. if err != nil {
  1966. panicf("Error parsing base URL %q: %v", basestr, err)
  1967. }
  1968. rel, err := url.Parse(relstr)
  1969. if err != nil {
  1970. panicf("Error parsing relative URL %q: %v", relstr, err)
  1971. }
  1972. u = u.ResolveReference(rel)
  1973. return u.String()
  1974. }
  1975. func (meth *Method) NewArguments() *arguments {
  1976. args := &arguments{
  1977. method: meth,
  1978. m: make(map[string]*argument),
  1979. }
  1980. pnames := meth.m.ParameterOrder
  1981. if len(pnames) == 0 {
  1982. // No parameterOrder; collect required parameters and sort by name.
  1983. for _, reqParam := range meth.grepParams(func(p *Param) bool { return p.p.Required }) {
  1984. pnames = append(pnames, reqParam.p.Name)
  1985. }
  1986. sort.Strings(pnames)
  1987. }
  1988. for _, pname := range pnames {
  1989. arg := meth.NewArg(pname, meth.NamedParam(pname))
  1990. args.AddArg(arg)
  1991. }
  1992. if rs := meth.m.Request; rs != nil {
  1993. args.AddArg(meth.NewBodyArg(rs))
  1994. }
  1995. return args
  1996. }
  1997. func (meth *Method) NewBodyArg(ds *disco.Schema) *argument {
  1998. s := meth.api.schemaNamed(ds.RefSchema.Name)
  1999. return &argument{
  2000. goname: validGoIdentifer(strings.ToLower(ds.Ref)),
  2001. apiname: "REQUEST",
  2002. gotype: "*" + s.GoName(),
  2003. apitype: ds.Ref,
  2004. location: "body",
  2005. schema: s,
  2006. }
  2007. }
  2008. func (meth *Method) NewArg(apiname string, p *Param) *argument {
  2009. apitype := p.p.Type
  2010. des := p.p.Description
  2011. goname := validGoIdentifer(apiname) // but might be changed later, if conflicts
  2012. if strings.Contains(des, "identifier") && !strings.HasSuffix(strings.ToLower(goname), "id") {
  2013. goname += "id" // yay
  2014. p.callFieldName = goname
  2015. }
  2016. gotype := mustSimpleTypeConvert(apitype, p.p.Format)
  2017. if p.p.Repeated {
  2018. gotype = "[]" + gotype
  2019. }
  2020. return &argument{
  2021. apiname: apiname,
  2022. apitype: apitype,
  2023. goname: goname,
  2024. gotype: gotype,
  2025. location: p.p.Location,
  2026. }
  2027. }
  2028. type argument struct {
  2029. method *Method
  2030. schema *Schema // Set if location == "body".
  2031. apiname, apitype string
  2032. goname, gotype string
  2033. location string // "path", "query", "body"
  2034. }
  2035. func (a *argument) String() string {
  2036. return a.goname + " " + a.gotype
  2037. }
  2038. func (a *argument) exprAsString(prefix string) string {
  2039. switch a.gotype {
  2040. case "[]string":
  2041. log.Printf("TODO(bradfitz): only including the first parameter in path query.")
  2042. return prefix + a.goname + `[0]`
  2043. case "string":
  2044. return prefix + a.goname
  2045. case "integer", "int64":
  2046. return "strconv.FormatInt(" + prefix + a.goname + ", 10)"
  2047. case "uint64":
  2048. return "strconv.FormatUint(" + prefix + a.goname + ", 10)"
  2049. case "bool":
  2050. return "strconv.FormatBool(" + prefix + a.goname + ")"
  2051. }
  2052. log.Panicf("unknown type: apitype=%q, gotype=%q", a.apitype, a.gotype)
  2053. return ""
  2054. }
  2055. // arguments are the arguments that a method takes
  2056. type arguments struct {
  2057. l []*argument
  2058. m map[string]*argument
  2059. method *Method
  2060. }
  2061. func (args *arguments) forLocation(loc string) []*argument {
  2062. matches := make([]*argument, 0)
  2063. for _, arg := range args.l {
  2064. if arg.location == loc {
  2065. matches = append(matches, arg)
  2066. }
  2067. }
  2068. return matches
  2069. }
  2070. func (args *arguments) bodyArg() *argument {
  2071. for _, arg := range args.l {
  2072. if arg.location == "body" {
  2073. return arg
  2074. }
  2075. }
  2076. return nil
  2077. }
  2078. func (args *arguments) AddArg(arg *argument) {
  2079. n := 1
  2080. oname := arg.goname
  2081. for {
  2082. _, present := args.m[arg.goname]
  2083. if !present {
  2084. args.m[arg.goname] = arg
  2085. args.l = append(args.l, arg)
  2086. return
  2087. }
  2088. n++
  2089. arg.goname = fmt.Sprintf("%s%d", oname, n)
  2090. }
  2091. }
  2092. func (a *arguments) String() string {
  2093. var buf bytes.Buffer
  2094. for i, arg := range a.l {
  2095. if i != 0 {
  2096. buf.Write([]byte(", "))
  2097. }
  2098. buf.Write([]byte(arg.String()))
  2099. }
  2100. return buf.String()
  2101. }
  2102. var urlRE = regexp.MustCompile(`^http\S+$`)
  2103. func asComment(pfx, c string) string {
  2104. var buf bytes.Buffer
  2105. const maxLen = 70
  2106. r := strings.NewReplacer(
  2107. "\n", "\n"+pfx+"// ",
  2108. "`\"", `"`,
  2109. "\"`", `"`,
  2110. )
  2111. for len(c) > 0 {
  2112. line := c
  2113. if len(line) < maxLen {
  2114. fmt.Fprintf(&buf, "%s// %s\n", pfx, r.Replace(line))
  2115. break
  2116. }
  2117. // Don't break URLs.
  2118. if !urlRE.MatchString(line[:maxLen]) {
  2119. line = line[:maxLen]
  2120. }
  2121. si := strings.LastIndex(line, " ")
  2122. if nl := strings.Index(line, "\n"); nl != -1 && nl < si {
  2123. si = nl
  2124. }
  2125. if si != -1 {
  2126. line = line[:si]
  2127. }
  2128. fmt.Fprintf(&buf, "%s// %s\n", pfx, r.Replace(line))
  2129. c = c[len(line):]
  2130. if si != -1 {
  2131. c = c[1:]
  2132. }
  2133. }
  2134. return buf.String()
  2135. }
  2136. func simpleTypeConvert(apiType, format string) (gotype string, ok bool) {
  2137. // From http://tools.ietf.org/html/draft-zyp-json-schema-03#section-5.1
  2138. switch apiType {
  2139. case "boolean":
  2140. gotype = "bool"
  2141. case "string":
  2142. gotype = "string"
  2143. switch format {
  2144. case "int64", "uint64", "int32", "uint32":
  2145. gotype = format
  2146. }
  2147. case "number":
  2148. gotype = "float64"
  2149. case "integer":
  2150. gotype = "int64"
  2151. case "any":
  2152. gotype = "interface{}"
  2153. }
  2154. return gotype, gotype != ""
  2155. }
  2156. func mustSimpleTypeConvert(apiType, format string) string {
  2157. if gotype, ok := simpleTypeConvert(apiType, format); ok {
  2158. return gotype
  2159. }
  2160. panic(fmt.Sprintf("failed to simpleTypeConvert(%q, %q)", apiType, format))
  2161. }
  2162. func responseType(api *API, m *disco.Method) string {
  2163. if m.Response == nil {
  2164. return ""
  2165. }
  2166. ref := m.Response.Ref
  2167. if ref != "" {
  2168. if s := api.schemas[ref]; s != nil {
  2169. return s.GoReturnType()
  2170. }
  2171. return "*" + ref
  2172. }
  2173. return ""
  2174. }
  2175. // Strips the leading '*' from a type name so that it can be used to create a literal.
  2176. func responseTypeLiteral(api *API, m *disco.Method) string {
  2177. v := responseType(api, m)
  2178. if strings.HasPrefix(v, "*") {
  2179. return v[1:]
  2180. }
  2181. return v
  2182. }
  2183. // initialCap returns the identifier with a leading capital letter.
  2184. // it also maps "foo-bar" to "FooBar".
  2185. func initialCap(ident string) string {
  2186. if ident == "" {
  2187. panic("blank identifier")
  2188. }
  2189. return depunct(ident, true)
  2190. }
  2191. func validGoIdentifer(ident string) string {
  2192. id := depunct(ident, false)
  2193. switch id {
  2194. case "break", "default", "func", "interface", "select",
  2195. "case", "defer", "go", "map", "struct",
  2196. "chan", "else", "goto", "package", "switch",
  2197. "const", "fallthrough", "if", "range", "type",
  2198. "continue", "for", "import", "return", "var":
  2199. return id + "_"
  2200. }
  2201. return id
  2202. }
  2203. // depunct removes '-', '.', '$', '/', '_' from identifers, making the
  2204. // following character uppercase. Multiple '_' are preserved.
  2205. func depunct(ident string, needCap bool) string {
  2206. var buf bytes.Buffer
  2207. preserve_ := false
  2208. for i, c := range ident {
  2209. if c == '_' {
  2210. if preserve_ || strings.HasPrefix(ident[i:], "__") {
  2211. preserve_ = true
  2212. } else {
  2213. needCap = true
  2214. continue
  2215. }
  2216. } else {
  2217. preserve_ = false
  2218. }
  2219. if c == '-' || c == '.' || c == '$' || c == '/' {
  2220. needCap = true
  2221. continue
  2222. }
  2223. if needCap {
  2224. c = unicode.ToUpper(c)
  2225. needCap = false
  2226. }
  2227. buf.WriteByte(byte(c))
  2228. }
  2229. return buf.String()
  2230. }
  2231. func addFieldValueComments(p func(format string, args ...interface{}), field Field, indent string, blankLine bool) {
  2232. var lines []string
  2233. if enum, ok := field.Enum(); ok {
  2234. desc := field.EnumDescriptions()
  2235. lines = append(lines, asComment(indent, "Possible values:"))
  2236. defval := field.Default()
  2237. for i, v := range enum {
  2238. more := ""
  2239. if v == defval {
  2240. more = " (default)"
  2241. }
  2242. if len(desc) > i && desc[i] != "" {
  2243. more = more + " - " + desc[i]
  2244. }
  2245. lines = append(lines, asComment(indent, ` "`+v+`"`+more))
  2246. }
  2247. } else if field.UnfortunateDefault() {
  2248. lines = append(lines, asComment("\t", fmt.Sprintf("Default: %s", field.Default())))
  2249. }
  2250. if blankLine && len(lines) > 0 {
  2251. p(indent + "//\n")
  2252. }
  2253. for _, l := range lines {
  2254. p("%s", l)
  2255. }
  2256. }