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