podman

Форк
0
515 строк · 12.0 Кб
1
// Copyright 2015 go-swagger maintainers
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//    http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14

15
package analysis
16

17
import (
18
	"fmt"
19
	"reflect"
20

21
	"github.com/go-openapi/spec"
22
)
23

24
// Mixin modifies the primary swagger spec by adding the paths and
25
// definitions from the mixin specs. Top level parameters and
26
// responses from the mixins are also carried over. Operation id
27
// collisions are avoided by appending "Mixin<N>" but only if
28
// needed.
29
//
30
// The following parts of primary are subject to merge, filling empty details
31
//   - Info
32
//   - BasePath
33
//   - Host
34
//   - ExternalDocs
35
//
36
// Consider calling FixEmptyResponseDescriptions() on the modified primary
37
// if you read them from storage and they are valid to start with.
38
//
39
// Entries in "paths", "definitions", "parameters" and "responses" are
40
// added to the primary in the order of the given mixins. If the entry
41
// already exists in primary it is skipped with a warning message.
42
//
43
// The count of skipped entries (from collisions) is returned so any
44
// deviation from the number expected can flag a warning in your build
45
// scripts. Carefully review the collisions before accepting them;
46
// consider renaming things if possible.
47
//
48
// No key normalization takes place (paths, type defs,
49
// etc). Ensure they are canonical if your downstream tools do
50
// key normalization of any form.
51
//
52
// Merging schemes (http, https), and consumers/producers do not account for
53
// collisions.
54
func Mixin(primary *spec.Swagger, mixins ...*spec.Swagger) []string {
55
	skipped := make([]string, 0, len(mixins))
56
	opIds := getOpIds(primary)
57
	initPrimary(primary)
58

59
	for i, m := range mixins {
60
		skipped = append(skipped, mergeSwaggerProps(primary, m)...)
61

62
		skipped = append(skipped, mergeConsumes(primary, m)...)
63

64
		skipped = append(skipped, mergeProduces(primary, m)...)
65

66
		skipped = append(skipped, mergeTags(primary, m)...)
67

68
		skipped = append(skipped, mergeSchemes(primary, m)...)
69

70
		skipped = append(skipped, mergeSecurityDefinitions(primary, m)...)
71

72
		skipped = append(skipped, mergeSecurityRequirements(primary, m)...)
73

74
		skipped = append(skipped, mergeDefinitions(primary, m)...)
75

76
		// merging paths requires a map of operationIDs to work with
77
		skipped = append(skipped, mergePaths(primary, m, opIds, i)...)
78

79
		skipped = append(skipped, mergeParameters(primary, m)...)
80

81
		skipped = append(skipped, mergeResponses(primary, m)...)
82
	}
83

84
	return skipped
85
}
86

87
// getOpIds extracts all the paths.<path>.operationIds from the given
88
// spec and returns them as the keys in a map with 'true' values.
89
func getOpIds(s *spec.Swagger) map[string]bool {
90
	rv := make(map[string]bool)
91
	if s.Paths == nil {
92
		return rv
93
	}
94

95
	for _, v := range s.Paths.Paths {
96
		piops := pathItemOps(v)
97

98
		for _, op := range piops {
99
			rv[op.ID] = true
100
		}
101
	}
102

103
	return rv
104
}
105

106
func pathItemOps(p spec.PathItem) []*spec.Operation {
107
	var rv []*spec.Operation
108
	rv = appendOp(rv, p.Get)
109
	rv = appendOp(rv, p.Put)
110
	rv = appendOp(rv, p.Post)
111
	rv = appendOp(rv, p.Delete)
112
	rv = appendOp(rv, p.Head)
113
	rv = appendOp(rv, p.Patch)
114

115
	return rv
116
}
117

118
func appendOp(ops []*spec.Operation, op *spec.Operation) []*spec.Operation {
119
	if op == nil {
120
		return ops
121
	}
122

123
	return append(ops, op)
124
}
125

