Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 22 additions & 16 deletions src/aignostics/gui/_frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,9 @@ def _update_health() -> None:
coroutine=_health_load_and_render(),
name="_health_load_and_render",
)
ui.run_javascript("document.getElementById('betterstack').src = document.getElementById('betterstack').src;")
# Only refresh the status iframe if it exists (production/staging)
if settings().status_page_url:
ui.run_javascript("document.getElementById('betterstack').src = document.getElementById('betterstack').src;")
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ui.run_javascript unconditionally dereferences document.getElementById('betterstack') when api_root is prod/staging. On initial page load (or during navigation) the timer callback can run before the iframe is in the DOM, which will cause a JS error (null.src). Consider guarding in the JS itself (check element exists) so the refresh is safe regardless of render timing.

Suggested change
ui.run_javascript("document.getElementById('betterstack').src = document.getElementById('betterstack').src;")
ui.run_javascript(
"var iframe = document.getElementById('betterstack');"
"if (iframe) { iframe.src = iframe.src; }"
)

Copilot uses AI. Check for mistakes.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure whether this is an actual problem or not. Are you able to start the Launchpad against all environments?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@olivermeyer yes, it looks correct against the branch - no Betterstack status page icon for dev and test, different links for staging and prod.


ui.timer(interval=HEALTH_UPDATE_INTERVAL, callback=_update_health, immediate=True)

Expand Down Expand Up @@ -342,13 +344,15 @@ def toggle_dark_mode() -> None:
ui.link("Get Support", "https://platform.aignostics.com/support", new_tab=True).mark(
"LINK_DOCUMENTATION"
)
with ui.item().props("clickable"):
with ui.item_section().props("avatar"):
ui.icon("check_circle", color="primary")
with ui.item_section():
ui.link("Check Platform Status", "https://status.aignostics.com", new_tab=True).mark(
"LINK_DOCUMENTATION"
)
status_url = settings().status_page_url
if status_url:
with ui.item().props("clickable"):
with ui.item_section().props("avatar"):
ui.icon("check_circle", color="primary")
with ui.item_section():
ui.link("Check Platform Status", status_url, new_tab=True).mark(
"LINK_DOCUMENTATION"
)
with ui.item().props("clickable"):
with ui.item_section().props("avatar"):
ui.icon("handshake", color="primary")
Expand All @@ -368,14 +372,16 @@ def toggle_dark_mode() -> None:
ui.row(align_items="center").classes("justify-start w-full"),
):
health_link()
with ui.row().style("padding: 0"):
ui.html(
'<iframe id="betterstack" src="https://status.aignostics.com/badge?theme=dark" '
'width="250" height="30" frameborder="0" scrolling="no" '
'style="color-scheme: dark"></iframe>',
sanitize=False,
).style("margin-left: 0px;")
ui.tooltip("Check Platform Status")
status_url = settings().status_page_url
if status_url:
with ui.row().style("padding: 0"):
ui.html(
f'<iframe id="betterstack" src="{status_url}/badge?theme=dark" '
'width="250" height="30" frameborder="0" scrolling="no" '
'style="color-scheme: dark"></iframe>',
sanitize=False,
).style("margin-left: 0px;")
ui.tooltip("Check Platform Status")
ui.space()
with ui.row():
flavor = " (native)" if getattr(sys, "frozen", False) else ""
Expand Down
4 changes: 4 additions & 0 deletions src/aignostics/platform/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@
REDIRECT_URI_TEST,
REDIRECT_URI_STAGING,
REDIRECT_URI_PRODUCTION,
STATUS_PAGE_URL_DEV,
STATUS_PAGE_URL_TEST,
STATUS_PAGE_URL_STAGING,
STATUS_PAGE_URL_PRODUCTION,
TOKEN_URL_DEV,
TOKEN_URL_TEST,
TOKEN_URL_STAGING,
Expand Down
4 changes: 4 additions & 0 deletions src/aignostics/platform/_constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
REDIRECT_URI_DEV = "http://localhost:8989/"
DEVICE_URL_DEV = "https://dev-8ouohmmrbuh2h4vu.eu.auth0.com/oauth/device/code"
JWS_JSON_URL_DEV = "https://dev-8ouohmmrbuh2h4vu.eu.auth0.com/.well-known/jwks.json"
STATUS_PAGE_URL_DEV = None # No dedicated status page for dev environment

