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