deep merge
This commit is contained in:
+83
-4
@@ -128,10 +128,8 @@ func ModelGenericUpdateOne(change map[string]interface{}, id string, a Accessor)
|
||||
r.Sign()
|
||||
}
|
||||
|
||||
loaded := r.Serialize(r) // get the loaded object
|
||||
for k, v := range change { // apply the changes, with a flatten method
|
||||
loaded[k] = v
|
||||
}
|
||||
loaded := r.Serialize(r) // get the loaded object
|
||||
deepMerge(loaded, change)
|
||||
newObj := a.NewObj()
|
||||
b, err = json.Marshal(loaded)
|
||||
if err != nil {
|
||||
@@ -255,6 +253,87 @@ func IsMySelf(peerID string, wfa Accessor) (bool, string) {
|
||||
return peerID == pp.GetID(), pp.GetID()
|
||||
}
|
||||
|
||||
// deepMerge overlays patch values onto base, preserving base values for keys
|
||||
// absent from patch, nil patch values, and empty strings when base is non-empty.
|
||||
// This prevents partial frontend payloads from silently erasing server-managed
|
||||
// fields (source, env, country, owners, creator_id, creation_date, …).
|
||||
func deepMerge(base, patch map[string]interface{}) {
|
||||
for k, pv := range patch {
|
||||
bv := base[k]
|
||||
switch pvTyped := pv.(type) {
|
||||
case map[string]interface{}:
|
||||
if bvMap, ok := bv.(map[string]interface{}); ok {
|
||||
deepMerge(bvMap, pvTyped)
|
||||
} else {
|
||||
base[k] = pv
|
||||
}
|
||||
case []interface{}:
|
||||
if bvSlice, ok := bv.([]interface{}); ok {
|
||||
base[k] = mergeSlices(bvSlice, pvTyped)
|
||||
} else {
|
||||
base[k] = pv
|
||||
}
|
||||
case string:
|
||||
// Don't overwrite a non-empty base value with an empty string.
|
||||
if pvTyped != "" {
|
||||
base[k] = pv
|
||||
}
|
||||
default:
|
||||
if pv != nil {
|
||||
base[k] = pv
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// mergeSlices merges two slices element-wise.
|
||||
// For slices of maps it matches elements by their "id" field when available;
|
||||
// falls back to positional matching. An empty patch slice leaves base intact.
|
||||
func mergeSlices(base, patch []interface{}) []interface{} {
|
||||
if len(patch) == 0 {
|
||||
return base
|
||||
}
|
||||
for _, e := range patch {
|
||||
if _, ok := e.(map[string]interface{}); !ok {
|
||||
return patch // non-map elements: replace wholesale
|
||||
}
|
||||
}
|
||||
baseByID := map[string]map[string]interface{}{}
|
||||
for _, e := range base {
|
||||
if em, ok := e.(map[string]interface{}); ok {
|
||||
if id, ok := em["id"].(string); ok && id != "" {
|
||||
baseByID[id] = em
|
||||
}
|
||||
}
|
||||
}
|
||||
result := make([]interface{}, 0, len(patch))
|
||||
for i, pe := range patch {
|
||||
pm, _ := pe.(map[string]interface{})
|
||||
if pm == nil {
|
||||
result = append(result, pe)
|
||||
continue
|
||||
}
|
||||
var baseElem map[string]interface{}
|
||||
if id, ok := pm["id"].(string); ok && id != "" {
|
||||
baseElem = baseByID[id]
|
||||
}
|
||||
if baseElem == nil && i < len(base) {
|
||||
baseElem, _ = base[i].(map[string]interface{})
|
||||
}
|
||||
if baseElem != nil {
|
||||
merged := make(map[string]interface{}, len(baseElem))
|
||||
for k, v := range baseElem {
|
||||
merged[k] = v
|
||||
}
|
||||
deepMerge(merged, pm)
|
||||
result = append(result, merged)
|
||||
} else {
|
||||
result = append(result, pe)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func GenerateNodeID() (string, error) {
|
||||
folderStatic := "/var/lib/opencloud-node"
|
||||
if _, err := os.Stat(folderStatic); err == nil {
|
||||
|
||||
Reference in New Issue
Block a user