github.com/AngusLu/go-swagger@v0.28.0/scan/route_params.go (about) 1 // +build !go1.11 2 3 package scan 4 5 import ( 6 "errors" 7 "strconv" 8 "strings" 9 10 "github.com/go-openapi/spec" 11 ) 12 13 const ( 14 // ParamDescriptionKey indicates the tag used to define a parameter description in swagger:route 15 ParamDescriptionKey = "description" 16 // ParamNameKey indicates the tag used to define a parameter name in swagger:route 17 ParamNameKey = "name" 18 // ParamInKey indicates the tag used to define a parameter location in swagger:route 19 ParamInKey = "in" 20 // ParamRequiredKey indicates the tag used to declare whether a parameter is required in swagger:route 21 ParamRequiredKey = "required" 22 // ParamTypeKey indicates the tag used to define the parameter type in swagger:route 23 ParamTypeKey = "type" 24 // ParamAllowEmptyKey indicates the tag used to indicate whether a parameter allows empty values in swagger:route 25 ParamAllowEmptyKey = "allowempty" 26 27 // SchemaMinKey indicates the tag used to indicate the minimum value allowed for this type in swagger:route 28 SchemaMinKey = "min" 29 // SchemaMaxKey indicates the tag used to indicate the maximum value allowed for this type in swagger:route 30 SchemaMaxKey = "max" 31 // SchemaEnumKey indicates the tag used to specify the allowed values for this type in swagger:route 32 SchemaEnumKey = "enum" 33 // SchemaFormatKey indicates the expected format for this field in swagger:route 34 SchemaFormatKey = "format" 35 // SchemaDefaultKey indicates the default value for this field in swagger:route 36 SchemaDefaultKey = "default" 37 // SchemaMinLenKey indicates the minimum length this field in swagger:route 38 SchemaMinLenKey = "minlength" 39 // SchemaMaxLenKey indicates the minimum length this field in swagger:route 40 SchemaMaxLenKey = "maxlength" 41 42 // TypeArray is the identifier for an array type in swagger:route 43 TypeArray = "array" 44 // TypeNumber is the identifier for a number type in swagger:route 45 TypeNumber = "number" 46 // TypeInteger is the identifier for an integer type in swagger:route 47 TypeInteger = "integer" 48 // TypeBoolean is the identifier for a boolean type in swagger:route 49 TypeBoolean = "boolean" 50 // TypeBool is the identifier for a boolean type in swagger:route 51 TypeBool = "bool" 52 // TypeObject is the identifier for an object type in swagger:route 53 TypeObject = "object" 54 // TypeString is the identifier for a string type in swagger:route 55 TypeString = "string" 56 ) 57 58 var ( 59 validIn = []string{"path", "query", "header", "body", "form"} 60 basicTypes = []string{TypeInteger, TypeNumber, TypeString, TypeBoolean, TypeBool, TypeArray} 61 ) 62 63 func newSetParams(params []*spec.Parameter, setter func([]*spec.Parameter)) *setOpParams { 64 return &setOpParams{ 65 set: setter, 66 parameters: params, 67 } 68 } 69 70 type setOpParams struct { 71 set func([]*spec.Parameter) 72 parameters []*spec.Parameter 73 } 74 75 func (s *setOpParams) Matches(line string) bool { 76 return rxParameters.MatchString(line) 77 } 78 79 func (s *setOpParams) Parse(lines []string) error { 80 if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) { 81 return nil 82 } 83 84 var current *spec.Parameter 85 var extraData map[string]string 86 87 for _, line := range lines { 88 l := strings.TrimSpace(line) 89 90 if strings.HasPrefix(l, "+") { 91 s.finalizeParam(current, extraData) 92 current = new(spec.Parameter) 93 extraData = make(map[string]string) 94 l = strings.TrimPrefix(l, "+") 95 } 96 97 kv := strings.SplitN(l, ":", 2) 98 99 if len(kv) <= 1 { 100 continue 101 } 102 103 key := strings.ToLower(strings.TrimSpace(kv[0])) 104 value := strings.TrimSpace(kv[1]) 105 106 if current == nil { 107 return errors.New("invalid route/operation schema provided") 108 } 109 110 switch key { 111 case ParamDescriptionKey: 112 current.Description = value 113 case ParamNameKey: 114 current.Name = value 115 case ParamInKey: 116 v := strings.ToLower(value) 117 if contains(validIn, v) { 118 current.In = v 119 } 120 case ParamRequiredKey: 121 if v, err := strconv.ParseBool(value); err == nil { 122 current.Required = v 123 } 124 case ParamTypeKey: 125 if current.Schema == nil { 126 current.Schema = new(spec.Schema) 127 } 128 if contains(basicTypes, value) { 129 current.Type = strings.ToLower(value) 130 if current.Type == TypeBool { 131 current.Type = TypeBoolean 132 } 133 } else { 134 if ref, err := spec.NewRef("#/definitions/" + value); err == nil { 135 current.Type = TypeObject 136 current.Schema.Ref = ref 137 } 138 } 139 current.Schema.Type = spec.StringOrArray{current.Type} 140 case ParamAllowEmptyKey: 141 if v, err := strconv.ParseBool(value); err == nil { 142 current.AllowEmptyValue = v 143 } 144 default: 145 extraData[key] = value 146 } 147 } 148 149 s.finalizeParam(current, extraData) 150 s.set(s.parameters) 151 return nil 152 } 153 154 func (s *setOpParams) finalizeParam(param *spec.Parameter, data map[string]string) { 155 if param == nil { 156 return 157 } 158 159 processSchema(data, param) 160 s.parameters = append(s.parameters, param) 161 } 162 163 func processSchema(data map[string]string, param *spec.Parameter) { 164 if param.Schema == nil { 165 return 166 } 167 168 var enumValues []string 169 170 for key, value := range data { 171 switch key { 172 case SchemaMinKey: 173 if t := getType(param.Schema); t == TypeNumber || t == TypeInteger { 174 v, _ := strconv.ParseFloat(value, 64) 175 param.Schema.Minimum = &v 176 } 177 case SchemaMaxKey: 178 if t := getType(param.Schema); t == TypeNumber || t == TypeInteger { 179 v, _ := strconv.ParseFloat(value, 64) 180 param.Schema.Maximum = &v 181 } 182 case SchemaMinLenKey: 183 if getType(param.Schema) == TypeArray { 184 v, _ := strconv.ParseInt(value, 10, 64) 185 param.Schema.MinLength = &v 186 } 187 case SchemaMaxLenKey: 188 if getType(param.Schema) == TypeArray { 189 v, _ := strconv.ParseInt(value, 10, 64) 190 param.Schema.MaxLength = &v 191 } 192 case SchemaEnumKey: 193 enumValues = strings.Split(value, ",") 194 case SchemaFormatKey: 195 param.Schema.Format = value 196 case SchemaDefaultKey: 197 param.Schema.Default = convert(param.Type, value) 198 } 199 } 200 201 if param.Description != "" { 202 param.Schema.Description = param.Description 203 } 204 205 convertEnum(param.Schema, enumValues) 206 } 207 208 func convertEnum(schema *spec.Schema, enumValues []string) { 209 if len(enumValues) == 0 { 210 return 211 } 212 213 var finalEnum []interface{} 214 for _, v := range enumValues { 215 finalEnum = append(finalEnum, convert(schema.Type[0], strings.TrimSpace(v))) 216 } 217 schema.Enum = finalEnum 218 } 219 220 func convert(typeStr, valueStr string) interface{} { 221 switch typeStr { 222 case TypeInteger: 223 fallthrough 224 case TypeNumber: 225 if num, err := strconv.ParseFloat(valueStr, 64); err == nil { 226 return num 227 } 228 case TypeBoolean: 229 fallthrough 230 case TypeBool: 231 if b, err := strconv.ParseBool(valueStr); err == nil { 232 return b 233 } 234 } 235 return valueStr 236 } 237 238 func getType(schema *spec.Schema) string { 239 if len(schema.Type) == 0 { 240 return "" 241 } 242 return schema.Type[0] 243 } 244 245 func contains(arr []string, obj string) bool { 246 for _, v := range arr { 247 if v == obj { 248 return true 249 } 250 } 251 return false 252 }