|
Packit Service |
509fd4 |
// Copyright 2019 DeepMap, Inc.
|
|
Packit Service |
509fd4 |
//
|
|
Packit Service |
509fd4 |
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
Packit Service |
509fd4 |
// you may not use this file except in compliance with the License.
|
|
Packit Service |
509fd4 |
// You may obtain a copy of the License at
|
|
Packit Service |
509fd4 |
//
|
|
Packit Service |
509fd4 |
// http://www.apache.org/licenses/LICENSE-2.0
|
|
Packit Service |
509fd4 |
//
|
|
Packit Service |
509fd4 |
// Unless required by applicable law or agreed to in writing, software
|
|
Packit Service |
509fd4 |
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
Packit Service |
509fd4 |
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
Packit Service |
509fd4 |
// See the License for the specific language governing permissions and
|
|
Packit Service |
509fd4 |
// limitations under the License.
|
|
Packit Service |
509fd4 |
package runtime
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
import (
|
|
Packit Service |
509fd4 |
"errors"
|
|
Packit Service |
509fd4 |
"fmt"
|
|
Packit Service |
509fd4 |
"reflect"
|
|
Packit Service |
509fd4 |
"sort"
|
|
Packit Service |
509fd4 |
"strconv"
|
|
Packit Service |
509fd4 |
"strings"
|
|
Packit Service |
509fd4 |
"time"
|
|
Packit Service |
509fd4 |
)
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
// Given an input value, such as a primitive type, array or object, turn it
|
|
Packit Service |
509fd4 |
// into a parameter based on style/explode definition.
|
|
Packit Service |
509fd4 |
func StyleParam(style string, explode bool, paramName string, value interface{}) (string, error) {
|
|
Packit Service |
509fd4 |
t := reflect.TypeOf(value)
|
|
Packit Service |
509fd4 |
v := reflect.ValueOf(value)
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
// Things may be passed in by pointer, we need to dereference, so return
|
|
Packit Service |
509fd4 |
// error on nil.
|
|
Packit Service |
509fd4 |
if t.Kind() == reflect.Ptr {
|
|
Packit Service |
509fd4 |
if v.IsNil() {
|
|
Packit Service |
509fd4 |
return "", fmt.Errorf("value is a nil pointer")
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
v = reflect.Indirect(v)
|
|
Packit Service |
509fd4 |
t = v.Type()
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
switch t.Kind() {
|
|
Packit Service |
509fd4 |
case reflect.Slice:
|
|
Packit Service |
509fd4 |
n := v.Len()
|
|
Packit Service |
509fd4 |
sliceVal := make([]interface{}, n)
|
|
Packit Service |
509fd4 |
for i := 0; i < n; i++ {
|
|
Packit Service |
509fd4 |
sliceVal[i] = v.Index(i).Interface()
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
return styleSlice(style, explode, paramName, sliceVal)
|
|
Packit Service |
509fd4 |
case reflect.Struct:
|
|
Packit Service |
509fd4 |
return styleStruct(style, explode, paramName, value)
|
|
Packit Service |
509fd4 |
case reflect.Map:
|
|
Packit Service |
509fd4 |
return styleMap(style, explode, paramName, value)
|
|
Packit Service |
509fd4 |
default:
|
|
Packit Service |
509fd4 |
return stylePrimitive(style, explode, paramName, value)
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
func styleSlice(style string, explode bool, paramName string, values []interface{}) (string, error) {
|
|
Packit Service |
509fd4 |
if style == "deepObject" {
|
|
Packit Service |
509fd4 |
if !explode {
|
|
Packit Service |
509fd4 |
return "", errors.New("deepObjects must be exploded")
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
return MarshalDeepObject(values, paramName)
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
var prefix string
|
|
Packit Service |
509fd4 |
var separator string
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
switch style {
|
|
Packit Service |
509fd4 |
case "simple":
|
|
Packit Service |
509fd4 |
separator = ","
|
|
Packit Service |
509fd4 |
case "label":
|
|
Packit Service |
509fd4 |
prefix = "."
|
|
Packit Service |
509fd4 |
if explode {
|
|
Packit Service |
509fd4 |
separator = "."
|
|
Packit Service |
509fd4 |
} else {
|
|
Packit Service |
509fd4 |
separator = ","
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
case "matrix":
|
|
Packit Service |
509fd4 |
prefix = fmt.Sprintf(";%s=", paramName)
|
|
Packit Service |
509fd4 |
if explode {
|
|
Packit Service |
509fd4 |
separator = prefix
|
|
Packit Service |
509fd4 |
} else {
|
|
Packit Service |
509fd4 |
separator = ","
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
case "form":
|
|
Packit Service |
509fd4 |
prefix = fmt.Sprintf("%s=", paramName)
|
|
Packit Service |
509fd4 |
if explode {
|
|
Packit Service |
509fd4 |
separator = "&" + prefix
|
|
Packit Service |
509fd4 |
} else {
|
|
Packit Service |
509fd4 |
separator = ","
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
case "spaceDelimited":
|
|
Packit Service |
509fd4 |
prefix = fmt.Sprintf("%s=", paramName)
|
|
Packit Service |
509fd4 |
if explode {
|
|
Packit Service |
509fd4 |
separator = "&" + prefix
|
|
Packit Service |
509fd4 |
} else {
|
|
Packit Service |
509fd4 |
separator = " "
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
case "pipeDelimited":
|
|
Packit Service |
509fd4 |
prefix = fmt.Sprintf("%s=", paramName)
|
|
Packit Service |
509fd4 |
if explode {
|
|
Packit Service |
509fd4 |
separator = "&" + prefix
|
|
Packit Service |
509fd4 |
} else {
|
|
Packit Service |
509fd4 |
separator = "|"
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
default:
|
|
Packit Service |
509fd4 |
return "", fmt.Errorf("unsupported style '%s'", style)
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
// We're going to assume here that the array is one of simple types.
|
|
Packit Service |
509fd4 |
var err error
|
|
Packit Service |
509fd4 |
parts := make([]string, len(values))
|
|
Packit Service |
509fd4 |
for i, v := range values {
|
|
Packit Service |
509fd4 |
parts[i], err = primitiveToString(v)
|
|
Packit Service |
509fd4 |
if err != nil {
|
|
Packit Service |
509fd4 |
return "", fmt.Errorf("error formatting '%s': %s", paramName, err)
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
return prefix + strings.Join(parts, separator), nil
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
func sortedKeys(strMap map[string]string) []string {
|
|
Packit Service |
509fd4 |
keys := make([]string, len(strMap))
|
|
Packit Service |
509fd4 |
i := 0
|
|
Packit Service |
509fd4 |
for k := range strMap {
|
|
Packit Service |
509fd4 |
keys[i] = k
|
|
Packit Service |
509fd4 |
i++
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
sort.Strings(keys)
|
|
Packit Service |
509fd4 |
return keys
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
// This is a special case. The struct may be a time, in which case, marshal
|
|
Packit Service |
509fd4 |
// it in RFC3339 format.
|
|
Packit Service |
509fd4 |
func marshalTimeValue(value interface{}) (string, bool) {
|
|
Packit Service |
509fd4 |
if timeVal, ok := value.(*time.Time); ok {
|
|
Packit Service |
509fd4 |
return timeVal.Format(time.RFC3339Nano), true
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
if timeVal, ok := value.(time.Time); ok {
|
|
Packit Service |
509fd4 |
return timeVal.Format(time.RFC3339Nano), true
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
return "", false
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
func styleStruct(style string, explode bool, paramName string, value interface{}) (string, error) {
|
|
Packit Service |
509fd4 |
if timeVal, ok := marshalTimeValue(value); ok {
|
|
Packit Service |
509fd4 |
return stylePrimitive(style, explode, paramName, timeVal)
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
if style == "deepObject" {
|
|
Packit Service |
509fd4 |
if !explode {
|
|
Packit Service |
509fd4 |
return "", errors.New("deepObjects must be exploded")
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
return MarshalDeepObject(value, paramName)
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
// Otherwise, we need to build a dictionary of the struct's fields. Each
|
|
Packit Service |
509fd4 |
// field may only be a primitive value.
|
|
Packit Service |
509fd4 |
v := reflect.ValueOf(value)
|
|
Packit Service |
509fd4 |
t := reflect.TypeOf(value)
|
|
Packit Service |
509fd4 |
fieldDict := make(map[string]string)
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
for i := 0; i < t.NumField(); i++ {
|
|
Packit Service |
509fd4 |
fieldT := t.Field(i)
|
|
Packit Service |
509fd4 |
// Find the json annotation on the field, and use the json specified
|
|
Packit Service |
509fd4 |
// name if available, otherwise, just the field name.
|
|
Packit Service |
509fd4 |
tag := fieldT.Tag.Get("json")
|
|
Packit Service |
509fd4 |
fieldName := fieldT.Name
|
|
Packit Service |
509fd4 |
if tag != "" {
|
|
Packit Service |
509fd4 |
tagParts := strings.Split(tag, ",")
|
|
Packit Service |
509fd4 |
name := tagParts[0]
|
|
Packit Service |
509fd4 |
if name != "" {
|
|
Packit Service |
509fd4 |
fieldName = name
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
f := v.Field(i)
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
// Unset optional fields will be nil pointers, skip over those.
|
|
Packit Service |
509fd4 |
if f.Type().Kind() == reflect.Ptr && f.IsNil() {
|
|
Packit Service |
509fd4 |
continue
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
str, err := primitiveToString(f.Interface())
|
|
Packit Service |
509fd4 |
if err != nil {
|
|
Packit Service |
509fd4 |
return "", fmt.Errorf("error formatting '%s': %s", paramName, err)
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
fieldDict[fieldName] = str
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
return processFieldDict(style, explode, paramName, fieldDict)
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
func styleMap(style string, explode bool, paramName string, value interface{}) (string, error) {
|
|
Packit Service |
509fd4 |
if style == "deepObject" {
|
|
Packit Service |
509fd4 |
if !explode {
|
|
Packit Service |
509fd4 |
return "", errors.New("deepObjects must be exploded")
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
return MarshalDeepObject(value, paramName)
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
dict, ok := value.(map[string]interface{})
|
|
Packit Service |
509fd4 |
if !ok {
|
|
Packit Service |
509fd4 |
return "", errors.New("map not of type map[string]interface{}")
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
fieldDict := make(map[string]string)
|
|
Packit Service |
509fd4 |
for fieldName, value := range dict {
|
|
Packit Service |
509fd4 |
str, err := primitiveToString(value)
|
|
Packit Service |
509fd4 |
if err != nil {
|
|
Packit Service |
509fd4 |
return "", fmt.Errorf("error formatting '%s': %s", paramName, err)
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
fieldDict[fieldName] = str
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
return processFieldDict(style, explode, paramName, fieldDict)
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
func processFieldDict(style string, explode bool, paramName string, fieldDict map[string]string) (string, error) {
|
|
Packit Service |
509fd4 |
var parts []string
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
// This works for everything except deepObject. We'll handle that one
|
|
Packit Service |
509fd4 |
// separately.
|
|
Packit Service |
509fd4 |
if style != "deepObject" {
|
|
Packit Service |
509fd4 |
if explode {
|
|
Packit Service |
509fd4 |
for _, k := range sortedKeys(fieldDict) {
|
|
Packit Service |
509fd4 |
v := fieldDict[k]
|
|
Packit Service |
509fd4 |
parts = append(parts, k+"="+v)
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
} else {
|
|
Packit Service |
509fd4 |
for _, k := range sortedKeys(fieldDict) {
|
|
Packit Service |
509fd4 |
v := fieldDict[k]
|
|
Packit Service |
509fd4 |
parts = append(parts, k)
|
|
Packit Service |
509fd4 |
parts = append(parts, v)
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
var prefix string
|
|
Packit Service |
509fd4 |
var separator string
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
switch style {
|
|
Packit Service |
509fd4 |
case "simple":
|
|
Packit Service |
509fd4 |
separator = ","
|
|
Packit Service |
509fd4 |
case "label":
|
|
Packit Service |
509fd4 |
prefix = "."
|
|
Packit Service |
509fd4 |
if explode {
|
|
Packit Service |
509fd4 |
separator = prefix
|
|
Packit Service |
509fd4 |
} else {
|
|
Packit Service |
509fd4 |
separator = ","
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
case "matrix":
|
|
Packit Service |
509fd4 |
if explode {
|
|
Packit Service |
509fd4 |
separator = ";"
|
|
Packit Service |
509fd4 |
prefix = ";"
|
|
Packit Service |
509fd4 |
} else {
|
|
Packit Service |
509fd4 |
separator = ","
|
|
Packit Service |
509fd4 |
prefix = fmt.Sprintf(";%s=", paramName)
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
case "form":
|
|
Packit Service |
509fd4 |
if explode {
|
|
Packit Service |
509fd4 |
separator = "&"
|
|
Packit Service |
509fd4 |
} else {
|
|
Packit Service |
509fd4 |
prefix = fmt.Sprintf("%s=", paramName)
|
|
Packit Service |
509fd4 |
separator = ","
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
case "deepObject":
|
|
Packit Service |
509fd4 |
{
|
|
Packit Service |
509fd4 |
if !explode {
|
|
Packit Service |
509fd4 |
return "", fmt.Errorf("deepObject parameters must be exploded")
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
for _, k := range sortedKeys(fieldDict) {
|
|
Packit Service |
509fd4 |
v := fieldDict[k]
|
|
Packit Service |
509fd4 |
part := fmt.Sprintf("%s[%s]=%s", paramName, k, v)
|
|
Packit Service |
509fd4 |
parts = append(parts, part)
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
separator = "&"
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
default:
|
|
Packit Service |
509fd4 |
return "", fmt.Errorf("unsupported style '%s'", style)
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
return prefix + strings.Join(parts, separator), nil
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
func stylePrimitive(style string, explode bool, paramName string, value interface{}) (string, error) {
|
|
Packit Service |
509fd4 |
strVal, err := primitiveToString(value)
|
|
Packit Service |
509fd4 |
if err != nil {
|
|
Packit Service |
509fd4 |
return "", err
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
var prefix string
|
|
Packit Service |
509fd4 |
switch style {
|
|
Packit Service |
509fd4 |
case "simple":
|
|
Packit Service |
509fd4 |
case "label":
|
|
Packit Service |
509fd4 |
prefix = "."
|
|
Packit Service |
509fd4 |
case "matrix":
|
|
Packit Service |
509fd4 |
prefix = fmt.Sprintf(";%s=", paramName)
|
|
Packit Service |
509fd4 |
case "form":
|
|
Packit Service |
509fd4 |
prefix = fmt.Sprintf("%s=", paramName)
|
|
Packit Service |
509fd4 |
default:
|
|
Packit Service |
509fd4 |
return "", fmt.Errorf("unsupported style '%s'", style)
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
return prefix + strVal, nil
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
// Converts a primitive value to a string. We need to do this based on the
|
|
Packit Service |
509fd4 |
// Kind of an interface, not the Type to work with aliased types.
|
|
Packit Service |
509fd4 |
func primitiveToString(value interface{}) (string, error) {
|
|
Packit Service |
509fd4 |
var output string
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
// Values may come in by pointer for optionals, so make sure to dereferene.
|
|
Packit Service |
509fd4 |
v := reflect.Indirect(reflect.ValueOf(value))
|
|
Packit Service |
509fd4 |
t := v.Type()
|
|
Packit Service |
509fd4 |
kind := t.Kind()
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
switch kind {
|
|
Packit Service |
509fd4 |
case reflect.Int8, reflect.Int32, reflect.Int64, reflect.Int:
|
|
Packit Service |
509fd4 |
output = strconv.FormatInt(v.Int(), 10)
|
|
Packit Service |
509fd4 |
case reflect.Float32, reflect.Float64:
|
|
Packit Service |
509fd4 |
output = strconv.FormatFloat(v.Float(), 'f', -1, 64)
|
|
Packit Service |
509fd4 |
case reflect.Bool:
|
|
Packit Service |
509fd4 |
if v.Bool() {
|
|
Packit Service |
509fd4 |
output = "true"
|
|
Packit Service |
509fd4 |
} else {
|
|
Packit Service |
509fd4 |
output = "false"
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
case reflect.String:
|
|
Packit Service |
509fd4 |
output = v.String()
|
|
Packit Service |
509fd4 |
default:
|
|
Packit Service |
509fd4 |
return "", fmt.Errorf("unsupported type %s", reflect.TypeOf(value).String())
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
return output, nil
|
|
Packit Service |
509fd4 |
}
|