diff --git a/docs/GalaxyRepository.md b/docs/GalaxyRepository.md index 8ef588b..ff61d60 100644 --- a/docs/GalaxyRepository.md +++ b/docs/GalaxyRepository.md @@ -36,6 +36,7 @@ The service is defined in | `GetLastDeployTime` | Returns the cached `galaxy.time_of_last_deploy`. Served from the shared hierarchy cache; refreshed in the background. | | `DiscoverHierarchy` | Returns one page of the deployed hierarchy plus each returned object's attributes (configured and built-in — see [Built-in vs configured attributes](#built-in-vs-configured-attributes)). **Served from cache** — see [Hierarchy Cache](#hierarchy-cache). | | `WatchDeployEvents` | **Server-streaming.** The server emits the current state immediately on subscribe (so clients can bootstrap without waiting), then emits one event per detected deploy change. See [Deploy Notifications](#deploy-notifications). | +| `BrowseChildren` | Returns the direct children of one parent object (or root objects when `parent` is unset). Filters mirror `DiscoverHierarchy`. Includes a per-child `has_children` hint so UIs can draw expand triangles without an extra round trip. **Served from cache.** | `DiscoverHierarchy` is a paged unary RPC. The raw request accepts `page_size` and `page_token`; the server defaults omitted page size to 1000 objects and @@ -52,6 +53,57 @@ alarm-only, historized-only, and `include_attributes = false` for a skeleton tree. All filters are applied with AND semantics, and `total_object_count` reports the post-filter count. +### BrowseChildren + +`BrowseChildren` is an OPC UA-style lazy expand: clients that walk one level at +a time — UI trees, OPC UA address-space bridges — call it instead of paging the +full hierarchy with `DiscoverHierarchy`. + +**Parent selection.** The `parent` oneof accepts `parent_gobject_id`, +`parent_tag_name`, or `parent_contained_path`. An empty oneof returns root +objects — those whose `parent_gobject_id` is 0. + +**Filters.** Category ids, template-chain substring, tag-name glob, alarm-only, +historized-only, and `include_attributes` all behave identically to +`DiscoverHierarchy` and are AND-combined. One important difference applies to +`alarm_bearing_only` and `historized_only`: an ancestor that does not itself +carry a matching attribute is still returned when one of its descendants does. +This is intentional — without it a UI tree cannot navigate to the matching +leaves. `DiscoverHierarchy`'s flat-list semantics filter out such intermediate +ancestors; `BrowseChildren` retains them so the path to each match remains +traversable. + +**`child_has_children` hint.** The reply carries a boolean parallel to +`children`, set true when the child has at least one matching descendant under +the same filter set. UIs can use this to decide whether to draw an expand +triangle without issuing a follow-up `BrowseChildren` call. Because the hint is +computed against the *filtered* descendant set, a branch that contains no +matching objects gets `false`, not `true`. + +**Paging.** Default page size is 500; the server caps any requested size at +5000. Page tokens encode `(cache_sequence, parent_id, filter_signature, +offset)`. A token from a different cache generation or a different filter set +returns `InvalidArgument`. The error messages reference "DiscoverHierarchy +page_token" because `BrowseChildren` reuses the same encoding and validation +path — if you see that wording in a `BrowseChildren` context it is expected. + +**Errors.** + +| Condition | Status code | +|-----------|-------------| +| Unknown parent | `NotFound` | +| First load not yet complete after 5 s | `Unavailable` | +| Stale or filter-mismatched page token | `InvalidArgument` | +| Missing `metadata:read` scope | `PermissionDenied` | +| No API key | `Unauthenticated` | + +**Authorization.** Same `metadata:read` scope as the other Galaxy RPCs. +`browse_subtrees` API-key constraints intersect with the result set. + +**Sort order.** Areas first, then `OrdinalIgnoreCase` by display name +(`browse_name` → `contained_name` → `tag_name`). Matches the dashboard tree so +server and dashboard views are consistent. + ## Hierarchy Cache The gateway holds a single shared `IGalaxyHierarchyCache` @@ -271,9 +323,13 @@ fields cannot express null. Use it to distinguish "no dimension reported" from ```text gRPC client(s) -> GalaxyRepositoryGrpcService (src/ZB.MOM.WW.MxGateway.Server/Grpc/) - DiscoverHierarchy, GetLastDeployTime -> IGalaxyHierarchyCache.Current - WatchDeployEvents -> IGalaxyDeployNotifier - TestConnection -> GalaxyRepository (direct SQL) + DiscoverHierarchy, GetLastDeployTime, BrowseChildren -> IGalaxyHierarchyCache.Current + WatchDeployEvents -> IGalaxyDeployNotifier + TestConnection -> GalaxyRepository (direct SQL) + +Dashboard (Blazor) + -> IDashboardBrowseService (DashboardBrowseService) + -> GalaxyBrowseProjector over IGalaxyHierarchyCache.Current GalaxyHierarchyRefreshService (BackgroundService) -> IGalaxyHierarchyCache.RefreshAsync @@ -309,9 +365,17 @@ Component breakdown: (`src/ZB.MOM.WW.MxGateway.Server/Grpc/GalaxyProtoMapper.cs`) converts row models to proto messages. Used by the cache during refresh to materialize the reply once. +- `GalaxyBrowseProjector` + (`src/ZB.MOM.WW.MxGateway.Server/Galaxy/GalaxyBrowseProjector.cs`) projects one level + of children out of an immutable cache entry. Memoizes the filtered child list + per cache-entry instance so repeated paging is an O(pageSize) slice rather than an + O(siblings) filter scan. The memo is keyed on the cache entry reference, so a new + entry from the background refresh makes the stale memo unreachable and it is + collected with it. `DashboardBrowseService` wraps this projector to drive the + dashboard's lazy-expand tree. - `GalaxyRepositoryGrpcService` (`src/ZB.MOM.WW.MxGateway.Server/Grpc/GalaxyRepositoryGrpcService.cs`) implements - the four RPCs. + the five RPCs. ## Configuration