Update of resource values/overrides implemented
Now it is able to add, update, delete keys in the resource values and overrides. Change-Id: I26a68f40d1c3f36dfbedfd93efaf4f79e86acd85
This commit is contained in:
parent
b2a878e536
commit
b7369eaa26
@ -59,12 +59,25 @@ api.add_resource(
|
||||
'/environments/<int:environment_id>/<levels:levels>resources/'
|
||||
'<id_or_name:resource_id_or_name>/values'
|
||||
)
|
||||
api.add_resource(
|
||||
resource_values.ResourceValuesKeys,
|
||||
'/environments/<int:environment_id>/<levels:levels>resources/'
|
||||
'<id_or_name:resource_id_or_name>/values/'
|
||||
'keys/<keys_operation:operation>'
|
||||
)
|
||||
|
||||
# Resource overrides
|
||||
api.add_resource(
|
||||
resource_overrides.ResourceOverrides,
|
||||
'/environments/<int:environment_id>/'
|
||||
'<levels:levels>resources/<id_or_name:resource_id_or_name>/overrides')
|
||||
'<levels:levels>resources/<id_or_name:resource_id_or_name>/overrides'
|
||||
)
|
||||
api.add_resource(
|
||||
resource_overrides.ResourceOverridesKeys,
|
||||
'/environments/<int:environment_id>/'
|
||||
'<levels:levels>resources/<id_or_name:resource_id_or_name>/overrides/'
|
||||
'keys/<keys_operation:operation>'
|
||||
)
|
||||
|
||||
# Environments
|
||||
api.add_resource(environments.EnvironmentsCollection, '/environments')
|
||||
|
@ -14,6 +14,7 @@ from sqlalchemy.orm import exc as sa_exc
|
||||
|
||||
from tuning_box import db
|
||||
from tuning_box import errors
|
||||
from tuning_box.library import levels_hierarchy
|
||||
|
||||
|
||||
def load_objects(model, ids):
|
||||
@ -71,3 +72,31 @@ def get_resource_definition(id_or_name, environment_id):
|
||||
raise sa_exc.MultipleResultsFound
|
||||
|
||||
return result[0]
|
||||
|
||||
|
||||
def get_resource_values(environment, levels, res_def):
|
||||
level_value = levels_hierarchy.get_environment_level_value(
|
||||
environment, levels)
|
||||
res_values = db.ResourceValues.query.filter_by(
|
||||
environment_id=environment.id,
|
||||
resource_definition_id=res_def.id,
|
||||
level_value_id=level_value.id,
|
||||
).all()
|
||||
|
||||
if not res_values:
|
||||
raise errors.TuningboxNotFound(
|
||||
"Resource values not found by environment {0}, "
|
||||
"resource definition {1}, level {2} with value {3}".format(
|
||||
environment.id, res_def.id, level_value.level.name,
|
||||
level_value.value
|
||||
)
|
||||
)
|
||||
elif len(res_values) > 1:
|
||||
raise errors.TuningboxIntegrityError(
|
||||
"Found more than one resource values for environment {0}, "
|
||||
"resource definition {1}, level {2} with value {3}".format(
|
||||
environment.id, res_def.id, level_value.level.name,
|
||||
level_value.value
|
||||
)
|
||||
)
|
||||
return res_values[0]
|
||||
|
@ -12,7 +12,11 @@
|
||||
|
||||
import copy
|
||||
|
||||
import flask
|
||||
|
||||
from tuning_box import db
|
||||
from tuning_box import errors
|
||||
from tuning_box import library
|
||||
|
||||
|
||||
class KeysOperationMixin(object):
|
||||
@ -116,3 +120,28 @@ class KeysOperationMixin(object):
|
||||
"Unknown operation: {0}. "
|
||||
"Allowed operations: {1}".format(operation, self.OPERATIONS)
|
||||
)
|
||||
|
||||
|
||||
class ResourceKeysMixin(KeysOperationMixin):
|
||||
|
||||
@db.with_transaction
|
||||
def _do_update(self, environment_id, levels,
|
||||
resource_id_or_name, operation, storage_name):
|
||||
|
||||
environment = db.Environment.query.get_or_404(environment_id)
|
||||
res_def = library.get_resource_definition(
|
||||
resource_id_or_name, environment_id)
|
||||
|
||||
if res_def.id != resource_id_or_name:
|
||||
from tuning_box.app import api
|
||||
return flask.redirect(api.url_for(
|
||||
self.__class__,
|
||||
environment_id=environment_id,
|
||||
levels=levels,
|
||||
resource_id_or_name=res_def.id,
|
||||
), code=308)
|
||||
|
||||
res_values = library.get_resource_values(environment, levels, res_def)
|
||||
result = self.perform_operation(
|
||||
operation, getattr(res_values, storage_name), flask.request.json)
|
||||
setattr(res_values, storage_name, result)
|
||||
|
@ -16,6 +16,7 @@ import flask_restful
|
||||
from tuning_box import db
|
||||
from tuning_box import library
|
||||
from tuning_box.library import levels_hierarchy
|
||||
from tuning_box.library import resource_keys_operation
|
||||
|
||||
|
||||
class ResourceOverrides(flask_restful.Resource):
|
||||
@ -23,15 +24,15 @@ class ResourceOverrides(flask_restful.Resource):
|
||||
@db.with_transaction
|
||||
def put(self, environment_id, levels, resource_id_or_name):
|
||||
environment = db.Environment.query.get_or_404(environment_id)
|
||||
resdef = library.get_resource_definition(
|
||||
res_def = library.get_resource_definition(
|
||||
resource_id_or_name, environment_id)
|
||||
if resdef.id != resource_id_or_name:
|
||||
if res_def.id != resource_id_or_name:
|
||||
from tuning_box.app import api
|
||||
return flask.redirect(api.url_for(
|
||||
ResourceOverrides,
|
||||
environment_id=environment_id,
|
||||
levels=levels,
|
||||
resource_id_or_name=resdef.id,
|
||||
resource_id_or_name=res_def.id,
|
||||
), code=308)
|
||||
|
||||
level_value = levels_hierarchy.get_environment_level_value(
|
||||
@ -39,7 +40,7 @@ class ResourceOverrides(flask_restful.Resource):
|
||||
esv = db.get_or_create(
|
||||
db.ResourceValues,
|
||||
environment=environment,
|
||||
resource_definition=resdef,
|
||||
resource_definition=res_def,
|
||||
level_value=level_value,
|
||||
)
|
||||
esv.overrides = flask.request.json
|
||||
@ -48,25 +49,38 @@ class ResourceOverrides(flask_restful.Resource):
|
||||
@db.with_transaction
|
||||
def get(self, environment_id, resource_id_or_name, levels):
|
||||
environment = db.Environment.query.get_or_404(environment_id)
|
||||
resdef = library.get_resource_definition(
|
||||
res_def = library.get_resource_definition(
|
||||
resource_id_or_name, environment_id)
|
||||
if resdef.id != resource_id_or_name:
|
||||
if res_def.id != resource_id_or_name:
|
||||
from tuning_box.app import api
|
||||
url = api.url_for(
|
||||
ResourceOverrides,
|
||||
environment_id=environment_id,
|
||||
levels=levels,
|
||||
resource_id_or_name=resdef.id,
|
||||
resource_id_or_name=res_def.id,
|
||||
)
|
||||
return flask.redirect(url, code=308)
|
||||
|
||||
level_value = levels_hierarchy.get_environment_level_value(
|
||||
environment, levels)
|
||||
res_values = db.ResourceValues.query.filter_by(
|
||||
resource_definition=resdef,
|
||||
resource_definition=res_def,
|
||||
environment=environment,
|
||||
level_value=level_value,
|
||||
).one_or_none()
|
||||
if not res_values:
|
||||
return {}
|
||||
return res_values.overrides
|
||||
|
||||
|
||||
class ResourceOverridesKeys(flask_restful.Resource,
|
||||
resource_keys_operation.ResourceKeysMixin):
|
||||
|
||||
def put(self, environment_id, levels, resource_id_or_name, operation):
|
||||
return self.patch(environment_id, levels,
|
||||
resource_id_or_name, operation)
|
||||
|
||||
def patch(self, environment_id, levels, resource_id_or_name, operation):
|
||||
self._do_update(environment_id, levels, resource_id_or_name,
|
||||
operation, 'overrides')
|
||||
return None, 204
|
||||
|
@ -17,6 +17,7 @@ import itertools
|
||||
from tuning_box import db
|
||||
from tuning_box import library
|
||||
from tuning_box.library import levels_hierarchy
|
||||
from tuning_box.library import resource_keys_operation
|
||||
|
||||
|
||||
class ResourceValues(flask_restful.Resource):
|
||||
@ -94,3 +95,16 @@ class ResourceValues(flask_restful.Resource):
|
||||
if not resource_values:
|
||||
return {}
|
||||
return resource_values.values
|
||||
|
||||
|
||||
class ResourceValuesKeys(flask_restful.Resource,
|
||||
resource_keys_operation.ResourceKeysMixin):
|
||||
|
||||
def put(self, environment_id, levels, resource_id_or_name, operation):
|
||||
return self.patch(environment_id, levels,
|
||||
resource_id_or_name, operation)
|
||||
|
||||
def patch(self, environment_id, levels, resource_id_or_name, operation):
|
||||
self._do_update(environment_id, levels, resource_id_or_name,
|
||||
operation, 'values')
|
||||
return None, 204
|
||||
|
@ -10,6 +10,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from tuning_box.app import db
|
||||
from tuning_box import errors
|
||||
from tuning_box import library
|
||||
from tuning_box.tests.test_app import BaseTest
|
||||
@ -76,3 +77,31 @@ class TestLibrary(BaseTest):
|
||||
self.assertEqual(res_id, actual_res.id)
|
||||
self.assertEqual(res_name, actual_res.name)
|
||||
self.assertEqual(component_id, actual_res.component_id)
|
||||
|
||||
def test_get_resource_values(self):
|
||||
self._fixture()
|
||||
res_def_id = 5
|
||||
environment_id = 9
|
||||
values = {'k': 'v'}
|
||||
levels = (('lvl1', 'val1'), ('lvl2', 'val2'))
|
||||
self._add_resource_values(environment_id, res_def_id, levels, values)
|
||||
|
||||
with self.app.app_context(), db.db.session.begin():
|
||||
|
||||
environment = db.Environment.query.get(environment_id)
|
||||
res_def = db.ResourceDefinition.query.get(res_def_id)
|
||||
res_values = library.get_resource_values(
|
||||
environment, levels, res_def)
|
||||
self.assertEqual(values, res_values.values)
|
||||
|
||||
def test_get_resource_values_not_found(self):
|
||||
self._fixture()
|
||||
res_def_id = 5
|
||||
environment_id = 9
|
||||
levels = (('lvl1', 'val1'), ('lvl2', 'val2'))
|
||||
with self.app.app_context(), db.db.session.begin():
|
||||
environment = db.Environment.query.get(environment_id)
|
||||
res_def = db.ResourceDefinition.query.get(res_def_id)
|
||||
self.assertRaises(errors.TuningboxNotFound,
|
||||
library.get_resource_values, environment,
|
||||
levels, res_def)
|
||||
|
@ -10,12 +10,17 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import itertools
|
||||
|
||||
from tuning_box import db
|
||||
from tuning_box.tests.test_app import BaseTest
|
||||
|
||||
|
||||
class TestResourceOverrides(BaseTest):
|
||||
|
||||
object_url = '/environments/{0}/{1}/resources/{2}/overrides'
|
||||
object_keys_url = object_url + '/keys/{3}'
|
||||
|
||||
def test_put_resource_values_overrides_root(self):
|
||||
self._fixture()
|
||||
res = self.client.put('/environments/9/resources/5/overrides',
|
||||
@ -43,17 +48,19 @@ class TestResourceOverrides(BaseTest):
|
||||
self.assertIsNotNone(resource_values)
|
||||
self.assertEqual(resource_values.overrides, {'k': 'v'})
|
||||
level_value = resource_values.level_value
|
||||
self.assertIsNotNone(level_value)
|
||||
self.assertEqual(level_value.level.name, 'lvl2')
|
||||
self.assertEqual(level_value.value, 'val2')
|
||||
level_value = level_value.parent
|
||||
self.assertIsNotNone(level_value)
|
||||
self.assertEqual(level_value.level.name, 'lvl1')
|
||||
self.assertEqual(level_value.value, 'val1')
|
||||
self.assertIsNone(level_value.parent)
|
||||
|
||||
def test_get_resource_values_local_override(self):
|
||||
self._fixture()
|
||||
res = self.client.put('/environments/9/lvl1/1/resources/5/values',
|
||||
data={'key': 'value1'})
|
||||
self.client.put('/environments/9/lvl1/1/resources/5/values',
|
||||
data={'key': 'value1'})
|
||||
res = self.client.put('/environments/9/lvl1/1/resources/5/overrides',
|
||||
data={'key': 'value2'})
|
||||
self.assertEqual(res.status_code, 204)
|
||||
@ -122,3 +129,100 @@ class TestResourceOverrides(BaseTest):
|
||||
'http://localhost'
|
||||
'/environments/9/lvl1/val1/lvl2/val2/resources/5/overrides',
|
||||
)
|
||||
|
||||
def test_put_resource_overrides_set_operation_error(self):
|
||||
self.app.config["PROPAGATE_EXCEPTIONS"] = True
|
||||
self._fixture()
|
||||
|
||||
environment_id = 9
|
||||
res_def_id = 5
|
||||
levels = (('lvl1', 'val1'), ('lvl2', 'val2'))
|
||||
overrides = {'key': 'val_overridden'}
|
||||
self._add_resource_overrides(environment_id, res_def_id, levels,
|
||||
overrides)
|
||||
|
||||
data = [['a', 'b', 'c', 'value']]
|
||||
obj_keys_url = self.object_keys_url.format(
|
||||
environment_id,
|
||||
'/'.join(itertools.chain.from_iterable(levels)),
|
||||
res_def_id,
|
||||
'set'
|
||||
)
|
||||
|
||||
res = self.client.put(obj_keys_url, data=data)
|
||||
self.assertEqual(409, res.status_code)
|
||||
|
||||
def test_put_resource_overrides_set(self):
|
||||
self._fixture()
|
||||
environment_id = 9
|
||||
res_def_id = 5
|
||||
levels = (('lvl1', 'val1'), ('lvl2', 'val2'))
|
||||
overrides = {'key': 'val_overridden'}
|
||||
self._add_resource_overrides(environment_id, res_def_id, levels,
|
||||
overrides)
|
||||
|
||||
obj_url = self.object_url.format(
|
||||
environment_id,
|
||||
'/'.join(itertools.chain.from_iterable(levels)),
|
||||
res_def_id
|
||||
)
|
||||
obj_keys_url = obj_url + '/keys/set'
|
||||
|
||||
data = [['key', 'key_over'], ['key_x', 'key_x_over']]
|
||||
res = self.client.put(obj_keys_url, data=data)
|
||||
self.assertEqual(204, res.status_code)
|
||||
|
||||
res = self.client.get(obj_url)
|
||||
self.assertEqual(200, res.status_code)
|
||||
actual = res.json
|
||||
self.assertEqual({'key': 'key_over', 'key_x': 'key_x_over'},
|
||||
actual)
|
||||
|
||||
def test_put_resource_overrides_delete(self):
|
||||
self._fixture()
|
||||
environment_id = 9
|
||||
res_def_id = 5
|
||||
levels = (('lvl1', 'val1'), ('lvl2', 'val2'))
|
||||
overrides = {'key_0': 'val_0', 'key_1': 'val_1'}
|
||||
self._add_resource_overrides(environment_id, res_def_id, levels,
|
||||
overrides)
|
||||
|
||||
obj_url = self.object_url.format(
|
||||
environment_id,
|
||||
'/'.join(itertools.chain.from_iterable(levels)),
|
||||
res_def_id
|
||||
)
|
||||
obj_keys_url = obj_url + '/keys/delete'
|
||||
|
||||
data = [['key_0']]
|
||||
res = self.client.put(obj_keys_url, data=data)
|
||||
self.assertEqual(204, res.status_code)
|
||||
|
||||
res = self.client.get(obj_url)
|
||||
self.assertEqual(200, res.status_code)
|
||||
actual = res.json
|
||||
self.assertEqual({'key_1': 'val_1'}, actual)
|
||||
|
||||
def test_put_resource_overrides_delete_operation_error(self):
|
||||
self.app.config["PROPAGATE_EXCEPTIONS"] = True
|
||||
self._fixture()
|
||||
environment_id = 9
|
||||
res_def_id = 5
|
||||
levels = (('lvl1', 'val1'), ('lvl2', 'val2'))
|
||||
overrides = {'key_0': 'val_0', 'key_1': 'val_1'}
|
||||
self._add_resource_overrides(environment_id, res_def_id, levels,
|
||||
overrides)
|
||||
|
||||
obj_keys_url = self.object_keys_url.format(
|
||||
environment_id,
|
||||
'/'.join(itertools.chain.from_iterable(levels)),
|
||||
res_def_id,
|
||||
'delete'
|
||||
)
|
||||
data = [['fake_key']]
|
||||
res = self.client.put(obj_keys_url, data=data)
|
||||
self.assertEqual(409, res.status_code)
|
||||
|
||||
data = [['key_0', 'val_0']]
|
||||
res = self.client.put(obj_keys_url, data=data)
|
||||
self.assertEqual(409, res.status_code)
|
||||
|
@ -10,12 +10,17 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import itertools
|
||||
|
||||
from tuning_box import db
|
||||
from tuning_box.tests.test_app import BaseTest
|
||||
|
||||
|
||||
class TestResourceValues(BaseTest):
|
||||
|
||||
object_url = '/environments/{0}/{1}/resources/{2}/values'
|
||||
object_keys_url = object_url + '/keys/{3}'
|
||||
|
||||
def test_put_resource_values_root(self):
|
||||
self._fixture()
|
||||
res = self.client.put('/environments/9/resources/5/values',
|
||||
@ -129,3 +134,106 @@ class TestResourceValues(BaseTest):
|
||||
'http://localhost'
|
||||
'/environments/9/lvl1/val1/lvl2/val2/resources/5/values',
|
||||
)
|
||||
|
||||
def test_put_resource_values_not_found(self):
|
||||
self.app.config["PROPAGATE_EXCEPTIONS"] = True
|
||||
self._fixture()
|
||||
|
||||
res = self.client.put(
|
||||
'/environments/9/lvl1/val1/resources/5/values/keys/set',
|
||||
data={}
|
||||
)
|
||||
self.assertEqual(404, res.status_code)
|
||||
|
||||
def test_put_resource_values_set_operation_error(self):
|
||||
self.app.config["PROPAGATE_EXCEPTIONS"] = True
|
||||
self._fixture()
|
||||
|
||||
environment_id = 9
|
||||
res_def_id = 5
|
||||
levels = (('lvl1', 'val1'), ('lvl2', 'val2'))
|
||||
values = {'key': 'val'}
|
||||
self._add_resource_values(environment_id, res_def_id, levels, values)
|
||||
|
||||
data = [['a', 'b', 'c', 'value']]
|
||||
obj_keys_url = self.object_keys_url.format(
|
||||
environment_id,
|
||||
'/'.join(itertools.chain.from_iterable(levels)),
|
||||
res_def_id,
|
||||
'set'
|
||||
)
|
||||
|
||||
res = self.client.put(obj_keys_url, data=data)
|
||||
self.assertEqual(409, res.status_code)
|
||||
|
||||
def test_put_resource_values_set(self):
|
||||
self._fixture()
|
||||
environment_id = 9
|
||||
res_def_id = 5
|
||||
levels = (('lvl1', 'val1'), ('lvl2', 'val2'))
|
||||
values = {'key': 'val'}
|
||||
self._add_resource_values(environment_id, res_def_id, levels, values)
|
||||
|
||||
obj_url = self.object_url.format(
|
||||
environment_id,
|
||||
'/'.join(itertools.chain.from_iterable(levels)),
|
||||
res_def_id
|
||||
)
|
||||
obj_keys_url = obj_url + '/keys/set'
|
||||
|
||||
data = [['key', 'key_value'], ['key_x', 'key_x_value']]
|
||||
res = self.client.put(obj_keys_url, data=data)
|
||||
self.assertEqual(204, res.status_code)
|
||||
|
||||
res = self.client.get(obj_url)
|
||||
self.assertEqual(200, res.status_code)
|
||||
actual = res.json
|
||||
self.assertEqual({'key': 'key_value', 'key_x': 'key_x_value'},
|
||||
actual)
|
||||
|
||||
def test_put_resource_values_delete(self):
|
||||
self._fixture()
|
||||
environment_id = 9
|
||||
res_def_id = 5
|
||||
levels = (('lvl1', 'val1'), ('lvl2', 'val2'))
|
||||
values = {'key_0': 'val_0', 'key_1': 'val_1'}
|
||||
self._add_resource_values(environment_id, res_def_id, levels, values)
|
||||
|
||||
obj_url = self.object_url.format(
|
||||
environment_id,
|
||||
'/'.join(itertools.chain.from_iterable(levels)),
|
||||
res_def_id
|
||||
)
|
||||
obj_keys_url = obj_url + '/keys/delete'
|
||||
|
||||
data = [['key_0']]
|
||||
res = self.client.put(obj_keys_url, data=data)
|
||||
self.assertEqual(204, res.status_code)
|
||||
|
||||
res = self.client.get(obj_url)
|
||||
self.assertEqual(200, res.status_code)
|
||||
actual = res.json
|
||||
self.assertEqual({'key_1': 'val_1'}, actual)
|
||||
|
||||
def test_put_resource_values_delete_operation_error(self):
|
||||
self.app.config["PROPAGATE_EXCEPTIONS"] = True
|
||||
self._fixture()
|
||||
environment_id = 9
|
||||
res_def_id = 5
|
||||
levels = (('lvl1', 'val1'), ('lvl2', 'val2'))
|
||||
values = {'key_0': 'val_0', 'key_1': 'val_1'}
|
||||
self._add_resource_values(environment_id, res_def_id, levels, values)
|
||||
|
||||
obj_keys_url = self.object_keys_url.format(
|
||||
environment_id,
|
||||
'/'.join(itertools.chain.from_iterable(levels)),
|
||||
res_def_id,
|
||||
'delete'
|
||||
)
|
||||
data = [['fake_key']]
|
||||
res = self.client.put(obj_keys_url, data=data)
|
||||
self.assertEqual(409, res.status_code)
|
||||
|
||||
data = [['key_0', 'val_0']]
|
||||
res = self.client.put(obj_keys_url, data=data)
|
||||
self.assertEqual(409, res.status_code)
|
||||
|
@ -10,6 +10,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import itertools
|
||||
import json
|
||||
|
||||
from flask import testing
|
||||
@ -72,6 +73,30 @@ class BaseTest(base.TestCase):
|
||||
environment.hierarchy_levels = hierarchy_levels
|
||||
db.db.session.add(environment)
|
||||
|
||||
def _add_resource_values(self, environment_id, res_def_id,
|
||||
levels, values):
|
||||
res = self.client.put(
|
||||
'/environments/{0}/{1}/resources/{2}/values'.format(
|
||||
environment_id,
|
||||
'/'.join(itertools.chain.from_iterable(levels)),
|
||||
res_def_id
|
||||
),
|
||||
data=values
|
||||
)
|
||||
self.assertEqual(res.status_code, 204)
|
||||
|
||||
def _add_resource_overrides(self, environment_id, res_def_id,
|
||||
levels, overrides):
|
||||
res = self.client.put(
|
||||
'/environments/{0}/{1}/resources/{2}/overrides'.format(
|
||||
environment_id,
|
||||
'/'.join(itertools.chain.from_iterable(levels)),
|
||||
res_def_id
|
||||
),
|
||||
data=overrides
|
||||
)
|
||||
self.assertEqual(res.status_code, 204)
|
||||
|
||||
def _assert_db_effect(self, model, key, fields, expected):
|
||||
with self.app.app_context():
|
||||
obj = model.query.get(key)
|
||||
|
Loading…
x
Reference in New Issue
Block a user