2017-11-11 04:44:02 +00:00
|
|
|
package builder
|
2017-10-16 02:23:15 +00:00
|
|
|
|
|
|
|
import (
|
2018-07-13 19:59:54 +00:00
|
|
|
"errors"
|
2017-10-16 02:23:15 +00:00
|
|
|
"fmt"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/rancher/norman/httperror"
|
2017-11-11 04:44:02 +00:00
|
|
|
"github.com/rancher/norman/types"
|
|
|
|
"github.com/rancher/norman/types/convert"
|
|
|
|
"github.com/rancher/norman/types/definition"
|
2018-01-18 17:55:53 +00:00
|
|
|
"k8s.io/apimachinery/pkg/util/validation"
|
2017-10-16 02:23:15 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
2018-07-13 19:59:54 +00:00
|
|
|
Create = Operation("create")
|
|
|
|
Update = Operation("update")
|
|
|
|
Action = Operation("action")
|
|
|
|
List = Operation("list")
|
|
|
|
ListForCreate = Operation("listcreate")
|
|
|
|
ErrComplexType = errors.New("complex type")
|
2017-10-16 02:23:15 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type Operation string
|
|
|
|
|
2018-01-20 04:28:40 +00:00
|
|
|
func (o Operation) IsList() bool {
|
|
|
|
return strings.HasPrefix(string(o), "list")
|
|
|
|
}
|
|
|
|
|
2017-10-16 02:23:15 +00:00
|
|
|
type Builder struct {
|
2018-01-17 23:33:05 +00:00
|
|
|
apiContext *types.APIContext
|
2017-11-11 04:44:02 +00:00
|
|
|
Version *types.APIVersion
|
|
|
|
Schemas *types.Schemas
|
|
|
|
RefValidator types.ReferenceValidator
|
2018-06-04 23:47:35 +00:00
|
|
|
edit bool
|
|
|
|
export bool
|
2018-07-13 19:59:54 +00:00
|
|
|
yaml bool
|
2017-10-16 02:23:15 +00:00
|
|
|
}
|
|
|
|
|
2017-11-11 04:44:02 +00:00
|
|
|
func NewBuilder(apiRequest *types.APIContext) *Builder {
|
|
|
|
return &Builder{
|
2018-01-17 23:33:05 +00:00
|
|
|
apiContext: apiRequest,
|
2018-07-13 19:59:54 +00:00
|
|
|
yaml: apiRequest.ResponseFormat == "yaml",
|
2018-06-04 23:47:35 +00:00
|
|
|
edit: apiRequest.Option("edit") == "true",
|
|
|
|
export: apiRequest.Option("export") == "true",
|
2017-11-11 04:44:02 +00:00
|
|
|
Version: apiRequest.Version,
|
|
|
|
Schemas: apiRequest.Schemas,
|
|
|
|
RefValidator: apiRequest.ReferenceValidator,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *Builder) Construct(schema *types.Schema, input map[string]interface{}, op Operation) (map[string]interface{}, error) {
|
2018-01-17 23:33:05 +00:00
|
|
|
result, err := b.copyFields(schema, input, op)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if (op == Create || op == Update) && schema.Validator != nil {
|
|
|
|
if err := schema.Validator(b.apiContext, schema, result); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result, nil
|
2017-10-16 02:23:15 +00:00
|
|
|
}
|
|
|
|
|
2017-11-11 04:44:02 +00:00
|
|
|
func (b *Builder) copyInputs(schema *types.Schema, input map[string]interface{}, op Operation, result map[string]interface{}) error {
|
2017-10-16 02:23:15 +00:00
|
|
|
for fieldName, value := range input {
|
|
|
|
field, ok := schema.ResourceFields[fieldName]
|
|
|
|
if !ok {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if !fieldMatchesOp(field, op) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
wasNull := value == nil && (field.Nullable || field.Default == nil)
|
|
|
|
value, err := b.convert(field.Type, value, op)
|
|
|
|
if err != nil {
|
2017-11-21 20:46:30 +00:00
|
|
|
return httperror.WrapFieldAPIError(err, httperror.InvalidFormat, fieldName, err.Error())
|
2017-10-16 02:23:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if value != nil || wasNull {
|
2018-01-20 04:28:40 +00:00
|
|
|
if !op.IsList() {
|
2017-11-11 04:44:02 +00:00
|
|
|
if slice, ok := value.([]interface{}); ok {
|
|
|
|
for _, sliceValue := range slice {
|
|
|
|
if sliceValue == nil {
|
2017-11-21 20:46:30 +00:00
|
|
|
return httperror.NewFieldAPIError(httperror.NotNullable, fieldName, "Individual array values can not be null")
|
2017-11-11 04:44:02 +00:00
|
|
|
}
|
2018-07-13 19:59:54 +00:00
|
|
|
if err := CheckFieldCriteria(fieldName, field, sliceValue); err != nil {
|
2017-11-11 04:44:02 +00:00
|
|
|
return err
|
|
|
|
}
|
2017-10-16 02:23:15 +00:00
|
|
|
}
|
2017-11-11 04:44:02 +00:00
|
|
|
} else {
|
2018-07-13 19:59:54 +00:00
|
|
|
if err := CheckFieldCriteria(fieldName, field, value); err != nil {
|
2017-10-16 02:23:15 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-03-05 05:40:05 +00:00
|
|
|
result[fieldName] = value
|
2018-03-01 21:22:08 +00:00
|
|
|
|
2018-07-13 19:59:54 +00:00
|
|
|
if op.IsList() && field.Type == "date" && value != "" && !b.edit {
|
2017-11-11 04:44:02 +00:00
|
|
|
ts, err := convert.ToTimestamp(value)
|
|
|
|
if err == nil {
|
|
|
|
result[fieldName+"TS"] = ts
|
|
|
|
}
|
|
|
|
}
|
2017-10-16 02:23:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-04 23:47:35 +00:00
|
|
|
if op.IsList() && !b.edit && !b.export {
|
2018-07-13 20:04:31 +00:00
|
|
|
if !convert.IsAPIObjectEmpty(input["type"]) {
|
2017-12-05 16:21:12 +00:00
|
|
|
result["type"] = input["type"]
|
|
|
|
}
|
2018-07-13 20:04:31 +00:00
|
|
|
if !convert.IsAPIObjectEmpty(input["id"]) {
|
2017-12-05 16:21:12 +00:00
|
|
|
result["id"] = input["id"]
|
|
|
|
}
|
2017-11-21 20:46:30 +00:00
|
|
|
}
|
|
|
|
|
2017-10-16 02:23:15 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-11-11 04:44:02 +00:00
|
|
|
func (b *Builder) checkDefaultAndRequired(schema *types.Schema, input map[string]interface{}, op Operation, result map[string]interface{}) error {
|
2017-10-16 02:23:15 +00:00
|
|
|
for fieldName, field := range schema.ResourceFields {
|
2018-03-05 05:05:03 +00:00
|
|
|
val, hasKey := result[fieldName]
|
|
|
|
if op == Create && (!hasKey || val == "") && field.Default != nil {
|
2017-10-16 02:23:15 +00:00
|
|
|
result[fieldName] = field.Default
|
|
|
|
}
|
|
|
|
|
|
|
|
_, hasKey = result[fieldName]
|
|
|
|
if op == Create && fieldMatchesOp(field, Create) && field.Required {
|
|
|
|
if !hasKey {
|
2017-11-21 20:46:30 +00:00
|
|
|
return httperror.NewFieldAPIError(httperror.MissingRequired, fieldName, "")
|
2017-10-16 02:23:15 +00:00
|
|
|
}
|
|
|
|
|
2017-11-11 04:44:02 +00:00
|
|
|
if definition.IsArrayType(field.Type) {
|
2018-03-15 01:57:14 +00:00
|
|
|
slice, err := b.convertArray(field.Type, result[fieldName], op)
|
2017-10-16 02:23:15 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if len(slice) == 0 {
|
2017-11-21 20:46:30 +00:00
|
|
|
return httperror.NewFieldAPIError(httperror.MissingRequired, fieldName, "")
|
2017-10-16 02:23:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-11-29 21:27:02 +00:00
|
|
|
|
2018-01-20 04:28:40 +00:00
|
|
|
if op.IsList() && fieldMatchesOp(field, List) && definition.IsReferenceType(field.Type) && !hasKey {
|
2017-11-29 21:27:02 +00:00
|
|
|
result[fieldName] = nil
|
2018-03-31 07:13:30 +00:00
|
|
|
} else if op.IsList() && fieldMatchesOp(field, List) && !hasKey && field.Default != nil {
|
|
|
|
result[fieldName] = field.Default
|
2017-11-29 21:27:02 +00:00
|
|
|
}
|
2017-10-16 02:23:15 +00:00
|
|
|
}
|
|
|
|
|
2018-06-04 23:47:35 +00:00
|
|
|
if op.IsList() && b.edit {
|
|
|
|
b.populateMissingFieldsForEdit(schema, result)
|
|
|
|
}
|
|
|
|
|
|
|
|
if op.IsList() && b.export {
|
2018-06-19 18:21:15 +00:00
|
|
|
b.dropDefaultsAndReadOnly(schema, result)
|
2018-06-04 23:47:35 +00:00
|
|
|
}
|
|
|
|
|
2017-10-16 02:23:15 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-06-19 18:21:15 +00:00
|
|
|
func (b *Builder) dropDefaultsAndReadOnly(schema *types.Schema, result map[string]interface{}) {
|
2018-06-04 23:47:35 +00:00
|
|
|
for name, existingVal := range result {
|
|
|
|
field, ok := schema.ResourceFields[name]
|
|
|
|
if !ok {
|
|
|
|
delete(result, name)
|
|
|
|
}
|
|
|
|
|
|
|
|
if !field.Create {
|
|
|
|
delete(result, name)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if field.Default == existingVal {
|
|
|
|
delete(result, name)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
val, err := b.convert(field.Type, nil, List)
|
|
|
|
if err == nil && val == existingVal {
|
|
|
|
delete(result, name)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2018-07-13 20:04:31 +00:00
|
|
|
if convert.IsAPIObjectEmpty(existingVal) {
|
2018-06-04 23:47:35 +00:00
|
|
|
delete(result, name)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *Builder) populateMissingFieldsForEdit(schema *types.Schema, result map[string]interface{}) {
|
|
|
|
for name, field := range schema.ResourceFields {
|
|
|
|
if !field.Update {
|
2018-07-13 19:59:54 +00:00
|
|
|
if name != "name" {
|
|
|
|
delete(result, name)
|
|
|
|
}
|
2018-06-04 23:47:35 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2018-07-13 19:59:54 +00:00
|
|
|
desc := field.Description
|
|
|
|
if len(desc) > 0 {
|
|
|
|
desc += " "
|
|
|
|
}
|
|
|
|
|
|
|
|
value, hasKey := result[name]
|
2018-06-04 23:47:35 +00:00
|
|
|
if hasKey {
|
2018-07-13 19:59:54 +00:00
|
|
|
if field.Default != nil && field.Default == value {
|
|
|
|
delete(result, name)
|
|
|
|
result["zzz#("+desc+")("+field.Type+")"+name] = value
|
|
|
|
}
|
2018-06-04 23:47:35 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if field.Default != nil {
|
2018-07-13 19:59:54 +00:00
|
|
|
result["zzz#("+desc+")("+field.Type+")"+name] = field.Default
|
2018-06-04 23:47:35 +00:00
|
|
|
} else {
|
|
|
|
val, err := b.convert(field.Type, nil, List)
|
|
|
|
if err == nil {
|
2018-07-13 19:59:54 +00:00
|
|
|
result["zzz#("+desc+")("+field.Type+")"+name] = val
|
2018-06-04 23:47:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-11 04:44:02 +00:00
|
|
|
func (b *Builder) copyFields(schema *types.Schema, input map[string]interface{}, op Operation) (map[string]interface{}, error) {
|
2017-10-16 02:23:15 +00:00
|
|
|
result := map[string]interface{}{}
|
|
|
|
|
|
|
|
if err := b.copyInputs(schema, input, op, result); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return result, b.checkDefaultAndRequired(schema, input, op, result)
|
|
|
|
}
|
|
|
|
|
2018-07-13 19:59:54 +00:00
|
|
|
func CheckFieldCriteria(fieldName string, field types.Field, value interface{}) error {
|
2017-10-16 02:23:15 +00:00
|
|
|
numVal, isNum := value.(int64)
|
|
|
|
strVal := ""
|
|
|
|
hasStrVal := false
|
|
|
|
|
|
|
|
if value == nil && field.Default != nil {
|
|
|
|
value = field.Default
|
|
|
|
}
|
|
|
|
|
2018-03-27 02:04:51 +00:00
|
|
|
if value != nil && value != "" {
|
2017-10-16 02:23:15 +00:00
|
|
|
hasStrVal = true
|
|
|
|
strVal = fmt.Sprint(value)
|
|
|
|
}
|
|
|
|
|
2017-11-21 20:46:30 +00:00
|
|
|
if (value == nil || value == "") && !field.Nullable {
|
2018-01-17 23:49:33 +00:00
|
|
|
if field.Default == nil {
|
|
|
|
return httperror.NewFieldAPIError(httperror.NotNullable, fieldName, "")
|
|
|
|
}
|
2017-10-16 02:23:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if isNum {
|
|
|
|
if field.Min != nil && numVal < *field.Min {
|
2017-11-21 20:46:30 +00:00
|
|
|
return httperror.NewFieldAPIError(httperror.MinLimitExceeded, fieldName, "")
|
2017-10-16 02:23:15 +00:00
|
|
|
}
|
|
|
|
if field.Max != nil && numVal > *field.Max {
|
2017-11-21 20:46:30 +00:00
|
|
|
return httperror.NewFieldAPIError(httperror.MaxLimitExceeded, fieldName, "")
|
2017-10-16 02:23:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-29 21:55:47 +00:00
|
|
|
if hasStrVal || value == "" {
|
2017-11-11 04:44:02 +00:00
|
|
|
if field.MinLength != nil && int64(len(strVal)) < *field.MinLength {
|
2017-11-21 20:46:30 +00:00
|
|
|
return httperror.NewFieldAPIError(httperror.MinLengthExceeded, fieldName, "")
|
2017-10-16 02:23:15 +00:00
|
|
|
}
|
2017-11-11 04:44:02 +00:00
|
|
|
if field.MaxLength != nil && int64(len(strVal)) > *field.MaxLength {
|
2017-11-21 20:46:30 +00:00
|
|
|
return httperror.NewFieldAPIError(httperror.MaxLengthExceeded, fieldName, "")
|
2017-10-16 02:23:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(field.Options) > 0 {
|
|
|
|
if hasStrVal || !field.Nullable {
|
|
|
|
found := false
|
|
|
|
for _, option := range field.Options {
|
|
|
|
if strVal == option {
|
|
|
|
found = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if !found {
|
2018-01-03 02:18:19 +00:00
|
|
|
return httperror.NewFieldAPIError(httperror.InvalidOption, fieldName, "")
|
2017-10-16 02:23:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(field.ValidChars) > 0 && hasStrVal {
|
|
|
|
for _, c := range strVal {
|
|
|
|
if !strings.ContainsRune(field.ValidChars, c) {
|
2018-01-03 02:18:19 +00:00
|
|
|
return httperror.NewFieldAPIError(httperror.InvalidCharacters, fieldName, "")
|
2017-10-16 02:23:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(field.InvalidChars) > 0 && hasStrVal {
|
|
|
|
if strings.ContainsAny(strVal, field.InvalidChars) {
|
2018-01-03 02:18:19 +00:00
|
|
|
return httperror.NewFieldAPIError(httperror.InvalidCharacters, fieldName, "")
|
2017-10-16 02:23:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-07-13 19:59:54 +00:00
|
|
|
func ConvertSimple(fieldType string, value interface{}, op Operation) (interface{}, error) {
|
2017-10-16 02:23:15 +00:00
|
|
|
if value == nil {
|
|
|
|
return value, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
switch fieldType {
|
|
|
|
case "json":
|
|
|
|
return value, nil
|
|
|
|
case "date":
|
2017-11-21 20:46:30 +00:00
|
|
|
v := convert.ToString(value)
|
|
|
|
if v == "" {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
return v, nil
|
2017-10-16 02:23:15 +00:00
|
|
|
case "boolean":
|
2017-11-11 04:44:02 +00:00
|
|
|
return convert.ToBool(value), nil
|
2017-10-16 02:23:15 +00:00
|
|
|
case "enum":
|
2017-11-11 04:44:02 +00:00
|
|
|
return convert.ToString(value), nil
|
2017-10-16 02:23:15 +00:00
|
|
|
case "int":
|
2017-11-11 04:44:02 +00:00
|
|
|
return convert.ToNumber(value)
|
2018-10-13 00:57:50 +00:00
|
|
|
case "float":
|
|
|
|
return convert.ToFloat(value)
|
2017-10-16 02:23:15 +00:00
|
|
|
case "password":
|
2017-11-11 04:44:02 +00:00
|
|
|
return convert.ToString(value), nil
|
2017-10-16 02:23:15 +00:00
|
|
|
case "string":
|
2018-09-04 16:27:12 +00:00
|
|
|
if op.IsList() {
|
|
|
|
return convert.ToStringNoTrim(value), nil
|
|
|
|
}
|
2017-11-11 04:44:02 +00:00
|
|
|
return convert.ToString(value), nil
|
2017-12-29 22:06:36 +00:00
|
|
|
case "dnsLabel":
|
2018-05-18 06:43:44 +00:00
|
|
|
str := convert.ToString(value)
|
|
|
|
if str == "" {
|
|
|
|
return "", nil
|
|
|
|
}
|
|
|
|
if op == Create || op == Update {
|
|
|
|
if errs := validation.IsDNS1123Label(str); len(errs) != 0 {
|
|
|
|
return value, httperror.NewAPIError(httperror.InvalidFormat, fmt.Sprintf("invalid value %s: %s", value,
|
|
|
|
strings.Join(errs, ",")))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return str, nil
|
2018-05-29 21:34:53 +00:00
|
|
|
case "dnsLabelRestricted":
|
|
|
|
str := convert.ToString(value)
|
|
|
|
if str == "" {
|
|
|
|
return "", nil
|
|
|
|
}
|
|
|
|
if op == Create || op == Update {
|
|
|
|
if errs := validation.IsDNS1035Label(str); len(errs) != 0 {
|
|
|
|
return value, httperror.NewAPIError(httperror.InvalidFormat, fmt.Sprintf("invalid value %s: %s", value,
|
|
|
|
strings.Join(errs, ",")))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return str, nil
|
2018-05-18 06:43:44 +00:00
|
|
|
case "hostname":
|
2018-01-21 06:23:24 +00:00
|
|
|
str := convert.ToString(value)
|
|
|
|
if str == "" {
|
|
|
|
return "", nil
|
|
|
|
}
|
2018-01-18 17:55:53 +00:00
|
|
|
if op == Create || op == Update {
|
2018-01-21 06:23:24 +00:00
|
|
|
if errs := validation.IsDNS1123Subdomain(str); len(errs) != 0 {
|
2018-01-18 17:55:53 +00:00
|
|
|
return value, httperror.NewAPIError(httperror.InvalidFormat, fmt.Sprintf("invalid value %s: %s", value,
|
|
|
|
strings.Join(errs, ",")))
|
|
|
|
}
|
|
|
|
}
|
2018-01-21 06:23:24 +00:00
|
|
|
return str, nil
|
2017-12-29 22:06:36 +00:00
|
|
|
case "intOrString":
|
|
|
|
num, err := convert.ToNumber(value)
|
|
|
|
if err == nil {
|
|
|
|
return num, nil
|
|
|
|
}
|
|
|
|
return convert.ToString(value), nil
|
2017-12-23 06:11:55 +00:00
|
|
|
case "base64":
|
|
|
|
return convert.ToString(value), nil
|
2017-10-16 02:23:15 +00:00
|
|
|
case "reference":
|
2017-11-11 04:44:02 +00:00
|
|
|
return convert.ToString(value), nil
|
2017-10-16 02:23:15 +00:00
|
|
|
}
|
|
|
|
|
2018-07-13 19:59:54 +00:00
|
|
|
return nil, ErrComplexType
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *Builder) convert(fieldType string, value interface{}, op Operation) (interface{}, error) {
|
|
|
|
if value == nil {
|
|
|
|
return value, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
switch {
|
|
|
|
case definition.IsMapType(fieldType):
|
|
|
|
return b.convertMap(fieldType, value, op)
|
|
|
|
case definition.IsArrayType(fieldType):
|
|
|
|
return b.convertArray(fieldType, value, op)
|
|
|
|
case definition.IsReferenceType(fieldType):
|
|
|
|
return b.convertReferenceType(fieldType, value)
|
|
|
|
}
|
|
|
|
|
|
|
|
newValue, err := ConvertSimple(fieldType, value, op)
|
|
|
|
if err == ErrComplexType {
|
|
|
|
return b.convertType(fieldType, value, op)
|
|
|
|
}
|
|
|
|
return newValue, err
|
2017-10-16 02:23:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (b *Builder) convertType(fieldType string, value interface{}, op Operation) (interface{}, error) {
|
2017-11-11 04:44:02 +00:00
|
|
|
schema := b.Schemas.Schema(b.Version, fieldType)
|
2017-10-16 02:23:15 +00:00
|
|
|
if schema == nil {
|
2017-11-21 20:46:30 +00:00
|
|
|
return nil, httperror.NewAPIError(httperror.InvalidType, "Failed to find type "+fieldType)
|
2017-10-16 02:23:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
mapValue, ok := value.(map[string]interface{})
|
|
|
|
if !ok {
|
2017-11-21 20:46:30 +00:00
|
|
|
return nil, httperror.NewAPIError(httperror.InvalidFormat, fmt.Sprintf("Value can not be converted to type %s: %v", fieldType, value))
|
2017-10-16 02:23:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return b.Construct(schema, mapValue, op)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *Builder) convertReferenceType(fieldType string, value interface{}) (string, error) {
|
2017-11-11 04:44:02 +00:00
|
|
|
subType := definition.SubType(fieldType)
|
|
|
|
strVal := convert.ToString(value)
|
|
|
|
if b.RefValidator != nil && !b.RefValidator.Validate(subType, strVal) {
|
2017-11-21 20:46:30 +00:00
|
|
|
return "", httperror.NewAPIError(httperror.InvalidReference, fmt.Sprintf("Not found type: %s id: %s", subType, strVal))
|
2017-10-16 02:23:15 +00:00
|
|
|
}
|
|
|
|
return strVal, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *Builder) convertArray(fieldType string, value interface{}, op Operation) ([]interface{}, error) {
|
2017-11-11 04:44:02 +00:00
|
|
|
if strSliceValue, ok := value.([]string); ok {
|
|
|
|
// Form data will be []string
|
2017-11-29 21:27:02 +00:00
|
|
|
var result []interface{}
|
2017-11-11 04:44:02 +00:00
|
|
|
for _, value := range strSliceValue {
|
|
|
|
result = append(result, value)
|
|
|
|
}
|
|
|
|
return result, nil
|
|
|
|
}
|
|
|
|
|
2017-10-16 02:23:15 +00:00
|
|
|
sliceValue, ok := value.([]interface{})
|
|
|
|
if !ok {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
2018-07-13 19:59:54 +00:00
|
|
|
var result []interface{}
|
2020-08-20 22:28:04 +00:00
|
|
|
if value == nil {
|
|
|
|
return result, nil
|
|
|
|
}
|
|
|
|
result = make([]interface{}, 0)
|
2017-11-11 04:44:02 +00:00
|
|
|
subType := definition.SubType(fieldType)
|
2017-10-16 02:23:15 +00:00
|
|
|
|
|
|
|
for _, value := range sliceValue {
|
|
|
|
val, err := b.convert(subType, value, op)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
result = append(result, val)
|
|
|
|
}
|
|
|
|
|
|
|
|
return result, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *Builder) convertMap(fieldType string, value interface{}, op Operation) (map[string]interface{}, error) {
|
|
|
|
mapValue, ok := value.(map[string]interface{})
|
|
|
|
if !ok {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
result := map[string]interface{}{}
|
2017-11-11 04:44:02 +00:00
|
|
|
subType := definition.SubType(fieldType)
|
2017-10-16 02:23:15 +00:00
|
|
|
|
|
|
|
for key, value := range mapValue {
|
|
|
|
val, err := b.convert(subType, value, op)
|
|
|
|
if err != nil {
|
2017-11-21 20:46:30 +00:00
|
|
|
return nil, httperror.WrapAPIError(err, httperror.InvalidFormat, err.Error())
|
2017-10-16 02:23:15 +00:00
|
|
|
}
|
|
|
|
result[key] = val
|
|
|
|
}
|
|
|
|
|
|
|
|
return result, nil
|
|
|
|
}
|
|
|
|
|
2017-11-11 04:44:02 +00:00
|
|
|
func fieldMatchesOp(field types.Field, op Operation) bool {
|
2017-10-16 02:23:15 +00:00
|
|
|
switch op {
|
|
|
|
case Create:
|
|
|
|
return field.Create
|
|
|
|
case Update:
|
|
|
|
return field.Update
|
2017-11-11 04:44:02 +00:00
|
|
|
case List:
|
2018-02-10 01:02:01 +00:00
|
|
|
if field.Type == "password" {
|
|
|
|
return false
|
|
|
|
}
|
2017-11-11 04:44:02 +00:00
|
|
|
return !field.WriteOnly
|
2018-01-20 04:28:40 +00:00
|
|
|
case ListForCreate:
|
2018-02-10 01:02:01 +00:00
|
|
|
if field.Type == "password" {
|
|
|
|
return false
|
|
|
|
}
|
2018-01-20 04:28:40 +00:00
|
|
|
return true
|
2017-10-16 02:23:15 +00:00
|
|
|
default:
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|