-
Notifications
You must be signed in to change notification settings - Fork 43
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
commonids: add composite resource id #208
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,83 @@ | ||||||||||||||||||||||||||||||||||||||||||
package commonids | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
import ( | ||||||||||||||||||||||||||||||||||||||||||
"fmt" | ||||||||||||||||||||||||||||||||||||||||||
"strings" | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
"github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" | ||||||||||||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
// CompositeResourceID is a struct representing the Resource ID for a Composite Resource Id | ||||||||||||||||||||||||||||||||||||||||||
type CompositeResourceID[T1 resourceids.ResourceId, T2 resourceids.ResourceId] struct { | ||||||||||||||||||||||||||||||||||||||||||
First T1 | ||||||||||||||||||||||||||||||||||||||||||
Second T2 | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
// ID returns the formatted Composite Resource Id | ||||||||||||||||||||||||||||||||||||||||||
func (id CompositeResourceID[T1, T2]) ID() string { | ||||||||||||||||||||||||||||||||||||||||||
fmtString := "%s|%s" | ||||||||||||||||||||||||||||||||||||||||||
return fmt.Sprintf(fmtString, id.First.ID(), id.Second.ID()) | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
// String returns a human-readable description of this Composite Resource Id | ||||||||||||||||||||||||||||||||||||||||||
func (id CompositeResourceID[T1, T2]) String() string { | ||||||||||||||||||||||||||||||||||||||||||
fmtString := "%s\n%s" | ||||||||||||||||||||||||||||||||||||||||||
return fmt.Sprintf(fmtString, id.First.String(), id.Second.String()) | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we call out that this is a Composite Resource ID:
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
// ParseCompositeResourceID parses 'input' and two ResourceIds (first,second) into a CompositeResourceID | ||||||||||||||||||||||||||||||||||||||||||
// The 'input' should be a string containing 2 resource ids separated by "|" | ||||||||||||||||||||||||||||||||||||||||||
// eg: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group|/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group/providers/Microsoft.Sql/servers/serverValue" | ||||||||||||||||||||||||||||||||||||||||||
// The first and second ResourceIds should match the types in the 'input' string in the order in which they appear | ||||||||||||||||||||||||||||||||||||||||||
// eg: | ||||||||||||||||||||||||||||||||||||||||||
// | ||||||||||||||||||||||||||||||||||||||||||
// input := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group|/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group/providers/Microsoft.Sql/servers/serverValue" | ||||||||||||||||||||||||||||||||||||||||||
// first := ResourceGroupId{} | ||||||||||||||||||||||||||||||||||||||||||
// second := SqlServerId{} | ||||||||||||||||||||||||||||||||||||||||||
// id, err := ParseCompositeResourceID(input, &first, &second) | ||||||||||||||||||||||||||||||||||||||||||
func ParseCompositeResourceID[T1 resourceids.ResourceId, T2 resourceids.ResourceId](input string, first T1, second T2) (*CompositeResourceID[T1, T2], error) { | ||||||||||||||||||||||||||||||||||||||||||
return parse(input, first, second, false) | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
// ParseCompositeResourceIDInsensitively parses 'input' and two ResourceIds (first,second) case-insensitively into a CompositeResourceID | ||||||||||||||||||||||||||||||||||||||||||
// note: this method should only be used for API response data and not user input | ||||||||||||||||||||||||||||||||||||||||||
func ParseCompositeResourceIDInsensitively[T1 resourceids.ResourceId, T2 resourceids.ResourceId](input string, first T1, second T2) (*CompositeResourceID[T1, T2], error) { | ||||||||||||||||||||||||||||||||||||||||||
return parse(input, first, second, true) | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
func parse[T1 resourceids.ResourceId, T2 resourceids.ResourceId](input string, first T1, second T2, insensitively bool) (*CompositeResourceID[T1, T2], error) { | ||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. minor but could we update
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
components := strings.Split(input, "|") | ||||||||||||||||||||||||||||||||||||||||||
if len(components) != 2 { | ||||||||||||||||||||||||||||||||||||||||||
return nil, fmt.Errorf("expected 2 resourceids but got %d", len(components)) | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
output := CompositeResourceID[T1, T2]{ | ||||||||||||||||||||||||||||||||||||||||||
First: first, | ||||||||||||||||||||||||||||||||||||||||||
Second: second, | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
// Parse the first of the two Resource IDs from the components | ||||||||||||||||||||||||||||||||||||||||||
firstParser := resourceids.NewParserFromResourceIdType(output.First) | ||||||||||||||||||||||||||||||||||||||||||
firstParseResult, err := firstParser.Parse(components[0], insensitively) | ||||||||||||||||||||||||||||||||||||||||||
if err != nil { | ||||||||||||||||||||||||||||||||||||||||||
return nil, fmt.Errorf("parsing first id part %q of CompositeResourceID: %v", components[0], err) | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
err = output.First.FromParseResult(*firstParseResult) | ||||||||||||||||||||||||||||||||||||||||||
if err != nil { | ||||||||||||||||||||||||||||||||||||||||||
return nil, fmt.Errorf("populating first id part %q of CompositeResourceID: %v", components[0], err) | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
// Parse the second of the two Resource IDs from the components | ||||||||||||||||||||||||||||||||||||||||||
secondParser := resourceids.NewParserFromResourceIdType(output.Second) | ||||||||||||||||||||||||||||||||||||||||||
secondParseResult, err := secondParser.Parse(components[1], insensitively) | ||||||||||||||||||||||||||||||||||||||||||
if err != nil { | ||||||||||||||||||||||||||||||||||||||||||
return nil, fmt.Errorf("parsing second id part %q of CompositeResourceID: %v", components[1], err) | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
err = output.Second.FromParseResult(*secondParseResult) | ||||||||||||||||||||||||||||||||||||||||||
if err != nil { | ||||||||||||||||||||||||||||||||||||||||||
return nil, fmt.Errorf("populating second id part %q of CompositeResourceID: %v", components[1], err) | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
return &output, nil | ||||||||||||||||||||||||||||||||||||||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
package commonids | ||
|
||
import ( | ||
"fmt" | ||
"testing" | ||
) | ||
|
||
func TestCompositeResourceID(t *testing.T) { | ||
idString := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group/providers/Microsoft.BotService/botServices/botServiceValue|/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group/providers/Microsoft.Web/sites/siteValue" | ||
|
||
id1 := BotServiceId{} | ||
id2 := AppServiceId{} | ||
id, err := ParseCompositeResourceID(idString, &id1, &id2) | ||
if err != nil { | ||
t.Fatalf("Expected CompositeResourceID to parse successfully but got Error: %q", err) | ||
} | ||
|
||
expectedId1 := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group/providers/Microsoft.BotService/botServices/botServiceValue" | ||
actualId1 := id.First.ID() | ||
if expectedId1 != actualId1 { | ||
t.Fatalf("Expected the First ID to be %q but got %q", expectedId1, actualId1) | ||
} | ||
|
||
expectedId2 := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group/providers/Microsoft.Web/sites/siteValue" | ||
actualId2 := id.Second.ID() | ||
if expectedId2 != actualId2 { | ||
t.Fatalf("Expected the Second ID to be %q but got %q", expectedId2, actualId2) | ||
} | ||
|
||
if idString != id.ID() { | ||
t.Fatalf("Expected the Composite ID to be %q but got %q", idString, id.ID()) | ||
} | ||
|
||
expectedIdString1 := `Bot Service (Subscription: "12345678-1234-9876-4563-123456789012" | ||
Resource Group Name: "example-resource-group" | ||
Bot Service Name: "botServiceValue")` | ||
actualIdString1 := id.First.String() | ||
if expectedIdString1 != actualIdString1 { | ||
t.Fatalf("Expected the First ID String to be %q but got %q", expectedIdString1, actualIdString1) | ||
} | ||
|
||
expectedIdString2 := `App Service (Subscription: "12345678-1234-9876-4563-123456789012" | ||
Resource Group Name: "example-resource-group" | ||
Site Name: "siteValue")` | ||
actualIdString2 := id.Second.String() | ||
if expectedIdString2 != actualIdString2 { | ||
t.Fatalf("Expected the Second ID String to be %q but got %q", expectedIdString2, actualIdString2) | ||
} | ||
|
||
expectedCompositeString := fmt.Sprintf("%s\n%s", expectedIdString1, expectedIdString2) | ||
actualCompositeString := id.String() | ||
if expectedCompositeString != actualCompositeString { | ||
t.Fatalf("Expected the Composite ID String to be %q but got %q", expectedCompositeString, actualCompositeString) | ||
} | ||
} | ||
|
||
func TestParseCompositeResourceIDInsensitively(t *testing.T) { | ||
idIncorrectCasing := "/subscriptions/12345678-1234-9876-4563-123456789012/ReSoUrCeGrOuPs/example-resource-group/providers/Microsoft.BotService/botServices/botServiceValue|/subscriptions/12345678-1234-9876-4563-123456789012/ReSoUrCeGrOuPs/example-resource-group/providers/Microsoft.Web/sites/siteValue" | ||
idCorrectCasing := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group/providers/Microsoft.BotService/botServices/botServiceValue|/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group/providers/Microsoft.Web/sites/siteValue" | ||
|
||
id1 := BotServiceId{} | ||
id2 := AppServiceId{} | ||
id, err := ParseCompositeResourceIDInsensitively(idIncorrectCasing, &id1, &id2) | ||
if err != nil { | ||
t.Fatalf("Expected CompositeResourceID to parse successfully but got Error: %q", err) | ||
} | ||
|
||
expectedId1 := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group/providers/Microsoft.BotService/botServices/botServiceValue" | ||
actualId1 := id.First.ID() | ||
if expectedId1 != actualId1 { | ||
t.Fatalf("Expected the First ID to be %q but got %q", expectedId1, actualId1) | ||
} | ||
|
||
expectedId2 := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group/providers/Microsoft.Web/sites/siteValue" | ||
actualId2 := id.Second.ID() | ||
if expectedId2 != actualId2 { | ||
t.Fatalf("Expected the Second ID to be %q but got %q", expectedId2, actualId2) | ||
} | ||
|
||
if idCorrectCasing != id.ID() { | ||
t.Fatalf("Expected the Composite ID to be %q but got %q", idIncorrectCasing, id.ID()) | ||
} | ||
|
||
expectedIdString1 := `Bot Service (Subscription: "12345678-1234-9876-4563-123456789012" | ||
Resource Group Name: "example-resource-group" | ||
Bot Service Name: "botServiceValue")` | ||
actualIdString1 := id.First.String() | ||
if expectedIdString1 != actualIdString1 { | ||
t.Fatalf("Expected the First ID String to be %q but got %q", expectedIdString1, actualIdString1) | ||
} | ||
|
||
expectedIdString2 := `App Service (Subscription: "12345678-1234-9876-4563-123456789012" | ||
Resource Group Name: "example-resource-group" | ||
Site Name: "siteValue")` | ||
actualIdString2 := id.Second.String() | ||
if expectedIdString2 != actualIdString2 { | ||
t.Fatalf("Expected the Second ID String to be %q but got %q", expectedIdString2, actualIdString2) | ||
} | ||
|
||
expectedCompositeString := fmt.Sprintf("%s\n%s", expectedIdString1, expectedIdString2) | ||
actualCompositeString := id.String() | ||
if expectedCompositeString != actualCompositeString { | ||
t.Fatalf("Expected the Composite ID String to be %q but got %q", expectedCompositeString, actualCompositeString) | ||
} | ||
} | ||
|
||
func TestCompositeResourceIDNumberOfIDsErrors(t *testing.T) { | ||
id1 := BotServiceId{} | ||
id2 := AppServiceId{} | ||
testData := []struct { | ||
Input string | ||
ExpectedError string | ||
}{ | ||
{ | ||
// 1 ID | ||
Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group/providers/Microsoft.BotService/botServices/botServiceValue", | ||
ExpectedError: "expected 2 resourceids but got 1", | ||
}, | ||
{ | ||
// 3 IDs | ||
Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group/providers/Microsoft.BotService/botServices/botServiceValue|/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group/providers/Microsoft.BotService/botServices/botServiceValue|/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group/providers/Microsoft.BotService/botServices/botServiceValue", | ||
ExpectedError: "expected 2 resourceids but got 3", | ||
}, | ||
} | ||
|
||
for _, v := range testData { | ||
t.Logf("[DEBUG] Testing %q", v.Input) | ||
|
||
_, err := ParseCompositeResourceID(v.Input, &id1, &id2) | ||
if err == nil { | ||
t.Fatalf("Expected an error but didn't get one") | ||
} | ||
if err.Error() != v.ExpectedError { | ||
t.Fatalf("Expected error %q but got %q", v.ExpectedError, err.Error()) | ||
} | ||
} | ||
} | ||
|
||
func TestCompositeResourceIDInvalidIDs(t *testing.T) { | ||
|
||
id1 := BotServiceId{} | ||
id2 := AppServiceId{} | ||
|
||
idStringFirstInvalid := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group/providers/Microsoft.BotService|/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group/providers/Microsoft.Web/sites/siteValue" | ||
_, err := ParseCompositeResourceID(idStringFirstInvalid, &id1, &id2) | ||
if err == nil { | ||
t.Fatalf("Expected error but didn't get one") | ||
} | ||
|
||
idStringSecondInvalid := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group/providers/Microsoft.BotService/botServices/botServiceValue|/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group/providers/Microsoft.Web" | ||
_, err = ParseCompositeResourceID(idStringSecondInvalid, &id1, &id2) | ||
if err == nil { | ||
t.Fatalf("Expected error but didn't get one") | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
minor but could we document these fields so that it's clearer what these are?