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