snapshot of https://github.com/zulip/zulip.git @ 48c69911c7dd4e03b20552eb3bd77ad8151c1357 (2026-09-15)
Some checks failed
Code scanning / CodeQL (push) Failing after 15s
Zulip production suite / Ubuntu 22.04 production build (push) Failing after 26s
Zulip production suite / Debian 12 production install with custom db name and user (push) Has been skipped
Zulip production suite / Ubuntu 22.04 production install and PostgreSQL upgrade with pgroonga (push) Has been skipped
Zulip production suite / Ubuntu 24.04 production install (push) Has been skipped
Zulip production suite / Ubuntu 26.04 production install (push) Has been skipped
Zulip production suite / Debian 13 production install (push) Has been skipped
Zulip production suite / 7.0 Version Upgrade (push) Has been skipped
Zulip production suite / 8.0 Version Upgrade (push) Has been skipped
Zulip production suite / 6.0 Version Upgrade (push) Has been skipped
Zulip production suite / 10.0 Version Upgrade (push) Has been skipped
Zulip production suite / 9.0 Version Upgrade (push) Has been skipped
Zulip production suite / 12.0 Version Upgrade (push) Has been skipped
Zulip production suite / 11.0 Version Upgrade (push) Has been skipped
GitHub Actions Security Analysis with zizmor / zizmor (push) Failing after 3s
Zulip CI / Debian 12 (Python 3.11, backend + documentation) (push) Failing after 26s
Zulip CI / Ubuntu 22.04 (Python 3.10, backend + frontend) (push) Failing after 3s
Zulip CI / Ubuntu 24.04 (Python 3.12, backend) (push) Failing after 25s
Zulip CI / Ubuntu 26.04 (Python 3.14, backend) (push) Failing after 25s
Zulip CI / Debian 13 (Python 3.13, backend) (push) Failing after 28s
Zulip production suite / Required jobs (push) Has been cancelled
Zulip CI / Required jobs (push) Has been cancelled
API Documentation Update Check / check-feature-level-updated (push) Has been cancelled
API Documentation Update Check / notify-if-api-docs-changed (push) Has been cancelled
Some checks failed
Code scanning / CodeQL (push) Failing after 15s
Zulip production suite / Ubuntu 22.04 production build (push) Failing after 26s
Zulip production suite / Debian 12 production install with custom db name and user (push) Has been skipped
Zulip production suite / Ubuntu 22.04 production install and PostgreSQL upgrade with pgroonga (push) Has been skipped
Zulip production suite / Ubuntu 24.04 production install (push) Has been skipped
Zulip production suite / Ubuntu 26.04 production install (push) Has been skipped
Zulip production suite / Debian 13 production install (push) Has been skipped
Zulip production suite / 7.0 Version Upgrade (push) Has been skipped
Zulip production suite / 8.0 Version Upgrade (push) Has been skipped
Zulip production suite / 6.0 Version Upgrade (push) Has been skipped
Zulip production suite / 10.0 Version Upgrade (push) Has been skipped
Zulip production suite / 9.0 Version Upgrade (push) Has been skipped
Zulip production suite / 12.0 Version Upgrade (push) Has been skipped
Zulip production suite / 11.0 Version Upgrade (push) Has been skipped
GitHub Actions Security Analysis with zizmor / zizmor (push) Failing after 3s
Zulip CI / Debian 12 (Python 3.11, backend + documentation) (push) Failing after 26s
Zulip CI / Ubuntu 22.04 (Python 3.10, backend + frontend) (push) Failing after 3s
Zulip CI / Ubuntu 24.04 (Python 3.12, backend) (push) Failing after 25s
Zulip CI / Ubuntu 26.04 (Python 3.14, backend) (push) Failing after 25s
Zulip CI / Debian 13 (Python 3.13, backend) (push) Failing after 28s
Zulip production suite / Required jobs (push) Has been cancelled
Zulip CI / Required jobs (push) Has been cancelled
API Documentation Update Check / check-feature-level-updated (push) Has been cancelled
API Documentation Update Check / notify-if-api-docs-changed (push) Has been cancelled
This commit is contained in:
0
zerver/webhooks/dropboxsign/__init__.py
Normal file
0
zerver/webhooks/dropboxsign/__init__.py
Normal file
24
zerver/webhooks/dropboxsign/doc.md
Normal file
24
zerver/webhooks/dropboxsign/doc.md
Normal file
@@ -0,0 +1,24 @@
|
||||
# Zulip Dropbox Sign integration
|
||||
|
||||
Configuring the Dropbox Sign integration is easy!
|
||||
|
||||
{start_tabs}
|
||||
|
||||
1. {!create-an-incoming-webhook.md!}
|
||||
|
||||
1. {!generate-webhook-url-basic.md!}
|
||||
|
||||
1. Go to your Dropbox Sign account **Settings**, and click on **API**.
|
||||
|
||||
1. Set **Account Callback** to the URL generated above. Click **Test**
|
||||
to send a test notification to your Zulip organization.
|
||||
|
||||
{end_tabs}
|
||||
|
||||
{!congrats.md!}
|
||||
|
||||

