feat: scope GalaxyRepositoryGrpcService results via IGalaxyBrowseScopeProvider
This commit is contained in:
+39
-14
@@ -15,18 +15,24 @@ namespace ZB.MOM.WW.GalaxyRepository.Grpc;
|
||||
/// <c>TestConnection</c> remains a direct SQL probe since callers use it as a health check.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// This service applies <b>no</b> per-identity browse-subtree filtering — the full
|
||||
/// hierarchy is projected (<c>null</c> subtree globs). Authorization (including any
|
||||
/// subtree scoping) is the responsibility of the hosting gateway's interceptor layer.
|
||||
/// Per-identity browse-subtree filtering is delegated to the injected
|
||||
/// <see cref="IGalaxyBrowseScopeProvider"/>. The default
|
||||
/// <see cref="NullGalaxyBrowseScopeProvider"/> returns <c>null</c> globs, so the full
|
||||
/// hierarchy is projected and behavior is unchanged. A hosting gateway can register its
|
||||
/// own provider to scope <c>DiscoverHierarchy</c>/<c>BrowseChildren</c> results — and the
|
||||
/// <c>object_count</c>/<c>attribute_count</c> totals streamed by <c>WatchDeployEvents</c> —
|
||||
/// to the caller's allowed subtrees.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <param name="repository">Direct SQL surface used by <c>TestConnection</c>.</param>
|
||||
/// <param name="cache">Shared hierarchy cache that <c>DiscoverHierarchy</c>/<c>BrowseChildren</c>/<c>GetLastDeployTime</c> serve from.</param>
|
||||
/// <param name="notifier">Deploy-event source streamed by <c>WatchDeployEvents</c>.</param>
|
||||
/// <param name="scope">Resolves the per-caller browse-subtree globs applied to browse/discover results.</param>
|
||||
public sealed class GalaxyRepositoryGrpcService(
|
||||
IGalaxyRepository repository,
|
||||
IGalaxyHierarchyCache cache,
|
||||
IGalaxyDeployNotifier notifier) : ProtoGalaxyRepository.GalaxyRepositoryBase
|
||||
IGalaxyDeployNotifier notifier,
|
||||
IGalaxyBrowseScopeProvider scope) : ProtoGalaxyRepository.GalaxyRepositoryBase
|
||||
{
|
||||
private static readonly TimeSpan FirstLoadWaitBudget = TimeSpan.FromSeconds(5);
|
||||
private const int DefaultDiscoverPageSize = 1000;
|
||||
@@ -82,14 +88,13 @@ public sealed class GalaxyRepositoryGrpcService(
|
||||
}
|
||||
|
||||
int pageSize = ResolvePageSize(request.PageSize);
|
||||
// The shared library applies no per-identity subtree scoping; the hosting
|
||||
// gateway enforces authorization at its interceptor layer.
|
||||
string filterSignature = GalaxyHierarchyProjector.ComputeFilterSignature(request, browseSubtreeGlobs: null);
|
||||
IReadOnlyList<string>? browseSubtrees = scope.ResolveBrowseSubtrees(context);
|
||||
string filterSignature = GalaxyHierarchyProjector.ComputeFilterSignature(request, browseSubtrees);
|
||||
PageToken pageToken = ParsePageToken(request.PageToken, entry.Sequence, filterSignature);
|
||||
GalaxyHierarchyQueryResult query = GalaxyHierarchyProjector.Project(
|
||||
entry,
|
||||
request,
|
||||
browseSubtreeGlobs: null,
|
||||
browseSubtrees,
|
||||
pageToken.Offset,
|
||||
pageSize);
|
||||
int offset = pageToken.Offset;
|
||||
@@ -131,6 +136,7 @@ public sealed class GalaxyRepositoryGrpcService(
|
||||
}
|
||||
|
||||
int pageSize = ResolveBrowsePageSize(request.PageSize);
|
||||
IReadOnlyList<string>? browseSubtrees = scope.ResolveBrowseSubtrees(context);
|
||||
|
||||
// Resolve the parent id once so the page-token signature can include it
|
||||
// and the projector sees the same resolved id when memoizing. The projector
|
||||
@@ -139,13 +145,13 @@ public sealed class GalaxyRepositoryGrpcService(
|
||||
// and keeps the projector self-contained.
|
||||
int parentId = GalaxyBrowseProjector.ResolveParentId(entry, request);
|
||||
string filterSignature = GalaxyBrowseProjector.ComputeFilterSignature(
|
||||
request, browseSubtreeGlobs: null, parentId);
|
||||
request, browseSubtrees, parentId);
|
||||
PageToken pageToken = ParsePageToken(request.PageToken, entry.Sequence, filterSignature);
|
||||
|
||||
GalaxyBrowseChildrenResult result = GalaxyBrowseProjector.ProjectChildren(
|
||||
entry,
|
||||
request,
|
||||
browseSubtreeGlobs: null,
|
||||
browseSubtrees,
|
||||
pageToken.Offset,
|
||||
pageSize);
|
||||
|
||||
@@ -181,6 +187,11 @@ public sealed class GalaxyRepositoryGrpcService(
|
||||
{
|
||||
DateTimeOffset? lastSeen = request.LastSeenDeployTime?.ToDateTimeOffset();
|
||||
|
||||
// The caller's identity (and therefore its browse-subtree constraints) is fixed
|
||||
// for the lifetime of the stream, so resolve the subtrees once rather than per
|
||||
// streamed event.
|
||||
IReadOnlyList<string>? browseSubtrees = scope.ResolveBrowseSubtrees(context);
|
||||
|
||||
await foreach (GalaxyDeployEventInfo info in notifier
|
||||
.SubscribeAsync(context.CancellationToken)
|
||||
.ConfigureAwait(false))
|
||||
@@ -195,7 +206,7 @@ public sealed class GalaxyRepositoryGrpcService(
|
||||
}
|
||||
lastSeen = null;
|
||||
|
||||
await responseStream.WriteAsync(MapDeployEvent(info), context.CancellationToken).ConfigureAwait(false);
|
||||
await responseStream.WriteAsync(MapDeployEvent(info, browseSubtrees), context.CancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -223,14 +234,28 @@ public sealed class GalaxyRepositoryGrpcService(
|
||||
}
|
||||
}
|
||||
|
||||
private static DeployEvent MapDeployEvent(GalaxyDeployEventInfo info)
|
||||
private DeployEvent MapDeployEvent(
|
||||
GalaxyDeployEventInfo info,
|
||||
IReadOnlyList<string>? browseSubtrees)
|
||||
{
|
||||
int objectCount = info.ObjectCount;
|
||||
int attributeCount = info.AttributeCount;
|
||||
if (browseSubtrees is { Count: > 0 } && cache.Current.HasData)
|
||||
{
|
||||
GalaxyHierarchyQueryResult scoped = GalaxyHierarchyProjector.Project(
|
||||
cache.Current,
|
||||
new DiscoverHierarchyRequest(),
|
||||
browseSubtrees);
|
||||
objectCount = scoped.TotalObjectCount;
|
||||
attributeCount = scoped.Objects.Sum(obj => obj.Attributes.Count);
|
||||
}
|
||||
|
||||
DeployEvent ev = new()
|
||||
{
|
||||
Sequence = (ulong)info.Sequence,
|
||||
ObservedAt = Timestamp.FromDateTimeOffset(info.ObservedAt),
|
||||
ObjectCount = info.ObjectCount,
|
||||
AttributeCount = info.AttributeCount,
|
||||
ObjectCount = objectCount,
|
||||
AttributeCount = attributeCount,
|
||||
TimeOfLastDeployPresent = info.TimeOfLastDeploy.HasValue,
|
||||
};
|
||||
if (info.TimeOfLastDeploy.HasValue)
|
||||
|
||||
Reference in New Issue
Block a user