API_ROOT_TEST = "https://platform-test.aignostics.ai"
CLIENT_ID_INTERACTIVE_TEST = "gqduveFvx7LX90drQPGzr4JGUYdh24gA" # not a secret, but a public client ID (same as dev)
Expand All @@ -17,6 +18,7 @@
REDIRECT_URI_TEST = "http://localhost:8989/"
DEVICE_URL_TEST = "https://dev-8ouohmmrbuh2h4vu.eu.auth0.com/oauth/device/code"
JWS_JSON_URL_TEST = "https://dev-8ouohmmrbuh2h4vu.eu.auth0.com/.well-known/jwks.json"
STATUS_PAGE_URL_TEST = None # No dedicated status page for test environment

API_ROOT_STAGING = "https://platform-staging.aignostics.com"
CLIENT_ID_INTERACTIVE_STAGING = "fQkbvYzQPPVwLxc3uque5JsyFW00rJ7b" # not a secret, but a public client ID
Expand All @@ -26,6 +28,7 @@
REDIRECT_URI_STAGING = "http://localhost:8989/"
DEVICE_URL_STAGING = "https://aignostics-platform-staging.eu.auth0.com/oauth/device/code"
JWS_JSON_URL_STAGING = "https://aignostics-platform-staging.eu.auth0.com/.well-known/jwks.json"
STATUS_PAGE_URL_STAGING = "https://status.platform-staging.aignostics.com"

API_ROOT_PRODUCTION = "https://platform.aignostics.com"
CLIENT_ID_INTERACTIVE_PRODUCTION = "YtJ7F9lAtxx16SZGQlYPe6wcjlXB78MM" # not a secret, but a public client ID
Expand All @@ -35,6 +38,7 @@
REDIRECT_URI_PRODUCTION = "http://localhost:8989/"
DEVICE_URL_PRODUCTION = "https://aignostics-platform.eu.auth0.com/oauth/device/code"
JWS_JSON_URL_PRODUCTION = "https://aignostics-platform.eu.auth0.com/.well-known/jwks.json"
STATUS_PAGE_URL_PRODUCTION = "https://status.platform.aignostics.com"

# Pipeline orchestration defaults
DEFAULT_GPU_TYPE = "L4"
Expand Down
21 changes: 21 additions & 0 deletions src/aignostics/platform/_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@
REDIRECT_URI_TEST,
REDIRECT_URI_STAGING,
REDIRECT_URI_PRODUCTION,
STATUS_PAGE_URL_DEV,
STATUS_PAGE_URL_TEST,
STATUS_PAGE_URL_STAGING,
STATUS_PAGE_URL_PRODUCTION,
TOKEN_URL_DEV,
TOKEN_URL_TEST,
TOKEN_URL_STAGING,
Expand Down Expand Up @@ -205,6 +209,9 @@ def profile_edit_url(self) -> str:
str, BeforeValidator(_validate_url), Field(description="JWS key set URL for token verification")
]
client_id_interactive: Annotated[str, Field(description="OAuth client ID for interactive flows")]
status_page_url: Annotated[
str | None, Field(description="Status page URL for monitoring platform health", default=None)
] = None

@computed_field # type: ignore[prop-decorator]
@property
Expand Down Expand Up @@ -510,6 +517,20 @@ def pre_init(cls, values: dict) -> dict: # type: ignore[type-arg] # noqa: N805
# See https://github.com/pydantic/pydantic/issues/9789
api_root = values.get("api_root", API_ROOT_PRODUCTION)

