Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion CHANGELOG_PENDING.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ Breaking Change
* To migrate your applications to use the updated wafregional and dynamodbstreams you'll need to update the package the impacted type is imported from to match the client the type is being used with.
* `aws`: Context has been added to EC2Metadata operations.([#461](https://github.com/aws/aws-sdk-go-v2/pull/461))
* Also updates utilities that directly or indirectly depend on EC2Metadata client. Signer utilities, credential providers now take in context.

* `private/model`: Add utility for validating shape names for structs and enums for the service packages ([#471](https://github.com/aws/aws-sdk-go-v2/pull/471))
* Fixes bug which allowed service package structs, enums to start with non alphabetic character
* Fixes the incorrect enum types in mediapackage service package, changing enum types __AdTriggersElement, __PeriodTriggersElement to AdTriggersElement, PeriodTriggersElement respectively.

Services
---

Expand Down
4 changes: 4 additions & 0 deletions private/model/api/load.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package api
import (
"encoding/json"
"fmt"
"log"
"os"
"path/filepath"
"sort"
Expand Down Expand Up @@ -172,6 +173,9 @@ func (a *API) Setup() {
}

a.fixStutterNames()
if err := a.validateShapeNames(); err != nil {
log.Fatalf(err.Error())
}
a.renameExportable()
a.applyShapeNameAliases()
a.createInputOutputShapes()
Expand Down
52 changes: 52 additions & 0 deletions private/model/api/passes.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"fmt"
"regexp"
"strings"
"unicode"
)

// updateTopLevelShapeReferences moves resultWrapper, locationName, and
Expand Down Expand Up @@ -158,6 +159,57 @@ func (a *API) fixStutterNames() {
}
}

// regexpForValidatingShapeName is used by validateShapeName to filter acceptable shape names
// that may be renamed to a new valid shape name, if not already.
// The regex allows underscores(_) at the beginning of the shape name
// There may be 0 or more underscores(_). The next character would be the leading character
// in the renamed shape name and thus, must be an alphabetic character.
// The regex allows alphanumeric characters along with underscores(_) in rest of the string.
var regexForValidatingShapeName = regexp.MustCompile("^[_]*[a-zA-Z][a-zA-Z0-9_]*$")

// validateShapeNames is valid only for shapes of type structure or enums
// We validate a shape name to check if its a valid shape name
// A valid shape name would only contain alphanumeric characters and have an alphabet as leading character.
//
// If we encounter a shape name with underscores(_), we remove the underscores, and
// follow a canonical upper camel case naming scheme to create a new shape name.
// If the shape name collides with an existing shape name we return an error.
// The resulting shape name must be a valid shape name or throw an error.
func (a *API) validateShapeNames() error {
for _, s := range a.Shapes {
if s.Type == "structure" || s.IsEnum() {
name := s.ShapeName
if b := regexForValidatingShapeName.MatchString(name); !b {
return fmt.Errorf("invalid shape name found: %v", s.ShapeName)
}

// Slice of strings returned after we split a string
// with a non alphanumeric character as delimiter.
slice := strings.FieldsFunc(name, func(r rune) bool {
return !unicode.IsLetter(r) && !unicode.IsNumber(r)
})

// Build a string that follows canonical upper camel casing
var b strings.Builder
for _, word := range slice {
b.WriteString(strings.Title(word))
}

name = b.String()
if s.ShapeName != name {
if a.Shapes[name] != nil {
// throw an error if shape with a new shape name already exists
return fmt.Errorf("attempt to rename shape %v to %v for package %v failed, as this rename would result in shape name collision",
s.ShapeName, name, a.PackageName())
}
debugLogger.Logf("Renaming shape %v to %v for package %v \n", s.ShapeName, name, a.PackageName())
s.Rename(name)
}
}
}
return nil
}

func (a *API) applyShapeNameAliases() {
service, ok := shapeNameAliases[a.ServiceID()]
if !ok {
Expand Down
148 changes: 148 additions & 0 deletions private/model/api/passes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package api
import (
"reflect"
"strconv"
"strings"
"testing"
)

Expand Down Expand Up @@ -501,3 +502,150 @@ func TestCreateInputOutputShapes(t *testing.T) {
})
}
}

func TestValidateShapeNameMethod(t *testing.T) {
cases := map[string]struct {
inputShapeName string
shapeType string
expectedShapeName string
expectedError string
}{
"empty case": {
inputShapeName: "",
shapeType: "structure",
expectedShapeName: "",
expectedError: "invalid shape name found",
},
"No rename": {
inputShapeName: "Sample123Shape",
shapeType: "structure",
expectedShapeName: "Sample123Shape",
},
"starts with underscores": {
inputShapeName: "__Sample123Shape",
shapeType: "structure",
expectedShapeName: "Sample123Shape",
},
"Contains underscores": {
inputShapeName: "__sample_123_shape__",
shapeType: "structure",
expectedShapeName: "Sample123Shape",
},
"Starts with numeric character": {
inputShapeName: "123__sampleShape",
shapeType: "structure",
expectedShapeName: "",
expectedError: "invalid shape name found",
},
"Starts with non alphabetic or non underscore character": {
inputShapeName: "&&SampleShape",
shapeType: "structure",
expectedShapeName: "",
expectedError: "invalid shape name found",
},
"Contains non Alphanumeric or non underscore character": {
inputShapeName: "Sample&__Shape",
shapeType: "structure",
expectedShapeName: "",
expectedError: "invalid shape name found",
},
"Renamed Shape already exists": {
inputShapeName: "__sample_shape",
shapeType: "structure",
expectedShapeName: "",
expectedError: "rename would result in shape name collision",
},
"empty case for enums shape type": {
inputShapeName: "",
shapeType: "string",
expectedShapeName: "",
expectedError: "invalid shape name found",
},
"No rename for enums shape type": {
inputShapeName: "Sample123Shape",
shapeType: "string",
expectedShapeName: "Sample123Shape",
},
"starts with underscores for enums shape type": {
inputShapeName: "__Sample123Shape",
shapeType: "string",
expectedShapeName: "Sample123Shape",
},
"Contains underscores for enums shape type": {
inputShapeName: "__sample_123_shape__",
shapeType: "string",
expectedShapeName: "Sample123Shape",
},
"Starts with numeric character for enums shape type": {
inputShapeName: "123__sampleShape",
shapeType: "string",
expectedShapeName: "",
expectedError: "invalid shape name found",
},
"Starts with non alphabetic or non underscore character for enums shape type": {
inputShapeName: "&&SampleShape",
shapeType: "string",
expectedShapeName: "",
expectedError: "invalid shape name found",
},
"Contains non Alphanumeric or non underscore character for enums shape type": {
inputShapeName: "Sample&__Shape",
shapeType: "string",
expectedShapeName: "",
expectedError: "invalid shape name found",
},
"Renamed Shape already exists for enums shape type": {
inputShapeName: "__sample_shape",
shapeType: "string",
expectedShapeName: "",
expectedError: "rename would result in shape name collision",
},
}

for name, c := range cases {
operation := "FooOperation"
t.Run(name, func(t *testing.T) {
a := &API{
Operations: map[string]*Operation{},
Shapes: map[string]*Shape{},
}
// add another shape with name SampleShape to check for collision
a.Shapes["SampleShape"] = &Shape{ShapeName: "SampleShape"}
o := &Operation{
Name: operation,
ExportedName: operation,
InputRef: ShapeRef{
API: a,
ShapeName: c.inputShapeName,
Shape: &Shape{
API: a,
ShapeName: c.inputShapeName,
Type: c.shapeType,
Enum: []string{"x"},
},
},
}
o.InputRef.Shape.refs = append(o.InputRef.Shape.refs, &o.InputRef)
a.Operations[o.Name] = o
a.Shapes[c.inputShapeName] = o.InputRef.Shape

err := a.validateShapeNames()
if err != nil || c.expectedError != "" {
if err == nil {
t.Fatalf("Received no error, expected error with log: \n \t %v ", c.expectedError)
}
if c.expectedError == "" {
t.Fatalf("Expected no error, got %v", err.Error())
}
if e, a := err.Error(), c.expectedError; !strings.Contains(e, a) {
t.Fatalf("Expected to receive error containing %v, got %v", e, a)
}
return
}

if e, a := c.expectedShapeName, o.InputRef.Shape.ShapeName; e != a {
t.Fatalf("Expected shape name to be %v, got %v", e, a)
}
})
}
}
34 changes: 17 additions & 17 deletions service/ec2/api_enums.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading