Resource values/overrides moved to library

Implementation of resource values operations moved to library.
Filtration of resource definition with respect to envirionment id
implemented.
Implementation of resource overrides operations moved to library.
All business logic finally moved from the app to the library.

Change-Id: If5d14699e56485761ca6c54bd8bbf34dd21e9155
This commit is contained in:
Alexander Kislitsky 2016-08-15 17:29:59 +03:00
parent c944943450
commit bbbc3d848f
10 changed files with 651 additions and 424 deletions

View File

@ -10,13 +10,9 @@
# License for the specific language governing permissions and limitations
# under the License.
import functools
import itertools
import flask
import flask_restful
from sqlalchemy import exc as sa_exc
from werkzeug import exceptions
from tuning_box import converters
from tuning_box import db
@ -24,6 +20,8 @@ from tuning_box import errors
from tuning_box.library import components
from tuning_box.library import environments
from tuning_box.library import resource_definitions
from tuning_box.library import resource_overrides
from tuning_box.library import resource_values
from tuning_box import logger
from tuning_box.middleware import keystone
@ -35,8 +33,11 @@ api_errors = {
}
api = flask_restful.Api(errors=api_errors)
# Components
api.add_resource(components.ComponentsCollection, '/components')
api.add_resource(components.Component, '/components/<int:component_id>')
# Resource definitions
api.add_resource(
resource_definitions.ResourceDefinitionsCollection,
'/resource_definitions',
@ -45,6 +46,21 @@ api.add_resource(
resource_definitions.ResourceDefinition,
'/resource_definition/<int:resource_definition_id>'
)
# Resource values
api.add_resource(
resource_values.ResourceValues,
'/environments/<int:environment_id>/<levels:levels>resources/'
'<id_or_name:resource_id_or_name>/values'
)
# Resource overrides
api.add_resource(
resource_overrides.ResourceOverrides,
'/environments/<int:environment_id>/'
'<levels:levels>resources/<id_or_name:resource_id_or_name>/overrides')
# Environments
api.add_resource(environments.EnvironmentsCollection, '/environments')
api.add_resource(
environments.Environment,
@ -53,166 +69,6 @@ api.add_resource(
)
def with_transaction(f):
@functools.wraps(f)
def inner(*args, **kwargs):
with db.db.session.begin():
return f(*args, **kwargs)
return inner
def iter_environment_level_values(environment, levels):
env_levels = db.EnvironmentHierarchyLevel.get_for_environment(environment)
level_pairs = zip(env_levels, levels)
parent_level_value = None
for env_level, (level_name, level_value) in level_pairs:
if env_level.name != level_name:
raise exceptions.BadRequest(
"Unexpected level name '%s'. Expected '%s'." % (
level_name, env_level.name))
level_value_db = db.get_or_create(
db.EnvironmentHierarchyLevelValue,
level=env_level,
parent=parent_level_value,
value=level_value,
)
yield level_value_db
parent_level_value = level_value_db
def get_environment_level_value(environment, levels):
level_value = None
for level_value in iter_environment_level_values(environment, levels):
pass
return level_value
@api.resource(
'/environments/<int:environment_id>' +
'/<levels:levels>resources/<id_or_name:resource_id_or_name>/values')
class ResourceValues(flask_restful.Resource):
@with_transaction
def put(self, environment_id, levels, resource_id_or_name):
environment = db.Environment.query.get_or_404(environment_id)
level_value = get_environment_level_value(environment, levels)
# TODO(yorik-sar): filter by environment
resdef = db.ResourceDefinition.query.get_by_id_or_name(
resource_id_or_name)
if resdef.id != resource_id_or_name:
return flask.redirect(api.url_for(
ResourceValues,
environment_id=environment_id,
levels=levels,
resource_id_or_name=resdef.id,
), code=308)
esv = db.get_or_create(
db.ResourceValues,
environment=environment,
resource_definition=resdef,
level_value=level_value,
)
esv.values = flask.request.json
return None, 204
@with_transaction
def get(self, environment_id, resource_id_or_name, levels):
environment = db.Environment.query.get_or_404(environment_id)
level_values = list(iter_environment_level_values(environment, levels))
# TODO(yorik-sar): filter by environment
resdef = db.ResourceDefinition.query.get_by_id_or_name(
resource_id_or_name)
if resdef.id != resource_id_or_name:
url = api.url_for(
ResourceValues,
environment_id=environment_id,
levels=levels,
resource_id_or_name=resdef.id,
)
if flask.request.query_string:
qs = flask.request.query_string.decode('utf-8')
url += '?' + qs
return flask.redirect(url, code=308)
if 'effective' in flask.request.args:
resource_values = db.ResourceValues.query.filter_by(
resource_definition=resdef,
environment=environment,
).all()
result = {}
for level_value in itertools.chain([None], level_values):
for resource_value in resource_values:
if resource_value.level_value == level_value:
result.update(resource_value.values)
result.update(resource_value.overrides)
break
return result
else:
if not level_values:
level_value = None
else:
level_value = level_values[-1]
resource_values = db.ResourceValues.query.filter_by(
resource_definition=resdef,
environment=environment,
level_value=level_value,
).one_or_none()
if not resource_values:
return {}
return resource_values.values
@api.resource(
'/environments/<int:environment_id>' +
'/<levels:levels>resources/<id_or_name:resource_id_or_name>/overrides')
class ResourceOverrides(flask_restful.Resource):
@with_transaction
def put(self, environment_id, levels, resource_id_or_name):
environment = db.Environment.query.get_or_404(environment_id)
level_value = get_environment_level_value(environment, levels)
# TODO(yorik-sar): filter by environment
resdef = db.ResourceDefinition.query.get_by_id_or_name(
resource_id_or_name)
if resdef.id != resource_id_or_name:
return flask.redirect(api.url_for(
ResourceOverrides,
environment_id=environment_id,
levels=levels,
resource_id_or_name=resdef.id,
), code=308)
esv = db.get_or_create(
db.ResourceValues,
environment=environment,
resource_definition=resdef,
level_value=level_value,
)
esv.overrides = flask.request.json
return None, 204
@with_transaction
def get(self, environment_id, resource_id_or_name, levels):
environment = db.Environment.query.get_or_404(environment_id)
level_value = get_environment_level_value(environment, levels)
# TODO(yorik-sar): filter by environment
resdef = db.ResourceDefinition.query.get_by_id_or_name(
resource_id_or_name)
if resdef.id != resource_id_or_name:
url = api.url_for(
ResourceOverrides,
environment_id=environment_id,
levels=levels,
resource_id_or_name=resdef.id,
)
return flask.redirect(url, code=308)
resource_values = db.ResourceValues.query.filter_by(
resource_definition=resdef,
environment=environment,
level_value=level_value,
).one_or_none()
if not resource_values:
return {}
return resource_values.overrides
def handle_integrity_error(exc):
response = flask.jsonify(msg=exc.args[0])
response.status_code = 409

View File

@ -10,6 +10,9 @@
# License for the specific language governing permissions and limitations
# under the License.
from sqlalchemy.orm import exc as sa_exc
from tuning_box import db
from tuning_box import errors
@ -42,3 +45,29 @@ def load_objects_by_id_or_name(model, identifiers):
)
result.append(obj)
return result
def get_resource_definition(id_or_name, environment_id):
query = db.ResourceDefinition.query.join(db.Component). \
join(db.Environment.environment_components_table). \
filter_by(environment_id=environment_id)
if isinstance(id_or_name, int):
query = query.filter(db.ResourceDefinition.id == id_or_name)
else:
query = query.filter(db.ResourceDefinition.name == id_or_name)
result = query.all()
if not result:
raise errors.TuningboxNotFound(
"{0} not found by {1} in environment {2}".format(
db.ResourceDefinition.__tablename__,
id_or_name,
environment_id
)
)
elif len(result) > 1:
raise sa_exc.MultipleResultsFound
return result[0]

View File

@ -0,0 +1,41 @@
# 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 werkzeug
from tuning_box import db
def iter_environment_level_values(environment, levels):
env_levels = db.EnvironmentHierarchyLevel.get_for_environment(environment)
level_pairs = zip(env_levels, levels)
parent_level_value = None
for env_level, (level_name, level_value) in level_pairs:
if env_level.name != level_name:
raise werkzeug.exceptions.BadRequest(
"Unexpected level name '{0}'. Expected '{1}'.".format(
level_name, env_level.name))
level_value_db = db.get_or_create(
db.EnvironmentHierarchyLevelValue,
level=env_level,
parent=parent_level_value,
value=level_value,
)
yield level_value_db
parent_level_value = level_value_db
def get_environment_level_value(environment, levels):
level_value = None
for level_value in iter_environment_level_values(environment, levels):
pass
return level_value

View File

@ -0,0 +1,72 @@
# 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 flask
import flask_restful
from tuning_box import db
from tuning_box import library
from tuning_box.library import levels_hierarchy
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(
resource_id_or_name, environment_id)
if resdef.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,
), code=308)
level_value = levels_hierarchy.get_environment_level_value(
environment, levels)
esv = db.get_or_create(
db.ResourceValues,
environment=environment,
resource_definition=resdef,
level_value=level_value,
)
esv.overrides = flask.request.json
return None, 204
@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(
resource_id_or_name, environment_id)
if resdef.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,
)
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,
environment=environment,
level_value=level_value,
).one_or_none()
if not res_values:
return {}
return res_values.overrides

View File

@ -0,0 +1,96 @@
# 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 flask
import flask_restful
import itertools
from tuning_box import db
from tuning_box import library
from tuning_box.library import levels_hierarchy
class ResourceValues(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(
resource_id_or_name, environment_id)
if resdef.id != resource_id_or_name:
from tuning_box.app import api
return flask.redirect(api.url_for(
ResourceValues,
environment_id=environment_id,
levels=levels,
resource_id_or_name=resdef.id,
), code=308)
level_value = levels_hierarchy.get_environment_level_value(
environment, levels)
esv = db.get_or_create(
db.ResourceValues,
environment=environment,
resource_definition=resdef,
level_value=level_value,
)
esv.values = flask.request.json
return None, 204
@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(
resource_id_or_name, environment_id)
if resdef.id != resource_id_or_name:
from tuning_box.app import api
url = api.url_for(
ResourceValues,
environment_id=environment_id,
levels=levels,
resource_id_or_name=resdef.id,
)
if flask.request.query_string:
qs = flask.request.query_string.decode('utf-8')
url += '?' + qs
return flask.redirect(url, code=308)
level_values = list(levels_hierarchy.iter_environment_level_values(
environment, levels))
if 'effective' in flask.request.args:
resource_values = db.ResourceValues.query.filter_by(
resource_definition=resdef,
environment=environment,
).all()
result = {}
for level_value in itertools.chain([None], level_values):
for resource_value in resource_values:
if resource_value.level_value == level_value:
result.update(resource_value.values)
result.update(resource_value.overrides)
break
return result
else:
if not level_values:
level_value = None
else:
level_value = level_values[-1]
resource_values = db.ResourceValues.query.filter_by(
resource_definition=resdef,
environment=environment,
level_value=level_value,
).one_or_none()
if not resource_values:
return {}
return resource_values.values

View File

@ -0,0 +1,59 @@
# 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 werkzeug
from tuning_box import db
from tuning_box.library import levels_hierarchy
from tuning_box.tests.test_app import BaseTest
class TestLevelsHierarchy(BaseTest):
def test_get_environment_level_value_root(self):
self._fixture()
with self.app.app_context(), db.db.session.begin():
level_value = levels_hierarchy.get_environment_level_value(
db.Environment(id=9),
[],
)
self.assertIsNone(level_value)
def test_get_environment_level_value_deep(self):
self._fixture()
with self.app.app_context(), db.db.session.begin():
level_value = levels_hierarchy.get_environment_level_value(
db.Environment(id=9),
[('lvl1', 'val1'), ('lvl2', 'val2')],
)
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_environment_level_value_bad_level(self):
self._fixture()
with self.app.app_context(), db.db.session.begin():
exc = self.assertRaises(
werkzeug.exceptions.BadRequest,
levels_hierarchy.get_environment_level_value,
db.Environment(id=9),
[('lvlx', 'val1')],
)
self.assertEqual(
exc.description,
"Unexpected level name 'lvlx'. Expected 'lvl1'.",
)

View File

@ -0,0 +1,78 @@
# 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.
from tuning_box import errors
from tuning_box import library
from tuning_box.tests.test_app import BaseTest
class TestLibrary(BaseTest):
def add_res_def_to_another_env(self, res_name):
component_data = {
'name': 'component2',
'resource_definitions': [{
'name': res_name,
'content': {'key': 'value'}
}]
}
res = self.client.post('/components', data=component_data)
self.assertEqual(201, res.status_code)
component_id = res.json['id']
env_data = {
'components': [component_id],
'hierarchy_levels': [],
}
res = self.client.post('/environments', data=env_data)
self.assertEqual(201, res.status_code)
def test_get_resource_definition(self):
self._fixture()
res_name = 'resdef1'
res_id = 5
environment_id = 9
component_id = 7
# Creating resource definition with the same name in another
# environment
self.add_res_def_to_another_env(res_name)
res = self.client.get('/resource_definitions')
self.assertEqual(200, res.status_code)
self.assertTrue(all(res_def['name'] == res_name
for res_def in res.json))
with self.app.app_context():
self.assertRaises(errors.TuningboxNotFound,
library.get_resource_definition, res_id, None)
self.assertRaises(errors.TuningboxNotFound,
library.get_resource_definition, res_name, None)
self.assertRaises(errors.TuningboxNotFound,
library.get_resource_definition, '',
environment_id)
self.assertRaises(errors.TuningboxNotFound,
library.get_resource_definition, None, None)
self.assertRaises(errors.TuningboxNotFound,
library.get_resource_definition, None,
environment_id)
actual_res = library.get_resource_definition(res_id,
environment_id)
self.assertEqual(res_id, actual_res.id)
self.assertEqual(res_name, actual_res.name)
self.assertEqual(component_id, actual_res.component_id)
actual_res = library.get_resource_definition(res_id,
environment_id)
self.assertEqual(res_id, actual_res.id)
self.assertEqual(res_name, actual_res.name)
self.assertEqual(component_id, actual_res.component_id)

View File

@ -0,0 +1,124 @@
# 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.
from tuning_box import db
from tuning_box.tests.test_app import BaseTest
class TestResourceOverrides(BaseTest):
def test_put_resource_values_overrides_root(self):
self._fixture()
res = self.client.put('/environments/9/resources/5/overrides',
data={'k': 'v'})
self.assertEqual(res.status_code, 204)
self.assertEqual(res.data, b'')
with self.app.app_context():
resource_values = db.ResourceValues.query.filter_by(
environment_id=9, resource_definition_id=5).one_or_none()
self.assertIsNotNone(resource_values)
self.assertEqual(resource_values.overrides, {'k': 'v'})
self.assertIsNone(resource_values.level_value)
def test_put_resource_values_overrides_deep(self):
self._fixture()
res = self.client.put(
'/environments/9/lvl1/val1/lvl2/val2/resources/5/overrides',
data={'k': 'v'},
)
self.assertEqual(res.status_code, 204)
self.assertEqual(res.data, b'')
with self.app.app_context():
resource_values = db.ResourceValues.query.filter_by(
environment_id=9, resource_definition_id=5).one_or_none()
self.assertIsNotNone(resource_values)
self.assertEqual(resource_values.overrides, {'k': 'v'})
level_value = resource_values.level_value
self.assertEqual(level_value.level.name, 'lvl2')
self.assertEqual(level_value.value, 'val2')
level_value = level_value.parent
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'})
res = self.client.put('/environments/9/lvl1/1/resources/5/overrides',
data={'key': 'value2'})
self.assertEqual(res.status_code, 204)
self.assertEqual(res.data, b'')
res = self.client.get(
'/environments/9/lvl1/1/resources/5/values?effective',
)
self.assertEqual(res.status_code, 200)
self.assertEqual(res.json, {'key': 'value2'})
def test_get_resource_values_level_override(self):
self._fixture()
self.client.put('/environments/9/resources/5/values',
data={'key': 'value', 'key1': 'value'})
self.client.put('/environments/9/lvl1/1/resources/5/values',
data={'key': 'value1'})
res = self.client.put('/environments/9/lvl1/2/resources/5/values',
data={'key1': 'value2'})
self.assertEqual(res.status_code, 204)
self.assertEqual(res.data, b'')
res = self.client.get(
'/environments/9/lvl1/1/resources/5/values?effective',
)
self.assertEqual(res.status_code, 200)
self.assertEqual(res.json, {'key': 'value1', 'key1': 'value'})
def test_get_resource_values_level_and_local_override(self):
self._fixture()
self.client.put('/environments/9/resources/5/values',
data={'key': 'value', 'key1': 'value'})
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={'key1': 'value2'})
self.assertEqual(res.status_code, 204)
self.assertEqual(res.data, b'')
res = self.client.get(
'/environments/9/lvl1/1/resources/5/values?effective',
)
self.assertEqual(res.status_code, 200)
self.assertEqual(res.json, {'key': 'value1', 'key1': 'value2'})
def test_put_resource_overrides_redirect(self):
self._fixture()
res = self.client.put(
'/environments/9/lvl1/val1/lvl2/val2/resources/resdef1/overrides',
data={'k': 'v'},
)
self.assertEqual(res.status_code, 308)
self.assertEqual(
res.headers['Location'],
'http://localhost'
'/environments/9/lvl1/val1/lvl2/val2/resources/5/overrides',
)
def test_get_resource_overrides_redirect(self):
self._fixture()
self.client.put('/environments/9/resources/5/overrides',
data={'key': 'value'})
res = self.client.get(
'/environments/9/lvl1/val1/lvl2/val2/resources/resdef1/overrides',
)
self.assertEqual(res.status_code, 308)
self.assertEqual(
res.headers['Location'],
'http://localhost'
'/environments/9/lvl1/val1/lvl2/val2/resources/5/overrides',
)

