Address PR comments

This commit is contained in:
Phillip Wittrock 2017-10-02 14:36:51 -07:00
parent df5fc7a2df
commit 32e16d09b3
14 changed files with 190 additions and 269 deletions

View File

@ -76,33 +76,6 @@ type FieldMeta interface {
GetFieldType() string GetFieldType() string
} }
// HasElementData contains whether a field was set in the recorded, local and remote sources
type HasElementData struct {
// RecordedSet is true if the field was found in the recorded object
RecordedSet bool
// LocalSet is true if the field was found in the local object
LocalSet bool
// RemoteSet is true if the field was found in the remote object
RemoteSet bool
}
// HasRecorded implements Element.HasRecorded
func (e HasElementData) HasRecorded() bool {
return e.RecordedSet
}
// HasLocal implements Element.HasLocal
func (e HasElementData) HasLocal() bool {
return e.LocalSet
}
// HasRemote implements Element.HasRemote
func (e HasElementData) HasRemote() bool {
return e.RemoteSet
}
// FieldMetaImpl implements FieldMeta // FieldMetaImpl implements FieldMeta
type FieldMetaImpl struct { type FieldMetaImpl struct {
// The type of merge strategy to use for this field // The type of merge strategy to use for this field
@ -195,7 +168,7 @@ type CombinedPrimitiveSlice struct {
// PrimitiveListItem represents a single value in a slice of primitives // PrimitiveListItem represents a single value in a slice of primitives
type PrimitiveListItem struct { type PrimitiveListItem struct {
// Value is the value of the primitive, should match Recorded, Local and Remote // Value is the value of the primitive, should match recorded, local and remote
Value interface{} Value interface{}
RawElementData RawElementData
@ -230,21 +203,27 @@ func (s *CombinedPrimitiveSlice) upsert(l interface{}) *PrimitiveListItem {
// slice for either the local or remote, set on that value as the recorded value // slice for either the local or remote, set on that value as the recorded value
// Otherwise append a new item to the list with the recorded value. // Otherwise append a new item to the list with the recorded value.
func (s *CombinedPrimitiveSlice) UpsertRecorded(l interface{}) { func (s *CombinedPrimitiveSlice) UpsertRecorded(l interface{}) {
s.upsert(l).Recorded = l v := s.upsert(l)
v.recorded = l
v.recordedSet = true
} }
// UpsertLocal adds l to the slice. If there is already a value of l in the // UpsertLocal adds l to the slice. If there is already a value of l in the
// slice for either the recorded or remote, set on that value as the local value // slice for either the recorded or remote, set on that value as the local value
// Otherwise append a new item to the list with the local value. // Otherwise append a new item to the list with the local value.
func (s *CombinedPrimitiveSlice) UpsertLocal(l interface{}) { func (s *CombinedPrimitiveSlice) UpsertLocal(l interface{}) {
s.upsert(l).Local = l v := s.upsert(l)
v.local = l
v.localSet = true
} }
// UpsertRemote adds l to the slice. If there is already a value of l in the // UpsertRemote adds l to the slice. If there is already a value of l in the
// slice for either the local or recorded, set on that value as the remote value // slice for either the local or recorded, set on that value as the remote value
// Otherwise append a new item to the list with the remote value. // Otherwise append a new item to the list with the remote value.
func (s *CombinedPrimitiveSlice) UpsertRemote(l interface{}) { func (s *CombinedPrimitiveSlice) UpsertRemote(l interface{}) {
s.upsert(l).Recorded = l v := s.upsert(l)
v.remote = l
v.remoteSet = true
} }
// ListItem represents a single value in a slice of maps or types // ListItem represents a single value in a slice of maps or types
@ -299,7 +278,8 @@ func (s *CombinedMapSlice) UpsertRecorded(key MergeKeys, l interface{}) error {
if err != nil { if err != nil {
return err return err
} }
item.Recorded = l item.recorded = l
item.recordedSet = true
return nil return nil
} }
@ -311,7 +291,8 @@ func (s *CombinedMapSlice) UpsertLocal(key MergeKeys, l interface{}) error {
if err != nil { if err != nil {
return err return err
} }
item.Local = l item.local = l
item.localSet = true
return nil return nil
} }
@ -323,7 +304,8 @@ func (s *CombinedMapSlice) UpsertRemote(key MergeKeys, l interface{}) error {
if err != nil { if err != nil {
return err return err
} }
item.Remote = l item.remote = l
item.remoteSet = true
return nil return nil
} }
@ -345,29 +327,97 @@ func IsAdd(e Element) bool {
return e.HasLocal() && !e.HasRemote() return e.HasLocal() && !e.HasRemote()
} }
// NewRawElementData returns a new RawElementData, setting IsSet to true for
// non-nil values, and leaving IsSet false for nil values.
// Note: use this only when you want a nil-value to be considered "unspecified"
// (ignore) and not "unset" (deleted).
func NewRawElementData(recorded, local, remote interface{}) RawElementData {
data := RawElementData{}
if recorded != nil {
data.SetRecorded(recorded)
}
if local != nil {
data.SetLocal(local)
}
if remote != nil {
data.SetRemote(remote)
}
return data
}
// RawElementData contains the raw recorded, local and remote data // RawElementData contains the raw recorded, local and remote data
// and metadata about whethere or not each was set
type RawElementData struct { type RawElementData struct {
// recorded contains the value of the field from the recorded object HasElementData
Recorded interface{}
// Local contains the value of the field from the recorded object recorded interface{}
Local interface{} local interface{}
remote interface{}
}
// Remote contains the value of the field from the recorded object // SetRecorded sets the recorded value
Remote interface{} func (b *RawElementData) SetRecorded(value interface{}) {
b.recorded = value
b.recordedSet = true
}
// SetLocal sets the recorded value
func (b *RawElementData) SetLocal(value interface{}) {
b.local = value
b.localSet = true
}
// SetRemote sets the recorded value
func (b *RawElementData) SetRemote(value interface{}) {
b.remote = value
b.remoteSet = true
} }
// GetRecorded implements Element.GetRecorded // GetRecorded implements Element.GetRecorded
func (e RawElementData) GetRecorded() interface{} { func (b RawElementData) GetRecorded() interface{} {
return e.Recorded // https://golang.org/doc/faq#nil_error
if b.recorded == nil {
return nil
}
return b.recorded
} }
// GetLocal implements Element.GetLocal // GetLocal implements Element.GetLocal
func (e RawElementData) GetLocal() interface{} { func (b RawElementData) GetLocal() interface{} {
return e.Local // https://golang.org/doc/faq#nil_error
if b.local == nil {
return nil
}
return b.local
} }
// GetRemote implements Element.GetRemote // GetRemote implements Element.GetRemote
func (e RawElementData) GetRemote() interface{} { func (b RawElementData) GetRemote() interface{} {
return e.Remote // https://golang.org/doc/faq#nil_error
if b.remote == nil {
return nil
}
return b.remote
}
// HasElementData contains whether a field was set in the recorded, local and remote sources
type HasElementData struct {
recordedSet bool
localSet bool
remoteSet bool
}
// HasRecorded implements Element.HasRecorded
func (e HasElementData) HasRecorded() bool {
return e.recordedSet
}
// HasLocal implements Element.HasLocal
func (e HasElementData) HasLocal() bool {
return e.localSet
}
// HasRemote implements Element.HasRemote
func (e HasElementData) HasRemote() bool {
return e.remoteSet
} }

View File

@ -22,10 +22,6 @@ type ListElement struct {
// FieldMetaImpl contains metadata about the field from openapi // FieldMetaImpl contains metadata about the field from openapi
FieldMetaImpl FieldMetaImpl
// HasElementData contains whether the field was set
HasElementData
// ListElementData contains the value a field was set to
ListElementData ListElementData
// Values contains the combined recorded-local-remote value of each item in the list // Values contains the combined recorded-local-remote value of each item in the list
@ -44,39 +40,28 @@ var _ Element = &ListElement{}
// ListElementData contains the recorded, local and remote data for a list // ListElementData contains the recorded, local and remote data for a list
type ListElementData struct { type ListElementData struct {
// recorded contains the value of the field from the recorded object RawElementData
Recorded []interface{}
// Local contains the value of the field from the recorded object
Local []interface{}
// Remote contains the value of the field from the recorded object
Remote []interface{}
} }
// GetRecorded implements Element.GetRecorded // GetRecordedList returns the Recorded value as a list
func (e ListElementData) GetRecorded() interface{} { func (e ListElementData) GetRecordedList() []interface{} {
// https://golang.org/doc/faq#nil_error return sliceCast(e.recorded)
if e.Recorded == nil { }
// GetLocalList returns the Local value as a list
func (e ListElementData) GetLocalList() []interface{} {
return sliceCast(e.local)
}
// GetRemoteList returns the Remote value as a list
func (e ListElementData) GetRemoteList() []interface{} {
return sliceCast(e.remote)
}
// sliceCast casts i to a slice if it is non-nil, otherwise returns nil
func sliceCast(i interface{}) []interface{} {
if i == nil {
return nil return nil
} }
return e.Recorded return i.([]interface{})
}
// GetLocal implements Element.GetLocal
func (e ListElementData) GetLocal() interface{} {
// https://golang.org/doc/faq#nil_error
if e.Local == nil {
return nil
}
return e.Local
}
// GetRemote implements Element.GetRemote
func (e ListElementData) GetRemote() interface{} {
// https://golang.org/doc/faq#nil_error
if e.Remote == nil {
return nil
}
return e.Remote
} }

View File

@ -22,9 +22,6 @@ type MapElement struct {
// FieldMetaImpl contains metadata about the field from openapi // FieldMetaImpl contains metadata about the field from openapi
FieldMetaImpl FieldMetaImpl
// HasElementData contains whether the field was set
HasElementData
// MapElementData contains the value a field was set to // MapElementData contains the value a field was set to
MapElementData MapElementData
@ -48,39 +45,28 @@ var _ Element = &MapElement{}
// MapElementData contains the recorded, local and remote data for a map or type // MapElementData contains the recorded, local and remote data for a map or type
type MapElementData struct { type MapElementData struct {
// recorded contains the value of the field from the recorded object RawElementData
Recorded map[string]interface{}
// Local contains the value of the field from the recorded object
Local map[string]interface{}
// Remote contains the value of the field from the recorded object
Remote map[string]interface{}
} }
// GetRecorded implements Element.GetRecorded // GetRecordedMap returns the Recorded value as a map
func (e MapElementData) GetRecorded() interface{} { func (e MapElementData) GetRecordedMap() map[string]interface{} {
// https://golang.org/doc/faq#nil_error return mapCast(e.recorded)
if e.Recorded == nil { }
// GetLocalMap returns the Local value as a map
func (e MapElementData) GetLocalMap() map[string]interface{} {
return mapCast(e.local)
}
// GetRemoteMap returns the Remote value as a map
func (e MapElementData) GetRemoteMap() map[string]interface{} {
return mapCast(e.remote)
}
// mapCast casts i to a map if it is non-nil, otherwise returns nil
func mapCast(i interface{}) map[string]interface{} {
if i == nil {
return nil return nil
} }
return e.Recorded return i.(map[string]interface{})
}
// GetLocal implements Element.GetLocal
func (e MapElementData) GetLocal() interface{} {
// https://golang.org/doc/faq#nil_error
if e.Local == nil {
return nil
}
return e.Local
}
// GetRemote implements Element.GetRemote
func (e MapElementData) GetRemote() interface{} {
// https://golang.org/doc/faq#nil_error
if e.Remote == nil {
return nil
}
return e.Remote
} }

View File

@ -48,14 +48,9 @@ func (b *Factory) CreateElement(recorded, local, remote map[string]interface{})
return nil, err return nil, err
} }
// Get the item for the type data := apply.NewRawElementData(recorded, local, remote)
hasRecorded := recorded != nil
hasLocal := local != nil
hasRemote := remote != nil
fieldName := "" fieldName := ""
item, err := visitor.getItem(oapiKind, fieldName, item, err := visitor.getItem(oapiKind, fieldName, data)
apply.RawElementData{recorded, local, remote},
apply.HasElementData{hasRecorded, hasLocal, hasRemote})
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -65,10 +60,8 @@ func (b *Factory) CreateElement(recorded, local, remote map[string]interface{})
} }
// getItem returns the appropriate Item based on the underlying type of the arguments // getItem returns the appropriate Item based on the underlying type of the arguments
func (v *ElementBuildingVisitor) getItem(s openapi.Schema, name string, func (v *ElementBuildingVisitor) getItem(s openapi.Schema, name string, data apply.RawElementData) (Item, error) {
data apply.RawElementData, kind, err := getType(data.GetRecorded(), data.GetLocal(), data.GetRemote())
isSet apply.HasElementData) (Item, error) {
kind, err := getType(data.Recorded, data.Local, data.Remote)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -86,38 +79,23 @@ func (v *ElementBuildingVisitor) getItem(s openapi.Schema, name string,
if err != nil { if err != nil {
return nil, fmt.Errorf("expected openapi Primitive, was %T for %v", s, kind) return nil, fmt.Errorf("expected openapi Primitive, was %T for %v", s, kind)
} }
return &primitiveItem{name, p, isSet, data}, nil return &primitiveItem{name, p, data}, nil
case reflect.Array, reflect.Slice: case reflect.Array, reflect.Slice:
a, err := getArray(s) a, err := getArray(s)
if err != nil { if err != nil {
return nil, fmt.Errorf("expected openapi Array, was %T for %v", s, kind) return nil, fmt.Errorf("expected openapi Array, was %T for %v", s, kind)
} }
return &listItem{name, a, isSet, return &listItem{name, a, apply.ListElementData{data}}, nil
apply.ListElementData{
sliceCast(data.Recorded),
sliceCast(data.Local),
sliceCast(data.Remote),
}}, nil
case reflect.Map: case reflect.Map:
if k, err := getKind(s); err == nil { if k, err := getKind(s); err == nil {
return &typeItem{name, k, isSet, return &typeItem{name, k, apply.MapElementData{data}}, nil
apply.MapElementData{
mapCast(data.Recorded),
mapCast(data.Local),
mapCast(data.Remote),
}}, nil
} }
// If it looks like a map, and no openapi type is found, default to mapItem // If it looks like a map, and no openapi type is found, default to mapItem
m, err := getMap(s) m, err := getMap(s)
if err != nil { if err != nil {
return nil, fmt.Errorf("expected openapi Kind or Map, was %T for %v", s, kind) return nil, fmt.Errorf("expected openapi Kind or Map, was %T for %v", s, kind)
} }
return &mapItem{name, m, isSet, return &mapItem{name, m, apply.MapElementData{data}}, nil
apply.MapElementData{
mapCast(data.Recorded),
mapCast(data.Local),
mapCast(data.Remote),
}}, nil
} }
return nil, fmt.Errorf("unsupported type type %v", kind) return nil, fmt.Errorf("unsupported type type %v", kind)
} }

