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