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