Put and patch methods implemented for components
Components implementation moved to separate module. Tests for components decoupled from test_app. Change-Id: I9a653f3211708ab071b146ba6cf8527b29a698f1
This commit is contained in:
parent
daa9536cd7
commit
042223382b
|
@ -21,6 +21,7 @@ from werkzeug import exceptions
|
|||
|
||||
from tuning_box import converters
|
||||
from tuning_box import db
|
||||
from tuning_box.library import components
|
||||
from tuning_box import logger
|
||||
from tuning_box.middleware import keystone
|
||||
|
||||
|
@ -30,19 +31,8 @@ api_errors = {
|
|||
}
|
||||
api = flask_restful.Api(errors=api_errors)
|
||||
|
||||
resource_definition_fields = {
|
||||
'id': fields.Integer,
|
||||
'name': fields.String,
|
||||
'component_id': fields.Integer,
|
||||
'content': fields.Raw,
|
||||
}
|
||||
|
||||
component_fields = {
|
||||
'id': fields.Integer,
|
||||
'name': fields.String,
|
||||
'resource_definitions': fields.List(
|
||||
fields.Nested(resource_definition_fields)),
|
||||
}
|
||||
api.add_resource(components.ComponentsCollection, '/components')
|
||||
api.add_resource(components.Component, '/components/<int:component_id>')
|
||||
|
||||
|
||||
def with_transaction(f):
|
||||
|
@ -54,38 +44,6 @@ def with_transaction(f):
|
|||
return inner
|
||||
|
||||
|
||||
@api.resource('/components')
|
||||
class ComponentsCollection(flask_restful.Resource):
|
||||
method_decorators = [flask_restful.marshal_with(component_fields)]
|
||||
|
||||
def get(self):
|
||||
return db.Component.query.all()
|
||||
|
||||
@with_transaction
|
||||
def post(self):
|
||||
component = db.Component(name=flask.request.json['name'])
|
||||
component.resource_definitions = []
|
||||
for resdef_data in flask.request.json.get('resource_definitions'):
|
||||
resdef = db.ResourceDefinition(name=resdef_data['name'],
|
||||
content=resdef_data.get('content'))
|
||||
component.resource_definitions.append(resdef)
|
||||
db.db.session.add(component)
|
||||
return component, 201
|
||||
|
||||
|
||||
@api.resource('/components/<int:component_id>')
|
||||
class Component(flask_restful.Resource):
|
||||
method_decorators = [flask_restful.marshal_with(component_fields)]
|
||||
|
||||
def get(self, component_id):
|
||||
return db.Component.query.get_or_404(component_id)
|
||||
|
||||
@with_transaction
|
||||
def delete(self, component_id):
|
||||
component = db.Component.query.get_or_404(component_id)
|
||||
db.db.session.delete(component)
|
||||
return None, 204
|
||||
|
||||
environment_fields = {
|
||||
'id': fields.Integer,
|
||||
'components': fields.List(fields.Integer(attribute='id')),
|
||||
|
|
|
@ -31,6 +31,15 @@ pk_type = db.Integer
|
|||
pk = functools.partial(db.Column, pk_type, primary_key=True)
|
||||
|
||||
|
||||
def with_transaction(f):
|
||||
@functools.wraps(f)
|
||||
def inner(*args, **kwargs):
|
||||
with db.session.begin():
|
||||
return f(*args, **kwargs)
|
||||
|
||||
return inner
|
||||
|
||||
|
||||
def fk(cls, **kwargs):
|
||||
return db.Column(pk_type, db.ForeignKey(cls.id), **kwargs)
|
||||
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
# 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 flask_restful import fields
|
||||
|
||||
from tuning_box import db
|
||||
|
||||
|
||||
resource_definition_fields = {
|
||||
'id': fields.Integer,
|
||||
'name': fields.String,
|
||||
'component_id': fields.Integer,
|
||||
'content': fields.Raw,
|
||||
}
|
||||
|
||||
component_fields = {
|
||||
'id': fields.Integer,
|
||||
'name': fields.String,
|
||||
'resource_definitions': fields.List(
|
||||
fields.Nested(resource_definition_fields)),
|
||||
}
|
||||
|
||||
|
||||
class ComponentsCollection(flask_restful.Resource):
|
||||
method_decorators = [flask_restful.marshal_with(component_fields)]
|
||||
|
||||
def get(self):
|
||||
return db.Component.query.all()
|
||||
|
||||
@db.with_transaction
|
||||
def post(self):
|
||||
component = db.Component(name=flask.request.json['name'])
|
||||
component.resource_definitions = []
|
||||
for resdef_data in flask.request.json.get('resource_definitions'):
|
||||
resdef = db.ResourceDefinition(name=resdef_data['name'],
|
||||
content=resdef_data.get('content'))
|
||||
component.resource_definitions.append(resdef)
|
||||
db.db.session.add(component)
|
||||
return component, 201
|
||||
|
||||
|
||||
class Component(flask_restful.Resource):
|
||||
method_decorators = [flask_restful.marshal_with(component_fields)]
|
||||
|
||||
def get(self, component_id):
|
||||
return db.Component.query.get_or_404(component_id)
|
||||
|
||||
@db.with_transaction
|
||||
def _perform_update(self, component_id):
|
||||
component = db.Component.query.get_or_404(component_id)
|
||||
update_by = flask.request.json
|
||||
component.name = update_by.get('name', component.name)
|
||||
resource_definitions = update_by.get('resource_definitions')
|
||||
if resource_definitions is not None:
|
||||
resources = []
|
||||
for resource_data in resource_definitions:
|
||||
resource = db.ResourceDefinition.query.filter_by(
|
||||
id=resource_data.get('id')
|
||||
).one()
|
||||
resource.component_id = component.id
|
||||
db.db.session.add(resource)
|
||||
resources.append(resource)
|
||||
component.resource_definitions = resources
|
||||
|
||||
def put(self, component_id):
|
||||
return self.patch(component_id)
|
||||
|
||||
def patch(self, component_id):
|
||||
self._perform_update(component_id)
|
||||
return None, 204
|
||||
|
||||
@db.with_transaction
|
||||
def delete(self, component_id):
|
||||
component = db.Component.query.get_or_404(component_id)
|
||||
db.db.session.delete(component)
|
||||
return None, 204
|
|
@ -0,0 +1,172 @@
|
|||
# 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.library import components
|
||||
from tuning_box.tests.test_app import BaseTest
|
||||
|
||||
|
||||
class TestComponents(BaseTest):
|
||||
|
||||
@property
|
||||
def _component_json(self):
|
||||
return {
|
||||
'id': 7,
|
||||
'name': 'component1',
|
||||
'resource_definitions': [{
|
||||
'id': 5,
|
||||
'name': 'resdef1',
|
||||
'component_id': 7,
|
||||
'content': {'key': 'nsname.key'},
|
||||
}],
|
||||
}
|
||||
|
||||
def test_get_components_empty(self):
|
||||
res = self.client.get('/components')
|
||||
self.assertEqual(res.status_code, 200)
|
||||
self.assertEqual(res.json, [])
|
||||
|
||||
def test_get_components(self):
|
||||
self._fixture()
|
||||
res = self.client.get('/components')
|
||||
self.assertEqual(res.status_code, 200)
|
||||
self.assertEqual(res.json, [self._component_json])
|
||||
|
||||
def test_get_one_component(self):
|
||||
self._fixture()
|
||||
res = self.client.get('/components/7')
|
||||
self.assertEqual(res.status_code, 200)
|
||||
self.assertEqual(res.json, self._component_json)
|
||||
|
||||
def test_get_one_component_404(self):
|
||||
res = self.client.get('/components/7')
|
||||
self.assertEqual(res.status_code, 404)
|
||||
|
||||
def test_post_component(self):
|
||||
self._fixture() # Just for namespace
|
||||
json = self._component_json
|
||||
del json['id']
|
||||
del json['resource_definitions'][0]['id']
|
||||
del json['resource_definitions'][0]['component_id']
|
||||
json['name'] = 'component2'
|
||||
res = self.client.post('/components', data=json)
|
||||
self.assertEqual(res.status_code, 201)
|
||||
json['id'] = 8
|
||||
json['resource_definitions'][0]['component_id'] = json['id']
|
||||
json['resource_definitions'][0]['id'] = 6
|
||||
self.assertEqual(res.json, json)
|
||||
self._assert_db_effect(db.Component, 8, components.component_fields,
|
||||
json)
|
||||
|
||||
def test_post_component_conflict(self):
|
||||
self._fixture() # Just for namespace
|
||||
json = self._component_json
|
||||
del json['id']
|
||||
del json['resource_definitions'][0]['id']
|
||||
del json['resource_definitions'][0]['component_id']
|
||||
res = self.client.post('/components', data=json)
|
||||
self.assertEqual(res.status_code, 409)
|
||||
self._assert_not_in_db(db.Component, 8)
|
||||
|
||||
def test_post_component_conflict_propagate_exc(self):
|
||||
self.app.config["PROPAGATE_EXCEPTIONS"] = True
|
||||
self._fixture() # Just for namespace
|
||||
json = self._component_json
|
||||
del json['id']
|
||||
del json['resource_definitions'][0]['id']
|
||||
del json['resource_definitions'][0]['component_id']
|
||||
res = self.client.post('/components', data=json)
|
||||
self.assertEqual(res.status_code, 409)
|
||||
self._assert_not_in_db(db.Component, 8)
|
||||
|
||||
def test_post_component_no_resdef_content(self):
|
||||
self._fixture() # Just for namespace
|
||||
json = self._component_json
|
||||
del json['id']
|
||||
del json['resource_definitions'][0]['id']
|
||||
del json['resource_definitions'][0]['component_id']
|
||||
del json['resource_definitions'][0]['content']
|
||||
json['name'] = 'component2'
|
||||
res = self.client.post('/components', data=json)
|
||||
self.assertEqual(res.status_code, 201)
|
||||
json['id'] = 8
|
||||
json['resource_definitions'][0]['component_id'] = json['id']
|
||||
json['resource_definitions'][0]['id'] = 6
|
||||
json['resource_definitions'][0]['content'] = None
|
||||
self.assertEqual(res.json, json)
|
||||
self._assert_db_effect(db.Component, 8, components.component_fields,
|
||||
json)
|
||||
|
||||
def test_delete_component(self):
|
||||
self._fixture()
|
||||
res = self.client.delete('/components/7')
|
||||
self.assertEqual(res.status_code, 204)
|
||||
self.assertEqual(res.data, b'')
|
||||
self._assert_not_in_db(db.Component, 7)
|
||||
|
||||
def test_delete_component_404(self):
|
||||
res = self.client.delete('/components/7')
|
||||
self.assertEqual(res.status_code, 404)
|
||||
|
||||
def test_put_component_404(self):
|
||||
res = self.client.put('/components/7')
|
||||
self.assertEqual(res.status_code, 404)
|
||||
|
||||
def test_put_component(self):
|
||||
self._fixture()
|
||||
component_url = '/components/7'
|
||||
initial_data = self._component_json
|
||||
new_name = 'new_{0}'.format(initial_data['name'])
|
||||
|
||||
# Updating name
|
||||
res = self.client.put(component_url, data={'name': new_name})
|
||||
self.assertEqual(res.status_code, 204)
|
||||
actual_component = self.client.get(component_url).json
|
||||
self.assertEqual(new_name, actual_component['name'])
|
||||
self.assertEqual(initial_data['resource_definitions'],
|
||||
actual_component['resource_definitions'])
|
||||
|
||||
# Updating resource_definitions
|
||||
res = self.client.put(component_url,
|
||||
data={'resource_definitions': []})
|
||||
self.assertEqual(res.status_code, 204)
|
||||
actual_component = self.client.get(component_url).json
|
||||
self.assertEqual([], actual_component['resource_definitions'])
|
||||
|
||||
# Restoring resource_definitions and name
|
||||
res = self.client.put(
|
||||
component_url,
|
||||
data={'name': initial_data['name'],
|
||||
'resource_definitions': initial_data['resource_definitions']}
|
||||
)
|
||||
self.assertEqual(res.status_code, 204)
|
||||
actual_component = self.client.get(component_url).json
|
||||
self.assertEqual(initial_data['name'],
|
||||
actual_component['name'])
|
||||
self.assertEqual(initial_data['resource_definitions'],
|
||||
actual_component['resource_definitions'])
|
||||
|
||||
def test_put_component_ignore_changing_id(self):
|
||||
self._fixture()
|
||||
component_url = '/components/7'
|
||||
initial_data = self._component_json
|
||||
new_name = 'new_{0}'.format(initial_data['name'])
|
||||
|
||||
res = self.client.put(component_url,
|
||||
data={'name': new_name, 'id': None,
|
||||
'fake': 'xxxx'})
|
||||
self.assertEqual(res.status_code, 204)
|
||||
actual_component = self.client.get(component_url).json
|
||||
self.assertEqual(new_name, actual_component['name'])
|
||||
self.assertEqual(initial_data['resource_definitions'],
|
||||
actual_component['resource_definitions'])
|
|
@ -40,9 +40,10 @@ class Client(testing.FlaskClient):
|
|||
return super(Client, self).open(*args, **kwargs)
|
||||
|
||||
|
||||
class TestApp(base.TestCase):
|
||||
class BaseTest(base.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestApp, self).setUp()
|
||||
super(BaseTest, self).setUp()
|
||||
self.app = app.build_app(configure_logging=False,
|
||||
with_keystone=False)
|
||||
self.app.config["SQLALCHEMY_DATABASE_URI"] = 'sqlite:///'
|
||||
|
@ -72,19 +73,6 @@ class TestApp(base.TestCase):
|
|||
environment.hierarchy_levels = hierarchy_levels
|
||||
db.db.session.add(environment)
|
||||
|
||||
@property
|
||||
def _component_json(self):
|
||||
return {
|
||||
'id': 7,
|
||||
'name': 'component1',
|
||||
'resource_definitions': [{
|
||||
'id': 5,
|
||||
'name': 'resdef1',
|
||||
'component_id': 7,
|
||||
'content': {'key': 'nsname.key'},
|
||||
}],
|
||||
}
|
||||
|
||||
def _assert_db_effect(self, model, key, fields, expected):
|
||||
with self.app.app_context():
|
||||
obj = model.query.get(key)
|
||||
|
@ -97,90 +85,8 @@ class TestApp(base.TestCase):
|
|||
obj = model.query.get(key)
|
||||
self.assertIsNone(obj)
|
||||
|
||||
def test_get_components_empty(self):
|
||||
res = self.client.get('/components')
|
||||
self.assertEqual(res.status_code, 200)
|
||||
self.assertEqual(res.json, [])
|
||||
|
||||
def test_get_components(self):
|
||||
self._fixture()
|
||||
res = self.client.get('/components')
|
||||
self.assertEqual(res.status_code, 200)
|
||||
self.assertEqual(res.json, [self._component_json])
|
||||
|
||||
def test_get_one_component(self):
|
||||
self._fixture()
|
||||
res = self.client.get('/components/7')
|
||||
self.assertEqual(res.status_code, 200)
|
||||
self.assertEqual(res.json, self._component_json)
|
||||
|
||||
def test_get_one_component_404(self):
|
||||
res = self.client.get('/components/7')
|
||||
self.assertEqual(res.status_code, 404)
|
||||
|
||||
def test_post_component(self):
|
||||
self._fixture() # Just for namespace
|
||||
json = self._component_json
|
||||
del json['id']
|
||||
del json['resource_definitions'][0]['id']
|
||||
del json['resource_definitions'][0]['component_id']
|
||||
json['name'] = 'component2'
|
||||
res = self.client.post('/components', data=json)
|
||||
self.assertEqual(res.status_code, 201)
|
||||
json['id'] = 8
|
||||
json['resource_definitions'][0]['component_id'] = json['id']
|
||||
json['resource_definitions'][0]['id'] = 6
|
||||
self.assertEqual(res.json, json)
|
||||
self._assert_db_effect(db.Component, 8, app.component_fields, json)
|
||||
|
||||
def test_post_component_conflict(self):
|
||||
self._fixture() # Just for namespace
|
||||
json = self._component_json
|
||||
del json['id']
|
||||
del json['resource_definitions'][0]['id']
|
||||
del json['resource_definitions'][0]['component_id']
|
||||
res = self.client.post('/components', data=json)
|
||||
self.assertEqual(res.status_code, 409)
|
||||
self._assert_not_in_db(db.Component, 8)
|
||||
|
||||
def test_post_component_conflict_propagate_exc(self):
|
||||
self.app.config["PROPAGATE_EXCEPTIONS"] = True
|
||||
self._fixture() # Just for namespace
|
||||
json = self._component_json
|
||||
del json['id']
|
||||
del json['resource_definitions'][0]['id']
|
||||
del json['resource_definitions'][0]['component_id']
|
||||
res = self.client.post('/components', data=json)
|
||||
self.assertEqual(res.status_code, 409)
|
||||
self._assert_not_in_db(db.Component, 8)
|
||||
|
||||
def test_post_component_no_resdef_content(self):
|
||||
self._fixture() # Just for namespace
|
||||
json = self._component_json
|
||||
del json['id']
|
||||
del json['resource_definitions'][0]['id']
|
||||
del json['resource_definitions'][0]['component_id']
|
||||
del json['resource_definitions'][0]['content']
|
||||
json['name'] = 'component2'
|
||||
res = self.client.post('/components', data=json)
|
||||
self.assertEqual(res.status_code, 201)
|
||||
json['id'] = 8
|
||||
json['resource_definitions'][0]['component_id'] = json['id']
|
||||
json['resource_definitions'][0]['id'] = 6
|
||||
json['resource_definitions'][0]['content'] = None
|
||||
self.assertEqual(res.json, json)
|
||||
self._assert_db_effect(db.Component, 8, app.component_fields, json)
|
||||
|
||||
def test_delete_component(self):
|
||||
self._fixture()
|
||||
res = self.client.delete('/components/7')
|
||||
self.assertEqual(res.status_code, 204)
|
||||
self.assertEqual(res.data, b'')
|
||||
self._assert_not_in_db(db.Component, 7)
|
||||
|
||||
def test_delete_component_404(self):
|
||||
res = self.client.delete('/components/7')
|
||||
self.assertEqual(res.status_code, 404)
|
||||
class TestApp(BaseTest):
|
||||
|
||||
def test_get_environments_empty(self):
|
||||
res = self.client.get('/environments')
|
||||
|
|
Loading…
Reference in New Issue