esup_signature: add new-with-workflow endpoint (#77670) #251

Merged
ecazenave merged 3 commits from wip/77670-esup-workflow into main 2023-05-30 14:35:13 +02:00
2 changed files with 270 additions and 14 deletions

View File

@ -35,7 +35,7 @@ SIGN_REQUEST_SCHEMA = {
'title': '',
'description': '',
'type': 'object',
'required': ['file', 'recipients_emails', 'eppn'],
'required': ['file', 'recipients_emails', 'create_by_eppn'],
'unflatten': True,
'properties': collections.OrderedDict(
{
@ -62,13 +62,120 @@ SIGN_REQUEST_SCHEMA = {
'description': 'Recipients emails',
'items': {'type': 'string'},
},
'eppn': {'type': 'string', 'description': 'EPPN of the sign request owner'},
'recipients_cc_emails': {
'type': 'array',
'description': 'Recipients CC emails',
'items': {'type': 'string'},
},
'all_sign_to_complete': {
'type': 'string',
'description': 'Every recipient has to sign',
'enum': ['true', 'false'],
'default': 'false',
},
'user_sign_first': {
'type': 'string',
'description': 'the author must sign first',
'enum': ['true', 'false'],
'default': 'false',
},
'pending': {
'type': 'string',
'description': 'Pending',
'enum': ['true', 'false'],
'default': 'true',
},
'force_all_sign': {
'type': 'string',
'description': 'Force signing on every document',
'enum': ['true', 'false'],
'default': 'false',
},
'comment': {'type': 'string', 'description': 'Comment'},
'sign_type': {
'type': 'string',
'description': 'Signature type',
'enum': ['visa', 'pdfImageStamp', 'certSign', 'nexuSign'],
'default': 'pdfImageStamp',
},
'create_by_eppn': {'type': 'string', 'description': 'EPPN of the sign request owner'},
'title': {'type': 'string', 'description': 'Title'},
'target_url': {
'type': 'string',
'description': 'End location',
},
}
),
}
SIGN_REQUEST_WITH_WORKFLOW_SCHEMA = {
'$schema': 'http://json-schema.org/draft-04/schema#',
'title': '',
'description': '',
'type': 'object',
'required': ['file', 'eppn', 'workflow_id'],
'unflatten': True,
'properties': collections.OrderedDict(
{
'file': {
'type': 'object',
'description': 'File object',
'required': ['filename', 'content_type', 'content'],
'properties': {
'filename': {
'type': 'string',
},
'content_type': {
'type': 'string',
'description': 'MIME content-type',
},
'content': {
'type': 'string',
'description': 'Content, base64 encoded',
},
},
},
'recipients_emails': {
'type': 'array',
'description': 'Recipients emails at each step',
'items': {'type': 'string'},
},
'target_emails': {
'type': 'array',
'description': 'Target emails',
'items': {'type': 'string'},
},
'all_sign_to_completes': {
'type': 'array',
'description': 'Steps numbers were every recipient has to sign',
'items': {'type': 'string'},
},
'eppn': {'type': 'string', 'description': 'EPPN of the sign request owner'},
'workflow_id': {'type': 'string', 'description': 'Identifier of the workflow'},
'title': {'type': 'string', 'description': 'Title'},
'target_urls': {
'type': 'array',
'description': 'End locations',
'items': {'type': 'string'},
},
'signrequest_params_jsonstring': {
'type': 'string',
'description': 'Signature parameters',
},
}
),
}
def clean_list(some_list):
return [elem for elem in some_list if elem]
def to_bool(some_str):
return some_str == 'true'
class EsupSignature(BaseResource, HTTPResource):
base_url = models.URLField(_('API URL'))
@ -77,12 +184,12 @@ class EsupSignature(BaseResource, HTTPResource):
class Meta:
verbose_name = _('Esup Signature')
def _call(self, path, method='get', data=None, files=None, expect_json=True):
def _call(self, path, method='get', params=None, files=None, expect_json=True):
url = urllib.parse.urljoin(self.base_url, path)
kwargs = {}
if method == 'post':
kwargs['data'] = data
kwargs['params'] = params
kwargs['files'] = files
try:
@ -125,8 +232,18 @@ class EsupSignature(BaseResource, HTTPResource):
'recipients_emails/0': 'xx@foo.com',
'recipients_emails/1': 'yy@foo.com',
'recipients_emails/2': 'zz@foo.com',
'eppn': 'aa@foo.com',
'recipients_cc_emails/0': 'xx@foo.com',
'recipients_cc_emails/1': 'yy@foo.com',
'recipients_cc_emails/2': 'zz@foo.com',
'all_sign_to_complete': 'true',
'user_sign_first': 'false',
'pending': 'true',
'force_all_sign': 'false',
'comment': 'a comment',
'sign_type': 'pdfImageStamp',
'create_by_eppn': 'aa@foo.com',
'title': 'a title',
'target_url': 'smb://foo.bar/location-1/',
},
},
)
@ -143,16 +260,92 @@ class EsupSignature(BaseResource, HTTPResource):
)
}
recipients_emails = [email for email in post_data['recipients_emails'] if email]
data = {
'signType': 'pdfImageStamp',
'recipientsEmails': recipients_emails,
'eppn': post_data['eppn'],
params = {
'recipientsEmails': clean_list(post_data['recipients_emails']),
'recipientsCCEmails': clean_list(post_data.get('recipients_cc_emails', [])),
'comment': post_data.get('comment', ''),
'signType': post_data.get('sign_type', 'pdfImageStamp'),
'createByEppn': post_data['create_by_eppn'],
'title': post_data.get('title', ''),
'pending': True,
'targetUrl': post_data.get('target_url', ''),
}
return {'data': self._call('ws/signrequests/new', method='post', data=data, files=files)}
bool_params = {
'all_sign_to_complete': ('allSignToComplete', False),
'user_sign_first': ('userSignFirst', False),
'pending': ('pending', True),
'force_all_sign': ('forceAllSign', False),
}
for key, value in bool_params.items():
ext_param, default = value
params[ext_param] = default
if key in post_data:
params[ext_param] = to_bool(post_data[key])
return {'data': self._call('ws/signrequests/new', method='post', params=params, files=files)}
@endpoint(
name='new-with-workflow',
description=_('Create a sign request'),
perm='can_access',
post={
'request_body': {
'schema': {
'application/json': SIGN_REQUEST_WITH_WORKFLOW_SCHEMA,
}
},
'input_example': {
'file': {
'filename': 'example-1.pdf',
'content_type': 'application/pdf',
'content': 'JVBERi0xL...(base64 PDF)...',
},
'workflow_id': '99',
'eppn': 'aa@foo.com',
'title': 'a title',
'recipients_emails/0': '0*xx@foo.com',
'recipients_emails/1': '0*yy@foo.com',
'recipients_emails/2': '1*zz@foo.com',
'all_sign_to_completes/0': '12',
'all_sign_to_completes/1': '13',
'target_emails/0': 'xx@foo.com',
'target_emails/1': 'yy@foo.com',
'target_emails/2': 'zz@foo.com',
'signrequest_params_jsonstring': 'List [ OrderedMap { "xPos": 100, "yPos": 100, "signPageNumber": 1 }, '
'OrderedMap { "xPos": 200, "yPos": 200, "signPageNumber": 1 } ]',
'target_urls/0': 'smb://foo.bar/location-1/',
'target_urls/1': 'smb://foo.bar/location-2/',
},
},
)
def new_with_workflow(self, request, post_data):
try:
file_bytes = io.BytesIO(base64.b64decode(post_data['file']['content']))
except (TypeError, binascii.Error):
raise APIError("Can't decode file")
files = {
'multipartFiles': (
post_data['file']['filename'],
file_bytes,
post_data['file']['content_type'],
)
}
params = {
'createByEppn': post_data['eppn'],
'title': post_data.get('title', ''),
'recipientsEmails': clean_list(post_data.get('recipients_emails', [])),
'allSignToCompletes': clean_list(post_data.get('all_sign_to_completes', [])),
'targetEmails': clean_list(post_data.get('target_emails', [])),
'signRequestParamsJsonString': post_data.get('signrequest_params_jsonstring', ''),
'targetUrls': clean_list(post_data.get('target_urls', [])),
}
return {
'data': self._call(
'/ws/workflows/%s/new' % post_data['workflow_id'], method='post', params=params, files=files
)
}
@endpoint(
methods=['get'],

View File

@ -31,11 +31,26 @@ def test_new(app, connector):
},
'recipients_emails/0': 'foo@invalid',
'recipients_emails/1': 'bar@invalid',
'eppn': 'baz@invalid',
'create_by_eppn': 'baz@invalid',
'title': 'a title',
}
with responses.RequestsMock() as rsps:
rsps.post('https://esup-signature.invalid/ws/signrequests/new', status=200, json=9)
query_params = {
'recipientsEmails': ['foo@invalid', 'bar@invalid'],
'createByEppn': 'baz@invalid',
'title': 'a title',
'signType': 'pdfImageStamp',
'pending': True,
'allSignToComplete': False,
'userSignFirst': False,
'forceAllSign': False,
}
rsps.post(
'https://esup-signature.invalid/ws/signrequests/new',
match=[responses.matchers.query_param_matcher(query_params)],
status=200,
json=9,
)
resp = app.post_json('/esup-signature/esup-signature/new', params=params)
assert len(rsps.calls) == 1
assert rsps.calls[0].request.headers['Content-Type'].startswith('multipart/form-data')
@ -44,6 +59,54 @@ def test_new(app, connector):
assert json_resp['data'] == 9
def test_new_with_workflow(app, connector):
params = {
'file': {
'filename': 'bla',
'content': base64.b64encode(b'who what').decode(),
'content_type': 'text/plain',
},
'workflow_id': '99',
'eppn': 'aa@foo.com',
'title': 'a title',
'recipients_emails/0': '0*xx@foo.com',
'recipients_emails/1': '0*yy@foo.com',
'recipients_emails/2': '1*zz@foo.com',
'all_sign_to_completes/0': '12',
'all_sign_to_completes/1': '13',
'target_emails/0': 'xx@foo.com',
'target_emails/1': 'yy@foo.com',
'target_emails/2': 'zz@foo.com',
'signrequest_params_jsonstring': 'List [ OrderedMap { "xPos": 100, "yPos": 100, "signPageNumber": 1 }, '
'OrderedMap { "xPos": 200, "yPos": 200, "signPageNumber": 1 } ]',
'target_urls/0': 'smb://foo.bar/location-1/',
'target_urls/1': 'smb://foo.bar/location-2/',
}
with responses.RequestsMock() as rsps:
query_params = {
'createByEppn': 'aa@foo.com',
'title': 'a title',
'recipientsEmails': ['0*xx@foo.com', '0*yy@foo.com', '1*zz@foo.com'],
'allSignToCompletes': ['12', '13'],
'targetEmails': ['xx@foo.com', 'yy@foo.com', 'zz@foo.com'],
'signRequestParamsJsonString': 'List [ OrderedMap { "xPos": 100, "yPos": 100, "signPageNumber": 1 }, '
'OrderedMap { "xPos": 200, "yPos": 200, "signPageNumber": 1 } ]',
'targetUrls': ['smb://foo.bar/location-1/', 'smb://foo.bar/location-2/'],
}
rsps.post(
'https://esup-signature.invalid/ws/workflows/99/new',
match=[responses.matchers.query_param_matcher(query_params)],
status=200,
json=9,
)
resp = app.post_json('/esup-signature/esup-signature/new-with-workflow', params=params)
assert len(rsps.calls) == 1
assert rsps.calls[0].request.headers['Content-Type'].startswith('multipart/form-data')
json_resp = resp.json
assert json_resp['err'] == 0
assert json_resp['data'] == 9
def test_status(app, connector):
with responses.RequestsMock() as rsps:
rsps.get('https://esup-signature.invalid/ws/signrequests/1', status=200, json={'status': 'completed'})