From 102d2d8bb350f6919d905bf9ed34a83d3e23863b Mon Sep 17 00:00:00 2001 From: ball42 Date: Fri, 10 Jul 2026 18:04:15 -0500 Subject: [PATCH] fix: link the Setup Required error page directly to /setup Creating a jamfpro/okta automation before JAWA is configured raised a 'Setup Required' error page that dead-ended the user -- Back and Dashboard only, no path to /setup where they actually need to go. AutomationError already carried an optional link, but the two raise sites passed none and the template rendered a link as a raw URL in a new tab. Now the raise sites pass link=/setup with a friendly label, and when a link is present the error page renders it as the primary in-page action button ('Go to Setup'), demoting Dashboard to secondary. Adds link_text to AutomationError for the button label. --- templates/error.html | 17 +++++++---- tests/test_setup_required.py | 42 ++++++++++++++++++++++++++++ views/_type_handlers/base.py | 9 +++++- views/_type_handlers/jamf_handler.py | 6 +++- views/_type_handlers/okta_handler.py | 6 +++- views/automation_view.py | 9 ++++-- 6 files changed, 77 insertions(+), 12 deletions(-) create mode 100644 tests/test_setup_required.py diff --git a/templates/error.html b/templates/error.html index edd8d21..62535af 100644 --- a/templates/error.html +++ b/templates/error.html @@ -156,19 +156,24 @@

{{ error }}

{{ error_message }} - {% if link %} - - {% endif %} -
+ {% if link %} + {# A supplied link is the recommended next step: make it the + primary action (in-page) and demote Dashboard to secondary. #} + + {{ link_text if link_text else link }} → + + + Dashboard → + + {% else %} Dashboard → + {% endif %}
diff --git a/tests/test_setup_required.py b/tests/test_setup_required.py new file mode 100644 index 0000000..623857a --- /dev/null +++ b/tests/test_setup_required.py @@ -0,0 +1,42 @@ +"""The 'Setup Required' error must link the user to /setup. + +Creating a jamfpro/okta automation before JAWA is configured raises +AutomationError("Setup Required", ...). The error page must give the +admin a direct, in-page way to reach /setup instead of dead-ending. +""" + +import json + + +def _unconfigure_jawa(jawa_env): + # Remove jawa_address so get_jawa_address() is falsy -> Setup Required. + jawa_env.server_file.write_text(json.dumps({"brand": "JAWA"})) + + +def test_jamfpro_new_without_jawa_links_to_setup(logged_in_client, jawa_env): + _unconfigure_jawa(jawa_env) + resp = logged_in_client.post( + "/automations/jamfpro/new", + data={"webhook_name": "test-hook"}, + ) + body = resp.data.decode() + assert "Setup Required" in body + # The page offers a direct link to /setup (the action the user needs). + assert 'href="/setup"' in body + # Friendly label, not the raw path as the only text. + assert "Go to Setup" in body + # Internal navigation -- not a new tab. + assert 'href="/setup" target="_blank"' not in body + + +def test_okta_new_without_jawa_links_to_setup(logged_in_client, jawa_env): + _unconfigure_jawa(jawa_env) + # Okta's handler reads the name from "webhookname" (no underscore). + resp = logged_in_client.post( + "/automations/okta/new", + data={"webhookname": "test-okta"}, + ) + body = resp.data.decode() + assert "Setup Required" in body + assert 'href="/setup"' in body + assert "Go to Setup" in body diff --git a/views/_type_handlers/base.py b/views/_type_handlers/base.py index d990adc..168f4fe 100644 --- a/views/_type_handlers/base.py +++ b/views/_type_handlers/base.py @@ -34,11 +34,18 @@ class AutomationError(Exception): """Raised when an automation operation fails.""" def __init__( - self, title: str, message: str, link: Optional[str] = None + self, + title: str, + message: str, + link: Optional[str] = None, + link_text: Optional[str] = None, ) -> None: self.title = title self.message = message self.link = link + # Friendly label for the link when rendered as an action button; + # falls back to the raw link if omitted. + self.link_text = link_text super().__init__(message) diff --git a/views/_type_handlers/jamf_handler.py b/views/_type_handlers/jamf_handler.py index 1aeee25..f9565d2 100644 --- a/views/_type_handlers/jamf_handler.py +++ b/views/_type_handlers/jamf_handler.py @@ -183,7 +183,11 @@ def process_create( server_address = get_jawa_address() if not server_address: raise AutomationError( - "Setup Required", "Please configure JAWA address first." + "Setup Required", + "Configure your JAWA address and Jamf Pro server " + "before creating an automation.", + link="/setup", + link_text="Go to Setup", ) # Ensure token is valid diff --git a/views/_type_handlers/okta_handler.py b/views/_type_handlers/okta_handler.py index 6c00c64..cf38960 100644 --- a/views/_type_handlers/okta_handler.py +++ b/views/_type_handlers/okta_handler.py @@ -75,7 +75,11 @@ def process_create( server_address = get_jawa_address() if not server_address: raise AutomationError( - "Setup Required", "Please configure JAWA address first." + "Setup Required", + "Configure your JAWA address and Jamf Pro server " + "before creating an automation.", + link="/setup", + link_text="Go to Setup", ) # Ensure okta verification file is executable diff --git a/views/automation_view.py b/views/automation_view.py index 8f2fc65..d62e9eb 100644 --- a/views/automation_view.py +++ b/views/automation_view.py @@ -77,12 +77,15 @@ def _get_session_data() -> dict: } -def _error_page(title: str, message: str, link: str = None) -> str: +def _error_page( + title: str, message: str, link: str = None, link_text: str = None +) -> str: return render_template( "error.html", error=title, error_message=message, link=link, + link_text=link_text, username=session.get("username"), ) @@ -175,7 +178,7 @@ def create(auto_type: str) -> Union[Response, str]: request.form, request.files, session_data ) except AutomationError as err: - return _error_page(err.title, err.message, err.link) + return _error_page(err.title, err.message, err.link, err.link_text) # Persist the entry entry = result.get("entry") @@ -288,7 +291,7 @@ def edit(auto_type: str, name: str) -> Union[Response, str]: all_items, ) except AutomationError as err: - return _error_page(err.title, err.message, err.link) + return _error_page(err.title, err.message, err.link, err.link_text) return render_template( SUCCESS_TEMPLATE,