diff --git a/static/images/integrations/logos/hellosign.png b/static/images/integrations/logos/hellosign.png new file mode 100644 index 0000000000..ca8e77ba16 Binary files /dev/null and b/static/images/integrations/logos/hellosign.png differ diff --git a/zerver/fixtures/hellosign/hellosign_signatures.json b/zerver/fixtures/hellosign/hellosign_signatures.json new file mode 100644 index 0000000000..744cb74d02 --- /dev/null +++ b/zerver/fixtures/hellosign/hellosign_signatures.json @@ -0,0 +1,49 @@ +{ + "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": "78caf2a1d01cd39cea2bc1cbb340dac3", + "signer_email_address": "jack@example.com", + "signer_name": "Jack", + "order": 0, + "status_code": "awaiting_signature", + "signed_at": null, + "last_viewed_at": null, + "last_reminded_at": null, + "has_pin": false + }, { + "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" + ] + } +} diff --git a/zerver/lib/integrations.py b/zerver/lib/integrations.py index 9f893ebda3..97fe8339b4 100644 --- a/zerver/lib/integrations.py +++ b/zerver/lib/integrations.py @@ -126,6 +126,7 @@ WEBHOOK_INTEGRATIONS = [ ), WebhookIntegration('gitlab', display_name='GitLab'), WebhookIntegration('gosquared', display_name='GoSquared'), + WebhookIntegration('hellosign', display_name='HelloSign'), WebhookIntegration('helloworld', display_name='Hello World'), WebhookIntegration('heroku', display_name='Heroku'), WebhookIntegration('ifttt', function='zerver.views.webhooks.ifttt.api_iftt_app_webhook', display_name='IFTTT'), diff --git a/zerver/tests/webhooks/test_hellosign.py b/zerver/tests/webhooks/test_hellosign.py new file mode 100644 index 0000000000..ffa30825a1 --- /dev/null +++ b/zerver/tests/webhooks/test_hellosign.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +from typing import Text +from zerver.lib.test_classes import WebhookTestCase + +class HelloSignHookTests(WebhookTestCase): + STREAM_NAME = 'hellosign' + URL_TEMPLATE = "/api/v1/external/hellosign?stream={stream}&api_key={api_key}" + FIXTURE_DIR_NAME = 'hellosign' + + def test_signatures_message(self): + # type: () -> 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.") + self.send_and_test_stream_message('signatures', expected_subject, expected_message, + content_type="application/x-www-form-urlencoded") + + def get_body(self, fixture_name): + # type: (Text) -> Text + return self.fixture_data("hellosign", fixture_name, file_type="json") diff --git a/zerver/views/webhooks/hellosign.py b/zerver/views/webhooks/hellosign.py new file mode 100644 index 0000000000..d6e565855f --- /dev/null +++ b/zerver/views/webhooks/hellosign.py @@ -0,0 +1,63 @@ +from __future__ import absolute_import +from django.utils.translation import ugettext as _ +from zerver.lib.actions import check_send_message +from zerver.lib.response import json_success, json_error +from zerver.decorator import REQ, has_request_variables, api_key_only_webhook_view + +from zerver.models import Client, UserProfile + +from django.http import HttpRequest, HttpResponse +from six import text_type +from six.moves import range +from typing import Dict, Any, Optional, Tuple, Union + +def format_body(signatories, model_payload): + # type: (List[Dict[str, Any]], Dict[str, Any]) -> str + def append_separator(i, len_sig): + # type: (int, 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)) + + append_separator(i, len(signatories)) + result = ''.join(result) + return result + +def ready_payload(signatories, payload): + # type: (List[Dict[str, Any]], 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 + +@api_key_only_webhook_view('HelloSign') +@has_request_variables +def api_hellosign_webhook(request, user_profile, client, + payload=REQ(argument_type='body'), + stream=REQ(default='hellosign'), + topic=REQ(default=None)): + # type: (HttpRequest, UserProfile, Client, Dict[str, Dict[str, Any]], text_type, text_type) -> HttpResponse + try: + model_payload = ready_payload(payload['signature_request']['signatures'], + payload) + except KeyError as e: + return json_error(_("Missing key {} in JSON").format(str(e))) + + body = format_body(payload['signature_request']['signatures'], model_payload) + topic = model_payload['contract_title'] + check_send_message(user_profile, client, 'stream', [stream], + topic, body) + return json_success()