api.go 23 KB

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