func mergeSpecs()

in pkg/aggregator/aggregator.go [161:259]


func mergeSpecs(dest, source *spec.Swagger, renameModelConflicts, ignorePathConflicts bool) (err error) {
	// Paths may be empty, due to [ACL constraints](http://goo.gl/8us55a#securityFiltering).
	if source.Paths == nil {
		// When a source spec does not have any path, that means none of the definitions
		// are used thus we should not do anything
		return nil
	}
	if dest.Paths == nil {
		dest.Paths = &spec.Paths{}
	}
	if ignorePathConflicts {
		keepPaths := []string{}
		hasConflictingPath := false
		for k := range source.Paths.Paths {
			if _, found := dest.Paths.Paths[k]; !found {
				keepPaths = append(keepPaths, k)
			} else {
				hasConflictingPath = true
			}
		}
		if len(keepPaths) == 0 {
			// There is nothing to merge. All paths are conflicting.
			return nil
		}
		if hasConflictingPath {
			source = FilterSpecByPathsWithoutSideEffects(source, keepPaths)
		}
	}

	// Check for model conflicts and rename to make definitions conflict-free (modulo different GVKs)
	usedNames := map[string]bool{}
	for k := range dest.Definitions {
		usedNames[k] = true
	}
	renames := map[string]string{}
DEFINITIONLOOP:
	for k, v := range source.Definitions {
		existing, found := dest.Definitions[k]
		if !found || deepEqualDefinitionsModuloGVKs(&existing, &v) {
			// skip for now, we copy them after the rename loop
			continue
		}

		if !renameModelConflicts {
			return fmt.Errorf("model name conflict in merging OpenAPI spec: %s", k)
		}

		// Reuse previously renamed model if one exists
		var newName string
		i := 1
		for found {
			i++
			newName = fmt.Sprintf("%s_v%d", k, i)
			existing, found = dest.Definitions[newName]
			if found && deepEqualDefinitionsModuloGVKs(&existing, &v) {
				renames[k] = newName
				continue DEFINITIONLOOP
			}
		}

		_, foundInSource := source.Definitions[newName]
		for usedNames[newName] || foundInSource {
			i++
			newName = fmt.Sprintf("%s_v%d", k, i)
			_, foundInSource = source.Definitions[newName]
		}
		renames[k] = newName
		usedNames[newName] = true
	}
	source = renameDefinition(source, renames)

	// now without conflict (modulo different GVKs), copy definitions to dest
	for k, v := range source.Definitions {
		if existing, found := dest.Definitions[k]; !found {
			if dest.Definitions == nil {
				dest.Definitions = spec.Definitions{}
			}
			dest.Definitions[k] = v
		} else if merged, changed, err := mergedGVKs(&existing, &v); err != nil {
			return err
		} else if changed {
			existing.Extensions[gvkKey] = merged
		}
	}

	// Check for path conflicts
	for k, v := range source.Paths.Paths {
		if _, found := dest.Paths.Paths[k]; found {
			return fmt.Errorf("unable to merge: duplicated path %s", k)
		}
		// PathItem may be empty, due to [ACL constraints](http://goo.gl/8us55a#securityFiltering).
		if dest.Paths.Paths == nil {
			dest.Paths.Paths = map[string]spec.PathItem{}
		}
		dest.Paths.Paths[k] = v
	}

	return nil
}