integrations: Fix Jotform integration by accepting form-data payloads.

This does not add support for files.

Fixes part of #32460.

Co-authored by: PieterCK <pieterceka123@gmail.com>
This commit is contained in:
Niloth P
2024-11-26 20:37:54 +05:30
committed by Tim Abbott
parent 2942a44224
commit e731fb9eba
6 changed files with 124 additions and 24 deletions

View File

@@ -29,6 +29,7 @@ IGNORED_PHRASES = [
r"IP",
r"JSON",
r"Jitsi",
r"Jotform",
r"Kerberos",
r"LinkedIn",
r"LDAP",

View File

@@ -755,7 +755,7 @@ DOC_SCREENSHOT_CONFIG: dict[str, list[BaseScreenshotConfig]] = {
"insping": [ScreenshotConfig("website_state_available.json")],
"intercom": [ScreenshotConfig("conversation_admin_replied.json")],
"jira": [ScreenshotConfig("created_v1.json")],
"jotform": [ScreenshotConfig("response.json")],
"jotform": [ScreenshotConfig("response.multipart")],
"json": [ScreenshotConfig("json_github_push__1_commit.json")],
"librato": [ScreenshotConfig("three_conditions_alert.json", payload_as_query_param=True)],
"lidarr": [ScreenshotConfig("lidarr_album_grabbed.json")],

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,93 @@
--------------------------J1JNT4vbVO4FCC1qLG4HSv
Content-Disposition: form-data; name="action"
--------------------------J1JNT4vbVO4FCC1qLG4HSv
Content-Disposition: form-data; name="webhookURL"
https://3a95-27-5-198-172.ngrok-free.app/api/v1/external/jotform?api_key=DhXGrkyy2bK2SXMQMXwhs95H4kVVwv2w
--------------------------J1JNT4vbVO4FCC1qLG4HSv
Content-Disposition: form-data; name="username"
UserNiloth
--------------------------J1JNT4vbVO4FCC1qLG4HSv
Content-Disposition: form-data; name="formID"
243231271343446
--------------------------J1JNT4vbVO4FCC1qLG4HSv
Content-Disposition: form-data; name="type"
WEB
--------------------------J1JNT4vbVO4FCC1qLG4HSv
Content-Disposition: form-data; name="customParams"
--------------------------J1JNT4vbVO4FCC1qLG4HSv
Content-Disposition: form-data; name="product"
--------------------------J1JNT4vbVO4FCC1qLG4HSv
Content-Disposition: form-data; name="formTitle"
Tutor Appointment Form
--------------------------J1JNT4vbVO4FCC1qLG4HSv
Content-Disposition: form-data; name="customTitle"
--------------------------J1JNT4vbVO4FCC1qLG4HSv
Content-Disposition: form-data; name="submissionID"
6084250982513018472
--------------------------J1JNT4vbVO4FCC1qLG4HSv
Content-Disposition: form-data; name="event"
--------------------------J1JNT4vbVO4FCC1qLG4HSv
Content-Disposition: form-data; name="documentID"
--------------------------J1JNT4vbVO4FCC1qLG4HSv
Content-Disposition: form-data; name="teamID"
--------------------------J1JNT4vbVO4FCC1qLG4HSv
Content-Disposition: form-data; name="subject"
--------------------------J1JNT4vbVO4FCC1qLG4HSv
Content-Disposition: form-data; name="isSilent"
--------------------------J1JNT4vbVO4FCC1qLG4HSv
Content-Disposition: form-data; name="customBody"
--------------------------J1JNT4vbVO4FCC1qLG4HSv
Content-Disposition: form-data; name="rawRequest"
{"slug":"submit\/243231271343446","jsExecutionTracker":"build-date-1732271172685=>init-started:1732615894703=>validator-called:1732615894706=>validator-mounted-false:1732615894706=>init-complete:1732615894707=>onsubmit-fired:1732615897940=>observerSubmitHandler_received-submit-event:1732615897941=>submit-validation-passed:1732615897942=>observerSubmitHandler_validation-passed-submitting-form:1732615897944","submitSource":"form","buildDate":"1732271172685","uploadServerUrl":"https:\/\/upload.jotform.com\/upload","eventObserver":"1","q3_studentsName":{"first":"Niloth","last":"P"},"q33_typeOf":"Online Tutoring","q34_subjectFor":"Math","q35_grade":"12","q28_appointment":{"implementation":"new","date":"","duration":"60","timezone":"Asia\/Calcutta (GMT+06:30)"},"timeToSubmit":"3","preview":"true","validatedNewRequiredFieldIDs":"{\"new\":1}","path":"\/submit\/243231271343446"}
--------------------------J1JNT4vbVO4FCC1qLG4HSv
Content-Disposition: form-data; name="fromTable"
--------------------------J1JNT4vbVO4FCC1qLG4HSv
Content-Disposition: form-data; name="appID"
--------------------------J1JNT4vbVO4FCC1qLG4HSv
Content-Disposition: form-data; name="pretty"
Student's Name:Niloth P, Type of Tutoring:Online Tutoring, Subject for Tutoring:Math, Grade:12
--------------------------J1JNT4vbVO4FCC1qLG4HSv
Content-Disposition: form-data; name="unread"
--------------------------J1JNT4vbVO4FCC1qLG4HSv
Content-Disposition: form-data; name="parent"
--------------------------J1JNT4vbVO4FCC1qLG4HSv
Content-Disposition: form-data; name="ip"
27.51.18.17
--------------------------J1JNT4vbVO4FCC1qLG4HSv--

View File

@@ -1,4 +1,7 @@
from typing_extensions import override
from zerver.lib.test_classes import WebhookTestCase
from zerver.lib.webhooks.common import parse_multipart_string
class JotformHookTests(WebhookTestCase):
@@ -7,15 +10,25 @@ class JotformHookTests(WebhookTestCase):
WEBHOOK_DIR_NAME = "jotform"
def test_response(self) -> None:
expected_title = "Form"
expected_title = "Tutor Appointment Form"
expected_message = """
* **Name**: Gaurav Pandey
* **Address**: Lampgarden-street wolfsquare Bengaluru Karnataka 165578
* **Signature**: uploads/gauravguitarrocks/202944822449057/4791133489169827307/4791133489169827307_signature_4.png""".strip()
* **Student's Name**: Niloth P
* **Type of Tutoring**: Online Tutoring
* **Subject for Tutoring**: Math
* **Grade**: 12""".strip()
self.check_webhook(
"response",
expected_title,
expected_message,
content_type="application/x-www-form-urlencoded",
content_type="multipart/form-data",
)
def test_bad_payload(self) -> None:
with self.assertRaisesRegex(AssertionError, "Unable to handle Jotform payload"):
self.check_webhook("response")
@override
def get_payload(self, fixture_name: str) -> dict[str, str]:
body = self.webhook_fixture_data("jotform", fixture_name, file_type="multipart")
return parse_multipart_string(body)

View File

@@ -1,29 +1,33 @@
# Webhooks for external integrations.
from django.http import HttpRequest, HttpResponse
from django.utils.translation import gettext as _
from zerver.decorator import webhook_view
from zerver.lib.exceptions import JsonableError
from zerver.lib.response import json_success
from zerver.lib.typed_endpoint import JsonBodyPayload, typed_endpoint
from zerver.lib.validator import WildValue, check_string
from zerver.lib.typed_endpoint import typed_endpoint_without_parameters
from zerver.lib.webhooks.common import check_send_webhook_message
from zerver.models import UserProfile
@webhook_view("Jotform")
@typed_endpoint
@typed_endpoint_without_parameters
def api_jotform_webhook(
request: HttpRequest,
user_profile: UserProfile,
*,
payload: JsonBodyPayload[WildValue],
) -> HttpResponse:
topic_name = payload["formTitle"].tame(check_string)
fields = payload["pretty"].tame(check_string).split(", ")
payload = request.POST
topic_name = payload.get("formTitle")
fields = payload.get("pretty", "").split(", ")
if not topic_name or not fields:
raise JsonableError(_("Unable to handle Jotform payload"))
form_response = ""
for field in fields:
label, value = field.split(":", 1)
# TODO: Add fixtures and tests for question-like fields and files
separator = " " if label.endswith("?") else ": "
form_response += f"* **{label}**{separator}{value}\n"
message = form_response.strip()