nuget reorganization
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
using System.Collections.Immutable;
|
||||
using System.Collections.Immutable;
|
||||
using StellaOps.Concelier.Models.Observations;
|
||||
using StellaOps.Concelier.RawModels;
|
||||
|
||||
namespace StellaOps.Concelier.WebService.Contracts;
|
||||
|
||||
using StellaOps.Concelier.Core.Linksets;
|
||||
|
||||
namespace StellaOps.Concelier.WebService.Contracts;
|
||||
|
||||
public sealed record AdvisoryObservationQueryResponse(
|
||||
ImmutableArray<AdvisoryObservation> Observations,
|
||||
AdvisoryObservationLinksetAggregateResponse Linkset,
|
||||
@@ -16,4 +17,6 @@ public sealed record AdvisoryObservationLinksetAggregateResponse(
|
||||
ImmutableArray<string> Cpes,
|
||||
ImmutableArray<AdvisoryObservationReference> References,
|
||||
ImmutableArray<string> Scopes,
|
||||
ImmutableArray<RawRelationship> Relationships);
|
||||
ImmutableArray<RawRelationship> Relationships,
|
||||
double Confidence,
|
||||
ImmutableArray<AdvisoryLinksetConflict> Conflicts);
|
||||
|
||||
@@ -440,7 +440,9 @@ var observationsEndpoint = app.MapGet("/concelier/observations", async (
|
||||
result.Linkset.Cpes,
|
||||
result.Linkset.References,
|
||||
result.Linkset.Scopes,
|
||||
result.Linkset.Relationships),
|
||||
result.Linkset.Relationships,
|
||||
result.Linkset.Confidence,
|
||||
result.Linkset.Conflicts),
|
||||
result.NextCursor,
|
||||
result.HasMore);
|
||||
|
||||
|
||||
@@ -31,8 +31,8 @@ internal static class AdvisoryLinksetNormalization
|
||||
ArgumentNullException.ThrowIfNull(linkset);
|
||||
|
||||
var normalized = Build(linkset.PackageUrls);
|
||||
var confidence = CoerceConfidence(providedConfidence);
|
||||
var conflicts = ExtractConflicts(linkset);
|
||||
var confidence = ComputeConfidence(providedConfidence, conflicts);
|
||||
|
||||
return (normalized, confidence, conflicts);
|
||||
}
|
||||
@@ -128,4 +128,20 @@ internal static class AdvisoryLinksetNormalization
|
||||
|
||||
return conflicts;
|
||||
}
|
||||
|
||||
private static double? ComputeConfidence(double? providedConfidence, IReadOnlyList<AdvisoryLinksetConflict> conflicts)
|
||||
{
|
||||
if (providedConfidence.HasValue)
|
||||
{
|
||||
return CoerceConfidence(providedConfidence);
|
||||
}
|
||||
|
||||
if (conflicts.Count > 0)
|
||||
{
|
||||
// Basic heuristic until scoring pipeline is wired: any conflicts => lower confidence.
|
||||
return 0.5;
|
||||
}
|
||||
|
||||
return 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,58 @@
|
||||
using System.Collections.Immutable;
|
||||
using StellaOps.Concelier.Core.Linksets;
|
||||
using StellaOps.Concelier.Models.Observations;
|
||||
using StellaOps.Concelier.RawModels;
|
||||
|
||||
namespace StellaOps.Concelier.Core.Observations;
|
||||
|
||||
/// <summary>
|
||||
/// Aggregated linkset facets (aliases, purls, cpes, references, scopes, relationships) built from a set of observations.
|
||||
/// </summary>
|
||||
public sealed record AdvisoryObservationLinksetAggregate(
|
||||
ImmutableArray<string> Aliases,
|
||||
ImmutableArray<string> Purls,
|
||||
ImmutableArray<string> Cpes,
|
||||
ImmutableArray<AdvisoryObservationReference> References,
|
||||
ImmutableArray<string> Scopes,
|
||||
ImmutableArray<RawRelationship> Relationships);
|
||||
public sealed record AdvisoryObservationLinksetAggregate
|
||||
{
|
||||
public ImmutableArray<string> Aliases { get; init; }
|
||||
public ImmutableArray<string> Purls { get; init; }
|
||||
public ImmutableArray<string> Cpes { get; init; }
|
||||
public ImmutableArray<AdvisoryObservationReference> References { get; init; }
|
||||
public ImmutableArray<string> Scopes { get; init; }
|
||||
public ImmutableArray<RawRelationship> Relationships { get; init; }
|
||||
public double Confidence { get; init; }
|
||||
public ImmutableArray<AdvisoryLinksetConflict> Conflicts { get; init; }
|
||||
|
||||
public AdvisoryObservationLinksetAggregate(
|
||||
ImmutableArray<string> aliases,
|
||||
ImmutableArray<string> purls,
|
||||
ImmutableArray<string> cpes,
|
||||
ImmutableArray<AdvisoryObservationReference> references)
|
||||
: this(
|
||||
aliases,
|
||||
purls,
|
||||
cpes,
|
||||
references,
|
||||
ImmutableArray<string>.Empty,
|
||||
ImmutableArray<RawRelationship>.Empty,
|
||||
1.0,
|
||||
ImmutableArray<AdvisoryLinksetConflict>.Empty)
|
||||
{
|
||||
}
|
||||
|
||||
public AdvisoryObservationLinksetAggregate(
|
||||
ImmutableArray<string> aliases,
|
||||
ImmutableArray<string> purls,
|
||||
ImmutableArray<string> cpes,
|
||||
ImmutableArray<AdvisoryObservationReference> references,
|
||||
ImmutableArray<string> scopes,
|
||||
ImmutableArray<RawRelationship> relationships,
|
||||
double confidence,
|
||||
ImmutableArray<AdvisoryLinksetConflict> conflicts)
|
||||
{
|
||||
Aliases = aliases;
|
||||
Purls = purls;
|
||||
Cpes = cpes;
|
||||
References = references;
|
||||
Scopes = scopes.IsDefault ? ImmutableArray<string>.Empty : scopes;
|
||||
Relationships = relationships.IsDefault ? ImmutableArray<RawRelationship>.Empty : relationships;
|
||||
Confidence = confidence;
|
||||
Conflicts = conflicts.IsDefault ? ImmutableArray<AdvisoryLinksetConflict>.Empty : conflicts;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,14 +72,3 @@ public sealed record AdvisoryObservationQueryResult(
|
||||
AdvisoryObservationLinksetAggregate Linkset,
|
||||
string? NextCursor,
|
||||
bool HasMore);
|
||||
|
||||
/// <summary>
|
||||
/// Aggregated linkset built from the observations returned by a query.
|
||||
/// </summary>
|
||||
public sealed record AdvisoryObservationLinksetAggregate(
|
||||
ImmutableArray<string> Aliases,
|
||||
ImmutableArray<string> Purls,
|
||||
ImmutableArray<string> Cpes,
|
||||
ImmutableArray<AdvisoryObservationReference> References,
|
||||
ImmutableArray<string> Scopes,
|
||||
ImmutableArray<RawRelationship> Relationships);
|
||||
|
||||
@@ -190,12 +190,12 @@ public sealed class AdvisoryObservationQueryService : IAdvisoryObservationQueryS
|
||||
return null;
|
||||
}
|
||||
|
||||
var payload = $"{observation.CreatedAt.UtcTicks.ToString(CultureInfo.InvariantCulture)}:{observation.ObservationId}";
|
||||
return Convert.ToBase64String(Encoding.UTF8.GetBytes(payload));
|
||||
}
|
||||
|
||||
private static AdvisoryObservationLinksetAggregate BuildAggregateLinkset(ImmutableArray<AdvisoryObservation> observations)
|
||||
{
|
||||
var payload = $"{observation.CreatedAt.UtcTicks.ToString(CultureInfo.InvariantCulture)}:{observation.ObservationId}";
|
||||
return Convert.ToBase64String(Encoding.UTF8.GetBytes(payload));
|
||||
}
|
||||
|
||||
private static AdvisoryObservationLinksetAggregate BuildAggregateLinkset(ImmutableArray<AdvisoryObservation> observations)
|
||||
{
|
||||
if (observations.IsDefaultOrEmpty)
|
||||
{
|
||||
return new AdvisoryObservationLinksetAggregate(
|
||||
@@ -204,15 +204,20 @@ public sealed class AdvisoryObservationQueryService : IAdvisoryObservationQueryS
|
||||
ImmutableArray<string>.Empty,
|
||||
ImmutableArray<AdvisoryObservationReference>.Empty,
|
||||
ImmutableArray<string>.Empty,
|
||||
ImmutableArray<RawRelationship>.Empty);
|
||||
ImmutableArray<RawRelationship>.Empty,
|
||||
1.0,
|
||||
ImmutableArray<AdvisoryLinksetConflict>.Empty);
|
||||
}
|
||||
|
||||
var aliasSet = new HashSet<string>(StringComparer.Ordinal);
|
||||
var purlSet = new HashSet<string>(StringComparer.Ordinal);
|
||||
var cpeSet = new HashSet<string>(StringComparer.Ordinal);
|
||||
|
||||
var aliasSet = new HashSet<string>(StringComparer.Ordinal);
|
||||
var purlSet = new HashSet<string>(StringComparer.Ordinal);
|
||||
var cpeSet = new HashSet<string>(StringComparer.Ordinal);
|
||||
var referenceSet = new HashSet<AdvisoryObservationReference>();
|
||||
var scopeSet = new HashSet<string>(StringComparer.Ordinal);
|
||||
var relationshipSet = new HashSet<RawRelationship>();
|
||||
var conflictSet = new HashSet<string>(StringComparer.Ordinal);
|
||||
var conflicts = new List<AdvisoryLinksetConflict>();
|
||||
var confidence = 1.0;
|
||||
|
||||
foreach (var observation in observations)
|
||||
{
|
||||
@@ -245,6 +250,18 @@ public sealed class AdvisoryObservationQueryService : IAdvisoryObservationQueryS
|
||||
{
|
||||
relationshipSet.Add(relationship);
|
||||
}
|
||||
|
||||
var (_, rawConfidence, rawConflicts) = AdvisoryLinksetNormalization.FromRawLinksetWithConfidence(observation.RawLinkset);
|
||||
confidence = Math.Min(confidence, rawConfidence ?? 1.0);
|
||||
|
||||
foreach (var conflict in rawConflicts)
|
||||
{
|
||||
var key = $"{conflict.Field}|{conflict.Reason}|{string.Join('|', conflict.Values ?? Array.Empty<string>())}";
|
||||
if (conflictSet.Add(key))
|
||||
{
|
||||
conflicts.Add(conflict);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new AdvisoryObservationLinksetAggregate(
|
||||
@@ -260,6 +277,12 @@ public sealed class AdvisoryObservationQueryService : IAdvisoryObservationQueryS
|
||||
.OrderBy(static rel => rel.Type, StringComparer.Ordinal)
|
||||
.ThenBy(static rel => rel.Source, StringComparer.Ordinal)
|
||||
.ThenBy(static rel => rel.Target, StringComparer.Ordinal)
|
||||
.ToImmutableArray(),
|
||||
confidence,
|
||||
conflicts
|
||||
.OrderBy(static c => c.Field, StringComparer.Ordinal)
|
||||
.ThenBy(static c => c.Reason, StringComparer.Ordinal)
|
||||
.ThenBy(static c => string.Join('|', c.Values ?? Array.Empty<string>()), StringComparer.Ordinal)
|
||||
.ToImmutableArray());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,454 @@
|
||||
{
|
||||
"runtimeTarget": {
|
||||
"name": ".NETCoreApp,Version=v10.0",
|
||||
"signature": ""
|
||||
},
|
||||
"compilationOptions": {},
|
||||
"targets": {
|
||||
".NETCoreApp,Version=v10.0": {
|
||||
"StellaOps.Concelier.Core/1.0.0": {
|
||||
"dependencies": {
|
||||
"Cronos": "0.10.0",
|
||||
"Microsoft.Extensions.Hosting.Abstractions": "10.0.0-rc.2.25502.107",
|
||||
"Microsoft.Extensions.Logging.Abstractions": "10.0.0-rc.2.25502.107",
|
||||
"Microsoft.Extensions.Options": "10.0.0-rc.2.25502.107",
|
||||
"MongoDB.Driver": "3.5.0",
|
||||
"SharpCompress": "0.41.0",
|
||||
"StellaOps.Aoc": "1.0.0",
|
||||
"StellaOps.Concelier.Models": "1.0.0",
|
||||
"StellaOps.Concelier.Normalization": "1.0.0",
|
||||
"StellaOps.Concelier.RawModels": "1.0.0",
|
||||
"StellaOps.Ingestion.Telemetry": "1.0.0",
|
||||
"StellaOps.Plugin": "1.0.0",
|
||||
"StellaOps.Provenance.Mongo": "1.0.0"
|
||||
},
|
||||
"runtime": {
|
||||
"StellaOps.Concelier.Core.dll": {}
|
||||
}
|
||||
},
|
||||
"Cronos/0.10.0": {
|
||||
"runtime": {
|
||||
"lib/net6.0/Cronos.dll": {
|
||||
"assemblyVersion": "0.10.0.0",
|
||||
"fileVersion": "0.0.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"DnsClient/1.6.1": {
|
||||
"runtime": {
|
||||
"lib/net5.0/DnsClient.dll": {
|
||||
"assemblyVersion": "1.6.1.0",
|
||||
"fileVersion": "1.6.1.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Configuration.Abstractions/10.0.0-rc.2.25502.107": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Primitives": "10.0.0-rc.2.25502.107"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.Extensions.Configuration.Abstractions.dll": {
|
||||
"assemblyVersion": "10.0.0.0",
|
||||
"fileVersion": "10.0.25.50307"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions/10.0.0-rc.2.25502.107": {
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll": {
|
||||
"assemblyVersion": "10.0.0.0",
|
||||
"fileVersion": "10.0.25.50307"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Diagnostics.Abstractions/10.0.0-rc.2.25502.107": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.0-rc.2.25502.107",
|
||||
"Microsoft.Extensions.Options": "10.0.0-rc.2.25502.107"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.Extensions.Diagnostics.Abstractions.dll": {
|
||||
"assemblyVersion": "10.0.0.0",
|
||||
"fileVersion": "10.0.25.50307"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.FileProviders.Abstractions/10.0.0-rc.2.25502.107": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Primitives": "10.0.0-rc.2.25502.107"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.Extensions.FileProviders.Abstractions.dll": {
|
||||
"assemblyVersion": "10.0.0.0",
|
||||
"fileVersion": "10.0.25.50307"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Hosting.Abstractions/10.0.0-rc.2.25502.107": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Configuration.Abstractions": "10.0.0-rc.2.25502.107",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.0-rc.2.25502.107",
|
||||
"Microsoft.Extensions.Diagnostics.Abstractions": "10.0.0-rc.2.25502.107",
|
||||
"Microsoft.Extensions.FileProviders.Abstractions": "10.0.0-rc.2.25502.107",
|
||||
"Microsoft.Extensions.Logging.Abstractions": "10.0.0-rc.2.25502.107"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.Extensions.Hosting.Abstractions.dll": {
|
||||
"assemblyVersion": "10.0.0.0",
|
||||
"fileVersion": "10.0.25.50307"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Logging.Abstractions/10.0.0-rc.2.25502.107": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.0-rc.2.25502.107"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.Extensions.Logging.Abstractions.dll": {
|
||||
"assemblyVersion": "10.0.0.0",
|
||||
"fileVersion": "10.0.25.50307"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Options/10.0.0-rc.2.25502.107": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.0-rc.2.25502.107",
|
||||
"Microsoft.Extensions.Primitives": "10.0.0-rc.2.25502.107"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.Extensions.Options.dll": {
|
||||
"assemblyVersion": "10.0.0.0",
|
||||
"fileVersion": "10.0.25.50307"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Primitives/10.0.0-rc.2.25502.107": {
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.Extensions.Primitives.dll": {
|
||||
"assemblyVersion": "10.0.0.0",
|
||||
"fileVersion": "10.0.25.50307"
|
||||
}
|
||||
}
|
||||
},
|
||||
"MongoDB.Bson/3.5.0": {
|
||||
"runtime": {
|
||||
"lib/net6.0/MongoDB.Bson.dll": {
|
||||
"assemblyVersion": "3.5.0.0",
|
||||
"fileVersion": "3.5.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"MongoDB.Driver/3.5.0": {
|
||||
"dependencies": {
|
||||
"DnsClient": "1.6.1",
|
||||
"Microsoft.Extensions.Logging.Abstractions": "10.0.0-rc.2.25502.107",
|
||||
"MongoDB.Bson": "3.5.0",
|
||||
"SharpCompress": "0.41.0",
|
||||
"Snappier": "1.0.0",
|
||||
"ZstdSharp.Port": "0.8.6"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net6.0/MongoDB.Driver.dll": {
|
||||
"assemblyVersion": "3.5.0.0",
|
||||
"fileVersion": "3.5.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"NuGet.Versioning/6.9.1": {
|
||||
"runtime": {
|
||||
"lib/netstandard2.0/NuGet.Versioning.dll": {
|
||||
"assemblyVersion": "6.9.1.3",
|
||||
"fileVersion": "6.9.1.3"
|
||||
}
|
||||
}
|
||||
},
|
||||
"SharpCompress/0.41.0": {
|
||||
"dependencies": {
|
||||
"ZstdSharp.Port": "0.8.6"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net8.0/SharpCompress.dll": {
|
||||
"assemblyVersion": "0.41.0.0",
|
||||
"fileVersion": "0.41.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Snappier/1.0.0": {
|
||||
"runtime": {
|
||||
"lib/net5.0/Snappier.dll": {
|
||||
"assemblyVersion": "1.0.0.0",
|
||||
"fileVersion": "1.0.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ZstdSharp.Port/0.8.6": {
|
||||
"runtime": {
|
||||
"lib/net9.0/ZstdSharp.dll": {
|
||||
"assemblyVersion": "0.8.6.0",
|
||||
"fileVersion": "0.8.6.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"StellaOps.Aoc/1.0.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.0-rc.2.25502.107",
|
||||
"SharpCompress": "0.41.0"
|
||||
},
|
||||
"runtime": {
|
||||
"StellaOps.Aoc.dll": {
|
||||
"assemblyVersion": "1.0.0.0",
|
||||
"fileVersion": "1.0.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"StellaOps.Concelier.Models/1.0.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "10.0.0-rc.2.25502.107",
|
||||
"SharpCompress": "0.41.0",
|
||||
"StellaOps.Concelier.RawModels": "1.0.0"
|
||||
},
|
||||
"runtime": {
|
||||
"StellaOps.Concelier.Models.dll": {
|
||||
"assemblyVersion": "1.0.0.0",
|
||||
"fileVersion": "1.0.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"StellaOps.Concelier.Normalization/1.0.0": {
|
||||
"dependencies": {
|
||||
"NuGet.Versioning": "6.9.1",
|
||||
"SharpCompress": "0.41.0",
|
||||
"StellaOps.Concelier.Models": "1.0.0"
|
||||
},
|
||||
"runtime": {
|
||||
"StellaOps.Concelier.Normalization.dll": {
|
||||
"assemblyVersion": "1.0.0.0",
|
||||
"fileVersion": "1.0.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"StellaOps.Concelier.RawModels/1.0.0": {
|
||||
"dependencies": {
|
||||
"MongoDB.Bson": "3.5.0",
|
||||
"SharpCompress": "0.41.0"
|
||||
},
|
||||
"runtime": {
|
||||
"StellaOps.Concelier.RawModels.dll": {
|
||||
"assemblyVersion": "1.0.0.0",
|
||||
"fileVersion": "1.0.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"StellaOps.DependencyInjection/1.0.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Configuration.Abstractions": "10.0.0-rc.2.25502.107",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.0-rc.2.25502.107",
|
||||
"SharpCompress": "0.41.0"
|
||||
},
|
||||
"runtime": {
|
||||
"StellaOps.DependencyInjection.dll": {
|
||||
"assemblyVersion": "1.0.0.0",
|
||||
"fileVersion": "1.0.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"StellaOps.Ingestion.Telemetry/1.0.0": {
|
||||
"dependencies": {
|
||||
"SharpCompress": "0.41.0"
|
||||
},
|
||||
"runtime": {
|
||||
"StellaOps.Ingestion.Telemetry.dll": {
|
||||
"assemblyVersion": "1.0.0.0",
|
||||
"fileVersion": "1.0.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"StellaOps.Plugin/1.0.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Configuration.Abstractions": "10.0.0-rc.2.25502.107",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.0-rc.2.25502.107",
|
||||
"Microsoft.Extensions.Logging.Abstractions": "10.0.0-rc.2.25502.107",
|
||||
"SharpCompress": "0.41.0",
|
||||
"StellaOps.DependencyInjection": "1.0.0"
|
||||
},
|
||||
"runtime": {
|
||||
"StellaOps.Plugin.dll": {
|
||||
"assemblyVersion": "1.0.0.0",
|
||||
"fileVersion": "1.0.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"StellaOps.Provenance.Mongo/1.0.0": {
|
||||
"dependencies": {
|
||||
"MongoDB.Driver": "3.5.0",
|
||||
"SharpCompress": "0.41.0"
|
||||
},
|
||||
"runtime": {
|
||||
"StellaOps.Provenance.Mongo.dll": {
|
||||
"assemblyVersion": "1.0.0.0",
|
||||
"fileVersion": "1.0.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"libraries": {
|
||||
"StellaOps.Concelier.Core/1.0.0": {
|
||||
"type": "project",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
},
|
||||
"Cronos/0.10.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-wHL4tr8mWTvrJt/4sI3raympCNVT4F3VJI4SJHA9A/wB+8Lsq84RFGQH9bHEtvNsN1lCBTKNk+uVoDotGcYJZA==",
|
||||
"path": "cronos/0.10.0",
|
||||
"hashPath": "cronos.0.10.0.nupkg.sha512"
|
||||
},
|
||||
"DnsClient/1.6.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-4H/f2uYJOZ+YObZjpY9ABrKZI+JNw3uizp6oMzTXwDw6F+2qIPhpRl/1t68O/6e98+vqNiYGu+lswmwdYUy3gg==",
|
||||
"path": "dnsclient/1.6.1",
|
||||
"hashPath": "dnsclient.1.6.1.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Extensions.Configuration.Abstractions/10.0.0-rc.2.25502.107": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-H+i/Qy30Rg/K9BcW2Z6DCHPCzwMH3bCwNOjEz31shWTUDK8GeeeMnrKVusprTcRA2Y6yPST+hg2zc3whPEs14Q==",
|
||||
"path": "microsoft.extensions.configuration.abstractions/10.0.0-rc.2.25502.107",
|
||||
"hashPath": "microsoft.extensions.configuration.abstractions.10.0.0-rc.2.25502.107.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions/10.0.0-rc.2.25502.107": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-8jujunpkNNfTkE9PFHp9/aD6GPKVfNCuz8tUbzOcyU5tQOCoIZId4hwQNVx3Tb8XEWw9BYdh0k5vPpqdCM+UtA==",
|
||||
"path": "microsoft.extensions.dependencyinjection.abstractions/10.0.0-rc.2.25502.107",
|
||||
"hashPath": "microsoft.extensions.dependencyinjection.abstractions.10.0.0-rc.2.25502.107.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Extensions.Diagnostics.Abstractions/10.0.0-rc.2.25502.107": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-x6XVv3RiwOlN2unjyX/Zat0gI0HiRoDDdjkwBCwsMftYWpbJu4SiyRwDbrv2zAF8v8nbEEvcWi3/pUxZfaqLQw==",
|
||||
"path": "microsoft.extensions.diagnostics.abstractions/10.0.0-rc.2.25502.107",
|
||||
"hashPath": "microsoft.extensions.diagnostics.abstractions.10.0.0-rc.2.25502.107.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Extensions.FileProviders.Abstractions/10.0.0-rc.2.25502.107": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-dOpmW14MkOZIwV6269iXhoMp6alCHBoxqCR4pJ37GLjFaBIyzsIy+Ra8tsGmjHtFvEHKq0JRDIsb1PUkrK+yxw==",
|
||||
"path": "microsoft.extensions.fileproviders.abstractions/10.0.0-rc.2.25502.107",
|
||||
"hashPath": "microsoft.extensions.fileproviders.abstractions.10.0.0-rc.2.25502.107.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Extensions.Hosting.Abstractions/10.0.0-rc.2.25502.107": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-M6zqZFbqjdCx8g5Y2XZKTfYfS0gAh4uJkmdAq/ZRDrpIr3Nd+u74allmw15jX1kM61IXM49EnTbhMzlWw5pGVQ==",
|
||||
"path": "microsoft.extensions.hosting.abstractions/10.0.0-rc.2.25502.107",
|
||||
"hashPath": "microsoft.extensions.hosting.abstractions.10.0.0-rc.2.25502.107.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Extensions.Logging.Abstractions/10.0.0-rc.2.25502.107": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-SKKKZjyCpBaDQ7yuFjdk6ELnRBRWeZsbnzUfo59Wc4PGhgf92chE3we/QlT6nk6NqlWcUgH/jogM+B/uq/Qdnw==",
|
||||
"path": "microsoft.extensions.logging.abstractions/10.0.0-rc.2.25502.107",
|
||||
"hashPath": "microsoft.extensions.logging.abstractions.10.0.0-rc.2.25502.107.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Extensions.Options/10.0.0-rc.2.25502.107": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-Ib6BCCjisp7ZUdhtNpSulFO0ODhz/IE4ZZd8OCqQWoRs363BQ0QOZi9KwpqpiEWo51S0kIXWqNicDPGXwpt9pQ==",
|
||||
"path": "microsoft.extensions.options/10.0.0-rc.2.25502.107",
|
||||
"hashPath": "microsoft.extensions.options.10.0.0-rc.2.25502.107.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Extensions.Primitives/10.0.0-rc.2.25502.107": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-9pm2zqqn5u/OsKs2zgkhJEQQeMx9KkVOWPdHrs7Kt5sfpk+eIh/gmpi/mMH/ljS2T/PFsFdCEtm+GS/6l7zoZA==",
|
||||
"path": "microsoft.extensions.primitives/10.0.0-rc.2.25502.107",
|
||||
"hashPath": "microsoft.extensions.primitives.10.0.0-rc.2.25502.107.nupkg.sha512"
|
||||
},
|
||||
"MongoDB.Bson/3.5.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-JGNK6BanLDEifgkvPLqVFCPus5EDCy416pxf1dxUBRSVd3D9+NB3AvMVX190eXlk5/UXuCxpsQv7jWfNKvppBQ==",
|
||||
"path": "mongodb.bson/3.5.0",
|
||||
"hashPath": "mongodb.bson.3.5.0.nupkg.sha512"
|
||||
},
|
||||
"MongoDB.Driver/3.5.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-ST90u7psyMkNNOWFgSkexsrB3kPn7Ynl2DlMFj2rJyYuc6SIxjmzu4ufy51yzM+cPVE1SvVcdb5UFobrRw6cMg==",
|
||||
"path": "mongodb.driver/3.5.0",
|
||||
"hashPath": "mongodb.driver.3.5.0.nupkg.sha512"
|
||||
},
|
||||
"NuGet.Versioning/6.9.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-ypnSvEtpNGo48bAWn95J1oHChycCXcevFSbn53fqzLxlXFSZP7dawu8p/7mHAfGufZQSV2sBpW80XQGIfXO8kQ==",
|
||||
"path": "nuget.versioning/6.9.1",
|
||||
"hashPath": "nuget.versioning.6.9.1.nupkg.sha512"
|
||||
},
|
||||
"SharpCompress/0.41.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-z04dBVdTIAFTRKi38f0LkajaKA++bR+M8kYCbasXePILD2H+qs7CkLpyiippB24CSbTrWIgpBKm6BenZqkUwvw==",
|
||||
"path": "sharpcompress/0.41.0",
|
||||
"hashPath": "sharpcompress.0.41.0.nupkg.sha512"
|
||||
},
|
||||
"Snappier/1.0.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-rFtK2KEI9hIe8gtx3a0YDXdHOpedIf9wYCEYtBEmtlyiWVX3XlCNV03JrmmAi/Cdfn7dxK+k0sjjcLv4fpHnqA==",
|
||||
"path": "snappier/1.0.0",
|
||||
"hashPath": "snappier.1.0.0.nupkg.sha512"
|
||||
},
|
||||
"ZstdSharp.Port/0.8.6": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-iP4jVLQoQmUjMU88g1WObiNr6YKZGvh4aOXn3yOJsHqZsflwRsxZPcIBvNXgjXO3vQKSLctXGLTpcBPLnWPS8A==",
|
||||
"path": "zstdsharp.port/0.8.6",
|
||||
"hashPath": "zstdsharp.port.0.8.6.nupkg.sha512"
|
||||
},
|
||||
"StellaOps.Aoc/1.0.0": {
|
||||
"type": "project",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
},
|
||||
"StellaOps.Concelier.Models/1.0.0": {
|
||||
"type": "project",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
},
|
||||
"StellaOps.Concelier.Normalization/1.0.0": {
|
||||
"type": "project",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
},
|
||||
"StellaOps.Concelier.RawModels/1.0.0": {
|
||||
"type": "project",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
},
|
||||
"StellaOps.DependencyInjection/1.0.0": {
|
||||
"type": "project",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
},
|
||||
"StellaOps.Ingestion.Telemetry/1.0.0": {
|
||||
"type": "project",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
},
|
||||
"StellaOps.Plugin/1.0.0": {
|
||||
"type": "project",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
},
|
||||
"StellaOps.Provenance.Mongo/1.0.0": {
|
||||
"type": "project",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
{
|
||||
"runtimeTarget": {
|
||||
"name": ".NETCoreApp,Version=v10.0",
|
||||
"signature": ""
|
||||
},
|
||||
"compilationOptions": {},
|
||||
"targets": {
|
||||
".NETCoreApp,Version=v10.0": {
|
||||
"StellaOps.Concelier.Models/1.0.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "10.0.0-rc.2.25502.107",
|
||||
"SharpCompress": "0.41.0",
|
||||
"StellaOps.Concelier.RawModels": "1.0.0"
|
||||
},
|
||||
"runtime": {
|
||||
"StellaOps.Concelier.Models.dll": {}
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions/10.0.0-rc.2.25502.107": {
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll": {
|
||||
"assemblyVersion": "10.0.0.0",
|
||||
"fileVersion": "10.0.25.50307"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Logging.Abstractions/10.0.0-rc.2.25502.107": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.0-rc.2.25502.107"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.Extensions.Logging.Abstractions.dll": {
|
||||
"assemblyVersion": "10.0.0.0",
|
||||
"fileVersion": "10.0.25.50307"
|
||||
}
|
||||
}
|
||||
},
|
||||
"MongoDB.Bson/3.5.0": {
|
||||
"runtime": {
|
||||
"lib/net6.0/MongoDB.Bson.dll": {
|
||||
"assemblyVersion": "3.5.0.0",
|
||||
"fileVersion": "3.5.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"SharpCompress/0.41.0": {
|
||||
"dependencies": {
|
||||
"ZstdSharp.Port": "0.8.6"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net8.0/SharpCompress.dll": {
|
||||
"assemblyVersion": "0.41.0.0",
|
||||
"fileVersion": "0.41.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ZstdSharp.Port/0.8.6": {
|
||||
"runtime": {
|
||||
"lib/net9.0/ZstdSharp.dll": {
|
||||
"assemblyVersion": "0.8.6.0",
|
||||
"fileVersion": "0.8.6.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"StellaOps.Concelier.RawModels/1.0.0": {
|
||||
"dependencies": {
|
||||
"MongoDB.Bson": "3.5.0",
|
||||
"SharpCompress": "0.41.0"
|
||||
},
|
||||
"runtime": {
|
||||
"StellaOps.Concelier.RawModels.dll": {
|
||||
"assemblyVersion": "1.0.0.0",
|
||||
"fileVersion": "1.0.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"libraries": {
|
||||
"StellaOps.Concelier.Models/1.0.0": {
|
||||
"type": "project",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions/10.0.0-rc.2.25502.107": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-8jujunpkNNfTkE9PFHp9/aD6GPKVfNCuz8tUbzOcyU5tQOCoIZId4hwQNVx3Tb8XEWw9BYdh0k5vPpqdCM+UtA==",
|
||||
"path": "microsoft.extensions.dependencyinjection.abstractions/10.0.0-rc.2.25502.107",
|
||||
"hashPath": "microsoft.extensions.dependencyinjection.abstractions.10.0.0-rc.2.25502.107.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Extensions.Logging.Abstractions/10.0.0-rc.2.25502.107": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-SKKKZjyCpBaDQ7yuFjdk6ELnRBRWeZsbnzUfo59Wc4PGhgf92chE3we/QlT6nk6NqlWcUgH/jogM+B/uq/Qdnw==",
|
||||
"path": "microsoft.extensions.logging.abstractions/10.0.0-rc.2.25502.107",
|
||||
"hashPath": "microsoft.extensions.logging.abstractions.10.0.0-rc.2.25502.107.nupkg.sha512"
|
||||
},
|
||||
"MongoDB.Bson/3.5.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-JGNK6BanLDEifgkvPLqVFCPus5EDCy416pxf1dxUBRSVd3D9+NB3AvMVX190eXlk5/UXuCxpsQv7jWfNKvppBQ==",
|
||||
"path": "mongodb.bson/3.5.0",
|
||||
"hashPath": "mongodb.bson.3.5.0.nupkg.sha512"
|
||||
},
|
||||
"SharpCompress/0.41.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-z04dBVdTIAFTRKi38f0LkajaKA++bR+M8kYCbasXePILD2H+qs7CkLpyiippB24CSbTrWIgpBKm6BenZqkUwvw==",
|
||||
"path": "sharpcompress/0.41.0",
|
||||
"hashPath": "sharpcompress.0.41.0.nupkg.sha512"
|
||||
},
|
||||
"ZstdSharp.Port/0.8.6": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-iP4jVLQoQmUjMU88g1WObiNr6YKZGvh4aOXn3yOJsHqZsflwRsxZPcIBvNXgjXO3vQKSLctXGLTpcBPLnWPS8A==",
|
||||
"path": "zstdsharp.port/0.8.6",
|
||||
"hashPath": "zstdsharp.port.0.8.6.nupkg.sha512"
|
||||
},
|
||||
"StellaOps.Concelier.RawModels/1.0.0": {
|
||||
"type": "project",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
{
|
||||
"runtimeTarget": {
|
||||
"name": ".NETCoreApp,Version=v10.0",
|
||||
"signature": ""
|
||||
},
|
||||
"compilationOptions": {},
|
||||
"targets": {
|
||||
".NETCoreApp,Version=v10.0": {
|
||||
"StellaOps.Concelier.Normalization/1.0.0": {
|
||||
"dependencies": {
|
||||
"NuGet.Versioning": "6.9.1",
|
||||
"SharpCompress": "0.41.0",
|
||||
"StellaOps.Concelier.Models": "1.0.0"
|
||||
},
|
||||
"runtime": {
|
||||
"StellaOps.Concelier.Normalization.dll": {}
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions/10.0.0-rc.2.25502.107": {
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll": {
|
||||
"assemblyVersion": "10.0.0.0",
|
||||
"fileVersion": "10.0.25.50307"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Logging.Abstractions/10.0.0-rc.2.25502.107": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.0-rc.2.25502.107"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.Extensions.Logging.Abstractions.dll": {
|
||||
"assemblyVersion": "10.0.0.0",
|
||||
"fileVersion": "10.0.25.50307"
|
||||
}
|
||||
}
|
||||
},
|
||||
"MongoDB.Bson/3.5.0": {
|
||||
"runtime": {
|
||||
"lib/net6.0/MongoDB.Bson.dll": {
|
||||
"assemblyVersion": "3.5.0.0",
|
||||
"fileVersion": "3.5.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"NuGet.Versioning/6.9.1": {
|
||||
"runtime": {
|
||||
"lib/netstandard2.0/NuGet.Versioning.dll": {
|
||||
"assemblyVersion": "6.9.1.3",
|
||||
"fileVersion": "6.9.1.3"
|
||||
}
|
||||
}
|
||||
},
|
||||
"SharpCompress/0.41.0": {
|
||||
"dependencies": {
|
||||
"ZstdSharp.Port": "0.8.6"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net8.0/SharpCompress.dll": {
|
||||
"assemblyVersion": "0.41.0.0",
|
||||
"fileVersion": "0.41.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ZstdSharp.Port/0.8.6": {
|
||||
"runtime": {
|
||||
"lib/net9.0/ZstdSharp.dll": {
|
||||
"assemblyVersion": "0.8.6.0",
|
||||
"fileVersion": "0.8.6.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"StellaOps.Concelier.Models/1.0.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "10.0.0-rc.2.25502.107",
|
||||
"SharpCompress": "0.41.0",
|
||||
"StellaOps.Concelier.RawModels": "1.0.0"
|
||||
},
|
||||
"runtime": {
|
||||
"StellaOps.Concelier.Models.dll": {
|
||||
"assemblyVersion": "1.0.0.0",
|
||||
"fileVersion": "1.0.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"StellaOps.Concelier.RawModels/1.0.0": {
|
||||
"dependencies": {
|
||||
"MongoDB.Bson": "3.5.0",
|
||||
"SharpCompress": "0.41.0"
|
||||
},
|
||||
"runtime": {
|
||||
"StellaOps.Concelier.RawModels.dll": {
|
||||
"assemblyVersion": "1.0.0.0",
|
||||
"fileVersion": "1.0.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"libraries": {
|
||||
"StellaOps.Concelier.Normalization/1.0.0": {
|
||||
"type": "project",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions/10.0.0-rc.2.25502.107": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-8jujunpkNNfTkE9PFHp9/aD6GPKVfNCuz8tUbzOcyU5tQOCoIZId4hwQNVx3Tb8XEWw9BYdh0k5vPpqdCM+UtA==",
|
||||
"path": "microsoft.extensions.dependencyinjection.abstractions/10.0.0-rc.2.25502.107",
|
||||
"hashPath": "microsoft.extensions.dependencyinjection.abstractions.10.0.0-rc.2.25502.107.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Extensions.Logging.Abstractions/10.0.0-rc.2.25502.107": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-SKKKZjyCpBaDQ7yuFjdk6ELnRBRWeZsbnzUfo59Wc4PGhgf92chE3we/QlT6nk6NqlWcUgH/jogM+B/uq/Qdnw==",
|
||||
"path": "microsoft.extensions.logging.abstractions/10.0.0-rc.2.25502.107",
|
||||
"hashPath": "microsoft.extensions.logging.abstractions.10.0.0-rc.2.25502.107.nupkg.sha512"
|
||||
},
|
||||
"MongoDB.Bson/3.5.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-JGNK6BanLDEifgkvPLqVFCPus5EDCy416pxf1dxUBRSVd3D9+NB3AvMVX190eXlk5/UXuCxpsQv7jWfNKvppBQ==",
|
||||
"path": "mongodb.bson/3.5.0",
|
||||
"hashPath": "mongodb.bson.3.5.0.nupkg.sha512"
|
||||
},
|
||||
"NuGet.Versioning/6.9.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-ypnSvEtpNGo48bAWn95J1oHChycCXcevFSbn53fqzLxlXFSZP7dawu8p/7mHAfGufZQSV2sBpW80XQGIfXO8kQ==",
|
||||
"path": "nuget.versioning/6.9.1",
|
||||
"hashPath": "nuget.versioning.6.9.1.nupkg.sha512"
|
||||
},
|
||||
"SharpCompress/0.41.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-z04dBVdTIAFTRKi38f0LkajaKA++bR+M8kYCbasXePILD2H+qs7CkLpyiippB24CSbTrWIgpBKm6BenZqkUwvw==",
|
||||
"path": "sharpcompress/0.41.0",
|
||||
"hashPath": "sharpcompress.0.41.0.nupkg.sha512"
|
||||
},
|
||||
"ZstdSharp.Port/0.8.6": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-iP4jVLQoQmUjMU88g1WObiNr6YKZGvh4aOXn3yOJsHqZsflwRsxZPcIBvNXgjXO3vQKSLctXGLTpcBPLnWPS8A==",
|
||||
"path": "zstdsharp.port/0.8.6",
|
||||
"hashPath": "zstdsharp.port.0.8.6.nupkg.sha512"
|
||||
},
|
||||
"StellaOps.Concelier.Models/1.0.0": {
|
||||
"type": "project",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
},
|
||||
"StellaOps.Concelier.RawModels/1.0.0": {
|
||||
"type": "project",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
{
|
||||
"runtimeTarget": {
|
||||
"name": ".NETCoreApp,Version=v10.0",
|
||||
"signature": ""
|
||||
},
|
||||
"compilationOptions": {},
|
||||
"targets": {
|
||||
".NETCoreApp,Version=v10.0": {
|
||||
"StellaOps.Concelier.RawModels/1.0.0": {
|
||||
"dependencies": {
|
||||
"MongoDB.Bson": "3.5.0",
|
||||
"SharpCompress": "0.41.0"
|
||||
},
|
||||
"runtime": {
|
||||
"StellaOps.Concelier.RawModels.dll": {}
|
||||
}
|
||||
},
|
||||
"MongoDB.Bson/3.5.0": {
|
||||
"runtime": {
|
||||
"lib/net6.0/MongoDB.Bson.dll": {
|
||||
"assemblyVersion": "3.5.0.0",
|
||||
"fileVersion": "3.5.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"SharpCompress/0.41.0": {
|
||||
"dependencies": {
|
||||
"ZstdSharp.Port": "0.8.6"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net8.0/SharpCompress.dll": {
|
||||
"assemblyVersion": "0.41.0.0",
|
||||
"fileVersion": "0.41.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ZstdSharp.Port/0.8.6": {
|
||||
"runtime": {
|
||||
"lib/net9.0/ZstdSharp.dll": {
|
||||
"assemblyVersion": "0.8.6.0",
|
||||
"fileVersion": "0.8.6.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"libraries": {
|
||||
"StellaOps.Concelier.RawModels/1.0.0": {
|
||||
"type": "project",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
},
|
||||
"MongoDB.Bson/3.5.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-JGNK6BanLDEifgkvPLqVFCPus5EDCy416pxf1dxUBRSVd3D9+NB3AvMVX190eXlk5/UXuCxpsQv7jWfNKvppBQ==",
|
||||
"path": "mongodb.bson/3.5.0",
|
||||
"hashPath": "mongodb.bson.3.5.0.nupkg.sha512"
|
||||
},
|
||||
"SharpCompress/0.41.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-z04dBVdTIAFTRKi38f0LkajaKA++bR+M8kYCbasXePILD2H+qs7CkLpyiippB24CSbTrWIgpBKm6BenZqkUwvw==",
|
||||
"path": "sharpcompress/0.41.0",
|
||||
"hashPath": "sharpcompress.0.41.0.nupkg.sha512"
|
||||
},
|
||||
"ZstdSharp.Port/0.8.6": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-iP4jVLQoQmUjMU88g1WObiNr6YKZGvh4aOXn3yOJsHqZsflwRsxZPcIBvNXgjXO3vQKSLctXGLTpcBPLnWPS8A==",
|
||||
"path": "zstdsharp.port/0.8.6",
|
||||
"hashPath": "zstdsharp.port.0.8.6.nupkg.sha512"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,23 @@
|
||||
# Mongo Schema Migration Playbook
|
||||
|
||||
This module owns the persistent shape of Concelier's MongoDB database. Upgrades must be deterministic and safe to run on live replicas. The `MongoMigrationRunner` executes idempotent migrations on startup immediately after the bootstrapper completes its collection and index checks.
|
||||
|
||||
## Execution Path
|
||||
|
||||
1. `StellaOps.Concelier.WebService` calls `MongoBootstrapper.InitializeAsync()` during startup.
|
||||
2. Once collections and baseline indexes are ensured, the bootstrapper invokes `MongoMigrationRunner.RunAsync()`.
|
||||
3. Each `IMongoMigration` implementation is sorted by its `Id` (ordinal compare) and executed exactly once. Completion is recorded in the `schema_migrations` collection.
|
||||
4. Failures surface during startup and prevent the service from serving traffic, matching our "fail-fast" requirement for storage incompatibilities.
|
||||
|
||||
## Creating a Migration
|
||||
|
||||
1. Implement `IMongoMigration` under `StellaOps.Concelier.Storage.Mongo.Migrations`. Use a monotonically increasing identifier such as `yyyyMMdd_description`.
|
||||
2. Keep the body idempotent: query state first, drop/re-create indexes only when mismatch is detected, and avoid multi-document transactions unless required.
|
||||
3. Add the migration to DI in `ServiceCollectionExtensions` so it flows into the runner.
|
||||
4. Write an integration test that exercises the migration against a Mongo2Go instance to validate behaviour.
|
||||
|
||||
## Current Migrations
|
||||
|
||||
# Mongo Schema Migration Playbook
|
||||
|
||||
This module owns the persistent shape of Concelier's MongoDB database. Upgrades must be deterministic and safe to run on live replicas. The `MongoMigrationRunner` executes idempotent migrations on startup immediately after the bootstrapper completes its collection and index checks.
|
||||
|
||||
## Execution Path
|
||||
|
||||
1. `StellaOps.Concelier.WebService` calls `MongoBootstrapper.InitializeAsync()` during startup.
|
||||
2. Once collections and baseline indexes are ensured, the bootstrapper invokes `MongoMigrationRunner.RunAsync()`.
|
||||
3. Each `IMongoMigration` implementation is sorted by its `Id` (ordinal compare) and executed exactly once. Completion is recorded in the `schema_migrations` collection.
|
||||
4. Failures surface during startup and prevent the service from serving traffic, matching our "fail-fast" requirement for storage incompatibilities.
|
||||
|
||||
## Creating a Migration
|
||||
|
||||
1. Implement `IMongoMigration` under `StellaOps.Concelier.Storage.Mongo.Migrations`. Use a monotonically increasing identifier such as `yyyyMMdd_description`.
|
||||
2. Keep the body idempotent: query state first, drop/re-create indexes only when mismatch is detected, and avoid multi-document transactions unless required.
|
||||
3. Add the migration to DI in `ServiceCollectionExtensions` so it flows into the runner.
|
||||
4. Write an integration test that exercises the migration against a Mongo2Go instance to validate behaviour.
|
||||
|
||||
## Current Migrations
|
||||
|
||||
| Id | Description |
|
||||
| --- | --- |
|
||||
| `20241005_document_expiry_indexes` | Ensures `document` collection uses the correct TTL/partial index depending on raw document retention settings. |
|
||||
@@ -27,6 +27,7 @@ This module owns the persistent shape of Concelier's MongoDB database. Upgrades
|
||||
| `20251028_advisory_supersedes_backfill` | Renames legacy `advisory` collection to a read-only backup view and backfills `supersedes` chains across `advisory_raw`. |
|
||||
| `20251028_advisory_raw_validator` | Applies Aggregation-Only Contract JSON schema validator to the `advisory_raw` collection with configurable enforcement level. |
|
||||
| `20251104_advisory_observations_raw_linkset` | Backfills `rawLinkset` on `advisory_observations` using stored `advisory_raw` documents so canonical and raw projections co-exist for downstream policy joins. |
|
||||
| `20251117_advisory_linksets_tenant_lower` | Lowercases `advisory_linksets.tenantId` to align writes with lookup filters. |
|
||||
|
||||
## Operator Runbook
|
||||
|
||||
@@ -40,10 +41,10 @@ This module owns the persistent shape of Concelier's MongoDB database. Upgrades
|
||||
- To re-run a migration in a lab, delete the corresponding document from `schema_migrations` and restart the service. **Do not** do this in production unless the migration body is known to be idempotent and safe.
|
||||
- When changing retention settings (`RawDocumentRetention`), deploy the new configuration and restart Concelier. The migration runner will adjust indexes on the next boot.
|
||||
- For the event-log collections (`advisory_statements`, `advisory_conflicts`), rollback is simply `db.advisory_statements.drop()` / `db.advisory_conflicts.drop()` followed by a restart if you must revert to the pre-event-log schema (only in labs). Production rollbacks should instead gate merge features that rely on these collections.
|
||||
- If migrations fail, restart with `Logging__LogLevel__StellaOps.Concelier.Storage.Mongo.Migrations=Debug` to surface diagnostic output. Remediate underlying index/collection drift before retrying.
|
||||
|
||||
## Validating an Upgrade
|
||||
|
||||
1. Run `dotnet test --filter MongoMigrationRunnerTests` to exercise integration coverage.
|
||||
2. In staging, execute `db.schema_migrations.find().sort({_id:1})` to verify applied migrations and timestamps.
|
||||
3. Inspect index shapes: `db.document.getIndexes()` and `db.documents.files.getIndexes()` for TTL/partial filter alignment.
|
||||
- If migrations fail, restart with `Logging__LogLevel__StellaOps.Concelier.Storage.Mongo.Migrations=Debug` to surface diagnostic output. Remediate underlying index/collection drift before retrying.
|
||||
|
||||
## Validating an Upgrade
|
||||
|
||||
1. Run `dotnet test --filter MongoMigrationRunnerTests` to exercise integration coverage.
|
||||
2. In staging, execute `db.schema_migrations.find().sort({_id:1})` to verify applied migrations and timestamps.
|
||||
3. Inspect index shapes: `db.document.getIndexes()` and `db.documents.files.getIndexes()` for TTL/partial filter alignment.
|
||||
|
||||
@@ -25,19 +25,22 @@ public sealed class EnsureAdvisoryLinksetsTenantLowerMigration : IMongoMigration
|
||||
|
||||
var collection = database.GetCollection<BsonDocument>(MongoStorageDefaults.Collections.AdvisoryLinksets);
|
||||
|
||||
var filter = Builders<BsonDocument>.Filter.Where(doc =>
|
||||
doc.Contains("TenantId") &&
|
||||
doc["TenantId"].BsonType == BsonType.String &&
|
||||
doc["TenantId"].AsString != doc["TenantId"].AsString.ToLowerInvariant());
|
||||
|
||||
using var cursor = await collection.Find(filter).ToCursorAsync(cancellationToken).ConfigureAwait(false);
|
||||
using var cursor = await collection
|
||||
.Find(Builders<BsonDocument>.Filter.Empty)
|
||||
.ToCursorAsync(cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
var writes = new List<WriteModel<BsonDocument>>(BatchSize);
|
||||
while (await cursor.MoveNextAsync(cancellationToken).ConfigureAwait(false))
|
||||
{
|
||||
foreach (var doc in cursor.Current)
|
||||
{
|
||||
var currentTenant = doc["TenantId"].AsString;
|
||||
if (!doc.TryGetValue("TenantId", out var tenantValue) || tenantValue.BsonType != BsonType.String)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var currentTenant = tenantValue.AsString;
|
||||
var lower = currentTenant.ToLowerInvariant();
|
||||
if (lower == currentTenant)
|
||||
{
|
||||
|
||||
@@ -118,6 +118,7 @@ public static class ServiceCollectionExtensions
|
||||
services.AddSingleton<IMongoMigration, EnsureAdvisoryCanonicalKeyBackfillMigration>();
|
||||
services.AddSingleton<IMongoMigration, EnsureAdvisoryRawValidatorMigration>();
|
||||
services.AddSingleton<IMongoMigration, EnsureAdvisoryObservationsRawLinksetMigration>();
|
||||
services.AddSingleton<IMongoMigration, EnsureAdvisoryLinksetsTenantLowerMigration>();
|
||||
services.AddSingleton<IMongoMigration, EnsureAdvisoryEventCollectionsMigration>();
|
||||
services.AddSingleton<IMongoMigration, SemVerStyleBackfillMigration>();
|
||||
|
||||
|
||||
@@ -0,0 +1,477 @@
|
||||
{
|
||||
"runtimeTarget": {
|
||||
"name": ".NETCoreApp,Version=v10.0",
|
||||
"signature": ""
|
||||
},
|
||||
"compilationOptions": {},
|
||||
"targets": {
|
||||
".NETCoreApp,Version=v10.0": {
|
||||
"StellaOps.Concelier.Storage.Mongo/1.0.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "10.0.0-rc.2.25502.107",
|
||||
"Microsoft.Extensions.Options": "10.0.0-rc.2.25502.107",
|
||||
"MongoDB.Driver": "3.5.0",
|
||||
"SharpCompress": "0.41.0",
|
||||
"StellaOps.Concelier.Core": "1.0.0",
|
||||
"StellaOps.Concelier.Models": "1.0.0",
|
||||
"StellaOps.Ingestion.Telemetry": "1.0.0",
|
||||
"StellaOps.Provenance.Mongo": "1.0.0"
|
||||
},
|
||||
"runtime": {
|
||||
"StellaOps.Concelier.Storage.Mongo.dll": {}
|
||||
}
|
||||
},
|
||||
"Cronos/0.10.0": {
|
||||
"runtime": {
|
||||
"lib/net6.0/Cronos.dll": {
|
||||
"assemblyVersion": "0.10.0.0",
|
||||
"fileVersion": "0.0.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"DnsClient/1.6.1": {
|
||||
"runtime": {
|
||||
"lib/net5.0/DnsClient.dll": {
|
||||
"assemblyVersion": "1.6.1.0",
|
||||
"fileVersion": "1.6.1.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Configuration.Abstractions/10.0.0-rc.2.25502.107": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Primitives": "10.0.0-rc.2.25502.107"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.Extensions.Configuration.Abstractions.dll": {
|
||||
"assemblyVersion": "10.0.0.0",
|
||||
"fileVersion": "10.0.25.50307"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions/10.0.0-rc.2.25502.107": {
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll": {
|
||||
"assemblyVersion": "10.0.0.0",
|
||||
"fileVersion": "10.0.25.50307"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Diagnostics.Abstractions/10.0.0-rc.2.25502.107": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.0-rc.2.25502.107",
|
||||
"Microsoft.Extensions.Options": "10.0.0-rc.2.25502.107"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.Extensions.Diagnostics.Abstractions.dll": {
|
||||
"assemblyVersion": "10.0.0.0",
|
||||
"fileVersion": "10.0.25.50307"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.FileProviders.Abstractions/10.0.0-rc.2.25502.107": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Primitives": "10.0.0-rc.2.25502.107"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.Extensions.FileProviders.Abstractions.dll": {
|
||||
"assemblyVersion": "10.0.0.0",
|
||||
"fileVersion": "10.0.25.50307"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Hosting.Abstractions/10.0.0-rc.2.25502.107": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Configuration.Abstractions": "10.0.0-rc.2.25502.107",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.0-rc.2.25502.107",
|
||||
"Microsoft.Extensions.Diagnostics.Abstractions": "10.0.0-rc.2.25502.107",
|
||||
"Microsoft.Extensions.FileProviders.Abstractions": "10.0.0-rc.2.25502.107",
|
||||
"Microsoft.Extensions.Logging.Abstractions": "10.0.0-rc.2.25502.107"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.Extensions.Hosting.Abstractions.dll": {
|
||||
"assemblyVersion": "10.0.0.0",
|
||||
"fileVersion": "10.0.25.50307"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Logging.Abstractions/10.0.0-rc.2.25502.107": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.0-rc.2.25502.107"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.Extensions.Logging.Abstractions.dll": {
|
||||
"assemblyVersion": "10.0.0.0",
|
||||
"fileVersion": "10.0.25.50307"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Options/10.0.0-rc.2.25502.107": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.0-rc.2.25502.107",
|
||||
"Microsoft.Extensions.Primitives": "10.0.0-rc.2.25502.107"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.Extensions.Options.dll": {
|
||||
"assemblyVersion": "10.0.0.0",
|
||||
"fileVersion": "10.0.25.50307"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Primitives/10.0.0-rc.2.25502.107": {
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.Extensions.Primitives.dll": {
|
||||
"assemblyVersion": "10.0.0.0",
|
||||
"fileVersion": "10.0.25.50307"
|
||||
}
|
||||
}
|
||||
},
|
||||
"MongoDB.Bson/3.5.0": {
|
||||
"runtime": {
|
||||
"lib/net6.0/MongoDB.Bson.dll": {
|
||||
"assemblyVersion": "3.5.0.0",
|
||||
"fileVersion": "3.5.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"MongoDB.Driver/3.5.0": {
|
||||
"dependencies": {
|
||||
"DnsClient": "1.6.1",
|
||||
"Microsoft.Extensions.Logging.Abstractions": "10.0.0-rc.2.25502.107",
|
||||
"MongoDB.Bson": "3.5.0",
|
||||
"SharpCompress": "0.41.0",
|
||||
"Snappier": "1.0.0",
|
||||
"ZstdSharp.Port": "0.8.6"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net6.0/MongoDB.Driver.dll": {
|
||||
"assemblyVersion": "3.5.0.0",
|
||||
"fileVersion": "3.5.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"NuGet.Versioning/6.9.1": {
|
||||
"runtime": {
|
||||
"lib/netstandard2.0/NuGet.Versioning.dll": {
|
||||
"assemblyVersion": "6.9.1.3",
|
||||
"fileVersion": "6.9.1.3"
|
||||
}
|
||||
}
|
||||
},
|
||||
"SharpCompress/0.41.0": {
|
||||
"dependencies": {
|
||||
"ZstdSharp.Port": "0.8.6"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net8.0/SharpCompress.dll": {
|
||||
"assemblyVersion": "0.41.0.0",
|
||||
"fileVersion": "0.41.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Snappier/1.0.0": {
|
||||
"runtime": {
|
||||
"lib/net5.0/Snappier.dll": {
|
||||
"assemblyVersion": "1.0.0.0",
|
||||
"fileVersion": "1.0.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ZstdSharp.Port/0.8.6": {
|
||||
"runtime": {
|
||||
"lib/net9.0/ZstdSharp.dll": {
|
||||
"assemblyVersion": "0.8.6.0",
|
||||
"fileVersion": "0.8.6.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"StellaOps.Aoc/1.0.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.0-rc.2.25502.107",
|
||||
"SharpCompress": "0.41.0"
|
||||
},
|
||||
"runtime": {
|
||||
"StellaOps.Aoc.dll": {
|
||||
"assemblyVersion": "1.0.0.0",
|
||||
"fileVersion": "1.0.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"StellaOps.Concelier.Core/1.0.0": {
|
||||
"dependencies": {
|
||||
"Cronos": "0.10.0",
|
||||
"Microsoft.Extensions.Hosting.Abstractions": "10.0.0-rc.2.25502.107",
|
||||
"Microsoft.Extensions.Logging.Abstractions": "10.0.0-rc.2.25502.107",
|
||||
"Microsoft.Extensions.Options": "10.0.0-rc.2.25502.107",
|
||||
"MongoDB.Driver": "3.5.0",
|
||||
"SharpCompress": "0.41.0",
|
||||
"StellaOps.Aoc": "1.0.0",
|
||||
"StellaOps.Concelier.Models": "1.0.0",
|
||||
"StellaOps.Concelier.Normalization": "1.0.0",
|
||||
"StellaOps.Concelier.RawModels": "1.0.0",
|
||||
"StellaOps.Ingestion.Telemetry": "1.0.0",
|
||||
"StellaOps.Plugin": "1.0.0",
|
||||
"StellaOps.Provenance.Mongo": "1.0.0"
|
||||
},
|
||||
"runtime": {
|
||||
"StellaOps.Concelier.Core.dll": {
|
||||
"assemblyVersion": "1.0.0.0",
|
||||
"fileVersion": "1.0.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"StellaOps.Concelier.Models/1.0.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "10.0.0-rc.2.25502.107",
|
||||
"SharpCompress": "0.41.0",
|
||||
"StellaOps.Concelier.RawModels": "1.0.0"
|
||||
},
|
||||
"runtime": {
|
||||
"StellaOps.Concelier.Models.dll": {
|
||||
"assemblyVersion": "1.0.0.0",
|
||||
"fileVersion": "1.0.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"StellaOps.Concelier.Normalization/1.0.0": {
|
||||
"dependencies": {
|
||||
"NuGet.Versioning": "6.9.1",
|
||||
"SharpCompress": "0.41.0",
|
||||
"StellaOps.Concelier.Models": "1.0.0"
|
||||
},
|
||||
"runtime": {
|
||||
"StellaOps.Concelier.Normalization.dll": {
|
||||
"assemblyVersion": "1.0.0.0",
|
||||
"fileVersion": "1.0.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"StellaOps.Concelier.RawModels/1.0.0": {
|
||||
"dependencies": {
|
||||
"MongoDB.Bson": "3.5.0",
|
||||
"SharpCompress": "0.41.0"
|
||||
},
|
||||
"runtime": {
|
||||
"StellaOps.Concelier.RawModels.dll": {
|
||||
"assemblyVersion": "1.0.0.0",
|
||||
"fileVersion": "1.0.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"StellaOps.DependencyInjection/1.0.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Configuration.Abstractions": "10.0.0-rc.2.25502.107",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.0-rc.2.25502.107",
|
||||
"SharpCompress": "0.41.0"
|
||||
},
|
||||
"runtime": {
|
||||
"StellaOps.DependencyInjection.dll": {
|
||||
"assemblyVersion": "1.0.0.0",
|
||||
"fileVersion": "1.0.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"StellaOps.Ingestion.Telemetry/1.0.0": {
|
||||
"dependencies": {
|
||||
"SharpCompress": "0.41.0"
|
||||
},
|
||||
"runtime": {
|
||||
"StellaOps.Ingestion.Telemetry.dll": {
|
||||
"assemblyVersion": "1.0.0.0",
|
||||
"fileVersion": "1.0.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"StellaOps.Plugin/1.0.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Configuration.Abstractions": "10.0.0-rc.2.25502.107",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.0-rc.2.25502.107",
|
||||
"Microsoft.Extensions.Logging.Abstractions": "10.0.0-rc.2.25502.107",
|
||||
"SharpCompress": "0.41.0",
|
||||
"StellaOps.DependencyInjection": "1.0.0"
|
||||
},
|
||||
"runtime": {
|
||||
"StellaOps.Plugin.dll": {
|
||||
"assemblyVersion": "1.0.0.0",
|
||||
"fileVersion": "1.0.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"StellaOps.Provenance.Mongo/1.0.0": {
|
||||
"dependencies": {
|
||||
"MongoDB.Driver": "3.5.0",
|
||||
"SharpCompress": "0.41.0"
|
||||
},
|
||||
"runtime": {
|
||||
"StellaOps.Provenance.Mongo.dll": {
|
||||
"assemblyVersion": "1.0.0.0",
|
||||
"fileVersion": "1.0.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"libraries": {
|
||||
"StellaOps.Concelier.Storage.Mongo/1.0.0": {
|
||||
"type": "project",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
},
|
||||
"Cronos/0.10.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-wHL4tr8mWTvrJt/4sI3raympCNVT4F3VJI4SJHA9A/wB+8Lsq84RFGQH9bHEtvNsN1lCBTKNk+uVoDotGcYJZA==",
|
||||
"path": "cronos/0.10.0",
|
||||
"hashPath": "cronos.0.10.0.nupkg.sha512"
|
||||
},
|
||||
"DnsClient/1.6.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-4H/f2uYJOZ+YObZjpY9ABrKZI+JNw3uizp6oMzTXwDw6F+2qIPhpRl/1t68O/6e98+vqNiYGu+lswmwdYUy3gg==",
|
||||
"path": "dnsclient/1.6.1",
|
||||
"hashPath": "dnsclient.1.6.1.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Extensions.Configuration.Abstractions/10.0.0-rc.2.25502.107": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-H+i/Qy30Rg/K9BcW2Z6DCHPCzwMH3bCwNOjEz31shWTUDK8GeeeMnrKVusprTcRA2Y6yPST+hg2zc3whPEs14Q==",
|
||||
"path": "microsoft.extensions.configuration.abstractions/10.0.0-rc.2.25502.107",
|
||||
"hashPath": "microsoft.extensions.configuration.abstractions.10.0.0-rc.2.25502.107.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions/10.0.0-rc.2.25502.107": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-8jujunpkNNfTkE9PFHp9/aD6GPKVfNCuz8tUbzOcyU5tQOCoIZId4hwQNVx3Tb8XEWw9BYdh0k5vPpqdCM+UtA==",
|
||||
"path": "microsoft.extensions.dependencyinjection.abstractions/10.0.0-rc.2.25502.107",
|
||||
"hashPath": "microsoft.extensions.dependencyinjection.abstractions.10.0.0-rc.2.25502.107.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Extensions.Diagnostics.Abstractions/10.0.0-rc.2.25502.107": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-x6XVv3RiwOlN2unjyX/Zat0gI0HiRoDDdjkwBCwsMftYWpbJu4SiyRwDbrv2zAF8v8nbEEvcWi3/pUxZfaqLQw==",
|
||||
"path": "microsoft.extensions.diagnostics.abstractions/10.0.0-rc.2.25502.107",
|
||||
"hashPath": "microsoft.extensions.diagnostics.abstractions.10.0.0-rc.2.25502.107.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Extensions.FileProviders.Abstractions/10.0.0-rc.2.25502.107": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-dOpmW14MkOZIwV6269iXhoMp6alCHBoxqCR4pJ37GLjFaBIyzsIy+Ra8tsGmjHtFvEHKq0JRDIsb1PUkrK+yxw==",
|
||||
"path": "microsoft.extensions.fileproviders.abstractions/10.0.0-rc.2.25502.107",
|
||||
"hashPath": "microsoft.extensions.fileproviders.abstractions.10.0.0-rc.2.25502.107.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Extensions.Hosting.Abstractions/10.0.0-rc.2.25502.107": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-M6zqZFbqjdCx8g5Y2XZKTfYfS0gAh4uJkmdAq/ZRDrpIr3Nd+u74allmw15jX1kM61IXM49EnTbhMzlWw5pGVQ==",
|
||||
"path": "microsoft.extensions.hosting.abstractions/10.0.0-rc.2.25502.107",
|
||||
"hashPath": "microsoft.extensions.hosting.abstractions.10.0.0-rc.2.25502.107.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Extensions.Logging.Abstractions/10.0.0-rc.2.25502.107": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-SKKKZjyCpBaDQ7yuFjdk6ELnRBRWeZsbnzUfo59Wc4PGhgf92chE3we/QlT6nk6NqlWcUgH/jogM+B/uq/Qdnw==",
|
||||
"path": "microsoft.extensions.logging.abstractions/10.0.0-rc.2.25502.107",
|
||||
"hashPath": "microsoft.extensions.logging.abstractions.10.0.0-rc.2.25502.107.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Extensions.Options/10.0.0-rc.2.25502.107": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-Ib6BCCjisp7ZUdhtNpSulFO0ODhz/IE4ZZd8OCqQWoRs363BQ0QOZi9KwpqpiEWo51S0kIXWqNicDPGXwpt9pQ==",
|
||||
"path": "microsoft.extensions.options/10.0.0-rc.2.25502.107",
|
||||
"hashPath": "microsoft.extensions.options.10.0.0-rc.2.25502.107.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Extensions.Primitives/10.0.0-rc.2.25502.107": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-9pm2zqqn5u/OsKs2zgkhJEQQeMx9KkVOWPdHrs7Kt5sfpk+eIh/gmpi/mMH/ljS2T/PFsFdCEtm+GS/6l7zoZA==",
|
||||
"path": "microsoft.extensions.primitives/10.0.0-rc.2.25502.107",
|
||||
"hashPath": "microsoft.extensions.primitives.10.0.0-rc.2.25502.107.nupkg.sha512"
|
||||
},
|
||||
"MongoDB.Bson/3.5.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-JGNK6BanLDEifgkvPLqVFCPus5EDCy416pxf1dxUBRSVd3D9+NB3AvMVX190eXlk5/UXuCxpsQv7jWfNKvppBQ==",
|
||||
"path": "mongodb.bson/3.5.0",
|
||||
"hashPath": "mongodb.bson.3.5.0.nupkg.sha512"
|
||||
},
|
||||
"MongoDB.Driver/3.5.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-ST90u7psyMkNNOWFgSkexsrB3kPn7Ynl2DlMFj2rJyYuc6SIxjmzu4ufy51yzM+cPVE1SvVcdb5UFobrRw6cMg==",
|
||||
"path": "mongodb.driver/3.5.0",
|
||||
"hashPath": "mongodb.driver.3.5.0.nupkg.sha512"
|
||||
},
|
||||
"NuGet.Versioning/6.9.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-ypnSvEtpNGo48bAWn95J1oHChycCXcevFSbn53fqzLxlXFSZP7dawu8p/7mHAfGufZQSV2sBpW80XQGIfXO8kQ==",
|
||||
"path": "nuget.versioning/6.9.1",
|
||||
"hashPath": "nuget.versioning.6.9.1.nupkg.sha512"
|
||||
},
|
||||
"SharpCompress/0.41.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-z04dBVdTIAFTRKi38f0LkajaKA++bR+M8kYCbasXePILD2H+qs7CkLpyiippB24CSbTrWIgpBKm6BenZqkUwvw==",
|
||||
"path": "sharpcompress/0.41.0",
|
||||
"hashPath": "sharpcompress.0.41.0.nupkg.sha512"
|
||||
},
|
||||
"Snappier/1.0.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-rFtK2KEI9hIe8gtx3a0YDXdHOpedIf9wYCEYtBEmtlyiWVX3XlCNV03JrmmAi/Cdfn7dxK+k0sjjcLv4fpHnqA==",
|
||||
"path": "snappier/1.0.0",
|
||||
"hashPath": "snappier.1.0.0.nupkg.sha512"
|
||||
},
|
||||
"ZstdSharp.Port/0.8.6": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-iP4jVLQoQmUjMU88g1WObiNr6YKZGvh4aOXn3yOJsHqZsflwRsxZPcIBvNXgjXO3vQKSLctXGLTpcBPLnWPS8A==",
|
||||
"path": "zstdsharp.port/0.8.6",
|
||||
"hashPath": "zstdsharp.port.0.8.6.nupkg.sha512"
|
||||
},
|
||||
"StellaOps.Aoc/1.0.0": {
|
||||
"type": "project",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
},
|
||||
"StellaOps.Concelier.Core/1.0.0": {
|
||||
"type": "project",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
},
|
||||
"StellaOps.Concelier.Models/1.0.0": {
|
||||
"type": "project",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
},
|
||||
"StellaOps.Concelier.Normalization/1.0.0": {
|
||||
"type": "project",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
},
|
||||
"StellaOps.Concelier.RawModels/1.0.0": {
|
||||
"type": "project",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
},
|
||||
"StellaOps.DependencyInjection/1.0.0": {
|
||||
"type": "project",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
},
|
||||
"StellaOps.Ingestion.Telemetry/1.0.0": {
|
||||
"type": "project",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
},
|
||||
"StellaOps.Plugin/1.0.0": {
|
||||
"type": "project",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
},
|
||||
"StellaOps.Provenance.Mongo/1.0.0": {
|
||||
"type": "project",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using StellaOps.Concelier.Core.Linksets;
|
||||
using StellaOps.Concelier.RawModels;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.Concelier.Core.Tests.Linksets;
|
||||
|
||||
public sealed class AdvisoryLinksetNormalizationTests
|
||||
{
|
||||
[Fact]
|
||||
public void FromRawLinksetWithConfidence_ExtractsNotesAsConflicts()
|
||||
{
|
||||
var linkset = new RawLinkset
|
||||
{
|
||||
PackageUrls = ImmutableArray.Create("pkg:npm/foo@1.0.0"),
|
||||
Notes = new Dictionary<string, string>
|
||||
{
|
||||
{ "severity", "disagree" }
|
||||
}
|
||||
};
|
||||
|
||||
var (normalized, confidence, conflicts) = AdvisoryLinksetNormalization.FromRawLinksetWithConfidence(linkset, 0.8);
|
||||
|
||||
Assert.NotNull(normalized);
|
||||
Assert.Equal(0.8, confidence);
|
||||
Assert.Single(conflicts);
|
||||
Assert.Equal("severity", conflicts[0].Field);
|
||||
Assert.Equal("disagree", conflicts[0].Reason);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(-1, 0)]
|
||||
[InlineData(2, 1)]
|
||||
[InlineData(double.NaN, null)]
|
||||
public void FromRawLinksetWithConfidence_ClampsConfidence(double input, double? expected)
|
||||
{
|
||||
var linkset = new RawLinkset
|
||||
{
|
||||
PackageUrls = ImmutableArray<string>.Empty
|
||||
};
|
||||
|
||||
var (_, confidence, _) = AdvisoryLinksetNormalization.FromRawLinksetWithConfidence(linkset, input);
|
||||
|
||||
Assert.Equal(expected, confidence);
|
||||
}
|
||||
}
|
||||
@@ -15,21 +15,24 @@ public sealed class AdvisoryLinksetQueryServiceTests
|
||||
new("tenant", "ghsa", "adv-003",
|
||||
ImmutableArray.Create("obs-003"),
|
||||
new AdvisoryLinksetNormalized(new[]{"pkg:npm/a"}, new[]{"1.0.0"}, null, null),
|
||||
null, DateTimeOffset.Parse("2025-11-10T12:00:00Z"), null),
|
||||
null, null, null,
|
||||
DateTimeOffset.Parse("2025-11-10T12:00:00Z"), null),
|
||||
new("tenant", "ghsa", "adv-002",
|
||||
ImmutableArray.Create("obs-002"),
|
||||
new AdvisoryLinksetNormalized(new[]{"pkg:npm/b"}, new[]{"2.0.0"}, null, null),
|
||||
null, DateTimeOffset.Parse("2025-11-09T12:00:00Z"), null),
|
||||
null, null, null,
|
||||
DateTimeOffset.Parse("2025-11-09T12:00:00Z"), null),
|
||||
new("tenant", "ghsa", "adv-001",
|
||||
ImmutableArray.Create("obs-001"),
|
||||
new AdvisoryLinksetNormalized(new[]{"pkg:npm/c"}, new[]{"3.0.0"}, null, null),
|
||||
null, DateTimeOffset.Parse("2025-11-08T12:00:00Z"), null),
|
||||
null, null, null,
|
||||
DateTimeOffset.Parse("2025-11-08T12:00:00Z"), null),
|
||||
};
|
||||
|
||||
var lookup = new FakeLinksetLookup(linksets);
|
||||
var service = new AdvisoryLinksetQueryService(lookup);
|
||||
|
||||
var firstPage = await service.QueryAsync(new AdvisoryLinksetQueryOptions("tenant", limit: 2), CancellationToken.None);
|
||||
var firstPage = await service.QueryAsync(new AdvisoryLinksetQueryOptions("tenant", Limit: 2), CancellationToken.None);
|
||||
|
||||
Assert.Equal(2, firstPage.Linksets.Length);
|
||||
Assert.True(firstPage.HasMore);
|
||||
@@ -37,7 +40,7 @@ public sealed class AdvisoryLinksetQueryServiceTests
|
||||
Assert.Equal("adv-003", firstPage.Linksets[0].AdvisoryId);
|
||||
Assert.Equal("pkg:npm/a", firstPage.Linksets[0].Normalized?.Purls?.First());
|
||||
|
||||
var secondPage = await service.QueryAsync(new AdvisoryLinksetQueryOptions("tenant", limit: 2, Cursor: firstPage.NextCursor), CancellationToken.None);
|
||||
var secondPage = await service.QueryAsync(new AdvisoryLinksetQueryOptions("tenant", Limit: 2, Cursor: firstPage.NextCursor), CancellationToken.None);
|
||||
|
||||
Assert.Single(secondPage.Linksets);
|
||||
Assert.False(secondPage.HasMore);
|
||||
@@ -53,7 +56,7 @@ public sealed class AdvisoryLinksetQueryServiceTests
|
||||
|
||||
await Assert.ThrowsAsync<FormatException>(async () =>
|
||||
{
|
||||
await service.QueryAsync(new AdvisoryLinksetQueryOptions("tenant", limit: 1, Cursor: "not-base64"), CancellationToken.None);
|
||||
await service.QueryAsync(new AdvisoryLinksetQueryOptions("tenant", Limit: 1, Cursor: "not-base64"), CancellationToken.None);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
using System;
|
||||
using System.Collections.Immutable;
|
||||
using System.Reflection;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using StellaOps.Concelier.Core.Observations;
|
||||
using StellaOps.Concelier.Models.Observations;
|
||||
using StellaOps.Concelier.RawModels;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.Concelier.Core.Tests.Observations;
|
||||
|
||||
public sealed class AdvisoryObservationAggregationTests
|
||||
{
|
||||
[Fact]
|
||||
public void BuildAggregateLinkset_AccumulatesScopesAndRelationships()
|
||||
{
|
||||
var rawLinkset = new RawLinkset
|
||||
{
|
||||
Scopes = ImmutableArray.Create("pkg:npm/foo", "os:debian"),
|
||||
Relationships = ImmutableArray.Create(
|
||||
new RawRelationship("depends_on", "pkg:npm/foo", "pkg:npm/bar"))
|
||||
};
|
||||
|
||||
var observation = CreateObservation("obs-1", rawLinkset);
|
||||
var method = typeof(AdvisoryObservationQueryService).GetMethod(
|
||||
"BuildAggregateLinkset",
|
||||
BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||
|
||||
var aggregate = (AdvisoryObservationLinksetAggregate)method.Invoke(
|
||||
null,
|
||||
new object?[] { ImmutableArray.Create(observation) })!;
|
||||
|
||||
Assert.Equal(ImmutableArray.Create("os:debian", "pkg:npm/foo"), aggregate.Scopes);
|
||||
Assert.Single(aggregate.Relationships);
|
||||
Assert.Equal("depends_on", aggregate.Relationships[0].Type);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FromRawLinksetWithConfidence_AssignsLowerConfidenceWhenConflictsPresent()
|
||||
{
|
||||
var linkset = new RawLinkset
|
||||
{
|
||||
Notes = new Dictionary<string, string>
|
||||
{
|
||||
{ "severity", "disagree" }
|
||||
}
|
||||
};
|
||||
|
||||
var (normalized, confidence, conflicts) = AdvisoryLinksetNormalization.FromRawLinksetWithConfidence(linkset);
|
||||
|
||||
Assert.Equal(0.5, confidence);
|
||||
Assert.Single(conflicts);
|
||||
Assert.Null(normalized); // no purls supplied
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildAggregateLinkset_EmptyInputReturnsEmptyArrays()
|
||||
{
|
||||
var method = typeof(AdvisoryObservationQueryService).GetMethod(
|
||||
"BuildAggregateLinkset",
|
||||
BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||
|
||||
var aggregate = (AdvisoryObservationLinksetAggregate)method.Invoke(
|
||||
null,
|
||||
new object?[] { ImmutableArray<AdvisoryObservation>.Empty })!;
|
||||
|
||||
Assert.True(aggregate.Scopes.IsEmpty);
|
||||
Assert.True(aggregate.Relationships.IsEmpty);
|
||||
}
|
||||
|
||||
private static AdvisoryObservation CreateObservation(string id, RawLinkset rawLinkset)
|
||||
{
|
||||
var source = new AdvisoryObservationSource("vendor", "stream", "api");
|
||||
var upstream = new AdvisoryObservationUpstream(
|
||||
"adv-id",
|
||||
null,
|
||||
DateTimeOffset.UtcNow,
|
||||
DateTimeOffset.UtcNow,
|
||||
"sha256:abc",
|
||||
new AdvisoryObservationSignature(false, null, null, null));
|
||||
|
||||
var content = new AdvisoryObservationContent("json", null, JsonNode.Parse("{}")!);
|
||||
var linkset = new AdvisoryObservationLinkset(
|
||||
Array.Empty<string>(),
|
||||
Array.Empty<string>(),
|
||||
Array.Empty<string>(),
|
||||
Array.Empty<AdvisoryObservationReference>());
|
||||
|
||||
return new AdvisoryObservation(
|
||||
id,
|
||||
"tenant",
|
||||
source,
|
||||
upstream,
|
||||
content,
|
||||
linkset,
|
||||
rawLinkset,
|
||||
DateTimeOffset.UtcNow);
|
||||
}
|
||||
}
|
||||
@@ -164,15 +164,69 @@ public sealed class AdvisoryRawServiceTests
|
||||
var guard = aocGuard ?? new AocWriteGuard();
|
||||
var resolvedWriteGuard = writeGuard ?? new NoOpWriteGuard();
|
||||
var linksetMapper = new PassthroughLinksetMapper();
|
||||
var observationFactory = new StubObservationFactory();
|
||||
var observationSink = new NullObservationSink();
|
||||
var linksetSink = new NullLinksetSink();
|
||||
|
||||
return new AdvisoryRawService(
|
||||
repository,
|
||||
resolvedWriteGuard,
|
||||
guard,
|
||||
linksetMapper,
|
||||
observationFactory,
|
||||
observationSink,
|
||||
linksetSink,
|
||||
TimeProvider.System,
|
||||
NullLogger<AdvisoryRawService>.Instance);
|
||||
}
|
||||
|
||||
private sealed class NullObservationSink : IAdvisoryObservationSink
|
||||
{
|
||||
public Task UpsertAsync(Models.Observations.AdvisoryObservation observation, CancellationToken cancellationToken)
|
||||
=> Task.CompletedTask;
|
||||
}
|
||||
|
||||
private sealed class NullLinksetSink : IAdvisoryLinksetSink
|
||||
{
|
||||
public Task UpsertAsync(AdvisoryLinkset linkset, CancellationToken cancellationToken)
|
||||
=> Task.CompletedTask;
|
||||
}
|
||||
|
||||
private sealed class StubObservationFactory : IAdvisoryObservationFactory
|
||||
{
|
||||
public Models.Observations.AdvisoryObservation Create(Models.Advisory advisory, string tenant, string source, RawModels.AdvisoryRawDocument raw, string advisoryKey, string observationId, DateTimeOffset createdAt)
|
||||
{
|
||||
var upstream = new Models.Observations.AdvisoryObservationUpstream(
|
||||
upstreamId: raw.Upstream.UpstreamId,
|
||||
documentVersion: raw.Upstream.DocumentVersion,
|
||||
fetchedAt: raw.Upstream.RetrievedAt ?? createdAt,
|
||||
receivedAt: createdAt,
|
||||
contentHash: raw.Upstream.ContentHash,
|
||||
signature: new Models.Observations.AdvisoryObservationSignature(raw.Upstream.Signature.Present, raw.Upstream.Signature.Format, raw.Upstream.Signature.KeyId, raw.Upstream.Signature.Signature),
|
||||
metadata: raw.Upstream.Provenance);
|
||||
|
||||
var content = new Models.Observations.AdvisoryObservationContent(raw.Content.Format, raw.Content.SpecVersion, JsonDocument.Parse(raw.Content.Raw.GetRawText()).RootElement);
|
||||
|
||||
var linkset = new Models.Observations.AdvisoryObservationLinkset(
|
||||
raw.Linkset.Aliases,
|
||||
raw.Linkset.PackageUrls,
|
||||
raw.Linkset.Cpes,
|
||||
ImmutableArray<Models.Observations.AdvisoryObservationReference>.Empty);
|
||||
|
||||
var rawLinkset = raw.Linkset;
|
||||
|
||||
return new Models.Observations.AdvisoryObservation(
|
||||
observationId,
|
||||
tenant,
|
||||
new Models.Observations.AdvisoryObservationSource(source, "stream", "api"),
|
||||
upstream,
|
||||
content,
|
||||
linkset,
|
||||
rawLinkset,
|
||||
createdAt);
|
||||
}
|
||||
}
|
||||
|
||||
private static AdvisoryRawDocument CreateDocument()
|
||||
{
|
||||
using var raw = JsonDocument.Parse("""{"id":"demo"}""");
|
||||
|
||||
@@ -20,13 +20,14 @@ public sealed class EnsureAdvisoryLinksetsTenantLowerMigrationTests : IClassFixt
|
||||
[Fact]
|
||||
public async Task ApplyAsync_LowersTenantIds()
|
||||
{
|
||||
var collection = _fixture.Database.GetCollection<BsonDocument>(MongoStorageDefaults.Collections.AdvisoryLinksets);
|
||||
await _fixture.Database.DropCollectionAsync(MongoStorageDefaults.Collections.AdvisoryLinksets);
|
||||
var collection = _fixture.Database.GetCollection<BsonDocument>(MongoStorageDefaults.Collections.AdvisoryLinksets);
|
||||
|
||||
await collection.InsertManyAsync(new[]
|
||||
{
|
||||
new BsonDocument { { "TenantId", "Tenant-A" }, { "Source", "src" }, { "AdvisoryId", "ADV-1" }, { "Observations", new BsonArray() } },
|
||||
new BsonDocument { { "TenantId", "tenant-b" }, { "Source", "src" }, { "AdvisoryId", "ADV-2" }, { "Observations", new BsonArray() } }
|
||||
new BsonDocument { { "TenantId", "tenant-b" }, { "Source", "src" }, { "AdvisoryId", "ADV-2" }, { "Observations", new BsonArray() } },
|
||||
new BsonDocument { { "Source", "src" }, { "AdvisoryId", "ADV-3" }, { "Observations", new BsonArray() } } // missing tenant should be ignored
|
||||
});
|
||||
|
||||
var migration = new EnsureAdvisoryLinksetsTenantLowerMigration();
|
||||
|
||||
@@ -4,6 +4,12 @@
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<CollectCoverage>false</CollectCoverage>
|
||||
<RunAnalyzers>false</RunAnalyzers>
|
||||
<RunAnalyzersDuringBuild>false</RunAnalyzersDuringBuild>
|
||||
<UseSharedCompilation>false</UseSharedCompilation>
|
||||
<CopyBuildOutputToOutputDirectory>true</CopyBuildOutputToOutputDirectory>
|
||||
<CopyOutputSymbolsToOutputDirectory>true</CopyOutputSymbolsToOutputDirectory>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="../../__Libraries/StellaOps.Concelier.Core/StellaOps.Concelier.Core.csproj" />
|
||||
|
||||
@@ -69,6 +69,7 @@ public sealed class WebServiceEndpointsTests : IAsyncLifetime
|
||||
|
||||
public Task InitializeAsync()
|
||||
{
|
||||
PrepareMongoEnvironment();
|
||||
_runner = MongoDbRunner.Start(singleNodeReplSet: true);
|
||||
_factory = new ConcelierApplicationFactory(_runner.ConnectionString);
|
||||
WarmupFactory(_factory);
|
||||
@@ -145,6 +146,12 @@ public sealed class WebServiceEndpointsTests : IAsyncLifetime
|
||||
Assert.Equal("https://example.test/advisory-1", references[0].GetProperty("url").GetString());
|
||||
Assert.Equal("patch", references[1].GetProperty("type").GetString());
|
||||
|
||||
var confidence = linkset.GetProperty("confidence").GetDouble();
|
||||
Assert.Equal(1.0, confidence);
|
||||
|
||||
var conflicts = linkset.GetProperty("conflicts").EnumerateArray().ToArray();
|
||||
Assert.Empty(conflicts);
|
||||
|
||||
Assert.False(root.GetProperty("hasMore").GetBoolean());
|
||||
Assert.True(root.GetProperty("nextCursor").ValueKind == JsonValueKind.Null);
|
||||
}
|
||||
@@ -2500,20 +2507,92 @@ public sealed class WebServiceEndpointsTests : IAsyncLifetime
|
||||
{
|
||||
_output.WriteLine($"[PROGRAM LOG] {entry.Level}: {entry.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void WarmupFactory(WebApplicationFactory<Program> factory)
|
||||
{
|
||||
using var client = factory.CreateClient();
|
||||
}
|
||||
private static void WarmupFactory(WebApplicationFactory<Program> factory)
|
||||
{
|
||||
using var client = factory.CreateClient();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensure Mongo2Go can start without external downloads by pointing it to cached binaries and OpenSSL 1.1 libs shipped in repo.
|
||||
/// </summary>
|
||||
private static void PrepareMongoEnvironment()
|
||||
{
|
||||
var repoRoot = FindRepoRoot();
|
||||
if (repoRoot is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var cacheDir = Path.Combine(repoRoot, ".cache", "mongodb-local");
|
||||
Directory.CreateDirectory(cacheDir);
|
||||
Environment.SetEnvironmentVariable("MONGO2GO_CACHE_LOCATION", cacheDir);
|
||||
Environment.SetEnvironmentVariable("MONGO2GO_DOWNLOADS", cacheDir);
|
||||
Environment.SetEnvironmentVariable("MONGO2GO_MONGODB_VERSION", "4.4.4");
|
||||
|
||||
var opensslPath = Path.Combine(repoRoot, "tests", "native", "openssl-1.1", "linux-x64");
|
||||
if (Directory.Exists(opensslPath))
|
||||
{
|
||||
// Prepend OpenSSL 1.1 path so Mongo2Go binaries find libssl/libcrypto.
|
||||
var existing = Environment.GetEnvironmentVariable("LD_LIBRARY_PATH");
|
||||
var combined = string.IsNullOrEmpty(existing) ? opensslPath : $"{opensslPath}:{existing}";
|
||||
Environment.SetEnvironmentVariable("LD_LIBRARY_PATH", combined);
|
||||
}
|
||||
|
||||
// Also drop the OpenSSL libs next to the mongod binary Mongo2Go will spawn, in case LD_LIBRARY_PATH is ignored.
|
||||
var mongoBin = Directory.Exists(Path.Combine(repoRoot, ".nuget"))
|
||||
? Directory.GetFiles(Path.Combine(repoRoot, ".nuget", "packages", "mongo2go"), "mongod", SearchOption.AllDirectories)
|
||||
.FirstOrDefault(path => path.Contains("mongodb-linux-4.4.4", StringComparison.OrdinalIgnoreCase))
|
||||
: null;
|
||||
if (mongoBin is not null && File.Exists(mongoBin) && Directory.Exists(opensslPath))
|
||||
{
|
||||
var binDir = Path.GetDirectoryName(mongoBin)!;
|
||||
foreach (var libName in new[] { "libssl.so.1.1", "libcrypto.so.1.1" })
|
||||
{
|
||||
var target = Path.Combine(binDir, libName);
|
||||
var source = Path.Combine(opensslPath, libName);
|
||||
if (File.Exists(source) && !File.Exists(target))
|
||||
{
|
||||
File.Copy(source, target);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static string? FindRepoRoot()
|
||||
{
|
||||
var current = AppContext.BaseDirectory;
|
||||
while (!string.IsNullOrEmpty(current))
|
||||
{
|
||||
if (File.Exists(Path.Combine(current, "Directory.Build.props")))
|
||||
{
|
||||
return current;
|
||||
}
|
||||
|
||||
var parent = Directory.GetParent(current);
|
||||
if (parent is null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
current = parent.FullName;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static AdvisoryIngestRequest BuildAdvisoryIngestRequest(
|
||||
string? contentHash,
|
||||
string upstreamId,
|
||||
bool enforceContentHash = true)
|
||||
bool enforceContentHash = true,
|
||||
IReadOnlyList<string>? purls = null,
|
||||
IReadOnlyList<string>? notes = null)
|
||||
{
|
||||
var raw = CreateJsonElement($@"{{""id"":""{upstreamId}"",""modified"":""{DefaultIngestTimestamp:O}""}}");
|
||||
var normalizedContentHash = NormalizeContentHash(contentHash, raw, enforceContentHash);
|
||||
var resolvedPurls = purls ?? new[] { "pkg:npm/demo@1.0.0" };
|
||||
var resolvedNotes = notes ?? Array.Empty<string>();
|
||||
var references = new[]
|
||||
{
|
||||
new AdvisoryLinksetReferenceRequest("advisory", $"https://example.test/advisories/{upstreamId}", null)
|
||||
@@ -2534,11 +2613,12 @@ public sealed class WebServiceEndpointsTests : IAsyncLifetime
|
||||
new[] { upstreamId, $"{upstreamId}-ALIAS" }),
|
||||
new AdvisoryLinksetRequest(
|
||||
new[] { upstreamId },
|
||||
new[] { "pkg:npm/demo@1.0.0" },
|
||||
resolvedPurls,
|
||||
Array.Empty<AdvisoryLinksetRelationshipRequest>(),
|
||||
Array.Empty<string>(),
|
||||
Array.Empty<string>(),
|
||||
references,
|
||||
Array.Empty<string>(),
|
||||
Array.Empty<string>(),
|
||||
resolvedNotes,
|
||||
new Dictionary<string, string> { ["note"] = "ingest-test" }));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user