Skip to content

Commit

Permalink
Allow 'web' as a hard coded resource name (#4489)
Browse files Browse the repository at this point in the history
* Refactor tests and add failing test case

* Allow 'web' as hard coded name too
  • Loading branch information
theunrepentantgeek authored Dec 6, 2024
1 parent 3e3f11a commit f98e00d
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 93 deletions.
34 changes: 19 additions & 15 deletions v2/tools/generator/internal/jsonast/swagger_type_extractor.go
Original file line number Diff line number Diff line change
Expand Up @@ -786,6 +786,10 @@ func (extractor *SwaggerTypeExtractor) extractResourceSubpath(operationPath stri
return "", "", eris.Errorf("no group name (‘Microsoft…’) found in %s", operationPath)
}

// Some services hardcode default into their URLs like : blobService/default/containers/{containerName}
// and we don't want the "default" name to be part of the resource type name for those cases, so we detect these hard coded names.
var hardCodedNames = set.Make("default", "web")

// inferNameFromURLPath attempts to extract a name from a Swagger operation path
// for example “…/Microsoft.GroupName/resourceType/{resourceId}” would result
// in the name “ResourceType”. Child resources are treated by converting (e.g.)
Expand All @@ -805,31 +809,31 @@ func (extractor *SwaggerTypeExtractor) inferNameFromURLPath(operationPath string
}

urlParts := strings.Split(subpath, "/")
skippedLast := false
expectingKind := true
for _, urlPart := range urlParts {
if len(urlPart) == 0 {
// skip empty parts
continue
}

// If default was defined as an enum in the Swagger, this check is not needed as it wouldn't have been expanded by
// the expandEnumsInPath method, but some services hardcode default into their URLs like : blobService/default/containers/{containerName}
// and we don't want the "default" name to be part of the resource type name for those cases, so we ignore it here.
if strings.EqualFold(urlPart, "default") {
// skip; shouldn’t be part of name
// TODO: I haven’t yet found where this is done in autorest/autorest.armresource to document this
} else if urlPart[0] == '{' {
// this is a URL parameter
if skippedLast {
// this means two {parameters} in a row
if expectingKind {
// Expecting a resource kind
if urlPart[0] == '{' {
// But we have a parameter, which means two parameters in a row
return "", "", "", eris.Errorf("multiple parameters in path")
}

skippedLast = true
} else {
// normal part of path, uppercase first character
// Add the resource kind to the name of this resource
nameParts = append(nameParts, urlPart)
skippedLast = false
expectingKind = false
} else {
// Expecting a parameter
if urlPart[0] != '{' && !hardCodedNames.Contains(strings.ToLower(urlPart)) {
// We didn't expect this name to be hard-coded
return "", "", "", eris.Errorf("unexpected hard coded name %q in path %s", urlPart, operationPath)
}

expectingKind = true
}
}

Expand Down
141 changes: 63 additions & 78 deletions v2/tools/generator/internal/jsonast/swagger_type_extractor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,93 +18,78 @@ import (
"github.com/Azure/azure-service-operator/v2/tools/generator/internal/config"
)

func Test_InferNameFromURLPath_ParentResource(t *testing.T) {
func Test_InferNameFromPath_GivenURL_ReturnsExpectedResult(t *testing.T) {
t.Parallel()
g := NewGomegaWithT(t)

extractor := &SwaggerTypeExtractor{
idFactory: astmodel.NewIdentifierFactory(),
}

group, resource, name, err := extractor.inferNameFromURLPath("/Microsoft.GroupName/resourceName/{resourceId}")
t.Logf("%s/%s: %s", group, resource, name)
// Output: Microsoft.GroupName/resourceName: ResourceName
g.Expect(err).ToNot(HaveOccurred())
g.Expect(group).To(Equal("Microsoft.GroupName"))
g.Expect(resource).To(Equal("resourceName"))
g.Expect(name).To(Equal("ResourceName"))
}

func Test_InferNameFromURLPath_ChildResources(t *testing.T) {
t.Parallel()
g := NewGomegaWithT(t)

extractor := &SwaggerTypeExtractor{
idFactory: astmodel.NewIdentifierFactory(),
}

group, resource, name, err := extractor.inferNameFromURLPath("/Microsoft.GroupName/resourceName/{resourceId}/someChild/{childId}")
t.Logf("%s/%s: %s", group, resource, name)
// Output: Microsoft.GroupName/resourceName/someChild: ResourceName_SomeChild
g.Expect(err).ToNot(HaveOccurred())
g.Expect(group).To(Equal("Microsoft.GroupName"))
g.Expect(resource).To(Equal("resourceName/someChild"))
g.Expect(name).To(Equal("ResourceName_SomeChild"))
}

func Test_InferNameFromURLPath_FailsWithMultipleParametersInARow(t *testing.T) {
t.Parallel()
g := NewGomegaWithT(t)
extractor := &SwaggerTypeExtractor{
idFactory: astmodel.NewIdentifierFactory(),
}

_, _, _, err := extractor.inferNameFromURLPath("/Microsoft.GroupName/resourceName/{resourceId}/{anotherParameter}")
g.Expect(err).To(Not(BeNil()))
g.Expect(err.Error()).To(ContainSubstring("multiple parameters"))
}

func Test_InferNameFromURLPath_FailsWithNoGroupName(t *testing.T) {
t.Parallel()
g := NewGomegaWithT(t)

extractor := &SwaggerTypeExtractor{
idFactory: astmodel.NewIdentifierFactory(),
cases := map[string]struct {
path string
group string
resource string
name string
err string
}{
"ParentResource": {
path: "/Microsoft.GroupName/resourceName/{resourceId}",
group: "Microsoft.GroupName",
resource: "resourceName",
name: "ResourceName",
},
"ChildResources": {
path: "/Microsoft.GroupName/resourceName/{resourceId}/someChild/{childId}",
group: "Microsoft.GroupName",
resource: "resourceName/someChild",
name: "ResourceName_SomeChild",
},
"FailsWithMultipleParametersInARow": {
path: "/Microsoft.GroupName/resourceName/{resourceId}/{anotherParameter}",
err: "multiple parameters",
},
"FailsWithNoGroupName": {
path: "/resourceName/{resourceId}/{anotherParameter}",
err: "no group name",
},
"SkipsDefault": {
path: "Microsoft.Storage/storageAccounts/{accountName}/blobServices/default/containers/{containerName}",
group: "Microsoft.Storage",
resource: "storageAccounts/blobServices/containers",
name: "StorageAccounts_BlobServices_Container",
},
"SkipsWeb": {
path: "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Web/sites/{name}/slots/{slot}/sourcecontrols/web",
group: "Microsoft.Web",
resource: "sites/slots/sourcecontrols",
name: "Sites_Slots_Sourcecontrol",
},
"ExtensionResource": {
path: "/{scope}/providers/Microsoft.Authorization/roleAssignments/{roleAssignmentName}",
group: "Microsoft.Authorization",
resource: "roleAssignments",
name: "RoleAssignment",
},
}

_, _, _, err := extractor.inferNameFromURLPath("/resourceName/{resourceId}/{anotherParameter}")
g.Expect(err).To(Not(BeNil()))
g.Expect(err.Error()).To(ContainSubstring("no group name"))
}

func Test_InferNameFromURLPath_SkipsDefault(t *testing.T) {
t.Parallel()
g := NewGomegaWithT(t)

extractor := &SwaggerTypeExtractor{
idFactory: astmodel.NewIdentifierFactory(),
}
for name, c := range cases {
t.Run(name, func(t *testing.T) {
t.Parallel()
g := NewGomegaWithT(t)

group, resource, name, err := extractor.inferNameFromURLPath("Microsoft.Storage/storageAccounts/{accountName}/blobServices/default/containers/{containerName}")
g.Expect(err).To(BeNil())
g.Expect(group).To(Equal("Microsoft.Storage"))
g.Expect(resource).To(Equal("storageAccounts/blobServices/containers"))
g.Expect(name).To(Equal("StorageAccounts_BlobServices_Container"))
}

func Test_InferNameFromURLPath_ExtensionResource(t *testing.T) {
t.Parallel()
g := NewGomegaWithT(t)

extractor := &SwaggerTypeExtractor{
idFactory: astmodel.NewIdentifierFactory(),
group, resource, name, err := extractor.inferNameFromURLPath(c.path)
t.Logf("%s/%s: %s", group, resource, name)

if c.err != "" {
g.Expect(err).To(HaveOccurred())
g.Expect(err.Error()).To(ContainSubstring(c.err))
} else {
g.Expect(err).ToNot(HaveOccurred())
g.Expect(group).To(Equal(c.group))
g.Expect(resource).To(Equal(c.resource))
g.Expect(name).To(Equal(c.name))
}
})
}

group, resource, name, err := extractor.inferNameFromURLPath("/{scope}/providers/Microsoft.Authorization/roleAssignments/{roleAssignmentName}")
g.Expect(err).To(BeNil())
g.Expect(group).To(Equal("Microsoft.Authorization"))
g.Expect(resource).To(Equal("roleAssignments"))
g.Expect(name).To(Equal("RoleAssignment"))
}

func Test_extractLastPathParam_ExtractsParameter(t *testing.T) {
Expand Down

0 comments on commit f98e00d

Please sign in to comment.