podman

Форк
0
293 строки · 7.0 Кб
1
package analysis
2

3
import (
4
	"fmt"
5
	"path"
6
	"sort"
7
	"strings"
8

9
	"github.com/go-openapi/analysis/internal/flatten/operations"
10
	"github.com/go-openapi/analysis/internal/flatten/replace"
11
	"github.com/go-openapi/analysis/internal/flatten/schutils"
12
	"github.com/go-openapi/analysis/internal/flatten/sortref"
13
	"github.com/go-openapi/spec"
14
	"github.com/go-openapi/swag"
15
)
16

17
// InlineSchemaNamer finds a new name for an inlined type
18
type InlineSchemaNamer struct {
19
	Spec           *spec.Swagger
20
	Operations     map[string]operations.OpRef
21
	flattenContext *context
22
	opts           *FlattenOpts
23
}
24

25
// Name yields a new name for the inline schema
26
func (isn *InlineSchemaNamer) Name(key string, schema *spec.Schema, aschema *AnalyzedSchema) error {
27
	debugLog("naming inlined schema at %s", key)
28

29
	parts := sortref.KeyParts(key)
30
	for _, name := range namesFromKey(parts, aschema, isn.Operations) {
31
		if name == "" {
32
			continue
33
		}
34

35
		// create unique name
36
		newName, isOAIGen := uniqifyName(isn.Spec.Definitions, swag.ToJSONName(name))
37

38
		// clone schema
39
		sch := schutils.Clone(schema)
40

41
		// replace values on schema
42
		if err := replace.RewriteSchemaToRef(isn.Spec, key,
43
			spec.MustCreateRef(path.Join(definitionsPath, newName))); err != nil {
44
			return fmt.Errorf("error while creating definition %q from inline schema: %w", newName, err)
45
		}
46

47
		// rewrite any dependent $ref pointing to this place,
48
		// when not already pointing to a top-level definition.
49
		//
50
		// NOTE: this is important if such referers use arbitrary JSON pointers.
51
		an := New(isn.Spec)
52
		for k, v := range an.references.allRefs {
53
			r, erd := replace.DeepestRef(isn.opts.Swagger(), isn.opts.ExpandOpts(false), v)
54
			if erd != nil {
55
				return fmt.Errorf("at %s, %w", k, erd)
56
			}
57

58
			if isn.opts.flattenContext != nil {
59
				isn.opts.flattenContext.warnings = append(isn.opts.flattenContext.warnings, r.Warnings...)
60
			}
61

62
			if r.Ref.String() != key && (r.Ref.String() != path.Join(definitionsPath, newName) || path.Dir(v.String()) == definitionsPath) {
63
				continue
64
			}
65

66
			debugLog("found a $ref to a rewritten schema: %s points to %s", k, v.String())
67

68
			// rewrite $ref to the new target
69
			if err := replace.UpdateRef(isn.Spec, k,
70
				spec.MustCreateRef(path.Join(definitionsPath, newName))); err != nil {
71
				return err
72
			}
73
		}
74

75
		// NOTE: this extension is currently not used by go-swagger (provided for information only)
76
		sch.AddExtension("x-go-gen-location", GenLocation(parts))
77

78
		// save cloned schema to definitions
79
		schutils.Save(isn.Spec, newName, sch)
80

81
		// keep track of created refs
82
		if isn.flattenContext == nil {
83
			continue
84
		}
85

86
		debugLog("track created ref: key=%s, newName=%s, isOAIGen=%t", key, newName, isOAIGen)
87
		resolved := false
88

89
		if _, ok := isn.flattenContext.newRefs[key]; ok {
90
			resolved = isn.flattenContext.newRefs[key].resolved
91
		}
92

93
		isn.flattenContext.newRefs[key] = &newRef{
94
			key:      key,
95
			newName:  newName,
96
			path:     path.Join(definitionsPath, newName),
97
			isOAIGen: isOAIGen,
98
			resolved: resolved,
99
			schema:   sch,
100
		}
101
	}
102

103
	return nil
104
}
105

106
// uniqifyName yields a unique name for a definition
107
func uniqifyName(definitions spec.Definitions, name string) (string, bool) {
108
	isOAIGen := false
109
	if name == "" {
110
		name = "oaiGen"
111
		isOAIGen = true
112
	}
113

114
	if len(definitions) == 0 {
115
		return name, isOAIGen
116
	}
117

118
	unq := true
119
	for k := range definitions {
120
		if strings.EqualFold(k, name) {
121
			unq = false
122

123
			break
124
		}
125
	}
126

127
	if unq {
128
		return name, isOAIGen
129
	}
130

131
	name += "OAIGen"
132
	isOAIGen = true
133
	var idx int
134
	unique := name
135
	_, known := definitions[unique]
136

137
	for known {
138
		idx++
139
		unique = fmt.Sprintf("%s%d", name, idx)
140
		_, known = definitions[unique]
141
	}
142

143
	return unique, isOAIGen
144
}
145