126
func mergeSecurityDefinitions(primary *spec.Swagger, m *spec.Swagger) (skipped []string) {
127
	for k, v := range m.SecurityDefinitions {
128
		if _, exists := primary.SecurityDefinitions[k]; exists {
129
			warn := fmt.Sprintf(
130
				"SecurityDefinitions entry '%v' already exists in primary or higher priority mixin, skipping\n", k)
131
			skipped = append(skipped, warn)
132

133
			continue
134
		}
135

136
		primary.SecurityDefinitions[k] = v
137
	}
138

139
	return
140
}
141

142
func mergeSecurityRequirements(primary *spec.Swagger, m *spec.Swagger) (skipped []string) {
143
	for _, v := range m.Security {
144
		found := false
145
		for _, vv := range primary.Security {
146
			if reflect.DeepEqual(v, vv) {
147
				found = true
148

149
				break
150
			}
151
		}
152

153
		if found {
154
			warn := fmt.Sprintf(
155
				"Security requirement: '%v' already exists in primary or higher priority mixin, skipping\n", v)
156
			skipped = append(skipped, warn)
157

158
			continue
159
		}
160
		primary.Security = append(primary.Security, v)
161
	}
162

163
	return
164
}
165

166
func mergeDefinitions(primary *spec.Swagger, m *spec.Swagger) (skipped []string) {
167
	for k, v := range m.Definitions {
168
		// assume name collisions represent IDENTICAL type. careful.
169
		if _, exists := primary.Definitions[k]; exists {
170
			warn := fmt.Sprintf(
171
				"definitions entry '%v' already exists in primary or higher priority mixin, skipping\n", k)
172
			skipped = append(skipped, warn)
173

174
			continue
175
		}
176
		primary.Definitions[k] = v
177
	}
178

179
	return
180
}
181

182
func mergePaths(primary *spec.Swagger, m *spec.Swagger, opIds map[string]bool, mixIndex int) (skipped []string) {
183
	if m.Paths != nil {
184
		for k, v := range m.Paths.Paths {
185
			if _, exists := primary.Paths.Paths[k]; exists {
186
				warn := fmt.Sprintf(
187
					"paths entry '%v' already exists in primary or higher priority mixin, skipping\n", k)
188
				skipped = append(skipped, warn)
189

190
				continue
191
			}
192

193
			// Swagger requires that operationIds be
194
			// unique within a spec. If we find a
195
			// collision we append "Mixin0" to the
196
			// operatoinId we are adding, where 0 is mixin
197
			// index.  We assume that operationIds with
198
			// all the proivded specs are already unique.
199
			piops := pathItemOps(v)
200
			for _, piop := range piops {
201
				if opIds[piop.ID] {
202
					piop.ID = fmt.Sprintf("%v%v%v", piop.ID, "Mixin", mixIndex)
203
				}
204
				opIds[piop.ID] = true
205
			}
206
			primary.Paths.Paths[k] = v
207
		}
208
	}
209

210
	return
211
}
212

213
func mergeParameters(primary *spec.Swagger, m *spec.Swagger) (skipped []string) {
214
	for k, v := range m.Parameters {
215
		// could try to rename on conflict but would
216
		// have to fix $refs in the mixin. Complain
217
		// for now
218
		if _, exists := primary.Parameters[k]; exists {
219
			warn := fmt.Sprintf(
220
				"top level parameters entry '%v' already exists in primary or higher priority mixin, skipping\n", k)
221
			skipped = append(skipped, warn)
222

223
			continue
224
		}
225
		primary.Parameters[k] = v
226
	}
227

228
	return
229
}
230

