operation.go 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. package api
  2. import (
  3. "bytes"
  4. "fmt"
  5. "regexp"
  6. "sort"
  7. "strings"
  8. "text/template"
  9. )
  10. // An Operation defines a specific API Operation.
  11. type Operation struct {
  12. API *API `json:"-"`
  13. ExportedName string
  14. Name string
  15. Documentation string
  16. HTTP HTTPInfo
  17. InputRef ShapeRef `json:"input"`
  18. OutputRef ShapeRef `json:"output"`
  19. Paginator *Paginator
  20. }
  21. // A HTTPInfo defines the method of HTTP request for the Operation.
  22. type HTTPInfo struct {
  23. Method string
  24. RequestURI string
  25. ResponseCode uint
  26. }
  27. // HasInput returns if the Operation accepts an input paramater
  28. func (o *Operation) HasInput() bool {
  29. return o.InputRef.ShapeName != ""
  30. }
  31. // HasOutput returns if the Operation accepts an output parameter
  32. func (o *Operation) HasOutput() bool {
  33. return o.OutputRef.ShapeName != ""
  34. }
  35. // tplOperation defines a template for rendering an API Operation
  36. var tplOperation = template.Must(template.New("operation").Parse(`
  37. const op{{ .ExportedName }} = "{{ .Name }}"
  38. // {{ .ExportedName }}Request generates a request for the {{ .ExportedName }} operation.
  39. func (c *{{ .API.StructName }}) {{ .ExportedName }}Request(` +
  40. `input {{ .InputRef.GoType }}) (req *request.Request, output {{ .OutputRef.GoType }}) {
  41. op := &request.Operation{
  42. Name: op{{ .ExportedName }},
  43. {{ if ne .HTTP.Method "" }}HTTPMethod: "{{ .HTTP.Method }}",
  44. {{ end }}{{ if ne .HTTP.RequestURI "" }}HTTPPath: "{{ .HTTP.RequestURI }}",
  45. {{ end }}{{ if .Paginator }}Paginator: &request.Paginator{
  46. InputTokens: {{ .Paginator.InputTokensString }},
  47. OutputTokens: {{ .Paginator.OutputTokensString }},
  48. LimitToken: "{{ .Paginator.LimitKey }}",
  49. TruncationToken: "{{ .Paginator.MoreResults }}",
  50. },
  51. {{ end }}
  52. }
  53. if input == nil {
  54. input = &{{ .InputRef.GoTypeElem }}{}
  55. }
  56. req = c.newRequest(op, input, output)
  57. output = &{{ .OutputRef.GoTypeElem }}{}
  58. req.Data = output
  59. return
  60. }
  61. {{ .Documentation }}func (c *{{ .API.StructName }}) {{ .ExportedName }}(` +
  62. `input {{ .InputRef.GoType }}) ({{ .OutputRef.GoType }}, error) {
  63. req, out := c.{{ .ExportedName }}Request(input)
  64. err := req.Send()
  65. return out, err
  66. }
  67. {{ if .Paginator }}
  68. func (c *{{ .API.StructName }}) {{ .ExportedName }}Pages(` +
  69. `input {{ .InputRef.GoType }}, fn func(p {{ .OutputRef.GoType }}, lastPage bool) (shouldContinue bool)) error {
  70. page, _ := c.{{ .ExportedName }}Request(input)
  71. return page.EachPage(func(p interface{}, lastPage bool) bool {
  72. return fn(p.({{ .OutputRef.GoType }}), lastPage)
  73. })
  74. }
  75. {{ end }}
  76. `))
  77. // GoCode returns a string of rendered GoCode for this Operation
  78. func (o *Operation) GoCode() string {
  79. var buf bytes.Buffer
  80. err := tplOperation.Execute(&buf, o)
  81. if err != nil {
  82. panic(err)
  83. }
  84. return strings.TrimSpace(buf.String())
  85. }
  86. // tplInfSig defines the template for rendering an Operation's signature within an Interface definition.
  87. var tplInfSig = template.Must(template.New("opsig").Parse(`
  88. {{ .ExportedName }}Request({{ .InputRef.GoTypeWithPkgName }}) (*request.Request, {{ .OutputRef.GoTypeWithPkgName }})
  89. {{ .ExportedName }}({{ .InputRef.GoTypeWithPkgName }}) ({{ .OutputRef.GoTypeWithPkgName }}, error)
  90. {{ if .Paginator }}
  91. {{ .ExportedName }}Pages({{ .InputRef.GoTypeWithPkgName }}, func({{ .OutputRef.GoTypeWithPkgName }}, bool) bool) error{{ end }}
  92. `))
  93. // InterfaceSignature returns a string representing the Operation's interface{}
  94. // functional signature.
  95. func (o *Operation) InterfaceSignature() string {
  96. var buf bytes.Buffer
  97. err := tplInfSig.Execute(&buf, o)
  98. if err != nil {
  99. panic(err)
  100. }
  101. return strings.TrimSpace(buf.String())
  102. }
  103. // tplExample defines the template for rendering an Operation example
  104. var tplExample = template.Must(template.New("operationExample").Parse(`
  105. func Example{{ .API.StructName }}_{{ .ExportedName }}() {
  106. svc := {{ .API.NewAPIGoCodeWithPkgName "nil" }}
  107. {{ .ExampleInput }}
  108. resp, err := svc.{{ .ExportedName }}(params)
  109. if err != nil {
  110. // Print the error, cast err to awserr.Error to get the Code and
  111. // Message from an error.
  112. fmt.Println(err.Error())
  113. return
  114. }
  115. // Pretty-print the response data.
  116. fmt.Println(resp)
  117. }
  118. `))
  119. // Example returns a string of the rendered Go code for the Operation
  120. func (o *Operation) Example() string {
  121. var buf bytes.Buffer
  122. err := tplExample.Execute(&buf, o)
  123. if err != nil {
  124. panic(err)
  125. }
  126. return strings.TrimSpace(buf.String())
  127. }
  128. // ExampleInput return a string of the rendered Go code for an example's input parameters
  129. func (o *Operation) ExampleInput() string {
  130. if len(o.InputRef.Shape.MemberRefs) == 0 {
  131. return fmt.Sprintf("var params *%s.%s",
  132. o.API.PackageName(), o.InputRef.GoTypeElem())
  133. }
  134. e := example{o, map[string]int{}}
  135. return "params := " + e.traverseAny(o.InputRef.Shape, false, false)
  136. }
  137. // A example provides
  138. type example struct {
  139. *Operation
  140. visited map[string]int
  141. }
  142. // traverseAny returns rendered Go code for the shape.
  143. func (e *example) traverseAny(s *Shape, required, payload bool) string {
  144. str := ""
  145. e.visited[s.ShapeName]++
  146. switch s.Type {
  147. case "structure":
  148. str = e.traverseStruct(s, required, payload)
  149. case "list":
  150. str = e.traverseList(s, required, payload)
  151. case "map":
  152. str = e.traverseMap(s, required, payload)
  153. default:
  154. str = e.traverseScalar(s, required, payload)
  155. }
  156. e.visited[s.ShapeName]--
  157. return str
  158. }
  159. var reType = regexp.MustCompile(`\b([A-Z])`)
  160. // traverseStruct returns rendered Go code for a structure type shape.
  161. func (e *example) traverseStruct(s *Shape, required, payload bool) string {
  162. var buf bytes.Buffer
  163. buf.WriteString("&" + s.API.PackageName() + "." + s.GoTypeElem() + "{")
  164. if required {
  165. buf.WriteString(" // Required")
  166. }
  167. buf.WriteString("\n")
  168. req := make([]string, len(s.Required))
  169. copy(req, s.Required)
  170. sort.Strings(req)
  171. if e.visited[s.ShapeName] < 2 {
  172. for _, n := range req {
  173. m := s.MemberRefs[n].Shape
  174. p := n == s.Payload && (s.MemberRefs[n].Streaming || m.Streaming)
  175. buf.WriteString(n + ": " + e.traverseAny(m, true, p) + ",")
  176. if m.Type != "list" && m.Type != "structure" && m.Type != "map" {
  177. buf.WriteString(" // Required")
  178. }
  179. buf.WriteString("\n")
  180. }
  181. for _, n := range s.MemberNames() {
  182. if s.IsRequired(n) {
  183. continue
  184. }
  185. m := s.MemberRefs[n].Shape
  186. p := n == s.Payload && (s.MemberRefs[n].Streaming || m.Streaming)
  187. buf.WriteString(n + ": " + e.traverseAny(m, false, p) + ",\n")
  188. }
  189. } else {
  190. buf.WriteString("// Recursive values...\n")
  191. }
  192. buf.WriteString("}")
  193. return buf.String()
  194. }
  195. // traverseMap returns rendered Go code for a map type shape.
  196. func (e *example) traverseMap(s *Shape, required, payload bool) string {
  197. var buf bytes.Buffer
  198. t := reType.ReplaceAllString(s.GoTypeElem(), s.API.PackageName()+".$1")
  199. buf.WriteString(t + "{")
  200. if required {
  201. buf.WriteString(" // Required")
  202. }
  203. buf.WriteString("\n")
  204. if e.visited[s.ShapeName] < 2 {
  205. m := s.ValueRef.Shape
  206. buf.WriteString("\"Key\": " + e.traverseAny(m, true, false) + ",")
  207. if m.Type != "list" && m.Type != "structure" && m.Type != "map" {
  208. buf.WriteString(" // Required")
  209. }
  210. buf.WriteString("\n// More values...\n")
  211. } else {
  212. buf.WriteString("// Recursive values...\n")
  213. }
  214. buf.WriteString("}")
  215. return buf.String()
  216. }
  217. // traverseList returns rendered Go code for a list type shape.
  218. func (e *example) traverseList(s *Shape, required, payload bool) string {
  219. var buf bytes.Buffer
  220. t := reType.ReplaceAllString(s.GoTypeElem(), s.API.PackageName()+".$1")
  221. buf.WriteString(t + "{")
  222. if required {
  223. buf.WriteString(" // Required")
  224. }
  225. buf.WriteString("\n")
  226. if e.visited[s.ShapeName] < 2 {
  227. m := s.MemberRef.Shape
  228. buf.WriteString(e.traverseAny(m, true, false) + ",")
  229. if m.Type != "list" && m.Type != "structure" && m.Type != "map" {
  230. buf.WriteString(" // Required")
  231. }
  232. buf.WriteString("\n// More values...\n")
  233. } else {
  234. buf.WriteString("// Recursive values...\n")
  235. }
  236. buf.WriteString("}")
  237. return buf.String()
  238. }
  239. // traverseScalar returns an AWS Type string representation initialized to a value.
  240. // Will panic if s is an unsupported shape type.
  241. func (e *example) traverseScalar(s *Shape, required, payload bool) string {
  242. str := ""
  243. switch s.Type {
  244. case "integer", "long":
  245. str = `aws.Int64(1)`
  246. case "float", "double":
  247. str = `aws.Float64(1.0)`
  248. case "string", "character":
  249. str = `aws.String("` + s.ShapeName + `")`
  250. case "blob":
  251. if payload {
  252. str = `bytes.NewReader([]byte("PAYLOAD"))`
  253. } else {
  254. str = `[]byte("PAYLOAD")`
  255. }
  256. case "boolean":
  257. str = `aws.Bool(true)`
  258. case "timestamp":
  259. str = `aws.Time(time.Now())`
  260. default:
  261. panic("unsupported shape " + s.Type)
  262. }
  263. return str
  264. }