1//go:build codegen
2// +build codegen
3
4package api
5
6import (
7	"bytes"
8	"fmt"
9	"regexp"
10	"sort"
11	"strings"
12	"text/template"
13)
14
15// An Operation defines a specific API Operation.
16type Operation struct {
17	API                 *API `json:"-"`
18	ExportedName        string
19	Name                string
20	Documentation       string
21	HTTP                HTTPInfo
22	Host                string     `json:"host"`
23	InputRef            ShapeRef   `json:"input"`
24	OutputRef           ShapeRef   `json:"output"`
25	ErrorRefs           []ShapeRef `json:"errors"`
26	Paginator           *Paginator
27	Deprecated          bool     `json:"deprecated"`
28	DeprecatedMsg       string   `json:"deprecatedMessage"`
29	AuthType            AuthType `json:"authtype"`
30	imports             map[string]bool
31	CustomBuildHandlers []string
32
33	EventStreamAPI *EventStreamAPI
34
35	IsEndpointDiscoveryOp  bool               `json:"endpointoperation"`
36	EndpointDiscovery      *EndpointDiscovery `json:"endpointdiscovery"`
37	Endpoint               *EndpointTrait     `json:"endpoint"`
38	IsHttpChecksumRequired bool               `json:"httpChecksumRequired"`
39}
40
41// EndpointTrait provides the structure of the modeled endpoint trait, and its
42// properties.
43type EndpointTrait struct {
44	// Specifies the hostPrefix template to prepend to the operation's request
45	// endpoint host.
46	HostPrefix string `json:"hostPrefix"`
47}
48
49// EndpointDiscovery represents a map of key values pairs that represents
50// metadata about how a given API will make a call to the discovery endpoint.
51type EndpointDiscovery struct {
52	// Required indicates that for a given operation that endpoint is required.
53	// Any required endpoint discovery operation cannot have endpoint discovery
54	// turned off.
55	Required bool `json:"required"`
56}
57
58// OperationForMethod returns the API operation name that corresponds to the
59// client method name provided.
60func (a *API) OperationForMethod(name string) *Operation {
61	for _, op := range a.Operations {
62		for _, m := range op.Methods() {
63			if m == name {
64				return op
65			}
66		}
67	}
68
69	return nil
70}
71
72// A HTTPInfo defines the method of HTTP request for the Operation.
73type HTTPInfo struct {
74	Method       string
75	RequestURI   string
76	ResponseCode uint
77}
78
79// Methods Returns a list of method names that will be generated.
80func (o *Operation) Methods() []string {
81	methods := []string{
82		o.ExportedName,
83		o.ExportedName + "Request",
84		o.ExportedName + "WithContext",
85	}
86
87	if o.Paginator != nil {
88		methods = append(methods, []string{
89			o.ExportedName + "Pages",
90			o.ExportedName + "PagesWithContext",
91		}...)
92	}
93
94	return methods
95}
96
97// HasInput returns if the Operation accepts an input parameter
98func (o *Operation) HasInput() bool {
99	return o.InputRef.ShapeName != ""
100}
101
102// HasOutput returns if the Operation accepts an output parameter
103func (o *Operation) HasOutput() bool {
104	return o.OutputRef.ShapeName != ""
105}
106
107// AuthType provides the enumeration of AuthType trait.
108type AuthType string
109
110// Enumeration values for AuthType trait
111const (
112	NoneAuthType           AuthType = "none"
113	V4UnsignedBodyAuthType AuthType = "v4-unsigned-body"
114)
115
116// ShouldSignRequestBody returns if the operation request body should be signed
117// or not.
118func (o *Operation) ShouldSignRequestBody() bool {
119	switch o.AuthType {
120	case NoneAuthType, V4UnsignedBodyAuthType:
121		return false
122	default:
123		return true
124	}
125}
126
127// GetSigner returns the signer that should be used for a API request.
128func (o *Operation) GetSigner() string {
129	buf := bytes.NewBuffer(nil)
130
131	switch o.AuthType {
132	case NoneAuthType:
133		o.API.AddSDKImport("aws/credentials")
134
135		buf.WriteString("req.Config.Credentials = credentials.AnonymousCredentials")
136	case V4UnsignedBodyAuthType:
137		o.API.AddSDKImport("aws/signer/v4")
138
139		buf.WriteString("req.Handlers.Sign.Remove(v4.SignRequestHandler)\n")
140		buf.WriteString("handler := v4.BuildNamedHandler(\"v4.CustomSignerHandler\", v4.WithUnsignedPayload)\n")
141		buf.WriteString("req.Handlers.Sign.PushFrontNamed(handler)")
142	}
143
144	return buf.String()
145}
146
147// HasAccountIDMemberWithARN returns true if an account id member exists for an input shape that may take in an ARN.
148func (o *Operation) HasAccountIDMemberWithARN() bool {
149	return o.InputRef.Shape.HasAccountIdMemberWithARN
150}
151
152// operationTmpl defines a template for rendering an API Operation
153var operationTmpl = template.Must(template.New("operation").Funcs(template.FuncMap{
154	"EnableStopOnSameToken": enableStopOnSameToken,
155	"GetDeprecatedMsg":      getDeprecatedMessage,
156}).Parse(`
157const op{{ .ExportedName }} = "{{ .Name }}"
158
159// {{ .ExportedName }}Request generates a "aws/request.Request" representing the
160// client's request for the {{ .ExportedName }} operation. The "output" return
161// value will be populated with the request's response once the request completes
162// successfully.
163//
164// Use "Send" method on the returned Request to send the API call to the service.
165// the "output" return value is not valid until after Send returns without error.
166//
167// See {{ .ExportedName }} for more information on using the {{ .ExportedName }}
168// API call, and error handling.
169//
170// This method is useful when you want to inject custom logic or configuration
171// into the SDK's request lifecycle. Such as custom headers, or retry logic.
172//
173//
174//    // Example sending a request using the {{ .ExportedName }}Request method.
175//    req, resp := client.{{ .ExportedName }}Request(params)
176//
177//    err := req.Send()
178//    if err == nil { // resp is now filled
179//        fmt.Println(resp)
180//    }
181{{ $crosslinkURL := $.API.GetCrosslinkURL $.ExportedName -}}
182{{ if ne $crosslinkURL "" -}}
183//
184// See also, {{ $crosslinkURL }}
185{{ end -}}
186{{- if .Deprecated }}//
187// Deprecated: {{ GetDeprecatedMsg .DeprecatedMsg .ExportedName }}
188{{ end -}}
189func (c *{{ .API.StructName }}) {{ .ExportedName }}Request(` +
190	`input {{ .InputRef.GoType }}) (req *request.Request, output {{ .OutputRef.GoType }}) {
191	{{ if (or .Deprecated (or .InputRef.Deprecated .OutputRef.Deprecated)) }}if c.Client.Config.Logger != nil {
192		c.Client.Config.Logger.Log("This operation, {{ .ExportedName }}, has been deprecated")
193	}
194	op := &request.Operation{ {{ else }} op := &request.Operation{ {{ end }}
195		Name:       op{{ .ExportedName }},
196		{{ if ne .HTTP.Method "" }}HTTPMethod: "{{ .HTTP.Method }}",
197		{{ end }}HTTPPath: {{ if ne .HTTP.RequestURI "" }}"{{ .HTTP.RequestURI }}"{{ else }}"/"{{ end }},
198		{{ if .Paginator }}Paginator: &request.Paginator{
199				InputTokens: {{ .Paginator.InputTokensString }},
200				OutputTokens: {{ .Paginator.OutputTokensString }},
201				LimitToken: "{{ .Paginator.LimitKey }}",
202				TruncationToken: "{{ .Paginator.MoreResults }}",
203		},
204		{{ end }}
205	}
206
207	if input == nil {
208		input = &{{ .InputRef.GoTypeElem }}{}
209	}
210
211	output = &{{ .OutputRef.GoTypeElem }}{}
212	req = c.newRequest(op, input, output)
213	{{- if ne .AuthType "" }}
214		{{ .GetSigner }}
215	{{- end }}
216
217	{{- if .HasAccountIDMemberWithARN }}
218		// update account id or check if provided input for account id member matches
219		// the account id present in ARN
220		req.Handlers.Validate.PushFrontNamed(updateAccountIDWithARNHandler)
221	{{- end }}
222
223	{{- if .ShouldDiscardResponse -}}
224		{{- $_ := .API.AddSDKImport "private/protocol" }}
225		{{- $_ := .API.AddSDKImport "private/protocol" .API.ProtocolPackage }}
226		req.Handlers.Unmarshal.Swap({{ .API.ProtocolPackage }}.UnmarshalHandler.Name, protocol.UnmarshalDiscardBodyHandler)
227	{{- else }}
228		{{- if $.EventStreamAPI }}
229			{{- $esapi := $.EventStreamAPI }}
230
231			{{- if $esapi.RequireHTTP2 }}
232				req.Handlers.UnmarshalMeta.PushBack(
233					protocol.RequireHTTPMinProtocol{Major:2}.Handler,
234				)
235			{{- end }}
236
237			es := New{{ $esapi.Name }}()
238			{{- if $esapi.Legacy }}
239				req.Handlers.Unmarshal.PushBack(es.setStreamCloser)
240			{{- end }}
241			output.{{ $esapi.OutputMemberName }} = es
242
243			{{- $inputStream := $esapi.InputStream }}
244			{{- $outputStream := $esapi.OutputStream }}
245
246			{{- $_ := .API.AddSDKImport "private/protocol" .API.ProtocolPackage }}
247			{{- $_ := .API.AddSDKImport "private/protocol/rest" }}
248
249			{{- if $inputStream }}
250
251				req.Handlers.Sign.PushFront(es.setupInputPipe)
252				req.Handlers.UnmarshalError.PushBackNamed(request.NamedHandler{
253					Name: "InputPipeCloser",
254					Fn: func (r *request.Request) {
255							err := es.closeInputPipe()
256							if err != nil {
257								r.Error = awserr.New(eventstreamapi.InputWriterCloseErrorCode, err.Error(), r.Error)
258							}
259						},
260				})
261				req.Handlers.Build.PushBack(request.WithSetRequestHeaders(map[string]string{
262					"Content-Type": "application/vnd.amazon.eventstream",
263					"X-Amz-Content-Sha256": "STREAMING-AWS4-HMAC-SHA256-EVENTS",
264				}))
265				req.Handlers.Build.Swap({{ .API.ProtocolPackage }}.BuildHandler.Name, rest.BuildHandler)
266				req.Handlers.Send.Swap(client.LogHTTPRequestHandler.Name, client.LogHTTPRequestHeaderHandler)
267				req.Handlers.Unmarshal.PushBack(es.runInputStream)
268
269				{{- if eq .API.Metadata.Protocol "json" }}
270					es.input = input
271					req.Handlers.Unmarshal.PushBack(es.sendInitialEvent)
272				{{- end }}
273			{{- end }}
274
275			{{- if $outputStream }}
276
277				req.Handlers.Send.Swap(client.LogHTTPResponseHandler.Name, client.LogHTTPResponseHeaderHandler)
278				req.Handlers.Unmarshal.Swap({{ .API.ProtocolPackage }}.UnmarshalHandler.Name, rest.UnmarshalHandler)
279				req.Handlers.Unmarshal.PushBack(es.runOutputStream)
280
281				{{- if eq .API.Metadata.Protocol "json" }}
282					es.output = output
283					req.Handlers.Unmarshal.PushBack(es.recvInitialEvent)
284				{{- end }}
285			{{- end }}
286			req.Handlers.Unmarshal.PushBack(es.runOnStreamPartClose)
287
288		{{- end }}
289	{{- end }}
290
291	{{- if .EndpointDiscovery }}
292		// if custom endpoint for the request is set to a non empty string,
293		// we skip the endpoint discovery workflow.
294		if req.Config.Endpoint == nil || *req.Config.Endpoint == "" {
295			{{- if not .EndpointDiscovery.Required }}
296				if aws.BoolValue(req.Config.EnableEndpointDiscovery) {
297			{{- end }}
298			de := discoverer{{ .API.EndpointDiscoveryOp.Name }}{
299				Required: {{ .EndpointDiscovery.Required }},
300				EndpointCache: c.endpointCache,
301				Params: map[string]*string{
302					"op": aws.String(req.Operation.Name),
303					{{- range $key, $ref := .InputRef.Shape.MemberRefs -}}
304						{{- if $ref.EndpointDiscoveryID -}}
305							{{- if ne (len $ref.LocationName) 0 -}}
306								"{{ $ref.LocationName }}": input.{{ $key }},
307							{{- else }}
308								"{{ $key }}": input.{{ $key }},
309							{{- end }}
310						{{- end }}
311					{{- end }}
312				},
313				Client: c,
314			}
315
316			for k, v := range de.Params {
317				if v == nil {
318					delete(de.Params, k)
319				}
320			}
321
322			req.Handlers.Build.PushFrontNamed(request.NamedHandler{
323				Name: "crr.endpointdiscovery",
324				Fn: de.Handler,
325			})
326			{{- if not .EndpointDiscovery.Required }}
327				}
328			{{- end }}
329		}
330	{{- end }}
331
332	{{- range $_, $handler := $.CustomBuildHandlers }}
333		req.Handlers.Build.PushBackNamed({{ $handler }})
334	{{- end }}
335
336	{{- if .IsHttpChecksumRequired }}
337		{{- $_ := .API.AddSDKImport "private/checksum" }}
338		req.Handlers.Build.PushBackNamed(request.NamedHandler{
339			Name: "contentMd5Handler",
340			Fn: checksum.AddBodyContentMD5Handler,
341		})
342	{{- end }}
343	return
344}
345
346// {{ .ExportedName }} API operation for {{ .API.Metadata.ServiceFullName }}.
347{{- if .Documentation }}
348//
349{{ .Documentation }}
350{{- end }}
351//
352// Returns awserr.Error for service API and SDK errors. Use runtime type assertions
353// with awserr.Error's Code and Message methods to get detailed information about
354// the error.
355//
356// See the AWS API reference guide for {{ .API.Metadata.ServiceFullName }}'s
357// API operation {{ .ExportedName }} for usage and error information.
358{{- if .ErrorRefs }}
359//
360// Returned Error {{ if $.API.WithGeneratedTypedErrors }}Types{{ else }}Codes{{ end }}:
361{{- range $_, $err := .ErrorRefs -}}
362{{- if $.API.WithGeneratedTypedErrors }}
363//   * {{ $err.ShapeName }}
364{{- else }}
365//   * {{ $err.Shape.ErrorCodeName }} "{{ $err.Shape.ErrorName}}"
366{{- end }}
367{{- if $err.Docstring }}
368{{ $err.IndentedDocstring }}
369{{- end }}
370//
371{{- end }}
372{{- end }}
373{{ $crosslinkURL := $.API.GetCrosslinkURL $.ExportedName -}}
374{{ if ne $crosslinkURL "" -}}
375// See also, {{ $crosslinkURL }}
376{{ end -}}
377{{- if .Deprecated }}//
378// Deprecated: {{ GetDeprecatedMsg .DeprecatedMsg .ExportedName }}
379{{ end -}}
380func (c *{{ .API.StructName }}) {{ .ExportedName }}(` +
381	`input {{ .InputRef.GoType }}) ({{ .OutputRef.GoType }}, error) {
382	req, out := c.{{ .ExportedName }}Request(input)
383	return out, req.Send()
384}
385
386// {{ .ExportedName }}WithContext is the same as {{ .ExportedName }} with the addition of
387// the ability to pass a context and additional request options.
388//
389// See {{ .ExportedName }} for details on how to use this API operation.
390//
391// The context must be non-nil and will be used for request cancellation. If
392// the context is nil a panic will occur. In the future the SDK may create
393// sub-contexts for http.Requests. See https://golang.org/pkg/context/
394// for more information on using Contexts.
395{{ if .Deprecated }}//
396// Deprecated: {{ GetDeprecatedMsg .DeprecatedMsg (printf "%s%s" .ExportedName "WithContext") }}
397{{ end -}}
398func (c *{{ .API.StructName }}) {{ .ExportedName }}WithContext(` +
399	`ctx aws.Context, input {{ .InputRef.GoType }}, opts ...request.Option) ` +
400	`({{ .OutputRef.GoType }}, error) {
401	req, out := c.{{ .ExportedName }}Request(input)
402	req.SetContext(ctx)
403	req.ApplyOptions(opts...)
404	return out, req.Send()
405}
406
407{{ if .Paginator }}
408// {{ .ExportedName }}Pages iterates over the pages of a {{ .ExportedName }} operation,
409// calling the "fn" function with the response data for each page. To stop
410// iterating, return false from the fn function.
411//
412// See {{ .ExportedName }} method for more information on how to use this operation.
413//
414// Note: This operation can generate multiple requests to a service.
415//
416//    // Example iterating over at most 3 pages of a {{ .ExportedName }} operation.
417//    pageNum := 0
418//    err := client.{{ .ExportedName }}Pages(params,
419//        func(page {{ .OutputRef.Shape.GoTypeWithPkgName }}, lastPage bool) bool {
420//            pageNum++
421//            fmt.Println(page)
422//            return pageNum <= 3
423//        })
424//
425{{ if .Deprecated }}//
426// Deprecated: {{ GetDeprecatedMsg .DeprecatedMsg (printf "%s%s" .ExportedName "Pages") }}
427{{ end -}}
428func (c *{{ .API.StructName }}) {{ .ExportedName }}Pages(` +
429	`input {{ .InputRef.GoType }}, fn func({{ .OutputRef.GoType }}, bool) bool) error {
430	return c.{{ .ExportedName }}PagesWithContext(aws.BackgroundContext(), input, fn)
431}
432
433// {{ .ExportedName }}PagesWithContext same as {{ .ExportedName }}Pages except
434// it takes a Context and allows setting request options on the pages.
435//
436// The context must be non-nil and will be used for request cancellation. If
437// the context is nil a panic will occur. In the future the SDK may create
438// sub-contexts for http.Requests. See https://golang.org/pkg/context/
439// for more information on using Contexts.
440{{ if .Deprecated }}//
441// Deprecated: {{ GetDeprecatedMsg .DeprecatedMsg (printf "%s%s" .ExportedName "PagesWithContext") }}
442{{ end -}}
443func (c *{{ .API.StructName }}) {{ .ExportedName }}PagesWithContext(` +
444	`ctx aws.Context, ` +
445	`input {{ .InputRef.GoType }}, ` +
446	`fn func({{ .OutputRef.GoType }}, bool) bool, ` +
447	`opts ...request.Option) error {
448	p := request.Pagination {
449		{{ if EnableStopOnSameToken .API.PackageName -}}EndPageOnSameToken: true,
450		{{ end -}}
451		NewRequest: func() (*request.Request, error) {
452			var inCpy {{ .InputRef.GoType }}
453			if input != nil  {
454				tmp := *input
455				inCpy = &tmp
456			}
457			req, _ := c.{{ .ExportedName }}Request(inCpy)
458			req.SetContext(ctx)
459			req.ApplyOptions(opts...)
460			return req, nil
461		},
462	}
463
464	for p.Next() {
465		if !fn(p.Page().({{ .OutputRef.GoType }}), !p.HasNextPage()) {
466			break
467		}
468	}
469
470	return p.Err()
471}
472{{ end }}
473
474{{- if .IsEndpointDiscoveryOp }}
475type discoverer{{ .ExportedName }} struct {
476	Client *{{ .API.StructName }}
477	Required bool
478	EndpointCache *crr.EndpointCache
479	Params map[string]*string
480	Key string
481	req *request.Request
482}
483
484func (d *discoverer{{ .ExportedName }}) Discover() (crr.Endpoint, error) {
485	input := &{{ .API.EndpointDiscoveryOp.InputRef.ShapeName }}{
486		{{ if .API.EndpointDiscoveryOp.InputRef.Shape.HasMember "Operation" -}}
487		Operation: d.Params["op"],
488		{{ end -}}
489		{{ if .API.EndpointDiscoveryOp.InputRef.Shape.HasMember "Identifiers" -}}
490		Identifiers: d.Params,
491		{{ end -}}
492	}
493
494	resp, err := d.Client.{{ .API.EndpointDiscoveryOp.Name }}(input)
495	if err != nil {
496		return crr.Endpoint{}, err
497	}
498
499	endpoint := crr.Endpoint{
500		Key: d.Key,
501	}
502
503	for _, e := range resp.Endpoints {
504		if e.Address == nil {
505			continue
506		}
507
508		address := *e.Address
509
510		var scheme string
511		if idx := strings.Index(address, "://"); idx != -1 {
512			scheme = address[:idx]
513		}
514
515		if len(scheme) == 0 {
516			address = fmt.Sprintf("%s://%s", d.req.HTTPRequest.URL.Scheme, address)
517		}
518
519		cachedInMinutes := aws.Int64Value(e.CachePeriodInMinutes)
520		u, err := url.Parse(address)
521		if err != nil {
522			continue
523		}
524
525		addr := crr.WeightedAddress{
526			URL: u,
527			Expired:  time.Now().Add(time.Duration(cachedInMinutes) * time.Minute),
528		}
529
530		endpoint.Add(addr)
531	}
532
533	d.EndpointCache.Add(endpoint)
534
535	return endpoint, nil
536}
537
538func (d *discoverer{{ .ExportedName }}) Handler(r *request.Request) {
539	endpointKey := crr.BuildEndpointKey(d.Params)
540	d.Key = endpointKey
541	d.req = r
542
543	endpoint, err := d.EndpointCache.Get(d, endpointKey, d.Required)
544	if err != nil {
545		r.Error = err
546		return
547	}
548
549	if endpoint.URL != nil && len(endpoint.URL.String()) > 0 {
550		r.HTTPRequest.URL = endpoint.URL
551	}
552}
553{{- end }}
554`))
555
556// GoCode returns a string of rendered GoCode for this Operation
557func (o *Operation) GoCode() string {
558	var buf bytes.Buffer
559
560	if o.API.EndpointDiscoveryOp != nil {
561		o.API.AddSDKImport("aws/crr")
562		o.API.AddImport("time")
563		o.API.AddImport("net/url")
564		o.API.AddImport("fmt")
565		o.API.AddImport("strings")
566	}
567
568	if o.Endpoint != nil && len(o.Endpoint.HostPrefix) != 0 {
569		setupEndpointHostPrefix(o)
570	}
571
572	if err := operationTmpl.Execute(&buf, o); err != nil {
573		panic(fmt.Sprintf("failed to render operation, %v, %v", o.ExportedName, err))
574	}
575
576	if o.EventStreamAPI != nil {
577		o.API.AddSDKImport("aws/client")
578		o.API.AddSDKImport("private/protocol")
579		o.API.AddSDKImport("private/protocol/rest")
580		o.API.AddSDKImport("private/protocol", o.API.ProtocolPackage())
581		if err := renderEventStreamAPI(&buf, o); err != nil {
582			panic(fmt.Sprintf("failed to render EventStreamAPI for %v, %v", o.ExportedName, err))
583		}
584	}
585
586	return strings.TrimSpace(buf.String())
587}
588
589// tplInfSig defines the template for rendering an Operation's signature within an Interface definition.
590var tplInfSig = template.Must(template.New("opsig").Parse(`
591{{ .ExportedName }}({{ .InputRef.GoTypeWithPkgName }}) ({{ .OutputRef.GoTypeWithPkgName }}, error)
592{{ .ExportedName }}WithContext(aws.Context, {{ .InputRef.GoTypeWithPkgName }}, ...request.Option) ({{ .OutputRef.GoTypeWithPkgName }}, error)
593{{ .ExportedName }}Request({{ .InputRef.GoTypeWithPkgName }}) (*request.Request, {{ .OutputRef.GoTypeWithPkgName }})
594
595{{ if .Paginator -}}
596{{ .ExportedName }}Pages({{ .InputRef.GoTypeWithPkgName }}, func({{ .OutputRef.GoTypeWithPkgName }}, bool) bool) error
597{{ .ExportedName }}PagesWithContext(aws.Context, {{ .InputRef.GoTypeWithPkgName }}, func({{ .OutputRef.GoTypeWithPkgName }}, bool) bool, ...request.Option) error
598{{- end }}
599`))
600
601// InterfaceSignature returns a string representing the Operation's interface{}
602// functional signature.
603func (o *Operation) InterfaceSignature() string {
604	var buf bytes.Buffer
605	err := tplInfSig.Execute(&buf, o)
606	if err != nil {
607		panic(err)
608	}
609
610	return strings.TrimSpace(buf.String())
611}
612
613// tplExample defines the template for rendering an Operation example
614var tplExample = template.Must(template.New("operationExample").Parse(`
615func Example{{ .API.StructName }}_{{ .ExportedName }}() {
616	sess := session.Must(session.NewSession())
617
618	svc := {{ .API.PackageName }}.New(sess)
619
620	{{ .ExampleInput }}
621	resp, err := svc.{{ .ExportedName }}(params)
622
623	if err != nil {
624		// Print the error, cast err to awserr.Error to get the Code and
625		// Message from an error.
626		fmt.Println(err.Error())
627		return
628	}
629
630	// Pretty-print the response data.
631	fmt.Println(resp)
632}
633`))
634
635// Example returns a string of the rendered Go code for the Operation
636func (o *Operation) Example() string {
637	var buf bytes.Buffer
638	err := tplExample.Execute(&buf, o)
639	if err != nil {
640		panic(err)
641	}
642
643	return strings.TrimSpace(buf.String())
644}
645
646// ExampleInput return a string of the rendered Go code for an example's input parameters
647func (o *Operation) ExampleInput() string {
648	if len(o.InputRef.Shape.MemberRefs) == 0 {
649		if strings.Contains(o.InputRef.GoTypeElem(), ".") {
650			o.imports[SDKImportRoot+"service/"+strings.Split(o.InputRef.GoTypeElem(), ".")[0]] = true
651			return fmt.Sprintf("var params *%s", o.InputRef.GoTypeElem())
652		}
653		return fmt.Sprintf("var params *%s.%s",
654			o.API.PackageName(), o.InputRef.GoTypeElem())
655	}
656	e := example{o, map[string]int{}}
657	return "params := " + e.traverseAny(o.InputRef.Shape, false, false)
658}
659
660// ShouldDiscardResponse returns if the operation should discard the response
661// returned by the service.
662func (o *Operation) ShouldDiscardResponse() bool {
663	s := o.OutputRef.Shape
664	return s.Placeholder || len(s.MemberRefs) == 0
665}
666
667// A example provides
668type example struct {
669	*Operation
670	visited map[string]int
671}
672
673// traverseAny returns rendered Go code for the shape.
674func (e *example) traverseAny(s *Shape, required, payload bool) string {
675	str := ""
676	e.visited[s.ShapeName]++
677
678	switch s.Type {
679	case "structure":
680		str = e.traverseStruct(s, required, payload)
681	case "list":
682		str = e.traverseList(s, required, payload)
683	case "map":
684		str = e.traverseMap(s, required, payload)
685	case "jsonvalue":
686		str = "aws.JSONValue{\"key\": \"value\"}"
687		if required {
688			str += " // Required"
689		}
690	default:
691		str = e.traverseScalar(s, required, payload)
692	}
693
694	e.visited[s.ShapeName]--
695
696	return str
697}
698
699var reType = regexp.MustCompile(`\b([A-Z])`)
700
701// traverseStruct returns rendered Go code for a structure type shape.
702func (e *example) traverseStruct(s *Shape, required, payload bool) string {
703	var buf bytes.Buffer
704
705	if s.resolvePkg != "" {
706		e.imports[s.resolvePkg] = true
707		buf.WriteString("&" + s.GoTypeElem() + "{")
708	} else {
709		buf.WriteString("&" + s.API.PackageName() + "." + s.GoTypeElem() + "{")
710	}
711
712	if required {
713		buf.WriteString(" // Required")
714	}
715	buf.WriteString("\n")
716
717	req := make([]string, len(s.Required))
718	copy(req, s.Required)
719	sort.Strings(req)
720
721	if e.visited[s.ShapeName] < 2 {
722		for _, n := range req {
723			m := s.MemberRefs[n].Shape
724			p := n == s.Payload && (s.MemberRefs[n].Streaming || m.Streaming)
725			buf.WriteString(n + ": " + e.traverseAny(m, true, p) + ",")
726			if m.Type != "list" && m.Type != "structure" && m.Type != "map" {
727				buf.WriteString(" // Required")
728			}
729			buf.WriteString("\n")
730		}
731
732		for _, n := range s.MemberNames() {
733			if s.IsRequired(n) {
734				continue
735			}
736			m := s.MemberRefs[n].Shape
737			p := n == s.Payload && (s.MemberRefs[n].Streaming || m.Streaming)
738			buf.WriteString(n + ": " + e.traverseAny(m, false, p) + ",\n")
739		}
740	} else {
741		buf.WriteString("// Recursive values...\n")
742	}
743
744	buf.WriteString("}")
745	return buf.String()
746}
747
748// traverseMap returns rendered Go code for a map type shape.
749func (e *example) traverseMap(s *Shape, required, payload bool) string {
750	var buf bytes.Buffer
751
752	t := ""
753	if s.resolvePkg != "" {
754		e.imports[s.resolvePkg] = true
755		t = s.GoTypeElem()
756	} else {
757		t = reType.ReplaceAllString(s.GoTypeElem(), s.API.PackageName()+".$1")
758	}
759	buf.WriteString(t + "{")
760	if required {
761		buf.WriteString(" // Required")
762	}
763	buf.WriteString("\n")
764
765	if e.visited[s.ShapeName] < 2 {
766		m := s.ValueRef.Shape
767		buf.WriteString("\"Key\": " + e.traverseAny(m, true, false) + ",")
768		if m.Type != "list" && m.Type != "structure" && m.Type != "map" {
769			buf.WriteString(" // Required")
770		}
771		buf.WriteString("\n// More values...\n")
772	} else {
773		buf.WriteString("// Recursive values...\n")
774	}
775	buf.WriteString("}")
776
777	return buf.String()
778}
779
780// traverseList returns rendered Go code for a list type shape.
781func (e *example) traverseList(s *Shape, required, payload bool) string {
782	var buf bytes.Buffer
783	t := ""
784	if s.resolvePkg != "" {
785		e.imports[s.resolvePkg] = true
786		t = s.GoTypeElem()
787	} else {
788		t = reType.ReplaceAllString(s.GoTypeElem(), s.API.PackageName()+".$1")
789	}
790
791	buf.WriteString(t + "{")
792	if required {
793		buf.WriteString(" // Required")
794	}
795	buf.WriteString("\n")
796
797	if e.visited[s.ShapeName] < 2 {
798		m := s.MemberRef.Shape
799		buf.WriteString(e.traverseAny(m, true, false) + ",")
800		if m.Type != "list" && m.Type != "structure" && m.Type != "map" {
801			buf.WriteString(" // Required")
802		}
803		buf.WriteString("\n// More values...\n")
804	} else {
805		buf.WriteString("// Recursive values...\n")
806	}
807	buf.WriteString("}")
808
809	return buf.String()
810}
811
812// traverseScalar returns an AWS Type string representation initialized to a value.
813// Will panic if s is an unsupported shape type.
814func (e *example) traverseScalar(s *Shape, required, payload bool) string {
815	str := ""
816	switch s.Type {
817	case "integer", "long":
818		str = `aws.Int64(1)`
819	case "float", "double":
820		str = `aws.Float64(1.0)`
821	case "string", "character":
822		str = `aws.String("` + s.ShapeName + `")`
823	case "blob":
824		if payload {
825			str = `bytes.NewReader([]byte("PAYLOAD"))`
826		} else {
827			str = `[]byte("PAYLOAD")`
828		}
829	case "boolean":
830		str = `aws.Bool(true)`
831	case "timestamp":
832		str = `aws.Time(time.Now())`
833	default:
834		panic("unsupported shape " + s.Type)
835	}
836
837	return str
838}
839