|
||||
|
||||
### Related documentation
|
||||
|
||||
{!webhooks-url-specification.md!}
|
||||
10
zerver/webhooks/dropboxsign/fixtures/callback_test.json
Normal file
10
zerver/webhooks/dropboxsign/fixtures/callback_test.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"event": {
|
||||
"event_time": "1729521679",
|
||||
"event_type": "callback_test",
|
||||
"event_hash": "1abb1c65915585ab5afgec6f62c479b2917a889605f7ee867f64cbb313666f8f",
|
||||
"event_metadata": {
|
||||
"reported_for_account_id": "7ca5011f9071abc48c498f10a5692ea36f9534e3"
|
||||
}
|
||||
}
|
||||
}
|
||||
49
zerver/webhooks/dropboxsign/fixtures/signatures.json
Normal file
49
zerver/webhooks/dropboxsign/fixtures/signatures.json
Normal file
@@ -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.dropboxsign.com/sign/2b388914e3ae3b738bd4e2ee2850c677e6dc53d2",
|
||||
"signing_redirect_url": null,
|
||||
"final_copy_uri": "/v3/signature_request/final_copy/2b388914e3ae3b738bd4e2ee2850c677e6dc53d2",
|
||||
"files_url": "https://api.dropboxsign.com/v3/signature_request/files/2b388914e3ae3b738bd4e2ee2850c677e6dc53d2",
|
||||
"details_url": "https://www.dropboxsign.com/home/manage?guid=2b388914e3ae3b738bd4e2ee2850c677e6dc53d2",
|
||||
"requester_email_address": "me@dropboxsign.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@dropboxsign.com",
|
||||
"lawyer2@example.com"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -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.dropboxsign.com/sign/2b388914e3ae3b738bd4e2ee2850c677e6dc53d2",
|
||||
"signing_redirect_url": null,
|
||||
"final_copy_uri": "/v3/signature_request/final_copy/2b388914e3ae3b738bd4e2ee2850c677e6dc53d2",
|
||||
"files_url": "https://api.dropboxsign.com/v3/signature_request/files/2b388914e3ae3b738bd4e2ee2850c677e6dc53d2",
|
||||
"details_url": "https://www.dropboxsign.com/home/manage?guid=2b388914e3ae3b738bd4e2ee2850c677e6dc53d2",
|
||||
"requester_email_address": "me@dropboxsign.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@dropboxsign.com",
|
||||
"lawyer2@example.com"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -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.dropboxsign.com\/sign\/a6ce6526d1e7496c96a1cdd11276efb5c4504084",
|
||||
"signing_redirect_url":null,
|
||||
"final_copy_uri":"\/\/signature_request\/final_copy\/a6ce6526d1e7496c96a1cdd11276efb5c4504084",
|
||||
"files_url":"https:\/\/api.dropboxsign.com\/signature_request\/files\/a6ce6526d1e7496c96a1cdd11276efb5c4504084",
|
||||
"details_url":"https:\/\/app.dropboxsign.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":[
|
||||
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -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.dropboxsign.com/sign/2b388914e3ae3b738bd4e2ee2850c677e6dc53d2",
|
||||
"signing_redirect_url": null,
|
||||
"final_copy_uri": "/v3/signature_request/final_copy/2b388914e3ae3b738bd4e2ee2850c677e6dc53d2",
|
||||
"files_url": "https://api.dropboxsign.com/v3/signature_request/files/2b388914e3ae3b738bd4e2ee2850c677e6dc53d2",
|
||||
"details_url": "https://www.dropboxsign.com/home/manage?guid=2b388914e3ae3b738bd4e2ee2850c677e6dc53d2",
|
||||
"requester_email_address": "me@dropboxsign.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@dropboxsign.com",
|
||||
"lawyer2@example.com"
|
||||
]
|
||||
}
|
||||
}
|
||||
76
zerver/webhooks/dropboxsign/tests.py
Normal file
76
zerver/webhooks/dropboxsign/tests.py
Normal file
@@ -0,0 +1,76 @@
|
||||
from typing_extensions import override
|
||||
|
||||
from zerver.lib.test_classes import WebhookTestCase
|
||||
|
||||
|
||||
class DropboxSignHookTests(WebhookTestCase):
|
||||
def test_legacy_urls(self) -> None:
|
||||
expected_topic_name = "NDA with Acme Co."
|
||||
expected_message = (
|
||||
"The `NDA with Acme Co.` document is awaiting the signature of "
|
||||
"Jack, and was just signed by Jill."
|
||||
)
|
||||
|
||||
legacy_names = ["hellosign"]
|
||||
for legacy_name in legacy_names:
|
||||
self.url = self.build_webhook_url(legacy_name=legacy_name)
|
||||
self.check_webhook(
|
||||
"signatures", expected_topic_name, expected_message, content_type=None
|
||||
)
|
||||
|
||||
def test_signatures_message(self) -> None:
|
||||
expected_topic_name = "NDA with Acme Co."
|
||||
expected_message = (
|
||||
"The `NDA with Acme Co.` document is awaiting the signature of "
|
||||
"Jack, and was just signed by Jill."
|
||||
)
|
||||
self.check_webhook("signatures", expected_topic_name, expected_message, content_type=None)
|
||||
|
||||
def test_signatures_message_signed_by_one(self) -> None:
|
||||
expected_topic_name = "NDA with Acme Co."
|
||||
expected_message = "The `NDA with Acme Co.` document was just signed by Jill."
|
||||
self.check_webhook(
|
||||
"signatures_signed_by_one_signatory",
|
||||
expected_topic_name,
|
||||
expected_message,
|
||||
content_type=None,
|
||||
)
|
||||
|
||||
def test_signatures_message_with_four_signatories(self) -> None:
|
||||
expected_topic_name = "Signature doc"
|
||||
expected_message = (
|
||||
"The `Signature doc` document is awaiting the signature of "
|
||||
"Eeshan Garg, John Smith, Jane Doe, and Stephen Strange."
|
||||
)
|
||||
self.check_webhook(
|
||||
"signatures_with_four_signatories",
|
||||
expected_topic_name,
|
||||
expected_message,
|
||||
content_type=None,
|
||||
)
|
||||
|
||||
def test_signatures_message_with_own_subject(self) -> None:
|
||||
expected_topic_name = "Our own subject."
|
||||
self.url = self.build_webhook_url(topic=expected_topic_name)
|
||||
expected_message = (
|
||||
"The `NDA with Acme Co.` document is awaiting the signature of "
|
||||
"Jack, and was just signed by Jill."
|
||||
)
|
||||
self.check_webhook(
|
||||
"signatures_with_own_subject",
|
||||
expected_topic_name,
|
||||
expected_message,
|
||||
content_type=None,
|
||||
topic=expected_topic_name,
|
||||
)
|
||||
|
||||
def test_callback_test(self) -> None:
|
||||
expected_topic_name = "Dropbox Sign"
|
||||
expected_message = "Dropbox Sign webhook has been successfully configured."
|
||||
self.check_webhook(
|
||||
"callback_test", expected_topic_name, expected_message, content_type=None
|
||||
)
|
||||
|
||||
@override
|
||||
def get_payload(self, fixture_name: str) -> dict[str, str]:
|
||||
return {"json": self.webhook_fixture_data("dropboxsign", fixture_name, file_type="json")}
|
||||
76
zerver/webhooks/dropboxsign/view.py
Normal file
76
zerver/webhooks/dropboxsign/view.py
Normal file
@@ -0,0 +1,76 @@
|
||||
from typing import Annotated
|
||||
|
||||
from django.http import HttpRequest, HttpResponse
|
||||
from pydantic import Json
|
||||
|
||||
from zerver.decorator import webhook_view
|
||||
from zerver.lib.response import json_success
|
||||
from zerver.lib.typed_endpoint import ApiParamConfig, typed_endpoint
|
||||
from zerver.lib.validator import WildValue, check_string
|
||||
from zerver.lib.webhooks.common import check_send_webhook_message, get_setup_webhook_message
|
||||
from zerver.models import UserProfile
|
||||
|
||||
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}."
|
||||
|
||||
|
||||
def get_message_body(payload: WildValue) -> str:
|
||||
contract_title = payload["signature_request"]["title"].tame(check_string)
|
||||
recipients: dict[str, list[str]] = {}
|
||||
signatures = payload["signature_request"]["signatures"]
|
||||
|
||||
for signature in signatures:
|
||||
status_code = signature["status_code"].tame(check_string)
|
||||
recipients.setdefault(status_code, [])
|
||||
recipients[status_code].append(signature["signer_name"].tame(check_string))
|
||||
|
||||
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 = f"{recipients_text}, and {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 += f"{recipient}, "
|
||||
recipients_text += f"and {recipients[-1]}"
|
||||
|
||||
return recipients_text
|
||||
|
||||
|
||||
@webhook_view("HelloSign")
|
||||
@typed_endpoint
|
||||
def api_dropboxsign_webhook(
|
||||
request: HttpRequest,
|
||||
user_profile: UserProfile,
|
||||
*,
|
||||
payload: Annotated[Json[WildValue], ApiParamConfig("json")],
|
||||
) -> HttpResponse:
|
||||
if "signature_request" in payload:
|
||||
body = get_message_body(payload)
|
||||
topic_name = payload["signature_request"]["title"].tame(check_string)
|
||||
check_send_webhook_message(request, user_profile, topic_name, body)
|
||||
elif payload["event"]["event_type"].tame(check_string) == "callback_test":
|
||||
body = get_setup_webhook_message("Dropbox Sign")
|
||||
topic_name = "Dropbox Sign"
|
||||
check_send_webhook_message(request, user_profile, topic_name, body)
|
||||
|
||||
return json_success(request, data={"msg": "Hello API Event Received"})
|
||||
Reference in New Issue
Block a user