Update Go AWS SDK to the latest version

This commit is contained in:
Andrey Smirnov
2019-07-13 00:03:55 +03:00
committed by Andrey Smirnov
parent d08be990ef
commit 94a72b23ff
2183 changed files with 885887 additions and 228114 deletions
+144 -68
View File
@@ -5,17 +5,18 @@ package api
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"path"
"path/filepath"
"regexp"
"sort"
"strings"
"text/template"
"unicode"
)
// SDKImportRoot is the root import path of the SDK.
const SDKImportRoot = "github.com/aws/aws-sdk-go"
// An API defines a service API's definition. and logic to serialize the definition.
type API struct {
Metadata Metadata
@@ -24,6 +25,7 @@ type API struct {
Waiters []Waiter
Documentation string
Examples Examples
SmokeTests SmokeTestSuite
// Set to true to avoid removing unused shapes
NoRemoveUnusedShapes bool
@@ -46,7 +48,7 @@ type API struct {
// Set to true to not generate struct field accessors
NoGenStructFieldAccessors bool
SvcClientImportPath string
BaseImportPath string
initialized bool
imports map[string]bool
@@ -54,6 +56,10 @@ type API struct {
path string
BaseCrosslinkURL string
HasEventStream bool `json:"-"`
EndpointDiscoveryOp *Operation
}
// A Metadata is the metadata about an API's definition.
@@ -67,21 +73,18 @@ type Metadata struct {
JSONVersion string
TargetPrefix string
Protocol string
ProtocolSettings ProtocolSettings
UID string
EndpointsID string
ServiceID string
NoResolveEndpoint bool
}
var serviceAliases map[string]string
func Bootstrap() error {
b, err := ioutil.ReadFile(filepath.Join("..", "models", "customizations", "service-aliases.json"))
if err != nil {
return err
}
return json.Unmarshal(b, &serviceAliases)
// ProtocolSettings define how the SDK should handle requests in the context
// of of a protocol.
type ProtocolSettings struct {
HTTP2 string `json:"h2,omitempty"`
}
// PackageName name of the API package
@@ -89,6 +92,11 @@ func (a *API) PackageName() string {
return strings.ToLower(a.StructName())
}
// ImportPath returns the client's full import path
func (a *API) ImportPath() string {
return path.Join(a.BaseImportPath, a.PackageName())
}
// InterfacePackageName returns the package name for the interface.
func (a *API) InterfacePackageName() string {
return a.PackageName() + "iface"
@@ -135,11 +143,6 @@ func (a *API) StructName() string {
// Strip out spaces.
name = strings.Replace(name, " ", "", -1)
// Swap out for alias name if one is defined.
if alias, ok := serviceAliases[strings.ToLower(name)]; ok {
name = alias
}
a.name = name
return a.name
}
@@ -217,8 +220,8 @@ func (a *API) ShapeNames() []string {
func (a *API) ShapeList() []*Shape {
list := make([]*Shape, 0, len(a.Shapes))
for _, n := range a.ShapeNames() {
// Ignore error shapes in list
if s := a.Shapes[n]; !s.IsError {
// Ignore non-eventstream exception shapes in list.
if s := a.Shapes[n]; !(s.Exception && len(s.EventFor) == 0) {
list = append(list, s)
}
}
@@ -230,7 +233,7 @@ func (a *API) ShapeListErrors() []*Shape {
list := []*Shape{}
for _, n := range a.ShapeNames() {
// Ignore error shapes in list
if s := a.Shapes[n]; s.IsError {
if s := a.Shapes[n]; s.Exception {
list = append(list, s)
}
}
@@ -239,9 +242,7 @@ func (a *API) ShapeListErrors() []*Shape {
// resetImports resets the import map to default values.
func (a *API) resetImports() {
a.imports = map[string]bool{
"github.com/aws/aws-sdk-go/aws": true,
}
a.imports = map[string]bool{}
}
// importsGoCode returns the generated Go import code.
@@ -293,22 +294,28 @@ var tplAPI = template.Must(template.New("api").Parse(`
{{ end }}
`))
// AddImport adds the import path to the generated file's import.
func (a *API) AddImport(v string) error {
a.imports[v] = true
return nil
}
// AddSDKImport adds a SDK package import to the generated file's import.
func (a *API) AddSDKImport(v ...string) error {
e := make([]string, 0, 5)
e = append(e, SDKImportRoot)
e = append(e, v...)
a.imports[path.Join(e...)] = true
return nil
}
// APIGoCode renders the API in Go code. Returning it as a string
func (a *API) APIGoCode() string {
a.resetImports()
a.imports["github.com/aws/aws-sdk-go/aws/awsutil"] = true
a.imports["github.com/aws/aws-sdk-go/aws/request"] = true
if a.OperationHasOutputPlaceholder() {
a.imports["github.com/aws/aws-sdk-go/private/protocol/"+a.ProtocolPackage()] = true
a.imports["github.com/aws/aws-sdk-go/private/protocol"] = true
}
for _, op := range a.Operations {
if op.AuthType == "none" {
a.imports["github.com/aws/aws-sdk-go/aws/credentials"] = true
break
}
}
a.AddSDKImport("aws")
a.AddSDKImport("aws/awsutil")
a.AddSDKImport("aws/request")
var buf bytes.Buffer
err := tplAPI.Execute(&buf, a)
@@ -407,7 +414,7 @@ var tplServiceDoc = template.Must(template.New("service docs").Funcs(template.Fu
//
// See the SDK's documentation for more information on how to use the SDK.
// https://docs.aws.amazon.com/sdk-for-go/api/
//
//
// See aws.Config documentation for more information on configuring SDK clients.
// https://docs.aws.amazon.com/sdk-for-go/api/aws/#Config
//
@@ -416,30 +423,62 @@ var tplServiceDoc = template.Must(template.New("service docs").Funcs(template.Fu
// https://docs.aws.amazon.com/sdk-for-go/api/service/{{ .PackageName }}/#New
`))
var serviceIDRegex = regexp.MustCompile("[^a-zA-Z0-9 ]+")
var prefixDigitRegex = regexp.MustCompile("^[0-9]+")
// ServiceID will return a unique identifier specific to a service.
func ServiceID(a *API) string {
if len(a.Metadata.ServiceID) > 0 {
return a.Metadata.ServiceID
}
name := a.Metadata.ServiceAbbreviation
if len(name) == 0 {
name = a.Metadata.ServiceFullName
}
name = strings.Replace(name, "Amazon", "", -1)
name = strings.Replace(name, "AWS", "", -1)
name = serviceIDRegex.ReplaceAllString(name, "")
name = prefixDigitRegex.ReplaceAllString(name, "")
name = strings.TrimSpace(name)
return name
}
// A tplService defines the template for the service generated code.
var tplService = template.Must(template.New("service").Funcs(template.FuncMap{
"ServiceNameConstValue": ServiceName,
"ServiceNameValue": func(a *API) string {
if a.NoConstServiceNames {
return fmt.Sprintf("%q", a.Metadata.EndpointPrefix)
if !a.NoConstServiceNames {
return "ServiceName"
}
return "ServiceName"
return fmt.Sprintf("%q", ServiceName(a))
},
"EndpointsIDConstValue": func(a *API) string {
if a.NoConstServiceNames {
return fmt.Sprintf("%q", a.Metadata.EndpointPrefix)
return fmt.Sprintf("%q", a.Metadata.EndpointsID)
}
if a.Metadata.EndpointPrefix == a.Metadata.EndpointsID {
if a.Metadata.EndpointsID == ServiceName(a) {
return "ServiceName"
}
return fmt.Sprintf("%q", a.Metadata.EndpointsID)
},
"EndpointsIDValue": func(a *API) string {
if a.NoConstServiceNames {
return fmt.Sprintf("%q", a.Metadata.EndpointPrefix)
return fmt.Sprintf("%q", a.Metadata.EndpointsID)
}
return "EndpointsID"
},
"ServiceIDVar": func(a *API) string {
if a.NoConstServiceNames {
return fmt.Sprintf("%q", ServiceID(a))
}
return "ServiceID"
},
"ServiceID": ServiceID,
}).Parse(`
// {{ .StructName }} provides the API operation methods for making requests to
// {{ .Metadata.ServiceFullName }}. See this package's package overview docs
@@ -449,6 +488,9 @@ var tplService = template.Must(template.New("service").Funcs(template.FuncMap{
// modify mutate any of the struct's properties though.
type {{ .StructName }} struct {
*client.Client
{{- if .EndpointDiscoveryOp }}
endpointCache *crr.EndpointCache
{{ end -}}
}
{{ if .UseInitMethods }}// Used for custom client initialization logic
@@ -462,8 +504,9 @@ var initRequest func(*request.Request)
{{ if not .NoConstServiceNames -}}
// Service information constants
const (
ServiceName = "{{ .Metadata.EndpointPrefix }}" // Service endpoint prefix API calls made to.
EndpointsID = {{ EndpointsIDConstValue . }} // Service ID for Regions and Endpoints metadata.
ServiceName = "{{ ServiceNameConstValue . }}" // Name of service.
EndpointsID = {{ EndpointsIDConstValue . }} // ID to lookup a service endpoint with.
ServiceID = "{{ ServiceID . }}" // ServiceID is a unique identifer of a specific service.
)
{{- end }}
@@ -504,14 +547,15 @@ func newClient(cfg aws.Config, handlers request.Handlers, endpoint, signingRegio
cfg,
metadata.ClientInfo{
ServiceName: {{ ServiceNameValue . }},
ServiceID : {{ ServiceIDVar . }},
SigningName: signingName,
SigningRegion: signingRegion,
Endpoint: endpoint,
APIVersion: "{{ .Metadata.APIVersion }}",
{{ if .Metadata.JSONVersion -}}
{{ if and (.Metadata.JSONVersion) (eq .Metadata.Protocol "json") -}}
JSONVersion: "{{ .Metadata.JSONVersion }}",
{{- end }}
{{ if .Metadata.TargetPrefix -}}
{{ if and (.Metadata.TargetPrefix) (eq .Metadata.Protocol "json") -}}
TargetPrefix: "{{ .Metadata.TargetPrefix }}",
{{- end }}
},
@@ -519,8 +563,22 @@ func newClient(cfg aws.Config, handlers request.Handlers, endpoint, signingRegio
),
}
{{- if .EndpointDiscoveryOp }}
svc.endpointCache = crr.NewEndpointCache(10)
{{- end }}
// Handlers
svc.Handlers.Sign.PushBackNamed({{if eq .Metadata.SignatureVersion "v2"}}v2{{else}}v4{{end}}.SignRequestHandler)
svc.Handlers.Sign.PushBackNamed(
{{- if eq .Metadata.SignatureVersion "v2" -}}
v2.SignRequestHandler
{{- else if or (eq .Metadata.SignatureVersion "s3") (eq .Metadata.SignatureVersion "s3v4") -}}
v4.BuildNamedHandler(v4.SignRequestHandler.Name, func(s *v4.Signer) {
s.DisableURIPathEscaping = true
})
{{- else -}}
v4.SignRequestHandler
{{- end -}}
)
{{- if eq .Metadata.SignatureVersion "v2" }}
svc.Handlers.Sign.PushBackNamed(corehandlers.BuildContentLengthHandler)
{{- end }}
@@ -528,6 +586,9 @@ func newClient(cfg aws.Config, handlers request.Handlers, endpoint, signingRegio
svc.Handlers.Unmarshal.PushBackNamed({{ .ProtocolPackage }}.UnmarshalHandler)
svc.Handlers.UnmarshalMeta.PushBackNamed({{ .ProtocolPackage }}.UnmarshalMetaHandler)
svc.Handlers.UnmarshalError.PushBackNamed({{ .ProtocolPackage }}.UnmarshalErrorHandler)
{{ if .HasEventStream }}
svc.Handlers.UnmarshalStream.PushBackNamed({{ .ProtocolPackage }}.UnmarshalHandler)
{{ end }}
{{ if .UseInitMethods }}// Run custom client initialization if present
if initClient != nil {
@@ -570,16 +631,20 @@ func (a *API) ServicePackageDoc() string {
// ServiceGoCode renders service go code. Returning it as a string.
func (a *API) ServiceGoCode() string {
a.resetImports()
a.imports["github.com/aws/aws-sdk-go/aws/client"] = true
a.imports["github.com/aws/aws-sdk-go/aws/client/metadata"] = true
a.imports["github.com/aws/aws-sdk-go/aws/request"] = true
a.AddSDKImport("aws")
a.AddSDKImport("aws/client")
a.AddSDKImport("aws/client/metadata")
a.AddSDKImport("aws/request")
if a.Metadata.SignatureVersion == "v2" {
a.imports["github.com/aws/aws-sdk-go/private/signer/v2"] = true
a.imports["github.com/aws/aws-sdk-go/aws/corehandlers"] = true
a.AddSDKImport("private/signer/v2")
a.AddSDKImport("aws/corehandlers")
} else {
a.imports["github.com/aws/aws-sdk-go/aws/signer/v4"] = true
a.AddSDKImport("aws/signer/v4")
}
a.AddSDKImport("private/protocol", a.ProtocolPackage())
if a.EndpointDiscoveryOp != nil {
a.AddSDKImport("aws/crr")
}
a.imports["github.com/aws/aws-sdk-go/private/protocol/"+a.ProtocolPackage()] = true
var buf bytes.Buffer
err := tplService.Execute(&buf, a)
@@ -607,9 +672,9 @@ func (a *API) ExampleGoCode() string {
"bytes",
"fmt",
"time",
"github.com/aws/aws-sdk-go/aws",
"github.com/aws/aws-sdk-go/aws/session",
path.Join(a.SvcClientImportPath, a.PackageName()),
SDKImportRoot+"/aws",
SDKImportRoot+"/aws/session",
a.ImportPath(),
)
for k := range imports {
code += fmt.Sprintf("%q\n", k)
@@ -665,7 +730,7 @@ var tplInterface = template.Must(template.New("interface").Parse(`
//
// It is important to note that this interface will have breaking changes
// when the service model is updated and adds new API operations, paginators,
// and waiters. Its suggested to use the pattern above for testing, or using
// and waiters. Its suggested to use the pattern above for testing, or using
// tooling to generate mocks to satisfy the interfaces.
type {{ .StructName }}API interface {
{{ range $_, $o := .OperationList }}
@@ -684,11 +749,9 @@ var _ {{ .StructName }}API = (*{{ .PackageName }}.{{ .StructName }})(nil)
// package than the service API's package.
func (a *API) InterfaceGoCode() string {
a.resetImports()
a.imports = map[string]bool{
"github.com/aws/aws-sdk-go/aws": true,
"github.com/aws/aws-sdk-go/aws/request": true,
path.Join(a.SvcClientImportPath, a.PackageName()): true,
}
a.AddSDKImport("aws")
a.AddSDKImport("aws/request")
a.AddImport(a.ImportPath())
var buf bytes.Buffer
err := tplInterface.Execute(&buf, a)
@@ -726,7 +789,6 @@ func resolveShapeValidations(s *Shape, ancestry ...*Shape) {
children := []string{}
for _, name := range s.MemberNames() {
ref := s.MemberRefs[name]
if s.IsRequired(name) && !s.Validations.Has(ref, ShapeValidationRequired) {
s.Validations = append(s.Validations, ShapeValidation{
Name: name, Ref: ref, Type: ShapeValidationRequired,
@@ -739,6 +801,12 @@ func resolveShapeValidations(s *Shape, ancestry ...*Shape) {
})
}
if !ref.CanBeEmpty() && !s.Validations.Has(ref, ShapeValidationMinVal) {
s.Validations = append(s.Validations, ShapeValidation{
Name: name, Ref: ref, Type: ShapeValidationMinVal,
})
}
switch ref.Shape.Type {
case "map", "list", "structure":
children = append(children, name)
@@ -806,7 +874,7 @@ func (a *API) APIErrorsGoCode() string {
// removeOperation removes an operation, its input/output shapes, as well as
// any references/shapes that are unique to this operation.
func (a *API) removeOperation(name string) {
fmt.Println("removing operation,", name)
debugLogger.Logln("removing operation,", name)
op := a.Operations[name]
delete(a.Operations, name)
@@ -820,7 +888,7 @@ func (a *API) removeOperation(name string) {
// shapes. Will also remove member reference targeted shapes if those shapes do
// not have any additional references.
func (a *API) removeShape(s *Shape) {
fmt.Println("removing shape,", s.ShapeName)
debugLogger.Logln("removing shape,", s.ShapeName)
delete(a.Shapes, s.ShapeName)
@@ -851,3 +919,11 @@ func (a *API) removeShapeRef(ref *ShapeRef) {
a.removeShape(ref.Shape)
}
}
func getDeprecatedMessage(msg string, name string) string {
if len(msg) == 0 {
return name + " has been deprecated"
}
return msg
}
+5 -3
View File
@@ -7,8 +7,8 @@ import (
)
func TestAPI_StructName(t *testing.T) {
origAliases := serviceAliases
defer func() { serviceAliases = origAliases }()
origAliases := serviceAliaseNames
defer func() { serviceAliaseNames = origAliases }()
cases := map[string]struct {
Aliases map[string]string
@@ -57,12 +57,14 @@ func TestAPI_StructName(t *testing.T) {
for k, c := range cases {
t.Run(k, func(t *testing.T) {
serviceAliases = c.Aliases
serviceAliaseNames = c.Aliases
a := API{
Metadata: c.Metadata,
}
a.Setup()
if e, o := c.StructName, a.StructName(); e != o {
t.Errorf("expect %v structName, got %v", e, o)
}
@@ -0,0 +1,131 @@
{
"version":"2.0",
"metadata":{
"apiVersion":"2018-08-31",
"endpointPrefix":"awsendpointdiscoverytestservice",
"jsonVersion":"1.1",
"protocol":"json",
"serviceAbbreviation":"AwsEndpointDiscoveryTest",
"serviceFullName":"AwsEndpointDiscoveryTest",
"signatureVersion":"v4",
"signingName":"awsendpointdiscoverytestservice",
"targetPrefix":"AwsEndpointDiscoveryTestService"
},
"operations":{
"DescribeEndpoints":{
"name":"DescribeEndpoints",
"http":{
"method":"POST",
"requestUri":"/"
},
"input":{"shape":"DescribeEndpointsRequest"},
"output":{"shape":"DescribeEndpointsResponse"},
"endpointoperation":true
},
"TestDiscoveryIdentifiersRequired":{
"name":"TestDiscoveryIdentifiersRequired",
"http":{
"method":"POST",
"requestUri":"/"
},
"input":{"shape":"TestDiscoveryIdentifiersRequiredRequest"},
"output":{"shape":"TestDiscoveryIdentifiersRequiredResponse"},
"endpointdiscovery":{"required":true}
},
"TestDiscoveryOptional":{
"name":"TestDiscoveryOptional",
"http":{
"method":"POST",
"requestUri":"/"
},
"input":{"shape":"TestDiscoveryOptionalRequest"},
"output":{"shape":"TestDiscoveryOptionalResponse"},
"endpointdiscovery":{
}
},
"TestDiscoveryRequired":{
"name":"TestDiscoveryRequired",
"http":{
"method":"POST",
"requestUri":"/"
},
"input":{"shape":"TestDiscoveryRequiredRequest"},
"output":{"shape":"TestDiscoveryRequiredResponse"},
"endpointdiscovery":{
}
}
},
"shapes":{
"Boolean":{"type":"boolean"},
"DescribeEndpointsRequest":{
"type":"structure",
"members":{
"Operation": {"shape":"String"}
}
},
"DescribeEndpointsResponse":{
"type":"structure",
"required":["Endpoints"],
"members":{
"Endpoints":{"shape":"Endpoints"}
}
},
"Endpoint":{
"type":"structure",
"required":[
"Address",
"CachePeriodInMinutes"
],
"members":{
"Address":{"shape":"String"},
"CachePeriodInMinutes":{"shape":"Long"}
}
},
"Endpoints":{
"type":"list",
"member":{"shape":"Endpoint"}
},
"Long":{"type":"long"},
"String":{"type":"string"},
"TestDiscoveryIdentifiersRequiredRequest":{
"type":"structure",
"required":["Sdk"],
"members":{
"Sdk":{
"shape":"String",
"endpointdiscoveryid":true
}
}
},
"TestDiscoveryIdentifiersRequiredResponse":{
"type":"structure",
"members":{
"RequestSuccessful":{"shape":"Boolean"}
}
},
"TestDiscoveryOptionalRequest":{
"type":"structure",
"members":{
"Sdk":{"shape":"String"}
}
},
"TestDiscoveryOptionalResponse":{
"type":"structure",
"members":{
"RequestSuccessful":{"shape":"Boolean"}
}
},
"TestDiscoveryRequiredRequest":{
"type":"structure",
"members":{
"Sdk":{"shape":"String"}
}
},
"TestDiscoveryRequiredResponse":{
"type":"structure",
"members":{
"RequestSuccessful":{"shape":"Boolean"}
}
}
}
}
@@ -0,0 +1,7 @@
{
"version": "2.0",
"service": null,
"operations": {
},
"shapes":{}
}
@@ -0,0 +1,228 @@
{
"version":"2.0",
"metadata":{
"apiVersion":"0000-00-00",
"endpointPrefix":"rpcservice",
"jsonVersion":"1.1",
"protocol":"json",
"serviceAbbreviation":"RPCService",
"serviceFullName":"RPC Service",
"serviceId":"RPCService",
"signatureVersion":"v4",
"targetPrefix":"RPCService_00000000",
"uid":"RPCService-0000-00-00"
},
"operations":{
"GetEventStream":{
"name":"GetEventStream",
"http":{
"method":"POST",
"requestUri":"/"
},
"input":{"shape":"GetEventStreamRequest"},
"output":{"shape":"GetEventStreamResponse"}
},
"EmptyStream":{
"name":"EmptyStream",
"http":{
"method":"POST",
"requestUri":"/"
},
"input":{"shape":"EmptyStreamRequest"},
"output":{"shape":"EmptyStreamResponse"}
},
"OtherOperation":{
"name":"OtherOperation",
"http":{
"method":"POST",
"requestUri":"/"
},
"errors":[
{"shape":"ExceptionEvent2"}
]
}
},
"shapes":{
"Bool":{"type":"boolean"},
"Byte":{"type":"byte"},
"Short":{"type":"short"},
"Integer":{"type":"integer"},
"Long":{"type":"long"},
"Blob":{"type":"blob"},
"String":{"type":"string"},
"Time":{"type":"timestamp"},
"GetEventStreamRequest":{
"type":"structure",
"members":{
"InputVal":{"shape":"String"}
}
},
"GetEventStreamResponse":{
"type":"structure",
"members":{
"StrVal":{"shape":"String"},
"IntVal":{"shape":"Integer"},
"EventStream":{"shape":"EventStream"}
}
},
"EventStream":{
"type":"structure",
"members":{
"Headers":{"shape":"HeaderOnlyEvent"},
"ImplicitPayload":{"shape":"ImplicitPayloadEvent"},
"ExplicitPayload":{"shape":"ExplicitPayloadEvent"},
"PayloadOnly":{"shape":"PayloadOnlyEvent"},
"PayloadOnlyBlob":{"shape":"PayloadOnlyBlobEvent"},
"PayloadOnlyString":{"shape":"PayloadOnlyStringEvent"},
"Empty":{"shape":"EmptyEvent"},
"Exception":{"shape":"ExceptionEvent"},
"Exception2":{"shape":"ExceptionEvent2"}
},
"eventstream":true
},
"EmptyStreamRequest":{
"type":"structure",
"members":{}
},
"EmptyStreamResponse":{
"type":"structure",
"members":{
"EventStream":{"shape":"EmptyEventStream"}
}
},
"EmptyEventStream":{
"type":"structure",
"members":{
},
"eventstream":true
},
"HeaderOnlyEvent":{
"type":"structure",
"members":{
"BoolVal":{
"shape":"Bool",
"eventheader":true
},
"ByteVal":{
"shape":"Byte",
"eventheader":true
},
"ShortVal":{
"shape":"Short",
"eventheader":true
},
"IntegerVal":{
"shape":"Integer",
"eventheader":true
},
"LongVal":{
"shape":"Long",
"eventheader":true
},
"StringVal":{
"shape":"String",
"eventheader":true
},
"BlobVal":{
"shape":"Blob",
"eventheader":true
},
"TimeVal":{
"shape":"Time",
"eventheader":true
}
},
"event":true
},
"ImplicitPayloadEvent": {
"type":"structure",
"members":{
"ByteVal":{
"shape":"Byte",
"eventheader":true
},
"ShortVal":{
"shape":"Short"
},
"IntegerVal":{
"shape":"Integer"
}
},
"event":true
},
"ExplicitPayloadEvent": {
"type":"structure",
"members":{
"LongVal":{
"shape":"Long",
"eventheader":true
},
"StringVal":{
"shape":"String",
"eventheader":true
},
"NestedVal":{
"shape":"NestedShape",
"eventpayload":true
}
},
"event":true
},
"PayloadOnlyEvent":{
"type":"structure",
"members":{
"NestedVal":{
"shape":"NestedShape",
"eventpayload":true
}
},
"event":true
},
"PayloadOnlyBlobEvent":{
"type":"structure",
"members":{
"BlobPayload":{
"shape":"Blob",
"eventpayload":true
}
},
"event":true
},
"PayloadOnlyStringEvent":{
"type":"structure",
"members":{
"StringPayload":{
"shape":"String",
"eventpayload":true
}
},
"event":true
},
"EmptyEvent": {
"type":"structure",
"members":{},
"event": true
},
"NestedShape": {
"type":"structure",
"members":{
"IntVal":{"shape":"Integer"},
"StrVal":{"shape":"String"}
}
},
"ExceptionEvent":{
"type":"structure",
"members":{
"IntVal":{"shape":"Integer"},
"message":{"shape":"String"}
},
"exception":true
},
"ExceptionEvent2":{
"type":"structure",
"members":{
},
"exception":true
}
}
}
@@ -0,0 +1,7 @@
{
"version": "2.0",
"service": null,
"operations": {
},
"shapes":{}
}
@@ -0,0 +1,226 @@
{
"version":"2.0",
"metadata":{
"apiVersion":"0000-00-00",
"endpointPrefix":"restjsonservice",
"protocol":"rest-json",
"serviceAbbreviation":"RESTJSONService",
"serviceFullName":"REST JSON Service",
"serviceId":"RESTJSONService",
"signatureVersion":"v4",
"uid":"RESTJSONService-0000-00-00"
},
"operations":{
"GetEventStream":{
"name":"GetEventStream",
"http":{
"method":"POST",
"requestUri":"/"
},
"input":{"shape":"GetEventStreamRequest"},
"output":{"shape":"GetEventStreamResponse"}
},
"EmptyStream":{
"name":"EmptyStream",
"http":{
"method":"POST",
"requestUri":"/"
},
"input":{"shape":"EmptyStreamRequest"},
"output":{"shape":"EmptyStreamResponse"}
},
"OtherOperation":{
"name":"OtherOperation",
"http":{
"method":"POST",
"requestUri":"/"
},
"errors":[
{"shape":"ExceptionEvent2"}
]
}
},
"shapes":{
"Bool":{"type":"boolean"},
"Byte":{"type":"byte"},
"Short":{"type":"short"},
"Integer":{"type":"integer"},
"Long":{"type":"long"},
"Blob":{"type":"blob"},
"String":{"type":"string"},
"Time":{"type":"timestamp"},
"GetEventStreamRequest":{
"type":"structure",
"members":{
"InputVal":{"shape":"String"}
}
},
"GetEventStreamResponse":{
"type":"structure",
"members":{
"StrVal":{"shape":"String"},
"IntVal":{"shape":"Integer"},
"EventStream":{"shape":"EventStream"}
}
},
"EventStream":{
"type":"structure",
"members":{
"Headers":{"shape":"HeaderOnlyEvent"},
"ImplicitPayload":{"shape":"ImplicitPayloadEvent"},
"ExplicitPayload":{"shape":"ExplicitPayloadEvent"},
"PayloadOnly":{"shape":"PayloadOnlyEvent"},
"PayloadOnlyBlob":{"shape":"PayloadOnlyBlobEvent"},
"PayloadOnlyString":{"shape":"PayloadOnlyStringEvent"},
"Empty":{"shape":"EmptyEvent"},
"Exception":{"shape":"ExceptionEvent"},
"Exception2":{"shape":"ExceptionEvent2"}
},
"eventstream":true
},
"EmptyStreamRequest":{
"type":"structure",
"members":{}
},
"EmptyStreamResponse":{
"type":"structure",
"members":{
"EventStream":{"shape":"EmptyEventStream"}
}
},
"EmptyEventStream":{
"type":"structure",
"members":{
},
"eventstream":true
},
"HeaderOnlyEvent":{
"type":"structure",
"members":{
"BoolVal":{
"shape":"Bool",
"eventheader":true
},
"ByteVal":{
"shape":"Byte",
"eventheader":true
},
"ShortVal":{
"shape":"Short",
"eventheader":true
},
"IntegerVal":{
"shape":"Integer",
"eventheader":true
},
"LongVal":{
"shape":"Long",
"eventheader":true
},
"StringVal":{
"shape":"String",
"eventheader":true
},
"BlobVal":{
"shape":"Blob",
"eventheader":true
},
"TimeVal":{
"shape":"Time",
"eventheader":true
}
},
"event":true
},
"ImplicitPayloadEvent": {
"type":"structure",
"members":{
"ByteVal":{
"shape":"Byte",
"eventheader":true
},
"ShortVal":{
"shape":"Short"
},
"IntegerVal":{
"shape":"Integer"
}
},
"event":true
},
"ExplicitPayloadEvent": {
"type":"structure",
"members":{
"LongVal":{
"shape":"Long",
"eventheader":true
},
"StringVal":{
"shape":"String",
"eventheader":true
},
"NestedVal":{
"shape":"NestedShape",
"eventpayload":true
}
},
"event":true
},
"PayloadOnlyEvent":{
"type":"structure",
"members":{
"NestedVal":{
"shape":"NestedShape",
"eventpayload":true
}
},
"event":true
},
"PayloadOnlyBlobEvent":{
"type":"structure",
"members":{
"BlobPayload":{
"shape":"Blob",
"eventpayload":true
}
},
"event":true
},
"PayloadOnlyStringEvent":{
"type":"structure",
"members":{
"StringPayload":{
"shape":"String",
"eventpayload":true
}
},
"event":true
},
"EmptyEvent": {
"type":"structure",
"members":{},
"event": true
},
"NestedShape": {
"type":"structure",
"members":{
"IntVal":{"shape":"Integer"},
"StrVal":{"shape":"String"}
}
},
"ExceptionEvent":{
"type":"structure",
"members":{
"IntVal":{"shape":"Integer"},
"message":{"shape":"String"}
},
"exception":true
},
"ExceptionEvent2":{
"type":"structure",
"members":{
},
"exception":true
}
}
}
@@ -0,0 +1,7 @@
{
"version": "2.0",
"service": null,
"operations": {
},
"shapes":{}
}
@@ -0,0 +1,226 @@
{
"version":"2.0",
"metadata":{
"apiVersion":"0000-00-00",
"endpointPrefix":"restxmlservice",
"protocol":"rest-xml",
"serviceAbbreviation":"RESTXMLService",
"serviceFullName":"REST XML Service",
"serviceId":"RESTXMLService",
"signatureVersion":"v4",
"uid":"RESTXMLService-0000-00-00"
},
"operations":{
"GetEventStream":{
"name":"GetEventStream",
"http":{
"method":"POST",
"requestUri":"/"
},
"input":{"shape":"GetEventStreamRequest"},
"output":{"shape":"GetEventStreamResponse"}
},
"EmptyStream":{
"name":"EmptyStream",
"http":{
"method":"POST",
"requestUri":"/"
},
"input":{"shape":"EmptyStreamRequest"},
"output":{"shape":"EmptyStreamResponse"}
},
"OtherOperation":{
"name":"OtherOperation",
"http":{
"method":"POST",
"requestUri":"/"
},
"errors":[
{"shape":"ExceptionEvent2"}
]
}
},
"shapes":{
"Bool":{"type":"boolean"},
"Byte":{"type":"byte"},
"Short":{"type":"short"},
"Integer":{"type":"integer"},
"Long":{"type":"long"},
"Blob":{"type":"blob"},
"String":{"type":"string"},
"Time":{"type":"timestamp"},
"GetEventStreamRequest":{
"type":"structure",
"members":{
"InputVal":{"shape":"String"}
}
},
"GetEventStreamResponse":{
"type":"structure",
"members":{
"StrVal":{"shape":"String"},
"IntVal":{"shape":"Integer"},
"EventStream":{"shape":"EventStream"}
}
},
"EventStream":{
"type":"structure",
"members":{
"Headers":{"shape":"HeaderOnlyEvent"},
"ImplicitPayload":{"shape":"ImplicitPayloadEvent"},
"ExplicitPayload":{"shape":"ExplicitPayloadEvent"},
"PayloadOnly":{"shape":"PayloadOnlyEvent"},
"PayloadOnlyBlob":{"shape":"PayloadOnlyBlobEvent"},
"PayloadOnlyString":{"shape":"PayloadOnlyStringEvent"},
"Empty":{"shape":"EmptyEvent"},
"Exception":{"shape":"ExceptionEvent"},
"Exception2":{"shape":"ExceptionEvent2"}
},
"eventstream":true
},
"EmptyStreamRequest":{
"type":"structure",
"members":{}
},
"EmptyStreamResponse":{
"type":"structure",
"members":{
"EventStream":{"shape":"EmptyEventStream"}
}
},
"EmptyEventStream":{
"type":"structure",
"members":{
},
"eventstream":true
},
"HeaderOnlyEvent":{
"type":"structure",
"members":{
"BoolVal":{
"shape":"Bool",
"eventheader":true
},
"ByteVal":{
"shape":"Byte",
"eventheader":true
},
"ShortVal":{
"shape":"Short",
"eventheader":true
},
"IntegerVal":{
"shape":"Integer",
"eventheader":true
},
"LongVal":{
"shape":"Long",
"eventheader":true
},
"StringVal":{
"shape":"String",
"eventheader":true
},
"BlobVal":{
"shape":"Blob",
"eventheader":true
},
"TimeVal":{
"shape":"Time",
"eventheader":true
}
},
"event":true
},
"ImplicitPayloadEvent": {
"type":"structure",
"members":{
"ByteVal":{
"shape":"Byte",
"eventheader":true
},
"ShortVal":{
"shape":"Short"
},
"IntegerVal":{
"shape":"Integer"
}
},
"event":true
},
"ExplicitPayloadEvent": {
"type":"structure",
"members":{
"LongVal":{
"shape":"Long",
"eventheader":true
},
"StringVal":{
"shape":"String",
"eventheader":true
},
"NestedVal":{
"shape":"NestedShape",
"eventpayload":true
}
},
"event":true
},
"PayloadOnlyEvent":{
"type":"structure",
"members":{
"NestedVal":{
"shape":"NestedShape",
"eventpayload":true
}
},
"event":true
},
"PayloadOnlyBlobEvent":{
"type":"structure",
"members":{
"BlobPayload":{
"shape":"Blob",
"eventpayload":true
}
},
"event":true
},
"PayloadOnlyStringEvent":{
"type":"structure",
"members":{
"StringPayload":{
"shape":"String",
"eventpayload":true
}
},
"event":true
},
"EmptyEvent": {
"type":"structure",
"members":{},
"event": true
},
"NestedShape": {
"type":"structure",
"members":{
"IntVal":{"shape":"Integer"},
"StrVal":{"shape":"String"}
}
},
"ExceptionEvent":{
"type":"structure",
"members":{
"IntVal":{"shape":"Integer"},
"message":{"shape":"String"}
},
"exception":true
},
"ExceptionEvent2":{
"type":"structure",
"members":{
},
"exception":true
}
}
}
@@ -0,0 +1,7 @@
{
"version": "2.0",
"service": null,
"operations": {
},
"shapes":{}
}
@@ -0,0 +1,635 @@
// Code generated by private/model/cli/gen-api/main.go. DO NOT EDIT.
package awsendpointdiscoverytest
import (
"net/url"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awsutil"
"github.com/aws/aws-sdk-go/aws/crr"
"github.com/aws/aws-sdk-go/aws/request"
)
const opDescribeEndpoints = "DescribeEndpoints"
// DescribeEndpointsRequest generates a "aws/request.Request" representing the
// client's request for the DescribeEndpoints operation. The "output" return
// value will be populated with the request's response once the request completes
// successfully.
//
// Use "Send" method on the returned Request to send the API call to the service.
// the "output" return value is not valid until after Send returns without error.
//
// See DescribeEndpoints for more information on using the DescribeEndpoints
// API call, and error handling.
//
// This method is useful when you want to inject custom logic or configuration
// into the SDK's request lifecycle. Such as custom headers, or retry logic.
//
//
// // Example sending a request using the DescribeEndpointsRequest method.
// req, resp := client.DescribeEndpointsRequest(params)
//
// err := req.Send()
// if err == nil { // resp is now filled
// fmt.Println(resp)
// }
func (c *AwsEndpointDiscoveryTest) DescribeEndpointsRequest(input *DescribeEndpointsInput) (req *request.Request, output *DescribeEndpointsOutput) {
op := &request.Operation{
Name: opDescribeEndpoints,
HTTPMethod: "POST",
HTTPPath: "/",
}
if input == nil {
input = &DescribeEndpointsInput{}
}
output = &DescribeEndpointsOutput{}
req = c.newRequest(op, input, output)
return
}
// DescribeEndpoints API operation for AwsEndpointDiscoveryTest.
//
// Returns awserr.Error for service API and SDK errors. Use runtime type assertions
// with awserr.Error's Code and Message methods to get detailed information about
// the error.
//
// See the AWS API reference guide for AwsEndpointDiscoveryTest's
// API operation DescribeEndpoints for usage and error information.
func (c *AwsEndpointDiscoveryTest) DescribeEndpoints(input *DescribeEndpointsInput) (*DescribeEndpointsOutput, error) {
req, out := c.DescribeEndpointsRequest(input)
return out, req.Send()
}
// DescribeEndpointsWithContext is the same as DescribeEndpoints with the addition of
// the ability to pass a context and additional request options.
//
// See DescribeEndpoints for details on how to use this API operation.
//
// The context must be non-nil and will be used for request cancellation. If
// the context is nil a panic will occur. In the future the SDK may create
// sub-contexts for http.Requests. See https://golang.org/pkg/context/
// for more information on using Contexts.
func (c *AwsEndpointDiscoveryTest) DescribeEndpointsWithContext(ctx aws.Context, input *DescribeEndpointsInput, opts ...request.Option) (*DescribeEndpointsOutput, error) {
req, out := c.DescribeEndpointsRequest(input)
req.SetContext(ctx)
req.ApplyOptions(opts...)
return out, req.Send()
}
type discovererDescribeEndpoints struct {
Client *AwsEndpointDiscoveryTest
Required bool
EndpointCache *crr.EndpointCache
Params map[string]*string
Key string
}
func (d *discovererDescribeEndpoints) Discover() (crr.Endpoint, error) {
input := &DescribeEndpointsInput{
Operation: d.Params["op"],
}
resp, err := d.Client.DescribeEndpoints(input)
if err != nil {
return crr.Endpoint{}, err
}
endpoint := crr.Endpoint{
Key: d.Key,
}
for _, e := range resp.Endpoints {
if e.Address == nil {
continue
}
cachedInMinutes := aws.Int64Value(e.CachePeriodInMinutes)
u, err := url.Parse(*e.Address)
if err != nil {
continue
}
addr := crr.WeightedAddress{
URL: u,
Expired: time.Now().Add(time.Duration(cachedInMinutes) * time.Minute),
}
endpoint.Add(addr)
}
d.EndpointCache.Add(endpoint)
return endpoint, nil
}
func (d *discovererDescribeEndpoints) Handler(r *request.Request) {
endpointKey := crr.BuildEndpointKey(d.Params)
d.Key = endpointKey
endpoint, err := d.EndpointCache.Get(d, endpointKey, d.Required)
if err != nil {
r.Error = err
return
}
if endpoint.URL != nil && len(endpoint.URL.String()) > 0 {
r.HTTPRequest.URL = endpoint.URL
}
}
const opTestDiscoveryIdentifiersRequired = "TestDiscoveryIdentifiersRequired"
// TestDiscoveryIdentifiersRequiredRequest generates a "aws/request.Request" representing the
// client's request for the TestDiscoveryIdentifiersRequired operation. The "output" return
// value will be populated with the request's response once the request completes
// successfully.
//
// Use "Send" method on the returned Request to send the API call to the service.
// the "output" return value is not valid until after Send returns without error.
//
// See TestDiscoveryIdentifiersRequired for more information on using the TestDiscoveryIdentifiersRequired
// API call, and error handling.
//
// This method is useful when you want to inject custom logic or configuration
// into the SDK's request lifecycle. Such as custom headers, or retry logic.
//
//
// // Example sending a request using the TestDiscoveryIdentifiersRequiredRequest method.
// req, resp := client.TestDiscoveryIdentifiersRequiredRequest(params)
//
// err := req.Send()
// if err == nil { // resp is now filled
// fmt.Println(resp)
// }
func (c *AwsEndpointDiscoveryTest) TestDiscoveryIdentifiersRequiredRequest(input *TestDiscoveryIdentifiersRequiredInput) (req *request.Request, output *TestDiscoveryIdentifiersRequiredOutput) {
op := &request.Operation{
Name: opTestDiscoveryIdentifiersRequired,
HTTPMethod: "POST",
HTTPPath: "/",
}
if input == nil {
input = &TestDiscoveryIdentifiersRequiredInput{}
}
output = &TestDiscoveryIdentifiersRequiredOutput{}
req = c.newRequest(op, input, output)
de := discovererDescribeEndpoints{
Required: true,
EndpointCache: c.endpointCache,
Params: map[string]*string{
"op": aws.String(req.Operation.Name),
"Sdk": input.Sdk,
},
Client: c,
}
for k, v := range de.Params {
if v == nil {
delete(de.Params, k)
}
}
req.Handlers.Build.PushFrontNamed(request.NamedHandler{
Name: "crr.endpointdiscovery",
Fn: de.Handler,
})
return
}
// TestDiscoveryIdentifiersRequired API operation for AwsEndpointDiscoveryTest.
//
// Returns awserr.Error for service API and SDK errors. Use runtime type assertions
// with awserr.Error's Code and Message methods to get detailed information about
// the error.
//
// See the AWS API reference guide for AwsEndpointDiscoveryTest's
// API operation TestDiscoveryIdentifiersRequired for usage and error information.
func (c *AwsEndpointDiscoveryTest) TestDiscoveryIdentifiersRequired(input *TestDiscoveryIdentifiersRequiredInput) (*TestDiscoveryIdentifiersRequiredOutput, error) {
req, out := c.TestDiscoveryIdentifiersRequiredRequest(input)
return out, req.Send()
}
// TestDiscoveryIdentifiersRequiredWithContext is the same as TestDiscoveryIdentifiersRequired with the addition of
// the ability to pass a context and additional request options.
//
// See TestDiscoveryIdentifiersRequired for details on how to use this API operation.
//
// The context must be non-nil and will be used for request cancellation. If
// the context is nil a panic will occur. In the future the SDK may create
// sub-contexts for http.Requests. See https://golang.org/pkg/context/
// for more information on using Contexts.
func (c *AwsEndpointDiscoveryTest) TestDiscoveryIdentifiersRequiredWithContext(ctx aws.Context, input *TestDiscoveryIdentifiersRequiredInput, opts ...request.Option) (*TestDiscoveryIdentifiersRequiredOutput, error) {
req, out := c.TestDiscoveryIdentifiersRequiredRequest(input)
req.SetContext(ctx)
req.ApplyOptions(opts...)
return out, req.Send()
}
const opTestDiscoveryOptional = "TestDiscoveryOptional"
// TestDiscoveryOptionalRequest generates a "aws/request.Request" representing the
// client's request for the TestDiscoveryOptional operation. The "output" return
// value will be populated with the request's response once the request completes
// successfully.
//
// Use "Send" method on the returned Request to send the API call to the service.
// the "output" return value is not valid until after Send returns without error.
//
// See TestDiscoveryOptional for more information on using the TestDiscoveryOptional
// API call, and error handling.
//
// This method is useful when you want to inject custom logic or configuration
// into the SDK's request lifecycle. Such as custom headers, or retry logic.
//
//
// // Example sending a request using the TestDiscoveryOptionalRequest method.
// req, resp := client.TestDiscoveryOptionalRequest(params)
//
// err := req.Send()
// if err == nil { // resp is now filled
// fmt.Println(resp)
// }
func (c *AwsEndpointDiscoveryTest) TestDiscoveryOptionalRequest(input *TestDiscoveryOptionalInput) (req *request.Request, output *TestDiscoveryOptionalOutput) {
op := &request.Operation{
Name: opTestDiscoveryOptional,
HTTPMethod: "POST",
HTTPPath: "/",
}
if input == nil {
input = &TestDiscoveryOptionalInput{}
}
output = &TestDiscoveryOptionalOutput{}
req = c.newRequest(op, input, output)
if aws.BoolValue(req.Config.EnableEndpointDiscovery) {
de := discovererDescribeEndpoints{
Required: false,
EndpointCache: c.endpointCache,
Params: map[string]*string{
"op": aws.String(req.Operation.Name),
},
Client: c,
}
for k, v := range de.Params {
if v == nil {
delete(de.Params, k)
}
}
req.Handlers.Build.PushFrontNamed(request.NamedHandler{
Name: "crr.endpointdiscovery",
Fn: de.Handler,
})
}
return
}
// TestDiscoveryOptional API operation for AwsEndpointDiscoveryTest.
//
// Returns awserr.Error for service API and SDK errors. Use runtime type assertions
// with awserr.Error's Code and Message methods to get detailed information about
// the error.
//
// See the AWS API reference guide for AwsEndpointDiscoveryTest's
// API operation TestDiscoveryOptional for usage and error information.
func (c *AwsEndpointDiscoveryTest) TestDiscoveryOptional(input *TestDiscoveryOptionalInput) (*TestDiscoveryOptionalOutput, error) {
req, out := c.TestDiscoveryOptionalRequest(input)
return out, req.Send()
}
// TestDiscoveryOptionalWithContext is the same as TestDiscoveryOptional with the addition of
// the ability to pass a context and additional request options.
//
// See TestDiscoveryOptional for details on how to use this API operation.
//
// The context must be non-nil and will be used for request cancellation. If
// the context is nil a panic will occur. In the future the SDK may create
// sub-contexts for http.Requests. See https://golang.org/pkg/context/
// for more information on using Contexts.
func (c *AwsEndpointDiscoveryTest) TestDiscoveryOptionalWithContext(ctx aws.Context, input *TestDiscoveryOptionalInput, opts ...request.Option) (*TestDiscoveryOptionalOutput, error) {
req, out := c.TestDiscoveryOptionalRequest(input)
req.SetContext(ctx)
req.ApplyOptions(opts...)
return out, req.Send()
}
const opTestDiscoveryRequired = "TestDiscoveryRequired"
// TestDiscoveryRequiredRequest generates a "aws/request.Request" representing the
// client's request for the TestDiscoveryRequired operation. The "output" return
// value will be populated with the request's response once the request completes
// successfully.
//
// Use "Send" method on the returned Request to send the API call to the service.
// the "output" return value is not valid until after Send returns without error.
//
// See TestDiscoveryRequired for more information on using the TestDiscoveryRequired
// API call, and error handling.
//
// This method is useful when you want to inject custom logic or configuration
// into the SDK's request lifecycle. Such as custom headers, or retry logic.
//
//
// // Example sending a request using the TestDiscoveryRequiredRequest method.
// req, resp := client.TestDiscoveryRequiredRequest(params)
//
// err := req.Send()
// if err == nil { // resp is now filled
// fmt.Println(resp)
// }
func (c *AwsEndpointDiscoveryTest) TestDiscoveryRequiredRequest(input *TestDiscoveryRequiredInput) (req *request.Request, output *TestDiscoveryRequiredOutput) {
op := &request.Operation{
Name: opTestDiscoveryRequired,
HTTPMethod: "POST",
HTTPPath: "/",
}
if input == nil {
input = &TestDiscoveryRequiredInput{}
}
output = &TestDiscoveryRequiredOutput{}
req = c.newRequest(op, input, output)
if aws.BoolValue(req.Config.EnableEndpointDiscovery) {
de := discovererDescribeEndpoints{
Required: false,
EndpointCache: c.endpointCache,
Params: map[string]*string{
"op": aws.String(req.Operation.Name),
},
Client: c,
}
for k, v := range de.Params {
if v == nil {
delete(de.Params, k)
}
}
req.Handlers.Build.PushFrontNamed(request.NamedHandler{
Name: "crr.endpointdiscovery",
Fn: de.Handler,
})
}
return
}
// TestDiscoveryRequired API operation for AwsEndpointDiscoveryTest.
//
// Returns awserr.Error for service API and SDK errors. Use runtime type assertions
// with awserr.Error's Code and Message methods to get detailed information about
// the error.
//
// See the AWS API reference guide for AwsEndpointDiscoveryTest's
// API operation TestDiscoveryRequired for usage and error information.
func (c *AwsEndpointDiscoveryTest) TestDiscoveryRequired(input *TestDiscoveryRequiredInput) (*TestDiscoveryRequiredOutput, error) {
req, out := c.TestDiscoveryRequiredRequest(input)
return out, req.Send()
}
// TestDiscoveryRequiredWithContext is the same as TestDiscoveryRequired with the addition of
// the ability to pass a context and additional request options.
//
// See TestDiscoveryRequired for details on how to use this API operation.
//
// The context must be non-nil and will be used for request cancellation. If
// the context is nil a panic will occur. In the future the SDK may create
// sub-contexts for http.Requests. See https://golang.org/pkg/context/
// for more information on using Contexts.
func (c *AwsEndpointDiscoveryTest) TestDiscoveryRequiredWithContext(ctx aws.Context, input *TestDiscoveryRequiredInput, opts ...request.Option) (*TestDiscoveryRequiredOutput, error) {
req, out := c.TestDiscoveryRequiredRequest(input)
req.SetContext(ctx)
req.ApplyOptions(opts...)
return out, req.Send()
}
type DescribeEndpointsInput struct {
_ struct{} `type:"structure"`
Operation *string `type:"string"`
}
// String returns the string representation
func (s DescribeEndpointsInput) String() string {
return awsutil.Prettify(s)
}
// GoString returns the string representation
func (s DescribeEndpointsInput) GoString() string {
return s.String()
}
// SetOperation sets the Operation field's value.
func (s *DescribeEndpointsInput) SetOperation(v string) *DescribeEndpointsInput {
s.Operation = &v
return s
}
type DescribeEndpointsOutput struct {
_ struct{} `type:"structure"`
// Endpoints is a required field
Endpoints []*Endpoint `type:"list" required:"true"`
}
// String returns the string representation
func (s DescribeEndpointsOutput) String() string {
return awsutil.Prettify(s)
}
// GoString returns the string representation
func (s DescribeEndpointsOutput) GoString() string {
return s.String()
}
// SetEndpoints sets the Endpoints field's value.
func (s *DescribeEndpointsOutput) SetEndpoints(v []*Endpoint) *DescribeEndpointsOutput {
s.Endpoints = v
return s
}
type Endpoint struct {
_ struct{} `type:"structure"`
// Address is a required field
Address *string `type:"string" required:"true"`
// CachePeriodInMinutes is a required field
CachePeriodInMinutes *int64 `type:"long" required:"true"`
}
// String returns the string representation
func (s Endpoint) String() string {
return awsutil.Prettify(s)
}
// GoString returns the string representation
func (s Endpoint) GoString() string {
return s.String()
}
// SetAddress sets the Address field's value.
func (s *Endpoint) SetAddress(v string) *Endpoint {
s.Address = &v
return s
}
// SetCachePeriodInMinutes sets the CachePeriodInMinutes field's value.
func (s *Endpoint) SetCachePeriodInMinutes(v int64) *Endpoint {
s.CachePeriodInMinutes = &v
return s
}
type TestDiscoveryIdentifiersRequiredInput struct {
_ struct{} `type:"structure"`
// Sdk is a required field
Sdk *string `type:"string" required:"true"`
}
// String returns the string representation
func (s TestDiscoveryIdentifiersRequiredInput) String() string {
return awsutil.Prettify(s)
}
// GoString returns the string representation
func (s TestDiscoveryIdentifiersRequiredInput) GoString() string {
return s.String()
}
// Validate inspects the fields of the type to determine if they are valid.
func (s *TestDiscoveryIdentifiersRequiredInput) Validate() error {
invalidParams := request.ErrInvalidParams{Context: "TestDiscoveryIdentifiersRequiredInput"}
if s.Sdk == nil {
invalidParams.Add(request.NewErrParamRequired("Sdk"))
}
if invalidParams.Len() > 0 {
return invalidParams
}
return nil
}
// SetSdk sets the Sdk field's value.
func (s *TestDiscoveryIdentifiersRequiredInput) SetSdk(v string) *TestDiscoveryIdentifiersRequiredInput {
s.Sdk = &v
return s
}
type TestDiscoveryIdentifiersRequiredOutput struct {
_ struct{} `type:"structure"`
RequestSuccessful *bool `type:"boolean"`
}
// String returns the string representation
func (s TestDiscoveryIdentifiersRequiredOutput) String() string {
return awsutil.Prettify(s)
}
// GoString returns the string representation
func (s TestDiscoveryIdentifiersRequiredOutput) GoString() string {
return s.String()
}
// SetRequestSuccessful sets the RequestSuccessful field's value.
func (s *TestDiscoveryIdentifiersRequiredOutput) SetRequestSuccessful(v bool) *TestDiscoveryIdentifiersRequiredOutput {
s.RequestSuccessful = &v
return s
}
type TestDiscoveryOptionalInput struct {
_ struct{} `type:"structure"`
Sdk *string `type:"string"`
}
// String returns the string representation
func (s TestDiscoveryOptionalInput) String() string {
return awsutil.Prettify(s)
}
// GoString returns the string representation
func (s TestDiscoveryOptionalInput) GoString() string {
return s.String()
}
// SetSdk sets the Sdk field's value.
func (s *TestDiscoveryOptionalInput) SetSdk(v string) *TestDiscoveryOptionalInput {
s.Sdk = &v
return s
}
type TestDiscoveryOptionalOutput struct {
_ struct{} `type:"structure"`
RequestSuccessful *bool `type:"boolean"`
}
// String returns the string representation
func (s TestDiscoveryOptionalOutput) String() string {
return awsutil.Prettify(s)
}
// GoString returns the string representation
func (s TestDiscoveryOptionalOutput) GoString() string {
return s.String()
}
// SetRequestSuccessful sets the RequestSuccessful field's value.
func (s *TestDiscoveryOptionalOutput) SetRequestSuccessful(v bool) *TestDiscoveryOptionalOutput {
s.RequestSuccessful = &v
return s
}
type TestDiscoveryRequiredInput struct {
_ struct{} `type:"structure"`
Sdk *string `type:"string"`
}
// String returns the string representation
func (s TestDiscoveryRequiredInput) String() string {
return awsutil.Prettify(s)
}
// GoString returns the string representation
func (s TestDiscoveryRequiredInput) GoString() string {
return s.String()
}
// SetSdk sets the Sdk field's value.
func (s *TestDiscoveryRequiredInput) SetSdk(v string) *TestDiscoveryRequiredInput {
s.Sdk = &v
return s
}
type TestDiscoveryRequiredOutput struct {
_ struct{} `type:"structure"`
RequestSuccessful *bool `type:"boolean"`
}
// String returns the string representation
func (s TestDiscoveryRequiredOutput) String() string {
return awsutil.Prettify(s)
}
// GoString returns the string representation
func (s TestDiscoveryRequiredOutput) GoString() string {
return s.String()
}
// SetRequestSuccessful sets the RequestSuccessful field's value.
func (s *TestDiscoveryRequiredOutput) SetRequestSuccessful(v bool) *TestDiscoveryRequiredOutput {
s.RequestSuccessful = &v
return s
}
@@ -0,0 +1,80 @@
// Code generated by private/model/cli/gen-api/main.go. DO NOT EDIT.
// Package awsendpointdiscoverytestiface provides an interface to enable mocking the AwsEndpointDiscoveryTest service client
// for testing your code.
//
// It is important to note that this interface will have breaking changes
// when the service model is updated and adds new API operations, paginators,
// and waiters.
package awsendpointdiscoverytestiface
import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/private/model/api/codegentest/service/awsendpointdiscoverytest"
)
// AwsEndpointDiscoveryTestAPI provides an interface to enable mocking the
// awsendpointdiscoverytest.AwsEndpointDiscoveryTest service client's API operation,
// paginators, and waiters. This make unit testing your code that calls out
// to the SDK's service client's calls easier.
//
// The best way to use this interface is so the SDK's service client's calls
// can be stubbed out for unit testing your code with the SDK without needing
// to inject custom request handlers into the SDK's request pipeline.
//
// // myFunc uses an SDK service client to make a request to
// // AwsEndpointDiscoveryTest.
// func myFunc(svc awsendpointdiscoverytestiface.AwsEndpointDiscoveryTestAPI) bool {
// // Make svc.DescribeEndpoints request
// }
//
// func main() {
// sess := session.New()
// svc := awsendpointdiscoverytest.New(sess)
//
// myFunc(svc)
// }
//
// In your _test.go file:
//
// // Define a mock struct to be used in your unit tests of myFunc.
// type mockAwsEndpointDiscoveryTestClient struct {
// awsendpointdiscoverytestiface.AwsEndpointDiscoveryTestAPI
// }
// func (m *mockAwsEndpointDiscoveryTestClient) DescribeEndpoints(input *awsendpointdiscoverytest.DescribeEndpointsInput) (*awsendpointdiscoverytest.DescribeEndpointsOutput, error) {
// // mock response/functionality
// }
//
// func TestMyFunc(t *testing.T) {
// // Setup Test
// mockSvc := &mockAwsEndpointDiscoveryTestClient{}
//
// myfunc(mockSvc)
//
// // Verify myFunc's functionality
// }
//
// It is important to note that this interface will have breaking changes
// when the service model is updated and adds new API operations, paginators,
// and waiters. Its suggested to use the pattern above for testing, or using
// tooling to generate mocks to satisfy the interfaces.
type AwsEndpointDiscoveryTestAPI interface {
DescribeEndpoints(*awsendpointdiscoverytest.DescribeEndpointsInput) (*awsendpointdiscoverytest.DescribeEndpointsOutput, error)
DescribeEndpointsWithContext(aws.Context, *awsendpointdiscoverytest.DescribeEndpointsInput, ...request.Option) (*awsendpointdiscoverytest.DescribeEndpointsOutput, error)
DescribeEndpointsRequest(*awsendpointdiscoverytest.DescribeEndpointsInput) (*request.Request, *awsendpointdiscoverytest.DescribeEndpointsOutput)
TestDiscoveryIdentifiersRequired(*awsendpointdiscoverytest.TestDiscoveryIdentifiersRequiredInput) (*awsendpointdiscoverytest.TestDiscoveryIdentifiersRequiredOutput, error)
TestDiscoveryIdentifiersRequiredWithContext(aws.Context, *awsendpointdiscoverytest.TestDiscoveryIdentifiersRequiredInput, ...request.Option) (*awsendpointdiscoverytest.TestDiscoveryIdentifiersRequiredOutput, error)
TestDiscoveryIdentifiersRequiredRequest(*awsendpointdiscoverytest.TestDiscoveryIdentifiersRequiredInput) (*request.Request, *awsendpointdiscoverytest.TestDiscoveryIdentifiersRequiredOutput)
TestDiscoveryOptional(*awsendpointdiscoverytest.TestDiscoveryOptionalInput) (*awsendpointdiscoverytest.TestDiscoveryOptionalOutput, error)
TestDiscoveryOptionalWithContext(aws.Context, *awsendpointdiscoverytest.TestDiscoveryOptionalInput, ...request.Option) (*awsendpointdiscoverytest.TestDiscoveryOptionalOutput, error)
TestDiscoveryOptionalRequest(*awsendpointdiscoverytest.TestDiscoveryOptionalInput) (*request.Request, *awsendpointdiscoverytest.TestDiscoveryOptionalOutput)
TestDiscoveryRequired(*awsendpointdiscoverytest.TestDiscoveryRequiredInput) (*awsendpointdiscoverytest.TestDiscoveryRequiredOutput, error)
TestDiscoveryRequiredWithContext(aws.Context, *awsendpointdiscoverytest.TestDiscoveryRequiredInput, ...request.Option) (*awsendpointdiscoverytest.TestDiscoveryRequiredOutput, error)
TestDiscoveryRequiredRequest(*awsendpointdiscoverytest.TestDiscoveryRequiredInput) (*request.Request, *awsendpointdiscoverytest.TestDiscoveryRequiredOutput)
}
var _ AwsEndpointDiscoveryTestAPI = (*awsendpointdiscoverytest.AwsEndpointDiscoveryTest)(nil)
@@ -0,0 +1,24 @@
// Code generated by private/model/cli/gen-api/main.go. DO NOT EDIT.
// Package awsendpointdiscoverytest provides the client and types for making API
// requests to AwsEndpointDiscoveryTest.
//
// See awsendpointdiscoverytest package documentation for more information.
// https://docs.aws.amazon.com/sdk-for-go/api/service/awsendpointdiscoverytest/
//
// Using the Client
//
// To contact AwsEndpointDiscoveryTest with the SDK use the New function to create
// a new service client. With that client you can make API requests to the service.
// These clients are safe to use concurrently.
//
// See the SDK's documentation for more information on how to use the SDK.
// https://docs.aws.amazon.com/sdk-for-go/api/
//
// See aws.Config documentation for more information on configuring SDK clients.
// https://docs.aws.amazon.com/sdk-for-go/api/aws/#Config
//
// See the AwsEndpointDiscoveryTest client AwsEndpointDiscoveryTest for more
// information on creating client for this service.
// https://docs.aws.amazon.com/sdk-for-go/api/service/awsendpointdiscoverytest/#New
package awsendpointdiscoverytest
@@ -0,0 +1,136 @@
package awsendpointdiscoverytest
import (
"sync"
"sync/atomic"
"testing"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/awstesting/unit"
)
func TestEndpointDiscovery(t *testing.T) {
svc := New(unit.Session, &aws.Config{
EnableEndpointDiscovery: aws.Bool(true),
})
svc.Handlers.Clear()
svc.Handlers.Send.PushBack(mockSendDescEndpoint)
var descCount int32
svc.Handlers.Complete.PushBack(func(r *request.Request) {
if r.Operation.Name != opDescribeEndpoints {
return
}
atomic.AddInt32(&descCount, 1)
})
for i := 0; i < 2; i++ {
req, _ := svc.TestDiscoveryIdentifiersRequiredRequest(
&TestDiscoveryIdentifiersRequiredInput{
Sdk: aws.String("sdk"),
},
)
req.Handlers.Send.PushBack(func(r *request.Request) {
if e, a := "http://foo", r.HTTPRequest.URL.String(); e != a {
t.Errorf("expected %q, but received %q", e, a)
}
})
if err := req.Send(); err != nil {
t.Fatal(err)
}
}
if e, a := int32(1), atomic.LoadInt32(&descCount); e != a {
t.Errorf("expect desc endpoint called %d, got %d", e, a)
}
}
func TestAsyncEndpointDiscovery(t *testing.T) {
t.Parallel()
svc := New(unit.Session, &aws.Config{
EnableEndpointDiscovery: aws.Bool(true),
})
svc.Handlers.Clear()
var firstAsyncReq sync.WaitGroup
firstAsyncReq.Add(1)
svc.Handlers.Build.PushBack(func(r *request.Request) {
if r.Operation.Name == opDescribeEndpoints {
firstAsyncReq.Wait()
}
})
svc.Handlers.Send.PushBack(mockSendDescEndpoint)
req, _ := svc.TestDiscoveryOptionalRequest(&TestDiscoveryOptionalInput{
Sdk: aws.String("sdk"),
})
const clientHost = "awsendpointdiscoverytestservice.mock-region.amazonaws.com"
req.Handlers.Send.PushBack(func(r *request.Request) {
if e, a := clientHost, r.HTTPRequest.URL.Host; e != a {
t.Errorf("expected %q, but received %q", e, a)
}
})
req.Handlers.Complete.PushBack(func(r *request.Request) {
firstAsyncReq.Done()
})
if err := req.Send(); err != nil {
t.Fatal(err)
}
var cacheUpdated bool
for s := time.Now().Add(10 * time.Second); s.After(time.Now()); {
// Wait for the cache to be updated before making second request.
if svc.endpointCache.Has(req.Operation.Name) {
cacheUpdated = true
break
}
time.Sleep(10 * time.Millisecond)
}
if !cacheUpdated {
t.Fatalf("expect endpoint cache to be updated, was not")
}
req, _ = svc.TestDiscoveryOptionalRequest(&TestDiscoveryOptionalInput{
Sdk: aws.String("sdk"),
})
req.Handlers.Send.PushBack(func(r *request.Request) {
if e, a := "http://foo", r.HTTPRequest.URL.String(); e != a {
t.Errorf("expected %q, but received %q", e, a)
}
})
if err := req.Send(); err != nil {
t.Fatal(err)
}
}
func removeHandlers(h request.Handlers, removeSendHandlers bool) request.Handlers {
if removeSendHandlers {
h.Send.Clear()
}
h.Unmarshal.Clear()
h.UnmarshalStream.Clear()
h.UnmarshalMeta.Clear()
h.UnmarshalError.Clear()
h.Validate.Clear()
h.Complete.Clear()
h.ValidateResponse.Clear()
return h
}
func mockSendDescEndpoint(r *request.Request) {
if r.Operation.Name != opDescribeEndpoints {
return
}
out, _ := r.Data.(*DescribeEndpointsOutput)
out.Endpoints = []*Endpoint{
{
Address: aws.String("http://foo"),
CachePeriodInMinutes: aws.Int64(5),
},
}
r.Data = out
}
@@ -0,0 +1,3 @@
// Code generated by private/model/cli/gen-api/main.go. DO NOT EDIT.
package awsendpointdiscoverytest
@@ -0,0 +1,103 @@
// Code generated by private/model/cli/gen-api/main.go. DO NOT EDIT.
package awsendpointdiscoverytest
import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/client"
"github.com/aws/aws-sdk-go/aws/client/metadata"
"github.com/aws/aws-sdk-go/aws/crr"
"github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/aws/signer/v4"
"github.com/aws/aws-sdk-go/private/protocol/jsonrpc"
)
// AwsEndpointDiscoveryTest provides the API operation methods for making requests to
// AwsEndpointDiscoveryTest. See this package's package overview docs
// for details on the service.
//
// AwsEndpointDiscoveryTest methods are safe to use concurrently. It is not safe to
// modify mutate any of the struct's properties though.
type AwsEndpointDiscoveryTest struct {
*client.Client
endpointCache *crr.EndpointCache
}
// Used for custom client initialization logic
var initClient func(*client.Client)
// Used for custom request initialization logic
var initRequest func(*request.Request)
// Service information constants
const (
ServiceName = "AwsEndpointDiscoveryTest" // Name of service.
EndpointsID = "awsendpointdiscoverytestservice" // ID to lookup a service endpoint with.
ServiceID = "AwsEndpointDiscoveryTest" // ServiceID is a unique identifer of a specific service.
)
// New creates a new instance of the AwsEndpointDiscoveryTest client with a session.
// If additional configuration is needed for the client instance use the optional
// aws.Config parameter to add your extra config.
//
// Example:
// // Create a AwsEndpointDiscoveryTest client from just a session.
// svc := awsendpointdiscoverytest.New(mySession)
//
// // Create a AwsEndpointDiscoveryTest client with additional configuration
// svc := awsendpointdiscoverytest.New(mySession, aws.NewConfig().WithRegion("us-west-2"))
func New(p client.ConfigProvider, cfgs ...*aws.Config) *AwsEndpointDiscoveryTest {
c := p.ClientConfig(EndpointsID, cfgs...)
if c.SigningNameDerived || len(c.SigningName) == 0 {
c.SigningName = "awsendpointdiscoverytestservice"
}
return newClient(*c.Config, c.Handlers, c.Endpoint, c.SigningRegion, c.SigningName)
}
// newClient creates, initializes and returns a new service client instance.
func newClient(cfg aws.Config, handlers request.Handlers, endpoint, signingRegion, signingName string) *AwsEndpointDiscoveryTest {
svc := &AwsEndpointDiscoveryTest{
Client: client.New(
cfg,
metadata.ClientInfo{
ServiceName: ServiceName,
ServiceID: ServiceID,
SigningName: signingName,
SigningRegion: signingRegion,
Endpoint: endpoint,
APIVersion: "2018-08-31",
JSONVersion: "1.1",
TargetPrefix: "AwsEndpointDiscoveryTestService",
},
handlers,
),
}
svc.endpointCache = crr.NewEndpointCache(10)
// Handlers
svc.Handlers.Sign.PushBackNamed(v4.SignRequestHandler)
svc.Handlers.Build.PushBackNamed(jsonrpc.BuildHandler)
svc.Handlers.Unmarshal.PushBackNamed(jsonrpc.UnmarshalHandler)
svc.Handlers.UnmarshalMeta.PushBackNamed(jsonrpc.UnmarshalMetaHandler)
svc.Handlers.UnmarshalError.PushBackNamed(jsonrpc.UnmarshalErrorHandler)
// Run custom client initialization if present
if initClient != nil {
initClient(svc.Client)
}
return svc
}
// newRequest creates a new request for a AwsEndpointDiscoveryTest operation and runs any
// custom request initialization.
func (c *AwsEndpointDiscoveryTest) newRequest(op *request.Operation, params, data interface{}) *request.Request {
req := c.NewRequest(op, params, data)
// Run custom request initialization if present
if initRequest != nil {
initRequest(req)
}
return req
}
@@ -0,0 +1,5 @@
// Package service contains automatically generated AWS clients.
package service
//go:generate go run -tags codegen ../../../cli/gen-api/main.go -path=../service -svc-import-path "github.com/aws/aws-sdk-go/private/model/api/codegentest/service" ../models/*/*/api-2.json
//go:generate gofmt -s -w ../service
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,26 @@
// Code generated by private/model/cli/gen-api/main.go. DO NOT EDIT.
// Package restjsonservice provides the client and types for making API
// requests to REST JSON Service.
//
// See https://docs.aws.amazon.com/goto/WebAPI/RESTJSONService-0000-00-00 for more information on this service.
//
// See restjsonservice package documentation for more information.
// https://docs.aws.amazon.com/sdk-for-go/api/service/restjsonservice/
//
// Using the Client
//
// To contact REST JSON Service with the SDK use the New function to create
// a new service client. With that client you can make API requests to the service.
// These clients are safe to use concurrently.
//
// See the SDK's documentation for more information on how to use the SDK.
// https://docs.aws.amazon.com/sdk-for-go/api/
//
// See aws.Config documentation for more information on configuring SDK clients.
// https://docs.aws.amazon.com/sdk-for-go/api/aws/#Config
//
// See the REST JSON Service client RESTJSONService for more
// information on creating client for this service.
// https://docs.aws.amazon.com/sdk-for-go/api/service/restjsonservice/#New
package restjsonservice
@@ -0,0 +1,14 @@
// Code generated by private/model/cli/gen-api/main.go. DO NOT EDIT.
package restjsonservice
const (
// ErrCodeExceptionEvent for service response error code
// "ExceptionEvent".
ErrCodeExceptionEvent = "ExceptionEvent"
// ErrCodeExceptionEvent2 for service response error code
// "ExceptionEvent2".
ErrCodeExceptionEvent2 = "ExceptionEvent2"
)
@@ -0,0 +1,526 @@
// Code generated by private/model/cli/gen-api/main.go. DO NOT EDIT.
// +build go1.6
package restjsonservice
import (
"bytes"
"io/ioutil"
"net/http"
"reflect"
"testing"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/corehandlers"
"github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/awstesting/unit"
"github.com/aws/aws-sdk-go/private/protocol"
"github.com/aws/aws-sdk-go/private/protocol/eventstream"
"github.com/aws/aws-sdk-go/private/protocol/eventstream/eventstreamapi"
"github.com/aws/aws-sdk-go/private/protocol/eventstream/eventstreamtest"
"github.com/aws/aws-sdk-go/private/protocol/restjson"
)
var _ time.Time
var _ awserr.Error
func TestEmptyStream_Read(t *testing.T) {
expectEvents, eventMsgs := mockEmptyStreamReadEvents()
sess, cleanupFn, err := eventstreamtest.SetupEventStreamSession(t,
eventstreamtest.ServeEventStream{
T: t,
Events: eventMsgs,
},
true,
)
if err != nil {
t.Fatalf("expect no error, %v", err)
}
defer cleanupFn()
svc := New(sess)
resp, err := svc.EmptyStream(nil)
if err != nil {
t.Fatalf("expect no error got, %v", err)
}
defer resp.EventStream.Close()
var i int
for event := range resp.EventStream.Events() {
if event == nil {
t.Errorf("%d, expect event, got nil", i)
}
if e, a := expectEvents[i], event; !reflect.DeepEqual(e, a) {
t.Errorf("%d, expect %T %v, got %T %v", i, e, e, a, a)
}
i++
}
if err := resp.EventStream.Err(); err != nil {
t.Errorf("expect no error, %v", err)
}
}
func TestEmptyStream_ReadClose(t *testing.T) {
_, eventMsgs := mockEmptyStreamReadEvents()
sess, cleanupFn, err := eventstreamtest.SetupEventStreamSession(t,
eventstreamtest.ServeEventStream{
T: t,
Events: eventMsgs,
},
true,
)
if err != nil {
t.Fatalf("expect no error, %v", err)
}
defer cleanupFn()
svc := New(sess)
resp, err := svc.EmptyStream(nil)
if err != nil {
t.Fatalf("expect no error got, %v", err)
}
resp.EventStream.Close()
<-resp.EventStream.Events()
if err := resp.EventStream.Err(); err != nil {
t.Errorf("expect no error, %v", err)
}
}
func BenchmarkEmptyStream_Read(b *testing.B) {
_, eventMsgs := mockEmptyStreamReadEvents()
var buf bytes.Buffer
encoder := eventstream.NewEncoder(&buf)
for _, msg := range eventMsgs {
if err := encoder.Encode(msg); err != nil {
b.Fatalf("failed to encode message, %v", err)
}
}
stream := &loopReader{source: bytes.NewReader(buf.Bytes())}
sess := unit.Session
svc := New(sess, &aws.Config{
Endpoint: aws.String("https://example.com"),
DisableParamValidation: aws.Bool(true),
})
svc.Handlers.Send.Swap(corehandlers.SendHandler.Name,
request.NamedHandler{Name: "mockSend",
Fn: func(r *request.Request) {
r.HTTPResponse = &http.Response{
Status: "200 OK",
StatusCode: 200,
Header: http.Header{},
Body: ioutil.NopCloser(stream),
}
},
},
)
resp, err := svc.EmptyStream(nil)
if err != nil {
b.Fatalf("failed to create request, %v", err)
}
defer resp.EventStream.Close()
b.ResetTimer()
for i := 0; i < b.N; i++ {
if err = resp.EventStream.Err(); err != nil {
b.Fatalf("expect no error, got %v", err)
}
event := <-resp.EventStream.Events()
if event == nil {
b.Fatalf("expect event, got nil, %v, %d", resp.EventStream.Err(), i)
}
}
}
func mockEmptyStreamReadEvents() (
[]EmptyEventStreamEvent,
[]eventstream.Message,
) {
expectEvents := []EmptyEventStreamEvent{}
var marshalers request.HandlerList
marshalers.PushBackNamed(restjson.BuildHandler)
payloadMarshaler := protocol.HandlerPayloadMarshal{
Marshalers: marshalers,
}
_ = payloadMarshaler
eventMsgs := []eventstream.Message{}
return expectEvents, eventMsgs
}
func TestGetEventStream_Read(t *testing.T) {
expectEvents, eventMsgs := mockGetEventStreamReadEvents()
sess, cleanupFn, err := eventstreamtest.SetupEventStreamSession(t,
eventstreamtest.ServeEventStream{
T: t,
Events: eventMsgs,
},
true,
)
if err != nil {
t.Fatalf("expect no error, %v", err)
}
defer cleanupFn()
svc := New(sess)
resp, err := svc.GetEventStream(nil)
if err != nil {
t.Fatalf("expect no error got, %v", err)
}
defer resp.EventStream.Close()
var i int
for event := range resp.EventStream.Events() {
if event == nil {
t.Errorf("%d, expect event, got nil", i)
}
if e, a := expectEvents[i], event; !reflect.DeepEqual(e, a) {
t.Errorf("%d, expect %T %v, got %T %v", i, e, e, a, a)
}
i++
}
if err := resp.EventStream.Err(); err != nil {
t.Errorf("expect no error, %v", err)
}
}
func TestGetEventStream_ReadClose(t *testing.T) {
_, eventMsgs := mockGetEventStreamReadEvents()
sess, cleanupFn, err := eventstreamtest.SetupEventStreamSession(t,
eventstreamtest.ServeEventStream{
T: t,
Events: eventMsgs,
},
true,
)
if err != nil {
t.Fatalf("expect no error, %v", err)
}
defer cleanupFn()
svc := New(sess)
resp, err := svc.GetEventStream(nil)
if err != nil {
t.Fatalf("expect no error got, %v", err)
}
resp.EventStream.Close()
<-resp.EventStream.Events()
if err := resp.EventStream.Err(); err != nil {
t.Errorf("expect no error, %v", err)
}
}
func BenchmarkGetEventStream_Read(b *testing.B) {
_, eventMsgs := mockGetEventStreamReadEvents()
var buf bytes.Buffer
encoder := eventstream.NewEncoder(&buf)
for _, msg := range eventMsgs {
if err := encoder.Encode(msg); err != nil {
b.Fatalf("failed to encode message, %v", err)
}
}
stream := &loopReader{source: bytes.NewReader(buf.Bytes())}
sess := unit.Session
svc := New(sess, &aws.Config{
Endpoint: aws.String("https://example.com"),
DisableParamValidation: aws.Bool(true),
})
svc.Handlers.Send.Swap(corehandlers.SendHandler.Name,
request.NamedHandler{Name: "mockSend",
Fn: func(r *request.Request) {
r.HTTPResponse = &http.Response{
Status: "200 OK",
StatusCode: 200,
Header: http.Header{},
Body: ioutil.NopCloser(stream),
}
},
},
)
resp, err := svc.GetEventStream(nil)
if err != nil {
b.Fatalf("failed to create request, %v", err)
}
defer resp.EventStream.Close()
b.ResetTimer()
for i := 0; i < b.N; i++ {
if err = resp.EventStream.Err(); err != nil {
b.Fatalf("expect no error, got %v", err)
}
event := <-resp.EventStream.Events()
if event == nil {
b.Fatalf("expect event, got nil, %v, %d", resp.EventStream.Err(), i)
}
}
}
func mockGetEventStreamReadEvents() (
[]EventStreamEvent,
[]eventstream.Message,
) {
expectEvents := []EventStreamEvent{
&EmptyEvent{},
&ExplicitPayloadEvent{
LongVal: aws.Int64(1234),
NestedVal: &NestedShape{
IntVal: aws.Int64(123),
StrVal: aws.String("string value goes here"),
},
StringVal: aws.String("string value goes here"),
},
&HeaderOnlyEvent{
BlobVal: []byte("blob value goes here"),
BoolVal: aws.Bool(true),
ByteVal: aws.Int64(1),
IntegerVal: aws.Int64(123),
LongVal: aws.Int64(1234),
ShortVal: aws.Int64(12),
StringVal: aws.String("string value goes here"),
TimeVal: aws.Time(time.Unix(1396594860, 0).UTC()),
},
&ImplicitPayloadEvent{
ByteVal: aws.Int64(1),
IntegerVal: aws.Int64(123),
ShortVal: aws.Int64(12),
},
&PayloadOnlyEvent{
NestedVal: &NestedShape{
IntVal: aws.Int64(123),
StrVal: aws.String("string value goes here"),
},
},
&PayloadOnlyBlobEvent{
BlobPayload: []byte("blob value goes here"),
},
&PayloadOnlyStringEvent{
StringPayload: aws.String("string value goes here"),
},
}
var marshalers request.HandlerList
marshalers.PushBackNamed(restjson.BuildHandler)
payloadMarshaler := protocol.HandlerPayloadMarshal{
Marshalers: marshalers,
}
_ = payloadMarshaler
eventMsgs := []eventstream.Message{
{
Headers: eventstream.Headers{
eventstreamtest.EventMessageTypeHeader,
{
Name: eventstreamapi.EventTypeHeader,
Value: eventstream.StringValue("Empty"),
},
},
},
{
Headers: eventstream.Headers{
eventstreamtest.EventMessageTypeHeader,
{
Name: eventstreamapi.EventTypeHeader,
Value: eventstream.StringValue("ExplicitPayload"),
},
{
Name: "LongVal",
Value: eventstream.Int64Value(*expectEvents[1].(*ExplicitPayloadEvent).LongVal),
},
{
Name: "StringVal",
Value: eventstream.StringValue(*expectEvents[1].(*ExplicitPayloadEvent).StringVal),
},
},
Payload: eventstreamtest.MarshalEventPayload(payloadMarshaler, expectEvents[1]),
},
{
Headers: eventstream.Headers{
eventstreamtest.EventMessageTypeHeader,
{
Name: eventstreamapi.EventTypeHeader,
Value: eventstream.StringValue("Headers"),
},
{
Name: "BlobVal",
Value: eventstream.BytesValue(expectEvents[2].(*HeaderOnlyEvent).BlobVal),
},
{
Name: "BoolVal",
Value: eventstream.BoolValue(*expectEvents[2].(*HeaderOnlyEvent).BoolVal),
},
{
Name: "ByteVal",
Value: eventstream.Int8Value(int8(*expectEvents[2].(*HeaderOnlyEvent).ByteVal)),
},
{
Name: "IntegerVal",
Value: eventstream.Int32Value(int32(*expectEvents[2].(*HeaderOnlyEvent).IntegerVal)),
},
{
Name: "LongVal",
Value: eventstream.Int64Value(*expectEvents[2].(*HeaderOnlyEvent).LongVal),
},
{
Name: "ShortVal",
Value: eventstream.Int16Value(int16(*expectEvents[2].(*HeaderOnlyEvent).ShortVal)),
},
{
Name: "StringVal",
Value: eventstream.StringValue(*expectEvents[2].(*HeaderOnlyEvent).StringVal),
},
{
Name: "TimeVal",
Value: eventstream.TimestampValue(*expectEvents[2].(*HeaderOnlyEvent).TimeVal),
},
},
},
{
Headers: eventstream.Headers{
eventstreamtest.EventMessageTypeHeader,
{
Name: eventstreamapi.EventTypeHeader,
Value: eventstream.StringValue("ImplicitPayload"),
},
{
Name: "ByteVal",
Value: eventstream.Int8Value(int8(*expectEvents[3].(*ImplicitPayloadEvent).ByteVal)),
},
},
Payload: eventstreamtest.MarshalEventPayload(payloadMarshaler, expectEvents[3]),
},
{
Headers: eventstream.Headers{
eventstreamtest.EventMessageTypeHeader,
{
Name: eventstreamapi.EventTypeHeader,
Value: eventstream.StringValue("PayloadOnly"),
},
},
Payload: eventstreamtest.MarshalEventPayload(payloadMarshaler, expectEvents[4]),
},
{
Headers: eventstream.Headers{
eventstreamtest.EventMessageTypeHeader,
{
Name: eventstreamapi.EventTypeHeader,
Value: eventstream.StringValue("PayloadOnlyBlob"),
},
},
Payload: expectEvents[5].(*PayloadOnlyBlobEvent).BlobPayload,
},
{
Headers: eventstream.Headers{
eventstreamtest.EventMessageTypeHeader,
{
Name: eventstreamapi.EventTypeHeader,
Value: eventstream.StringValue("PayloadOnlyString"),
},
},
Payload: []byte(*expectEvents[6].(*PayloadOnlyStringEvent).StringPayload),
},
}
return expectEvents, eventMsgs
}
func TestGetEventStream_ReadException(t *testing.T) {
expectEvents := []EventStreamEvent{
&ExceptionEvent{
IntVal: aws.Int64(123),
Message_: aws.String("string value goes here"),
},
}
var marshalers request.HandlerList
marshalers.PushBackNamed(restjson.BuildHandler)
payloadMarshaler := protocol.HandlerPayloadMarshal{
Marshalers: marshalers,
}
eventMsgs := []eventstream.Message{
{
Headers: eventstream.Headers{
eventstreamtest.EventExceptionTypeHeader,
{
Name: eventstreamapi.ExceptionTypeHeader,
Value: eventstream.StringValue("Exception"),
},
},
Payload: eventstreamtest.MarshalEventPayload(payloadMarshaler, expectEvents[0]),
},
}
sess, cleanupFn, err := eventstreamtest.SetupEventStreamSession(t,
eventstreamtest.ServeEventStream{
T: t,
Events: eventMsgs,
},
true,
)
if err != nil {
t.Fatalf("expect no error, %v", err)
}
defer cleanupFn()
svc := New(sess)
resp, err := svc.GetEventStream(nil)
if err != nil {
t.Fatalf("expect no error got, %v", err)
}
defer resp.EventStream.Close()
<-resp.EventStream.Events()
err = resp.EventStream.Err()
if err == nil {
t.Fatalf("expect err, got none")
}
expectErr := &ExceptionEvent{
IntVal: aws.Int64(123),
Message_: aws.String("string value goes here"),
}
aerr, ok := err.(awserr.Error)
if !ok {
t.Errorf("expect exception, got %T, %#v", err, err)
}
if e, a := expectErr.Code(), aerr.Code(); e != a {
t.Errorf("expect %v, got %v", e, a)
}
if e, a := expectErr.Message(), aerr.Message(); e != a {
t.Errorf("expect %v, got %v", e, a)
}
if e, a := expectErr, aerr; !reflect.DeepEqual(e, a) {
t.Errorf("expect %#v, got %#v", e, a)
}
}
var _ awserr.Error = (*ExceptionEvent)(nil)
var _ awserr.Error = (*ExceptionEvent2)(nil)
type loopReader struct {
source *bytes.Reader
}
func (c *loopReader) Read(p []byte) (int, error) {
if c.source.Len() == 0 {
c.source.Seek(0, 0)
}
return c.source.Read(p)
}
@@ -0,0 +1,76 @@
// Code generated by private/model/cli/gen-api/main.go. DO NOT EDIT.
// Package restjsonserviceiface provides an interface to enable mocking the REST JSON Service service client
// for testing your code.
//
// It is important to note that this interface will have breaking changes
// when the service model is updated and adds new API operations, paginators,
// and waiters.
package restjsonserviceiface
import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/private/model/api/codegentest/service/restjsonservice"
)
// RESTJSONServiceAPI provides an interface to enable mocking the
// restjsonservice.RESTJSONService service client's API operation,
// paginators, and waiters. This make unit testing your code that calls out
// to the SDK's service client's calls easier.
//
// The best way to use this interface is so the SDK's service client's calls
// can be stubbed out for unit testing your code with the SDK without needing
// to inject custom request handlers into the SDK's request pipeline.
//
// // myFunc uses an SDK service client to make a request to
// // REST JSON Service.
// func myFunc(svc restjsonserviceiface.RESTJSONServiceAPI) bool {
// // Make svc.EmptyStream request
// }
//
// func main() {
// sess := session.New()
// svc := restjsonservice.New(sess)
//
// myFunc(svc)
// }
//
// In your _test.go file:
//
// // Define a mock struct to be used in your unit tests of myFunc.
// type mockRESTJSONServiceClient struct {
// restjsonserviceiface.RESTJSONServiceAPI
// }
// func (m *mockRESTJSONServiceClient) EmptyStream(input *restjsonservice.EmptyStreamInput) (*restjsonservice.EmptyStreamOutput, error) {
// // mock response/functionality
// }
//
// func TestMyFunc(t *testing.T) {
// // Setup Test
// mockSvc := &mockRESTJSONServiceClient{}
//
// myfunc(mockSvc)
//
// // Verify myFunc's functionality
// }
//
// It is important to note that this interface will have breaking changes
// when the service model is updated and adds new API operations, paginators,
// and waiters. Its suggested to use the pattern above for testing, or using
// tooling to generate mocks to satisfy the interfaces.
type RESTJSONServiceAPI interface {
EmptyStream(*restjsonservice.EmptyStreamInput) (*restjsonservice.EmptyStreamOutput, error)
EmptyStreamWithContext(aws.Context, *restjsonservice.EmptyStreamInput, ...request.Option) (*restjsonservice.EmptyStreamOutput, error)
EmptyStreamRequest(*restjsonservice.EmptyStreamInput) (*request.Request, *restjsonservice.EmptyStreamOutput)
GetEventStream(*restjsonservice.GetEventStreamInput) (*restjsonservice.GetEventStreamOutput, error)
GetEventStreamWithContext(aws.Context, *restjsonservice.GetEventStreamInput, ...request.Option) (*restjsonservice.GetEventStreamOutput, error)
GetEventStreamRequest(*restjsonservice.GetEventStreamInput) (*request.Request, *restjsonservice.GetEventStreamOutput)
OtherOperation(*restjsonservice.OtherOperationInput) (*restjsonservice.OtherOperationOutput, error)
OtherOperationWithContext(aws.Context, *restjsonservice.OtherOperationInput, ...request.Option) (*restjsonservice.OtherOperationOutput, error)
OtherOperationRequest(*restjsonservice.OtherOperationInput) (*request.Request, *restjsonservice.OtherOperationOutput)
}
var _ RESTJSONServiceAPI = (*restjsonservice.RESTJSONService)(nil)
@@ -0,0 +1,97 @@
// Code generated by private/model/cli/gen-api/main.go. DO NOT EDIT.
package restjsonservice
import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/client"
"github.com/aws/aws-sdk-go/aws/client/metadata"
"github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/aws/signer/v4"
"github.com/aws/aws-sdk-go/private/protocol/restjson"
)
// RESTJSONService provides the API operation methods for making requests to
// REST JSON Service. See this package's package overview docs
// for details on the service.
//
// RESTJSONService methods are safe to use concurrently. It is not safe to
// modify mutate any of the struct's properties though.
type RESTJSONService struct {
*client.Client
}
// Used for custom client initialization logic
var initClient func(*client.Client)
// Used for custom request initialization logic
var initRequest func(*request.Request)
// Service information constants
const (
ServiceName = "RESTJSONService" // Name of service.
EndpointsID = "restjsonservice" // ID to lookup a service endpoint with.
ServiceID = "RESTJSONService" // ServiceID is a unique identifer of a specific service.
)
// New creates a new instance of the RESTJSONService client with a session.
// If additional configuration is needed for the client instance use the optional
// aws.Config parameter to add your extra config.
//
// Example:
// // Create a RESTJSONService client from just a session.
// svc := restjsonservice.New(mySession)
//
// // Create a RESTJSONService client with additional configuration
// svc := restjsonservice.New(mySession, aws.NewConfig().WithRegion("us-west-2"))
func New(p client.ConfigProvider, cfgs ...*aws.Config) *RESTJSONService {
c := p.ClientConfig(EndpointsID, cfgs...)
return newClient(*c.Config, c.Handlers, c.Endpoint, c.SigningRegion, c.SigningName)
}
// newClient creates, initializes and returns a new service client instance.
func newClient(cfg aws.Config, handlers request.Handlers, endpoint, signingRegion, signingName string) *RESTJSONService {
svc := &RESTJSONService{
Client: client.New(
cfg,
metadata.ClientInfo{
ServiceName: ServiceName,
ServiceID: ServiceID,
SigningName: signingName,
SigningRegion: signingRegion,
Endpoint: endpoint,
APIVersion: "0000-00-00",
},
handlers,
),
}
// Handlers
svc.Handlers.Sign.PushBackNamed(v4.SignRequestHandler)
svc.Handlers.Build.PushBackNamed(restjson.BuildHandler)
svc.Handlers.Unmarshal.PushBackNamed(restjson.UnmarshalHandler)
svc.Handlers.UnmarshalMeta.PushBackNamed(restjson.UnmarshalMetaHandler)
svc.Handlers.UnmarshalError.PushBackNamed(restjson.UnmarshalErrorHandler)
svc.Handlers.UnmarshalStream.PushBackNamed(restjson.UnmarshalHandler)
// Run custom client initialization if present
if initClient != nil {
initClient(svc.Client)
}
return svc
}
// newRequest creates a new request for a RESTJSONService operation and runs any
// custom request initialization.
func (c *RESTJSONService) newRequest(op *request.Operation, params, data interface{}) *request.Request {
req := c.NewRequest(op, params, data)
// Run custom request initialization if present
if initRequest != nil {
initRequest(req)
}
return req
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,26 @@
// Code generated by private/model/cli/gen-api/main.go. DO NOT EDIT.
// Package restxmlservice provides the client and types for making API
// requests to REST XML Service.
//
// See https://docs.aws.amazon.com/goto/WebAPI/RESTXMLService-0000-00-00 for more information on this service.
//
// See restxmlservice package documentation for more information.
// https://docs.aws.amazon.com/sdk-for-go/api/service/restxmlservice/
//
// Using the Client
//
// To contact REST XML Service with the SDK use the New function to create
// a new service client. With that client you can make API requests to the service.
// These clients are safe to use concurrently.
//
// See the SDK's documentation for more information on how to use the SDK.
// https://docs.aws.amazon.com/sdk-for-go/api/
//
// See aws.Config documentation for more information on configuring SDK clients.
// https://docs.aws.amazon.com/sdk-for-go/api/aws/#Config
//
// See the REST XML Service client RESTXMLService for more
// information on creating client for this service.
// https://docs.aws.amazon.com/sdk-for-go/api/service/restxmlservice/#New
package restxmlservice
@@ -0,0 +1,14 @@
// Code generated by private/model/cli/gen-api/main.go. DO NOT EDIT.
package restxmlservice
const (
// ErrCodeExceptionEvent for service response error code
// "ExceptionEvent".
ErrCodeExceptionEvent = "ExceptionEvent"
// ErrCodeExceptionEvent2 for service response error code
// "ExceptionEvent2".
ErrCodeExceptionEvent2 = "ExceptionEvent2"
)
@@ -0,0 +1,526 @@
// Code generated by private/model/cli/gen-api/main.go. DO NOT EDIT.
// +build go1.6
package restxmlservice
import (
"bytes"
"io/ioutil"
"net/http"
"reflect"
"testing"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/corehandlers"
"github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/awstesting/unit"
"github.com/aws/aws-sdk-go/private/protocol"
"github.com/aws/aws-sdk-go/private/protocol/eventstream"
"github.com/aws/aws-sdk-go/private/protocol/eventstream/eventstreamapi"
"github.com/aws/aws-sdk-go/private/protocol/eventstream/eventstreamtest"
"github.com/aws/aws-sdk-go/private/protocol/restxml"
)
var _ time.Time
var _ awserr.Error
func TestEmptyStream_Read(t *testing.T) {
expectEvents, eventMsgs := mockEmptyStreamReadEvents()
sess, cleanupFn, err := eventstreamtest.SetupEventStreamSession(t,
eventstreamtest.ServeEventStream{
T: t,
Events: eventMsgs,
},
true,
)
if err != nil {
t.Fatalf("expect no error, %v", err)
}
defer cleanupFn()
svc := New(sess)
resp, err := svc.EmptyStream(nil)
if err != nil {
t.Fatalf("expect no error got, %v", err)
}
defer resp.EventStream.Close()
var i int
for event := range resp.EventStream.Events() {
if event == nil {
t.Errorf("%d, expect event, got nil", i)
}
if e, a := expectEvents[i], event; !reflect.DeepEqual(e, a) {
t.Errorf("%d, expect %T %v, got %T %v", i, e, e, a, a)
}
i++
}
if err := resp.EventStream.Err(); err != nil {
t.Errorf("expect no error, %v", err)
}
}
func TestEmptyStream_ReadClose(t *testing.T) {
_, eventMsgs := mockEmptyStreamReadEvents()
sess, cleanupFn, err := eventstreamtest.SetupEventStreamSession(t,
eventstreamtest.ServeEventStream{
T: t,
Events: eventMsgs,
},
true,
)
if err != nil {
t.Fatalf("expect no error, %v", err)
}
defer cleanupFn()
svc := New(sess)
resp, err := svc.EmptyStream(nil)
if err != nil {
t.Fatalf("expect no error got, %v", err)
}
resp.EventStream.Close()
<-resp.EventStream.Events()
if err := resp.EventStream.Err(); err != nil {
t.Errorf("expect no error, %v", err)
}
}
func BenchmarkEmptyStream_Read(b *testing.B) {
_, eventMsgs := mockEmptyStreamReadEvents()
var buf bytes.Buffer
encoder := eventstream.NewEncoder(&buf)
for _, msg := range eventMsgs {
if err := encoder.Encode(msg); err != nil {
b.Fatalf("failed to encode message, %v", err)
}
}
stream := &loopReader{source: bytes.NewReader(buf.Bytes())}
sess := unit.Session
svc := New(sess, &aws.Config{
Endpoint: aws.String("https://example.com"),
DisableParamValidation: aws.Bool(true),
})
svc.Handlers.Send.Swap(corehandlers.SendHandler.Name,
request.NamedHandler{Name: "mockSend",
Fn: func(r *request.Request) {
r.HTTPResponse = &http.Response{
Status: "200 OK",
StatusCode: 200,
Header: http.Header{},
Body: ioutil.NopCloser(stream),
}
},
},
)
resp, err := svc.EmptyStream(nil)
if err != nil {
b.Fatalf("failed to create request, %v", err)
}
defer resp.EventStream.Close()
b.ResetTimer()
for i := 0; i < b.N; i++ {
if err = resp.EventStream.Err(); err != nil {
b.Fatalf("expect no error, got %v", err)
}
event := <-resp.EventStream.Events()
if event == nil {
b.Fatalf("expect event, got nil, %v, %d", resp.EventStream.Err(), i)
}
}
}
func mockEmptyStreamReadEvents() (
[]EmptyEventStreamEvent,
[]eventstream.Message,
) {
expectEvents := []EmptyEventStreamEvent{}
var marshalers request.HandlerList
marshalers.PushBackNamed(restxml.BuildHandler)
payloadMarshaler := protocol.HandlerPayloadMarshal{
Marshalers: marshalers,
}
_ = payloadMarshaler
eventMsgs := []eventstream.Message{}
return expectEvents, eventMsgs
}
func TestGetEventStream_Read(t *testing.T) {
expectEvents, eventMsgs := mockGetEventStreamReadEvents()
sess, cleanupFn, err := eventstreamtest.SetupEventStreamSession(t,
eventstreamtest.ServeEventStream{
T: t,
Events: eventMsgs,
},
true,
)
if err != nil {
t.Fatalf("expect no error, %v", err)
}
defer cleanupFn()
svc := New(sess)
resp, err := svc.GetEventStream(nil)
if err != nil {
t.Fatalf("expect no error got, %v", err)
}
defer resp.EventStream.Close()
var i int
for event := range resp.EventStream.Events() {
if event == nil {
t.Errorf("%d, expect event, got nil", i)
}
if e, a := expectEvents[i], event; !reflect.DeepEqual(e, a) {
t.Errorf("%d, expect %T %v, got %T %v", i, e, e, a, a)
}
i++
}
if err := resp.EventStream.Err(); err != nil {
t.Errorf("expect no error, %v", err)
}
}
func TestGetEventStream_ReadClose(t *testing.T) {
_, eventMsgs := mockGetEventStreamReadEvents()
sess, cleanupFn, err := eventstreamtest.SetupEventStreamSession(t,
eventstreamtest.ServeEventStream{
T: t,
Events: eventMsgs,
},
true,
)
if err != nil {
t.Fatalf("expect no error, %v", err)
}
defer cleanupFn()
svc := New(sess)
resp, err := svc.GetEventStream(nil)
if err != nil {
t.Fatalf("expect no error got, %v", err)
}
resp.EventStream.Close()
<-resp.EventStream.Events()
if err := resp.EventStream.Err(); err != nil {
t.Errorf("expect no error, %v", err)
}
}
func BenchmarkGetEventStream_Read(b *testing.B) {
_, eventMsgs := mockGetEventStreamReadEvents()
var buf bytes.Buffer
encoder := eventstream.NewEncoder(&buf)
for _, msg := range eventMsgs {
if err := encoder.Encode(msg); err != nil {
b.Fatalf("failed to encode message, %v", err)
}
}
stream := &loopReader{source: bytes.NewReader(buf.Bytes())}
sess := unit.Session
svc := New(sess, &aws.Config{
Endpoint: aws.String("https://example.com"),
DisableParamValidation: aws.Bool(true),
})
svc.Handlers.Send.Swap(corehandlers.SendHandler.Name,
request.NamedHandler{Name: "mockSend",
Fn: func(r *request.Request) {
r.HTTPResponse = &http.Response{
Status: "200 OK",
StatusCode: 200,
Header: http.Header{},
Body: ioutil.NopCloser(stream),
}
},
},
)
resp, err := svc.GetEventStream(nil)
if err != nil {
b.Fatalf("failed to create request, %v", err)
}
defer resp.EventStream.Close()
b.ResetTimer()
for i := 0; i < b.N; i++ {
if err = resp.EventStream.Err(); err != nil {
b.Fatalf("expect no error, got %v", err)
}
event := <-resp.EventStream.Events()
if event == nil {
b.Fatalf("expect event, got nil, %v, %d", resp.EventStream.Err(), i)
}
}
}
func mockGetEventStreamReadEvents() (
[]EventStreamEvent,
[]eventstream.Message,
) {
expectEvents := []EventStreamEvent{
&EmptyEvent{},
&ExplicitPayloadEvent{
LongVal: aws.Int64(1234),
NestedVal: &NestedShape{
IntVal: aws.Int64(123),
StrVal: aws.String("string value goes here"),
},
StringVal: aws.String("string value goes here"),
},
&HeaderOnlyEvent{
BlobVal: []byte("blob value goes here"),
BoolVal: aws.Bool(true),
ByteVal: aws.Int64(1),
IntegerVal: aws.Int64(123),
LongVal: aws.Int64(1234),
ShortVal: aws.Int64(12),
StringVal: aws.String("string value goes here"),
TimeVal: aws.Time(time.Unix(1396594860, 0).UTC()),
},
&ImplicitPayloadEvent{
ByteVal: aws.Int64(1),
IntegerVal: aws.Int64(123),
ShortVal: aws.Int64(12),
},
&PayloadOnlyEvent{
NestedVal: &NestedShape{
IntVal: aws.Int64(123),
StrVal: aws.String("string value goes here"),
},
},
&PayloadOnlyBlobEvent{
BlobPayload: []byte("blob value goes here"),
},
&PayloadOnlyStringEvent{
StringPayload: aws.String("string value goes here"),
},
}
var marshalers request.HandlerList
marshalers.PushBackNamed(restxml.BuildHandler)
payloadMarshaler := protocol.HandlerPayloadMarshal{
Marshalers: marshalers,
}
_ = payloadMarshaler
eventMsgs := []eventstream.Message{
{
Headers: eventstream.Headers{
eventstreamtest.EventMessageTypeHeader,
{
Name: eventstreamapi.EventTypeHeader,
Value: eventstream.StringValue("Empty"),
},
},
},
{
Headers: eventstream.Headers{
eventstreamtest.EventMessageTypeHeader,
{
Name: eventstreamapi.EventTypeHeader,
Value: eventstream.StringValue("ExplicitPayload"),
},
{
Name: "LongVal",
Value: eventstream.Int64Value(*expectEvents[1].(*ExplicitPayloadEvent).LongVal),
},
{
Name: "StringVal",
Value: eventstream.StringValue(*expectEvents[1].(*ExplicitPayloadEvent).StringVal),
},
},
Payload: eventstreamtest.MarshalEventPayload(payloadMarshaler, expectEvents[1]),
},
{
Headers: eventstream.Headers{
eventstreamtest.EventMessageTypeHeader,
{
Name: eventstreamapi.EventTypeHeader,
Value: eventstream.StringValue("Headers"),
},
{
Name: "BlobVal",
Value: eventstream.BytesValue(expectEvents[2].(*HeaderOnlyEvent).BlobVal),
},
{
Name: "BoolVal",
Value: eventstream.BoolValue(*expectEvents[2].(*HeaderOnlyEvent).BoolVal),
},
{
Name: "ByteVal",
Value: eventstream.Int8Value(int8(*expectEvents[2].(*HeaderOnlyEvent).ByteVal)),
},
{
Name: "IntegerVal",
Value: eventstream.Int32Value(int32(*expectEvents[2].(*HeaderOnlyEvent).IntegerVal)),
},
{
Name: "LongVal",
Value: eventstream.Int64Value(*expectEvents[2].(*HeaderOnlyEvent).LongVal),
},
{
Name: "ShortVal",
Value: eventstream.Int16Value(int16(*expectEvents[2].(*HeaderOnlyEvent).ShortVal)),
},
{
Name: "StringVal",
Value: eventstream.StringValue(*expectEvents[2].(*HeaderOnlyEvent).StringVal),
},
{
Name: "TimeVal",
Value: eventstream.TimestampValue(*expectEvents[2].(*HeaderOnlyEvent).TimeVal),
},
},
},
{
Headers: eventstream.Headers{
eventstreamtest.EventMessageTypeHeader,
{
Name: eventstreamapi.EventTypeHeader,
Value: eventstream.StringValue("ImplicitPayload"),
},
{
Name: "ByteVal",
Value: eventstream.Int8Value(int8(*expectEvents[3].(*ImplicitPayloadEvent).ByteVal)),
},
},
Payload: eventstreamtest.MarshalEventPayload(payloadMarshaler, expectEvents[3]),
},
{
Headers: eventstream.Headers{
eventstreamtest.EventMessageTypeHeader,
{
Name: eventstreamapi.EventTypeHeader,
Value: eventstream.StringValue("PayloadOnly"),
},
},
Payload: eventstreamtest.MarshalEventPayload(payloadMarshaler, expectEvents[4]),
},
{
Headers: eventstream.Headers{
eventstreamtest.EventMessageTypeHeader,
{
Name: eventstreamapi.EventTypeHeader,
Value: eventstream.StringValue("PayloadOnlyBlob"),
},
},
Payload: expectEvents[5].(*PayloadOnlyBlobEvent).BlobPayload,
},
{
Headers: eventstream.Headers{
eventstreamtest.EventMessageTypeHeader,
{
Name: eventstreamapi.EventTypeHeader,
Value: eventstream.StringValue("PayloadOnlyString"),
},
},
Payload: []byte(*expectEvents[6].(*PayloadOnlyStringEvent).StringPayload),
},
}
return expectEvents, eventMsgs
}
func TestGetEventStream_ReadException(t *testing.T) {
expectEvents := []EventStreamEvent{
&ExceptionEvent{
IntVal: aws.Int64(123),
Message_: aws.String("string value goes here"),
},
}
var marshalers request.HandlerList
marshalers.PushBackNamed(restxml.BuildHandler)
payloadMarshaler := protocol.HandlerPayloadMarshal{
Marshalers: marshalers,
}
eventMsgs := []eventstream.Message{
{
Headers: eventstream.Headers{
eventstreamtest.EventExceptionTypeHeader,
{
Name: eventstreamapi.ExceptionTypeHeader,
Value: eventstream.StringValue("Exception"),
},
},
Payload: eventstreamtest.MarshalEventPayload(payloadMarshaler, expectEvents[0]),
},
}
sess, cleanupFn, err := eventstreamtest.SetupEventStreamSession(t,
eventstreamtest.ServeEventStream{
T: t,
Events: eventMsgs,
},
true,
)
if err != nil {
t.Fatalf("expect no error, %v", err)
}
defer cleanupFn()
svc := New(sess)
resp, err := svc.GetEventStream(nil)
if err != nil {
t.Fatalf("expect no error got, %v", err)
}
defer resp.EventStream.Close()
<-resp.EventStream.Events()
err = resp.EventStream.Err()
if err == nil {
t.Fatalf("expect err, got none")
}
expectErr := &ExceptionEvent{
IntVal: aws.Int64(123),
Message_: aws.String("string value goes here"),
}
aerr, ok := err.(awserr.Error)
if !ok {
t.Errorf("expect exception, got %T, %#v", err, err)
}
if e, a := expectErr.Code(), aerr.Code(); e != a {
t.Errorf("expect %v, got %v", e, a)
}
if e, a := expectErr.Message(), aerr.Message(); e != a {
t.Errorf("expect %v, got %v", e, a)
}
if e, a := expectErr, aerr; !reflect.DeepEqual(e, a) {
t.Errorf("expect %#v, got %#v", e, a)
}
}
var _ awserr.Error = (*ExceptionEvent)(nil)
var _ awserr.Error = (*ExceptionEvent2)(nil)
type loopReader struct {
source *bytes.Reader
}
func (c *loopReader) Read(p []byte) (int, error) {
if c.source.Len() == 0 {
c.source.Seek(0, 0)
}
return c.source.Read(p)
}
@@ -0,0 +1,76 @@
// Code generated by private/model/cli/gen-api/main.go. DO NOT EDIT.
// Package restxmlserviceiface provides an interface to enable mocking the REST XML Service service client
// for testing your code.
//
// It is important to note that this interface will have breaking changes
// when the service model is updated and adds new API operations, paginators,
// and waiters.
package restxmlserviceiface
import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/private/model/api/codegentest/service/restxmlservice"
)
// RESTXMLServiceAPI provides an interface to enable mocking the
// restxmlservice.RESTXMLService service client's API operation,
// paginators, and waiters. This make unit testing your code that calls out
// to the SDK's service client's calls easier.
//
// The best way to use this interface is so the SDK's service client's calls
// can be stubbed out for unit testing your code with the SDK without needing
// to inject custom request handlers into the SDK's request pipeline.
//
// // myFunc uses an SDK service client to make a request to
// // REST XML Service.
// func myFunc(svc restxmlserviceiface.RESTXMLServiceAPI) bool {
// // Make svc.EmptyStream request
// }
//
// func main() {
// sess := session.New()
// svc := restxmlservice.New(sess)
//
// myFunc(svc)
// }
//
// In your _test.go file:
//
// // Define a mock struct to be used in your unit tests of myFunc.
// type mockRESTXMLServiceClient struct {
// restxmlserviceiface.RESTXMLServiceAPI
// }
// func (m *mockRESTXMLServiceClient) EmptyStream(input *restxmlservice.EmptyStreamInput) (*restxmlservice.EmptyStreamOutput, error) {
// // mock response/functionality
// }
//
// func TestMyFunc(t *testing.T) {
// // Setup Test
// mockSvc := &mockRESTXMLServiceClient{}
//
// myfunc(mockSvc)
//
// // Verify myFunc's functionality
// }
//
// It is important to note that this interface will have breaking changes
// when the service model is updated and adds new API operations, paginators,
// and waiters. Its suggested to use the pattern above for testing, or using
// tooling to generate mocks to satisfy the interfaces.
type RESTXMLServiceAPI interface {
EmptyStream(*restxmlservice.EmptyStreamInput) (*restxmlservice.EmptyStreamOutput, error)
EmptyStreamWithContext(aws.Context, *restxmlservice.EmptyStreamInput, ...request.Option) (*restxmlservice.EmptyStreamOutput, error)
EmptyStreamRequest(*restxmlservice.EmptyStreamInput) (*request.Request, *restxmlservice.EmptyStreamOutput)
GetEventStream(*restxmlservice.GetEventStreamInput) (*restxmlservice.GetEventStreamOutput, error)
GetEventStreamWithContext(aws.Context, *restxmlservice.GetEventStreamInput, ...request.Option) (*restxmlservice.GetEventStreamOutput, error)
GetEventStreamRequest(*restxmlservice.GetEventStreamInput) (*request.Request, *restxmlservice.GetEventStreamOutput)
OtherOperation(*restxmlservice.OtherOperationInput) (*restxmlservice.OtherOperationOutput, error)
OtherOperationWithContext(aws.Context, *restxmlservice.OtherOperationInput, ...request.Option) (*restxmlservice.OtherOperationOutput, error)
OtherOperationRequest(*restxmlservice.OtherOperationInput) (*request.Request, *restxmlservice.OtherOperationOutput)
}
var _ RESTXMLServiceAPI = (*restxmlservice.RESTXMLService)(nil)
@@ -0,0 +1,97 @@
// Code generated by private/model/cli/gen-api/main.go. DO NOT EDIT.
package restxmlservice
import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/client"
"github.com/aws/aws-sdk-go/aws/client/metadata"
"github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/aws/signer/v4"
"github.com/aws/aws-sdk-go/private/protocol/restxml"
)
// RESTXMLService provides the API operation methods for making requests to
// REST XML Service. See this package's package overview docs
// for details on the service.
//
// RESTXMLService methods are safe to use concurrently. It is not safe to
// modify mutate any of the struct's properties though.
type RESTXMLService struct {
*client.Client
}
// Used for custom client initialization logic
var initClient func(*client.Client)
// Used for custom request initialization logic
var initRequest func(*request.Request)
// Service information constants
const (
ServiceName = "RESTXMLService" // Name of service.
EndpointsID = "restxmlservice" // ID to lookup a service endpoint with.
ServiceID = "RESTXMLService" // ServiceID is a unique identifer of a specific service.
)
// New creates a new instance of the RESTXMLService client with a session.
// If additional configuration is needed for the client instance use the optional
// aws.Config parameter to add your extra config.
//
// Example:
// // Create a RESTXMLService client from just a session.
// svc := restxmlservice.New(mySession)
//
// // Create a RESTXMLService client with additional configuration
// svc := restxmlservice.New(mySession, aws.NewConfig().WithRegion("us-west-2"))
func New(p client.ConfigProvider, cfgs ...*aws.Config) *RESTXMLService {
c := p.ClientConfig(EndpointsID, cfgs...)
return newClient(*c.Config, c.Handlers, c.Endpoint, c.SigningRegion, c.SigningName)
}
// newClient creates, initializes and returns a new service client instance.
func newClient(cfg aws.Config, handlers request.Handlers, endpoint, signingRegion, signingName string) *RESTXMLService {
svc := &RESTXMLService{
Client: client.New(
cfg,
metadata.ClientInfo{
ServiceName: ServiceName,
ServiceID: ServiceID,
SigningName: signingName,
SigningRegion: signingRegion,
Endpoint: endpoint,
APIVersion: "0000-00-00",
},
handlers,
),
}
// Handlers
svc.Handlers.Sign.PushBackNamed(v4.SignRequestHandler)
svc.Handlers.Build.PushBackNamed(restxml.BuildHandler)
svc.Handlers.Unmarshal.PushBackNamed(restxml.UnmarshalHandler)
svc.Handlers.UnmarshalMeta.PushBackNamed(restxml.UnmarshalMetaHandler)
svc.Handlers.UnmarshalError.PushBackNamed(restxml.UnmarshalErrorHandler)
svc.Handlers.UnmarshalStream.PushBackNamed(restxml.UnmarshalHandler)
// Run custom client initialization if present
if initClient != nil {
initClient(svc.Client)
}
return svc
}
// newRequest creates a new request for a RESTXMLService operation and runs any
// custom request initialization.
func (c *RESTXMLService) newRequest(op *request.Operation, params, data interface{}) *request.Request {
req := c.NewRequest(op, params, data)
// Run custom request initialization if present
if initRequest != nil {
initRequest(req)
}
return req
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,26 @@
// Code generated by private/model/cli/gen-api/main.go. DO NOT EDIT.
// Package rpcservice provides the client and types for making API
// requests to RPC Service.
//
// See https://docs.aws.amazon.com/goto/WebAPI/RPCService-0000-00-00 for more information on this service.
//
// See rpcservice package documentation for more information.
// https://docs.aws.amazon.com/sdk-for-go/api/service/rpcservice/
//
// Using the Client
//
// To contact RPC Service with the SDK use the New function to create
// a new service client. With that client you can make API requests to the service.
// These clients are safe to use concurrently.
//
// See the SDK's documentation for more information on how to use the SDK.
// https://docs.aws.amazon.com/sdk-for-go/api/
//
// See aws.Config documentation for more information on configuring SDK clients.
// https://docs.aws.amazon.com/sdk-for-go/api/aws/#Config
//
// See the RPC Service client RPCService for more
// information on creating client for this service.
// https://docs.aws.amazon.com/sdk-for-go/api/service/rpcservice/#New
package rpcservice
@@ -0,0 +1,14 @@
// Code generated by private/model/cli/gen-api/main.go. DO NOT EDIT.
package rpcservice
const (
// ErrCodeExceptionEvent for service response error code
// "ExceptionEvent".
ErrCodeExceptionEvent = "ExceptionEvent"
// ErrCodeExceptionEvent2 for service response error code
// "ExceptionEvent2".
ErrCodeExceptionEvent2 = "ExceptionEvent2"
)
@@ -0,0 +1,578 @@
// Code generated by private/model/cli/gen-api/main.go. DO NOT EDIT.
// +build go1.6
package rpcservice
import (
"bytes"
"io/ioutil"
"net/http"
"reflect"
"testing"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/corehandlers"
"github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/awstesting/unit"
"github.com/aws/aws-sdk-go/private/protocol"
"github.com/aws/aws-sdk-go/private/protocol/eventstream"
"github.com/aws/aws-sdk-go/private/protocol/eventstream/eventstreamapi"
"github.com/aws/aws-sdk-go/private/protocol/eventstream/eventstreamtest"
"github.com/aws/aws-sdk-go/private/protocol/jsonrpc"
)
var _ time.Time
var _ awserr.Error
func TestEmptyStream_Read(t *testing.T) {
expectEvents, eventMsgs := mockEmptyStreamReadEvents()
sess, cleanupFn, err := eventstreamtest.SetupEventStreamSession(t,
eventstreamtest.ServeEventStream{
T: t,
Events: eventMsgs,
},
true,
)
if err != nil {
t.Fatalf("expect no error, %v", err)
}
defer cleanupFn()
svc := New(sess)
resp, err := svc.EmptyStream(nil)
if err != nil {
t.Fatalf("expect no error got, %v", err)
}
defer resp.EventStream.Close()
// Trim off response output type pseudo event so only event messages remain.
expectEvents = expectEvents[1:]
var i int
for event := range resp.EventStream.Events() {
if event == nil {
t.Errorf("%d, expect event, got nil", i)
}
if e, a := expectEvents[i], event; !reflect.DeepEqual(e, a) {
t.Errorf("%d, expect %T %v, got %T %v", i, e, e, a, a)
}
i++
}
if err := resp.EventStream.Err(); err != nil {
t.Errorf("expect no error, %v", err)
}
}
func TestEmptyStream_ReadClose(t *testing.T) {
_, eventMsgs := mockEmptyStreamReadEvents()
sess, cleanupFn, err := eventstreamtest.SetupEventStreamSession(t,
eventstreamtest.ServeEventStream{
T: t,
Events: eventMsgs,
},
true,
)
if err != nil {
t.Fatalf("expect no error, %v", err)
}
defer cleanupFn()
svc := New(sess)
resp, err := svc.EmptyStream(nil)
if err != nil {
t.Fatalf("expect no error got, %v", err)
}
resp.EventStream.Close()
<-resp.EventStream.Events()
if err := resp.EventStream.Err(); err != nil {
t.Errorf("expect no error, %v", err)
}
}
func BenchmarkEmptyStream_Read(b *testing.B) {
_, eventMsgs := mockEmptyStreamReadEvents()
var buf bytes.Buffer
encoder := eventstream.NewEncoder(&buf)
for _, msg := range eventMsgs {
if err := encoder.Encode(msg); err != nil {
b.Fatalf("failed to encode message, %v", err)
}
}
stream := &loopReader{source: bytes.NewReader(buf.Bytes())}
sess := unit.Session
svc := New(sess, &aws.Config{
Endpoint: aws.String("https://example.com"),
DisableParamValidation: aws.Bool(true),
})
svc.Handlers.Send.Swap(corehandlers.SendHandler.Name,
request.NamedHandler{Name: "mockSend",
Fn: func(r *request.Request) {
r.HTTPResponse = &http.Response{
Status: "200 OK",
StatusCode: 200,
Header: http.Header{},
Body: ioutil.NopCloser(stream),
}
},
},
)
resp, err := svc.EmptyStream(nil)
if err != nil {
b.Fatalf("failed to create request, %v", err)
}
defer resp.EventStream.Close()
b.ResetTimer()
for i := 0; i < b.N; i++ {
if err = resp.EventStream.Err(); err != nil {
b.Fatalf("expect no error, got %v", err)
}
event := <-resp.EventStream.Events()
if event == nil {
b.Fatalf("expect event, got nil, %v, %d", resp.EventStream.Err(), i)
}
}
}
func mockEmptyStreamReadEvents() (
[]EmptyEventStreamEvent,
[]eventstream.Message,
) {
expectEvents := []EmptyEventStreamEvent{
&EmptyStreamOutput{},
}
var marshalers request.HandlerList
marshalers.PushBackNamed(jsonrpc.BuildHandler)
payloadMarshaler := protocol.HandlerPayloadMarshal{
Marshalers: marshalers,
}
_ = payloadMarshaler
eventMsgs := []eventstream.Message{
{
Headers: eventstream.Headers{
eventstreamtest.EventMessageTypeHeader,
{
Name: eventstreamapi.EventTypeHeader,
Value: eventstream.StringValue("initial-response"),
},
},
Payload: eventstreamtest.MarshalEventPayload(payloadMarshaler, expectEvents[0]),
},
}
return expectEvents, eventMsgs
}
func TestGetEventStream_Read(t *testing.T) {
expectEvents, eventMsgs := mockGetEventStreamReadEvents()
sess, cleanupFn, err := eventstreamtest.SetupEventStreamSession(t,
eventstreamtest.ServeEventStream{
T: t,
Events: eventMsgs,
},
true,
)
if err != nil {
t.Fatalf("expect no error, %v", err)
}
defer cleanupFn()
svc := New(sess)
resp, err := svc.GetEventStream(nil)
if err != nil {
t.Fatalf("expect no error got, %v", err)
}
defer resp.EventStream.Close()
expectResp := expectEvents[0].(*GetEventStreamOutput)
if e, a := expectResp.IntVal, resp.IntVal; !reflect.DeepEqual(e, a) {
t.Errorf("expect %v, got %v", e, a)
}
if e, a := expectResp.StrVal, resp.StrVal; !reflect.DeepEqual(e, a) {
t.Errorf("expect %v, got %v", e, a)
}
// Trim off response output type pseudo event so only event messages remain.
expectEvents = expectEvents[1:]
var i int
for event := range resp.EventStream.Events() {
if event == nil {
t.Errorf("%d, expect event, got nil", i)
}
if e, a := expectEvents[i], event; !reflect.DeepEqual(e, a) {
t.Errorf("%d, expect %T %v, got %T %v", i, e, e, a, a)
}
i++
}
if err := resp.EventStream.Err(); err != nil {
t.Errorf("expect no error, %v", err)
}
}
func TestGetEventStream_ReadClose(t *testing.T) {
_, eventMsgs := mockGetEventStreamReadEvents()
sess, cleanupFn, err := eventstreamtest.SetupEventStreamSession(t,
eventstreamtest.ServeEventStream{
T: t,
Events: eventMsgs,
},
true,
)
if err != nil {
t.Fatalf("expect no error, %v", err)
}
defer cleanupFn()
svc := New(sess)
resp, err := svc.GetEventStream(nil)
if err != nil {
t.Fatalf("expect no error got, %v", err)
}
resp.EventStream.Close()
<-resp.EventStream.Events()
if err := resp.EventStream.Err(); err != nil {
t.Errorf("expect no error, %v", err)
}
}
func BenchmarkGetEventStream_Read(b *testing.B) {
_, eventMsgs := mockGetEventStreamReadEvents()
var buf bytes.Buffer
encoder := eventstream.NewEncoder(&buf)
for _, msg := range eventMsgs {
if err := encoder.Encode(msg); err != nil {
b.Fatalf("failed to encode message, %v", err)
}
}
stream := &loopReader{source: bytes.NewReader(buf.Bytes())}
sess := unit.Session
svc := New(sess, &aws.Config{
Endpoint: aws.String("https://example.com"),
DisableParamValidation: aws.Bool(true),
})
svc.Handlers.Send.Swap(corehandlers.SendHandler.Name,
request.NamedHandler{Name: "mockSend",
Fn: func(r *request.Request) {
r.HTTPResponse = &http.Response{
Status: "200 OK",
StatusCode: 200,
Header: http.Header{},
Body: ioutil.NopCloser(stream),
}
},
},
)
resp, err := svc.GetEventStream(nil)
if err != nil {
b.Fatalf("failed to create request, %v", err)
}
defer resp.EventStream.Close()
b.ResetTimer()
for i := 0; i < b.N; i++ {
if err = resp.EventStream.Err(); err != nil {
b.Fatalf("expect no error, got %v", err)
}
event := <-resp.EventStream.Events()
if event == nil {
b.Fatalf("expect event, got nil, %v, %d", resp.EventStream.Err(), i)
}
}
}
func mockGetEventStreamReadEvents() (
[]EventStreamEvent,
[]eventstream.Message,
) {
expectEvents := []EventStreamEvent{
&GetEventStreamOutput{
IntVal: aws.Int64(123),
StrVal: aws.String("string value goes here"),
},
&EmptyEvent{},
&ExplicitPayloadEvent{
LongVal: aws.Int64(1234),
NestedVal: &NestedShape{
IntVal: aws.Int64(123),
StrVal: aws.String("string value goes here"),
},
StringVal: aws.String("string value goes here"),
},
&HeaderOnlyEvent{
BlobVal: []byte("blob value goes here"),
BoolVal: aws.Bool(true),
ByteVal: aws.Int64(1),
IntegerVal: aws.Int64(123),
LongVal: aws.Int64(1234),
ShortVal: aws.Int64(12),
StringVal: aws.String("string value goes here"),
TimeVal: aws.Time(time.Unix(1396594860, 0).UTC()),
},
&ImplicitPayloadEvent{
ByteVal: aws.Int64(1),
IntegerVal: aws.Int64(123),
ShortVal: aws.Int64(12),
},
&PayloadOnlyEvent{
NestedVal: &NestedShape{
IntVal: aws.Int64(123),
StrVal: aws.String("string value goes here"),
},
},
&PayloadOnlyBlobEvent{
BlobPayload: []byte("blob value goes here"),
},
&PayloadOnlyStringEvent{
StringPayload: aws.String("string value goes here"),
},
}
var marshalers request.HandlerList
marshalers.PushBackNamed(jsonrpc.BuildHandler)
payloadMarshaler := protocol.HandlerPayloadMarshal{
Marshalers: marshalers,
}
_ = payloadMarshaler
eventMsgs := []eventstream.Message{
{
Headers: eventstream.Headers{
eventstreamtest.EventMessageTypeHeader,
{
Name: eventstreamapi.EventTypeHeader,
Value: eventstream.StringValue("initial-response"),
},
},
Payload: eventstreamtest.MarshalEventPayload(payloadMarshaler, expectEvents[0]),
},
{
Headers: eventstream.Headers{
eventstreamtest.EventMessageTypeHeader,
{
Name: eventstreamapi.EventTypeHeader,
Value: eventstream.StringValue("Empty"),
},
},
},
{
Headers: eventstream.Headers{
eventstreamtest.EventMessageTypeHeader,
{
Name: eventstreamapi.EventTypeHeader,
Value: eventstream.StringValue("ExplicitPayload"),
},
{
Name: "LongVal",
Value: eventstream.Int64Value(*expectEvents[2].(*ExplicitPayloadEvent).LongVal),
},
{
Name: "StringVal",
Value: eventstream.StringValue(*expectEvents[2].(*ExplicitPayloadEvent).StringVal),
},
},
Payload: eventstreamtest.MarshalEventPayload(payloadMarshaler, expectEvents[2]),
},
{
Headers: eventstream.Headers{
eventstreamtest.EventMessageTypeHeader,
{
Name: eventstreamapi.EventTypeHeader,
Value: eventstream.StringValue("Headers"),
},
{
Name: "BlobVal",
Value: eventstream.BytesValue(expectEvents[3].(*HeaderOnlyEvent).BlobVal),
},
{
Name: "BoolVal",
Value: eventstream.BoolValue(*expectEvents[3].(*HeaderOnlyEvent).BoolVal),
},
{
Name: "ByteVal",
Value: eventstream.Int8Value(int8(*expectEvents[3].(*HeaderOnlyEvent).ByteVal)),
},
{
Name: "IntegerVal",
Value: eventstream.Int32Value(int32(*expectEvents[3].(*HeaderOnlyEvent).IntegerVal)),
},
{
Name: "LongVal",
Value: eventstream.Int64Value(*expectEvents[3].(*HeaderOnlyEvent).LongVal),
},
{
Name: "ShortVal",
Value: eventstream.Int16Value(int16(*expectEvents[3].(*HeaderOnlyEvent).ShortVal)),
},
{
Name: "StringVal",
Value: eventstream.StringValue(*expectEvents[3].(*HeaderOnlyEvent).StringVal),
},
{
Name: "TimeVal",
Value: eventstream.TimestampValue(*expectEvents[3].(*HeaderOnlyEvent).TimeVal),
},
},
},
{
Headers: eventstream.Headers{
eventstreamtest.EventMessageTypeHeader,
{
Name: eventstreamapi.EventTypeHeader,
Value: eventstream.StringValue("ImplicitPayload"),
},
{
Name: "ByteVal",
Value: eventstream.Int8Value(int8(*expectEvents[4].(*ImplicitPayloadEvent).ByteVal)),
},
},
Payload: eventstreamtest.MarshalEventPayload(payloadMarshaler, expectEvents[4]),
},
{
Headers: eventstream.Headers{
eventstreamtest.EventMessageTypeHeader,
{
Name: eventstreamapi.EventTypeHeader,
Value: eventstream.StringValue("PayloadOnly"),
},
},
Payload: eventstreamtest.MarshalEventPayload(payloadMarshaler, expectEvents[5]),
},
{
Headers: eventstream.Headers{
eventstreamtest.EventMessageTypeHeader,
{
Name: eventstreamapi.EventTypeHeader,
Value: eventstream.StringValue("PayloadOnlyBlob"),
},
},
Payload: expectEvents[6].(*PayloadOnlyBlobEvent).BlobPayload,
},
{
Headers: eventstream.Headers{
eventstreamtest.EventMessageTypeHeader,
{
Name: eventstreamapi.EventTypeHeader,
Value: eventstream.StringValue("PayloadOnlyString"),
},
},
Payload: []byte(*expectEvents[7].(*PayloadOnlyStringEvent).StringPayload),
},
}
return expectEvents, eventMsgs
}
func TestGetEventStream_ReadException(t *testing.T) {
expectEvents := []EventStreamEvent{
&GetEventStreamOutput{
IntVal: aws.Int64(123),
StrVal: aws.String("string value goes here"),
},
&ExceptionEvent{
IntVal: aws.Int64(123),
Message_: aws.String("string value goes here"),
},
}
var marshalers request.HandlerList
marshalers.PushBackNamed(jsonrpc.BuildHandler)
payloadMarshaler := protocol.HandlerPayloadMarshal{
Marshalers: marshalers,
}
eventMsgs := []eventstream.Message{
{
Headers: eventstream.Headers{
eventstreamtest.EventMessageTypeHeader,
{
Name: eventstreamapi.EventTypeHeader,
Value: eventstream.StringValue("initial-response"),
},
},
Payload: eventstreamtest.MarshalEventPayload(payloadMarshaler, expectEvents[0]),
},
{
Headers: eventstream.Headers{
eventstreamtest.EventExceptionTypeHeader,
{
Name: eventstreamapi.ExceptionTypeHeader,
Value: eventstream.StringValue("Exception"),
},
},
Payload: eventstreamtest.MarshalEventPayload(payloadMarshaler, expectEvents[1]),
},
}
sess, cleanupFn, err := eventstreamtest.SetupEventStreamSession(t,
eventstreamtest.ServeEventStream{
T: t,
Events: eventMsgs,
},
true,
)
if err != nil {
t.Fatalf("expect no error, %v", err)
}
defer cleanupFn()
svc := New(sess)
resp, err := svc.GetEventStream(nil)
if err != nil {
t.Fatalf("expect no error got, %v", err)
}
defer resp.EventStream.Close()
<-resp.EventStream.Events()
err = resp.EventStream.Err()
if err == nil {
t.Fatalf("expect err, got none")
}
expectErr := &ExceptionEvent{
IntVal: aws.Int64(123),
Message_: aws.String("string value goes here"),
}
aerr, ok := err.(awserr.Error)
if !ok {
t.Errorf("expect exception, got %T, %#v", err, err)
}
if e, a := expectErr.Code(), aerr.Code(); e != a {
t.Errorf("expect %v, got %v", e, a)
}
if e, a := expectErr.Message(), aerr.Message(); e != a {
t.Errorf("expect %v, got %v", e, a)
}
if e, a := expectErr, aerr; !reflect.DeepEqual(e, a) {
t.Errorf("expect %#v, got %#v", e, a)
}
}
var _ awserr.Error = (*ExceptionEvent)(nil)
var _ awserr.Error = (*ExceptionEvent2)(nil)
type loopReader struct {
source *bytes.Reader
}
func (c *loopReader) Read(p []byte) (int, error) {
if c.source.Len() == 0 {
c.source.Seek(0, 0)
}
return c.source.Read(p)
}
@@ -0,0 +1,76 @@
// Code generated by private/model/cli/gen-api/main.go. DO NOT EDIT.
// Package rpcserviceiface provides an interface to enable mocking the RPC Service service client
// for testing your code.
//
// It is important to note that this interface will have breaking changes
// when the service model is updated and adds new API operations, paginators,
// and waiters.
package rpcserviceiface
import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/private/model/api/codegentest/service/rpcservice"
)
// RPCServiceAPI provides an interface to enable mocking the
// rpcservice.RPCService service client's API operation,
// paginators, and waiters. This make unit testing your code that calls out
// to the SDK's service client's calls easier.
//
// The best way to use this interface is so the SDK's service client's calls
// can be stubbed out for unit testing your code with the SDK without needing
// to inject custom request handlers into the SDK's request pipeline.
//
// // myFunc uses an SDK service client to make a request to
// // RPC Service.
// func myFunc(svc rpcserviceiface.RPCServiceAPI) bool {
// // Make svc.EmptyStream request
// }
//
// func main() {
// sess := session.New()
// svc := rpcservice.New(sess)
//
// myFunc(svc)
// }
//
// In your _test.go file:
//
// // Define a mock struct to be used in your unit tests of myFunc.
// type mockRPCServiceClient struct {
// rpcserviceiface.RPCServiceAPI
// }
// func (m *mockRPCServiceClient) EmptyStream(input *rpcservice.EmptyStreamInput) (*rpcservice.EmptyStreamOutput, error) {
// // mock response/functionality
// }
//
// func TestMyFunc(t *testing.T) {
// // Setup Test
// mockSvc := &mockRPCServiceClient{}
//
// myfunc(mockSvc)
//
// // Verify myFunc's functionality
// }
//
// It is important to note that this interface will have breaking changes
// when the service model is updated and adds new API operations, paginators,
// and waiters. Its suggested to use the pattern above for testing, or using
// tooling to generate mocks to satisfy the interfaces.
type RPCServiceAPI interface {
EmptyStream(*rpcservice.EmptyStreamInput) (*rpcservice.EmptyStreamOutput, error)
EmptyStreamWithContext(aws.Context, *rpcservice.EmptyStreamInput, ...request.Option) (*rpcservice.EmptyStreamOutput, error)
EmptyStreamRequest(*rpcservice.EmptyStreamInput) (*request.Request, *rpcservice.EmptyStreamOutput)
GetEventStream(*rpcservice.GetEventStreamInput) (*rpcservice.GetEventStreamOutput, error)
GetEventStreamWithContext(aws.Context, *rpcservice.GetEventStreamInput, ...request.Option) (*rpcservice.GetEventStreamOutput, error)
GetEventStreamRequest(*rpcservice.GetEventStreamInput) (*request.Request, *rpcservice.GetEventStreamOutput)
OtherOperation(*rpcservice.OtherOperationInput) (*rpcservice.OtherOperationOutput, error)
OtherOperationWithContext(aws.Context, *rpcservice.OtherOperationInput, ...request.Option) (*rpcservice.OtherOperationOutput, error)
OtherOperationRequest(*rpcservice.OtherOperationInput) (*request.Request, *rpcservice.OtherOperationOutput)
}
var _ RPCServiceAPI = (*rpcservice.RPCService)(nil)
@@ -0,0 +1,99 @@
// Code generated by private/model/cli/gen-api/main.go. DO NOT EDIT.
package rpcservice
import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/client"
"github.com/aws/aws-sdk-go/aws/client/metadata"
"github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/aws/signer/v4"
"github.com/aws/aws-sdk-go/private/protocol/jsonrpc"
)
// RPCService provides the API operation methods for making requests to
// RPC Service. See this package's package overview docs
// for details on the service.
//
// RPCService methods are safe to use concurrently. It is not safe to
// modify mutate any of the struct's properties though.
type RPCService struct {
*client.Client
}
// Used for custom client initialization logic
var initClient func(*client.Client)
// Used for custom request initialization logic
var initRequest func(*request.Request)
// Service information constants
const (
ServiceName = "RPCService" // Name of service.
EndpointsID = "rpcservice" // ID to lookup a service endpoint with.
ServiceID = "RPCService" // ServiceID is a unique identifer of a specific service.
)
// New creates a new instance of the RPCService client with a session.
// If additional configuration is needed for the client instance use the optional
// aws.Config parameter to add your extra config.
//
// Example:
// // Create a RPCService client from just a session.
// svc := rpcservice.New(mySession)
//
// // Create a RPCService client with additional configuration
// svc := rpcservice.New(mySession, aws.NewConfig().WithRegion("us-west-2"))
func New(p client.ConfigProvider, cfgs ...*aws.Config) *RPCService {
c := p.ClientConfig(EndpointsID, cfgs...)
return newClient(*c.Config, c.Handlers, c.Endpoint, c.SigningRegion, c.SigningName)
}
// newClient creates, initializes and returns a new service client instance.
func newClient(cfg aws.Config, handlers request.Handlers, endpoint, signingRegion, signingName string) *RPCService {
svc := &RPCService{
Client: client.New(
cfg,
metadata.ClientInfo{
ServiceName: ServiceName,
ServiceID: ServiceID,
SigningName: signingName,
SigningRegion: signingRegion,
Endpoint: endpoint,
APIVersion: "0000-00-00",
JSONVersion: "1.1",
TargetPrefix: "RPCService_00000000",
},
handlers,
),
}
// Handlers
svc.Handlers.Sign.PushBackNamed(v4.SignRequestHandler)
svc.Handlers.Build.PushBackNamed(jsonrpc.BuildHandler)
svc.Handlers.Unmarshal.PushBackNamed(jsonrpc.UnmarshalHandler)
svc.Handlers.UnmarshalMeta.PushBackNamed(jsonrpc.UnmarshalMetaHandler)
svc.Handlers.UnmarshalError.PushBackNamed(jsonrpc.UnmarshalErrorHandler)
svc.Handlers.UnmarshalStream.PushBackNamed(jsonrpc.UnmarshalHandler)
// Run custom client initialization if present
if initClient != nil {
initClient(svc.Client)
}
return svc
}
// newRequest creates a new request for a RPCService operation and runs any
// custom request initialization.
func (c *RPCService) newRequest(op *request.Operation, params, data interface{}) *request.Request {
req := c.NewRequest(op, params, data)
// Run custom request initialization if present
if initRequest != nil {
initRequest(req)
}
return req
}
+90 -4
View File
@@ -3,7 +3,9 @@
package api
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
)
@@ -27,10 +29,24 @@ var mergeServices = map[string]service{
},
}
var serviceAliaseNames = map[string]string{
"costandusagereportservice": "CostandUsageReportService",
"elasticloadbalancing": "ELB",
"elasticloadbalancingv2": "ELBV2",
"config": "ConfigService",
}
func (a *API) setServiceAliaseName() {
if newName, ok := serviceAliaseNames[a.PackageName()]; ok {
a.name = newName
}
}
// customizationPasses Executes customization logic for the API by package name.
func (a *API) customizationPasses() {
var svcCustomizations = map[string]func(*API){
"s3": s3Customizations,
"s3control": s3ControlCustomizations,
"cloudfront": cloudfrontCustomizations,
"rds": rdsCustomizations,
@@ -38,6 +54,23 @@ func (a *API) customizationPasses() {
// to provide endpoint them selves.
"cloudsearchdomain": disableEndpointResolving,
"iotdataplane": disableEndpointResolving,
// MTurk smoke test is invalid. The service requires AWS account to be
// linked to Amazon Mechanical Turk Account.
"mturk": supressSmokeTest,
// Backfill the authentication type for cognito identity and sts.
// Removes the need for the customizations in these services.
"cognitoidentity": backfillAuthType(NoneAuthType,
"GetId",
"GetOpenIdToken",
"UnlinkIdentity",
"GetCredentialsForIdentity",
),
"sts": backfillAuthType(NoneAuthType,
"AssumeRoleWithSAML",
"AssumeRoleWithWebIdentity",
),
}
for k := range mergeServices {
@@ -49,13 +82,17 @@ func (a *API) customizationPasses() {
}
}
func supressSmokeTest(a *API) {
a.SmokeTests.TestCases = []SmokeTestCase{}
}
// s3Customizations customizes the API generation to replace values specific to S3.
func s3Customizations(a *API) {
var strExpires *Shape
var keepContentMD5Ref = map[string]struct{}{
"PutObjectInput": struct{}{},
"UploadPartInput": struct{}{},
"PutObjectInput": {},
"UploadPartInput": {},
}
for name, s := range a.Shapes {
@@ -73,6 +110,22 @@ func s3Customizations(a *API) {
}
}
// Decorate member references that are modeled with the wrong type.
// Specifically the case where a member was modeled as a string, but is
// expected to sent across the wire as a base64 value.
//
// e.g. S3's SSECustomerKey and CopySourceSSECustomerKey
for _, refName := range []string{
"SSECustomerKey",
"CopySourceSSECustomerKey",
} {
if ref, ok := s.MemberRefs[refName]; ok {
ref.CustomTags = append(ref.CustomTags, ShapeTag{
"marshal-as", "blob",
})
}
}
// Expires should be a string not time.Time since the format is not
// enforced by S3, and any value can be set to this field outside of the SDK.
if strings.HasSuffix(name, "Output") {
@@ -110,6 +163,22 @@ func s3CustRemoveHeadObjectModeledErrors(a *API) {
op.ErrorRefs = []ShapeRef{}
}
// S3 service operations with an AccountId need accessors to be generated for
// them so the fields can be dynamically accessed without reflection.
func s3ControlCustomizations(a *API) {
for opName, op := range a.Operations {
// Add moving AccountId into the hostname instead of header.
if ref, ok := op.InputRef.Shape.MemberRefs["AccountId"]; ok {
if op.Endpoint != nil {
fmt.Fprintf(os.Stderr, "S3 Control, %s, model already defining endpoint trait, remove this customization.\n", opName)
}
op.Endpoint = &EndpointTrait{HostPrefix: "{AccountId}."}
ref.HostLabel = true
}
}
}
// cloudfrontCustomizations customized the API generation to replace values
// specific to CloudFront.
func cloudfrontCustomizations(a *API) {
@@ -129,7 +198,7 @@ func mergeServicesCustomizations(a *API) {
p := strings.Replace(a.path, info.srcName, info.dstName, -1)
if info.serviceVersion != "" {
index := strings.LastIndex(p, "/")
index := strings.LastIndex(p, string(filepath.Separator))
files, _ := ioutil.ReadDir(p[:index])
if len(files) > 1 {
panic("New version was introduced")
@@ -145,7 +214,7 @@ func mergeServicesCustomizations(a *API) {
for n := range a.Shapes {
if _, ok := serviceAPI.Shapes[n]; ok {
a.Shapes[n].resolvePkg = "github.com/aws/aws-sdk-go/service/" + info.dstName
a.Shapes[n].resolvePkg = SDKImportRoot + "/service/" + info.dstName
}
}
}
@@ -178,3 +247,20 @@ func rdsCustomizations(a *API) {
func disableEndpointResolving(a *API) {
a.Metadata.NoResolveEndpoint = true
}
func backfillAuthType(typ AuthType, opNames ...string) func(*API) {
return func(a *API) {
for _, opName := range opNames {
op, ok := a.Operations[opName]
if !ok {
panic("unable to backfill auth-type for unknown operation " + opName)
}
if v := op.AuthType; len(v) != 0 {
fmt.Fprintf(os.Stderr, "unable to backfill auth-type for %s, already set, %s", opName, v)
continue
}
op.AuthType = typ
}
}
}
+372 -239
View File
@@ -3,15 +3,17 @@
package api
import (
"bytes"
"bufio"
"encoding/json"
"fmt"
"html"
"io"
"os"
"regexp"
"strings"
xhtml "golang.org/x/net/html"
"golang.org/x/net/html/atom"
)
type apiDocumentation struct {
@@ -47,8 +49,13 @@ func (a *API) AttachDocs(filename string) {
func (d *apiDocumentation) setup() {
d.API.Documentation = docstring(d.Service)
for op, doc := range d.Operations {
d.API.Operations[op].Documentation = docstring(doc)
for opName, doc := range d.Operations {
if _, ok := d.API.Operations[opName]; !ok {
panic(fmt.Sprintf("%s, doc op %q not found in API op set",
d.API.name, opName),
)
}
d.API.Operations[opName].Documentation = docstring(doc)
}
for shape, info := range d.Shapes {
@@ -108,7 +115,6 @@ func docstring(doc string) string {
doc = html.UnescapeString(doc)
// Replace doc with full name if doc is empty.
doc = strings.TrimSpace(doc)
if len(doc) == 0 {
doc = fullname
}
@@ -120,17 +126,6 @@ const (
indent = " "
)
// style is what we want to prefix a string with.
// For instance, <li>Foo</li><li>Bar</li>, will generate
// * Foo
// * Bar
var style = map[string]string{
"ul": indent + "* ",
"li": indent + "* ",
"code": indent,
"pre": indent,
}
// commentify converts a string to a Go comment
func commentify(doc string) string {
if len(doc) == 0 {
@@ -155,257 +150,395 @@ func commentify(doc string) string {
return ""
}
// wrap returns a rewritten version of text to have line breaks
// at approximately length characters. Line breaks will only be
// inserted into whitespace.
func wrap(text string, length int, isIndented bool) string {
var buf bytes.Buffer
var last rune
var lastNL bool
var col int
func wrap(text string, length int) string {
var b strings.Builder
for _, c := range text {
switch c {
case '\r': // ignore this
continue // and also don't track `last`
case '\n': // ignore this too, but reset col
if col >= length || last == '\n' {
buf.WriteString("\n")
s := bufio.NewScanner(strings.NewReader(text))
for s.Scan() {
line := s.Text()
// cleanup the line's spaces
var i int
for i = 0; i < len(line); i++ {
c := line[i]
// Ignore leading spaces, e.g indents.
if !(c == ' ' || c == '\t') {
break
}
buf.WriteString("\n")
col = 0
case ' ', '\t': // opportunity to split
if col >= length {
buf.WriteByte('\n')
col = 0
if isIndented {
buf.WriteString(indent)
col += 3
}
} else {
// We only want to write a leading space if the col is greater than zero.
// This will provide the proper spacing for documentation.
buf.WriteRune(c)
col++ // count column
}
default:
buf.WriteRune(c)
col++
}
lastNL = c == '\n'
_ = lastNL
last = c
line = line[:i] + strings.Join(strings.Fields(line[i:]), " ")
splitLine(&b, line, length)
}
return buf.String()
return strings.TrimRight(b.String(), "\n")
}
type tagInfo struct {
tag string
key string
val string
txt string
raw string
closingTag bool
func splitLine(w stringWriter, line string, length int) {
leading := getLeadingWhitespace(line)
line = line[len(leading):]
length -= len(leading)
const splitOn = " "
for len(line) > length {
// Find the next whitespace to the length
idx := strings.Index(line[length:], splitOn)
if idx == -1 {
break
}
offset := length + idx
if v := line[offset+len(splitOn):]; len(v) == 1 && strings.ContainsAny(v, `,.!?'"`) {
// Workaround for long lines with space before the punctuation mark.
break
}
w.WriteString(leading)
w.WriteString(line[:offset])
w.WriteByte('\n')
line = strings.TrimLeft(line[offset+len(splitOn):], " \t")
}
if len(line) > 0 {
w.WriteString(leading)
w.WriteString(line)
}
// Add the newline back in that was stripped out by scanner.
w.WriteByte('\n')
}
func getLeadingWhitespace(v string) string {
var o strings.Builder
for _, c := range v {
if c == ' ' || c == '\t' {
o.WriteRune(c)
} else {
break
}
}
return o.String()
}
// generateDoc will generate the proper doc string for html encoded or plain text doc entries.
func generateDoc(htmlSrc string) string {
tokenizer := xhtml.NewTokenizer(strings.NewReader(htmlSrc))
tokens := buildTokenArray(tokenizer)
scopes := findScopes(tokens)
return walk(scopes)
var builder strings.Builder
if err := encodeHTMLToText(&builder, tokenizer); err != nil {
panic(fmt.Sprintf("failed to generated docs, %v", err))
}
return wrap(strings.Trim(builder.String(), "\n"), 72)
}
func buildTokenArray(tokenizer *xhtml.Tokenizer) []tagInfo {
tokens := []tagInfo{}
for tt := tokenizer.Next(); tt != xhtml.ErrorToken; tt = tokenizer.Next() {
switch tt {
case xhtml.TextToken:
txt := string(tokenizer.Text())
if len(tokens) == 0 {
info := tagInfo{
raw: txt,
}
tokens = append(tokens, info)
}
tn, _ := tokenizer.TagName()
key, val, _ := tokenizer.TagAttr()
info := tagInfo{
tag: string(tn),
key: string(key),
val: string(val),
txt: txt,
}
tokens = append(tokens, info)
case xhtml.StartTagToken:
tn, _ := tokenizer.TagName()
key, val, _ := tokenizer.TagAttr()
info := tagInfo{
tag: string(tn),
key: string(key),
val: string(val),
}
tokens = append(tokens, info)
case xhtml.SelfClosingTagToken, xhtml.EndTagToken:
tn, _ := tokenizer.TagName()
key, val, _ := tokenizer.TagAttr()
info := tagInfo{
tag: string(tn),
key: string(key),
val: string(val),
closingTag: true,
}
tokens = append(tokens, info)
}
}
return tokens
type stringWriter interface {
Write([]byte) (int, error)
WriteByte(byte) error
WriteRune(rune) (int, error)
WriteString(string) (int, error)
}
// walk is used to traverse each scoped block. These scoped
// blocks will act as blocked text where we do most of our
// text manipulation.
func walk(scopes [][]tagInfo) string {
doc := ""
// Documentation will be chunked by scopes.
// Meaning, for each scope will be divided by one or more newlines.
for _, scope := range scopes {
indentStr, isIndented := priorityIndentation(scope)
block := ""
href := ""
after := false
level := 0
lastTag := ""
for _, token := range scope {
if token.closingTag {
endl := closeTag(token, level)
block += endl
level--
lastTag = ""
} else if token.txt == "" {
if token.val != "" {
href, after = formatText(token, "")
}
if level == 1 && isIndented {
block += indentStr
}
level++
lastTag = token.tag
} else {
if token.txt != " " {
str, _ := formatText(token, lastTag)
block += str
if after {
block += href
after = false
}
} else {
fmt.Println(token.tag)
str, _ := formatText(tagInfo{}, lastTag)
block += str
}
func encodeHTMLToText(w stringWriter, z *xhtml.Tokenizer) error {
encoder := newHTMLTokenEncoder(w)
defer encoder.Flush()
for {
tt := z.Next()
if tt == xhtml.ErrorToken {
if err := z.Err(); err == io.EOF {
return nil
} else if err != nil {
return err
}
}
if !isIndented {
block = strings.TrimPrefix(block, " ")
if err := encoder.Encode(z.Token()); err != nil {
return err
}
block = wrap(block, 72, isIndented)
doc += block
}
return doc
}
// closeTag will divide up the blocks of documentation to be formated properly.
func closeTag(token tagInfo, level int) string {
switch token.tag {
case "pre", "li", "div":
return "\n"
case "p", "h1", "h2", "h3", "h4", "h5", "h6":
return "\n\n"
case "code":
// indented code is only at the 0th level.
if level == 0 {
return "\n"
}
}
return ""
type htmlTokenHandler interface {
OnStartTagToken(xhtml.Token) htmlTokenHandler
OnEndTagToken(xhtml.Token, bool)
OnSelfClosingTagToken(xhtml.Token)
OnTextTagToken(xhtml.Token)
}
// formatText will format any sort of text based off of a tag. It will also return
// a boolean to add the string after the text token.
func formatText(token tagInfo, lastTag string) (string, bool) {
switch token.tag {
case "a":
if token.val != "" {
return fmt.Sprintf(" (%s)", token.val), true
}
}
// We don't care about a single space nor no text.
if len(token.txt) == 0 || token.txt == " " {
return "", false
}
// Here we want to indent code blocks that are newlines
if lastTag == "code" {
// Greater than one, because we don't care about newlines in the beginning
block := ""
if lines := strings.Split(token.txt, "\n"); len(lines) > 1 {
for _, line := range lines {
block += indent + line
}
block += "\n"
return block, false
}
}
return token.txt, false
type htmlTokenEncoder struct {
w stringWriter
depth int
handlers []tokenHandlerItem
baseHandler tokenHandlerItem
}
// This is a parser to check what type of indention is needed.
func priorityIndentation(blocks []tagInfo) (string, bool) {
if len(blocks) == 0 {
return "", false
}
v, ok := style[blocks[0].tag]
return v, ok
type tokenHandlerItem struct {
handler htmlTokenHandler
depth int
}
// Divides into scopes based off levels.
// For instance,
// <p>Testing<code>123</code></p><ul><li>Foo</li></ul>
// This has 2 scopes, the <p> and <ul>
func findScopes(tokens []tagInfo) [][]tagInfo {
level := 0
scope := []tagInfo{}
scopes := [][]tagInfo{}
for _, token := range tokens {
// we will clear empty tagged tokens from the array
txt := strings.TrimSpace(token.txt)
tag := strings.TrimSpace(token.tag)
if len(txt) == 0 && len(tag) == 0 {
continue
}
func newHTMLTokenEncoder(w stringWriter) *htmlTokenEncoder {
baseHandler := newBlockTokenHandler(w)
baseHandler.rootBlock = true
scope = append(scope, token)
// If it is a closing tag then we check what level
// we are on. If it is 0, then that means we have found a
// scoped block.
if token.closingTag {
level--
if level == 0 {
scopes = append(scopes, scope)
scope = []tagInfo{}
}
// Check opening tags and increment the level
} else if token.txt == "" {
level++
}
return &htmlTokenEncoder{
w: w,
baseHandler: tokenHandlerItem{
handler: baseHandler,
},
}
// In this case, we did not run into a closing tag. This would mean
// we have plaintext for documentation.
if len(scopes) == 0 {
scopes = append(scopes, scope)
}
return scopes
}
func (e *htmlTokenEncoder) Flush() error {
e.baseHandler.handler.OnEndTagToken(xhtml.Token{Type: xhtml.TextToken}, true)
return nil
}
func (e *htmlTokenEncoder) Encode(token xhtml.Token) error {
h := e.baseHandler
if len(e.handlers) != 0 {
h = e.handlers[len(e.handlers)-1]
}
switch token.Type {
case xhtml.StartTagToken:
e.depth++
next := h.handler.OnStartTagToken(token)
if next != nil {
e.handlers = append(e.handlers, tokenHandlerItem{
handler: next,
depth: e.depth,
})
}
case xhtml.EndTagToken:
handlerBlockClosing := e.depth == h.depth
h.handler.OnEndTagToken(token, handlerBlockClosing)
// Remove all but the root handler as the handler is no longer needed.
if handlerBlockClosing {
e.handlers = e.handlers[:len(e.handlers)-1]
}
e.depth--
case xhtml.SelfClosingTagToken:
h.handler.OnSelfClosingTagToken(token)
case xhtml.TextToken:
h.handler.OnTextTagToken(token)
}
return nil
}
type baseTokenHandler struct {
w stringWriter
}
func (e *baseTokenHandler) OnStartTagToken(token xhtml.Token) htmlTokenHandler { return nil }
func (e *baseTokenHandler) OnEndTagToken(token xhtml.Token, blockClosing bool) {}
func (e *baseTokenHandler) OnSelfClosingTagToken(token xhtml.Token) {}
func (e *baseTokenHandler) OnTextTagToken(token xhtml.Token) {
e.w.WriteString(token.Data)
}
type blockTokenHandler struct {
baseTokenHandler
rootBlock bool
origWriter stringWriter
strBuilder *strings.Builder
started bool
newlineBeforeNextBlock bool
}
func newBlockTokenHandler(w stringWriter) *blockTokenHandler {
strBuilder := &strings.Builder{}
return &blockTokenHandler{
origWriter: w,
strBuilder: strBuilder,
baseTokenHandler: baseTokenHandler{
w: strBuilder,
},
}
}
func (e *blockTokenHandler) OnStartTagToken(token xhtml.Token) htmlTokenHandler {
e.started = true
if e.newlineBeforeNextBlock {
e.w.WriteString("\n")
e.newlineBeforeNextBlock = false
}
switch token.DataAtom {
case atom.A:
return newLinkTokenHandler(e.w, token)
case atom.Ul:
e.w.WriteString("\n")
e.newlineBeforeNextBlock = true
return newListTokenHandler(e.w)
case atom.Div, atom.Dt, atom.P, atom.H1, atom.H2, atom.H3, atom.H4, atom.H5, atom.H6:
e.w.WriteString("\n")
e.newlineBeforeNextBlock = true
return newBlockTokenHandler(e.w)
case atom.Pre, atom.Code:
if e.rootBlock {
e.w.WriteString("\n")
e.w.WriteString(indent)
e.newlineBeforeNextBlock = true
}
return newBlockTokenHandler(e.w)
}
return nil
}
func (e *blockTokenHandler) OnEndTagToken(token xhtml.Token, blockClosing bool) {
if !blockClosing {
return
}
e.origWriter.WriteString(e.strBuilder.String())
if e.newlineBeforeNextBlock {
e.origWriter.WriteString("\n")
e.newlineBeforeNextBlock = false
}
e.strBuilder.Reset()
}
func (e *blockTokenHandler) OnTextTagToken(token xhtml.Token) {
if e.newlineBeforeNextBlock {
e.w.WriteString("\n")
e.newlineBeforeNextBlock = false
}
if !e.started {
token.Data = strings.TrimLeft(token.Data, " \t\n")
}
if len(token.Data) != 0 {
e.started = true
}
e.baseTokenHandler.OnTextTagToken(token)
}
type linkTokenHandler struct {
baseTokenHandler
linkToken xhtml.Token
}
func newLinkTokenHandler(w stringWriter, token xhtml.Token) *linkTokenHandler {
return &linkTokenHandler{
baseTokenHandler: baseTokenHandler{
w: w,
},
linkToken: token,
}
}
func (e *linkTokenHandler) OnEndTagToken(token xhtml.Token, blockClosing bool) {
if !blockClosing {
return
}
if href, ok := getHTMLTokenAttr(e.linkToken.Attr, "href"); ok && len(href) != 0 {
fmt.Fprintf(e.w, " (%s)", strings.TrimSpace(href))
}
}
type listTokenHandler struct {
baseTokenHandler
items int
}
func newListTokenHandler(w stringWriter) *listTokenHandler {
return &listTokenHandler{
baseTokenHandler: baseTokenHandler{
w: w,
},
}
}
func (e *listTokenHandler) OnStartTagToken(token xhtml.Token) htmlTokenHandler {
switch token.DataAtom {
case atom.Li:
if e.items >= 1 {
e.w.WriteString("\n\n")
}
e.items++
return newListItemTokenHandler(e.w)
}
return nil
}
func (e *listTokenHandler) OnTextTagToken(token xhtml.Token) {
// Squash whitespace between list and items
}
type listItemTokenHandler struct {
baseTokenHandler
origWriter stringWriter
strBuilder *strings.Builder
}
func newListItemTokenHandler(w stringWriter) *listItemTokenHandler {
strBuilder := &strings.Builder{}
return &listItemTokenHandler{
origWriter: w,
strBuilder: strBuilder,
baseTokenHandler: baseTokenHandler{
w: strBuilder,
},
}
}
func (e *listItemTokenHandler) OnStartTagToken(token xhtml.Token) htmlTokenHandler {
switch token.DataAtom {
case atom.P:
return newBlockTokenHandler(e.w)
}
return nil
}
func (e *listItemTokenHandler) OnEndTagToken(token xhtml.Token, blockClosing bool) {
if !blockClosing {
return
}
e.origWriter.WriteString(indent + "* ")
e.origWriter.WriteString(strings.TrimSpace(e.strBuilder.String()))
}
type trimSpaceTokenHandler struct {
baseTokenHandler
origWriter stringWriter
strBuilder *strings.Builder
}
func newTrimSpaceTokenHandler(w stringWriter) *trimSpaceTokenHandler {
strBuilder := &strings.Builder{}
return &trimSpaceTokenHandler{
origWriter: w,
strBuilder: strBuilder,
baseTokenHandler: baseTokenHandler{
w: strBuilder,
},
}
}
func (e *trimSpaceTokenHandler) OnEndTagToken(token xhtml.Token, blockClosing bool) {
if !blockClosing {
return
}
e.origWriter.WriteString(strings.TrimSpace(e.strBuilder.String()))
}
func getHTMLTokenAttr(attr []xhtml.Attribute, name string) (string, bool) {
for _, a := range attr {
if strings.EqualFold(a.Key, name) {
return a.Val, true
}
}
return "", false
}
+71 -90
View File
@@ -1,4 +1,4 @@
// +build 1.6,codegen
// +build go1.8,codegen
package api
@@ -6,95 +6,76 @@ import (
"testing"
)
func TestNonHTMLDocGen(t *testing.T) {
doc := "Testing 1 2 3"
expected := "// Testing 1 2 3\n"
doc = docstring(doc)
func TestDocstring(t *testing.T) {
cases := map[string]struct {
In string
Expect string
}{
"non HTML": {
In: "Testing 1 2 3",
Expect: "// Testing 1 2 3",
},
"link": {
In: `<a href="https://example.com">a link</a>`,
Expect: "// a link (https://example.com)",
},
"link with space": {
In: `<a href=" https://example.com">a link</a>`,
Expect: "// a link (https://example.com)",
},
"list HTML 01": {
In: "<ul><li>Testing 1 2 3</li> <li>FooBar</li></ul>",
Expect: "// * Testing 1 2 3\n// \n// * FooBar",
},
"list HTML 02": {
In: "<ul> <li>Testing 1 2 3</li> <li>FooBar</li> </ul>",
Expect: "// * Testing 1 2 3\n// \n// * FooBar",
},
"list HTML leading spaces": {
In: " <ul> <li>Testing 1 2 3</li> <li>FooBar</li> </ul>",
Expect: "// * Testing 1 2 3\n// \n// * FooBar",
},
"list HTML paragraph": {
In: "<ul> <li> <p>Testing 1 2 3</p> </li><li> <p>FooBar</p></li></ul>",
Expect: "// * Testing 1 2 3\n// \n// * FooBar",
},
"inline code HTML": {
In: "<ul> <li><code>Testing</code>: 1 2 3</li> <li>FooBar</li> </ul>",
Expect: "// * Testing: 1 2 3\n// \n// * FooBar",
},
"complex list paragraph": {
In: "<ul> <li><p><code>FOO</code> Bar</p></li><li><p><code>Xyz</code> ABC</p></li></ul>",
Expect: "// * FOO Bar\n// \n// * Xyz ABC",
},
"inline code in paragraph": {
In: "<p><code>Testing</code>: 1 2 3</p>",
Expect: "// Testing: 1 2 3",
},
"root pre": {
In: "<pre><code>Testing</code></pre>",
Expect: "// Testing",
},
"paragraph": {
In: "<p>Testing 1 2 3</p>",
Expect: "// Testing 1 2 3",
},
"wrap lines": {
In: "<span data-target-type=\"operation\" data-service=\"secretsmanager\" data-target=\"CreateSecret\">CreateSecret</span> <span data-target-type=\"structure\" data-service=\"secretsmanager\" data-target=\"SecretListEntry\">SecretListEntry</span> <span data-target-type=\"structure\" data-service=\"secretsmanager\" data-target=\"CreateSecret$SecretName\">SecretName</span> <span data-target-type=\"structure\" data-service=\"secretsmanager\" data-target=\"SecretListEntry$KmsKeyId\">KmsKeyId</span>",
Expect: "// CreateSecret SecretListEntry SecretName KmsKeyId",
},
"links with spaces": {
In: "<p> Deletes the replication configuration from the bucket. For information about replication configuration, see <a href=\" https://docs.aws.amazon.com/AmazonS3/latest/dev/crr.html\">Cross-Region Replication (CRR)</a> in the <i>Amazon S3 Developer Guide</i>. </p>",
Expect: "// Deletes the replication configuration from the bucket. For information about\n// replication configuration, see Cross-Region Replication (CRR) (https://docs.aws.amazon.com/AmazonS3/latest/dev/crr.html)\n// in the Amazon S3 Developer Guide.",
},
}
if expected != doc {
t.Errorf("Expected %s, but received %s", expected, doc)
}
}
func TestListsHTMLDocGen(t *testing.T) {
doc := "<ul><li>Testing 1 2 3</li> <li>FooBar</li></ul>"
expected := "// * Testing 1 2 3\n// * FooBar\n"
doc = docstring(doc)
if expected != doc {
t.Errorf("Expected %s, but received %s", expected, doc)
}
doc = "<ul> <li>Testing 1 2 3</li> <li>FooBar</li> </ul>"
expected = "// * Testing 1 2 3\n// * FooBar\n"
doc = docstring(doc)
if expected != doc {
t.Errorf("Expected %s, but received %s", expected, doc)
}
// Test leading spaces
doc = " <ul> <li>Testing 1 2 3</li> <li>FooBar</li> </ul>"
doc = docstring(doc)
if expected != doc {
t.Errorf("Expected %s, but received %s", expected, doc)
}
// Paragraph check
doc = "<ul> <li> <p>Testing 1 2 3</p> </li><li> <p>FooBar</p></li></ul>"
expected = "// * Testing 1 2 3\n// \n// * FooBar\n"
doc = docstring(doc)
if expected != doc {
t.Errorf("Expected %s, but received %s", expected, doc)
}
}
func TestInlineCodeHTMLDocGen(t *testing.T) {
doc := "<ul> <li><code>Testing</code>: 1 2 3</li> <li>FooBar</li> </ul>"
expected := "// * Testing: 1 2 3\n// * FooBar\n"
doc = docstring(doc)
if expected != doc {
t.Errorf("Expected %s, but received %s", expected, doc)
}
}
func TestInlineCodeInParagraphHTMLDocGen(t *testing.T) {
doc := "<p><code>Testing</code>: 1 2 3</p>"
expected := "// Testing: 1 2 3\n"
doc = docstring(doc)
if expected != doc {
t.Errorf("Expected %s, but received %s", expected, doc)
}
}
func TestEmptyPREInlineCodeHTMLDocGen(t *testing.T) {
doc := "<pre><code>Testing</code></pre>"
expected := "// Testing\n"
doc = docstring(doc)
if expected != doc {
t.Errorf("Expected %s, but received %s", expected, doc)
}
}
func TestParagraph(t *testing.T) {
doc := "<p>Testing 1 2 3</p>"
expected := "// Testing 1 2 3\n"
doc = docstring(doc)
if expected != doc {
t.Errorf("Expected %s, but received %s", expected, doc)
}
}
func TestComplexListParagraphCode(t *testing.T) {
doc := "<ul> <li><p><code>FOO</code> Bar</p></li><li><p><code>Xyz</code> ABC</p></li></ul>"
expected := "// * FOO Bar\n// \n// * Xyz ABC\n"
doc = docstring(doc)
if expected != doc {
t.Errorf("Expected %s, but received %s", expected, doc)
for name, c := range cases {
t.Run(name, func(t *testing.T) {
t.Log("Input", c.In)
actual := docstring(c.In)
if e, a := c.Expect, actual; e != a {
t.Errorf("expect %q, got %q", e, a)
}
})
}
}
+59
View File
@@ -0,0 +1,59 @@
// +build codegen
package api
import (
"fmt"
"text/template"
)
func setupEndpointHostPrefix(op *Operation) {
op.API.AddSDKImport("private/protocol")
buildHandler := fmt.Sprintf("protocol.NewHostPrefixHandler(%q, ",
op.Endpoint.HostPrefix)
if op.InputRef.Shape.HasHostLabelMembers() {
buildHandler += "input.hostLabels"
} else {
buildHandler += "nil"
}
buildHandler += ")"
op.CustomBuildHandlers = append(op.CustomBuildHandlers,
buildHandler,
"protocol.ValidateEndpointHostHandler",
)
}
// HasHostLabelMembers returns true if the shape contains any members which are
// decorated with the hostLabel trait.
func (s *Shape) HasHostLabelMembers() bool {
for _, ref := range s.MemberRefs {
if ref.HostLabel {
return true
}
}
return false
}
var hostLabelsShapeTmpl = template.Must(
template.New("hostLabelsShapeTmpl").
Parse(hostLabelsShapeTmplDef),
)
const hostLabelsShapeTmplDef = `
{{- define "hostLabelsShapeTmpl" }}
func (s *{{ $.ShapeName }}) hostLabels() map[string]string {
return map[string]string{
{{- range $name, $ref := $.MemberRefs }}
{{- if $ref.HostLabel }}
"{{ $name }}": aws.StringValue(s.{{ $name }}),
{{- end }}
{{- end }}
}
}
{{- end }}
`
File diff suppressed because it is too large Load Diff
-79
View File
@@ -1,79 +0,0 @@
// +build go1.6,codegen
package api
import (
"testing"
)
func TestSuppressEventStream(t *testing.T) {
cases := []struct {
API *API
Ops []string
Shapes []string
}{
{
API: &API{
Operations: map[string]*Operation{
"abc": {
InputRef: ShapeRef{
ShapeName: "abcRequest",
},
OutputRef: ShapeRef{
ShapeName: "abcResponse",
},
},
"eventStreamOp": {
InputRef: ShapeRef{
ShapeName: "eventStreamOpRequest",
},
OutputRef: ShapeRef{
ShapeName: "eventStreamOpResponse",
},
},
},
Shapes: map[string]*Shape{
"abcRequest": {},
"abcResponse": {},
"eventStreamOpRequest": {},
"eventStreamOpResponse": {
MemberRefs: map[string]*ShapeRef{
"eventStreamShape": {
ShapeName: "eventStreamShape",
},
},
},
"eventStreamShape": {
IsEventStream: true,
},
},
},
Ops: []string{"Abc"},
Shapes: []string{"AbcInput", "AbcOutput"},
},
}
for _, c := range cases {
c.API.Setup()
if e, a := c.Ops, c.API.OperationNames(); !stringsEqual(e, a) {
t.Errorf("expect %v ops, got %v", e, a)
}
if e, a := c.Shapes, c.API.ShapeNames(); !stringsEqual(e, a) {
t.Errorf("expect %v ops, got %v", e, a)
}
}
}
func stringsEqual(a, b []string) bool {
if len(a) != len(b) {
return false
}
for i := 0; i < len(a); i++ {
if a[i] != b[i] {
return false
}
}
return true
}
+7 -6
View File
@@ -54,14 +54,12 @@ var exampleCustomizations = map[string]template.FuncMap{}
var exampleTmpls = template.Must(template.New("example").Funcs(exampleFuncMap).Parse(`
{{ generateTypes . }}
{{ commentify (wrap .Title 80 false) }}
{{ commentify (wrap .Title 80) }}
//
{{ commentify (wrap .Description 80 false) }}
{{ commentify (wrap .Description 80) }}
func Example{{ .API.StructName }}_{{ .MethodName }}() {
svc := {{ .API.PackageName }}.New(session.New())
input := &{{ .Operation.InputRef.Shape.GoTypeWithPkgNameElem }} {
{{ generateExampleInput . -}}
}
input := {{ generateExampleInput . }}
result, err := svc.{{ .OperationName }}(input)
if err != nil {
@@ -130,7 +128,10 @@ func (ex Example) GoCode() string {
func generateExampleInput(ex Example) string {
if ex.Operation.HasInput() {
return ex.Builder.BuildShape(&ex.Operation.InputRef, ex.Input, false)
return fmt.Sprintf("&%s{\n%s\n}",
ex.Builder.GoType(&ex.Operation.InputRef, true),
ex.Builder.BuildShape(&ex.Operation.InputRef, ex.Input, false),
)
}
return ""
}
+4 -4
View File
@@ -220,10 +220,10 @@ import (
"strings"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/fooservice"
"` + SDKImportRoot + `/aws"
"` + SDKImportRoot + `/aws/awserr"
"` + SDKImportRoot + `/aws/session"
"` + SDKImportRoot + `/service/fooservice"
)
var _ time.Duration
+9 -204
View File
@@ -2,221 +2,26 @@
package api
import (
"bytes"
"fmt"
"reflect"
"sort"
"strings"
)
type examplesBuilder interface {
BuildShape(*ShapeRef, map[string]interface{}, bool) string
BuildList(string, string, *ShapeRef, []interface{}) string
BuildComplex(string, string, *ShapeRef, map[string]interface{}) string
GoType(*ShapeRef, bool) string
Imports(*API) string
}
type defaultExamplesBuilder struct{}
// BuildShape will recursively build the referenced shape based on the json object
// provided.
// isMap will dictate how the field name is specified. If isMap is true, we will expect
// the member name to be quotes like "Foo".
func (builder defaultExamplesBuilder) BuildShape(ref *ShapeRef, shapes map[string]interface{}, isMap bool) string {
order := make([]string, len(shapes))
for k := range shapes {
order = append(order, k)
}
sort.Strings(order)
ret := ""
for _, name := range order {
if name == "" {
continue
}
shape := shapes[name]
// If the shape isn't a map, we want to export the value, since every field
// defined in our shapes are exported.
if len(name) > 0 && !isMap && strings.ToLower(name[0:1]) == name[0:1] {
name = strings.Title(name)
}
memName := name
passRef := ref.Shape.MemberRefs[name]
if isMap {
memName = fmt.Sprintf("%q", memName)
passRef = &ref.Shape.ValueRef
}
switch v := shape.(type) {
case map[string]interface{}:
ret += builder.BuildComplex(name, memName, passRef, v)
case []interface{}:
ret += builder.BuildList(name, memName, passRef, v)
default:
ret += builder.BuildScalar(name, memName, passRef, v, ref.Shape.Payload == name)
}
}
return ret
}
// BuildList will construct a list shape based off the service's definition
// of that list.
func (builder defaultExamplesBuilder) BuildList(name, memName string, ref *ShapeRef, v []interface{}) string {
ret := ""
if len(v) == 0 || ref == nil {
return ""
}
passRef := &ref.Shape.MemberRef
ret += fmt.Sprintf("%s: %s {\n", memName, builder.GoType(ref, false))
ret += builder.buildListElements(passRef, v)
ret += "},\n"
return ret
}
func (builder defaultExamplesBuilder) buildListElements(ref *ShapeRef, v []interface{}) string {
if len(v) == 0 || ref == nil {
return ""
}
ret := ""
format := ""
isComplex := false
isList := false
// get format for atomic type. If it is not an atomic type,
// get the element.
switch v[0].(type) {
case string:
format = "%s"
case bool:
format = "%t"
case float64:
switch ref.Shape.Type {
case "integer", "int64", "long":
format = "%d"
default:
format = "%f"
}
case []interface{}:
isList = true
case map[string]interface{}:
isComplex = true
}
for _, elem := range v {
if isComplex {
ret += fmt.Sprintf("{\n%s\n},\n", builder.BuildShape(ref, elem.(map[string]interface{}), ref.Shape.Type == "map"))
} else if isList {
ret += fmt.Sprintf("{\n%s\n},\n", builder.buildListElements(&ref.Shape.MemberRef, elem.([]interface{})))
} else {
switch ref.Shape.Type {
case "integer", "int64", "long":
elem = int(elem.(float64))
}
ret += fmt.Sprintf("%s,\n", getValue(ref.Shape.Type, fmt.Sprintf(format, elem)))
}
}
return ret
}
// BuildScalar will build atomic Go types.
func (builder defaultExamplesBuilder) BuildScalar(name, memName string, ref *ShapeRef, shape interface{}, isPayload bool) string {
if ref == nil || ref.Shape == nil {
return ""
}
switch v := shape.(type) {
case bool:
return convertToCorrectType(memName, ref.Shape.Type, fmt.Sprintf("%t", v))
case int:
if ref.Shape.Type == "timestamp" {
return parseTimeString(ref, memName, fmt.Sprintf("%d", v))
}
return convertToCorrectType(memName, ref.Shape.Type, fmt.Sprintf("%d", v))
case float64:
dataType := ref.Shape.Type
if dataType == "integer" || dataType == "int64" || dataType == "long" {
return convertToCorrectType(memName, ref.Shape.Type, fmt.Sprintf("%d", int(shape.(float64))))
}
return convertToCorrectType(memName, ref.Shape.Type, fmt.Sprintf("%f", v))
case string:
t := ref.Shape.Type
switch t {
case "timestamp":
return parseTimeString(ref, memName, fmt.Sprintf("%s", v))
case "blob":
if (ref.Streaming || ref.Shape.Streaming) && isPayload {
return fmt.Sprintf("%s: aws.ReadSeekCloser(strings.NewReader(%q)),\n", memName, v)
}
return fmt.Sprintf("%s: []byte(%q),\n", memName, v)
default:
return convertToCorrectType(memName, t, v)
}
default:
panic(fmt.Errorf("Unsupported scalar type: %v", reflect.TypeOf(v)))
}
return ""
}
func (builder defaultExamplesBuilder) BuildComplex(name, memName string, ref *ShapeRef, v map[string]interface{}) string {
switch ref.Shape.Type {
case "structure":
return fmt.Sprintf(`%s: &%s{
%s
},
`, memName, builder.GoType(ref, true), builder.BuildShape(ref, v, false))
case "map":
return fmt.Sprintf(`%s: %s{
%s
},
`, name, builder.GoType(ref, false), builder.BuildShape(ref, v, true))
default:
panic(fmt.Sprintf("Expected complex type but recieved %q", ref.Shape.Type))
}
return ""
}
func (builder defaultExamplesBuilder) GoType(ref *ShapeRef, elem bool) string {
if ref.Shape.Type != "structure" && ref.Shape.Type != "list" && ref.Shape.Type != "map" {
return ref.GoTypeWithPkgName()
}
prefix := ""
if ref.Shape.Type == "list" {
ref = &ref.Shape.MemberRef
prefix = "[]"
}
name := ref.GoTypeWithPkgName()
if elem {
name = ref.GoTypeElem()
if !strings.Contains(name, ".") {
name = strings.Join([]string{ref.API.PackageName(), name}, ".")
}
}
return prefix + name
type defaultExamplesBuilder struct {
ShapeValueBuilder
}
func (builder defaultExamplesBuilder) Imports(a *API) string {
buf := bytes.NewBuffer(nil)
buf.WriteString(`"fmt"
return `"fmt"
"strings"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/session"
`)
buf.WriteString(fmt.Sprintf("\"%s/%s\"", "github.com/aws/aws-sdk-go/service", a.PackageName()))
return buf.String()
"` + SDKImportRoot + `/aws"
"` + SDKImportRoot + `/aws/awserr"
"` + SDKImportRoot + `/aws/session"
"` + a.ImportPath() + `"
`
}
@@ -2,27 +2,19 @@
package api
import (
"bytes"
"fmt"
)
type wafregionalExamplesBuilder struct {
defaultExamplesBuilder
}
func (builder wafregionalExamplesBuilder) Imports(a *API) string {
buf := bytes.NewBuffer(nil)
buf.WriteString(`"fmt"
return `"fmt"
"strings"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/waf"
`)
buf.WriteString(fmt.Sprintf("\"%s/%s\"", "github.com/aws/aws-sdk-go/service", a.PackageName()))
return buf.String()
"` + SDKImportRoot + `/aws"
"` + SDKImportRoot + `/aws/awserr"
"` + SDKImportRoot + `/aws/session"
"` + SDKImportRoot + `/service/waf"
"` + SDKImportRoot + `/service/` + a.PackageName() + `"
`
}
+92 -4
View File
@@ -1,13 +1,34 @@
package api
type persistAPIType struct {
output bool
input bool
}
type persistAPITypes map[string]map[string]persistAPIType
func (ts persistAPITypes) Lookup(serviceName, opName string) persistAPIType {
service, ok := shamelist[serviceName]
if !ok {
return persistAPIType{}
}
return service[opName]
}
func (ts persistAPITypes) Input(serviceName, opName string) bool {
return ts.Lookup(serviceName, opName).input
}
func (ts persistAPITypes) Output(serviceName, opName string) bool {
return ts.Lookup(serviceName, opName).output
}
// shamelist is used to not rename certain operation's input and output shapes.
// We need to maintain backwards compatibility with pre-existing services. Since
// not generating unique input/output shapes is not desired, we will generate
// unique input/output shapes for new operations.
var shamelist = map[string]map[string]struct {
input bool
output bool
}{
var shamelist = persistAPITypes{
"APIGateway": {
"CreateApiKey": {
output: true,
@@ -352,6 +373,11 @@ var shamelist = map[string]map[string]struct {
output: true,
},
},
"ElasticTranscoder": {
"CreateJob": {
output: true,
},
},
"Glacier": {
"DescribeJob": {
output: true,
@@ -431,6 +457,54 @@ var shamelist = map[string]map[string]struct {
output: true,
},
},
"MQ": {
"CreateBroker": {
input: true,
output: true,
},
"CreateConfiguration": {
input: true,
output: true,
},
"CreateUser": {
input: true,
},
"DeleteBroker": {
output: true,
},
"DescribeBroker": {
output: true,
},
"DescribeUser": {
output: true,
},
"DescribeConfigurationRevision": {
output: true,
},
"ListBrokers": {
output: true,
},
"ListConfigurations": {
output: true,
},
"ListConfigurationRevisions": {
output: true,
},
"ListUsers": {
output: true,
},
"UpdateBroker": {
input: true,
output: true,
},
"UpdateConfiguration": {
input: true,
output: true,
},
"UpdateUser": {
input: true,
},
},
"RDS": {
"ModifyDBClusterParameterGroup": {
output: true,
@@ -472,6 +546,20 @@ var shamelist = map[string]map[string]struct {
output: true,
},
},
"ServerlessApplicationRepository": {
"CreateApplication": {
input: true,
},
"CreateApplicationVersion": {
input: true,
},
"CreateCloudFormationChangeSet": {
input: true,
},
"UpdateApplication": {
input: true,
},
},
"SWF": {
"CountClosedWorkflowExecutions": {
output: true,
+140 -6
View File
@@ -7,8 +7,136 @@ import (
"fmt"
"os"
"path/filepath"
"sort"
"strings"
)
// APIs provides a set of API models loaded by API package name.
type APIs map[string]*API
// LoadAPIs loads the API model files from disk returning the map of API
// package. Returns error if multiple API model resolve to the same package
// name.
func LoadAPIs(modelPaths []string, baseImport string) (APIs, error) {
apis := APIs{}
for _, modelPath := range modelPaths {
a, err := loadAPI(modelPath, baseImport)
if err != nil {
return nil, fmt.Errorf("failed to load API, %v, %v", modelPath, err)
}
importPath := a.ImportPath()
if _, ok := apis[importPath]; ok {
return nil, fmt.Errorf(
"package names must be unique attempted to load %v twice. Second model file: %v",
importPath, modelPath)
}
apis[importPath] = a
}
return apis, nil
}
func loadAPI(modelPath, baseImport string) (*API, error) {
a := &API{
BaseImportPath: baseImport,
BaseCrosslinkURL: "https://docs.aws.amazon.com",
}
modelFile := filepath.Base(modelPath)
modelDir := filepath.Dir(modelPath)
err := attachModelFiles(modelDir,
modelLoader{modelFile, a.Attach, true},
modelLoader{"docs-2.json", a.AttachDocs, false},
modelLoader{"paginators-1.json", a.AttachPaginators, false},
modelLoader{"waiters-2.json", a.AttachWaiters, false},
modelLoader{"examples-1.json", a.AttachExamples, false},
modelLoader{"smoke.json", a.AttachSmokeTests, false},
)
if err != nil {
return nil, err
}
a.Setup()
return a, nil
}
type modelLoader struct {
Filename string
Loader func(string)
Required bool
}
func attachModelFiles(modelPath string, modelFiles ...modelLoader) error {
for _, m := range modelFiles {
filepath := filepath.Join(modelPath, m.Filename)
_, err := os.Stat(filepath)
if os.IsNotExist(err) && !m.Required {
continue
} else if err != nil {
return fmt.Errorf("failed to load model file %v, %v", m.Filename, err)
}
m.Loader(filepath)
}
return nil
}
// ExpandModelGlobPath returns a slice of model paths expanded from the glob
// pattern passed in. Returns the path of the model file to be loaded. Includes
// all versions of a service model.
//
// e.g:
// models/apis/*/*/api-2.json
//
// Or with specific model file:
// models/apis/service/version/api-2.json
func ExpandModelGlobPath(globs ...string) ([]string, error) {
modelPaths := []string{}
for _, g := range globs {
filepaths, err := filepath.Glob(g)
if err != nil {
return nil, err
}
for _, p := range filepaths {
modelPaths = append(modelPaths, p)
}
}
return modelPaths, nil
}
// TrimModelServiceVersions sorts the model paths by service version then
// returns recent model versions, and model version excluded.
//
// Uses the third from last path element to determine unique service. Only one
// service version will be included.
//
// models/apis/service/version/api-2.json
func TrimModelServiceVersions(modelPaths []string) (include, exclude []string) {
sort.Strings(modelPaths)
// Remove old API versions from list
m := map[string]struct{}{}
for i := len(modelPaths) - 1; i >= 0; i-- {
// service name is 2nd-to-last component
parts := strings.Split(modelPaths[i], string(filepath.Separator))
svc := parts[len(parts)-3]
if _, ok := m[svc]; ok {
// Removed unused service version
exclude = append(exclude, modelPaths[i])
continue
}
include = append(include, modelPaths[i])
m[svc] = struct{}{}
}
return include, exclude
}
// Attach opens a file by name, and unmarshal its JSON data.
// Will proceed to setup the API if not already done so.
func (a *API) Attach(filename string) {
@@ -35,19 +163,25 @@ func (a *API) AttachString(str string) {
// Setup initializes the API.
func (a *API) Setup() {
a.setServiceAliaseName()
a.setMetadataEndpointsKey()
a.writeShapeNames()
a.resolveReferences()
a.fixStutterNames()
a.renameExportable()
if !a.NoRenameToplevelShapes {
a.renameToplevelShapes()
if !a.NoRemoveUnusedShapes {
a.removeUnusedShapes()
}
a.fixStutterNames()
a.renameExportable()
a.applyShapeNameAliases()
a.createInputOutputShapes()
a.renameAPIPayloadShapes()
a.renameCollidingFields()
a.updateTopLevelShapeReferences()
a.createInputOutputShapes()
a.suppressEventStreams()
a.setupEventStreams()
a.findEndpointDiscoveryOp()
a.injectUnboundedOutputStreaming()
a.customizationPasses()
if !a.NoRemoveUnusedShapes {
+45 -1
View File
@@ -1,8 +1,11 @@
// +build 1.6,codegen
// +build codegen
package api
import (
"path/filepath"
"reflect"
"strconv"
"testing"
)
@@ -30,3 +33,44 @@ func TestResolvedReferences(t *testing.T) {
t.Errorf("Expected %d, but received %d", 2, len(a.Shapes["OtherTest"].refs))
}
}
func TestTrimModelServiceVersions(t *testing.T) {
cases := []struct {
Paths []string
Include []string
Exclude []string
}{
{
Paths: []string{
filepath.Join("foo", "baz", "2018-01-02", "api-2.json"),
filepath.Join("foo", "baz", "2019-01-02", "api-2.json"),
filepath.Join("foo", "baz", "2017-01-02", "api-2.json"),
filepath.Join("foo", "bar", "2019-01-02", "api-2.json"),
filepath.Join("foo", "bar", "2013-04-02", "api-2.json"),
filepath.Join("foo", "bar", "2019-01-03", "api-2.json"),
},
Include: []string{
filepath.Join("foo", "baz", "2019-01-02", "api-2.json"),
filepath.Join("foo", "bar", "2019-01-03", "api-2.json"),
},
Exclude: []string{
filepath.Join("foo", "baz", "2018-01-02", "api-2.json"),
filepath.Join("foo", "baz", "2017-01-02", "api-2.json"),
filepath.Join("foo", "bar", "2019-01-02", "api-2.json"),
filepath.Join("foo", "bar", "2013-04-02", "api-2.json"),
},
},
}
for i, c := range cases {
t.Run(strconv.Itoa(i), func(t *testing.T) {
include, exclude := TrimModelServiceVersions(c.Paths)
if e, a := c.Include, include; !reflect.DeepEqual(e, a) {
t.Errorf("expect include %v, got %v", e, a)
}
if e, a := c.Exclude, exclude; !reflect.DeepEqual(e, a) {
t.Errorf("expect exclude %v, got %v", e, a)
}
})
}
}
+54
View File
@@ -0,0 +1,54 @@
// +build codegen
package api
import (
"fmt"
"io"
"sync"
)
var debugLogger *logger
var initDebugLoggerOnce sync.Once
// logger provides a basic logging
type logger struct {
w io.Writer
}
// LogDebug initialize's the debug logger for the components in the api
// package to log debug lines to.
//
// Panics if called multiple times.
//
// Must be used prior to any model loading or code gen.
func LogDebug(w io.Writer) {
var initialized bool
initDebugLoggerOnce.Do(func() {
debugLogger = &logger{
w: w,
}
initialized = true
})
if !initialized && debugLogger != nil {
panic("LogDebug called multiple times. Can only be called once")
}
}
// Logf logs using the fmt printf pattern. Appends a new line to the end of the
// logged statement.
func (l *logger) Logf(format string, args ...interface{}) {
if l == nil {
return
}
fmt.Fprintf(l.w, format+"\n", args...)
}
// Logln logs using the fmt println pattern.
func (l *logger) Logln(args ...interface{}) {
if l == nil {
return
}
fmt.Fprintln(l.w, args...)
}
+261 -32
View File
@@ -13,18 +13,58 @@ import (
// An Operation defines a specific API Operation.
type Operation struct {
API *API `json:"-"`
ExportedName string
Name string
Documentation string
HTTP HTTPInfo
InputRef ShapeRef `json:"input"`
OutputRef ShapeRef `json:"output"`
ErrorRefs []ShapeRef `json:"errors"`
Paginator *Paginator
Deprecated bool `json:"deprecated"`
AuthType string `json:"authtype"`
imports map[string]bool
API *API `json:"-"`
ExportedName string
Name string
Documentation string
HTTP HTTPInfo
Host string `json:"host"`
InputRef ShapeRef `json:"input"`
OutputRef ShapeRef `json:"output"`
ErrorRefs []ShapeRef `json:"errors"`
Paginator *Paginator
Deprecated bool `json:"deprecated"`
DeprecatedMsg string `json:"deprecatedMessage"`
AuthType AuthType `json:"authtype"`
imports map[string]bool
CustomBuildHandlers []string
EventStreamAPI *EventStreamAPI
IsEndpointDiscoveryOp bool `json:"endpointoperation"`
EndpointDiscovery *EndpointDiscovery `json:"endpointdiscovery"`
Endpoint *EndpointTrait `json:"endpoint"`
}
// EndpointTrait provides the structure of the modeled enpdoint trait, and its
// properties.
type EndpointTrait struct {
// Specifies the hostPrefix template to prepend to the operation's request
// endpoint host.
HostPrefix string `json:"hostPrefix"`
}
// EndpointDiscovery represents a map of key values pairs that represents
// metadata about how a given API will make a call to the discovery endpoint.
type EndpointDiscovery struct {
// Required indicates that for a given operation that endpoint is required.
// Any required endpoint discovery operation cannot have endpoint discovery
// turned off.
Required bool `json:"required"`
}
// OperationForMethod returns the API operation name that corresponds to the
// client method name provided.
func (a *API) OperationForMethod(name string) *Operation {
for _, op := range a.Operations {
for _, m := range op.Methods() {
if m == name {
return op
}
}
}
return nil
}
// A HTTPInfo defines the method of HTTP request for the Operation.
@@ -34,6 +74,24 @@ type HTTPInfo struct {
ResponseCode uint
}
// Methods Returns a list of method names that will be generated.
func (o *Operation) Methods() []string {
methods := []string{
o.ExportedName,
o.ExportedName + "Request",
o.ExportedName + "WithContext",
}
if o.Paginator != nil {
methods = append(methods, []string{
o.ExportedName + "Pages",
o.ExportedName + "PagesWithContext",
}...)
}
return methods
}
// HasInput returns if the Operation accepts an input paramater
func (o *Operation) HasInput() bool {
return o.InputRef.ShapeName != ""
@@ -44,17 +102,27 @@ func (o *Operation) HasOutput() bool {
return o.OutputRef.ShapeName != ""
}
func (o *Operation) GetSigner() string {
if o.AuthType == "v4-unsigned-body" {
o.API.imports["github.com/aws/aws-sdk-go/aws/signer/v4"] = true
}
// AuthType provides the enumeration of AuthType trait.
type AuthType string
// Enumeration values for AuthType trait
const (
NoneAuthType AuthType = "none"
V4UnsignedBodyAuthType AuthType = "v4-unsigned-body"
)
// GetSigner returns the signer that should be used for a API request.
func (o *Operation) GetSigner() string {
buf := bytes.NewBuffer(nil)
switch o.AuthType {
case "none":
case NoneAuthType:
o.API.AddSDKImport("aws/credentials")
buf.WriteString("req.Config.Credentials = credentials.AnonymousCredentials")
case "v4-unsigned-body":
case V4UnsignedBodyAuthType:
o.API.AddSDKImport("aws/signer/v4")
buf.WriteString("req.Handlers.Sign.Remove(v4.SignRequestHandler)\n")
buf.WriteString("handler := v4.BuildNamedHandler(\"v4.CustomSignerHandler\", v4.WithUnsignedPayload)\n")
buf.WriteString("req.Handlers.Sign.PushFrontNamed(handler)")
@@ -64,16 +132,18 @@ func (o *Operation) GetSigner() string {
return buf.String()
}
// tplOperation defines a template for rendering an API Operation
var tplOperation = template.Must(template.New("operation").Funcs(template.FuncMap{
"GetCrosslinkURL": GetCrosslinkURL,
// operationTmpl defines a template for rendering an API Operation
var operationTmpl = template.Must(template.New("operation").Funcs(template.FuncMap{
"GetCrosslinkURL": GetCrosslinkURL,
"EnableStopOnSameToken": enableStopOnSameToken,
"GetDeprecatedMsg": getDeprecatedMessage,
}).Parse(`
const op{{ .ExportedName }} = "{{ .Name }}"
// {{ .ExportedName }}Request generates a "aws/request.Request" representing the
// client's request for the {{ .ExportedName }} operation. The "output" return
// value will be populated with the request's response once the request completes
// successfuly.
// successfully.
//
// Use "Send" method on the returned Request to send the API call to the service.
// the "output" return value is not valid until after Send returns without error.
@@ -93,16 +163,19 @@ const op{{ .ExportedName }} = "{{ .Name }}"
// fmt.Println(resp)
// }
{{ $crosslinkURL := GetCrosslinkURL $.API.BaseCrosslinkURL $.API.Metadata.UID $.ExportedName -}}
{{ if ne $crosslinkURL "" -}}
{{ if ne $crosslinkURL "" -}}
//
// See also, {{ $crosslinkURL }}
{{ end -}}
{{- if .Deprecated }}//
// Deprecated: {{ GetDeprecatedMsg .DeprecatedMsg .ExportedName }}
{{ end -}}
func (c *{{ .API.StructName }}) {{ .ExportedName }}Request(` +
`input {{ .InputRef.GoType }}) (req *request.Request, output {{ .OutputRef.GoType }}) {
{{ if (or .Deprecated (or .InputRef.Deprecated .OutputRef.Deprecated)) }}if c.Client.Config.Logger != nil {
c.Client.Config.Logger.Log("This operation, {{ .ExportedName }}, has been deprecated")
}
op := &request.Operation{ {{ else }} op := &request.Operation{ {{ end }}
op := &request.Operation{ {{ else }} op := &request.Operation{ {{ end }}
Name: op{{ .ExportedName }},
{{ if ne .HTTP.Method "" }}HTTPMethod: "{{ .HTTP.Method }}",
{{ end }}HTTPPath: {{ if ne .HTTP.RequestURI "" }}"{{ .HTTP.RequestURI }}"{{ else }}"/"{{ end }},
@@ -120,10 +193,57 @@ func (c *{{ .API.StructName }}) {{ .ExportedName }}Request(` +
}
output = &{{ .OutputRef.GoTypeElem }}{}
req = c.newRequest(op, input, output){{ if eq .OutputRef.Shape.Placeholder true }}
req.Handlers.Unmarshal.Remove({{ .API.ProtocolPackage }}.UnmarshalHandler)
req.Handlers.Unmarshal.PushBackNamed(protocol.UnmarshalDiscardBodyHandler){{ end }}
{{ if ne .AuthType "" }}{{ .GetSigner }}{{ end -}}
req = c.newRequest(op, input, output)
{{ if ne .AuthType "" }}{{ .GetSigner }}{{ end }}
{{- if .ShouldDiscardResponse -}}
{{- $_ := .API.AddSDKImport "private/protocol" -}}
{{- $_ := .API.AddSDKImport "private/protocol" .API.ProtocolPackage -}}
req.Handlers.Unmarshal.Swap({{ .API.ProtocolPackage }}.UnmarshalHandler.Name, protocol.UnmarshalDiscardBodyHandler)
{{ else if .OutputRef.Shape.EventStreamsMemberName -}}
{{- $_ := .API.AddSDKImport "private/protocol" .API.ProtocolPackage -}}
{{- $_ := .API.AddSDKImport "private/protocol/rest" -}}
req.Handlers.Send.Swap(client.LogHTTPResponseHandler.Name, client.LogHTTPResponseHeaderHandler)
req.Handlers.Unmarshal.Swap({{ .API.ProtocolPackage }}.UnmarshalHandler.Name, rest.UnmarshalHandler)
req.Handlers.Unmarshal.PushBack(output.runEventStreamLoop)
{{ if eq .API.Metadata.Protocol "json" -}}
req.Handlers.Unmarshal.PushBack(output.unmarshalInitialResponse)
{{ end -}}
{{ end -}}
{{ if .EndpointDiscovery -}}
{{if not .EndpointDiscovery.Required -}}
if aws.BoolValue(req.Config.EnableEndpointDiscovery) {
{{end -}}
de := discoverer{{ .API.EndpointDiscoveryOp.Name }}{
Required: {{ .EndpointDiscovery.Required }},
EndpointCache: c.endpointCache,
Params: map[string]*string{
"op": aws.String(req.Operation.Name),
{{ range $key, $ref := .InputRef.Shape.MemberRefs -}}
{{ if $ref.EndpointDiscoveryID -}}
"{{ $ref.OrigShapeName }}": input.{{ $key }},
{{ end -}}
{{- end }}
},
Client: c,
}
for k, v := range de.Params {
if v == nil {
delete(de.Params, k)
}
}
req.Handlers.Build.PushFrontNamed(request.NamedHandler{
Name: "crr.endpointdiscovery",
Fn: de.Handler,
})
{{if not .EndpointDiscovery.Required -}}
}
{{ end -}}
{{ end -}}
{{- range $_, $handler := $.CustomBuildHandlers -}}
req.Handlers.Build.PushBackNamed({{ $handler }})
{{ end -}}
return
}
@@ -151,9 +271,12 @@ func (c *{{ .API.StructName }}) {{ .ExportedName }}Request(` +
{{ end -}}
{{ end -}}
{{ $crosslinkURL := GetCrosslinkURL $.API.BaseCrosslinkURL $.API.Metadata.UID $.ExportedName -}}
{{ if ne $crosslinkURL "" -}}
{{ if ne $crosslinkURL "" -}}
// See also, {{ $crosslinkURL }}
{{ end -}}
{{- if .Deprecated }}//
// Deprecated: {{ GetDeprecatedMsg .DeprecatedMsg .ExportedName }}
{{ end -}}
func (c *{{ .API.StructName }}) {{ .ExportedName }}(` +
`input {{ .InputRef.GoType }}) ({{ .OutputRef.GoType }}, error) {
req, out := c.{{ .ExportedName }}Request(input)
@@ -169,6 +292,9 @@ func (c *{{ .API.StructName }}) {{ .ExportedName }}(` +
// the context is nil a panic will occur. In the future the SDK may create
// sub-contexts for http.Requests. See https://golang.org/pkg/context/
// for more information on using Contexts.
{{ if .Deprecated }}//
// Deprecated: {{ GetDeprecatedMsg .DeprecatedMsg (printf "%s%s" .ExportedName "WithContext") }}
{{ end -}}
func (c *{{ .API.StructName }}) {{ .ExportedName }}WithContext(` +
`ctx aws.Context, input {{ .InputRef.GoType }}, opts ...request.Option) ` +
`({{ .OutputRef.GoType }}, error) {
@@ -190,12 +316,15 @@ func (c *{{ .API.StructName }}) {{ .ExportedName }}WithContext(` +
// // Example iterating over at most 3 pages of a {{ .ExportedName }} operation.
// pageNum := 0
// err := client.{{ .ExportedName }}Pages(params,
// func(page {{ .OutputRef.GoType }}, lastPage bool) bool {
// func(page {{ .OutputRef.Shape.GoTypeWithPkgName }}, lastPage bool) bool {
// pageNum++
// fmt.Println(page)
// return pageNum <= 3
// })
//
{{ if .Deprecated }}//
// Deprecated: {{ GetDeprecatedMsg .DeprecatedMsg (printf "%s%s" .ExportedName "Pages") }}
{{ end -}}
func (c *{{ .API.StructName }}) {{ .ExportedName }}Pages(` +
`input {{ .InputRef.GoType }}, fn func({{ .OutputRef.GoType }}, bool) bool) error {
return c.{{ .ExportedName }}PagesWithContext(aws.BackgroundContext(), input, fn)
@@ -208,12 +337,17 @@ func (c *{{ .API.StructName }}) {{ .ExportedName }}Pages(` +
// the context is nil a panic will occur. In the future the SDK may create
// sub-contexts for http.Requests. See https://golang.org/pkg/context/
// for more information on using Contexts.
{{ if .Deprecated }}//
// Deprecated: {{ GetDeprecatedMsg .DeprecatedMsg (printf "%s%s" .ExportedName "PagesWithContext") }}
{{ end -}}
func (c *{{ .API.StructName }}) {{ .ExportedName }}PagesWithContext(` +
`ctx aws.Context, ` +
`input {{ .InputRef.GoType }}, ` +
`fn func({{ .OutputRef.GoType }}, bool) bool, ` +
`opts ...request.Option) error {
p := request.Pagination {
{{ if EnableStopOnSameToken .API.PackageName -}}EndPageOnSameToken: true,
{{ end -}}
NewRequest: func() (*request.Request, error) {
var inCpy {{ .InputRef.GoType }}
if input != nil {
@@ -234,12 +368,100 @@ func (c *{{ .API.StructName }}) {{ .ExportedName }}PagesWithContext(` +
return p.Err()
}
{{ end }}
{{ if .IsEndpointDiscoveryOp -}}
type discoverer{{ .ExportedName }} struct {
Client *{{ .API.StructName }}
Required bool
EndpointCache *crr.EndpointCache
Params map[string]*string
Key string
}
func (d *discoverer{{ .ExportedName }}) Discover() (crr.Endpoint, error) {
input := &{{ .API.EndpointDiscoveryOp.InputRef.ShapeName }}{
{{ if .API.EndpointDiscoveryOp.InputRef.Shape.HasMember "Operation" -}}
Operation: d.Params["op"],
{{ end -}}
{{ if .API.EndpointDiscoveryOp.InputRef.Shape.HasMember "Identifiers" -}}
Identifiers: d.Params,
{{ end -}}
}
resp, err := d.Client.{{ .API.EndpointDiscoveryOp.Name }}(input)
if err != nil {
return crr.Endpoint{}, err
}
endpoint := crr.Endpoint{
Key: d.Key,
}
for _, e := range resp.Endpoints {
if e.Address == nil {
continue
}
cachedInMinutes := aws.Int64Value(e.CachePeriodInMinutes)
u, err := url.Parse(*e.Address)
if err != nil {
continue
}
addr := crr.WeightedAddress{
URL: u,
Expired: time.Now().Add(time.Duration(cachedInMinutes) * time.Minute),
}
endpoint.Add(addr)
}
d.EndpointCache.Add(endpoint)
return endpoint, nil
}
func (d *discoverer{{ .ExportedName }}) Handler(r *request.Request) {
endpointKey := crr.BuildEndpointKey(d.Params)
d.Key = endpointKey
endpoint, err := d.EndpointCache.Get(d, endpointKey, d.Required)
if err != nil {
r.Error = err
return
}
if endpoint.URL != nil && len(endpoint.URL.String()) > 0 {
r.HTTPRequest.URL = endpoint.URL
}
}
{{ end -}}
`))
// GoCode returns a string of rendered GoCode for this Operation
func (o *Operation) GoCode() string {
var buf bytes.Buffer
err := tplOperation.Execute(&buf, o)
if len(o.OutputRef.Shape.EventStreamsMemberName) != 0 {
o.API.AddSDKImport("aws/client")
o.API.AddSDKImport("private/protocol")
o.API.AddSDKImport("private/protocol/rest")
o.API.AddSDKImport("private/protocol", o.API.ProtocolPackage())
}
if o.API.EndpointDiscoveryOp != nil {
o.API.AddSDKImport("aws/crr")
o.API.AddImport("time")
o.API.AddImport("net/url")
}
if o.Endpoint != nil && len(o.Endpoint.HostPrefix) != 0 {
setupEndpointHostPrefix(o)
}
err := operationTmpl.Execute(&buf, o)
if err != nil {
panic(err)
}
@@ -308,7 +530,7 @@ func (o *Operation) Example() string {
func (o *Operation) ExampleInput() string {
if len(o.InputRef.Shape.MemberRefs) == 0 {
if strings.Contains(o.InputRef.GoTypeElem(), ".") {
o.imports["github.com/aws/aws-sdk-go/service/"+strings.Split(o.InputRef.GoTypeElem(), ".")[0]] = true
o.imports[SDKImportRoot+"service/"+strings.Split(o.InputRef.GoTypeElem(), ".")[0]] = true
return fmt.Sprintf("var params *%s", o.InputRef.GoTypeElem())
}
return fmt.Sprintf("var params *%s.%s",
@@ -318,6 +540,13 @@ func (o *Operation) ExampleInput() string {
return "params := " + e.traverseAny(o.InputRef.Shape, false, false)
}
// ShouldDiscardResponse returns if the operation should discard the response
// returned by the service.
func (o *Operation) ShouldDiscardResponse() bool {
s := o.OutputRef.Shape
return s.Placeholder || len(s.MemberRefs) == 0
}
// A example provides
type example struct {
*Operation
+9
View File
@@ -89,3 +89,12 @@ func (p *paginationDefinitions) setup() {
}
}
}
func enableStopOnSameToken(service string) bool {
switch service {
case "cloudwatchlogs":
return true
default:
return false
}
}
+2 -2
View File
@@ -3,8 +3,8 @@
package api
import (
"fmt"
"encoding/json"
"fmt"
"reflect"
"strings"
@@ -83,7 +83,7 @@ func (f paramFiller) paramsStructAny(value interface{}, shape *Shape) string {
case "jsonvalue":
v, err := json.Marshal(value)
if err != nil {
panic("failed to marshal JSONValue, "+err.Error())
panic("failed to marshal JSONValue, " + err.Error())
}
const tmpl = `func() aws.JSONValue {
var m aws.JSONValue
+115 -61
View File
@@ -58,7 +58,8 @@ func (a *API) resolveReferences() {
// Resolve references for errors also
for i := range o.ErrorRefs {
resolver.resolveReference(&o.ErrorRefs[i])
o.ErrorRefs[i].Shape.IsError = true
o.ErrorRefs[i].Shape.Exception = true
o.ErrorRefs[i].Shape.ErrorInfo.Type = o.ErrorRefs[i].Shape.ShapeName
}
}
}
@@ -70,14 +71,6 @@ type referenceResolver struct {
visited map[*ShapeRef]bool
}
var jsonvalueShape = &Shape{
ShapeName: "JSONValue",
Type: "jsonvalue",
ValueRef: ShapeRef{
JSONValue: true,
},
}
// resolveReference updates a shape reference to reference the API and
// its shape definition. All other nested references are also resolved.
func (r *referenceResolver) resolveReference(ref *ShapeRef) {
@@ -88,12 +81,20 @@ func (r *referenceResolver) resolveReference(ref *ShapeRef) {
shape, ok := r.API.Shapes[ref.ShapeName]
if !ok {
panic(fmt.Sprintf("unable resolve reference, %s", ref.ShapeName))
return
}
if ref.JSONValue {
ref.ShapeName = "JSONValue"
r.API.Shapes[ref.ShapeName] = jsonvalueShape
if _, ok := r.API.Shapes[ref.ShapeName]; !ok {
r.API.Shapes[ref.ShapeName] = &Shape{
API: r.API,
ShapeName: "JSONValue",
Type: "jsonvalue",
ValueRef: ShapeRef{
JSONValue: true,
},
}
}
}
ref.API = r.API // resolve reference back to API
@@ -121,40 +122,6 @@ func (r *referenceResolver) resolveShape(shape *Shape) {
}
}
// renameToplevelShapes renames all top level shapes of an API to their
// exportable variant. The shapes are also updated to include notations
// if they are Input or Outputs.
func (a *API) renameToplevelShapes() {
for _, v := range a.OperationList() {
if v.HasInput() {
name := v.ExportedName + "Input"
switch {
case a.Shapes[name] == nil:
if service, ok := shamelist[a.name]; ok {
if check, ok := service[v.Name]; ok && check.input {
break
}
}
v.InputRef.Shape.Rename(name)
}
}
if v.HasOutput() {
name := v.ExportedName + "Output"
switch {
case a.Shapes[name] == nil:
if service, ok := shamelist[a.name]; ok {
if check, ok := service[v.Name]; ok && check.output {
break
}
}
v.OutputRef.Shape.Rename(name)
}
}
v.InputRef.Payload = a.ExportableName(v.InputRef.Payload)
v.OutputRef.Payload = a.ExportableName(v.OutputRef.Payload)
}
}
// fixStutterNames fixes all name struttering based on Go naming conventions.
// "Stuttering" is when the prefix of a structure or function matches the
// package name (case insensitive).
@@ -205,6 +172,10 @@ func (a *API) renameExportable() {
}
for mName, member := range s.MemberRefs {
ref := s.MemberRefs[mName]
ref.OrigShapeName = mName
s.MemberRefs[mName] = ref
newName := a.ExportableName(mName)
if newName != mName {
delete(s.MemberRefs, mName)
@@ -254,28 +225,26 @@ func (a *API) renameCollidingFields() {
for _, v := range a.Shapes {
namesWithSet := map[string]struct{}{}
for k, field := range v.MemberRefs {
if strings.HasPrefix(k, "Set") {
namesWithSet[k] = struct{}{}
if _, ok := v.MemberRefs["Set"+k]; ok {
namesWithSet["Set"+k] = struct{}{}
}
if collides(k) {
if collides(k) || (v.Exception && exceptionCollides(k)) {
renameCollidingField(k, v, field)
}
}
// checks if any field names collide with setters.
for name := range namesWithSet {
if field, ok := v.MemberRefs["Set"+name]; ok {
renameCollidingField(name, v, field)
}
field := v.MemberRefs[name]
renameCollidingField(name, v, field)
}
}
}
func renameCollidingField(name string, v *Shape, field *ShapeRef) {
newName := name + "_"
fmt.Printf("Shape %s's field %q renamed to %q\n", v.ShapeName, name, newName)
debugLogger.Logf("Shape %s's field %q renamed to %q", v.ShapeName, name, newName)
delete(v.MemberRefs, name)
v.MemberRefs[newName] = field
}
@@ -287,8 +256,32 @@ func collides(name string) bool {
"GoString",
"Validate":
return true
default:
return false
}
return false
}
func exceptionCollides(name string) bool {
switch name {
case "Code",
"Message",
"OrigErr":
return true
}
return false
}
func (a *API) applyShapeNameAliases() {
service, ok := shapeNameAliases[a.name]
if !ok {
return
}
// Generic Shape Aliases
for name, s := range a.Shapes {
if alias, ok := service[name]; ok {
s.Rename(alias)
s.AliasedShapeName = true
}
}
}
@@ -297,15 +290,47 @@ func collides(name string) bool {
// have an input and output structure in the signature.
func (a *API) createInputOutputShapes() {
for _, op := range a.Operations {
if !op.HasInput() {
setAsPlacholderShape(&op.InputRef, op.ExportedName+"Input", a)
}
if !op.HasOutput() {
setAsPlacholderShape(&op.OutputRef, op.ExportedName+"Output", a)
}
createAPIParamShape(a, op.Name, &op.InputRef, op.ExportedName+"Input",
shamelist.Input,
)
createAPIParamShape(a, op.Name, &op.OutputRef, op.ExportedName+"Output",
shamelist.Output,
)
}
}
func (a *API) renameAPIPayloadShapes() {
for _, op := range a.Operations {
op.InputRef.Payload = a.ExportableName(op.InputRef.Payload)
op.OutputRef.Payload = a.ExportableName(op.OutputRef.Payload)
}
}
func createAPIParamShape(a *API, opName string, ref *ShapeRef, shapeName string, shamelistLookup func(string, string) bool) {
if len(ref.ShapeName) == 0 {
setAsPlacholderShape(ref, shapeName, a)
return
}
// nothing to do if already the correct name.
if s := ref.Shape; s.AliasedShapeName || s.ShapeName == shapeName || shamelistLookup(a.name, opName) {
return
}
if s, ok := a.Shapes[shapeName]; ok {
panic(fmt.Sprintf(
"attempting to create duplicate API parameter shape, %v, %v, %v, %v\n",
shapeName, opName, ref.ShapeName, s.OrigShapeName,
))
}
ref.Shape.removeRef(ref)
ref.OrigShapeName = shapeName
ref.ShapeName = shapeName
ref.Shape = ref.Shape.Clone(shapeName)
ref.Shape.refs = append(ref.Shape.refs, ref)
}
func setAsPlacholderShape(tgtShapeRef *ShapeRef, name string, a *API) {
shape := a.makeIOShape(name)
shape.Placeholder = true
@@ -352,3 +377,32 @@ func (a *API) setMetadataEndpointsKey() {
a.Metadata.EndpointsID = a.Metadata.EndpointPrefix
}
}
func (a *API) findEndpointDiscoveryOp() {
for _, op := range a.Operations {
if op.IsEndpointDiscoveryOp {
a.EndpointDiscoveryOp = op
return
}
}
}
func (a *API) injectUnboundedOutputStreaming() {
for _, op := range a.Operations {
if op.AuthType != V4UnsignedBodyAuthType {
continue
}
for _, ref := range op.InputRef.Shape.MemberRefs {
if ref.Streaming || ref.Shape.Streaming {
if len(ref.Documentation) != 0 {
ref.Documentation += `
//`
}
ref.Documentation += `
// To use an non-seekable io.Reader for this request wrap the io.Reader with
// "aws.ReadSeekCloser". The SDK will not retry request errors for non-seekable
// readers. This will allow the SDK to send the reader's payload as chunked
// transfer encoding.`
}
}
}
}
+461 -111
View File
@@ -1,35 +1,35 @@
// +build 1.6,codegen
// +build go1.8,codegen
package api
import (
"reflect"
"strconv"
"testing"
)
func TestUniqueInputAndOutputs(t *testing.T) {
shamelist["FooService"] = map[string]struct {
input bool
output bool
}{}
v := shamelist["FooService"]["OpOutputNoRename"]
v.output = true
shamelist["FooService"]["OpOutputNoRename"] = v
v = shamelist["FooService"]["InputNoRename"]
v.input = true
shamelist["FooService"]["OpInputNoRename"] = v
v = shamelist["FooService"]["BothNoRename"]
v.input = true
v.output = true
shamelist["FooService"]["OpBothNoRename"] = v
const serviceName = "FooService"
shamelist[serviceName] = map[string]persistAPIType{
"OpOutputNoRename": {
output: true,
},
"OpInputNoRename": {
input: true,
},
"OpBothNoRename": {
input: true,
output: true,
},
}
cases := [][]struct {
expectedInput string
expectedOutput string
operation string
input string
inputRef string
output string
outputRef string
}{
{
{
@@ -37,18 +37,14 @@ func TestUniqueInputAndOutputs(t *testing.T) {
expectedOutput: "FooOperationOutput",
operation: "FooOperation",
input: "FooInputShape",
inputRef: "FooInputShapeRef",
output: "FooOutputShape",
outputRef: "FooOutputShapeRef",
},
{
expectedInput: "BarOperationInput",
expectedOutput: "BarOperationOutput",
operation: "BarOperation",
input: "FooInputShape",
inputRef: "FooInputShapeRef",
output: "FooOutputShape",
outputRef: "FooOutputShapeRef",
},
},
{
@@ -57,18 +53,14 @@ func TestUniqueInputAndOutputs(t *testing.T) {
expectedOutput: "FooOperationOutput",
operation: "FooOperation",
input: "FooInputShape",
inputRef: "FooInputShapeRef",
output: "FooOutputShape",
outputRef: "FooOutputShapeRef",
},
{
expectedInput: "OpOutputNoRenameInput",
expectedOutput: "OpOutputNoRenameOutputShape",
operation: "OpOutputNoRename",
input: "OpOutputNoRenameInputShape",
inputRef: "OpOutputNoRenameInputRef",
output: "OpOutputNoRenameOutputShape",
outputRef: "OpOutputNoRenameOutputRef",
},
},
{
@@ -77,18 +69,14 @@ func TestUniqueInputAndOutputs(t *testing.T) {
expectedOutput: "FooOperationOutput",
operation: "FooOperation",
input: "FooInputShape",
inputRef: "FooInputShapeRef",
output: "FooOutputShape",
outputRef: "FooOutputShapeRef",
},
{
expectedInput: "OpInputNoRenameInputShape",
expectedOutput: "OpInputNoRenameOutput",
operation: "OpInputNoRename",
input: "OpInputNoRenameInputShape",
inputRef: "OpInputNoRenameInputRef",
output: "OpInputNoRenameOutputShape",
outputRef: "OpInputNoRenameOutputRef",
},
},
{
@@ -97,115 +85,477 @@ func TestUniqueInputAndOutputs(t *testing.T) {
expectedOutput: "FooOperationOutput",
operation: "FooOperation",
input: "FooInputShape",
inputRef: "FooInputShapeRef",
output: "FooOutputShape",
outputRef: "FooOutputShapeRef",
},
{
expectedInput: "OpInputNoRenameInputShape",
expectedOutput: "OpInputNoRenameOutputShape",
operation: "OpBothNoRename",
input: "OpInputNoRenameInputShape",
inputRef: "OpInputNoRenameInputRef",
output: "OpInputNoRenameOutputShape",
outputRef: "OpInputNoRenameOutputRef",
},
},
}
for _, c := range cases {
a := &API{
name: "FooService",
Operations: map[string]*Operation{},
}
expected := map[string][]string{}
a.Shapes = map[string]*Shape{}
for _, op := range c {
a.Operations[op.operation] = &Operation{
ExportedName: op.operation,
}
a.Operations[op.operation].Name = op.operation
a.Operations[op.operation].InputRef = ShapeRef{
API: a,
ShapeName: op.inputRef,
Shape: &Shape{
API: a,
ShapeName: op.input,
},
}
a.Operations[op.operation].OutputRef = ShapeRef{
API: a,
ShapeName: op.outputRef,
Shape: &Shape{
API: a,
ShapeName: op.output,
},
for i, c := range cases {
t.Run(strconv.Itoa(i), func(t *testing.T) {
a := &API{
name: serviceName,
Operations: map[string]*Operation{},
Shapes: map[string]*Shape{},
}
a.Shapes[op.input] = &Shape{
ShapeName: op.input,
}
a.Shapes[op.output] = &Shape{
ShapeName: op.output,
expected := map[string][]string{}
for _, op := range c {
o := &Operation{
Name: op.operation,
ExportedName: op.operation,
InputRef: ShapeRef{
API: a,
ShapeName: op.input,
Shape: &Shape{
API: a,
ShapeName: op.input,
},
},
OutputRef: ShapeRef{
API: a,
ShapeName: op.input,
Shape: &Shape{
API: a,
ShapeName: op.input,
},
},
}
o.InputRef.Shape.refs = append(o.InputRef.Shape.refs, &o.InputRef)
o.OutputRef.Shape.refs = append(o.OutputRef.Shape.refs, &o.OutputRef)
a.Operations[o.Name] = o
a.Shapes[op.input] = o.InputRef.Shape
a.Shapes[op.output] = o.OutputRef.Shape
expected[op.operation] = append(expected[op.operation],
op.expectedInput,
op.expectedOutput,
)
}
expected[op.operation] = append(expected[op.operation], op.expectedInput)
expected[op.operation] = append(expected[op.operation], op.expectedOutput)
}
a.fixStutterNames()
a.renameToplevelShapes()
for k, v := range expected {
if a.Operations[k].InputRef.Shape.ShapeName != v[0] {
t.Errorf("Error %d case: Expected %q, but received %q", k, v[0], a.Operations[k].InputRef.Shape.ShapeName)
a.fixStutterNames()
a.applyShapeNameAliases()
a.createInputOutputShapes()
for k, v := range expected {
if a.Operations[k].InputRef.Shape.ShapeName != v[0] {
t.Errorf("Error %s case: Expected %q, but received %q", k, v[0], a.Operations[k].InputRef.Shape.ShapeName)
}
if a.Operations[k].OutputRef.Shape.ShapeName != v[1] {
t.Errorf("Error %s case: Expected %q, but received %q", k, v[1], a.Operations[k].OutputRef.Shape.ShapeName)
}
}
if a.Operations[k].OutputRef.Shape.ShapeName != v[1] {
t.Errorf("Error %d case: Expected %q, but received %q", k, v[1], a.Operations[k].OutputRef.Shape.ShapeName)
}
}
})
}
}
func TestCollidingFields(t *testing.T) {
cases := []struct {
api *API
expected []*Shapes
cases := map[string]struct {
MemberRefs map[string]*ShapeRef
Expect []string
IsException bool
}{
{
&API{
name: "FooService",
Shapes: []*Shapes{
{
MemberRefs: map[string]*ShapeRef{
"String": &ShapeRef{},
"GoString": &ShapeRef{},
"Validate": &ShapeRef{},
"Foo": &ShapeRef{},
"SetFoo": &ShapeRef{},
},
},
},
"SimpleMembers": {
MemberRefs: map[string]*ShapeRef{
"Code": {},
"Foo": {},
"GoString": {},
"Message": {},
"OrigErr": {},
"SetFoo": {},
"String": {},
"Validate": {},
},
[]*Shapes{
{
MemberRefs: map[string]*ShapeRef{
"String_": &ShapeRef{},
"GoString_": &ShapeRef{},
"Validate_": &ShapeRef{},
"Foo": &ShapeRef{},
"SetFoo_": &ShapeRef{},
},
},
Expect: []string{
"Code",
"Foo",
"GoString_",
"Message",
"OrigErr",
"SetFoo_",
"String_",
"Validate_",
},
},
"ExceptionShape": {
IsException: true,
MemberRefs: map[string]*ShapeRef{
"Code": {},
"Message": {},
"OrigErr": {},
"Other": {},
"String": {},
},
Expect: []string{
"Code_",
"Message_",
"OrigErr_",
"Other",
"String_",
},
},
}
for _, c := range testCases {
c.api.renameCollidingFields()
if !reflect.DeepEqual(c.api.Shapes, c.expected) {
t.Errorf("expected %v, but received %v", c.expected, c.api.Shapes)
}
for k, c := range cases {
t.Run(k, func(t *testing.T) {
a := &API{
Shapes: map[string]*Shape{
"shapename": {
ShapeName: k,
MemberRefs: c.MemberRefs,
Exception: c.IsException,
},
},
}
a.renameCollidingFields()
for i, name := range a.Shapes["shapename"].MemberNames() {
if e, a := c.Expect[i], name; e != a {
t.Errorf("expect %v, got %v", e, a)
}
}
})
}
}
func TestCreateInputOutputShapes(t *testing.T) {
meta := Metadata{
APIVersion: "0000-00-00",
EndpointPrefix: "rpcservice",
JSONVersion: "1.1",
Protocol: "json",
ServiceAbbreviation: "RPCService",
ServiceFullName: "RPC Service",
ServiceID: "RPCService",
SignatureVersion: "v4",
TargetPrefix: "RPCService_00000000",
UID: "RPCService-0000-00-00",
}
type OpExpect struct {
Input string
Output string
}
cases := map[string]struct {
API *API
ExpectOps map[string]OpExpect
ExpectShapes []string
}{
"allRename": {
API: &API{Metadata: meta,
Operations: map[string]*Operation{
"FirstOp": {Name: "FirstOp",
InputRef: ShapeRef{ShapeName: "FirstOpRequest"},
OutputRef: ShapeRef{ShapeName: "FirstOpResponse"},
},
"SecondOp": {Name: "SecondOp",
InputRef: ShapeRef{ShapeName: "SecondOpRequest"},
OutputRef: ShapeRef{ShapeName: "SecondOpResponse"},
},
},
Shapes: map[string]*Shape{
"FirstOpRequest": {ShapeName: "FirstOpRequest", Type: "structure"},
"FirstOpResponse": {ShapeName: "FirstOpResponse", Type: "structure"},
"SecondOpRequest": {ShapeName: "SecondOpRequest", Type: "structure"},
"SecondOpResponse": {ShapeName: "SecondOpResponse", Type: "structure"},
},
},
ExpectOps: map[string]OpExpect{
"FirstOp": {
Input: "FirstOpInput",
Output: "FirstOpOutput",
},
"SecondOp": {
Input: "SecondOpInput",
Output: "SecondOpOutput",
},
},
ExpectShapes: []string{
"FirstOpInput", "FirstOpOutput",
"SecondOpInput", "SecondOpOutput",
},
},
"noRename": {
API: &API{Metadata: meta,
Operations: map[string]*Operation{
"FirstOp": {Name: "FirstOp",
InputRef: ShapeRef{ShapeName: "FirstOpInput"},
OutputRef: ShapeRef{ShapeName: "FirstOpOutput"},
},
"SecondOp": {Name: "SecondOp",
InputRef: ShapeRef{ShapeName: "SecondOpInput"},
OutputRef: ShapeRef{ShapeName: "SecondOpOutput"},
},
},
Shapes: map[string]*Shape{
"FirstOpInput": {ShapeName: "FirstOpInput", Type: "structure"},
"FirstOpOutput": {ShapeName: "FirstOpOutput", Type: "structure"},
"SecondOpInput": {ShapeName: "SecondOpInput", Type: "structure"},
"SecondOpOutput": {ShapeName: "SecondOpOutput", Type: "structure"},
},
},
ExpectOps: map[string]OpExpect{
"FirstOp": {
Input: "FirstOpInput",
Output: "FirstOpOutput",
},
"SecondOp": {
Input: "SecondOpInput",
Output: "SecondOpOutput",
},
},
ExpectShapes: []string{
"FirstOpInput", "FirstOpOutput",
"SecondOpInput", "SecondOpOutput",
},
},
"renameWithNested": {
API: &API{Metadata: meta,
Operations: map[string]*Operation{
"FirstOp": {Name: "FirstOp",
InputRef: ShapeRef{ShapeName: "FirstOpWriteMe"},
OutputRef: ShapeRef{ShapeName: "FirstOpReadMe"},
},
"SecondOp": {Name: "SecondOp",
InputRef: ShapeRef{ShapeName: "SecondOpWriteMe"},
OutputRef: ShapeRef{ShapeName: "SecondOpReadMe"},
},
},
Shapes: map[string]*Shape{
"FirstOpWriteMe": {ShapeName: "FirstOpWriteMe", Type: "structure",
MemberRefs: map[string]*ShapeRef{
"Foo": {ShapeName: "String"},
},
},
"FirstOpReadMe": {ShapeName: "FirstOpReadMe", Type: "structure",
MemberRefs: map[string]*ShapeRef{
"Bar": {ShapeName: "Struct"},
"Once": {ShapeName: "Once"},
},
},
"SecondOpWriteMe": {ShapeName: "SecondOpWriteMe", Type: "structure"},
"SecondOpReadMe": {ShapeName: "SecondOpReadMe", Type: "structure"},
"Once": {ShapeName: "Once", Type: "string"},
"String": {ShapeName: "String", Type: "string"},
"Struct": {ShapeName: "Struct", Type: "structure",
MemberRefs: map[string]*ShapeRef{
"Foo": {ShapeName: "String"},
"Bar": {ShapeName: "Struct"},
},
},
},
},
ExpectOps: map[string]OpExpect{
"FirstOp": {
Input: "FirstOpInput",
Output: "FirstOpOutput",
},
"SecondOp": {
Input: "SecondOpInput",
Output: "SecondOpOutput",
},
},
ExpectShapes: []string{
"FirstOpInput", "FirstOpOutput",
"Once",
"SecondOpInput", "SecondOpOutput",
"String", "Struct",
},
},
"aliasedInput": {
API: &API{Metadata: meta,
Operations: map[string]*Operation{
"FirstOp": {Name: "FirstOp",
InputRef: ShapeRef{ShapeName: "FirstOpRequest"},
OutputRef: ShapeRef{ShapeName: "FirstOpResponse"},
},
},
Shapes: map[string]*Shape{
"FirstOpRequest": {ShapeName: "FirstOpRequest", Type: "structure",
AliasedShapeName: true,
},
"FirstOpResponse": {ShapeName: "FirstOpResponse", Type: "structure"},
},
},
ExpectOps: map[string]OpExpect{
"FirstOp": {
Input: "FirstOpRequest",
Output: "FirstOpOutput",
},
},
ExpectShapes: []string{
"FirstOpOutput", "FirstOpRequest",
},
},
"aliasedOutput": {
API: &API{Metadata: meta,
Operations: map[string]*Operation{
"FirstOp": {Name: "FirstOp",
InputRef: ShapeRef{ShapeName: "FirstOpRequest"},
OutputRef: ShapeRef{ShapeName: "FirstOpResponse"},
},
},
Shapes: map[string]*Shape{
"FirstOpRequest": {ShapeName: "FirstOpRequest", Type: "structure"},
"FirstOpResponse": {ShapeName: "FirstOpResponse", Type: "structure",
AliasedShapeName: true,
},
},
},
ExpectOps: map[string]OpExpect{
"FirstOp": {
Input: "FirstOpInput",
Output: "FirstOpResponse",
},
},
ExpectShapes: []string{
"FirstOpInput", "FirstOpResponse",
},
},
"resusedShape": {
API: &API{Metadata: meta,
Operations: map[string]*Operation{
"FirstOp": {Name: "FirstOp",
InputRef: ShapeRef{ShapeName: "FirstOpRequest"},
OutputRef: ShapeRef{ShapeName: "ReusedShape"},
},
},
Shapes: map[string]*Shape{
"FirstOpRequest": {ShapeName: "FirstOpRequest", Type: "structure",
MemberRefs: map[string]*ShapeRef{
"Foo": {ShapeName: "ReusedShape"},
"ooF": {ShapeName: "ReusedShapeList"},
},
},
"ReusedShape": {ShapeName: "ReusedShape", Type: "structure"},
"ReusedShapeList": {ShapeName: "ReusedShapeList", Type: "list",
MemberRef: ShapeRef{ShapeName: "ReusedShape"},
},
},
},
ExpectOps: map[string]OpExpect{
"FirstOp": {
Input: "FirstOpInput",
Output: "FirstOpOutput",
},
},
ExpectShapes: []string{
"FirstOpInput", "FirstOpOutput",
"ReusedShape", "ReusedShapeList",
},
},
"aliasedResusedShape": {
API: &API{Metadata: meta,
Operations: map[string]*Operation{
"FirstOp": {Name: "FirstOp",
InputRef: ShapeRef{ShapeName: "FirstOpRequest"},
OutputRef: ShapeRef{ShapeName: "ReusedShape"},
},
},
Shapes: map[string]*Shape{
"FirstOpRequest": {ShapeName: "FirstOpRequest", Type: "structure",
MemberRefs: map[string]*ShapeRef{
"Foo": {ShapeName: "ReusedShape"},
"ooF": {ShapeName: "ReusedShapeList"},
},
},
"ReusedShape": {ShapeName: "ReusedShape", Type: "structure",
AliasedShapeName: true,
},
"ReusedShapeList": {ShapeName: "ReusedShapeList", Type: "list",
MemberRef: ShapeRef{ShapeName: "ReusedShape"},
},
},
},
ExpectOps: map[string]OpExpect{
"FirstOp": {
Input: "FirstOpInput",
Output: "ReusedShape",
},
},
ExpectShapes: []string{
"FirstOpInput",
"ReusedShape", "ReusedShapeList",
},
},
"unsetInput": {
API: &API{Metadata: meta,
Operations: map[string]*Operation{
"FirstOp": {Name: "FirstOp",
OutputRef: ShapeRef{ShapeName: "FirstOpResponse"},
},
},
Shapes: map[string]*Shape{
"FirstOpResponse": {ShapeName: "FirstOpResponse", Type: "structure"},
},
},
ExpectOps: map[string]OpExpect{
"FirstOp": {
Input: "FirstOpInput",
Output: "FirstOpOutput",
},
},
ExpectShapes: []string{
"FirstOpInput", "FirstOpOutput",
},
},
"unsetOutput": {
API: &API{Metadata: meta,
Operations: map[string]*Operation{
"FirstOp": {Name: "FirstOp",
InputRef: ShapeRef{ShapeName: "FirstOpRequest"},
},
},
Shapes: map[string]*Shape{
"FirstOpRequest": {ShapeName: "FirstOpRequest", Type: "structure"},
},
},
ExpectOps: map[string]OpExpect{
"FirstOp": {
Input: "FirstOpInput",
Output: "FirstOpOutput",
},
},
ExpectShapes: []string{
"FirstOpInput", "FirstOpOutput",
},
},
}
for name, c := range cases {
t.Run(name, func(t *testing.T) {
a := c.API
a.Setup()
for opName, op := range a.Operations {
if e, a := op.InputRef.ShapeName, op.InputRef.Shape.ShapeName; e != a {
t.Errorf("expect input ref and shape names to match, %s, %s", e, a)
}
if e, a := c.ExpectOps[opName].Input, op.InputRef.ShapeName; e != a {
t.Errorf("expect %v input shape, got %v", e, a)
}
if e, a := op.OutputRef.ShapeName, op.OutputRef.Shape.ShapeName; e != a {
t.Errorf("expect output ref and shape names to match, %s, %s", e, a)
}
if e, a := c.ExpectOps[opName].Output, op.OutputRef.ShapeName; e != a {
t.Errorf("expect %v output shape, got %v", e, a)
}
}
if e, a := c.ExpectShapes, a.ShapeNames(); !reflect.DeepEqual(e, a) {
t.Errorf("expect %v shapes, got %v", e, a)
}
})
}
}
+86
View File
@@ -0,0 +1,86 @@
// +build codegen
package api
import (
"bytes"
"fmt"
"text/template"
)
// S3ManagerUploadInputGoCode returns the Go code for the S3 Upload Manager's
// input structure.
func S3ManagerUploadInputGoCode(a *API) string {
if v := a.PackageName(); v != "s3" {
panic(fmt.Sprintf("unexpected API model %s", v))
}
s, ok := a.Shapes["PutObjectInput"]
if !ok {
panic(fmt.Sprintf("unable to find PutObjectInput shape in S3 model"))
}
a.resetImports()
a.AddImport("io")
a.AddImport("time")
var w bytes.Buffer
if err := s3managerUploadInputTmpl.Execute(&w, s); err != nil {
panic(fmt.Sprintf("failed to execute %s template, %v",
s3managerUploadInputTmpl.Name(), err))
}
return a.importsGoCode() + w.String()
}
var s3managerUploadInputTmpl = template.Must(
template.New("s3managerUploadInputTmpl").
Funcs(template.FuncMap{
"GetDeprecatedMsg": getDeprecatedMessage,
}).
Parse(s3managerUploadInputTmplDef),
)
const s3managerUploadInputTmplDef = `
// UploadInput provides the input parameters for uploading a stream or buffer
// to an object in an Amazon S3 bucket. This type is similar to the s3
// package's PutObjectInput with the exception that the Body member is an
// io.Reader instead of an io.ReadSeeker.
type UploadInput struct {
_ struct{} {{ .GoTags true false }}
{{ range $name, $ref := $.MemberRefs -}}
{{ if eq $name "Body" }}
// The readable body payload to send to S3.
Body io.Reader
{{ else if eq $name "ContentLength" }}
{{/* S3 Upload Manager does not use modeled content length */}}
{{ else }}
{{ $isBlob := $.WillRefBeBase64Encoded $name -}}
{{ $isRequired := $.IsRequired $name -}}
{{ $doc := $ref.Docstring -}}
{{ if $doc -}}
{{ $doc }}
{{ if $ref.Deprecated -}}
//
// Deprecated: {{ GetDeprecatedMsg $ref.DeprecatedMsg $name }}
{{ end -}}
{{ end -}}
{{ if $isBlob -}}
{{ if $doc -}}
//
{{ end -}}
// {{ $name }} is automatically base64 encoded/decoded by the SDK.
{{ end -}}
{{ if $isRequired -}}
{{ if or $doc $isBlob -}}
//
{{ end -}}
// {{ $name }} is a required field
{{ end -}}
{{ $name }} {{ $.GoStructType $name $ref }} {{ $ref.GoTags false $isRequired }}
{{ end }}
{{ end }}
}
`
+159
View File
@@ -0,0 +1,159 @@
// +build codegen
package api
// ServiceName returns the SDK's naming of the service. Has
// backwards compatibility built in for services that were
// incorrectly named with the service's endpoint prefix.
func ServiceName(a *API) string {
if oldName, ok := oldServiceNames[a.PackageName()]; ok {
return oldName
}
return ServiceID(a)
}
var oldServiceNames = map[string]string{
"migrationhub": "mgh",
"acmpca": "acm-pca",
"acm": "acm",
"alexaforbusiness": "a4b",
"apigateway": "apigateway",
"applicationautoscaling": "autoscaling",
"appstream": "appstream2",
"appsync": "appsync",
"athena": "athena",
"autoscalingplans": "autoscaling",
"autoscaling": "autoscaling",
"batch": "batch",
"budgets": "budgets",
"costexplorer": "ce",
"cloud9": "cloud9",
"clouddirectory": "clouddirectory",
"cloudformation": "cloudformation",
"cloudfront": "cloudfront",
"cloudhsm": "cloudhsm",
"cloudhsmv2": "cloudhsmv2",
"cloudsearch": "cloudsearch",
"cloudsearchdomain": "cloudsearchdomain",
"cloudtrail": "cloudtrail",
"codebuild": "codebuild",
"codecommit": "codecommit",
"codedeploy": "codedeploy",
"codepipeline": "codepipeline",
"codestar": "codestar",
"cognitoidentity": "cognito-identity",
"cognitoidentityprovider": "cognito-idp",
"cognitosync": "cognito-sync",
"comprehend": "comprehend",
"configservice": "config",
"connect": "connect",
"costandusagereportservice": "cur",
"datapipeline": "datapipeline",
"dax": "dax",
"devicefarm": "devicefarm",
"directconnect": "directconnect",
"applicationdiscoveryservice": "discovery",
"databasemigrationservice": "dms",
"directoryservice": "ds",
"dynamodb": "dynamodb",
"ec2": "ec2",
"ecr": "ecr",
"ecs": "ecs",
"eks": "eks",
"elasticache": "elasticache",
"elasticbeanstalk": "elasticbeanstalk",
"efs": "elasticfilesystem",
"elb": "elasticloadbalancing",
"elbv2": "elasticloadbalancing",
"emr": "elasticmapreduce",
"elastictranscoder": "elastictranscoder",
"ses": "email",
"marketplaceentitlementservice": "entitlement.marketplace",
"elasticsearchservice": "es",
"cloudwatchevents": "events",
"firehose": "firehose",
"fms": "fms",
"gamelift": "gamelift",
"glacier": "glacier",
"glue": "glue",
"greengrass": "greengrass",
"guardduty": "guardduty",
"health": "health",
"iam": "iam",
"inspector": "inspector",
"iotdataplane": "data.iot",
"iotjobsdataplane": "data.jobs.iot",
"iot": "iot",
"iot1clickdevicesservice": "devices.iot1click",
"iot1clickprojects": "projects.iot1click",
"iotanalytics": "iotanalytics",
"kinesisvideoarchivedmedia": "kinesisvideo",
"kinesisvideomedia": "kinesisvideo",
"kinesis": "kinesis",
"kinesisanalytics": "kinesisanalytics",
"kinesisvideo": "kinesisvideo",
"kms": "kms",
"lambda": "lambda",
"lexmodelbuildingservice": "models.lex",
"lightsail": "lightsail",
"cloudwatchlogs": "logs",
"machinelearning": "machinelearning",
"marketplacecommerceanalytics": "marketplacecommerceanalytics",
"mediaconvert": "mediaconvert",
"medialive": "medialive",
"mediapackage": "mediapackage",
"mediastoredata": "data.mediastore",
"mediastore": "mediastore",
"mediatailor": "api.mediatailor",
"marketplacemetering": "metering.marketplace",
"mobile": "mobile",
"mobileanalytics": "mobileanalytics",
"cloudwatch": "monitoring",
"mq": "mq",
"mturk": "mturk-requester",
"neptune": "rds",
"opsworks": "opsworks",
"opsworkscm": "opsworks-cm",
"organizations": "organizations",
"pi": "pi",
"pinpoint": "pinpoint",
"polly": "polly",
"pricing": "api.pricing",
"rds": "rds",
"redshift": "redshift",
"rekognition": "rekognition",
"resourcegroups": "resource-groups",
"resourcegroupstaggingapi": "tagging",
"route53": "route53",
"route53domains": "route53domains",
"lexruntimeservice": "runtime.lex",
"sagemakerruntime": "runtime.sagemaker",
"s3": "s3",
"sagemaker": "sagemaker",
"simpledb": "sdb",
"secretsmanager": "secretsmanager",
"serverlessapplicationrepository": "serverlessrepo",
"servicecatalog": "servicecatalog",
"servicediscovery": "servicediscovery",
"shield": "shield",
"sms": "sms",
"snowball": "snowball",
"sns": "sns",
"sqs": "sqs",
"ssm": "ssm",
"sfn": "states",
"storagegateway": "storagegateway",
"dynamodbstreams": "streams.dynamodb",
"sts": "sts",
"support": "support",
"swf": "swf",
"transcribeservice": "transcribe",
"translate": "translate",
"wafregional": "waf-regional",
"waf": "waf",
"workdocs": "workdocs",
"workmail": "workmail",
"workspaces": "workspaces",
"xray": "xray",
}
+314 -77
View File
@@ -10,8 +10,23 @@ import (
"sort"
"strings"
"text/template"
"github.com/aws/aws-sdk-go/private/protocol"
)
// ErrorInfo represents the error block of a shape's structure
type ErrorInfo struct {
Type string
Code string
HTTPStatusCode int
}
// A XMLInfo defines URL and prefix for Shapes when rendered as XML
type XMLInfo struct {
Prefix string
URI string
}
// A ShapeRef defines the usage of a shape within the API.
type ShapeRef struct {
API *API `json:"-"`
@@ -25,28 +40,26 @@ type ShapeRef struct {
Streaming bool
XMLAttribute bool
// Ignore, if set, will not be sent over the wire
Ignore bool
XMLNamespace XMLInfo
Payload string
IdempotencyToken bool `json:"idempotencyToken"`
JSONValue bool `json:"jsonvalue"`
Deprecated bool `json:"deprecated"`
Ignore bool
XMLNamespace XMLInfo
Payload string
IdempotencyToken bool `json:"idempotencyToken"`
TimestampFormat string `json:"timestampFormat"`
JSONValue bool `json:"jsonvalue"`
Deprecated bool `json:"deprecated"`
DeprecatedMsg string `json:"deprecatedMessage"`
EndpointDiscoveryID bool `json:"endpointdiscoveryid"`
HostLabel bool `json:"hostLabel"`
OrigShapeName string `json:"-"`
GenerateGetter bool
}
// ErrorInfo represents the error block of a shape's structure
type ErrorInfo struct {
Code string
HTTPStatusCode int
}
IsEventPayload bool `json:"eventpayload"`
IsEventHeader bool `json:"eventheader"`
// A XMLInfo defines URL and prefix for Shapes when rendered as XML
type XMLInfo struct {
Prefix string
URI string
// Collection of custom tags the shape reference includes.
CustomTags ShapeTags
}
// A Shape defines the definition of a shape type
@@ -68,12 +81,17 @@ type Shape struct {
Streaming bool
Location string
LocationName string
IdempotencyToken bool `json:"idempotencyToken"`
IdempotencyToken bool `json:"idempotencyToken"`
TimestampFormat string `json:"timestampFormat"`
XMLNamespace XMLInfo
Min float64 // optional Minimum length (string, list) or value (number)
Max float64 // optional Maximum length (string, list) or value (number)
EventStreamsMemberName string `json:"-"`
EventStreamAPI *EventStreamAPI `json:"-"`
EventFor []*EventStream `json:"-"`
IsEventStream bool `json:"eventstream"`
IsEvent bool `json:"event"`
refs []*ShapeRef // References to this shape
resolvePkg string // use this package in the goType() if present
@@ -83,13 +101,34 @@ type Shape struct {
// Defines if the shape is a placeholder and should not be used directly
Placeholder bool
Deprecated bool `json:"deprecated"`
Deprecated bool `json:"deprecated"`
DeprecatedMsg string `json:"deprecatedMessage"`
Validations ShapeValidations
// Error information that is set if the shape is an error shape.
IsError bool
ErrorInfo ErrorInfo `json:"error"`
// Flags that the shape cannot be rename. Prevents the shape from being
// renamed further by the Input/Output.
AliasedShapeName bool
// Sensitive types should not be logged by SDK type loggers.
Sensitive bool `json:"sensitive"`
}
// CanBeEmpty returns if the shape value can sent request as an empty value.
// String, blob, list, and map are types must not be empty when the member is
// serialized to the uri path, or decorated with HostLabel.
func (ref *ShapeRef) CanBeEmpty() bool {
switch ref.Shape.Type {
case "string":
return !(ref.Location == "uri" || ref.HostLabel)
case "blob", "map", "list":
return !(ref.Location == "uri")
default:
return true
}
}
// ErrorCodeName will return the error shape's name formated for
@@ -101,7 +140,7 @@ func (s *Shape) ErrorCodeName() string {
// ErrorName will return the shape's name or error code if available based
// on the API's protocol. This is the error code string returned by the service.
func (s *Shape) ErrorName() string {
name := s.ShapeName
name := s.ErrorInfo.Type
switch s.API.Metadata.Protocol {
case "query", "ec2query", "rest-xml":
if len(s.ErrorInfo.Code) > 0 {
@@ -109,9 +148,33 @@ func (s *Shape) ErrorName() string {
}
}
if len(name) == 0 {
name = s.OrigShapeName
}
if len(name) == 0 {
name = s.ShapeName
}
return name
}
// PayloadRefName returns the payload member of the shape if there is one
// modeled. If no payload is modeled, empty string will be returned.
func (s *Shape) PayloadRefName() string {
if name := s.Payload; len(name) != 0 {
// Root shape
return name
}
for name, ref := range s.MemberRefs {
if ref.IsEventPayload {
return name
}
}
return ""
}
// GoTags returns the struct tags for a shape.
func (s *Shape) GoTags(root, required bool) string {
ref := &ShapeRef{ShapeName: s.ShapeName, API: s.API, Shape: s}
@@ -121,6 +184,11 @@ func (s *Shape) GoTags(root, required bool) string {
// Rename changes the name of the Shape to newName. Also updates
// the associated API's reference to use newName.
func (s *Shape) Rename(newName string) {
if s.AliasedShapeName {
panic(fmt.Sprintf("attempted to rename %s, but flagged as aliased",
s.ShapeName))
}
for _, r := range s.refs {
r.OrigShapeName = r.ShapeName
r.ShapeName = newName
@@ -143,12 +211,21 @@ func (s *Shape) MemberNames() []string {
return names
}
// HasMember will return whether or not the shape has a given
// member by name.
func (s *Shape) HasMember(name string) bool {
_, ok := s.MemberRefs[name]
return ok
}
// GoTypeWithPkgName returns a shape's type as a string with the package name in
// <packageName>.<type> format. Package naming only applies to structures.
func (s *Shape) GoTypeWithPkgName() string {
return goType(s, true)
}
// GoTypeWithPkgNameElem returns the shapes type as a string with the "*"
// removed if there was one preset.
func (s *Shape) GoTypeWithPkgNameElem() string {
t := goType(s, true)
if strings.HasPrefix(t, "*") {
@@ -157,7 +234,7 @@ func (s *Shape) GoTypeWithPkgNameElem() string {
return t
}
// GenAccessors returns if the shape's reference should have setters generated.
// UseIndirection returns if the shape's reference should use indirection or not.
func (s *ShapeRef) UseIndirection() bool {
switch s.Shape.Type {
case "map", "list", "blob", "structure", "jsonvalue":
@@ -175,6 +252,32 @@ func (s *ShapeRef) UseIndirection() bool {
return true
}
func (s Shape) GetTimestampFormat() string {
format := s.TimestampFormat
if len(format) > 0 && !protocol.IsKnownTimestampFormat(format) {
panic(fmt.Sprintf("Unknown timestampFormat %s, for %s",
format, s.ShapeName))
}
return format
}
func (ref ShapeRef) GetTimestampFormat() string {
format := ref.TimestampFormat
if len(format) == 0 {
format = ref.Shape.TimestampFormat
}
if len(format) > 0 && !protocol.IsKnownTimestampFormat(format) {
panic(fmt.Sprintf("Unknown timestampFormat %s, for %s",
format, ref.ShapeName))
}
return format
}
// GoStructValueType returns the Shape's Go type value instead of a pointer
// for the type.
func (s *Shape) GoStructValueType(name string, ref *ShapeRef) string {
@@ -201,7 +304,7 @@ func (s *Shape) GoStructType(name string, ref *ShapeRef) string {
}
if ref.JSONValue {
s.API.imports["github.com/aws/aws-sdk-go/aws"] = true
s.API.AddSDKImport("aws")
return "aws.JSONValue"
}
@@ -267,7 +370,7 @@ func goType(s *Shape, withPkgName bool) string {
return "*string"
case "blob":
return "[]byte"
case "integer", "long":
case "byte", "short", "integer", "long":
return "*int64"
case "float", "double":
return "*float64"
@@ -334,18 +437,23 @@ func (s ShapeTags) String() string {
// GoTags returns the rendered tags string for the ShapeRef
func (ref *ShapeRef) GoTags(toplevel bool, isRequired bool) string {
tags := ShapeTags{}
tags := append(ShapeTags{}, ref.CustomTags...)
if ref.Location != "" {
tags = append(tags, ShapeTag{"location", ref.Location})
} else if ref.Shape.Location != "" {
tags = append(tags, ShapeTag{"location", ref.Shape.Location})
} else if ref.IsEventHeader {
tags = append(tags, ShapeTag{"location", "header"})
}
if ref.LocationName != "" {
tags = append(tags, ShapeTag{"locationName", ref.LocationName})
} else if ref.Shape.LocationName != "" {
tags = append(tags, ShapeTag{"locationName", ref.Shape.LocationName})
} else if len(ref.Shape.EventFor) != 0 && ref.API.Metadata.Protocol == "rest-xml" {
// RPC JSON events need to have location name modeled for round trip testing.
tags = append(tags, ShapeTag{"locationName", ref.Shape.ShapeName})
}
if ref.QueryName != "" {
@@ -373,18 +481,12 @@ func (ref *ShapeRef) GoTags(toplevel bool, isRequired bool) string {
// embed the timestamp type for easier lookups
if ref.Shape.Type == "timestamp" {
t := ShapeTag{Key: "timestampFormat"}
if ref.Location == "header" {
t.Val = "rfc822"
} else {
switch ref.API.Metadata.Protocol {
case "json", "rest-json":
t.Val = "unix"
case "rest-xml", "ec2", "query":
t.Val = "iso8601"
}
if format := ref.GetTimestampFormat(); len(format) > 0 {
tags = append(tags, ShapeTag{
Key: "timestampFormat",
Val: format,
})
}
tags = append(tags, t)
}
if ref.Shape.Flattened || ref.Flattened {
@@ -401,8 +503,8 @@ func (ref *ShapeRef) GoTags(toplevel bool, isRequired bool) string {
}
if toplevel {
if ref.Shape.Payload != "" {
tags = append(tags, ShapeTag{"payload", ref.Shape.Payload})
if name := ref.Shape.PayloadRefName(); len(name) > 0 {
tags = append(tags, ShapeTag{"payload", name})
}
}
@@ -426,6 +528,10 @@ func (ref *ShapeRef) GoTags(toplevel bool, isRequired bool) string {
tags = append(tags, ShapeTag{"ignore", "true"})
}
if ref.Shape.Sensitive {
tags = append(tags, ShapeTag{"sensitive", "true"})
}
return fmt.Sprintf("`%s`", tags)
}
@@ -514,10 +620,49 @@ func (s *Shape) NestedShape() *Shape {
return nestedShape
}
var structShapeTmpl = template.Must(template.New("StructShape").Funcs(template.FuncMap{
"GetCrosslinkURL": GetCrosslinkURL,
}).Parse(`
var structShapeTmpl = func() *template.Template {
shapeTmpl := template.Must(
template.New("structShapeTmpl").
Funcs(template.FuncMap{
"GetCrosslinkURL": GetCrosslinkURL,
"GetDeprecatedMsg": getDeprecatedMessage,
}).
Parse(structShapeTmplDef),
)
template.Must(
shapeTmpl.AddParseTree(
"eventStreamAPILoopMethodTmpl", eventStreamAPILoopMethodTmpl.Tree),
)
template.Must(
shapeTmpl.AddParseTree(
"eventStreamEventShapeTmpl", eventStreamEventShapeTmpl.Tree),
)
template.Must(
shapeTmpl.AddParseTree(
"eventStreamExceptionEventShapeTmpl",
eventStreamExceptionEventShapeTmpl.Tree),
)
shapeTmpl.Funcs(eventStreamEventShapeTmplFuncs)
template.Must(
shapeTmpl.AddParseTree(
"hostLabelsShapeTmpl",
hostLabelsShapeTmpl.Tree),
)
return shapeTmpl
}()
const structShapeTmplDef = `
{{ .Docstring }}
{{ if .Deprecated -}}
{{ if .Docstring -}}
//
{{ end -}}
// Deprecated: {{ GetDeprecatedMsg .DeprecatedMsg .ShapeName }}
{{ end -}}
{{ $context := . -}}
type {{ .ShapeName }} struct {
_ struct{} {{ .GoTags true false }}
@@ -530,6 +675,10 @@ type {{ .ShapeName }} struct {
{{ if $doc -}}
{{ $doc }}
{{ if $elem.Deprecated -}}
//
// Deprecated: {{ GetDeprecatedMsg $elem.DeprecatedMsg $name }}
{{ end -}}
{{ end -}}
{{ if $isBlob -}}
{{ if $doc -}}
@@ -550,45 +699,58 @@ type {{ .ShapeName }} struct {
{{ if not .API.NoStringerMethods }}
{{ .GoCodeStringers }}
{{ end }}
{{ if not .API.NoValidataShapeMethods }}
{{ if not (or .API.NoValidataShapeMethods .Exception) }}
{{ if .Validations -}}
{{ .Validations.GoCode . }}
{{ end }}
{{ end }}
{{ if not .API.NoGenStructFieldAccessors }}
{{ if not (or .API.NoGenStructFieldAccessors .Exception) }}
{{ $builderShapeName := print .ShapeName -}}
{{ range $_, $name := $context.MemberNames -}}
{{ $elem := index $context.MemberRefs $name -}}
{{ $builderShapeName := print .ShapeName -}}
{{ range $_, $name := $context.MemberNames -}}
{{ $elem := index $context.MemberRefs $name -}}
// Set{{ $name }} sets the {{ $name }} field's value.
func (s *{{ $builderShapeName }}) Set{{ $name }}(v {{ $context.GoStructValueType $name $elem }}) *{{ $builderShapeName }} {
{{ if $elem.UseIndirection -}}
s.{{ $name }} = &v
{{ else -}}
s.{{ $name }} = v
{{ end -}}
return s
}
{{ if $elem.GenerateGetter -}}
func (s *{{ $builderShapeName }}) get{{ $name }}() (v {{ $context.GoStructValueType $name $elem }}) {
{{ if $elem.UseIndirection -}}
if s.{{ $name }} == nil {
return v
// Set{{ $name }} sets the {{ $name }} field's value.
func (s *{{ $builderShapeName }}) Set{{ $name }}(v {{ $context.GoStructValueType $name $elem }}) *{{ $builderShapeName }} {
{{ if $elem.UseIndirection -}}
s.{{ $name }} = &v
{{ else -}}
s.{{ $name }} = v
{{ end -}}
return s
}
return *s.{{ $name }}
{{ else -}}
return s.{{ $name }}
{{ end -}}
}
{{- end }}
{{ if $elem.GenerateGetter -}}
func (s *{{ $builderShapeName }}) get{{ $name }}() (v {{ $context.GoStructValueType $name $elem }}) {
{{ if $elem.UseIndirection -}}
if s.{{ $name }} == nil {
return v
}
return *s.{{ $name }}
{{ else -}}
return s.{{ $name }}
{{ end -}}
}
{{- end }}
{{ end }}
{{ end }}
{{ if $.EventStreamsMemberName }}
{{ template "eventStreamAPILoopMethodTmpl" $ }}
{{ end }}
`))
{{ if $.EventFor }}
{{ template "eventStreamEventShapeTmpl" $ }}
{{- if $.Exception }}
{{ template "eventStreamExceptionEventShapeTmpl" $ }}
{{ end -}}
{{ end }}
{{ if $.HasHostLabelMembers }}
{{ template "hostLabelsShapeTmpl" $ }}
{{ end }}
`
var enumShapeTmpl = template.Must(template.New("EnumShape").Parse(`
{{ .Docstring }}
@@ -605,22 +767,38 @@ const (
// GoCode returns the rendered Go code for the Shape.
func (s *Shape) GoCode() string {
b := &bytes.Buffer{}
w := &bytes.Buffer{}
switch {
case s.EventStreamAPI != nil:
if err := renderEventStreamAPIShape(w, s); err != nil {
panic(
fmt.Sprintf(
"failed to generate eventstream API shape, %s, %v",
s.ShapeName, err),
)
}
case s.Type == "structure":
if err := structShapeTmpl.Execute(b, s); err != nil {
panic(fmt.Sprintf("Failed to generate struct shape %s, %v\n", s.ShapeName, err))
if err := structShapeTmpl.Execute(w, s); err != nil {
panic(
fmt.Sprintf(
"Failed to generate struct shape %s, %v",
s.ShapeName, err),
)
}
case s.IsEnum():
if err := enumShapeTmpl.Execute(b, s); err != nil {
panic(fmt.Sprintf("Failed to generate enum shape %s, %v\n", s.ShapeName, err))
if err := enumShapeTmpl.Execute(w, s); err != nil {
panic(
fmt.Sprintf(
"Failed to generate enum shape %s, %v",
s.ShapeName, err),
)
}
default:
panic(fmt.Sprintln("Cannot generate toplevel shape for", s.Type))
}
return b.String()
return w.String()
}
// IsEnum returns whether this shape is an enum list
@@ -628,8 +806,22 @@ func (s *Shape) IsEnum() bool {
return s.Type == "string" && len(s.Enum) > 0
}
// IsRequired returns if member is a required field.
// IsRequired returns if member is a required field. Required fields are fields
// marked as required, hostLabels, or location of uri path.
func (s *Shape) IsRequired(member string) bool {
ref, ok := s.MemberRefs[member]
if !ok {
panic(fmt.Sprintf(
"attempted to check required for unknown member, %s.%s",
s.ShapeName, member,
))
}
if ref.IdempotencyToken || ref.Shape.IdempotencyToken {
return false
}
if ref.Location == "uri" || ref.HostLabel {
return true
}
for _, n := range s.Required {
if n == member {
return true
@@ -673,3 +865,48 @@ func (s *Shape) WillRefBeBase64Encoded(refName string) bool {
return ref.Shape.Type == "blob"
}
// Clone returns a cloned version of the shape with all references clones.
//
// Does not clone EventStream or Validate related values.
func (s *Shape) Clone(newName string) *Shape {
if s.AliasedShapeName {
panic(fmt.Sprintf("attempted to clone and rename %s, but flagged as aliased",
s.ShapeName))
}
n := new(Shape)
*n = *s
debugLogger.Logln("cloning", s.ShapeName, "to", newName)
n.MemberRefs = map[string]*ShapeRef{}
for k, r := range s.MemberRefs {
nr := new(ShapeRef)
*nr = *r
nr.Shape.refs = append(nr.Shape.refs, nr)
n.MemberRefs[k] = nr
}
if n.MemberRef.Shape != nil {
n.MemberRef.Shape.refs = append(n.MemberRef.Shape.refs, &n.MemberRef)
}
if n.KeyRef.Shape != nil {
n.KeyRef.Shape.refs = append(n.KeyRef.Shape.refs, &n.KeyRef)
}
if n.ValueRef.Shape != nil {
n.ValueRef.Shape.refs = append(n.ValueRef.Shape.refs, &n.ValueRef)
}
n.refs = []*ShapeRef{}
n.Required = append([]string{}, n.Required...)
n.Enum = append([]string{}, n.Enum...)
n.EnumConsts = append([]string{}, n.EnumConsts...)
n.OrigShapeName = n.ShapeName
n.API.Shapes[newName] = n
n.ShapeName = newName
return n
}
+19
View File
@@ -0,0 +1,19 @@
package api
var shapeNameAliases = map[string]map[string]string{
"APIGateway": map[string]string{
"RequestValidator": "UpdateRequestValidatorOutput",
"VpcLink": "UpdateVpcLinkOutput",
"GatewayResponse": "UpdateGatewayResponseOutput",
},
"Lambda": map[string]string{
"Concurrency": "PutFunctionConcurrencyOutput",
},
"Neptune": map[string]string{
"DBClusterParameterGroupNameMessage": "ResetDBClusterParameterGroupOutput",
"DBParameterGroupNameMessage": "ResetDBParameterGroupOutput",
},
"RDS": map[string]string{
"DBClusterBacktrack": "BacktrackDBClusterOutput",
},
}
+24 -10
View File
@@ -35,29 +35,43 @@ type ShapeValidation struct {
Type ShapeValidationType
}
var validationGoCodeTmpls = template.Must(template.New("validationGoCodeTmpls").Parse(`
var validationGoCodeTmpls = template.Must(
template.New("validationGoCodeTmpls").
Funcs(template.FuncMap{
"getMin": func(ref *ShapeRef) float64 {
if !ref.CanBeEmpty() && ref.Shape.Min <= 0 {
return 1
}
return ref.Shape.Min
},
}).
Parse(`
{{ define "requiredValue" -}}
if s.{{ .Name }} == nil {
if s.{{ .Name }} == nil {
invalidParams.Add(request.NewErrParamRequired("{{ .Name }}"))
}
{{- end }}
{{ define "minLen" -}}
if s.{{ .Name }} != nil && len(s.{{ .Name }}) < {{ .Ref.Shape.Min }} {
invalidParams.Add(request.NewErrParamMinLen("{{ .Name }}", {{ .Ref.Shape.Min }}))
{{- $min := getMin .Ref -}}
if s.{{ .Name }} != nil && len(s.{{ .Name }}) < {{ $min }} {
invalidParams.Add(request.NewErrParamMinLen("{{ .Name }}", {{ $min }}))
}
{{- end }}
{{ define "minLenString" -}}
if s.{{ .Name }} != nil && len(*s.{{ .Name }}) < {{ .Ref.Shape.Min }} {
invalidParams.Add(request.NewErrParamMinLen("{{ .Name }}", {{ .Ref.Shape.Min }}))
{{- $min := getMin .Ref -}}
if s.{{ .Name }} != nil && len(*s.{{ .Name }}) < {{ $min }} {
invalidParams.Add(request.NewErrParamMinLen("{{ .Name }}", {{ $min }}))
}
{{- end }}
{{ define "minVal" -}}
if s.{{ .Name }} != nil && *s.{{ .Name }} < {{ .Ref.Shape.Min }} {
invalidParams.Add(request.NewErrParamMinValue("{{ .Name }}", {{ .Ref.Shape.Min }}))
{{- $min := getMin .Ref -}}
if s.{{ .Name }} != nil && *s.{{ .Name }} < {{ $min }} {
invalidParams.Add(request.NewErrParamMinValue("{{ .Name }}", {{ $min }}))
}
{{- end }}
{{ define "nestedMapList" -}}
if s.{{ .Name }} != nil {
if s.{{ .Name }} != nil {
for i, v := range s.{{ .Name }} {
if v == nil { continue }
if err := v.Validate(); err != nil {
@@ -67,7 +81,7 @@ var validationGoCodeTmpls = template.Must(template.New("validationGoCodeTmpls").
}
{{- end }}
{{ define "nestedStruct" -}}
if s.{{ .Name }} != nil {
if s.{{ .Name }} != nil {
if err := s.{{ .Name }}.Validate(); err != nil {
invalidParams.AddNested("{{ .Name }}", err.(request.ErrInvalidParams))
}
@@ -0,0 +1,195 @@
// +build codegen
package api
import (
"fmt"
"reflect"
"sort"
"strings"
)
// ShapeValueBuilder provides the logic to build the nested values for a shape.
type ShapeValueBuilder struct{}
// BuildShape will recursively build the referenced shape based on the json
// object provided. isMap will dictate how the field name is specified. If
// isMap is true, we will expect the member name to be quotes like "Foo".
func (b ShapeValueBuilder) BuildShape(ref *ShapeRef, shapes map[string]interface{}, isMap bool) string {
order := make([]string, len(shapes))
for k := range shapes {
order = append(order, k)
}
sort.Strings(order)
ret := ""
for _, name := range order {
if name == "" {
continue
}
shape := shapes[name]
// If the shape isn't a map, we want to export the value, since every field
// defined in our shapes are exported.
if len(name) > 0 && !isMap && strings.ToLower(name[0:1]) == name[0:1] {
name = strings.Title(name)
}
memName := name
passRef := ref.Shape.MemberRefs[name]
if isMap {
memName = fmt.Sprintf("%q", memName)
passRef = &ref.Shape.ValueRef
}
switch v := shape.(type) {
case map[string]interface{}:
ret += b.BuildComplex(name, memName, passRef, v)
case []interface{}:
ret += b.BuildList(name, memName, passRef, v)
default:
ret += b.BuildScalar(name, memName, passRef, v, ref.Shape.Payload == name)
}
}
return ret
}
// BuildList will construct a list shape based off the service's definition of
// that list.
func (b ShapeValueBuilder) BuildList(name, memName string, ref *ShapeRef, v []interface{}) string {
ret := ""
if len(v) == 0 || ref == nil {
return ""
}
passRef := &ref.Shape.MemberRef
ret += fmt.Sprintf("%s: %s {\n", memName, b.GoType(ref, false))
ret += b.buildListElements(passRef, v)
ret += "},\n"
return ret
}
func (b ShapeValueBuilder) buildListElements(ref *ShapeRef, v []interface{}) string {
if len(v) == 0 || ref == nil {
return ""
}
ret := ""
format := ""
isComplex := false
isList := false
// get format for atomic type. If it is not an atomic type,
// get the element.
switch v[0].(type) {
case string:
format = "%s"
case bool:
format = "%t"
case float64:
switch ref.Shape.Type {
case "integer", "int64", "long":
format = "%d"
default:
format = "%f"
}
case []interface{}:
isList = true
case map[string]interface{}:
isComplex = true
}
for _, elem := range v {
if isComplex {
ret += fmt.Sprintf("{\n%s\n},\n", b.BuildShape(ref, elem.(map[string]interface{}), ref.Shape.Type == "map"))
} else if isList {
ret += fmt.Sprintf("{\n%s\n},\n", b.buildListElements(&ref.Shape.MemberRef, elem.([]interface{})))
} else {
switch ref.Shape.Type {
case "integer", "int64", "long":
elem = int(elem.(float64))
}
ret += fmt.Sprintf("%s,\n", getValue(ref.Shape.Type, fmt.Sprintf(format, elem)))
}
}
return ret
}
// BuildScalar will build atomic Go types.
func (b ShapeValueBuilder) BuildScalar(name, memName string, ref *ShapeRef, shape interface{}, isPayload bool) string {
if ref == nil || ref.Shape == nil {
return ""
}
switch v := shape.(type) {
case bool:
return convertToCorrectType(memName, ref.Shape.Type, fmt.Sprintf("%t", v))
case int:
if ref.Shape.Type == "timestamp" {
return parseTimeString(ref, memName, fmt.Sprintf("%d", v))
}
return convertToCorrectType(memName, ref.Shape.Type, fmt.Sprintf("%d", v))
case float64:
dataType := ref.Shape.Type
if dataType == "integer" || dataType == "int64" || dataType == "long" {
return convertToCorrectType(memName, ref.Shape.Type, fmt.Sprintf("%d", int(shape.(float64))))
}
return convertToCorrectType(memName, ref.Shape.Type, fmt.Sprintf("%f", v))
case string:
t := ref.Shape.Type
switch t {
case "timestamp":
return parseTimeString(ref, memName, fmt.Sprintf("%s", v))
case "blob":
if (ref.Streaming || ref.Shape.Streaming) && isPayload {
return fmt.Sprintf("%s: aws.ReadSeekCloser(strings.NewReader(%q)),\n", memName, v)
}
return fmt.Sprintf("%s: []byte(%q),\n", memName, v)
default:
return convertToCorrectType(memName, t, v)
}
default:
panic(fmt.Errorf("Unsupported scalar type: %v", reflect.TypeOf(v)))
}
}
// BuildComplex will build the shape's value for complex types such as structs,
// and maps.
func (b ShapeValueBuilder) BuildComplex(name, memName string, ref *ShapeRef, v map[string]interface{}) string {
switch ref.Shape.Type {
case "structure":
return fmt.Sprintf(`%s: &%s{
%s
},
`, memName, b.GoType(ref, true), b.BuildShape(ref, v, false))
case "map":
return fmt.Sprintf(`%s: %s{
%s
},
`, name, b.GoType(ref, false), b.BuildShape(ref, v, true))
default:
panic(fmt.Sprintf("Expected complex type but received %q", ref.Shape.Type))
}
}
// GoType returns the string of the shape's Go type identifier.
func (b ShapeValueBuilder) GoType(ref *ShapeRef, elem bool) string {
if ref.Shape.Type != "structure" && ref.Shape.Type != "list" && ref.Shape.Type != "map" {
// Scalars are always pointers.
return ref.GoTypeWithPkgName()
}
prefix := ""
if ref.Shape.Type == "list" {
ref = &ref.Shape.MemberRef
prefix = "[]"
}
if elem {
return prefix + ref.Shape.GoTypeWithPkgNameElem()
}
return prefix + ref.GoTypeWithPkgName()
}
+122
View File
@@ -0,0 +1,122 @@
// +build codegen
package api
import (
"bytes"
"encoding/json"
"fmt"
"os"
"text/template"
)
// SmokeTestSuite defines the test suite for smoke tests.
type SmokeTestSuite struct {
Version int `json:"version"`
DefaultRegion string `json:"defaultRegion"`
TestCases []SmokeTestCase `json:"testCases"`
}
// SmokeTestCase provides the definition for a integration smoke test case.
type SmokeTestCase struct {
OpName string `json:"operationName"`
Input map[string]interface{} `json:"input"`
ExpectErr bool `json:"errorExpectedFromService"`
}
// BuildInputShape returns the Go code as a string for initializing the test
// case's input shape.
func (c SmokeTestCase) BuildInputShape(ref *ShapeRef) string {
var b ShapeValueBuilder
return fmt.Sprintf("&%s{\n%s\n}",
b.GoType(ref, true),
b.BuildShape(ref, c.Input, false),
)
}
// AttachSmokeTests attaches the smoke test cases to the API model.
func (a *API) AttachSmokeTests(filename string) {
f, err := os.Open(filename)
if err != nil {
panic(fmt.Sprintf("failed to open smoke tests %s, err: %v", filename, err))
}
defer f.Close()
if err := json.NewDecoder(f).Decode(&a.SmokeTests); err != nil {
panic(fmt.Sprintf("failed to decode smoke tests %s, err: %v", filename, err))
}
if v := a.SmokeTests.Version; v != 1 {
panic(fmt.Sprintf("invalid smoke test version, %d", v))
}
}
// APISmokeTestsGoCode returns the Go Code string for the smoke tests.
func (a *API) APISmokeTestsGoCode() string {
w := bytes.NewBuffer(nil)
a.resetImports()
a.AddImport("context")
a.AddImport("testing")
a.AddImport("time")
a.AddSDKImport("aws")
a.AddSDKImport("aws/request")
a.AddSDKImport("aws/awserr")
a.AddSDKImport("aws/request")
a.AddSDKImport("awstesting/integration")
a.AddImport(a.ImportPath())
smokeTests := struct {
API *API
SmokeTestSuite
}{
API: a,
SmokeTestSuite: a.SmokeTests,
}
if err := smokeTestTmpl.Execute(w, smokeTests); err != nil {
panic(fmt.Sprintf("failed to create smoke tests, %v", err))
}
ignoreImports := `
var _ aws.Config
var _ awserr.Error
var _ request.Request
`
return a.importsGoCode() + ignoreImports + w.String()
}
var smokeTestTmpl = template.Must(template.New(`smokeTestTmpl`).Parse(`
{{- range $i, $testCase := $.TestCases }}
{{- $op := index $.API.Operations $testCase.OpName }}
func TestInteg_{{ printf "%02d" $i }}_{{ $op.ExportedName }}(t *testing.T) {
ctx, cancelFn := context.WithTimeout(context.Background(), 5 *time.Second)
defer cancelFn()
sess := integration.SessionWithDefaultRegion("{{ $.DefaultRegion }}")
svc := {{ $.API.PackageName }}.New(sess)
params := {{ $testCase.BuildInputShape $op.InputRef }}
_, err := svc.{{ $op.ExportedName }}WithContext(ctx, params)
{{- if $testCase.ExpectErr }}
if err == nil {
t.Fatalf("expect request to fail")
}
aerr, ok := err.(awserr.RequestFailure)
if !ok {
t.Fatalf("expect awserr, was %T", err)
}
if len(aerr.Code()) == 0 {
t.Errorf("expect non-empty error code")
}
if v := aerr.Code(); v == request.ErrCodeSerialization {
t.Errorf("expect API error code got serialization failure")
}
{{- else }}
if err != nil {
t.Errorf("expect no error, got %v", err)
}
{{- end }}
}
{{- end }}
`))
+2 -2
View File
@@ -47,8 +47,8 @@ func (a *API) WaitersGoCode() string {
var buf bytes.Buffer
fmt.Fprintf(&buf, "import (\n%q\n\n%q\n%q\n)",
"time",
"github.com/aws/aws-sdk-go/aws",
"github.com/aws/aws-sdk-go/aws/request",
SDKImportRoot+"/aws",
SDKImportRoot+"/aws/request",
)
for _, w := range a.Waiters {
@@ -0,0 +1,28 @@
// +build codegen
package main
import (
"fmt"
"os"
"path/filepath"
"github.com/aws/aws-sdk-go/private/model/api"
)
func main() {
glob := filepath.FromSlash(os.Args[1])
modelPaths, err := api.ExpandModelGlobPath(glob)
if err != nil {
fmt.Fprintf(os.Stderr, "failed to expand glob, %v\n", err)
os.Exit(1)
}
_, excluded := api.TrimModelServiceVersions(modelPaths)
for _, exclude := range excluded {
modelPath := filepath.Dir(exclude)
fmt.Println("removing:", modelPath)
os.RemoveAll(modelPath)
}
}
+128 -135
View File
@@ -13,7 +13,6 @@ import (
"os"
"path/filepath"
"runtime/debug"
"sort"
"strings"
"sync"
@@ -21,87 +20,21 @@ import (
"github.com/aws/aws-sdk-go/private/util"
)
type generateInfo struct {
*api.API
PackageDir string
}
func usage() {
fmt.Fprintln(os.Stderr, `Usage: api-gen <options> [model path | file path]
Loads API models from file and generates SDK clients from the models.
var excludeServices = map[string]struct{}{
"importexport": {},
}
The model path arguments can be globs, or paths to individual files. The
utiliity requires that the API model files follow the following pattern:
// newGenerateInfo initializes the service API's folder structure for a specific service.
// If the SERVICES environment variable is set, and this service is not apart of the list
// this service will be skipped.
func newGenerateInfo(modelFile, svcPath, svcImportPath string) *generateInfo {
g := &generateInfo{API: &api.API{SvcClientImportPath: svcImportPath, BaseCrosslinkURL: "https://docs.aws.amazon.com"}}
g.API.Attach(modelFile)
<root>/<servicename>/<api-version>/<model json files>
if _, ok := excludeServices[g.API.PackageName()]; ok {
return nil
}
e.g:
paginatorsFile := strings.Replace(modelFile, "api-2.json", "paginators-1.json", -1)
if _, err := os.Stat(paginatorsFile); err == nil {
g.API.AttachPaginators(paginatorsFile)
} else if !os.IsNotExist(err) {
fmt.Println("api-2.json error:", err)
}
./models/apis/s3/2006-03-01/*.json
docsFile := strings.Replace(modelFile, "api-2.json", "docs-2.json", -1)
if _, err := os.Stat(docsFile); err == nil {
g.API.AttachDocs(docsFile)
} else {
fmt.Println("docs-2.json error:", err)
}
waitersFile := strings.Replace(modelFile, "api-2.json", "waiters-2.json", -1)
if _, err := os.Stat(waitersFile); err == nil {
g.API.AttachWaiters(waitersFile)
} else if !os.IsNotExist(err) {
fmt.Println("waiters-2.json error:", err)
}
examplesFile := strings.Replace(modelFile, "api-2.json", "examples-1.json", -1)
if _, err := os.Stat(examplesFile); err == nil {
g.API.AttachExamples(examplesFile)
} else if !os.IsNotExist(err) {
fmt.Println("examples-1.json error:", err)
}
// pkgDocAddonsFile := strings.Replace(modelFile, "api-2.json", "go-pkg-doc.gotmpl", -1)
// if _, err := os.Stat(pkgDocAddonsFile); err == nil {
// g.API.AttachPackageDocAddons(pkgDocAddonsFile)
// } else if !os.IsNotExist(err) {
// fmt.Println("go-pkg-doc.gotmpl error:", err)
// }
g.API.Setup()
if svc := os.Getenv("SERVICES"); svc != "" {
svcs := strings.Split(svc, ",")
included := false
for _, s := range svcs {
if s == g.API.PackageName() {
included = true
break
}
}
if !included {
// skip this non-included service
return nil
}
}
// ensure the directory exists
pkgDir := filepath.Join(svcPath, g.API.PackageName())
os.MkdirAll(pkgDir, 0775)
os.MkdirAll(filepath.Join(pkgDir, g.API.InterfacePackageName()), 0775)
g.PackageDir = pkgDir
return g
Flags:`)
flag.PrintDefaults()
}
// Generates service api, examples, and interface from api json definition files.
@@ -112,88 +45,109 @@ func newGenerateInfo(modelFile, svcPath, svcImportPath string) *generateInfo {
// Env:
// SERVICES comma separated list of services to generate.
func main() {
var svcPath, sessionPath, svcImportPath string
flag.StringVar(&svcPath, "path", "service", "directory to generate service clients in")
flag.StringVar(&sessionPath, "sessionPath", filepath.Join("aws", "session"), "generate session service client factories")
flag.StringVar(&svcImportPath, "svc-import-path", "github.com/aws/aws-sdk-go/service", "namespace to generate service client Go code import path under")
var svcPath, svcImportPath string
flag.StringVar(&svcPath, "path", "service",
"The `path` to generate service clients in to.",
)
flag.StringVar(&svcImportPath, "svc-import-path",
api.SDKImportRoot+"/service",
"The Go `import path` to generate client to be under.",
)
flag.Usage = usage
flag.Parse()
api.Bootstrap()
files := []string{}
for i := 0; i < flag.NArg(); i++ {
file := flag.Arg(i)
if strings.Contains(file, "*") {
paths, _ := filepath.Glob(file)
files = append(files, paths...)
} else {
files = append(files, file)
if len(os.Getenv("AWS_SDK_CODEGEN_DEBUG")) != 0 {
api.LogDebug(os.Stdout)
}
// Make sure all paths are based on platform's pathing not Unix
globs := flag.Args()
for i, g := range globs {
globs[i] = filepath.FromSlash(g)
}
svcPath = filepath.FromSlash(svcPath)
modelPaths, err := api.ExpandModelGlobPath(globs...)
if err != nil {
fmt.Fprintln(os.Stderr, "failed to glob file pattern", err)
os.Exit(1)
}
modelPaths, _ = api.TrimModelServiceVersions(modelPaths)
apis, err := api.LoadAPIs(modelPaths, svcImportPath)
if err != nil {
fmt.Fprintln(os.Stderr, "failed to load API models", err)
os.Exit(1)
}
if len(apis) == 0 {
fmt.Fprintf(os.Stderr, "expected to load models, but found none")
os.Exit(1)
}
if v := os.Getenv("SERVICES"); len(v) != 0 {
svcs := strings.Split(v, ",")
for pkgName, a := range apis {
var found bool
for _, include := range svcs {
if a.PackageName() == include {
found = true
break
}
}
if !found {
delete(apis, pkgName)
}
}
}
for svcName := range excludeServices {
if strings.Contains(os.Getenv("SERVICES"), svcName) {
fmt.Fprintf(os.Stderr, "Service %s is not supported\n", svcName)
os.Exit(1)
}
}
sort.Strings(files)
// Remove old API versions from list
m := map[string]bool{}
// caches paths to ensure we are not overriding previously generated
// code.
var wg sync.WaitGroup
servicePaths := map[string]struct{}{}
for i := range files {
idx := len(files) - 1 - i
parts := strings.Split(files[idx], string(filepath.Separator))
svc := parts[len(parts)-3] // service name is 2nd-to-last component
if m[svc] {
files[idx] = "" // wipe this one out if we already saw the service
}
m[svc] = true
}
wg := sync.WaitGroup{}
for i := range files {
filename := files[i]
if filename == "" { // empty file
for _, a := range apis {
if _, ok := excludeServices[a.PackageName()]; ok {
continue
}
genInfo := newGenerateInfo(filename, svcPath, svcImportPath)
if genInfo == nil {
continue
}
if _, ok := excludeServices[genInfo.API.PackageName()]; ok {
// Skip services not yet supported.
continue
}
// Create the output path for the model.
pkgDir := filepath.Join(svcPath, a.PackageName())
os.MkdirAll(filepath.Join(pkgDir, a.InterfacePackageName()), 0775)
if _, ok := servicePaths[genInfo.PackageDir]; ok {
fmt.Fprintf(os.Stderr, "Path %q has already been generated", genInfo.PackageDir)
if _, ok := servicePaths[pkgDir]; ok {
fmt.Fprintf(os.Stderr,
"attempted to generate a client into %s twice. Second model package, %v\n",
pkgDir, a.PackageName())
os.Exit(1)
}
servicePaths[pkgDir] = struct{}{}
servicePaths[genInfo.PackageDir] = struct{}{}
g := &generateInfo{
API: a,
PackageDir: pkgDir,
}
wg.Add(1)
go func(g *generateInfo, filename string) {
go func() {
defer wg.Done()
writeServiceFiles(g, filename)
}(genInfo, filename)
writeServiceFiles(g, pkgDir)
}()
}
wg.Wait()
}
func writeServiceFiles(g *generateInfo, filename string) {
type generateInfo struct {
*api.API
PackageDir string
}
var excludeServices = map[string]struct{}{
"importexport": {},
}
func writeServiceFiles(g *generateInfo, pkgDir string) {
defer func() {
if r := recover(); r != nil {
fmt.Fprintf(os.Stderr, "Error generating %s\n%s\n%s\n",
filename, r, debug.Stack())
pkgDir, r, debug.Stack())
os.Exit(1)
}
}()
@@ -209,6 +163,18 @@ func writeServiceFiles(g *generateInfo, filename string) {
Must(writeWaitersFile(g))
Must(writeAPIErrorsFile(g))
Must(writeExamplesFile(g))
if g.API.HasEventStream {
Must(writeAPIEventStreamTestFile(g))
}
if g.API.PackageName() == "s3" {
Must(writeS3ManagerUploadInputFile(g))
}
if len(g.API.SmokeTests.TestCases) > 0 {
Must(writeAPISmokeTestsFile(g))
}
}
// Must will panic if the error passed in is not nil.
@@ -313,3 +279,30 @@ func writeAPIErrorsFile(g *generateInfo) error {
g.API.APIErrorsGoCode(),
)
}
func writeAPIEventStreamTestFile(g *generateInfo) error {
return writeGoFile(filepath.Join(g.PackageDir, "eventstream_test.go"),
codeLayout,
"// +build go1.6\n",
g.API.PackageName(),
g.API.APIEventStreamTestGoCode(),
)
}
func writeS3ManagerUploadInputFile(g *generateInfo) error {
return writeGoFile(filepath.Join(g.PackageDir, "s3manager", "upload_input.go"),
codeLayout,
"",
"s3manager",
api.S3ManagerUploadInputGoCode(g.API),
)
}
func writeAPISmokeTestsFile(g *generateInfo) error {
return writeGoFile(filepath.Join(g.PackageDir, "integ_test.go"),
codeLayout,
"// +build go1.10,integration\n",
g.API.PackageName()+"_test",
g.API.APISmokeTestsGoCode(),
)
}
+3 -1
View File
@@ -45,7 +45,9 @@ func main() {
}
}()
if err := endpoints.CodeGenModel(modelFile, outFile); err != nil {
if err := endpoints.CodeGenModel(modelFile, outFile, func(o *endpoints.CodeGenOptions) {
o.DisableGenerateServiceIDs = true
}); err != nil {
exitErrorf("failed to codegen model, %v", err)
}
}