Skip to content
Snippets Groups Projects
Commit 540fd1cb authored by Megan Henning's avatar Megan Henning
Browse files

Add replace functionality to api

parent 3d18253e
No related branches found
No related tags found
No related merge requests found
......@@ -15,7 +15,7 @@ def default_container(handler, container=None, target_parent_container=None):
on the container before actually executing this method.
"""
def g(exec_op):
def f(method, _id=None, payload=None, recursive=False):
def f(method, _id=None, payload=None, recursive=False, r_payload=None, replace_metadata=False):
projection = None
if method == 'GET' and container.get('public', False):
has_access = True
......
......@@ -25,7 +25,8 @@ class ContainerStorage(object):
return self._get_el(_id)
def exec_op(self, action, _id=None, payload=None, query=None, user=None,
public=False, projection=None, recursive=False):
public=False, projection=None, recursive=False, r_payload=None,
replace_metadata=True):
"""
Generic method to exec an operation.
The request is dispatched to the corresponding private methods.
......@@ -40,7 +41,7 @@ class ContainerStorage(object):
if action == 'DELETE':
return self._delete_el(_id)
if action == 'PUT':
return self._update_el(_id, payload, recursive)
return self._update_el(_id, payload, recursive, r_payload, replace_metadata)
if action == 'POST':
return self._create_el(payload)
raise ValueError('action should be one of GET, POST, PUT, DELETE')
......@@ -49,17 +50,28 @@ class ContainerStorage(object):
log.debug(payload)
return self.dbc.insert_one(payload)
def _update_el(self, _id, payload, recursive=False):
def _update_el(self, _id, payload, recursive=False, r_payload=None, replace_metadata=False):
replace = None
if replace_metadata:
replace = {}
if payload.get('metadata') is not None:
replace['metadata'] = util.mongo_sanitize_fields(payload.pop('metadata'))
if payload.get('subject') is not None and payload['subject'].get('metadata') is not None:
replace['subject.metadata'] = util.mongo_sanitize_fields(payload['subject'].pop('metadata'))
update = {
'$set': util.mongo_dict(payload)
}
if replace is not None:
update['$set'].update(replace)
if self.use_object_id:
try:
_id = bson.objectid.ObjectId(_id)
except bson.errors.InvalidId as e:
raise APIStorageException(e.message)
if recursive:
self._propagate_changes(_id, update)
if recursive and r_payload is not None:
self._propagate_changes(_id, {'$set': util.mongo_dict(r_payload)})
return self.dbc.update_one({'_id': _id}, update)
def _propagate_changes(self, _id, update):
......
......@@ -305,13 +305,16 @@ class ContainerHandler(base.RequestHandler):
payload = self.request.json_body
payload_validator(payload, 'PUT')
# Check if any payload keys are a propogated property, ensure they all are
# Check if any payload keys are any propogated property, add to r_payload
rec = False
if set(payload.keys()).intersection(set(self.config.get('propagated_properties', []))):
if not set(payload.keys()).issubset(set(self.config['propagated_properties'])):
self.abort(400, 'Cannot update propagated properties together with unpropagated properties')
else:
rec = True # Mark PUT request for propogation
r_payload = {}
prop_keys = set(payload.keys()).intersection(set(self.config.get('propagated_properties', [])))
if prop_keys:
rec = True
for key in prop_keys:
r_payload[key] = payload[key]
# Check if we are updating the parent container of the element (ie we are moving the container)
# In this case, we will check permissions on it.
target_parent_container, parent_id_property = self._get_parent_container(payload)
......@@ -331,10 +334,14 @@ class ContainerHandler(base.RequestHandler):
# Ensure subject id is a bson object
payload['subject']['_id'] = bson.ObjectId(str(payload['subject']['_id']))
permchecker = self._get_permchecker(container, target_parent_container)
# Specifies wether the metadata fields should be replaced or patched with payload value
replace_metadata = self.get_param('replace_metadata', default=False)
try:
# This line exec the actual request validating the payload that will update the container
# and checking permissions using respectively the two decorators, mongo_validator and permchecker
result = mongo_validator(permchecker(self.storage.exec_op))('PUT', _id=_id, payload=payload, recursive=rec)
result = mongo_validator(permchecker(self.storage.exec_op))('PUT',
_id=_id, payload=payload, recursive=rec, r_payload=r_payload, replace_metadata=replace_metadata)
except APIStorageException as e:
self.abort(400, e.message)
......
......@@ -48,6 +48,26 @@ def mongo_dict(d):
)
return dict(_mongo_list(d))
def mongo_sanitize_fields(d):
"""
Sanitize keys of arbitrarily structured map without flattening into dot notation
Adapted from http://stackoverflow.com/questions/8429318/how-to-use-dot-in-field-name
"""
if isinstance(d, dict):
return {mongo_sanitize_fields(str(key)): value if isinstance(value, str) else mongo_sanitize_fields(value) for key,value in d.iteritems()}
elif isinstance(d, list):
return [mongo_sanitize_fields(element) for element in i]
elif isinstance(d, str):
# not allowing dots nor dollar signs in fieldnames
d = d.replace('.','_')
d = d.replace('$', '-')
return d
else:
return d
def user_perm(permissions, _id, site=None):
for perm in permissions:
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment