diff --git a/.last-branch b/.last-branch index 782eaef..f8987a1 100644 --- a/.last-branch +++ b/.last-branch @@ -1 +1 @@ -v20260115-04-autotask-reference-data-fix +v20260115-05-autotask-queues-picklist-fix diff --git a/containers/backupchecks/src/backend/app/integrations/autotask/client.py b/containers/backupchecks/src/backend/app/integrations/autotask/client.py index 95eb90c..b82983c 100644 --- a/containers/backupchecks/src/backend/app/integrations/autotask/client.py +++ b/containers/backupchecks/src/backend/app/integrations/autotask/client.py @@ -163,10 +163,43 @@ class AutotaskClient: return self._as_items_list(data) def get_queues(self) -> List[Dict[str, Any]]: - return self._get_collection("Queues") + """Return Ticket Queue picklist values. + + Autotask does not expose a universal top-level Queues entity in all tenants. + The reliable source is the Tickets.queueID picklist metadata. + """ + return self._get_ticket_picklist_values(field_names=["queueid", "queue"]) def get_ticket_sources(self) -> List[Dict[str, Any]]: - return self._get_collection("TicketSources") + """Return Ticket Source picklist values. + + Similar to queues, Ticket Source values are best retrieved via the + Tickets.source picklist metadata to avoid relying on optional entities. + """ + return self._get_ticket_picklist_values(field_names=["source", "sourceid"]) + + def _get_ticket_picklist_values(self, field_names: List[str]) -> List[Dict[str, Any]]: + fields = self._get_entity_fields("Tickets") + wanted = {n.strip().lower() for n in (field_names or []) if str(n).strip()} + + field: Optional[Dict[str, Any]] = None + for f in fields: + name = str(f.get("name") or "").strip().lower() + if name in wanted: + field = f + break + + if not field: + raise AutotaskError(f"Unable to locate Tickets field metadata for picklist retrieval: {sorted(wanted)}") + + if not bool(field.get("isPickList")): + raise AutotaskError(f"Tickets.{field.get('name')} is not marked as a picklist in Autotask metadata.") + + picklist_path = field.get("picklistValues") + if not isinstance(picklist_path, str) or not picklist_path.strip(): + raise AutotaskError(f"Tickets.{field.get('name')} metadata did not include a picklistValues endpoint.") + + return self._call_picklist_values(picklist_path) def get_ticket_priorities(self) -> List[Dict[str, Any]]: """Return Ticket Priority picklist values. diff --git a/containers/backupchecks/src/backend/app/main/routes_settings.py b/containers/backupchecks/src/backend/app/main/routes_settings.py index 1127187..5897121 100644 --- a/containers/backupchecks/src/backend/app/main/routes_settings.py +++ b/containers/backupchecks/src/backend/app/main/routes_settings.py @@ -1324,13 +1324,25 @@ def settings_autotask_refresh_reference_data(): priorities = client.get_ticket_priorities() # Store a minimal subset for dropdowns (id + name/label) + # Note: Some "reference" values are exposed as picklists (value/label) + # instead of entity collections (id/name). We normalize both shapes. def _norm(items): out = [] for it in items or []: if not isinstance(it, dict): continue _id = it.get("id") - name = it.get("name") or it.get("label") or it.get("queueName") or it.get("sourceName") or it.get("description") or "" + if _id is None: + _id = it.get("value") + + name = ( + it.get("name") + or it.get("label") + or it.get("queueName") + or it.get("sourceName") + or it.get("description") + or "" + ) try: _id_int = int(_id) except Exception: diff --git a/docs/changelog.md b/docs/changelog.md index 93888d6..161d863 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -36,6 +36,15 @@ - Improved “Test connection” to validate authentication and reference data access reliably. - Fixed admin event logging to prevent secondary exceptions during error handling. +## v20260115-05-autotask-queues-picklist-fix + +Changes: +- Reworked Autotask reference data retrieval to use Ticket entity picklists instead of non-existent top-level resources. +- Retrieved Queues via the Tickets.queueID picklist to ensure compatibility with all Autotask tenants. +- Retrieved Ticket Sources via the Tickets.source picklist instead of a direct collection endpoint. +- Kept Priority retrieval fully dynamic using the Tickets.priority picklist. +- Normalized picklist values so IDs and display labels are handled consistently in settings dropdowns. +- Fixed Autotask connection test to rely on picklist availability, preventing false 404 errors. ***