146
func namesFromKey(parts sortref.SplitKey, aschema *AnalyzedSchema, operations map[string]operations.OpRef) []string {
147
	var (
148
		baseNames  [][]string
149
		startIndex int
150
	)
151

152
	if parts.IsOperation() {
153
		baseNames, startIndex = namesForOperation(parts, operations)
154
	}
155

156
	// definitions
157
	if parts.IsDefinition() {
158
		baseNames, startIndex = namesForDefinition(parts)
159
	}
160

161
	result := make([]string, 0, len(baseNames))
162
	for _, segments := range baseNames {
163
		nm := parts.BuildName(segments, startIndex, partAdder(aschema))
164
		if nm == "" {
165
			continue
166
		}
167

168
		result = append(result, nm)
169
	}
170
	sort.Strings(result)
171

172
	return result
173
}
174

175
func namesForParam(parts sortref.SplitKey, operations map[string]operations.OpRef) ([][]string, int) {
176
	var (
177
		baseNames  [][]string
178
		startIndex int
179
	)
180

181
	piref := parts.PathItemRef()
182
	if piref.String() != "" && parts.IsOperationParam() {
183
		if op, ok := operations[piref.String()]; ok {
184
			startIndex = 5
185
			baseNames = append(baseNames, []string{op.ID, "params", "body"})
186
		}
187
	} else if parts.IsSharedOperationParam() {
188
		pref := parts.PathRef()
189
		for k, v := range operations {
190
			if strings.HasPrefix(k, pref.String()) {
191
				startIndex = 4
192
				baseNames = append(baseNames, []string{v.ID, "params", "body"})
193
			}
194
		}
195
	}
196

197
	return baseNames, startIndex
198
}
199

200
func namesForOperation(parts sortref.SplitKey, operations map[string]operations.OpRef) ([][]string, int) {
201
	var (
202
		baseNames  [][]string
203
		startIndex int
204
	)
205

206
	// params
207
	if parts.IsOperationParam() || parts.IsSharedOperationParam() {
208
		baseNames, startIndex = namesForParam(parts, operations)
209
	}
210

211
	// responses
212
	if parts.IsOperationResponse() {
213
		piref := parts.PathItemRef()
214
		if piref.String() != "" {
215
			if op, ok := operations[piref.String()]; ok {
216
				startIndex = 6
217
				baseNames = append(baseNames, []string{op.ID, parts.ResponseName(), "body"})
218
			}
219
		}
220
	}
221

222
	return baseNames, startIndex
223
}
224

225
func namesForDefinition(parts sortref.SplitKey) ([][]string, int) {
226
	nm := parts.DefinitionName()
227
	if nm != "" {
228
		return [][]string{{parts.DefinitionName()}}, 2
229
	}
230

231
	return [][]string{}, 0
232
}
233

234
// partAdder knows how to interpret a schema when it comes to build a name from parts
235
func partAdder(aschema *AnalyzedSchema) sortref.PartAdder {
236
	return func(part string) []string {
237
		segments := make([]string, 0, 2)
238

239
		if part == "items" || part == "additionalItems" {
240
			if aschema.IsTuple || aschema.IsTupleWithExtra {
241
				segments = append(segments, "tuple")
242
			} else {
243
				segments = append(segments, "items")
244
			}
245

246
			if part == "additionalItems" {
247
				segments = append(segments, part)
248
			}
249

250
			return segments
251
		}
252

253
		segments = append(segments, part)
254

255
		return segments
256
	}
257
}
258

259
func nameFromRef(ref spec.Ref) string {
260
	u := ref.GetURL()
261
	if u.Fragment != "" {
262
		return swag.ToJSONName(path.Base(u.Fragment))
263
	}
264

265
	if u.Path != "" {
266
		bn := path.Base(u.Path)
267
		if bn != "" && bn != "/" {
268
			ext := path.Ext(bn)
269
			if ext != "" {
270
				return swag.ToJSONName(bn[:len(bn)-len(ext)])
271
			}
272

273
			return swag.ToJSONName(bn)
274
		}
275
	}
276

277
	return swag.ToJSONName(strings.ReplaceAll(u.Host, ".", " "))
278
}
279

280
// GenLocation indicates from which section of the specification (models or operations) a definition has been created.
281
//
282
// This is reflected in the output spec with a "x-go-gen-location" extension. At the moment, this is is provided
283
// for information only.
284
func GenLocation(parts sortref.SplitKey) string {
285
	switch {
286
	case parts.IsOperation():
287
		return "operations"
288
	case parts.IsDefinition():
289
		return "models"
290
	default:
291
		return ""
292
	}
293
}
294

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.