From ac670a559e1d4b2401a9380b2d67e27eb3261eb0 Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Mon, 16 Oct 2023 07:57:47 +0200 Subject: [PATCH] New Common IDs for Kusto Cluster and Kusto Database Reduces external package churn as seen in https://github.com/hashicorp/terraform-provider-azurerm/pull/23381 --- resourcemanager/commonids/kusto_cluster.go | 127 +++++++ .../commonids/kusto_cluster_test.go | 282 +++++++++++++++ resourcemanager/commonids/kusto_database.go | 140 ++++++++ .../commonids/kusto_database_test.go | 327 ++++++++++++++++++ 4 files changed, 876 insertions(+) create mode 100644 resourcemanager/commonids/kusto_cluster.go create mode 100644 resourcemanager/commonids/kusto_cluster_test.go create mode 100644 resourcemanager/commonids/kusto_database.go create mode 100644 resourcemanager/commonids/kusto_database_test.go diff --git a/resourcemanager/commonids/kusto_cluster.go b/resourcemanager/commonids/kusto_cluster.go new file mode 100644 index 0000000..a653235 --- /dev/null +++ b/resourcemanager/commonids/kusto_cluster.go @@ -0,0 +1,127 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package commonids + +import ( + "fmt" + "strings" + + "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" +) + +var _ resourceids.ResourceId = KustoClusterId{} + +// KustoClusterId is a struct representing the Resource ID for a Kusto Cluster +type KustoClusterId struct { + SubscriptionId string + ResourceGroupName string + KustoClusterName string +} + +// NewKustoClusterID returns a new KustoClusterId struct +func NewKustoClusterID(subscriptionId string, resourceGroupName string, kustoClusterName string) KustoClusterId { + return KustoClusterId{ + SubscriptionId: subscriptionId, + ResourceGroupName: resourceGroupName, + KustoClusterName: kustoClusterName, + } +} + +// ParseKustoClusterID parses 'input' into a KustoClusterId +func ParseKustoClusterID(input string) (*KustoClusterId, error) { + parser := resourceids.NewParserFromResourceIdType(KustoClusterId{}) + parsed, err := parser.Parse(input, false) + if err != nil { + return nil, fmt.Errorf("parsing %q: %+v", input, err) + } + + var ok bool + id := KustoClusterId{} + + if id.SubscriptionId, ok = parsed.Parsed["subscriptionId"]; !ok { + return nil, resourceids.NewSegmentNotSpecifiedError(id, "subscriptionId", *parsed) + } + + if id.ResourceGroupName, ok = parsed.Parsed["resourceGroupName"]; !ok { + return nil, resourceids.NewSegmentNotSpecifiedError(id, "resourceGroupName", *parsed) + } + + if id.KustoClusterName, ok = parsed.Parsed["kustoClusterName"]; !ok { + return nil, resourceids.NewSegmentNotSpecifiedError(id, "kustoClusterName", *parsed) + } + + return &id, nil +} + +// ParseKustoClusterIDInsensitively parses 'input' case-insensitively into a KustoClusterId +// note: this method should only be used for API response data and not user input +func ParseKustoClusterIDInsensitively(input string) (*KustoClusterId, error) { + parser := resourceids.NewParserFromResourceIdType(KustoClusterId{}) + parsed, err := parser.Parse(input, true) + if err != nil { + return nil, fmt.Errorf("parsing %q: %+v", input, err) + } + + var ok bool + id := KustoClusterId{} + + if id.SubscriptionId, ok = parsed.Parsed["subscriptionId"]; !ok { + return nil, resourceids.NewSegmentNotSpecifiedError(id, "subscriptionId", *parsed) + } + + if id.ResourceGroupName, ok = parsed.Parsed["resourceGroupName"]; !ok { + return nil, resourceids.NewSegmentNotSpecifiedError(id, "resourceGroupName", *parsed) + } + + if id.KustoClusterName, ok = parsed.Parsed["kustoClusterName"]; !ok { + return nil, resourceids.NewSegmentNotSpecifiedError(id, "kustoClusterName", *parsed) + } + + return &id, nil +} + +// ValidateKustoClusterID checks that 'input' can be parsed as a Kusto Cluster ID +func ValidateKustoClusterID(input interface{}, key string) (warnings []string, errors []error) { + v, ok := input.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected %q to be a string", key)) + return + } + + if _, err := ParseKustoClusterID(v); err != nil { + errors = append(errors, err) + } + + return +} + +// ID returns the formatted Kusto Cluster ID +func (id KustoClusterId) ID() string { + fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Kusto/clusters/%s" + return fmt.Sprintf(fmtString, id.SubscriptionId, id.ResourceGroupName, id.KustoClusterName) +} + +// Segments returns a slice of Resource ID Segments which comprise this Kusto Cluster ID +func (id KustoClusterId) Segments() []resourceids.Segment { + return []resourceids.Segment{ + resourceids.StaticSegment("staticSubscriptions", "subscriptions", "subscriptions"), + resourceids.SubscriptionIdSegment("subscriptionId", "12345678-1234-9876-4563-123456789012"), + resourceids.StaticSegment("staticResourceGroups", "resourceGroups", "resourceGroups"), + resourceids.ResourceGroupSegment("resourceGroupName", "example-resource-group"), + resourceids.StaticSegment("staticProviders", "providers", "providers"), + resourceids.ResourceProviderSegment("staticMicrosoftKusto", "Microsoft.Kusto", "Microsoft.Kusto"), + resourceids.StaticSegment("staticClusters", "clusters", "clusters"), + resourceids.UserSpecifiedSegment("kustoClusterName", "clusterValue"), + } +} + +// String returns a human-readable description of this Kusto Cluster ID +func (id KustoClusterId) String() string { + components := []string{ + fmt.Sprintf("Subscription: %q", id.SubscriptionId), + fmt.Sprintf("Resource Group Name: %q", id.ResourceGroupName), + fmt.Sprintf("Kusto Cluster Name: %q", id.KustoClusterName), + } + return fmt.Sprintf("Cluster (%s)", strings.Join(components, "\n")) +} diff --git a/resourcemanager/commonids/kusto_cluster_test.go b/resourcemanager/commonids/kusto_cluster_test.go new file mode 100644 index 0000000..59af278 --- /dev/null +++ b/resourcemanager/commonids/kusto_cluster_test.go @@ -0,0 +1,282 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package commonids + +import ( + "testing" + + "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" +) + +var _ resourceids.ResourceId = KustoClusterId{} + +func TestNewKustoClusterID(t *testing.T) { + id := NewKustoClusterID("12345678-1234-9876-4563-123456789012", "example-resource-group", "clusterValue") + + if id.SubscriptionId != "12345678-1234-9876-4563-123456789012" { + t.Fatalf("Expected %q but got %q for Segment 'SubscriptionId'", id.SubscriptionId, "12345678-1234-9876-4563-123456789012") + } + + if id.ResourceGroupName != "example-resource-group" { + t.Fatalf("Expected %q but got %q for Segment 'ResourceGroupName'", id.ResourceGroupName, "example-resource-group") + } + + if id.KustoClusterName != "clusterValue" { + t.Fatalf("Expected %q but got %q for Segment 'KustoClusterName'", id.KustoClusterName, "clusterValue") + } +} + +func TestFormatKustoClusterID(t *testing.T) { + actual := NewKustoClusterID("12345678-1234-9876-4563-123456789012", "example-resource-group", "clusterValue").ID() + expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group/providers/Microsoft.Kusto/clusters/clusterValue" + if actual != expected { + t.Fatalf("Expected the Formatted ID to be %q but got %q", expected, actual) + } +} + +func TestParseKustoClusterID(t *testing.T) { + testData := []struct { + Input string + Error bool + Expected *KustoClusterId + }{ + { + // Incomplete URI + Input: "", + Error: true, + }, + { + // Incomplete URI + Input: "/subscriptions", + Error: true, + }, + { + // Incomplete URI + Input: "/subscriptions/12345678-1234-9876-4563-123456789012", + Error: true, + }, + { + // Incomplete URI + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups", + Error: true, + }, + { + // Incomplete URI + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group", + Error: true, + }, + { + // Incomplete URI + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group/providers", + Error: true, + }, + { + // Incomplete URI + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group/providers/Microsoft.Kusto", + Error: true, + }, + { + // Incomplete URI + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group/providers/Microsoft.Kusto/clusters", + Error: true, + }, + { + // Valid URI + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group/providers/Microsoft.Kusto/clusters/clusterValue", + Expected: &KustoClusterId{ + SubscriptionId: "12345678-1234-9876-4563-123456789012", + ResourceGroupName: "example-resource-group", + KustoClusterName: "clusterValue", + }, + }, + { + // Invalid (Valid Uri with Extra segment) + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group/providers/Microsoft.Kusto/clusters/clusterValue/extra", + Error: true, + }, + } + for _, v := range testData { + t.Logf("[DEBUG] Testing %q", v.Input) + + actual, err := ParseKustoClusterID(v.Input) + if err != nil { + if v.Error { + continue + } + + t.Fatalf("Expect a value but got an error: %+v", err) + } + if v.Error { + t.Fatal("Expect an error but didn't get one") + } + + if actual.SubscriptionId != v.Expected.SubscriptionId { + t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) + } + + if actual.ResourceGroupName != v.Expected.ResourceGroupName { + t.Fatalf("Expected %q but got %q for ResourceGroupName", v.Expected.ResourceGroupName, actual.ResourceGroupName) + } + + if actual.KustoClusterName != v.Expected.KustoClusterName { + t.Fatalf("Expected %q but got %q for KustoClusterName", v.Expected.KustoClusterName, actual.KustoClusterName) + } + + } +} + +func TestParseKustoClusterIDInsensitively(t *testing.T) { + testData := []struct { + Input string + Error bool + Expected *KustoClusterId + }{ + { + // Incomplete URI + Input: "", + Error: true, + }, + { + // Incomplete URI + Input: "/subscriptions", + Error: true, + }, + { + // Incomplete URI (mIxEd CaSe since this is insensitive) + Input: "/sUbScRiPtIoNs", + Error: true, + }, + { + // Incomplete URI + Input: "/subscriptions/12345678-1234-9876-4563-123456789012", + Error: true, + }, + { + // Incomplete URI (mIxEd CaSe since this is insensitive) + Input: "/sUbScRiPtIoNs/12345678-1234-9876-4563-123456789012", + Error: true, + }, + { + // Incomplete URI + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups", + Error: true, + }, + { + // Incomplete URI (mIxEd CaSe since this is insensitive) + Input: "/sUbScRiPtIoNs/12345678-1234-9876-4563-123456789012/rEsOuRcEgRoUpS", + Error: true, + }, + { + // Incomplete URI + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group", + Error: true, + }, + { + // Incomplete URI (mIxEd CaSe since this is insensitive) + Input: "/sUbScRiPtIoNs/12345678-1234-9876-4563-123456789012/rEsOuRcEgRoUpS/eXaMpLe-rEsOuRcE-GrOuP", + Error: true, + }, + { + // Incomplete URI + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group/providers", + Error: true, + }, + { + // Incomplete URI (mIxEd CaSe since this is insensitive) + Input: "/sUbScRiPtIoNs/12345678-1234-9876-4563-123456789012/rEsOuRcEgRoUpS/eXaMpLe-rEsOuRcE-GrOuP/pRoViDeRs", + Error: true, + }, + { + // Incomplete URI + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group/providers/Microsoft.Kusto", + Error: true, + }, + { + // Incomplete URI (mIxEd CaSe since this is insensitive) + Input: "/sUbScRiPtIoNs/12345678-1234-9876-4563-123456789012/rEsOuRcEgRoUpS/eXaMpLe-rEsOuRcE-GrOuP/pRoViDeRs/mIcRoSoFt.kUsTo", + Error: true, + }, + { + // Incomplete URI + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group/providers/Microsoft.Kusto/clusters", + Error: true, + }, + { + // Incomplete URI (mIxEd CaSe since this is insensitive) + Input: "/sUbScRiPtIoNs/12345678-1234-9876-4563-123456789012/rEsOuRcEgRoUpS/eXaMpLe-rEsOuRcE-GrOuP/pRoViDeRs/mIcRoSoFt.kUsTo/cLuStErS", + Error: true, + }, + { + // Valid URI + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group/providers/Microsoft.Kusto/clusters/clusterValue", + Expected: &KustoClusterId{ + SubscriptionId: "12345678-1234-9876-4563-123456789012", + ResourceGroupName: "example-resource-group", + KustoClusterName: "clusterValue", + }, + }, + { + // Invalid (Valid Uri with Extra segment) + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group/providers/Microsoft.Kusto/clusters/clusterValue/extra", + Error: true, + }, + { + // Valid URI (mIxEd CaSe since this is insensitive) + Input: "/sUbScRiPtIoNs/12345678-1234-9876-4563-123456789012/rEsOuRcEgRoUpS/eXaMpLe-rEsOuRcE-GrOuP/pRoViDeRs/mIcRoSoFt.kUsTo/cLuStErS/cLuStErVaLuE", + Expected: &KustoClusterId{ + SubscriptionId: "12345678-1234-9876-4563-123456789012", + ResourceGroupName: "eXaMpLe-rEsOuRcE-GrOuP", + KustoClusterName: "cLuStErVaLuE", + }, + }, + { + // Invalid (Valid Uri with Extra segment - mIxEd CaSe since this is insensitive) + Input: "/sUbScRiPtIoNs/12345678-1234-9876-4563-123456789012/rEsOuRcEgRoUpS/eXaMpLe-rEsOuRcE-GrOuP/pRoViDeRs/mIcRoSoFt.kUsTo/cLuStErS/cLuStErVaLuE/extra", + Error: true, + }, + } + for _, v := range testData { + t.Logf("[DEBUG] Testing %q", v.Input) + + actual, err := ParseKustoClusterIDInsensitively(v.Input) + if err != nil { + if v.Error { + continue + } + + t.Fatalf("Expect a value but got an error: %+v", err) + } + if v.Error { + t.Fatal("Expect an error but didn't get one") + } + + if actual.SubscriptionId != v.Expected.SubscriptionId { + t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) + } + + if actual.ResourceGroupName != v.Expected.ResourceGroupName { + t.Fatalf("Expected %q but got %q for ResourceGroupName", v.Expected.ResourceGroupName, actual.ResourceGroupName) + } + + if actual.KustoClusterName != v.Expected.KustoClusterName { + t.Fatalf("Expected %q but got %q for ClusterName", v.Expected.KustoClusterName, actual.KustoClusterName) + } + + } +} + +func TestSegmentsForKustoClusterId(t *testing.T) { + segments := KustoClusterId{}.Segments() + if len(segments) == 0 { + t.Fatalf("ClusterId has no segments") + } + + uniqueNames := make(map[string]struct{}, 0) + for _, segment := range segments { + uniqueNames[segment.Name] = struct{}{} + } + if len(uniqueNames) != len(segments) { + t.Fatalf("Expected the Segments to be unique but got %q unique segments and %d total segments", len(uniqueNames), len(segments)) + } +} diff --git a/resourcemanager/commonids/kusto_database.go b/resourcemanager/commonids/kusto_database.go new file mode 100644 index 0000000..b5ba65f --- /dev/null +++ b/resourcemanager/commonids/kusto_database.go @@ -0,0 +1,140 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package commonids + +import ( + "fmt" + "strings" + + "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" +) + +var _ resourceids.ResourceId = KustoDatabaseId{} + +// KustoDatabaseId is a struct representing the Resource ID for a Kusto Database +type KustoDatabaseId struct { + SubscriptionId string + ResourceGroupName string + KustoClusterName string + KustoDatabaseName string +} + +// NewKustoDatabaseID returns a new KustoDatabaseId struct +func NewKustoDatabaseID(subscriptionId string, resourceGroupName string, kustoClusterName string, kustoDatabaseName string) KustoDatabaseId { + return KustoDatabaseId{ + SubscriptionId: subscriptionId, + ResourceGroupName: resourceGroupName, + KustoClusterName: kustoClusterName, + KustoDatabaseName: kustoDatabaseName, + } +} + +// ParseKustoDatabaseID parses 'input' into a KustoDatabaseId +func ParseKustoDatabaseID(input string) (*KustoDatabaseId, error) { + parser := resourceids.NewParserFromResourceIdType(KustoDatabaseId{}) + parsed, err := parser.Parse(input, false) + if err != nil { + return nil, fmt.Errorf("parsing %q: %+v", input, err) + } + + var ok bool + id := KustoDatabaseId{} + + if id.SubscriptionId, ok = parsed.Parsed["subscriptionId"]; !ok { + return nil, resourceids.NewSegmentNotSpecifiedError(id, "subscriptionId", *parsed) + } + + if id.ResourceGroupName, ok = parsed.Parsed["resourceGroupName"]; !ok { + return nil, resourceids.NewSegmentNotSpecifiedError(id, "resourceGroupName", *parsed) + } + + if id.KustoClusterName, ok = parsed.Parsed["kustoClusterName"]; !ok { + return nil, resourceids.NewSegmentNotSpecifiedError(id, "kustoClusterName", *parsed) + } + + if id.KustoDatabaseName, ok = parsed.Parsed["kustoDatabaseName"]; !ok { + return nil, resourceids.NewSegmentNotSpecifiedError(id, "kustoDatabaseName", *parsed) + } + + return &id, nil +} + +// ParseKustoDatabaseIDInsensitively parses 'input' case-insensitively into a KustoDatabaseId +// note: this method should only be used for API response data and not user input +func ParseKustoDatabaseIDInsensitively(input string) (*KustoDatabaseId, error) { + parser := resourceids.NewParserFromResourceIdType(KustoDatabaseId{}) + parsed, err := parser.Parse(input, true) + if err != nil { + return nil, fmt.Errorf("parsing %q: %+v", input, err) + } + + var ok bool + id := KustoDatabaseId{} + + if id.SubscriptionId, ok = parsed.Parsed["subscriptionId"]; !ok { + return nil, resourceids.NewSegmentNotSpecifiedError(id, "subscriptionId", *parsed) + } + + if id.ResourceGroupName, ok = parsed.Parsed["resourceGroupName"]; !ok { + return nil, resourceids.NewSegmentNotSpecifiedError(id, "resourceGroupName", *parsed) + } + + if id.KustoClusterName, ok = parsed.Parsed["kustoClusterName"]; !ok { + return nil, resourceids.NewSegmentNotSpecifiedError(id, "kustoClusterName", *parsed) + } + + if id.KustoDatabaseName, ok = parsed.Parsed["kustoDatabaseName"]; !ok { + return nil, resourceids.NewSegmentNotSpecifiedError(id, "kustoDatabaseName", *parsed) + } + + return &id, nil +} + +// ValidateKustoDatabaseID checks that 'input' can be parsed as a KustoDatabase ID +func ValidateKustoDatabaseID(input interface{}, key string) (warnings []string, errors []error) { + v, ok := input.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected %q to be a string", key)) + return + } + + if _, err := ParseKustoDatabaseID(v); err != nil { + errors = append(errors, err) + } + + return +} + +// ID returns the formatted Kusto Database ID +func (id KustoDatabaseId) ID() string { + fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Kusto/clusters/%s/databases/%s" + return fmt.Sprintf(fmtString, id.SubscriptionId, id.ResourceGroupName, id.KustoClusterName, id.KustoDatabaseName) +} + +// Segments returns a slice of Resource ID Segments which comprise this Kusto Database ID +func (id KustoDatabaseId) Segments() []resourceids.Segment { + return []resourceids.Segment{ + resourceids.StaticSegment("staticSubscriptions", "subscriptions", "subscriptions"), + resourceids.SubscriptionIdSegment("subscriptionId", "12345678-1234-9876-4563-123456789012"), + resourceids.StaticSegment("staticResourceGroups", "resourceGroups", "resourceGroups"), + resourceids.ResourceGroupSegment("resourceGroupName", "example-resource-group"), + resourceids.StaticSegment("staticProviders", "providers", "providers"), + resourceids.ResourceProviderSegment("staticMicrosoftKusto", "Microsoft.Kusto", "Microsoft.Kusto"), + resourceids.StaticSegment("staticClusters", "clusters", "clusters"), + resourceids.UserSpecifiedSegment("kustoClusterName", "clusterValue"), + resourceids.StaticSegment("staticDatabases", "databases", "databases"), + resourceids.UserSpecifiedSegment("kustoDatabaseName", "databaseValue"), + } +} + +// String returns a human-readable description of this Kusto Database ID +func (id KustoDatabaseId) String() string { + components := []string{ + fmt.Sprintf("Subscription: %q", id.SubscriptionId), + fmt.Sprintf("Resource Group Name: %q", id.ResourceGroupName), + fmt.Sprintf("Kusto Cluster Name: %q", id.KustoClusterName), + fmt.Sprintf("Kusto Database Name: %q", id.KustoDatabaseName), + } + return fmt.Sprintf("Kusto Database (%s)", strings.Join(components, "\n")) +} diff --git a/resourcemanager/commonids/kusto_database_test.go b/resourcemanager/commonids/kusto_database_test.go new file mode 100644 index 0000000..58a7eff --- /dev/null +++ b/resourcemanager/commonids/kusto_database_test.go @@ -0,0 +1,327 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package commonids + +import ( + "testing" + + "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" +) + +var _ resourceids.ResourceId = KustoDatabaseId{} + +func TestNewKustoDatabaseID(t *testing.T) { + id := NewKustoDatabaseID("12345678-1234-9876-4563-123456789012", "example-resource-group", "clusterValue", "databaseValue") + + if id.SubscriptionId != "12345678-1234-9876-4563-123456789012" { + t.Fatalf("Expected %q but got %q for Segment 'SubscriptionId'", id.SubscriptionId, "12345678-1234-9876-4563-123456789012") + } + + if id.ResourceGroupName != "example-resource-group" { + t.Fatalf("Expected %q but got %q for Segment 'ResourceGroupName'", id.ResourceGroupName, "example-resource-group") + } + + if id.KustoClusterName != "clusterValue" { + t.Fatalf("Expected %q but got %q for Segment 'KustoClusterName'", id.KustoClusterName, "clusterValue") + } + + if id.KustoDatabaseName != "databaseValue" { + t.Fatalf("Expected %q but got %q for Segment 'KustoDatabaseName'", id.KustoDatabaseName, "databaseValue") + } +} + +func TestFormatKustoDatabaseID(t *testing.T) { + actual := NewKustoDatabaseID("12345678-1234-9876-4563-123456789012", "example-resource-group", "clusterValue", "databaseValue").ID() + expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group/providers/Microsoft.Kusto/clusters/clusterValue/databases/databaseValue" + if actual != expected { + t.Fatalf("Expected the Formatted ID to be %q but got %q", expected, actual) + } +} + +func TestParseKustoDatabaseID(t *testing.T) { + testData := []struct { + Input string + Error bool + Expected *KustoDatabaseId + }{ + { + // Incomplete URI + Input: "", + Error: true, + }, + { + // Incomplete URI + Input: "/subscriptions", + Error: true, + }, + { + // Incomplete URI + Input: "/subscriptions/12345678-1234-9876-4563-123456789012", + Error: true, + }, + { + // Incomplete URI + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups", + Error: true, + }, + { + // Incomplete URI + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group", + Error: true, + }, + { + // Incomplete URI + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group/providers", + Error: true, + }, + { + // Incomplete URI + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group/providers/Microsoft.Kusto", + Error: true, + }, + { + // Incomplete URI + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group/providers/Microsoft.Kusto/clusters", + Error: true, + }, + { + // Incomplete URI + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group/providers/Microsoft.Kusto/clusters/clusterValue", + Error: true, + }, + { + // Incomplete URI + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group/providers/Microsoft.Kusto/clusters/clusterValue/databases", + Error: true, + }, + { + // Valid URI + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group/providers/Microsoft.Kusto/clusters/clusterValue/databases/databaseValue", + Expected: &KustoDatabaseId{ + SubscriptionId: "12345678-1234-9876-4563-123456789012", + ResourceGroupName: "example-resource-group", + KustoClusterName: "clusterValue", + KustoDatabaseName: "databaseValue", + }, + }, + { + // Invalid (Valid Uri with Extra segment) + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group/providers/Microsoft.Kusto/clusters/clusterValue/databases/databaseValue/extra", + Error: true, + }, + } + for _, v := range testData { + t.Logf("[DEBUG] Testing %q", v.Input) + + actual, err := ParseKustoDatabaseID(v.Input) + if err != nil { + if v.Error { + continue + } + + t.Fatalf("Expect a value but got an error: %+v", err) + } + if v.Error { + t.Fatal("Expect an error but didn't get one") + } + + if actual.SubscriptionId != v.Expected.SubscriptionId { + t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) + } + + if actual.ResourceGroupName != v.Expected.ResourceGroupName { + t.Fatalf("Expected %q but got %q for ResourceGroupName", v.Expected.ResourceGroupName, actual.ResourceGroupName) + } + + if actual.KustoClusterName != v.Expected.KustoClusterName { + t.Fatalf("Expected %q but got %q for KustoClusterName", v.Expected.KustoClusterName, actual.KustoClusterName) + } + + if actual.KustoDatabaseName != v.Expected.KustoDatabaseName { + t.Fatalf("Expected %q but got %q for KustoDatabaseName", v.Expected.KustoDatabaseName, actual.KustoDatabaseName) + } + + } +} + +func TestParseKustoDatabaseIDInsensitively(t *testing.T) { + testData := []struct { + Input string + Error bool + Expected *KustoDatabaseId + }{ + { + // Incomplete URI + Input: "", + Error: true, + }, + { + // Incomplete URI + Input: "/subscriptions", + Error: true, + }, + { + // Incomplete URI (mIxEd CaSe since this is insensitive) + Input: "/sUbScRiPtIoNs", + Error: true, + }, + { + // Incomplete URI + Input: "/subscriptions/12345678-1234-9876-4563-123456789012", + Error: true, + }, + { + // Incomplete URI (mIxEd CaSe since this is insensitive) + Input: "/sUbScRiPtIoNs/12345678-1234-9876-4563-123456789012", + Error: true, + }, + { + // Incomplete URI + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups", + Error: true, + }, + { + // Incomplete URI (mIxEd CaSe since this is insensitive) + Input: "/sUbScRiPtIoNs/12345678-1234-9876-4563-123456789012/rEsOuRcEgRoUpS", + Error: true, + }, + { + // Incomplete URI + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group", + Error: true, + }, + { + // Incomplete URI (mIxEd CaSe since this is insensitive) + Input: "/sUbScRiPtIoNs/12345678-1234-9876-4563-123456789012/rEsOuRcEgRoUpS/eXaMpLe-rEsOuRcE-GrOuP", + Error: true, + }, + { + // Incomplete URI + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group/providers", + Error: true, + }, + { + // Incomplete URI (mIxEd CaSe since this is insensitive) + Input: "/sUbScRiPtIoNs/12345678-1234-9876-4563-123456789012/rEsOuRcEgRoUpS/eXaMpLe-rEsOuRcE-GrOuP/pRoViDeRs", + Error: true, + }, + { + // Incomplete URI + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group/providers/Microsoft.Kusto", + Error: true, + }, + { + // Incomplete URI (mIxEd CaSe since this is insensitive) + Input: "/sUbScRiPtIoNs/12345678-1234-9876-4563-123456789012/rEsOuRcEgRoUpS/eXaMpLe-rEsOuRcE-GrOuP/pRoViDeRs/mIcRoSoFt.kUsTo", + Error: true, + }, + { + // Incomplete URI + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group/providers/Microsoft.Kusto/clusters", + Error: true, + }, + { + // Incomplete URI (mIxEd CaSe since this is insensitive) + Input: "/sUbScRiPtIoNs/12345678-1234-9876-4563-123456789012/rEsOuRcEgRoUpS/eXaMpLe-rEsOuRcE-GrOuP/pRoViDeRs/mIcRoSoFt.kUsTo/cLuStErS", + Error: true, + }, + { + // Incomplete URI + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group/providers/Microsoft.Kusto/clusters/clusterValue", + Error: true, + }, + { + // Incomplete URI (mIxEd CaSe since this is insensitive) + Input: "/sUbScRiPtIoNs/12345678-1234-9876-4563-123456789012/rEsOuRcEgRoUpS/eXaMpLe-rEsOuRcE-GrOuP/pRoViDeRs/mIcRoSoFt.kUsTo/cLuStErS/cLuStErVaLuE", + Error: true, + }, + { + // Incomplete URI + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group/providers/Microsoft.Kusto/clusters/clusterValue/databases", + Error: true, + }, + { + // Incomplete URI (mIxEd CaSe since this is insensitive) + Input: "/sUbScRiPtIoNs/12345678-1234-9876-4563-123456789012/rEsOuRcEgRoUpS/eXaMpLe-rEsOuRcE-GrOuP/pRoViDeRs/mIcRoSoFt.kUsTo/cLuStErS/cLuStErVaLuE/dAtAbAsEs", + Error: true, + }, + { + // Valid URI + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group/providers/Microsoft.Kusto/clusters/clusterValue/databases/databaseValue", + Expected: &KustoDatabaseId{ + SubscriptionId: "12345678-1234-9876-4563-123456789012", + ResourceGroupName: "example-resource-group", + KustoClusterName: "clusterValue", + KustoDatabaseName: "databaseValue", + }, + }, + { + // Invalid (Valid Uri with Extra segment) + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group/providers/Microsoft.Kusto/clusters/clusterValue/databases/databaseValue/extra", + Error: true, + }, + { + // Valid URI (mIxEd CaSe since this is insensitive) + Input: "/sUbScRiPtIoNs/12345678-1234-9876-4563-123456789012/rEsOuRcEgRoUpS/eXaMpLe-rEsOuRcE-GrOuP/pRoViDeRs/mIcRoSoFt.kUsTo/cLuStErS/cLuStErVaLuE/dAtAbAsEs/dAtAbAsEvAlUe", + Expected: &KustoDatabaseId{ + SubscriptionId: "12345678-1234-9876-4563-123456789012", + ResourceGroupName: "eXaMpLe-rEsOuRcE-GrOuP", + KustoClusterName: "cLuStErVaLuE", + KustoDatabaseName: "dAtAbAsEvAlUe", + }, + }, + { + // Invalid (Valid Uri with Extra segment - mIxEd CaSe since this is insensitive) + Input: "/sUbScRiPtIoNs/12345678-1234-9876-4563-123456789012/rEsOuRcEgRoUpS/eXaMpLe-rEsOuRcE-GrOuP/pRoViDeRs/mIcRoSoFt.kUsTo/cLuStErS/cLuStErVaLuE/dAtAbAsEs/dAtAbAsEvAlUe/extra", + Error: true, + }, + } + for _, v := range testData { + t.Logf("[DEBUG] Testing %q", v.Input) + + actual, err := ParseKustoDatabaseIDInsensitively(v.Input) + if err != nil { + if v.Error { + continue + } + + t.Fatalf("Expect a value but got an error: %+v", err) + } + if v.Error { + t.Fatal("Expect an error but didn't get one") + } + + if actual.SubscriptionId != v.Expected.SubscriptionId { + t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) + } + + if actual.ResourceGroupName != v.Expected.ResourceGroupName { + t.Fatalf("Expected %q but got %q for ResourceGroupName", v.Expected.ResourceGroupName, actual.ResourceGroupName) + } + + if actual.KustoClusterName != v.Expected.KustoClusterName { + t.Fatalf("Expected %q but got %q for ClusterName", v.Expected.KustoClusterName, actual.KustoClusterName) + } + + if actual.KustoDatabaseName != v.Expected.KustoDatabaseName { + t.Fatalf("Expected %q but got %q for DatabaseName", v.Expected.KustoDatabaseName, actual.KustoDatabaseName) + } + + } +} + +func TestSegmentsForKustoDatabaseId(t *testing.T) { + segments := KustoDatabaseId{}.Segments() + if len(segments) == 0 { + t.Fatalf("DatabaseId has no segments") + } + + uniqueNames := make(map[string]struct{}, 0) + for _, segment := range segments { + uniqueNames[segment.Name] = struct{}{} + } + if len(uniqueNames) != len(segments) { + t.Fatalf("Expected the Segments to be unique but got %q unique segments and %d total segments", len(uniqueNames), len(segments)) + } +}