operation.go 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712
  1. // +build codegen
  2. package api
  3. import (
  4. "bytes"
  5. "fmt"
  6. "regexp"
  7. "sort"
  8. "strings"
  9. "text/template"
  10. )
  11. // An Operation defines a specific API Operation.
  12. type Operation struct {
  13. API *API `json:"-"`
  14. ExportedName string
  15. Name string
  16. Documentation string
  17. HTTP HTTPInfo
  18. Host string `json:"host"`
  19. InputRef ShapeRef `json:"input"`
  20. OutputRef ShapeRef `json:"output"`
  21. ErrorRefs []ShapeRef `json:"errors"`
  22. Paginator *Paginator
  23. Deprecated bool `json:"deprecated"`
  24. DeprecatedMsg string `json:"deprecatedMessage"`
  25. AuthType string `json:"authtype"`
  26. imports map[string]bool
  27. CustomBuildHandlers []string
  28. EventStreamAPI *EventStreamAPI
  29. IsEndpointDiscoveryOp bool `json:"endpointoperation"`
  30. EndpointDiscovery *EndpointDiscovery `json:"endpointdiscovery"`
  31. Endpoint *EndpointTrait `json:"endpoint"`
  32. }
  33. // EndpointTrait provides the structure of the modeled enpdoint trait, and its
  34. // properties.
  35. type EndpointTrait struct {
  36. // Specifies the hostPrefix template to prepend to the operation's request
  37. // endpoint host.
  38. HostPrefix string `json:"hostPrefix"`
  39. }
  40. // EndpointDiscovery represents a map of key values pairs that represents
  41. // metadata about how a given API will make a call to the discovery endpoint.
  42. type EndpointDiscovery struct {
  43. // Required indicates that for a given operation that endpoint is required.
  44. // Any required endpoint discovery operation cannot have endpoint discovery
  45. // turned off.
  46. Required bool `json:"required"`
  47. }
  48. // OperationForMethod returns the API operation name that corresponds to the
  49. // client method name provided.
  50. func (a *API) OperationForMethod(name string) *Operation {
  51. for _, op := range a.Operations {
  52. for _, m := range op.Methods() {
  53. if m == name {
  54. return op
  55. }
  56. }
  57. }
  58. return nil
  59. }
  60. // A HTTPInfo defines the method of HTTP request for the Operation.
  61. type HTTPInfo struct {
  62. Method string
  63. RequestURI string
  64. ResponseCode uint
  65. }
  66. // Methods Returns a list of method names that will be generated.
  67. func (o *Operation) Methods() []string {
  68. methods := []string{
  69. o.ExportedName,
  70. o.ExportedName + "Request",
  71. o.ExportedName + "WithContext",
  72. }
  73. if o.Paginator != nil {
  74. methods = append(methods, []string{
  75. o.ExportedName + "Pages",
  76. o.ExportedName + "PagesWithContext",
  77. }...)
  78. }
  79. return methods
  80. }
  81. // HasInput returns if the Operation accepts an input paramater
  82. func (o *Operation) HasInput() bool {
  83. return o.InputRef.ShapeName != ""
  84. }
  85. // HasOutput returns if the Operation accepts an output parameter
  86. func (o *Operation) HasOutput() bool {
  87. return o.OutputRef.ShapeName != ""
  88. }
  89. // GetSigner returns the signer that should be used for a API request.
  90. func (o *Operation) GetSigner() string {
  91. buf := bytes.NewBuffer(nil)
  92. switch o.AuthType {
  93. case "none":
  94. o.API.AddSDKImport("aws/credentials")
  95. buf.WriteString("req.Config.Credentials = credentials.AnonymousCredentials")
  96. case "v4-unsigned-body":
  97. o.API.AddSDKImport("aws/signer/v4")
  98. buf.WriteString("req.Handlers.Sign.Remove(v4.SignRequestHandler)\n")
  99. buf.WriteString("handler := v4.BuildNamedHandler(\"v4.CustomSignerHandler\", v4.WithUnsignedPayload)\n")
  100. buf.WriteString("req.Handlers.Sign.PushFrontNamed(handler)")
  101. }
  102. buf.WriteString("\n")
  103. return buf.String()
  104. }
  105. // operationTmpl defines a template for rendering an API Operation
  106. var operationTmpl = template.Must(template.New("operation").Funcs(template.FuncMap{
  107. "GetCrosslinkURL": GetCrosslinkURL,
  108. "EnableStopOnSameToken": enableStopOnSameToken,
  109. "GetDeprecatedMsg": getDeprecatedMessage,
  110. }).Parse(`
  111. const op{{ .ExportedName }} = "{{ .Name }}"
  112. // {{ .ExportedName }}Request generates a "aws/request.Request" representing the
  113. // client's request for the {{ .ExportedName }} operation. The "output" return
  114. // value will be populated with the request's response once the request completes
  115. // successfully.
  116. //
  117. // Use "Send" method on the returned Request to send the API call to the service.
  118. // the "output" return value is not valid until after Send returns without error.
  119. //
  120. // See {{ .ExportedName }} for more information on using the {{ .ExportedName }}
  121. // API call, and error handling.
  122. //
  123. // This method is useful when you want to inject custom logic or configuration
  124. // into the SDK's request lifecycle. Such as custom headers, or retry logic.
  125. //
  126. //
  127. // // Example sending a request using the {{ .ExportedName }}Request method.
  128. // req, resp := client.{{ .ExportedName }}Request(params)
  129. //
  130. // err := req.Send()
  131. // if err == nil { // resp is now filled
  132. // fmt.Println(resp)
  133. // }
  134. {{ $crosslinkURL := GetCrosslinkURL $.API.BaseCrosslinkURL $.API.Metadata.UID $.ExportedName -}}
  135. {{ if ne $crosslinkURL "" -}}
  136. //
  137. // See also, {{ $crosslinkURL }}
  138. {{ end -}}
  139. {{- if .Deprecated }}//
  140. // Deprecated: {{ GetDeprecatedMsg .DeprecatedMsg .ExportedName }}
  141. {{ end -}}
  142. func (c *{{ .API.StructName }}) {{ .ExportedName }}Request(` +
  143. `input {{ .InputRef.GoType }}) (req *request.Request, output {{ .OutputRef.GoType }}) {
  144. {{ if (or .Deprecated (or .InputRef.Deprecated .OutputRef.Deprecated)) }}if c.Client.Config.Logger != nil {
  145. c.Client.Config.Logger.Log("This operation, {{ .ExportedName }}, has been deprecated")
  146. }
  147. op := &request.Operation{ {{ else }} op := &request.Operation{ {{ end }}
  148. Name: op{{ .ExportedName }},
  149. {{ if ne .HTTP.Method "" }}HTTPMethod: "{{ .HTTP.Method }}",
  150. {{ end }}HTTPPath: {{ if ne .HTTP.RequestURI "" }}"{{ .HTTP.RequestURI }}"{{ else }}"/"{{ end }},
  151. {{ if .Paginator }}Paginator: &request.Paginator{
  152. InputTokens: {{ .Paginator.InputTokensString }},
  153. OutputTokens: {{ .Paginator.OutputTokensString }},
  154. LimitToken: "{{ .Paginator.LimitKey }}",
  155. TruncationToken: "{{ .Paginator.MoreResults }}",
  156. },
  157. {{ end }}
  158. }
  159. if input == nil {
  160. input = &{{ .InputRef.GoTypeElem }}{}
  161. }
  162. output = &{{ .OutputRef.GoTypeElem }}{}
  163. req = c.newRequest(op, input, output)
  164. {{ if ne .AuthType "" }}{{ .GetSigner }}{{ end }}
  165. {{- if .ShouldDiscardResponse -}}
  166. {{- $_ := .API.AddSDKImport "private/protocol" -}}
  167. {{- $_ := .API.AddSDKImport "private/protocol" .API.ProtocolPackage -}}
  168. req.Handlers.Unmarshal.Swap({{ .API.ProtocolPackage }}.UnmarshalHandler.Name, protocol.UnmarshalDiscardBodyHandler)
  169. {{ else if .OutputRef.Shape.EventStreamsMemberName -}}
  170. {{- $_ := .API.AddSDKImport "private/protocol" .API.ProtocolPackage -}}
  171. {{- $_ := .API.AddSDKImport "private/protocol/rest" -}}
  172. req.Handlers.Send.Swap(client.LogHTTPResponseHandler.Name, client.LogHTTPResponseHeaderHandler)
  173. req.Handlers.Unmarshal.Swap({{ .API.ProtocolPackage }}.UnmarshalHandler.Name, rest.UnmarshalHandler)
  174. req.Handlers.Unmarshal.PushBack(output.runEventStreamLoop)
  175. {{ if eq .API.Metadata.Protocol "json" -}}
  176. req.Handlers.Unmarshal.PushBack(output.unmarshalInitialResponse)
  177. {{ end -}}
  178. {{ end -}}
  179. {{ if .EndpointDiscovery -}}
  180. {{if not .EndpointDiscovery.Required -}}
  181. if aws.BoolValue(req.Config.EnableEndpointDiscovery) {
  182. {{end -}}
  183. de := discoverer{{ .API.EndpointDiscoveryOp.Name }}{
  184. Required: {{ .EndpointDiscovery.Required }},
  185. EndpointCache: c.endpointCache,
  186. Params: map[string]*string{
  187. "op": aws.String(req.Operation.Name),
  188. {{ range $key, $ref := .InputRef.Shape.MemberRefs -}}
  189. {{ if $ref.EndpointDiscoveryID -}}
  190. "{{ $ref.OrigShapeName }}": input.{{ $key }},
  191. {{ end -}}
  192. {{- end }}
  193. },
  194. Client: c,
  195. }
  196. for k, v := range de.Params {
  197. if v == nil {
  198. delete(de.Params, k)
  199. }
  200. }
  201. req.Handlers.Build.PushFrontNamed(request.NamedHandler{
  202. Name: "crr.endpointdiscovery",
  203. Fn: de.Handler,
  204. })
  205. {{if not .EndpointDiscovery.Required -}}
  206. }
  207. {{ end -}}
  208. {{ end -}}
  209. {{- range $_, $handler := $.CustomBuildHandlers -}}
  210. req.Handlers.Build.PushBackNamed({{ $handler }})
  211. {{ end -}}
  212. return
  213. }
  214. // {{ .ExportedName }} API operation for {{ .API.Metadata.ServiceFullName }}.
  215. {{ if .Documentation -}}
  216. //
  217. {{ .Documentation }}
  218. {{ end -}}
  219. //
  220. // Returns awserr.Error for service API and SDK errors. Use runtime type assertions
  221. // with awserr.Error's Code and Message methods to get detailed information about
  222. // the error.
  223. //
  224. // See the AWS API reference guide for {{ .API.Metadata.ServiceFullName }}'s
  225. // API operation {{ .ExportedName }} for usage and error information.
  226. {{ if .ErrorRefs -}}
  227. //
  228. // Returned Error Codes:
  229. {{ range $_, $err := .ErrorRefs -}}
  230. // * {{ $err.Shape.ErrorCodeName }} "{{ $err.Shape.ErrorName}}"
  231. {{ if $err.Docstring -}}
  232. {{ $err.IndentedDocstring }}
  233. {{ end -}}
  234. //
  235. {{ end -}}
  236. {{ end -}}
  237. {{ $crosslinkURL := GetCrosslinkURL $.API.BaseCrosslinkURL $.API.Metadata.UID $.ExportedName -}}
  238. {{ if ne $crosslinkURL "" -}}
  239. // See also, {{ $crosslinkURL }}
  240. {{ end -}}
  241. {{- if .Deprecated }}//
  242. // Deprecated: {{ GetDeprecatedMsg .DeprecatedMsg .ExportedName }}
  243. {{ end -}}
  244. func (c *{{ .API.StructName }}) {{ .ExportedName }}(` +
  245. `input {{ .InputRef.GoType }}) ({{ .OutputRef.GoType }}, error) {
  246. req, out := c.{{ .ExportedName }}Request(input)
  247. return out, req.Send()
  248. }
  249. // {{ .ExportedName }}WithContext is the same as {{ .ExportedName }} with the addition of
  250. // the ability to pass a context and additional request options.
  251. //
  252. // See {{ .ExportedName }} for details on how to use this API operation.
  253. //
  254. // The context must be non-nil and will be used for request cancellation. If
  255. // the context is nil a panic will occur. In the future the SDK may create
  256. // sub-contexts for http.Requests. See https://golang.org/pkg/context/
  257. // for more information on using Contexts.
  258. {{ if .Deprecated }}//
  259. // Deprecated: {{ GetDeprecatedMsg .DeprecatedMsg (printf "%s%s" .ExportedName "WithContext") }}
  260. {{ end -}}
  261. func (c *{{ .API.StructName }}) {{ .ExportedName }}WithContext(` +
  262. `ctx aws.Context, input {{ .InputRef.GoType }}, opts ...request.Option) ` +
  263. `({{ .OutputRef.GoType }}, error) {
  264. req, out := c.{{ .ExportedName }}Request(input)
  265. req.SetContext(ctx)
  266. req.ApplyOptions(opts...)
  267. return out, req.Send()
  268. }
  269. {{ if .Paginator }}
  270. // {{ .ExportedName }}Pages iterates over the pages of a {{ .ExportedName }} operation,
  271. // calling the "fn" function with the response data for each page. To stop
  272. // iterating, return false from the fn function.
  273. //
  274. // See {{ .ExportedName }} method for more information on how to use this operation.
  275. //
  276. // Note: This operation can generate multiple requests to a service.
  277. //
  278. // // Example iterating over at most 3 pages of a {{ .ExportedName }} operation.
  279. // pageNum := 0
  280. // err := client.{{ .ExportedName }}Pages(params,
  281. // func(page {{ .OutputRef.GoType }}, lastPage bool) bool {
  282. // pageNum++
  283. // fmt.Println(page)
  284. // return pageNum <= 3
  285. // })
  286. //
  287. {{ if .Deprecated }}//
  288. // Deprecated: {{ GetDeprecatedMsg .DeprecatedMsg (printf "%s%s" .ExportedName "Pages") }}
  289. {{ end -}}
  290. func (c *{{ .API.StructName }}) {{ .ExportedName }}Pages(` +
  291. `input {{ .InputRef.GoType }}, fn func({{ .OutputRef.GoType }}, bool) bool) error {
  292. return c.{{ .ExportedName }}PagesWithContext(aws.BackgroundContext(), input, fn)
  293. }
  294. // {{ .ExportedName }}PagesWithContext same as {{ .ExportedName }}Pages except
  295. // it takes a Context and allows setting request options on the pages.
  296. //
  297. // The context must be non-nil and will be used for request cancellation. If
  298. // the context is nil a panic will occur. In the future the SDK may create
  299. // sub-contexts for http.Requests. See https://golang.org/pkg/context/
  300. // for more information on using Contexts.
  301. {{ if .Deprecated }}//
  302. // Deprecated: {{ GetDeprecatedMsg .DeprecatedMsg (printf "%s%s" .ExportedName "PagesWithContext") }}
  303. {{ end -}}
  304. func (c *{{ .API.StructName }}) {{ .ExportedName }}PagesWithContext(` +
  305. `ctx aws.Context, ` +
  306. `input {{ .InputRef.GoType }}, ` +
  307. `fn func({{ .OutputRef.GoType }}, bool) bool, ` +
  308. `opts ...request.Option) error {
  309. p := request.Pagination {
  310. {{ if EnableStopOnSameToken .API.PackageName -}}EndPageOnSameToken: true,
  311. {{ end -}}
  312. NewRequest: func() (*request.Request, error) {
  313. var inCpy {{ .InputRef.GoType }}
  314. if input != nil {
  315. tmp := *input
  316. inCpy = &tmp
  317. }
  318. req, _ := c.{{ .ExportedName }}Request(inCpy)
  319. req.SetContext(ctx)
  320. req.ApplyOptions(opts...)
  321. return req, nil
  322. },
  323. }
  324. cont := true
  325. for p.Next() && cont {
  326. cont = fn(p.Page().({{ .OutputRef.GoType }}), !p.HasNextPage())
  327. }
  328. return p.Err()
  329. }
  330. {{ end }}
  331. {{ if .IsEndpointDiscoveryOp -}}
  332. type discoverer{{ .ExportedName }} struct {
  333. Client *{{ .API.StructName }}
  334. Required bool
  335. EndpointCache *crr.EndpointCache
  336. Params map[string]*string
  337. Key string
  338. }
  339. func (d *discoverer{{ .ExportedName }}) Discover() (crr.Endpoint, error) {
  340. input := &{{ .API.EndpointDiscoveryOp.InputRef.ShapeName }}{
  341. {{ if .API.EndpointDiscoveryOp.InputRef.Shape.HasMember "Operation" -}}
  342. Operation: d.Params["op"],
  343. {{ end -}}
  344. {{ if .API.EndpointDiscoveryOp.InputRef.Shape.HasMember "Identifiers" -}}
  345. Identifiers: d.Params,
  346. {{ end -}}
  347. }
  348. resp, err := d.Client.{{ .API.EndpointDiscoveryOp.Name }}(input)
  349. if err != nil {
  350. return crr.Endpoint{}, err
  351. }
  352. endpoint := crr.Endpoint{
  353. Key: d.Key,
  354. }
  355. for _, e := range resp.Endpoints {
  356. if e.Address == nil {
  357. continue
  358. }
  359. cachedInMinutes := aws.Int64Value(e.CachePeriodInMinutes)
  360. u, err := url.Parse(*e.Address)
  361. if err != nil {
  362. continue
  363. }
  364. addr := crr.WeightedAddress{
  365. URL: u,
  366. Expired: time.Now().Add(time.Duration(cachedInMinutes) * time.Minute),
  367. }
  368. endpoint.Add(addr)
  369. }
  370. d.EndpointCache.Add(endpoint)
  371. return endpoint, nil
  372. }
  373. func (d *discoverer{{ .ExportedName }}) Handler(r *request.Request) {
  374. endpointKey := crr.BuildEndpointKey(d.Params)
  375. d.Key = endpointKey
  376. endpoint, err := d.EndpointCache.Get(d, endpointKey, d.Required)
  377. if err != nil {
  378. r.Error = err
  379. return
  380. }
  381. if endpoint.URL != nil && len(endpoint.URL.String()) > 0 {
  382. r.HTTPRequest.URL = endpoint.URL
  383. }
  384. }
  385. {{ end -}}
  386. `))
  387. // GoCode returns a string of rendered GoCode for this Operation
  388. func (o *Operation) GoCode() string {
  389. var buf bytes.Buffer
  390. if len(o.OutputRef.Shape.EventStreamsMemberName) != 0 {
  391. o.API.AddSDKImport("aws/client")
  392. o.API.AddSDKImport("private/protocol")
  393. o.API.AddSDKImport("private/protocol/rest")
  394. o.API.AddSDKImport("private/protocol", o.API.ProtocolPackage())
  395. }
  396. if o.API.EndpointDiscoveryOp != nil {
  397. o.API.AddSDKImport("aws/crr")
  398. o.API.AddImport("time")
  399. o.API.AddImport("net/url")
  400. }
  401. if o.Endpoint != nil && len(o.Endpoint.HostPrefix) != 0 {
  402. setupEndpointHostPrefix(o)
  403. }
  404. err := operationTmpl.Execute(&buf, o)
  405. if err != nil {
  406. panic(err)
  407. }
  408. return strings.TrimSpace(buf.String())
  409. }
  410. // tplInfSig defines the template for rendering an Operation's signature within an Interface definition.
  411. var tplInfSig = template.Must(template.New("opsig").Parse(`
  412. {{ .ExportedName }}({{ .InputRef.GoTypeWithPkgName }}) ({{ .OutputRef.GoTypeWithPkgName }}, error)
  413. {{ .ExportedName }}WithContext(aws.Context, {{ .InputRef.GoTypeWithPkgName }}, ...request.Option) ({{ .OutputRef.GoTypeWithPkgName }}, error)
  414. {{ .ExportedName }}Request({{ .InputRef.GoTypeWithPkgName }}) (*request.Request, {{ .OutputRef.GoTypeWithPkgName }})
  415. {{ if .Paginator -}}
  416. {{ .ExportedName }}Pages({{ .InputRef.GoTypeWithPkgName }}, func({{ .OutputRef.GoTypeWithPkgName }}, bool) bool) error
  417. {{ .ExportedName }}PagesWithContext(aws.Context, {{ .InputRef.GoTypeWithPkgName }}, func({{ .OutputRef.GoTypeWithPkgName }}, bool) bool, ...request.Option) error
  418. {{- end }}
  419. `))
  420. // InterfaceSignature returns a string representing the Operation's interface{}
  421. // functional signature.
  422. func (o *Operation) InterfaceSignature() string {
  423. var buf bytes.Buffer
  424. err := tplInfSig.Execute(&buf, o)
  425. if err != nil {
  426. panic(err)
  427. }
  428. return strings.TrimSpace(buf.String())
  429. }
  430. // tplExample defines the template for rendering an Operation example
  431. var tplExample = template.Must(template.New("operationExample").Parse(`
  432. func Example{{ .API.StructName }}_{{ .ExportedName }}() {
  433. sess := session.Must(session.NewSession())
  434. svc := {{ .API.PackageName }}.New(sess)
  435. {{ .ExampleInput }}
  436. resp, err := svc.{{ .ExportedName }}(params)
  437. if err != nil {
  438. // Print the error, cast err to awserr.Error to get the Code and
  439. // Message from an error.
  440. fmt.Println(err.Error())
  441. return
  442. }
  443. // Pretty-print the response data.
  444. fmt.Println(resp)
  445. }
  446. `))
  447. // Example returns a string of the rendered Go code for the Operation
  448. func (o *Operation) Example() string {
  449. var buf bytes.Buffer
  450. err := tplExample.Execute(&buf, o)
  451. if err != nil {
  452. panic(err)
  453. }
  454. return strings.TrimSpace(buf.String())
  455. }
  456. // ExampleInput return a string of the rendered Go code for an example's input parameters
  457. func (o *Operation) ExampleInput() string {
  458. if len(o.InputRef.Shape.MemberRefs) == 0 {
  459. if strings.Contains(o.InputRef.GoTypeElem(), ".") {
  460. o.imports[SDKImportRoot+"service/"+strings.Split(o.InputRef.GoTypeElem(), ".")[0]] = true
  461. return fmt.Sprintf("var params *%s", o.InputRef.GoTypeElem())
  462. }
  463. return fmt.Sprintf("var params *%s.%s",
  464. o.API.PackageName(), o.InputRef.GoTypeElem())
  465. }
  466. e := example{o, map[string]int{}}
  467. return "params := " + e.traverseAny(o.InputRef.Shape, false, false)
  468. }
  469. // ShouldDiscardResponse returns if the operation should discard the response
  470. // returned by the service.
  471. func (o *Operation) ShouldDiscardResponse() bool {
  472. s := o.OutputRef.Shape
  473. return s.Placeholder || len(s.MemberRefs) == 0
  474. }
  475. // A example provides
  476. type example struct {
  477. *Operation
  478. visited map[string]int
  479. }
  480. // traverseAny returns rendered Go code for the shape.
  481. func (e *example) traverseAny(s *Shape, required, payload bool) string {
  482. str := ""
  483. e.visited[s.ShapeName]++
  484. switch s.Type {
  485. case "structure":
  486. str = e.traverseStruct(s, required, payload)
  487. case "list":
  488. str = e.traverseList(s, required, payload)
  489. case "map":
  490. str = e.traverseMap(s, required, payload)
  491. case "jsonvalue":
  492. str = "aws.JSONValue{\"key\": \"value\"}"
  493. if required {
  494. str += " // Required"
  495. }
  496. default:
  497. str = e.traverseScalar(s, required, payload)
  498. }
  499. e.visited[s.ShapeName]--
  500. return str
  501. }
  502. var reType = regexp.MustCompile(`\b([A-Z])`)
  503. // traverseStruct returns rendered Go code for a structure type shape.
  504. func (e *example) traverseStruct(s *Shape, required, payload bool) string {
  505. var buf bytes.Buffer
  506. if s.resolvePkg != "" {
  507. e.imports[s.resolvePkg] = true
  508. buf.WriteString("&" + s.GoTypeElem() + "{")
  509. } else {
  510. buf.WriteString("&" + s.API.PackageName() + "." + s.GoTypeElem() + "{")
  511. }
  512. if required {
  513. buf.WriteString(" // Required")
  514. }
  515. buf.WriteString("\n")
  516. req := make([]string, len(s.Required))
  517. copy(req, s.Required)
  518. sort.Strings(req)
  519. if e.visited[s.ShapeName] < 2 {
  520. for _, n := range req {
  521. m := s.MemberRefs[n].Shape
  522. p := n == s.Payload && (s.MemberRefs[n].Streaming || m.Streaming)
  523. buf.WriteString(n + ": " + e.traverseAny(m, true, p) + ",")
  524. if m.Type != "list" && m.Type != "structure" && m.Type != "map" {
  525. buf.WriteString(" // Required")
  526. }
  527. buf.WriteString("\n")
  528. }
  529. for _, n := range s.MemberNames() {
  530. if s.IsRequired(n) {
  531. continue
  532. }
  533. m := s.MemberRefs[n].Shape
  534. p := n == s.Payload && (s.MemberRefs[n].Streaming || m.Streaming)
  535. buf.WriteString(n + ": " + e.traverseAny(m, false, p) + ",\n")
  536. }
  537. } else {
  538. buf.WriteString("// Recursive values...\n")
  539. }
  540. buf.WriteString("}")
  541. return buf.String()
  542. }
  543. // traverseMap returns rendered Go code for a map type shape.
  544. func (e *example) traverseMap(s *Shape, required, payload bool) string {
  545. var buf bytes.Buffer
  546. t := ""
  547. if s.resolvePkg != "" {
  548. e.imports[s.resolvePkg] = true
  549. t = s.GoTypeElem()
  550. } else {
  551. t = reType.ReplaceAllString(s.GoTypeElem(), s.API.PackageName()+".$1")
  552. }
  553. buf.WriteString(t + "{")
  554. if required {
  555. buf.WriteString(" // Required")
  556. }
  557. buf.WriteString("\n")
  558. if e.visited[s.ShapeName] < 2 {
  559. m := s.ValueRef.Shape
  560. buf.WriteString("\"Key\": " + e.traverseAny(m, true, false) + ",")
  561. if m.Type != "list" && m.Type != "structure" && m.Type != "map" {
  562. buf.WriteString(" // Required")
  563. }
  564. buf.WriteString("\n// More values...\n")
  565. } else {
  566. buf.WriteString("// Recursive values...\n")
  567. }
  568. buf.WriteString("}")
  569. return buf.String()
  570. }
  571. // traverseList returns rendered Go code for a list type shape.
  572. func (e *example) traverseList(s *Shape, required, payload bool) string {
  573. var buf bytes.Buffer
  574. t := ""
  575. if s.resolvePkg != "" {
  576. e.imports[s.resolvePkg] = true
  577. t = s.GoTypeElem()
  578. } else {
  579. t = reType.ReplaceAllString(s.GoTypeElem(), s.API.PackageName()+".$1")
  580. }
  581. buf.WriteString(t + "{")
  582. if required {
  583. buf.WriteString(" // Required")
  584. }
  585. buf.WriteString("\n")
  586. if e.visited[s.ShapeName] < 2 {
  587. m := s.MemberRef.Shape
  588. buf.WriteString(e.traverseAny(m, true, false) + ",")
  589. if m.Type != "list" && m.Type != "structure" && m.Type != "map" {
  590. buf.WriteString(" // Required")
  591. }
  592. buf.WriteString("\n// More values...\n")
  593. } else {
  594. buf.WriteString("// Recursive values...\n")
  595. }
  596. buf.WriteString("}")
  597. return buf.String()
  598. }
  599. // traverseScalar returns an AWS Type string representation initialized to a value.
  600. // Will panic if s is an unsupported shape type.
  601. func (e *example) traverseScalar(s *Shape, required, payload bool) string {
  602. str := ""
  603. switch s.Type {
  604. case "integer", "long":
  605. str = `aws.Int64(1)`
  606. case "float", "double":
  607. str = `aws.Float64(1.0)`
  608. case "string", "character":
  609. str = `aws.String("` + s.ShapeName + `")`
  610. case "blob":
  611. if payload {
  612. str = `bytes.NewReader([]byte("PAYLOAD"))`
  613. } else {
  614. str = `[]byte("PAYLOAD")`
  615. }
  616. case "boolean":
  617. str = `aws.Bool(true)`
  618. case "timestamp":
  619. str = `aws.Time(time.Now())`
  620. default:
  621. panic("unsupported shape " + s.Type)
  622. }
  623. return str
  624. }