summaryrefslogtreecommitdiff
path: root/vendor/github.com/go-openapi/analysis/flatten_name.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/go-openapi/analysis/flatten_name.go')
-rw-r--r--vendor/github.com/go-openapi/analysis/flatten_name.go293
1 files changed, 293 insertions, 0 deletions
diff --git a/vendor/github.com/go-openapi/analysis/flatten_name.go b/vendor/github.com/go-openapi/analysis/flatten_name.go
new file mode 100644
index 000000000..3ad2ccfbf
--- /dev/null
+++ b/vendor/github.com/go-openapi/analysis/flatten_name.go
@@ -0,0 +1,293 @@
+package analysis
+
+import (
+ "fmt"
+ "path"
+ "sort"
+ "strings"
+
+ "github.com/go-openapi/analysis/internal/flatten/operations"
+ "github.com/go-openapi/analysis/internal/flatten/replace"
+ "github.com/go-openapi/analysis/internal/flatten/schutils"
+ "github.com/go-openapi/analysis/internal/flatten/sortref"
+ "github.com/go-openapi/spec"
+ "github.com/go-openapi/swag"
+)
+
+// InlineSchemaNamer finds a new name for an inlined type
+type InlineSchemaNamer struct {
+ Spec *spec.Swagger
+ Operations map[string]operations.OpRef
+ flattenContext *context
+ opts *FlattenOpts
+}
+
+// Name yields a new name for the inline schema
+func (isn *InlineSchemaNamer) Name(key string, schema *spec.Schema, aschema *AnalyzedSchema) error {
+ debugLog("naming inlined schema at %s", key)
+
+ parts := sortref.KeyParts(key)
+ for _, name := range namesFromKey(parts, aschema, isn.Operations) {
+ if name == "" {
+ continue
+ }
+
+ // create unique name
+ newName, isOAIGen := uniqifyName(isn.Spec.Definitions, swag.ToJSONName(name))
+
+ // clone schema
+ sch := schutils.Clone(schema)
+
+ // replace values on schema
+ if err := replace.RewriteSchemaToRef(isn.Spec, key,
+ spec.MustCreateRef(path.Join(definitionsPath, newName))); err != nil {
+ return fmt.Errorf("error while creating definition %q from inline schema: %w", newName, err)
+ }
+
+ // rewrite any dependent $ref pointing to this place,
+ // when not already pointing to a top-level definition.
+ //
+ // NOTE: this is important if such referers use arbitrary JSON pointers.
+ an := New(isn.Spec)
+ for k, v := range an.references.allRefs {
+ r, erd := replace.DeepestRef(isn.opts.Swagger(), isn.opts.ExpandOpts(false), v)
+ if erd != nil {
+ return fmt.Errorf("at %s, %w", k, erd)
+ }
+
+ if isn.opts.flattenContext != nil {
+ isn.opts.flattenContext.warnings = append(isn.opts.flattenContext.warnings, r.Warnings...)
+ }
+
+ if r.Ref.String() != key && (r.Ref.String() != path.Join(definitionsPath, newName) || path.Dir(v.String()) == definitionsPath) {
+ continue
+ }
+
+ debugLog("found a $ref to a rewritten schema: %s points to %s", k, v.String())
+
+ // rewrite $ref to the new target
+ if err := replace.UpdateRef(isn.Spec, k,
+ spec.MustCreateRef(path.Join(definitionsPath, newName))); err != nil {
+ return err
+ }
+ }
+
+ // NOTE: this extension is currently not used by go-swagger (provided for information only)
+ sch.AddExtension("x-go-gen-location", GenLocation(parts))
+
+ // save cloned schema to definitions
+ schutils.Save(isn.Spec, newName, sch)
+
+ // keep track of created refs
+ if isn.flattenContext == nil {
+ continue
+ }
+
+ debugLog("track created ref: key=%s, newName=%s, isOAIGen=%t", key, newName, isOAIGen)
+ resolved := false
+
+ if _, ok := isn.flattenContext.newRefs[key]; ok {
+ resolved = isn.flattenContext.newRefs[key].resolved
+ }
+
+ isn.flattenContext.newRefs[key] = &newRef{
+ key: key,
+ newName: newName,
+ path: path.Join(definitionsPath, newName),
+ isOAIGen: isOAIGen,
+ resolved: resolved,
+ schema: sch,
+ }
+ }
+
+ return nil
+}
+
+// uniqifyName yields a unique name for a definition
+func uniqifyName(definitions spec.Definitions, name string) (string, bool) {
+ isOAIGen := false
+ if name == "" {
+ name = "oaiGen"
+ isOAIGen = true
+ }
+
+ if len(definitions) == 0 {
+ return name, isOAIGen
+ }
+
+ unq := true
+ for k := range definitions {
+ if strings.EqualFold(k, name) {
+ unq = false
+
+ break
+ }
+ }
+
+ if unq {
+ return name, isOAIGen
+ }
+
+ name += "OAIGen"
+ isOAIGen = true
+ var idx int
+ unique := name
+ _, known := definitions[unique]
+
+ for known {
+ idx++
+ unique = fmt.Sprintf("%s%d", name, idx)
+ _, known = definitions[unique]
+ }
+
+ return unique, isOAIGen
+}
+
+func namesFromKey(parts sortref.SplitKey, aschema *AnalyzedSchema, operations map[string]operations.OpRef) []string {
+ var (
+ baseNames [][]string
+ startIndex int
+ )
+
+ if parts.IsOperation() {
+ baseNames, startIndex = namesForOperation(parts, operations)
+ }
+
+ // definitions
+ if parts.IsDefinition() {
+ baseNames, startIndex = namesForDefinition(parts)
+ }
+
+ result := make([]string, 0, len(baseNames))
+ for _, segments := range baseNames {
+ nm := parts.BuildName(segments, startIndex, partAdder(aschema))
+ if nm == "" {
+ continue
+ }
+
+ result = append(result, nm)
+ }
+ sort.Strings(result)
+
+ return result
+}
+
+func namesForParam(parts sortref.SplitKey, operations map[string]operations.OpRef) ([][]string, int) {
+ var (
+ baseNames [][]string
+ startIndex int
+ )
+
+ piref := parts.PathItemRef()
+ if piref.String() != "" && parts.IsOperationParam() {
+ if op, ok := operations[piref.String()]; ok {
+ startIndex = 5
+ baseNames = append(baseNames, []string{op.ID, "params", "body"})
+ }
+ } else if parts.IsSharedOperationParam() {
+ pref := parts.PathRef()
+ for k, v := range operations {
+ if strings.HasPrefix(k, pref.String()) {
+ startIndex = 4
+ baseNames = append(baseNames, []string{v.ID, "params", "body"})
+ }
+ }
+ }
+
+ return baseNames, startIndex
+}
+
+func namesForOperation(parts sortref.SplitKey, operations map[string]operations.OpRef) ([][]string, int) {
+ var (
+ baseNames [][]string
+ startIndex int
+ )
+
+ // params
+ if parts.IsOperationParam() || parts.IsSharedOperationParam() {
+ baseNames, startIndex = namesForParam(parts, operations)
+ }
+
+ // responses
+ if parts.IsOperationResponse() {
+ piref := parts.PathItemRef()
+ if piref.String() != "" {
+ if op, ok := operations[piref.String()]; ok {
+ startIndex = 6
+ baseNames = append(baseNames, []string{op.ID, parts.ResponseName(), "body"})
+ }
+ }
+ }
+
+ return baseNames, startIndex
+}
+
+func namesForDefinition(parts sortref.SplitKey) ([][]string, int) {
+ nm := parts.DefinitionName()
+ if nm != "" {
+ return [][]string{{parts.DefinitionName()}}, 2
+ }
+
+ return [][]string{}, 0
+}
+
+// partAdder knows how to interpret a schema when it comes to build a name from parts
+func partAdder(aschema *AnalyzedSchema) sortref.PartAdder {
+ return func(part string) []string {
+ segments := make([]string, 0, 2)
+
+ if part == "items" || part == "additionalItems" {
+ if aschema.IsTuple || aschema.IsTupleWithExtra {
+ segments = append(segments, "tuple")
+ } else {
+ segments = append(segments, "items")
+ }
+
+ if part == "additionalItems" {
+ segments = append(segments, part)
+ }
+
+ return segments
+ }
+
+ segments = append(segments, part)
+
+ return segments
+ }
+}
+
+func nameFromRef(ref spec.Ref) string {
+ u := ref.GetURL()
+ if u.Fragment != "" {
+ return swag.ToJSONName(path.Base(u.Fragment))
+ }
+
+ if u.Path != "" {
+ bn := path.Base(u.Path)
+ if bn != "" && bn != "/" {
+ ext := path.Ext(bn)
+ if ext != "" {
+ return swag.ToJSONName(bn[:len(bn)-len(ext)])
+ }
+
+ return swag.ToJSONName(bn)
+ }
+ }
+
+ return swag.ToJSONName(strings.ReplaceAll(u.Host, ".", " "))
+}
+
+// GenLocation indicates from which section of the specification (models or operations) a definition has been created.
+//
+// This is reflected in the output spec with a "x-go-gen-location" extension. At the moment, this is is provided
+// for information only.
+func GenLocation(parts sortref.SplitKey) string {
+ switch {
+ case parts.IsOperation():
+ return "operations"
+ case parts.IsDefinition():
+ return "models"
+ default:
+ return ""
+ }
+}