View File

@ -33,7 +33,6 @@ type primitiveItem struct {
Name string Name string
Primitive *openapi.Primitive Primitive *openapi.Primitive
apply.HasElementData
apply.RawElementData apply.RawElementData
} }
@ -54,7 +53,6 @@ type listItem struct {
Name string Name string
Array *openapi.Array Array *openapi.Array
apply.HasElementData
apply.ListElementData apply.ListElementData
} }
@ -75,7 +73,6 @@ type mapItem struct {
Name string Name string
Map *openapi.Map Map *openapi.Map
apply.HasElementData
apply.MapElementData apply.MapElementData
} }
@ -96,7 +93,6 @@ type typeItem struct {
Name string Name string
Type *openapi.Kind Type *openapi.Kind
apply.HasElementData
apply.MapElementData apply.MapElementData
} }

View File

@ -47,7 +47,6 @@ func (v ElementBuildingVisitor) doPrimitiveList(meta apply.FieldMetaImpl, item *
MergeType: "merge", MergeType: "merge",
Name: item.Name, Name: item.Name,
}, },
HasElementData: item.HasElementData,
ListElementData: item.ListElementData, ListElementData: item.ListElementData,
Values: []apply.Element{}, Values: []apply.Element{},
} }
@ -57,32 +56,26 @@ func (v ElementBuildingVisitor) doPrimitiveList(meta apply.FieldMetaImpl, item *
// Locally defined items come first and retain their order // Locally defined items come first and retain their order
// as defined locally // as defined locally
for _, l := range item.Local { for _, l := range item.GetLocalList() {
orderedKeys.UpsertLocal(l) orderedKeys.UpsertLocal(l)
} }
// Mixin remote values, adding any that are not present locally // Mixin remote values, adding any that are not present locally
for _, l := range item.Remote { for _, l := range item.GetRemoteList() {
orderedKeys.UpsertRemote(l) orderedKeys.UpsertRemote(l)
} }
// Mixin recorded values, adding any that are not present locally // Mixin recorded values, adding any that are not present locally
// or remotely // or remotely
for _, l := range item.Recorded { for _, l := range item.GetRecordedList() {
orderedKeys.UpsertRecorded(l) orderedKeys.UpsertRecorded(l)
} }
for i, l := range orderedKeys.Items { for i, l := range orderedKeys.Items {
recordedSet := l.Recorded != nil
localSet := l.Local != nil
remoteSet := l.Remote != nil
var s openapi.Schema var s openapi.Schema
if item.Array != nil && item.Array.SubType != nil { if item.Array != nil && item.Array.SubType != nil {
s = item.Array.SubType s = item.Array.SubType
} }
subitem, err := v.getItem(s, fmt.Sprintf("%d", i), subitem, err := v.getItem(s, fmt.Sprintf("%d", i), l.RawElementData)
l.RawElementData,
apply.HasElementData{recordedSet, localSet, remoteSet})
if err != nil { if err != nil {
return nil, err return nil, err
@ -111,7 +104,6 @@ func (v ElementBuildingVisitor) doMapList(meta apply.FieldMetaImpl, item *listIt
MergeKeys: key, MergeKeys: key,
Name: item.Name, Name: item.Name,
}, },
HasElementData: item.HasElementData,
ListElementData: item.ListElementData, ListElementData: item.ListElementData,
Values: []apply.Element{}, Values: []apply.Element{},
} }
@ -121,31 +113,25 @@ func (v ElementBuildingVisitor) doMapList(meta apply.FieldMetaImpl, item *listIt
// Locally defined items come first and retain their order // Locally defined items come first and retain their order
// as defined locally // as defined locally
for _, l := range item.Local { for _, l := range item.GetLocalList() {
orderedKeys.UpsertLocal(key, l) orderedKeys.UpsertLocal(key, l)
} }
// Mixin remote values, adding any that are not present locally // Mixin remote values, adding any that are not present locally
for _, l := range item.Remote { for _, l := range item.GetRemoteList() {
orderedKeys.UpsertRemote(key, l) orderedKeys.UpsertRemote(key, l)
} }
// Mixin recorded values, adding any that are not present locally // Mixin recorded values, adding any that are not present locally
// or remotely // or remotely
for _, l := range item.Recorded { for _, l := range item.GetRecordedList() {
orderedKeys.UpsertRecorded(key, l) orderedKeys.UpsertRecorded(key, l)
} }
for i, l := range orderedKeys.Items { for i, l := range orderedKeys.Items {
recordedSet := l.Recorded != nil
localSet := l.Local != nil
remoteSet := l.Remote != nil
var s openapi.Schema var s openapi.Schema
if item.Array != nil && item.Array.SubType != nil { if item.Array != nil && item.Array.SubType != nil {
s = item.Array.SubType s = item.Array.SubType
} }
subitem, err := v.getItem(s, fmt.Sprintf("%d", i), subitem, err := v.getItem(s, fmt.Sprintf("%d", i), l.RawElementData)
l.RawElementData,
apply.HasElementData{recordedSet, localSet, remoteSet})
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -166,30 +152,30 @@ func (v ElementBuildingVisitor) doMapList(meta apply.FieldMetaImpl, item *listIt
// replaceListElement builds a new ListElement from a listItem // replaceListElement builds a new ListElement from a listItem
// Uses the "replace" strategy and identify "same" elements across lists by their index // Uses the "replace" strategy and identify "same" elements across lists by their index
func (v ElementBuildingVisitor) replaceListElement(meta apply.FieldMetaImpl, item *listItem) (*apply.ListElement, error) { func (v ElementBuildingVisitor) replaceListElement(meta apply.FieldMetaImpl, item *listItem) (*apply.ListElement, error) {
result := &apply.ListElement{ meta.Name = item.Name
FieldMetaImpl: meta, result := &apply.ListElement{meta, item.ListElementData, []apply.Element{}}
ListElementData: item.ListElementData,
HasElementData: item.HasElementData,
Values: []apply.Element{},
}
result.Name = item.Name
// Use the max length to iterate over the slices // Use the max length to iterate over the slices
for i := 0; i < max(len(item.Recorded), len(item.Local), len(item.Remote)); i++ { for i := 0; i < max(len(item.GetRecordedList()), len(item.GetLocalList()), len(item.GetRemoteList())); i++ {
// Lookup the item from each list // Lookup the item from each list
recorded, recordedSet := boundsSafeLookup(i, item.Recorded) data := apply.RawElementData{}
local, localSet := boundsSafeLookup(i, item.Local) if recorded, recordedSet := boundsSafeLookup(i, item.GetRecordedList()); recordedSet {
remote, remoteSet := boundsSafeLookup(i, item.Remote) data.SetRecorded(recorded)
}
if local, localSet := boundsSafeLookup(i, item.GetLocalList()); localSet {
data.SetLocal(local)
}
if remote, remoteSet := boundsSafeLookup(i, item.GetRemoteList()); remoteSet {
data.SetRemote(remote)
}
// Create the Item // Create the Item
var s openapi.Schema var s openapi.Schema
if item.Array != nil && item.Array.SubType != nil { if item.Array != nil && item.Array.SubType != nil {
s = item.Array.SubType s = item.Array.SubType
} }
subitem, err := v.getItem(s, fmt.Sprintf("%d", i), subitem, err := v.getItem(s, fmt.Sprintf("%d", i), data)
apply.RawElementData{recorded, local, remote},
apply.HasElementData{recordedSet, localSet, remoteSet})
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -33,18 +33,13 @@ func (v ElementBuildingVisitor) mapElement(meta apply.FieldMetaImpl, item *mapIt
} }
// Collect same fields from multiple maps into a map of elements // Collect same fields from multiple maps into a map of elements
values, err := v.createMapValues(fn, meta, item.HasElementData, item.MapElementData) values, err := v.createMapValues(fn, meta, item.MapElementData)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Return the result // Return the result
return &apply.MapElement{ return &apply.MapElement{meta, item.MapElementData, values}, nil
FieldMetaImpl: meta,
HasElementData: item.HasElementData,
MapElementData: item.MapElementData,
Values: values,
}, nil
} }
// schemaFn returns the schema for a field or map value based on its name or key // schemaFn returns the schema for a field or map value based on its name or key
@ -55,20 +50,24 @@ type schemaFn func(key string) openapi.Schema
func (v ElementBuildingVisitor) createMapValues( func (v ElementBuildingVisitor) createMapValues(
schemaFn schemaFn, schemaFn schemaFn,
meta apply.FieldMetaImpl, meta apply.FieldMetaImpl,
hasData apply.HasElementData,
data apply.MapElementData) (map[string]apply.Element, error) { data apply.MapElementData) (map[string]apply.Element, error) {
// Collate each key in the map // Collate each key in the map
values := map[string]apply.Element{} values := map[string]apply.Element{}
for _, key := range keysUnion(data.Recorded, data.Local, data.Remote) { for _, key := range keysUnion(data.GetRecordedMap(), data.GetLocalMap(), data.GetRemoteMap()) {
recorded, recordedSet := nilSafeLookup(key, data.Recorded) combined := apply.RawElementData{}
local, localSet := nilSafeLookup(key, data.Local) if recorded, recordedSet := nilSafeLookup(key, data.GetRecordedMap()); recordedSet {
remote, remoteSet := nilSafeLookup(key, data.Remote) combined.SetRecorded(recorded)
}
if local, localSet := nilSafeLookup(key, data.GetLocalMap()); localSet {
combined.SetLocal(local)
}
if remote, remoteSet := nilSafeLookup(key, data.GetRemoteMap()); remoteSet {
combined.SetRemote(remote)
}
// Create an item for the field // Create an item for the field
field, err := v.getItem(schemaFn(key), key, field, err := v.getItem(schemaFn(key), key, combined)
apply.RawElementData{recorded, local, remote},
apply.HasElementData{recordedSet, localSet, remoteSet})
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -20,11 +20,6 @@ import "k8s.io/kubernetes/pkg/kubectl/apply"
// primitiveElement builds a new primitiveElement from a PrimitiveItem // primitiveElement builds a new primitiveElement from a PrimitiveItem
func (v ElementBuildingVisitor) primitiveElement(item *primitiveItem) (*apply.PrimitiveElement, error) { func (v ElementBuildingVisitor) primitiveElement(item *primitiveItem) (*apply.PrimitiveElement, error) {
result := &apply.PrimitiveElement{ meta := apply.FieldMetaImpl{Name: item.Name}
HasElementData: item.HasElementData, return &apply.PrimitiveElement{meta, item.RawElementData}, nil
RawElementData: item.RawElementData,
}
result.Name = item.Name
return result, nil
} }

View File

@ -32,16 +32,11 @@ func (v ElementBuildingVisitor) typeElement(meta apply.FieldMetaImpl, item *type
} }
// Collect same fields from multiple maps into a map of elements // Collect same fields from multiple maps into a map of elements
values, err := v.createMapValues(fn, meta, item.HasElementData, item.MapElementData) values, err := v.createMapValues(fn, meta, item.MapElementData)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Return the result // Return the result
return &apply.TypeElement{ return &apply.TypeElement{meta, item.MapElementData, values}, nil
FieldMetaImpl: meta,
HasElementData: item.HasElementData,
MapElementData: item.MapElementData,
Values: values,
}, nil
} }

View File

@ -95,22 +95,6 @@ func getType(args ...interface{}) (reflect.Type, error) {
return reflect.TypeOf(last), nil return reflect.TypeOf(last), nil
} }
// sliceCast casts i to a slice if it is non-nil, otherwise returns nil
func sliceCast(i interface{}) []interface{} {
if i == nil {
return nil
}
return i.([]interface{})
}
// mapCast casts i to a map if it is non-nil, otherwise returns nil
func mapCast(i interface{}) map[string]interface{} {
if i == nil {
return nil
}
return i.(map[string]interface{})
}
// getFieldMeta parses the metadata about the field from the openapi spec // getFieldMeta parses the metadata about the field from the openapi spec
func getFieldMeta(s openapi.Schema, name string) (apply.FieldMetaImpl, error) { func getFieldMeta(s openapi.Schema, name string) (apply.FieldMetaImpl, error) {
m := apply.FieldMetaImpl{} m := apply.FieldMetaImpl{}

View File

@ -16,19 +16,12 @@ limitations under the License.
package apply package apply
import (
"fmt"
)
// PrimitiveElement contains the recorded, local and remote values for a field // PrimitiveElement contains the recorded, local and remote values for a field
// of type primitive // of type primitive
type PrimitiveElement struct { type PrimitiveElement struct {
// FieldMetaImpl contains metadata about the field from openapi // FieldMetaImpl contains metadata about the field from openapi
FieldMetaImpl FieldMetaImpl
// HasElementData contains whether the field was set
HasElementData
// RawElementData contains the values the field was set to // RawElementData contains the values the field was set to
RawElementData RawElementData
} }
@ -38,9 +31,4 @@ func (e PrimitiveElement) Merge(v Strategy) (Result, error) {
return v.MergePrimitive(e) return v.MergePrimitive(e)
} }
// String returns a string representation of the PrimitiveElement
func (e PrimitiveElement) String() string {
return fmt.Sprintf("name: %s recorded: %v local: %v remote: %v", e.Name, e.Recorded, e.Local, e.Remote)
}
var _ Element = &PrimitiveElement{} var _ Element = &PrimitiveElement{}

View File

@ -20,32 +20,14 @@ import "k8s.io/kubernetes/pkg/kubectl/apply"
// Options controls how a merge will be executed // Options controls how a merge will be executed
type Options struct { type Options struct {
// FailOnConflict when true will fail patch creation if the Recorded and Remote // FailOnConflict when true will fail patch creation if the recorded and remote
// have 2 fields set for the same value that cannot be merged. // have 2 fields set for the same value that cannot be merged.
// e.g. primitive values, list values with replace strategy, and map values with do // e.g. primitive values, list values with replace strategy, and map values with do
// strategy // strategy
FailOnConflict bool FailOnConflict bool
// ListOrder controls the order of items in a list after it is merged
ListOrder ListOrder
} }
// Create returns a new apply.Visitor for merging multiple objects together // Create returns a new apply.Visitor for merging multiple objects together
func Create(options Options) apply.Strategy { func Create(options Options) apply.Strategy {
return createDelegatingStrategy(options) return createDelegatingStrategy(options)
} }
// ListOrder allows the caller to controller how ordering is determined when merging
// lists
// TODO: Implement this
type ListOrder int
const (
// RemoteOnlyLast appends items only appearing in the remote list to the end
RemoteOnlyLast ListOrder = iota
// RemoteOnlyFirst prepends items only appearing in the remote list to the beginning
RemoteOnlyFirst
// RemoteCollated will try to preserve the order of elements as much as possible by
// interleaving items
RemoteCollated
)

View File

@ -46,19 +46,20 @@ func (v mergeStrategy) MergeList(e apply.ListElement) (apply.Result, error) {
merged := []interface{}{} merged := []interface{}{}
for _, value := range e.Values { for _, value := range e.Values {
// Recursively merge the list element before adding the value to the list // Recursively merge the list element before adding the value to the list
result, err := value.Merge(v.strategic) m, err := value.Merge(v.strategic)
if err != nil { if err != nil {
return apply.Result{}, err return apply.Result{}, err
} }
switch result.Operation { fmt.Printf("\nResult %+v\n%+v\n", m.MergedResult, value)
switch m.Operation {
case apply.SET: case apply.SET:
// Keep the list item value // Keep the list item value
merged = append(merged, result.MergedResult) merged = append(merged, m.MergedResult)
case apply.DROP: case apply.DROP:
// Drop the list item value // Drop the list item value
default: default:
panic(fmt.Errorf("Unexpected result operation type %+v", result)) panic(fmt.Errorf("Unexpected result operation type %+v", m))
} }
} }

View File

@ -22,10 +22,6 @@ type TypeElement struct {
// FieldMetaImpl contains metadata about the field from openapi // FieldMetaImpl contains metadata about the field from openapi
FieldMetaImpl FieldMetaImpl
// HasElementData contains whether the field was set
HasElementData
// ListElementData contains the value a field was set to
MapElementData MapElementData
// Values contains the combined recorded-local-remote value of each field in the type // Values contains the combined recorded-local-remote value of each field in the type