Skip to content

Commit

Permalink
resolver: Implement replace operation
Browse files Browse the repository at this point in the history
As first step to implement the Replace operation at the resolver this
change implement path value resplacement but just for current state
since it does not involve changing path.go file to traverse capture
entry path ref and apply a function.

The replace operation has to be able to use a capture ref path as input,
also some tuning is needed at the apply functions since the modified
value has to be returned and that's different from filter function.

Signed-off-by: Quique Llorente <[email protected]>
  • Loading branch information
qinqon committed Dec 2, 2021
1 parent 060fe1f commit 0649f70
Show file tree
Hide file tree
Showing 6 changed files with 263 additions and 31 deletions.
8 changes: 8 additions & 0 deletions nmpolicy/internal/resolver/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,11 @@ func wrapWithResolveError(err error) error {
func wrapWithEqFilterError(err error) error {
return fmt.Errorf("eqfilter error: %v", err)
}

func replaceError(format string, a ...interface{}) error {
return wrapWithReplaceError(fmt.Errorf(format, a...))
}

func wrapWithReplaceError(err error) error {
return fmt.Errorf("replace error: %v", err)
}
2 changes: 1 addition & 1 deletion nmpolicy/internal/resolver/filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import (
)

func filter(inputState map[string]interface{}, path ast.VariadicOperator, expectedNode ast.Node) (map[string]interface{}, error) {
filtered, err := applyFuncOnPath(inputState, path, expectedNode, mapContainsValue, true)
filtered, err := applyFuncOnMap(path, inputState, expectedNode, mapContainsValue, true, true)

if err != nil {
return nil, fmt.Errorf("failed applying operation on the path: %v", err)
Expand Down
41 changes: 25 additions & 16 deletions nmpolicy/internal/resolver/path.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func applyFuncOnPath(inputState interface{},
path []ast.Node,
expectedNode ast.Node,
funcToApply func(map[string]interface{}, string, ast.Node) (interface{}, error),
shouldFilterSlice bool) (interface{}, error) {
shouldFilterSlice bool, shouldFilterMap bool) (interface{}, error) {
if len(path) == 0 {
return inputState, nil
}
Expand All @@ -41,7 +41,7 @@ func applyFuncOnPath(inputState interface{},
if len(path) == 1 {
return applyFuncOnLastMapOnPath(path, originalMap, expectedNode, inputState, funcToApply)
}
return applyFuncOnMap(path, originalMap, expectedNode, funcToApply, shouldFilterSlice)
return applyFuncOnMap(path, originalMap, expectedNode, funcToApply, shouldFilterSlice, shouldFilterMap)
}

originalSlice, isSlice := inputState.([]interface{})
Expand All @@ -57,32 +57,33 @@ func applyFuncOnSlice(originalSlice []interface{},
expectedNode ast.Node,
funcToApply func(map[string]interface{}, string, ast.Node) (interface{}, error),
shouldFilterSlice bool) (interface{}, error) {
filteredSlice := []interface{}{}
adjustedSlice := []interface{}{}
sliceEmptyAfterApply := true
for _, valueToCheck := range originalSlice {
value, err := applyFuncOnPath(valueToCheck, path, expectedNode, funcToApply, false)
valueAfterApply, err := applyFuncOnPath(valueToCheck, path, expectedNode, funcToApply, false, false)
if err != nil {
return nil, err
}
if value != nil {
filteredSlice = append(filteredSlice, valueToCheck)
if valueAfterApply != nil {
sliceEmptyAfterApply = false
adjustedSlice = append(adjustedSlice, valueAfterApply)
} else if !shouldFilterSlice {
adjustedSlice = append(adjustedSlice, valueToCheck)
}
}

if len(filteredSlice) == 0 {
if sliceEmptyAfterApply {
return nil, nil
}

if shouldFilterSlice {
return filteredSlice, nil
}
return originalSlice, nil
return adjustedSlice, nil
}

func applyFuncOnMap(path []ast.Node,
originalMap map[string]interface{},
expectedNode ast.Node,
funcToApply func(map[string]interface{}, string, ast.Node) (interface{}, error),
shouldFilterSlice bool) (interface{}, error) {
shouldFilterSlice bool, shouldFilterMap bool) (interface{}, error) {
currentStep := path[0]
if currentStep.Identity == nil {
return nil, pathError("%v has unsupported fromat", currentStep)
Expand All @@ -96,14 +97,22 @@ func applyFuncOnMap(path []ast.Node,
return nil, pathError("cannot find key %s in %v", key, originalMap)
}

adjuctedValue, err := applyFuncOnPath(value, nextPath, expectedNode, funcToApply, shouldFilterSlice)
adjustedValue, err := applyFuncOnPath(value, nextPath, expectedNode, funcToApply, shouldFilterSlice, shouldFilterMap)
if err != nil {
return nil, err
}
if adjuctedValue != nil {
return map[string]interface{}{key: adjuctedValue}, nil
if adjustedValue == nil {
return nil, nil
}

adjustedMap := map[string]interface{}{}
if !shouldFilterMap {
for k, v := range originalMap {
adjustedMap[k] = v
}
}
return nil, nil
adjustedMap[key] = adjustedValue
return adjustedMap, nil
}

func applyFuncOnLastMapOnPath(path []ast.Node,
Expand Down
48 changes: 48 additions & 0 deletions nmpolicy/internal/resolver/replace.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Copyright 2021 NMPolicy Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package resolver

import (
"github.com/nmstate/nmpolicy/nmpolicy/internal/ast"
)

func replace(inputState map[string]interface{}, path ast.VariadicOperator, expectedNode ast.Node) (map[string]interface{}, error) {
replaced, err := applyFuncOnMap(path, inputState, expectedNode, replaceMapFieldValue, false, false)

if err != nil {
return nil, replaceError("failed applying operation on the path: %v", err)
}

replacedMap, ok := replaced.(map[string]interface{})
if !ok {
return nil, replaceError("failed converting result to a map")
}
return replacedMap, nil
}

func replaceMapFieldValue(inputMap map[string]interface{}, mapEntryKeyToReplace string, expectedNode ast.Node) (interface{}, error) {
if expectedNode.String == nil {
return false, replaceError("the desired value %+v is not supported. Curretly only string values are supported", expectedNode)
}
modifiedMap := map[string]interface{}{}
for k, v := range inputMap {
modifiedMap[k] = v
}

modifiedMap[mapEntryKeyToReplace] = *expectedNode.String
return modifiedMap, nil
}
59 changes: 45 additions & 14 deletions nmpolicy/internal/resolver/resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,41 +93,72 @@ func (r *resolver) resolveCaptureEntryName(captureEntryName string) (types.NMSta
func (r resolver) resolveCaptureASTEntry(captureASTEntry ast.Node) (types.NMState, error) {
if captureASTEntry.EqFilter != nil {
return r.resolveEqFilter(captureASTEntry.EqFilter)
} else if captureASTEntry.Replace != nil {
return r.resolveReplace(captureASTEntry.Replace)
}
return nil, fmt.Errorf("root node has unsupported operation : %v", captureASTEntry)
return nil, fmt.Errorf("root node has unsupported operation : %+v", captureASTEntry)
}

func (r resolver) resolveEqFilter(operator *ast.TernaryOperator) (types.NMState, error) {
inputSource, err := r.resolveInputSource((*operator)[0], r.currentState)
filteredState, err := r.resolveTernaryOperator(operator, filter)
if err != nil {
return nil, wrapWithEqFilterError(err)
}
return filteredState, nil
}

func (r resolver) resolveReplace(operator *ast.TernaryOperator) (types.NMState, error) {
replacedState, err := r.resolveTernaryOperator(operator, replace)
if err != nil {
return nil, wrapWithResolveError(err)
}
return replacedState, nil
}

func (r resolver) resolveTernaryOperator(operator *ast.TernaryOperator,
resolverFunc func(map[string]interface{}, ast.VariadicOperator, ast.Node) (map[string]interface{}, error)) (types.NMState, error) {
inputSource, err := r.resolveInputSource((*operator)[0])
if err != nil {
return nil, err
}

path, err := r.resolvePath((*operator)[1])
if err != nil {
return nil, wrapWithEqFilterError(err)
return nil, err
}
filteredValue, err := r.resolveFilteredValue((*operator)[2])
value, err := r.resolveStringOrCaptureEntryPath((*operator)[2])
if err != nil {
return nil, wrapWithEqFilterError(err)
return nil, err
}
filteredState, err := filter(inputSource, path.steps, *filteredValue)
resolvedState, err := resolverFunc(inputSource, path.steps, *value)
if err != nil {
return nil, wrapWithEqFilterError(err)
return nil, err
}
return filteredState, nil
return resolvedState, nil
}

func (r resolver) resolveInputSource(inputSourceNode ast.Node,
currentState types.NMState) (types.NMState, error) {
func (r resolver) resolveInputSource(inputSourceNode ast.Node) (types.NMState, error) {
if ast.CurrentStateIdentity().DeepEqual(inputSourceNode.Terminal) {
return currentState, nil
return r.currentState, nil
} else if inputSourceNode.Path != nil {
resolvedPath, err := r.resolvePath(inputSourceNode)
if err != nil {
return nil, err
}
if resolvedPath.captureEntryName == "" {
return nil, fmt.Errorf("invalid path input source, only capture reference is supported")
}
capturedState, err := r.resolveCaptureEntryName(resolvedPath.captureEntryName)
if err != nil {
return nil, err
}
return capturedState, nil
}

return nil, fmt.Errorf("not supported input source %v. Only the current state is supported", inputSourceNode)
return nil, fmt.Errorf("invalid input source %v, only current state or capture reference is supported", inputSourceNode)
}

func (r resolver) resolveFilteredValue(filteredValueNode ast.Node) (*ast.Node, error) {
func (r resolver) resolveStringOrCaptureEntryPath(filteredValueNode ast.Node) (*ast.Node, error) {
if filteredValueNode.String != nil {
return &filteredValueNode, nil
} else if filteredValueNode.Path != nil {
Expand All @@ -143,7 +174,7 @@ func (r resolver) resolveFilteredValue(filteredValueNode ast.Node) (*ast.Node, e
Terminal: *terminal,
}, nil
} else {
return nil, fmt.Errorf("not supported filtered value. Only string or paths are supported")
return nil, fmt.Errorf("not supported value. Only string or capture entry path are supported")
}
}

Expand Down
Loading

0 comments on commit 0649f70

Please sign in to comment.