231
func mergeResponses(primary *spec.Swagger, m *spec.Swagger) (skipped []string) {
232
	for k, v := range m.Responses {
233
		// could try to rename on conflict but would
234
		// have to fix $refs in the mixin. Complain
235
		// for now
236
		if _, exists := primary.Responses[k]; exists {
237
			warn := fmt.Sprintf(
238
				"top level responses entry '%v' already exists in primary or higher priority mixin, skipping\n", k)
239
			skipped = append(skipped, warn)
240

241
			continue
242
		}
243
		primary.Responses[k] = v
244
	}
245

246
	return skipped
247
}
248

249
func mergeConsumes(primary *spec.Swagger, m *spec.Swagger) []string {
250
	for _, v := range m.Consumes {
251
		found := false
252
		for _, vv := range primary.Consumes {
253
			if v == vv {
254
				found = true
255

256
				break
257
			}
258
		}
259

260
		if found {
261
			// no warning here: we just skip it
262
			continue
263
		}
264
		primary.Consumes = append(primary.Consumes, v)
265
	}
266

267
	return []string{}
268
}
269

270
func mergeProduces(primary *spec.Swagger, m *spec.Swagger) []string {
271
	for _, v := range m.Produces {
272
		found := false
273
		for _, vv := range primary.Produces {
274
			if v == vv {
275
				found = true
276

277
				break
278
			}
279
		}
280

281
		if found {
282
			// no warning here: we just skip it
283
			continue
284
		}
285
		primary.Produces = append(primary.Produces, v)
286
	}
287

288
	return []string{}
289
}
290

291
func mergeTags(primary *spec.Swagger, m *spec.Swagger) (skipped []string) {
292
	for _, v := range m.Tags {
293
		found := false
294
		for _, vv := range primary.Tags {
295
			if v.Name == vv.Name {
296
				found = true
297

298
				break
299
			}
300
		}
301

302
		if found {
303
			warn := fmt.Sprintf(
304
				"top level tags entry with name '%v' already exists in primary or higher priority mixin, skipping\n",
305
				v.Name,
306
			)
307
			skipped = append(skipped, warn)
308

309
			continue
310
		}
311

312
		primary.Tags = append(primary.Tags, v)
313
	}
314

315
	return
316
}
317

318
func mergeSchemes(primary *spec.Swagger, m *spec.Swagger) []string {
319
	for _, v := range m.Schemes {
320
		found := false
321
		for _, vv := range primary.Schemes {
322
			if v == vv {
323
				found = true
324

325
				break
326
			}
327
		}
328

329
		if found {
330
			// no warning here: we just skip it
331
			continue
332
		}
333
		primary.Schemes = append(primary.Schemes, v)
334
	}
335

336
	return []string{}
337
}
338

339
func mergeSwaggerProps(primary *spec.Swagger, m *spec.Swagger) []string {
340
	var skipped, skippedInfo, skippedDocs []string
341

342
	primary.Extensions, skipped = mergeExtensions(primary.Extensions, m.Extensions)
343

344
	// merging details in swagger top properties
345
	if primary.Host == "" {
346
		primary.Host = m.Host
347
	}
348

349
	if primary.BasePath == "" {
350
		primary.BasePath = m.BasePath
351
	}
352

353
	if primary.Info == nil {
354
		primary.Info = m.Info
355
	} else if m.Info != nil {
356
		skippedInfo = mergeInfo(primary.Info, m.Info)
357
		skipped = append(skipped, skippedInfo...)
358
	}
359

360
	if primary.ExternalDocs == nil {
361
		primary.ExternalDocs = m.ExternalDocs
362
	} else if m != nil {
363
		skippedDocs = mergeExternalDocs(primary.ExternalDocs, m.ExternalDocs)
364
		skipped = append(skipped, skippedDocs...)
365
	}
366

367
	return skipped
368
}
369

370
// nolint: unparam
371
func mergeExternalDocs(primary *spec.ExternalDocumentation, m *spec.ExternalDocumentation) []string {
372
	if primary.Description == "" {
373
		primary.Description = m.Description
374
	}
375

376
	if primary.URL == "" {
377
		primary.URL = m.URL
378
	}
379

380
	return nil
381
}
382

