cmis: add object type and property support (#39416)

This commit is contained in:
Valentin Deniaud 2020-02-05 16:23:04 +01:00
parent af5fa68c06
commit 45428e944a
2 changed files with 71 additions and 5 deletions

View File

@ -25,6 +25,7 @@ from cmislib.exceptions import CmisException
from cmislib.exceptions import ObjectNotFoundException
from cmislib.exceptions import PermissionDeniedException
from cmislib.exceptions import UpdateConflictException
from cmislib.exceptions import InvalidArgumentException
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.utils.six import BytesIO
@ -63,8 +64,14 @@ UPLOAD_SCHEMA = {
'type': 'string',
'pattern': FILE_PATH_PATTERN,
},
'object_type': {'type': 'string'},
'properties': {
'type': 'object',
'additionalProperties': {'type': 'string'}
},
},
'required': ['file', 'path']
'required': ['file', 'path'],
'unflatten': True,
}
@ -103,7 +110,10 @@ class CmisConnector(BaseResource):
doc = cmis_gateway.create_doc(
filename,
data['path'],
data['file_byte_content'])
data['file_byte_content'],
object_type=data.get('object_type'),
properties=data.get('properties'),
)
return {'data': {'properties': doc.properties}}
def _validate_inputs(self, data):
@ -119,6 +129,10 @@ class CmisConnector(BaseResource):
except (TypeError, binascii.Error):
return True, '"file[\'content\']" must be a valid base64 string', None
if 'properties' in data and 'object_type' not in data:
if any(prop for prop in data['properties'] if not prop.startswith('cmis:')):
return True, 'Properties other than cmis: require object_type to be set', None
return False, '', data
@ -134,6 +148,8 @@ def wrap_cmis_error(f):
raise APIError("permission denied: %s" % e)
except UpdateConflictException as e:
raise APIError("update conflict: %s" % e)
except InvalidArgumentException as e:
raise APIError("invalid property name: %s" % e)
except CmisException as e:
raise APIError("cmis binding error: %s" % e)
return wrapper
@ -169,6 +185,11 @@ class CMISGateway(object):
return folder
@wrap_cmis_error
def create_doc(self, file_name, file_path, file_byte_content):
def create_doc(self, file_name, file_path, file_byte_content,
object_type=None, properties=None):
folder = self._get_or_create_folder(file_path)
return folder.createDocument(file_name, contentFile=BytesIO(file_byte_content))
properties = properties or {}
if object_type:
properties['cmis:objectTypeId'] = object_type
return folder.createDocument(file_name, contentFile=BytesIO(file_byte_content),
properties=properties)

View File

@ -7,6 +7,7 @@ from cmislib.exceptions import CmisException
from cmislib.exceptions import ObjectNotFoundException
from cmislib.exceptions import PermissionDeniedException
from cmislib.exceptions import UpdateConflictException
from cmislib.exceptions import InvalidArgumentException
from django.contrib.contenttypes.models import ContentType
from django.utils.encoding import force_bytes, force_text
from django.utils.six.moves.urllib import error as urllib2
@ -41,7 +42,8 @@ def test_uploadfile(app, setup, tmpdir, monkeypatch):
def __init__(self, *args, **kwargs):
pass
def create_doc(self, file_name, file_path, file_byte_content):
def create_doc(self, file_name, file_path, file_byte_content,
object_type=None, properties=None):
with open(file_name, 'wb') as f:
f.write(file_byte_content)
return Mock(properties={"toto": "tata"})
@ -82,6 +84,34 @@ def test_uploadfile(app, setup, tmpdir, monkeypatch):
assert json_result['data']['properties'] == {"toto": "tata"}
def test_upload_file_metadata(app, setup, monkeypatch):
class FakeFolder:
def createDocument(self, filename, contentFile, properties):
return Mock(properties=properties)
from passerelle.apps.cmis.models import CMISGateway
monkeypatch.setattr(CMISGateway, '_get_or_create_folder', lambda x, y: FakeFolder())
response = app.post_json(
'/cmis/slug-cmis/uploadfile',
params={"path": "/some/folder/structure",
"file": {"filename": "bla",
"content": b64encode('bla')},
"object_type": "D:dui:type",
"properties": {
"cmis:description": "Coucou",
"dui:tnumDossier": "42",
},
"properties/dui:ttypeStructure": "Accueil de loisirs",
})
assert response.json['data']['properties'] == {
"cmis:objectTypeId": "D:dui:type",
"cmis:description": "Coucou",
"dui:tnumDossier": "42",
"dui:ttypeStructure": "Accueil de loisirs",
}
def test_uploadfile_error_if_no_file_name(app, setup):
response = app.post_json(
'/cmis/slug-cmis/uploadfile',
@ -208,6 +238,20 @@ def test_uploadfile_error_if_no_proper_base64_encoding(app, setup):
assert response.json['err_desc'].startswith('"file[\'content\']" must be a valid base64 string')
def test_upload_file_error_metadata(app, setup):
response = app.post_json(
'/cmis/slug-cmis/uploadfile',
params={"path": "/some/folder/structure",
"file": {"filename": "bla",
"content": b64encode('bla')},
"properties": {"dui:tnumDossier": "42"}},
expect_errors=True)
assert response.status_code == 400
assert response.json['err'] == 1
assert 'object_type' in response.json['err_desc']
def test_uploadfile_cmis_gateway_error(app, setup, monkeypatch):
from passerelle.utils.jsonresponse import APIError
cmis_gateway = Mock()
@ -315,6 +359,7 @@ def test_create_doc():
(urllib2.URLError, "connection error"),
(PermissionDeniedException, "permission denied"),
(UpdateConflictException, "update conflict"),
(InvalidArgumentException, "invalid property"),
(CmisException, "cmis binding error")
])
def test_wrap_cmis_error(app, setup, monkeypatch, cmis_exc, err_msg):