PageRenderTime 56ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/google-api-go-generator/gen.go

https://code.google.com/p/google-api-go-client/
Go | 1402 lines | 1191 code | 164 blank | 47 comment | 270 complexity | 44f0dcf49166e23e4bbe3fe874eaa03b MD5 | raw file
Possible License(s): BSD-3-Clause
  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/parser"
  12. "go/printer"
  13. "go/token"
  14. "io/ioutil"
  15. "log"
  16. "net/http"
  17. "net/url"
  18. "os"
  19. "os/exec"
  20. "path/filepath"
  21. "strings"
  22. "unicode"
  23. )
  24. // goGenVersion is the version of the Go code generator
  25. const goGenVersion = "0.5"
  26. var (
  27. apiToGenerate = flag.String("api", "*", "The API ID to generate, like 'tasks:v1'. A value of '*' means all.")
  28. useCache = flag.Bool("cache", true, "Use cache of discovered Google API discovery documents.")
  29. genDir = flag.String("gendir", "", "Directory to use to write out generated Go files and Makefiles")
  30. build = flag.Bool("build", false, "Compile generated packages.")
  31. install = flag.Bool("install", false, "Install generated packages.")
  32. apisURL = flag.String("discoveryurl", "https://www.googleapis.com/discovery/v1/apis", "URL to root discovery document")
  33. publicOnly = flag.Bool("publiconly", true, "Only build public, released APIs. Only applicable for Google employees.")
  34. )
  35. // API represents an API to generate, as well as its state while it's
  36. // generating.
  37. type API struct {
  38. ID string `json:"id"`
  39. Name string `json:"name"`
  40. Version string `json:"version"`
  41. Title string `json:"title"`
  42. DiscoveryLink string `json:"discoveryLink"` // relative
  43. Preferred bool `json:"preferred"`
  44. m map[string]interface{}
  45. usedNames namePool
  46. schemas map[string]*Schema // apiName -> schema
  47. p func(format string, args ...interface{}) // print raw
  48. pn func(format string, args ...interface{}) // print with indent and newline
  49. }
  50. type AllAPIs struct {
  51. Items []*API `json:"items"`
  52. }
  53. type generateError struct {
  54. api *API
  55. error error
  56. }
  57. func (e *generateError) Error() string {
  58. return fmt.Sprintf("API %s failed to generate code: %v", e.api.ID, e.error)
  59. }
  60. type compileError struct {
  61. api *API
  62. output string
  63. }
  64. func (e *compileError) Error() string {
  65. return fmt.Sprintf("API %s failed to compile:\n%v", e.api.ID, e.output)
  66. }
  67. func main() {
  68. flag.Parse()
  69. if *install {
  70. *build = true
  71. }
  72. var (
  73. apiIds = []string{}
  74. matches = []*API{}
  75. errors = []error{}
  76. )
  77. for _, api := range getAPIs() {
  78. apiIds = append(apiIds, api.ID)
  79. if !api.want() {
  80. continue
  81. }
  82. matches = append(matches, api)
  83. log.Printf("Generating API %s", api.ID)
  84. err := api.GenerateCode()
  85. if err != nil {
  86. errors = append(errors, &generateError{api, err})
  87. continue
  88. }
  89. if *build {
  90. var args []string
  91. if *install {
  92. args = append(args, "install")
  93. } else {
  94. args = append(args, "build")
  95. }
  96. args = append(args, "./"+api.SourceDir())
  97. out, err := exec.Command("go", args...).CombinedOutput()
  98. if err != nil {
  99. errors = append(errors, &compileError{api, string(out)})
  100. }
  101. }
  102. }
  103. if len(matches) == 0 {
  104. log.Fatalf("No APIs matched %q; options are %v", *apiToGenerate, apiIds)
  105. }
  106. if len(errors) > 0 {
  107. log.Printf("%d API(s) failed to generate or compile:", len(errors))
  108. for _, ce := range errors {
  109. log.Printf(ce.Error())
  110. }
  111. os.Exit(1)
  112. }
  113. }
  114. func (a *API) want() bool {
  115. if strings.Contains(a.ID, "buzz") {
  116. // R.I.P.
  117. return false
  118. }
  119. return *apiToGenerate == "*" || *apiToGenerate == a.ID
  120. }
  121. func getAPIs() []*API {
  122. var all AllAPIs
  123. disco := slurpURL(*apisURL)
  124. if err := json.Unmarshal(disco, &all); err != nil {
  125. log.Fatalf("error decoding JSON in %s: %v", apisURL, err)
  126. }
  127. return all.Items
  128. }
  129. func writeFile(file string, contents []byte) error {
  130. // Don't write it if the contents are identical.
  131. existing, err := ioutil.ReadFile(file)
  132. if err == nil && bytes.Equal(existing, contents) {
  133. return nil
  134. }
  135. return ioutil.WriteFile(file, contents, 0644)
  136. }
  137. func slurpURL(urlStr string) []byte {
  138. diskFile := filepath.Join(os.TempDir(), "google-api-cache-"+url.QueryEscape(urlStr))
  139. if *useCache {
  140. bs, err := ioutil.ReadFile(diskFile)
  141. if err == nil && len(bs) > 0 {
  142. return bs
  143. }
  144. }
  145. req, err := http.NewRequest("GET", urlStr, nil)
  146. if err != nil {
  147. log.Fatal(err)
  148. }
  149. if *publicOnly {
  150. req.Header.Add("X-User-IP", "0.0.0.0") // hack
  151. }
  152. res, err := http.DefaultClient.Do(req)
  153. if err != nil {
  154. log.Fatalf("Error fetching URL %s: %v", urlStr, err)
  155. }
  156. bs, err := ioutil.ReadAll(res.Body)
  157. if err != nil {
  158. log.Fatalf("Error reading body of URL %s: %v", urlStr, err)
  159. }
  160. if *useCache {
  161. if err := ioutil.WriteFile(diskFile, bs, 0666); err != nil {
  162. log.Printf("Warning: failed to write JSON of %s to disk file %s: %v", urlStr, diskFile, err)
  163. }
  164. }
  165. return bs
  166. }
  167. func panicf(format string, args ...interface{}) {
  168. panic(fmt.Sprintf(format, args...))
  169. }
  170. // namePool keeps track of used names and assigns free ones based on a
  171. // preferred name
  172. type namePool struct {
  173. m map[string]bool // lazily initialized
  174. }
  175. func (p *namePool) Get(preferred string) string {
  176. if p.m == nil {
  177. p.m = make(map[string]bool)
  178. }
  179. name := preferred
  180. tries := 0
  181. for p.m[name] {
  182. tries++
  183. name = fmt.Sprintf("%s%d", preferred, tries)
  184. }
  185. p.m[name] = true
  186. return name
  187. }
  188. func (a *API) SourceDir() string {
  189. return filepath.Join(*genDir, a.Package(), a.Version)
  190. }
  191. func (a *API) DiscoveryURL() string {
  192. if a.DiscoveryLink == "" {
  193. log.Fatalf("API %s has no DiscoveryLink", a.ID)
  194. }
  195. base, _ := url.Parse(*apisURL)
  196. u, err := base.Parse(a.DiscoveryLink)
  197. if err != nil {
  198. log.Fatalf("API %s has bogus DiscoveryLink %s: %v", a.ID, a.DiscoveryLink, err)
  199. }
  200. return u.String()
  201. }
  202. func (a *API) Package() string {
  203. return strings.ToLower(a.Name)
  204. }
  205. func (a *API) Target() string {
  206. return fmt.Sprintf("code.google.com/p/google-api-go-client/%s/%s", a.Package(), a.Version)
  207. }
  208. // GetName returns a free top-level function/type identifier in the package.
  209. // It tries to return your preferred match if it's free.
  210. func (a *API) GetName(preferred string) string {
  211. return a.usedNames.Get(preferred)
  212. }
  213. func (a *API) apiBaseURL() string {
  214. return resolveRelative(*apisURL, jstr(a.m, "basePath"))
  215. }
  216. func (a *API) needsDataWrapper() bool {
  217. for _, feature := range jstrlist(a.m, "features") {
  218. if feature == "dataWrapper" {
  219. return true
  220. }
  221. }
  222. return false
  223. }
  224. func (a *API) GenerateCode() (outerr error) {
  225. a.m = make(map[string]interface{})
  226. m := a.m
  227. jsonBytes := slurpURL(a.DiscoveryURL())
  228. err := json.Unmarshal(jsonBytes, &a.m)
  229. if err != nil {
  230. return err
  231. }
  232. outdir := a.SourceDir()
  233. err = os.MkdirAll(outdir, 0755)
  234. if err != nil {
  235. return fmt.Errorf("failed to Mkdir %s: %v", outdir, err)
  236. }
  237. pkg := a.Package()
  238. writeFile(filepath.Join(outdir, a.Package()+"-api.json"), jsonBytes)
  239. genfilename := filepath.Join(outdir, pkg+"-gen.go")
  240. // Buffer the output in memory, for gofmt'ing later in the defer.
  241. var buf bytes.Buffer
  242. a.p = func(format string, args ...interface{}) {
  243. _, err := fmt.Fprintf(&buf, format, args...)
  244. if err != nil {
  245. log.Fatalf("Error writing to %s: %v", genfilename, err)
  246. }
  247. }
  248. a.pn = func(format string, args ...interface{}) {
  249. a.p(format+"\n", args...)
  250. }
  251. // Write the file out after running gofmt on it.
  252. defer func() {
  253. if outerr != nil {
  254. writeFile(genfilename, buf.Bytes())
  255. return
  256. }
  257. fset := token.NewFileSet()
  258. ast, err := parser.ParseFile(fset, "", buf.Bytes(), parser.ParseComments)
  259. if err != nil {
  260. writeFile(genfilename, buf.Bytes())
  261. outerr = err
  262. return
  263. }
  264. var clean bytes.Buffer
  265. config := &printer.Config{
  266. Mode: printer.TabIndent | printer.UseSpaces,
  267. Tabwidth: 8,
  268. }
  269. err = config.Fprint(&clean, fset, ast)
  270. if err != nil {
  271. outerr = err
  272. writeFile(genfilename, buf.Bytes())
  273. return
  274. }
  275. if err := writeFile(genfilename, clean.Bytes()); err != nil {
  276. outerr = err
  277. }
  278. }()
  279. p, pn := a.p, a.pn
  280. reslist := a.Resources()
  281. p("// Package %s provides access to the %s.\n", pkg, jstr(m, "title"))
  282. if docs := jstr(m, "documentationLink"); docs != "" {
  283. p("//\n")
  284. p("// See %s\n", docs)
  285. }
  286. p("//\n// Usage example:\n")
  287. p("//\n")
  288. p("// import %q\n", a.Target())
  289. p("// ...\n")
  290. p("// %sService, err := %s.New(oauthHttpClient)\n", pkg, pkg)
  291. p("package %s\n", pkg)
  292. p("\n")
  293. p("import (\n")
  294. for _, pkg := range []string{"bytes", "fmt", "net/http", "io", "encoding/json", "errors", "strings", "strconv", "net/url",
  295. "code.google.com/p/google-api-go-client/googleapi"} {
  296. p("\t%q\n", pkg)
  297. }
  298. p(")\n\n")
  299. pn("var _ = bytes.NewBuffer")
  300. pn("var _ = strconv.Itoa")
  301. pn("var _ = fmt.Sprintf")
  302. pn("var _ = json.NewDecoder")
  303. pn("var _ = io.Copy")
  304. pn("var _ = url.Parse")
  305. pn("var _ = googleapi.Version")
  306. pn("var _ = errors.New")
  307. pn("")
  308. pn("const apiId = %q", jstr(m, "id"))
  309. pn("const apiName = %q", jstr(m, "name"))
  310. pn("const apiVersion = %q", jstr(m, "version"))
  311. p("const basePath = %q\n", a.apiBaseURL())
  312. p("\n")
  313. a.generateScopeConstants()
  314. a.GetName("New") // ignore return value; we're the first caller
  315. pn("func New(client *http.Client) (*Service, error) {")
  316. pn("if client == nil { return nil, errors.New(\"client is nil\") }")
  317. pn("s := &Service{client: client}")
  318. for _, res := range reslist {
  319. pn("s.%s = &%s{s: s}", res.GoField(), res.GoType())
  320. }
  321. pn("return s, nil")
  322. pn("}")
  323. a.GetName("Service") // ignore return value; no user-defined names yet
  324. p("\ntype Service struct {\n")
  325. p("\tclient *http.Client\n")
  326. for _, res := range reslist {
  327. p("\n\t%s\t*%s\n", res.GoField(), res.GoType())
  328. }
  329. p("}\n")
  330. for _, res := range reslist {
  331. p("\ntype %s struct {\n", res.GoType())
  332. p("\ts *Service\n}\n")
  333. }
  334. a.PopulateSchemas()
  335. for _, s := range a.schemas {
  336. s.writeSchemaCode()
  337. }
  338. for _, meth := range a.APIMethods() {
  339. meth.generateCode()
  340. }
  341. for _, res := range reslist {
  342. for _, meth := range res.Methods() {
  343. meth.generateCode()
  344. }
  345. }
  346. pn("\nfunc cleanPathString(s string) string { return strings.Map(func(r rune) rune { if r >= 0x30 && r <= 0x7a { return r }; return -1 }, s) }")
  347. return nil
  348. }
  349. func (a *API) generateScopeConstants() {
  350. auth := jobj(a.m, "auth")
  351. if auth == nil {
  352. return
  353. }
  354. oauth2 := jobj(auth, "oauth2")
  355. if oauth2 == nil {
  356. return
  357. }
  358. scopes := jobj(oauth2, "scopes")
  359. if scopes == nil || len(scopes) == 0 {
  360. return
  361. }
  362. a.p("// OAuth2 scopes used by this API.\n")
  363. a.p("const (\n")
  364. n := 0
  365. for scope, mi := range scopes {
  366. if n > 0 {
  367. a.p("\n")
  368. }
  369. n++
  370. ident := scopeIdentifierFromURL(scope)
  371. if des := jstr(mi.(map[string]interface{}), "description"); des != "" {
  372. a.p("%s", asComment("\t", des))
  373. }
  374. a.p("\t%s = %q\n", ident, scope)
  375. }
  376. a.p(")\n\n")
  377. }
  378. func scopeIdentifierFromURL(urlStr string) string {
  379. const prefix = "https://www.googleapis.com/auth/"
  380. if !strings.HasPrefix(urlStr, prefix) {
  381. log.Fatalf("Unexpected oauth2 scope %q doesn't start with %q", urlStr, prefix)
  382. }
  383. ident := validGoIdentifer(initialCap(urlStr[len(prefix):])) + "Scope"
  384. return ident
  385. }
  386. type Schema struct {
  387. api *API
  388. m map[string]interface{} // original JSON map
  389. typ *Type // lazily populated by Type
  390. apiName string // the native API-defined name of this type
  391. goName string // lazily populated by GoName
  392. }
  393. type Property struct {
  394. s *Schema // property of which schema
  395. apiName string // the native API-defined name of this property
  396. m map[string]interface{} // original JSON map
  397. typ *Type // lazily populated by Type
  398. }
  399. func (p *Property) Type() *Type {
  400. if p.typ == nil {
  401. p.typ = &Type{api: p.s.api, m: p.m}
  402. }
  403. return p.typ
  404. }
  405. func (p *Property) GoName() string {
  406. return initialCap(p.apiName)
  407. }
  408. func (p *Property) APIName() string {
  409. return p.apiName
  410. }
  411. func (p *Property) Description() string {
  412. return jstr(p.m, "description")
  413. }
  414. type Type struct {
  415. m map[string]interface{} // JSON map containing key "type" and maybe "items", "properties"
  416. api *API
  417. }
  418. func (t *Type) apiType() string {
  419. // Note: returns "" on reference types
  420. if t, ok := t.m["type"].(string); ok {
  421. return t
  422. }
  423. return ""
  424. }
  425. func (t *Type) apiTypeFormat() string {
  426. if f, ok := t.m["format"].(string); ok {
  427. return f
  428. }
  429. return ""
  430. }
  431. func (t *Type) isIntAsString() bool {
  432. return t.apiType() == "string" && strings.Contains(t.apiTypeFormat(), "int")
  433. }
  434. func (t *Type) asSimpleGoType() (goType string, ok bool) {
  435. return simpleTypeConvert(t.apiType(), t.apiTypeFormat())
  436. }
  437. func (t *Type) String() string {
  438. return fmt.Sprintf("[type=%q, map=%s]", t.apiType(), prettyJSON(t.m))
  439. }
  440. func (t *Type) AsGo() string {
  441. if t, ok := t.asSimpleGoType(); ok {
  442. return t
  443. }
  444. if at, ok := t.ArrayType(); ok {
  445. return "[]" + at.AsGo()
  446. }
  447. if ref, ok := t.Reference(); ok {
  448. s := t.api.schemas[ref]
  449. if s == nil {
  450. panic(fmt.Sprintf("in Type.AsGo(), failed to find referenced type %q for %s",
  451. ref, prettyJSON(t.m)))
  452. }
  453. return s.Type().AsGo()
  454. }
  455. if t.IsStruct() {
  456. if apiName, ok := t.m["_apiName"].(string); ok {
  457. s := t.api.schemas[apiName]
  458. if s == nil {
  459. panic(fmt.Sprintf("in Type.AsGo, _apiName of %q didn't point to a valid schema; json: %s",
  460. apiName, prettyJSON(t.m)))
  461. }
  462. return "*" + s.GoName()
  463. }
  464. panic("in Type.AsGo, no _apiName found for struct type " + prettyJSON(t.m))
  465. }
  466. panic("unhandled Type.AsGo for " + prettyJSON(t.m))
  467. }
  468. func (t *Type) IsSimple() bool {
  469. _, ok := simpleTypeConvert(t.apiType(), t.apiTypeFormat())
  470. return ok
  471. }
  472. func (t *Type) IsStruct() bool {
  473. return t.apiType() == "object"
  474. }
  475. func (t *Type) Reference() (apiName string, ok bool) {
  476. apiName = jstr(t.m, "$ref")
  477. ok = apiName != ""
  478. return
  479. }
  480. func (t *Type) IsReference() bool {
  481. return jstr(t.m, "$ref") != ""
  482. }
  483. func (t *Type) ReferenceSchema() (s *Schema, ok bool) {
  484. apiName, ok := t.Reference()
  485. if !ok {
  486. return
  487. }
  488. s = t.api.schemas[apiName]
  489. if s == nil {
  490. panicf("failed to find t.api.schemas[%q] while resolving reference",
  491. apiName)
  492. }
  493. return s, true
  494. }
  495. func (t *Type) ArrayType() (elementType *Type, ok bool) {
  496. if t.apiType() != "array" {
  497. return
  498. }
  499. items := jobj(t.m, "items")
  500. if items == nil {
  501. panicf("can't handle array type missing its 'items' key. map is %#v", t.m)
  502. }
  503. return &Type{api: t.api, m: items}, true
  504. }
  505. func (s *Schema) Type() *Type {
  506. if s.typ == nil {
  507. s.typ = &Type{api: s.api, m: s.m}
  508. }
  509. return s.typ
  510. }
  511. func (s *Schema) properties() []*Property {
  512. if !s.Type().IsStruct() {
  513. panic("called properties on non-object schema")
  514. }
  515. pl := []*Property{}
  516. for name, im := range jobj(s.m, "properties") {
  517. m := im.(map[string]interface{})
  518. pl = append(pl, &Property{
  519. s: s,
  520. m: m,
  521. apiName: name,
  522. })
  523. }
  524. return pl
  525. }
  526. func (s *Schema) populateSubSchemas() (outerr error) {
  527. defer func() {
  528. r := recover()
  529. if r == nil {
  530. return
  531. }
  532. outerr = fmt.Errorf("%v", r)
  533. }()
  534. addSubStruct := func(subApiName string, t *Type) {
  535. if s.api.schemas[subApiName] != nil {
  536. panic("dup schema apiName: " + subApiName)
  537. }
  538. subm := t.m
  539. subm["_apiName"] = subApiName
  540. subs := &Schema{
  541. api: s.api,
  542. m: subm,
  543. typ: t,
  544. apiName: subApiName,
  545. }
  546. s.api.schemas[subApiName] = subs
  547. err := subs.populateSubSchemas()
  548. if err != nil {
  549. panicf("in sub-struct %q: %v", subApiName, err)
  550. }
  551. }
  552. if s.Type().IsStruct() {
  553. for _, p := range s.properties() {
  554. if p.Type().IsSimple() {
  555. continue
  556. }
  557. if at, ok := p.Type().ArrayType(); ok {
  558. if at.IsSimple() || at.IsReference() {
  559. continue
  560. }
  561. subApiName := fmt.Sprintf("%s.%s", s.apiName, p.apiName)
  562. if at.IsStruct() {
  563. addSubStruct(subApiName, at) // was p.Type()?
  564. continue
  565. }
  566. if _, ok := at.ArrayType(); ok {
  567. addSubStruct(subApiName, at)
  568. continue
  569. }
  570. panicf("Unknown property array type for %q: %s", subApiName, at)
  571. continue
  572. }
  573. subApiName := fmt.Sprintf("%s.%s", s.apiName, p.apiName)
  574. if p.Type().IsStruct() {
  575. addSubStruct(subApiName, p.Type())
  576. continue
  577. }
  578. if p.Type().IsReference() {
  579. continue
  580. }
  581. panicf("Unknown type for %q: %s", subApiName, p.Type())
  582. }
  583. return
  584. }
  585. if at, ok := s.Type().ArrayType(); ok {
  586. if at.IsSimple() || at.IsReference() {
  587. return
  588. }
  589. subApiName := fmt.Sprintf("%s.Item", s.apiName)
  590. if at.IsStruct() {
  591. addSubStruct(subApiName, at)
  592. return
  593. }
  594. panicf("Unknown array type for %q: %s", subApiName, at)
  595. return
  596. }
  597. if s.Type().IsSimple() || s.Type().IsReference() {
  598. return
  599. }
  600. fmt.Fprintf(os.Stderr, "in populateSubSchemas, schema is: %s", prettyJSON(s.m))
  601. panicf("populateSubSchemas: unsupported type for schema %q", s.apiName)
  602. panic("unreachable")
  603. }
  604. // GoName returns (or creates and returns) the bare Go name
  605. // of the apiName, making sure that it's a proper Go identifier
  606. // and doesn't conflict with an existing name.
  607. func (s *Schema) GoName() string {
  608. if s.goName == "" {
  609. s.goName = s.api.GetName(initialCap(s.apiName))
  610. }
  611. return s.goName
  612. }
  613. func (s *Schema) writeSchemaCode() {
  614. if s.Type().IsStruct() {
  615. s.writeSchemaStruct()
  616. return
  617. }
  618. if _, ok := s.Type().ArrayType(); ok {
  619. log.Printf("TODO writeSchemaCode for arrays for %s", s.GoName())
  620. return
  621. }
  622. if destSchema, ok := s.Type().ReferenceSchema(); ok {
  623. // Convert it to a struct using embedding.
  624. s.api.p("\ntype %s struct {\n", s.GoName())
  625. s.api.p("\t%s\n", destSchema.GoName())
  626. s.api.p("}\n")
  627. return
  628. }
  629. if s.Type().IsSimple() {
  630. return
  631. }
  632. fmt.Fprintf(os.Stderr, "in writeSchemaCode, schema is: %s", prettyJSON(s.m))
  633. panicf("writeSchemaCode: unsupported type for schema %q", s.apiName)
  634. }
  635. func (s *Schema) writeSchemaStruct() {
  636. // TODO: description
  637. s.api.p("\ntype %s struct {\n", s.GoName())
  638. for i, p := range s.properties() {
  639. if i > 0 {
  640. s.api.p("\n")
  641. }
  642. pname := p.GoName()
  643. if des := p.Description(); des != "" {
  644. s.api.p("%s", asComment("\t", fmt.Sprintf("%s: %s", pname, des)))
  645. }
  646. var extraOpt string
  647. if p.Type().isIntAsString() {
  648. extraOpt += ",string"
  649. }
  650. s.api.p("\t%s %s `json:\"%s,omitempty%s\"`\n", pname, p.Type().AsGo(), p.APIName(), extraOpt)
  651. }
  652. s.api.p("}\n")
  653. }
  654. // PopulateSchemas reads all the API types ("schemas") from the JSON file
  655. // and converts them to *Schema instances, returning an identically
  656. // keyed map, additionally containing subresources. For instance,
  657. //
  658. // A resource "Foo" of type "object" with a property "bar", also of type
  659. // "object" (an anonymous sub-resource), will get a synthetic API name
  660. // of "Foo.bar".
  661. //
  662. // A resource "Foo" of type "array" with an "items" of type "object"
  663. // will get a synthetic API name of "Foo.Item".
  664. func (a *API) PopulateSchemas() {
  665. m := jobj(a.m, "schemas")
  666. if a.schemas != nil {
  667. panic("")
  668. }
  669. a.schemas = make(map[string]*Schema)
  670. for name, mi := range m {
  671. s := &Schema{
  672. api: a,
  673. apiName: name,
  674. m: mi.(map[string]interface{}),
  675. }
  676. // And a little gross hack, so a map alone is good
  677. // enough to get its apiName:
  678. s.m["_apiName"] = name
  679. a.schemas[name] = s
  680. err := s.populateSubSchemas()
  681. if err != nil {
  682. panicf("Error populating schema with API name %q: %v", name, err)
  683. }
  684. }
  685. }
  686. type Resource struct {
  687. api *API
  688. name string
  689. m map[string]interface{}
  690. }
  691. func (r *Resource) GoField() string {
  692. return initialCap(r.name)
  693. }
  694. func (r *Resource) GoType() string {
  695. return initialCap(r.name) + "Service"
  696. }
  697. func (r *Resource) Methods() []*Method {
  698. ms := []*Method{}
  699. for mname, mi := range jobj(r.m, "methods") {
  700. ms = append(ms, &Method{
  701. api: r.api,
  702. r: r,
  703. name: mname,
  704. m: mi.(map[string]interface{}),
  705. })
  706. }
  707. return ms
  708. }
  709. type Method struct {
  710. api *API
  711. r *Resource // or nil if a API-level (top-level) method
  712. name string
  713. m map[string]interface{} // original JSON
  714. params []*Param // all Params, of each type, lazily set by first access to Parameters
  715. }
  716. func (m *Method) Id() string {
  717. return jstr(m.m, "id")
  718. }
  719. func (m *Method) supportsMedia() bool {
  720. return jobj(m.m, "mediaUpload") != nil
  721. }
  722. func (m *Method) mediaPath() string {
  723. return jstr(jobj(jobj(jobj(m.m, "mediaUpload"), "protocols"), "simple"), "path")
  724. }
  725. func (m *Method) Params() []*Param {
  726. if m.params == nil {
  727. for name, mi := range jobj(m.m, "parameters") {
  728. pm := mi.(map[string]interface{})
  729. m.params = append(m.params, &Param{
  730. name: name,
  731. m: pm,
  732. method: m,
  733. })
  734. }
  735. }
  736. return m.params
  737. }
  738. func (m *Method) grepParams(f func(*Param) bool) []*Param {
  739. matches := make([]*Param, 0)
  740. for _, param := range m.Params() {
  741. if f(param) {
  742. matches = append(matches, param)
  743. }
  744. }
  745. return matches
  746. }
  747. func (m *Method) NamedParam(name string) *Param {
  748. matches := m.grepParams(func(p *Param) bool {
  749. return p.name == name
  750. })
  751. if len(matches) < 1 {
  752. log.Panicf("failed to find named parameter %q", name)
  753. }
  754. if len(matches) > 1 {
  755. log.Panicf("found multiple parameters for parameter name %q", name)
  756. }
  757. return matches[0]
  758. }
  759. func (m *Method) OptParams() []*Param {
  760. return m.grepParams(func(p *Param) bool {
  761. return !p.IsRequired()
  762. })
  763. }
  764. func (m *Method) RequiredRepeatedQueryParams() []*Param {
  765. return m.grepParams(func(p *Param) bool {
  766. return p.IsRequired() && p.IsRepeated() && p.Location() == "query"
  767. })
  768. }
  769. func (m *Method) RequiredQueryParams() []*Param {
  770. return m.grepParams(func(p *Param) bool {
  771. return p.IsRequired() && !p.IsRepeated() && p.Location() == "query"
  772. })
  773. }
  774. func (meth *Method) generateCode() {
  775. res := meth.r // may be nil if a top-level method
  776. a := meth.api
  777. p, pn := a.p, a.pn
  778. pn("\n// method id %q:", meth.Id())
  779. retTypeComma := responseType(meth.m)
  780. if retTypeComma != "" {
  781. retTypeComma += ", "
  782. }
  783. args := meth.NewArguments()
  784. methodName := initialCap(meth.name)
  785. prefix := ""
  786. if res != nil {
  787. prefix = initialCap(res.name)
  788. }
  789. callName := a.GetName(prefix + methodName + "Call")
  790. p("\ntype %s struct {\n", callName)
  791. p("\ts *Service\n")
  792. for _, arg := range args.l {
  793. p("\t%s %s\n", arg.goname, arg.gotype)
  794. }
  795. p("\topt_ map[string]interface{}\n")
  796. if meth.supportsMedia() {
  797. p("\tmedia_ io.Reader\n")
  798. }
  799. p("}\n")
  800. p("\n%s", asComment("", methodName+": "+jstr(meth.m, "description")))
  801. var servicePtr string
  802. if res == nil {
  803. p("func (s *Service) %s(%s) *%s {\n", methodName, args, callName)
  804. servicePtr = "s"
  805. } else {
  806. p("func (r *%s) %s(%s) *%s {\n", res.GoType(), methodName, args, callName)
  807. servicePtr = "r.s"
  808. }
  809. p("\tc := &%s{s: %s, opt_: make(map[string]interface{})}\n", callName, servicePtr)
  810. for _, arg := range args.l {
  811. p("\tc.%s = %s\n", arg.goname, arg.goname)
  812. }
  813. p("\treturn c\n")
  814. p("}\n")
  815. for _, opt := range meth.OptParams() {
  816. setter := initialCap(opt.name)
  817. des := jstr(opt.m, "description")
  818. des = strings.Replace(des, "Optional.", "", 1)
  819. des = strings.TrimSpace(des)
  820. p("\n%s", asComment("", fmt.Sprintf("%s sets the optional parameter %q: %s", setter, opt.name, des)))
  821. np := new(namePool)
  822. np.Get("c") // take the receiver's name
  823. paramName := np.Get(validGoIdentifer(opt.name))
  824. p("func (c *%s) %s(%s %s) *%s {\n", callName, setter, paramName, opt.GoType(), callName)
  825. p("c.opt_[%q] = %s\n", opt.name, paramName)
  826. p("return c\n")
  827. p("}\n")
  828. }
  829. if meth.supportsMedia() {
  830. p("func (c *%s) Media(r io.Reader) *%s {\n", callName, callName)
  831. p("c.media_ = r\n")
  832. p("return c\n")
  833. p("}\n")
  834. }
  835. pn("\nfunc (c *%s) Do() (%serror) {", callName, retTypeComma)
  836. nilRet := ""
  837. if retTypeComma != "" {
  838. nilRet = "nil, "
  839. }
  840. pn("var body io.Reader = nil")
  841. hasContentType := false
  842. if ba := args.bodyArg(); ba != nil {
  843. style := "WithoutDataWrapper"
  844. if a.needsDataWrapper() {
  845. style = "WithDataWrapper"
  846. }
  847. pn("body, err := googleapi.%s.JSONReader(c.%s)", style, ba.goname)
  848. pn("if err != nil { return %serr }", nilRet)
  849. pn(`ctype := "application/json"`)
  850. hasContentType = true
  851. }
  852. pn("params := make(url.Values)")
  853. // Set this first. if they override it, though, might be gross. We don't expect
  854. // XML replies elsewhere. TODO(bradfitz): hide this option in the generated code?
  855. pn(`params.Set("alt", "json")`)
  856. for _, p := range meth.RequiredQueryParams() {
  857. pn("params.Set(%q, fmt.Sprintf(\"%%v\", c.%s))", p.name, p.goCallFieldName())
  858. }
  859. for _, p := range meth.RequiredRepeatedQueryParams() {
  860. pn("for _, v := range c.%s { params.Add(%q, fmt.Sprintf(\"%%v\", v)) }",
  861. p.name, p.name)
  862. }
  863. for _, p := range meth.OptParams() {
  864. pn("if v, ok := c.opt_[%q]; ok { params.Set(%q, fmt.Sprintf(\"%%v\", v)) }",
  865. p.name, p.name)
  866. }
  867. urlStr := resolveRelative(a.apiBaseURL(), jstr(meth.m, "path"))
  868. urlStr = strings.Replace(urlStr, "%7B", "{", -1)
  869. urlStr = strings.Replace(urlStr, "%7D", "}", -1)
  870. p("urls := googleapi.ResolveRelative(%q, %q)\n", a.apiBaseURL(), jstr(meth.m, "path"))
  871. if meth.supportsMedia() {
  872. pn("if c.media_ != nil {")
  873. // Hack guess, since we get a 404 otherwise:
  874. //pn("urls = googleapi.ResolveRelative(%q, %q)", a.apiBaseURL(), meth.mediaPath())
  875. // Further hack. Discovery doc is wrong?
  876. pn("urls = strings.Replace(urls, %q, %q, 1)", "https://www.googleapis.com/", "https://www.googleapis.com/upload/")
  877. pn("}")
  878. }
  879. for _, arg := range args.forLocation("path") {
  880. p("\turls = strings.Replace(urls, \"{%s}\", %s, 1)\n", arg.apiname, arg.cleanExpr("c."))
  881. }
  882. pn("urls += \"?\" + params.Encode()")
  883. if meth.supportsMedia() {
  884. pn("contentLength_, hasMedia_ := googleapi.ConditionallyIncludeMedia(c.media_, &body, &ctype)")
  885. }
  886. pn("req, _ := http.NewRequest(%q, urls, body)", jstr(meth.m, "httpMethod"))
  887. if meth.supportsMedia() {
  888. pn("if hasMedia_ { req.ContentLength = contentLength_ }")
  889. }
  890. if hasContentType {
  891. pn(`req.Header.Set("Content-Type", ctype)`)
  892. }
  893. pn(`req.Header.Set("User-Agent", "google-api-go-client/` + goGenVersion + `")`)
  894. pn("res, err := c.s.client.Do(req);")
  895. pn("if err != nil { return %serr }", nilRet)
  896. pn("if err := googleapi.CheckResponse(res); err != nil { return %serr }", nilRet)
  897. if retTypeComma == "" {
  898. pn("return nil")
  899. } else {
  900. pn("ret := new(%s)", responseType(meth.m)[1:])
  901. pn("if err := json.NewDecoder(res.Body).Decode(ret); err != nil { return nil, err }")
  902. pn("return ret, nil")
  903. }
  904. bs, _ := json.MarshalIndent(meth.m, "\t// ", " ")
  905. pn("// %s\n", string(bs))
  906. pn("}")
  907. }
  908. type Param struct {
  909. method *Method
  910. name string
  911. m map[string]interface{}
  912. callFieldName string // empty means to use the default
  913. }
  914. func (p *Param) IsRequired() bool {
  915. v, _ := p.m["required"].(bool)
  916. return v
  917. }
  918. func (p *Param) IsRepeated() bool {
  919. v, _ := p.m["repeated"].(bool)
  920. return v
  921. }
  922. func (p *Param) Location() string {
  923. return p.m["location"].(string)
  924. }
  925. func (p *Param) GoType() string {
  926. typ, format := jstr(p.m, "type"), jstr(p.m, "format")
  927. if typ == "string" && strings.Contains(format, "int") && p.Location() != "query" {
  928. panic("unexpected int parameter encoded as string, not in query: " + p.name)
  929. }
  930. t, ok := simpleTypeConvert(typ, format)
  931. if !ok {
  932. panic("failed to convert parameter type " + fmt.Sprintf("type=%q, format=%q", typ, format))
  933. }
  934. return t
  935. }
  936. // goCallFieldName returns the name of this parameter's field in a
  937. // method's "Call" struct.
  938. func (p *Param) goCallFieldName() string {
  939. if p.callFieldName != "" {
  940. return p.callFieldName
  941. }
  942. return validGoIdentifer(p.name)
  943. }
  944. // APIMethods returns top-level ("API-level") methods. They don't have an associated resource.
  945. func (a *API) APIMethods() []*Method {
  946. meths := []*Method{}
  947. for name, mi := range jobj(a.m, "methods") {
  948. meths = append(meths, &Method{
  949. api: a,
  950. r: nil, // to be explicit
  951. name: name,
  952. m: mi.(map[string]interface{}),
  953. })
  954. }
  955. return meths
  956. }
  957. func (a *API) Resources() []*Resource {
  958. res := []*Resource{}
  959. for rname, rmi := range jobj(a.m, "resources") {
  960. rm := rmi.(map[string]interface{})
  961. res = append(res, &Resource{a, rname, rm})
  962. }
  963. return res
  964. }
  965. func resolveRelative(basestr, relstr string) string {
  966. u, err := url.Parse(basestr)
  967. if err != nil {
  968. panicf("Error parsing base URL %q: %v", basestr, err)
  969. }
  970. rel, err := url.Parse(relstr)
  971. if err != nil {
  972. panicf("Error parsing relative URL %q: %v", relstr, err)
  973. }
  974. u = u.ResolveReference(rel)
  975. return u.String()
  976. }
  977. func (meth *Method) NewArguments() (args *arguments) {
  978. args = &arguments{
  979. method: meth,
  980. m: make(map[string]*argument),
  981. }
  982. po, ok := meth.m["parameterOrder"].([]interface{})
  983. if ok {
  984. for _, poi := range po {
  985. pname := poi.(string)
  986. arg := meth.NewArg(pname, meth.NamedParam(pname))
  987. args.AddArg(arg)
  988. }
  989. }
  990. if ro := jobj(meth.m, "request"); ro != nil {
  991. args.AddArg(meth.NewBodyArg(ro))
  992. }
  993. return
  994. }
  995. func (meth *Method) NewBodyArg(m map[string]interface{}) *argument {
  996. reftype := jstr(m, "$ref")
  997. return &argument{
  998. goname: validGoIdentifer(strings.ToLower(reftype)),
  999. apiname: "REQUEST",
  1000. gotype: "*" + reftype,
  1001. apitype: reftype,
  1002. location: "body",
  1003. }
  1004. }
  1005. func (meth *Method) NewArg(apiname string, p *Param) *argument {
  1006. m := p.m
  1007. apitype := jstr(m, "type")
  1008. des := jstr(m, "description")
  1009. goname := validGoIdentifer(apiname) // but might be changed later, if conflicts
  1010. if strings.Contains(des, "identifier") && !strings.HasSuffix(strings.ToLower(goname), "id") {
  1011. goname += "id" // yay
  1012. p.callFieldName = goname
  1013. }
  1014. gotype := mustSimpleTypeConvert(apitype, jstr(m, "format"))
  1015. if p.IsRepeated() {
  1016. gotype = "[]" + gotype
  1017. }
  1018. return &argument{
  1019. apiname: apiname,
  1020. apitype: apitype,
  1021. goname: goname,
  1022. gotype: gotype,
  1023. location: jstr(m, "location"),
  1024. }
  1025. }
  1026. type argument struct {
  1027. method *Method
  1028. apiname, apitype string
  1029. goname, gotype string
  1030. location string // "path", "query", "body"
  1031. }
  1032. func (a *argument) String() string {
  1033. return a.goname + " " + a.gotype
  1034. }
  1035. func (a *argument) cleanExpr(prefix string) string {
  1036. switch a.gotype {
  1037. case "[]string":
  1038. log.Printf("TODO(bradfitz): only including the first parameter in path query.")
  1039. return "cleanPathString(" + prefix + a.goname + "[0])"
  1040. case "string":
  1041. return "cleanPathString(" + prefix + a.goname + ")"
  1042. case "integer", "int64":
  1043. return "strconv.FormatInt(" + prefix + a.goname + ", 10)"
  1044. }
  1045. log.Panicf("unknown type: apitype=%q, gotype=%q", a.apitype, a.gotype)
  1046. return ""
  1047. }
  1048. // arguments are the arguments that a method takes
  1049. type arguments struct {
  1050. l []*argument
  1051. m map[string]*argument
  1052. method *Method
  1053. }
  1054. func (args *arguments) forLocation(loc string) []*argument {
  1055. matches := make([]*argument, 0)
  1056. for _, arg := range args.l {
  1057. if arg.location == loc {
  1058. matches = append(matches, arg)
  1059. }
  1060. }
  1061. return matches
  1062. }
  1063. func (args *arguments) bodyArg() *argument {
  1064. for _, arg := range args.l {
  1065. if arg.location == "body" {
  1066. return arg
  1067. }
  1068. }
  1069. return nil
  1070. }
  1071. func (args *arguments) AddArg(arg *argument) {
  1072. n := 1
  1073. oname := arg.goname
  1074. for {
  1075. _, present := args.m[arg.goname]
  1076. if !present {
  1077. args.m[arg.goname] = arg
  1078. args.l = append(args.l, arg)
  1079. return
  1080. }
  1081. n++
  1082. arg.goname = fmt.Sprintf("%s%d", oname, n)
  1083. }
  1084. }
  1085. func (a *arguments) String() string {
  1086. var buf bytes.Buffer
  1087. for i, arg := range a.l {
  1088. if i != 0 {
  1089. buf.Write([]byte(", "))
  1090. }
  1091. buf.Write([]byte(arg.String()))
  1092. }
  1093. return buf.String()
  1094. }
  1095. func asComment(pfx, c string) string {
  1096. var buf bytes.Buffer
  1097. const maxLen = 70
  1098. removeNewlines := func(s string) string {
  1099. return strings.Replace(s, "\n", "\n"+pfx+"// ", -1)
  1100. }
  1101. for len(c) > 0 {
  1102. line := c
  1103. if len(line) < maxLen {
  1104. fmt.Fprintf(&buf, "%s// %s\n", pfx, removeNewlines(line))
  1105. break
  1106. }
  1107. line = line[:maxLen]
  1108. si := strings.LastIndex(line, " ")
  1109. if si != -1 {
  1110. line = line[:si]
  1111. }
  1112. fmt.Fprintf(&buf, "%s// %s\n", pfx, removeNewlines(line))
  1113. c = c[len(line):]
  1114. if si != -1 {
  1115. c = c[1:]
  1116. }
  1117. }
  1118. return buf.String()
  1119. }
  1120. func simpleTypeConvert(apiType, format string) (gotype string, ok bool) {
  1121. // From http://tools.ietf.org/html/draft-zyp-json-schema-03#section-5.1
  1122. switch apiType {
  1123. case "boolean":
  1124. gotype = "bool"
  1125. case "string":
  1126. gotype = "string"
  1127. switch format {
  1128. case "int64", "uint64", "int32", "uint32":
  1129. gotype = format
  1130. }
  1131. case "number":
  1132. gotype = "float64"
  1133. case "integer":
  1134. gotype = "int64"
  1135. case "any":
  1136. gotype = "interface{}"
  1137. }
  1138. return gotype, gotype != ""
  1139. }
  1140. func mustSimpleTypeConvert(apiType, format string) string {
  1141. if gotype, ok := simpleTypeConvert(apiType, format); ok {
  1142. return gotype
  1143. }
  1144. panic(fmt.Sprintf("failed to simpleTypeConvert(%q, %q)", apiType, format))
  1145. }
  1146. func (a *API) goTypeOfJsonObject(outerName, memberName string, m map[string]interface{}) (string, error) {
  1147. apitype := jstr(m, "type")
  1148. switch apitype {
  1149. case "array":
  1150. items := jobj(m, "items")
  1151. if items == nil {
  1152. return "", errors.New("no items but type was array")
  1153. }
  1154. if ref := jstr(items, "$ref"); ref != "" {
  1155. return "[]*" + ref, nil // TODO: wrong; delete this whole function
  1156. }
  1157. if atype := jstr(items, "type"); atype != "" {
  1158. return "[]" + mustSimpleTypeConvert(atype, jstr(items, "format")), nil
  1159. }
  1160. return "", errors.New("unsupported 'array' type")
  1161. case "object":
  1162. return "*" + outerName + "_" + memberName, nil
  1163. //return "", os.NewError("unsupported 'object' type")
  1164. }
  1165. return mustSimpleTypeConvert(apitype, jstr(m, "format")), nil
  1166. }
  1167. func responseType(m map[string]interface{}) string {
  1168. ro := jobj(m, "response")
  1169. if ro != nil {
  1170. if ref := jstr(ro, "$ref"); ref != "" {
  1171. return "*" + ref
  1172. }
  1173. }
  1174. return ""
  1175. }
  1176. // initialCap returns the identifier with a leading capital letter.
  1177. // it also maps "foo-bar" to "FooBar".
  1178. func initialCap(ident string) string {
  1179. if ident == "" {
  1180. panic("blank identifier")
  1181. }
  1182. return depunct(ident, true)
  1183. }
  1184. func validGoIdentifer(ident string) string {
  1185. id := depunct(ident, false)
  1186. switch id {
  1187. case "type":
  1188. return "type_"
  1189. }
  1190. return id
  1191. }
  1192. // depunct removes '-', '.', '$', '/' from identifers, making the
  1193. // following character uppercase
  1194. func depunct(ident string, needCap bool) string {
  1195. var buf bytes.Buffer
  1196. for _, c := range ident {
  1197. if c == '-' || c == '.' || c == '$' || c == '/' {
  1198. needCap = true
  1199. continue
  1200. }
  1201. if needCap {
  1202. c = unicode.ToUpper(c)
  1203. needCap = false
  1204. }
  1205. buf.WriteByte(byte(c))
  1206. }
  1207. return buf.String()
  1208. }
  1209. func prettyJSON(m map[string]interface{}) string {
  1210. bs, err := json.MarshalIndent(m, "", " ")
  1211. if err != nil {
  1212. return fmt.Sprintf("[JSON error %v on %#v]", err, m)
  1213. }
  1214. return string(bs)
  1215. }
  1216. func jstr(m map[string]interface{}, key string) string {
  1217. if s, ok := m[key].(string); ok {
  1218. return s
  1219. }
  1220. return ""
  1221. }
  1222. func jobj(m map[string]interface{}, key string) map[string]interface{} {
  1223. if m, ok := m[key].(map[string]interface{}); ok {
  1224. return m
  1225. }
  1226. return nil
  1227. }
  1228. func jstrlist(m map[string]interface{}, key string) []string {
  1229. si, ok := m[key].([]interface{})
  1230. if !ok {
  1231. return nil
  1232. }
  1233. sl := make([]string, 0)
  1234. for _, si := range si {
  1235. sl = append(sl, si.(string))
  1236. }
  1237. return sl
  1238. }