Fix Autotask resolution to preserve exact field values from GET response
The issueType, subIssueType, and source fields must be sent in the PUT payload with their exact values from GET response, including null. This fixes HTTP 500 error where Autotask rejected picklist value 0. Changed _pick function to return (found, value) tuple to distinguish between "field missing" and "field is null". Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
8baf4e8ff1
commit
8a733a356b
@ -597,37 +597,51 @@ class AutotaskClient:
|
||||
if not isinstance(ticket, dict) or not ticket:
|
||||
raise AutotaskError("Autotask did not return a ticket object.")
|
||||
|
||||
def _pick(d: Dict[str, Any], keys: List[str]) -> Any:
|
||||
def _pick(d: Dict[str, Any], keys: List[str]) -> tuple[bool, Any]:
|
||||
"""Pick first available field from possible field names.
|
||||
|
||||
Returns tuple: (found, value)
|
||||
- found=True if field exists (even if value is None)
|
||||
- found=False if field doesn't exist in dict
|
||||
|
||||
This allows us to distinguish between "field missing" vs "field is null",
|
||||
which is critical for Autotask PUT payloads that require exact values.
|
||||
"""
|
||||
for k in keys:
|
||||
if k in d and d.get(k) not in (None, ""):
|
||||
return d.get(k)
|
||||
return None
|
||||
if k in d:
|
||||
return (True, d[k])
|
||||
return (False, None)
|
||||
|
||||
# Required stabilising fields for safe resolution updates (validated via Postman tests)
|
||||
resolved_issue_type = _pick(ticket, ["issueType", "issueTypeID", "issueTypeId"])
|
||||
resolved_sub_issue_type = _pick(ticket, ["subIssueType", "subIssueTypeID", "subIssueTypeId"])
|
||||
resolved_source = _pick(ticket, ["source", "sourceID", "sourceId"])
|
||||
resolved_status = _pick(ticket, ["status", "statusID", "statusId"])
|
||||
# Required stabilising fields for safe resolution updates (validated via Postman tests).
|
||||
# Field names are camelCase as per API contract (docs/autotask_rest_api.md section 2.1).
|
||||
# We must copy the EXACT values from GET response to PUT payload, even if null.
|
||||
found_id, ticket_id = _pick(ticket, ["id"])
|
||||
found_issue_type, resolved_issue_type = _pick(ticket, ["issueType", "issueTypeID", "issueTypeId"])
|
||||
found_sub_issue_type, resolved_sub_issue_type = _pick(ticket, ["subIssueType", "subIssueTypeID", "subIssueTypeId"])
|
||||
found_source, resolved_source = _pick(ticket, ["source", "sourceID", "sourceId"])
|
||||
found_status, resolved_status = _pick(ticket, ["status", "statusID", "statusId"])
|
||||
|
||||
# Validate that required fields exist in the response
|
||||
missing: List[str] = []
|
||||
if _pick(ticket, ["id"]) in (None, ""):
|
||||
if not found_id or ticket_id in (None, ""):
|
||||
missing.append("id")
|
||||
if resolved_issue_type in (None, ""):
|
||||
missing.append("issueType")
|
||||
if resolved_sub_issue_type in (None, ""):
|
||||
missing.append("subIssueType")
|
||||
if resolved_source in (None, ""):
|
||||
missing.append("source")
|
||||
if resolved_status in (None, ""):
|
||||
if not found_status or resolved_status in (None, ""):
|
||||
missing.append("status")
|
||||
if not found_issue_type:
|
||||
missing.append("issueType")
|
||||
if not found_sub_issue_type:
|
||||
missing.append("subIssueType")
|
||||
if not found_source:
|
||||
missing.append("source")
|
||||
|
||||
if missing:
|
||||
raise AutotaskError(
|
||||
"Cannot safely update ticket resolution because required fields are missing: " + ", ".join(missing)
|
||||
)
|
||||
|
||||
# Build payload with exact values from GET response (including null if that's what we got)
|
||||
payload: Dict[str, Any] = {
|
||||
"id": int(ticket.get("id")),
|
||||
"id": int(ticket_id),
|
||||
"issueType": resolved_issue_type,
|
||||
"subIssueType": resolved_sub_issue_type,
|
||||
"source": resolved_source,
|
||||
@ -648,7 +662,7 @@ class AutotaskClient:
|
||||
]
|
||||
for f in optional_fields:
|
||||
if f in ticket:
|
||||
payload[f] = ticket.get(f)
|
||||
payload[f] = ticket[f]
|
||||
|
||||
return self.update_ticket(payload)
|
||||
|
||||
|
||||
@ -4,6 +4,11 @@ This file documents all changes made to this project via Claude Code.
|
||||
|
||||
## [2026-02-05]
|
||||
|
||||
### Fixed
|
||||
- Autotask ticket resolution update now correctly preserves exact field values from GET response in PUT payload.
|
||||
The `issueType`, `subIssueType`, and `source` fields are copied with their exact values (including null)
|
||||
from the GET response, as required by Autotask API. Previously these fields were being skipped or modified.
|
||||
|
||||
### Added
|
||||
- Restored Autotask PSA integration from branch `v20260203-13-autotask-resolution-item-wrapper`:
|
||||
- `integrations/autotask/client.py` - Autotask REST API client with full support for:
|
||||
|
||||
Loading…
Reference in New Issue
Block a user