Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tools/importer-rest-api-specs: refactoring the Parser package #4307

Merged
merged 58 commits into from
Oct 9, 2024
Merged
Changes from 1 commit
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
f255687
`tools/importer-rest-api-specs`: refactoring the `commonschema` Match…
tombuildsstuff Jul 4, 2024
0d1ab7f
`tools/importer-rest-api-specs`: refactoring the Constant package
tombuildsstuff Jul 4, 2024
33321b6
`tools/importer-rest-api-specs`: porting the Network workaround into …
tombuildsstuff Jul 5, 2024
5eea5fe
`tools/importer-rest-api-specs`: porting the `dataworkarounds` packag…
tombuildsstuff Jul 5, 2024
bcb98ac
`tools/importer-rest-api-specs`: scaffolding out the logic to parse a…
tombuildsstuff Jul 5, 2024
fdd6a18
`tools/importer-rest-api-specs`: porting over the "remove unused item…
tombuildsstuff Jul 5, 2024
d86c9be
`tools/importer-rest-api-specs`: adding a shim to the updated `datawo…
tombuildsstuff Jul 5, 2024
dc3da36
dependencies: updating to `v0.69.0` of `github.com/hashicorp/go-azure…
tombuildsstuff Jul 5, 2024
f315a58
`tools/importer-rest-api-specs`: refactoring the `normalization` pack…
tombuildsstuff Jul 5, 2024
4fc8d23
`tools/importer-rest-api-specs`: moving all of the `cleanup` items in…
tombuildsstuff Jul 5, 2024
2238e45
`tools/importer-rest-api-specs`: adding inner most sdk operation obje…
tombuildsstuff Jul 5, 2024
868c6be
`tools/importer-rest-api-specs`: moving the `commonids` into a subpac…
tombuildsstuff Jul 5, 2024
f316c93
`tools/data-api-sdk`: removing an empty todo file
tombuildsstuff Jul 5, 2024
5421539
`tools/importer-rest-api-specs`: refactoring more logic out to the ne…
tombuildsstuff Jul 5, 2024
022aa27
`tools/importer-rest-api-specs`: moving another `ignore` function
tombuildsstuff Jul 5, 2024
7da937c
WIP
tombuildsstuff Jul 9, 2024
9041a95
tools/importer-rest-api-specs: reimplementing #4290 atop the rebase
tombuildsstuff Jul 10, 2024
5ac3c6b
`tools/importer-rest-api-specs`: adding a placeholder for parsing an …
tombuildsstuff Jul 10, 2024
1c24bf8
`tools/importer-rest-api-specs`: refactoring the parser into smaller …
tombuildsstuff Jul 12, 2024
2a0a72a
`tools/importer-rest-api-specs`: fixing an issue where the `validate`…
tombuildsstuff Jul 12, 2024
1f89c95
`tools/importer-rest-api-specs`: title-casing the ReferenceName - whi…
tombuildsstuff Jul 12, 2024
1c27957
`tools/importer-rest-api-specs`: ensuring that TypeSpec examples are …
tombuildsstuff Jul 12, 2024
e19ecbc
`tools/importer-rest-api-specs`: trimming `*.json` from the filename …
tombuildsstuff Jul 12, 2024
84e3913
`tools/importer-rest-api-specs`: removing (most) of the old code path
tombuildsstuff Jul 12, 2024
41b548a
`tools/data-api-sdk`: making IsCommonType required so this is more ex…
tombuildsstuff Jul 12, 2024
61de948
`tools/importer-rest-api-specs`: fixing the validation
tombuildsstuff Jul 12, 2024
b05c0cf
`tools/importer-rest-api-specs`: re-enabling most of the tests
tombuildsstuff Jul 12, 2024
b77acef
`tools/importer-rest-api-specs`: consolidating/porting over the data …
tombuildsstuff Jul 15, 2024
58f5bae
`tools/importer-rest-api-specs`: fixing the test format
tombuildsstuff Jul 15, 2024
fe98217
tools/importer-rest-api-specs: skipping the local test
tombuildsstuff Jul 15, 2024
26268db
`tools/importer-rest-api-specs`: fixing the non-discriminator tests
tombuildsstuff Jul 15, 2024
9643f43
`tools/importer-rest-api-specs`: updating/reworking the supplementary…
tombuildsstuff Jul 15, 2024
898d028
`importer-rest-api-specs`: fix up discriminator handling for datafactory
manicminer Jul 17, 2024
4412616
`importer-rest-api-specs`: shelve SupplementaryData support for now, …
manicminer Jul 17, 2024
8526060
`importer-rest-api-specs`: implement `-services` argument
manicminer Jul 17, 2024
b585e84
`importer-rest-api-specs`: operate on maps instead of passing them ar…
manicminer Jul 17, 2024
6507fde
`importer-rest-api-specs`: normalize swagger tag names to prevent dup…
manicminer Jul 18, 2024
170e490
`importer-rest-api-specs`: normalize API resources after parsing all …
manicminer Jul 18, 2024
42b4244
`importer-rest-api-specs`: ensure new APIResource is populated
manicminer Jul 18, 2024
9d49558
`importer-rest-api-specs`: maps are pointers
manicminer Jul 19, 2024
dccc285
`importer-rest-api-specs`: dereference more maps
manicminer Jul 19, 2024
cc8d566
`importer-rest-api-specs`: remove use of strings.Title, dereference m…
manicminer Jul 19, 2024
0c29e2b
limit scope of SDK generation test to the SDK being tested
manicminer Jul 19, 2024
6af799d
`importer-rest-api-specs`: two loops become one
manicminer Jul 19, 2024
be21531
`importer-rest-api-specs`: workaround inconsistent casing of "Virtual…
manicminer Jul 19, 2024
530ffaf
Revert "`importer-rest-api-specs`: two loops become one"
manicminer Jul 19, 2024
5422698
`importer-rest-api-specs`: normalize "attachednetworks" segment of re…
manicminer Jul 19, 2024
a96d443
Revert "`tools/data-api-sdk`: making IsCommonType required so this is…
manicminer Sep 25, 2024
eaa8229
importer-rest-api-specs: use updated forks for `go-openapi/analysis`,…
manicminer Oct 1, 2024
ef8b51d
importer-rest-api-specs: remove Supplementary Data parser as is not u…
manicminer Oct 1, 2024
bb6631c
importer-rest-api-specs: rework the swagger spec parser and manually …
manicminer Oct 1, 2024
2d11e96
importer-rest-api-specs: fix format string, ensure model names are UR…
manicminer Oct 1, 2024
dbcf1ab
importer-rest-api-specs: updated tests for discriminated models to ex…
manicminer Oct 1, 2024
8fc6c76
importer-rest-api-specs: remove vestiges of supplementary data spike
manicminer Oct 1, 2024
4921b5c
importer-rest-api-specs: do not set the description, see https://gith…
manicminer Oct 1, 2024
a5c820f
importer-rest-api-specs: hack for conflicting constant names in the s…
manicminer Oct 1, 2024
1da6920
importer-rest-api-specs: disable check for non-default Resource Provi…
manicminer Oct 1, 2024
975945b
upper case URL in the go sdk
stephybun Oct 9, 2024
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
Prev Previous commit
Next Next commit
tools/importer-rest-api-specs: porting over the "remove unused item…
…s" logic
tombuildsstuff authored and stephybun committed Oct 9, 2024
commit fdd6a18c1a19ab96e68e764511a92e567b233d89
Original file line number Diff line number Diff line change
@@ -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
}
Original file line number Diff line number Diff line change
@@ -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,13 +31,21 @@ 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 {
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, 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
}