github.com/aavshr/aws-sdk-go@v1.41.3/private/model/api/api.go (about)

     1  //go:build codegen
     2  // +build codegen
     3  
     4  // Package api represents API abstractions for rendering service generated files.
     5  package api
     6  
     7  import (
     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.
    19  const SDKImportRoot = "github.com/aavshr/aws-sdk-go"
    20  
    21  // An API defines a service API's definition. and logic to serialize the definition.
    22  type 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.
    77  type 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.
    97  type ProtocolSettings struct {
    98  	HTTP2 string `json:"h2,omitempty"`
    99  }
   100  
   101  // PackageName name of the API package
   102  func (a *API) PackageName() string {
   103  	return strings.ToLower(a.StructName())
   104  }
   105  
   106  // ImportPath returns the client's full import path
   107  func (a *API) ImportPath() string {
   108  	return path.Join(a.BaseImportPath, a.PackageName())
   109  }
   110  
   111  // InterfacePackageName returns the package name for the interface.
   112  func (a *API) InterfacePackageName() string {
   113  	return a.PackageName() + "iface"
   114  }
   115  
   116  var stripServiceNamePrefixes = []string{
   117  	"Amazon",
   118  	"AWS",
   119  }
   120  
   121  // StructName returns the struct name for a given API.
   122  func (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.
   162  func (a *API) UseInitMethods() bool {
   163  	return !a.NoInitMethods
   164  }
   165  
   166  // NiceName returns the human friendly API name.
   167  func (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.
   175  func (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.
   187  func (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
   198  func (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.
   208  func (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.
   218  func (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.
   231  func (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
   243  func (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.
   255  func (a *API) resetImports() {
   256  	a.imports = map[string]bool{}
   257  }
   258  
   259  // importsGoCode returns the generated Go import code.
   260  func (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
   291  var 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.
   316  func (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.
   322  func (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
   332  func (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  
   357  var 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 .
   375  func 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.
   382  func (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.
   399  func 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.
   416  func (a *API) APIName() string {
   417  	return a.name
   418  }
   419  
   420  var 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  
   454  var serviceIDRegex = regexp.MustCompile("[^a-zA-Z0-9 ]+")
   455  var prefixDigitRegex = regexp.MustCompile("^[0-9]+")
   456  
   457  // ServiceID will return a unique identifier specific to a service.
   458  func 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.
   477  var 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.
   517  type {{ .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
   525  var initClient func(*client.Client)
   526  
   527  // Used for custom request initialization logic
   528  var initRequest func(*request.Request)
   529  {{ end }}
   530  
   531  
   532  {{ if not .NoConstServiceNames -}}
   533  // Service information constants
   534  const (
   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"))
   553  func 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)
   571  }
   572  
   573  // newClient creates, initializes and returns a new service client instance.
   574  func newClient(cfg aws.Config, handlers request.Handlers, partitionID, endpoint, signingRegion, signingName 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  			{{ if and (.Metadata.JSONVersion) (eq .Metadata.Protocol "json") -}}
   587  				JSONVersion:  "{{ .Metadata.JSONVersion }}",
   588  			{{- end }}
   589  			{{ if and (.Metadata.TargetPrefix) (eq .Metadata.Protocol "json") -}}
   590  				TargetPrefix: "{{ .Metadata.TargetPrefix }}",
   591  			{{- end }}
   592      		},
   593      		handlers,
   594      	),
   595      }
   596  
   597  	{{- if .EndpointDiscoveryOp }}
   598  	svc.endpointCache = crr.NewEndpointCache(10)
   599  	{{- end }}
   600  
   601  	// Handlers
   602  	svc.Handlers.Sign.PushBackNamed(
   603  		{{- if eq .Metadata.SignatureVersion "v2" -}}
   604  			v2.SignRequestHandler
   605  		{{- else if or (eq .Metadata.SignatureVersion "s3") (eq .Metadata.SignatureVersion "s3v4") -}}
   606  			v4.BuildNamedHandler(v4.SignRequestHandler.Name, func(s *v4.Signer) {
   607  				s.DisableURIPathEscaping = true
   608  			})
   609  		{{- else -}}
   610  			v4.SignRequestHandler
   611  		{{- end -}}
   612  	)
   613  	{{- if eq .Metadata.SignatureVersion "v2" }}
   614  		svc.Handlers.Sign.PushBackNamed(corehandlers.BuildContentLengthHandler)
   615  	{{- end }}
   616  	svc.Handlers.Build.PushBackNamed({{ .ProtocolPackage }}.BuildHandler)
   617  	svc.Handlers.Unmarshal.PushBackNamed({{ .ProtocolPackage }}.UnmarshalHandler)
   618  	svc.Handlers.UnmarshalMeta.PushBackNamed({{ .ProtocolPackage }}.UnmarshalMetaHandler)
   619  
   620  	{{- if and $.WithGeneratedTypedErrors (gt (len $.ShapeListErrors) 0) }}
   621  		{{- $_ := $.AddSDKImport "private/protocol" }}
   622  		svc.Handlers.UnmarshalError.PushBackNamed(
   623  			protocol.NewUnmarshalErrorHandler({{ .ProtocolPackage }}.NewUnmarshalTypedError(exceptionFromCode)).NamedHandler(),
   624  		)
   625  	{{- else }}
   626  		svc.Handlers.UnmarshalError.PushBackNamed({{ .ProtocolPackage }}.UnmarshalErrorHandler)
   627  	{{- end }}
   628  
   629  	{{- if .HasEventStream }}
   630  
   631  		svc.Handlers.BuildStream.PushBackNamed({{ .ProtocolPackage }}.BuildHandler)
   632  		svc.Handlers.UnmarshalStream.PushBackNamed({{ .ProtocolPackage }}.UnmarshalHandler)
   633  	{{- end }}
   634  
   635  	{{- if .UseInitMethods }}
   636  
   637  		// Run custom client initialization if present
   638  		if initClient != nil {
   639  			initClient(svc.Client)
   640  		}
   641  	{{- end  }}
   642  
   643  	return svc
   644  }
   645  
   646  // newRequest creates a new request for a {{ .StructName }} operation and runs any
   647  // custom request initialization.
   648  func (c *{{ .StructName }}) newRequest(op *request.Operation, params, data interface{}) *request.Request {
   649  	req := c.NewRequest(op, params, data)
   650  
   651  	{{- if .UseInitMethods }}
   652  
   653  		// Run custom request initialization if present
   654  		if initRequest != nil {
   655  			initRequest(req)
   656  		}
   657  	{{- end }}
   658  
   659  	return req
   660  }
   661  `))
   662  
   663  // ServicePackageDoc generates the contents of the doc file for the service.
   664  //
   665  // Will also read in the custom doc templates for the service if found.
   666  func (a *API) ServicePackageDoc() string {
   667  	a.imports = map[string]bool{}
   668  
   669  	var buf bytes.Buffer
   670  	if err := tplServiceDoc.Execute(&buf, a); err != nil {
   671  		panic(err)
   672  	}
   673  
   674  	return buf.String()
   675  }
   676  
   677  // ServiceGoCode renders service go code. Returning it as a string.
   678  func (a *API) ServiceGoCode() string {
   679  	a.resetImports()
   680  	a.AddSDKImport("aws")
   681  	a.AddSDKImport("aws/client")
   682  	a.AddSDKImport("aws/client/metadata")
   683  	a.AddSDKImport("aws/request")
   684  	if a.Metadata.SignatureVersion == "v2" {
   685  		a.AddSDKImport("private/signer/v2")
   686  		a.AddSDKImport("aws/corehandlers")
   687  	} else {
   688  		a.AddSDKImport("aws/signer/v4")
   689  	}
   690  	a.AddSDKImport("private/protocol", a.ProtocolPackage())
   691  	if a.EndpointDiscoveryOp != nil {
   692  		a.AddSDKImport("aws/crr")
   693  	}
   694  
   695  	var buf bytes.Buffer
   696  	err := tplService.Execute(&buf, a)
   697  	if err != nil {
   698  		panic(err)
   699  	}
   700  
   701  	code := a.importsGoCode() + buf.String()
   702  	return code
   703  }
   704  
   705  // ExampleGoCode renders service example code. Returning it as a string.
   706  func (a *API) ExampleGoCode() string {
   707  	exs := []string{}
   708  	imports := map[string]bool{}
   709  	for _, o := range a.OperationList() {
   710  		o.imports = map[string]bool{}
   711  		exs = append(exs, o.Example())
   712  		for k, v := range o.imports {
   713  			imports[k] = v
   714  		}
   715  	}
   716  
   717  	code := fmt.Sprintf("import (\n%q\n%q\n%q\n\n%q\n%q\n%q\n",
   718  		"bytes",
   719  		"fmt",
   720  		"time",
   721  		SDKImportRoot+"/aws",
   722  		SDKImportRoot+"/aws/session",
   723  		a.ImportPath(),
   724  	)
   725  	for k := range imports {
   726  		code += fmt.Sprintf("%q\n", k)
   727  	}
   728  	code += ")\n\n"
   729  	code += "var _ time.Duration\nvar _ bytes.Buffer\n\n"
   730  	code += strings.Join(exs, "\n\n")
   731  	return code
   732  }
   733  
   734  // A tplInterface defines the template for the service interface type.
   735  var tplInterface = template.Must(template.New("interface").Parse(`
   736  // {{ .StructName }}API provides an interface to enable mocking the
   737  // {{ .PackageName }}.{{ .StructName }} service client's API operation,
   738  // paginators, and waiters. This make unit testing your code that calls out
   739  // to the SDK's service client's calls easier.
   740  //
   741  // The best way to use this interface is so the SDK's service client's calls
   742  // can be stubbed out for unit testing your code with the SDK without needing
   743  // to inject custom request handlers into the SDK's request pipeline.
   744  //
   745  //    // myFunc uses an SDK service client to make a request to
   746  //    // {{.Metadata.ServiceFullName}}. {{ $opts := .OperationList }}{{ $opt := index $opts 0 }}
   747  //    func myFunc(svc {{ .InterfacePackageName }}.{{ .StructName }}API) bool {
   748  //        // Make svc.{{ $opt.ExportedName }} request
   749  //    }
   750  //
   751  //    func main() {
   752  //        sess := session.New()
   753  //        svc := {{ .PackageName }}.New(sess)
   754  //
   755  //        myFunc(svc)
   756  //    }
   757  //
   758  // In your _test.go file:
   759  //
   760  //    // Define a mock struct to be used in your unit tests of myFunc.
   761  //    type mock{{ .StructName }}Client struct {
   762  //        {{ .InterfacePackageName }}.{{ .StructName }}API
   763  //    }
   764  //    func (m *mock{{ .StructName }}Client) {{ $opt.ExportedName }}(input {{ $opt.InputRef.GoTypeWithPkgName }}) ({{ $opt.OutputRef.GoTypeWithPkgName }}, error) {
   765  //        // mock response/functionality
   766  //    }
   767  //
   768  //    func TestMyFunc(t *testing.T) {
   769  //        // Setup Test
   770  //        mockSvc := &mock{{ .StructName }}Client{}
   771  //
   772  //        myfunc(mockSvc)
   773  //
   774  //        // Verify myFunc's functionality
   775  //    }
   776  //
   777  // It is important to note that this interface will have breaking changes
   778  // when the service model is updated and adds new API operations, paginators,
   779  // and waiters. Its suggested to use the pattern above for testing, or using
   780  // tooling to generate mocks to satisfy the interfaces.
   781  type {{ .StructName }}API interface {
   782      {{ range $_, $o := .OperationList }}
   783          {{ $o.InterfaceSignature }}
   784      {{ end }}
   785      {{ range $_, $w := .Waiters }}
   786          {{ $w.InterfaceSignature }}
   787      {{ end }}
   788  }
   789  
   790  var _ {{ .StructName }}API = (*{{ .PackageName }}.{{ .StructName }})(nil)
   791  `))
   792  
   793  // InterfaceGoCode returns the go code for the service's API operations as an
   794  // interface{}. Assumes that the interface is being created in a different
   795  // package than the service API's package.
   796  func (a *API) InterfaceGoCode() string {
   797  	a.resetImports()
   798  	a.AddSDKImport("aws")
   799  	a.AddSDKImport("aws/request")
   800  	a.AddImport(a.ImportPath())
   801  
   802  	var buf bytes.Buffer
   803  	err := tplInterface.Execute(&buf, a)
   804  
   805  	if err != nil {
   806  		panic(err)
   807  	}
   808  
   809  	code := a.importsGoCode() + strings.TrimSpace(buf.String())
   810  	return code
   811  }
   812  
   813  // NewAPIGoCodeWithPkgName returns a string of instantiating the API prefixed
   814  // with its package name. Takes a string depicting the Config.
   815  func (a *API) NewAPIGoCodeWithPkgName(cfg string) string {
   816  	return fmt.Sprintf("%s.New(%s)", a.PackageName(), cfg)
   817  }
   818  
   819  // computes the validation chain for all input shapes
   820  func (a *API) addShapeValidations() {
   821  	for _, o := range a.Operations {
   822  		resolveShapeValidations(o.InputRef.Shape)
   823  	}
   824  }
   825  
   826  // Updates the source shape and all nested shapes with the validations that
   827  // could possibly be needed.
   828  func resolveShapeValidations(s *Shape, ancestry ...*Shape) {
   829  	for _, a := range ancestry {
   830  		if a == s {
   831  			return
   832  		}
   833  	}
   834  
   835  	children := []string{}
   836  	for _, name := range s.MemberNames() {
   837  		ref := s.MemberRefs[name]
   838  		if s.IsRequired(name) && !s.Validations.Has(ref, ShapeValidationRequired) {
   839  			s.Validations = append(s.Validations, ShapeValidation{
   840  				Name: name, Ref: ref, Type: ShapeValidationRequired,
   841  			})
   842  		}
   843  
   844  		if ref.Shape.Min != 0 && !s.Validations.Has(ref, ShapeValidationMinVal) {
   845  			s.Validations = append(s.Validations, ShapeValidation{
   846  				Name: name, Ref: ref, Type: ShapeValidationMinVal,
   847  			})
   848  		}
   849  
   850  		if !ref.CanBeEmpty() && !s.Validations.Has(ref, ShapeValidationMinVal) {
   851  			s.Validations = append(s.Validations, ShapeValidation{
   852  				Name: name, Ref: ref, Type: ShapeValidationMinVal,
   853  			})
   854  		}
   855  
   856  		switch ref.Shape.Type {
   857  		case "map", "list", "structure":
   858  			children = append(children, name)
   859  		}
   860  	}
   861  
   862  	ancestry = append(ancestry, s)
   863  	for _, name := range children {
   864  		ref := s.MemberRefs[name]
   865  		// Since this is a grab bag we will just continue since
   866  		// we can't validate because we don't know the valued shape.
   867  		if ref.JSONValue || (s.UsedAsInput && ref.Shape.IsEventStream) {
   868  			continue
   869  		}
   870  
   871  		nestedShape := ref.Shape.NestedShape()
   872  
   873  		var v *ShapeValidation
   874  		if len(nestedShape.Validations) > 0 {
   875  			v = &ShapeValidation{
   876  				Name: name, Ref: ref, Type: ShapeValidationNested,
   877  			}
   878  		} else {
   879  			resolveShapeValidations(nestedShape, ancestry...)
   880  			if len(nestedShape.Validations) > 0 {
   881  				v = &ShapeValidation{
   882  					Name: name, Ref: ref, Type: ShapeValidationNested,
   883  				}
   884  			}
   885  		}
   886  
   887  		if v != nil && !s.Validations.Has(v.Ref, v.Type) {
   888  			s.Validations = append(s.Validations, *v)
   889  		}
   890  	}
   891  	ancestry = ancestry[:len(ancestry)-1]
   892  }
   893  
   894  // A tplAPIErrors is the top level template for the API
   895  var tplAPIErrors = template.Must(template.New("api").Parse(`
   896  const (
   897  	{{- range $_, $s := $.ShapeListErrors }}
   898  
   899  		// {{ $s.ErrorCodeName }} for service response error code
   900  		// {{ printf "%q" $s.ErrorName }}.
   901  		{{ if $s.Docstring -}}
   902  		//
   903  		{{ $s.Docstring }}
   904  		{{ end -}}
   905  		{{ $s.ErrorCodeName }} = {{ printf "%q" $s.ErrorName }}
   906  	{{- end }}
   907  )
   908  
   909  {{- if $.WithGeneratedTypedErrors }}
   910  	{{- $_ := $.AddSDKImport "private/protocol" }}
   911  
   912  	var exceptionFromCode = map[string]func(protocol.ResponseMetadata)error {
   913  		{{- range $_, $s := $.ShapeListErrors }}
   914  			"{{ $s.ErrorName }}": newError{{ $s.ShapeName }},
   915  		{{- end }}
   916  	}
   917  {{- end }}
   918  `))
   919  
   920  // APIErrorsGoCode returns the Go code for the errors.go file.
   921  func (a *API) APIErrorsGoCode() string {
   922  	a.resetImports()
   923  
   924  	var buf bytes.Buffer
   925  	err := tplAPIErrors.Execute(&buf, a)
   926  
   927  	if err != nil {
   928  		panic(err)
   929  	}
   930  
   931  	return a.importsGoCode() + strings.TrimSpace(buf.String())
   932  }
   933  
   934  // removeOperation removes an operation, its input/output shapes, as well as
   935  // any references/shapes that are unique to this operation.
   936  func (a *API) removeOperation(name string) {
   937  	debugLogger.Logln("removing operation,", name)
   938  	op := a.Operations[name]
   939  
   940  	delete(a.Operations, name)
   941  	delete(a.Examples, name)
   942  
   943  	a.removeShape(op.InputRef.Shape)
   944  	a.removeShape(op.OutputRef.Shape)
   945  }
   946  
   947  // removeShape removes the given shape, and all form member's reference target
   948  // shapes. Will also remove member reference targeted shapes if those shapes do
   949  // not have any additional references.
   950  func (a *API) removeShape(s *Shape) {
   951  	debugLogger.Logln("removing shape,", s.ShapeName)
   952  
   953  	delete(a.Shapes, s.ShapeName)
   954  
   955  	for name, ref := range s.MemberRefs {
   956  		a.removeShapeRef(ref)
   957  		delete(s.MemberRefs, name)
   958  	}
   959  
   960  	for _, ref := range []*ShapeRef{&s.MemberRef, &s.KeyRef, &s.ValueRef} {
   961  		if ref.Shape == nil {
   962  			continue
   963  		}
   964  		a.removeShapeRef(ref)
   965  		*ref = ShapeRef{}
   966  	}
   967  }
   968  
   969  // removeShapeRef removes the shape reference from its target shape. If the
   970  // reference was the last reference to the target shape, the shape will also be
   971  // removed.
   972  func (a *API) removeShapeRef(ref *ShapeRef) {
   973  	if ref.Shape == nil {
   974  		return
   975  	}
   976  
   977  	ref.Shape.removeRef(ref)
   978  	if len(ref.Shape.refs) == 0 {
   979  		a.removeShape(ref.Shape)
   980  	}
   981  }
   982  
   983  // writeInputOutputLocationName writes the ShapeName to the
   984  // shapes LocationName in the event that there is no LocationName
   985  // specified.
   986  func (a *API) writeInputOutputLocationName() {
   987  	for _, o := range a.Operations {
   988  		setInput := len(o.InputRef.LocationName) == 0 && a.Metadata.Protocol == "rest-xml"
   989  		setOutput := len(o.OutputRef.LocationName) == 0 && (a.Metadata.Protocol == "rest-xml" || a.Metadata.Protocol == "ec2")
   990  
   991  		if setInput {
   992  			o.InputRef.LocationName = o.InputRef.Shape.OrigShapeName
   993  		}
   994  		if setOutput {
   995  			o.OutputRef.LocationName = o.OutputRef.Shape.OrigShapeName
   996  		}
   997  	}
   998  }
   999  
  1000  func (a *API) addHeaderMapDocumentation() {
  1001  	for _, shape := range a.Shapes {
  1002  		if !shape.UsedAsOutput {
  1003  			continue
  1004  		}
  1005  		for _, shapeRef := range shape.MemberRefs {
  1006  			if shapeRef.Location == "headers" {
  1007  				if dLen := len(shapeRef.Documentation); dLen > 0 {
  1008  					if shapeRef.Documentation[dLen-1] != '\n' {
  1009  						shapeRef.Documentation += "\n"
  1010  					}
  1011  					shapeRef.Documentation += "//"
  1012  				}
  1013  				shapeRef.Documentation += `
  1014  // By default unmarshaled keys are written as a map keys in following canonicalized format:
  1015  // the first letter and any letter following a hyphen will be capitalized, and the rest as lowercase.
  1016  // Set ` + "`aws.Config.LowerCaseHeaderMaps`" + ` to ` + "`true`" + ` to write unmarshaled keys to the map as lowercase.
  1017  `
  1018  			}
  1019  		}
  1020  	}
  1021  }
  1022  
  1023  func (a *API) validateNoDocumentShapes() error {
  1024  	var shapes []string
  1025  	for name, shape := range a.Shapes {
  1026  		if shape.Type != "structure" {
  1027  			continue
  1028  		}
  1029  		if shape.Document {
  1030  			shapes = append(shapes, name)
  1031  		}
  1032  	}
  1033  
  1034  	if len(shapes) == 0 {
  1035  		return nil
  1036  	}
  1037  
  1038  	return fmt.Errorf("model contains document shapes: %s", strings.Join(shapes, ", "))
  1039  }
  1040  
  1041  func getDeprecatedMessage(msg string, name string) string {
  1042  	if len(msg) == 0 {
  1043  		return name + " has been deprecated"
  1044  	}
  1045  
  1046  	return msg
  1047  }