Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
## 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.
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package dataworkarounds

import (
"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 *models.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 models.AzureApiDefinition) (*models.AzureApiDefinition, error)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package dataworkarounds

import (
"fmt"

"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 *models.AzureApiDefinition) bool {
return apiDefinition.ServiceName == "ContainerService" && apiDefinition.ApiVersion == "2022-09-02-preview"
}

func (workaroundContainerService21394) Name() string {
return "ContainerService / 21394"
}

func (workaroundContainerService21394) Process(apiDefinition models.AzureApiDefinition) (*models.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

model.Fields["DnsPrefix"] = field
resource.Models["FleetHubProfile"] = model
apiDefinition.Resources["Fleets"] = resource
return &apiDefinition, nil
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package dataworkarounds

import (
"fmt"

"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 *models.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 models.AzureApiDefinition) (*models.AzureApiDefinition, error) {
resource, ok := apiDefinition.Resources["LoadTests"]
if !ok {
return nil, fmt.Errorf("couldn't find API Resource LoadTests")
}
model, ok := resource.Models["LoadTestResourcePatchRequestBody"]
if !ok {
return nil, fmt.Errorf("couldn't find Model LoadTestResourcePatchRequestBody")
}
field, ok := model.Fields["Tags"]
if !ok {
return nil, fmt.Errorf("couldn't find field Tags within model LoadTestResourcePatchRequestBody")
}
tagsType := models.CustomFieldTypeTags
field.CustomFieldType = &tagsType
field.ObjectDefinition = nil

model.Fields["Tags"] = field
resource.Models["LoadTestResourcePatchRequestBody"] = model
apiDefinition.Resources["LoadTests"] = resource
return &apiDefinition, nil
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package dataworkarounds

import "github.com/hashicorp/pandora/tools/importer-rest-api-specs/models"

var _ workaround = workaroundMedia21581{}

// workaroundMedia21581 works around the Update operation having the incorrect Swagger Tag
// (StreamingEndpoint rather than StreamingEndpoints).
// Swagger PR: https://github.com/Azure/azure-rest-api-specs/pull/21581
type workaroundMedia21581 struct {
}

func (workaroundMedia21581) IsApplicable(apiDefinition *models.AzureApiDefinition) bool {
return apiDefinition.ServiceName == "Media" && apiDefinition.ApiVersion == "2020-05-01"
}

func (workaroundMedia21581) Name() string {
return "Media / 21581"
}

func (workaroundMedia21581) Process(apiDefinition models.AzureApiDefinition) (*models.AzureApiDefinition, error) {
singular, singularExists := apiDefinition.Resources["StreamingEndpoint"]
plural, pluralExists := apiDefinition.Resources["StreamingEndpoints"]
if singularExists && pluralExists {
updateOperation, ok := singular.Operations["Update"]
if ok {
// NOTE: we should be moving the Model too, but as it's the same as for Create this should be fine
plural.Operations["Update"] = updateOperation
apiDefinition.Resources["StreamingEndpoints"] = plural
delete(apiDefinition.Resources, "StreamingEndpoint")
}
}
return &apiDefinition, nil
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
package dataworkarounds

import (
"github.com/hashicorp/go-azure-helpers/lang/pointer"
"github.com/hashicorp/pandora/tools/importer-rest-api-specs/models"
"github.com/hashicorp/pandora/tools/sdk/resourcemanager"
)

var _ workaround = workaroundRecoveryServicesSiteRecovery21667{}

// workaroundRecoveryServicesSiteRecovery21667 works around the Resource ID Segments being inconsistent within
// the RecoveryServicesSiteRecovery Resource Provider - namely that `replicatedProtectedItemName` should be
// `replicationProtectedItemName` - but is used interchangeably.
//
// Swagger PR: https://github.com/Azure/azure-rest-api-specs/pull/21667
type workaroundRecoveryServicesSiteRecovery21667 struct {
}

func (workaroundRecoveryServicesSiteRecovery21667) IsApplicable(apiDefinition *models.AzureApiDefinition) bool {
serviceMatches := apiDefinition.ServiceName == "RecoveryServicesSiteRecovery"
apiVersionMatches := apiDefinition.ApiVersion == "2022-05-01" || apiDefinition.ApiVersion == "2022-10-01"
return serviceMatches && apiVersionMatches
}

func (workaroundRecoveryServicesSiteRecovery21667) Name() string {
return "RecoveryServicesSiteRecovery / 21667"
}

func (w workaroundRecoveryServicesSiteRecovery21667) Process(apiDefinition models.AzureApiDefinition) (*models.AzureApiDefinition, error) {
resourcesToPatch := []string{
"RecoveryPoints",
"ReplicationProtectedItems",
"TargetComputeSizes",
}
for _, resourceName := range resourcesToPatch {
if resource, ok := apiDefinition.Resources[resourceName]; ok {
if rid, ok := resource.ResourceIds["ReplicationProtectedItem"]; ok {
if rid.Matches(w.expectedResourceId()) {
rid.Segments = w.correctedResourceId().Segments
}
resource.ResourceIds["ReplicationProtectedItem"] = rid
}
apiDefinition.Resources[resourceName] = resource
}
}
return &apiDefinition, nil
}

func (w workaroundRecoveryServicesSiteRecovery21667) expectedResourceId() models.ParsedResourceId {
return models.ParsedResourceId{
Segments: []resourcemanager.ResourceIdSegment{
{
Type: resourcemanager.StaticSegment,
Name: "staticSubscriptions",
FixedValue: pointer.To("subscriptions"),
},
{
Type: resourcemanager.SubscriptionIdSegment,
Name: "subscriptionId",
},
{
Type: resourcemanager.StaticSegment,
Name: "staticResourceGroups",
FixedValue: pointer.To("resourceGroups"),
},
{
Type: resourcemanager.ResourceGroupSegment,
Name: "resourceGroupName",
},
{
Type: resourcemanager.StaticSegment,
Name: "staticProviders",
FixedValue: pointer.To("providers"),
},
{
Type: resourcemanager.ResourceProviderSegment,
Name: "staticMicrosoftRecoveryServices",
FixedValue: pointer.To("Microsoft.RecoveryServices"),
},
{
Type: resourcemanager.StaticSegment,
Name: "staticVaults",
FixedValue: pointer.To("vaults"),
},
{
Type: resourcemanager.UserSpecifiedSegment,
Name: "resourceName",
},
{
Type: resourcemanager.StaticSegment,
Name: "staticReplicationFabrics",
FixedValue: pointer.To("replicationFabrics"),
},
{
Type: resourcemanager.UserSpecifiedSegment,
Name: "fabricName",
},
{
Type: resourcemanager.StaticSegment,
Name: "staticReplicationProtectionContainers",
FixedValue: pointer.To("replicationProtectionContainers"),
},
{
Type: resourcemanager.UserSpecifiedSegment,
Name: "protectionContainerName",
},
{
Type: resourcemanager.StaticSegment,
Name: "staticReplicationProtectedItems",
FixedValue: pointer.To("replicationProtectedItems"),
},
{
Type: resourcemanager.UserSpecifiedSegment,
Name: "replicatedProtectedItemName",
},
},
Constants: map[string]resourcemanager.ConstantDetails{},
}
}

func (w workaroundRecoveryServicesSiteRecovery21667) correctedResourceId() models.ParsedResourceId {
return models.ParsedResourceId{
Segments: []resourcemanager.ResourceIdSegment{
{
Type: resourcemanager.StaticSegment,
Name: "staticSubscriptions",
FixedValue: pointer.To("subscriptions"),
},
{
Type: resourcemanager.SubscriptionIdSegment,
Name: "subscriptionId",
},
{
Type: resourcemanager.StaticSegment,
Name: "staticResourceGroups",
FixedValue: pointer.To("resourceGroups"),
},
{
Type: resourcemanager.ResourceGroupSegment,
Name: "resourceGroupName",
},
{
Type: resourcemanager.StaticSegment,
Name: "staticProviders",
FixedValue: pointer.To("providers"),
},
{
Type: resourcemanager.ResourceProviderSegment,
Name: "staticMicrosoftRecoveryServices",
FixedValue: pointer.To("Microsoft.RecoveryServices"),
},
{
Type: resourcemanager.StaticSegment,
Name: "staticVaults",
FixedValue: pointer.To("vaults"),
},
{
Type: resourcemanager.UserSpecifiedSegment,
Name: "resourceName",
},
{
Type: resourcemanager.StaticSegment,
Name: "staticReplicationFabrics",
FixedValue: pointer.To("replicationFabrics"),
},
{
Type: resourcemanager.UserSpecifiedSegment,
Name: "fabricName",
},
{
Type: resourcemanager.StaticSegment,
Name: "staticReplicationProtectionContainers",
FixedValue: pointer.To("replicationProtectionContainers"),
},
{
Type: resourcemanager.UserSpecifiedSegment,
Name: "protectionContainerName",
},
{
Type: resourcemanager.StaticSegment,
Name: "staticReplicationProtectedItems",
FixedValue: pointer.To("replicationProtectedItems"),
},
{
Type: resourcemanager.UserSpecifiedSegment,
Name: "replicationProtectedItemName",
},
},
Constants: map[string]resourcemanager.ConstantDetails{},
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package dataworkarounds

import (
"fmt"

"github.com/hashicorp/go-hclog"
"github.com/hashicorp/pandora/tools/importer-rest-api-specs/models"
)

var workarounds = []workaround{
workaroundContainerService21394{},
workaroundLoadTest20961{},
workaroundMedia21581{},
}

func ApplyWorkarounds(input []models.AzureApiDefinition, logger hclog.Logger) (*[]models.AzureApiDefinition, error) {
output := make([]models.AzureApiDefinition, 0)
logger.Trace("Processing Swagger Data Workarounds..")
for _, item := range input {
for _, fix := range workarounds {
if fix.IsApplicable(&item) {
logger.Trace(fmt.Sprintf("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
}
Loading