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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
9 changes: 9 additions & 0 deletions openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11839,6 +11839,15 @@ components:
title: Roll Up Method
description: Default roll-up aggregation method for this metric (e.g., 'sum',
'average').
metric_key_alias:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Manually adding code to this file will not work. These edits will be lost on the next automated update.

If changes are necessary in Galileo's api definition that needs to be done on https://github.com/rungalileo/api

anyOf:
- type: string
- type: 'null'
title: Metric Key Alias
description: Alternate metric key for this column. When scorer UUIDs are used
as column IDs (e.g. "metrics/{uuid}"), this holds the legacy snake_case
metric name (e.g. "correctness") for display and dual-key query fallback.
None for non-metric columns.
Comment on lines +11842 to +11850

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

🟠 major (design): This metric_key_alias field is added by hand-editing openapi.yaml, but openapi.yaml is a generated artifact: scripts/import-openapi-yaml.sh regenerates it from the live server's openapi.json. Because the server spec does not include metric_key_alias on the ColumnInfo schema, the next run of import-openapi-yaml.sh will overwrite this file and silently drop the field again — reintroducing the exact 5 test failures this PR is fixing. This is the same "lost patch on regeneration" failure mode the PR is otherwise hardening against for the HTTPValidationError patch.

The durable location for local spec fixups is the yq patch block in scripts/import-openapi-yaml.sh (see the existing precedent there: StepType.enum += ["control"], with the comment "server omits this value from the spec but returns it" and "add the openapi.yaml patches here"). Please add the metric_key_alias injection to that yq expression (e.g. set .components.schemas.ColumnInfo.properties.metric_key_alias) so it survives a spec re-import. Note LogRecordsColumnInfo and the metric_* schemas already carry the field in the server spec, so only ColumnInfo needs the manual patch.

🤖 Generated by the Astra agent

type: object
required:
- id
Expand Down
32 changes: 18 additions & 14 deletions scripts/patch_http_validation_error.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,25 +37,29 @@
# Patterns to find in the auto-generated file
# ---------------------------------------------------------------------------

# Inside from_dict — bare initialisation + for-loop produced by the generator:
# Inside from_dict — 0.29.0 generator output:
#
# detail = []
# _detail = d.pop("detail", UNSET)
# for detail_item_data in _detail or []:
# detail_item = ValidationError.from_dict(detail_item_data)
# detail: list[ValidationError] | Unset = UNSET
# if _detail is not UNSET:
# detail = []
# for detail_item_data in _detail:
# detail_item = ValidationError.from_dict(detail_item_data)
# <- blank line
# detail.append(detail_item)
# detail.append(detail_item)
#
# The class-level field annotation already comes out as
# `Union[Unset, list["ValidationError"]] = UNSET` from the generator, so it
# does NOT need to be patched — only the from_dict body is rewritten here.
# Changes from 0.26.x: _detail = d.pop(...) now comes BEFORE the type-annotated
# init; the loop body is wrapped in `if _detail is not UNSET:`; the `or []` is
# gone; type annotation uses `list[...] | Unset` instead of `Union[Unset, list[...]]`.
_LOOP_RE = re.compile(
r"(?P<indent>[ \t]+)detail = \[\]\n"
r"(?P=indent)_detail = d\.pop\(\"detail\", UNSET\)\n"
r"(?P=indent)for detail_item_data in _detail or \[\]:\n"
r"(?P=indent) detail_item = ValidationError\.from_dict\(detail_item_data\)\n"
r"(?P<indent>[ \t]+)_detail = d\.pop\(\"detail\", UNSET\)\n"
r"(?P=indent)detail: list\[ValidationError\] \| Unset = UNSET\n"
r"(?P=indent)if _detail is not UNSET:\n"
r"(?P=indent) detail = \[\]\n"
r"(?P=indent) for detail_item_data in _detail:\n"
r"(?P=indent) detail_item = ValidationError\.from_dict\(detail_item_data\)\n"
r"[ \t]*\n"
r"(?P=indent) detail\.append\(detail_item\)",
r"(?P=indent) detail\.append\(detail_item\)",
re.MULTILINE,
)