View File

@ -0,0 +1,131 @@
# 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.
from tuning_box import db
from tuning_box.tests.test_app import BaseTest
class TestResourceValues(BaseTest):
def test_put_resource_values_root(self):
self._fixture()
res = self.client.put('/environments/9/resources/5/values',
data={'k': 'v'})
self.assertEqual(res.status_code, 204)
self.assertEqual(res.data, b'')
with self.app.app_context():
resource_values = db.ResourceValues.query.filter_by(
environment_id=9, resource_definition_id=5).one_or_none()
self.assertIsNotNone(resource_values)
self.assertEqual(resource_values.values, {'k': 'v'})
self.assertIsNone(resource_values.level_value)
def test_put_resource_values_deep(self):
self._fixture()
res = self.client.put(
'/environments/9/lvl1/val1/lvl2/val2/resources/5/values',
data={'k': 'v'},
)
self.assertEqual(res.status_code, 204)
self.assertEqual(res.data, b'')
with self.app.app_context():
resource_values = db.ResourceValues.query.filter_by(
environment_id=9, resource_definition_id=5).one_or_none()
self.assertIsNotNone(resource_values)
self.assertEqual(resource_values.values, {'k': 'v'})
level_value = resource_values.level_value
self.assertEqual(level_value.level.name, 'lvl2')
self.assertEqual(level_value.value, 'val2')
level_value = level_value.parent
self.assertEqual(level_value.level.name, 'lvl1')
self.assertEqual(level_value.value, 'val1')
self.assertIsNone(level_value.parent)
def test_put_resource_values_bad_level(self):
self._fixture()
res = self.client.put('/environments/9/lvlx/1/resources/5/values',
data={'k': 'v'})
self.assertEqual(res.status_code, 400)
self.assertEqual(
res.json,
{"message": "Unexpected level name 'lvlx'. Expected 'lvl1'."},
)
with self.app.app_context():
resource_values = db.ResourceValues.query.filter_by(
environment_id=9, resource_definition_id=5).one_or_none()
self.assertIsNone(resource_values)
def test_get_resource_values(self):
self._fixture()
res = self.client.put('/environments/9/resources/5/values',
data={'key': 'value'})
self.assertEqual(res.status_code, 204)
self.assertEqual(res.data, b'')
res = self.client.get(
'/environments/9/lvl1/1/resources/5/values',
)
self.assertEqual(res.status_code, 200)
self.assertEqual(res.json, {})
def test_get_resource_values_effective(self):
self._fixture()
res = self.client.put('/environments/9/resources/5/values',
data={'key': 'value'})
self.assertEqual(res.status_code, 204)
self.assertEqual(res.data, b'')
res = self.client.get(
'/environments/9/lvl1/1/resources/5/values?effective',
)
self.assertEqual(res.status_code, 200)
self.assertEqual(res.json, {'key': 'value'})
def test_get_resource_values_redirect(self):
self._fixture()
self.client.put('/environments/9/resources/5/values',
data={'key': 'value'})
res = self.client.get(
'/environments/9/lvl1/val1/lvl2/val2/resources/resdef1/values',
)
self.assertEqual(res.status_code, 308)
self.assertEqual(
res.headers['Location'],
'http://localhost'
'/environments/9/lvl1/val1/lvl2/val2/resources/5/values',
)
def test_get_resource_values_redirect_with_query(self):
self._fixture()
self.client.put('/environments/9/resources/5/values',
data={'key': 'value'})
res = self.client.get(
'/environments/9/lvl1/val1/lvl2/val2/resources/resdef1/values'
'?effective',
)
self.assertEqual(res.status_code, 308)
self.assertEqual(
res.headers['Location'],
'http://localhost'
'/environments/9/lvl1/val1/lvl2/val2/resources/5/values?effective',
)
def test_put_resource_values_redirect(self):
self._fixture()
res = self.client.put(
'/environments/9/lvl1/val1/lvl2/val2/resources/resdef1/values',
data={'k': 'v'},
)
self.assertEqual(res.status_code, 308)
self.assertEqual(
res.headers['Location'],
'http://localhost'
'/environments/9/lvl1/val1/lvl2/val2/resources/5/values',
)