383
func mergeInfo(primary *spec.Info, m *spec.Info) []string {
384
	var sk, skipped []string
385

386
	primary.Extensions, sk = mergeExtensions(primary.Extensions, m.Extensions)
387
	skipped = append(skipped, sk...)
388

389
	if primary.Description == "" {
390
		primary.Description = m.Description
391
	}
392

393
	if primary.Title == "" {
394
		primary.Description = m.Description
395
	}
396

397
	if primary.TermsOfService == "" {
398
		primary.TermsOfService = m.TermsOfService
399
	}
400

401
	if primary.Version == "" {
402
		primary.Version = m.Version
403
	}
404

405
	if primary.Contact == nil {
406
		primary.Contact = m.Contact
407
	} else if m.Contact != nil {
408
		var csk []string
409
		primary.Contact.Extensions, csk = mergeExtensions(primary.Contact.Extensions, m.Contact.Extensions)
410
		skipped = append(skipped, csk...)
411

412
		if primary.Contact.Name == "" {
413
			primary.Contact.Name = m.Contact.Name
414
		}
415

416
		if primary.Contact.URL == "" {
417
			primary.Contact.URL = m.Contact.URL
418
		}
419

420
		if primary.Contact.Email == "" {
421
			primary.Contact.Email = m.Contact.Email
422
		}
423
	}
424

425
	if primary.License == nil {
426
		primary.License = m.License
427
	} else if m.License != nil {
428
		var lsk []string
429
		primary.License.Extensions, lsk = mergeExtensions(primary.License.Extensions, m.License.Extensions)
430
		skipped = append(skipped, lsk...)
431

432
		if primary.License.Name == "" {
433
			primary.License.Name = m.License.Name
434
		}
435

436
		if primary.License.URL == "" {
437
			primary.License.URL = m.License.URL
438
		}
439
	}
440

441
	return skipped
442
}
443

444
func mergeExtensions(primary spec.Extensions, m spec.Extensions) (result spec.Extensions, skipped []string) {
445
	if primary == nil {
446
		result = m
447

448
		return
449
	}
450

451
	if m == nil {
452
		result = primary
453

454
		return
455
	}
456

457
	result = primary
458
	for k, v := range m {
459
		if _, found := primary[k]; found {
460
			skipped = append(skipped, k)
461

462
			continue
463
		}
464

465
		primary[k] = v
466
	}
467

468
	return
469
}
470

471
func initPrimary(primary *spec.Swagger) {
472
	if primary.SecurityDefinitions == nil {
473
		primary.SecurityDefinitions = make(map[string]*spec.SecurityScheme)
474
	}
475

476
	if primary.Security == nil {
477
		primary.Security = make([]map[string][]string, 0, 10)
478
	}
479

480
	if primary.Produces == nil {
481
		primary.Produces = make([]string, 0, 10)
482
	}
483

484
	if primary.Consumes == nil {
485
		primary.Consumes = make([]string, 0, 10)
486
	}
487

488
	if primary.Tags == nil {
489
		primary.Tags = make([]spec.Tag, 0, 10)
490
	}
491

492
	if primary.Schemes == nil {
493
		primary.Schemes = make([]string, 0, 10)
494
	}
495

496
	if primary.Paths == nil {
497
		primary.Paths = &spec.Paths{Paths: make(map[string]spec.PathItem)}
498
	}
499

500
	if primary.Paths.Paths == nil {
501
		primary.Paths.Paths = make(map[string]spec.PathItem)
502
	}
503

504
	if primary.Definitions == nil {
505
		primary.Definitions = make(spec.Definitions)
506
	}
507

508
	if primary.Parameters == nil {
509
		primary.Parameters = make(map[string]spec.Parameter)
510
	}
511

512
	if primary.Responses == nil {
513
		primary.Responses = make(map[string]spec.Response)
514
	}
515
}
516

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

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

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

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