Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support wildcards with transformation targets #2325

Merged
merged 19 commits into from
Jun 9, 2022
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,6 @@ dist
template.json

hack/tools

# Private powershell scripts
.ps
Original file line number Diff line number Diff line change
Expand Up @@ -110,14 +110,23 @@ func (pr LocalPackageReference) HasVersionPrefix(prefix string) bool {
return pr.generatorVersion == prefix
}

// GeneratorVersion returns the part of the package name refering to the version of the generator
func (pr LocalPackageReference) GeneratorVersion() string {
return pr.generatorVersion
}

// ApiVersion returns the API version of this reference, separate from the generator version
func (pr LocalPackageReference) ApiVersion() string {
return pr.apiVersion
}

// HasApiVersion returns true if this reference has the specified API version
func (pr LocalPackageReference) HasApiVersion(ver string) bool {
// TODO: When we start preserving the API version properly switch this to
matthchr marked this conversation as resolved.
Show resolved Hide resolved
// a simple strings.EqualFold()
return strings.EqualFold(pr.apiVersion, sanitizePackageName(ver))
}

// IsLocalPackageReference returns true if the supplied reference is a local one
func IsLocalPackageReference(ref PackageReference) bool {
_, ok := ref.(LocalPackageReference)
Expand Down
6 changes: 4 additions & 2 deletions v2/tools/generator/internal/astmodel/meta_type.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,8 @@ func AsEnumType(aType Type) (*EnumType, bool) {
return nil, false
}

// AsTypeName unwraps any wrappers around the provided type and returns either the underlying TypeName and true, or a blank and false.
// AsTypeName unwraps any wrappers around the provided type and returns either the underlying TypeName and true, or a
// blank and false.
func AsTypeName(aType Type) (TypeName, bool) {
if name, ok := aType.(TypeName); ok {
return name, true
Expand All @@ -111,7 +112,8 @@ func AsTypeName(aType Type) (TypeName, bool) {
return TypeName{}, false
}

// AsResourceType unwraps any wrappers around the provided type and returns either the underlying ResourceType and true, or a nil and false.
// AsResourceType unwraps any wrappers around the provided type and returns either the underlying ResourceType and true,
// or a nil and false.
func AsResourceType(aType Type) (*ResourceType, bool) {
if name, ok := aType.(*ResourceType); ok {
return name, true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func ApplyPropertyRewrites(config *config.Configuration) *Stage {

transformations := config.TransformTypeProperties(name, objectType)
for _, transformation := range transformations {
klog.V(2).Infof("Transforming %s", transformation)
klog.V(3).Infof("Transforming %s", transformation)
objectType = transformation.NewType
}

Expand Down
5 changes: 5 additions & 0 deletions v2/tools/generator/internal/config/always_matcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,8 @@ func (a alwaysMatcher) Matches(value string) bool {
func (a alwaysMatcher) WasMatched() error {
return nil
}

// IsRestrictive returns false because the always matcher doesn't restrict anything
func (a alwaysMatcher) IsRestrictive() bool {
return false
}
2 changes: 1 addition & 1 deletion v2/tools/generator/internal/config/configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ func (config *Configuration) initialize(configPath string) error {
errs = append(errs, err)
}

if transformer.Property.String() != "" {
if transformer.Property.IsRestrictive() {
propertyTransformers = append(propertyTransformers, transformer)
} else {
typeTransformers = append(typeTransformers, transformer)
Expand Down
5 changes: 5 additions & 0 deletions v2/tools/generator/internal/config/field_matcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ func (dm *FieldMatcher) WasMatched() error {
return dm.actual.WasMatched()
}

// IsRestrictive returns true if our nested matcher is present and restrictive, false otherwise
func (dm *FieldMatcher) IsRestrictive() bool {
return dm.actual != nil && dm.actual.IsRestrictive()
}

// UnmarshalYAML populates our instance from the YAML.
// We expect just a single string, which we use create an actual StringMatcher
func (dm *FieldMatcher) UnmarshalYAML(value *yaml.Node) error {
Expand Down
29 changes: 29 additions & 0 deletions v2/tools/generator/internal/config/field_matcher_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,32 @@ func TestFieldMatcher_DeserializedFromYaml_GivesExpectedMatchResult(t *testing.T
type matcherHost struct {
Field FieldMatcher `yaml:"field"`
}

func TestFieldMatcher_IsRestrictive_GivesExpectedResults(t *testing.T) {
t.Parallel()

cases := []struct {
name string
yaml string
restrictive bool
}{
{"Empty literal is unrestrictive", "field: ''", false},
{"Wildcard is unrestrictive", "field: '*'", false},
{"Literal is restrictive", "field: Foo", true},
{"Complex wildcard is restrictive", "field: Foo*", true},
}

for _, c := range cases {
c := c
t.Run(
c.name,
func(t *testing.T) {
t.Parallel()
g := NewGomegaWithT(t)

var h matcherHost
g.Expect(yaml.Unmarshal([]byte(c.yaml), &h)).To(Succeed())
g.Expect(h.Field.IsRestrictive()).To(Equal(c.restrictive))
})
}
}
5 changes: 5 additions & 0 deletions v2/tools/generator/internal/config/glob_matcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@ func (gm *globMatcher) WasMatched() error {
strings.Join(choices, ", "))
}

// IsRestrictive returns false if we are blank or a universal wildcard, true otherwise.
func (gm *globMatcher) IsRestrictive() bool {
return gm.glob != "" && gm.glob != "*"
}

func (gm *globMatcher) createRegex() {
g := regexp.QuoteMeta(gm.glob)
g = strings.ReplaceAll(g, "\\*", ".*")
Expand Down
25 changes: 25 additions & 0 deletions v2/tools/generator/internal/config/glob_matcher_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,28 @@ func TestGlobMatcher_GivenTerms_MatchesExpectedStrings(t *testing.T) {
})
}
}

func TestGlobMatcher_IsRestrictive_GivesExpectedResults(t *testing.T) {
t.Parallel()

cases := []struct {
name string
glob string
restrictive bool
}{
{"Wildcard is not restrictive", "*", false},
{"Complex match is restrictive", "Foo*", true},
}

for _, c := range cases {
c := c
t.Run(
c.name,
func(t *testing.T) {
t.Parallel()
g := NewGomegaWithT(t)
matcher := newGlobMatcher(c.glob)
g.Expect(matcher.IsRestrictive()).To(Equal(c.restrictive))
})
}
}
5 changes: 5 additions & 0 deletions v2/tools/generator/internal/config/literal_matcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@ func (lm *literalMatcher) WasMatched() error {
return lm.advisor.Errorf(lm.literal, "no match for %q", lm.literal)
}

// IsRestrictive returns true if we have a non-empty literal to match
func (lm *literalMatcher) IsRestrictive() bool {
return lm.literal != ""
}

// String returns the literal we match
func (lm *literalMatcher) String() string {
return lm.literal
Expand Down
26 changes: 26 additions & 0 deletions v2/tools/generator/internal/config/literal_matcher_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,29 @@ func TestLiteralMatcher_Matches_GivesExpectedResults(t *testing.T) {
})
}
}

func TestLiteralMatcher_IsRestrictive_GivesExpectedResults(t *testing.T) {
t.Parallel()

cases := []struct {
name string
literal string
restrictive bool
}{
{"Empty is not restrictive", "", false},
{"Whitespace is not restrictive", " ", false},
{"Content is restrictive", "content", true},
}

for _, c := range cases {
c := c
t.Run(
c.name,
func(t *testing.T) {
t.Parallel()
g := NewGomegaWithT(t)
matcher := newLiteralMatcher(c.literal)
g.Expect(matcher.IsRestrictive()).To(Equal(c.restrictive))
})
}
}
17 changes: 14 additions & 3 deletions v2/tools/generator/internal/config/multi_matcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,15 @@ func (mm *MultiMatcher) WasMatched() error {
len(mm.matchers))
}

// HasMultipleMatchers returns true if the matcher contains multiple definitions
func HasMultipleMatchers(matcher string) bool {
return strings.ContainsRune(matcher, ';')
// IsRestrictive returns true if any of our contained matchers are restrictive
func (mm MultiMatcher) IsRestrictive() bool {
for _, m := range mm.matchers {
if m.IsRestrictive() {
return true
}
}

return false
}

// String returns all the matchers we contain
Expand All @@ -83,3 +89,8 @@ func (mm *MultiMatcher) String() string {

return builder.String()
}

// HasMultipleMatchers returns true if the matcher contains multiple definitions
func HasMultipleMatchers(matcher string) bool {
return strings.ContainsRune(matcher, ';')
}
26 changes: 26 additions & 0 deletions v2/tools/generator/internal/config/multi_matcher_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,3 +105,29 @@ func TestMultiMatcher_DoesNotShortCircuit(t *testing.T) {
}

}

func TestMultiMatcher_IsRestrictive_GivesExpectedResults(t *testing.T) {
t.Parallel()

cases := []struct {
name string
matcher string
restrictive bool
}{
{"Two unrestrictive matchers are unrestrictive", "*;", false},
{"Restrictive wildcard is restrictive", "Foo*;", true},
{"Restrictive literal is restrictive", "*;Foo", true},
}

for _, c := range cases {
c := c
t.Run(
c.name,
func(t *testing.T) {
t.Parallel()
g := NewGomegaWithT(t)
matcher := newMultiMatcher(c.matcher)
g.Expect(matcher.IsRestrictive()).To(Equal(c.restrictive))
})
}
}
2 changes: 2 additions & 0 deletions v2/tools/generator/internal/config/string_matcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ type StringMatcher interface {
Matches(value string) bool
// WasMatched returns nil if the matcher had a match, otherwise returning a diagnostic error
WasMatched() error
// IsRestrictive returns true if the matcher is populated and will restrict matches
IsRestrictive() bool
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This name threw me for a bit before I understood what it was doing. I'm not sure that I necessarily have a better name for it, but wondering if there is one.

NeedsMatch or RequiresMatch? Not really sure those are any better...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think those are better - they imply that tha match is required, whereas the semantics I'm looking for are "is there a matcher here that will reduce the number of matches?"

}

// NewStringMatcher returns a matcher for the specified string
Expand Down
6 changes: 3 additions & 3 deletions v2/tools/generator/internal/config/type_matcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,17 +108,17 @@ func (t *TypeMatcher) WasMatched() error {
func (t *TypeMatcher) String() string {
var result strings.Builder
var spacer string
if t.Group.String() != "" {
if t.Group.IsRestrictive() {
result.WriteString(fmt.Sprintf("Group: %q", t.Group.String()))
spacer = "; "
}

if t.Version.String() != "" {
if t.Version.IsRestrictive() {
result.WriteString(fmt.Sprintf("%sVersion: %q", spacer, t.Version.String()))
spacer = "; "
}

if t.Name.String() != "" {
if t.Name.IsRestrictive() {
result.WriteString(fmt.Sprintf("%sName: %q", spacer, t.Name.String()))
spacer = "; "
}
Expand Down
Loading