Enables Row Model to Update All Rows by HTTP Request

PushedDataSourceDriver has to receive http request to update its
rows. For the updating, the driver uses existing web server for
API request.

The update requests calls API row model and the model call rpc to
PushedDriver to update row data.

Implement blueprint: push-type-datasource-driver
Change-Id: I440e6c6fe444af68ff4f728873fbced1d30450dc
This commit is contained in:
Masahito Muroi 2016-01-12 18:03:30 +09:00
parent b8df453df8
commit e43c68524b
7 changed files with 118 additions and 4 deletions

View File

@ -122,7 +122,7 @@ class APIRouterV1(object):
rows_path = "%s/rows" % table_path
row_collection_handler = webservice.CollectionHandler(
rows_path,
table_rows)
table_rows, allow_update=True)
resource_mgr.register_handler(row_collection_handler)
row_path = "%s/(?P<row_id>[^/]+)" % rows_path
row_element_handler = webservice.ElementHandler(row_path, table_rows)

View File

@ -101,6 +101,37 @@ class RowModel(deepsix.deepSix):
else:
return {'results': result}
def update_items(self, items, params, context=None):
"""Updates all data in a table.
Args:
id_: A table id for updating all row
items: A data for new rows
params: A dict-like object containing parameters from
request query
context: Key-values providing frame of reference of request
Returns: None
Raises:
KeyError: table id doesn't exist
DataModelException: any error occurs during replacing rows.
"""
LOG.info("update_items(context=%s)" % context)
caller, source_id = api_utils.get_id_from_context(context,
self.datasource_mgr,
self.engine)
table_id = context['table_id']
try:
self.rpc(caller, 'update_entire_data',
table_id, source_id, items)
except exception.CongressException as e:
m = ("Error occurred while processing updating rows for "
"source_id '%s' and table_id '%s'" % (source_id, table_id))
LOG.exception(m)
raise webservice.DataModelException.create(e)
LOG.info("finish update_items(context=%s)" % context)
LOG.debug("updated table %s with row items: %s" %
(table_id, str(items)))
# TODO(thinrichs): It makes sense to sometimes allow users to create
# a new row for internal data sources. But since we don't have
# those yet all tuples are read-only from the API.

View File

@ -341,7 +341,8 @@ class CollectionHandler(AbstractApiHandler):
"""
def __init__(self, path_regex, model,
allow_named_create=True, allow_list=True, allow_create=True):
allow_named_create=True, allow_list=True, allow_create=True,
allow_update=False):
"""Initialize a collection handler.
Args:
@ -356,6 +357,7 @@ class CollectionHandler(AbstractApiHandler):
self.allow_named_create = allow_named_create
self.allow_list = allow_list
self.allow_create = allow_create
self.allow_update = allow_update
def handle_request(self, request):
"""Handle a REST request.
@ -391,6 +393,8 @@ class CollectionHandler(AbstractApiHandler):
return self.list_members(request)
elif request.method == 'POST' and self.allow_create:
return self.create_member(request)
elif request.method == 'PUT' and self.allow_update:
return self.update_members(request)
return NOT_SUPPORTED_RESPONSE
def _get_action_type(self, method):
@ -441,6 +445,21 @@ class CollectionHandler(AbstractApiHandler):
content_type='application/json',
location="%s/%s" % (request.path, id_))
def update_members(self, request):
if not hasattr(self.model, 'update_items'):
return NOT_SUPPORTED_RESPONSE
items = self._parse_json_body(request)
context = self._get_context(request)
try:
self.model.update_items(items, request.params, context)
except KeyError as e:
LOG.exception("Error occured")
return error_response(httplib.BAD_REQUEST, httplib.BAD_REQUEST,
e.message or
'Update %s Failed' % context['table_id'])
return webob.Response(body="", status=httplib.OK,
content_type='application/json')
class SimpleDataModel(object):
"""A container providing access to a single type of data."""
@ -555,3 +574,15 @@ class SimpleDataModel(object):
ret = self.items.setdefault(cstr, {})[id_]
del self.items[cstr][id_]
return ret
def update_items(self, items, params, context=None):
"""Update items in the model.
Args:
items: A dict-like object containing new data
params: A dict-like object containing parameters
context: Key-values providing frame of reference of request
Returns:
None.
"""
self.items = items

View File

@ -214,7 +214,14 @@ class DataSourceManager(object):
return datasource_obj.get_row_data(table_id)
@classmethod
def get_tablename(cls, table_id, source_id):
def update_entire_data(cls, table_id, source_id, objs):
datasource = cls.get_datasource(source_id)
cage = d6cage.d6Cage()
datasource_obj = cage.service_object(datasource['name'])
return datasource_obj.update_entire_data(table_id, objs)
@classmethod
def get_tablename(cls, source_id, table_id):
obj = cls.load_module_object(source_id)
if obj:
return obj.get_tablename(table_id)

View File

@ -46,7 +46,9 @@ class TestRowModel(base.SqlTestCase):
'username': 'foo',
'password': 'password',
'tenant_name': 'foo'}
self.datasource = self.datasource_mgr.add_datasource(req)
self.datasource_mgr.add_datasource(req)
self.datasource = self.cage.getservice(name='fake_datasource',
type_='datasource_driver')
self.engine = self.cage.service_object('engine')
self.api_rule = self.cage.service_object('api-rule')
self.row_model = row_model.RowModel("row_model", {},
@ -104,3 +106,29 @@ class TestRowModel(base.SqlTestCase):
self.assertRaises(webservice.DataModelException,
self.row_model.get_items, {}, context)
def test_update_items(self):
context = {'ds_id': self.datasource['id'],
'table_id': 'fake_table'}
objs = [
{"id": 'id-1', "name": 'name-1'},
{"id": 'id-2', "name": 'name-2'}
]
expected_state = (('id-1', 'name-1'), ('id-2', 'name-2'))
self.row_model.update_items(objs, {}, context=context)
table_row = self.datasource['object'].state['fake_table']
self.assertEqual(len(expected_state), len(table_row))
for row in expected_state:
self.assertTrue(row in table_row)
def test_update_items_invalid_table(self):
context = {'ds_id': self.datasource['id'],
'table_id': 'invalid-table'}
objs = [
{"id": 'id-1', "name": 'name-1'},
{"id": 'id-2', "name": 'name-2'}
]
self.assertRaises(webservice.DataModelException,
self.row_model.update_items, objs, {}, context)

View File

@ -313,3 +313,19 @@ class TestCollectionHandler(base.TestCase):
self.assertEqual(expected_body, response.body)
self.assertEqual('application/json', response.content_type)
self.assertEqual(str(httplib.OK) + " OK", response.status)
def test_update_members(self):
collection_handler = webservice.CollectionHandler(r'/', '')
collection_handler.model = webservice.SimpleDataModel('test')
request = webob.Request.blank('/')
request.content_type = 'application/json'
request.body = '{"key1": "value1", "key2": "value2"}'.encode('utf-8')
response = collection_handler.update_members(request)
self.assertEqual('application/json', response.content_type)
self.assertEqual(str(httplib.OK) + " OK", response.status)
expected_items = {
"key1": "value1",
"key2": "value2",
}
self.assertEqual(expected_items, collection_handler.model.items)

View File

@ -31,6 +31,7 @@ def d6service(name, keys, inbox, datapath, args):
class FakeDataSource(datasource_driver.PollingDataSourceDriver,
datasource_driver.PushedDataSourceDriver,
datasource_driver.ExecutionDriver):
value_trans = {'translation-type': 'VALUE'}