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