View File

@ -14,7 +14,6 @@ import json
from flask import testing
import flask_restful
from werkzeug import exceptions
from werkzeug import wrappers
from tuning_box import app
@ -87,265 +86,7 @@ class BaseTest(base.TestCase):
class TestApp(BaseTest):
def test_get_environment_level_value_root(self):
self._fixture()
with self.app.app_context(), db.db.session.begin():
level_value = app.get_environment_level_value(
db.Environment(id=9),
[],
)
self.assertIsNone(level_value)
def test_get_environment_level_value_deep(self):
self._fixture()
with self.app.app_context(), db.db.session.begin():
level_value = app.get_environment_level_value(
db.Environment(id=9),
[('lvl1', 'val1'), ('lvl2', 'val2')],
)
self.assertIsNotNone(level_value)
self.assertEqual(level_value.level.name, 'lvl2')
self.assertEqual(level_value.value, 'val2')
level_value = level_value.parent
self.assertEqual(level_value.level.name, 'lvl1')
self.assertEqual(level_value.value, 'val1')
self.assertIsNone(level_value.parent)
def test_get_environment_level_value_bad_level(self):
self._fixture()
with self.app.app_context(), db.db.session.begin():
exc = self.assertRaises(
exceptions.BadRequest,
app.get_environment_level_value,
db.Environment(id=9),
[('lvlx', 'val1')],
)
self.assertEqual(
exc.description,
"Unexpected level name 'lvlx'. Expected 'lvl1'.",
)
def test_put_resource_values_root(self):
self._fixture()
res = self.client.put('/environments/9/resources/5/values',
data={'k': 'v'})
self.assertEqual(res.status_code, 204)
self.assertEqual(res.data, b'')
with self.app.app_context():
resource_values = db.ResourceValues.query.filter_by(
environment_id=9, resource_definition_id=5).one_or_none()
self.assertIsNotNone(resource_values)
self.assertEqual(resource_values.values, {'k': 'v'})
self.assertIsNone(resource_values.level_value)
def test_put_resource_values_deep(self):
self._fixture()
res = self.client.put(
'/environments/9/lvl1/val1/lvl2/val2/resources/5/values',
data={'k': 'v'},
)
self.assertEqual(res.status_code, 204)
self.assertEqual(res.data, b'')
with self.app.app_context():
resource_values = db.ResourceValues.query.filter_by(
environment_id=9, resource_definition_id=5).one_or_none()
self.assertIsNotNone(resource_values)
self.assertEqual(resource_values.values, {'k': 'v'})
level_value = resource_values.level_value
self.assertEqual(level_value.level.name, 'lvl2')
self.assertEqual(level_value.value, 'val2')
level_value = level_value.parent
self.assertEqual(level_value.level.name, 'lvl1')
self.assertEqual(level_value.value, 'val1')
self.assertIsNone(level_value.parent)
def test_put_resource_values_overrides_root(self):
self._fixture()
res = self.client.put('/environments/9/resources/5/overrides',
data={'k': 'v'})
self.assertEqual(res.status_code, 204)
self.assertEqual(res.data, b'')
with self.app.app_context():
resource_values = db.ResourceValues.query.filter_by(
environment_id=9, resource_definition_id=5).one_or_none()
self.assertIsNotNone(resource_values)
self.assertEqual(resource_values.overrides, {'k': 'v'})
self.assertIsNone(resource_values.level_value)
def test_put_resource_values_overrides_deep(self):
self._fixture()
res = self.client.put(
'/environments/9/lvl1/val1/lvl2/val2/resources/5/overrides',
data={'k': 'v'},
)
self.assertEqual(res.status_code, 204)
self.assertEqual(res.data, b'')
with self.app.app_context():
resource_values = db.ResourceValues.query.filter_by(
environment_id=9, resource_definition_id=5).one_or_none()
self.assertIsNotNone(resource_values)
self.assertEqual(resource_values.overrides, {'k': 'v'})
level_value = resource_values.level_value
self.assertEqual(level_value.level.name, 'lvl2')
self.assertEqual(level_value.value, 'val2')
level_value = level_value.parent
self.assertEqual(level_value.level.name, 'lvl1')
self.assertEqual(level_value.value, 'val1')
self.assertIsNone(level_value.parent)
def test_put_resource_values_bad_level(self):
self._fixture()
res = self.client.put('/environments/9/lvlx/1/resources/5/values',
data={'k': 'v'})
self.assertEqual(res.status_code, 400)
self.assertEqual(
res.json,
{"message": "Unexpected level name 'lvlx'. Expected 'lvl1'."},
)
with self.app.app_context():
resource_values = db.ResourceValues.query.filter_by(
environment_id=9, resource_definition_id=5).one_or_none()
self.assertIsNone(resource_values)
def test_get_resource_values(self):
self._fixture()
res = self.client.put('/environments/9/resources/5/values',
data={'key': 'value'})
self.assertEqual(res.status_code, 204)
self.assertEqual(res.data, b'')
res = self.client.get(
'/environments/9/lvl1/1/resources/5/values',
)
self.assertEqual(res.status_code, 200)
self.assertEqual(res.json, {})
def test_get_resource_values_effective(self):
self._fixture()
res = self.client.put('/environments/9/resources/5/values',
data={'key': 'value'})
self.assertEqual(res.status_code, 204)
self.assertEqual(res.data, b'')
res = self.client.get(
'/environments/9/lvl1/1/resources/5/values?effective',
)
self.assertEqual(res.status_code, 200)
self.assertEqual(res.json, {'key': 'value'})
def test_get_resource_values_local_override(self):
self._fixture()
res = 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)
self.assertEqual(res.data, b'')
res = self.client.get(
'/environments/9/lvl1/1/resources/5/values?effective',
)
self.assertEqual(res.status_code, 200)
self.assertEqual(res.json, {'key': 'value2'})
def test_get_resource_values_level_override(self):
self._fixture()
res = self.client.put('/environments/9/resources/5/values',
data={'key': 'value', 'key1': 'value'})
res = self.client.put('/environments/9/lvl1/1/resources/5/values',
data={'key': 'value1'})
res = self.client.put('/environments/9/lvl1/2/resources/5/values',
data={'key1': 'value2'})
self.assertEqual(res.status_code, 204)
self.assertEqual(res.data, b'')
res = self.client.get(
'/environments/9/lvl1/1/resources/5/values?effective',
)
self.assertEqual(res.status_code, 200)
self.assertEqual(res.json, {'key': 'value1', 'key1': 'value'})
def test_get_resource_values_level_and_local_override(self):
self._fixture()
res = self.client.put('/environments/9/resources/5/values',
data={'key': 'value', 'key1': 'value'})
res = 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={'key1': 'value2'})
self.assertEqual(res.status_code, 204)
self.assertEqual(res.data, b'')
res = self.client.get(
'/environments/9/lvl1/1/resources/5/values?effective',
)
self.assertEqual(res.status_code, 200)
self.assertEqual(res.json, {'key': 'value1', 'key1': 'value2'})
def test_put_resource_values_redirect(self):
self._fixture()
res = self.client.put(
'/environments/9/lvl1/val1/lvl2/val2/resources/resdef1/values',
data={'k': 'v'},
)
self.assertEqual(res.status_code, 308)
self.assertEqual(
res.headers['Location'],
'http://localhost'
'/environments/9/lvl1/val1/lvl2/val2/resources/5/values',
)
def test_get_resource_values_redirect(self):
self._fixture()
res = self.client.put('/environments/9/resources/5/values',
data={'key': 'value'})
res = self.client.get(
'/environments/9/lvl1/val1/lvl2/val2/resources/resdef1/values',
)
self.assertEqual(res.status_code, 308)
self.assertEqual(
res.headers['Location'],
'http://localhost'
'/environments/9/lvl1/val1/lvl2/val2/resources/5/values',
)
def test_get_resource_values_redirect_with_query(self):
self._fixture()
res = self.client.put('/environments/9/resources/5/values',
data={'key': 'value'})
res = self.client.get(
'/environments/9/lvl1/val1/lvl2/val2/resources/resdef1/values'
'?effective',
)
self.assertEqual(res.status_code, 308)
self.assertEqual(
res.headers['Location'],
'http://localhost'
'/environments/9/lvl1/val1/lvl2/val2/resources/5/values?effective',
)
def test_put_resource_overrides_redirect(self):
self._fixture()
res = self.client.put(
'/environments/9/lvl1/val1/lvl2/val2/resources/resdef1/overrides',
data={'k': 'v'},
)
self.assertEqual(res.status_code, 308)
self.assertEqual(
res.headers['Location'],
'http://localhost'
'/environments/9/lvl1/val1/lvl2/val2/resources/5/overrides',
)
def test_get_resource_overrides_redirect(self):
self._fixture()
res = self.client.put('/environments/9/resources/5/overrides',
data={'key': 'value'})
res = self.client.get(
'/environments/9/lvl1/val1/lvl2/val2/resources/resdef1/overrides',
)
self.assertEqual(res.status_code, 308)
self.assertEqual(
res.headers['Location'],
'http://localhost'
'/environments/9/lvl1/val1/lvl2/val2/resources/5/overrides',
)
pass
class TestAppPrefixed(base.PrefixedTestCaseMixin, TestApp):