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