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