From 4e2d2de48a539f35626eaec4411b67848a9fe587 Mon Sep 17 00:00:00 2001 From: Daniel Bradley Date: Fri, 13 Dec 2024 16:52:10 +0000 Subject: [PATCH 01/10] Refactor name overrides into function This will allow us to extend our rules to conditionally split larger namespaces. --- provider/pkg/resources/resources.go | 48 +++++++++++++++++++---------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/provider/pkg/resources/resources.go b/provider/pkg/resources/resources.go index b2802355e21f..f76e64c24486 100644 --- a/provider/pkg/resources/resources.go +++ b/provider/pkg/resources/resources.go @@ -258,30 +258,20 @@ var wellKnownProviderNames = map[string]string{ "visualstudio": "VisualStudio", } -// For the cases below, we use folder (SDK) name for module names instead of the ARM name. -var folderModulePattern = regexp.MustCompile(`.*/specification/([a-z]+)/resource-manager/.*`) -var folderModuleNames = map[string]string{ - "videoanalyzer": "VideoAnalyzer", - "webpubsub": "WebPubSub", -} - // ResourceProvider returns a provider name given Open API spec file and resource's API URI. func ResourceProvider(filePath, apiUri string) (string, error) { - // Start with extracting the provider from the folder path. If the folder name is explicitly listed, - // use it as the provider name. This is the new style we use for newer resources after 1.0. Older - // resources to be migrated as part of https://github.com/pulumi/pulumi-azure-native/issues/690. - subMatches := folderModulePattern.FindStringSubmatch(filePath) - if len(subMatches) > 1 { - moduleAlias := subMatches[1] - if name, ok := folderModuleNames[moduleAlias]; ok { - return name, nil - } - } // We extract the provider name from two sources: // - from the folder name of the Open API spec // - from the URI of the API endpoint (we take the last provider in the URI) + specFolderName := getSpecFolderName(filePath) fileProvider := resourceProvider(filePath, "") apiProvider := resourceProvider(apiUri, "Resources") + // Start with extracting the provider from the folder path. If the folder name is explicitly listed, + // use it as the provider name. This is the new style we use for newer resources after 1.0. Older + // resources to be migrated as part of https://github.com/pulumi/pulumi-azure-native/issues/690. + if override, hasOverride := getNameOverride(specFolderName, fileProvider, apiProvider); hasOverride { + return override, nil + } // We proceed with the endpoint if both provider values match. This way, we avoid flukes and // declarations outside of the current API provider. if strings.ToLower(fileProvider) != strings.ToLower(apiProvider) { @@ -290,6 +280,21 @@ func ResourceProvider(filePath, apiUri string) (string, error) { return fileProvider, nil } +// For the cases below, we use folder (SDK) name for module names instead of the ARM name. +var folderModulePattern = regexp.MustCompile(`.*/specification/([a-z]+)/resource-manager/.*`) +var folderModuleNames = map[string]string{ + "videoanalyzer": "VideoAnalyzer", + "webpubsub": "WebPubSub", +} + +func getNameOverride(specFolderName, fileProvider, apiProvider string) (string, bool) { + // Check if it's named after the top-level folder. + if name, ok := folderModuleNames[specFolderName]; ok { + return name, true + } + return "", false +} + func resourceProvider(path, defaultValue string) string { parts := strings.Split(path, "/") if len(parts) < 3 { @@ -312,6 +317,15 @@ func resourceProvider(path, defaultValue string) string { return defaultValue } +func getSpecFolderName(path string) string { + subMatches := folderModulePattern.FindStringSubmatch(path) + if len(subMatches) > 1 { + moduleAlias := subMatches[1] + return moduleAlias + } + return "" +} + var verbReplacer = strings.NewReplacer( "GetProperties", "", "Get", "", From aaae478ab893716504c2475d6a92a056f087f4b2 Mon Sep 17 00:00:00 2001 From: Daniel Bradley Date: Fri, 13 Dec 2024 17:09:33 +0000 Subject: [PATCH 02/10] Return old name when overridden Allow us to build aliases as required for renames between major versions. --- provider/pkg/openapi/discover.go | 2 +- provider/pkg/resources/resources.go | 25 ++++++++++++++++-------- provider/pkg/resources/resources_test.go | 10 +++++----- 3 files changed, 23 insertions(+), 14 deletions(-) diff --git a/provider/pkg/openapi/discover.go b/provider/pkg/openapi/discover.go index 130826e5eff3..dbbdc2786ebc 100644 --- a/provider/pkg/openapi/discover.go +++ b/provider/pkg/openapi/discover.go @@ -507,7 +507,7 @@ func exclude(filePath string) bool { // addAPIPath considers whether an API path contains resources and/or invokes and adds corresponding entries to the // provider map. `providers` are mutated in-place. func (providers AzureProviders) addAPIPath(specsDir, fileLocation, path string, swagger *Spec) DiscoveryDiagnostics { - prov, err := resources.ResourceProvider(filepath.Join(specsDir, fileLocation), path) + prov, _, err := resources.ResourceProvider(filepath.Join(specsDir, fileLocation), path) if err != nil { return DiscoveryDiagnostics{ ProviderNameErrors: []ProviderNameError{ diff --git a/provider/pkg/resources/resources.go b/provider/pkg/resources/resources.go index f76e64c24486..8c2ec26a8a8e 100644 --- a/provider/pkg/resources/resources.go +++ b/provider/pkg/resources/resources.go @@ -9,6 +9,7 @@ import ( "strings" "github.com/gedex/inflector" + "github.com/pulumi/pulumi-azure-native/v2/provider/pkg/version" ) // SingleValueProperty is the name of the property that we insert into the schema for non-object type responses of invokes. @@ -259,7 +260,7 @@ var wellKnownProviderNames = map[string]string{ } // ResourceProvider returns a provider name given Open API spec file and resource's API URI. -func ResourceProvider(filePath, apiUri string) (string, error) { +func ResourceProvider(filePath, apiUri string) (string, *string, error) { // We extract the provider name from two sources: // - from the folder name of the Open API spec // - from the URI of the API endpoint (we take the last provider in the URI) @@ -269,15 +270,15 @@ func ResourceProvider(filePath, apiUri string) (string, error) { // Start with extracting the provider from the folder path. If the folder name is explicitly listed, // use it as the provider name. This is the new style we use for newer resources after 1.0. Older // resources to be migrated as part of https://github.com/pulumi/pulumi-azure-native/issues/690. - if override, hasOverride := getNameOverride(specFolderName, fileProvider, apiProvider); hasOverride { - return override, nil + if override, oldModule, hasOverride := getNameOverride(specFolderName, fileProvider, apiProvider); hasOverride { + return override, oldModule, nil } // We proceed with the endpoint if both provider values match. This way, we avoid flukes and // declarations outside of the current API provider. if strings.ToLower(fileProvider) != strings.ToLower(apiProvider) { - return "", fmt.Errorf("resolved provider name mismatch: file: %s, uri: %s", fileProvider, apiProvider) + return "", nil, fmt.Errorf("resolved provider name mismatch: file: %s, uri: %s", fileProvider, apiProvider) } - return fileProvider, nil + return fileProvider, nil, nil } // For the cases below, we use folder (SDK) name for module names instead of the ARM name. @@ -287,12 +288,20 @@ var folderModuleNames = map[string]string{ "webpubsub": "WebPubSub", } -func getNameOverride(specFolderName, fileProvider, apiProvider string) (string, bool) { +// getNameOverride returns a name override for a given spec folder name, file provider, and API provider. +// The second return value is true if an override is found. +func getNameOverride(specFolderName, fileProvider, apiProvider string) (string, *string, bool) { // Check if it's named after the top-level folder. if name, ok := folderModuleNames[specFolderName]; ok { - return name, true + return name, nil, true } - return "", false + // Disable additional rules for v2 and below. + // TODO: Remove after v3 release. + if version.GetVersion().Major < 3 { + return "", nil, false + } + // TODO: Add new rules for v3 and above + return "", nil, false } func resourceProvider(path, defaultValue string) string { diff --git a/provider/pkg/resources/resources_test.go b/provider/pkg/resources/resources_test.go index b53e69c6fc44..25e3e7f48bc3 100644 --- a/provider/pkg/resources/resources_test.go +++ b/provider/pkg/resources/resources_test.go @@ -201,27 +201,27 @@ func TestTraverseProperties(t *testing.T) { func TestResourceProviderNaming(t *testing.T) { t.Run("Standard case", func(t *testing.T) { - actual, err := ResourceProvider("/go/pulumi-azure-native/azure-rest-api-specs/specification/EnterpriseKnowledgeGraph/resource-manager/Microsoft.EnterpriseKnowledgeGraph/preview/2018-12-03/EnterpriseKnowledgeGraphSwagger.json", "/providers/Microsoft.EnterpriseKnowledgeGraph/operations") + actual, _, err := ResourceProvider("/go/pulumi-azure-native/azure-rest-api-specs/specification/EnterpriseKnowledgeGraph/resource-manager/Microsoft.EnterpriseKnowledgeGraph/preview/2018-12-03/EnterpriseKnowledgeGraphSwagger.json", "/providers/Microsoft.EnterpriseKnowledgeGraph/operations") assert.Nil(t, err) assert.Equal(t, "EnterpriseKnowledgeGraph", actual) }) t.Run("PaloAltoNetworks namespace", func(t *testing.T) { - actual, err := ResourceProvider("/go/pulumi-azure-native/azure-rest-api-specs/specification/paloaltonetworks/resource-manager/PaloAltoNetworks.Cloudngfw/preview/2022-08-29-preview/PaloAltoNetworks.Cloudngfw.json", "/providers/PaloAltoNetworks.Cloudngfw/globalRulestacks") + actual, _, err := ResourceProvider("/go/pulumi-azure-native/azure-rest-api-specs/specification/paloaltonetworks/resource-manager/PaloAltoNetworks.Cloudngfw/preview/2022-08-29-preview/PaloAltoNetworks.Cloudngfw.json", "/providers/PaloAltoNetworks.Cloudngfw/globalRulestacks") assert.Nil(t, err) assert.Equal(t, "Cloudngfw", actual) }) t.Run("When the provider names of file path and URI don't match, return empty", func(t *testing.T) { - actual, err := ResourceProvider("/go/pulumi-azure-native/azure-rest-api-specs/specification/EnterpriseKnowledgeGraph/resource-manager/Microsoft.One/preview/2018-12-03/EnterpriseKnowledgeGraphSwagger.json", "/providers/Microsoft.Two/operations") + actual, _, err := ResourceProvider("/go/pulumi-azure-native/azure-rest-api-specs/specification/EnterpriseKnowledgeGraph/resource-manager/Microsoft.One/preview/2018-12-03/EnterpriseKnowledgeGraphSwagger.json", "/providers/Microsoft.Two/operations") assert.ErrorContains(t, err, "resolved provider name mismatch: file: One, uri: Two") assert.Equal(t, "", actual) }) t.Run("Change lower case to title case", func(t *testing.T) { - actual, err := ResourceProvider("/go/pulumi-azure-native/azure-rest-api-specs/specification/EnterpriseKnowledgeGraph/resource-manager/microsoft.fooBar/preview/2018-12-03/EnterpriseKnowledgeGraphSwagger.json", "/providers/microsoft.fooBar/operations") + actual, _, err := ResourceProvider("/go/pulumi-azure-native/azure-rest-api-specs/specification/EnterpriseKnowledgeGraph/resource-manager/microsoft.fooBar/preview/2018-12-03/EnterpriseKnowledgeGraphSwagger.json", "/providers/microsoft.fooBar/operations") assert.Nil(t, err) assert.Equal(t, "FooBar", actual) }) t.Run("Folder named resource", func(t *testing.T) { - actual, err := ResourceProvider("/go/pulumi-azure-native/azure-rest-api-specs/specification/videoanalyzer/resource-manager/Microsoft.Media/preview/2021-11-01-preview/PipelineTopologies.json", "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Media/videoAnalyzers/{accountName}/edgeModules/{edgeModuleName}") + actual, _, err := ResourceProvider("/go/pulumi-azure-native/azure-rest-api-specs/specification/videoanalyzer/resource-manager/Microsoft.Media/preview/2021-11-01-preview/PipelineTopologies.json", "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Media/videoAnalyzers/{accountName}/edgeModules/{edgeModuleName}") assert.Nil(t, err) assert.Equal(t, "VideoAnalyzer", actual) }) From 0915233b70628eed7dcd7e58cc292e153b285cf4 Mon Sep 17 00:00:00 2001 From: Daniel Bradley Date: Wed, 8 Jan 2025 10:57:37 +0000 Subject: [PATCH 03/10] Fix case insensitive comparison --- provider/pkg/resources/resources.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/provider/pkg/resources/resources.go b/provider/pkg/resources/resources.go index 8c2ec26a8a8e..a71c2c6bc610 100644 --- a/provider/pkg/resources/resources.go +++ b/provider/pkg/resources/resources.go @@ -275,7 +275,7 @@ func ResourceProvider(filePath, apiUri string) (string, *string, error) { } // We proceed with the endpoint if both provider values match. This way, we avoid flukes and // declarations outside of the current API provider. - if strings.ToLower(fileProvider) != strings.ToLower(apiProvider) { + if !strings.EqualFold(fileProvider, apiProvider) { return "", nil, fmt.Errorf("resolved provider name mismatch: file: %s, uri: %s", fileProvider, apiProvider) } return fileProvider, nil, nil From 292ed317eae9986c212173a74af9dd0bd4050615 Mon Sep 17 00:00:00 2001 From: Daniel Bradley Date: Thu, 9 Jan 2025 13:53:58 +0000 Subject: [PATCH 04/10] Move folderModulePattern Move to near its only usage. --- provider/pkg/resources/resources.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/provider/pkg/resources/resources.go b/provider/pkg/resources/resources.go index a71c2c6bc610..f0f08522f8cc 100644 --- a/provider/pkg/resources/resources.go +++ b/provider/pkg/resources/resources.go @@ -282,7 +282,6 @@ func ResourceProvider(filePath, apiUri string) (string, *string, error) { } // For the cases below, we use folder (SDK) name for module names instead of the ARM name. -var folderModulePattern = regexp.MustCompile(`.*/specification/([a-z]+)/resource-manager/.*`) var folderModuleNames = map[string]string{ "videoanalyzer": "VideoAnalyzer", "webpubsub": "WebPubSub", @@ -326,6 +325,8 @@ func resourceProvider(path, defaultValue string) string { return defaultValue } +var folderModulePattern = regexp.MustCompile(`.*/specification/([a-z]+)/resource-manager/.*`) + func getSpecFolderName(path string) string { subMatches := folderModulePattern.FindStringSubmatch(path) if len(subMatches) > 1 { From 85781fa8a9584f684baee36c52da3449682c90bd Mon Sep 17 00:00:00 2001 From: Daniel Bradley Date: Wed, 8 Jan 2025 15:05:57 +0000 Subject: [PATCH 05/10] Make oldProvider accessible from generateAliases function Add PreviousProviderName to ResourceSpec which is embedded into resourceVariant and passed into `func (g *packageGenerator) generateAliases(...)` --- provider/pkg/openapi/discover.go | 34 ++++++++++++++------------- provider/pkg/openapi/discover_test.go | 4 ++-- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/provider/pkg/openapi/discover.go b/provider/pkg/openapi/discover.go index dbbdc2786ebc..2f2052c150ca 100644 --- a/provider/pkg/openapi/discover.go +++ b/provider/pkg/openapi/discover.go @@ -206,14 +206,15 @@ func (v VersionResources) All() map[string]*ResourceSpec { // ResourceSpec contains a pointer in an Open API Spec that defines a resource and related metadata. type ResourceSpec struct { - Path string - PathItem *spec.PathItem - PathItemList *spec.PathItem - Swagger *Spec - CompatibleVersions []SdkVersion - DefaultBody map[string]interface{} - DeprecationMessage string - PreviousVersion ApiVersion + Path string + PathItem *spec.PathItem + PathItemList *spec.PathItem + Swagger *Spec + CompatibleVersions []SdkVersion + DefaultBody map[string]interface{} + DeprecationMessage string + PreviousVersion ApiVersion + PreviousProviderName *string } // ApplyProvidersTransformations adds the default version for each provider and deprecates and removes specified API versions. @@ -507,7 +508,7 @@ func exclude(filePath string) bool { // addAPIPath considers whether an API path contains resources and/or invokes and adds corresponding entries to the // provider map. `providers` are mutated in-place. func (providers AzureProviders) addAPIPath(specsDir, fileLocation, path string, swagger *Spec) DiscoveryDiagnostics { - prov, _, err := resources.ResourceProvider(filepath.Join(specsDir, fileLocation), path) + prov, oldProvider, err := resources.ResourceProvider(filepath.Join(specsDir, fileLocation), path) if err != nil { return DiscoveryDiagnostics{ ProviderNameErrors: []ProviderNameError{ @@ -536,10 +537,10 @@ func (providers AzureProviders) addAPIPath(specsDir, fileLocation, path string, versionMap[sdkVersion] = version } - return addResourcesAndInvokes(version, fileLocation, path, prov, swagger) + return addResourcesAndInvokes(version, fileLocation, path, prov, oldProvider, swagger) } -func addResourcesAndInvokes(version VersionResources, fileLocation, path, provider string, swagger *Spec) DiscoveryDiagnostics { +func addResourcesAndInvokes(version VersionResources, fileLocation, path, provider string, oldProvider *string, swagger *Spec) DiscoveryDiagnostics { apiVersion := ApiVersion(swagger.Info.Version) sdkVersion := ApiToSdkVersion(apiVersion) @@ -569,11 +570,12 @@ func addResourcesAndInvokes(version VersionResources, fileLocation, path, provid foundResourceOrInvoke := false addResource := func(typeName string, defaultBody map[string]interface{}, pathItemList *spec.PathItem) { version.Resources[typeName] = &ResourceSpec{ - Path: path, - PathItem: &pathItem, - Swagger: swagger, - DefaultBody: defaultBody, - PathItemList: pathItemList, + Path: path, + PathItem: &pathItem, + Swagger: swagger, + DefaultBody: defaultBody, + PathItemList: pathItemList, + PreviousProviderName: oldProvider, } foundResourceOrInvoke = true } diff --git a/provider/pkg/openapi/discover_test.go b/provider/pkg/openapi/discover_test.go index e84331e9fcfc..b9b14b524a40 100644 --- a/provider/pkg/openapi/discover_test.go +++ b/provider/pkg/openapi/discover_test.go @@ -72,7 +72,7 @@ func TestAddInvokes(t *testing.T) { version := NewVersionResources() spec := createTestSwaggerForInvoke(tc.path, tc.operationId, tc.httpMethod) - addResourcesAndInvokes(version, "/file/path", tc.path, "foo", &spec) + addResourcesAndInvokes(version, "/file/path", tc.path, "foo", nil, &spec) assert.Empty(t, version.Resources, tcName) assert.NotEmpty(t, version.Invokes, tcName) @@ -126,7 +126,7 @@ func TestDefaultState(t *testing.T) { swagger := makeSwagger(path) version := NewVersionResources() - addResourcesAndInvokes(version, "/file/path", path, "insights", &swagger) + addResourcesAndInvokes(version, "/file/path", path, "insights", nil, &swagger) require.NotEmpty(t, version.Resources) res, ok := version.Resources["DiagnosticSettingsCategory"] From eb9af1491cb7bad597a95fd05334ccf87c41bf88 Mon Sep 17 00:00:00 2001 From: Daniel Bradley Date: Wed, 8 Jan 2025 15:41:49 +0000 Subject: [PATCH 06/10] Prefer simple functions over class methods Prefer pure functions for simple string manipulation so the inputs are transparent to the caller. Make pulumi provider name a constant rather than referencing from the packageGenerator object. Simplify & test goModuleName 1. Add test of old function. 2. Add goModuleRepoPath prefixing to implementation & test. 3. Simplify implementation. --- provider/pkg/gen/schema.go | 35 +++++++++++++++++---------------- provider/pkg/gen/schema_test.go | 10 ++++++++++ 2 files changed, 28 insertions(+), 17 deletions(-) diff --git a/provider/pkg/gen/schema.go b/provider/pkg/gen/schema.go index 315f4d73d7a5..718b3c614930 100644 --- a/provider/pkg/gen/schema.go +++ b/provider/pkg/gen/schema.go @@ -44,6 +44,9 @@ const goBasePath = "github.com/pulumi/pulumi-azure-native-sdk/v2" const goModuleRepoPath = "github.com/pulumi/pulumi-azure-native-sdk" const goModuleVersion = "/v2" +// pulumiProviderName is the name of the provider as used in all tokens. +const pulumiProviderName = "azure-native" + type ResourceDeprecation struct { ReplacementToken string } @@ -323,7 +326,7 @@ func PulumiSchema(rootDir string, providerMap openapi.AzureProviders, versioning javaPackages[module] = fmt.Sprintf("%s.%s", strings.ToLower(providerName), sdkVersion) } pythonModuleNames[module] = module - golangImportAliases[filepath.Join(goModuleRepoPath, gen.versionedModuleName())] = strings.ToLower(providerName) + golangImportAliases[goModuleName(gen.provider, gen.sdkVersion)] = strings.ToLower(providerName) // Populate resources and get invokes. items := versionMap[sdkVersion] @@ -773,7 +776,7 @@ func (g *packageGenerator) findResourceVariants(resource *openapi.ResourceSpec) } func (g *packageGenerator) makeTypeAlias(alias string, apiVersion openapi.SdkVersion) pschema.AliasSpec { - fqAlias := g.generateTok(alias, apiVersion) + fqAlias := generateTok(g.provider, alias, apiVersion) return pschema.AliasSpec{Type: &fqAlias} } @@ -1056,7 +1059,7 @@ func (g *packageGenerator) genFunctions(typeName, path string, specParams []spec } // Generate the function to get this resource. - functionTok := g.generateTok(typeName, g.sdkVersion) + functionTok := generateTok(g.provider, typeName, g.sdkVersion) if !g.shouldInclude(typeName, functionTok, g.apiVersion) { return } @@ -1106,27 +1109,25 @@ func (g *packageGenerator) genFunctions(typeName, path string, specParams []spec // moduleName produces the module name from the provider name and the version e.g. `compute/v20200701`. func (g *packageGenerator) moduleName() string { - return g.providerApiToModule(g.sdkVersion) + return providerApiToModule(g.provider, g.sdkVersion) } -func (g *packageGenerator) versionedModuleName() string { - versionedModule := strings.ToLower(g.provider) + goModuleVersion - if g.sdkVersion == "" { - return versionedModule - } - return fmt.Sprintf("%s/%s", versionedModule, g.sdkVersion) +// goModuleName produces the *Go* module name from the provider name and the version e.g. `compute/v20200701`. +// or just the provider name if the version is empty (default version) e.g. `compute`. +func goModuleName(provider openapi.ProviderName, sdkVersion openapi.SdkVersion) string { + return filepath.Join(goModuleRepoPath, strings.ToLower(provider), goModuleVersion, string(sdkVersion)) } // providerApiToModule produces the module name from the provider name (g.provider), and the version if not empty, e.g. `compute/v20200701`. -func (g *packageGenerator) providerApiToModule(apiVersion openapi.SdkVersion) string { - if apiVersion == "" { - return strings.ToLower(g.provider) +func providerApiToModule(provider openapi.ProviderName, sdkVersion openapi.SdkVersion) string { + if sdkVersion == "" { + return strings.ToLower(provider) } - return fmt.Sprintf("%s/%s", strings.ToLower(g.provider), apiVersion) + return fmt.Sprintf("%s/%s", strings.ToLower(provider), sdkVersion) } -func (g *packageGenerator) generateTok(typeName string, apiVersion openapi.SdkVersion) string { - return fmt.Sprintf(`%s:%s:%s`, g.pkg.Name, g.providerApiToModule(apiVersion), typeName) +func generateTok(provider openapi.ProviderName, typeName string, apiVersion openapi.SdkVersion) string { + return fmt.Sprintf(`%s:%s:%s`, pulumiProviderName, providerApiToModule(provider, apiVersion), typeName) } func (g *packageGenerator) shouldInclude(typeName, tok string, version *openapi.ApiVersion) bool { @@ -1159,7 +1160,7 @@ func (g *packageGenerator) formatDescription(desc string, typeName string, defau if v == defaultVersion { continue } - tok := g.generateTok(typeName, openapi.ApiToSdkVersion(v)) + tok := generateTok(g.provider, typeName, openapi.ApiToSdkVersion(v)) if g.shouldInclude(typeName, tok, &v) { includedVersions = append(includedVersions, string(v)) } diff --git a/provider/pkg/gen/schema_test.go b/provider/pkg/gen/schema_test.go index 9f05f9d75058..c8f5a7f19514 100644 --- a/provider/pkg/gen/schema_test.go +++ b/provider/pkg/gen/schema_test.go @@ -288,3 +288,13 @@ func TestResourceIsSingleton(t *testing.T) { assert.False(t, isSingleton(res)) }) } + +func TestGoModuleName(t *testing.T) { + t.Run("explicit version", func(t *testing.T) { + assert.Equal(t, "github.com/pulumi/pulumi-azure-native-sdk/network/v2/v20220222", goModuleName("Network", "v20220222")) + }) + + t.Run("default version", func(t *testing.T) { + assert.Equal(t, "github.com/pulumi/pulumi-azure-native-sdk/network/v2", goModuleName("Network", "")) + }) +} From 8fc71cd143734b18429a5d162687bff125f033e1 Mon Sep 17 00:00:00 2001 From: Daniel Bradley Date: Wed, 8 Jan 2025 16:32:14 +0000 Subject: [PATCH 07/10] Refactor generateAliases Replace makeTypeAlias with local function so it can also add to the slice directly. Allow setting the module name too so we can add aliases between modules. Add previous module variations of aliases Remove makeTypeAlias test --- provider/pkg/gen/schema.go | 36 +++++++++++++++++++++++++-------- provider/pkg/gen/schema_test.go | 16 --------------- 2 files changed, 28 insertions(+), 24 deletions(-) diff --git a/provider/pkg/gen/schema.go b/provider/pkg/gen/schema.go index 718b3c614930..41cb4628b97c 100644 --- a/provider/pkg/gen/schema.go +++ b/provider/pkg/gen/schema.go @@ -775,11 +775,6 @@ func (g *packageGenerator) findResourceVariants(resource *openapi.ResourceSpec) return result, nil } -func (g *packageGenerator) makeTypeAlias(alias string, apiVersion openapi.SdkVersion) pschema.AliasSpec { - fqAlias := generateTok(g.provider, alias, apiVersion) - return pschema.AliasSpec{Type: &fqAlias} -} - func (g *packageGenerator) genResourceVariant(apiSpec *openapi.ResourceSpec, resource *resourceVariant, nestedResourceBodyRefs []string, typeNameAliases ...string) error { module := g.moduleName() swagger := resource.Swagger @@ -970,16 +965,41 @@ func isSingleton(resource *resourceVariant) bool { func (g *packageGenerator) generateAliases(resource *resourceVariant, typeNameAliases ...string) []pschema.AliasSpec { var aliases []pschema.AliasSpec + addAlias := func(module, typeName string, version openapi.SdkVersion) { + alias := generateTok(module, typeName, version) + aliases = append(aliases, pschema.AliasSpec{Type: &alias}) + } + + // Add an alias for the same version of the resource, but in its old module. + if resource.PreviousProviderName != nil { + addAlias(*resource.PreviousProviderName, resource.typeName, g.sdkVersion) + } + for _, alias := range typeNameAliases { - aliases = append(aliases, g.makeTypeAlias(alias, g.sdkVersion)) + addAlias(g.provider, alias, g.sdkVersion) + // Add an alias for the same alias, but in its old module. + if resource.PreviousProviderName != nil { + addAlias(*resource.PreviousProviderName, alias, g.sdkVersion) + } } // Add an alias for each API version that has the same path in it. for _, version := range resource.CompatibleVersions { - aliases = append(aliases, g.makeTypeAlias(resource.typeName, version)) + addAlias(g.provider, resource.typeName, version) + + // Add an alias for the other versions, but from its old module. + if resource.PreviousProviderName != nil { + addAlias(*resource.PreviousProviderName, resource.typeName, version) + } + // Add type name aliases for each compatible version. for _, alias := range typeNameAliases { - aliases = append(aliases, g.makeTypeAlias(alias, version)) + addAlias(g.provider, alias, version) + + // Add an alias for the other version, with alias, from its old module. + if resource.PreviousProviderName != nil { + addAlias(*resource.PreviousProviderName, alias, version) + } } } diff --git a/provider/pkg/gen/schema_test.go b/provider/pkg/gen/schema_test.go index c8f5a7f19514..1736a6eac3c7 100644 --- a/provider/pkg/gen/schema_test.go +++ b/provider/pkg/gen/schema_test.go @@ -13,22 +13,6 @@ import ( "github.com/stretchr/testify/require" ) -func TestTypeAliasFormatting(t *testing.T) { - generator := packageGenerator{ - pkg: &pschema.PackageSpec{Name: "azure-native"}, - sdkVersion: "v20220222", - provider: "Compute", - } - - actual := generator.makeTypeAlias("VirtualMachine", "v18851225") - assert.NotNil(t, actual) - assert.Equal(t, "azure-native:compute/v18851225:VirtualMachine", *actual.Type) - - actual = generator.makeTypeAlias("VirtualMachine", "") - assert.NotNil(t, actual) - assert.Equal(t, "azure-native:compute:VirtualMachine", *actual.Type) -} - // Ensure our VersionMetadata type implements the gen.Versioning interface // The compiler will raise an error here if the interface isn't implemented var _ Versioning = (*versioningStub)(nil) From 90c3d7d20b8d499ca4556da9a96d5df99ff9b4c6 Mon Sep 17 00:00:00 2001 From: Daniel Bradley Date: Wed, 8 Jan 2025 10:51:42 +0000 Subject: [PATCH 08/10] Add manual split of Network module Note: We might be able to do this from the specification metadata at some point instead of hard-coding the corrected cases. --- provider/pkg/resources/resources.go | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/provider/pkg/resources/resources.go b/provider/pkg/resources/resources.go index f0f08522f8cc..63c0da59036d 100644 --- a/provider/pkg/resources/resources.go +++ b/provider/pkg/resources/resources.go @@ -299,7 +299,22 @@ func getNameOverride(specFolderName, fileProvider, apiProvider string) (string, if version.GetVersion().Major < 3 { return "", nil, false } - // TODO: Add new rules for v3 and above + // New rules for v3 and above + if fileProvider == "Network" && specFolderName == "dns" { + return "Dns", &fileProvider, true + } + if fileProvider == "Network" && specFolderName == "dnsresolver" { + return "DnsResolver", &fileProvider, true + } + if fileProvider == "Network" && specFolderName == "frontdoor" { + return "FrontDoor", &fileProvider, true + } + if fileProvider == "Network" && specFolderName == "privatedns" { + return "PrivateDns", &fileProvider, true + } + if fileProvider == "Network" && specFolderName == "trafficmanager" { + return "TrafficManager", &fileProvider, true + } return "", nil, false } From 04066030ac33a7fa1905da9fe34eb52886179f86 Mon Sep 17 00:00:00 2001 From: Daniel Bradley Date: Thu, 9 Jan 2025 13:52:34 +0000 Subject: [PATCH 09/10] Refactor getNameOverride() - Use maps instead of conditionals for conciseness & to get us to 100% coverage. - Move fetching major version number to call in Discover module so we can unit test both v2 and v3 paths. - Rename resourceProvider() to findNamespaceWithoutPrefixFromPath() - Rename fileProvider and apiProvider to be more explicit as to their string content. --- provider/pkg/openapi/discover.go | 3 +- provider/pkg/resources/resources.go | 61 ++++++++++++------------ provider/pkg/resources/resources_test.go | 59 +++++++++++++++++++---- 3 files changed, 81 insertions(+), 42 deletions(-) diff --git a/provider/pkg/openapi/discover.go b/provider/pkg/openapi/discover.go index 2f2052c150ca..630902634e76 100644 --- a/provider/pkg/openapi/discover.go +++ b/provider/pkg/openapi/discover.go @@ -17,6 +17,7 @@ import ( "github.com/pulumi/pulumi-azure-native/v2/provider/pkg/openapi/paths" "github.com/pulumi/pulumi-azure-native/v2/provider/pkg/resources" "github.com/pulumi/pulumi-azure-native/v2/provider/pkg/resources/customresources" + "github.com/pulumi/pulumi-azure-native/v2/provider/pkg/version" "github.com/pulumi/pulumi/pkg/v3/codegen" ) @@ -508,7 +509,7 @@ func exclude(filePath string) bool { // addAPIPath considers whether an API path contains resources and/or invokes and adds corresponding entries to the // provider map. `providers` are mutated in-place. func (providers AzureProviders) addAPIPath(specsDir, fileLocation, path string, swagger *Spec) DiscoveryDiagnostics { - prov, oldProvider, err := resources.ResourceProvider(filepath.Join(specsDir, fileLocation), path) + prov, oldProvider, err := resources.ResourceProvider(version.GetVersion().Major, filepath.Join(specsDir, fileLocation), path) if err != nil { return DiscoveryDiagnostics{ ProviderNameErrors: []ProviderNameError{ diff --git a/provider/pkg/resources/resources.go b/provider/pkg/resources/resources.go index 63c0da59036d..4e9acabe1a87 100644 --- a/provider/pkg/resources/resources.go +++ b/provider/pkg/resources/resources.go @@ -9,7 +9,6 @@ import ( "strings" "github.com/gedex/inflector" - "github.com/pulumi/pulumi-azure-native/v2/provider/pkg/version" ) // SingleValueProperty is the name of the property that we insert into the schema for non-object type responses of invokes. @@ -260,65 +259,65 @@ var wellKnownProviderNames = map[string]string{ } // ResourceProvider returns a provider name given Open API spec file and resource's API URI. -func ResourceProvider(filePath, apiUri string) (string, *string, error) { +// Returns the module name, optional old module name, or error. +func ResourceProvider(majorVersion uint64, filePath, apiUri string) (string, *string, error) { // We extract the provider name from two sources: // - from the folder name of the Open API spec // - from the URI of the API endpoint (we take the last provider in the URI) specFolderName := getSpecFolderName(filePath) - fileProvider := resourceProvider(filePath, "") - apiProvider := resourceProvider(apiUri, "Resources") + namespaceWithoutPrefixFromSpecFilePath := findNamespaceWithoutPrefixFromPath(filePath, "") + namespaceWithoutPrefixFromResourceUrl := findNamespaceWithoutPrefixFromPath(apiUri, "Resources") // Start with extracting the provider from the folder path. If the folder name is explicitly listed, // use it as the provider name. This is the new style we use for newer resources after 1.0. Older // resources to be migrated as part of https://github.com/pulumi/pulumi-azure-native/issues/690. - if override, oldModule, hasOverride := getNameOverride(specFolderName, fileProvider, apiProvider); hasOverride { + if override, oldModule, hasOverride := getNameOverride(majorVersion, specFolderName, namespaceWithoutPrefixFromSpecFilePath); hasOverride { return override, oldModule, nil } // We proceed with the endpoint if both provider values match. This way, we avoid flukes and // declarations outside of the current API provider. - if !strings.EqualFold(fileProvider, apiProvider) { - return "", nil, fmt.Errorf("resolved provider name mismatch: file: %s, uri: %s", fileProvider, apiProvider) + if !strings.EqualFold(namespaceWithoutPrefixFromSpecFilePath, namespaceWithoutPrefixFromResourceUrl) { + return "", nil, fmt.Errorf("resolved provider name mismatch: file: %s, uri: %s", namespaceWithoutPrefixFromSpecFilePath, namespaceWithoutPrefixFromResourceUrl) } - return fileProvider, nil, nil + return namespaceWithoutPrefixFromSpecFilePath, nil, nil } -// For the cases below, we use folder (SDK) name for module names instead of the ARM name. -var folderModuleNames = map[string]string{ +var modulesNamedByFolder = map[string]string{ "videoanalyzer": "VideoAnalyzer", "webpubsub": "WebPubSub", } -// getNameOverride returns a name override for a given spec folder name, file provider, and API provider. +var moduleNameOverridesWithAliases = map[string]map[string]string{ + "Network": { + "dns": "Dns", + "dnsresolver": "DnsResolver", + "frontdoor": "FrontDoor", + "privatedns": "PrivateDns", + "trafficmanager": "TrafficManager", + }, +} + +// getNameOverride returns a name override for a given folder name, and non-prefixed namespace. // The second return value is true if an override is found. -func getNameOverride(specFolderName, fileProvider, apiProvider string) (string, *string, bool) { - // Check if it's named after the top-level folder. - if name, ok := folderModuleNames[specFolderName]; ok { +func getNameOverride(majorVersion uint64, specFolderName, namespaceWithoutPrefix string) (string, *string, bool) { + // For the cases below, we use folder (SDK) name for module names instead of the ARM name. + if name, ok := modulesNamedByFolder[specFolderName]; ok { return name, nil, true } // Disable additional rules for v2 and below. // TODO: Remove after v3 release. - if version.GetVersion().Major < 3 { + if majorVersion < 3 { return "", nil, false } - // New rules for v3 and above - if fileProvider == "Network" && specFolderName == "dns" { - return "Dns", &fileProvider, true - } - if fileProvider == "Network" && specFolderName == "dnsresolver" { - return "DnsResolver", &fileProvider, true - } - if fileProvider == "Network" && specFolderName == "frontdoor" { - return "FrontDoor", &fileProvider, true - } - if fileProvider == "Network" && specFolderName == "privatedns" { - return "PrivateDns", &fileProvider, true - } - if fileProvider == "Network" && specFolderName == "trafficmanager" { - return "TrafficManager", &fileProvider, true + // New rules for v3 and above which include aliases back to the original namespace. + if namespaceOverrides, ok := moduleNameOverridesWithAliases[namespaceWithoutPrefix]; ok { + if folderName, ok := namespaceOverrides[specFolderName]; ok { + return folderName, &namespaceWithoutPrefix, true + } } return "", nil, false } -func resourceProvider(path, defaultValue string) string { +func findNamespaceWithoutPrefixFromPath(path, defaultValue string) string { parts := strings.Split(path, "/") if len(parts) < 3 { return "" diff --git a/provider/pkg/resources/resources_test.go b/provider/pkg/resources/resources_test.go index 25e3e7f48bc3..a7971cc2e125 100644 --- a/provider/pkg/resources/resources_test.go +++ b/provider/pkg/resources/resources_test.go @@ -201,28 +201,67 @@ func TestTraverseProperties(t *testing.T) { func TestResourceProviderNaming(t *testing.T) { t.Run("Standard case", func(t *testing.T) { - actual, _, err := ResourceProvider("/go/pulumi-azure-native/azure-rest-api-specs/specification/EnterpriseKnowledgeGraph/resource-manager/Microsoft.EnterpriseKnowledgeGraph/preview/2018-12-03/EnterpriseKnowledgeGraphSwagger.json", "/providers/Microsoft.EnterpriseKnowledgeGraph/operations") + module, oldModule, err := ResourceProvider(2, + "/go/pulumi-azure-native/azure-rest-api-specs/specification/EnterpriseKnowledgeGraph/resource-manager/Microsoft.EnterpriseKnowledgeGraph/preview/2018-12-03/EnterpriseKnowledgeGraphSwagger.json", + "/providers/Microsoft.EnterpriseKnowledgeGraph/operations") assert.Nil(t, err) - assert.Equal(t, "EnterpriseKnowledgeGraph", actual) + assert.Equal(t, "EnterpriseKnowledgeGraph", module) + assert.Nil(t, oldModule) + }) + t.Run("Standard case v3", func(t *testing.T) { + module, oldModule, err := ResourceProvider(3, + "/go/pulumi-azure-native/azure-rest-api-specs/specification/EnterpriseKnowledgeGraph/resource-manager/Microsoft.EnterpriseKnowledgeGraph/preview/2018-12-03/EnterpriseKnowledgeGraphSwagger.json", + "/providers/Microsoft.EnterpriseKnowledgeGraph/operations") + assert.Nil(t, err) + assert.Equal(t, "EnterpriseKnowledgeGraph", module) + assert.Nil(t, oldModule) }) t.Run("PaloAltoNetworks namespace", func(t *testing.T) { - actual, _, err := ResourceProvider("/go/pulumi-azure-native/azure-rest-api-specs/specification/paloaltonetworks/resource-manager/PaloAltoNetworks.Cloudngfw/preview/2022-08-29-preview/PaloAltoNetworks.Cloudngfw.json", "/providers/PaloAltoNetworks.Cloudngfw/globalRulestacks") + module, oldModule, err := ResourceProvider(2, + "/go/pulumi-azure-native/azure-rest-api-specs/specification/paloaltonetworks/resource-manager/PaloAltoNetworks.Cloudngfw/preview/2022-08-29-preview/PaloAltoNetworks.Cloudngfw.json", + "/providers/PaloAltoNetworks.Cloudngfw/globalRulestacks") assert.Nil(t, err) - assert.Equal(t, "Cloudngfw", actual) + assert.Equal(t, "Cloudngfw", module) + assert.Nil(t, oldModule) }) t.Run("When the provider names of file path and URI don't match, return empty", func(t *testing.T) { - actual, _, err := ResourceProvider("/go/pulumi-azure-native/azure-rest-api-specs/specification/EnterpriseKnowledgeGraph/resource-manager/Microsoft.One/preview/2018-12-03/EnterpriseKnowledgeGraphSwagger.json", "/providers/Microsoft.Two/operations") + module, oldModule, err := ResourceProvider(2, + "/go/pulumi-azure-native/azure-rest-api-specs/specification/EnterpriseKnowledgeGraph/resource-manager/Microsoft.One/preview/2018-12-03/EnterpriseKnowledgeGraphSwagger.json", + "/providers/Microsoft.Two/operations") assert.ErrorContains(t, err, "resolved provider name mismatch: file: One, uri: Two") - assert.Equal(t, "", actual) + assert.Equal(t, "", module) + assert.Nil(t, oldModule) }) t.Run("Change lower case to title case", func(t *testing.T) { - actual, _, err := ResourceProvider("/go/pulumi-azure-native/azure-rest-api-specs/specification/EnterpriseKnowledgeGraph/resource-manager/microsoft.fooBar/preview/2018-12-03/EnterpriseKnowledgeGraphSwagger.json", "/providers/microsoft.fooBar/operations") + module, oldModule, err := ResourceProvider(2, + "/go/pulumi-azure-native/azure-rest-api-specs/specification/EnterpriseKnowledgeGraph/resource-manager/microsoft.fooBar/preview/2018-12-03/EnterpriseKnowledgeGraphSwagger.json", + "/providers/microsoft.fooBar/operations") assert.Nil(t, err) - assert.Equal(t, "FooBar", actual) + assert.Equal(t, "FooBar", module) + assert.Nil(t, oldModule) }) t.Run("Folder named resource", func(t *testing.T) { - actual, _, err := ResourceProvider("/go/pulumi-azure-native/azure-rest-api-specs/specification/videoanalyzer/resource-manager/Microsoft.Media/preview/2021-11-01-preview/PipelineTopologies.json", "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Media/videoAnalyzers/{accountName}/edgeModules/{edgeModuleName}") + module, oldModule, err := ResourceProvider(2, + "/go/pulumi-azure-native/azure-rest-api-specs/specification/videoanalyzer/resource-manager/Microsoft.Media/preview/2021-11-01-preview/PipelineTopologies.json", + "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Media/videoAnalyzers/{accountName}/edgeModules/{edgeModuleName}") + assert.Nil(t, err) + assert.Equal(t, "VideoAnalyzer", module) + assert.Nil(t, oldModule) + }) + t.Run("Network overrides not applied to v2", func(t *testing.T) { + module, oldModule, err := ResourceProvider(2, + "/go/pulumi-azure-native/azure-rest-api-specs/specification/dns/resource-manager/Microsoft.Network/stable/2018-05-01/dns.json", + "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Network/dnsZones/{zoneName}") + assert.Nil(t, err) + assert.Equal(t, "Network", module) + assert.Nil(t, oldModule) + }) + t.Run("Network overrides applied to v3", func(t *testing.T) { + module, oldModule, err := ResourceProvider(3, + "/go/pulumi-azure-native/azure-rest-api-specs/specification/dns/resource-manager/Microsoft.Network/stable/2018-05-01/dns.json", + "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Network/dnsZones/{zoneName}") assert.Nil(t, err) - assert.Equal(t, "VideoAnalyzer", actual) + assert.Equal(t, "Dns", module) + assert.Equal(t, "Network", *oldModule) }) } From 34f6aef23e3ebf3838f12d47e45567b1e2bf4ebd Mon Sep 17 00:00:00 2001 From: Daniel Bradley Date: Thu, 9 Jan 2025 16:39:32 +0000 Subject: [PATCH 10/10] Systematically test alias generation --- provider/pkg/gen/schema_test.go | 125 ++++++++++++++++++++++++++------ 1 file changed, 101 insertions(+), 24 deletions(-) diff --git a/provider/pkg/gen/schema_test.go b/provider/pkg/gen/schema_test.go index 1736a6eac3c7..5fe1e6659e2f 100644 --- a/provider/pkg/gen/schema_test.go +++ b/provider/pkg/gen/schema_test.go @@ -7,7 +7,6 @@ import ( "github.com/go-openapi/spec" "github.com/pulumi/pulumi-azure-native/v2/provider/pkg/openapi" - "github.com/pulumi/pulumi/pkg/v3/codegen" pschema "github.com/pulumi/pulumi/pkg/v3/codegen/schema" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -45,32 +44,110 @@ func (v versioningStub) GetAllVersions(provider, resource string) []openapi.ApiV } func TestAliases(t *testing.T) { - generator := packageGenerator{ - pkg: &pschema.PackageSpec{Name: "azure-native"}, - sdkVersion: "v20220222", - versioning: versioningStub{}, - provider: "Insights", - majorVersion: 2, - } + // Wrap the generation of type aliases in a function to make it easier to test + generateTypeAliases := func(provider, typeName string, sdkVersion openapi.SdkVersion, previousProviderName string, typeNameAliases []string, versions []openapi.SdkVersion) []string { + generator := packageGenerator{ + pkg: &pschema.PackageSpec{Name: "azure-native"}, + sdkVersion: sdkVersion, + versioning: versioningStub{}, + provider: provider, + majorVersion: 2, + } - resource := &resourceVariant{ - ResourceSpec: &openapi.ResourceSpec{ - CompatibleVersions: []openapi.SdkVersion{"v20210111"}, - }, - typeName: "PrivateLinkForAzureAd", - } + resource := &resourceVariant{ + ResourceSpec: &openapi.ResourceSpec{ + CompatibleVersions: versions, + }, + typeName: typeName, + } + if previousProviderName != "" { + resource.PreviousProviderName = &previousProviderName + } - aliases := generator.generateAliases(resource, "privateLinkForAzureAd") - actual := codegen.NewStringSet() - for _, alias := range aliases { - actual.Add(*alias.Type) + aliasSpecs := generator.generateAliases(resource, typeNameAliases...) + typeAliases := []string{} + for _, alias := range aliasSpecs { + typeAliases = append(typeAliases, *alias.Type) + } + return typeAliases } - expected := codegen.NewStringSet( - "azure-native:insights/v20210111:privateLinkForAzureAd", - "azure-native:insights/v20210111:PrivateLinkForAzureAd", - "azure-native:insights/v20220222:privateLinkForAzureAd", - ) - assert.Equal(t, expected, actual) + + t.Run("compatible version", func(t *testing.T) { + actual := generateTypeAliases("Insights", "PrivateLinkForAzureAd", "v20220222", "", nil, []openapi.SdkVersion{"v20200110", "v20210111"}) + expected := []string{ + "azure-native:insights/v20200110:PrivateLinkForAzureAd", + "azure-native:insights/v20210111:PrivateLinkForAzureAd", + } + assert.ElementsMatch(t, expected, actual) + }) + + t.Run("type alias", func(t *testing.T) { + actual := generateTypeAliases("Insights", "PrivateLinkForAzureAd", "v20220222", "", []string{"privateLinkForAzureAd"}, []openapi.SdkVersion{}) + expected := []string{ + "azure-native:insights/v20220222:privateLinkForAzureAd", + } + assert.ElementsMatch(t, expected, actual) + }) + + t.Run("compatible version & type alias", func(t *testing.T) { + actual := generateTypeAliases("Insights", "PrivateLinkForAzureAd", "v20220222", "", []string{"privateLinkForAzureAd"}, []openapi.SdkVersion{"v20200110", "v20210111"}) + expected := []string{ + "azure-native:insights/v20200110:privateLinkForAzureAd", + "azure-native:insights/v20200110:PrivateLinkForAzureAd", + "azure-native:insights/v20210111:privateLinkForAzureAd", + "azure-native:insights/v20210111:PrivateLinkForAzureAd", + "azure-native:insights/v20220222:privateLinkForAzureAd", + } + assert.ElementsMatch(t, expected, actual) + }) + + t.Run("previous provider", func(t *testing.T) { + actual := generateTypeAliases("Monitor", "PrivateLinkForAzureAd", "v20220222", "Insights", nil, []openapi.SdkVersion{}) + expected := []string{ + "azure-native:insights/v20220222:PrivateLinkForAzureAd", + } + assert.ElementsMatch(t, expected, actual) + }) + + t.Run("previous provider & type alias", func(t *testing.T) { + actual := generateTypeAliases("Monitor", "PrivateLinkForAzureAd", "v20220222", "Insights", []string{"privateLinkForAzureAd"}, []openapi.SdkVersion{}) + expected := []string{ + "azure-native:monitor/v20220222:privateLinkForAzureAd", // change case + "azure-native:insights/v20220222:PrivateLinkForAzureAd", // change module + "azure-native:insights/v20220222:privateLinkForAzureAd", // change module and case + } + assert.ElementsMatch(t, expected, actual) + }) + + t.Run("previous provider & compatible version", func(t *testing.T) { + actual := generateTypeAliases("Monitor", "PrivateLinkForAzureAd", "v20220222", "Insights", nil, []openapi.SdkVersion{"v20200110", "v20210111"}) + expected := []string{ + "azure-native:monitor/v20200110:PrivateLinkForAzureAd", // change version + "azure-native:insights/v20200110:PrivateLinkForAzureAd", // change version & module + "azure-native:monitor/v20210111:PrivateLinkForAzureAd", // change version + "azure-native:insights/v20210111:PrivateLinkForAzureAd", // change version & module + "azure-native:insights/v20220222:PrivateLinkForAzureAd", // change module + } + assert.ElementsMatch(t, expected, actual) + }) + + t.Run("previous provider, compatible version & type alias", func(t *testing.T) { + actual := generateTypeAliases("Monitor", "PrivateLinkForAzureAd", "v20220222", "Insights", []string{"privateLinkForAzureAd"}, []openapi.SdkVersion{"v20200110", "v20210111"}) + expected := []string{ + "azure-native:monitor/v20200110:PrivateLinkForAzureAd", // change version + "azure-native:monitor/v20200110:privateLinkForAzureAd", // change version & case + "azure-native:insights/v20200110:PrivateLinkForAzureAd", // change version & module + "azure-native:insights/v20200110:privateLinkForAzureAd", // change version & module & case + "azure-native:monitor/v20210111:PrivateLinkForAzureAd", // change version + "azure-native:monitor/v20210111:privateLinkForAzureAd", // change version & case + "azure-native:insights/v20210111:PrivateLinkForAzureAd", // change version & module + "azure-native:insights/v20210111:privateLinkForAzureAd", // change version & module & case + "azure-native:insights/v20220222:PrivateLinkForAzureAd", // change module + "azure-native:insights/v20220222:privateLinkForAzureAd", // change module & case + "azure-native:monitor/v20220222:privateLinkForAzureAd", // change case + } + assert.ElementsMatch(t, expected, actual) + }) } func TestFindNestedResources(t *testing.T) {