Skip to content

Commit

Permalink
feat: add create sign request function with different required parame…
Browse files Browse the repository at this point in the history
…ters (#878)
  • Loading branch information
mwwoda authored Jul 10, 2024
1 parent 3026d2a commit d972f54
Show file tree
Hide file tree
Showing 5 changed files with 187 additions and 51 deletions.
164 changes: 125 additions & 39 deletions boxsdk/client/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -1512,7 +1512,68 @@ def sign_request(self, sign_request_id: str) -> 'SignRequest':
"""
return self.translator.get('sign_request')(session=self._session, object_id=sign_request_id)

# pylint: disable=too-many-branches
def __create_sign_request(
self,
signers: Iterable,
files: Optional[Iterable] = None,
parent_folder_id: Optional[str] = None,
prefill_tags: Optional[Iterable] = None,
are_reminders_enabled: Optional[bool] = None,
are_text_signatures_enabled: Optional[bool] = None,
days_valid: Optional[str] = None,
email_message: Optional[Iterable] = None,
email_subject: Optional[str] = None,
external_id: Optional[str] = None,
is_document_preparation_needed: Optional[bool] = None,
redirect_url: Optional[str] = None,
declined_redirect_url: Optional[str] = None,
template_id: Optional[str] = None) -> 'SignRequest':
url = self._session.get_url('sign_requests')

body = {
'signers': signers,
}

if files:
body['source_files'] = files
if parent_folder_id:
body['parent_folder'] = {
'id': parent_folder_id,
'type': 'folder'
}
if prefill_tags:
body['prefill_tags'] = prefill_tags
if are_reminders_enabled:
body['are_reminders_enabled'] = are_reminders_enabled
if are_text_signatures_enabled:
body['are_text_signatures_enabled'] = are_text_signatures_enabled
if days_valid:
body['days_valid'] = days_valid
if email_message:
body['email_message'] = email_message
if email_subject:
body['email_subject'] = email_subject
if external_id:
body['external_id'] = external_id
if is_document_preparation_needed:
body['is_document_preparation_needed'] = is_document_preparation_needed
if redirect_url:
body['redirect_url'] = redirect_url
if declined_redirect_url:
body['declined_redirect_url'] = declined_redirect_url
if template_id:
body['template_id'] = template_id

box_response = self._session.post(url, data=json.dumps(body))
response = box_response.json()
return self.translator.translate(
session=self._session,
response_object=response,
)

@api_call
@deprecated('Use create_sign_request_v2 instead')
def create_sign_request(
self,
files: Iterable,
Expand All @@ -1529,7 +1590,7 @@ def create_sign_request(
redirect_url: Optional[str] = None,
declined_redirect_url: Optional[str] = None,
template_id: Optional[str] = None,
) -> dict:
) -> 'SignRequest':
"""
Used to create a new sign request.
Expand Down Expand Up @@ -1570,46 +1631,71 @@ def create_sign_request(
:returns:
A dictionary representing a created SignRequest
"""
url = self._session.get_url('sign_requests')
return self.__create_sign_request(
signers, files, parent_folder_id, prefill_tags, are_reminders_enabled, are_text_signatures_enabled, days_valid, email_message,
email_subject, external_id, is_document_preparation_needed, redirect_url, declined_redirect_url, template_id)

body = {
'source_files': files,
'signers': signers,
'parent_folder': {
'id': parent_folder_id,
'type': 'folder'
}
}

if prefill_tags:
body['prefill_tags'] = prefill_tags
if are_reminders_enabled:
body['are_reminders_enabled'] = are_reminders_enabled
if are_text_signatures_enabled:
body['are_text_signatures_enabled'] = are_text_signatures_enabled
if days_valid:
body['days_valid'] = days_valid
if email_message:
body['email_message'] = email_message
if email_subject:
body['email_subject'] = email_subject
if external_id:
body['external_id'] = external_id
if is_document_preparation_needed:
body['is_document_preparation_needed'] = is_document_preparation_needed
if redirect_url:
body['redirect_url'] = redirect_url
if declined_redirect_url:
body['declined_redirect_url'] = declined_redirect_url
if template_id:
body['template_id'] = template_id
@api_call
def create_sign_request_v2(
self,
signers: Iterable,
files: Optional[Iterable] = None,
parent_folder_id: Optional[str] = None,
prefill_tags: Optional[Iterable] = None,
are_reminders_enabled: Optional[bool] = None,
are_text_signatures_enabled: Optional[bool] = None,
days_valid: Optional[str] = None,
email_message: Optional[Iterable] = None,
email_subject: Optional[str] = None,
external_id: Optional[str] = None,
is_document_preparation_needed: Optional[bool] = None,
redirect_url: Optional[str] = None,
declined_redirect_url: Optional[str] = None,
template_id: Optional[str] = None,
) -> 'SignRequest':
"""
Used to create a new sign request.
box_response = self._session.post(url, data=json.dumps(body))
response = box_response.json()
return self.translator.translate(
session=self._session,
response_object=response,
)
:param signers:
List of signers for the sign request. 35 is the max number of signers permitted.
:param files:
List of files to create a signing document from.
:param parent_folder_id:
The id of the destination folder to place sign request specific data in.
:param prefill_tags:
When a document contains sign related tags in the content,
you can prefill them using this prefill_tags by referencing the 'id' of the tag as the external_id field of the prefill tag.
:param are_reminders_enabled:
Reminds signers to sign a document on day 3, 8, 13 and 18. Reminders are only sent to outstanding signers.
:param are_text_signatures_enabled:
Disables the usage of signatures generated by typing (text).
:param days_valid:
Number of days after which this request will automatically expire if not completed.
:param email_message:
Message to include in sign request email. The field is cleaned through sanitization of specific characters.
However, some html tags are allowed. Links included in the message are also converted to hyperlinks in the email.
The message may contain the following html tags including a, abbr, acronym, b, blockquote, code, em, i, ul, li, ol, and strong.
Be aware that when the text to html ratio is too high, the email may end up in spam filters. Custom styles on these tags are not allowed.
If this field is not passed, a default message will be used.
:param email_subject:
Subject of sign request email. This is cleaned by sign request. If this field is not passed, a default subject will be used.
:param external_id:
This can be used to reference an ID in an external system that the sign request is related to.
:param is_document_preparation_needed:
Indicates if the sender should receive a prepare_url in the response to complete document preparation via UI.
:param redirect_url:
The URL that a signer will be redirected to after signing a document.
If no declined redirect URL is specified, this URL will be used for decline actions as well.
:param declined_redirect_url:
The URL that a signer will be redirected to after declining to sign a document.
:param template_id:
The ID of the sign template to use for the sign request.
:returns:
A dictionary representing a created SignRequest
"""
return self.__create_sign_request(
signers, files, parent_folder_id, prefill_tags, are_reminders_enabled, are_text_signatures_enabled, days_valid, email_message,
email_subject, external_id, is_document_preparation_needed, redirect_url, declined_redirect_url, template_id)

@api_call
def get_sign_requests(
Expand Down
25 changes: 20 additions & 5 deletions docs/usage/sign_requests.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,13 @@ A Sign Request can refer to one or more Box Files and can be sent to one or more
Create Sign Request
------------------------

The [`client.create_sign_request(files, signers, parent_folder_id, prefill_tags=None, are_reminders_enabled=None, are_text_signatures_enabled=None, days_valid=None, email_message=None, email_subject=None, external_id=None, is_document_preparation_needed=None, redirect_url=None, declined_redirect_url=None, template_id=None)`][create-sign-request]
method will create a Sign Request. You need to provide at least one file and up to 10 files (from which the signing document will be created) with at least one signer to receive the Sign Request.
The [`client.create_sign_request_v2(signers, files=None, parent_folder_id=None, prefill_tags=None, are_reminders_enabled=None, are_text_signatures_enabled=None, days_valid=None, email_message=None, email_subject=None, external_id=None, is_document_preparation_needed=None, redirect_url=None, declined_redirect_url=None, template_id=None)`][create-sign-request]
method will create a Sign Request. You need to provide at least one file and up to 10 files (from which the signing document will be created) or template_id of the sign request template. You need to include at least one signer that will receive the Sign Request.

Example with files:

<!-- sample post_sign_requests -->

```python
source_file = {
'id': '12345',
Expand All @@ -32,12 +35,24 @@ files = [source_file]

signer = {
'name': 'John Doe',
'email': 'signer@mail.com'
'email': 'signer@mail.com'
}
signers = [signer]

parent_folder_id = '123456789'
new_sign_request = client.create_sign_request(files, signers, parent_folder_id)

new_sign_request = client.create_sign_request_v2(signers, files=files, parent_folder_id=parent_folder_id)
print(f'(Sign Request ID: {new_sign_request.id})')
```

Example with sign template

```python
signer = {
'name': 'John Doe',
'email': 'signer@mail.com'
}

new_sign_request = client.create_sign_request_v2(signers, template_id='12345')
print(f'(Sign Request ID: {new_sign_request.id})')
```

Expand Down
2 changes: 1 addition & 1 deletion test/integration_new/context_managers/box_sign_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
class BoxTestSignRequest:

def __init__(self, *, files: Iterable, signers: Iterable, parent_folder_id: str):
self._sign_request = CLIENT.create_sign_request(files, signers, parent_folder_id)
self._sign_request = CLIENT.create_sign_request_v2(signers, files, parent_folder_id)

def __enter__(self) -> WebLink:
return self._sign_request
Expand Down
2 changes: 1 addition & 1 deletion test/integration_new/object/sign_request_itest.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def test_test_sign_request(parent_folder, small_file_path):
}
signers = [signer1, signer2]

sign_request = CLIENT.create_sign_request(
sign_request = CLIENT.create_sign_request_v2(
files=files,
signers=signers,
parent_folder_id=parent_folder.id
Expand Down
45 changes: 40 additions & 5 deletions test/unit/client/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -1819,6 +1819,11 @@ def test_create_sign_request(mock_client, mock_box_session, mock_sign_request_re
parent_folder_id = '12345'

data = json.dumps({
'signers': [
{
'email': signer['email']
}
],
'source_files': [
{
'id': source_file['id'],
Expand All @@ -1829,11 +1834,6 @@ def test_create_sign_request(mock_client, mock_box_session, mock_sign_request_re
'type': source_file2['type']
}
],
'signers': [
{
'email': signer['email']
}
],
'parent_folder':
{
'id': parent_folder_id,
Expand All @@ -1859,6 +1859,41 @@ def test_create_sign_request(mock_client, mock_box_session, mock_sign_request_re
assert new_sign_request['template_id'] == template_id


def test_create_sign_request_v2(mock_client, mock_box_session, mock_sign_request_response):
expected_url = f'{API.BASE_API_URL}/sign_requests'
redirect_url = 'https://www.box.com/accepted'
declined_redirect_url = 'https://www.box.com/declined'
template_id = '123075213-af2c8822-3ef2-4952-8557-52d69c2fe9cb'

signer = {
'email': 'example@gmail.com'
}
signers = [signer]

data = json.dumps({
'signers': [
{
'email': signer['email']
}
],
'redirect_url': redirect_url,
'declined_redirect_url': declined_redirect_url,
'template_id': template_id
})
mock_box_session.post.return_value.json.return_value = mock_sign_request_response

new_sign_request = mock_client.create_sign_request_v2(
signers,
redirect_url=redirect_url, declined_redirect_url=declined_redirect_url, template_id=template_id)

mock_box_session.post.assert_called_once_with(expected_url, data=data)
assert isinstance(new_sign_request, SignRequest)
assert new_sign_request['signers'][0]['email'] == signer['email']
assert new_sign_request['redirect_url'] == redirect_url
assert new_sign_request['declined_redirect_url'] == declined_redirect_url
assert new_sign_request['template_id'] == template_id


def test_file_request(mock_client):
# pylint:disable=redefined-outer-name
file_request_id = '12345'
Expand Down

0 comments on commit d972f54

Please sign in to comment.