api.go 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694
  1. // +build codegen
  2. // Package api represents API abstractions for rendering service generated files.
  3. package api
  4. import (
  5. "bytes"
  6. "encoding/json"
  7. "fmt"
  8. "io/ioutil"
  9. "path"
  10. "path/filepath"
  11. "regexp"
  12. "sort"
  13. "strings"
  14. "text/template"
  15. )
  16. // An API defines a service API's definition. and logic to serialize the definition.
  17. type API struct {
  18. Metadata Metadata
  19. Operations map[string]*Operation
  20. Shapes map[string]*Shape
  21. Waiters []Waiter
  22. Documentation string
  23. // Set to true to avoid removing unused shapes
  24. NoRemoveUnusedShapes bool
  25. // Set to true to avoid renaming to 'Input/Output' postfixed shapes
  26. NoRenameToplevelShapes bool
  27. // Set to true to ignore service/request init methods (for testing)
  28. NoInitMethods bool
  29. // Set to true to ignore String() and GoString methods (for generated tests)
  30. NoStringerMethods bool
  31. // Set to true to not generate API service name constants
  32. NoConstServiceNames bool
  33. // Set to true to not generate validation shapes
  34. NoValidataShapeMethods bool
  35. // Set to true to not generate struct field accessors
  36. NoGenStructFieldAccessors bool
  37. SvcClientImportPath string
  38. initialized bool
  39. imports map[string]bool
  40. name string
  41. path string
  42. BaseCrosslinkURL string
  43. }
  44. // A Metadata is the metadata about an API's definition.
  45. type Metadata struct {
  46. APIVersion string
  47. EndpointPrefix string
  48. SigningName string
  49. ServiceAbbreviation string
  50. ServiceFullName string
  51. SignatureVersion string
  52. JSONVersion string
  53. TargetPrefix string
  54. Protocol string
  55. UID string
  56. EndpointsID string
  57. NoResolveEndpoint bool
  58. }
  59. var serviceAliases map[string]string
  60. func Bootstrap() error {
  61. b, err := ioutil.ReadFile(filepath.Join("..", "models", "customizations", "service-aliases.json"))
  62. if err != nil {
  63. return err
  64. }
  65. return json.Unmarshal(b, &serviceAliases)
  66. }
  67. // PackageName name of the API package
  68. func (a *API) PackageName() string {
  69. return strings.ToLower(a.StructName())
  70. }
  71. // InterfacePackageName returns the package name for the interface.
  72. func (a *API) InterfacePackageName() string {
  73. return a.PackageName() + "iface"
  74. }
  75. var nameRegex = regexp.MustCompile(`^Amazon|AWS\s*|\(.*|\s+|\W+`)
  76. // StructName returns the struct name for a given API.
  77. func (a *API) StructName() string {
  78. if a.name == "" {
  79. name := a.Metadata.ServiceAbbreviation
  80. if name == "" {
  81. name = a.Metadata.ServiceFullName
  82. }
  83. name = nameRegex.ReplaceAllString(name, "")
  84. a.name = name
  85. if name, ok := serviceAliases[strings.ToLower(name)]; ok {
  86. a.name = name
  87. }
  88. }
  89. return a.name
  90. }
  91. // UseInitMethods returns if the service's init method should be rendered.
  92. func (a *API) UseInitMethods() bool {
  93. return !a.NoInitMethods
  94. }
  95. // NiceName returns the human friendly API name.
  96. func (a *API) NiceName() string {
  97. if a.Metadata.ServiceAbbreviation != "" {
  98. return a.Metadata.ServiceAbbreviation
  99. }
  100. return a.Metadata.ServiceFullName
  101. }
  102. // ProtocolPackage returns the package name of the protocol this API uses.
  103. func (a *API) ProtocolPackage() string {
  104. switch a.Metadata.Protocol {
  105. case "json":
  106. return "jsonrpc"
  107. case "ec2":
  108. return "ec2query"
  109. default:
  110. return strings.Replace(a.Metadata.Protocol, "-", "", -1)
  111. }
  112. }
  113. // OperationNames returns a slice of API operations supported.
  114. func (a *API) OperationNames() []string {
  115. i, names := 0, make([]string, len(a.Operations))
  116. for n := range a.Operations {
  117. names[i] = n
  118. i++
  119. }
  120. sort.Strings(names)
  121. return names
  122. }
  123. // OperationList returns a slice of API operation pointers
  124. func (a *API) OperationList() []*Operation {
  125. list := make([]*Operation, len(a.Operations))
  126. for i, n := range a.OperationNames() {
  127. list[i] = a.Operations[n]
  128. }
  129. return list
  130. }
  131. // OperationHasOutputPlaceholder returns if any of the API operation input
  132. // or output shapes are place holders.
  133. func (a *API) OperationHasOutputPlaceholder() bool {
  134. for _, op := range a.Operations {
  135. if op.OutputRef.Shape.Placeholder {
  136. return true
  137. }
  138. }
  139. return false
  140. }
  141. // ShapeNames returns a slice of names for each shape used by the API.
  142. func (a *API) ShapeNames() []string {
  143. i, names := 0, make([]string, len(a.Shapes))
  144. for n := range a.Shapes {
  145. names[i] = n
  146. i++
  147. }
  148. sort.Strings(names)
  149. return names
  150. }
  151. // ShapeList returns a slice of shape pointers used by the API.
  152. //
  153. // Will exclude error shapes from the list of shapes returned.
  154. func (a *API) ShapeList() []*Shape {
  155. list := make([]*Shape, 0, len(a.Shapes))
  156. for _, n := range a.ShapeNames() {
  157. // Ignore error shapes in list
  158. if s := a.Shapes[n]; !s.IsError {
  159. list = append(list, s)
  160. }
  161. }
  162. return list
  163. }
  164. // ShapeListErrors returns a list of the errors defined by the API model
  165. func (a *API) ShapeListErrors() []*Shape {
  166. list := []*Shape{}
  167. for _, n := range a.ShapeNames() {
  168. // Ignore error shapes in list
  169. if s := a.Shapes[n]; s.IsError {
  170. list = append(list, s)
  171. }
  172. }
  173. return list
  174. }
  175. // resetImports resets the import map to default values.
  176. func (a *API) resetImports() {
  177. a.imports = map[string]bool{
  178. "github.com/aws/aws-sdk-go/aws": true,
  179. }
  180. }
  181. // importsGoCode returns the generated Go import code.
  182. func (a *API) importsGoCode() string {
  183. if len(a.imports) == 0 {
  184. return ""
  185. }
  186. corePkgs, extPkgs := []string{}, []string{}
  187. for i := range a.imports {
  188. if strings.Contains(i, ".") {
  189. extPkgs = append(extPkgs, i)
  190. } else {
  191. corePkgs = append(corePkgs, i)
  192. }
  193. }
  194. sort.Strings(corePkgs)
  195. sort.Strings(extPkgs)
  196. code := "import (\n"
  197. for _, i := range corePkgs {
  198. code += fmt.Sprintf("\t%q\n", i)
  199. }
  200. if len(corePkgs) > 0 {
  201. code += "\n"
  202. }
  203. for _, i := range extPkgs {
  204. code += fmt.Sprintf("\t%q\n", i)
  205. }
  206. code += ")\n\n"
  207. return code
  208. }
  209. // A tplAPI is the top level template for the API
  210. var tplAPI = template.Must(template.New("api").Parse(`
  211. {{ range $_, $o := .OperationList }}
  212. {{ $o.GoCode }}
  213. {{ end }}
  214. {{ range $_, $s := .ShapeList }}
  215. {{ if and $s.IsInternal (eq $s.Type "structure") }}{{ $s.GoCode }}{{ end }}
  216. {{ end }}
  217. {{ range $_, $s := .ShapeList }}
  218. {{ if $s.IsEnum }}{{ $s.GoCode }}{{ end }}
  219. {{ end }}
  220. `))
  221. // APIGoCode renders the API in Go code. Returning it as a string
  222. func (a *API) APIGoCode() string {
  223. a.resetImports()
  224. a.imports["github.com/aws/aws-sdk-go/aws/awsutil"] = true
  225. a.imports["github.com/aws/aws-sdk-go/aws/request"] = true
  226. if a.OperationHasOutputPlaceholder() {
  227. a.imports["github.com/aws/aws-sdk-go/private/protocol/"+a.ProtocolPackage()] = true
  228. a.imports["github.com/aws/aws-sdk-go/private/protocol"] = true
  229. }
  230. for _, op := range a.Operations {
  231. if op.AuthType == "none" {
  232. a.imports["github.com/aws/aws-sdk-go/aws/credentials"] = true
  233. break
  234. }
  235. }
  236. var buf bytes.Buffer
  237. err := tplAPI.Execute(&buf, a)
  238. if err != nil {
  239. panic(err)
  240. }
  241. code := a.importsGoCode() + strings.TrimSpace(buf.String())
  242. return code
  243. }
  244. var noCrossLinkServices = map[string]struct{}{
  245. "apigateway": struct{}{},
  246. "budgets": struct{}{},
  247. "cloudsearch": struct{}{},
  248. "cloudsearchdomain": struct{}{},
  249. "discovery": struct{}{},
  250. "elastictranscoder": struct{}{},
  251. "es": struct{}{},
  252. "glacier": struct{}{},
  253. "importexport": struct{}{},
  254. "iot": struct{}{},
  255. "iot-data": struct{}{},
  256. "lambda": struct{}{},
  257. "machinelearning": struct{}{},
  258. "rekognition": struct{}{},
  259. "sdb": struct{}{},
  260. "swf": struct{}{},
  261. }
  262. func GetCrosslinkURL(baseURL, name, uid string, params ...string) string {
  263. _, ok := noCrossLinkServices[strings.ToLower(name)]
  264. if uid != "" && baseURL != "" && !ok {
  265. return strings.Join(append([]string{baseURL, "goto", "WebAPI", uid}, params...), "/")
  266. }
  267. return ""
  268. }
  269. func (a *API) APIName() string {
  270. return a.name
  271. }
  272. // A tplService defines the template for the service generated code.
  273. var tplService = template.Must(template.New("service").Funcs(template.FuncMap{
  274. "ServiceNameValue": func(a *API) string {
  275. if a.NoConstServiceNames {
  276. return fmt.Sprintf("%q", a.Metadata.EndpointPrefix)
  277. }
  278. return "ServiceName"
  279. },
  280. "GetCrosslinkURL": GetCrosslinkURL,
  281. "EndpointsIDConstValue": func(a *API) string {
  282. if a.NoConstServiceNames {
  283. return fmt.Sprintf("%q", a.Metadata.EndpointPrefix)
  284. }
  285. if a.Metadata.EndpointPrefix == a.Metadata.EndpointsID {
  286. return "ServiceName"
  287. }
  288. return fmt.Sprintf("%q", a.Metadata.EndpointsID)
  289. },
  290. "EndpointsIDValue": func(a *API) string {
  291. if a.NoConstServiceNames {
  292. return fmt.Sprintf("%q", a.Metadata.EndpointPrefix)
  293. }
  294. return "EndpointsID"
  295. },
  296. }).Parse(`
  297. {{ .Documentation }}// The service client's operations are safe to be used concurrently.
  298. // It is not safe to mutate any of the client's properties though.
  299. {{ $crosslinkURL := GetCrosslinkURL $.BaseCrosslinkURL $.APIName $.Metadata.UID -}}
  300. {{ if ne $crosslinkURL "" -}}
  301. // Please also see {{ $crosslinkURL }}
  302. {{ end -}}
  303. type {{ .StructName }} struct {
  304. *client.Client
  305. }
  306. {{ if .UseInitMethods }}// Used for custom client initialization logic
  307. var initClient func(*client.Client)
  308. // Used for custom request initialization logic
  309. var initRequest func(*request.Request)
  310. {{ end }}
  311. {{ if not .NoConstServiceNames -}}
  312. // Service information constants
  313. const (
  314. ServiceName = "{{ .Metadata.EndpointPrefix }}" // Service endpoint prefix API calls made to.
  315. EndpointsID = {{ EndpointsIDConstValue . }} // Service ID for Regions and Endpoints metadata.
  316. )
  317. {{- end }}
  318. // New creates a new instance of the {{ .StructName }} client with a session.
  319. // If additional configuration is needed for the client instance use the optional
  320. // aws.Config parameter to add your extra config.
  321. //
  322. // Example:
  323. // // Create a {{ .StructName }} client from just a session.
  324. // svc := {{ .PackageName }}.New(mySession)
  325. //
  326. // // Create a {{ .StructName }} client with additional configuration
  327. // svc := {{ .PackageName }}.New(mySession, aws.NewConfig().WithRegion("us-west-2"))
  328. func New(p client.ConfigProvider, cfgs ...*aws.Config) *{{ .StructName }} {
  329. {{ if .Metadata.NoResolveEndpoint -}}
  330. var c client.Config
  331. if v, ok := p.(client.ConfigNoResolveEndpointProvider); ok {
  332. c = v.ClientConfigNoResolveEndpoint(cfgs...)
  333. } else {
  334. c = p.ClientConfig({{ EndpointsIDValue . }}, cfgs...)
  335. }
  336. {{- else -}}
  337. c := p.ClientConfig({{ EndpointsIDValue . }}, cfgs...)
  338. {{- end }}
  339. return newClient(*c.Config, c.Handlers, c.Endpoint, c.SigningRegion, c.SigningName)
  340. }
  341. // newClient creates, initializes and returns a new service client instance.
  342. func newClient(cfg aws.Config, handlers request.Handlers, endpoint, signingRegion, signingName string) *{{ .StructName }} {
  343. {{- if .Metadata.SigningName }}
  344. if len(signingName) == 0 {
  345. signingName = "{{ .Metadata.SigningName }}"
  346. }
  347. {{- end }}
  348. svc := &{{ .StructName }}{
  349. Client: client.New(
  350. cfg,
  351. metadata.ClientInfo{
  352. ServiceName: {{ ServiceNameValue . }},
  353. SigningName: signingName,
  354. SigningRegion: signingRegion,
  355. Endpoint: endpoint,
  356. APIVersion: "{{ .Metadata.APIVersion }}",
  357. {{ if .Metadata.JSONVersion -}}
  358. JSONVersion: "{{ .Metadata.JSONVersion }}",
  359. {{- end }}
  360. {{ if .Metadata.TargetPrefix -}}
  361. TargetPrefix: "{{ .Metadata.TargetPrefix }}",
  362. {{- end }}
  363. },
  364. handlers,
  365. ),
  366. }
  367. // Handlers
  368. svc.Handlers.Sign.PushBackNamed({{if eq .Metadata.SignatureVersion "v2"}}v2{{else}}v4{{end}}.SignRequestHandler)
  369. {{- if eq .Metadata.SignatureVersion "v2" }}
  370. svc.Handlers.Sign.PushBackNamed(corehandlers.BuildContentLengthHandler)
  371. {{- end }}
  372. svc.Handlers.Build.PushBackNamed({{ .ProtocolPackage }}.BuildHandler)
  373. svc.Handlers.Unmarshal.PushBackNamed({{ .ProtocolPackage }}.UnmarshalHandler)
  374. svc.Handlers.UnmarshalMeta.PushBackNamed({{ .ProtocolPackage }}.UnmarshalMetaHandler)
  375. svc.Handlers.UnmarshalError.PushBackNamed({{ .ProtocolPackage }}.UnmarshalErrorHandler)
  376. {{ if .UseInitMethods }}// Run custom client initialization if present
  377. if initClient != nil {
  378. initClient(svc.Client)
  379. }
  380. {{ end }}
  381. return svc
  382. }
  383. // newRequest creates a new request for a {{ .StructName }} operation and runs any
  384. // custom request initialization.
  385. func (c *{{ .StructName }}) newRequest(op *request.Operation, params, data interface{}) *request.Request {
  386. req := c.NewRequest(op, params, data)
  387. {{ if .UseInitMethods }}// Run custom request initialization if present
  388. if initRequest != nil {
  389. initRequest(req)
  390. }
  391. {{ end }}
  392. return req
  393. }
  394. `))
  395. // ServiceGoCode renders service go code. Returning it as a string.
  396. func (a *API) ServiceGoCode() string {
  397. a.resetImports()
  398. a.imports["github.com/aws/aws-sdk-go/aws/client"] = true
  399. a.imports["github.com/aws/aws-sdk-go/aws/client/metadata"] = true
  400. a.imports["github.com/aws/aws-sdk-go/aws/request"] = true
  401. if a.Metadata.SignatureVersion == "v2" {
  402. a.imports["github.com/aws/aws-sdk-go/private/signer/v2"] = true
  403. a.imports["github.com/aws/aws-sdk-go/aws/corehandlers"] = true
  404. } else {
  405. a.imports["github.com/aws/aws-sdk-go/aws/signer/v4"] = true
  406. }
  407. a.imports["github.com/aws/aws-sdk-go/private/protocol/"+a.ProtocolPackage()] = true
  408. var buf bytes.Buffer
  409. err := tplService.Execute(&buf, a)
  410. if err != nil {
  411. panic(err)
  412. }
  413. code := a.importsGoCode() + buf.String()
  414. return code
  415. }
  416. // ExampleGoCode renders service example code. Returning it as a string.
  417. func (a *API) ExampleGoCode() string {
  418. exs := []string{}
  419. imports := map[string]bool{}
  420. for _, o := range a.OperationList() {
  421. o.imports = map[string]bool{}
  422. exs = append(exs, o.Example())
  423. for k, v := range o.imports {
  424. imports[k] = v
  425. }
  426. }
  427. code := fmt.Sprintf("import (\n%q\n%q\n%q\n\n%q\n%q\n%q\n",
  428. "bytes",
  429. "fmt",
  430. "time",
  431. "github.com/aws/aws-sdk-go/aws",
  432. "github.com/aws/aws-sdk-go/aws/session",
  433. path.Join(a.SvcClientImportPath, a.PackageName()),
  434. )
  435. for k, _ := range imports {
  436. code += fmt.Sprintf("%q\n", k)
  437. }
  438. code += ")\n\n"
  439. code += "var _ time.Duration\nvar _ bytes.Buffer\n\n"
  440. code += strings.Join(exs, "\n\n")
  441. return code
  442. }
  443. // A tplInterface defines the template for the service interface type.
  444. var tplInterface = template.Must(template.New("interface").Parse(`
  445. // {{ .StructName }}API provides an interface to enable mocking the
  446. // {{ .PackageName }}.{{ .StructName }} service client's API operation,
  447. // paginators, and waiters. This make unit testing your code that calls out
  448. // to the SDK's service client's calls easier.
  449. //
  450. // The best way to use this interface is so the SDK's service client's calls
  451. // can be stubbed out for unit testing your code with the SDK without needing
  452. // to inject custom request handlers into the the SDK's request pipeline.
  453. //
  454. // // myFunc uses an SDK service client to make a request to
  455. // // {{.Metadata.ServiceFullName}}. {{ $opts := .OperationList }}{{ $opt := index $opts 0 }}
  456. // func myFunc(svc {{ .InterfacePackageName }}.{{ .StructName }}API) bool {
  457. // // Make svc.{{ $opt.ExportedName }} request
  458. // }
  459. //
  460. // func main() {
  461. // sess := session.New()
  462. // svc := {{ .PackageName }}.New(sess)
  463. //
  464. // myFunc(svc)
  465. // }
  466. //
  467. // In your _test.go file:
  468. //
  469. // // Define a mock struct to be used in your unit tests of myFunc.
  470. // type mock{{ .StructName }}Client struct {
  471. // {{ .InterfacePackageName }}.{{ .StructName }}API
  472. // }
  473. // func (m *mock{{ .StructName }}Client) {{ $opt.ExportedName }}(input {{ $opt.InputRef.GoTypeWithPkgName }}) ({{ $opt.OutputRef.GoTypeWithPkgName }}, error) {
  474. // // mock response/functionality
  475. // }
  476. //
  477. // func TestMyFunc(t *testing.T) {
  478. // // Setup Test
  479. // mockSvc := &mock{{ .StructName }}Client{}
  480. //
  481. // myfunc(mockSvc)
  482. //
  483. // // Verify myFunc's functionality
  484. // }
  485. //
  486. // It is important to note that this interface will have breaking changes
  487. // when the service model is updated and adds new API operations, paginators,
  488. // and waiters. Its suggested to use the pattern above for testing, or using
  489. // tooling to generate mocks to satisfy the interfaces.
  490. type {{ .StructName }}API interface {
  491. {{ range $_, $o := .OperationList }}
  492. {{ $o.InterfaceSignature }}
  493. {{ end }}
  494. {{ range $_, $w := .Waiters }}
  495. {{ $w.InterfaceSignature }}
  496. {{ end }}
  497. }
  498. var _ {{ .StructName }}API = (*{{ .PackageName }}.{{ .StructName }})(nil)
  499. `))
  500. // InterfaceGoCode returns the go code for the service's API operations as an
  501. // interface{}. Assumes that the interface is being created in a different
  502. // package than the service API's package.
  503. func (a *API) InterfaceGoCode() string {
  504. a.resetImports()
  505. a.imports = map[string]bool{
  506. "github.com/aws/aws-sdk-go/aws": true,
  507. "github.com/aws/aws-sdk-go/aws/request": true,
  508. path.Join(a.SvcClientImportPath, a.PackageName()): true,
  509. }
  510. var buf bytes.Buffer
  511. err := tplInterface.Execute(&buf, a)
  512. if err != nil {
  513. panic(err)
  514. }
  515. code := a.importsGoCode() + strings.TrimSpace(buf.String())
  516. return code
  517. }
  518. // NewAPIGoCodeWithPkgName returns a string of instantiating the API prefixed
  519. // with its package name. Takes a string depicting the Config.
  520. func (a *API) NewAPIGoCodeWithPkgName(cfg string) string {
  521. return fmt.Sprintf("%s.New(%s)", a.PackageName(), cfg)
  522. }
  523. // computes the validation chain for all input shapes
  524. func (a *API) addShapeValidations() {
  525. for _, o := range a.Operations {
  526. resolveShapeValidations(o.InputRef.Shape)
  527. }
  528. }
  529. // Updates the source shape and all nested shapes with the validations that
  530. // could possibly be needed.
  531. func resolveShapeValidations(s *Shape, ancestry ...*Shape) {
  532. for _, a := range ancestry {
  533. if a == s {
  534. return
  535. }
  536. }
  537. children := []string{}
  538. for _, name := range s.MemberNames() {
  539. ref := s.MemberRefs[name]
  540. if s.IsRequired(name) && !s.Validations.Has(ref, ShapeValidationRequired) {
  541. s.Validations = append(s.Validations, ShapeValidation{
  542. Name: name, Ref: ref, Type: ShapeValidationRequired,
  543. })
  544. }
  545. if ref.Shape.Min != 0 && !s.Validations.Has(ref, ShapeValidationMinVal) {
  546. s.Validations = append(s.Validations, ShapeValidation{
  547. Name: name, Ref: ref, Type: ShapeValidationMinVal,
  548. })
  549. }
  550. switch ref.Shape.Type {
  551. case "map", "list", "structure":
  552. children = append(children, name)
  553. }
  554. }
  555. ancestry = append(ancestry, s)
  556. for _, name := range children {
  557. ref := s.MemberRefs[name]
  558. // Since this is a grab bag we will just continue since
  559. // we can't validate because we don't know the valued shape.
  560. if ref.JSONValue {
  561. continue
  562. }
  563. nestedShape := ref.Shape.NestedShape()
  564. var v *ShapeValidation
  565. if len(nestedShape.Validations) > 0 {
  566. v = &ShapeValidation{
  567. Name: name, Ref: ref, Type: ShapeValidationNested,
  568. }
  569. } else {
  570. resolveShapeValidations(nestedShape, ancestry...)
  571. if len(nestedShape.Validations) > 0 {
  572. v = &ShapeValidation{
  573. Name: name, Ref: ref, Type: ShapeValidationNested,
  574. }
  575. }
  576. }
  577. if v != nil && !s.Validations.Has(v.Ref, v.Type) {
  578. s.Validations = append(s.Validations, *v)
  579. }
  580. }
  581. ancestry = ancestry[:len(ancestry)-1]
  582. }
  583. // A tplAPIErrors is the top level template for the API
  584. var tplAPIErrors = template.Must(template.New("api").Parse(`
  585. const (
  586. {{ range $_, $s := $.ShapeListErrors }}
  587. // {{ $s.ErrorCodeName }} for service response error code
  588. // {{ printf "%q" $s.ErrorName }}.
  589. {{ if $s.Docstring -}}
  590. //
  591. {{ $s.Docstring }}
  592. {{ end -}}
  593. {{ $s.ErrorCodeName }} = {{ printf "%q" $s.ErrorName }}
  594. {{ end }}
  595. )
  596. `))
  597. func (a *API) APIErrorsGoCode() string {
  598. var buf bytes.Buffer
  599. err := tplAPIErrors.Execute(&buf, a)
  600. if err != nil {
  601. panic(err)
  602. }
  603. return strings.TrimSpace(buf.String())
  604. }