1//go:build codegen
2// +build codegen
3
4// Package api represents API abstractions for rendering service generated files.
5package api
6
7import (
8	"bytes"
9	"fmt"
10	"path"
11	"regexp"
12	"sort"
13	"strings"
14	"text/template"
15	"unicode"
16)
17
18// SDKImportRoot is the root import path of the SDK.
19const SDKImportRoot = "github.com/aws/aws-sdk-go"
20
21// An API defines a service API's definition. and logic to serialize the definition.
22type API struct {
23	Metadata      Metadata
24	Operations    map[string]*Operation
25	Shapes        map[string]*Shape
26	Waiters       []Waiter
27	Documentation string `json:"-"`
28	Examples      Examples
29	SmokeTests    SmokeTestSuite
30
31	IgnoreUnsupportedAPIs bool
32
33	// Set to true to avoid removing unused shapes
34	NoRemoveUnusedShapes bool
35
36	// Set to true to avoid renaming to 'Input/Output' postfixed shapes
37	NoRenameToplevelShapes bool
38
39	// Set to true to ignore service/request init methods (for testing)
40	NoInitMethods bool
41
42	// Set to true to ignore String() and GoString methods (for generated tests)
43	NoStringerMethods bool
44
45	// Set to true to not generate API service name constants
46	NoConstServiceNames bool
47
48	// Set to true to not generate validation shapes
49	NoValidataShapeMethods bool
50
51	// Set to true to not generate struct field accessors
52	NoGenStructFieldAccessors bool
53
54	BaseImportPath string
55
56	initialized bool
57	imports     map[string]bool
58	name        string
59	path        string
60
61	BaseCrosslinkURL string
62
63	HasEventStream bool `json:"-"`
64
65	EndpointDiscoveryOp *Operation
66
67	HasEndpointARN bool `json:"-"`
68
69	HasOutpostID bool `json:"-"`
70
71	HasAccountIdWithARN bool `json:"-"`
72
73	WithGeneratedTypedErrors bool
74}
75
76// A Metadata is the metadata about an API's definition.
77type Metadata struct {
78	APIVersion          string
79	EndpointPrefix      string
80	SigningName         string
81	ServiceAbbreviation string
82	ServiceFullName     string
83	SignatureVersion    string
84	JSONVersion         string
85	TargetPrefix        string
86	Protocol            string
87	ProtocolSettings    ProtocolSettings
88	UID                 string
89	EndpointsID         string
90	ServiceID           string
91
92	NoResolveEndpoint bool
93}
94
95// ProtocolSettings define how the SDK should handle requests in the context
96// of of a protocol.
97type ProtocolSettings struct {
98	HTTP2 string `json:"h2,omitempty"`
99}
100
101// PackageName name of the API package
102func (a *API) PackageName() string {
103	return strings.ToLower(a.StructName())
104}
105
106// ImportPath returns the client's full import path
107func (a *API) ImportPath() string {
108	return path.Join(a.BaseImportPath, a.PackageName())
109}
110
111// InterfacePackageName returns the package name for the interface.
112func (a *API) InterfacePackageName() string {
113	return a.PackageName() + "iface"
114}
115
116var stripServiceNamePrefixes = []string{
117	"Amazon",
118	"AWS",
119}
120
121// StructName returns the struct name for a given API.
122func (a *API) StructName() string {
123	if len(a.name) != 0 {
124		return a.name
125	}
126
127	name := a.Metadata.ServiceAbbreviation
128	if len(name) == 0 {
129		name = a.Metadata.ServiceFullName
130	}
131
132	name = strings.TrimSpace(name)
133
134	// Strip out prefix names not reflected in service client symbol names.
135	for _, prefix := range stripServiceNamePrefixes {
136		if strings.HasPrefix(name, prefix) {
137			name = name[len(prefix):]
138			break
139		}
140	}
141
142	// Replace all Non-letter/number values with space
143	runes := []rune(name)
144	for i := 0; i < len(runes); i++ {
145		if r := runes[i]; !(unicode.IsNumber(r) || unicode.IsLetter(r)) {
146			runes[i] = ' '
147		}
148	}
149	name = string(runes)
150
151	// Title case name so its readable as a symbol.
152	name = strings.Title(name)
153
154	// Strip out spaces.
155	name = strings.Replace(name, " ", "", -1)
156
157	a.name = name
158	return a.name
159}
160
161// UseInitMethods returns if the service's init method should be rendered.
162func (a *API) UseInitMethods() bool {
163	return !a.NoInitMethods
164}
165
166// NiceName returns the human friendly API name.
167func (a *API) NiceName() string {
168	if a.Metadata.ServiceAbbreviation != "" {
169		return a.Metadata.ServiceAbbreviation
170	}
171	return a.Metadata.ServiceFullName
172}
173
174// ProtocolPackage returns the package name of the protocol this API uses.
175func (a *API) ProtocolPackage() string {
176	switch a.Metadata.Protocol {
177	case "json":
178		return "jsonrpc"
179	case "ec2":
180		return "ec2query"
181	default:
182		return strings.Replace(a.Metadata.Protocol, "-", "", -1)
183	}
184}
185
186// OperationNames returns a slice of API operations supported.
187func (a *API) OperationNames() []string {
188	i, names := 0, make([]string, len(a.Operations))
189	for n := range a.Operations {
190		names[i] = n
191		i++
192	}
193	sort.Strings(names)
194	return names
195}
196
197// OperationList returns a slice of API operation pointers
198func (a *API) OperationList() []*Operation {
199	list := make([]*Operation, len(a.Operations))
200	for i, n := range a.OperationNames() {
201		list[i] = a.Operations[n]
202	}
203	return list
204}
205
206// OperationHasOutputPlaceholder returns if any of the API operation input
207// or output shapes are place holders.
208func (a *API) OperationHasOutputPlaceholder() bool {
209	for _, op := range a.Operations {
210		if op.OutputRef.Shape.Placeholder {
211			return true
212		}
213	}
214	return false
215}
216
217// ShapeNames returns a slice of names for each shape used by the API.
218func (a *API) ShapeNames() []string {
219	i, names := 0, make([]string, len(a.Shapes))
220	for n := range a.Shapes {
221		names[i] = n
222		i++
223	}
224	sort.Strings(names)
225	return names
226}
227
228// ShapeList returns a slice of shape pointers used by the API.
229//
230// Will exclude error shapes from the list of shapes returned.
231func (a *API) ShapeList() []*Shape {
232	list := make([]*Shape, 0, len(a.Shapes))
233	for _, n := range a.ShapeNames() {
234		// Ignore non-eventstream exception shapes in list.
235		if s := a.Shapes[n]; !(s.Exception && len(s.EventFor) == 0) {
236			list = append(list, s)
237		}
238	}
239	return list
240}
241
242// ShapeListErrors returns a list of the errors defined by the API model
243func (a *API) ShapeListErrors() []*Shape {
244	list := []*Shape{}
245	for _, n := range a.ShapeNames() {
246		// Ignore error shapes in list
247		if s := a.Shapes[n]; s.Exception {
248			list = append(list, s)
249		}
250	}
251	return list
252}
253
254// resetImports resets the import map to default values.
255func (a *API) resetImports() {
256	a.imports = map[string]bool{}
257}
258
259// importsGoCode returns the generated Go import code.
260func (a *API) importsGoCode() string {
261	if len(a.imports) == 0 {
262		return ""
263	}
264
265	corePkgs, extPkgs := []string{}, []string{}
266	for i := range a.imports {
267		if strings.Contains(i, ".") {
268			extPkgs = append(extPkgs, i)
269		} else {
270			corePkgs = append(corePkgs, i)
271		}
272	}
273	sort.Strings(corePkgs)
274	sort.Strings(extPkgs)
275
276	code := "import (\n"
277	for _, i := range corePkgs {
278		code += fmt.Sprintf("\t%q\n", i)
279	}
280	if len(corePkgs) > 0 {
281		code += "\n"
282	}
283	for _, i := range extPkgs {
284		code += fmt.Sprintf("\t%q\n", i)
285	}
286	code += ")\n\n"
287	return code
288}
289
290// A tplAPI is the top level template for the API
291var tplAPI = template.Must(template.New("api").Parse(`
292{{- range $_, $o := .OperationList }}
293
294	{{ $o.GoCode }}
295{{- end }}
296
297{{- range $_, $s := $.Shapes }}
298	{{- if and $s.IsInternal (eq $s.Type "structure") (not $s.Exception) }}
299
300		{{ $s.GoCode }}
301	{{- else if and $s.Exception (or $.WithGeneratedTypedErrors $s.EventFor) }}
302
303		{{ $s.GoCode }}
304	{{- end }}
305{{- end }}
306
307{{- range $_, $s := $.Shapes }}
308	{{- if $s.IsEnum }}
309
310		{{ $s.GoCode }}
311	{{- end }}
312{{- end }}
313`))
314
315// AddImport adds the import path to the generated file's import.
316func (a *API) AddImport(v string) error {
317	a.imports[v] = true
318	return nil
319}
320
321// AddSDKImport adds a SDK package import to the generated file's import.
322func (a *API) AddSDKImport(v ...string) error {
323	e := make([]string, 0, 5)
324	e = append(e, SDKImportRoot)
325	e = append(e, v...)
326
327	a.imports[path.Join(e...)] = true
328	return nil
329}
330
331// APIGoCode renders the API in Go code. Returning it as a string
332func (a *API) APIGoCode() string {
333	a.resetImports()
334	a.AddSDKImport("aws")
335	a.AddSDKImport("aws/awsutil")
336	a.AddSDKImport("aws/request")
337
338	if a.HasEndpointARN {
339		a.AddImport("fmt")
340		if a.PackageName() == "s3" || a.PackageName() == "s3control" {
341			a.AddSDKImport("internal/s3shared/arn")
342		} else {
343			a.AddSDKImport("service", a.PackageName(), "internal", "arn")
344		}
345	}
346
347	var buf bytes.Buffer
348	err := tplAPI.Execute(&buf, a)
349	if err != nil {
350		panic(err)
351	}
352
353	code := a.importsGoCode() + strings.TrimSpace(buf.String())
354	return code
355}
356
357var noCrossLinkServices = map[string]struct{}{
358	"apigateway":           {},
359	"budgets":              {},
360	"cloudsearch":          {},
361	"cloudsearchdomain":    {},
362	"elastictranscoder":    {},
363	"elasticsearchservice": {},
364	"glacier":              {},
365	"importexport":         {},
366	"iot":                  {},
367	"iotdataplane":         {},
368	"machinelearning":      {},
369	"rekognition":          {},
370	"sdb":                  {},
371	"swf":                  {},
372}
373
374// HasCrosslinks will return whether or not a service has crosslinking .
375func HasCrosslinks(service string) bool {
376	_, ok := noCrossLinkServices[service]
377	return !ok
378}
379
380// GetCrosslinkURL returns the crosslinking URL for the shape based on the name and
381// uid provided. Empty string is returned if no crosslink link could be determined.
382func (a *API) GetCrosslinkURL(params ...string) string {
383	baseURL := a.BaseCrosslinkURL
384	uid := a.Metadata.UID
385
386	if a.Metadata.UID == "" || a.BaseCrosslinkURL == "" {
387		return ""
388	}
389
390	if !HasCrosslinks(strings.ToLower(a.PackageName())) {
391		return ""
392	}
393
394	return strings.Join(append([]string{baseURL, "goto", "WebAPI", uid}, params...), "/")
395}
396
397// ServiceIDFromUID will parse the service id from the uid and return
398// the service id that was found.
399func ServiceIDFromUID(uid string) string {
400	found := 0
401	i := len(uid) - 1
402	for ; i >= 0; i-- {
403		if uid[i] == '-' {
404			found++
405		}
406		// Terminate after the date component is found, e.g. es-2017-11-11
407		if found == 3 {
408			break
409		}
410	}
411
412	return uid[0:i]
413}
414
415// APIName returns the API's service name.
416func (a *API) APIName() string {
417	return a.name
418}
419
420var tplServiceDoc = template.Must(template.New("service docs").
421	Parse(`
422// Package {{ .PackageName }} provides the client and types for making API
423// requests to {{ .Metadata.ServiceFullName }}.
424{{ if .Documentation -}}
425//
426{{ .Documentation }}
427{{ end -}}
428{{ $crosslinkURL := $.GetCrosslinkURL -}}
429{{ if $crosslinkURL -}}
430//
431// See {{ $crosslinkURL }} for more information on this service.
432{{ end -}}
433//
434// See {{ .PackageName }} package documentation for more information.
435// https://docs.aws.amazon.com/sdk-for-go/api/service/{{ .PackageName }}/
436//
437// Using the Client
438//
439// To contact {{ .Metadata.ServiceFullName }} with the SDK use the New function to create
440// a new service client. With that client you can make API requests to the service.
441// These clients are safe to use concurrently.
442//
443// See the SDK's documentation for more information on how to use the SDK.
444// https://docs.aws.amazon.com/sdk-for-go/api/
445//
446// See aws.Config documentation for more information on configuring SDK clients.
447// https://docs.aws.amazon.com/sdk-for-go/api/aws/#Config
448//
449// See the {{ .Metadata.ServiceFullName }} client {{ .StructName }} for more
450// information on creating client for this service.
451// https://docs.aws.amazon.com/sdk-for-go/api/service/{{ .PackageName }}/#New
452`))
453
454var serviceIDRegex = regexp.MustCompile("[^a-zA-Z0-9 ]+")
455var prefixDigitRegex = regexp.MustCompile("^[0-9]+")
456
457// ServiceID will return a unique identifier specific to a service.
458func ServiceID(a *API) string {
459	if len(a.Metadata.ServiceID) > 0 {
460		return a.Metadata.ServiceID
461	}
462
463	name := a.Metadata.ServiceAbbreviation
464	if len(name) == 0 {
465		name = a.Metadata.ServiceFullName
466	}
467
468	name = strings.Replace(name, "Amazon", "", -1)
469	name = strings.Replace(name, "AWS", "", -1)
470	name = serviceIDRegex.ReplaceAllString(name, "")
471	name = prefixDigitRegex.ReplaceAllString(name, "")
472	name = strings.TrimSpace(name)
473	return name
474}
475
476// A tplService defines the template for the service generated code.
477var tplService = template.Must(template.New("service").Funcs(template.FuncMap{
478	"ServiceNameConstValue": ServiceName,
479	"ServiceNameValue": func(a *API) string {
480		if !a.NoConstServiceNames {
481			return "ServiceName"
482		}
483		return fmt.Sprintf("%q", ServiceName(a))
484	},
485	"EndpointsIDConstValue": func(a *API) string {
486		if a.NoConstServiceNames {
487			return fmt.Sprintf("%q", a.Metadata.EndpointsID)
488		}
489		if a.Metadata.EndpointsID == ServiceName(a) {
490			return "ServiceName"
491		}
492
493		return fmt.Sprintf("%q", a.Metadata.EndpointsID)
494	},
495	"EndpointsIDValue": func(a *API) string {
496		if a.NoConstServiceNames {
497			return fmt.Sprintf("%q", a.Metadata.EndpointsID)
498		}
499
500		return "EndpointsID"
501	},
502	"ServiceIDVar": func(a *API) string {
503		if a.NoConstServiceNames {
504			return fmt.Sprintf("%q", ServiceID(a))
505		}
506
507		return "ServiceID"
508	},
509	"ServiceID": ServiceID,
510}).Parse(`
511// {{ .StructName }} provides the API operation methods for making requests to
512// {{ .Metadata.ServiceFullName }}. See this package's package overview docs
513// for details on the service.
514//
515// {{ .StructName }} methods are safe to use concurrently. It is not safe to
516// modify mutate any of the struct's properties though.
517type {{ .StructName }} struct {
518	*client.Client
519	{{- if .EndpointDiscoveryOp }}
520	endpointCache *crr.EndpointCache
521	{{ end -}}
522}
523
524{{ if .UseInitMethods }}// Used for custom client initialization logic
525var initClient func(*client.Client)
526
527// Used for custom request initialization logic
528var initRequest func(*request.Request)
529{{ end }}
530
531
532{{ if not .NoConstServiceNames -}}
533// Service information constants
534const (
535	ServiceName = "{{ ServiceNameConstValue . }}" // Name of service.
536	EndpointsID = {{ EndpointsIDConstValue . }} // ID to lookup a service endpoint with.
537	ServiceID = "{{ ServiceID . }}" // ServiceID is a unique identifier of a specific service.
538)
539{{- end }}
540
541// New creates a new instance of the {{ .StructName }} client with a session.
542// If additional configuration is needed for the client instance use the optional
543// aws.Config parameter to add your extra config.
544//
545// Example:
546//     mySession := session.Must(session.NewSession())
547//
548//     // Create a {{ .StructName }} client from just a session.
549//     svc := {{ .PackageName }}.New(mySession)
550//
551//     // Create a {{ .StructName }} client with additional configuration
552//     svc := {{ .PackageName }}.New(mySession, aws.NewConfig().WithRegion("us-west-2"))
553func New(p client.ConfigProvider, cfgs ...*aws.Config) *{{ .StructName }} {
554	{{ if .Metadata.NoResolveEndpoint -}}
555		var c client.Config
556		if v, ok := p.(client.ConfigNoResolveEndpointProvider); ok {
557			c = v.ClientConfigNoResolveEndpoint(cfgs...)
558		} else {
559			c = p.ClientConfig({{ EndpointsIDValue . }}, cfgs...)
560		}
561	{{- else -}}
562		c := p.ClientConfig({{ EndpointsIDValue . }}, cfgs...)
563	{{- end }}
564
565	{{- if .Metadata.SigningName }}
566		if c.SigningNameDerived || len(c.SigningName) == 0{
567			c.SigningName = "{{ .Metadata.SigningName }}"
568		}
569	{{- end }}
570	return newClient(*c.Config, c.Handlers, c.PartitionID, c.Endpoint, c.SigningRegion, c.SigningName, c.ResolvedRegion)
571}
572
573// newClient creates, initializes and returns a new service client instance.
574func newClient(cfg aws.Config, handlers request.Handlers, partitionID, endpoint, signingRegion, signingName, resolvedRegion string) *{{ .StructName }} {
575    svc := &{{ .StructName }}{
576    	Client: client.New(
577    		cfg,
578    		metadata.ClientInfo{
579			ServiceName: {{ ServiceNameValue . }},
580			ServiceID : {{ ServiceIDVar . }},
581			SigningName: signingName,
582			SigningRegion: signingRegion,
583			PartitionID: partitionID,
584			Endpoint:     endpoint,
585			APIVersion:   "{{ .Metadata.APIVersion }}",
586            ResolvedRegion: resolvedRegion,
587			{{ if and (.Metadata.JSONVersion) (eq .Metadata.Protocol "json") -}}
588				JSONVersion:  "{{ .Metadata.JSONVersion }}",
589			{{- end }}
590			{{ if and (.Metadata.TargetPrefix) (eq .Metadata.Protocol "json") -}}
591				TargetPrefix: "{{ .Metadata.TargetPrefix }}",
592			{{- end }}
593    		},
594    		handlers,
595    	),
596    }
597
598	{{- if .EndpointDiscoveryOp }}
599	svc.endpointCache = crr.NewEndpointCache(10)
600	{{- end }}
601
602	// Handlers
603	svc.Handlers.Sign.PushBackNamed(
604		{{- if eq .Metadata.SignatureVersion "v2" -}}
605			v2.SignRequestHandler
606		{{- else if or (eq .Metadata.SignatureVersion "s3") (eq .Metadata.SignatureVersion "s3v4") -}}
607			v4.BuildNamedHandler(v4.SignRequestHandler.Name, func(s *v4.Signer) {
608				s.DisableURIPathEscaping = true
609			})
610		{{- else -}}
611			v4.SignRequestHandler
612		{{- end -}}
613	)
614	{{- if eq .Metadata.SignatureVersion "v2" }}
615		svc.Handlers.Sign.PushBackNamed(corehandlers.BuildContentLengthHandler)
616	{{- end }}
617	svc.Handlers.Build.PushBackNamed({{ .ProtocolPackage }}.BuildHandler)
618	svc.Handlers.Unmarshal.PushBackNamed({{ .ProtocolPackage }}.UnmarshalHandler)
619	svc.Handlers.UnmarshalMeta.PushBackNamed({{ .ProtocolPackage }}.UnmarshalMetaHandler)
620
621	{{- if and $.WithGeneratedTypedErrors (gt (len $.ShapeListErrors) 0) }}
622		{{- $_ := $.AddSDKImport "private/protocol" }}
623		svc.Handlers.UnmarshalError.PushBackNamed(
624			protocol.NewUnmarshalErrorHandler({{ .ProtocolPackage }}.NewUnmarshalTypedError(exceptionFromCode)).NamedHandler(),
625		)
626	{{- else }}
627		svc.Handlers.UnmarshalError.PushBackNamed({{ .ProtocolPackage }}.UnmarshalErrorHandler)
628	{{- end }}
629
630	{{- if .HasEventStream }}
631
632		svc.Handlers.BuildStream.PushBackNamed({{ .ProtocolPackage }}.BuildHandler)
633		svc.Handlers.UnmarshalStream.PushBackNamed({{ .ProtocolPackage }}.UnmarshalHandler)
634	{{- end }}
635
636	{{- if .UseInitMethods }}
637
638		// Run custom client initialization if present
639		if initClient != nil {
640			initClient(svc.Client)
641		}
642	{{- end  }}
643
644	return svc
645}
646
647// newRequest creates a new request for a {{ .StructName }} operation and runs any
648// custom request initialization.
649func (c *{{ .StructName }}) newRequest(op *request.Operation, params, data interface{}) *request.Request {
650	req := c.NewRequest(op, params, data)
651
652	{{- if .UseInitMethods }}
653
654		// Run custom request initialization if present
655		if initRequest != nil {
656			initRequest(req)
657		}
658	{{- end }}
659
660	return req
661}
662`))
663
664// ServicePackageDoc generates the contents of the doc file for the service.
665//
666// Will also read in the custom doc templates for the service if found.
667func (a *API) ServicePackageDoc() string {
668	a.imports = map[string]bool{}
669
670	var buf bytes.Buffer
671	if err := tplServiceDoc.Execute(&buf, a); err != nil {
672		panic(err)
673	}
674
675	return buf.String()
676}
677
678// ServiceGoCode renders service go code. Returning it as a string.
679func (a *API) ServiceGoCode() string {
680	a.resetImports()
681	a.AddSDKImport("aws")
682	a.AddSDKImport("aws/client")
683	a.AddSDKImport("aws/client/metadata")
684	a.AddSDKImport("aws/request")
685	if a.Metadata.SignatureVersion == "v2" {
686		a.AddSDKImport("private/signer/v2")
687		a.AddSDKImport("aws/corehandlers")
688	} else {
689		a.AddSDKImport("aws/signer/v4")
690	}
691	a.AddSDKImport("private/protocol", a.ProtocolPackage())
692	if a.EndpointDiscoveryOp != nil {
693		a.AddSDKImport("aws/crr")
694	}
695
696	var buf bytes.Buffer
697	err := tplService.Execute(&buf, a)
698	if err != nil {
699		panic(err)
700	}
701
702	code := a.importsGoCode() + buf.String()
703	return code
704}
705
706// ExampleGoCode renders service example code. Returning it as a string.
707func (a *API) ExampleGoCode() string {
708	exs := []string{}
709	imports := map[string]bool{}
710	for _, o := range a.OperationList() {
711		o.imports = map[string]bool{}
712		exs = append(exs, o.Example())
713		for k, v := range o.imports {
714			imports[k] = v
715		}
716	}
717
718	code := fmt.Sprintf("import (\n%q\n%q\n%q\n\n%q\n%q\n%q\n",
719		"bytes",
720		"fmt",
721		"time",
722		SDKImportRoot+"/aws",
723		SDKImportRoot+"/aws/session",
724		a.ImportPath(),
725	)
726	for k := range imports {
727		code += fmt.Sprintf("%q\n", k)
728	}
729	code += ")\n\n"
730	code += "var _ time.Duration\nvar _ bytes.Buffer\n\n"
731	code += strings.Join(exs, "\n\n")
732	return code
733}
734
735// A tplInterface defines the template for the service interface type.
736var tplInterface = template.Must(template.New("interface").Parse(`
737// {{ .StructName }}API provides an interface to enable mocking the
738// {{ .PackageName }}.{{ .StructName }} service client's API operation,
739// paginators, and waiters. This make unit testing your code that calls out
740// to the SDK's service client's calls easier.
741//
742// The best way to use this interface is so the SDK's service client's calls
743// can be stubbed out for unit testing your code with the SDK without needing
744// to inject custom request handlers into the SDK's request pipeline.
745//
746//    // myFunc uses an SDK service client to make a request to
747//    // {{.Metadata.ServiceFullName}}. {{ $opts := .OperationList }}{{ $opt := index $opts 0 }}
748//    func myFunc(svc {{ .InterfacePackageName }}.{{ .StructName }}API) bool {
749//        // Make svc.{{ $opt.ExportedName }} request
750//    }
751//
752//    func main() {
753//        sess := session.New()
754//        svc := {{ .PackageName }}.New(sess)
755//
756//        myFunc(svc)
757//    }
758//
759// In your _test.go file:
760//
761//    // Define a mock struct to be used in your unit tests of myFunc.
762//    type mock{{ .StructName }}Client struct {
763//        {{ .InterfacePackageName }}.{{ .StructName }}API
764//    }
765//    func (m *mock{{ .StructName }}Client) {{ $opt.ExportedName }}(input {{ $opt.InputRef.GoTypeWithPkgName }}) ({{ $opt.OutputRef.GoTypeWithPkgName }}, error) {
766//        // mock response/functionality
767//    }
768//
769//    func TestMyFunc(t *testing.T) {
770//        // Setup Test
771//        mockSvc := &mock{{ .StructName }}Client{}
772//
773//        myfunc(mockSvc)
774//
775//        // Verify myFunc's functionality
776//    }
777//
778// It is important to note that this interface will have breaking changes
779// when the service model is updated and adds new API operations, paginators,
780// and waiters. Its suggested to use the pattern above for testing, or using
781// tooling to generate mocks to satisfy the interfaces.
782type {{ .StructName }}API interface {
783    {{ range $_, $o := .OperationList }}
784        {{ $o.InterfaceSignature }}
785    {{ end }}
786    {{ range $_, $w := .Waiters }}
787        {{ $w.InterfaceSignature }}
788    {{ end }}
789}
790
791var _ {{ .StructName }}API = (*{{ .PackageName }}.{{ .StructName }})(nil)
792`))
793
794// InterfaceGoCode returns the go code for the service's API operations as an
795// interface{}. Assumes that the interface is being created in a different
796// package than the service API's package.
797func (a *API) InterfaceGoCode() string {
798	a.resetImports()
799	a.AddSDKImport("aws")
800	a.AddSDKImport("aws/request")
801	a.AddImport(a.ImportPath())
802
803	var buf bytes.Buffer
804	err := tplInterface.Execute(&buf, a)
805
806	if err != nil {
807		panic(err)
808	}
809
810	code := a.importsGoCode() + strings.TrimSpace(buf.String())
811	return code
812}
813
814// NewAPIGoCodeWithPkgName returns a string of instantiating the API prefixed
815// with its package name. Takes a string depicting the Config.
816func (a *API) NewAPIGoCodeWithPkgName(cfg string) string {
817	return fmt.Sprintf("%s.New(%s)", a.PackageName(), cfg)
818}
819
820// computes the validation chain for all input shapes
821func (a *API) addShapeValidations() {
822	for _, o := range a.Operations {
823		resolveShapeValidations(o.InputRef.Shape)
824	}
825}
826
827// Updates the source shape and all nested shapes with the validations that
828// could possibly be needed.
829func resolveShapeValidations(s *Shape, ancestry ...*Shape) {
830	for _, a := range ancestry {
831		if a == s {
832			return
833		}
834	}
835
836	children := []string{}
837	for _, name := range s.MemberNames() {
838		ref := s.MemberRefs[name]
839		if s.IsRequired(name) && !s.Validations.Has(ref, ShapeValidationRequired) {
840			s.Validations = append(s.Validations, ShapeValidation{
841				Name: name, Ref: ref, Type: ShapeValidationRequired,
842			})
843		}
844
845		if ref.Shape.Min != 0 && !s.Validations.Has(ref, ShapeValidationMinVal) {
846			s.Validations = append(s.Validations, ShapeValidation{
847				Name: name, Ref: ref, Type: ShapeValidationMinVal,
848			})
849		}
850
851		if !ref.CanBeEmpty() && !s.Validations.Has(ref, ShapeValidationMinVal) {
852			s.Validations = append(s.Validations, ShapeValidation{
853				Name: name, Ref: ref, Type: ShapeValidationMinVal,
854			})
855		}
856
857		switch ref.Shape.Type {
858		case "map", "list", "structure":
859			children = append(children, name)
860		}
861	}
862
863	ancestry = append(ancestry, s)
864	for _, name := range children {
865		ref := s.MemberRefs[name]
866		// Since this is a grab bag we will just continue since
867		// we can't validate because we don't know the valued shape.
868		if ref.JSONValue || (s.UsedAsInput && ref.Shape.IsEventStream) {
869			continue
870		}
871
872		nestedShape := ref.Shape.NestedShape()
873
874		var v *ShapeValidation
875		if len(nestedShape.Validations) > 0 {
876			v = &ShapeValidation{
877				Name: name, Ref: ref, Type: ShapeValidationNested,
878			}
879		} else {
880			resolveShapeValidations(nestedShape, ancestry...)
881			if len(nestedShape.Validations) > 0 {
882				v = &ShapeValidation{
883					Name: name, Ref: ref, Type: ShapeValidationNested,
884				}
885			}
886		}
887
888		if v != nil && !s.Validations.Has(v.Ref, v.Type) {
889			s.Validations = append(s.Validations, *v)
890		}
891	}
892	ancestry = ancestry[:len(ancestry)-1]
893}
894
895// A tplAPIErrors is the top level template for the API
896var tplAPIErrors = template.Must(template.New("api").Parse(`
897const (
898	{{- range $_, $s := $.ShapeListErrors }}
899
900		// {{ $s.ErrorCodeName }} for service response error code
901		// {{ printf "%q" $s.ErrorName }}.
902		{{ if $s.Docstring -}}
903		//
904		{{ $s.Docstring }}
905		{{ end -}}
906		{{ $s.ErrorCodeName }} = {{ printf "%q" $s.ErrorName }}
907	{{- end }}
908)
909
910{{- if $.WithGeneratedTypedErrors }}
911	{{- $_ := $.AddSDKImport "private/protocol" }}
912
913	var exceptionFromCode = map[string]func(protocol.ResponseMetadata)error {
914		{{- range $_, $s := $.ShapeListErrors }}
915			"{{ $s.ErrorName }}": newError{{ $s.ShapeName }},
916		{{- end }}
917	}
918{{- end }}
919`))
920
921// APIErrorsGoCode returns the Go code for the errors.go file.
922func (a *API) APIErrorsGoCode() string {
923	a.resetImports()
924
925	var buf bytes.Buffer
926	err := tplAPIErrors.Execute(&buf, a)
927
928	if err != nil {
929		panic(err)
930	}
931
932	return a.importsGoCode() + strings.TrimSpace(buf.String())
933}
934
935// removeOperation removes an operation, its input/output shapes, as well as
936// any references/shapes that are unique to this operation.
937func (a *API) removeOperation(name string) {
938	debugLogger.Logln("removing operation,", name)
939	op := a.Operations[name]
940
941	delete(a.Operations, name)
942	delete(a.Examples, name)
943
944	a.removeShape(op.InputRef.Shape)
945	a.removeShape(op.OutputRef.Shape)
946}
947
948// removeShape removes the given shape, and all form member's reference target
949// shapes. Will also remove member reference targeted shapes if those shapes do
950// not have any additional references.
951func (a *API) removeShape(s *Shape) {
952	debugLogger.Logln("removing shape,", s.ShapeName)
953
954	delete(a.Shapes, s.ShapeName)
955
956	for name, ref := range s.MemberRefs {
957		a.removeShapeRef(ref)
958		delete(s.MemberRefs, name)
959	}
960
961	for _, ref := range []*ShapeRef{&s.MemberRef, &s.KeyRef, &s.ValueRef} {
962		if ref.Shape == nil {
963			continue
964		}
965		a.removeShapeRef(ref)
966		*ref = ShapeRef{}
967	}
968}
969
970// removeShapeRef removes the shape reference from its target shape. If the
971// reference was the last reference to the target shape, the shape will also be
972// removed.
973func (a *API) removeShapeRef(ref *ShapeRef) {
974	if ref.Shape == nil {
975		return
976	}
977
978	ref.Shape.removeRef(ref)
979	if len(ref.Shape.refs) == 0 {
980		a.removeShape(ref.Shape)
981	}
982}
983
984// writeInputOutputLocationName writes the ShapeName to the
985// shapes LocationName in the event that there is no LocationName
986// specified.
987func (a *API) writeInputOutputLocationName() {
988	for _, o := range a.Operations {
989		setInput := len(o.InputRef.LocationName) == 0 && a.Metadata.Protocol == "rest-xml"
990		setOutput := len(o.OutputRef.LocationName) == 0 && (a.Metadata.Protocol == "rest-xml" || a.Metadata.Protocol == "ec2")
991
992		if setInput {
993			o.InputRef.LocationName = o.InputRef.Shape.OrigShapeName
994		}
995		if setOutput {
996			o.OutputRef.LocationName = o.OutputRef.Shape.OrigShapeName
997		}
998	}
999}
1000
1001func (a *API) addHeaderMapDocumentation() {
1002	for _, shape := range a.Shapes {
1003		if !shape.UsedAsOutput {
1004			continue
1005		}
1006		for _, shapeRef := range shape.MemberRefs {
1007			if shapeRef.Location == "headers" {
1008				if dLen := len(shapeRef.Documentation); dLen > 0 {
1009					if shapeRef.Documentation[dLen-1] != '\n' {
1010						shapeRef.Documentation += "\n"
1011					}
1012					shapeRef.Documentation += "//"
1013				}
1014				shapeRef.Documentation += `
1015// By default unmarshaled keys are written as a map keys in following canonicalized format:
1016// the first letter and any letter following a hyphen will be capitalized, and the rest as lowercase.
1017// Set ` + "`aws.Config.LowerCaseHeaderMaps`" + ` to ` + "`true`" + ` to write unmarshaled keys to the map as lowercase.
1018`
1019			}
1020		}
1021	}
1022}
1023
1024func (a *API) validateNoDocumentShapes() error {
1025	var shapes []string
1026	for name, shape := range a.Shapes {
1027		if shape.Type != "structure" {
1028			continue
1029		}
1030		if shape.Document {
1031			shapes = append(shapes, name)
1032		}
1033	}
1034
1035	if len(shapes) == 0 {
1036		return nil
1037	}
1038
1039	return fmt.Errorf("model contains document shapes: %s", strings.Join(shapes, ", "))
1040}
1041
1042func getDeprecatedMessage(msg string, name string) string {
1043	if len(msg) == 0 {
1044		return name + " has been deprecated"
1045	}
1046
1047	return msg
1048}
1049