Skip to content

Commit 8db806c

Browse files
committed
integrations: Add ClickUp integration.
Creates an incoming webhook integration for ClickUp. The main use case is getting notifications when new ClickUp items such as task, list, folder, space, goals are created, updated or deleted. Fixes zulip#26529.
1 parent 15cec69 commit 8db806c

37 files changed

+1528
-1
lines changed
Loading
26.6 KB
Loading
Lines changed: 1 addition & 0 deletions
Loading

zerver/lib/integrations.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
from django.utils.translation import gettext_lazy
99
from django_stubs_ext import StrPromise
1010
from typing_extensions import TypeAlias
11-
1211
from zerver.lib.storage import static_path
1312

1413
"""This module declares all of the (documented) integrations available
@@ -375,6 +374,7 @@ def __init__(self, name: str, *args: Any, **kwargs: Any) -> None:
375374
WebhookIntegration("buildbot", ["continuous-integration"], display_name="Buildbot"),
376375
WebhookIntegration("canarytoken", ["monitoring"], display_name="Thinkst Canarytokens"),
377376
WebhookIntegration("circleci", ["continuous-integration"], display_name="CircleCI"),
377+
WebhookIntegration("clickup", ["project-management"], display_name="ClickUp"),
378378
WebhookIntegration("clubhouse", ["project-management"]),
379379
WebhookIntegration("codeship", ["continuous-integration", "deployment"]),
380380
WebhookIntegration("crashlytics", ["monitoring"]),
@@ -735,6 +735,7 @@ def __init__(self, name: str, *args: Any, **kwargs: Any) -> None:
735735
ScreenshotConfig("bitbucket_job_completed.json", image_name="001.png"),
736736
ScreenshotConfig("github_job_completed.json", image_name="002.png"),
737737
],
738+
"clickup": [ScreenshotConfig("task_created.json")],
738739
"clubhouse": [ScreenshotConfig("story_create.json")],
739740
"codeship": [ScreenshotConfig("error_build.json")],
740741
"crashlytics": [ScreenshotConfig("issue_message.json")],

zerver/webhooks/clickup/__init__.py

Whitespace-only changes.
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
from typing import Any, Dict
2+
3+
import requests
4+
from urllib.parse import urljoin
5+
from zerver.lib.outgoing_http import OutgoingSession
6+
7+
8+
class Error(Exception):
9+
pass
10+
11+
12+
class APIUnavailableError(Error):
13+
pass
14+
15+
16+
class BadRequestError(Error):
17+
pass
18+
19+
20+
class ClickUpSession(OutgoingSession):
21+
def __init__(self, **kwargs: Any) -> None:
22+
super().__init__(role="clickup", timeout=5, **kwargs)
23+
24+
25+
def make_clickup_request(path: str, api_key: str) -> Dict[str, Any]:
26+
headers: Dict[str, str] = {
27+
"Content-Type": "application/json",
28+
"Authorization": api_key,
29+
}
30+
31+
try:
32+
base_url = "https://api.clickup.com/api/v2/"
33+
api_endpoint = urljoin(base_url, path)
34+
response = ClickUpSession(headers=headers).get(
35+
api_endpoint,
36+
)
37+
response.raise_for_status()
38+
except (requests.ConnectionError, requests.Timeout) as e:
39+
raise APIUnavailableError from e
40+
except requests.HTTPError as e:
41+
raise BadRequestError from e
42+
43+
return response.json()
44+
45+
46+
def get_list(api_key: str, list_id: str) -> Dict[str, Any]:
47+
data = make_clickup_request(f"list/{list_id}", api_key)
48+
return data
49+
50+
51+
def get_task(api_key: str, task_id: str) -> Dict[str, Any]:
52+
data = make_clickup_request(f"task/{task_id}", api_key)
53+
return data
54+
55+
56+
def get_folder(api_key: str, folder_id: str) -> Dict[str, Any]:
57+
data = make_clickup_request(f"folder/{folder_id}", api_key)
58+
return data
59+
60+
61+
def get_goal(api_key: str, goal_id: str) -> Dict[str, Any]:
62+
data = make_clickup_request(f"goal/{goal_id}", api_key)
63+
return data
64+
65+
66+
def get_space(api_key: str, space_id: str) -> Dict[str, Any]:
67+
data = make_clickup_request(f"space/{space_id}", api_key)
68+
return data
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"id": "457",
3+
"name": "Lord Foldemort",
4+
"orderindex": 0,
5+
"override_statuses": false,
6+
"hidden": false,
7+
"space": {
8+
"id": "789",
9+
"name": "Space Name",
10+
"access": true
11+
},
12+
"task_count": "0",
13+
"lists": []
14+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
{
2+
"goal": {
3+
"id": "e53a033c-900e-462d-a849-4a216b06d930",
4+
"name": "hat-trick",
5+
"team_id": "512",
6+
"date_created": "1568044355026",
7+
"start_date": null,
8+
"due_date": "1568036964079",
9+
"description": "Updated Goal Description",
10+
"private": false,
11+
"archived": false,
12+
"creator": 183,
13+
"color": "#32a852",
14+
"pretty_id": "6",
15+
"multiple_owners": true,
16+
"folder_id": null,
17+
"members": [],
18+
"owners": [
19+
{
20+
"id": 182,
21+
"username": "Pieter CK",
22+
"email": "[email protected]",
23+
"color": "#7b68ee",
24+
"initials": "PK",
25+
"profilePicture": "https://attachments-public.clickup.com/profilePictures/182_abc.jpg"
26+
}
27+
],
28+
"key_results": [],
29+
"percent_completed": 0,
30+
"history": [],
31+
"pretty_url": "https://app.clickup.com/512/goals/6"
32+
}
33+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
{
2+
"id": "124",
3+
"name": "List-an al Gaib",
4+
"orderindex": 1,
5+
"content": "Updated List Content",
6+
"status": {
7+
"status": "red",
8+
"color": "#e50000",
9+
"hide_label": true
10+
},
11+
"priority": {
12+
"priority": "high",
13+
"color": "#f50000"
14+
},
15+
"assignee": null,
16+
"due_date": "1567780450202",
17+
"due_date_time": true,
18+
"start_date": null,
19+
"start_date_time": null,
20+
"folder": {
21+
"id": "456",
22+
"name": "Folder Name",
23+
"hidden": false,
24+
"access": true
25+
},
26+
"space": {
27+
"id": "789",
28+
"name": "Space Name",
29+
"access": true
30+
},
31+
"inbound_address": "add.task.124.ac725f.31518a6a-05bb-4997-92a6-1dcfe2f527ca@tasks.clickup.com",
32+
"archived": false,
33+
"override_statuses": false,
34+
"statuses": [
35+
{
36+
"status": "to do",
37+
"orderindex": 0,
38+
"color": "#d3d3d3",
39+
"type": "open"
40+
},
41+
{
42+
"status": "complete",
43+
"orderindex": 1,
44+
"color": "#6bc950",
45+
"type": "closed"
46+
}
47+
],
48+
"permission_level": "create"
49+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
{
2+
"id": "790",
3+
"name": "the Milky Way",
4+
"private": false,
5+
"statuses": [
6+
{
7+
"status": "to do",
8+
"type": "open",
9+
"orderindex": 0,
10+
"color": "#d3d3d3"
11+
},
12+
{
13+
"status": "complete",
14+
"type": "closed",
15+
"orderindex": 1,
16+
"color": "#6bc950"
17+
}
18+
],
19+
"multiple_assignees": false,
20+
"features": {
21+
"due_dates": {
22+
"enabled": false,
23+
"start_date": false,
24+
"remap_due_dates": false,
25+
"remap_closed_due_date": false
26+
},
27+
"time_tracking": {
28+
"enabled": false
29+
},
30+
"tags": {
31+
"enabled": false
32+
},
33+
"time_estimates": {
34+
"enabled": false
35+
},
36+
"checklists": {
37+
"enabled": true
38+
},
39+
"custom_fields": {
40+
"enabled": true
41+
},
42+
"remap_dependencies": {
43+
"enabled": false
44+
},
45+
"dependency_warning": {
46+
"enabled": false
47+
},
48+
"portfolios": {
49+
"enabled": false
50+
}
51+
}
52+
}

0 commit comments

Comments
 (0)