operation.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426
  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. InputRef ShapeRef `json:"input"`
  19. OutputRef ShapeRef `json:"output"`
  20. ErrorRefs []ShapeRef `json:"errors"`
  21. Paginator *Paginator
  22. Deprecated bool `json:"deprecated"`
  23. AuthType string `json:"authtype"`
  24. imports map[string]bool
  25. }
  26. // A HTTPInfo defines the method of HTTP request for the Operation.
  27. type HTTPInfo struct {
  28. Method string
  29. RequestURI string
  30. ResponseCode uint
  31. }
  32. // HasInput returns if the Operation accepts an input paramater
  33. func (o *Operation) HasInput() bool {
  34. return o.InputRef.ShapeName != ""
  35. }
  36. // HasOutput returns if the Operation accepts an output parameter
  37. func (o *Operation) HasOutput() bool {
  38. return o.OutputRef.ShapeName != ""
  39. }
  40. // tplOperation defines a template for rendering an API Operation
  41. var tplOperation = template.Must(template.New("operation").Funcs(template.FuncMap{
  42. "GetCrosslinkURL": GetCrosslinkURL,
  43. }).Parse(`
  44. const op{{ .ExportedName }} = "{{ .Name }}"
  45. // {{ .ExportedName }}Request generates a "aws/request.Request" representing the
  46. // client's request for the {{ .ExportedName }} operation. The "output" return
  47. // value can be used to capture response data after the request's "Send" method
  48. // is called.
  49. //
  50. // See {{ .ExportedName }} for usage and error information.
  51. //
  52. // Creating a request object using this method should be used when you want to inject
  53. // custom logic into the request's lifecycle using a custom handler, or if you want to
  54. // access properties on the request object before or after sending the request. If
  55. // you just want the service response, call the {{ .ExportedName }} method directly
  56. // instead.
  57. //
  58. // Note: You must call the "Send" method on the returned request object in order
  59. // to execute the request.
  60. //
  61. // // Example sending a request using the {{ .ExportedName }}Request method.
  62. // req, resp := client.{{ .ExportedName }}Request(params)
  63. //
  64. // err := req.Send()
  65. // if err == nil { // resp is now filled
  66. // fmt.Println(resp)
  67. // }
  68. {{ $crosslinkURL := GetCrosslinkURL $.API.BaseCrosslinkURL $.API.APIName $.API.Metadata.UID $.ExportedName -}}
  69. {{ if ne $crosslinkURL "" -}}
  70. //
  71. // Please also see {{ $crosslinkURL }}
  72. {{ end -}}
  73. func (c *{{ .API.StructName }}) {{ .ExportedName }}Request(` +
  74. `input {{ .InputRef.GoType }}) (req *request.Request, output {{ .OutputRef.GoType }}) {
  75. {{ if (or .Deprecated (or .InputRef.Deprecated .OutputRef.Deprecated)) }}if c.Client.Config.Logger != nil {
  76. c.Client.Config.Logger.Log("This operation, {{ .ExportedName }}, has been deprecated")
  77. }
  78. op := &request.Operation{ {{ else }} op := &request.Operation{ {{ end }}
  79. Name: op{{ .ExportedName }},
  80. {{ if ne .HTTP.Method "" }}HTTPMethod: "{{ .HTTP.Method }}",
  81. {{ end }}HTTPPath: {{ if ne .HTTP.RequestURI "" }}"{{ .HTTP.RequestURI }}"{{ else }}"/"{{ end }},
  82. {{ if .Paginator }}Paginator: &request.Paginator{
  83. InputTokens: {{ .Paginator.InputTokensString }},
  84. OutputTokens: {{ .Paginator.OutputTokensString }},
  85. LimitToken: "{{ .Paginator.LimitKey }}",
  86. TruncationToken: "{{ .Paginator.MoreResults }}",
  87. },
  88. {{ end }}
  89. }
  90. if input == nil {
  91. input = &{{ .InputRef.GoTypeElem }}{}
  92. }
  93. output = &{{ .OutputRef.GoTypeElem }}{}
  94. req = c.newRequest(op, input, output){{ if eq .OutputRef.Shape.Placeholder true }}
  95. req.Handlers.Unmarshal.Remove({{ .API.ProtocolPackage }}.UnmarshalHandler)
  96. req.Handlers.Unmarshal.PushBackNamed(protocol.UnmarshalDiscardBodyHandler){{ end }}
  97. {{ if eq .AuthType "none" }}req.Config.Credentials = credentials.AnonymousCredentials
  98. {{ end -}}
  99. return
  100. }
  101. // {{ .ExportedName }} API operation for {{ .API.Metadata.ServiceFullName }}.
  102. {{ if .Documentation -}}
  103. //
  104. {{ .Documentation }}
  105. {{ end -}}
  106. //
  107. // Returns awserr.Error for service API and SDK errors. Use runtime type assertions
  108. // with awserr.Error's Code and Message methods to get detailed information about
  109. // the error.
  110. //
  111. // See the AWS API reference guide for {{ .API.Metadata.ServiceFullName }}'s
  112. // API operation {{ .ExportedName }} for usage and error information.
  113. {{ if .ErrorRefs -}}
  114. //
  115. // Returned Error Codes:
  116. {{ range $_, $err := .ErrorRefs -}}
  117. {{ $errDoc := $err.IndentedDocstring -}}
  118. // * {{ $err.Shape.ErrorName }}
  119. {{ if $errDoc -}}
  120. {{ $errDoc }}{{ end }}
  121. //
  122. {{ end -}}
  123. {{ end -}}
  124. {{ $crosslinkURL := GetCrosslinkURL $.API.BaseCrosslinkURL $.API.APIName $.API.Metadata.UID $.ExportedName -}}
  125. {{ if ne $crosslinkURL "" -}}
  126. // Please also see {{ $crosslinkURL }}
  127. {{ end -}}
  128. func (c *{{ .API.StructName }}) {{ .ExportedName }}(` +
  129. `input {{ .InputRef.GoType }}) ({{ .OutputRef.GoType }}, error) {
  130. req, out := c.{{ .ExportedName }}Request(input)
  131. err := req.Send()
  132. return out, err
  133. }
  134. {{ if .Paginator }}
  135. // {{ .ExportedName }}Pages iterates over the pages of a {{ .ExportedName }} operation,
  136. // calling the "fn" function with the response data for each page. To stop
  137. // iterating, return false from the fn function.
  138. //
  139. // See {{ .ExportedName }} method for more information on how to use this operation.
  140. //
  141. // Note: This operation can generate multiple requests to a service.
  142. //
  143. // // Example iterating over at most 3 pages of a {{ .ExportedName }} operation.
  144. // pageNum := 0
  145. // err := client.{{ .ExportedName }}Pages(params,
  146. // func(page {{ .OutputRef.GoType }}, lastPage bool) bool {
  147. // pageNum++
  148. // fmt.Println(page)
  149. // return pageNum <= 3
  150. // })
  151. //
  152. func (c *{{ .API.StructName }}) {{ .ExportedName }}Pages(` +
  153. `input {{ .InputRef.GoType }}, fn func(p {{ .OutputRef.GoType }}, lastPage bool) (shouldContinue bool)) error {
  154. page, _ := c.{{ .ExportedName }}Request(input)
  155. page.Handlers.Build.PushBack(request.MakeAddToUserAgentFreeFormHandler("Paginator"))
  156. return page.EachPage(func(p interface{}, lastPage bool) bool {
  157. return fn(p.({{ .OutputRef.GoType }}), lastPage)
  158. })
  159. }
  160. {{ end }}
  161. `))
  162. // GoCode returns a string of rendered GoCode for this Operation
  163. func (o *Operation) GoCode() string {
  164. var buf bytes.Buffer
  165. err := tplOperation.Execute(&buf, o)
  166. if err != nil {
  167. panic(err)
  168. }
  169. return strings.TrimSpace(buf.String())
  170. }
  171. // tplInfSig defines the template for rendering an Operation's signature within an Interface definition.
  172. var tplInfSig = template.Must(template.New("opsig").Parse(`
  173. {{ .ExportedName }}Request({{ .InputRef.GoTypeWithPkgName }}) (*request.Request, {{ .OutputRef.GoTypeWithPkgName }})
  174. {{ .ExportedName }}({{ .InputRef.GoTypeWithPkgName }}) ({{ .OutputRef.GoTypeWithPkgName }}, error)
  175. {{ if .Paginator -}}
  176. {{ .ExportedName }}Pages({{ .InputRef.GoTypeWithPkgName }}, func({{ .OutputRef.GoTypeWithPkgName }}, bool) bool) error
  177. {{- end }}
  178. `))
  179. // InterfaceSignature returns a string representing the Operation's interface{}
  180. // functional signature.
  181. func (o *Operation) InterfaceSignature() string {
  182. var buf bytes.Buffer
  183. err := tplInfSig.Execute(&buf, o)
  184. if err != nil {
  185. panic(err)
  186. }
  187. return strings.TrimSpace(buf.String())
  188. }
  189. // tplExample defines the template for rendering an Operation example
  190. var tplExample = template.Must(template.New("operationExample").Parse(`
  191. func Example{{ .API.StructName }}_{{ .ExportedName }}() {
  192. sess, err := session.NewSession()
  193. if err != nil {
  194. fmt.Println("failed to create session,", err)
  195. return
  196. }
  197. svc := {{ .API.PackageName }}.New(sess)
  198. {{ .ExampleInput }}
  199. resp, err := svc.{{ .ExportedName }}(params)
  200. if err != nil {
  201. // Print the error, cast err to awserr.Error to get the Code and
  202. // Message from an error.
  203. fmt.Println(err.Error())
  204. return
  205. }
  206. // Pretty-print the response data.
  207. fmt.Println(resp)
  208. }
  209. `))
  210. // Example returns a string of the rendered Go code for the Operation
  211. func (o *Operation) Example() string {
  212. var buf bytes.Buffer
  213. err := tplExample.Execute(&buf, o)
  214. if err != nil {
  215. panic(err)
  216. }
  217. return strings.TrimSpace(buf.String())
  218. }
  219. // ExampleInput return a string of the rendered Go code for an example's input parameters
  220. func (o *Operation) ExampleInput() string {
  221. if len(o.InputRef.Shape.MemberRefs) == 0 {
  222. if strings.Contains(o.InputRef.GoTypeElem(), ".") {
  223. o.imports["github.com/aws/aws-sdk-go/service/"+strings.Split(o.InputRef.GoTypeElem(), ".")[0]] = true
  224. return fmt.Sprintf("var params *%s", o.InputRef.GoTypeElem())
  225. }
  226. return fmt.Sprintf("var params *%s.%s",
  227. o.API.PackageName(), o.InputRef.GoTypeElem())
  228. }
  229. e := example{o, map[string]int{}}
  230. return "params := " + e.traverseAny(o.InputRef.Shape, false, false)
  231. }
  232. // A example provides
  233. type example struct {
  234. *Operation
  235. visited map[string]int
  236. }
  237. // traverseAny returns rendered Go code for the shape.
  238. func (e *example) traverseAny(s *Shape, required, payload bool) string {
  239. str := ""
  240. e.visited[s.ShapeName]++
  241. switch s.Type {
  242. case "structure":
  243. str = e.traverseStruct(s, required, payload)
  244. case "list":
  245. str = e.traverseList(s, required, payload)
  246. case "map":
  247. str = e.traverseMap(s, required, payload)
  248. default:
  249. str = e.traverseScalar(s, required, payload)
  250. }
  251. e.visited[s.ShapeName]--
  252. return str
  253. }
  254. var reType = regexp.MustCompile(`\b([A-Z])`)
  255. // traverseStruct returns rendered Go code for a structure type shape.
  256. func (e *example) traverseStruct(s *Shape, required, payload bool) string {
  257. var buf bytes.Buffer
  258. if s.resolvePkg != "" {
  259. e.imports[s.resolvePkg] = true
  260. buf.WriteString("&" + s.GoTypeElem() + "{")
  261. } else {
  262. buf.WriteString("&" + s.API.PackageName() + "." + s.GoTypeElem() + "{")
  263. }
  264. if required {
  265. buf.WriteString(" // Required")
  266. }
  267. buf.WriteString("\n")
  268. req := make([]string, len(s.Required))
  269. copy(req, s.Required)
  270. sort.Strings(req)
  271. if e.visited[s.ShapeName] < 2 {
  272. for _, n := range req {
  273. m := s.MemberRefs[n].Shape
  274. p := n == s.Payload && (s.MemberRefs[n].Streaming || m.Streaming)
  275. buf.WriteString(n + ": " + e.traverseAny(m, true, p) + ",")
  276. if m.Type != "list" && m.Type != "structure" && m.Type != "map" {
  277. buf.WriteString(" // Required")
  278. }
  279. buf.WriteString("\n")
  280. }
  281. for _, n := range s.MemberNames() {
  282. if s.IsRequired(n) {
  283. continue
  284. }
  285. m := s.MemberRefs[n].Shape
  286. p := n == s.Payload && (s.MemberRefs[n].Streaming || m.Streaming)
  287. buf.WriteString(n + ": " + e.traverseAny(m, false, p) + ",\n")
  288. }
  289. } else {
  290. buf.WriteString("// Recursive values...\n")
  291. }
  292. buf.WriteString("}")
  293. return buf.String()
  294. }
  295. // traverseMap returns rendered Go code for a map type shape.
  296. func (e *example) traverseMap(s *Shape, required, payload bool) string {
  297. var buf bytes.Buffer
  298. t := ""
  299. if s.resolvePkg != "" {
  300. e.imports[s.resolvePkg] = true
  301. t = s.GoTypeElem()
  302. } else {
  303. t = reType.ReplaceAllString(s.GoTypeElem(), s.API.PackageName()+".$1")
  304. }
  305. buf.WriteString(t + "{")
  306. if required {
  307. buf.WriteString(" // Required")
  308. }
  309. buf.WriteString("\n")
  310. if e.visited[s.ShapeName] < 2 {
  311. m := s.ValueRef.Shape
  312. buf.WriteString("\"Key\": " + e.traverseAny(m, true, false) + ",")
  313. if m.Type != "list" && m.Type != "structure" && m.Type != "map" {
  314. buf.WriteString(" // Required")
  315. }
  316. buf.WriteString("\n// More values...\n")
  317. } else {
  318. buf.WriteString("// Recursive values...\n")
  319. }
  320. buf.WriteString("}")
  321. return buf.String()
  322. }
  323. // traverseList returns rendered Go code for a list type shape.
  324. func (e *example) traverseList(s *Shape, required, payload bool) string {
  325. var buf bytes.Buffer
  326. t := ""
  327. if s.resolvePkg != "" {
  328. e.imports[s.resolvePkg] = true
  329. t = s.GoTypeElem()
  330. } else {
  331. t = reType.ReplaceAllString(s.GoTypeElem(), s.API.PackageName()+".$1")
  332. }
  333. buf.WriteString(t + "{")
  334. if required {
  335. buf.WriteString(" // Required")
  336. }
  337. buf.WriteString("\n")
  338. if e.visited[s.ShapeName] < 2 {
  339. m := s.MemberRef.Shape
  340. buf.WriteString(e.traverseAny(m, true, false) + ",")
  341. if m.Type != "list" && m.Type != "structure" && m.Type != "map" {
  342. buf.WriteString(" // Required")
  343. }
  344. buf.WriteString("\n// More values...\n")
  345. } else {
  346. buf.WriteString("// Recursive values...\n")
  347. }
  348. buf.WriteString("}")
  349. return buf.String()
  350. }
  351. // traverseScalar returns an AWS Type string representation initialized to a value.
  352. // Will panic if s is an unsupported shape type.
  353. func (e *example) traverseScalar(s *Shape, required, payload bool) string {
  354. str := ""
  355. switch s.Type {
  356. case "integer", "long":
  357. str = `aws.Int64(1)`
  358. case "float", "double":
  359. str = `aws.Float64(1.0)`
  360. case "string", "character":
  361. str = `aws.String("` + s.ShapeName + `")`
  362. case "blob":
  363. if payload {
  364. str = `bytes.NewReader([]byte("PAYLOAD"))`
  365. } else {
  366. str = `[]byte("PAYLOAD")`
  367. }
  368. case "boolean":
  369. str = `aws.Bool(true)`
  370. case "timestamp":
  371. str = `aws.Time(time.Now())`
  372. default:
  373. panic("unsupported shape " + s.Type)
  374. }
  375. return str
  376. }