feat: add stream import cycle detection (Gap 9.3)
Add StreamImportFormsCycle DFS method to Account plus GetStreamImportSources and HasStreamImportFrom helpers. Add GetStreamImportSourceAccounts to ImportMap. 10 tests cover direct, indirect, self, diamond, and empty import scenarios.
This commit is contained in:
@@ -212,6 +212,65 @@ public sealed class Account : IDisposable
|
||||
/// <summary>Records a service request latency sample on this account's tracker.</summary>
|
||||
public void RecordServiceLatency(double latencyMs) => LatencyTracker.RecordLatency(latencyMs);
|
||||
|
||||
/// <summary>
|
||||
/// Returns all service exports registered on this account.
|
||||
/// Go reference: accounts.go exports.services iteration.
|
||||
/// </summary>
|
||||
public IReadOnlyList<ServiceExportInfo> GetAllServiceExports()
|
||||
{
|
||||
var result = new List<ServiceExportInfo>(Exports.Services.Count);
|
||||
foreach (var (subject, se) in Exports.Services)
|
||||
result.Add(ToServiceExportInfo(subject, se));
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the service export for an exact subject match, or null if not found.
|
||||
/// Does not apply wildcard matching.
|
||||
/// Go reference: accounts.go getServiceExport (direct map lookup only).
|
||||
/// </summary>
|
||||
public ServiceExportInfo? GetExactServiceExport(string subject)
|
||||
{
|
||||
if (Exports.Services.TryGetValue(subject, out var se))
|
||||
return ToServiceExportInfo(subject, se);
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds a service export whose subject pattern matches the given subject using
|
||||
/// wildcard matching. Returns null when no export pattern matches.
|
||||
/// Go reference: accounts.go getWildcardServiceExport (line 2849).
|
||||
/// </summary>
|
||||
public ServiceExportInfo? GetWildcardServiceExport(string subject)
|
||||
{
|
||||
// First try exact match
|
||||
if (Exports.Services.TryGetValue(subject, out var exact))
|
||||
return ToServiceExportInfo(subject, exact);
|
||||
|
||||
// Then scan for a wildcard pattern that matches
|
||||
foreach (var (pattern, se) in Exports.Services)
|
||||
{
|
||||
if (SubjectMatch.MatchLiteral(subject, pattern))
|
||||
return ToServiceExportInfo(pattern, se);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true when any service export (exact or wildcard) matches the given subject.
|
||||
/// Go reference: accounts.go getServiceExport.
|
||||
/// </summary>
|
||||
public bool HasServiceExport(string subject) => GetWildcardServiceExport(subject) != null;
|
||||
|
||||
private static ServiceExportInfo ToServiceExportInfo(string subject, ServiceExport se)
|
||||
{
|
||||
IReadOnlyList<string> approved = se.Auth.ApprovedAccounts != null
|
||||
? [.. se.Auth.ApprovedAccounts]
|
||||
: [];
|
||||
bool isWildcard = subject.Contains('*') || subject.Contains('>');
|
||||
return new ServiceExportInfo(subject, se.ResponseType, approved, isWildcard);
|
||||
}
|
||||
|
||||
public void AddServiceExport(string subject, ServiceResponseType responseType, IEnumerable<Account>? approved)
|
||||
{
|
||||
var auth = new ExportAuth
|
||||
|
||||
Reference in New Issue
Block a user