Exemplo n.º 1
0
func sortMergeListsByNameMap(s map[string]interface{}, t reflect.Type) (map[string]interface{}, error) {
	newS := map[string]interface{}{}
	for k, v := range s {
		if k != directiveMarker {
			fieldType, fieldPatchStrategy, fieldPatchMergeKey, err := forkedjson.LookupPatchMetadata(t, k)
			if err != nil {
				return nil, err
			}

			// If v is a map or a merge slice, recurse.
			if typedV, ok := v.(map[string]interface{}); ok {
				var err error
				v, err = sortMergeListsByNameMap(typedV, fieldType)
				if err != nil {
					return nil, err
				}
			} else if typedV, ok := v.([]interface{}); ok {
				if fieldPatchStrategy == mergeDirective {
					var err error
					v, err = sortMergeListsByNameArray(typedV, fieldType.Elem(), fieldPatchMergeKey, true)
					if err != nil {
						return nil, err
					}
				}
			}
		}

		newS[k] = v
	}

	return newS, nil
}
Exemplo n.º 2
0
func mapsHaveConflicts(typedLeft, typedRight map[string]interface{}, structType reflect.Type) (bool, error) {
	for key, leftValue := range typedLeft {
		if key != directiveMarker {
			if rightValue, ok := typedRight[key]; ok {
				fieldType, fieldPatchStrategy, fieldPatchMergeKey, err := forkedjson.LookupPatchMetadata(structType, key)
				if err != nil {
					return true, err
				}

				if hasConflicts, err := mergingMapFieldsHaveConflicts(leftValue, rightValue,
					fieldType, fieldPatchStrategy, fieldPatchMergeKey); hasConflicts {
					return true, err
				}
			}
		}
	}

	return false, nil
}
Exemplo n.º 3
0
// Function sortMergeListsByNameMap recursively sorts the merge lists by its mergeKey in a map.
func sortMergeListsByNameMap(s map[string]interface{}, t reflect.Type) (map[string]interface{}, error) {
	newS := map[string]interface{}{}
	for k, v := range s {
		if strings.HasPrefix(k, deleteFromPrimitiveListDirectivePrefix) {
			typedV, ok := v.([]interface{})
			if !ok {
				return nil, errBadPatchFormatForPrimitiveList
			}
			v = uniqifyAndSortScalars(typedV)
		} else if k != directiveMarker {
			fieldType, fieldPatchStrategy, fieldPatchMergeKey, err := forkedjson.LookupPatchMetadata(t, k)
			if err != nil {
				return nil, err
			}

			// If v is a map or a merge slice, recurse.
			if typedV, ok := v.(map[string]interface{}); ok {
				var err error
				v, err = sortMergeListsByNameMap(typedV, fieldType)
				if err != nil {
					return nil, err
				}
			} else if typedV, ok := v.([]interface{}); ok {
				if fieldPatchStrategy == mergeDirective {
					var err error
					v, err = sortMergeListsByNameArray(typedV, fieldType.Elem(), fieldPatchMergeKey, true)
					if err != nil {
						return nil, err
					}
				}
			}
		}

		newS[k] = v
	}

	return newS, nil
}
Exemplo n.º 4
0
func mapsHaveConflicts(typedLeft, typedRight map[string]interface{}, structType reflect.Type) (bool, error) {
	isForListOfPrimitives := false
	if leftDirective, ok := typedLeft[directiveMarker]; ok {
		if rightDirective, ok := typedRight[directiveMarker]; ok {
			if leftDirective == MergePrimitivesListDirective && rightDirective == rightDirective {
				isForListOfPrimitives = true
			}
		}
	}
	for key, leftValue := range typedLeft {
		if key != directiveMarker {
			if rightValue, ok := typedRight[key]; ok {
				var fieldType reflect.Type
				var fieldPatchStrategy, fieldPatchMergeKey string
				var err error
				if isForListOfPrimitives {
					fieldType = reflect.TypeOf(leftValue)
					fieldPatchStrategy = ""
					fieldPatchMergeKey = ""
				} else {
					fieldType, fieldPatchStrategy, fieldPatchMergeKey, err = forkedjson.LookupPatchMetadata(structType, key)
					if err != nil {
						return true, err
					}
				}

				if hasConflicts, err := mergingMapFieldsHaveConflicts(leftValue, rightValue,
					fieldType, fieldPatchStrategy, fieldPatchMergeKey); hasConflicts {
					return true, err
				}
			}
		}
	}

	return false, nil
}
Exemplo n.º 5
0
// Merge fields from a patch map into the original map. Note: This may modify
// both the original map and the patch because getting a deep copy of a map in
// golang is highly non-trivial.
func mergeMap(original, patch map[string]interface{}, t reflect.Type) (map[string]interface{}, error) {
	if v, ok := patch[directiveMarker]; ok {
		if v == replaceDirective {
			// If the patch contains "$patch: replace", don't merge it, just use the
			// patch directly. Later on, we can add a single level replace that only
			// affects the map that the $patch is in.
			delete(patch, directiveMarker)
			return patch, nil
		}

		if v == deleteDirective {
			// If the patch contains "$patch: delete", don't merge it, just return
			//  an empty map.
			return map[string]interface{}{}, nil
		}

		if v == MergePrimitivesListDirective {
			// delete the directiveMarker's key-value pair to avoid delta map and delete map
			// overlaping with each other when calculating a ThreeWayDiff for list of Primitives.
			// Otherwise, the overlaping will cause it calling LookupPatchMetadata() which will
			// return an error since the metadata shows it's a slice but it is actually a map.
			delete(original, directiveMarker)
		} else {
			return nil, fmt.Errorf(errBadPatchTypeFmt, v, patch)
		}
	}

	// nil is an accepted value for original to simplify logic in other places.
	// If original is nil, replace it with an empty map and then apply the patch.
	if original == nil {
		original = map[string]interface{}{}
	}

	// Start merging the patch into the original.
	for k, patchV := range patch {
		// If the value of this key is null, delete the key if it exists in the
		// original. Otherwise, skip it.
		if patchV == nil {
			if _, ok := original[k]; ok {
				delete(original, k)
			}

			continue
		}

		_, ok := original[k]
		if !ok {
			// If it's not in the original document, just take the patch value.
			original[k] = patchV
			continue
		}

		// If the data type is a pointer, resolve the element.
		if t.Kind() == reflect.Ptr {
			t = t.Elem()
		}

		// If they're both maps or lists, recurse into the value.
		originalType := reflect.TypeOf(original[k])
		patchType := reflect.TypeOf(patchV)
		// check if we are trying to merge a slice with a map for list of primitives
		isMergeSliceOfPrimitivesWithAPatchMap := originalType != nil && patchType != nil && originalType.Kind() == reflect.Slice && patchType.Kind() == reflect.Map
		if originalType == patchType || isMergeSliceOfPrimitivesWithAPatchMap {
			// First find the fieldPatchStrategy and fieldPatchMergeKey.
			fieldType, fieldPatchStrategy, fieldPatchMergeKey, err := forkedjson.LookupPatchMetadata(t, k)
			if err != nil {
				return nil, err
			}

			if originalType.Kind() == reflect.Map && fieldPatchStrategy != replaceDirective {
				typedOriginal := original[k].(map[string]interface{})
				typedPatch := patchV.(map[string]interface{})
				var err error
				original[k], err = mergeMap(typedOriginal, typedPatch, fieldType)
				if err != nil {
					return nil, err
				}

				continue
			}

			if originalType.Kind() == reflect.Slice && fieldPatchStrategy == mergeDirective {
				elemType := fieldType.Elem()
				typedOriginal := original[k].([]interface{})
				var err error
				original[k], err = mergeSlice(typedOriginal, patchV, elemType, fieldPatchMergeKey)
				if err != nil {
					return nil, err
				}

				continue
			}
		}

		// If originalType and patchType are different OR the types are both
		// maps or slices but we're just supposed to replace them, just take
		// the value from patch.
		original[k] = patchV
	}

	return original, nil
}
Exemplo n.º 6
0
// Returns a (recursive) strategic merge patch that yields modified when applied to original.
func diffMaps(original, modified map[string]interface{}, t reflect.Type, ignoreChangesAndAdditions, ignoreDeletions bool, smPatchVersion StrategicMergePatchVersion) (map[string]interface{}, error) {
	patch := map[string]interface{}{}
	if t.Kind() == reflect.Ptr {
		t = t.Elem()
	}

	for key, modifiedValue := range modified {
		originalValue, ok := original[key]
		if !ok {
			// Key was added, so add to patch
			if !ignoreChangesAndAdditions {
				patch[key] = modifiedValue
			}

			continue
		}

		if key == directiveMarker {
			originalString, ok := originalValue.(string)
			if !ok {
				return nil, fmt.Errorf("invalid value for special key: %s", directiveMarker)
			}

			modifiedString, ok := modifiedValue.(string)
			if !ok {
				return nil, fmt.Errorf("invalid value for special key: %s", directiveMarker)
			}

			if modifiedString != originalString {
				patch[directiveMarker] = modifiedValue
			}

			continue
		}

		if reflect.TypeOf(originalValue) != reflect.TypeOf(modifiedValue) {
			// Types have changed, so add to patch
			if !ignoreChangesAndAdditions {
				patch[key] = modifiedValue
			}

			continue
		}

		// Types are the same, so compare values
		switch originalValueTyped := originalValue.(type) {
		case map[string]interface{}:
			modifiedValueTyped := modifiedValue.(map[string]interface{})
			fieldType, _, _, err := forkedjson.LookupPatchMetadata(t, key)
			if err != nil {
				return nil, err
			}

			patchValue, err := diffMaps(originalValueTyped, modifiedValueTyped, fieldType, ignoreChangesAndAdditions, ignoreDeletions, smPatchVersion)
			if err != nil {
				return nil, err
			}

			if len(patchValue) > 0 {
				patch[key] = patchValue
			}

			continue
		case []interface{}:
			modifiedValueTyped := modifiedValue.([]interface{})
			fieldType, fieldPatchStrategy, fieldPatchMergeKey, err := forkedjson.LookupPatchMetadata(t, key)
			if err != nil {
				return nil, err
			}

			if fieldPatchStrategy == mergeDirective {
				patchValue, err := diffLists(originalValueTyped, modifiedValueTyped, fieldType.Elem(), fieldPatchMergeKey, ignoreChangesAndAdditions, ignoreDeletions, smPatchVersion)
				if err != nil {
					return nil, err
				}
				if patchValue == nil {
					continue
				}

				switch typedPatchValue := patchValue.(type) {
				case []interface{}:
					if len(typedPatchValue) > 0 {
						patch[key] = typedPatchValue
					}
				case map[string]interface{}:
					if len(typedPatchValue) > 0 {
						patch[key] = typedPatchValue
					}
				default:
					return nil, fmt.Errorf("invalid type of patch: %v", reflect.TypeOf(patchValue))
				}

				continue
			}
		}

		if !ignoreChangesAndAdditions {
			if !reflect.DeepEqual(originalValue, modifiedValue) {
				// Values are different, so add to patch
				patch[key] = modifiedValue
			}
		}
	}

	if !ignoreDeletions {
		// Add nils for deleted values
		for key := range original {
			_, found := modified[key]
			if !found {
				patch[key] = nil
			}
		}
	}

	return patch, nil
}
Exemplo n.º 7
0
// Merge fields from a patch map into the original map. Note: This may modify
// both the original map and the patch because getting a deep copy of a map in
// golang is highly non-trivial.
func mergeMap(original, patch map[string]interface{}, t reflect.Type) (map[string]interface{}, error) {
	if v, ok := patch[directiveMarker]; ok {
		if v == replaceDirective {
			// If the patch contains "$patch: replace", don't merge it, just use the
			// patch directly. Later on, we can add a single level replace that only
			// affects the map that the $patch is in.
			delete(patch, directiveMarker)
			return patch, nil
		}

		if v == deleteDirective {
			// If the patch contains "$patch: delete", don't merge it, just return
			//  an empty map.
			return map[string]interface{}{}, nil
		}

		return nil, fmt.Errorf(errBadPatchTypeFmt, v, patch)
	}

	// nil is an accepted value for original to simplify logic in other places.
	// If original is nil, replace it with an empty map and then apply the patch.
	if original == nil {
		original = map[string]interface{}{}
	}

	// Start merging the patch into the original.
	for k, patchV := range patch {
		// If the value of this key is null, delete the key if it exists in the
		// original. Otherwise, skip it.
		if patchV == nil {
			if _, ok := original[k]; ok {
				delete(original, k)
			}

			continue
		}

		_, ok := original[k]
		if !ok {
			// If it's not in the original document, just take the patch value.
			original[k] = patchV
			continue
		}

		// If the data type is a pointer, resolve the element.
		if t.Kind() == reflect.Ptr {
			t = t.Elem()
		}

		// If they're both maps or lists, recurse into the value.
		originalType := reflect.TypeOf(original[k])
		patchType := reflect.TypeOf(patchV)
		if originalType == patchType {
			// First find the fieldPatchStrategy and fieldPatchMergeKey.
			fieldType, fieldPatchStrategy, fieldPatchMergeKey, err := forkedjson.LookupPatchMetadata(t, k)
			if err != nil {
				return nil, err
			}

			if originalType.Kind() == reflect.Map && fieldPatchStrategy != replaceDirective {
				typedOriginal := original[k].(map[string]interface{})
				typedPatch := patchV.(map[string]interface{})
				var err error
				original[k], err = mergeMap(typedOriginal, typedPatch, fieldType)
				if err != nil {
					return nil, err
				}

				continue
			}

			if originalType.Kind() == reflect.Slice && fieldPatchStrategy == mergeDirective {
				elemType := fieldType.Elem()
				typedOriginal := original[k].([]interface{})
				typedPatch := patchV.([]interface{})
				var err error
				original[k], err = mergeSlice(typedOriginal, typedPatch, elemType, fieldPatchMergeKey)
				if err != nil {
					return nil, err
				}

				continue
			}
		}

		// If originalType and patchType are different OR the types are both
		// maps or slices but we're just supposed to replace them, just take
		// the value from patch.
		original[k] = patchV
	}

	return original, nil
}
Exemplo n.º 8
0
// Returns a (recursive) strategic merge patch that yields modified when applied to original.
func diffMaps(original, modified map[string]interface{}, t reflect.Type, ignoreChangesAndAdditions, ignoreDeletions bool) (map[string]interface{}, error) {
	patch := map[string]interface{}{}
	if t.Kind() == reflect.Ptr {
		t = t.Elem()
	}

	for key, modifiedValue := range modified {
		originalValue, ok := original[key]
		if !ok {
			// Key was added, so add to patch
			if !ignoreChangesAndAdditions {
				patch[key] = modifiedValue
			}

			continue
		}

		// The patch has a patch directive
		if key == directiveMarker {
			originalString, ok := originalValue.(string)
			if !ok {
				return nil, fmt.Errorf("invalid value for special key: %s", directiveMarker)
			}

			modifiedString, ok := modifiedValue.(string)
			if !ok {
				return nil, fmt.Errorf("invalid value for special key: %s", directiveMarker)
			}

			if modifiedString != originalString {
				patch[directiveMarker] = modifiedValue
			}

			continue
		}

		if reflect.TypeOf(originalValue) != reflect.TypeOf(modifiedValue) {
			// Types have changed, so add to patch
			if !ignoreChangesAndAdditions {
				patch[key] = modifiedValue
			}

			continue
		}

		// Types are the same, so compare values
		switch originalValueTyped := originalValue.(type) {
		case map[string]interface{}:
			modifiedValueTyped := modifiedValue.(map[string]interface{})
			fieldType, _, _, err := forkedjson.LookupPatchMetadata(t, key)
			if err != nil {
				return nil, err
			}

			patchValue, err := diffMaps(originalValueTyped, modifiedValueTyped, fieldType, ignoreChangesAndAdditions, ignoreDeletions)
			if err != nil {
				return nil, err
			}

			if len(patchValue) > 0 {
				patch[key] = patchValue
			}

			continue
		case []interface{}:
			modifiedValueTyped := modifiedValue.([]interface{})
			fieldType, fieldPatchStrategy, fieldPatchMergeKey, err := forkedjson.LookupPatchMetadata(t, key)
			if err != nil {
				return nil, err
			}

			if fieldPatchStrategy == mergeDirective {
				addList, deletionList, err := diffLists(originalValueTyped, modifiedValueTyped, fieldType.Elem(), fieldPatchMergeKey, ignoreChangesAndAdditions, ignoreDeletions)
				if err != nil {
					return nil, err
				}

				if len(addList) > 0 {
					patch[key] = addList
				}

				// generate a parallel list for deletion
				if len(deletionList) > 0 {
					parallelDeletionListKey := fmt.Sprintf("%s/%s", deleteFromPrimitiveListDirectivePrefix, key)
					patch[parallelDeletionListKey] = deletionList
				}

				continue
			}
		}

		if !ignoreChangesAndAdditions {
			if !reflect.DeepEqual(originalValue, modifiedValue) {
				// Values are different, so add to patch
				patch[key] = modifiedValue
			}
		}
	}

	if !ignoreDeletions {
		// Add nils for deleted values
		for key := range original {
			_, found := modified[key]
			if !found {
				patch[key] = nil
			}
		}
	}

	return patch, nil
}