2017-12-08 12:03:10 +00:00
|
|
|
// +build codegen
|
|
|
|
|
|
|
|
package api
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"regexp"
|
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
|
|
|
// updateTopLevelShapeReferences moves resultWrapper, locationName, and
|
|
|
|
// xmlNamespace traits from toplevel shape references to the toplevel
|
|
|
|
// shapes for easier code generation
|
|
|
|
func (a *API) updateTopLevelShapeReferences() {
|
|
|
|
for _, o := range a.Operations {
|
|
|
|
// these are for REST-XML services
|
|
|
|
if o.InputRef.LocationName != "" {
|
|
|
|
o.InputRef.Shape.LocationName = o.InputRef.LocationName
|
|
|
|
}
|
|
|
|
if o.InputRef.Location != "" {
|
|
|
|
o.InputRef.Shape.Location = o.InputRef.Location
|
|
|
|
}
|
|
|
|
if o.InputRef.Payload != "" {
|
|
|
|
o.InputRef.Shape.Payload = o.InputRef.Payload
|
|
|
|
}
|
|
|
|
if o.InputRef.XMLNamespace.Prefix != "" {
|
|
|
|
o.InputRef.Shape.XMLNamespace.Prefix = o.InputRef.XMLNamespace.Prefix
|
|
|
|
}
|
|
|
|
if o.InputRef.XMLNamespace.URI != "" {
|
|
|
|
o.InputRef.Shape.XMLNamespace.URI = o.InputRef.XMLNamespace.URI
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// writeShapeNames sets each shape's API and shape name values. Binding the
|
|
|
|
// shape to its parent API.
|
|
|
|
func (a *API) writeShapeNames() {
|
|
|
|
for n, s := range a.Shapes {
|
|
|
|
s.API = a
|
|
|
|
s.ShapeName = n
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *API) resolveReferences() {
|
|
|
|
resolver := referenceResolver{API: a, visited: map[*ShapeRef]bool{}}
|
|
|
|
|
|
|
|
for _, s := range a.Shapes {
|
|
|
|
resolver.resolveShape(s)
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, o := range a.Operations {
|
|
|
|
o.API = a // resolve parent reference
|
|
|
|
|
|
|
|
resolver.resolveReference(&o.InputRef)
|
|
|
|
resolver.resolveReference(&o.OutputRef)
|
|
|
|
|
|
|
|
// Resolve references for errors also
|
|
|
|
for i := range o.ErrorRefs {
|
|
|
|
resolver.resolveReference(&o.ErrorRefs[i])
|
|
|
|
o.ErrorRefs[i].Shape.IsError = true
|
2019-01-21 14:27:20 +00:00
|
|
|
o.ErrorRefs[i].Shape.ErrorInfo.Type = o.ErrorRefs[i].Shape.ShapeName
|
2017-12-08 12:03:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// A referenceResolver provides a way to resolve shape references to
|
|
|
|
// shape definitions.
|
|
|
|
type referenceResolver struct {
|
|
|
|
*API
|
|
|
|
visited map[*ShapeRef]bool
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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) {
|
|
|
|
if ref.ShapeName == "" {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-01-21 14:27:20 +00:00
|
|
|
shape, ok := r.API.Shapes[ref.ShapeName]
|
|
|
|
if !ok {
|
|
|
|
panic(fmt.Sprintf("unable resolve reference, %s", ref.ShapeName))
|
|
|
|
}
|
2017-12-08 12:03:10 +00:00
|
|
|
|
2019-01-21 14:27:20 +00:00
|
|
|
if ref.JSONValue {
|
|
|
|
ref.ShapeName = "JSONValue"
|
|
|
|
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,
|
|
|
|
},
|
|
|
|
}
|
2017-12-08 12:03:10 +00:00
|
|
|
}
|
2019-01-21 14:27:20 +00:00
|
|
|
}
|
2017-12-08 12:03:10 +00:00
|
|
|
|
2019-01-21 14:27:20 +00:00
|
|
|
ref.API = r.API // resolve reference back to API
|
|
|
|
ref.Shape = shape // resolve shape reference
|
2017-12-08 12:03:10 +00:00
|
|
|
|
2019-01-21 14:27:20 +00:00
|
|
|
if r.visited[ref] {
|
|
|
|
return
|
2017-12-08 12:03:10 +00:00
|
|
|
}
|
2019-01-21 14:27:20 +00:00
|
|
|
r.visited[ref] = true
|
|
|
|
|
|
|
|
shape.refs = append(shape.refs, ref) // register the ref
|
|
|
|
|
|
|
|
// resolve shape's references, if it has any
|
|
|
|
r.resolveShape(shape)
|
2017-12-08 12:03:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// resolveShape resolves a shape's Member Key Value, and nested member
|
|
|
|
// shape references.
|
|
|
|
func (r *referenceResolver) resolveShape(shape *Shape) {
|
|
|
|
r.resolveReference(&shape.MemberRef)
|
|
|
|
r.resolveReference(&shape.KeyRef)
|
|
|
|
r.resolveReference(&shape.ValueRef)
|
|
|
|
for _, m := range shape.MemberRefs {
|
|
|
|
r.resolveReference(m)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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).
|
|
|
|
func (a *API) fixStutterNames() {
|
|
|
|
str, end := a.StructName(), ""
|
|
|
|
if len(str) > 1 {
|
|
|
|
l := len(str) - 1
|
|
|
|
str, end = str[0:l], str[l:]
|
|
|
|
}
|
|
|
|
re := regexp.MustCompile(fmt.Sprintf(`\A(?i:%s)%s`, str, end))
|
|
|
|
|
|
|
|
for name, op := range a.Operations {
|
|
|
|
newName := re.ReplaceAllString(name, "")
|
|
|
|
if newName != name && len(newName) > 0 {
|
|
|
|
delete(a.Operations, name)
|
|
|
|
a.Operations[newName] = op
|
|
|
|
}
|
|
|
|
op.ExportedName = newName
|
|
|
|
}
|
|
|
|
|
|
|
|
for k, s := range a.Shapes {
|
|
|
|
newName := re.ReplaceAllString(k, "")
|
|
|
|
if newName != s.ShapeName && len(newName) > 0 {
|
|
|
|
s.Rename(newName)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// renameExportable renames all operation names to be exportable names.
|
|
|
|
// All nested Shape names are also updated to the exportable variant.
|
|
|
|
func (a *API) renameExportable() {
|
|
|
|
for name, op := range a.Operations {
|
|
|
|
newName := a.ExportableName(name)
|
|
|
|
if newName != name {
|
|
|
|
delete(a.Operations, name)
|
|
|
|
a.Operations[newName] = op
|
|
|
|
}
|
|
|
|
op.ExportedName = newName
|
|
|
|
}
|
|
|
|
|
|
|
|
for k, s := range a.Shapes {
|
|
|
|
// FIXME SNS has lower and uppercased shape names with the same name,
|
|
|
|
// except the lowercased variant is used exclusively for string and
|
|
|
|
// other primitive types. Renaming both would cause a collision.
|
|
|
|
// We work around this by only renaming the structure shapes.
|
|
|
|
if s.Type == "string" {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
for mName, member := range s.MemberRefs {
|
2019-01-21 14:27:20 +00:00
|
|
|
ref := s.MemberRefs[mName]
|
|
|
|
ref.OrigShapeName = mName
|
|
|
|
s.MemberRefs[mName] = ref
|
|
|
|
|
2017-12-08 12:03:10 +00:00
|
|
|
newName := a.ExportableName(mName)
|
|
|
|
if newName != mName {
|
|
|
|
delete(s.MemberRefs, mName)
|
|
|
|
s.MemberRefs[newName] = member
|
|
|
|
|
|
|
|
// also apply locationName trait so we keep the old one
|
|
|
|
// but only if there's no locationName trait on ref or shape
|
|
|
|
if member.LocationName == "" && member.Shape.LocationName == "" {
|
|
|
|
member.LocationName = mName
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if newName == "_" {
|
|
|
|
panic("Shape " + s.ShapeName + " uses reserved member name '_'")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
newName := a.ExportableName(k)
|
|
|
|
if newName != s.ShapeName {
|
|
|
|
s.Rename(newName)
|
|
|
|
}
|
|
|
|
|
|
|
|
s.Payload = a.ExportableName(s.Payload)
|
|
|
|
|
|
|
|
// fix required trait names
|
|
|
|
for i, n := range s.Required {
|
|
|
|
s.Required[i] = a.ExportableName(n)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, s := range a.Shapes {
|
|
|
|
// fix enum names
|
|
|
|
if s.IsEnum() {
|
|
|
|
s.EnumConsts = make([]string, len(s.Enum))
|
|
|
|
for i := range s.Enum {
|
|
|
|
shape := s.ShapeName
|
|
|
|
shape = strings.ToUpper(shape[0:1]) + shape[1:]
|
|
|
|
s.EnumConsts[i] = shape + s.EnumName(i)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-29 22:13:47 +00:00
|
|
|
// renameCollidingFields will rename any fields that uses an SDK or Golang
|
|
|
|
// specific name.
|
|
|
|
func (a *API) renameCollidingFields() {
|
|
|
|
for _, v := range a.Shapes {
|
|
|
|
namesWithSet := map[string]struct{}{}
|
|
|
|
for k, field := range v.MemberRefs {
|
2019-01-21 14:27:20 +00:00
|
|
|
if _, ok := v.MemberRefs["Set"+k]; ok {
|
|
|
|
namesWithSet["Set"+k] = struct{}{}
|
2017-12-29 22:13:47 +00:00
|
|
|
}
|
|
|
|
|
2019-01-21 14:27:20 +00:00
|
|
|
if collides(k) || (v.Exception && exceptionCollides(k)) {
|
2017-12-29 22:13:47 +00:00
|
|
|
renameCollidingField(k, v, field)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// checks if any field names collide with setters.
|
|
|
|
for name := range namesWithSet {
|
2019-01-21 14:27:20 +00:00
|
|
|
field := v.MemberRefs[name]
|
|
|
|
renameCollidingField(name, v, field)
|
2017-12-29 22:13:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func renameCollidingField(name string, v *Shape, field *ShapeRef) {
|
|
|
|
newName := name + "_"
|
2019-01-21 14:27:20 +00:00
|
|
|
debugLogger.Logf("Shape %s's field %q renamed to %q", v.ShapeName, name, newName)
|
2017-12-29 22:13:47 +00:00
|
|
|
delete(v.MemberRefs, name)
|
|
|
|
v.MemberRefs[newName] = field
|
|
|
|
}
|
|
|
|
|
|
|
|
// collides will return true if it is a name used by the SDK or Golang.
|
|
|
|
func collides(name string) bool {
|
|
|
|
switch name {
|
|
|
|
case "String",
|
|
|
|
"GoString",
|
|
|
|
"Validate":
|
|
|
|
return true
|
2019-01-21 14:27:20 +00:00
|
|
|
}
|
|
|
|
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
|
|
|
|
}
|
2017-12-29 22:13:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-08 12:03:10 +00:00
|
|
|
// createInputOutputShapes creates toplevel input/output shapes if they
|
|
|
|
// have not been defined in the API. This normalizes all APIs to always
|
|
|
|
// have an input and output structure in the signature.
|
|
|
|
func (a *API) createInputOutputShapes() {
|
|
|
|
for _, op := range a.Operations {
|
2019-01-21 14:27:20 +00:00
|
|
|
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
|
2017-12-08 12:03:10 +00:00
|
|
|
}
|
2019-01-21 14:27:20 +00:00
|
|
|
|
|
|
|
// 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)
|
2017-12-08 12:03:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func setAsPlacholderShape(tgtShapeRef *ShapeRef, name string, a *API) {
|
|
|
|
shape := a.makeIOShape(name)
|
|
|
|
shape.Placeholder = true
|
|
|
|
*tgtShapeRef = ShapeRef{API: a, ShapeName: shape.ShapeName, Shape: shape}
|
|
|
|
shape.refs = append(shape.refs, tgtShapeRef)
|
|
|
|
}
|
|
|
|
|
|
|
|
// makeIOShape returns a pointer to a new Shape initialized by the name provided.
|
|
|
|
func (a *API) makeIOShape(name string) *Shape {
|
|
|
|
shape := &Shape{
|
|
|
|
API: a, ShapeName: name, Type: "structure",
|
|
|
|
MemberRefs: map[string]*ShapeRef{},
|
|
|
|
}
|
|
|
|
a.Shapes[name] = shape
|
|
|
|
return shape
|
|
|
|
}
|
|
|
|
|
|
|
|
// removeUnusedShapes removes shapes from the API which are not referenced by any
|
|
|
|
// other shape in the API.
|
|
|
|
func (a *API) removeUnusedShapes() {
|
2019-01-21 14:27:20 +00:00
|
|
|
for _, s := range a.Shapes {
|
2017-12-08 12:03:10 +00:00
|
|
|
if len(s.refs) == 0 {
|
2019-01-21 14:27:20 +00:00
|
|
|
a.removeShape(s)
|
2017-12-08 12:03:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Represents the service package name to EndpointsID mapping
|
|
|
|
var custEndpointsKey = map[string]string{
|
|
|
|
"applicationautoscaling": "application-autoscaling",
|
|
|
|
}
|
|
|
|
|
|
|
|
// Sents the EndpointsID field of Metadata with the value of the
|
|
|
|
// EndpointPrefix if EndpointsID is not set. Also adds
|
|
|
|
// customizations for services if EndpointPrefix is not a valid key.
|
|
|
|
func (a *API) setMetadataEndpointsKey() {
|
|
|
|
if len(a.Metadata.EndpointsID) != 0 {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if v, ok := custEndpointsKey[a.PackageName()]; ok {
|
|
|
|
a.Metadata.EndpointsID = v
|
|
|
|
} else {
|
|
|
|
a.Metadata.EndpointsID = a.Metadata.EndpointPrefix
|
|
|
|
}
|
|
|
|
}
|
2019-01-21 14:27:20 +00:00
|
|
|
|
|
|
|
// Suppress event stream must be run before setup event stream
|
|
|
|
func (a *API) suppressHTTP2EventStreams() {
|
|
|
|
if a.Metadata.ProtocolSettings.HTTP2 != "eventstream" {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
for name, op := range a.Operations {
|
|
|
|
outbound := hasEventStream(op.InputRef.Shape)
|
|
|
|
inbound := hasEventStream(op.OutputRef.Shape)
|
|
|
|
|
|
|
|
if !(outbound || inbound) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
a.removeOperation(name)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *API) findEndpointDiscoveryOp() {
|
|
|
|
for _, op := range a.Operations {
|
|
|
|
if op.IsEndpointDiscoveryOp {
|
|
|
|
a.EndpointDiscoveryOp = op
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|