dex-connector-clickup 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ Metadata-Version: 2.5
2
+ Name: dex-connector-clickup
3
+ Version: 0.1.0
4
+ Summary: ClickUp connector for Dex Flows
5
+ License-Expression: MIT
6
+ License-File: LICENSE
7
+ Requires-Python: >=3.11
8
+ Requires-Dist: dex-connectors-sdk==0.1.0
9
+ Requires-Dist: dex-python-sdk>=1.28.0
10
+ Requires-Dist: httpx>=0.27
11
+ Description-Content-Type: text/markdown
12
+
13
+ # ClickUp Python connector
14
+
15
+ The package implements the ClickUp `searchTasks`, `getTask`, `createTask`, `updateTask`, `updateTaskTags`, `addComment`, and `findMemberByEmail` operations for Dex Flows. The `taskEvent` webhook Trigger remains Go-only. Install `dex-connector-clickup==0.1.0` alongside a Dex Python application and load its named connection from Dex project configuration. The connection uses a ClickUp personal API token and resolves it before each provider request.
16
+
17
+ The [getTask runnable example](examples/get_task/README.md) uses a static `clickup-workspace` connection and a real Dex Worker and Client. Its integration test exercises a retry and an unwired rejection branch against a local ClickUp stand-in. Run from this directory with `uv sync --locked`, then `uv run pytest tests/ examples/get_task/integration_test.py` while `dexcli dev` is running and `DEX_FLOW_SERVICE_ADDRESS` points to it.
18
+
19
+ `createTask` and `addComment` store a Dex heartbeat checkpoint before an unkeyed write. If a retry sees the checkpoint, it reads back instead of sending the write again; an unconfirmable outcome selects `uncertain`. The provider has no idempotency-key support for these calls. The Python package currently has no live ClickUp authorization or API verification.
20
+
21
+ `getTask` returns custom field JSON values only when Python can represent their numbers exactly. A value that would lose precision selects `invalidResponse` and identifies the field. `updateTaskTags` bounds its sequence of provider requests to 25 seconds; an incomplete sequence retries.
@@ -0,0 +1,21 @@
1
+ dex_connectors/clickup/__init__.py,sha256=Eaaa0BjxIgjX3WTXHyLXRh60Sz9fpAsiXza2qoZ2zdg,2707
2
+ dex_connectors/clickup/client.py,sha256=1_ed8nbOiW5T1mhjJdXrQP5ZIX0NvTR-YJ4y8qTBFiU,23257
3
+ dex_connectors/clickup/connection.py,sha256=wFi4Yucb6tYGqUKV6MLyNTV9np84kd-mpiOYRGB1lkw,424
4
+ dex_connectors/clickup/dex-connector.json,sha256=cKiQD2qoPB4xT8ktLsuIPWdL2R4JHPdDt1rcXrJ7hcU,6419
5
+ dex_connectors/clickup/models.py,sha256=kp2R3g5rCEDwyilbmOsRoVIVX-3RPDGgY5H5X7fDJRY,1380
6
+ dex_connectors/clickup/operations.py,sha256=M8SCVE5Mz-2eq5mbL5bZy7ZnRDKgm5AlE6X8Tn0G6yc,41564
7
+ dex_connectors/clickup/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
+ dex_connectors/clickup/steps.py,sha256=ngOlQdc2Qvb9uzhmdlBZOWIJwUTMaIC04tHOOJ4zNj0,667
9
+ dex_connectors/clickup/_generated/__init__.py,sha256=ShfMsAGzSt4xuhCaf20ibgtfXl4pXomZb00ovUw6CeI,66
10
+ dex_connectors/clickup/_generated/config.py,sha256=A2ygvyxiyu7X-WQ2iBV7jEdItNlSNPhtxniVelSGrNA,544
11
+ dex_connectors/clickup/_generated/connection.py,sha256=EN4J1dYeizOhLonHHpitihD6QooRw9hGWdlccUMTOAI,8997
12
+ dex_connectors/clickup/_generated/definitions.py,sha256=dMjE5iVMbL31H-1_uTPH0e0ePD8vjPbR4I_Wu4CCPug,7435
13
+ dex_connectors/clickup/_generated/mock.py,sha256=h1su5PqovNUOd0I1cCY8o2pxHOrQkaXntz7WesaTxXc,24792
14
+ dex_connectors/clickup/_generated/models.py,sha256=_lUiEUxgzn0i4MhTMbSEpxcWcdYWIngmirRLuBg9hAg,24681
15
+ dex_connectors/clickup/_generated/steps.py,sha256=59KdwrQNzAfe7RrmBLEUXV3D9Ssx1ss89kXV_Z0WJe4,18804
16
+ dex_connectors/clickup/_generated/timestamps.py,sha256=Z8VwrE60w-33InIMgEMLiMPTFyRjj6oBq8tUro8Ssi8,3164
17
+ dex_connectors/clickup/clickupmock/__init__.py,sha256=sEODfqQ6BJ0_cxRmH7FUHlzuhGC26jntpGD0cmUtm_U,167
18
+ dex_connector_clickup-0.1.0.dist-info/METADATA,sha256=mQs1D691Ivgp2Tl8kKVMp4-i7bXY2_KCPTFKaXOIbzg,1888
19
+ dex_connector_clickup-0.1.0.dist-info/WHEEL,sha256=2iZrJkDIkeDh0k9SikKMqFuRP-ua6mb4Ir1AsER4BEw,87
20
+ dex_connector_clickup-0.1.0.dist-info/licenses/LICENSE,sha256=WNj87Zd5micrAShl1LujOPM6PDzDqjlNLrryDZ4hE0M,1070
21
+ dex_connector_clickup-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.33.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Super Durable
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,71 @@
1
+ # Copyright (c) 2026 Super Durable
2
+ # SPDX-License-Identifier: MIT
3
+ """Dex-native ClickUp task operations."""
4
+
5
+ from .client import Client as Client
6
+ from .client import Config as Config
7
+ from .client import Credentials as Credentials
8
+ from .connection import Connection as Connection
9
+ from .connection import new_connection as new_connection
10
+ from .connection import new_connection_with_operations as new_connection_with_operations
11
+ from .connection import new_project_connection as new_project_connection
12
+ from .models import AddCommentInput as AddCommentInput
13
+ from .models import AddCommentOutput as AddCommentOutput
14
+ from .models import CreateTaskInput as CreateTaskInput
15
+ from .models import CreateTaskOutput as CreateTaskOutput
16
+ from .models import CustomFieldInput as CustomFieldInput
17
+ from .models import CustomFieldValue as CustomFieldValue
18
+ from .models import FindMemberByEmailInput as FindMemberByEmailInput
19
+ from .models import GetTaskInput as GetTaskInput
20
+ from .models import Member as Member
21
+ from .models import SearchTasksInput as SearchTasksInput
22
+ from .models import SearchTasksOutput as SearchTasksOutput
23
+ from .models import Task as Task
24
+ from .models import TaskStatus as TaskStatus
25
+ from .models import UpdateTaskInput as UpdateTaskInput
26
+ from .models import UpdateTaskOutput as UpdateTaskOutput
27
+ from .models import UpdateTaskTagsInput as UpdateTaskTagsInput
28
+ from .models import UpdateTaskTagsOutput as UpdateTaskTagsOutput
29
+ from .models import User as User
30
+ from .steps import new_add_comment_step as new_add_comment_step
31
+ from .steps import new_create_task_step as new_create_task_step
32
+ from .steps import new_find_member_by_email_step as new_find_member_by_email_step
33
+ from .steps import new_get_task_step as new_get_task_step
34
+ from .steps import new_search_tasks_step as new_search_tasks_step
35
+ from .steps import new_update_task_step as new_update_task_step
36
+ from .steps import new_update_task_tags_step as new_update_task_tags_step
37
+
38
+ __all__ = [
39
+ "AddCommentInput",
40
+ "AddCommentOutput",
41
+ "Client",
42
+ "Config",
43
+ "Connection",
44
+ "CreateTaskInput",
45
+ "CreateTaskOutput",
46
+ "Credentials",
47
+ "CustomFieldInput",
48
+ "CustomFieldValue",
49
+ "FindMemberByEmailInput",
50
+ "GetTaskInput",
51
+ "Member",
52
+ "SearchTasksInput",
53
+ "SearchTasksOutput",
54
+ "Task",
55
+ "TaskStatus",
56
+ "UpdateTaskInput",
57
+ "UpdateTaskOutput",
58
+ "UpdateTaskTagsInput",
59
+ "UpdateTaskTagsOutput",
60
+ "User",
61
+ "new_add_comment_step",
62
+ "new_connection",
63
+ "new_connection_with_operations",
64
+ "new_create_task_step",
65
+ "new_find_member_by_email_step",
66
+ "new_get_task_step",
67
+ "new_project_connection",
68
+ "new_search_tasks_step",
69
+ "new_update_task_step",
70
+ "new_update_task_tags_step",
71
+ ]
@@ -0,0 +1,2 @@
1
+ # Copyright (c) 2026 Super Durable
2
+ # SPDX-License-Identifier: MIT
@@ -0,0 +1,25 @@
1
+ # Copyright (c) 2026 Super Durable
2
+ # SPDX-License-Identifier: MIT
3
+ # ruff: noqa
4
+
5
+ from dataclasses import dataclass
6
+
7
+ from dex_connectors.sdk import SecretString
8
+
9
+
10
+ @dataclass(frozen=True, slots=True)
11
+ class Config:
12
+
13
+ max_response_bytes: int = 4194304
14
+ webhook_max_body_bytes: int = 1048576
15
+
16
+ setattr(Config, '__module__', 'dex_connectors.clickup.client')
17
+
18
+
19
+ @dataclass(frozen=True, slots=True)
20
+ class Credentials:
21
+
22
+ api_token: SecretString
23
+ webhook_secret: SecretString
24
+
25
+ setattr(Credentials, '__module__', 'dex_connectors.clickup.client')
@@ -0,0 +1,175 @@
1
+ # Copyright (c) 2026 Super Durable
2
+ # SPDX-License-Identifier: MIT
3
+ # ruff: noqa
4
+
5
+ from typing import Any
6
+
7
+ import httpx
8
+ from dex_connectors.sdk import ConnectionRef, Mutation, Query, Call, SecretString
9
+ from dex_connectors.sdk.projectconfig import ConnectionKey, LoadedProject
10
+
11
+ from ..client import Client, Config, Credentials
12
+ from .definitions import CONNECTOR_ID, ADD_COMMENT_DEFINITION, CREATE_TASK_DEFINITION, FIND_MEMBER_BY_EMAIL_DEFINITION, GET_TASK_DEFINITION, SEARCH_TASKS_DEFINITION, UPDATE_TASK_DEFINITION, UPDATE_TASK_TAGS_DEFINITION
13
+ from ..models import AddCommentInput, AddCommentOutput, CreateTaskInput, CreateTaskOutput, FindMemberByEmailInput, GetTaskInput, Member, SearchTasksInput, SearchTasksOutput, Task, UpdateTaskInput, UpdateTaskOutput, UpdateTaskTagsInput, UpdateTaskTagsOutput
14
+
15
+ class Connection:
16
+
17
+ __slots__ = ('_search_tasks', '_get_task', '_create_task', '_update_task', '_update_task_tags', '_add_comment', '_find_member_by_email', '_client', 'reference')
18
+
19
+ def __init__(self,
20
+ reference: ConnectionRef,
21
+ search_tasks: Query[SearchTasksInput, SearchTasksOutput],
22
+ get_task: Query[GetTaskInput, Task],
23
+ create_task: Mutation[CreateTaskInput, CreateTaskOutput],
24
+ update_task: Mutation[UpdateTaskInput, UpdateTaskOutput],
25
+ update_task_tags: Mutation[UpdateTaskTagsInput, UpdateTaskTagsOutput],
26
+ add_comment: Mutation[AddCommentInput, AddCommentOutput],
27
+ find_member_by_email: Query[FindMemberByEmailInput, Member],
28
+ client: Client | None = None,
29
+ ) -> None:
30
+ reference.validate()
31
+ if reference.provider != CONNECTOR_ID or search_tasks is None or get_task is None or create_task is None or update_task is None or update_task_tags is None or add_comment is None or find_member_by_email is None:
32
+ raise ValueError("ClickUp connection provider and operations are required")
33
+ if search_tasks.definition().operation != SEARCH_TASKS_DEFINITION.operation:
34
+ raise ValueError("ClickUp connection operation identity differs")
35
+ if get_task.definition().operation != GET_TASK_DEFINITION.operation:
36
+ raise ValueError("ClickUp connection operation identity differs")
37
+ if create_task.definition().operation != CREATE_TASK_DEFINITION.operation:
38
+ raise ValueError("ClickUp connection operation identity differs")
39
+ if update_task.definition().operation != UPDATE_TASK_DEFINITION.operation:
40
+ raise ValueError("ClickUp connection operation identity differs")
41
+ if update_task_tags.definition().operation != UPDATE_TASK_TAGS_DEFINITION.operation:
42
+ raise ValueError("ClickUp connection operation identity differs")
43
+ if add_comment.definition().operation != ADD_COMMENT_DEFINITION.operation:
44
+ raise ValueError("ClickUp connection operation identity differs")
45
+ if find_member_by_email.definition().operation != FIND_MEMBER_BY_EMAIL_DEFINITION.operation:
46
+ raise ValueError("ClickUp connection operation identity differs")
47
+ self.reference = reference
48
+ self._search_tasks = search_tasks
49
+ self._get_task = get_task
50
+ self._create_task = create_task
51
+ self._update_task = update_task
52
+ self._update_task_tags = update_task_tags
53
+ self._add_comment = add_comment
54
+ self._find_member_by_email = find_member_by_email
55
+ self._client = client
56
+
57
+ def search_tasks(self) -> Query[SearchTasksInput, SearchTasksOutput]:
58
+ return self._search_tasks
59
+
60
+ def get_task(self) -> Query[GetTaskInput, Task]:
61
+ return self._get_task
62
+
63
+ def create_task(self) -> Mutation[CreateTaskInput, CreateTaskOutput]:
64
+ return self._create_task
65
+
66
+ def update_task(self) -> Mutation[UpdateTaskInput, UpdateTaskOutput]:
67
+ return self._update_task
68
+
69
+ def update_task_tags(self) -> Mutation[UpdateTaskTagsInput, UpdateTaskTagsOutput]:
70
+ return self._update_task_tags
71
+
72
+ def add_comment(self) -> Mutation[AddCommentInput, AddCommentOutput]:
73
+ return self._add_comment
74
+
75
+ def find_member_by_email(self) -> Query[FindMemberByEmailInput, Member]:
76
+ return self._find_member_by_email
77
+
78
+ async def aclose(self) -> None:
79
+ if self._client is not None:
80
+ await self._client.aclose()
81
+
82
+ def __repr__(self) -> str:
83
+ return "clickup.Connection([REDACTED])"
84
+
85
+ def __reduce__(self) -> tuple[object, ...]:
86
+ raise TypeError("ClickUp connections cannot be serialized")
87
+
88
+ setattr(Connection, '__module__', 'dex_connectors.clickup.connection')
89
+
90
+
91
+ def new_connection(client: Client, reference: ConnectionRef) -> Connection:
92
+ if client is None:
93
+ raise ValueError("ClickUp client is required")
94
+ return Connection(reference, client.search_tasks(), client.get_task(), client.create_task(), client.update_task(), client.update_task_tags(), client.add_comment(), client.find_member_by_email(), client)
95
+
96
+ setattr(new_connection, '__module__', 'dex_connectors.clickup.connection')
97
+
98
+
99
+ def new_connection_with_operations(
100
+ search_tasks: Query[SearchTasksInput, SearchTasksOutput],
101
+ get_task: Query[GetTaskInput, Task],
102
+ create_task: Mutation[CreateTaskInput, CreateTaskOutput],
103
+ update_task: Mutation[UpdateTaskInput, UpdateTaskOutput],
104
+ update_task_tags: Mutation[UpdateTaskTagsInput, UpdateTaskTagsOutput],
105
+ add_comment: Mutation[AddCommentInput, AddCommentOutput],
106
+ find_member_by_email: Query[FindMemberByEmailInput, Member],
107
+ reference: ConnectionRef,
108
+ ) -> Connection:
109
+ return Connection(reference, search_tasks, get_task, create_task, update_task, update_task_tags, add_comment, find_member_by_email)
110
+
111
+ setattr(new_connection_with_operations, '__module__', 'dex_connectors.clickup.connection')
112
+
113
+
114
+ class _ProjectCredentialSource:
115
+
116
+ def __init__(self, project: LoadedProject, key: ConnectionKey) -> None:
117
+ self.project = project
118
+ self.key = key
119
+
120
+ async def resolve(self, call: Call) -> Credentials:
121
+ material = await self.project.resolve_credential(self.key, str(call.id))
122
+ if not isinstance(material, dict):
123
+ raise ValueError("ClickUp stored credential does not match the connector credential fields")
124
+ if set(material) - set(('api_token', 'webhook_secret')):
125
+ raise ValueError("ClickUp stored credential does not match the connector credential fields")
126
+ api_token = material.get("api_token")
127
+ if api_token is not None and not (isinstance(api_token, str)):
128
+ raise ValueError("ClickUp credential api_token has an invalid type")
129
+ webhook_secret = material.get("webhook_secret")
130
+ if webhook_secret is not None and not (isinstance(webhook_secret, str)):
131
+ raise ValueError("ClickUp credential webhook_secret has an invalid type")
132
+ if not api_token:
133
+ raise ValueError("ClickUp credential api_token is required")
134
+ return Credentials(SecretString(api_token), SecretString(webhook_secret or ''))
135
+
136
+
137
+ def new_project_connection(
138
+ project: LoadedProject, connection_name: str, http_client: httpx.AsyncClient | None = None
139
+ ) -> Connection:
140
+ if project is None:
141
+ raise ValueError("ClickUp connection requires loaded project configuration")
142
+ reference = ConnectionRef(CONNECTOR_ID, connection_name)
143
+ reference.validate()
144
+ key = ConnectionKey(CONNECTOR_ID, connection_name)
145
+ matching = [
146
+ entry
147
+ for entry in project.configuration.connections
148
+ if entry.connector_id == CONNECTOR_ID and entry.connection_name == connection_name
149
+ ]
150
+ if len(matching) != 1:
151
+ raise ValueError("ClickUp project connection is missing or duplicated")
152
+ if matching[0].provider != CONNECTOR_ID:
153
+ raise ValueError("ClickUp project connection provider differs")
154
+ config = _decode_config(matching[0].configuration)
155
+ client = Client(config, _ProjectCredentialSource(project, key), http_client)
156
+ return new_connection(client, reference)
157
+
158
+ setattr(new_project_connection, '__module__', 'dex_connectors.clickup.connection')
159
+
160
+
161
+ def _decode_config(value: dict[str, Any]) -> Config:
162
+ if set(value) - {"maxResponseBytes", "webhookMaxBodyBytes"}:
163
+ raise ValueError("ClickUp configuration has unknown fields")
164
+ config_arguments: dict[str, Any] = {}
165
+ if "maxResponseBytes" in value and value["maxResponseBytes"] != '':
166
+ max_response_bytes = value["maxResponseBytes"]
167
+ if not (type(max_response_bytes) is int):
168
+ raise ValueError("ClickUp configuration maxResponseBytes has an invalid type")
169
+ config_arguments['max_response_bytes'] = max_response_bytes
170
+ if "webhookMaxBodyBytes" in value and value["webhookMaxBodyBytes"] != '':
171
+ webhook_max_body_bytes = value["webhookMaxBodyBytes"]
172
+ if not (type(webhook_max_body_bytes) is int):
173
+ raise ValueError("ClickUp configuration webhookMaxBodyBytes has an invalid type")
174
+ config_arguments['webhook_max_body_bytes'] = webhook_max_body_bytes
175
+ return Config(**config_arguments)
@@ -0,0 +1,147 @@
1
+ # Copyright (c) 2026 Super Durable
2
+ # SPDX-License-Identifier: MIT
3
+ # ruff: noqa
4
+
5
+ from datetime import timedelta
6
+
7
+ from dex_connectors.sdk import (
8
+ BranchDefinition,
9
+ MutationDefinition,
10
+ OperationRef,
11
+ QueryDefinition,
12
+ RetryPolicy,
13
+ StepDefaults,
14
+ default_connector_step_execute_retry_policy,
15
+ )
16
+
17
+ CONNECTOR_ID = "clickup"
18
+ PROVIDER = "clickup"
19
+ SEARCH_TASKS_OPERATION_ID = "searchTasks"
20
+ GET_TASK_OPERATION_ID = "getTask"
21
+ CREATE_TASK_OPERATION_ID = "createTask"
22
+ UPDATE_TASK_OPERATION_ID = "updateTask"
23
+ UPDATE_TASK_TAGS_OPERATION_ID = "updateTaskTags"
24
+ ADD_COMMENT_OPERATION_ID = "addComment"
25
+ FIND_MEMBER_BY_EMAIL_OPERATION_ID = "findMemberByEmail"
26
+ ADDED = "added"
27
+ CREATED = "created"
28
+ DEFECT = "defect"
29
+ FOUND = "found"
30
+ INVALID_RESPONSE = "invalidResponse"
31
+ NOT_FOUND = "notFound"
32
+ PROVIDER_REJECTED = "providerRejected"
33
+ SEARCHED = "searched"
34
+ UNCERTAIN = "uncertain"
35
+ UPDATED = "updated"
36
+
37
+ SEARCH_TASKS_DEFINITION = QueryDefinition(
38
+ OperationRef(CONNECTOR_ID, SEARCH_TASKS_OPERATION_ID),
39
+ (
40
+ BranchDefinition(SEARCHED, "One page of matching tasks was read; it can be empty.", False),
41
+ BranchDefinition(NOT_FOUND, "The Workspace does not exist or the token's user is not a member of it.", True),
42
+ BranchDefinition(PROVIDER_REJECTED, "ClickUp conclusively rejected the search, such as an invalid filter or an invalid token.", True),
43
+ BranchDefinition(INVALID_RESPONSE, "ClickUp returned an invalid or oversized page of tasks.", True),
44
+ BranchDefinition(DEFECT, "Local input, connection configuration, or connector definition is invalid.", True),
45
+ ),
46
+ StepDefaults(
47
+ timedelta(seconds=30),
48
+ 2,
49
+ execute_retry=default_connector_step_execute_retry_policy(),
50
+ ),
51
+ )
52
+
53
+ GET_TASK_DEFINITION = QueryDefinition(
54
+ OperationRef(CONNECTOR_ID, GET_TASK_OPERATION_ID),
55
+ (
56
+ BranchDefinition(FOUND, "The task was read.", False),
57
+ BranchDefinition(NOT_FOUND, "The task does not exist or the connection cannot see it.", True),
58
+ BranchDefinition(PROVIDER_REJECTED, "ClickUp conclusively rejected the task read.", True),
59
+ BranchDefinition(INVALID_RESPONSE, "ClickUp returned an invalid or oversized task.", True),
60
+ BranchDefinition(DEFECT, "Local input, connection configuration, or connector definition is invalid.", True),
61
+ ),
62
+ StepDefaults(
63
+ timedelta(seconds=30),
64
+ 2,
65
+ execute_retry=default_connector_step_execute_retry_policy(),
66
+ ),
67
+ )
68
+
69
+ CREATE_TASK_DEFINITION = MutationDefinition(
70
+ OperationRef(CONNECTOR_ID, CREATE_TASK_OPERATION_ID),
71
+ (
72
+ BranchDefinition(CREATED, "ClickUp created the task; a retried attempt that found its own earlier task in the List reports it instead of creating another.", False),
73
+ BranchDefinition(NOT_FOUND, "The List does not exist or the connection cannot see it; nothing was created.", True),
74
+ BranchDefinition(PROVIDER_REJECTED, "ClickUp conclusively rejected the task, such as an unknown status, assignee, or custom field value, or missing permission; nothing was created.", True),
75
+ BranchDefinition(UNCERTAIN, "An earlier attempt sent the create but its outcome cannot be confirmed and the List shows no single matching task, so the task may exist; the connector never sends it again.", True),
76
+ BranchDefinition(DEFECT, "Local input, connection configuration, or connector definition is invalid.", True),
77
+ ),
78
+ StepDefaults(
79
+ timedelta(seconds=30),
80
+ 1,
81
+ execute_retry=RetryPolicy(timedelta(seconds=1), 2, timedelta(seconds=60), 5, timedelta(seconds=300)),
82
+ ),
83
+ )
84
+
85
+ UPDATE_TASK_DEFINITION = MutationDefinition(
86
+ OperationRef(CONNECTOR_ID, UPDATE_TASK_OPERATION_ID),
87
+ (
88
+ BranchDefinition(UPDATED, "ClickUp applied every requested change, including when the task already had those values, and the Value holds the task it returned.", False),
89
+ BranchDefinition(NOT_FOUND, "The task does not exist or the connection cannot see it.", True),
90
+ BranchDefinition(PROVIDER_REJECTED, "ClickUp conclusively rejected the change, such as a status that is not in the task's List or missing permission.", True),
91
+ BranchDefinition(INVALID_RESPONSE, "ClickUp returned an invalid or oversized task after the update, so the change may have been applied.", True),
92
+ BranchDefinition(DEFECT, "Local input, connection configuration, or connector definition is invalid.", True),
93
+ ),
94
+ StepDefaults(
95
+ timedelta(seconds=30),
96
+ 2,
97
+ execute_retry=default_connector_step_execute_retry_policy(),
98
+ ),
99
+ )
100
+
101
+ UPDATE_TASK_TAGS_DEFINITION = MutationDefinition(
102
+ OperationRef(CONNECTOR_ID, UPDATE_TASK_TAGS_OPERATION_ID),
103
+ (
104
+ BranchDefinition(UPDATED, "ClickUp applied every tag change, and the Value holds the task's tags read back.", False),
105
+ BranchDefinition(NOT_FOUND, "The task does not exist or the connection cannot see it.", True),
106
+ BranchDefinition(PROVIDER_REJECTED, "ClickUp conclusively rejected a tag change, such as missing permission; earlier changes in the same Step may have been applied.", True),
107
+ BranchDefinition(INVALID_RESPONSE, "ClickUp returned an invalid or oversized task when its tags were read back; the tag changes were applied.", True),
108
+ BranchDefinition(DEFECT, "Local input, connection configuration, or connector definition is invalid.", True),
109
+ ),
110
+ StepDefaults(
111
+ timedelta(seconds=30),
112
+ 2,
113
+ execute_retry=default_connector_step_execute_retry_policy(),
114
+ ),
115
+ )
116
+
117
+ ADD_COMMENT_DEFINITION = MutationDefinition(
118
+ OperationRef(CONNECTOR_ID, ADD_COMMENT_OPERATION_ID),
119
+ (
120
+ BranchDefinition(ADDED, "The comment is on the task; a retried attempt that found its own earlier comment reports it instead of adding another.", False),
121
+ BranchDefinition(NOT_FOUND, "The task does not exist or the connection cannot see it; nothing was added.", True),
122
+ BranchDefinition(PROVIDER_REJECTED, "ClickUp conclusively rejected the comment, such as missing permission; nothing was added.", True),
123
+ BranchDefinition(UNCERTAIN, "An earlier attempt sent the comment but its outcome cannot be confirmed and the task shows no matching comment, so the comment may exist; the connector never sends it again.", True),
124
+ BranchDefinition(DEFECT, "Local input, connection configuration, or connector definition is invalid.", True),
125
+ ),
126
+ StepDefaults(
127
+ timedelta(seconds=30),
128
+ 1,
129
+ execute_retry=RetryPolicy(timedelta(seconds=1), 2, timedelta(seconds=60), 5, timedelta(seconds=300)),
130
+ ),
131
+ )
132
+
133
+ FIND_MEMBER_BY_EMAIL_DEFINITION = QueryDefinition(
134
+ OperationRef(CONNECTOR_ID, FIND_MEMBER_BY_EMAIL_OPERATION_ID),
135
+ (
136
+ BranchDefinition(FOUND, "A member of the Workspace has the email address.", False),
137
+ BranchDefinition(NOT_FOUND, "No member of the Workspace has the email address, or the token's user is not a member of the Workspace.", True),
138
+ BranchDefinition(PROVIDER_REJECTED, "ClickUp conclusively rejected the Workspace read.", True),
139
+ BranchDefinition(INVALID_RESPONSE, "ClickUp returned an invalid or oversized list of Workspaces.", True),
140
+ BranchDefinition(DEFECT, "Local input, connection configuration, or connector definition is invalid.", True),
141
+ ),
142
+ StepDefaults(
143
+ timedelta(seconds=30),
144
+ 2,
145
+ execute_retry=default_connector_step_execute_retry_policy(),
146
+ ),
147
+ )