Skip to content

Commit f2f9dcb

Browse files
authored
docs(issues): log ISSUE-128..134, verify 127/129/131 against Egeria source (#438)
* docs(issues): log ISSUE-128..134, verify 127/129/131 against Egeria source - ISSUE-128, 130: Egeria server bugs (Retention subtype typo; YAML secrets store caches users forever), logged by the Resource Explorer session. - ISSUE-129: get_related_metadata_elements and two linked-by-* siblings drop paging/lineage arguments. Contract checked against MetadataExpertResource.java: startingAtEnd is a current query parameter on related-elements only. - ISSUE-131: 13 (not 27) *_by_name wrappers drop the type filter; the 14 find_* grep hits are correct. - ISSUE-132: DeleteElementRequestBody sends cascadeDelete, Egeria reads cascadedDelete and ignores unknown fields. On hold for an Egeria fix. - ISSUE-133: governance_officer reads subComponents; Egeria 6.2 returns nestedSolutionComponents. - ISSUE-134: PYEGERIA_ENABLE_LOGGER_CATCH makes @dynamic_catch return None on any failure. - ISSUE-126/127: marked merged in #429; 127 is only partly fixed (ISSUE-132). Signed-off-by: Dan Wolfson <dan.wolfson@pdr-associates.com> * docs(issues): ISSUE-131 and ISSUE-133 fixed in #439; correct 133's impact The governance_officer formatter ISSUE-133 named as the real bug has no callers, so no live output was affected. Signed-off-by: Dan Wolfson <dan.wolfson@pdr-associates.com> --------- Signed-off-by: Dan Wolfson <dan.wolfson@pdr-associates.com>
1 parent c9dc7a4 commit f2f9dcb

1 file changed

Lines changed: 182 additions & 8 deletions

File tree

‎PYEGERIA_ISSUES.md‎

Lines changed: 182 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,180 @@ enough to track there too).
143143

144144
---
145145

