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