Skip to content
Merged
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
2 changes: 2 additions & 0 deletions PYEGERIA_ISSUES.md
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,8 @@ Egeria says none of its changes break existing requests: the old class names and

**Layer:** Pyegeria 路 **Status:** open (found 2026-10-15 by Resource Explorer's ClassificationAnnotation backfill; not fixed, per this repo's policy)

**Fixed 2026-10-20 (owner approved) on branch `fix/issue-152-update-body-slot`, not yet merged or released.** All five call sites now pass `body=body`. `tests/micro-tests/test_issue_152_update_body_slot.py` sends a dict body and an `UpdateElementRequestBody` through each method and checks that the body reaches the HTTP layer. All 10 cases fail on the old lines and pass on the fix, and the full micro-test suite (1,404) passes in a clean python:3.13-slim container. Not live-tested. Hit again 2026-10-20 by the TestGen evaluation session (RE #791, `update_annotation`), which carries its own workaround until a release ships this.

`_async_update_element_body_request(self, url, prop=None, body=None)` (`pyegeria/core/_server_client.py:7317`) takes the body as its **third** parameter. Five callers pass it positionally as the second, so it lands in `prop` and `body` is `None`; the call fails before any request goes out (the builder observed an AttributeError):

- `pyegeria/omvs/data_discovery.py:299` (`_async_update_annotation`, behind `update_annotation`)
Expand Down
2 changes: 1 addition & 1 deletion pyegeria/omvs/data_discovery.py
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ async def _async_update_annotation(
```
"""
url = f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/data-discovery/annotations/{annotation_guid}/update"
await self._async_update_element_body_request(url, body)
await self._async_update_element_body_request(url, body=body)

def update_annotation(self, annotation_guid: str, body: dict | UpdateElementRequestBody) -> None:
"""Update an annotation.
Expand Down
4 changes: 2 additions & 2 deletions pyegeria/omvs/schema_maker.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ async def _async_update_schema_type(
self, schema_type_guid: str, body: dict | UpdateElementRequestBody
) -> None:
url = f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/schema-maker/schema-types/{schema_type_guid}/update"
await self._async_update_element_body_request(url, body)
await self._async_update_element_body_request(url, body=body)

def update_schema_type(self, schema_type_guid: str, body: dict | UpdateElementRequestBody) -> None:
loop = asyncio.get_event_loop()
Expand Down Expand Up @@ -171,7 +171,7 @@ async def _async_update_schema_attribute(
self, schema_attribute_guid: str, body: dict | UpdateElementRequestBody
) -> None:
url = f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/schema-maker/schema-attributes/{schema_attribute_guid}/update"
await self._async_update_element_body_request(url, body)
await self._async_update_element_body_request(url, body=body)

def update_schema_attribute(
self, schema_attribute_guid: str, body: dict | UpdateElementRequestBody
Expand Down
2 changes: 1 addition & 1 deletion pyegeria/omvs/subject_area.py
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ async def _async_update_subject_area(
```
"""
url = f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/subject-area/collections/{subject_area_guid}/update"
await self._async_update_element_body_request(url, body)
await self._async_update_element_body_request(url, body=body)

def update_subject_area(self, subject_area_guid: str, body: dict | UpdateElementRequestBody) -> None:
"""Update a subject area.
Expand Down
2 changes: 1 addition & 1 deletion pyegeria/omvs/time_keeper.py
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,7 @@ async def _async_update_context_event(
```
"""
url = f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/time-keeper/context-events/{context_event_guid}/update"
await self._async_update_element_body_request(url, body)
await self._async_update_element_body_request(url, body=body)

def update_context_event(self, context_event_guid: str, body: dict | UpdateElementRequestBody) -> None:
"""Update a context event.
Expand Down
81 changes: 81 additions & 0 deletions tests/micro-tests/test_issue_152_update_body_slot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
"""
ISSUE-152: five update methods passed the body positionally into
_async_update_element_body_request(url, prop=None, body=None), so it landed in
`prop`, `body` was None, and the call failed before sending anything
("'NoneType' object has no attribute 'model_dump_json'").
No live server: the HTTP call is captured and the body asserted.
"""
import json

import pytest

from pyegeria.models import UpdateElementRequestBody
from pyegeria.omvs.data_discovery import DataDiscovery
from pyegeria.omvs.schema_maker import SchemaMaker
from pyegeria.omvs.subject_area import SubjectArea
from pyegeria.omvs.time_keeper import TimeKeeper

GUID = "44444444-4444-4444-4444-444444444444"

CASES = [
(DataDiscovery, "_async_update_annotation", "AnnotationProperties",
f"/api/open-metadata/data-discovery/annotations/{GUID}/update"),
(TimeKeeper, "_async_update_context_event", "ContextEventProperties",
f"/api/open-metadata/time-keeper/context-events/{GUID}/update"),
(SchemaMaker, "_async_update_schema_type", "SchemaTypeProperties",
f"/api/open-metadata/schema-maker/schema-types/{GUID}/update"),
(SchemaMaker, "_async_update_schema_attribute", "SchemaAttributeProperties",
f"/api/open-metadata/schema-maker/schema-attributes/{GUID}/update"),
(SubjectArea, "_async_update_subject_area", "SubjectAreaProperties",
f"/api/open-metadata/subject-area/collections/{GUID}/update"),
]
IDS = [c[1] for c in CASES]


class _Response:
def json(self):
return {"class": "VoidResponse"}


def _client(cls, calls: list, monkeypatch):
monkeypatch.setattr(cls, "check_connection", lambda self: "test")
client = cls("view-server", "https://localhost:1", "user", "pwd")

async def fake_make_request(method, url, payload=None, *args, **kwargs):
calls.append({"method": method, "url": url,
"body": json.loads(payload) if isinstance(payload, str) else payload})
return _Response()

client._async_make_request = fake_make_request
return client


def _body(prop_class: str) -> dict:
return {"class": "UpdateElementRequestBody", "mergeUpdate": True,
"properties": {"class": prop_class, "displayName": "pyeg-test"}}


@pytest.mark.asyncio
@pytest.mark.parametrize("cls, method, prop_class, path", CASES, ids=IDS)
async def test_update_sends_dict_body(cls, method, prop_class, path, monkeypatch):
calls = []
client = _client(cls, calls, monkeypatch)
await getattr(client, method)(GUID, _body(prop_class))

assert len(calls) == 1
assert calls[0]["method"] == "POST"
assert calls[0]["url"].endswith(path)
assert calls[0]["body"]["class"] == "UpdateElementRequestBody"
assert calls[0]["body"]["properties"] == {"class": prop_class, "displayName": "pyeg-test"}


@pytest.mark.asyncio
@pytest.mark.parametrize("cls, method, prop_class, path", CASES, ids=IDS)
async def test_update_sends_model_body(cls, method, prop_class, path, monkeypatch):
calls = []
client = _client(cls, calls, monkeypatch)
await getattr(client, method)(GUID, UpdateElementRequestBody.model_validate(_body(prop_class)))

assert len(calls) == 1
assert calls[0]["url"].endswith(path)
assert calls[0]["body"]["properties"]["class"] == prop_class
Loading