webhooks/hellosign: Rewrite the integration from scratch.

After discovering a couple of bugs, I decided to thoroughly test
and rewrite this integration from scratch. The older code wasn't
generating coherent messages.

This also commit gets this integration up to 100% test coverage.
This commit is contained in:
Eeshan Garg 2018-10-04 12:53:27 -02:30 committed by Rishi Gupta
parent 5039f6dfb5
commit 316f9aa78b
5 changed files with 192 additions and 33 deletions

View File

@ -102,7 +102,6 @@ not_yet_fully_covered = {
# Webhook integrations with incomplete coverage
'zerver/webhooks/github_legacy/view.py',
'zerver/webhooks/greenhouse/view.py',
'zerver/webhooks/hellosign/view.py',
'zerver/webhooks/ifttt/view.py',
'zerver/webhooks/jira/view.py',
'zerver/webhooks/librato/view.py',

View File

@ -0,0 +1,39 @@
{
"signature_request": {
"signature_request_id": "2b388914e3ae3b738bd4e2ee2850c677e6dc53d2",
"title": "NDA with Acme Co.",
"subject": "The NDA we talked about",
"message": "Please sign this NDA and then we can discuss more. Let me know if you have any questions.",
"test_mode": true,
"metadata": {
"custom_id": "1234",
"custom_text": "NDA #9"
},
"is_complete": false,
"is_declined": false,
"has_error": false,
"custom_fields": [],
"response_data": [],
"signing_url": "https://www.hellosign.com/sign/2b388914e3ae3b738bd4e2ee2850c677e6dc53d2",
"signing_redirect_url": null,
"final_copy_uri": "/v3/signature_request/final_copy/2b388914e3ae3b738bd4e2ee2850c677e6dc53d2",
"files_url": "https://api.hellosign.com/v3/signature_request/files/2b388914e3ae3b738bd4e2ee2850c677e6dc53d2",
"details_url": "https://www.hellosign.com/home/manage?guid=2b388914e3ae3b738bd4e2ee2850c677e6dc53d2",
"requester_email_address": "me@hellosign.com",
"signatures": [{
"signature_id": "616629ed37f8588d28600be17ab5d6b7",
"signer_email_address": "jill@example.com",
"signer_name": "Jill",
"order": 1,
"status_code": "signed",
"signed_at": null,
"last_viewed_at": null,
"last_reminded_at": null,
"has_pin": false
}],
"cc_email_addresses": [
"lawyer1@hellosign.com",
"lawyer2@example.com"
]
}
}

View File

@ -0,0 +1,93 @@
{
"event":{
"event_type":"signature_request_sent",
"event_time":"1538590065",
"event_hash":"a755305281fde7bee3517cfd6858e5300bc12c85a2c0f51b47babe98800258d4",
"event_metadata":{
"related_signature_id":null,
"reported_for_account_id":"4686cb274933adf9c9b3a450c4131b0105a2ac14",
"reported_for_app_id":null
}
},
"account_guid":"4686cb274933adf9c9b3a450c4131b0105a2ac14",
"client_id":null,
"signature_request":{
"signature_request_id":"a6ce6526d1e7496c96a1cdd11276efb5c4504084",
"test_mode":false,
"title":"Signature doc",
"original_title":"Signature doc",
"subject":"Signature doc",
"message":null,
"metadata":{
},
"is_complete":false,
"is_declined":false,
"has_error":false,
"custom_fields":[
],
"response_data":[
],
"signing_url":"https:\/\/app.hellosign.com\/sign\/a6ce6526d1e7496c96a1cdd11276efb5c4504084",
"signing_redirect_url":null,
"final_copy_uri":"\/\/signature_request\/final_copy\/a6ce6526d1e7496c96a1cdd11276efb5c4504084",
"files_url":"https:\/\/api.hellosign.com\/signature_request\/files\/a6ce6526d1e7496c96a1cdd11276efb5c4504084",
"details_url":"https:\/\/app.hellosign.com\/home\/manage?guid=a6ce6526d1e7496c96a1cdd11276efb5c4504084",
"requester_email_address":"jerryguitarist@gmail.com",
"signatures":[
{
"signature_id":"1d3e6160c86ce2ffadc5032f449ccc7f",
"has_pin":false,
"signer_email_address":"jerryguitarist@gmail.com",
"signer_name":"Eeshan Garg",
"order":null,
"status_code":"awaiting_signature",
"signed_at":null,
"last_viewed_at":null,
"last_reminded_at":null,
"error":null
},
{
"signature_id":"4f6270cc1df39ffe86a47fd9293bc49c",
"has_pin":false,
"signer_email_address":"johnsmith@example.com",
"signer_name":"John Smith",
"order":null,
"status_code":"awaiting_signature",
"signed_at":null,
"last_viewed_at":null,
"last_reminded_at":null,
"error":null
},
{
"signature_id":"4f532706f781664d50bca2c88da81361",
"has_pin":false,
"signer_email_address":"janedoe@example.com",
"signer_name":"Jane Doe",
"order":null,
"status_code":"awaiting_signature",
"signed_at":null,
"last_viewed_at":null,
"last_reminded_at":null,
"error":null
},
{
"signature_id":"d13912af43be9cfc8119597b0d6e9c68",
"has_pin":false,
"signer_email_address":"drstrange@example.com",
"signer_name":"Stephen Strange",
"order":null,
"status_code":"awaiting_signature",
"signed_at":null,
"last_viewed_at":null,
"last_reminded_at":null,
"error":null
}
],
"cc_email_addresses":[
]
}
}

View File

@ -9,16 +9,30 @@ class HelloSignHookTests(WebhookTestCase):
def test_signatures_message(self) -> None:
expected_subject = "NDA with Acme Co."
expected_message = ("The NDA with Acme Co. is awaiting the signature of "
"Jack and was just signed by Jill.")
expected_message = ("The `NDA with Acme Co.` document is awaiting the signature of "
"Jack, and was just signed by Jill.")
self.send_and_test_stream_message('signatures', expected_subject, expected_message,
content_type="application/x-www-form-urlencoded")
def test_signatures_message_signed_by_one(self) -> None:
expected_subject = "NDA with Acme Co."
expected_message = ("The `NDA with Acme Co.` document was just signed by Jill.")
self.send_and_test_stream_message('signatures_signed_by_one_signatory',
expected_subject, expected_message,
content_type="application/x-www-form-urlencoded")
def test_signatures_message_with_four_signatories(self) -> None:
expected_subject = "Signature doc"
expected_message = ("The `Signature doc` document is awaiting the signature of "
"Eeshan Garg, John Smith, Jane Doe, and Stephen Strange.")
self.send_and_test_stream_message('signatures_with_four_signatories', expected_subject, expected_message,
content_type="application/x-www-form-urlencoded")
def test_signatures_message_with_own_subject(self) -> None:
expected_subject = "Our own subject."
self.url = self.build_webhook_url(topic=expected_subject)
expected_message = ("The NDA with Acme Co. is awaiting the signature of "
"Jack and was just signed by Jill.")
expected_message = ("The `NDA with Acme Co.` document is awaiting the signature of "
"Jack, and was just signed by Jill.")
self.send_and_test_stream_message('signatures_with_own_subject', expected_subject, expected_message,
content_type="application/x-www-form-urlencoded", topic=expected_subject)

View File

@ -9,41 +9,55 @@ from zerver.lib.response import json_error, json_success
from zerver.lib.webhooks.common import check_send_webhook_message
from zerver.models import UserProfile
def format_body(signatories: List[Dict[str, Any]], model_payload: Dict[str, Any]) -> str:
def append_separator(i: int) -> None:
if i + 1 == len(signatories):
result.append('.')
elif i + 2 == len(signatories):
result.append(' and')
elif i + 3 != len(signatories):
result.append(',')
result = ["The {}".format(model_payload['contract_title'])] # type: Any
for i, signatory in enumerate(signatories):
name = model_payload['name_{}'.format(i)]
if signatory['status_code'] == 'awaiting_signature':
result.append(" is awaiting the signature of {}".format(name))
elif signatory['status_code'] in ['signed', 'declined']:
status = model_payload['status_{}'.format(i)]
result.append(" was just {} by {}".format(status, name))
IS_AWAITING_SIGNATURE = "is awaiting the signature of {awaiting_recipients}"
WAS_JUST_SIGNED_BY = "was just signed by {signed_recipients}"
BODY = "The `{contract_title}` document {actions}."
append_separator(i)
return ''.join(result)
def get_message_body(payload: Dict[str, Dict[str, Any]]) -> str:
contract_title = payload['signature_request']['title']
recipients = {} # type: Dict[str, List[str]]
signatures = payload['signature_request']['signatures']
def ready_payload(signatories: List[Dict[str, Any]],
payload: Dict[str, Dict[str, Any]]) -> Dict[str, Any]:
model_payload = {'contract_title': payload['signature_request']['title']}
for i, signatory in enumerate(signatories):
model_payload['name_{}'.format(i)] = signatory['signer_name']
model_payload['status_{}'.format(i)] = signatory['status_code']
return model_payload
for signature in signatures:
recipients.setdefault(signature['status_code'], [])
recipients[signature['status_code']].append(signature['signer_name'])
recipients_text = ""
if recipients.get('awaiting_signature'):
recipients_text += IS_AWAITING_SIGNATURE.format(
awaiting_recipients=get_recipients_text(recipients['awaiting_signature'])
)
if recipients.get('signed'):
text = WAS_JUST_SIGNED_BY.format(
signed_recipients=get_recipients_text(recipients['signed'])
)
if recipients_text:
recipients_text = "{}, and {}".format(recipients_text, text)
else:
recipients_text = text
return BODY.format(contract_title=contract_title,
actions=recipients_text).strip()
def get_recipients_text(recipients: List[str]) -> str:
recipients_text = ""
if len(recipients) == 1:
recipients_text = "{}".format(*recipients)
else:
for recipient in recipients[:-1]:
recipients_text += "{}, ".format(recipient)
recipients_text += "and {}".format(recipients[-1])
return recipients_text
@api_key_only_webhook_view('HelloSign')
@has_request_variables
def api_hellosign_webhook(request: HttpRequest, user_profile: UserProfile,
payload: Dict[str, Dict[str, Any]]=REQ(argument_type='body')) -> HttpResponse:
model_payload = ready_payload(payload['signature_request']['signatures'], payload)
body = format_body(payload['signature_request']['signatures'], model_payload)
topic = model_payload['contract_title']
body = get_message_body(payload)
topic = payload['signature_request']['title']
check_send_webhook_message(request, user_profile, topic, body)
return json_success()