diff --git a/scripts/automation-generate-go-sdk.sh b/scripts/automation-generate-go-sdk.sh index aab7b2b940c..aaa76406fcc 100755 --- a/scripts/automation-generate-go-sdk.sh +++ b/scripts/automation-generate-go-sdk.sh @@ -6,17 +6,21 @@ set -e DIR="$(cd "$(dirname "$0")" && pwd)/.." - sdkToGenerate="${1}" -if [[ "${sdkToGenerate}" == "" ]]; then - echo "must specify SDK to generate!" >&2 + +usage() { + echo "Usage: automation-generate-go-sdk.sh sdkToGenerate" >&2 echo "" >&2 - echo "supported values are:" >&2 + echo "sdkToGenerate is required and should be one of:" >&2 echo " microsoft-graph" >&2 echo " resource-manager" >&2 echo "" >&2 - echo "example usage:" >&2 - echo "${0} resource-manager" >&2 +} + +if [[ "${sdkToGenerate}" == "" ]]; then + echo "sdkToGenerate not specified" >&2 + echo "" >&2 + usage exit 1 fi diff --git a/tools/data-api-sdk/v1/helpers/inner_most_operation_object_definition.go b/tools/data-api-sdk/v1/helpers/inner_most_operation_object_definition.go new file mode 100644 index 00000000000..a11c26aa880 --- /dev/null +++ b/tools/data-api-sdk/v1/helpers/inner_most_operation_object_definition.go @@ -0,0 +1,21 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package helpers + +import "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" + +// InnerMostSDKOperationOptionObjectDefinition returns the inner most SDKOperationOptionObjectDefinition. +// +// In the event of an SDKOperationOptionObjectDefinition with no NestedItem, the current item will be returned. +// In the event of a NestedItem being present, or a NestedItem having a NestedItem, this method +// will recurse until it finds the SDKOperationOptionObjectDefinition without a NestedItem. +// +// This is useful for obtaining the inner type for assignment purposes. +func InnerMostSDKOperationOptionObjectDefinition(input models.SDKOperationOptionObjectDefinition) models.SDKOperationOptionObjectDefinition { + if input.NestedItem != nil { + return InnerMostSDKOperationOptionObjectDefinition(*input.NestedItem) + } + + return input +} diff --git a/tools/data-api-sdk/v1/helpers/todo.go b/tools/data-api-sdk/v1/helpers/todo.go deleted file mode 100644 index 4d32f6eb14d..00000000000 --- a/tools/data-api-sdk/v1/helpers/todo.go +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package helpers - -// TODO: unit tests covering these functions diff --git a/tools/importer-rest-api-specs/GNUmakefile b/tools/importer-rest-api-specs/GNUmakefile index b889a835faf..8087008d5e5 100644 --- a/tools/importer-rest-api-specs/GNUmakefile +++ b/tools/importer-rest-api-specs/GNUmakefile @@ -16,6 +16,9 @@ import: build import-with-api: build ./importer-rest-api-specs import --data-api=http://localhost:8080 +validate: build + ./importer-rest-api-specs validate + test: build go test -v ./... -timeout=60m diff --git a/tools/importer-rest-api-specs/components/discovery/data.go b/tools/importer-rest-api-specs/components/discovery/data.go deleted file mode 100644 index d93bee5256e..00000000000 --- a/tools/importer-rest-api-specs/components/discovery/data.go +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package discovery - -import ( - "fmt" - "path/filepath" - "strings" - - "github.com/hashicorp/go-hclog" - "github.com/hashicorp/pandora/tools/sdk/config/definitions" - "github.com/hashicorp/pandora/tools/sdk/config/services" -) - -type FindServiceInput struct { - ConfigFilePath string - OutputDirectory string - SwaggerDirectory string - Logger hclog.Logger -} - -func FindServices(input FindServiceInput, terraformConfig definitions.Config) (*[]ServiceInput, error) { - parsed, err := services.LoadFromFile(input.ConfigFilePath) - if err != nil { - return nil, fmt.Errorf("loading config: %+v", err) - } - - specificationsDirectory := filepath.Join(input.SwaggerDirectory, "specification") - resourceManagerServices, err := FindResourceManagerServices(specificationsDirectory, input.Logger) - if err != nil { - return nil, fmt.Errorf("retrieving Resource Manager Service details from %q: %+v", specificationsDirectory, err) - } - - output := make([]ServiceInput, 0) - for _, service := range parsed.Services { - // find the versions for each directory within this Service - serviceDetails, ok := (*resourceManagerServices)[service.Directory] - if !ok { - return nil, fmt.Errorf("details for the Service %q were not found - does it exist on disk?", service.Directory) - } - - for _, version := range service.Available { - versionDirectories, ok := serviceDetails.ApiVersionPaths[version] - if !ok { - return nil, fmt.Errorf("details for the Version %q of Service %q were not found - does it exist on disk?", version, service.Directory) - } - - for _, versionDirectory := range versionDirectories { - filesForVersion := make([]string, 0) - filesInDirectory, err := SwaggerFilesInDirectory(versionDirectory) - if err != nil { - return nil, fmt.Errorf("finding the swagger files within %q: %+v", versionDirectory, err) - } - for _, file := range *filesInDirectory { - fileWithoutPrefix := strings.TrimPrefix(file, versionDirectory) - filesForVersion = append(filesForVersion, fileWithoutPrefix) - } - - resourceManagerService := ResourceManagerServiceInput{ - ServiceName: service.Name, - ApiVersion: version, - ResourceProvider: &serviceDetails.ResourceProvider, - ResourceProviderToFilterTo: service.ResourceProvider, - OutputDirectoryJson: input.OutputDirectory, - SwaggerDirectory: versionDirectory, - SwaggerFiles: filesForVersion, - } - - definition, ok := terraformConfig.Services[service.Name] - if ok { - resourceManagerService.TerraformServiceDefinition = &definition - } - - output = append(output, resourceManagerService.ToRunInput()) - } - } - } - return &output, nil -} - -func FindServicesByName(input FindServiceInput, terraformConfig definitions.Config, servicesToFind []string) (*[]ServiceInput, error) { - parsed, err := services.LoadFromFile(input.ConfigFilePath) - if err != nil { - return nil, fmt.Errorf("loading config: %+v", err) - } - - specificationsDirectory := filepath.Join(input.SwaggerDirectory, "specification") - resourceManagerServices, err := FindResourceManagerServices(specificationsDirectory, input.Logger) - if err != nil { - return nil, fmt.Errorf("retrieving Resource Manager Service details from %q: %+v", specificationsDirectory, err) - } - - serviceNames := make(map[string]struct{}) - for _, serviceName := range servicesToFind { - serviceNames[serviceName] = struct{}{} - } - - output := make([]ServiceInput, 0) - for _, service := range parsed.Services { - if _, ok := serviceNames[service.Name]; !ok { - continue - } - - // find the versions for each directory within this Service - serviceDetails, ok := (*resourceManagerServices)[service.Directory] - if !ok { - return nil, fmt.Errorf("details for the Service %q were not found - does it exist on disk?", service.Directory) - } - - for _, version := range service.Available { - versionDirectories, ok := serviceDetails.ApiVersionPaths[version] - if !ok { - return nil, fmt.Errorf("details for the Version %q of Service %q were not found - does it exist on disk?", version, service.Directory) - } - - for _, versionDirectory := range versionDirectories { - filesForVersion := make([]string, 0) - filesInDirectory, err := SwaggerFilesInDirectory(versionDirectory) - if err != nil { - return nil, fmt.Errorf("finding the swagger files within %q: %+v", versionDirectory, err) - } - for _, file := range *filesInDirectory { - fileWithoutPrefix := strings.TrimPrefix(file, versionDirectory) - filesForVersion = append(filesForVersion, fileWithoutPrefix) - } - - resourceManagerService := ResourceManagerServiceInput{ - ServiceName: service.Name, - ApiVersion: version, - ResourceProvider: &serviceDetails.ResourceProvider, - ResourceProviderToFilterTo: service.ResourceProvider, - OutputDirectoryJson: input.OutputDirectory, - SwaggerDirectory: versionDirectory, - SwaggerFiles: filesForVersion, - } - - definition, ok := terraformConfig.Services[service.Name] - if ok { - resourceManagerService.TerraformServiceDefinition = &definition - } - - output = append(output, resourceManagerService.ToRunInput()) - } - } - } - return &output, nil -} diff --git a/tools/importer-rest-api-specs/components/discovery/discovery.go b/tools/importer-rest-api-specs/components/discovery/discovery.go deleted file mode 100644 index fb5052cbca4..00000000000 --- a/tools/importer-rest-api-specs/components/discovery/discovery.go +++ /dev/null @@ -1,181 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package discovery - -import ( - "fmt" - "io/fs" - "os" - "path/filepath" - "sort" - "strings" - - "github.com/hashicorp/go-hclog" -) - -type resourceManagerService struct { - apiVersions map[string][]string - resourceProvider string -} - -type swaggerDirectoryMetaData struct { - serviceName string - serviceType string - resourceProvider string - serviceReleaseState string - apiVersion string -} - -func getMetaDataForSwaggerDirectory(input []string) *swaggerDirectoryMetaData { - for _, v := range input { - if strings.EqualFold(v, "examples") { - return nil - } - } - - if len(input) == 5 { - // appconfiguration/data-plane/Microsoft.AppConfiguration/stable/1.0 - // vmware/resource-manager/Microsoft.AVS/{preview|stable}/{version} - item := swaggerDirectoryMetaData{ - serviceName: input[0], - serviceType: input[1], - resourceProvider: input[2], - serviceReleaseState: input[3], - apiVersion: input[4], - } - if item.isValid() { - return &item - } - } - if len(input) >= 6 { - // compute/resource-manager/Microsoft.Compute/CloudserviceRP/stable/2022-04-04 - // compute/resource-manager/Microsoft.Compute/CloudserviceRP/stable/2022-04-04/CloudServiceRP - item := swaggerDirectoryMetaData{ - serviceName: input[0], - serviceType: input[1], - resourceProvider: input[2], - serviceReleaseState: input[4], - apiVersion: input[5], - } - if item.isValid() { - return &item - } - } - - return nil -} - -func (d swaggerDirectoryMetaData) String() string { - return strings.Join([]string{ - fmt.Sprintf("Service %q", d.serviceName), - fmt.Sprintf("Type %q", d.serviceType), - fmt.Sprintf("RP %q", d.resourceProvider), - fmt.Sprintf("Status %q", d.serviceReleaseState), - fmt.Sprintf("Version %q", d.apiVersion), - }, " / ") -} - -func (d swaggerDirectoryMetaData) isValid() bool { - if !strings.EqualFold(d.serviceType, "resource-manager") { - return false - } - // ignore 'common' e.g. ./specification/streamanalytics/resource-manager/Microsoft.StreamAnalytics/common/v1/definitions.json - if !strings.EqualFold(d.serviceReleaseState, "stable") && !strings.EqualFold(d.serviceReleaseState, "preview") { - return false - } - - return true -} - -func FindResourceManagerServices(directory string, logger hclog.Logger) (*map[string]ResourceManagerService, error) { - services := make(map[string]resourceManagerService, 0) - err := filepath.Walk(directory, - func(fullPath string, info os.FileInfo, err error) error { - if err != nil { - return err - } - if !info.IsDir() { - return nil - } - - relativePath := strings.TrimPrefix(fullPath, directory) - relativePath = strings.TrimPrefix(relativePath, string(os.PathSeparator)) - trimmed := strings.TrimPrefix(relativePath, directory) - segments := strings.Split(trimmed, string(os.PathSeparator)) - - metadata := getMetaDataForSwaggerDirectory(segments) - if metadata == nil { - return nil - } - - logger.Debug(metadata.String()) - logger.Debug(fmt.Sprintf("Path %q", fullPath)) - - existingPaths, ok := services[metadata.serviceName] - if !ok { - existingPaths = resourceManagerService{ - resourceProvider: metadata.resourceProvider, - apiVersions: map[string][]string{}, - } - } - existingPaths.apiVersions[metadata.apiVersion] = append(existingPaths.apiVersions[metadata.apiVersion], fullPath) - services[metadata.serviceName] = existingPaths - - return nil - }) - if err != nil { - return nil, err - } - - serviceNames := make([]string, 0) - for serviceName := range services { - serviceNames = append(serviceNames, serviceName) - } - sort.Strings(serviceNames) - out := make(map[string]ResourceManagerService, 0) - for _, serviceName := range serviceNames { - paths := services[serviceName] - - sortedApiVersions := make([]string, 0) - for k := range paths.apiVersions { - sortedApiVersions = append(sortedApiVersions, k) - } - sort.Strings(sortedApiVersions) - - out[serviceName] = ResourceManagerService{ - Name: serviceName, - ApiVersionPaths: paths.apiVersions, - ResourceProvider: paths.resourceProvider, - } - } - return &out, nil -} - -type ResourceManagerService struct { - Name string - ApiVersionPaths map[string][]string - ResourceProvider string -} - -func SwaggerFilesInDirectory(directory string) (*[]string, error) { - swaggerFiles := make([]string, 0) - if err := filepath.WalkDir(directory, func(path string, d fs.DirEntry, err error) error { - if err != nil { - return err - } - name := filepath.Base(path) - if strings.EqualFold(name, "examples") { - return fs.SkipDir - } - - extension := filepath.Ext(name) - if strings.EqualFold(extension, ".json") { - swaggerFiles = append(swaggerFiles, path) - } - return nil - }); err != nil { - return nil, err - } - return &swaggerFiles, nil -} diff --git a/tools/importer-rest-api-specs/components/discovery/discovery_test.go b/tools/importer-rest-api-specs/components/discovery/discovery_test.go deleted file mode 100644 index c07d309cc0e..00000000000 --- a/tools/importer-rest-api-specs/components/discovery/discovery_test.go +++ /dev/null @@ -1,216 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package discovery - -import "testing" - -func TestGetMetaDataForSwaggerDirectory(t *testing.T) { - testData := []struct { - input []string - expected *swaggerDirectoryMetaData - }{ - { - input: []string{ - "not", - "enough", - "segments", - }, - expected: nil, - }, - { - // Data Plane URIs aren't valid - input: []string{ - "appconfiguration", - "data-plane", - "Microsoft.AppConfiguration", - "stable", - "1.0", - }, - expected: nil, - }, - - { - // Resource Manager (most items) - Stable - input: []string{ - "vmware", - "resource-manager", - "Microsoft.AVS", - "stable", - "2022-01-01", - }, - expected: &swaggerDirectoryMetaData{ - serviceName: "vmware", - serviceType: "resource-manager", - resourceProvider: "Microsoft.AVS", - serviceReleaseState: "stable", - apiVersion: "2022-01-01", - }, - }, - { - // Resource Manager (most items) - Preview - input: []string{ - "vmware", - "resource-manager", - "Microsoft.AVS", - "preview", - "2022-01-01", - }, - expected: &swaggerDirectoryMetaData{ - serviceName: "vmware", - serviceType: "resource-manager", - resourceProvider: "Microsoft.AVS", - serviceReleaseState: "preview", - apiVersion: "2022-01-01", - }, - }, - { - // Resource Manager (most items) - Unknown - input: []string{ - "vmware", - "resource-manager", - "Microsoft.AVS", - "mercury", - "2022-01-01", - }, - expected: nil, - }, - - { - // Folder Structure for Service Group (e.g. Compute) - top-level - Stable - // compute/resource-manager/Microsoft.Compute/CloudserviceRP/stable/2022-04-04 - input: []string{ - "compute", - "resource-manager", - "Microsoft.Compute", - "CloudserviceRP", - "stable", - "2022-04-04", - }, - expected: &swaggerDirectoryMetaData{ - serviceName: "compute", - serviceType: "resource-manager", - resourceProvider: "Microsoft.Compute", - serviceReleaseState: "stable", - apiVersion: "2022-04-04", - }, - }, - { - // Folder Structure for Service Group (e.g. Compute) - top-level - Preview - // compute/resource-manager/Microsoft.Compute/CloudserviceRP/stable/2022-04-04 - input: []string{ - "compute", - "resource-manager", - "Microsoft.Compute", - "CloudserviceRP", - "preview", - "2022-04-04", - }, - expected: &swaggerDirectoryMetaData{ - serviceName: "compute", - serviceType: "resource-manager", - resourceProvider: "Microsoft.Compute", - serviceReleaseState: "preview", - apiVersion: "2022-04-04", - }, - }, - { - // Folder Structure for Service Group (e.g. Compute) - top-level - Unknown - // compute/resource-manager/Microsoft.Compute/CloudserviceRP/stable/2022-04-04 - input: []string{ - "compute", - "resource-manager", - "Microsoft.Compute", - "CloudserviceRP", - "mercury", - "2022-04-04", - }, - expected: nil, - }, - - { - // Folder Structure for Service Group (e.g. Compute) - component - Stable - // compute/resource-manager/Microsoft.Compute/CloudserviceRP/stable/2022-04-04/CloudServiceRP - input: []string{ - "compute", - "resource-manager", - "Microsoft.Compute", - "CloudserviceRP", - "stable", - "2022-04-04", - "CloudServiceRP", - }, - expected: &swaggerDirectoryMetaData{ - serviceName: "compute", - serviceType: "resource-manager", - resourceProvider: "Microsoft.Compute", - serviceReleaseState: "stable", - apiVersion: "2022-04-04", - }, - }, - { - // Folder Structure for Service Group (e.g. Compute) - component - Preview - // compute/resource-manager/Microsoft.Compute/CloudserviceRP/stable/2022-04-04/CloudServiceRP - input: []string{ - "compute", - "resource-manager", - "Microsoft.Compute", - "CloudserviceRP", - "preview", - "2022-04-04", - "CloudServiceRP", - }, - expected: &swaggerDirectoryMetaData{ - serviceName: "compute", - serviceType: "resource-manager", - resourceProvider: "Microsoft.Compute", - serviceReleaseState: "preview", - apiVersion: "2022-04-04", - }, - }, - { - // Folder Structure for Service Group (e.g. Compute) - component - Unknown - // compute/resource-manager/Microsoft.Compute/CloudserviceRP/stable/2022-04-04/CloudServiceRP - input: []string{ - "compute", - "resource-manager", - "Microsoft.Compute", - "CloudserviceRP", - "mercury", - "2022-04-04", - "CloudServiceRP", - }, - expected: nil, - }, - } - for i, data := range testData { - t.Logf("Iteration %d..", i) - - actual := getMetaDataForSwaggerDirectory(data.input) - if actual == nil { - if data.expected == nil { - continue - } - t.Fatalf("expected a result but didn't get one") - } - if data.expected == nil { - t.Fatalf("expected no result but got: %s", actual.String()) - } - - if actual.serviceName != data.expected.serviceName { - t.Fatalf("expected serviceName to be %q but got %q", data.expected.serviceName, actual.serviceName) - } - if actual.serviceType != data.expected.serviceType { - t.Fatalf("expected serviceType to be %q but got %q", data.expected.serviceType, actual.serviceType) - } - if actual.resourceProvider != data.expected.resourceProvider { - t.Fatalf("expected resourceProvider to be %q but got %q", data.expected.resourceProvider, actual.resourceProvider) - } - if actual.serviceReleaseState != data.expected.serviceReleaseState { - t.Fatalf("expected serviceReleaseState to be %q but got %q", data.expected.serviceReleaseState, actual.serviceReleaseState) - } - if actual.apiVersion != data.expected.apiVersion { - t.Fatalf("expected apiVersion to be %q but got %q", data.expected.apiVersion, actual.apiVersion) - } - } -} diff --git a/tools/importer-rest-api-specs/components/discovery/interface.go b/tools/importer-rest-api-specs/components/discovery/interface.go deleted file mode 100644 index 9b27346c0b4..00000000000 --- a/tools/importer-rest-api-specs/components/discovery/interface.go +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package discovery - -import "github.com/hashicorp/pandora/tools/sdk/config/definitions" - -type ServiceInput struct { - // ServiceName is the name of the Service (e.g. `Compute`). - ServiceName string - - // ApiVersion is the version of the API (e.g. `2020-10-01`). - ApiVersion string - - // OutputDirectoryJson is the directory where the generated JSON files should be output. - OutputDirectoryJson string - - // ResourceProvider is the ResourceProvider associated with this Service (for example `Microsoft.Compute`) - // parsed from the Resource ID. - ResourceProvider *string - - // ResourceProviderToFilterTo allows filtering the operations for the given Service/API Version to only - // operations contained within this Resource Provider. Whilst the majority of the time this can be left - // unset, some Services such as Network include operations from different Resource Providers which can - // cause issues - as such this field can be conditionally set to remove unrelated operations. - ResourceProviderToFilterTo *string - - // SwaggerDirectory is the path to the directory containing the Swagger/OpenAPI files for this Service/API Version. - SwaggerDirectory string - - // SwaggerFiles is a list of the Swagger/OpenAPI files for this Service/API Version. - SwaggerFiles []string - - // TerraformServiceDefinition defines the Terraform related details for this Service, used for Terraform - // related processing/generation. - TerraformServiceDefinition *definitions.ServiceDefinition -} - -// ResourceManagerServiceInput provides a Resource Manager specific wrapper around a ServiceInput -type ResourceManagerServiceInput struct { - ServiceName string - ApiVersion string - OutputDirectoryJson string - ResourceProvider *string - ResourceProviderToFilterTo *string - SwaggerDirectory string - SwaggerFiles []string - TerraformServiceDefinition *definitions.ServiceDefinition -} - -func (rmi ResourceManagerServiceInput) ToRunInput() ServiceInput { - return ServiceInput{ - ServiceName: rmi.ServiceName, - ApiVersion: rmi.ApiVersion, - ResourceProvider: rmi.ResourceProvider, - ResourceProviderToFilterTo: rmi.ResourceProviderToFilterTo, - OutputDirectoryJson: rmi.OutputDirectoryJson, - SwaggerDirectory: rmi.SwaggerDirectory, - SwaggerFiles: rmi.SwaggerFiles, - TerraformServiceDefinition: rmi.TerraformServiceDefinition, - } -} diff --git a/tools/importer-rest-api-specs/components/parser/cleanup/cleanup_test.go b/tools/importer-rest-api-specs/components/parser/cleanup/cleanup_test.go deleted file mode 100644 index 1d7f48a6d2f..00000000000 --- a/tools/importer-rest-api-specs/components/parser/cleanup/cleanup_test.go +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package cleanup - -import "testing" - -func TestNormalizeReservedKeywords(t *testing.T) { - testData := []struct { - input string - expected string - }{ - { - input: "default", - expected: "defaultName", - }, - { - input: "type", - expected: "typeName", - }, - { - input: "interface", - expected: "interfaceName", - }, - { - input: "chubbyPandas", - expected: "chubbyPandas", - }, - } - - for _, v := range testData { - t.Logf("[DEBUG] Testing %s", v.input) - - actual := NormalizeReservedKeywords(v.input) - if actual != v.expected { - t.Fatalf("Expected %s but got %s", v.expected, actual) - } - } -} - -func TestNormalizeSegmentName(t *testing.T) { - testData := []struct { - input string - expected string - }{ - { - input: "searches", - expected: "Search", - }, - { - input: "batches", - expected: "Batch", - }, - { - input: "classes", - expected: "Class", - }, - { - input: "prefixes", - expected: "Prefix", - }, - { - input: "databases", - expected: "Database", - }, - { - input: "services", - expected: "Service", - }, - { - input: "accounts", - expected: "Account", - }, - { - input: "studios", - expected: "Studio", - }, - { - input: "caches", - expected: "Cache", - }, - { - input: "identities", - expected: "Identity", - }, - { - input: "series", - expected: "Series", - }, - } - - for _, v := range testData { - t.Logf("[DEBUG] Testing %s", v.input) - - actual := NormalizeSegmentName(v.input) - if actual != v.expected { - t.Fatalf("Expected %s but got %s", v.expected, actual) - } - } -} - -func TestPluraliseName(t *testing.T) { - testData := []struct { - input string - expected string - }{ - { - input: "/name", - expected: "names", - }, - { - input: "/ServiceLinker", - expected: "ServiceLinker", - }, - } - - for _, v := range testData { - t.Logf("[DEBUG] Testing %s", v.input) - - actual := PluraliseName(v.input) - if actual != v.expected { - t.Fatalf("Expected %s but got %s", v.expected, actual) - } - } -} diff --git a/tools/importer-rest-api-specs/components/parser/commonschema/custom_fields.go b/tools/importer-rest-api-specs/components/parser/commonschema/custom_fields.go deleted file mode 100644 index 1fab053e0e5..00000000000 --- a/tools/importer-rest-api-specs/components/parser/commonschema/custom_fields.go +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package commonschema - -import ( - sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/components/parser/internal" -) - -type customFieldMatcher interface { - // IsMatch returns whether the field and definition provided match this Custom Field Matcher - // meaning that the types should be replaced with the CustomFieldType found in customFieldType - IsMatch(field sdkModels.SDKField, known internal.ParseResult) bool - - // ReplacementObjectDefinition returns the replacement SDKObjectDefinition which should be used - // in place of the parsed SDKObjectDefinition for this SDKField. - ReplacementObjectDefinition() sdkModels.SDKObjectDefinition -} - -var CustomFieldMatchers = []customFieldMatcher{ - edgeZoneFieldMatcher{}, - locationMatcher{}, - systemAssignedIdentityMatcher{}, - legacySystemAndUserAssignedIdentityListMatcher{}, - legacySystemAndUserAssignedIdentityMapMatcher{}, - systemAndUserAssignedIdentityListMatcher{}, - systemAndUserAssignedIdentityMapMatcher{}, - systemOrUserAssignedIdentityListMatcher{}, - systemOrUserAssignedIdentityMapMatcher{}, - tagsMatcher{}, - userAssignedIdentityListMatcher{}, - userAssignedIdentityMapMatcher{}, - systemDataMatcher{}, - zoneFieldMatcher{}, - zonesFieldMatcher{}, -} diff --git a/tools/importer-rest-api-specs/components/parser/constants/interface.go b/tools/importer-rest-api-specs/components/parser/constants/interface.go deleted file mode 100644 index 441f182f514..00000000000 --- a/tools/importer-rest-api-specs/components/parser/constants/interface.go +++ /dev/null @@ -1,333 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package constants - -import ( - "fmt" - "reflect" - "regexp" - "strconv" - "strings" - - "github.com/go-openapi/spec" - sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/components/parser/cleanup" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/featureflags" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/logging" -) - -type constantExtension struct { - // name defines the Name that should be used for this Constant - name string - - // valuesToDisplayNames defines any display name overrides that should be used for this Constant - // NOTE: whilst the API Definitions may define a value with no display name - this map contains - // only values with a name defined. - valuesToDisplayNames *map[interface{}]string -} - -type ParsedConstant struct { - Name string - Details sdkModels.SDKConstant -} - -func MapConstant(typeVal spec.StringOrArray, fieldName string, modelName *string, values []interface{}, extensions spec.Extensions) (*ParsedConstant, error) { - if len(values) == 0 { - return nil, fmt.Errorf("Enum in %q has no values", fieldName) - } - - constantName := fieldName - - // the name needs to come from the `x-ms-enum` extension - constExtension, err := parseConstantExtensionFromExtension(extensions) - if err != nil { - if featureflags.AllowConstantsWithoutXMSEnum { - logging.Debugf("Field %q had an invalid `x-ms-enum`: %+v", fieldName, err) - // this attempts to construct a unique name for a constant out of the model name and field name - // to prevent duplicate definitions of constants, specifically constants called `type` - // of which there are several in data factory (#3725) - if strings.EqualFold(fieldName, "type") && modelName != nil { - constantPrefix := strings.TrimSuffix(*modelName, "Type") - constantName = constantPrefix + strings.Title(fieldName) - logging.Debugf("Field %q renamed to %q", fieldName, constantName) - } - - } else { - return nil, fmt.Errorf("parsing x-ms-enum: %+v", err) - } - } - if constExtension != nil { - constantName = constExtension.name - } - - constantType := sdkModels.StringSDKConstantType - if typeVal.Contains("integer") { - constantType = sdkModels.IntegerSDKConstantType - } else if typeVal.Contains("number") { - constantType = sdkModels.FloatSDKConstantType - } - - keysAndValues := make(map[string]string) - for i, raw := range values { - if constantType == sdkModels.StringSDKConstantType { - value, ok := raw.(string) - if !ok { - return nil, fmt.Errorf("expected a string but got %+v for the %d value for %q", raw, i, constExtension.name) - } - // Some numbers are modelled as strings - if numVal, err := strconv.ParseFloat(value, 64); err == nil { - if strings.Contains(value, ".") { - normalizedName := normalizeConstantKey(value) - keysAndValues[normalizedName] = value - continue - } - - key := keyValueForInteger(int64(numVal)) - val := fmt.Sprintf("%d", int64(numVal)) - normalizedName := normalizeConstantKey(key) - keysAndValues[normalizedName] = val - continue - } - normalizedName := normalizeConstantKey(value) - keysAndValues[normalizedName] = value - continue - } - - if constantType == sdkModels.IntegerSDKConstantType { - // This gets parsed out as a float64 even though it's an Integer :upside_down_smile: - value, ok := raw.(float64) - if !ok { - // Except sometimes it's actually a string. That's numberwang. - v, ok := raw.(string) - if !ok { - typeName := reflect.TypeOf(raw).Name() - return nil, fmt.Errorf("expected a float64/string but got type %q value %+v for at index %d for %q", typeName, raw, i, constExtension.name) - } - - val, err := strconv.Atoi(v) - if err != nil { - return nil, fmt.Errorf("converting string value %q to an integer: %+v", v, err) - } - - value = float64(val) - } - - key := keyValueForInteger(int64(value)) - // if an override name is defined for this Constant then we should use it - if constExtension != nil && constExtension.valuesToDisplayNames != nil { - overrideName, hasOverride := (*constExtension.valuesToDisplayNames)[value] - if hasOverride { - key = overrideName - } - } - - val := fmt.Sprintf("%d", int64(value)) - normalizedName := normalizeConstantKey(key) - keysAndValues[normalizedName] = val - continue - } - - if constantType == sdkModels.FloatSDKConstantType { - value, ok := raw.(float64) - if !ok { - return nil, fmt.Errorf("expected an float but got %+v for the %d value for %q", raw, i, constExtension.name) - } - - key := keyValueForFloat(value) - val := stringValueForFloat(value) - normalizedName := normalizeConstantKey(key) - keysAndValues[normalizedName] = val - continue - } - - return nil, fmt.Errorf("unsupported constant type %q", string(constantType)) - } - - // allows us to parse out the actual types above then force a string here if needed - if constExtension == nil { - constantType = sdkModels.StringSDKConstantType - } - - return &ParsedConstant{ - Name: constantName, - Details: sdkModels.SDKConstant{ - Values: keysAndValues, - Type: constantType, - }, - }, nil -} - -func parseConstantExtensionFromExtension(field spec.Extensions) (*constantExtension, error) { - // Constants should always have `x-ms-enum` - enumDetailsRaw, ok := field["x-ms-enum"] - if !ok { - return nil, fmt.Errorf("constant is missing x-ms-enum") - } - - enumDetails, ok := enumDetailsRaw.(map[string]interface{}) - if !ok { - return nil, fmt.Errorf("enum details weren't a map[string]interface{}") - } - - var enumName *string - var valuesToDisplayNames *map[interface{}]string - for k, v := range enumDetails { - // presume inconsistencies in the data - if strings.EqualFold(k, "name") { - normalizedEnumName := cleanup.NormalizeName(v.(string)) - enumName = &normalizedEnumName - } - - if strings.EqualFold(k, "values") { - items := v.([]interface{}) - displayNameOverrides := make(map[interface{}]string) - for _, itemRaw := range items { - item := itemRaw.(map[string]interface{}) - name, ok := item["name"].(string) - if !ok || name == "" { - // there isn't a custom name defined for this, so we should ignore it - continue - } - value, ok := item["value"].(interface{}) - if !ok { - continue - } - // NOTE: whilst `x-ms-enum` includes a `description` field we don't support that today - // support for that is tracked in https://github.com/hashicorp/pandora/issues/231 - - displayNameOverrides[value] = name - } - if len(displayNameOverrides) > 0 { - valuesToDisplayNames = &displayNameOverrides - } - } - - // NOTE: the Swagger Extension defines `modelAsString` which is used to define whether - // this should be output as a fixed set of values (e.g. a constant) or an extendable - // list of strings (e.g. a set of possible string values with other values possible) - // however we're not concerned with the difference - so we ignore this. - } - if enumName == nil { - return nil, fmt.Errorf("enum details are missing a `name`") - } - - output := constantExtension{ - name: *enumName, - } - if valuesToDisplayNames != nil { - output.valuesToDisplayNames = valuesToDisplayNames - } - return &output, nil -} - -func keyValueForInteger(value int64) string { - s := fmt.Sprintf("%d", value) - vals := map[int32]string{ - '-': "Negative", - '0': "Zero", - '1': "One", - '2': "Two", - '3': "Three", - '4': "Four", - '5': "Five", - '6': "Six", - '7': "Seven", - '8': "Eight", - '9': "Nine", - } - out := "" - for _, c := range s { - v, ok := vals[c] - if !ok { - panic(fmt.Sprintf("missing mapping for %q", string(c))) - } - out += v - } - - return out -} - -func keyValueForFloat(value float64) string { - stringified := stringValueForFloat(value) - return stringifyNumberInput(stringified) -} - -func stringValueForFloat(value float64) string { - return strconv.FormatFloat(value, 'f', -1, 64) -} - -func normalizeConstantKey(input string) string { - output := input - output = stringifyNumberInput(output) - if !strings.Contains(output, "Point") { - output = renameMultiplesOfZero(output) - } - - output = strings.ReplaceAll(output, "*", "Any") - // TODO: add more if we find them - - output = cleanup.NormalizeName(output) - return output -} - -func stringifyNumberInput(input string) string { - vals := map[int32]string{ - '.': "Point", - '+': "Positive", - '-': "Negative", - '0': "Zero", - '1': "One", - '2': "Two", - '3': "Three", - '4': "Four", - '5': "Five", - '6': "Six", - '7': "Seven", - '8': "Eight", - '9': "Nine", - } - output := "" - for _, c := range input { - v, ok := vals[c] - if !ok { - output += string(c) - continue - } - output += v - } - return output -} - -func renameMultiplesOfZero(input string) string { - if strings.HasPrefix(input, "Zero") && !strings.HasSuffix(input, "Zero") { - return input - } - - re := regexp.MustCompile("(?:Zero)") - zeros := re.FindAllStringIndex(input, -1) - z := len(zeros) - - if z < 2 { - return input - } - - vals := map[int]string{ - 2: "Hundred", - 3: "Thousand", - 4: "Thousand", - 5: "HundredThousand", - 6: "Million", - } - - if v, ok := vals[z]; ok { - switch z { - case 4: - return strings.Replace(input, strings.Repeat("Zero", z), "Zero"+v, 1) - default: - return strings.Replace(input, strings.Repeat("Zero", z), v, 1) - } - } - - return input -} diff --git a/tools/importer-rest-api-specs/components/parser/dataworkarounds/README.md b/tools/importer-rest-api-specs/components/parser/dataworkarounds/README.md deleted file mode 100644 index 583fe4e500b..00000000000 --- a/tools/importer-rest-api-specs/components/parser/dataworkarounds/README.md +++ /dev/null @@ -1,5 +0,0 @@ -## Swagger Data Workarounds - -This package provides temporary Swagger Data Workarounds whilst the source data is being corrected within [the `Azure/azure-rest-api-specs` repository](https://github.com/Azure/azure-rest-api-specs). - -Whilst this approach isn't ideal, since each workaround must be accompanied by a Pull Request to fix the incorrect data upstream, we believe this is a pragmatic solution to both unblock us in the short-term and fix the source data to enable quality improvements in the medium/long-term. diff --git a/tools/importer-rest-api-specs/components/parser/dataworkarounds/interface.go b/tools/importer-rest-api-specs/components/parser/dataworkarounds/interface.go deleted file mode 100644 index b622986b94a..00000000000 --- a/tools/importer-rest-api-specs/components/parser/dataworkarounds/interface.go +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package dataworkarounds - -import importerModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/models" - -type workaround interface { - // IsApplicable determines whether this workaround is applicable for this AzureApiDefinition - IsApplicable(apiDefinition *importerModels.AzureApiDefinition) bool - - // Name returns the Service Name and associated Pull Request number - Name() string - - // Process takes the apiDefinition and applies the Workaround to this AzureApiDefinition - Process(apiDefinition importerModels.AzureApiDefinition) (*importerModels.AzureApiDefinition, error) -} diff --git a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_temp_readonly_fields.go b/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_temp_readonly_fields.go deleted file mode 100644 index 602e3dfc5f8..00000000000 --- a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_temp_readonly_fields.go +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package dataworkarounds - -import ( - "fmt" - - importerModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/models" -) - -var _ workaround = workaroundTempReadOnlyFields{} - -type workaroundTempReadOnlyFields struct { -} - -func (w workaroundTempReadOnlyFields) IsApplicable(apiDefinition *importerModels.AzureApiDefinition) bool { - if apiDefinition.ServiceName == "ContainerService" && apiDefinition.ApiVersion == "2022-09-02-preview" { - return true - } - - if apiDefinition.ServiceName == "DevCenter" && apiDefinition.ApiVersion == "2023-04-01" { - return true - } - - if apiDefinition.ServiceName == "LoadTestService" && apiDefinition.ApiVersion == "2022-12-01" { - return true - } - - if apiDefinition.ServiceName == "ManagedIdentity" && apiDefinition.ApiVersion == "2023-01-31" { - return true - } - - return false -} - -func (w workaroundTempReadOnlyFields) Name() string { - return "Temp - Mark readonly fields as readonly" -} - -func (w workaroundTempReadOnlyFields) Process(apiDefinition importerModels.AzureApiDefinition) (*importerModels.AzureApiDefinition, error) { - if apiDefinition.ServiceName == "ContainerService" && apiDefinition.ApiVersion == "2022-09-02-preview" { - definition, err := w.markFieldAsComputed(apiDefinition, "Fleets", "FleetHubProfile", "Fqdn") - if err != nil { - return nil, err - } - - definition, err = w.markFieldAsComputed(*definition, "Fleets", "FleetHubProfile", "KubernetesVersion") - if err != nil { - return nil, err - } - - return definition, nil - } - - if apiDefinition.ServiceName == "DevCenter" && apiDefinition.ApiVersion == "2023-04-01" { - definition, err := w.markFieldAsComputed(apiDefinition, "Projects", "ProjectProperties", "DevCenterUri") - if err != nil { - return nil, err - } - - definition, err = w.markFieldAsComputed(*definition, "DevCenters", "DevCenterProperties", "DevCenterUri") - if err != nil { - return nil, err - } - - return definition, nil - } - - if apiDefinition.ServiceName == "LoadTestService" && apiDefinition.ApiVersion == "2022-12-01" { - definition, err := w.markFieldAsComputed(apiDefinition, "LoadTests", "LoadTestProperties", "DataPlaneURI") - if err != nil { - return nil, err - } - - return definition, nil - } - - if apiDefinition.ServiceName == "ManagedIdentity" && apiDefinition.ApiVersion == "2023-01-31" { - definition, err := w.markFieldAsComputed(apiDefinition, "ManagedIdentities", "UserAssignedIdentityProperties", "ClientId") - if err != nil { - return nil, err - } - - definition, err = w.markFieldAsComputed(*definition, "ManagedIdentities", "UserAssignedIdentityProperties", "PrincipalId") - if err != nil { - return nil, err - } - - definition, err = w.markFieldAsComputed(*definition, "ManagedIdentities", "UserAssignedIdentityProperties", "TenantId") - if err != nil { - return nil, err - } - - return definition, nil - } - - return nil, fmt.Errorf("unexpected Service %q / API Version %q", apiDefinition.ServiceName, apiDefinition.ApiVersion) -} - -func (w workaroundTempReadOnlyFields) markFieldAsComputed(input importerModels.AzureApiDefinition, apiResourceName, modelName, fieldName string) (*importerModels.AzureApiDefinition, error) { - resource, ok := input.Resources[apiResourceName] - if !ok { - return nil, fmt.Errorf("expected an APIResource %q but didn't get one", apiResourceName) - } - model, ok := resource.Models[modelName] - if !ok { - return nil, fmt.Errorf("expected a Model %q but didn't get one", modelName) - } - field, ok := model.Fields[fieldName] - if !ok { - return nil, fmt.Errorf("expected a Field %q but didn't get one", fieldName) - } - field.Optional = false - field.ReadOnly = true - field.Required = false - field.Sensitive = false - model.Fields[fieldName] = field - - resource.Models[modelName] = model - input.Resources[apiResourceName] = resource - return &input, nil -} diff --git a/tools/importer-rest-api-specs/components/parser/definition.go b/tools/importer-rest-api-specs/components/parser/definition.go deleted file mode 100644 index 79bc8d0dcc5..00000000000 --- a/tools/importer-rest-api-specs/components/parser/definition.go +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package parser - -import ( - "github.com/go-openapi/analysis" - "github.com/go-openapi/spec" -) - -type SwaggerDefinition struct { - // Name is the name of this Swagger file - Name string - - // swaggerSpecExpanded is a flattened version of the Swagger spec into a single file - swaggerSpecExpanded *analysis.Spec - - // swaggerSpecWithReferences is the same as Swagger Spec Expanded but contains the 'ref' details - swaggerSpecWithReferences *analysis.Spec - - swaggerSpecRaw *spec.Swagger - - swaggerSpecExtendedRaw *spec.Swagger -} diff --git a/tools/importer-rest-api-specs/components/parser/flattener.go b/tools/importer-rest-api-specs/components/parser/flattener.go deleted file mode 100644 index 108b534f5a6..00000000000 --- a/tools/importer-rest-api-specs/components/parser/flattener.go +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package parser - -import ( - "fmt" - "path/filepath" - "strings" - - "github.com/go-openapi/analysis" - "github.com/go-openapi/loads" - "github.com/go-openapi/spec" -) - -// load loads the swagger document and flattens it to ensure this contains -// all of the properties within a single file, which makes this easier to parse-out -// This can then be used with the Parser -func load(directory string, fileName string) (*SwaggerDefinition, error) { - filePath := filepath.Join(directory, fileName) - - // parsing this twice looks silly, so why are we doing this? - // we want the name and the properties of the objects, and the "expanded" version - // removes the names - it could be we can patch this to also include this, but - // for the moment there's essentially no harm to doing both - swaggerDocWithReferences, err := loads.Spec(filePath) - if err != nil { - return nil, fmt.Errorf("loading swagger file %q: %+v", filePath, err) - } - swaggerDocWithReferences, err = findAndMergeLocalMixins(swaggerDocWithReferences, directory, fileName) - if err != nil { - return nil, fmt.Errorf("could not mixin remote swagger files referenced by %q: %+v", filePath, err) - } - flattenedWithReferencesOpts := &analysis.FlattenOpts{ - Minimal: true, - Verbose: true, - Expand: false, - RemoveUnused: false, - //ContinueOnError: true, - - BasePath: swaggerDocWithReferences.SpecFilePath(), - Spec: analysis.New(swaggerDocWithReferences.Spec()), - } - if err := analysis.Flatten(*flattenedWithReferencesOpts); err != nil { - return nil, fmt.Errorf("flattening swagger file with references %q: %+v", filePath, err) - } - - swaggerSpecWithReferences := swaggerDocWithReferences.Analyzer - swaggerSpecWithReferencesRaw := swaggerDocWithReferences.Spec() - - expandedSwaggerDoc, err := loads.Spec(filePath) - if err != nil { - return nil, fmt.Errorf("loading swagger file %q: %+v", filePath, err) - } - expandedSwaggerDoc, err = findAndMergeLocalMixins(expandedSwaggerDoc, directory, fileName) - if err != nil { - return nil, fmt.Errorf("could not mixin remote swagger files referenced by %q: %+v", filePath, err) - } - - flattenedExpandedOpts := &analysis.FlattenOpts{ - Minimal: false, - Verbose: true, - Expand: false, - RemoveUnused: false, - //ContinueOnError: true, - - BasePath: expandedSwaggerDoc.SpecFilePath(), - Spec: analysis.New(expandedSwaggerDoc.Spec()), - } - if err := analysis.Flatten(*flattenedExpandedOpts); err != nil { - return nil, fmt.Errorf("flattening expanded swagger file %q: %+v", filePath, err) - } - - serviceName := strings.Title(strings.TrimSuffix(fileName, ".json")) - - swaggerSpecExpanded := expandedSwaggerDoc.Analyzer - swaggerSpecExpandedRaw := expandedSwaggerDoc.Spec() - - return &SwaggerDefinition{ - Name: serviceName, - swaggerSpecExpanded: swaggerSpecExpanded, - swaggerSpecWithReferences: swaggerSpecWithReferences, - swaggerSpecRaw: swaggerSpecWithReferencesRaw, - swaggerSpecExtendedRaw: swaggerSpecExpandedRaw, - }, nil -} - -func findAndMergeLocalMixins(input *loads.Document, basePath string, baseFile string) (*loads.Document, error) { - if len(strings.Split(baseFile, "/")) != 2 { // We only care about local files, not sub-folders - return input, nil - } - pathsToMixin := make([]string, 0) - if input.Analyzer != nil { - allRefs := input.Analyzer.AllRefs() - for _, v := range allRefs { - if path := v.Ref.GetURL(); path != nil && path.Path != "" && len(strings.Split(path.Path, "/")) == 2 { // Check if we have a reference in the CWD. - pathsToMixin = append(pathsToMixin, path.Path) - } - } - } - - if len(pathsToMixin) > 0 { - uniqueFilter := make(map[string]bool) - uniquePaths := make([]string, 0) - for _, path := range pathsToMixin { - if _, ok := uniqueFilter[path]; !ok { - uniqueFilter[path] = true - uniquePaths = append(uniquePaths, path) - } - } - mixins := make([]*spec.Swagger, 0) - for _, v := range uniquePaths { - doc, err := loads.Spec(fmt.Sprintf("%s/%s", basePath, v)) - if err != nil { - return nil, fmt.Errorf("could not load remote ref %q for mixin in %q: %+v", v, baseFile, err) - } - mixins = append(mixins, doc.Spec()) - } - _ = analysis.Mixin(input.Spec(), mixins...) - } - return input, nil -} diff --git a/tools/importer-rest-api-specs/components/parser/helpers.go b/tools/importer-rest-api-specs/components/parser/helpers.go deleted file mode 100644 index 403a546fc09..00000000000 --- a/tools/importer-rest-api-specs/components/parser/helpers.go +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package parser - -import ( - "fmt" - "strings" - - "github.com/go-openapi/spec" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/components/parser/cleanup" -) - -func fragmentNameFromReference(input spec.Ref) *string { - fragmentName := input.String() - return fragmentNameFromString(fragmentName) -} - -func fragmentNameFromString(fragmentName string) *string { - if fragmentName != "" { - fragments := strings.Split(fragmentName, "/") // format #/definitions/ConfigurationStoreListResult - referenceName := fragments[len(fragments)-1] - return &referenceName - } - - return nil -} - -func inlinedModelName(parentModelName, fieldName string) string { - // intentionally split out for consistency - val := fmt.Sprintf("%s%s", strings.Title(parentModelName), strings.Title(fieldName)) - return cleanup.NormalizeName(val) -} - -func operationMatchesTag(operation *spec.Operation, tag *string) bool { - // if there's no tags defined, we should capture it when the tag matched - if tag == nil { - return len(operation.Tags) == 0 - } - - for _, thisTag := range operation.Tags { - if strings.EqualFold(thisTag, *tag) { - return true - } - } - - return false -} diff --git a/tools/importer-rest-api-specs/components/parser/load_and_parse.go b/tools/importer-rest-api-specs/components/parser/load_and_parse.go deleted file mode 100644 index e3eb97e942f..00000000000 --- a/tools/importer-rest-api-specs/components/parser/load_and_parse.go +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package parser - -import ( - "fmt" - "strings" - - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/components/parser/dataworkarounds" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/components/parser/resourceids" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/logging" - importerModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/models" -) - -func LoadAndParseFiles(directory string, fileNames []string, serviceName, apiVersion string, resourceProvider *string) (*importerModels.AzureApiDefinition, error) { - // Some Services have been deprecated or should otherwise be ignored - check before proceeding - if serviceShouldBeIgnored(serviceName) { - logging.Debugf("Service %q should be ignored - skipping", serviceName) - return &importerModels.AzureApiDefinition{}, nil - } - - // First go through and parse all of the Resource ID's across all of the files - // this means that the names which are generated are unique across the Service - // which means these won't conflict and ultimately enables #44 (aliasing) in - // the future. - resourceIdResult := &resourceids.ParseResult{} - var file2Swagger = make(map[string]*SwaggerDefinition, len(fileNames)) - for _, file := range fileNames { - swaggerFile, err := load(directory, file) - if err != nil { - return nil, fmt.Errorf("parsing file %q: %+v", file, err) - } - file2Swagger[file] = swaggerFile - - parsedResourceIds, err := swaggerFile.ParseResourceIds(resourceProvider) - if err != nil { - return nil, fmt.Errorf("parsing Resource Ids from %q (Service %q / Api Version %q): %+v", file, serviceName, apiVersion, err) - } - if err := resourceIdResult.Append(*parsedResourceIds); err != nil { - return nil, fmt.Errorf("appending Resource Ids: %+v", err) - } - } - - parsed := make(map[string]importerModels.AzureApiDefinition, 0) - for _, file := range fileNames { - swaggerFile := file2Swagger[file] - - definition, err := swaggerFile.parse(serviceName, apiVersion, resourceProvider, *resourceIdResult) - if err != nil { - return nil, fmt.Errorf("parsing definition: %+v", err) - } - - data := importerModels.AzureApiDefinition{ - ServiceName: definition.ServiceName, - ApiVersion: definition.ApiVersion, - Resources: definition.Resources, - } - key := keyForAzureApiDefinition(data) - - if existing, ok := parsed[key]; ok { - // it's possible for Swagger tags to exist in multiple files, as EventHubs has DeleteAuthorizationRule which - // lives in the AuthorizationRule json, but is technically part of the EventHubs namespace - as such we need - // to combine the items rather than overwriting the key - resources, err := combineResourcesWith(data, existing.Resources) - if err != nil { - return nil, fmt.Errorf("combining resources for %q: %+v", key, err) - } - data.Resources = *resources - } - - parsed[key] = data - } - - out := make([]importerModels.AzureApiDefinition, 0) - for _, v := range parsed { - // the Data API expects that an API Version will contain at least 1 Resource - avoid bad data here - if len(v.Resources) == 0 { - logging.Infof("Service %q / Api Version %q contains no resources, skipping.", v.ServiceName, v.ApiVersion) - continue - } - - out = append(out, v) - } - - logging.Tracef("Applying overrides to workaround invalid Swagger Definitions..") - output, err := dataworkarounds.ApplyWorkarounds(out) - if err != nil { - return nil, fmt.Errorf("applying Swagger overrides: %+v", err) - } - - out = *output - - for _, service := range out { - service.Resources = removeUnusedItems(service.Resources) - } - - if len(out) > 1 { - return nil, fmt.Errorf("internal-error:the Swagger files for Service %q / API Version %q contained multiple resources (%d total)", serviceName, apiVersion, len(out)) - } - - if len(out) == 1 { - return &out[0], nil - } - - return nil, nil -} - -func serviceShouldBeIgnored(name string) bool { - servicesToIgnore := []string{ - "ADHybridHealthService", // TODO: is this EOL? Contains a Constant of an empty string: https://github.com/Azure/azure-rest-api-specs/blob/3eaa729b3686f20817145e771a8ab707c739dbbd/specification/adhybridhealthservice/resource-manager/Microsoft.ADHybridHealthService/stable/2014-01-01/ADHybridHealthService.json#L460-L471 - "Blockchain", // EOL - https://github.com/Azure-Samples/blockchain/blob/1b712d6d05cca8da17bdd1894de8c3d25905685d/abs/migration-guide.md - "DevSpaces", // EOL - https://azure.microsoft.com/en-us/updates/azure-dev-spaces-is-retiring-on-31-october-2023/ - "DynamicsTelemetry", // Fake RP - https://github.com/Azure/azure-rest-api-specs/pull/5161#issuecomment-486705231 - "IoTSpaces", // EOL - https://github.com/Azure/azure-rest-api-specs/pull/13993 - "ServiceFabricMesh", // EOL - https://azure.microsoft.com/en-us/updates/azure-service-fabric-mesh-preview-retirement/ - } - for _, v := range servicesToIgnore { - if strings.EqualFold(name, v) { - return true - } - } - return false -} - -func keyForAzureApiDefinition(input importerModels.AzureApiDefinition) string { - return fmt.Sprintf("%s-%s", input.ServiceName, input.ApiVersion) -} diff --git a/tools/importer-rest-api-specs/components/parser/models.go b/tools/importer-rest-api-specs/components/parser/models.go deleted file mode 100644 index 56ff76269e0..00000000000 --- a/tools/importer-rest-api-specs/components/parser/models.go +++ /dev/null @@ -1,888 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package parser - -import ( - "fmt" - "strings" - - "github.com/go-openapi/spec" - "github.com/hashicorp/go-azure-helpers/lang/pointer" - sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/components/parser/cleanup" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/components/parser/constants" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/components/parser/internal" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/featureflags" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/logging" -) - -func (d *SwaggerDefinition) parseModel(name string, input spec.Schema) (*internal.ParseResult, error) { - result := internal.ParseResult{ - Constants: map[string]sdkModels.SDKConstant{}, - Models: map[string]sdkModels.SDKModel{}, - } - - // 1. find any constants used within this model - nestedResult, err := d.findConstantsWithinModel(name, nil, input, result) - if err != nil { - return nil, fmt.Errorf("finding constants within model: %+v", err) - } - if err := result.Append(*nestedResult); err != nil { - return nil, fmt.Errorf("appending nestedResult from constants: %+v", err) - } - - // 2. iterate over the fields and find all of the fields for this model - fields, nestedResult, err := d.fieldsForModel(name, input, result) - if err != nil { - return nil, fmt.Errorf("finding fields for model: %+v", err) - } - if err := result.Append(*nestedResult); err != nil { - return nil, fmt.Errorf("appending nestedResult from fields: %+v", err) - } - - // if it's just got constants, we can skip it - if len(*fields) == 0 { - return &result, nil - } - - // 3. finally build this model directly - // Notably, we **DO NOT** load models used by this models here - this is handled once we - // know all the models which we want to load - to avoid infinite loops - model, err := d.modelDetailsFromObject(name, input, *fields) - if err != nil { - return nil, fmt.Errorf("populating model details for %q: %+v", name, err) - } - result.Models[name] = *model - - return &result, nil -} - -func (d *SwaggerDefinition) findConstantsWithinModel(fieldName string, modelName *string, input spec.Schema, known internal.ParseResult) (*internal.ParseResult, error) { - // NOTE: both Models and Fields are passed in here - result := internal.ParseResult{ - Constants: map[string]sdkModels.SDKConstant{}, - Models: map[string]sdkModels.SDKModel{}, - } - result.Append(known) - - if len(input.Enum) > 0 { - constant, err := constants.MapConstant(input.Type, fieldName, modelName, input.Enum, input.Extensions) - if err != nil { - return nil, fmt.Errorf("parsing constant: %+v", err) - } - result.Constants[constant.Name] = constant.Details - } - - // Check any object that this model inherits from - if len(input.AllOf) > 0 { - for _, parent := range input.AllOf { - fragmentName := fragmentNameFromReference(parent.Ref) - if fragmentName == nil { - continue - } - - // have we already obtained this model, if so skip it - if _, alreadyParsedModel := result.Models[*fragmentName]; alreadyParsedModel { - continue - } - - topLevelModel, err := d.findTopLevelObject(*fragmentName) - if err != nil { - return nil, fmt.Errorf("finding top level model %q for constants: %+v", *fragmentName, err) - } - - nestedResult, err := d.findConstantsWithinModel(*fragmentName, &fieldName, *topLevelModel, result) - if err != nil { - return nil, fmt.Errorf("finding constants within parent model %q: %+v", *fragmentName, err) - } - - if err := result.Append(*nestedResult); err != nil { - return nil, fmt.Errorf("appending nestedResult: %+v", err) - } - } - } - - for propName, propVal := range input.Properties { - logging.Tracef("Processing Property %q..", propName) - // models can contain nested models - either can contain constants, so around we go.. - nestedResult, err := d.findConstantsWithinModel(propName, &fieldName, propVal, result) - if err != nil { - return nil, fmt.Errorf("finding nested constants within %q: %+v", propName, err) - } - if err := result.Append(*nestedResult); err != nil { - return nil, fmt.Errorf("appending nestedResult: %+v", err) - } - } - - if input.AdditionalProperties != nil && input.AdditionalProperties.Schema != nil { - for propName, propVal := range input.AdditionalProperties.Schema.Properties { - logging.Tracef("Processing Additional Property %q..", propName) - // models can contain nested models - either can contain constants, so around we go.. - nestedConstants, err := d.findConstantsWithinModel(propName, &fieldName, propVal, result) - if err != nil { - return nil, fmt.Errorf("finding nested constants within %q: %+v", propName, err) - } - - if err := result.Append(*nestedConstants); err != nil { - return nil, fmt.Errorf("appending nestedResult: %+v", err) - } - } - } - - return &result, nil -} - -func (d *SwaggerDefinition) detailsForField(modelName string, propertyName string, value spec.Schema, isRequired bool, known internal.ParseResult) (*sdkModels.SDKField, *internal.ParseResult, error) { - logging.Tracef("Parsing details for field %q in %q..", propertyName, modelName) - - result := internal.ParseResult{ - Constants: map[string]sdkModels.SDKConstant{}, - Models: map[string]sdkModels.SDKModel{}, - } - result.Append(known) - - field := sdkModels.SDKField{ - Required: isRequired, - Optional: !isRequired, //TODO: re-enable readonly && !value.ReadOnly, - ReadOnly: false, // TODO: re-enable readonly value.ReadOnly, - Sensitive: false, // todo: this probably needs to be a predefined list, unless there's something we can parse - JsonName: propertyName, - //Description: value.Description, // TODO: currently causes flapping diff in api definitions, see https://github.com/hashicorp/pandora/issues/3325 - } - - // first get the object definition - parsingModel := false - objectDefinition, nestedResult, err := d.parseObjectDefinition(modelName, propertyName, &value, result, parsingModel) - if err != nil { - return nil, nil, fmt.Errorf("parsing object definition: %+v", err) - } - if nestedResult != nil { - result.Append(*nestedResult) - } - - // TODO: support for other date formats (RFC3339Nano etc) - // https://github.com/hashicorp/pandora/issues/8 - if objectDefinition.Type == sdkModels.DateTimeSDKObjectDefinitionType { - field.DateFormat = pointer.To(sdkModels.RFC3339SDKDateFormat) - } - - // if there are more than 1 allOf, it can not use a simple reference type, but a new definition - if len(value.Properties) > 0 || len(value.AllOf) > 1 { - // there's a nested model we need to pull out - inlinedName := inlinedModelName(modelName, propertyName) - nestedFields := make(map[string]sdkModels.SDKField, 0) - for propName, propVal := range value.Properties { - nestedFieldRequired := false - for _, field := range value.Required { - if strings.EqualFold(field, propName) { - nestedFieldRequired = true - break - } - } - nestedField, nestedResult, err := d.detailsForField(inlinedName, propName, propVal, nestedFieldRequired, result) - if err != nil { - return nil, nil, fmt.Errorf("parsing inlined model %q: %+v", inlinedName, err) - } - if err := result.Append(*nestedResult); err != nil { - return nil, nil, fmt.Errorf("appending nestedResult: %+v", err) - } - nestedFields[propName] = *nestedField - } - for _, inlinedModel := range value.AllOf { - remoteRef := fragmentNameFromReference(inlinedModel.Ref) - if remoteRef == nil { - // it's possible for the AllOf to just be a description (or contain a Type) - continue - } - - remoteSpec, err := d.findTopLevelObject(*remoteRef) - if err != nil { - return nil, nil, fmt.Errorf("could not find allOf referenced model %q", *remoteRef) - } - - for propName, propVal := range remoteSpec.Properties { - nestedFieldRequired := false - for _, field := range value.Required { - if strings.EqualFold(field, propName) { - nestedFieldRequired = true - break - } - } - nestedField, nestedResult, err := d.detailsForField(inlinedName, propName, propVal, nestedFieldRequired, result) - if err != nil { - return nil, nil, fmt.Errorf("parsing inlined model %q: %+v", inlinedName, err) - } - if err := result.Append(*nestedResult); err != nil { - return nil, nil, fmt.Errorf("appending nestedResult: %+v", err) - } - nestedFields[propName] = *nestedField - } - } - - inlinedModelDetails, err := d.modelDetailsFromObject(inlinedName, value, nestedFields) - if err != nil { - return nil, nil, fmt.Errorf("building model details for inlined model %q: %+v", inlinedName, err) - } - result.Models[inlinedName] = *inlinedModelDetails - // then swap out the reference - objectDefinition.Type = sdkModels.ReferenceSDKObjectDefinitionType - objectDefinition.ReferenceName = &inlinedName - } - - // Custom Types are determined once all the models/constants have been pulled out at the end - // so just assign this for now - field.ObjectDefinition = *objectDefinition - - return &field, &result, err -} - -func (d *SwaggerDefinition) fieldsForModel(modelName string, input spec.Schema, known internal.ParseResult) (*map[string]sdkModels.SDKField, *internal.ParseResult, error) { - fields := make(map[string]sdkModels.SDKField, 0) - result := internal.ParseResult{ - Constants: map[string]sdkModels.SDKConstant{}, - Models: map[string]sdkModels.SDKModel{}, - } - result.Append(known) - - requiredFields := make(map[string]struct{}, 0) - for _, k := range input.Required { - requiredFields[k] = struct{}{} - } - - // models can inherit from other models, so let's get all of the parent fields here - for i, parent := range input.AllOf { - fragmentName := fragmentNameFromReference(parent.Ref) - if fragmentName == nil { - // sometimes this is bad data rather than a reference, so it should be skipped, example: - // > "allOf": [ - // > { - // > "$ref": "#/definitions/AccessReviewDecisionIdentity" - // > }, - // > { - // > "type": "object", - // > "description": "AccessReviewDecisionUserIdentity" - // > } - // > ], - - // however sometimes these contain actual properties and should be parsed out: - // > "allOf": [ - // > { - // > "$ref": "#/definitions/DigitalTwinsEndpointResourceProperties" - // > }, - // > { - // > "type": "object", - // > "properties": { - // > "TopicEndpoint": { - // > "description": "EventGrid Topic Endpoint", - // > "type": "string" - // > }, - // > "accessKey1": { - // > "x-ms-secret": true, - // > "description": "EventGrid secondary accesskey. Will be obfuscated during read.", - // > "type": "string", - // > "x-nullable": true - // > }, - // > "accessKey2": { - // > "x-ms-secret": true, - // > "description": "EventGrid secondary accesskey. Will be obfuscated during read.", - // > "type": "string", - // > "x-nullable": true - // > } - // > } - // > } - // > ] - // > }, - - if parent.Type.Contains("object") { - innerModelName := modelName - if parent.Title != "" { - innerModelName = parent.Title - } - parsedParent, nestedResult, err := d.fieldsForModel(innerModelName, parent, known) - if err != nil { - return nil, nil, fmt.Errorf("parsing fields within allOf model %q (index %d): %+v", innerModelName, i, err) - } - if nestedResult != nil { - if err := result.Append(*nestedResult); err != nil { - return nil, nil, fmt.Errorf("appending nestedResult: %+v", err) - } - } - if parsedParent != nil { - for k, v := range *parsedParent { - fields[k] = v - } - } - } - - continue - } - - topLevelObject, err := d.findTopLevelObject(*fragmentName) - if err != nil { - return nil, nil, fmt.Errorf("parsing top level object %q: %+v", *fragmentName, err) - } - for _, k := range topLevelObject.Required { - requiredFields[k] = struct{}{} - } - - nestedFields, nestedResult, err := d.fieldsForModel(*fragmentName, *topLevelObject, result) - if err != nil { - return nil, nil, fmt.Errorf("finding fields for parent model %q: %+v", *fragmentName, err) - } - for k, v := range *nestedFields { - isRequired := isFieldRequired(k, requiredFields) - v.Required = isRequired - fields[k] = v - } - if nestedResult != nil { - result.Append(*nestedResult) - } - } - - // then we get the simple thing of iterating over these fields - for propName, propVal := range input.Properties { - isRequired := isFieldRequired(propName, requiredFields) - field, nestedResult, err := d.detailsForField(modelName, propName, propVal, isRequired, result) - if err != nil { - return nil, nil, fmt.Errorf("mapping field %q for %q: %+v", propName, modelName, err) - } - if nestedResult != nil { - if err := result.Append(*nestedResult); err != nil { - return nil, nil, fmt.Errorf("appending nestedResult: %+v", err) - } - } - - // whilst we could look to normalize the name we're intentionally not doing so here - fields[propName] = *field - } - - return &fields, &result, nil -} - -func (d *SwaggerDefinition) findTopLevelObject(name string) (*spec.Schema, error) { - for modelName, model := range d.swaggerSpecRaw.Definitions { - if strings.EqualFold(modelName, name) { - return &model, nil - } - } - - for modelName, model := range d.swaggerSpecExtendedRaw.Definitions { - if strings.EqualFold(modelName, name) { - return &model, nil - } - } - - return nil, fmt.Errorf("the top level object %q was not found", name) -} - -func (d *SwaggerDefinition) modelDetailsFromObject(modelName string, input spec.Schema, fields map[string]sdkModels.SDKField) (*sdkModels.SDKModel, error) { - details := sdkModels.SDKModel{ - Fields: fields, - } - - // if this is a Parent - if input.Discriminator != "" { - details.FieldNameContainingDiscriminatedValue = &input.Discriminator - - // check that there's at least one implementation of this type - otherwise this this isn't a discriminated type - // but bad data we should ignore - implementations, err := d.findModelNamesWhichImplement(modelName) - if err != nil { - return nil, fmt.Errorf("finding models which implement %q: %+v", modelName, err) - } - hasAtLeastOneImplementation := len(*implementations) > 0 - if !hasAtLeastOneImplementation { - details.FieldNameContainingDiscriminatedValue = nil - } - } - - // this would be an Implementation - if v, ok := input.Extensions.GetString("x-ms-discriminator-value"); ok { - details.DiscriminatedValue = &v - - // so we need to find the ancestor details - parentTypeName, discriminator, err := d.findAncestorType(input) - if err != nil { - return nil, fmt.Errorf("finding ancestor type for %q: %+v", modelName, err) - } - if parentTypeName != nil && discriminator != nil { - details.ParentTypeName = parentTypeName - details.FieldNameContainingDiscriminatedValue = discriminator - } - - // however if there's a Discriminator value defined but no parent type - this is bad data - so we should ignore it - if details.ParentTypeName == nil || details.FieldNameContainingDiscriminatedValue == nil { - details.DiscriminatedValue = nil - } - } - - return &details, nil -} - -func (d *SwaggerDefinition) findAncestorType(input spec.Schema) (*string, *string, error) { - for _, parentRaw := range input.AllOf { - parentFragmentName := fragmentNameFromReference(parentRaw.Ref) - if parentFragmentName == nil { - continue - } - - parent, err := d.findTopLevelObject(*parentFragmentName) - if err != nil { - return nil, nil, fmt.Errorf("finding top level object %q: %+v", *parentFragmentName, err) - } - - if parent.Discriminator != "" { - return parentFragmentName, &parent.Discriminator, nil - } - - // does the parent itself inherit from something? - if len(parent.AllOf) == 0 { - continue - } - - parentTypeName, discriminator, err := d.findAncestorType(*parent) - if err != nil { - return nil, nil, fmt.Errorf("finding ancestor type for %q: %+v", *parentFragmentName, err) - } - if parentTypeName != nil && discriminator != nil { - return parentTypeName, discriminator, nil - } - } - return nil, nil, nil -} - -func (d *SwaggerDefinition) findOrphanedDiscriminatedModels(serviceName string) (*internal.ParseResult, error) { - result := internal.ParseResult{ - Constants: map[string]sdkModels.SDKConstant{}, - Models: map[string]sdkModels.SDKModel{}, - } - - for modelName, definition := range d.swaggerSpecRaw.Definitions { - if _, ok := definition.Extensions.GetString("x-ms-discriminator-value"); ok { - details, err := d.parseModel(modelName, definition) - if err != nil { - return nil, fmt.Errorf("parsing model details for model %q: %+v", modelName, err) - } - if err := result.Append(*details); err != nil { - return nil, fmt.Errorf("appending model %q: %+v", modelName, err) - } - } - - // intentionally scoped to `datafactory` given the peculiarities in the swagger definition - // in particular question 4. in this issue https://github.com/Azure/azure-rest-api-specs/issues/28380 - if strings.EqualFold(serviceName, "datafactory") { - // this catches orphaned discriminated models where the discriminator information is housed in the parent - // and uses the name of the model as the discriminated value - if _, ok := definition.Extensions.GetString("x-ms-discriminator-value"); !ok && len(definition.AllOf) > 0 { - parentType, discriminator, err := d.findAncestorType(definition) - if err != nil { - return nil, fmt.Errorf("determining ancestor type for model %q: %+v", modelName, err) - } - - details, err := d.parseModel(modelName, definition) - if err != nil { - return nil, fmt.Errorf("parsing model details for model %q: %+v", modelName, err) - } - if parentType != nil && discriminator != nil { - model := details.Models[modelName] - model.ParentTypeName = parentType - model.FieldNameContainingDiscriminatedValue = discriminator - model.DiscriminatedValue = pointer.To(modelName) - details.Models[modelName] = model - } - if err := result.Append(*details); err != nil { - return nil, fmt.Errorf("appending model %q: %+v", modelName, err) - } - } - } - } - - // this will also pull out the parent model in the file which will already have been parsed, but that's ok - // since they will be de-duplicated when we call combineResourcesWith - nestedResult, err := d.findNestedItemsYetToBeParsed(map[string]sdkModels.SDKOperation{}, result) - if err != nil { - return nil, fmt.Errorf("finding nested items yet to be parsed: %+v", err) - } - if err := result.Append(*nestedResult); err != nil { - return nil, fmt.Errorf("appending nestedResult from Models used by existing Items: %+v", err) - } - - return &result, nil -} - -// if `inputForModel` is false, it means the `input` schema cannot be used to parse the model of `modelName` -func (d SwaggerDefinition) parseObjectDefinition( - modelName, propertyName string, - input *spec.Schema, - known internal.ParseResult, - parsingModel bool, -) (*sdkModels.SDKObjectDefinition, *internal.ParseResult, error) { - // find the object and any models and constants etc we can find - // however _don't_ look for discriminator implementations - since that should be done when we're completely done - result := internal.ParseResult{ - Constants: map[string]sdkModels.SDKConstant{}, - Models: map[string]sdkModels.SDKModel{}, - } - result.Append(known) - - // if it's an enum then parse that out - if len(input.Enum) > 0 { - constant, err := constants.MapConstant(input.Type, propertyName, &modelName, input.Enum, input.Extensions) - if err != nil { - return nil, nil, fmt.Errorf("parsing constant: %+v", err) - } - result.Constants[constant.Name] = constant.Details - - definition := sdkModels.SDKObjectDefinition{ - Type: sdkModels.ReferenceSDKObjectDefinitionType, - ReferenceName: &constant.Name, - } - - //TODO: re-enable min/max/unique - //if input.MaxItems != nil { - // v := int(*input.MaxItems) - // definition.Maximum = &v - //} - //if input.MinItems != nil { - // v := int(*input.MinItems) - // definition.Minimum = &v - //} - //v := input.UniqueItems - //definition.UniqueItems = &v - - return &definition, &result, nil - } - - // if it's a reference to a model, return that - if objectName := fragmentNameFromReference(input.Ref); objectName != nil { - // first find the top level object - topLevelObject, err := d.findTopLevelObject(*objectName) - if err != nil { - return nil, nil, fmt.Errorf("finding top level model %q: %+v", *objectName, err) - } - - knownIncludingPlaceholder := internal.ParseResult{ - Constants: map[string]sdkModels.SDKConstant{}, - Models: map[string]sdkModels.SDKModel{}, - } - - if err := knownIncludingPlaceholder.Append(result); err != nil { - return nil, nil, fmt.Errorf("appending nestedResult: %+v", err) - } - if *objectName != "" { - knownIncludingPlaceholder.Models[*objectName] = sdkModels.SDKModel{ - // add a placeholder to avoid circular references - } - } - - // then call ourselves to work out what to do with it - objectDefinition, nestedResult, err := d.parseObjectDefinition(*objectName, propertyName, topLevelObject, knownIncludingPlaceholder, true) - if err != nil { - return nil, nil, err - } - if nestedResult != nil && *objectName != "" { - delete(nestedResult.Models, *objectName) - } - return objectDefinition, nestedResult, nil - } - - // however we should only do this when we're parsing a model (`parsingModel`) directly rather than when parsing a model from a field - and only if we haven't already parsed this model - if len(input.Properties) > 0 || len(input.AllOf) > 0 { - // special-case: if the model has no properties and inherits from one model - // then just return that object instead, there's no point creating the wrapper type - if len(input.Properties) == 0 && len(input.AllOf) > 0 { - // `AllOf` can contain either a Reference, a model/constant or just a description. - // As such we need to filter out the description-only `AllOf`'s when determining whether the model - // should be replaced by the single type it's referencing. - allOfFields := make([]spec.Schema, 0) - for _, item := range input.AllOf { - fragmentName := fragmentNameFromReference(item.Ref) - if fragmentName == nil && len(item.Type) == 0 && len(item.Properties) == 0 { - continue - } - allOfFields = append(allOfFields, item) - } - if len(allOfFields) == 1 { - inheritedModel := allOfFields[0] - return d.parseObjectDefinition(inheritedModel.Title, propertyName, &inheritedModel, result, true) - } - } - - // check for / avoid circular references, - // however, we should only do this when we're parsing a model (`parsingModel`) directly rather than when parsing a model from a field - and only if we haven't already parsed this model - if _, ok := result.Models[modelName]; !ok && parsingModel { - nestedResult, err := d.parseModel(modelName, *input) - if err != nil { - return nil, nil, fmt.Errorf("parsing object from inlined model %q: %+v", modelName, err) - } - if nestedResult == nil { - return nil, nil, fmt.Errorf("parsing object from inlined response model %q: no model returned", modelName) - } - if err := result.Append(*nestedResult); err != nil { - return nil, nil, fmt.Errorf("appending nestedResult: %+v", err) - } - } - - definition := sdkModels.SDKObjectDefinition{ - Type: sdkModels.ReferenceSDKObjectDefinitionType, - ReferenceName: &modelName, - } - // TODO: re-enable min/max/unique - //if input.MaxItems != nil { - // v := int(*input.MaxItems) - // definition.Maximum = &v - //} - //if input.MinItems != nil { - // v := int(*input.MinItems) - // definition.Minimum = &v - //} - //v := input.UniqueItems - //definition.UniqueItems = &v - return &definition, &result, nil - } - - if input.AdditionalProperties != nil && input.AdditionalProperties.Schema != nil { - // it'll be a Dictionary, so pull out the nested item and return that - // however we need a name for this model - innerModelName := fmt.Sprintf("%sProperties", propertyName) - if input.AdditionalProperties.Schema.Title != "" { - innerModelName = input.AdditionalProperties.Schema.Title - } - - nestedItem, nestedResult, err := d.parseObjectDefinition(innerModelName, propertyName, input.AdditionalProperties.Schema, result, true) - if err != nil { - return nil, nil, fmt.Errorf("parsing nested item for dictionary: %+v", err) - } - if nestedItem == nil { - return nil, nil, fmt.Errorf("parsing nested item for dictionary: no nested item returned") - } - if err := result.Append(*nestedResult); err != nil { - return nil, nil, fmt.Errorf("appending nestedResult: %+v", err) - } - return &sdkModels.SDKObjectDefinition{ - Type: sdkModels.DictionarySDKObjectDefinitionType, - NestedItem: nestedItem, - }, &result, nil - } - - if input.Type.Contains("array") && input.Items.Schema != nil { - inlinedName := input.Items.Schema.Title - if inlinedName == "" { - // generate one based on the info we have - inlinedName = fmt.Sprintf("%s%sInlined", cleanup.NormalizeName(modelName), cleanup.NormalizeName(propertyName)) - } - - nestedItem, nestedResult, err := d.parseObjectDefinition(inlinedName, propertyName, input.Items.Schema, result, true) - if err != nil { - return nil, nil, fmt.Errorf("parsing nested item for array: %+v", err) - } - if nestedItem == nil { - return nil, nil, fmt.Errorf("parsing nested item for array: no nested item returned") - } - - // TODO: re-enable min/max/unique - //if input.MaxItems != nil { - // v := int(*input.MaxItems) - // nestedItem.Maximum = &v - //} - //if input.MinItems != nil { - // v := int(*input.MinItems) - // nestedItem.Minimum = &v - //} - //v := input.UniqueItems - //nestedItem.UniqueItems = &v - - if err := result.Append(*nestedResult); err != nil { - return nil, nil, fmt.Errorf("appending nestedResult: %+v", err) - } - return &sdkModels.SDKObjectDefinition{ - Type: sdkModels.ListSDKObjectDefinitionType, - NestedItem: nestedItem, - }, &result, nil - } - - // Data Factory has a bunch of Custom Types, so we need to check for/handle those - dataFactoryObjectDefinition, parseResult, err := d.parseDataFactoryCustomTypes(input, known) - if err != nil { - return nil, nil, fmt.Errorf("parsing the Data Factory Object Definition: %+v", err) - } - if dataFactoryObjectDefinition != nil { - if parseResult != nil { - if err := result.Append(*parseResult); err != nil { - return nil, nil, fmt.Errorf("appending parseResult: %+v", err) - } - } - return dataFactoryObjectDefinition, &result, nil - } - - // if it's a simple type, there'll be no other objects - if nativeType := d.parseNativeType(input); nativeType != nil { - return nativeType, &result, nil - } - - return nil, nil, fmt.Errorf("unimplemented object definition") -} - -func (d SwaggerDefinition) parseDataFactoryCustomTypes(input *spec.Schema, known internal.ParseResult) (*sdkModels.SDKObjectDefinition, *internal.ParseResult, error) { - formatVal := "" - if input.Type.Contains("object") { - formatVal, _ = input.Extensions.GetString("x-ms-format") - } - if formatVal == "" { - return nil, nil, nil - } - - // DataFactory has a bunch of CustomTypes, which use `"type": "object" and "x-ms-format"` to describe the type - // as such we need to handle that here - // Simple Types - if strings.EqualFold(formatVal, "dfe-bool") { - return &sdkModels.SDKObjectDefinition{ - Type: sdkModels.BooleanSDKObjectDefinitionType, - }, nil, nil - } - if strings.EqualFold(formatVal, "dfe-double") { - return &sdkModels.SDKObjectDefinition{ - Type: sdkModels.FloatSDKObjectDefinitionType, - }, nil, nil - } - if strings.EqualFold(formatVal, "dfe-key-value-pairs") { - return &sdkModels.SDKObjectDefinition{ - Type: sdkModels.DictionarySDKObjectDefinitionType, - NestedItem: &sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - }, nil, nil - } - if strings.EqualFold(formatVal, "dfe-int") { - return &sdkModels.SDKObjectDefinition{ - Type: sdkModels.IntegerSDKObjectDefinitionType, - }, nil, nil - } - if strings.EqualFold(formatVal, "dfe-string") { - return &sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, nil, nil - } - - // DataFactory has some specific reimplementations of List too.. - if strings.EqualFold(formatVal, "dfe-list-generic") && featureflags.ParseDataFactoryListsOfReferencesAsRegularObjectDefinitionTypes { - // NOTE: it's also possible to have - elementType, ok := input.Extensions.GetString("x-ms-format-element-type") - if !ok { - return nil, nil, fmt.Errorf("when `x-ms-format` is set to `dfe-list-generic` a `x-ms-format-element-type` must be set - but was not found") - } - referencedModel, err := d.findTopLevelObject(elementType) - if err != nil { - return nil, nil, fmt.Errorf("finding the top-level-object %q: %+v", elementType, err) - } - parseResult, err := d.parseModel(elementType, *referencedModel) - if err != nil { - return nil, nil, fmt.Errorf("parsing the Model %q: %+v", elementType, err) - } - if err := known.Append(*parseResult); err != nil { - return nil, nil, fmt.Errorf("appending `parseResult`: %+v", err) - } - return &sdkModels.SDKObjectDefinition{ - Type: sdkModels.ListSDKObjectDefinitionType, - NestedItem: &sdkModels.SDKObjectDefinition{ - Type: sdkModels.ReferenceSDKObjectDefinitionType, - ReferenceName: pointer.To(elementType), - }, - }, &known, nil - } - - if strings.EqualFold(formatVal, "dfe-list-string") { - return &sdkModels.SDKObjectDefinition{ - Type: sdkModels.ListSDKObjectDefinitionType, - NestedItem: &sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - }, nil, nil - } - - // otherwise let this fall through, since the "least bad" thing to do here is to mark this as an object - - return &sdkModels.SDKObjectDefinition{ - Type: sdkModels.RawObjectSDKObjectDefinitionType, - }, nil, nil -} - -func (d SwaggerDefinition) parseNativeType(input *spec.Schema) *sdkModels.SDKObjectDefinition { - if input == nil { - return nil - } - - if input.Type.Contains("bool") || input.Type.Contains("boolean") { - return &sdkModels.SDKObjectDefinition{ - Type: sdkModels.BooleanSDKObjectDefinitionType, - } - } - - if input.Type.Contains("file") { - return &sdkModels.SDKObjectDefinition{ - Type: sdkModels.RawFileSDKObjectDefinitionType, - } - } - - if input.Type.Contains("integer") { - return &sdkModels.SDKObjectDefinition{ - Type: sdkModels.IntegerSDKObjectDefinitionType, - } - } - - if input.Type.Contains("number") { - return &sdkModels.SDKObjectDefinition{ - Type: sdkModels.FloatSDKObjectDefinitionType, - } - } - - if input.Type.Contains("object") { - if strings.EqualFold(input.Format, "file") { - return &sdkModels.SDKObjectDefinition{ - Type: sdkModels.RawFileSDKObjectDefinitionType, - } - } - - return &sdkModels.SDKObjectDefinition{ - Type: sdkModels.RawObjectSDKObjectDefinitionType, - } - } - - // NOTE: whilst a DateTime _should_ always be Type: String with a Format of DateTime - bad data means - // that this could have no Type value but a Format value, so we have to check this separately. - if strings.EqualFold(input.Format, "date-time") { - // TODO: handle there being a custom format - for now we assume these are all using RFC3339 (#8) - return &sdkModels.SDKObjectDefinition{ - Type: sdkModels.DateTimeSDKObjectDefinitionType, - } - } - - if input.Type.Contains("string") { - // TODO: handle the `format` of `arm-id` (#1289) - return &sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - } - } - - // whilst all fields _should_ have a Type field, it's not guaranteed that they do - // NOTE: this is _intentionally_ not part of the Object comparison above - if len(input.Type) == 0 { - return &sdkModels.SDKObjectDefinition{ - Type: sdkModels.RawObjectSDKObjectDefinitionType, - } - } - - return nil -} - -func isFieldRequired(name string, required map[string]struct{}) bool { - for k := range required { - // assume data inconsistencies - if strings.EqualFold(k, name) { - return true - } - } - - return false -} diff --git a/tools/importer-rest-api-specs/components/parser/operations.go b/tools/importer-rest-api-specs/components/parser/operations.go deleted file mode 100644 index 22d37f31a11..00000000000 --- a/tools/importer-rest-api-specs/components/parser/operations.go +++ /dev/null @@ -1,581 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package parser - -import ( - "fmt" - "net/http" - "sort" - "strings" - - "github.com/go-openapi/spec" - sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/components/parser/cleanup" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/components/parser/constants" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/components/parser/internal" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/components/parser/resourceids" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/logging" -) - -type operationsParser struct { - operations []parsedOperation - operationIdsToParsedOperations map[string]resourceids.ParsedOperation - swaggerDefinition *SwaggerDefinition -} - -func (d *SwaggerDefinition) parseOperationsWithinTag(tag *string, operationIdsToParsedOperations map[string]resourceids.ParsedOperation, resourceProvider *string, found internal.ParseResult) (*map[string]sdkModels.SDKOperation, *internal.ParseResult, error) { - operations := make(map[string]sdkModels.SDKOperation, 0) - result := internal.ParseResult{ - Constants: map[string]sdkModels.SDKConstant{}, - Models: map[string]sdkModels.SDKModel{}, - } - result.Append(found) - - parser := operationsParser{ - operationIdsToParsedOperations: operationIdsToParsedOperations, - swaggerDefinition: d, - } - - // first find the operations then pull out everything we can - operationsForThisTag := d.findOperationsMatchingTag(tag) - for _, operation := range *operationsForThisTag { - logging.Debugf("Operation - %s %q..", operation.httpMethod, operation.uri) - - if internal.OperationShouldBeIgnored(operation.uri) { - logging.Debugf("Operation should be ignored - skipping..") - continue - } - - op, nestedResult, err := parser.parseOperation(operation, resourceProvider) - if err != nil { - return nil, nil, fmt.Errorf("parsing %s operation %q: %+v", operation.httpMethod, operation.uri, err) - } - if nestedResult != nil { - if err := result.Append(*nestedResult); err != nil { - return nil, nil, fmt.Errorf("appending nestedResult: %+v", err) - } - } - - if existing, hasExisting := operations[operation.name]; hasExisting { - return nil, nil, fmt.Errorf("conflicting operations with the Name %q - first %q - second %q", operation.name, existing.Method, op.Method) - } - - if op == nil { - continue - } - operations[operation.name] = *op - } - - return &operations, &result, nil -} - -func (p operationsParser) parseOperation(operation parsedOperation, resourceProvider *string) (*sdkModels.SDKOperation, *internal.ParseResult, error) { - result := internal.ParseResult{ - Constants: map[string]sdkModels.SDKConstant{}, - Models: map[string]sdkModels.SDKModel{}, - } - - contentType := p.determineContentType(operation) - expectedStatusCodes := p.expectedStatusCodesForOperation(operation) - paginationField := p.fieldContainingPaginationDetailsForOperation(operation) - requestObject, nestedResult, err := p.requestObjectForOperation(operation, result) - if err != nil { - return nil, nil, fmt.Errorf("determining request operation for %q (method %q / ID %q): %+v", operation.name, operation.httpMethod, operation.operation.ID, err) - } - if nestedResult != nil { - if err := result.Append(*nestedResult); err != nil { - return nil, nil, fmt.Errorf("appending nestedResult: %+v", err) - } - } - responseResult, nestedResult, err := p.responseObjectForOperation(operation, result) - if err != nil { - return nil, nil, fmt.Errorf("determining response operation for %q (method %q / ID %q): %+v", operation.name, operation.httpMethod, operation.operation.ID, err) - } - if nestedResult != nil { - if err := result.Append(*nestedResult); err != nil { - return nil, nil, fmt.Errorf("appending nestedResult: %+v", err) - } - } - if paginationField == nil && responseResult.paginationFieldName != nil { - paginationField = responseResult.paginationFieldName - } - longRunning := p.operationIsLongRunning(operation) - - options, nestedResult, err := p.optionsForOperation(operation) - if err != nil { - return nil, nil, fmt.Errorf("building options for operation %q: %+v", operation.name, err) - } - if nestedResult != nil { - if err := result.Append(*nestedResult); err != nil { - return nil, nil, fmt.Errorf("appending nestedResult: %+v", err) - } - } - - resourceId := p.operationIdsToParsedOperations[operation.operation.ID] - usesADifferentResourceProvider, err := resourceIdUsesAResourceProviderOtherThan(resourceId.ResourceId, resourceProvider) - if err != nil { - return nil, nil, err - } - if usesADifferentResourceProvider != nil && *usesADifferentResourceProvider { - return nil, nil, nil - } - - operationData := sdkModels.SDKOperation{ - ContentType: contentType, - ExpectedStatusCodes: expectedStatusCodes, - FieldContainingPaginationDetails: paginationField, - LongRunning: longRunning, - Method: strings.ToUpper(operation.httpMethod), - Options: *options, - RequestObject: requestObject, - ResourceIDName: resourceId.ResourceIdName, - ResponseObject: responseResult.objectDefinition, - URISuffix: resourceId.UriSuffix, - } - - if p.operationShouldBeIgnored(operationData) { - return nil, nil, nil - } - - return &operationData, &result, nil -} - -func (p operationsParser) determineObjectDefinitionForOption(input spec.Parameter) (*sdkModels.SDKOperationOptionObjectDefinition, error) { - if strings.EqualFold(input.Type, "array") { - // https://github.com/Azure/azure-rest-api-specs/blob/1b0ed8edd58bb7c9ade9a27430759527bd4eec8e/specification/trafficmanager/resource-manager/Microsoft.Network/stable/2018-03-01/trafficmanager.json#L735-L738 - if input.Items == nil { - return nil, fmt.Errorf("an array/csv option type was specified with no items") - } - - innerType, err := p.determineObjectDefinitionForOptionRaw(input.Items.Type, input.Items.CollectionFormat, input.Items.Format) - if err != nil { - return nil, fmt.Errorf("determining nested object definition for option: %+v", err) - } - - if strings.EqualFold(input.CollectionFormat, "csv") { - return &sdkModels.SDKOperationOptionObjectDefinition{ - Type: sdkModels.CSVSDKOperationOptionObjectDefinitionType, - NestedItem: innerType, - }, nil - } - - return &sdkModels.SDKOperationOptionObjectDefinition{ - Type: sdkModels.ListSDKOperationOptionObjectDefinitionType, - NestedItem: innerType, - }, nil - } - - return p.determineObjectDefinitionForOptionRaw(input.Type, input.CollectionFormat, input.Format) -} - -func (p operationsParser) determineObjectDefinitionForOptionRaw(paramType string, collectionFormat string, format string) (*sdkModels.SDKOperationOptionObjectDefinition, error) { - switch strings.ToLower(paramType) { - case "array": - { - if strings.EqualFold(collectionFormat, "csv") { - return nil, fmt.Errorf("cannot contain a csv") - } - - return nil, fmt.Errorf("cannot contain an array") - } - - case "boolean": - return &sdkModels.SDKOperationOptionObjectDefinition{ - Type: sdkModels.BooleanSDKOperationOptionObjectDefinitionType, - }, nil - - case "integer": - return &sdkModels.SDKOperationOptionObjectDefinition{ - Type: sdkModels.IntegerSDKOperationOptionObjectDefinitionType, - }, nil - - case "number": - { - if strings.EqualFold(format, "double") { - return &sdkModels.SDKOperationOptionObjectDefinition{ - Type: sdkModels.FloatSDKOperationOptionObjectDefinitionType, - }, nil - } - - if strings.EqualFold(format, "decimal") { - return &sdkModels.SDKOperationOptionObjectDefinition{ - Type: sdkModels.FloatSDKOperationOptionObjectDefinitionType, - }, nil - } - - if format != "" { - // we don't know what this is, better to raise an error and handle it than make - // it an integer if it should be a float or something - return nil, fmt.Errorf("unsupported format type for number %q", format) - } - - return &sdkModels.SDKOperationOptionObjectDefinition{ - Type: sdkModels.IntegerSDKOperationOptionObjectDefinitionType, - }, nil - } - - case "string": - return &sdkModels.SDKOperationOptionObjectDefinition{ - Type: sdkModels.StringSDKOperationOptionObjectDefinitionType, - }, nil - } - return nil, fmt.Errorf("unsupported field type %q", paramType) -} - -func (p operationsParser) determineContentType(operation parsedOperation) string { - contentType := "application/json" - - if strings.EqualFold(operation.httpMethod, "HEAD") || strings.EqualFold(operation.httpMethod, "GET") { - if len(operation.operation.Produces) > 0 { - contentType = operation.operation.Produces[0] - } - } else { - if len(operation.operation.Consumes) > 0 { - contentType = operation.operation.Consumes[0] - } - } - - return contentType -} - -func (p operationsParser) expectedStatusCodesForOperation(input parsedOperation) []int { - statusCodes := make([]int, 0) - - for statusCode, resp := range input.operation.Responses.StatusCodeResponses { - // sanity checking - if p.operationIsASuccess(statusCode, resp) { - statusCodes = append(statusCodes, statusCode) - } - } - - if !usesNonDefaultStatusCodes(input, statusCodes) { - if p.operationIsLongRunning(input) { - if strings.EqualFold(input.httpMethod, "delete") { - statusCodes = []int{200, 202} - } - if strings.EqualFold(input.httpMethod, "post") { - statusCodes = []int{201, 202} - } - if strings.EqualFold(input.httpMethod, "put") { - statusCodes = []int{201, 202} - } - } - if p.isListOperation(input) { - if strings.EqualFold(input.httpMethod, "get") { - statusCodes = []int{200} - } - } - if strings.EqualFold(input.httpMethod, "delete") || strings.EqualFold(input.httpMethod, "get") || strings.EqualFold(input.httpMethod, "post") || strings.EqualFold(input.httpMethod, "head") { - statusCodes = []int{200} - } - if strings.EqualFold(input.httpMethod, "put") || strings.EqualFold(input.httpMethod, "patch") { - statusCodes = []int{200, 201} - } - } - sort.Ints(statusCodes) - - return statusCodes -} - -func (p operationsParser) fieldContainingPaginationDetailsForOperation(input parsedOperation) *string { - if raw, ok := input.operation.VendorExtensible.Extensions["x-ms-pageable"]; ok { - val, ok := raw.(map[string]interface{}) - if ok { - for k, v := range val { - // this can be 'null' in the swagger - if v == nil { - continue - } - if strings.EqualFold("nextLinkName", k) { - str := v.(string) - return &str - } - } - } - } - - return nil -} - -func (p operationsParser) isListOperation(input parsedOperation) bool { - paginationField := p.fieldContainingPaginationDetailsForOperation(input) - if paginationField != nil { - return true - } - - // otherwise if we have a parameter of `$skiptoken` in the query, we assume that it is - for _, parameter := range input.operation.Parameters { - if strings.EqualFold(parameter.Name, "$skipToken") { - return true - } - } - - return false -} - -func (p operationsParser) operationIsLongRunning(input parsedOperation) bool { - // Some Swaggers have the following defined on an Operation: - // > "x-ms-long-running-operation": true, - // > "x-ms-long-running-operation-options": { - // > "final-state-via": "azure-async-operation" - // > } - // Whilst these _could_ be useful at some point we can likely ignore them for - // the moment since the convention is either `Location` or `Azure-AsyncOperation` - val, exists := input.operation.Extensions.GetBool("x-ms-long-running-operation") - if !exists { - return false - } - - return val -} - -func (p operationsParser) optionsForOperation(input parsedOperation) (*map[string]sdkModels.SDKOperationOption, *internal.ParseResult, error) { - output := make(map[string]sdkModels.SDKOperationOption) - result := internal.ParseResult{ - Constants: map[string]sdkModels.SDKConstant{}, - } - - for _, param := range input.operation.Parameters { - // these are (currently) handled elsewhere, so we're good for now - if strings.EqualFold(param.Name, "$skipToken") { - // NOTE: we may also need to do the odata ones, media has an example - continue - } - - // handled elsewhere - if strings.EqualFold(param.Name, "api-version") { - continue - } - - if strings.EqualFold(param.In, "header") || strings.EqualFold(param.In, "query") { - val := param.Name - name := cleanup.NormalizeName(val) - - option := sdkModels.SDKOperationOption{ - Required: param.Required, - } - - if strings.EqualFold(param.In, "header") { - option.HeaderName = &val - } - if strings.EqualFold(param.In, "query") { - option.QueryStringName = &val - } - - // looks like these can be dates etc too - // ./commerce/resource-manager/Microsoft.Commerce/preview/2015-06-01-preview/commerce.json- "name": "reportedEndTime", - // ./commerce/resource-manager/Microsoft.Commerce/preview/2015-06-01-preview/commerce.json- "in": "query", - // ./commerce/resource-manager/Microsoft.Commerce/preview/2015-06-01-preview/commerce.json- "required": true, - // ./commerce/resource-manager/Microsoft.Commerce/preview/2015-06-01-preview/commerce.json- "type": "string", - // ./commerce/resource-manager/Microsoft.Commerce/preview/2015-06-01-preview/commerce.json: "format": "date-time", - // ./commerce/resource-manager/Microsoft.Commerce/preview/2015-06-01-preview/commerce.json- "description": "The end of the time range to retrieve data for." - objectDefinition, err := p.determineObjectDefinitionForOption(param) - if err != nil { - return nil, nil, fmt.Errorf("determining field type for operation: %+v", err) - } - option.ObjectDefinition = *objectDefinition - - if param.Enum != nil { - types := []string{ - param.Type, - } - constant, err := constants.MapConstant(types, param.Name, nil, param.Enum, param.Extensions) - if err != nil { - return nil, nil, fmt.Errorf("mapping %q: %+v", param.Name, err) - } - result.Constants[constant.Name] = constant.Details - - option.ObjectDefinition = sdkModels.SDKOperationOptionObjectDefinition{ - Type: sdkModels.ReferenceSDKOperationOptionObjectDefinitionType, - ReferenceName: &constant.Name, - } - } - - output[name] = option - } - } - - return &output, &result, nil -} - -func (p operationsParser) operationShouldBeIgnored(input sdkModels.SDKOperation) bool { - // Some HTTP Operations don't make sense for us to expose at this time, for example - // a GET request which returns no content. They may at some point in the future but - // for now there's not much point - // - // Example: the 'GetSubscriptionOperationWithAsyncResponse' in Web, which returns the - // result of a LRO - but in our case that's handled elsewhere so we don't need it - if strings.EqualFold(input.Method, "GET") { - if len(input.ExpectedStatusCodes) == 1 && input.ExpectedStatusCodes[0] == http.StatusNoContent && input.ResponseObject == nil { - return true - } - } - - return false -} - -func (p operationsParser) requestObjectForOperation(input parsedOperation, known internal.ParseResult) (*sdkModels.SDKObjectDefinition, *internal.ParseResult, error) { - // all we should parse out is the top level object - nothing more. - - // find the same operation in the unexpanded swagger spec since we need the reference name - _, _, unexpandedOperation, found := p.swaggerDefinition.swaggerSpecWithReferences.OperationForName(input.operation.ID) - if !found { - return nil, nil, nil - } - - for _, param := range unexpandedOperation.Parameters { - if strings.EqualFold(param.In, "body") { - parsingModel := true - objectDefinition, result, err := p.swaggerDefinition.parseObjectDefinition(param.Schema.Title, param.Schema.Title, param.Schema, known, parsingModel) - if err != nil { - return nil, nil, fmt.Errorf("parsing request object for parameter %q: %+v", param.Name, err) - } - if objectDefinition != nil { - return objectDefinition, result, nil - } - } - } - - return nil, nil, nil -} - -type operationResponseObjectResult struct { - objectDefinition *sdkModels.SDKObjectDefinition - paginationFieldName *string -} - -func (p operationsParser) operationIsASuccess(statusCode int, resp spec.Response) bool { - // Status Codes with the extension `x-ms-error-response` reference an error response - // which should be ignored in our case - as errors will instead be pulled out via the - // base layer - isErrorValue, exists := resp.Extensions.GetBool("x-ms-error-response") - if exists && isErrorValue { - return false - } - - return statusCode >= 200 && statusCode < 300 -} - -func (p operationsParser) responseObjectForOperation(input parsedOperation, known internal.ParseResult) (*operationResponseObjectResult, *internal.ParseResult, error) { - output := operationResponseObjectResult{} - result := internal.ParseResult{ - Constants: map[string]sdkModels.SDKConstant{}, - Models: map[string]sdkModels.SDKModel{}, - } - result.Append(known) - - // find the same operation in the unexpanded swagger spec since we need the reference name - _, _, unexpandedOperation, found := p.swaggerDefinition.swaggerSpecWithReferences.OperationForName(input.operation.ID) - if !found { - return nil, nil, fmt.Errorf("couldn't find Operation ID %q in the unexpanded Swagger spec", input.operation.ID) - } - - // since it's possible for operations to have multiple status codes, parse out all the objects and then find the most applicable - statusCodes := make([]int, 0) - objectDefinitionsByStatusCode := map[int]sdkModels.SDKObjectDefinition{} - for statusCode, details := range unexpandedOperation.Responses.StatusCodeResponses { - if !p.operationIsASuccess(statusCode, details) { - continue - } - - if details.ResponseProps.Schema == nil { - continue - } - - parsingModel := true - objectDefinition, nestedResult, err := p.swaggerDefinition.parseObjectDefinition(details.ResponseProps.Schema.Title, details.ResponseProps.Schema.Title, details.ResponseProps.Schema, result, parsingModel) - if err != nil { - return nil, nil, fmt.Errorf("parsing response object from status code %d: %+v", statusCode, err) - } - - statusCodes = append(statusCodes, statusCode) - objectDefinitionsByStatusCode[statusCode] = *objectDefinition - result.Append(*nestedResult) - } - - sort.Ints(statusCodes) - // if there's multiple status codes, pick the first successful one (which should be a 200) - for _, statusCode := range statusCodes { - if statusCode < 200 || statusCode >= 300 { - continue - } - - object, ok := objectDefinitionsByStatusCode[statusCode] - if !ok { - return nil, nil, fmt.Errorf("internal-error: missing object definitions by status code for %d", statusCode) - } - output.objectDefinition = &object - break - } - // otherwise just take the first one - if len(statusCodes) > 0 && output.objectDefinition == nil { - statusCode := statusCodes[0] - object, ok := objectDefinitionsByStatusCode[statusCode] - if !ok { - return nil, nil, fmt.Errorf("internal-error: missing object definitions by status code for %d", statusCode) - } - output.objectDefinition = &object - } - - return &output, &result, nil -} - -type parsedOperation struct { - name string - uri string - httpMethod string - operation *spec.Operation -} - -func (d *SwaggerDefinition) findOperationsMatchingTag(tag *string) *[]parsedOperation { - result := make([]parsedOperation, 0) - for httpMethod, operation := range d.swaggerSpecExpanded.Operations() { - // operation = inferMissingTags(operation, tag) - for uri, operationDetails := range operation { - if !operationMatchesTag(operationDetails, tag) { - continue - } - - operationName := normalizeOperationName(operationDetails.ID, tag) - result = append(result, parsedOperation{ - name: operationName, - uri: uri, - httpMethod: httpMethod, - operation: operationDetails, - }) - } - } - - return &result -} - -func usesNonDefaultStatusCodes(input parsedOperation, statusCodes []int) bool { - defaultStatusCodes := map[string][]int{ - "get": {200}, - "post": {200, 201}, - "put": {200, 201}, - "delete": {200, 201}, - "patch": {200, 201}, - } - expected, ok := defaultStatusCodes[strings.ToLower(input.httpMethod)] - if !ok { - // potentially an unsupported use-case but fine for now - return true - } - - if len(expected) != len(statusCodes) { - return true - } - - sort.Ints(expected) - sort.Ints(statusCodes) - for i, ev := range expected { - av := statusCodes[i] - if ev != av { - return true - } - } - - return false -} diff --git a/tools/importer-rest-api-specs/components/parser/parse_data.go b/tools/importer-rest-api-specs/components/parser/parse_data.go deleted file mode 100644 index 993fc2bf8a1..00000000000 --- a/tools/importer-rest-api-specs/components/parser/parse_data.go +++ /dev/null @@ -1,181 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package parser - -import ( - "fmt" - - "github.com/hashicorp/pandora/tools/data-api-sdk/v1/helpers" - sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/components/parser/resourceids" - importerModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/models" -) - -func combineResourcesWith(first importerModels.AzureApiDefinition, other map[string]sdkModels.APIResource) (*map[string]sdkModels.APIResource, error) { - resources := make(map[string]sdkModels.APIResource) - for k, v := range first.Resources { - resources[k] = v - } - - for k, v := range other { - existing, ok := resources[k] - if !ok { - resources[k] = v - continue - } - - constants, err := combineConstants(existing.Constants, v.Constants) - if err != nil { - return nil, fmt.Errorf("combining constants: %+v", err) - } - existing.Constants = *constants - - models, err := combineModels(existing.Models, v.Models) - if err != nil { - return nil, fmt.Errorf("combining models: %+v", err) - } - existing.Models = *models - - operations, err := combineOperations(existing.Operations, v.Operations) - if err != nil { - return nil, fmt.Errorf("combining operations: %+v", err) - } - existing.Operations = *operations - - resourceIds, err := combineResourceIds(existing.ResourceIDs, v.ResourceIDs) - if err != nil { - return nil, fmt.Errorf("combining resource ids: %+v", err) - } - existing.ResourceIDs = *resourceIds - - resources[k] = existing - } - - return &resources, nil -} - -func combineConstants(first, second map[string]sdkModels.SDKConstant) (*map[string]sdkModels.SDKConstant, error) { - constants := make(map[string]sdkModels.SDKConstant) - for k, v := range first { - constants[k] = v - } - - for k, v := range second { - existingConst, ok := constants[k] - if !ok { - constants[k] = v - continue - } - - if existingConst.Type != v.Type { - return nil, fmt.Errorf("combining constant %q - multiple field types defined as %q and %q", k, string(existingConst.Type), string(v.Type)) - } - - vals, err := combineMaps(existingConst.Values, v.Values) - if err != nil { - return nil, fmt.Errorf("combining maps: %+v", err) - } - existingConst.Values = *vals - constants[k] = existingConst - } - - return &constants, nil -} - -func combineModels(first map[string]sdkModels.SDKModel, second map[string]sdkModels.SDKModel) (*map[string]sdkModels.SDKModel, error) { - output := make(map[string]sdkModels.SDKModel) - - for k, v := range first { - output[k] = v - } - - for k, v := range second { - existing, ok := output[k] - if ok && len(existing.Fields) != len(v.Fields) { - return nil, fmt.Errorf("duplicate models named %q with different fields - first %d - second %d", k, len(existing.Fields), len(v.Fields)) - } - output[k] = v - } - - // once we've combined the models for a resource and de-duplicated them, we will iterate over all of them to link any - // orphaned discriminated models to their parent - for k, v := range output { - // this model is an implementation, so we need to find/update the parent - if v.ParentTypeName != nil && v.FieldNameContainingDiscriminatedValue != nil && v.DiscriminatedValue != nil { - parent, ok := output[*v.ParentTypeName] - if !ok { - return nil, fmt.Errorf("no parent definition %q found for implementation %q", *v.ParentTypeName, k) - } - // discriminated models that are defined in a separate file with no reference to a path/tag/resource ID - // will not be found when we parse the parent, as a result the parent's TypeHintIn is set to nil, - // here we set the information back into the parent after we've found the implementation - if parent.FieldNameContainingDiscriminatedValue == nil { - parent.FieldNameContainingDiscriminatedValue = v.FieldNameContainingDiscriminatedValue - } - output[*v.ParentTypeName] = parent - } - } - - return &output, nil -} - -func combineOperations(first map[string]sdkModels.SDKOperation, second map[string]sdkModels.SDKOperation) (*map[string]sdkModels.SDKOperation, error) { - output := make(map[string]sdkModels.SDKOperation, 0) - - for k, v := range first { - output[k] = v - } - - for k, v := range second { - // if there's duplicate operations named the same thing in different Swaggers, this is likely a data issue - _, ok := output[k] - if ok { - return nil, fmt.Errorf("duplicate operations named %q", k) - } - - output[k] = v - } - - return &output, nil -} - -func combineResourceIds(first map[string]sdkModels.ResourceID, second map[string]sdkModels.ResourceID) (*map[string]sdkModels.ResourceID, error) { - output := make(map[string]sdkModels.ResourceID) - - for k, v := range first { - output[k] = v - } - - for k, v := range second { - // if there's duplicate Resource ID's named the same thing in different Swaggers, this is likely a data issue - otherVal, ok := output[k] - if ok && !resourceids.ResourceIdsMatch(v, otherVal) { - return nil, fmt.Errorf("duplicate Resource ID named %q (First %q / Second %q)", k, helpers.DisplayValueForResourceID(v), helpers.DisplayValueForResourceID(otherVal)) - } - - output[k] = v - } - - return &output, nil -} - -func combineMaps(first map[string]string, second map[string]string) (*map[string]string, error) { - vals := make(map[string]string, 0) - for k, v := range first { - vals[k] = v - } - for k, v := range second { - existing, ok := vals[k] - if !ok { - vals[k] = v - continue - } - - if existing != v { - return nil, fmt.Errorf("duplicate key %q with different value - first %q - second %q", k, existing, v) - } - } - - return &vals, nil -} diff --git a/tools/importer-rest-api-specs/components/parser/parser.go b/tools/importer-rest-api-specs/components/parser/parser.go deleted file mode 100644 index b818e709957..00000000000 --- a/tools/importer-rest-api-specs/components/parser/parser.go +++ /dev/null @@ -1,221 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package parser - -import ( - "fmt" - "strings" - - "github.com/hashicorp/go-azure-helpers/lang/pointer" - "github.com/hashicorp/pandora/tools/data-api-sdk/v1/helpers" - sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/components/parser/cleanup" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/components/parser/resourceids" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/logging" - importerModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/models" -) - -func (d *SwaggerDefinition) parse(serviceName, apiVersion string, resourceProvider *string, resourceIds resourceids.ParseResult) (*importerModels.AzureApiDefinition, error) { - resources := make(map[string]sdkModels.APIResource, 0) - - tags := d.findTags() - // first we assume everything has a tag - for _, tag := range tags { - if tagShouldBeIgnored(tag) { - continue - } - - resource, err := d.parseResourcesWithinSwaggerTag(&tag, resourceProvider, resourceIds) - if err != nil { - return nil, fmt.Errorf("finding resources for tag %q: %+v", tag, err) - } - - if resource != nil { - logging.Tracef("The Tag %q has %d API Operations", tag, len(resource.Operations)) - normalizedTag := normalizeTag(tag) - normalizedTag = cleanup.NormalizeResourceName(normalizedTag) - resources[normalizedTag] = *resource - } - } - - // The swagger for PrivateLinkService in Network associates the CreateOrUpdate method with the tag - // `PrivateLinkService` instead of `PrivateLinkServices` like the rest of the operations do. This consolidates - // the two resources that Pandora identifies into one. - // Can be removed when https://github.com/Azure/azure-rest-api-specs/pull/29303 has been merged. - if strings.EqualFold(serviceName, "network") && strings.Contains(d.Name, "PrivateLinkService") { - privateLinkService, ok := resources["PrivateLinkService"] - if !ok { - return nil, fmt.Errorf("resource `PrivateLinkService` was not found") - } - privateLinkServices, ok := resources["PrivateLinkServices"] - if !ok { - return nil, fmt.Errorf("resource `PrivateLinkServices` was not found") - } - - resources["PrivateLinkServices"] = importerModels.MergeResourcesForTag(privateLinkServices, privateLinkService) - delete(resources, "PrivateLinkService") - } - - // however some things don't, so we then need to iterate over any without them - if _, shouldIgnore := tagsToIgnore[strings.ToLower(serviceName)]; !shouldIgnore { - resource, err := d.parseResourcesWithinSwaggerTag(nil, resourceProvider, resourceIds) - if err != nil { - return nil, fmt.Errorf("finding resources for tag %q: %+v", serviceName, err) - } - - // Since we're dealing with missing tag data in the swagger, we'll assume the proper tag name here is the file name - // This is less than ideal, but _should_ be fine. - inferredTag := cleanup.PluraliseName(d.Name) - - if resource != nil { - normalizedTag := normalizeTag(inferredTag) - normalizedTag = cleanup.NormalizeResourceName(normalizedTag) - - if mergeResources, ok := resources[normalizedTag]; ok { - resources[normalizedTag] = importerModels.MergeResourcesForTag(mergeResources, *resource) - } else { - resources[normalizedTag] = *resource - } - } - } - - // now that we have a canonical list of resources, can we simplify the Operation names at all? - resourcesOut := make(map[string]sdkModels.APIResource) - for resourceName, resource := range resources { - logging.Tracef("Simplifying operation names for resource %q", resourceName) - updated := d.simplifyOperationNamesForResource(resource, resourceName) - resourcesOut[resourceName] = updated - } - - // discriminator implementations that are defined in separate files with no link to a swagger tag - // are not parsed. So far there are two known instances of this (Data Factory, Chaos Studio) where the - // files are defined in a nested directory e.g. d.Name = /Types/Capabilities - swaggerFileName := strings.Split(d.Name, "/") - if len(resources) == 0 && len(swaggerFileName) > 2 { - // if we're here then there is no tag in this file, so we'll use the file name - inferredTag := cleanup.PluraliseName(swaggerFileName[len(swaggerFileName)-1]) - normalizedTag := cleanup.NormalizeResourceName(inferredTag) - - result, err := d.findOrphanedDiscriminatedModels(serviceName) - if err != nil { - return nil, fmt.Errorf("finding orphaned discriminated models in %q: %+v", d.Name, err) - } - - // this is to avoid the creation of empty packages/directories in the api definitions - if len(result.Models) > 0 || len(result.Constants) > 0 { - resource := sdkModels.APIResource{ - Constants: result.Constants, - Models: result.Models, - } - resource = normalizeAzureApiResource(resource) - - if mergeResources, ok := resources[normalizedTag]; ok { - resources[normalizedTag] = importerModels.MergeResourcesForTag(mergeResources, resource) - } else { - resourcesOut[normalizedTag] = resource - } - } - } - - return &importerModels.AzureApiDefinition{ - ServiceName: cleanup.NormalizeServiceName(serviceName), - ApiVersion: apiVersion, - Resources: resourcesOut, - }, nil -} - -func (d *SwaggerDefinition) simplifyOperationNamesForResource(resource sdkModels.APIResource, resourceName string) sdkModels.APIResource { - allOperationsStartWithPrefix := true - resourceNameLower := strings.ToLower(resourceName) - for operationName := range resource.Operations { - operationNameLowered := strings.ToLower(operationName) - if !strings.HasPrefix(operationNameLowered, resourceNameLower) || strings.EqualFold(operationNameLowered, resourceNameLower) { - allOperationsStartWithPrefix = false - break - } - } - - if !allOperationsStartWithPrefix { - logging.Tracef("Skipping simplifying operation names for resource %q", resourceName) - return resource - } - - output := make(map[string]sdkModels.SDKOperation) - for key, value := range resource.Operations { - updatedKey := key[len(resourceNameLower):] - // Trim off any spurious `s` at the start. This happens when the Swagger Tag and the Operation ID - // use different pluralizations (e.g. one is Singular and the other is Plural) - // - // Whilst it's possible this could happen for other suffixes (e.g. `ies`, or `y`) - // the Data only shows `s` at this point in time, so this is sufficient for now: - // https://github.com/hashicorp/pandora/pull/3016#pullrequestreview-1612987765 - // - // Any other examples will generate successfully but be unusable in the Go SDK since these - // will be treated as unexported methods - and can be addressed then. - if strings.HasPrefix(updatedKey, "s") { - updatedKey = updatedKey[1:] - } - - logging.Tracef("Simplifying Operation %q to %q", key, updatedKey) - output[updatedKey] = value - } - - resource.Operations = output - return resource -} - -func (d *SwaggerDefinition) ParseResourceIds(resourceProvider *string) (*resourceids.ParseResult, error) { - parser := resourceids.NewParser(d.swaggerSpecExpanded) - - resourceIds, err := parser.Parse() - if err != nil { - return nil, fmt.Errorf("finding Resource IDs: %+v", err) - } - - return resourceIds, nil -} - -func (d *SwaggerDefinition) filterResourceIdsToResourceProvider(input resourceids.ParseResult, resourceProvider string) (*resourceids.ParseResult, error) { - output := resourceids.ParseResult{ - OperationIdsToParsedResourceIds: input.OperationIdsToParsedResourceIds, - NamesToResourceIDs: map[string]sdkModels.ResourceID{}, - Constants: input.Constants, - } - - for name := range input.NamesToResourceIDs { - value := input.NamesToResourceIDs[name] - - logging.Tracef("Processing ID %q (%q)", name, helpers.DisplayValueForResourceID(value)) - usesADifferentResourceProvider, err := resourceIdUsesAResourceProviderOtherThan(pointer.To(value), pointer.To(resourceProvider)) - if err != nil { - return nil, err - } - - if !*usesADifferentResourceProvider { - output.NamesToResourceIDs[name] = value - } - } - - return &output, nil -} - -func resourceIdUsesAResourceProviderOtherThan(input *sdkModels.ResourceID, resourceProvider *string) (*bool, error) { - if input == nil || resourceProvider == nil { - return pointer.To(false), nil - } - - for i, segment := range input.Segments { - if segment.Type != sdkModels.ResourceProviderResourceIDSegmentType { - continue - } - - if segment.FixedValue == nil { - return nil, fmt.Errorf("the Resource ID %q Segment %d was a ResourceProviderSegment with no FixedValue", helpers.DisplayValueForResourceID(*input), i) - } - if !strings.EqualFold(*segment.FixedValue, *resourceProvider) { - return pointer.To(true), nil - } - } - return pointer.To(false), nil -} diff --git a/tools/importer-rest-api-specs/components/parser/parser_test.go b/tools/importer-rest-api-specs/components/parser/parser_test.go deleted file mode 100644 index 9c21c4c2a26..00000000000 --- a/tools/importer-rest-api-specs/components/parser/parser_test.go +++ /dev/null @@ -1,191 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package parser - -import ( - "context" - "fmt" - "log" - "os" - "strings" - "testing" - "time" - - "github.com/hashicorp/go-hclog" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/components/discovery" -) - -const swaggerDirectory = "../../../submodules/rest-api-specs/specification" -const runAllEnvVar = "ALL" - -func TestAllSwaggersUsingParser(t *testing.T) { - if os.Getenv(runAllEnvVar) == "" { - t.Skipf("skipping since %q is unset", runAllEnvVar) - } - // works around the OAIGen bug - os.Setenv("OAIGEN_DEDUPE", "false") - - services, err := discovery.FindResourceManagerServices(swaggerDirectory, hclog.New(hclog.DefaultOptions)) - if err != nil { - t.Fatal(err) - } - - for _, service := range *services { - for apiVersion, versionPaths := range service.ApiVersionPaths { - for _, versionPath := range versionPaths { - serviceType := "resource-manager" - if strings.Contains(versionPath, "data-plane") { - serviceType = "data-plane" - } - - t.Run(fmt.Sprintf("%s/%s/%s", service.Name, serviceType, apiVersion), func(t *testing.T) { - t.Parallel() - - // safety - _, done := context.WithTimeout(context.TODO(), 90*time.Second) - defer done() - log.Printf("[DEBUG] Validating %q at %q..", service.Name, versionPath) - err := validateDirectory(service.Name, apiVersion, versionPath) - if err != nil { - t.Fatal(err) - } - }) - } - } - } -} - -func TestAllSwaggersValidateAllContainTypes(t *testing.T) { - if os.Getenv(runAllEnvVar) == "" { - t.Skipf("skipping since %q is unset", runAllEnvVar) - } - // works around the OAIGen bug - os.Setenv("OAIGEN_DEDUPE", "false") - - services, err := discovery.FindResourceManagerServices(swaggerDirectory, hclog.New(hclog.DefaultOptions)) - if err != nil { - t.Fatal(err) - } - - for _, service := range *services { - for apiVersion, versionPaths := range service.ApiVersionPaths { - for _, versionPath := range versionPaths { - serviceType := "resource-manager" - if strings.Contains(versionPath, "data-plane") { - serviceType = "data-plane" - } - - t.Run(fmt.Sprintf("%s/%s/%s", service.Name, serviceType, apiVersion), func(t *testing.T) { - t.Parallel() - - // safety - _, done := context.WithTimeout(context.TODO(), 90*time.Second) - defer done() - log.Printf("[DEBUG] Validating %q at %q..", service.Name, versionPath) - err := validateDirectory(service.Name, apiVersion, versionPath) - if err != nil { - if strings.HasSuffix(err.Error(), "is missing a type") { - t.Fatal(err) - } - } - }) - } - } - } -} - -func TestAllSwaggersValidateFindOAIGenParserBug(t *testing.T) { - if os.Getenv(runAllEnvVar) == "" { - t.Skipf("skipping since %q is unset", runAllEnvVar) - } - // works around the OAIGen bug - os.Setenv("OAIGEN_DEDUPE", "false") - - services, err := discovery.FindResourceManagerServices(swaggerDirectory, hclog.New(hclog.DefaultOptions)) - if err != nil { - t.Fatal(err) - } - - for _, service := range *services { - for apiVersion, versionPaths := range service.ApiVersionPaths { - for _, versionPath := range versionPaths { - serviceType := "resource-manager" - if strings.Contains(versionPath, "data-plane") { - serviceType = "data-plane" - } - - t.Run(fmt.Sprintf("%s/%s/%s", service.Name, serviceType, apiVersion), func(t *testing.T) { - // safety - _, done := context.WithTimeout(context.TODO(), 30*time.Second) - defer done() - log.Printf("[DEBUG] Validating %q at %q..", service.Name, versionPath) - err := validateDirectory(service.Name, apiVersion, versionPath) - if err != nil { - if strings.Contains(err.Error(), "OAIGen") { - t.Fatal(err) - } - } - }) - } - } - } -} - -func TestAllSwaggersValidateFindUnknownBugs(t *testing.T) { - if os.Getenv(runAllEnvVar) == "" { - t.Skipf("skipping since %q is unset", runAllEnvVar) - } - // works around the OAIGen bug - os.Setenv("OAIGEN_DEDUPE", "false") - - services, err := discovery.FindResourceManagerServices(swaggerDirectory, hclog.New(hclog.DefaultOptions)) - if err != nil { - t.Fatal(err) - } - - for _, service := range *services { - for apiVersion, versionPaths := range service.ApiVersionPaths { - for _, versionPath := range versionPaths { - serviceType := "resource-manager" - if strings.Contains(versionPath, "data-plane") { - serviceType = "data-plane" - } - - t.Run(fmt.Sprintf("%s/%s/%s", service.Name, serviceType, apiVersion), func(t *testing.T) { - // safety - _, done := context.WithTimeout(context.TODO(), 30*time.Second) - defer done() - log.Printf("[DEBUG] Validating %q at %q..", service.Name, versionPath) - err := validateDirectory(service.Name, apiVersion, versionPath) - if err != nil { - if !strings.Contains(err.Error(), "OAIGen") && - !strings.HasSuffix(err.Error(), "is missing a type") && - !strings.HasSuffix(err.Error(), "duplicate operation ID") { - t.Fatal(err) - } - } - }) - } - } - } -} - -func validateDirectory(serviceName, apiVersion, versionDirectory string) error { - swaggerFiles, err := discovery.SwaggerFilesInDirectory(versionDirectory) - if err != nil { - return fmt.Errorf("parsing swagger files in %q: %+v", versionDirectory, err) - } - - fileNames := make([]string, 0) - for _, file := range *swaggerFiles { - fileName := strings.TrimPrefix(file, versionDirectory) - fileNames = append(fileNames, fileName) - } - - if _, err := LoadAndParseFiles(versionDirectory, *swaggerFiles, serviceName, apiVersion, nil); err != nil { - return err - } - - return nil -} diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_management_group_test.go b/tools/importer-rest-api-specs/components/parser/resourceids/common_id_management_group_test.go deleted file mode 100644 index d2186c5c914..00000000000 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_management_group_test.go +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package resourceids - -import ( - "testing" - - sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" -) - -func TestCommonResourceID_ManagementGroup(t *testing.T) { - valid := sdkModels.ResourceID{ - ConstantNames: []string{}, - Segments: []sdkModels.ResourceIDSegment{ - sdkModels.NewStaticValueResourceIDSegment("providers", "providers"), - sdkModels.NewResourceProviderResourceIDSegment("resourceProvider", "Microsoft.Management"), - sdkModels.NewStaticValueResourceIDSegment("managementGroups", "managementGroups"), - sdkModels.NewUserSpecifiedResourceIDSegment("groupId", "groupId"), - }, - } - invalid := sdkModels.ResourceID{ - ConstantNames: []string{}, - Segments: []sdkModels.ResourceIDSegment{ - sdkModels.NewStaticValueResourceIDSegment("providers", "providers"), - sdkModels.NewResourceProviderResourceIDSegment("resourceProvider", "Microsoft.Management"), - sdkModels.NewStaticValueResourceIDSegment("managementGroups", "managementGroups"), - sdkModels.NewUserSpecifiedResourceIDSegment("groupId", "groupId"), - sdkModels.NewStaticValueResourceIDSegment("someResource", "someResource"), - sdkModels.NewUserSpecifiedResourceIDSegment("resourceName", "resourceName"), - }, - } - input := []sdkModels.ResourceID{ - valid, - invalid, - } - output := switchOutCommonResourceIDsAsNeeded(input) - for _, actual := range output { - if normalizedResourceId(actual.Segments) == normalizedResourceId(valid.Segments) { - if actual.CommonIDAlias == nil { - t.Fatalf("Expected `valid` to have the CommonIDAlias `ManagementGroup` but got nil") - } - if *actual.CommonIDAlias != "ManagementGroup" { - t.Fatalf("Expected `valid` to have the CommonIDAlias `ManagementGroup` but got %q", *actual.CommonIDAlias) - } - - continue - } - - if normalizedResourceId(actual.Segments) == normalizedResourceId(invalid.Segments) { - if actual.CommonIDAlias != nil { - t.Fatalf("Expected `invalid` to have no CommonIDAlias but got %q", *actual.CommonIDAlias) - } - continue - } - - t.Fatalf("unexpected Resource ID %q", normalizedResourceId(actual.Segments)) - } -} diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_resource_group_test.go b/tools/importer-rest-api-specs/components/parser/resourceids/common_id_resource_group_test.go deleted file mode 100644 index 2155cf7730a..00000000000 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_resource_group_test.go +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package resourceids - -import ( - "testing" - - sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" -) - -func TestCommonResourceID_ResourceGroup(t *testing.T) { - valid := sdkModels.ResourceID{ - ConstantNames: []string{}, - Segments: []sdkModels.ResourceIDSegment{ - sdkModels.NewStaticValueResourceIDSegment("subscriptions", "subscriptions"), - sdkModels.NewSubscriptionIDResourceIDSegment("subscriptionId"), - sdkModels.NewStaticValueResourceIDSegment("resourceGroups", "resourceGroups"), - sdkModels.NewResourceGroupNameResourceIDSegment("resourceGroupName"), - }, - } - invalid := sdkModels.ResourceID{ - ConstantNames: []string{}, - Segments: []sdkModels.ResourceIDSegment{ - sdkModels.NewStaticValueResourceIDSegment("subscriptions", "subscriptions"), - sdkModels.NewSubscriptionIDResourceIDSegment("subscriptionId"), - sdkModels.NewStaticValueResourceIDSegment("resourceGroups", "resourceGroups"), - sdkModels.NewResourceGroupNameResourceIDSegment("resourceGroupName"), - sdkModels.NewStaticValueResourceIDSegment("someResource", "someResource"), - sdkModels.NewUserSpecifiedResourceIDSegment("resourceName", "resourceName"), - }, - } - input := []sdkModels.ResourceID{ - valid, - invalid, - } - output := switchOutCommonResourceIDsAsNeeded(input) - for _, actual := range output { - if normalizedResourceId(actual.Segments) == normalizedResourceId(valid.Segments) { - if actual.CommonIDAlias == nil { - t.Fatalf("Expected `valid` to have the CommonIDAlias `ResourceGroup` but got nil") - } - if *actual.CommonIDAlias != "ResourceGroup" { - t.Fatalf("Expected `valid` to have the CommonIDAlias `ResourceGroup` but got %q", *actual.CommonIDAlias) - } - - continue - } - - if normalizedResourceId(actual.Segments) == normalizedResourceId(invalid.Segments) { - if actual.CommonIDAlias != nil { - t.Fatalf("Expected `invalid` to have no CommonIDAlias but got %q", *actual.CommonIDAlias) - } - continue - } - - t.Fatalf("unexpected Resource ID %q", normalizedResourceId(actual.Segments)) - } -} - -func TestCommonResourceID_ResourceGroupIncorrectSegment(t *testing.T) { - input := []sdkModels.ResourceID{ - { - ConstantNames: []string{}, - Segments: []sdkModels.ResourceIDSegment{ - sdkModels.NewStaticValueResourceIDSegment("subscriptions", "subscriptions"), - sdkModels.NewSubscriptionIDResourceIDSegment("subscriptionId"), - sdkModels.NewStaticValueResourceIDSegment("resourceGroups", "resourceGroups"), - sdkModels.NewResourceGroupNameResourceIDSegment("resourceGroupName"), - }, - }, - { - ConstantNames: []string{}, - Segments: []sdkModels.ResourceIDSegment{ - sdkModels.NewStaticValueResourceIDSegment("subscriptions", "subscriptions"), - sdkModels.NewSubscriptionIDResourceIDSegment("subscriptionId"), - sdkModels.NewStaticValueResourceIDSegment("resourceGroups", "resourceGroups"), - sdkModels.NewResourceGroupNameResourceIDSegment("sourceResourceGroupName"), - }, - }, - } - output := switchOutCommonResourceIDsAsNeeded(input) - for i, actual := range output { - t.Logf("testing %d", i) - if actual.CommonIDAlias == nil || *actual.CommonIDAlias != "ResourceGroup" { - t.Fatalf("expected item %d to be detected as a ResourceGroup but it wasn't", i) - } - } -} diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_scope_test.go b/tools/importer-rest-api-specs/components/parser/resourceids/common_id_scope_test.go deleted file mode 100644 index 621a22da48e..00000000000 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_scope_test.go +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package resourceids - -import ( - "testing" - - sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" -) - -func TestCommonResourceID_Scope(t *testing.T) { - valid := sdkModels.ResourceID{ - ConstantNames: []string{}, - Segments: []sdkModels.ResourceIDSegment{ - sdkModels.NewScopeResourceIDSegment("resourcePath"), - }, - } - invalid := sdkModels.ResourceID{ - ConstantNames: []string{}, - Segments: []sdkModels.ResourceIDSegment{ - sdkModels.NewScopeResourceIDSegment("scope"), - sdkModels.NewStaticValueResourceIDSegment("someResource", "someResource"), - sdkModels.NewUserSpecifiedResourceIDSegment("resourceName", "resourceName"), - }, - } - input := []sdkModels.ResourceID{ - valid, - invalid, - } - output := switchOutCommonResourceIDsAsNeeded(input) - for _, actual := range output { - if ResourceIdsMatch(actual, valid) { - if actual.CommonIDAlias == nil { - t.Fatalf("Expected `valid` to have the CommonIDAlias `Scope` but got nil") - } - if *actual.CommonIDAlias != "Scope" { - t.Fatalf("Expected `valid` to have the CommonIDAlias `Scope` but got %q", *actual.CommonIDAlias) - } - - continue - } - - if ResourceIdsMatch(actual, invalid) { - if actual.CommonIDAlias != nil { - t.Fatalf("Expected `invalid` to have no CommonIDAlias but got %q", *actual.CommonIDAlias) - } - continue - } - - t.Fatalf("unexpected Resource ID %q", normalizedResourceId(actual.Segments)) - } -} diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_subscription_test.go b/tools/importer-rest-api-specs/components/parser/resourceids/common_id_subscription_test.go deleted file mode 100644 index ac2a2f16802..00000000000 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_subscription_test.go +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package resourceids - -import ( - "testing" - - sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" -) - -func TestCommonResourceID_Subscription(t *testing.T) { - valid := sdkModels.ResourceID{ - ConstantNames: []string{}, - Segments: []sdkModels.ResourceIDSegment{ - sdkModels.NewStaticValueResourceIDSegment("subscriptions", "subscriptions"), - sdkModels.NewSubscriptionIDResourceIDSegment("subscriptionId"), - }, - } - invalid := sdkModels.ResourceID{ - ConstantNames: []string{}, - Segments: []sdkModels.ResourceIDSegment{ - sdkModels.NewStaticValueResourceIDSegment("subscriptions", "subscriptions"), - sdkModels.NewSubscriptionIDResourceIDSegment("subscriptionId"), - sdkModels.NewStaticValueResourceIDSegment("someResource", "someResource"), - sdkModels.NewUserSpecifiedResourceIDSegment("resourceName", "resourceName"), - }, - } - input := []sdkModels.ResourceID{ - valid, - invalid, - } - output := switchOutCommonResourceIDsAsNeeded(input) - for _, actual := range output { - if normalizedResourceId(actual.Segments) == normalizedResourceId(valid.Segments) { - if actual.CommonIDAlias == nil { - t.Fatalf("Expected `valid` to have the CommonIDAlias `Subscription` but got nil") - } - if *actual.CommonIDAlias != "Subscription" { - t.Fatalf("Expected `valid` to have the CommonIDAlias `Subscription` but got %q", *actual.CommonIDAlias) - } - - continue - } - - if normalizedResourceId(actual.Segments) == normalizedResourceId(invalid.Segments) { - if actual.CommonIDAlias != nil { - t.Fatalf("Expected `invalid` to have no CommonAlias but got %q", *actual.CommonIDAlias) - } - continue - } - - t.Fatalf("unexpected Resource ID %q", normalizedResourceId(actual.Segments)) - } -} diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_user_assigned_identity_test.go b/tools/importer-rest-api-specs/components/parser/resourceids/common_id_user_assigned_identity_test.go deleted file mode 100644 index 65812fbb5dd..00000000000 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_user_assigned_identity_test.go +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package resourceids - -import ( - "testing" - - sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" -) - -func TestCommonResourceID_UserAssignedIdentity(t *testing.T) { - valid := sdkModels.ResourceID{ - ConstantNames: []string{}, - Segments: []sdkModels.ResourceIDSegment{ - sdkModels.NewStaticValueResourceIDSegment("subscriptions", "subscriptions"), - sdkModels.NewSubscriptionIDResourceIDSegment("subscriptionId"), - sdkModels.NewStaticValueResourceIDSegment("resourceGroups", "resourceGroups"), - sdkModels.NewResourceGroupNameResourceIDSegment("resourceGroupName"), - sdkModels.NewStaticValueResourceIDSegment("providers", "providers"), - sdkModels.NewResourceProviderResourceIDSegment("resourceProvider", "Microsoft.ManagedIdentity"), - sdkModels.NewStaticValueResourceIDSegment("userAssignedIdentities", "userAssignedIdentities"), - sdkModels.NewUserSpecifiedResourceIDSegment("userAssignedIdentityName", "userAssignedIdentityName"), - }, - } - invalid := sdkModels.ResourceID{ - ConstantNames: []string{}, - Segments: []sdkModels.ResourceIDSegment{ - sdkModels.NewStaticValueResourceIDSegment("subscriptions", "subscriptions"), - sdkModels.NewSubscriptionIDResourceIDSegment("subscriptionId"), - sdkModels.NewStaticValueResourceIDSegment("resourceGroups", "resourceGroups"), - sdkModels.NewResourceGroupNameResourceIDSegment("resourceGroupName"), - sdkModels.NewStaticValueResourceIDSegment("providers", "providers"), - sdkModels.NewResourceProviderResourceIDSegment("resourceProvider", "Microsoft.ManagedIdentity"), - sdkModels.NewStaticValueResourceIDSegment("userAssignedIdentities", "userAssignedIdentities"), - sdkModels.NewUserSpecifiedResourceIDSegment("userAssignedIdentityName", "userAssignedIdentityName"), - sdkModels.NewStaticValueResourceIDSegment("someResource", "someResource"), - sdkModels.NewUserSpecifiedResourceIDSegment("resourceName", "resourceName"), - }, - } - input := []sdkModels.ResourceID{ - valid, - invalid, - } - output := switchOutCommonResourceIDsAsNeeded(input) - for _, actual := range output { - if normalizedResourceId(actual.Segments) == normalizedResourceId(valid.Segments) { - if actual.CommonIDAlias == nil { - t.Fatalf("Expected `valid` to have the CommonIDAlias `UserAssignedIdentity` but got nil") - } - if *actual.CommonIDAlias != "UserAssignedIdentity" { - t.Fatalf("Expected `valid` to have the CommonIDAlias `UserAssignedIdentity` but got %q", *actual.CommonIDAlias) - } - - continue - } - - if normalizedResourceId(actual.Segments) == normalizedResourceId(invalid.Segments) { - if actual.CommonIDAlias != nil { - t.Fatalf("Expected `invalid` to have no CommonIDAlias but got %q", *actual.CommonIDAlias) - } - continue - } - - t.Fatalf("unexpected Resource ID %q", normalizedResourceId(actual.Segments)) - } -} diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/helpers.go b/tools/importer-rest-api-specs/components/parser/resourceids/helpers.go deleted file mode 100644 index d1505f6c96e..00000000000 --- a/tools/importer-rest-api-specs/components/parser/resourceids/helpers.go +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package resourceids - -import ( - "fmt" - "strings" - - sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/components/parser/cleanup" -) - -func normalizedResourceManagerResourceId(pri sdkModels.ResourceID) string { - segments := segmentsWithoutUriSuffix(pri) - return normalizedResourceId(segments) -} - -func segmentsWithoutUriSuffix(pri sdkModels.ResourceID) []sdkModels.ResourceIDSegment { - segments := pri.Segments - lastUserValueSegment := -1 - for i, segment := range segments { - // everything else technically is a user configurable component - if segment.Type != sdkModels.StaticResourceIDSegmentType && segment.Type != sdkModels.ResourceProviderResourceIDSegmentType { - lastUserValueSegment = i - } - } - if lastUserValueSegment >= 0 && len(segments) > lastUserValueSegment+1 { - // remove any URI Suffix since this isn't relevant for the ID's - segments = segments[0 : lastUserValueSegment+1] - } - return segments -} - -func normalizedResourceId(segments []sdkModels.ResourceIDSegment) string { - components := make([]string, 0) - for _, segment := range segments { - switch segment.Type { - case sdkModels.ResourceProviderResourceIDSegmentType: - { - normalizedSegment := cleanup.NormalizeResourceProviderName(*segment.FixedValue) - components = append(components, normalizedSegment) - continue - } - - case sdkModels.StaticResourceIDSegmentType: - { - normalizedSegment := cleanup.NormalizeSegment(*segment.FixedValue, true) - components = append(components, normalizedSegment) - continue - } - - case sdkModels.ConstantResourceIDSegmentType, sdkModels.ResourceGroupResourceIDSegmentType, sdkModels.ScopeResourceIDSegmentType, sdkModels.SubscriptionIDResourceIDSegmentType, sdkModels.UserSpecifiedResourceIDSegmentType: - // e.g. {example} - normalizedSegment := segment.Name - normalizedSegment = cleanup.NormalizeReservedKeywords(segment.Name) - components = append(components, fmt.Sprintf("{%s}", normalizedSegment)) - continue - - default: - panic(fmt.Sprintf("unimplemented segment type %q", string(segment.Type))) - } - } - - return fmt.Sprintf("/%s", strings.Join(components, "/")) -} - -func ResourceIdsMatch(first, second sdkModels.ResourceID) bool { - if len(first.Segments) != len(second.Segments) { - return false - } - - for i, first := range first.Segments { - second := second.Segments[i] - if first.Type != second.Type { - return false - } - - // Whilst these should match, it's possible that they don't but are the same e.g. - // /subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Devices/provisioningServices/{resourceName} - // /subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Devices/provisioningServices/{provisioningServiceName} - // as such providing they're both user specified segments (and the rest is the same) then they're the same - if first.Type == sdkModels.ResourceGroupResourceIDSegmentType || first.Type == sdkModels.SubscriptionIDResourceIDSegmentType || first.Type == sdkModels.UserSpecifiedResourceIDSegmentType { - continue - } - - // With a Scope the key doesn't matter as much as that it's a Scope, so presuming the types match (above) we're good. - if first.Type == sdkModels.ScopeResourceIDSegmentType { - continue - } - - if first.Type == sdkModels.ConstantResourceIDSegmentType { - if first.ConstantReference != nil && second.ConstantReference == nil { - return false - } - if first.ConstantReference == nil && second.ConstantReference != nil { - return false - } - - // We're intentionally not checking the constants involved, since both the name and values could differ - // between different operations due to data issues - however when either happens we'd end up using a - // Common ID to resolve this - therefore presuming the rest of the Resource ID matches we should be good. - - continue - } - - if first.Type == sdkModels.ResourceProviderResourceIDSegmentType || first.Type == sdkModels.StaticResourceIDSegmentType { - if first.FixedValue != nil && second.FixedValue == nil { - return false - } - if first.FixedValue == nil && second.FixedValue != nil { - return false - } - if first.FixedValue != nil && second.FixedValue != nil && *first.FixedValue != *second.FixedValue { - return false - } - - continue - } - - if !strings.EqualFold(first.Name, second.Name) { - return false - } - } - - return true -} diff --git a/tools/importer-rest-api-specs/components/parser/swagger_resources.go b/tools/importer-rest-api-specs/components/parser/swagger_resources.go deleted file mode 100644 index b961bdda56e..00000000000 --- a/tools/importer-rest-api-specs/components/parser/swagger_resources.go +++ /dev/null @@ -1,449 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package parser - -import ( - "fmt" - "net/http" - "strings" - - "github.com/go-openapi/spec" - "github.com/hashicorp/go-azure-helpers/lang/pointer" - "github.com/hashicorp/pandora/tools/data-api-sdk/v1/helpers" - sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/components/parser/commonschema" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/components/parser/constants" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/components/parser/internal" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/components/parser/resourceids" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/logging" -) - -func (d *SwaggerDefinition) parseResourcesWithinSwaggerTag(tag *string, resourceProvider *string, resourceIds resourceids.ParseResult) (*sdkModels.APIResource, error) { - result := internal.ParseResult{ - Constants: map[string]sdkModels.SDKConstant{}, - Models: map[string]sdkModels.SDKModel{}, - } - - // note that Resource ID's can contain Constants (used as segments) - if err := result.AppendConstants(resourceIds.Constants); err != nil { - return nil, fmt.Errorf("appending nestedResult from Constants: %+v", err) - } - - // pull out the operations and any inlined/top-level constants/models - operations, nestedResult, err := d.parseOperationsWithinTag(tag, resourceIds.OperationIdsToParsedResourceIds, resourceProvider, result) - if err != nil { - return nil, fmt.Errorf("finding operations: %+v", err) - } - if err := result.Append(*nestedResult); err != nil { - return nil, fmt.Errorf("appending nestedResult from Operations: %+v", err) - } - - // pull out all of the remaining models based on what we've got - nestedResult, err = d.findNestedItemsYetToBeParsed(*operations, result) - if err != nil { - return nil, fmt.Errorf("finding nested items yet to be parsed: %+v", err) - } - if err := result.Append(*nestedResult); err != nil { - return nil, fmt.Errorf("appending nestedResult from Models used by existing Items: %+v", err) - } - - // then pull out the embedded model for List operations (e.g. we don't want the wrapper type but the type for the `value` field) - operations, err = pullOutModelForListOperations(*operations, result) - if err != nil { - return nil, fmt.Errorf("pulling out model from list operations: %+v", err) - } - - // then switch out any custom types (e.g. Identity) - result = switchOutCustomTypesAsNeeded(result) - - // if there's nothing here, there's no point generating a package - if len(*operations) == 0 { - return nil, nil - } - - resource := sdkModels.APIResource{ - Constants: result.Constants, - Models: result.Models, - Operations: *operations, - ResourceIDs: resourceIds.NamesToResourceIDs, - } - - // first Normalize the names, meaning `foo` -> `Foo` for consistency - resource = normalizeAzureApiResource(resource) - - return &resource, nil -} - -type listOperationDetails struct { - fieldContainingPaginationDetails *string - valueObjectDefinition *sdkModels.SDKObjectDefinition -} - -func listOperationDetailsForOperation(input sdkModels.SDKOperation, known internal.ParseResult) *listOperationDetails { - if !strings.EqualFold(input.Method, http.MethodGet) && !strings.EqualFold(input.Method, http.MethodPost) { - return nil - } - - // an operation without a response object isn't going to be listable - if input.ResponseObject == nil { - return nil - } - if input.ResponseObject.Type == sdkModels.ReferenceSDKObjectDefinitionType { - responseModel, isModel := known.Models[*input.ResponseObject.ReferenceName] - if !isModel { - // a constant wouldn't be listable - return nil - } - - out := listOperationDetails{} - if input.FieldContainingPaginationDetails != nil { - out.fieldContainingPaginationDetails = input.FieldContainingPaginationDetails - } - for fieldName, v := range responseModel.Fields { - if strings.EqualFold(fieldName, "nextLink") { - out.fieldContainingPaginationDetails = pointer.To(fieldName) - continue - } - - if strings.EqualFold(fieldName, "Value") { - // switch out the reference to be the SDKObjectDefinition for the `Value` field, rather than - // the wrapper type - definition := helpers.InnerMostSDKObjectDefinition(v.ObjectDefinition) - out.valueObjectDefinition = pointer.To(definition) - continue - } - } - if out.fieldContainingPaginationDetails != nil && out.valueObjectDefinition != nil { - return &out - } - } - - return nil -} - -func pullOutModelForListOperations(input map[string]sdkModels.SDKOperation, known internal.ParseResult) (*map[string]sdkModels.SDKOperation, error) { - // List Operations return an object which contains a NextLink and a Value (which is the actual Object - // being paginated on) - so we want to replace the wrapper object with the Value so that these can be - // paginated correctly as needed. - output := make(map[string]sdkModels.SDKOperation) - - for operationName := range input { - operation := input[operationName] - - // if the Response Object is a List Operation (identifiable via - listDetails := listOperationDetailsForOperation(operation, known) - if listDetails != nil { - operation.FieldContainingPaginationDetails = listDetails.fieldContainingPaginationDetails - operation.ResponseObject = listDetails.valueObjectDefinition - } - - output[operationName] = operation - } - - return &output, nil -} - -func switchOutCustomTypesAsNeeded(input internal.ParseResult) internal.ParseResult { - result := internal.ParseResult{ - Constants: map[string]sdkModels.SDKConstant{}, - Models: map[string]sdkModels.SDKModel{}, - } - result.Append(input) - - for modelName, model := range result.Models { - fields := model.Fields - for fieldName := range model.Fields { - field := model.Fields[fieldName] - - // switch out the Object Definition for this field if needed - for _, matcher := range commonschema.CustomFieldMatchers { - if matcher.IsMatch(field, result) { - field.ObjectDefinition = matcher.ReplacementObjectDefinition() - break - } - } - - fields[fieldName] = field - } - model.Fields = fields - result.Models[modelName] = model - } - - return input -} - -func (d *SwaggerDefinition) findNestedItemsYetToBeParsed(operations map[string]sdkModels.SDKOperation, known internal.ParseResult) (*internal.ParseResult, error) { - result := internal.ParseResult{ - Constants: map[string]sdkModels.SDKConstant{}, - Models: map[string]sdkModels.SDKModel{}, - } - result.Append(known) - - // Now that we have a complete list of all of the nested items to find, loop around and find them - // this is intentionally not fetching nested models to avoid an infinite loop with Model1 referencing - // Model2 which references Model1 (they instead get picked up in the next iteration) - referencesToFind, err := d.determineObjectsRequiredButNotParsed(operations, result) - if err != nil { - return nil, fmt.Errorf("determining objects required but not parsed: %+v", err) - } - for len(*referencesToFind) > 0 { - for _, referenceName := range *referencesToFind { - topLevelObject, err := d.findTopLevelObject(referenceName) - if err != nil { - return nil, fmt.Errorf("finding top level object named %q: %+v", referenceName, err) - } - - parsedAsAConstant, constErr := constants.MapConstant(topLevelObject.Type, referenceName, nil, topLevelObject.Enum, topLevelObject.Extensions) - parsedAsAModel, modelErr := d.parseModel(referenceName, *topLevelObject) - if (constErr != nil && modelErr != nil) || (parsedAsAConstant == nil && parsedAsAModel == nil) { - return nil, fmt.Errorf("reference %q didn't parse as a Model or a Constant.\n\nConstant Error: %+v\n\nModel Error: %+v", referenceName, constErr, modelErr) - } - - if parsedAsAConstant != nil { - result.Constants[parsedAsAConstant.Name] = parsedAsAConstant.Details - } - if parsedAsAModel != nil { - if err := result.Append(*parsedAsAModel); err != nil { - return nil, fmt.Errorf("appending model: %+v", err) - } - } - } - - remainingReferencesToFind, err := d.determineObjectsRequiredButNotParsed(operations, result) - if err != nil { - return nil, fmt.Errorf("determining objects required but not parsed: %+v", err) - } - if referencesAreTheSame(*referencesToFind, *remainingReferencesToFind) { - return nil, fmt.Errorf("the following references couldn't be found: %q", strings.Join(*referencesToFind, ", ")) - } - referencesToFind = remainingReferencesToFind - } - - return &result, nil -} - -func referencesAreTheSame(first []string, second []string) bool { - if len(first) != len(second) { - return false - } - - // first load the existing keys - keys := make(map[string]struct{}, 0) - for _, key := range first { - keys[key] = struct{}{} - } - - // then check the remaining ones - for _, key := range second { - if _, exists := keys[key]; !exists { - return false - } - } - - return true -} - -func (d *SwaggerDefinition) determineObjectsRequiredButNotParsed(operations map[string]sdkModels.SDKOperation, known internal.ParseResult) (*[]string, error) { - referencesToFind := make(map[string]struct{}, 0) - - var objectsRequiredByModel = func(modelName string, model sdkModels.SDKModel) (*[]string, error) { - result := make(map[string]struct{}, 0) - // if it's a model, we need to check all of the fields for this to find any constant or models - // that we don't know about - typesToFind, err := d.objectsUsedByModel(modelName, model) - if err != nil { - return nil, fmt.Errorf("determining objects used by model %q: %+v", modelName, err) - } - for _, typeName := range *typesToFind { - _, existingConstant := known.Constants[typeName] - _, existingModel := known.Models[typeName] - if !existingConstant && !existingModel { - result[typeName] = struct{}{} - } - } - - out := make([]string, 0) - for k := range result { - out = append(out, k) - } - return &out, nil - } - - for _, operation := range operations { - if operation.RequestObject != nil { - topLevelRef := helpers.InnerMostSDKObjectDefinition(*operation.RequestObject) - if topLevelRef.Type == sdkModels.ReferenceSDKObjectDefinitionType { - isKnownConstant, isKnownModel := isObjectKnown(*topLevelRef.ReferenceName, known) - if !isKnownConstant && !isKnownModel { - referencesToFind[*topLevelRef.ReferenceName] = struct{}{} - } - - if isKnownModel { - modelName := *topLevelRef.ReferenceName - model := known.Models[modelName] - missingReferencesInModel, err := objectsRequiredByModel(modelName, model) - if err != nil { - return nil, fmt.Errorf("determining objects required by model %q: %+v", modelName, err) - } - for _, name := range *missingReferencesInModel { - referencesToFind[name] = struct{}{} - } - } - } - } - - if operation.ResponseObject != nil { - topLevelRef := helpers.InnerMostSDKObjectDefinition(*operation.ResponseObject) - if topLevelRef.Type == sdkModels.ReferenceSDKObjectDefinitionType { - isKnownConstant, isKnownModel := isObjectKnown(*topLevelRef.ReferenceName, known) - if !isKnownConstant && !isKnownModel { - referencesToFind[*topLevelRef.ReferenceName] = struct{}{} - } - - if isKnownModel { - // if it's a model, we need to check all of the fields for this to find any constant or models - // that we don't know about - modelName := *topLevelRef.ReferenceName - model := known.Models[modelName] - missingReferencesInModel, err := objectsRequiredByModel(modelName, model) - if err != nil { - return nil, fmt.Errorf("determining objects required by model %q: %+v", modelName, err) - } - for _, name := range *missingReferencesInModel { - referencesToFind[name] = struct{}{} - } - } - } - } - - for _, value := range operation.Options { - topLevelRef := topLevelOptionsObjectDefinition(value.ObjectDefinition) - if topLevelRef.Type != sdkModels.ReferenceSDKOperationOptionObjectDefinitionType { - continue - } - - if _, isKnown := known.Constants[*topLevelRef.ReferenceName]; !isKnown { - referencesToFind[*topLevelRef.ReferenceName] = struct{}{} - } - } - } - - // then verify we have all of the models for the current models we know about - for modelName, model := range known.Models { - missingReferencesInModel, err := objectsRequiredByModel(modelName, model) - if err != nil { - return nil, fmt.Errorf("determining objects required by model %q: %+v", modelName, err) - } - for _, name := range *missingReferencesInModel { - referencesToFind[name] = struct{}{} - } - } - - out := make([]string, 0) - for k := range referencesToFind { - if _, exists := known.Constants[k]; exists { - continue - } - if _, exists := known.Models[k]; exists { - continue - } - - out = append(out, k) - } - - return &out, nil -} - -func (d *SwaggerDefinition) objectsUsedByModel(modelName string, model sdkModels.SDKModel) (*[]string, error) { - typeNames := make(map[string]struct{}, 0) - - for _, field := range model.Fields { - definition := helpers.InnerMostSDKObjectDefinition(field.ObjectDefinition) - if definition.ReferenceName != nil { - typeNames[*definition.ReferenceName] = struct{}{} - } - } - - if model.ParentTypeName != nil { - typeNames[*model.ParentTypeName] = struct{}{} - } - - if model.FieldNameContainingDiscriminatedValue != nil { - // this must be a discriminator - modelNamesThatImplementThis, err := d.findModelNamesWhichImplement(modelName) - if err != nil { - return nil, fmt.Errorf("finding models which implement %q: %+v", modelName, err) - } - for _, k := range *modelNamesThatImplementThis { - typeNames[k] = struct{}{} - } - } - - out := make([]string, 0) - for k := range typeNames { - out = append(out, k) - } - return &out, nil -} - -func (d *SwaggerDefinition) findModelNamesWhichImplement(parentName string) (*[]string, error) { - modelNames := make([]string, 0) - - for childName, value := range d.swaggerSpecExtendedRaw.Definitions { - implementsParent, err := d.doesModelImplement(childName, value, parentName) - if err != nil { - return nil, fmt.Errorf("determining if model %q implements %q: %+v", childName, parentName, err) - } - if !*implementsParent { - continue - } - - logging.Tracef("Found %q implements %q", childName, parentName) - modelNames = append(modelNames, childName) - } - - return &modelNames, nil -} - -func (d *SwaggerDefinition) doesModelImplement(modelName string, value spec.Schema, parentName string) (*bool, error) { - implementsParent := false - if !strings.EqualFold(modelName, parentName) { - // does it implement (AllOf) the base class - for _, parent := range value.AllOf { - fragmentName := fragmentNameFromReference(parent.Ref) - if fragmentName == nil { - continue - } - - if strings.EqualFold(*fragmentName, parentName) { - implementsParent = true - break - } - - // otherwise does this model inherit from a model which does? - item, err := d.findTopLevelObject(*fragmentName) - if err != nil { - return nil, fmt.Errorf("loading Parent %q: %+v", *fragmentName, err) - } - if len(item.AllOf) > 0 { - inheritsFromParent, err := d.doesModelImplement(*fragmentName, *item, parentName) - if err != nil { - return nil, fmt.Errorf("determining if model %q implements %q: %+v", *fragmentName, parentName, err) - } - if *inheritsFromParent { - implementsParent = true - break - } - } - } - } - - return &implementsParent, nil -} - -func isObjectKnown(name string, known internal.ParseResult) (bool, bool) { - _, isConstant := known.Constants[name] - _, isModel := known.Models[name] - return isConstant, isModel -} diff --git a/tools/importer-rest-api-specs/components/parser/swagger_tags.go b/tools/importer-rest-api-specs/components/parser/swagger_tags.go deleted file mode 100644 index 7c4c3d43cc1..00000000000 --- a/tools/importer-rest-api-specs/components/parser/swagger_tags.go +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package parser - -import ( - "sort" - "strings" -) - -var tagsToIgnore = map[string]struct{}{ - "azurefirewallfqdntags": {}, - "usage": {}, -} - -func (d *SwaggerDefinition) findTags() []string { - tags := make(map[string]struct{}) - - // first we go through, assuming there are tags - for _, operation := range d.swaggerSpecExpanded.Operations() { - for _, details := range operation { - for _, tag := range details.Tags { - tags[tag] = struct{}{} - } - } - } - - out := make([]string, 0) - for key := range tags { - out = append(out, strings.Title(key)) - } - sort.Strings(out) - return out -} - -func tagShouldBeIgnored(tag string) bool { - lowered := strings.ToLower(tag) - for key := range tagsToIgnore { - // exact matches e.g. Usage - if strings.EqualFold(tag, key) { - return true - } - - // suffixes e.g. `ComputeUsage` - if strings.HasSuffix(lowered, strings.ToLower(key)) { - return true - } - } - - // there's a handful of these (e.g. `FluxConfigurationOperationStatus`) - if strings.Contains(lowered, "operationstatus") { - return true - } - - return false -} diff --git a/tools/importer-rest-api-specs/components/transformer/internal_to_dataapisdk.go b/tools/importer-rest-api-specs/components/transformer/internal_to_dataapisdk.go deleted file mode 100644 index fc04fd71281..00000000000 --- a/tools/importer-rest-api-specs/components/transformer/internal_to_dataapisdk.go +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package transformer - -import ( - "fmt" - - sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/logging" - importerModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/models" -) - -// NOTE: this file contains temporary glue code to enable refactoring this tool gradually, component-by-component. - -func MapInternalTypesToDataAPISDKTypes(serviceName string, inputApiVersions []importerModels.AzureApiDefinition, resourceProvider *string) (*sdkModels.Service, error) { - apiVersions := make(map[string]sdkModels.APIVersion) - - logging.Debugf("Mapping API Versions..") - for _, item := range inputApiVersions { - logging.Tracef("Mapping Service %q / API Version %q", item.ServiceName, item.ApiVersion) - mapped, err := mapInternalAPIVersionTypeToDataAPISDKType(item.ApiVersion, item) - if err != nil { - return nil, fmt.Errorf("mapping API Version %q: %+v", item.ApiVersion, err) - } - - if mapped == nil { - // handle there being only Constants and Models but no data - continue - } - - apiVersions[item.ApiVersion] = *mapped - } - - output := sdkModels.Service{ - APIVersions: apiVersions, - Generate: true, - Name: serviceName, - ResourceProvider: resourceProvider, - } - - return &output, nil -} - -func mapInternalAPIVersionTypeToDataAPISDKType(apiVersion string, input importerModels.AzureApiDefinition) (*sdkModels.APIVersion, error) { - resources := make(map[string]sdkModels.APIResource) - - for apiResource, apiResourceDetails := range input.Resources { - // Skip outputting APIResources containing only Constants/Models - // since these aren't usable without Operations - if len(apiResourceDetails.Operations) == 0 { - continue - } - - resources[apiResource] = sdkModels.APIResource{ - Constants: apiResourceDetails.Constants, - Models: apiResourceDetails.Models, - Operations: apiResourceDetails.Operations, - ResourceIDs: apiResourceDetails.ResourceIDs, - } - } - - // if all of the APIResources have been filtered out, let's ignore this APIVersion - if len(resources) == 0 { - return nil, nil - } - - return &sdkModels.APIVersion{ - APIVersion: apiVersion, - Generate: true, - Preview: input.IsPreviewVersion(), - Resources: resources, - Source: sdkModels.AzureRestAPISpecsSourceDataOrigin, - }, nil -} diff --git a/tools/importer-rest-api-specs/go.mod b/tools/importer-rest-api-specs/go.mod index 0b42b21e6f9..39076d8bc4f 100644 --- a/tools/importer-rest-api-specs/go.mod +++ b/tools/importer-rest-api-specs/go.mod @@ -3,12 +3,13 @@ module github.com/hashicorp/pandora/tools/importer-rest-api-specs go 1.22.1 require ( + github.com/davecgh/go-spew v1.1.1 github.com/gertd/go-pluralize v0.2.1 github.com/go-git/go-git/v5 v5.11.0 github.com/go-openapi/analysis v0.20.1 github.com/go-openapi/loads v0.20.2 github.com/go-openapi/spec v0.20.3 - github.com/hashicorp/go-azure-helpers v0.66.2 + github.com/hashicorp/go-azure-helpers v0.69.0 github.com/hashicorp/go-hclog v1.4.0 github.com/hashicorp/hcl/v2 v2.16.2 github.com/hashicorp/pandora/tools/data-api-repository v0.0.0-00010101000000-000000000000 @@ -16,6 +17,7 @@ require ( github.com/hashicorp/pandora/tools/sdk v0.0.0-00010101000000-000000000000 github.com/mitchellh/cli v1.1.4 github.com/zclconf/go-cty v1.13.1 + golang.org/x/text v0.16.0 ) require ( @@ -25,8 +27,6 @@ require ( github.com/Masterminds/sprig/v3 v3.2.0 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 // indirect - github.com/PuerkitoBio/purell v1.1.1 // indirect - github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect github.com/agext/levenshtein v1.2.2 // indirect github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310 // indirect @@ -39,10 +39,10 @@ require ( github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-git/go-billy/v5 v5.5.0 // indirect github.com/go-openapi/errors v0.19.9 // indirect - github.com/go-openapi/jsonpointer v0.19.5 // indirect - github.com/go-openapi/jsonreference v0.19.5 // indirect + github.com/go-openapi/jsonpointer v0.21.0 // indirect + github.com/go-openapi/jsonreference v0.21.0 // indirect github.com/go-openapi/strfmt v0.20.0 // indirect - github.com/go-openapi/swag v0.19.14 // indirect + github.com/go-openapi/swag v0.23.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/uuid v1.1.2 // indirect @@ -53,7 +53,7 @@ require ( github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/kevinburke/ssh_config v1.2.0 // indirect - github.com/mailru/easyjson v0.7.6 // indirect + github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.12 // indirect github.com/mattn/go-isatty v0.0.14 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect @@ -68,20 +68,26 @@ require ( github.com/spf13/cast v1.3.1 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect go.mongodb.org/mongo-driver v1.10.3 // indirect - golang.org/x/crypto v0.21.0 // indirect - golang.org/x/mod v0.12.0 // indirect - golang.org/x/net v0.23.0 // indirect - golang.org/x/sys v0.18.0 // indirect - golang.org/x/text v0.14.0 // indirect - golang.org/x/tools v0.13.0 // indirect + golang.org/x/crypto v0.24.0 // indirect + golang.org/x/mod v0.18.0 // indirect + golang.org/x/net v0.26.0 // indirect + golang.org/x/sync v0.7.0 // indirect + golang.org/x/sys v0.21.0 // indirect + golang.org/x/tools v0.22.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) -replace github.com/go-openapi/analysis v0.20.1 => github.com/jackofallops/analysis v0.20.2-0.20210705135157-888aa8dbc8e5 - replace github.com/hashicorp/pandora/tools/data-api-repository => ../data-api-repository replace github.com/hashicorp/pandora/tools/data-api-sdk => ../data-api-sdk replace github.com/hashicorp/pandora/tools/sdk => ../sdk + +replace github.com/go-openapi/analysis v0.20.1 => github.com/jackofallops/analysis v0.20.2-0.20210705135157-888aa8dbc8e5 + +replace github.com/go-openapi/jsonreference v0.19.5 => github.com/stephybun/jsonreference v0.21.1-0.20241001092726-f8a8f352cb85 + +replace github.com/go-openapi/jsonreference v0.21.0 => github.com/stephybun/jsonreference v0.21.1-0.20241001092726-f8a8f352cb85 + +replace github.com/go-openapi/spec v0.20.3 => github.com/stephybun/spec v0.21.1-0.20241001093718-b6a387937386 diff --git a/tools/importer-rest-api-specs/go.sum b/tools/importer-rest-api-specs/go.sum index 13ffe64aa4c..f967f8c8a92 100644 --- a/tools/importer-rest-api-specs/go.sum +++ b/tools/importer-rest-api-specs/go.sum @@ -14,9 +14,7 @@ github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5 github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 h1:kkhsdkhsCvIsutKu5zLMgWtgh9YxGCNAw8Ad8hjwfYg= github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/agext/levenshtein v1.2.2 h1:0S/Yg6LYmFJ5stwQeRp6EeOcCbj7xiqQSdNelsXvaqE= github.com/agext/levenshtein v1.2.2/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= @@ -93,14 +91,13 @@ github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwds github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= +github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= -github.com/go-openapi/jsonreference v0.19.5 h1:1WJP/wi4OjB4iV8KVbH73rQaoialJrqv8gitZLxGLtM= -github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= @@ -128,8 +125,6 @@ github.com/go-openapi/spec v0.19.8/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHK github.com/go-openapi/spec v0.19.15/go.mod h1:+81FIL1JwC5P3/Iuuozq3pPE9dXdIEGxFutcFKaVbmU= github.com/go-openapi/spec v0.20.0/go.mod h1:+81FIL1JwC5P3/Iuuozq3pPE9dXdIEGxFutcFKaVbmU= github.com/go-openapi/spec v0.20.1/go.mod h1:93x7oh+d+FQsmsieroS4cmR3u0p/ywH649a3qwC9OsQ= -github.com/go-openapi/spec v0.20.3 h1:uH9RQ6vdyPSs2pSy9fL8QPspDF2AMIMPtmK5coSSjtQ= -github.com/go-openapi/spec v0.20.3/go.mod h1:gG4F8wdEDN+YPBMVnzE85Rbhf+Th2DTvA9nFPQ5AYEg= github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY= @@ -148,8 +143,9 @@ github.com/go-openapi/swag v0.19.7/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfT github.com/go-openapi/swag v0.19.9/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfTe5McPyhY= github.com/go-openapi/swag v0.19.12/go.mod h1:eFdyEBkTdoAf/9RXBvj4cr1nH7GD8Kzo5HTt47gr72M= github.com/go-openapi/swag v0.19.13/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= -github.com/go-openapi/swag v0.19.14 h1:gm3vOOXfiuw5i9p5N9xJvfjvuofpyvLA9Wr6QfK5Fng= github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= +github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA= github.com/go-openapi/validate v0.19.3/go.mod h1:90Vh6jjkTn+OT1Eefm0ZixWNFjhtOH7vS9k0lo6zwJo= @@ -201,8 +197,8 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-azure-helpers v0.66.2 h1:+Pzuo7pdKl0hBXXr5ymmhs4Q40tHAo2nAvHq4WgSjx8= -github.com/hashicorp/go-azure-helpers v0.66.2/go.mod h1:kJxXrFtJKJdOEqvad8pllAe7dhP4DbN8J6sqFZe47+4= +github.com/hashicorp/go-azure-helpers v0.69.0 h1:JwUWXyDgyr6OafU4CgSvrbEP1wcMjfz4gxRQciDQkBQ= +github.com/hashicorp/go-azure-helpers v0.69.0/go.mod h1:BmbF4JDYXK5sEmFeU5hcn8Br21uElcqLfdQxjatwQKw= github.com/hashicorp/go-hclog v1.4.0 h1:ctuWFGrhFha8BnnzxqeRGidlEcQkDyL5u8J8t5eA11I= github.com/hashicorp/go-hclog v1.4.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= @@ -236,6 +232,7 @@ github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47e github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -250,8 +247,9 @@ github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.1/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= -github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= @@ -288,6 +286,7 @@ github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUr github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -299,6 +298,7 @@ github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndr github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= @@ -316,18 +316,28 @@ github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/stephybun/jsonreference v0.21.1-0.20241001092726-f8a8f352cb85 h1:lq33vsdMWKb7ms9/80Rnz9gzKLoQFA3RoSdpmeUrTgg= +github.com/stephybun/jsonreference v0.21.1-0.20241001092726-f8a8f352cb85/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= +github.com/stephybun/spec v0.21.1-0.20241001093718-b6a387937386 h1:gybBLlwXOykSdvgdBE/UZgJtQcnPPFqFhkP0EJAEgfU= +github.com/stephybun/spec v0.21.1-0.20241001093718-b6a387937386/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= @@ -365,12 +375,13 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= -golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= +golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= -golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= +golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -391,8 +402,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= -golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= +golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -400,8 +411,8 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= -golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -430,15 +441,15 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= -golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= -golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= -golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= +golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= +golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -449,8 +460,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -463,8 +474,8 @@ golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= -golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= +golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/tools/importer-rest-api-specs/internal/cmd/import.go b/tools/importer-rest-api-specs/internal/cmd/import.go index 27dfc2528c7..3892325e1f8 100644 --- a/tools/importer-rest-api-specs/internal/cmd/import.go +++ b/tools/importer-rest-api-specs/internal/cmd/import.go @@ -8,8 +8,8 @@ import ( "log" "strings" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/logging" - legacyPipeline "github.com/hashicorp/pandora/tools/importer-rest-api-specs/pipeline" + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/pipeline" "github.com/mitchellh/cli" ) @@ -54,34 +54,17 @@ func (c ImportCommand) Run(args []string) int { serviceNames = strings.Split(serviceNamesRaw, ",") } - // TODO: can't enable this until the Parser is refactored - //opts := pipeline.Options{ - // APIDefinitionsDirectory: c.outputDirectory, - // ConfigFilePath: c.resourceManagerConfigPath, - // ProviderPrefix: "azurerm", - // RestAPISpecsDirectory: c.restAPISpecsRepositoryDirectoryPath, - // ServiceNamesToLimitTo: serviceNames, - // SourceDataOrigin: sdkModels.AzureRestAPISpecsSourceDataOrigin, - // SourceDataType: sdkModels.ResourceManagerSourceDataType, - // TerraformDefinitionsDirectory: c.terraformDefinitionsPath, - //} - //if err := pipeline.RunImporter(opts); err != nil { - // log.Printf("Error: %+v", err) - // return 1 - //} - // - //return 0 - - input := legacyPipeline.RunInput{ - ConfigFilePath: c.resourceManagerConfigPath, - Logger: logging.Log, - OutputDirectory: c.outputDirectory, - ProviderPrefix: "azurerm", - Services: serviceNames, - SwaggerDirectory: c.restAPISpecsRepositoryDirectoryPath, - TerraformDefinitionsPath: c.terraformDefinitionsPath, + opts := pipeline.Options{ + APIDefinitionsDirectory: c.outputDirectory, + ConfigFilePath: c.resourceManagerConfigPath, + ProviderPrefix: "azurerm", + RestAPISpecsDirectory: c.restAPISpecsRepositoryDirectoryPath, + ServiceNamesToLimitTo: serviceNames, + SourceDataOrigin: sdkModels.AzureRestAPISpecsSourceDataOrigin, + SourceDataType: sdkModels.ResourceManagerSourceDataType, + TerraformDefinitionsDirectory: c.terraformDefinitionsPath, } - if err := legacyPipeline.Run(input); err != nil { + if err := pipeline.RunImporter(opts); err != nil { log.Printf("Error: %+v", err) return 1 } diff --git a/tools/importer-rest-api-specs/internal/cmd/validate.go b/tools/importer-rest-api-specs/internal/cmd/validate.go index 18f330b53f9..ef3f8092fbc 100644 --- a/tools/importer-rest-api-specs/internal/cmd/validate.go +++ b/tools/importer-rest-api-specs/internal/cmd/validate.go @@ -4,11 +4,12 @@ package cmd import ( - legacyPipeline "github.com/hashicorp/pandora/tools/importer-rest-api-specs/pipeline" + "flag" "log" - "os" + "strings" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/logging" + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/pipeline" "github.com/mitchellh/cli" ) @@ -35,34 +36,28 @@ func (ValidateCommand) Help() string { } func (c ValidateCommand) Run(args []string) int { - // TODO: can't enable this until the Parser is refactored - //opts := pipeline.Options{ - // APIDefinitionsDirectory: "", // not used for this - // ConfigFilePath: c.resourceManagerConfigPath, - // ProviderPrefix: "azurerm", - // RestAPISpecsDirectory: c.restAPISpecsRepositoryDirectoryPath, - // ServiceNamesToLimitTo: nil, // not used for this - // SourceDataOrigin: sdkModels.AzureRestAPISpecsSourceDataOrigin, - // SourceDataType: sdkModels.ResourceManagerSourceDataType, - // TerraformDefinitionsDirectory: c.terraformDefinitionsPath, - //} - //if err := pipeline.RunValidate(opts); err != nil { - // log.Printf("Error: %+v", err) - // return 1 - //} - // - //return 0 + var serviceNamesRaw string - input := legacyPipeline.RunInput{ - ConfigFilePath: c.resourceManagerConfigPath, - JustParseData: true, - Logger: logging.Log, - OutputDirectory: os.DevNull, - ProviderPrefix: "azurerm", - SwaggerDirectory: c.restAPISpecsRepositoryDirectoryPath, - TerraformDefinitionsPath: c.terraformDefinitionsPath, + f := flag.NewFlagSet("importer-rest-api-specs", flag.ExitOnError) + f.StringVar(&serviceNamesRaw, "services", "", "A list of comma separated Service named from the Data API to validate") + f.Parse(args) + + var serviceNames []string + if serviceNamesRaw != "" { + serviceNames = strings.Split(serviceNamesRaw, ",") + } + + opts := pipeline.Options{ + APIDefinitionsDirectory: "", // not used for this + ConfigFilePath: c.resourceManagerConfigPath, + ProviderPrefix: "azurerm", + RestAPISpecsDirectory: c.restAPISpecsRepositoryDirectoryPath, + ServiceNamesToLimitTo: serviceNames, + SourceDataOrigin: sdkModels.AzureRestAPISpecsSourceDataOrigin, + SourceDataType: sdkModels.ResourceManagerSourceDataType, + TerraformDefinitionsDirectory: c.terraformDefinitionsPath, } - if err := legacyPipeline.Run(input); err != nil { + if err := pipeline.RunValidate(opts); err != nil { log.Printf("Error: %+v", err) return 1 } diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/models_debugging_test.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/models_debugging_test.go new file mode 100644 index 00000000000..47ddbc5ea80 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/models_debugging_test.go @@ -0,0 +1,24 @@ +package apidefinitions + +import ( + "testing" + + "github.com/hashicorp/go-azure-helpers/lang/pointer" + discoveryModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/discovery/models" +) + +func TestDebugSingleSwaggerFile(t *testing.T) { + t.Skipf("This is intended to be used for debugging a single API Definition") + input := discoveryModels.AvailableDataSetForAPIVersion{ + APIVersion: "2023-05-01", + ContainsStableAPIVersion: false, + FilePathsContainingAPIDefinitions: []string{ + "/path/to/submodules/rest-api-specs/specification/storagecache/resource-manager/Microsoft.StorageCache/stable/2023-05-01/amlfilesystem.json", + }, + } + result, err := parseAPIVersion("StorageCache", input, pointer.To("Microsoft.StorageCache")) + if err != nil { + t.Fatalf(err.Error()) + } + t.Logf("Got %d APIResources", len(result.Resources)) +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parse_api_resource.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parse_api_resource.go new file mode 100644 index 00000000000..a8135df4a6a --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parse_api_resource.go @@ -0,0 +1,119 @@ +package apidefinitions + +import ( + "fmt" + + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/combine" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/ignore" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/logging" +) + +func parseAPIResourcesFromFile(filePath, serviceName string, resourceProvider *string, parsedAPIResources map[string]sdkModels.APIResource, resourceIds resourceids.ParseResult) (map[string]sdkModels.APIResource, error) { + parser, err := parser.NewAPIDefinitionsParser(filePath) + if err != nil { + return nil, fmt.Errorf("parsing the API Definitions within %q: %+v", filePath, err) + } + + // 1. Find the Swagger Tags + tags := parser.ParseSwaggerTags() + + // 2. Then iterate over each of the Swagger Operations with each Tag + for _, tag := range tags { + if ignore.SwaggerTag(tag) { + continue + } + + normalizedTag := cleanup.NormalizeTag(tag) + normalizedTag = cleanup.NormalizeResourceName(normalizedTag) + + // pass in any existing/known data so that we can reuse the models/references + existing := newAPIResource(normalizedTag) + if v, ok := parsedAPIResources[normalizedTag]; ok { + existing = v + } + + resource, err := parser.ParseAPIResourceWithinSwaggerTag(&tag, resourceProvider, resourceIds, existing) + if err != nil { + return nil, fmt.Errorf("finding resources for tag %q: %+v", tag, err) + } + + if resource != nil { + logging.Tracef("The Tag %q has %d API Operations", tag, len(resource.Operations)) + discoveredResources := map[string]sdkModels.APIResource{ + normalizedTag: *resource, + } + if err = combine.APIResourcesWith(parsedAPIResources, discoveredResources); err != nil { + return nil, fmt.Errorf("combining the APIResources for the identified Swagger Tag %q: %+v", tag, err) + } + } + } + + // 3. Then parse over any Swagger Operations which DON'T have a Tag + if !ignore.SwaggerTag(serviceName) { + inferredTag := cleanup.InferTagFromFilename(filePath) + + // pass in any existing/known data so that we can reuse the models/references + existing := newAPIResource(inferredTag) + if v, ok := parsedAPIResources[inferredTag]; ok { + existing = v + } + + resource, err := parser.ParseAPIResourceWithinSwaggerTag(nil, resourceProvider, resourceIds, existing) + if err != nil { + return nil, fmt.Errorf("finding resources for tag %q: %+v", serviceName, err) + } + + if resource != nil { + discoveredResources := map[string]sdkModels.APIResource{ + inferredTag: *resource, + } + if err = combine.APIResourcesWith(parsedAPIResources, discoveredResources); err != nil { + return nil, fmt.Errorf("combining the APIResources for the inferred Swagger Tag %q: %+v", inferredTag, err) + } + } + } + + // 4. Discriminator implementations that are defined in separate files with no link to a swagger tag + // are not parsed. So far there are two known instances of this (Data Factory, Chaos Studio) where + // the files are defined in a nested directory e.g. d.Name = /Types/Capabilities + if len(parsedAPIResources) == 0 { + // if we're here then there is no tag in this file, so we'll use the file name + inferredTag := cleanup.InferTagFromFilename(filePath) + + result, err := parser.FindOrphanedDiscriminatedModels(serviceName) + if err != nil { + return nil, fmt.Errorf("finding orphaned discriminated models for the inferred Swagger Tag %q in %q: %+v", inferredTag, filePath, err) + } + + // this is to avoid the creation of empty packages/directories in the api definitions + if len(result.Models) > 0 || len(result.Constants) > 0 { + resource := sdkModels.APIResource{ + Constants: result.Constants, + Models: result.Models, + } + + discoveredResources := map[string]sdkModels.APIResource{ + inferredTag: resource, + } + if err = combine.APIResourcesWith(parsedAPIResources, discoveredResources); err != nil { + return nil, fmt.Errorf("combining the APIResources for the inferred Swagger Tag %q: %+v", inferredTag, err) + } + } + } + + return parsedAPIResources, nil +} + +func newAPIResource(name string) sdkModels.APIResource { + return sdkModels.APIResource{ + Constants: make(map[string]sdkModels.SDKConstant), + Models: make(map[string]sdkModels.SDKModel), + Name: name, + Operations: make(map[string]sdkModels.SDKOperation), + ResourceIDs: make(map[string]sdkModels.ResourceID), + } +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parse_api_version.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parse_api_version.go new file mode 100644 index 00000000000..7c00de49bbf --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parse_api_version.go @@ -0,0 +1,92 @@ +package apidefinitions + +import ( + "fmt" + + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids" + discoveryModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/discovery/models" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/logging" +) + +// parseAPIVersion parses the information for this APIVersion from the AvailableDataSetForAPIVersion. +func parseAPIVersion(serviceName string, input discoveryModels.AvailableDataSetForAPIVersion, resourceProvider *string) (*sdkModels.APIVersion, error) { + // First we need to pull out a list of each of the Resource IDs within this API Version + // This is required to ensure we have consistent naming of these across the API Version which + // makes for a better user experience + foundResourceIDs := resourceids.ParseResult{ + OperationIdsToParsedResourceIds: make(map[string]resourceids.ParsedOperation), + NamesToResourceIDs: make(map[string]sdkModels.ResourceID), + Constants: make(map[string]sdkModels.SDKConstant), + } + for _, filePath := range input.FilePathsContainingAPIDefinitions { + logging.Tracef("Loading the Resource IDs from %q..", filePath) + parser, err := parser.NewAPIDefinitionsParser(filePath) + if err != nil { + return nil, fmt.Errorf("parsing the API Definitions within %q: %+v", filePath, err) + } + parsedResourceIds, err := parser.ParseResourceIds() + if err != nil { + return nil, fmt.Errorf("parsing the Resource IDs from %q: %+v", filePath, err) + } + if err := foundResourceIDs.Append(*parsedResourceIds); err != nil { + return nil, fmt.Errorf("appending the Resource IDs from %q: %+v", filePath, err) + } + logging.Tracef("Load the Resource IDs from %q - Completed.", filePath) + } + logging.Tracef("Loaded a total of %d Resource IDs (with %d Constants)", len(foundResourceIDs.NamesToResourceIDs), len(foundResourceIDs.Constants)) + + // Next let's go through and process each of the API Definitions to give us a map of APIResource name (key) to APIResource (value) + apiResources := make(map[string]sdkModels.APIResource) + for _, filePath := range input.FilePathsContainingAPIDefinitions { + logging.Tracef("Processing API Definitions from file %q..", filePath) + var err error + if apiResources, err = parseAPIResourcesFromFile(filePath, serviceName, resourceProvider, apiResources, foundResourceIDs); err != nil { + return nil, fmt.Errorf("parsing the APIResources from the API Definitions within %q: %+v", filePath, err) + } + + logging.Tracef("There are now %d APIResources", len(apiResources)) + logging.Tracef("Processing API Definitions from file %q - Completed.", filePath) + } + + // Normalize names of all operations, resource IDs, constants, models, and field names, ensuring they are + // appropriately Pascal cased. We are doing this after building out all the resources, since some references + // can get updated out-of-band and would subsequently be missed if we normalized immediately after parsing + // each resource. + for resourceName, resource := range apiResources { + apiResources[resourceName] = cleanup.NormalizeAPIResource(resource) + } + + // Now that we have a canonical list of resources - can we simplify the Operation names at all? + for resourceName, resource := range apiResources { + logging.Tracef("Simplifying operation names for resource %q", resourceName) + updated := cleanup.SimplifyOperationNamesForAPIResource(resourceName, resource) + apiResources[resourceName] = updated + } + + apiVersion := sdkModels.APIVersion{ + APIVersion: input.APIVersion, + Generate: true, + Preview: !input.ContainsStableAPIVersion, + Resources: apiResources, + Source: sdkModels.AzureRestAPISpecsSourceDataOrigin, + } + + // Next let's apply any data workarounds + logging.Debugf("Applying Data Workarounds..") + withFixesApplied, err := dataworkarounds.Apply(serviceName, apiVersion) + if err != nil { + return nil, fmt.Errorf("applying Data Workarounds for Service %q / API Version %q: %+v", serviceName, input.APIVersion, err) + } + logging.Debugf("Applying Data Workarounds - Complete.") + + // Finally let's remove any unused items + logging.Debugf("Removing unused items..") + output := cleanup.RemoveUnusedItems(*withFixesApplied) + logging.Debugf("Removing unused items - Complete.") + + return &output, nil +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parse_service.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parse_service.go new file mode 100644 index 00000000000..8d17bff7407 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parse_service.go @@ -0,0 +1,44 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package apidefinitions + +import ( + "fmt" + + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/ignore" + discoveryModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/discovery/models" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/logging" +) + +func ParseService(input discoveryModels.AvailableDataSet) (*sdkModels.Service, error) { + // Some Services have been deprecated or should otherwise be ignored - check before proceeding + if ignore.Services(input.ServiceName) { + logging.Debugf("Service %q should be ignored - skipping", input.ServiceName) + return nil, nil + } + + logging.Infof("Parsing Data for Service %q..", input.ServiceName) + apiVersions := make(map[string]sdkModels.APIVersion) + + for apiVersionName, dataSet := range input.DataSetsForAPIVersions { + logging.Infof("Parsing Data for API Version %q..", apiVersionName) + parsed, err := parseAPIVersion(input.ServiceName, dataSet, input.ResourceProvider) + if err != nil { + return nil, fmt.Errorf("parsing API Version %q: %+v", apiVersionName, err) + } + + apiVersions[apiVersionName] = *parsed + logging.Infof("Parsing Data for API Version %q - Completed", apiVersionName) + } + + logging.Infof("Parsing Data for Service %q - Completed", input.ServiceName) + return &sdkModels.Service{ + APIVersions: apiVersions, + Generate: true, + Name: input.ServiceName, + ResourceProvider: input.ResourceProvider, + TerraformDefinition: nil, // built-up later in the process + }, nil +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser.go deleted file mode 100644 index acafd57013c..00000000000 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser.go +++ /dev/null @@ -1,4 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package apidefinitions diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/README.md b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/README.md new file mode 100644 index 00000000000..a98168ec213 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/README.md @@ -0,0 +1,13 @@ +TODO: + +## A note on terminology + +This package intentionally uses a combination of terminology to reflect the differing API Definitions available within `Azure/azure-rest-api-specs`. + +* `Swagger` is used to refer to the API Definitions defined using Swagger 2.x (at the time of writing, the majority of the API Definitions). +* `OpenAPI` is used to refer to the API Definitions defined using OpenAPI 3.x (at the time of writing, few exist). +* `TypeSpec` is used to refer to the API Definitions written using [TypeSpec](https://github.com/Microsoft/typespec) which are typically compiled to Swagger 2.x or OpenAPI 3.x - and the majority of the Azure Resource Manager related items are based upon [the `Azure/typespec-azure` package](https://github.com/Azure/typespec-azure). + +Where possible we use the terminology `API Definition` since these are implementation details - but this more specific terminology will be used where necessary. + +TODO also mention Supplemental Data \ No newline at end of file diff --git a/tools/importer-rest-api-specs/components/parser/normalization.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/normalize_api_definitions.go similarity index 65% rename from tools/importer-rest-api-specs/components/parser/normalization.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/normalize_api_definitions.go index 835b394abe2..3930dbb88f5 100644 --- a/tools/importer-rest-api-specs/components/parser/normalization.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/normalize_api_definitions.go @@ -1,16 +1,12 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package parser +package cleanup import ( "fmt" + "path/filepath" "strings" - - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/components/parser/cleanup" ) -func normalizeOperationName(operationId string, tag *string) string { +func NormalizeOperationName(operationId string, tag *string) string { operationName := operationId // in some cases the OperationId *is* the Tag, in this instance I guess we take that as ok? if tag != nil && !strings.EqualFold(operationName, *tag) { @@ -31,11 +27,11 @@ func normalizeOperationName(operationId string, tag *string) string { operationName = strings.TrimPrefix(operationName, "s") // plurals } operationName = strings.TrimPrefix(operationName, "_") - operationName = cleanup.NormalizeName(operationName) + operationName = NormalizeName(operationName) return operationName } -func normalizeTag(input string) string { +func NormalizeTag(input string) string { // NOTE: we could be smarter here, but given this is a handful of cases it's // probably prudent to hard-code these for now (and fix the swaggers as we // come across them?) @@ -43,7 +39,20 @@ func normalizeTag(input string) string { output = strings.ReplaceAll(output, "EndPoint", "Endpoint") output = strings.ReplaceAll(output, "NetWork", "Network") output = strings.ReplaceAll(output, "Baremetalinfrastructure", "BareMetalInfrastructure") + output = strings.ReplaceAll(output, "virtualWans", "VirtualWANs") output = strings.ReplaceAll(output, "VirtualWans", "VirtualWANs") return output } + +// InferTagFromFilename is a workaround for missing tag data in the swagger. We assume the proper tag name +// is the file name. This is less than ideal, but _should_ be fine. +func InferTagFromFilename(input string) string { + directory := filepath.Dir(input) + fileName := strings.TrimPrefix(input, directory) + fileName = strings.TrimPrefix(fileName, fmt.Sprintf("%c", filepath.Separator)) + fileName = strings.TrimSuffix(fileName, ".json") + inferredTag := PluraliseName(fileName) + normalizedTag := NormalizeTag(inferredTag) + return NormalizeResourceName(normalizedTag) +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/normalize_resource_id.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/normalize_resource_id.go new file mode 100644 index 00000000000..aa2a841f54a --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/normalize_resource_id.go @@ -0,0 +1,162 @@ +package cleanup + +import "strings" + +// TODO: this wants moving into the `resourceids` package, but for now + +// NormalizeSegment normalizes the segments in the URI, since this data isn't normalized at review time :shrug: +func NormalizeSegment(input string, camelCase bool) string { + // property names in Models that are normalized in NormalizeCanonicalisation to begin with IP need to be excluded here + if strings.HasPrefix(input, "IP") { + return input + } + + fixed := map[string]string{ + // these are intentionally lower-case keys -> camelCased segments + "accounts": "accounts", + "alertrules": "alertRules", + "alertruletemplates": "alertRuleTemplates", + "apikeys": "apiKeys", + "apiversion": "apiVersion", + "applicationgatewaywebapplicationfirewallpolicies": "applicationGatewayWebApplicationFirewallPolicies", + "appsettings": "appSettings", + "armtemplates": "armTemplates", + "artifactsources": "artifactSources", + "artifacttypes": "artifactTypes", + "attacheddatabaseconfigurations": "attachedDatabaseConfigurations", + "attachednetworks": "attachedNetworks", + "attestationprovider": "attestationProviders", // NOTE: this is a Swagger issue we need to fix too + "authorizationrules": "authorizationRules", + "authproviders": "authProviders", + "automationrules": "automationRules", + "autoscalesettings": "autoScaleSettings", + "azureasyncoperations": "azureAsyncOperations", + "backupstorageconfig": "backupStorageConfig", + "buildpackBindings": "buildPackBindings", + "cdnwebapplicationfirewallpolicies": "cdnWebApplicationFirewallPolicies", + "certificates": "certificates", // handles Certificates + "channels": "channels", + "clusterpools": "clusterPools", + "clusters": "clusters", // handles Clusters + "compilationjobs": "compilationJobs", + "connectionstrings": "connectionStrings", + "configreferences": "configReferences", + "consumergroups": "consumerGroups", + "contentproducttemplates": "contentProductTemplates", + "continuouswebjobs": "continuousWebJobs", + "customimages": "customImages", + "customipprefixes": "customIPPrefixes", + "databases": "databases", // handles Databases + "datareferences": "dataReferences", // handles MachineLearningServices + "dataconnections": "dataConnections", + "datastores": "dataStores", + "dedicatedsqlminimaltlssettings": "dedicatedSQLMinimalTLSSettings", + "default": "default", // handles Default + "deletedservices": "deletedServices", + "devboxdefinitions": "devBoxDefinitions", + "devcenters": "devCenters", + "dicomservices": "dicomServices", + "dnszones": "dnsZones", + "endpoints": "endpoints", // handles Endpoints + "enrichments": "enrichments", // handles Enrichments + "exportconfiguration": "exportConfiguration", // inconsistency in ApplicationInsights (singular, not plural) + "fhirdestinations": "fhirDestinations", + "fhirservices": "fhirServices", + "fileservers": "fileServers", + "fallbackroute": "fallbackRoute", + "featuresets": "featureSets", + "featurestoreentities": "featureStoreEntities", + "fqdnlists": "fqdnLists", + "fqn": "fqn", // handles FQN + "frontdoorwebapplicationfirewallpolicies": "frontDoorWebApplicationFirewallPolicies", + "functionappsettings": "functionAppSettings", + "globalrulestacks": "globalRulestacks", // (@jackofallops) - "Rulestack" is considered one word, but also casing bug in the service. https://github.com/Azure/azure-rest-api-specs/issues/24780#issuecomment-1635234884 + "hostruntime": "hostRuntime", // inconsistency in Web + "hybridconnection": "hybridConnection", + "hypervsites": "hyperVSites", + "integrationruntimes": "integrationRuntimes", + "iotconnectors": "iotConnectors", + "iothubkeys": "iotHubKeys", + "iothubs": "iotHubs", + "iotsecuritysolutions": "iotSecuritySolutions", + "ipconfigurations": "ipConfigurations", + "iscsiservers": "iscsiServers", + "linkedservices": "linkedServices", + "listkeys": "listKeys", + "localrulestacks": "localRulestacks", // (@jackofallops) - "Rulestack" is considered one word, but also casing bug in the service. https://github.com/Azure/azure-rest-api-specs/issues/24780#issuecomment-1635234884 + "logprofiles": "logProfiles", + "managedclusters": "managedClusters", + "mediaservices": "mediaServices", + "managedclustersnapshots": "managedClusterSnapshots", + "managementassociations": "managementAssociations", + "managementconfigurations": "managementConfigurations", + "managedprivateendpoints": "managedPrivateEndpoints", + "mastersites": "masterSites", + "mediaservice": "mediaService", + "migratemysql": "migrateMySql", + "nodecounts": "nodeCounts", + "notificationchannels": "notificationChannels", + "openshiftclusters": "openShiftClusters", + "operationresults": "operationResults", + "p2svpnGateways": "p2sVpnGateways", + "pipelineruns": "pipelineRuns", + "policysets": "policySets", + "portalconfigs": "portalConfigs", + "prefixlists": "prefixLists", + "premieraddons": "premierAddons", + "principalassignments": "principalAssignments", + "publicipaddresses": "publicIPAddresses", + "reservationorders": "reservationOrders", + "resourcegroups": "resourceGroups", + "resourcetyperegistrations": "resourceTypeRegistrations", + "role": "role", // handles Role + "routes": "routes", // handles Routes + "rulesengines": "rulesEngines", + "runasaccounts": "runAsAccounts", + "saasresources": "saasResources", + "schemagroups": "schemaGroups", + "scripts": "scripts", // handles Scripts + "serverfarms": "serverFarms", + "servicefabrics": "serviceFabrics", + "servicerunners": "serviceRunners", + "siteextensions": "siteExtensions", + "smartdetectoralertrules": "smartDetectorAlertRules", + "sourcecontrols": "sourceControls", + "sparkconfigurations": "sparkConfigurations", + "streamingjobs": "streamingJobs", + "supportedbuildpacks": "supportedBuildPacks", + "spring": "spring", + "subscriptions": "subscriptions", // e.g. /Subscriptions -> /subscriptions + "subvolumes": "subVolumes", + "trafficmanagerprofiles": "trafficManagerProfiles", + "triggeredwebjobs": "triggeredWebJobs", + "vaultstorageconfig": "vaultStorageConfig", + "vcenters": "vCenters", + "virtualendpoints": "virtualEndpoints", // exists in Postgresql + "virtualnetworks": "virtualNetworks", + "virtualmachine": "virtualMachine", // exists in MarketplaceOrdering + "virtualmachines": "virtualMachines", + "vmname": "virtualMachineName", // inconsistency in Compute + "vmscalesetname": "virtualMachineScaleSetName", // inconsistency in Compute (#1204) + "vmwaresites": "vmwareSites", + "vmextension": "vmExtension", + "vmimage": "vmImage", + "volumegroups": "volumeGroups", + "webhooks": "webHooks", + "webjobs": "webJobs", + "webtests": "webTests", + "workbooktemplates": "workbookTemplates", + } + + for k, v := range fixed { + if strings.EqualFold(k, input) { + if camelCase { + return v + } else { + return Title(v) + } + } + } + + return input +} diff --git a/tools/importer-rest-api-specs/components/parser/normalizer.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/normalize_sdk_objects.go similarity index 71% rename from tools/importer-rest-api-specs/components/parser/normalizer.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/normalize_sdk_objects.go index 6a428665646..da4bde3ea29 100644 --- a/tools/importer-rest-api-specs/components/parser/normalizer.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/normalize_sdk_objects.go @@ -1,43 +1,42 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package parser +package cleanup import ( "github.com/hashicorp/go-azure-helpers/lang/pointer" sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/components/parser/cleanup" ) -// normalizeAzureApiResource works through the parsed AzureApiResource and ensures +// NormalizeAPIResource works through the parsed AzureApiResource and ensures // that all the Names and References are consistent (TitleCase) as a final effort // to ensure the Swagger Data is normalized. -func normalizeAzureApiResource(input sdkModels.APIResource) sdkModels.APIResource { +func NormalizeAPIResource(input sdkModels.APIResource) sdkModels.APIResource { normalizedConstants := make(map[string]sdkModels.SDKConstant) for k, v := range input.Constants { - name := cleanup.NormalizeName(k) + name := NormalizeName(k) normalizedConstants[name] = v } normalizedModels := make(map[string]sdkModels.SDKModel) for k, v := range input.Models { - modelName := cleanup.NormalizeName(k) + modelName := NormalizeName(k) fields := make(map[string]sdkModels.SDKField) for fieldName, fieldVal := range v.Fields { - normalizedFieldName := cleanup.NormalizeName(fieldName) + normalizedFieldName := NormalizeName(fieldName) fieldVal.ObjectDefinition = normalizeSDKObjectDefinition(fieldVal.ObjectDefinition) fields[normalizedFieldName] = fieldVal } v.Fields = fields if v.ParentTypeName != nil { - val := cleanup.NormalizeName(*v.ParentTypeName) + val := NormalizeName(*v.ParentTypeName) v.ParentTypeName = &val } // Discriminators can be `@type` which get normalized to `Type` so we need to normalize the field name here if v.FieldNameContainingDiscriminatedValue != nil { - val := cleanup.NormalizeName(*v.FieldNameContainingDiscriminatedValue) + val := NormalizeName(*v.FieldNameContainingDiscriminatedValue) v.FieldNameContainingDiscriminatedValue = &val } @@ -47,7 +46,7 @@ func normalizeAzureApiResource(input sdkModels.APIResource) sdkModels.APIResourc normalizedOperations := make(map[string]sdkModels.SDKOperation) for k, v := range input.Operations { if v.ResourceIDName != nil { - normalized := cleanup.NormalizeName(*v.ResourceIDName) + normalized := NormalizeName(*v.ResourceIDName) v.ResourceIDName = &normalized } @@ -63,9 +62,9 @@ func normalizeAzureApiResource(input sdkModels.APIResource) sdkModels.APIResourc normalizedOptions := make(map[string]sdkModels.SDKOperationOption, 0) for optionKey, optionVal := range v.Options { - optionKey = cleanup.NormalizeName(optionKey) + optionKey = NormalizeName(optionKey) - optionVal.ObjectDefinition = normalizeOptionsObjectDefinition(optionVal.ObjectDefinition) + optionVal.ObjectDefinition = normalizeSDKOptionsObjectDefinition(optionVal.ObjectDefinition) normalizedOptions[optionKey] = optionVal } @@ -80,14 +79,14 @@ func normalizeAzureApiResource(input sdkModels.APIResource) sdkModels.APIResourc normalizedConstantNames := make([]string, 0) for _, cn := range v.ConstantNames { - name := cleanup.NormalizeName(cn) + name := NormalizeName(cn) normalizedConstantNames = append(normalizedConstantNames, name) } v.ConstantNames = normalizedConstantNames for _, segment := range v.Segments { if segment.ConstantReference != nil { - normalized := cleanup.NormalizeName(*segment.ConstantReference) + normalized := NormalizeName(*segment.ConstantReference) segment.ConstantReference = &normalized } segments = append(segments, segment) @@ -107,8 +106,8 @@ func normalizeAzureApiResource(input sdkModels.APIResource) sdkModels.APIResourc func normalizeSDKObjectDefinition(input sdkModels.SDKObjectDefinition) sdkModels.SDKObjectDefinition { if input.ReferenceName != nil { - normalized := cleanup.NormalizeName(*input.ReferenceName) - input.ReferenceName = &normalized + normalized := NormalizeName(*input.ReferenceName) + input.ReferenceName = pointer.To(Title(normalized)) } if input.NestedItem != nil { @@ -119,14 +118,14 @@ func normalizeSDKObjectDefinition(input sdkModels.SDKObjectDefinition) sdkModels return input } -func normalizeOptionsObjectDefinition(input sdkModels.SDKOperationOptionObjectDefinition) sdkModels.SDKOperationOptionObjectDefinition { +func normalizeSDKOptionsObjectDefinition(input sdkModels.SDKOperationOptionObjectDefinition) sdkModels.SDKOperationOptionObjectDefinition { if input.ReferenceName != nil { - normalized := cleanup.NormalizeName(*input.ReferenceName) + normalized := NormalizeName(*input.ReferenceName) input.ReferenceName = &normalized } if input.NestedItem != nil { - nestedItem := normalizeOptionsObjectDefinition(*input.NestedItem) + nestedItem := normalizeSDKOptionsObjectDefinition(*input.NestedItem) input.NestedItem = pointer.To(nestedItem) } diff --git a/tools/importer-rest-api-specs/components/parser/cleanup/pluralise_helper.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/pluralise_helper.go similarity index 98% rename from tools/importer-rest-api-specs/components/parser/cleanup/pluralise_helper.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/pluralise_helper.go index eff3048cf2d..6c5918b6e40 100644 --- a/tools/importer-rest-api-specs/components/parser/cleanup/pluralise_helper.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/pluralise_helper.go @@ -159,7 +159,7 @@ func detectCasing(input string) caseType { return UPPER case input == strings.ToLower(input): return LOWER - case input == strings.Title(input): + case input == Title(input): return TITLE } // Fallback? @@ -174,7 +174,7 @@ func returnCased(input string, casing caseType) string { return strings.ToUpper(input) } if casing == TITLE { - return strings.Title(input) + return Title(input) } return input diff --git a/tools/importer-rest-api-specs/components/parser/cleanup/pluralise_helper_test.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/pluralise_helper_test.go similarity index 100% rename from tools/importer-rest-api-specs/components/parser/cleanup/pluralise_helper_test.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/pluralise_helper_test.go diff --git a/tools/importer-rest-api-specs/components/parser/remove_unused_items.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/remove_unused_items.go similarity index 86% rename from tools/importer-rest-api-specs/components/parser/remove_unused_items.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/remove_unused_items.go index 894777be35e..c97f7197464 100644 --- a/tools/importer-rest-api-specs/components/parser/remove_unused_items.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/remove_unused_items.go @@ -1,22 +1,22 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package parser +package cleanup import ( "strings" - "github.com/hashicorp/pandora/tools/data-api-sdk/v1/helpers" + sdkHelpers "github.com/hashicorp/pandora/tools/data-api-sdk/v1/helpers" sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" ) -func removeUnusedItems(resources map[string]sdkModels.APIResource) map[string]sdkModels.APIResource { +func RemoveUnusedItems(input sdkModels.APIVersion) sdkModels.APIVersion { // The ordering matters here, we need to remove the ResourceIDs first since // they contain references to Constants - as do Models, so remove unused // Resource IDs, then Models, then Constants else we can have orphaned // constants within a package - - for resource, details := range resources { + outputResources := make(map[string]sdkModels.APIResource) + for resource, details := range input.Resources { resourceIdsForThisResource := make(map[string]sdkModels.ResourceID) for k, v := range details.ResourceIDs { resourceIdsForThisResource[k] = v @@ -53,7 +53,7 @@ func removeUnusedItems(resources map[string]sdkModels.APIResource) map[string]sd unusedConstants = findUnusedConstants(details.Operations, resourceIdsForThisResource, details.Models, details.Constants) } - resources[resource] = sdkModels.APIResource{ + outputResources[resource] = sdkModels.APIResource{ Constants: details.Constants, Models: details.Models, Operations: details.Operations, @@ -61,7 +61,8 @@ func removeUnusedItems(resources map[string]sdkModels.APIResource) map[string]sd } } - return resources + input.Resources = outputResources + return input } func findUnusedConstants(operations map[string]sdkModels.SDKOperation, resourceIds map[string]sdkModels.ResourceID, resourceModels map[string]sdkModels.SDKModel, resourceConstants map[string]sdkModels.SDKConstant) []string { @@ -71,7 +72,7 @@ func findUnusedConstants(operations map[string]sdkModels.SDKOperation, resourceI usedInAModel := false for _, model := range resourceModels { for _, field := range model.Fields { - definition := helpers.InnerMostSDKObjectDefinition(field.ObjectDefinition) + definition := sdkHelpers.InnerMostSDKObjectDefinition(field.ObjectDefinition) if definition.Type != sdkModels.ReferenceSDKObjectDefinitionType { continue } @@ -92,7 +93,7 @@ func findUnusedConstants(operations map[string]sdkModels.SDKOperation, resourceI usedInAnOperation := false for _, operation := range operations { if operation.RequestObject != nil { - definition := helpers.InnerMostSDKObjectDefinition(*operation.RequestObject) + definition := sdkHelpers.InnerMostSDKObjectDefinition(*operation.RequestObject) if definition.Type == sdkModels.ReferenceSDKObjectDefinitionType && *definition.ReferenceName == constantName { usedInAnOperation = true break @@ -100,7 +101,7 @@ func findUnusedConstants(operations map[string]sdkModels.SDKOperation, resourceI } if operation.ResponseObject != nil { - definition := helpers.InnerMostSDKObjectDefinition(*operation.ResponseObject) + definition := sdkHelpers.InnerMostSDKObjectDefinition(*operation.ResponseObject) if definition.Type == sdkModels.ReferenceSDKObjectDefinitionType && *definition.ReferenceName == constantName { usedInAnOperation = true break @@ -108,7 +109,7 @@ func findUnusedConstants(operations map[string]sdkModels.SDKOperation, resourceI } for _, v := range operation.Options { - definition := topLevelOptionsObjectDefinition(v.ObjectDefinition) + definition := sdkHelpers.InnerMostSDKOperationOptionObjectDefinition(v.ObjectDefinition) if definition.Type != sdkModels.ReferenceSDKOperationOptionObjectDefinitionType { continue } @@ -154,14 +155,6 @@ func findUnusedConstants(operations map[string]sdkModels.SDKOperation, resourceI return out } -func topLevelOptionsObjectDefinition(input sdkModels.SDKOperationOptionObjectDefinition) sdkModels.SDKOperationOptionObjectDefinition { - if input.NestedItem != nil { - return topLevelOptionsObjectDefinition(*input.NestedItem) - } - - return input -} - func findUnusedModels(operations map[string]sdkModels.SDKOperation, resourceModels map[string]sdkModels.SDKModel) []string { unusedModels := make(map[string]struct{}) for modelName, model := range resourceModels { @@ -176,7 +169,7 @@ func findUnusedModels(operations map[string]sdkModels.SDKOperation, resourceMode usedInAnOperation := false for _, operation := range operations { if operation.RequestObject != nil { - definition := helpers.InnerMostSDKObjectDefinition(*operation.RequestObject) + definition := sdkHelpers.InnerMostSDKObjectDefinition(*operation.RequestObject) if definition.Type == sdkModels.ReferenceSDKObjectDefinitionType && *definition.ReferenceName == modelName { usedInAnOperation = true break @@ -184,7 +177,7 @@ func findUnusedModels(operations map[string]sdkModels.SDKOperation, resourceMode } if operation.ResponseObject != nil { - definition := helpers.InnerMostSDKObjectDefinition(*operation.ResponseObject) + definition := sdkHelpers.InnerMostSDKObjectDefinition(*operation.ResponseObject) if definition.Type == sdkModels.ReferenceSDKObjectDefinitionType && *definition.ReferenceName == modelName { usedInAnOperation = true break @@ -193,7 +186,7 @@ func findUnusedModels(operations map[string]sdkModels.SDKOperation, resourceMode // @tombuildsstuff: whilst I don't _think_ there are any examples of this today, checking it because it's an option for _, v := range operation.Options { - definition := topLevelOptionsObjectDefinition(v.ObjectDefinition) + definition := sdkHelpers.InnerMostSDKOperationOptionObjectDefinition(v.ObjectDefinition) if definition.Type != sdkModels.ReferenceSDKOperationOptionObjectDefinitionType { continue } @@ -215,7 +208,7 @@ func findUnusedModels(operations map[string]sdkModels.SDKOperation, resourceMode } for _, field := range thisModel.Fields { - definition := helpers.InnerMostSDKObjectDefinition(field.ObjectDefinition) + definition := sdkHelpers.InnerMostSDKObjectDefinition(field.ObjectDefinition) if definition.Type != sdkModels.ReferenceSDKObjectDefinitionType { continue } diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/simplify_operation_names.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/simplify_operation_names.go new file mode 100644 index 00000000000..70ff1f22aee --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/simplify_operation_names.go @@ -0,0 +1,48 @@ +package cleanup + +import ( + "strings" + + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/logging" +) + +func SimplifyOperationNamesForAPIResource(apiResourceName string, apiResource sdkModels.APIResource) sdkModels.APIResource { + allOperationsStartWithPrefix := true + resourceNameLower := strings.ToLower(apiResourceName) + for operationName := range apiResource.Operations { + operationNameLowered := strings.ToLower(operationName) + if !strings.HasPrefix(operationNameLowered, resourceNameLower) || strings.EqualFold(operationNameLowered, resourceNameLower) { + allOperationsStartWithPrefix = false + break + } + } + + if !allOperationsStartWithPrefix { + logging.Tracef("Skipping simplifying operation names for resource %q", apiResourceName) + return apiResource + } + + output := make(map[string]sdkModels.SDKOperation) + for key, value := range apiResource.Operations { + updatedKey := key[len(resourceNameLower):] + // Trim off any spurious `s` at the start. This happens when the Swagger Tag and the Operation ID + // use different pluralizations (e.g. one is Singular and the other is Plural) + // + // Whilst it's possible this could happen for other suffixes (e.g. `ies`, or `y`) + // the Data only shows `s` at this point in time, so this is sufficient for now: + // https://github.com/hashicorp/pandora/pull/3016#pullrequestreview-1612987765 + // + // Any other examples will generate successfully but be unusable in the Go SDK since these + // will be treated as unexported methods - and can be addressed then. + if strings.HasPrefix(updatedKey, "s") { + updatedKey = updatedKey[1:] + } + + logging.Tracef("Simplifying Operation %q to %q", key, updatedKey) + output[updatedKey] = value + } + + apiResource.Operations = output + return apiResource +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/title.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/title.go new file mode 100644 index 00000000000..d3e0faa5061 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/title.go @@ -0,0 +1,10 @@ +package cleanup + +import ( + "golang.org/x/text/cases" + "golang.org/x/text/language" +) + +func Title(in string) string { + return cases.Title(language.AmericanEnglish, cases.NoLower).String(in) +} diff --git a/tools/importer-rest-api-specs/components/parser/cleanup/cleanup.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/to_sort.go similarity index 68% rename from tools/importer-rest-api-specs/components/parser/cleanup/cleanup.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/to_sort.go index 566eb1d8d21..9c738d13cfb 100644 --- a/tools/importer-rest-api-specs/components/parser/cleanup/cleanup.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/to_sort.go @@ -1,6 +1,3 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - package cleanup import ( @@ -44,7 +41,7 @@ func RemoveInvalidCharacters(input string, titleCaseSegments bool) string { for _, word := range split { word = strings.ReplaceAll(word, find, "") if titleCaseSegments { - word = strings.Title(word) + word = Title(word) } newVal += word } @@ -60,7 +57,7 @@ func NormalizeName(input string) string { output = NormalizeCanonicalisation(output) output = RemoveInvalidCharacters(output, true) output = NormalizeSegment(output, false) - output = strings.Title(output) + output = Title(output) return output } @@ -72,166 +69,10 @@ func NormalizeSegmentName(input string) string { output = strings.TrimSuffix(output, "Name") } - output = strings.Title(GetSingular(output)) + output = Title(GetSingular(output)) return output } -// NormalizeSegment normalizes the segments in the URI, since this data isn't normalized at review time :shrug: -func NormalizeSegment(input string, camelCase bool) string { - // property names in Models that are normalized in NormalizeCanonicalisation to begin with IP need to be excluded here - if strings.HasPrefix(input, "IP") { - return input - } - - fixed := map[string]string{ - // these are intentionally lower-case keys -> camelCased segments - "accounts": "accounts", - "alertrules": "alertRules", - "alertruletemplates": "alertRuleTemplates", - "apikeys": "apiKeys", - "apiversion": "apiVersion", - "applicationgatewaywebapplicationfirewallpolicies": "applicationGatewayWebApplicationFirewallPolicies", - "appsettings": "appSettings", - "armtemplates": "armTemplates", - "artifactsources": "artifactSources", - "artifacttypes": "artifactTypes", - "attacheddatabaseconfigurations": "attachedDatabaseConfigurations", - "attestationprovider": "attestationProviders", // NOTE: this is a Swagger issue we need to fix too - "authorizationrules": "authorizationRules", - "authproviders": "authProviders", - "automationrules": "automationRules", - "autoscalesettings": "autoScaleSettings", - "azureasyncoperations": "azureAsyncOperations", - "backupstorageconfig": "backupStorageConfig", - "buildpackBindings": "buildPackBindings", - "cdnwebapplicationfirewallpolicies": "cdnWebApplicationFirewallPolicies", - "certificates": "certificates", // handles Certificates - "channels": "channels", - "clusterpools": "clusterPools", - "clusters": "clusters", // handles Clusters - "compilationjobs": "compilationJobs", - "connectionstrings": "connectionStrings", - "configreferences": "configReferences", - "consumergroups": "consumerGroups", - "contentproducttemplates": "contentProductTemplates", - "continuouswebjobs": "continuousWebJobs", - "customimages": "customImages", - "customipprefixes": "customIPPrefixes", - "databases": "databases", // handles Databases - "datareferences": "dataReferences", // handles MachineLearningServices - "dataconnections": "dataConnections", - "datastores": "dataStores", - "dedicatedsqlminimaltlssettings": "dedicatedSQLMinimalTLSSettings", - "default": "default", // handles Default - "deletedservices": "deletedServices", - "devboxdefinitions": "devBoxDefinitions", - "devcenters": "devCenters", - "dicomservices": "dicomServices", - "dnszones": "dnsZones", - "endpoints": "endpoints", // handles Endpoints - "enrichments": "enrichments", // handles Enrichments - "exportconfiguration": "exportConfiguration", // inconsistency in ApplicationInsights (singular, not plural) - "fhirdestinations": "fhirDestinations", - "fhirservices": "fhirServices", - "fileservers": "fileServers", - "fallbackroute": "fallbackRoute", - "featuresets": "featureSets", - "featurestoreentities": "featureStoreEntities", - "fqdnlists": "fqdnLists", - "fqn": "fqn", // handles FQN - "frontdoorwebapplicationfirewallpolicies": "frontDoorWebApplicationFirewallPolicies", - "functionappsettings": "functionAppSettings", - "globalrulestacks": "globalRulestacks", // (@jackofallops) - "Rulestack" is considered one word, but also casing bug in the service. https://github.com/Azure/azure-rest-api-specs/issues/24780#issuecomment-1635234884 - "hostruntime": "hostRuntime", // inconsistency in Web - "hybridconnection": "hybridConnection", - "hypervsites": "hyperVSites", - "integrationruntimes": "integrationRuntimes", - "iotconnectors": "iotConnectors", - "iothubkeys": "iotHubKeys", - "iothubs": "iotHubs", - "iotsecuritysolutions": "iotSecuritySolutions", - "ipconfigurations": "ipConfigurations", - "iscsiservers": "iscsiServers", - "linkedservices": "linkedServices", - "listkeys": "listKeys", - "localrulestacks": "localRulestacks", // (@jackofallops) - "Rulestack" is considered one word, but also casing bug in the service. https://github.com/Azure/azure-rest-api-specs/issues/24780#issuecomment-1635234884 - "logprofiles": "logProfiles", - "managedclusters": "managedClusters", - "mediaservices": "mediaServices", - "managedclustersnapshots": "managedClusterSnapshots", - "managementassociations": "managementAssociations", - "managementconfigurations": "managementConfigurations", - "managedprivateendpoints": "managedPrivateEndpoints", - "mastersites": "masterSites", - "mediaservice": "mediaService", - "migratemysql": "migrateMySql", - "nodecounts": "nodeCounts", - "notificationchannels": "notificationChannels", - "openshiftclusters": "openShiftClusters", - "operationresults": "operationResults", - "p2svpnGateways": "p2sVpnGateways", - "pipelineruns": "pipelineRuns", - "policysets": "policySets", - "portalconfigs": "portalConfigs", - "prefixlists": "prefixLists", - "premieraddons": "premierAddons", - "principalassignments": "principalAssignments", - "publicipaddresses": "publicIPAddresses", - "reservationorders": "reservationOrders", - "resourcegroups": "resourceGroups", - "resourcetyperegistrations": "resourceTypeRegistrations", - "role": "role", // handles Role - "routes": "routes", // handles Routes - "rulesengines": "rulesEngines", - "runasaccounts": "runAsAccounts", - "saasresources": "saasResources", - "schemagroups": "schemaGroups", - "scripts": "scripts", // handles Scripts - "serverfarms": "serverFarms", - "servicefabrics": "serviceFabrics", - "servicerunners": "serviceRunners", - "siteextensions": "siteExtensions", - "smartdetectoralertrules": "smartDetectorAlertRules", - "sourcecontrols": "sourceControls", - "sparkconfigurations": "sparkConfigurations", - "streamingjobs": "streamingJobs", - "supportedbuildpacks": "supportedBuildPacks", - "spring": "spring", - "subscriptions": "subscriptions", // e.g. /Subscriptions -> /subscriptions - "subvolumes": "subVolumes", - "trafficmanagerprofiles": "trafficManagerProfiles", - "triggeredwebjobs": "triggeredWebJobs", - "vaultstorageconfig": "vaultStorageConfig", - "vcenters": "vCenters", - "virtualendpoints": "virtualEndpoints", // exists in Postgresql - "virtualnetworks": "virtualNetworks", - "virtualmachine": "virtualMachine", // exists in MarketplaceOrdering - "virtualmachines": "virtualMachines", - "vmname": "virtualMachineName", // inconsistency in Compute - "vmscalesetname": "virtualMachineScaleSetName", // inconsistency in Compute (#1204) - "vmwaresites": "vmwareSites", - "vmextension": "vmExtension", - "vmimage": "vmImage", - "volumegroups": "volumeGroups", - "webhooks": "webHooks", - "webjobs": "webJobs", - "webtests": "webTests", - "workbooktemplates": "workbookTemplates", - } - - for k, v := range fixed { - if strings.EqualFold(k, input) { - if camelCase { - return v - } else { - return strings.Title(v) - } - } - } - - return input -} - func NormalizeReservedKeywords(input string) string { keywords := []string{ "const", @@ -394,7 +235,7 @@ func NormalizeResourceProviderName(input string) string { output := make([]string, 0) for _, segment := range segments { - segment = strings.Title(segment) + segment = Title(segment) output = append(output, segment) } @@ -545,7 +386,7 @@ func NormalizeServiceName(input string) string { } if v, ok := fixed[strings.ToLower(input)]; ok { - return strings.Title(v) + return Title(v) } return input @@ -629,6 +470,7 @@ func NormalizeCanonicalisation(input string) string { output = strings.ReplaceAll(output, "Vm", "VM") output = strings.ReplaceAll(output, "vm", "VM") output = strings.ReplaceAll(output, "Cpu", "CPU") + output = strings.ReplaceAll(output, "Url", "URL") output = strings.ReplaceAll(output, "Https", "HTTPS") output = strings.ReplaceAll(output, "Http", "HTTP") diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/combine/api_resource.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/combine/api_resource.go new file mode 100644 index 00000000000..00996f841b5 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/combine/api_resource.go @@ -0,0 +1,34 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package combine + +import ( + "fmt" + + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" +) + +func APIResource(first, other sdkModels.APIResource) (*sdkModels.APIResource, error) { + constants, err := Constants(first.Constants, other.Constants) + if err != nil { + return nil, fmt.Errorf("combining constants: %+v", err) + } + first.Constants = constants + + models, err := Models(first.Models, other.Models) + if err != nil { + return nil, fmt.Errorf("combining models: %+v", err) + } + first.Models = models + + if err = combineOperations(first.Operations, other.Operations); err != nil { + return nil, fmt.Errorf("combining operations: %+v", err) + } + + if err = combineResourceIds(first.ResourceIDs, other.ResourceIDs); err != nil { + return nil, fmt.Errorf("combining resource ids: %+v", err) + } + + return &first, nil +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/combine/api_resources.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/combine/api_resources.go new file mode 100644 index 00000000000..cee40d9e24f --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/combine/api_resources.go @@ -0,0 +1,44 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package combine + +import ( + "fmt" + + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" +) + +func APIResourcesWith(resources, other map[string]sdkModels.APIResource) error { + for k, v := range other { + resource, ok := resources[k] + if !ok { + resources[k] = v + continue + } + + constants, err := Constants(resource.Constants, v.Constants) + if err != nil { + return fmt.Errorf("combining constants: %+v", err) + } + resource.Constants = constants + + models, err := Models(resource.Models, v.Models) + if err != nil { + return fmt.Errorf("combining models: %+v", err) + } + resource.Models = models + + if err = combineOperations(resource.Operations, v.Operations); err != nil { + return fmt.Errorf("combining operations: %+v", err) + } + + if err = combineResourceIds(resource.ResourceIDs, v.ResourceIDs); err != nil { + return fmt.Errorf("combining resource ids: %+v", err) + } + + resources[k] = resource + } + + return nil +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/combine/constants.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/combine/constants.go new file mode 100644 index 00000000000..f9555398c29 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/combine/constants.go @@ -0,0 +1,38 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package combine + +import ( + "fmt" + + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" +) + +func Constants(first, second map[string]sdkModels.SDKConstant) (map[string]sdkModels.SDKConstant, error) { + output := make(map[string]sdkModels.SDKConstant) + for k, v := range first { + output[k] = v + } + + for k, v := range second { + existingConst, ok := output[k] + if !ok { + output[k] = v + continue + } + + if existingConst.Type != v.Type { + return nil, fmt.Errorf("combining constant %q - multiple field types defined as %q and %q", k, string(existingConst.Type), string(v.Type)) + } + + vals, err := maps(existingConst.Values, v.Values) + if err != nil { + return nil, fmt.Errorf("combining values: %+v", err) + } + existingConst.Values = vals + output[k] = existingConst + } + + return output, nil +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/combine/maps.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/combine/maps.go new file mode 100644 index 00000000000..4e46b5863bd --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/combine/maps.go @@ -0,0 +1,28 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package combine + +import ( + "fmt" +) + +func maps(first map[string]string, second map[string]string) (map[string]string, error) { + vals := make(map[string]string, 0) + for k, v := range first { + vals[k] = v + } + for k, v := range second { + existing, ok := vals[k] + if !ok { + vals[k] = v + continue + } + + if existing != v { + return nil, fmt.Errorf("duplicate key %q with different value - first %q - second %q", k, existing, v) + } + } + + return vals, nil +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/combine/models.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/combine/models.go new file mode 100644 index 00000000000..9fe22eacee2 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/combine/models.go @@ -0,0 +1,47 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package combine + +import ( + "fmt" + + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" +) + +func Models(first map[string]sdkModels.SDKModel, second map[string]sdkModels.SDKModel) (map[string]sdkModels.SDKModel, error) { + output := make(map[string]sdkModels.SDKModel) + + for k, v := range first { + output[k] = v + } + + for k, v := range second { + existing, ok := output[k] + if ok && len(existing.Fields) != len(v.Fields) { + return nil, fmt.Errorf("duplicate models named %q with different fields - first %d - second %d", k, len(existing.Fields), len(v.Fields)) + } + output[k] = v + } + + // once we've combined the models for a resource and de-duplicated them, we will iterate over all of them to link any + // orphaned discriminated models to their parent + for k, v := range output { + // this model is an implementation, so we need to find/update the parent + if v.ParentTypeName != nil && v.FieldNameContainingDiscriminatedValue != nil && v.DiscriminatedValue != nil { + parent, ok := output[*v.ParentTypeName] + if !ok { + return nil, fmt.Errorf("no parent definition %q found for implementation %q", *v.ParentTypeName, k) + } + // discriminated models that are defined in a separate file with no reference to a path/tag/resource ID + // will not be found when we parse the parent, as a result the parent's TypeHintIn is set to nil, + // here we set the information back into the parent after we've found the implementation + if parent.FieldNameContainingDiscriminatedValue == nil { + parent.FieldNameContainingDiscriminatedValue = v.FieldNameContainingDiscriminatedValue + } + output[*v.ParentTypeName] = parent + } + } + + return output, nil +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/combine/operations.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/combine/operations.go new file mode 100644 index 00000000000..2c1fc23b557 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/combine/operations.go @@ -0,0 +1,28 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package combine + +import ( + "fmt" + + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/comparison" +) + +func combineOperations(operations map[string]sdkModels.SDKOperation, other map[string]sdkModels.SDKOperation) error { + for k, v := range other { + // if there's duplicate combineOperations named the same thing in different Swaggers, this is likely a data issue + operation, ok := operations[k] + if !ok { + operations[k] = v + continue + } + + if ok, err := comparison.OperationsMatch(v, operation); !ok { + return fmt.Errorf("differing Operations named %q. First: %+v / Second %+v / Error: %+v", k, v, operation, err) + } + } + + return nil +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/combine/resource_ids.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/combine/resource_ids.go new file mode 100644 index 00000000000..4a65206c9d2 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/combine/resource_ids.go @@ -0,0 +1,26 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package combine + +import ( + "fmt" + + sdkHelpers "github.com/hashicorp/pandora/tools/data-api-sdk/v1/helpers" + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/comparison" +) + +func combineResourceIds(resourceIds, other map[string]sdkModels.ResourceID) error { + for k, v := range other { + // if there's duplicate Resource IDs named the same in different Swaggers, this is likely a data issue + resourceId, ok := resourceIds[k] + if ok && !comparison.ResourceIDsMatch(v, resourceId) { + return fmt.Errorf("duplicate Resource ID named %q (First %q / Second %q)", k, sdkHelpers.DisplayValueForResourceID(v), sdkHelpers.DisplayValueForResourceID(resourceId)) + } + + resourceIds[k] = v + } + + return nil +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema/README.md b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema/README.md new file mode 100644 index 00000000000..9728c648f44 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema/README.md @@ -0,0 +1,5 @@ +## `./internal/components/apidefinitions/parser/commonschema` + +This package contains logic used to identify when an Object Definition should be replaced by a Common Schema type. + +Each Common Schema type implements the Matcher interface, which allows identifying if the SDKField in question matches the Common Schema type. diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema/apply.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema/apply.go new file mode 100644 index 00000000000..30a75a2a254 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema/apply.go @@ -0,0 +1,27 @@ +package commonschema + +import sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" + +func ReplaceSDKObjectDefinitionsAsNeeded(input sdkModels.APIResource) sdkModels.APIResource { + output := input + for modelName, model := range input.Models { + fields := model.Fields + for fieldName := range model.Fields { + field := model.Fields[fieldName] + + // switch out the SDK Object Definition for this field if needed + for _, matcher := range Matchers { + if matcher.IsMatch(field, input) { + field.ObjectDefinition = matcher.ReplacementObjectDefinition() + break + } + } + + fields[fieldName] = field + } + model.Fields = fields + output.Models[modelName] = model + } + + return output +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema/interface.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema/interface.go new file mode 100644 index 00000000000..35be78a01a9 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema/interface.go @@ -0,0 +1,14 @@ +package commonschema + +import sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" + +type Matcher interface { + // IsMatch returns whether the SDKField implements a CommonSchema ObjectDefinition + // meaning that the ObjectDefinition used by this SDKField should be replaced by the SDKObjectDefinition + // defined in ReplacementObjectDefinition. + IsMatch(field sdkModels.SDKField, resource sdkModels.APIResource) bool + + // ReplacementObjectDefinition returns the replacement SDKObjectDefinition which should be used + // in place of the parsed SDKObjectDefinition for this SDKField. + ReplacementObjectDefinition() sdkModels.SDKObjectDefinition +} diff --git a/tools/importer-rest-api-specs/components/parser/commonschema/custom_field_edge_zone.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema/matcher_edge_zone.go similarity index 70% rename from tools/importer-rest-api-specs/components/parser/commonschema/custom_field_edge_zone.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema/matcher_edge_zone.go index fd28efe720e..fc8ef38c578 100644 --- a/tools/importer-rest-api-specs/components/parser/commonschema/custom_field_edge_zone.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema/matcher_edge_zone.go @@ -7,27 +7,20 @@ import ( "strings" sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/components/parser/internal" ) -var _ customFieldMatcher = edgeZoneFieldMatcher{} +var _ Matcher = edgeZoneFieldMatcher{} type edgeZoneFieldMatcher struct { } -func (e edgeZoneFieldMatcher) ReplacementObjectDefinition() sdkModels.SDKObjectDefinition { - return sdkModels.SDKObjectDefinition{ - Type: sdkModels.EdgeZoneSDKObjectDefinitionType, - } -} - -func (e edgeZoneFieldMatcher) IsMatch(field sdkModels.SDKField, known internal.ParseResult) bool { +func (edgeZoneFieldMatcher) IsMatch(field sdkModels.SDKField, resource sdkModels.APIResource) bool { if field.ObjectDefinition.Type != sdkModels.ReferenceSDKObjectDefinitionType { return false } // retrieve the model from the reference - model, ok := known.Models[*field.ObjectDefinition.ReferenceName] + model, ok := resource.Models[*field.ObjectDefinition.ReferenceName] if !ok { return false } @@ -45,7 +38,7 @@ func (e edgeZoneFieldMatcher) IsMatch(field sdkModels.SDKField, known internal.P if fieldVal.ObjectDefinition.Type != sdkModels.ReferenceSDKObjectDefinitionType { continue } - constant, ok := known.Constants[*fieldVal.ObjectDefinition.ReferenceName] + constant, ok := resource.Constants[*fieldVal.ObjectDefinition.ReferenceName] if !ok || len(constant.Values) != 1 { continue } @@ -63,3 +56,9 @@ func (e edgeZoneFieldMatcher) IsMatch(field sdkModels.SDKField, known internal.P return hasName && hasMatchingType } + +func (edgeZoneFieldMatcher) ReplacementObjectDefinition() sdkModels.SDKObjectDefinition { + return sdkModels.SDKObjectDefinition{ + Type: sdkModels.EdgeZoneSDKObjectDefinitionType, + } +} diff --git a/tools/importer-rest-api-specs/components/parser/commonschema/custom_field_identity_legacy_system_and_user_assigned_list.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema/matcher_identity_legacy_system_and_user_assigned_list.go similarity index 84% rename from tools/importer-rest-api-specs/components/parser/commonschema/custom_field_identity_legacy_system_and_user_assigned_list.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema/matcher_identity_legacy_system_and_user_assigned_list.go index bb4020ef297..4a939dbd315 100644 --- a/tools/importer-rest-api-specs/components/parser/commonschema/custom_field_identity_legacy_system_and_user_assigned_list.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema/matcher_identity_legacy_system_and_user_assigned_list.go @@ -7,26 +7,19 @@ import ( "strings" sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/components/parser/internal" ) -var _ customFieldMatcher = legacySystemAndUserAssignedIdentityListMatcher{} +var _ Matcher = legacySystemAndUserAssignedIdentityListMatcher{} type legacySystemAndUserAssignedIdentityListMatcher struct{} -func (legacySystemAndUserAssignedIdentityListMatcher) ReplacementObjectDefinition() sdkModels.SDKObjectDefinition { - return sdkModels.SDKObjectDefinition{ - Type: sdkModels.LegacySystemAndUserAssignedIdentityListSDKObjectDefinitionType, - } -} - -func (legacySystemAndUserAssignedIdentityListMatcher) IsMatch(field sdkModels.SDKField, known internal.ParseResult) bool { +func (legacySystemAndUserAssignedIdentityListMatcher) IsMatch(field sdkModels.SDKField, resource sdkModels.APIResource) bool { if field.ObjectDefinition.Type != sdkModels.ReferenceSDKObjectDefinitionType { return false } // retrieve the model from the reference - model, ok := known.Models[*field.ObjectDefinition.ReferenceName] + model, ok := resource.Models[*field.ObjectDefinition.ReferenceName] if !ok { return false } @@ -64,7 +57,7 @@ func (legacySystemAndUserAssignedIdentityListMatcher) IsMatch(field sdkModels.SD if fieldVal.ObjectDefinition.Type != sdkModels.ReferenceSDKObjectDefinitionType { continue } - constant, ok := known.Constants[*fieldVal.ObjectDefinition.ReferenceName] + constant, ok := resource.Constants[*fieldVal.ObjectDefinition.ReferenceName] if !ok { continue } @@ -83,3 +76,9 @@ func (legacySystemAndUserAssignedIdentityListMatcher) IsMatch(field sdkModels.SD return hasUserAssignedIdentities && hasMatchingType && hasPrincipalId && hasTenantId } + +func (legacySystemAndUserAssignedIdentityListMatcher) ReplacementObjectDefinition() sdkModels.SDKObjectDefinition { + return sdkModels.SDKObjectDefinition{ + Type: sdkModels.LegacySystemAndUserAssignedIdentityListSDKObjectDefinitionType, + } +} diff --git a/tools/importer-rest-api-specs/components/parser/commonschema/custom_field_identity_legacy_system_and_user_assigned_map.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema/matcher_identity_legacy_system_and_user_assigned_map.go similarity index 87% rename from tools/importer-rest-api-specs/components/parser/commonschema/custom_field_identity_legacy_system_and_user_assigned_map.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema/matcher_identity_legacy_system_and_user_assigned_map.go index 8493e1e078d..b4795b3485a 100644 --- a/tools/importer-rest-api-specs/components/parser/commonschema/custom_field_identity_legacy_system_and_user_assigned_map.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema/matcher_identity_legacy_system_and_user_assigned_map.go @@ -7,26 +7,19 @@ import ( "strings" sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/components/parser/internal" ) -var _ customFieldMatcher = legacySystemAndUserAssignedIdentityMapMatcher{} +var _ Matcher = legacySystemAndUserAssignedIdentityMapMatcher{} type legacySystemAndUserAssignedIdentityMapMatcher struct{} -func (legacySystemAndUserAssignedIdentityMapMatcher) ReplacementObjectDefinition() sdkModels.SDKObjectDefinition { - return sdkModels.SDKObjectDefinition{ - Type: sdkModels.LegacySystemAndUserAssignedIdentityMapSDKObjectDefinitionType, - } -} - -func (legacySystemAndUserAssignedIdentityMapMatcher) IsMatch(field sdkModels.SDKField, known internal.ParseResult) bool { +func (legacySystemAndUserAssignedIdentityMapMatcher) IsMatch(field sdkModels.SDKField, resource sdkModels.APIResource) bool { if field.ObjectDefinition.Type != sdkModels.ReferenceSDKObjectDefinitionType { return false } // retrieve the model from the reference - model, ok := known.Models[*field.ObjectDefinition.ReferenceName] + model, ok := resource.Models[*field.ObjectDefinition.ReferenceName] if !ok { return false } @@ -64,7 +57,7 @@ func (legacySystemAndUserAssignedIdentityMapMatcher) IsMatch(field sdkModels.SDK continue } - inlinedModel, ok := known.Models[*fieldVal.ObjectDefinition.NestedItem.ReferenceName] + inlinedModel, ok := resource.Models[*fieldVal.ObjectDefinition.NestedItem.ReferenceName] if !ok { continue } @@ -101,7 +94,7 @@ func (legacySystemAndUserAssignedIdentityMapMatcher) IsMatch(field sdkModels.SDK if fieldVal.ObjectDefinition.Type != sdkModels.ReferenceSDKObjectDefinitionType { continue } - constant, ok := known.Constants[*fieldVal.ObjectDefinition.ReferenceName] + constant, ok := resource.Constants[*fieldVal.ObjectDefinition.ReferenceName] if !ok { continue } @@ -120,3 +113,9 @@ func (legacySystemAndUserAssignedIdentityMapMatcher) IsMatch(field sdkModels.SDK return hasUserAssignedIdentities && hasMatchingType && hasPrincipalId && hasTenantId } + +func (legacySystemAndUserAssignedIdentityMapMatcher) ReplacementObjectDefinition() sdkModels.SDKObjectDefinition { + return sdkModels.SDKObjectDefinition{ + Type: sdkModels.LegacySystemAndUserAssignedIdentityMapSDKObjectDefinitionType, + } +} diff --git a/tools/importer-rest-api-specs/components/parser/commonschema/custom_field_identity_system_and_user_assigned_list.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema/matcher_identity_system_and_user_assigned_list.go similarity index 85% rename from tools/importer-rest-api-specs/components/parser/commonschema/custom_field_identity_system_and_user_assigned_list.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema/matcher_identity_system_and_user_assigned_list.go index de709d19553..b4860253b59 100644 --- a/tools/importer-rest-api-specs/components/parser/commonschema/custom_field_identity_system_and_user_assigned_list.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema/matcher_identity_system_and_user_assigned_list.go @@ -7,26 +7,19 @@ import ( "strings" sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/components/parser/internal" ) -var _ customFieldMatcher = systemAndUserAssignedIdentityListMatcher{} +var _ Matcher = systemAndUserAssignedIdentityListMatcher{} type systemAndUserAssignedIdentityListMatcher struct{} -func (systemAndUserAssignedIdentityListMatcher) ReplacementObjectDefinition() sdkModels.SDKObjectDefinition { - return sdkModels.SDKObjectDefinition{ - Type: sdkModels.SystemAndUserAssignedIdentityListSDKObjectDefinitionType, - } -} - -func (systemAndUserAssignedIdentityListMatcher) IsMatch(field sdkModels.SDKField, known internal.ParseResult) bool { +func (systemAndUserAssignedIdentityListMatcher) IsMatch(field sdkModels.SDKField, resource sdkModels.APIResource) bool { if field.ObjectDefinition.Type != sdkModels.ReferenceSDKObjectDefinitionType { return false } // retrieve the model from the reference - model, ok := known.Models[*field.ObjectDefinition.ReferenceName] + model, ok := resource.Models[*field.ObjectDefinition.ReferenceName] if !ok { return false } @@ -64,7 +57,7 @@ func (systemAndUserAssignedIdentityListMatcher) IsMatch(field sdkModels.SDKField if fieldVal.ObjectDefinition.Type != sdkModels.ReferenceSDKObjectDefinitionType { continue } - constant, ok := known.Constants[*fieldVal.ObjectDefinition.ReferenceName] + constant, ok := resource.Constants[*fieldVal.ObjectDefinition.ReferenceName] if !ok { continue } @@ -83,3 +76,9 @@ func (systemAndUserAssignedIdentityListMatcher) IsMatch(field sdkModels.SDKField return hasUserAssignedIdentities && hasMatchingType && hasPrincipalId && hasTenantId } + +func (systemAndUserAssignedIdentityListMatcher) ReplacementObjectDefinition() sdkModels.SDKObjectDefinition { + return sdkModels.SDKObjectDefinition{ + Type: sdkModels.SystemAndUserAssignedIdentityListSDKObjectDefinitionType, + } +} diff --git a/tools/importer-rest-api-specs/components/parser/commonschema/custom_field_identity_system_and_user_assigned_map.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema/matcher_identity_system_and_user_assigned_map.go similarity index 86% rename from tools/importer-rest-api-specs/components/parser/commonschema/custom_field_identity_system_and_user_assigned_map.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema/matcher_identity_system_and_user_assigned_map.go index 96e1128fdc9..e3b36853b83 100644 --- a/tools/importer-rest-api-specs/components/parser/commonschema/custom_field_identity_system_and_user_assigned_map.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema/matcher_identity_system_and_user_assigned_map.go @@ -7,26 +7,19 @@ import ( "strings" sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/components/parser/internal" ) -var _ customFieldMatcher = systemAndUserAssignedIdentityMapMatcher{} +var _ Matcher = systemAndUserAssignedIdentityMapMatcher{} type systemAndUserAssignedIdentityMapMatcher struct{} -func (systemAndUserAssignedIdentityMapMatcher) ReplacementObjectDefinition() sdkModels.SDKObjectDefinition { - return sdkModels.SDKObjectDefinition{ - Type: sdkModels.SystemAndUserAssignedIdentityMapSDKObjectDefinitionType, - } -} - -func (systemAndUserAssignedIdentityMapMatcher) IsMatch(field sdkModels.SDKField, known internal.ParseResult) bool { +func (systemAndUserAssignedIdentityMapMatcher) IsMatch(field sdkModels.SDKField, resource sdkModels.APIResource) bool { if field.ObjectDefinition.Type != sdkModels.ReferenceSDKObjectDefinitionType { return false } // retrieve the model from the reference - model, ok := known.Models[*field.ObjectDefinition.ReferenceName] + model, ok := resource.Models[*field.ObjectDefinition.ReferenceName] if !ok { return false } @@ -56,7 +49,7 @@ func (systemAndUserAssignedIdentityMapMatcher) IsMatch(field sdkModels.SDKField, continue } - inlinedModel, ok := known.Models[*fieldVal.ObjectDefinition.NestedItem.ReferenceName] + inlinedModel, ok := resource.Models[*fieldVal.ObjectDefinition.NestedItem.ReferenceName] if !ok { continue } @@ -93,7 +86,7 @@ func (systemAndUserAssignedIdentityMapMatcher) IsMatch(field sdkModels.SDKField, if fieldVal.ObjectDefinition.Type != sdkModels.ReferenceSDKObjectDefinitionType { continue } - constant, ok := known.Constants[*fieldVal.ObjectDefinition.ReferenceName] + constant, ok := resource.Constants[*fieldVal.ObjectDefinition.ReferenceName] if !ok { continue } @@ -112,3 +105,9 @@ func (systemAndUserAssignedIdentityMapMatcher) IsMatch(field sdkModels.SDKField, return hasUserAssignedIdentities && hasMatchingType && hasPrincipalId && hasTenantId } + +func (systemAndUserAssignedIdentityMapMatcher) ReplacementObjectDefinition() sdkModels.SDKObjectDefinition { + return sdkModels.SDKObjectDefinition{ + Type: sdkModels.SystemAndUserAssignedIdentityMapSDKObjectDefinitionType, + } +} diff --git a/tools/importer-rest-api-specs/components/parser/commonschema/custom_field_identity_system_assigned.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema/matcher_identity_system_assigned.go similarity index 87% rename from tools/importer-rest-api-specs/components/parser/commonschema/custom_field_identity_system_assigned.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema/matcher_identity_system_assigned.go index 6d151782887..93252cd3e40 100644 --- a/tools/importer-rest-api-specs/components/parser/commonschema/custom_field_identity_system_assigned.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema/matcher_identity_system_assigned.go @@ -8,26 +8,19 @@ import ( "strings" sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/components/parser/internal" ) -var _ customFieldMatcher = systemAssignedIdentityMatcher{} +var _ Matcher = systemAssignedIdentityMatcher{} type systemAssignedIdentityMatcher struct{} -func (systemAssignedIdentityMatcher) ReplacementObjectDefinition() sdkModels.SDKObjectDefinition { - return sdkModels.SDKObjectDefinition{ - Type: sdkModels.SystemAssignedIdentitySDKObjectDefinitionType, - } -} - -func (systemAssignedIdentityMatcher) IsMatch(field sdkModels.SDKField, known internal.ParseResult) bool { +func (systemAssignedIdentityMatcher) IsMatch(field sdkModels.SDKField, resource sdkModels.APIResource) bool { if field.ObjectDefinition.Type != sdkModels.ReferenceSDKObjectDefinitionType { return false } // retrieve the model from the reference - model, ok := known.Models[*field.ObjectDefinition.ReferenceName] + model, ok := resource.Models[*field.ObjectDefinition.ReferenceName] if !ok { return false } @@ -51,7 +44,7 @@ func (systemAssignedIdentityMatcher) IsMatch(field sdkModels.SDKField, known int if fieldVal.ObjectDefinition.Type != sdkModels.ReferenceSDKObjectDefinitionType { continue } - constant, ok := known.Constants[*fieldVal.ObjectDefinition.ReferenceName] + constant, ok := resource.Constants[*fieldVal.ObjectDefinition.ReferenceName] if !ok { continue } @@ -69,6 +62,12 @@ func (systemAssignedIdentityMatcher) IsMatch(field sdkModels.SDKField, known int return hasMatchingType && hasPrincipalId && hasTenantId } +func (systemAssignedIdentityMatcher) ReplacementObjectDefinition() sdkModels.SDKObjectDefinition { + return sdkModels.SDKObjectDefinition{ + Type: sdkModels.SystemAssignedIdentitySDKObjectDefinitionType, + } +} + func validateIdentityConstantValues(input sdkModels.SDKConstant, expected map[string]string) bool { if input.Type != sdkModels.StringSDKConstantType { return false diff --git a/tools/importer-rest-api-specs/components/parser/commonschema/custom_field_identity_system_or_user_assigned_list.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema/matcher_identity_system_or_user_assigned_list.go similarity index 84% rename from tools/importer-rest-api-specs/components/parser/commonschema/custom_field_identity_system_or_user_assigned_list.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema/matcher_identity_system_or_user_assigned_list.go index 35ebbe64736..abaf82af462 100644 --- a/tools/importer-rest-api-specs/components/parser/commonschema/custom_field_identity_system_or_user_assigned_list.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema/matcher_identity_system_or_user_assigned_list.go @@ -7,26 +7,19 @@ import ( "strings" sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/components/parser/internal" ) -var _ customFieldMatcher = systemOrUserAssignedIdentityListMatcher{} +var _ Matcher = systemOrUserAssignedIdentityListMatcher{} type systemOrUserAssignedIdentityListMatcher struct{} -func (systemOrUserAssignedIdentityListMatcher) ReplacementObjectDefinition() sdkModels.SDKObjectDefinition { - return sdkModels.SDKObjectDefinition{ - Type: sdkModels.SystemOrUserAssignedIdentityListSDKObjectDefinitionType, - } -} - -func (systemOrUserAssignedIdentityListMatcher) IsMatch(field sdkModels.SDKField, known internal.ParseResult) bool { +func (systemOrUserAssignedIdentityListMatcher) IsMatch(field sdkModels.SDKField, resource sdkModels.APIResource) bool { if field.ObjectDefinition.Type != sdkModels.ReferenceSDKObjectDefinitionType { return false } // retrieve the model from the reference - model, ok := known.Models[*field.ObjectDefinition.ReferenceName] + model, ok := resource.Models[*field.ObjectDefinition.ReferenceName] if !ok { return false } @@ -64,7 +57,7 @@ func (systemOrUserAssignedIdentityListMatcher) IsMatch(field sdkModels.SDKField, if fieldVal.ObjectDefinition.Type != sdkModels.ReferenceSDKObjectDefinitionType { continue } - constant, ok := known.Constants[*fieldVal.ObjectDefinition.ReferenceName] + constant, ok := resource.Constants[*fieldVal.ObjectDefinition.ReferenceName] if !ok { continue } @@ -82,3 +75,9 @@ func (systemOrUserAssignedIdentityListMatcher) IsMatch(field sdkModels.SDKField, return hasUserAssignedIdentities && hasMatchingType && hasPrincipalId && hasTenantId } + +func (systemOrUserAssignedIdentityListMatcher) ReplacementObjectDefinition() sdkModels.SDKObjectDefinition { + return sdkModels.SDKObjectDefinition{ + Type: sdkModels.SystemOrUserAssignedIdentityListSDKObjectDefinitionType, + } +} diff --git a/tools/importer-rest-api-specs/components/parser/commonschema/custom_field_identity_system_or_user_assigned_map.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema/matcher_identity_system_or_user_assigned_map.go similarity index 86% rename from tools/importer-rest-api-specs/components/parser/commonschema/custom_field_identity_system_or_user_assigned_map.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema/matcher_identity_system_or_user_assigned_map.go index 11eaa780054..facfd55308e 100644 --- a/tools/importer-rest-api-specs/components/parser/commonschema/custom_field_identity_system_or_user_assigned_map.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema/matcher_identity_system_or_user_assigned_map.go @@ -7,26 +7,19 @@ import ( "strings" sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/components/parser/internal" ) -var _ customFieldMatcher = systemOrUserAssignedIdentityMapMatcher{} +var _ Matcher = systemOrUserAssignedIdentityMapMatcher{} type systemOrUserAssignedIdentityMapMatcher struct{} -func (systemOrUserAssignedIdentityMapMatcher) ReplacementObjectDefinition() sdkModels.SDKObjectDefinition { - return sdkModels.SDKObjectDefinition{ - Type: sdkModels.SystemOrUserAssignedIdentityMapSDKObjectDefinitionType, - } -} - -func (systemOrUserAssignedIdentityMapMatcher) IsMatch(field sdkModels.SDKField, known internal.ParseResult) bool { +func (systemOrUserAssignedIdentityMapMatcher) IsMatch(field sdkModels.SDKField, resource sdkModels.APIResource) bool { if field.ObjectDefinition.Type != sdkModels.ReferenceSDKObjectDefinitionType { return false } // retrieve the model from the reference - model, ok := known.Models[*field.ObjectDefinition.ReferenceName] + model, ok := resource.Models[*field.ObjectDefinition.ReferenceName] if !ok { return false } @@ -56,7 +49,7 @@ func (systemOrUserAssignedIdentityMapMatcher) IsMatch(field sdkModels.SDKField, continue } - inlinedModel, ok := known.Models[*fieldVal.ObjectDefinition.NestedItem.ReferenceName] + inlinedModel, ok := resource.Models[*fieldVal.ObjectDefinition.NestedItem.ReferenceName] if !ok { continue } @@ -93,7 +86,7 @@ func (systemOrUserAssignedIdentityMapMatcher) IsMatch(field sdkModels.SDKField, if fieldVal.ObjectDefinition.Type != sdkModels.ReferenceSDKObjectDefinitionType { continue } - constant, ok := known.Constants[*fieldVal.ObjectDefinition.ReferenceName] + constant, ok := resource.Constants[*fieldVal.ObjectDefinition.ReferenceName] if !ok { continue } @@ -116,3 +109,9 @@ func (systemOrUserAssignedIdentityMapMatcher) IsMatch(field sdkModels.SDKField, return hasUserAssignedIdentities && hasMatchingType && hasPrincipalId && hasTenantId } + +func (systemOrUserAssignedIdentityMapMatcher) ReplacementObjectDefinition() sdkModels.SDKObjectDefinition { + return sdkModels.SDKObjectDefinition{ + Type: sdkModels.SystemOrUserAssignedIdentityMapSDKObjectDefinitionType, + } +} diff --git a/tools/importer-rest-api-specs/components/parser/commonschema/custom_field_identity_user_assigned_list.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema/matcher_identity_user_assigned_list.go similarity index 85% rename from tools/importer-rest-api-specs/components/parser/commonschema/custom_field_identity_user_assigned_list.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema/matcher_identity_user_assigned_list.go index eb1138a7231..573472d8255 100644 --- a/tools/importer-rest-api-specs/components/parser/commonschema/custom_field_identity_user_assigned_list.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema/matcher_identity_user_assigned_list.go @@ -7,26 +7,19 @@ import ( "strings" sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/components/parser/internal" ) -var _ customFieldMatcher = userAssignedIdentityListMatcher{} +var _ Matcher = userAssignedIdentityListMatcher{} type userAssignedIdentityListMatcher struct{} -func (userAssignedIdentityListMatcher) ReplacementObjectDefinition() sdkModels.SDKObjectDefinition { - return sdkModels.SDKObjectDefinition{ - Type: sdkModels.UserAssignedIdentityListSDKObjectDefinitionType, - } -} - -func (userAssignedIdentityListMatcher) IsMatch(field sdkModels.SDKField, known internal.ParseResult) bool { +func (userAssignedIdentityListMatcher) IsMatch(field sdkModels.SDKField, resource sdkModels.APIResource) bool { if field.ObjectDefinition.Type != sdkModels.ReferenceSDKObjectDefinitionType { return false } // retrieve the model from the reference - model, ok := known.Models[*field.ObjectDefinition.ReferenceName] + model, ok := resource.Models[*field.ObjectDefinition.ReferenceName] if !ok { return false } @@ -54,7 +47,7 @@ func (userAssignedIdentityListMatcher) IsMatch(field sdkModels.SDKField, known i if fieldVal.ObjectDefinition.Type != sdkModels.ReferenceSDKObjectDefinitionType { continue } - constant, ok := known.Constants[*fieldVal.ObjectDefinition.ReferenceName] + constant, ok := resource.Constants[*fieldVal.ObjectDefinition.ReferenceName] if !ok { continue } @@ -73,3 +66,9 @@ func (userAssignedIdentityListMatcher) IsMatch(field sdkModels.SDKField, known i return hasUserAssignedIdentities } + +func (userAssignedIdentityListMatcher) ReplacementObjectDefinition() sdkModels.SDKObjectDefinition { + return sdkModels.SDKObjectDefinition{ + Type: sdkModels.UserAssignedIdentityListSDKObjectDefinitionType, + } +} diff --git a/tools/importer-rest-api-specs/components/parser/commonschema/custom_field_identity_user_assigned_map.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema/matcher_identity_user_assigned_map.go similarity index 88% rename from tools/importer-rest-api-specs/components/parser/commonschema/custom_field_identity_user_assigned_map.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema/matcher_identity_user_assigned_map.go index 18d5ef25d32..52d24f7ac17 100644 --- a/tools/importer-rest-api-specs/components/parser/commonschema/custom_field_identity_user_assigned_map.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema/matcher_identity_user_assigned_map.go @@ -7,26 +7,19 @@ import ( "strings" sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/components/parser/internal" ) -var _ customFieldMatcher = userAssignedIdentityMapMatcher{} +var _ Matcher = userAssignedIdentityMapMatcher{} type userAssignedIdentityMapMatcher struct{} -func (userAssignedIdentityMapMatcher) ReplacementObjectDefinition() sdkModels.SDKObjectDefinition { - return sdkModels.SDKObjectDefinition{ - Type: sdkModels.UserAssignedIdentityMapSDKObjectDefinitionType, - } -} - -func (userAssignedIdentityMapMatcher) IsMatch(field sdkModels.SDKField, known internal.ParseResult) bool { +func (userAssignedIdentityMapMatcher) IsMatch(field sdkModels.SDKField, resource sdkModels.APIResource) bool { if field.ObjectDefinition.Type != sdkModels.ReferenceSDKObjectDefinitionType { return false } // retrieve the model from the reference - model, ok := known.Models[*field.ObjectDefinition.ReferenceName] + model, ok := resource.Models[*field.ObjectDefinition.ReferenceName] if !ok { return false } @@ -44,7 +37,7 @@ func (userAssignedIdentityMapMatcher) IsMatch(field sdkModels.SDKField, known in continue } - inlinedModel, ok := known.Models[*fieldVal.ObjectDefinition.NestedItem.ReferenceName] + inlinedModel, ok := resource.Models[*fieldVal.ObjectDefinition.NestedItem.ReferenceName] if !ok { continue } @@ -82,7 +75,7 @@ func (userAssignedIdentityMapMatcher) IsMatch(field sdkModels.SDKField, known in if fieldVal.ObjectDefinition.Type != sdkModels.ReferenceSDKObjectDefinitionType { continue } - constant, ok := known.Constants[*fieldVal.ObjectDefinition.ReferenceName] + constant, ok := resource.Constants[*fieldVal.ObjectDefinition.ReferenceName] if !ok { continue } @@ -106,3 +99,9 @@ func (userAssignedIdentityMapMatcher) IsMatch(field sdkModels.SDKField, known in return hasUserAssignedIdentities && hasTypeMatch } + +func (userAssignedIdentityMapMatcher) ReplacementObjectDefinition() sdkModels.SDKObjectDefinition { + return sdkModels.SDKObjectDefinition{ + Type: sdkModels.UserAssignedIdentityMapSDKObjectDefinitionType, + } +} diff --git a/tools/importer-rest-api-specs/components/parser/commonschema/custom_field_location.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema/matcher_location.go similarity index 58% rename from tools/importer-rest-api-specs/components/parser/commonschema/custom_field_location.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema/matcher_location.go index 79a342fe30e..12160883aad 100644 --- a/tools/importer-rest-api-specs/components/parser/commonschema/custom_field_location.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema/matcher_location.go @@ -7,19 +7,18 @@ import ( "strings" sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/components/parser/internal" ) -var _ customFieldMatcher = locationMatcher{} +var _ Matcher = locationMatcher{} type locationMatcher struct{} -func (l locationMatcher) ReplacementObjectDefinition() sdkModels.SDKObjectDefinition { +func (locationMatcher) IsMatch(field sdkModels.SDKField, _ sdkModels.APIResource) bool { + return strings.EqualFold(field.JsonName, "location") && field.ObjectDefinition.Type == sdkModels.StringSDKObjectDefinitionType +} + +func (locationMatcher) ReplacementObjectDefinition() sdkModels.SDKObjectDefinition { return sdkModels.SDKObjectDefinition{ Type: sdkModels.LocationSDKObjectDefinitionType, } } - -func (l locationMatcher) IsMatch(field sdkModels.SDKField, _ internal.ParseResult) bool { - return strings.EqualFold(field.JsonName, "location") && field.ObjectDefinition.Type == sdkModels.StringSDKObjectDefinitionType -} diff --git a/tools/importer-rest-api-specs/components/parser/commonschema/custom_field_system_data.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema/matcher_system_data.go similarity index 90% rename from tools/importer-rest-api-specs/components/parser/commonschema/custom_field_system_data.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema/matcher_system_data.go index ed09a3cb628..f7fc9c4dbaa 100644 --- a/tools/importer-rest-api-specs/components/parser/commonschema/custom_field_system_data.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema/matcher_system_data.go @@ -8,26 +8,19 @@ import ( "strings" sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/components/parser/internal" ) -var _ customFieldMatcher = systemDataMatcher{} +var _ Matcher = systemDataMatcher{} type systemDataMatcher struct{} -func (systemDataMatcher) ReplacementObjectDefinition() sdkModels.SDKObjectDefinition { - return sdkModels.SDKObjectDefinition{ - Type: sdkModels.SystemDataSDKObjectDefinitionType, - } -} - -func (systemDataMatcher) IsMatch(field sdkModels.SDKField, known internal.ParseResult) bool { +func (systemDataMatcher) IsMatch(field sdkModels.SDKField, resource sdkModels.APIResource) bool { if field.ObjectDefinition.Type != sdkModels.ReferenceSDKObjectDefinitionType { return false } // retrieve the model from the reference - model, ok := known.Models[*field.ObjectDefinition.ReferenceName] + model, ok := resource.Models[*field.ObjectDefinition.ReferenceName] if !ok { return false } @@ -80,7 +73,7 @@ func (systemDataMatcher) IsMatch(field sdkModels.SDKField, known internal.ParseR "ManagedIdentity": "ManagedIdentity", "Key": "Key", } - constant, ok := known.Constants[*fieldVal.ObjectDefinition.ReferenceName] + constant, ok := resource.Constants[*fieldVal.ObjectDefinition.ReferenceName] if !ok { continue } @@ -108,7 +101,7 @@ func (systemDataMatcher) IsMatch(field sdkModels.SDKField, known internal.ParseR "ManagedIdentity": "ManagedIdentity", "Key": "Key", } - constant, ok := known.Constants[*fieldVal.ObjectDefinition.ReferenceName] + constant, ok := resource.Constants[*fieldVal.ObjectDefinition.ReferenceName] if !ok { continue } @@ -124,6 +117,12 @@ func (systemDataMatcher) IsMatch(field sdkModels.SDKField, known internal.ParseR return hasCreatedByType && hasCreatedBy && hasLastModifiedbyType && hasLastModifiedAt && hasLastModifiedBy && hasCreatedAt } +func (systemDataMatcher) ReplacementObjectDefinition() sdkModels.SDKObjectDefinition { + return sdkModels.SDKObjectDefinition{ + Type: sdkModels.SystemDataSDKObjectDefinitionType, + } +} + func validateSystemDataConstantValues(input sdkModels.SDKConstant, expected map[string]string) bool { if input.Type != sdkModels.StringSDKConstantType { return false diff --git a/tools/importer-rest-api-specs/components/parser/commonschema/custom_field_tags.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema/matcher_tags.go similarity index 75% rename from tools/importer-rest-api-specs/components/parser/commonschema/custom_field_tags.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema/matcher_tags.go index 1f41b0f51cb..2a581c4467a 100644 --- a/tools/importer-rest-api-specs/components/parser/commonschema/custom_field_tags.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema/matcher_tags.go @@ -7,21 +7,20 @@ import ( "strings" sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/components/parser/internal" ) -var _ customFieldMatcher = tagsMatcher{} +var _ Matcher = tagsMatcher{} type tagsMatcher struct{} +func (tagsMatcher) IsMatch(field sdkModels.SDKField, _ sdkModels.APIResource) bool { + nameMatches := strings.EqualFold(field.JsonName, "tags") + typeMatches := field.ObjectDefinition.Type == sdkModels.DictionarySDKObjectDefinitionType && field.ObjectDefinition.NestedItem.Type == sdkModels.StringSDKObjectDefinitionType + return nameMatches && typeMatches +} + func (tagsMatcher) ReplacementObjectDefinition() sdkModels.SDKObjectDefinition { return sdkModels.SDKObjectDefinition{ Type: sdkModels.TagsSDKObjectDefinitionType, } } - -func (tagsMatcher) IsMatch(field sdkModels.SDKField, _ internal.ParseResult) bool { - nameMatches := strings.EqualFold(field.JsonName, "tags") - typeMatches := field.ObjectDefinition.Type == sdkModels.DictionarySDKObjectDefinitionType && field.ObjectDefinition.NestedItem.Type == sdkModels.StringSDKObjectDefinitionType - return nameMatches && typeMatches -} diff --git a/tools/importer-rest-api-specs/components/parser/commonschema/custom_field_zone.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema/matcher_zone.go similarity index 74% rename from tools/importer-rest-api-specs/components/parser/commonschema/custom_field_zone.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema/matcher_zone.go index 64d7e7de326..eb6aa04bcab 100644 --- a/tools/importer-rest-api-specs/components/parser/commonschema/custom_field_zone.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema/matcher_zone.go @@ -7,22 +7,21 @@ import ( "strings" sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/components/parser/internal" ) -var _ customFieldMatcher = zoneFieldMatcher{} +var _ Matcher = zoneFieldMatcher{} type zoneFieldMatcher struct { } +func (zoneFieldMatcher) IsMatch(field sdkModels.SDKField, _ sdkModels.APIResource) bool { + nameMatches := strings.EqualFold(field.JsonName, "availabilityZone") || strings.EqualFold(field.JsonName, "zone") + typeMatches := field.ObjectDefinition.Type == sdkModels.StringSDKObjectDefinitionType + return nameMatches && typeMatches +} + func (zoneFieldMatcher) ReplacementObjectDefinition() sdkModels.SDKObjectDefinition { return sdkModels.SDKObjectDefinition{ Type: sdkModels.ZoneSDKObjectDefinitionType, } } - -func (zoneFieldMatcher) IsMatch(field sdkModels.SDKField, _ internal.ParseResult) bool { - nameMatches := strings.EqualFold(field.JsonName, "availabilityZone") || strings.EqualFold(field.JsonName, "zone") - typeMatches := field.ObjectDefinition.Type == sdkModels.StringSDKObjectDefinitionType - return nameMatches && typeMatches -} diff --git a/tools/importer-rest-api-specs/components/parser/commonschema/custom_field_zones.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema/matcher_zones.go similarity index 77% rename from tools/importer-rest-api-specs/components/parser/commonschema/custom_field_zones.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema/matcher_zones.go index 1c511c2abab..0578c1c5f7e 100644 --- a/tools/importer-rest-api-specs/components/parser/commonschema/custom_field_zones.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema/matcher_zones.go @@ -7,22 +7,21 @@ import ( "strings" sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/components/parser/internal" ) -var _ customFieldMatcher = zonesFieldMatcher{} +var _ Matcher = zonesFieldMatcher{} type zonesFieldMatcher struct { } +func (zonesFieldMatcher) IsMatch(field sdkModels.SDKField, _ sdkModels.APIResource) bool { + nameMatches := strings.EqualFold(field.JsonName, "availabilityZones") || strings.EqualFold(field.JsonName, "zones") + typesMatch := field.ObjectDefinition.Type == sdkModels.ListSDKObjectDefinitionType && field.ObjectDefinition.NestedItem != nil && field.ObjectDefinition.NestedItem.Type == sdkModels.StringSDKObjectDefinitionType + return nameMatches && typesMatch +} + func (zonesFieldMatcher) ReplacementObjectDefinition() sdkModels.SDKObjectDefinition { return sdkModels.SDKObjectDefinition{ Type: sdkModels.ZonesSDKObjectDefinitionType, } } - -func (zonesFieldMatcher) IsMatch(field sdkModels.SDKField, _ internal.ParseResult) bool { - nameMatches := strings.EqualFold(field.JsonName, "availabilityZones") || strings.EqualFold(field.JsonName, "zones") - typesMatch := field.ObjectDefinition.Type == sdkModels.ListSDKObjectDefinitionType && field.ObjectDefinition.NestedItem != nil && field.ObjectDefinition.NestedItem.Type == sdkModels.StringSDKObjectDefinitionType - return nameMatches && typesMatch -} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema/matchers.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema/matchers.go new file mode 100644 index 00000000000..d6c41016b8b --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema/matchers.go @@ -0,0 +1,19 @@ +package commonschema + +var Matchers = []Matcher{ + edgeZoneFieldMatcher{}, + locationMatcher{}, + legacySystemAndUserAssignedIdentityListMatcher{}, + legacySystemAndUserAssignedIdentityMapMatcher{}, + systemAndUserAssignedIdentityListMatcher{}, + systemAndUserAssignedIdentityMapMatcher{}, + systemAssignedIdentityMatcher{}, + systemDataMatcher{}, + systemOrUserAssignedIdentityListMatcher{}, + systemOrUserAssignedIdentityMapMatcher{}, + tagsMatcher{}, + userAssignedIdentityListMatcher{}, + userAssignedIdentityMapMatcher{}, + zoneFieldMatcher{}, + zonesFieldMatcher{}, +} diff --git a/tools/importer-rest-api-specs/components/parser/models_commonschema_test.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema_test.go similarity index 81% rename from tools/importer-rest-api-specs/components/parser/models_commonschema_test.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema_test.go index a0337519f4e..b0b1fb2352f 100644 --- a/tools/importer-rest-api-specs/components/parser/models_commonschema_test.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema_test.go @@ -1,14 +1,14 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package parser +package parser_test import ( "testing" "github.com/hashicorp/go-azure-helpers/lang/pointer" sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" - importerModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/models" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testhelpers" ) // TODO: Edge Zones @@ -18,14 +18,13 @@ import ( // TODO: Zones func TestParseModel_CommonSchema_IdentityLegacySystemAndUserAssignedList(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_commonschema_identitylegacysystemanduserassignedlist.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_commonschema_identitylegacysystemanduserassignedlist.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Resource": { Models: map[string]sdkModels.SDKModel{ @@ -63,18 +62,17 @@ func TestParseModel_CommonSchema_IdentityLegacySystemAndUserAssignedList(t *test }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseModel_CommonSchema_IdentityLegacySystemAndUserAssignedMap(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_commonschema_identitylegacysystemanduserassignedmap.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_commonschema_identitylegacysystemanduserassignedmap.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Resource": { Models: map[string]sdkModels.SDKModel{ @@ -112,20 +110,19 @@ func TestParseModel_CommonSchema_IdentityLegacySystemAndUserAssignedMap(t *testi }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseModel_CommonSchema_IdentityLegacySystemAndUserAssignedMap_ExtraFields(t *testing.T) { // this handles the scenario of a System Assigned & User Assigned model having extra fields // in the user assigned identity block (#1066) - for example an extra `appId` - actual, err := ParseSwaggerFileForTesting(t, "model_commonschema_identitylegacysystemanduserassignedmap_extrafields.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_commonschema_identitylegacysystemanduserassignedmap_extrafields.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Resource": { Models: map[string]sdkModels.SDKModel{ @@ -163,18 +160,17 @@ func TestParseModel_CommonSchema_IdentityLegacySystemAndUserAssignedMap_ExtraFie }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseModel_CommonSchema_IdentityLegacySystemAndUserAssignedMap_GenericDictionary(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_commonschema_identitylegacysystemanduserassignedmap_genericdictionary.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_commonschema_identitylegacysystemanduserassignedmap_genericdictionary.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Resource": { Models: map[string]sdkModels.SDKModel{ @@ -212,18 +208,17 @@ func TestParseModel_CommonSchema_IdentityLegacySystemAndUserAssignedMap_GenericD }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseModel_CommonSchema_IdentitySystemAssigned(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_commonschema_identitysystemassigned.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_commonschema_identitysystemassigned.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Resource": { Models: map[string]sdkModels.SDKModel{ @@ -261,18 +256,17 @@ func TestParseModel_CommonSchema_IdentitySystemAssigned(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseModel_CommonSchema_IdentitySystemAndUserAssignedList(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_commonschema_identitysystemanduserassignedlist.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_commonschema_identitysystemanduserassignedlist.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Resource": { Models: map[string]sdkModels.SDKModel{ @@ -310,18 +304,17 @@ func TestParseModel_CommonSchema_IdentitySystemAndUserAssignedList(t *testing.T) }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseModel_CommonSchema_IdentitySystemAndUserAssignedMap(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_commonschema_identitysystemanduserassignedmap.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_commonschema_identitysystemanduserassignedmap.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Resource": { Models: map[string]sdkModels.SDKModel{ @@ -359,20 +352,19 @@ func TestParseModel_CommonSchema_IdentitySystemAndUserAssignedMap(t *testing.T) }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseModel_CommonSchema_IdentitySystemAndUserAssignedMap_ExtraFields(t *testing.T) { // this handles the scenario of a System Assigned & User Assigned model having extra fields // in the user assigned identity block (#1066) - for example an extra `appId` - actual, err := ParseSwaggerFileForTesting(t, "model_commonschema_identitysystemanduserassignedmap.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_commonschema_identitysystemanduserassignedmap.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Resource": { Models: map[string]sdkModels.SDKModel{ @@ -410,18 +402,17 @@ func TestParseModel_CommonSchema_IdentitySystemAndUserAssignedMap_ExtraFields(t }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseModel_CommonSchema_IdentitySystemOrUserAssignedList(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_commonschema_identitysystemoruserassignedlist.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_commonschema_identitysystemoruserassignedlist.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Resource": { Models: map[string]sdkModels.SDKModel{ @@ -459,18 +450,17 @@ func TestParseModel_CommonSchema_IdentitySystemOrUserAssignedList(t *testing.T) }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseModel_CommonSchema_IdentitySystemOrUserAssignedMap(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_commonschema_identitysystemoruserassignedmap.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_commonschema_identitysystemoruserassignedmap.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Resource": { Models: map[string]sdkModels.SDKModel{ @@ -508,20 +498,19 @@ func TestParseModel_CommonSchema_IdentitySystemOrUserAssignedMap(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseModel_CommonSchema_IdentitySystemOrUserAssignedMap_DelegatedResources(t *testing.T) { // this handles the scenario of a System Assigned & User Assigned model having a DelegatedResources block // this block isn't intended to be used by users of the API (and is for internal ARM usage only) - actual, err := ParseSwaggerFileForTesting(t, "model_commonschema_identitysystemoruserassignedmap_delegatedresources.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_commonschema_identitysystemoruserassignedmap_delegatedresources.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Resource": { Models: map[string]sdkModels.SDKModel{ @@ -559,20 +548,19 @@ func TestParseModel_CommonSchema_IdentitySystemOrUserAssignedMap_DelegatedResour }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseModel_CommonSchema_IdentitySystemOrUserAssignedMap_ExtraFields(t *testing.T) { // this handles the scenario of a System Assigned & User Assigned model having extra fields // in the user assigned identity block (#1066) - for example an extra `appId` - actual, err := ParseSwaggerFileForTesting(t, "model_commonschema_identitysystemoruserassignedmap_extrafields.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_commonschema_identitysystemoruserassignedmap_extrafields.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Resource": { Models: map[string]sdkModels.SDKModel{ @@ -610,18 +598,17 @@ func TestParseModel_CommonSchema_IdentitySystemOrUserAssignedMap_ExtraFields(t * }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseModel_CommonSchema_IdentityUserAssignedList(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_commonschema_identityuserassignedlist.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_commonschema_identityuserassignedlist.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Resource": { Models: map[string]sdkModels.SDKModel{ @@ -659,18 +646,17 @@ func TestParseModel_CommonSchema_IdentityUserAssignedList(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseModel_CommonSchema_IdentityUserAssignedMap(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_commonschema_identityuserassignedmap.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_commonschema_identityuserassignedmap.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Resource": { Models: map[string]sdkModels.SDKModel{ @@ -708,18 +694,17 @@ func TestParseModel_CommonSchema_IdentityUserAssignedMap(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseModel_CommonSchema_IdentityUserAssignedMap_PrincipalID(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_commonschema_identityuserassignedmap_principalid.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_commonschema_identityuserassignedmap_principalid.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Resource": { Models: map[string]sdkModels.SDKModel{ @@ -757,18 +742,17 @@ func TestParseModel_CommonSchema_IdentityUserAssignedMap_PrincipalID(t *testing. }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseModel_CommonSchema_IdentityUserAssignedMap_TenantID(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_commonschema_identityuserassignedmap_tenantid.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_commonschema_identityuserassignedmap_tenantid.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Resource": { Models: map[string]sdkModels.SDKModel{ @@ -806,18 +790,17 @@ func TestParseModel_CommonSchema_IdentityUserAssignedMap_TenantID(t *testing.T) }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseModel_CommonSchema_Location(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_commonschema_location.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_commonschema_location.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Resource": { Models: map[string]sdkModels.SDKModel{ @@ -855,5 +838,5 @@ func TestParseModel_CommonSchema_Location(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/comparison/object_definition.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/comparison/object_definition.go new file mode 100644 index 00000000000..399ed3860a9 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/comparison/object_definition.go @@ -0,0 +1,62 @@ +package comparison + +import ( + "fmt" + "strings" + + "github.com/hashicorp/go-azure-helpers/lang/pointer" + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/logging" +) + +func ObjectDefinitionsMatch(first, second *sdkModels.SDKObjectDefinition) (bool, error) { + if first == nil && second == nil { + logging.Tracef("Both ObjectDefinitions were nil - so the same") + return true, nil + } + if first != nil && second == nil { + logging.Tracef("First was %+v - Second was nil", *first) + return false, fmt.Errorf("First was %+v - Second was nil", *first) + } + if first == nil && second != nil { + logging.Tracef("First was nil - Second was %+v", *second) + return false, fmt.Errorf("First was nil - Second was %+v", *second) + } + + if first.Type != second.Type { + logging.Tracef("Type differed - %q vs %q", string(first.Type), string(second.Type)) + return false, fmt.Errorf("Type differed - %q vs %q", string(first.Type), string(second.Type)) + } + if !strings.EqualFold(pointer.From(first.ReferenceName), pointer.From(second.ReferenceName)) { + logging.Tracef("ReferenceName differed - %q vs %q", pointer.From(first.ReferenceName), pointer.From(second.ReferenceName)) + return false, fmt.Errorf("ReferenceName differed - %q vs %q", pointer.From(first.ReferenceName), pointer.From(second.ReferenceName)) + } + if first.ReferenceNameIsCommonType != second.ReferenceNameIsCommonType { + firstValue := "nil" + secondValue := "nil" + if first.ReferenceNameIsCommonType != nil { + firstValue = fmt.Sprintf("%t", *first.ReferenceNameIsCommonType) + } + if second.ReferenceNameIsCommonType != nil { + secondValue = fmt.Sprintf("%t", *second.ReferenceNameIsCommonType) + } + logging.Tracef("ReferenceNameIsCommonType differed - %s vs %s", firstValue, secondValue) + return false, fmt.Errorf("ReferenceNameIsCommonType differed - %s vs %s", firstValue, secondValue) + } + if first.NestedItem != nil && second.NestedItem != nil { + if ok, err := ObjectDefinitionsMatch(first.NestedItem, second.NestedItem); !ok { + logging.Tracef("NestedItem differed: %+v vs %+v", *first.NestedItem, *second.NestedItem) + return false, fmt.Errorf("NestedItem differed: %+v", err) + } + } + if first.NestedItem != nil && second.NestedItem == nil { + logging.Tracef("NestedItem differed - First was %+v - Second was nil", *first.NestedItem) + return false, fmt.Errorf("NestedItem differed - First was %+v - Second was nil", *first.NestedItem) + } + if first.NestedItem == nil && second.NestedItem != nil { + logging.Tracef("NestedItem differed - First was nil - Second was %+v", *second.NestedItem) + return false, fmt.Errorf("NestedItem differed - First was nil - Second was %+v", *second.NestedItem) + } + + return true, nil +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/comparison/operation.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/comparison/operation.go new file mode 100644 index 00000000000..f2f16faa21a --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/comparison/operation.go @@ -0,0 +1,57 @@ +package comparison + +import ( + "fmt" + "reflect" + "strings" + + "github.com/davecgh/go-spew/spew" + "github.com/hashicorp/go-azure-helpers/lang/pointer" + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/logging" +) + +func OperationsMatch(first, second sdkModels.SDKOperation) (bool, error) { + if first.ContentType != second.ContentType { + logging.Tracef("Differing ContentType %q and %q", first.ContentType, second.ContentType) + return false, fmt.Errorf("Differing ContentType %q and %q", first.ContentType, second.ContentType) + } + if !reflect.DeepEqual(first.ExpectedStatusCodes, second.ExpectedStatusCodes) { + logging.Tracef("Differing ExpectedStatusCodes %+v and %+v", first.ExpectedStatusCodes, second.ExpectedStatusCodes) + return false, fmt.Errorf("Differing ExpectedStatusCodes %+v and %+v", first.ExpectedStatusCodes, second.ExpectedStatusCodes) + } + if !strings.EqualFold(pointer.From(first.FieldContainingPaginationDetails), pointer.From(second.FieldContainingPaginationDetails)) { + logging.Tracef("Differing FieldContainingPaginationDetails %q and %q", pointer.From(first.FieldContainingPaginationDetails), pointer.From(second.FieldContainingPaginationDetails)) + return false, fmt.Errorf("Differing FieldContainingPaginationDetails %q and %q", pointer.From(first.FieldContainingPaginationDetails), pointer.From(second.FieldContainingPaginationDetails)) + } + if first.LongRunning != second.LongRunning { + logging.Tracef("Differing LongRunning %t and %t", first.LongRunning, second.LongRunning) + return false, fmt.Errorf("Differing LongRunning %t and %t", first.LongRunning, second.LongRunning) + } + if first.Method != second.Method { + logging.Tracef("Differing Method %q and %q", first.Method, second.Method) + return false, fmt.Errorf("Differing Method %q and %q", first.Method, second.Method) + } + if !OperationOptionsMatch(first.Options, second.Options) { + logging.Tracef("Differing Options %+v and %+v", first.Options, second.Options) + return false, fmt.Errorf("Differing Options %+v and %+v", spew.Sdump(first.Options), spew.Sdump(second.Options)) + } + if ok, err := ObjectDefinitionsMatch(first.RequestObject, second.RequestObject); !ok { + logging.Tracef("Differing RequestObject %+v and %+v", first.RequestObject, second.RequestObject) + return false, fmt.Errorf("Differing RequestObject: %+v", err) + } + if pointer.From(first.ResourceIDName) != pointer.From(second.ResourceIDName) { + logging.Tracef("Differing ResourceIDName %q and %q", pointer.From(first.ResourceIDName), pointer.From(second.ResourceIDName)) + return false, fmt.Errorf("Differing ResourceIDName %q and %q", pointer.From(first.ResourceIDName), pointer.From(second.ResourceIDName)) + } + if ok, err := ObjectDefinitionsMatch(first.ResponseObject, second.ResponseObject); !ok { + logging.Tracef("Differing ResponseObjects %+v and %+v", first.ResponseObject, second.ResponseObject) + return false, fmt.Errorf("Differing ResponseObjects: %+v", err) + } + if pointer.From(first.URISuffix) != pointer.From(second.URISuffix) { + logging.Tracef("Differing URISuffix %q and %q", pointer.From(first.URISuffix), pointer.From(second.URISuffix)) + return false, fmt.Errorf("Differing URISuffix %q and %q", pointer.From(first.URISuffix), pointer.From(second.URISuffix)) + } + + return true, nil +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/comparison/operation_option.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/comparison/operation_option.go new file mode 100644 index 00000000000..002c6eb1c03 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/comparison/operation_option.go @@ -0,0 +1,28 @@ +package comparison + +import ( + "github.com/hashicorp/go-azure-helpers/lang/pointer" + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/logging" +) + +func OperationOptionMatch(first, second sdkModels.SDKOperationOption) bool { + if pointer.From(first.HeaderName) != pointer.From(second.HeaderName) { + logging.Tracef("HeaderName differed - %q vs %q", pointer.From(first.HeaderName), pointer.From(second.HeaderName)) + return false + } + if pointer.From(first.QueryStringName) != pointer.From(second.QueryStringName) { + logging.Tracef("QueryStringName differed - %q vs %q", pointer.From(first.QueryStringName), pointer.From(second.QueryStringName)) + return false + } + if !OperationOptionObjectDefinitionMatch(first.ObjectDefinition, second.ObjectDefinition) { + logging.Tracef("ObjectDefinition differed - %+v vs %+v", first.ObjectDefinition, second.ObjectDefinition) + return false + } + if first.Required != second.Required { + logging.Tracef("Required differed - %t vs %t", first.Required, second.Required) + return false + } + + return true +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/comparison/operation_option_object_definition.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/comparison/operation_option_object_definition.go new file mode 100644 index 00000000000..0da26e707d4 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/comparison/operation_option_object_definition.go @@ -0,0 +1,36 @@ +package comparison + +import ( + "strings" + + "github.com/hashicorp/go-azure-helpers/lang/pointer" + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/logging" +) + +func OperationOptionObjectDefinitionMatch(first, second sdkModels.SDKOperationOptionObjectDefinition) bool { + if first.Type != second.Type { + logging.Tracef("Type differed - %q vs %q", string(first.Type), string(second.Type)) + return false + } + if !strings.EqualFold(pointer.From(first.ReferenceName), pointer.From(second.ReferenceName)) { + logging.Tracef("ReferenceName differed - %q vs %q", pointer.From(first.ReferenceName), pointer.From(second.ReferenceName)) + return false + } + if first.NestedItem != nil && second.NestedItem != nil { + if !OperationOptionObjectDefinitionMatch(*first.NestedItem, *second.NestedItem) { + logging.Tracef("NestedItem differed: %+v vs %+v", *first.NestedItem, *second.NestedItem) + return false + } + } + if first.NestedItem != nil && second.NestedItem == nil { + logging.Tracef("NestedItem differed - First was %+v - Second was nil", *first.NestedItem) + return false + } + if first.NestedItem == nil && second.NestedItem != nil { + logging.Tracef("NestedItem differed - First was nil - Second was %+v", *second.NestedItem) + return false + } + + return true +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/comparison/operation_options.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/comparison/operation_options.go new file mode 100644 index 00000000000..bfe5d069f92 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/comparison/operation_options.go @@ -0,0 +1,28 @@ +package comparison + +import ( + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/logging" +) + +func OperationOptionsMatch(first, second map[string]sdkModels.SDKOperationOption) bool { + if len(first) != len(second) { + logging.Tracef("Differing number of Options - %d vs %d", len(first), len(second)) + return false + } + + for key, firstVal := range first { + secondVal, ok := second[key] + if !ok { + logging.Tracef("Option %q was present in First but not Second", key) + return false + } + + if !OperationOptionMatch(firstVal, secondVal) { + logging.Tracef("Option %q differed between First and Second. First: %+v. Second: %+v", key, firstVal, secondVal) + return false + } + } + + return true +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/comparison/resource_ids.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/comparison/resource_ids.go new file mode 100644 index 00000000000..20e72f7b209 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/comparison/resource_ids.go @@ -0,0 +1,68 @@ +package comparison + +import ( + "strings" + + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" +) + +func ResourceIDsMatch(first, second sdkModels.ResourceID) bool { + if len(first.Segments) != len(second.Segments) { + return false + } + + for i, firstSegment := range first.Segments { + secondSegment := second.Segments[i] + if firstSegment.Type != secondSegment.Type { + return false + } + + // Whilst these should match, it's possible that they don't but are the same e.g. + // /subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Devices/provisioningServices/{resourceName} + // /subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Devices/provisioningServices/{provisioningServiceName} + // as such providing they're both user specified segments (and the rest is the same) then they're the same + if firstSegment.Type == sdkModels.ResourceGroupResourceIDSegmentType || firstSegment.Type == sdkModels.SubscriptionIDResourceIDSegmentType || firstSegment.Type == sdkModels.UserSpecifiedResourceIDSegmentType { + continue + } + + // With a Scope the key doesn't matter as much as that it's a Scope, so presuming the types match (above) we're good. + if firstSegment.Type == sdkModels.ScopeResourceIDSegmentType { + continue + } + + if firstSegment.Type == sdkModels.ConstantResourceIDSegmentType { + if firstSegment.ConstantReference != nil && secondSegment.ConstantReference == nil { + return false + } + if firstSegment.ConstantReference == nil && secondSegment.ConstantReference != nil { + return false + } + + // We're intentionally not checking the constants involved, since both the name and values could differ + // between different operations due to data issues - however when either happens we'd end up using a + // Common ID to resolve this - therefore presuming the rest of the Resource ID matches we should be good. + + continue + } + + if firstSegment.Type == sdkModels.ResourceProviderResourceIDSegmentType || firstSegment.Type == sdkModels.StaticResourceIDSegmentType { + if firstSegment.FixedValue != nil && secondSegment.FixedValue == nil { + return false + } + if firstSegment.FixedValue == nil && secondSegment.FixedValue != nil { + return false + } + if firstSegment.FixedValue != nil && secondSegment.FixedValue != nil && *firstSegment.FixedValue != *secondSegment.FixedValue { + return false + } + + continue + } + + if !strings.EqualFold(firstSegment.Name, secondSegment.Name) { + return false + } + } + + return true +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/constants/extension_parser.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/constants/extension_parser.go new file mode 100644 index 00000000000..0ad8f670ea2 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/constants/extension_parser.go @@ -0,0 +1,82 @@ +package constants + +import ( + "fmt" + "strings" + + "github.com/go-openapi/spec" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup" +) + +type constantExtension struct { + // name defines the Name that should be used for this Constant + name string + + // valuesToDisplayNames defines any display name overrides that should be used for this Constant + // NOTE: whilst the API Definitions may define a value with no display name - this map contains + // only values with a name defined. + valuesToDisplayNames map[interface{}]string +} + +func parseConstantExtensionFromExtension(input spec.Extensions) (*constantExtension, error) { + // Constants should always have `x-ms-enum` + enumDetailsRaw, ok := input["x-ms-enum"] + if !ok { + return nil, fmt.Errorf("constant is missing x-ms-enum") + } + + enumDetails, ok := enumDetailsRaw.(map[string]interface{}) + if !ok { + return nil, fmt.Errorf("enum details weren't a map[string]interface{}") + } + + var enumName *string + var valuesToDisplayNames map[interface{}]string + for k, v := range enumDetails { + // presume inconsistencies in the data + if strings.EqualFold(k, "name") { + normalizedEnumName := cleanup.NormalizeName(v.(string)) + enumName = &normalizedEnumName + } + + if strings.EqualFold(k, "values") { + items := v.([]interface{}) + displayNameOverrides := make(map[interface{}]string) + for _, itemRaw := range items { + item := itemRaw.(map[string]interface{}) + name, ok := item["name"].(string) + if !ok || name == "" { + // there isn't a custom name defined for this, so we should ignore it + continue + } + value, ok := item["value"].(interface{}) + if !ok { + continue + } + // NOTE: whilst `x-ms-enum` includes a `description` field we don't support that today + // support for that is tracked in https://github.com/hashicorp/pandora/issues/231 + + displayNameOverrides[value] = name + } + if len(displayNameOverrides) > 0 { + valuesToDisplayNames = displayNameOverrides + } + } + + // NOTE: the Swagger Extension defines `modelAsString` which is used to define whether + // this should be output as a fixed set of values (e.g. a constant) or an extendable + // list of strings (e.g. a set of possible string values with other values possible) + // however we're not concerned with the difference - so we ignore this. + } + if enumName == nil { + return nil, fmt.Errorf("enum details are missing a `name`") + } + + output := constantExtension{ + name: *enumName, + } + if valuesToDisplayNames != nil { + output.valuesToDisplayNames = valuesToDisplayNames + } + return &output, nil +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/constants/helpers.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/constants/helpers.go new file mode 100644 index 00000000000..ea1a316a18b --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/constants/helpers.go @@ -0,0 +1,119 @@ +package constants + +import ( + "fmt" + "regexp" + "strconv" + "strings" + + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup" +) + +func keyValueForFloat(input float64) string { + stringified := stringValueForFloat(input) + return stringifyNumberInput(stringified) +} + +func keyValueForInteger(input int64) string { + s := fmt.Sprintf("%d", input) + vals := map[int32]string{ + '-': "Negative", + '0': "Zero", + '1': "One", + '2': "Two", + '3': "Three", + '4': "Four", + '5': "Five", + '6': "Six", + '7': "Seven", + '8': "Eight", + '9': "Nine", + } + out := "" + for _, c := range s { + v, ok := vals[c] + if !ok { + panic(fmt.Sprintf("missing mapping for %q", string(c))) + } + out += v + } + + return out +} + +func normalizeConstantKey(input string) string { + output := input + output = stringifyNumberInput(output) + if !strings.Contains(output, "Point") { + output = renameMultiplesOfZero(output) + } + + output = strings.ReplaceAll(output, "*", "Any") + output = cleanup.NormalizeName(output) + return output +} + +func renameMultiplesOfZero(input string) string { + if strings.HasPrefix(input, "Zero") && !strings.HasSuffix(input, "Zero") { + return input + } + + re := regexp.MustCompile("(?:Zero)") + zeros := re.FindAllStringIndex(input, -1) + z := len(zeros) + + if z < 2 { + return input + } + + vals := map[int]string{ + 2: "Hundred", + 3: "Thousand", + 4: "Thousand", + 5: "HundredThousand", + 6: "Million", + } + + if v, ok := vals[z]; ok { + switch z { + case 4: + return strings.Replace(input, strings.Repeat("Zero", z), "Zero"+v, 1) + default: + return strings.Replace(input, strings.Repeat("Zero", z), v, 1) + } + } + + return input +} + +func stringValueForFloat(input float64) string { + return strconv.FormatFloat(input, 'f', -1, 64) +} + +func stringifyNumberInput(input string) string { + vals := map[int32]string{ + '.': "Point", + '+': "Positive", + '-': "Negative", + '0': "Zero", + '1': "One", + '2': "Two", + '3': "Three", + '4': "Four", + '5': "Five", + '6': "Six", + '7': "Seven", + '8': "Eight", + '9': "Nine", + } + output := "" + for _, c := range input { + v, ok := vals[c] + if !ok { + output += string(c) + continue + } + output += v + } + return output +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/constants/parse.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/constants/parse.go new file mode 100644 index 00000000000..c3bb70a07f4 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/constants/parse.go @@ -0,0 +1,157 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package constants + +import ( + "fmt" + "reflect" + "strconv" + "strings" + + "github.com/go-openapi/spec" + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/featureflags" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/logging" +) + +type ParsedConstant struct { + Name string + Details sdkModels.SDKConstant +} + +func Parse(typeVal spec.StringOrArray, fieldName string, modelName *string, values []interface{}, extensions spec.Extensions) (*ParsedConstant, error) { + if len(values) == 0 { + return nil, fmt.Errorf("the Enum in %q has no values", fieldName) + } + + constantName := fieldName + + // the name needs to come from the `x-ms-enum` extension + constExtension, err := parseConstantExtensionFromExtension(extensions) + if err != nil { + if featureflags.AllowConstantsWithoutXMSEnum { + logging.Debugf("Field %q had an invalid `x-ms-enum`: %+v", fieldName, err) + // this attempts to construct a unique name for a constant out of the model name and field name + // to prevent duplicate definitions of constants, specifically constants called `type` + // of which there are several in data factory (#3725) + if strings.EqualFold(fieldName, "type") && modelName != nil { + constantPrefix := strings.TrimSuffix(*modelName, "Type") + constantName = constantPrefix + cleanup.Title(fieldName) + logging.Debugf("Field %q renamed to %q", fieldName, constantName) + } + + } else { + return nil, fmt.Errorf("parsing x-ms-enum: %+v", err) + } + } + if constExtension != nil { + constantName = constExtension.name + } + + constantType := sdkModels.StringSDKConstantType + if typeVal.Contains("integer") { + constantType = sdkModels.IntegerSDKConstantType + } else if typeVal.Contains("number") { + constantType = sdkModels.FloatSDKConstantType + } + + keysAndValues, err := parseKeysAndValues(values, constantType, constExtension) + if err != nil { + return nil, fmt.Errorf("parsing keys/values: %+v", err) + } + + // allows us to parse out the actual types above then force a string here if needed + if constExtension == nil { + constantType = sdkModels.StringSDKConstantType + } + + return &ParsedConstant{ + Name: cleanup.Title(constantName), + Details: sdkModels.SDKConstant{ + Values: keysAndValues, + Type: constantType, + }, + }, nil +} + +func parseKeysAndValues(input []interface{}, constantType sdkModels.SDKConstantType, constExtension *constantExtension) (map[string]string, error) { + keysAndValues := make(map[string]string) + for i, raw := range input { + if constantType == sdkModels.StringSDKConstantType { + value, ok := raw.(string) + if !ok { + return nil, fmt.Errorf("expected a string but got %+v for the %d value for %q", raw, i, constExtension.name) + } + // Some numbers are modelled as strings + if numVal, err := strconv.ParseFloat(value, 64); err == nil { + if strings.Contains(value, ".") { + normalizedName := normalizeConstantKey(value) + keysAndValues[normalizedName] = value + continue + } + + key := keyValueForInteger(int64(numVal)) + val := fmt.Sprintf("%d", int64(numVal)) + normalizedName := normalizeConstantKey(key) + keysAndValues[normalizedName] = val + continue + } + normalizedName := normalizeConstantKey(value) + keysAndValues[normalizedName] = value + continue + } + + if constantType == sdkModels.IntegerSDKConstantType { + // This gets parsed out as a float64 even though it's an Integer :upside_down_smile: + value, ok := raw.(float64) + if !ok { + // Except sometimes it's actually a string. That's numberwang. + v, ok := raw.(string) + if !ok { + typeName := reflect.TypeOf(raw).Name() + return nil, fmt.Errorf("expected a float64/string but got type %q value %+v for at index %d for %q", typeName, raw, i, constExtension.name) + } + + val, err := strconv.Atoi(v) + if err != nil { + return nil, fmt.Errorf("converting string value %q to an integer: %+v", v, err) + } + + value = float64(val) + } + + key := keyValueForInteger(int64(value)) + // if an override name is defined for this Constant then we should use it + if constExtension != nil && constExtension.valuesToDisplayNames != nil { + overrideName, hasOverride := constExtension.valuesToDisplayNames[value] + if hasOverride { + key = overrideName + } + } + + val := fmt.Sprintf("%d", int64(value)) + normalizedName := normalizeConstantKey(key) + keysAndValues[normalizedName] = val + continue + } + + if constantType == sdkModels.FloatSDKConstantType { + value, ok := raw.(float64) + if !ok { + return nil, fmt.Errorf("expected an float but got %+v for the %d value for %q", raw, i, constExtension.name) + } + + key := keyValueForFloat(value) + val := stringValueForFloat(value) + normalizedName := normalizeConstantKey(key) + keysAndValues[normalizedName] = val + continue + } + + return nil, fmt.Errorf("unsupported constant type %q", string(constantType)) + } + + return keysAndValues, nil +} diff --git a/tools/importer-rest-api-specs/components/parser/constants/interface_test.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/constants/parse_test.go similarity index 100% rename from tools/importer-rest-api-specs/components/parser/constants/interface_test.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/constants/parse_test.go diff --git a/tools/importer-rest-api-specs/components/parser/constants_test.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/constants_test.go similarity index 85% rename from tools/importer-rest-api-specs/components/parser/constants_test.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/constants_test.go index 1ed62e5b2ec..10eef33d1d3 100644 --- a/tools/importer-rest-api-specs/components/parser/constants_test.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/constants_test.go @@ -1,26 +1,25 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package parser +package parser_test import ( + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testhelpers" "testing" "github.com/hashicorp/go-azure-helpers/lang/pointer" sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" - importerModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/models" ) func TestParseConstantsIntegersTopLevelAsInts(t *testing.T) { // Enums can either be modelled as strings or not.. this is an Int that's output as an Integer. - actual, err := ParseSwaggerFileForTesting(t, "constants_integers_as_ints.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "constants_integers_as_ints.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Example": { Constants: map[string]sdkModels.SDKConstant{ @@ -63,20 +62,19 @@ func TestParseConstantsIntegersTopLevelAsInts(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseConstantsIntegersTopLevelAsIntsWithDisplayName(t *testing.T) { // This is an Integer Enum where there's a (Display) Name listed for the integer // so we should be using `Name (string): Value (integer`) - actual, err := ParseSwaggerFileForTesting(t, "constants_integers_with_names.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "constants_integers_with_names.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Example": { Constants: map[string]sdkModels.SDKConstant{ @@ -118,19 +116,18 @@ func TestParseConstantsIntegersTopLevelAsIntsWithDisplayName(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseConstantsIntegersTopLevelAsStrings(t *testing.T) { // Tests an Integer Constant with modelAsString, which is bad data / should be ignored - actual, err := ParseSwaggerFileForTesting(t, "constants_integers_as_strings.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "constants_integers_as_strings.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Example": { Constants: map[string]sdkModels.SDKConstant{ @@ -172,19 +169,18 @@ func TestParseConstantsIntegersTopLevelAsStrings(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseConstantsIntegersInlinedAsInts(t *testing.T) { // Enums can either be modelled as strings or not.. this is an Int that's output as an Integer. - actual, err := ParseSwaggerFileForTesting(t, "constants_integers_as_ints_inlined.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "constants_integers_as_ints_inlined.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Example": { Constants: map[string]sdkModels.SDKConstant{ @@ -226,20 +222,19 @@ func TestParseConstantsIntegersInlinedAsInts(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseConstantsIntegersInlinedAsIntsWithDisplayName(t *testing.T) { // This is an Integer Enum where there's a (Display) Name listed for the integer // so we should be using `Name (string): Value (integer`) - actual, err := ParseSwaggerFileForTesting(t, "constants_integers_with_names_inlined.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "constants_integers_with_names_inlined.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Example": { Constants: map[string]sdkModels.SDKConstant{ @@ -281,18 +276,17 @@ func TestParseConstantsIntegersInlinedAsIntsWithDisplayName(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseConstantsMultipleTypeEnums(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "constants_multiple_type_enums.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "constants_multiple_type_enums.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Example": { Constants: map[string]sdkModels.SDKConstant{ @@ -363,19 +357,18 @@ func TestParseConstantsMultipleTypeEnums(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseConstantsIntegersInlinedAsStrings(t *testing.T) { // Tests an Integer Constant defined Inline with modelAsString, which is bad data / should be ignored - actual, err := ParseSwaggerFileForTesting(t, "constants_integers_as_strings_inlined.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "constants_integers_as_strings_inlined.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Example": { Constants: map[string]sdkModels.SDKConstant{ @@ -417,19 +410,18 @@ func TestParseConstantsIntegersInlinedAsStrings(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseConstantsFloatsTopLevelAsFloats(t *testing.T) { // Enums can either be modelled as strings or not.. this is an Float that's output as a Float. - actual, err := ParseSwaggerFileForTesting(t, "constants_floats_as_floats.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "constants_floats_as_floats.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Example": { Constants: map[string]sdkModels.SDKConstant{ @@ -471,19 +463,18 @@ func TestParseConstantsFloatsTopLevelAsFloats(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseConstantsFloatsTopLevelAsStrings(t *testing.T) { // Tests an Float Constant with modelAsString, which is bad data / should be ignored - actual, err := ParseSwaggerFileForTesting(t, "constants_floats_as_strings.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "constants_floats_as_strings.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Example": { Constants: map[string]sdkModels.SDKConstant{ @@ -525,19 +516,18 @@ func TestParseConstantsFloatsTopLevelAsStrings(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseConstantsFloatsInlinedAsFloats(t *testing.T) { // Enums can either be modelled as strings or not.. this is an Float that's output as a Float. - actual, err := ParseSwaggerFileForTesting(t, "constants_floats_as_floats_inlined.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "constants_floats_as_floats_inlined.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Example": { Constants: map[string]sdkModels.SDKConstant{ @@ -579,19 +569,18 @@ func TestParseConstantsFloatsInlinedAsFloats(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseConstantsFloatsInlinedAsStrings(t *testing.T) { // Tests an Float Constant defined inline with modelAsString, which is bad data / should be ignored - actual, err := ParseSwaggerFileForTesting(t, "constants_floats_as_strings_inlined.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "constants_floats_as_strings_inlined.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Example": { Constants: map[string]sdkModels.SDKConstant{ @@ -633,18 +622,17 @@ func TestParseConstantsFloatsInlinedAsStrings(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseConstantsStringsTopLevel(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "constants_strings.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "constants_strings.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Example": { Constants: map[string]sdkModels.SDKConstant{ @@ -687,21 +675,20 @@ func TestParseConstantsStringsTopLevel(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseConstantsStringsTopLevelAsNonStrings(t *testing.T) { // whilst the value is "string", due to (what appears to be) bad data the // "modelAsString" property can be set to false - as such we force it to // a string either way, since that's what it is - actual, err := ParseSwaggerFileForTesting(t, "constants_strings_as_non_strings.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "constants_strings_as_non_strings.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Example": { Constants: map[string]sdkModels.SDKConstant{ @@ -743,18 +730,17 @@ func TestParseConstantsStringsTopLevelAsNonStrings(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseConstantsStringsInlined(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "constants_strings_inlined.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "constants_strings_inlined.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Example": { Constants: map[string]sdkModels.SDKConstant{ @@ -796,18 +782,17 @@ func TestParseConstantsStringsInlined(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseConstantsStringsInlinedAsNonStrings(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "constants_strings_inlined_as_non_strings.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "constants_strings_inlined_as_non_strings.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Example": { Constants: map[string]sdkModels.SDKConstant{ @@ -849,19 +834,18 @@ func TestParseConstantsStringsInlinedAsNonStrings(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseConstantsStringsTopLevelContainingFloats(t *testing.T) { // Enums can either be modelled as strings or not.. this is a Float that's output as a String. - actual, err := ParseSwaggerFileForTesting(t, "constants_strings_which_are_floats.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "constants_strings_which_are_floats.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Example": { Constants: map[string]sdkModels.SDKConstant{ @@ -903,19 +887,18 @@ func TestParseConstantsStringsTopLevelContainingFloats(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseConstantsStringsInlinedContainingFloats(t *testing.T) { // Enums can either be modelled as strings or not.. this is a Float that's output as a Float. - actual, err := ParseSwaggerFileForTesting(t, "constants_floats_as_floats_inlined.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "constants_floats_as_floats_inlined.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Example": { Constants: map[string]sdkModels.SDKConstant{ @@ -957,11 +940,11 @@ func TestParseConstantsStringsInlinedContainingFloats(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseConstantsFromParameters(t *testing.T) { - result, err := ParseSwaggerFileForTesting(t, "constants_in_operation_parameters.json", nil) + result, err := testhelpers.ParseSwaggerFileForTesting(t, "constants_in_operation_parameters.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/README.md b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/README.md new file mode 100644 index 00000000000..bbaaad55dba --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/README.md @@ -0,0 +1,8 @@ +## `./internal/components/apidefinitions/parser/dataworkarounds` + +This package is used to define Data Workarounds, which applies workarounds/fixes to the parsed API Definitions. + +This is typically done to workaround a correctness issue (for example a Field being defined as an Integer rather than a String), but could also be to add constant values, discriminated types/implementations etc. + +These are intended purely as a short-term workaround (and clearly aren't ideal) - so ultimately we want the data issues fixed upstream - and so each workaround should have an accompanying pull request in [the `Azure/azure-rest-api-specs` repository](https://github.com/Azure/azure-rest-api-specs). + diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/apply.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/apply.go new file mode 100644 index 00000000000..4e9a2553db2 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/apply.go @@ -0,0 +1,35 @@ +package dataworkarounds + +import ( + "fmt" + + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/logging" +) + +// Apply goes through and determines if any workarounds are required for the Service/API Version +// and applies those - which allows for patching API Definitions to workaround issues; for example a correctness +// issue (a field is defined as an Integer rather than a String), or adding/removing Fields/Models/Constants etc. +// +// These workarounds are intended as a short-term workaround only - so we'll want to ensure there's an accompanying +// pull request to fix the issues in question - else we'll end up diverging overtime/this could become problematic. +func Apply(serviceName string, input sdkModels.APIVersion) (*sdkModels.APIVersion, error) { + logging.Debugf("Applying Data Workarounds to the API Version %q..", input.APIVersion) + output := input + for _, fix := range workarounds { + if !fix.IsApplicable(serviceName, output) { + logging.Tracef("Data Workaround %q is not applicable - skipping..", fix.Name()) + continue + } + + logging.Tracef("Applying Data Workaround %q..", fix.Name()) + updated, err := fix.Process(output) + if err != nil { + return nil, fmt.Errorf("applying Swagger Data Workaround %q to Service %q / API Version %q: %+v", fix.Name(), serviceName, input.APIVersion, err) + } + output = *updated + logging.Tracef("Applying Data Workaround %q - Completed", fix.Name()) + } + logging.Debugf("Applying Data Workarounds to the API Version %q - Completed", input.APIVersion) + return &output, nil +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/helpers.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/helpers.go new file mode 100644 index 00000000000..270cb98db50 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/helpers.go @@ -0,0 +1,31 @@ +package dataworkarounds + +import ( + "fmt" + + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" +) + +func markFieldAsComputed(input sdkModels.APIVersion, apiResourceName, modelName, fieldName string) (*sdkModels.APIVersion, error) { + resource, ok := input.Resources[apiResourceName] + if !ok { + return nil, fmt.Errorf("expected an APIResource %q but didn't get one", apiResourceName) + } + model, ok := resource.Models[modelName] + if !ok { + return nil, fmt.Errorf("expected a Model %q but didn't get one", modelName) + } + field, ok := model.Fields[fieldName] + if !ok { + return nil, fmt.Errorf("expected a Field %q but didn't get one", fieldName) + } + field.Optional = false + field.ReadOnly = true + field.Required = false + field.Sensitive = false + model.Fields[fieldName] = field + + resource.Models[modelName] = model + input.Resources[apiResourceName] = resource + return &input, nil +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/interface.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/interface.go new file mode 100644 index 00000000000..49b0b171ba9 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/interface.go @@ -0,0 +1,20 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package dataworkarounds + +import sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" + +// workaround defines a workaround for a given set of data, allowing for the parsed data to be +// patched to workaround issues. This is intended only as a short-term workaround, and wants to +// be accompanied by a matching workaround upstream. +type workaround interface { + // IsApplicable determines whether this workaround is applicable for this Service / APIVersion + IsApplicable(serviceName string, apiVersion sdkModels.APIVersion) bool + + // Name returns the Service Name and associated Pull Request number associated with this workaround + Name() string + + // Process takes the APIVersion and applies the workaround to it + Process(input sdkModels.APIVersion) (*sdkModels.APIVersion, error) +} diff --git a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_alertsmanagement.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_alertsmanagement.go similarity index 65% rename from tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_alertsmanagement.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_alertsmanagement.go index 0f63bfb3784..19d5533866b 100644 --- a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_alertsmanagement.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_alertsmanagement.go @@ -5,9 +5,10 @@ package dataworkarounds import ( "fmt" + "sort" "github.com/hashicorp/go-azure-helpers/lang/pointer" - importerModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/models" + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" ) var _ workaround = workaroundAlertsManagement{} @@ -18,9 +19,9 @@ var _ workaround = workaroundAlertsManagement{} type workaroundAlertsManagement struct { } -func (workaroundAlertsManagement) IsApplicable(apiDefinition *importerModels.AzureApiDefinition) bool { - serviceMatches := apiDefinition.ServiceName == "AlertsManagement" - apiVersionMatches := apiDefinition.ApiVersion == "2019-05-05-preview" +func (workaroundAlertsManagement) IsApplicable(serviceName string, apiVersion sdkModels.APIVersion) bool { + serviceMatches := serviceName == "AlertsManagement" + apiVersionMatches := apiVersion.APIVersion == "2019-05-05-preview" return serviceMatches && apiVersionMatches } @@ -28,10 +29,15 @@ func (workaroundAlertsManagement) Name() string { return "AlertsManagement" } -func (workaroundAlertsManagement) Process(apiDefinition importerModels.AzureApiDefinition) (*importerModels.AzureApiDefinition, error) { - resource, ok := apiDefinition.Resources["ActionRules"] +func (workaroundAlertsManagement) Process(input sdkModels.APIVersion) (*sdkModels.APIVersion, error) { + resource, ok := input.Resources["ActionRules"] if !ok { - return nil, fmt.Errorf("expected a Resource named `ActionRules`") + keys := make([]string, 0) + for k := range input.Resources { + keys = append(keys, k) + } + sort.Strings(keys) + return nil, fmt.Errorf("expected a Resource named `ActionRules` but got [%+v]", keys) } _, ok = resource.Models["ActionRuleProperties"] if !ok { @@ -55,7 +61,7 @@ func (workaroundAlertsManagement) Process(apiDefinition importerModels.AzureApiD resource.Models[name] = model } - apiDefinition.Resources["ActionRules"] = resource + input.Resources["ActionRules"] = resource - return &apiDefinition, nil + return &input, nil } diff --git a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_authorization_25080.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_authorization_25080.go similarity index 63% rename from tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_authorization_25080.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_authorization_25080.go index 694c8b0e832..e8b255b1f72 100644 --- a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_authorization_25080.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_authorization_25080.go @@ -8,7 +8,6 @@ import ( "github.com/hashicorp/go-azure-helpers/lang/pointer" sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" - importerModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/models" ) var _ workaround = workaroundAuthorization25080{} @@ -16,16 +15,16 @@ var _ workaround = workaroundAuthorization25080{} type workaroundAuthorization25080 struct { } -func (workaroundAuthorization25080) IsApplicable(apiDefinition *importerModels.AzureApiDefinition) bool { - return apiDefinition.ServiceName == "Authorization" && apiDefinition.ApiVersion == "2020-10-01" +func (workaroundAuthorization25080) IsApplicable(serviceName string, apiVersion sdkModels.APIVersion) bool { + return serviceName == "Authorization" && apiVersion.APIVersion == "2020-10-01" } func (workaroundAuthorization25080) Name() string { return "Authorization / 25080" } -func (workaroundAuthorization25080) Process(apiDefinition importerModels.AzureApiDefinition) (*importerModels.AzureApiDefinition, error) { - resource, ok := apiDefinition.Resources["RoleManagementPolicies"] +func (workaroundAuthorization25080) Process(input sdkModels.APIVersion) (*sdkModels.APIVersion, error) { + resource, ok := input.Resources["RoleManagementPolicies"] if !ok { return nil, fmt.Errorf("expected a Resource named `RoleManagementPolicies` but didn't get one") } @@ -41,6 +40,6 @@ func (workaroundAuthorization25080) Process(apiDefinition importerModels.AzureAp Required: false, } resource.Operations["ListForScope"] = operation - apiDefinition.Resources["RoleManagementPolicies"] = resource - return &apiDefinition, nil + input.Resources["RoleManagementPolicies"] = resource + return &input, nil } diff --git a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_automation_25108.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_automation_25108.go similarity index 52% rename from tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_automation_25108.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_automation_25108.go index f4b82e931c2..e9a582dd5f6 100644 --- a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_automation_25108.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_automation_25108.go @@ -6,7 +6,7 @@ package dataworkarounds import ( "fmt" - importerModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/models" + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" ) var _ workaround = workaroundAutomation25108{} @@ -14,9 +14,9 @@ var _ workaround = workaroundAutomation25108{} type workaroundAutomation25108 struct { } -func (workaroundAutomation25108) IsApplicable(apiDefinition *importerModels.AzureApiDefinition) bool { - serviceMatches := apiDefinition.ServiceName == "Automation" - apiVersionMatches := apiDefinition.ApiVersion == "2022-08-08" || apiDefinition.ApiVersion == "2023-11-01" +func (workaroundAutomation25108) IsApplicable(serviceName string, apiVersion sdkModels.APIVersion) bool { + serviceMatches := serviceName == "Automation" + apiVersionMatches := apiVersion.APIVersion == "2022-08-08" || apiVersion.APIVersion == "2023-11-01" return serviceMatches && apiVersionMatches } @@ -24,8 +24,8 @@ func (workaroundAutomation25108) Name() string { return "Automation / 25108" } -func (workaroundAutomation25108) Process(apiDefinition importerModels.AzureApiDefinition) (*importerModels.AzureApiDefinition, error) { - resource, ok := apiDefinition.Resources["ListAllHybridRunbookWorkerGroupInAutomationAccount"] +func (workaroundAutomation25108) Process(input sdkModels.APIVersion) (*sdkModels.APIVersion, error) { + resource, ok := input.Resources["ListAllHybridRunbookWorkerGroupInAutomationAccount"] if !ok { return nil, fmt.Errorf("expected a Resource named `ListAllHybridRunbookWorkerGroupInAutomationAccount`") } @@ -34,7 +34,7 @@ func (workaroundAutomation25108) Process(apiDefinition importerModels.AzureApiDe return nil, fmt.Errorf("expected an Operation named `HybridRunbookWorkerGroupDelete`") } - otherResource, ok := apiDefinition.Resources["HybridRunbookWorkerGroup"] + otherResource, ok := input.Resources["HybridRunbookWorkerGroup"] if !ok { return nil, fmt.Errorf("expected a Resource named `HybridRunbookWorkerGroup`") } @@ -43,8 +43,8 @@ func (workaroundAutomation25108) Process(apiDefinition importerModels.AzureApiDe } otherResource.Operations["Delete"] = operation - apiDefinition.Resources["HybridRunbookWorkerGroup"] = otherResource - delete(apiDefinition.Resources, "ListAllHybridRunbookWorkerGroupInAutomationAccount") + input.Resources["HybridRunbookWorkerGroup"] = otherResource + delete(input.Resources, "ListAllHybridRunbookWorkerGroupInAutomationAccount") - return &apiDefinition, nil + return &input, nil } diff --git a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_automation_25435.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_automation_25435.go similarity index 57% rename from tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_automation_25435.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_automation_25435.go index 73b74499087..3cfe87e71a9 100644 --- a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_automation_25435.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_automation_25435.go @@ -6,7 +6,7 @@ package dataworkarounds import ( "fmt" - importerModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/models" + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" ) var _ workaround = workaroundAutomation25435{} @@ -16,9 +16,9 @@ var _ workaround = workaroundAutomation25435{} type workaroundAutomation25435 struct { } -func (workaroundAutomation25435) IsApplicable(apiDefinition *importerModels.AzureApiDefinition) bool { - serviceMatches := apiDefinition.ServiceName == "Automation" - apiVersionMatches := apiDefinition.ApiVersion == "2022-08-08" || apiDefinition.ApiVersion == "2023-11-01" +func (workaroundAutomation25435) IsApplicable(serviceName string, apiVersion sdkModels.APIVersion) bool { + serviceMatches := serviceName == "Automation" + apiVersionMatches := apiVersion.APIVersion == "2022-08-08" || apiVersion.APIVersion == "2023-11-01" return serviceMatches && apiVersionMatches } @@ -26,8 +26,8 @@ func (workaroundAutomation25435) Name() string { return "Automation / 25434" } -func (workaroundAutomation25435) Process(apiDefinition importerModels.AzureApiDefinition) (*importerModels.AzureApiDefinition, error) { - resource, ok := apiDefinition.Resources["Python3Package"] +func (workaroundAutomation25435) Process(input sdkModels.APIVersion) (*sdkModels.APIVersion, error) { + resource, ok := input.Resources["Python3Package"] if !ok { return nil, fmt.Errorf("expected a Resource named `Python3Package`") } @@ -38,7 +38,7 @@ func (workaroundAutomation25435) Process(apiDefinition importerModels.AzureApiDe operation.LongRunning = true resource.Operations["CreateOrUpdate"] = operation - apiDefinition.Resources["Python3Package"] = resource + input.Resources["Python3Package"] = resource - return &apiDefinition, nil + return &input, nil } diff --git a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_batch_21291.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_batch_21291.go similarity index 62% rename from tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_batch_21291.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_batch_21291.go index 9ae5a6e243d..ed9b93ef942 100644 --- a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_batch_21291.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_batch_21291.go @@ -6,7 +6,7 @@ package dataworkarounds import ( "fmt" - importerModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/models" + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" ) var _ workaround = workaroundBatch21291{} @@ -17,9 +17,9 @@ var _ workaround = workaroundBatch21291{} type workaroundBatch21291 struct { } -func (workaroundBatch21291) IsApplicable(apiDefinition *importerModels.AzureApiDefinition) bool { - serviceMatches := apiDefinition.ServiceName == "Batch" - apiVersionMatches := apiDefinition.ApiVersion == "2022-01-01" || apiDefinition.ApiVersion == "2022-10-01" || apiDefinition.ApiVersion == "2023-05-01" +func (workaroundBatch21291) IsApplicable(serviceName string, apiVersion sdkModels.APIVersion) bool { + serviceMatches := serviceName == "Batch" + apiVersionMatches := apiVersion.APIVersion == "2022-01-01" || apiVersion.APIVersion == "2022-10-01" || apiVersion.APIVersion == "2023-05-01" return serviceMatches && apiVersionMatches } @@ -27,8 +27,8 @@ func (workaroundBatch21291) Name() string { return "Batch / 21291" } -func (workaroundBatch21291) Process(apiDefinition importerModels.AzureApiDefinition) (*importerModels.AzureApiDefinition, error) { - resource, ok := apiDefinition.Resources["BatchAccount"] +func (workaroundBatch21291) Process(input sdkModels.APIVersion) (*sdkModels.APIVersion, error) { + resource, ok := input.Resources["BatchAccount"] if !ok { return nil, fmt.Errorf("couldn't find API Resource BatchAccount") } @@ -44,6 +44,6 @@ func (workaroundBatch21291) Process(apiDefinition importerModels.AzureApiDefinit model.Fields["StorageAccountId"] = field resource.Models["AutoStorageBaseProperties"] = model - apiDefinition.Resources["BatchAccount"] = resource - return &apiDefinition, nil + input.Resources["BatchAccount"] = resource + return &input, nil } diff --git a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_botservice_27351.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_botservice_27351.go similarity index 86% rename from tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_botservice_27351.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_botservice_27351.go index 79d94c0c7c4..3215ff9c310 100644 --- a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_botservice_27351.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_botservice_27351.go @@ -8,7 +8,6 @@ import ( "github.com/hashicorp/go-azure-helpers/lang/pointer" sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" - importerModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/models" ) var _ workaround = workaroundBotService27351{} @@ -16,7 +15,7 @@ var _ workaround = workaroundBotService27351{} type workaroundBotService27351 struct { } -func (workaroundBotService27351) IsApplicable(apiDefinition *importerModels.AzureApiDefinition) bool { +func (workaroundBotService27351) IsApplicable(serviceName string, apiVersion sdkModels.APIVersion) bool { // This workaround fixes an issue where the BotService Channel URI is defined using two subtly different Resource IDs. // Fix: https://github.com/Azure/azure-rest-api-specs/pull/27351 // @@ -28,7 +27,7 @@ func (workaroundBotService27351) IsApplicable(apiDefinition *importerModels.Azur // // As such this workaround fixes this issue by normalizing the resulting output - since these are the same endpoint/should // support the same values for this URI Segment. - serviceMatches := apiDefinition.ServiceName == "BotService" + serviceMatches := serviceName == "BotService" apiVersions := map[string]struct{}{ "2021-05-01-preview": {}, "2022-06-15-preview": {}, @@ -37,7 +36,7 @@ func (workaroundBotService27351) IsApplicable(apiDefinition *importerModels.Azur "2021-03-01": {}, "2022-09-15": {}, } - _, apiVersionMatches := apiVersions[apiDefinition.ApiVersion] + _, apiVersionMatches := apiVersions[apiVersion.APIVersion] return serviceMatches && apiVersionMatches } @@ -45,7 +44,7 @@ func (workaroundBotService27351) Name() string { return "BotService / 27351" } -func (workaroundBotService27351) Process(input importerModels.AzureApiDefinition) (*importerModels.AzureApiDefinition, error) { +func (workaroundBotService27351) Process(input sdkModels.APIVersion) (*sdkModels.APIVersion, error) { output := input resource, ok := output.Resources["Channel"] diff --git a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_containerservice_21394.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_containerservice_21394.go similarity index 63% rename from tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_containerservice_21394.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_containerservice_21394.go index b779be6f421..e49f663e8ec 100644 --- a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_containerservice_21394.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_containerservice_21394.go @@ -6,7 +6,7 @@ package dataworkarounds import ( "fmt" - importerModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/models" + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" ) var _ workaround = workaroundContainerService21394{} @@ -15,16 +15,16 @@ var _ workaround = workaroundContainerService21394{} // Swagger PR: https://github.com/Azure/azure-rest-api-specs/pull/21394 type workaroundContainerService21394 struct{} -func (workaroundContainerService21394) IsApplicable(apiDefinition *importerModels.AzureApiDefinition) bool { - return apiDefinition.ServiceName == "ContainerService" && apiDefinition.ApiVersion == "2022-09-02-preview" +func (workaroundContainerService21394) IsApplicable(serviceName string, apiVersion sdkModels.APIVersion) bool { + return serviceName == "ContainerService" && apiVersion.APIVersion == "2022-09-02-preview" } func (workaroundContainerService21394) Name() string { return "ContainerService / 21394" } -func (workaroundContainerService21394) Process(apiDefinition importerModels.AzureApiDefinition) (*importerModels.AzureApiDefinition, error) { - resource, ok := apiDefinition.Resources["Fleets"] +func (workaroundContainerService21394) Process(input sdkModels.APIVersion) (*sdkModels.APIVersion, error) { + resource, ok := input.Resources["Fleets"] if !ok { return nil, fmt.Errorf("couldn't find API Resource Fleets") } @@ -42,6 +42,6 @@ func (workaroundContainerService21394) Process(apiDefinition importerModels.Azur model.Fields["DnsPrefix"] = field resource.Models["FleetHubProfile"] = model - apiDefinition.Resources["Fleets"] = resource - return &apiDefinition, nil + input.Resources["Fleets"] = resource + return &input, nil } diff --git a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_datafactory_23013.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_datafactory_23013.go similarity index 81% rename from tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_datafactory_23013.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_datafactory_23013.go index 2f6e888b06a..f49b4500b53 100644 --- a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_datafactory_23013.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_datafactory_23013.go @@ -8,7 +8,6 @@ import ( "github.com/hashicorp/go-azure-helpers/lang/pointer" sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" - importerModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/models" ) var _ workaround = workaroundDataFactory23013{} @@ -18,16 +17,16 @@ var _ workaround = workaroundDataFactory23013{} // Swagger PR: https://github.com/Azure/azure-rest-api-specs/pull/23013 type workaroundDataFactory23013 struct{} -func (workaroundDataFactory23013) IsApplicable(apiDefinition *importerModels.AzureApiDefinition) bool { - return apiDefinition.ServiceName == "DataFactory" && apiDefinition.ApiVersion == "2018-06-01" +func (workaroundDataFactory23013) IsApplicable(serviceName string, apiVersion sdkModels.APIVersion) bool { + return serviceName == "DataFactory" && apiVersion.APIVersion == "2018-06-01" } func (workaroundDataFactory23013) Name() string { return "DataFactory / 23013" } -func (workaroundDataFactory23013) Process(apiDefinition importerModels.AzureApiDefinition) (*importerModels.AzureApiDefinition, error) { - resource, ok := apiDefinition.Resources["DataFlowDebugSession"] +func (workaroundDataFactory23013) Process(input sdkModels.APIVersion) (*sdkModels.APIVersion, error) { + resource, ok := input.Resources["DataFlowDebugSession"] if !ok { return nil, fmt.Errorf("couldn't find API Resource DataFlowDebugSession") } @@ -88,6 +87,6 @@ func (workaroundDataFactory23013) Process(apiDefinition importerModels.AzureApiD // delete the now unused `Type` constant delete(resource.Constants, "Type") - apiDefinition.Resources["DataFlowDebugSession"] = resource - return &apiDefinition, nil + input.Resources["DataFlowDebugSession"] = resource + return &input, nil } diff --git a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_devcenter_id_to_required.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_devcenter_id_to_required.go similarity index 71% rename from tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_devcenter_id_to_required.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_devcenter_id_to_required.go index 7449ae17e00..21323b0416e 100644 --- a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_devcenter_id_to_required.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_devcenter_id_to_required.go @@ -6,7 +6,7 @@ package dataworkarounds import ( "fmt" - importerModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/models" + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" ) // This workaround marks the DevCenter field `DevCenterId` as Required rather than Optional @@ -19,18 +19,18 @@ var _ workaround = workaroundDevCenterIdToRequired{} type workaroundDevCenterIdToRequired struct { } -func (workaroundDevCenterIdToRequired) IsApplicable(apiDefinition *importerModels.AzureApiDefinition) bool { - return apiDefinition.ServiceName == "DevCenter" && apiDefinition.ApiVersion == "2023-04-01" +func (workaroundDevCenterIdToRequired) IsApplicable(serviceName string, apiVersion sdkModels.APIVersion) bool { + return serviceName == "DevCenter" && apiVersion.APIVersion == "2023-04-01" } func (workaroundDevCenterIdToRequired) Name() string { return "DevCenter" } -func (workaroundDevCenterIdToRequired) Process(apiDefinition importerModels.AzureApiDefinition) (*importerModels.AzureApiDefinition, error) { +func (workaroundDevCenterIdToRequired) Process(input sdkModels.APIVersion) (*sdkModels.APIVersion, error) { // The Field `DevCenterId` within the SDKModel `Projects` within the APIResource `Projects` should be marked as Required - projectsResource, ok := apiDefinition.Resources["Projects"] + projectsResource, ok := input.Resources["Projects"] if !ok { return nil, fmt.Errorf("expected a Resource named `Projects` but didn't get one") } @@ -48,7 +48,7 @@ func (workaroundDevCenterIdToRequired) Process(apiDefinition importerModels.Azur projectModel.Fields["DevCenterId"] = projectDevCenterIdField projectsResource.Models["ProjectProperties"] = projectModel - apiDefinition.Resources["Projects"] = projectsResource + input.Resources["Projects"] = projectsResource - return &apiDefinition, nil + return &input, nil } diff --git a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_digitaltwins_25120.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_digitaltwins_25120.go similarity index 72% rename from tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_digitaltwins_25120.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_digitaltwins_25120.go index da2d980afa6..23b7151f73b 100644 --- a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_digitaltwins_25120.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_digitaltwins_25120.go @@ -7,7 +7,6 @@ import ( "fmt" sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" - importerModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/models" ) var _ workaround = workaroundDigitalTwins25120{} @@ -15,20 +14,20 @@ var _ workaround = workaroundDigitalTwins25120{} // Swagger PR: https://github.com/Azure/azure-rest-api-specs/pull/21520 type workaroundDigitalTwins25120 struct{} -func (workaroundDigitalTwins25120) IsApplicable(apiDefinition *importerModels.AzureApiDefinition) bool { +func (workaroundDigitalTwins25120) IsApplicable(serviceName string, apiVersion sdkModels.APIVersion) bool { // API Defines a Constant with the string values `"true"` and `"false`: // RecordPropertyAndItemRemovals *RecordPropertyAndItemRemovals `json:"recordPropertyAndItemRemovals,omitempty"` // but the API returns a boolean: // "recordPropertyAndItemRemovals": false, - return apiDefinition.ServiceName == "DigitalTwins" && apiDefinition.ApiVersion == "2023-01-31" + return serviceName == "DigitalTwins" && apiVersion.APIVersion == "2023-01-31" } func (workaroundDigitalTwins25120) Name() string { return "DigitalTwins / 25120" } -func (workaroundDigitalTwins25120) Process(apiDefinition importerModels.AzureApiDefinition) (*importerModels.AzureApiDefinition, error) { - resource, ok := apiDefinition.Resources["TimeSeriesDatabaseConnections"] +func (workaroundDigitalTwins25120) Process(input sdkModels.APIVersion) (*sdkModels.APIVersion, error) { + resource, ok := input.Resources["TimeSeriesDatabaseConnections"] if !ok { return nil, fmt.Errorf("expected a Resource named `TimeSeriesDatabaseConnections`") } @@ -52,6 +51,6 @@ func (workaroundDigitalTwins25120) Process(apiDefinition importerModels.AzureApi } delete(resource.Constants, "RecordPropertyAndItemRemovals") - apiDefinition.Resources["TimeSeriesDatabaseConnections"] = resource - return &apiDefinition, nil + input.Resources["TimeSeriesDatabaseConnections"] = resource + return &input, nil } diff --git a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_hdinsight_26838.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_hdinsight_26838.go similarity index 79% rename from tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_hdinsight_26838.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_hdinsight_26838.go index 2d07876bafb..a1ff2fa49db 100644 --- a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_hdinsight_26838.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_hdinsight_26838.go @@ -8,7 +8,6 @@ import ( "github.com/hashicorp/go-azure-helpers/lang/pointer" sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" - importerModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/models" ) var _ workaround = workaroundHDInsight26838{} @@ -16,7 +15,7 @@ var _ workaround = workaroundHDInsight26838{} // Swagger PR: https://github.com/Azure/azure-rest-api-specs/pull/26838 type workaroundHDInsight26838 struct{} -func (workaroundHDInsight26838) IsApplicable(apiDefinition *importerModels.AzureApiDefinition) bool { +func (workaroundHDInsight26838) IsApplicable(serviceName string, apiVersion sdkModels.APIVersion) bool { // The field `kind` is a constant within the API but isn't documented as such - whilst // we've previously been using this as a String - since we need to recase the value as // the API returns this inconsistently - and there's a fixed list of possible values @@ -31,8 +30,8 @@ func (workaroundHDInsight26838) IsApplicable(apiDefinition *importerModels.Azure "2023-08-15-preview": {}, } - serviceMatches := apiDefinition.ServiceName == "HDInsight" - _, apiVersionMatches := supportedApiVersions[apiDefinition.ApiVersion] + serviceMatches := serviceName == "HDInsight" + _, apiVersionMatches := supportedApiVersions[apiVersion.APIVersion] return serviceMatches && apiVersionMatches } @@ -40,8 +39,8 @@ func (workaroundHDInsight26838) Name() string { return "HDInsight / 26838" } -func (workaroundHDInsight26838) Process(apiDefinition importerModels.AzureApiDefinition) (*importerModels.AzureApiDefinition, error) { - resource, ok := apiDefinition.Resources["Clusters"] +func (workaroundHDInsight26838) Process(input sdkModels.APIVersion) (*sdkModels.APIVersion, error) { + resource, ok := input.Resources["Clusters"] if !ok { return nil, fmt.Errorf("expected a Resource named `Clusters`") } @@ -76,6 +75,6 @@ func (workaroundHDInsight26838) Process(apiDefinition importerModels.AzureApiDef }, } - apiDefinition.Resources["Clusters"] = resource - return &apiDefinition, nil + input.Resources["Clusters"] = resource + return &input, nil } diff --git a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_inconsistent_swagger_segments.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_inconsistent_swagger_segments.go similarity index 71% rename from tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_inconsistent_swagger_segments.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_inconsistent_swagger_segments.go index a20ce9cfdb2..b2e91847987 100644 --- a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_inconsistent_swagger_segments.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_inconsistent_swagger_segments.go @@ -7,17 +7,16 @@ import ( "fmt" "strings" - "github.com/hashicorp/pandora/tools/data-api-sdk/v1/helpers" + sdkHelpers "github.com/hashicorp/pandora/tools/data-api-sdk/v1/helpers" sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/components/parser/cleanup" - importerModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/models" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup" ) var _ workaround = workaroundInconsistentlyDefinedSegments{} type workaroundInconsistentlyDefinedSegments struct{} -func (workaroundInconsistentlyDefinedSegments) IsApplicable(_ *importerModels.AzureApiDefinition) bool { +func (workaroundInconsistentlyDefinedSegments) IsApplicable(_ string, _ sdkModels.APIVersion) bool { return true } @@ -25,10 +24,10 @@ func (workaroundInconsistentlyDefinedSegments) Name() string { return "Workaround Inconsistently Defined Resource ID Segments" } -func (workaroundInconsistentlyDefinedSegments) Process(apiDefinition importerModels.AzureApiDefinition) (*importerModels.AzureApiDefinition, error) { - resources := make(map[string]sdkModels.APIResource, 0) - for resourceName := range apiDefinition.Resources { - resource := apiDefinition.Resources[resourceName] +func (workaroundInconsistentlyDefinedSegments) Process(input sdkModels.APIVersion) (*sdkModels.APIVersion, error) { + resources := make(map[string]sdkModels.APIResource) + for resourceName := range input.Resources { + resource := input.Resources[resourceName] ids := make(map[string]sdkModels.ResourceID) for key := range resource.ResourceIDs { @@ -54,12 +53,12 @@ func (workaroundInconsistentlyDefinedSegments) Process(apiDefinition importerMod segments = append(segments, val) } id.Segments = segments - id.ExampleValue = helpers.DisplayValueForResourceID(id) + id.ExampleValue = sdkHelpers.DisplayValueForResourceID(id) ids[key] = id } resource.ResourceIDs = ids resources[resourceName] = resource } - apiDefinition.Resources = resources - return &apiDefinition, nil + input.Resources = resources + return &input, nil } diff --git a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_invalid_go_package_names.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_invalid_go_package_names.go similarity index 63% rename from tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_invalid_go_package_names.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_invalid_go_package_names.go index 14887002041..790fc4ae306 100644 --- a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_invalid_go_package_names.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_invalid_go_package_names.go @@ -8,15 +8,14 @@ import ( "strings" sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" - importerModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/models" ) var _ workaround = workaroundInvalidGoPackageNames{} type workaroundInvalidGoPackageNames struct{} -func (workaroundInvalidGoPackageNames) IsApplicable(apiDefinition *importerModels.AzureApiDefinition) bool { - for key := range apiDefinition.Resources { +func (workaroundInvalidGoPackageNames) IsApplicable(_ string, apiVersion sdkModels.APIVersion) bool { + for key := range apiVersion.Resources { if strings.EqualFold(key, "documentation") { return true } @@ -28,24 +27,24 @@ func (workaroundInvalidGoPackageNames) Name() string { return "Workaround Invalid Go Package Names" } -func (workaroundInvalidGoPackageNames) Process(apiDefinition importerModels.AzureApiDefinition) (*importerModels.AzureApiDefinition, error) { +func (workaroundInvalidGoPackageNames) Process(input sdkModels.APIVersion) (*sdkModels.APIVersion, error) { resources := make(map[string]sdkModels.APIResource, 0) - for resourceName := range apiDefinition.Resources { + for resourceName := range input.Resources { originalName := resourceName - resource := apiDefinition.Resources[originalName] + resource := input.Resources[originalName] // `documentation` is not a valid Go package name, so let's rename it to `DocumentationResource` // double-checking that we're not overwriting anything if strings.EqualFold(resourceName, "documentation") { resourceName = "DocumentationResource" - if _, ok := apiDefinition.Resources[resourceName]; ok { + if _, ok := input.Resources[resourceName]; ok { return nil, fmt.Errorf("the Resource %q is not valid as a Go Package Name - however the replacement name %q is already in use", originalName, resourceName) } } resources[resourceName] = resource } - apiDefinition.Resources = resources - return &apiDefinition, nil + input.Resources = resources + return &input, nil } diff --git a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_loadtest_20961.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_loadtest_20961.go similarity index 62% rename from tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_loadtest_20961.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_loadtest_20961.go index 2dd17ada4c4..7fbb3dc876b 100644 --- a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_loadtest_20961.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_loadtest_20961.go @@ -7,7 +7,6 @@ import ( "fmt" sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" - importerModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/models" ) var _ workaround = workaroundLoadTest20961{} @@ -18,9 +17,9 @@ var _ workaround = workaroundLoadTest20961{} type workaroundLoadTest20961 struct { } -func (workaroundLoadTest20961) IsApplicable(apiDefinition *importerModels.AzureApiDefinition) bool { - serviceMatches := apiDefinition.ServiceName == "LoadTestService" - apiVersionMatches := apiDefinition.ApiVersion == "2021-12-01-preview" || apiDefinition.ApiVersion == "2022-04-15-preview" || apiDefinition.ApiVersion == "2022-12-01" +func (workaroundLoadTest20961) IsApplicable(serviceName string, apiVersion sdkModels.APIVersion) bool { + serviceMatches := serviceName == "LoadTestService" + apiVersionMatches := apiVersion.APIVersion == "2021-12-01-preview" || apiVersion.APIVersion == "2022-04-15-preview" || apiVersion.APIVersion == "2022-12-01" return serviceMatches && apiVersionMatches } @@ -28,8 +27,8 @@ func (workaroundLoadTest20961) Name() string { return "LoadTest / 20961" } -func (workaroundLoadTest20961) Process(apiDefinition importerModels.AzureApiDefinition) (*importerModels.AzureApiDefinition, error) { - resource, ok := apiDefinition.Resources["LoadTests"] +func (workaroundLoadTest20961) Process(input sdkModels.APIVersion) (*sdkModels.APIVersion, error) { + resource, ok := input.Resources["LoadTests"] if !ok { return nil, fmt.Errorf("couldn't find API Resource LoadTests") } @@ -47,6 +46,6 @@ func (workaroundLoadTest20961) Process(apiDefinition importerModels.AzureApiDefi model.Fields["Tags"] = field resource.Models["LoadTestResourceUpdate"] = model - apiDefinition.Resources["LoadTests"] = resource - return &apiDefinition, nil + input.Resources["LoadTests"] = resource + return &input, nil } diff --git a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_machinelearning_25142.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_machinelearning_25142.go similarity index 59% rename from tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_machinelearning_25142.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_machinelearning_25142.go index ef7f2d7e9d8..63d9df34dad 100644 --- a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_machinelearning_25142.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_machinelearning_25142.go @@ -7,7 +7,7 @@ import ( "fmt" "net/http" - importerModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/models" + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" ) var _ workaround = workaroundMachineLearning25142{} @@ -17,9 +17,9 @@ var _ workaround = workaroundMachineLearning25142{} type workaroundMachineLearning25142 struct { } -func (workaroundMachineLearning25142) IsApplicable(apiDefinition *importerModels.AzureApiDefinition) bool { - serviceMatches := apiDefinition.ServiceName == "MachineLearningServices" - apiVersionMatches := apiDefinition.ApiVersion == "2023-04-01" +func (workaroundMachineLearning25142) IsApplicable(serviceName string, apiVersion sdkModels.APIVersion) bool { + serviceMatches := serviceName == "MachineLearningServices" + apiVersionMatches := apiVersion.APIVersion == "2023-04-01" return serviceMatches && apiVersionMatches } @@ -27,8 +27,8 @@ func (workaroundMachineLearning25142) Name() string { return "MachineLearningServices / 25142" } -func (workaroundMachineLearning25142) Process(apiDefinition importerModels.AzureApiDefinition) (*importerModels.AzureApiDefinition, error) { - resource, ok := apiDefinition.Resources["RegistryManagement"] +func (workaroundMachineLearning25142) Process(input sdkModels.APIVersion) (*sdkModels.APIVersion, error) { + resource, ok := input.Resources["RegistryManagement"] if !ok { return nil, fmt.Errorf("couldn't find API Resource RegistryManagement") } @@ -41,6 +41,6 @@ func (workaroundMachineLearning25142) Process(apiDefinition importerModels.Azure operation.ExpectedStatusCodes = append(operation.ExpectedStatusCodes, http.StatusAccepted) resource.Operations["RegistriesCreateOrUpdate"] = operation - apiDefinition.Resources["RegistryManagement"] = resource - return &apiDefinition, nil + input.Resources["RegistryManagement"] = resource + return &input, nil } diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_network_29303.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_network_29303.go new file mode 100644 index 00000000000..b4d1f91d5d0 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_network_29303.go @@ -0,0 +1,48 @@ +package dataworkarounds + +import ( + "fmt" + + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/combine" +) + +var _ workaround = workaroundNetwork29303{} + +// The swagger for PrivateLinkService in Network associates the CreateOrUpdate method with the tag +// `PrivateLinkService` instead of `PrivateLinkServices` like the rest of the operations do. This consolidates +// the two resources that Pandora identifies into one. +// Can be removed when https://github.com/Azure/azure-rest-api-specs/pull/29303 has been merged. +type workaroundNetwork29303 struct { +} + +func (workaroundNetwork29303) IsApplicable(serviceName string, apiVersion sdkModels.APIVersion) bool { + serviceMatches := serviceName == "Network" + _, hasCorrectlyNamedAPIResource := apiVersion.Resources["PrivateLinkService"] + _, hasMisnamedAPIResource := apiVersion.Resources["PrivateLinkServices"] + return serviceMatches && hasCorrectlyNamedAPIResource && hasMisnamedAPIResource +} + +func (workaroundNetwork29303) Name() string { + return "Network / 29303" +} + +func (workaroundNetwork29303) Process(input sdkModels.APIVersion) (*sdkModels.APIVersion, error) { + correctlyNamedAPIResource, ok := input.Resources["PrivateLinkServices"] + if !ok { + return nil, fmt.Errorf("expected an APIResource named `PrivateLinkServices` but didn't find one") + } + misnamedAPIResource, ok := input.Resources["PrivateLinkService"] + if !ok { + return nil, fmt.Errorf("expected an APIResource named `PrivateLinkService` but didn't find one") + } + + combined, err := combine.APIResource(correctlyNamedAPIResource, misnamedAPIResource) + if err != nil { + return nil, fmt.Errorf("combining the APIResource `PrivateLinkServices`: %+v", err) + } + input.Resources["PrivateLinkServices"] = *combined + delete(input.Resources, "PrivateLinkService") + + return &input, nil +} diff --git a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_newrelic_29256.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_newrelic_29256.go similarity index 69% rename from tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_newrelic_29256.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_newrelic_29256.go index 603cdf858c7..403be1db58b 100644 --- a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_newrelic_29256.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_newrelic_29256.go @@ -7,7 +7,6 @@ import ( "fmt" sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" - importerModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/models" ) var _ workaround = workaroundNewRelic29256{} @@ -18,9 +17,9 @@ var _ workaround = workaroundNewRelic29256{} type workaroundNewRelic29256 struct { } -func (workaroundNewRelic29256) IsApplicable(apiDefinition *importerModels.AzureApiDefinition) bool { - serviceMatches := apiDefinition.ServiceName == "NewRelic" - apiVersionMatches := apiDefinition.ApiVersion == "2022-07-01" || apiDefinition.ApiVersion == "2024-03-01" +func (workaroundNewRelic29256) IsApplicable(serviceName string, apiVersion sdkModels.APIVersion) bool { + serviceMatches := serviceName == "NewRelic" + apiVersionMatches := apiVersion.APIVersion == "2022-07-01" || apiVersion.APIVersion == "2024-03-01" return serviceMatches && apiVersionMatches } @@ -28,9 +27,9 @@ func (workaroundNewRelic29256) Name() string { return "NewRelic / 29256" } -func (workaroundNewRelic29256) Process(apiDefinition importerModels.AzureApiDefinition) (*importerModels.AzureApiDefinition, error) { +func (workaroundNewRelic29256) Process(input sdkModels.APIVersion) (*sdkModels.APIVersion, error) { // NewRelicMonitorResource,NewRelicMonitorResourceUpdate - resource, ok := apiDefinition.Resources["Monitors"] + resource, ok := input.Resources["Monitors"] if !ok { return nil, fmt.Errorf("couldn't find API Resource `Monitors`") } @@ -56,6 +55,6 @@ func (workaroundNewRelic29256) Process(apiDefinition importerModels.AzureApiDefi resource.Models[modelName] = model } - apiDefinition.Resources["Monitors"] = resource - return &apiDefinition, nil + input.Resources["Monitors"] = resource + return &input, nil } diff --git a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_operationalinsights_27524.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_operationalinsights_27524.go similarity index 59% rename from tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_operationalinsights_27524.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_operationalinsights_27524.go index 63e2a2f685e..37d438b2bb1 100644 --- a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_operationalinsights_27524.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_operationalinsights_27524.go @@ -6,7 +6,7 @@ package dataworkarounds import ( "fmt" - importerModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/models" + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" ) var _ workaround = workaroundOperationalinsights27524{} @@ -15,16 +15,16 @@ var _ workaround = workaroundOperationalinsights27524{} // Swagger PR: https://github.com/Azure/azure-rest-api-specs/pull/27524 type workaroundOperationalinsights27524 struct{} -func (workaroundOperationalinsights27524) IsApplicable(apiDefinition *importerModels.AzureApiDefinition) bool { - return apiDefinition.ServiceName == "OperationalInsights" && apiDefinition.ApiVersion == "2019-09-01" +func (workaroundOperationalinsights27524) IsApplicable(serviceName string, apiVersion sdkModels.APIVersion) bool { + return serviceName == "OperationalInsights" && apiVersion.APIVersion == "2019-09-01" } func (workaroundOperationalinsights27524) Name() string { return "OperationalInsights / 27524" } -func (workaroundOperationalinsights27524) Process(apiDefinition importerModels.AzureApiDefinition) (*importerModels.AzureApiDefinition, error) { - resource, ok := apiDefinition.Resources["QueryPacks"] +func (workaroundOperationalinsights27524) Process(input sdkModels.APIVersion) (*sdkModels.APIVersion, error) { + resource, ok := input.Resources["QueryPacks"] if !ok { return nil, fmt.Errorf("expected a Resource named `QueryPacks` but didn't get one") } @@ -32,8 +32,9 @@ func (workaroundOperationalinsights27524) Process(apiDefinition importerModels.A if !ok { return nil, fmt.Errorf("expected an Operation named `CreateOrUpdate` but didn't get one") } + operation.ExpectedStatusCodes = append(operation.ExpectedStatusCodes, 201) resource.Operations["CreateOrUpdate"] = operation - apiDefinition.Resources["QueryPacks"] = resource - return &apiDefinition, nil + input.Resources["QueryPacks"] = resource + return &input, nil } diff --git a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_recoveryservicessiterecovery_26680.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_recoveryservicessiterecovery_26680.go similarity index 78% rename from tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_recoveryservicessiterecovery_26680.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_recoveryservicessiterecovery_26680.go index 5c9d21ed2f5..558aaf4f70a 100644 --- a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_recoveryservicessiterecovery_26680.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_recoveryservicessiterecovery_26680.go @@ -8,7 +8,6 @@ import ( "github.com/hashicorp/go-azure-helpers/lang/pointer" sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" - importerModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/models" ) var _ workaround = workaroundRecoveryServicesSiteRecovery26680{} @@ -21,8 +20,8 @@ var _ workaround = workaroundRecoveryServicesSiteRecovery26680{} type workaroundRecoveryServicesSiteRecovery26680 struct { } -func (w workaroundRecoveryServicesSiteRecovery26680) IsApplicable(apiDefinition *importerModels.AzureApiDefinition) bool { - if apiDefinition.ServiceName != "RecoveryServicesSiteRecovery" { +func (w workaroundRecoveryServicesSiteRecovery26680) IsApplicable(serviceName string, apiVersion sdkModels.APIVersion) bool { + if serviceName != "RecoveryServicesSiteRecovery" { return false } @@ -33,7 +32,7 @@ func (w workaroundRecoveryServicesSiteRecovery26680) IsApplicable(apiDefinition "2023-04-01": {}, "2023-06-01": {}, } - if _, apiVersionMatches := apiVersions[apiDefinition.ApiVersion]; !apiVersionMatches { + if _, apiVersionMatches := apiVersions[apiVersion.APIVersion]; !apiVersionMatches { return false } @@ -48,8 +47,8 @@ func (w workaroundRecoveryServicesSiteRecovery26680) Name() string { return "RecoveryServicesSiteRecovery / 26680" } -func (w workaroundRecoveryServicesSiteRecovery26680) Process(apiDefinition importerModels.AzureApiDefinition) (*importerModels.AzureApiDefinition, error) { - resource, ok := apiDefinition.Resources["VaultCertificates"] +func (w workaroundRecoveryServicesSiteRecovery26680) Process(input sdkModels.APIVersion) (*sdkModels.APIVersion, error) { + resource, ok := input.Resources["VaultCertificates"] if !ok { return nil, fmt.Errorf("expected a Resource named `VaultCertificates` but didn't get one") } @@ -85,6 +84,6 @@ func (w workaroundRecoveryServicesSiteRecovery26680) Process(apiDefinition impor } resource.Models["CertificateRequest"] = model - apiDefinition.Resources["VaultCertificates"] = resource - return &apiDefinition, nil + input.Resources["VaultCertificates"] = resource + return &input, nil } diff --git a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_redis_22407.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_redis_22407.go similarity index 58% rename from tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_redis_22407.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_redis_22407.go index 7b3022c9b4c..5e987928086 100644 --- a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_redis_22407.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_redis_22407.go @@ -5,15 +5,14 @@ package dataworkarounds import ( sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" - importerModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/models" ) type workaroundRedis22407 struct { } -func (w workaroundRedis22407) IsApplicable(apiDefinition *importerModels.AzureApiDefinition) bool { - serviceMatches := apiDefinition.ServiceName == "Redis" - apiVersionMatches := apiDefinition.ApiVersion == "2022-06-01" || apiDefinition.ApiVersion == "2023-04-01" || apiDefinition.ApiVersion == "2023-08-01" +func (w workaroundRedis22407) IsApplicable(serviceName string, apiVersion sdkModels.APIVersion) bool { + serviceMatches := serviceName == "Redis" + apiVersionMatches := apiVersion.APIVersion == "2022-06-01" || apiVersion.APIVersion == "2023-04-01" || apiVersion.APIVersion == "2023-08-01" return serviceMatches && apiVersionMatches } @@ -21,8 +20,8 @@ func (w workaroundRedis22407) Name() string { return "Redis / 22407" } -func (w workaroundRedis22407) Process(apiDefinition importerModels.AzureApiDefinition) (*importerModels.AzureApiDefinition, error) { - if resource, ok := apiDefinition.Resources["Redis"]; ok { +func (w workaroundRedis22407) Process(input sdkModels.APIVersion) (*sdkModels.APIVersion, error) { + if resource, ok := input.Resources["Redis"]; ok { if model, ok := resource.Models["RedisCommonPropertiesRedisConfiguration"]; ok { if _, ok := model.Fields["NotifyKeyspaceEvents"]; !ok { @@ -39,7 +38,7 @@ func (w workaroundRedis22407) Process(apiDefinition importerModels.AzureApiDefin } resource.Models["RedisCommonPropertiesRedisConfiguration"] = model } - apiDefinition.Resources["Redis"] = resource + input.Resources["Redis"] = resource } - return &apiDefinition, nil + return &input, nil } diff --git a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_streamanalytics_27577.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_streamanalytics_27577.go similarity index 72% rename from tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_streamanalytics_27577.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_streamanalytics_27577.go index 654808b405f..63db159ab0f 100644 --- a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_streamanalytics_27577.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_streamanalytics_27577.go @@ -7,7 +7,6 @@ import ( "fmt" sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" - importerModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/models" ) // workaroundStreamAnalytics27577 is a workaround to account for StreamAnalytics containing its own @@ -17,9 +16,9 @@ import ( type workaroundStreamAnalytics27577 struct { } -func (w workaroundStreamAnalytics27577) IsApplicable(apiDefinition *importerModels.AzureApiDefinition) bool { - serviceMatches := apiDefinition.ServiceName == "StreamAnalytics" - apiVersionMatches := apiDefinition.ApiVersion == "2020-03-01" || apiDefinition.ApiVersion == "2021-10-01-preview" +func (w workaroundStreamAnalytics27577) IsApplicable(serviceName string, apiVersion sdkModels.APIVersion) bool { + serviceMatches := serviceName == "StreamAnalytics" + apiVersionMatches := apiVersion.APIVersion == "2020-03-01" || apiVersion.APIVersion == "2021-10-01-preview" return serviceMatches && apiVersionMatches } @@ -27,18 +26,18 @@ func (w workaroundStreamAnalytics27577) Name() string { return "StreamAnalytics / 27577" } -func (w workaroundStreamAnalytics27577) Process(apiDefinition importerModels.AzureApiDefinition) (*importerModels.AzureApiDefinition, error) { +func (w workaroundStreamAnalytics27577) Process(input sdkModels.APIVersion) (*sdkModels.APIVersion, error) { apiResourcesToFix := []string{ "StreamingJobs", } // API version 2021-10-01-preview and (presumably) later contain the full `StreamingJob` object - however // API version `2020-03-01` doesn't define it - if apiDefinition.ApiVersion != "2020-03-01" { + if input.APIVersion != "2020-03-01" { apiResourcesToFix = append(apiResourcesToFix, "Subscriptions") } for _, apiResource := range apiResourcesToFix { - resource, ok := apiDefinition.Resources[apiResource] + resource, ok := input.Resources[apiResource] if !ok { return nil, fmt.Errorf("expected to find the API Resource %q but didn't", apiResource) } @@ -57,7 +56,7 @@ func (w workaroundStreamAnalytics27577) Process(apiDefinition importerModels.Azu field.ObjectDefinition = sdkModels.SDKObjectDefinition{ Type: sdkModels.SystemOrUserAssignedIdentityMapSDKObjectDefinitionType, } - if apiDefinition.ApiVersion == "2020-03-01" { + if input.APIVersion == "2020-03-01" { // however API version 2020-03-01 only supports SystemAssigned field.ObjectDefinition.Type = sdkModels.SystemAssignedIdentitySDKObjectDefinitionType } @@ -68,7 +67,7 @@ func (w workaroundStreamAnalytics27577) Process(apiDefinition importerModels.Azu // then delete the standalone identity model delete(resource.Models, "Identity") - apiDefinition.Resources[apiResource] = resource + input.Resources[apiResource] = resource } - return &apiDefinition, nil + return &input, nil } diff --git a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_subscription_20254.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_subscription_20254.go similarity index 78% rename from tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_subscription_20254.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_subscription_20254.go index b6dfa8c9c4e..2a1a482b829 100644 --- a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_subscription_20254.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_subscription_20254.go @@ -8,7 +8,6 @@ import ( "github.com/hashicorp/go-azure-helpers/lang/pointer" sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" - importerModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/models" ) var _ workaround = workaroundSubscriptions20254{} @@ -18,15 +17,15 @@ var _ workaround = workaroundSubscriptions20254{} type workaroundSubscriptions20254 struct { } -func (w workaroundSubscriptions20254) IsApplicable(apiDefinition *importerModels.AzureApiDefinition) bool { - return apiDefinition.ServiceName == "Subscription" && apiDefinition.ApiVersion == "2021-10-01" +func (w workaroundSubscriptions20254) IsApplicable(serviceName string, apiVersion sdkModels.APIVersion) bool { + return serviceName == "Subscription" && apiVersion.APIVersion == "2021-10-01" } func (w workaroundSubscriptions20254) Name() string { return "Subscription / 20254" } -func (w workaroundSubscriptions20254) Process(input importerModels.AzureApiDefinition) (*importerModels.AzureApiDefinition, error) { +func (w workaroundSubscriptions20254) Process(input sdkModels.APIVersion) (*sdkModels.APIVersion, error) { output := input subscriptionResource, ok := input.Resources["Subscriptions"] diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_temp_readonly_fields_containerservice.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_temp_readonly_fields_containerservice.go new file mode 100644 index 00000000000..1da2dee0a61 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_temp_readonly_fields_containerservice.go @@ -0,0 +1,32 @@ +package dataworkarounds + +import ( + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" +) + +var _ workaround = workaroundTempReadOnlyFieldsContainerService{} + +type workaroundTempReadOnlyFieldsContainerService struct { +} + +func (w workaroundTempReadOnlyFieldsContainerService) IsApplicable(serviceName string, apiVersion sdkModels.APIVersion) bool { + return serviceName == "ContainerService" && apiVersion.APIVersion == "2022-09-02-preview" +} + +func (w workaroundTempReadOnlyFieldsContainerService) Name() string { + return "Temp - Mark readonly fields as readonly - ContainerService" +} + +func (w workaroundTempReadOnlyFieldsContainerService) Process(input sdkModels.APIVersion) (*sdkModels.APIVersion, error) { + definition, err := markFieldAsComputed(input, "Fleets", "FleetHubProfile", "Fqdn") + if err != nil { + return nil, err + } + + definition, err = markFieldAsComputed(*definition, "Fleets", "FleetHubProfile", "KubernetesVersion") + if err != nil { + return nil, err + } + + return definition, nil +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_temp_readonly_fields_devcenter.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_temp_readonly_fields_devcenter.go new file mode 100644 index 00000000000..af091d71a2e --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_temp_readonly_fields_devcenter.go @@ -0,0 +1,35 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package dataworkarounds + +import ( + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" +) + +var _ workaround = workaroundTempReadOnlyFieldsDevCenter{} + +type workaroundTempReadOnlyFieldsDevCenter struct { +} + +func (w workaroundTempReadOnlyFieldsDevCenter) IsApplicable(serviceName string, apiVersion sdkModels.APIVersion) bool { + return serviceName == "DevCenter" && apiVersion.APIVersion == "2023-04-01" +} + +func (w workaroundTempReadOnlyFieldsDevCenter) Name() string { + return "Temp - Mark readonly fields as readonly - DevCenter" +} + +func (w workaroundTempReadOnlyFieldsDevCenter) Process(input sdkModels.APIVersion) (*sdkModels.APIVersion, error) { + definition, err := markFieldAsComputed(input, "Projects", "ProjectProperties", "DevCenterUri") + if err != nil { + return nil, err + } + + definition, err = markFieldAsComputed(*definition, "DevCenters", "DevCenterProperties", "DevCenterUri") + if err != nil { + return nil, err + } + + return definition, nil +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_temp_readonly_fields_loadtest.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_temp_readonly_fields_loadtest.go new file mode 100644 index 00000000000..01406d7bff8 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_temp_readonly_fields_loadtest.go @@ -0,0 +1,27 @@ +package dataworkarounds + +import ( + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" +) + +var _ workaround = workaroundTempReadOnlyFieldsLoadTest{} + +type workaroundTempReadOnlyFieldsLoadTest struct { +} + +func (w workaroundTempReadOnlyFieldsLoadTest) IsApplicable(serviceName string, apiVersion sdkModels.APIVersion) bool { + return serviceName == "LoadTestService" && apiVersion.APIVersion == "2022-12-01" +} + +func (w workaroundTempReadOnlyFieldsLoadTest) Name() string { + return "Temp - Mark readonly fields as readonly - LoadTest" +} + +func (w workaroundTempReadOnlyFieldsLoadTest) Process(input sdkModels.APIVersion) (*sdkModels.APIVersion, error) { + definition, err := markFieldAsComputed(input, "LoadTests", "LoadTestProperties", "DataPlaneURI") + if err != nil { + return nil, err + } + + return definition, nil +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_temp_readonly_fields_managedidentity.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_temp_readonly_fields_managedidentity.go new file mode 100644 index 00000000000..bbc9d9eaf15 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_temp_readonly_fields_managedidentity.go @@ -0,0 +1,37 @@ +package dataworkarounds + +import ( + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" +) + +var _ workaround = workaroundTempReadOnlyFieldsManagedIdentity{} + +type workaroundTempReadOnlyFieldsManagedIdentity struct { +} + +func (w workaroundTempReadOnlyFieldsManagedIdentity) IsApplicable(serviceName string, apiVersion sdkModels.APIVersion) bool { + return serviceName == "ManagedIdentity" && apiVersion.APIVersion == "2023-01-31" +} + +func (w workaroundTempReadOnlyFieldsManagedIdentity) Name() string { + return "Temp - Mark readonly fields as readonly - ManagedIdentity" +} + +func (w workaroundTempReadOnlyFieldsManagedIdentity) Process(input sdkModels.APIVersion) (*sdkModels.APIVersion, error) { + definition, err := markFieldAsComputed(input, "ManagedIdentities", "UserAssignedIdentityProperties", "ClientId") + if err != nil { + return nil, err + } + + definition, err = markFieldAsComputed(*definition, "ManagedIdentities", "UserAssignedIdentityProperties", "PrincipalId") + if err != nil { + return nil, err + } + + definition, err = markFieldAsComputed(*definition, "ManagedIdentities", "UserAssignedIdentityProperties", "TenantId") + if err != nil { + return nil, err + } + + return definition, nil +} diff --git a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workarounds.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workarounds.go similarity index 52% rename from tools/importer-rest-api-specs/components/parser/dataworkarounds/workarounds.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workarounds.go index eee35cec341..148b45e5771 100644 --- a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workarounds.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workarounds.go @@ -3,13 +3,6 @@ package dataworkarounds -import ( - "fmt" - - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/logging" - importerModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/models" -) - var workarounds = []workaround{ // These workarounds are related to issues with the upstream API Definitions workaroundAlertsManagement{}, @@ -30,33 +23,16 @@ var workarounds = []workaround{ workaroundRecoveryServicesSiteRecovery26680{}, workaroundStreamAnalytics27577{}, workaroundSubscriptions20254{}, + workaroundNetwork29303{}, // These workarounds relate to Terraform specific overrides we want to apply (for example for Resource Generation) workaroundDevCenterIdToRequired{}, - workaroundTempReadOnlyFields{}, + workaroundTempReadOnlyFieldsContainerService{}, + workaroundTempReadOnlyFieldsDevCenter{}, + workaroundTempReadOnlyFieldsLoadTest{}, + workaroundTempReadOnlyFieldsManagedIdentity{}, // These workarounds relate to data inconsistencies workaroundInconsistentlyDefinedSegments{}, workaroundInvalidGoPackageNames{}, } - -func ApplyWorkarounds(input []importerModels.AzureApiDefinition) (*[]importerModels.AzureApiDefinition, error) { - output := make([]importerModels.AzureApiDefinition, 0) - logging.Tracef("Processing Swagger Data Workarounds..") - for _, item := range input { - for _, fix := range workarounds { - if fix.IsApplicable(&item) { - logging.Tracef("Applying Swagger Data Workaround %q to Service %q / API Version %q", fix.Name(), item.ServiceName, item.ApiVersion) - updated, err := fix.Process(item) - if err != nil { - return nil, fmt.Errorf("applying Swagger Data Workaround %q to Service %q / API Version %q: %+v", fix.Name(), item.ServiceName, item.ApiVersion, err) - } - - item = *updated - } - } - output = append(output, item) - } - - return &output, nil -} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/ignore/README.md b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/ignore/README.md new file mode 100644 index 00000000000..73f77617e42 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/ignore/README.md @@ -0,0 +1,5 @@ +TODO + +This package contains a set of data which should be ignored - whilst this used to be scattered around, it was hard to find. + +So whilst it's debatable whether this should live together, for the ease of discovery I believe this belongs together. \ No newline at end of file diff --git a/tools/importer-rest-api-specs/components/parser/internal/helpers.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/ignore/operation.go similarity index 97% rename from tools/importer-rest-api-specs/components/parser/internal/helpers.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/ignore/operation.go index 169566f47f9..48418105228 100644 --- a/tools/importer-rest-api-specs/components/parser/internal/helpers.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/ignore/operation.go @@ -1,11 +1,11 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package internal +package ignore import "strings" -func OperationShouldBeIgnored(operationUri string) bool { +func Operation(operationUri string) bool { loweredOperationUri := strings.ToLower(operationUri) // we're not concerned with exposing the "operations" API's at this time - e.g. diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/ignore/services.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/ignore/services.go new file mode 100644 index 00000000000..234a5fcd298 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/ignore/services.go @@ -0,0 +1,20 @@ +package ignore + +import "strings" + +func Services(name string) bool { + servicesToIgnore := []string{ + "ADHybridHealthService", // EOL? Contains a Constant of an empty string: https://github.com/Azure/azure-rest-api-specs/blob/3eaa729b3686f20817145e771a8ab707c739dbbd/specification/adhybridhealthservice/resource-manager/Microsoft.ADHybridHealthService/stable/2014-01-01/ADHybridHealthService.json#L460-L471 + "Blockchain", // EOL - https://github.com/Azure-Samples/blockchain/blob/1b712d6d05cca8da17bdd1894de8c3d25905685d/abs/migration-guide.md + "DevSpaces", // EOL - https://azure.microsoft.com/en-us/updates/azure-dev-spaces-is-retiring-on-31-october-2023/ + "DynamicsTelemetry", // Fake RP - https://github.com/Azure/azure-rest-api-specs/pull/5161#issuecomment-486705231 + "IoTSpaces", // EOL - https://github.com/Azure/azure-rest-api-specs/pull/13993 + "ServiceFabricMesh", // EOL - https://azure.microsoft.com/en-us/updates/azure-service-fabric-mesh-preview-retirement/ + } + for _, v := range servicesToIgnore { + if strings.EqualFold(name, v) { + return true + } + } + return false +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/ignore/swagger_tags.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/ignore/swagger_tags.go new file mode 100644 index 00000000000..0b1568dbc17 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/ignore/swagger_tags.go @@ -0,0 +1,30 @@ +package ignore + +import "strings" + +var swaggerTagsToIgnore = map[string]struct{}{ + "azurefirewallfqdntags": {}, + "usage": {}, +} + +func SwaggerTag(tag string) bool { + lowered := strings.ToLower(tag) + for key := range swaggerTagsToIgnore { + // exact matches e.g. Usage + if strings.EqualFold(tag, key) { + return true + } + + // suffixes e.g. `ComputeUsage` + if strings.HasSuffix(lowered, strings.ToLower(key)) { + return true + } + } + + // there's a handful of these (e.g. `FluxConfigurationOperationStatus`) + if strings.Contains(lowered, "operationstatus") { + return true + } + + return false +} diff --git a/tools/importer-rest-api-specs/components/parser/internal/structs.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models/parse_result.go similarity index 75% rename from tools/importer-rest-api-specs/components/parser/internal/structs.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models/parse_result.go index ef8aa687ac9..31f524caa3f 100644 --- a/tools/importer-rest-api-specs/components/parser/internal/structs.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models/parse_result.go @@ -1,11 +1,7 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package internal +package models import ( "fmt" - "reflect" "strings" "github.com/hashicorp/go-azure-helpers/lang/pointer" @@ -47,9 +43,15 @@ func (r *ParseResult) AppendConstants(other map[string]sdkModels.SDKConstant) er return fmt.Errorf("conflicting constant %q with different types - first type %q - second type %q", k, string(existing.Type), string(v.Type)) } - if !reflect.DeepEqual(existing.Values, v.Values) { - return fmt.Errorf("conflicting constant %q with different values. First: %+v. Second: %+v", k, existing.Values, v.Values) + // where the constant types are the same, we will combine their values instead of failing here. + // originally added for: SecurityInsights@2022-10-01-preview / EntityQueryKind + for valueName, valueValue := range v.Values { + r.Constants[k].Values[valueName] = valueValue } + + //if !reflect.DeepEqual(existing.Values, v.Values) { + // return fmt.Errorf("conflicting constant %q with different values. First: %+v. Second: %+v", k, existing.Values, v.Values) + //} } return nil @@ -81,6 +83,12 @@ func (r *ParseResult) AppendModels(other map[string]sdkModels.SDKModel) error { return nil } +func (r *ParseResult) IsObjectKnown(name string) (bool, bool) { + _, isConstant := r.Constants[name] + _, isModel := r.Models[name] + return isConstant, isModel +} + func compareFields(first map[string]sdkModels.SDKField, second map[string]sdkModels.SDKField) error { if len(first) != len(second) { return fmt.Errorf("first had %d fields but second had %d fields", len(first), len(second)) @@ -88,7 +96,8 @@ func compareFields(first map[string]sdkModels.SDKField, second map[string]sdkMod for k := range first { firstVal := first[k] - secondVal, ok := second[k] + // Keys aren't normalised until processing is completed, so we need to insensitively look for this + secondVal, ok := itemForKeyInsensitively(second, k) if !ok { return fmt.Errorf("second didn't contain the key %q", k) } @@ -114,6 +123,22 @@ func compareFields(first map[string]sdkModels.SDKField, second map[string]sdkMod return nil } +func itemForKeyInsensitively[T any](input map[string]T, key string) (*T, bool) { + // if we've got it case-sensitively then save the effort of iterating over all the keys + val, ok := input[key] + if ok { + return &val, true + } + + for k, v := range input { + if strings.EqualFold(k, key) { + return &v, true + } + } + + return nil, false +} + func objectDefinitionsMatch(first, second sdkModels.SDKObjectDefinition) error { if first.NestedItem != nil && second.NestedItem == nil { return fmt.Errorf("`first` had a nested item but `second` didn't. First: %+v. Second: %+v", first, second) @@ -129,8 +154,8 @@ func objectDefinitionsMatch(first, second sdkModels.SDKObjectDefinition) error { if first.Type != second.Type { return fmt.Errorf("first.Type was %q but second.Type was %q", string(first.Type), string(second.Type)) } - if err := compareNilableString(first.ReferenceName, second.ReferenceName); err != nil { - return fmt.Errorf("value for ReferenceName differs: %+v\n\nFirst was %q but second was %q", err, pointer.From(first.ReferenceName), pointer.From(second.ReferenceName)) + if !strings.EqualFold(pointer.From(first.ReferenceName), pointer.From(second.ReferenceName)) { + return fmt.Errorf("value for ReferenceName differs.\n\nFirst was %q but second was %q", pointer.From(first.ReferenceName), pointer.From(second.ReferenceName)) } // TODO: re-enable minimum/maximum/unique on Object Definition @@ -147,27 +172,11 @@ func objectDefinitionsMatch(first, second sdkModels.SDKObjectDefinition) error { return nil } -func compareNilableString(first *string, second *string) error { - if first != nil { - if second == nil { - return fmt.Errorf("first value was %q but second value was nil", *first) - } - - // @tombuildsstuff: the Azure API Definitions are wholy inconsistent here, so we'll case-insensitively - // compare the references as they're normalized at the final stage - // * first value was "daprMetadata" but second value was "DaprMetadata" - // * first value was "status" but second value was "Status" - // * first value was "status" but second value was "Status" - // * first value was "DataProviderMetadata" but second value was "dataProviderMetadata" - if !strings.EqualFold(*first, *second) { - return fmt.Errorf("first value was %q but second value was %q", *first, *second) - } - - return nil - } - - if second != nil { - return fmt.Errorf("first value was nil but second value was %q", *second) +func compareNilableString(first, second *string) error { + firstVal := pointer.From(first) + secondValue := pointer.From(second) + if firstVal != secondValue { + return fmt.Errorf("the first value %q didn't match the second value %q", firstVal, secondValue) } return nil diff --git a/tools/importer-rest-api-specs/components/parser/models_datafactorycustom_test.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models_datafactorycustom_test.go similarity index 94% rename from tools/importer-rest-api-specs/components/parser/models_datafactorycustom_test.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models_datafactorycustom_test.go index 8faf33654bb..b342552846b 100644 --- a/tools/importer-rest-api-specs/components/parser/models_datafactorycustom_test.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models_datafactorycustom_test.go @@ -1,20 +1,20 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package parser +package parser_test import ( + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testhelpers" "testing" "github.com/hashicorp/go-azure-helpers/lang/pointer" sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/featureflags" - importerModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/models" ) func TestParseModelWithDataFactoryCustomTypes(t *testing.T) { // This test ensures that we parse the Data Factory Custom Types out to regular Object Definitions. - actual, err := ParseSwaggerFileForTesting(t, "model_using_datafactory_custom_types.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_using_datafactory_custom_types.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -172,9 +172,8 @@ func TestParseModelWithDataFactoryCustomTypes(t *testing.T) { } } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Example": { Models: expectedModels, @@ -198,5 +197,5 @@ func TestParseModelWithDataFactoryCustomTypes(t *testing.T) { }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } diff --git a/tools/importer-rest-api-specs/components/parser/models_dictionaries_test.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models_dictionaries_test.go similarity index 86% rename from tools/importer-rest-api-specs/components/parser/models_dictionaries_test.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models_dictionaries_test.go index f843a431379..e545ff9e403 100644 --- a/tools/importer-rest-api-specs/components/parser/models_dictionaries_test.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models_dictionaries_test.go @@ -1,25 +1,24 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package parser +package parser_test import ( "testing" "github.com/hashicorp/go-azure-helpers/lang/pointer" sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" - importerModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/models" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testhelpers" ) func TestParseModelWithADictionaryOfIntegers(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_dictionary_of_integers.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_dictionary_of_integers.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Hello": { Models: map[string]sdkModels.SDKModel{ @@ -60,18 +59,17 @@ func TestParseModelWithADictionaryOfIntegers(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseModelWithADictionaryOfIntegersInlined(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_dictionary_of_integers_inlined.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_dictionary_of_integers_inlined.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Hello": { Models: map[string]sdkModels.SDKModel{ @@ -112,18 +110,17 @@ func TestParseModelWithADictionaryOfIntegersInlined(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseModelWithADictionaryOfAnObject(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_dictionary_of_object.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_dictionary_of_object.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Hello": { Models: map[string]sdkModels.SDKModel{ @@ -197,18 +194,17 @@ func TestParseModelWithADictionaryOfAnObject(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseModelWithADictionaryOfAnObjectInlined(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_dictionary_of_object_inlined.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_dictionary_of_object_inlined.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Hello": { Models: map[string]sdkModels.SDKModel{ @@ -282,18 +278,17 @@ func TestParseModelWithADictionaryOfAnObjectInlined(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseModelWithADictionaryOfString(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_dictionary_of_string.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_dictionary_of_string.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Hello": { Models: map[string]sdkModels.SDKModel{ @@ -334,18 +329,17 @@ func TestParseModelWithADictionaryOfString(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseModelWithADictionaryOfStringInlined(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_dictionary_of_string_inlined.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_dictionary_of_string_inlined.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Hello": { Models: map[string]sdkModels.SDKModel{ @@ -386,5 +380,5 @@ func TestParseModelWithADictionaryOfStringInlined(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } diff --git a/tools/importer-rest-api-specs/components/parser/models_discriminators_test.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models_discriminators_test.go similarity index 82% rename from tools/importer-rest-api-specs/components/parser/models_discriminators_test.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models_discriminators_test.go index 919bd79c6c3..3e31d83805c 100644 --- a/tools/importer-rest-api-specs/components/parser/models_discriminators_test.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models_discriminators_test.go @@ -1,25 +1,24 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package parser +package parser_test import ( "testing" "github.com/hashicorp/go-azure-helpers/lang/pointer" sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" - importerModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/models" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testhelpers" ) func TestParseDiscriminatorsTopLevel(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_discriminators_simple.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_discriminators_simple.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Discriminator": { Models: map[string]sdkModels.SDKModel{ @@ -105,18 +104,17 @@ func TestParseDiscriminatorsTopLevel(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseDiscriminatorsWithinArray(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_discriminators_within_array.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_discriminators_within_array.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Discriminator": { Models: map[string]sdkModels.SDKModel{ @@ -212,18 +210,17 @@ func TestParseDiscriminatorsWithinArray(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseDiscriminatorsWithinDiscriminators(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_discriminators_within_discriminators.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_discriminators_within_discriminators.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Discriminator": { Models: map[string]sdkModels.SDKModel{ @@ -394,21 +391,20 @@ func TestParseDiscriminatorsWithinDiscriminators(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseDiscriminatedParentTypeThatShouldntBe(t *testing.T) { // Some Swagger files define top level types with a Discriminator value which don't inherit // from anything. As such these aren't actually discriminated types but bad data - so we should // look to ensure these are parsed out as a regular non-discriminated type. - actual, err := ParseSwaggerFileForTesting(t, "model_discriminators_parent_that_shouldnt_be.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_discriminators_parent_that_shouldnt_be.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Discriminator": { Models: map[string]sdkModels.SDKModel{ @@ -439,21 +435,20 @@ func TestParseDiscriminatedParentTypeThatShouldntBe(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseDiscriminatedChildTypeThatShouldntBe(t *testing.T) { // Some Swagger files define top level types with a Discriminator value which don't inherit // from anything. As such these aren't actually discriminated types but bad data - so we should // look to ensure these are parsed out as a regular non-discriminated type. - actual, err := ParseSwaggerFileForTesting(t, "model_discriminators_child_that_shouldnt_be.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_discriminators_child_that_shouldnt_be.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Discriminator": { Models: map[string]sdkModels.SDKModel{ @@ -484,21 +479,20 @@ func TestParseDiscriminatedChildTypeThatShouldntBe(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseDiscriminatedChildTypeWhereParentShouldNotBeUsed(t *testing.T) { // Some Swagger files contain Models with a reference to a Discriminated Type (e.g. an implementation // where a Parent should be used instead) - this asserts that we shouldn't switch these out to // referencing the Parent, instead should just use the Implementation itself. - actual, err := ParseSwaggerFileForTesting(t, "model_discriminators_child_used_as_parent.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_discriminators_child_used_as_parent.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Discriminator": { Models: map[string]sdkModels.SDKModel{ @@ -584,18 +578,17 @@ func TestParseDiscriminatedChildTypeWhereParentShouldNotBeUsed(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseDiscriminatorsInheritingFromOtherDiscriminators(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_discriminators_inherited_from_discriminators.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_discriminators_inherited_from_discriminators.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Discriminator": { Models: map[string]sdkModels.SDKModel{ @@ -688,18 +681,17 @@ func TestParseDiscriminatorsInheritingFromOtherDiscriminators(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseDiscriminatorsDeepInheritance(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_discriminators_deep_inheritance.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_discriminators_deep_inheritance.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Discriminator": { Models: map[string]sdkModels.SDKModel{ @@ -869,20 +861,19 @@ func TestParseDiscriminatorsDeepInheritance(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseDiscriminatorsWithMultipleParents(t *testing.T) { // In this scenario the discriminated type Human inherits from NamedEntity (containing just properties) // which inherits from the discriminated parent type BiologicalEntity - actual, err := ParseSwaggerFileForTesting(t, "model_discriminators_multiple_parents.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_discriminators_multiple_parents.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Discriminator": { Models: map[string]sdkModels.SDKModel{ @@ -1001,9 +992,40 @@ func TestParseDiscriminatorsWithMultipleParents(t *testing.T) { FieldNameContainingDiscriminatedValue: pointer.To("TypeName"), DiscriminatedValue: pointer.To("human"), }, - // NOTE: Whilst NamedEntity is present in the Swagger it shouldn't be in the result since - // it's just an abstract type (defining the shared fields for Car and Human), rather than - // being directly used. + "NamedEntity": { + Fields: map[string]sdkModels.SDKField{ + "FirstName": { + JsonName: "firstName", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: true, + }, + "LastName": { + JsonName: "lastName", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + }, + "SomeField": { + JsonName: "someField", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + "TypeName": { + JsonName: "typeName", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: true, + }, + }, + ParentTypeName: pointer.To("BiologicalEntity"), + FieldNameContainingDiscriminatedValue: pointer.To("TypeName"), + DiscriminatedValue: pointer.To("NamedEntity"), + }, }, Operations: map[string]sdkModels.SDKOperation{ "Test": { @@ -1020,20 +1042,19 @@ func TestParseDiscriminatorsWithMultipleParents(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseDiscriminatorsWithMultipleParentsWithinArray(t *testing.T) { // In this scenario the discriminated type Human inherits from NamedEntity (containing just properties) // which inherits from the discriminated parent type BiologicalEntity - actual, err := ParseSwaggerFileForTesting(t, "model_discriminators_multiple_parents_within_array.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_discriminators_multiple_parents_within_array.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Discriminator": { Models: map[string]sdkModels.SDKModel{ @@ -1155,9 +1176,40 @@ func TestParseDiscriminatorsWithMultipleParentsWithinArray(t *testing.T) { FieldNameContainingDiscriminatedValue: pointer.To("TypeName"), DiscriminatedValue: pointer.To("human"), }, - // NOTE: Whilst NamedEntity is present in the Swagger it shouldn't be in the result since - // it's just an abstract type (defining the shared fields for Car and Human), rather than - // being directly used. + "NamedEntity": { + Fields: map[string]sdkModels.SDKField{ + "FirstName": { + JsonName: "firstName", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: true, + }, + "LastName": { + JsonName: "lastName", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + }, + "SomeField": { + JsonName: "someField", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + "TypeName": { + JsonName: "typeName", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: true, + }, + }, + ParentTypeName: pointer.To("BiologicalEntity"), + FieldNameContainingDiscriminatedValue: pointer.To("TypeName"), + DiscriminatedValue: pointer.To("NamedEntity"), + }, }, Operations: map[string]sdkModels.SDKOperation{ "Test": { @@ -1174,18 +1226,17 @@ func TestParseDiscriminatorsWithMultipleParentsWithinArray(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseDiscriminatorsOrphanedChild(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "/nestedtestdata/model_discriminators_orphaned_child.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "/nestedtestdata/model_discriminators_orphaned_child.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ // NOTE: since there's no Operations defined the Models are placed into an APIResource based on the // File Name. Whilst in a normal scenario this would make sense - for testing purposes it leads to @@ -1251,18 +1302,17 @@ func TestParseDiscriminatorsOrphanedChild(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseDiscriminatorsOrphanedChildWithNestedModel(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "/nestedtestdata/model_discriminators_orphaned_child_with_nested_model.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "/nestedtestdata/model_discriminators_orphaned_child_with_nested_model.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ // NOTE: since there's no Operations defined the Models are placed into an APIResource based on the // File Name. Whilst in a normal scenario this would make sense - for testing purposes it leads to @@ -1357,18 +1407,18 @@ func TestParseDiscriminatorsOrphanedChildWithNestedModel(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseDiscriminatorsOrphanedChildWithoutDiscriminatorValue(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "/nestedtestdata/model_discriminators_orphaned_child_without_discriminator_value.json", pointer.To("datafactory")) + // see https://github.com/Azure/azure-rest-api-specs/issues/28380 + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "/nestedtestdata/model_discriminators_orphaned_child_without_discriminator_value.json", pointer.To("DataFactory")) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "DataFactory", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ // NOTE: since there's no Operations defined the Models are placed into an APIResource based on the // File Name. Whilst in a normal scenario this would make sense - for testing purposes it leads to @@ -1420,20 +1470,94 @@ func TestParseDiscriminatorsOrphanedChildWithoutDiscriminatorValue(t *testing.T) }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseDiscriminatorsReferenceInAnotherSpecFile(t *testing.T) { + // This tests whether Swagger Refs from another spec file are parsed correctly. The go-openapi module actually does + // the heaving lifting here (thankfully!), loading in another file transparently when an external Ref is encountered. + // An external Ref looks something like this: + // + // "$ref": "./model_discriminators_ref_in_another_spec.json#/definitions/Animal" + // + // Where the anchor is preceded by a path to a neighboring spec. We don't need to code for this explicitly, nor + // do we _really_ need to test for this, but since DataFactory relies heavily on this, this helps ensure we don't + // break this in the future. + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_discriminators_ref_from_another_spec.json", pointer.To("DataFactory")) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Example": { + Constants: make(map[string]sdkModels.SDKConstant), + Models: map[string]sdkModels.SDKModel{ + "Animal": { + Fields: map[string]sdkModels.SDKField{ + "AnimalType": { + JsonName: "animalType", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: true, + }, + }, + FieldNameContainingDiscriminatedValue: pointer.To("AnimalType"), + }, + "Cat": { + Fields: map[string]sdkModels.SDKField{ + "AnimalType": { + JsonName: "animalType", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: true, + }, + "IsFluffy": { + JsonName: "isFluffy", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.BooleanSDKObjectDefinitionType, + }, + Required: true, + }, + }, + ParentTypeName: pointer.To("Animal"), + FieldNameContainingDiscriminatedValue: pointer.To("AnimalType"), + DiscriminatedValue: pointer.To("Cat"), + }, + }, + Name: "Example", + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + URISuffix: pointer.To("/example"), + ResponseObject: &sdkModels.SDKObjectDefinition{ + Type: sdkModels.ReferenceSDKObjectDefinitionType, + ReferenceName: pointer.To("Animal"), + }, + }, + }, + ResourceIDs: map[string]sdkModels.ResourceID{}, + }, + }, + } + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseDiscriminatorsOrphanedChildWithoutDiscriminatorValueForDifferentService(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "/nestedtestdata/model_discriminators_orphaned_child_without_discriminator_value.json", pointer.To("Compute")) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "/nestedtestdata/model_discriminators_orphaned_child_without_discriminator_value.json", pointer.To("Compute")) if err != nil { t.Fatalf("parsing: %+v", err) } // This test ensures that this behaviour is scoped to Data Factory and won't impact other services - expected := importerModels.AzureApiDefinition{ - ServiceName: "Compute", - ApiVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{}, + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{}, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } diff --git a/tools/importer-rest-api-specs/components/parser/models_test.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models_test.go similarity index 89% rename from tools/importer-rest-api-specs/components/parser/models_test.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models_test.go index 7298f9fedad..62934a65772 100644 --- a/tools/importer-rest-api-specs/components/parser/models_test.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models_test.go @@ -1,27 +1,26 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package parser +package parser_test import ( "testing" "github.com/hashicorp/go-azure-helpers/lang/pointer" sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" - importerModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/models" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testhelpers" ) // TODO: tests for the different types of Object Definition func TestParseModelTopLevel(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_top_level.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_top_level.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Example": { Models: map[string]sdkModels.SDKModel{ @@ -91,18 +90,17 @@ func TestParseModelTopLevel(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseModelTopLevelWithRawFile(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_top_level_with_rawfile.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_top_level_with_rawfile.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Example": { Operations: map[string]sdkModels.SDKOperation{ @@ -122,18 +120,17 @@ func TestParseModelTopLevelWithRawFile(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseModelTopLevelWithInlinedModel(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_top_level_with_inlined_model.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_top_level_with_inlined_model.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Example": { Models: map[string]sdkModels.SDKModel{ @@ -222,18 +219,17 @@ func TestParseModelTopLevelWithInlinedModel(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseModelWithDateTimeNoType(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_with_datetime_no_type.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_with_datetime_no_type.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Example": { Models: map[string]sdkModels.SDKModel{ @@ -268,18 +264,17 @@ func TestParseModelWithDateTimeNoType(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseModelWithInlinedObject(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_with_inlined_object.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_with_inlined_object.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Example": { Models: map[string]sdkModels.SDKModel{ @@ -363,18 +358,17 @@ func TestParseModelWithInlinedObject(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseModelWithNumberPrefixedField(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_with_number_prefixed_field.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_with_number_prefixed_field.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Example": { Models: map[string]sdkModels.SDKModel{ @@ -412,18 +406,17 @@ func TestParseModelWithNumberPrefixedField(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseModelWithReference(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_with_reference.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_with_reference.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Example": { Models: map[string]sdkModels.SDKModel{ @@ -495,18 +488,17 @@ func TestParseModelWithReference(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseModelWithReferenceToArray(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_with_reference_array.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_with_reference_array.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Example": { Models: map[string]sdkModels.SDKModel{ @@ -564,18 +556,17 @@ func TestParseModelWithReferenceToArray(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseModelWithReferenceToConstant(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_with_reference_constant.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_with_reference_constant.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Example": { Constants: map[string]sdkModels.SDKConstant{ @@ -645,18 +636,17 @@ func TestParseModelWithReferenceToConstant(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseModelWithReferenceToString(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_with_reference_string.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_with_reference_string.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Example": { Models: map[string]sdkModels.SDKModel{ @@ -716,18 +706,17 @@ func TestParseModelWithReferenceToString(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseModelWithCircularReferences(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_with_circular_reference.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_with_circular_reference.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Example": { Models: map[string]sdkModels.SDKModel{ @@ -818,18 +807,17 @@ func TestParseModelWithCircularReferences(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseModelInheritingFromObjectWithNoExtraFields(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_inheriting_from_other_model_no_new_fields.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_inheriting_from_other_model_no_new_fields.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Example": { Models: map[string]sdkModels.SDKModel{ @@ -862,18 +850,17 @@ func TestParseModelInheritingFromObjectWithNoExtraFields(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseModelInheritingFromObjectWithNoExtraFieldsInlined(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_inheriting_from_other_model_no_new_fields_inlined.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_inheriting_from_other_model_no_new_fields_inlined.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Example": { Models: map[string]sdkModels.SDKModel{ @@ -923,18 +910,17 @@ func TestParseModelInheritingFromObjectWithNoExtraFieldsInlined(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseModelInheritingFromObjectWithOnlyDescription(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_inheriting_from_other_model_with_only_description.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_inheriting_from_other_model_with_only_description.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Example": { Models: map[string]sdkModels.SDKModel{ @@ -967,7 +953,7 @@ func TestParseModelInheritingFromObjectWithOnlyDescription(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseModelInheritingFromObjectWithPropertiesWithinAllOf(t *testing.T) { @@ -976,14 +962,13 @@ func TestParseModelInheritingFromObjectWithPropertiesWithinAllOf(t *testing.T) { // This covers a regression from https://github.com/hashicorp/pandora/pull/3720 // which surfaced in https://github.com/hashicorp/pandora/pull/3726 for the model `AgentPool` // within `ContainerService@2019-08-01/AgentPools` which was renamed `SubResource`. - actual, err := ParseSwaggerFileForTesting(t, "model_inheriting_from_other_model_with_properties_within_allof.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_inheriting_from_other_model_with_properties_within_allof.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Example": { Models: map[string]sdkModels.SDKModel{ @@ -1017,18 +1002,17 @@ func TestParseModelInheritingFromObjectWithPropertiesWithinAllOf(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseModelContainingAllOfToTypeObject(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_containing_allof_object_type.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_containing_allof_object_type.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Example": { Models: map[string]sdkModels.SDKModel{ @@ -1070,18 +1054,17 @@ func TestParseModelContainingAllOfToTypeObject(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseModelContainingAllOfToTypeObjectWithProperties(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_containing_allof_object_type_with_properties.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_containing_allof_object_type_with_properties.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Example": { Models: map[string]sdkModels.SDKModel{ @@ -1123,18 +1106,17 @@ func TestParseModelContainingAllOfToTypeObjectWithProperties(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseModelContainingAllOfWithinProperties(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_containing_allof_within_properties.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_containing_allof_within_properties.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Example": { Models: map[string]sdkModels.SDKModel{ @@ -1202,18 +1184,17 @@ func TestParseModelContainingAllOfWithinProperties(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseModelContainingMultipleAllOfWithinProperties(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_containing_allof_within_properties_multiple.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_containing_allof_within_properties_multiple.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Example": { Models: map[string]sdkModels.SDKModel{ @@ -1293,18 +1274,17 @@ func TestParseModelContainingMultipleAllOfWithinProperties(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseModelContainingLists(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_containing_lists.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_containing_lists.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Example": { Models: map[string]sdkModels.SDKModel{ @@ -1379,18 +1359,17 @@ func TestParseModelContainingLists(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseModelInlinedWithNoName(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_inlined_with_no_name.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_inlined_with_no_name.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Example": { Models: map[string]sdkModels.SDKModel{ @@ -1436,18 +1415,17 @@ func TestParseModelInlinedWithNoName(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseModelInheritingFromParent(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_inheriting_from_parent.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_inheriting_from_parent.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Example": { Models: map[string]sdkModels.SDKModel{ @@ -1510,18 +1488,17 @@ func TestParseModelInheritingFromParent(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseModelMultipleTopLevelModelsAndOperations(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_multiple_top_level_models_and_operations.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_multiple_top_level_models_and_operations.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Example": { Models: map[string]sdkModels.SDKModel{ @@ -1619,18 +1596,17 @@ func TestParseModelMultipleTopLevelModelsAndOperations(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseModelBug2675DuplicateModel(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "models_bug_2675_duplicate_model.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "models_bug_2675_duplicate_model.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Example": { Models: map[string]sdkModels.SDKModel{ @@ -1842,5 +1818,5 @@ func TestParseModelBug2675DuplicateModel(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/operation/helpers.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/operation/helpers.go new file mode 100644 index 00000000000..a7d475599a3 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/operation/helpers.go @@ -0,0 +1,190 @@ +package operation + +import ( + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" + "net/http" + "sort" + "strings" + + "github.com/go-openapi/spec" +) + +func determineContentType(operation parsedOperation) string { + contentType := "application/json" + + if strings.EqualFold(operation.httpMethod, "HEAD") || strings.EqualFold(operation.httpMethod, "GET") { + if len(operation.operation.Produces) > 0 { + contentType = operation.operation.Produces[0] + } + } else { + if len(operation.operation.Consumes) > 0 { + contentType = operation.operation.Consumes[0] + } + } + + return contentType +} + +func expectedStatusCodesForOperation(input parsedOperation) []int { + statusCodes := make([]int, 0) + + for statusCode, resp := range input.operation.Responses.StatusCodeResponses { + // sanity checking + if isASuccessResponse(statusCode, resp) { + statusCodes = append(statusCodes, statusCode) + } + } + + if !usesNonDefaultStatusCodes(input, statusCodes) { + if isLongRunning(input) { + if strings.EqualFold(input.httpMethod, "delete") { + statusCodes = []int{200, 202} + } + if strings.EqualFold(input.httpMethod, "post") { + statusCodes = []int{201, 202} + } + if strings.EqualFold(input.httpMethod, "put") { + statusCodes = []int{201, 202} + } + } + if isListOperation(input) { + if strings.EqualFold(input.httpMethod, "get") { + statusCodes = []int{200} + } + } + if strings.EqualFold(input.httpMethod, "delete") || strings.EqualFold(input.httpMethod, "get") || strings.EqualFold(input.httpMethod, "post") || strings.EqualFold(input.httpMethod, "head") { + statusCodes = []int{200} + } + if strings.EqualFold(input.httpMethod, "put") || strings.EqualFold(input.httpMethod, "patch") { + statusCodes = []int{200, 201} + } + } + sort.Ints(statusCodes) + + return statusCodes +} + +func fieldContainingPaginationDetailsForOperation(input parsedOperation) *string { + if raw, ok := input.operation.VendorExtensible.Extensions["x-ms-pageable"]; ok { + val, ok := raw.(map[string]interface{}) + if ok { + for k, v := range val { + // this can be 'null' in the swagger + if v == nil { + continue + } + if strings.EqualFold("nextLinkName", k) { + str := v.(string) + return &str + } + } + } + } + + return nil +} + +func isASuccessResponse(statusCode int, resp spec.Response) bool { + // Status Codes with the extension `x-ms-error-response` reference an error response + // which should be ignored in our case - as errors will instead be pulled out via the + // base layer + isErrorValue, exists := resp.Extensions.GetBool("x-ms-error-response") + if exists && isErrorValue { + return false + } + + return statusCode >= 200 && statusCode < 300 +} + +func isListOperation(input parsedOperation) bool { + paginationField := fieldContainingPaginationDetailsForOperation(input) + if paginationField != nil { + return true + } + + // otherwise if we have a parameter of `$skiptoken` in the query, we assume that it is + for _, parameter := range input.operation.Parameters { + if strings.EqualFold(parameter.Name, "$skipToken") { + return true + } + } + + return false +} + +func isLongRunning(input parsedOperation) bool { + // Some Swaggers have the following defined on an Operation: + // > "x-ms-long-running-operation": true, + // > "x-ms-long-running-operation-options": { + // > "final-state-via": "azure-async-operation" + // > } + // Whilst these _could_ be useful at some point we can likely ignore them for + // the moment since the convention is either `Location` or `Azure-AsyncOperation` + val, exists := input.operation.Extensions.GetBool("x-ms-long-running-operation") + if !exists { + return false + } + + return val +} + +func matchesTag(operation *spec.Operation, tag *string) bool { + // if there's no tags defined, we should capture it when the tag matched + if tag == nil { + return len(operation.Tags) == 0 + } + + for _, thisTag := range operation.Tags { + if strings.EqualFold(thisTag, *tag) { + return true + } + } + + return false +} + +func shouldBeIgnored(input sdkModels.SDKOperation) bool { + // Some HTTP Operations don't make sense for us to expose at this time, for example + // a GET request which returns no content. They may at some point in the future but + // for now there's not much point + // + // Example: the 'GetSubscriptionOperationWithAsyncResponse' in Web, which returns the + // result of a LRO - but in our case that's handled elsewhere so we don't need it + if strings.EqualFold(input.Method, "GET") { + if len(input.ExpectedStatusCodes) == 1 && input.ExpectedStatusCodes[0] == http.StatusNoContent && input.ResponseObject == nil { + return true + } + } + + return false +} + +func usesNonDefaultStatusCodes(input parsedOperation, statusCodes []int) bool { + defaultStatusCodes := map[string][]int{ + "get": {200}, + "post": {200, 201}, + "put": {200, 201}, + "delete": {200, 201}, + "patch": {200, 201}, + } + expected, ok := defaultStatusCodes[strings.ToLower(input.httpMethod)] + if !ok { + // potentially an unsupported use-case but fine for now + return true + } + + if len(expected) != len(statusCodes) { + return true + } + + sort.Ints(expected) + sort.Ints(statusCodes) + for i, ev := range expected { + av := statusCodes[i] + if ev != av { + return true + } + } + + return false +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/operation/identification.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/operation/identification.go new file mode 100644 index 00000000000..75aa40f2829 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/operation/identification.go @@ -0,0 +1,35 @@ +package operation + +import ( + "github.com/go-openapi/spec" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parsingcontext" +) + +type parsedOperation struct { + name string + uri string + httpMethod string + operation *spec.Operation +} + +func findOperationsMatchingTag(parsingContext *parsingcontext.Context, tag *string) *[]parsedOperation { + result := make([]parsedOperation, 0) + for httpMethod, operation := range parsingContext.SwaggerSpecExpanded.Operations() { + for uri, operationDetails := range operation { + if !matchesTag(operationDetails, tag) { + continue + } + + operationName := cleanup.NormalizeOperationName(operationDetails.ID, tag) + result = append(result, parsedOperation{ + name: operationName, + uri: uri, + httpMethod: httpMethod, + operation: operationDetails, + }) + } + } + + return &result +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/operation/list.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/operation/list.go new file mode 100644 index 00000000000..6d8d15f38a8 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/operation/list.go @@ -0,0 +1,80 @@ +package operation + +import ( + "net/http" + "strings" + + "github.com/hashicorp/go-azure-helpers/lang/pointer" + sdkHelpers "github.com/hashicorp/pandora/tools/data-api-sdk/v1/helpers" + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" + parserModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models" +) + +type listOperationDetails struct { + fieldContainingPaginationDetails *string + valueObjectDefinition *sdkModels.SDKObjectDefinition +} + +func listOperationDetailsForOperation(input sdkModels.SDKOperation, known parserModels.ParseResult) *listOperationDetails { + if !strings.EqualFold(input.Method, http.MethodGet) && !strings.EqualFold(input.Method, http.MethodPost) { + return nil + } + + // an operation without a response object isn't going to be listable + if input.ResponseObject == nil { + return nil + } + if input.ResponseObject.Type == sdkModels.ReferenceSDKObjectDefinitionType { + responseModel, isModel := known.Models[*input.ResponseObject.ReferenceName] + if !isModel { + // a constant wouldn't be listable + return nil + } + + out := listOperationDetails{} + if input.FieldContainingPaginationDetails != nil { + out.fieldContainingPaginationDetails = input.FieldContainingPaginationDetails + } + for fieldName, v := range responseModel.Fields { + if strings.EqualFold(fieldName, "nextLink") { + out.fieldContainingPaginationDetails = pointer.To(fieldName) + continue + } + + if strings.EqualFold(fieldName, "Value") { + // switch out the reference to be the SDKObjectDefinition for the `Value` field, rather than + // the wrapper type + definition := sdkHelpers.InnerMostSDKObjectDefinition(v.ObjectDefinition) + out.valueObjectDefinition = pointer.To(definition) + continue + } + } + if out.fieldContainingPaginationDetails != nil && out.valueObjectDefinition != nil { + return &out + } + } + + return nil +} + +func RemoveWrapperModelForListOperations(input map[string]sdkModels.SDKOperation, known parserModels.ParseResult) (map[string]sdkModels.SDKOperation, error) { + // List Operations return an object which contains a NextLink and a Value (which is the actual Object + // being paginated on) - so we want to replace the wrapper object with the Value so that these can be + // paginated correctly as needed. + output := make(map[string]sdkModels.SDKOperation) + + for operationName := range input { + operation := input[operationName] + + // if the Response Object is a List Operation (identifiable via + listDetails := listOperationDetailsForOperation(operation, known) + if listDetails != nil { + operation.FieldContainingPaginationDetails = listDetails.fieldContainingPaginationDetails + operation.ResponseObject = listDetails.valueObjectDefinition + } + + output[operationName] = operation + } + + return output, nil +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/operation/options.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/operation/options.go new file mode 100644 index 00000000000..2ab4e52b2d8 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/operation/options.go @@ -0,0 +1,163 @@ +package operation + +import ( + "fmt" + "strings" + + "github.com/go-openapi/spec" + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/constants" + parserModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models" +) + +func optionsForOperation(input parsedOperation) (map[string]sdkModels.SDKOperationOption, *parserModels.ParseResult, error) { + output := make(map[string]sdkModels.SDKOperationOption) + result := parserModels.ParseResult{ + Constants: map[string]sdkModels.SDKConstant{}, + } + + for _, param := range input.operation.Parameters { + // these are (currently) handled elsewhere, so we're good for now + if strings.EqualFold(param.Name, "$skipToken") { + // NOTE: we may also need to do the odata ones, media has an example + continue + } + + // handled elsewhere + if strings.EqualFold(param.Name, "api-version") { + continue + } + + if strings.EqualFold(param.In, "header") || strings.EqualFold(param.In, "query") { + val := param.Name + name := cleanup.NormalizeName(val) + + option := sdkModels.SDKOperationOption{ + Required: param.Required, + } + + if strings.EqualFold(param.In, "header") { + option.HeaderName = &val + } + if strings.EqualFold(param.In, "query") { + option.QueryStringName = &val + } + + // looks like these can be dates etc too + // ./commerce/resource-manager/Microsoft.Commerce/preview/2015-06-01-preview/commerce.json- "name": "reportedEndTime", + // ./commerce/resource-manager/Microsoft.Commerce/preview/2015-06-01-preview/commerce.json- "in": "query", + // ./commerce/resource-manager/Microsoft.Commerce/preview/2015-06-01-preview/commerce.json- "required": true, + // ./commerce/resource-manager/Microsoft.Commerce/preview/2015-06-01-preview/commerce.json- "type": "string", + // ./commerce/resource-manager/Microsoft.Commerce/preview/2015-06-01-preview/commerce.json: "format": "date-time", + // ./commerce/resource-manager/Microsoft.Commerce/preview/2015-06-01-preview/commerce.json- "description": "The end of the time range to retrieve data for." + objectDefinition, err := determineObjectDefinitionForOption(param) + if err != nil { + return nil, nil, fmt.Errorf("determining field type for operation: %+v", err) + } + option.ObjectDefinition = *objectDefinition + + if param.Enum != nil { + types := []string{ + param.Type, + } + constant, err := constants.Parse(types, param.Name, nil, param.Enum, param.Extensions) + if err != nil { + return nil, nil, fmt.Errorf("mapping %q: %+v", param.Name, err) + } + result.Constants[constant.Name] = constant.Details + + option.ObjectDefinition = sdkModels.SDKOperationOptionObjectDefinition{ + Type: sdkModels.ReferenceSDKOperationOptionObjectDefinitionType, + ReferenceName: &constant.Name, + } + } + + output[name] = option + } + } + + return output, &result, nil +} + +func determineObjectDefinitionForOption(input spec.Parameter) (*sdkModels.SDKOperationOptionObjectDefinition, error) { + if strings.EqualFold(input.Type, "array") { + // https://github.com/Azure/azure-rest-api-specs/blob/1b0ed8edd58bb7c9ade9a27430759527bd4eec8e/specification/trafficmanager/resource-manager/Microsoft.Network/stable/2018-03-01/trafficmanager.json#L735-L738 + if input.Items == nil { + return nil, fmt.Errorf("an array/csv option type was specified with no items") + } + + innerType, err := determineObjectDefinitionForOptionRaw(input.Items.Type, input.Items.CollectionFormat, input.Items.Format) + if err != nil { + return nil, fmt.Errorf("determining nested object definition for option: %+v", err) + } + + if strings.EqualFold(input.CollectionFormat, "csv") { + return &sdkModels.SDKOperationOptionObjectDefinition{ + Type: sdkModels.CSVSDKOperationOptionObjectDefinitionType, + NestedItem: innerType, + }, nil + } + + return &sdkModels.SDKOperationOptionObjectDefinition{ + Type: sdkModels.ListSDKOperationOptionObjectDefinitionType, + NestedItem: innerType, + }, nil + } + + return determineObjectDefinitionForOptionRaw(input.Type, input.CollectionFormat, input.Format) +} + +func determineObjectDefinitionForOptionRaw(paramType string, collectionFormat string, format string) (*sdkModels.SDKOperationOptionObjectDefinition, error) { + switch strings.ToLower(paramType) { + case "array": + { + if strings.EqualFold(collectionFormat, "csv") { + return nil, fmt.Errorf("cannot contain a csv") + } + + return nil, fmt.Errorf("cannot contain an array") + } + + case "boolean": + return &sdkModels.SDKOperationOptionObjectDefinition{ + Type: sdkModels.BooleanSDKOperationOptionObjectDefinitionType, + }, nil + + case "integer": + return &sdkModels.SDKOperationOptionObjectDefinition{ + Type: sdkModels.IntegerSDKOperationOptionObjectDefinitionType, + }, nil + + case "number": + { + if strings.EqualFold(format, "double") { + return &sdkModels.SDKOperationOptionObjectDefinition{ + Type: sdkModels.FloatSDKOperationOptionObjectDefinitionType, + }, nil + } + + if strings.EqualFold(format, "decimal") { + return &sdkModels.SDKOperationOptionObjectDefinition{ + Type: sdkModels.FloatSDKOperationOptionObjectDefinitionType, + }, nil + } + + if format != "" { + // we don't know what this is, better to raise an error and handle it than make + // it an integer if it should be a float or something + return nil, fmt.Errorf("unsupported format type for number %q", format) + } + + return &sdkModels.SDKOperationOptionObjectDefinition{ + Type: sdkModels.IntegerSDKOperationOptionObjectDefinitionType, + }, nil + } + + case "string": + return &sdkModels.SDKOperationOptionObjectDefinition{ + Type: sdkModels.StringSDKOperationOptionObjectDefinitionType, + }, nil + } + return nil, fmt.Errorf("unsupported field type %q", paramType) +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/operation/parse_operation.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/operation/parse_operation.go new file mode 100644 index 00000000000..99c0626043c --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/operation/parse_operation.go @@ -0,0 +1,87 @@ +package operation + +import ( + "fmt" + "strings" + + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" + parserModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parsingcontext" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/logging" +) + +func parseOperation(parsingContext *parsingcontext.Context, operation parsedOperation, resourceProvider *string, operationIdsToParsedOperations map[string]resourceids.ParsedOperation) (*sdkModels.SDKOperation, *parserModels.ParseResult, error) { + result := parserModels.ParseResult{ + Constants: map[string]sdkModels.SDKConstant{}, + Models: map[string]sdkModels.SDKModel{}, + } + + contentType := determineContentType(operation) + expectedStatusCodes := expectedStatusCodesForOperation(operation) + paginationField := fieldContainingPaginationDetailsForOperation(operation) + requestObject, nestedResult, err := requestObjectForOperation(parsingContext, operation, result) + if err != nil { + return nil, nil, fmt.Errorf("determining request operation for %q (method %q / ID %q): %+v", operation.name, operation.httpMethod, operation.operation.ID, err) + } + if nestedResult != nil { + if err := result.Append(*nestedResult); err != nil { + return nil, nil, fmt.Errorf("appending nestedResult: %+v", err) + } + } + responseResult, nestedResult, err := responseObjectForOperation(parsingContext, operation, result) + if err != nil { + return nil, nil, fmt.Errorf("determining response operation for %q (method %q / ID %q): %+v", operation.name, operation.httpMethod, operation.operation.ID, err) + } + if nestedResult != nil { + if err := result.Append(*nestedResult); err != nil { + return nil, nil, fmt.Errorf("appending nestedResult: %+v", err) + } + } + if paginationField == nil && responseResult.paginationFieldName != nil { + paginationField = responseResult.paginationFieldName + } + longRunning := isLongRunning(operation) + + options, nestedResult, err := optionsForOperation(operation) + if err != nil { + return nil, nil, fmt.Errorf("building options for operation %q: %+v", operation.name, err) + } + if nestedResult != nil { + if err := result.Append(*nestedResult); err != nil { + return nil, nil, fmt.Errorf("appending nestedResult: %+v", err) + } + } + + resourceId := operationIdsToParsedOperations[operation.operation.ID] + usesADifferentResourceProvider, err := resourceIdUsesAResourceProviderOtherThan(resourceId.ResourceId, resourceProvider) + if err != nil { + return nil, nil, err + } + if usesADifferentResourceProvider != nil && *usesADifferentResourceProvider { + // this check currently disabled as we want to import all resources defined for a service, even when + // the ResourceProvider may be different across those resources. prior to refactoring the importer to use + // Data API SDK types, this check never worked, but now it does, and we don't need it. + logging.Tracef("Skipping default ResourceProvider check for %q (%q)", *resourceId.ResourceIdName, *resourceProvider) + //return nil, nil, nil + } + + operationData := sdkModels.SDKOperation{ + ContentType: contentType, + ExpectedStatusCodes: expectedStatusCodes, + FieldContainingPaginationDetails: paginationField, + LongRunning: longRunning, + Method: strings.ToUpper(operation.httpMethod), + Options: options, + RequestObject: requestObject, + ResourceIDName: resourceId.ResourceIdName, + ResponseObject: responseResult.objectDefinition, + URISuffix: resourceId.UriSuffix, + } + + if shouldBeIgnored(operationData) { + return nil, nil, nil + } + + return &operationData, &result, nil +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/operation/parse_operations.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/operation/parse_operations.go new file mode 100644 index 00000000000..461a0ecfcf0 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/operation/parse_operations.go @@ -0,0 +1,56 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package operation + +import ( + "fmt" + + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/ignore" + parserModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parsingcontext" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/logging" +) + +func ParseOperationsWithinTag(parsingContext *parsingcontext.Context, tag *string, operationIdsToParsedOperations map[string]resourceids.ParsedOperation, resourceProvider *string, found parserModels.ParseResult) (map[string]sdkModels.SDKOperation, *parserModels.ParseResult, error) { + operations := make(map[string]sdkModels.SDKOperation, 0) + result := parserModels.ParseResult{ + Constants: map[string]sdkModels.SDKConstant{}, + Models: map[string]sdkModels.SDKModel{}, + } + result.Append(found) + + // first find the operations then pull out everything we can + operationsForThisTag := findOperationsMatchingTag(parsingContext, tag) + for _, operation := range *operationsForThisTag { + logging.Debugf("Operation - %s %q..", operation.httpMethod, operation.uri) + + if ignore.Operation(operation.uri) { + logging.Debugf("Operation should be ignored - skipping..") + continue + } + + op, nestedResult, err := parseOperation(parsingContext, operation, resourceProvider, operationIdsToParsedOperations) + if err != nil { + return nil, nil, fmt.Errorf("parsing %s operation %q: %+v", operation.httpMethod, operation.uri, err) + } + if nestedResult != nil { + if err := result.Append(*nestedResult); err != nil { + return nil, nil, fmt.Errorf("appending nestedResult: %+v", err) + } + } + + if existing, hasExisting := operations[operation.name]; hasExisting { + return nil, nil, fmt.Errorf("conflicting operations with the Name %q - first %q - second %q", operation.name, existing.Method, op.Method) + } + + if op == nil { + continue + } + operations[operation.name] = *op + } + + return operations, &result, nil +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/operation/request_object.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/operation/request_object.go new file mode 100644 index 00000000000..e4e43ab2831 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/operation/request_object.go @@ -0,0 +1,35 @@ +package operation + +import ( + "fmt" + "strings" + + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" + parserModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parsingcontext" +) + +func requestObjectForOperation(parsingContext *parsingcontext.Context, input parsedOperation, known parserModels.ParseResult) (*sdkModels.SDKObjectDefinition, *parserModels.ParseResult, error) { + // all we should parse out is the top level object - nothing more. + + // find the same operation in the unexpanded swagger spec since we need the reference name + _, _, unexpandedOperation, found := parsingContext.SwaggerSpecWithReferences.OperationForName(input.operation.ID) + if !found { + return nil, nil, nil + } + + for _, param := range unexpandedOperation.Parameters { + if strings.EqualFold(param.In, "body") { + parsingModel := true + objectDefinition, result, err := parsingContext.ParseObjectDefinition(param.Schema.Title, param.Schema.Title, param.Schema, known, parsingModel) + if err != nil { + return nil, nil, fmt.Errorf("parsing request object for parameter %q: %+v", param.Name, err) + } + if objectDefinition != nil { + return objectDefinition, result, nil + } + } + } + + return nil, nil, nil +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/operation/resource_id.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/operation/resource_id.go new file mode 100644 index 00000000000..b436fd4a76d --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/operation/resource_id.go @@ -0,0 +1,30 @@ +package operation + +import ( + "fmt" + "strings" + + "github.com/hashicorp/go-azure-helpers/lang/pointer" + sdkHelpers "github.com/hashicorp/pandora/tools/data-api-sdk/v1/helpers" + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" +) + +func resourceIdUsesAResourceProviderOtherThan(input *sdkModels.ResourceID, resourceProvider *string) (*bool, error) { + if input == nil || resourceProvider == nil { + return pointer.To(false), nil + } + + for i, segment := range input.Segments { + if segment.Type != sdkModels.ResourceProviderResourceIDSegmentType { + continue + } + + if segment.FixedValue == nil { + return nil, fmt.Errorf("the Resource ID %q Segment %d was a ResourceProviderSegment with no FixedValue", sdkHelpers.DisplayValueForResourceID(*input), i) + } + if !strings.EqualFold(*segment.FixedValue, *resourceProvider) { + return pointer.To(true), nil + } + } + return pointer.To(false), nil +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/operation/response_object.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/operation/response_object.go new file mode 100644 index 00000000000..355f7db1953 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/operation/response_object.go @@ -0,0 +1,79 @@ +package operation + +import ( + "fmt" + "sort" + + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" + parserModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parsingcontext" +) + +type operationResponseObjectResult struct { + objectDefinition *sdkModels.SDKObjectDefinition + paginationFieldName *string +} + +func responseObjectForOperation(parsingContext *parsingcontext.Context, input parsedOperation, known parserModels.ParseResult) (*operationResponseObjectResult, *parserModels.ParseResult, error) { + output := operationResponseObjectResult{} + result := parserModels.ParseResult{ + Constants: map[string]sdkModels.SDKConstant{}, + Models: map[string]sdkModels.SDKModel{}, + } + result.Append(known) + + // find the same operation in the unexpanded swagger spec since we need the reference name + _, _, unexpandedOperation, found := parsingContext.SwaggerSpecWithReferences.OperationForName(input.operation.ID) + if !found { + return nil, nil, fmt.Errorf("couldn't find Operation ID %q in the unexpanded Swagger spec", input.operation.ID) + } + + // since it's possible for operations to have multiple status codes, parse out all the objects and then find the most applicable + statusCodes := make([]int, 0) + objectDefinitionsByStatusCode := map[int]sdkModels.SDKObjectDefinition{} + for statusCode, details := range unexpandedOperation.Responses.StatusCodeResponses { + if !isASuccessResponse(statusCode, details) { + continue + } + + if details.ResponseProps.Schema == nil { + continue + } + + parsingModel := true + objectDefinition, nestedResult, err := parsingContext.ParseObjectDefinition(details.ResponseProps.Schema.Title, details.ResponseProps.Schema.Title, details.ResponseProps.Schema, result, parsingModel) + if err != nil { + return nil, nil, fmt.Errorf("parsing response object from status code %d: %+v", statusCode, err) + } + + statusCodes = append(statusCodes, statusCode) + objectDefinitionsByStatusCode[statusCode] = *objectDefinition + result.Append(*nestedResult) + } + + sort.Ints(statusCodes) + // if there's multiple status codes, pick the first successful one (which should be a 200) + for _, statusCode := range statusCodes { + if statusCode < 200 || statusCode >= 300 { + continue + } + + object, ok := objectDefinitionsByStatusCode[statusCode] + if !ok { + return nil, nil, fmt.Errorf("internal-error: missing object definitions by status code for %d", statusCode) + } + output.objectDefinition = &object + break + } + // otherwise just take the first one + if len(statusCodes) > 0 && output.objectDefinition == nil { + statusCode := statusCodes[0] + object, ok := objectDefinitionsByStatusCode[statusCode] + if !ok { + return nil, nil, fmt.Errorf("internal-error: missing object definitions by status code for %d", statusCode) + } + output.objectDefinition = &object + } + + return &output, &result, nil +} diff --git a/tools/importer-rest-api-specs/components/parser/operations_test.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/operations_test.go similarity index 78% rename from tools/importer-rest-api-specs/components/parser/operations_test.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/operations_test.go index 0f33d0190f6..77f03d6e166 100644 --- a/tools/importer-rest-api-specs/components/parser/operations_test.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/operations_test.go @@ -1,40 +1,38 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package parser +package parser_test import ( + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testhelpers" "net/http" "testing" "github.com/hashicorp/go-azure-helpers/lang/pointer" sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" - importerModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/models" ) // TODO: tests for the different types of Operation Object Definition - including CSV's inner object func TestParseOperationsEmpty(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_empty.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_empty.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{}, + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{}, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleWithTag(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_with_tag.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_with_tag.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Hello": { Operations: map[string]sdkModels.SDKOperation{ @@ -49,18 +47,17 @@ func TestParseOperationSingleWithTag(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleWithTagAndResourceId(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_with_tag_resource_id.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_with_tag_resource_id.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Hello": { Operations: map[string]sdkModels.SDKOperation{ @@ -81,25 +78,24 @@ func TestParseOperationSingleWithTagAndResourceId(t *testing.T) { sdkModels.NewStaticValueResourceIDSegment("staticProviders", "providers"), sdkModels.NewResourceProviderResourceIDSegment("staticMicrosoftFooBar", "Microsoft.FooBar"), sdkModels.NewStaticValueResourceIDSegment("staticThings", "things"), - sdkModels.NewUserSpecifiedResourceIDSegment("thing", "thing"), + sdkModels.NewUserSpecifiedResourceIDSegment("thingName", "thing"), }, }, }, }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleWithTagAndResourceIdSuffix(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_with_tag_resource_id_suffix.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_with_tag_resource_id_suffix.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Hello": { Operations: map[string]sdkModels.SDKOperation{ @@ -121,25 +117,24 @@ func TestParseOperationSingleWithTagAndResourceIdSuffix(t *testing.T) { sdkModels.NewStaticValueResourceIDSegment("staticProviders", "providers"), sdkModels.NewResourceProviderResourceIDSegment("staticMicrosoftFooBar", "Microsoft.FooBar"), sdkModels.NewStaticValueResourceIDSegment("staticThings", "things"), - sdkModels.NewUserSpecifiedResourceIDSegment("thing", "thing"), + sdkModels.NewUserSpecifiedResourceIDSegment("thingName", "thing"), }, }, }, }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleWithRequestObject(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_with_request_object.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_with_request_object.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Hello": { Models: map[string]sdkModels.SDKModel{ @@ -170,18 +165,17 @@ func TestParseOperationSingleWithRequestObject(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleWithRequestObjectInlined(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_with_request_object_inlined.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_with_request_object_inlined.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Hello": { Models: map[string]sdkModels.SDKModel{ @@ -212,18 +206,17 @@ func TestParseOperationSingleWithRequestObjectInlined(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleWithResponseObject(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_with_response_object.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_with_response_object.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Hello": { Models: map[string]sdkModels.SDKModel{ @@ -254,18 +247,17 @@ func TestParseOperationSingleWithResponseObject(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleWithResponseObjectInlined(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_with_response_object_inlined.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_with_response_object_inlined.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Hello": { Models: map[string]sdkModels.SDKModel{ @@ -296,18 +288,17 @@ func TestParseOperationSingleWithResponseObjectInlined(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleWithResponseObjectInlinedList(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_with_response_object_inlined_list.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_with_response_object_inlined_list.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Hello": { Models: map[string]sdkModels.SDKModel{ @@ -341,18 +332,17 @@ func TestParseOperationSingleWithResponseObjectInlinedList(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleRequestingWithABool(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_requesting_with_a_bool.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_requesting_with_a_bool.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Hello": { Operations: map[string]sdkModels.SDKOperation{ @@ -369,18 +359,17 @@ func TestParseOperationSingleRequestingWithABool(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleRequestingWithAInteger(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_requesting_with_a_int.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_requesting_with_a_int.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Hello": { Operations: map[string]sdkModels.SDKOperation{ @@ -397,18 +386,17 @@ func TestParseOperationSingleRequestingWithAInteger(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleRequestingWithADictionaryOfStrings(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_requesting_with_a_dictionary_of_strings.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_requesting_with_a_dictionary_of_strings.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Hello": { Operations: map[string]sdkModels.SDKOperation{ @@ -428,18 +416,17 @@ func TestParseOperationSingleRequestingWithADictionaryOfStrings(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleRequestingWithAListOfStrings(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_requesting_with_a_list_of_strings.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_requesting_with_a_list_of_strings.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Hello": { Operations: map[string]sdkModels.SDKOperation{ @@ -459,20 +446,19 @@ func TestParseOperationSingleRequestingWithAListOfStrings(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } // Models are already tested above func TestParseOperationSingleRequestingWithAString(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_requesting_with_a_string.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_requesting_with_a_string.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Hello": { Operations: map[string]sdkModels.SDKOperation{ @@ -489,18 +475,17 @@ func TestParseOperationSingleRequestingWithAString(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleReturningABool(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_returning_a_bool.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_returning_a_bool.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Hello": { Operations: map[string]sdkModels.SDKOperation{ @@ -517,18 +502,17 @@ func TestParseOperationSingleReturningABool(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleReturningAFloat(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_returning_a_float.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_returning_a_float.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Hello": { Operations: map[string]sdkModels.SDKOperation{ @@ -545,18 +529,17 @@ func TestParseOperationSingleReturningAFloat(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleReturningAFile(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_returning_a_file.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_returning_a_file.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Hello": { Operations: map[string]sdkModels.SDKOperation{ @@ -573,18 +556,17 @@ func TestParseOperationSingleReturningAFile(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleReturningAnInteger(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_returning_an_integer.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_returning_an_integer.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Hello": { Operations: map[string]sdkModels.SDKOperation{ @@ -601,18 +583,17 @@ func TestParseOperationSingleReturningAnInteger(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleReturningAString(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_returning_a_string.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_returning_a_string.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Hello": { Operations: map[string]sdkModels.SDKOperation{ @@ -629,19 +610,18 @@ func TestParseOperationSingleReturningAString(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleReturningAnErrorStatusCode(t *testing.T) { // In this instance the error status code should be ignored we're only concerned with 2XX status codes - actual, err := ParseSwaggerFileForTesting(t, "operations_single_returning_an_error_status_code.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_returning_an_error_status_code.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Hello": { Operations: map[string]sdkModels.SDKOperation{ @@ -658,18 +638,17 @@ func TestParseOperationSingleReturningAnErrorStatusCode(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleReturningATopLevelRawObject(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_returning_a_top_level_raw_object.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_returning_a_top_level_raw_object.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Hello": { Operations: map[string]sdkModels.SDKOperation{ @@ -689,18 +668,17 @@ func TestParseOperationSingleReturningATopLevelRawObject(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleReturningADictionaryOfAModel(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_returning_a_dictionary_of_model.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_returning_a_dictionary_of_model.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Hello": { Models: map[string]sdkModels.SDKModel{ @@ -734,18 +712,17 @@ func TestParseOperationSingleReturningADictionaryOfAModel(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleReturningADictionaryOfStrings(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_returning_a_dictionary_of_strings.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_returning_a_dictionary_of_strings.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Hello": { Operations: map[string]sdkModels.SDKOperation{ @@ -765,18 +742,17 @@ func TestParseOperationSingleReturningADictionaryOfStrings(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleReturningAListOfIntegers(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_returning_a_list_of_ints.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_returning_a_list_of_ints.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Hello": { Operations: map[string]sdkModels.SDKOperation{ @@ -796,18 +772,17 @@ func TestParseOperationSingleReturningAListOfIntegers(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleReturningAListOfAModel(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_returning_a_list_of_model.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_returning_a_list_of_model.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Hello": { Models: map[string]sdkModels.SDKModel{ @@ -841,18 +816,17 @@ func TestParseOperationSingleReturningAListOfAModel(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleReturningAListOfStrings(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_returning_a_list_of_strings.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_returning_a_list_of_strings.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Hello": { Operations: map[string]sdkModels.SDKOperation{ @@ -872,18 +846,17 @@ func TestParseOperationSingleReturningAListOfStrings(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleReturningAListOfListOfAModel(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_returning_a_list_of_list_of_model.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_returning_a_list_of_list_of_model.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Hello": { Models: map[string]sdkModels.SDKModel{ @@ -920,18 +893,17 @@ func TestParseOperationSingleReturningAListOfListOfAModel(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleReturningAListOfListOfStrings(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_returning_a_list_of_list_of_strings.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_returning_a_list_of_list_of_strings.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Hello": { Operations: map[string]sdkModels.SDKOperation{ @@ -954,18 +926,17 @@ func TestParseOperationSingleReturningAListOfListOfStrings(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleWithList(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_list.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_list.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Hello": { Models: map[string]sdkModels.SDKModel{ @@ -997,20 +968,19 @@ func TestParseOperationSingleWithList(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleWithListWhichIsNotAList(t *testing.T) { // all List operations should have an `x-ms-pageable` attribute, but some don't due to bad data // as such this checks we can duck-type it out - actual, err := ParseSwaggerFileForTesting(t, "operations_single_list_which_is_not_a_list.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_list_which_is_not_a_list.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Hello": { Models: map[string]sdkModels.SDKModel{ @@ -1046,18 +1016,17 @@ func TestParseOperationSingleWithListWhichIsNotAList(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleWithListReturningAListOfStrings(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_list_of_strings.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_list_of_strings.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Hello": { Operations: map[string]sdkModels.SDKOperation{ @@ -1075,20 +1044,19 @@ func TestParseOperationSingleWithListReturningAListOfStrings(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleWithListWithoutPageable(t *testing.T) { // all List operations should have an `x-ms-pageable` attribute, but some don't due to bad data // as such this checks we can duck-type it out - actual, err := ParseSwaggerFileForTesting(t, "operations_single_list_without_pageable.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_list_without_pageable.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Hello": { Models: map[string]sdkModels.SDKModel{ @@ -1120,18 +1088,17 @@ func TestParseOperationSingleWithListWithoutPageable(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleWithLongRunningOperation(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_long_running.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_long_running.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Hello": { Models: map[string]sdkModels.SDKModel{ @@ -1163,18 +1130,17 @@ func TestParseOperationSingleWithLongRunningOperation(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleWithRequestAndResponseObject(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_with_request_and_response_object.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_with_request_and_response_object.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Hello": { Models: map[string]sdkModels.SDKModel{ @@ -1209,18 +1175,17 @@ func TestParseOperationSingleWithRequestAndResponseObject(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleWithMultipleTags(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_multiple_tags.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_multiple_tags.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Hello": { Operations: map[string]sdkModels.SDKOperation{ @@ -1246,18 +1211,17 @@ func TestParseOperationSingleWithMultipleTags(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleWithInferredTag(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_with_no_tag.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_with_no_tag.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ // since there's no tags, the file name is used to infer the tag (in this case, 'OperationsSingleWithNoTags') "OperationsSingleWithNoTags": { @@ -1273,18 +1237,17 @@ func TestParseOperationSingleWithInferredTag(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleWithHeaderOptions(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_with_header_options.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_with_header_options.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Hello": { Operations: map[string]sdkModels.SDKOperation{ @@ -1355,18 +1318,17 @@ func TestParseOperationSingleWithHeaderOptions(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleWithQueryStringOptions(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_with_querystring_options.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_with_querystring_options.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Hello": { Operations: map[string]sdkModels.SDKOperation{ @@ -1437,18 +1399,17 @@ func TestParseOperationSingleWithQueryStringOptions(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationMultipleBasedOnTheSameResourceId(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_multiple_same_resource_id.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_multiple_same_resource_id.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Hello": { Operations: map[string]sdkModels.SDKOperation{ @@ -1476,25 +1437,24 @@ func TestParseOperationMultipleBasedOnTheSameResourceId(t *testing.T) { sdkModels.NewStaticValueResourceIDSegment("staticProviders", "providers"), sdkModels.NewResourceProviderResourceIDSegment("staticMicrosoftFooBar", "Microsoft.FooBar"), sdkModels.NewStaticValueResourceIDSegment("staticThings", "things"), - sdkModels.NewUserSpecifiedResourceIDSegment("thing", "thing"), + sdkModels.NewUserSpecifiedResourceIDSegment("thingName", "thing"), }, }, }, }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationsContainingContentTypes(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operation_content_types.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operation_content_types.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Hello": { Operations: map[string]sdkModels.SDKOperation{ @@ -1545,18 +1505,17 @@ func TestParseOperationsContainingContentTypes(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationContainingMultipleReturnObjects(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_multiple_return_objects.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_multiple_return_objects.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Hello": { Models: map[string]sdkModels.SDKModel{ @@ -1587,18 +1546,17 @@ func TestParseOperationContainingMultipleReturnObjects(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationsWithStutteringNames(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_with_stuttering_names.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_with_stuttering_names.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "ExampleTag": { Operations: map[string]sdkModels.SDKOperation{ @@ -1627,5 +1585,5 @@ func TestParseOperationsWithStutteringNames(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_discriminated.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_discriminated.go new file mode 100644 index 00000000000..97d9faaba64 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_discriminated.go @@ -0,0 +1,70 @@ +package parser + +import ( + "fmt" + "strings" + + "github.com/hashicorp/go-azure-helpers/lang/pointer" + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" + parserModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models" +) + +func (p *apiDefinitionsParser) FindOrphanedDiscriminatedModels(serviceName string) (*parserModels.ParseResult, error) { + result := parserModels.ParseResult{ + Constants: map[string]sdkModels.SDKConstant{}, + Models: map[string]sdkModels.SDKModel{}, + } + + for modelName, definition := range p.context.SwaggerSpecWithReferencesRaw.Definitions { + if _, ok := definition.Extensions.GetString("x-ms-discriminator-value"); ok { + details, err := p.context.ParseModel(modelName, definition) + if err != nil { + return nil, fmt.Errorf("parsing model details for model %q: %+v", modelName, err) + } + if err := result.Append(*details); err != nil { + return nil, fmt.Errorf("appending model %q: %+v", modelName, err) + } + } + + // intentionally scoped to `datafactory` given the peculiarities in the swagger definition + // in particular question 4. in this issue https://github.com/Azure/azure-rest-api-specs/issues/28380 + // TODO: determine whether it is safe to remove this hardcoded restriction and apply this logic to all services + if strings.EqualFold(serviceName, "datafactory") { + // this catches orphaned discriminated models where the discriminator information is housed in the parent + // and uses the name of the model as the discriminated value + if _, ok := definition.Extensions.GetString("x-ms-discriminator-value"); !ok && len(definition.AllOf) > 0 { + parentType, discriminator, err := p.context.FindAncestorType(definition) + if err != nil { + return nil, fmt.Errorf("determining ancestor type for model %q: %+v", modelName, err) + } + + details, err := p.context.ParseModel(modelName, definition) + if err != nil { + return nil, fmt.Errorf("parsing model details for model %q: %+v", modelName, err) + } + if parentType != nil && discriminator != nil { + model := details.Models[modelName] + model.ParentTypeName = parentType + model.FieldNameContainingDiscriminatedValue = discriminator + model.DiscriminatedValue = pointer.To(modelName) + details.Models[modelName] = model + } + if err := result.Append(*details); err != nil { + return nil, fmt.Errorf("appending model %q: %+v", modelName, err) + } + } + } + } + + // this will also pull out the parent model in the file which will already have been parsed, but that's ok + // since they will be de-duplicated when we call combineResourcesWith + nestedResult, err := p.context.FindNestedItemsYetToBeParsed(map[string]sdkModels.SDKOperation{}, result) + if err != nil { + return nil, fmt.Errorf("finding nested items yet to be parsed: %+v", err) + } + if err := result.Append(*nestedResult); err != nil { + return nil, fmt.Errorf("appending nestedResult from Models used by existing Items: %+v", err) + } + + return &result, nil +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_resource_ids.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_resource_ids.go new file mode 100644 index 00000000000..bceb6fff48e --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_resource_ids.go @@ -0,0 +1,17 @@ +package parser + +import ( + "fmt" + + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids" +) + +func (p *apiDefinitionsParser) ParseResourceIds() (*resourceids.ParseResult, error) { + parser := resourceids.NewParser(p.context.SwaggerSpecExpanded) + resourceIds, err := parser.Parse() + if err != nil { + return nil, fmt.Errorf("finding Resource IDs: %+v", err) + } + + return resourceIds, nil +} diff --git a/tools/importer-rest-api-specs/components/parser/resource_ids_test.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_resource_ids_test.go similarity index 83% rename from tools/importer-rest-api-specs/components/parser/resource_ids_test.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_resource_ids_test.go index bf7a41dac03..3afae76e38b 100644 --- a/tools/importer-rest-api-specs/components/parser/resource_ids_test.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_resource_ids_test.go @@ -1,25 +1,24 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package parser +package parser_test import ( "testing" "github.com/hashicorp/go-azure-helpers/lang/pointer" sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" - importerModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/models" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testhelpers" ) func TestParseResourceIdBasic(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "resource_ids_basic.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "resource_ids_basic.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Example": { ResourceIDs: map[string]sdkModels.ResourceID{ @@ -47,18 +46,17 @@ func TestParseResourceIdBasic(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseResourceIdContainingAConstant(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "resource_ids_containing_constant.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "resource_ids_containing_constant.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Example": { Constants: map[string]sdkModels.SDKConstant{ @@ -94,18 +92,17 @@ func TestParseResourceIdContainingAConstant(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseResourceIdContainingAScope(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "resource_ids_containing_scope.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "resource_ids_containing_scope.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Example": { ResourceIDs: map[string]sdkModels.ResourceID{ @@ -130,7 +127,7 @@ func TestParseResourceIdContainingAScope(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseResourceIdContainingAHiddenScope(t *testing.T) { @@ -143,14 +140,13 @@ func TestParseResourceIdContainingAHiddenScope(t *testing.T) { t.Run(file, func(t *testing.T) { fileName := file - actual, err := ParseSwaggerFileForTesting(t, fileName, nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, fileName, nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Example": { ResourceIDs: map[string]sdkModels.ResourceID{ @@ -172,21 +168,20 @@ func TestParseResourceIdContainingAHiddenScope(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) }) } } func TestParseResourceIdContainingAHiddenScopeWithExtraSegment(t *testing.T) { // The extra segment should be ignored and detected as a regular scope - actual, err := ParseSwaggerFileForTesting(t, "resource_ids_containing_hidden_scope_with_extra_segment.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "resource_ids_containing_hidden_scope_with_extra_segment.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Example": { ResourceIDs: map[string]sdkModels.ResourceID{ @@ -208,18 +203,17 @@ func TestParseResourceIdContainingAHiddenScopeWithExtraSegment(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseResourceIdContainingAHiddenScopeWithSuffix(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "resource_ids_containing_hidden_scope_with_suffix.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "resource_ids_containing_hidden_scope_with_suffix.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Example": { ResourceIDs: map[string]sdkModels.ResourceID{ @@ -242,7 +236,7 @@ func TestParseResourceIdContainingAHiddenScopeWithSuffix(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseResourceIdContainingAHiddenScopeNested(t *testing.T) { @@ -255,14 +249,13 @@ func TestParseResourceIdContainingAHiddenScopeNested(t *testing.T) { t.Run(file, func(t *testing.T) { fileName := file - actual, err := ParseSwaggerFileForTesting(t, fileName, nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, fileName, nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Example": { ResourceIDs: map[string]sdkModels.ResourceID{ @@ -284,20 +277,19 @@ func TestParseResourceIdContainingAHiddenScopeNested(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) }) } } func TestParseResourceIdContainingAHiddenScopeNestedWithExtraSegment(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "resource_ids_containing_hidden_scope_nested_with_extra_segment.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "resource_ids_containing_hidden_scope_nested_with_extra_segment.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Example": { ResourceIDs: map[string]sdkModels.ResourceID{ @@ -319,18 +311,17 @@ func TestParseResourceIdContainingAHiddenScopeNestedWithExtraSegment(t *testing. }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseResourceIdContainingAHiddenScopeNestedWithSuffix(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "resource_ids_containing_hidden_scope_nested_with_suffix.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "resource_ids_containing_hidden_scope_nested_with_suffix.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Example": { ResourceIDs: map[string]sdkModels.ResourceID{ @@ -353,18 +344,17 @@ func TestParseResourceIdContainingAHiddenScopeNestedWithSuffix(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseResourceIdWithJustUriSuffix(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "resource_ids_with_just_suffix.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "resource_ids_with_just_suffix.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Example": { Operations: map[string]sdkModels.SDKOperation{ @@ -378,18 +368,17 @@ func TestParseResourceIdWithJustUriSuffix(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseResourceIdWithResourceIdAndUriSuffix(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "resource_ids_with_suffix.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "resource_ids_with_suffix.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Example": { ResourceIDs: map[string]sdkModels.ResourceID{ @@ -418,18 +407,17 @@ func TestParseResourceIdWithResourceIdAndUriSuffix(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseResourceIdWithResourceIdAndUriSuffixForMultipleUris(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "resource_ids_with_suffix_multiple_uris.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "resource_ids_with_suffix_multiple_uris.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Example": { ResourceIDs: map[string]sdkModels.ResourceID{ @@ -471,18 +459,17 @@ func TestParseResourceIdWithResourceIdAndUriSuffixForMultipleUris(t *testing.T) }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseResourceIdContainingResourceProviderShouldGetTitleCased(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "resource_ids_lowercased_resource_provider.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "resource_ids_lowercased_resource_provider.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Example": { ResourceIDs: map[string]sdkModels.ResourceID{ @@ -510,18 +497,17 @@ func TestParseResourceIdContainingResourceProviderShouldGetTitleCased(t *testing }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseResourceIdContainingTheSameResourceIdWithDifferentSegments(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "resource_ids_same_id_different_segment_casing.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "resource_ids_same_id_different_segment_casing.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Example": { ResourceIDs: map[string]sdkModels.ResourceID{ @@ -534,7 +520,7 @@ func TestParseResourceIdContainingTheSameResourceIdWithDifferentSegments(t *test sdkModels.NewStaticValueResourceIDSegment("staticProviders", "providers"), sdkModels.NewResourceProviderResourceIDSegment("staticMicrosoftSomeResourceProvider", "Microsoft.SomeResourceProvider"), sdkModels.NewStaticValueResourceIDSegment("staticVirtualMachines", "virtualMachines"), - sdkModels.NewUserSpecifiedResourceIDSegment("machineName", "machineName"), + sdkModels.NewUserSpecifiedResourceIDSegment("virtualMachineName", "virtualMachineName"), }, }, }, @@ -556,18 +542,17 @@ func TestParseResourceIdContainingTheSameResourceIdWithDifferentSegments(t *test }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseResourceIdContainingTheSegmentsNamedTheSame(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "resource_ids_multiple_segments_same_name.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "resource_ids_multiple_segments_same_name.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Example": { ResourceIDs: map[string]sdkModels.ResourceID{ @@ -596,7 +581,7 @@ func TestParseResourceIdContainingTheSegmentsNamedTheSame(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseResourceIdsWhereTheSameUriContainsDifferentConstantValuesPerOperation(t *testing.T) { @@ -613,14 +598,13 @@ func TestParseResourceIdsWhereTheSameUriContainsDifferentConstantValuesPerOperat for i := 0; i < 100; i++ { t.Logf("iteration %d", i) - actual, err := ParseSwaggerFileForTesting(t, "resource_ids_same_uri_different_constant_values_per_operation.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "resource_ids_same_uri_different_constant_values_per_operation.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Hello": { Constants: map[string]sdkModels.SDKConstant{ @@ -669,19 +653,18 @@ func TestParseResourceIdsWhereTheSameUriContainsDifferentConstantValuesPerOperat }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } } func TestParseResourceIdsCommon(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "resource_ids_common.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "resource_ids_common.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Example": { ResourceIDs: map[string]sdkModels.ResourceID{ @@ -765,5 +748,5 @@ func TestParseResourceIdsCommon(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_swagger_tag.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_swagger_tag.go new file mode 100644 index 00000000000..cce6ca5df93 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_swagger_tag.go @@ -0,0 +1,64 @@ +package parser + +import ( + "fmt" + + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema" + parserModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/operation" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids" +) + +func (p *apiDefinitionsParser) ParseAPIResourceWithinSwaggerTag(tag, resourceProvider *string, resourceIds resourceids.ParseResult, existingAPIResource sdkModels.APIResource) (*sdkModels.APIResource, error) { + result := parserModels.ParseResult{ + Constants: existingAPIResource.Constants, + Models: existingAPIResource.Models, + } + + // note that Resource ID's can contain Constants (used as segments) + if err := result.AppendConstants(resourceIds.Constants); err != nil { + return nil, fmt.Errorf("appending nestedResult from Constants: %+v", err) + } + + // pull out the operations and any inlined/top-level constants/models + operations, nestedResult, err := operation.ParseOperationsWithinTag(p.context, tag, resourceIds.OperationIdsToParsedResourceIds, resourceProvider, result) + if err != nil { + return nil, fmt.Errorf("finding operations: %+v", err) + } + if err := result.Append(*nestedResult); err != nil { + return nil, fmt.Errorf("appending nestedResult from Operations: %+v", err) + } + + // pull out each of the remaining models based on what we've got + nestedResult, err = p.context.FindNestedItemsYetToBeParsed(operations, result) + if err != nil { + return nil, fmt.Errorf("finding nested items yet to be parsed: %+v", err) + } + if err := result.Append(*nestedResult); err != nil { + return nil, fmt.Errorf("appending nestedResult from Models used by existing Items: %+v", err) + } + + // then pull out the embedded model for List operations (e.g. we don't want the wrapper type but the type for the `value` field) + operations, err = operation.RemoveWrapperModelForListOperations(operations, result) + if err != nil { + return nil, fmt.Errorf("pulling out model from list operations: %+v", err) + } + + // if there's nothing here, there's no point generating a package + if len(operations) == 0 { + return nil, nil + } + + resource := sdkModels.APIResource{ + Constants: result.Constants, + Models: result.Models, + Operations: operations, + ResourceIDs: resourceIds.NamesToResourceIDs, + } + + // then switch out any Common Schema Types (e.g. Identity) + resource = commonschema.ReplaceSDKObjectDefinitionsAsNeeded(resource) + + return &resource, nil +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_swagger_tags.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_swagger_tags.go new file mode 100644 index 00000000000..e5e9127e403 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_swagger_tags.go @@ -0,0 +1,30 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package parser + +import ( + "sort" + + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup" +) + +func (p *apiDefinitionsParser) ParseSwaggerTags() []string { + tags := make(map[string]struct{}) + + for _, operation := range p.context.SwaggerSpecExpanded.Operations() { + for _, details := range operation { + for _, tag := range details.Tags { + normalizedTag := cleanup.Title(tag) + tags[normalizedTag] = struct{}{} + } + } + } + + out := make([]string, 0) + for key := range tags { + out = append(out, key) + } + sort.Strings(out) + return out +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parser.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parser.go new file mode 100644 index 00000000000..f24bfa2386f --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parser.go @@ -0,0 +1,22 @@ +package parser + +import ( + "fmt" + + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parsingcontext" +) + +type apiDefinitionsParser struct { + context *parsingcontext.Context +} + +func NewAPIDefinitionsParser(filePath string) (*apiDefinitionsParser, error) { + parsingContext, err := parsingcontext.BuildFromFile(filePath) + if err != nil { + return nil, fmt.Errorf("building the parsing context: %+v", err) + } + + return &apiDefinitionsParser{ + context: parsingContext, + }, nil +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parsingcontext/build.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parsingcontext/build.go new file mode 100644 index 00000000000..773b9372fe5 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parsingcontext/build.go @@ -0,0 +1,333 @@ +package parsingcontext + +import ( + "fmt" + "net/url" + "path/filepath" + "strings" + + "github.com/go-openapi/analysis" + "github.com/go-openapi/loads" + "github.com/go-openapi/spec" +) + +// BuildFromFile loads swagger specs from the filesystem and parses them, returning a `Context` struct that contains +// the parsed specs for consumption in the Resource Manager parser. +// +// We are intentionally parsing each swagger file twice and flattening it with different options. +// On the first pass, the file is flattened with the `Minimal: true` option, which leaves `allOf` refs in place +// and does not inline properties from inherited models. This is the `SwaggerSpecWithReferences` version, and +// allows us to inspect the inheritance tree and properly derive discriminated models. +// +// Note that we also export the `spec.Swagger` struct containing the raw spec. This is currently used in a few +// cases where we want to manually parse the definitions, notably for traversing model inheritance for +// discriminated models. +// +// On the second pass, we flatten with `Minimal: false`, inlines all refs including `allOf`. This is the +// `SwaggerSpecExpanded` version, and is useful because models that inherit from parents will/ have all their +// fields collapsed into that model, making it easier for us to iterate them. +// +// Additionally, on each pass of parsing a swagger file, we iterate the definitions found within it, and manually +// resolve all remote references. This works around buggy behavior in the go-openapi modules where relative paths +// are mangled and thus fail to be ingested. To prevent accidental clobbering of models/constants in external swagger +// files that have the same name, we specifically only compile the models/constants that are being remotely referenced, +// as well as any references they may have to ensure no reference remains unresolved. To trick the `analysis.Flatten` +// function, we strip out the paths in remote references after resolving them, to make it appear like everything was +// loaded from the original swagger file. +func BuildFromFile(filePath string) (*Context, error) { + + // 1. Parse the file, then resolve all remote refs, and flatten without inlining `allOf` references + + // load and parse the spec + swaggerDocWithReferences, err := loads.Spec(filePath) + if err != nil { + return nil, fmt.Errorf("loading swagger file %q: %+v", filePath, err) + } + + // walk the refs in the loaded spec and resolve any external references + specWithRemotelyReferencedSchemas, err := resolveExternalSwaggerReferences(swaggerDocWithReferences, filePath) + if err != nil { + return nil, fmt.Errorf("could not resolve remote swagger references for %q: %+v", filePath, err) + } + + // mix-in the remotely referenced schemas from any external swagger documents, + analysis.Mixin(swaggerDocWithReferences.Spec(), specWithRemotelyReferencedSchemas) + + // flatten the spec, whilst retaining refs instead of inlining them + basePath := swaggerDocWithReferences.SpecFilePath() + flattenedWithReferencesOpts := &analysis.FlattenOpts{ + Minimal: true, + Verbose: true, + Expand: false, + RemoveUnused: false, + + BasePath: basePath, + Spec: analysis.New(swaggerDocWithReferences.Spec()), + } + if err = analysis.Flatten(*flattenedWithReferencesOpts); err != nil { + return nil, fmt.Errorf("flattening swagger file with references %q: %+v", filePath, err) + } + + // 2. Now parse the file again, resolve all remote refs and flatten completely, inlining complex refs such as `allOf` + + // load and parse the spec + expandedSwaggerDoc, err := loads.Spec(filePath) + if err != nil { + return nil, fmt.Errorf("loading swagger file %q: %+v", filePath, err) + } + + // walk the refs in the loaded spec and resolve any external references + specWithRemotelyReferencedSchemas, err = resolveExternalSwaggerReferences(expandedSwaggerDoc, filePath) + if err != nil { + return nil, fmt.Errorf("could not resolve remote swagger references for %q: %+v", filePath, err) + } + + // mix-in the refs from any external swagger documents, + analysis.Mixin(expandedSwaggerDoc.Spec(), specWithRemotelyReferencedSchemas) + + // flatten the spec, inlining any refs + basePath = expandedSwaggerDoc.SpecFilePath() + flattenedExpandedOpts := &analysis.FlattenOpts{ + Minimal: false, + Verbose: true, + Expand: false, + RemoveUnused: false, + + BasePath: basePath, + Spec: analysis.New(expandedSwaggerDoc.Spec()), + } + if err = analysis.Flatten(*flattenedExpandedOpts); err != nil { + return nil, fmt.Errorf("flattening expanded swagger file %q: %+v", filePath, err) + } + + return &Context{ + FilePath: filePath, + + SwaggerSpecWithReferences: swaggerDocWithReferences.Analyzer, + SwaggerSpecWithReferencesRaw: swaggerDocWithReferences.Spec(), + + SwaggerSpecExpanded: expandedSwaggerDoc.Analyzer, + }, nil +} + +// resolveExternalSwaggerReferences attempts to resolve all external refs in the current document. each external ref +// will be parsed and resolved recursively, until all possible references have been collated and returned in a *spec.Swagger +// containing all these definitions. +// We are doing this because the go-openapi parser has very buggy relative path resolution logic, and is unable to +// resolve relative paths in external referenced swagger files that are not located in the current directory. +func resolveExternalSwaggerReferences(input *loads.Document, filePath string) (*spec.Swagger, error) { + externalReferences := make(map[string]spec.Schema) + + dir, _ := filepath.Split(filePath) + + if input.Analyzer != nil { + refs := input.Analyzer.AllRefs() + for _, ref := range refs { + path := ref.GetURL().Path + if path == "" { + // don't resolve refs in the same document + continue + } + + if err := resolveRefsForModel(ref, filePath, dir, input, externalReferences); err != nil { + return nil, fmt.Errorf("resolving external ref %q in %q: %+v", ref.String(), filePath, err) + } + } + } + + return &spec.Swagger{ + SwaggerProps: spec.SwaggerProps{ + Definitions: externalReferences, + }, + }, nil +} + +// resolveRefsForModel accepts a reference to a model, and attempts to resolve it by loading an external swagger file, +// or finding it in the current swagger document. Once loaded, it is added to the `refs` map and recursively +// inspected for any refs so that they may also be resolved. +func resolveRefsForModel(ref spec.Ref, filePath, topLevelDir string, doc *loads.Document, refs map[string]spec.Schema) error { + // ignore parameters as we don't use these. we might need to include these at some point, if the go-openapi parser + // is unable to resolve them internally (which has not happened yet) + if strings.HasPrefix(ref.GetURL().Fragment, "/parameters/") { + return nil + } + + // determine the model name, and a swagger doc where it should be defined + modelName, modelFilePath := modelNamePathFromRef(ref, filePath, topLevelDir) + + // the path in the ref will no longer be needed, as we are going to resolve it ourselves + // we can do this because `ReferenceURL` is a *url.URL, so this will thankfully propagate upstream + ref.Ref.ReferenceURL.Path = "" + + if _, ok := refs[modelName]; ok { + // skip if this has already been resolved + return nil + } + + if modelFilePath == "" { + // no valid swagger doc was found for this model + return fmt.Errorf("swagger document not found for ref: %s", ref.String()) + } + + // decide whether to look for the model definition in the current file, or another referenced file + refDoc := doc + if modelFilePath != filePath { + newDoc, err := loads.Spec(modelFilePath) + if err != nil { + return fmt.Errorf("load swagger %s: %+v", modelFilePath, err) + } + + refDoc = newDoc + } + + // retrieve the resolved model + resolvedModel, ok := refDoc.Spec().Definitions[modelName] + if !ok { + return fmt.Errorf("resolve ref %s from %s: model not found", ref.String(), filePath) + } + refs[modelName] = resolvedModel + + // look for any refs in the properties (fields) of the resolved model + if err := resolveRefsForProperties(resolvedModel.Properties, modelFilePath, topLevelDir, refDoc, refs); err != nil { + return err + } + + // some models use `additionalProperties`, these may contain refs + if resolvedModel.AdditionalProperties != nil && resolvedModel.AdditionalProperties.Schema != nil { + if resolvedModel.AdditionalProperties.Schema.Ref.String() != "" { + if err := resolveRefsForModel(resolvedModel.AdditionalProperties.Schema.Ref, modelFilePath, topLevelDir, refDoc, refs); err != nil { + return err + } + } + } + + // some models use a top-level `items`, i.e. in the case of a type array(object) + if items := resolvedModel.Items; items != nil && items.Schema != nil && items.Schema.Ref.String() != "" { + if err := resolveRefsForModel(items.Schema.Ref, modelFilePath, topLevelDir, refDoc, refs); err != nil { + return err + } + } + + // look for parent models - this could be a plain child model, or a discriminated implementation + for _, allOf := range resolvedModel.AllOf { + if allOf.Ref.String() != "" { + if err := resolveRefsForModel(allOf.Ref, modelFilePath, topLevelDir, refDoc, refs); err != nil { + return err + } + } + } + + // if this is a parent model, look for implementations. this will currently only work for children defined in + // the same swagger file. + if resolvedModel.Discriminator != "" { + for defName, def := range refDoc.Spec().Definitions { + if defName == modelName { + // skip over self, cannot be own parent + continue + } + + // look at parent types for each model and see if this model is the parent + for _, allOf := range def.AllOf { + if allOf.Ref.String() != "" { + allOfRefModelName, _ := modelNamePathFromRef(allOf.Ref, modelFilePath, topLevelDir) + if modelName == allOfRefModelName { + + // this is an implementation of our parent, so construct a Ref for it and attempt to resolve it + childRef, err := spec.NewRef("#/definitions/" + defName) + if err != nil { + return fmt.Errorf("constructing a Ref for %q: %v", defName, err) + } + + if err = resolveRefsForModel(childRef, modelFilePath, topLevelDir, refDoc, refs); err != nil { + return err + } + break + } + } + } + } + } + + return nil +} + +// resolveRefsForProperties attempts to resolve any references contained within the properties for a model +func resolveRefsForProperties(properties spec.SchemaProperties, modelFilePath, topLevelDir string, refDoc *loads.Document, refs map[string]spec.Schema) error { + for _, prop := range properties { + // some property types inherit from another model + for _, allOf := range prop.AllOf { + if allOf.Ref.String() != "" { + if err := resolveRefsForModel(allOf.Ref, modelFilePath, topLevelDir, refDoc, refs); err != nil { + return err + } + } + } + + // some properties use `additionalProperties` + if prop.AdditionalProperties != nil && prop.AdditionalProperties.Schema != nil { + if prop.AdditionalProperties.Schema.Ref.String() != "" { + if err := resolveRefsForModel(prop.AdditionalProperties.Schema.Ref, modelFilePath, topLevelDir, refDoc, refs); err != nil { + return err + } + } + + if items := prop.AdditionalProperties.Schema.Items; items != nil && items.Schema != nil && items.Schema.Ref.String() != "" { + if err := resolveRefsForModel(items.Schema.Ref, modelFilePath, topLevelDir, refDoc, refs); err != nil { + return err + } + } + } + + // look for a model referenced directly by the property + if prop.Ref.String() != "" { + if err := resolveRefsForModel(prop.Ref, modelFilePath, topLevelDir, refDoc, refs); err != nil { + return err + } + } + + // look for a model referenced directly by the items of a property (i.e. for array elements) + if items := prop.Items; items != nil && items.Schema != nil && items.Schema.Ref.String() != "" { + if err := resolveRefsForModel(items.Schema.Ref, modelFilePath, topLevelDir, refDoc, refs); err != nil { + return err + } + } + + // recursively resolve any refs for properties which have inlined models + if err := resolveRefsForProperties(prop.Properties, modelFilePath, topLevelDir, refDoc, refs); err != nil { + return err + } + } + + return nil +} + +// modelNamePathFromRef determines the model name, and the path to the swagger doc that defines it, from a supplied ref +func modelNamePathFromRef(ref spec.Ref, sourcePath, topLevelDir string) (modelName string, modelFilePath string) { + refUrl := ref.GetURL() + if refUrl == nil { + return "", "" + } + + modelFilePath = refUrl.Path + if modelFilePath == "" { + // not an external reference, so use the sourcePath + modelFilePath = sourcePath + } else { + // external reference, so prepend the directory from the sourcePath + // note: there is a known swagger issue where refs have invalid paths like `.../../cosmos-db.json`, fortunately + // `filepath.Join` does a good job of cleaning these up and _so far_ these continue to resolve to the actual + // correct path. if this problem gets worse, this is a good place to add any manual workarounds. + sourceDir, _ := filepath.Split(sourcePath) + modelFilePath = filepath.Join(sourceDir, modelFilePath) + } + + // get the modelName from the last slug in the URL fragment + fragments := strings.Split(refUrl.Fragment, "/") + modelName = fragments[len(fragments)-1] + + // url-decode the model name, as it comes from the fragment which might contain urlencoded characters like `[` or `]` + // ignoring any errors here, since this function will err if invalid escape sequences are present + modelName, _ = url.QueryUnescape(modelName) + + return +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parsingcontext/context.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parsingcontext/context.go new file mode 100644 index 00000000000..aeceed8a2ea --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parsingcontext/context.go @@ -0,0 +1,21 @@ +package parsingcontext + +import ( + "github.com/go-openapi/analysis" + "github.com/go-openapi/spec" +) + +// Context contains a working set of information about the parsed API Definition. +// This includes the interpretations of the files themselves +type Context struct { + FilePath string + + // SwaggerSpecWithReferences is the parsed spec with references intact + SwaggerSpecWithReferences *analysis.Spec + + // SwaggerSpecWithReferencesRaw is the `spec.Swagger` root document, with references intact + SwaggerSpecWithReferencesRaw *spec.Swagger + + // SwaggerSpecExpanded is the parsed spec with all references resolved, and their target properties inlined + SwaggerSpecExpanded *analysis.Spec +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parsingcontext/helpers.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parsingcontext/helpers.go new file mode 100644 index 00000000000..ab9dd896c12 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parsingcontext/helpers.go @@ -0,0 +1,283 @@ +package parsingcontext + +import ( + "fmt" + "net/url" + "strings" + + "github.com/go-openapi/spec" + sdkHelpers "github.com/hashicorp/pandora/tools/data-api-sdk/v1/helpers" + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/constants" + parserModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/logging" +) + +func fragmentNameFromReference(input spec.Ref) *string { + fragmentName := input.String() + return fragmentNameFromString(fragmentName) +} + +func fragmentNameFromString(fragmentName string) *string { + if fragmentName != "" { + fragments := strings.Split(fragmentName, "/") // format #/definitions/ConfigurationStoreListResult + referenceName := fragments[len(fragments)-1] + + // url-decode the referenceName, as it comes from the fragment which might contain urlencoded characters like + // `[` or `]` - ignoring any errors here, since this function will err if invalid escape sequences are present + referenceName, _ = url.QueryUnescape(referenceName) + return &referenceName + } + + return nil +} + +func inlinedModelName(parentModelName, fieldName string) string { + // intentionally split out for consistency + val := fmt.Sprintf("%s%s", cleanup.Title(parentModelName), cleanup.Title(fieldName)) + return cleanup.NormalizeName(val) +} + +func operationMatchesTag(operation *spec.Operation, tag *string) bool { + // if there's no tags defined, we should capture it when the tag matched + if tag == nil { + return len(operation.Tags) == 0 + } + + for _, thisTag := range operation.Tags { + if strings.EqualFold(thisTag, *tag) { + return true + } + } + + return false +} + +func referencesAreTheSame(first []string, second []string) bool { + if len(first) != len(second) { + return false + } + + // first load the existing keys + keys := make(map[string]struct{}, 0) + for _, key := range first { + keys[key] = struct{}{} + } + + // then check the remaining ones + for _, key := range second { + if _, exists := keys[key]; !exists { + return false + } + } + + return true +} + +func isFieldRequired(name string, required map[string]struct{}) bool { + for k := range required { + // assume data inconsistencies + if strings.EqualFold(k, name) { + return true + } + } + + return false +} + +func (c *Context) FindNestedItemsYetToBeParsed(operations map[string]sdkModels.SDKOperation, known parserModels.ParseResult) (*parserModels.ParseResult, error) { + result := parserModels.ParseResult{ + Constants: map[string]sdkModels.SDKConstant{}, + Models: map[string]sdkModels.SDKModel{}, + } + result.Append(known) + + // Now that we have a complete list of all the nested items to find, loop around and find them + // this is intentionally not fetching nested models to avoid an infinite loop with Model1 referencing + // Model2 which references Model1 (they instead get picked up in the next iteration) + referencesToFind, err := c.determineObjectsRequiredButNotParsed(operations, result) + if err != nil { + return nil, fmt.Errorf("determining objects required but not parsed: %+v", err) + } + for len(*referencesToFind) > 0 { + for _, referenceName := range *referencesToFind { + topLevelObject, err := c.findTopLevelObject(referenceName) + if err != nil { + return nil, fmt.Errorf("finding top level object named %q: %+v", referenceName, err) + } + + parsedAsAConstant, constErr := constants.Parse(topLevelObject.Type, referenceName, nil, topLevelObject.Enum, topLevelObject.Extensions) + parsedAsAModel, modelErr := c.ParseModel(referenceName, *topLevelObject) + if (constErr != nil && modelErr != nil) || (parsedAsAConstant == nil && parsedAsAModel == nil) { + return nil, fmt.Errorf("reference %q didn't parse as a Model or a Constant.\n\nConstant Error: %+v\n\nModel Error: %+v", referenceName, constErr, modelErr) + } + + if parsedAsAConstant != nil { + result.Constants[parsedAsAConstant.Name] = parsedAsAConstant.Details + } + if parsedAsAModel != nil { + if err := result.Append(*parsedAsAModel); err != nil { + return nil, fmt.Errorf("appending model: %+v", err) + } + } + } + + remainingReferencesToFind, err := c.determineObjectsRequiredButNotParsed(operations, result) + if err != nil { + return nil, fmt.Errorf("determining objects required but not parsed: %+v", err) + } + if referencesAreTheSame(*referencesToFind, *remainingReferencesToFind) { + return nil, fmt.Errorf("the following references couldn't be found: %q", strings.Join(*referencesToFind, ", ")) + } + referencesToFind = remainingReferencesToFind + } + + return &result, nil +} + +func (c *Context) determineObjectsRequiredButNotParsed(operations map[string]sdkModels.SDKOperation, known parserModels.ParseResult) (*[]string, error) { + referencesToFind := make(map[string]struct{}) + + for _, operation := range operations { + if operation.RequestObject != nil { + topLevelRef := sdkHelpers.InnerMostSDKObjectDefinition(*operation.RequestObject) + if topLevelRef.Type == sdkModels.ReferenceSDKObjectDefinitionType { + isKnownConstant, isKnownModel := known.IsObjectKnown(*topLevelRef.ReferenceName) + if !isKnownConstant && !isKnownModel { + referencesToFind[*topLevelRef.ReferenceName] = struct{}{} + } + + if isKnownModel { + modelName := *topLevelRef.ReferenceName + model := known.Models[modelName] + missingReferencesInModel, err := c.objectsRequiredByModel(modelName, model, known) + if err != nil { + return nil, fmt.Errorf("determining objects required by model %q: %+v", modelName, err) + } + for _, name := range *missingReferencesInModel { + referencesToFind[name] = struct{}{} + } + } + } + } + + if operation.ResponseObject != nil { + topLevelRef := sdkHelpers.InnerMostSDKObjectDefinition(*operation.ResponseObject) + if topLevelRef.Type == sdkModels.ReferenceSDKObjectDefinitionType { + isKnownConstant, isKnownModel := known.IsObjectKnown(*topLevelRef.ReferenceName) + if !isKnownConstant && !isKnownModel { + referencesToFind[*topLevelRef.ReferenceName] = struct{}{} + } + + if isKnownModel { + // if it's a model, we need to check all the fields for this to find any constant or models + // that we don't know about + modelName := *topLevelRef.ReferenceName + model := known.Models[modelName] + missingReferencesInModel, err := c.objectsRequiredByModel(modelName, model, known) + if err != nil { + return nil, fmt.Errorf("determining objects required by model %q: %+v", modelName, err) + } + for _, name := range *missingReferencesInModel { + referencesToFind[name] = struct{}{} + } + } + } + } + + for _, value := range operation.Options { + topLevelRef := sdkHelpers.InnerMostSDKOperationOptionObjectDefinition(value.ObjectDefinition) + if topLevelRef.Type != sdkModels.ReferenceSDKOperationOptionObjectDefinitionType { + continue + } + + if _, isKnown := known.Constants[*topLevelRef.ReferenceName]; !isKnown { + referencesToFind[*topLevelRef.ReferenceName] = struct{}{} + } + } + } + + // then verify we have each of the models for the current models we know about + for modelName, model := range known.Models { + missingReferencesInModel, err := c.objectsRequiredByModel(modelName, model, known) + if err != nil { + return nil, fmt.Errorf("determining objects required by model %q: %+v", modelName, err) + } + for _, name := range *missingReferencesInModel { + referencesToFind[name] = struct{}{} + } + } + + out := make([]string, 0) + for k := range referencesToFind { + if _, exists := known.Constants[k]; exists { + continue + } + if _, exists := known.Models[k]; exists { + continue + } + + out = append(out, k) + } + + return &out, nil +} + +func (c *Context) objectsUsedByModel(modelName string, model sdkModels.SDKModel) (*[]string, error) { + typeNames := make(map[string]struct{}, 0) + + for fieldName, field := range model.Fields { + logging.Tracef("Determining objects used by field %q..", fieldName) + definition := sdkHelpers.InnerMostSDKObjectDefinition(field.ObjectDefinition) + if definition.ReferenceName != nil { + typeNames[*definition.ReferenceName] = struct{}{} + } + } + + if model.ParentTypeName != nil { + typeNames[*model.ParentTypeName] = struct{}{} + } + + if model.FieldNameContainingDiscriminatedValue != nil { + // this must be a discriminator + modelNamesThatImplementThis, err := c.findModelNamesWhichImplement(modelName) + if err != nil { + return nil, fmt.Errorf("finding models which implement %q: %+v", modelName, err) + } + for _, k := range *modelNamesThatImplementThis { + typeNames[k] = struct{}{} + } + } + + out := make([]string, 0) + for k := range typeNames { + out = append(out, k) + } + return &out, nil +} + +func (c *Context) objectsRequiredByModel(modelName string, model sdkModels.SDKModel, known parserModels.ParseResult) (*[]string, error) { + result := make(map[string]struct{}) + // if it's a model, we need to check each of the fields for this to find any constant or models + // that we don't know about + logging.Tracef("Determining the Objects used by the Model %q..", modelName) + typesToFind, err := c.objectsUsedByModel(modelName, model) + if err != nil { + return nil, fmt.Errorf("determining objects used by model %q: %+v", modelName, err) + } + logging.Tracef("Found %d items: %+v", len(*typesToFind), *typesToFind) + for _, typeName := range *typesToFind { + _, existingConstant := known.Constants[typeName] + _, existingModel := known.Models[typeName] + if !existingConstant && !existingModel { + result[typeName] = struct{}{} + } + } + + out := make([]string, 0) + for k := range result { + out = append(out, k) + } + return &out, nil +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parsingcontext/helpers_implements.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parsingcontext/helpers_implements.go new file mode 100644 index 00000000000..b1a2ad02a32 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parsingcontext/helpers_implements.go @@ -0,0 +1,64 @@ +package parsingcontext + +import ( + "fmt" + "strings" + + "github.com/go-openapi/spec" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/logging" +) + +func (c *Context) doesModelImplement(modelName string, value spec.Schema, parentName string) (*bool, error) { + implementsParent := false + if !strings.EqualFold(modelName, parentName) { + // does it implement (AllOf) the base class + for _, parent := range value.AllOf { + fragmentName := fragmentNameFromReference(parent.Ref) + if fragmentName == nil { + continue + } + + if strings.EqualFold(*fragmentName, parentName) { + implementsParent = true + break + } + + // otherwise does this model inherit from a model which does? + item, err := c.findTopLevelObject(*fragmentName) + if err != nil { + return nil, fmt.Errorf("loading Parent %q: %+v", *fragmentName, err) + } + if len(item.AllOf) > 0 { + inheritsFromParent, err := c.doesModelImplement(*fragmentName, *item, parentName) + if err != nil { + return nil, fmt.Errorf("determining if model %q implements %q: %+v", *fragmentName, parentName, err) + } + if *inheritsFromParent { + implementsParent = true + break + } + } + } + } + + return &implementsParent, nil +} + +func (c *Context) findModelNamesWhichImplement(parentName string) (*[]string, error) { + modelNames := make([]string, 0) + + for childName, value := range c.SwaggerSpecWithReferencesRaw.Definitions { + implementsParent, err := c.doesModelImplement(childName, value, parentName) + if err != nil { + return nil, fmt.Errorf("determining if model %q implements %q: %+v", childName, parentName, err) + } + if !*implementsParent { + continue + } + + logging.Tracef("Found %q implements %q", childName, parentName) + modelNames = append(modelNames, childName) + } + + return &modelNames, nil +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parsingcontext/parse_constant.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parsingcontext/parse_constant.go new file mode 100644 index 00000000000..755c3197121 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parsingcontext/parse_constant.go @@ -0,0 +1,17 @@ +package parsingcontext + +import ( + "fmt" + + "github.com/go-openapi/spec" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/constants" +) + +func (c *Context) ParseConstant(constantName string, spec spec.Schema) (*constants.ParsedConstant, error) { + constant, err := constants.Parse(spec.Type, constantName, nil, spec.Enum, spec.Extensions) + if err != nil { + return nil, fmt.Errorf("parsing constant: %+v", err) + } + + return constant, nil +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parsingcontext/parse_model.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parsingcontext/parse_model.go new file mode 100644 index 00000000000..93f1c7892ec --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parsingcontext/parse_model.go @@ -0,0 +1,436 @@ +package parsingcontext + +import ( + "fmt" + "strings" + + "github.com/go-openapi/spec" + "github.com/hashicorp/go-azure-helpers/lang/pointer" + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/constants" + parserModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/logging" +) + +func (c *Context) ParseModel(name string, input spec.Schema) (*parserModels.ParseResult, error) { + logging.Tracef("Parsing details for Model %q..", name) + result := parserModels.ParseResult{ + Constants: map[string]sdkModels.SDKConstant{}, + Models: map[string]sdkModels.SDKModel{}, + } + + // 1. find any constants used within this model + nestedResult, err := c.findConstantsWithinModel(name, nil, input, result) + if err != nil { + return nil, fmt.Errorf("finding constants within model: %+v", err) + } + if err := result.Append(*nestedResult); err != nil { + return nil, fmt.Errorf("appending nestedResult from constants: %+v", err) + } + + // 2. iterate over the fields and find each of the fields for this model + fields, nestedResult, err := c.fieldsForModel(name, input, result) + if err != nil { + return nil, fmt.Errorf("finding fields for model: %+v", err) + } + if err := result.Append(*nestedResult); err != nil { + return nil, fmt.Errorf("appending nestedResult from fields: %+v", err) + } + + // if it's just got constants, we can skip it + if len(fields) == 0 { + return &result, nil + } + + // 3. finally build this model directly + // Notably, we **DO NOT** load models used by this model here - this is handled once we + // know all the models which we want to load - to avoid infinite loops + model, err := c.modelDetailsFromObject(name, input, fields) + if err != nil { + return nil, fmt.Errorf("populating model details for %q: %+v", name, err) + } + result.Models[name] = *model + + return &result, nil +} + +func (c *Context) findConstantsWithinModel(fieldName string, modelName *string, input spec.Schema, known parserModels.ParseResult) (*parserModels.ParseResult, error) { + // NOTE: both Models and Fields are passed in here + result := parserModels.ParseResult{ + Constants: map[string]sdkModels.SDKConstant{}, + Models: map[string]sdkModels.SDKModel{}, + } + result.Append(known) + + if len(input.Enum) > 0 { + constant, err := constants.Parse(input.Type, fieldName, modelName, input.Enum, input.Extensions) + if err != nil { + return nil, fmt.Errorf("parsing constant: %+v", err) + } + result.Constants[constant.Name] = constant.Details + } + + // Check any object that this model inherits from + if len(input.AllOf) > 0 { + for _, parent := range input.AllOf { + fragmentName := fragmentNameFromReference(parent.Ref) + if fragmentName == nil { + continue + } + + // have we already obtained this model, if so skip it + if _, alreadyParsedModel := result.Models[*fragmentName]; alreadyParsedModel { + continue + } + + topLevelModel, err := c.findTopLevelObject(*fragmentName) + if err != nil { + return nil, fmt.Errorf("finding top level model %q for constants: %+v", *fragmentName, err) + } + + nestedResult, err := c.findConstantsWithinModel(*fragmentName, &fieldName, *topLevelModel, result) + if err != nil { + return nil, fmt.Errorf("finding constants within parent model %q: %+v", *fragmentName, err) + } + + if err := result.Append(*nestedResult); err != nil { + return nil, fmt.Errorf("appending nestedResult: %+v", err) + } + } + } + + for propName, propVal := range input.Properties { + logging.Tracef("Processing Property %q..", propName) + // models can contain nested models - either can contain constants, so around we go.. + nestedResult, err := c.findConstantsWithinModel(propName, &fieldName, propVal, result) + if err != nil { + return nil, fmt.Errorf("finding nested constants within %q: %+v", propName, err) + } + if err := result.Append(*nestedResult); err != nil { + return nil, fmt.Errorf("appending nestedResult: %+v", err) + } + } + + if input.AdditionalProperties != nil && input.AdditionalProperties.Schema != nil { + for propName, propVal := range input.AdditionalProperties.Schema.Properties { + logging.Tracef("Processing Additional Property %q..", propName) + // models can contain nested models - either can contain constants, so around we go.. + nestedConstants, err := c.findConstantsWithinModel(propName, &fieldName, propVal, result) + if err != nil { + return nil, fmt.Errorf("finding nested constants within %q: %+v", propName, err) + } + + if err := result.Append(*nestedConstants); err != nil { + return nil, fmt.Errorf("appending nestedResult: %+v", err) + } + } + } + + return &result, nil +} + +func (c *Context) detailsForField(modelName string, propertyName string, value spec.Schema, isRequired bool, known parserModels.ParseResult) (*sdkModels.SDKField, *parserModels.ParseResult, error) { + logging.Tracef("Parsing details for field %q in %q..", propertyName, modelName) + + result := parserModels.ParseResult{ + Constants: map[string]sdkModels.SDKConstant{}, + Models: map[string]sdkModels.SDKModel{}, + } + result.Append(known) + + field := sdkModels.SDKField{ + Required: isRequired, + Optional: !isRequired, //TODO: re-enable readonly && !value.ReadOnly, + ReadOnly: false, // TODO: re-enable readonly value.ReadOnly, + Sensitive: false, // todo: this probably needs to be a predefined list, unless there's something we can parse + JsonName: propertyName, + //Description: value.Description, // TODO: currently causes flapping diff in api definitions, see https://github.com/hashicorp/pandora/issues/3325 + } + + // first get the object definition + parsingModel := false + objectDefinition, nestedResult, err := c.ParseObjectDefinition(modelName, propertyName, &value, result, parsingModel) + if err != nil { + return nil, nil, fmt.Errorf("parsing object definition: %+v", err) + } + if nestedResult != nil { + result.Append(*nestedResult) + } + + // TODO: support for other date formats (RFC3339Nano etc) + // https://github.com/hashicorp/pandora/issues/8 + if objectDefinition.Type == sdkModels.DateTimeSDKObjectDefinitionType { + field.DateFormat = pointer.To(sdkModels.RFC3339SDKDateFormat) + } + + // if there are more than 1 allOf, it can not use a simple reference type, but a new definition + if len(value.Properties) > 0 || len(value.AllOf) > 1 { + // there's a nested model we need to pull out + inlinedName := inlinedModelName(modelName, propertyName) + nestedFields := make(map[string]sdkModels.SDKField, 0) + for propName, propVal := range value.Properties { + nestedFieldRequired := false + for _, field := range value.Required { + if strings.EqualFold(field, propName) { + nestedFieldRequired = true + break + } + } + nestedField, nestedResult, err := c.detailsForField(inlinedName, propName, propVal, nestedFieldRequired, result) + if err != nil { + return nil, nil, fmt.Errorf("parsing inlined model %q: %+v", inlinedName, err) + } + if err := result.Append(*nestedResult); err != nil { + return nil, nil, fmt.Errorf("appending nestedResult: %+v", err) + } + nestedFields[propName] = *nestedField + } + for _, inlinedModel := range value.AllOf { + remoteRef := fragmentNameFromReference(inlinedModel.Ref) + if remoteRef == nil { + // it's possible for the AllOf to just be a description (or contain a Type) + continue + } + + remoteSpec, err := c.findTopLevelObject(*remoteRef) + if err != nil { + return nil, nil, fmt.Errorf("could not find allOf referenced model %q", *remoteRef) + } + + for propName, propVal := range remoteSpec.Properties { + nestedFieldRequired := false + for _, field := range value.Required { + if strings.EqualFold(field, propName) { + nestedFieldRequired = true + break + } + } + nestedField, nestedResult, err := c.detailsForField(inlinedName, propName, propVal, nestedFieldRequired, result) + if err != nil { + return nil, nil, fmt.Errorf("parsing inlined model %q: %+v", inlinedName, err) + } + if err := result.Append(*nestedResult); err != nil { + return nil, nil, fmt.Errorf("appending nestedResult: %+v", err) + } + nestedFields[propName] = *nestedField + } + } + + inlinedModelDetails, err := c.modelDetailsFromObject(inlinedName, value, nestedFields) + if err != nil { + return nil, nil, fmt.Errorf("building model details for inlined model %q: %+v", inlinedName, err) + } + result.Models[inlinedName] = *inlinedModelDetails + // then swap out the reference + objectDefinition.Type = sdkModels.ReferenceSDKObjectDefinitionType + objectDefinition.ReferenceName = pointer.To(cleanup.Title(inlinedName)) + } + + // Custom Types are determined once all the models/constants have been pulled out at the end + // so just assign this for now + field.ObjectDefinition = *objectDefinition + + return &field, &result, err +} + +func (c *Context) fieldsForModel(modelName string, input spec.Schema, known parserModels.ParseResult) (map[string]sdkModels.SDKField, *parserModels.ParseResult, error) { + fields := make(map[string]sdkModels.SDKField, 0) + result := parserModels.ParseResult{ + Constants: map[string]sdkModels.SDKConstant{}, + Models: map[string]sdkModels.SDKModel{}, + } + result.Append(known) + + requiredFields := make(map[string]struct{}, 0) + for _, k := range input.Required { + requiredFields[k] = struct{}{} + } + + // models can inherit from other models, so let's get all of the parent fields here + for i, parent := range input.AllOf { + fragmentName := fragmentNameFromReference(parent.Ref) + if fragmentName == nil { + // sometimes this is bad data rather than a reference, so it should be skipped, example: + // > "allOf": [ + // > { + // > "$ref": "#/definitions/AccessReviewDecisionIdentity" + // > }, + // > { + // > "type": "object", + // > "description": "AccessReviewDecisionUserIdentity" + // > } + // > ], + + // however sometimes these contain actual properties and should be parsed out: + // > "allOf": [ + // > { + // > "$ref": "#/definitions/DigitalTwinsEndpointResourceProperties" + // > }, + // > { + // > "type": "object", + // > "properties": { + // > "TopicEndpoint": { + // > "description": "EventGrid Topic Endpoint", + // > "type": "string" + // > }, + // > "accessKey1": { + // > "x-ms-secret": true, + // > "description": "EventGrid secondary accesskey. Will be obfuscated during reac.", + // > "type": "string", + // > "x-nullable": true + // > }, + // > "accessKey2": { + // > "x-ms-secret": true, + // > "description": "EventGrid secondary accesskey. Will be obfuscated during reac.", + // > "type": "string", + // > "x-nullable": true + // > } + // > } + // > } + // > ] + // > }, + + if parent.Type.Contains("object") { + innerModelName := modelName + if parent.Title != "" { + innerModelName = parent.Title + } + parsedParent, nestedResult, err := c.fieldsForModel(innerModelName, parent, known) + if err != nil { + return nil, nil, fmt.Errorf("parsing fields within allOf model %q (index %d): %+v", innerModelName, i, err) + } + if nestedResult != nil { + if err := result.Append(*nestedResult); err != nil { + return nil, nil, fmt.Errorf("appending nestedResult: %+v", err) + } + } + if parsedParent != nil { + for k, v := range parsedParent { + fields[k] = v + } + } + } + + continue + } + + topLevelObject, err := c.findTopLevelObject(*fragmentName) + if err != nil { + return nil, nil, fmt.Errorf("parsing top level object %q: %+v", *fragmentName, err) + } + for _, k := range topLevelObject.Required { + requiredFields[k] = struct{}{} + } + + nestedFields, nestedResult, err := c.fieldsForModel(*fragmentName, *topLevelObject, result) + if err != nil { + return nil, nil, fmt.Errorf("finding fields for parent model %q: %+v", *fragmentName, err) + } + for k, v := range nestedFields { + isRequired := isFieldRequired(k, requiredFields) + v.Required = isRequired + fields[k] = v + } + if nestedResult != nil { + result.Append(*nestedResult) + } + } + + // then we get the simple thing of iterating over these fields + for propName, propVal := range input.Properties { + isRequired := isFieldRequired(propName, requiredFields) + field, nestedResult, err := c.detailsForField(modelName, propName, propVal, isRequired, result) + if err != nil { + return nil, nil, fmt.Errorf("mapping field %q for %q: %+v", propName, modelName, err) + } + if nestedResult != nil { + if err := result.Append(*nestedResult); err != nil { + return nil, nil, fmt.Errorf("appending nestedResult: %+v", err) + } + } + + // whilst we could look to normalize the name we're intentionally not doing so here + fields[propName] = *field + } + + return fields, &result, nil +} + +func (c *Context) modelDetailsFromObject(modelName string, input spec.Schema, fields map[string]sdkModels.SDKField) (*sdkModels.SDKModel, error) { + details := sdkModels.SDKModel{ + Fields: fields, + } + + // if this is a Parent + if input.Discriminator != "" { + details.FieldNameContainingDiscriminatedValue = &input.Discriminator + + // check that there's at least one implementation of this type - otherwise this this isn't a discriminated type + // but bad data we should ignore + implementations, err := c.findModelNamesWhichImplement(modelName) + if err != nil { + return nil, fmt.Errorf("finding models which implement %q: %+v", modelName, err) + } + hasAtLeastOneImplementation := len(*implementations) > 0 + if !hasAtLeastOneImplementation { + details.FieldNameContainingDiscriminatedValue = nil + } + } + + // look for a potential ancestor + parentTypeName, discriminator, err := c.FindAncestorType(input) + if err != nil { + return nil, fmt.Errorf("finding ancestor type for %q: %+v", modelName, err) + } + if parentTypeName != nil && discriminator != nil { + details.ParentTypeName = parentTypeName + details.FieldNameContainingDiscriminatedValue = discriminator + + // look for the discriminated value, or use the model name + if v, ok := input.Extensions.GetString("x-ms-discriminator-value"); ok { + details.DiscriminatedValue = &v + } else { + details.DiscriminatedValue = pointer.To(modelName) + } + } + + // if there is a discriminated value but no parent type - this is bad data - so we should ignore it + if details.ParentTypeName == nil || details.FieldNameContainingDiscriminatedValue == nil { + details.DiscriminatedValue = nil + } + + return &details, nil +} + +func (c *Context) FindAncestorType(input spec.Schema) (*string, *string, error) { + for _, parentRaw := range input.AllOf { + parentFragmentName := fragmentNameFromReference(parentRaw.Ref) + if parentFragmentName == nil { + continue + } + + parent, err := c.findTopLevelObject(*parentFragmentName) + if err != nil { + return nil, nil, fmt.Errorf("finding top level object %q: %+v", *parentFragmentName, err) + } + + if parent.Discriminator != "" { + return parentFragmentName, &parent.Discriminator, nil + } + + // does the parent itself inherit from something? + if len(parent.AllOf) == 0 { + continue + } + + parentTypeName, discriminator, err := c.FindAncestorType(*parent) + if err != nil { + return nil, nil, fmt.Errorf("finding ancestor type for %q: %+v", *parentFragmentName, err) + } + if parentTypeName != nil && discriminator != nil { + return parentTypeName, discriminator, nil + } + } + return nil, nil, nil +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parsingcontext/parse_object_definition.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parsingcontext/parse_object_definition.go new file mode 100644 index 00000000000..01e92df8e91 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parsingcontext/parse_object_definition.go @@ -0,0 +1,377 @@ +package parsingcontext + +import ( + "fmt" + "strings" + + "github.com/go-openapi/spec" + "github.com/hashicorp/go-azure-helpers/lang/pointer" + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/constants" + parserModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/featureflags" +) + +// ParseObjectDefinition ... TODO +// if `parsingModel` is false, it means the `input` schema cannot be used to parse the model of `modelName` +func (c *Context) ParseObjectDefinition(modelName, propertyName string, input *spec.Schema, known parserModels.ParseResult, parsingModel bool) (*sdkModels.SDKObjectDefinition, *parserModels.ParseResult, error) { + // find the object and any models and constants etc we can find + // however _don't_ look for discriminator implementations - since that should be done when we're completely done + result := parserModels.ParseResult{ + Constants: map[string]sdkModels.SDKConstant{}, + Models: map[string]sdkModels.SDKModel{}, + } + _ = result.Append(known) + + // if it's an enum then parse that out + if len(input.Enum) > 0 { + constant, err := constants.Parse(input.Type, propertyName, &modelName, input.Enum, input.Extensions) + if err != nil { + return nil, nil, fmt.Errorf("parsing constant: %+v", err) + } + result.Constants[constant.Name] = constant.Details + + definition := sdkModels.SDKObjectDefinition{ + Type: sdkModels.ReferenceSDKObjectDefinitionType, + ReferenceName: pointer.To(constant.Name), + } + + //TODO: re-enable min/max/unique + //if input.MaxItems != nil { + // v := int(*input.MaxItems) + // definition.Maximum = &v + //} + //if input.MinItems != nil { + // v := int(*input.MinItems) + // definition.Minimum = &v + //} + //v := input.UniqueItems + //definition.UniqueItems = &v + + return &definition, &result, nil + } + + // if it's a reference to a model, return that + if objectName := fragmentNameFromReference(input.Ref); objectName != nil { + // first find the top level object + topLevelObject, err := c.findTopLevelObject(*objectName) + if err != nil { + return nil, nil, fmt.Errorf("finding top level model %q: %+v", *objectName, err) + } + + knownIncludingPlaceholder := parserModels.ParseResult{ + Constants: map[string]sdkModels.SDKConstant{}, + Models: map[string]sdkModels.SDKModel{}, + } + + if err := knownIncludingPlaceholder.Append(result); err != nil { + return nil, nil, fmt.Errorf("appending nestedResult: %+v", err) + } + if *objectName != "" { + knownIncludingPlaceholder.Models[*objectName] = sdkModels.SDKModel{ + // add a placeholder to avoid circular references + } + } + + // then call ourselves to work out what to do with it + const localParsingModel = true + objectDefinition, nestedResult, err := c.ParseObjectDefinition(*objectName, propertyName, topLevelObject, knownIncludingPlaceholder, localParsingModel) + if err != nil { + return nil, nil, err + } + if nestedResult != nil && *objectName != "" { + delete(nestedResult.Models, *objectName) + } + return objectDefinition, nestedResult, nil + } + + // however we should only do this when we're parsing a model (`parsingModel`) directly rather than when parsing a model from a field - and only if we haven't already parsed this model + if len(input.Properties) > 0 || len(input.AllOf) > 0 { + // special-case: if the model has no properties and inherits from one model + // then just return that object instead, there's no point creating the wrapper type + if len(input.Properties) == 0 && len(input.AllOf) > 0 { + // `AllOf` can contain either a Reference, a model/constant or just a description. + // As such we need to filter out the description-only `AllOf`'s when determining whether the model + // should be replaced by the single type it's referencing. + allOfFields := make([]spec.Schema, 0) + for _, item := range input.AllOf { + fragmentName := fragmentNameFromReference(item.Ref) + if fragmentName == nil && len(item.Type) == 0 && len(item.Properties) == 0 { + continue + } + allOfFields = append(allOfFields, item) + } + if len(allOfFields) == 1 { + inheritedModel := allOfFields[0] + const localParsingModel = true + return c.ParseObjectDefinition(inheritedModel.Title, propertyName, &inheritedModel, result, localParsingModel) + } + } + + // check for / avoid circular references, + // however, we should only do this when we're parsing a model (`parsingModel`) directly rather than when parsing a model from a field - and only if we haven't already parsed this model + if _, ok := result.Models[modelName]; !ok && parsingModel { + nestedResult, err := c.ParseModel(modelName, *input) + if err != nil { + return nil, nil, fmt.Errorf("parsing object from inlined model %q: %+v", modelName, err) + } + if nestedResult == nil { + return nil, nil, fmt.Errorf("parsing object from inlined response model %q: no model returned", modelName) + } + if err := result.Append(*nestedResult); err != nil { + return nil, nil, fmt.Errorf("appending nestedResult: %+v", err) + } + } + + definition := sdkModels.SDKObjectDefinition{ + Type: sdkModels.ReferenceSDKObjectDefinitionType, + ReferenceName: pointer.To(modelName), + } + // TODO: re-enable min/max/unique + //if input.MaxItems != nil { + // v := int(*input.MaxItems) + // definition.Maximum = &v + //} + //if input.MinItems != nil { + // v := int(*input.MinItems) + // definition.Minimum = &v + //} + //v := input.UniqueItems + //definition.UniqueItems = &v + return &definition, &result, nil + } + + if input.AdditionalProperties != nil && input.AdditionalProperties.Schema != nil { + // it'll be a Dictionary, so pull out the nested item and return that + // however we need a name for this model + innerModelName := fmt.Sprintf("%sProperties", propertyName) + if input.AdditionalProperties.Schema.Title != "" { + innerModelName = input.AdditionalProperties.Schema.Title + } + + nestedItem, nestedResult, err := c.ParseObjectDefinition(innerModelName, propertyName, input.AdditionalProperties.Schema, result, true) + if err != nil { + return nil, nil, fmt.Errorf("parsing nested item for dictionary: %+v", err) + } + if nestedItem == nil { + return nil, nil, fmt.Errorf("parsing nested item for dictionary: no nested item returned") + } + if err := result.Append(*nestedResult); err != nil { + return nil, nil, fmt.Errorf("appending nestedResult: %+v", err) + } + return &sdkModels.SDKObjectDefinition{ + Type: sdkModels.DictionarySDKObjectDefinitionType, + NestedItem: nestedItem, + }, &result, nil + } + + if input.Type.Contains("array") && input.Items.Schema != nil { + inlinedName := input.Items.Schema.Title + if inlinedName == "" { + // generate one based on the info we have + inlinedName = fmt.Sprintf("%s%sInlined", cleanup.NormalizeName(modelName), cleanup.NormalizeName(propertyName)) + } + + nestedItem, nestedResult, err := c.ParseObjectDefinition(inlinedName, propertyName, input.Items.Schema, result, true) + if err != nil { + return nil, nil, fmt.Errorf("parsing nested item for array: %+v", err) + } + if nestedItem == nil { + return nil, nil, fmt.Errorf("parsing nested item for array: no nested item returned") + } + + // TODO: re-enable min/max/unique + //if input.MaxItems != nil { + // v := int(*input.MaxItems) + // nestedItem.Maximum = &v + //} + //if input.MinItems != nil { + // v := int(*input.MinItems) + // nestedItem.Minimum = &v + //} + //v := input.UniqueItems + //nestedItem.UniqueItems = &v + + if err := result.Append(*nestedResult); err != nil { + return nil, nil, fmt.Errorf("appending nestedResult: %+v", err) + } + return &sdkModels.SDKObjectDefinition{ + Type: sdkModels.ListSDKObjectDefinitionType, + NestedItem: nestedItem, + }, &result, nil + } + + // Data Factory has a bunch of Custom Types, so we need to check for/handle those + dataFactoryObjectDefinition, parseResult, err := c.parseDataFactoryCustomTypes(input, known) + if err != nil { + return nil, nil, fmt.Errorf("parsing the Data Factory Object Definition: %+v", err) + } + if dataFactoryObjectDefinition != nil { + if parseResult != nil { + if err := result.Append(*parseResult); err != nil { + return nil, nil, fmt.Errorf("appending parseResult: %+v", err) + } + } + return dataFactoryObjectDefinition, &result, nil + } + + // if it's a simple type, there'll be no other objects + if nativeType := c.parseNativeType(input); nativeType != nil { + return nativeType, &result, nil + } + + return nil, nil, fmt.Errorf("unimplemented object definition") +} + +func (c *Context) parseDataFactoryCustomTypes(input *spec.Schema, known parserModels.ParseResult) (*sdkModels.SDKObjectDefinition, *parserModels.ParseResult, error) { + formatVal := "" + if input.Type.Contains("object") { + formatVal, _ = input.Extensions.GetString("x-ms-format") + } + if formatVal == "" { + return nil, nil, nil + } + + // DataFactory has a bunch of CustomTypes, which use `"type": "object" and "x-ms-format"` to describe the type + // as such we need to handle that here + // Simple Types + if strings.EqualFold(formatVal, "dfe-bool") { + return &sdkModels.SDKObjectDefinition{ + Type: sdkModels.BooleanSDKObjectDefinitionType, + }, nil, nil + } + if strings.EqualFold(formatVal, "dfe-double") { + return &sdkModels.SDKObjectDefinition{ + Type: sdkModels.FloatSDKObjectDefinitionType, + }, nil, nil + } + if strings.EqualFold(formatVal, "dfe-key-value-pairs") { + return &sdkModels.SDKObjectDefinition{ + Type: sdkModels.DictionarySDKObjectDefinitionType, + NestedItem: &sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + }, nil, nil + } + if strings.EqualFold(formatVal, "dfe-int") { + return &sdkModels.SDKObjectDefinition{ + Type: sdkModels.IntegerSDKObjectDefinitionType, + }, nil, nil + } + if strings.EqualFold(formatVal, "dfe-string") { + return &sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, nil, nil + } + + // DataFactory has some specific reimplementations of List too.. + if strings.EqualFold(formatVal, "dfe-list-generic") && featureflags.ParseDataFactoryListsOfReferencesAsRegularObjectDefinitionTypes { + // NOTE: it's also possible to have + elementType, ok := input.Extensions.GetString("x-ms-format-element-type") + if !ok { + return nil, nil, fmt.Errorf("when `x-ms-format` is set to `dfe-list-generic` a `x-ms-format-element-type` must be set - but was not found") + } + referencedModel, err := c.findTopLevelObject(elementType) + if err != nil { + return nil, nil, fmt.Errorf("finding the top-level-object %q: %+v", elementType, err) + } + parseResult, err := c.ParseModel(elementType, *referencedModel) + if err != nil { + return nil, nil, fmt.Errorf("parsing the Model %q: %+v", elementType, err) + } + if err := known.Append(*parseResult); err != nil { + return nil, nil, fmt.Errorf("appending `parseResult`: %+v", err) + } + return &sdkModels.SDKObjectDefinition{ + Type: sdkModels.ListSDKObjectDefinitionType, + NestedItem: &sdkModels.SDKObjectDefinition{ + Type: sdkModels.ReferenceSDKObjectDefinitionType, + ReferenceName: pointer.To(cleanup.Title(elementType)), + }, + }, &known, nil + } + + if strings.EqualFold(formatVal, "dfe-list-string") { + return &sdkModels.SDKObjectDefinition{ + Type: sdkModels.ListSDKObjectDefinitionType, + NestedItem: &sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + }, nil, nil + } + + // otherwise let this fall through, since the "least bad" thing to do here is to mark this as an object + + return &sdkModels.SDKObjectDefinition{ + Type: sdkModels.RawObjectSDKObjectDefinitionType, + }, nil, nil +} + +func (c *Context) parseNativeType(input *spec.Schema) *sdkModels.SDKObjectDefinition { + if input == nil { + return nil + } + + if input.Type.Contains("bool") || input.Type.Contains("boolean") { + return &sdkModels.SDKObjectDefinition{ + Type: sdkModels.BooleanSDKObjectDefinitionType, + } + } + + if input.Type.Contains("file") { + return &sdkModels.SDKObjectDefinition{ + Type: sdkModels.RawFileSDKObjectDefinitionType, + } + } + + if input.Type.Contains("integer") { + return &sdkModels.SDKObjectDefinition{ + Type: sdkModels.IntegerSDKObjectDefinitionType, + } + } + + if input.Type.Contains("number") { + return &sdkModels.SDKObjectDefinition{ + Type: sdkModels.FloatSDKObjectDefinitionType, + } + } + + if input.Type.Contains("object") { + if strings.EqualFold(input.Format, "file") { + return &sdkModels.SDKObjectDefinition{ + Type: sdkModels.RawFileSDKObjectDefinitionType, + } + } + + return &sdkModels.SDKObjectDefinition{ + Type: sdkModels.RawObjectSDKObjectDefinitionType, + } + } + + // NOTE: whilst a DateTime _should_ always be Type: String with a Format of DateTime - bad data means + // that this could have no Type value but a Format value, so we have to check this separately. + if strings.EqualFold(input.Format, "date-time") { + // TODO: handle there being a custom format - for now we assume these are all using RFC3339 (#8) + return &sdkModels.SDKObjectDefinition{ + Type: sdkModels.DateTimeSDKObjectDefinitionType, + } + } + + if input.Type.Contains("string") { + // TODO: handle the `format` of `arm-id` (#1289) + return &sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + } + } + + // whilst all fields _should_ have a Type field, it's not guaranteed that they do + // NOTE: this is _intentionally_ not part of the Object comparison above + if len(input.Type) == 0 { + return &sdkModels.SDKObjectDefinition{ + Type: sdkModels.RawObjectSDKObjectDefinitionType, + } + } + + return nil +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parsingcontext/top_level_object.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parsingcontext/top_level_object.go new file mode 100644 index 00000000000..6b2a6e2682a --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parsingcontext/top_level_object.go @@ -0,0 +1,24 @@ +package parsingcontext + +import ( + "fmt" + "strings" + + "github.com/go-openapi/spec" +) + +func (c *Context) findTopLevelObject(name string) (*spec.Schema, error) { + for modelName, model := range c.SwaggerSpecWithReferencesRaw.Definitions { + if strings.EqualFold(modelName, name) { + return &model, nil + } + } + + for modelName, model := range c.SwaggerSpecWithReferencesRaw.Definitions { + if strings.EqualFold(modelName, name) { + return &model, nil + } + } + + return nil, fmt.Errorf("the top level object %q was not found", name) +} diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/architecture.md b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/architecture.md similarity index 100% rename from tools/importer-rest-api-specs/components/parser/resourceids/architecture.md rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/architecture.md diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/common_ids.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/common_ids.go new file mode 100644 index 00000000000..0693d97a3c1 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/common_ids.go @@ -0,0 +1,31 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package resourceids + +import ( + sdkHelpers "github.com/hashicorp/pandora/tools/data-api-sdk/v1/helpers" + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/comparison" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids" +) + +func switchOutCommonResourceIDsAsNeeded(input []sdkModels.ResourceID) []sdkModels.ResourceID { + output := make([]sdkModels.ResourceID, 0) + + for _, value := range input { + // TODO: we should expose a `[]CommonIDs` function from `hashicorp/go-azure-helpers` so that we can reuse these + // the types (intentionally) don't align but we have enough information here to map the data across + for _, commonId := range commonids.CommonIDTypes { + if comparison.ResourceIDsMatch(commonId.ID(), value) { + value = commonId.ID() + value.ExampleValue = sdkHelpers.DisplayValueForResourceID(value) + break + } + } + + output = append(output, value) + } + + return output +} diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_app_service.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_app_service.go similarity index 92% rename from tools/importer-rest-api-specs/components/parser/resourceids/common_id_app_service.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_app_service.go index 5af111eee66..cf061c43d60 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_app_service.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_app_service.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package resourceids +package commonids import ( sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" @@ -11,7 +11,7 @@ var _ commonIdMatcher = commonIdAppService{} type commonIdAppService struct{} -func (c commonIdAppService) id() sdkModels.ResourceID { +func (c commonIdAppService) ID() sdkModels.ResourceID { name := "AppService" return sdkModels.ResourceID{ CommonIDAlias: &name, diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_app_service_environment.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_app_service_environment.go similarity index 92% rename from tools/importer-rest-api-specs/components/parser/resourceids/common_id_app_service_environment.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_app_service_environment.go index ebd4e19c143..250024eab76 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_app_service_environment.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_app_service_environment.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package resourceids +package commonids import ( sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" @@ -11,7 +11,7 @@ var _ commonIdMatcher = commonIdAppServiceEnvironment{} type commonIdAppServiceEnvironment struct{} -func (c commonIdAppServiceEnvironment) id() sdkModels.ResourceID { +func (c commonIdAppServiceEnvironment) ID() sdkModels.ResourceID { name := "AppServiceEnvironment" return sdkModels.ResourceID{ CommonIDAlias: &name, diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_app_service_plan.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_app_service_plan.go similarity index 92% rename from tools/importer-rest-api-specs/components/parser/resourceids/common_id_app_service_plan.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_app_service_plan.go index 29d6ac56ad9..4eef902cf8b 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_app_service_plan.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_app_service_plan.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package resourceids +package commonids import ( sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" @@ -11,7 +11,7 @@ var _ commonIdMatcher = commonIdAppServicePlan{} type commonIdAppServicePlan struct{} -func (c commonIdAppServicePlan) id() sdkModels.ResourceID { +func (c commonIdAppServicePlan) ID() sdkModels.ResourceID { name := "AppServicePlan" return sdkModels.ResourceID{ CommonIDAlias: &name, diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_automation_compilation_job.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_automation_compilation_job.go similarity index 93% rename from tools/importer-rest-api-specs/components/parser/resourceids/common_id_automation_compilation_job.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_automation_compilation_job.go index e5d9cef1e86..a8bc85e78ce 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_automation_compilation_job.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_automation_compilation_job.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package resourceids +package commonids import ( sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" @@ -11,7 +11,7 @@ var _ commonIdMatcher = commonIdAutomationCompilationJob{} type commonIdAutomationCompilationJob struct{} -func (c commonIdAutomationCompilationJob) id() sdkModels.ResourceID { +func (c commonIdAutomationCompilationJob) ID() sdkModels.ResourceID { name := "AutomationCompilationJob" return sdkModels.ResourceID{ CommonIDAlias: &name, diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_availability_set.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_availability_set.go similarity index 92% rename from tools/importer-rest-api-specs/components/parser/resourceids/common_id_availability_set.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_availability_set.go index 6c6a8f6c225..f2390141ad6 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_availability_set.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_availability_set.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package resourceids +package commonids import ( sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" @@ -11,7 +11,7 @@ var _ commonIdMatcher = commonIdAvailabilitySet{} type commonIdAvailabilitySet struct{} -func (c commonIdAvailabilitySet) id() sdkModels.ResourceID { +func (c commonIdAvailabilitySet) ID() sdkModels.ResourceID { name := "AvailabilitySet" return sdkModels.ResourceID{ CommonIDAlias: &name, diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_bot_service.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_bot_service.go similarity index 93% rename from tools/importer-rest-api-specs/components/parser/resourceids/common_id_bot_service.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_bot_service.go index e6544f98ed7..0a535aa094e 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_bot_service.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_bot_service.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package resourceids +package commonids import ( sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" @@ -11,7 +11,7 @@ var _ commonIdMatcher = commonIdBotService{} type commonIdBotService struct{} -func (commonIdBotService) id() sdkModels.ResourceID { +func (commonIdBotService) ID() sdkModels.ResourceID { name := "BotService" return sdkModels.ResourceID{ CommonIDAlias: &name, diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_bot_service_channel.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_bot_service_channel.go similarity index 96% rename from tools/importer-rest-api-specs/components/parser/resourceids/common_id_bot_service_channel.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_bot_service_channel.go index c6113480e6e..a39bb69d046 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_bot_service_channel.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_bot_service_channel.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package resourceids +package commonids import ( sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" @@ -11,7 +11,7 @@ var _ commonIdMatcher = commonIdBotServiceChannel{} type commonIdBotServiceChannel struct{} -func (commonIdBotServiceChannel) id() sdkModels.ResourceID { +func (commonIdBotServiceChannel) ID() sdkModels.ResourceID { name := "BotServiceChannel" return sdkModels.ResourceID{ CommonIDAlias: &name, diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_chaos_studio_capability.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_chaos_studio_capability.go similarity index 91% rename from tools/importer-rest-api-specs/components/parser/resourceids/common_id_chaos_studio_capability.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_chaos_studio_capability.go index 12e7ce3f34d..f0dc9afce90 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_chaos_studio_capability.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_chaos_studio_capability.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package resourceids +package commonids import ( sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" @@ -11,7 +11,7 @@ var _ commonIdMatcher = commonIdChaosStudioCapability{} type commonIdChaosStudioCapability struct{} -func (commonIdChaosStudioCapability) id() sdkModels.ResourceID { +func (commonIdChaosStudioCapability) ID() sdkModels.ResourceID { name := "ChaosStudioCapability" return sdkModels.ResourceID{ CommonIDAlias: &name, diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_chaos_studio_target.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_chaos_studio_target.go similarity index 90% rename from tools/importer-rest-api-specs/components/parser/resourceids/common_id_chaos_studio_target.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_chaos_studio_target.go index 6addeae2948..0754fbf366a 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_chaos_studio_target.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_chaos_studio_target.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package resourceids +package commonids import ( sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" @@ -11,7 +11,7 @@ var _ commonIdMatcher = commonIdChaosStudioTarget{} type commonIdChaosStudioTarget struct{} -func (commonIdChaosStudioTarget) id() sdkModels.ResourceID { +func (commonIdChaosStudioTarget) ID() sdkModels.ResourceID { name := "ChaosStudioTarget" return sdkModels.ResourceID{ CommonIDAlias: &name, diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_cloud_services_ip_configuration.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_cloud_services_ip_configuration.go similarity index 95% rename from tools/importer-rest-api-specs/components/parser/resourceids/common_id_cloud_services_ip_configuration.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_cloud_services_ip_configuration.go index cbe01ef3b78..b20fd6a2733 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_cloud_services_ip_configuration.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_cloud_services_ip_configuration.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package resourceids +package commonids import ( sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" @@ -11,7 +11,7 @@ var _ commonIdMatcher = commonIdCloudServicesIPConfiguration{} type commonIdCloudServicesIPConfiguration struct{} -func (c commonIdCloudServicesIPConfiguration) id() sdkModels.ResourceID { +func (c commonIdCloudServicesIPConfiguration) ID() sdkModels.ResourceID { name := "CloudServicesIPConfiguration" return sdkModels.ResourceID{ CommonIDAlias: &name, diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_cloud_services_public_ip_address.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_cloud_services_public_ip_address.go similarity index 95% rename from tools/importer-rest-api-specs/components/parser/resourceids/common_id_cloud_services_public_ip_address.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_cloud_services_public_ip_address.go index 12f9b6d8094..fbf25b98fc9 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_cloud_services_public_ip_address.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_cloud_services_public_ip_address.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package resourceids +package commonids import ( sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" @@ -11,7 +11,7 @@ var _ commonIdMatcher = commonIdCloudServicesPublicIPAddress{} type commonIdCloudServicesPublicIPAddress struct{} -func (c commonIdCloudServicesPublicIPAddress) id() sdkModels.ResourceID { +func (c commonIdCloudServicesPublicIPAddress) ID() sdkModels.ResourceID { name := "CloudServicesPublicIPAddress" return sdkModels.ResourceID{ CommonIDAlias: &name, diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_dedicated_host.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_dedicated_host.go similarity index 93% rename from tools/importer-rest-api-specs/components/parser/resourceids/common_id_dedicated_host.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_dedicated_host.go index a1f81184bf9..213a3d8448d 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_dedicated_host.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_dedicated_host.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package resourceids +package commonids import ( sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" @@ -11,7 +11,7 @@ var _ commonIdMatcher = commonIdDedicatedHost{} type commonIdDedicatedHost struct{} -func (c commonIdDedicatedHost) id() sdkModels.ResourceID { +func (c commonIdDedicatedHost) ID() sdkModels.ResourceID { name := "DedicatedHost" return sdkModels.ResourceID{ CommonIDAlias: &name, diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_dedicated_host_group.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_dedicated_host_group.go similarity index 92% rename from tools/importer-rest-api-specs/components/parser/resourceids/common_id_dedicated_host_group.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_dedicated_host_group.go index 9926ccb2bc9..916b44a01ca 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_dedicated_host_group.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_dedicated_host_group.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package resourceids +package commonids import ( sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" @@ -11,7 +11,7 @@ var _ commonIdMatcher = commonIdDedicatedHostGroup{} type commonIdDedicatedHostGroup struct{} -func (c commonIdDedicatedHostGroup) id() sdkModels.ResourceID { +func (c commonIdDedicatedHostGroup) ID() sdkModels.ResourceID { name := "DedicatedHostGroup" return sdkModels.ResourceID{ CommonIDAlias: &name, diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_disk_encryption_set.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_disk_encryption_set.go similarity index 92% rename from tools/importer-rest-api-specs/components/parser/resourceids/common_id_disk_encryption_set.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_disk_encryption_set.go index 4193ce50621..a54b7599836 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_disk_encryption_set.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_disk_encryption_set.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package resourceids +package commonids import ( sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" @@ -11,7 +11,7 @@ var _ commonIdMatcher = commonIdDiskEncryptionSet{} type commonIdDiskEncryptionSet struct{} -func (c commonIdDiskEncryptionSet) id() sdkModels.ResourceID { +func (c commonIdDiskEncryptionSet) ID() sdkModels.ResourceID { name := "DiskEncryptionSet" return sdkModels.ResourceID{ CommonIDAlias: &name, diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_expressroute_circuit_peering.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_expressroute_circuit_peering.go similarity index 93% rename from tools/importer-rest-api-specs/components/parser/resourceids/common_id_expressroute_circuit_peering.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_expressroute_circuit_peering.go index 566db8ca475..a4c29e12963 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_expressroute_circuit_peering.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_expressroute_circuit_peering.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package resourceids +package commonids import ( sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" @@ -11,7 +11,7 @@ var _ commonIdMatcher = commonIdExpressRouteCircuitPeering{} type commonIdExpressRouteCircuitPeering struct{} -func (c commonIdExpressRouteCircuitPeering) id() sdkModels.ResourceID { +func (c commonIdExpressRouteCircuitPeering) ID() sdkModels.ResourceID { name := "ExpressRouteCircuitPeering" return sdkModels.ResourceID{ CommonIDAlias: &name, diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_hdinsight_cluster.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_hdinsight_cluster.go similarity index 92% rename from tools/importer-rest-api-specs/components/parser/resourceids/common_id_hdinsight_cluster.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_hdinsight_cluster.go index 0e6bec7a696..22d06be6d4a 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_hdinsight_cluster.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_hdinsight_cluster.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package resourceids +package commonids import ( sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" @@ -11,7 +11,7 @@ var _ commonIdMatcher = commonIdHDInsightCluster{} type commonIdHDInsightCluster struct{} -func (c commonIdHDInsightCluster) id() sdkModels.ResourceID { +func (c commonIdHDInsightCluster) ID() sdkModels.ResourceID { name := "HDInsightCluster" return sdkModels.ResourceID{ CommonIDAlias: &name, diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_hyperv_site_job.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_hyperv_site_job.go similarity index 93% rename from tools/importer-rest-api-specs/components/parser/resourceids/common_id_hyperv_site_job.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_hyperv_site_job.go index d77aa5318ff..eb835544c92 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_hyperv_site_job.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_hyperv_site_job.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package resourceids +package commonids import ( sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" @@ -11,7 +11,7 @@ var _ commonIdMatcher = commonIdHyperVSiteJob{} type commonIdHyperVSiteJob struct{} -func (c commonIdHyperVSiteJob) id() sdkModels.ResourceID { +func (c commonIdHyperVSiteJob) ID() sdkModels.ResourceID { name := "HyperVSiteJob" return sdkModels.ResourceID{ CommonIDAlias: &name, diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_hyperv_site_machine.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_hyperv_site_machine.go similarity index 93% rename from tools/importer-rest-api-specs/components/parser/resourceids/common_id_hyperv_site_machine.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_hyperv_site_machine.go index a461460939d..f0a6f9457f5 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_hyperv_site_machine.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_hyperv_site_machine.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package resourceids +package commonids import ( sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" @@ -11,7 +11,7 @@ var _ commonIdMatcher = commonIdHyperVSiteMachine{} type commonIdHyperVSiteMachine struct{} -func (c commonIdHyperVSiteMachine) id() sdkModels.ResourceID { +func (c commonIdHyperVSiteMachine) ID() sdkModels.ResourceID { name := "HyperVSiteMachine" return sdkModels.ResourceID{ CommonIDAlias: &name, diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_hyperv_site_runasaccount.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_hyperv_site_runasaccount.go similarity index 93% rename from tools/importer-rest-api-specs/components/parser/resourceids/common_id_hyperv_site_runasaccount.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_hyperv_site_runasaccount.go index 855074166ac..49716498a97 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_hyperv_site_runasaccount.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_hyperv_site_runasaccount.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package resourceids +package commonids import ( sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" @@ -11,7 +11,7 @@ var _ commonIdMatcher = commonIdHyperVSiteRunAsAccount{} type commonIdHyperVSiteRunAsAccount struct{} -func (c commonIdHyperVSiteRunAsAccount) id() sdkModels.ResourceID { +func (c commonIdHyperVSiteRunAsAccount) ID() sdkModels.ResourceID { name := "HyperVSiteRunAsAccount" return sdkModels.ResourceID{ CommonIDAlias: &name, diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_key_vault.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_key_vault.go similarity index 93% rename from tools/importer-rest-api-specs/components/parser/resourceids/common_id_key_vault.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_key_vault.go index d793612b76b..72c07044dd5 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_key_vault.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_key_vault.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package resourceids +package commonids import ( sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" @@ -11,7 +11,7 @@ var _ commonIdMatcher = commonIdKeyVault{} type commonIdKeyVault struct{} -func (c commonIdKeyVault) id() sdkModels.ResourceID { +func (c commonIdKeyVault) ID() sdkModels.ResourceID { name := "KeyVault" return sdkModels.ResourceID{ CommonIDAlias: &name, diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_key_vault_key.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_key_vault_key.go similarity index 93% rename from tools/importer-rest-api-specs/components/parser/resourceids/common_id_key_vault_key.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_key_vault_key.go index 8842347ed1d..29ba155c7ba 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_key_vault_key.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_key_vault_key.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package resourceids +package commonids import ( sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" @@ -11,7 +11,7 @@ var _ commonIdMatcher = commonIdKeyVaultKey{} type commonIdKeyVaultKey struct{} -func (c commonIdKeyVaultKey) id() sdkModels.ResourceID { +func (c commonIdKeyVaultKey) ID() sdkModels.ResourceID { name := "KeyVaultKey" return sdkModels.ResourceID{ CommonIDAlias: &name, diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_key_vault_key_version.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_key_vault_key_version.go similarity index 93% rename from tools/importer-rest-api-specs/components/parser/resourceids/common_id_key_vault_key_version.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_key_vault_key_version.go index 6227e79c790..e784832c0cc 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_key_vault_key_version.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_key_vault_key_version.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package resourceids +package commonids import ( sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" @@ -11,7 +11,7 @@ var _ commonIdMatcher = commonIdKeyVaultKeyVersion{} type commonIdKeyVaultKeyVersion struct{} -func (c commonIdKeyVaultKeyVersion) id() sdkModels.ResourceID { +func (c commonIdKeyVaultKeyVersion) ID() sdkModels.ResourceID { name := "KeyVaultKeyVersion" return sdkModels.ResourceID{ CommonIDAlias: &name, diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_key_vault_private_endpoint_connection.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_key_vault_private_endpoint_connection.go similarity index 93% rename from tools/importer-rest-api-specs/components/parser/resourceids/common_id_key_vault_private_endpoint_connection.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_key_vault_private_endpoint_connection.go index 61f9adb3f54..ae56596cfcf 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_key_vault_private_endpoint_connection.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_key_vault_private_endpoint_connection.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package resourceids +package commonids import ( sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" @@ -11,7 +11,7 @@ var _ commonIdMatcher = commonIdKeyVaultPrivateEndpointConnection{} type commonIdKeyVaultPrivateEndpointConnection struct{} -func (c commonIdKeyVaultPrivateEndpointConnection) id() sdkModels.ResourceID { +func (c commonIdKeyVaultPrivateEndpointConnection) ID() sdkModels.ResourceID { name := "KeyVaultPrivateEndpointConnection" return sdkModels.ResourceID{ CommonIDAlias: &name, diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_kubernetes_cluster.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_kubernetes_cluster.go similarity index 92% rename from tools/importer-rest-api-specs/components/parser/resourceids/common_id_kubernetes_cluster.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_kubernetes_cluster.go index 31750bfbe44..f0ff43c5696 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_kubernetes_cluster.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_kubernetes_cluster.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package resourceids +package commonids import ( sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" @@ -11,7 +11,7 @@ var _ commonIdMatcher = commonIdKubernetesCluster{} type commonIdKubernetesCluster struct{} -func (c commonIdKubernetesCluster) id() sdkModels.ResourceID { +func (c commonIdKubernetesCluster) ID() sdkModels.ResourceID { name := "KubernetesCluster" return sdkModels.ResourceID{ CommonIDAlias: &name, diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_kubernetes_fleet.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_kubernetes_fleet.go similarity index 92% rename from tools/importer-rest-api-specs/components/parser/resourceids/common_id_kubernetes_fleet.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_kubernetes_fleet.go index 1ab1d1c0e82..9b201417756 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_kubernetes_fleet.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_kubernetes_fleet.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package resourceids +package commonids import ( "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" @@ -11,7 +11,7 @@ var _ commonIdMatcher = commonIdKubernetesFleet{} type commonIdKubernetesFleet struct{} -func (c commonIdKubernetesFleet) id() models.ResourceID { +func (c commonIdKubernetesFleet) ID() models.ResourceID { name := "KubernetesFleet" return models.ResourceID{ CommonIDAlias: &name, diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_kusto_cluster.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_kusto_cluster.go similarity index 93% rename from tools/importer-rest-api-specs/components/parser/resourceids/common_id_kusto_cluster.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_kusto_cluster.go index ea7db2821d5..a8f525bc284 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_kusto_cluster.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_kusto_cluster.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package resourceids +package commonids import ( sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" @@ -11,7 +11,7 @@ var _ commonIdMatcher = commonIdKustoCluster{} type commonIdKustoCluster struct{} -func (c commonIdKustoCluster) id() sdkModels.ResourceID { +func (c commonIdKustoCluster) ID() sdkModels.ResourceID { name := "KustoCluster" return sdkModels.ResourceID{ CommonIDAlias: &name, diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_kusto_database.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_kusto_database.go similarity index 93% rename from tools/importer-rest-api-specs/components/parser/resourceids/common_id_kusto_database.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_kusto_database.go index 54f12b53f60..d46192b6fa1 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_kusto_database.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_kusto_database.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package resourceids +package commonids import ( sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" @@ -11,7 +11,7 @@ var _ commonIdMatcher = commonIdKustoDatabase{} type commonIdKustoDatabase struct{} -func (c commonIdKustoDatabase) id() sdkModels.ResourceID { +func (c commonIdKustoDatabase) ID() sdkModels.ResourceID { name := "KustoDatabase" return sdkModels.ResourceID{ CommonIDAlias: &name, diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_managed_disk.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_managed_disk.go similarity index 92% rename from tools/importer-rest-api-specs/components/parser/resourceids/common_id_managed_disk.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_managed_disk.go index df6b45a1ced..d8300cf8552 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_managed_disk.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_managed_disk.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package resourceids +package commonids import ( sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" @@ -11,7 +11,7 @@ var _ commonIdMatcher = commonIdManagedDisk{} type commonIdManagedDisk struct{} -func (c commonIdManagedDisk) id() sdkModels.ResourceID { +func (c commonIdManagedDisk) ID() sdkModels.ResourceID { name := "ManagedDisk" return sdkModels.ResourceID{ CommonIDAlias: &name, diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_management_group.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_management_group.go similarity index 89% rename from tools/importer-rest-api-specs/components/parser/resourceids/common_id_management_group.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_management_group.go index 35986c9eace..8f40f3c7bf2 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_management_group.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_management_group.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package resourceids +package commonids import ( sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" @@ -11,7 +11,7 @@ var _ commonIdMatcher = commonIdManagementGroupMatcher{} type commonIdManagementGroupMatcher struct{} -func (commonIdManagementGroupMatcher) id() sdkModels.ResourceID { +func (commonIdManagementGroupMatcher) ID() sdkModels.ResourceID { name := "ManagementGroup" return sdkModels.ResourceID{ CommonIDAlias: &name, diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_network_interface.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_network_interface.go similarity index 92% rename from tools/importer-rest-api-specs/components/parser/resourceids/common_id_network_interface.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_network_interface.go index d577cde5740..c0c9bc1d8fc 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_network_interface.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_network_interface.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package resourceids +package commonids import ( sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" @@ -11,7 +11,7 @@ var _ commonIdMatcher = commonIdNetworkInterface{} type commonIdNetworkInterface struct{} -func (c commonIdNetworkInterface) id() sdkModels.ResourceID { +func (c commonIdNetworkInterface) ID() sdkModels.ResourceID { name := "NetworkInterface" return sdkModels.ResourceID{ CommonIDAlias: &name, diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_network_interface_ip_configuration.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_network_interface_ip_configuration.go similarity index 93% rename from tools/importer-rest-api-specs/components/parser/resourceids/common_id_network_interface_ip_configuration.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_network_interface_ip_configuration.go index ebbf68a3212..a0ac585aca4 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_network_interface_ip_configuration.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_network_interface_ip_configuration.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package resourceids +package commonids import ( sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" @@ -11,7 +11,7 @@ var _ commonIdMatcher = commonIdNetworkInterfaceIPConfiguration{} type commonIdNetworkInterfaceIPConfiguration struct{} -func (c commonIdNetworkInterfaceIPConfiguration) id() sdkModels.ResourceID { +func (c commonIdNetworkInterfaceIPConfiguration) ID() sdkModels.ResourceID { name := "NetworkInterfaceIPConfiguration" return sdkModels.ResourceID{ CommonIDAlias: &name, diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_p2s_vpn_gateway.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_p2s_vpn_gateway.go similarity index 92% rename from tools/importer-rest-api-specs/components/parser/resourceids/common_id_p2s_vpn_gateway.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_p2s_vpn_gateway.go index dd903262d52..74b4d89cab9 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_p2s_vpn_gateway.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_p2s_vpn_gateway.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package resourceids +package commonids import ( sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" @@ -11,7 +11,7 @@ var _ commonIdMatcher = commonIdP2sVPNGateway{} type commonIdP2sVPNGateway struct{} -func (c commonIdP2sVPNGateway) id() sdkModels.ResourceID { +func (c commonIdP2sVPNGateway) ID() sdkModels.ResourceID { name := "P2sVPNGateway" return sdkModels.ResourceID{ CommonIDAlias: &name, diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_provisioning_service_id.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_provisioning_service_id.go similarity index 92% rename from tools/importer-rest-api-specs/components/parser/resourceids/common_id_provisioning_service_id.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_provisioning_service_id.go index d26454f948f..096340b0a47 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_provisioning_service_id.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_provisioning_service_id.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package resourceids +package commonids import ( sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" @@ -11,7 +11,7 @@ var _ commonIdMatcher = commonIdProvisioningService{} type commonIdProvisioningService struct{} -func (c commonIdProvisioningService) id() sdkModels.ResourceID { +func (c commonIdProvisioningService) ID() sdkModels.ResourceID { name := "ProvisioningService" return sdkModels.ResourceID{ CommonIDAlias: &name, diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_public_ip_address.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_public_ip_address.go similarity index 92% rename from tools/importer-rest-api-specs/components/parser/resourceids/common_id_public_ip_address.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_public_ip_address.go index ef11273ac4f..25df754d2aa 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_public_ip_address.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_public_ip_address.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package resourceids +package commonids import ( sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" @@ -11,7 +11,7 @@ var _ commonIdMatcher = commonIdPublicIPAddress{} type commonIdPublicIPAddress struct{} -func (c commonIdPublicIPAddress) id() sdkModels.ResourceID { +func (c commonIdPublicIPAddress) ID() sdkModels.ResourceID { name := "PublicIPAddress" return sdkModels.ResourceID{ CommonIDAlias: &name, diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_resource_group.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_resource_group.go similarity index 89% rename from tools/importer-rest-api-specs/components/parser/resourceids/common_id_resource_group.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_resource_group.go index e566bd83b75..7a76b0357b6 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_resource_group.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_resource_group.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package resourceids +package commonids import ( sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" @@ -11,7 +11,7 @@ var _ commonIdMatcher = commonIdResourceGroupMatcher{} type commonIdResourceGroupMatcher struct{} -func (commonIdResourceGroupMatcher) id() sdkModels.ResourceID { +func (commonIdResourceGroupMatcher) ID() sdkModels.ResourceID { name := "ResourceGroup" return sdkModels.ResourceID{ CommonIDAlias: &name, diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_scope.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_scope.go similarity index 85% rename from tools/importer-rest-api-specs/components/parser/resourceids/common_id_scope.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_scope.go index 2c3d5d71feb..7c5c4b19f4e 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_scope.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_scope.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package resourceids +package commonids import ( sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" @@ -11,7 +11,7 @@ var _ commonIdMatcher = commonIdScopeMatcher{} type commonIdScopeMatcher struct{} -func (commonIdScopeMatcher) id() sdkModels.ResourceID { +func (commonIdScopeMatcher) ID() sdkModels.ResourceID { name := "Scope" return sdkModels.ResourceID{ CommonIDAlias: &name, diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_shared_image_gallery.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_shared_image_gallery.go similarity index 92% rename from tools/importer-rest-api-specs/components/parser/resourceids/common_id_shared_image_gallery.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_shared_image_gallery.go index 91c776f1de4..484adf7ca9c 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_shared_image_gallery.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_shared_image_gallery.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package resourceids +package commonids import ( sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" @@ -11,7 +11,7 @@ var _ commonIdMatcher = commonIdSharedImageGallery{} type commonIdSharedImageGallery struct{} -func (c commonIdSharedImageGallery) id() sdkModels.ResourceID { +func (c commonIdSharedImageGallery) ID() sdkModels.ResourceID { name := "SharedImageGallery" return sdkModels.ResourceID{ CommonIDAlias: &name, diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_spring_cloud_service.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_spring_cloud_service.go similarity index 92% rename from tools/importer-rest-api-specs/components/parser/resourceids/common_id_spring_cloud_service.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_spring_cloud_service.go index db5280ed1cd..4fe39fcf758 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_spring_cloud_service.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_spring_cloud_service.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package resourceids +package commonids import ( sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" @@ -11,7 +11,7 @@ var _ commonIdMatcher = commonIdSpringCloudService{} type commonIdSpringCloudService struct{} -func (c commonIdSpringCloudService) id() sdkModels.ResourceID { +func (c commonIdSpringCloudService) ID() sdkModels.ResourceID { name := "SpringCloudService" return sdkModels.ResourceID{ CommonIDAlias: &name, diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_sql_database.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_sql_database.go similarity index 93% rename from tools/importer-rest-api-specs/components/parser/resourceids/common_id_sql_database.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_sql_database.go index ded3cac3fe7..557e43c07cf 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_sql_database.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_sql_database.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package resourceids +package commonids import ( sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" @@ -11,7 +11,7 @@ var _ commonIdMatcher = commonIdSqlDatabase{} type commonIdSqlDatabase struct{} -func (c commonIdSqlDatabase) id() sdkModels.ResourceID { +func (c commonIdSqlDatabase) ID() sdkModels.ResourceID { name := "SqlDatabase" return sdkModels.ResourceID{ CommonIDAlias: &name, diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_sql_elastic_pool.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_sql_elastic_pool.go similarity index 93% rename from tools/importer-rest-api-specs/components/parser/resourceids/common_id_sql_elastic_pool.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_sql_elastic_pool.go index 4de0125bbe4..765bb576640 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_sql_elastic_pool.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_sql_elastic_pool.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package resourceids +package commonids import ( sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" @@ -11,7 +11,7 @@ var _ commonIdMatcher = commonIdSqlElasticPool{} type commonIdSqlElasticPool struct{} -func (c commonIdSqlElasticPool) id() sdkModels.ResourceID { +func (c commonIdSqlElasticPool) ID() sdkModels.ResourceID { name := "SqlElasticPool" return sdkModels.ResourceID{ CommonIDAlias: &name, diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_sql_managed_instance.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_sql_managed_instance.go similarity index 92% rename from tools/importer-rest-api-specs/components/parser/resourceids/common_id_sql_managed_instance.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_sql_managed_instance.go index 942841eeefd..075d800cfd9 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_sql_managed_instance.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_sql_managed_instance.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package resourceids +package commonids import ( sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" @@ -11,7 +11,7 @@ var _ commonIdMatcher = commonIdSqlManagedInstance{} type commonIdSqlManagedInstance struct{} -func (c commonIdSqlManagedInstance) id() sdkModels.ResourceID { +func (c commonIdSqlManagedInstance) ID() sdkModels.ResourceID { name := "SqlManagedInstance" return sdkModels.ResourceID{ CommonIDAlias: &name, diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_sql_managed_instance_database.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_sql_managed_instance_database.go similarity index 93% rename from tools/importer-rest-api-specs/components/parser/resourceids/common_id_sql_managed_instance_database.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_sql_managed_instance_database.go index a8fa42138ba..239958cee37 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_sql_managed_instance_database.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_sql_managed_instance_database.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package resourceids +package commonids import ( sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" @@ -11,7 +11,7 @@ var _ commonIdMatcher = commonIdSqlManagedInstanceDatabase{} type commonIdSqlManagedInstanceDatabase struct{} -func (c commonIdSqlManagedInstanceDatabase) id() sdkModels.ResourceID { +func (c commonIdSqlManagedInstanceDatabase) ID() sdkModels.ResourceID { name := "SqlManagedInstanceDatabase" return sdkModels.ResourceID{ CommonIDAlias: &name, diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_sql_server.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_sql_server.go similarity index 92% rename from tools/importer-rest-api-specs/components/parser/resourceids/common_id_sql_server.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_sql_server.go index 00d62f60b1c..9f93a1855df 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_sql_server.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_sql_server.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package resourceids +package commonids import ( sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" @@ -11,7 +11,7 @@ var _ commonIdMatcher = commonIdSqlServer{} type commonIdSqlServer struct{} -func (c commonIdSqlServer) id() sdkModels.ResourceID { +func (c commonIdSqlServer) ID() sdkModels.ResourceID { name := "SqlServer" return sdkModels.ResourceID{ CommonIDAlias: &name, diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_storage_account.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_storage_account.go similarity index 92% rename from tools/importer-rest-api-specs/components/parser/resourceids/common_id_storage_account.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_storage_account.go index 54b6d71327a..673649102d5 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_storage_account.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_storage_account.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package resourceids +package commonids import ( sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" @@ -11,7 +11,7 @@ var _ commonIdMatcher = commonIdStorageAccount{} type commonIdStorageAccount struct{} -func (c commonIdStorageAccount) id() sdkModels.ResourceID { +func (c commonIdStorageAccount) ID() sdkModels.ResourceID { name := "StorageAccount" return sdkModels.ResourceID{ CommonIDAlias: &name, diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_storage_container.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_storage_container.go similarity index 94% rename from tools/importer-rest-api-specs/components/parser/resourceids/common_id_storage_container.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_storage_container.go index 83b1a287840..12e3c6dc347 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_storage_container.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_storage_container.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package resourceids +package commonids import ( sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" @@ -11,7 +11,7 @@ var _ commonIdMatcher = commonIdStorageContainer{} type commonIdStorageContainer struct{} -func (c commonIdStorageContainer) id() sdkModels.ResourceID { +func (c commonIdStorageContainer) ID() sdkModels.ResourceID { name := "StorageContainer" return sdkModels.ResourceID{ CommonIDAlias: &name, diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_subnet.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_subnet.go similarity index 94% rename from tools/importer-rest-api-specs/components/parser/resourceids/common_id_subnet.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_subnet.go index cd02923821e..887b7c81a3e 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_subnet.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_subnet.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package resourceids +package commonids import ( sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" @@ -11,7 +11,7 @@ var _ commonIdMatcher = commonIdSubnet{} type commonIdSubnet struct{} -func (c commonIdSubnet) id() sdkModels.ResourceID { +func (c commonIdSubnet) ID() sdkModels.ResourceID { name := "Subnet" return sdkModels.ResourceID{ CommonIDAlias: &name, diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_subscription.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_subscription.go similarity index 87% rename from tools/importer-rest-api-specs/components/parser/resourceids/common_id_subscription.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_subscription.go index 95fb9d45488..65904ad292d 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_subscription.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_subscription.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package resourceids +package commonids import ( sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" @@ -11,7 +11,7 @@ var _ commonIdMatcher = commonIdSubscriptionMatcher{} type commonIdSubscriptionMatcher struct{} -func (commonIdSubscriptionMatcher) id() sdkModels.ResourceID { +func (commonIdSubscriptionMatcher) ID() sdkModels.ResourceID { name := "Subscription" return sdkModels.ResourceID{ CommonIDAlias: &name, diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_user_assigned_identity.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_user_assigned_identity.go similarity index 92% rename from tools/importer-rest-api-specs/components/parser/resourceids/common_id_user_assigned_identity.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_user_assigned_identity.go index 324560711a9..5d4361d1be2 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_user_assigned_identity.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_user_assigned_identity.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package resourceids +package commonids import ( sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" @@ -11,7 +11,7 @@ var _ commonIdMatcher = commonIdUserAssignedIdentity{} type commonIdUserAssignedIdentity struct{} -func (commonIdUserAssignedIdentity) id() sdkModels.ResourceID { +func (commonIdUserAssignedIdentity) ID() sdkModels.ResourceID { name := "UserAssignedIdentity" return sdkModels.ResourceID{ CommonIDAlias: &name, diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_virtual_hub_bgp_connection.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_virtual_hub_bgp_connection.go similarity index 93% rename from tools/importer-rest-api-specs/components/parser/resourceids/common_id_virtual_hub_bgp_connection.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_virtual_hub_bgp_connection.go index 852504ffe8d..13ab1dc1695 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_virtual_hub_bgp_connection.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_virtual_hub_bgp_connection.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package resourceids +package commonids import sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" @@ -9,7 +9,7 @@ var _ commonIdMatcher = commonIdVirtualHubBGPConnection{} type commonIdVirtualHubBGPConnection struct{} -func (c commonIdVirtualHubBGPConnection) id() sdkModels.ResourceID { +func (c commonIdVirtualHubBGPConnection) ID() sdkModels.ResourceID { name := "VirtualHubBGPConnection" return sdkModels.ResourceID{ CommonIDAlias: &name, diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_virtual_machine_scale_set_ip_configuration.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_virtual_machine_scale_set_ip_configuration.go similarity index 95% rename from tools/importer-rest-api-specs/components/parser/resourceids/common_id_virtual_machine_scale_set_ip_configuration.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_virtual_machine_scale_set_ip_configuration.go index e206d18dab6..583d0b91752 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_virtual_machine_scale_set_ip_configuration.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_virtual_machine_scale_set_ip_configuration.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package resourceids +package commonids import sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" @@ -9,7 +9,7 @@ var _ commonIdMatcher = commonIdVirtualMachineScaleSetIPConfiguration{} type commonIdVirtualMachineScaleSetIPConfiguration struct{} -func (c commonIdVirtualMachineScaleSetIPConfiguration) id() sdkModels.ResourceID { +func (c commonIdVirtualMachineScaleSetIPConfiguration) ID() sdkModels.ResourceID { name := "VirtualMachineScaleSetIPConfiguration" return sdkModels.ResourceID{ CommonIDAlias: &name, diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_virtual_machine_scale_set_network_interface.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_virtual_machine_scale_set_network_interface.go similarity index 94% rename from tools/importer-rest-api-specs/components/parser/resourceids/common_id_virtual_machine_scale_set_network_interface.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_virtual_machine_scale_set_network_interface.go index 7ee3e7ae4aa..d506b43bcc7 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_virtual_machine_scale_set_network_interface.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_virtual_machine_scale_set_network_interface.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package resourceids +package commonids import sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" @@ -9,7 +9,7 @@ var _ commonIdMatcher = commonIdVirtualMachineScaleSetNetworkInterface{} type commonIdVirtualMachineScaleSetNetworkInterface struct{} -func (c commonIdVirtualMachineScaleSetNetworkInterface) id() sdkModels.ResourceID { +func (c commonIdVirtualMachineScaleSetNetworkInterface) ID() sdkModels.ResourceID { name := "VirtualMachineScaleSetNetworkInterface" return sdkModels.ResourceID{ CommonIDAlias: &name, diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_virtual_machine_scale_set_public_ip_address.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_virtual_machine_scale_set_public_ip_address.go similarity index 95% rename from tools/importer-rest-api-specs/components/parser/resourceids/common_id_virtual_machine_scale_set_public_ip_address.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_virtual_machine_scale_set_public_ip_address.go index 34cf9fd26d3..51d6693787c 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_virtual_machine_scale_set_public_ip_address.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_virtual_machine_scale_set_public_ip_address.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package resourceids +package commonids import sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" @@ -9,7 +9,7 @@ var _ commonIdMatcher = commonIdVirtualMachineScaleSetPublicIPAddress{} type commonIdVirtualMachineScaleSetPublicIPAddress struct{} -func (c commonIdVirtualMachineScaleSetPublicIPAddress) id() sdkModels.ResourceID { +func (c commonIdVirtualMachineScaleSetPublicIPAddress) ID() sdkModels.ResourceID { name := "VirtualMachineScaleSetPublicIPAddress" return sdkModels.ResourceID{ CommonIDAlias: &name, diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_virtual_network.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_virtual_network.go similarity index 92% rename from tools/importer-rest-api-specs/components/parser/resourceids/common_id_virtual_network.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_virtual_network.go index a34192ae0fd..a0214089f5a 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_virtual_network.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_virtual_network.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package resourceids +package commonids import sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" @@ -9,7 +9,7 @@ var _ commonIdMatcher = commonIdVirtualNetwork{} type commonIdVirtualNetwork struct{} -func (c commonIdVirtualNetwork) id() sdkModels.ResourceID { +func (c commonIdVirtualNetwork) ID() sdkModels.ResourceID { name := "VirtualNetwork" return sdkModels.ResourceID{ CommonIDAlias: &name, diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_virtual_router_peering.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_virtual_router_peering.go similarity index 93% rename from tools/importer-rest-api-specs/components/parser/resourceids/common_id_virtual_router_peering.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_virtual_router_peering.go index eb7efd709fa..25d689fc66f 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_virtual_router_peering.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_virtual_router_peering.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package resourceids +package commonids import sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" @@ -9,7 +9,7 @@ var _ commonIdMatcher = commonIdVirtualRouterPeering{} type commonIdVirtualRouterPeering struct{} -func (c commonIdVirtualRouterPeering) id() sdkModels.ResourceID { +func (c commonIdVirtualRouterPeering) ID() sdkModels.ResourceID { name := "VirtualRouterPeering" return sdkModels.ResourceID{ CommonIDAlias: &name, diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_virtual_wan_p2s_vpn_gateway.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_virtual_wan_p2s_vpn_gateway.go similarity index 92% rename from tools/importer-rest-api-specs/components/parser/resourceids/common_id_virtual_wan_p2s_vpn_gateway.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_virtual_wan_p2s_vpn_gateway.go index 385ea400e71..1617115ceb4 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_virtual_wan_p2s_vpn_gateway.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_virtual_wan_p2s_vpn_gateway.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package resourceids +package commonids import sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" @@ -9,7 +9,7 @@ var _ commonIdMatcher = commonIdVirtualWANP2SVPNGateway{} type commonIdVirtualWANP2SVPNGateway struct{} -func (c commonIdVirtualWANP2SVPNGateway) id() sdkModels.ResourceID { +func (c commonIdVirtualWANP2SVPNGateway) ID() sdkModels.ResourceID { name := "VirtualWANP2SVPNGateway" return sdkModels.ResourceID{ CommonIDAlias: &name, diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_virtualhub_ip_configuration.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_virtualhub_ip_configuration.go similarity index 93% rename from tools/importer-rest-api-specs/components/parser/resourceids/common_id_virtualhub_ip_configuration.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_virtualhub_ip_configuration.go index 4affc790bb9..bb56b0e3eb4 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_virtualhub_ip_configuration.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_virtualhub_ip_configuration.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package resourceids +package commonids import sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" @@ -9,7 +9,7 @@ var _ commonIdMatcher = commonIdVirtualHubIPConfiguration{} type commonIdVirtualHubIPConfiguration struct{} -func (c commonIdVirtualHubIPConfiguration) id() sdkModels.ResourceID { +func (c commonIdVirtualHubIPConfiguration) ID() sdkModels.ResourceID { name := "VirtualHubIPConfiguration" return sdkModels.ResourceID{ CommonIDAlias: &name, diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_vmware_site_job.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_vmware_site_job.go similarity index 93% rename from tools/importer-rest-api-specs/components/parser/resourceids/common_id_vmware_site_job.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_vmware_site_job.go index d9f33e8fdc7..7c538c26ff8 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_vmware_site_job.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_vmware_site_job.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package resourceids +package commonids import sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" @@ -10,7 +10,7 @@ var _ commonIdMatcher = commonIdVMwareSiteJob{} type commonIdVMwareSiteJob struct { } -func (c commonIdVMwareSiteJob) id() sdkModels.ResourceID { +func (c commonIdVMwareSiteJob) ID() sdkModels.ResourceID { name := "VMwareSiteJob" return sdkModels.ResourceID{ CommonIDAlias: &name, diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_vmware_site_machine.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_vmware_site_machine.go similarity index 93% rename from tools/importer-rest-api-specs/components/parser/resourceids/common_id_vmware_site_machine.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_vmware_site_machine.go index 608f8c33362..7120fd38695 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_vmware_site_machine.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_vmware_site_machine.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package resourceids +package commonids import sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" @@ -10,7 +10,7 @@ var _ commonIdMatcher = commonIdVMwareSiteMachine{} type commonIdVMwareSiteMachine struct { } -func (c commonIdVMwareSiteMachine) id() sdkModels.ResourceID { +func (c commonIdVMwareSiteMachine) ID() sdkModels.ResourceID { name := "VMwareSiteMachine" return sdkModels.ResourceID{ CommonIDAlias: &name, diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_vmware_site_runasaccount.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_vmware_site_runasaccount.go similarity index 93% rename from tools/importer-rest-api-specs/components/parser/resourceids/common_id_vmware_site_runasaccount.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_vmware_site_runasaccount.go index 6aa7ea70a35..ac4e4cc8cb8 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_vmware_site_runasaccount.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_vmware_site_runasaccount.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package resourceids +package commonids import sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" @@ -10,7 +10,7 @@ var _ commonIdMatcher = commonIdVMwareSiteRunAsAccount{} type commonIdVMwareSiteRunAsAccount struct { } -func (c commonIdVMwareSiteRunAsAccount) id() sdkModels.ResourceID { +func (c commonIdVMwareSiteRunAsAccount) ID() sdkModels.ResourceID { name := "VMwareSiteRunAsAccount" return sdkModels.ResourceID{ CommonIDAlias: &name, diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_vpn_gateway_vpn_connection.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_vpn_gateway_vpn_connection.go similarity index 93% rename from tools/importer-rest-api-specs/components/parser/resourceids/common_id_vpn_gateway_vpn_connection.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_vpn_gateway_vpn_connection.go index ea4d3b4f49a..3e60c32ad44 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_vpn_gateway_vpn_connection.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_vpn_gateway_vpn_connection.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package resourceids +package commonids import sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" @@ -9,7 +9,7 @@ var _ commonIdMatcher = commonIdVPNConnection{} type commonIdVPNConnection struct{} -func (c commonIdVPNConnection) id() sdkModels.ResourceID { +func (c commonIdVPNConnection) ID() sdkModels.ResourceID { name := "VPNConnection" return sdkModels.ResourceID{ CommonIDAlias: &name, diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_ids.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_ids.go similarity index 70% rename from tools/importer-rest-api-specs/components/parser/resourceids/common_ids.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_ids.go index 4dd513b4f44..81eef950073 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_ids.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_ids.go @@ -1,19 +1,6 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 +package commonids -package resourceids - -import ( - "github.com/hashicorp/pandora/tools/data-api-sdk/v1/helpers" - sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" -) - -type commonIdMatcher interface { - // id returns the Resource ID for this Common ID - id() sdkModels.ResourceID -} - -var commonIdTypes = []commonIdMatcher{ +var CommonIDTypes = []commonIdMatcher{ commonIdManagementGroupMatcher{}, commonIdResourceGroupMatcher{}, commonIdSubscriptionMatcher{}, @@ -106,23 +93,3 @@ var commonIdTypes = []commonIdMatcher{ commonIdAppServiceEnvironment{}, commonIdAppServicePlan{}, } - -func switchOutCommonResourceIDsAsNeeded(input []sdkModels.ResourceID) []sdkModels.ResourceID { - output := make([]sdkModels.ResourceID, 0) - - for _, value := range input { - // TODO: we should expose a `[]CommonIDs` function from `hashicorp/go-azure-helpers` so that we can reuse these - // the types (intentionally) don't align but we have enough information here to map the data across - for _, commonId := range commonIdTypes { - if ResourceIdsMatch(commonId.id(), value) { - value = commonId.id() - value.ExampleValue = helpers.DisplayValueForResourceID(value) - break - } - } - - output = append(output, value) - } - - return output -} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/interface.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/interface.go new file mode 100644 index 00000000000..3966f3cdd6d --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/interface.go @@ -0,0 +1,8 @@ +package commonids + +import sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" + +type commonIdMatcher interface { + // ID returns the Resource ID for this Common ID + ID() sdkModels.ResourceID +} diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/distinct_resource_ids.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/distinct_resource_ids.go similarity index 79% rename from tools/importer-rest-api-specs/components/parser/resourceids/distinct_resource_ids.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/distinct_resource_ids.go index e7d48fd93e1..661bcf731d4 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/distinct_resource_ids.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/distinct_resource_ids.go @@ -6,8 +6,9 @@ package resourceids import ( "sort" - "github.com/hashicorp/pandora/tools/data-api-sdk/v1/helpers" + sdkHelpers "github.com/hashicorp/pandora/tools/data-api-sdk/v1/helpers" sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/comparison" ) func (p *Parser) distinctResourceIds(input map[string]processedResourceId) ([]sdkModels.ResourceID, map[string]sdkModels.SDKConstant) { @@ -36,11 +37,11 @@ func (p *Parser) distinctResourceIds(input map[string]processedResourceId) ([]sd ConstantNames: constantNames, Segments: *operation.segments, } - item.ExampleValue = helpers.DisplayValueForResourceID(item) + item.ExampleValue = sdkHelpers.DisplayValueForResourceID(item) matchFound := false for _, existing := range out { - if ResourceIdsMatch(item, existing) { + if comparison.ResourceIDsMatch(item, existing) { matchFound = true break } diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/generate_names.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/generate_names.go similarity index 84% rename from tools/importer-rest-api-specs/components/parser/resourceids/generate_names.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/generate_names.go index f4180792b27..1c22327a914 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/generate_names.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/generate_names.go @@ -8,17 +8,19 @@ import ( "sort" "strings" - "github.com/hashicorp/pandora/tools/data-api-sdk/v1/helpers" + sdkHelpers "github.com/hashicorp/pandora/tools/data-api-sdk/v1/helpers" sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/components/parser/cleanup" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/comparison" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids" ) -func (p *Parser) generateNamesForResourceIds(input []sdkModels.ResourceID, uriToResourceId map[string]ParsedOperation) (*map[string]sdkModels.ResourceID, error) { +func (p *Parser) generateNamesForResourceIds(input []sdkModels.ResourceID, uriToResourceId map[string]ParsedOperation) (map[string]sdkModels.ResourceID, error) { return generateNamesForResourceIds(input, uriToResourceId) } -func generateNamesForResourceIds(input []sdkModels.ResourceID, uriToResourceId map[string]ParsedOperation) (*map[string]sdkModels.ResourceID, error) { - // now that we have all of the Resource ID's, we then need to go through and determine Unique ID's for those +func generateNamesForResourceIds(input []sdkModels.ResourceID, uriToResourceId map[string]ParsedOperation) (map[string]sdkModels.ResourceID, error) { + // now that we have all the Resource ID's, we then need to go through and determine Unique ID's for those // we need all of them here to avoid conflicts, e.g. AuthorizationRule which can be a NamespaceAuthorizationRule // or an EventHubAuthorizationRule, but is named AuthorizationRule in both @@ -47,9 +49,9 @@ func generateNamesForResourceIds(input []sdkModels.ResourceID, uriToResourceId m urisThatAreCommonIds := make(map[string]struct{}) for _, uri := range sortedUris { resourceId := uniqueUris[uri] - for i, commonIdType := range commonIdTypes { - commonId := commonIdType.id() - if ResourceIdsMatch(commonId, resourceId) { + for i, commonIdType := range commonids.CommonIDTypes { + commonId := commonIdType.ID() + if comparison.ResourceIDsMatch(commonId, resourceId) { if commonId.CommonIDAlias == nil { return nil, fmt.Errorf("the Common ID %d had no Alias: %+v", i, commonId) } @@ -106,13 +108,13 @@ func generateNamesForResourceIds(input []sdkModels.ResourceID, uriToResourceId m if err != nil { uris := make([]string, 0) for _, uri := range conflictingUris { - uris = append(uris, helpers.DisplayValueForResourceID(uri)) + uris = append(uris, sdkHelpers.DisplayValueForResourceID(uri)) } return nil, fmt.Errorf("determining unique names for conflicting uri's %q: %+v", strings.Join(uris, " | "), err) } - for k, v := range *uniqueNames { + for k, v := range uniqueNames { conflictUniqueNames[k] = struct{}{} candidateNamesToUris[k] = v } @@ -126,7 +128,7 @@ func generateNamesForResourceIds(input []sdkModels.ResourceID, uriToResourceId m if _, ok := conflictUniqueNames[k]; ok { for idx, v2 := range uriToResourceId { - if v2.ResourceId != nil && ResourceIdsMatch(*v2.ResourceId, v) && (v2.ResourceIdName != nil && *v2.ResourceIdName != key) { + if v2.ResourceId != nil && comparison.ResourceIDsMatch(*v2.ResourceId, v) && (v2.ResourceIdName != nil && *v2.ResourceIdName != key) { v2.ResourceIdName = &key uriToResourceId[idx] = v2 } @@ -134,10 +136,10 @@ func generateNamesForResourceIds(input []sdkModels.ResourceID, uriToResourceId m } } - return &outputNamesToUris, nil + return outputNamesToUris, nil } -func determineUniqueNamesFor(conflictingUris []sdkModels.ResourceID, existingCandidateNames map[string]sdkModels.ResourceID) (*map[string]sdkModels.ResourceID, error) { +func determineUniqueNamesFor(conflictingUris []sdkModels.ResourceID, existingCandidateNames map[string]sdkModels.ResourceID) (map[string]sdkModels.ResourceID, error) { proposedNames := make(map[string]sdkModels.ResourceID) for _, resourceId := range conflictingUris { availableSegments := SegmentsAvailableForNaming(resourceId) @@ -155,7 +157,7 @@ func determineUniqueNamesFor(conflictingUris []sdkModels.ResourceID, existingCan uri, hasConflictWithExisting := existingCandidateNames[proposedName] if hasConflictWithExisting { - if ResourceIdsMatch(uri, resourceId) { + if comparison.ResourceIDsMatch(uri, resourceId) { // it's this ID from a different type hasConflictWithExisting = false } @@ -163,7 +165,7 @@ func determineUniqueNamesFor(conflictingUris []sdkModels.ResourceID, existingCan uri, hasConflictWithProposed := proposedNames[proposedName] if hasConflictWithProposed { - if ResourceIdsMatch(uri, resourceId) { + if comparison.ResourceIDsMatch(uri, resourceId) { // it's this ID from a different type hasConflictWithProposed = false } @@ -181,13 +183,13 @@ func determineUniqueNamesFor(conflictingUris []sdkModels.ResourceID, existingCan conflictingUri, hasConflict = proposedNames[proposedName] } - return nil, fmt.Errorf("not enough segments in %q to determine a unique name - conflicts with %q", helpers.DisplayValueForResourceID(resourceId), helpers.DisplayValueForResourceID(conflictingUri)) + return nil, fmt.Errorf("not enough segments in %q to determine a unique name - conflicts with %q", sdkHelpers.DisplayValueForResourceID(resourceId), sdkHelpers.DisplayValueForResourceID(conflictingUri)) } proposedNames[proposedName] = resourceId } - return &proposedNames, nil + return proposedNames, nil } func SegmentsAvailableForNaming(pri sdkModels.ResourceID) []string { diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/generate_names_test.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/generate_names_test.go similarity index 91% rename from tools/importer-rest-api-specs/components/parser/resourceids/generate_names_test.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/generate_names_test.go index 07bb78ab198..07ff760a30f 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/generate_names_test.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/generate_names_test.go @@ -8,7 +8,7 @@ import ( "testing" "github.com/hashicorp/go-azure-helpers/lang/pointer" - "github.com/hashicorp/pandora/tools/data-api-sdk/v1/helpers" + sdkHelpers "github.com/hashicorp/pandora/tools/data-api-sdk/v1/helpers" sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" ) @@ -157,8 +157,8 @@ func TestResourceIDNamingEmpty(t *testing.T) { return } - if len(*actualNamesToIds) > 0 { - t.Fatalf("expected there to be no namesToIds but got %+v", *actualNamesToIds) + if len(actualNamesToIds) > 0 { + t.Fatalf("expected there to be no namesToIds but got %+v", actualNamesToIds) } } @@ -177,8 +177,8 @@ func TestResourceIDNamingSubscriptionId(t *testing.T) { return } - if !reflect.DeepEqual(expectedNamesToIds, *actualNamesToIds) { - t.Fatalf("expected namesToIds to be %+v but got %+v", expectedNamesToIds, *actualNamesToIds) + if !reflect.DeepEqual(expectedNamesToIds, actualNamesToIds) { + t.Fatalf("expected namesToIds to be %+v but got %+v", expectedNamesToIds, actualNamesToIds) } } @@ -199,8 +199,8 @@ func TestResourceIDNamingSubscriptionIdAndSuffix(t *testing.T) { return } - if !reflect.DeepEqual(expectedNamesToIds, *actualNamesToIds) { - t.Fatalf("expected namesToIds to be %+v but got %+v", expectedNamesToIds, *actualNamesToIds) + if !reflect.DeepEqual(expectedNamesToIds, actualNamesToIds) { + t.Fatalf("expected namesToIds to be %+v but got %+v", expectedNamesToIds, actualNamesToIds) } } @@ -219,8 +219,8 @@ func TestResourceIDNamingResourceGroupId(t *testing.T) { return } - if !reflect.DeepEqual(expectedNamesToIds, *actualNamesToIds) { - t.Fatalf("expected namesToIds to be %+v but got %+v", expectedNamesToIds, *actualNamesToIds) + if !reflect.DeepEqual(expectedNamesToIds, actualNamesToIds) { + t.Fatalf("expected namesToIds to be %+v but got %+v", expectedNamesToIds, actualNamesToIds) } } @@ -241,8 +241,8 @@ func TestResourceIDNamingResourceGroupIdAndSuffix(t *testing.T) { return } - if !reflect.DeepEqual(expectedNamesToIds, *actualNamesToIds) { - t.Fatalf("expected namesToIds to be %+v but got %+v", expectedNamesToIds, *actualNamesToIds) + if !reflect.DeepEqual(expectedNamesToIds, actualNamesToIds) { + t.Fatalf("expected namesToIds to be %+v but got %+v", expectedNamesToIds, actualNamesToIds) } } @@ -261,8 +261,8 @@ func TestResourceIDNamingManagementGroupId(t *testing.T) { return } - if !reflect.DeepEqual(expectedNamesToIds, *actualNamesToIds) { - t.Fatalf("expected namesToIds to be %+v but got %+v", expectedNamesToIds, *actualNamesToIds) + if !reflect.DeepEqual(expectedNamesToIds, actualNamesToIds) { + t.Fatalf("expected namesToIds to be %+v but got %+v", expectedNamesToIds, actualNamesToIds) } } @@ -283,8 +283,8 @@ func TestResourceIDNamingManagementGroupIdAndSuffix(t *testing.T) { return } - if !reflect.DeepEqual(expectedNamesToIds, *actualNamesToIds) { - t.Fatalf("expected namesToIds to be %+v but got %+v", expectedNamesToIds, *actualNamesToIds) + if !reflect.DeepEqual(expectedNamesToIds, actualNamesToIds) { + t.Fatalf("expected namesToIds to be %+v but got %+v", expectedNamesToIds, actualNamesToIds) } } @@ -303,8 +303,8 @@ func TestResourceIDNamingEventHubSkuId(t *testing.T) { return } - if !reflect.DeepEqual(expectedNamesToIds, *actualNamesToIds) { - t.Fatalf("expected namesToIds to be %+v but got %+v", expectedNamesToIds, *actualNamesToIds) + if !reflect.DeepEqual(expectedNamesToIds, actualNamesToIds) { + t.Fatalf("expected namesToIds to be %+v but got %+v", expectedNamesToIds, actualNamesToIds) } } @@ -330,8 +330,8 @@ func TestResourceIDNamingTopLevelScope(t *testing.T) { return } - if !reflect.DeepEqual(expectedNamesToIds, *actualNamesToIds) { - t.Fatalf("expected namesToIds to be %+v but got %+v", expectedNamesToIds, *actualNamesToIds) + if !reflect.DeepEqual(expectedNamesToIds, actualNamesToIds) { + t.Fatalf("expected namesToIds to be %+v but got %+v", expectedNamesToIds, actualNamesToIds) } } @@ -366,8 +366,8 @@ func TestResourceIDNamingContainingAConstant(t *testing.T) { return } - if !reflect.DeepEqual(expectedNamesToIds, *actualNamesToIds) { - t.Fatalf("expected namesToIds to be %+v but got %+v", expectedNamesToIds, *actualNamesToIds) + if !reflect.DeepEqual(expectedNamesToIds, actualNamesToIds) { + t.Fatalf("expected namesToIds to be %+v but got %+v", expectedNamesToIds, actualNamesToIds) } } @@ -404,8 +404,8 @@ func TestResourceIDNamingContainingAConstantAndSuffix(t *testing.T) { return } - if !reflect.DeepEqual(expectedNamesToIds, *actualNamesToIds) { - t.Fatalf("expected namesToIds to be %+v but got %+v", expectedNamesToIds, *actualNamesToIds) + if !reflect.DeepEqual(expectedNamesToIds, actualNamesToIds) { + t.Fatalf("expected namesToIds to be %+v but got %+v", expectedNamesToIds, actualNamesToIds) } } @@ -424,8 +424,8 @@ func TestResourceIdNamingTopLevelResourceId(t *testing.T) { return } - if !reflect.DeepEqual(expectedNamesToIds, *actualNamesToIds) { - t.Fatalf("expected namesToIds to be %+v but got %+v", expectedNamesToIds, *actualNamesToIds) + if !reflect.DeepEqual(expectedNamesToIds, actualNamesToIds) { + t.Fatalf("expected namesToIds to be %+v but got %+v", expectedNamesToIds, actualNamesToIds) } } @@ -446,8 +446,8 @@ func TestResourceIdNamingTopLevelAndNestedResourceId(t *testing.T) { return } - if !reflect.DeepEqual(expectedNamesToIds, *actualNamesToIds) { - t.Fatalf("expected namesToIds to be %+v but got %+v", expectedNamesToIds, *actualNamesToIds) + if !reflect.DeepEqual(expectedNamesToIds, actualNamesToIds) { + t.Fatalf("expected namesToIds to be %+v but got %+v", expectedNamesToIds, actualNamesToIds) } } @@ -466,8 +466,8 @@ func TestResourceIdNamingNestedResourceId(t *testing.T) { return } - if !reflect.DeepEqual(expectedNamesToIds, *actualNamesToIds) { - t.Fatalf("expected namesToIds to be %+v but got %+v", expectedNamesToIds, *actualNamesToIds) + if !reflect.DeepEqual(expectedNamesToIds, actualNamesToIds) { + t.Fatalf("expected namesToIds to be %+v but got %+v", expectedNamesToIds, actualNamesToIds) } } @@ -486,8 +486,8 @@ func TestResourceIdNamingResourceUnderScope(t *testing.T) { return } - if !reflect.DeepEqual(expectedNamesToIds, *actualNamesToIds) { - t.Fatalf("expected namesToIds to be %+v but got %+v", expectedNamesToIds, *actualNamesToIds) + if !reflect.DeepEqual(expectedNamesToIds, actualNamesToIds) { + t.Fatalf("expected namesToIds to be %+v but got %+v", expectedNamesToIds, actualNamesToIds) } } @@ -508,8 +508,8 @@ func TestResourceIdNamingConflictingTwoLevels(t *testing.T) { return } - if !reflect.DeepEqual(expectedNamesToIds, *actualNamesToIds) { - t.Fatalf("expected namesToIds to be:\n\n%+v\nbut got:\n\n%+v", expectedNamesToIds, *actualNamesToIds) + if !reflect.DeepEqual(expectedNamesToIds, actualNamesToIds) { + t.Fatalf("expected namesToIds to be:\n\n%+v\nbut got:\n\n%+v", expectedNamesToIds, actualNamesToIds) } } @@ -523,11 +523,11 @@ func TestResourceIdNamingConflictingWithUpdatingOperation(t *testing.T) { "ExtensionId": virtualNetworkExtensionResourceId, } uriToParsedOperation := map[string]ParsedOperation{ - helpers.DisplayValueForResourceID(virtualMachineExtensionResourceId): { + sdkHelpers.DisplayValueForResourceID(virtualMachineExtensionResourceId): { ResourceId: &virtualMachineExtensionResourceId, ResourceIdName: pointer.To("ExtensionId"), }, - helpers.DisplayValueForResourceID(virtualNetworkExtensionResourceId): { + sdkHelpers.DisplayValueForResourceID(virtualNetworkExtensionResourceId): { ResourceId: &virtualNetworkExtensionResourceId, ResourceIdName: pointer.To("ExtensionId"), }, @@ -539,12 +539,12 @@ func TestResourceIdNamingConflictingWithUpdatingOperation(t *testing.T) { return } - if !reflect.DeepEqual(expectedNamesToIds, *actualNamesToIds) { - t.Fatalf("expected namesToIds to be:\n\n%+v\nbut got:\n\n%+v", expectedNamesToIds, *actualNamesToIds) + if !reflect.DeepEqual(expectedNamesToIds, actualNamesToIds) { + t.Fatalf("expected namesToIds to be:\n\n%+v\nbut got:\n\n%+v", expectedNamesToIds, actualNamesToIds) } for wantIDName, resourceID := range expectedNamesToIds { - id := helpers.DisplayValueForResourceID(resourceID) + id := sdkHelpers.DisplayValueForResourceID(resourceID) got := uriToParsedOperation[id].ResourceIdName if got == nil || *got != wantIDName { t.Fatalf("expected ResourceIdName of virtualMachineResourceId to be: %s\nbut got:%s\n", wantIDName, *got) @@ -648,8 +648,8 @@ func TestResourceIdNamingConflictingMultipleLevels(t *testing.T) { return } - if !reflect.DeepEqual(expectedNamesToIds, *actualNamesToIds) { - t.Fatalf("expected namesToIds to be:\n\n%+v\n\nbut got:\n%+v", expectedNamesToIds, *actualNamesToIds) + if !reflect.DeepEqual(expectedNamesToIds, actualNamesToIds) { + t.Fatalf("expected namesToIds to be:\n\n%+v\n\nbut got:\n%+v", expectedNamesToIds, actualNamesToIds) } } @@ -668,8 +668,8 @@ func TestResourceIdNamingSignalRId(t *testing.T) { return } - if !reflect.DeepEqual(expectedNamesToIds, *actualNamesToIds) { - t.Fatalf("expected namesToIds to be %+v but got %+v", expectedNamesToIds, *actualNamesToIds) + if !reflect.DeepEqual(expectedNamesToIds, actualNamesToIds) { + t.Fatalf("expected namesToIds to be %+v but got %+v", expectedNamesToIds, actualNamesToIds) } } @@ -688,8 +688,8 @@ func TestResourceIdNamingTrafficManagerEndpoint(t *testing.T) { return } - if !reflect.DeepEqual(expectedNamesToIds, *actualNamesToIds) { - t.Fatalf("expected namesToIds to be %+v but got %+v", expectedNamesToIds, *actualNamesToIds) + if !reflect.DeepEqual(expectedNamesToIds, actualNamesToIds) { + t.Fatalf("expected namesToIds to be %+v but got %+v", expectedNamesToIds, actualNamesToIds) } } @@ -708,7 +708,7 @@ func TestResourceIDNamingRedisDefaultId(t *testing.T) { return } - if !reflect.DeepEqual(expectedNamesToIds, *actualNamesToIds) { - t.Fatalf("expected namesToIds to be %+v but got %+v", expectedNamesToIds, *actualNamesToIds) + if !reflect.DeepEqual(expectedNamesToIds, actualNamesToIds) { + t.Fatalf("expected namesToIds to be %+v but got %+v", expectedNamesToIds, actualNamesToIds) } } diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/helpers.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/helpers.go new file mode 100644 index 00000000000..af49fb60ffa --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/helpers.go @@ -0,0 +1,66 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package resourceids + +import ( + "fmt" + "strings" + + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup" +) + +func normalizedResourceManagerResourceId(pri sdkModels.ResourceID) string { + segments := segmentsWithoutUriSuffix(pri) + return normalizedResourceId(segments) +} + +func segmentsWithoutUriSuffix(pri sdkModels.ResourceID) []sdkModels.ResourceIDSegment { + segments := pri.Segments + lastUserValueSegment := -1 + for i, segment := range segments { + // everything else technically is a user configurable component + if segment.Type != sdkModels.StaticResourceIDSegmentType && segment.Type != sdkModels.ResourceProviderResourceIDSegmentType { + lastUserValueSegment = i + } + } + if lastUserValueSegment >= 0 && len(segments) > lastUserValueSegment+1 { + // remove any URI Suffix since this isn't relevant for the ID's + segments = segments[0 : lastUserValueSegment+1] + } + return segments +} + +func normalizedResourceId(segments []sdkModels.ResourceIDSegment) string { + components := make([]string, 0) + for _, segment := range segments { + switch segment.Type { + case sdkModels.ResourceProviderResourceIDSegmentType: + { + normalizedSegment := cleanup.NormalizeResourceProviderName(*segment.FixedValue) + components = append(components, normalizedSegment) + continue + } + + case sdkModels.StaticResourceIDSegmentType: + { + normalizedSegment := cleanup.NormalizeSegment(*segment.FixedValue, true) + components = append(components, normalizedSegment) + continue + } + + case sdkModels.ConstantResourceIDSegmentType, sdkModels.ResourceGroupResourceIDSegmentType, sdkModels.ScopeResourceIDSegmentType, sdkModels.SubscriptionIDResourceIDSegmentType, sdkModels.UserSpecifiedResourceIDSegmentType: + // e.g. {example} + normalizedSegment := segment.Name + normalizedSegment = cleanup.NormalizeReservedKeywords(segment.Name) + components = append(components, fmt.Sprintf("{%s}", normalizedSegment)) + continue + + default: + panic(fmt.Sprintf("unimplemented segment type %q", string(segment.Type))) + } + } + + return fmt.Sprintf("/%s", strings.Join(components, "/")) +} diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/interface.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/interface.go similarity index 100% rename from tools/importer-rest-api-specs/components/parser/resourceids/interface.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/interface.go diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/models.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/models.go similarity index 86% rename from tools/importer-rest-api-specs/components/parser/resourceids/models.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/models.go index 610a0fa3cba..07eb99771a0 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/models.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/models.go @@ -7,7 +7,8 @@ import ( "fmt" sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/components/parser/internal" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/comparison" + parserModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models" ) type ParsedOperation struct { @@ -36,7 +37,7 @@ type ParseResult struct { } func (r *ParseResult) Append(other ParseResult) error { - intermediate := internal.ParseResult{ + intermediate := parserModels.ParseResult{ Constants: map[string]sdkModels.SDKConstant{}, } intermediate.AppendConstants(r.Constants) @@ -55,7 +56,7 @@ func (r *ParseResult) Append(other ParseResult) error { if existingVal, existing := operationIdsToParsedOperations[k]; existing { matches := false - if v.ResourceId != nil && existingVal.ResourceId != nil && ResourceIdsMatch(*v.ResourceId, *existingVal.ResourceId) { + if v.ResourceId != nil && existingVal.ResourceId != nil && comparison.ResourceIDsMatch(*v.ResourceId, *existingVal.ResourceId) { matches = true } if v.UriSuffix != nil && existingVal.UriSuffix != nil && *v.UriSuffix == *existingVal.UriSuffix { @@ -81,7 +82,7 @@ func (r *ParseResult) Append(other ParseResult) error { for _, v := range other.NamesToResourceIDs { foundMatching := false for _, otherId := range combinedResourceIds { - if ResourceIdsMatch(v, otherId) { + if comparison.ResourceIDsMatch(v, otherId) { foundMatching = true break } @@ -96,7 +97,7 @@ func (r *ParseResult) Append(other ParseResult) error { if err != nil { return fmt.Errorf("regenerating Names : Resource IDs for combined list: %+v", err) } - r.NamesToResourceIDs = *namesToResourceIds + r.NamesToResourceIDs = namesToResourceIds } return nil diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/parse_segments.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/parse_segments.go similarity index 92% rename from tools/importer-rest-api-specs/components/parser/resourceids/parse_segments.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/parse_segments.go index 1c12e4272c1..9c68a415ef3 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/parse_segments.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/parse_segments.go @@ -10,9 +10,10 @@ import ( "github.com/go-openapi/spec" sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/components/parser/cleanup" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/components/parser/constants" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/components/parser/internal" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/constants" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/ignore" + parserModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models" "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/logging" ) @@ -35,13 +36,13 @@ type processedResourceId struct { constants map[string]sdkModels.SDKConstant } -func (p *Parser) parseSegmentsForEachOperation() (*map[string]processedResourceId, error) { +func (p *Parser) parseSegmentsForEachOperation() (map[string]processedResourceId, error) { // TODO: document this operationIdsToProcessedResourceIds := make(map[string]processedResourceId, 0) for _, operation := range p.swaggerSpecExpanded.Operations() { for uri, operationDetails := range operation { - if internal.OperationShouldBeIgnored(uri) { + if ignore.Operation(uri) { logging.Debugf("Ignoring %q", uri) continue } @@ -56,14 +57,14 @@ func (p *Parser) parseSegmentsForEachOperation() (*map[string]processedResourceI } } - return &operationIdsToProcessedResourceIds, nil + return operationIdsToProcessedResourceIds, nil } func (p *Parser) parseResourceIdFromOperation(uri string, operation *spec.Operation) (*processedResourceId, error) { // TODO: document this segments := make([]sdkModels.ResourceIDSegment, 0) - result := internal.ParseResult{ + result := parserModels.ParseResult{ Constants: map[string]sdkModels.SDKConstant{}, } @@ -132,7 +133,7 @@ func (p *Parser) parseResourceIdFromOperation(uri string, operation *spec.Operat if param.Enum != nil { // then find the constant itself - constant, err := constants.MapConstant([]string{param.Type}, param.Name, nil, param.Enum, param.Extensions) + constant, err := constants.Parse([]string{param.Type}, param.Name, nil, param.Enum, param.Extensions) if err != nil { return nil, fmt.Errorf("parsing constant from %q: %+v", uriSegment, err) } @@ -182,20 +183,20 @@ func (p *Parser) parseResourceIdFromOperation(uri string, operation *spec.Operat // prefix this with `static{name}` so that the segment is unique // these aren't parsed out anyway, but we need unique names - normalizedSegment = normalizeSegment(fmt.Sprintf("static%s", strings.Title(resourceProviderValue))) + normalizedSegment = normalizeSegment(fmt.Sprintf("static%s", cleanup.Title(resourceProviderValue))) segments = append(segments, sdkModels.NewResourceProviderResourceIDSegment(normalizedSegment, resourceProviderValue)) continue } // prefix this with `static{name}` so that the segment is unique // these aren't parsed out anyway, but we need unique names - normalizedName := normalizeSegment(fmt.Sprintf("static%s", strings.Title(normalizedSegment))) + normalizedName := normalizeSegment(fmt.Sprintf("static%s", cleanup.Title(normalizedSegment))) segments = append(segments, sdkModels.NewStaticValueResourceIDSegment(normalizedName, normalizedSegment)) } - // now that we've parsed all of the URI Segments, let's determine if this contains a Resource ID scope + // now that we've parsed all the URI Segments, let's determine if this contains a Resource ID scope - // Some Swaggers define Operation URI's which are generic scopes but which use the full Resource ID format + // Some Swaggers define Operation URIs which are generic scopes but which use the full Resource ID format // rather than a /{scope} parameter, e.g. // /subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.EventGrid/{parentType}/{parentName} // diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/parse_segments_test.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/parse_segments_test.go similarity index 100% rename from tools/importer-rest-api-specs/components/parser/resourceids/parse_segments_test.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/parse_segments_test.go diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/parser.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/parser.go similarity index 84% rename from tools/importer-rest-api-specs/components/parser/resourceids/parser.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/parser.go index 1e5f974c909..147e32315e5 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/parser.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/parser.go @@ -8,6 +8,7 @@ import ( "sort" sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/comparison" "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/logging" ) @@ -24,10 +25,10 @@ func (p *Parser) Parse() (*ParseResult, error) { // 2. Process the list of parsed segments to obtain a unique list of Resource IDs logging.Tracef("Determining the list of unique Resource IDs from the parsed input") - uniqueResourceIds, distinctConstants := p.distinctResourceIds(*operationIdsToSegments) + uniqueResourceIds, distinctConstants := p.distinctResourceIds(operationIdsToSegments) // 3. Then we need to find any Common Resource IDs and switch those references out - logging.Tracef("Generating Names for Resource IDs..") + logging.Tracef("Switching out Common IDs as needed..") resourceIds := switchOutCommonResourceIDsAsNeeded(uniqueResourceIds) // 4. We then need to generate a unique Resource ID name for each of the Resource IDs @@ -39,19 +40,19 @@ func (p *Parser) Parse() (*ParseResult, error) { // 5. Then we need to work through the list of Resource IDs and Operation IDs to map the data across logging.Tracef("Updating the Parsed Operations with the Processed ResourceIds..") - operationIdsToResourceIds, err := p.updateParsedOperationsWithProcessedResourceIds(*operationIdsToSegments, *namesToResourceIds) + operationIdsToResourceIds, err := p.updateParsedOperationsWithProcessedResourceIds(operationIdsToSegments, namesToResourceIds) if err != nil { return nil, fmt.Errorf("updating the parsed Operations with the Processed Resource ID information: %+v", err) } return &ParseResult{ - OperationIdsToParsedResourceIds: *operationIdsToResourceIds, - NamesToResourceIDs: *namesToResourceIds, + OperationIdsToParsedResourceIds: operationIdsToResourceIds, + NamesToResourceIDs: namesToResourceIds, Constants: distinctConstants, }, nil } -func (p *Parser) updateParsedOperationsWithProcessedResourceIds(operationIdsToSegments map[string]processedResourceId, namesToResourceIds map[string]sdkModels.ResourceID) (*map[string]ParsedOperation, error) { +func (p *Parser) updateParsedOperationsWithProcessedResourceIds(operationIdsToSegments map[string]processedResourceId, namesToResourceIds map[string]sdkModels.ResourceID) (map[string]ParsedOperation, error) { output := make(map[string]ParsedOperation) for operationId, operation := range operationIdsToSegments { @@ -85,7 +86,7 @@ func (p *Parser) updateParsedOperationsWithProcessedResourceIds(operationIdsToSe ConstantNames: resourceId.ConstantNames, Segments: resourceId.Segments, } - if ResourceIdsMatch(placeholder, other) { + if comparison.ResourceIDsMatch(placeholder, other) { output[operationId] = ParsedOperation{ ResourceId: &resourceId, ResourceIdName: &name, @@ -101,5 +102,5 @@ func (p *Parser) updateParsedOperationsWithProcessedResourceIds(operationIdsToSe } } - return &output, nil + return output, nil } diff --git a/tools/importer-rest-api-specs/components/parser/swagger_tags_test.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/swagger_tags_test.go similarity index 89% rename from tools/importer-rest-api-specs/components/parser/swagger_tags_test.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/swagger_tags_test.go index ccf6ecfe9da..61bdd746b2c 100644 --- a/tools/importer-rest-api-specs/components/parser/swagger_tags_test.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/swagger_tags_test.go @@ -1,25 +1,24 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package parser +package parser_test import ( "testing" "github.com/hashicorp/go-azure-helpers/lang/pointer" sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" - importerModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/models" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testhelpers" ) func TestParsingOperationsUsingTheSameSwaggerTagInDifferentCasings(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_tag_different_casing.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_tag_different_casing.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Hello": { Models: map[string]sdkModels.SDKModel{ @@ -83,18 +82,17 @@ func TestParsingOperationsUsingTheSameSwaggerTagInDifferentCasings(t *testing.T) }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParsingOperationsOnResources(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_on_resources.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_on_resources.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } - expected := importerModels.AzureApiDefinition{ - ServiceName: "Example", - ApiVersion: "2020-01-01", + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", Resources: map[string]sdkModels.APIResource{ "Hello": { Models: map[string]sdkModels.SDKModel{ @@ -174,5 +172,5 @@ func TestParsingOperationsOnResources(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } diff --git a/tools/importer-rest-api-specs/components/parser/testdata/constants_floats_as_floats.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/constants_floats_as_floats.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/constants_floats_as_floats.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/constants_floats_as_floats.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/constants_floats_as_floats_inlined.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/constants_floats_as_floats_inlined.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/constants_floats_as_floats_inlined.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/constants_floats_as_floats_inlined.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/constants_floats_as_strings.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/constants_floats_as_strings.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/constants_floats_as_strings.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/constants_floats_as_strings.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/constants_floats_as_strings_inlined.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/constants_floats_as_strings_inlined.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/constants_floats_as_strings_inlined.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/constants_floats_as_strings_inlined.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/constants_in_operation_parameters.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/constants_in_operation_parameters.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/constants_in_operation_parameters.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/constants_in_operation_parameters.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/constants_integers_as_ints.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/constants_integers_as_ints.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/constants_integers_as_ints.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/constants_integers_as_ints.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/constants_integers_as_ints_inlined.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/constants_integers_as_ints_inlined.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/constants_integers_as_ints_inlined.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/constants_integers_as_ints_inlined.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/constants_integers_as_strings.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/constants_integers_as_strings.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/constants_integers_as_strings.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/constants_integers_as_strings.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/constants_integers_as_strings_inlined.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/constants_integers_as_strings_inlined.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/constants_integers_as_strings_inlined.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/constants_integers_as_strings_inlined.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/constants_integers_with_names.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/constants_integers_with_names.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/constants_integers_with_names.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/constants_integers_with_names.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/constants_integers_with_names_inlined.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/constants_integers_with_names_inlined.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/constants_integers_with_names_inlined.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/constants_integers_with_names_inlined.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/constants_multiple_type_enums.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/constants_multiple_type_enums.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/constants_multiple_type_enums.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/constants_multiple_type_enums.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/constants_strings.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/constants_strings.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/constants_strings.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/constants_strings.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/constants_strings_as_non_strings.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/constants_strings_as_non_strings.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/constants_strings_as_non_strings.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/constants_strings_as_non_strings.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/constants_strings_containing_floats_inlined.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/constants_strings_containing_floats_inlined.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/constants_strings_containing_floats_inlined.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/constants_strings_containing_floats_inlined.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/constants_strings_inlined.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/constants_strings_inlined.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/constants_strings_inlined.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/constants_strings_inlined.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/constants_strings_inlined_as_non_strings.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/constants_strings_inlined_as_non_strings.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/constants_strings_inlined_as_non_strings.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/constants_strings_inlined_as_non_strings.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/constants_strings_which_are_floats.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/constants_strings_which_are_floats.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/constants_strings_which_are_floats.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/constants_strings_which_are_floats.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/model_commonschema_identitylegacysystemanduserassignedlist.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_commonschema_identitylegacysystemanduserassignedlist.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/model_commonschema_identitylegacysystemanduserassignedlist.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_commonschema_identitylegacysystemanduserassignedlist.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/model_commonschema_identitylegacysystemanduserassignedmap.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_commonschema_identitylegacysystemanduserassignedmap.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/model_commonschema_identitylegacysystemanduserassignedmap.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_commonschema_identitylegacysystemanduserassignedmap.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/model_commonschema_identitylegacysystemanduserassignedmap_extrafields.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_commonschema_identitylegacysystemanduserassignedmap_extrafields.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/model_commonschema_identitylegacysystemanduserassignedmap_extrafields.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_commonschema_identitylegacysystemanduserassignedmap_extrafields.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/model_commonschema_identitylegacysystemanduserassignedmap_genericdictionary.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_commonschema_identitylegacysystemanduserassignedmap_genericdictionary.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/model_commonschema_identitylegacysystemanduserassignedmap_genericdictionary.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_commonschema_identitylegacysystemanduserassignedmap_genericdictionary.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/model_commonschema_identitysystemanduserassignedlist.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_commonschema_identitysystemanduserassignedlist.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/model_commonschema_identitysystemanduserassignedlist.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_commonschema_identitysystemanduserassignedlist.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/model_commonschema_identitysystemanduserassignedmap.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_commonschema_identitysystemanduserassignedmap.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/model_commonschema_identitysystemanduserassignedmap.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_commonschema_identitysystemanduserassignedmap.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/model_commonschema_identitysystemanduserassignedmap_extrafields.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_commonschema_identitysystemanduserassignedmap_extrafields.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/model_commonschema_identitysystemanduserassignedmap_extrafields.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_commonschema_identitysystemanduserassignedmap_extrafields.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/model_commonschema_identitysystemassigned.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_commonschema_identitysystemassigned.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/model_commonschema_identitysystemassigned.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_commonschema_identitysystemassigned.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/model_commonschema_identitysystemoruserassignedlist.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_commonschema_identitysystemoruserassignedlist.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/model_commonschema_identitysystemoruserassignedlist.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_commonschema_identitysystemoruserassignedlist.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/model_commonschema_identitysystemoruserassignedmap.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_commonschema_identitysystemoruserassignedmap.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/model_commonschema_identitysystemoruserassignedmap.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_commonschema_identitysystemoruserassignedmap.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/model_commonschema_identitysystemoruserassignedmap_delegatedresources.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_commonschema_identitysystemoruserassignedmap_delegatedresources.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/model_commonschema_identitysystemoruserassignedmap_delegatedresources.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_commonschema_identitysystemoruserassignedmap_delegatedresources.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/model_commonschema_identitysystemoruserassignedmap_extrafields.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_commonschema_identitysystemoruserassignedmap_extrafields.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/model_commonschema_identitysystemoruserassignedmap_extrafields.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_commonschema_identitysystemoruserassignedmap_extrafields.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/model_commonschema_identityuserassignedlist.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_commonschema_identityuserassignedlist.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/model_commonschema_identityuserassignedlist.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_commonschema_identityuserassignedlist.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/model_commonschema_identityuserassignedmap.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_commonschema_identityuserassignedmap.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/model_commonschema_identityuserassignedmap.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_commonschema_identityuserassignedmap.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/model_commonschema_identityuserassignedmap_principalid.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_commonschema_identityuserassignedmap_principalid.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/model_commonschema_identityuserassignedmap_principalid.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_commonschema_identityuserassignedmap_principalid.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/model_commonschema_identityuserassignedmap_tenantid.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_commonschema_identityuserassignedmap_tenantid.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/model_commonschema_identityuserassignedmap_tenantid.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_commonschema_identityuserassignedmap_tenantid.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/model_commonschema_location.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_commonschema_location.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/model_commonschema_location.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_commonschema_location.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/model_containing_allof_object_type.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_containing_allof_object_type.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/model_containing_allof_object_type.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_containing_allof_object_type.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/model_containing_allof_object_type_with_properties.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_containing_allof_object_type_with_properties.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/model_containing_allof_object_type_with_properties.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_containing_allof_object_type_with_properties.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/model_containing_allof_within_properties.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_containing_allof_within_properties.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/model_containing_allof_within_properties.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_containing_allof_within_properties.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/model_containing_allof_within_properties_multiple.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_containing_allof_within_properties_multiple.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/model_containing_allof_within_properties_multiple.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_containing_allof_within_properties_multiple.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/model_containing_lists.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_containing_lists.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/model_containing_lists.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_containing_lists.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/model_dictionary_of_integers.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_dictionary_of_integers.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/model_dictionary_of_integers.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_dictionary_of_integers.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/model_dictionary_of_integers_inlined.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_dictionary_of_integers_inlined.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/model_dictionary_of_integers_inlined.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_dictionary_of_integers_inlined.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/model_dictionary_of_object.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_dictionary_of_object.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/model_dictionary_of_object.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_dictionary_of_object.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/model_dictionary_of_object_inlined.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_dictionary_of_object_inlined.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/model_dictionary_of_object_inlined.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_dictionary_of_object_inlined.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/model_dictionary_of_string.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_dictionary_of_string.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/model_dictionary_of_string.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_dictionary_of_string.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/model_dictionary_of_string_inlined.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_dictionary_of_string_inlined.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/model_dictionary_of_string_inlined.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_dictionary_of_string_inlined.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/model_discriminators_child_that_shouldnt_be.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_discriminators_child_that_shouldnt_be.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/model_discriminators_child_that_shouldnt_be.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_discriminators_child_that_shouldnt_be.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/model_discriminators_child_used_as_parent.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_discriminators_child_used_as_parent.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/model_discriminators_child_used_as_parent.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_discriminators_child_used_as_parent.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/model_discriminators_deep_inheritance.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_discriminators_deep_inheritance.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/model_discriminators_deep_inheritance.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_discriminators_deep_inheritance.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/model_discriminators_inherited_from_discriminators.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_discriminators_inherited_from_discriminators.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/model_discriminators_inherited_from_discriminators.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_discriminators_inherited_from_discriminators.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/model_discriminators_multiple_parents.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_discriminators_multiple_parents.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/model_discriminators_multiple_parents.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_discriminators_multiple_parents.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/model_discriminators_multiple_parents_within_array.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_discriminators_multiple_parents_within_array.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/model_discriminators_multiple_parents_within_array.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_discriminators_multiple_parents_within_array.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/model_discriminators_parent_that_shouldnt_be.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_discriminators_parent_that_shouldnt_be.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/model_discriminators_parent_that_shouldnt_be.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_discriminators_parent_that_shouldnt_be.json diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_discriminators_ref_from_another_spec.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_discriminators_ref_from_another_spec.json new file mode 100644 index 00000000000..5c45ec0e49e --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_discriminators_ref_from_another_spec.json @@ -0,0 +1,42 @@ +{ + "swagger": "2.0", + "info": { + "title": "DataFactory", + "description": "Example", + "version": "2020-01-01" + }, + "host": "management.mysite.com", + "schemes": [ + "https" + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "security": [], + "securityDefinitions": {}, + "paths": { + "/example": { + "get": { + "tags": [ + "Example" + ], + "operationId": "Example_Test", + "description": "An example returning a parent discriminated type", + "parameters": [], + "responses": { + "200": { + "description": "Success.", + "schema": { + "$ref": "./model_discriminators_ref_in_another_spec.json#/definitions/Animal" + } + } + } + } + } + }, + "definitions": {}, + "parameters": {} +} \ No newline at end of file diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_discriminators_ref_in_another_spec.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_discriminators_ref_in_another_spec.json new file mode 100644 index 00000000000..4a5b6db19e7 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_discriminators_ref_in_another_spec.json @@ -0,0 +1,59 @@ +{ + "swagger": "2.0", + "info": { + "title": "Example", + "description": "Example", + "version": "2020-01-01" + }, + "host": "management.mysite.com", + "schemes": [ + "https" + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "security": [], + "securityDefinitions": {}, + "paths": {}, + "definitions": { + "Animal": { + "discriminator": "animalType", + "properties": { + "animalType": { + "type": "string", + "description": "The type of Animal this is, used as a Discriminator value." + } + }, + "required": [ + "animalType" + ], + "title": "Animal", + "type": "object" + }, + "Cat": { + "description": "A cat is a kind of ParentType", + "allOf": [ + { + "$ref": "#/definitions/Animal" + } + ], + "properties": { + "isFluffy": { + "type": "boolean", + "description": "Are cats fluffy?" + } + }, + "required": [ + "isFluffy", + "typeName" + ], + "title": "Cat", + "type": "object", + "x-ms-discriminator-value": "Cat" + } + }, + "parameters": {} +} \ No newline at end of file diff --git a/tools/importer-rest-api-specs/components/parser/testdata/model_discriminators_simple.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_discriminators_simple.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/model_discriminators_simple.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_discriminators_simple.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/model_discriminators_within_array.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_discriminators_within_array.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/model_discriminators_within_array.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_discriminators_within_array.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/model_discriminators_within_discriminators.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_discriminators_within_discriminators.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/model_discriminators_within_discriminators.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_discriminators_within_discriminators.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/model_inheriting_from_other_model_no_new_fields.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_inheriting_from_other_model_no_new_fields.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/model_inheriting_from_other_model_no_new_fields.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_inheriting_from_other_model_no_new_fields.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/model_inheriting_from_other_model_no_new_fields_inlined.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_inheriting_from_other_model_no_new_fields_inlined.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/model_inheriting_from_other_model_no_new_fields_inlined.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_inheriting_from_other_model_no_new_fields_inlined.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/model_inheriting_from_other_model_with_only_description.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_inheriting_from_other_model_with_only_description.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/model_inheriting_from_other_model_with_only_description.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_inheriting_from_other_model_with_only_description.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/model_inheriting_from_other_model_with_properties_within_allof.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_inheriting_from_other_model_with_properties_within_allof.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/model_inheriting_from_other_model_with_properties_within_allof.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_inheriting_from_other_model_with_properties_within_allof.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/model_inheriting_from_parent.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_inheriting_from_parent.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/model_inheriting_from_parent.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_inheriting_from_parent.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/model_inlined_with_no_name.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_inlined_with_no_name.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/model_inlined_with_no_name.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_inlined_with_no_name.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/model_multiple_top_level_models_and_operations.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_multiple_top_level_models_and_operations.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/model_multiple_top_level_models_and_operations.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_multiple_top_level_models_and_operations.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/model_top_level.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_top_level.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/model_top_level.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_top_level.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/model_top_level_with_inlined_model.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_top_level_with_inlined_model.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/model_top_level_with_inlined_model.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_top_level_with_inlined_model.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/model_top_level_with_rawfile.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_top_level_with_rawfile.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/model_top_level_with_rawfile.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_top_level_with_rawfile.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/model_using_datafactory_custom_types.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_using_datafactory_custom_types.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/model_using_datafactory_custom_types.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_using_datafactory_custom_types.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/model_with_circular_reference.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_with_circular_reference.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/model_with_circular_reference.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_with_circular_reference.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/model_with_datetime_no_type.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_with_datetime_no_type.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/model_with_datetime_no_type.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_with_datetime_no_type.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/model_with_inlined_object.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_with_inlined_object.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/model_with_inlined_object.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_with_inlined_object.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/model_with_number_prefixed_field.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_with_number_prefixed_field.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/model_with_number_prefixed_field.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_with_number_prefixed_field.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/model_with_reference.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_with_reference.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/model_with_reference.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_with_reference.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/model_with_reference_array.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_with_reference_array.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/model_with_reference_array.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_with_reference_array.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/model_with_reference_constant.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_with_reference_constant.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/model_with_reference_constant.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_with_reference_constant.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/model_with_reference_string.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_with_reference_string.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/model_with_reference_string.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_with_reference_string.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/models_bug_2675_duplicate_model.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/models_bug_2675_duplicate_model.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/models_bug_2675_duplicate_model.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/models_bug_2675_duplicate_model.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/nestedtestdata/model_discriminators_orphaned_child.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/nestedtestdata/model_discriminators_orphaned_child.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/nestedtestdata/model_discriminators_orphaned_child.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/nestedtestdata/model_discriminators_orphaned_child.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/nestedtestdata/model_discriminators_orphaned_child_with_nested_model.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/nestedtestdata/model_discriminators_orphaned_child_with_nested_model.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/nestedtestdata/model_discriminators_orphaned_child_with_nested_model.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/nestedtestdata/model_discriminators_orphaned_child_with_nested_model.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/nestedtestdata/model_discriminators_orphaned_child_without_discriminator_value.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/nestedtestdata/model_discriminators_orphaned_child_without_discriminator_value.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/nestedtestdata/model_discriminators_orphaned_child_without_discriminator_value.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/nestedtestdata/model_discriminators_orphaned_child_without_discriminator_value.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/operation_content_types.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operation_content_types.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/operation_content_types.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operation_content_types.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/operations_empty.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_empty.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/operations_empty.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_empty.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/operations_multiple_same_resource_id.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_multiple_same_resource_id.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/operations_multiple_same_resource_id.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_multiple_same_resource_id.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/operations_on_resources.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_on_resources.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/operations_on_resources.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_on_resources.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/operations_single_list.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_list.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/operations_single_list.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_list.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/operations_single_list_of_strings.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_list_of_strings.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/operations_single_list_of_strings.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_list_of_strings.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/operations_single_list_which_is_not_a_list.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_list_which_is_not_a_list.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/operations_single_list_which_is_not_a_list.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_list_which_is_not_a_list.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/operations_single_list_without_pageable.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_list_without_pageable.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/operations_single_list_without_pageable.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_list_without_pageable.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/operations_single_long_running.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_long_running.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/operations_single_long_running.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_long_running.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/operations_single_multiple_return_objects.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_multiple_return_objects.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/operations_single_multiple_return_objects.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_multiple_return_objects.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/operations_single_multiple_tags.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_multiple_tags.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/operations_single_multiple_tags.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_multiple_tags.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/operations_single_requesting_with_a_bool.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_requesting_with_a_bool.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/operations_single_requesting_with_a_bool.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_requesting_with_a_bool.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/operations_single_requesting_with_a_dictionary_of_strings.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_requesting_with_a_dictionary_of_strings.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/operations_single_requesting_with_a_dictionary_of_strings.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_requesting_with_a_dictionary_of_strings.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/operations_single_requesting_with_a_int.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_requesting_with_a_int.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/operations_single_requesting_with_a_int.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_requesting_with_a_int.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/operations_single_requesting_with_a_list_of_strings.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_requesting_with_a_list_of_strings.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/operations_single_requesting_with_a_list_of_strings.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_requesting_with_a_list_of_strings.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/operations_single_requesting_with_a_string.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_requesting_with_a_string.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/operations_single_requesting_with_a_string.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_requesting_with_a_string.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/operations_single_returning_a_bool.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_returning_a_bool.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/operations_single_returning_a_bool.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_returning_a_bool.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/operations_single_returning_a_dictionary_of_model.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_returning_a_dictionary_of_model.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/operations_single_returning_a_dictionary_of_model.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_returning_a_dictionary_of_model.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/operations_single_returning_a_dictionary_of_strings.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_returning_a_dictionary_of_strings.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/operations_single_returning_a_dictionary_of_strings.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_returning_a_dictionary_of_strings.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/operations_single_returning_a_file.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_returning_a_file.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/operations_single_returning_a_file.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_returning_a_file.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/operations_single_returning_a_float.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_returning_a_float.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/operations_single_returning_a_float.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_returning_a_float.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/operations_single_returning_a_list_of_ints.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_returning_a_list_of_ints.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/operations_single_returning_a_list_of_ints.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_returning_a_list_of_ints.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/operations_single_returning_a_list_of_list_of_model.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_returning_a_list_of_list_of_model.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/operations_single_returning_a_list_of_list_of_model.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_returning_a_list_of_list_of_model.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/operations_single_returning_a_list_of_list_of_strings.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_returning_a_list_of_list_of_strings.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/operations_single_returning_a_list_of_list_of_strings.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_returning_a_list_of_list_of_strings.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/operations_single_returning_a_list_of_model.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_returning_a_list_of_model.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/operations_single_returning_a_list_of_model.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_returning_a_list_of_model.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/operations_single_returning_a_list_of_strings.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_returning_a_list_of_strings.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/operations_single_returning_a_list_of_strings.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_returning_a_list_of_strings.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/operations_single_returning_a_string.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_returning_a_string.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/operations_single_returning_a_string.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_returning_a_string.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/operations_single_returning_a_top_level_raw_object.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_returning_a_top_level_raw_object.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/operations_single_returning_a_top_level_raw_object.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_returning_a_top_level_raw_object.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/operations_single_returning_an_error_status_code.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_returning_an_error_status_code.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/operations_single_returning_an_error_status_code.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_returning_an_error_status_code.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/operations_single_returning_an_integer.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_returning_an_integer.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/operations_single_returning_an_integer.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_returning_an_integer.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/operations_single_tag_different_casing.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_tag_different_casing.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/operations_single_tag_different_casing.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_tag_different_casing.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/operations_single_with_header_options.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_with_header_options.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/operations_single_with_header_options.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_with_header_options.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/operations_single_with_no_tag.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_with_no_tag.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/operations_single_with_no_tag.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_with_no_tag.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/operations_single_with_querystring_options.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_with_querystring_options.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/operations_single_with_querystring_options.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_with_querystring_options.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/operations_single_with_request_and_response_object.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_with_request_and_response_object.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/operations_single_with_request_and_response_object.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_with_request_and_response_object.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/operations_single_with_request_object.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_with_request_object.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/operations_single_with_request_object.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_with_request_object.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/operations_single_with_request_object_inlined.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_with_request_object_inlined.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/operations_single_with_request_object_inlined.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_with_request_object_inlined.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/operations_single_with_response_object.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_with_response_object.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/operations_single_with_response_object.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_with_response_object.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/operations_single_with_response_object_inlined.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_with_response_object_inlined.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/operations_single_with_response_object_inlined.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_with_response_object_inlined.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/operations_single_with_response_object_inlined_list.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_with_response_object_inlined_list.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/operations_single_with_response_object_inlined_list.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_with_response_object_inlined_list.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/operations_single_with_tag.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_with_tag.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/operations_single_with_tag.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_with_tag.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/operations_single_with_tag_resource_id.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_with_tag_resource_id.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/operations_single_with_tag_resource_id.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_with_tag_resource_id.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/operations_single_with_tag_resource_id_suffix.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_with_tag_resource_id_suffix.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/operations_single_with_tag_resource_id_suffix.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_single_with_tag_resource_id_suffix.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/operations_with_stuttering_names.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_with_stuttering_names.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/operations_with_stuttering_names.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/operations_with_stuttering_names.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/resource_ids_basic.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/resource_ids_basic.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/resource_ids_basic.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/resource_ids_basic.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/resource_ids_common.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/resource_ids_common.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/resource_ids_common.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/resource_ids_common.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/resource_ids_containing_constant.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/resource_ids_containing_constant.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/resource_ids_containing_constant.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/resource_ids_containing_constant.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/resource_ids_containing_hidden_scope.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/resource_ids_containing_hidden_scope.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/resource_ids_containing_hidden_scope.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/resource_ids_containing_hidden_scope.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/resource_ids_containing_hidden_scope_constant.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/resource_ids_containing_hidden_scope_constant.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/resource_ids_containing_hidden_scope_constant.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/resource_ids_containing_hidden_scope_constant.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/resource_ids_containing_hidden_scope_hardcoded_provider.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/resource_ids_containing_hidden_scope_hardcoded_provider.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/resource_ids_containing_hidden_scope_hardcoded_provider.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/resource_ids_containing_hidden_scope_hardcoded_provider.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/resource_ids_containing_hidden_scope_nested.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/resource_ids_containing_hidden_scope_nested.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/resource_ids_containing_hidden_scope_nested.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/resource_ids_containing_hidden_scope_nested.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/resource_ids_containing_hidden_scope_nested_constants.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/resource_ids_containing_hidden_scope_nested_constants.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/resource_ids_containing_hidden_scope_nested_constants.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/resource_ids_containing_hidden_scope_nested_constants.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/resource_ids_containing_hidden_scope_nested_hardcoded_provider.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/resource_ids_containing_hidden_scope_nested_hardcoded_provider.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/resource_ids_containing_hidden_scope_nested_hardcoded_provider.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/resource_ids_containing_hidden_scope_nested_hardcoded_provider.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/resource_ids_containing_hidden_scope_nested_with_extra_segment.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/resource_ids_containing_hidden_scope_nested_with_extra_segment.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/resource_ids_containing_hidden_scope_nested_with_extra_segment.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/resource_ids_containing_hidden_scope_nested_with_extra_segment.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/resource_ids_containing_hidden_scope_nested_with_suffix.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/resource_ids_containing_hidden_scope_nested_with_suffix.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/resource_ids_containing_hidden_scope_nested_with_suffix.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/resource_ids_containing_hidden_scope_nested_with_suffix.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/resource_ids_containing_hidden_scope_with_extra_segment.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/resource_ids_containing_hidden_scope_with_extra_segment.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/resource_ids_containing_hidden_scope_with_extra_segment.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/resource_ids_containing_hidden_scope_with_extra_segment.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/resource_ids_containing_hidden_scope_with_suffix.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/resource_ids_containing_hidden_scope_with_suffix.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/resource_ids_containing_hidden_scope_with_suffix.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/resource_ids_containing_hidden_scope_with_suffix.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/resource_ids_containing_scope.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/resource_ids_containing_scope.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/resource_ids_containing_scope.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/resource_ids_containing_scope.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/resource_ids_lowercased_resource_provider.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/resource_ids_lowercased_resource_provider.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/resource_ids_lowercased_resource_provider.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/resource_ids_lowercased_resource_provider.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/resource_ids_multiple_segments_same_name.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/resource_ids_multiple_segments_same_name.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/resource_ids_multiple_segments_same_name.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/resource_ids_multiple_segments_same_name.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/resource_ids_same_id_different_segment_casing.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/resource_ids_same_id_different_segment_casing.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/resource_ids_same_id_different_segment_casing.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/resource_ids_same_id_different_segment_casing.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/resource_ids_same_uri_different_constant_values_per_operation.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/resource_ids_same_uri_different_constant_values_per_operation.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/resource_ids_same_uri_different_constant_values_per_operation.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/resource_ids_same_uri_different_constant_values_per_operation.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/resource_ids_with_just_suffix.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/resource_ids_with_just_suffix.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/resource_ids_with_just_suffix.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/resource_ids_with_just_suffix.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/resource_ids_with_suffix.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/resource_ids_with_suffix.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/resource_ids_with_suffix.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/resource_ids_with_suffix.json diff --git a/tools/importer-rest-api-specs/components/parser/testdata/resource_ids_with_suffix_multiple_uris.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/resource_ids_with_suffix_multiple_uris.json similarity index 100% rename from tools/importer-rest-api-specs/components/parser/testdata/resource_ids_with_suffix_multiple_uris.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/resource_ids_with_suffix_multiple_uris.json diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testhelpers/parse_swagger_file.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testhelpers/parse_swagger_file.go new file mode 100644 index 00000000000..2adde990627 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testhelpers/parse_swagger_file.go @@ -0,0 +1,43 @@ +package testhelpers + +import ( + "path/filepath" + "testing" + + "github.com/hashicorp/go-azure-helpers/lang/pointer" + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions" + discoveryModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/discovery/models" +) + +func ParseSwaggerFileForTesting(t *testing.T, filePath string, serviceName *string) (*sdkModels.APIVersion, error) { + if serviceName == nil { + serviceName = pointer.To("Example") + } + input := discoveryModels.AvailableDataSet{ + ServiceName: *serviceName, + DataSetsForAPIVersions: map[string]discoveryModels.AvailableDataSetForAPIVersion{ + "2020-01-01": { + APIVersion: "2020-01-01", + ContainsStableAPIVersion: false, + FilePathsContainingAPIDefinitions: []string{ + filepath.Join("testdata", filePath), + }, + }, + }, + ResourceProvider: nil, + } + return ParseDataSetForTesting(t, input, "2020-01-01") +} + +func ParseDataSetForTesting(t *testing.T, input discoveryModels.AvailableDataSet, apiVersion string) (*sdkModels.APIVersion, error) { + result, err := apidefinitions.ParseService(input) + if err != nil { + t.Fatalf(err.Error()) + } + out, ok := result.APIVersions[apiVersion] + if !ok { + t.Fatalf("expected an API Version `%s` but didn't get one", apiVersion) + } + return &out, nil +} diff --git a/tools/importer-rest-api-specs/components/parser/helpers_test.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testhelpers/validate_result_matches.go similarity index 88% rename from tools/importer-rest-api-specs/components/parser/helpers_test.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testhelpers/validate_result_matches.go index 0bff133a4c2..5fb9891f68e 100644 --- a/tools/importer-rest-api-specs/components/parser/helpers_test.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testhelpers/validate_result_matches.go @@ -1,7 +1,4 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package parser +package testhelpers import ( "fmt" @@ -10,48 +7,14 @@ import ( "github.com/hashicorp/go-azure-helpers/lang/pointer" sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" - importerModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/models" ) -func ParseSwaggerFileForTesting(t *testing.T, file string, serviceName *string) (*importerModels.AzureApiDefinition, error) { - // TODO: make this function private - parsed, err := load("testdata/", file) - if err != nil { - t.Fatalf("loading: %+v", err) - } - - var resourceProvider *string = nil // we're not filtering to just this RP, so it's fine - resourceIds, err := parsed.ParseResourceIds(resourceProvider) - if err != nil { - t.Fatalf("parsing Resource Ids: %+v", err) - } - - service := "Example" - if serviceName != nil { - service = *serviceName - } - out, err := parsed.parse(service, "2020-01-01", resourceProvider, *resourceIds) - if err != nil { - t.Fatalf("parsing file %q: %+v", file, err) - } - - // removeUnusedItems used to be called as we iterated through the swagger files - // it's now called once after all the processing for a service has been done so must be called here - // to replicate the entire parsing process for swagger files - out.Resources = removeUnusedItems(out.Resources) - - return out, nil -} - -func validateParsedSwaggerResultMatches(t *testing.T, expected importerModels.AzureApiDefinition, actual *importerModels.AzureApiDefinition) { +func ValidateParsedSwaggerResultMatches(t *testing.T, expected sdkModels.APIVersion, actual *sdkModels.APIVersion) { if actual == nil { t.Fatal("`actual` was nil") } - if actual.ServiceName != expected.ServiceName { - t.Fatalf("expected `ServiceName` to be %q but got %q", expected.ServiceName, actual.ServiceName) - } - if actual.ApiVersion != expected.ApiVersion { - t.Fatalf("expected `ApiVersion` to be %q but got %q", expected.ApiVersion, actual.ApiVersion) + if actual.APIVersion != expected.APIVersion { + t.Fatalf("expected `APIVersion` to be %q but got %q", expected.APIVersion, actual.APIVersion) } validateMapsMatch(t, expected.Resources, actual.Resources, "API Resource", validateParsedApiResourceMatches) diff --git a/tools/importer-rest-api-specs/internal/components/data_test.go b/tools/importer-rest-api-specs/internal/components/data_test.go new file mode 100644 index 00000000000..4009f9af2d1 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/data_test.go @@ -0,0 +1,158 @@ +package components + +import ( + "fmt" + "os" + "testing" + + "github.com/hashicorp/go-azure-helpers/lang/pointer" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/discovery" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/terraform" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/logging" + "github.com/hashicorp/pandora/tools/sdk/config/definitions" + "github.com/hashicorp/pandora/tools/sdk/config/services" +) + +const configurationFilePath = "../../../../config/resource-manager.hcl" +const providerPrefix = "azurerm" +const restAPISpecsDirectory = "../../../../submodules/rest-api-specs" +const terraformDefinitionsDirectory = "../../../../config/resources" + +func init() { + // works around the OAIGen bug + os.Setenv("OAIGEN_DEDUPE", "false") +} + +func TestCanDiscoverFilesForAllServicesFromConfig(t *testing.T) { + servicesFromConfigurationFile, err := services.LoadFromFile(configurationFilePath) + if err != nil { + t.Fatalf("loading config at %q: %+v", configurationFilePath, err) + } + + for _, service := range servicesFromConfigurationFile.Services { + t.Run(service.Name, func(t *testing.T) { + availableDataSet, err := discovery.DiscoverForService(service, restAPISpecsDirectory) + if err != nil { + t.Fatalf("discovering Data for Service %q: %+v", service.Name, err) + } + + t.Logf("Service %q contains %d API Versions", service.Name, len(availableDataSet.DataSetsForAPIVersions)) + }) + } +} + +func TestCanParseFilesForAllServicesFromConfig(t *testing.T) { + // NOTE: this test is an extension on the test above - and whilst we COULD combine them + // it's actually helpful context to know whether we're having an issue loading the files + // or parsing them, so that we know where to look - so I think it's worth having these as + // separate tests for now. + servicesFromConfigurationFile, err := services.LoadFromFile(configurationFilePath) + if err != nil { + t.Fatalf("loading config at %q: %+v", configurationFilePath, err) + } + + for _, service := range servicesFromConfigurationFile.Services { + t.Run(service.Name, func(t *testing.T) { + availableDataSet, err := discovery.DiscoverForService(service, restAPISpecsDirectory) + if err != nil { + t.Fatalf("discovering Data for Service %q: %+v", service.Name, err) + } + + parsedService, err := apidefinitions.ParseService(*availableDataSet) + if err != nil { + t.Fatalf("parsing Data for Service %q: %+v", service.Name, err) + } + + t.Logf("Service %q contains %d API Versions", service.Name, len(availableDataSet.DataSetsForAPIVersions)) + for apiVersion, details := range parsedService.APIVersions { + t.Logf("Service %q API Version %q contains %d APIResources", service.Name, apiVersion, len(details.Resources)) + } + }) + } +} + +func TestCanParseTerraformConfigurations(t *testing.T) { + terraformConfigurations, err := loadTerraformConfigurations(terraformDefinitionsDirectory) + if err != nil { + t.Fatalf(err.Error()) + } + t.Logf("Loaded %d Terraform Configurations", len(terraformConfigurations)) +} + +func TestCanBuildTerraformResources(t *testing.T) { + servicesFromConfigurationFile, err := services.LoadFromFile(configurationFilePath) + if err != nil { + t.Fatalf("loading config at %q: %+v", configurationFilePath, err) + } + + terraformConfigurations, err := loadTerraformConfigurations(terraformDefinitionsDirectory) + if err != nil { + t.Fatalf(err.Error()) + } + + for serviceName, serviceDetails := range terraformConfigurations { + service := findService(servicesFromConfigurationFile, serviceName) + if service == nil { + t.Fatalf("Unable to find the Configuration for the Service %q referenced in Terraform Resources", serviceName) + } + + availableDataSet, err := discovery.DiscoverForService(*service, restAPISpecsDirectory) + if err != nil { + t.Fatalf("discovering Data for Service %q: %+v", serviceName, err) + } + + parsedService, err := apidefinitions.ParseService(*availableDataSet) + if err != nil { + t.Errorf("parsing Data for Service %q: %+v", serviceName, err) + } + + buildTerraform, err := terraform.BuildForService(*parsedService, serviceDetails.resourceLabelToResourceDefinitions, providerPrefix, serviceDetails.terraformPackageName) + if err != nil { + t.Fatalf("building Terraform for Service %q: %+v", serviceName, err) + } + + t.Logf("Service %q contains %d Terraform Resources", serviceName, len(buildTerraform.TerraformDefinition.Resources)) + } +} + +type terraformDetailsForService struct { + resourceLabelToResourceDefinitions map[string]definitions.ResourceDefinition + terraformPackageName *string +} + +func loadTerraformConfigurations(terraformDefinitionsDirectory string) (map[string]terraformDetailsForService, error) { + logging.Debugf("Parsing the Terraform Resource Definitions..") + terraformResourceDefinitions, err := definitions.LoadFromDirectory(terraformDefinitionsDirectory) + if err != nil { + return nil, fmt.Errorf("parsing the Terraform Definitions from %q: %+v", terraformDefinitionsDirectory, err) + } + + // NOTE: when the `definitions` package is refactored, this can be cleaned up + // part of https://github.com/hashicorp/pandora/issues/3754 + servicesToTerraformDetails := make(map[string]terraformDetailsForService) + for serviceName, serviceData := range terraformResourceDefinitions.Services { + terraformResourceDefinition := make(map[string]definitions.ResourceDefinition) + for _, apiVersionData := range serviceData.ApiVersions { + for _, apiResourceData := range apiVersionData.Packages { + for resourceLabel, resourceData := range apiResourceData.Definitions { + terraformResourceDefinition[resourceLabel] = resourceData + } + } + } + servicesToTerraformDetails[serviceName] = terraformDetailsForService{ + resourceLabelToResourceDefinitions: terraformResourceDefinition, + terraformPackageName: pointer.To(serviceData.TerraformPackageName), + } + } + return servicesToTerraformDetails, nil +} + +func findService(input *services.Config, serviceName string) *services.Service { + for _, item := range input.Services { + if item.Name == serviceName { + return &item + } + } + return nil +} diff --git a/tools/importer-rest-api-specs/internal/components/discovery/for_api_version.go b/tools/importer-rest-api-specs/internal/components/discovery/for_api_version.go index 44556cc841c..96925942c75 100644 --- a/tools/importer-rest-api-specs/internal/components/discovery/for_api_version.go +++ b/tools/importer-rest-api-specs/internal/components/discovery/for_api_version.go @@ -13,8 +13,7 @@ import ( "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/logging" ) -// discoverDataSetForAPIVersion parses a set of filePaths and identifies the files which contain API Definitions and -// those containing supplementary information for the API Version in question. +// discoverDataSetForAPIVersion parses a set of filePaths and identifies the files which contain API Definitions for the API Version. func discoverDataSetForAPIVersion(apiVersion string, filePaths []string) (*models.AvailableDataSetForAPIVersion, error) { // handle this being Stable/Preview etc // e.g. /2020-02-01/ to ensure we don't unintentionally also bring in Preview API versions @@ -24,6 +23,27 @@ func discoverDataSetForAPIVersion(apiVersion string, filePaths []string) (*model filePathsForThisAPIVersion := make([]string, 0) for _, filePath := range filePaths { if strings.Contains(filePath, apiVersionDirectory) { + // We need to ignore Examples at this point to handle both TypeSpec and Swagger examples + // TypeSpec examples live within `./{service}/{namespace}/examples/{apiVersion}/{fileName}` + // Swagger examples live within `./{service}/resource-manager/(stable|preview)/{apiVersion}/examples/{fileName}` + // So just handling the directory name here is fine + shouldIgnore := false + for _, item := range strings.Split(filePath, fmt.Sprintf("%c", filepath.Separator)) { + if strings.EqualFold(item, "data-plane") { + logging.Tracef("File contains `data-plane`, skipping..") + shouldIgnore = true + break + } + if strings.EqualFold(item, "examples") { + logging.Tracef("File contains examples, skipping..") + shouldIgnore = true + break + } + } + if shouldIgnore { + continue + } + filePathsForThisAPIVersion = append(filePathsForThisAPIVersion, filePath) } } @@ -33,7 +53,6 @@ func discoverDataSetForAPIVersion(apiVersion string, filePaths []string) (*model // now that we know which files are available, let's go through and bucket them filePathsContainingAPIDefinitions := make([]string, 0) - filePathsContainingSupplementaryData := make([]string, 0) for _, filePath := range filePathsForThisAPIVersion { logging.Tracef("Processing %q..", filePath) // However since there's multiple different directory structures, we only need to pull out the files within the directory for this API Version @@ -53,36 +72,16 @@ func discoverDataSetForAPIVersion(apiVersion string, filePaths []string) (*model continue } - shouldIgnore := false - for _, item := range components { - if strings.EqualFold(item, "examples") { - logging.Tracef("File contains examples, skipping..") - shouldIgnore = true - break - } - } - if shouldIgnore { - continue - } - - withinASubDirectory := len(components) > 1 - if withinASubDirectory { - // anything located within a sub-directory that hasn't been ignored will be supplementary data - filePathsContainingSupplementaryData = append(filePathsContainingSupplementaryData, filePath) - } else { - filePathsContainingAPIDefinitions = append(filePathsContainingAPIDefinitions, filePath) - } + filePathsContainingAPIDefinitions = append(filePathsContainingAPIDefinitions, filePath) } sort.Strings(filePathsContainingAPIDefinitions) - sort.Strings(filePathsContainingSupplementaryData) containsStableAPIVersion := isStableAPIVersion(apiVersion) return &models.AvailableDataSetForAPIVersion{ - APIVersion: apiVersion, - ContainsStableAPIVersion: containsStableAPIVersion, - FilePathsContainingAPIDefinitions: filePathsContainingAPIDefinitions, - FilePathsContainingSupplementaryData: filePathsContainingSupplementaryData, + APIVersion: apiVersion, + ContainsStableAPIVersion: containsStableAPIVersion, + FilePathsContainingAPIDefinitions: filePathsContainingAPIDefinitions, }, nil } diff --git a/tools/importer-rest-api-specs/internal/components/discovery/for_api_version_test.go b/tools/importer-rest-api-specs/internal/components/discovery/for_api_version_test.go index f26af20964f..1b6ff7d1b54 100644 --- a/tools/importer-rest-api-specs/internal/components/discovery/for_api_version_test.go +++ b/tools/importer-rest-api-specs/internal/components/discovery/for_api_version_test.go @@ -15,6 +15,7 @@ import ( func TestDiscoverDataSetForAPIVersion_IgnoresExamples(t *testing.T) { apiVersion := "2020-01-01" filePaths := []string{ + "specification/compute/Compute.Management/examples/2020-01-01/VirtualMachine_Get.json", "specification/compute/resource-manager/stable/2020-01-01/virtualMachines.json", "specification/compute/resource-manager/stable/2020-01-01/examples/VirtualMachine_Get.json", } @@ -24,7 +25,6 @@ func TestDiscoverDataSetForAPIVersion_IgnoresExamples(t *testing.T) { FilePathsContainingAPIDefinitions: []string{ "specification/compute/resource-manager/stable/2020-01-01/virtualMachines.json", }, - FilePathsContainingSupplementaryData: make([]string, 0), } logging.Log = hclog.New(hclog.DefaultOptions) actual, err := discoverDataSetForAPIVersion(apiVersion, filePaths) @@ -54,36 +54,6 @@ func TestDiscoverDataSetForAPIVersion_IgnoresNonJSONFiles(t *testing.T) { FilePathsContainingAPIDefinitions: []string{ "specification/compute/resource-manager/stable/2020-01-01/virtualMachines.json", }, - FilePathsContainingSupplementaryData: make([]string, 0), - } - logging.Log = hclog.New(hclog.DefaultOptions) - actual, err := discoverDataSetForAPIVersion(apiVersion, filePaths) - if err != nil { - t.Fatalf("unexpected error: %+v", err) - } - if actual == nil { - t.Fatalf("expected a value but got nil") - } - if !reflect.DeepEqual(expected, *actual) { - t.Fatalf("expected and actual did not match - expected |%+v| but got |%+v", expected, *actual) - } -} - -func TestDiscoverDataSetForAPIVersion_IdentifiesDiscriminatedTypesAsSupplementaryData(t *testing.T) { - apiVersion := "2020-01-01" - filePaths := []string{ - "specification/compute/resource-manager/stable/2020-01-01/virtualMachines.json", - "specification/compute/resource-manager/stable/2020-01-01/entities/virtualMachineTypes.json", - } - expected := models.AvailableDataSetForAPIVersion{ - APIVersion: "2020-01-01", - ContainsStableAPIVersion: true, - FilePathsContainingAPIDefinitions: []string{ - "specification/compute/resource-manager/stable/2020-01-01/virtualMachines.json", - }, - FilePathsContainingSupplementaryData: []string{ - "specification/compute/resource-manager/stable/2020-01-01/entities/virtualMachineTypes.json", - }, } logging.Log = hclog.New(hclog.DefaultOptions) actual, err := discoverDataSetForAPIVersion(apiVersion, filePaths) @@ -110,7 +80,6 @@ func TestDiscoverDataSetForAPIVersion_PreviewAPIVersion(t *testing.T) { FilePathsContainingAPIDefinitions: []string{ "specification/compute/resource-manager/stable/2020-01-01-preview/virtualMachines.json", }, - FilePathsContainingSupplementaryData: make([]string, 0), } logging.Log = hclog.New(hclog.DefaultOptions) actual, err := discoverDataSetForAPIVersion(apiVersion, filePaths) @@ -137,7 +106,6 @@ func TestDiscoverDataSetForAPIVersion_StableAPIVersion(t *testing.T) { FilePathsContainingAPIDefinitions: []string{ "specification/compute/resource-manager/stable/2020-01-01/virtualMachines.json", }, - FilePathsContainingSupplementaryData: make([]string, 0), } logging.Log = hclog.New(hclog.DefaultOptions) actual, err := discoverDataSetForAPIVersion(apiVersion, filePaths) diff --git a/tools/importer-rest-api-specs/internal/components/discovery/for_service.go b/tools/importer-rest-api-specs/internal/components/discovery/for_service.go index 2aa71582cfe..8fc000e1745 100644 --- a/tools/importer-rest-api-specs/internal/components/discovery/for_service.go +++ b/tools/importer-rest-api-specs/internal/components/discovery/for_service.go @@ -51,7 +51,7 @@ func DiscoverForService(service services.Service, workingDirectory string) (*mod if err != nil { return nil, fmt.Errorf("discovering the Data Set for the API Version %q for Service %q: %+v", apiVersion, service.Name, err) } - logging.Tracef("Identified %d API Definitions and %d sets of supplementary data for API Version %q..", len(dataSet.FilePathsContainingAPIDefinitions), len(dataSet.FilePathsContainingSupplementaryData), apiVersion) + logging.Tracef("Identified %d API Definitions for API Version %q..", len(dataSet.FilePathsContainingAPIDefinitions), apiVersion) dataSetsForAPIVersions[apiVersion] = *dataSet } diff --git a/tools/importer-rest-api-specs/internal/components/discovery/models/available_data_set_for_api_version.go b/tools/importer-rest-api-specs/internal/components/discovery/models/available_data_set_for_api_version.go index cdaf998c0c4..9c4f8809603 100644 --- a/tools/importer-rest-api-specs/internal/components/discovery/models/available_data_set_for_api_version.go +++ b/tools/importer-rest-api-specs/internal/components/discovery/models/available_data_set_for_api_version.go @@ -19,9 +19,4 @@ type AvailableDataSetForAPIVersion struct { // FilePathsContainingAPIDefinitions is a slice of the absolute file paths which contain the APIDefinitions // for the Service/API Version combination. FilePathsContainingAPIDefinitions []string - - // FilePathsContainingSupplementaryData is a slice of the absolute file paths which contain supplementary data - // related to the APIDefinitions for the Service/API Version combination. These should be parsed prior to - // parsing the files within FilePathsContainingAPIDefinitions - and typically contain Discriminated Models. - FilePathsContainingSupplementaryData []string } diff --git a/tools/importer-rest-api-specs/internal/components/discovery/resource_provider.go b/tools/importer-rest-api-specs/internal/components/discovery/resource_provider.go index ccca4086006..8f171baff69 100644 --- a/tools/importer-rest-api-specs/internal/components/discovery/resource_provider.go +++ b/tools/importer-rest-api-specs/internal/components/discovery/resource_provider.go @@ -5,11 +5,11 @@ package discovery import ( "fmt" - "github.com/hashicorp/go-azure-helpers/lang/pointer" "path/filepath" "sort" "strings" + "github.com/hashicorp/go-azure-helpers/lang/pointer" "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/logging" ) diff --git a/tools/importer-rest-api-specs/internal/components/terraform/identification/helpers.go b/tools/importer-rest-api-specs/internal/components/terraform/identification/helpers.go index b6988a1b0ba..ed5bd20dc9e 100644 --- a/tools/importer-rest-api-specs/internal/components/terraform/identification/helpers.go +++ b/tools/importer-rest-api-specs/internal/components/terraform/identification/helpers.go @@ -7,7 +7,7 @@ import ( "fmt" "github.com/hashicorp/go-azure-helpers/lang/pointer" - "github.com/hashicorp/pandora/tools/data-api-sdk/v1/helpers" + sdkHelpers "github.com/hashicorp/pandora/tools/data-api-sdk/v1/helpers" sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" ) @@ -58,7 +58,7 @@ func modelContainsDiscriminatedTypes(model sdkModels.SDKModel, apiResource sdkMo return true } - topLevelObjectDefinition := helpers.InnerMostSDKObjectDefinition(field.ObjectDefinition) + topLevelObjectDefinition := sdkHelpers.InnerMostSDKObjectDefinition(field.ObjectDefinition) if topLevelObjectDefinition.Type != sdkModels.ReferenceSDKObjectDefinitionType { continue } diff --git a/tools/importer-rest-api-specs/internal/components/terraform/identification/methods.go b/tools/importer-rest-api-specs/internal/components/terraform/identification/methods.go index bafd8811d02..6ea56c44410 100644 --- a/tools/importer-rest-api-specs/internal/components/terraform/identification/methods.go +++ b/tools/importer-rest-api-specs/internal/components/terraform/identification/methods.go @@ -7,7 +7,7 @@ import ( "fmt" "strings" - "github.com/hashicorp/pandora/tools/data-api-sdk/v1/helpers" + sdkHelpers "github.com/hashicorp/pandora/tools/data-api-sdk/v1/helpers" sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/logging" "github.com/hashicorp/pandora/tools/sdk/config/definitions" @@ -49,7 +49,7 @@ func identifyMethodsForAPIResource(input sdkModels.APIResource, resourceMetaData if v != "update" && strings.HasPrefix(v, "update") { continue } - objectDefinition := helpers.InnerMostSDKObjectDefinition(*operation.RequestObject) + objectDefinition := sdkHelpers.InnerMostSDKObjectDefinition(*operation.RequestObject) if objectDefinition.Type != sdkModels.ReferenceSDKObjectDefinitionType { continue } diff --git a/tools/importer-rest-api-specs/internal/components/terraform/schema/build.go b/tools/importer-rest-api-specs/internal/components/terraform/schema/build.go index ab907011393..07b8d927c14 100644 --- a/tools/importer-rest-api-specs/internal/components/terraform/schema/build.go +++ b/tools/importer-rest-api-specs/internal/components/terraform/schema/build.go @@ -22,8 +22,8 @@ func Build(input models.WorkInProgressData) (*models.WorkInProgressData, error) return nil, fmt.Errorf("building the Terraform Schema: %+v", err) } - logging.Tracef("The Resource %q has %d models", resourceLabel, len(*schemaModels)) - resource.Resource.SchemaModels = *schemaModels + logging.Tracef("The Resource %q has %d models", resourceLabel, len(schemaModels)) + resource.Resource.SchemaModels = schemaModels resource.Resource.Mappings = *mappings input.Resources[resourceLabel] = resource diff --git a/tools/importer-rest-api-specs/internal/components/terraform/schema/build_resource_group_test.go b/tools/importer-rest-api-specs/internal/components/terraform/schema/build_resource_group_test.go index 98d01a2829c..251d492112a 100644 --- a/tools/importer-rest-api-specs/internal/components/terraform/schema/build_resource_group_test.go +++ b/tools/importer-rest-api-specs/internal/components/terraform/schema/build_resource_group_test.go @@ -336,7 +336,7 @@ func TestBuildForResourceGroupUsingRealData(t *testing.T) { testValidateResourceGroupSchema(t, actualModels, actualMappings) } -func testValidateResourceGroupSchema(t *testing.T, actualModels *map[string]sdkModels.TerraformSchemaModel, actualMappings *sdkModels.TerraformMappingDefinition) { +func testValidateResourceGroupSchema(t *testing.T, actualModels map[string]sdkModels.TerraformSchemaModel, actualMappings *sdkModels.TerraformMappingDefinition) { r := resourceUnderTest{ Name: "Resource Group", } @@ -344,11 +344,11 @@ func testValidateResourceGroupSchema(t *testing.T, actualModels *map[string]sdkM if actualModels == nil { t.Fatalf("expected 1 model but got nil") } - if len(*actualModels) != 1 { - t.Errorf("expected 1 model but got %d", len(*actualModels)) + if len(actualModels) != 1 { + t.Errorf("expected 1 model but got %d", len(actualModels)) } r.CurrentModel = "ResourceGroupResource" - currentModel, ok := (*actualModels)[r.CurrentModel] + currentModel, ok := (actualModels)[r.CurrentModel] if !ok { t.Errorf("top level model %q missing", r.CurrentModel) } else { @@ -361,7 +361,7 @@ func testValidateResourceGroupSchema(t *testing.T, actualModels *map[string]sdkM } r.CurrentModel = "ResourceGroupResourceGroupProperties" - currentModel, ok = (*actualModels)[r.CurrentModel] + currentModel, ok = (actualModels)[r.CurrentModel] if ok { t.Errorf("expected model %q to be removed but it was present", r.CurrentModel) } diff --git a/tools/importer-rest-api-specs/internal/components/terraform/schema/builder.go b/tools/importer-rest-api-specs/internal/components/terraform/schema/builder.go index 84fd8c14203..db30abbf228 100644 --- a/tools/importer-rest-api-specs/internal/components/terraform/schema/builder.go +++ b/tools/importer-rest-api-specs/internal/components/terraform/schema/builder.go @@ -6,7 +6,7 @@ package schema import ( "fmt" - "github.com/hashicorp/pandora/tools/data-api-sdk/v1/helpers" + sdkHelpers "github.com/hashicorp/pandora/tools/data-api-sdk/v1/helpers" sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" terraformHelpers "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/terraform/schema/helpers" "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/terraform/schema/processors" @@ -35,7 +35,7 @@ func NewBuilder(apiResource sdkModels.APIResource) Builder { } // Build produces a map of TerraformSchemaModelDefinitions which comprise the Schema for this Resource -func (b Builder) Build(input sdkModels.TerraformResourceDefinition, resourceDefinition definitions.ResourceDefinition) (*map[string]sdkModels.TerraformSchemaModel, *sdkModels.TerraformMappingDefinition, error) { +func (b Builder) Build(input sdkModels.TerraformResourceDefinition, resourceDefinition definitions.ResourceDefinition) (map[string]sdkModels.TerraformSchemaModel, *sdkModels.TerraformMappingDefinition, error) { mappings := sdkModels.TerraformMappingDefinition{ Fields: []sdkModels.TerraformFieldMappingDefinition{}, ResourceID: []sdkModels.TerraformResourceIDMappingDefinition{}, @@ -90,7 +90,7 @@ func (b Builder) Build(input sdkModels.TerraformResourceDefinition, resourceDefi if err != nil { return nil, nil, fmt.Errorf("processing models: %+v", err) } - schemaModels = *updatedSchemaModels + schemaModels = updatedSchemaModels mappings = *updatedMappings } @@ -98,7 +98,7 @@ func (b Builder) Build(input sdkModels.TerraformResourceDefinition, resourceDefi for fieldName, field := range schemaModels[modelName].Fields { field.HCLName = terraformHelpers.ConvertToSnakeCase(fieldName) fieldsWithHclNames[fieldName] = field - objectDefinition := helpers.InnerMostTerraformSchemaObjectDefinition(field.ObjectDefinition) + objectDefinition := sdkHelpers.InnerMostTerraformSchemaObjectDefinition(field.ObjectDefinition) if objectDefinition.Type == sdkModels.ReferenceTerraformSchemaObjectDefinitionType { if objectDefinition.ReferenceName == nil { return nil, nil, fmt.Errorf("the Field %q within Model %q was a Reference with no ReferenceName", fieldName, modelName) @@ -125,7 +125,7 @@ func (b Builder) Build(input sdkModels.TerraformResourceDefinition, resourceDefi return outputSchemaModels, outputMappings, nil } -func (b Builder) removeUnusedModelsAndMappings(input sdkModels.TerraformResourceDefinition, schemaModels map[string]sdkModels.TerraformSchemaModel, mappings sdkModels.TerraformMappingDefinition) (*map[string]sdkModels.TerraformSchemaModel, *sdkModels.TerraformMappingDefinition, error) { +func (b Builder) removeUnusedModelsAndMappings(input sdkModels.TerraformResourceDefinition, schemaModels map[string]sdkModels.TerraformSchemaModel, mappings sdkModels.TerraformMappingDefinition) (map[string]sdkModels.TerraformSchemaModel, *sdkModels.TerraformMappingDefinition, error) { unusedModels := make(map[string]struct{}, 0) // first assume everything is unused for modelName := range schemaModels { @@ -134,7 +134,7 @@ func (b Builder) removeUnusedModelsAndMappings(input sdkModels.TerraformResource for _, model := range schemaModels { for _, field := range model.Fields { - objectDefinition := helpers.InnerMostTerraformSchemaObjectDefinition(field.ObjectDefinition) + objectDefinition := sdkHelpers.InnerMostTerraformSchemaObjectDefinition(field.ObjectDefinition) if objectDefinition.Type == sdkModels.ReferenceTerraformSchemaObjectDefinitionType { // TODO: we should check if this is a const too delete(unusedModels, *objectDefinition.ReferenceName) @@ -163,7 +163,7 @@ func (b Builder) removeUnusedModelsAndMappings(input sdkModels.TerraformResource } mappings = *outputMappings - return &schemaModels, &mappings, nil + return schemaModels, &mappings, nil } func (b Builder) removeUnusedModelToModelMappings(input sdkModels.TerraformMappingDefinition) (*sdkModels.TerraformMappingDefinition, error) { @@ -185,7 +185,7 @@ func (b Builder) removeUnusedModelToModelMappings(input sdkModels.TerraformMappi if !ok { return nil, fmt.Errorf("field %q was not found in SDK Model %q", v.ModelToModel.SDKFieldName, v.ModelToModel.SDKModelName) } - objectDefinition := helpers.InnerMostSDKObjectDefinition(sdkField.ObjectDefinition) + objectDefinition := sdkHelpers.InnerMostSDKObjectDefinition(sdkField.ObjectDefinition) if objectDefinition.Type != sdkModels.ReferenceSDKObjectDefinitionType { // nothing to do here, move along now. output.Fields = append(output.Fields, mapping) @@ -257,27 +257,25 @@ func (b Builder) schemaFromTopLevelModel(input sdkModels.TerraformResourceDefini return nil, fmt.Errorf("parsing top-level fields from create/read/update: %+v", err) } - schemaFields := *fields - resourceId, ok := b.apiResource.ResourceIDs[input.ResourceIDName] if !ok { return nil, fmt.Errorf("couldn't find Resource ID named %q", input.ResourceIDName) } fieldsWithinResourceId, mappings, err := b.identifyTopLevelFieldsWithinResourceID(resourceId, mappings, input.DisplayName, resourceDefinition) if err != nil { - displayValueForResourceId := helpers.DisplayValueForResourceID(resourceId) + displayValueForResourceId := sdkHelpers.DisplayValueForResourceID(resourceId) return nil, fmt.Errorf("identifying top level fields within Resource ID %q: %+v", displayValueForResourceId, err) } - for k, v := range *fieldsWithinResourceId { - schemaFields[k] = v + for k, v := range fieldsWithinResourceId { + fields[k] = v } fieldsWithinProperties, mappings, err := b.identifyFieldsWithinPropertiesBlock(input.SchemaModelName, *createReadUpdateMethods, &input, mappings, resourceDefinition) if err != nil { return nil, fmt.Errorf("parsing fields within the `properties` block for the create/read/update methods: %+v", err) } - for k, v := range *fieldsWithinProperties { - schemaFields[k] = v + for k, v := range fieldsWithinProperties { + fields[k] = v } modelsUsedWithinProperties, mappings, err := b.identifyModelsWithinPropertiesBlock(*createReadUpdateMethods, mappings) @@ -292,13 +290,13 @@ func (b Builder) schemaFromTopLevelModel(input sdkModels.TerraformResourceDefini return &modelParseResult{ mappings: *mappings, model: sdkModels.TerraformSchemaModel{ - Fields: schemaFields, + Fields: fields, }, - nestedModels: *modelsUsedWithinProperties, + nestedModels: modelsUsedWithinProperties, }, nil } -func (b Builder) identifyModelsWithinPropertiesBlock(payloads operationPayloads, mappings *sdkModels.TerraformMappingDefinition) (*map[string]sdkModels.SDKModel, *sdkModels.TerraformMappingDefinition, error) { +func (b Builder) identifyModelsWithinPropertiesBlock(payloads operationPayloads, mappings *sdkModels.TerraformMappingDefinition) (map[string]sdkModels.SDKModel, *sdkModels.TerraformMappingDefinition, error) { allFields := make(map[string]sdkModels.SDKField) for fieldName, field := range payloads.readPayload.Fields { if _, ok := allFields[fieldName]; ok { @@ -324,7 +322,7 @@ func (b Builder) identifyModelsWithinPropertiesBlock(payloads operationPayloads, return nil, nil, nil } - for k, v := range *modelsWithinField { + for k, v := range modelsWithinField { if other, ok := allModels[k]; ok { if !modelsMatch(v, other) { return nil, nil, fmt.Errorf("duplicate models named %q were parsed with different fields: %+v / %+v", k, v.Fields, other.Fields) @@ -335,7 +333,7 @@ func (b Builder) identifyModelsWithinPropertiesBlock(payloads operationPayloads, } } - return &allModels, mappings, nil + return allModels, mappings, nil } func modelsMatch(first sdkModels.SDKModel, second sdkModels.SDKModel) bool { @@ -465,10 +463,10 @@ func (b Builder) buildNestedModelDefinition(schemaModelName, topLevelModelName, }, &mappings, nil } -func (b Builder) identifyModelsWithinField(field sdkModels.SDKField, knownModels map[string]sdkModels.SDKModel) (*map[string]sdkModels.SDKModel, error) { +func (b Builder) identifyModelsWithinField(field sdkModels.SDKField, knownModels map[string]sdkModels.SDKModel) (map[string]sdkModels.SDKModel, error) { out := make(map[string]sdkModels.SDKModel, 0) - objectDefinition := helpers.InnerMostSDKObjectDefinition(field.ObjectDefinition) + objectDefinition := sdkHelpers.InnerMostSDKObjectDefinition(field.ObjectDefinition) if objectDefinition.ReferenceName != nil { // we need to identify both this model and any models nested within it allModels := make(map[string]sdkModels.SDKModel) @@ -507,7 +505,7 @@ func (b Builder) identifyModelsWithinField(field sdkModels.SDKField, knownModels // something within it was marked as ignore return nil, nil } - for k, v := range *nestedModels { + for k, v := range nestedModels { out[k] = v allModels[k] = v } @@ -515,7 +513,7 @@ func (b Builder) identifyModelsWithinField(field sdkModels.SDKField, knownModels } } - return &out, nil + return out, nil } func objectDefinitionShouldBeSkipped(input sdkModels.SDKObjectDefinitionType) bool { diff --git a/tools/importer-rest-api-specs/internal/components/terraform/schema/fields_nested.go b/tools/importer-rest-api-specs/internal/components/terraform/schema/fields_nested.go index 1045bb82115..04d5222fd5f 100644 --- a/tools/importer-rest-api-specs/internal/components/terraform/schema/fields_nested.go +++ b/tools/importer-rest-api-specs/internal/components/terraform/schema/fields_nested.go @@ -13,7 +13,7 @@ import ( "github.com/hashicorp/pandora/tools/sdk/config/definitions" ) -func (b Builder) identifyFieldsWithinPropertiesBlock(schemaModelName string, input operationPayloads, resource *sdkModels.TerraformResourceDefinition, mappings *sdkModels.TerraformMappingDefinition, resourceDefinition definitions.ResourceDefinition) (*map[string]sdkModels.TerraformSchemaField, *sdkModels.TerraformMappingDefinition, error) { +func (b Builder) identifyFieldsWithinPropertiesBlock(schemaModelName string, input operationPayloads, resource *sdkModels.TerraformResourceDefinition, mappings *sdkModels.TerraformMappingDefinition, resourceDefinition definitions.ResourceDefinition) (map[string]sdkModels.TerraformSchemaField, *sdkModels.TerraformMappingDefinition, error) { allFields := make(map[string]struct{}, 0) propertiesPayloads := input.createReadUpdatePayloadsProperties() for _, model := range propertiesPayloads { @@ -177,7 +177,7 @@ func (b Builder) identifyFieldsWithinPropertiesBlock(schemaModelName string, inp } } - return &out, mappings, nil + return out, mappings, nil } func fieldExists(payload sdkModels.SDKModel, fieldName string) bool { diff --git a/tools/importer-rest-api-specs/internal/components/terraform/schema/fields_resource_id.go b/tools/importer-rest-api-specs/internal/components/terraform/schema/fields_resource_id.go index 9d9efd630ce..afac3cad270 100644 --- a/tools/importer-rest-api-specs/internal/components/terraform/schema/fields_resource_id.go +++ b/tools/importer-rest-api-specs/internal/components/terraform/schema/fields_resource_id.go @@ -7,14 +7,14 @@ import ( "fmt" "strings" - "github.com/hashicorp/pandora/tools/data-api-sdk/v1/helpers" + sdkHelpers "github.com/hashicorp/pandora/tools/data-api-sdk/v1/helpers" sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" terraformHelpers "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/terraform/schema/helpers" "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/logging" "github.com/hashicorp/pandora/tools/sdk/config/definitions" ) -func (b Builder) identifyTopLevelFieldsWithinResourceID(input sdkModels.ResourceID, mappings *sdkModels.TerraformMappingDefinition, displayName string, resourceDefinition definitions.ResourceDefinition) (*map[string]sdkModels.TerraformSchemaField, *sdkModels.TerraformMappingDefinition, error) { +func (b Builder) identifyTopLevelFieldsWithinResourceID(input sdkModels.ResourceID, mappings *sdkModels.TerraformMappingDefinition, displayName string, resourceDefinition definitions.ResourceDefinition) (map[string]sdkModels.TerraformSchemaField, *sdkModels.TerraformMappingDefinition, error) { out := make(map[string]sdkModels.TerraformSchemaField) overrides := make([]definitions.Override, 0) @@ -152,11 +152,11 @@ func (b Builder) identifyTopLevelFieldsWithinResourceID(input sdkModels.Resource } if userConfigurableSegments == 0 { - return nil, nil, fmt.Errorf("no user-configurable segments were found in the Resource ID %q", helpers.DisplayValueForResourceID(input)) + return nil, nil, fmt.Errorf("no user-configurable segments were found in the Resource ID %q", sdkHelpers.DisplayValueForResourceID(input)) } } - return &out, mappings, nil + return out, mappings, nil } func descriptionForResourceIDSegment(input, resourceDisplayName string, overrides []definitions.Override) string { diff --git a/tools/importer-rest-api-specs/internal/components/terraform/schema/fields_resource_id_test.go b/tools/importer-rest-api-specs/internal/components/terraform/schema/fields_resource_id_test.go index 1a025f3800e..dc02c5dd3ac 100644 --- a/tools/importer-rest-api-specs/internal/components/terraform/schema/fields_resource_id_test.go +++ b/tools/importer-rest-api-specs/internal/components/terraform/schema/fields_resource_id_test.go @@ -28,10 +28,10 @@ func TestTopLevelFieldsWithinResourceId_NoSegmentsShouldError(t *testing.T) { t.Fatalf("expected an error but didn't get one") } if actualFields != nil { - t.Fatalf("expected actualFields to be nil when an error is returned but got %+v", *actualFields) + t.Fatalf("expected actualFields to be nil when an error is returned but got %+v", actualFields) } if actualMappings != nil { - t.Fatalf("expected actualMappings to be nil when an error is returned but got %+v", *actualMappings) + t.Fatalf("expected actualMappings to be nil when an error is returned but got %+v", actualMappings) } } @@ -58,10 +58,10 @@ func TestTopLevelFieldsWithinResourceId_ResourceGroup(t *testing.T) { if actualFields == nil { t.Fatalf("expected actualFields to be non-nil but was nil") } - if len(*actualFields) != 1 { - t.Fatalf("expected actualFields to contain 1 item but got %d", len(*actualFields)) + if len(actualFields) != 1 { + t.Fatalf("expected actualFields to contain 1 item but got %d", len(actualFields)) } - name, ok := (*actualFields)["Name"] + name, ok := (actualFields)["Name"] if !ok { t.Fatalf("expected there to be a field called `Name` but there wasn't") } @@ -118,10 +118,10 @@ func TestTopLevelFieldsWithinResourceId_VirtualMachine(t *testing.T) { if actualFields == nil { t.Fatalf("expected actualFields to be non-nil but was nil") } - if len(*actualFields) != 2 { - t.Fatalf("expected actualFields to contain 2 items but got %d", len(*actualFields)) + if len(actualFields) != 2 { + t.Fatalf("expected actualFields to contain 2 items but got %d", len(actualFields)) } - resourceGroupName, ok := (*actualFields)["ResourceGroupName"] + resourceGroupName, ok := (actualFields)["ResourceGroupName"] if !ok { t.Fatalf("expected there to be a field called `ResourceGroupName` but there wasn't") } @@ -138,7 +138,7 @@ func TestTopLevelFieldsWithinResourceId_VirtualMachine(t *testing.T) { t.Fatalf("expected the description for `ResourceGroupName` to be `Specifies the name of the Resource Group within which this Virtual Machine should exist.` but got %q", resourceGroupName.Documentation.Markdown) } - name, ok := (*actualFields)["Name"] + name, ok := (actualFields)["Name"] if !ok { t.Fatalf("expected there to be a field called `Name` but there wasn't") } @@ -215,16 +215,16 @@ func TestTopLevelFieldsWithinResourceId_VirtualMachineExtension(t *testing.T) { if actualFields == nil { t.Fatalf("expected actualFields to be non-nil but was nil") } - if len(*actualFields) != 2 { - t.Fatalf("expected actualFields to contain 2 items but got %d", len(*actualFields)) + if len(actualFields) != 2 { + t.Fatalf("expected actualFields to contain 2 items but got %d", len(actualFields)) } - _, ok := (*actualFields)["ResourceGroupName"] + _, ok := (actualFields)["ResourceGroupName"] if ok { t.Fatalf("expected the field `ResourceGroupName` be absent") } - virtualMachineId, ok := (*actualFields)["VirtualMachineId"] + virtualMachineId, ok := (actualFields)["VirtualMachineId"] if !ok { t.Fatalf("expected there to be a field called `VirtualMachineId` but there wasn't") } @@ -241,7 +241,7 @@ func TestTopLevelFieldsWithinResourceId_VirtualMachineExtension(t *testing.T) { t.Fatalf("expected the description for `VirtualMachineId` to be `Specifies the Virtual Machine Id within which this Virtual Machine Extension should exist.` but got %q", virtualMachineId.Documentation.Markdown) } - name, ok := (*actualFields)["Name"] + name, ok := (actualFields)["Name"] if !ok { t.Fatalf("expected there to be a field called `Name` but there wasn't") } @@ -320,14 +320,14 @@ func TestTopLevelFieldsWithinResourceId_KubernetesTrustedAccessRoleBinding(t *te if actualFields == nil { t.Fatalf("expected actualFields to be non-nil but was nil") } - if len(*actualFields) != 2 { - t.Fatalf("expected actualFields to contain 2 items but got %d", len(*actualFields)) + if len(actualFields) != 2 { + t.Fatalf("expected actualFields to contain 2 items but got %d", len(actualFields)) } - _, ok := (*actualFields)["ResourceGroupName"] + _, ok := (actualFields)["ResourceGroupName"] if ok { t.Fatalf("expected the field `ResourceGroupName` be absent") } - kubernetesClusterId, ok := (*actualFields)["KubernetesClusterId"] + kubernetesClusterId, ok := (actualFields)["KubernetesClusterId"] if !ok { t.Fatalf("expected there to be a field called `KubernetesClusterId`") } @@ -344,7 +344,7 @@ func TestTopLevelFieldsWithinResourceId_KubernetesTrustedAccessRoleBinding(t *te t.Fatalf("expected the description for `KubernetesClusterId` to be `Specifies the Kubernetes Cluster Id within which this Kubernetes Cluster Trusted Access Role Binding should exist.` but got %q", kubernetesClusterId.Documentation.Markdown) } - name, ok := (*actualFields)["Name"] + name, ok := (actualFields)["Name"] if !ok { t.Fatalf("expected there to be a field called `Name` but there wasn't") } @@ -432,14 +432,14 @@ func TestTopLevelFieldsWithinResourceId_ParentIdSchemaOverride(t *testing.T) { if actualFields == nil { t.Fatalf("expected actualFields to be non-nil but was nil") } - if len(*actualFields) != 2 { - t.Fatalf("expected actualFields to contain 2 items but got %d", len(*actualFields)) + if len(actualFields) != 2 { + t.Fatalf("expected actualFields to contain 2 items but got %d", len(actualFields)) } - _, ok := (*actualFields)["ResourceGroupName"] + _, ok := (actualFields)["ResourceGroupName"] if ok { t.Fatalf("expected the field `ResourceGroupName` be absent") } - kubernetesClusterId, ok := (*actualFields)["RenamedClusterId"] + kubernetesClusterId, ok := (actualFields)["RenamedClusterId"] if !ok { t.Fatalf("expected there to be a field called `RenamedClusterId`") } @@ -456,7 +456,7 @@ func TestTopLevelFieldsWithinResourceId_ParentIdSchemaOverride(t *testing.T) { t.Fatalf("expected the description for `RenamedClusterId` to be `Specifies the Renamed Cluster Id within which this Kubernetes Cluster Trusted Access Role Binding should exist.` but got %q", kubernetesClusterId.Documentation.Markdown) } - name, ok := (*actualFields)["Name"] + name, ok := (actualFields)["Name"] if !ok { t.Fatalf("expected there to be a field called `Name` but there wasn't") } @@ -527,10 +527,10 @@ func TestTopLevelFieldsWithinResourceId_SchemaOverride(t *testing.T) { if actualFields == nil { t.Fatalf("expected actualFields to be non-nil but was nil") } - if len(*actualFields) != 2 { - t.Fatalf("expected actualFields to contain 2 items but got %d", len(*actualFields)) + if len(actualFields) != 2 { + t.Fatalf("expected actualFields to contain 2 items but got %d", len(actualFields)) } - resourceGroupName, ok := (*actualFields)["ResourceGroupName"] + resourceGroupName, ok := (actualFields)["ResourceGroupName"] if !ok { t.Fatalf("expected there to be a field called `ResourceGroupName` but there wasn't") } @@ -547,7 +547,7 @@ func TestTopLevelFieldsWithinResourceId_SchemaOverride(t *testing.T) { t.Fatalf("expected the description for `ResourceGroupName` to be `Specifies the name of the Resource Group within which this Chaos Studio Target should exist.` but got %q", resourceGroupName.Documentation.Markdown) } - targetType, ok := (*actualFields)["TargetType"] + targetType, ok := (actualFields)["TargetType"] if !ok { t.Fatalf("expected there to be a field called `TargetType` but there wasn't") } @@ -618,10 +618,10 @@ func TestTopLevelFieldsWithinResourceId_DocumentationOverride(t *testing.T) { if actualFields == nil { t.Fatalf("expected actualFields to be non-nil but was nil") } - if len(*actualFields) != 2 { - t.Fatalf("expected actualFields to contain 2 items but got %d", len(*actualFields)) + if len(actualFields) != 2 { + t.Fatalf("expected actualFields to contain 2 items but got %d", len(actualFields)) } - resourceGroupName, ok := (*actualFields)["ResourceGroupName"] + resourceGroupName, ok := (actualFields)["ResourceGroupName"] if !ok { t.Fatalf("expected there to be a field called `ResourceGroupName` but there wasn't") } @@ -638,7 +638,7 @@ func TestTopLevelFieldsWithinResourceId_DocumentationOverride(t *testing.T) { t.Fatalf("expected the description for `ResourceGroupName` to be `Specifies the name of the Resource Group within which this Chaos Studio Target should exist.` but got %q", resourceGroupName.Documentation.Markdown) } - targetType, ok := (*actualFields)["TargetType"] + targetType, ok := (actualFields)["TargetType"] if !ok { t.Fatalf("expected there to be a field called `TargetType` but there wasn't") } diff --git a/tools/importer-rest-api-specs/internal/components/terraform/schema/fields_top_level.go b/tools/importer-rest-api-specs/internal/components/terraform/schema/fields_top_level.go index e491f97c13a..21d1329b946 100644 --- a/tools/importer-rest-api-specs/internal/components/terraform/schema/fields_top_level.go +++ b/tools/importer-rest-api-specs/internal/components/terraform/schema/fields_top_level.go @@ -10,7 +10,7 @@ import ( sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" ) -func (b Builder) schemaFromTopLevelFields(schemaModelName string, input operationPayloads, mappings *sdkModels.TerraformMappingDefinition, resourceDisplayName string) (*map[string]sdkModels.TerraformSchemaField, *sdkModels.TerraformMappingDefinition, error) { +func (b Builder) schemaFromTopLevelFields(schemaModelName string, input operationPayloads, mappings *sdkModels.TerraformMappingDefinition, resourceDisplayName string) (map[string]sdkModels.TerraformSchemaField, *sdkModels.TerraformMappingDefinition, error) { allFields := make(map[string]struct{}) for _, model := range input.createReadUpdatePayloads() { for k := range model.Fields { @@ -179,7 +179,7 @@ func (b Builder) schemaFromTopLevelFields(schemaModelName string, input operatio // TODO: go through any fields _only_ in the Read function which are ReadOnly/Computed - return &schemaFields, mappings, nil + return schemaFields, mappings, nil } func directAssignmentMappingForTopLevelField(schemaModelName, schemaModelField string, input operationPayloads, sdkFieldName string, hasCreate bool, hasUpdate bool, hasRead bool) []sdkModels.TerraformFieldMappingDefinition { diff --git a/tools/importer-rest-api-specs/internal/components/terraform/schema/processors/field_name_plural_to_singular.go b/tools/importer-rest-api-specs/internal/components/terraform/schema/processors/field_name_plural_to_singular.go index 0a6f0056625..84e780dd4b2 100644 --- a/tools/importer-rest-api-specs/internal/components/terraform/schema/processors/field_name_plural_to_singular.go +++ b/tools/importer-rest-api-specs/internal/components/terraform/schema/processors/field_name_plural_to_singular.go @@ -5,7 +5,7 @@ package processors import ( "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/components/parser/cleanup" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup" ) var _ FieldNameProcessor = fieldNamePluralToSingular{} diff --git a/tools/importer-rest-api-specs/internal/components/terraform/schema/processors/model_flatten_list_reference_ids.go b/tools/importer-rest-api-specs/internal/components/terraform/schema/processors/model_flatten_list_reference_ids.go index 66d470c5467..3947365e575 100644 --- a/tools/importer-rest-api-specs/internal/components/terraform/schema/processors/model_flatten_list_reference_ids.go +++ b/tools/importer-rest-api-specs/internal/components/terraform/schema/processors/model_flatten_list_reference_ids.go @@ -13,9 +13,9 @@ var _ ModelProcessor = modelFlattenListReferenceIds{} type modelFlattenListReferenceIds struct{} -func (modelFlattenListReferenceIds) ProcessModel(modelName string, model sdkModels.TerraformSchemaModel, schemaModels map[string]sdkModels.TerraformSchemaModel, mappings sdkModels.TerraformMappingDefinition) (*map[string]sdkModels.TerraformSchemaModel, *sdkModels.TerraformMappingDefinition, error) { +func (modelFlattenListReferenceIds) ProcessModel(modelName string, model sdkModels.TerraformSchemaModel, schemaModels map[string]sdkModels.TerraformSchemaModel, mappings sdkModels.TerraformMappingDefinition) (map[string]sdkModels.TerraformSchemaModel, *sdkModels.TerraformMappingDefinition, error) { if len(model.Fields) != 1 { - return &schemaModels, &mappings, nil + return schemaModels, &mappings, nil } fields := make(map[string]sdkModels.TerraformSchemaField) @@ -67,5 +67,5 @@ func (modelFlattenListReferenceIds) ProcessModel(modelName string, model sdkMode } model.Fields = fields schemaModels[modelName] = model - return &schemaModels, &mappings, nil + return schemaModels, &mappings, nil } diff --git a/tools/importer-rest-api-specs/internal/components/terraform/schema/processors/model_flatten_properties_into_parent.go b/tools/importer-rest-api-specs/internal/components/terraform/schema/processors/model_flatten_properties_into_parent.go index 867287e1dd8..dd86d11f579 100644 --- a/tools/importer-rest-api-specs/internal/components/terraform/schema/processors/model_flatten_properties_into_parent.go +++ b/tools/importer-rest-api-specs/internal/components/terraform/schema/processors/model_flatten_properties_into_parent.go @@ -14,7 +14,7 @@ var _ ModelProcessor = modelFlattenPropertiesIntoParent{} type modelFlattenPropertiesIntoParent struct{} -func (modelFlattenPropertiesIntoParent) ProcessModel(modelName string, model sdkModels.TerraformSchemaModel, schemaModels map[string]sdkModels.TerraformSchemaModel, mappings sdkModels.TerraformMappingDefinition) (*map[string]sdkModels.TerraformSchemaModel, *sdkModels.TerraformMappingDefinition, error) { +func (modelFlattenPropertiesIntoParent) ProcessModel(modelName string, model sdkModels.TerraformSchemaModel, schemaModels map[string]sdkModels.TerraformSchemaModel, mappings sdkModels.TerraformMappingDefinition) (map[string]sdkModels.TerraformSchemaModel, *sdkModels.TerraformMappingDefinition, error) { fields := make(map[string]sdkModels.TerraformSchemaField) fieldKeys := make(map[string]struct{}) // first ensure we have a canonical list of all fields within the model to be able to use for a unique check @@ -47,7 +47,7 @@ func (modelFlattenPropertiesIntoParent) ProcessModel(modelName string, model sdk if _, hasExisting := fieldKeys[strings.ToLower(nestedFieldName)]; hasExisting { // if the top level model contains a field with the same name then we shouldn't be flattening // the nested model into it, otherwise we'll have naming conflicts - return &schemaModels, &mappings, nil + return schemaModels, &mappings, nil } fields[nestedFieldName] = nestedFieldValue @@ -56,5 +56,5 @@ func (modelFlattenPropertiesIntoParent) ProcessModel(modelName string, model sdk } model.Fields = fields schemaModels[modelName] = model - return &schemaModels, &mappings, nil + return schemaModels, &mappings, nil } diff --git a/tools/importer-rest-api-specs/internal/components/terraform/schema/processors/model_flatten_reference_id.go b/tools/importer-rest-api-specs/internal/components/terraform/schema/processors/model_flatten_reference_id.go index 4da1e72e48b..89a3c295942 100644 --- a/tools/importer-rest-api-specs/internal/components/terraform/schema/processors/model_flatten_reference_id.go +++ b/tools/importer-rest-api-specs/internal/components/terraform/schema/processors/model_flatten_reference_id.go @@ -14,7 +14,7 @@ var _ ModelProcessor = modelFlattenReferenceId{} type modelFlattenReferenceId struct{} -func (modelFlattenReferenceId) ProcessModel(modelName string, model sdkModels.TerraformSchemaModel, schemaModels map[string]sdkModels.TerraformSchemaModel, mappings sdkModels.TerraformMappingDefinition) (*map[string]sdkModels.TerraformSchemaModel, *sdkModels.TerraformMappingDefinition, error) { +func (modelFlattenReferenceId) ProcessModel(modelName string, model sdkModels.TerraformSchemaModel, schemaModels map[string]sdkModels.TerraformSchemaModel, mappings sdkModels.TerraformMappingDefinition) (map[string]sdkModels.TerraformSchemaModel, *sdkModels.TerraformMappingDefinition, error) { fields := make(map[string]sdkModels.TerraformSchemaField) for fieldName, fieldValue := range model.Fields { @@ -57,5 +57,5 @@ func (modelFlattenReferenceId) ProcessModel(modelName string, model sdkModels.Te } model.Fields = fields schemaModels[modelName] = model - return &schemaModels, &mappings, nil + return schemaModels, &mappings, nil } diff --git a/tools/importer-rest-api-specs/internal/components/terraform/schema/processors/model_flatten_sku_name.go b/tools/importer-rest-api-specs/internal/components/terraform/schema/processors/model_flatten_sku_name.go index 73ccb681223..f157ebd1600 100644 --- a/tools/importer-rest-api-specs/internal/components/terraform/schema/processors/model_flatten_sku_name.go +++ b/tools/importer-rest-api-specs/internal/components/terraform/schema/processors/model_flatten_sku_name.go @@ -14,7 +14,7 @@ var _ ModelProcessor = modelFlattenSkuName{} type modelFlattenSkuName struct{} -func (modelFlattenSkuName) ProcessModel(modelName string, model sdkModels.TerraformSchemaModel, schemaModels map[string]sdkModels.TerraformSchemaModel, mappings sdkModels.TerraformMappingDefinition) (*map[string]sdkModels.TerraformSchemaModel, *sdkModels.TerraformMappingDefinition, error) { +func (modelFlattenSkuName) ProcessModel(modelName string, model sdkModels.TerraformSchemaModel, schemaModels map[string]sdkModels.TerraformSchemaModel, mappings sdkModels.TerraformMappingDefinition) (map[string]sdkModels.TerraformSchemaModel, *sdkModels.TerraformMappingDefinition, error) { fields := make(map[string]sdkModels.TerraformSchemaField) for fieldName, fieldValue := range model.Fields { fields[fieldName] = fieldValue @@ -48,5 +48,5 @@ func (modelFlattenSkuName) ProcessModel(modelName string, model sdkModels.Terraf } model.Fields = fields schemaModels[modelName] = model - return &schemaModels, &mappings, nil + return schemaModels, &mappings, nil } diff --git a/tools/importer-rest-api-specs/internal/components/terraform/schema/processors/model_remove_status_detail.go b/tools/importer-rest-api-specs/internal/components/terraform/schema/processors/model_remove_status_detail.go index 12ed81e8af2..ec056deee2f 100644 --- a/tools/importer-rest-api-specs/internal/components/terraform/schema/processors/model_remove_status_detail.go +++ b/tools/importer-rest-api-specs/internal/components/terraform/schema/processors/model_remove_status_detail.go @@ -14,7 +14,7 @@ var _ ModelProcessor = modelRemoveStatusAndDetail{} type modelRemoveStatusAndDetail struct{} -func (modelRemoveStatusAndDetail) ProcessModel(modelName string, model sdkModels.TerraformSchemaModel, schemaModels map[string]sdkModels.TerraformSchemaModel, mappings sdkModels.TerraformMappingDefinition) (*map[string]sdkModels.TerraformSchemaModel, *sdkModels.TerraformMappingDefinition, error) { +func (modelRemoveStatusAndDetail) ProcessModel(modelName string, model sdkModels.TerraformSchemaModel, schemaModels map[string]sdkModels.TerraformSchemaModel, mappings sdkModels.TerraformMappingDefinition) (map[string]sdkModels.TerraformSchemaModel, *sdkModels.TerraformMappingDefinition, error) { fields := make(map[string]sdkModels.TerraformSchemaField) status := regexp.MustCompile("\\w?(Status)$") @@ -32,5 +32,5 @@ func (modelRemoveStatusAndDetail) ProcessModel(modelName string, model sdkModels } model.Fields = fields schemaModels[modelName] = model - return &schemaModels, &mappings, nil + return schemaModels, &mappings, nil } diff --git a/tools/importer-rest-api-specs/internal/components/terraform/schema/processors/model_rename_zones.go b/tools/importer-rest-api-specs/internal/components/terraform/schema/processors/model_rename_zones.go index 8845e4e1c96..07dd149dc00 100644 --- a/tools/importer-rest-api-specs/internal/components/terraform/schema/processors/model_rename_zones.go +++ b/tools/importer-rest-api-specs/internal/components/terraform/schema/processors/model_rename_zones.go @@ -14,7 +14,7 @@ var _ ModelProcessor = modelRenameZones{} type modelRenameZones struct{} -func (modelRenameZones) ProcessModel(modelName string, model sdkModels.TerraformSchemaModel, schemaModels map[string]sdkModels.TerraformSchemaModel, mappings sdkModels.TerraformMappingDefinition) (*map[string]sdkModels.TerraformSchemaModel, *sdkModels.TerraformMappingDefinition, error) { +func (modelRenameZones) ProcessModel(modelName string, model sdkModels.TerraformSchemaModel, schemaModels map[string]sdkModels.TerraformSchemaModel, mappings sdkModels.TerraformMappingDefinition) (map[string]sdkModels.TerraformSchemaModel, *sdkModels.TerraformMappingDefinition, error) { fields := make(map[string]sdkModels.TerraformSchemaField) for fieldName, fieldValue := range model.Fields { fields[fieldName] = fieldValue @@ -65,5 +65,5 @@ func (modelRenameZones) ProcessModel(modelName string, model sdkModels.Terraform } model.Fields = fields schemaModels[modelName] = model - return &schemaModels, &mappings, nil + return schemaModels, &mappings, nil } diff --git a/tools/importer-rest-api-specs/internal/components/terraform/schema/processors/models.go b/tools/importer-rest-api-specs/internal/components/terraform/schema/processors/models.go index 0ac6c687514..5ceac5841fc 100644 --- a/tools/importer-rest-api-specs/internal/components/terraform/schema/processors/models.go +++ b/tools/importer-rest-api-specs/internal/components/terraform/schema/processors/models.go @@ -8,7 +8,7 @@ import ( ) type ModelProcessor interface { - ProcessModel(modelName string, model sdkModels.TerraformSchemaModel, schemaModels map[string]sdkModels.TerraformSchemaModel, mappings sdkModels.TerraformMappingDefinition) (*map[string]sdkModels.TerraformSchemaModel, *sdkModels.TerraformMappingDefinition, error) + ProcessModel(modelName string, model sdkModels.TerraformSchemaModel, schemaModels map[string]sdkModels.TerraformSchemaModel, mappings sdkModels.TerraformMappingDefinition) (map[string]sdkModels.TerraformSchemaModel, *sdkModels.TerraformMappingDefinition, error) } var ModelRules = []ModelProcessor{ diff --git a/tools/importer-rest-api-specs/internal/components/terraform/schema/processors/models_test.go b/tools/importer-rest-api-specs/internal/components/terraform/schema/processors/models_test.go index b73cdd9c4b6..40de5cae198 100644 --- a/tools/importer-rest-api-specs/internal/components/terraform/schema/processors/models_test.go +++ b/tools/importer-rest-api-specs/internal/components/terraform/schema/processors/models_test.go @@ -54,22 +54,22 @@ func mappingTypesMatch(t *testing.T, first sdkModels.TerraformFieldMappingDefini t.Fatalf("encoding second: %+v", err) return false } - if !reflect.DeepEqual(*firstEncoded, *secondEncoded) { - t.Fatalf("mapping types didn't match - first [%+v] - second [%+v]", *firstEncoded, *secondEncoded) + if !reflect.DeepEqual(firstEncoded, secondEncoded) { + t.Fatalf("mapping types didn't match - first [%+v] - second [%+v]", firstEncoded, secondEncoded) return false } return true } -func modelDefinitionsMatch(t *testing.T, actual *map[string]sdkModels.TerraformSchemaModel, expected map[string]sdkModels.TerraformSchemaModel) { +func modelDefinitionsMatch(t *testing.T, actual map[string]sdkModels.TerraformSchemaModel, expected map[string]sdkModels.TerraformSchemaModel) { if actual == nil { t.Fatalf("actual was nil") } - if len(*actual) != len(expected) { - t.Fatalf("actual had %d models but expected had %d models", len(*actual), len(expected)) + if len(actual) != len(expected) { + t.Fatalf("actual had %d models but expected had %d models", len(actual), len(expected)) } - for key, firstVal := range *actual { + for key, firstVal := range actual { secondVal, ok := expected[key] if !ok { t.Fatalf("key %q was present in actual but not expected", key) @@ -190,15 +190,15 @@ func validatorsMatch(t *testing.T, first sdkModels.TerraformSchemaFieldValidatio return false } - if !reflect.DeepEqual(*firstEncoded, *secondEncoded) { - t.Fatalf("validation values didn't match - first [%+v] / second [%+v]", *firstEncoded, *secondEncoded) + if !reflect.DeepEqual(firstEncoded, secondEncoded) { + t.Fatalf("validation values didn't match - first [%+v] / second [%+v]", firstEncoded, secondEncoded) return false } return true } -func marshalValue(input any) (*map[string]any, error) { +func marshalValue(input any) (map[string]any, error) { encoded, err := json.Marshal(input) if err != nil { return nil, err @@ -208,7 +208,7 @@ func marshalValue(input any) (*map[string]any, error) { if err := json.Unmarshal(encoded, &out); err != nil { return nil, fmt.Errorf("decoding into `map[string]any`: %+v", err) } - return &out, nil + return out, nil } func stringifyValues(input []interface{}) []string { diff --git a/tools/importer-rest-api-specs/internal/pipeline/import.go b/tools/importer-rest-api-specs/internal/pipeline/import.go index 28253ab8d2f..552cbc49fd6 100644 --- a/tools/importer-rest-api-specs/internal/pipeline/import.go +++ b/tools/importer-rest-api-specs/internal/pipeline/import.go @@ -21,7 +21,8 @@ func RunImporter(opts Options) error { } logging.Debugf("Completed - Building the repository.") - logging.Debugf("Reticulating splines..") + loadingMessage() + p := &Pipeline{ opts: opts, repository: repo, @@ -45,8 +46,22 @@ func RunImporter(opts Options) error { } logging.Debugf("Completed - Clearing any existing API Definitions.") - logging.Infof("Processing the %d Services..", len(p.servicesFromConfigurationFiles)) + logging.Infof("Processing %d Services..", len(p.servicesFromConfigurationFiles)) for _, service := range p.servicesFromConfigurationFiles { + if len(opts.ServiceNamesToLimitTo) > 0 { + processThisService := false + for _, serviceNameToLimitTo := range opts.ServiceNamesToLimitTo { + if service.Name == serviceNameToLimitTo { + processThisService = true + break + } + } + if !processThisService { + logging.Infof("Skipping the Service %q..", service.Name) + continue + } + } + logging.Infof("Discovering the Data for Service %q..", service.Name) data, err := p.parseDataForService(service) if err != nil { diff --git a/tools/importer-rest-api-specs/internal/pipeline/messages.go b/tools/importer-rest-api-specs/internal/pipeline/messages.go new file mode 100644 index 00000000000..2372d9d2af6 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/pipeline/messages.go @@ -0,0 +1,122 @@ +package pipeline + +import ( + "math/rand" + "time" + + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/logging" +) + +var loadingMessages = []string{ + "Adding Hidden Agendas", + "Adjusting Bell Curves", + "Aesthesizing Industrial Areas", + "Aligning Covariance Matrices", + "Applying Feng Shui Shaders", + "Applying Theatre Soda Layer", + "Asserting Packed Exemplars", + "Attempting to Lock Back-Buffer", + "Binding Sapling Root System", + "Breeding Fauna", + "Building Data Trees", + "Bureacritizing Bureaucracies", + "Calculating Inverse Probability Matrices", + "Calculating Llama Expectoration Trajectory", + "Calibrating Blue Skies", + "Charging Ozone Layer", + "Coalescing Cloud Formations", + "Cohorting Exemplars", + "Collecting Meteor Particles", + "Compounding Inert Tessellations", + "Compressing Fish Files", + "Computing Optimal Bin Packing", + "Concatenating Sub-Contractors", + "Containing Existential Buffer", + "Debarking Ark Ramp", + "Debunching Unionized Commercial Services", + "Deciding What Message to Display Next", + "Decomposing Singular Values", + "Decrementing Tectonic Plates", + "Deleting Ferry Routes", + "Depixelating Inner Mountain Surface Back Faces", + "Depositing Slush Funds", + "Destabilizing Economic Indicators", + "Determining Width of Blast Fronts", + "Deunionizing Bulldozers", + "Dicing Models", + "Diluting Livestock Nutrition Variables", + "Downloading Satellite Terrain Data", + "Exposing Flash Variables to Streak System", + "Extracting Resources", + "Factoring Pay Scale", + "Fixing Election Outcome Matrix", + "Flood-Filling Ground Water", + "Flushing Pipe Network", + "Gathering Particle Sources", + "Generating Jobs", + "Gesticulating Mimes", + "Graphing Whale Migration", + "Hiding Willio Webnet Mask", + "Implementing Impeachment Routine", + "Increasing Accuracy of RCI Simulators", + "Increasing Magmafacation", + "Initializing My Sim Tracking Mechanism", + "Initializing Rhinoceros Breeding Timetable", + "Initializing Robotic Click-Path AI", + "Inserting Sublimated Messages", + "Integrating Curves", + "Integrating Illumination Form Factors", + "Integrating Population Graphs", + "Iterating Cellular Automata", + "Lecturing Errant Subsystems", + "Mixing Genetic Pool", + "Modeling Object Components", + "Mopping Occupant Leaks", + "Normalizing Power", + "Obfuscating Quigley Matrix", + "Overconstraining Dirty Industry Calculations", + "Partitioning City Grid Singularities", + "Perturbing Matrices", + "Pixalating Nude Patch", + "Polishing Water Highlights", + "Populating Lot Templates", + "Preparing Sprites for Random Walks", + "Prioritizing Landmarks", + "Projecting Law Enforcement Pastry Intake", + "Realigning Alternate Time Frames", + "Reconfiguring User Mental Processes", + "Relaxing Splines", + "Removing Road Network Speed Bumps", + "Removing Texture Gradients", + "Removing Vehicle Avoidance Behavior", + "Resolving GUID Conflict", + "Reticulating Splines", + "Retracting Phong Shader", + "Retrieving from Back Store", + "Reverse Engineering Image Consultant", + "Routing Neural Network Infanstructure", + "Scattering Rhino Food Sources", + "Scrubbing Terrain", + "Searching for Llamas", + "Seeding Architecture Simulation Parameters", + "Sequencing Particles", + "Setting Advisor Moods", + "Setting Inner Deity Indicators", + "Setting Universal Physical Constants", + "Sonically Enhancing Occupant-Free Timber", + "Speculating Stock Market Indices", + "Splatting Transforms", + "Stratifying Ground Layers", + "Sub-Sampling Water Data", + "Synthesizing Gravity", + "Synthesizing Wavelets", + "Time-Compressing Simulator Clock", + "Unable to Reveal Current Activity", + "Weathering Buildings", + "Zeroing Crime Network", +} + +func loadingMessage() { + logging.Infof("%s..", loadingMessages[rand.Intn(len(loadingMessages)-1)]) + time.Sleep(1800 * time.Millisecond) +} diff --git a/tools/importer-rest-api-specs/internal/pipeline/stage_parse_data_for_service.go b/tools/importer-rest-api-specs/internal/pipeline/stage_parse_data_for_service.go index dcce4c3bf96..d6edf6d4a2f 100644 --- a/tools/importer-rest-api-specs/internal/pipeline/stage_parse_data_for_service.go +++ b/tools/importer-rest-api-specs/internal/pipeline/stage_parse_data_for_service.go @@ -7,21 +7,26 @@ import ( "fmt" sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions" "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/discovery" "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/logging" "github.com/hashicorp/pandora/tools/sdk/config/services" ) -func (p *Pipeline) parseDataForService(service services.Service) (*sdkModels.Service, error) { - // TODO: this isn't fully usable until the Parser package is refactored - so enable this after - data, err := discovery.DiscoverForService(service, p.opts.RestAPISpecsDirectory) +func (p *Pipeline) parseDataForService(input services.Service) (*sdkModels.Service, error) { + logging.Debugf("Discovering Data for Service %q in %q..", input.Name, p.opts.RestAPISpecsDirectory) + data, err := discovery.DiscoverForService(input, p.opts.RestAPISpecsDirectory) if err != nil { - return nil, fmt.Errorf("discovering for Service %q: %+v", service.Name, err) + return nil, fmt.Errorf("discovering for Service %q: %+v", input.Name, err) } - logging.Tracef("Resource Provider is %q", *data.ResourceProvider) + logging.Tracef("Resource Provider is %q for the Service %q", *data.ResourceProvider, input.Name) + logging.Debugf("Discovering Data for Service %q in %q - Completed", input.Name, p.opts.RestAPISpecsDirectory) - for version, versionData := range data.DataSetsForAPIVersions { - logging.Debugf("API Version %q had %d and %d", version, len(versionData.FilePathsContainingAPIDefinitions), len(versionData.FilePathsContainingSupplementaryData)) + logging.Debugf("Parsing Data for Service %q..", input.Name) + service, err := apidefinitions.ParseService(*data) + if err != nil { + return nil, fmt.Errorf("parsing Data for Service %q: %+v", input.Name, err) } - return nil, fmt.Errorf("TODO") + logging.Debugf("Parsing Data for Service %q - Completed", input.Name) + return service, nil } diff --git a/tools/importer-rest-api-specs/internal/pipeline/validate.go b/tools/importer-rest-api-specs/internal/pipeline/validate.go index dc52436ed35..74a73db0325 100644 --- a/tools/importer-rest-api-specs/internal/pipeline/validate.go +++ b/tools/importer-rest-api-specs/internal/pipeline/validate.go @@ -16,7 +16,7 @@ func RunValidate(opts Options) error { logging.Infof("Validating Data..") logging.Debugf("Building the repository..") - repo, err := repository.NewRepository(opts.APIDefinitionsDirectory, opts.SourceDataType, &opts.ServiceNamesToLimitTo, logging.Log) + repo, err := repository.NewRepository(opts.RestAPISpecsDirectory, opts.SourceDataType, &opts.ServiceNamesToLimitTo, logging.Log) if err != nil { return fmt.Errorf("building repository: %+v", err) } diff --git a/tools/importer-rest-api-specs/models/models.go b/tools/importer-rest-api-specs/models/models.go deleted file mode 100644 index a05797d7159..00000000000 --- a/tools/importer-rest-api-specs/models/models.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package models - -import ( - "strings" - - sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" -) - -type AzureApiDefinition struct { - ServiceName string - ApiVersion string - Resources map[string]sdkModels.APIResource -} - -func (d AzureApiDefinition) IsPreviewVersion() bool { - lower := strings.ToLower(d.ApiVersion) - // handles preview, privatepreview and publicpreview - if strings.Contains(lower, "preview") { - return true - } - if strings.Contains(lower, "beta") { - return true - } - if strings.Contains(lower, "alpha") { - return true - } - - return false -} - -func MergeResourcesForTag(base, merge sdkModels.APIResource) sdkModels.APIResource { - for k, v := range merge.Constants { - if _, ok := base.Constants[k]; !ok { - base.Constants[k] = v - } - } - - for k, v := range merge.Models { - if _, ok := base.Models[k]; !ok { - base.Models[k] = v - } - } - - for k, v := range merge.Operations { - if _, ok := base.Operations[k]; !ok { - base.Operations[k] = v - } - } - for k, v := range merge.ResourceIDs { - if _, ok := base.ResourceIDs[k]; !ok { - base.ResourceIDs[k] = v - } - } - - // `base.Terraform` and `merge.Terraform` should both be nil here, so we don't process it - - return base -} diff --git a/tools/importer-rest-api-specs/pipeline/git.go b/tools/importer-rest-api-specs/pipeline/git.go deleted file mode 100644 index 44ac4147f9d..00000000000 --- a/tools/importer-rest-api-specs/pipeline/git.go +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package pipeline - -import ( - "github.com/go-git/go-git/v5" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/logging" -) - -func determineGitSha(repositoryPath string) (*string, error) { - repo, err := git.PlainOpen(repositoryPath) - if err != nil { - return nil, err - } - - ref, err := repo.Head() - if err != nil { - return nil, err - } - - commit := ref.Hash().String() - logging.Debugf("Swagger Repository Commit SHA is %q", commit) - return &commit, nil -} diff --git a/tools/importer-rest-api-specs/pipeline/interface.go b/tools/importer-rest-api-specs/pipeline/interface.go deleted file mode 100644 index f8fba0d3b3f..00000000000 --- a/tools/importer-rest-api-specs/pipeline/interface.go +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package pipeline - -import ( - "fmt" - "strings" - - "github.com/hashicorp/go-hclog" - legacyDiscovery "github.com/hashicorp/pandora/tools/importer-rest-api-specs/components/discovery" - "github.com/hashicorp/pandora/tools/sdk/config/definitions" -) - -type RunInput struct { - ConfigFilePath string - JustParseData bool - Logger hclog.Logger - OutputDirectory string - ProviderPrefix string - Services []string - SwaggerDirectory string - TerraformDefinitionsPath string -} - -func Run(input RunInput) error { - logger := hclog.New(hclog.DefaultOptions) - resources, err := definitions.LoadFromDirectory(input.TerraformDefinitionsPath) - if err != nil { - return fmt.Errorf("loading terraform definitions from %q: %+v", input.TerraformDefinitionsPath, err) - } - - findInput := legacyDiscovery.FindServiceInput{ - SwaggerDirectory: input.SwaggerDirectory, - ConfigFilePath: input.ConfigFilePath, - OutputDirectory: input.OutputDirectory, - Logger: input.Logger.Named("Discovery"), - } - - var generationData *[]legacyDiscovery.ServiceInput - - if len(input.Services) > 0 { - logger.Info(fmt.Sprintf("Finding only the Services %q", strings.Join(input.Services, ", "))) - generationData, err = legacyDiscovery.FindServicesByName(findInput, *resources, input.Services) - } else { - logger.Info("Finding all services.. this may take a while..") - generationData, err = legacyDiscovery.FindServices(findInput, *resources) - } - - if err != nil { - return fmt.Errorf("loading data: %+v", err) - } - - swaggerGitSha, err := determineGitSha(input.SwaggerDirectory) - if err != nil { - return fmt.Errorf("determining Git SHA at %q: %+v", input.SwaggerDirectory, err) - } - - if input.JustParseData { - return validateCanParseData(*generationData) - } - - return runImporter(input, *generationData, *swaggerGitSha) -} diff --git a/tools/importer-rest-api-specs/pipeline/run_importer.go b/tools/importer-rest-api-specs/pipeline/run_importer.go deleted file mode 100644 index f31b1559713..00000000000 --- a/tools/importer-rest-api-specs/pipeline/run_importer.go +++ /dev/null @@ -1,188 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package pipeline - -import ( - "fmt" - "sort" - - "github.com/hashicorp/go-azure-helpers/lang/pointer" - "github.com/hashicorp/pandora/tools/data-api-repository/repository" - sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/components/discovery" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/components/transformer" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/terraform" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/logging" - importerModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/models" - "github.com/hashicorp/pandora/tools/sdk/config/definitions" -) - -func runImporter(input RunInput, generationData []discovery.ServiceInput, swaggerGitSha string) error { - sourceDataType := sdkModels.ResourceManagerSourceDataType - sourceDataOrigin := sdkModels.AzureRestAPISpecsSourceDataOrigin - repo, err := repository.NewRepository(input.OutputDirectory, sourceDataType, nil, logging.Log) - if err != nil { - return fmt.Errorf("building repository: %+v", err) - } - - // Clear any existing data - if len(input.Services) == 0 { - logging.Infof("Purging all existing Source Data for Source Data Type %q / Source Data Origin %q..", sourceDataType, sourceDataOrigin) - if err := repo.PurgeExistingData(sourceDataOrigin); err != nil { - return fmt.Errorf("purging the existing Source Data for the Source Data Type %q / Source Data Origin %q: %+v", sourceDataType, sourceDataOrigin, err) - } - } else { - logging.Infof("Purging the existing Source Data for the Services [%+v] for for Source Data Type %q / Source Data Origin %q..", input.Services, sourceDataType, sourceDataOrigin) - for _, serviceName := range input.Services { - logging.Debugf("Removing the existing Data for Service %q..", serviceName) - opts := repository.RemoveServiceOptions{ - ServiceName: serviceName, - SourceDataOrigin: sourceDataOrigin, - } - if err := repo.RemoveService(opts); err != nil { - return fmt.Errorf("removing the existing Data for Service %q: %+v", serviceName, err) - } - } - } - - // group the API Versions by Service - dataByServices := make(map[string][]discovery.ServiceInput) - for _, v := range generationData { - existing, ok := dataByServices[v.ServiceName] - if !ok { - existing = append(existing, v) - dataByServices[v.ServiceName] = existing - continue - } else { - existing = append(existing, v) - dataByServices[v.ServiceName] = existing - continue - } - } - - // sort these so it's easier for parsing/tracing - serviceNames := make([]string, 0) - for serviceName := range dataByServices { - serviceNames = append(serviceNames, serviceName) - } - sort.Strings(serviceNames) - - // then parse/process the data for each of the API Versions for each service - for _, serviceName := range serviceNames { - serviceDetails := dataByServices[serviceName] - - logging.Log.Debug(fmt.Sprintf("Removing any existing API Definitions for the Service %q", serviceName)) - removeServiceOpts := repository.RemoveServiceOptions{ - ServiceName: serviceName, - SourceDataOrigin: sourceDataOrigin, - } - if err := repo.RemoveService(removeServiceOpts); err != nil { - return fmt.Errorf("removing existing API Definitions for Service %q: %+v", serviceName, err) - } - - logging.Infof("Importer for Service %q", serviceName) - if err := runImportForService(input, serviceName, serviceDetails, sourceDataOrigin, swaggerGitSha, repo); err != nil { - return fmt.Errorf("parsing data for Service %q: %+v", serviceName, err) - } - } - - return nil -} - -func runImportForService(input RunInput, serviceName string, apiVersionsForService []discovery.ServiceInput, sourceDataOrigin sdkModels.SourceDataOrigin, swaggerGitSha string, repo repository.Repository) error { - task := pipelineTask{} - var resourceProvider *string - var terraformPackageName *string - - consolidatedApiVersions := make(map[string][]discovery.ServiceInput) - terraformResourceDefinitions := make(map[string]definitions.ResourceDefinition) - - // scan for fragmented API - e.g. Compute 2021-07-01 - for _, v := range apiVersionsForService { - if resourceProvider != nil && v.ResourceProvider != nil && *resourceProvider != *v.ResourceProvider { - return fmt.Errorf("multiple Resource Providers were found for the Service %q. First %q / Second %q", serviceName, *resourceProvider, *v.ResourceProvider) - } - resourceProvider = v.ResourceProvider - - if _, ok := consolidatedApiVersions[v.ApiVersion]; !ok { - consolidatedApiVersions[v.ApiVersion] = []discovery.ServiceInput{v} - } else { - consolidatedApiVersions[v.ApiVersion] = append(consolidatedApiVersions[v.ApiVersion], v) - } - - if v.TerraformServiceDefinition != nil { - if terraformPackageName != nil && *terraformPackageName != v.TerraformServiceDefinition.TerraformPackageName { - return fmt.Errorf("duplicate Terraform Package names for the Service %q. First %q / Second %q", serviceName, *terraformPackageName, v.TerraformServiceDefinition.TerraformPackageName) - } - terraformPackageName = pointer.To(v.TerraformServiceDefinition.TerraformPackageName) - - for _, apiVersionDefinition := range v.TerraformServiceDefinition.ApiVersions { - for _, apiResourceDetails := range apiVersionDefinition.Packages { - for resourceLabel, resourceDefinition := range apiResourceDetails.Definitions { - // TODO: until this is refactored this needs to stay as-is - //if _, existing := terraformResourceDefinitions[resourceLabel]; existing { - // return fmt.Errorf("a duplicate Terraform Resource Definition exists for %q", resourceLabel) - //} - terraformResourceDefinitions[resourceLabel] = resourceDefinition - } - } - - } - } - } - - // Populate all of the data for this API Version.. - dataForApiVersions := make([]importerModels.AzureApiDefinition, 0) - for apiVersion, api := range consolidatedApiVersions { - logging.Tracef("Task: Parsing Data for API Version %q..", apiVersion) - dataForApiVersion := &importerModels.AzureApiDefinition{ - ServiceName: serviceName, - ApiVersion: apiVersion, - Resources: map[string]sdkModels.APIResource{}, - } - for _, v := range api { - tempDataForApiVersion, err := task.parseDataForApiVersion(v) - if err != nil { - return fmt.Errorf("parsing data for Service %q / Version %q: %+v", v.ServiceName, v.ApiVersion, err) - } - if tempDataForApiVersion == nil { - continue - } - for name, resource := range tempDataForApiVersion.Resources { - dataForApiVersion.Resources[name] = resource - } - } - - dataForApiVersions = append(dataForApiVersions, *dataForApiVersion) - } - - // temporary glue to enable refactoring this tool piece-by-piece - logging.Infof("Transforming to the Data API SDK types..") - service, err := transformer.MapInternalTypesToDataAPISDKTypes(serviceName, dataForApiVersions, resourceProvider) - if err != nil { - return fmt.Errorf("transforming the internal types to the Data API SDK types: %+v", err) - } - - // Now that we've got all of the API Versions, build up the Terraform Resources - logging.Log.Info(fmt.Sprintf("Building Terraform Resources for the Service %q..", service.Name)) - service, err = terraform.BuildForService(*service, terraformResourceDefinitions, input.ProviderPrefix, terraformPackageName) - if err != nil { - return fmt.Errorf("building the Terraform Details: %+v", err) - } - - // Now that we have the populated data, let's go ahead and output that.. - logging.Infof("Persisting API Definitions for Service %s..", serviceName) - opts := repository.SaveServiceOptions{ - SourceCommitSHA: pointer.To(swaggerGitSha), - ResourceProvider: resourceProvider, - Service: *service, - ServiceName: serviceName, - SourceDataOrigin: sourceDataOrigin, - } - if err := repo.SaveService(opts); err != nil { - return fmt.Errorf("persisting Data API Definitions for Service %q: %+v", serviceName, err) - } - - return nil -} diff --git a/tools/importer-rest-api-specs/pipeline/run_validate_can_parse_data.go b/tools/importer-rest-api-specs/pipeline/run_validate_can_parse_data.go deleted file mode 100644 index 0d932157adb..00000000000 --- a/tools/importer-rest-api-specs/pipeline/run_validate_can_parse_data.go +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package pipeline - -import ( - "log" - "os" - "sync" - - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/components/discovery" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/logging" -) - -func validateCanParseData(generationData []discovery.ServiceInput) error { - var wg sync.WaitGroup - for _, v := range generationData { - wg.Add(1) - go func(v discovery.ServiceInput) { - logging.Infof("Importer Service %q / API Version %q", v.ServiceName, v.ApiVersion) - task := pipelineTask{} - if _, err := task.parseDataForApiVersion(v); err != nil { - log.Printf("validating that data can be processed for Service %q / Version %q: %+v", v.ServiceName, v.ApiVersion, err) - wg.Done() - os.Exit(1) - return - } - - wg.Done() - }(v) - } - - wg.Wait() - return nil -} diff --git a/tools/importer-rest-api-specs/pipeline/run_validate_can_parse_data_test.go b/tools/importer-rest-api-specs/pipeline/run_validate_can_parse_data_test.go deleted file mode 100644 index 7b01035180d..00000000000 --- a/tools/importer-rest-api-specs/pipeline/run_validate_can_parse_data_test.go +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package pipeline - -import ( - "fmt" - "os" - "regexp" - "testing" - - "github.com/hashicorp/go-hclog" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/components/discovery" - "github.com/hashicorp/pandora/tools/sdk/config/definitions" -) - -const ( - outputDirectoryJson = "../../../api-definitions/" - swaggerDirectory = "../../../submodules/rest-api-specs" - resourceManagerConfig = "../../../config/resource-manager.hcl" -) - -func TestConfigContainsValidServiceNames(t *testing.T) { - resources := definitions.Config{ - Services: map[string]definitions.ServiceDefinition{}, - } - input := discovery.FindServiceInput{ - SwaggerDirectory: swaggerDirectory, - ConfigFilePath: resourceManagerConfig, - OutputDirectory: outputDirectoryJson, - Logger: hclog.New(hclog.DefaultOptions), - } - generationData, err := discovery.FindServices(input, resources) - if err != nil { - t.Fatalf("building generation data: %+v", err) - } - - nameRegex, err := regexp.Compile("^[A-Z]{1}[A-Za-z0-9_]{1,}$") - if err != nil { - t.Fatalf("compiling regex: %+v", err) - } - - for _, data := range *generationData { - t.Run(fmt.Sprintf("%s-%s", data.ServiceName, data.ApiVersion), func(t *testing.T) { - generationData := data - - if !nameRegex.MatchString(generationData.ServiceName) { - t.Fatalf("name wasn't valid for %q - must contain only alphanumeric characters and underscores", generationData.ServiceName) - } - }) - } -} - -func TestExistingDataCanBeGenerated(t *testing.T) { - // works around the OAIGen bug - os.Setenv("OAIGEN_DEDUPE", "false") - - resources := definitions.Config{ - Services: map[string]definitions.ServiceDefinition{}, - } - input := discovery.FindServiceInput{ - SwaggerDirectory: swaggerDirectory, - ConfigFilePath: resourceManagerConfig, - OutputDirectory: outputDirectoryJson, - Logger: hclog.New(hclog.DefaultOptions), - } - generationData, err := discovery.FindServices(input, resources) - if err != nil { - t.Fatalf("building generation data: %+v", err) - } - - for _, data := range *generationData { - t.Run(fmt.Sprintf("%s-%s", data.ServiceName, data.ApiVersion), func(t *testing.T) { - generationData := data - - task := pipelineTask{} - if _, err := task.parseDataForApiVersion(generationData); err != nil { - t.Fatalf("error: %+v", err) - } - }) - } -} diff --git a/tools/importer-rest-api-specs/pipeline/task.go b/tools/importer-rest-api-specs/pipeline/task.go deleted file mode 100644 index 6e46d95e834..00000000000 --- a/tools/importer-rest-api-specs/pipeline/task.go +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package pipeline - -type pipelineTask struct { -} diff --git a/tools/importer-rest-api-specs/pipeline/task_parse_data.go b/tools/importer-rest-api-specs/pipeline/task_parse_data.go deleted file mode 100644 index 6b5c10d204b..00000000000 --- a/tools/importer-rest-api-specs/pipeline/task_parse_data.go +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package pipeline - -import ( - "fmt" - - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/components/discovery" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/components/parser" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/logging" - importerModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/models" -) - -func (pipelineTask) parseDataForApiVersion(input discovery.ServiceInput) (*importerModels.AzureApiDefinition, error) { - logging.Tracef("Parsing Swagger Files..") - data, err := parseSwaggerFiles(input) - if err != nil { - err = fmt.Errorf("parsing Swagger files: %+v", err) - logging.Infof(fmt.Sprintf("❌ Service %q - Api Version %q", input.ServiceName, input.ApiVersion)) - logging.Errorf(fmt.Sprintf(" 💥 Error: %+v", err)) - return nil, err - } - if data == nil { - logging.Infof("😵 Service %q / Api Version %q contains no resources, skipping.", input.ServiceName, input.ApiVersion) - return nil, nil - } - - return data, nil -} - -func parseSwaggerFiles(input discovery.ServiceInput) (*importerModels.AzureApiDefinition, error) { - parseResult, err := parser.LoadAndParseFiles(input.SwaggerDirectory, input.SwaggerFiles, input.ServiceName, input.ApiVersion, input.ResourceProviderToFilterTo) - if err != nil { - return nil, fmt.Errorf("parsing files in %q: %+v", input.SwaggerDirectory, err) - } - - return parseResult, nil -}