Allow limited PATCHing of shots from Blender and Web.

Soon PATCH should become the only way in which shots are edited, so
we should possibly remove PUT permissions.
This commit is contained in:
2016-09-23 17:17:07 +02:00
parent 56567532ff
commit 13c67e3ab8
2 changed files with 235 additions and 4 deletions

View File

@@ -1,17 +1,38 @@
"""Shot management."""
import collections
import logging
import attr
import flask
import flask_login
from eve.methods.put import put_internal
from werkzeug import exceptions as wz_exceptions
import pillarsdk
import pillar.api.utils
from pillar.web.system_util import pillar_api
from pillar.api.nodes.custom import register_patch_handler
from attract import attrs_extra
from attract.node_types import node_type_shot, node_type_task
# From patch operation name to fields that operation may edit.
VALID_PATCH_OPERATIONS = {
u'from-blender': {
u'name',
u'properties.trim_start_in_frames',
u'properties.duration_in_edit_in_frames',
u'properties.cut_in_timeline_in_frames',
u'properties.status',
},
u'from-web': {
u'properties.status',
u'properties.notes',
},
}
log = logging.getLogger(__name__)
@attr.s
class ShotManager(object):
@@ -23,14 +44,17 @@ class ShotManager(object):
:rtype: pillarsdk.Node
"""
project_id = project['_id']
self._log.info('Creating shot for project %s', project_id)
api = pillar_api()
node_type = project.get_node_type(node_type_shot['name'])
if not node_type:
raise ValueError('Project %s not set up for Attract' % project._id)
raise ValueError('Project %s not set up for Attract' % project_id)
node_props = dict(
name='New shot',
project=project['_id'],
project=project_id,
user=flask_login.current_user.objectid,
node_type=node_type['name'],
properties={
@@ -106,3 +130,66 @@ class ShotManager(object):
shot.update(api=api)
return shot
def node_setattr(node, key, value):
"""Sets a node property by dotted key.
Modifies the node in-place.
"""
set_on = node
while key and '.' in key:
head, key = key.split('.', 1)
set_on = node[head]
set_on[key] = value
@register_patch_handler(node_type_shot['name'])
def patch_shot(node_id, patch):
assert_is_valid_patch(patch)
# Find the full node, so we can PUT it through Eve for validation.
nodes_coll = flask.current_app.data.driver.db['nodes']
node_query = {'_id': node_id,
'node_type': node_type_shot['name']}
node = nodes_coll.find_one(node_query)
if node is None:
log.warning('How can node %s not be found?', node_id)
raise wz_exceptions.NotFound('Node %s not found' % node_id)
# Set the fields
log.info('Patching node %s: %s', node_id, patch)
for key, value in patch['$set'].items():
node_setattr(node, key, value)
node = pillar.api.utils.remove_private_keys(node)
r, _, _, status = put_internal('nodes', node, _id=node_id)
return pillar.api.utils.jsonify(r, status=status)
def assert_is_valid_patch(patch):
"""Raises an exception when the patch isn't valid."""
try:
op = patch['op']
except KeyError:
raise wz_exceptions.BadRequest("PATCH should have a key 'op' indicating the operation.")
try:
allowed_fields = VALID_PATCH_OPERATIONS[op]
except KeyError:
valid_ops = u', '.join(VALID_PATCH_OPERATIONS.keys())
raise wz_exceptions.BadRequest(u'Operation should be one of %s' % valid_ops)
try:
fields = set(patch['$set'].keys())
except KeyError:
raise wz_exceptions.BadRequest("PATCH should have a key '$set' "
"indicating the fields to set.")
disallowed_fields = fields - allowed_fields
if disallowed_fields:
raise wz_exceptions.BadRequest(u"Operation '%s' does not allow you to set fields %s" % (
op, disallowed_fields
))