Skip to content

Commit d972f54

Browse files
authored
feat: add create sign request function with different required parameters (#878)
1 parent 3026d2a commit d972f54

File tree

5 files changed

+187
-51
lines changed

5 files changed

+187
-51
lines changed

boxsdk/client/client.py

Lines changed: 125 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1512,7 +1512,68 @@ def sign_request(self, sign_request_id: str) -> 'SignRequest':
15121512
"""
15131513
return self.translator.get('sign_request')(session=self._session, object_id=sign_request_id)
15141514

1515+
# pylint: disable=too-many-branches
1516+
def __create_sign_request(
1517+
self,
1518+
signers: Iterable,
1519+
files: Optional[Iterable] = None,
1520+
parent_folder_id: Optional[str] = None,
1521+
prefill_tags: Optional[Iterable] = None,
1522+
are_reminders_enabled: Optional[bool] = None,
1523+
are_text_signatures_enabled: Optional[bool] = None,
1524+
days_valid: Optional[str] = None,
1525+
email_message: Optional[Iterable] = None,
1526+
email_subject: Optional[str] = None,
1527+
external_id: Optional[str] = None,
1528+
is_document_preparation_needed: Optional[bool] = None,
1529+
redirect_url: Optional[str] = None,
1530+
declined_redirect_url: Optional[str] = None,
1531+
template_id: Optional[str] = None) -> 'SignRequest':
1532+
url = self._session.get_url('sign_requests')
1533+
1534+
body = {
1535+
'signers': signers,
1536+
}
1537+
1538+
if files:
1539+
body['source_files'] = files
1540+
if parent_folder_id:
1541+
body['parent_folder'] = {
1542+
'id': parent_folder_id,
1543+
'type': 'folder'
1544+
}
1545+
if prefill_tags:
1546+
body['prefill_tags'] = prefill_tags
1547+
if are_reminders_enabled:
1548+
body['are_reminders_enabled'] = are_reminders_enabled
1549+
if are_text_signatures_enabled:
1550+
body['are_text_signatures_enabled'] = are_text_signatures_enabled
1551+
if days_valid:
1552+
body['days_valid'] = days_valid
1553+
if email_message:
1554+
body['email_message'] = email_message
1555+
if email_subject:
1556+
body['email_subject'] = email_subject
1557+
if external_id:
1558+
body['external_id'] = external_id
1559+
if is_document_preparation_needed:
1560+
body['is_document_preparation_needed'] = is_document_preparation_needed
1561+
if redirect_url:
1562+
body['redirect_url'] = redirect_url
1563+
if declined_redirect_url:
1564+
body['declined_redirect_url'] = declined_redirect_url
1565+
if template_id:
1566+
body['template_id'] = template_id
1567+
1568+
box_response = self._session.post(url, data=json.dumps(body))
1569+
response = box_response.json()
1570+
return self.translator.translate(
1571+
session=self._session,
1572+
response_object=response,
1573+
)
1574+
15151575
@api_call
1576+
@deprecated('Use create_sign_request_v2 instead')
15161577
def create_sign_request(
15171578
self,
15181579
files: Iterable,
@@ -1529,7 +1590,7 @@ def create_sign_request(
15291590
redirect_url: Optional[str] = None,
15301591
declined_redirect_url: Optional[str] = None,
15311592
template_id: Optional[str] = None,
1532-
) -> dict:
1593+
) -> 'SignRequest':
15331594
"""
15341595
Used to create a new sign request.
15351596
@@ -1570,46 +1631,71 @@ def create_sign_request(
15701631
:returns:
15711632
A dictionary representing a created SignRequest
15721633
"""
1573-
url = self._session.get_url('sign_requests')
1634+
return self.__create_sign_request(
1635+
signers, files, parent_folder_id, prefill_tags, are_reminders_enabled, are_text_signatures_enabled, days_valid, email_message,
1636+
email_subject, external_id, is_document_preparation_needed, redirect_url, declined_redirect_url, template_id)
15741637

1575-
body = {
1576-
'source_files': files,
1577-
'signers': signers,
1578-
'parent_folder': {
1579-
'id': parent_folder_id,
1580-
'type': 'folder'
1581-
}
1582-
}
1583-
1584-
if prefill_tags:
1585-
body['prefill_tags'] = prefill_tags
1586-
if are_reminders_enabled:
1587-
body['are_reminders_enabled'] = are_reminders_enabled
1588-
if are_text_signatures_enabled:
1589-
body['are_text_signatures_enabled'] = are_text_signatures_enabled
1590-
if days_valid:
1591-
body['days_valid'] = days_valid
1592-
if email_message:
1593-
body['email_message'] = email_message
1594-
if email_subject:
1595-
body['email_subject'] = email_subject
1596-
if external_id:
1597-
body['external_id'] = external_id
1598-
if is_document_preparation_needed:
1599-
body['is_document_preparation_needed'] = is_document_preparation_needed
1600-
if redirect_url:
1601-
body['redirect_url'] = redirect_url
1602-
if declined_redirect_url:
1603-
body['declined_redirect_url'] = declined_redirect_url
1604-
if template_id:
1605-
body['template_id'] = template_id
1638+
@api_call
1639+
def create_sign_request_v2(
1640+
self,
1641+
signers: Iterable,
1642+
files: Optional[Iterable] = None,
1643+
parent_folder_id: Optional[str] = None,
1644+
prefill_tags: Optional[Iterable] = None,
1645+
are_reminders_enabled: Optional[bool] = None,
1646+
are_text_signatures_enabled: Optional[bool] = None,
1647+
days_valid: Optional[str] = None,
1648+
email_message: Optional[Iterable] = None,
1649+
email_subject: Optional[str] = None,
1650+
external_id: Optional[str] = None,
1651+
is_document_preparation_needed: Optional[bool] = None,
1652+
redirect_url: Optional[str] = None,
1653+
declined_redirect_url: Optional[str] = None,
1654+
template_id: Optional[str] = None,
1655+
) -> 'SignRequest':
1656+
"""
1657+
Used to create a new sign request.
16061658
1607-
box_response = self._session.post(url, data=json.dumps(body))
1608-
response = box_response.json()
1609-
return self.translator.translate(
1610-
session=self._session,
1611-
response_object=response,
1612-
)
1659+
:param signers:
1660+
List of signers for the sign request. 35 is the max number of signers permitted.
1661+
:param files:
1662+
List of files to create a signing document from.
1663+
:param parent_folder_id:
1664+
The id of the destination folder to place sign request specific data in.
1665+
:param prefill_tags:
1666+
When a document contains sign related tags in the content,
1667+
you can prefill them using this prefill_tags by referencing the 'id' of the tag as the external_id field of the prefill tag.
1668+
:param are_reminders_enabled:
1669+
Reminds signers to sign a document on day 3, 8, 13 and 18. Reminders are only sent to outstanding signers.
1670+
:param are_text_signatures_enabled:
1671+
Disables the usage of signatures generated by typing (text).
1672+
:param days_valid:
1673+
Number of days after which this request will automatically expire if not completed.
1674+
:param email_message:
1675+
Message to include in sign request email. The field is cleaned through sanitization of specific characters.
1676+
However, some html tags are allowed. Links included in the message are also converted to hyperlinks in the email.
1677+
The message may contain the following html tags including a, abbr, acronym, b, blockquote, code, em, i, ul, li, ol, and strong.
1678+
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.
1679+
If this field is not passed, a default message will be used.
1680+
:param email_subject:
1681+
Subject of sign request email. This is cleaned by sign request. If this field is not passed, a default subject will be used.
1682+
:param external_id:
1683+
This can be used to reference an ID in an external system that the sign request is related to.
1684+
:param is_document_preparation_needed:
1685+
Indicates if the sender should receive a prepare_url in the response to complete document preparation via UI.
1686+
:param redirect_url:
1687+
The URL that a signer will be redirected to after signing a document.
1688+
If no declined redirect URL is specified, this URL will be used for decline actions as well.
1689+
:param declined_redirect_url:
1690+
The URL that a signer will be redirected to after declining to sign a document.
1691+
:param template_id:
1692+
The ID of the sign template to use for the sign request.
1693+
:returns:
1694+
A dictionary representing a created SignRequest
1695+
"""
1696+
return self.__create_sign_request(
1697+
signers, files, parent_folder_id, prefill_tags, are_reminders_enabled, are_text_signatures_enabled, days_valid, email_message,
1698+
email_subject, external_id, is_document_preparation_needed, redirect_url, declined_redirect_url, template_id)
16131699

16141700
@api_call
16151701
def get_sign_requests(

docs/usage/sign_requests.md

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,13 @@ A Sign Request can refer to one or more Box Files and can be sent to one or more
1919
Create Sign Request
2020
------------------------
2121

22-
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]
23-
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.
22+
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]
23+
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.
24+
25+
Example with files:
2426

2527
<!-- sample post_sign_requests -->
28+
2629
```python
2730
source_file = {
2831
'id': '12345',
@@ -32,12 +35,24 @@ files = [source_file]
3235

3336
signer = {
3437
'name': 'John Doe',
35-
'email': '[email protected]'
38+
'email': '[email protected]'
3639
}
3740
signers = [signer]
38-
3941
parent_folder_id = '123456789'
40-
new_sign_request = client.create_sign_request(files, signers, parent_folder_id)
42+
43+
new_sign_request = client.create_sign_request_v2(signers, files=files, parent_folder_id=parent_folder_id)
44+
print(f'(Sign Request ID: {new_sign_request.id})')
45+
```
46+
47+
Example with sign template
48+
49+
```python
50+
signer = {
51+
'name': 'John Doe',
52+
'email': '[email protected]'
53+
}
54+
55+
new_sign_request = client.create_sign_request_v2(signers, template_id='12345')
4156
print(f'(Sign Request ID: {new_sign_request.id})')
4257
```
4358

test/integration_new/context_managers/box_sign_request.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
class BoxTestSignRequest:
88

99
def __init__(self, *, files: Iterable, signers: Iterable, parent_folder_id: str):
10-
self._sign_request = CLIENT.create_sign_request(files, signers, parent_folder_id)
10+
self._sign_request = CLIENT.create_sign_request_v2(signers, files, parent_folder_id)
1111

1212
def __enter__(self) -> WebLink:
1313
return self._sign_request

test/integration_new/object/sign_request_itest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ def test_test_sign_request(parent_folder, small_file_path):
3434
}
3535
signers = [signer1, signer2]
3636

37-
sign_request = CLIENT.create_sign_request(
37+
sign_request = CLIENT.create_sign_request_v2(
3838
files=files,
3939
signers=signers,
4040
parent_folder_id=parent_folder.id

test/unit/client/test_client.py

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1819,6 +1819,11 @@ def test_create_sign_request(mock_client, mock_box_session, mock_sign_request_re
18191819
parent_folder_id = '12345'
18201820

18211821
data = json.dumps({
1822+
'signers': [
1823+
{
1824+
'email': signer['email']
1825+
}
1826+
],
18221827
'source_files': [
18231828
{
18241829
'id': source_file['id'],
@@ -1829,11 +1834,6 @@ def test_create_sign_request(mock_client, mock_box_session, mock_sign_request_re
18291834
'type': source_file2['type']
18301835
}
18311836
],
1832-
'signers': [
1833-
{
1834-
'email': signer['email']
1835-
}
1836-
],
18371837
'parent_folder':
18381838
{
18391839
'id': parent_folder_id,
@@ -1859,6 +1859,41 @@ def test_create_sign_request(mock_client, mock_box_session, mock_sign_request_re
18591859
assert new_sign_request['template_id'] == template_id
18601860

18611861

1862+
def test_create_sign_request_v2(mock_client, mock_box_session, mock_sign_request_response):
1863+
expected_url = f'{API.BASE_API_URL}/sign_requests'
1864+
redirect_url = 'https://www.box.com/accepted'
1865+
declined_redirect_url = 'https://www.box.com/declined'
1866+
template_id = '123075213-af2c8822-3ef2-4952-8557-52d69c2fe9cb'
1867+
1868+
signer = {
1869+
'email': '[email protected]'
1870+
}
1871+
signers = [signer]
1872+
1873+
data = json.dumps({
1874+
'signers': [
1875+
{
1876+
'email': signer['email']
1877+
}
1878+
],
1879+
'redirect_url': redirect_url,
1880+
'declined_redirect_url': declined_redirect_url,
1881+
'template_id': template_id
1882+
})
1883+
mock_box_session.post.return_value.json.return_value = mock_sign_request_response
1884+
1885+
new_sign_request = mock_client.create_sign_request_v2(
1886+
signers,
1887+
redirect_url=redirect_url, declined_redirect_url=declined_redirect_url, template_id=template_id)
1888+
1889+
mock_box_session.post.assert_called_once_with(expected_url, data=data)
1890+
assert isinstance(new_sign_request, SignRequest)
1891+
assert new_sign_request['signers'][0]['email'] == signer['email']
1892+
assert new_sign_request['redirect_url'] == redirect_url
1893+
assert new_sign_request['declined_redirect_url'] == declined_redirect_url
1894+
assert new_sign_request['template_id'] == template_id
1895+
1896+
18621897
def test_file_request(mock_client):
18631898
# pylint:disable=redefined-outer-name
18641899
file_request_id = '12345'

0 commit comments

Comments
 (0)