From f25568796c2c33756d08b25b0d11969e2877d6ee Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Thu, 4 Jul 2024 17:08:36 +0200 Subject: [PATCH 01/58] `tools/importer-rest-api-specs`: refactoring the `commonschema` Matchers out into the new structure --- .../parser/commonschema/custom_fields.go | 37 ------------------- .../components/parser/swagger_resources.go | 27 ++++++-------- .../parser/commonschema/README.md | 5 +++ .../parser/commonschema/interface.go | 14 +++++++ .../parser/commonschema/matcher_edge_zone.go} | 21 +++++------ ...y_legacy_system_and_user_assigned_list.go} | 21 +++++------ ...ty_legacy_system_and_user_assigned_map.go} | 23 ++++++------ ...identity_system_and_user_assigned_list.go} | 21 +++++------ ..._identity_system_and_user_assigned_map.go} | 23 ++++++------ .../matcher_identity_system_assigned.go} | 21 +++++------ ..._identity_system_or_user_assigned_list.go} | 21 +++++------ ...r_identity_system_or_user_assigned_map.go} | 23 ++++++------ .../matcher_identity_user_assigned_list.go} | 21 +++++------ .../matcher_identity_user_assigned_map.go} | 23 ++++++------ .../parser/commonschema/matcher_location.go} | 13 +++---- .../commonschema/matcher_system_data.go} | 23 ++++++------ .../parser/commonschema/matcher_tags.go} | 15 ++++---- .../parser/commonschema/matcher_zone.go} | 15 ++++---- .../parser/commonschema/matcher_zones.go} | 15 ++++---- .../parser/commonschema/matchers.go | 19 ++++++++++ 20 files changed, 191 insertions(+), 210 deletions(-) delete mode 100644 tools/importer-rest-api-specs/components/parser/commonschema/custom_fields.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema/README.md create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema/interface.go rename tools/importer-rest-api-specs/{components/parser/commonschema/custom_field_edge_zone.go => internal/components/apidefinitions/parser/commonschema/matcher_edge_zone.go} (70%) rename tools/importer-rest-api-specs/{components/parser/commonschema/custom_field_identity_legacy_system_and_user_assigned_list.go => internal/components/apidefinitions/parser/commonschema/matcher_identity_legacy_system_and_user_assigned_list.go} (84%) rename tools/importer-rest-api-specs/{components/parser/commonschema/custom_field_identity_legacy_system_and_user_assigned_map.go => internal/components/apidefinitions/parser/commonschema/matcher_identity_legacy_system_and_user_assigned_map.go} (87%) rename tools/importer-rest-api-specs/{components/parser/commonschema/custom_field_identity_system_and_user_assigned_list.go => internal/components/apidefinitions/parser/commonschema/matcher_identity_system_and_user_assigned_list.go} (85%) rename tools/importer-rest-api-specs/{components/parser/commonschema/custom_field_identity_system_and_user_assigned_map.go => internal/components/apidefinitions/parser/commonschema/matcher_identity_system_and_user_assigned_map.go} (86%) rename tools/importer-rest-api-specs/{components/parser/commonschema/custom_field_identity_system_assigned.go => internal/components/apidefinitions/parser/commonschema/matcher_identity_system_assigned.go} (87%) rename tools/importer-rest-api-specs/{components/parser/commonschema/custom_field_identity_system_or_user_assigned_list.go => internal/components/apidefinitions/parser/commonschema/matcher_identity_system_or_user_assigned_list.go} (84%) rename tools/importer-rest-api-specs/{components/parser/commonschema/custom_field_identity_system_or_user_assigned_map.go => internal/components/apidefinitions/parser/commonschema/matcher_identity_system_or_user_assigned_map.go} (86%) rename tools/importer-rest-api-specs/{components/parser/commonschema/custom_field_identity_user_assigned_list.go => internal/components/apidefinitions/parser/commonschema/matcher_identity_user_assigned_list.go} (85%) rename tools/importer-rest-api-specs/{components/parser/commonschema/custom_field_identity_user_assigned_map.go => internal/components/apidefinitions/parser/commonschema/matcher_identity_user_assigned_map.go} (88%) rename tools/importer-rest-api-specs/{components/parser/commonschema/custom_field_location.go => internal/components/apidefinitions/parser/commonschema/matcher_location.go} (58%) rename tools/importer-rest-api-specs/{components/parser/commonschema/custom_field_system_data.go => internal/components/apidefinitions/parser/commonschema/matcher_system_data.go} (90%) rename tools/importer-rest-api-specs/{components/parser/commonschema/custom_field_tags.go => internal/components/apidefinitions/parser/commonschema/matcher_tags.go} (75%) rename tools/importer-rest-api-specs/{components/parser/commonschema/custom_field_zone.go => internal/components/apidefinitions/parser/commonschema/matcher_zone.go} (74%) rename tools/importer-rest-api-specs/{components/parser/commonschema/custom_field_zones.go => internal/components/apidefinitions/parser/commonschema/matcher_zones.go} (77%) create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema/matchers.go 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/swagger_resources.go b/tools/importer-rest-api-specs/components/parser/swagger_resources.go index b961bdda56e..26e0d07be41 100644 --- a/tools/importer-rest-api-specs/components/parser/swagger_resources.go +++ b/tools/importer-rest-api-specs/components/parser/swagger_resources.go @@ -5,6 +5,7 @@ package parser import ( "fmt" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema" "net/http" "strings" @@ -12,7 +13,6 @@ import ( "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" @@ -54,9 +54,6 @@ func (d *SwaggerDefinition) parseResourcesWithinSwaggerTag(tag *string, resource 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 @@ -69,6 +66,9 @@ func (d *SwaggerDefinition) parseResourcesWithinSwaggerTag(tag *string, resource ResourceIDs: resourceIds.NamesToResourceIDs, } + // then switch out any custom types (e.g. Identity) + resource = switchOutCustomTypesAsNeeded(resource) + // first Normalize the names, meaning `foo` -> `Foo` for consistency resource = normalizeAzureApiResource(resource) @@ -144,21 +144,16 @@ func pullOutModelForListOperations(input map[string]sdkModels.SDKOperation, know 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 { +func switchOutCustomTypesAsNeeded(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 Object Definition for this field if needed - for _, matcher := range commonschema.CustomFieldMatchers { - if matcher.IsMatch(field, result) { + for _, matcher := range commonschema.Matchers { + if matcher.IsMatch(field, input) { field.ObjectDefinition = matcher.ReplacementObjectDefinition() break } @@ -167,10 +162,10 @@ func switchOutCustomTypesAsNeeded(input internal.ParseResult) internal.ParseResu fields[fieldName] = field } model.Fields = fields - result.Models[modelName] = model + output.Models[modelName] = model } - return input + return output } func (d *SwaggerDefinition) findNestedItemsYetToBeParsed(operations map[string]sdkModels.SDKOperation, known internal.ParseResult) (*internal.ParseResult, error) { 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/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{}, +} From 0d1ab7f162cd3e4285383774f99f9ad82c07a5c2 Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Thu, 4 Jul 2024 18:14:46 +0200 Subject: [PATCH 02/58] `tools/importer-rest-api-specs`: refactoring the Constant package --- .../components/parser/models.go | 6 +- .../components/parser/operations.go | 4 +- .../parser/resourceids/parse_segments.go | 4 +- .../components/parser/swagger_resources.go | 6 +- .../parser/constants/extension_parser.go | 82 +++++++++ .../parser/constants/helpers.go | 119 +++++++++++++ .../apidefinitions/parser/constants/parse.go | 156 ++++++++++++++++++ .../parser/constants/parse_test.go} | 0 8 files changed, 367 insertions(+), 10 deletions(-) create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/constants/extension_parser.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/constants/helpers.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/constants/parse.go rename tools/importer-rest-api-specs/{components/parser/constants/interface_test.go => internal/components/apidefinitions/parser/constants/parse_test.go} (100%) diff --git a/tools/importer-rest-api-specs/components/parser/models.go b/tools/importer-rest-api-specs/components/parser/models.go index 56ff76269e0..bda590ffc96 100644 --- a/tools/importer-rest-api-specs/components/parser/models.go +++ b/tools/importer-rest-api-specs/components/parser/models.go @@ -11,8 +11,8 @@ 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" - "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/constants" "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/featureflags" "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/logging" ) @@ -67,7 +67,7 @@ func (d *SwaggerDefinition) findConstantsWithinModel(fieldName string, modelName result.Append(known) if len(input.Enum) > 0 { - constant, err := constants.MapConstant(input.Type, fieldName, modelName, input.Enum, input.Extensions) + constant, err := constants.Parse(input.Type, fieldName, modelName, input.Enum, input.Extensions) if err != nil { return nil, fmt.Errorf("parsing constant: %+v", err) } @@ -528,7 +528,7 @@ func (d SwaggerDefinition) parseObjectDefinition( // 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) + constant, err := constants.Parse(input.Type, propertyName, &modelName, input.Enum, input.Extensions) if err != nil { return nil, nil, fmt.Errorf("parsing constant: %+v", err) } diff --git a/tools/importer-rest-api-specs/components/parser/operations.go b/tools/importer-rest-api-specs/components/parser/operations.go index 22d37f31a11..ee9d7897704 100644 --- a/tools/importer-rest-api-specs/components/parser/operations.go +++ b/tools/importer-rest-api-specs/components/parser/operations.go @@ -12,9 +12,9 @@ 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/components/parser/resourceids" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/constants" "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/logging" ) @@ -380,7 +380,7 @@ func (p operationsParser) optionsForOperation(input parsedOperation) (*map[strin types := []string{ param.Type, } - constant, err := constants.MapConstant(types, param.Name, nil, param.Enum, param.Extensions) + 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) } diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/parse_segments.go b/tools/importer-rest-api-specs/components/parser/resourceids/parse_segments.go index 1c12e4272c1..d889ac14497 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/parse_segments.go +++ b/tools/importer-rest-api-specs/components/parser/resourceids/parse_segments.go @@ -11,8 +11,8 @@ 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/constants" "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/logging" ) @@ -132,7 +132,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) } diff --git a/tools/importer-rest-api-specs/components/parser/swagger_resources.go b/tools/importer-rest-api-specs/components/parser/swagger_resources.go index 26e0d07be41..f8c7dc955b2 100644 --- a/tools/importer-rest-api-specs/components/parser/swagger_resources.go +++ b/tools/importer-rest-api-specs/components/parser/swagger_resources.go @@ -5,7 +5,6 @@ package parser import ( "fmt" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema" "net/http" "strings" @@ -13,9 +12,10 @@ import ( "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/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/components/apidefinitions/parser/commonschema" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/constants" "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/logging" ) @@ -189,7 +189,7 @@ func (d *SwaggerDefinition) findNestedItemsYetToBeParsed(operations map[string]s 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) + parsedAsAConstant, constErr := constants.Parse(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) 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..cb7c9ca858b --- /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/components/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..c510761fd50 --- /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/components/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..930e94d12a5 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/constants/parse.go @@ -0,0 +1,156 @@ +// 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/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("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, 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: 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.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 From 33321b6dc92d4efbed1ca1bdec75727f5a7ead1d Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Fri, 5 Jul 2024 10:05:59 +0200 Subject: [PATCH 03/58] `tools/importer-rest-api-specs`: porting the Network workaround into a Data Workaround --- .../workaround_network_29303.go | 43 +++++++++++++++++++ .../parser/dataworkarounds/workarounds.go | 1 + .../components/parser/parser.go | 18 -------- 3 files changed, 44 insertions(+), 18 deletions(-) create mode 100644 tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_network_29303.go diff --git a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_network_29303.go b/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_network_29303.go new file mode 100644 index 00000000000..f33b4afc550 --- /dev/null +++ b/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_network_29303.go @@ -0,0 +1,43 @@ +package dataworkarounds + +import ( + "fmt" + + importerModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/models" +) + +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(apiDefinition *importerModels.AzureApiDefinition) bool { + serviceMatches := apiDefinition.ServiceName == "Network" + _, hasCorrectlyNamedAPIResource := apiDefinition.Resources["PrivateLinkService"] + _, hasMisnamedAPIResource := apiDefinition.Resources["PrivateLinkServices"] + return serviceMatches && hasCorrectlyNamedAPIResource && hasMisnamedAPIResource +} + +func (workaroundNetwork29303) Name() string { + return "Network / 29303" +} + +func (workaroundNetwork29303) Process(apiDefinition importerModels.AzureApiDefinition) (*importerModels.AzureApiDefinition, error) { + correctlyNamedAPIResource, ok := apiDefinition.Resources["PrivateLinkServices"] + if !ok { + return nil, fmt.Errorf("expected an APIResource named `PrivateLinkServices` but didn't find one") + } + misnamedAPIResource, ok := apiDefinition.Resources["PrivateLinkService"] + if !ok { + return nil, fmt.Errorf("expected an APIResource named `PrivateLinkService` but didn't find one") + } + + apiDefinition.Resources["PrivateLinkServices"] = importerModels.MergeResourcesForTag(correctlyNamedAPIResource, misnamedAPIResource) + delete(apiDefinition.Resources, "PrivateLinkService") + + return &apiDefinition, nil +} diff --git a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workarounds.go b/tools/importer-rest-api-specs/components/parser/dataworkarounds/workarounds.go index eee35cec341..a9eaf9be9ae 100644 --- a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workarounds.go +++ b/tools/importer-rest-api-specs/components/parser/dataworkarounds/workarounds.go @@ -30,6 +30,7 @@ var workarounds = []workaround{ workaroundRecoveryServicesSiteRecovery26680{}, workaroundStreamAnalytics27577{}, workaroundSubscriptions20254{}, + workaroundNetwork29303{}, // These workarounds relate to Terraform specific overrides we want to apply (for example for Resource Generation) workaroundDevCenterIdToRequired{}, diff --git a/tools/importer-rest-api-specs/components/parser/parser.go b/tools/importer-rest-api-specs/components/parser/parser.go index b818e709957..660a8620cde 100644 --- a/tools/importer-rest-api-specs/components/parser/parser.go +++ b/tools/importer-rest-api-specs/components/parser/parser.go @@ -39,24 +39,6 @@ func (d *SwaggerDefinition) parse(serviceName, apiVersion string, resourceProvid } } - // 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) From 5eea5fed95882103b74075ec018526eef5538c81 Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Fri, 5 Jul 2024 11:25:37 +0200 Subject: [PATCH 04/58] `tools/importer-rest-api-specs`: porting the `dataworkarounds` package over to using the SDK types/new path This is a copy and patch up, since I want to leave the existing code path as-is for the moment, to break it apart more effectively --- .../parser/dataworkarounds/README.md | 8 ++ .../dataworkarounds/apply_workarounds.go | 40 ++++++++ .../parser/dataworkarounds/helpers.go | 31 +++++++ .../parser/dataworkarounds/interface.go | 20 ++++ .../workaround_alertsmanagement.go | 61 ++++++++++++ .../workaround_authorization_25080.go | 45 +++++++++ .../workaround_automation_25108.go | 50 ++++++++++ .../workaround_automation_25435.go | 44 +++++++++ .../dataworkarounds/workaround_batch_21291.go | 49 ++++++++++ .../workaround_botservice_27351.go | 83 +++++++++++++++++ .../workaround_containerservice_21394.go | 47 ++++++++++ .../workaround_datafactory_23013.go | 92 +++++++++++++++++++ .../workaround_devcenter_id_to_required.go | 54 +++++++++++ .../workaround_digitaltwins_25120.go | 56 +++++++++++ .../workaround_hdinsight_26838.go | 80 ++++++++++++++++ ...orkaround_inconsistent_swagger_segments.go | 62 +++++++++++++ .../workaround_invalid_go_package_names.go | 50 ++++++++++ .../workaround_loadtest_20961.go | 51 ++++++++++ .../workaround_machinelearning_25142.go | 46 ++++++++++ .../workaround_network_29303.go | 44 +++++++++ .../workaround_newrelic_29256.go | 60 ++++++++++++ .../workaround_operationalinsights_27524.go | 40 ++++++++ ...ound_recoveryservicessiterecovery_26680.go | 89 ++++++++++++++++++ .../dataworkarounds/workaround_redis_22407.go | 44 +++++++++ .../workaround_streamanalytics_27577.go | 73 +++++++++++++++ .../workaround_subscription_20254.go | 53 +++++++++++ ...d_temp_readonly_fields_containerservice.go | 32 +++++++ ...rkaround_temp_readonly_fields_devcenter.go | 35 +++++++ ...orkaround_temp_readonly_fields_loadtest.go | 27 ++++++ ...nd_temp_readonly_fields_managedidentity.go | 37 ++++++++ .../parser/dataworkarounds/workarounds.go | 38 ++++++++ 31 files changed, 1541 insertions(+) create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/README.md create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/apply_workarounds.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/helpers.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/interface.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_alertsmanagement.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_authorization_25080.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_automation_25108.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_automation_25435.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_batch_21291.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_botservice_27351.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_containerservice_21394.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_datafactory_23013.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_devcenter_id_to_required.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_digitaltwins_25120.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_hdinsight_26838.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_inconsistent_swagger_segments.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_invalid_go_package_names.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_loadtest_20961.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_machinelearning_25142.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_network_29303.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_newrelic_29256.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_operationalinsights_27524.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_recoveryservicessiterecovery_26680.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_redis_22407.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_streamanalytics_27577.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_subscription_20254.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_temp_readonly_fields_containerservice.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_temp_readonly_fields_devcenter.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_temp_readonly_fields_loadtest.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_temp_readonly_fields_managedidentity.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workarounds.go 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_workarounds.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/apply_workarounds.go new file mode 100644 index 00000000000..6b300e22404 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/apply_workarounds.go @@ -0,0 +1,40 @@ +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" +) + +// ApplyWorkarounds 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 ApplyWorkarounds(input sdkModels.Service) (*sdkModels.Service, error) { + logging.Debugf("Applying Data Workarounds to the Service %q..", input.Name) + apiVersions := make(map[string]sdkModels.APIVersion) + for _, apiVersion := range input.APIVersions { + logging.Debugf("Applying Data Workarounds to the API Version %q..", apiVersion.APIVersion) + for _, fix := range workarounds { + if !fix.IsApplicable(input.Name, apiVersion) { + logging.Tracef("Data Workaround %q is not applicable - skipping..", fix.Name()) + continue + } + + logging.Tracef("Applying Data Workaround %q..", fix.Name()) + updated, err := fix.Process(apiVersion) + if err != nil { + return nil, fmt.Errorf("applying Swagger Data Workaround %q to Service %q / API Version %q: %+v", fix.Name(), input.Name, apiVersion.APIVersion, err) + } + apiVersion = *updated + logging.Tracef("Applying Data Workaround %q - Completed", fix.Name()) + } + apiVersions[apiVersion.APIVersion] = apiVersion + logging.Debugf("Applying Data Workarounds to the API Version %q - Completed", apiVersion.APIVersion) + } + input.APIVersions = apiVersions + return &input, 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/internal/components/apidefinitions/parser/dataworkarounds/workaround_alertsmanagement.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_alertsmanagement.go new file mode 100644 index 00000000000..d7e67374186 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_alertsmanagement.go @@ -0,0 +1,61 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package dataworkarounds + +import ( + "fmt" + + "github.com/hashicorp/go-azure-helpers/lang/pointer" + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" +) + +var _ workaround = workaroundAlertsManagement{} + +// workaroundAlertsManagement works around the missing discriminator implementations for +// ActionRuleProperties. This workaround can be removed when v4.0 of the AzureRM Provider +// has been released. +type workaroundAlertsManagement struct { +} + +func (workaroundAlertsManagement) IsApplicable(serviceName string, apiVersion sdkModels.APIVersion) bool { + serviceMatches := serviceName == "AlertsManagement" + apiVersionMatches := apiVersion.APIVersion == "2019-05-05-preview" + return serviceMatches && apiVersionMatches +} + +func (workaroundAlertsManagement) Name() string { + return "AlertsManagement" +} + +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`") + } + _, ok = resource.Models["ActionRuleProperties"] + if !ok { + return nil, fmt.Errorf("expected a Resource named `ActionRuleProperties`") + } + + modelNames := []string{ + "ActionGroup", + "Diagnostics", + "Suppression", + } + + for _, name := range modelNames { + model, ok := resource.Models[name] + if !ok { + return nil, fmt.Errorf("expected a Model named `%s`", name) + } + model.DiscriminatedValue = pointer.To(name) + model.ParentTypeName = pointer.To("ActionRuleProperties") + model.FieldNameContainingDiscriminatedValue = pointer.To("Type") + + resource.Models[name] = model + } + input.Resources["ActionRules"] = resource + + return &input, nil +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_authorization_25080.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_authorization_25080.go new file mode 100644 index 00000000000..e8b255b1f72 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_authorization_25080.go @@ -0,0 +1,45 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package dataworkarounds + +import ( + "fmt" + + "github.com/hashicorp/go-azure-helpers/lang/pointer" + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" +) + +var _ workaround = workaroundAuthorization25080{} + +type workaroundAuthorization25080 struct { +} + +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(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") + } + operation, ok := resource.Operations["ListForScope"] + if !ok { + return nil, fmt.Errorf("expected an Operation named `ListForScope` but didn't get one") + } + operation.Options["Filter"] = sdkModels.SDKOperationOption{ + ObjectDefinition: sdkModels.SDKOperationOptionObjectDefinition{ + Type: sdkModels.StringSDKOperationOptionObjectDefinitionType, + }, + QueryStringName: pointer.To("$filter"), + Required: false, + } + resource.Operations["ListForScope"] = operation + input.Resources["RoleManagementPolicies"] = resource + return &input, nil +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_automation_25108.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_automation_25108.go new file mode 100644 index 00000000000..e9a582dd5f6 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_automation_25108.go @@ -0,0 +1,50 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package dataworkarounds + +import ( + "fmt" + + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" +) + +var _ workaround = workaroundAutomation25108{} + +type workaroundAutomation25108 struct { +} + +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 +} + +func (workaroundAutomation25108) Name() string { + return "Automation / 25108" +} + +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`") + } + operation, ok := resource.Operations["HybridRunbookWorkerGroupDelete"] + if !ok { + return nil, fmt.Errorf("expected an Operation named `HybridRunbookWorkerGroupDelete`") + } + + otherResource, ok := input.Resources["HybridRunbookWorkerGroup"] + if !ok { + return nil, fmt.Errorf("expected a Resource named `HybridRunbookWorkerGroup`") + } + if _, hasExisting := otherResource.Operations["Delete"]; hasExisting { + return nil, fmt.Errorf("an existing `Delete` operation exists for `HybridRunbookWorkerGroup`") + } + otherResource.Operations["Delete"] = operation + + input.Resources["HybridRunbookWorkerGroup"] = otherResource + delete(input.Resources, "ListAllHybridRunbookWorkerGroupInAutomationAccount") + + return &input, nil +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_automation_25435.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_automation_25435.go new file mode 100644 index 00000000000..3cfe87e71a9 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_automation_25435.go @@ -0,0 +1,44 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package dataworkarounds + +import ( + "fmt" + + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" +) + +var _ workaround = workaroundAutomation25435{} + +// workaround for https://github.com/Azure/azure-rest-api-specs/pull/25435 +// this is a workaround for the fact that the `CreateOrUpdate` operation for `Python3Package` is not marked as `LongRunning` +type workaroundAutomation25435 struct { +} + +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 +} + +func (workaroundAutomation25435) Name() string { + return "Automation / 25434" +} + +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`") + } + operation, ok := resource.Operations["CreateOrUpdate"] + if !ok { + return nil, fmt.Errorf("expected an Operation named `CreateOrUpdate` for `Python3Package`") + } + + operation.LongRunning = true + resource.Operations["CreateOrUpdate"] = operation + input.Resources["Python3Package"] = resource + + return &input, nil +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_batch_21291.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_batch_21291.go new file mode 100644 index 00000000000..ed9b93ef942 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_batch_21291.go @@ -0,0 +1,49 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package dataworkarounds + +import ( + "fmt" + + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" +) + +var _ workaround = workaroundBatch21291{} + +// workaroundBatch21291 works around the StorageAccountId property being marked as Required which means it isn't +// nullable/removable like it was with the Azure Track1 SDK. +// The Swagger PR: https://github.com/Azure/azure-rest-api-specs/pull/21291 +type workaroundBatch21291 struct { +} + +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 +} + +func (workaroundBatch21291) Name() string { + return "Batch / 21291" +} + +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") + } + model, ok := resource.Models["AutoStorageBaseProperties"] + if !ok { + return nil, fmt.Errorf("couldn't find Model AutoStorageBaseProperties") + } + field, ok := model.Fields["StorageAccountId"] + if !ok { + return nil, fmt.Errorf("couldn't find the field StorageAccountId within model AutoStorageProperties") + } + field.Required = false + + model.Fields["StorageAccountId"] = field + resource.Models["AutoStorageBaseProperties"] = model + input.Resources["BatchAccount"] = resource + return &input, nil +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_botservice_27351.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_botservice_27351.go new file mode 100644 index 00000000000..3215ff9c310 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_botservice_27351.go @@ -0,0 +1,83 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package dataworkarounds + +import ( + "fmt" + + "github.com/hashicorp/go-azure-helpers/lang/pointer" + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" +) + +var _ workaround = workaroundBotService27351{} + +type workaroundBotService27351 struct { +} + +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 + // + // The DELETE and GET define the Uri Parameter `channelName` as a String - whereas the PATCH and POST define it as + // a Constant. + // The result of this is that whilst we switch out the Resource ID for a Common ID, this inconsistency means we end up + // with 2 different Resource IDs in the output, the Common ID (`BotServiceChannelId`) using a Constant Segment and another + // Resource ID (`ChannelId`) using a String segment. + // + // 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 := serviceName == "BotService" + apiVersions := map[string]struct{}{ + "2021-05-01-preview": {}, + "2022-06-15-preview": {}, + "2023-09-15-preview": {}, + "2020-06-02": {}, + "2021-03-01": {}, + "2022-09-15": {}, + } + _, apiVersionMatches := apiVersions[apiVersion.APIVersion] + return serviceMatches && apiVersionMatches +} + +func (workaroundBotService27351) Name() string { + return "BotService / 27351" +} + +func (workaroundBotService27351) Process(input sdkModels.APIVersion) (*sdkModels.APIVersion, error) { + output := input + + resource, ok := output.Resources["Channel"] + if !ok { + return nil, fmt.Errorf("expected a Resource named `Channel` but didn't get one") + } + // ensure we've got the Resource ID we're going to switch over to + if _, ok := resource.ResourceIDs["BotServiceChannelId"]; !ok { + return nil, fmt.Errorf("expected a Resource ID named `BotServiceChannelId` but didn't get one") + } + + // ensure we've got the mismatched Resource ID in order to remove it + if _, ok := resource.ResourceIDs["ChannelId"]; !ok { + return nil, fmt.Errorf("expected a Resource ID named `ChannelId` but didn't get one") + } + delete(resource.ResourceIDs, "ChannelId") + + for operationName, operation := range resource.Operations { + if operation.ResourceIDName != nil && *operation.ResourceIDName == "ChannelId" { + operation.ResourceIDName = pointer.To("BotServiceChannelId") + } + resource.Operations[operationName] = operation + } + + // ensure the Constant `EmailChannelAuthMethod` is updated to be an Integer rather than a Float + constant, ok := resource.Constants["EmailChannelAuthMethod"] + if !ok { + return nil, fmt.Errorf("expected a Constant named `EmailChannelAuthMethod` but didn't get one") + } + constant.Type = sdkModels.IntegerSDKConstantType + resource.Constants["EmailChannelAuthMethod"] = constant + + output.Resources["Channel"] = resource + + return &output, nil +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_containerservice_21394.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_containerservice_21394.go new file mode 100644 index 00000000000..e49f663e8ec --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_containerservice_21394.go @@ -0,0 +1,47 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package dataworkarounds + +import ( + "fmt" + + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" +) + +var _ workaround = workaroundContainerService21394{} + +// workaroundContainerService21394 works around the `DnsPrefix` field being required but being marked as Optional +// Swagger PR: https://github.com/Azure/azure-rest-api-specs/pull/21394 +type workaroundContainerService21394 struct{} + +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(input sdkModels.APIVersion) (*sdkModels.APIVersion, error) { + resource, ok := input.Resources["Fleets"] + if !ok { + return nil, fmt.Errorf("couldn't find API Resource Fleets") + } + model, ok := resource.Models["FleetHubProfile"] + if !ok { + return nil, fmt.Errorf("couldn't find Model FleetHubProfile") + } + field, ok := model.Fields["DnsPrefix"] + if !ok { + return nil, fmt.Errorf("couldn't find field DnsPrefix within model FleetHubProfile") + } + field.Required = true + field.Optional = false + field.ReadOnly = false + + model.Fields["DnsPrefix"] = field + resource.Models["FleetHubProfile"] = model + input.Resources["Fleets"] = resource + return &input, nil +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_datafactory_23013.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_datafactory_23013.go new file mode 100644 index 00000000000..f49b4500b53 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_datafactory_23013.go @@ -0,0 +1,92 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package dataworkarounds + +import ( + "fmt" + + "github.com/hashicorp/go-azure-helpers/lang/pointer" + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" +) + +var _ workaround = workaroundDataFactory23013{} + +// workaroundDataFactory23013 works around the `IntegrationRuntimeReference` and `LinkedServiceReference` models both +// defining a `type` field indicating that these are discriminated types but not defined as a Discriminated Type +// Swagger PR: https://github.com/Azure/azure-rest-api-specs/pull/23013 +type workaroundDataFactory23013 struct{} + +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(input sdkModels.APIVersion) (*sdkModels.APIVersion, error) { + resource, ok := input.Resources["DataFlowDebugSession"] + if !ok { + return nil, fmt.Errorf("couldn't find API Resource DataFlowDebugSession") + } + + // add the new discriminated parent type + resource.Models["Reference"] = sdkModels.SDKModel{ + FieldNameContainingDiscriminatedValue: pointer.To("Type"), + Fields: map[string]sdkModels.SDKField{ + "Type": { + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: true, + JsonName: "type", + }, + }, + } + + // update the existing models to be discriminated types and remove the `type` field from them + modelNames := []string{ + "IntegrationRuntimeReference", + "LinkedServiceReference", + } + for _, modelName := range modelNames { + model, ok := resource.Models[modelName] + if !ok { + return nil, fmt.Errorf("couldn't find model %q", modelName) + } + delete(model.Fields, "Type") + model.ParentTypeName = pointer.To("Reference") + model.FieldNameContainingDiscriminatedValue = pointer.To("Type") + model.DiscriminatedValue = pointer.To(modelName) + resource.Models[modelName] = model + } + + // we need to update the usages of the discriminated types to use the parent + usages := map[string]string{ + "LinkedService": "ConnectVia", + "Dataset": "LinkedServiceName", + "DataFlowStagingInfo": "LinkedService", + } + + for modelName, fieldName := range usages { + model, ok := resource.Models[modelName] + if !ok { + return nil, fmt.Errorf("couldn't find model %q", modelName) + } + field, ok := model.Fields[fieldName] + if !ok { + return nil, fmt.Errorf("couldn't find field %q in model %q", fieldName, modelName) + } + field.ObjectDefinition.ReferenceName = pointer.To("Reference") + + model.Fields[fieldName] = field + resource.Models[modelName] = model + } + + // delete the now unused `Type` constant + delete(resource.Constants, "Type") + + input.Resources["DataFlowDebugSession"] = resource + return &input, nil +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/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 new file mode 100644 index 00000000000..21323b0416e --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_devcenter_id_to_required.go @@ -0,0 +1,54 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package dataworkarounds + +import ( + "fmt" + + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" +) + +// This workaround marks the DevCenter field `DevCenterId` as Required rather than Optional +// since whilst this has a default value when unspecified on the Azure API side this is going +// to cause a diff on our side, which is likely to cause user confusion. As such we'll mark +// this field as Required instead so that it's explicit about what's being deployed. + +var _ workaround = workaroundDevCenterIdToRequired{} + +type workaroundDevCenterIdToRequired struct { +} + +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(input sdkModels.APIVersion) (*sdkModels.APIVersion, error) { + // The Field `DevCenterId` within the SDKModel `Projects` within the APIResource `Projects` should be marked as Required + + projectsResource, ok := input.Resources["Projects"] + if !ok { + return nil, fmt.Errorf("expected a Resource named `Projects` but didn't get one") + } + projectModel, ok := projectsResource.Models["ProjectProperties"] + if !ok { + return nil, fmt.Errorf("expected a Model named `ProjectProperties` but didn't get one") + } + projectDevCenterIdField, ok := projectModel.Fields["DevCenterId"] + if !ok { + return nil, fmt.Errorf("expected a Field named `DevCenterId` but didn't get one") + } + projectDevCenterIdField.Required = true + projectDevCenterIdField.ReadOnly = false + projectDevCenterIdField.Optional = false + projectModel.Fields["DevCenterId"] = projectDevCenterIdField + + projectsResource.Models["ProjectProperties"] = projectModel + input.Resources["Projects"] = projectsResource + + return &input, nil +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_digitaltwins_25120.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_digitaltwins_25120.go new file mode 100644 index 00000000000..23b7151f73b --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_digitaltwins_25120.go @@ -0,0 +1,56 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package dataworkarounds + +import ( + "fmt" + + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" +) + +var _ workaround = workaroundDigitalTwins25120{} + +// Swagger PR: https://github.com/Azure/azure-rest-api-specs/pull/21520 +type workaroundDigitalTwins25120 struct{} + +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 serviceName == "DigitalTwins" && apiVersion.APIVersion == "2023-01-31" +} + +func (workaroundDigitalTwins25120) Name() string { + return "DigitalTwins / 25120" +} + +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`") + } + + model, ok := resource.Models["AzureDataExplorerConnectionProperties"] + if !ok { + return nil, fmt.Errorf("expected a Model named `AzureDataExplorerConnectionProperties`") + } + field, ok := model.Fields["RecordPropertyAndItemRemovals"] + if !ok { + return nil, fmt.Errorf("expected a Field named `RecordPropertyAndItemRemovals`") + } + field.ObjectDefinition = sdkModels.SDKObjectDefinition{ + Type: sdkModels.BooleanSDKObjectDefinitionType, + } + model.Fields["RecordPropertyAndItemRemovals"] = field + resource.Models["AzureDataExplorerConnectionProperties"] = model + + if _, ok := resource.Constants["RecordPropertyAndItemRemovals"]; !ok { + return nil, fmt.Errorf("expected a Constant named `RecordPropertyAndItemRemovals`") + } + delete(resource.Constants, "RecordPropertyAndItemRemovals") + + input.Resources["TimeSeriesDatabaseConnections"] = resource + return &input, nil +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_hdinsight_26838.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_hdinsight_26838.go new file mode 100644 index 00000000000..a1ff2fa49db --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_hdinsight_26838.go @@ -0,0 +1,80 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package dataworkarounds + +import ( + "fmt" + + "github.com/hashicorp/go-azure-helpers/lang/pointer" + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" +) + +var _ workaround = workaroundHDInsight26838{} + +// Swagger PR: https://github.com/Azure/azure-rest-api-specs/pull/26838 +type workaroundHDInsight26838 struct{} + +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 + // PR https://github.com/Azure/azure-rest-api-specs/pull/26838 updates the field within + // the API Definition to be an Enum - so that we can transform this into a Constant. + // + // NOTE: the fix for this currently spans multiple API Versions + supportedApiVersions := map[string]struct{}{ + "2018-06-01-preview": {}, + "2021-06-01": {}, + "2023-04-15-preview": {}, + "2023-08-15-preview": {}, + } + + serviceMatches := serviceName == "HDInsight" + _, apiVersionMatches := supportedApiVersions[apiVersion.APIVersion] + return serviceMatches && apiVersionMatches +} + +func (workaroundHDInsight26838) Name() string { + return "HDInsight / 26838" +} + +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`") + } + + model, ok := resource.Models["ClusterDefinition"] + if !ok { + return nil, fmt.Errorf("expected a Model named `ClusterDefinition`") + } + field, ok := model.Fields["Kind"] + if !ok { + return nil, fmt.Errorf("expected a Field named `Kind`") + } + field.ObjectDefinition = sdkModels.SDKObjectDefinition{ + Type: sdkModels.ReferenceSDKObjectDefinitionType, + ReferenceName: pointer.To("ClusterKind"), + } + model.Fields["Kind"] = field + resource.Models["ClusterDefinition"] = model + + // then add the backing constant + if v, ok := resource.Constants["ClusterKind"]; ok { + return nil, fmt.Errorf("an existing Constant exists with the name `ClusterKind`: %+v", v) + } + resource.Constants["ClusterKind"] = sdkModels.SDKConstant{ + Type: sdkModels.StringSDKConstantType, + Values: map[string]string{ + "Hadoop": "HADOOP", + "HBase": "HBASE", + "Kafka": "KAFKA", + "InteractiveHive": "INTERACTIVEHIVE", + "Spark": "SPARK", + }, + } + + input.Resources["Clusters"] = resource + return &input, nil +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_inconsistent_swagger_segments.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_inconsistent_swagger_segments.go new file mode 100644 index 00000000000..bbd7f2e22be --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_inconsistent_swagger_segments.go @@ -0,0 +1,62 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package dataworkarounds + +import ( + "fmt" + "strings" + + "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" +) + +var _ workaround = workaroundInconsistentlyDefinedSegments{} + +type workaroundInconsistentlyDefinedSegments struct{} + +func (workaroundInconsistentlyDefinedSegments) IsApplicable(_ string, _ sdkModels.APIVersion) bool { + return true +} + +func (workaroundInconsistentlyDefinedSegments) Name() string { + return "Workaround Inconsistently Defined Resource ID Segments" +} + +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 { + id := resource.ResourceIDs[key] + segments := make([]sdkModels.ResourceIDSegment, 0) + for i, val := range id.Segments { + if i > 0 && val.Type == sdkModels.UserSpecifiedResourceIDSegmentType { + // switch out the name of the User Specified Segment presuming it's not an `{val}Id` or `{val}Key`, since these names make more sense + // this works around issues in the Swagger where the same URI may be defined differently depending on the endpoint + if !strings.HasSuffix(val.Name, "Alias") && !strings.HasSuffix(val.Name, "Id") && !strings.HasSuffix(val.Name, "Key") && !strings.HasSuffix(val.Name, "Identifier") { + previous := segments[i-1] + if previous.Type == sdkModels.StaticResourceIDSegmentType && previous.FixedValue != nil { + singularNameOfPrevious := cleanup.GetSingular(*previous.FixedValue) + val.Name = singularNameOfPrevious + if !strings.HasSuffix(val.Name, "Name") { + val.Name = fmt.Sprintf("%sName", singularNameOfPrevious) + } + } + } + } + segments = append(segments, val) + } + id.Segments = segments + id.ExampleValue = helpers.DisplayValueForResourceID(id) + ids[key] = id + } + resource.ResourceIDs = ids + resources[resourceName] = resource + } + input.Resources = resources + return &input, nil +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/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 new file mode 100644 index 00000000000..790fc4ae306 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_invalid_go_package_names.go @@ -0,0 +1,50 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package dataworkarounds + +import ( + "fmt" + "strings" + + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" +) + +var _ workaround = workaroundInvalidGoPackageNames{} + +type workaroundInvalidGoPackageNames struct{} + +func (workaroundInvalidGoPackageNames) IsApplicable(_ string, apiVersion sdkModels.APIVersion) bool { + for key := range apiVersion.Resources { + if strings.EqualFold(key, "documentation") { + return true + } + } + return false +} + +func (workaroundInvalidGoPackageNames) Name() string { + return "Workaround Invalid Go Package Names" +} + +func (workaroundInvalidGoPackageNames) Process(input sdkModels.APIVersion) (*sdkModels.APIVersion, error) { + resources := make(map[string]sdkModels.APIResource, 0) + + for resourceName := range input.Resources { + originalName := resourceName + 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 := 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 + } + input.Resources = resources + return &input, nil +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_loadtest_20961.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_loadtest_20961.go new file mode 100644 index 00000000000..7fbb3dc876b --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_loadtest_20961.go @@ -0,0 +1,51 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package dataworkarounds + +import ( + "fmt" + + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" +) + +var _ workaround = workaroundLoadTest20961{} + +// workaroundLoadTest20961 works around the Patch Model having no type for the Tags field (which is parsed +// as an Object instead). +// Swagger PR: https://github.com/Azure/azure-rest-api-specs/pull/20961 +type workaroundLoadTest20961 struct { +} + +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 +} + +func (workaroundLoadTest20961) Name() string { + return "LoadTest / 20961" +} + +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") + } + model, ok := resource.Models["LoadTestResourceUpdate"] + if !ok { + return nil, fmt.Errorf("couldn't find Model LoadTestResourceUpdate") + } + field, ok := model.Fields["Tags"] + if !ok { + return nil, fmt.Errorf("couldn't find field Tags within model LoadTestResourceUpdate") + } + field.ObjectDefinition = sdkModels.SDKObjectDefinition{ + Type: sdkModels.TagsSDKObjectDefinitionType, + } + + model.Fields["Tags"] = field + resource.Models["LoadTestResourceUpdate"] = model + input.Resources["LoadTests"] = resource + return &input, nil +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_machinelearning_25142.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_machinelearning_25142.go new file mode 100644 index 00000000000..63d9df34dad --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_machinelearning_25142.go @@ -0,0 +1,46 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package dataworkarounds + +import ( + "fmt" + "net/http" + + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" +) + +var _ workaround = workaroundMachineLearning25142{} + +// workaroundMachineLearning25142 works around the missed 202 status code. +// Swagger PR: https://github.com/Azure/azure-rest-api-specs/pull/25142 +type workaroundMachineLearning25142 struct { +} + +func (workaroundMachineLearning25142) IsApplicable(serviceName string, apiVersion sdkModels.APIVersion) bool { + serviceMatches := serviceName == "MachineLearningServices" + apiVersionMatches := apiVersion.APIVersion == "2023-04-01" + return serviceMatches && apiVersionMatches +} + +func (workaroundMachineLearning25142) Name() string { + return "MachineLearningServices / 25142" +} + +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") + } + + operation, ok := resource.Operations["RegistriesCreateOrUpdate"] + if !ok { + return nil, fmt.Errorf("couldn't find Operation RegistriesCreateOrUpdate") + } + + operation.ExpectedStatusCodes = append(operation.ExpectedStatusCodes, http.StatusAccepted) + resource.Operations["RegistriesCreateOrUpdate"] = operation + + 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..5d5c08168f5 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_network_29303.go @@ -0,0 +1,44 @@ +package dataworkarounds + +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 = 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") + } + + input.Resources["PrivateLinkServices"] = importerModels.MergeResourcesForTag(correctlyNamedAPIResource, misnamedAPIResource) + delete(input.Resources, "PrivateLinkService") + + return &input, nil +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_newrelic_29256.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_newrelic_29256.go new file mode 100644 index 00000000000..403be1db58b --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_newrelic_29256.go @@ -0,0 +1,60 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package dataworkarounds + +import ( + "fmt" + + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" +) + +var _ workaround = workaroundNewRelic29256{} + +// workaroundNewRelic29256 works around the Swagger defining Identity as SystemAssignedAndUserAssigned +// when the API only supports SystemAssigned. +// Swagger Issue: https://github.com/Azure/azure-rest-api-specs/issues/29256 +type workaroundNewRelic29256 struct { +} + +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 +} + +func (workaroundNewRelic29256) Name() string { + return "NewRelic / 29256" +} + +func (workaroundNewRelic29256) Process(input sdkModels.APIVersion) (*sdkModels.APIVersion, error) { + // NewRelicMonitorResource,NewRelicMonitorResourceUpdate + resource, ok := input.Resources["Monitors"] + if !ok { + return nil, fmt.Errorf("couldn't find API Resource `Monitors`") + } + + modelsToPatch := []string{ + "NewRelicMonitorResource", + "NewRelicMonitorResourceUpdate", + } + + for _, modelName := range modelsToPatch { + model, ok := resource.Models[modelName] + if !ok { + return nil, fmt.Errorf("couldn't find Model `%s`", modelName) + } + field, ok := model.Fields["Identity"] + if !ok { + return nil, fmt.Errorf("expected the Model `%s` to have a field `Identity` but it didn't exist", modelName) + } + field.ObjectDefinition = sdkModels.SDKObjectDefinition{ + Type: sdkModels.SystemAssignedIdentitySDKObjectDefinitionType, + } + model.Fields["Identity"] = field + resource.Models[modelName] = model + } + + input.Resources["Monitors"] = resource + return &input, nil +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_operationalinsights_27524.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_operationalinsights_27524.go new file mode 100644 index 00000000000..37d438b2bb1 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_operationalinsights_27524.go @@ -0,0 +1,40 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package dataworkarounds + +import ( + "fmt" + + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" +) + +var _ workaround = workaroundOperationalinsights27524{} + +// workaroundOperationalinsights27524 works around the missed 201 status code. +// Swagger PR: https://github.com/Azure/azure-rest-api-specs/pull/27524 +type workaroundOperationalinsights27524 struct{} + +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(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") + } + operation, ok := resource.Operations["CreateOrUpdate"] + 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 + input.Resources["QueryPacks"] = resource + return &input, nil +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_recoveryservicessiterecovery_26680.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_recoveryservicessiterecovery_26680.go new file mode 100644 index 00000000000..558aaf4f70a --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_recoveryservicessiterecovery_26680.go @@ -0,0 +1,89 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package dataworkarounds + +import ( + "fmt" + + "github.com/hashicorp/go-azure-helpers/lang/pointer" + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" +) + +var _ workaround = workaroundRecoveryServicesSiteRecovery26680{} + +// workaroundRecoveryServicesSiteRecovery26680 works around the model `CreateCertificateOptions` being +// present within the API but not being documented in the API Definitions - as such this adds the missing +// model/fields to make this useful. +// +// Swagger PR: https://github.com/Azure/azure-rest-api-specs/pull/26680 +type workaroundRecoveryServicesSiteRecovery26680 struct { +} + +func (w workaroundRecoveryServicesSiteRecovery26680) IsApplicable(serviceName string, apiVersion sdkModels.APIVersion) bool { + if serviceName != "RecoveryServicesSiteRecovery" { + return false + } + + apiVersions := map[string]struct{}{ + "2022-10-01": {}, + "2023-01-01": {}, + "2023-02-01": {}, + "2023-04-01": {}, + "2023-06-01": {}, + } + if _, apiVersionMatches := apiVersions[apiVersion.APIVersion]; !apiVersionMatches { + return false + } + + if _, resourceExists := apiVersions["VaultCertificates"]; !resourceExists { + return false + } + + return true +} + +func (w workaroundRecoveryServicesSiteRecovery26680) Name() string { + return "RecoveryServicesSiteRecovery / 26680" +} + +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") + } + + // 1. Add the missing model + resource.Models["CertificateCreateOptions"] = sdkModels.SDKModel{ + Fields: map[string]sdkModels.SDKField{ + "ValidityInHours": { + Required: false, + JsonName: "validityInHours", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.IntegerSDKObjectDefinitionType, + }, + }, + }, + } + + // 2. Add the field referencing the missing model + model, ok := resource.Models["CertificateRequest"] + if !ok { + return nil, fmt.Errorf("expected a Model named `CertificateRequest` but didn't get one") + } + model.Fields["CertificateCreateOptions"] = sdkModels.SDKField{ + Required: false, + ReadOnly: false, + Sensitive: false, + JsonName: "certificateCreateOptions", + Description: "", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("CertificateCreateOptions"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + } + resource.Models["CertificateRequest"] = model + + input.Resources["VaultCertificates"] = resource + return &input, nil +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_redis_22407.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_redis_22407.go new file mode 100644 index 00000000000..5e987928086 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_redis_22407.go @@ -0,0 +1,44 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package dataworkarounds + +import ( + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" +) + +type workaroundRedis22407 struct { +} + +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 +} + +func (w workaroundRedis22407) Name() string { + return "Redis / 22407" +} + +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 { + model.Fields["NotifyKeyspaceEvents"] = sdkModels.SDKField{ + Required: false, + ReadOnly: false, + Sensitive: false, + JsonName: "notify-keyspace-events", + Description: "The KeySpace Events which should be monitored.", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + } + } + resource.Models["RedisCommonPropertiesRedisConfiguration"] = model + } + input.Resources["Redis"] = resource + } + return &input, nil +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_streamanalytics_27577.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_streamanalytics_27577.go new file mode 100644 index 00000000000..63db159ab0f --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_streamanalytics_27577.go @@ -0,0 +1,73 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package dataworkarounds + +import ( + "fmt" + + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" +) + +// workaroundStreamAnalytics27577 is a workaround to account for StreamAnalytics containing its own +// `Identity` type when it should be picked up as a Common Type. This is because the API Definition +// doesn't fully describe the object - which is what https://github.com/Azure/azure-rest-api-specs/pull/27577 +// looks to fix. +type workaroundStreamAnalytics27577 struct { +} + +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 +} + +func (w workaroundStreamAnalytics27577) Name() string { + return "StreamAnalytics / 27577" +} + +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 input.APIVersion != "2020-03-01" { + apiResourcesToFix = append(apiResourcesToFix, "Subscriptions") + } + + for _, apiResource := range apiResourcesToFix { + resource, ok := input.Resources[apiResource] + if !ok { + return nil, fmt.Errorf("expected to find the API Resource %q but didn't", apiResource) + } + + model, ok := resource.Models["StreamingJob"] + if !ok { + return nil, fmt.Errorf("expected the API Resource %q to contain Model `StreamingJob` it didn't", apiResource) + } + + field, ok := model.Fields["Identity"] + if !ok { + return nil, fmt.Errorf("expected the Model `StreamingJob` to contain a field `Identity` but it didn't") + } + + // update the reference to be a System OR UserAssigned identity + field.ObjectDefinition = sdkModels.SDKObjectDefinition{ + Type: sdkModels.SystemOrUserAssignedIdentityMapSDKObjectDefinitionType, + } + if input.APIVersion == "2020-03-01" { + // however API version 2020-03-01 only supports SystemAssigned + field.ObjectDefinition.Type = sdkModels.SystemAssignedIdentitySDKObjectDefinitionType + } + + model.Fields["Identity"] = field + resource.Models["StreamingJob"] = model + + // then delete the standalone identity model + delete(resource.Models, "Identity") + + input.Resources[apiResource] = resource + } + return &input, nil +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_subscription_20254.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_subscription_20254.go new file mode 100644 index 00000000000..2a1a482b829 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_subscription_20254.go @@ -0,0 +1,53 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package dataworkarounds + +import ( + "fmt" + + "github.com/hashicorp/go-azure-helpers/lang/pointer" + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" +) + +var _ workaround = workaroundSubscriptions20254{} + +// workaroundSubscriptions20254 works around the `SubscriptionCancel` API not exposing the `IgnoreResourceCheck` querystring parameter +// Swagger Issue: https://github.com/Azure/azure-rest-api-specs/issues/20254 +type workaroundSubscriptions20254 struct { +} + +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 sdkModels.APIVersion) (*sdkModels.APIVersion, error) { + output := input + + subscriptionResource, ok := input.Resources["Subscriptions"] + if !ok { + return nil, fmt.Errorf("expected an APIResource named `Subscriptions` but didn't get one") + } + operation, ok := subscriptionResource.Operations["SubscriptionCancel"] + if !ok { + return nil, fmt.Errorf("expected an Operation named `SubscriptionCancel` but didn't get one") + } + operation.Options = map[string]sdkModels.SDKOperationOption{ + // IgnoreResourceCheck allows deleting a Subscription even if it contains nested Resources + "IgnoreResourceCheck": { + QueryStringName: pointer.To("IgnoreResourceCheck"), + ObjectDefinition: sdkModels.SDKOperationOptionObjectDefinition{ + Type: sdkModels.BooleanSDKOperationOptionObjectDefinitionType, + }, + Required: false, + }, + } + subscriptionResource.Operations["SubscriptionCancel"] = operation + output.Resources["Subscriptions"] = subscriptionResource + + return &output, nil +} 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/internal/components/apidefinitions/parser/dataworkarounds/workarounds.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workarounds.go new file mode 100644 index 00000000000..148b45e5771 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workarounds.go @@ -0,0 +1,38 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package dataworkarounds + +var workarounds = []workaround{ + // These workarounds are related to issues with the upstream API Definitions + workaroundAlertsManagement{}, + workaroundAuthorization25080{}, + workaroundDigitalTwins25120{}, + workaroundAutomation25108{}, + workaroundAutomation25435{}, + workaroundBatch21291{}, + workaroundBotService27351{}, + workaroundContainerService21394{}, + workaroundDataFactory23013{}, + workaroundHDInsight26838{}, + workaroundLoadTest20961{}, + workaroundRedis22407{}, + workaroundMachineLearning25142{}, + workaroundNewRelic29256{}, + workaroundOperationalinsights27524{}, + workaroundRecoveryServicesSiteRecovery26680{}, + workaroundStreamAnalytics27577{}, + workaroundSubscriptions20254{}, + workaroundNetwork29303{}, + + // These workarounds relate to Terraform specific overrides we want to apply (for example for Resource Generation) + workaroundDevCenterIdToRequired{}, + workaroundTempReadOnlyFieldsContainerService{}, + workaroundTempReadOnlyFieldsDevCenter{}, + workaroundTempReadOnlyFieldsLoadTest{}, + workaroundTempReadOnlyFieldsManagedIdentity{}, + + // These workarounds relate to data inconsistencies + workaroundInconsistentlyDefinedSegments{}, + workaroundInvalidGoPackageNames{}, +} From bcb98acc476cd4f4e4a5c45b83004c197570a70f Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Fri, 5 Jul 2024 12:38:15 +0200 Subject: [PATCH 05/58] `tools/importer-rest-api-specs`: scaffolding out the logic to parse a Service/APIVersion --- .../components/apidefinitions/parser.go | 34 +++++++++++++++ .../parser/dataworkarounds/apply.go | 35 ++++++++++++++++ .../dataworkarounds/apply_workarounds.go | 40 ------------------ .../apidefinitions/parser/parser.go | 42 +++++++++++++++++++ .../pipeline/stage_parse_data_for_service.go | 21 ++++++---- 5 files changed, 124 insertions(+), 48 deletions(-) create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/apply.go delete mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/apply_workarounds.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parser.go diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser.go index acafd57013c..23ef9722f27 100644 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser.go @@ -2,3 +2,37 @@ // 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" + 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) { + 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 := parser.ParseAPIVersion(input.ServiceName, dataSet) + 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/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/apply_workarounds.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/apply_workarounds.go deleted file mode 100644 index 6b300e22404..00000000000 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/apply_workarounds.go +++ /dev/null @@ -1,40 +0,0 @@ -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" -) - -// ApplyWorkarounds 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 ApplyWorkarounds(input sdkModels.Service) (*sdkModels.Service, error) { - logging.Debugf("Applying Data Workarounds to the Service %q..", input.Name) - apiVersions := make(map[string]sdkModels.APIVersion) - for _, apiVersion := range input.APIVersions { - logging.Debugf("Applying Data Workarounds to the API Version %q..", apiVersion.APIVersion) - for _, fix := range workarounds { - if !fix.IsApplicable(input.Name, apiVersion) { - logging.Tracef("Data Workaround %q is not applicable - skipping..", fix.Name()) - continue - } - - logging.Tracef("Applying Data Workaround %q..", fix.Name()) - updated, err := fix.Process(apiVersion) - if err != nil { - return nil, fmt.Errorf("applying Swagger Data Workaround %q to Service %q / API Version %q: %+v", fix.Name(), input.Name, apiVersion.APIVersion, err) - } - apiVersion = *updated - logging.Tracef("Applying Data Workaround %q - Completed", fix.Name()) - } - apiVersions[apiVersion.APIVersion] = apiVersion - logging.Debugf("Applying Data Workarounds to the API Version %q - Completed", apiVersion.APIVersion) - } - input.APIVersions = apiVersions - return &input, nil -} 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..ff6cc541992 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parser.go @@ -0,0 +1,42 @@ +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/dataworkarounds" + 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 ParseAPIVersion(serviceName string, input discoveryModels.AvailableDataSetForAPIVersion) (*sdkModels.APIVersion, error) { + apiResource := make(map[string]sdkModels.APIResource) + + // Firstly let's go through and process each of the Supplementary Files + for _, filePath := range input.FilePathsContainingSupplementaryData { + logging.Tracef("Processing Supplementary Data from file %q..", filePath) + } + + // Next let's go through and process each of the API Definitions + for _, filePath := range input.FilePathsContainingAPIDefinitions { + logging.Tracef("Processing API Definitions from file %q..", filePath) + } + + apiVersion := sdkModels.APIVersion{ + APIVersion: input.APIVersion, + Generate: true, + Preview: !input.ContainsStableAPIVersion, + Resources: apiResource, + Source: sdkModels.AzureRestAPISpecsSourceDataOrigin, + } + + // Finally let's apply any data workarounds + logging.Debugf("Applying Data Workarounds..") + output, 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.") + + return output, nil +} 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 } From fdd6a18c1a19ab96e68e764511a92e567b233d89 Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Fri, 5 Jul 2024 12:43:04 +0200 Subject: [PATCH 06/58] `tools/importer-rest-api-specs`: porting over the "remove unused items" logic --- .../parser/cleanup/remove_unused_items.go | 282 ++++++++++++++++++ .../apidefinitions/parser/parser.go | 11 +- 2 files changed, 292 insertions(+), 1 deletion(-) create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/remove_unused_items.go diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/remove_unused_items.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/remove_unused_items.go new file mode 100644 index 00000000000..f93e5547211 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/remove_unused_items.go @@ -0,0 +1,282 @@ +package cleanup + +import ( + "strings" + + "github.com/hashicorp/pandora/tools/data-api-sdk/v1/helpers" + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" +) + +func RemoveUnusedItems(input sdkModels.APIVersion) (*sdkModels.APIVersion, error) { + // 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 + 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 + } + unusedResourceIds := findUnusedResourceIds(details.Operations, resourceIdsForThisResource) + for len(unusedResourceIds) > 0 { + for _, resourceIdName := range unusedResourceIds { + delete(resourceIdsForThisResource, resourceIdName) + } + + // then go around again + unusedResourceIds = findUnusedResourceIds(details.Operations, resourceIdsForThisResource) + } + + unusedModels := findUnusedModels(details.Operations, details.Models) + for len(unusedModels) > 0 { + // remove those models + for _, modelName := range unusedModels { + delete(details.Models, modelName) + } + + // then go around again + unusedModels = findUnusedModels(details.Operations, details.Models) + } + + unusedConstants := findUnusedConstants(details.Operations, resourceIdsForThisResource, details.Models, details.Constants) + for len(unusedConstants) > 0 { + // remove those constants + for _, constantName := range unusedConstants { + delete(details.Constants, constantName) + } + + // then go around again + unusedConstants = findUnusedConstants(details.Operations, resourceIdsForThisResource, details.Models, details.Constants) + } + + outputResources[resource] = sdkModels.APIResource{ + Constants: details.Constants, + Models: details.Models, + Operations: details.Operations, + ResourceIDs: resourceIdsForThisResource, + } + } + + input.Resources = outputResources + return &input, nil +} + +func findUnusedConstants(operations map[string]sdkModels.SDKOperation, resourceIds map[string]sdkModels.ResourceID, resourceModels map[string]sdkModels.SDKModel, resourceConstants map[string]sdkModels.SDKConstant) []string { + unusedConstants := make(map[string]struct{}) + for constantName := range resourceConstants { + // constants are either housed inside a Model + usedInAModel := false + for _, model := range resourceModels { + for _, field := range model.Fields { + definition := helpers.InnerMostSDKObjectDefinition(field.ObjectDefinition) + if definition.Type != sdkModels.ReferenceSDKObjectDefinitionType { + continue + } + if *definition.ReferenceName == constantName { + usedInAModel = true + break + } + } + + if usedInAModel { + break + } + } + if usedInAModel { + continue + } + + usedInAnOperation := false + for _, operation := range operations { + if operation.RequestObject != nil { + definition := helpers.InnerMostSDKObjectDefinition(*operation.RequestObject) + if definition.Type == sdkModels.ReferenceSDKObjectDefinitionType && *definition.ReferenceName == constantName { + usedInAnOperation = true + break + } + } + + if operation.ResponseObject != nil { + definition := helpers.InnerMostSDKObjectDefinition(*operation.ResponseObject) + if definition.Type == sdkModels.ReferenceSDKObjectDefinitionType && *definition.ReferenceName == constantName { + usedInAnOperation = true + break + } + } + + for _, v := range operation.Options { + definition := topLevelOptionsObjectDefinition(v.ObjectDefinition) + if definition.Type != sdkModels.ReferenceSDKOperationOptionObjectDefinitionType { + continue + } + if *definition.ReferenceName == constantName { + usedInAnOperation = true + break + } + } + } + if usedInAnOperation { + continue + } + + usedInAResourceId := false + for _, rid := range resourceIds { + for _, segment := range rid.Segments { + if segment.ConstantReference == nil { + continue + } + + if strings.EqualFold(*segment.ConstantReference, constantName) { + usedInAResourceId = true + break + } + } + + if usedInAResourceId { + break + } + } + if usedInAResourceId { + continue + } + + unusedConstants[constantName] = struct{}{} + } + + out := make([]string, 0) + for k := range unusedConstants { + out = append(out, k) + } + + 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 { + if modelName == "" { + // if the model name is empty this is an unused model (so is safe to remove) - but is a bug + // TODO: track down empty models and then remove this + unusedModels[modelName] = struct{}{} + continue + } + + // models are either referenced by operations + usedInAnOperation := false + for _, operation := range operations { + if operation.RequestObject != nil { + definition := helpers.InnerMostSDKObjectDefinition(*operation.RequestObject) + if definition.Type == sdkModels.ReferenceSDKObjectDefinitionType && *definition.ReferenceName == modelName { + usedInAnOperation = true + break + } + } + + if operation.ResponseObject != nil { + definition := helpers.InnerMostSDKObjectDefinition(*operation.ResponseObject) + if definition.Type == sdkModels.ReferenceSDKObjectDefinitionType && *definition.ReferenceName == modelName { + usedInAnOperation = true + break + } + } + + // @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) + if definition.Type != sdkModels.ReferenceSDKOperationOptionObjectDefinitionType { + continue + } + if *definition.ReferenceName == modelName { + usedInAnOperation = true + continue + } + } + } + if usedInAnOperation { + continue + } + + // or on other models + usedInAModel := false + for thisModelName, thisModel := range resourceModels { + if thisModelName == modelName { + continue + } + + for _, field := range thisModel.Fields { + definition := helpers.InnerMostSDKObjectDefinition(field.ObjectDefinition) + if definition.Type != sdkModels.ReferenceSDKObjectDefinitionType { + continue + } + if *definition.ReferenceName == modelName { + usedInAModel = true + break + } + } + + if thisModel.ParentTypeName != nil && *thisModel.ParentTypeName == modelName { + // if this model inherits from the main type (e.g. discriminated) then it + // should be kept + usedInAModel = true + break + } + if model.ParentTypeName != nil && *model.ParentTypeName == thisModelName { + // likewise if it's the inverse + usedInAModel = true + break + } + } + if usedInAModel { + continue + } + + unusedModels[modelName] = struct{}{} + } + + out := make([]string, 0) + for k := range unusedModels { + out = append(out, k) + } + + return out +} + +func findUnusedResourceIds(operations map[string]sdkModels.SDKOperation, resourceIds map[string]sdkModels.ResourceID) []string { + unusedResourceIds := make(map[string]struct{}, 0) + + // first add everything + for name := range resourceIds { + unusedResourceIds[name] = struct{}{} + } + + // then go through and remove the Resource ID if it's used + for _, operation := range operations { + if operation.ResourceIDName == nil { + continue + } + + // since this hasn't been normalized yet, find the correct casing for the key to remove + for key := range unusedResourceIds { + if strings.EqualFold(*operation.ResourceIDName, key) { + delete(unusedResourceIds, key) + } + } + + } + + output := make([]string, 0) + for name := range unusedResourceIds { + output = append(output, name) + } + + return output +} 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 index ff6cc541992..fb1c11d3ecb 100644 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parser.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parser.go @@ -2,6 +2,7 @@ package parser import ( "fmt" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup" sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds" @@ -30,7 +31,7 @@ func ParseAPIVersion(serviceName string, input discoveryModels.AvailableDataSetF Source: sdkModels.AzureRestAPISpecsSourceDataOrigin, } - // Finally let's apply any data workarounds + // Next let's apply any data workarounds logging.Debugf("Applying Data Workarounds..") output, err := dataworkarounds.Apply(serviceName, apiVersion) if err != nil { @@ -38,5 +39,13 @@ func ParseAPIVersion(serviceName string, input discoveryModels.AvailableDataSetF } logging.Debugf("Applying Data Workarounds - Complete.") + // Finally let's remove any unused items + logging.Debugf("Removing unused items..") + output, err = cleanup.RemoveUnusedItems(*output) + if err != nil { + return nil, fmt.Errorf("removing unused items from Service %q / API Version %q: %+v", serviceName, input.APIVersion, err) + } + logging.Debugf("Removing unused items - Complete.") + return output, nil } From d86c9bedf5cc350e7c827d8da990ced4b0a3d522 Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Fri, 5 Jul 2024 13:24:45 +0200 Subject: [PATCH 07/58] `tools/importer-rest-api-specs`: adding a shim to the updated `dataworkarounds` package --- .../parser/dataworkarounds/README.md | 5 - .../parser/dataworkarounds/interface.go | 17 --- .../workaround_alertsmanagement.go | 61 --------- .../workaround_authorization_25080.go | 46 ------- .../workaround_automation_25108.go | 50 ------- .../workaround_automation_25435.go | 44 ------- .../dataworkarounds/workaround_batch_21291.go | 49 ------- .../workaround_botservice_27351.go | 84 ------------ .../workaround_containerservice_21394.go | 47 ------- .../workaround_datafactory_23013.go | 93 ------------- .../workaround_devcenter_id_to_required.go | 54 -------- .../workaround_digitaltwins_25120.go | 57 -------- .../workaround_hdinsight_26838.go | 81 ------------ ...orkaround_inconsistent_swagger_segments.go | 65 --------- .../workaround_invalid_go_package_names.go | 51 -------- .../workaround_loadtest_20961.go | 52 -------- .../workaround_machinelearning_25142.go | 46 ------- .../workaround_network_29303.go | 43 ------ .../workaround_newrelic_29256.go | 61 --------- .../workaround_operationalinsights_27524.go | 39 ------ ...ound_recoveryservicessiterecovery_26680.go | 90 ------------- .../dataworkarounds/workaround_redis_22407.go | 45 ------- .../workaround_streamanalytics_27577.go | 74 ----------- .../workaround_subscription_20254.go | 54 -------- .../workaround_temp_readonly_fields.go | 123 ------------------ .../parser/dataworkarounds/workarounds.go | 63 --------- ...orkaround_inconsistent_swagger_segments.go | 2 + 27 files changed, 2 insertions(+), 1494 deletions(-) delete mode 100644 tools/importer-rest-api-specs/components/parser/dataworkarounds/README.md delete mode 100644 tools/importer-rest-api-specs/components/parser/dataworkarounds/interface.go delete mode 100644 tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_alertsmanagement.go delete mode 100644 tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_authorization_25080.go delete mode 100644 tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_automation_25108.go delete mode 100644 tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_automation_25435.go delete mode 100644 tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_batch_21291.go delete mode 100644 tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_botservice_27351.go delete mode 100644 tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_containerservice_21394.go delete mode 100644 tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_datafactory_23013.go delete mode 100644 tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_devcenter_id_to_required.go delete mode 100644 tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_digitaltwins_25120.go delete mode 100644 tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_hdinsight_26838.go delete mode 100644 tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_inconsistent_swagger_segments.go delete mode 100644 tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_invalid_go_package_names.go delete mode 100644 tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_loadtest_20961.go delete mode 100644 tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_machinelearning_25142.go delete mode 100644 tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_network_29303.go delete mode 100644 tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_newrelic_29256.go delete mode 100644 tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_operationalinsights_27524.go delete mode 100644 tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_recoveryservicessiterecovery_26680.go delete mode 100644 tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_redis_22407.go delete mode 100644 tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_streamanalytics_27577.go delete mode 100644 tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_subscription_20254.go delete mode 100644 tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_temp_readonly_fields.go delete mode 100644 tools/importer-rest-api-specs/components/parser/dataworkarounds/workarounds.go 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_alertsmanagement.go b/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_alertsmanagement.go deleted file mode 100644 index 0f63bfb3784..00000000000 --- a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_alertsmanagement.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package dataworkarounds - -import ( - "fmt" - - "github.com/hashicorp/go-azure-helpers/lang/pointer" - importerModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/models" -) - -var _ workaround = workaroundAlertsManagement{} - -// workaroundAlertsManagement works around the missing discriminator implementations for -// ActionRuleProperties. This workaround can be removed when v4.0 of the AzureRM Provider -// has been released. -type workaroundAlertsManagement struct { -} - -func (workaroundAlertsManagement) IsApplicable(apiDefinition *importerModels.AzureApiDefinition) bool { - serviceMatches := apiDefinition.ServiceName == "AlertsManagement" - apiVersionMatches := apiDefinition.ApiVersion == "2019-05-05-preview" - return serviceMatches && apiVersionMatches -} - -func (workaroundAlertsManagement) Name() string { - return "AlertsManagement" -} - -func (workaroundAlertsManagement) Process(apiDefinition importerModels.AzureApiDefinition) (*importerModels.AzureApiDefinition, error) { - resource, ok := apiDefinition.Resources["ActionRules"] - if !ok { - return nil, fmt.Errorf("expected a Resource named `ActionRules`") - } - _, ok = resource.Models["ActionRuleProperties"] - if !ok { - return nil, fmt.Errorf("expected a Resource named `ActionRuleProperties`") - } - - modelNames := []string{ - "ActionGroup", - "Diagnostics", - "Suppression", - } - - for _, name := range modelNames { - model, ok := resource.Models[name] - if !ok { - return nil, fmt.Errorf("expected a Model named `%s`", name) - } - model.DiscriminatedValue = pointer.To(name) - model.ParentTypeName = pointer.To("ActionRuleProperties") - model.FieldNameContainingDiscriminatedValue = pointer.To("Type") - - resource.Models[name] = model - } - apiDefinition.Resources["ActionRules"] = resource - - return &apiDefinition, nil -} diff --git a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_authorization_25080.go b/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_authorization_25080.go deleted file mode 100644 index 694c8b0e832..00000000000 --- a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_authorization_25080.go +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package dataworkarounds - -import ( - "fmt" - - "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{} - -type workaroundAuthorization25080 struct { -} - -func (workaroundAuthorization25080) IsApplicable(apiDefinition *importerModels.AzureApiDefinition) bool { - return apiDefinition.ServiceName == "Authorization" && apiDefinition.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"] - if !ok { - return nil, fmt.Errorf("expected a Resource named `RoleManagementPolicies` but didn't get one") - } - operation, ok := resource.Operations["ListForScope"] - if !ok { - return nil, fmt.Errorf("expected an Operation named `ListForScope` but didn't get one") - } - operation.Options["Filter"] = sdkModels.SDKOperationOption{ - ObjectDefinition: sdkModels.SDKOperationOptionObjectDefinition{ - Type: sdkModels.StringSDKOperationOptionObjectDefinitionType, - }, - QueryStringName: pointer.To("$filter"), - Required: false, - } - resource.Operations["ListForScope"] = operation - apiDefinition.Resources["RoleManagementPolicies"] = resource - return &apiDefinition, nil -} diff --git a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_automation_25108.go b/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_automation_25108.go deleted file mode 100644 index f4b82e931c2..00000000000 --- a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_automation_25108.go +++ /dev/null @@ -1,50 +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 = 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" - return serviceMatches && apiVersionMatches -} - -func (workaroundAutomation25108) Name() string { - return "Automation / 25108" -} - -func (workaroundAutomation25108) Process(apiDefinition importerModels.AzureApiDefinition) (*importerModels.AzureApiDefinition, error) { - resource, ok := apiDefinition.Resources["ListAllHybridRunbookWorkerGroupInAutomationAccount"] - if !ok { - return nil, fmt.Errorf("expected a Resource named `ListAllHybridRunbookWorkerGroupInAutomationAccount`") - } - operation, ok := resource.Operations["HybridRunbookWorkerGroupDelete"] - if !ok { - return nil, fmt.Errorf("expected an Operation named `HybridRunbookWorkerGroupDelete`") - } - - otherResource, ok := apiDefinition.Resources["HybridRunbookWorkerGroup"] - if !ok { - return nil, fmt.Errorf("expected a Resource named `HybridRunbookWorkerGroup`") - } - if _, hasExisting := otherResource.Operations["Delete"]; hasExisting { - return nil, fmt.Errorf("an existing `Delete` operation exists for `HybridRunbookWorkerGroup`") - } - otherResource.Operations["Delete"] = operation - - apiDefinition.Resources["HybridRunbookWorkerGroup"] = otherResource - delete(apiDefinition.Resources, "ListAllHybridRunbookWorkerGroupInAutomationAccount") - - return &apiDefinition, nil -} diff --git a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_automation_25435.go b/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_automation_25435.go deleted file mode 100644 index 73b74499087..00000000000 --- a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_automation_25435.go +++ /dev/null @@ -1,44 +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 = workaroundAutomation25435{} - -// workaround for https://github.com/Azure/azure-rest-api-specs/pull/25435 -// this is a workaround for the fact that the `CreateOrUpdate` operation for `Python3Package` is not marked as `LongRunning` -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" - return serviceMatches && apiVersionMatches -} - -func (workaroundAutomation25435) Name() string { - return "Automation / 25434" -} - -func (workaroundAutomation25435) Process(apiDefinition importerModels.AzureApiDefinition) (*importerModels.AzureApiDefinition, error) { - resource, ok := apiDefinition.Resources["Python3Package"] - if !ok { - return nil, fmt.Errorf("expected a Resource named `Python3Package`") - } - operation, ok := resource.Operations["CreateOrUpdate"] - if !ok { - return nil, fmt.Errorf("expected an Operation named `CreateOrUpdate` for `Python3Package`") - } - - operation.LongRunning = true - resource.Operations["CreateOrUpdate"] = operation - apiDefinition.Resources["Python3Package"] = resource - - return &apiDefinition, nil -} diff --git a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_batch_21291.go b/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_batch_21291.go deleted file mode 100644 index 9ae5a6e243d..00000000000 --- a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_batch_21291.go +++ /dev/null @@ -1,49 +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 = workaroundBatch21291{} - -// workaroundBatch21291 works around the StorageAccountId property being marked as Required which means it isn't -// nullable/removable like it was with the Azure Track1 SDK. -// The Swagger PR: https://github.com/Azure/azure-rest-api-specs/pull/21291 -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" - return serviceMatches && apiVersionMatches -} - -func (workaroundBatch21291) Name() string { - return "Batch / 21291" -} - -func (workaroundBatch21291) Process(apiDefinition importerModels.AzureApiDefinition) (*importerModels.AzureApiDefinition, error) { - resource, ok := apiDefinition.Resources["BatchAccount"] - if !ok { - return nil, fmt.Errorf("couldn't find API Resource BatchAccount") - } - model, ok := resource.Models["AutoStorageBaseProperties"] - if !ok { - return nil, fmt.Errorf("couldn't find Model AutoStorageBaseProperties") - } - field, ok := model.Fields["StorageAccountId"] - if !ok { - return nil, fmt.Errorf("couldn't find the field StorageAccountId within model AutoStorageProperties") - } - field.Required = false - - model.Fields["StorageAccountId"] = field - resource.Models["AutoStorageBaseProperties"] = model - apiDefinition.Resources["BatchAccount"] = resource - return &apiDefinition, nil -} diff --git a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_botservice_27351.go b/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_botservice_27351.go deleted file mode 100644 index 79d94c0c7c4..00000000000 --- a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_botservice_27351.go +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package dataworkarounds - -import ( - "fmt" - - "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{} - -type workaroundBotService27351 struct { -} - -func (workaroundBotService27351) IsApplicable(apiDefinition *importerModels.AzureApiDefinition) 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 - // - // The DELETE and GET define the Uri Parameter `channelName` as a String - whereas the PATCH and POST define it as - // a Constant. - // The result of this is that whilst we switch out the Resource ID for a Common ID, this inconsistency means we end up - // with 2 different Resource IDs in the output, the Common ID (`BotServiceChannelId`) using a Constant Segment and another - // Resource ID (`ChannelId`) using a String segment. - // - // 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" - apiVersions := map[string]struct{}{ - "2021-05-01-preview": {}, - "2022-06-15-preview": {}, - "2023-09-15-preview": {}, - "2020-06-02": {}, - "2021-03-01": {}, - "2022-09-15": {}, - } - _, apiVersionMatches := apiVersions[apiDefinition.ApiVersion] - return serviceMatches && apiVersionMatches -} - -func (workaroundBotService27351) Name() string { - return "BotService / 27351" -} - -func (workaroundBotService27351) Process(input importerModels.AzureApiDefinition) (*importerModels.AzureApiDefinition, error) { - output := input - - resource, ok := output.Resources["Channel"] - if !ok { - return nil, fmt.Errorf("expected a Resource named `Channel` but didn't get one") - } - // ensure we've got the Resource ID we're going to switch over to - if _, ok := resource.ResourceIDs["BotServiceChannelId"]; !ok { - return nil, fmt.Errorf("expected a Resource ID named `BotServiceChannelId` but didn't get one") - } - - // ensure we've got the mismatched Resource ID in order to remove it - if _, ok := resource.ResourceIDs["ChannelId"]; !ok { - return nil, fmt.Errorf("expected a Resource ID named `ChannelId` but didn't get one") - } - delete(resource.ResourceIDs, "ChannelId") - - for operationName, operation := range resource.Operations { - if operation.ResourceIDName != nil && *operation.ResourceIDName == "ChannelId" { - operation.ResourceIDName = pointer.To("BotServiceChannelId") - } - resource.Operations[operationName] = operation - } - - // ensure the Constant `EmailChannelAuthMethod` is updated to be an Integer rather than a Float - constant, ok := resource.Constants["EmailChannelAuthMethod"] - if !ok { - return nil, fmt.Errorf("expected a Constant named `EmailChannelAuthMethod` but didn't get one") - } - constant.Type = sdkModels.IntegerSDKConstantType - resource.Constants["EmailChannelAuthMethod"] = constant - - output.Resources["Channel"] = resource - - return &output, nil -} diff --git a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_containerservice_21394.go b/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_containerservice_21394.go deleted file mode 100644 index b779be6f421..00000000000 --- a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_containerservice_21394.go +++ /dev/null @@ -1,47 +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 = workaroundContainerService21394{} - -// workaroundContainerService21394 works around the `DnsPrefix` field being required but being marked as Optional -// 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) Name() string { - return "ContainerService / 21394" -} - -func (workaroundContainerService21394) Process(apiDefinition importerModels.AzureApiDefinition) (*importerModels.AzureApiDefinition, error) { - resource, ok := apiDefinition.Resources["Fleets"] - if !ok { - return nil, fmt.Errorf("couldn't find API Resource Fleets") - } - model, ok := resource.Models["FleetHubProfile"] - if !ok { - return nil, fmt.Errorf("couldn't find Model FleetHubProfile") - } - field, ok := model.Fields["DnsPrefix"] - if !ok { - return nil, fmt.Errorf("couldn't find field DnsPrefix within model FleetHubProfile") - } - field.Required = true - field.Optional = false - field.ReadOnly = false - - model.Fields["DnsPrefix"] = field - resource.Models["FleetHubProfile"] = model - apiDefinition.Resources["Fleets"] = resource - return &apiDefinition, nil -} diff --git a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_datafactory_23013.go b/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_datafactory_23013.go deleted file mode 100644 index 2f6e888b06a..00000000000 --- a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_datafactory_23013.go +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package dataworkarounds - -import ( - "fmt" - - "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{} - -// workaroundDataFactory23013 works around the `IntegrationRuntimeReference` and `LinkedServiceReference` models both -// defining a `type` field indicating that these are discriminated types but not defined as a Discriminated Type -// 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) Name() string { - return "DataFactory / 23013" -} - -func (workaroundDataFactory23013) Process(apiDefinition importerModels.AzureApiDefinition) (*importerModels.AzureApiDefinition, error) { - resource, ok := apiDefinition.Resources["DataFlowDebugSession"] - if !ok { - return nil, fmt.Errorf("couldn't find API Resource DataFlowDebugSession") - } - - // add the new discriminated parent type - resource.Models["Reference"] = sdkModels.SDKModel{ - FieldNameContainingDiscriminatedValue: pointer.To("Type"), - Fields: map[string]sdkModels.SDKField{ - "Type": { - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: true, - JsonName: "type", - }, - }, - } - - // update the existing models to be discriminated types and remove the `type` field from them - modelNames := []string{ - "IntegrationRuntimeReference", - "LinkedServiceReference", - } - for _, modelName := range modelNames { - model, ok := resource.Models[modelName] - if !ok { - return nil, fmt.Errorf("couldn't find model %q", modelName) - } - delete(model.Fields, "Type") - model.ParentTypeName = pointer.To("Reference") - model.FieldNameContainingDiscriminatedValue = pointer.To("Type") - model.DiscriminatedValue = pointer.To(modelName) - resource.Models[modelName] = model - } - - // we need to update the usages of the discriminated types to use the parent - usages := map[string]string{ - "LinkedService": "ConnectVia", - "Dataset": "LinkedServiceName", - "DataFlowStagingInfo": "LinkedService", - } - - for modelName, fieldName := range usages { - model, ok := resource.Models[modelName] - if !ok { - return nil, fmt.Errorf("couldn't find model %q", modelName) - } - field, ok := model.Fields[fieldName] - if !ok { - return nil, fmt.Errorf("couldn't find field %q in model %q", fieldName, modelName) - } - field.ObjectDefinition.ReferenceName = pointer.To("Reference") - - model.Fields[fieldName] = field - resource.Models[modelName] = model - } - - // delete the now unused `Type` constant - delete(resource.Constants, "Type") - - apiDefinition.Resources["DataFlowDebugSession"] = resource - return &apiDefinition, nil -} diff --git a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_devcenter_id_to_required.go b/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_devcenter_id_to_required.go deleted file mode 100644 index 7449ae17e00..00000000000 --- a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_devcenter_id_to_required.go +++ /dev/null @@ -1,54 +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" -) - -// This workaround marks the DevCenter field `DevCenterId` as Required rather than Optional -// since whilst this has a default value when unspecified on the Azure API side this is going -// to cause a diff on our side, which is likely to cause user confusion. As such we'll mark -// this field as Required instead so that it's explicit about what's being deployed. - -var _ workaround = workaroundDevCenterIdToRequired{} - -type workaroundDevCenterIdToRequired struct { -} - -func (workaroundDevCenterIdToRequired) IsApplicable(apiDefinition *importerModels.AzureApiDefinition) bool { - return apiDefinition.ServiceName == "DevCenter" && apiDefinition.ApiVersion == "2023-04-01" -} - -func (workaroundDevCenterIdToRequired) Name() string { - return "DevCenter" -} - -func (workaroundDevCenterIdToRequired) Process(apiDefinition importerModels.AzureApiDefinition) (*importerModels.AzureApiDefinition, error) { - // The Field `DevCenterId` within the SDKModel `Projects` within the APIResource `Projects` should be marked as Required - - projectsResource, ok := apiDefinition.Resources["Projects"] - if !ok { - return nil, fmt.Errorf("expected a Resource named `Projects` but didn't get one") - } - projectModel, ok := projectsResource.Models["ProjectProperties"] - if !ok { - return nil, fmt.Errorf("expected a Model named `ProjectProperties` but didn't get one") - } - projectDevCenterIdField, ok := projectModel.Fields["DevCenterId"] - if !ok { - return nil, fmt.Errorf("expected a Field named `DevCenterId` but didn't get one") - } - projectDevCenterIdField.Required = true - projectDevCenterIdField.ReadOnly = false - projectDevCenterIdField.Optional = false - projectModel.Fields["DevCenterId"] = projectDevCenterIdField - - projectsResource.Models["ProjectProperties"] = projectModel - apiDefinition.Resources["Projects"] = projectsResource - - return &apiDefinition, nil -} diff --git a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_digitaltwins_25120.go b/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_digitaltwins_25120.go deleted file mode 100644 index da2d980afa6..00000000000 --- a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_digitaltwins_25120.go +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package dataworkarounds - -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{} - -// Swagger PR: https://github.com/Azure/azure-rest-api-specs/pull/21520 -type workaroundDigitalTwins25120 struct{} - -func (workaroundDigitalTwins25120) IsApplicable(apiDefinition *importerModels.AzureApiDefinition) 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" -} - -func (workaroundDigitalTwins25120) Name() string { - return "DigitalTwins / 25120" -} - -func (workaroundDigitalTwins25120) Process(apiDefinition importerModels.AzureApiDefinition) (*importerModels.AzureApiDefinition, error) { - resource, ok := apiDefinition.Resources["TimeSeriesDatabaseConnections"] - if !ok { - return nil, fmt.Errorf("expected a Resource named `TimeSeriesDatabaseConnections`") - } - - model, ok := resource.Models["AzureDataExplorerConnectionProperties"] - if !ok { - return nil, fmt.Errorf("expected a Model named `AzureDataExplorerConnectionProperties`") - } - field, ok := model.Fields["RecordPropertyAndItemRemovals"] - if !ok { - return nil, fmt.Errorf("expected a Field named `RecordPropertyAndItemRemovals`") - } - field.ObjectDefinition = sdkModels.SDKObjectDefinition{ - Type: sdkModels.BooleanSDKObjectDefinitionType, - } - model.Fields["RecordPropertyAndItemRemovals"] = field - resource.Models["AzureDataExplorerConnectionProperties"] = model - - if _, ok := resource.Constants["RecordPropertyAndItemRemovals"]; !ok { - return nil, fmt.Errorf("expected a Constant named `RecordPropertyAndItemRemovals`") - } - delete(resource.Constants, "RecordPropertyAndItemRemovals") - - apiDefinition.Resources["TimeSeriesDatabaseConnections"] = resource - return &apiDefinition, nil -} diff --git a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_hdinsight_26838.go b/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_hdinsight_26838.go deleted file mode 100644 index 2d07876bafb..00000000000 --- a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_hdinsight_26838.go +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package dataworkarounds - -import ( - "fmt" - - "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{} - -// Swagger PR: https://github.com/Azure/azure-rest-api-specs/pull/26838 -type workaroundHDInsight26838 struct{} - -func (workaroundHDInsight26838) IsApplicable(apiDefinition *importerModels.AzureApiDefinition) 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 - // PR https://github.com/Azure/azure-rest-api-specs/pull/26838 updates the field within - // the API Definition to be an Enum - so that we can transform this into a Constant. - // - // NOTE: the fix for this currently spans multiple API Versions - supportedApiVersions := map[string]struct{}{ - "2018-06-01-preview": {}, - "2021-06-01": {}, - "2023-04-15-preview": {}, - "2023-08-15-preview": {}, - } - - serviceMatches := apiDefinition.ServiceName == "HDInsight" - _, apiVersionMatches := supportedApiVersions[apiDefinition.ApiVersion] - return serviceMatches && apiVersionMatches -} - -func (workaroundHDInsight26838) Name() string { - return "HDInsight / 26838" -} - -func (workaroundHDInsight26838) Process(apiDefinition importerModels.AzureApiDefinition) (*importerModels.AzureApiDefinition, error) { - resource, ok := apiDefinition.Resources["Clusters"] - if !ok { - return nil, fmt.Errorf("expected a Resource named `Clusters`") - } - - model, ok := resource.Models["ClusterDefinition"] - if !ok { - return nil, fmt.Errorf("expected a Model named `ClusterDefinition`") - } - field, ok := model.Fields["Kind"] - if !ok { - return nil, fmt.Errorf("expected a Field named `Kind`") - } - field.ObjectDefinition = sdkModels.SDKObjectDefinition{ - Type: sdkModels.ReferenceSDKObjectDefinitionType, - ReferenceName: pointer.To("ClusterKind"), - } - model.Fields["Kind"] = field - resource.Models["ClusterDefinition"] = model - - // then add the backing constant - if v, ok := resource.Constants["ClusterKind"]; ok { - return nil, fmt.Errorf("an existing Constant exists with the name `ClusterKind`: %+v", v) - } - resource.Constants["ClusterKind"] = sdkModels.SDKConstant{ - Type: sdkModels.StringSDKConstantType, - Values: map[string]string{ - "Hadoop": "HADOOP", - "HBase": "HBASE", - "Kafka": "KAFKA", - "InteractiveHive": "INTERACTIVEHIVE", - "Spark": "SPARK", - }, - } - - apiDefinition.Resources["Clusters"] = resource - return &apiDefinition, nil -} diff --git a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_inconsistent_swagger_segments.go b/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_inconsistent_swagger_segments.go deleted file mode 100644 index a20ce9cfdb2..00000000000 --- a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_inconsistent_swagger_segments.go +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package dataworkarounds - -import ( - "fmt" - "strings" - - "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" -) - -var _ workaround = workaroundInconsistentlyDefinedSegments{} - -type workaroundInconsistentlyDefinedSegments struct{} - -func (workaroundInconsistentlyDefinedSegments) IsApplicable(_ *importerModels.AzureApiDefinition) bool { - return true -} - -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] - - ids := make(map[string]sdkModels.ResourceID) - for key := range resource.ResourceIDs { - id := resource.ResourceIDs[key] - segments := make([]sdkModels.ResourceIDSegment, 0) - for i, val := range id.Segments { - if i > 0 && val.Type == sdkModels.UserSpecifiedResourceIDSegmentType { - // switch out the name of the User Specified Segment presuming it's not an `{val}Id` or `{val}Key`, since these names make more sense - // this works around issues in the Swagger where the same URI may be defined differently depending on the endpoint - if !strings.HasSuffix(val.Name, "Alias") && !strings.HasSuffix(val.Name, "Id") && !strings.HasSuffix(val.Name, "Key") && !strings.HasSuffix(val.Name, "Identifier") { - previous := segments[i-1] - if previous.Type == sdkModels.StaticResourceIDSegmentType && previous.FixedValue != nil { - singularNameOfPrevious := cleanup.GetSingular(*previous.FixedValue) - val.Name = singularNameOfPrevious - if !strings.HasSuffix(val.Name, "Name") { - val.Name = fmt.Sprintf("%sName", singularNameOfPrevious) - // the parser sets the same value into Name and ExampleValue so this needs to be applied to ExampleValue as well - val.ExampleValue = fmt.Sprintf("%sName", singularNameOfPrevious) - } - } - } - } - segments = append(segments, val) - } - id.Segments = segments - id.ExampleValue = helpers.DisplayValueForResourceID(id) - ids[key] = id - } - resource.ResourceIDs = ids - resources[resourceName] = resource - } - apiDefinition.Resources = resources - return &apiDefinition, nil -} diff --git a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_invalid_go_package_names.go b/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_invalid_go_package_names.go deleted file mode 100644 index 14887002041..00000000000 --- a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_invalid_go_package_names.go +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package dataworkarounds - -import ( - "fmt" - "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 { - if strings.EqualFold(key, "documentation") { - return true - } - } - return false -} - -func (workaroundInvalidGoPackageNames) Name() string { - return "Workaround Invalid Go Package Names" -} - -func (workaroundInvalidGoPackageNames) Process(apiDefinition importerModels.AzureApiDefinition) (*importerModels.AzureApiDefinition, error) { - resources := make(map[string]sdkModels.APIResource, 0) - - for resourceName := range apiDefinition.Resources { - originalName := resourceName - resource := apiDefinition.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 { - 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 -} diff --git a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_loadtest_20961.go b/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_loadtest_20961.go deleted file mode 100644 index 2dd17ada4c4..00000000000 --- a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_loadtest_20961.go +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package dataworkarounds - -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{} - -// workaroundLoadTest20961 works around the Patch Model having no type for the Tags field (which is parsed -// as an Object instead). -// Swagger PR: https://github.com/Azure/azure-rest-api-specs/pull/20961 -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" - return serviceMatches && apiVersionMatches -} - -func (workaroundLoadTest20961) Name() string { - return "LoadTest / 20961" -} - -func (workaroundLoadTest20961) Process(apiDefinition importerModels.AzureApiDefinition) (*importerModels.AzureApiDefinition, error) { - resource, ok := apiDefinition.Resources["LoadTests"] - if !ok { - return nil, fmt.Errorf("couldn't find API Resource LoadTests") - } - model, ok := resource.Models["LoadTestResourceUpdate"] - if !ok { - return nil, fmt.Errorf("couldn't find Model LoadTestResourceUpdate") - } - field, ok := model.Fields["Tags"] - if !ok { - return nil, fmt.Errorf("couldn't find field Tags within model LoadTestResourceUpdate") - } - field.ObjectDefinition = sdkModels.SDKObjectDefinition{ - Type: sdkModels.TagsSDKObjectDefinitionType, - } - - model.Fields["Tags"] = field - resource.Models["LoadTestResourceUpdate"] = model - apiDefinition.Resources["LoadTests"] = resource - return &apiDefinition, nil -} diff --git a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_machinelearning_25142.go b/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_machinelearning_25142.go deleted file mode 100644 index ef7f2d7e9d8..00000000000 --- a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_machinelearning_25142.go +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package dataworkarounds - -import ( - "fmt" - "net/http" - - importerModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/models" -) - -var _ workaround = workaroundMachineLearning25142{} - -// workaroundMachineLearning25142 works around the missed 202 status code. -// Swagger PR: https://github.com/Azure/azure-rest-api-specs/pull/25142 -type workaroundMachineLearning25142 struct { -} - -func (workaroundMachineLearning25142) IsApplicable(apiDefinition *importerModels.AzureApiDefinition) bool { - serviceMatches := apiDefinition.ServiceName == "MachineLearningServices" - apiVersionMatches := apiDefinition.ApiVersion == "2023-04-01" - return serviceMatches && apiVersionMatches -} - -func (workaroundMachineLearning25142) Name() string { - return "MachineLearningServices / 25142" -} - -func (workaroundMachineLearning25142) Process(apiDefinition importerModels.AzureApiDefinition) (*importerModels.AzureApiDefinition, error) { - resource, ok := apiDefinition.Resources["RegistryManagement"] - if !ok { - return nil, fmt.Errorf("couldn't find API Resource RegistryManagement") - } - - operation, ok := resource.Operations["RegistriesCreateOrUpdate"] - if !ok { - return nil, fmt.Errorf("couldn't find Operation RegistriesCreateOrUpdate") - } - - operation.ExpectedStatusCodes = append(operation.ExpectedStatusCodes, http.StatusAccepted) - resource.Operations["RegistriesCreateOrUpdate"] = operation - - apiDefinition.Resources["RegistryManagement"] = resource - return &apiDefinition, nil -} diff --git a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_network_29303.go b/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_network_29303.go deleted file mode 100644 index f33b4afc550..00000000000 --- a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_network_29303.go +++ /dev/null @@ -1,43 +0,0 @@ -package dataworkarounds - -import ( - "fmt" - - importerModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/models" -) - -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(apiDefinition *importerModels.AzureApiDefinition) bool { - serviceMatches := apiDefinition.ServiceName == "Network" - _, hasCorrectlyNamedAPIResource := apiDefinition.Resources["PrivateLinkService"] - _, hasMisnamedAPIResource := apiDefinition.Resources["PrivateLinkServices"] - return serviceMatches && hasCorrectlyNamedAPIResource && hasMisnamedAPIResource -} - -func (workaroundNetwork29303) Name() string { - return "Network / 29303" -} - -func (workaroundNetwork29303) Process(apiDefinition importerModels.AzureApiDefinition) (*importerModels.AzureApiDefinition, error) { - correctlyNamedAPIResource, ok := apiDefinition.Resources["PrivateLinkServices"] - if !ok { - return nil, fmt.Errorf("expected an APIResource named `PrivateLinkServices` but didn't find one") - } - misnamedAPIResource, ok := apiDefinition.Resources["PrivateLinkService"] - if !ok { - return nil, fmt.Errorf("expected an APIResource named `PrivateLinkService` but didn't find one") - } - - apiDefinition.Resources["PrivateLinkServices"] = importerModels.MergeResourcesForTag(correctlyNamedAPIResource, misnamedAPIResource) - delete(apiDefinition.Resources, "PrivateLinkService") - - return &apiDefinition, nil -} diff --git a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_newrelic_29256.go b/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_newrelic_29256.go deleted file mode 100644 index 603cdf858c7..00000000000 --- a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_newrelic_29256.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package dataworkarounds - -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{} - -// workaroundNewRelic29256 works around the Swagger defining Identity as SystemAssignedAndUserAssigned -// when the API only supports SystemAssigned. -// Swagger Issue: https://github.com/Azure/azure-rest-api-specs/issues/29256 -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" - return serviceMatches && apiVersionMatches -} - -func (workaroundNewRelic29256) Name() string { - return "NewRelic / 29256" -} - -func (workaroundNewRelic29256) Process(apiDefinition importerModels.AzureApiDefinition) (*importerModels.AzureApiDefinition, error) { - // NewRelicMonitorResource,NewRelicMonitorResourceUpdate - resource, ok := apiDefinition.Resources["Monitors"] - if !ok { - return nil, fmt.Errorf("couldn't find API Resource `Monitors`") - } - - modelsToPatch := []string{ - "NewRelicMonitorResource", - "NewRelicMonitorResourceUpdate", - } - - for _, modelName := range modelsToPatch { - model, ok := resource.Models[modelName] - if !ok { - return nil, fmt.Errorf("couldn't find Model `%s`", modelName) - } - field, ok := model.Fields["Identity"] - if !ok { - return nil, fmt.Errorf("expected the Model `%s` to have a field `Identity` but it didn't exist", modelName) - } - field.ObjectDefinition = sdkModels.SDKObjectDefinition{ - Type: sdkModels.SystemAssignedIdentitySDKObjectDefinitionType, - } - model.Fields["Identity"] = field - resource.Models[modelName] = model - } - - apiDefinition.Resources["Monitors"] = resource - return &apiDefinition, nil -} diff --git a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_operationalinsights_27524.go b/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_operationalinsights_27524.go deleted file mode 100644 index 63e2a2f685e..00000000000 --- a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_operationalinsights_27524.go +++ /dev/null @@ -1,39 +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 = workaroundOperationalinsights27524{} - -// workaroundOperationalinsights27524 works around the missed 201 status code. -// 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) Name() string { - return "OperationalInsights / 27524" -} - -func (workaroundOperationalinsights27524) Process(apiDefinition importerModels.AzureApiDefinition) (*importerModels.AzureApiDefinition, error) { - resource, ok := apiDefinition.Resources["QueryPacks"] - if !ok { - return nil, fmt.Errorf("expected a Resource named `QueryPacks` but didn't get one") - } - operation, ok := resource.Operations["CreateOrUpdate"] - 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 -} diff --git a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_recoveryservicessiterecovery_26680.go b/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_recoveryservicessiterecovery_26680.go deleted file mode 100644 index 5c9d21ed2f5..00000000000 --- a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_recoveryservicessiterecovery_26680.go +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package dataworkarounds - -import ( - "fmt" - - "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{} - -// workaroundRecoveryServicesSiteRecovery26680 works around the model `CreateCertificateOptions` being -// present within the API but not being documented in the API Definitions - as such this adds the missing -// model/fields to make this useful. -// -// Swagger PR: https://github.com/Azure/azure-rest-api-specs/pull/26680 -type workaroundRecoveryServicesSiteRecovery26680 struct { -} - -func (w workaroundRecoveryServicesSiteRecovery26680) IsApplicable(apiDefinition *importerModels.AzureApiDefinition) bool { - if apiDefinition.ServiceName != "RecoveryServicesSiteRecovery" { - return false - } - - apiVersions := map[string]struct{}{ - "2022-10-01": {}, - "2023-01-01": {}, - "2023-02-01": {}, - "2023-04-01": {}, - "2023-06-01": {}, - } - if _, apiVersionMatches := apiVersions[apiDefinition.ApiVersion]; !apiVersionMatches { - return false - } - - if _, resourceExists := apiVersions["VaultCertificates"]; !resourceExists { - return false - } - - return true -} - -func (w workaroundRecoveryServicesSiteRecovery26680) Name() string { - return "RecoveryServicesSiteRecovery / 26680" -} - -func (w workaroundRecoveryServicesSiteRecovery26680) Process(apiDefinition importerModels.AzureApiDefinition) (*importerModels.AzureApiDefinition, error) { - resource, ok := apiDefinition.Resources["VaultCertificates"] - if !ok { - return nil, fmt.Errorf("expected a Resource named `VaultCertificates` but didn't get one") - } - - // 1. Add the missing model - resource.Models["CertificateCreateOptions"] = sdkModels.SDKModel{ - Fields: map[string]sdkModels.SDKField{ - "ValidityInHours": { - Required: false, - JsonName: "validityInHours", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.IntegerSDKObjectDefinitionType, - }, - }, - }, - } - - // 2. Add the field referencing the missing model - model, ok := resource.Models["CertificateRequest"] - if !ok { - return nil, fmt.Errorf("expected a Model named `CertificateRequest` but didn't get one") - } - model.Fields["CertificateCreateOptions"] = sdkModels.SDKField{ - Required: false, - ReadOnly: false, - Sensitive: false, - JsonName: "certificateCreateOptions", - Description: "", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("CertificateCreateOptions"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - } - resource.Models["CertificateRequest"] = model - - apiDefinition.Resources["VaultCertificates"] = resource - return &apiDefinition, nil -} diff --git a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_redis_22407.go b/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_redis_22407.go deleted file mode 100644 index 7b3022c9b4c..00000000000 --- a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_redis_22407.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -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" - return serviceMatches && apiVersionMatches -} - -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 { - if model, ok := resource.Models["RedisCommonPropertiesRedisConfiguration"]; ok { - - if _, ok := model.Fields["NotifyKeyspaceEvents"]; !ok { - model.Fields["NotifyKeyspaceEvents"] = sdkModels.SDKField{ - Required: false, - ReadOnly: false, - Sensitive: false, - JsonName: "notify-keyspace-events", - Description: "The KeySpace Events which should be monitored.", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - } - } - resource.Models["RedisCommonPropertiesRedisConfiguration"] = model - } - apiDefinition.Resources["Redis"] = resource - } - return &apiDefinition, nil -} diff --git a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_streamanalytics_27577.go b/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_streamanalytics_27577.go deleted file mode 100644 index 654808b405f..00000000000 --- a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_streamanalytics_27577.go +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package dataworkarounds - -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 -// `Identity` type when it should be picked up as a Common Type. This is because the API Definition -// doesn't fully describe the object - which is what https://github.com/Azure/azure-rest-api-specs/pull/27577 -// looks to fix. -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" - return serviceMatches && apiVersionMatches -} - -func (w workaroundStreamAnalytics27577) Name() string { - return "StreamAnalytics / 27577" -} - -func (w workaroundStreamAnalytics27577) Process(apiDefinition importerModels.AzureApiDefinition) (*importerModels.AzureApiDefinition, 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" { - apiResourcesToFix = append(apiResourcesToFix, "Subscriptions") - } - - for _, apiResource := range apiResourcesToFix { - resource, ok := apiDefinition.Resources[apiResource] - if !ok { - return nil, fmt.Errorf("expected to find the API Resource %q but didn't", apiResource) - } - - model, ok := resource.Models["StreamingJob"] - if !ok { - return nil, fmt.Errorf("expected the API Resource %q to contain Model `StreamingJob` it didn't", apiResource) - } - - field, ok := model.Fields["Identity"] - if !ok { - return nil, fmt.Errorf("expected the Model `StreamingJob` to contain a field `Identity` but it didn't") - } - - // update the reference to be a System OR UserAssigned identity - field.ObjectDefinition = sdkModels.SDKObjectDefinition{ - Type: sdkModels.SystemOrUserAssignedIdentityMapSDKObjectDefinitionType, - } - if apiDefinition.ApiVersion == "2020-03-01" { - // however API version 2020-03-01 only supports SystemAssigned - field.ObjectDefinition.Type = sdkModels.SystemAssignedIdentitySDKObjectDefinitionType - } - - model.Fields["Identity"] = field - resource.Models["StreamingJob"] = model - - // then delete the standalone identity model - delete(resource.Models, "Identity") - - apiDefinition.Resources[apiResource] = resource - } - return &apiDefinition, nil -} diff --git a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_subscription_20254.go b/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_subscription_20254.go deleted file mode 100644 index b6dfa8c9c4e..00000000000 --- a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workaround_subscription_20254.go +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package dataworkarounds - -import ( - "fmt" - - "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{} - -// workaroundSubscriptions20254 works around the `SubscriptionCancel` API not exposing the `IgnoreResourceCheck` querystring parameter -// Swagger Issue: https://github.com/Azure/azure-rest-api-specs/issues/20254 -type workaroundSubscriptions20254 struct { -} - -func (w workaroundSubscriptions20254) IsApplicable(apiDefinition *importerModels.AzureApiDefinition) bool { - return apiDefinition.ServiceName == "Subscription" && apiDefinition.ApiVersion == "2021-10-01" -} - -func (w workaroundSubscriptions20254) Name() string { - return "Subscription / 20254" -} - -func (w workaroundSubscriptions20254) Process(input importerModels.AzureApiDefinition) (*importerModels.AzureApiDefinition, error) { - output := input - - subscriptionResource, ok := input.Resources["Subscriptions"] - if !ok { - return nil, fmt.Errorf("expected an APIResource named `Subscriptions` but didn't get one") - } - operation, ok := subscriptionResource.Operations["SubscriptionCancel"] - if !ok { - return nil, fmt.Errorf("expected an Operation named `SubscriptionCancel` but didn't get one") - } - operation.Options = map[string]sdkModels.SDKOperationOption{ - // IgnoreResourceCheck allows deleting a Subscription even if it contains nested Resources - "IgnoreResourceCheck": { - QueryStringName: pointer.To("IgnoreResourceCheck"), - ObjectDefinition: sdkModels.SDKOperationOptionObjectDefinition{ - Type: sdkModels.BooleanSDKOperationOptionObjectDefinitionType, - }, - Required: false, - }, - } - subscriptionResource.Operations["SubscriptionCancel"] = operation - output.Resources["Subscriptions"] = subscriptionResource - - return &output, nil -} 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/dataworkarounds/workarounds.go b/tools/importer-rest-api-specs/components/parser/dataworkarounds/workarounds.go deleted file mode 100644 index a9eaf9be9ae..00000000000 --- a/tools/importer-rest-api-specs/components/parser/dataworkarounds/workarounds.go +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -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{}, - workaroundAuthorization25080{}, - workaroundDigitalTwins25120{}, - workaroundAutomation25108{}, - workaroundAutomation25435{}, - workaroundBatch21291{}, - workaroundBotService27351{}, - workaroundContainerService21394{}, - workaroundDataFactory23013{}, - workaroundHDInsight26838{}, - workaroundLoadTest20961{}, - workaroundRedis22407{}, - workaroundMachineLearning25142{}, - workaroundNewRelic29256{}, - workaroundOperationalinsights27524{}, - workaroundRecoveryServicesSiteRecovery26680{}, - workaroundStreamAnalytics27577{}, - workaroundSubscriptions20254{}, - workaroundNetwork29303{}, - - // These workarounds relate to Terraform specific overrides we want to apply (for example for Resource Generation) - workaroundDevCenterIdToRequired{}, - workaroundTempReadOnlyFields{}, - - // 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/dataworkarounds/workaround_inconsistent_swagger_segments.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_inconsistent_swagger_segments.go index bbd7f2e22be..a0de4f05f12 100644 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_inconsistent_swagger_segments.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_inconsistent_swagger_segments.go @@ -44,6 +44,8 @@ func (workaroundInconsistentlyDefinedSegments) Process(input sdkModels.APIVersio val.Name = singularNameOfPrevious if !strings.HasSuffix(val.Name, "Name") { val.Name = fmt.Sprintf("%sName", singularNameOfPrevious) + // the parser sets the same value into Name and ExampleValue so this needs to be applied to ExampleValue as well + val.ExampleValue = fmt.Sprintf("%sName", singularNameOfPrevious) } } } From dc3da3609d448d50791adbdfbd4779db6811b790 Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Fri, 5 Jul 2024 13:52:30 +0200 Subject: [PATCH 08/58] dependencies: updating to `v0.69.0` of `github.com/hashicorp/go-azure-helpers` --- tools/importer-rest-api-specs/go.mod | 2 +- tools/importer-rest-api-specs/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/importer-rest-api-specs/go.mod b/tools/importer-rest-api-specs/go.mod index 0b42b21e6f9..6174be629a6 100644 --- a/tools/importer-rest-api-specs/go.mod +++ b/tools/importer-rest-api-specs/go.mod @@ -8,7 +8,7 @@ require ( 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 diff --git a/tools/importer-rest-api-specs/go.sum b/tools/importer-rest-api-specs/go.sum index 13ffe64aa4c..e97aff3fbad 100644 --- a/tools/importer-rest-api-specs/go.sum +++ b/tools/importer-rest-api-specs/go.sum @@ -201,8 +201,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= From f315a5884edbb22b56820bf6e075c76b11ab7c25 Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Fri, 5 Jul 2024 14:58:24 +0200 Subject: [PATCH 09/58] `tools/importer-rest-api-specs`: refactoring the `normalization` package into the new structure --- .../components/parser/load_and_parse.go | 13 ++++- .../components/parser/normalization.go | 49 ----------------- .../components/parser/operations.go | 7 +-- .../components/parser/parser.go | 21 ++++---- .../components/parser/swagger_resources.go | 3 +- .../parser/cleanup/normalization.go} | 53 ++++++++++++++++--- .../parser/cleanup/remove_unused_items.go | 7 ++- .../apidefinitions/parser/parser.go | 9 ++-- 8 files changed, 84 insertions(+), 78 deletions(-) delete mode 100644 tools/importer-rest-api-specs/components/parser/normalization.go rename tools/importer-rest-api-specs/{components/parser/normalizer.go => internal/components/apidefinitions/parser/cleanup/normalization.go} (62%) 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 index e3eb97e942f..178584da408 100644 --- a/tools/importer-rest-api-specs/components/parser/load_and_parse.go +++ b/tools/importer-rest-api-specs/components/parser/load_and_parse.go @@ -7,8 +7,10 @@ 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/dataworkarounds" "github.com/hashicorp/pandora/tools/importer-rest-api-specs/components/parser/resourceids" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup" "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/logging" importerModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/models" ) @@ -92,7 +94,16 @@ func LoadAndParseFiles(directory string, fileNames []string, serviceName, apiVer out = *output for _, service := range out { - service.Resources = removeUnusedItems(service.Resources) + // temporary shim to enable using the new logic + version := sdkModels.APIVersion{ + APIVersion: service.ApiVersion, + Generate: true, + Preview: service.IsPreviewVersion(), + Resources: service.Resources, + Source: sdkModels.AzureRestAPISpecsSourceDataOrigin, + } + version = cleanup.RemoveUnusedItems(version) + service.Resources = version.Resources } if len(out) > 1 { diff --git a/tools/importer-rest-api-specs/components/parser/normalization.go b/tools/importer-rest-api-specs/components/parser/normalization.go deleted file mode 100644 index 835b394abe2..00000000000 --- a/tools/importer-rest-api-specs/components/parser/normalization.go +++ /dev/null @@ -1,49 +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/cleanup" -) - -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) { - // we're intentionally not using `strings.TrimPrefix` here since we want - // to account for the casing of the tag being different - if strings.HasPrefix(strings.ToLower(operationName), strings.ToLower(*tag)) { - operationName = operationName[len(*tag):] - - // however if the Tag is `ManagementGroupsSubscriptions`, then we need to keep the extra `S` around - if *tag == "ManagementGroups" && !strings.HasPrefix(operationName, "_") { - operationName = fmt.Sprintf("S%s", operationName) - } - } - } - operationName = strings.ReplaceAll(operationName, "_", "") - operationName = strings.TrimPrefix(operationName, "Operations") // sanity checking - if !strings.HasPrefix(strings.ToLower(operationName), "subscriptions") { - operationName = strings.TrimPrefix(operationName, "s") // plurals - } - operationName = strings.TrimPrefix(operationName, "_") - operationName = cleanup.NormalizeName(operationName) - return operationName -} - -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?) - output := input - output = strings.ReplaceAll(output, "EndPoint", "Endpoint") - output = strings.ReplaceAll(output, "NetWork", "Network") - output = strings.ReplaceAll(output, "Baremetalinfrastructure", "BareMetalInfrastructure") - output = strings.ReplaceAll(output, "VirtualWans", "VirtualWANs") - - return output -} diff --git a/tools/importer-rest-api-specs/components/parser/operations.go b/tools/importer-rest-api-specs/components/parser/operations.go index ee9d7897704..0ffd039571a 100644 --- a/tools/importer-rest-api-specs/components/parser/operations.go +++ b/tools/importer-rest-api-specs/components/parser/operations.go @@ -11,9 +11,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" + legacyCleanup "github.com/hashicorp/pandora/tools/importer-rest-api-specs/components/parser/cleanup" "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/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/logging" ) @@ -350,7 +351,7 @@ func (p operationsParser) optionsForOperation(input parsedOperation) (*map[strin if strings.EqualFold(param.In, "header") || strings.EqualFold(param.In, "query") { val := param.Name - name := cleanup.NormalizeName(val) + name := legacyCleanup.NormalizeName(val) option := sdkModels.SDKOperationOption{ Required: param.Required, @@ -537,7 +538,7 @@ func (d *SwaggerDefinition) findOperationsMatchingTag(tag *string) *[]parsedOper continue } - operationName := normalizeOperationName(operationDetails.ID, tag) + operationName := cleanup.NormalizeOperationName(operationDetails.ID, tag) result = append(result, parsedOperation{ name: operationName, uri: uri, diff --git a/tools/importer-rest-api-specs/components/parser/parser.go b/tools/importer-rest-api-specs/components/parser/parser.go index 660a8620cde..78717e894b5 100644 --- a/tools/importer-rest-api-specs/components/parser/parser.go +++ b/tools/importer-rest-api-specs/components/parser/parser.go @@ -10,8 +10,9 @@ import ( "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" + legacyCleanup "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/components/apidefinitions/parser/cleanup" "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/logging" importerModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/models" ) @@ -33,8 +34,8 @@ func (d *SwaggerDefinition) parse(serviceName, apiVersion string, resourceProvid if resource != nil { logging.Tracef("The Tag %q has %d API Operations", tag, len(resource.Operations)) - normalizedTag := normalizeTag(tag) - normalizedTag = cleanup.NormalizeResourceName(normalizedTag) + normalizedTag := cleanup.NormalizeTag(tag) + normalizedTag = legacyCleanup.NormalizeResourceName(normalizedTag) resources[normalizedTag] = *resource } } @@ -48,11 +49,11 @@ func (d *SwaggerDefinition) parse(serviceName, apiVersion string, resourceProvid // 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) + inferredTag := legacyCleanup.PluraliseName(d.Name) if resource != nil { - normalizedTag := normalizeTag(inferredTag) - normalizedTag = cleanup.NormalizeResourceName(normalizedTag) + normalizedTag := cleanup.NormalizeTag(inferredTag) + normalizedTag = legacyCleanup.NormalizeResourceName(normalizedTag) if mergeResources, ok := resources[normalizedTag]; ok { resources[normalizedTag] = importerModels.MergeResourcesForTag(mergeResources, *resource) @@ -76,8 +77,8 @@ func (d *SwaggerDefinition) parse(serviceName, apiVersion string, resourceProvid 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) + inferredTag := legacyCleanup.PluraliseName(swaggerFileName[len(swaggerFileName)-1]) + normalizedTag := legacyCleanup.NormalizeResourceName(inferredTag) result, err := d.findOrphanedDiscriminatedModels(serviceName) if err != nil { @@ -90,7 +91,7 @@ func (d *SwaggerDefinition) parse(serviceName, apiVersion string, resourceProvid Constants: result.Constants, Models: result.Models, } - resource = normalizeAzureApiResource(resource) + resource = cleanup.NormalizeAPIResource(resource) if mergeResources, ok := resources[normalizedTag]; ok { resources[normalizedTag] = importerModels.MergeResourcesForTag(mergeResources, resource) @@ -101,7 +102,7 @@ func (d *SwaggerDefinition) parse(serviceName, apiVersion string, resourceProvid } return &importerModels.AzureApiDefinition{ - ServiceName: cleanup.NormalizeServiceName(serviceName), + ServiceName: legacyCleanup.NormalizeServiceName(serviceName), ApiVersion: apiVersion, Resources: resourcesOut, }, nil diff --git a/tools/importer-rest-api-specs/components/parser/swagger_resources.go b/tools/importer-rest-api-specs/components/parser/swagger_resources.go index f8c7dc955b2..27d438b09fc 100644 --- a/tools/importer-rest-api-specs/components/parser/swagger_resources.go +++ b/tools/importer-rest-api-specs/components/parser/swagger_resources.go @@ -14,6 +14,7 @@ import ( 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/components/parser/resourceids" + "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/commonschema" "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/constants" "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/logging" @@ -70,7 +71,7 @@ func (d *SwaggerDefinition) parseResourcesWithinSwaggerTag(tag *string, resource resource = switchOutCustomTypesAsNeeded(resource) // first Normalize the names, meaning `foo` -> `Foo` for consistency - resource = normalizeAzureApiResource(resource) + resource = cleanup.NormalizeAPIResource(resource) return &resource, nil } diff --git a/tools/importer-rest-api-specs/components/parser/normalizer.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/normalization.go similarity index 62% rename from tools/importer-rest-api-specs/components/parser/normalizer.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/normalization.go index 6a428665646..cf1a74ede23 100644 --- a/tools/importer-rest-api-specs/components/parser/normalizer.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/normalization.go @@ -1,18 +1,21 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package parser +package cleanup 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/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) @@ -65,7 +68,7 @@ func normalizeAzureApiResource(input sdkModels.APIResource) sdkModels.APIResourc for optionKey, optionVal := range v.Options { optionKey = cleanup.NormalizeName(optionKey) - optionVal.ObjectDefinition = normalizeOptionsObjectDefinition(optionVal.ObjectDefinition) + optionVal.ObjectDefinition = normalizeSDKOptionsObjectDefinition(optionVal.ObjectDefinition) normalizedOptions[optionKey] = optionVal } @@ -119,16 +122,54 @@ 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) input.ReferenceName = &normalized } if input.NestedItem != nil { - nestedItem := normalizeOptionsObjectDefinition(*input.NestedItem) + nestedItem := normalizeSDKOptionsObjectDefinition(*input.NestedItem) input.NestedItem = pointer.To(nestedItem) } return input } + +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) { + // we're intentionally not using `strings.TrimPrefix` here since we want + // to account for the casing of the tag being different + if strings.HasPrefix(strings.ToLower(operationName), strings.ToLower(*tag)) { + operationName = operationName[len(*tag):] + + // however if the Tag is `ManagementGroupsSubscriptions`, then we need to keep the extra `S` around + if *tag == "ManagementGroups" && !strings.HasPrefix(operationName, "_") { + operationName = fmt.Sprintf("S%s", operationName) + } + } + } + operationName = strings.ReplaceAll(operationName, "_", "") + operationName = strings.TrimPrefix(operationName, "Operations") // sanity checking + if !strings.HasPrefix(strings.ToLower(operationName), "subscriptions") { + operationName = strings.TrimPrefix(operationName, "s") // plurals + } + operationName = strings.TrimPrefix(operationName, "_") + operationName = cleanup.NormalizeName(operationName) + return operationName +} + +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?) + output := input + output = strings.ReplaceAll(output, "EndPoint", "Endpoint") + output = strings.ReplaceAll(output, "NetWork", "Network") + output = strings.ReplaceAll(output, "Baremetalinfrastructure", "BareMetalInfrastructure") + output = strings.ReplaceAll(output, "VirtualWans", "VirtualWANs") + + return output +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/remove_unused_items.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/remove_unused_items.go index f93e5547211..3c0417d85b2 100644 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/remove_unused_items.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/remove_unused_items.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package cleanup import ( @@ -7,7 +10,7 @@ import ( sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" ) -func RemoveUnusedItems(input sdkModels.APIVersion) (*sdkModels.APIVersion, error) { +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 @@ -59,7 +62,7 @@ func RemoveUnusedItems(input sdkModels.APIVersion) (*sdkModels.APIVersion, error } input.Resources = outputResources - return &input, nil + 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 { 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 index fb1c11d3ecb..ed1bc701be6 100644 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parser.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parser.go @@ -33,7 +33,7 @@ func ParseAPIVersion(serviceName string, input discoveryModels.AvailableDataSetF // Next let's apply any data workarounds logging.Debugf("Applying Data Workarounds..") - output, err := dataworkarounds.Apply(serviceName, apiVersion) + 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) } @@ -41,11 +41,8 @@ func ParseAPIVersion(serviceName string, input discoveryModels.AvailableDataSetF // Finally let's remove any unused items logging.Debugf("Removing unused items..") - output, err = cleanup.RemoveUnusedItems(*output) - if err != nil { - return nil, fmt.Errorf("removing unused items from Service %q / API Version %q: %+v", serviceName, input.APIVersion, err) - } + output := cleanup.RemoveUnusedItems(*withFixesApplied) logging.Debugf("Removing unused items - Complete.") - return output, nil + return &output, nil } From 4fc8d234672b4b257998438c77c47d081cb6f14a Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Fri, 5 Jul 2024 16:41:32 +0200 Subject: [PATCH 10/58] `tools/importer-rest-api-specs`: moving all of the `cleanup` items into the new package This wants refactoring, but moving it over allows cleaning the rest up --- .../components/parser/cleanup/cleanup_test.go | 125 -------------- .../components/parser/helpers.go | 2 +- .../components/parser/models.go | 2 +- .../components/parser/operations.go | 3 +- .../components/parser/parser.go | 13 +- .../parser/resourceids/generate_names.go | 2 +- .../components/parser/resourceids/helpers.go | 2 +- .../parser/resourceids/parse_segments.go | 2 +- .../cleanup/normalize_api_definitions.go | 44 +++++ .../parser/cleanup/normalize_resource_id.go | 161 ++++++++++++++++++ ...malization.go => normalize_sdk_objects.go} | 64 ++----- .../parser/cleanup/pluralise_helper.go | 0 .../parser/cleanup/pluralise_helper_test.go | 0 .../apidefinitions/parser/cleanup/to_sort.go} | 159 ----------------- .../parser/constants/extension_parser.go | 2 +- .../parser/constants/helpers.go | 2 +- ...orkaround_inconsistent_swagger_segments.go | 2 +- .../field_name_plural_to_singular.go | 2 +- 18 files changed, 232 insertions(+), 355 deletions(-) delete mode 100644 tools/importer-rest-api-specs/components/parser/cleanup/cleanup_test.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/normalize_api_definitions.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/normalize_resource_id.go rename tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/{normalization.go => normalize_sdk_objects.go} (59%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/cleanup/pluralise_helper.go (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/cleanup/pluralise_helper_test.go (100%) rename tools/importer-rest-api-specs/{components/parser/cleanup/cleanup.go => internal/components/apidefinitions/parser/cleanup/to_sort.go} (69%) 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/helpers.go b/tools/importer-rest-api-specs/components/parser/helpers.go index 403a546fc09..cf8f59bb719 100644 --- a/tools/importer-rest-api-specs/components/parser/helpers.go +++ b/tools/importer-rest-api-specs/components/parser/helpers.go @@ -8,7 +8,7 @@ import ( "strings" "github.com/go-openapi/spec" - "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" ) func fragmentNameFromReference(input spec.Ref) *string { diff --git a/tools/importer-rest-api-specs/components/parser/models.go b/tools/importer-rest-api-specs/components/parser/models.go index bda590ffc96..9a24105efba 100644 --- a/tools/importer-rest-api-specs/components/parser/models.go +++ b/tools/importer-rest-api-specs/components/parser/models.go @@ -10,8 +10,8 @@ import ( "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/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/featureflags" "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/logging" diff --git a/tools/importer-rest-api-specs/components/parser/operations.go b/tools/importer-rest-api-specs/components/parser/operations.go index 0ffd039571a..8798ee851e3 100644 --- a/tools/importer-rest-api-specs/components/parser/operations.go +++ b/tools/importer-rest-api-specs/components/parser/operations.go @@ -11,7 +11,6 @@ import ( "github.com/go-openapi/spec" sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" - legacyCleanup "github.com/hashicorp/pandora/tools/importer-rest-api-specs/components/parser/cleanup" "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/components/apidefinitions/parser/cleanup" @@ -351,7 +350,7 @@ func (p operationsParser) optionsForOperation(input parsedOperation) (*map[strin if strings.EqualFold(param.In, "header") || strings.EqualFold(param.In, "query") { val := param.Name - name := legacyCleanup.NormalizeName(val) + name := cleanup.NormalizeName(val) option := sdkModels.SDKOperationOption{ Required: param.Required, diff --git a/tools/importer-rest-api-specs/components/parser/parser.go b/tools/importer-rest-api-specs/components/parser/parser.go index 78717e894b5..58a0912dbb0 100644 --- a/tools/importer-rest-api-specs/components/parser/parser.go +++ b/tools/importer-rest-api-specs/components/parser/parser.go @@ -10,7 +10,6 @@ import ( "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" - legacyCleanup "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/components/apidefinitions/parser/cleanup" "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/logging" @@ -35,7 +34,7 @@ func (d *SwaggerDefinition) parse(serviceName, apiVersion string, resourceProvid if resource != nil { logging.Tracef("The Tag %q has %d API Operations", tag, len(resource.Operations)) normalizedTag := cleanup.NormalizeTag(tag) - normalizedTag = legacyCleanup.NormalizeResourceName(normalizedTag) + normalizedTag = cleanup.NormalizeResourceName(normalizedTag) resources[normalizedTag] = *resource } } @@ -49,11 +48,11 @@ func (d *SwaggerDefinition) parse(serviceName, apiVersion string, resourceProvid // 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 := legacyCleanup.PluraliseName(d.Name) + inferredTag := cleanup.PluraliseName(d.Name) if resource != nil { normalizedTag := cleanup.NormalizeTag(inferredTag) - normalizedTag = legacyCleanup.NormalizeResourceName(normalizedTag) + normalizedTag = cleanup.NormalizeResourceName(normalizedTag) if mergeResources, ok := resources[normalizedTag]; ok { resources[normalizedTag] = importerModels.MergeResourcesForTag(mergeResources, *resource) @@ -77,8 +76,8 @@ func (d *SwaggerDefinition) parse(serviceName, apiVersion string, resourceProvid 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 := legacyCleanup.PluraliseName(swaggerFileName[len(swaggerFileName)-1]) - normalizedTag := legacyCleanup.NormalizeResourceName(inferredTag) + inferredTag := cleanup.PluraliseName(swaggerFileName[len(swaggerFileName)-1]) + normalizedTag := cleanup.NormalizeResourceName(inferredTag) result, err := d.findOrphanedDiscriminatedModels(serviceName) if err != nil { @@ -102,7 +101,7 @@ func (d *SwaggerDefinition) parse(serviceName, apiVersion string, resourceProvid } return &importerModels.AzureApiDefinition{ - ServiceName: legacyCleanup.NormalizeServiceName(serviceName), + ServiceName: cleanup.NormalizeServiceName(serviceName), ApiVersion: apiVersion, Resources: resourcesOut, }, nil diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/generate_names.go b/tools/importer-rest-api-specs/components/parser/resourceids/generate_names.go index f4180792b27..bf93f91fb60 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/generate_names.go +++ b/tools/importer-rest-api-specs/components/parser/resourceids/generate_names.go @@ -10,7 +10,7 @@ import ( "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" ) func (p *Parser) generateNamesForResourceIds(input []sdkModels.ResourceID, uriToResourceId map[string]ParsedOperation) (*map[string]sdkModels.ResourceID, error) { diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/helpers.go b/tools/importer-rest-api-specs/components/parser/resourceids/helpers.go index d1505f6c96e..2713d15ba9f 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/helpers.go +++ b/tools/importer-rest-api-specs/components/parser/resourceids/helpers.go @@ -8,7 +8,7 @@ import ( "strings" 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" ) func normalizedResourceManagerResourceId(pri sdkModels.ResourceID) string { diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/parse_segments.go b/tools/importer-rest-api-specs/components/parser/resourceids/parse_segments.go index d889ac14497..082d4df3b67 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/parse_segments.go +++ b/tools/importer-rest-api-specs/components/parser/resourceids/parse_segments.go @@ -10,8 +10,8 @@ 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/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/logging" ) diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/normalize_api_definitions.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/normalize_api_definitions.go new file mode 100644 index 00000000000..db96a26f327 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/normalize_api_definitions.go @@ -0,0 +1,44 @@ +package cleanup + +import ( + "fmt" + "strings" +) + +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) { + // we're intentionally not using `strings.TrimPrefix` here since we want + // to account for the casing of the tag being different + if strings.HasPrefix(strings.ToLower(operationName), strings.ToLower(*tag)) { + operationName = operationName[len(*tag):] + + // however if the Tag is `ManagementGroupsSubscriptions`, then we need to keep the extra `S` around + if *tag == "ManagementGroups" && !strings.HasPrefix(operationName, "_") { + operationName = fmt.Sprintf("S%s", operationName) + } + } + } + operationName = strings.ReplaceAll(operationName, "_", "") + operationName = strings.TrimPrefix(operationName, "Operations") // sanity checking + if !strings.HasPrefix(strings.ToLower(operationName), "subscriptions") { + operationName = strings.TrimPrefix(operationName, "s") // plurals + } + operationName = strings.TrimPrefix(operationName, "_") + operationName = NormalizeName(operationName) + return operationName +} + +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?) + output := input + output = strings.ReplaceAll(output, "EndPoint", "Endpoint") + output = strings.ReplaceAll(output, "NetWork", "Network") + output = strings.ReplaceAll(output, "Baremetalinfrastructure", "BareMetalInfrastructure") + output = strings.ReplaceAll(output, "VirtualWans", "VirtualWANs") + + return output +} 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..3deef879974 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/normalize_resource_id.go @@ -0,0 +1,161 @@ +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", + "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 +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/normalization.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/normalize_sdk_objects.go similarity index 59% rename from tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/normalization.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/normalize_sdk_objects.go index cf1a74ede23..0e549a6677c 100644 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/normalization.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/normalize_sdk_objects.go @@ -4,12 +4,8 @@ package cleanup 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/components/parser/cleanup" ) // NormalizeAPIResource works through the parsed AzureApiResource and ensures @@ -18,29 +14,29 @@ import ( 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 } @@ -50,7 +46,7 @@ func NormalizeAPIResource(input sdkModels.APIResource) sdkModels.APIResource { 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 } @@ -66,7 +62,7 @@ func NormalizeAPIResource(input sdkModels.APIResource) sdkModels.APIResource { normalizedOptions := make(map[string]sdkModels.SDKOperationOption, 0) for optionKey, optionVal := range v.Options { - optionKey = cleanup.NormalizeName(optionKey) + optionKey = NormalizeName(optionKey) optionVal.ObjectDefinition = normalizeSDKOptionsObjectDefinition(optionVal.ObjectDefinition) @@ -83,14 +79,14 @@ func NormalizeAPIResource(input sdkModels.APIResource) sdkModels.APIResource { 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) @@ -110,7 +106,7 @@ func NormalizeAPIResource(input sdkModels.APIResource) sdkModels.APIResource { func normalizeSDKObjectDefinition(input sdkModels.SDKObjectDefinition) sdkModels.SDKObjectDefinition { if input.ReferenceName != nil { - normalized := cleanup.NormalizeName(*input.ReferenceName) + normalized := NormalizeName(*input.ReferenceName) input.ReferenceName = &normalized } @@ -124,7 +120,7 @@ func normalizeSDKObjectDefinition(input sdkModels.SDKObjectDefinition) sdkModels func normalizeSDKOptionsObjectDefinition(input sdkModels.SDKOperationOptionObjectDefinition) sdkModels.SDKOperationOptionObjectDefinition { if input.ReferenceName != nil { - normalized := cleanup.NormalizeName(*input.ReferenceName) + normalized := NormalizeName(*input.ReferenceName) input.ReferenceName = &normalized } @@ -135,41 +131,3 @@ func normalizeSDKOptionsObjectDefinition(input sdkModels.SDKOperationOptionObjec return input } - -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) { - // we're intentionally not using `strings.TrimPrefix` here since we want - // to account for the casing of the tag being different - if strings.HasPrefix(strings.ToLower(operationName), strings.ToLower(*tag)) { - operationName = operationName[len(*tag):] - - // however if the Tag is `ManagementGroupsSubscriptions`, then we need to keep the extra `S` around - if *tag == "ManagementGroups" && !strings.HasPrefix(operationName, "_") { - operationName = fmt.Sprintf("S%s", operationName) - } - } - } - operationName = strings.ReplaceAll(operationName, "_", "") - operationName = strings.TrimPrefix(operationName, "Operations") // sanity checking - if !strings.HasPrefix(strings.ToLower(operationName), "subscriptions") { - operationName = strings.TrimPrefix(operationName, "s") // plurals - } - operationName = strings.TrimPrefix(operationName, "_") - operationName = cleanup.NormalizeName(operationName) - return operationName -} - -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?) - output := input - output = strings.ReplaceAll(output, "EndPoint", "Endpoint") - output = strings.ReplaceAll(output, "NetWork", "Network") - output = strings.ReplaceAll(output, "Baremetalinfrastructure", "BareMetalInfrastructure") - output = strings.ReplaceAll(output, "VirtualWans", "VirtualWANs") - - return output -} 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 100% 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 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/cleanup/cleanup.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/to_sort.go similarity index 69% 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..ad3786d66e0 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 ( @@ -76,162 +73,6 @@ func NormalizeSegmentName(input string) string { 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", 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 index cb7c9ca858b..0e4e8d0c14f 100644 --- 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 @@ -5,7 +5,7 @@ import ( "strings" "github.com/go-openapi/spec" - "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" ) type constantExtension struct { 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 index c510761fd50..ea1a316a18b 100644 --- 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 @@ -6,7 +6,7 @@ import ( "strconv" "strings" - "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" ) func keyValueForFloat(input float64) string { diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_inconsistent_swagger_segments.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_inconsistent_swagger_segments.go index a0de4f05f12..2a55fd9eca5 100644 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_inconsistent_swagger_segments.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_inconsistent_swagger_segments.go @@ -9,7 +9,7 @@ import ( "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" ) var _ workaround = workaroundInconsistentlyDefinedSegments{} 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{} From 2238e45011af6d530ae455078ba632c9d0c09dfb Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Fri, 5 Jul 2024 17:29:53 +0200 Subject: [PATCH 11/58] `tools/importer-rest-api-specs`: adding inner most sdk operation object definition --- .../inner_most_operation_object_definition.go | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 tools/data-api-sdk/v1/helpers/inner_most_operation_object_definition.go 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 +} From 868c6be04bf542cac3a19a656a531d41d3fe3f35 Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Fri, 5 Jul 2024 17:41:39 +0200 Subject: [PATCH 12/58] `tools/importer-rest-api-specs`: moving the `commonids` into a subpackage There's a bunch of these, and whilst these'll get replaced in time with https://github.com/hashicorp/pandora/issues/4017 but for now this cleans this up --- .../parser/resourceids/common_ids.go | 106 +----------------- .../{ => commonids}/common_id_app_service.go | 4 +- .../common_id_app_service_environment.go | 4 +- .../common_id_app_service_plan.go | 4 +- .../common_id_automation_compilation_job.go | 4 +- .../common_id_availability_set.go | 4 +- .../{ => commonids}/common_id_bot_service.go | 4 +- .../common_id_bot_service_channel.go | 4 +- .../common_id_chaos_studio_capability.go | 4 +- .../common_id_chaos_studio_target.go | 4 +- ...mmon_id_cloud_services_ip_configuration.go | 4 +- ...mon_id_cloud_services_public_ip_address.go | 4 +- .../common_id_dedicated_host.go | 4 +- .../common_id_dedicated_host_group.go | 4 +- .../common_id_disk_encryption_set.go | 4 +- .../common_id_expressroute_circuit_peering.go | 4 +- .../common_id_hdinsight_cluster.go | 4 +- .../common_id_hyperv_site_job.go | 4 +- .../common_id_hyperv_site_machine.go | 4 +- .../common_id_hyperv_site_runasaccount.go | 4 +- .../{ => commonids}/common_id_key_vault.go | 4 +- .../common_id_key_vault_key.go | 4 +- .../common_id_key_vault_key_version.go | 4 +- ...d_key_vault_private_endpoint_connection.go | 4 +- .../common_id_kubernetes_cluster.go | 4 +- .../common_id_kubernetes_fleet.go | 4 +- .../common_id_kusto_cluster.go | 4 +- .../common_id_kusto_database.go | 4 +- .../{ => commonids}/common_id_managed_disk.go | 4 +- .../common_id_management_group.go | 4 +- .../common_id_management_group_test.go | 2 +- .../common_id_network_interface.go | 4 +- ...n_id_network_interface_ip_configuration.go | 4 +- .../common_id_p2s_vpn_gateway.go | 4 +- .../common_id_provisioning_service_id.go | 4 +- .../common_id_public_ip_address.go | 4 +- .../common_id_resource_group.go | 4 +- .../common_id_resource_group_test.go | 2 +- .../{ => commonids}/common_id_scope.go | 4 +- .../{ => commonids}/common_id_scope_test.go | 2 +- .../common_id_shared_image_gallery.go | 4 +- .../common_id_spring_cloud_service.go | 4 +- .../{ => commonids}/common_id_sql_database.go | 4 +- .../common_id_sql_elastic_pool.go | 4 +- .../common_id_sql_managed_instance.go | 4 +- ...common_id_sql_managed_instance_database.go | 4 +- .../{ => commonids}/common_id_sql_server.go | 4 +- .../common_id_storage_account.go | 4 +- .../common_id_storage_container.go | 4 +- .../{ => commonids}/common_id_subnet.go | 4 +- .../{ => commonids}/common_id_subscription.go | 4 +- .../common_id_subscription_test.go | 2 +- .../common_id_user_assigned_identity.go | 4 +- .../common_id_user_assigned_identity_test.go | 2 +- .../common_id_virtual_hub_bgp_connection.go | 4 +- ...tual_machine_scale_set_ip_configuration.go | 4 +- ...ual_machine_scale_set_network_interface.go | 4 +- ...ual_machine_scale_set_public_ip_address.go | 4 +- .../common_id_virtual_network.go | 4 +- .../common_id_virtual_router_peering.go | 4 +- .../common_id_virtual_wan_p2s_vpn_gateway.go | 4 +- .../common_id_virtualhub_ip_configuration.go | 4 +- .../common_id_vmware_site_job.go | 4 +- .../common_id_vmware_site_machine.go | 4 +- .../common_id_vmware_site_runasaccount.go | 4 +- .../common_id_vpn_gateway_vpn_connection.go | 4 +- .../resourceids/commonids/common_ids.go | 95 ++++++++++++++++ .../parser/resourceids/commonids/interface.go | 8 ++ .../parser/resourceids/generate_names.go | 5 +- .../parser/resourceids/parse_segments.go | 3 +- .../components/parser/resourceids/parser.go | 2 +- 71 files changed, 238 insertions(+), 231 deletions(-) rename tools/importer-rest-api-specs/components/parser/resourceids/{ => commonids}/common_id_app_service.go (92%) rename tools/importer-rest-api-specs/components/parser/resourceids/{ => commonids}/common_id_app_service_environment.go (92%) rename tools/importer-rest-api-specs/components/parser/resourceids/{ => commonids}/common_id_app_service_plan.go (92%) rename tools/importer-rest-api-specs/components/parser/resourceids/{ => commonids}/common_id_automation_compilation_job.go (93%) rename tools/importer-rest-api-specs/components/parser/resourceids/{ => commonids}/common_id_availability_set.go (92%) rename tools/importer-rest-api-specs/components/parser/resourceids/{ => commonids}/common_id_bot_service.go (93%) rename tools/importer-rest-api-specs/components/parser/resourceids/{ => commonids}/common_id_bot_service_channel.go (96%) rename tools/importer-rest-api-specs/components/parser/resourceids/{ => commonids}/common_id_chaos_studio_capability.go (91%) rename tools/importer-rest-api-specs/components/parser/resourceids/{ => commonids}/common_id_chaos_studio_target.go (90%) rename tools/importer-rest-api-specs/components/parser/resourceids/{ => commonids}/common_id_cloud_services_ip_configuration.go (95%) rename tools/importer-rest-api-specs/components/parser/resourceids/{ => commonids}/common_id_cloud_services_public_ip_address.go (95%) rename tools/importer-rest-api-specs/components/parser/resourceids/{ => commonids}/common_id_dedicated_host.go (93%) rename tools/importer-rest-api-specs/components/parser/resourceids/{ => commonids}/common_id_dedicated_host_group.go (92%) rename tools/importer-rest-api-specs/components/parser/resourceids/{ => commonids}/common_id_disk_encryption_set.go (92%) rename tools/importer-rest-api-specs/components/parser/resourceids/{ => commonids}/common_id_expressroute_circuit_peering.go (93%) rename tools/importer-rest-api-specs/components/parser/resourceids/{ => commonids}/common_id_hdinsight_cluster.go (92%) rename tools/importer-rest-api-specs/components/parser/resourceids/{ => commonids}/common_id_hyperv_site_job.go (93%) rename tools/importer-rest-api-specs/components/parser/resourceids/{ => commonids}/common_id_hyperv_site_machine.go (93%) rename tools/importer-rest-api-specs/components/parser/resourceids/{ => commonids}/common_id_hyperv_site_runasaccount.go (93%) rename tools/importer-rest-api-specs/components/parser/resourceids/{ => commonids}/common_id_key_vault.go (93%) rename tools/importer-rest-api-specs/components/parser/resourceids/{ => commonids}/common_id_key_vault_key.go (93%) rename tools/importer-rest-api-specs/components/parser/resourceids/{ => commonids}/common_id_key_vault_key_version.go (93%) rename tools/importer-rest-api-specs/components/parser/resourceids/{ => commonids}/common_id_key_vault_private_endpoint_connection.go (93%) rename tools/importer-rest-api-specs/components/parser/resourceids/{ => commonids}/common_id_kubernetes_cluster.go (92%) rename tools/importer-rest-api-specs/components/parser/resourceids/{ => commonids}/common_id_kubernetes_fleet.go (92%) rename tools/importer-rest-api-specs/components/parser/resourceids/{ => commonids}/common_id_kusto_cluster.go (93%) rename tools/importer-rest-api-specs/components/parser/resourceids/{ => commonids}/common_id_kusto_database.go (93%) rename tools/importer-rest-api-specs/components/parser/resourceids/{ => commonids}/common_id_managed_disk.go (92%) rename tools/importer-rest-api-specs/components/parser/resourceids/{ => commonids}/common_id_management_group.go (89%) rename tools/importer-rest-api-specs/components/parser/resourceids/{ => commonids}/common_id_management_group_test.go (99%) rename tools/importer-rest-api-specs/components/parser/resourceids/{ => commonids}/common_id_network_interface.go (92%) rename tools/importer-rest-api-specs/components/parser/resourceids/{ => commonids}/common_id_network_interface_ip_configuration.go (93%) rename tools/importer-rest-api-specs/components/parser/resourceids/{ => commonids}/common_id_p2s_vpn_gateway.go (92%) rename tools/importer-rest-api-specs/components/parser/resourceids/{ => commonids}/common_id_provisioning_service_id.go (92%) rename tools/importer-rest-api-specs/components/parser/resourceids/{ => commonids}/common_id_public_ip_address.go (92%) rename tools/importer-rest-api-specs/components/parser/resourceids/{ => commonids}/common_id_resource_group.go (89%) rename tools/importer-rest-api-specs/components/parser/resourceids/{ => commonids}/common_id_resource_group_test.go (99%) rename tools/importer-rest-api-specs/components/parser/resourceids/{ => commonids}/common_id_scope.go (85%) rename tools/importer-rest-api-specs/components/parser/resourceids/{ => commonids}/common_id_scope_test.go (98%) rename tools/importer-rest-api-specs/components/parser/resourceids/{ => commonids}/common_id_shared_image_gallery.go (92%) rename tools/importer-rest-api-specs/components/parser/resourceids/{ => commonids}/common_id_spring_cloud_service.go (92%) rename tools/importer-rest-api-specs/components/parser/resourceids/{ => commonids}/common_id_sql_database.go (93%) rename tools/importer-rest-api-specs/components/parser/resourceids/{ => commonids}/common_id_sql_elastic_pool.go (93%) rename tools/importer-rest-api-specs/components/parser/resourceids/{ => commonids}/common_id_sql_managed_instance.go (92%) rename tools/importer-rest-api-specs/components/parser/resourceids/{ => commonids}/common_id_sql_managed_instance_database.go (93%) rename tools/importer-rest-api-specs/components/parser/resourceids/{ => commonids}/common_id_sql_server.go (92%) rename tools/importer-rest-api-specs/components/parser/resourceids/{ => commonids}/common_id_storage_account.go (92%) rename tools/importer-rest-api-specs/components/parser/resourceids/{ => commonids}/common_id_storage_container.go (94%) rename tools/importer-rest-api-specs/components/parser/resourceids/{ => commonids}/common_id_subnet.go (94%) rename tools/importer-rest-api-specs/components/parser/resourceids/{ => commonids}/common_id_subscription.go (87%) rename tools/importer-rest-api-specs/components/parser/resourceids/{ => commonids}/common_id_subscription_test.go (98%) rename tools/importer-rest-api-specs/components/parser/resourceids/{ => commonids}/common_id_user_assigned_identity.go (92%) rename tools/importer-rest-api-specs/components/parser/resourceids/{ => commonids}/common_id_user_assigned_identity_test.go (99%) rename tools/importer-rest-api-specs/components/parser/resourceids/{ => commonids}/common_id_virtual_hub_bgp_connection.go (93%) rename tools/importer-rest-api-specs/components/parser/resourceids/{ => commonids}/common_id_virtual_machine_scale_set_ip_configuration.go (95%) rename tools/importer-rest-api-specs/components/parser/resourceids/{ => commonids}/common_id_virtual_machine_scale_set_network_interface.go (94%) rename tools/importer-rest-api-specs/components/parser/resourceids/{ => commonids}/common_id_virtual_machine_scale_set_public_ip_address.go (95%) rename tools/importer-rest-api-specs/components/parser/resourceids/{ => commonids}/common_id_virtual_network.go (92%) rename tools/importer-rest-api-specs/components/parser/resourceids/{ => commonids}/common_id_virtual_router_peering.go (93%) rename tools/importer-rest-api-specs/components/parser/resourceids/{ => commonids}/common_id_virtual_wan_p2s_vpn_gateway.go (92%) rename tools/importer-rest-api-specs/components/parser/resourceids/{ => commonids}/common_id_virtualhub_ip_configuration.go (93%) rename tools/importer-rest-api-specs/components/parser/resourceids/{ => commonids}/common_id_vmware_site_job.go (93%) rename tools/importer-rest-api-specs/components/parser/resourceids/{ => commonids}/common_id_vmware_site_machine.go (93%) rename tools/importer-rest-api-specs/components/parser/resourceids/{ => commonids}/common_id_vmware_site_runasaccount.go (93%) rename tools/importer-rest-api-specs/components/parser/resourceids/{ => commonids}/common_id_vpn_gateway_vpn_connection.go (93%) create mode 100644 tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_ids.go create mode 100644 tools/importer-rest-api-specs/components/parser/resourceids/commonids/interface.go diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_ids.go b/tools/importer-rest-api-specs/components/parser/resourceids/common_ids.go index 4dd513b4f44..2fbc692982f 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_ids.go +++ b/tools/importer-rest-api-specs/components/parser/resourceids/common_ids.go @@ -6,116 +6,18 @@ package resourceids import ( "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/commonids" ) -type commonIdMatcher interface { - // id returns the Resource ID for this Common ID - id() sdkModels.ResourceID -} - -var commonIdTypes = []commonIdMatcher{ - commonIdManagementGroupMatcher{}, - commonIdResourceGroupMatcher{}, - commonIdSubscriptionMatcher{}, - commonIdScopeMatcher{}, - commonIdUserAssignedIdentity{}, - - // Network ids - // "Core" - commonIdNetworkInterface{}, - commonIdPublicIPAddress{}, - commonIdSubnet{}, - commonIdVirtualNetwork{}, - commonIdVPNConnection{}, - - // Kusto - commonIdKustoCluster{}, - commonIdKustoDatabase{}, - - // RP Specific - commonIdCloudServicesIPConfiguration{}, - commonIdCloudServicesPublicIPAddress{}, - commonIdExpressRouteCircuitPeering{}, - commonIdNetworkInterfaceIPConfiguration{}, - //commonIdP2sVPNGateway{}, - commonIdVirtualHubBGPConnection{}, - commonIdVirtualHubIPConfiguration{}, - commonIdVirtualMachineScaleSetIPConfiguration{}, - commonIdVirtualMachineScaleSetNetworkInterface{}, - commonIdVirtualMachineScaleSetPublicIPAddress{}, - commonIdVirtualRouterPeering{}, - commonIdVirtualWANP2SVPNGateway{}, - - // https://github.com/hashicorp/pandora/pull/1962#issuecomment-1375005199 - commonIdHyperVSiteJob{}, - commonIdHyperVSiteMachine{}, - commonIdHyperVSiteRunAsAccount{}, - commonIdVMwareSiteJob{}, - commonIdVMwareSiteMachine{}, - commonIdVMwareSiteRunAsAccount{}, - - // Misc data fixes - commonIdAutomationCompilationJob{}, // (@stephybun) CompilationJobId segment is defined in three different ways `jobId`, `compilationJobId` and `compilationJobName` - commonIdProvisioningService{}, // (@jackofallops): Inconsistent user specified fields in the swagger - `provisioningServices/{resourceName}` vs `provisioningServices/{provisioningServiceName}` - - // Bot Service - commonIdBotService{}, - commonIdBotServiceChannel{}, - - // Chaos - commonIdChaosStudioCapability{}, - commonIdChaosStudioTarget{}, - - // Compute - commonIdAvailabilitySet{}, - commonIdDedicatedHost{}, - commonIdDedicatedHostGroup{}, - commonIdDiskEncryptionSet{}, - commonIdManagedDisk{}, - commonIdSharedImageGallery{}, - - // HDInsight - commonIdHDInsightCluster{}, - - // Key Vault - commonIdKeyVault{}, - commonIdKeyVaultKey{}, - commonIdKeyVaultKeyVersion{}, - commonIdKeyVaultPrivateEndpointConnection{}, - - // Kubernetes - commonIdKubernetesCluster{}, - //commonIdKubernetesFleet{}, - - // SQL - commonIdSqlDatabase{}, - commonIdSqlElasticPool{}, - commonIdSqlManagedInstance{}, - commonIdSqlManagedInstanceDatabase{}, - commonIdSqlServer{}, - - // Spring Cloud - commonIdSpringCloudService{}, - - // Storage - commonIdStorageAccount{}, - commonIdStorageContainer{}, - - // Web / App Service - commonIdAppService{}, - 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() + for _, commonId := range commonids.CommonIDTypes { + if ResourceIdsMatch(commonId.ID(), value) { + value = commonId.ID() value.ExampleValue = helpers.DisplayValueForResourceID(value) break } diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_app_service.go b/tools/importer-rest-api-specs/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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_management_group_test.go b/tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_management_group_test.go similarity index 99% rename from tools/importer-rest-api-specs/components/parser/resourceids/common_id_management_group_test.go rename to tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_management_group_test.go index d2186c5c914..0016702859c 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_management_group_test.go +++ b/tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_management_group_test.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package resourceids +package commonids import ( "testing" diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_network_interface.go b/tools/importer-rest-api-specs/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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_resource_group_test.go b/tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_resource_group_test.go similarity index 99% rename from tools/importer-rest-api-specs/components/parser/resourceids/common_id_resource_group_test.go rename to tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_resource_group_test.go index 2155cf7730a..f001ab73761 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_resource_group_test.go +++ b/tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_resource_group_test.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package resourceids +package commonids import ( "testing" diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_scope.go b/tools/importer-rest-api-specs/components/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/components/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/components/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_scope_test.go b/tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_scope_test.go similarity index 98% rename from tools/importer-rest-api-specs/components/parser/resourceids/common_id_scope_test.go rename to tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_scope_test.go index 621a22da48e..6739f618047 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_scope_test.go +++ b/tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_scope_test.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package resourceids +package commonids import ( "testing" diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_shared_image_gallery.go b/tools/importer-rest-api-specs/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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_subscription_test.go b/tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_subscription_test.go similarity index 98% rename from tools/importer-rest-api-specs/components/parser/resourceids/common_id_subscription_test.go rename to tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_subscription_test.go index ac2a2f16802..1e131dd0192 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_subscription_test.go +++ b/tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_subscription_test.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package resourceids +package commonids import ( "testing" diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_user_assigned_identity.go b/tools/importer-rest-api-specs/components/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/components/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/components/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_user_assigned_identity_test.go b/tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_user_assigned_identity_test.go similarity index 99% rename from tools/importer-rest-api-specs/components/parser/resourceids/common_id_user_assigned_identity_test.go rename to tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_user_assigned_identity_test.go index 65812fbb5dd..25c0c426c5c 100644 --- 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/commonids/common_id_user_assigned_identity_test.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package resourceids +package commonids import ( "testing" diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/common_id_virtual_hub_bgp_connection.go b/tools/importer-rest-api-specs/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/components/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/commonids/common_ids.go b/tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_ids.go new file mode 100644 index 00000000000..81eef950073 --- /dev/null +++ b/tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_ids.go @@ -0,0 +1,95 @@ +package commonids + +var CommonIDTypes = []commonIdMatcher{ + commonIdManagementGroupMatcher{}, + commonIdResourceGroupMatcher{}, + commonIdSubscriptionMatcher{}, + commonIdScopeMatcher{}, + commonIdUserAssignedIdentity{}, + + // Network ids + // "Core" + commonIdNetworkInterface{}, + commonIdPublicIPAddress{}, + commonIdSubnet{}, + commonIdVirtualNetwork{}, + commonIdVPNConnection{}, + + // Kusto + commonIdKustoCluster{}, + commonIdKustoDatabase{}, + + // RP Specific + commonIdCloudServicesIPConfiguration{}, + commonIdCloudServicesPublicIPAddress{}, + commonIdExpressRouteCircuitPeering{}, + commonIdNetworkInterfaceIPConfiguration{}, + //commonIdP2sVPNGateway{}, + commonIdVirtualHubBGPConnection{}, + commonIdVirtualHubIPConfiguration{}, + commonIdVirtualMachineScaleSetIPConfiguration{}, + commonIdVirtualMachineScaleSetNetworkInterface{}, + commonIdVirtualMachineScaleSetPublicIPAddress{}, + commonIdVirtualRouterPeering{}, + commonIdVirtualWANP2SVPNGateway{}, + + // https://github.com/hashicorp/pandora/pull/1962#issuecomment-1375005199 + commonIdHyperVSiteJob{}, + commonIdHyperVSiteMachine{}, + commonIdHyperVSiteRunAsAccount{}, + commonIdVMwareSiteJob{}, + commonIdVMwareSiteMachine{}, + commonIdVMwareSiteRunAsAccount{}, + + // Misc data fixes + commonIdAutomationCompilationJob{}, // (@stephybun) CompilationJobId segment is defined in three different ways `jobId`, `compilationJobId` and `compilationJobName` + commonIdProvisioningService{}, // (@jackofallops): Inconsistent user specified fields in the swagger - `provisioningServices/{resourceName}` vs `provisioningServices/{provisioningServiceName}` + + // Bot Service + commonIdBotService{}, + commonIdBotServiceChannel{}, + + // Chaos + commonIdChaosStudioCapability{}, + commonIdChaosStudioTarget{}, + + // Compute + commonIdAvailabilitySet{}, + commonIdDedicatedHost{}, + commonIdDedicatedHostGroup{}, + commonIdDiskEncryptionSet{}, + commonIdManagedDisk{}, + commonIdSharedImageGallery{}, + + // HDInsight + commonIdHDInsightCluster{}, + + // Key Vault + commonIdKeyVault{}, + commonIdKeyVaultKey{}, + commonIdKeyVaultKeyVersion{}, + commonIdKeyVaultPrivateEndpointConnection{}, + + // Kubernetes + commonIdKubernetesCluster{}, + //commonIdKubernetesFleet{}, + + // SQL + commonIdSqlDatabase{}, + commonIdSqlElasticPool{}, + commonIdSqlManagedInstance{}, + commonIdSqlManagedInstanceDatabase{}, + commonIdSqlServer{}, + + // Spring Cloud + commonIdSpringCloudService{}, + + // Storage + commonIdStorageAccount{}, + commonIdStorageContainer{}, + + // Web / App Service + commonIdAppService{}, + commonIdAppServiceEnvironment{}, + commonIdAppServicePlan{}, +} diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/commonids/interface.go b/tools/importer-rest-api-specs/components/parser/resourceids/commonids/interface.go new file mode 100644 index 00000000000..3966f3cdd6d --- /dev/null +++ b/tools/importer-rest-api-specs/components/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/generate_names.go b/tools/importer-rest-api-specs/components/parser/resourceids/generate_names.go index bf93f91fb60..b419f455d28 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/generate_names.go +++ b/tools/importer-rest-api-specs/components/parser/resourceids/generate_names.go @@ -5,6 +5,7 @@ package resourceids import ( "fmt" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/components/parser/resourceids/commonids" "sort" "strings" @@ -47,8 +48,8 @@ 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() + for i, commonIdType := range commonids.CommonIDTypes { + commonId := commonIdType.ID() if ResourceIdsMatch(commonId, resourceId) { if commonId.CommonIDAlias == nil { return nil, fmt.Errorf("the Common ID %d had no Alias: %+v", i, commonId) diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/parse_segments.go b/tools/importer-rest-api-specs/components/parser/resourceids/parse_segments.go index 082d4df3b67..1344b1a2ffb 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/parse_segments.go +++ b/tools/importer-rest-api-specs/components/parser/resourceids/parse_segments.go @@ -13,6 +13,7 @@ import ( "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" "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/logging" ) @@ -41,7 +42,7 @@ func (p *Parser) parseSegmentsForEachOperation() (*map[string]processedResourceI 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 } diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/parser.go b/tools/importer-rest-api-specs/components/parser/resourceids/parser.go index 1e5f974c909..00db4acd75c 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/parser.go +++ b/tools/importer-rest-api-specs/components/parser/resourceids/parser.go @@ -27,7 +27,7 @@ func (p *Parser) Parse() (*ParseResult, error) { 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 From f316c93fab5d25c1eafc6584adf95424bf50f8dc Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Fri, 5 Jul 2024 17:42:41 +0200 Subject: [PATCH 13/58] `tools/data-api-sdk`: removing an empty todo file --- tools/data-api-sdk/v1/helpers/todo.go | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 tools/data-api-sdk/v1/helpers/todo.go 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 From 542153958a12661f92f67979280db1dc7ffa2353 Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Fri, 5 Jul 2024 17:45:16 +0200 Subject: [PATCH 14/58] `tools/importer-rest-api-specs`: refactoring more logic out to the new package The `parser` package itself needs cleaning, but there's a few things to do first --- .../components/parser/helpers.go | 28 ++ .../parser/helpers_list_operations.go | 83 +++++ .../components/parser/internal/structs.go | 27 +- .../components/parser/load_and_parse.go | 3 +- .../components/parser/operations.go | 3 +- .../components/parser/parser.go | 5 +- .../components/parser/refactor_glue_logic.go | 24 ++ .../components/parser/remove_unused_items.go | 284 ------------------ .../components/parser/swagger_resources.go | 138 +-------- .../components/parser/swagger_tags.go | 27 -- .../parser/cleanup/remove_unused_items.go | 26 +- .../apidefinitions/parser/combine/todo.go} | 27 +- .../parser/commonschema/apply.go | 27 ++ .../apidefinitions/parser/ignore/README.md | 5 + .../parser/ignore/operation.go} | 4 +- .../parser/ignore/swagger_tags.go | 30 ++ .../apidefinitions/parser/parser.go | 2 + 17 files changed, 243 insertions(+), 500 deletions(-) create mode 100644 tools/importer-rest-api-specs/components/parser/helpers_list_operations.go create mode 100644 tools/importer-rest-api-specs/components/parser/refactor_glue_logic.go delete mode 100644 tools/importer-rest-api-specs/components/parser/remove_unused_items.go rename tools/importer-rest-api-specs/{components/parser/parse_data.go => internal/components/apidefinitions/parser/combine/todo.go} (77%) create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema/apply.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/ignore/README.md rename tools/importer-rest-api-specs/{components/parser/internal/helpers.go => internal/components/apidefinitions/parser/ignore/operation.go} (97%) create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/ignore/swagger_tags.go diff --git a/tools/importer-rest-api-specs/components/parser/helpers.go b/tools/importer-rest-api-specs/components/parser/helpers.go index cf8f59bb719..79a5b304c80 100644 --- a/tools/importer-rest-api-specs/components/parser/helpers.go +++ b/tools/importer-rest-api-specs/components/parser/helpers.go @@ -8,6 +8,7 @@ import ( "strings" "github.com/go-openapi/spec" + "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" ) @@ -32,6 +33,12 @@ func inlinedModelName(parentModelName, fieldName string) string { return cleanup.NormalizeName(val) } +func isObjectKnown(name string, known internal.ParseResult) (bool, bool) { + _, isConstant := known.Constants[name] + _, isModel := known.Models[name] + return isConstant, isModel +} + 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 { @@ -46,3 +53,24 @@ func operationMatchesTag(operation *spec.Operation, tag *string) bool { 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 +} diff --git a/tools/importer-rest-api-specs/components/parser/helpers_list_operations.go b/tools/importer-rest-api-specs/components/parser/helpers_list_operations.go new file mode 100644 index 00000000000..613d836370b --- /dev/null +++ b/tools/importer-rest-api-specs/components/parser/helpers_list_operations.go @@ -0,0 +1,83 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package parser + +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" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/components/parser/internal" +) + +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 := sdkHelpers.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 +} diff --git a/tools/importer-rest-api-specs/components/parser/internal/structs.go b/tools/importer-rest-api-specs/components/parser/internal/structs.go index ef8aa687ac9..f17fbec09ed 100644 --- a/tools/importer-rest-api-specs/components/parser/internal/structs.go +++ b/tools/importer-rest-api-specs/components/parser/internal/structs.go @@ -6,7 +6,6 @@ package internal import ( "fmt" "reflect" - "strings" "github.com/hashicorp/go-azure-helpers/lang/pointer" sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" @@ -147,27 +146,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/load_and_parse.go b/tools/importer-rest-api-specs/components/parser/load_and_parse.go index 178584da408..1a6df12b8ec 100644 --- a/tools/importer-rest-api-specs/components/parser/load_and_parse.go +++ b/tools/importer-rest-api-specs/components/parser/load_and_parse.go @@ -11,6 +11,7 @@ import ( "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/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/logging" importerModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/models" ) @@ -64,7 +65,7 @@ func LoadAndParseFiles(directory string, fileNames []string, serviceName, apiVer // 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) + resources, err := combine.ResourcesWith(data.Resources, existing.Resources) if err != nil { return nil, fmt.Errorf("combining resources for %q: %+v", key, err) } diff --git a/tools/importer-rest-api-specs/components/parser/operations.go b/tools/importer-rest-api-specs/components/parser/operations.go index 8798ee851e3..1279b113953 100644 --- a/tools/importer-rest-api-specs/components/parser/operations.go +++ b/tools/importer-rest-api-specs/components/parser/operations.go @@ -5,6 +5,7 @@ package parser import ( "fmt" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/ignore" "net/http" "sort" "strings" @@ -42,7 +43,7 @@ func (d *SwaggerDefinition) parseOperationsWithinTag(tag *string, operationIdsTo for _, operation := range *operationsForThisTag { logging.Debugf("Operation - %s %q..", operation.httpMethod, operation.uri) - if internal.OperationShouldBeIgnored(operation.uri) { + if ignore.Operation(operation.uri) { logging.Debugf("Operation should be ignored - skipping..") continue } diff --git a/tools/importer-rest-api-specs/components/parser/parser.go b/tools/importer-rest-api-specs/components/parser/parser.go index 58a0912dbb0..7d6d3e5eea3 100644 --- a/tools/importer-rest-api-specs/components/parser/parser.go +++ b/tools/importer-rest-api-specs/components/parser/parser.go @@ -12,6 +12,7 @@ import ( sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" "github.com/hashicorp/pandora/tools/importer-rest-api-specs/components/parser/resourceids" "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/ignore" "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/logging" importerModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/models" ) @@ -22,7 +23,7 @@ func (d *SwaggerDefinition) parse(serviceName, apiVersion string, resourceProvid tags := d.findTags() // first we assume everything has a tag for _, tag := range tags { - if tagShouldBeIgnored(tag) { + if ignore.SwaggerTag(tag) { continue } @@ -40,7 +41,7 @@ func (d *SwaggerDefinition) parse(serviceName, apiVersion string, resourceProvid } // however some things don't, so we then need to iterate over any without them - if _, shouldIgnore := tagsToIgnore[strings.ToLower(serviceName)]; !shouldIgnore { + if !ignore.SwaggerTag(serviceName) { resource, err := d.parseResourcesWithinSwaggerTag(nil, resourceProvider, resourceIds) if err != nil { return nil, fmt.Errorf("finding resources for tag %q: %+v", serviceName, err) diff --git a/tools/importer-rest-api-specs/components/parser/refactor_glue_logic.go b/tools/importer-rest-api-specs/components/parser/refactor_glue_logic.go new file mode 100644 index 00000000000..d961211c1a3 --- /dev/null +++ b/tools/importer-rest-api-specs/components/parser/refactor_glue_logic.go @@ -0,0 +1,24 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package parser + +import ( + 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 removeUnusedItems(input map[string]sdkModels.APIResource) map[string]sdkModels.APIResource { + // temporary glue logic to use the new package without breaking things too much + apiVersion := sdkModels.APIVersion{ + Resources: input, + + // placeholders + APIVersion: "2020-01-01", + Generate: true, + Preview: false, + Source: sdkModels.AzureRestAPISpecsSourceDataOrigin, + } + apiVersion = cleanup.RemoveUnusedItems(apiVersion) + return apiVersion.Resources +} diff --git a/tools/importer-rest-api-specs/components/parser/remove_unused_items.go b/tools/importer-rest-api-specs/components/parser/remove_unused_items.go deleted file mode 100644 index 894777be35e..00000000000 --- a/tools/importer-rest-api-specs/components/parser/remove_unused_items.go +++ /dev/null @@ -1,284 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package parser - -import ( - "strings" - - "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 { - // 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 { - resourceIdsForThisResource := make(map[string]sdkModels.ResourceID) - for k, v := range details.ResourceIDs { - resourceIdsForThisResource[k] = v - } - unusedResourceIds := findUnusedResourceIds(details.Operations, resourceIdsForThisResource) - for len(unusedResourceIds) > 0 { - for _, resourceIdName := range unusedResourceIds { - delete(resourceIdsForThisResource, resourceIdName) - } - - // then go around again - unusedResourceIds = findUnusedResourceIds(details.Operations, resourceIdsForThisResource) - } - - unusedModels := findUnusedModels(details.Operations, details.Models) - for len(unusedModels) > 0 { - // remove those models - for _, modelName := range unusedModels { - delete(details.Models, modelName) - } - - // then go around again - unusedModels = findUnusedModels(details.Operations, details.Models) - } - - unusedConstants := findUnusedConstants(details.Operations, resourceIdsForThisResource, details.Models, details.Constants) - for len(unusedConstants) > 0 { - // remove those constants - for _, constantName := range unusedConstants { - delete(details.Constants, constantName) - } - - // then go around again - unusedConstants = findUnusedConstants(details.Operations, resourceIdsForThisResource, details.Models, details.Constants) - } - - resources[resource] = sdkModels.APIResource{ - Constants: details.Constants, - Models: details.Models, - Operations: details.Operations, - ResourceIDs: resourceIdsForThisResource, - } - } - - return resources -} - -func findUnusedConstants(operations map[string]sdkModels.SDKOperation, resourceIds map[string]sdkModels.ResourceID, resourceModels map[string]sdkModels.SDKModel, resourceConstants map[string]sdkModels.SDKConstant) []string { - unusedConstants := make(map[string]struct{}) - for constantName := range resourceConstants { - // constants are either housed inside a Model - usedInAModel := false - for _, model := range resourceModels { - for _, field := range model.Fields { - definition := helpers.InnerMostSDKObjectDefinition(field.ObjectDefinition) - if definition.Type != sdkModels.ReferenceSDKObjectDefinitionType { - continue - } - if *definition.ReferenceName == constantName { - usedInAModel = true - break - } - } - - if usedInAModel { - break - } - } - if usedInAModel { - continue - } - - usedInAnOperation := false - for _, operation := range operations { - if operation.RequestObject != nil { - definition := helpers.InnerMostSDKObjectDefinition(*operation.RequestObject) - if definition.Type == sdkModels.ReferenceSDKObjectDefinitionType && *definition.ReferenceName == constantName { - usedInAnOperation = true - break - } - } - - if operation.ResponseObject != nil { - definition := helpers.InnerMostSDKObjectDefinition(*operation.ResponseObject) - if definition.Type == sdkModels.ReferenceSDKObjectDefinitionType && *definition.ReferenceName == constantName { - usedInAnOperation = true - break - } - } - - for _, v := range operation.Options { - definition := topLevelOptionsObjectDefinition(v.ObjectDefinition) - if definition.Type != sdkModels.ReferenceSDKOperationOptionObjectDefinitionType { - continue - } - if *definition.ReferenceName == constantName { - usedInAnOperation = true - break - } - } - } - if usedInAnOperation { - continue - } - - usedInAResourceId := false - for _, rid := range resourceIds { - for _, segment := range rid.Segments { - if segment.ConstantReference == nil { - continue - } - - if strings.EqualFold(*segment.ConstantReference, constantName) { - usedInAResourceId = true - break - } - } - - if usedInAResourceId { - break - } - } - if usedInAResourceId { - continue - } - - unusedConstants[constantName] = struct{}{} - } - - out := make([]string, 0) - for k := range unusedConstants { - out = append(out, k) - } - - 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 { - if modelName == "" { - // if the model name is empty this is an unused model (so is safe to remove) - but is a bug - // TODO: track down empty models and then remove this - unusedModels[modelName] = struct{}{} - continue - } - - // models are either referenced by operations - usedInAnOperation := false - for _, operation := range operations { - if operation.RequestObject != nil { - definition := helpers.InnerMostSDKObjectDefinition(*operation.RequestObject) - if definition.Type == sdkModels.ReferenceSDKObjectDefinitionType && *definition.ReferenceName == modelName { - usedInAnOperation = true - break - } - } - - if operation.ResponseObject != nil { - definition := helpers.InnerMostSDKObjectDefinition(*operation.ResponseObject) - if definition.Type == sdkModels.ReferenceSDKObjectDefinitionType && *definition.ReferenceName == modelName { - usedInAnOperation = true - break - } - } - - // @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) - if definition.Type != sdkModels.ReferenceSDKOperationOptionObjectDefinitionType { - continue - } - if *definition.ReferenceName == modelName { - usedInAnOperation = true - continue - } - } - } - if usedInAnOperation { - continue - } - - // or on other models - usedInAModel := false - for thisModelName, thisModel := range resourceModels { - if thisModelName == modelName { - continue - } - - for _, field := range thisModel.Fields { - definition := helpers.InnerMostSDKObjectDefinition(field.ObjectDefinition) - if definition.Type != sdkModels.ReferenceSDKObjectDefinitionType { - continue - } - if *definition.ReferenceName == modelName { - usedInAModel = true - break - } - } - - if thisModel.ParentTypeName != nil && *thisModel.ParentTypeName == modelName { - // if this model inherits from the main type (e.g. discriminated) then it - // should be kept - usedInAModel = true - break - } - if model.ParentTypeName != nil && *model.ParentTypeName == thisModelName { - // likewise if it's the inverse - usedInAModel = true - break - } - } - if usedInAModel { - continue - } - - unusedModels[modelName] = struct{}{} - } - - out := make([]string, 0) - for k := range unusedModels { - out = append(out, k) - } - - return out -} - -func findUnusedResourceIds(operations map[string]sdkModels.SDKOperation, resourceIds map[string]sdkModels.ResourceID) []string { - unusedResourceIds := make(map[string]struct{}, 0) - - // first add everything - for name := range resourceIds { - unusedResourceIds[name] = struct{}{} - } - - // then go through and remove the Resource ID if it's used - for _, operation := range operations { - if operation.ResourceIDName == nil { - continue - } - - // since this hasn't been normalized yet, find the correct casing for the key to remove - for key := range unusedResourceIds { - if strings.EqualFold(*operation.ResourceIDName, key) { - delete(unusedResourceIds, key) - } - } - - } - - output := make([]string, 0) - for name := range unusedResourceIds { - output = append(output, name) - } - - return output -} diff --git a/tools/importer-rest-api-specs/components/parser/swagger_resources.go b/tools/importer-rest-api-specs/components/parser/swagger_resources.go index 27d438b09fc..ac10967c2f4 100644 --- a/tools/importer-rest-api-specs/components/parser/swagger_resources.go +++ b/tools/importer-rest-api-specs/components/parser/swagger_resources.go @@ -5,17 +5,15 @@ package parser import ( "fmt" - "net/http" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema" "strings" "github.com/go-openapi/spec" - "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" "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/components/apidefinitions/parser/cleanup" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema" "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/constants" "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/logging" ) @@ -67,8 +65,8 @@ func (d *SwaggerDefinition) parseResourcesWithinSwaggerTag(tag *string, resource ResourceIDs: resourceIds.NamesToResourceIDs, } - // then switch out any custom types (e.g. Identity) - resource = switchOutCustomTypesAsNeeded(resource) + // then switch out any Common Schema Types (e.g. Identity) + resource = commonschema.ReplaceSDKObjectDefinitionsAsNeeded(resource) // first Normalize the names, meaning `foo` -> `Foo` for consistency resource = cleanup.NormalizeAPIResource(resource) @@ -76,99 +74,6 @@ func (d *SwaggerDefinition) parseResourcesWithinSwaggerTag(tag *string, 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 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 Object Definition for this field if needed - for _, matcher := range commonschema.Matchers { - if matcher.IsMatch(field, input) { - field.ObjectDefinition = matcher.ReplacementObjectDefinition() - break - } - } - - fields[fieldName] = field - } - model.Fields = fields - output.Models[modelName] = model - } - - return output -} - func (d *SwaggerDefinition) findNestedItemsYetToBeParsed(operations map[string]sdkModels.SDKOperation, known internal.ParseResult) (*internal.ParseResult, error) { result := internal.ParseResult{ Constants: map[string]sdkModels.SDKConstant{}, @@ -219,27 +124,6 @@ func (d *SwaggerDefinition) findNestedItemsYetToBeParsed(operations map[string]s 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) @@ -268,7 +152,7 @@ func (d *SwaggerDefinition) determineObjectsRequiredButNotParsed(operations map[ for _, operation := range operations { if operation.RequestObject != nil { - topLevelRef := helpers.InnerMostSDKObjectDefinition(*operation.RequestObject) + topLevelRef := sdkHelpers.InnerMostSDKObjectDefinition(*operation.RequestObject) if topLevelRef.Type == sdkModels.ReferenceSDKObjectDefinitionType { isKnownConstant, isKnownModel := isObjectKnown(*topLevelRef.ReferenceName, known) if !isKnownConstant && !isKnownModel { @@ -290,7 +174,7 @@ func (d *SwaggerDefinition) determineObjectsRequiredButNotParsed(operations map[ } if operation.ResponseObject != nil { - topLevelRef := helpers.InnerMostSDKObjectDefinition(*operation.ResponseObject) + topLevelRef := sdkHelpers.InnerMostSDKObjectDefinition(*operation.ResponseObject) if topLevelRef.Type == sdkModels.ReferenceSDKObjectDefinitionType { isKnownConstant, isKnownModel := isObjectKnown(*topLevelRef.ReferenceName, known) if !isKnownConstant && !isKnownModel { @@ -314,7 +198,7 @@ func (d *SwaggerDefinition) determineObjectsRequiredButNotParsed(operations map[ } for _, value := range operation.Options { - topLevelRef := topLevelOptionsObjectDefinition(value.ObjectDefinition) + topLevelRef := sdkHelpers.InnerMostSDKOperationOptionObjectDefinition(value.ObjectDefinition) if topLevelRef.Type != sdkModels.ReferenceSDKOperationOptionObjectDefinitionType { continue } @@ -355,7 +239,7 @@ func (d *SwaggerDefinition) objectsUsedByModel(modelName string, model sdkModels typeNames := make(map[string]struct{}, 0) for _, field := range model.Fields { - definition := helpers.InnerMostSDKObjectDefinition(field.ObjectDefinition) + definition := sdkHelpers.InnerMostSDKObjectDefinition(field.ObjectDefinition) if definition.ReferenceName != nil { typeNames[*definition.ReferenceName] = struct{}{} } @@ -437,9 +321,3 @@ func (d *SwaggerDefinition) doesModelImplement(modelName string, value spec.Sche 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 index 7c4c3d43cc1..e572d414089 100644 --- a/tools/importer-rest-api-specs/components/parser/swagger_tags.go +++ b/tools/importer-rest-api-specs/components/parser/swagger_tags.go @@ -8,11 +8,6 @@ import ( "strings" ) -var tagsToIgnore = map[string]struct{}{ - "azurefirewallfqdntags": {}, - "usage": {}, -} - func (d *SwaggerDefinition) findTags() []string { tags := make(map[string]struct{}) @@ -32,25 +27,3 @@ func (d *SwaggerDefinition) findTags() []string { 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/internal/components/apidefinitions/parser/cleanup/remove_unused_items.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/remove_unused_items.go index 3c0417d85b2..c97f7197464 100644 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/remove_unused_items.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/remove_unused_items.go @@ -6,7 +6,7 @@ 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" ) @@ -72,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 } @@ -93,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 @@ -101,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 @@ -109,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 } @@ -155,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 { @@ -177,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 @@ -185,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 @@ -194,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 } @@ -216,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/components/parser/parse_data.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/combine/todo.go similarity index 77% rename from tools/importer-rest-api-specs/components/parser/parse_data.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/combine/todo.go index 993fc2bf8a1..59a6787d798 100644 --- a/tools/importer-rest-api-specs/components/parser/parse_data.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/combine/todo.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package parser +package combine import ( "fmt" @@ -9,12 +9,11 @@ import ( "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) { +func ResourcesWith(first, other map[string]sdkModels.APIResource) (*map[string]sdkModels.APIResource, error) { resources := make(map[string]sdkModels.APIResource) - for k, v := range first.Resources { + for k, v := range first { resources[k] = v } @@ -25,25 +24,25 @@ func combineResourcesWith(first importerModels.AzureApiDefinition, other map[str continue } - constants, err := combineConstants(existing.Constants, v.Constants) + constants, err := constants(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) + models, err := models(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) + operations, err := operations(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) + resourceIds, err := resourceIds(existing.ResourceIDs, v.ResourceIDs) if err != nil { return nil, fmt.Errorf("combining resource ids: %+v", err) } @@ -55,7 +54,7 @@ func combineResourcesWith(first importerModels.AzureApiDefinition, other map[str return &resources, nil } -func combineConstants(first, second map[string]sdkModels.SDKConstant) (*map[string]sdkModels.SDKConstant, error) { +func constants(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 @@ -72,7 +71,7 @@ func combineConstants(first, second map[string]sdkModels.SDKConstant) (*map[stri 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) + vals, err := maps(existingConst.Values, v.Values) if err != nil { return nil, fmt.Errorf("combining maps: %+v", err) } @@ -83,7 +82,7 @@ func combineConstants(first, second map[string]sdkModels.SDKConstant) (*map[stri return &constants, nil } -func combineModels(first map[string]sdkModels.SDKModel, second map[string]sdkModels.SDKModel) (*map[string]sdkModels.SDKModel, error) { +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 { @@ -120,7 +119,7 @@ func combineModels(first map[string]sdkModels.SDKModel, second map[string]sdkMod return &output, nil } -func combineOperations(first map[string]sdkModels.SDKOperation, second map[string]sdkModels.SDKOperation) (*map[string]sdkModels.SDKOperation, error) { +func operations(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 { @@ -140,7 +139,7 @@ func combineOperations(first map[string]sdkModels.SDKOperation, second map[strin return &output, nil } -func combineResourceIds(first map[string]sdkModels.ResourceID, second map[string]sdkModels.ResourceID) (*map[string]sdkModels.ResourceID, error) { +func resourceIds(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 { @@ -160,7 +159,7 @@ func combineResourceIds(first map[string]sdkModels.ResourceID, second map[string return &output, nil } -func combineMaps(first map[string]string, second map[string]string) (*map[string]string, error) { +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 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/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/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/internal/components/apidefinitions/parser/parser.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parser.go index ed1bc701be6..37758ddbc4f 100644 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parser.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parser.go @@ -16,11 +16,13 @@ func ParseAPIVersion(serviceName string, input discoveryModels.AvailableDataSetF // Firstly let's go through and process each of the Supplementary Files for _, filePath := range input.FilePathsContainingSupplementaryData { logging.Tracef("Processing Supplementary Data from file %q..", filePath) + // TODO: parse the file } // Next let's go through and process each of the API Definitions for _, filePath := range input.FilePathsContainingAPIDefinitions { logging.Tracef("Processing API Definitions from file %q..", filePath) + // TODO: parse the file } apiVersion := sdkModels.APIVersion{ From 022aa27f171e64d67f136e0cd24c37cdad7fb1dc Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Fri, 5 Jul 2024 17:55:08 +0200 Subject: [PATCH 15/58] `tools/importer-rest-api-specs`: moving another `ignore` function --- .../components/parser/load_and_parse.go | 22 ++----------------- .../apidefinitions/parser/ignore/services.go | 20 +++++++++++++++++ 2 files changed, 22 insertions(+), 20 deletions(-) create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/ignore/services.go 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 index 1a6df12b8ec..36f02aa77fb 100644 --- a/tools/importer-rest-api-specs/components/parser/load_and_parse.go +++ b/tools/importer-rest-api-specs/components/parser/load_and_parse.go @@ -5,20 +5,19 @@ package parser 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/dataworkarounds" "github.com/hashicorp/pandora/tools/importer-rest-api-specs/components/parser/resourceids" "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/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) { + if ignore.Services(serviceName) { logging.Debugf("Service %q should be ignored - skipping", serviceName) return &importerModels.AzureApiDefinition{}, nil } @@ -118,23 +117,6 @@ func LoadAndParseFiles(directory string, fileNames []string, serviceName, apiVer 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/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 +} From 7da937c6a937c502b1a7a5b7518cbb09befad32a Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Tue, 9 Jul 2024 18:36:46 +0200 Subject: [PATCH 16/58] WIP --- .../components/parser/helpers.go | 7 - .../components/parser/helpers_resource_id.go | 30 ++ .../components/parser/helpers_test.go | 2 +- .../components/parser/internal/structs.go | 151 +------ .../components/parser/load_and_parse.go | 2 +- .../components/parser/models.go | 378 ----------------- .../components/parser/object_definition.go | 390 ++++++++++++++++++ .../components/parser/parser.go | 57 --- .../components/parser/resource_ids.go | 49 +++ .../components/parser/swagger_resources.go | 4 +- .../apidefinitions/parser/README.md | 11 + .../parser/combine/api_resource.go | 53 +++ .../parser/combine/constants.go | 38 ++ .../apidefinitions/parser/combine/maps.go | 28 ++ .../apidefinitions/parser/combine/models.go | 47 +++ .../parser/combine/operations.go | 30 ++ .../parser/combine/resource_ids.go | 32 ++ .../apidefinitions/parser/combine/todo.go | 180 -------- .../parser/models/parse_result.go | 160 +++++++ 19 files changed, 874 insertions(+), 775 deletions(-) create mode 100644 tools/importer-rest-api-specs/components/parser/helpers_resource_id.go create mode 100644 tools/importer-rest-api-specs/components/parser/object_definition.go create mode 100644 tools/importer-rest-api-specs/components/parser/resource_ids.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/README.md create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/combine/api_resource.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/combine/constants.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/combine/maps.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/combine/models.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/combine/operations.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/combine/resource_ids.go delete mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/combine/todo.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models/parse_result.go diff --git a/tools/importer-rest-api-specs/components/parser/helpers.go b/tools/importer-rest-api-specs/components/parser/helpers.go index 79a5b304c80..1ebc35a39d0 100644 --- a/tools/importer-rest-api-specs/components/parser/helpers.go +++ b/tools/importer-rest-api-specs/components/parser/helpers.go @@ -8,7 +8,6 @@ import ( "strings" "github.com/go-openapi/spec" - "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" ) @@ -33,12 +32,6 @@ func inlinedModelName(parentModelName, fieldName string) string { return cleanup.NormalizeName(val) } -func isObjectKnown(name string, known internal.ParseResult) (bool, bool) { - _, isConstant := known.Constants[name] - _, isModel := known.Models[name] - return isConstant, isModel -} - 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 { diff --git a/tools/importer-rest-api-specs/components/parser/helpers_resource_id.go b/tools/importer-rest-api-specs/components/parser/helpers_resource_id.go new file mode 100644 index 00000000000..c9e3d5e0ad8 --- /dev/null +++ b/tools/importer-rest-api-specs/components/parser/helpers_resource_id.go @@ -0,0 +1,30 @@ +package parser + +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/components/parser/helpers_test.go b/tools/importer-rest-api-specs/components/parser/helpers_test.go index 0bff133a4c2..f9703d8188b 100644 --- a/tools/importer-rest-api-specs/components/parser/helpers_test.go +++ b/tools/importer-rest-api-specs/components/parser/helpers_test.go @@ -21,7 +21,7 @@ func ParseSwaggerFileForTesting(t *testing.T, file string, serviceName *string) } var resourceProvider *string = nil // we're not filtering to just this RP, so it's fine - resourceIds, err := parsed.ParseResourceIds(resourceProvider) + resourceIds, err := parsed.ParseResourceIds() if err != nil { t.Fatalf("parsing Resource Ids: %+v", err) } diff --git a/tools/importer-rest-api-specs/components/parser/internal/structs.go b/tools/importer-rest-api-specs/components/parser/internal/structs.go index f17fbec09ed..7357cb3f3dd 100644 --- a/tools/importer-rest-api-specs/components/parser/internal/structs.go +++ b/tools/importer-rest-api-specs/components/parser/internal/structs.go @@ -4,154 +4,7 @@ package internal import ( - "fmt" - "reflect" - - "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" ) -type ParseResult struct { - Constants map[string]sdkModels.SDKConstant - Models map[string]sdkModels.SDKModel -} - -func (r *ParseResult) Append(other ParseResult) error { - if r.Constants == nil { - r.Constants = make(map[string]sdkModels.SDKConstant) - } - if err := r.AppendConstants(other.Constants); err != nil { - return fmt.Errorf("appending constants: %+v", err) - } - - if r.Models == nil { - r.Models = make(map[string]sdkModels.SDKModel) - } - if err := r.AppendModels(other.Models); err != nil { - return fmt.Errorf("appending models: %+v", err) - } - - return nil -} - -func (r *ParseResult) AppendConstants(other map[string]sdkModels.SDKConstant) error { - for k, v := range other { - existing, hasExisting := r.Constants[k] - if !hasExisting { - r.Constants[k] = v - continue - } - - if v.Type != existing.Type { - 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) - } - } - - return nil -} - -func (r *ParseResult) AppendModels(other map[string]sdkModels.SDKModel) error { - for k, v := range other { - existing, hasExisting := r.Models[k] - if !hasExisting { - r.Models[k] = v - continue - } - - if err := compareNilableString(existing.DiscriminatedValue, v.DiscriminatedValue); err != nil { - return fmt.Errorf("comparing DiscriminatedValue: %+v", err) - } - if err := compareNilableString(existing.FieldNameContainingDiscriminatedValue, v.FieldNameContainingDiscriminatedValue); err != nil { - return fmt.Errorf("comparing FieldNameContainingDiscriminatedValue: %+v", err) - } - if err := compareNilableString(existing.ParentTypeName, v.ParentTypeName); err != nil { - return fmt.Errorf("comparing ParentTypeName: %+v", err) - } - - if err := compareFields(existing.Fields, v.Fields); err != nil { - return fmt.Errorf("different model objects for Model %q: %+v.\n\nFirst fields: %+v.\n\nSecond fields: %+v", k, err, existing.Fields, v.Fields) - } - } - - return nil -} - -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)) - } - - for k := range first { - firstVal := first[k] - secondVal, ok := second[k] - if !ok { - return fmt.Errorf("second didn't contain the key %q", k) - } - - // TODO this can be uncommented when #3325 has been fixed - //if firstVal.Description != secondVal.Description { - // return fmt.Errorf("first.Description was %q but second.Description was %q", firstVal.Description, secondVal.Description) - //} - if firstVal.JsonName != secondVal.JsonName { - return fmt.Errorf("first.JsonName was %q but second.JsonName was %q", firstVal.JsonName, secondVal.JsonName) - } - if firstVal.Required != secondVal.Required { - return fmt.Errorf("first.Required was %t but second.Required was %t", firstVal.Required, secondVal.Required) - } - if firstVal.Sensitive != secondVal.Sensitive { - return fmt.Errorf("first.Sensitive was %t but second.Sensitive was %t", firstVal.Sensitive, secondVal.Sensitive) - } - if err := objectDefinitionsMatch(firstVal.ObjectDefinition, secondVal.ObjectDefinition); err != nil { - return fmt.Errorf("object definitions differ: %+v.\n\nFirst %+v\n\nSecond %+v", err, firstVal.ObjectDefinition, secondVal.ObjectDefinition) - } - } - - return nil -} - -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) - } - if first.NestedItem == nil && second.NestedItem != nil { - return fmt.Errorf("`second` had a nested item but `first` didn't. First: %+v. Second: %+v", first, second) - } - if first.NestedItem != nil && second.NestedItem != nil { - if err := objectDefinitionsMatch(*first.NestedItem, *second.NestedItem); err != nil { - return fmt.Errorf("nested items differ: %+v", err) - } - } - 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)) - } - - // TODO: re-enable minimum/maximum/unique on Object Definition - //if err := compareNilableInteger(first.Maximum, second.Maximum); err != nil { - // return fmt.Errorf("value for Maximum differs: %+v\n\nFirst was %d but second was %d", err, pointer.From(first.Maximum), pointer.From(second.Maximum)) - //} - //if err := compareNilableInteger(first.Minimum, second.Minimum); err != nil { - // return fmt.Errorf("value for Minimum differs: %+v\n\nFirst was %d but second was %d", err, pointer.From(first.Minimum), pointer.From(second.Minimum)) - //} - //if err := compareNilableBoolean(first.UniqueItems, second.UniqueItems); err != nil { - // return fmt.Errorf("value for Unique Items differs: %+v\n\nFirst was %t but second was %t", err, pointer.From(first.UniqueItems), pointer.From(second.UniqueItems)) - //} - - return nil -} - -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 -} +type ParseResult = parserModels.ParseResult 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 index 36f02aa77fb..ce56f1d73f3 100644 --- a/tools/importer-rest-api-specs/components/parser/load_and_parse.go +++ b/tools/importer-rest-api-specs/components/parser/load_and_parse.go @@ -35,7 +35,7 @@ func LoadAndParseFiles(directory string, fileNames []string, serviceName, apiVer } file2Swagger[file] = swaggerFile - parsedResourceIds, err := swaggerFile.ParseResourceIds(resourceProvider) + parsedResourceIds, err := swaggerFile.ParseResourceIds() if err != nil { return nil, fmt.Errorf("parsing Resource Ids from %q (Service %q / Api Version %q): %+v", file, serviceName, apiVersion, err) } diff --git a/tools/importer-rest-api-specs/components/parser/models.go b/tools/importer-rest-api-specs/components/parser/models.go index 9a24105efba..7442ce4b59e 100644 --- a/tools/importer-rest-api-specs/components/parser/models.go +++ b/tools/importer-rest-api-specs/components/parser/models.go @@ -11,9 +11,7 @@ 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/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/featureflags" "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/logging" ) @@ -510,379 +508,3 @@ func (d *SwaggerDefinition) findOrphanedDiscriminatedModels(serviceName string) 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.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: &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/object_definition.go b/tools/importer-rest-api-specs/components/parser/object_definition.go new file mode 100644 index 00000000000..9c7507c9c97 --- /dev/null +++ b/tools/importer-rest-api-specs/components/parser/object_definition.go @@ -0,0 +1,390 @@ +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/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/featureflags" +) + +// 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.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: &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/parser.go b/tools/importer-rest-api-specs/components/parser/parser.go index 7d6d3e5eea3..7b5660c585c 100644 --- a/tools/importer-rest-api-specs/components/parser/parser.go +++ b/tools/importer-rest-api-specs/components/parser/parser.go @@ -7,8 +7,6 @@ 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/resourceids" "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup" @@ -147,58 +145,3 @@ func (d *SwaggerDefinition) simplifyOperationNamesForResource(resource sdkModels 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/resource_ids.go b/tools/importer-rest-api-specs/components/parser/resource_ids.go new file mode 100644 index 00000000000..1c5a0df8e6d --- /dev/null +++ b/tools/importer-rest-api-specs/components/parser/resource_ids.go @@ -0,0 +1,49 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package parser + +import ( + "fmt" + + "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/resourceids" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/logging" +) + +func (d *SwaggerDefinition) ParseResourceIds() (*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 +} diff --git a/tools/importer-rest-api-specs/components/parser/swagger_resources.go b/tools/importer-rest-api-specs/components/parser/swagger_resources.go index ac10967c2f4..50b7e9ea652 100644 --- a/tools/importer-rest-api-specs/components/parser/swagger_resources.go +++ b/tools/importer-rest-api-specs/components/parser/swagger_resources.go @@ -154,7 +154,7 @@ func (d *SwaggerDefinition) determineObjectsRequiredButNotParsed(operations map[ if operation.RequestObject != nil { topLevelRef := sdkHelpers.InnerMostSDKObjectDefinition(*operation.RequestObject) if topLevelRef.Type == sdkModels.ReferenceSDKObjectDefinitionType { - isKnownConstant, isKnownModel := isObjectKnown(*topLevelRef.ReferenceName, known) + isKnownConstant, isKnownModel := known.IsObjectKnown(*topLevelRef.ReferenceName) if !isKnownConstant && !isKnownModel { referencesToFind[*topLevelRef.ReferenceName] = struct{}{} } @@ -176,7 +176,7 @@ func (d *SwaggerDefinition) determineObjectsRequiredButNotParsed(operations map[ if operation.ResponseObject != nil { topLevelRef := sdkHelpers.InnerMostSDKObjectDefinition(*operation.ResponseObject) if topLevelRef.Type == sdkModels.ReferenceSDKObjectDefinitionType { - isKnownConstant, isKnownModel := isObjectKnown(*topLevelRef.ReferenceName, known) + isKnownConstant, isKnownModel := known.IsObjectKnown(*topLevelRef.ReferenceName) if !isKnownConstant && !isKnownModel { referencesToFind[*topLevelRef.ReferenceName] = struct{}{} } 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..bf885931ec3 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/README.md @@ -0,0 +1,11 @@ +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. 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..6ef89d3c0e9 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/combine/api_resource.go @@ -0,0 +1,53 @@ +// 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 ResourcesWith(first, other map[string]sdkModels.APIResource) (*map[string]sdkModels.APIResource, error) { + resources := make(map[string]sdkModels.APIResource) + for k, v := range first { + resources[k] = v + } + + for k, v := range other { + existing, ok := resources[k] + if !ok { + resources[k] = v + continue + } + + constants, err := constants(existing.Constants, v.Constants) + if err != nil { + return nil, fmt.Errorf("combining constants: %+v", err) + } + existing.Constants = *constants + + models, err := models(existing.Models, v.Models) + if err != nil { + return nil, fmt.Errorf("combining models: %+v", err) + } + existing.Models = *models + + operations, err := operations(existing.Operations, v.Operations) + if err != nil { + return nil, fmt.Errorf("combining operations: %+v", err) + } + existing.Operations = *operations + + resourceIds, err := resourceIds(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 +} 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..bb65503e300 --- /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..578c1cb46d9 --- /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..b0da49857b6 --- /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..6ec92390cd3 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/combine/operations.go @@ -0,0 +1,30 @@ +// 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 operations(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 +} 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..4d43f94ed23 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/combine/resource_ids.go @@ -0,0 +1,32 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package combine + +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" +) + +func resourceIds(first, 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 +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/combine/todo.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/combine/todo.go deleted file mode 100644 index 59a6787d798..00000000000 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/combine/todo.go +++ /dev/null @@ -1,180 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package combine - -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" -) - -func ResourcesWith(first, other map[string]sdkModels.APIResource) (*map[string]sdkModels.APIResource, error) { - resources := make(map[string]sdkModels.APIResource) - for k, v := range first { - resources[k] = v - } - - for k, v := range other { - existing, ok := resources[k] - if !ok { - resources[k] = v - continue - } - - constants, err := constants(existing.Constants, v.Constants) - if err != nil { - return nil, fmt.Errorf("combining constants: %+v", err) - } - existing.Constants = *constants - - models, err := models(existing.Models, v.Models) - if err != nil { - return nil, fmt.Errorf("combining models: %+v", err) - } - existing.Models = *models - - operations, err := operations(existing.Operations, v.Operations) - if err != nil { - return nil, fmt.Errorf("combining operations: %+v", err) - } - existing.Operations = *operations - - resourceIds, err := resourceIds(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 constants(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 := maps(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 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 -} - -func operations(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 resourceIds(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 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/models/parse_result.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models/parse_result.go new file mode 100644 index 00000000000..ff7e678629c --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models/parse_result.go @@ -0,0 +1,160 @@ +package models + +import ( + "fmt" + "reflect" + + "github.com/hashicorp/go-azure-helpers/lang/pointer" + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" +) + +type ParseResult struct { + Constants map[string]sdkModels.SDKConstant + Models map[string]sdkModels.SDKModel +} + +func (r *ParseResult) Append(other ParseResult) error { + if r.Constants == nil { + r.Constants = make(map[string]sdkModels.SDKConstant) + } + if err := r.AppendConstants(other.Constants); err != nil { + return fmt.Errorf("appending constants: %+v", err) + } + + if r.Models == nil { + r.Models = make(map[string]sdkModels.SDKModel) + } + if err := r.AppendModels(other.Models); err != nil { + return fmt.Errorf("appending models: %+v", err) + } + + return nil +} + +func (r *ParseResult) AppendConstants(other map[string]sdkModels.SDKConstant) error { + for k, v := range other { + existing, hasExisting := r.Constants[k] + if !hasExisting { + r.Constants[k] = v + continue + } + + if v.Type != existing.Type { + 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) + } + } + + return nil +} + +func (r *ParseResult) AppendModels(other map[string]sdkModels.SDKModel) error { + for k, v := range other { + existing, hasExisting := r.Models[k] + if !hasExisting { + r.Models[k] = v + continue + } + + if err := compareNilableString(existing.DiscriminatedValue, v.DiscriminatedValue); err != nil { + return fmt.Errorf("comparing DiscriminatedValue: %+v", err) + } + if err := compareNilableString(existing.FieldNameContainingDiscriminatedValue, v.FieldNameContainingDiscriminatedValue); err != nil { + return fmt.Errorf("comparing FieldNameContainingDiscriminatedValue: %+v", err) + } + if err := compareNilableString(existing.ParentTypeName, v.ParentTypeName); err != nil { + return fmt.Errorf("comparing ParentTypeName: %+v", err) + } + + if err := compareFields(existing.Fields, v.Fields); err != nil { + return fmt.Errorf("different model objects for Model %q: %+v.\n\nFirst fields: %+v.\n\nSecond fields: %+v", k, err, existing.Fields, v.Fields) + } + } + + 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)) + } + + for k := range first { + firstVal := first[k] + secondVal, ok := second[k] + if !ok { + return fmt.Errorf("second didn't contain the key %q", k) + } + + // TODO this can be uncommented when #3325 has been fixed + //if firstVal.Description != secondVal.Description { + // return fmt.Errorf("first.Description was %q but second.Description was %q", firstVal.Description, secondVal.Description) + //} + if firstVal.JsonName != secondVal.JsonName { + return fmt.Errorf("first.JsonName was %q but second.JsonName was %q", firstVal.JsonName, secondVal.JsonName) + } + if firstVal.Required != secondVal.Required { + return fmt.Errorf("first.Required was %t but second.Required was %t", firstVal.Required, secondVal.Required) + } + if firstVal.Sensitive != secondVal.Sensitive { + return fmt.Errorf("first.Sensitive was %t but second.Sensitive was %t", firstVal.Sensitive, secondVal.Sensitive) + } + if err := objectDefinitionsMatch(firstVal.ObjectDefinition, secondVal.ObjectDefinition); err != nil { + return fmt.Errorf("object definitions differ: %+v.\n\nFirst %+v\n\nSecond %+v", err, firstVal.ObjectDefinition, secondVal.ObjectDefinition) + } + } + + return nil +} + +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) + } + if first.NestedItem == nil && second.NestedItem != nil { + return fmt.Errorf("`second` had a nested item but `first` didn't. First: %+v. Second: %+v", first, second) + } + if first.NestedItem != nil && second.NestedItem != nil { + if err := objectDefinitionsMatch(*first.NestedItem, *second.NestedItem); err != nil { + return fmt.Errorf("nested items differ: %+v", err) + } + } + 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)) + } + + // TODO: re-enable minimum/maximum/unique on Object Definition + //if err := compareNilableInteger(first.Maximum, second.Maximum); err != nil { + // return fmt.Errorf("value for Maximum differs: %+v\n\nFirst was %d but second was %d", err, pointer.From(first.Maximum), pointer.From(second.Maximum)) + //} + //if err := compareNilableInteger(first.Minimum, second.Minimum); err != nil { + // return fmt.Errorf("value for Minimum differs: %+v\n\nFirst was %d but second was %d", err, pointer.From(first.Minimum), pointer.From(second.Minimum)) + //} + //if err := compareNilableBoolean(first.UniqueItems, second.UniqueItems); err != nil { + // return fmt.Errorf("value for Unique Items differs: %+v\n\nFirst was %t but second was %t", err, pointer.From(first.UniqueItems), pointer.From(second.UniqueItems)) + //} + + return nil +} + +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 +} From 9041a95c3162ffac1afc3dd74f8325bb67753098 Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Wed, 10 Jul 2024 12:05:23 +0200 Subject: [PATCH 17/58] tools/importer-rest-api-specs: reimplementing #4290 atop the rebase The rebase is messed up, but it's a minor patch so should be fine --- .../components/parser/constants/interface.go | 333 ------------------ .../apidefinitions/parser/constants/parse.go | 2 +- 2 files changed, 1 insertion(+), 334 deletions(-) delete mode 100644 tools/importer-rest-api-specs/components/parser/constants/interface.go 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/internal/components/apidefinitions/parser/constants/parse.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/constants/parse.go index 930e94d12a5..7c1111830df 100644 --- 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 @@ -123,7 +123,7 @@ func parseKeysAndValues(input []interface{}, constantType sdkModels.SDKConstantT key := keyValueForInteger(int64(value)) // if an override name is defined for this Constant then we should use it - if constExtension.valuesToDisplayNames != nil { + if constExtension != nil && constExtension.valuesToDisplayNames != nil { overrideName, hasOverride := (*constExtension.valuesToDisplayNames)[value] if hasOverride { key = overrideName From 5ac3c6b487410f2a693165c5aab34d362e53458a Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Wed, 10 Jul 2024 13:25:14 +0200 Subject: [PATCH 18/58] `tools/importer-rest-api-specs`: adding a placeholder for parsing an APIResource --- .../parser/parse_api_resource.go | 11 +++++++++ .../apidefinitions/parser/parser.go | 23 +++++++++++++++---- 2 files changed, 30 insertions(+), 4 deletions(-) create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_api_resource.go diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_api_resource.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_api_resource.go new file mode 100644 index 00000000000..dfdaa6eb328 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_api_resource.go @@ -0,0 +1,11 @@ +package parser + +import ( + "fmt" + + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" +) + +func parseAPIResourcesWithin(filePath string, existingAPIResources map[string]sdkModels.APIResource) (*map[string]sdkModels.APIResource, error) { + return nil, fmt.Errorf("unimplemented until the refactor is completed") +} 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 index 37758ddbc4f..8848d117cbc 100644 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parser.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parser.go @@ -10,26 +10,41 @@ import ( "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) (*sdkModels.APIVersion, error) { - apiResource := make(map[string]sdkModels.APIResource) + apiResources := make(map[string]sdkModels.APIResource) // Firstly let's go through and process each of the Supplementary Files for _, filePath := range input.FilePathsContainingSupplementaryData { logging.Tracef("Processing Supplementary Data from file %q..", filePath) - // TODO: parse the file + resources, err := parseAPIResourcesWithin(filePath, apiResources) + if err != nil { + return nil, fmt.Errorf("parsing the APIResources from the Supplementary Data within %q: %+v", filePath, err) + } + + logging.Tracef("There are now %d APIResources", len(*resources)) + apiResources = *resources + logging.Tracef("Processing Supplementary Data from file %q - Completed.", filePath) } // Next let's go through and process each of the API Definitions for _, filePath := range input.FilePathsContainingAPIDefinitions { logging.Tracef("Processing API Definitions from file %q..", filePath) - // TODO: parse the file + resources, err := parseAPIResourcesWithin(filePath, apiResources) + if err != nil { + return nil, fmt.Errorf("parsing the APIResources from the Supplementary Data within %q: %+v", filePath, err) + } + + logging.Tracef("There are now %d APIResources", len(*resources)) + apiResources = *resources + logging.Tracef("Processing API Definitions from file %q - Completed.", filePath) } apiVersion := sdkModels.APIVersion{ APIVersion: input.APIVersion, Generate: true, Preview: !input.ContainsStableAPIVersion, - Resources: apiResource, + Resources: apiResources, Source: sdkModels.AzureRestAPISpecsSourceDataOrigin, } From 1c24bf8f2c23a8cba2e21b021f3dbddbc854ad30 Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Fri, 12 Jul 2024 11:53:30 +0200 Subject: [PATCH 19/58] `tools/importer-rest-api-specs`: refactoring the parser into smaller packages This leaves the old parser logic in place for now, which'll be cleaned up in the next commit --- .../components/parser/internal/structs.go | 10 - .../parser/ported_constants_test.go | 1007 +++++++++ .../{definition.go => ported_definition.go} | 0 .../{flattener.go => ported_flattener.go} | 0 .../parser/{helpers.go => ported_helpers.go} | 11 + ...ce_id.go => ported_helpers_resource_id.go} | 0 ...helpers_test.go => ported_helpers_test.go} | 12 +- .../parser/{models.go => ported_models.go} | 22 +- ....go => ported_models_commonschema_test.go} | 86 +- .../ported_models_datafactorycustom_test.go | 200 ++ .../parser/ported_models_dictionaries_test.go | 383 ++++ .../ported_models_discriminators_test.go | 1424 +++++++++++++ .../components/parser/ported_models_test.go | 1821 +++++++++++++++++ ...inition.go => ported_object_definition.go} | 25 +- .../{operations.go => ported_operations.go} | 24 +- .../parser/ported_operations_test.go | 1588 ++++++++++++++ .../parser/{parser.go => ported_parser.go} | 62 +- ...logic.go => ported_refactor_glue_logic.go} | 0 .../components/parser/ported_resource_ids.go | 21 + ...ds_test.go => ported_resource_ids_test.go} | 86 +- ...sources.go => ported_swagger_resources.go} | 19 +- ...swagger_tags.go => ported_swagger_tags.go} | 0 .../parser/ported_swagger_tags_test.go | 175 ++ .../components/parser/resource_ids.go | 49 - .../common_id_management_group_test.go | 59 - .../common_id_resource_group_test.go | 89 - .../commonids/common_id_scope_test.go | 53 - .../commonids/common_id_subscription_test.go | 55 - .../common_id_user_assigned_identity_test.go | 67 - ...d_parse.go => rewritten_load_and_parse.go} | 65 +- .../transformer/internal_to_dataapisdk.go | 75 - .../apidefinitions/parse_api_resource.go | 92 + .../apidefinitions/parse_api_version.go | 96 + .../{parser.go => parse_service.go} | 10 +- .../parse_supplementary_data.go | 26 + .../apidefinitions/parser/README.md | 2 + .../cleanup/simplify_operation_names.go | 48 + .../parser/combine/api_resource.go | 4 +- .../parser/combine/constants.go | 2 +- .../apidefinitions/parser/combine/models.go | 2 +- .../parser/combine/resource_ids.go | 8 +- .../parser/commonschema_test.go | 842 ++++++++ .../parser/comparison/resource_ids.go | 68 + .../apidefinitions/parser/constants/parse.go | 2 +- .../apidefinitions}/parser/constants_test.go | 157 +- ...orkaround_inconsistent_swagger_segments.go | 4 +- .../parser/models/supplementary_data.go | 50 + .../parser/models_datafactorycustom_test.go | 11 +- .../parser/models_dictionaries_test.go | 56 +- .../parser/models_discriminators_test.go | 130 +- .../apidefinitions}/parser/models_test.go | 121 +- .../parser/operation/helpers.go | 190 ++ .../parser/operation/identification.go | 35 + .../apidefinitions/parser/operation/list.go} | 11 +- .../parser/operation/options.go | 163 ++ .../parser/operation/parse_operation.go | 82 + .../parser/operation/parse_operations.go | 56 + .../parser/operation/request_object.go | 36 + .../parser/operation/resource_id.go | 30 + .../parser/operation/response_object.go | 80 + .../apidefinitions}/parser/operations_test.go | 297 ++- .../parser/parse_api_resource.go | 11 - .../parser/parse_resource_ids.go | 17 + .../parser/parse_resource_ids_test.go | 752 +++++++ .../parser/parse_supplementary_data.go | 55 + .../parser/parse_supplementary_data_test.go | 26 + .../parser/parse_swagger_tag.go | 68 + .../parser/parse_swagger_tags.go | 29 + .../apidefinitions/parser/parser.go | 64 +- .../parser/parsingcontext/build.go | 115 ++ .../parser/parsingcontext/context.go | 26 + .../parser/parsingcontext/helpers.go | 274 +++ .../parsingcontext/helpers_implements.go | 64 + .../parser/parsingcontext/parse_constant.go | 17 + .../parser/parsingcontext/parse_model.go | 496 +++++ .../parsingcontext/parse_object_definition.go | 375 ++++ .../parser/parsingcontext/top_level_object.go | 24 + .../parser/resourceids/architecture.md | 0 .../parser/resourceids/common_ids.go | 9 +- .../commonids/common_id_app_service.go | 0 .../common_id_app_service_environment.go | 0 .../commonids/common_id_app_service_plan.go | 0 .../common_id_automation_compilation_job.go | 0 .../commonids/common_id_availability_set.go | 0 .../commonids/common_id_bot_service.go | 0 .../common_id_bot_service_channel.go | 0 .../common_id_chaos_studio_capability.go | 0 .../common_id_chaos_studio_target.go | 0 ...mmon_id_cloud_services_ip_configuration.go | 0 ...mon_id_cloud_services_public_ip_address.go | 0 .../commonids/common_id_dedicated_host.go | 0 .../common_id_dedicated_host_group.go | 0 .../common_id_disk_encryption_set.go | 0 .../common_id_expressroute_circuit_peering.go | 0 .../commonids/common_id_hdinsight_cluster.go | 0 .../commonids/common_id_hyperv_site_job.go | 0 .../common_id_hyperv_site_machine.go | 0 .../common_id_hyperv_site_runasaccount.go | 0 .../commonids/common_id_key_vault.go | 0 .../commonids/common_id_key_vault_key.go | 0 .../common_id_key_vault_key_version.go | 0 ...d_key_vault_private_endpoint_connection.go | 0 .../commonids/common_id_kubernetes_cluster.go | 0 .../commonids/common_id_kubernetes_fleet.go | 0 .../commonids/common_id_kusto_cluster.go | 0 .../commonids/common_id_kusto_database.go | 0 .../commonids/common_id_managed_disk.go | 0 .../commonids/common_id_management_group.go | 0 .../commonids/common_id_network_interface.go | 0 ...n_id_network_interface_ip_configuration.go | 0 .../commonids/common_id_p2s_vpn_gateway.go | 0 .../common_id_provisioning_service_id.go | 0 .../commonids/common_id_public_ip_address.go | 0 .../commonids/common_id_resource_group.go | 0 .../resourceids/commonids/common_id_scope.go | 0 .../common_id_shared_image_gallery.go | 0 .../common_id_spring_cloud_service.go | 0 .../commonids/common_id_sql_database.go | 0 .../commonids/common_id_sql_elastic_pool.go | 0 .../common_id_sql_managed_instance.go | 0 ...common_id_sql_managed_instance_database.go | 0 .../commonids/common_id_sql_server.go | 0 .../commonids/common_id_storage_account.go | 0 .../commonids/common_id_storage_container.go | 0 .../resourceids/commonids/common_id_subnet.go | 0 .../commonids/common_id_subscription.go | 0 .../common_id_user_assigned_identity.go | 0 .../common_id_virtual_hub_bgp_connection.go | 0 ...tual_machine_scale_set_ip_configuration.go | 0 ...ual_machine_scale_set_network_interface.go | 0 ...ual_machine_scale_set_public_ip_address.go | 0 .../commonids/common_id_virtual_network.go | 0 .../common_id_virtual_router_peering.go | 0 .../common_id_virtual_wan_p2s_vpn_gateway.go | 0 .../common_id_virtualhub_ip_configuration.go | 0 .../commonids/common_id_vmware_site_job.go | 0 .../common_id_vmware_site_machine.go | 0 .../common_id_vmware_site_runasaccount.go | 0 .../common_id_vpn_gateway_vpn_connection.go | 0 .../resourceids/commonids/common_ids.go | 0 .../parser/resourceids/commonids/interface.go | 0 .../resourceids/distinct_resource_ids.go | 7 +- .../parser/resourceids/generate_names.go | 17 +- .../parser/resourceids/generate_names_test.go | 8 +- .../parser/resourceids/helpers.go | 61 - .../parser/resourceids/interface.go | 0 .../parser/resourceids/models.go | 9 +- .../parser/resourceids/parse_segments.go | 4 +- .../parser/resourceids/parse_segments_test.go | 0 .../parser/resourceids/parser.go | 3 +- .../parser/swagger_tags_test.go | 16 +- .../testdata/constants_floats_as_floats.json | 0 .../constants_floats_as_floats_inlined.json | 0 .../testdata/constants_floats_as_strings.json | 0 .../constants_floats_as_strings_inlined.json | 0 .../constants_in_operation_parameters.json | 0 .../testdata/constants_integers_as_ints.json | 0 .../constants_integers_as_ints_inlined.json | 0 .../constants_integers_as_strings.json | 0 ...constants_integers_as_strings_inlined.json | 0 .../constants_integers_with_names.json | 0 ...constants_integers_with_names_inlined.json | 0 .../constants_multiple_type_enums.json | 0 .../parser/testdata/constants_strings.json | 0 .../constants_strings_as_non_strings.json | 0 ...nts_strings_containing_floats_inlined.json | 0 .../testdata/constants_strings_inlined.json | 0 ...stants_strings_inlined_as_non_strings.json | 0 .../constants_strings_which_are_floats.json | 0 ...entitylegacysystemanduserassignedlist.json | 0 ...dentitylegacysystemanduserassignedmap.json | 0 ...ysystemanduserassignedmap_extrafields.json | 0 ...manduserassignedmap_genericdictionary.json | 0 ...ema_identitysystemanduserassignedlist.json | 0 ...hema_identitysystemanduserassignedmap.json | 0 ...ysystemanduserassignedmap_extrafields.json | 0 ...l_commonschema_identitysystemassigned.json | 0 ...hema_identitysystemoruserassignedlist.json | 0 ...chema_identitysystemoruserassignedmap.json | 0 ...moruserassignedmap_delegatedresources.json | 0 ...tysystemoruserassignedmap_extrafields.json | 0 ...commonschema_identityuserassignedlist.json | 0 ..._commonschema_identityuserassignedmap.json | 0 ...a_identityuserassignedmap_principalid.json | 0 ...hema_identityuserassignedmap_tenantid.json | 0 .../testdata/model_commonschema_location.json | 0 .../model_containing_allof_object_type.json | 0 ...ing_allof_object_type_with_properties.json | 0 ...el_containing_allof_within_properties.json | 0 ...ning_allof_within_properties_multiple.json | 0 .../testdata/model_containing_lists.json | 0 .../model_dictionary_of_integers.json | 0 .../model_dictionary_of_integers_inlined.json | 0 .../testdata/model_dictionary_of_object.json | 0 .../model_dictionary_of_object_inlined.json | 0 .../testdata/model_dictionary_of_string.json | 0 .../model_dictionary_of_string_inlined.json | 0 ...discriminators_child_that_shouldnt_be.json | 0 ...l_discriminators_child_used_as_parent.json | 0 ...model_discriminators_deep_inheritance.json | 0 ...inators_inherited_from_discriminators.json | 0 ...model_discriminators_multiple_parents.json | 0 ...inators_multiple_parents_within_array.json | 0 ...iscriminators_parent_that_shouldnt_be.json | 0 .../testdata/model_discriminators_simple.json | 0 .../model_discriminators_within_array.json | 0 ..._discriminators_within_discriminators.json | 0 ...riting_from_other_model_no_new_fields.json | 0 ...rom_other_model_no_new_fields_inlined.json | 0 ...rom_other_model_with_only_description.json | 0 ...er_model_with_properties_within_allof.json | 0 .../model_inheriting_from_parent.json | 0 .../testdata/model_inlined_with_no_name.json | 0 ...tiple_top_level_models_and_operations.json | 0 .../parser/testdata/model_top_level.json | 0 .../model_top_level_with_inlined_model.json | 0 .../model_top_level_with_rawfile.json | 0 .../model_using_datafactory_custom_types.json | 0 .../model_with_circular_reference.json | 0 .../testdata/model_with_datetime_no_type.json | 0 .../testdata/model_with_inlined_object.json | 0 .../model_with_number_prefixed_field.json | 0 .../parser/testdata/model_with_reference.json | 0 .../testdata/model_with_reference_array.json | 0 .../model_with_reference_constant.json | 0 .../testdata/model_with_reference_string.json | 0 .../models_bug_2675_duplicate_model.json | 0 .../model_discriminators_orphaned_child.json | 0 ...tors_orphaned_child_with_nested_model.json | 0 ...ned_child_without_discriminator_value.json | 0 .../testdata/operation_content_types.json | 0 .../parser/testdata/operations_empty.json | 0 .../operations_multiple_same_resource_id.json | 0 .../testdata/operations_on_resources.json | 0 .../testdata/operations_single_list.json | 0 .../operations_single_list_of_strings.json | 0 ...tions_single_list_which_is_not_a_list.json | 0 ...erations_single_list_without_pageable.json | 0 .../operations_single_long_running.json | 0 ...ations_single_multiple_return_objects.json | 0 .../operations_single_multiple_tags.json | 0 ...rations_single_requesting_with_a_bool.json | 0 ...questing_with_a_dictionary_of_strings.json | 0 ...erations_single_requesting_with_a_int.json | 0 ...gle_requesting_with_a_list_of_strings.json | 0 ...tions_single_requesting_with_a_string.json | 0 .../operations_single_returning_a_bool.json | 0 ...ingle_returning_a_dictionary_of_model.json | 0 ...gle_returning_a_dictionary_of_strings.json | 0 .../operations_single_returning_a_file.json | 0 .../operations_single_returning_a_float.json | 0 ...tions_single_returning_a_list_of_ints.json | 0 ...gle_returning_a_list_of_list_of_model.json | 0 ...e_returning_a_list_of_list_of_strings.json | 0 ...ions_single_returning_a_list_of_model.json | 0 ...ns_single_returning_a_list_of_strings.json | 0 .../operations_single_returning_a_string.json | 0 ...ngle_returning_a_top_level_raw_object.json | 0 ...single_returning_an_error_status_code.json | 0 ...perations_single_returning_an_integer.json | 0 ...perations_single_tag_different_casing.json | 0 ...operations_single_with_header_options.json | 0 .../operations_single_with_no_tag.json | 0 ...tions_single_with_querystring_options.json | 0 ...ngle_with_request_and_response_object.json | 0 ...operations_single_with_request_object.json | 0 ...ns_single_with_request_object_inlined.json | 0 ...perations_single_with_response_object.json | 0 ...s_single_with_response_object_inlined.json | 0 ...gle_with_response_object_inlined_list.json | 0 .../testdata/operations_single_with_tag.json | 0 ...perations_single_with_tag_resource_id.json | 0 ...ns_single_with_tag_resource_id_suffix.json | 0 .../operations_with_stuttering_names.json | 0 .../parser/testdata/resource_ids_basic.json | 0 .../parser/testdata/resource_ids_common.json | 0 .../resource_ids_containing_constant.json | 0 .../resource_ids_containing_hidden_scope.json | 0 ..._ids_containing_hidden_scope_constant.json | 0 ...ining_hidden_scope_hardcoded_provider.json | 0 ...ce_ids_containing_hidden_scope_nested.json | 0 ...taining_hidden_scope_nested_constants.json | 0 ...idden_scope_nested_hardcoded_provider.json | 0 ...idden_scope_nested_with_extra_segment.json | 0 ...ining_hidden_scope_nested_with_suffix.json | 0 ...ining_hidden_scope_with_extra_segment.json | 0 ...s_containing_hidden_scope_with_suffix.json | 0 .../resource_ids_containing_scope.json | 0 ...urce_ids_lowercased_resource_provider.json | 0 ...ource_ids_multiple_segments_same_name.json | 0 ..._ids_same_id_different_segment_casing.json | 0 ...fferent_constant_values_per_operation.json | 0 .../resource_ids_with_just_suffix.json | 0 .../testdata/resource_ids_with_suffix.json | 0 ...esource_ids_with_suffix_multiple_uris.json | 0 .../testhelpers/todo_parse_swagger_file.go | 41 + .../testhelpers/validate_result_matches.go | 248 +++ .../terraform/identification/helpers.go | 4 +- .../terraform/identification/methods.go | 4 +- .../components/terraform/schema/builder.go | 12 +- .../terraform/schema/fields_resource_id.go | 4 +- .../pipeline/run_importer.go | 50 +- .../pipeline/task_parse_data.go | 12 +- 303 files changed, 11779 insertions(+), 1342 deletions(-) delete mode 100644 tools/importer-rest-api-specs/components/parser/internal/structs.go create mode 100644 tools/importer-rest-api-specs/components/parser/ported_constants_test.go rename tools/importer-rest-api-specs/components/parser/{definition.go => ported_definition.go} (100%) rename tools/importer-rest-api-specs/components/parser/{flattener.go => ported_flattener.go} (100%) rename tools/importer-rest-api-specs/components/parser/{helpers.go => ported_helpers.go} (88%) rename tools/importer-rest-api-specs/components/parser/{helpers_resource_id.go => ported_helpers_resource_id.go} (100%) rename tools/importer-rest-api-specs/components/parser/{helpers_test.go => ported_helpers_test.go} (95%) rename tools/importer-rest-api-specs/components/parser/{models.go => ported_models.go} (95%) rename tools/importer-rest-api-specs/components/parser/{models_commonschema_test.go => ported_models_commonschema_test.go} (92%) create mode 100644 tools/importer-rest-api-specs/components/parser/ported_models_datafactorycustom_test.go create mode 100644 tools/importer-rest-api-specs/components/parser/ported_models_dictionaries_test.go create mode 100644 tools/importer-rest-api-specs/components/parser/ported_models_discriminators_test.go create mode 100644 tools/importer-rest-api-specs/components/parser/ported_models_test.go rename tools/importer-rest-api-specs/components/parser/{object_definition.go => ported_object_definition.go} (94%) rename tools/importer-rest-api-specs/components/parser/{operations.go => ported_operations.go} (95%) create mode 100644 tools/importer-rest-api-specs/components/parser/ported_operations_test.go rename tools/importer-rest-api-specs/components/parser/{parser.go => ported_parser.go} (60%) rename tools/importer-rest-api-specs/components/parser/{refactor_glue_logic.go => ported_refactor_glue_logic.go} (100%) create mode 100644 tools/importer-rest-api-specs/components/parser/ported_resource_ids.go rename tools/importer-rest-api-specs/components/parser/{resource_ids_test.go => ported_resource_ids_test.go} (93%) rename tools/importer-rest-api-specs/components/parser/{swagger_resources.go => ported_swagger_resources.go} (93%) rename tools/importer-rest-api-specs/components/parser/{swagger_tags.go => ported_swagger_tags.go} (100%) create mode 100644 tools/importer-rest-api-specs/components/parser/ported_swagger_tags_test.go delete mode 100644 tools/importer-rest-api-specs/components/parser/resource_ids.go delete mode 100644 tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_management_group_test.go delete mode 100644 tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_resource_group_test.go delete mode 100644 tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_scope_test.go delete mode 100644 tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_subscription_test.go delete mode 100644 tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_user_assigned_identity_test.go rename tools/importer-rest-api-specs/components/parser/{load_and_parse.go => rewritten_load_and_parse.go} (69%) delete mode 100644 tools/importer-rest-api-specs/components/transformer/internal_to_dataapisdk.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parse_api_resource.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parse_api_version.go rename tools/importer-rest-api-specs/internal/components/apidefinitions/{parser.go => parse_service.go} (78%) create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parse_supplementary_data.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/simplify_operation_names.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema_test.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/comparison/resource_ids.go rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/constants_test.go (85%) create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models/supplementary_data.go rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/models_datafactorycustom_test.go (94%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/models_dictionaries_test.go (86%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/models_discriminators_test.go (91%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/models_test.go (95%) create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/operation/helpers.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/operation/identification.go rename tools/importer-rest-api-specs/{components/parser/helpers_list_operations.go => internal/components/apidefinitions/parser/operation/list.go} (86%) create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/operation/options.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/operation/parse_operation.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/operation/parse_operations.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/operation/request_object.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/operation/resource_id.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/operation/response_object.go rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/operations_test.go (86%) delete mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_api_resource.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_resource_ids.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_resource_ids_test.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_supplementary_data.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_supplementary_data_test.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_swagger_tag.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_swagger_tags.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parsingcontext/build.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parsingcontext/context.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parsingcontext/helpers.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parsingcontext/helpers_implements.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parsingcontext/parse_constant.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parsingcontext/parse_model.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parsingcontext/parse_object_definition.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parsingcontext/top_level_object.go rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/architecture.md (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/common_ids.go (61%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/commonids/common_id_app_service.go (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/commonids/common_id_app_service_environment.go (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/commonids/common_id_app_service_plan.go (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/commonids/common_id_automation_compilation_job.go (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/commonids/common_id_availability_set.go (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/commonids/common_id_bot_service.go (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/commonids/common_id_bot_service_channel.go (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/commonids/common_id_chaos_studio_capability.go (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/commonids/common_id_chaos_studio_target.go (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/commonids/common_id_cloud_services_ip_configuration.go (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/commonids/common_id_cloud_services_public_ip_address.go (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/commonids/common_id_dedicated_host.go (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/commonids/common_id_dedicated_host_group.go (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/commonids/common_id_disk_encryption_set.go (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/commonids/common_id_expressroute_circuit_peering.go (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/commonids/common_id_hdinsight_cluster.go (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/commonids/common_id_hyperv_site_job.go (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/commonids/common_id_hyperv_site_machine.go (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/commonids/common_id_hyperv_site_runasaccount.go (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/commonids/common_id_key_vault.go (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/commonids/common_id_key_vault_key.go (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/commonids/common_id_key_vault_key_version.go (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/commonids/common_id_key_vault_private_endpoint_connection.go (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/commonids/common_id_kubernetes_cluster.go (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/commonids/common_id_kubernetes_fleet.go (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/commonids/common_id_kusto_cluster.go (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/commonids/common_id_kusto_database.go (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/commonids/common_id_managed_disk.go (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/commonids/common_id_management_group.go (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/commonids/common_id_network_interface.go (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/commonids/common_id_network_interface_ip_configuration.go (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/commonids/common_id_p2s_vpn_gateway.go (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/commonids/common_id_provisioning_service_id.go (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/commonids/common_id_public_ip_address.go (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/commonids/common_id_resource_group.go (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/commonids/common_id_scope.go (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/commonids/common_id_shared_image_gallery.go (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/commonids/common_id_spring_cloud_service.go (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/commonids/common_id_sql_database.go (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/commonids/common_id_sql_elastic_pool.go (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/commonids/common_id_sql_managed_instance.go (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/commonids/common_id_sql_managed_instance_database.go (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/commonids/common_id_sql_server.go (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/commonids/common_id_storage_account.go (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/commonids/common_id_storage_container.go (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/commonids/common_id_subnet.go (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/commonids/common_id_subscription.go (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/commonids/common_id_user_assigned_identity.go (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/commonids/common_id_virtual_hub_bgp_connection.go (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/commonids/common_id_virtual_machine_scale_set_ip_configuration.go (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/commonids/common_id_virtual_machine_scale_set_network_interface.go (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/commonids/common_id_virtual_machine_scale_set_public_ip_address.go (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/commonids/common_id_virtual_network.go (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/commonids/common_id_virtual_router_peering.go (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/commonids/common_id_virtual_wan_p2s_vpn_gateway.go (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/commonids/common_id_virtualhub_ip_configuration.go (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/commonids/common_id_vmware_site_job.go (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/commonids/common_id_vmware_site_machine.go (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/commonids/common_id_vmware_site_runasaccount.go (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/commonids/common_id_vpn_gateway_vpn_connection.go (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/commonids/common_ids.go (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/commonids/interface.go (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/distinct_resource_ids.go (79%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/generate_names.go (91%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/generate_names_test.go (98%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/helpers.go (50%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/interface.go (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/models.go (87%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/parse_segments.go (98%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/parse_segments_test.go (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/resourceids/parser.go (95%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/swagger_tags_test.go (92%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/constants_floats_as_floats.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/constants_floats_as_floats_inlined.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/constants_floats_as_strings.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/constants_floats_as_strings_inlined.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/constants_in_operation_parameters.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/constants_integers_as_ints.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/constants_integers_as_ints_inlined.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/constants_integers_as_strings.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/constants_integers_as_strings_inlined.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/constants_integers_with_names.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/constants_integers_with_names_inlined.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/constants_multiple_type_enums.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/constants_strings.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/constants_strings_as_non_strings.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/constants_strings_containing_floats_inlined.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/constants_strings_inlined.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/constants_strings_inlined_as_non_strings.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/constants_strings_which_are_floats.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/model_commonschema_identitylegacysystemanduserassignedlist.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/model_commonschema_identitylegacysystemanduserassignedmap.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/model_commonschema_identitylegacysystemanduserassignedmap_extrafields.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/model_commonschema_identitylegacysystemanduserassignedmap_genericdictionary.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/model_commonschema_identitysystemanduserassignedlist.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/model_commonschema_identitysystemanduserassignedmap.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/model_commonschema_identitysystemanduserassignedmap_extrafields.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/model_commonschema_identitysystemassigned.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/model_commonschema_identitysystemoruserassignedlist.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/model_commonschema_identitysystemoruserassignedmap.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/model_commonschema_identitysystemoruserassignedmap_delegatedresources.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/model_commonschema_identitysystemoruserassignedmap_extrafields.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/model_commonschema_identityuserassignedlist.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/model_commonschema_identityuserassignedmap.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/model_commonschema_identityuserassignedmap_principalid.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/model_commonschema_identityuserassignedmap_tenantid.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/model_commonschema_location.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/model_containing_allof_object_type.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/model_containing_allof_object_type_with_properties.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/model_containing_allof_within_properties.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/model_containing_allof_within_properties_multiple.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/model_containing_lists.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/model_dictionary_of_integers.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/model_dictionary_of_integers_inlined.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/model_dictionary_of_object.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/model_dictionary_of_object_inlined.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/model_dictionary_of_string.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/model_dictionary_of_string_inlined.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/model_discriminators_child_that_shouldnt_be.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/model_discriminators_child_used_as_parent.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/model_discriminators_deep_inheritance.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/model_discriminators_inherited_from_discriminators.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/model_discriminators_multiple_parents.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/model_discriminators_multiple_parents_within_array.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/model_discriminators_parent_that_shouldnt_be.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/model_discriminators_simple.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/model_discriminators_within_array.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/model_discriminators_within_discriminators.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/model_inheriting_from_other_model_no_new_fields.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/model_inheriting_from_other_model_no_new_fields_inlined.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/model_inheriting_from_other_model_with_only_description.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/model_inheriting_from_other_model_with_properties_within_allof.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/model_inheriting_from_parent.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/model_inlined_with_no_name.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/model_multiple_top_level_models_and_operations.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/model_top_level.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/model_top_level_with_inlined_model.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/model_top_level_with_rawfile.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/model_using_datafactory_custom_types.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/model_with_circular_reference.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/model_with_datetime_no_type.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/model_with_inlined_object.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/model_with_number_prefixed_field.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/model_with_reference.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/model_with_reference_array.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/model_with_reference_constant.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/model_with_reference_string.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/models_bug_2675_duplicate_model.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/nestedtestdata/model_discriminators_orphaned_child.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/nestedtestdata/model_discriminators_orphaned_child_with_nested_model.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/nestedtestdata/model_discriminators_orphaned_child_without_discriminator_value.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/operation_content_types.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/operations_empty.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/operations_multiple_same_resource_id.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/operations_on_resources.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/operations_single_list.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/operations_single_list_of_strings.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/operations_single_list_which_is_not_a_list.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/operations_single_list_without_pageable.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/operations_single_long_running.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/operations_single_multiple_return_objects.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/operations_single_multiple_tags.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/operations_single_requesting_with_a_bool.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/operations_single_requesting_with_a_dictionary_of_strings.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/operations_single_requesting_with_a_int.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/operations_single_requesting_with_a_list_of_strings.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/operations_single_requesting_with_a_string.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/operations_single_returning_a_bool.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/operations_single_returning_a_dictionary_of_model.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/operations_single_returning_a_dictionary_of_strings.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/operations_single_returning_a_file.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/operations_single_returning_a_float.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/operations_single_returning_a_list_of_ints.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/operations_single_returning_a_list_of_list_of_model.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/operations_single_returning_a_list_of_list_of_strings.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/operations_single_returning_a_list_of_model.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/operations_single_returning_a_list_of_strings.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/operations_single_returning_a_string.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/operations_single_returning_a_top_level_raw_object.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/operations_single_returning_an_error_status_code.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/operations_single_returning_an_integer.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/operations_single_tag_different_casing.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/operations_single_with_header_options.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/operations_single_with_no_tag.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/operations_single_with_querystring_options.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/operations_single_with_request_and_response_object.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/operations_single_with_request_object.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/operations_single_with_request_object_inlined.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/operations_single_with_response_object.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/operations_single_with_response_object_inlined.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/operations_single_with_response_object_inlined_list.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/operations_single_with_tag.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/operations_single_with_tag_resource_id.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/operations_single_with_tag_resource_id_suffix.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/operations_with_stuttering_names.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/resource_ids_basic.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/resource_ids_common.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/resource_ids_containing_constant.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/resource_ids_containing_hidden_scope.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/resource_ids_containing_hidden_scope_constant.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/resource_ids_containing_hidden_scope_hardcoded_provider.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/resource_ids_containing_hidden_scope_nested.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/resource_ids_containing_hidden_scope_nested_constants.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/resource_ids_containing_hidden_scope_nested_hardcoded_provider.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/resource_ids_containing_hidden_scope_nested_with_extra_segment.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/resource_ids_containing_hidden_scope_nested_with_suffix.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/resource_ids_containing_hidden_scope_with_extra_segment.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/resource_ids_containing_hidden_scope_with_suffix.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/resource_ids_containing_scope.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/resource_ids_lowercased_resource_provider.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/resource_ids_multiple_segments_same_name.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/resource_ids_same_id_different_segment_casing.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/resource_ids_same_uri_different_constant_values_per_operation.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/resource_ids_with_just_suffix.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/resource_ids_with_suffix.json (100%) rename tools/importer-rest-api-specs/{components => internal/components/apidefinitions}/parser/testdata/resource_ids_with_suffix_multiple_uris.json (100%) create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testhelpers/todo_parse_swagger_file.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testhelpers/validate_result_matches.go diff --git a/tools/importer-rest-api-specs/components/parser/internal/structs.go b/tools/importer-rest-api-specs/components/parser/internal/structs.go deleted file mode 100644 index 7357cb3f3dd..00000000000 --- a/tools/importer-rest-api-specs/components/parser/internal/structs.go +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package internal - -import ( - parserModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models" -) - -type ParseResult = parserModels.ParseResult diff --git a/tools/importer-rest-api-specs/components/parser/ported_constants_test.go b/tools/importer-rest-api-specs/components/parser/ported_constants_test.go new file mode 100644 index 00000000000..191ff3f2add --- /dev/null +++ b/tools/importer-rest-api-specs/components/parser/ported_constants_test.go @@ -0,0 +1,1007 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package parser + +import ( + "testing" + + "github.com/hashicorp/go-azure-helpers/lang/pointer" + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/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) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Example": { + Constants: map[string]sdkModels.SDKConstant{ + "TableNumber": { + Type: sdkModels.IntegerSDKConstantType, + Values: map[string]string{ + "One": "1", + "Two": "2", + "Three": "3", + "FourFiveSixSeven": "4567", + }, + }, + }, + Models: map[string]sdkModels.SDKModel{ + "ExampleWrapper": { + Fields: map[string]sdkModels.SDKField{ + "FavouriteTable": { + JsonName: "favouriteTable", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("TableNumber"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("ExampleWrapper"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/example"), + }, + }, + }, + }, + } + 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) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Example": { + Constants: map[string]sdkModels.SDKConstant{ + "TableNumber": { + Type: sdkModels.IntegerSDKConstantType, + Values: map[string]string{ + "First": "1", + "Second": "2", + "Third": "3", + }, + }, + }, + Models: map[string]sdkModels.SDKModel{ + "ExampleWrapper": { + Fields: map[string]sdkModels.SDKField{ + "FavouriteTable": { + JsonName: "favouriteTable", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("TableNumber"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("ExampleWrapper"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/example"), + }, + }, + }, + }, + } + 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) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Example": { + Constants: map[string]sdkModels.SDKConstant{ + "TableNumber": { + Type: sdkModels.IntegerSDKConstantType, + Values: map[string]string{ + "One": "1", + "Two": "2", + "Three": "3", + }, + }, + }, + Models: map[string]sdkModels.SDKModel{ + "ExampleWrapper": { + Fields: map[string]sdkModels.SDKField{ + "FavouriteTable": { + JsonName: "favouriteTable", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("TableNumber"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("ExampleWrapper"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/example"), + }, + }, + }, + }, + } + 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) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Example": { + Constants: map[string]sdkModels.SDKConstant{ + "TableNumber": { + Type: sdkModels.IntegerSDKConstantType, + Values: map[string]string{ + "One": "1", + "Two": "2", + "Three": "3", + }, + }, + }, + Models: map[string]sdkModels.SDKModel{ + "ExampleWrapper": { + Fields: map[string]sdkModels.SDKField{ + "FavouriteTable": { + JsonName: "favouriteTable", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("TableNumber"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("ExampleWrapper"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/example"), + }, + }, + }, + }, + } + 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) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Example": { + Constants: map[string]sdkModels.SDKConstant{ + "TableNumber": { + Type: sdkModels.IntegerSDKConstantType, + Values: map[string]string{ + "First": "1", + "Second": "2", + "Third": "3", + }, + }, + }, + Models: map[string]sdkModels.SDKModel{ + "ExampleWrapper": { + Fields: map[string]sdkModels.SDKField{ + "FavouriteTable": { + JsonName: "favouriteTable", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("TableNumber"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("ExampleWrapper"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/example"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseConstantsMultipleTypeEnums(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "constants_multiple_type_enums.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Example": { + Constants: map[string]sdkModels.SDKConstant{ + "AnimalType": { + Type: sdkModels.StringSDKConstantType, + Values: map[string]string{ + "Cat": "cat", + "Dog": "dog", + "Panda": "panda", + }, + }, + "PlanetType": { + Type: sdkModels.StringSDKConstantType, + Values: map[string]string{ + "Mercury": "mercury", + "Saturn": "saturn", + }, + }, + }, + Models: map[string]sdkModels.SDKModel{ + "Animal": { + Fields: map[string]sdkModels.SDKField{ + "Type": { + JsonName: "type", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("AnimalType"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + "Planet": { + Fields: map[string]sdkModels.SDKField{ + "Type": { + JsonName: "type", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("PlanetType"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Animal": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Animal"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/animal"), + }, + "Planet": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Planet"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/planet"), + }, + }, + }, + }, + } + 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) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Example": { + Constants: map[string]sdkModels.SDKConstant{ + "TableNumber": { + Type: sdkModels.IntegerSDKConstantType, + Values: map[string]string{ + "One": "1", + "Two": "2", + "Three": "3", + }, + }, + }, + Models: map[string]sdkModels.SDKModel{ + "ExampleWrapper": { + Fields: map[string]sdkModels.SDKField{ + "FavouriteTable": { + JsonName: "favouriteTable", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("TableNumber"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("ExampleWrapper"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/example"), + }, + }, + }, + }, + } + 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) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Example": { + Constants: map[string]sdkModels.SDKConstant{ + "TableNumber": { + Type: sdkModels.FloatSDKConstantType, + Values: map[string]string{ + "OnePointOne": "1.1", + "TwoPointTwo": "2.2", + "ThreePointThreeZeroZeroZeroFour": "3.30004", + }, + }, + }, + Models: map[string]sdkModels.SDKModel{ + "ExampleWrapper": { + Fields: map[string]sdkModels.SDKField{ + "FavouriteTable": { + JsonName: "favouriteTable", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("TableNumber"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("ExampleWrapper"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/example"), + }, + }, + }, + }, + } + 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) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Example": { + Constants: map[string]sdkModels.SDKConstant{ + "TableNumber": { + Type: sdkModels.FloatSDKConstantType, + Values: map[string]string{ + "OnePointOne": "1.1", + "TwoPointTwo": "2.2", + "ThreePointThreeZeroZeroZeroFour": "3.30004", + }, + }, + }, + Models: map[string]sdkModels.SDKModel{ + "ExampleWrapper": { + Fields: map[string]sdkModels.SDKField{ + "FavouriteTable": { + JsonName: "favouriteTable", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("TableNumber"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("ExampleWrapper"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/example"), + }, + }, + }, + }, + } + 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) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Example": { + Constants: map[string]sdkModels.SDKConstant{ + "TableNumber": { + Type: sdkModels.FloatSDKConstantType, + Values: map[string]string{ + "OnePointOne": "1.1", + "TwoPointTwo": "2.2", + "ThreePointThreeZeroZeroZeroFour": "3.30004", + }, + }, + }, + Models: map[string]sdkModels.SDKModel{ + "ExampleWrapper": { + Fields: map[string]sdkModels.SDKField{ + "FavouriteTable": { + JsonName: "favouriteTable", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("TableNumber"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("ExampleWrapper"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/example"), + }, + }, + }, + }, + } + 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) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Example": { + Constants: map[string]sdkModels.SDKConstant{ + "TableNumber": { + Type: sdkModels.FloatSDKConstantType, + Values: map[string]string{ + "OnePointOne": "1.1", + "TwoPointTwo": "2.2", + "ThreePointThreeZeroZeroZeroFour": "3.30004", + }, + }, + }, + Models: map[string]sdkModels.SDKModel{ + "ExampleWrapper": { + Fields: map[string]sdkModels.SDKField{ + "FavouriteTable": { + JsonName: "favouriteTable", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("TableNumber"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("ExampleWrapper"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/example"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseConstantsStringsTopLevel(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "constants_strings.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Example": { + Constants: map[string]sdkModels.SDKConstant{ + "AnimalType": { + Type: sdkModels.StringSDKConstantType, + Values: map[string]string{ + "Cat": "cat", + "Dog": "dog", + "Panda": "panda", + "Any": "*", + }, + }, + }, + Models: map[string]sdkModels.SDKModel{ + "ExampleWrapper": { + Fields: map[string]sdkModels.SDKField{ + "Type": { + JsonName: "type", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("AnimalType"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("ExampleWrapper"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/example"), + }, + }, + }, + }, + } + 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) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Example": { + Constants: map[string]sdkModels.SDKConstant{ + "AnimalType": { + Type: sdkModels.StringSDKConstantType, + Values: map[string]string{ + "Cat": "cat", + "Dog": "dog", + "Panda": "panda", + }, + }, + }, + Models: map[string]sdkModels.SDKModel{ + "ExampleWrapper": { + Fields: map[string]sdkModels.SDKField{ + "Type": { + JsonName: "type", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("AnimalType"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("ExampleWrapper"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/example"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseConstantsStringsInlined(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "constants_strings_inlined.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Example": { + Constants: map[string]sdkModels.SDKConstant{ + "AnimalType": { + Type: sdkModels.StringSDKConstantType, + Values: map[string]string{ + "Cat": "cat", + "Dog": "dog", + "Panda": "panda", + }, + }, + }, + Models: map[string]sdkModels.SDKModel{ + "ExampleWrapper": { + Fields: map[string]sdkModels.SDKField{ + "Type": { + JsonName: "type", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("AnimalType"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("ExampleWrapper"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/example"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseConstantsStringsInlinedAsNonStrings(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "constants_strings_inlined_as_non_strings.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Example": { + Constants: map[string]sdkModels.SDKConstant{ + "AnimalType": { + Type: sdkModels.StringSDKConstantType, + Values: map[string]string{ + "Cat": "cat", + "Dog": "dog", + "Panda": "panda", + }, + }, + }, + Models: map[string]sdkModels.SDKModel{ + "ExampleWrapper": { + Fields: map[string]sdkModels.SDKField{ + "Type": { + JsonName: "type", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("AnimalType"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("ExampleWrapper"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/example"), + }, + }, + }, + }, + } + 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) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Example": { + Constants: map[string]sdkModels.SDKConstant{ + "TableNumber": { + Type: sdkModels.StringSDKConstantType, + Values: map[string]string{ + "OnePointOne": "1.1", + "TwoPointTwo": "2.2", + "ThreePointThreeZeroZeroZeroFour": "3.30004", + }, + }, + }, + Models: map[string]sdkModels.SDKModel{ + "ExampleWrapper": { + Fields: map[string]sdkModels.SDKField{ + "FavouriteTable": { + JsonName: "favouriteTable", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("TableNumber"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("ExampleWrapper"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/example"), + }, + }, + }, + }, + } + 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) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Example": { + Constants: map[string]sdkModels.SDKConstant{ + "TableNumber": { + Type: sdkModels.FloatSDKConstantType, + Values: map[string]string{ + "OnePointOne": "1.1", + "TwoPointTwo": "2.2", + "ThreePointThreeZeroZeroZeroFour": "3.30004", + }, + }, + }, + Models: map[string]sdkModels.SDKModel{ + "ExampleWrapper": { + Fields: map[string]sdkModels.SDKField{ + "FavouriteTable": { + JsonName: "favouriteTable", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("TableNumber"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("ExampleWrapper"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/example"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseConstantsFromParameters(t *testing.T) { + result, err := ParseSwaggerFileForTesting(t, "constants_in_operation_parameters.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + if result == nil { + t.Fatal("result was nil") + } + if len(result.Resources) != 1 { + t.Fatalf("expected 1 resource but got %d", len(result.Resources)) + } + + resource, ok := result.Resources["ConstantFunTime"] + if !ok { + t.Fatal("the Resource 'ConstantFunTime' was not found") + } + + // sanity checking + if len(resource.Constants) != 1 { + t.Fatalf("expected 1 constant but got %d", len(resource.Constants)) + } + if len(resource.Models) != 0 { + t.Fatalf("expected 0 models but got %d", len(resource.Models)) + } + if len(resource.Operations) != 1 { + t.Fatalf("expected 1 operation but got %d", len(resource.Operations)) + } + if len(resource.ResourceIDs) != 1 { + t.Fatalf("expected 1 Resource ID but got %d", len(resource.ResourceIDs)) + } + + favouriteTable, ok := resource.Constants["MediaType"] + if !ok { + t.Fatalf("resource.Constants['TableNumber'] was not found") + } + if favouriteTable.Type != sdkModels.StringSDKConstantType { + t.Fatalf("expected resource.Constants['TableNumber'].Type to be 'String' but got %q", favouriteTable.Type) + } + if len(favouriteTable.Values) != 3 { + t.Fatalf("expected resource.Constants['TableNumber'] to have 3 values but got %d", len(favouriteTable.Values)) + } + v, ok := favouriteTable.Values["EightTrack"] + if !ok { + t.Fatalf("resource.Constants['MediaType'] didn't contain the key 'One'") + } + if v != "8Track" { + t.Fatalf("expected the value for resource.Constants['MediaType'].Values['EightTrack'] to be '8Track' but got %q", v) + } + v, ok = favouriteTable.Values["Cassette"] + if !ok { + t.Fatalf("resource.Constants['MediaType'] didn't contain the key 'Cassette'") + } + if v != "Cassette" { + t.Fatalf("expected the value for resource.Constants['MediaType'].Values['Cassette'] to be 'Cassette' but got %q", v) + } + v, ok = favouriteTable.Values["Vinyl"] + if !ok { + t.Fatalf("resource.Constants['MediaType'] didn't contain the key 'Vinyl'") + } + if v != "Vinyl" { + t.Fatalf("expected the value for resource.Constants['MediaType'].Values['Vinyl'] to be 'Vinyl' but got %q", v) + } +} diff --git a/tools/importer-rest-api-specs/components/parser/definition.go b/tools/importer-rest-api-specs/components/parser/ported_definition.go similarity index 100% rename from tools/importer-rest-api-specs/components/parser/definition.go rename to tools/importer-rest-api-specs/components/parser/ported_definition.go diff --git a/tools/importer-rest-api-specs/components/parser/flattener.go b/tools/importer-rest-api-specs/components/parser/ported_flattener.go similarity index 100% rename from tools/importer-rest-api-specs/components/parser/flattener.go rename to tools/importer-rest-api-specs/components/parser/ported_flattener.go diff --git a/tools/importer-rest-api-specs/components/parser/helpers.go b/tools/importer-rest-api-specs/components/parser/ported_helpers.go similarity index 88% rename from tools/importer-rest-api-specs/components/parser/helpers.go rename to tools/importer-rest-api-specs/components/parser/ported_helpers.go index 1ebc35a39d0..5477fd96b15 100644 --- a/tools/importer-rest-api-specs/components/parser/helpers.go +++ b/tools/importer-rest-api-specs/components/parser/ported_helpers.go @@ -67,3 +67,14 @@ func referencesAreTheSame(first []string, second []string) bool { 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 +} diff --git a/tools/importer-rest-api-specs/components/parser/helpers_resource_id.go b/tools/importer-rest-api-specs/components/parser/ported_helpers_resource_id.go similarity index 100% rename from tools/importer-rest-api-specs/components/parser/helpers_resource_id.go rename to tools/importer-rest-api-specs/components/parser/ported_helpers_resource_id.go diff --git a/tools/importer-rest-api-specs/components/parser/helpers_test.go b/tools/importer-rest-api-specs/components/parser/ported_helpers_test.go similarity index 95% rename from tools/importer-rest-api-specs/components/parser/helpers_test.go rename to tools/importer-rest-api-specs/components/parser/ported_helpers_test.go index f9703d8188b..1e36c4279be 100644 --- a/tools/importer-rest-api-specs/components/parser/helpers_test.go +++ b/tools/importer-rest-api-specs/components/parser/ported_helpers_test.go @@ -10,10 +10,9 @@ 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) { +func ParseSwaggerFileForTesting(t *testing.T, file string, serviceName *string) (*sdkModels.APIVersion, error) { // TODO: make this function private parsed, err := load("testdata/", file) if err != nil { @@ -43,15 +42,12 @@ func ParseSwaggerFileForTesting(t *testing.T, file string, serviceName *string) 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/components/parser/models.go b/tools/importer-rest-api-specs/components/parser/ported_models.go similarity index 95% rename from tools/importer-rest-api-specs/components/parser/models.go rename to tools/importer-rest-api-specs/components/parser/ported_models.go index 7442ce4b59e..5e69007dc6e 100644 --- a/tools/importer-rest-api-specs/components/parser/models.go +++ b/tools/importer-rest-api-specs/components/parser/ported_models.go @@ -10,13 +10,13 @@ import ( "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/internal" "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 (d *SwaggerDefinition) parseModel(name string, input spec.Schema) (*internal.ParseResult, error) { - result := internal.ParseResult{ +func (d *SwaggerDefinition) parseModel(name string, input spec.Schema) (*parserModels.ParseResult, error) { + result := parserModels.ParseResult{ Constants: map[string]sdkModels.SDKConstant{}, Models: map[string]sdkModels.SDKModel{}, } @@ -56,9 +56,9 @@ func (d *SwaggerDefinition) parseModel(name string, input spec.Schema) (*interna return &result, nil } -func (d *SwaggerDefinition) findConstantsWithinModel(fieldName string, modelName *string, input spec.Schema, known internal.ParseResult) (*internal.ParseResult, error) { +func (d *SwaggerDefinition) findConstantsWithinModel(fieldName string, modelName *string, input spec.Schema, known parserModels.ParseResult) (*parserModels.ParseResult, error) { // NOTE: both Models and Fields are passed in here - result := internal.ParseResult{ + result := parserModels.ParseResult{ Constants: map[string]sdkModels.SDKConstant{}, Models: map[string]sdkModels.SDKModel{}, } @@ -131,10 +131,10 @@ func (d *SwaggerDefinition) findConstantsWithinModel(fieldName string, modelName return &result, nil } -func (d *SwaggerDefinition) detailsForField(modelName string, propertyName string, value spec.Schema, isRequired bool, known internal.ParseResult) (*sdkModels.SDKField, *internal.ParseResult, error) { +func (d *SwaggerDefinition) 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 := internal.ParseResult{ + result := parserModels.ParseResult{ Constants: map[string]sdkModels.SDKConstant{}, Models: map[string]sdkModels.SDKModel{}, } @@ -235,9 +235,9 @@ func (d *SwaggerDefinition) detailsForField(modelName string, propertyName strin return &field, &result, err } -func (d *SwaggerDefinition) fieldsForModel(modelName string, input spec.Schema, known internal.ParseResult) (*map[string]sdkModels.SDKField, *internal.ParseResult, error) { +func (d *SwaggerDefinition) fieldsForModel(modelName string, input spec.Schema, known parserModels.ParseResult) (*map[string]sdkModels.SDKField, *parserModels.ParseResult, error) { fields := make(map[string]sdkModels.SDKField, 0) - result := internal.ParseResult{ + result := parserModels.ParseResult{ Constants: map[string]sdkModels.SDKConstant{}, Models: map[string]sdkModels.SDKModel{}, } @@ -450,8 +450,8 @@ func (d *SwaggerDefinition) findAncestorType(input spec.Schema) (*string, *strin return nil, nil, nil } -func (d *SwaggerDefinition) findOrphanedDiscriminatedModels(serviceName string) (*internal.ParseResult, error) { - result := internal.ParseResult{ +func (d *SwaggerDefinition) findOrphanedDiscriminatedModels(serviceName string) (*parserModels.ParseResult, error) { + result := parserModels.ParseResult{ Constants: map[string]sdkModels.SDKConstant{}, Models: map[string]sdkModels.SDKModel{}, } diff --git a/tools/importer-rest-api-specs/components/parser/models_commonschema_test.go b/tools/importer-rest-api-specs/components/parser/ported_models_commonschema_test.go similarity index 92% rename from tools/importer-rest-api-specs/components/parser/models_commonschema_test.go rename to tools/importer-rest-api-specs/components/parser/ported_models_commonschema_test.go index a0337519f4e..7c38fd55903 100644 --- a/tools/importer-rest-api-specs/components/parser/models_commonschema_test.go +++ b/tools/importer-rest-api-specs/components/parser/ported_models_commonschema_test.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" ) // TODO: Edge Zones @@ -23,9 +22,8 @@ func TestParseModel_CommonSchema_IdentityLegacySystemAndUserAssignedList(t *test 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{ @@ -72,9 +70,8 @@ func TestParseModel_CommonSchema_IdentityLegacySystemAndUserAssignedMap(t *testi 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{ @@ -123,9 +120,8 @@ func TestParseModel_CommonSchema_IdentityLegacySystemAndUserAssignedMap_ExtraFie 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{ @@ -172,9 +168,8 @@ func TestParseModel_CommonSchema_IdentityLegacySystemAndUserAssignedMap_GenericD 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{ @@ -221,9 +216,8 @@ func TestParseModel_CommonSchema_IdentitySystemAssigned(t *testing.T) { 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{ @@ -270,9 +264,8 @@ func TestParseModel_CommonSchema_IdentitySystemAndUserAssignedList(t *testing.T) 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{ @@ -319,9 +312,8 @@ func TestParseModel_CommonSchema_IdentitySystemAndUserAssignedMap(t *testing.T) 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{ @@ -370,9 +362,8 @@ func TestParseModel_CommonSchema_IdentitySystemAndUserAssignedMap_ExtraFields(t 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{ @@ -419,9 +410,8 @@ func TestParseModel_CommonSchema_IdentitySystemOrUserAssignedList(t *testing.T) 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{ @@ -468,9 +458,8 @@ func TestParseModel_CommonSchema_IdentitySystemOrUserAssignedMap(t *testing.T) { 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{ @@ -519,9 +508,8 @@ func TestParseModel_CommonSchema_IdentitySystemOrUserAssignedMap_DelegatedResour 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{ @@ -570,9 +558,8 @@ func TestParseModel_CommonSchema_IdentitySystemOrUserAssignedMap_ExtraFields(t * 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{ @@ -619,9 +606,8 @@ func TestParseModel_CommonSchema_IdentityUserAssignedList(t *testing.T) { 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{ @@ -668,9 +654,8 @@ func TestParseModel_CommonSchema_IdentityUserAssignedMap(t *testing.T) { 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{ @@ -717,9 +702,8 @@ func TestParseModel_CommonSchema_IdentityUserAssignedMap_PrincipalID(t *testing. 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{ @@ -766,9 +750,8 @@ func TestParseModel_CommonSchema_IdentityUserAssignedMap_TenantID(t *testing.T) 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{ @@ -815,9 +798,8 @@ func TestParseModel_CommonSchema_Location(t *testing.T) { 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{ diff --git a/tools/importer-rest-api-specs/components/parser/ported_models_datafactorycustom_test.go b/tools/importer-rest-api-specs/components/parser/ported_models_datafactorycustom_test.go new file mode 100644 index 00000000000..99aae633dcf --- /dev/null +++ b/tools/importer-rest-api-specs/components/parser/ported_models_datafactorycustom_test.go @@ -0,0 +1,200 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package parser + +import ( + "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" +) + +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) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expectedModels := map[string]sdkModels.SDKModel{ + "Model": { + Fields: map[string]sdkModels.SDKField{ + // Simple Types + "BooleanField": { + JsonName: "booleanField", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.BooleanSDKObjectDefinitionType, + }, + Required: false, + }, + "DoubleField": { + JsonName: "doubleField", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.FloatSDKObjectDefinitionType, + }, + Required: false, + }, + "KeyValuePairField": { + JsonName: "keyValuePairField", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.DictionarySDKObjectDefinitionType, + NestedItem: &sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + }, + Required: false, + }, + "IntegerField": { + JsonName: "integerField", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.IntegerSDKObjectDefinitionType, + }, + Required: false, + }, + "StringField": { + JsonName: "stringField", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + "UnknownField": { + // In this case, the `dfe-*` value is unknown, so we should return an object + // this is the default behaviour, mostly for sanity-checking + JsonName: "unknownField", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.RawObjectSDKObjectDefinitionType, + }, + Required: false, + }, + + // Dictionaries of a Simple Type (using the regular Swagger/OpenAPI syntax) + "DictionaryOfBooleanField": { + JsonName: "dictionaryOfBooleanField", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.DictionarySDKObjectDefinitionType, + NestedItem: &sdkModels.SDKObjectDefinition{ + Type: sdkModels.BooleanSDKObjectDefinitionType, + }, + }, + Required: false, + }, + "DictionaryOfDoubleField": { + JsonName: "dictionaryOfDoubleField", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.DictionarySDKObjectDefinitionType, + NestedItem: &sdkModels.SDKObjectDefinition{ + Type: sdkModels.FloatSDKObjectDefinitionType, + }, + }, + Required: false, + }, + "DictionaryOfIntegerField": { + JsonName: "dictionaryOfIntegerField", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.DictionarySDKObjectDefinitionType, + NestedItem: &sdkModels.SDKObjectDefinition{ + Type: sdkModels.IntegerSDKObjectDefinitionType, + }, + }, + Required: false, + }, + "DictionaryOfStringField": { + JsonName: "dictionaryOfStringField", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.DictionarySDKObjectDefinitionType, + NestedItem: &sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + }, + Required: false, + }, + "DictionaryOfUnknownField": { + // In this case, the `dfe-*` value is unknown, so we should return an object + // this is the default behaviour, mostly for sanity-checking + JsonName: "dictionaryOfUnknownField", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.DictionarySDKObjectDefinitionType, + NestedItem: &sdkModels.SDKObjectDefinition{ + Type: sdkModels.RawObjectSDKObjectDefinitionType, + }, + }, + Required: false, + }, + + // DFE specific List implementations + "DfeCustomListOfString": { + JsonName: "dfeCustomListOfString", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.ListSDKObjectDefinitionType, + NestedItem: &sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + }, + Required: false, + }, + "DfeCustomListOfAnotherObject": { + JsonName: "dfeCustomListOfAnotherObject", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.ListSDKObjectDefinitionType, + NestedItem: &sdkModels.SDKObjectDefinition{ + Type: sdkModels.ReferenceSDKObjectDefinitionType, + ReferenceName: pointer.To("SecondModel"), + }, + }, + Required: false, + }, + }, + }, + "SecondModel": { + Fields: map[string]sdkModels.SDKField{ + "Example": { + JsonName: "example", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + }, + }, + }, + } + + if !featureflags.ParseDataFactoryListsOfReferencesAsRegularObjectDefinitionTypes { + delete(expectedModels, "SecondModel") + expectedModels["Model"].Fields["DfeCustomListOfAnotherObject"] = sdkModels.SDKField{ + JsonName: "dfeCustomListOfAnotherObject", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.RawObjectSDKObjectDefinitionType, + }, + Required: false, + } + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Example": { + Models: expectedModels, + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "PUT", + RequestObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Model"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Model"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/example"), + }, + }, + }, + }, + } + + validateParsedSwaggerResultMatches(t, expected, actual) +} diff --git a/tools/importer-rest-api-specs/components/parser/ported_models_dictionaries_test.go b/tools/importer-rest-api-specs/components/parser/ported_models_dictionaries_test.go new file mode 100644 index 00000000000..75c7e74c560 --- /dev/null +++ b/tools/importer-rest-api-specs/components/parser/ported_models_dictionaries_test.go @@ -0,0 +1,383 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package parser + +import ( + "testing" + + "github.com/hashicorp/go-azure-helpers/lang/pointer" + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" +) + +func TestParseModelWithADictionaryOfIntegers(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "model_dictionary_of_integers.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Hello": { + Models: map[string]sdkModels.SDKModel{ + "Example": { + Fields: map[string]sdkModels.SDKField{ + "Name": { + JsonName: "name", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + "MapField": { + JsonName: "mapField", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.DictionarySDKObjectDefinitionType, + NestedItem: &sdkModels.SDKObjectDefinition{ + Type: sdkModels.IntegerSDKObjectDefinitionType, + }, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "GetWorld": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Example"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/things"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseModelWithADictionaryOfIntegersInlined(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "model_dictionary_of_integers_inlined.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Hello": { + Models: map[string]sdkModels.SDKModel{ + "Example": { + Fields: map[string]sdkModels.SDKField{ + "Name": { + JsonName: "name", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + "MapField": { + JsonName: "mapField", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.DictionarySDKObjectDefinitionType, + NestedItem: &sdkModels.SDKObjectDefinition{ + Type: sdkModels.IntegerSDKObjectDefinitionType, + }, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "GetWorld": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Example"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/things"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseModelWithADictionaryOfAnObject(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "model_dictionary_of_object.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Hello": { + Models: map[string]sdkModels.SDKModel{ + "Example": { + Fields: map[string]sdkModels.SDKField{ + "Name": { + JsonName: "name", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + "MapField": { + JsonName: "mapField", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + NestedItem: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("MapFieldProperties"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + Type: sdkModels.DictionarySDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + "MapFieldProperties": { + Fields: map[string]sdkModels.SDKField{ + "Line1": { + JsonName: "line1", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + "Line2": { + JsonName: "line2", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + "City": { + JsonName: "city", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + "Country": { + JsonName: "country", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "GetWorld": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Example"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/things"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseModelWithADictionaryOfAnObjectInlined(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "model_dictionary_of_object_inlined.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Hello": { + Models: map[string]sdkModels.SDKModel{ + "Example": { + Fields: map[string]sdkModels.SDKField{ + "Name": { + JsonName: "name", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + "MapField": { + JsonName: "mapField", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + NestedItem: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("MapFieldProperties"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + Type: sdkModels.DictionarySDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + "MapFieldProperties": { + Fields: map[string]sdkModels.SDKField{ + "Line1": { + JsonName: "line1", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + "Line2": { + JsonName: "line2", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + "City": { + JsonName: "city", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + "Country": { + JsonName: "country", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "GetWorld": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Example"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/things"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseModelWithADictionaryOfString(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "model_dictionary_of_string.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Hello": { + Models: map[string]sdkModels.SDKModel{ + "Example": { + Fields: map[string]sdkModels.SDKField{ + "Name": { + JsonName: "name", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + "MapField": { + JsonName: "mapField", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.DictionarySDKObjectDefinitionType, + NestedItem: &sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "GetWorld": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Example"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/things"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseModelWithADictionaryOfStringInlined(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "model_dictionary_of_string_inlined.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Hello": { + Models: map[string]sdkModels.SDKModel{ + "Example": { + Fields: map[string]sdkModels.SDKField{ + "Name": { + JsonName: "name", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + "MapField": { + JsonName: "mapField", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.DictionarySDKObjectDefinitionType, + NestedItem: &sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "GetWorld": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Example"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/things"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} diff --git a/tools/importer-rest-api-specs/components/parser/ported_models_discriminators_test.go b/tools/importer-rest-api-specs/components/parser/ported_models_discriminators_test.go new file mode 100644 index 00000000000..9da6a29835f --- /dev/null +++ b/tools/importer-rest-api-specs/components/parser/ported_models_discriminators_test.go @@ -0,0 +1,1424 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package parser + +import ( + "testing" + + "github.com/hashicorp/go-azure-helpers/lang/pointer" + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" +) + +func TestParseDiscriminatorsTopLevel(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "model_discriminators_simple.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Discriminator": { + Models: map[string]sdkModels.SDKModel{ + "ExampleWrapper": { + Fields: map[string]sdkModels.SDKField{ + "Nested": { + JsonName: "nested", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Animal"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + Required: true, + }, + }, + }, + "Animal": { + Fields: map[string]sdkModels.SDKField{ + "AnimalType": { + JsonName: "animalType", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: true, + }, + }, + FieldNameContainingDiscriminatedValue: pointer.To("AnimalType"), + }, + "Cat": { + ParentTypeName: pointer.To("Animal"), + FieldNameContainingDiscriminatedValue: pointer.To("AnimalType"), + DiscriminatedValue: pointer.To("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, + }, + }, + }, + "Dog": { + ParentTypeName: pointer.To("Animal"), + FieldNameContainingDiscriminatedValue: pointer.To("AnimalType"), + DiscriminatedValue: pointer.To("dog"), + Fields: map[string]sdkModels.SDKField{ + "AnimalType": { + JsonName: "animalType", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: true, + }, + "Barks": { + JsonName: "barks", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.BooleanSDKObjectDefinitionType, + }, + Required: true, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("ExampleWrapper"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/example"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseDiscriminatorsWithinArray(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "model_discriminators_within_array.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Discriminator": { + Models: map[string]sdkModels.SDKModel{ + "ExampleWrapper": { + Fields: map[string]sdkModels.SDKField{ + "BiologicalEntities": { + JsonName: "biologicalEntities", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + NestedItem: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("BiologicalEntity"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + Type: sdkModels.ListSDKObjectDefinitionType, + }, + Required: true, + }, + }, + }, + "BiologicalEntity": { + Fields: map[string]sdkModels.SDKField{ + "TypeName": { + JsonName: "typeName", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: true, + }, + }, + FieldNameContainingDiscriminatedValue: pointer.To("TypeName"), + }, + "Cat": { + ParentTypeName: pointer.To("BiologicalEntity"), + FieldNameContainingDiscriminatedValue: pointer.To("TypeName"), + DiscriminatedValue: pointer.To("cat"), + Fields: map[string]sdkModels.SDKField{ + "IsFluffy": { + JsonName: "isFluffy", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.BooleanSDKObjectDefinitionType, + }, + Required: true, + }, + "TypeName": { + JsonName: "typeName", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: true, + }, + }, + }, + "Human": { + ParentTypeName: pointer.To("BiologicalEntity"), + FieldNameContainingDiscriminatedValue: pointer.To("TypeName"), + DiscriminatedValue: pointer.To("human"), + 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, + }, + Required: true, + }, + "TypeName": { + JsonName: "typeName", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: true, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("ExampleWrapper"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/example"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseDiscriminatorsWithinDiscriminators(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "model_discriminators_within_discriminators.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Discriminator": { + Models: map[string]sdkModels.SDKModel{ + "Animal": { + Fields: map[string]sdkModels.SDKField{ + "AnimalType": { + JsonName: "animalType", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: true, + }, + "FavouriteToy": { + JsonName: "favouriteToy", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Toy"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + Required: false, + }, + }, + FieldNameContainingDiscriminatedValue: pointer.To("AnimalType"), + }, + "Bone": { + Fields: map[string]sdkModels.SDKField{ + "Length": { + JsonName: "length", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.FloatSDKObjectDefinitionType, + }, + Required: true, + }, + "ToyType": { + JsonName: "toyType", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: true, + }, + }, + ParentTypeName: pointer.To("Toy"), + FieldNameContainingDiscriminatedValue: pointer.To("ToyType"), + DiscriminatedValue: pointer.To("bone"), + }, + "Cat": { + ParentTypeName: pointer.To("Animal"), + FieldNameContainingDiscriminatedValue: pointer.To("AnimalType"), + DiscriminatedValue: pointer.To("cat"), + Fields: map[string]sdkModels.SDKField{ + "AnimalType": { + JsonName: "animalType", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: true, + }, + "FavouriteToy": { + JsonName: "favouriteToy", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Toy"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + Required: false, + }, + "IsFluffy": { + JsonName: "isFluffy", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.BooleanSDKObjectDefinitionType, + }, + Required: true, + }, + }, + }, + "Dog": { + ParentTypeName: pointer.To("Animal"), + FieldNameContainingDiscriminatedValue: pointer.To("AnimalType"), + DiscriminatedValue: pointer.To("dog"), + Fields: map[string]sdkModels.SDKField{ + "AnimalType": { + JsonName: "animalType", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: true, + }, + "FavouriteToy": { + JsonName: "favouriteToy", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Toy"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + Required: false, + }, + "Barks": { + JsonName: "barks", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.BooleanSDKObjectDefinitionType, + }, + Required: true, + }, + }, + }, + "ExampleWrapper": { + Fields: map[string]sdkModels.SDKField{ + "Nested": { + JsonName: "nested", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Animal"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + Required: true, + }, + }, + }, + "LaserBeam": { + Fields: map[string]sdkModels.SDKField{ + "Colour": { + JsonName: "colour", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: true, + }, + "Intensity": { + JsonName: "intensity", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.IntegerSDKObjectDefinitionType, + }, + Required: false, + }, + "ToyType": { + JsonName: "toyType", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: true, + }, + }, + ParentTypeName: pointer.To("Toy"), + FieldNameContainingDiscriminatedValue: pointer.To("ToyType"), + DiscriminatedValue: pointer.To("laser-beam"), + }, + "Toy": { + Fields: map[string]sdkModels.SDKField{ + "ToyType": { + JsonName: "toyType", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: true, + }, + }, + FieldNameContainingDiscriminatedValue: pointer.To("ToyType"), + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("ExampleWrapper"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/example"), + }, + }, + }, + }, + } + 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) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Discriminator": { + Models: map[string]sdkModels.SDKModel{ + "Animal": { + Fields: map[string]sdkModels.SDKField{ + "Type": { + JsonName: "type", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Animal"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/example"), + }, + }, + }, + }, + } + 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) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Discriminator": { + Models: map[string]sdkModels.SDKModel{ + "Dog": { + Fields: map[string]sdkModels.SDKField{ + "Barks": { + JsonName: "barks", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.BooleanSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Dog"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/example"), + }, + }, + }, + }, + } + 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) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Discriminator": { + 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"), + }, + "Dog": { + Fields: map[string]sdkModels.SDKField{ + "AnimalType": { + JsonName: "animalType", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: true, + }, + "Barks": { + JsonName: "barks", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.BooleanSDKObjectDefinitionType, + }, + Required: true, + }, + }, + ParentTypeName: pointer.To("Animal"), + FieldNameContainingDiscriminatedValue: pointer.To("AnimalType"), + DiscriminatedValue: pointer.To("dog"), + }, + "ExampleWrapper": { + Fields: map[string]sdkModels.SDKField{ + "Nested": { + JsonName: "nested", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Dog"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + Required: true, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("ExampleWrapper"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/example"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseDiscriminatorsInheritingFromOtherDiscriminators(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "model_discriminators_inherited_from_discriminators.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Discriminator": { + 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"), + }, + "Dog": { + Fields: map[string]sdkModels.SDKField{ + "AnimalType": { + JsonName: "animalType", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: true, + }, + "Barks": { + JsonName: "barks", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.BooleanSDKObjectDefinitionType, + }, + Required: true, + }, + "IsFluffy": { + JsonName: "isFluffy", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.BooleanSDKObjectDefinitionType, + }, + Required: true, + }, + }, + ParentTypeName: pointer.To("Animal"), + FieldNameContainingDiscriminatedValue: pointer.To("AnimalType"), + DiscriminatedValue: pointer.To("dog"), + }, + "ExampleWrapper": { + Fields: map[string]sdkModels.SDKField{ + "Nested": { + JsonName: "nested", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Animal"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + Required: true, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("ExampleWrapper"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/example"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseDiscriminatorsDeepInheritance(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "model_discriminators_deep_inheritance.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Discriminator": { + Models: map[string]sdkModels.SDKModel{ + "Animal": { + Fields: map[string]sdkModels.SDKField{ + "TypeName": { + JsonName: "typeName", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: true, + }, + "IsPlantEater": { + JsonName: "isPlantEater", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.BooleanSDKObjectDefinitionType, + }, + Required: true, + }, + }, + ParentTypeName: pointer.To("BiologicalEntity"), + FieldNameContainingDiscriminatedValue: pointer.To("TypeName"), + DiscriminatedValue: pointer.To("animal"), + }, + "BiologicalEntity": { + Fields: map[string]sdkModels.SDKField{ + "TypeName": { + JsonName: "typeName", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: true, + }, + }, + FieldNameContainingDiscriminatedValue: pointer.To("TypeName"), + }, + "Carnivore": { + Fields: map[string]sdkModels.SDKField{ + "IsPlantEater": { + JsonName: "isPlantEater", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.BooleanSDKObjectDefinitionType, + }, + Required: true, + }, + "IsPredator": { + JsonName: "isPredator", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.BooleanSDKObjectDefinitionType, + }, + Required: true, + }, + "TypeName": { + JsonName: "typeName", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: true, + }, + }, + ParentTypeName: pointer.To("BiologicalEntity"), + FieldNameContainingDiscriminatedValue: pointer.To("TypeName"), + DiscriminatedValue: pointer.To("carnivore"), + }, + "Cat": { + Fields: map[string]sdkModels.SDKField{ + "IsFluffy": { + JsonName: "isFluffy", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.BooleanSDKObjectDefinitionType, + }, + Required: true, + }, + "IsPlantEater": { + JsonName: "isPlantEater", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.BooleanSDKObjectDefinitionType, + }, + Required: false, + }, + "IsPredator": { + JsonName: "isPredator", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.BooleanSDKObjectDefinitionType, + }, + Required: true, + }, + "TypeName": { + JsonName: "typeName", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: true, + }, + }, + ParentTypeName: pointer.To("BiologicalEntity"), + FieldNameContainingDiscriminatedValue: pointer.To("TypeName"), + DiscriminatedValue: pointer.To("cat"), + }, + "ExampleWrapper": { + Fields: map[string]sdkModels.SDKField{ + "BiologicalEntity": { + JsonName: "biologicalEntity", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("BiologicalEntity"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + Required: true, + }, + }, + }, + "PersianCat": { + ParentTypeName: pointer.To("BiologicalEntity"), + FieldNameContainingDiscriminatedValue: pointer.To("TypeName"), + DiscriminatedValue: pointer.To("persian-cat"), + Fields: map[string]sdkModels.SDKField{ + "IsFluffy": { + JsonName: "isFluffy", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.BooleanSDKObjectDefinitionType, + }, + Required: true, + }, + "IsFriendly": { + JsonName: "isFriendly", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.BooleanSDKObjectDefinitionType, + }, + Required: true, + }, + "IsPlantEater": { + JsonName: "isPlantEater", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.BooleanSDKObjectDefinitionType, + }, + Required: false, + }, + "IsPredator": { + JsonName: "isPredator", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.BooleanSDKObjectDefinitionType, + }, + Required: false, + }, + "TypeName": { + JsonName: "typeName", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: true, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("ExampleWrapper"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/example"), + }, + }, + }, + }, + } + 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) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Discriminator": { + Models: map[string]sdkModels.SDKModel{ + "BiologicalEntity": { + Fields: map[string]sdkModels.SDKField{ + "SomeField": { + JsonName: "someField", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + "TypeName": { + JsonName: "typeName", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: true, + }, + }, + FieldNameContainingDiscriminatedValue: pointer.To("TypeName"), + }, + "Cat": { + Fields: map[string]sdkModels.SDKField{ + "FirstName": { + JsonName: "firstName", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: true, + }, + "IsFluffy": { + JsonName: "isFluffy", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.BooleanSDKObjectDefinitionType, + }, + Required: true, + }, + "LastName": { + JsonName: "lastName", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + "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("cat"), + }, + "ExampleWrapper": { + Fields: map[string]sdkModels.SDKField{ + "Item": { + JsonName: "item", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("BiologicalEntity"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + Required: true, + }, + }, + }, + "Human": { + Fields: map[string]sdkModels.SDKField{ + "Age": { + JsonName: "age", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.IntegerSDKObjectDefinitionType, + }, + Required: true, + }, + "FirstName": { + JsonName: "firstName", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: true, + }, + "LastName": { + JsonName: "lastName", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: true, + }, + "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("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. + }, + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("ExampleWrapper"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/example"), + }, + }, + }, + }, + } + 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) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Discriminator": { + Models: map[string]sdkModels.SDKModel{ + "BiologicalEntity": { + Fields: map[string]sdkModels.SDKField{ + "SomeField": { + JsonName: "someField", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + "TypeName": { + JsonName: "typeName", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: true, + }, + }, + FieldNameContainingDiscriminatedValue: pointer.To("TypeName"), + }, + "Cat": { + Fields: map[string]sdkModels.SDKField{ + "FirstName": { + JsonName: "firstName", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: true, + }, + "IsFluffy": { + JsonName: "isFluffy", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.BooleanSDKObjectDefinitionType, + }, + Required: true, + }, + "LastName": { + JsonName: "lastName", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + "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("cat"), + }, + "ExampleWrapper": { + Fields: map[string]sdkModels.SDKField{ + "Items": { + JsonName: "items", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + NestedItem: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("BiologicalEntity"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + Type: sdkModels.ListSDKObjectDefinitionType, + }, + Required: true, + }, + }, + }, + "Human": { + Fields: map[string]sdkModels.SDKField{ + "Age": { + JsonName: "age", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.IntegerSDKObjectDefinitionType, + }, + Required: true, + }, + "FirstName": { + JsonName: "firstName", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: true, + }, + "LastName": { + JsonName: "lastName", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: true, + }, + "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("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. + }, + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("ExampleWrapper"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/example"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseDiscriminatorsOrphanedChild(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "/nestedtestdata/model_discriminators_orphaned_child.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + 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 + // some unexpectedly named data, but this is fine providing the APIResource we're expecting exists. + "ModelDiscriminatorsOrphanedChildren": { + 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"), + }, + "Dog": { + Fields: map[string]sdkModels.SDKField{ + "AnimalType": { + JsonName: "animalType", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: true, + }, + "Barks": { + JsonName: "barks", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.BooleanSDKObjectDefinitionType, + }, + Required: true, + }, + }, + ParentTypeName: pointer.To("Animal"), + FieldNameContainingDiscriminatedValue: pointer.To("AnimalType"), + DiscriminatedValue: pointer.To("dog"), + }, + // ExampleWrapper is present in the Swagger but unused + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseDiscriminatorsOrphanedChildWithNestedModel(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "/nestedtestdata/model_discriminators_orphaned_child_with_nested_model.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + 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 + // some unexpectedly named data, but this is fine providing the APIResource we're expecting exists. + "ModelDiscriminatorsOrphanedChildWithNestedModels": { + 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"), + }, + "Dog": { + Fields: map[string]sdkModels.SDKField{ + "AnimalType": { + JsonName: "animalType", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: true, + }, + "Barks": { + JsonName: "barks", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.BooleanSDKObjectDefinitionType, + }, + Required: true, + }, + "Parameters": { + JsonName: "parameters", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + NestedItem: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("KeyValuePair"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + Type: sdkModels.ListSDKObjectDefinitionType, + }, + Required: false, + }, + }, + ParentTypeName: pointer.To("Animal"), + FieldNameContainingDiscriminatedValue: pointer.To("AnimalType"), + DiscriminatedValue: pointer.To("dog"), + }, + // ExampleWrapper is present in the Swagger but unused + "KeyValuePair": { + Fields: map[string]sdkModels.SDKField{ + "Key": { + JsonName: "key", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: true, + }, + "Value": { + JsonName: "value", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: true, + }, + }, + }, + }, + }, + }, + } + 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")) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + 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 + // some unexpectedly named data, but this is fine providing the APIResource we're expecting exists. + "ModelDiscriminatorsOrphanedChildWithoutDiscriminatorValues": { + 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, + }, + }, + ParentTypeName: pointer.To("Animal"), + FieldNameContainingDiscriminatedValue: pointer.To("AnimalType"), + DiscriminatedValue: pointer.To("Cat"), + }, + "Dog": { + Fields: map[string]sdkModels.SDKField{ + "AnimalType": { + JsonName: "animalType", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: true, + }, + }, + ParentTypeName: pointer.To("Animal"), + FieldNameContainingDiscriminatedValue: pointer.To("AnimalType"), + DiscriminatedValue: pointer.To("Dog"), + }, + // ExampleWrapper is present in the Swagger but unused + }, + }, + }, + } + 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")) + 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 := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{}, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} diff --git a/tools/importer-rest-api-specs/components/parser/ported_models_test.go b/tools/importer-rest-api-specs/components/parser/ported_models_test.go new file mode 100644 index 00000000000..22bf5c9f75b --- /dev/null +++ b/tools/importer-rest-api-specs/components/parser/ported_models_test.go @@ -0,0 +1,1821 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package parser + +import ( + "testing" + + "github.com/hashicorp/go-azure-helpers/lang/pointer" + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" +) + +// TODO: tests for the different types of Object Definition + +func TestParseModelTopLevel(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "model_top_level.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Example": { + Models: map[string]sdkModels.SDKModel{ + "Model": { + Fields: map[string]sdkModels.SDKField{ + "Age": { + JsonName: "age", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.IntegerSDKObjectDefinitionType, + }, + Required: false, + }, + "Enabled": { + JsonName: "enabled", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.BooleanSDKObjectDefinitionType, + }, + Required: false, + }, + "Height": { + JsonName: "height", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.FloatSDKObjectDefinitionType, + }, + Required: false, + }, + "Name": { + JsonName: "name", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: true, + }, + "Tags": { + JsonName: "tags", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.TagsSDKObjectDefinitionType, + }, + Required: false, + }, + "Value": { + JsonName: "value", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.RawObjectSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "PUT", + RequestObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Model"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Model"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/example"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseModelTopLevelWithRawFile(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "model_top_level_with_rawfile.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Example": { + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "PUT", + RequestObject: &sdkModels.SDKObjectDefinition{ + Type: sdkModels.RawFileSDKObjectDefinitionType, + }, + ResponseObject: &sdkModels.SDKObjectDefinition{ + Type: sdkModels.RawFileSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/example"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseModelTopLevelWithInlinedModel(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "model_top_level_with_inlined_model.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Example": { + Models: map[string]sdkModels.SDKModel{ + "Model": { + Fields: map[string]sdkModels.SDKField{ + "Name": { + JsonName: "name", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: true, + }, + "Properties": { + JsonName: "properties", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("ModelProperties"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + "ModelProperties": { + Fields: map[string]sdkModels.SDKField{ + "Age": { + JsonName: "age", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.IntegerSDKObjectDefinitionType, + }, + Required: false, + }, + "Enabled": { + JsonName: "enabled", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.BooleanSDKObjectDefinitionType, + }, + Required: false, + }, + "Height": { + JsonName: "height", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.FloatSDKObjectDefinitionType, + }, + Required: false, + }, + "Nickname": { + JsonName: "nickname", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + "Tags": { + JsonName: "tags", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.TagsSDKObjectDefinitionType, + }, + Required: false, + }, + "Value": { + JsonName: "value", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.RawObjectSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "PUT", + RequestObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Model"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Model"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/example"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseModelWithDateTimeNoType(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "model_with_datetime_no_type.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Example": { + Models: map[string]sdkModels.SDKModel{ + "Model": { + Fields: map[string]sdkModels.SDKField{ + "SomeDateValue": { + JsonName: "someDateValue", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.DateTimeSDKObjectDefinitionType, + }, + Required: true, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "PUT", + RequestObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Model"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Model"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/example"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseModelWithInlinedObject(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "model_with_inlined_object.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Example": { + Models: map[string]sdkModels.SDKModel{ + "Model": { + Fields: map[string]sdkModels.SDKField{ + "Name": { + JsonName: "name", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + "ThingProps": { + JsonName: "thingProps", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + NestedItem: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("ThingProperties"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + Type: sdkModels.ListSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + "ThingProperties": { + Fields: map[string]sdkModels.SDKField{ + "KeyName": { + JsonName: "keyName", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + "UserAssignedIdentities": { + JsonName: "userAssignedIdentities", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + NestedItem: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("UserAssignedIdentitiesProperties"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + Type: sdkModels.DictionarySDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + "UserAssignedIdentitiesProperties": { + Fields: map[string]sdkModels.SDKField{ + "ClientId": { + JsonName: "clientId", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + ReadOnly: true, + Required: false, + }, + "PrincipalId": { + JsonName: "principalId", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + ReadOnly: true, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Model"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/example"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseModelWithNumberPrefixedField(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "model_with_number_prefixed_field.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Example": { + Models: map[string]sdkModels.SDKModel{ + "Model": { + Fields: map[string]sdkModels.SDKField{ + "Name": { + JsonName: "name", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + "Five0PercentDone": { + JsonName: "50PercentDone", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Model"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/example"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseModelWithReference(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "model_with_reference.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Example": { + Models: map[string]sdkModels.SDKModel{ + "Model": { + Fields: map[string]sdkModels.SDKField{ + "Name": { + JsonName: "name", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + "ThingProps": { + JsonName: "thingProps", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + NestedItem: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("ThingProperties"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + Type: sdkModels.ListSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + "ThingProperties": { + Fields: map[string]sdkModels.SDKField{ + "KeyName": { + JsonName: "keyName", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + "Identity": { + JsonName: "identity", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("UserAssignedIdentityProperties"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + "UserAssignedIdentityProperties": { + Fields: map[string]sdkModels.SDKField{ + "UserAssignedIdentity": { + JsonName: "userAssignedIdentity", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Model"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/example"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseModelWithReferenceToArray(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "model_with_reference_array.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Example": { + Models: map[string]sdkModels.SDKModel{ + "Model": { + Fields: map[string]sdkModels.SDKField{ + "Name": { + JsonName: "name", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + "Pets": { + JsonName: "pets", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + NestedItem: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Pet"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + + // TODO: re-enable min/max/unique + // Minimum: pointer.To(1), + // Maximum: pointer.To(2), + // UniqueItems: pointer.To(true), + }, + Type: sdkModels.ListSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + "Pet": { + Fields: map[string]sdkModels.SDKField{ + "Name": { + JsonName: "name", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Model"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/example"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseModelWithReferenceToConstant(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "model_with_reference_constant.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Example": { + Constants: map[string]sdkModels.SDKConstant{ + "AnimalType": { + Type: sdkModels.StringSDKConstantType, + Values: map[string]string{ + "Cat": "Cat", + "Dog": "Dog", + }, + }, + }, + Models: map[string]sdkModels.SDKModel{ + "Model": { + Fields: map[string]sdkModels.SDKField{ + "Name": { + JsonName: "name", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + "ThingProps": { + JsonName: "thingProps", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + NestedItem: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("ThingProperties"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + Type: sdkModels.ListSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + "ThingProperties": { + Fields: map[string]sdkModels.SDKField{ + "Animal": { + JsonName: "animal", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("AnimalType"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + Required: false, + }, + "KeyName": { + JsonName: "keyName", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Model"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/example"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseModelWithReferenceToString(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "model_with_reference_string.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Example": { + Models: map[string]sdkModels.SDKModel{ + "Model": { + Fields: map[string]sdkModels.SDKField{ + "Name": { + JsonName: "name", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + "ThingProps": { + JsonName: "thingProps", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + NestedItem: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("ThingProperties"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + Type: sdkModels.ListSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + "ThingProperties": { + Fields: map[string]sdkModels.SDKField{ + "FullyQualifiedDomainName": { + JsonName: "fullyQualifiedDomainName", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + "KeyName": { + JsonName: "keyName", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Model"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/example"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseModelWithCircularReferences(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "model_with_circular_reference.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Example": { + Models: map[string]sdkModels.SDKModel{ + "Animal": { + Fields: map[string]sdkModels.SDKField{ + "FavouriteHouse": { + JsonName: "favouriteHouse", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("House"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + Required: false, + }, + "FavouriteHuman": { + JsonName: "favouriteHuman", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Human"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + Required: false, + }, + "Name": { + JsonName: "name", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + "House": { + Fields: map[string]sdkModels.SDKField{ + "Address": { + JsonName: "address", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + "Residents": { + JsonName: "residents", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + NestedItem: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Human"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + Type: sdkModels.ListSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + "Human": { + Fields: map[string]sdkModels.SDKField{ + "Pets": { + JsonName: "pets", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + NestedItem: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Animal"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + Type: sdkModels.ListSDKObjectDefinitionType, + }, + Required: false, + }, + "Name": { + JsonName: "name", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("House"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/example"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseModelInheritingFromObjectWithNoExtraFields(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "model_inheriting_from_other_model_no_new_fields.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Example": { + Models: map[string]sdkModels.SDKModel{ + "FirstObject": { + Fields: map[string]sdkModels.SDKField{ + "Name": { + JsonName: "name", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + // whilst the response model references SecondObject, it's only inheriting from FirstObject + // and doesn't contain any new fields, so it should be switched out + ReferenceName: pointer.To("FirstObject"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/example"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseModelInheritingFromObjectWithNoExtraFieldsInlined(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "model_inheriting_from_other_model_no_new_fields_inlined.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Example": { + Models: map[string]sdkModels.SDKModel{ + "FirstObject": { + Fields: map[string]sdkModels.SDKField{ + "Endpoints": { + JsonName: "endpoints", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("SecondObject"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + Required: false, + }, + "Name": { + JsonName: "name", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + "SecondObject": { + Fields: map[string]sdkModels.SDKField{ + "Name": { + JsonName: "name", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("FirstObject"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/example"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseModelInheritingFromObjectWithOnlyDescription(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "model_inheriting_from_other_model_with_only_description.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Example": { + Models: map[string]sdkModels.SDKModel{ + "FirstObject": { + Fields: map[string]sdkModels.SDKField{ + "Name": { + JsonName: "name", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + // whilst the response model references SecondObject, it's only inheriting from FirstObject + // and doesn't contain any new fields, so it should be switched out + ReferenceName: pointer.To("FirstObject"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/example"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseModelInheritingFromObjectWithPropertiesWithinAllOf(t *testing.T) { + // This test ensures that when a Model inherits from a Model and defines properties within + // the `AllOf` field, that the Model isn't flattened into the Parent Model. + // 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) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Example": { + Models: map[string]sdkModels.SDKModel{ + "SecondObject": { + Fields: map[string]sdkModels.SDKField{ + "Name": { + JsonName: "name", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + // SecondObject is referenced as the Response Object, but because it inherits from one Model + // (FirstObject) and uses another (ThirdObject) it shouldn't be flattened into the parent type(s) + // and should instead remain `SecondObject`. + ReferenceName: pointer.To("SecondObject"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/example"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseModelContainingAllOfToTypeObject(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "model_containing_allof_object_type.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Example": { + Models: map[string]sdkModels.SDKModel{ + "Model": { + Fields: map[string]sdkModels.SDKField{ + "Name": { + JsonName: "name", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: true, + }, + "Country": { + JsonName: "country", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: true, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "PUT", + RequestObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Model"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Model"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/example"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseModelContainingAllOfToTypeObjectWithProperties(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "model_containing_allof_object_type_with_properties.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Example": { + Models: map[string]sdkModels.SDKModel{ + "Model": { + Fields: map[string]sdkModels.SDKField{ + "Name": { + JsonName: "name", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + "Country": { + JsonName: "country", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: true, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "PUT", + RequestObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Model"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Model"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/example"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseModelContainingAllOfWithinProperties(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "model_containing_allof_within_properties.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Example": { + Models: map[string]sdkModels.SDKModel{ + "Model": { + Fields: map[string]sdkModels.SDKField{ + "Country": { + JsonName: "country", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: true, + }, + "Properties": { + JsonName: "properties", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("ModelProperties"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + "ModelProperties": { + Fields: map[string]sdkModels.SDKField{ + "MoreNested": { + JsonName: "moreNested", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + "Name": { + JsonName: "name", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: true, + }, + "Nested": { + JsonName: "nested", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "PUT", + RequestObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Model"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Model"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/example"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseModelContainingMultipleAllOfWithinProperties(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "model_containing_allof_within_properties_multiple.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Example": { + Models: map[string]sdkModels.SDKModel{ + "Model": { + Fields: map[string]sdkModels.SDKField{ + "Options": { + JsonName: "options", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("ResourceWithLocation"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + Required: false, + }, + "Resource": { + JsonName: "resource", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("ModelResource"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + "ModelResource": { + Fields: map[string]sdkModels.SDKField{ + "Country": { + JsonName: "country", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + "MoreNested": { + JsonName: "moreNested", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + "Nested": { + JsonName: "nested", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + "ResourceWithLocation": { + Fields: map[string]sdkModels.SDKField{ + "Country": { + JsonName: "country", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: true, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "PUT", + RequestObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Model"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Model"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/example"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseModelContainingLists(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "model_containing_lists.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Example": { + Models: map[string]sdkModels.SDKModel{ + "Model": { + Fields: map[string]sdkModels.SDKField{ + "Animals": { + JsonName: "animals", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + NestedItem: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Animal"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + Type: sdkModels.ListSDKObjectDefinitionType, + }, + Required: false, + }, + "Name": { + JsonName: "name", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: true, + }, + "Plants": { + JsonName: "plants", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + NestedItem: &sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + + // TODO: re-enable min/max/unique + // Maximum: pointer.To(10), + // Minimum: pointer.To(1), + // UniqueItems: pointer.To(true), + }, + Type: sdkModels.ListSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + "Animal": { + Fields: map[string]sdkModels.SDKField{ + "Age": { + JsonName: "age", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.IntegerSDKObjectDefinitionType, + }, + Required: false, + }, + "Name": { + JsonName: "name", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: true, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Model"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/example"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseModelInlinedWithNoName(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "model_inlined_with_no_name.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Example": { + Models: map[string]sdkModels.SDKModel{ + "Container": { + Fields: map[string]sdkModels.SDKField{ + "Planets": { + JsonName: "planets", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + NestedItem: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("ContainerPlanetsInlined"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + Type: sdkModels.ListSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + "ContainerPlanetsInlined": { + Fields: map[string]sdkModels.SDKField{ + "ExampleField": { + JsonName: "exampleField", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Container"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/example"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseModelInheritingFromParent(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "model_inheriting_from_parent.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Example": { + Models: map[string]sdkModels.SDKModel{ + "Model": { + Fields: map[string]sdkModels.SDKField{ + "Age": { + JsonName: "age", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.IntegerSDKObjectDefinitionType, + }, + Required: false, + }, + "Enabled": { + JsonName: "enabled", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.BooleanSDKObjectDefinitionType, + }, + Required: true, + }, + "Height": { + JsonName: "height", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.FloatSDKObjectDefinitionType, + }, + Required: false, + }, + "Name": { + JsonName: "name", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: true, + }, + "Tags": { + JsonName: "tags", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.TagsSDKObjectDefinitionType, + }, + Required: true, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "PUT", + RequestObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Model"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Model"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/example"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseModelMultipleTopLevelModelsAndOperations(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "model_multiple_top_level_models_and_operations.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Example": { + Models: map[string]sdkModels.SDKModel{ + "GetExample": { + Fields: map[string]sdkModels.SDKField{ + "Age": { + JsonName: "age", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.IntegerSDKObjectDefinitionType, + }, + Required: false, + }, + "Enabled": { + JsonName: "enabled", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.BooleanSDKObjectDefinitionType, + }, + Required: false, + }, + "Name": { + JsonName: "name", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: true, + }, + "Tags": { + JsonName: "tags", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.TagsSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + "PutExample": { + Fields: map[string]sdkModels.SDKField{ + "Age": { + JsonName: "age", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.IntegerSDKObjectDefinitionType, + }, + Required: false, + }, + "Enabled": { + JsonName: "enabled", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.BooleanSDKObjectDefinitionType, + }, + Required: false, + }, + "Name": { + JsonName: "name", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: true, + }, + "Tags": { + JsonName: "tags", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.TagsSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Get": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("GetExample"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/example"), + }, + "Put": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "PUT", + RequestObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("PutExample"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("PutExample"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/example"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseModelBug2675DuplicateModel(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "models_bug_2675_duplicate_model.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Example": { + Models: map[string]sdkModels.SDKModel{ + "EnvironmentRole": { + Fields: map[string]sdkModels.SDKField{ + "Description": { + JsonName: "description", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + ReadOnly: true, + Required: false, + }, + "RoleName": { + JsonName: "roleName", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + ReadOnly: true, + Required: false, + }, + }, + }, + "ExampleEnvironment": { + Fields: map[string]sdkModels.SDKField{ + "Location": { + JsonName: "location", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.LocationSDKObjectDefinitionType, + }, + Required: false, + }, + "Properties": { + JsonName: "properties", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("ExampleEnvironmentProperties"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + "ExampleEnvironmentProperties": { + Fields: map[string]sdkModels.SDKField{ + "CreatorRoleAssignment": { + JsonName: "creatorRoleAssignment", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("ExampleEnvironmentUpdatePropertiesCreatorRoleAssignment"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + Required: false, + }, + "DeploymentTargetId": { + JsonName: "deploymentTargetId", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + "ProvisioningState": { + JsonName: "provisioningState", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + ReadOnly: true, + Required: false, + }, + "UserRoleAssignments": { + JsonName: "userRoleAssignments", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + NestedItem: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("UserRoleAssignment"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + Type: sdkModels.DictionarySDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + "ExampleEnvironmentUpdate": { + Fields: map[string]sdkModels.SDKField{ + "Example": { + JsonName: "example", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + "Properties": { + JsonName: "properties", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("ExampleEnvironmentUpdateProperties"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + "ExampleEnvironmentUpdateProperties": { + Fields: map[string]sdkModels.SDKField{ + "CreatorRoleAssignment": { + JsonName: "creatorRoleAssignment", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("ExampleEnvironmentUpdatePropertiesCreatorRoleAssignment"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + Required: false, + }, + "DeploymentTargetId": { + JsonName: "deploymentTargetId", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + "UserRoleAssignments": { + JsonName: "userRoleAssignments", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + NestedItem: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("UserRoleAssignment"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + Type: sdkModels.DictionarySDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + "ExampleEnvironmentUpdatePropertiesCreatorRoleAssignment": { + Fields: map[string]sdkModels.SDKField{ + "Roles": { + JsonName: "roles", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + NestedItem: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("EnvironmentRole"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + Type: sdkModels.DictionarySDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + "UserRoleAssignment": { + Fields: map[string]sdkModels.SDKField{ + "Roles": { + JsonName: "roles", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + NestedItem: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("EnvironmentRole"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + Type: sdkModels.DictionarySDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "CreateOrUpdate": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "PUT", + RequestObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("ExampleEnvironment"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + ResourceIDName: pointer.To("EnvironmentId"), + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("ExampleEnvironment"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + }, + "Get": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResourceIDName: pointer.To("EnvironmentId"), + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("ExampleEnvironment"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + }, + "Update": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "PATCH", + RequestObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("ExampleEnvironmentUpdate"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + ResourceIDName: pointer.To("EnvironmentId"), + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("ExampleEnvironment"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + }, + }, + ResourceIDs: map[string]sdkModels.ResourceID{ + "EnvironmentId": { + Segments: []sdkModels.ResourceIDSegment{ + sdkModels.NewStaticValueResourceIDSegment("staticEnvironments", "environments"), + sdkModels.NewUserSpecifiedResourceIDSegment("environmentName", "environmentName"), + }, + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} diff --git a/tools/importer-rest-api-specs/components/parser/object_definition.go b/tools/importer-rest-api-specs/components/parser/ported_object_definition.go similarity index 94% rename from tools/importer-rest-api-specs/components/parser/object_definition.go rename to tools/importer-rest-api-specs/components/parser/ported_object_definition.go index 9c7507c9c97..a53d5e9c20c 100644 --- a/tools/importer-rest-api-specs/components/parser/object_definition.go +++ b/tools/importer-rest-api-specs/components/parser/ported_object_definition.go @@ -7,22 +7,22 @@ import ( "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/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" + 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" ) -// if `inputForModel` is false, it means the `input` schema cannot be used to parse the model of `modelName` +// if `parsingModel` 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, + known parserModels.ParseResult, parsingModel bool, -) (*sdkModels.SDKObjectDefinition, *internal.ParseResult, error) { +) (*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 := internal.ParseResult{ + result := parserModels.ParseResult{ Constants: map[string]sdkModels.SDKConstant{}, Models: map[string]sdkModels.SDKModel{}, } @@ -64,7 +64,7 @@ func (d *SwaggerDefinition) parseObjectDefinition( return nil, nil, fmt.Errorf("finding top level model %q: %+v", *objectName, err) } - knownIncludingPlaceholder := internal.ParseResult{ + knownIncludingPlaceholder := parserModels.ParseResult{ Constants: map[string]sdkModels.SDKConstant{}, Models: map[string]sdkModels.SDKModel{}, } @@ -226,7 +226,7 @@ func (d *SwaggerDefinition) parseObjectDefinition( return nil, nil, fmt.Errorf("unimplemented object definition") } -func (d *SwaggerDefinition) parseDataFactoryCustomTypes(input *spec.Schema, known internal.ParseResult) (*sdkModels.SDKObjectDefinition, *internal.ParseResult, error) { +func (d *SwaggerDefinition) 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") @@ -377,14 +377,3 @@ func (d *SwaggerDefinition) parseNativeType(input *spec.Schema) *sdkModels.SDKOb 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/ported_operations.go similarity index 95% rename from tools/importer-rest-api-specs/components/parser/operations.go rename to tools/importer-rest-api-specs/components/parser/ported_operations.go index 1279b113953..875c4e30d6e 100644 --- a/tools/importer-rest-api-specs/components/parser/operations.go +++ b/tools/importer-rest-api-specs/components/parser/ported_operations.go @@ -5,17 +5,17 @@ package parser import ( "fmt" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/ignore" "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/internal" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/components/parser/resourceids" "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/components/apidefinitions/parser/resourceids" "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/logging" ) @@ -25,9 +25,9 @@ type operationsParser struct { 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) { +func (d *SwaggerDefinition) parseOperationsWithinTag(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 := internal.ParseResult{ + result := parserModels.ParseResult{ Constants: map[string]sdkModels.SDKConstant{}, Models: map[string]sdkModels.SDKModel{}, } @@ -71,8 +71,8 @@ func (d *SwaggerDefinition) parseOperationsWithinTag(tag *string, operationIdsTo return &operations, &result, nil } -func (p operationsParser) parseOperation(operation parsedOperation, resourceProvider *string) (*sdkModels.SDKOperation, *internal.ParseResult, error) { - result := internal.ParseResult{ +func (p operationsParser) parseOperation(operation parsedOperation, resourceProvider *string) (*sdkModels.SDKOperation, *parserModels.ParseResult, error) { + result := parserModels.ParseResult{ Constants: map[string]sdkModels.SDKConstant{}, Models: map[string]sdkModels.SDKModel{}, } @@ -331,9 +331,9 @@ func (p operationsParser) operationIsLongRunning(input parsedOperation) bool { return val } -func (p operationsParser) optionsForOperation(input parsedOperation) (*map[string]sdkModels.SDKOperationOption, *internal.ParseResult, error) { +func (p operationsParser) optionsForOperation(input parsedOperation) (*map[string]sdkModels.SDKOperationOption, *parserModels.ParseResult, error) { output := make(map[string]sdkModels.SDKOperationOption) - result := internal.ParseResult{ + result := parserModels.ParseResult{ Constants: map[string]sdkModels.SDKConstant{}, } @@ -416,7 +416,7 @@ func (p operationsParser) operationShouldBeIgnored(input sdkModels.SDKOperation) return false } -func (p operationsParser) requestObjectForOperation(input parsedOperation, known internal.ParseResult) (*sdkModels.SDKObjectDefinition, *internal.ParseResult, error) { +func (p operationsParser) requestObjectForOperation(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 @@ -458,9 +458,9 @@ func (p operationsParser) operationIsASuccess(statusCode int, resp spec.Response return statusCode >= 200 && statusCode < 300 } -func (p operationsParser) responseObjectForOperation(input parsedOperation, known internal.ParseResult) (*operationResponseObjectResult, *internal.ParseResult, error) { +func (p operationsParser) responseObjectForOperation(input parsedOperation, known parserModels.ParseResult) (*operationResponseObjectResult, *parserModels.ParseResult, error) { output := operationResponseObjectResult{} - result := internal.ParseResult{ + result := parserModels.ParseResult{ Constants: map[string]sdkModels.SDKConstant{}, Models: map[string]sdkModels.SDKModel{}, } diff --git a/tools/importer-rest-api-specs/components/parser/ported_operations_test.go b/tools/importer-rest-api-specs/components/parser/ported_operations_test.go new file mode 100644 index 00000000000..aa32b57e400 --- /dev/null +++ b/tools/importer-rest-api-specs/components/parser/ported_operations_test.go @@ -0,0 +1,1588 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package parser + +import ( + "net/http" + "testing" + + "github.com/hashicorp/go-azure-helpers/lang/pointer" + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/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) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{}, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseOperationSingleWithTag(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "operations_single_with_tag.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Hello": { + Operations: map[string]sdkModels.SDKOperation{ + "HeadWorld": { + ContentType: "application/json", + ExpectedStatusCodes: []int{http.StatusOK}, + LongRunning: false, + Method: http.MethodHead, + URISuffix: pointer.To("/things"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseOperationSingleWithTagAndResourceId(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "operations_single_with_tag_resource_id.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Hello": { + Operations: map[string]sdkModels.SDKOperation{ + "HeadWorld": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "HEAD", + ResourceIDName: pointer.To("ThingId"), + }, + }, + ResourceIDs: map[string]sdkModels.ResourceID{ + "ThingId": { + Segments: []sdkModels.ResourceIDSegment{ + sdkModels.NewStaticValueResourceIDSegment("staticSubscriptions", "subscriptions"), + sdkModels.NewSubscriptionIDResourceIDSegment("subscriptionId"), + sdkModels.NewStaticValueResourceIDSegment("staticResourceGroups", "resourceGroups"), + sdkModels.NewResourceGroupNameResourceIDSegment("resourceGroupName"), + sdkModels.NewStaticValueResourceIDSegment("staticProviders", "providers"), + sdkModels.NewResourceProviderResourceIDSegment("staticMicrosoftFooBar", "Microsoft.FooBar"), + sdkModels.NewStaticValueResourceIDSegment("staticThings", "things"), + sdkModels.NewUserSpecifiedResourceIDSegment("thing", "thing"), + }, + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseOperationSingleWithTagAndResourceIdSuffix(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "operations_single_with_tag_resource_id_suffix.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Hello": { + Operations: map[string]sdkModels.SDKOperation{ + "HeadWorld": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "HEAD", + ResourceIDName: pointer.To("ThingId"), + URISuffix: pointer.To("/restart"), + }, + }, + ResourceIDs: map[string]sdkModels.ResourceID{ + "ThingId": { + Segments: []sdkModels.ResourceIDSegment{ + sdkModels.NewStaticValueResourceIDSegment("staticSubscriptions", "subscriptions"), + sdkModels.NewSubscriptionIDResourceIDSegment("subscriptionId"), + sdkModels.NewStaticValueResourceIDSegment("staticResourceGroups", "resourceGroups"), + sdkModels.NewResourceGroupNameResourceIDSegment("resourceGroupName"), + sdkModels.NewStaticValueResourceIDSegment("staticProviders", "providers"), + sdkModels.NewResourceProviderResourceIDSegment("staticMicrosoftFooBar", "Microsoft.FooBar"), + sdkModels.NewStaticValueResourceIDSegment("staticThings", "things"), + sdkModels.NewUserSpecifiedResourceIDSegment("thing", "thing"), + }, + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseOperationSingleWithRequestObject(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "operations_single_with_request_object.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Hello": { + Models: map[string]sdkModels.SDKModel{ + "Example": { + Fields: map[string]sdkModels.SDKField{ + "Name": { + JsonName: "name", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "PutWorld": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "PUT", + RequestObject: &sdkModels.SDKObjectDefinition{ + Type: sdkModels.ReferenceSDKObjectDefinitionType, + ReferenceName: pointer.To("Example"), + }, + URISuffix: pointer.To("/things"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseOperationSingleWithRequestObjectInlined(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "operations_single_with_request_object_inlined.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Hello": { + Models: map[string]sdkModels.SDKModel{ + "Example": { + Fields: map[string]sdkModels.SDKField{ + "Name": { + JsonName: "name", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "PutWorld": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "PUT", + RequestObject: &sdkModels.SDKObjectDefinition{ + Type: sdkModels.ReferenceSDKObjectDefinitionType, + ReferenceName: pointer.To("Example"), + }, + URISuffix: pointer.To("/things"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseOperationSingleWithResponseObject(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "operations_single_with_response_object.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Hello": { + Models: map[string]sdkModels.SDKModel{ + "Example": { + Fields: map[string]sdkModels.SDKField{ + "Name": { + JsonName: "name", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "GetWorld": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + Type: sdkModels.ReferenceSDKObjectDefinitionType, + ReferenceName: pointer.To("Example"), + }, + URISuffix: pointer.To("/things"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseOperationSingleWithResponseObjectInlined(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "operations_single_with_response_object_inlined.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Hello": { + Models: map[string]sdkModels.SDKModel{ + "Example": { + Fields: map[string]sdkModels.SDKField{ + "Name": { + JsonName: "name", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "GetWorld": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + Type: sdkModels.ReferenceSDKObjectDefinitionType, + ReferenceName: pointer.To("Example"), + }, + URISuffix: pointer.To("/things"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseOperationSingleWithResponseObjectInlinedList(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "operations_single_with_response_object_inlined_list.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Hello": { + Models: map[string]sdkModels.SDKModel{ + "Example": { + Fields: map[string]sdkModels.SDKField{ + "Name": { + JsonName: "name", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "GetWorld": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + Type: sdkModels.ListSDKObjectDefinitionType, + NestedItem: &sdkModels.SDKObjectDefinition{ + Type: sdkModels.ReferenceSDKObjectDefinitionType, + ReferenceName: pointer.To("Example"), + }, + }, + URISuffix: pointer.To("/things"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseOperationSingleRequestingWithABool(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "operations_single_requesting_with_a_bool.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Hello": { + Operations: map[string]sdkModels.SDKOperation{ + "PutWorld": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "PUT", + RequestObject: &sdkModels.SDKObjectDefinition{ + Type: sdkModels.BooleanSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/things"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseOperationSingleRequestingWithAInteger(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "operations_single_requesting_with_a_int.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Hello": { + Operations: map[string]sdkModels.SDKOperation{ + "PutWorld": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "PUT", + RequestObject: &sdkModels.SDKObjectDefinition{ + Type: sdkModels.IntegerSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/things"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseOperationSingleRequestingWithADictionaryOfStrings(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "operations_single_requesting_with_a_dictionary_of_strings.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Hello": { + Operations: map[string]sdkModels.SDKOperation{ + "PutWorld": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "PUT", + RequestObject: &sdkModels.SDKObjectDefinition{ + Type: sdkModels.DictionarySDKObjectDefinitionType, + NestedItem: &sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + }, + URISuffix: pointer.To("/things"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseOperationSingleRequestingWithAListOfStrings(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "operations_single_requesting_with_a_list_of_strings.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Hello": { + Operations: map[string]sdkModels.SDKOperation{ + "PutWorld": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "PUT", + RequestObject: &sdkModels.SDKObjectDefinition{ + Type: sdkModels.ListSDKObjectDefinitionType, + NestedItem: &sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + }, + URISuffix: pointer.To("/things"), + }, + }, + }, + }, + } + 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) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Hello": { + Operations: map[string]sdkModels.SDKOperation{ + "PutWorld": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "PUT", + RequestObject: &sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/things"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseOperationSingleReturningABool(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "operations_single_returning_a_bool.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Hello": { + Operations: map[string]sdkModels.SDKOperation{ + "GimmeABoolean": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + Type: sdkModels.BooleanSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/worlds/favourite"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseOperationSingleReturningAFloat(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "operations_single_returning_a_float.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Hello": { + Operations: map[string]sdkModels.SDKOperation{ + "GimmeAFloat": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + Type: sdkModels.FloatSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/worlds/favourite"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseOperationSingleReturningAFile(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "operations_single_returning_a_file.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Hello": { + Operations: map[string]sdkModels.SDKOperation{ + "GimmeAFile": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + Type: sdkModels.RawFileSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/worlds/favourite"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseOperationSingleReturningAnInteger(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "operations_single_returning_an_integer.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Hello": { + Operations: map[string]sdkModels.SDKOperation{ + "GimmeAnInteger": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + Type: sdkModels.IntegerSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/worlds/favourite"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseOperationSingleReturningAString(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "operations_single_returning_a_string.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Hello": { + Operations: map[string]sdkModels.SDKOperation{ + "GimmeAString": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/worlds/favourite"), + }, + }, + }, + }, + } + 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) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Hello": { + Operations: map[string]sdkModels.SDKOperation{ + "GimmeAString": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/worlds/favourite"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseOperationSingleReturningATopLevelRawObject(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "operations_single_returning_a_top_level_raw_object.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Hello": { + Operations: map[string]sdkModels.SDKOperation{ + "RawObjectToMeToYou": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + RequestObject: &sdkModels.SDKObjectDefinition{ + Type: sdkModels.RawObjectSDKObjectDefinitionType, + }, + ResponseObject: &sdkModels.SDKObjectDefinition{ + Type: sdkModels.RawObjectSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/chuckle"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseOperationSingleReturningADictionaryOfAModel(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "operations_single_returning_a_dictionary_of_model.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Hello": { + Models: map[string]sdkModels.SDKModel{ + "Person": { + Fields: map[string]sdkModels.SDKField{ + "Name": { + JsonName: "name", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "GimmeADictionaryOfAModel": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + Type: sdkModels.DictionarySDKObjectDefinitionType, + NestedItem: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Person"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + }, + URISuffix: pointer.To("/worlds/favourite"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseOperationSingleReturningADictionaryOfStrings(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "operations_single_returning_a_dictionary_of_strings.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Hello": { + Operations: map[string]sdkModels.SDKOperation{ + "GimmeADictionaryOfStrings": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + Type: sdkModels.DictionarySDKObjectDefinitionType, + NestedItem: &sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + }, + URISuffix: pointer.To("/worlds/favourite"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseOperationSingleReturningAListOfIntegers(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "operations_single_returning_a_list_of_ints.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Hello": { + Operations: map[string]sdkModels.SDKOperation{ + "GimmeAListOfIntegers": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + Type: sdkModels.ListSDKObjectDefinitionType, + NestedItem: &sdkModels.SDKObjectDefinition{ + Type: sdkModels.IntegerSDKObjectDefinitionType, + }, + }, + URISuffix: pointer.To("/worlds/favourite"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseOperationSingleReturningAListOfAModel(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "operations_single_returning_a_list_of_model.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Hello": { + Models: map[string]sdkModels.SDKModel{ + "Person": { + Fields: map[string]sdkModels.SDKField{ + "Name": { + JsonName: "name", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "GimmeAListOfModels": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + Type: sdkModels.ListSDKObjectDefinitionType, + NestedItem: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Person"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + }, + URISuffix: pointer.To("/worlds/favourite"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseOperationSingleReturningAListOfStrings(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "operations_single_returning_a_list_of_strings.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Hello": { + Operations: map[string]sdkModels.SDKOperation{ + "GimmeAListOfStrings": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + Type: sdkModels.ListSDKObjectDefinitionType, + NestedItem: &sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + }, + URISuffix: pointer.To("/worlds/favourite"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseOperationSingleReturningAListOfListOfAModel(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "operations_single_returning_a_list_of_list_of_model.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Hello": { + Models: map[string]sdkModels.SDKModel{ + "Person": { + Fields: map[string]sdkModels.SDKField{ + "Name": { + JsonName: "name", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "GimmeAListOfListOfModels": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + Type: sdkModels.ListSDKObjectDefinitionType, + NestedItem: &sdkModels.SDKObjectDefinition{ + Type: sdkModels.ListSDKObjectDefinitionType, + NestedItem: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Person"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + }, + }, + URISuffix: pointer.To("/worlds/favourite"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseOperationSingleReturningAListOfListOfStrings(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "operations_single_returning_a_list_of_list_of_strings.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Hello": { + Operations: map[string]sdkModels.SDKOperation{ + "GimmeAListOfListOfStrings": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + Type: sdkModels.ListSDKObjectDefinitionType, + NestedItem: &sdkModels.SDKObjectDefinition{ + Type: sdkModels.ListSDKObjectDefinitionType, + NestedItem: &sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + }, + }, + URISuffix: pointer.To("/worlds/favourite"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseOperationSingleWithList(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "operations_single_list.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Hello": { + Models: map[string]sdkModels.SDKModel{ + "World": { + Fields: map[string]sdkModels.SDKField{ + "Name": { + JsonName: "name", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "ListWorlds": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + FieldContainingPaginationDetails: pointer.To("nextLink"), + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("World"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/worlds"), + }, + }, + }, + }, + } + 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) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Hello": { + Models: map[string]sdkModels.SDKModel{ + "World": { + Fields: map[string]sdkModels.SDKField{ + "Name": { + JsonName: "name", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "ListWorlds": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + // This signifies there's a list of items without a means of paging over it. + // This is _likely_ a badly documented API Definition, but it's hard to say for sure. + // Since there isn't a pagination field present in the response, we have to assume this isn't + // a list operation, even if there's a `skipToken` present. + FieldContainingPaginationDetails: nil, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("World"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/worlds"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseOperationSingleWithListReturningAListOfStrings(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "operations_single_list_of_strings.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Hello": { + Operations: map[string]sdkModels.SDKOperation{ + "ListWorlds": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + FieldContainingPaginationDetails: pointer.To("nextLink"), + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/worlds"), + }, + }, + }, + }, + } + 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) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Hello": { + Models: map[string]sdkModels.SDKModel{ + "World": { + Fields: map[string]sdkModels.SDKField{ + "Name": { + JsonName: "name", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "ListWorlds": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + FieldContainingPaginationDetails: pointer.To("nextLink"), + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("World"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/worlds"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseOperationSingleWithLongRunningOperation(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "operations_single_long_running.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Hello": { + Models: map[string]sdkModels.SDKModel{ + "Example": { + Fields: map[string]sdkModels.SDKField{ + "Name": { + JsonName: "name", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "PutWorld": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + LongRunning: true, + Method: "PUT", + RequestObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Example"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/things"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseOperationSingleWithRequestAndResponseObject(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "operations_single_with_request_and_response_object.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Hello": { + Models: map[string]sdkModels.SDKModel{ + "Example": { + Fields: map[string]sdkModels.SDKField{ + "Name": { + JsonName: "name", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "PutWorld": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "PUT", + RequestObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Example"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Example"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/things"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseOperationSingleWithMultipleTags(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "operations_single_multiple_tags.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Hello": { + Operations: map[string]sdkModels.SDKOperation{ + "HeadThings": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "HEAD", + URISuffix: pointer.To("/things"), + }, + }, + }, + "Other": { + Operations: map[string]sdkModels.SDKOperation{ + // Whilst the operation name should be `HeadThings`, since this is another Tag + // it's intentionally prefixed for when things cross boundaries (to avoid conflicts) + "HelloHeadThings": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "HEAD", + URISuffix: pointer.To("/things"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseOperationSingleWithInferredTag(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "operations_single_with_no_tag.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + 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": { + Operations: map[string]sdkModels.SDKOperation{ + // since the prefix doesn't match the Tag (since no tag) this gets a combined name + "HelloHeadWorld": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "HEAD", + URISuffix: pointer.To("/things"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseOperationSingleWithHeaderOptions(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "operations_single_with_header_options.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Hello": { + Operations: map[string]sdkModels.SDKOperation{ + "HeadWorld": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "HEAD", + Options: map[string]sdkModels.SDKOperationOption{ + "BoolValue": { + HeaderName: pointer.To("boolValue"), + ObjectDefinition: sdkModels.SDKOperationOptionObjectDefinition{ + Type: sdkModels.BooleanSDKOperationOptionObjectDefinitionType, + }, + Required: true, + }, + "CsvOfDoubleValue": { + HeaderName: pointer.To("csvOfDoubleValue"), + ObjectDefinition: sdkModels.SDKOperationOptionObjectDefinition{ + Type: sdkModels.CSVSDKOperationOptionObjectDefinitionType, + NestedItem: &sdkModels.SDKOperationOptionObjectDefinition{ + Type: sdkModels.FloatSDKOperationOptionObjectDefinitionType, + }, + }, + Required: true, + }, + "CsvOfStringValue": { + HeaderName: pointer.To("csvOfStringValue"), + ObjectDefinition: sdkModels.SDKOperationOptionObjectDefinition{ + Type: sdkModels.CSVSDKOperationOptionObjectDefinitionType, + NestedItem: &sdkModels.SDKOperationOptionObjectDefinition{ + Type: sdkModels.StringSDKOperationOptionObjectDefinitionType, + }, + }, + Required: true, + }, + "DecimalValue": { + HeaderName: pointer.To("decimalValue"), + ObjectDefinition: sdkModels.SDKOperationOptionObjectDefinition{ + Type: sdkModels.FloatSDKOperationOptionObjectDefinitionType, + }, + Required: true, + }, + "DoubleValue": { + HeaderName: pointer.To("doubleValue"), + ObjectDefinition: sdkModels.SDKOperationOptionObjectDefinition{ + Type: sdkModels.FloatSDKOperationOptionObjectDefinitionType, + }, + Required: true, + }, + "IntValue": { + HeaderName: pointer.To("intValue"), + ObjectDefinition: sdkModels.SDKOperationOptionObjectDefinition{ + Type: sdkModels.IntegerSDKOperationOptionObjectDefinitionType, + }, + Required: true, + }, + "StringValue": { + HeaderName: pointer.To("stringValue"), + ObjectDefinition: sdkModels.SDKOperationOptionObjectDefinition{ + Type: sdkModels.StringSDKOperationOptionObjectDefinitionType, + }, + Required: true, + }, + }, + URISuffix: pointer.To("/things"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseOperationSingleWithQueryStringOptions(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "operations_single_with_querystring_options.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Hello": { + Operations: map[string]sdkModels.SDKOperation{ + "HeadWorld": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "HEAD", + Options: map[string]sdkModels.SDKOperationOption{ + "BoolValue": { + QueryStringName: pointer.To("boolValue"), + ObjectDefinition: sdkModels.SDKOperationOptionObjectDefinition{ + Type: sdkModels.BooleanSDKOperationOptionObjectDefinitionType, + }, + Required: true, + }, + "CsvOfDoubleValue": { + QueryStringName: pointer.To("csvOfDoubleValue"), + ObjectDefinition: sdkModels.SDKOperationOptionObjectDefinition{ + Type: sdkModels.CSVSDKOperationOptionObjectDefinitionType, + NestedItem: &sdkModels.SDKOperationOptionObjectDefinition{ + Type: sdkModels.FloatSDKOperationOptionObjectDefinitionType, + }, + }, + Required: true, + }, + "CsvOfStringValue": { + QueryStringName: pointer.To("csvOfStringValue"), + ObjectDefinition: sdkModels.SDKOperationOptionObjectDefinition{ + Type: sdkModels.CSVSDKOperationOptionObjectDefinitionType, + NestedItem: &sdkModels.SDKOperationOptionObjectDefinition{ + Type: sdkModels.StringSDKOperationOptionObjectDefinitionType, + }, + }, + Required: true, + }, + "DecimalValue": { + QueryStringName: pointer.To("decimalValue"), + ObjectDefinition: sdkModels.SDKOperationOptionObjectDefinition{ + Type: sdkModels.FloatSDKOperationOptionObjectDefinitionType, + }, + Required: true, + }, + "DoubleValue": { + QueryStringName: pointer.To("doubleValue"), + ObjectDefinition: sdkModels.SDKOperationOptionObjectDefinition{ + Type: sdkModels.FloatSDKOperationOptionObjectDefinitionType, + }, + Required: true, + }, + "IntValue": { + QueryStringName: pointer.To("intValue"), + ObjectDefinition: sdkModels.SDKOperationOptionObjectDefinition{ + Type: sdkModels.IntegerSDKOperationOptionObjectDefinitionType, + }, + Required: true, + }, + "StringValue": { + QueryStringName: pointer.To("stringValue"), + ObjectDefinition: sdkModels.SDKOperationOptionObjectDefinition{ + Type: sdkModels.StringSDKOperationOptionObjectDefinitionType, + }, + Required: true, + }, + }, + URISuffix: pointer.To("/things"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseOperationMultipleBasedOnTheSameResourceId(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "operations_multiple_same_resource_id.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Hello": { + Operations: map[string]sdkModels.SDKOperation{ + "HeadWorld": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "HEAD", + ResourceIDName: pointer.To("ThingId"), + }, + "RestartWorld": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "HEAD", + ResourceIDName: pointer.To("ThingId"), + URISuffix: pointer.To("/restart"), + }, + }, + ResourceIDs: map[string]sdkModels.ResourceID{ + "ThingId": { + Segments: []sdkModels.ResourceIDSegment{ + sdkModels.NewStaticValueResourceIDSegment("staticSubscriptions", "subscriptions"), + sdkModels.NewSubscriptionIDResourceIDSegment("subscriptionId"), + sdkModels.NewStaticValueResourceIDSegment("staticResourceGroups", "resourceGroups"), + sdkModels.NewResourceGroupNameResourceIDSegment("resourceGroupName"), + sdkModels.NewStaticValueResourceIDSegment("staticProviders", "providers"), + sdkModels.NewResourceProviderResourceIDSegment("staticMicrosoftFooBar", "Microsoft.FooBar"), + sdkModels.NewStaticValueResourceIDSegment("staticThings", "things"), + sdkModels.NewUserSpecifiedResourceIDSegment("thing", "thing"), + }, + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseOperationsContainingContentTypes(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "operation_content_types.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Hello": { + Operations: map[string]sdkModels.SDKOperation{ + "Default": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "HEAD", + URISuffix: pointer.To("/default"), + }, + "JsonRequest": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + RequestObject: &sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + // this can become `/json-request` when https://github.com/hashicorp/pandora/issues/3807 is fixed + URISuffix: pointer.To("/jsonRequest"), + }, + "JsonResponse": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "PUT", + ResponseObject: &sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/jsonResponse"), + }, + "XmlRequest": { + ContentType: "application/xml", + ExpectedStatusCodes: []int{200}, + Method: "GET", + RequestObject: &sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/xmlRequest"), + }, + "XmlResponse": { + ContentType: "application/xml", + ExpectedStatusCodes: []int{200}, + Method: "PUT", + ResponseObject: &sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/xmlResponse"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseOperationContainingMultipleReturnObjects(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "operations_single_multiple_return_objects.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Hello": { + Models: map[string]sdkModels.SDKModel{ + "FirstModel": { + Fields: map[string]sdkModels.SDKField{ + "Hello": { + JsonName: "hello", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "HeadWorld": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200, 202}, + Method: "PUT", + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("FirstModel"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/things"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseOperationsWithStutteringNames(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "operations_with_stuttering_names.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "ExampleTag": { + Operations: map[string]sdkModels.SDKOperation{ + "Mars": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "HEAD", + URISuffix: pointer.To("/mars"), + }, + "There": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "HEAD", + URISuffix: pointer.To("/there"), + }, + "World": { + // TODO: change the Swagger Tag to be `ExampleTag` for this operation + // There's a bug where multiple operations using the same (normalized) Swagger Tag aren't being + // flattened correctly. + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "HEAD", + URISuffix: pointer.To("/world"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} diff --git a/tools/importer-rest-api-specs/components/parser/parser.go b/tools/importer-rest-api-specs/components/parser/ported_parser.go similarity index 60% rename from tools/importer-rest-api-specs/components/parser/parser.go rename to tools/importer-rest-api-specs/components/parser/ported_parser.go index 7b5660c585c..59d3f3ab1b1 100644 --- a/tools/importer-rest-api-specs/components/parser/parser.go +++ b/tools/importer-rest-api-specs/components/parser/ported_parser.go @@ -8,15 +8,15 @@ import ( "strings" sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/components/parser/resourceids" "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/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" 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) + apiResources := make(map[string]sdkModels.APIResource) tags := d.findTags() // first we assume everything has a tag @@ -34,7 +34,7 @@ func (d *SwaggerDefinition) parse(serviceName, apiVersion string, resourceProvid logging.Tracef("The Tag %q has %d API Operations", tag, len(resource.Operations)) normalizedTag := cleanup.NormalizeTag(tag) normalizedTag = cleanup.NormalizeResourceName(normalizedTag) - resources[normalizedTag] = *resource + apiResources[normalizedTag] = *resource } } @@ -53,19 +53,19 @@ func (d *SwaggerDefinition) parse(serviceName, apiVersion string, resourceProvid normalizedTag := cleanup.NormalizeTag(inferredTag) normalizedTag = cleanup.NormalizeResourceName(normalizedTag) - if mergeResources, ok := resources[normalizedTag]; ok { - resources[normalizedTag] = importerModels.MergeResourcesForTag(mergeResources, *resource) + if mergeResources, ok := apiResources[normalizedTag]; ok { + apiResources[normalizedTag] = importerModels.MergeResourcesForTag(mergeResources, *resource) } else { - resources[normalizedTag] = *resource + apiResources[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 { + for resourceName, resource := range apiResources { logging.Tracef("Simplifying operation names for resource %q", resourceName) - updated := d.simplifyOperationNamesForResource(resource, resourceName) + updated := cleanup.SimplifyOperationNamesForAPIResource(resourceName, resource) resourcesOut[resourceName] = updated } @@ -73,7 +73,7 @@ func (d *SwaggerDefinition) parse(serviceName, apiVersion string, resourceProvid // 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 len(apiResources) == 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) @@ -91,8 +91,8 @@ func (d *SwaggerDefinition) parse(serviceName, apiVersion string, resourceProvid } resource = cleanup.NormalizeAPIResource(resource) - if mergeResources, ok := resources[normalizedTag]; ok { - resources[normalizedTag] = importerModels.MergeResourcesForTag(mergeResources, resource) + if mergeResources, ok := apiResources[normalizedTag]; ok { + apiResources[normalizedTag] = importerModels.MergeResourcesForTag(mergeResources, resource) } else { resourcesOut[normalizedTag] = resource } @@ -105,43 +105,3 @@ func (d *SwaggerDefinition) parse(serviceName, apiVersion string, resourceProvid 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 -} diff --git a/tools/importer-rest-api-specs/components/parser/refactor_glue_logic.go b/tools/importer-rest-api-specs/components/parser/ported_refactor_glue_logic.go similarity index 100% rename from tools/importer-rest-api-specs/components/parser/refactor_glue_logic.go rename to tools/importer-rest-api-specs/components/parser/ported_refactor_glue_logic.go diff --git a/tools/importer-rest-api-specs/components/parser/ported_resource_ids.go b/tools/importer-rest-api-specs/components/parser/ported_resource_ids.go new file mode 100644 index 00000000000..8451f6cfa64 --- /dev/null +++ b/tools/importer-rest-api-specs/components/parser/ported_resource_ids.go @@ -0,0 +1,21 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package parser + +import ( + "fmt" + + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids" +) + +func (d *SwaggerDefinition) ParseResourceIds() (*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 +} diff --git a/tools/importer-rest-api-specs/components/parser/resource_ids_test.go b/tools/importer-rest-api-specs/components/parser/ported_resource_ids_test.go similarity index 93% rename from tools/importer-rest-api-specs/components/parser/resource_ids_test.go rename to tools/importer-rest-api-specs/components/parser/ported_resource_ids_test.go index bf7a41dac03..a86008582d3 100644 --- a/tools/importer-rest-api-specs/components/parser/resource_ids_test.go +++ b/tools/importer-rest-api-specs/components/parser/ported_resource_ids_test.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" ) func TestParseResourceIdBasic(t *testing.T) { @@ -17,9 +16,8 @@ func TestParseResourceIdBasic(t *testing.T) { 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{ @@ -56,9 +54,8 @@ func TestParseResourceIdContainingAConstant(t *testing.T) { 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{ @@ -103,9 +100,8 @@ func TestParseResourceIdContainingAScope(t *testing.T) { 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{ @@ -148,9 +144,8 @@ func TestParseResourceIdContainingAHiddenScope(t *testing.T) { 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{ @@ -184,9 +179,8 @@ func TestParseResourceIdContainingAHiddenScopeWithExtraSegment(t *testing.T) { 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{ @@ -217,9 +211,8 @@ func TestParseResourceIdContainingAHiddenScopeWithSuffix(t *testing.T) { 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{ @@ -260,9 +253,8 @@ func TestParseResourceIdContainingAHiddenScopeNested(t *testing.T) { 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{ @@ -295,9 +287,8 @@ func TestParseResourceIdContainingAHiddenScopeNestedWithExtraSegment(t *testing. 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{ @@ -328,9 +319,8 @@ func TestParseResourceIdContainingAHiddenScopeNestedWithSuffix(t *testing.T) { 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{ @@ -362,9 +352,8 @@ func TestParseResourceIdWithJustUriSuffix(t *testing.T) { 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{ @@ -387,9 +376,8 @@ func TestParseResourceIdWithResourceIdAndUriSuffix(t *testing.T) { 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{ @@ -427,9 +415,8 @@ func TestParseResourceIdWithResourceIdAndUriSuffixForMultipleUris(t *testing.T) 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{ @@ -480,9 +467,8 @@ func TestParseResourceIdContainingResourceProviderShouldGetTitleCased(t *testing 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{ @@ -519,9 +505,8 @@ func TestParseResourceIdContainingTheSameResourceIdWithDifferentSegments(t *test 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{ @@ -565,9 +550,8 @@ func TestParseResourceIdContainingTheSegmentsNamedTheSame(t *testing.T) { 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{ @@ -618,9 +602,8 @@ func TestParseResourceIdsWhereTheSameUriContainsDifferentConstantValuesPerOperat 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{ @@ -679,9 +662,8 @@ func TestParseResourceIdsCommon(t *testing.T) { 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{ diff --git a/tools/importer-rest-api-specs/components/parser/swagger_resources.go b/tools/importer-rest-api-specs/components/parser/ported_swagger_resources.go similarity index 93% rename from tools/importer-rest-api-specs/components/parser/swagger_resources.go rename to tools/importer-rest-api-specs/components/parser/ported_swagger_resources.go index 50b7e9ea652..74457a6ed24 100644 --- a/tools/importer-rest-api-specs/components/parser/swagger_resources.go +++ b/tools/importer-rest-api-specs/components/parser/ported_swagger_resources.go @@ -5,21 +5,22 @@ package parser import ( "fmt" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/operation" "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/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/components/apidefinitions/parser/cleanup" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema" "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/components/apidefinitions/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{ + result := parserModels.ParseResult{ Constants: map[string]sdkModels.SDKConstant{}, Models: map[string]sdkModels.SDKModel{}, } @@ -38,7 +39,7 @@ func (d *SwaggerDefinition) parseResourcesWithinSwaggerTag(tag *string, resource return nil, fmt.Errorf("appending nestedResult from Operations: %+v", err) } - // pull out all of the remaining models based on what we've got + // pull out each 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) @@ -48,7 +49,7 @@ func (d *SwaggerDefinition) parseResourcesWithinSwaggerTag(tag *string, resource } // 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) + operations, err = operation.RemoveWrapperModelForListOperations(*operations, result) if err != nil { return nil, fmt.Errorf("pulling out model from list operations: %+v", err) } @@ -74,8 +75,8 @@ func (d *SwaggerDefinition) parseResourcesWithinSwaggerTag(tag *string, resource return &resource, nil } -func (d *SwaggerDefinition) findNestedItemsYetToBeParsed(operations map[string]sdkModels.SDKOperation, known internal.ParseResult) (*internal.ParseResult, error) { - result := internal.ParseResult{ +func (d *SwaggerDefinition) 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{}, } @@ -124,7 +125,7 @@ func (d *SwaggerDefinition) findNestedItemsYetToBeParsed(operations map[string]s return &result, nil } -func (d *SwaggerDefinition) determineObjectsRequiredButNotParsed(operations map[string]sdkModels.SDKOperation, known internal.ParseResult) (*[]string, error) { +func (d *SwaggerDefinition) determineObjectsRequiredButNotParsed(operations map[string]sdkModels.SDKOperation, known parserModels.ParseResult) (*[]string, error) { referencesToFind := make(map[string]struct{}, 0) var objectsRequiredByModel = func(modelName string, model sdkModels.SDKModel) (*[]string, error) { diff --git a/tools/importer-rest-api-specs/components/parser/swagger_tags.go b/tools/importer-rest-api-specs/components/parser/ported_swagger_tags.go similarity index 100% rename from tools/importer-rest-api-specs/components/parser/swagger_tags.go rename to tools/importer-rest-api-specs/components/parser/ported_swagger_tags.go diff --git a/tools/importer-rest-api-specs/components/parser/ported_swagger_tags_test.go b/tools/importer-rest-api-specs/components/parser/ported_swagger_tags_test.go new file mode 100644 index 00000000000..972efb13d84 --- /dev/null +++ b/tools/importer-rest-api-specs/components/parser/ported_swagger_tags_test.go @@ -0,0 +1,175 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package parser + +import ( + "testing" + + "github.com/hashicorp/go-azure-helpers/lang/pointer" + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" +) + +func TestParsingOperationsUsingTheSameSwaggerTagInDifferentCasings(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "operations_single_tag_different_casing.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Hello": { + Models: map[string]sdkModels.SDKModel{ + "Example": { + Fields: map[string]sdkModels.SDKField{ + "Name": { + JsonName: "name", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + + "First": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "PUT", + RequestObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Example"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + // https://github.com/hashicorp/pandora/issues/3807 + URISuffix: pointer.To("/someotheruri"), + }, + "PutBar": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "PUT", + RequestObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Example"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/bar"), + }, + "PutFoo": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "PUT", + RequestObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Example"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/foo"), + }, + "Second": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "PATCH", + RequestObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Example"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + // https://github.com/hashicorp/pandora/issues/3807 + URISuffix: pointer.To("/someotheruri"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParsingOperationsOnResources(t *testing.T) { + actual, err := ParseSwaggerFileForTesting(t, "operations_on_resources.json", nil) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Hello": { + Models: map[string]sdkModels.SDKModel{ + "Example": { + Fields: map[string]sdkModels.SDKField{ + "Name": { + JsonName: "name", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "First": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "PUT", + RequestObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Example"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + // https://github.com/hashicorp/pandora/issues/3807 + URISuffix: pointer.To("/someotheruri"), + }, + "PutBar": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "PUT", + RequestObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Example"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/bar"), + }, + "Second": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "PATCH", + RequestObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Example"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + // https://github.com/hashicorp/pandora/issues/3807 + URISuffix: pointer.To("/someotheruri"), + }, + }, + }, + "HelloOperations": { + Models: map[string]sdkModels.SDKModel{ + "Example": { + Fields: map[string]sdkModels.SDKField{ + "Name": { + JsonName: "name", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "HelloRestart": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "POST", + RequestObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Example"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/foo"), + }, + }, + }, + }, + } + validateParsedSwaggerResultMatches(t, expected, actual) +} diff --git a/tools/importer-rest-api-specs/components/parser/resource_ids.go b/tools/importer-rest-api-specs/components/parser/resource_ids.go deleted file mode 100644 index 1c5a0df8e6d..00000000000 --- a/tools/importer-rest-api-specs/components/parser/resource_ids.go +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package parser - -import ( - "fmt" - - "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/resourceids" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/logging" -) - -func (d *SwaggerDefinition) ParseResourceIds() (*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 -} diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_management_group_test.go b/tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_management_group_test.go deleted file mode 100644 index 0016702859c..00000000000 --- a/tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_management_group_test.go +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package commonids - -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/commonids/common_id_resource_group_test.go b/tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_resource_group_test.go deleted file mode 100644 index f001ab73761..00000000000 --- a/tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_resource_group_test.go +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package commonids - -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/commonids/common_id_scope_test.go b/tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_scope_test.go deleted file mode 100644 index 6739f618047..00000000000 --- a/tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_scope_test.go +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package commonids - -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/commonids/common_id_subscription_test.go b/tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_subscription_test.go deleted file mode 100644 index 1e131dd0192..00000000000 --- a/tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_subscription_test.go +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package commonids - -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/commonids/common_id_user_assigned_identity_test.go b/tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_user_assigned_identity_test.go deleted file mode 100644 index 25c0c426c5c..00000000000 --- a/tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_user_assigned_identity_test.go +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package commonids - -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/load_and_parse.go b/tools/importer-rest-api-specs/components/parser/rewritten_load_and_parse.go similarity index 69% rename from tools/importer-rest-api-specs/components/parser/load_and_parse.go rename to tools/importer-rest-api-specs/components/parser/rewritten_load_and_parse.go index ce56f1d73f3..d8780f06cd5 100644 --- a/tools/importer-rest-api-specs/components/parser/load_and_parse.go +++ b/tools/importer-rest-api-specs/components/parser/rewritten_load_and_parse.go @@ -5,21 +5,23 @@ package parser import ( "fmt" + "sort" + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" - "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/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/dataworkarounds" "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" 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) { +func LoadAndParseFiles(directory string, fileNames []string, serviceName, apiVersion string, resourceProvider *string) (*sdkModels.APIVersion, error) { // Some Services have been deprecated or should otherwise be ignored - check before proceeding if ignore.Services(serviceName) { logging.Debugf("Service %q should be ignored - skipping", serviceName) - return &importerModels.AzureApiDefinition{}, nil + return nil, nil } // First go through and parse all of the Resource ID's across all of the files @@ -44,7 +46,8 @@ func LoadAndParseFiles(directory string, fileNames []string, serviceName, apiVer } } - parsed := make(map[string]importerModels.AzureApiDefinition, 0) + // TODO: Update this to be `parsedAPIVersion` + parsed := make(map[string]importerModels.AzureApiDefinition) for _, file := range fileNames { swaggerFile := file2Swagger[file] @@ -86,35 +89,55 @@ func LoadAndParseFiles(directory string, fileNames []string, serviceName, apiVer } 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 + results := make(map[string]sdkModels.APIVersion) for _, service := range out { - // temporary shim to enable using the new logic - version := sdkModels.APIVersion{ + tempAPIVersion := &sdkModels.APIVersion{ APIVersion: service.ApiVersion, Generate: true, Preview: service.IsPreviewVersion(), Resources: service.Resources, Source: sdkModels.AzureRestAPISpecsSourceDataOrigin, } - version = cleanup.RemoveUnusedItems(version) - service.Resources = version.Resources - } + if existing, hasExisting := results[service.ApiVersion]; hasExisting { + tempAPIVersion = &existing + } + tempAPIVersion, err := dataworkarounds.Apply(service.ServiceName, *tempAPIVersion) + if err != nil { + return nil, fmt.Errorf("applying Swagger overrides: %+v", err) + } - 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)) + results[service.ApiVersion] = *tempAPIVersion } - if len(out) == 1 { - return &out[0], nil + keys := keysForMap(results) + if len(keys) == 0 { + // possible there's no data for this API Version + return nil, nil + } + if len(results) > 1 { + // we're parsing a single API Version out, so if there's multiple that'd be unexpected/a bug + return nil, fmt.Errorf("expected a single API Version but got: %+v", keys) } - return nil, nil + result := results[keys[0]] + // finally, now that we've parsed everything out, iterate over to cleanup any unused items + result = cleanup.RemoveUnusedItems(result) + return &result, nil +} + +func keysForMap(input map[string]sdkModels.APIVersion) []string { + keys := make(map[string]struct{}) + for k := range input { + keys[k] = struct{}{} + } + // sort for consistency + output := make([]string, 0) + for k := range keys { + output = append(output, k) + } + sort.Strings(output) + return output } func keyForAzureApiDefinition(input importerModels.AzureApiDefinition) string { 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/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..11b764cb053 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parse_api_resource.go @@ -0,0 +1,92 @@ +package apidefinitions + +import ( + "fmt" + "path/filepath" + "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" + "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" + 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/resourceids" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/logging" +) + +func parseAPIResourcesFromFile(filePath, serviceName string, resourceProvider *string, existingAPIResources map[string]sdkModels.APIResource, supplementaryData parserModels.SupplementaryData, resourceIds resourceids.ParseResult) (*map[string]sdkModels.APIResource, error) { + parser, err := parser.NewAPIDefinitionsParser(filePath, supplementaryData) + 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 + parsedAPIResources := existingAPIResources + for _, tag := range tags { + if ignore.SwaggerTag(tag) { + continue + } + + resource, err := parser.ParseAPIResourceWithinSwaggerTag(&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 := cleanup.NormalizeTag(tag) + normalizedTag = cleanup.NormalizeResourceName(normalizedTag) + discoveredResources := map[string]sdkModels.APIResource{ + normalizedTag: *resource, + } + combined, err := combine.ResourcesWith(parsedAPIResources, discoveredResources) + if err != nil { + return nil, fmt.Errorf("combining the APIResources for the identified Swagger Tag %q: %+v", tag, err) + } + parsedAPIResources = *combined + } + } + + // 3. Then parse over any Swagger Operations which DON'T have a Tag + if !ignore.SwaggerTag(serviceName) { + resource, err := parser.ParseAPIResourceWithinSwaggerTag(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. + directory := filepath.Dir(filePath) + fileName := strings.TrimPrefix(filePath, directory) + fileName = strings.TrimPrefix(fileName, fmt.Sprintf("%c", filepath.Separator)) + inferredTag := cleanup.PluraliseName(fileName) + + if resource != nil { + normalizedTag := cleanup.NormalizeTag(inferredTag) + normalizedTag = cleanup.NormalizeResourceName(normalizedTag) + + discoveredResources := map[string]sdkModels.APIResource{ + normalizedTag: *resource, + } + combined, err := combine.ResourcesWith(parsedAPIResources, discoveredResources) + if err != nil { + return nil, fmt.Errorf("combining the APIResources for the inferred Swagger Tag %q: %+v", normalizedTag, err) + } + parsedAPIResources = *combined + } + } + + // 3. Now that we have a canonical list of resources - can we simplify the Operation names at all? + simplifiedAPIDefinitions := make(map[string]sdkModels.APIResource) + for resourceName, resource := range parsedAPIResources { + logging.Tracef("Simplifying operation names for resource %q", resourceName) + updated := cleanup.SimplifyOperationNamesForAPIResource(resourceName, resource) + simplifiedAPIDefinitions[resourceName] = updated + } + + return &simplifiedAPIDefinitions, nil +} 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..8cefde401c6 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parse_api_version.go @@ -0,0 +1,96 @@ +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" + 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/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) { + // Firstly let's go through and process each of the Supplementary Files + supplementaryData := parserModels.SupplementaryData{ + Constants: map[string]sdkModels.SDKConstant{}, + Models: map[string]sdkModels.SDKModel{}, + } + for _, filePath := range input.FilePathsContainingSupplementaryData { + logging.Tracef("Processing Supplementary Data from file %q..", filePath) + updatedSupplementaryData, err := parseSupplementaryDataFromFile(filePath, supplementaryData) + if err != nil { + return nil, fmt.Errorf("parsing the Supplementary Data within %q: %+v", filePath, err) + } + logging.Tracef("Processing Supplementary Data from file %q - Completed.", filePath) + + supplementaryData = *updatedSupplementaryData + logging.Tracef("The Supplementary Data now contains %d Constants and %d Models", len(supplementaryData.Constants), len(supplementaryData.Models)) + } + + // Next 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, supplementaryData) + 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) + resources, err := parseAPIResourcesFromFile(filePath, serviceName, resourceProvider, apiResources, supplementaryData, foundResourceIDs) + if err != nil { + return nil, fmt.Errorf("parsing the APIResources from the Supplementary Data within %q: %+v", filePath, err) + } + + logging.Tracef("There are now %d APIResources", len(*resources)) + apiResources = *resources + logging.Tracef("Processing API Definitions from file %q - Completed.", filePath) + } + + 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/parser.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parse_service.go similarity index 78% rename from tools/importer-rest-api-specs/internal/components/apidefinitions/parser.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parse_service.go index 23ef9722f27..6a4c46ea96b 100644 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parse_service.go @@ -5,20 +5,26 @@ package apidefinitions import ( "fmt" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/ignore" sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser" 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 := parser.ParseAPIVersion(input.ServiceName, dataSet) + parsed, err := parseAPIVersion(input.ServiceName, dataSet, input.ResourceProvider) if err != nil { return nil, fmt.Errorf("parsing API Version %q: %+v", apiVersionName, err) } diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parse_supplementary_data.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parse_supplementary_data.go new file mode 100644 index 00000000000..1181488e3d7 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parse_supplementary_data.go @@ -0,0 +1,26 @@ +package apidefinitions + +import ( + "fmt" + + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser" + 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" +) + +// parseSupplementaryDataFromFile loads any available Supplementary Data from the file in question +func parseSupplementaryDataFromFile(filePath string, knownData parserModels.SupplementaryData) (*parserModels.SupplementaryData, error) { + logging.Tracef("Building an Definitions Parser for %q..", filePath) + parser, err := parser.NewAPIDefinitionsParser(filePath, knownData) + if err != nil { + return nil, fmt.Errorf("building the APIDefinitions Parser for %q: %+v", filePath, err) + } + logging.Tracef("Parsing the Supplementary Data from %q..", filePath) + data, err := parser.SupplementaryData() + if err != nil { + return nil, fmt.Errorf("parsing the Supplementary Data from %q: %+v", filePath, err) + } + logging.Tracef("Loaded the Supplementary Data from %q.", filePath) + + return data, nil +} 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 index bf885931ec3..a98168ec213 100644 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/README.md +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/README.md @@ -9,3 +9,5 @@ This package intentionally uses a combination of terminology to reflect the diff * `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/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/combine/api_resource.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/combine/api_resource.go index 6ef89d3c0e9..979b5ccdd66 100644 --- 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 @@ -22,13 +22,13 @@ func ResourcesWith(first, other map[string]sdkModels.APIResource) (*map[string]s continue } - constants, err := constants(existing.Constants, v.Constants) + constants, err := Constants(existing.Constants, v.Constants) if err != nil { return nil, fmt.Errorf("combining constants: %+v", err) } existing.Constants = *constants - models, err := models(existing.Models, v.Models) + models, err := Models(existing.Models, v.Models) if err != nil { return nil, fmt.Errorf("combining models: %+v", err) } 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 index bb65503e300..c7b2ee28ec6 100644 --- 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 @@ -9,7 +9,7 @@ import ( sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" ) -func constants(first, second map[string]sdkModels.SDKConstant) (*map[string]sdkModels.SDKConstant, error) { +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 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 index b0da49857b6..653d14717e7 100644 --- 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 @@ -9,7 +9,7 @@ import ( 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) { +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 { 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 index 4d43f94ed23..108f9d18e73 100644 --- 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 @@ -6,9 +6,9 @@ package combine 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" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/components/parser/resourceids" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/comparison" ) func resourceIds(first, second map[string]sdkModels.ResourceID) (*map[string]sdkModels.ResourceID, error) { @@ -21,8 +21,8 @@ func resourceIds(first, second map[string]sdkModels.ResourceID) (*map[string]sdk 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)) + if ok && !comparison.ResourceIDsMatch(v, otherVal) { + return nil, fmt.Errorf("duplicate Resource ID named %q (First %q / Second %q)", k, sdkHelpers.DisplayValueForResourceID(v), sdkHelpers.DisplayValueForResourceID(otherVal)) } output[k] = v diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema_test.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema_test.go new file mode 100644 index 00000000000..1223f809d90 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema_test.go @@ -0,0 +1,842 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package parser + +import ( + "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/parser/testhelpers" +) + +// TODO: Edge Zones +// TODO: SystemData +// TODO: Tags +// TODO: Zone +// TODO: Zones + +func TestParseModel_CommonSchema_IdentityLegacySystemAndUserAssignedList(t *testing.T) { + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_commonschema_identitylegacysystemanduserassignedlist.json") + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Resource": { + Models: map[string]sdkModels.SDKModel{ + "Model": { + Fields: map[string]sdkModels.SDKField{ + "Identity": { + JsonName: "identity", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.LegacySystemAndUserAssignedIdentityListSDKObjectDefinitionType, + }, + Required: false, + }, + "Name": { + JsonName: "name", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Model"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/example"), + }, + }, + }, + }, + } + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseModel_CommonSchema_IdentityLegacySystemAndUserAssignedMap(t *testing.T) { + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_commonschema_identitylegacysystemanduserassignedmap.json") + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Resource": { + Models: map[string]sdkModels.SDKModel{ + "Model": { + Fields: map[string]sdkModels.SDKField{ + "Identity": { + JsonName: "identity", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.LegacySystemAndUserAssignedIdentityMapSDKObjectDefinitionType, + }, + Required: false, + }, + "Name": { + JsonName: "name", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Model"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/example"), + }, + }, + }, + }, + } + 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 := testhelpers.ParseSwaggerFileForTesting(t, "model_commonschema_identitylegacysystemanduserassignedmap_extrafields.json") + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Resource": { + Models: map[string]sdkModels.SDKModel{ + "Model": { + Fields: map[string]sdkModels.SDKField{ + "Identity": { + JsonName: "identity", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.LegacySystemAndUserAssignedIdentityMapSDKObjectDefinitionType, + }, + Required: false, + }, + "Name": { + JsonName: "name", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Model"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/example"), + }, + }, + }, + }, + } + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseModel_CommonSchema_IdentityLegacySystemAndUserAssignedMap_GenericDictionary(t *testing.T) { + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_commonschema_identitylegacysystemanduserassignedmap_genericdictionary.json") + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Resource": { + Models: map[string]sdkModels.SDKModel{ + "Model": { + Fields: map[string]sdkModels.SDKField{ + "Identity": { + JsonName: "identity", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.LegacySystemAndUserAssignedIdentityMapSDKObjectDefinitionType, + }, + Required: false, + }, + "Name": { + JsonName: "name", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Model"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/example"), + }, + }, + }, + }, + } + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseModel_CommonSchema_IdentitySystemAssigned(t *testing.T) { + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_commonschema_identitysystemassigned.json") + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Resource": { + Models: map[string]sdkModels.SDKModel{ + "Model": { + Fields: map[string]sdkModels.SDKField{ + "Identity": { + JsonName: "identity", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.SystemAssignedIdentitySDKObjectDefinitionType, + }, + Required: false, + }, + "Name": { + JsonName: "name", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Model"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/example"), + }, + }, + }, + }, + } + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseModel_CommonSchema_IdentitySystemAndUserAssignedList(t *testing.T) { + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_commonschema_identitysystemanduserassignedlist.json") + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Resource": { + Models: map[string]sdkModels.SDKModel{ + "Model": { + Fields: map[string]sdkModels.SDKField{ + "Identity": { + JsonName: "identity", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.SystemAndUserAssignedIdentityListSDKObjectDefinitionType, + }, + Required: false, + }, + "Name": { + JsonName: "name", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Model"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/example"), + }, + }, + }, + }, + } + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseModel_CommonSchema_IdentitySystemAndUserAssignedMap(t *testing.T) { + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_commonschema_identitysystemanduserassignedmap.json") + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Resource": { + Models: map[string]sdkModels.SDKModel{ + "Model": { + Fields: map[string]sdkModels.SDKField{ + "Identity": { + JsonName: "identity", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.SystemAndUserAssignedIdentityMapSDKObjectDefinitionType, + }, + Required: false, + }, + "Name": { + JsonName: "name", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Model"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/example"), + }, + }, + }, + }, + } + 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 := testhelpers.ParseSwaggerFileForTesting(t, "model_commonschema_identitysystemanduserassignedmap.json") + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Resource": { + Models: map[string]sdkModels.SDKModel{ + "Model": { + Fields: map[string]sdkModels.SDKField{ + "Identity": { + JsonName: "identity", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.SystemAndUserAssignedIdentityMapSDKObjectDefinitionType, + }, + Required: false, + }, + "Name": { + JsonName: "name", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Model"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/example"), + }, + }, + }, + }, + } + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseModel_CommonSchema_IdentitySystemOrUserAssignedList(t *testing.T) { + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_commonschema_identitysystemoruserassignedlist.json") + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Resource": { + Models: map[string]sdkModels.SDKModel{ + "Model": { + Fields: map[string]sdkModels.SDKField{ + "Identity": { + JsonName: "identity", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.SystemOrUserAssignedIdentityListSDKObjectDefinitionType, + }, + Required: false, + }, + "Name": { + JsonName: "name", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Model"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/example"), + }, + }, + }, + }, + } + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseModel_CommonSchema_IdentitySystemOrUserAssignedMap(t *testing.T) { + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_commonschema_identitysystemoruserassignedmap.json") + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Resource": { + Models: map[string]sdkModels.SDKModel{ + "Model": { + Fields: map[string]sdkModels.SDKField{ + "Identity": { + JsonName: "identity", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.SystemOrUserAssignedIdentityMapSDKObjectDefinitionType, + }, + Required: false, + }, + "Name": { + JsonName: "name", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Model"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/example"), + }, + }, + }, + }, + } + 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 := testhelpers.ParseSwaggerFileForTesting(t, "model_commonschema_identitysystemoruserassignedmap_delegatedresources.json") + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Resource": { + Models: map[string]sdkModels.SDKModel{ + "Model": { + Fields: map[string]sdkModels.SDKField{ + "Identity": { + JsonName: "identity", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.SystemOrUserAssignedIdentityMapSDKObjectDefinitionType, + }, + Required: false, + }, + "Name": { + JsonName: "name", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Model"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/example"), + }, + }, + }, + }, + } + 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 := testhelpers.ParseSwaggerFileForTesting(t, "model_commonschema_identitysystemoruserassignedmap_extrafields.json") + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Resource": { + Models: map[string]sdkModels.SDKModel{ + "Model": { + Fields: map[string]sdkModels.SDKField{ + "Identity": { + JsonName: "identity", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.SystemOrUserAssignedIdentityMapSDKObjectDefinitionType, + }, + Required: false, + }, + "Name": { + JsonName: "name", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Model"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/example"), + }, + }, + }, + }, + } + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseModel_CommonSchema_IdentityUserAssignedList(t *testing.T) { + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_commonschema_identityuserassignedlist.json") + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Resource": { + Models: map[string]sdkModels.SDKModel{ + "Model": { + Fields: map[string]sdkModels.SDKField{ + "Identity": { + JsonName: "identity", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.UserAssignedIdentityListSDKObjectDefinitionType, + }, + Required: false, + }, + "Name": { + JsonName: "name", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Model"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/example"), + }, + }, + }, + }, + } + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseModel_CommonSchema_IdentityUserAssignedMap(t *testing.T) { + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_commonschema_identityuserassignedmap.json") + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Resource": { + Models: map[string]sdkModels.SDKModel{ + "Model": { + Fields: map[string]sdkModels.SDKField{ + "Identity": { + JsonName: "identity", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.UserAssignedIdentityMapSDKObjectDefinitionType, + }, + Required: false, + }, + "Name": { + JsonName: "name", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Model"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/example"), + }, + }, + }, + }, + } + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseModel_CommonSchema_IdentityUserAssignedMap_PrincipalID(t *testing.T) { + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_commonschema_identityuserassignedmap_principalid.json") + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Resource": { + Models: map[string]sdkModels.SDKModel{ + "Model": { + Fields: map[string]sdkModels.SDKField{ + "Identity": { + JsonName: "identity", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.UserAssignedIdentityMapSDKObjectDefinitionType, + }, + Required: false, + }, + "Name": { + JsonName: "name", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Model"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/example"), + }, + }, + }, + }, + } + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseModel_CommonSchema_IdentityUserAssignedMap_TenantID(t *testing.T) { + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_commonschema_identityuserassignedmap_tenantid.json") + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Resource": { + Models: map[string]sdkModels.SDKModel{ + "Model": { + Fields: map[string]sdkModels.SDKField{ + "Identity": { + JsonName: "identity", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.UserAssignedIdentityMapSDKObjectDefinitionType, + }, + Required: false, + }, + "Name": { + JsonName: "name", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Model"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/example"), + }, + }, + }, + }, + } + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseModel_CommonSchema_Location(t *testing.T) { + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_commonschema_location.json") + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Resource": { + Models: map[string]sdkModels.SDKModel{ + "Model": { + Fields: map[string]sdkModels.SDKField{ + "Location": { + JsonName: "location", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.LocationSDKObjectDefinitionType, + }, + Required: false, + }, + "Name": { + JsonName: "name", + ObjectDefinition: sdkModels.SDKObjectDefinition{ + Type: sdkModels.StringSDKObjectDefinitionType, + }, + Required: false, + }, + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "GET", + ResponseObject: &sdkModels.SDKObjectDefinition{ + ReferenceName: pointer.To("Model"), + Type: sdkModels.ReferenceSDKObjectDefinitionType, + }, + URISuffix: pointer.To("/example"), + }, + }, + }, + }, + } + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) +} 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/parse.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/constants/parse.go index 7c1111830df..7161e654011 100644 --- 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 @@ -22,7 +22,7 @@ type ParsedConstant struct { func Parse(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) + return nil, fmt.Errorf("the Enum in %q has no values", fieldName) } constantName := fieldName 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..30012ad2030 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 @@ -4,23 +4,22 @@ package parser 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") 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") 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") 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") 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") 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") 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") 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") 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") 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") 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") 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") 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") 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") 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") 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") 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") 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") if err != nil { t.Fatalf("parsing: %+v", err) } diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_inconsistent_swagger_segments.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_inconsistent_swagger_segments.go index 2a55fd9eca5..b2e91847987 100644 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_inconsistent_swagger_segments.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_inconsistent_swagger_segments.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/components/apidefinitions/parser/cleanup" ) @@ -53,7 +53,7 @@ func (workaroundInconsistentlyDefinedSegments) Process(input sdkModels.APIVersio segments = append(segments, val) } id.Segments = segments - id.ExampleValue = helpers.DisplayValueForResourceID(id) + id.ExampleValue = sdkHelpers.DisplayValueForResourceID(id) ids[key] = id } resource.ResourceIDs = ids diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models/supplementary_data.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models/supplementary_data.go new file mode 100644 index 00000000000..61a23de801e --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models/supplementary_data.go @@ -0,0 +1,50 @@ +package models + +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" +) + +// SupplementaryData contains information about any Constants and Models which are Supplementary. +// These Constants and Models aren't directly used in either Operations or Resource IDs, but are +// typically used in Discriminator Implementations for Models which are, and so need to be included +// and considered when identifying Discriminator Implementations. +type SupplementaryData struct { + // Constants specifies a map of Constant Name (key, valid as an identifier) to SDKConstant (value). + Constants map[string]sdkModels.SDKConstant + + // Models specifies a map of Model Name (key, valid as an identifier) to SDKModel (value). + Models map[string]sdkModels.SDKModel +} + +func (d *SupplementaryData) AppendConstant(name string, value sdkModels.SDKConstant) error { + constants := map[string]sdkModels.SDKConstant{ + name: value, + } + + combinedConstants, err := combine.Constants(d.Constants, constants) + if err != nil { + return fmt.Errorf("combining Constants: %+v", err) + } + d.Constants = *combinedConstants + + return nil +} + +func (d *SupplementaryData) AppendParseResult(other ParseResult) error { + combinedConstants, err := combine.Constants(d.Constants, other.Constants) + if err != nil { + return fmt.Errorf("combining Constants: %+v", err) + } + d.Constants = *combinedConstants + + combinedModels, err := combine.Models(d.Models, other.Models) + if err != nil { + return fmt.Errorf("combining Models: %+v", err) + } + d.Models = *combinedModels + + 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..3380628f3cf 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 @@ -4,17 +4,17 @@ package parser 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") 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..668dacecb67 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 @@ -4,22 +4,21 @@ package parser 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 TestParseModelWithADictionaryOfIntegers(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_dictionary_of_integers.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_dictionary_of_integers.json") 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") 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") 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") 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") 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") 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 91% 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..d3ec6a5a5fe 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 @@ -4,22 +4,21 @@ package parser 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 TestParseDiscriminatorsTopLevel(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_discriminators_simple.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_discriminators_simple.json") 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") 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") 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") 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") 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") 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") 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") 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") 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{ @@ -1020,20 +1011,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") 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{ @@ -1174,18 +1164,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") 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 +1240,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") 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 +1345,17 @@ 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")) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "/nestedtestdata/model_discriminators_orphaned_child_without_discriminator_value.json") 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 +1407,19 @@ func TestParseDiscriminatorsOrphanedChildWithoutDiscriminatorValue(t *testing.T) }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + 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") 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 95% 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..22bf5c9f75b 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 @@ -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" ) // TODO: tests for the different types of Object Definition @@ -19,9 +18,8 @@ func TestParseModelTopLevel(t *testing.T) { 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{ @@ -100,9 +98,8 @@ func TestParseModelTopLevelWithRawFile(t *testing.T) { 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{ @@ -131,9 +128,8 @@ func TestParseModelTopLevelWithInlinedModel(t *testing.T) { 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{ @@ -231,9 +227,8 @@ func TestParseModelWithDateTimeNoType(t *testing.T) { 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{ @@ -277,9 +272,8 @@ func TestParseModelWithInlinedObject(t *testing.T) { 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{ @@ -372,9 +366,8 @@ func TestParseModelWithNumberPrefixedField(t *testing.T) { 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{ @@ -421,9 +414,8 @@ func TestParseModelWithReference(t *testing.T) { 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{ @@ -504,9 +496,8 @@ func TestParseModelWithReferenceToArray(t *testing.T) { 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{ @@ -573,9 +564,8 @@ func TestParseModelWithReferenceToConstant(t *testing.T) { 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{ @@ -654,9 +644,8 @@ func TestParseModelWithReferenceToString(t *testing.T) { 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{ @@ -725,9 +714,8 @@ func TestParseModelWithCircularReferences(t *testing.T) { 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{ @@ -827,9 +815,8 @@ func TestParseModelInheritingFromObjectWithNoExtraFields(t *testing.T) { 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{ @@ -871,9 +858,8 @@ func TestParseModelInheritingFromObjectWithNoExtraFieldsInlined(t *testing.T) { 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{ @@ -932,9 +918,8 @@ func TestParseModelInheritingFromObjectWithOnlyDescription(t *testing.T) { 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{ @@ -981,9 +966,8 @@ func TestParseModelInheritingFromObjectWithPropertiesWithinAllOf(t *testing.T) { 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{ @@ -1026,9 +1010,8 @@ func TestParseModelContainingAllOfToTypeObject(t *testing.T) { 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{ @@ -1079,9 +1062,8 @@ func TestParseModelContainingAllOfToTypeObjectWithProperties(t *testing.T) { 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{ @@ -1132,9 +1114,8 @@ func TestParseModelContainingAllOfWithinProperties(t *testing.T) { 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{ @@ -1211,9 +1192,8 @@ func TestParseModelContainingMultipleAllOfWithinProperties(t *testing.T) { 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{ @@ -1302,9 +1282,8 @@ func TestParseModelContainingLists(t *testing.T) { 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{ @@ -1388,9 +1367,8 @@ func TestParseModelInlinedWithNoName(t *testing.T) { 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{ @@ -1445,9 +1423,8 @@ func TestParseModelInheritingFromParent(t *testing.T) { 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{ @@ -1519,9 +1496,8 @@ func TestParseModelMultipleTopLevelModelsAndOperations(t *testing.T) { 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{ @@ -1628,9 +1604,8 @@ func TestParseModelBug2675DuplicateModel(t *testing.T) { 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{ 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/components/parser/helpers_list_operations.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/operation/list.go similarity index 86% rename from tools/importer-rest-api-specs/components/parser/helpers_list_operations.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/operation/list.go index 613d836370b..e6b7ce2c683 100644 --- a/tools/importer-rest-api-specs/components/parser/helpers_list_operations.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/operation/list.go @@ -1,7 +1,4 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package parser +package operation import ( "net/http" @@ -10,7 +7,7 @@ import ( "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" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/components/parser/internal" + parserModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models" ) type listOperationDetails struct { @@ -18,7 +15,7 @@ type listOperationDetails struct { valueObjectDefinition *sdkModels.SDKObjectDefinition } -func listOperationDetailsForOperation(input sdkModels.SDKOperation, known internal.ParseResult) *listOperationDetails { +func listOperationDetailsForOperation(input sdkModels.SDKOperation, known parserModels.ParseResult) *listOperationDetails { if !strings.EqualFold(input.Method, http.MethodGet) && !strings.EqualFold(input.Method, http.MethodPost) { return nil } @@ -60,7 +57,7 @@ func listOperationDetailsForOperation(input sdkModels.SDKOperation, known intern return nil } -func pullOutModelForListOperations(input map[string]sdkModels.SDKOperation, known internal.ParseResult) (*map[string]sdkModels.SDKOperation, error) { +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. 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..5d0f0bcf064 --- /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..8d545950696 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/operation/parse_operation.go @@ -0,0 +1,82 @@ +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" +) + +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 { + 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..c0a1eade93b --- /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..feac4f82c7e --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/operation/request_object.go @@ -0,0 +1,36 @@ +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 + loadParentType := true + objectDefinition, result, err := parsingContext.ParseObjectDefinition(param.Schema.Title, param.Schema.Title, param.Schema, known, parsingModel, loadParentType) + 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..f2aaf220317 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/operation/response_object.go @@ -0,0 +1,80 @@ +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 + loadParentType := true + objectDefinition, nestedResult, err := parsingContext.ParseObjectDefinition(details.ResponseProps.Schema.Title, details.ResponseProps.Schema.Title, details.ResponseProps.Schema, result, parsingModel, loadParentType) + 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 86% 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..ada2b42b50d 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 @@ -9,7 +9,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" ) // TODO: tests for the different types of Operation Object Definition - including CSV's inner object @@ -19,12 +18,11 @@ func TestParseOperationsEmpty(t *testing.T) { 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) { @@ -32,9 +30,8 @@ func TestParseOperationSingleWithTag(t *testing.T) { 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,7 +46,7 @@ func TestParseOperationSingleWithTag(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleWithTagAndResourceId(t *testing.T) { @@ -58,9 +55,8 @@ func TestParseOperationSingleWithTagAndResourceId(t *testing.T) { 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{ @@ -88,7 +84,7 @@ func TestParseOperationSingleWithTagAndResourceId(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleWithTagAndResourceIdSuffix(t *testing.T) { @@ -97,9 +93,8 @@ func TestParseOperationSingleWithTagAndResourceIdSuffix(t *testing.T) { 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{ @@ -128,7 +123,7 @@ func TestParseOperationSingleWithTagAndResourceIdSuffix(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleWithRequestObject(t *testing.T) { @@ -137,9 +132,8 @@ func TestParseOperationSingleWithRequestObject(t *testing.T) { 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,7 +164,7 @@ func TestParseOperationSingleWithRequestObject(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleWithRequestObjectInlined(t *testing.T) { @@ -179,9 +173,8 @@ func TestParseOperationSingleWithRequestObjectInlined(t *testing.T) { 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,7 +205,7 @@ func TestParseOperationSingleWithRequestObjectInlined(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleWithResponseObject(t *testing.T) { @@ -221,9 +214,8 @@ func TestParseOperationSingleWithResponseObject(t *testing.T) { 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,7 +246,7 @@ func TestParseOperationSingleWithResponseObject(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleWithResponseObjectInlined(t *testing.T) { @@ -263,9 +255,8 @@ func TestParseOperationSingleWithResponseObjectInlined(t *testing.T) { 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,7 +287,7 @@ func TestParseOperationSingleWithResponseObjectInlined(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleWithResponseObjectInlinedList(t *testing.T) { @@ -305,9 +296,8 @@ func TestParseOperationSingleWithResponseObjectInlinedList(t *testing.T) { 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,7 +331,7 @@ func TestParseOperationSingleWithResponseObjectInlinedList(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleRequestingWithABool(t *testing.T) { @@ -350,9 +340,8 @@ func TestParseOperationSingleRequestingWithABool(t *testing.T) { 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,7 +358,7 @@ func TestParseOperationSingleRequestingWithABool(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleRequestingWithAInteger(t *testing.T) { @@ -378,9 +367,8 @@ func TestParseOperationSingleRequestingWithAInteger(t *testing.T) { 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,7 +385,7 @@ func TestParseOperationSingleRequestingWithAInteger(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleRequestingWithADictionaryOfStrings(t *testing.T) { @@ -406,9 +394,8 @@ func TestParseOperationSingleRequestingWithADictionaryOfStrings(t *testing.T) { 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,7 +415,7 @@ func TestParseOperationSingleRequestingWithADictionaryOfStrings(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleRequestingWithAListOfStrings(t *testing.T) { @@ -437,9 +424,8 @@ func TestParseOperationSingleRequestingWithAListOfStrings(t *testing.T) { 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,7 +445,7 @@ func TestParseOperationSingleRequestingWithAListOfStrings(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } // Models are already tested above @@ -470,9 +456,8 @@ func TestParseOperationSingleRequestingWithAString(t *testing.T) { 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,7 +474,7 @@ func TestParseOperationSingleRequestingWithAString(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleReturningABool(t *testing.T) { @@ -498,9 +483,8 @@ func TestParseOperationSingleReturningABool(t *testing.T) { 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,7 +501,7 @@ func TestParseOperationSingleReturningABool(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleReturningAFloat(t *testing.T) { @@ -526,9 +510,8 @@ func TestParseOperationSingleReturningAFloat(t *testing.T) { 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,7 +528,7 @@ func TestParseOperationSingleReturningAFloat(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleReturningAFile(t *testing.T) { @@ -554,9 +537,8 @@ func TestParseOperationSingleReturningAFile(t *testing.T) { 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,7 +555,7 @@ func TestParseOperationSingleReturningAFile(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleReturningAnInteger(t *testing.T) { @@ -582,9 +564,8 @@ func TestParseOperationSingleReturningAnInteger(t *testing.T) { 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,7 +582,7 @@ func TestParseOperationSingleReturningAnInteger(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleReturningAString(t *testing.T) { @@ -610,9 +591,8 @@ func TestParseOperationSingleReturningAString(t *testing.T) { 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,7 +609,7 @@ func TestParseOperationSingleReturningAString(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleReturningAnErrorStatusCode(t *testing.T) { @@ -639,9 +619,8 @@ func TestParseOperationSingleReturningAnErrorStatusCode(t *testing.T) { 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,7 +637,7 @@ func TestParseOperationSingleReturningAnErrorStatusCode(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleReturningATopLevelRawObject(t *testing.T) { @@ -667,9 +646,8 @@ func TestParseOperationSingleReturningATopLevelRawObject(t *testing.T) { 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,7 +667,7 @@ func TestParseOperationSingleReturningATopLevelRawObject(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleReturningADictionaryOfAModel(t *testing.T) { @@ -698,9 +676,8 @@ func TestParseOperationSingleReturningADictionaryOfAModel(t *testing.T) { 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,7 +711,7 @@ func TestParseOperationSingleReturningADictionaryOfAModel(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleReturningADictionaryOfStrings(t *testing.T) { @@ -743,9 +720,8 @@ func TestParseOperationSingleReturningADictionaryOfStrings(t *testing.T) { 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,7 +741,7 @@ func TestParseOperationSingleReturningADictionaryOfStrings(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleReturningAListOfIntegers(t *testing.T) { @@ -774,9 +750,8 @@ func TestParseOperationSingleReturningAListOfIntegers(t *testing.T) { 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,7 +771,7 @@ func TestParseOperationSingleReturningAListOfIntegers(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleReturningAListOfAModel(t *testing.T) { @@ -805,9 +780,8 @@ func TestParseOperationSingleReturningAListOfAModel(t *testing.T) { 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,7 +815,7 @@ func TestParseOperationSingleReturningAListOfAModel(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleReturningAListOfStrings(t *testing.T) { @@ -850,9 +824,8 @@ func TestParseOperationSingleReturningAListOfStrings(t *testing.T) { 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,7 +845,7 @@ func TestParseOperationSingleReturningAListOfStrings(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleReturningAListOfListOfAModel(t *testing.T) { @@ -881,9 +854,8 @@ func TestParseOperationSingleReturningAListOfListOfAModel(t *testing.T) { 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,7 +892,7 @@ func TestParseOperationSingleReturningAListOfListOfAModel(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleReturningAListOfListOfStrings(t *testing.T) { @@ -929,9 +901,8 @@ func TestParseOperationSingleReturningAListOfListOfStrings(t *testing.T) { 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,7 +925,7 @@ func TestParseOperationSingleReturningAListOfListOfStrings(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleWithList(t *testing.T) { @@ -963,9 +934,8 @@ func TestParseOperationSingleWithList(t *testing.T) { 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,7 +967,7 @@ func TestParseOperationSingleWithList(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleWithListWhichIsNotAList(t *testing.T) { @@ -1008,9 +978,8 @@ func TestParseOperationSingleWithListWhichIsNotAList(t *testing.T) { 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,7 +1015,7 @@ func TestParseOperationSingleWithListWhichIsNotAList(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleWithListReturningAListOfStrings(t *testing.T) { @@ -1055,9 +1024,8 @@ func TestParseOperationSingleWithListReturningAListOfStrings(t *testing.T) { 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,7 +1043,7 @@ func TestParseOperationSingleWithListReturningAListOfStrings(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleWithListWithoutPageable(t *testing.T) { @@ -1086,9 +1054,8 @@ func TestParseOperationSingleWithListWithoutPageable(t *testing.T) { 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,7 +1087,7 @@ func TestParseOperationSingleWithListWithoutPageable(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleWithLongRunningOperation(t *testing.T) { @@ -1129,9 +1096,8 @@ func TestParseOperationSingleWithLongRunningOperation(t *testing.T) { 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,7 +1129,7 @@ func TestParseOperationSingleWithLongRunningOperation(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleWithRequestAndResponseObject(t *testing.T) { @@ -1172,9 +1138,8 @@ func TestParseOperationSingleWithRequestAndResponseObject(t *testing.T) { 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,7 +1174,7 @@ func TestParseOperationSingleWithRequestAndResponseObject(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleWithMultipleTags(t *testing.T) { @@ -1218,9 +1183,8 @@ func TestParseOperationSingleWithMultipleTags(t *testing.T) { 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,7 +1210,7 @@ func TestParseOperationSingleWithMultipleTags(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleWithInferredTag(t *testing.T) { @@ -1255,9 +1219,8 @@ func TestParseOperationSingleWithInferredTag(t *testing.T) { 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,7 +1236,7 @@ func TestParseOperationSingleWithInferredTag(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleWithHeaderOptions(t *testing.T) { @@ -1282,9 +1245,8 @@ func TestParseOperationSingleWithHeaderOptions(t *testing.T) { 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,7 +1317,7 @@ func TestParseOperationSingleWithHeaderOptions(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationSingleWithQueryStringOptions(t *testing.T) { @@ -1364,9 +1326,8 @@ func TestParseOperationSingleWithQueryStringOptions(t *testing.T) { 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,7 +1398,7 @@ func TestParseOperationSingleWithQueryStringOptions(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationMultipleBasedOnTheSameResourceId(t *testing.T) { @@ -1446,9 +1407,8 @@ func TestParseOperationMultipleBasedOnTheSameResourceId(t *testing.T) { 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{ @@ -1483,7 +1443,7 @@ func TestParseOperationMultipleBasedOnTheSameResourceId(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationsContainingContentTypes(t *testing.T) { @@ -1492,9 +1452,8 @@ func TestParseOperationsContainingContentTypes(t *testing.T) { 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,7 +1504,7 @@ func TestParseOperationsContainingContentTypes(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationContainingMultipleReturnObjects(t *testing.T) { @@ -1554,9 +1513,8 @@ func TestParseOperationContainingMultipleReturnObjects(t *testing.T) { 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,7 +1545,7 @@ func TestParseOperationContainingMultipleReturnObjects(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseOperationsWithStutteringNames(t *testing.T) { @@ -1596,9 +1554,8 @@ func TestParseOperationsWithStutteringNames(t *testing.T) { 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 +1584,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_api_resource.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_api_resource.go deleted file mode 100644 index dfdaa6eb328..00000000000 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_api_resource.go +++ /dev/null @@ -1,11 +0,0 @@ -package parser - -import ( - "fmt" - - sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" -) - -func parseAPIResourcesWithin(filePath string, existingAPIResources map[string]sdkModels.APIResource) (*map[string]sdkModels.APIResource, error) { - return nil, fmt.Errorf("unimplemented until the refactor is completed") -} 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/internal/components/apidefinitions/parser/parse_resource_ids_test.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_resource_ids_test.go new file mode 100644 index 00000000000..81a7b0b565b --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_resource_ids_test.go @@ -0,0 +1,752 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package parser_test + +import ( + "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/parser/testhelpers" +) + +func TestParseResourceIdBasic(t *testing.T) { + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "resource_ids_basic.json") + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Example": { + ResourceIDs: map[string]sdkModels.ResourceID{ + "ServerId": { + Segments: []sdkModels.ResourceIDSegment{ + sdkModels.NewStaticValueResourceIDSegment("staticSubscriptions", "subscriptions"), + sdkModels.NewSubscriptionIDResourceIDSegment("subscriptionId"), + sdkModels.NewStaticValueResourceIDSegment("staticResourceGroups", "resourceGroups"), + sdkModels.NewResourceGroupNameResourceIDSegment("resourceGroupName"), + sdkModels.NewStaticValueResourceIDSegment("staticProviders", "providers"), + sdkModels.NewResourceProviderResourceIDSegment("staticMicrosoftSomeResourceProvider", "Microsoft.SomeResourceProvider"), + sdkModels.NewStaticValueResourceIDSegment("staticServers", "servers"), + sdkModels.NewUserSpecifiedResourceIDSegment("serverName", "serverName"), + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "HEAD", + ResourceIDName: pointer.To("ServerId"), + }, + }, + }, + }, + } + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseResourceIdContainingAConstant(t *testing.T) { + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "resource_ids_containing_constant.json") + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Example": { + Constants: map[string]sdkModels.SDKConstant{ + "Planet": { + Type: sdkModels.StringSDKConstantType, + Values: map[string]string{ + "Earth": "Earth", + "Jupiter": "Jupiter", + "Mars": "Mars", + "Saturn": "Saturn", + }, + }, + }, + ResourceIDs: map[string]sdkModels.ResourceID{ + "PlanetId": { + ConstantNames: []string{ + "Planet", + }, + Segments: []sdkModels.ResourceIDSegment{ + sdkModels.NewStaticValueResourceIDSegment("staticPlanets", "planets"), + sdkModels.NewConstantResourceIDSegment("planetName", "Planet", "Earth"), + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "OperationContainingAConstant": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "HEAD", + ResourceIDName: pointer.To("PlanetId"), + }, + }, + }, + }, + } + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseResourceIdContainingAScope(t *testing.T) { + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "resource_ids_containing_scope.json") + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Example": { + ResourceIDs: map[string]sdkModels.ResourceID{ + "ScopedVirtualMachineId": { + Segments: []sdkModels.ResourceIDSegment{ + sdkModels.NewScopeResourceIDSegment("scope"), + sdkModels.NewStaticValueResourceIDSegment("staticProviders", "providers"), + sdkModels.NewResourceProviderResourceIDSegment("staticMicrosoftFooBar", "Microsoft.FooBar"), + sdkModels.NewStaticValueResourceIDSegment("staticVirtualMachines", "virtualMachines"), + sdkModels.NewUserSpecifiedResourceIDSegment("virtualMachineName", "virtualMachinesName"), + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "OperationContainingAScope": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "HEAD", + ResourceIDName: pointer.To("ScopedVirtualMachineId"), + }, + }, + }, + }, + } + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseResourceIdContainingAHiddenScope(t *testing.T) { + files := []string{ + "resource_ids_containing_hidden_scope.json", + "resource_ids_containing_hidden_scope_constant.json", + "resource_ids_containing_hidden_scope_hardcoded_provider.json", + } + for _, file := range files { + t.Run(file, func(t *testing.T) { + fileName := file + + actual, err := testhelpers.ParseSwaggerFileForTesting(t, fileName) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Example": { + ResourceIDs: map[string]sdkModels.ResourceID{ + "ScopeId": { + CommonIDAlias: pointer.To("Scope"), + Segments: []sdkModels.ResourceIDSegment{ + sdkModels.NewScopeResourceIDSegment("scope"), + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "OperationContainingAHiddenScope": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "HEAD", + ResourceIDName: pointer.To("ScopeId"), + }, + }, + }, + }, + } + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) + }) + } +} + +func TestParseResourceIdContainingAHiddenScopeWithExtraSegment(t *testing.T) { + // The extra segment should be ignored and detected as a regular scope + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "resource_ids_containing_hidden_scope_with_extra_segment.json") + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Example": { + ResourceIDs: map[string]sdkModels.ResourceID{ + "ScopeId": { + CommonIDAlias: pointer.To("Scope"), + Segments: []sdkModels.ResourceIDSegment{ + sdkModels.NewScopeResourceIDSegment("scope"), + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "OperationContainingAHiddenScope": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "HEAD", + ResourceIDName: pointer.To("ScopeId"), + }, + }, + }, + }, + } + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseResourceIdContainingAHiddenScopeWithSuffix(t *testing.T) { + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "resource_ids_containing_hidden_scope_with_suffix.json") + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Example": { + ResourceIDs: map[string]sdkModels.ResourceID{ + "ScopeId": { + CommonIDAlias: pointer.To("Scope"), + Segments: []sdkModels.ResourceIDSegment{ + sdkModels.NewScopeResourceIDSegment("scope"), + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "OperationContainingAHiddenScope": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "HEAD", + ResourceIDName: pointer.To("ScopeId"), + URISuffix: pointer.To("/someEndpoint"), + }, + }, + }, + }, + } + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseResourceIdContainingAHiddenScopeNested(t *testing.T) { + files := []string{ + "resource_ids_containing_hidden_scope_nested.json", + "resource_ids_containing_hidden_scope_nested_constants.json", + "resource_ids_containing_hidden_scope_nested_hardcoded_provider.json", + } + for _, file := range files { + t.Run(file, func(t *testing.T) { + fileName := file + + actual, err := testhelpers.ParseSwaggerFileForTesting(t, fileName) + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Example": { + ResourceIDs: map[string]sdkModels.ResourceID{ + "ScopeId": { + CommonIDAlias: pointer.To("Scope"), + Segments: []sdkModels.ResourceIDSegment{ + sdkModels.NewScopeResourceIDSegment("scope"), + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "OperationContainingAHiddenScope": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "HEAD", + ResourceIDName: pointer.To("ScopeId"), + }, + }, + }, + }, + } + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) + }) + } +} + +func TestParseResourceIdContainingAHiddenScopeNestedWithExtraSegment(t *testing.T) { + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "resource_ids_containing_hidden_scope_nested_with_extra_segment.json") + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Example": { + ResourceIDs: map[string]sdkModels.ResourceID{ + "ScopeId": { + CommonIDAlias: pointer.To("Scope"), + Segments: []sdkModels.ResourceIDSegment{ + sdkModels.NewScopeResourceIDSegment("scope"), + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "OperationContainingAHiddenScope": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "HEAD", + ResourceIDName: pointer.To("ScopeId"), + }, + }, + }, + }, + } + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseResourceIdContainingAHiddenScopeNestedWithSuffix(t *testing.T) { + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "resource_ids_containing_hidden_scope_nested_with_suffix.json") + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Example": { + ResourceIDs: map[string]sdkModels.ResourceID{ + "ScopeId": { + CommonIDAlias: pointer.To("Scope"), + Segments: []sdkModels.ResourceIDSegment{ + sdkModels.NewScopeResourceIDSegment("scope"), + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "OperationContainingAHiddenScope": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "HEAD", + ResourceIDName: pointer.To("ScopeId"), + URISuffix: pointer.To("/someEndpoint"), + }, + }, + }, + }, + } + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseResourceIdWithJustUriSuffix(t *testing.T) { + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "resource_ids_with_just_suffix.json") + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Example": { + Operations: map[string]sdkModels.SDKOperation{ + "JustSuffix": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "HEAD", + URISuffix: pointer.To("/restart"), + }, + }, + }, + }, + } + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseResourceIdWithResourceIdAndUriSuffix(t *testing.T) { + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "resource_ids_with_suffix.json") + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Example": { + ResourceIDs: map[string]sdkModels.ResourceID{ + "ServerId": { + Segments: []sdkModels.ResourceIDSegment{ + sdkModels.NewStaticValueResourceIDSegment("staticSubscriptions", "subscriptions"), + sdkModels.NewSubscriptionIDResourceIDSegment("subscriptionId"), + sdkModels.NewStaticValueResourceIDSegment("staticResourceGroups", "resourceGroups"), + sdkModels.NewResourceGroupNameResourceIDSegment("resourceGroupName"), + sdkModels.NewStaticValueResourceIDSegment("staticProviders", "providers"), + sdkModels.NewResourceProviderResourceIDSegment("staticMicrosoftSomeResourceProvider", "Microsoft.SomeResourceProvider"), + sdkModels.NewStaticValueResourceIDSegment("staticServers", "servers"), + sdkModels.NewUserSpecifiedResourceIDSegment("serverName", "serverName"), + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "HEAD", + ResourceIDName: pointer.To("ServerId"), + URISuffix: pointer.To("/someOperation"), + }, + }, + }, + }, + } + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseResourceIdWithResourceIdAndUriSuffixForMultipleUris(t *testing.T) { + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "resource_ids_with_suffix_multiple_uris.json") + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Example": { + ResourceIDs: map[string]sdkModels.ResourceID{ + "ServerId": { + Segments: []sdkModels.ResourceIDSegment{ + sdkModels.NewStaticValueResourceIDSegment("staticSubscriptions", "subscriptions"), + sdkModels.NewSubscriptionIDResourceIDSegment("subscriptionId"), + sdkModels.NewStaticValueResourceIDSegment("staticResourceGroups", "resourceGroups"), + sdkModels.NewResourceGroupNameResourceIDSegment("resourceGroupName"), + sdkModels.NewStaticValueResourceIDSegment("staticProviders", "providers"), + sdkModels.NewResourceProviderResourceIDSegment("staticMicrosoftSomeResourceProvider", "Microsoft.SomeResourceProvider"), + sdkModels.NewStaticValueResourceIDSegment("staticServers", "servers"), + sdkModels.NewUserSpecifiedResourceIDSegment("serverName", "serverName"), + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Restart": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "HEAD", + ResourceIDName: pointer.To("ServerId"), + URISuffix: pointer.To("/restart"), + }, + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "HEAD", + ResourceIDName: pointer.To("ServerId"), + URISuffix: pointer.To("/someOperation"), + }, + "TopLevel": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "HEAD", + ResourceIDName: pointer.To("ServerId"), + }, + }, + }, + }, + } + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseResourceIdContainingResourceProviderShouldGetTitleCased(t *testing.T) { + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "resource_ids_lowercased_resource_provider.json") + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Example": { + ResourceIDs: map[string]sdkModels.ResourceID{ + "ServerId": { + Segments: []sdkModels.ResourceIDSegment{ + sdkModels.NewStaticValueResourceIDSegment("staticSubscriptions", "subscriptions"), + sdkModels.NewSubscriptionIDResourceIDSegment("subscriptionId"), + sdkModels.NewStaticValueResourceIDSegment("staticResourceGroups", "resourceGroups"), + sdkModels.NewResourceGroupNameResourceIDSegment("resourceGroupName"), + sdkModels.NewStaticValueResourceIDSegment("staticProviders", "providers"), + sdkModels.NewResourceProviderResourceIDSegment("staticMicrosoftSomeResourceProvider", "Microsoft.SomeResourceProvider"), + sdkModels.NewStaticValueResourceIDSegment("staticServers", "servers"), + sdkModels.NewUserSpecifiedResourceIDSegment("serverName", "serverName"), + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "HEAD", + ResourceIDName: pointer.To("ServerId"), + }, + }, + }, + }, + } + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseResourceIdContainingTheSameResourceIdWithDifferentSegments(t *testing.T) { + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "resource_ids_same_id_different_segment_casing.json") + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Example": { + ResourceIDs: map[string]sdkModels.ResourceID{ + "VirtualMachineId": { + Segments: []sdkModels.ResourceIDSegment{ + sdkModels.NewStaticValueResourceIDSegment("staticSubscriptions", "subscriptions"), + sdkModels.NewSubscriptionIDResourceIDSegment("subscriptionId"), + sdkModels.NewStaticValueResourceIDSegment("staticResourceGroups", "resourceGroups"), + sdkModels.NewResourceGroupNameResourceIDSegment("resourceGroupName"), + sdkModels.NewStaticValueResourceIDSegment("staticProviders", "providers"), + sdkModels.NewResourceProviderResourceIDSegment("staticMicrosoftSomeResourceProvider", "Microsoft.SomeResourceProvider"), + sdkModels.NewStaticValueResourceIDSegment("staticVirtualMachines", "virtualMachines"), + sdkModels.NewUserSpecifiedResourceIDSegment("machineName", "machineName"), + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Restart": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "HEAD", + ResourceIDName: pointer.To("VirtualMachineId"), + URISuffix: pointer.To("/restart"), + }, + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "HEAD", + ResourceIDName: pointer.To("VirtualMachineId"), + }, + }, + }, + }, + } + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseResourceIdContainingTheSegmentsNamedTheSame(t *testing.T) { + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "resource_ids_multiple_segments_same_name.json") + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Example": { + ResourceIDs: map[string]sdkModels.ResourceID{ + "BillingPeriodId": { + Segments: []sdkModels.ResourceIDSegment{ + sdkModels.NewStaticValueResourceIDSegment("staticProviders", "providers"), + sdkModels.NewResourceProviderResourceIDSegment("staticMicrosoftManagement", "Microsoft.Management"), + sdkModels.NewStaticValueResourceIDSegment("staticManagementGroups", "managementGroups"), + sdkModels.NewUserSpecifiedResourceIDSegment("managementGroupId", "managementGroupId"), + sdkModels.NewStaticValueResourceIDSegment("staticProviders2", "providers"), + sdkModels.NewResourceProviderResourceIDSegment("staticMicrosoftBilling", "Microsoft.Billing"), + sdkModels.NewStaticValueResourceIDSegment("staticBillingPeriods", "billingPeriods"), + sdkModels.NewUserSpecifiedResourceIDSegment("billingPeriodName", "billingPeriodName"), + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "HEAD", + ResourceIDName: pointer.To("BillingPeriodId"), + URISuffix: pointer.To("/Microsoft.Consumption/aggregatedCost"), + }, + }, + }, + }, + } + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) +} + +func TestParseResourceIdsWhereTheSameUriContainsDifferentConstantValuesPerOperation(t *testing.T) { + // Whilst a URI may contain Constants, the values for those constants can differ per HTTP Operation + // as such we need to ensure that these are output as different Resource ID types + // + // In this case there are 2 constants defined, `PlanetNames` and `PlanetEarth` - however `PlanetEarth` is a constant + // with a single value - therefore `PlanetEarth` will be removed. + // The Operation `HEAD /galaxies/{galaxyName}/hello/{planetName}` should remain as-is + // The Operation `DELETE /galaxies/{galaxyName}/hello/{planetName}` should be transformed to `/galaxies/{galaxyName}/hello/Earth` + // This means we should end up with 2 IDs, GalaxyId and PlanetId (with Earth and Mars the Constant PlanetNames in PlanetId) + // + // Experience has shown this is eventually consistent, maybe 100x is overkill, but it'll do for now. + for i := 0; i < 100; i++ { + t.Logf("iteration %d", i) + + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "resource_ids_same_uri_different_constant_values_per_operation.json") + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Hello": { + Constants: map[string]sdkModels.SDKConstant{ + "PlanetNames": { + Type: sdkModels.StringSDKConstantType, + Values: map[string]string{ + "Earth": "Earth", + "Mars": "Mars", + }, + }, + }, + ResourceIDs: map[string]sdkModels.ResourceID{ + "GalaxyId": { + Segments: []sdkModels.ResourceIDSegment{ + sdkModels.NewStaticValueResourceIDSegment("staticGalaxies", "galaxies"), + sdkModels.NewUserSpecifiedResourceIDSegment("galaxyName", "galaxyName"), + }, + }, + "PlanetId": { + ConstantNames: []string{ + "PlanetNames", + }, + Segments: []sdkModels.ResourceIDSegment{ + sdkModels.NewStaticValueResourceIDSegment("staticGalaxies", "galaxies"), + sdkModels.NewUserSpecifiedResourceIDSegment("galaxyName", "galaxyName"), + sdkModels.NewStaticValueResourceIDSegment("staticHello", "hello"), + sdkModels.NewConstantResourceIDSegment("planetName", "PlanetNames", "Earth"), + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "Delete": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "DELETE", + ResourceIDName: pointer.To("GalaxyId"), + URISuffix: pointer.To("/hello/Earth"), + }, + "Head": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "HEAD", + ResourceIDName: pointer.To("PlanetId"), + }, + }, + }, + }, + } + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) + } +} + +func TestParseResourceIdsCommon(t *testing.T) { + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "resource_ids_common.json") + if err != nil { + t.Fatalf("parsing: %+v", err) + } + + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Example": { + ResourceIDs: map[string]sdkModels.ResourceID{ + "ManagementGroupId": { + CommonIDAlias: pointer.To("ManagementGroup"), + Segments: []sdkModels.ResourceIDSegment{ + sdkModels.NewStaticValueResourceIDSegment("providers", "providers"), + sdkModels.NewResourceProviderResourceIDSegment("resourceProvider", "Microsoft.Management"), + sdkModels.NewStaticValueResourceIDSegment("managementGroups", "managementGroups"), + sdkModels.NewUserSpecifiedResourceIDSegment("groupId", "groupId"), + }, + }, + "ResourceGroupId": { + CommonIDAlias: pointer.To("ResourceGroup"), + Segments: []sdkModels.ResourceIDSegment{ + sdkModels.NewStaticValueResourceIDSegment("subscriptions", "subscriptions"), + sdkModels.NewSubscriptionIDResourceIDSegment("subscriptionId"), + sdkModels.NewStaticValueResourceIDSegment("resourceGroups", "resourceGroups"), + sdkModels.NewResourceGroupNameResourceIDSegment("resourceGroupName"), + }, + }, + "ScopeId": { + CommonIDAlias: pointer.To("Scope"), + Segments: []sdkModels.ResourceIDSegment{ + sdkModels.NewScopeResourceIDSegment("scope"), + }, + }, + "SubscriptionId": { + CommonIDAlias: pointer.To("Subscription"), + Segments: []sdkModels.ResourceIDSegment{ + sdkModels.NewStaticValueResourceIDSegment("subscriptions", "subscriptions"), + sdkModels.NewSubscriptionIDResourceIDSegment("subscriptionId"), + }, + }, + "UserAssignedIdentityId": { + CommonIDAlias: pointer.To("UserAssignedIdentity"), + 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"), + }, + }, + }, + Operations: map[string]sdkModels.SDKOperation{ + "GetManagementGroup": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "HEAD", + ResourceIDName: pointer.To("ManagementGroupId"), + }, + "GetResourceGroup": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "HEAD", + ResourceIDName: pointer.To("ResourceGroupId"), + }, + "GetScope": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "HEAD", + ResourceIDName: pointer.To("ScopeId"), + }, + "GetSubscription": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "HEAD", + ResourceIDName: pointer.To("SubscriptionId"), + }, + "GetUserAssignedIdentity": { + ContentType: "application/json", + ExpectedStatusCodes: []int{200}, + Method: "HEAD", + ResourceIDName: pointer.To("UserAssignedIdentityId"), + }, + }, + }, + }, + } + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_supplementary_data.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_supplementary_data.go new file mode 100644 index 00000000000..03183957aac --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_supplementary_data.go @@ -0,0 +1,55 @@ +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/constants" + parserModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models" +) + +func (p *apiDefinitionsParser) SupplementaryData() (*parserModels.SupplementaryData, error) { + result := parserModels.SupplementaryData{ + Constants: map[string]sdkModels.SDKConstant{}, + Models: map[string]sdkModels.SDKModel{}, + } + + for objectName, definition := range p.context.SwaggerSpecRaw.Definitions { + var parsedConstant *constants.ParsedConstant + parsedModel, err := p.context.ParseModel(objectName, definition, false) + if err != nil { + var err2 error + parsedConstant, err2 = p.context.ParseConstant(objectName, definition) + if err != nil { + return nil, fmt.Errorf("failed to parse %q as a Constant or a Model. Constant error: [%+v]. Model error: [%+v]", objectName, err, err2) + } + } + if parsedModel != nil { + if err := result.AppendParseResult(*parsedModel); err != nil { + return nil, fmt.Errorf("appending model %q: %+v", objectName, err) + } + } + if parsedConstant != nil { + if err := result.AppendConstant(parsedConstant.Name, parsedConstant.Details); err != nil { + return nil, fmt.Errorf("appending constant %q: %+v", objectName, err) + } + } + } + + // FindNestedItemsYetToBeParsed takes ParseResult and so we need to shim this across + //shim := parserModels.ParseResult{ + // Constants: result.Constants, + // Models: result.Models, + //} + //// 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{}, shim) + //if err != nil { + // return nil, fmt.Errorf("finding nested items yet to be parsed: %+v", err) + //} + //if err := result.AppendParseResult(*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_supplementary_data_test.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_supplementary_data_test.go new file mode 100644 index 00000000000..7af46a9ae11 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_supplementary_data_test.go @@ -0,0 +1,26 @@ +package parser + +import ( + "testing" + + 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 TestParseSupplementaryData(t *testing.T) { + filePath := "/Users/tharvey/code/src/github.com/hashicorp/pandora/submodules/rest-api-specs/specification/datafactory/resource-manager/Microsoft.DataFactory/stable/2018-06-01/entityTypes/LinkedService.json" + parser, err := NewAPIDefinitionsParser(filePath, parserModels.SupplementaryData{ + Constants: make(map[string]sdkModels.SDKConstant), + Models: make(map[string]sdkModels.SDKModel), + }) + if err != nil { + t.Fatalf(err.Error()) + } + data, err := parser.SupplementaryData() + if err != nil { + t.Fatalf(err.Error()) + } + + t.Logf("Got %d Constants", len(data.Constants)) + t.Logf("Got %d Models", len(data.Models)) +} 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..6e933eb3a7d --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_swagger_tag.go @@ -0,0 +1,68 @@ +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/cleanup" + "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) (*sdkModels.APIResource, error) { + result := parserModels.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 := 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) + + // first Normalize the names, meaning `foo` -> `Foo` for consistency + resource = cleanup.NormalizeAPIResource(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..2ca9e650917 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_swagger_tags.go @@ -0,0 +1,29 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package parser + +import ( + "sort" + "strings" +) + +func (p *apiDefinitionsParser) ParseSwaggerTags() []string { + tags := make(map[string]struct{}) + + // first we go through, assuming there are tags + for _, operation := range p.context.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 +} 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 index 8848d117cbc..8de2be7e344 100644 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parser.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parser.go @@ -2,64 +2,22 @@ package parser import ( "fmt" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup" - sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds" - 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" + 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" ) -// ParseAPIVersion parses the information for this APIVersion from the AvailableDataSetForAPIVersion. -func ParseAPIVersion(serviceName string, input discoveryModels.AvailableDataSetForAPIVersion) (*sdkModels.APIVersion, error) { - apiResources := make(map[string]sdkModels.APIResource) - - // Firstly let's go through and process each of the Supplementary Files - for _, filePath := range input.FilePathsContainingSupplementaryData { - logging.Tracef("Processing Supplementary Data from file %q..", filePath) - resources, err := parseAPIResourcesWithin(filePath, apiResources) - if err != nil { - return nil, fmt.Errorf("parsing the APIResources from the Supplementary Data within %q: %+v", filePath, err) - } - - logging.Tracef("There are now %d APIResources", len(*resources)) - apiResources = *resources - logging.Tracef("Processing Supplementary Data from file %q - Completed.", filePath) - } - - // Next let's go through and process each of the API Definitions - for _, filePath := range input.FilePathsContainingAPIDefinitions { - logging.Tracef("Processing API Definitions from file %q..", filePath) - resources, err := parseAPIResourcesWithin(filePath, apiResources) - if err != nil { - return nil, fmt.Errorf("parsing the APIResources from the Supplementary Data within %q: %+v", filePath, err) - } - - logging.Tracef("There are now %d APIResources", len(*resources)) - apiResources = *resources - logging.Tracef("Processing API Definitions from file %q - Completed.", filePath) - } - - apiVersion := sdkModels.APIVersion{ - APIVersion: input.APIVersion, - Generate: true, - Preview: !input.ContainsStableAPIVersion, - Resources: apiResources, - Source: sdkModels.AzureRestAPISpecsSourceDataOrigin, - } +type apiDefinitionsParser struct { + context *parsingcontext.Context +} - // Next let's apply any data workarounds - logging.Debugf("Applying Data Workarounds..") - withFixesApplied, err := dataworkarounds.Apply(serviceName, apiVersion) +func NewAPIDefinitionsParser(filePath string, supplementaryData parserModels.SupplementaryData) (*apiDefinitionsParser, error) { + paringContext, err := parsingcontext.BuildFromFile(filePath, supplementaryData) if err != nil { - return nil, fmt.Errorf("applying Data Workarounds for Service %q / API Version %q: %+v", serviceName, input.APIVersion, err) + return nil, fmt.Errorf("building the parsing context: %+v", 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 + return &apiDefinitionsParser{ + context: paringContext, + }, 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..32ea56c2f71 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parsingcontext/build.go @@ -0,0 +1,115 @@ +package parsingcontext + +import ( + "fmt" + "path/filepath" + "strings" + + "github.com/go-openapi/analysis" + "github.com/go-openapi/loads" + "github.com/go-openapi/spec" + parserModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models" +) + +func BuildFromFile(filePath string, supplementaryData parserModels.SupplementaryData) (*Context, error) { + directory := filepath.Dir(filePath) + + // 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, filePath) + 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, filePath) + 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) + } + + swaggerSpecExpanded := expandedSwaggerDoc.Analyzer + swaggerSpecExpandedRaw := expandedSwaggerDoc.Spec() + return &Context{ + FilePath: filePath, + SupplementaryData: supplementaryData, + 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, fmt.Sprintf("%c", filepath.Separator))) != 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/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..0391877e7f8 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parsingcontext/context.go @@ -0,0 +1,26 @@ +package parsingcontext + +import ( + "github.com/go-openapi/analysis" + "github.com/go-openapi/spec" + parserModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models" +) + +// 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 + + SupplementaryData parserModels.SupplementaryData + + // SwaggerSpecExpanded contains 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 + + // TODO: confirm the the need for these two + + SwaggerSpecRaw *spec.Swagger + SwaggerSpecExtendedRaw *spec.Swagger +} 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..97542de0fa7 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parsingcontext/helpers.go @@ -0,0 +1,274 @@ +package parsingcontext + +import ( + "fmt" + "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" +) + +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 +} + +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 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 := 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, false) + 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{}, 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 := c.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 := 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 := 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 := 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 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 := 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 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 (c *Context) objectsUsedByModel(modelName string, model sdkModels.SDKModel) (*[]string, error) { + typeNames := make(map[string]struct{}, 0) + + for _, field := range model.Fields { + 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 +} 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..5f85ab34283 --- /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.SwaggerSpecExtendedRaw.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..3af9560fbc7 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parsingcontext/parse_model.go @@ -0,0 +1,496 @@ +package parsingcontext + +import ( + "fmt" + "github.com/hashicorp/go-azure-helpers/lang/pointer" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/constants" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/logging" + "strings" + + "github.com/go-openapi/spec" + 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 (c *Context) ParseModel(name string, input spec.Schema, loadParentType bool) (*parserModels.ParseResult, error) { + 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 all of the fields for this model + fields, nestedResult, err := c.fieldsForModel(name, input, result, loadParentType) + 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 := c.modelDetailsFromObject(name, input, *fields, loadParentType) + 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, loadParentType bool) (*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, + } + + // first get the object definition + parsingModel := false + objectDefinition, nestedResult, err := c.ParseObjectDefinition(modelName, propertyName, &value, result, parsingModel, loadParentType) + 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, loadParentType) + 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, loadParentType) + 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, loadParentType) + 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 (c *Context) fieldsForModel(modelName string, input spec.Schema, known parserModels.ParseResult, loadParentType bool) (*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, true) + 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, loadParentType) + 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, loadParentType) + 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, loadParentType bool) (*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 + } + } + + // this would be an Implementation + if v, ok := input.Extensions.GetString("x-ms-discriminator-value"); ok { + details.DiscriminatedValue = &v + + // NOTE: we want the option to conditionally load the parent type to be able to load the Supplementary Data + // first (since these may only contain the Discriminated Implementations and not the Parent Type). + if loadParentType { + // so we need to find the ancestor details + 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 + } + + // 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 (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 +} + +func (c *Context) findOrphanedDiscriminatedModels(serviceName string) (*parserModels.ParseResult, error) { + result := parserModels.ParseResult{ + Constants: map[string]sdkModels.SDKConstant{}, + Models: map[string]sdkModels.SDKModel{}, + } + + for modelName, definition := range c.SwaggerSpecRaw.Definitions { + if _, ok := definition.Extensions.GetString("x-ms-discriminator-value"); ok { + details, err := c.ParseModel(modelName, definition, false) + 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 := c.FindAncestorType(definition) + if err != nil { + return nil, fmt.Errorf("determining ancestor type for model %q: %+v", modelName, err) + } + + details, err := c.ParseModel(modelName, definition, false) + 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 := c.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/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..5d000355d0b --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parsingcontext/parse_object_definition.go @@ -0,0 +1,375 @@ +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, loadParentType 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: &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 + objectDefinition, nestedResult, err := c.ParseObjectDefinition(*objectName, propertyName, topLevelObject, knownIncludingPlaceholder, true, loadParentType) + 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 c.ParseObjectDefinition(inheritedModel.Title, propertyName, &inheritedModel, result, true, loadParentType) + } + } + + // 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, loadParentType) + 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 := c.ParseObjectDefinition(innerModelName, propertyName, input.AdditionalProperties.Schema, result, true, loadParentType) + 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, loadParentType) + 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, true) + 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 (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..1c35ea0d806 --- /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.SwaggerSpecRaw.Definitions { + if strings.EqualFold(modelName, name) { + return &model, nil + } + } + + for modelName, model := range c.SwaggerSpecExtendedRaw.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/components/parser/resourceids/common_ids.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/common_ids.go similarity index 61% 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/common_ids.go index 2fbc692982f..0693d97a3c1 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/common_ids.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/common_ids.go @@ -4,9 +4,10 @@ package resourceids import ( - "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/resourceids/commonids" + "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 { @@ -16,9 +17,9 @@ func switchOutCommonResourceIDsAsNeeded(input []sdkModels.ResourceID) []sdkModel // 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 ResourceIdsMatch(commonId.ID(), value) { + if comparison.ResourceIDsMatch(commonId.ID(), value) { value = commonId.ID() - value.ExampleValue = helpers.DisplayValueForResourceID(value) + value.ExampleValue = sdkHelpers.DisplayValueForResourceID(value) break } } diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_app_service.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_app_service.go similarity index 100% rename from tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_app_service.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_app_service.go diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 100% rename from tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 100% rename from tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 100% rename from tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_availability_set.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_availability_set.go similarity index 100% rename from tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_availability_set.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_availability_set.go diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_bot_service.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_bot_service.go similarity index 100% rename from tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_bot_service.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_bot_service.go diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 100% rename from tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 100% rename from tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 100% rename from tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 100% rename from tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 100% rename from tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_dedicated_host.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_dedicated_host.go similarity index 100% rename from tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_dedicated_host.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_dedicated_host.go diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 100% rename from tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 100% rename from tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 100% rename from tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_hdinsight_cluster.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_hdinsight_cluster.go similarity index 100% rename from tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_hdinsight_cluster.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_hdinsight_cluster.go diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 100% rename from tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 100% rename from tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 100% rename from tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_key_vault.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_key_vault.go similarity index 100% rename from tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_key_vault.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_key_vault.go diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 100% rename from tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 100% rename from tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 100% rename from tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_kubernetes_cluster.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_kubernetes_cluster.go similarity index 100% rename from tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_kubernetes_cluster.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_kubernetes_cluster.go diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_kubernetes_fleet.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_kubernetes_fleet.go similarity index 100% rename from tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_kubernetes_fleet.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_kubernetes_fleet.go diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_kusto_cluster.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_kusto_cluster.go similarity index 100% rename from tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_kusto_cluster.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_kusto_cluster.go diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_kusto_database.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_kusto_database.go similarity index 100% rename from tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_kusto_database.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_kusto_database.go diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_managed_disk.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_managed_disk.go similarity index 100% rename from tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_managed_disk.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_managed_disk.go diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_management_group.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_management_group.go similarity index 100% rename from tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_management_group.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_management_group.go diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_network_interface.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_network_interface.go similarity index 100% rename from tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_network_interface.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_network_interface.go diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 100% rename from tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 100% rename from tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 100% rename from tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 100% rename from tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_resource_group.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_resource_group.go similarity index 100% rename from tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_resource_group.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_resource_group.go diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_scope.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_scope.go similarity index 100% rename from tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_scope.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_scope.go diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 100% rename from tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 100% rename from tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_sql_database.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_sql_database.go similarity index 100% rename from tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_sql_database.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_sql_database.go diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 100% rename from tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 100% rename from tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 100% rename from tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_sql_server.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_sql_server.go similarity index 100% rename from tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_sql_server.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_sql_server.go diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_storage_account.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_storage_account.go similarity index 100% rename from tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_storage_account.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_storage_account.go diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_storage_container.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_storage_container.go similarity index 100% rename from tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_storage_container.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_storage_container.go diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_subnet.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_subnet.go similarity index 100% rename from tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_subnet.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_subnet.go diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_subscription.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_subscription.go similarity index 100% rename from tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_subscription.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_subscription.go diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 100% rename from tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 100% rename from tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 100% rename from tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 100% rename from tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 100% rename from tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_virtual_network.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_virtual_network.go similarity index 100% rename from tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_id_virtual_network.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_id_virtual_network.go diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 100% rename from tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 100% rename from tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 100% rename from tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 100% rename from tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 100% rename from tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 100% rename from tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 100% rename from tools/importer-rest-api-specs/components/parser/resourceids/commonids/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 diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_ids.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_ids.go similarity index 100% rename from tools/importer-rest-api-specs/components/parser/resourceids/commonids/common_ids.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/common_ids.go diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/commonids/interface.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/interface.go similarity index 100% rename from tools/importer-rest-api-specs/components/parser/resourceids/commonids/interface.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/commonids/interface.go 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 91% 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 b419f455d28..7e73a3b1d8a 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 @@ -5,13 +5,14 @@ package resourceids import ( "fmt" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/components/parser/resourceids/commonids" "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/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) { @@ -50,7 +51,7 @@ func generateNamesForResourceIds(input []sdkModels.ResourceID, uriToResourceId m resourceId := uniqueUris[uri] for i, commonIdType := range commonids.CommonIDTypes { commonId := commonIdType.ID() - if ResourceIdsMatch(commonId, resourceId) { + if comparison.ResourceIDsMatch(commonId, resourceId) { if commonId.CommonIDAlias == nil { return nil, fmt.Errorf("the Common ID %d had no Alias: %+v", i, commonId) } @@ -107,7 +108,7 @@ 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) @@ -127,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 } @@ -156,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 } @@ -164,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 } @@ -182,7 +183,7 @@ 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 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 98% 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..2bc716c2df0 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" ) @@ -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"), }, @@ -544,7 +544,7 @@ func TestResourceIdNamingConflictingWithUpdatingOperation(t *testing.T) { } 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) diff --git a/tools/importer-rest-api-specs/components/parser/resourceids/helpers.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/helpers.go similarity index 50% rename from tools/importer-rest-api-specs/components/parser/resourceids/helpers.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/helpers.go index 2713d15ba9f..af49fb60ffa 100644 --- a/tools/importer-rest-api-specs/components/parser/resourceids/helpers.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/helpers.go @@ -64,64 +64,3 @@ func normalizedResourceId(segments []sdkModels.ResourceIDSegment) string { 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/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 87% 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..4309d15f7b6 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 } 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 98% 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 1344b1a2ffb..4af257d7fec 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,10 +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/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" ) @@ -64,7 +64,7 @@ func (p *Parser) parseResourceIdFromOperation(uri string, operation *spec.Operat // TODO: document this segments := make([]sdkModels.ResourceIDSegment, 0) - result := internal.ParseResult{ + result := parserModels.ParseResult{ Constants: map[string]sdkModels.SDKConstant{}, } 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 95% 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 00db4acd75c..2d90b0205b0 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" ) @@ -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, 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 92% 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..5af84a4254b 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 @@ -4,11 +4,11 @@ package parser 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 TestParsingOperationsUsingTheSameSwaggerTagInDifferentCasings(t *testing.T) { @@ -17,9 +17,8 @@ func TestParsingOperationsUsingTheSameSwaggerTagInDifferentCasings(t *testing.T) 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,7 +82,7 @@ func TestParsingOperationsUsingTheSameSwaggerTagInDifferentCasings(t *testing.T) }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParsingOperationsOnResources(t *testing.T) { @@ -92,9 +91,8 @@ func TestParsingOperationsOnResources(t *testing.T) { 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/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/todo_parse_swagger_file.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testhelpers/todo_parse_swagger_file.go new file mode 100644 index 00000000000..25058194ea6 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testhelpers/todo_parse_swagger_file.go @@ -0,0 +1,41 @@ +package testhelpers + +import ( + "fmt" + "testing" + + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" +) + +func ParseSwaggerFileForTesting(t *testing.T, filePath string) (*sdkModels.APIVersion, error) { + /* + 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() + 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 + */ + + return nil, fmt.Errorf("TODO") +} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testhelpers/validate_result_matches.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testhelpers/validate_result_matches.go new file mode 100644 index 00000000000..5fb9891f68e --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testhelpers/validate_result_matches.go @@ -0,0 +1,248 @@ +package testhelpers + +import ( + "fmt" + "sort" + "testing" + + "github.com/hashicorp/go-azure-helpers/lang/pointer" + sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" +) + +func ValidateParsedSwaggerResultMatches(t *testing.T, expected sdkModels.APIVersion, actual *sdkModels.APIVersion) { + if actual == nil { + t.Fatal("`actual` was nil") + } + 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) +} + +func validateParsedApiResourceMatches(t *testing.T, expected, actual sdkModels.APIResource, apiResourceName string) { + t.Logf("Validating API Resource %q..", apiResourceName) + // Validate each of the maps matches what we're expecting + validateMapsMatch(t, expected.Constants, actual.Constants, "Constants", validateParsedConstantsMatch) + validateMapsMatch(t, expected.Models, actual.Models, "Models", validateParsedSDKModelsMatch) + validateMapsMatch(t, expected.Operations, actual.Operations, "Operations", validateParsedOperationsMatch) + validateMapsMatch(t, expected.ResourceIDs, actual.ResourceIDs, "Resource IDs", validateParsedResourceIDsMatch) +} + +func validateParsedConstantsMatch(t *testing.T, expected, actual sdkModels.SDKConstant, constantName string) { + if expected.Type != actual.Type { + t.Fatalf("expected Type to be %q but got %q for Constant %q", string(expected.Type), string(actual.Type), constantName) + } + validateMapsMatch(t, expected.Values, actual.Values, "Values", validateStringsMatch) +} + +func validateParsedSDKFieldsMatch(t *testing.T, expected, actual sdkModels.SDKField, fieldName string) { + if expected.JsonName != actual.JsonName { + t.Fatalf("expected `JsonName` to be %q but got %q for Field %q", expected.JsonName, actual.JsonName, fieldName) + } + // TODO: @tombuildsstuff: reenable readonly support + //if expected.ReadOnly != actual.ReadOnly { + // t.Fatalf("expected `ReadOnly` to be %t but got %t for Field %q", expected.ReadOnly, actual.ReadOnly, fieldName) + //} + if expected.Required != actual.Required { + t.Fatalf("expected `Required` to be %t but got %t for Field %q", expected.Required, actual.Required, fieldName) + } + if expected.Sensitive != actual.Sensitive { + t.Fatalf("expected `Sensitive` to be %t but got %t for Field %q", expected.Sensitive, actual.Sensitive, fieldName) + } + + validateParsedObjectDefinitionsMatch(t, expected.ObjectDefinition, actual.ObjectDefinition, fieldName) +} + +func validateParsedSDKModelsMatch(t *testing.T, expected, actual sdkModels.SDKModel, modelName string) { + t.Logf("Validating Model %q...", modelName) + if pointer.From(expected.ParentTypeName) != pointer.From(actual.ParentTypeName) { + // NOTE: this should be nil when unset, otherwise a value + t.Fatalf("expected `ParentTypeName` to be %q but got %q for Model %q", pointer.From(expected.ParentTypeName), pointer.From(actual.ParentTypeName), modelName) + } + if pointer.From(expected.FieldNameContainingDiscriminatedValue) != pointer.From(actual.FieldNameContainingDiscriminatedValue) { + // NOTE: this should be nil when unset, otherwise a value + t.Fatalf("expected `FieldNameContainingDiscriminatedValue` to be %q but got %q for Model %q", pointer.From(expected.FieldNameContainingDiscriminatedValue), pointer.From(actual.FieldNameContainingDiscriminatedValue), modelName) + } + if pointer.From(expected.DiscriminatedValue) != pointer.From(actual.DiscriminatedValue) { + // NOTE: this should be nil when unset, otherwise a value + t.Fatalf("expected `DiscriminatedValue` to be %q but got %q for Model %q", pointer.From(expected.DiscriminatedValue), pointer.From(actual.DiscriminatedValue), modelName) + } + // TODO: thread description through once https://github.com/hashicorp/pandora/issues/3325 is completed + //if expected.Description != actual.Description { + // t.Fatalf("expected `Description` to be %q but got %q for Model %q", expected.Description, actual.Description, modelName) + //} + validateMapsMatch(t, expected.Fields, actual.Fields, "Fields", validateParsedSDKFieldsMatch) +} + +func validateParsedObjectDefinitionsMatch(t *testing.T, expected, actual sdkModels.SDKObjectDefinition, fieldName string) { + if expected.Type != actual.Type { + t.Fatalf("expected `Type` to be %q but got %q for Field %q", string(expected.Type), string(actual.Type), fieldName) + } + if pointer.From(expected.ReferenceName) != pointer.From(actual.ReferenceName) { + t.Fatalf("expected `ReferenceName` to be %q but got %q for Field %q", pointer.From(expected.ReferenceName), pointer.From(actual.ReferenceName), fieldName) + } + //// TODO: re-enable Min/Max/Unique + //if pointer.From(expected.Maximum) != pointer.From(actual.Maximum) { + // t.Fatalf("expected `Maximum` to be %d but got %d for Field %q", pointer.From(expected.Maximum), pointer.From(actual.Maximum), fieldName) + //} + //if pointer.From(expected.Minimum) != pointer.From(actual.Minimum) { + // t.Fatalf("expected `Minimum` to be %d but got %d for Field %q", pointer.From(expected.Minimum), pointer.From(actual.Minimum), fieldName) + //} + //if pointer.From(expected.UniqueItems) != pointer.From(actual.UniqueItems) { + // t.Fatalf("expected `UniqueItems` to be %t but got %t for Field %q", pointer.From(expected.UniqueItems), pointer.From(actual.UniqueItems), fieldName) + //} + + validateObjectsMatch(t, expected.NestedItem, actual.NestedItem, "NestedItem", validateParsedObjectDefinitionsMatch) +} + +func validateParsedOperationsMatch(t *testing.T, expected, actual sdkModels.SDKOperation, operationName string) { + t.Logf("Validating Operation %q..", operationName) + if expected.ContentType != actual.ContentType { + t.Fatalf("expected `ContentType` to be %q but got %q for Operation %q", expected.ContentType, actual.ContentType, operationName) + } + validateSlicesMatch(t, expected.ExpectedStatusCodes, actual.ExpectedStatusCodes, "ExpectedStatusCodes", validateIntegersMatch) + if pointer.From(expected.FieldContainingPaginationDetails) != pointer.From(actual.FieldContainingPaginationDetails) { + t.Fatalf("expected `FieldContainingPaginationDetails` to be %q but got %q for Operation %q", pointer.From(expected.FieldContainingPaginationDetails), pointer.From(actual.FieldContainingPaginationDetails), operationName) + } + if expected.LongRunning != actual.LongRunning { + t.Fatalf("expected `LongRunning` to be %t but got %t for Operation %q", expected.LongRunning, actual.LongRunning, operationName) + } + if expected.Method != actual.Method { + t.Fatalf("expected `Method` to be %q but got %q for Operation %q", expected.Method, actual.Method, operationName) + } + validateMapsMatch(t, expected.Options, actual.Options, "Options", validateParsedOptionsMatch) + if pointer.From(expected.ResourceIDName) != pointer.From(actual.ResourceIDName) { + t.Fatalf("expected `ResourceIDName` to be %q but got %q for Operation %q", pointer.From(expected.ResourceIDName), pointer.From(actual.ResourceIDName), operationName) + } + validateObjectsMatch(t, expected.RequestObject, actual.RequestObject, "RequestObject", validateParsedObjectDefinitionsMatch) + validateObjectsMatch(t, expected.ResponseObject, actual.ResponseObject, "ResponseObject", validateParsedObjectDefinitionsMatch) + if pointer.From(expected.URISuffix) != pointer.From(actual.URISuffix) { + t.Fatalf("expected `URISuffix` to be %q but got %q for Operation %q", pointer.From(expected.URISuffix), pointer.From(actual.URISuffix), operationName) + } +} + +func validateParsedOptionsMatch(t *testing.T, expected, actual sdkModels.SDKOperationOption, optionName string) { + if pointer.From(expected.HeaderName) != pointer.From(actual.HeaderName) { + t.Errorf("expected `HeaderName` to be %q but got %q for Option %q", pointer.From(expected.HeaderName), pointer.From(actual.HeaderName), optionName) + } + if pointer.From(expected.QueryStringName) != pointer.From(actual.QueryStringName) { + t.Errorf("expected `QueryStringName` to be %q but got %q for Option %q", pointer.From(expected.QueryStringName), pointer.From(actual.QueryStringName), optionName) + } + if expected.Required != actual.Required { + t.Errorf("expected `Required` to be %t but got %t for Option %q", expected.Required, actual.Required, optionName) + } + validateParsedOptionsObjectDefinitionsMatch(t, expected.ObjectDefinition, actual.ObjectDefinition, "OptionObjectDefinition") +} + +func validateParsedOptionsObjectDefinitionsMatch(t *testing.T, expected, actual sdkModels.SDKOperationOptionObjectDefinition, fieldName string) { + if expected.Type != actual.Type { + t.Fatalf("expected `Type` to be %q but got %q for Field %q", string(expected.Type), string(actual.Type), fieldName) + } + if pointer.From(expected.ReferenceName) != pointer.From(actual.ReferenceName) { + t.Fatalf("expected `ReferenceName` to be %q but got %q for Field %q", pointer.From(expected.ReferenceName), pointer.From(actual.ReferenceName), fieldName) + } + + validateObjectsMatch(t, expected.NestedItem, actual.NestedItem, "NestedItem", validateParsedOptionsObjectDefinitionsMatch) +} + +func validateParsedResourceIDsMatch(t *testing.T, expected, actual sdkModels.ResourceID, resourceIdName string) { + if pointer.From(expected.CommonIDAlias) != pointer.From(actual.CommonIDAlias) { + t.Errorf("expected `CommonIDAlias` to be %q but got %q for Resource ID %q", pointer.From(expected.CommonIDAlias), pointer.From(actual.CommonIDAlias), resourceIdName) + } + validateSlicesMatch(t, expected.ConstantNames, actual.ConstantNames, "ConstantNames", validateStringsMatch) + validateSlicesMatch(t, expected.Segments, actual.Segments, "Segments", validateParsedResourceIDSegmentsMatch) +} + +func validateParsedResourceIDSegmentsMatch(t *testing.T, expected, actual sdkModels.ResourceIDSegment, segmentName string) { + if pointer.From(expected.ConstantReference) != pointer.From(actual.ConstantReference) { + t.Errorf("expected `ConstantReference` to be %q but got %q for %s", pointer.From(expected.ConstantReference), pointer.From(actual.ConstantReference), segmentName) + } + // TODO: enable once the SDK migration is completed + //if expected.ExampleValue != actual.ExampleValue { + // t.Errorf("expected `ExampleValue` to be %q but got %q for %s", expected.ExampleValue, actual.ExampleValue, segmentName) + //} + if pointer.From(expected.FixedValue) != pointer.From(actual.FixedValue) { + t.Errorf("expected `FixedValue` to be %q but got %q for %s", pointer.From(expected.FixedValue), pointer.From(actual.FixedValue), segmentName) + } + if expected.Name != actual.Name { + t.Errorf("expected `Name` to be %q but got %q for %s", expected.Name, actual.Name, segmentName) + } + if string(expected.Type) != string(actual.Type) { + t.Errorf("expected `Type` to be %q but got %q for %s", string(expected.Type), string(actual.Type), segmentName) + } +} + +func validateIntegersMatch(t *testing.T, expected, actual int, key string) { + if expected != actual { + t.Fatalf("expected %q to be %d but got %d", key, expected, actual) + } +} + +func validateStringsMatch(t *testing.T, expected, actual string, key string) { + if expected != actual { + t.Fatalf("expected %q to be %q but got %q", key, expected, actual) + } +} + +// Generic helper functions below this point + +func validateObjectsMatch[T any](t *testing.T, expected, actual *T, name string, innerAssert func(t *testing.T, expected T, actual T, key string)) { + if expected != nil && actual == nil { + t.Fatalf("expected %q to be %q but got nil for %q", name, expected, name) + } + if expected == nil && actual != nil { + t.Fatalf("expected %q to be nil but got %q for %q", name, actual, name) + } + if expected != nil && actual != nil { + innerAssert(t, *expected, *actual, name) + } +} + +func validateMapsMatch[T any](t *testing.T, expected, actual map[string]T, name string, innerAssert func(t *testing.T, expected T, actual T, key string)) { + if len(actual) != len(expected) { + t.Fatalf("expected there to be %d %q but got %d", len(expected), name, len(actual)) + } + resourceKeys := uniqueKeys[T](actual, expected) + for _, resourceKey := range resourceKeys { + expectedItem, ok := expected[resourceKey] + if !ok { + t.Fatalf("found an unexpected `%s` called %q", name, resourceKey) + } + actualItem, ok := actual[resourceKey] + if !ok { + t.Fatalf("expected a `%s` called %q but got nil", name, resourceKey) + } + + innerAssert(t, expectedItem, actualItem, resourceKey) + } +} + +func validateSlicesMatch[T any](t *testing.T, expected, actual []T, name string, innerAssert func(t *testing.T, expected T, actual T, key string)) { + if len(actual) != len(expected) { + t.Fatalf("expected there to be %d %q but got %d", len(expected), name, len(actual)) + } + for i := range expected { + expectedItem := expected[i] + actualItem := actual[i] + + innerAssert(t, expectedItem, actualItem, fmt.Sprintf("%s item %d", name, i)) + } +} + +func uniqueKeys[T any](first, second map[string]T) []string { + keys := make(map[string]struct{}) + for key := range first { + keys[key] = struct{}{} + } + for key := range second { + keys[key] = struct{}{} + } + + out := make([]string, 0) + for k := range keys { + out = append(out, k) + } + sort.Strings(out) + return out +} 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/builder.go b/tools/importer-rest-api-specs/internal/components/terraform/schema/builder.go index 84fd8c14203..2498ab6811c 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" @@ -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) @@ -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) @@ -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) @@ -265,7 +265,7 @@ func (b Builder) schemaFromTopLevelModel(input sdkModels.TerraformResourceDefini } 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 { @@ -468,7 +468,7 @@ func (b Builder) buildNestedModelDefinition(schemaModelName, topLevelModelName, 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) 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..9a85d35da13 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,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" 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" @@ -152,7 +152,7 @@ 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)) } } diff --git a/tools/importer-rest-api-specs/pipeline/run_importer.go b/tools/importer-rest-api-specs/pipeline/run_importer.go index f31b1559713..f1773b9d607 100644 --- a/tools/importer-rest-api-specs/pipeline/run_importer.go +++ b/tools/importer-rest-api-specs/pipeline/run_importer.go @@ -6,15 +6,14 @@ package pipeline import ( "fmt" "sort" + "strings" "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" ) @@ -132,14 +131,17 @@ func runImportForService(input RunInput, serviceName string, apiVersionsForServi } } - // Populate all of the data for this API Version.. - dataForApiVersions := make([]importerModels.AzureApiDefinition, 0) + // Populate all the data for this API Version.. + apiVersions := make(map[string]sdkModels.APIVersion, 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{}, + isPreview := isPreviewVersion(apiVersion) + dataForApiVersion := &sdkModels.APIVersion{ + APIVersion: apiVersion, + Generate: true, + Preview: isPreview, + Resources: make(map[string]sdkModels.APIResource), + Source: sdkModels.AzureRestAPISpecsSourceDataOrigin, } for _, v := range api { tempDataForApiVersion, err := task.parseDataForApiVersion(v) @@ -149,24 +151,24 @@ func runImportForService(input RunInput, serviceName string, apiVersionsForServi if tempDataForApiVersion == nil { continue } - for name, resource := range tempDataForApiVersion.Resources { + for name, resource := range *tempDataForApiVersion { dataForApiVersion.Resources[name] = resource } } - dataForApiVersions = append(dataForApiVersions, *dataForApiVersion) + apiVersions[apiVersion] = *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) + service := &sdkModels.Service{ + APIVersions: apiVersions, + Generate: true, + Name: serviceName, + ResourceProvider: resourceProvider, } // 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) + service, err := terraform.BuildForService(*service, terraformResourceDefinitions, input.ProviderPrefix, terraformPackageName) if err != nil { return fmt.Errorf("building the Terraform Details: %+v", err) } @@ -186,3 +188,19 @@ func runImportForService(input RunInput, serviceName string, apiVersionsForServi return nil } + +func isPreviewVersion(input string) bool { + lower := strings.ToLower(input) + // 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 +} diff --git a/tools/importer-rest-api-specs/pipeline/task_parse_data.go b/tools/importer-rest-api-specs/pipeline/task_parse_data.go index 6b5c10d204b..0aab144fc27 100644 --- a/tools/importer-rest-api-specs/pipeline/task_parse_data.go +++ b/tools/importer-rest-api-specs/pipeline/task_parse_data.go @@ -6,13 +6,13 @@ package pipeline import ( "fmt" + 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/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) { +func (pipelineTask) parseDataForApiVersion(input discovery.ServiceInput) (*map[string]sdkModels.APIResource, error) { logging.Tracef("Parsing Swagger Files..") data, err := parseSwaggerFiles(input) if err != nil { @@ -29,11 +29,15 @@ func (pipelineTask) parseDataForApiVersion(input discovery.ServiceInput) (*impor return data, nil } -func parseSwaggerFiles(input discovery.ServiceInput) (*importerModels.AzureApiDefinition, error) { +func parseSwaggerFiles(input discovery.ServiceInput) (*map[string]sdkModels.APIResource, 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 + if parseResult == nil { + return nil, nil + } + + return &parseResult.Resources, nil } From 2a0a72a45465ef7c1c2e17b42a018dfa2d6edeb1 Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Fri, 12 Jul 2024 11:54:30 +0200 Subject: [PATCH 20/58] `tools/importer-rest-api-specs`: fixing an issue where the `validate` command didn't pass the API Definitions Directory correctly --- tools/importer-rest-api-specs/internal/pipeline/validate.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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) } From 1f89c95c0005119ca4ec519910861e1d8f221051 Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Fri, 12 Jul 2024 12:26:04 +0200 Subject: [PATCH 21/58] `tools/importer-rest-api-specs`: title-casing the ReferenceName - which ensures that when we compare the ObjectDefinition from a previously parsed/currently parsed dataset we have matching values These are normalised post-parsing, so when comparing against the old data set we need to use TitleCase too, so we might as well do this here --- .../apidefinitions/parser/cleanup/normalize_sdk_objects.go | 3 ++- .../apidefinitions/parser/parsingcontext/parse_model.go | 2 +- .../parser/parsingcontext/parse_object_definition.go | 6 +++--- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/normalize_sdk_objects.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/normalize_sdk_objects.go index 0e549a6677c..f1601a58f65 100644 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/normalize_sdk_objects.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/normalize_sdk_objects.go @@ -6,6 +6,7 @@ package cleanup import ( "github.com/hashicorp/go-azure-helpers/lang/pointer" sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" + "strings" ) // NormalizeAPIResource works through the parsed AzureApiResource and ensures @@ -107,7 +108,7 @@ func NormalizeAPIResource(input sdkModels.APIResource) sdkModels.APIResource { func normalizeSDKObjectDefinition(input sdkModels.SDKObjectDefinition) sdkModels.SDKObjectDefinition { if input.ReferenceName != nil { normalized := NormalizeName(*input.ReferenceName) - input.ReferenceName = &normalized + input.ReferenceName = pointer.To(strings.Title(normalized)) } if input.NestedItem != 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 index 3af9560fbc7..259a002d0fa 100644 --- 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 @@ -222,7 +222,7 @@ func (c *Context) detailsForField(modelName string, propertyName string, value s result.Models[inlinedName] = *inlinedModelDetails // then swap out the reference objectDefinition.Type = sdkModels.ReferenceSDKObjectDefinitionType - objectDefinition.ReferenceName = &inlinedName + objectDefinition.ReferenceName = pointer.To(strings.Title(inlinedName)) } // Custom Types are determined once all the models/constants have been pulled out at the end 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 index 5d000355d0b..731a07b7085 100644 --- 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 @@ -34,7 +34,7 @@ func (c *Context) ParseObjectDefinition(modelName, propertyName string, input *s definition := sdkModels.SDKObjectDefinition{ Type: sdkModels.ReferenceSDKObjectDefinitionType, - ReferenceName: &constant.Name, + ReferenceName: pointer.To(strings.Title(constant.Name)), } //TODO: re-enable min/max/unique @@ -124,7 +124,7 @@ func (c *Context) ParseObjectDefinition(modelName, propertyName string, input *s definition := sdkModels.SDKObjectDefinition{ Type: sdkModels.ReferenceSDKObjectDefinitionType, - ReferenceName: &modelName, + ReferenceName: pointer.To(strings.Title(modelName)), } // TODO: re-enable min/max/unique //if input.MaxItems != nil { @@ -285,7 +285,7 @@ func (c *Context) parseDataFactoryCustomTypes(input *spec.Schema, known parserMo Type: sdkModels.ListSDKObjectDefinitionType, NestedItem: &sdkModels.SDKObjectDefinition{ Type: sdkModels.ReferenceSDKObjectDefinitionType, - ReferenceName: pointer.To(elementType), + ReferenceName: pointer.To(strings.Title(elementType)), }, }, &known, nil } From 1c27957bea6392576858dae6f8830b257c066a50 Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Fri, 12 Jul 2024 12:52:08 +0200 Subject: [PATCH 22/58] `tools/importer-rest-api-specs`: ensuring that TypeSpec examples are filtered out --- .../components/discovery/for_api_version.go | 28 +++++++++++-------- .../discovery/for_api_version_test.go | 1 + 2 files changed, 17 insertions(+), 12 deletions(-) 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..b33d896f807 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 @@ -24,6 +24,22 @@ 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, "examples") { + logging.Tracef("File contains examples, skipping..") + shouldIgnore = true + break + } + } + if shouldIgnore { + continue + } + filePathsForThisAPIVersion = append(filePathsForThisAPIVersion, filePath) } } @@ -53,18 +69,6 @@ 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 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..6e39cdccc8f 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", } From e19ecbc2f970e20dc50a2c54993a65dcd5709e25 Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Fri, 12 Jul 2024 13:11:21 +0200 Subject: [PATCH 23/58] `tools/importer-rest-api-specs`: trimming `*.json` from the filename to build the api resource name --- .../internal/components/apidefinitions/parse_api_resource.go | 1 + 1 file changed, 1 insertion(+) 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 index 11b764cb053..c6bba0eed31 100644 --- 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 @@ -63,6 +63,7 @@ func parseAPIResourcesFromFile(filePath, serviceName string, resourceProvider *s directory := filepath.Dir(filePath) fileName := strings.TrimPrefix(filePath, directory) fileName = strings.TrimPrefix(fileName, fmt.Sprintf("%c", filepath.Separator)) + fileName = strings.TrimSuffix(fileName, ".json") inferredTag := cleanup.PluraliseName(fileName) if resource != nil { From 84e391318ade2d7c688ce382a00780b6ad723f14 Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Fri, 12 Jul 2024 13:22:07 +0200 Subject: [PATCH 24/58] `tools/importer-rest-api-specs`: removing (most) of the old code path --- .../components/discovery/data.go | 148 -- .../components/discovery/discovery.go | 181 -- .../components/discovery/discovery_test.go | 216 -- .../components/discovery/interface.go | 62 - .../parser/ported_constants_test.go | 1007 --------- .../components/parser/ported_definition.go | 24 - .../components/parser/ported_flattener.go | 122 -- .../components/parser/ported_helpers.go | 80 - .../parser/ported_helpers_resource_id.go | 30 - .../components/parser/ported_helpers_test.go | 281 --- .../components/parser/ported_models.go | 510 ----- .../parser/ported_models_commonschema_test.go | 841 -------- .../ported_models_datafactorycustom_test.go | 200 -- .../parser/ported_models_dictionaries_test.go | 383 ---- .../ported_models_discriminators_test.go | 1424 ------------- .../components/parser/ported_models_test.go | 1821 ----------------- .../parser/ported_object_definition.go | 379 ---- .../components/parser/ported_operations.go | 582 ------ .../parser/ported_operations_test.go | 1588 -------------- .../components/parser/ported_parser.go | 107 - .../parser/ported_refactor_glue_logic.go | 24 - .../components/parser/ported_resource_ids.go | 21 - .../parser/ported_resource_ids_test.go | 751 ------- .../parser/ported_swagger_resources.go | 324 --- .../components/parser/ported_swagger_tags.go | 29 - .../parser/ported_swagger_tags_test.go | 175 -- .../parser/rewritten_load_and_parse.go | 145 -- .../internal/cmd/import.go | 41 +- .../internal/cmd/validate.go | 42 +- .../workaround_alertsmanagement.go | 8 +- .../importer-rest-api-specs/models/models.go | 24 - tools/importer-rest-api-specs/pipeline/git.go | 25 - .../pipeline/interface.go | 64 - .../pipeline/run_importer.go | 206 -- .../pipeline/run_validate_can_parse_data.go | 35 - .../run_validate_can_parse_data_test.go | 118 +- .../importer-rest-api-specs/pipeline/task.go | 7 - .../pipeline/task_parse_data.go | 43 - 38 files changed, 88 insertions(+), 11980 deletions(-) delete mode 100644 tools/importer-rest-api-specs/components/discovery/data.go delete mode 100644 tools/importer-rest-api-specs/components/discovery/discovery.go delete mode 100644 tools/importer-rest-api-specs/components/discovery/discovery_test.go delete mode 100644 tools/importer-rest-api-specs/components/discovery/interface.go delete mode 100644 tools/importer-rest-api-specs/components/parser/ported_constants_test.go delete mode 100644 tools/importer-rest-api-specs/components/parser/ported_definition.go delete mode 100644 tools/importer-rest-api-specs/components/parser/ported_flattener.go delete mode 100644 tools/importer-rest-api-specs/components/parser/ported_helpers.go delete mode 100644 tools/importer-rest-api-specs/components/parser/ported_helpers_resource_id.go delete mode 100644 tools/importer-rest-api-specs/components/parser/ported_helpers_test.go delete mode 100644 tools/importer-rest-api-specs/components/parser/ported_models.go delete mode 100644 tools/importer-rest-api-specs/components/parser/ported_models_commonschema_test.go delete mode 100644 tools/importer-rest-api-specs/components/parser/ported_models_datafactorycustom_test.go delete mode 100644 tools/importer-rest-api-specs/components/parser/ported_models_dictionaries_test.go delete mode 100644 tools/importer-rest-api-specs/components/parser/ported_models_discriminators_test.go delete mode 100644 tools/importer-rest-api-specs/components/parser/ported_models_test.go delete mode 100644 tools/importer-rest-api-specs/components/parser/ported_object_definition.go delete mode 100644 tools/importer-rest-api-specs/components/parser/ported_operations.go delete mode 100644 tools/importer-rest-api-specs/components/parser/ported_operations_test.go delete mode 100644 tools/importer-rest-api-specs/components/parser/ported_parser.go delete mode 100644 tools/importer-rest-api-specs/components/parser/ported_refactor_glue_logic.go delete mode 100644 tools/importer-rest-api-specs/components/parser/ported_resource_ids.go delete mode 100644 tools/importer-rest-api-specs/components/parser/ported_resource_ids_test.go delete mode 100644 tools/importer-rest-api-specs/components/parser/ported_swagger_resources.go delete mode 100644 tools/importer-rest-api-specs/components/parser/ported_swagger_tags.go delete mode 100644 tools/importer-rest-api-specs/components/parser/ported_swagger_tags_test.go delete mode 100644 tools/importer-rest-api-specs/components/parser/rewritten_load_and_parse.go delete mode 100644 tools/importer-rest-api-specs/pipeline/git.go delete mode 100644 tools/importer-rest-api-specs/pipeline/interface.go delete mode 100644 tools/importer-rest-api-specs/pipeline/run_importer.go delete mode 100644 tools/importer-rest-api-specs/pipeline/run_validate_can_parse_data.go delete mode 100644 tools/importer-rest-api-specs/pipeline/task.go delete mode 100644 tools/importer-rest-api-specs/pipeline/task_parse_data.go 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/ported_constants_test.go b/tools/importer-rest-api-specs/components/parser/ported_constants_test.go deleted file mode 100644 index 191ff3f2add..00000000000 --- a/tools/importer-rest-api-specs/components/parser/ported_constants_test.go +++ /dev/null @@ -1,1007 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package parser - -import ( - "testing" - - "github.com/hashicorp/go-azure-helpers/lang/pointer" - sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/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) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Example": { - Constants: map[string]sdkModels.SDKConstant{ - "TableNumber": { - Type: sdkModels.IntegerSDKConstantType, - Values: map[string]string{ - "One": "1", - "Two": "2", - "Three": "3", - "FourFiveSixSeven": "4567", - }, - }, - }, - Models: map[string]sdkModels.SDKModel{ - "ExampleWrapper": { - Fields: map[string]sdkModels.SDKField{ - "FavouriteTable": { - JsonName: "favouriteTable", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("TableNumber"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("ExampleWrapper"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/example"), - }, - }, - }, - }, - } - 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) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Example": { - Constants: map[string]sdkModels.SDKConstant{ - "TableNumber": { - Type: sdkModels.IntegerSDKConstantType, - Values: map[string]string{ - "First": "1", - "Second": "2", - "Third": "3", - }, - }, - }, - Models: map[string]sdkModels.SDKModel{ - "ExampleWrapper": { - Fields: map[string]sdkModels.SDKField{ - "FavouriteTable": { - JsonName: "favouriteTable", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("TableNumber"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("ExampleWrapper"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/example"), - }, - }, - }, - }, - } - 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) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Example": { - Constants: map[string]sdkModels.SDKConstant{ - "TableNumber": { - Type: sdkModels.IntegerSDKConstantType, - Values: map[string]string{ - "One": "1", - "Two": "2", - "Three": "3", - }, - }, - }, - Models: map[string]sdkModels.SDKModel{ - "ExampleWrapper": { - Fields: map[string]sdkModels.SDKField{ - "FavouriteTable": { - JsonName: "favouriteTable", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("TableNumber"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("ExampleWrapper"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/example"), - }, - }, - }, - }, - } - 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) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Example": { - Constants: map[string]sdkModels.SDKConstant{ - "TableNumber": { - Type: sdkModels.IntegerSDKConstantType, - Values: map[string]string{ - "One": "1", - "Two": "2", - "Three": "3", - }, - }, - }, - Models: map[string]sdkModels.SDKModel{ - "ExampleWrapper": { - Fields: map[string]sdkModels.SDKField{ - "FavouriteTable": { - JsonName: "favouriteTable", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("TableNumber"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("ExampleWrapper"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/example"), - }, - }, - }, - }, - } - 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) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Example": { - Constants: map[string]sdkModels.SDKConstant{ - "TableNumber": { - Type: sdkModels.IntegerSDKConstantType, - Values: map[string]string{ - "First": "1", - "Second": "2", - "Third": "3", - }, - }, - }, - Models: map[string]sdkModels.SDKModel{ - "ExampleWrapper": { - Fields: map[string]sdkModels.SDKField{ - "FavouriteTable": { - JsonName: "favouriteTable", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("TableNumber"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("ExampleWrapper"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/example"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseConstantsMultipleTypeEnums(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "constants_multiple_type_enums.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Example": { - Constants: map[string]sdkModels.SDKConstant{ - "AnimalType": { - Type: sdkModels.StringSDKConstantType, - Values: map[string]string{ - "Cat": "cat", - "Dog": "dog", - "Panda": "panda", - }, - }, - "PlanetType": { - Type: sdkModels.StringSDKConstantType, - Values: map[string]string{ - "Mercury": "mercury", - "Saturn": "saturn", - }, - }, - }, - Models: map[string]sdkModels.SDKModel{ - "Animal": { - Fields: map[string]sdkModels.SDKField{ - "Type": { - JsonName: "type", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("AnimalType"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - "Planet": { - Fields: map[string]sdkModels.SDKField{ - "Type": { - JsonName: "type", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("PlanetType"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Animal": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Animal"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/animal"), - }, - "Planet": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Planet"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/planet"), - }, - }, - }, - }, - } - 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) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Example": { - Constants: map[string]sdkModels.SDKConstant{ - "TableNumber": { - Type: sdkModels.IntegerSDKConstantType, - Values: map[string]string{ - "One": "1", - "Two": "2", - "Three": "3", - }, - }, - }, - Models: map[string]sdkModels.SDKModel{ - "ExampleWrapper": { - Fields: map[string]sdkModels.SDKField{ - "FavouriteTable": { - JsonName: "favouriteTable", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("TableNumber"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("ExampleWrapper"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/example"), - }, - }, - }, - }, - } - 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) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Example": { - Constants: map[string]sdkModels.SDKConstant{ - "TableNumber": { - Type: sdkModels.FloatSDKConstantType, - Values: map[string]string{ - "OnePointOne": "1.1", - "TwoPointTwo": "2.2", - "ThreePointThreeZeroZeroZeroFour": "3.30004", - }, - }, - }, - Models: map[string]sdkModels.SDKModel{ - "ExampleWrapper": { - Fields: map[string]sdkModels.SDKField{ - "FavouriteTable": { - JsonName: "favouriteTable", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("TableNumber"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("ExampleWrapper"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/example"), - }, - }, - }, - }, - } - 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) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Example": { - Constants: map[string]sdkModels.SDKConstant{ - "TableNumber": { - Type: sdkModels.FloatSDKConstantType, - Values: map[string]string{ - "OnePointOne": "1.1", - "TwoPointTwo": "2.2", - "ThreePointThreeZeroZeroZeroFour": "3.30004", - }, - }, - }, - Models: map[string]sdkModels.SDKModel{ - "ExampleWrapper": { - Fields: map[string]sdkModels.SDKField{ - "FavouriteTable": { - JsonName: "favouriteTable", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("TableNumber"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("ExampleWrapper"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/example"), - }, - }, - }, - }, - } - 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) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Example": { - Constants: map[string]sdkModels.SDKConstant{ - "TableNumber": { - Type: sdkModels.FloatSDKConstantType, - Values: map[string]string{ - "OnePointOne": "1.1", - "TwoPointTwo": "2.2", - "ThreePointThreeZeroZeroZeroFour": "3.30004", - }, - }, - }, - Models: map[string]sdkModels.SDKModel{ - "ExampleWrapper": { - Fields: map[string]sdkModels.SDKField{ - "FavouriteTable": { - JsonName: "favouriteTable", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("TableNumber"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("ExampleWrapper"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/example"), - }, - }, - }, - }, - } - 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) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Example": { - Constants: map[string]sdkModels.SDKConstant{ - "TableNumber": { - Type: sdkModels.FloatSDKConstantType, - Values: map[string]string{ - "OnePointOne": "1.1", - "TwoPointTwo": "2.2", - "ThreePointThreeZeroZeroZeroFour": "3.30004", - }, - }, - }, - Models: map[string]sdkModels.SDKModel{ - "ExampleWrapper": { - Fields: map[string]sdkModels.SDKField{ - "FavouriteTable": { - JsonName: "favouriteTable", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("TableNumber"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("ExampleWrapper"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/example"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseConstantsStringsTopLevel(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "constants_strings.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Example": { - Constants: map[string]sdkModels.SDKConstant{ - "AnimalType": { - Type: sdkModels.StringSDKConstantType, - Values: map[string]string{ - "Cat": "cat", - "Dog": "dog", - "Panda": "panda", - "Any": "*", - }, - }, - }, - Models: map[string]sdkModels.SDKModel{ - "ExampleWrapper": { - Fields: map[string]sdkModels.SDKField{ - "Type": { - JsonName: "type", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("AnimalType"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("ExampleWrapper"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/example"), - }, - }, - }, - }, - } - 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) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Example": { - Constants: map[string]sdkModels.SDKConstant{ - "AnimalType": { - Type: sdkModels.StringSDKConstantType, - Values: map[string]string{ - "Cat": "cat", - "Dog": "dog", - "Panda": "panda", - }, - }, - }, - Models: map[string]sdkModels.SDKModel{ - "ExampleWrapper": { - Fields: map[string]sdkModels.SDKField{ - "Type": { - JsonName: "type", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("AnimalType"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("ExampleWrapper"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/example"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseConstantsStringsInlined(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "constants_strings_inlined.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Example": { - Constants: map[string]sdkModels.SDKConstant{ - "AnimalType": { - Type: sdkModels.StringSDKConstantType, - Values: map[string]string{ - "Cat": "cat", - "Dog": "dog", - "Panda": "panda", - }, - }, - }, - Models: map[string]sdkModels.SDKModel{ - "ExampleWrapper": { - Fields: map[string]sdkModels.SDKField{ - "Type": { - JsonName: "type", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("AnimalType"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("ExampleWrapper"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/example"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseConstantsStringsInlinedAsNonStrings(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "constants_strings_inlined_as_non_strings.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Example": { - Constants: map[string]sdkModels.SDKConstant{ - "AnimalType": { - Type: sdkModels.StringSDKConstantType, - Values: map[string]string{ - "Cat": "cat", - "Dog": "dog", - "Panda": "panda", - }, - }, - }, - Models: map[string]sdkModels.SDKModel{ - "ExampleWrapper": { - Fields: map[string]sdkModels.SDKField{ - "Type": { - JsonName: "type", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("AnimalType"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("ExampleWrapper"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/example"), - }, - }, - }, - }, - } - 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) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Example": { - Constants: map[string]sdkModels.SDKConstant{ - "TableNumber": { - Type: sdkModels.StringSDKConstantType, - Values: map[string]string{ - "OnePointOne": "1.1", - "TwoPointTwo": "2.2", - "ThreePointThreeZeroZeroZeroFour": "3.30004", - }, - }, - }, - Models: map[string]sdkModels.SDKModel{ - "ExampleWrapper": { - Fields: map[string]sdkModels.SDKField{ - "FavouriteTable": { - JsonName: "favouriteTable", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("TableNumber"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("ExampleWrapper"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/example"), - }, - }, - }, - }, - } - 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) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Example": { - Constants: map[string]sdkModels.SDKConstant{ - "TableNumber": { - Type: sdkModels.FloatSDKConstantType, - Values: map[string]string{ - "OnePointOne": "1.1", - "TwoPointTwo": "2.2", - "ThreePointThreeZeroZeroZeroFour": "3.30004", - }, - }, - }, - Models: map[string]sdkModels.SDKModel{ - "ExampleWrapper": { - Fields: map[string]sdkModels.SDKField{ - "FavouriteTable": { - JsonName: "favouriteTable", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("TableNumber"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("ExampleWrapper"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/example"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseConstantsFromParameters(t *testing.T) { - result, err := ParseSwaggerFileForTesting(t, "constants_in_operation_parameters.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - if result == nil { - t.Fatal("result was nil") - } - if len(result.Resources) != 1 { - t.Fatalf("expected 1 resource but got %d", len(result.Resources)) - } - - resource, ok := result.Resources["ConstantFunTime"] - if !ok { - t.Fatal("the Resource 'ConstantFunTime' was not found") - } - - // sanity checking - if len(resource.Constants) != 1 { - t.Fatalf("expected 1 constant but got %d", len(resource.Constants)) - } - if len(resource.Models) != 0 { - t.Fatalf("expected 0 models but got %d", len(resource.Models)) - } - if len(resource.Operations) != 1 { - t.Fatalf("expected 1 operation but got %d", len(resource.Operations)) - } - if len(resource.ResourceIDs) != 1 { - t.Fatalf("expected 1 Resource ID but got %d", len(resource.ResourceIDs)) - } - - favouriteTable, ok := resource.Constants["MediaType"] - if !ok { - t.Fatalf("resource.Constants['TableNumber'] was not found") - } - if favouriteTable.Type != sdkModels.StringSDKConstantType { - t.Fatalf("expected resource.Constants['TableNumber'].Type to be 'String' but got %q", favouriteTable.Type) - } - if len(favouriteTable.Values) != 3 { - t.Fatalf("expected resource.Constants['TableNumber'] to have 3 values but got %d", len(favouriteTable.Values)) - } - v, ok := favouriteTable.Values["EightTrack"] - if !ok { - t.Fatalf("resource.Constants['MediaType'] didn't contain the key 'One'") - } - if v != "8Track" { - t.Fatalf("expected the value for resource.Constants['MediaType'].Values['EightTrack'] to be '8Track' but got %q", v) - } - v, ok = favouriteTable.Values["Cassette"] - if !ok { - t.Fatalf("resource.Constants['MediaType'] didn't contain the key 'Cassette'") - } - if v != "Cassette" { - t.Fatalf("expected the value for resource.Constants['MediaType'].Values['Cassette'] to be 'Cassette' but got %q", v) - } - v, ok = favouriteTable.Values["Vinyl"] - if !ok { - t.Fatalf("resource.Constants['MediaType'] didn't contain the key 'Vinyl'") - } - if v != "Vinyl" { - t.Fatalf("expected the value for resource.Constants['MediaType'].Values['Vinyl'] to be 'Vinyl' but got %q", v) - } -} diff --git a/tools/importer-rest-api-specs/components/parser/ported_definition.go b/tools/importer-rest-api-specs/components/parser/ported_definition.go deleted file mode 100644 index 79bc8d0dcc5..00000000000 --- a/tools/importer-rest-api-specs/components/parser/ported_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/ported_flattener.go b/tools/importer-rest-api-specs/components/parser/ported_flattener.go deleted file mode 100644 index 108b534f5a6..00000000000 --- a/tools/importer-rest-api-specs/components/parser/ported_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/ported_helpers.go b/tools/importer-rest-api-specs/components/parser/ported_helpers.go deleted file mode 100644 index 5477fd96b15..00000000000 --- a/tools/importer-rest-api-specs/components/parser/ported_helpers.go +++ /dev/null @@ -1,80 +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/internal/components/apidefinitions/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 -} - -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 -} diff --git a/tools/importer-rest-api-specs/components/parser/ported_helpers_resource_id.go b/tools/importer-rest-api-specs/components/parser/ported_helpers_resource_id.go deleted file mode 100644 index c9e3d5e0ad8..00000000000 --- a/tools/importer-rest-api-specs/components/parser/ported_helpers_resource_id.go +++ /dev/null @@ -1,30 +0,0 @@ -package parser - -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/components/parser/ported_helpers_test.go b/tools/importer-rest-api-specs/components/parser/ported_helpers_test.go deleted file mode 100644 index 1e36c4279be..00000000000 --- a/tools/importer-rest-api-specs/components/parser/ported_helpers_test.go +++ /dev/null @@ -1,281 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package parser - -import ( - "fmt" - "sort" - "testing" - - "github.com/hashicorp/go-azure-helpers/lang/pointer" - sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" -) - -func ParseSwaggerFileForTesting(t *testing.T, file string, serviceName *string) (*sdkModels.APIVersion, 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() - 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 sdkModels.APIVersion, actual *sdkModels.APIVersion) { - if actual == nil { - t.Fatal("`actual` was nil") - } - 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) -} - -func validateParsedApiResourceMatches(t *testing.T, expected, actual sdkModels.APIResource, apiResourceName string) { - t.Logf("Validating API Resource %q..", apiResourceName) - // Validate each of the maps matches what we're expecting - validateMapsMatch(t, expected.Constants, actual.Constants, "Constants", validateParsedConstantsMatch) - validateMapsMatch(t, expected.Models, actual.Models, "Models", validateParsedSDKModelsMatch) - validateMapsMatch(t, expected.Operations, actual.Operations, "Operations", validateParsedOperationsMatch) - validateMapsMatch(t, expected.ResourceIDs, actual.ResourceIDs, "Resource IDs", validateParsedResourceIDsMatch) -} - -func validateParsedConstantsMatch(t *testing.T, expected, actual sdkModels.SDKConstant, constantName string) { - if expected.Type != actual.Type { - t.Fatalf("expected Type to be %q but got %q for Constant %q", string(expected.Type), string(actual.Type), constantName) - } - validateMapsMatch(t, expected.Values, actual.Values, "Values", validateStringsMatch) -} - -func validateParsedSDKFieldsMatch(t *testing.T, expected, actual sdkModels.SDKField, fieldName string) { - if expected.JsonName != actual.JsonName { - t.Fatalf("expected `JsonName` to be %q but got %q for Field %q", expected.JsonName, actual.JsonName, fieldName) - } - // TODO: @tombuildsstuff: reenable readonly support - //if expected.ReadOnly != actual.ReadOnly { - // t.Fatalf("expected `ReadOnly` to be %t but got %t for Field %q", expected.ReadOnly, actual.ReadOnly, fieldName) - //} - if expected.Required != actual.Required { - t.Fatalf("expected `Required` to be %t but got %t for Field %q", expected.Required, actual.Required, fieldName) - } - if expected.Sensitive != actual.Sensitive { - t.Fatalf("expected `Sensitive` to be %t but got %t for Field %q", expected.Sensitive, actual.Sensitive, fieldName) - } - - validateParsedObjectDefinitionsMatch(t, expected.ObjectDefinition, actual.ObjectDefinition, fieldName) -} - -func validateParsedSDKModelsMatch(t *testing.T, expected, actual sdkModels.SDKModel, modelName string) { - t.Logf("Validating Model %q...", modelName) - if pointer.From(expected.ParentTypeName) != pointer.From(actual.ParentTypeName) { - // NOTE: this should be nil when unset, otherwise a value - t.Fatalf("expected `ParentTypeName` to be %q but got %q for Model %q", pointer.From(expected.ParentTypeName), pointer.From(actual.ParentTypeName), modelName) - } - if pointer.From(expected.FieldNameContainingDiscriminatedValue) != pointer.From(actual.FieldNameContainingDiscriminatedValue) { - // NOTE: this should be nil when unset, otherwise a value - t.Fatalf("expected `FieldNameContainingDiscriminatedValue` to be %q but got %q for Model %q", pointer.From(expected.FieldNameContainingDiscriminatedValue), pointer.From(actual.FieldNameContainingDiscriminatedValue), modelName) - } - if pointer.From(expected.DiscriminatedValue) != pointer.From(actual.DiscriminatedValue) { - // NOTE: this should be nil when unset, otherwise a value - t.Fatalf("expected `DiscriminatedValue` to be %q but got %q for Model %q", pointer.From(expected.DiscriminatedValue), pointer.From(actual.DiscriminatedValue), modelName) - } - // TODO: thread description through once https://github.com/hashicorp/pandora/issues/3325 is completed - //if expected.Description != actual.Description { - // t.Fatalf("expected `Description` to be %q but got %q for Model %q", expected.Description, actual.Description, modelName) - //} - validateMapsMatch(t, expected.Fields, actual.Fields, "Fields", validateParsedSDKFieldsMatch) -} - -func validateParsedObjectDefinitionsMatch(t *testing.T, expected, actual sdkModels.SDKObjectDefinition, fieldName string) { - if expected.Type != actual.Type { - t.Fatalf("expected `Type` to be %q but got %q for Field %q", string(expected.Type), string(actual.Type), fieldName) - } - if pointer.From(expected.ReferenceName) != pointer.From(actual.ReferenceName) { - t.Fatalf("expected `ReferenceName` to be %q but got %q for Field %q", pointer.From(expected.ReferenceName), pointer.From(actual.ReferenceName), fieldName) - } - //// TODO: re-enable Min/Max/Unique - //if pointer.From(expected.Maximum) != pointer.From(actual.Maximum) { - // t.Fatalf("expected `Maximum` to be %d but got %d for Field %q", pointer.From(expected.Maximum), pointer.From(actual.Maximum), fieldName) - //} - //if pointer.From(expected.Minimum) != pointer.From(actual.Minimum) { - // t.Fatalf("expected `Minimum` to be %d but got %d for Field %q", pointer.From(expected.Minimum), pointer.From(actual.Minimum), fieldName) - //} - //if pointer.From(expected.UniqueItems) != pointer.From(actual.UniqueItems) { - // t.Fatalf("expected `UniqueItems` to be %t but got %t for Field %q", pointer.From(expected.UniqueItems), pointer.From(actual.UniqueItems), fieldName) - //} - - validateObjectsMatch(t, expected.NestedItem, actual.NestedItem, "NestedItem", validateParsedObjectDefinitionsMatch) -} - -func validateParsedOperationsMatch(t *testing.T, expected, actual sdkModels.SDKOperation, operationName string) { - t.Logf("Validating Operation %q..", operationName) - if expected.ContentType != actual.ContentType { - t.Fatalf("expected `ContentType` to be %q but got %q for Operation %q", expected.ContentType, actual.ContentType, operationName) - } - validateSlicesMatch(t, expected.ExpectedStatusCodes, actual.ExpectedStatusCodes, "ExpectedStatusCodes", validateIntegersMatch) - if pointer.From(expected.FieldContainingPaginationDetails) != pointer.From(actual.FieldContainingPaginationDetails) { - t.Fatalf("expected `FieldContainingPaginationDetails` to be %q but got %q for Operation %q", pointer.From(expected.FieldContainingPaginationDetails), pointer.From(actual.FieldContainingPaginationDetails), operationName) - } - if expected.LongRunning != actual.LongRunning { - t.Fatalf("expected `LongRunning` to be %t but got %t for Operation %q", expected.LongRunning, actual.LongRunning, operationName) - } - if expected.Method != actual.Method { - t.Fatalf("expected `Method` to be %q but got %q for Operation %q", expected.Method, actual.Method, operationName) - } - validateMapsMatch(t, expected.Options, actual.Options, "Options", validateParsedOptionsMatch) - if pointer.From(expected.ResourceIDName) != pointer.From(actual.ResourceIDName) { - t.Fatalf("expected `ResourceIDName` to be %q but got %q for Operation %q", pointer.From(expected.ResourceIDName), pointer.From(actual.ResourceIDName), operationName) - } - validateObjectsMatch(t, expected.RequestObject, actual.RequestObject, "RequestObject", validateParsedObjectDefinitionsMatch) - validateObjectsMatch(t, expected.ResponseObject, actual.ResponseObject, "ResponseObject", validateParsedObjectDefinitionsMatch) - if pointer.From(expected.URISuffix) != pointer.From(actual.URISuffix) { - t.Fatalf("expected `URISuffix` to be %q but got %q for Operation %q", pointer.From(expected.URISuffix), pointer.From(actual.URISuffix), operationName) - } -} - -func validateParsedOptionsMatch(t *testing.T, expected, actual sdkModels.SDKOperationOption, optionName string) { - if pointer.From(expected.HeaderName) != pointer.From(actual.HeaderName) { - t.Errorf("expected `HeaderName` to be %q but got %q for Option %q", pointer.From(expected.HeaderName), pointer.From(actual.HeaderName), optionName) - } - if pointer.From(expected.QueryStringName) != pointer.From(actual.QueryStringName) { - t.Errorf("expected `QueryStringName` to be %q but got %q for Option %q", pointer.From(expected.QueryStringName), pointer.From(actual.QueryStringName), optionName) - } - if expected.Required != actual.Required { - t.Errorf("expected `Required` to be %t but got %t for Option %q", expected.Required, actual.Required, optionName) - } - validateParsedOptionsObjectDefinitionsMatch(t, expected.ObjectDefinition, actual.ObjectDefinition, "OptionObjectDefinition") -} - -func validateParsedOptionsObjectDefinitionsMatch(t *testing.T, expected, actual sdkModels.SDKOperationOptionObjectDefinition, fieldName string) { - if expected.Type != actual.Type { - t.Fatalf("expected `Type` to be %q but got %q for Field %q", string(expected.Type), string(actual.Type), fieldName) - } - if pointer.From(expected.ReferenceName) != pointer.From(actual.ReferenceName) { - t.Fatalf("expected `ReferenceName` to be %q but got %q for Field %q", pointer.From(expected.ReferenceName), pointer.From(actual.ReferenceName), fieldName) - } - - validateObjectsMatch(t, expected.NestedItem, actual.NestedItem, "NestedItem", validateParsedOptionsObjectDefinitionsMatch) -} - -func validateParsedResourceIDsMatch(t *testing.T, expected, actual sdkModels.ResourceID, resourceIdName string) { - if pointer.From(expected.CommonIDAlias) != pointer.From(actual.CommonIDAlias) { - t.Errorf("expected `CommonIDAlias` to be %q but got %q for Resource ID %q", pointer.From(expected.CommonIDAlias), pointer.From(actual.CommonIDAlias), resourceIdName) - } - validateSlicesMatch(t, expected.ConstantNames, actual.ConstantNames, "ConstantNames", validateStringsMatch) - validateSlicesMatch(t, expected.Segments, actual.Segments, "Segments", validateParsedResourceIDSegmentsMatch) -} - -func validateParsedResourceIDSegmentsMatch(t *testing.T, expected, actual sdkModels.ResourceIDSegment, segmentName string) { - if pointer.From(expected.ConstantReference) != pointer.From(actual.ConstantReference) { - t.Errorf("expected `ConstantReference` to be %q but got %q for %s", pointer.From(expected.ConstantReference), pointer.From(actual.ConstantReference), segmentName) - } - // TODO: enable once the SDK migration is completed - //if expected.ExampleValue != actual.ExampleValue { - // t.Errorf("expected `ExampleValue` to be %q but got %q for %s", expected.ExampleValue, actual.ExampleValue, segmentName) - //} - if pointer.From(expected.FixedValue) != pointer.From(actual.FixedValue) { - t.Errorf("expected `FixedValue` to be %q but got %q for %s", pointer.From(expected.FixedValue), pointer.From(actual.FixedValue), segmentName) - } - if expected.Name != actual.Name { - t.Errorf("expected `Name` to be %q but got %q for %s", expected.Name, actual.Name, segmentName) - } - if string(expected.Type) != string(actual.Type) { - t.Errorf("expected `Type` to be %q but got %q for %s", string(expected.Type), string(actual.Type), segmentName) - } -} - -func validateIntegersMatch(t *testing.T, expected, actual int, key string) { - if expected != actual { - t.Fatalf("expected %q to be %d but got %d", key, expected, actual) - } -} - -func validateStringsMatch(t *testing.T, expected, actual string, key string) { - if expected != actual { - t.Fatalf("expected %q to be %q but got %q", key, expected, actual) - } -} - -// Generic helper functions below this point - -func validateObjectsMatch[T any](t *testing.T, expected, actual *T, name string, innerAssert func(t *testing.T, expected T, actual T, key string)) { - if expected != nil && actual == nil { - t.Fatalf("expected %q to be %q but got nil for %q", name, expected, name) - } - if expected == nil && actual != nil { - t.Fatalf("expected %q to be nil but got %q for %q", name, actual, name) - } - if expected != nil && actual != nil { - innerAssert(t, *expected, *actual, name) - } -} - -func validateMapsMatch[T any](t *testing.T, expected, actual map[string]T, name string, innerAssert func(t *testing.T, expected T, actual T, key string)) { - if len(actual) != len(expected) { - t.Fatalf("expected there to be %d %q but got %d", len(expected), name, len(actual)) - } - resourceKeys := uniqueKeys[T](actual, expected) - for _, resourceKey := range resourceKeys { - expectedItem, ok := expected[resourceKey] - if !ok { - t.Fatalf("found an unexpected `%s` called %q", name, resourceKey) - } - actualItem, ok := actual[resourceKey] - if !ok { - t.Fatalf("expected a `%s` called %q but got nil", name, resourceKey) - } - - innerAssert(t, expectedItem, actualItem, resourceKey) - } -} - -func validateSlicesMatch[T any](t *testing.T, expected, actual []T, name string, innerAssert func(t *testing.T, expected T, actual T, key string)) { - if len(actual) != len(expected) { - t.Fatalf("expected there to be %d %q but got %d", len(expected), name, len(actual)) - } - for i := range expected { - expectedItem := expected[i] - actualItem := actual[i] - - innerAssert(t, expectedItem, actualItem, fmt.Sprintf("%s item %d", name, i)) - } -} - -func uniqueKeys[T any](first, second map[string]T) []string { - keys := make(map[string]struct{}) - for key := range first { - keys[key] = struct{}{} - } - for key := range second { - keys[key] = struct{}{} - } - - out := make([]string, 0) - for k := range keys { - out = append(out, k) - } - sort.Strings(out) - return out -} diff --git a/tools/importer-rest-api-specs/components/parser/ported_models.go b/tools/importer-rest-api-specs/components/parser/ported_models.go deleted file mode 100644 index 5e69007dc6e..00000000000 --- a/tools/importer-rest-api-specs/components/parser/ported_models.go +++ /dev/null @@ -1,510 +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/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 (d *SwaggerDefinition) parseModel(name string, input spec.Schema) (*parserModels.ParseResult, error) { - result := parserModels.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 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 := 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 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 := 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 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 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) (*parserModels.ParseResult, error) { - result := parserModels.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 -} diff --git a/tools/importer-rest-api-specs/components/parser/ported_models_commonschema_test.go b/tools/importer-rest-api-specs/components/parser/ported_models_commonschema_test.go deleted file mode 100644 index 7c38fd55903..00000000000 --- a/tools/importer-rest-api-specs/components/parser/ported_models_commonschema_test.go +++ /dev/null @@ -1,841 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package parser - -import ( - "testing" - - "github.com/hashicorp/go-azure-helpers/lang/pointer" - sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" -) - -// TODO: Edge Zones -// TODO: SystemData -// TODO: Tags -// TODO: Zone -// TODO: Zones - -func TestParseModel_CommonSchema_IdentityLegacySystemAndUserAssignedList(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_commonschema_identitylegacysystemanduserassignedlist.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Resource": { - Models: map[string]sdkModels.SDKModel{ - "Model": { - Fields: map[string]sdkModels.SDKField{ - "Identity": { - JsonName: "identity", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.LegacySystemAndUserAssignedIdentityListSDKObjectDefinitionType, - }, - Required: false, - }, - "Name": { - JsonName: "name", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Model"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/example"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseModel_CommonSchema_IdentityLegacySystemAndUserAssignedMap(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_commonschema_identitylegacysystemanduserassignedmap.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Resource": { - Models: map[string]sdkModels.SDKModel{ - "Model": { - Fields: map[string]sdkModels.SDKField{ - "Identity": { - JsonName: "identity", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.LegacySystemAndUserAssignedIdentityMapSDKObjectDefinitionType, - }, - Required: false, - }, - "Name": { - JsonName: "name", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Model"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/example"), - }, - }, - }, - }, - } - 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) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Resource": { - Models: map[string]sdkModels.SDKModel{ - "Model": { - Fields: map[string]sdkModels.SDKField{ - "Identity": { - JsonName: "identity", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.LegacySystemAndUserAssignedIdentityMapSDKObjectDefinitionType, - }, - Required: false, - }, - "Name": { - JsonName: "name", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Model"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/example"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseModel_CommonSchema_IdentityLegacySystemAndUserAssignedMap_GenericDictionary(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_commonschema_identitylegacysystemanduserassignedmap_genericdictionary.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Resource": { - Models: map[string]sdkModels.SDKModel{ - "Model": { - Fields: map[string]sdkModels.SDKField{ - "Identity": { - JsonName: "identity", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.LegacySystemAndUserAssignedIdentityMapSDKObjectDefinitionType, - }, - Required: false, - }, - "Name": { - JsonName: "name", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Model"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/example"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseModel_CommonSchema_IdentitySystemAssigned(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_commonschema_identitysystemassigned.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Resource": { - Models: map[string]sdkModels.SDKModel{ - "Model": { - Fields: map[string]sdkModels.SDKField{ - "Identity": { - JsonName: "identity", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.SystemAssignedIdentitySDKObjectDefinitionType, - }, - Required: false, - }, - "Name": { - JsonName: "name", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Model"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/example"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseModel_CommonSchema_IdentitySystemAndUserAssignedList(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_commonschema_identitysystemanduserassignedlist.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Resource": { - Models: map[string]sdkModels.SDKModel{ - "Model": { - Fields: map[string]sdkModels.SDKField{ - "Identity": { - JsonName: "identity", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.SystemAndUserAssignedIdentityListSDKObjectDefinitionType, - }, - Required: false, - }, - "Name": { - JsonName: "name", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Model"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/example"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseModel_CommonSchema_IdentitySystemAndUserAssignedMap(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_commonschema_identitysystemanduserassignedmap.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Resource": { - Models: map[string]sdkModels.SDKModel{ - "Model": { - Fields: map[string]sdkModels.SDKField{ - "Identity": { - JsonName: "identity", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.SystemAndUserAssignedIdentityMapSDKObjectDefinitionType, - }, - Required: false, - }, - "Name": { - JsonName: "name", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Model"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/example"), - }, - }, - }, - }, - } - 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) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Resource": { - Models: map[string]sdkModels.SDKModel{ - "Model": { - Fields: map[string]sdkModels.SDKField{ - "Identity": { - JsonName: "identity", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.SystemAndUserAssignedIdentityMapSDKObjectDefinitionType, - }, - Required: false, - }, - "Name": { - JsonName: "name", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Model"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/example"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseModel_CommonSchema_IdentitySystemOrUserAssignedList(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_commonschema_identitysystemoruserassignedlist.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Resource": { - Models: map[string]sdkModels.SDKModel{ - "Model": { - Fields: map[string]sdkModels.SDKField{ - "Identity": { - JsonName: "identity", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.SystemOrUserAssignedIdentityListSDKObjectDefinitionType, - }, - Required: false, - }, - "Name": { - JsonName: "name", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Model"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/example"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseModel_CommonSchema_IdentitySystemOrUserAssignedMap(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_commonschema_identitysystemoruserassignedmap.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Resource": { - Models: map[string]sdkModels.SDKModel{ - "Model": { - Fields: map[string]sdkModels.SDKField{ - "Identity": { - JsonName: "identity", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.SystemOrUserAssignedIdentityMapSDKObjectDefinitionType, - }, - Required: false, - }, - "Name": { - JsonName: "name", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Model"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/example"), - }, - }, - }, - }, - } - 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) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Resource": { - Models: map[string]sdkModels.SDKModel{ - "Model": { - Fields: map[string]sdkModels.SDKField{ - "Identity": { - JsonName: "identity", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.SystemOrUserAssignedIdentityMapSDKObjectDefinitionType, - }, - Required: false, - }, - "Name": { - JsonName: "name", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Model"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/example"), - }, - }, - }, - }, - } - 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) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Resource": { - Models: map[string]sdkModels.SDKModel{ - "Model": { - Fields: map[string]sdkModels.SDKField{ - "Identity": { - JsonName: "identity", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.SystemOrUserAssignedIdentityMapSDKObjectDefinitionType, - }, - Required: false, - }, - "Name": { - JsonName: "name", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Model"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/example"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseModel_CommonSchema_IdentityUserAssignedList(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_commonschema_identityuserassignedlist.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Resource": { - Models: map[string]sdkModels.SDKModel{ - "Model": { - Fields: map[string]sdkModels.SDKField{ - "Identity": { - JsonName: "identity", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.UserAssignedIdentityListSDKObjectDefinitionType, - }, - Required: false, - }, - "Name": { - JsonName: "name", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Model"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/example"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseModel_CommonSchema_IdentityUserAssignedMap(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_commonschema_identityuserassignedmap.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Resource": { - Models: map[string]sdkModels.SDKModel{ - "Model": { - Fields: map[string]sdkModels.SDKField{ - "Identity": { - JsonName: "identity", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.UserAssignedIdentityMapSDKObjectDefinitionType, - }, - Required: false, - }, - "Name": { - JsonName: "name", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Model"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/example"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseModel_CommonSchema_IdentityUserAssignedMap_PrincipalID(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_commonschema_identityuserassignedmap_principalid.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Resource": { - Models: map[string]sdkModels.SDKModel{ - "Model": { - Fields: map[string]sdkModels.SDKField{ - "Identity": { - JsonName: "identity", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.UserAssignedIdentityMapSDKObjectDefinitionType, - }, - Required: false, - }, - "Name": { - JsonName: "name", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Model"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/example"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseModel_CommonSchema_IdentityUserAssignedMap_TenantID(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_commonschema_identityuserassignedmap_tenantid.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Resource": { - Models: map[string]sdkModels.SDKModel{ - "Model": { - Fields: map[string]sdkModels.SDKField{ - "Identity": { - JsonName: "identity", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.UserAssignedIdentityMapSDKObjectDefinitionType, - }, - Required: false, - }, - "Name": { - JsonName: "name", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Model"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/example"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseModel_CommonSchema_Location(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_commonschema_location.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Resource": { - Models: map[string]sdkModels.SDKModel{ - "Model": { - Fields: map[string]sdkModels.SDKField{ - "Location": { - JsonName: "location", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.LocationSDKObjectDefinitionType, - }, - Required: false, - }, - "Name": { - JsonName: "name", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Model"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/example"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} diff --git a/tools/importer-rest-api-specs/components/parser/ported_models_datafactorycustom_test.go b/tools/importer-rest-api-specs/components/parser/ported_models_datafactorycustom_test.go deleted file mode 100644 index 99aae633dcf..00000000000 --- a/tools/importer-rest-api-specs/components/parser/ported_models_datafactorycustom_test.go +++ /dev/null @@ -1,200 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package parser - -import ( - "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" -) - -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) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expectedModels := map[string]sdkModels.SDKModel{ - "Model": { - Fields: map[string]sdkModels.SDKField{ - // Simple Types - "BooleanField": { - JsonName: "booleanField", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.BooleanSDKObjectDefinitionType, - }, - Required: false, - }, - "DoubleField": { - JsonName: "doubleField", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.FloatSDKObjectDefinitionType, - }, - Required: false, - }, - "KeyValuePairField": { - JsonName: "keyValuePairField", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.DictionarySDKObjectDefinitionType, - NestedItem: &sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - }, - Required: false, - }, - "IntegerField": { - JsonName: "integerField", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.IntegerSDKObjectDefinitionType, - }, - Required: false, - }, - "StringField": { - JsonName: "stringField", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - "UnknownField": { - // In this case, the `dfe-*` value is unknown, so we should return an object - // this is the default behaviour, mostly for sanity-checking - JsonName: "unknownField", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.RawObjectSDKObjectDefinitionType, - }, - Required: false, - }, - - // Dictionaries of a Simple Type (using the regular Swagger/OpenAPI syntax) - "DictionaryOfBooleanField": { - JsonName: "dictionaryOfBooleanField", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.DictionarySDKObjectDefinitionType, - NestedItem: &sdkModels.SDKObjectDefinition{ - Type: sdkModels.BooleanSDKObjectDefinitionType, - }, - }, - Required: false, - }, - "DictionaryOfDoubleField": { - JsonName: "dictionaryOfDoubleField", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.DictionarySDKObjectDefinitionType, - NestedItem: &sdkModels.SDKObjectDefinition{ - Type: sdkModels.FloatSDKObjectDefinitionType, - }, - }, - Required: false, - }, - "DictionaryOfIntegerField": { - JsonName: "dictionaryOfIntegerField", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.DictionarySDKObjectDefinitionType, - NestedItem: &sdkModels.SDKObjectDefinition{ - Type: sdkModels.IntegerSDKObjectDefinitionType, - }, - }, - Required: false, - }, - "DictionaryOfStringField": { - JsonName: "dictionaryOfStringField", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.DictionarySDKObjectDefinitionType, - NestedItem: &sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - }, - Required: false, - }, - "DictionaryOfUnknownField": { - // In this case, the `dfe-*` value is unknown, so we should return an object - // this is the default behaviour, mostly for sanity-checking - JsonName: "dictionaryOfUnknownField", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.DictionarySDKObjectDefinitionType, - NestedItem: &sdkModels.SDKObjectDefinition{ - Type: sdkModels.RawObjectSDKObjectDefinitionType, - }, - }, - Required: false, - }, - - // DFE specific List implementations - "DfeCustomListOfString": { - JsonName: "dfeCustomListOfString", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.ListSDKObjectDefinitionType, - NestedItem: &sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - }, - Required: false, - }, - "DfeCustomListOfAnotherObject": { - JsonName: "dfeCustomListOfAnotherObject", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.ListSDKObjectDefinitionType, - NestedItem: &sdkModels.SDKObjectDefinition{ - Type: sdkModels.ReferenceSDKObjectDefinitionType, - ReferenceName: pointer.To("SecondModel"), - }, - }, - Required: false, - }, - }, - }, - "SecondModel": { - Fields: map[string]sdkModels.SDKField{ - "Example": { - JsonName: "example", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - }, - }, - }, - } - - if !featureflags.ParseDataFactoryListsOfReferencesAsRegularObjectDefinitionTypes { - delete(expectedModels, "SecondModel") - expectedModels["Model"].Fields["DfeCustomListOfAnotherObject"] = sdkModels.SDKField{ - JsonName: "dfeCustomListOfAnotherObject", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.RawObjectSDKObjectDefinitionType, - }, - Required: false, - } - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Example": { - Models: expectedModels, - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "PUT", - RequestObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Model"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Model"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/example"), - }, - }, - }, - }, - } - - validateParsedSwaggerResultMatches(t, expected, actual) -} diff --git a/tools/importer-rest-api-specs/components/parser/ported_models_dictionaries_test.go b/tools/importer-rest-api-specs/components/parser/ported_models_dictionaries_test.go deleted file mode 100644 index 75c7e74c560..00000000000 --- a/tools/importer-rest-api-specs/components/parser/ported_models_dictionaries_test.go +++ /dev/null @@ -1,383 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package parser - -import ( - "testing" - - "github.com/hashicorp/go-azure-helpers/lang/pointer" - sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" -) - -func TestParseModelWithADictionaryOfIntegers(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_dictionary_of_integers.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Hello": { - Models: map[string]sdkModels.SDKModel{ - "Example": { - Fields: map[string]sdkModels.SDKField{ - "Name": { - JsonName: "name", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - "MapField": { - JsonName: "mapField", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.DictionarySDKObjectDefinitionType, - NestedItem: &sdkModels.SDKObjectDefinition{ - Type: sdkModels.IntegerSDKObjectDefinitionType, - }, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "GetWorld": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Example"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/things"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseModelWithADictionaryOfIntegersInlined(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_dictionary_of_integers_inlined.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Hello": { - Models: map[string]sdkModels.SDKModel{ - "Example": { - Fields: map[string]sdkModels.SDKField{ - "Name": { - JsonName: "name", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - "MapField": { - JsonName: "mapField", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.DictionarySDKObjectDefinitionType, - NestedItem: &sdkModels.SDKObjectDefinition{ - Type: sdkModels.IntegerSDKObjectDefinitionType, - }, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "GetWorld": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Example"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/things"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseModelWithADictionaryOfAnObject(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_dictionary_of_object.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Hello": { - Models: map[string]sdkModels.SDKModel{ - "Example": { - Fields: map[string]sdkModels.SDKField{ - "Name": { - JsonName: "name", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - "MapField": { - JsonName: "mapField", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - NestedItem: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("MapFieldProperties"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - Type: sdkModels.DictionarySDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - "MapFieldProperties": { - Fields: map[string]sdkModels.SDKField{ - "Line1": { - JsonName: "line1", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - "Line2": { - JsonName: "line2", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - "City": { - JsonName: "city", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - "Country": { - JsonName: "country", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "GetWorld": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Example"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/things"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseModelWithADictionaryOfAnObjectInlined(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_dictionary_of_object_inlined.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Hello": { - Models: map[string]sdkModels.SDKModel{ - "Example": { - Fields: map[string]sdkModels.SDKField{ - "Name": { - JsonName: "name", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - "MapField": { - JsonName: "mapField", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - NestedItem: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("MapFieldProperties"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - Type: sdkModels.DictionarySDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - "MapFieldProperties": { - Fields: map[string]sdkModels.SDKField{ - "Line1": { - JsonName: "line1", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - "Line2": { - JsonName: "line2", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - "City": { - JsonName: "city", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - "Country": { - JsonName: "country", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "GetWorld": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Example"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/things"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseModelWithADictionaryOfString(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_dictionary_of_string.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Hello": { - Models: map[string]sdkModels.SDKModel{ - "Example": { - Fields: map[string]sdkModels.SDKField{ - "Name": { - JsonName: "name", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - "MapField": { - JsonName: "mapField", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.DictionarySDKObjectDefinitionType, - NestedItem: &sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "GetWorld": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Example"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/things"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseModelWithADictionaryOfStringInlined(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_dictionary_of_string_inlined.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Hello": { - Models: map[string]sdkModels.SDKModel{ - "Example": { - Fields: map[string]sdkModels.SDKField{ - "Name": { - JsonName: "name", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - "MapField": { - JsonName: "mapField", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.DictionarySDKObjectDefinitionType, - NestedItem: &sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "GetWorld": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Example"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/things"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} diff --git a/tools/importer-rest-api-specs/components/parser/ported_models_discriminators_test.go b/tools/importer-rest-api-specs/components/parser/ported_models_discriminators_test.go deleted file mode 100644 index 9da6a29835f..00000000000 --- a/tools/importer-rest-api-specs/components/parser/ported_models_discriminators_test.go +++ /dev/null @@ -1,1424 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package parser - -import ( - "testing" - - "github.com/hashicorp/go-azure-helpers/lang/pointer" - sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" -) - -func TestParseDiscriminatorsTopLevel(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_discriminators_simple.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Discriminator": { - Models: map[string]sdkModels.SDKModel{ - "ExampleWrapper": { - Fields: map[string]sdkModels.SDKField{ - "Nested": { - JsonName: "nested", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Animal"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - Required: true, - }, - }, - }, - "Animal": { - Fields: map[string]sdkModels.SDKField{ - "AnimalType": { - JsonName: "animalType", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: true, - }, - }, - FieldNameContainingDiscriminatedValue: pointer.To("AnimalType"), - }, - "Cat": { - ParentTypeName: pointer.To("Animal"), - FieldNameContainingDiscriminatedValue: pointer.To("AnimalType"), - DiscriminatedValue: pointer.To("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, - }, - }, - }, - "Dog": { - ParentTypeName: pointer.To("Animal"), - FieldNameContainingDiscriminatedValue: pointer.To("AnimalType"), - DiscriminatedValue: pointer.To("dog"), - Fields: map[string]sdkModels.SDKField{ - "AnimalType": { - JsonName: "animalType", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: true, - }, - "Barks": { - JsonName: "barks", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.BooleanSDKObjectDefinitionType, - }, - Required: true, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("ExampleWrapper"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/example"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseDiscriminatorsWithinArray(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_discriminators_within_array.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Discriminator": { - Models: map[string]sdkModels.SDKModel{ - "ExampleWrapper": { - Fields: map[string]sdkModels.SDKField{ - "BiologicalEntities": { - JsonName: "biologicalEntities", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - NestedItem: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("BiologicalEntity"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - Type: sdkModels.ListSDKObjectDefinitionType, - }, - Required: true, - }, - }, - }, - "BiologicalEntity": { - Fields: map[string]sdkModels.SDKField{ - "TypeName": { - JsonName: "typeName", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: true, - }, - }, - FieldNameContainingDiscriminatedValue: pointer.To("TypeName"), - }, - "Cat": { - ParentTypeName: pointer.To("BiologicalEntity"), - FieldNameContainingDiscriminatedValue: pointer.To("TypeName"), - DiscriminatedValue: pointer.To("cat"), - Fields: map[string]sdkModels.SDKField{ - "IsFluffy": { - JsonName: "isFluffy", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.BooleanSDKObjectDefinitionType, - }, - Required: true, - }, - "TypeName": { - JsonName: "typeName", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: true, - }, - }, - }, - "Human": { - ParentTypeName: pointer.To("BiologicalEntity"), - FieldNameContainingDiscriminatedValue: pointer.To("TypeName"), - DiscriminatedValue: pointer.To("human"), - 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, - }, - Required: true, - }, - "TypeName": { - JsonName: "typeName", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: true, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("ExampleWrapper"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/example"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseDiscriminatorsWithinDiscriminators(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_discriminators_within_discriminators.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Discriminator": { - Models: map[string]sdkModels.SDKModel{ - "Animal": { - Fields: map[string]sdkModels.SDKField{ - "AnimalType": { - JsonName: "animalType", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: true, - }, - "FavouriteToy": { - JsonName: "favouriteToy", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Toy"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - Required: false, - }, - }, - FieldNameContainingDiscriminatedValue: pointer.To("AnimalType"), - }, - "Bone": { - Fields: map[string]sdkModels.SDKField{ - "Length": { - JsonName: "length", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.FloatSDKObjectDefinitionType, - }, - Required: true, - }, - "ToyType": { - JsonName: "toyType", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: true, - }, - }, - ParentTypeName: pointer.To("Toy"), - FieldNameContainingDiscriminatedValue: pointer.To("ToyType"), - DiscriminatedValue: pointer.To("bone"), - }, - "Cat": { - ParentTypeName: pointer.To("Animal"), - FieldNameContainingDiscriminatedValue: pointer.To("AnimalType"), - DiscriminatedValue: pointer.To("cat"), - Fields: map[string]sdkModels.SDKField{ - "AnimalType": { - JsonName: "animalType", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: true, - }, - "FavouriteToy": { - JsonName: "favouriteToy", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Toy"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - Required: false, - }, - "IsFluffy": { - JsonName: "isFluffy", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.BooleanSDKObjectDefinitionType, - }, - Required: true, - }, - }, - }, - "Dog": { - ParentTypeName: pointer.To("Animal"), - FieldNameContainingDiscriminatedValue: pointer.To("AnimalType"), - DiscriminatedValue: pointer.To("dog"), - Fields: map[string]sdkModels.SDKField{ - "AnimalType": { - JsonName: "animalType", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: true, - }, - "FavouriteToy": { - JsonName: "favouriteToy", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Toy"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - Required: false, - }, - "Barks": { - JsonName: "barks", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.BooleanSDKObjectDefinitionType, - }, - Required: true, - }, - }, - }, - "ExampleWrapper": { - Fields: map[string]sdkModels.SDKField{ - "Nested": { - JsonName: "nested", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Animal"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - Required: true, - }, - }, - }, - "LaserBeam": { - Fields: map[string]sdkModels.SDKField{ - "Colour": { - JsonName: "colour", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: true, - }, - "Intensity": { - JsonName: "intensity", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.IntegerSDKObjectDefinitionType, - }, - Required: false, - }, - "ToyType": { - JsonName: "toyType", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: true, - }, - }, - ParentTypeName: pointer.To("Toy"), - FieldNameContainingDiscriminatedValue: pointer.To("ToyType"), - DiscriminatedValue: pointer.To("laser-beam"), - }, - "Toy": { - Fields: map[string]sdkModels.SDKField{ - "ToyType": { - JsonName: "toyType", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: true, - }, - }, - FieldNameContainingDiscriminatedValue: pointer.To("ToyType"), - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("ExampleWrapper"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/example"), - }, - }, - }, - }, - } - 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) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Discriminator": { - Models: map[string]sdkModels.SDKModel{ - "Animal": { - Fields: map[string]sdkModels.SDKField{ - "Type": { - JsonName: "type", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Animal"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/example"), - }, - }, - }, - }, - } - 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) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Discriminator": { - Models: map[string]sdkModels.SDKModel{ - "Dog": { - Fields: map[string]sdkModels.SDKField{ - "Barks": { - JsonName: "barks", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.BooleanSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Dog"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/example"), - }, - }, - }, - }, - } - 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) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Discriminator": { - 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"), - }, - "Dog": { - Fields: map[string]sdkModels.SDKField{ - "AnimalType": { - JsonName: "animalType", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: true, - }, - "Barks": { - JsonName: "barks", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.BooleanSDKObjectDefinitionType, - }, - Required: true, - }, - }, - ParentTypeName: pointer.To("Animal"), - FieldNameContainingDiscriminatedValue: pointer.To("AnimalType"), - DiscriminatedValue: pointer.To("dog"), - }, - "ExampleWrapper": { - Fields: map[string]sdkModels.SDKField{ - "Nested": { - JsonName: "nested", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Dog"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - Required: true, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("ExampleWrapper"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/example"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseDiscriminatorsInheritingFromOtherDiscriminators(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_discriminators_inherited_from_discriminators.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Discriminator": { - 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"), - }, - "Dog": { - Fields: map[string]sdkModels.SDKField{ - "AnimalType": { - JsonName: "animalType", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: true, - }, - "Barks": { - JsonName: "barks", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.BooleanSDKObjectDefinitionType, - }, - Required: true, - }, - "IsFluffy": { - JsonName: "isFluffy", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.BooleanSDKObjectDefinitionType, - }, - Required: true, - }, - }, - ParentTypeName: pointer.To("Animal"), - FieldNameContainingDiscriminatedValue: pointer.To("AnimalType"), - DiscriminatedValue: pointer.To("dog"), - }, - "ExampleWrapper": { - Fields: map[string]sdkModels.SDKField{ - "Nested": { - JsonName: "nested", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Animal"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - Required: true, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("ExampleWrapper"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/example"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseDiscriminatorsDeepInheritance(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_discriminators_deep_inheritance.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Discriminator": { - Models: map[string]sdkModels.SDKModel{ - "Animal": { - Fields: map[string]sdkModels.SDKField{ - "TypeName": { - JsonName: "typeName", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: true, - }, - "IsPlantEater": { - JsonName: "isPlantEater", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.BooleanSDKObjectDefinitionType, - }, - Required: true, - }, - }, - ParentTypeName: pointer.To("BiologicalEntity"), - FieldNameContainingDiscriminatedValue: pointer.To("TypeName"), - DiscriminatedValue: pointer.To("animal"), - }, - "BiologicalEntity": { - Fields: map[string]sdkModels.SDKField{ - "TypeName": { - JsonName: "typeName", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: true, - }, - }, - FieldNameContainingDiscriminatedValue: pointer.To("TypeName"), - }, - "Carnivore": { - Fields: map[string]sdkModels.SDKField{ - "IsPlantEater": { - JsonName: "isPlantEater", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.BooleanSDKObjectDefinitionType, - }, - Required: true, - }, - "IsPredator": { - JsonName: "isPredator", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.BooleanSDKObjectDefinitionType, - }, - Required: true, - }, - "TypeName": { - JsonName: "typeName", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: true, - }, - }, - ParentTypeName: pointer.To("BiologicalEntity"), - FieldNameContainingDiscriminatedValue: pointer.To("TypeName"), - DiscriminatedValue: pointer.To("carnivore"), - }, - "Cat": { - Fields: map[string]sdkModels.SDKField{ - "IsFluffy": { - JsonName: "isFluffy", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.BooleanSDKObjectDefinitionType, - }, - Required: true, - }, - "IsPlantEater": { - JsonName: "isPlantEater", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.BooleanSDKObjectDefinitionType, - }, - Required: false, - }, - "IsPredator": { - JsonName: "isPredator", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.BooleanSDKObjectDefinitionType, - }, - Required: true, - }, - "TypeName": { - JsonName: "typeName", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: true, - }, - }, - ParentTypeName: pointer.To("BiologicalEntity"), - FieldNameContainingDiscriminatedValue: pointer.To("TypeName"), - DiscriminatedValue: pointer.To("cat"), - }, - "ExampleWrapper": { - Fields: map[string]sdkModels.SDKField{ - "BiologicalEntity": { - JsonName: "biologicalEntity", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("BiologicalEntity"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - Required: true, - }, - }, - }, - "PersianCat": { - ParentTypeName: pointer.To("BiologicalEntity"), - FieldNameContainingDiscriminatedValue: pointer.To("TypeName"), - DiscriminatedValue: pointer.To("persian-cat"), - Fields: map[string]sdkModels.SDKField{ - "IsFluffy": { - JsonName: "isFluffy", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.BooleanSDKObjectDefinitionType, - }, - Required: true, - }, - "IsFriendly": { - JsonName: "isFriendly", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.BooleanSDKObjectDefinitionType, - }, - Required: true, - }, - "IsPlantEater": { - JsonName: "isPlantEater", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.BooleanSDKObjectDefinitionType, - }, - Required: false, - }, - "IsPredator": { - JsonName: "isPredator", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.BooleanSDKObjectDefinitionType, - }, - Required: false, - }, - "TypeName": { - JsonName: "typeName", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: true, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("ExampleWrapper"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/example"), - }, - }, - }, - }, - } - 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) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Discriminator": { - Models: map[string]sdkModels.SDKModel{ - "BiologicalEntity": { - Fields: map[string]sdkModels.SDKField{ - "SomeField": { - JsonName: "someField", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - "TypeName": { - JsonName: "typeName", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: true, - }, - }, - FieldNameContainingDiscriminatedValue: pointer.To("TypeName"), - }, - "Cat": { - Fields: map[string]sdkModels.SDKField{ - "FirstName": { - JsonName: "firstName", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: true, - }, - "IsFluffy": { - JsonName: "isFluffy", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.BooleanSDKObjectDefinitionType, - }, - Required: true, - }, - "LastName": { - JsonName: "lastName", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - "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("cat"), - }, - "ExampleWrapper": { - Fields: map[string]sdkModels.SDKField{ - "Item": { - JsonName: "item", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("BiologicalEntity"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - Required: true, - }, - }, - }, - "Human": { - Fields: map[string]sdkModels.SDKField{ - "Age": { - JsonName: "age", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.IntegerSDKObjectDefinitionType, - }, - Required: true, - }, - "FirstName": { - JsonName: "firstName", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: true, - }, - "LastName": { - JsonName: "lastName", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: true, - }, - "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("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. - }, - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("ExampleWrapper"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/example"), - }, - }, - }, - }, - } - 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) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Discriminator": { - Models: map[string]sdkModels.SDKModel{ - "BiologicalEntity": { - Fields: map[string]sdkModels.SDKField{ - "SomeField": { - JsonName: "someField", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - "TypeName": { - JsonName: "typeName", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: true, - }, - }, - FieldNameContainingDiscriminatedValue: pointer.To("TypeName"), - }, - "Cat": { - Fields: map[string]sdkModels.SDKField{ - "FirstName": { - JsonName: "firstName", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: true, - }, - "IsFluffy": { - JsonName: "isFluffy", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.BooleanSDKObjectDefinitionType, - }, - Required: true, - }, - "LastName": { - JsonName: "lastName", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - "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("cat"), - }, - "ExampleWrapper": { - Fields: map[string]sdkModels.SDKField{ - "Items": { - JsonName: "items", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - NestedItem: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("BiologicalEntity"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - Type: sdkModels.ListSDKObjectDefinitionType, - }, - Required: true, - }, - }, - }, - "Human": { - Fields: map[string]sdkModels.SDKField{ - "Age": { - JsonName: "age", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.IntegerSDKObjectDefinitionType, - }, - Required: true, - }, - "FirstName": { - JsonName: "firstName", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: true, - }, - "LastName": { - JsonName: "lastName", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: true, - }, - "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("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. - }, - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("ExampleWrapper"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/example"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseDiscriminatorsOrphanedChild(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "/nestedtestdata/model_discriminators_orphaned_child.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - 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 - // some unexpectedly named data, but this is fine providing the APIResource we're expecting exists. - "ModelDiscriminatorsOrphanedChildren": { - 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"), - }, - "Dog": { - Fields: map[string]sdkModels.SDKField{ - "AnimalType": { - JsonName: "animalType", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: true, - }, - "Barks": { - JsonName: "barks", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.BooleanSDKObjectDefinitionType, - }, - Required: true, - }, - }, - ParentTypeName: pointer.To("Animal"), - FieldNameContainingDiscriminatedValue: pointer.To("AnimalType"), - DiscriminatedValue: pointer.To("dog"), - }, - // ExampleWrapper is present in the Swagger but unused - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseDiscriminatorsOrphanedChildWithNestedModel(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "/nestedtestdata/model_discriminators_orphaned_child_with_nested_model.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - 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 - // some unexpectedly named data, but this is fine providing the APIResource we're expecting exists. - "ModelDiscriminatorsOrphanedChildWithNestedModels": { - 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"), - }, - "Dog": { - Fields: map[string]sdkModels.SDKField{ - "AnimalType": { - JsonName: "animalType", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: true, - }, - "Barks": { - JsonName: "barks", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.BooleanSDKObjectDefinitionType, - }, - Required: true, - }, - "Parameters": { - JsonName: "parameters", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - NestedItem: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("KeyValuePair"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - Type: sdkModels.ListSDKObjectDefinitionType, - }, - Required: false, - }, - }, - ParentTypeName: pointer.To("Animal"), - FieldNameContainingDiscriminatedValue: pointer.To("AnimalType"), - DiscriminatedValue: pointer.To("dog"), - }, - // ExampleWrapper is present in the Swagger but unused - "KeyValuePair": { - Fields: map[string]sdkModels.SDKField{ - "Key": { - JsonName: "key", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: true, - }, - "Value": { - JsonName: "value", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: true, - }, - }, - }, - }, - }, - }, - } - 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")) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - 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 - // some unexpectedly named data, but this is fine providing the APIResource we're expecting exists. - "ModelDiscriminatorsOrphanedChildWithoutDiscriminatorValues": { - 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, - }, - }, - ParentTypeName: pointer.To("Animal"), - FieldNameContainingDiscriminatedValue: pointer.To("AnimalType"), - DiscriminatedValue: pointer.To("Cat"), - }, - "Dog": { - Fields: map[string]sdkModels.SDKField{ - "AnimalType": { - JsonName: "animalType", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: true, - }, - }, - ParentTypeName: pointer.To("Animal"), - FieldNameContainingDiscriminatedValue: pointer.To("AnimalType"), - DiscriminatedValue: pointer.To("Dog"), - }, - // ExampleWrapper is present in the Swagger but unused - }, - }, - }, - } - 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")) - 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 := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{}, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} diff --git a/tools/importer-rest-api-specs/components/parser/ported_models_test.go b/tools/importer-rest-api-specs/components/parser/ported_models_test.go deleted file mode 100644 index 22bf5c9f75b..00000000000 --- a/tools/importer-rest-api-specs/components/parser/ported_models_test.go +++ /dev/null @@ -1,1821 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package parser - -import ( - "testing" - - "github.com/hashicorp/go-azure-helpers/lang/pointer" - sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" -) - -// TODO: tests for the different types of Object Definition - -func TestParseModelTopLevel(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_top_level.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Example": { - Models: map[string]sdkModels.SDKModel{ - "Model": { - Fields: map[string]sdkModels.SDKField{ - "Age": { - JsonName: "age", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.IntegerSDKObjectDefinitionType, - }, - Required: false, - }, - "Enabled": { - JsonName: "enabled", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.BooleanSDKObjectDefinitionType, - }, - Required: false, - }, - "Height": { - JsonName: "height", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.FloatSDKObjectDefinitionType, - }, - Required: false, - }, - "Name": { - JsonName: "name", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: true, - }, - "Tags": { - JsonName: "tags", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.TagsSDKObjectDefinitionType, - }, - Required: false, - }, - "Value": { - JsonName: "value", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.RawObjectSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "PUT", - RequestObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Model"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Model"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/example"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseModelTopLevelWithRawFile(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_top_level_with_rawfile.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Example": { - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "PUT", - RequestObject: &sdkModels.SDKObjectDefinition{ - Type: sdkModels.RawFileSDKObjectDefinitionType, - }, - ResponseObject: &sdkModels.SDKObjectDefinition{ - Type: sdkModels.RawFileSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/example"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseModelTopLevelWithInlinedModel(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_top_level_with_inlined_model.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Example": { - Models: map[string]sdkModels.SDKModel{ - "Model": { - Fields: map[string]sdkModels.SDKField{ - "Name": { - JsonName: "name", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: true, - }, - "Properties": { - JsonName: "properties", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("ModelProperties"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - "ModelProperties": { - Fields: map[string]sdkModels.SDKField{ - "Age": { - JsonName: "age", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.IntegerSDKObjectDefinitionType, - }, - Required: false, - }, - "Enabled": { - JsonName: "enabled", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.BooleanSDKObjectDefinitionType, - }, - Required: false, - }, - "Height": { - JsonName: "height", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.FloatSDKObjectDefinitionType, - }, - Required: false, - }, - "Nickname": { - JsonName: "nickname", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - "Tags": { - JsonName: "tags", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.TagsSDKObjectDefinitionType, - }, - Required: false, - }, - "Value": { - JsonName: "value", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.RawObjectSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "PUT", - RequestObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Model"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Model"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/example"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseModelWithDateTimeNoType(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_with_datetime_no_type.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Example": { - Models: map[string]sdkModels.SDKModel{ - "Model": { - Fields: map[string]sdkModels.SDKField{ - "SomeDateValue": { - JsonName: "someDateValue", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.DateTimeSDKObjectDefinitionType, - }, - Required: true, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "PUT", - RequestObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Model"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Model"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/example"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseModelWithInlinedObject(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_with_inlined_object.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Example": { - Models: map[string]sdkModels.SDKModel{ - "Model": { - Fields: map[string]sdkModels.SDKField{ - "Name": { - JsonName: "name", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - "ThingProps": { - JsonName: "thingProps", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - NestedItem: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("ThingProperties"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - Type: sdkModels.ListSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - "ThingProperties": { - Fields: map[string]sdkModels.SDKField{ - "KeyName": { - JsonName: "keyName", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - "UserAssignedIdentities": { - JsonName: "userAssignedIdentities", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - NestedItem: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("UserAssignedIdentitiesProperties"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - Type: sdkModels.DictionarySDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - "UserAssignedIdentitiesProperties": { - Fields: map[string]sdkModels.SDKField{ - "ClientId": { - JsonName: "clientId", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - ReadOnly: true, - Required: false, - }, - "PrincipalId": { - JsonName: "principalId", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - ReadOnly: true, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Model"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/example"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseModelWithNumberPrefixedField(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_with_number_prefixed_field.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Example": { - Models: map[string]sdkModels.SDKModel{ - "Model": { - Fields: map[string]sdkModels.SDKField{ - "Name": { - JsonName: "name", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - "Five0PercentDone": { - JsonName: "50PercentDone", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Model"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/example"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseModelWithReference(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_with_reference.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Example": { - Models: map[string]sdkModels.SDKModel{ - "Model": { - Fields: map[string]sdkModels.SDKField{ - "Name": { - JsonName: "name", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - "ThingProps": { - JsonName: "thingProps", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - NestedItem: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("ThingProperties"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - Type: sdkModels.ListSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - "ThingProperties": { - Fields: map[string]sdkModels.SDKField{ - "KeyName": { - JsonName: "keyName", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - "Identity": { - JsonName: "identity", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("UserAssignedIdentityProperties"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - "UserAssignedIdentityProperties": { - Fields: map[string]sdkModels.SDKField{ - "UserAssignedIdentity": { - JsonName: "userAssignedIdentity", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Model"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/example"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseModelWithReferenceToArray(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_with_reference_array.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Example": { - Models: map[string]sdkModels.SDKModel{ - "Model": { - Fields: map[string]sdkModels.SDKField{ - "Name": { - JsonName: "name", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - "Pets": { - JsonName: "pets", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - NestedItem: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Pet"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - - // TODO: re-enable min/max/unique - // Minimum: pointer.To(1), - // Maximum: pointer.To(2), - // UniqueItems: pointer.To(true), - }, - Type: sdkModels.ListSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - "Pet": { - Fields: map[string]sdkModels.SDKField{ - "Name": { - JsonName: "name", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Model"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/example"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseModelWithReferenceToConstant(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_with_reference_constant.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Example": { - Constants: map[string]sdkModels.SDKConstant{ - "AnimalType": { - Type: sdkModels.StringSDKConstantType, - Values: map[string]string{ - "Cat": "Cat", - "Dog": "Dog", - }, - }, - }, - Models: map[string]sdkModels.SDKModel{ - "Model": { - Fields: map[string]sdkModels.SDKField{ - "Name": { - JsonName: "name", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - "ThingProps": { - JsonName: "thingProps", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - NestedItem: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("ThingProperties"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - Type: sdkModels.ListSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - "ThingProperties": { - Fields: map[string]sdkModels.SDKField{ - "Animal": { - JsonName: "animal", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("AnimalType"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - Required: false, - }, - "KeyName": { - JsonName: "keyName", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Model"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/example"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseModelWithReferenceToString(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_with_reference_string.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Example": { - Models: map[string]sdkModels.SDKModel{ - "Model": { - Fields: map[string]sdkModels.SDKField{ - "Name": { - JsonName: "name", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - "ThingProps": { - JsonName: "thingProps", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - NestedItem: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("ThingProperties"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - Type: sdkModels.ListSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - "ThingProperties": { - Fields: map[string]sdkModels.SDKField{ - "FullyQualifiedDomainName": { - JsonName: "fullyQualifiedDomainName", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - "KeyName": { - JsonName: "keyName", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Model"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/example"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseModelWithCircularReferences(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_with_circular_reference.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Example": { - Models: map[string]sdkModels.SDKModel{ - "Animal": { - Fields: map[string]sdkModels.SDKField{ - "FavouriteHouse": { - JsonName: "favouriteHouse", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("House"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - Required: false, - }, - "FavouriteHuman": { - JsonName: "favouriteHuman", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Human"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - Required: false, - }, - "Name": { - JsonName: "name", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - "House": { - Fields: map[string]sdkModels.SDKField{ - "Address": { - JsonName: "address", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - "Residents": { - JsonName: "residents", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - NestedItem: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Human"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - Type: sdkModels.ListSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - "Human": { - Fields: map[string]sdkModels.SDKField{ - "Pets": { - JsonName: "pets", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - NestedItem: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Animal"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - Type: sdkModels.ListSDKObjectDefinitionType, - }, - Required: false, - }, - "Name": { - JsonName: "name", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("House"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/example"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseModelInheritingFromObjectWithNoExtraFields(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_inheriting_from_other_model_no_new_fields.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Example": { - Models: map[string]sdkModels.SDKModel{ - "FirstObject": { - Fields: map[string]sdkModels.SDKField{ - "Name": { - JsonName: "name", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - // whilst the response model references SecondObject, it's only inheriting from FirstObject - // and doesn't contain any new fields, so it should be switched out - ReferenceName: pointer.To("FirstObject"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/example"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseModelInheritingFromObjectWithNoExtraFieldsInlined(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_inheriting_from_other_model_no_new_fields_inlined.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Example": { - Models: map[string]sdkModels.SDKModel{ - "FirstObject": { - Fields: map[string]sdkModels.SDKField{ - "Endpoints": { - JsonName: "endpoints", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("SecondObject"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - Required: false, - }, - "Name": { - JsonName: "name", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - "SecondObject": { - Fields: map[string]sdkModels.SDKField{ - "Name": { - JsonName: "name", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("FirstObject"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/example"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseModelInheritingFromObjectWithOnlyDescription(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_inheriting_from_other_model_with_only_description.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Example": { - Models: map[string]sdkModels.SDKModel{ - "FirstObject": { - Fields: map[string]sdkModels.SDKField{ - "Name": { - JsonName: "name", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - // whilst the response model references SecondObject, it's only inheriting from FirstObject - // and doesn't contain any new fields, so it should be switched out - ReferenceName: pointer.To("FirstObject"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/example"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseModelInheritingFromObjectWithPropertiesWithinAllOf(t *testing.T) { - // This test ensures that when a Model inherits from a Model and defines properties within - // the `AllOf` field, that the Model isn't flattened into the Parent Model. - // 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) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Example": { - Models: map[string]sdkModels.SDKModel{ - "SecondObject": { - Fields: map[string]sdkModels.SDKField{ - "Name": { - JsonName: "name", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - // SecondObject is referenced as the Response Object, but because it inherits from one Model - // (FirstObject) and uses another (ThirdObject) it shouldn't be flattened into the parent type(s) - // and should instead remain `SecondObject`. - ReferenceName: pointer.To("SecondObject"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/example"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseModelContainingAllOfToTypeObject(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_containing_allof_object_type.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Example": { - Models: map[string]sdkModels.SDKModel{ - "Model": { - Fields: map[string]sdkModels.SDKField{ - "Name": { - JsonName: "name", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: true, - }, - "Country": { - JsonName: "country", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: true, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "PUT", - RequestObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Model"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Model"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/example"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseModelContainingAllOfToTypeObjectWithProperties(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_containing_allof_object_type_with_properties.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Example": { - Models: map[string]sdkModels.SDKModel{ - "Model": { - Fields: map[string]sdkModels.SDKField{ - "Name": { - JsonName: "name", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - "Country": { - JsonName: "country", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: true, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "PUT", - RequestObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Model"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Model"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/example"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseModelContainingAllOfWithinProperties(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_containing_allof_within_properties.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Example": { - Models: map[string]sdkModels.SDKModel{ - "Model": { - Fields: map[string]sdkModels.SDKField{ - "Country": { - JsonName: "country", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: true, - }, - "Properties": { - JsonName: "properties", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("ModelProperties"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - "ModelProperties": { - Fields: map[string]sdkModels.SDKField{ - "MoreNested": { - JsonName: "moreNested", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - "Name": { - JsonName: "name", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: true, - }, - "Nested": { - JsonName: "nested", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "PUT", - RequestObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Model"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Model"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/example"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseModelContainingMultipleAllOfWithinProperties(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_containing_allof_within_properties_multiple.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Example": { - Models: map[string]sdkModels.SDKModel{ - "Model": { - Fields: map[string]sdkModels.SDKField{ - "Options": { - JsonName: "options", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("ResourceWithLocation"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - Required: false, - }, - "Resource": { - JsonName: "resource", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("ModelResource"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - "ModelResource": { - Fields: map[string]sdkModels.SDKField{ - "Country": { - JsonName: "country", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - "MoreNested": { - JsonName: "moreNested", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - "Nested": { - JsonName: "nested", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - "ResourceWithLocation": { - Fields: map[string]sdkModels.SDKField{ - "Country": { - JsonName: "country", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: true, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "PUT", - RequestObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Model"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Model"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/example"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseModelContainingLists(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_containing_lists.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Example": { - Models: map[string]sdkModels.SDKModel{ - "Model": { - Fields: map[string]sdkModels.SDKField{ - "Animals": { - JsonName: "animals", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - NestedItem: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Animal"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - Type: sdkModels.ListSDKObjectDefinitionType, - }, - Required: false, - }, - "Name": { - JsonName: "name", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: true, - }, - "Plants": { - JsonName: "plants", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - NestedItem: &sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - - // TODO: re-enable min/max/unique - // Maximum: pointer.To(10), - // Minimum: pointer.To(1), - // UniqueItems: pointer.To(true), - }, - Type: sdkModels.ListSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - "Animal": { - Fields: map[string]sdkModels.SDKField{ - "Age": { - JsonName: "age", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.IntegerSDKObjectDefinitionType, - }, - Required: false, - }, - "Name": { - JsonName: "name", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: true, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Model"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/example"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseModelInlinedWithNoName(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_inlined_with_no_name.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Example": { - Models: map[string]sdkModels.SDKModel{ - "Container": { - Fields: map[string]sdkModels.SDKField{ - "Planets": { - JsonName: "planets", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - NestedItem: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("ContainerPlanetsInlined"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - Type: sdkModels.ListSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - "ContainerPlanetsInlined": { - Fields: map[string]sdkModels.SDKField{ - "ExampleField": { - JsonName: "exampleField", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Container"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/example"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseModelInheritingFromParent(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_inheriting_from_parent.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Example": { - Models: map[string]sdkModels.SDKModel{ - "Model": { - Fields: map[string]sdkModels.SDKField{ - "Age": { - JsonName: "age", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.IntegerSDKObjectDefinitionType, - }, - Required: false, - }, - "Enabled": { - JsonName: "enabled", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.BooleanSDKObjectDefinitionType, - }, - Required: true, - }, - "Height": { - JsonName: "height", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.FloatSDKObjectDefinitionType, - }, - Required: false, - }, - "Name": { - JsonName: "name", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: true, - }, - "Tags": { - JsonName: "tags", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.TagsSDKObjectDefinitionType, - }, - Required: true, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "PUT", - RequestObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Model"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Model"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/example"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseModelMultipleTopLevelModelsAndOperations(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "model_multiple_top_level_models_and_operations.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Example": { - Models: map[string]sdkModels.SDKModel{ - "GetExample": { - Fields: map[string]sdkModels.SDKField{ - "Age": { - JsonName: "age", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.IntegerSDKObjectDefinitionType, - }, - Required: false, - }, - "Enabled": { - JsonName: "enabled", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.BooleanSDKObjectDefinitionType, - }, - Required: false, - }, - "Name": { - JsonName: "name", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: true, - }, - "Tags": { - JsonName: "tags", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.TagsSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - "PutExample": { - Fields: map[string]sdkModels.SDKField{ - "Age": { - JsonName: "age", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.IntegerSDKObjectDefinitionType, - }, - Required: false, - }, - "Enabled": { - JsonName: "enabled", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.BooleanSDKObjectDefinitionType, - }, - Required: false, - }, - "Name": { - JsonName: "name", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: true, - }, - "Tags": { - JsonName: "tags", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.TagsSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Get": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("GetExample"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/example"), - }, - "Put": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "PUT", - RequestObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("PutExample"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("PutExample"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/example"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseModelBug2675DuplicateModel(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "models_bug_2675_duplicate_model.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Example": { - Models: map[string]sdkModels.SDKModel{ - "EnvironmentRole": { - Fields: map[string]sdkModels.SDKField{ - "Description": { - JsonName: "description", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - ReadOnly: true, - Required: false, - }, - "RoleName": { - JsonName: "roleName", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - ReadOnly: true, - Required: false, - }, - }, - }, - "ExampleEnvironment": { - Fields: map[string]sdkModels.SDKField{ - "Location": { - JsonName: "location", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.LocationSDKObjectDefinitionType, - }, - Required: false, - }, - "Properties": { - JsonName: "properties", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("ExampleEnvironmentProperties"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - "ExampleEnvironmentProperties": { - Fields: map[string]sdkModels.SDKField{ - "CreatorRoleAssignment": { - JsonName: "creatorRoleAssignment", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("ExampleEnvironmentUpdatePropertiesCreatorRoleAssignment"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - Required: false, - }, - "DeploymentTargetId": { - JsonName: "deploymentTargetId", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - "ProvisioningState": { - JsonName: "provisioningState", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - ReadOnly: true, - Required: false, - }, - "UserRoleAssignments": { - JsonName: "userRoleAssignments", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - NestedItem: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("UserRoleAssignment"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - Type: sdkModels.DictionarySDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - "ExampleEnvironmentUpdate": { - Fields: map[string]sdkModels.SDKField{ - "Example": { - JsonName: "example", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - "Properties": { - JsonName: "properties", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("ExampleEnvironmentUpdateProperties"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - "ExampleEnvironmentUpdateProperties": { - Fields: map[string]sdkModels.SDKField{ - "CreatorRoleAssignment": { - JsonName: "creatorRoleAssignment", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("ExampleEnvironmentUpdatePropertiesCreatorRoleAssignment"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - Required: false, - }, - "DeploymentTargetId": { - JsonName: "deploymentTargetId", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - "UserRoleAssignments": { - JsonName: "userRoleAssignments", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - NestedItem: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("UserRoleAssignment"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - Type: sdkModels.DictionarySDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - "ExampleEnvironmentUpdatePropertiesCreatorRoleAssignment": { - Fields: map[string]sdkModels.SDKField{ - "Roles": { - JsonName: "roles", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - NestedItem: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("EnvironmentRole"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - Type: sdkModels.DictionarySDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - "UserRoleAssignment": { - Fields: map[string]sdkModels.SDKField{ - "Roles": { - JsonName: "roles", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - NestedItem: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("EnvironmentRole"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - Type: sdkModels.DictionarySDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "CreateOrUpdate": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "PUT", - RequestObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("ExampleEnvironment"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - ResourceIDName: pointer.To("EnvironmentId"), - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("ExampleEnvironment"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - }, - "Get": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResourceIDName: pointer.To("EnvironmentId"), - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("ExampleEnvironment"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - }, - "Update": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "PATCH", - RequestObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("ExampleEnvironmentUpdate"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - ResourceIDName: pointer.To("EnvironmentId"), - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("ExampleEnvironment"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - }, - }, - ResourceIDs: map[string]sdkModels.ResourceID{ - "EnvironmentId": { - Segments: []sdkModels.ResourceIDSegment{ - sdkModels.NewStaticValueResourceIDSegment("staticEnvironments", "environments"), - sdkModels.NewUserSpecifiedResourceIDSegment("environmentName", "environmentName"), - }, - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} diff --git a/tools/importer-rest-api-specs/components/parser/ported_object_definition.go b/tools/importer-rest-api-specs/components/parser/ported_object_definition.go deleted file mode 100644 index a53d5e9c20c..00000000000 --- a/tools/importer-rest-api-specs/components/parser/ported_object_definition.go +++ /dev/null @@ -1,379 +0,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/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" -) - -// if `parsingModel` 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 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: &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 := 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 - 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 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 := 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 -} diff --git a/tools/importer-rest-api-specs/components/parser/ported_operations.go b/tools/importer-rest-api-specs/components/parser/ported_operations.go deleted file mode 100644 index 875c4e30d6e..00000000000 --- a/tools/importer-rest-api-specs/components/parser/ported_operations.go +++ /dev/null @@ -1,582 +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/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/components/apidefinitions/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 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) - - 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 ignore.Operation(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, *parserModels.ParseResult, error) { - result := parserModels.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, *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 := 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.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 (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 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 := 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 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 := 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 := cleanup.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/ported_operations_test.go b/tools/importer-rest-api-specs/components/parser/ported_operations_test.go deleted file mode 100644 index aa32b57e400..00000000000 --- a/tools/importer-rest-api-specs/components/parser/ported_operations_test.go +++ /dev/null @@ -1,1588 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package parser - -import ( - "net/http" - "testing" - - "github.com/hashicorp/go-azure-helpers/lang/pointer" - sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/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) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{}, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseOperationSingleWithTag(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_with_tag.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Hello": { - Operations: map[string]sdkModels.SDKOperation{ - "HeadWorld": { - ContentType: "application/json", - ExpectedStatusCodes: []int{http.StatusOK}, - LongRunning: false, - Method: http.MethodHead, - URISuffix: pointer.To("/things"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseOperationSingleWithTagAndResourceId(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_with_tag_resource_id.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Hello": { - Operations: map[string]sdkModels.SDKOperation{ - "HeadWorld": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "HEAD", - ResourceIDName: pointer.To("ThingId"), - }, - }, - ResourceIDs: map[string]sdkModels.ResourceID{ - "ThingId": { - Segments: []sdkModels.ResourceIDSegment{ - sdkModels.NewStaticValueResourceIDSegment("staticSubscriptions", "subscriptions"), - sdkModels.NewSubscriptionIDResourceIDSegment("subscriptionId"), - sdkModels.NewStaticValueResourceIDSegment("staticResourceGroups", "resourceGroups"), - sdkModels.NewResourceGroupNameResourceIDSegment("resourceGroupName"), - sdkModels.NewStaticValueResourceIDSegment("staticProviders", "providers"), - sdkModels.NewResourceProviderResourceIDSegment("staticMicrosoftFooBar", "Microsoft.FooBar"), - sdkModels.NewStaticValueResourceIDSegment("staticThings", "things"), - sdkModels.NewUserSpecifiedResourceIDSegment("thing", "thing"), - }, - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseOperationSingleWithTagAndResourceIdSuffix(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_with_tag_resource_id_suffix.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Hello": { - Operations: map[string]sdkModels.SDKOperation{ - "HeadWorld": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "HEAD", - ResourceIDName: pointer.To("ThingId"), - URISuffix: pointer.To("/restart"), - }, - }, - ResourceIDs: map[string]sdkModels.ResourceID{ - "ThingId": { - Segments: []sdkModels.ResourceIDSegment{ - sdkModels.NewStaticValueResourceIDSegment("staticSubscriptions", "subscriptions"), - sdkModels.NewSubscriptionIDResourceIDSegment("subscriptionId"), - sdkModels.NewStaticValueResourceIDSegment("staticResourceGroups", "resourceGroups"), - sdkModels.NewResourceGroupNameResourceIDSegment("resourceGroupName"), - sdkModels.NewStaticValueResourceIDSegment("staticProviders", "providers"), - sdkModels.NewResourceProviderResourceIDSegment("staticMicrosoftFooBar", "Microsoft.FooBar"), - sdkModels.NewStaticValueResourceIDSegment("staticThings", "things"), - sdkModels.NewUserSpecifiedResourceIDSegment("thing", "thing"), - }, - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseOperationSingleWithRequestObject(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_with_request_object.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Hello": { - Models: map[string]sdkModels.SDKModel{ - "Example": { - Fields: map[string]sdkModels.SDKField{ - "Name": { - JsonName: "name", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "PutWorld": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "PUT", - RequestObject: &sdkModels.SDKObjectDefinition{ - Type: sdkModels.ReferenceSDKObjectDefinitionType, - ReferenceName: pointer.To("Example"), - }, - URISuffix: pointer.To("/things"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseOperationSingleWithRequestObjectInlined(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_with_request_object_inlined.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Hello": { - Models: map[string]sdkModels.SDKModel{ - "Example": { - Fields: map[string]sdkModels.SDKField{ - "Name": { - JsonName: "name", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "PutWorld": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "PUT", - RequestObject: &sdkModels.SDKObjectDefinition{ - Type: sdkModels.ReferenceSDKObjectDefinitionType, - ReferenceName: pointer.To("Example"), - }, - URISuffix: pointer.To("/things"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseOperationSingleWithResponseObject(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_with_response_object.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Hello": { - Models: map[string]sdkModels.SDKModel{ - "Example": { - Fields: map[string]sdkModels.SDKField{ - "Name": { - JsonName: "name", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "GetWorld": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - Type: sdkModels.ReferenceSDKObjectDefinitionType, - ReferenceName: pointer.To("Example"), - }, - URISuffix: pointer.To("/things"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseOperationSingleWithResponseObjectInlined(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_with_response_object_inlined.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Hello": { - Models: map[string]sdkModels.SDKModel{ - "Example": { - Fields: map[string]sdkModels.SDKField{ - "Name": { - JsonName: "name", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "GetWorld": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - Type: sdkModels.ReferenceSDKObjectDefinitionType, - ReferenceName: pointer.To("Example"), - }, - URISuffix: pointer.To("/things"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseOperationSingleWithResponseObjectInlinedList(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_with_response_object_inlined_list.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Hello": { - Models: map[string]sdkModels.SDKModel{ - "Example": { - Fields: map[string]sdkModels.SDKField{ - "Name": { - JsonName: "name", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "GetWorld": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - Type: sdkModels.ListSDKObjectDefinitionType, - NestedItem: &sdkModels.SDKObjectDefinition{ - Type: sdkModels.ReferenceSDKObjectDefinitionType, - ReferenceName: pointer.To("Example"), - }, - }, - URISuffix: pointer.To("/things"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseOperationSingleRequestingWithABool(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_requesting_with_a_bool.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Hello": { - Operations: map[string]sdkModels.SDKOperation{ - "PutWorld": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "PUT", - RequestObject: &sdkModels.SDKObjectDefinition{ - Type: sdkModels.BooleanSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/things"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseOperationSingleRequestingWithAInteger(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_requesting_with_a_int.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Hello": { - Operations: map[string]sdkModels.SDKOperation{ - "PutWorld": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "PUT", - RequestObject: &sdkModels.SDKObjectDefinition{ - Type: sdkModels.IntegerSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/things"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseOperationSingleRequestingWithADictionaryOfStrings(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_requesting_with_a_dictionary_of_strings.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Hello": { - Operations: map[string]sdkModels.SDKOperation{ - "PutWorld": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "PUT", - RequestObject: &sdkModels.SDKObjectDefinition{ - Type: sdkModels.DictionarySDKObjectDefinitionType, - NestedItem: &sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - }, - URISuffix: pointer.To("/things"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseOperationSingleRequestingWithAListOfStrings(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_requesting_with_a_list_of_strings.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Hello": { - Operations: map[string]sdkModels.SDKOperation{ - "PutWorld": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "PUT", - RequestObject: &sdkModels.SDKObjectDefinition{ - Type: sdkModels.ListSDKObjectDefinitionType, - NestedItem: &sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - }, - URISuffix: pointer.To("/things"), - }, - }, - }, - }, - } - 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) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Hello": { - Operations: map[string]sdkModels.SDKOperation{ - "PutWorld": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "PUT", - RequestObject: &sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/things"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseOperationSingleReturningABool(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_returning_a_bool.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Hello": { - Operations: map[string]sdkModels.SDKOperation{ - "GimmeABoolean": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - Type: sdkModels.BooleanSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/worlds/favourite"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseOperationSingleReturningAFloat(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_returning_a_float.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Hello": { - Operations: map[string]sdkModels.SDKOperation{ - "GimmeAFloat": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - Type: sdkModels.FloatSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/worlds/favourite"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseOperationSingleReturningAFile(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_returning_a_file.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Hello": { - Operations: map[string]sdkModels.SDKOperation{ - "GimmeAFile": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - Type: sdkModels.RawFileSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/worlds/favourite"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseOperationSingleReturningAnInteger(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_returning_an_integer.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Hello": { - Operations: map[string]sdkModels.SDKOperation{ - "GimmeAnInteger": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - Type: sdkModels.IntegerSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/worlds/favourite"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseOperationSingleReturningAString(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_returning_a_string.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Hello": { - Operations: map[string]sdkModels.SDKOperation{ - "GimmeAString": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/worlds/favourite"), - }, - }, - }, - }, - } - 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) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Hello": { - Operations: map[string]sdkModels.SDKOperation{ - "GimmeAString": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/worlds/favourite"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseOperationSingleReturningATopLevelRawObject(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_returning_a_top_level_raw_object.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Hello": { - Operations: map[string]sdkModels.SDKOperation{ - "RawObjectToMeToYou": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - RequestObject: &sdkModels.SDKObjectDefinition{ - Type: sdkModels.RawObjectSDKObjectDefinitionType, - }, - ResponseObject: &sdkModels.SDKObjectDefinition{ - Type: sdkModels.RawObjectSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/chuckle"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseOperationSingleReturningADictionaryOfAModel(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_returning_a_dictionary_of_model.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Hello": { - Models: map[string]sdkModels.SDKModel{ - "Person": { - Fields: map[string]sdkModels.SDKField{ - "Name": { - JsonName: "name", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "GimmeADictionaryOfAModel": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - Type: sdkModels.DictionarySDKObjectDefinitionType, - NestedItem: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Person"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - }, - URISuffix: pointer.To("/worlds/favourite"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseOperationSingleReturningADictionaryOfStrings(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_returning_a_dictionary_of_strings.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Hello": { - Operations: map[string]sdkModels.SDKOperation{ - "GimmeADictionaryOfStrings": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - Type: sdkModels.DictionarySDKObjectDefinitionType, - NestedItem: &sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - }, - URISuffix: pointer.To("/worlds/favourite"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseOperationSingleReturningAListOfIntegers(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_returning_a_list_of_ints.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Hello": { - Operations: map[string]sdkModels.SDKOperation{ - "GimmeAListOfIntegers": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - Type: sdkModels.ListSDKObjectDefinitionType, - NestedItem: &sdkModels.SDKObjectDefinition{ - Type: sdkModels.IntegerSDKObjectDefinitionType, - }, - }, - URISuffix: pointer.To("/worlds/favourite"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseOperationSingleReturningAListOfAModel(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_returning_a_list_of_model.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Hello": { - Models: map[string]sdkModels.SDKModel{ - "Person": { - Fields: map[string]sdkModels.SDKField{ - "Name": { - JsonName: "name", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "GimmeAListOfModels": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - Type: sdkModels.ListSDKObjectDefinitionType, - NestedItem: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Person"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - }, - URISuffix: pointer.To("/worlds/favourite"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseOperationSingleReturningAListOfStrings(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_returning_a_list_of_strings.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Hello": { - Operations: map[string]sdkModels.SDKOperation{ - "GimmeAListOfStrings": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - Type: sdkModels.ListSDKObjectDefinitionType, - NestedItem: &sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - }, - URISuffix: pointer.To("/worlds/favourite"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseOperationSingleReturningAListOfListOfAModel(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_returning_a_list_of_list_of_model.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Hello": { - Models: map[string]sdkModels.SDKModel{ - "Person": { - Fields: map[string]sdkModels.SDKField{ - "Name": { - JsonName: "name", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "GimmeAListOfListOfModels": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - Type: sdkModels.ListSDKObjectDefinitionType, - NestedItem: &sdkModels.SDKObjectDefinition{ - Type: sdkModels.ListSDKObjectDefinitionType, - NestedItem: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Person"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - }, - }, - URISuffix: pointer.To("/worlds/favourite"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseOperationSingleReturningAListOfListOfStrings(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_returning_a_list_of_list_of_strings.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Hello": { - Operations: map[string]sdkModels.SDKOperation{ - "GimmeAListOfListOfStrings": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - Type: sdkModels.ListSDKObjectDefinitionType, - NestedItem: &sdkModels.SDKObjectDefinition{ - Type: sdkModels.ListSDKObjectDefinitionType, - NestedItem: &sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - }, - }, - URISuffix: pointer.To("/worlds/favourite"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseOperationSingleWithList(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_list.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Hello": { - Models: map[string]sdkModels.SDKModel{ - "World": { - Fields: map[string]sdkModels.SDKField{ - "Name": { - JsonName: "name", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "ListWorlds": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - FieldContainingPaginationDetails: pointer.To("nextLink"), - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("World"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/worlds"), - }, - }, - }, - }, - } - 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) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Hello": { - Models: map[string]sdkModels.SDKModel{ - "World": { - Fields: map[string]sdkModels.SDKField{ - "Name": { - JsonName: "name", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "ListWorlds": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - // This signifies there's a list of items without a means of paging over it. - // This is _likely_ a badly documented API Definition, but it's hard to say for sure. - // Since there isn't a pagination field present in the response, we have to assume this isn't - // a list operation, even if there's a `skipToken` present. - FieldContainingPaginationDetails: nil, - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("World"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/worlds"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseOperationSingleWithListReturningAListOfStrings(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_list_of_strings.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Hello": { - Operations: map[string]sdkModels.SDKOperation{ - "ListWorlds": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - FieldContainingPaginationDetails: pointer.To("nextLink"), - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/worlds"), - }, - }, - }, - }, - } - 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) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Hello": { - Models: map[string]sdkModels.SDKModel{ - "World": { - Fields: map[string]sdkModels.SDKField{ - "Name": { - JsonName: "name", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "ListWorlds": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - FieldContainingPaginationDetails: pointer.To("nextLink"), - Method: "GET", - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("World"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/worlds"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseOperationSingleWithLongRunningOperation(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_long_running.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Hello": { - Models: map[string]sdkModels.SDKModel{ - "Example": { - Fields: map[string]sdkModels.SDKField{ - "Name": { - JsonName: "name", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "PutWorld": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - LongRunning: true, - Method: "PUT", - RequestObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Example"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/things"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseOperationSingleWithRequestAndResponseObject(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_with_request_and_response_object.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Hello": { - Models: map[string]sdkModels.SDKModel{ - "Example": { - Fields: map[string]sdkModels.SDKField{ - "Name": { - JsonName: "name", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "PutWorld": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "PUT", - RequestObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Example"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Example"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/things"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseOperationSingleWithMultipleTags(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_multiple_tags.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Hello": { - Operations: map[string]sdkModels.SDKOperation{ - "HeadThings": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "HEAD", - URISuffix: pointer.To("/things"), - }, - }, - }, - "Other": { - Operations: map[string]sdkModels.SDKOperation{ - // Whilst the operation name should be `HeadThings`, since this is another Tag - // it's intentionally prefixed for when things cross boundaries (to avoid conflicts) - "HelloHeadThings": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "HEAD", - URISuffix: pointer.To("/things"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseOperationSingleWithInferredTag(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_with_no_tag.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - 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": { - Operations: map[string]sdkModels.SDKOperation{ - // since the prefix doesn't match the Tag (since no tag) this gets a combined name - "HelloHeadWorld": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "HEAD", - URISuffix: pointer.To("/things"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseOperationSingleWithHeaderOptions(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_with_header_options.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Hello": { - Operations: map[string]sdkModels.SDKOperation{ - "HeadWorld": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "HEAD", - Options: map[string]sdkModels.SDKOperationOption{ - "BoolValue": { - HeaderName: pointer.To("boolValue"), - ObjectDefinition: sdkModels.SDKOperationOptionObjectDefinition{ - Type: sdkModels.BooleanSDKOperationOptionObjectDefinitionType, - }, - Required: true, - }, - "CsvOfDoubleValue": { - HeaderName: pointer.To("csvOfDoubleValue"), - ObjectDefinition: sdkModels.SDKOperationOptionObjectDefinition{ - Type: sdkModels.CSVSDKOperationOptionObjectDefinitionType, - NestedItem: &sdkModels.SDKOperationOptionObjectDefinition{ - Type: sdkModels.FloatSDKOperationOptionObjectDefinitionType, - }, - }, - Required: true, - }, - "CsvOfStringValue": { - HeaderName: pointer.To("csvOfStringValue"), - ObjectDefinition: sdkModels.SDKOperationOptionObjectDefinition{ - Type: sdkModels.CSVSDKOperationOptionObjectDefinitionType, - NestedItem: &sdkModels.SDKOperationOptionObjectDefinition{ - Type: sdkModels.StringSDKOperationOptionObjectDefinitionType, - }, - }, - Required: true, - }, - "DecimalValue": { - HeaderName: pointer.To("decimalValue"), - ObjectDefinition: sdkModels.SDKOperationOptionObjectDefinition{ - Type: sdkModels.FloatSDKOperationOptionObjectDefinitionType, - }, - Required: true, - }, - "DoubleValue": { - HeaderName: pointer.To("doubleValue"), - ObjectDefinition: sdkModels.SDKOperationOptionObjectDefinition{ - Type: sdkModels.FloatSDKOperationOptionObjectDefinitionType, - }, - Required: true, - }, - "IntValue": { - HeaderName: pointer.To("intValue"), - ObjectDefinition: sdkModels.SDKOperationOptionObjectDefinition{ - Type: sdkModels.IntegerSDKOperationOptionObjectDefinitionType, - }, - Required: true, - }, - "StringValue": { - HeaderName: pointer.To("stringValue"), - ObjectDefinition: sdkModels.SDKOperationOptionObjectDefinition{ - Type: sdkModels.StringSDKOperationOptionObjectDefinitionType, - }, - Required: true, - }, - }, - URISuffix: pointer.To("/things"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseOperationSingleWithQueryStringOptions(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_with_querystring_options.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Hello": { - Operations: map[string]sdkModels.SDKOperation{ - "HeadWorld": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "HEAD", - Options: map[string]sdkModels.SDKOperationOption{ - "BoolValue": { - QueryStringName: pointer.To("boolValue"), - ObjectDefinition: sdkModels.SDKOperationOptionObjectDefinition{ - Type: sdkModels.BooleanSDKOperationOptionObjectDefinitionType, - }, - Required: true, - }, - "CsvOfDoubleValue": { - QueryStringName: pointer.To("csvOfDoubleValue"), - ObjectDefinition: sdkModels.SDKOperationOptionObjectDefinition{ - Type: sdkModels.CSVSDKOperationOptionObjectDefinitionType, - NestedItem: &sdkModels.SDKOperationOptionObjectDefinition{ - Type: sdkModels.FloatSDKOperationOptionObjectDefinitionType, - }, - }, - Required: true, - }, - "CsvOfStringValue": { - QueryStringName: pointer.To("csvOfStringValue"), - ObjectDefinition: sdkModels.SDKOperationOptionObjectDefinition{ - Type: sdkModels.CSVSDKOperationOptionObjectDefinitionType, - NestedItem: &sdkModels.SDKOperationOptionObjectDefinition{ - Type: sdkModels.StringSDKOperationOptionObjectDefinitionType, - }, - }, - Required: true, - }, - "DecimalValue": { - QueryStringName: pointer.To("decimalValue"), - ObjectDefinition: sdkModels.SDKOperationOptionObjectDefinition{ - Type: sdkModels.FloatSDKOperationOptionObjectDefinitionType, - }, - Required: true, - }, - "DoubleValue": { - QueryStringName: pointer.To("doubleValue"), - ObjectDefinition: sdkModels.SDKOperationOptionObjectDefinition{ - Type: sdkModels.FloatSDKOperationOptionObjectDefinitionType, - }, - Required: true, - }, - "IntValue": { - QueryStringName: pointer.To("intValue"), - ObjectDefinition: sdkModels.SDKOperationOptionObjectDefinition{ - Type: sdkModels.IntegerSDKOperationOptionObjectDefinitionType, - }, - Required: true, - }, - "StringValue": { - QueryStringName: pointer.To("stringValue"), - ObjectDefinition: sdkModels.SDKOperationOptionObjectDefinition{ - Type: sdkModels.StringSDKOperationOptionObjectDefinitionType, - }, - Required: true, - }, - }, - URISuffix: pointer.To("/things"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseOperationMultipleBasedOnTheSameResourceId(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_multiple_same_resource_id.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Hello": { - Operations: map[string]sdkModels.SDKOperation{ - "HeadWorld": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "HEAD", - ResourceIDName: pointer.To("ThingId"), - }, - "RestartWorld": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "HEAD", - ResourceIDName: pointer.To("ThingId"), - URISuffix: pointer.To("/restart"), - }, - }, - ResourceIDs: map[string]sdkModels.ResourceID{ - "ThingId": { - Segments: []sdkModels.ResourceIDSegment{ - sdkModels.NewStaticValueResourceIDSegment("staticSubscriptions", "subscriptions"), - sdkModels.NewSubscriptionIDResourceIDSegment("subscriptionId"), - sdkModels.NewStaticValueResourceIDSegment("staticResourceGroups", "resourceGroups"), - sdkModels.NewResourceGroupNameResourceIDSegment("resourceGroupName"), - sdkModels.NewStaticValueResourceIDSegment("staticProviders", "providers"), - sdkModels.NewResourceProviderResourceIDSegment("staticMicrosoftFooBar", "Microsoft.FooBar"), - sdkModels.NewStaticValueResourceIDSegment("staticThings", "things"), - sdkModels.NewUserSpecifiedResourceIDSegment("thing", "thing"), - }, - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseOperationsContainingContentTypes(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operation_content_types.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Hello": { - Operations: map[string]sdkModels.SDKOperation{ - "Default": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "HEAD", - URISuffix: pointer.To("/default"), - }, - "JsonRequest": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "GET", - RequestObject: &sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - // this can become `/json-request` when https://github.com/hashicorp/pandora/issues/3807 is fixed - URISuffix: pointer.To("/jsonRequest"), - }, - "JsonResponse": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "PUT", - ResponseObject: &sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/jsonResponse"), - }, - "XmlRequest": { - ContentType: "application/xml", - ExpectedStatusCodes: []int{200}, - Method: "GET", - RequestObject: &sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/xmlRequest"), - }, - "XmlResponse": { - ContentType: "application/xml", - ExpectedStatusCodes: []int{200}, - Method: "PUT", - ResponseObject: &sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/xmlResponse"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseOperationContainingMultipleReturnObjects(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_multiple_return_objects.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Hello": { - Models: map[string]sdkModels.SDKModel{ - "FirstModel": { - Fields: map[string]sdkModels.SDKField{ - "Hello": { - JsonName: "hello", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "HeadWorld": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200, 202}, - Method: "PUT", - ResponseObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("FirstModel"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/things"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseOperationsWithStutteringNames(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_with_stuttering_names.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "ExampleTag": { - Operations: map[string]sdkModels.SDKOperation{ - "Mars": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "HEAD", - URISuffix: pointer.To("/mars"), - }, - "There": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "HEAD", - URISuffix: pointer.To("/there"), - }, - "World": { - // TODO: change the Swagger Tag to be `ExampleTag` for this operation - // There's a bug where multiple operations using the same (normalized) Swagger Tag aren't being - // flattened correctly. - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "HEAD", - URISuffix: pointer.To("/world"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} diff --git a/tools/importer-rest-api-specs/components/parser/ported_parser.go b/tools/importer-rest-api-specs/components/parser/ported_parser.go deleted file mode 100644 index 59d3f3ab1b1..00000000000 --- a/tools/importer-rest-api-specs/components/parser/ported_parser.go +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package parser - -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" - "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" - 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) { - apiResources := make(map[string]sdkModels.APIResource) - - tags := d.findTags() - // first we assume everything has a tag - for _, tag := range tags { - if ignore.SwaggerTag(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 := cleanup.NormalizeTag(tag) - normalizedTag = cleanup.NormalizeResourceName(normalizedTag) - apiResources[normalizedTag] = *resource - } - } - - // however some things don't, so we then need to iterate over any without them - if !ignore.SwaggerTag(serviceName) { - 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 := cleanup.NormalizeTag(inferredTag) - normalizedTag = cleanup.NormalizeResourceName(normalizedTag) - - if mergeResources, ok := apiResources[normalizedTag]; ok { - apiResources[normalizedTag] = importerModels.MergeResourcesForTag(mergeResources, *resource) - } else { - apiResources[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 apiResources { - logging.Tracef("Simplifying operation names for resource %q", resourceName) - updated := cleanup.SimplifyOperationNamesForAPIResource(resourceName, resource) - 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(apiResources) == 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 = cleanup.NormalizeAPIResource(resource) - - if mergeResources, ok := apiResources[normalizedTag]; ok { - apiResources[normalizedTag] = importerModels.MergeResourcesForTag(mergeResources, resource) - } else { - resourcesOut[normalizedTag] = resource - } - } - } - - return &importerModels.AzureApiDefinition{ - ServiceName: cleanup.NormalizeServiceName(serviceName), - ApiVersion: apiVersion, - Resources: resourcesOut, - }, nil -} diff --git a/tools/importer-rest-api-specs/components/parser/ported_refactor_glue_logic.go b/tools/importer-rest-api-specs/components/parser/ported_refactor_glue_logic.go deleted file mode 100644 index d961211c1a3..00000000000 --- a/tools/importer-rest-api-specs/components/parser/ported_refactor_glue_logic.go +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package parser - -import ( - 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 removeUnusedItems(input map[string]sdkModels.APIResource) map[string]sdkModels.APIResource { - // temporary glue logic to use the new package without breaking things too much - apiVersion := sdkModels.APIVersion{ - Resources: input, - - // placeholders - APIVersion: "2020-01-01", - Generate: true, - Preview: false, - Source: sdkModels.AzureRestAPISpecsSourceDataOrigin, - } - apiVersion = cleanup.RemoveUnusedItems(apiVersion) - return apiVersion.Resources -} diff --git a/tools/importer-rest-api-specs/components/parser/ported_resource_ids.go b/tools/importer-rest-api-specs/components/parser/ported_resource_ids.go deleted file mode 100644 index 8451f6cfa64..00000000000 --- a/tools/importer-rest-api-specs/components/parser/ported_resource_ids.go +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package parser - -import ( - "fmt" - - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids" -) - -func (d *SwaggerDefinition) ParseResourceIds() (*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 -} diff --git a/tools/importer-rest-api-specs/components/parser/ported_resource_ids_test.go b/tools/importer-rest-api-specs/components/parser/ported_resource_ids_test.go deleted file mode 100644 index a86008582d3..00000000000 --- a/tools/importer-rest-api-specs/components/parser/ported_resource_ids_test.go +++ /dev/null @@ -1,751 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package parser - -import ( - "testing" - - "github.com/hashicorp/go-azure-helpers/lang/pointer" - sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" -) - -func TestParseResourceIdBasic(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "resource_ids_basic.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Example": { - ResourceIDs: map[string]sdkModels.ResourceID{ - "ServerId": { - Segments: []sdkModels.ResourceIDSegment{ - sdkModels.NewStaticValueResourceIDSegment("staticSubscriptions", "subscriptions"), - sdkModels.NewSubscriptionIDResourceIDSegment("subscriptionId"), - sdkModels.NewStaticValueResourceIDSegment("staticResourceGroups", "resourceGroups"), - sdkModels.NewResourceGroupNameResourceIDSegment("resourceGroupName"), - sdkModels.NewStaticValueResourceIDSegment("staticProviders", "providers"), - sdkModels.NewResourceProviderResourceIDSegment("staticMicrosoftSomeResourceProvider", "Microsoft.SomeResourceProvider"), - sdkModels.NewStaticValueResourceIDSegment("staticServers", "servers"), - sdkModels.NewUserSpecifiedResourceIDSegment("serverName", "serverName"), - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "HEAD", - ResourceIDName: pointer.To("ServerId"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseResourceIdContainingAConstant(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "resource_ids_containing_constant.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Example": { - Constants: map[string]sdkModels.SDKConstant{ - "Planet": { - Type: sdkModels.StringSDKConstantType, - Values: map[string]string{ - "Earth": "Earth", - "Jupiter": "Jupiter", - "Mars": "Mars", - "Saturn": "Saturn", - }, - }, - }, - ResourceIDs: map[string]sdkModels.ResourceID{ - "PlanetId": { - ConstantNames: []string{ - "Planet", - }, - Segments: []sdkModels.ResourceIDSegment{ - sdkModels.NewStaticValueResourceIDSegment("staticPlanets", "planets"), - sdkModels.NewConstantResourceIDSegment("planetName", "Planet", "Earth"), - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "OperationContainingAConstant": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "HEAD", - ResourceIDName: pointer.To("PlanetId"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseResourceIdContainingAScope(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "resource_ids_containing_scope.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Example": { - ResourceIDs: map[string]sdkModels.ResourceID{ - "ScopedVirtualMachineId": { - Segments: []sdkModels.ResourceIDSegment{ - sdkModels.NewScopeResourceIDSegment("scope"), - sdkModels.NewStaticValueResourceIDSegment("staticProviders", "providers"), - sdkModels.NewResourceProviderResourceIDSegment("staticMicrosoftFooBar", "Microsoft.FooBar"), - sdkModels.NewStaticValueResourceIDSegment("staticVirtualMachines", "virtualMachines"), - sdkModels.NewUserSpecifiedResourceIDSegment("virtualMachineName", "virtualMachinesName"), - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "OperationContainingAScope": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "HEAD", - ResourceIDName: pointer.To("ScopedVirtualMachineId"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseResourceIdContainingAHiddenScope(t *testing.T) { - files := []string{ - "resource_ids_containing_hidden_scope.json", - "resource_ids_containing_hidden_scope_constant.json", - "resource_ids_containing_hidden_scope_hardcoded_provider.json", - } - for _, file := range files { - t.Run(file, func(t *testing.T) { - fileName := file - - actual, err := ParseSwaggerFileForTesting(t, fileName, nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Example": { - ResourceIDs: map[string]sdkModels.ResourceID{ - "ScopeId": { - CommonIDAlias: pointer.To("Scope"), - Segments: []sdkModels.ResourceIDSegment{ - sdkModels.NewScopeResourceIDSegment("scope"), - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "OperationContainingAHiddenScope": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "HEAD", - ResourceIDName: pointer.To("ScopeId"), - }, - }, - }, - }, - } - 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) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Example": { - ResourceIDs: map[string]sdkModels.ResourceID{ - "ScopeId": { - CommonIDAlias: pointer.To("Scope"), - Segments: []sdkModels.ResourceIDSegment{ - sdkModels.NewScopeResourceIDSegment("scope"), - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "OperationContainingAHiddenScope": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "HEAD", - ResourceIDName: pointer.To("ScopeId"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseResourceIdContainingAHiddenScopeWithSuffix(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "resource_ids_containing_hidden_scope_with_suffix.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Example": { - ResourceIDs: map[string]sdkModels.ResourceID{ - "ScopeId": { - CommonIDAlias: pointer.To("Scope"), - Segments: []sdkModels.ResourceIDSegment{ - sdkModels.NewScopeResourceIDSegment("scope"), - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "OperationContainingAHiddenScope": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "HEAD", - ResourceIDName: pointer.To("ScopeId"), - URISuffix: pointer.To("/someEndpoint"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseResourceIdContainingAHiddenScopeNested(t *testing.T) { - files := []string{ - "resource_ids_containing_hidden_scope_nested.json", - "resource_ids_containing_hidden_scope_nested_constants.json", - "resource_ids_containing_hidden_scope_nested_hardcoded_provider.json", - } - for _, file := range files { - t.Run(file, func(t *testing.T) { - fileName := file - - actual, err := ParseSwaggerFileForTesting(t, fileName, nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Example": { - ResourceIDs: map[string]sdkModels.ResourceID{ - "ScopeId": { - CommonIDAlias: pointer.To("Scope"), - Segments: []sdkModels.ResourceIDSegment{ - sdkModels.NewScopeResourceIDSegment("scope"), - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "OperationContainingAHiddenScope": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "HEAD", - ResourceIDName: pointer.To("ScopeId"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) - }) - } -} - -func TestParseResourceIdContainingAHiddenScopeNestedWithExtraSegment(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "resource_ids_containing_hidden_scope_nested_with_extra_segment.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Example": { - ResourceIDs: map[string]sdkModels.ResourceID{ - "ScopeId": { - CommonIDAlias: pointer.To("Scope"), - Segments: []sdkModels.ResourceIDSegment{ - sdkModels.NewScopeResourceIDSegment("scope"), - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "OperationContainingAHiddenScope": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "HEAD", - ResourceIDName: pointer.To("ScopeId"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseResourceIdContainingAHiddenScopeNestedWithSuffix(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "resource_ids_containing_hidden_scope_nested_with_suffix.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Example": { - ResourceIDs: map[string]sdkModels.ResourceID{ - "ScopeId": { - CommonIDAlias: pointer.To("Scope"), - Segments: []sdkModels.ResourceIDSegment{ - sdkModels.NewScopeResourceIDSegment("scope"), - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "OperationContainingAHiddenScope": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "HEAD", - ResourceIDName: pointer.To("ScopeId"), - URISuffix: pointer.To("/someEndpoint"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseResourceIdWithJustUriSuffix(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "resource_ids_with_just_suffix.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Example": { - Operations: map[string]sdkModels.SDKOperation{ - "JustSuffix": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "HEAD", - URISuffix: pointer.To("/restart"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseResourceIdWithResourceIdAndUriSuffix(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "resource_ids_with_suffix.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Example": { - ResourceIDs: map[string]sdkModels.ResourceID{ - "ServerId": { - Segments: []sdkModels.ResourceIDSegment{ - sdkModels.NewStaticValueResourceIDSegment("staticSubscriptions", "subscriptions"), - sdkModels.NewSubscriptionIDResourceIDSegment("subscriptionId"), - sdkModels.NewStaticValueResourceIDSegment("staticResourceGroups", "resourceGroups"), - sdkModels.NewResourceGroupNameResourceIDSegment("resourceGroupName"), - sdkModels.NewStaticValueResourceIDSegment("staticProviders", "providers"), - sdkModels.NewResourceProviderResourceIDSegment("staticMicrosoftSomeResourceProvider", "Microsoft.SomeResourceProvider"), - sdkModels.NewStaticValueResourceIDSegment("staticServers", "servers"), - sdkModels.NewUserSpecifiedResourceIDSegment("serverName", "serverName"), - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "HEAD", - ResourceIDName: pointer.To("ServerId"), - URISuffix: pointer.To("/someOperation"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseResourceIdWithResourceIdAndUriSuffixForMultipleUris(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "resource_ids_with_suffix_multiple_uris.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Example": { - ResourceIDs: map[string]sdkModels.ResourceID{ - "ServerId": { - Segments: []sdkModels.ResourceIDSegment{ - sdkModels.NewStaticValueResourceIDSegment("staticSubscriptions", "subscriptions"), - sdkModels.NewSubscriptionIDResourceIDSegment("subscriptionId"), - sdkModels.NewStaticValueResourceIDSegment("staticResourceGroups", "resourceGroups"), - sdkModels.NewResourceGroupNameResourceIDSegment("resourceGroupName"), - sdkModels.NewStaticValueResourceIDSegment("staticProviders", "providers"), - sdkModels.NewResourceProviderResourceIDSegment("staticMicrosoftSomeResourceProvider", "Microsoft.SomeResourceProvider"), - sdkModels.NewStaticValueResourceIDSegment("staticServers", "servers"), - sdkModels.NewUserSpecifiedResourceIDSegment("serverName", "serverName"), - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Restart": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "HEAD", - ResourceIDName: pointer.To("ServerId"), - URISuffix: pointer.To("/restart"), - }, - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "HEAD", - ResourceIDName: pointer.To("ServerId"), - URISuffix: pointer.To("/someOperation"), - }, - "TopLevel": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "HEAD", - ResourceIDName: pointer.To("ServerId"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseResourceIdContainingResourceProviderShouldGetTitleCased(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "resource_ids_lowercased_resource_provider.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Example": { - ResourceIDs: map[string]sdkModels.ResourceID{ - "ServerId": { - Segments: []sdkModels.ResourceIDSegment{ - sdkModels.NewStaticValueResourceIDSegment("staticSubscriptions", "subscriptions"), - sdkModels.NewSubscriptionIDResourceIDSegment("subscriptionId"), - sdkModels.NewStaticValueResourceIDSegment("staticResourceGroups", "resourceGroups"), - sdkModels.NewResourceGroupNameResourceIDSegment("resourceGroupName"), - sdkModels.NewStaticValueResourceIDSegment("staticProviders", "providers"), - sdkModels.NewResourceProviderResourceIDSegment("staticMicrosoftSomeResourceProvider", "Microsoft.SomeResourceProvider"), - sdkModels.NewStaticValueResourceIDSegment("staticServers", "servers"), - sdkModels.NewUserSpecifiedResourceIDSegment("serverName", "serverName"), - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "HEAD", - ResourceIDName: pointer.To("ServerId"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseResourceIdContainingTheSameResourceIdWithDifferentSegments(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "resource_ids_same_id_different_segment_casing.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Example": { - ResourceIDs: map[string]sdkModels.ResourceID{ - "VirtualMachineId": { - Segments: []sdkModels.ResourceIDSegment{ - sdkModels.NewStaticValueResourceIDSegment("staticSubscriptions", "subscriptions"), - sdkModels.NewSubscriptionIDResourceIDSegment("subscriptionId"), - sdkModels.NewStaticValueResourceIDSegment("staticResourceGroups", "resourceGroups"), - sdkModels.NewResourceGroupNameResourceIDSegment("resourceGroupName"), - sdkModels.NewStaticValueResourceIDSegment("staticProviders", "providers"), - sdkModels.NewResourceProviderResourceIDSegment("staticMicrosoftSomeResourceProvider", "Microsoft.SomeResourceProvider"), - sdkModels.NewStaticValueResourceIDSegment("staticVirtualMachines", "virtualMachines"), - sdkModels.NewUserSpecifiedResourceIDSegment("machineName", "machineName"), - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Restart": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "HEAD", - ResourceIDName: pointer.To("VirtualMachineId"), - URISuffix: pointer.To("/restart"), - }, - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "HEAD", - ResourceIDName: pointer.To("VirtualMachineId"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseResourceIdContainingTheSegmentsNamedTheSame(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "resource_ids_multiple_segments_same_name.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Example": { - ResourceIDs: map[string]sdkModels.ResourceID{ - "BillingPeriodId": { - Segments: []sdkModels.ResourceIDSegment{ - sdkModels.NewStaticValueResourceIDSegment("staticProviders", "providers"), - sdkModels.NewResourceProviderResourceIDSegment("staticMicrosoftManagement", "Microsoft.Management"), - sdkModels.NewStaticValueResourceIDSegment("staticManagementGroups", "managementGroups"), - sdkModels.NewUserSpecifiedResourceIDSegment("managementGroupId", "managementGroupId"), - sdkModels.NewStaticValueResourceIDSegment("staticProviders2", "providers"), - sdkModels.NewResourceProviderResourceIDSegment("staticMicrosoftBilling", "Microsoft.Billing"), - sdkModels.NewStaticValueResourceIDSegment("staticBillingPeriods", "billingPeriods"), - sdkModels.NewUserSpecifiedResourceIDSegment("billingPeriodName", "billingPeriodName"), - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "HEAD", - ResourceIDName: pointer.To("BillingPeriodId"), - URISuffix: pointer.To("/Microsoft.Consumption/aggregatedCost"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParseResourceIdsWhereTheSameUriContainsDifferentConstantValuesPerOperation(t *testing.T) { - // Whilst a URI may contain Constants, the values for those constants can differ per HTTP Operation - // as such we need to ensure that these are output as different Resource ID types - // - // In this case there are 2 constants defined, `PlanetNames` and `PlanetEarth` - however `PlanetEarth` is a constant - // with a single value - therefore `PlanetEarth` will be removed. - // The Operation `HEAD /galaxies/{galaxyName}/hello/{planetName}` should remain as-is - // The Operation `DELETE /galaxies/{galaxyName}/hello/{planetName}` should be transformed to `/galaxies/{galaxyName}/hello/Earth` - // This means we should end up with 2 IDs, GalaxyId and PlanetId (with Earth and Mars the Constant PlanetNames in PlanetId) - // - // Experience has shown this is eventually consistent, maybe 100x is overkill, but it'll do for now. - 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) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Hello": { - Constants: map[string]sdkModels.SDKConstant{ - "PlanetNames": { - Type: sdkModels.StringSDKConstantType, - Values: map[string]string{ - "Earth": "Earth", - "Mars": "Mars", - }, - }, - }, - ResourceIDs: map[string]sdkModels.ResourceID{ - "GalaxyId": { - Segments: []sdkModels.ResourceIDSegment{ - sdkModels.NewStaticValueResourceIDSegment("staticGalaxies", "galaxies"), - sdkModels.NewUserSpecifiedResourceIDSegment("galaxyName", "galaxyName"), - }, - }, - "PlanetId": { - ConstantNames: []string{ - "PlanetNames", - }, - Segments: []sdkModels.ResourceIDSegment{ - sdkModels.NewStaticValueResourceIDSegment("staticGalaxies", "galaxies"), - sdkModels.NewUserSpecifiedResourceIDSegment("galaxyName", "galaxyName"), - sdkModels.NewStaticValueResourceIDSegment("staticHello", "hello"), - sdkModels.NewConstantResourceIDSegment("planetName", "PlanetNames", "Earth"), - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "Delete": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "DELETE", - ResourceIDName: pointer.To("GalaxyId"), - URISuffix: pointer.To("/hello/Earth"), - }, - "Head": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "HEAD", - ResourceIDName: pointer.To("PlanetId"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) - } -} - -func TestParseResourceIdsCommon(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "resource_ids_common.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Example": { - ResourceIDs: map[string]sdkModels.ResourceID{ - "ManagementGroupId": { - CommonIDAlias: pointer.To("ManagementGroup"), - Segments: []sdkModels.ResourceIDSegment{ - sdkModels.NewStaticValueResourceIDSegment("providers", "providers"), - sdkModels.NewResourceProviderResourceIDSegment("resourceProvider", "Microsoft.Management"), - sdkModels.NewStaticValueResourceIDSegment("managementGroups", "managementGroups"), - sdkModels.NewUserSpecifiedResourceIDSegment("groupId", "groupId"), - }, - }, - "ResourceGroupId": { - CommonIDAlias: pointer.To("ResourceGroup"), - Segments: []sdkModels.ResourceIDSegment{ - sdkModels.NewStaticValueResourceIDSegment("subscriptions", "subscriptions"), - sdkModels.NewSubscriptionIDResourceIDSegment("subscriptionId"), - sdkModels.NewStaticValueResourceIDSegment("resourceGroups", "resourceGroups"), - sdkModels.NewResourceGroupNameResourceIDSegment("resourceGroupName"), - }, - }, - "ScopeId": { - CommonIDAlias: pointer.To("Scope"), - Segments: []sdkModels.ResourceIDSegment{ - sdkModels.NewScopeResourceIDSegment("scope"), - }, - }, - "SubscriptionId": { - CommonIDAlias: pointer.To("Subscription"), - Segments: []sdkModels.ResourceIDSegment{ - sdkModels.NewStaticValueResourceIDSegment("subscriptions", "subscriptions"), - sdkModels.NewSubscriptionIDResourceIDSegment("subscriptionId"), - }, - }, - "UserAssignedIdentityId": { - CommonIDAlias: pointer.To("UserAssignedIdentity"), - 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"), - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "GetManagementGroup": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "HEAD", - ResourceIDName: pointer.To("ManagementGroupId"), - }, - "GetResourceGroup": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "HEAD", - ResourceIDName: pointer.To("ResourceGroupId"), - }, - "GetScope": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "HEAD", - ResourceIDName: pointer.To("ScopeId"), - }, - "GetSubscription": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "HEAD", - ResourceIDName: pointer.To("SubscriptionId"), - }, - "GetUserAssignedIdentity": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "HEAD", - ResourceIDName: pointer.To("UserAssignedIdentityId"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} diff --git a/tools/importer-rest-api-specs/components/parser/ported_swagger_resources.go b/tools/importer-rest-api-specs/components/parser/ported_swagger_resources.go deleted file mode 100644 index 74457a6ed24..00000000000 --- a/tools/importer-rest-api-specs/components/parser/ported_swagger_resources.go +++ /dev/null @@ -1,324 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package parser - -import ( - "fmt" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/operation" - "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/commonschema" - "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/components/apidefinitions/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 := parserModels.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 each 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 = 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) - - // first Normalize the names, meaning `foo` -> `Foo` for consistency - resource = cleanup.NormalizeAPIResource(resource) - - return &resource, nil -} - -func (d *SwaggerDefinition) 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 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.Parse(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 (d *SwaggerDefinition) determineObjectsRequiredButNotParsed(operations map[string]sdkModels.SDKOperation, known parserModels.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 := 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 := 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 := 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 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 := 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 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 := 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 := 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 -} diff --git a/tools/importer-rest-api-specs/components/parser/ported_swagger_tags.go b/tools/importer-rest-api-specs/components/parser/ported_swagger_tags.go deleted file mode 100644 index e572d414089..00000000000 --- a/tools/importer-rest-api-specs/components/parser/ported_swagger_tags.go +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package parser - -import ( - "sort" - "strings" -) - -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 -} diff --git a/tools/importer-rest-api-specs/components/parser/ported_swagger_tags_test.go b/tools/importer-rest-api-specs/components/parser/ported_swagger_tags_test.go deleted file mode 100644 index 972efb13d84..00000000000 --- a/tools/importer-rest-api-specs/components/parser/ported_swagger_tags_test.go +++ /dev/null @@ -1,175 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package parser - -import ( - "testing" - - "github.com/hashicorp/go-azure-helpers/lang/pointer" - sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" -) - -func TestParsingOperationsUsingTheSameSwaggerTagInDifferentCasings(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_tag_different_casing.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Hello": { - Models: map[string]sdkModels.SDKModel{ - "Example": { - Fields: map[string]sdkModels.SDKField{ - "Name": { - JsonName: "name", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - - "First": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "PUT", - RequestObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Example"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - // https://github.com/hashicorp/pandora/issues/3807 - URISuffix: pointer.To("/someotheruri"), - }, - "PutBar": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "PUT", - RequestObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Example"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/bar"), - }, - "PutFoo": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "PUT", - RequestObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Example"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/foo"), - }, - "Second": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "PATCH", - RequestObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Example"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - // https://github.com/hashicorp/pandora/issues/3807 - URISuffix: pointer.To("/someotheruri"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} - -func TestParsingOperationsOnResources(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_on_resources.json", nil) - if err != nil { - t.Fatalf("parsing: %+v", err) - } - - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Hello": { - Models: map[string]sdkModels.SDKModel{ - "Example": { - Fields: map[string]sdkModels.SDKField{ - "Name": { - JsonName: "name", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "First": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "PUT", - RequestObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Example"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - // https://github.com/hashicorp/pandora/issues/3807 - URISuffix: pointer.To("/someotheruri"), - }, - "PutBar": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "PUT", - RequestObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Example"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/bar"), - }, - "Second": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "PATCH", - RequestObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Example"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - // https://github.com/hashicorp/pandora/issues/3807 - URISuffix: pointer.To("/someotheruri"), - }, - }, - }, - "HelloOperations": { - Models: map[string]sdkModels.SDKModel{ - "Example": { - Fields: map[string]sdkModels.SDKField{ - "Name": { - JsonName: "name", - ObjectDefinition: sdkModels.SDKObjectDefinition{ - Type: sdkModels.StringSDKObjectDefinitionType, - }, - Required: false, - }, - }, - }, - }, - Operations: map[string]sdkModels.SDKOperation{ - "HelloRestart": { - ContentType: "application/json", - ExpectedStatusCodes: []int{200}, - Method: "POST", - RequestObject: &sdkModels.SDKObjectDefinition{ - ReferenceName: pointer.To("Example"), - Type: sdkModels.ReferenceSDKObjectDefinitionType, - }, - URISuffix: pointer.To("/foo"), - }, - }, - }, - }, - } - validateParsedSwaggerResultMatches(t, expected, actual) -} diff --git a/tools/importer-rest-api-specs/components/parser/rewritten_load_and_parse.go b/tools/importer-rest-api-specs/components/parser/rewritten_load_and_parse.go deleted file mode 100644 index d8780f06cd5..00000000000 --- a/tools/importer-rest-api-specs/components/parser/rewritten_load_and_parse.go +++ /dev/null @@ -1,145 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package parser - -import ( - "fmt" - "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/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/dataworkarounds" - "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" - importerModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/models" -) - -func LoadAndParseFiles(directory string, fileNames []string, serviceName, apiVersion string, resourceProvider *string) (*sdkModels.APIVersion, error) { - // Some Services have been deprecated or should otherwise be ignored - check before proceeding - if ignore.Services(serviceName) { - logging.Debugf("Service %q should be ignored - skipping", serviceName) - return nil, 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() - 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) - } - } - - // TODO: Update this to be `parsedAPIVersion` - parsed := make(map[string]importerModels.AzureApiDefinition) - 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 := combine.ResourcesWith(data.Resources, 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..") - - results := make(map[string]sdkModels.APIVersion) - for _, service := range out { - tempAPIVersion := &sdkModels.APIVersion{ - APIVersion: service.ApiVersion, - Generate: true, - Preview: service.IsPreviewVersion(), - Resources: service.Resources, - Source: sdkModels.AzureRestAPISpecsSourceDataOrigin, - } - if existing, hasExisting := results[service.ApiVersion]; hasExisting { - tempAPIVersion = &existing - } - tempAPIVersion, err := dataworkarounds.Apply(service.ServiceName, *tempAPIVersion) - if err != nil { - return nil, fmt.Errorf("applying Swagger overrides: %+v", err) - } - - results[service.ApiVersion] = *tempAPIVersion - } - - keys := keysForMap(results) - if len(keys) == 0 { - // possible there's no data for this API Version - return nil, nil - } - if len(results) > 1 { - // we're parsing a single API Version out, so if there's multiple that'd be unexpected/a bug - return nil, fmt.Errorf("expected a single API Version but got: %+v", keys) - } - - result := results[keys[0]] - // finally, now that we've parsed everything out, iterate over to cleanup any unused items - result = cleanup.RemoveUnusedItems(result) - return &result, nil -} - -func keysForMap(input map[string]sdkModels.APIVersion) []string { - keys := make(map[string]struct{}) - for k := range input { - keys[k] = struct{}{} - } - // sort for consistency - output := make([]string, 0) - for k := range keys { - output = append(output, k) - } - sort.Strings(output) - return output -} - -func keyForAzureApiDefinition(input importerModels.AzureApiDefinition) string { - return fmt.Sprintf("%s-%s", input.ServiceName, input.ApiVersion) -} 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..0455868de4a 100644 --- a/tools/importer-rest-api-specs/internal/cmd/validate.go +++ b/tools/importer-rest-api-specs/internal/cmd/validate.go @@ -4,11 +4,10 @@ package cmd import ( - legacyPipeline "github.com/hashicorp/pandora/tools/importer-rest-api-specs/pipeline" "log" - "os" - "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 +34,17 @@ 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 - - input := legacyPipeline.RunInput{ - ConfigFilePath: c.resourceManagerConfigPath, - JustParseData: true, - Logger: logging.Log, - OutputDirectory: os.DevNull, - ProviderPrefix: "azurerm", - SwaggerDirectory: c.restAPISpecsRepositoryDirectoryPath, - TerraformDefinitionsPath: c.terraformDefinitionsPath, + 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 := 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/parser/dataworkarounds/workaround_alertsmanagement.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_alertsmanagement.go index d7e67374186..19d5533866b 100644 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_alertsmanagement.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_alertsmanagement.go @@ -5,6 +5,7 @@ package dataworkarounds import ( "fmt" + "sort" "github.com/hashicorp/go-azure-helpers/lang/pointer" sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" @@ -31,7 +32,12 @@ func (workaroundAlertsManagement) Name() string { 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 { diff --git a/tools/importer-rest-api-specs/models/models.go b/tools/importer-rest-api-specs/models/models.go index a05797d7159..74281091708 100644 --- a/tools/importer-rest-api-specs/models/models.go +++ b/tools/importer-rest-api-specs/models/models.go @@ -4,33 +4,9 @@ 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 { 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 f1773b9d607..00000000000 --- a/tools/importer-rest-api-specs/pipeline/run_importer.go +++ /dev/null @@ -1,206 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package pipeline - -import ( - "fmt" - "sort" - "strings" - - "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/internal/components/terraform" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/logging" - "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 the data for this API Version.. - apiVersions := make(map[string]sdkModels.APIVersion, 0) - for apiVersion, api := range consolidatedApiVersions { - logging.Tracef("Task: Parsing Data for API Version %q..", apiVersion) - isPreview := isPreviewVersion(apiVersion) - dataForApiVersion := &sdkModels.APIVersion{ - APIVersion: apiVersion, - Generate: true, - Preview: isPreview, - Resources: make(map[string]sdkModels.APIResource), - Source: sdkModels.AzureRestAPISpecsSourceDataOrigin, - } - 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 { - dataForApiVersion.Resources[name] = resource - } - } - - apiVersions[apiVersion] = *dataForApiVersion - } - - service := &sdkModels.Service{ - APIVersions: apiVersions, - Generate: true, - Name: serviceName, - ResourceProvider: resourceProvider, - } - - // 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 -} - -func isPreviewVersion(input string) bool { - lower := strings.ToLower(input) - // 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 -} 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 index 7b01035180d..ca2f4c4904b 100644 --- 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 @@ -4,14 +4,7 @@ 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 ( @@ -21,62 +14,65 @@ const ( ) 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) - } - }) - } + t.Fatalf("TODO") + //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 + t.Fatalf("TODO") - task := pipelineTask{} - if _, err := task.parseDataForApiVersion(generationData); err != nil { - t.Fatalf("error: %+v", err) - } - }) - } + //// 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 0aab144fc27..00000000000 --- a/tools/importer-rest-api-specs/pipeline/task_parse_data.go +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package pipeline - -import ( - "fmt" - - 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/parser" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/logging" -) - -func (pipelineTask) parseDataForApiVersion(input discovery.ServiceInput) (*map[string]sdkModels.APIResource, 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) (*map[string]sdkModels.APIResource, 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) - } - - if parseResult == nil { - return nil, nil - } - - return &parseResult.Resources, nil -} From 41b548aed642c0da559bfe9fe672fe289629cb02 Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Fri, 12 Jul 2024 19:22:15 +0200 Subject: [PATCH 25/58] `tools/data-api-sdk`: making IsCommonType required so this is more explicit --- tools/data-api-sdk/v1/models/sdk_object_definition.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/data-api-sdk/v1/models/sdk_object_definition.go b/tools/data-api-sdk/v1/models/sdk_object_definition.go index bafe6af10d1..402a9095935 100644 --- a/tools/data-api-sdk/v1/models/sdk_object_definition.go +++ b/tools/data-api-sdk/v1/models/sdk_object_definition.go @@ -22,7 +22,7 @@ type SDKObjectDefinition struct { ReferenceName *string `json:"referenceName,omitempty"` // ReferenceNameIsCommonType specifies whether the referenced Constant or Model is a common type - ReferenceNameIsCommonType *bool `json:"referenceNameIsCommonType,omitempty"` + ReferenceNameIsCommonType bool `json:"referenceNameIsCommonType"` // Type specifies the Type that represents this SDK Object Definition. This can be either a // Simple type (e.g. a String/Integer), a Reference to a Constant/Model or a more complex object From 61de9481a424459fd78a452c1f313bd7ddc85125 Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Fri, 12 Jul 2024 19:23:29 +0200 Subject: [PATCH 26/58] `tools/importer-rest-api-specs`: fixing the validation --- tools/importer-rest-api-specs/GNUmakefile | 3 + .../components/parser/parser_test.go | 359 +++++++++--------- tools/importer-rest-api-specs/go.mod | 1 + .../internal/cmd/validate.go | 15 +- .../apidefinitions/models_debugging_test.go | 24 ++ .../apidefinitions/parse_api_resource.go | 46 ++- .../apidefinitions/parse_api_version.go | 22 +- .../parser/combine/operations.go | 13 +- .../parser/comparison/object_definition.go | 54 +++ .../parser/comparison/operation.go | 57 +++ .../parser/comparison/operation_option.go | 28 ++ .../operation_option_object_definition.go | 36 ++ .../parser/comparison/operation_options.go | 28 ++ .../apidefinitions/parser/constants/parse.go | 2 +- .../parser/models/parse_result.go | 24 +- .../parser/parse_swagger_tag.go | 6 +- .../parser/parsingcontext/helpers.go | 60 +-- .../parser/parsingcontext/parse_model.go | 15 +- .../parsingcontext/parse_object_definition.go | 4 +- .../components/discovery/for_api_version.go | 5 + 20 files changed, 547 insertions(+), 255 deletions(-) create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/models_debugging_test.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/comparison/object_definition.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/comparison/operation.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/comparison/operation_option.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/comparison/operation_option_object_definition.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/comparison/operation_options.go 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/parser/parser_test.go b/tools/importer-rest-api-specs/components/parser/parser_test.go index 9c21c4c2a26..35fdd5ad821 100644 --- a/tools/importer-rest-api-specs/components/parser/parser_test.go +++ b/tools/importer-rest-api-specs/components/parser/parser_test.go @@ -3,189 +3,182 @@ package parser -import ( - "context" - "fmt" - "log" - "os" - "strings" - "testing" - "time" +import "testing" - "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 TestFoo(t *testing.T) { + t.Fatal("TODO") } -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 -} +//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/go.mod b/tools/importer-rest-api-specs/go.mod index 6174be629a6..7a27d0f83a6 100644 --- a/tools/importer-rest-api-specs/go.mod +++ b/tools/importer-rest-api-specs/go.mod @@ -3,6 +3,7 @@ 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 diff --git a/tools/importer-rest-api-specs/internal/cmd/validate.go b/tools/importer-rest-api-specs/internal/cmd/validate.go index 0455868de4a..ef3f8092fbc 100644 --- a/tools/importer-rest-api-specs/internal/cmd/validate.go +++ b/tools/importer-rest-api-specs/internal/cmd/validate.go @@ -4,7 +4,9 @@ package cmd import ( + "flag" "log" + "strings" sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/pipeline" @@ -34,12 +36,23 @@ func (ValidateCommand) Help() string { } func (c ValidateCommand) Run(args []string) int { + var serviceNamesRaw string + + 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: nil, // not used for this + ServiceNamesToLimitTo: serviceNames, SourceDataOrigin: sdkModels.AzureRestAPISpecsSourceDataOrigin, SourceDataType: sdkModels.ResourceManagerSourceDataType, TerraformDefinitionsDirectory: c.terraformDefinitionsPath, 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..447359d0c23 --- /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) { + input := discoveryModels.AvailableDataSetForAPIVersion{ + APIVersion: "2023-05-01", + ContainsStableAPIVersion: false, + FilePathsContainingAPIDefinitions: []string{ + "/Users/tharvey/code/src/github.com/hashicorp/pandora/submodules/rest-api-specs/specification/storagecache/resource-manager/Microsoft.StorageCache/stable/2023-05-01/amlfilesystem.json", + }, + FilePathsContainingSupplementaryData: []string{}, + } + 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 index c6bba0eed31..4e0e23a97c6 100644 --- 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 @@ -31,15 +31,28 @@ func parseAPIResourcesFromFile(filePath, serviceName string, resourceProvider *s continue } - resource, err := parser.ParseAPIResourceWithinSwaggerTag(&tag, resourceProvider, resourceIds) + normalizedTag := cleanup.NormalizeTag(tag) + normalizedTag = cleanup.NormalizeResourceName(normalizedTag) + + // pass in any existing/known data so that we can reuse the models/references + existing := sdkModels.APIResource{ + Constants: make(map[string]sdkModels.SDKConstant), + Models: make(map[string]sdkModels.SDKModel), + Name: normalizedTag, + Operations: make(map[string]sdkModels.SDKOperation), + ResourceIDs: make(map[string]sdkModels.ResourceID), + } + 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)) - normalizedTag := cleanup.NormalizeTag(tag) - normalizedTag = cleanup.NormalizeResourceName(normalizedTag) discoveredResources := map[string]sdkModels.APIResource{ normalizedTag: *resource, } @@ -53,11 +66,6 @@ func parseAPIResourcesFromFile(filePath, serviceName string, resourceProvider *s // 3. Then parse over any Swagger Operations which DON'T have a Tag if !ignore.SwaggerTag(serviceName) { - resource, err := parser.ParseAPIResourceWithinSwaggerTag(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. directory := filepath.Dir(filePath) @@ -65,11 +73,27 @@ func parseAPIResourcesFromFile(filePath, serviceName string, resourceProvider *s fileName = strings.TrimPrefix(fileName, fmt.Sprintf("%c", filepath.Separator)) fileName = strings.TrimSuffix(fileName, ".json") inferredTag := cleanup.PluraliseName(fileName) + normalizedTag := cleanup.NormalizeTag(inferredTag) + normalizedTag = cleanup.NormalizeResourceName(normalizedTag) - if resource != nil { - normalizedTag := cleanup.NormalizeTag(inferredTag) - normalizedTag = cleanup.NormalizeResourceName(normalizedTag) + // pass in any existing/known data so that we can reuse the models/references + existing := sdkModels.APIResource{ + Constants: make(map[string]sdkModels.SDKConstant), + Models: make(map[string]sdkModels.SDKModel), + Name: normalizedTag, + Operations: make(map[string]sdkModels.SDKOperation), + ResourceIDs: make(map[string]sdkModels.ResourceID), + } + if v, ok := parsedAPIResources[normalizedTag]; 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{ normalizedTag: *resource, } 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 index 8cefde401c6..81168591335 100644 --- 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 @@ -20,17 +20,17 @@ func parseAPIVersion(serviceName string, input discoveryModels.AvailableDataSetF Constants: map[string]sdkModels.SDKConstant{}, Models: map[string]sdkModels.SDKModel{}, } - for _, filePath := range input.FilePathsContainingSupplementaryData { - logging.Tracef("Processing Supplementary Data from file %q..", filePath) - updatedSupplementaryData, err := parseSupplementaryDataFromFile(filePath, supplementaryData) - if err != nil { - return nil, fmt.Errorf("parsing the Supplementary Data within %q: %+v", filePath, err) - } - logging.Tracef("Processing Supplementary Data from file %q - Completed.", filePath) - - supplementaryData = *updatedSupplementaryData - logging.Tracef("The Supplementary Data now contains %d Constants and %d Models", len(supplementaryData.Constants), len(supplementaryData.Models)) - } + //for _, filePath := range input.FilePathsContainingSupplementaryData { + // logging.Tracef("Processing Supplementary Data from file %q..", filePath) + // updatedSupplementaryData, err := parseSupplementaryDataFromFile(filePath, supplementaryData) + // if err != nil { + // return nil, fmt.Errorf("parsing the Supplementary Data within %q: %+v", filePath, err) + // } + // logging.Tracef("Processing Supplementary Data from file %q - Completed.", filePath) + // + // supplementaryData = *updatedSupplementaryData + // logging.Tracef("The Supplementary Data now contains %d Constants and %d Models", len(supplementaryData.Constants), len(supplementaryData.Models)) + //} // Next 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 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 index 6ec92390cd3..5e570574601 100644 --- 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 @@ -5,8 +5,8 @@ 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 operations(first map[string]sdkModels.SDKOperation, second map[string]sdkModels.SDKOperation) (*map[string]sdkModels.SDKOperation, error) { @@ -18,12 +18,15 @@ func operations(first map[string]sdkModels.SDKOperation, second map[string]sdkMo 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) + other, ok := output[k] + if !ok { + output[k] = v + continue } - output[k] = v + if ok, err := comparison.OperationsMatch(v, other); !ok { + return nil, fmt.Errorf("differing Operations named %q. First: %+v / Second %+v / Error: %+v", k, v, other, err) + } } return &output, nil 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..65a64472589 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/comparison/object_definition.go @@ -0,0 +1,54 @@ +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 { + logging.Tracef("ReferenceNameIsCommonType differed - %t vs %t", first.ReferenceNameIsCommonType, second.ReferenceNameIsCommonType) + return false, fmt.Errorf("ReferenceNameIsCommonType differed - %t vs %t", first.ReferenceNameIsCommonType, second.ReferenceNameIsCommonType) + } + 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..107b473c1b9 --- /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", first.HeaderName, second.HeaderName) + return false + } + if pointer.From(first.QueryStringName) != pointer.From(second.QueryStringName) { + logging.Tracef("QueryStringName differed - %q vs %q", first.QueryStringName, 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/constants/parse.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/constants/parse.go index 7161e654011..a2b9cef7c05 100644 --- 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 @@ -67,7 +67,7 @@ func Parse(typeVal spec.StringOrArray, fieldName string, modelName *string, valu } return &ParsedConstant{ - Name: constantName, + Name: strings.Title(constantName), Details: sdkModels.SDKConstant{ Values: *keysAndValues, Type: constantType, diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models/parse_result.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models/parse_result.go index ff7e678629c..5ecd3d4d884 100644 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models/parse_result.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models/parse_result.go @@ -3,6 +3,7 @@ package models import ( "fmt" "reflect" + "strings" "github.com/hashicorp/go-azure-helpers/lang/pointer" sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" @@ -90,7 +91,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) } @@ -116,6 +118,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) @@ -131,8 +149,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 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 index 6e933eb3a7d..f32b256b2e0 100644 --- 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 @@ -11,10 +11,10 @@ import ( "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids" ) -func (p *apiDefinitionsParser) ParseAPIResourceWithinSwaggerTag(tag, resourceProvider *string, resourceIds resourceids.ParseResult) (*sdkModels.APIResource, error) { +func (p *apiDefinitionsParser) ParseAPIResourceWithinSwaggerTag(tag, resourceProvider *string, resourceIds resourceids.ParseResult, existingAPIResource sdkModels.APIResource) (*sdkModels.APIResource, error) { result := parserModels.ParseResult{ - Constants: map[string]sdkModels.SDKConstant{}, - Models: map[string]sdkModels.SDKModel{}, + Constants: existingAPIResource.Constants, + Models: existingAPIResource.Models, } // note that Resource ID's can contain Constants (used as segments) 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 index 97542de0fa7..bdab3c8877b 100644 --- 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 @@ -2,6 +2,7 @@ package parsingcontext import ( "fmt" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/logging" "strings" "github.com/go-openapi/spec" @@ -133,29 +134,6 @@ func (c *Context) FindNestedItemsYetToBeParsed(operations map[string]sdkModels.S func (c *Context) determineObjectsRequiredButNotParsed(operations map[string]sdkModels.SDKOperation, known parserModels.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 := c.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 := sdkHelpers.InnerMostSDKObjectDefinition(*operation.RequestObject) @@ -168,7 +146,7 @@ func (c *Context) determineObjectsRequiredButNotParsed(operations map[string]sdk if isKnownModel { modelName := *topLevelRef.ReferenceName model := known.Models[modelName] - missingReferencesInModel, err := objectsRequiredByModel(modelName, model) + missingReferencesInModel, err := c.objectsRequiredByModel(modelName, model, known) if err != nil { return nil, fmt.Errorf("determining objects required by model %q: %+v", modelName, err) } @@ -192,7 +170,7 @@ func (c *Context) determineObjectsRequiredButNotParsed(operations map[string]sdk // that we don't know about modelName := *topLevelRef.ReferenceName model := known.Models[modelName] - missingReferencesInModel, err := objectsRequiredByModel(modelName, model) + missingReferencesInModel, err := c.objectsRequiredByModel(modelName, model, known) if err != nil { return nil, fmt.Errorf("determining objects required by model %q: %+v", modelName, err) } @@ -215,9 +193,9 @@ func (c *Context) determineObjectsRequiredButNotParsed(operations map[string]sdk } } - // then verify we have all of the models for the current models we know about + // then verify we have each of the models for the current models we know about for modelName, model := range known.Models { - missingReferencesInModel, err := objectsRequiredByModel(modelName, model) + missingReferencesInModel, err := c.objectsRequiredByModel(modelName, model, known) if err != nil { return nil, fmt.Errorf("determining objects required by model %q: %+v", modelName, err) } @@ -244,7 +222,8 @@ func (c *Context) determineObjectsRequiredButNotParsed(operations map[string]sdk func (c *Context) objectsUsedByModel(modelName string, model sdkModels.SDKModel) (*[]string, error) { typeNames := make(map[string]struct{}, 0) - for _, field := range model.Fields { + for fieldName, field := range model.Fields { + logging.Log.Trace("Determining objects used by field %q..", fieldName) definition := sdkHelpers.InnerMostSDKObjectDefinition(field.ObjectDefinition) if definition.ReferenceName != nil { typeNames[*definition.ReferenceName] = struct{}{} @@ -272,3 +251,28 @@ func (c *Context) objectsUsedByModel(modelName string, model sdkModels.SDKModel) } 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/parse_model.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parsingcontext/parse_model.go index 259a002d0fa..e64db3c6b73 100644 --- 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 @@ -13,6 +13,7 @@ import ( ) func (c *Context) ParseModel(name string, input spec.Schema, loadParentType bool) (*parserModels.ParseResult, error) { + logging.Tracef("Parsing details for Model %q (Load Parent Type %t)..", name, loadParentType) result := parserModels.ParseResult{ Constants: map[string]sdkModels.SDKConstant{}, Models: map[string]sdkModels.SDKModel{}, @@ -484,13 +485,13 @@ func (c *Context) findOrphanedDiscriminatedModels(serviceName string) (*parserMo // 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 := c.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) - } + //nestedResult, err := c.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/parsingcontext/parse_object_definition.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parsingcontext/parse_object_definition.go index 731a07b7085..f5f99a1e2a1 100644 --- 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 @@ -34,7 +34,7 @@ func (c *Context) ParseObjectDefinition(modelName, propertyName string, input *s definition := sdkModels.SDKObjectDefinition{ Type: sdkModels.ReferenceSDKObjectDefinitionType, - ReferenceName: pointer.To(strings.Title(constant.Name)), + ReferenceName: pointer.To(constant.Name), } //TODO: re-enable min/max/unique @@ -124,7 +124,7 @@ func (c *Context) ParseObjectDefinition(modelName, propertyName string, input *s definition := sdkModels.SDKObjectDefinition{ Type: sdkModels.ReferenceSDKObjectDefinitionType, - ReferenceName: pointer.To(strings.Title(modelName)), + ReferenceName: pointer.To(modelName), } // TODO: re-enable min/max/unique //if input.MaxItems != 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 b33d896f807..20cc806518f 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 @@ -30,6 +30,11 @@ func discoverDataSetForAPIVersion(apiVersion string, filePaths []string) (*model // 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 From b05c0cf64ad882b6b015999b4bac2e6726d90c23 Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Fri, 12 Jul 2024 20:23:14 +0200 Subject: [PATCH 27/58] `tools/importer-rest-api-specs`: re-enabling most of the tests --- .../apidefinitions/parse_api_version.go | 1 + .../parser/commonschema_test.go | 2 +- .../apidefinitions/parser/constants_test.go | 2 +- .../parser/models_datafactorycustom_test.go | 2 +- .../parser/models_dictionaries_test.go | 2 +- .../parser/models_discriminators_test.go | 2 +- .../apidefinitions/parser/models_test.go | 99 ++++++++++--------- .../apidefinitions/parser/operations_test.go | 87 ++++++++-------- .../parser/parse_supplementary_data_test.go | 5 +- .../parser/parsingcontext/helpers.go | 2 +- .../parser/parsingcontext/parse_model.go | 14 +-- .../parser/swagger_tags_test.go | 8 +- .../testhelpers/todo_parse_swagger_file.go | 64 ++++++------ 13 files changed, 148 insertions(+), 142 deletions(-) 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 index 81168591335..c0ea7cc09c6 100644 --- 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 @@ -20,6 +20,7 @@ func parseAPIVersion(serviceName string, input discoveryModels.AvailableDataSetF Constants: map[string]sdkModels.SDKConstant{}, Models: map[string]sdkModels.SDKModel{}, } + // TODO: re-enable this //for _, filePath := range input.FilePathsContainingSupplementaryData { // logging.Tracef("Processing Supplementary Data from file %q..", filePath) // updatedSupplementaryData, err := parseSupplementaryDataFromFile(filePath, supplementaryData) diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema_test.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema_test.go index 1223f809d90..79cca22b502 100644 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema_test.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema_test.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -package parser +package parser_test import ( "testing" diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/constants_test.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/constants_test.go index 30012ad2030..2e980713dff 100644 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/constants_test.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/constants_test.go @@ -1,7 +1,7 @@ // 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" diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models_datafactorycustom_test.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models_datafactorycustom_test.go index 3380628f3cf..89040042329 100644 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models_datafactorycustom_test.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models_datafactorycustom_test.go @@ -1,7 +1,7 @@ // 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" diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models_dictionaries_test.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models_dictionaries_test.go index 668dacecb67..cf4b4b2a734 100644 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models_dictionaries_test.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models_dictionaries_test.go @@ -1,7 +1,7 @@ // 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" diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models_discriminators_test.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models_discriminators_test.go index d3ec6a5a5fe..42f32d7463b 100644 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models_discriminators_test.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models_discriminators_test.go @@ -1,7 +1,7 @@ // 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" diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models_test.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models_test.go index 22bf5c9f75b..8dc2492a799 100644 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models_test.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models_test.go @@ -1,19 +1,20 @@ // 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" + "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") if err != nil { t.Fatalf("parsing: %+v", err) } @@ -89,11 +90,11 @@ 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") if err != nil { t.Fatalf("parsing: %+v", err) } @@ -119,11 +120,11 @@ 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") if err != nil { t.Fatalf("parsing: %+v", err) } @@ -218,11 +219,11 @@ 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") if err != nil { t.Fatalf("parsing: %+v", err) } @@ -263,11 +264,11 @@ 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") if err != nil { t.Fatalf("parsing: %+v", err) } @@ -357,11 +358,11 @@ 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") if err != nil { t.Fatalf("parsing: %+v", err) } @@ -405,11 +406,11 @@ 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") if err != nil { t.Fatalf("parsing: %+v", err) } @@ -487,11 +488,11 @@ 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") if err != nil { t.Fatalf("parsing: %+v", err) } @@ -555,11 +556,11 @@ 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") if err != nil { t.Fatalf("parsing: %+v", err) } @@ -635,11 +636,11 @@ 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") if err != nil { t.Fatalf("parsing: %+v", err) } @@ -705,11 +706,11 @@ 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") if err != nil { t.Fatalf("parsing: %+v", err) } @@ -806,11 +807,11 @@ 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") if err != nil { t.Fatalf("parsing: %+v", err) } @@ -849,11 +850,11 @@ 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") if err != nil { t.Fatalf("parsing: %+v", err) } @@ -909,11 +910,11 @@ 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") if err != nil { t.Fatalf("parsing: %+v", err) } @@ -952,7 +953,7 @@ func TestParseModelInheritingFromObjectWithOnlyDescription(t *testing.T) { }, }, } - validateParsedSwaggerResultMatches(t, expected, actual) + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } func TestParseModelInheritingFromObjectWithPropertiesWithinAllOf(t *testing.T) { @@ -961,7 +962,7 @@ 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") if err != nil { t.Fatalf("parsing: %+v", err) } @@ -1001,11 +1002,11 @@ 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") if err != nil { t.Fatalf("parsing: %+v", err) } @@ -1053,11 +1054,11 @@ 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") if err != nil { t.Fatalf("parsing: %+v", err) } @@ -1105,11 +1106,11 @@ 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") if err != nil { t.Fatalf("parsing: %+v", err) } @@ -1183,11 +1184,11 @@ 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") if err != nil { t.Fatalf("parsing: %+v", err) } @@ -1273,11 +1274,11 @@ 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") if err != nil { t.Fatalf("parsing: %+v", err) } @@ -1358,11 +1359,11 @@ 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") if err != nil { t.Fatalf("parsing: %+v", err) } @@ -1414,11 +1415,11 @@ 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") if err != nil { t.Fatalf("parsing: %+v", err) } @@ -1487,11 +1488,11 @@ 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") if err != nil { t.Fatalf("parsing: %+v", err) } @@ -1595,11 +1596,11 @@ 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") if err != nil { t.Fatalf("parsing: %+v", err) } @@ -1817,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/operations_test.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/operations_test.go index ada2b42b50d..7ca1c73c0bc 100644 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/operations_test.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/operations_test.go @@ -1,9 +1,10 @@ // 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" @@ -14,7 +15,7 @@ import ( // 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") if err != nil { t.Fatalf("parsing: %+v", err) } @@ -26,7 +27,7 @@ func TestParseOperationsEmpty(t *testing.T) { } 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") if err != nil { t.Fatalf("parsing: %+v", err) } @@ -50,7 +51,7 @@ func TestParseOperationSingleWithTag(t *testing.T) { } 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") if err != nil { t.Fatalf("parsing: %+v", err) } @@ -88,7 +89,7 @@ func TestParseOperationSingleWithTagAndResourceId(t *testing.T) { } 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") if err != nil { t.Fatalf("parsing: %+v", err) } @@ -127,7 +128,7 @@ func TestParseOperationSingleWithTagAndResourceIdSuffix(t *testing.T) { } 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") if err != nil { t.Fatalf("parsing: %+v", err) } @@ -168,7 +169,7 @@ func TestParseOperationSingleWithRequestObject(t *testing.T) { } 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") if err != nil { t.Fatalf("parsing: %+v", err) } @@ -209,7 +210,7 @@ func TestParseOperationSingleWithRequestObjectInlined(t *testing.T) { } 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") if err != nil { t.Fatalf("parsing: %+v", err) } @@ -250,7 +251,7 @@ func TestParseOperationSingleWithResponseObject(t *testing.T) { } 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") if err != nil { t.Fatalf("parsing: %+v", err) } @@ -291,7 +292,7 @@ func TestParseOperationSingleWithResponseObjectInlined(t *testing.T) { } 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") if err != nil { t.Fatalf("parsing: %+v", err) } @@ -335,7 +336,7 @@ func TestParseOperationSingleWithResponseObjectInlinedList(t *testing.T) { } 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") if err != nil { t.Fatalf("parsing: %+v", err) } @@ -362,7 +363,7 @@ func TestParseOperationSingleRequestingWithABool(t *testing.T) { } 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") if err != nil { t.Fatalf("parsing: %+v", err) } @@ -389,7 +390,7 @@ func TestParseOperationSingleRequestingWithAInteger(t *testing.T) { } 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") if err != nil { t.Fatalf("parsing: %+v", err) } @@ -419,7 +420,7 @@ func TestParseOperationSingleRequestingWithADictionaryOfStrings(t *testing.T) { } 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") if err != nil { t.Fatalf("parsing: %+v", err) } @@ -451,7 +452,7 @@ func TestParseOperationSingleRequestingWithAListOfStrings(t *testing.T) { // 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") if err != nil { t.Fatalf("parsing: %+v", err) } @@ -478,7 +479,7 @@ func TestParseOperationSingleRequestingWithAString(t *testing.T) { } 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") if err != nil { t.Fatalf("parsing: %+v", err) } @@ -505,7 +506,7 @@ func TestParseOperationSingleReturningABool(t *testing.T) { } 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") if err != nil { t.Fatalf("parsing: %+v", err) } @@ -532,7 +533,7 @@ func TestParseOperationSingleReturningAFloat(t *testing.T) { } 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") if err != nil { t.Fatalf("parsing: %+v", err) } @@ -559,7 +560,7 @@ func TestParseOperationSingleReturningAFile(t *testing.T) { } 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") if err != nil { t.Fatalf("parsing: %+v", err) } @@ -586,7 +587,7 @@ func TestParseOperationSingleReturningAnInteger(t *testing.T) { } 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") if err != nil { t.Fatalf("parsing: %+v", err) } @@ -614,7 +615,7 @@ func TestParseOperationSingleReturningAString(t *testing.T) { 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") if err != nil { t.Fatalf("parsing: %+v", err) } @@ -641,7 +642,7 @@ func TestParseOperationSingleReturningAnErrorStatusCode(t *testing.T) { } 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") if err != nil { t.Fatalf("parsing: %+v", err) } @@ -671,7 +672,7 @@ func TestParseOperationSingleReturningATopLevelRawObject(t *testing.T) { } 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") if err != nil { t.Fatalf("parsing: %+v", err) } @@ -715,7 +716,7 @@ func TestParseOperationSingleReturningADictionaryOfAModel(t *testing.T) { } 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") if err != nil { t.Fatalf("parsing: %+v", err) } @@ -745,7 +746,7 @@ func TestParseOperationSingleReturningADictionaryOfStrings(t *testing.T) { } 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") if err != nil { t.Fatalf("parsing: %+v", err) } @@ -775,7 +776,7 @@ func TestParseOperationSingleReturningAListOfIntegers(t *testing.T) { } 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") if err != nil { t.Fatalf("parsing: %+v", err) } @@ -819,7 +820,7 @@ func TestParseOperationSingleReturningAListOfAModel(t *testing.T) { } 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") if err != nil { t.Fatalf("parsing: %+v", err) } @@ -849,7 +850,7 @@ func TestParseOperationSingleReturningAListOfStrings(t *testing.T) { } 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") if err != nil { t.Fatalf("parsing: %+v", err) } @@ -896,7 +897,7 @@ func TestParseOperationSingleReturningAListOfListOfAModel(t *testing.T) { } 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") if err != nil { t.Fatalf("parsing: %+v", err) } @@ -929,7 +930,7 @@ func TestParseOperationSingleReturningAListOfListOfStrings(t *testing.T) { } func TestParseOperationSingleWithList(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_single_list.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_list.json") if err != nil { t.Fatalf("parsing: %+v", err) } @@ -973,7 +974,7 @@ func TestParseOperationSingleWithList(t *testing.T) { 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") if err != nil { t.Fatalf("parsing: %+v", err) } @@ -1019,7 +1020,7 @@ func TestParseOperationSingleWithListWhichIsNotAList(t *testing.T) { } 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") if err != nil { t.Fatalf("parsing: %+v", err) } @@ -1049,7 +1050,7 @@ func TestParseOperationSingleWithListReturningAListOfStrings(t *testing.T) { 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") if err != nil { t.Fatalf("parsing: %+v", err) } @@ -1091,7 +1092,7 @@ func TestParseOperationSingleWithListWithoutPageable(t *testing.T) { } 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") if err != nil { t.Fatalf("parsing: %+v", err) } @@ -1133,7 +1134,7 @@ func TestParseOperationSingleWithLongRunningOperation(t *testing.T) { } 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") if err != nil { t.Fatalf("parsing: %+v", err) } @@ -1178,7 +1179,7 @@ func TestParseOperationSingleWithRequestAndResponseObject(t *testing.T) { } 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") if err != nil { t.Fatalf("parsing: %+v", err) } @@ -1214,7 +1215,7 @@ func TestParseOperationSingleWithMultipleTags(t *testing.T) { } 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") if err != nil { t.Fatalf("parsing: %+v", err) } @@ -1240,7 +1241,7 @@ func TestParseOperationSingleWithInferredTag(t *testing.T) { } 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") if err != nil { t.Fatalf("parsing: %+v", err) } @@ -1321,7 +1322,7 @@ func TestParseOperationSingleWithHeaderOptions(t *testing.T) { } 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") if err != nil { t.Fatalf("parsing: %+v", err) } @@ -1402,7 +1403,7 @@ func TestParseOperationSingleWithQueryStringOptions(t *testing.T) { } 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") if err != nil { t.Fatalf("parsing: %+v", err) } @@ -1447,7 +1448,7 @@ func TestParseOperationMultipleBasedOnTheSameResourceId(t *testing.T) { } func TestParseOperationsContainingContentTypes(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operation_content_types.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operation_content_types.json") if err != nil { t.Fatalf("parsing: %+v", err) } @@ -1508,7 +1509,7 @@ func TestParseOperationsContainingContentTypes(t *testing.T) { } 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") if err != nil { t.Fatalf("parsing: %+v", err) } @@ -1549,7 +1550,7 @@ func TestParseOperationContainingMultipleReturnObjects(t *testing.T) { } 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") if err != nil { t.Fatalf("parsing: %+v", err) } diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_supplementary_data_test.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_supplementary_data_test.go index 7af46a9ae11..342661d5848 100644 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_supplementary_data_test.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_supplementary_data_test.go @@ -1,15 +1,16 @@ -package parser +package parser_test import ( "testing" sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser" parserModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models" ) func TestParseSupplementaryData(t *testing.T) { filePath := "/Users/tharvey/code/src/github.com/hashicorp/pandora/submodules/rest-api-specs/specification/datafactory/resource-manager/Microsoft.DataFactory/stable/2018-06-01/entityTypes/LinkedService.json" - parser, err := NewAPIDefinitionsParser(filePath, parserModels.SupplementaryData{ + parser, err := parser.NewAPIDefinitionsParser(filePath, parserModels.SupplementaryData{ Constants: make(map[string]sdkModels.SDKConstant), Models: make(map[string]sdkModels.SDKModel), }) 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 index bdab3c8877b..52fbe7d9c8f 100644 --- 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 @@ -223,7 +223,7 @@ func (c *Context) objectsUsedByModel(modelName string, model sdkModels.SDKModel) typeNames := make(map[string]struct{}, 0) for fieldName, field := range model.Fields { - logging.Log.Trace("Determining objects used by field %q..", fieldName) + logging.Tracef("Determining objects used by field %q..", fieldName) definition := sdkHelpers.InnerMostSDKObjectDefinition(field.ObjectDefinition) if definition.ReferenceName != nil { typeNames[*definition.ReferenceName] = struct{}{} 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 index e64db3c6b73..e0ec354ee71 100644 --- 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 @@ -485,13 +485,13 @@ func (c *Context) findOrphanedDiscriminatedModels(serviceName string) (*parserMo // 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 := c.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) - //} + nestedResult, err := c.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/swagger_tags_test.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/swagger_tags_test.go index 5af84a4254b..5d24a9cc3d4 100644 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/swagger_tags_test.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/swagger_tags_test.go @@ -1,18 +1,18 @@ // 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/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") if err != nil { t.Fatalf("parsing: %+v", err) } @@ -86,7 +86,7 @@ func TestParsingOperationsUsingTheSameSwaggerTagInDifferentCasings(t *testing.T) } func TestParsingOperationsOnResources(t *testing.T) { - actual, err := ParseSwaggerFileForTesting(t, "operations_on_resources.json", nil) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_on_resources.json") if err != nil { t.Fatalf("parsing: %+v", err) } diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testhelpers/todo_parse_swagger_file.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testhelpers/todo_parse_swagger_file.go index 25058194ea6..77e25e31785 100644 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testhelpers/todo_parse_swagger_file.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testhelpers/todo_parse_swagger_file.go @@ -1,41 +1,43 @@ package testhelpers import ( - "fmt" + "path/filepath" "testing" 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) (*sdkModels.APIVersion, error) { - /* - 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() - 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) - } +/* +1. Enable the existing tests +2. Re-enable the "validation" tests (these check we can import the RM services/api versions from the config) +3. Re-enabling supplementary types +4. Documentation/explaning what the packages are +*/ - // 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 - */ - - return nil, fmt.Errorf("TODO") +func ParseSwaggerFileForTesting(t *testing.T, filePath string) (*sdkModels.APIVersion, error) { + input := discoveryModels.AvailableDataSet{ + ServiceName: "Example", + DataSetsForAPIVersions: map[string]discoveryModels.AvailableDataSetForAPIVersion{ + "2020-01-01": { + APIVersion: "2020-01-01", + ContainsStableAPIVersion: false, + FilePathsContainingAPIDefinitions: []string{ + filepath.Join("testdata", filePath), + }, + FilePathsContainingSupplementaryData: []string{}, + }, + }, + ResourceProvider: nil, + } + result, err := apidefinitions.ParseService(input) + if err != nil { + t.Fatalf(err.Error()) + } + apiVersion, ok := result.APIVersions["2020-01-01"] + if !ok { + t.Fatalf("expected an API Version `2020-01-01` but didn't get one") + } + return &apiVersion, nil } From b77acefcf945ea103f08644a4e24eb63e86008ba Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Mon, 15 Jul 2024 13:34:54 +0200 Subject: [PATCH 28/58] `tools/importer-rest-api-specs`: consolidating/porting over the data tests These ensure that the Configs can be parsed and that the Data from them can be loaded --- .../components/parser/parser_test.go | 184 ------------------ .../apidefinitions/parse_api_resource.go | 4 +- .../parser/combine/api_resource.go | 59 +++--- .../parser/combine/api_resources.go | 53 +++++ .../workaround_network_29303.go | 8 +- ..._swagger_file.go => parse_swagger_file.go} | 7 - .../internal/components/data_test.go | 158 +++++++++++++++ .../importer-rest-api-specs/models/models.go | 37 ---- .../run_validate_can_parse_data_test.go | 78 -------- 9 files changed, 241 insertions(+), 347 deletions(-) delete mode 100644 tools/importer-rest-api-specs/components/parser/parser_test.go create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/combine/api_resources.go rename tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testhelpers/{todo_parse_swagger_file.go => parse_swagger_file.go} (83%) create mode 100644 tools/importer-rest-api-specs/internal/components/data_test.go delete mode 100644 tools/importer-rest-api-specs/models/models.go delete mode 100644 tools/importer-rest-api-specs/pipeline/run_validate_can_parse_data_test.go 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 35fdd5ad821..00000000000 --- a/tools/importer-rest-api-specs/components/parser/parser_test.go +++ /dev/null @@ -1,184 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package parser - -import "testing" - -func TestFoo(t *testing.T) { - t.Fatal("TODO") -} - -//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/internal/components/apidefinitions/parse_api_resource.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parse_api_resource.go index 4e0e23a97c6..d4328468454 100644 --- 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 @@ -56,7 +56,7 @@ func parseAPIResourcesFromFile(filePath, serviceName string, resourceProvider *s discoveredResources := map[string]sdkModels.APIResource{ normalizedTag: *resource, } - combined, err := combine.ResourcesWith(parsedAPIResources, discoveredResources) + combined, err := combine.APIResourcesWith(parsedAPIResources, discoveredResources) if err != nil { return nil, fmt.Errorf("combining the APIResources for the identified Swagger Tag %q: %+v", tag, err) } @@ -97,7 +97,7 @@ func parseAPIResourcesFromFile(filePath, serviceName string, resourceProvider *s discoveredResources := map[string]sdkModels.APIResource{ normalizedTag: *resource, } - combined, err := combine.ResourcesWith(parsedAPIResources, discoveredResources) + combined, err := combine.APIResourcesWith(parsedAPIResources, discoveredResources) if err != nil { return nil, fmt.Errorf("combining the APIResources for the inferred Swagger Tag %q: %+v", normalizedTag, err) } 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 index 979b5ccdd66..712a24aed26 100644 --- 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 @@ -9,45 +9,30 @@ import ( sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" ) -func ResourcesWith(first, other map[string]sdkModels.APIResource) (*map[string]sdkModels.APIResource, error) { - resources := make(map[string]sdkModels.APIResource) - for k, v := range first { - resources[k] = v +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 - for k, v := range other { - existing, ok := resources[k] - if !ok { - resources[k] = v - continue - } - - constants, err := Constants(existing.Constants, v.Constants) - if err != nil { - return nil, fmt.Errorf("combining constants: %+v", err) - } - existing.Constants = *constants - - models, err := Models(existing.Models, v.Models) - if err != nil { - return nil, fmt.Errorf("combining models: %+v", err) - } - existing.Models = *models - - operations, err := operations(existing.Operations, v.Operations) - if err != nil { - return nil, fmt.Errorf("combining operations: %+v", err) - } - existing.Operations = *operations - - resourceIds, err := resourceIds(existing.ResourceIDs, v.ResourceIDs) - if err != nil { - return nil, fmt.Errorf("combining resource ids: %+v", err) - } - existing.ResourceIDs = *resourceIds - - resources[k] = existing + models, err := Models(first.Models, other.Models) + if err != nil { + return nil, fmt.Errorf("combining models: %+v", err) } + first.Models = *models - return &resources, nil + combinedOperations, err := operations(first.Operations, other.Operations) + if err != nil { + return nil, fmt.Errorf("combining operations: %+v", err) + } + first.Operations = *combinedOperations + + combinedResourceIDs, err := resourceIds(first.ResourceIDs, other.ResourceIDs) + if err != nil { + return nil, fmt.Errorf("combining resource ids: %+v", err) + } + first.ResourceIDs = *combinedResourceIDs + + 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..0d1f93a046a --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/combine/api_resources.go @@ -0,0 +1,53 @@ +// 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(first, other map[string]sdkModels.APIResource) (*map[string]sdkModels.APIResource, error) { + resources := make(map[string]sdkModels.APIResource) + for k, v := range first { + resources[k] = v + } + + for k, v := range other { + existing, ok := resources[k] + if !ok { + resources[k] = v + continue + } + + constants, err := Constants(existing.Constants, v.Constants) + if err != nil { + return nil, fmt.Errorf("combining constants: %+v", err) + } + existing.Constants = *constants + + models, err := Models(existing.Models, v.Models) + if err != nil { + return nil, fmt.Errorf("combining models: %+v", err) + } + existing.Models = *models + + operations, err := operations(existing.Operations, v.Operations) + if err != nil { + return nil, fmt.Errorf("combining operations: %+v", err) + } + existing.Operations = *operations + + resourceIds, err := resourceIds(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 +} 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 index 5d5c08168f5..dce893d19e1 100644 --- 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 @@ -2,9 +2,9 @@ package dataworkarounds import ( "fmt" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/combine" sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" - importerModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/models" ) var _ workaround = workaroundNetwork29303{} @@ -37,7 +37,11 @@ func (workaroundNetwork29303) Process(input sdkModels.APIVersion) (*sdkModels.AP return nil, fmt.Errorf("expected an APIResource named `PrivateLinkService` but didn't find one") } - input.Resources["PrivateLinkServices"] = importerModels.MergeResourcesForTag(correctlyNamedAPIResource, misnamedAPIResource) + 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/internal/components/apidefinitions/parser/testhelpers/todo_parse_swagger_file.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testhelpers/parse_swagger_file.go similarity index 83% rename from tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testhelpers/todo_parse_swagger_file.go rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testhelpers/parse_swagger_file.go index 77e25e31785..47607da87d5 100644 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testhelpers/todo_parse_swagger_file.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testhelpers/parse_swagger_file.go @@ -9,13 +9,6 @@ import ( discoveryModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/discovery/models" ) -/* -1. Enable the existing tests -2. Re-enable the "validation" tests (these check we can import the RM services/api versions from the config) -3. Re-enabling supplementary types -4. Documentation/explaning what the packages are -*/ - func ParseSwaggerFileForTesting(t *testing.T, filePath string) (*sdkModels.APIVersion, error) { input := discoveryModels.AvailableDataSet{ ServiceName: "Example", 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..681eacd3a0e --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/data_test.go @@ -0,0 +1,158 @@ +package components + +import ( + "fmt" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/terraform" + "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/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.Errorf("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/models/models.go b/tools/importer-rest-api-specs/models/models.go deleted file mode 100644 index 74281091708..00000000000 --- a/tools/importer-rest-api-specs/models/models.go +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package models - -import ( - sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" -) - -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/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 ca2f4c4904b..00000000000 --- a/tools/importer-rest-api-specs/pipeline/run_validate_can_parse_data_test.go +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package pipeline - -import ( - "testing" -) - -const ( - outputDirectoryJson = "../../../api-definitions/" - swaggerDirectory = "../../../submodules/rest-api-specs" - resourceManagerConfig = "../../../config/resource-manager.hcl" -) - -func TestConfigContainsValidServiceNames(t *testing.T) { - t.Fatalf("TODO") - //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) { - t.Fatalf("TODO") - - //// 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) - // } - // }) - //} -} From 58f5bae840a1c6c0c0efb64ff4f9c6146469848a Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Mon, 15 Jul 2024 13:48:40 +0200 Subject: [PATCH 29/58] `tools/importer-rest-api-specs`: fixing the test format --- .../apidefinitions/parser/comparison/operation_option.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 index 107b473c1b9..002c6eb1c03 100644 --- 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 @@ -8,11 +8,11 @@ import ( func OperationOptionMatch(first, second sdkModels.SDKOperationOption) bool { if pointer.From(first.HeaderName) != pointer.From(second.HeaderName) { - logging.Tracef("HeaderName differed - %q vs %q", first.HeaderName, 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", first.QueryStringName, 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) { From fe98217caed66d6d2bac716e79ca0fbd6aac51f2 Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Mon, 15 Jul 2024 13:51:46 +0200 Subject: [PATCH 30/58] tools/importer-rest-api-specs: skipping the local test --- .../components/apidefinitions/models_debugging_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 index 447359d0c23..33359970137 100644 --- 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 @@ -8,11 +8,12 @@ import ( ) 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{ - "/Users/tharvey/code/src/github.com/hashicorp/pandora/submodules/rest-api-specs/specification/storagecache/resource-manager/Microsoft.StorageCache/stable/2023-05-01/amlfilesystem.json", + "/path/to/submodules/rest-api-specs/specification/storagecache/resource-manager/Microsoft.StorageCache/stable/2023-05-01/amlfilesystem.json", }, FilePathsContainingSupplementaryData: []string{}, } From 26268dbde91e510255d9782ecd9ff041cef9fca5 Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Mon, 15 Jul 2024 13:55:35 +0200 Subject: [PATCH 31/58] `tools/importer-rest-api-specs`: fixing the non-discriminator tests --- .../components/apidefinitions/parser/operations_test.go | 6 +++--- .../apidefinitions/parser/parse_resource_ids_test.go | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/operations_test.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/operations_test.go index 7ca1c73c0bc..2ac62e246b8 100644 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/operations_test.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/operations_test.go @@ -78,7 +78,7 @@ 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"), }, }, }, @@ -117,7 +117,7 @@ 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"), }, }, }, @@ -1437,7 +1437,7 @@ 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"), }, }, }, diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_resource_ids_test.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_resource_ids_test.go index 81a7b0b565b..632c2d4182a 100644 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_resource_ids_test.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_resource_ids_test.go @@ -520,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"), }, }, }, From 9643f43765ca735a7e7b24e5730881653c9a248f Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Mon, 15 Jul 2024 14:58:41 +0200 Subject: [PATCH 32/58] `tools/importer-rest-api-specs`: updating/reworking the supplementary data test @manicminer is going to work through fixing this one up, this requires parsing the data identifying any parent types and patching any disciminated implementations across --- .../apidefinitions/parse_api_resource.go | 5 +- .../apidefinitions/parse_api_version.go | 25 +------- .../parse_supplementary_data.go | 4 +- .../parser/models_discriminators_test.go | 2 +- .../parser/operation/request_object.go | 3 +- .../parser/operation/response_object.go | 3 +- .../parser/parse_supplementary_data.go | 28 ++++----- .../parser/parse_supplementary_data_test.go | 56 +++++++++++++---- .../apidefinitions/parser/parser.go | 5 +- .../parser/parsingcontext/build.go | 4 +- .../parser/parsingcontext/context.go | 5 +- .../parser/parsingcontext/helpers.go | 2 +- .../parser/parsingcontext/parse_model.go | 63 +++++++++---------- .../parsingcontext/parse_object_definition.go | 18 +++--- .../supplementary_data_implementations.json | 45 +++++++++++++ .../testdata/supplementary_data_parent.json | 53 ++++++++++++++++ .../parser/testhelpers/parse_swagger_file.go | 10 ++- 17 files changed, 216 insertions(+), 115 deletions(-) create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/supplementary_data_implementations.json create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/supplementary_data_parent.json 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 index d4328468454..7b13cbd7511 100644 --- 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 @@ -10,13 +10,12 @@ import ( "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" - 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/resourceids" "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/logging" ) -func parseAPIResourcesFromFile(filePath, serviceName string, resourceProvider *string, existingAPIResources map[string]sdkModels.APIResource, supplementaryData parserModels.SupplementaryData, resourceIds resourceids.ParseResult) (*map[string]sdkModels.APIResource, error) { - parser, err := parser.NewAPIDefinitionsParser(filePath, supplementaryData) +func parseAPIResourcesFromFile(filePath, serviceName string, resourceProvider *string, existingAPIResources 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) } 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 index c0ea7cc09c6..de2229f2e3d 100644 --- 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 @@ -7,7 +7,6 @@ import ( "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" - 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/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" @@ -15,25 +14,7 @@ import ( // parseAPIVersion parses the information for this APIVersion from the AvailableDataSetForAPIVersion. func parseAPIVersion(serviceName string, input discoveryModels.AvailableDataSetForAPIVersion, resourceProvider *string) (*sdkModels.APIVersion, error) { - // Firstly let's go through and process each of the Supplementary Files - supplementaryData := parserModels.SupplementaryData{ - Constants: map[string]sdkModels.SDKConstant{}, - Models: map[string]sdkModels.SDKModel{}, - } - // TODO: re-enable this - //for _, filePath := range input.FilePathsContainingSupplementaryData { - // logging.Tracef("Processing Supplementary Data from file %q..", filePath) - // updatedSupplementaryData, err := parseSupplementaryDataFromFile(filePath, supplementaryData) - // if err != nil { - // return nil, fmt.Errorf("parsing the Supplementary Data within %q: %+v", filePath, err) - // } - // logging.Tracef("Processing Supplementary Data from file %q - Completed.", filePath) - // - // supplementaryData = *updatedSupplementaryData - // logging.Tracef("The Supplementary Data now contains %d Constants and %d Models", len(supplementaryData.Constants), len(supplementaryData.Models)) - //} - - // Next we need to pull out a list of each of the Resource IDs within this API Version + // 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{ @@ -43,7 +24,7 @@ func parseAPIVersion(serviceName string, input discoveryModels.AvailableDataSetF } for _, filePath := range input.FilePathsContainingAPIDefinitions { logging.Tracef("Loading the Resource IDs from %q..", filePath) - parser, err := parser.NewAPIDefinitionsParser(filePath, supplementaryData) + parser, err := parser.NewAPIDefinitionsParser(filePath) if err != nil { return nil, fmt.Errorf("parsing the API Definitions within %q: %+v", filePath, err) } @@ -62,7 +43,7 @@ func parseAPIVersion(serviceName string, input discoveryModels.AvailableDataSetF apiResources := make(map[string]sdkModels.APIResource) for _, filePath := range input.FilePathsContainingAPIDefinitions { logging.Tracef("Processing API Definitions from file %q..", filePath) - resources, err := parseAPIResourcesFromFile(filePath, serviceName, resourceProvider, apiResources, supplementaryData, foundResourceIDs) + resources, err := parseAPIResourcesFromFile(filePath, serviceName, resourceProvider, apiResources, foundResourceIDs) if err != nil { return nil, fmt.Errorf("parsing the APIResources from the Supplementary Data within %q: %+v", filePath, err) } diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parse_supplementary_data.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parse_supplementary_data.go index 1181488e3d7..2eac14db824 100644 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parse_supplementary_data.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parse_supplementary_data.go @@ -9,9 +9,9 @@ import ( ) // parseSupplementaryDataFromFile loads any available Supplementary Data from the file in question -func parseSupplementaryDataFromFile(filePath string, knownData parserModels.SupplementaryData) (*parserModels.SupplementaryData, error) { +func parseSupplementaryDataFromFile(filePath string) (*parserModels.SupplementaryData, error) { logging.Tracef("Building an Definitions Parser for %q..", filePath) - parser, err := parser.NewAPIDefinitionsParser(filePath, knownData) + parser, err := parser.NewAPIDefinitionsParser(filePath) if err != nil { return nil, fmt.Errorf("building the APIDefinitions Parser for %q: %+v", filePath, err) } diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models_discriminators_test.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models_discriminators_test.go index 42f32d7463b..f6be470d715 100644 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models_discriminators_test.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models_discriminators_test.go @@ -4,11 +4,11 @@ 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/components/apidefinitions/parser/testhelpers" ) func TestParseDiscriminatorsTopLevel(t *testing.T) { 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 index feac4f82c7e..e4e43ab2831 100644 --- 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 @@ -21,8 +21,7 @@ func requestObjectForOperation(parsingContext *parsingcontext.Context, input par for _, param := range unexpandedOperation.Parameters { if strings.EqualFold(param.In, "body") { parsingModel := true - loadParentType := true - objectDefinition, result, err := parsingContext.ParseObjectDefinition(param.Schema.Title, param.Schema.Title, param.Schema, known, parsingModel, loadParentType) + 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) } 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 index f2aaf220317..355f7db1953 100644 --- 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 @@ -41,8 +41,7 @@ func responseObjectForOperation(parsingContext *parsingcontext.Context, input pa } parsingModel := true - loadParentType := true - objectDefinition, nestedResult, err := parsingContext.ParseObjectDefinition(details.ResponseProps.Schema.Title, details.ResponseProps.Schema.Title, details.ResponseProps.Schema, result, parsingModel, loadParentType) + 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) } diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_supplementary_data.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_supplementary_data.go index 03183957aac..37630c8b83b 100644 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_supplementary_data.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_supplementary_data.go @@ -16,7 +16,7 @@ func (p *apiDefinitionsParser) SupplementaryData() (*parserModels.SupplementaryD for objectName, definition := range p.context.SwaggerSpecRaw.Definitions { var parsedConstant *constants.ParsedConstant - parsedModel, err := p.context.ParseModel(objectName, definition, false) + parsedModel, err := p.context.ParseModel(objectName, definition) if err != nil { var err2 error parsedConstant, err2 = p.context.ParseConstant(objectName, definition) @@ -37,19 +37,19 @@ func (p *apiDefinitionsParser) SupplementaryData() (*parserModels.SupplementaryD } // FindNestedItemsYetToBeParsed takes ParseResult and so we need to shim this across - //shim := parserModels.ParseResult{ - // Constants: result.Constants, - // Models: result.Models, - //} - //// 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{}, shim) - //if err != nil { - // return nil, fmt.Errorf("finding nested items yet to be parsed: %+v", err) - //} - //if err := result.AppendParseResult(*nestedResult); err != nil { - // return nil, fmt.Errorf("appending nestedResult from Models used by existing Items: %+v", err) - //} + shim := parserModels.ParseResult{ + Constants: result.Constants, + Models: result.Models, + } + // 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{}, shim) + if err != nil { + return nil, fmt.Errorf("finding nested items yet to be parsed: %+v", err) + } + if err := result.AppendParseResult(*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_supplementary_data_test.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_supplementary_data_test.go index 342661d5848..e5bb8e5fcd7 100644 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_supplementary_data_test.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_supplementary_data_test.go @@ -1,27 +1,57 @@ package parser_test import ( + "github.com/hashicorp/go-azure-helpers/lang/pointer" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testhelpers" + discoveryModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/discovery/models" + "path/filepath" "testing" sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser" - parserModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models" ) func TestParseSupplementaryData(t *testing.T) { - filePath := "/Users/tharvey/code/src/github.com/hashicorp/pandora/submodules/rest-api-specs/specification/datafactory/resource-manager/Microsoft.DataFactory/stable/2018-06-01/entityTypes/LinkedService.json" - parser, err := parser.NewAPIDefinitionsParser(filePath, parserModels.SupplementaryData{ - Constants: make(map[string]sdkModels.SDKConstant), - Models: make(map[string]sdkModels.SDKModel), - }) - if err != nil { - t.Fatalf(err.Error()) + dataSet := discoveryModels.AvailableDataSet{ + ServiceName: "Example", + DataSetsForAPIVersions: map[string]discoveryModels.AvailableDataSetForAPIVersion{ + "2020-01-01": { + APIVersion: "2020-01-01", + ContainsStableAPIVersion: true, + FilePathsContainingAPIDefinitions: []string{ + filepath.Join("testdata", "supplementary_data_parent.json"), + }, + FilePathsContainingSupplementaryData: []string{ + filepath.Join("testdata", "supplementary_data_implementations.json"), + }, + }, + }, + ResourceProvider: nil, } - data, err := parser.SupplementaryData() + actual, err := testhelpers.ParseDataSetForTesting(t, dataSet, "2020-01-01") if err != nil { t.Fatalf(err.Error()) } - - t.Logf("Got %d Constants", len(data.Constants)) - t.Logf("Got %d Models", len(data.Models)) + expected := sdkModels.APIVersion{ + APIVersion: "2020-01-01", + Resources: map[string]sdkModels.APIResource{ + "Example": { + Constants: make(map[string]sdkModels.SDKConstant), + Models: map[string]sdkModels.SDKModel{ + "Cat": {}, + "ParentType": {}, + }, + Name: "Example", + Operations: map[string]sdkModels.SDKOperation{ + "Test": { + ResponseObject: &sdkModels.SDKObjectDefinition{ + Type: sdkModels.ReferenceSDKObjectDefinitionType, + ReferenceName: pointer.To("ParentType"), + }, + }, + }, + ResourceIDs: map[string]sdkModels.ResourceID{}, + }, + }, + } + testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) } 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 index 8de2be7e344..a87c277c6d4 100644 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parser.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parser.go @@ -3,7 +3,6 @@ package parser import ( "fmt" - 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" ) @@ -11,8 +10,8 @@ type apiDefinitionsParser struct { context *parsingcontext.Context } -func NewAPIDefinitionsParser(filePath string, supplementaryData parserModels.SupplementaryData) (*apiDefinitionsParser, error) { - paringContext, err := parsingcontext.BuildFromFile(filePath, supplementaryData) +func NewAPIDefinitionsParser(filePath string) (*apiDefinitionsParser, error) { + paringContext, err := parsingcontext.BuildFromFile(filePath) if err != nil { return nil, fmt.Errorf("building the parsing context: %+v", err) } 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 index 32ea56c2f71..fa8046c5027 100644 --- 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 @@ -8,10 +8,9 @@ import ( "github.com/go-openapi/analysis" "github.com/go-openapi/loads" "github.com/go-openapi/spec" - parserModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models" ) -func BuildFromFile(filePath string, supplementaryData parserModels.SupplementaryData) (*Context, error) { +func BuildFromFile(filePath string) (*Context, error) { directory := filepath.Dir(filePath) // parsing this twice looks silly, so why are we doing this? @@ -70,7 +69,6 @@ func BuildFromFile(filePath string, supplementaryData parserModels.Supplementary swaggerSpecExpandedRaw := expandedSwaggerDoc.Spec() return &Context{ FilePath: filePath, - SupplementaryData: supplementaryData, SwaggerSpecExpanded: swaggerSpecExpanded, SwaggerSpecWithReferences: swaggerSpecWithReferences, SwaggerSpecRaw: swaggerSpecWithReferencesRaw, 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 index 0391877e7f8..8eb2d894af3 100644 --- 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 @@ -3,16 +3,13 @@ package parsingcontext import ( "github.com/go-openapi/analysis" "github.com/go-openapi/spec" - parserModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models" ) // 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 - - SupplementaryData parserModels.SupplementaryData - + // SwaggerSpecExpanded contains a flattened version of the Swagger spec into a single file 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 index 52fbe7d9c8f..92358d9656f 100644 --- 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 @@ -103,7 +103,7 @@ func (c *Context) FindNestedItemsYetToBeParsed(operations map[string]sdkModels.S } parsedAsAConstant, constErr := constants.Parse(topLevelObject.Type, referenceName, nil, topLevelObject.Enum, topLevelObject.Extensions) - parsedAsAModel, modelErr := c.ParseModel(referenceName, *topLevelObject, false) + 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) } 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 index e0ec354ee71..7749b387dd1 100644 --- 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 @@ -12,8 +12,8 @@ import ( parserModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models" ) -func (c *Context) ParseModel(name string, input spec.Schema, loadParentType bool) (*parserModels.ParseResult, error) { - logging.Tracef("Parsing details for Model %q (Load Parent Type %t)..", name, loadParentType) +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{}, @@ -28,8 +28,8 @@ func (c *Context) ParseModel(name string, input spec.Schema, loadParentType bool 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 := c.fieldsForModel(name, input, result, loadParentType) + // 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) } @@ -45,7 +45,7 @@ func (c *Context) ParseModel(name string, input spec.Schema, loadParentType bool // 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 := c.modelDetailsFromObject(name, input, *fields, loadParentType) + model, err := c.modelDetailsFromObject(name, input, *fields) if err != nil { return nil, fmt.Errorf("populating model details for %q: %+v", name, err) } @@ -129,7 +129,7 @@ func (c *Context) findConstantsWithinModel(fieldName string, modelName *string, return &result, nil } -func (c *Context) detailsForField(modelName string, propertyName string, value spec.Schema, isRequired bool, known parserModels.ParseResult, loadParentType bool) (*sdkModels.SDKField, *parserModels.ParseResult, error) { +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{ @@ -149,7 +149,7 @@ func (c *Context) detailsForField(modelName string, propertyName string, value s // first get the object definition parsingModel := false - objectDefinition, nestedResult, err := c.ParseObjectDefinition(modelName, propertyName, &value, result, parsingModel, loadParentType) + objectDefinition, nestedResult, err := c.ParseObjectDefinition(modelName, propertyName, &value, result, parsingModel) if err != nil { return nil, nil, fmt.Errorf("parsing object definition: %+v", err) } @@ -176,7 +176,7 @@ func (c *Context) detailsForField(modelName string, propertyName string, value s break } } - nestedField, nestedResult, err := c.detailsForField(inlinedName, propName, propVal, nestedFieldRequired, result, loadParentType) + 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) } @@ -205,7 +205,7 @@ func (c *Context) detailsForField(modelName string, propertyName string, value s break } } - nestedField, nestedResult, err := c.detailsForField(inlinedName, propName, propVal, nestedFieldRequired, result, loadParentType) + 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) } @@ -216,7 +216,7 @@ func (c *Context) detailsForField(modelName string, propertyName string, value s } } - inlinedModelDetails, err := c.modelDetailsFromObject(inlinedName, value, nestedFields, loadParentType) + 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) } @@ -233,7 +233,7 @@ func (c *Context) detailsForField(modelName string, propertyName string, value s return &field, &result, err } -func (c *Context) fieldsForModel(modelName string, input spec.Schema, known parserModels.ParseResult, loadParentType bool) (*map[string]sdkModels.SDKField, *parserModels.ParseResult, error) { +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{}, @@ -295,7 +295,7 @@ func (c *Context) fieldsForModel(modelName string, input spec.Schema, known pars if parent.Title != "" { innerModelName = parent.Title } - parsedParent, nestedResult, err := c.fieldsForModel(innerModelName, parent, known, true) + 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) } @@ -322,7 +322,7 @@ func (c *Context) fieldsForModel(modelName string, input spec.Schema, known pars requiredFields[k] = struct{}{} } - nestedFields, nestedResult, err := c.fieldsForModel(*fragmentName, *topLevelObject, result, loadParentType) + 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) } @@ -339,7 +339,7 @@ func (c *Context) fieldsForModel(modelName string, input spec.Schema, known pars // 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, loadParentType) + 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) } @@ -356,7 +356,7 @@ func (c *Context) fieldsForModel(modelName string, input spec.Schema, known pars return &fields, &result, nil } -func (c *Context) modelDetailsFromObject(modelName string, input spec.Schema, fields map[string]sdkModels.SDKField, loadParentType bool) (*sdkModels.SDKModel, error) { +func (c *Context) modelDetailsFromObject(modelName string, input spec.Schema, fields map[string]sdkModels.SDKField) (*sdkModels.SDKModel, error) { details := sdkModels.SDKModel{ Fields: fields, } @@ -381,25 +381,20 @@ func (c *Context) modelDetailsFromObject(modelName string, input spec.Schema, fi if v, ok := input.Extensions.GetString("x-ms-discriminator-value"); ok { details.DiscriminatedValue = &v - // NOTE: we want the option to conditionally load the parent type to be able to load the Supplementary Data - // first (since these may only contain the Discriminated Implementations and not the Parent Type). - if loadParentType { - // so we need to find the ancestor details - 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 - } - - // 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 - } + // so we need to find the ancestor details + 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 } + // 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 @@ -445,7 +440,7 @@ func (c *Context) findOrphanedDiscriminatedModels(serviceName string) (*parserMo for modelName, definition := range c.SwaggerSpecRaw.Definitions { if _, ok := definition.Extensions.GetString("x-ms-discriminator-value"); ok { - details, err := c.ParseModel(modelName, definition, false) + details, err := c.ParseModel(modelName, definition) if err != nil { return nil, fmt.Errorf("parsing model details for model %q: %+v", modelName, err) } @@ -465,7 +460,7 @@ func (c *Context) findOrphanedDiscriminatedModels(serviceName string) (*parserMo return nil, fmt.Errorf("determining ancestor type for model %q: %+v", modelName, err) } - details, err := c.ParseModel(modelName, definition, false) + details, err := c.ParseModel(modelName, definition) if err != nil { return nil, fmt.Errorf("parsing model details for model %q: %+v", modelName, err) } 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 index f5f99a1e2a1..464f281e61c 100644 --- 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 @@ -15,14 +15,14 @@ import ( // 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, loadParentType bool) (*sdkModels.SDKObjectDefinition, *parserModels.ParseResult, error) { +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) + _ = result.Append(known) // if it's an enum then parse that out if len(input.Enum) > 0 { @@ -75,7 +75,8 @@ func (c *Context) ParseObjectDefinition(modelName, propertyName string, input *s } // then call ourselves to work out what to do with it - objectDefinition, nestedResult, err := c.ParseObjectDefinition(*objectName, propertyName, topLevelObject, knownIncludingPlaceholder, true, loadParentType) + const localParsingModel = true + objectDefinition, nestedResult, err := c.ParseObjectDefinition(*objectName, propertyName, topLevelObject, knownIncludingPlaceholder, localParsingModel) if err != nil { return nil, nil, err } @@ -103,14 +104,15 @@ func (c *Context) ParseObjectDefinition(modelName, propertyName string, input *s } if len(allOfFields) == 1 { inheritedModel := allOfFields[0] - return c.ParseObjectDefinition(inheritedModel.Title, propertyName, &inheritedModel, result, true, loadParentType) + 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, loadParentType) + nestedResult, err := c.ParseModel(modelName, *input) if err != nil { return nil, nil, fmt.Errorf("parsing object from inlined model %q: %+v", modelName, err) } @@ -148,7 +150,7 @@ func (c *Context) ParseObjectDefinition(modelName, propertyName string, input *s innerModelName = input.AdditionalProperties.Schema.Title } - nestedItem, nestedResult, err := c.ParseObjectDefinition(innerModelName, propertyName, input.AdditionalProperties.Schema, result, true, loadParentType) + 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) } @@ -171,7 +173,7 @@ func (c *Context) ParseObjectDefinition(modelName, propertyName string, input *s inlinedName = fmt.Sprintf("%s%sInlined", cleanup.NormalizeName(modelName), cleanup.NormalizeName(propertyName)) } - nestedItem, nestedResult, err := c.ParseObjectDefinition(inlinedName, propertyName, input.Items.Schema, result, true, loadParentType) + 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) } @@ -274,7 +276,7 @@ func (c *Context) parseDataFactoryCustomTypes(input *spec.Schema, known parserMo if err != nil { return nil, nil, fmt.Errorf("finding the top-level-object %q: %+v", elementType, err) } - parseResult, err := c.ParseModel(elementType, *referencedModel, true) + parseResult, err := c.ParseModel(elementType, *referencedModel) if err != nil { return nil, nil, fmt.Errorf("parsing the Model %q: %+v", elementType, err) } diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/supplementary_data_implementations.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/supplementary_data_implementations.json new file mode 100644 index 00000000000..ae7e1f676c7 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/supplementary_data_implementations.json @@ -0,0 +1,45 @@ +{ + "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": { + "Cat": { + "description": "A cat is a kind of ParentType", + "allOf": [ + { + "$ref": "#/definitions/ParentType" + } + ], + "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/internal/components/apidefinitions/parser/testdata/supplementary_data_parent.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/supplementary_data_parent.json new file mode 100644 index 00000000000..8fa4340aae7 --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/supplementary_data_parent.json @@ -0,0 +1,53 @@ +{ + "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": { + "/example": { + "get": { + "tags": [ + "Example" + ], + "operationId": "Example_Test", + "description": "An example returning a parent discriminated type", + "parameters": [], + "responses": { + "200": { + "description": "Success.", + "schema": { + "$ref": "#/definitions/ParentType" + } + } + } + } + } + }, + "definitions": { + "ParentType": { + "discriminator": "typeName", + "properties": { + "typeName": { + "type": "string" + } + }, + "type": "object", + "title": "ParentType" + } + }, + "parameters": {} +} \ No newline at end of file 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 index 47607da87d5..2d83ef2ba2c 100644 --- 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 @@ -24,13 +24,17 @@ func ParseSwaggerFileForTesting(t *testing.T, filePath string) (*sdkModels.APIVe }, 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()) } - apiVersion, ok := result.APIVersions["2020-01-01"] + out, ok := result.APIVersions[apiVersion] if !ok { - t.Fatalf("expected an API Version `2020-01-01` but didn't get one") + t.Fatalf("expected an API Version `%s` but didn't get one", apiVersion) } - return &apiVersion, nil + return &out, nil } From 898d028071c23d60309913cf72bf25e354f66556 Mon Sep 17 00:00:00 2001 From: Tom Bamford Date: Wed, 17 Jul 2024 01:27:34 +0100 Subject: [PATCH 33/58] `importer-rest-api-specs`: fix up discriminator handling for datafactory --- .../apidefinitions/parse_api_resource.go | 53 ++++++++---- .../cleanup/normalize_api_definitions.go | 13 +++ .../parser/commonschema_test.go | 34 ++++---- .../apidefinitions/parser/constants_test.go | 36 ++++---- .../workaround_network_29303.go | 2 +- .../parser/models_datafactorycustom_test.go | 2 +- .../parser/models_dictionaries_test.go | 14 ++-- .../parser/models_discriminators_test.go | 29 +++---- .../apidefinitions/parser/models_test.go | 48 +++++------ .../apidefinitions/parser/operations_test.go | 84 +++++++++---------- .../parser/parse_discriminated.go | 69 +++++++++++++++ .../parser/parse_resource_ids_test.go | 34 ++++---- .../parser/parse_supplementary_data_test.go | 6 +- .../parser/parsingcontext/helpers.go | 4 +- .../parser/parsingcontext/parse_model.go | 59 ------------- .../parser/swagger_tags_test.go | 4 +- .../parser/testhelpers/parse_swagger_file.go | 8 +- .../internal/components/data_test.go | 2 +- 18 files changed, 275 insertions(+), 226 deletions(-) create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_discriminated.go 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 index 7b13cbd7511..48c2c1e17d7 100644 --- 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 @@ -2,8 +2,6 @@ package apidefinitions import ( "fmt" - "path/filepath" - "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" @@ -65,25 +63,17 @@ func parseAPIResourcesFromFile(filePath, serviceName string, resourceProvider *s // 3. Then parse over any Swagger Operations which DON'T have a Tag if !ignore.SwaggerTag(serviceName) { - // 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. - directory := filepath.Dir(filePath) - fileName := strings.TrimPrefix(filePath, directory) - fileName = strings.TrimPrefix(fileName, fmt.Sprintf("%c", filepath.Separator)) - fileName = strings.TrimSuffix(fileName, ".json") - inferredTag := cleanup.PluraliseName(fileName) - normalizedTag := cleanup.NormalizeTag(inferredTag) - normalizedTag = cleanup.NormalizeResourceName(normalizedTag) + inferredTag := cleanup.InferTagFromFilename(filePath) // pass in any existing/known data so that we can reuse the models/references existing := sdkModels.APIResource{ Constants: make(map[string]sdkModels.SDKConstant), Models: make(map[string]sdkModels.SDKModel), - Name: normalizedTag, + Name: inferredTag, Operations: make(map[string]sdkModels.SDKOperation), ResourceIDs: make(map[string]sdkModels.ResourceID), } - if v, ok := parsedAPIResources[normalizedTag]; ok { + if v, ok := parsedAPIResources[inferredTag]; ok { existing = v } @@ -94,17 +84,48 @@ func parseAPIResourcesFromFile(filePath, serviceName string, resourceProvider *s if resource != nil { discoveredResources := map[string]sdkModels.APIResource{ - normalizedTag: *resource, + inferredTag: *resource, + } + combined, err := combine.APIResourcesWith(parsedAPIResources, discoveredResources) + if err != nil { + return nil, fmt.Errorf("combining the APIResources for the inferred Swagger Tag %q: %+v", inferredTag, err) + } + parsedAPIResources = *combined + } + } + + // 3. 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, + } + resource = cleanup.NormalizeAPIResource(resource) + + discoveredResources := map[string]sdkModels.APIResource{ + inferredTag: resource, } combined, err := combine.APIResourcesWith(parsedAPIResources, discoveredResources) if err != nil { - return nil, fmt.Errorf("combining the APIResources for the inferred Swagger Tag %q: %+v", normalizedTag, err) + return nil, fmt.Errorf("combining the APIResources for the inferred Swagger Tag %q: %+v", inferredTag, err) } parsedAPIResources = *combined } } - // 3. Now that we have a canonical list of resources - can we simplify the Operation names at all? + // 4. Now that we have a canonical list of resources - can we simplify the Operation names at all? simplifiedAPIDefinitions := make(map[string]sdkModels.APIResource) for resourceName, resource := range parsedAPIResources { logging.Tracef("Simplifying operation names for resource %q", resourceName) diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/normalize_api_definitions.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/normalize_api_definitions.go index db96a26f327..368787f19ee 100644 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/normalize_api_definitions.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/normalize_api_definitions.go @@ -2,6 +2,7 @@ package cleanup import ( "fmt" + "path/filepath" "strings" ) @@ -42,3 +43,15 @@ func NormalizeTag(input string) string { 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/commonschema_test.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema_test.go index 79cca22b502..b0b1fb2352f 100644 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema_test.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/commonschema_test.go @@ -18,7 +18,7 @@ import ( // TODO: Zones func TestParseModel_CommonSchema_IdentityLegacySystemAndUserAssignedList(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_commonschema_identitylegacysystemanduserassignedlist.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_commonschema_identitylegacysystemanduserassignedlist.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -66,7 +66,7 @@ func TestParseModel_CommonSchema_IdentityLegacySystemAndUserAssignedList(t *test } func TestParseModel_CommonSchema_IdentityLegacySystemAndUserAssignedMap(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_commonschema_identitylegacysystemanduserassignedmap.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_commonschema_identitylegacysystemanduserassignedmap.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -116,7 +116,7 @@ func TestParseModel_CommonSchema_IdentityLegacySystemAndUserAssignedMap(t *testi 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 := testhelpers.ParseSwaggerFileForTesting(t, "model_commonschema_identitylegacysystemanduserassignedmap_extrafields.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_commonschema_identitylegacysystemanduserassignedmap_extrafields.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -164,7 +164,7 @@ func TestParseModel_CommonSchema_IdentityLegacySystemAndUserAssignedMap_ExtraFie } func TestParseModel_CommonSchema_IdentityLegacySystemAndUserAssignedMap_GenericDictionary(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_commonschema_identitylegacysystemanduserassignedmap_genericdictionary.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_commonschema_identitylegacysystemanduserassignedmap_genericdictionary.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -212,7 +212,7 @@ func TestParseModel_CommonSchema_IdentityLegacySystemAndUserAssignedMap_GenericD } func TestParseModel_CommonSchema_IdentitySystemAssigned(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_commonschema_identitysystemassigned.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_commonschema_identitysystemassigned.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -260,7 +260,7 @@ func TestParseModel_CommonSchema_IdentitySystemAssigned(t *testing.T) { } func TestParseModel_CommonSchema_IdentitySystemAndUserAssignedList(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_commonschema_identitysystemanduserassignedlist.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_commonschema_identitysystemanduserassignedlist.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -308,7 +308,7 @@ func TestParseModel_CommonSchema_IdentitySystemAndUserAssignedList(t *testing.T) } func TestParseModel_CommonSchema_IdentitySystemAndUserAssignedMap(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_commonschema_identitysystemanduserassignedmap.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_commonschema_identitysystemanduserassignedmap.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -358,7 +358,7 @@ func TestParseModel_CommonSchema_IdentitySystemAndUserAssignedMap(t *testing.T) 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 := testhelpers.ParseSwaggerFileForTesting(t, "model_commonschema_identitysystemanduserassignedmap.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_commonschema_identitysystemanduserassignedmap.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -406,7 +406,7 @@ func TestParseModel_CommonSchema_IdentitySystemAndUserAssignedMap_ExtraFields(t } func TestParseModel_CommonSchema_IdentitySystemOrUserAssignedList(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_commonschema_identitysystemoruserassignedlist.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_commonschema_identitysystemoruserassignedlist.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -454,7 +454,7 @@ func TestParseModel_CommonSchema_IdentitySystemOrUserAssignedList(t *testing.T) } func TestParseModel_CommonSchema_IdentitySystemOrUserAssignedMap(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_commonschema_identitysystemoruserassignedmap.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_commonschema_identitysystemoruserassignedmap.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -504,7 +504,7 @@ func TestParseModel_CommonSchema_IdentitySystemOrUserAssignedMap(t *testing.T) { 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 := testhelpers.ParseSwaggerFileForTesting(t, "model_commonschema_identitysystemoruserassignedmap_delegatedresources.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_commonschema_identitysystemoruserassignedmap_delegatedresources.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -554,7 +554,7 @@ func TestParseModel_CommonSchema_IdentitySystemOrUserAssignedMap_DelegatedResour 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 := testhelpers.ParseSwaggerFileForTesting(t, "model_commonschema_identitysystemoruserassignedmap_extrafields.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_commonschema_identitysystemoruserassignedmap_extrafields.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -602,7 +602,7 @@ func TestParseModel_CommonSchema_IdentitySystemOrUserAssignedMap_ExtraFields(t * } func TestParseModel_CommonSchema_IdentityUserAssignedList(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_commonschema_identityuserassignedlist.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_commonschema_identityuserassignedlist.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -650,7 +650,7 @@ func TestParseModel_CommonSchema_IdentityUserAssignedList(t *testing.T) { } func TestParseModel_CommonSchema_IdentityUserAssignedMap(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_commonschema_identityuserassignedmap.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_commonschema_identityuserassignedmap.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -698,7 +698,7 @@ func TestParseModel_CommonSchema_IdentityUserAssignedMap(t *testing.T) { } func TestParseModel_CommonSchema_IdentityUserAssignedMap_PrincipalID(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_commonschema_identityuserassignedmap_principalid.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_commonschema_identityuserassignedmap_principalid.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -746,7 +746,7 @@ func TestParseModel_CommonSchema_IdentityUserAssignedMap_PrincipalID(t *testing. } func TestParseModel_CommonSchema_IdentityUserAssignedMap_TenantID(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_commonschema_identityuserassignedmap_tenantid.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_commonschema_identityuserassignedmap_tenantid.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -794,7 +794,7 @@ func TestParseModel_CommonSchema_IdentityUserAssignedMap_TenantID(t *testing.T) } func TestParseModel_CommonSchema_Location(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_commonschema_location.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_commonschema_location.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/constants_test.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/constants_test.go index 2e980713dff..10eef33d1d3 100644 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/constants_test.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/constants_test.go @@ -13,7 +13,7 @@ import ( 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 := testhelpers.ParseSwaggerFileForTesting(t, "constants_integers_as_ints.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "constants_integers_as_ints.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -68,7 +68,7 @@ func TestParseConstantsIntegersTopLevelAsInts(t *testing.T) { 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 := testhelpers.ParseSwaggerFileForTesting(t, "constants_integers_with_names.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "constants_integers_with_names.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -121,7 +121,7 @@ func TestParseConstantsIntegersTopLevelAsIntsWithDisplayName(t *testing.T) { func TestParseConstantsIntegersTopLevelAsStrings(t *testing.T) { // Tests an Integer Constant with modelAsString, which is bad data / should be ignored - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "constants_integers_as_strings.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "constants_integers_as_strings.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -174,7 +174,7 @@ func TestParseConstantsIntegersTopLevelAsStrings(t *testing.T) { 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 := testhelpers.ParseSwaggerFileForTesting(t, "constants_integers_as_ints_inlined.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "constants_integers_as_ints_inlined.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -228,7 +228,7 @@ func TestParseConstantsIntegersInlinedAsInts(t *testing.T) { 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 := testhelpers.ParseSwaggerFileForTesting(t, "constants_integers_with_names_inlined.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "constants_integers_with_names_inlined.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -280,7 +280,7 @@ func TestParseConstantsIntegersInlinedAsIntsWithDisplayName(t *testing.T) { } func TestParseConstantsMultipleTypeEnums(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "constants_multiple_type_enums.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "constants_multiple_type_enums.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -362,7 +362,7 @@ func TestParseConstantsMultipleTypeEnums(t *testing.T) { func TestParseConstantsIntegersInlinedAsStrings(t *testing.T) { // Tests an Integer Constant defined Inline with modelAsString, which is bad data / should be ignored - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "constants_integers_as_strings_inlined.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "constants_integers_as_strings_inlined.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -415,7 +415,7 @@ func TestParseConstantsIntegersInlinedAsStrings(t *testing.T) { 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 := testhelpers.ParseSwaggerFileForTesting(t, "constants_floats_as_floats.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "constants_floats_as_floats.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -468,7 +468,7 @@ func TestParseConstantsFloatsTopLevelAsFloats(t *testing.T) { func TestParseConstantsFloatsTopLevelAsStrings(t *testing.T) { // Tests an Float Constant with modelAsString, which is bad data / should be ignored - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "constants_floats_as_strings.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "constants_floats_as_strings.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -521,7 +521,7 @@ func TestParseConstantsFloatsTopLevelAsStrings(t *testing.T) { 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 := testhelpers.ParseSwaggerFileForTesting(t, "constants_floats_as_floats_inlined.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "constants_floats_as_floats_inlined.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -574,7 +574,7 @@ func TestParseConstantsFloatsInlinedAsFloats(t *testing.T) { func TestParseConstantsFloatsInlinedAsStrings(t *testing.T) { // Tests an Float Constant defined inline with modelAsString, which is bad data / should be ignored - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "constants_floats_as_strings_inlined.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "constants_floats_as_strings_inlined.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -626,7 +626,7 @@ func TestParseConstantsFloatsInlinedAsStrings(t *testing.T) { } func TestParseConstantsStringsTopLevel(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "constants_strings.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "constants_strings.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -682,7 +682,7 @@ 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 := testhelpers.ParseSwaggerFileForTesting(t, "constants_strings_as_non_strings.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "constants_strings_as_non_strings.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -734,7 +734,7 @@ func TestParseConstantsStringsTopLevelAsNonStrings(t *testing.T) { } func TestParseConstantsStringsInlined(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "constants_strings_inlined.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "constants_strings_inlined.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -786,7 +786,7 @@ func TestParseConstantsStringsInlined(t *testing.T) { } func TestParseConstantsStringsInlinedAsNonStrings(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "constants_strings_inlined_as_non_strings.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "constants_strings_inlined_as_non_strings.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -839,7 +839,7 @@ func TestParseConstantsStringsInlinedAsNonStrings(t *testing.T) { 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 := testhelpers.ParseSwaggerFileForTesting(t, "constants_strings_which_are_floats.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "constants_strings_which_are_floats.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -892,7 +892,7 @@ func TestParseConstantsStringsTopLevelContainingFloats(t *testing.T) { 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 := testhelpers.ParseSwaggerFileForTesting(t, "constants_floats_as_floats_inlined.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "constants_floats_as_floats_inlined.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -944,7 +944,7 @@ func TestParseConstantsStringsInlinedContainingFloats(t *testing.T) { } func TestParseConstantsFromParameters(t *testing.T) { - result, err := testhelpers.ParseSwaggerFileForTesting(t, "constants_in_operation_parameters.json") + 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/workaround_network_29303.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/dataworkarounds/workaround_network_29303.go index dce893d19e1..b4d1f91d5d0 100644 --- 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 @@ -2,9 +2,9 @@ package dataworkarounds import ( "fmt" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/combine" 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{} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models_datafactorycustom_test.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models_datafactorycustom_test.go index 89040042329..b342552846b 100644 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models_datafactorycustom_test.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models_datafactorycustom_test.go @@ -14,7 +14,7 @@ import ( func TestParseModelWithDataFactoryCustomTypes(t *testing.T) { // This test ensures that we parse the Data Factory Custom Types out to regular Object Definitions. - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_using_datafactory_custom_types.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_using_datafactory_custom_types.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models_dictionaries_test.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models_dictionaries_test.go index cf4b4b2a734..e545ff9e403 100644 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models_dictionaries_test.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models_dictionaries_test.go @@ -4,15 +4,15 @@ 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/components/apidefinitions/parser/testhelpers" ) func TestParseModelWithADictionaryOfIntegers(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_dictionary_of_integers.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_dictionary_of_integers.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -63,7 +63,7 @@ func TestParseModelWithADictionaryOfIntegers(t *testing.T) { } func TestParseModelWithADictionaryOfIntegersInlined(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_dictionary_of_integers_inlined.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_dictionary_of_integers_inlined.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -114,7 +114,7 @@ func TestParseModelWithADictionaryOfIntegersInlined(t *testing.T) { } func TestParseModelWithADictionaryOfAnObject(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_dictionary_of_object.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_dictionary_of_object.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -198,7 +198,7 @@ func TestParseModelWithADictionaryOfAnObject(t *testing.T) { } func TestParseModelWithADictionaryOfAnObjectInlined(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_dictionary_of_object_inlined.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_dictionary_of_object_inlined.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -282,7 +282,7 @@ func TestParseModelWithADictionaryOfAnObjectInlined(t *testing.T) { } func TestParseModelWithADictionaryOfString(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_dictionary_of_string.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_dictionary_of_string.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -333,7 +333,7 @@ func TestParseModelWithADictionaryOfString(t *testing.T) { } func TestParseModelWithADictionaryOfStringInlined(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_dictionary_of_string_inlined.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_dictionary_of_string_inlined.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models_discriminators_test.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models_discriminators_test.go index f6be470d715..ae496c23c7d 100644 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models_discriminators_test.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models_discriminators_test.go @@ -12,7 +12,7 @@ import ( ) func TestParseDiscriminatorsTopLevel(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_discriminators_simple.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_discriminators_simple.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -108,7 +108,7 @@ func TestParseDiscriminatorsTopLevel(t *testing.T) { } func TestParseDiscriminatorsWithinArray(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_discriminators_within_array.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_discriminators_within_array.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -214,7 +214,7 @@ func TestParseDiscriminatorsWithinArray(t *testing.T) { } func TestParseDiscriminatorsWithinDiscriminators(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_discriminators_within_discriminators.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_discriminators_within_discriminators.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -398,7 +398,7 @@ 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 := testhelpers.ParseSwaggerFileForTesting(t, "model_discriminators_parent_that_shouldnt_be.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_discriminators_parent_that_shouldnt_be.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -442,7 +442,7 @@ 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 := testhelpers.ParseSwaggerFileForTesting(t, "model_discriminators_child_that_shouldnt_be.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_discriminators_child_that_shouldnt_be.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -486,7 +486,7 @@ 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 := testhelpers.ParseSwaggerFileForTesting(t, "model_discriminators_child_used_as_parent.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_discriminators_child_used_as_parent.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -582,7 +582,7 @@ func TestParseDiscriminatedChildTypeWhereParentShouldNotBeUsed(t *testing.T) { } func TestParseDiscriminatorsInheritingFromOtherDiscriminators(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_discriminators_inherited_from_discriminators.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_discriminators_inherited_from_discriminators.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -685,7 +685,7 @@ func TestParseDiscriminatorsInheritingFromOtherDiscriminators(t *testing.T) { } func TestParseDiscriminatorsDeepInheritance(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_discriminators_deep_inheritance.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_discriminators_deep_inheritance.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -867,7 +867,7 @@ func TestParseDiscriminatorsDeepInheritance(t *testing.T) { 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 := testhelpers.ParseSwaggerFileForTesting(t, "model_discriminators_multiple_parents.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_discriminators_multiple_parents.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -1017,7 +1017,7 @@ func TestParseDiscriminatorsWithMultipleParents(t *testing.T) { 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 := testhelpers.ParseSwaggerFileForTesting(t, "model_discriminators_multiple_parents_within_array.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_discriminators_multiple_parents_within_array.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -1168,7 +1168,7 @@ func TestParseDiscriminatorsWithMultipleParentsWithinArray(t *testing.T) { } func TestParseDiscriminatorsOrphanedChild(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "/nestedtestdata/model_discriminators_orphaned_child.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "/nestedtestdata/model_discriminators_orphaned_child.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -1244,7 +1244,7 @@ func TestParseDiscriminatorsOrphanedChild(t *testing.T) { } func TestParseDiscriminatorsOrphanedChildWithNestedModel(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "/nestedtestdata/model_discriminators_orphaned_child_with_nested_model.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "/nestedtestdata/model_discriminators_orphaned_child_with_nested_model.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -1349,7 +1349,8 @@ func TestParseDiscriminatorsOrphanedChildWithNestedModel(t *testing.T) { } func TestParseDiscriminatorsOrphanedChildWithoutDiscriminatorValue(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "/nestedtestdata/model_discriminators_orphaned_child_without_discriminator_value.json") + // 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) } @@ -1411,7 +1412,7 @@ func TestParseDiscriminatorsOrphanedChildWithoutDiscriminatorValue(t *testing.T) } func TestParseDiscriminatorsOrphanedChildWithoutDiscriminatorValueForDifferentService(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "/nestedtestdata/model_discriminators_orphaned_child_without_discriminator_value.json") + 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) } diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models_test.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models_test.go index 8dc2492a799..62934a65772 100644 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models_test.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models_test.go @@ -14,7 +14,7 @@ import ( // TODO: tests for the different types of Object Definition func TestParseModelTopLevel(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_top_level.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_top_level.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -94,7 +94,7 @@ func TestParseModelTopLevel(t *testing.T) { } func TestParseModelTopLevelWithRawFile(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_top_level_with_rawfile.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_top_level_with_rawfile.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -124,7 +124,7 @@ func TestParseModelTopLevelWithRawFile(t *testing.T) { } func TestParseModelTopLevelWithInlinedModel(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_top_level_with_inlined_model.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_top_level_with_inlined_model.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -223,7 +223,7 @@ func TestParseModelTopLevelWithInlinedModel(t *testing.T) { } func TestParseModelWithDateTimeNoType(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_with_datetime_no_type.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_with_datetime_no_type.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -268,7 +268,7 @@ func TestParseModelWithDateTimeNoType(t *testing.T) { } func TestParseModelWithInlinedObject(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_with_inlined_object.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_with_inlined_object.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -362,7 +362,7 @@ func TestParseModelWithInlinedObject(t *testing.T) { } func TestParseModelWithNumberPrefixedField(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_with_number_prefixed_field.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_with_number_prefixed_field.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -410,7 +410,7 @@ func TestParseModelWithNumberPrefixedField(t *testing.T) { } func TestParseModelWithReference(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_with_reference.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_with_reference.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -492,7 +492,7 @@ func TestParseModelWithReference(t *testing.T) { } func TestParseModelWithReferenceToArray(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_with_reference_array.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_with_reference_array.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -560,7 +560,7 @@ func TestParseModelWithReferenceToArray(t *testing.T) { } func TestParseModelWithReferenceToConstant(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_with_reference_constant.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_with_reference_constant.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -640,7 +640,7 @@ func TestParseModelWithReferenceToConstant(t *testing.T) { } func TestParseModelWithReferenceToString(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_with_reference_string.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_with_reference_string.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -710,7 +710,7 @@ func TestParseModelWithReferenceToString(t *testing.T) { } func TestParseModelWithCircularReferences(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_with_circular_reference.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_with_circular_reference.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -811,7 +811,7 @@ func TestParseModelWithCircularReferences(t *testing.T) { } func TestParseModelInheritingFromObjectWithNoExtraFields(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_inheriting_from_other_model_no_new_fields.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_inheriting_from_other_model_no_new_fields.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -854,7 +854,7 @@ func TestParseModelInheritingFromObjectWithNoExtraFields(t *testing.T) { } func TestParseModelInheritingFromObjectWithNoExtraFieldsInlined(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_inheriting_from_other_model_no_new_fields_inlined.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_inheriting_from_other_model_no_new_fields_inlined.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -914,7 +914,7 @@ func TestParseModelInheritingFromObjectWithNoExtraFieldsInlined(t *testing.T) { } func TestParseModelInheritingFromObjectWithOnlyDescription(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_inheriting_from_other_model_with_only_description.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_inheriting_from_other_model_with_only_description.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -962,7 +962,7 @@ 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 := testhelpers.ParseSwaggerFileForTesting(t, "model_inheriting_from_other_model_with_properties_within_allof.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_inheriting_from_other_model_with_properties_within_allof.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -1006,7 +1006,7 @@ func TestParseModelInheritingFromObjectWithPropertiesWithinAllOf(t *testing.T) { } func TestParseModelContainingAllOfToTypeObject(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_containing_allof_object_type.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_containing_allof_object_type.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -1058,7 +1058,7 @@ func TestParseModelContainingAllOfToTypeObject(t *testing.T) { } func TestParseModelContainingAllOfToTypeObjectWithProperties(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_containing_allof_object_type_with_properties.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_containing_allof_object_type_with_properties.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -1110,7 +1110,7 @@ func TestParseModelContainingAllOfToTypeObjectWithProperties(t *testing.T) { } func TestParseModelContainingAllOfWithinProperties(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_containing_allof_within_properties.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_containing_allof_within_properties.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -1188,7 +1188,7 @@ func TestParseModelContainingAllOfWithinProperties(t *testing.T) { } func TestParseModelContainingMultipleAllOfWithinProperties(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_containing_allof_within_properties_multiple.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_containing_allof_within_properties_multiple.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -1278,7 +1278,7 @@ func TestParseModelContainingMultipleAllOfWithinProperties(t *testing.T) { } func TestParseModelContainingLists(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_containing_lists.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_containing_lists.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -1363,7 +1363,7 @@ func TestParseModelContainingLists(t *testing.T) { } func TestParseModelInlinedWithNoName(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_inlined_with_no_name.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_inlined_with_no_name.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -1419,7 +1419,7 @@ func TestParseModelInlinedWithNoName(t *testing.T) { } func TestParseModelInheritingFromParent(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_inheriting_from_parent.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_inheriting_from_parent.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -1492,7 +1492,7 @@ func TestParseModelInheritingFromParent(t *testing.T) { } func TestParseModelMultipleTopLevelModelsAndOperations(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_multiple_top_level_models_and_operations.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "model_multiple_top_level_models_and_operations.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -1600,7 +1600,7 @@ func TestParseModelMultipleTopLevelModelsAndOperations(t *testing.T) { } func TestParseModelBug2675DuplicateModel(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "models_bug_2675_duplicate_model.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "models_bug_2675_duplicate_model.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/operations_test.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/operations_test.go index 2ac62e246b8..77f03d6e166 100644 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/operations_test.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/operations_test.go @@ -15,7 +15,7 @@ import ( // TODO: tests for the different types of Operation Object Definition - including CSV's inner object func TestParseOperationsEmpty(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_empty.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_empty.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -27,7 +27,7 @@ func TestParseOperationsEmpty(t *testing.T) { } func TestParseOperationSingleWithTag(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_with_tag.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_with_tag.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -51,7 +51,7 @@ func TestParseOperationSingleWithTag(t *testing.T) { } func TestParseOperationSingleWithTagAndResourceId(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_with_tag_resource_id.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_with_tag_resource_id.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -89,7 +89,7 @@ func TestParseOperationSingleWithTagAndResourceId(t *testing.T) { } func TestParseOperationSingleWithTagAndResourceIdSuffix(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_with_tag_resource_id_suffix.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_with_tag_resource_id_suffix.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -128,7 +128,7 @@ func TestParseOperationSingleWithTagAndResourceIdSuffix(t *testing.T) { } func TestParseOperationSingleWithRequestObject(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_with_request_object.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_with_request_object.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -169,7 +169,7 @@ func TestParseOperationSingleWithRequestObject(t *testing.T) { } func TestParseOperationSingleWithRequestObjectInlined(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_with_request_object_inlined.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_with_request_object_inlined.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -210,7 +210,7 @@ func TestParseOperationSingleWithRequestObjectInlined(t *testing.T) { } func TestParseOperationSingleWithResponseObject(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_with_response_object.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_with_response_object.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -251,7 +251,7 @@ func TestParseOperationSingleWithResponseObject(t *testing.T) { } func TestParseOperationSingleWithResponseObjectInlined(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_with_response_object_inlined.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_with_response_object_inlined.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -292,7 +292,7 @@ func TestParseOperationSingleWithResponseObjectInlined(t *testing.T) { } func TestParseOperationSingleWithResponseObjectInlinedList(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_with_response_object_inlined_list.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_with_response_object_inlined_list.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -336,7 +336,7 @@ func TestParseOperationSingleWithResponseObjectInlinedList(t *testing.T) { } func TestParseOperationSingleRequestingWithABool(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_requesting_with_a_bool.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_requesting_with_a_bool.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -363,7 +363,7 @@ func TestParseOperationSingleRequestingWithABool(t *testing.T) { } func TestParseOperationSingleRequestingWithAInteger(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_requesting_with_a_int.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_requesting_with_a_int.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -390,7 +390,7 @@ func TestParseOperationSingleRequestingWithAInteger(t *testing.T) { } func TestParseOperationSingleRequestingWithADictionaryOfStrings(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_requesting_with_a_dictionary_of_strings.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_requesting_with_a_dictionary_of_strings.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -420,7 +420,7 @@ func TestParseOperationSingleRequestingWithADictionaryOfStrings(t *testing.T) { } func TestParseOperationSingleRequestingWithAListOfStrings(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_requesting_with_a_list_of_strings.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_requesting_with_a_list_of_strings.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -452,7 +452,7 @@ func TestParseOperationSingleRequestingWithAListOfStrings(t *testing.T) { // Models are already tested above func TestParseOperationSingleRequestingWithAString(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_requesting_with_a_string.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_requesting_with_a_string.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -479,7 +479,7 @@ func TestParseOperationSingleRequestingWithAString(t *testing.T) { } func TestParseOperationSingleReturningABool(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_returning_a_bool.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_returning_a_bool.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -506,7 +506,7 @@ func TestParseOperationSingleReturningABool(t *testing.T) { } func TestParseOperationSingleReturningAFloat(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_returning_a_float.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_returning_a_float.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -533,7 +533,7 @@ func TestParseOperationSingleReturningAFloat(t *testing.T) { } func TestParseOperationSingleReturningAFile(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_returning_a_file.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_returning_a_file.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -560,7 +560,7 @@ func TestParseOperationSingleReturningAFile(t *testing.T) { } func TestParseOperationSingleReturningAnInteger(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_returning_an_integer.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_returning_an_integer.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -587,7 +587,7 @@ func TestParseOperationSingleReturningAnInteger(t *testing.T) { } func TestParseOperationSingleReturningAString(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_returning_a_string.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_returning_a_string.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -615,7 +615,7 @@ func TestParseOperationSingleReturningAString(t *testing.T) { 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 := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_returning_an_error_status_code.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_returning_an_error_status_code.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -642,7 +642,7 @@ func TestParseOperationSingleReturningAnErrorStatusCode(t *testing.T) { } func TestParseOperationSingleReturningATopLevelRawObject(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_returning_a_top_level_raw_object.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_returning_a_top_level_raw_object.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -672,7 +672,7 @@ func TestParseOperationSingleReturningATopLevelRawObject(t *testing.T) { } func TestParseOperationSingleReturningADictionaryOfAModel(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_returning_a_dictionary_of_model.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_returning_a_dictionary_of_model.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -716,7 +716,7 @@ func TestParseOperationSingleReturningADictionaryOfAModel(t *testing.T) { } func TestParseOperationSingleReturningADictionaryOfStrings(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_returning_a_dictionary_of_strings.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_returning_a_dictionary_of_strings.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -746,7 +746,7 @@ func TestParseOperationSingleReturningADictionaryOfStrings(t *testing.T) { } func TestParseOperationSingleReturningAListOfIntegers(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_returning_a_list_of_ints.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_returning_a_list_of_ints.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -776,7 +776,7 @@ func TestParseOperationSingleReturningAListOfIntegers(t *testing.T) { } func TestParseOperationSingleReturningAListOfAModel(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_returning_a_list_of_model.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_returning_a_list_of_model.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -820,7 +820,7 @@ func TestParseOperationSingleReturningAListOfAModel(t *testing.T) { } func TestParseOperationSingleReturningAListOfStrings(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_returning_a_list_of_strings.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_returning_a_list_of_strings.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -850,7 +850,7 @@ func TestParseOperationSingleReturningAListOfStrings(t *testing.T) { } func TestParseOperationSingleReturningAListOfListOfAModel(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_returning_a_list_of_list_of_model.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_returning_a_list_of_list_of_model.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -897,7 +897,7 @@ func TestParseOperationSingleReturningAListOfListOfAModel(t *testing.T) { } func TestParseOperationSingleReturningAListOfListOfStrings(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_returning_a_list_of_list_of_strings.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_returning_a_list_of_list_of_strings.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -930,7 +930,7 @@ func TestParseOperationSingleReturningAListOfListOfStrings(t *testing.T) { } func TestParseOperationSingleWithList(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_list.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_list.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -974,7 +974,7 @@ func TestParseOperationSingleWithList(t *testing.T) { 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 := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_list_which_is_not_a_list.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_list_which_is_not_a_list.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -1020,7 +1020,7 @@ func TestParseOperationSingleWithListWhichIsNotAList(t *testing.T) { } func TestParseOperationSingleWithListReturningAListOfStrings(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_list_of_strings.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_list_of_strings.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -1050,7 +1050,7 @@ func TestParseOperationSingleWithListReturningAListOfStrings(t *testing.T) { 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 := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_list_without_pageable.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_list_without_pageable.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -1092,7 +1092,7 @@ func TestParseOperationSingleWithListWithoutPageable(t *testing.T) { } func TestParseOperationSingleWithLongRunningOperation(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_long_running.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_long_running.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -1134,7 +1134,7 @@ func TestParseOperationSingleWithLongRunningOperation(t *testing.T) { } func TestParseOperationSingleWithRequestAndResponseObject(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_with_request_and_response_object.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_with_request_and_response_object.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -1179,7 +1179,7 @@ func TestParseOperationSingleWithRequestAndResponseObject(t *testing.T) { } func TestParseOperationSingleWithMultipleTags(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_multiple_tags.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_multiple_tags.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -1215,7 +1215,7 @@ func TestParseOperationSingleWithMultipleTags(t *testing.T) { } func TestParseOperationSingleWithInferredTag(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_with_no_tag.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_with_no_tag.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -1241,7 +1241,7 @@ func TestParseOperationSingleWithInferredTag(t *testing.T) { } func TestParseOperationSingleWithHeaderOptions(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_with_header_options.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_with_header_options.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -1322,7 +1322,7 @@ func TestParseOperationSingleWithHeaderOptions(t *testing.T) { } func TestParseOperationSingleWithQueryStringOptions(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_with_querystring_options.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_with_querystring_options.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -1403,7 +1403,7 @@ func TestParseOperationSingleWithQueryStringOptions(t *testing.T) { } func TestParseOperationMultipleBasedOnTheSameResourceId(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_multiple_same_resource_id.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_multiple_same_resource_id.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -1448,7 +1448,7 @@ func TestParseOperationMultipleBasedOnTheSameResourceId(t *testing.T) { } func TestParseOperationsContainingContentTypes(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operation_content_types.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operation_content_types.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -1509,7 +1509,7 @@ func TestParseOperationsContainingContentTypes(t *testing.T) { } func TestParseOperationContainingMultipleReturnObjects(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_multiple_return_objects.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_multiple_return_objects.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -1550,7 +1550,7 @@ func TestParseOperationContainingMultipleReturnObjects(t *testing.T) { } func TestParseOperationsWithStutteringNames(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_with_stuttering_names.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_with_stuttering_names.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } 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..21b6815ab9f --- /dev/null +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_discriminated.go @@ -0,0 +1,69 @@ +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.SwaggerSpecRaw.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 + 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_test.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_resource_ids_test.go index 632c2d4182a..3afae76e38b 100644 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_resource_ids_test.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_resource_ids_test.go @@ -12,7 +12,7 @@ import ( ) func TestParseResourceIdBasic(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "resource_ids_basic.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "resource_ids_basic.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -50,7 +50,7 @@ func TestParseResourceIdBasic(t *testing.T) { } func TestParseResourceIdContainingAConstant(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "resource_ids_containing_constant.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "resource_ids_containing_constant.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -96,7 +96,7 @@ func TestParseResourceIdContainingAConstant(t *testing.T) { } func TestParseResourceIdContainingAScope(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "resource_ids_containing_scope.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "resource_ids_containing_scope.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -140,7 +140,7 @@ func TestParseResourceIdContainingAHiddenScope(t *testing.T) { t.Run(file, func(t *testing.T) { fileName := file - actual, err := testhelpers.ParseSwaggerFileForTesting(t, fileName) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, fileName, nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -175,7 +175,7 @@ func TestParseResourceIdContainingAHiddenScope(t *testing.T) { func TestParseResourceIdContainingAHiddenScopeWithExtraSegment(t *testing.T) { // The extra segment should be ignored and detected as a regular scope - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "resource_ids_containing_hidden_scope_with_extra_segment.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "resource_ids_containing_hidden_scope_with_extra_segment.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -207,7 +207,7 @@ func TestParseResourceIdContainingAHiddenScopeWithExtraSegment(t *testing.T) { } func TestParseResourceIdContainingAHiddenScopeWithSuffix(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "resource_ids_containing_hidden_scope_with_suffix.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "resource_ids_containing_hidden_scope_with_suffix.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -249,7 +249,7 @@ func TestParseResourceIdContainingAHiddenScopeNested(t *testing.T) { t.Run(file, func(t *testing.T) { fileName := file - actual, err := testhelpers.ParseSwaggerFileForTesting(t, fileName) + actual, err := testhelpers.ParseSwaggerFileForTesting(t, fileName, nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -283,7 +283,7 @@ func TestParseResourceIdContainingAHiddenScopeNested(t *testing.T) { } func TestParseResourceIdContainingAHiddenScopeNestedWithExtraSegment(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "resource_ids_containing_hidden_scope_nested_with_extra_segment.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "resource_ids_containing_hidden_scope_nested_with_extra_segment.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -315,7 +315,7 @@ func TestParseResourceIdContainingAHiddenScopeNestedWithExtraSegment(t *testing. } func TestParseResourceIdContainingAHiddenScopeNestedWithSuffix(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "resource_ids_containing_hidden_scope_nested_with_suffix.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "resource_ids_containing_hidden_scope_nested_with_suffix.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -348,7 +348,7 @@ func TestParseResourceIdContainingAHiddenScopeNestedWithSuffix(t *testing.T) { } func TestParseResourceIdWithJustUriSuffix(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "resource_ids_with_just_suffix.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "resource_ids_with_just_suffix.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -372,7 +372,7 @@ func TestParseResourceIdWithJustUriSuffix(t *testing.T) { } func TestParseResourceIdWithResourceIdAndUriSuffix(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "resource_ids_with_suffix.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "resource_ids_with_suffix.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -411,7 +411,7 @@ func TestParseResourceIdWithResourceIdAndUriSuffix(t *testing.T) { } func TestParseResourceIdWithResourceIdAndUriSuffixForMultipleUris(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "resource_ids_with_suffix_multiple_uris.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "resource_ids_with_suffix_multiple_uris.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -463,7 +463,7 @@ func TestParseResourceIdWithResourceIdAndUriSuffixForMultipleUris(t *testing.T) } func TestParseResourceIdContainingResourceProviderShouldGetTitleCased(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "resource_ids_lowercased_resource_provider.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "resource_ids_lowercased_resource_provider.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -501,7 +501,7 @@ func TestParseResourceIdContainingResourceProviderShouldGetTitleCased(t *testing } func TestParseResourceIdContainingTheSameResourceIdWithDifferentSegments(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "resource_ids_same_id_different_segment_casing.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "resource_ids_same_id_different_segment_casing.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -546,7 +546,7 @@ func TestParseResourceIdContainingTheSameResourceIdWithDifferentSegments(t *test } func TestParseResourceIdContainingTheSegmentsNamedTheSame(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "resource_ids_multiple_segments_same_name.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "resource_ids_multiple_segments_same_name.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -598,7 +598,7 @@ func TestParseResourceIdsWhereTheSameUriContainsDifferentConstantValuesPerOperat for i := 0; i < 100; i++ { t.Logf("iteration %d", i) - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "resource_ids_same_uri_different_constant_values_per_operation.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "resource_ids_same_uri_different_constant_values_per_operation.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -658,7 +658,7 @@ func TestParseResourceIdsWhereTheSameUriContainsDifferentConstantValuesPerOperat } func TestParseResourceIdsCommon(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "resource_ids_common.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "resource_ids_common.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_supplementary_data_test.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_supplementary_data_test.go index e5bb8e5fcd7..fb6645cf351 100644 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_supplementary_data_test.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_supplementary_data_test.go @@ -1,13 +1,13 @@ package parser_test import ( - "github.com/hashicorp/go-azure-helpers/lang/pointer" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testhelpers" - discoveryModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/discovery/models" "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/parser/testhelpers" + discoveryModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/discovery/models" ) func TestParseSupplementaryData(t *testing.T) { 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 index 92358d9656f..5beabdac43c 100644 --- 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 @@ -2,7 +2,6 @@ package parsingcontext import ( "fmt" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/logging" "strings" "github.com/go-openapi/spec" @@ -11,6 +10,7 @@ import ( "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 { @@ -88,7 +88,7 @@ func (c *Context) FindNestedItemsYetToBeParsed(operations map[string]sdkModels.S } result.Append(known) - // Now that we have a complete list of all of the nested items to find, loop around and find them + // 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) 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 index 7749b387dd1..bb84c1ee570 100644 --- 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 @@ -431,62 +431,3 @@ func (c *Context) FindAncestorType(input spec.Schema) (*string, *string, error) } return nil, nil, nil } - -func (c *Context) findOrphanedDiscriminatedModels(serviceName string) (*parserModels.ParseResult, error) { - result := parserModels.ParseResult{ - Constants: map[string]sdkModels.SDKConstant{}, - Models: map[string]sdkModels.SDKModel{}, - } - - for modelName, definition := range c.SwaggerSpecRaw.Definitions { - if _, ok := definition.Extensions.GetString("x-ms-discriminator-value"); ok { - details, err := c.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 := c.FindAncestorType(definition) - if err != nil { - return nil, fmt.Errorf("determining ancestor type for model %q: %+v", modelName, err) - } - - details, err := c.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 := c.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/swagger_tags_test.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/swagger_tags_test.go index 5d24a9cc3d4..61bdd746b2c 100644 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/swagger_tags_test.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/swagger_tags_test.go @@ -12,7 +12,7 @@ import ( ) func TestParsingOperationsUsingTheSameSwaggerTagInDifferentCasings(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_tag_different_casing.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_single_tag_different_casing.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } @@ -86,7 +86,7 @@ func TestParsingOperationsUsingTheSameSwaggerTagInDifferentCasings(t *testing.T) } func TestParsingOperationsOnResources(t *testing.T) { - actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_on_resources.json") + actual, err := testhelpers.ParseSwaggerFileForTesting(t, "operations_on_resources.json", nil) if err != nil { t.Fatalf("parsing: %+v", err) } 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 index 2d83ef2ba2c..d00f0930d3f 100644 --- 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 @@ -4,14 +4,18 @@ 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) (*sdkModels.APIVersion, error) { +func ParseSwaggerFileForTesting(t *testing.T, filePath string, serviceName *string) (*sdkModels.APIVersion, error) { + if serviceName == nil { + serviceName = pointer.To("Example") + } input := discoveryModels.AvailableDataSet{ - ServiceName: "Example", + ServiceName: *serviceName, DataSetsForAPIVersions: map[string]discoveryModels.AvailableDataSetForAPIVersion{ "2020-01-01": { APIVersion: "2020-01-01", diff --git a/tools/importer-rest-api-specs/internal/components/data_test.go b/tools/importer-rest-api-specs/internal/components/data_test.go index 681eacd3a0e..f585875b6dc 100644 --- a/tools/importer-rest-api-specs/internal/components/data_test.go +++ b/tools/importer-rest-api-specs/internal/components/data_test.go @@ -2,13 +2,13 @@ package components import ( "fmt" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/terraform" "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" From 441261609ed80e6107ac0e848e527399d49f46b0 Mon Sep 17 00:00:00 2001 From: Tom Bamford Date: Wed, 17 Jul 2024 11:51:24 +0100 Subject: [PATCH 34/58] `importer-rest-api-specs`: shelve SupplementaryData support for now, since it doesn't seem to be required for DataFactory models as they use external Swagger Refs. --- .../apidefinitions/parse_api_version.go | 2 +- .../apidefinitions/parse_service.go | 2 +- .../parse_supplementary_data.go | 38 ++++------ .../parser/models_discriminators_test.go | 75 +++++++++++++++++++ .../parser/parse_supplementary_data.go | 6 +- .../parser/parse_supplementary_data_test.go | 57 -------------- ...discriminators_ref_from_another_spec.json} | 17 +---- ...l_discriminators_ref_in_another_spec.json} | 18 ++++- 8 files changed, 116 insertions(+), 99 deletions(-) delete mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_supplementary_data_test.go rename tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/{supplementary_data_parent.json => model_discriminators_ref_from_another_spec.json} (70%) rename tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/{supplementary_data_implementations.json => model_discriminators_ref_in_another_spec.json} (64%) 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 index de2229f2e3d..d3c49b5310c 100644 --- 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 @@ -45,7 +45,7 @@ func parseAPIVersion(serviceName string, input discoveryModels.AvailableDataSetF logging.Tracef("Processing API Definitions from file %q..", filePath) resources, err := parseAPIResourcesFromFile(filePath, serviceName, resourceProvider, apiResources, foundResourceIDs) if err != nil { - return nil, fmt.Errorf("parsing the APIResources from the Supplementary Data within %q: %+v", filePath, err) + return nil, fmt.Errorf("parsing the APIResources from the API Definitions within %q: %+v", filePath, err) } logging.Tracef("There are now %d APIResources", len(*resources)) 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 index 6a4c46ea96b..8d17bff7407 100644 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parse_service.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parse_service.go @@ -5,9 +5,9 @@ package apidefinitions import ( "fmt" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/ignore" 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" ) diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parse_supplementary_data.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parse_supplementary_data.go index 2eac14db824..64aed08e76d 100644 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parse_supplementary_data.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parse_supplementary_data.go @@ -1,26 +1,18 @@ package apidefinitions -import ( - "fmt" - - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser" - 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" -) - // parseSupplementaryDataFromFile loads any available Supplementary Data from the file in question -func parseSupplementaryDataFromFile(filePath string) (*parserModels.SupplementaryData, error) { - logging.Tracef("Building an Definitions Parser for %q..", filePath) - parser, err := parser.NewAPIDefinitionsParser(filePath) - if err != nil { - return nil, fmt.Errorf("building the APIDefinitions Parser for %q: %+v", filePath, err) - } - logging.Tracef("Parsing the Supplementary Data from %q..", filePath) - data, err := parser.SupplementaryData() - if err != nil { - return nil, fmt.Errorf("parsing the Supplementary Data from %q: %+v", filePath, err) - } - logging.Tracef("Loaded the Supplementary Data from %q.", filePath) - - return data, nil -} +//func parseSupplementaryDataFromFile(filePath string) (*parserModels.SupplementaryData, error) { +// logging.Tracef("Building an Definitions Parser for %q..", filePath) +// parser, err := parser.NewAPIDefinitionsParser(filePath) +// if err != nil { +// return nil, fmt.Errorf("building the APIDefinitions Parser for %q: %+v", filePath, err) +// } +// logging.Tracef("Parsing the Supplementary Data from %q..", filePath) +// data, err := parser.SupplementaryData() +// if err != nil { +// return nil, fmt.Errorf("parsing the Supplementary Data from %q: %+v", filePath, err) +// } +// logging.Tracef("Loaded the Supplementary Data from %q.", filePath) +// +// return data, nil +//} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models_discriminators_test.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models_discriminators_test.go index ae496c23c7d..af1aa4511ef 100644 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models_discriminators_test.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models_discriminators_test.go @@ -1411,6 +1411,81 @@ func TestParseDiscriminatorsOrphanedChildWithoutDiscriminatorValue(t *testing.T) 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 := testhelpers.ParseSwaggerFileForTesting(t, "/nestedtestdata/model_discriminators_orphaned_child_without_discriminator_value.json", pointer.To("Compute")) if err != nil { diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_supplementary_data.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_supplementary_data.go index 37630c8b83b..f048ce280c7 100644 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_supplementary_data.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_supplementary_data.go @@ -8,6 +8,10 @@ import ( parserModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models" ) +// Deprecated: SupplementaryData is unused at this time, but being left in as a potential requirement. Note that this +// doesn't seem to be needed at this time for DataFactory, which uses external swagger refs - since support for this is +// built into the go-openapi module. +// TODO: @manicminer revisit and determine whether we'll implement this func (p *apiDefinitionsParser) SupplementaryData() (*parserModels.SupplementaryData, error) { result := parserModels.SupplementaryData{ Constants: map[string]sdkModels.SDKConstant{}, @@ -36,7 +40,7 @@ func (p *apiDefinitionsParser) SupplementaryData() (*parserModels.SupplementaryD } } - // FindNestedItemsYetToBeParsed takes ParseResult and so we need to shim this across + // FindNestedItemsYetToBeParsed takes ParseResult, so we need to shim this across shim := parserModels.ParseResult{ Constants: result.Constants, Models: result.Models, diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_supplementary_data_test.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_supplementary_data_test.go deleted file mode 100644 index fb6645cf351..00000000000 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_supplementary_data_test.go +++ /dev/null @@ -1,57 +0,0 @@ -package parser_test - -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/parser/testhelpers" - discoveryModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/discovery/models" -) - -func TestParseSupplementaryData(t *testing.T) { - dataSet := discoveryModels.AvailableDataSet{ - ServiceName: "Example", - DataSetsForAPIVersions: map[string]discoveryModels.AvailableDataSetForAPIVersion{ - "2020-01-01": { - APIVersion: "2020-01-01", - ContainsStableAPIVersion: true, - FilePathsContainingAPIDefinitions: []string{ - filepath.Join("testdata", "supplementary_data_parent.json"), - }, - FilePathsContainingSupplementaryData: []string{ - filepath.Join("testdata", "supplementary_data_implementations.json"), - }, - }, - }, - ResourceProvider: nil, - } - actual, err := testhelpers.ParseDataSetForTesting(t, dataSet, "2020-01-01") - if err != nil { - t.Fatalf(err.Error()) - } - expected := sdkModels.APIVersion{ - APIVersion: "2020-01-01", - Resources: map[string]sdkModels.APIResource{ - "Example": { - Constants: make(map[string]sdkModels.SDKConstant), - Models: map[string]sdkModels.SDKModel{ - "Cat": {}, - "ParentType": {}, - }, - Name: "Example", - Operations: map[string]sdkModels.SDKOperation{ - "Test": { - ResponseObject: &sdkModels.SDKObjectDefinition{ - Type: sdkModels.ReferenceSDKObjectDefinitionType, - ReferenceName: pointer.To("ParentType"), - }, - }, - }, - ResourceIDs: map[string]sdkModels.ResourceID{}, - }, - }, - } - testhelpers.ValidateParsedSwaggerResultMatches(t, expected, actual) -} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/supplementary_data_parent.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_discriminators_ref_from_another_spec.json similarity index 70% rename from tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/supplementary_data_parent.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_discriminators_ref_from_another_spec.json index 8fa4340aae7..5c45ec0e49e 100644 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/supplementary_data_parent.json +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_discriminators_ref_from_another_spec.json @@ -1,7 +1,7 @@ { "swagger": "2.0", "info": { - "title": "Example", + "title": "DataFactory", "description": "Example", "version": "2020-01-01" }, @@ -30,24 +30,13 @@ "200": { "description": "Success.", "schema": { - "$ref": "#/definitions/ParentType" + "$ref": "./model_discriminators_ref_in_another_spec.json#/definitions/Animal" } } } } } }, - "definitions": { - "ParentType": { - "discriminator": "typeName", - "properties": { - "typeName": { - "type": "string" - } - }, - "type": "object", - "title": "ParentType" - } - }, + "definitions": {}, "parameters": {} } \ No newline at end of file diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/supplementary_data_implementations.json b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_discriminators_ref_in_another_spec.json similarity index 64% rename from tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/supplementary_data_implementations.json rename to tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_discriminators_ref_in_another_spec.json index ae7e1f676c7..4a5b6db19e7 100644 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/supplementary_data_implementations.json +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/testdata/model_discriminators_ref_in_another_spec.json @@ -19,11 +19,25 @@ "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/ParentType" + "$ref": "#/definitions/Animal" } ], "properties": { @@ -38,7 +52,7 @@ ], "title": "Cat", "type": "object", - "x-ms-discriminator-value": "cat" + "x-ms-discriminator-value": "Cat" } }, "parameters": {} From 8526060783bee9ecf7bc5996c485211b1aba7bb0 Mon Sep 17 00:00:00 2001 From: Tom Bamford Date: Wed, 17 Jul 2024 13:32:49 +0100 Subject: [PATCH 35/58] `importer-rest-api-specs`: implement `-services` argument --- .../internal/pipeline/import.go | 17 ++- .../internal/pipeline/messages.go | 122 ++++++++++++++++++ 2 files changed, 138 insertions(+), 1 deletion(-) create mode 100644 tools/importer-rest-api-specs/internal/pipeline/messages.go diff --git a/tools/importer-rest-api-specs/internal/pipeline/import.go b/tools/importer-rest-api-specs/internal/pipeline/import.go index 28253ab8d2f..2958433e75e 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, @@ -47,6 +48,20 @@ func RunImporter(opts Options) error { logging.Infof("Processing the %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) +} From b585e8414198f04ff77ec0c07bb3bd6523eaedc0 Mon Sep 17 00:00:00 2001 From: Tom Bamford Date: Wed, 17 Jul 2024 19:32:16 +0100 Subject: [PATCH 36/58] `importer-rest-api-specs`: operate on maps instead of passing them around --- .../apidefinitions/parse_api_resource.go | 41 ++++--------------- .../apidefinitions/parse_api_version.go | 14 +++++-- .../parser/cleanup/normalize_sdk_objects.go | 3 +- .../parser/combine/api_resource.go | 8 +--- .../parser/combine/api_resources.go | 37 +++++++---------- .../parser/combine/operations.go | 23 ++++------- .../parser/combine/resource_ids.go | 22 ++++------ .../parser/parse_discriminated.go | 1 + .../parser/parse_swagger_tag.go | 2 +- .../parser/parsingcontext/helpers.go | 4 +- .../parser/parsingcontext/parse_model.go | 8 ++-- 11 files changed, 60 insertions(+), 103 deletions(-) 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 index 48c2c1e17d7..568cf4e78f2 100644 --- 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 @@ -12,7 +12,7 @@ import ( "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/logging" ) -func parseAPIResourcesFromFile(filePath, serviceName string, resourceProvider *string, existingAPIResources map[string]sdkModels.APIResource, resourceIds resourceids.ParseResult) (*map[string]sdkModels.APIResource, error) { +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) @@ -22,7 +22,6 @@ func parseAPIResourcesFromFile(filePath, serviceName string, resourceProvider *s tags := parser.ParseSwaggerTags() // 2. Then iterate over each of the Swagger Operations with each Tag - parsedAPIResources := existingAPIResources for _, tag := range tags { if ignore.SwaggerTag(tag) { continue @@ -32,13 +31,7 @@ func parseAPIResourcesFromFile(filePath, serviceName string, resourceProvider *s normalizedTag = cleanup.NormalizeResourceName(normalizedTag) // pass in any existing/known data so that we can reuse the models/references - existing := sdkModels.APIResource{ - Constants: make(map[string]sdkModels.SDKConstant), - Models: make(map[string]sdkModels.SDKModel), - Name: normalizedTag, - Operations: make(map[string]sdkModels.SDKOperation), - ResourceIDs: make(map[string]sdkModels.ResourceID), - } + existing := sdkModels.NewAPIResource(normalizedTag) if v, ok := parsedAPIResources[normalizedTag]; ok { existing = v } @@ -53,11 +46,9 @@ func parseAPIResourcesFromFile(filePath, serviceName string, resourceProvider *s discoveredResources := map[string]sdkModels.APIResource{ normalizedTag: *resource, } - combined, err := combine.APIResourcesWith(parsedAPIResources, discoveredResources) - if err != nil { + if err = combine.APIResourcesWith(parsedAPIResources, discoveredResources); err != nil { return nil, fmt.Errorf("combining the APIResources for the identified Swagger Tag %q: %+v", tag, err) } - parsedAPIResources = *combined } } @@ -66,13 +57,7 @@ func parseAPIResourcesFromFile(filePath, serviceName string, resourceProvider *s inferredTag := cleanup.InferTagFromFilename(filePath) // pass in any existing/known data so that we can reuse the models/references - existing := sdkModels.APIResource{ - Constants: make(map[string]sdkModels.SDKConstant), - Models: make(map[string]sdkModels.SDKModel), - Name: inferredTag, - Operations: make(map[string]sdkModels.SDKOperation), - ResourceIDs: make(map[string]sdkModels.ResourceID), - } + existing := sdkModels.NewAPIResource(inferredTag) if v, ok := parsedAPIResources[inferredTag]; ok { existing = v } @@ -86,11 +71,9 @@ func parseAPIResourcesFromFile(filePath, serviceName string, resourceProvider *s discoveredResources := map[string]sdkModels.APIResource{ inferredTag: *resource, } - combined, err := combine.APIResourcesWith(parsedAPIResources, discoveredResources) - if err != nil { + if err = combine.APIResourcesWith(parsedAPIResources, discoveredResources); err != nil { return nil, fmt.Errorf("combining the APIResources for the inferred Swagger Tag %q: %+v", inferredTag, err) } - parsedAPIResources = *combined } } @@ -117,21 +100,11 @@ func parseAPIResourcesFromFile(filePath, serviceName string, resourceProvider *s discoveredResources := map[string]sdkModels.APIResource{ inferredTag: resource, } - combined, err := combine.APIResourcesWith(parsedAPIResources, discoveredResources) - if err != nil { + if err = combine.APIResourcesWith(parsedAPIResources, discoveredResources); err != nil { return nil, fmt.Errorf("combining the APIResources for the inferred Swagger Tag %q: %+v", inferredTag, err) } - parsedAPIResources = *combined } } - // 4. Now that we have a canonical list of resources - can we simplify the Operation names at all? - simplifiedAPIDefinitions := make(map[string]sdkModels.APIResource) - for resourceName, resource := range parsedAPIResources { - logging.Tracef("Simplifying operation names for resource %q", resourceName) - updated := cleanup.SimplifyOperationNamesForAPIResource(resourceName, resource) - simplifiedAPIDefinitions[resourceName] = updated - } - - return &simplifiedAPIDefinitions, nil + return parsedAPIResources, nil } 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 index d3c49b5310c..ca3d779a161 100644 --- 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 @@ -43,16 +43,22 @@ func parseAPIVersion(serviceName string, input discoveryModels.AvailableDataSetF apiResources := make(map[string]sdkModels.APIResource) for _, filePath := range input.FilePathsContainingAPIDefinitions { logging.Tracef("Processing API Definitions from file %q..", filePath) - resources, err := parseAPIResourcesFromFile(filePath, serviceName, resourceProvider, apiResources, foundResourceIDs) - if err != nil { + 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(*resources)) - apiResources = *resources + logging.Tracef("There are now %d APIResources", len(apiResources)) logging.Tracef("Processing API Definitions from file %q - Completed.", filePath) } + // 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, diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/normalize_sdk_objects.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/normalize_sdk_objects.go index f1601a58f65..e76acc801ec 100644 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/normalize_sdk_objects.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/normalize_sdk_objects.go @@ -4,9 +4,10 @@ package cleanup import ( + "strings" + "github.com/hashicorp/go-azure-helpers/lang/pointer" sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" - "strings" ) // NormalizeAPIResource works through the parsed AzureApiResource and ensures 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 index 712a24aed26..e9d7197a9ed 100644 --- 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 @@ -22,17 +22,13 @@ func APIResource(first, other sdkModels.APIResource) (*sdkModels.APIResource, er } first.Models = *models - combinedOperations, err := operations(first.Operations, other.Operations) - if err != nil { + if err = combineOperations(first.Operations, other.Operations); err != nil { return nil, fmt.Errorf("combining operations: %+v", err) } - first.Operations = *combinedOperations - combinedResourceIDs, err := resourceIds(first.ResourceIDs, other.ResourceIDs) - if err != nil { + if err = combineResourceIds(first.ResourceIDs, other.ResourceIDs); err != nil { return nil, fmt.Errorf("combining resource ids: %+v", err) } - first.ResourceIDs = *combinedResourceIDs 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 index 0d1f93a046a..713d267a798 100644 --- 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 @@ -9,45 +9,36 @@ import ( sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" ) -func APIResourcesWith(first, other map[string]sdkModels.APIResource) (*map[string]sdkModels.APIResource, error) { - resources := make(map[string]sdkModels.APIResource) - for k, v := range first { - resources[k] = v - } - +func APIResourcesWith(resources, other map[string]sdkModels.APIResource) error { for k, v := range other { - existing, ok := resources[k] + resource, ok := resources[k] if !ok { resources[k] = v continue } - constants, err := Constants(existing.Constants, v.Constants) + constants, err := Constants(resource.Constants, v.Constants) if err != nil { - return nil, fmt.Errorf("combining constants: %+v", err) + return fmt.Errorf("combining constants: %+v", err) } - existing.Constants = *constants + resource.Constants = *constants - models, err := Models(existing.Models, v.Models) + models, err := Models(resource.Models, v.Models) if err != nil { - return nil, fmt.Errorf("combining models: %+v", err) + return fmt.Errorf("combining models: %+v", err) } - existing.Models = *models + resource.Models = *models - operations, err := operations(existing.Operations, v.Operations) - if err != nil { - return nil, fmt.Errorf("combining operations: %+v", err) + if err = combineOperations(resource.Operations, v.Operations); err != nil { + return fmt.Errorf("combining operations: %+v", err) } - existing.Operations = *operations - resourceIds, err := resourceIds(existing.ResourceIDs, v.ResourceIDs) - if err != nil { - return nil, fmt.Errorf("combining resource ids: %+v", err) + if err = combineResourceIds(resource.ResourceIDs, v.ResourceIDs); err != nil { + return fmt.Errorf("combining resource ids: %+v", err) } - existing.ResourceIDs = *resourceIds - resources[k] = existing + resources[k] = resource } - return &resources, nil + return 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 index 5e570574601..2c1fc23b557 100644 --- 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 @@ -5,29 +5,24 @@ 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 operations(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 - other, ok := output[k] +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 { - output[k] = v + operations[k] = v continue } - if ok, err := comparison.OperationsMatch(v, other); !ok { - return nil, fmt.Errorf("differing Operations named %q. First: %+v / Second %+v / Error: %+v", k, v, other, err) + 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 &output, nil + 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 index 108f9d18e73..4a65206c9d2 100644 --- 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 @@ -11,22 +11,16 @@ import ( "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/comparison" ) -func resourceIds(first, 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 && !comparison.ResourceIDsMatch(v, otherVal) { - return nil, fmt.Errorf("duplicate Resource ID named %q (First %q / Second %q)", k, sdkHelpers.DisplayValueForResourceID(v), sdkHelpers.DisplayValueForResourceID(otherVal)) +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)) } - output[k] = v + resourceIds[k] = v } - return &output, nil + return nil } 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 index 21b6815ab9f..529ba0a61e8 100644 --- 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 @@ -28,6 +28,7 @@ func (p *apiDefinitionsParser) FindOrphanedDiscriminatedModels(serviceName strin // 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 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 index f32b256b2e0..876bad6b377 100644 --- 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 @@ -61,7 +61,7 @@ func (p *apiDefinitionsParser) ParseAPIResourceWithinSwaggerTag(tag, resourcePro // then switch out any Common Schema Types (e.g. Identity) resource = commonschema.ReplaceSDKObjectDefinitionsAsNeeded(resource) - // first Normalize the names, meaning `foo` -> `Foo` for consistency + // Normalize the names, ensuring they are appropriately Pascal cased resource = cleanup.NormalizeAPIResource(resource) return &resource, nil 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 index 5beabdac43c..3e80dbc2294 100644 --- 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 @@ -132,7 +132,7 @@ func (c *Context) FindNestedItemsYetToBeParsed(operations map[string]sdkModels.S } func (c *Context) determineObjectsRequiredButNotParsed(operations map[string]sdkModels.SDKOperation, known parserModels.ParseResult) (*[]string, error) { - referencesToFind := make(map[string]struct{}, 0) + referencesToFind := make(map[string]struct{}) for _, operation := range operations { if operation.RequestObject != nil { @@ -166,7 +166,7 @@ func (c *Context) determineObjectsRequiredButNotParsed(operations map[string]sdk } if isKnownModel { - // if it's a model, we need to check all of the fields for this to find any constant or models + // 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] 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 index bb84c1ee570..3ab3fac71ea 100644 --- 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 @@ -2,14 +2,14 @@ package parsingcontext import ( "fmt" - "github.com/hashicorp/go-azure-helpers/lang/pointer" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/constants" - "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/logging" "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/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) { @@ -43,7 +43,7 @@ func (c *Context) ParseModel(name string, input spec.Schema) (*parserModels.Pars } // 3. finally build this model directly - // Notably, we **DO NOT** load models used by this models here - this is handled once we + // 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 { From 6507fde2d60e3f497fd7d8258be5af248b03fe86 Mon Sep 17 00:00:00 2001 From: Tom Bamford Date: Thu, 18 Jul 2024 23:36:03 +0100 Subject: [PATCH 37/58] `importer-rest-api-specs`: normalize swagger tag names to prevent duplicates (avoid parsing the same tag twice) --- .../apidefinitions/parser/parse_swagger_tags.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) 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 index 2ca9e650917..ca80b73b859 100644 --- 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 @@ -5,24 +5,26 @@ package parser import ( "sort" - "strings" + + "golang.org/x/text/cases" + "golang.org/x/text/language" ) func (p *apiDefinitionsParser) ParseSwaggerTags() []string { tags := make(map[string]struct{}) - // first we go through, assuming there are tags for _, operation := range p.context.SwaggerSpecExpanded.Operations() { for _, details := range operation { for _, tag := range details.Tags { - tags[tag] = struct{}{} + normalizedTag := cases.Title(language.AmericanEnglish, cases.NoLower).String(tag) + tags[normalizedTag] = struct{}{} } } } out := make([]string, 0) for key := range tags { - out = append(out, strings.Title(key)) + out = append(out, key) } sort.Strings(out) return out From 170e4906703b329c625b2ad968b787f3292de254 Mon Sep 17 00:00:00 2001 From: Tom Bamford Date: Thu, 18 Jul 2024 23:37:09 +0100 Subject: [PATCH 38/58] `importer-rest-api-specs`: normalize API resources after parsing all of them --- .../components/apidefinitions/parse_api_resource.go | 3 +-- .../components/apidefinitions/parse_api_version.go | 8 ++++++++ .../components/apidefinitions/parser/parse_swagger_tag.go | 4 ---- tools/importer-rest-api-specs/internal/pipeline/import.go | 2 +- 4 files changed, 10 insertions(+), 7 deletions(-) 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 index 568cf4e78f2..470f5c92cf3 100644 --- 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 @@ -77,7 +77,7 @@ func parseAPIResourcesFromFile(filePath, serviceName string, resourceProvider *s } } - // 3. Discriminator implementations that are defined in separate files with no link to a swagger tag + // 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 { @@ -95,7 +95,6 @@ func parseAPIResourcesFromFile(filePath, serviceName string, resourceProvider *s Constants: result.Constants, Models: result.Models, } - resource = cleanup.NormalizeAPIResource(resource) discoveredResources := map[string]sdkModels.APIResource{ inferredTag: resource, 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 index ca3d779a161..7c00de49bbf 100644 --- 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 @@ -52,6 +52,14 @@ func parseAPIVersion(serviceName string, input discoveryModels.AvailableDataSetF 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) 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 index 876bad6b377..84c7f4810a5 100644 --- 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 @@ -4,7 +4,6 @@ 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/cleanup" "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" @@ -61,8 +60,5 @@ func (p *apiDefinitionsParser) ParseAPIResourceWithinSwaggerTag(tag, resourcePro // then switch out any Common Schema Types (e.g. Identity) resource = commonschema.ReplaceSDKObjectDefinitionsAsNeeded(resource) - // Normalize the names, ensuring they are appropriately Pascal cased - resource = cleanup.NormalizeAPIResource(resource) - return &resource, nil } diff --git a/tools/importer-rest-api-specs/internal/pipeline/import.go b/tools/importer-rest-api-specs/internal/pipeline/import.go index 2958433e75e..552cbc49fd6 100644 --- a/tools/importer-rest-api-specs/internal/pipeline/import.go +++ b/tools/importer-rest-api-specs/internal/pipeline/import.go @@ -46,7 +46,7 @@ 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 From 42b42448915affc453d4cdb1b296c0705922c4b4 Mon Sep 17 00:00:00 2001 From: Tom Bamford Date: Fri, 19 Jul 2024 00:10:15 +0100 Subject: [PATCH 39/58] `importer-rest-api-specs`: ensure new APIResource is populated --- .../apidefinitions/parse_api_resource.go | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) 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 index 470f5c92cf3..a8135df4a6a 100644 --- 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 @@ -31,7 +31,7 @@ func parseAPIResourcesFromFile(filePath, serviceName string, resourceProvider *s normalizedTag = cleanup.NormalizeResourceName(normalizedTag) // pass in any existing/known data so that we can reuse the models/references - existing := sdkModels.NewAPIResource(normalizedTag) + existing := newAPIResource(normalizedTag) if v, ok := parsedAPIResources[normalizedTag]; ok { existing = v } @@ -57,7 +57,7 @@ func parseAPIResourcesFromFile(filePath, serviceName string, resourceProvider *s inferredTag := cleanup.InferTagFromFilename(filePath) // pass in any existing/known data so that we can reuse the models/references - existing := sdkModels.NewAPIResource(inferredTag) + existing := newAPIResource(inferredTag) if v, ok := parsedAPIResources[inferredTag]; ok { existing = v } @@ -107,3 +107,13 @@ func parseAPIResourcesFromFile(filePath, serviceName string, resourceProvider *s 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), + } +} From 9d49558726d58ff52f4142b09a04584edd182448 Mon Sep 17 00:00:00 2001 From: Tom Bamford Date: Fri, 19 Jul 2024 01:00:08 +0100 Subject: [PATCH 40/58] `importer-rest-api-specs`: maps are pointers --- .../parser/combine/api_resource.go | 4 +-- .../parser/combine/api_resources.go | 4 +-- .../parser/combine/constants.go | 6 ++-- .../apidefinitions/parser/combine/maps.go | 4 +-- .../apidefinitions/parser/combine/models.go | 4 +-- .../apidefinitions/parser/constants/parse.go | 6 ++-- .../parser/models/supplementary_data.go | 6 ++-- .../apidefinitions/parser/operation/list.go | 4 +-- .../parser/operation/options.go | 4 +-- .../parser/operation/parse_operation.go | 2 +- .../parser/operation/parse_operations.go | 4 +-- .../parser/parse_swagger_tag.go | 8 ++--- .../parser/parsingcontext/parse_model.go | 12 +++---- .../parser/resourceids/generate_names.go | 14 ++++---- .../parser/resourceids/models.go | 2 +- .../parser/resourceids/parse_segments.go | 4 +-- .../parser/resourceids/parser.go | 12 +++---- .../internal/components/data_test.go | 8 ++--- .../components/terraform/schema/build.go | 4 +-- .../components/terraform/schema/builder.go | 32 +++++++++---------- .../terraform/schema/fields_nested.go | 4 +-- .../terraform/schema/fields_top_level.go | 4 +-- .../model_flatten_list_reference_ids.go | 6 ++-- .../model_flatten_properties_into_parent.go | 6 ++-- .../processors/model_flatten_reference_id.go | 4 +-- .../processors/model_flatten_sku_name.go | 4 +-- .../processors/model_remove_status_detail.go | 4 +-- .../schema/processors/model_rename_zones.go | 4 +-- .../terraform/schema/processors/models.go | 2 +- .../schema/processors/models_test.go | 12 +++---- 30 files changed, 96 insertions(+), 98 deletions(-) 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 index e9d7197a9ed..00996f841b5 100644 --- 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 @@ -14,13 +14,13 @@ func APIResource(first, other sdkModels.APIResource) (*sdkModels.APIResource, er if err != nil { return nil, fmt.Errorf("combining constants: %+v", err) } - first.Constants = *constants + first.Constants = constants models, err := Models(first.Models, other.Models) if err != nil { return nil, fmt.Errorf("combining models: %+v", err) } - first.Models = *models + first.Models = models if err = combineOperations(first.Operations, other.Operations); err != nil { return nil, fmt.Errorf("combining operations: %+v", err) 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 index 713d267a798..cee40d9e24f 100644 --- 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 @@ -21,13 +21,13 @@ func APIResourcesWith(resources, other map[string]sdkModels.APIResource) error { if err != nil { return fmt.Errorf("combining constants: %+v", err) } - resource.Constants = *constants + resource.Constants = constants models, err := Models(resource.Models, v.Models) if err != nil { return fmt.Errorf("combining models: %+v", err) } - resource.Models = *models + resource.Models = models if err = combineOperations(resource.Operations, v.Operations); err != nil { return fmt.Errorf("combining operations: %+v", err) 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 index c7b2ee28ec6..f9555398c29 100644 --- 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 @@ -9,7 +9,7 @@ import ( sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" ) -func Constants(first, second map[string]sdkModels.SDKConstant) (*map[string]sdkModels.SDKConstant, error) { +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 @@ -30,9 +30,9 @@ func Constants(first, second map[string]sdkModels.SDKConstant) (*map[string]sdkM if err != nil { return nil, fmt.Errorf("combining values: %+v", err) } - existingConst.Values = *vals + existingConst.Values = vals output[k] = existingConst } - return &output, nil + 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 index 578c1cb46d9..4e46b5863bd 100644 --- 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 @@ -7,7 +7,7 @@ import ( "fmt" ) -func maps(first map[string]string, second map[string]string) (*map[string]string, error) { +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 @@ -24,5 +24,5 @@ func maps(first map[string]string, second map[string]string) (*map[string]string } } - return &vals, nil + 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 index 653d14717e7..9fe22eacee2 100644 --- 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 @@ -9,7 +9,7 @@ import ( 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) { +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 { @@ -43,5 +43,5 @@ func Models(first map[string]sdkModels.SDKModel, second map[string]sdkModels.SDK } } - return &output, nil + return output, nil } 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 index a2b9cef7c05..30b7efd3c84 100644 --- 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 @@ -69,13 +69,13 @@ func Parse(typeVal spec.StringOrArray, fieldName string, modelName *string, valu return &ParsedConstant{ Name: strings.Title(constantName), Details: sdkModels.SDKConstant{ - Values: *keysAndValues, + Values: keysAndValues, Type: constantType, }, }, nil } -func parseKeysAndValues(input []interface{}, constantType sdkModels.SDKConstantType, constExtension *constantExtension) (*map[string]string, error) { +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 { @@ -152,5 +152,5 @@ func parseKeysAndValues(input []interface{}, constantType sdkModels.SDKConstantT return nil, fmt.Errorf("unsupported constant type %q", string(constantType)) } - return &keysAndValues, nil + return keysAndValues, nil } diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models/supplementary_data.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models/supplementary_data.go index 61a23de801e..5823e97af3b 100644 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models/supplementary_data.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models/supplementary_data.go @@ -28,7 +28,7 @@ func (d *SupplementaryData) AppendConstant(name string, value sdkModels.SDKConst if err != nil { return fmt.Errorf("combining Constants: %+v", err) } - d.Constants = *combinedConstants + d.Constants = combinedConstants return nil } @@ -38,13 +38,13 @@ func (d *SupplementaryData) AppendParseResult(other ParseResult) error { if err != nil { return fmt.Errorf("combining Constants: %+v", err) } - d.Constants = *combinedConstants + d.Constants = combinedConstants combinedModels, err := combine.Models(d.Models, other.Models) if err != nil { return fmt.Errorf("combining Models: %+v", err) } - d.Models = *combinedModels + d.Models = combinedModels return nil } 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 index e6b7ce2c683..6d8d15f38a8 100644 --- 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 @@ -57,7 +57,7 @@ func listOperationDetailsForOperation(input sdkModels.SDKOperation, known parser return nil } -func RemoveWrapperModelForListOperations(input map[string]sdkModels.SDKOperation, known parserModels.ParseResult) (*map[string]sdkModels.SDKOperation, error) { +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. @@ -76,5 +76,5 @@ func RemoveWrapperModelForListOperations(input map[string]sdkModels.SDKOperation output[operationName] = operation } - return &output, nil + 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 index 5d0f0bcf064..2ab4e52b2d8 100644 --- 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 @@ -11,7 +11,7 @@ import ( 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) { +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{}, @@ -77,7 +77,7 @@ func optionsForOperation(input parsedOperation) (*map[string]sdkModels.SDKOperat } } - return &output, &result, nil + return output, &result, nil } func determineObjectDefinitionForOption(input spec.Parameter) (*sdkModels.SDKOperationOptionObjectDefinition, error) { 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 index 8d545950696..adeadadae38 100644 --- 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 @@ -67,7 +67,7 @@ func parseOperation(parsingContext *parsingcontext.Context, operation parsedOper FieldContainingPaginationDetails: paginationField, LongRunning: longRunning, Method: strings.ToUpper(operation.httpMethod), - Options: *options, + Options: options, RequestObject: requestObject, ResourceIDName: resourceId.ResourceIdName, ResponseObject: responseResult.objectDefinition, 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 index c0a1eade93b..461a0ecfcf0 100644 --- 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 @@ -14,7 +14,7 @@ import ( "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) { +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{}, @@ -52,5 +52,5 @@ func ParseOperationsWithinTag(parsingContext *parsingcontext.Context, tag *strin operations[operation.name] = *op } - return &operations, &result, nil + return operations, &result, nil } 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 index 84c7f4810a5..cce6ca5df93 100644 --- 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 @@ -31,7 +31,7 @@ func (p *apiDefinitionsParser) ParseAPIResourceWithinSwaggerTag(tag, resourcePro } // pull out each of the remaining models based on what we've got - nestedResult, err = p.context.FindNestedItemsYetToBeParsed(*operations, result) + nestedResult, err = p.context.FindNestedItemsYetToBeParsed(operations, result) if err != nil { return nil, fmt.Errorf("finding nested items yet to be parsed: %+v", err) } @@ -40,20 +40,20 @@ func (p *apiDefinitionsParser) ParseAPIResourceWithinSwaggerTag(tag, resourcePro } // 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) + 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 { + if len(operations) == 0 { return nil, nil } resource := sdkModels.APIResource{ Constants: result.Constants, Models: result.Models, - Operations: *operations, + Operations: operations, ResourceIDs: resourceIds.NamesToResourceIDs, } 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 index 3ab3fac71ea..38b29a72464 100644 --- 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 @@ -38,14 +38,14 @@ func (c *Context) ParseModel(name string, input spec.Schema) (*parserModels.Pars } // if it's just got constants, we can skip it - if len(*fields) == 0 { + 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) + model, err := c.modelDetailsFromObject(name, input, fields) if err != nil { return nil, fmt.Errorf("populating model details for %q: %+v", name, err) } @@ -233,7 +233,7 @@ func (c *Context) detailsForField(modelName string, propertyName string, value s return &field, &result, err } -func (c *Context) fieldsForModel(modelName string, input spec.Schema, known parserModels.ParseResult) (*map[string]sdkModels.SDKField, *parserModels.ParseResult, error) { +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{}, @@ -305,7 +305,7 @@ func (c *Context) fieldsForModel(modelName string, input spec.Schema, known pars } } if parsedParent != nil { - for k, v := range *parsedParent { + for k, v := range parsedParent { fields[k] = v } } @@ -326,7 +326,7 @@ func (c *Context) fieldsForModel(modelName string, input spec.Schema, known pars if err != nil { return nil, nil, fmt.Errorf("finding fields for parent model %q: %+v", *fragmentName, err) } - for k, v := range *nestedFields { + for k, v := range nestedFields { isRequired := isFieldRequired(k, requiredFields) v.Required = isRequired fields[k] = v @@ -353,7 +353,7 @@ func (c *Context) fieldsForModel(modelName string, input spec.Schema, known pars fields[propName] = *field } - return &fields, &result, nil + return fields, &result, nil } func (c *Context) modelDetailsFromObject(modelName string, input spec.Schema, fields map[string]sdkModels.SDKField) (*sdkModels.SDKModel, error) { diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/generate_names.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/generate_names.go index 7e73a3b1d8a..1c22327a914 100644 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/generate_names.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/generate_names.go @@ -15,12 +15,12 @@ import ( "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 @@ -114,7 +114,7 @@ func generateNamesForResourceIds(input []sdkModels.ResourceID, uriToResourceId m 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 } @@ -136,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) @@ -189,7 +189,7 @@ func determineUniqueNamesFor(conflictingUris []sdkModels.ResourceID, existingCan proposedNames[proposedName] = resourceId } - return &proposedNames, nil + return proposedNames, nil } func SegmentsAvailableForNaming(pri sdkModels.ResourceID) []string { diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/models.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/models.go index 4309d15f7b6..07eb99771a0 100644 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/models.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/models.go @@ -97,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/internal/components/apidefinitions/parser/resourceids/parse_segments.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/parse_segments.go index 4af257d7fec..aa9fe3818f3 100644 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/parse_segments.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/parse_segments.go @@ -36,7 +36,7 @@ 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) @@ -57,7 +57,7 @@ func (p *Parser) parseSegmentsForEachOperation() (*map[string]processedResourceI } } - return &operationIdsToProcessedResourceIds, nil + return operationIdsToProcessedResourceIds, nil } func (p *Parser) parseResourceIdFromOperation(uri string, operation *spec.Operation) (*processedResourceId, error) { diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/parser.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/parser.go index 2d90b0205b0..147e32315e5 100644 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/parser.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/parser.go @@ -25,7 +25,7 @@ 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("Switching out Common IDs as needed..") @@ -40,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 { @@ -102,5 +102,5 @@ func (p *Parser) updateParsedOperationsWithProcessedResourceIds(operationIdsToSe } } - return &output, nil + return output, nil } diff --git a/tools/importer-rest-api-specs/internal/components/data_test.go b/tools/importer-rest-api-specs/internal/components/data_test.go index f585875b6dc..d7271277286 100644 --- a/tools/importer-rest-api-specs/internal/components/data_test.go +++ b/tools/importer-rest-api-specs/internal/components/data_test.go @@ -77,7 +77,7 @@ func TestCanParseTerraformConfigurations(t *testing.T) { if err != nil { t.Fatalf(err.Error()) } - t.Logf("Loaded %d Terraform Configurations", len(*terraformConfigurations)) + t.Logf("Loaded %d Terraform Configurations", len(terraformConfigurations)) } func TestCanBuildTerraformResources(t *testing.T) { @@ -91,7 +91,7 @@ func TestCanBuildTerraformResources(t *testing.T) { t.Fatalf(err.Error()) } - for serviceName, serviceDetails := range *terraformConfigurations { + 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) @@ -121,7 +121,7 @@ type terraformDetailsForService struct { terraformPackageName *string } -func loadTerraformConfigurations(terraformDefinitionsDirectory string) (*map[string]terraformDetailsForService, error) { +func loadTerraformConfigurations(terraformDefinitionsDirectory string) (map[string]terraformDetailsForService, error) { logging.Debugf("Parsing the Terraform Resource Definitions..") terraformResourceDefinitions, err := definitions.LoadFromDirectory(terraformDefinitionsDirectory) if err != nil { @@ -145,7 +145,7 @@ func loadTerraformConfigurations(terraformDefinitionsDirectory string) (*map[str terraformPackageName: pointer.To(serviceData.TerraformPackageName), } } - return &servicesToTerraformDetails, nil + return servicesToTerraformDetails, nil } func findService(input *services.Config, serviceName string) *services.Service { 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/builder.go b/tools/importer-rest-api-specs/internal/components/terraform/schema/builder.go index 2498ab6811c..53c9f46f25a 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 @@ -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 } @@ -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 { @@ -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) { @@ -257,8 +257,6 @@ 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) @@ -269,15 +267,15 @@ func (b Builder) schemaFromTopLevelModel(input sdkModels.TerraformResourceDefini return nil, fmt.Errorf("identifying top level fields within Resource ID %q: %+v", displayValueForResourceId, err) } for k, v := range *fieldsWithinResourceId { - schemaFields[k] = v + 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,7 +463,7 @@ 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 := sdkHelpers.InnerMostSDKObjectDefinition(field.ObjectDefinition) @@ -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_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/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..73b9fdbb885 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,8 +54,8 @@ 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 @@ -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 { From dccc2859e930385bad7a3b51658f7366894dce96 Mon Sep 17 00:00:00 2001 From: Tom Bamford Date: Fri, 19 Jul 2024 13:36:55 +0100 Subject: [PATCH 41/58] `importer-rest-api-specs`: dereference more maps --- .../parser/constants/extension_parser.go | 6 +++--- .../terraform/schema/build_resource_group_test.go | 10 +++++----- .../components/terraform/schema/fields_resource_id.go | 4 ++-- .../terraform/schema/processors/models_test.go | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) 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 index 0e4e8d0c14f..0ad8f670ea2 100644 --- 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 @@ -15,7 +15,7 @@ type constantExtension struct { // 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 + valuesToDisplayNames map[interface{}]string } func parseConstantExtensionFromExtension(input spec.Extensions) (*constantExtension, error) { @@ -31,7 +31,7 @@ func parseConstantExtensionFromExtension(input spec.Extensions) (*constantExtens } var enumName *string - var valuesToDisplayNames *map[interface{}]string + var valuesToDisplayNames map[interface{}]string for k, v := range enumDetails { // presume inconsistencies in the data if strings.EqualFold(k, "name") { @@ -59,7 +59,7 @@ func parseConstantExtensionFromExtension(input spec.Extensions) (*constantExtens displayNameOverrides[value] = name } if len(displayNameOverrides) > 0 { - valuesToDisplayNames = &displayNameOverrides + valuesToDisplayNames = displayNameOverrides } } 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/fields_resource_id.go b/tools/importer-rest-api-specs/internal/components/terraform/schema/fields_resource_id.go index 9a85d35da13..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 @@ -14,7 +14,7 @@ import ( "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) @@ -156,7 +156,7 @@ func (b Builder) identifyTopLevelFieldsWithinResourceID(input sdkModels.Resource } } - 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/processors/models_test.go b/tools/importer-rest-api-specs/internal/components/terraform/schema/processors/models_test.go index 73b9fdbb885..0dfa698f4b3 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 @@ -61,7 +61,7 @@ func mappingTypesMatch(t *testing.T, first sdkModels.TerraformFieldMappingDefini 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") } From cc8d566dbedae2930dab40af6465eabc11934651 Mon Sep 17 00:00:00 2001 From: Tom Bamford Date: Fri, 19 Jul 2024 14:07:54 +0100 Subject: [PATCH 42/58] `importer-rest-api-specs`: remove use of strings.Title, dereference more maps --- tools/importer-rest-api-specs/go.mod | 2 +- .../parser/cleanup/normalize_resource_id.go | 2 +- .../parser/cleanup/normalize_sdk_objects.go | 4 +- .../parser/cleanup/pluralise_helper.go | 4 +- .../apidefinitions/parser/cleanup/title.go | 10 +++ .../apidefinitions/parser/cleanup/to_sort.go | 10 +-- .../apidefinitions/parser/constants/parse.go | 7 +- .../parser/parse_swagger_tags.go | 5 +- .../parser/parsingcontext/helpers.go | 2 +- .../parser/parsingcontext/parse_model.go | 3 +- .../parsingcontext/parse_object_definition.go | 2 +- .../parser/resourceids/generate_names_test.go | 84 +++++++++---------- .../parser/resourceids/parse_segments.go | 8 +- .../components/terraform/schema/builder.go | 2 +- .../schema/fields_resource_id_test.go | 64 +++++++------- .../schema/processors/models_test.go | 6 +- 16 files changed, 112 insertions(+), 103 deletions(-) create mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/title.go diff --git a/tools/importer-rest-api-specs/go.mod b/tools/importer-rest-api-specs/go.mod index 7a27d0f83a6..38e36d2d2e7 100644 --- a/tools/importer-rest-api-specs/go.mod +++ b/tools/importer-rest-api-specs/go.mod @@ -17,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.14.0 ) require ( @@ -73,7 +74,6 @@ require ( 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 gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect 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 index 3deef879974..b395dee3c39 100644 --- 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 @@ -152,7 +152,7 @@ func NormalizeSegment(input string, camelCase bool) string { if camelCase { return v } else { - return strings.Title(v) + return Title(v) } } } diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/normalize_sdk_objects.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/normalize_sdk_objects.go index e76acc801ec..da4bde3ea29 100644 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/normalize_sdk_objects.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/normalize_sdk_objects.go @@ -4,8 +4,6 @@ package cleanup import ( - "strings" - "github.com/hashicorp/go-azure-helpers/lang/pointer" sdkModels "github.com/hashicorp/pandora/tools/data-api-sdk/v1/models" ) @@ -109,7 +107,7 @@ func NormalizeAPIResource(input sdkModels.APIResource) sdkModels.APIResource { func normalizeSDKObjectDefinition(input sdkModels.SDKObjectDefinition) sdkModels.SDKObjectDefinition { if input.ReferenceName != nil { normalized := NormalizeName(*input.ReferenceName) - input.ReferenceName = pointer.To(strings.Title(normalized)) + input.ReferenceName = pointer.To(Title(normalized)) } if input.NestedItem != nil { diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/pluralise_helper.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/pluralise_helper.go index eff3048cf2d..6c5918b6e40 100644 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/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/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/internal/components/apidefinitions/parser/cleanup/to_sort.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/to_sort.go index ad3786d66e0..bdf71ce38e9 100644 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/to_sort.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/to_sort.go @@ -41,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 } @@ -57,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 } @@ -69,7 +69,7 @@ func NormalizeSegmentName(input string) string { output = strings.TrimSuffix(output, "Name") } - output = strings.Title(GetSingular(output)) + output = Title(GetSingular(output)) return output } @@ -235,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) } @@ -386,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 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 index 30b7efd3c84..c3bb70a07f4 100644 --- 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 @@ -11,6 +11,7 @@ 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/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" ) @@ -37,7 +38,7 @@ func Parse(typeVal spec.StringOrArray, fieldName string, modelName *string, valu // 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) + constantName = constantPrefix + cleanup.Title(fieldName) logging.Debugf("Field %q renamed to %q", fieldName, constantName) } @@ -67,7 +68,7 @@ func Parse(typeVal spec.StringOrArray, fieldName string, modelName *string, valu } return &ParsedConstant{ - Name: strings.Title(constantName), + Name: cleanup.Title(constantName), Details: sdkModels.SDKConstant{ Values: keysAndValues, Type: constantType, @@ -124,7 +125,7 @@ func parseKeysAndValues(input []interface{}, constantType sdkModels.SDKConstantT 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] + overrideName, hasOverride := constExtension.valuesToDisplayNames[value] if hasOverride { key = overrideName } 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 index ca80b73b859..e5e9127e403 100644 --- 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 @@ -6,8 +6,7 @@ package parser import ( "sort" - "golang.org/x/text/cases" - "golang.org/x/text/language" + "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup" ) func (p *apiDefinitionsParser) ParseSwaggerTags() []string { @@ -16,7 +15,7 @@ func (p *apiDefinitionsParser) ParseSwaggerTags() []string { for _, operation := range p.context.SwaggerSpecExpanded.Operations() { for _, details := range operation { for _, tag := range details.Tags { - normalizedTag := cases.Title(language.AmericanEnglish, cases.NoLower).String(tag) + normalizedTag := cleanup.Title(tag) tags[normalizedTag] = struct{}{} } } 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 index 3e80dbc2294..6fd75fa89c3 100644 --- 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 @@ -30,7 +30,7 @@ func fragmentNameFromString(fragmentName string) *string { func inlinedModelName(parentModelName, fieldName string) string { // intentionally split out for consistency - val := fmt.Sprintf("%s%s", strings.Title(parentModelName), strings.Title(fieldName)) + val := fmt.Sprintf("%s%s", cleanup.Title(parentModelName), cleanup.Title(fieldName)) return cleanup.NormalizeName(val) } 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 index 38b29a72464..8d51e2b46e5 100644 --- 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 @@ -7,6 +7,7 @@ import ( "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" @@ -223,7 +224,7 @@ func (c *Context) detailsForField(modelName string, propertyName string, value s result.Models[inlinedName] = *inlinedModelDetails // then swap out the reference objectDefinition.Type = sdkModels.ReferenceSDKObjectDefinitionType - objectDefinition.ReferenceName = pointer.To(strings.Title(inlinedName)) + objectDefinition.ReferenceName = pointer.To(cleanup.Title(inlinedName)) } // Custom Types are determined once all the models/constants have been pulled out at the end 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 index 464f281e61c..01e92df8e91 100644 --- 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 @@ -287,7 +287,7 @@ func (c *Context) parseDataFactoryCustomTypes(input *spec.Schema, known parserMo Type: sdkModels.ListSDKObjectDefinitionType, NestedItem: &sdkModels.SDKObjectDefinition{ Type: sdkModels.ReferenceSDKObjectDefinitionType, - ReferenceName: pointer.To(strings.Title(elementType)), + ReferenceName: pointer.To(cleanup.Title(elementType)), }, }, &known, nil } diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/generate_names_test.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/generate_names_test.go index 2bc716c2df0..07ff760a30f 100644 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/generate_names_test.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/generate_names_test.go @@ -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) } } @@ -539,8 +539,8 @@ 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 { @@ -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/parse_segments.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/parse_segments.go index aa9fe3818f3..9c68a415ef3 100644 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/parse_segments.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/resourceids/parse_segments.go @@ -183,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/internal/components/terraform/schema/builder.go b/tools/importer-rest-api-specs/internal/components/terraform/schema/builder.go index 53c9f46f25a..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 @@ -266,7 +266,7 @@ func (b Builder) schemaFromTopLevelModel(input sdkModels.TerraformResourceDefini displayValueForResourceId := sdkHelpers.DisplayValueForResourceID(resourceId) return nil, fmt.Errorf("identifying top level fields within Resource ID %q: %+v", displayValueForResourceId, err) } - for k, v := range *fieldsWithinResourceId { + for k, v := range fieldsWithinResourceId { fields[k] = v } 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/processors/models_test.go b/tools/importer-rest-api-specs/internal/components/terraform/schema/processors/models_test.go index 0dfa698f4b3..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 @@ -65,11 +65,11 @@ func modelDefinitionsMatch(t *testing.T, actual map[string]sdkModels.TerraformSc 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) From 0c29e2b3ff29e87b5aac3e4944b6918e594651d6 Mon Sep 17 00:00:00 2001 From: Tom Bamford Date: Fri, 19 Jul 2024 15:39:20 +0100 Subject: [PATCH 43/58] limit scope of SDK generation test to the SDK being tested --- scripts/automation-generate-go-sdk.sh | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) 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 From 6af799d286e3e376e5a8bbd900d3a8d920d3c77f Mon Sep 17 00:00:00 2001 From: Tom Bamford Date: Fri, 19 Jul 2024 19:06:09 +0100 Subject: [PATCH 44/58] `importer-rest-api-specs`: two loops become one --- .../apidefinitions/parse_api_version.go | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) 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 index 7c00de49bbf..1b12aa99e66 100644 --- 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 @@ -52,19 +52,17 @@ func parseAPIVersion(serviceName string, input discoveryModels.AvailableDataSetF 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 { + // 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. 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 { + // Now that we have a canonical list of resources - can we simplify the Operation names at all? logging.Tracef("Simplifying operation names for resource %q", resourceName) - updated := cleanup.SimplifyOperationNamesForAPIResource(resourceName, resource) - apiResources[resourceName] = updated + resource = cleanup.SimplifyOperationNamesForAPIResource(resourceName, resource) + apiResources[resourceName] = resource } apiVersion := sdkModels.APIVersion{ From be215319d64acdd11159eb347c0c6398c53a2488 Mon Sep 17 00:00:00 2001 From: Tom Bamford Date: Fri, 19 Jul 2024 19:06:55 +0100 Subject: [PATCH 45/58] `importer-rest-api-specs`: workaround inconsistent casing of "VirtualWANs" --- .../apidefinitions/parser/cleanup/normalize_api_definitions.go | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/normalize_api_definitions.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/normalize_api_definitions.go index 368787f19ee..3930dbb88f5 100644 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/normalize_api_definitions.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/normalize_api_definitions.go @@ -39,6 +39,7 @@ 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 From 530ffaf861c0f205e921ecc3e2fce0a97a2f9b1a Mon Sep 17 00:00:00 2001 From: Tom Bamford Date: Fri, 19 Jul 2024 19:27:07 +0100 Subject: [PATCH 46/58] Revert "`importer-rest-api-specs`: two loops become one" This reverts commit 31b45ca060ed92da8142ca991428acfe76dabfe1. --- .../apidefinitions/parse_api_version.go | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) 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 index 1b12aa99e66..7c00de49bbf 100644 --- 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 @@ -52,17 +52,19 @@ func parseAPIVersion(serviceName string, input discoveryModels.AvailableDataSetF 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 { - // 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. apiResources[resourceName] = cleanup.NormalizeAPIResource(resource) + } - // Now that we have a canonical list of resources - can we simplify the Operation names at all? + // 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) - resource = cleanup.SimplifyOperationNamesForAPIResource(resourceName, resource) - apiResources[resourceName] = resource + updated := cleanup.SimplifyOperationNamesForAPIResource(resourceName, resource) + apiResources[resourceName] = updated } apiVersion := sdkModels.APIVersion{ From 5422698335bec8ac4dca416181fa673f036fe464 Mon Sep 17 00:00:00 2001 From: Tom Bamford Date: Fri, 19 Jul 2024 20:37:16 +0100 Subject: [PATCH 47/58] `importer-rest-api-specs`: normalize "attachednetworks" segment of resource IDs --- .../apidefinitions/parser/cleanup/normalize_resource_id.go | 1 + 1 file changed, 1 insertion(+) 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 index b395dee3c39..aa2a841f54a 100644 --- 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 @@ -24,6 +24,7 @@ func NormalizeSegment(input string, camelCase bool) string { "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", From a96d44320691c43c3d8a48fb9c541ec29f5d0f35 Mon Sep 17 00:00:00 2001 From: Tom Bamford Date: Wed, 25 Sep 2024 19:52:57 +0100 Subject: [PATCH 48/58] Revert "`tools/data-api-sdk`: making IsCommonType required so this is more explicit" This reverts commit 71a166f520d75e3db6070e757dfabb9cdd6e9675. --- tools/data-api-sdk/v1/models/sdk_object_definition.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/data-api-sdk/v1/models/sdk_object_definition.go b/tools/data-api-sdk/v1/models/sdk_object_definition.go index 402a9095935..bafe6af10d1 100644 --- a/tools/data-api-sdk/v1/models/sdk_object_definition.go +++ b/tools/data-api-sdk/v1/models/sdk_object_definition.go @@ -22,7 +22,7 @@ type SDKObjectDefinition struct { ReferenceName *string `json:"referenceName,omitempty"` // ReferenceNameIsCommonType specifies whether the referenced Constant or Model is a common type - ReferenceNameIsCommonType bool `json:"referenceNameIsCommonType"` + ReferenceNameIsCommonType *bool `json:"referenceNameIsCommonType,omitempty"` // Type specifies the Type that represents this SDK Object Definition. This can be either a // Simple type (e.g. a String/Integer), a Reference to a Constant/Model or a more complex object From eaa8229ceb759ccbca8766a33ace1999f583f9ef Mon Sep 17 00:00:00 2001 From: Tom Bamford Date: Tue, 1 Oct 2024 10:41:09 +0100 Subject: [PATCH 49/58] importer-rest-api-specs: use updated forks for `go-openapi/analysis`, `go-openapi/jsonreference`, and `go-openapi/spec` --- tools/importer-rest-api-specs/go.mod | 35 +++++++++------- tools/importer-rest-api-specs/go.sum | 63 ++++++++++++++++------------ 2 files changed, 57 insertions(+), 41 deletions(-) diff --git a/tools/importer-rest-api-specs/go.mod b/tools/importer-rest-api-specs/go.mod index 38e36d2d2e7..39076d8bc4f 100644 --- a/tools/importer-rest-api-specs/go.mod +++ b/tools/importer-rest-api-specs/go.mod @@ -17,7 +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.14.0 + golang.org/x/text v0.16.0 ) require ( @@ -27,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 @@ -41,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 @@ -55,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 @@ -70,19 +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/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 e97aff3fbad..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= @@ -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= From ef8b51d5fb8c1e9136983b008cc3238232477082 Mon Sep 17 00:00:00 2001 From: Tom Bamford Date: Tue, 1 Oct 2024 11:15:48 +0100 Subject: [PATCH 50/58] importer-rest-api-specs: remove Supplementary Data parser as is not used/needed --- .../parser/parse_supplementary_data.go | 59 ------------------- 1 file changed, 59 deletions(-) delete mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_supplementary_data.go diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_supplementary_data.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_supplementary_data.go deleted file mode 100644 index f048ce280c7..00000000000 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parse_supplementary_data.go +++ /dev/null @@ -1,59 +0,0 @@ -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/constants" - parserModels "github.com/hashicorp/pandora/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models" -) - -// Deprecated: SupplementaryData is unused at this time, but being left in as a potential requirement. Note that this -// doesn't seem to be needed at this time for DataFactory, which uses external swagger refs - since support for this is -// built into the go-openapi module. -// TODO: @manicminer revisit and determine whether we'll implement this -func (p *apiDefinitionsParser) SupplementaryData() (*parserModels.SupplementaryData, error) { - result := parserModels.SupplementaryData{ - Constants: map[string]sdkModels.SDKConstant{}, - Models: map[string]sdkModels.SDKModel{}, - } - - for objectName, definition := range p.context.SwaggerSpecRaw.Definitions { - var parsedConstant *constants.ParsedConstant - parsedModel, err := p.context.ParseModel(objectName, definition) - if err != nil { - var err2 error - parsedConstant, err2 = p.context.ParseConstant(objectName, definition) - if err != nil { - return nil, fmt.Errorf("failed to parse %q as a Constant or a Model. Constant error: [%+v]. Model error: [%+v]", objectName, err, err2) - } - } - if parsedModel != nil { - if err := result.AppendParseResult(*parsedModel); err != nil { - return nil, fmt.Errorf("appending model %q: %+v", objectName, err) - } - } - if parsedConstant != nil { - if err := result.AppendConstant(parsedConstant.Name, parsedConstant.Details); err != nil { - return nil, fmt.Errorf("appending constant %q: %+v", objectName, err) - } - } - } - - // FindNestedItemsYetToBeParsed takes ParseResult, so we need to shim this across - shim := parserModels.ParseResult{ - Constants: result.Constants, - Models: result.Models, - } - // 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{}, shim) - if err != nil { - return nil, fmt.Errorf("finding nested items yet to be parsed: %+v", err) - } - if err := result.AppendParseResult(*nestedResult); err != nil { - return nil, fmt.Errorf("appending nestedResult from Models used by existing Items: %+v", err) - } - - return &result, nil -} From bb6631c8df91e8cfe8f498fd9b69a047b7eab3e6 Mon Sep 17 00:00:00 2001 From: Tom Bamford Date: Tue, 1 Oct 2024 11:20:39 +0100 Subject: [PATCH 51/58] importer-rest-api-specs: rework the swagger spec parser and manually resolve all remote references --- .../parser/parse_discriminated.go | 2 +- .../apidefinitions/parser/parser.go | 4 +- .../parser/parsingcontext/build.go | 309 +++++++++++++++--- .../parser/parsingcontext/context.go | 12 +- .../parsingcontext/helpers_implements.go | 2 +- .../parser/parsingcontext/parse_model.go | 34 +- .../parser/parsingcontext/top_level_object.go | 4 +- 7 files changed, 292 insertions(+), 75 deletions(-) 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 index 529ba0a61e8..97d9faaba64 100644 --- 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 @@ -15,7 +15,7 @@ func (p *apiDefinitionsParser) FindOrphanedDiscriminatedModels(serviceName strin Models: map[string]sdkModels.SDKModel{}, } - for modelName, definition := range p.context.SwaggerSpecRaw.Definitions { + 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 { 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 index a87c277c6d4..f24bfa2386f 100644 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parser.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parser.go @@ -11,12 +11,12 @@ type apiDefinitionsParser struct { } func NewAPIDefinitionsParser(filePath string) (*apiDefinitionsParser, error) { - paringContext, err := parsingcontext.BuildFromFile(filePath) + parsingContext, err := parsingcontext.BuildFromFile(filePath) if err != nil { return nil, fmt.Errorf("building the parsing context: %+v", err) } return &apiDefinitionsParser{ - context: paringContext, + 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 index fa8046c5027..234ecd5438f 100644 --- 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 @@ -10,21 +10,50 @@ import ( "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 remove references after resolving them, to make it appear like everything was +// loaded from the original swagger file. func BuildFromFile(filePath string) (*Context, error) { - directory := filepath.Dir(filePath) - // 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 + // 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) } - swaggerDocWithReferences, err = findAndMergeLocalMixins(swaggerDocWithReferences, directory, filePath) + + // 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 mixin remote swagger files referenced by %q: %+v", filePath, err) + 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, @@ -32,25 +61,32 @@ func BuildFromFile(filePath string) (*Context, error) { RemoveUnused: false, //ContinueOnError: true, - BasePath: swaggerDocWithReferences.SpecFilePath(), + BasePath: basePath, Spec: analysis.New(swaggerDocWithReferences.Spec()), } - if err := analysis.Flatten(*flattenedWithReferencesOpts); err != nil { + 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() + // 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) } - expandedSwaggerDoc, err = findAndMergeLocalMixins(expandedSwaggerDoc, directory, filePath) + + // 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 mixin remote swagger files referenced by %q: %+v", filePath, err) + 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, @@ -58,56 +94,237 @@ func BuildFromFile(filePath string) (*Context, error) { RemoveUnused: false, //ContinueOnError: true, - BasePath: expandedSwaggerDoc.SpecFilePath(), + BasePath: basePath, Spec: analysis.New(expandedSwaggerDoc.Spec()), } - if err := analysis.Flatten(*flattenedExpandedOpts); err != nil { + if err = analysis.Flatten(*flattenedExpandedOpts); err != nil { return nil, fmt.Errorf("flattening expanded swagger file %q: %+v", filePath, err) } - swaggerSpecExpanded := expandedSwaggerDoc.Analyzer - swaggerSpecExpandedRaw := expandedSwaggerDoc.Spec() return &Context{ - FilePath: filePath, - SwaggerSpecExpanded: swaggerSpecExpanded, - SwaggerSpecWithReferences: swaggerSpecWithReferences, - SwaggerSpecRaw: swaggerSpecWithReferencesRaw, - SwaggerSpecExtendedRaw: swaggerSpecExpandedRaw, + FilePath: filePath, + + SwaggerSpecWithReferences: swaggerDocWithReferences.Analyzer, + SwaggerSpecWithReferencesRaw: swaggerDocWithReferences.Spec(), + + SwaggerSpecExpanded: expandedSwaggerDoc.Analyzer, }, nil } -func findAndMergeLocalMixins(input *loads.Document, basePath string, baseFile string) (*loads.Document, error) { - if len(strings.Split(baseFile, fmt.Sprintf("%c", filepath.Separator))) != 2 { // We only care about local files, not sub-folders - return input, nil - } - pathsToMixin := make([]string, 0) +// 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 { - 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) + 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 } } } - 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) + // 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 + } } } - 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) + + // 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 } - mixins = append(mixins, doc.Spec()) } - _ = analysis.Mixin(input.Spec(), mixins...) + + // 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 "", "" } - return input, nil + + 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 URL fragment + fragments := strings.Split(refUrl.Fragment, "/") + modelName = fragments[len(fragments)-1] + + 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 index 8eb2d894af3..aeceed8a2ea 100644 --- 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 @@ -9,15 +9,13 @@ import ( // This includes the interpretations of the files themselves type Context struct { FilePath string - - // SwaggerSpecExpanded contains 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 is the parsed spec with references intact SwaggerSpecWithReferences *analysis.Spec - // TODO: confirm the the need for these two + // SwaggerSpecWithReferencesRaw is the `spec.Swagger` root document, with references intact + SwaggerSpecWithReferencesRaw *spec.Swagger - SwaggerSpecRaw *spec.Swagger - SwaggerSpecExtendedRaw *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_implements.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parsingcontext/helpers_implements.go index 5f85ab34283..b1a2ad02a32 100644 --- 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 @@ -47,7 +47,7 @@ func (c *Context) doesModelImplement(modelName string, value spec.Schema, parent func (c *Context) findModelNamesWhichImplement(parentName string) (*[]string, error) { modelNames := make([]string, 0) - for childName, value := range c.SwaggerSpecExtendedRaw.Definitions { + 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) 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 index 8d51e2b46e5..137f07c3c92 100644 --- 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 @@ -378,24 +378,26 @@ func (c *Context) modelDetailsFromObject(modelName string, input spec.Schema, fi } } - // 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 := 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 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) } + } - // 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 - } + // 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 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 index 1c35ea0d806..6b2a6e2682a 100644 --- 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 @@ -8,13 +8,13 @@ import ( ) func (c *Context) findTopLevelObject(name string) (*spec.Schema, error) { - for modelName, model := range c.SwaggerSpecRaw.Definitions { + for modelName, model := range c.SwaggerSpecWithReferencesRaw.Definitions { if strings.EqualFold(modelName, name) { return &model, nil } } - for modelName, model := range c.SwaggerSpecExtendedRaw.Definitions { + for modelName, model := range c.SwaggerSpecWithReferencesRaw.Definitions { if strings.EqualFold(modelName, name) { return &model, nil } From 2d11e965eb46eb2d8261337024fcb4f47f041129 Mon Sep 17 00:00:00 2001 From: Tom Bamford Date: Tue, 1 Oct 2024 12:10:47 +0100 Subject: [PATCH 52/58] importer-rest-api-specs: fix format string, ensure model names are URL-decoded when parsing from the Ref URL fragment --- .../parser/comparison/object_definition.go | 14 +++++++++++--- .../apidefinitions/parser/parsingcontext/build.go | 11 +++++++---- .../parser/parsingcontext/helpers.go | 5 +++++ .../internal/components/data_test.go | 2 +- 4 files changed, 24 insertions(+), 8 deletions(-) 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 index 65a64472589..399ed3860a9 100644 --- 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 @@ -3,7 +3,7 @@ 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" @@ -32,8 +32,16 @@ func ObjectDefinitionsMatch(first, second *sdkModels.SDKObjectDefinition) (bool, return false, fmt.Errorf("ReferenceName differed - %q vs %q", pointer.From(first.ReferenceName), pointer.From(second.ReferenceName)) } if first.ReferenceNameIsCommonType != second.ReferenceNameIsCommonType { - logging.Tracef("ReferenceNameIsCommonType differed - %t vs %t", first.ReferenceNameIsCommonType, second.ReferenceNameIsCommonType) - return false, fmt.Errorf("ReferenceNameIsCommonType differed - %t vs %t", 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 { 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 index 234ecd5438f..773b9372fe5 100644 --- 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 @@ -2,6 +2,7 @@ package parsingcontext import ( "fmt" + "net/url" "path/filepath" "strings" @@ -31,7 +32,7 @@ import ( // 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 remove references after resolving them, to make it appear like everything was +// 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) { @@ -59,7 +60,6 @@ func BuildFromFile(filePath string) (*Context, error) { Verbose: true, Expand: false, RemoveUnused: false, - //ContinueOnError: true, BasePath: basePath, Spec: analysis.New(swaggerDocWithReferences.Spec()), @@ -92,7 +92,6 @@ func BuildFromFile(filePath string) (*Context, error) { Verbose: true, Expand: false, RemoveUnused: false, - //ContinueOnError: true, BasePath: basePath, Spec: analysis.New(expandedSwaggerDoc.Spec()), @@ -322,9 +321,13 @@ func modelNamePathFromRef(ref spec.Ref, sourcePath, topLevelDir string) (modelNa modelFilePath = filepath.Join(sourceDir, modelFilePath) } - // get the modelName from the last URL fragment + // 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/helpers.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/parsingcontext/helpers.go index 6fd75fa89c3..ab9dd896c12 100644 --- 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 @@ -2,6 +2,7 @@ package parsingcontext import ( "fmt" + "net/url" "strings" "github.com/go-openapi/spec" @@ -22,6 +23,10 @@ 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 } diff --git a/tools/importer-rest-api-specs/internal/components/data_test.go b/tools/importer-rest-api-specs/internal/components/data_test.go index d7271277286..4009f9af2d1 100644 --- a/tools/importer-rest-api-specs/internal/components/data_test.go +++ b/tools/importer-rest-api-specs/internal/components/data_test.go @@ -61,7 +61,7 @@ func TestCanParseFilesForAllServicesFromConfig(t *testing.T) { parsedService, err := apidefinitions.ParseService(*availableDataSet) if err != nil { - t.Errorf("parsing Data for Service %q: %+v", service.Name, err) + t.Fatalf("parsing Data for Service %q: %+v", service.Name, err) } t.Logf("Service %q contains %d API Versions", service.Name, len(availableDataSet.DataSetsForAPIVersions)) From dbcf1ab27c8fb265ac2dc69dade75bdc5194372d Mon Sep 17 00:00:00 2001 From: Tom Bamford Date: Tue, 1 Oct 2024 13:04:26 +0100 Subject: [PATCH 53/58] importer-rest-api-specs: updated tests for discriminated models to expect full ancestry --- .../parser/models_discriminators_test.go | 74 +++++++++++++++++-- 1 file changed, 68 insertions(+), 6 deletions(-) diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models_discriminators_test.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models_discriminators_test.go index af1aa4511ef..3e31d83805c 100644 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models_discriminators_test.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models_discriminators_test.go @@ -992,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": { @@ -1145,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": { From 8fc6c76be8f8f2b86ba07966737526e5ee940778 Mon Sep 17 00:00:00 2001 From: Tom Bamford Date: Tue, 1 Oct 2024 13:17:19 +0100 Subject: [PATCH 54/58] importer-rest-api-specs: remove vestiges of supplementary data spike --- .../apidefinitions/models_debugging_test.go | 1 - .../parse_supplementary_data.go | 18 ------- .../parser/models/supplementary_data.go | 50 ------------------- .../parser/testhelpers/parse_swagger_file.go | 1 - .../components/discovery/for_api_version.go | 20 ++------ .../discovery/for_api_version_test.go | 33 ------------ .../components/discovery/for_service.go | 2 +- .../available_data_set_for_api_version.go | 5 -- 8 files changed, 6 insertions(+), 124 deletions(-) delete mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parse_supplementary_data.go delete mode 100644 tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models/supplementary_data.go 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 index 33359970137..47ddbc5ea80 100644 --- 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 @@ -15,7 +15,6 @@ func TestDebugSingleSwaggerFile(t *testing.T) { FilePathsContainingAPIDefinitions: []string{ "/path/to/submodules/rest-api-specs/specification/storagecache/resource-manager/Microsoft.StorageCache/stable/2023-05-01/amlfilesystem.json", }, - FilePathsContainingSupplementaryData: []string{}, } result, err := parseAPIVersion("StorageCache", input, pointer.To("Microsoft.StorageCache")) if err != nil { diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parse_supplementary_data.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parse_supplementary_data.go deleted file mode 100644 index 64aed08e76d..00000000000 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parse_supplementary_data.go +++ /dev/null @@ -1,18 +0,0 @@ -package apidefinitions - -// parseSupplementaryDataFromFile loads any available Supplementary Data from the file in question -//func parseSupplementaryDataFromFile(filePath string) (*parserModels.SupplementaryData, error) { -// logging.Tracef("Building an Definitions Parser for %q..", filePath) -// parser, err := parser.NewAPIDefinitionsParser(filePath) -// if err != nil { -// return nil, fmt.Errorf("building the APIDefinitions Parser for %q: %+v", filePath, err) -// } -// logging.Tracef("Parsing the Supplementary Data from %q..", filePath) -// data, err := parser.SupplementaryData() -// if err != nil { -// return nil, fmt.Errorf("parsing the Supplementary Data from %q: %+v", filePath, err) -// } -// logging.Tracef("Loaded the Supplementary Data from %q.", filePath) -// -// return data, nil -//} diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models/supplementary_data.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models/supplementary_data.go deleted file mode 100644 index 5823e97af3b..00000000000 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models/supplementary_data.go +++ /dev/null @@ -1,50 +0,0 @@ -package models - -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" -) - -// SupplementaryData contains information about any Constants and Models which are Supplementary. -// These Constants and Models aren't directly used in either Operations or Resource IDs, but are -// typically used in Discriminator Implementations for Models which are, and so need to be included -// and considered when identifying Discriminator Implementations. -type SupplementaryData struct { - // Constants specifies a map of Constant Name (key, valid as an identifier) to SDKConstant (value). - Constants map[string]sdkModels.SDKConstant - - // Models specifies a map of Model Name (key, valid as an identifier) to SDKModel (value). - Models map[string]sdkModels.SDKModel -} - -func (d *SupplementaryData) AppendConstant(name string, value sdkModels.SDKConstant) error { - constants := map[string]sdkModels.SDKConstant{ - name: value, - } - - combinedConstants, err := combine.Constants(d.Constants, constants) - if err != nil { - return fmt.Errorf("combining Constants: %+v", err) - } - d.Constants = combinedConstants - - return nil -} - -func (d *SupplementaryData) AppendParseResult(other ParseResult) error { - combinedConstants, err := combine.Constants(d.Constants, other.Constants) - if err != nil { - return fmt.Errorf("combining Constants: %+v", err) - } - d.Constants = combinedConstants - - combinedModels, err := combine.Models(d.Models, other.Models) - if err != nil { - return fmt.Errorf("combining Models: %+v", err) - } - d.Models = combinedModels - - return nil -} 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 index d00f0930d3f..2adde990627 100644 --- 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 @@ -23,7 +23,6 @@ func ParseSwaggerFileForTesting(t *testing.T, filePath string, serviceName *stri FilePathsContainingAPIDefinitions: []string{ filepath.Join("testdata", filePath), }, - FilePathsContainingSupplementaryData: []string{}, }, }, ResourceProvider: 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 20cc806518f..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 @@ -54,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 @@ -74,24 +72,16 @@ func discoverDataSetForAPIVersion(apiVersion string, filePaths []string) (*model 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 6e39cdccc8f..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 @@ -25,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) @@ -55,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) @@ -111,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) @@ -138,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 } From 4921b5cec151291b049fec794c072f1a93b7695d Mon Sep 17 00:00:00 2001 From: Tom Bamford Date: Tue, 1 Oct 2024 13:17:46 +0100 Subject: [PATCH 55/58] importer-rest-api-specs: do not set the description, see https://github.com/hashicorp/pandora/pull/4434 --- .../parser/parsingcontext/parse_model.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) 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 index 137f07c3c92..93f1c7892ec 100644 --- 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 @@ -140,12 +140,12 @@ func (c *Context) detailsForField(modelName string, propertyName string, value s 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, + 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 From a5c820f316b68e848f293537efbc26f9bd065713 Mon Sep 17 00:00:00 2001 From: Tom Bamford Date: Tue, 1 Oct 2024 15:40:22 +0100 Subject: [PATCH 56/58] importer-rest-api-specs: hack for conflicting constant names in the same set of resources --- .../apidefinitions/parser/models/parse_result.go | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models/parse_result.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models/parse_result.go index 5ecd3d4d884..31f524caa3f 100644 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models/parse_result.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/models/parse_result.go @@ -2,7 +2,6 @@ package models import ( "fmt" - "reflect" "strings" "github.com/hashicorp/go-azure-helpers/lang/pointer" @@ -44,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 From 1da6920a480f93c5e66f14d931f1c5d0ba3040d4 Mon Sep 17 00:00:00 2001 From: Tom Bamford Date: Tue, 1 Oct 2024 15:41:17 +0100 Subject: [PATCH 57/58] importer-rest-api-specs: disable check for non-default Resource Provider when parsing operations, as this was never utilised and leads to skipping resources/operations --- .../apidefinitions/parser/operation/parse_operation.go | 7 ++++++- .../internal/components/discovery/resource_provider.go | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) 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 index adeadadae38..99c0626043c 100644 --- 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 @@ -8,6 +8,7 @@ import ( 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) { @@ -58,7 +59,11 @@ func parseOperation(parsingContext *parsingcontext.Context, operation parsedOper return nil, nil, err } if usesADifferentResourceProvider != nil && *usesADifferentResourceProvider { - return nil, nil, nil + // 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{ 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" ) From 975945b4b154fdaab10aa1205eb57bbb30bf5550 Mon Sep 17 00:00:00 2001 From: Steph Date: Wed, 9 Oct 2024 12:42:23 +0200 Subject: [PATCH 58/58] upper case URL in the go sdk --- .../internal/components/apidefinitions/parser/cleanup/to_sort.go | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/to_sort.go b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/to_sort.go index bdf71ce38e9..9c738d13cfb 100644 --- a/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/to_sort.go +++ b/tools/importer-rest-api-specs/internal/components/apidefinitions/parser/cleanup/to_sort.go @@ -470,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")