1
0
mirror of https://github.com/rancher/norman.git synced 2025-09-06 17:50:25 +00:00

Add edit/export modes and drop default resource fields if empty

This commit is contained in:
Darren Shepherd
2018-06-04 16:47:35 -07:00
parent da330debe9
commit 6db31068a0
3 changed files with 123 additions and 18 deletions

View File

@@ -30,11 +30,15 @@ type Builder struct {
Version *types.APIVersion Version *types.APIVersion
Schemas *types.Schemas Schemas *types.Schemas
RefValidator types.ReferenceValidator RefValidator types.ReferenceValidator
edit bool
export bool
} }
func NewBuilder(apiRequest *types.APIContext) *Builder { func NewBuilder(apiRequest *types.APIContext) *Builder {
return &Builder{ return &Builder{
apiContext: apiRequest, apiContext: apiRequest,
edit: apiRequest.Option("edit") == "true",
export: apiRequest.Option("export") == "true",
Version: apiRequest.Version, Version: apiRequest.Version,
Schemas: apiRequest.Schemas, Schemas: apiRequest.Schemas,
RefValidator: apiRequest.ReferenceValidator, RefValidator: apiRequest.ReferenceValidator,
@@ -99,7 +103,7 @@ func (b *Builder) copyInputs(schema *types.Schema, input map[string]interface{},
} }
} }
if op.IsList() { if op.IsList() && !b.edit && !b.export {
if !convert.IsEmpty(input["type"]) { if !convert.IsEmpty(input["type"]) {
result["type"] = input["type"] result["type"] = input["type"]
} }
@@ -142,9 +146,69 @@ func (b *Builder) checkDefaultAndRequired(schema *types.Schema, input map[string
} }
} }
if op.IsList() && b.edit {
b.populateMissingFieldsForEdit(schema, result)
}
if op.IsList() && b.export {
b.dropDefaults(schema, result)
}
return nil return nil
} }
func (b *Builder) dropDefaults(schema *types.Schema, result map[string]interface{}) {
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
}
if convert.IsEmpty(existingVal) {
delete(result, name)
continue
}
}
}
func (b *Builder) populateMissingFieldsForEdit(schema *types.Schema, result map[string]interface{}) {
for name, field := range schema.ResourceFields {
if !field.Update {
continue
}
_, hasKey := result[name]
if hasKey {
continue
}
if field.Default != nil {
result[name] = field.Default
} else {
val, err := b.convert(field.Type, nil, List)
if err == nil {
result[name] = val
}
}
}
}
func (b *Builder) copyFields(schema *types.Schema, input map[string]interface{}, op Operation) (map[string]interface{}, error) { func (b *Builder) copyFields(schema *types.Schema, input map[string]interface{}, op Operation) (map[string]interface{}, error) {
result := map[string]interface{}{} result := map[string]interface{}{}

View File

@@ -343,7 +343,28 @@ type MultiErrors struct {
Errors []error Errors []error
} }
func NewErrors(errors ...error) error { type Errors struct {
errors []error
}
func (e *Errors) Add(err error) {
if err != nil {
e.errors = append(e.errors, err)
}
}
func (e *Errors) Err() error {
return NewErrors(e.errors...)
}
func NewErrors(inErrors ...error) error {
var errors []error
for _, err := range inErrors {
if err != nil {
errors = append(errors, err)
}
}
if len(errors) == 0 { if len(errors) == 0 {
return nil return nil
} else if len(errors) == 1 { } else if len(errors) == 1 {

View File

@@ -12,13 +12,14 @@ type ValuesMap struct {
} }
type RawResource struct { type RawResource struct {
ID string `json:"id,omitempty" yaml:"id,omitempty"` ID string `json:"id,omitempty" yaml:"id,omitempty"`
Type string `json:"type,omitempty" yaml:"type,omitempty"` Type string `json:"type,omitempty" yaml:"type,omitempty"`
Schema *Schema `json:"-" yaml:"-"` Schema *Schema `json:"-" yaml:"-"`
Links map[string]string `json:"links" yaml:"links"` Links map[string]string `json:"links,omitempty" yaml:"links,omitempty"`
Actions map[string]string `json:"actions" yaml:"actions"` Actions map[string]string `json:"actions,omitempty" yaml:"actions,omitempty"`
Values map[string]interface{} `json:",inline"` Values map[string]interface{} `json:",inline" yaml:",inline"`
ActionLinks bool `json:"-"` ActionLinks bool `json:"-" yaml:"-"`
DropReadOnly bool `json:"-" yaml:"-"`
} }
func (r *RawResource) AddAction(apiContext *APIContext, name string) { func (r *RawResource) AddAction(apiContext *APIContext, name string) {
@@ -26,23 +27,38 @@ func (r *RawResource) AddAction(apiContext *APIContext, name string) {
} }
func (r *RawResource) MarshalJSON() ([]byte, error) { func (r *RawResource) MarshalJSON() ([]byte, error) {
return json.Marshal(r.ToMap())
}
func (r *RawResource) ToMap() map[string]interface{} {
data := map[string]interface{}{} data := map[string]interface{}{}
for k, v := range r.Values { for k, v := range r.Values {
data[k] = v data[k] = v
} }
if r.ID != "" {
if r.ID != "" && !r.DropReadOnly {
data["id"] = r.ID data["id"] = r.ID
} }
data["type"] = r.Type if r.Type != "" && !r.DropReadOnly {
data["baseType"] = r.Schema.BaseType data["type"] = r.Type
data["links"] = r.Links
if r.ActionLinks {
data["actionLinks"] = r.Actions
} else {
data["actions"] = r.Actions
} }
return json.Marshal(data) if r.Schema.BaseType != "" && !r.DropReadOnly {
data["baseType"] = r.Schema.BaseType
}
if len(r.Links) > 0 && !r.DropReadOnly {
data["links"] = r.Links
}
if len(r.Actions) > 0 && !r.DropReadOnly {
if r.ActionLinks {
data["actionLinks"] = r.Actions
} else {
data["actions"] = r.Actions
}
}
return data
} }
type ActionHandler func(actionName string, action *Action, request *APIContext) error type ActionHandler func(actionName string, action *Action, request *APIContext) error
@@ -124,6 +140,10 @@ func GetAPIContext(ctx context.Context) *APIContext {
return apiContext return apiContext
} }
func (r *APIContext) Option(key string) string {
return r.Query.Get("_" + key)
}
func (r *APIContext) WriteResponse(code int, obj interface{}) { func (r *APIContext) WriteResponse(code int, obj interface{}) {
r.ResponseWriter.Write(r, code, obj) r.ResponseWriter.Write(r, code, obj)
} }