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
45 changes: 45 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,51 @@ going forward, so there's no mirroring step: once the API edit +
`refresh_specs` regeneration + processor wiring are done and verified,
that's the complete change.

## Auditing the OMVS clients against the `.http` ground truth

`scripts/omvs_audit.py` reconciles every `_async_*` method in `pyegeria/omvs/`
against the `.http` collections, which are the ground truth for URLs, verbs,
and request-body classes.

```bash
# pyegeria/http clients/ is GITIGNORED — absent in fresh worktrees.
# Point the script at a checkout that has it:
export PYEGERIA_HTTP_DIR="/path/to/egeria-python/pyegeria/http clients"

python scripts/omvs_audit.py --quiet # full audit -> omvs_audit_report.md
python scripts/omvs_audit.py --service location-arena # one service
```

Exit status is 1 when any confirmed defect is found, so it can gate CI.

**What it checks, and why each check exists** — every category below was added
after a real defect slipped through an earlier, weaker version of this script:

- **VERB** — compares the actual HTTP verb. Helper verbs are discovered by
parsing `_server_client.py` rather than hardcoded: *every* `_async_*_request`
helper is POST, and an earlier hardcoded table wrongly mapped the `get_*`
helpers to GET, which is how a batch of GET-vs-POST defects went undetected.
A direct `_async_make_request` wins over a helper, because some methods call
a GUID-resolution helper first.
- **PATH** — compares full normalised paths. It deliberately does **not** strip
trailing verbs (`/attach` vs `/detach`) or the leading service segment; an
earlier version did, and that masked exactly the bugs worth finding.
- **BODY** — compares the request-body class (`FilterRequestBody` vs
`ResultsRequestBody`, etc.). Heuristic: it takes the first `*RequestBody`
identifier in the method, which may be a signature annotation, so treat body
findings as lower-confidence than verb/path.
- **LINT** — structural URL defects detectable without ground truth: double
slashes, a path parameter with no separator before it (`/agreements{guid}`),
and underscores in path segments (Egeria uses hyphens).

URL extraction is AST-based, not regex — multi-line f-strings silently produced
empty paths under the regex version, which made whole modules look clean.

When triaging a finding, confirm it against the `.http` file before changing
code, and prefer the SDK's existing helpers (`_async_get_name_request`,
`_async_get_results_body_request`, …) over hand-rolled `_async_make_request`
calls, so verb and body shape stay correct by construction.

## Commits

- Always use `git commit -s` to sign off commits. This appends `Signed-off-by: Dan Wolfson <dan.wolfson@pdr-associates.com>` — DCO is enforced on this repo and unsigned commits will be rejected.
Expand Down
705 changes: 705 additions & 0 deletions omvs_audit_report.md

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions pyegeria/omvs/action_author.py
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,7 @@ async def _async_get_governance_action_process(
dict | list | str
The governance action process.
"""
url = f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/action-author/governance-action-processes/{process_guid}/retrieve"
url = f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/governance-officer/governance-definitions/{process_guid}/retrieve"
return await self._async_get_guid_request(
url,
"GovernanceActionProcess",
Expand Down Expand Up @@ -451,7 +451,7 @@ async def _async_get_governance_action_process_graph(
dict | list | str
The governance action process graph.
"""
url = f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/action-author/governance-action-processes/{process_guid}/graph"
url = f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/governance-officer/governance-action-processes/{process_guid}/graph"
return await self._async_get_guid_request(
url,
"GovernanceActionProcess",
Expand Down
2 changes: 1 addition & 1 deletion pyegeria/omvs/actor_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -578,7 +578,7 @@ async def _async_detach_asset_from_profile(self, asset_guid: str, it_profile_gui
"forDuplicateProcessing": false
}
"""
url = (f"{self.command_root}/assets/{asset_guid}/it_profiles/{it_profile_guid}/detach")
url = (f"{self.command_root}/assets/{asset_guid}/it-profiles/{it_profile_guid}/detach")

await self._async_delete_element_request(url, body)
logger.debug(f"Detached asset {asset_guid} from it profile {it_profile_guid}")
Expand Down
4 changes: 2 additions & 2 deletions pyegeria/omvs/asset_maker.py
Original file line number Diff line number Diff line change
Expand Up @@ -1650,7 +1650,7 @@ async def _async_detach_catalog_target(
"class" : "DeleteRelationshipRequestBody"
}
"""
url = f"{self.asset_command_root}/integration-connectors/{integration_connector_guid}/catalog-targets/{metadata_element_guid}/delete"
url = f"{self.asset_command_root}/integration-connectors/{integration_connector_guid}/catalog-targets/{metadata_element_guid}/detach"
await self._async_delete_relationship_request(url, body)

@dynamic_catch
Expand Down Expand Up @@ -4856,7 +4856,7 @@ async def _async_get_actions_for_requester(
-------
list | dict | str
"""
url = f"{self.asset_command_root}/actions/by-requester/{metadata_element_guid}"
url = f"{self.asset_command_root}/elements/{metadata_element_guid}/requested/actions"
return await self._async_activity_status_request(
url,
_type="Action",
Expand Down
2 changes: 1 addition & 1 deletion pyegeria/omvs/collection_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -5423,7 +5423,7 @@ async def _async_detach_agreement_item(self, agreement_guid: str, agreement_item
"""
url = (
f"{self.platform_url}/servers/{self.view_server}/api/open-metadata/collection-manager/collections"
f"/agreements"
f"/agreements/"
f"{agreement_guid}/agreement-items/{agreement_item_guid}/detach")
await self._async_delete_relationship_request(url, body)
logger.info(f"Detached agreement item {agreement_item_guid} from {agreement_guid}")
Expand Down
Loading