gitea/vendor/github.com/go-swagger/go-swagger/scan/validators.go

829 lines
19 KiB
Go

// +build !go1.11
// Copyright 2015 go-swagger maintainers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package scan
import (
"encoding/json"
"fmt"
"regexp"
"strconv"
"strings"
"github.com/go-openapi/spec"
)
type validationBuilder interface {
SetMaximum(float64, bool)
SetMinimum(float64, bool)
SetMultipleOf(float64)
SetMinItems(int64)
SetMaxItems(int64)
SetMinLength(int64)
SetMaxLength(int64)
SetPattern(string)
SetUnique(bool)
SetEnum(string)
SetDefault(interface{})
SetExample(interface{})
}
type valueParser interface {
Parse([]string) error
Matches(string) bool
}
type setMaximum struct {
builder validationBuilder
rx *regexp.Regexp
}
func (sm *setMaximum) Parse(lines []string) error {
if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) {
return nil
}
matches := sm.rx.FindStringSubmatch(lines[0])
if len(matches) > 2 && len(matches[2]) > 0 {
max, err := strconv.ParseFloat(matches[2], 64)
if err != nil {
return err
}
sm.builder.SetMaximum(max, matches[1] == "<")
}
return nil
}
func (sm *setMaximum) Matches(line string) bool {
return sm.rx.MatchString(line)
}
type setMinimum struct {
builder validationBuilder
rx *regexp.Regexp
}
func (sm *setMinimum) Matches(line string) bool {
return sm.rx.MatchString(line)
}
func (sm *setMinimum) Parse(lines []string) error {
if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) {
return nil
}
matches := sm.rx.FindStringSubmatch(lines[0])
if len(matches) > 2 && len(matches[2]) > 0 {
min, err := strconv.ParseFloat(matches[2], 64)
if err != nil {
return err
}
sm.builder.SetMinimum(min, matches[1] == ">")
}
return nil
}
type setMultipleOf struct {
builder validationBuilder
rx *regexp.Regexp
}
func (sm *setMultipleOf) Matches(line string) bool {
return sm.rx.MatchString(line)
}
func (sm *setMultipleOf) Parse(lines []string) error {
if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) {
return nil
}
matches := sm.rx.FindStringSubmatch(lines[0])
if len(matches) > 2 && len(matches[1]) > 0 {
multipleOf, err := strconv.ParseFloat(matches[1], 64)
if err != nil {
return err
}
sm.builder.SetMultipleOf(multipleOf)
}
return nil
}
type setMaxItems struct {
builder validationBuilder
rx *regexp.Regexp
}
func (sm *setMaxItems) Matches(line string) bool {
return sm.rx.MatchString(line)
}
func (sm *setMaxItems) Parse(lines []string) error {
if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) {
return nil
}
matches := sm.rx.FindStringSubmatch(lines[0])
if len(matches) > 1 && len(matches[1]) > 0 {
maxItems, err := strconv.ParseInt(matches[1], 10, 64)
if err != nil {
return err
}
sm.builder.SetMaxItems(maxItems)
}
return nil
}
type setMinItems struct {
builder validationBuilder
rx *regexp.Regexp
}
func (sm *setMinItems) Matches(line string) bool {
return sm.rx.MatchString(line)
}
func (sm *setMinItems) Parse(lines []string) error {
if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) {
return nil
}
matches := sm.rx.FindStringSubmatch(lines[0])
if len(matches) > 1 && len(matches[1]) > 0 {
minItems, err := strconv.ParseInt(matches[1], 10, 64)
if err != nil {
return err
}
sm.builder.SetMinItems(minItems)
}
return nil
}
type setMaxLength struct {
builder validationBuilder
rx *regexp.Regexp
}
func (sm *setMaxLength) Parse(lines []string) error {
if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) {
return nil
}
matches := sm.rx.FindStringSubmatch(lines[0])
if len(matches) > 1 && len(matches[1]) > 0 {
maxLength, err := strconv.ParseInt(matches[1], 10, 64)
if err != nil {
return err
}
sm.builder.SetMaxLength(maxLength)
}
return nil
}
func (sm *setMaxLength) Matches(line string) bool {
return sm.rx.MatchString(line)
}
type setMinLength struct {
builder validationBuilder
rx *regexp.Regexp
}
func (sm *setMinLength) Parse(lines []string) error {
if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) {
return nil
}
matches := sm.rx.FindStringSubmatch(lines[0])
if len(matches) > 1 && len(matches[1]) > 0 {
minLength, err := strconv.ParseInt(matches[1], 10, 64)
if err != nil {
return err
}
sm.builder.SetMinLength(minLength)
}
return nil
}
func (sm *setMinLength) Matches(line string) bool {
return sm.rx.MatchString(line)
}
type setPattern struct {
builder validationBuilder
rx *regexp.Regexp
}
func (sm *setPattern) Parse(lines []string) error {
if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) {
return nil
}
matches := sm.rx.FindStringSubmatch(lines[0])
if len(matches) > 1 && len(matches[1]) > 0 {
sm.builder.SetPattern(matches[1])
}
return nil
}
func (sm *setPattern) Matches(line string) bool {
return sm.rx.MatchString(line)
}
type setCollectionFormat struct {
builder operationValidationBuilder
rx *regexp.Regexp
}
func (sm *setCollectionFormat) Parse(lines []string) error {
if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) {
return nil
}
matches := sm.rx.FindStringSubmatch(lines[0])
if len(matches) > 1 && len(matches[1]) > 0 {
sm.builder.SetCollectionFormat(matches[1])
}
return nil
}
func (sm *setCollectionFormat) Matches(line string) bool {
return sm.rx.MatchString(line)
}
type setUnique struct {
builder validationBuilder
rx *regexp.Regexp
}
func (su *setUnique) Matches(line string) bool {
return su.rx.MatchString(line)
}
func (su *setUnique) Parse(lines []string) error {
if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) {
return nil
}
matches := su.rx.FindStringSubmatch(lines[0])
if len(matches) > 1 && len(matches[1]) > 0 {
req, err := strconv.ParseBool(matches[1])
if err != nil {
return err
}
su.builder.SetUnique(req)
}
return nil
}
type setEnum struct {
builder validationBuilder
rx *regexp.Regexp
}
func (se *setEnum) Matches(line string) bool {
return se.rx.MatchString(line)
}
func (se *setEnum) Parse(lines []string) error {
if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) {
return nil
}
matches := se.rx.FindStringSubmatch(lines[0])
if len(matches) > 1 && len(matches[1]) > 0 {
se.builder.SetEnum(matches[1])
}
return nil
}
func parseValueFromSchema(s string, schema *spec.SimpleSchema) (interface{}, error) {
if schema != nil {
switch strings.Trim(schema.TypeName(), "\"") {
case "integer", "int", "int64", "int32", "int16":
return strconv.Atoi(s)
case "bool", "boolean":
return strconv.ParseBool(s)
case "number", "float64", "float32":
return strconv.ParseFloat(s, 64)
case "object":
var obj map[string]interface{}
if err := json.Unmarshal([]byte(s), &obj); err != nil {
// If we can't parse it, just return the string.
return s, nil
}
return obj, nil
case "array":
var slice []interface{}
if err := json.Unmarshal([]byte(s), &slice); err != nil {
// If we can't parse it, just return the string.
return s, nil
}
return slice, nil
default:
return s, nil
}
} else {
return s, nil
}
}
type setDefault struct {
scheme *spec.SimpleSchema
builder validationBuilder
rx *regexp.Regexp
}
func (sd *setDefault) Matches(line string) bool {
return sd.rx.MatchString(line)
}
func (sd *setDefault) Parse(lines []string) error {
if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) {
return nil
}
matches := sd.rx.FindStringSubmatch(lines[0])
if len(matches) > 1 && len(matches[1]) > 0 {
d, err := parseValueFromSchema(matches[1], sd.scheme)
if err != nil {
return err
}
sd.builder.SetDefault(d)
}
return nil
}
type setExample struct {
scheme *spec.SimpleSchema
builder validationBuilder
rx *regexp.Regexp
}
func (se *setExample) Matches(line string) bool {
return se.rx.MatchString(line)
}
func (se *setExample) Parse(lines []string) error {
if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) {
return nil
}
matches := se.rx.FindStringSubmatch(lines[0])
if len(matches) > 1 && len(matches[1]) > 0 {
d, err := parseValueFromSchema(matches[1], se.scheme)
if err != nil {
return err
}
se.builder.SetExample(d)
}
return nil
}
type matchOnlyParam struct {
tgt *spec.Parameter
rx *regexp.Regexp
}
func (mo *matchOnlyParam) Matches(line string) bool {
return mo.rx.MatchString(line)
}
func (mo *matchOnlyParam) Parse(lines []string) error {
return nil
}
type setRequiredParam struct {
tgt *spec.Parameter
}
func (su *setRequiredParam) Matches(line string) bool {
return rxRequired.MatchString(line)
}
func (su *setRequiredParam) Parse(lines []string) error {
if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) {
return nil
}
matches := rxRequired.FindStringSubmatch(lines[0])
if len(matches) > 1 && len(matches[1]) > 0 {
req, err := strconv.ParseBool(matches[1])
if err != nil {
return err
}
su.tgt.Required = req
}
return nil
}
type setReadOnlySchema struct {
tgt *spec.Schema
}
func (su *setReadOnlySchema) Matches(line string) bool {
return rxReadOnly.MatchString(line)
}
func (su *setReadOnlySchema) Parse(lines []string) error {
if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) {
return nil
}
matches := rxReadOnly.FindStringSubmatch(lines[0])
if len(matches) > 1 && len(matches[1]) > 0 {
req, err := strconv.ParseBool(matches[1])
if err != nil {
return err
}
su.tgt.ReadOnly = req
}
return nil
}
type setDiscriminator struct {
schema *spec.Schema
field string
}
func (su *setDiscriminator) Matches(line string) bool {
return rxDiscriminator.MatchString(line)
}
func (su *setDiscriminator) Parse(lines []string) error {
if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) {
return nil
}
matches := rxDiscriminator.FindStringSubmatch(lines[0])
if len(matches) > 1 && len(matches[1]) > 0 {
req, err := strconv.ParseBool(matches[1])
if err != nil {
return err
}
if req {
su.schema.Discriminator = su.field
} else {
if su.schema.Discriminator == su.field {
su.schema.Discriminator = ""
}
}
}
return nil
}
type setRequiredSchema struct {
schema *spec.Schema
field string
}
func (su *setRequiredSchema) Matches(line string) bool {
return rxRequired.MatchString(line)
}
func (su *setRequiredSchema) Parse(lines []string) error {
if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) {
return nil
}
matches := rxRequired.FindStringSubmatch(lines[0])
if len(matches) > 1 && len(matches[1]) > 0 {
req, err := strconv.ParseBool(matches[1])
if err != nil {
return err
}
midx := -1
for i, nm := range su.schema.Required {
if nm == su.field {
midx = i
break
}
}
if req {
if midx < 0 {
su.schema.Required = append(su.schema.Required, su.field)
}
} else if midx >= 0 {
su.schema.Required = append(su.schema.Required[:midx], su.schema.Required[midx+1:]...)
}
}
return nil
}
func newMultilineDropEmptyParser(rx *regexp.Regexp, set func([]string)) *multiLineDropEmptyParser {
return &multiLineDropEmptyParser{
rx: rx,
set: set,
}
}
type multiLineDropEmptyParser struct {
set func([]string)
rx *regexp.Regexp
}
func (m *multiLineDropEmptyParser) Matches(line string) bool {
return m.rx.MatchString(line)
}
func (m *multiLineDropEmptyParser) Parse(lines []string) error {
m.set(removeEmptyLines(lines))
return nil
}
func newSetSchemes(set func([]string)) *setSchemes {
return &setSchemes{
set: set,
rx: rxSchemes,
}
}
type setSchemes struct {
set func([]string)
rx *regexp.Regexp
}
func (ss *setSchemes) Matches(line string) bool {
return ss.rx.MatchString(line)
}
func (ss *setSchemes) Parse(lines []string) error {
if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) {
return nil
}
matches := ss.rx.FindStringSubmatch(lines[0])
if len(matches) > 1 && len(matches[1]) > 0 {
sch := strings.Split(matches[1], ", ")
var schemes []string
for _, s := range sch {
ts := strings.TrimSpace(s)
if ts != "" {
schemes = append(schemes, ts)
}
}
ss.set(schemes)
}
return nil
}
func newSetSecurity(rx *regexp.Regexp, setter func([]map[string][]string)) *setSecurity {
return &setSecurity{
set: setter,
rx: rx,
}
}
type setSecurity struct {
set func([]map[string][]string)
rx *regexp.Regexp
}
func (ss *setSecurity) Matches(line string) bool {
return ss.rx.MatchString(line)
}
func (ss *setSecurity) Parse(lines []string) error {
if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) {
return nil
}
var result []map[string][]string
for _, line := range lines {
kv := strings.SplitN(line, ":", 2)
scopes := []string{}
var key string
if len(kv) > 1 {
scs := strings.Split(kv[1], ",")
for _, scope := range scs {
tr := strings.TrimSpace(scope)
if tr != "" {
tr = strings.SplitAfter(tr, " ")[0]
scopes = append(scopes, strings.TrimSpace(tr))
}
}
key = strings.TrimSpace(kv[0])
result = append(result, map[string][]string{key: scopes})
}
}
ss.set(result)
return nil
}
func newSetResponses(definitions map[string]spec.Schema, responses map[string]spec.Response, setter func(*spec.Response, map[int]spec.Response)) *setOpResponses {
return &setOpResponses{
set: setter,
rx: rxResponses,
definitions: definitions,
responses: responses,
}
}
type setOpResponses struct {
set func(*spec.Response, map[int]spec.Response)
rx *regexp.Regexp
definitions map[string]spec.Schema
responses map[string]spec.Response
}
func (ss *setOpResponses) Matches(line string) bool {
return ss.rx.MatchString(line)
}
//ResponseTag used when specifying a response to point to a defined swagger:response
const ResponseTag = "response"
//BodyTag used when specifying a response to point to a model/schema
const BodyTag = "body"
//DescriptionTag used when specifying a response that gives a description of the response
const DescriptionTag = "description"
func parseTags(line string) (modelOrResponse string, arrays int, isDefinitionRef bool, description string, err error) {
tags := strings.Split(line, " ")
parsedModelOrResponse := false
for i, tagAndValue := range tags {
tagValList := strings.SplitN(tagAndValue, ":", 2)
var tag, value string
if len(tagValList) > 1 {
tag = tagValList[0]
value = tagValList[1]
} else {
//TODO: Print a warning, and in the long term, do not support not tagged values
//Add a default tag if none is supplied
if i == 0 {
tag = ResponseTag
} else {
tag = DescriptionTag
}
value = tagValList[0]
}
foundModelOrResponse := false
if !parsedModelOrResponse {
if tag == BodyTag {
foundModelOrResponse = true
isDefinitionRef = true
}
if tag == ResponseTag {
foundModelOrResponse = true
isDefinitionRef = false
}
}
if foundModelOrResponse {
//Read the model or response tag
parsedModelOrResponse = true
//Check for nested arrays
arrays = 0
for strings.HasPrefix(value, "[]") {
arrays++
value = value[2:]
}
//What's left over is the model name
modelOrResponse = value
} else {
foundDescription := false
if tag == DescriptionTag {
foundDescription = true
}
if foundDescription {
//Descriptions are special, they make they read the rest of the line
descriptionWords := []string{value}
if i < len(tags)-1 {
descriptionWords = append(descriptionWords, tags[i+1:]...)
}
description = strings.Join(descriptionWords, " ")
break
} else {
if tag == ResponseTag || tag == BodyTag || tag == DescriptionTag {
err = fmt.Errorf("Found valid tag %s, but not in a valid position", tag)
} else {
err = fmt.Errorf("Found invalid tag: %s", tag)
}
//return error
return
}
}
}
//TODO: Maybe do, if !parsedModelOrResponse {return some error}
return
}
func (ss *setOpResponses) Parse(lines []string) error {
if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) {
return nil
}
var def *spec.Response
var scr map[int]spec.Response
for _, line := range lines {
kv := strings.SplitN(line, ":", 2)
var key, value string
if len(kv) > 1 {
key = strings.TrimSpace(kv[0])
if key == "" {
// this must be some weird empty line
continue
}
value = strings.TrimSpace(kv[1])
if value == "" {
var resp spec.Response
if strings.EqualFold("default", key) {
if def == nil {
def = &resp
}
} else {
if sc, err := strconv.Atoi(key); err == nil {
if scr == nil {
scr = make(map[int]spec.Response)
}
scr[sc] = resp
}
}
continue
}
refTarget, arrays, isDefinitionRef, description, err := parseTags(value)
if err != nil {
return err
}
//A possible exception for having a definition
if _, ok := ss.responses[refTarget]; !ok {
if _, ok := ss.definitions[refTarget]; ok {
isDefinitionRef = true
}
}
var ref spec.Ref
if isDefinitionRef {
if description == "" {
description = refTarget
}
ref, err = spec.NewRef("#/definitions/" + refTarget)
} else {
ref, err = spec.NewRef("#/responses/" + refTarget)
}
if err != nil {
return err
}
// description should used on anyway.
resp := spec.Response{ResponseProps: spec.ResponseProps{Description: description}}
if isDefinitionRef {
resp.Schema = new(spec.Schema)
resp.Description = description
if arrays == 0 {
resp.Schema.Ref = ref
} else {
cs := resp.Schema
for i := 0; i < arrays; i++ {
cs.Typed("array", "")
cs.Items = new(spec.SchemaOrArray)
cs.Items.Schema = new(spec.Schema)
cs = cs.Items.Schema
}
cs.Ref = ref
}
// ref. could be empty while use description tag
} else if len(refTarget) > 0 {
resp.Ref = ref
}
if strings.EqualFold("default", key) {
if def == nil {
def = &resp
}
} else {
if sc, err := strconv.Atoi(key); err == nil {
if scr == nil {
scr = make(map[int]spec.Response)
}
scr[sc] = resp
}
}
}
}
ss.set(def, scr)
return nil
}
func parseEnum(val string, s *spec.SimpleSchema) []interface{} {
list := strings.Split(val, ",")
interfaceSlice := make([]interface{}, len(list))
for i, d := range list {
v, err := parseValueFromSchema(d, s)
if err != nil {
interfaceSlice[i] = d
continue
}
interfaceSlice[i] = v
}
return interfaceSlice
}