146+
### ISSUE-134: With `PYEGERIA_ENABLE_LOGGER_CATCH` on, `@dynamic_catch` turns every failure into a `None` return, indistinguishable from "nothing found"
147+
148+
**Layer:** pyegeria · **Status:** open, not fixed · **Found:** 2026-10-10, reported by the Resource Explorer
149+
session; confirmed from code.
150+
151+
**What:** `dynamic_catch` (`pyegeria/core/utils.py` ~439) returns `logger.catch(func)` when
152+
`app_settings.Debug.enable_logger_catch` is true (`PYEGERIA_ENABLE_LOGGER_CATCH`, `config.py` ~315). loguru's
153+
`catch` defaults to `reraise=False`, so any exception -- 401, 500, timeout, validation error -- is logged and the
154+
method returns `None`. Checked with loguru 0.7.3: both a sync and an `async def` function return `None`. It applies
155+
to all ~1,657 `@dynamic_catch` methods in `pyegeria/omvs/`. The choice is also made once, at import time, so
156+
changing the setting afterwards has no effect.
157+
158+
**Effect:** a caller cannot tell a failed read from an empty one; a `None` can render as "no results". Same family as
159+
ISSUE-123 (a failed measurement indistinguishable from a real zero). Resource Explorer now treats `None` from a
160+
wrapped method as "unreadable", never as "no result".
161+
162+
**Fix (not made):** pass `reraise=True` to `logger.catch` (keeps the logging, restores the exception), or document
163+
that the setting is debug-only and must not be used by applications that act on results.
164+
165+
---
166+
167+
### ISSUE-133: Egeria 6.2 returns a solution component's children as `nestedSolutionComponents`; one pyegeria formatter still reads only `subComponents`
168+
169+
**Layer:** pyegeria · **Status:** fixed in #439 (2026-10-10), pending merge · **Found:** 2026-10-10, reported by the Resource Explorer session
170+
(`SolutionArchitect.get_solution_component_by_guid` JSON on 6.2); confirmed from Egeria source.
171+
172+
**What:** Egeria's REST element (`AttributedMetadataElement.java` ~387) carries child components as
173+
`nestedSolutionComponents` (the `SolutionComposition` relationship, 0730), each a `RelatedMetadataElementSummary`
174+
(`relatedElement.elementHeader.guid`). It has no `subComponents` field; `subComponents` appears in Egeria only in
175+
Java archive builders, not in any REST response. A caller reading `subComponents` sees no children.
176+
177+
**pyegeria readers:**
178+
- `pyegeria/omvs/governance_officer.py` ~274 (`_extract_solution_components_properties`) reads **only**
179+
`subComponents`, so its sub-components column would always be empty. It turned out to have no callers
180+
(`SolutionArchitect` has its own method of the same name), so no live output was affected.
181+
- `solution_architect.py` ~609 and ~5181, and `md_processing/v2/solution_architect.py` ~384, already read
182+
`nestedSolutionComponents`. Each also has a leftover `subComponents` fallback (~675, ~5187, ~388) that never
183+
matches. These do no harm, but they use the old flat shape (`elementHeader` directly, not `relatedElement`), so they
184+
would mis-parse if the field ever came back.
185+
186+
**Fixed in #439:** `governance_officer.py` reads `nestedSolutionComponents` (through `relatedElement`), and the
187+
three dead `subComponents` fallbacks are removed. Micro-tests in `tests/micro-tests/test_issue_131_133.py`.
188+
189+
---
190+
191+
### ISSUE-132: `DeleteElementRequestBody` serializes the cascade flag as `cascadeDelete`; Egeria's field is `cascadedDelete` and unknown fields are ignored, so no pyegeria cascade delete has ever cascaded
192+
193+
**Layer:** pyegeria · **Status:** open, on hold: an Egeria fix for cascade delete is coming (owner, 2026-10-10). Wait
194+
for it, live-test cascade delete against it, then decide the pyegeria change; the fix below may change once the
195+
Egeria side lands. Confirmed from Egeria source, not yet live-tested · **Found:**
196+
2026-10-10, while checking ISSUE-129's server contract against Egeria source instead of the `.http` files.
197+
198+
**What:** `pyegeria/models/models.py` `DeleteElementRequestBody.cascade_delete` accepts both spellings on input but
199+
serializes as `serialization_alias="cascadeDelete"`. The comment there records that the `.http` files were split
200+
about 50/50 and that the choice was unverified (ISSUE-62). Egeria source settles it (`egeria-v6/egeria` at
201+
`af400039c2`, 2026-10-06): `DeleteElementRequestBody` -> `DeleteRequestBody` -> `DeleteOptions`
202+
(`open-metadata-framework/.../search/DeleteOptions.java`), whose field is `cascadedDelete` with
203+
`getCascadedDelete()`/`setCascadedDelete()`. It is unchanged since 2025-08-04. The class is
204+
`@JsonAutoDetect(getterVisibility=PUBLIC_ONLY, fieldVisibility=NONE)` and `@JsonIgnoreProperties(ignoreUnknown=true)`,
205+
with no `@JsonProperty`/`@JsonAlias`, so the JSON name is `cascadedDelete` and `cascadeDelete` is silently ignored.
206+
207+
**Effect:** every pyegeria delete that uses `DeleteElementRequestBody` with cascade on (the ISSUE-62 fix,
208+
`validate_delete_element_request`, ISSUE-127's fix in #429, `MetadataExpert.delete_metadata_element(cascade_delete=True)`)
209+
sends a flag Egeria discards, so it deletes without cascading. A caller-supplied dict using the correct
210+
`cascadedDelete` key is also re-serialized as `cascadeDelete`, so even that does not work. The models module's own
211+
`DeleteRequestBody.cascaded_delete` already uses the right name.
212+
213+
**Fix (not made):** change the serialization alias to `cascadedDelete` (keep both spellings accepted on input), and
214+
fix the 19 `"cascadeDelete"` references in `_server_client.py`, `product_manager.py`, `actor_manager.py`,
215+
`valid_metadata.py`, `metadata_expert.py` and `reference_data.py` (several are docstring samples). Then live-test one
216+
cascade delete, and re-test ISSUE-127's `DataStructure` refusal. The `.http` files using `cascadedDelete` are correct;
217+
the ones using `cascadeDelete` are the ones in error (worth reporting to Egeria).
218+
219+
**Lesson:** where the `.http` files disagree with each other, read the Java DTO (getter/setter names plus Jackson
220+
annotations); do not choose by majority or by what the code already sends.
221+
222+
---
223+
224+
### ISSUE-131: 13 `*_by_name` wrappers pass `metadata_element_type=`, but `_async_get_name_request` reads `metadata_element_type_name`, so the type filter is silently dropped
225+
226+
**Layer:** pyegeria · **Status:** fixed in #439 (2026-10-10), pending merge · **Found:** 2026-10-10, while building Resource Explorer's actor-graph lookups (`get_user_identities_by_name`, `get_actor_profiles_by_name`).
227+
228+
**What:** wrappers such as `ActorManager._async_get_user_identities_by_name` (`pyegeria/omvs/actor_manager.py` ~4195) build `params = {"metadata_element_type": metadata_element_type_name, ...}` and call `self._async_get_name_request(url, **params)`. The base method (`pyegeria/core/_server_client.py` ~6581) takes `metadata_element_type_name` and writes it to `"metadataElementTypeName"`, so the wrapper's key falls into `**kwargs` and never reaches the request body. The by-name searches run unfiltered by type.
229+
230+
**Scope (checked 2026-10-10):** `grep -rn '"metadata_element_type": metadata_element_type_name' pyegeria` matches 27 lines, but only 13 are broken. The other 14 are `find_*` wrappers that call `_async_find_request`, whose parameter really is `metadata_element_type` (`_server_client.py` ~6468), so they are correct; do not change them. The 13 broken ones are all the `*_by_name` wrappers that call `_async_get_name_request`:
231+
- `actor_manager.py`: `get_actor_profiles_by_name`, `get_actor_roles_by_name`, `get_user_identities_by_name`, `get_contribution_records_by_name`, `get_contact_details_by_name`, `get_perspectives_by_name`, `get_skills_by_name`
232+
- `collection_manager.py`: `get_collections_by_name`
233+
- `digital_business.py`: `get_business_capabilities_by_name`
234+
- `location_arena.py`: `get_locations_by_name`
235+
- `runtime_manager.py`: `get_metadata_repository_cohorts_by_name`
236+
- `subject_area.py`: `get_subject_areas_by_name`
237+
- `time_keeper.py`: `get_context_events_by_name`
238+
239+
**Root cause:** the two base helpers spell the same argument differently (`_async_find_request(metadata_element_type=...)` vs `_async_get_name_request(metadata_element_type_name=...)`), so a wrapper copied from a `find_*` sibling silently loses the filter.
240+
241+
**Effect:** callers relying on the type filter get elements of other types. Resource Explorer works round it by passing a full `FilterRequestBody` with `metadataElementTypeName`.
242+
243+
**Fixed in #439:** `_async_get_name_request` accepts both spellings (`metadata_element_type_name` wins if both are given), and the 13 wrappers now use `metadata_element_type_name`. The 14 `find_*` wrappers are unchanged. Micro-tests in `tests/micro-tests/test_issue_131_133.py` capture the request body for each wrapper. Same family as ISSUE-62, ISSUE-127 and ISSUE-129: an argument accepted and silently dropped.
244+
245+
---
246+
247+
### ISSUE-130: The YAML secrets store's user directory caches a user forever, so editing an existing user needs a platform restart
248+
249+
**Layer:** Egeria server (`YAMLSecretsStoreConnector`) · **Status:** open, reported to the Egeria lead · **Found:** 2026-10-09, while adding Resource Explorer's own account (`resourceexplorernpa`).
250+
251+
**What:** `getUser(userId)` returns the entry from `userAccountMap` if it is already there. `refreshSecrets()` re-reads the YAML file every `refreshTimeInterval` minutes, but `userAccountMap` (and `securityAccessControlMap`) are created only on the first load (when `secretsStoreFile == null`) and are never cleared afterwards. Re-reading the file only helps users the cache has never seen; the comment says "this may be a new user".
252+
253+
**Seen:** the account was first added without its `secrets` block, and the first token request cached it. After the password was added (and matched the servers file), `/api/token` still answered 401 for more than 10 minutes. It worked straight after a platform restart.
254+
255+
**Effect:** any change to an existing user (password, status, named-list membership) needs a platform restart.
256+
257+
**Fix (Egeria):** clear or rebuild `userAccountMap` and `securityAccessControlMap` in `refreshSecrets()`.
258+
259+
---
260+
261+
### ISSUE-129: `MetadataExpert.get_related_metadata_elements` (and two sibling relationship queries) accept paging, direction and lineage arguments but never send them
262+
263+
**Layer:** pyegeria · **Status:** open, not fixed · **Found:** 2026-10-09, in a code review of Resource Explorer's
264+
6.2 composition read (`blueprint_materializer.sub_component_guids`).
265+
266+
**What:** `_async_get_related_metadata_elements` (`pyegeria/omvs/metadata_expert.py` ~2886-2964) builds the URL from
267+
`guid` and `relationship_type` only, and posts `body_slimmer(body)`. `starting_at_end`, `start_from`, `page_size`,
268+
`for_lineage` and `for_duplicate_processing` are documented and silently dropped. So the server's default page size
269+
decides how many relationships come back, both ends of the relationship are returned whatever `starting_at_end` says,
270+
and Memento-classified elements are handled by the server default rather than the caller's `for_lineage`. A caller
271+
that guards "a full page means I cannot tell if there are more" is guarding a value that never reached Egeria. Same
272+
shape as ISSUE-62 and ISSUE-127.
273+
274+
**Same bug in two siblings (checked 2026-10-10):** `_async_get_all_metadata_element_relationships` (~3056,
275+
`linked-by-any-type`) and `_async_get_metadata_element_relationships` (~3233, `linked-by-type`) also accept
276+
`start_from` and `page_size` and send neither. They also accept `starting_at_end`, but Egeria's endpoints for
277+
these two take no such parameter (both ends are fixed by the URL), so that argument is meaningless there and
278+
should be removed rather than wired up.
279+
280+
**Workaround (RE):** put `startFrom`/`pageSize` in the `ResultsRequestBody` itself and filter entries by end.
281+
282+
**Server contract (verified 2026-10-10 against Egeria source, `egeria-v6/egeria` at `af400039c2`, 2026-10-06 --
283+
not just the `.http` file):** `MetadataExpertResource.java` declares `startingAtEnd` as
284+
`@RequestParam(required = false, defaultValue = "0") int` on `/related-elements/{elementGUID}/any-type` and
285+
`/related-elements/{elementGUID}/type/{relationshipTypeName}`, and `MetadataExpertRESTServices` passes it through, so
286+
the `?startingAtEnd=` query parameter is current, not a leftover. The two `linked-by-*` endpoints take only a
287+
`ResultsRequestBody`. `ResultsRequestBody` extends `QueryOptions` -> `PagingOptions` (`startFrom`, `pageSize`) ->
288+
`GetOptions` -> `BasicOptions` (`forLineage`, `forDuplicateProcessing`, `effectiveTime`), so the other four
289+
arguments belong in the body.
290+
291+
**Fix (not made):** in the two `related-elements` methods, add `?startingAtEnd={starting_at_end}` to the URL
292+
(`_async_get_all_related_metadata_elements`, ~2800-2808, already does this and is the pattern to copy), and put
293+
`startFrom`/`pageSize`/`forLineage`/`forDuplicateProcessing` in the body. In the two `linked-by-*` methods, put
294+
paging in the body and drop `starting_at_end`. Only add body fields the caller's body does not already set, so
295+
"body supersedes" (which ISSUE-127 relies on) still holds; the `page_size` default is `max_paging_size`, so
296+
overwriting would silently change callers who set it in the body.
297+
298+
---
299+
300+
### ISSUE-128: Retention cannot be set on Egeria 6.2: the server registers the Jackson subtype as `RetentionProperties.class` (typo), and pyegeria only allows `RetentionClassificationProperties`
301+
302+
**Status:** open, found 2026-10-09 by Resource Explorer's first Retention publish on Egeria 6.2. **Layer:** Egeria server (a type registration typo). **Decision (owner, 2026-10-09):** wait for the Egeria fix; no pyegeria workaround. Retention publishing stays blocked until then.
303+
304+
**What (all proven live on 6.2, with raw HTTP POSTs to `classification-explorer/elements/{guid}/retention` on a throwaway asset):**
305+
- `"class": "RetentionClassificationProperties"` is refused with `InvalidTypeIdException`. This is what pyegeria documents and whitelists, and it worked on the pre-6.2 server (processors.py comment, 2026-08-03).
306+
- `"class": "RetentionProperties"` (the `.http` ground truth) is also refused with `InvalidTypeIdException`.
307+
- `"class": "RetentionProperties.class"` gets **HTTP 200**, and the classification reads back with `retentionBasis` 2 and the notes.
308+
- The server's known-type list in the error has 96 ids. The only Retention entry is literally `RetentionProperties.class`. That looks like an `@JsonSubTypes.Type(name = "RetentionProperties.class")` typo in Egeria's `ClassificationProperties`.
309+
- pyegeria's `validate_new_classification_request` whitelist (`prop=["RetentionClassificationProperties"]`) refuses anything else client-side, so no Retention can be set through pyegeria on 6.2.
310+
311+
(An earlier version of this entry said 6.2 knows `RetentionProperties`. That was a misread of the error text, corrected the same day.)
312+
313+
**Where seen:** Resource Explorer's Curate classifications step on two repository assets. Both were refused, so nothing was written. Confidentiality, Criticality, Confidence and Impact property classes are registered normally.
314+
315+
**Candidate fix:**
316+
1. **Egeria:** correct the subtype name to `RetentionProperties`, matching the `.http` file. Report it to Mandy.
317+
2. **pyegeria, after Egeria's fix:** change the whitelist and docstring to `RetentionProperties`, the `.http` name. No workaround for the typo, by the owner's decision.
318+
3. Re-check `md_processing/v2/processors.py` and `v2/curation.py`, which both pin `RetentionClassificationProperties`.
319+
146320
### ISSUE-117: Cascade delete that takes the soft-delete (Memento) path fails partway with `OMAG-REPOSITORY-HANDLER-400-010` unless `forLineage=true` — leaves the asset live and an anchored element already soft-deleted
147321

148322
**Layer:** Egeria Server · **Status:** open, workaround known, not yet
@@ -1184,8 +1358,7 @@ into the entry now, so it isn't rediscovered from scratch later.
11841358

11851359
### ISSUE-126: `ProductManager` and `DigitalBusiness` never set `collection_command_root`, so every inherited `CollectionManager` method that uses it raises `AttributeError`
11861360

1187-
**Layer:** pyegeria · **Status:** fixed on branch `fix/issue-126-127-collection-manager-subclasses`
1188-
(2026-10-05), pending PR/merge · **Found:** 2026-10-05, live cleanup of a throwaway data contract (PR #427's
1361+
**Layer:** pyegeria · **Status:** fixed, merged to `main` in #429 (`68279835`) · **Found:** 2026-10-05, live cleanup of a throwaway data contract (PR #427's
11891362
live check).
11901363

11911364
**Fix + scope (2026-10-05):** both constructors now call `CollectionManager.__init__`, as `GlossaryManager`
@@ -1204,15 +1377,13 @@ pm.delete_collection(guid, cascade=True)
12041377
# AttributeError: 'ProductManager' object has no attribute 'collection_command_root'
12051378
```
12061379

1207-
**Fix (not made):** call `CollectionManager.__init__` from both constructors, or set the attribute. A test that
1208-
constructs each `CollectionManager` subclass and asserts the attribute exists would have caught it.
1380+
**Fixed in #429:** see "Fix + scope" above.
12091381

12101382
---
12111383

12121384
### ISSUE-127: `CollectionManager.delete_collection(cascade=True)` silently never sends `cascadeDelete` -- same shape as ISSUE-62
12131385

1214-
**Layer:** pyegeria · **Status:** fixed on branch `fix/issue-126-127-collection-manager-subclasses`
1215-
(2026-10-05), pending PR/merge · **Found:** 2026-10-05, found by capturing the request body while diagnosing a
1386+
**Layer:** pyegeria · **Status:** partly fixed in #429 (`68279835`); cascade still blocked by ISSUE-132 · **Found:** 2026-10-05, found by capturing the request body while diagnosing a
12161387
failed cleanup.
12171388

12181389
**Fix + scope (2026-10-05):** a scan of all 42 callers of `_async_delete_element_request` found exactly two
@@ -1230,8 +1401,11 @@ the flag is discarded. Captured: `delete_collection("g", cascade=True)` posts
12301401
`{'class': 'DeleteElementRequestBody', 'forLineage': False, 'forDuplicateProcessing': False}` with no
12311402
`cascadeDelete`. `MetadataExpert.delete_metadata_element(cascade_delete=True)` does send it.
12321403

1233-
**Fix (not made):** do not pre-fill the body (pass `None` so the helper applies the flag), or put `cascadeDelete` in
1234-
the pre-filled dict. Worth grepping for other callers that pre-fill a default dict *and* pass a cascade flag.
1404+
**Fixed in #429, but cascade still does not reach Egeria -- see ISSUE-132.** #429 made the flag reach the request
1405+
body, but under the name `cascadeDelete`. Egeria's real field is `cascadedDelete`, and it ignores unknown fields, so
1406+
the flag is still dropped, now on the server side. #429's docstring "correction" from `cascadedDelete` to
1407+
`cascadeDelete` was also the wrong way round. The `DataStructure` refusal described below is probably this bug
1408+
rather than an Egeria quirk; re-test once ISSUE-132 is fixed.
12351409

12361410
**Related, an Egeria quirk found alongside:** even with `cascadeDelete: true`, Egeria refused to delete a
12371411
`DataStructure` that still had a member `DataField` (`OMAG-GENERIC-HANDLERS-403-005 ... validateNoMemberDataFields`),

0 commit comments

Comments
 (0)