Expand All @@ -65,8 +69,8 @@ def _loop_replacement(indent: str) -> str:
i4 = indent + " "
return "\n".join(
[
f"{i}detail: Union[Unset, list[ValidationError]] = UNSET",
f'{i}_detail = d.pop("detail", UNSET)',
f"{i}detail: list[ValidationError] | Unset = UNSET",
f"{i}if isinstance(_detail, list):",
f"{i4}detail = [ValidationError.from_dict(item) for item in _detail]",
f"{i}elif isinstance(_detail, str) and _detail:",
Expand Down
2 changes: 1 addition & 1 deletion src/splunk_ao/resources/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""A client library for accessing FastAPI."""
"""A client library for accessing FastAPI"""

from .client import AuthenticatedClient, Client

Expand Down
2 changes: 1 addition & 1 deletion src/splunk_ao/resources/api/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
"""Contains methods for accessing the API."""
"""Contains methods for accessing the API"""
2 changes: 1 addition & 1 deletion src/splunk_ao/resources/api/auth/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
"""Contains endpoint functions for accessing the API."""
"""Contains endpoint functions for accessing the API"""
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
from http import HTTPStatus
from typing import Any
from typing import Any, Optional

import httpx

from galileo_core.constants.request_method import RequestMethod
from galileo_core.helpers.api_client import ApiClient
from splunk_ao.exceptions import (
AuthenticationError,
BadRequestError,
Expand All @@ -13,8 +15,6 @@
ServerError,
)
from splunk_ao.utils.headers_data import get_sdk_header
from galileo_core.constants.request_method import RequestMethod
from galileo_core.helpers.api_client import ApiClient

from ... import errors
from ...models.api_key_login_request import ApiKeyLoginRequest
Expand All @@ -40,10 +40,14 @@ def _get_kwargs(*, body: ApiKeyLoginRequest) -> dict[str, Any]:

def _parse_response(*, client: ApiClient, response: httpx.Response) -> HTTPValidationError | Token:
if response.status_code == 200:
return Token.from_dict(response.json())
response_200 = Token.from_dict(response.json())

return response_200

if response.status_code == 422:
return HTTPValidationError.from_dict(response.json())
response_422 = HTTPValidationError.from_dict(response.json())

return response_422

# Handle common HTTP errors with actionable messages
if response.status_code == 400:
Expand Down Expand Up @@ -73,80 +77,76 @@ def _build_response(*, client: ApiClient, response: httpx.Response) -> Response[


def sync_detailed(*, client: ApiClient, body: ApiKeyLoginRequest) -> Response[HTTPValidationError | Token]:
"""Login Api Key.
"""Login Api Key

Args:
body (ApiKeyLoginRequest):

Raises
------
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.

Returns
-------
Response[Union[HTTPValidationError, Token]]
Returns:
Response[HTTPValidationError | Token]
"""

kwargs = _get_kwargs(body=body)

response = client.request(**kwargs)

return _build_response(client=client, response=response)


def sync(*, client: ApiClient, body: ApiKeyLoginRequest) -> HTTPValidationError | Token | None:
"""Login Api Key.
def sync(*, client: ApiClient, body: ApiKeyLoginRequest) -> Optional[HTTPValidationError | Token]:
"""Login Api Key

Args:
body (ApiKeyLoginRequest):

Raises
------
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.

Returns
-------
Union[HTTPValidationError, Token]
Returns:
HTTPValidationError | Token
"""

return sync_detailed(client=client, body=body).parsed


async def asyncio_detailed(*, client: ApiClient, body: ApiKeyLoginRequest) -> Response[HTTPValidationError | Token]:
"""Login Api Key.
"""Login Api Key

Args:
body (ApiKeyLoginRequest):

Raises
------
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.

Returns
-------
Response[Union[HTTPValidationError, Token]]
Returns:
Response[HTTPValidationError | Token]
"""

kwargs = _get_kwargs(body=body)

response = await client.arequest(**kwargs)

return _build_response(client=client, response=response)


async def asyncio(*, client: ApiClient, body: ApiKeyLoginRequest) -> HTTPValidationError | Token | None:
"""Login Api Key.
async def asyncio(*, client: ApiClient, body: ApiKeyLoginRequest) -> Optional[HTTPValidationError | Token]:
"""Login Api Key

Args:
body (ApiKeyLoginRequest):

Raises
------
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.

Returns
-------
Union[HTTPValidationError, Token]
Returns:
HTTPValidationError | Token
"""

return (await asyncio_detailed(client=client, body=body)).parsed
62 changes: 31 additions & 31 deletions src/splunk_ao/resources/api/auth/login_email_login_post.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
from http import HTTPStatus
from typing import Any
from typing import Any, Optional

import httpx

from galileo_core.constants.request_method import RequestMethod
from galileo_core.helpers.api_client import ApiClient
from splunk_ao.exceptions import (
AuthenticationError,
BadRequestError,
Expand All @@ -13,8 +15,6 @@
ServerError,
)
from splunk_ao.utils.headers_data import get_sdk_header
from galileo_core.constants.request_method import RequestMethod
from galileo_core.helpers.api_client import ApiClient

from ... import errors
from ...models.body_login_email_login_post import BodyLoginEmailLoginPost
Expand All @@ -40,10 +40,14 @@ def _get_kwargs(*, body: BodyLoginEmailLoginPost) -> dict[str, Any]:

def _parse_response(*, client: ApiClient, response: httpx.Response) -> HTTPValidationError | Token:
if response.status_code == 200:
return Token.from_dict(response.json())
response_200 = Token.from_dict(response.json())

return response_200

if response.status_code == 422:
return HTTPValidationError.from_dict(response.json())
response_422 = HTTPValidationError.from_dict(response.json())

return response_422

# Handle common HTTP errors with actionable messages
if response.status_code == 400:
Expand Down Expand Up @@ -73,82 +77,78 @@ def _build_response(*, client: ApiClient, response: httpx.Response) -> Response[


def sync_detailed(*, client: ApiClient, body: BodyLoginEmailLoginPost) -> Response[HTTPValidationError | Token]:
"""Login Email.
"""Login Email

Args:
body (BodyLoginEmailLoginPost):

Raises
------
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.

Returns
-------
Response[Union[HTTPValidationError, Token]]
Returns:
Response[HTTPValidationError | Token]
"""

kwargs = _get_kwargs(body=body)

response = client.request(**kwargs)

return _build_response(client=client, response=response)


def sync(*, client: ApiClient, body: BodyLoginEmailLoginPost) -> HTTPValidationError | Token | None:
"""Login Email.
def sync(*, client: ApiClient, body: BodyLoginEmailLoginPost) -> Optional[HTTPValidationError | Token]:
"""Login Email

Args:
body (BodyLoginEmailLoginPost):

Raises
------
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.

Returns
-------
Union[HTTPValidationError, Token]
Returns:
HTTPValidationError | Token
"""

return sync_detailed(client=client, body=body).parsed


async def asyncio_detailed(
*, client: ApiClient, body: BodyLoginEmailLoginPost
) -> Response[HTTPValidationError | Token]:
"""Login Email.
"""Login Email

Args:
body (BodyLoginEmailLoginPost):

Raises
------
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.

Returns
-------
Response[Union[HTTPValidationError, Token]]
Returns:
Response[HTTPValidationError | Token]
"""

kwargs = _get_kwargs(body=body)

response = await client.arequest(**kwargs)

return _build_response(client=client, response=response)


async def asyncio(*, client: ApiClient, body: BodyLoginEmailLoginPost) -> HTTPValidationError | Token | None:
"""Login Email.
async def asyncio(*, client: ApiClient, body: BodyLoginEmailLoginPost) -> Optional[HTTPValidationError | Token]:
"""Login Email

Args:
body (BodyLoginEmailLoginPost):

Raises
------
Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.

Returns
-------
Union[HTTPValidationError, Token]
Returns:
HTTPValidationError | Token
"""

return (await asyncio_detailed(client=client, body=body)).parsed
Original file line number Diff line number Diff line change
@@ -1 +1 @@
"""Contains endpoint functions for accessing the API."""
"""Contains endpoint functions for accessing the API"""
Loading
Loading