Refactor code structure for improved readability and maintainability
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
using MongoDB.Bson.Serialization.Attributes;
|
||||
using StellaOps.Scanner.Core.Contracts;
|
||||
|
||||
namespace StellaOps.Scanner.Storage.Catalog;
|
||||
|
||||
[BsonIgnoreExtraElements]
|
||||
public sealed class BunPackageInventoryDocument
|
||||
{
|
||||
[BsonId]
|
||||
public string ScanId { get; set; } = string.Empty;
|
||||
|
||||
[BsonElement("imageDigest")]
|
||||
[BsonIgnoreIfNull]
|
||||
public string? ImageDigest { get; set; }
|
||||
= null;
|
||||
|
||||
[BsonElement("generatedAtUtc")]
|
||||
public DateTime GeneratedAtUtc { get; set; }
|
||||
= DateTime.UtcNow;
|
||||
|
||||
[BsonElement("packages")]
|
||||
public List<BunPackageDocument> Packages { get; set; }
|
||||
= new();
|
||||
}
|
||||
|
||||
[BsonIgnoreExtraElements]
|
||||
public sealed class BunPackageDocument
|
||||
{
|
||||
[BsonElement("id")]
|
||||
public string Id { get; set; } = string.Empty;
|
||||
|
||||
[BsonElement("name")]
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
[BsonElement("version")]
|
||||
[BsonIgnoreIfNull]
|
||||
public string? Version { get; set; }
|
||||
= null;
|
||||
|
||||
[BsonElement("source")]
|
||||
[BsonIgnoreIfNull]
|
||||
public string? Source { get; set; }
|
||||
= null;
|
||||
|
||||
[BsonElement("resolved")]
|
||||
[BsonIgnoreIfNull]
|
||||
public string? Resolved { get; set; }
|
||||
= null;
|
||||
|
||||
[BsonElement("integrity")]
|
||||
[BsonIgnoreIfNull]
|
||||
public string? Integrity { get; set; }
|
||||
= null;
|
||||
|
||||
[BsonElement("isDev")]
|
||||
[BsonIgnoreIfNull]
|
||||
public bool? IsDev { get; set; }
|
||||
= null;
|
||||
|
||||
[BsonElement("isDirect")]
|
||||
[BsonIgnoreIfNull]
|
||||
public bool? IsDirect { get; set; }
|
||||
= null;
|
||||
|
||||
[BsonElement("isPatched")]
|
||||
[BsonIgnoreIfNull]
|
||||
public bool? IsPatched { get; set; }
|
||||
= null;
|
||||
|
||||
[BsonElement("provenance")]
|
||||
[BsonIgnoreIfNull]
|
||||
public BunPackageProvenance? Provenance { get; set; }
|
||||
= null;
|
||||
|
||||
[BsonElement("metadata")]
|
||||
[BsonIgnoreIfNull]
|
||||
public Dictionary<string, string?>? Metadata { get; set; }
|
||||
= null;
|
||||
}
|
||||
@@ -1,16 +1,16 @@
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using Amazon;
|
||||
using Amazon.S3;
|
||||
using Amazon.Runtime;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using MongoDB.Driver;
|
||||
using StellaOps.Scanner.Core.Contracts;
|
||||
using StellaOps.Scanner.EntryTrace;
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using Amazon;
|
||||
using Amazon.S3;
|
||||
using Amazon.Runtime;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using MongoDB.Driver;
|
||||
using StellaOps.Scanner.Core.Contracts;
|
||||
using StellaOps.Scanner.EntryTrace;
|
||||
using StellaOps.Scanner.Storage.Migrations;
|
||||
using StellaOps.Scanner.Storage.Mongo;
|
||||
using StellaOps.Scanner.Storage.ObjectStore;
|
||||
@@ -62,65 +62,67 @@ public static class ServiceCollectionExtensions
|
||||
services.TryAddSingleton<MongoBootstrapper>();
|
||||
services.TryAddSingleton<ArtifactRepository>();
|
||||
services.TryAddSingleton<ImageRepository>();
|
||||
services.TryAddSingleton<LayerRepository>();
|
||||
services.TryAddSingleton<LinkRepository>();
|
||||
services.TryAddSingleton<JobRepository>();
|
||||
services.TryAddSingleton<LifecycleRuleRepository>();
|
||||
services.TryAddSingleton<RuntimeEventRepository>();
|
||||
services.TryAddSingleton<EntryTraceRepository>();
|
||||
services.TryAddSingleton<RubyPackageInventoryRepository>();
|
||||
services.AddSingleton<IEntryTraceResultStore, EntryTraceResultStore>();
|
||||
services.AddSingleton<IRubyPackageInventoryStore, RubyPackageInventoryStore>();
|
||||
|
||||
services.AddHttpClient(RustFsArtifactObjectStore.HttpClientName)
|
||||
.ConfigureHttpClient((sp, client) =>
|
||||
{
|
||||
var options = sp.GetRequiredService<IOptions<ScannerStorageOptions>>().Value.ObjectStore;
|
||||
if (!options.IsRustFsDriver())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Uri.TryCreate(options.RustFs.BaseUrl, UriKind.Absolute, out var baseUri))
|
||||
{
|
||||
throw new InvalidOperationException("RustFS baseUrl must be a valid absolute URI.");
|
||||
}
|
||||
|
||||
client.BaseAddress = baseUri;
|
||||
client.Timeout = options.RustFs.Timeout;
|
||||
|
||||
foreach (var header in options.Headers)
|
||||
{
|
||||
client.DefaultRequestHeaders.TryAddWithoutValidation(header.Key, header.Value);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(options.RustFs.ApiKeyHeader)
|
||||
&& !string.IsNullOrWhiteSpace(options.RustFs.ApiKey))
|
||||
{
|
||||
client.DefaultRequestHeaders.TryAddWithoutValidation(options.RustFs.ApiKeyHeader, options.RustFs.ApiKey);
|
||||
}
|
||||
})
|
||||
.ConfigurePrimaryHttpMessageHandler(sp =>
|
||||
{
|
||||
var options = sp.GetRequiredService<IOptions<ScannerStorageOptions>>().Value.ObjectStore;
|
||||
if (!options.IsRustFsDriver())
|
||||
{
|
||||
return new HttpClientHandler();
|
||||
}
|
||||
|
||||
var handler = new HttpClientHandler();
|
||||
if (options.RustFs.AllowInsecureTls)
|
||||
{
|
||||
handler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
|
||||
}
|
||||
|
||||
return handler;
|
||||
});
|
||||
|
||||
services.TryAddSingleton(CreateAmazonS3Client);
|
||||
services.TryAddSingleton<IArtifactObjectStore>(CreateArtifactObjectStore);
|
||||
services.TryAddSingleton<ArtifactStorageService>();
|
||||
}
|
||||
services.TryAddSingleton<LayerRepository>();
|
||||
services.TryAddSingleton<LinkRepository>();
|
||||
services.TryAddSingleton<JobRepository>();
|
||||
services.TryAddSingleton<LifecycleRuleRepository>();
|
||||
services.TryAddSingleton<RuntimeEventRepository>();
|
||||
services.TryAddSingleton<EntryTraceRepository>();
|
||||
services.TryAddSingleton<RubyPackageInventoryRepository>();
|
||||
services.TryAddSingleton<BunPackageInventoryRepository>();
|
||||
services.AddSingleton<IEntryTraceResultStore, EntryTraceResultStore>();
|
||||
services.AddSingleton<IRubyPackageInventoryStore, RubyPackageInventoryStore>();
|
||||
services.AddSingleton<IBunPackageInventoryStore, BunPackageInventoryStore>();
|
||||
|
||||
services.AddHttpClient(RustFsArtifactObjectStore.HttpClientName)
|
||||
.ConfigureHttpClient((sp, client) =>
|
||||
{
|
||||
var options = sp.GetRequiredService<IOptions<ScannerStorageOptions>>().Value.ObjectStore;
|
||||
if (!options.IsRustFsDriver())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Uri.TryCreate(options.RustFs.BaseUrl, UriKind.Absolute, out var baseUri))
|
||||
{
|
||||
throw new InvalidOperationException("RustFS baseUrl must be a valid absolute URI.");
|
||||
}
|
||||
|
||||
client.BaseAddress = baseUri;
|
||||
client.Timeout = options.RustFs.Timeout;
|
||||
|
||||
foreach (var header in options.Headers)
|
||||
{
|
||||
client.DefaultRequestHeaders.TryAddWithoutValidation(header.Key, header.Value);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(options.RustFs.ApiKeyHeader)
|
||||
&& !string.IsNullOrWhiteSpace(options.RustFs.ApiKey))
|
||||
{
|
||||
client.DefaultRequestHeaders.TryAddWithoutValidation(options.RustFs.ApiKeyHeader, options.RustFs.ApiKey);
|
||||
}
|
||||
})
|
||||
.ConfigurePrimaryHttpMessageHandler(sp =>
|
||||
{
|
||||
var options = sp.GetRequiredService<IOptions<ScannerStorageOptions>>().Value.ObjectStore;
|
||||
if (!options.IsRustFsDriver())
|
||||
{
|
||||
return new HttpClientHandler();
|
||||
}
|
||||
|
||||
var handler = new HttpClientHandler();
|
||||
if (options.RustFs.AllowInsecureTls)
|
||||
{
|
||||
handler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
|
||||
}
|
||||
|
||||
return handler;
|
||||
});
|
||||
|
||||
services.TryAddSingleton(CreateAmazonS3Client);
|
||||
services.TryAddSingleton<IArtifactObjectStore>(CreateArtifactObjectStore);
|
||||
services.TryAddSingleton<ArtifactStorageService>();
|
||||
}
|
||||
|
||||
private static IMongoClient CreateMongoClient(IServiceProvider provider)
|
||||
{
|
||||
@@ -149,47 +151,47 @@ public static class ServiceCollectionExtensions
|
||||
return client.GetDatabase(databaseName);
|
||||
}
|
||||
|
||||
private static IAmazonS3 CreateAmazonS3Client(IServiceProvider provider)
|
||||
{
|
||||
var options = provider.GetRequiredService<IOptions<ScannerStorageOptions>>().Value.ObjectStore;
|
||||
var config = new AmazonS3Config
|
||||
{
|
||||
RegionEndpoint = RegionEndpoint.GetBySystemName(options.Region),
|
||||
ForcePathStyle = options.ForcePathStyle,
|
||||
};
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(options.ServiceUrl))
|
||||
{
|
||||
config.ServiceURL = options.ServiceUrl;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(options.AccessKeyId) && !string.IsNullOrWhiteSpace(options.SecretAccessKey))
|
||||
{
|
||||
AWSCredentials credentials = string.IsNullOrWhiteSpace(options.SessionToken)
|
||||
? new BasicAWSCredentials(options.AccessKeyId, options.SecretAccessKey)
|
||||
: new SessionAWSCredentials(options.AccessKeyId, options.SecretAccessKey, options.SessionToken);
|
||||
return new AmazonS3Client(credentials, config);
|
||||
}
|
||||
|
||||
return new AmazonS3Client(config);
|
||||
}
|
||||
|
||||
private static IArtifactObjectStore CreateArtifactObjectStore(IServiceProvider provider)
|
||||
{
|
||||
var options = provider.GetRequiredService<IOptions<ScannerStorageOptions>>();
|
||||
var objectStore = options.Value.ObjectStore;
|
||||
|
||||
if (objectStore.IsRustFsDriver())
|
||||
{
|
||||
return new RustFsArtifactObjectStore(
|
||||
provider.GetRequiredService<IHttpClientFactory>(),
|
||||
options,
|
||||
provider.GetRequiredService<ILogger<RustFsArtifactObjectStore>>());
|
||||
}
|
||||
|
||||
return new S3ArtifactObjectStore(
|
||||
provider.GetRequiredService<IAmazonS3>(),
|
||||
options,
|
||||
provider.GetRequiredService<ILogger<S3ArtifactObjectStore>>());
|
||||
}
|
||||
}
|
||||
private static IAmazonS3 CreateAmazonS3Client(IServiceProvider provider)
|
||||
{
|
||||
var options = provider.GetRequiredService<IOptions<ScannerStorageOptions>>().Value.ObjectStore;
|
||||
var config = new AmazonS3Config
|
||||
{
|
||||
RegionEndpoint = RegionEndpoint.GetBySystemName(options.Region),
|
||||
ForcePathStyle = options.ForcePathStyle,
|
||||
};
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(options.ServiceUrl))
|
||||
{
|
||||
config.ServiceURL = options.ServiceUrl;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(options.AccessKeyId) && !string.IsNullOrWhiteSpace(options.SecretAccessKey))
|
||||
{
|
||||
AWSCredentials credentials = string.IsNullOrWhiteSpace(options.SessionToken)
|
||||
? new BasicAWSCredentials(options.AccessKeyId, options.SecretAccessKey)
|
||||
: new SessionAWSCredentials(options.AccessKeyId, options.SecretAccessKey, options.SessionToken);
|
||||
return new AmazonS3Client(credentials, config);
|
||||
}
|
||||
|
||||
return new AmazonS3Client(config);
|
||||
}
|
||||
|
||||
private static IArtifactObjectStore CreateArtifactObjectStore(IServiceProvider provider)
|
||||
{
|
||||
var options = provider.GetRequiredService<IOptions<ScannerStorageOptions>>();
|
||||
var objectStore = options.Value.ObjectStore;
|
||||
|
||||
if (objectStore.IsRustFsDriver())
|
||||
{
|
||||
return new RustFsArtifactObjectStore(
|
||||
provider.GetRequiredService<IHttpClientFactory>(),
|
||||
options,
|
||||
provider.GetRequiredService<ILogger<RustFsArtifactObjectStore>>());
|
||||
}
|
||||
|
||||
return new S3ArtifactObjectStore(
|
||||
provider.GetRequiredService<IAmazonS3>(),
|
||||
options,
|
||||
provider.GetRequiredService<ILogger<S3ArtifactObjectStore>>());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,14 +16,15 @@ public sealed class MongoCollectionProvider
|
||||
}
|
||||
|
||||
public IMongoCollection<ArtifactDocument> Artifacts => GetCollection<ArtifactDocument>(ScannerStorageDefaults.Collections.Artifacts);
|
||||
public IMongoCollection<ImageDocument> Images => GetCollection<ImageDocument>(ScannerStorageDefaults.Collections.Images);
|
||||
public IMongoCollection<LayerDocument> Layers => GetCollection<LayerDocument>(ScannerStorageDefaults.Collections.Layers);
|
||||
public IMongoCollection<LinkDocument> Links => GetCollection<LinkDocument>(ScannerStorageDefaults.Collections.Links);
|
||||
public IMongoCollection<JobDocument> Jobs => GetCollection<JobDocument>(ScannerStorageDefaults.Collections.Jobs);
|
||||
public IMongoCollection<LifecycleRuleDocument> LifecycleRules => GetCollection<LifecycleRuleDocument>(ScannerStorageDefaults.Collections.LifecycleRules);
|
||||
public IMongoCollection<RuntimeEventDocument> RuntimeEvents => GetCollection<RuntimeEventDocument>(ScannerStorageDefaults.Collections.RuntimeEvents);
|
||||
public IMongoCollection<EntryTraceDocument> EntryTrace => GetCollection<EntryTraceDocument>(ScannerStorageDefaults.Collections.EntryTrace);
|
||||
public IMongoCollection<RubyPackageInventoryDocument> RubyPackages => GetCollection<RubyPackageInventoryDocument>(ScannerStorageDefaults.Collections.RubyPackages);
|
||||
public IMongoCollection<ImageDocument> Images => GetCollection<ImageDocument>(ScannerStorageDefaults.Collections.Images);
|
||||
public IMongoCollection<LayerDocument> Layers => GetCollection<LayerDocument>(ScannerStorageDefaults.Collections.Layers);
|
||||
public IMongoCollection<LinkDocument> Links => GetCollection<LinkDocument>(ScannerStorageDefaults.Collections.Links);
|
||||
public IMongoCollection<JobDocument> Jobs => GetCollection<JobDocument>(ScannerStorageDefaults.Collections.Jobs);
|
||||
public IMongoCollection<LifecycleRuleDocument> LifecycleRules => GetCollection<LifecycleRuleDocument>(ScannerStorageDefaults.Collections.LifecycleRules);
|
||||
public IMongoCollection<RuntimeEventDocument> RuntimeEvents => GetCollection<RuntimeEventDocument>(ScannerStorageDefaults.Collections.RuntimeEvents);
|
||||
public IMongoCollection<EntryTraceDocument> EntryTrace => GetCollection<EntryTraceDocument>(ScannerStorageDefaults.Collections.EntryTrace);
|
||||
public IMongoCollection<RubyPackageInventoryDocument> RubyPackages => GetCollection<RubyPackageInventoryDocument>(ScannerStorageDefaults.Collections.RubyPackages);
|
||||
public IMongoCollection<BunPackageInventoryDocument> BunPackages => GetCollection<BunPackageInventoryDocument>(ScannerStorageDefaults.Collections.BunPackages);
|
||||
|
||||
private IMongoCollection<TDocument> GetCollection<TDocument>(string name)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
using MongoDB.Driver;
|
||||
using StellaOps.Scanner.Storage.Catalog;
|
||||
using StellaOps.Scanner.Storage.Mongo;
|
||||
|
||||
namespace StellaOps.Scanner.Storage.Repositories;
|
||||
|
||||
public sealed class BunPackageInventoryRepository
|
||||
{
|
||||
private readonly MongoCollectionProvider _collections;
|
||||
|
||||
public BunPackageInventoryRepository(MongoCollectionProvider collections)
|
||||
{
|
||||
_collections = collections ?? throw new ArgumentNullException(nameof(collections));
|
||||
}
|
||||
|
||||
public async Task<BunPackageInventoryDocument?> GetAsync(string scanId, CancellationToken cancellationToken)
|
||||
{
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(scanId);
|
||||
return await _collections.BunPackages
|
||||
.Find(x => x.ScanId == scanId)
|
||||
.FirstOrDefaultAsync(cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task UpsertAsync(BunPackageInventoryDocument document, CancellationToken cancellationToken)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(document);
|
||||
var options = new ReplaceOptions { IsUpsert = true };
|
||||
await _collections.BunPackages
|
||||
.ReplaceOneAsync(x => x.ScanId == document.ScanId, document, options, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
namespace StellaOps.Scanner.Storage;
|
||||
|
||||
public static class ScannerStorageDefaults
|
||||
{
|
||||
public const string DefaultDatabaseName = "scanner";
|
||||
namespace StellaOps.Scanner.Storage;
|
||||
|
||||
public static class ScannerStorageDefaults
|
||||
{
|
||||
public const string DefaultDatabaseName = "scanner";
|
||||
public const string DefaultBucketName = "stellaops";
|
||||
public const string DefaultRootPrefix = "scanner";
|
||||
|
||||
@@ -24,9 +24,10 @@ public static class ScannerStorageDefaults
|
||||
public const string RuntimeEvents = "runtime.events";
|
||||
public const string EntryTrace = "entrytrace";
|
||||
public const string RubyPackages = "ruby.packages";
|
||||
public const string BunPackages = "bun.packages";
|
||||
public const string Migrations = "schema_migrations";
|
||||
}
|
||||
|
||||
|
||||
public static class ObjectPrefixes
|
||||
{
|
||||
public const string Layers = "layers";
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
using System.Collections.Immutable;
|
||||
using StellaOps.Scanner.Core.Contracts;
|
||||
using StellaOps.Scanner.Storage.Catalog;
|
||||
using StellaOps.Scanner.Storage.Repositories;
|
||||
|
||||
namespace StellaOps.Scanner.Storage.Services;
|
||||
|
||||
public sealed class BunPackageInventoryStore : IBunPackageInventoryStore
|
||||
{
|
||||
private readonly BunPackageInventoryRepository _repository;
|
||||
|
||||
public BunPackageInventoryStore(BunPackageInventoryRepository repository)
|
||||
{
|
||||
_repository = repository ?? throw new ArgumentNullException(nameof(repository));
|
||||
}
|
||||
|
||||
public async Task StoreAsync(BunPackageInventory inventory, CancellationToken cancellationToken)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(inventory);
|
||||
|
||||
var document = new BunPackageInventoryDocument
|
||||
{
|
||||
ScanId = inventory.ScanId,
|
||||
ImageDigest = inventory.ImageDigest,
|
||||
GeneratedAtUtc = inventory.GeneratedAtUtc.UtcDateTime,
|
||||
Packages = inventory.Packages.Select(ToDocument).ToList()
|
||||
};
|
||||
|
||||
await _repository.UpsertAsync(document, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task<BunPackageInventory?> GetAsync(string scanId, CancellationToken cancellationToken)
|
||||
{
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(scanId);
|
||||
|
||||
var document = await _repository.GetAsync(scanId, cancellationToken).ConfigureAwait(false);
|
||||
if (document is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var generatedAt = DateTime.SpecifyKind(document.GeneratedAtUtc, DateTimeKind.Utc);
|
||||
var packages = document.Packages?.Select(FromDocument).ToImmutableArray()
|
||||
?? ImmutableArray<BunPackageArtifact>.Empty;
|
||||
|
||||
return new BunPackageInventory(
|
||||
document.ScanId,
|
||||
document.ImageDigest ?? string.Empty,
|
||||
new DateTimeOffset(generatedAt),
|
||||
packages);
|
||||
}
|
||||
|
||||
private static BunPackageDocument ToDocument(BunPackageArtifact artifact)
|
||||
{
|
||||
var doc = new BunPackageDocument
|
||||
{
|
||||
Id = artifact.Id,
|
||||
Name = artifact.Name,
|
||||
Version = artifact.Version,
|
||||
Source = artifact.Source,
|
||||
Resolved = artifact.Resolved,
|
||||
Integrity = artifact.Integrity,
|
||||
IsDev = artifact.IsDev,
|
||||
IsDirect = artifact.IsDirect,
|
||||
IsPatched = artifact.IsPatched,
|
||||
Provenance = artifact.Provenance,
|
||||
Metadata = artifact.Metadata is null ? null : new Dictionary<string, string?>(artifact.Metadata, StringComparer.OrdinalIgnoreCase)
|
||||
};
|
||||
|
||||
return doc;
|
||||
}
|
||||
|
||||
private static BunPackageArtifact FromDocument(BunPackageDocument document)
|
||||
{
|
||||
IReadOnlyDictionary<string, string?>? metadata = document.Metadata;
|
||||
|
||||
return new BunPackageArtifact(
|
||||
document.Id,
|
||||
document.Name,
|
||||
document.Version,
|
||||
document.Source,
|
||||
document.Resolved,
|
||||
document.Integrity,
|
||||
document.IsDev,
|
||||
document.IsDirect,
|
||||
document.IsPatched,
|
||||
document.Provenance,
|
||||
metadata);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user