// Copyright 2019 DeepMap, Inc. // // 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 runtime import ( ) // BindStyledParameter binds a parameter as described in the Path Parameters // section here to a Go object: // https://swagger.io/docs/specification/serialization/ // It is a backward compatible function to clients generated with codegen // up to version v1.5.5. v1.5.6+ calls the function below. // Deprecated: BindStyledParameter is deprecated. func ( string, bool, string, string, interface{}) error { return BindStyledParameterWithOptions(, , , , BindStyledParameterOptions{ ParamLocation: ParamLocationUndefined, Explode: , Required: true, }) } // BindStyledParameterWithLocation binds a parameter as described in the Path Parameters // section here to a Go object: // https://swagger.io/docs/specification/serialization/ // This is a compatibility function which is used by oapi-codegen v2.0.0 and earlier. // Deprecated: BindStyledParameterWithLocation is deprecated. func ( string, bool, string, ParamLocation, string, interface{}) error { return BindStyledParameterWithOptions(, , , , BindStyledParameterOptions{ ParamLocation: , Explode: , Required: true, // This emulates behavior before the required parameter was optional. }) } // BindStyledParameterOptions defines optional arguments for BindStyledParameterWithOptions type BindStyledParameterOptions struct { // ParamLocation tells us where the parameter is located in the request. ParamLocation ParamLocation // Whether the parameter should use exploded structure Explode bool // Whether the parameter is required in the query Required bool } // BindStyledParameterWithOptions binds a parameter as described in the Path Parameters // section here to a Go object: // https://swagger.io/docs/specification/serialization/ func ( string, string, string, any, BindStyledParameterOptions) error { if .Required { if == "" { return fmt.Errorf("parameter '%s' is empty, can't bind its value", ) } } // Based on the location of the parameter, we need to unescape it properly. var error switch .ParamLocation { case ParamLocationQuery, ParamLocationUndefined: // We unescape undefined parameter locations here for older generated code, // since prior to this refactoring, they always query unescaped. , = url.QueryUnescape() if != nil { return fmt.Errorf("error unescaping query parameter '%s': %v", , ) } case ParamLocationPath: , = url.PathUnescape() if != nil { return fmt.Errorf("error unescaping path parameter '%s': %v", , ) } default: // Headers and cookies aren't escaped. } // If the destination implements encoding.TextUnmarshaler we use it for binding if , := .(encoding.TextUnmarshaler); { if := .UnmarshalText([]byte()); != nil { return fmt.Errorf("error unmarshaling '%s' text as %T: %s", , , ) } return nil } // Everything comes in by pointer, dereference it := reflect.Indirect(reflect.ValueOf()) // This is the basic type of the destination object. := .Type() if .Kind() == reflect.Struct { // We've got a destination object, we'll create a JSON representation // of the input value, and let the json library deal with the unmarshaling , := splitStyledParameter(, .Explode, true, , ) if != nil { return } return bindSplitPartsToDestinationStruct(, , .Explode, ) } if .Kind() == reflect.Slice { // Chop up the parameter into parts based on its style , := splitStyledParameter(, .Explode, false, , ) if != nil { return fmt.Errorf("error splitting input '%s' into parts: %s", , ) } return bindSplitPartsToDestinationArray(, ) } // Try to bind the remaining types as a base type. return BindStringToObject(, ) } // This is a complex set of operations, but each given parameter style can be // packed together in multiple ways, using different styles of separators, and // different packing strategies based on the explode flag. This function takes // as input any parameter format, and unpacks it to a simple list of strings // or key-values which we can then treat generically. // Why, oh why, great Swagger gods, did you have to make this so complicated? func ( string, bool, bool, string, string) ([]string, error) { switch { case "simple": // In the simple case, we always split on comma := strings.Split(, ",") return , nil case "label": // In the label case, it's more tricky. In the no explode case, we have // /users/.3,4,5 for arrays // /users/.role,admin,firstName,Alex for objects // in the explode case, we have: // /users/.3.4.5 // /users/.role=admin.firstName=Alex if { // In the exploded case, split everything on periods. := strings.Split(, ".") // The first part should be an empty string because we have a // leading period. if [0] != "" { return nil, fmt.Errorf("invalid format for label parameter '%s', should start with '.'", ) } return [1:], nil } else { // In the unexploded case, we strip off the leading period. if [0] != '.' { return nil, fmt.Errorf("invalid format for label parameter '%s', should start with '.'", ) } // The rest is comma separated. return strings.Split([1:], ","), nil } case "matrix": if { // In the exploded case, we break everything up on semicolon := strings.Split(, ";") // The first part should always be empty string, since we started // with ;something if [0] != "" { return nil, fmt.Errorf("invalid format for matrix parameter '%s', should start with ';'", ) } = [1:] // Now, if we have an object, we just have a list of x=y statements. // for a non-object, like an array, we have id=x, id=y. id=z, etc, // so we need to strip the prefix from each of them. if ! { := + "=" for := range { [] = strings.TrimPrefix([], ) } } return , nil } else { // In the unexploded case, parameters will start with ;paramName= := ";" + + "=" if !strings.HasPrefix(, ) { return nil, fmt.Errorf("expected parameter '%s' to start with %s", , ) } := strings.TrimPrefix(, ) return strings.Split(, ","), nil } case "form": var []string if { = strings.Split(, "&") if ! { := + "=" for := range { [] = strings.TrimPrefix([], ) } } return , nil } else { = strings.Split(, ",") := + "=" for := range { [] = strings.TrimPrefix([], ) } } return , nil } return nil, fmt.Errorf("unhandled parameter style: %s", ) } // Given a set of values as a slice, create a slice to hold them all, and // assign to each one by one. func ( []string, interface{}) error { // Everything comes in by pointer, dereference it := reflect.Indirect(reflect.ValueOf()) // This is the basic type of the destination object. := .Type() // We've got a destination array, bind each object one by one. // This generates a slice of the correct element type and length to // hold all the parts. := reflect.MakeSlice(, len(), len()) for , := range { := BindStringToObject(, .Index().Addr().Interface()) if != nil { return fmt.Errorf("error setting array element: %w", ) } } .Set() return nil } // Given a set of chopped up parameter parts, bind them to a destination // struct. The exploded parameter controls whether we send key value pairs // in the exploded case, or a sequence of values which are interpreted as // tuples. // Given the struct Id { firstName string, role string }, as in the canonical // swagger examples, in the exploded case, we would pass // ["firstName=Alex", "role=admin"], where in the non-exploded case, we would // pass "firstName", "Alex", "role", "admin"] // // We punt the hard work of binding these values to the object to the json // library. We'll turn those arrays into JSON strings, and unmarshal // into the struct. func ( string, []string, bool, interface{}) error { // We've got a destination object, we'll create a JSON representation // of the input value, and let the json library deal with the unmarshaling var []string if { = make([]string, len()) for , := range { := strings.Split(, "=") if len() != 2 { return fmt.Errorf("parameter '%s' has invalid exploded format", ) } [] = "\"" + [0] + "\":\"" + [1] + "\"" } } else { if len()%2 != 0 { return fmt.Errorf("parameter '%s' has invalid format, property/values need to be pairs", ) } = make([]string, len()/2) for := 0; < len(); += 2 { := [] := [+1] [/2] = "\"" + + "\":\"" + + "\"" } } := "{" + strings.Join(, ",") + "}" := json.Unmarshal([]byte(), ) if != nil { return fmt.Errorf("error binding parameter %s fields: %s", , ) } return nil } // BindQueryParameter works much like BindStyledParameter, however it takes a query argument // input array from the url package, since query arguments come through a // different path than the styled arguments. They're also exceptionally fussy. // For example, consider the exploded and unexploded form parameter examples: // (exploded) /users?role=admin&firstName=Alex // (unexploded) /users?id=role,admin,firstName,Alex // // In the first case, we can pull the "id" parameter off the context, // and unmarshal via json as an intermediate. Easy. In the second case, we // don't have the id QueryParam present, but must find "role", and "firstName". // what if there is another parameter similar to "ID" named "role"? We can't // tell them apart. This code tries to fail, but the moral of the story is that // you shouldn't pass objects via form styled query arguments, just use // the Content parameter form. func ( string, bool, bool, string, url.Values, interface{}) error { // dv = destination value. := reflect.Indirect(reflect.ValueOf()) // intermediate value form which is either dv or dv dereferenced. := // inner code will bind the string's value to this interface. var interface{} if { // If the parameter is required, then the generated code will pass us // a pointer to it: &int, &object, and so forth. We can directly set // them. = } else { // For optional parameters, we have an extra indirect. An optional // parameter of type "int" will be *int on the struct. We pass that // in by pointer, and have **int. // If the destination, is a nil pointer, we need to allocate it. if .IsNil() { := .Type() := reflect.New(.Elem()) // for now, hang onto the output buffer separately from destination, // as we don't want to write anything to destination until we can // unmarshal successfully, and check whether a field is required. = .Interface() } else { // If the destination isn't nil, just use that. = .Interface() } // Get rid of that extra indirect as compared to the required case, // so the code below doesn't have to care. = reflect.Indirect(reflect.ValueOf()) } // This is the basic type of the destination object. := .Type() := .Kind() switch { case "form": var []string if { // ok, the explode case in query arguments is very, very annoying, // because an exploded object, such as /users?role=admin&firstName=Alex // isn't actually present in the parameter array. We have to do // different things based on destination type. , := [] var error switch { case reflect.Slice: // In the slice case, we simply use the arguments provided by // http library. if ! { if { return fmt.Errorf("query parameter '%s' is required", ) } else { // If an optional parameter is not found, we do nothing, return nil } } = bindSplitPartsToDestinationArray(, ) case reflect.Struct: // This case is really annoying, and error prone, but the // form style object binding doesn't tell us which arguments // in the query string correspond to the object's fields. We'll // try to bind field by field. var bool , = bindParamsToExplodedObject(, , ) // If no fields were set, and there is no error, we will not fall // through to assign the destination. if ! { return nil } default: // Primitive object case. We expect to have 1 value to // unmarshal. if len() == 0 { if { return fmt.Errorf("query parameter '%s' is required", ) } else { return nil } } if len() != 1 { return fmt.Errorf("multiple values for single value parameter '%s'", ) } if ! { if { return fmt.Errorf("query parameter '%s' is required", ) } else { // If an optional parameter is not found, we do nothing, return nil } } = BindStringToObject([0], ) } if != nil { return } // If the parameter is required, and we've successfully unmarshaled // it, this assigns the new object to the pointer pointer. if ! { .Set(reflect.ValueOf()) } return nil } else { , := [] if ! { if { return fmt.Errorf("query parameter '%s' is required", ) } else { return nil } } if len() != 1 { return fmt.Errorf("parameter '%s' is not exploded, but is specified multiple times", ) } = strings.Split([0], ",") } var error switch { case reflect.Slice: = bindSplitPartsToDestinationArray(, ) case reflect.Struct: = bindSplitPartsToDestinationStruct(, , , ) default: if len() == 0 { if { return fmt.Errorf("query parameter '%s' is required", ) } else { return nil } } if len() != 1 { return fmt.Errorf("multiple values for single value parameter '%s'", ) } = BindStringToObject([0], ) } if != nil { return } if ! { .Set(reflect.ValueOf()) } return nil case "deepObject": if ! { return errors.New("deepObjects must be exploded") } return UnmarshalDeepObject(, , ) case "spaceDelimited", "pipeDelimited": return fmt.Errorf("query arguments of style '%s' aren't yet supported", ) default: return fmt.Errorf("style '%s' on parameter '%s' is invalid", , ) } } // bindParamsToExplodedObject reflects the destination structure, and pulls the value for // each settable field from the given parameters map. This is to deal with the // exploded form styled object which may occupy any number of parameter names. // We don't try to be smart here, if the field exists as a query argument, // set its value. This function returns a boolean, telling us whether there was // anything to bind. There will be nothing to bind if a parameter isn't found by name, // or none of an exploded object's fields are present. func ( string, url.Values, interface{}) (bool, error) { // Dereference pointers to their destination values , , := indirect() if != nil { , := [] if ! { return false, nil } return true, BindStringToObject(.Get(), ) } if .Kind() != reflect.Struct { return false, fmt.Errorf("unmarshaling query arg '%s' into wrong type", ) } := false for := 0; < .NumField(); ++ { := .Field() // Skip unsettable fields, such as internal ones. if !.Field().CanSet() { continue } // Find the json annotation on the field, and use the json specified // name if available, otherwise, just the field name. := .Tag.Get("json") := .Name if != "" { := strings.Split(, ",") := [0] if != "" { = } } // At this point, we look up field name in the parameter list. , := [] if { if len() != 1 { return false, fmt.Errorf("field '%s' specified multiple times for param '%s'", , ) } := BindStringToObject([0], .Field().Addr().Interface()) if != nil { return false, fmt.Errorf("could not bind query arg '%s' to request object: %s'", , ) } = true } } return , nil } // indirect func ( interface{}) (interface{}, reflect.Value, reflect.Type) { := reflect.ValueOf() if .Type().NumMethod() > 0 && .CanInterface() { if , := .Interface().(Binder); { return , reflect.Value{}, nil } } = reflect.Indirect() := .Type() // special handling for custom types which might look like an object. We // don't want to use object binding on them, but rather treat them as // primitive types. time.Time{} is a unique case since we can't add a Binder // to it without changing the underlying generated code. if .ConvertibleTo(reflect.TypeOf(time.Time{})) { return , reflect.Value{}, nil } if .ConvertibleTo(reflect.TypeOf(types.Date{})) { return , reflect.Value{}, nil } return nil, , }