
Now it is able to add, update, delete keys in resource definition content. Change-Id: I9624f97bf35eae15d6a7ddc2d5d42768292db4f0
119 lines
4.4 KiB
Python
119 lines
4.4 KiB
Python
# 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.
|
|
|
|
import copy
|
|
|
|
from tuning_box import errors
|
|
|
|
|
|
class KeysOperationMixin(object):
|
|
|
|
OPERATION_SET = 'set'
|
|
OPERATION_DELETE = 'delete'
|
|
|
|
OPERATIONS = (OPERATION_SET, OPERATION_DELETE)
|
|
|
|
def _check_out_of_index(self, cur_point, key, keys_path):
|
|
if isinstance(cur_point, (list, tuple)) and key >= len(cur_point):
|
|
raise errors.KeysPathNotExisted(
|
|
"Keys path doesn't exist {0}. "
|
|
"Failed on the key {1}".format(keys_path, key)
|
|
)
|
|
|
|
def _check_key_existed(self, cur_point, key, keys_path):
|
|
if isinstance(cur_point, dict) and key not in cur_point:
|
|
raise errors.KeysPathNotExisted(
|
|
"Keys path doesn't exist {0}. "
|
|
"Failed on the key {1}".format(keys_path, key)
|
|
)
|
|
|
|
def _check_path_is_reachable(self, cur_point, key, keys_path):
|
|
if not isinstance(cur_point, (list, tuple, dict)):
|
|
raise errors.KeysPathUnreachable(
|
|
"Leaf value {0} found on key {1} "
|
|
"in keys path {2}".format(cur_point, key, keys_path)
|
|
)
|
|
|
|
def do_set(self, storage, keys_paths):
|
|
"""Sets values from keys paths to storage.
|
|
|
|
Keys path is list of keys paths. If we have keys_paths
|
|
[['a', 'b', 'val']], then storage['a']['b'] will be set to 'val'.
|
|
Last value in the keys path is value to be set.
|
|
|
|
:param storage: original data
|
|
:param keys_paths: lists of keys paths to be set
|
|
:returns: result of merging keys_paths and storage
|
|
"""
|
|
|
|
storage_copy = copy.deepcopy(storage)
|
|
for keys_path in keys_paths:
|
|
cur_point = storage_copy
|
|
if len(keys_path) < 2:
|
|
raise errors.KeysPathInvalid(
|
|
"Keys path {0} invalid. Keys path should contain "
|
|
"at least one key and value".format(keys_path)
|
|
)
|
|
|
|
for key in keys_path[:-2]:
|
|
self._check_path_is_reachable(cur_point, key, keys_path)
|
|
self._check_out_of_index(cur_point, key, keys_path)
|
|
self._check_key_existed(cur_point, key, keys_path)
|
|
cur_point = cur_point[key]
|
|
|
|
assign_to = keys_path[-2]
|
|
self._check_out_of_index(cur_point, assign_to, keys_path)
|
|
cur_point[assign_to] = keys_path[-1]
|
|
|
|
return storage_copy
|
|
|
|
def do_delete(self, storage, keys_paths):
|
|
"""Deletes keys paths from storage.
|
|
|
|
Keys path is list of keys paths. If we have keys_paths
|
|
[['a', 'b']], then storage['a']['b'] will be removed.
|
|
|
|
:param storage: data
|
|
:param keys_paths: lists of keys paths to be deleted
|
|
:returns: result of keys_paths deletion from storage
|
|
"""
|
|
|
|
storage_copy = copy.deepcopy(storage)
|
|
for keys_path in keys_paths:
|
|
cur_point = storage_copy
|
|
if not keys_path:
|
|
continue
|
|
|
|
try:
|
|
for key in keys_path[:-1]:
|
|
cur_point = cur_point[key]
|
|
key = keys_path[-1]
|
|
self._check_path_is_reachable(cur_point, key, keys_path)
|
|
del cur_point[key]
|
|
except (KeyError, IndexError):
|
|
raise errors.KeysPathNotExisted(
|
|
"Keys path doesn't exist {0}. "
|
|
"Failed on the key {1}".format(keys_path, key)
|
|
)
|
|
return storage_copy
|
|
|
|
def perform_operation(self, operation, storage, keys_paths):
|
|
if operation == self.OPERATION_SET:
|
|
return self.do_set(storage, keys_paths)
|
|
elif operation == self.OPERATION_DELETE:
|
|
return self.do_delete(storage, keys_paths)
|
|
else:
|
|
raise errors.UnknownKeysOperation(
|
|
"Unknown operation: {0}. "
|
|
"Allowed operations: {1}".format(operation, self.OPERATIONS)
|
|
)
|