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