# Set status_page_url based on environment (independent of auth fields)
if "status_page_url" not in values:
match api_root:
case x if x == API_ROOT_DEV:
values["status_page_url"] = STATUS_PAGE_URL_DEV
case x if x == API_ROOT_TEST:
values["status_page_url"] = STATUS_PAGE_URL_TEST
case x if x == API_ROOT_STAGING:
values["status_page_url"] = STATUS_PAGE_URL_STAGING
case x if x == API_ROOT_PRODUCTION:
values["status_page_url"] = STATUS_PAGE_URL_PRODUCTION
case _:
values["status_page_url"] = None
Comment on lines +520 to +532
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not put this into the existing match..case block below? Then you can simply extend the existing tests (e.g. this one for prod).


# Check if all required auth fields are already provided
auth_fields = [
"audience",
Expand Down
1 change: 1 addition & 0 deletions tests/aignostics/gui/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Tests for GUI module."""
84 changes: 84 additions & 0 deletions tests/aignostics/gui/frame_test.py
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we decide to move the status page URL resolution to the existing match..case block in the settings module, we don't need new tests; if we decide otherwise then we do need new tests but these are not in the right place. They should be in tests/aignostics/platform/settings_test.py.

Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
"""Tests for GUI frame module."""

import pytest

from aignostics.platform import (
API_ROOT_DEV,
API_ROOT_PRODUCTION,
API_ROOT_STAGING,
API_ROOT_TEST,
STATUS_PAGE_URL_DEV,
STATUS_PAGE_URL_PRODUCTION,
STATUS_PAGE_URL_STAGING,
STATUS_PAGE_URL_TEST,
Settings,
)


@pytest.mark.unit
def test_status_page_url_production(record_property) -> None:
"""Test that production environment has correct status page URL in settings.

Args:
record_property: pytest record_property fixture
"""
record_property("tested-item-id", "SPEC-PLATFORM-SETTINGS")
settings = Settings(api_root=API_ROOT_PRODUCTION)
assert settings.status_page_url == STATUS_PAGE_URL_PRODUCTION
assert settings.status_page_url == "https://status.platform.aignostics.com"


@pytest.mark.unit
def test_status_page_url_staging(record_property) -> None:
"""Test that staging environment has correct status page URL in settings.

Args:
record_property: pytest record_property fixture
"""
record_property("tested-item-id", "SPEC-PLATFORM-SETTINGS")
settings = Settings(api_root=API_ROOT_STAGING)
assert settings.status_page_url == STATUS_PAGE_URL_STAGING
assert settings.status_page_url == "https://status.platform-staging.aignostics.com"


@pytest.mark.unit
def test_status_page_url_dev(record_property) -> None:
"""Test that dev environment has no status page URL in settings.

Args:
record_property: pytest record_property fixture
"""
record_property("tested-item-id", "SPEC-PLATFORM-SETTINGS")
settings = Settings(api_root=API_ROOT_DEV)
assert settings.status_page_url == STATUS_PAGE_URL_DEV
assert settings.status_page_url is None


@pytest.mark.unit
def test_status_page_url_test(record_property) -> None:
"""Test that test environment has no status page URL in settings.

Args:
record_property: pytest record_property fixture
"""
record_property("tested-item-id", "SPEC-PLATFORM-SETTINGS")
settings = Settings(api_root=API_ROOT_TEST)
assert settings.status_page_url == STATUS_PAGE_URL_TEST
assert settings.status_page_url is None


@pytest.mark.unit
def test_status_page_url_configurable(record_property, monkeypatch) -> None:
"""Test that status page URL can be explicitly configured via settings.

Args:
record_property: pytest record_property fixture
monkeypatch: pytest monkeypatch fixture
"""
record_property("tested-item-id", "SPEC-PLATFORM-SETTINGS")
# Test that we can override the status page URL
custom_url = "https://custom-status.example.com"
monkeypatch.setenv("AIGNOSTICS_STATUS_PAGE_URL", custom_url)
settings = Settings(api_root=API_ROOT_PRODUCTION)
assert settings.status_page_url == custom_url

Loading