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:
parent
b8df453df8
commit
e43c68524b
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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'}
|
||||
|
|
Loading…
Reference in New Issue