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 15package scan 16 17import ( 18 "fmt" 19 "regexp" 20 "strconv" 21 "strings" 22 23 "github.com/go-openapi/spec" 24) 25 26type validationBuilder interface { 27 SetMaximum(float64, bool) 28 SetMinimum(float64, bool) 29 SetMultipleOf(float64) 30 31 SetMinItems(int64) 32 SetMaxItems(int64) 33 34 SetMinLength(int64) 35 SetMaxLength(int64) 36 SetPattern(string) 37 38 SetUnique(bool) 39 SetEnum(string) 40 SetDefault(string) 41} 42 43type valueParser interface { 44 Parse([]string) error 45 Matches(string) bool 46} 47 48type setMaximum struct { 49 builder validationBuilder 50 rx *regexp.Regexp 51} 52 53func (sm *setMaximum) Parse(lines []string) error { 54 if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) { 55 return nil 56 } 57 matches := sm.rx.FindStringSubmatch(lines[0]) 58 if len(matches) > 2 && len(matches[2]) > 0 { 59 max, err := strconv.ParseFloat(matches[2], 64) 60 if err != nil { 61 return err 62 } 63 sm.builder.SetMaximum(max, matches[1] == "<") 64 } 65 return nil 66} 67 68func (sm *setMaximum) Matches(line string) bool { 69 return sm.rx.MatchString(line) 70} 71 72type setMinimum struct { 73 builder validationBuilder 74 rx *regexp.Regexp 75} 76 77func (sm *setMinimum) Matches(line string) bool { 78 return sm.rx.MatchString(line) 79} 80 81func (sm *setMinimum) Parse(lines []string) error { 82 if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) { 83 return nil 84 } 85 matches := sm.rx.FindStringSubmatch(lines[0]) 86 if len(matches) > 2 && len(matches[2]) > 0 { 87 min, err := strconv.ParseFloat(matches[2], 64) 88 if err != nil { 89 return err 90 } 91 sm.builder.SetMinimum(min, matches[1] == ">") 92 } 93 return nil 94} 95 96type setMultipleOf struct { 97 builder validationBuilder 98 rx *regexp.Regexp 99} 100 101func (sm *setMultipleOf) Matches(line string) bool { 102 return sm.rx.MatchString(line) 103} 104 105func (sm *setMultipleOf) Parse(lines []string) error { 106 if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) { 107 return nil 108 } 109 matches := sm.rx.FindStringSubmatch(lines[0]) 110 if len(matches) > 2 && len(matches[1]) > 0 { 111 multipleOf, err := strconv.ParseFloat(matches[1], 64) 112 if err != nil { 113 return err 114 } 115 sm.builder.SetMultipleOf(multipleOf) 116 } 117 return nil 118} 119 120type setMaxItems struct { 121 builder validationBuilder 122 rx *regexp.Regexp 123} 124 125func (sm *setMaxItems) Matches(line string) bool { 126 return sm.rx.MatchString(line) 127} 128 129func (sm *setMaxItems) Parse(lines []string) error { 130 if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) { 131 return nil 132 } 133 matches := sm.rx.FindStringSubmatch(lines[0]) 134 if len(matches) > 1 && len(matches[1]) > 0 { 135 maxItems, err := strconv.ParseInt(matches[1], 10, 64) 136 if err != nil { 137 return err 138 } 139 sm.builder.SetMaxItems(maxItems) 140 } 141 return nil 142} 143 144type setMinItems struct { 145 builder validationBuilder 146 rx *regexp.Regexp 147} 148 149func (sm *setMinItems) Matches(line string) bool { 150 return sm.rx.MatchString(line) 151} 152 153func (sm *setMinItems) Parse(lines []string) error { 154 if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) { 155 return nil 156 } 157 matches := sm.rx.FindStringSubmatch(lines[0]) 158 if len(matches) > 1 && len(matches[1]) > 0 { 159 minItems, err := strconv.ParseInt(matches[1], 10, 64) 160 if err != nil { 161 return err 162 } 163 sm.builder.SetMinItems(minItems) 164 } 165 return nil 166} 167 168type setMaxLength struct { 169 builder validationBuilder 170 rx *regexp.Regexp 171} 172 173func (sm *setMaxLength) Parse(lines []string) error { 174 if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) { 175 return nil 176 } 177 matches := sm.rx.FindStringSubmatch(lines[0]) 178 if len(matches) > 1 && len(matches[1]) > 0 { 179 maxLength, err := strconv.ParseInt(matches[1], 10, 64) 180 if err != nil { 181 return err 182 } 183 sm.builder.SetMaxLength(maxLength) 184 } 185 return nil 186} 187 188func (sm *setMaxLength) Matches(line string) bool { 189 return sm.rx.MatchString(line) 190} 191 192type setMinLength struct { 193 builder validationBuilder 194 rx *regexp.Regexp 195} 196 197func (sm *setMinLength) Parse(lines []string) error { 198 if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) { 199 return nil 200 } 201 matches := sm.rx.FindStringSubmatch(lines[0]) 202 if len(matches) > 1 && len(matches[1]) > 0 { 203 minLength, err := strconv.ParseInt(matches[1], 10, 64) 204 if err != nil { 205 return err 206 } 207 sm.builder.SetMinLength(minLength) 208 } 209 return nil 210} 211 212func (sm *setMinLength) Matches(line string) bool { 213 return sm.rx.MatchString(line) 214} 215 216type setPattern struct { 217 builder validationBuilder 218 rx *regexp.Regexp 219} 220 221func (sm *setPattern) Parse(lines []string) error { 222 if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) { 223 return nil 224 } 225 matches := sm.rx.FindStringSubmatch(lines[0]) 226 if len(matches) > 1 && len(matches[1]) > 0 { 227 sm.builder.SetPattern(matches[1]) 228 } 229 return nil 230} 231 232func (sm *setPattern) Matches(line string) bool { 233 return sm.rx.MatchString(line) 234} 235 236type setCollectionFormat struct { 237 builder operationValidationBuilder 238 rx *regexp.Regexp 239} 240 241func (sm *setCollectionFormat) Parse(lines []string) error { 242 if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) { 243 return nil 244 } 245 matches := sm.rx.FindStringSubmatch(lines[0]) 246 if len(matches) > 1 && len(matches[1]) > 0 { 247 sm.builder.SetCollectionFormat(matches[1]) 248 } 249 return nil 250} 251 252func (sm *setCollectionFormat) Matches(line string) bool { 253 return sm.rx.MatchString(line) 254} 255 256type setUnique struct { 257 builder validationBuilder 258 rx *regexp.Regexp 259} 260 261func (su *setUnique) Matches(line string) bool { 262 return su.rx.MatchString(line) 263} 264 265func (su *setUnique) Parse(lines []string) error { 266 if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) { 267 return nil 268 } 269 matches := su.rx.FindStringSubmatch(lines[0]) 270 if len(matches) > 1 && len(matches[1]) > 0 { 271 req, err := strconv.ParseBool(matches[1]) 272 if err != nil { 273 return err 274 } 275 su.builder.SetUnique(req) 276 } 277 return nil 278} 279 280type setEnum struct { 281 builder validationBuilder 282 rx *regexp.Regexp 283} 284 285func (se *setEnum) Matches(line string) bool { 286 return se.rx.MatchString(line) 287} 288 289func (se *setEnum) Parse(lines []string) error { 290 if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) { 291 return nil 292 } 293 matches := se.rx.FindStringSubmatch(lines[0]) 294 if len(matches) > 1 && len(matches[1]) > 0 { 295 se.builder.SetEnum(matches[1]) 296 } 297 return nil 298} 299 300type setDefault struct { 301 builder validationBuilder 302 rx *regexp.Regexp 303} 304 305func (sd *setDefault) Matches(line string) bool { 306 return sd.rx.MatchString(line) 307} 308 309func (sd *setDefault) Parse(lines []string) error { 310 if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) { 311 return nil 312 } 313 matches := sd.rx.FindStringSubmatch(lines[0]) 314 if len(matches) > 1 && len(matches[1]) > 0 { 315 sd.builder.SetDefault(matches[1]) 316 } 317 return nil 318} 319 320type matchOnlyParam struct { 321 tgt *spec.Parameter 322 rx *regexp.Regexp 323} 324 325func (mo *matchOnlyParam) Matches(line string) bool { 326 return mo.rx.MatchString(line) 327} 328 329func (mo *matchOnlyParam) Parse(lines []string) error { 330 return nil 331} 332 333type setRequiredParam struct { 334 tgt *spec.Parameter 335} 336 337func (su *setRequiredParam) Matches(line string) bool { 338 return rxRequired.MatchString(line) 339} 340 341func (su *setRequiredParam) Parse(lines []string) error { 342 if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) { 343 return nil 344 } 345 matches := rxRequired.FindStringSubmatch(lines[0]) 346 if len(matches) > 1 && len(matches[1]) > 0 { 347 req, err := strconv.ParseBool(matches[1]) 348 if err != nil { 349 return err 350 } 351 su.tgt.Required = req 352 } 353 return nil 354} 355 356type setReadOnlySchema struct { 357 tgt *spec.Schema 358} 359 360func (su *setReadOnlySchema) Matches(line string) bool { 361 return rxReadOnly.MatchString(line) 362} 363 364func (su *setReadOnlySchema) Parse(lines []string) error { 365 if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) { 366 return nil 367 } 368 matches := rxReadOnly.FindStringSubmatch(lines[0]) 369 if len(matches) > 1 && len(matches[1]) > 0 { 370 req, err := strconv.ParseBool(matches[1]) 371 if err != nil { 372 return err 373 } 374 su.tgt.ReadOnly = req 375 } 376 return nil 377} 378 379type setDiscriminator struct { 380 schema *spec.Schema 381 field string 382} 383 384func (su *setDiscriminator) Matches(line string) bool { 385 return rxDiscriminator.MatchString(line) 386} 387 388func (su *setDiscriminator) Parse(lines []string) error { 389 if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) { 390 return nil 391 } 392 matches := rxDiscriminator.FindStringSubmatch(lines[0]) 393 if len(matches) > 1 && len(matches[1]) > 0 { 394 req, err := strconv.ParseBool(matches[1]) 395 if err != nil { 396 return err 397 } 398 if req { 399 su.schema.Discriminator = su.field 400 } else { 401 if su.schema.Discriminator == su.field { 402 su.schema.Discriminator = "" 403 } 404 } 405 } 406 return nil 407} 408 409type setRequiredSchema struct { 410 schema *spec.Schema 411 field string 412} 413 414func (su *setRequiredSchema) Matches(line string) bool { 415 return rxRequired.MatchString(line) 416} 417 418func (su *setRequiredSchema) Parse(lines []string) error { 419 if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) { 420 return nil 421 } 422 matches := rxRequired.FindStringSubmatch(lines[0]) 423 if len(matches) > 1 && len(matches[1]) > 0 { 424 req, err := strconv.ParseBool(matches[1]) 425 if err != nil { 426 return err 427 } 428 midx := -1 429 for i, nm := range su.schema.Required { 430 if nm == su.field { 431 midx = i 432 break 433 } 434 } 435 if req { 436 if midx < 0 { 437 su.schema.Required = append(su.schema.Required, su.field) 438 } 439 } else if midx >= 0 { 440 su.schema.Required = append(su.schema.Required[:midx], su.schema.Required[midx+1:]...) 441 } 442 } 443 return nil 444} 445 446func newMultilineDropEmptyParser(rx *regexp.Regexp, set func([]string)) *multiLineDropEmptyParser { 447 return &multiLineDropEmptyParser{ 448 rx: rx, 449 set: set, 450 } 451} 452 453type multiLineDropEmptyParser struct { 454 set func([]string) 455 rx *regexp.Regexp 456} 457 458func (m *multiLineDropEmptyParser) Matches(line string) bool { 459 return m.rx.MatchString(line) 460} 461 462func (m *multiLineDropEmptyParser) Parse(lines []string) error { 463 m.set(removeEmptyLines(lines)) 464 return nil 465} 466 467func newSetSchemes(set func([]string)) *setSchemes { 468 return &setSchemes{ 469 set: set, 470 rx: rxSchemes, 471 } 472} 473 474type setSchemes struct { 475 set func([]string) 476 rx *regexp.Regexp 477} 478 479func (ss *setSchemes) Matches(line string) bool { 480 return ss.rx.MatchString(line) 481} 482 483func (ss *setSchemes) Parse(lines []string) error { 484 if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) { 485 return nil 486 } 487 matches := ss.rx.FindStringSubmatch(lines[0]) 488 if len(matches) > 1 && len(matches[1]) > 0 { 489 sch := strings.Split(matches[1], ", ") 490 491 var schemes []string 492 for _, s := range sch { 493 ts := strings.TrimSpace(s) 494 if ts != "" { 495 schemes = append(schemes, ts) 496 } 497 } 498 ss.set(schemes) 499 } 500 return nil 501} 502 503func newSetSecurityDefinitions(rx *regexp.Regexp, setter func([]map[string][]string)) *setSecurityDefinitions { 504 return &setSecurityDefinitions{ 505 set: setter, 506 rx: rx, 507 } 508} 509 510type setSecurityDefinitions struct { 511 set func([]map[string][]string) 512 rx *regexp.Regexp 513} 514 515func (ss *setSecurityDefinitions) Matches(line string) bool { 516 return ss.rx.MatchString(line) 517} 518 519func (ss *setSecurityDefinitions) Parse(lines []string) error { 520 if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) { 521 return nil 522 } 523 524 var result []map[string][]string 525 for _, line := range lines { 526 kv := strings.SplitN(line, ":", 2) 527 scopes := []string{} 528 var key string 529 530 if len(kv) > 1 { 531 scs := strings.Split(kv[1], ",") 532 for _, scope := range scs { 533 tr := strings.TrimSpace(scope) 534 if tr != "" { 535 tr = strings.SplitAfter(tr, " ")[0] 536 scopes = append(scopes, strings.TrimSpace(tr)) 537 } 538 } 539 540 key = strings.TrimSpace(kv[0]) 541 542 result = append(result, map[string][]string{key: scopes}) 543 } 544 } 545 ss.set(result) 546 return nil 547} 548 549func newSetResponses(definitions map[string]spec.Schema, responses map[string]spec.Response, setter func(*spec.Response, map[int]spec.Response)) *setOpResponses { 550 return &setOpResponses{ 551 set: setter, 552 rx: rxResponses, 553 definitions: definitions, 554 responses: responses, 555 } 556} 557 558type setOpResponses struct { 559 set func(*spec.Response, map[int]spec.Response) 560 rx *regexp.Regexp 561 definitions map[string]spec.Schema 562 responses map[string]spec.Response 563} 564 565func (ss *setOpResponses) Matches(line string) bool { 566 return ss.rx.MatchString(line) 567} 568 569//Tag used when specifying a response to point to a defined swagger:response 570const ResponseTag = "response" 571 572//Tag used when specifying a response to point to a model/schema 573const BodyTag = "body" 574 575//Tag used when specifying a response that gives a description of the response 576const DescriptionTag = "description" 577 578func parseTags(line string) (modelOrResponse string, arrays int, isDefinitionRef bool, description string, err error) { 579 tags := strings.Split(line, " ") 580 parsedModelOrResponse := false 581 parsedDescription := false 582 583 for i, tagAndValue := range tags { 584 tagValList := strings.SplitN(tagAndValue, ":", 2) 585 var tag, value string 586 if len(tagValList) > 1 { 587 tag = tagValList[0] 588 value = tagValList[1] 589 } else { 590 //TODO: Print a warning, and in the long term, do not support not tagged values 591 //Add a default tag if none is supplied 592 if i == 0 { 593 tag = ResponseTag 594 } else { 595 tag = DescriptionTag 596 } 597 value = tagValList[0] 598 } 599 600 foundModelOrResponse := false 601 if !parsedModelOrResponse { 602 if tag == BodyTag { 603 foundModelOrResponse = true 604 isDefinitionRef = true 605 } 606 if tag == ResponseTag { 607 foundModelOrResponse = true 608 isDefinitionRef = false 609 } 610 } 611 if foundModelOrResponse { 612 //Read the model or response tag 613 parsedModelOrResponse = true 614 //Check for nested arrays 615 arrays = 0 616 for strings.HasPrefix(value, "[]") { 617 arrays++ 618 value = value[2:] 619 } 620 //What's left over is the model name 621 modelOrResponse = value 622 } else { 623 foundDescription := false 624 if !parsedDescription { 625 if tag == DescriptionTag { 626 foundDescription = true 627 } 628 } 629 if foundDescription { 630 //Descriptions are special, they make they read the rest of the line 631 descriptionWords := []string{value} 632 if i < len(tags)-1 { 633 descriptionWords = append(descriptionWords, tags[i+1:len(tags)]...) 634 } 635 description = strings.Join(descriptionWords, " ") 636 parsedDescription = true 637 break 638 } else { 639 if tag == ResponseTag || tag == BodyTag || tag == DescriptionTag { 640 err = fmt.Errorf("Found valid tag %s, but not in a valid position", tag) 641 } else { 642 err = fmt.Errorf("Found invalid tag: %s", tag) 643 } 644 //return error 645 return 646 } 647 } 648 } 649 650 //TODO: Maybe do, if !parsedModelOrResponse && !parsedDescription {return some error} 651 return 652} 653 654func (ss *setOpResponses) Parse(lines []string) error { 655 if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) { 656 return nil 657 } 658 659 var def *spec.Response 660 var scr map[int]spec.Response 661 662 for _, line := range lines { 663 kv := strings.SplitN(line, ":", 2) 664 var key, value string 665 666 if len(kv) > 1 { 667 key = strings.TrimSpace(kv[0]) 668 if key == "" { 669 // this must be some weird empty line 670 continue 671 } 672 value = strings.TrimSpace(kv[1]) 673 if value == "" { 674 var resp spec.Response 675 if strings.EqualFold("default", key) { 676 if def == nil { 677 def = &resp 678 } 679 } else { 680 if sc, err := strconv.Atoi(key); err == nil { 681 if scr == nil { 682 scr = make(map[int]spec.Response) 683 } 684 scr[sc] = resp 685 } 686 } 687 continue 688 } 689 refTarget, arrays, isDefinitionRef, description, err := parseTags(value) 690 if err != nil { 691 return err 692 } 693 //A possible exception for having a definition 694 if _, ok := ss.responses[refTarget]; !ok { 695 if _, ok := ss.definitions[refTarget]; ok { 696 isDefinitionRef = true 697 } 698 } 699 700 var ref spec.Ref 701 if isDefinitionRef { 702 if description == "" { 703 description = refTarget 704 } 705 ref, err = spec.NewRef("#/definitions/" + refTarget) 706 } else { 707 ref, err = spec.NewRef("#/responses/" + refTarget) 708 } 709 if err != nil { 710 return err 711 } 712 713 var resp spec.Response 714 715 if !isDefinitionRef { 716 resp.Ref = ref 717 } else { 718 resp.Schema = new(spec.Schema) 719 resp.Description = description 720 if arrays == 0 { 721 resp.Schema.Ref = ref 722 } else { 723 cs := resp.Schema 724 for i := 0; i < arrays; i++ { 725 cs.Typed("array", "") 726 cs.Items = new(spec.SchemaOrArray) 727 cs.Items.Schema = new(spec.Schema) 728 cs = cs.Items.Schema 729 } 730 cs.Ref = ref 731 } 732 } 733 734 if strings.EqualFold("default", key) { 735 if def == nil { 736 def = &resp 737 } 738 } else { 739 if sc, err := strconv.Atoi(key); err == nil { 740 if scr == nil { 741 scr = make(map[int]spec.Response) 742 } 743 scr[sc] = resp 744 } 745 } 746 } 747 } 748 ss.set(def, scr) 749 return nil 750} 751