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 - correctly find all referenced models to mixin #4411

Closed
wants to merge 2 commits into from
Closed
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
131 changes: 101 additions & 30 deletions tools/importer-rest-api-specs/components/parser/flattener.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,18 @@ func load(directory string, fileName string) (*SwaggerDefinition, error) {
if err != nil {
return nil, fmt.Errorf("loading swagger file %q: %+v", filePath, err)
}
swaggerDocWithReferences, err = findAndMergeLocalMixins(swaggerDocWithReferences, directory, fileName)

localMixins, err := findLocalMixins(swaggerDocWithReferences, 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,
// ContinueOnError: true,

BasePath: swaggerDocWithReferences.SpecFilePath(),
Spec: analysis.New(swaggerDocWithReferences.Spec()),
Expand All @@ -52,17 +54,15 @@ func load(directory string, fileName string) (*SwaggerDefinition, error) {
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)
}

_ = analysis.Mixin(expandedSwaggerDoc.Spec(), localMixins)

flattenedExpandedOpts := &analysis.FlattenOpts{
Minimal: false,
Verbose: true,
Expand: false,
RemoveUnused: false,
//ContinueOnError: true,
// ContinueOnError: true,

BasePath: expandedSwaggerDoc.SpecFilePath(),
Spec: analysis.New(expandedSwaggerDoc.Spec()),
Expand All @@ -85,38 +85,109 @@ func load(directory string, fileName string) (*SwaggerDefinition, error) {
}, 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
func findLocalMixins(input *loads.Document, filePath string) (*spec.Swagger, error) {
mixins := &spec.Swagger{
SwaggerProps: spec.SwaggerProps{
Definitions: make(map[string]spec.Schema),
},
}
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)
for _, ref := range input.Analyzer.AllRefs() {
if err := findMixinForRef(ref, filePath, input, mixins); err != nil {
return nil, fmt.Errorf("find mixin for ref %q in %q: %+v", ref.String(), filePath, 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)
return mixins, nil
}

func findMixinForRef(ref spec.Ref, filePath string, doc *loads.Document, mixins *spec.Swagger) error {
if path := ref.GetURL(); path == nil || (path.Path != "" && len(strings.Split(path.Path, "/")) != 2) {
// Check if we have a reference in the CWD (otherwise there will be issue resolving relative path in analysis.Flatten). if path.Path is empty string, it refers to the same file
return nil
}

if strings.HasPrefix(ref.GetURL().Fragment, "/parameters/") {
return nil
}

modelName, modelFilePath := modelNamePathFromRef(ref, filePath)
if _, ok := mixins.Definitions[modelName]; ok {
// skip if this has already been resolved
return nil
}

refDoc := doc
if modelFilePath != filePath {
newDoc, err := loads.Spec(modelFilePath)
if err != nil {
return fmt.Errorf("load swagger %s: %+v", modelFilePath, err)
}

refDoc = newDoc
}
resolvedModel, ok := refDoc.Spec().Definitions[modelName]
if !ok {
return fmt.Errorf("resolve ref %s from %s: model not found", ref.String(), filePath)
}
mixins.Definitions[modelName] = resolvedModel

temp := &spec.Swagger{
SwaggerProps: spec.SwaggerProps{
Definitions: map[string]spec.Schema{
modelName: resolvedModel,
},
},
}

_, isVariant := resolvedModel.Extensions.GetString("x-ms-discriminator-value")
if resolvedModel.Discriminator != "" || isVariant {
for defName, def := range refDoc.Spec().Definitions {
if defName == modelName {
continue
}
for _, allOf := range def.AllOf {
if allOf.Ref.String() != "" {
allOfRefModelName, _ := modelNamePathFromRef(allOf.Ref, modelFilePath)
if modelName == allOfRefModelName {
mixins.Definitions[defName] = def
temp.Definitions[defName] = def

break
}
}
}
}
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)
}

if len(temp.Definitions) > 0 {
analyzer := analysis.New(temp)
for _, r := range analyzer.AllRefs() {
if err := findMixinForRef(r, modelFilePath, refDoc, mixins); err != nil {
return fmt.Errorf("find mixin for ref %q in %q: %+v", r.String(), modelFilePath, err)
}
mixins = append(mixins, doc.Spec())
}
_ = analysis.Mixin(input.Spec(), mixins...)
}
return input, nil

return nil
}

func modelNamePathFromRef(ref spec.Ref, filePath string) (modelName string, modelFilePath string) {
refUrl := ref.GetURL()
if refUrl == nil {
return "", ""
}

modelFilePath = refUrl.Path
if modelFilePath == "" {
modelFilePath = filePath
} else {
filePath, _ := filepath.Split(filePath)
modelFilePath = filepath.Join(filePath, modelFilePath)
}

fragments := strings.Split(refUrl.Fragment, "/")
return fragments[len(fragments)-1], modelFilePath
}
Loading