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