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