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