public async Task <IActionResult> Get(string id, string version, CancellationToken cancellationToken) { if (!NuGetVersion.TryParse(version, out var nugetVersion)) { return(NotFound()); } // Allow read-through caching to happen if it is confiured. await _mirror.MirrorAsync(id, nugetVersion, cancellationToken); var package = await _packages.FindAsync(id, nugetVersion); if (package == null) { return(NotFound()); } // Documentation: https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource var result = new RegistrationLeaf( registrationUri: Url.PackageRegistration(id, nugetVersion), listed: package.Listed, downloads: package.Downloads, packageContentUri: Url.PackageDownload(id, nugetVersion), published: package.Published, registrationIndexUri: Url.PackageRegistration(id)); return(Json(result)); }
public async Task <IActionResult> Get(string id, string version, CancellationToken cancellationToken) { if (!NuGetVersion.TryParse(version, out var nugetVersion)) { return(NotFound()); } // Allow read-through caching to happen if it is configured. await _mirror.MirrorAsync(id, nugetVersion, cancellationToken); var package = await _packages.FindOrNullAsync(id, nugetVersion, includeUnlisted : true); if (package == null) { return(NotFound()); } var result = new RegistrationLeaf( type: RegistrationLeaf.DefaultType, registrationUri: Url.PackageRegistration(id, nugetVersion), listed: package.Listed, downloads: package.Downloads, packageContentUrl: Url.PackageDownload(id, nugetVersion), published: package.Published, registrationIndexUrl: Url.PackageRegistration(id)); return(Json(result)); }
public async Task WriteLeafAsync( HiveType hive, IReadOnlyList <HiveType> replicaHives, string id, NuGetVersion version, RegistrationLeaf leaf) { var path = _urlBuilder.GetLeafPath(id, version); await WriteAsync(hive, replicaHives, path, leaf, "leaf", _entityBuilder.UpdateLeafUrls); }
public async Task <IActionResult> Get(string id, string version) { if (!NuGetVersion.TryParse(version, out var nugetVersion)) { return(NotFound()); } var package = await _packages.FindAsync(id, nugetVersion); if (package == null) { return(NotFound()); } // Documentation: https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource var result = new RegistrationLeaf( registrationUri: Url.PackageRegistration(id, nugetVersion), listed: package.Listed, packageContentUri: Url.PackageDownload(id, nugetVersion), published: package.Published, registrationIndexUri: Url.PackageRegistration(id)); return(Json(result)); }
public Facts(ITestOutputHelper output) { CloudBlobClient = new Mock <ICloudBlobClient>(); EntityBuilder = new Mock <IEntityBuilder>(); Throttle = new Mock <IThrottle>(); Options = new Mock <IOptionsSnapshot <Catalog2RegistrationConfiguration> >(); Logger = output.GetLogger <HiveStorage>(); Config = new Catalog2RegistrationConfiguration { LegacyBaseUrl = "https://example/reg/", LegacyStorageContainer = "reg", GzippedBaseUrl = "https://example/reg-gz/", GzippedStorageContainer = "reg-gz", SemVer2BaseUrl = "https://example/reg-gz-semver2/", SemVer2StorageContainer = "reg-gz-semver2", EnsureSingleSnapshot = false, }; LegacyContainer = new Mock <ICloudBlobContainer>(); GzippedContainer = new Mock <ICloudBlobContainer>(); SemVer2Container = new Mock <ICloudBlobContainer>(); LegacyBlob = new Mock <ISimpleCloudBlob>(); GzippedBlob = new Mock <ISimpleCloudBlob>(); SemVer2Blob = new Mock <ISimpleCloudBlob>(); LegacyStream = new MemoryStream(); GzippedStream = new MemoryStream(); SemVer2Stream = new MemoryStream(); LegacySegment = new Mock <ISimpleBlobResultSegment>(); Hive = HiveType.Legacy; ReplicaHives = new List <HiveType>(); Id = "NuGet.Versioning"; Index = new RegistrationIndex(); Page = new RegistrationPage(); Leaf = new RegistrationLeaf(); Options.Setup(x => x.Value).Returns(() => Config); CloudBlobClient.Setup(x => x.GetContainerReference(Config.LegacyStorageContainer)).Returns(() => LegacyContainer.Object); CloudBlobClient.Setup(x => x.GetContainerReference(Config.GzippedStorageContainer)).Returns(() => GzippedContainer.Object); CloudBlobClient.Setup(x => x.GetContainerReference(Config.SemVer2StorageContainer)).Returns(() => SemVer2Container.Object); LegacyContainer.Setup(x => x.GetBlobReference(It.IsAny <string>())).Returns(() => LegacyBlob.Object); GzippedContainer.Setup(x => x.GetBlobReference(It.IsAny <string>())).Returns(() => GzippedBlob.Object); SemVer2Container.Setup(x => x.GetBlobReference(It.IsAny <string>())).Returns(() => SemVer2Blob.Object); LegacyBlob.Setup(x => x.Properties).Returns(new BlobProperties()); LegacyBlob.Setup(x => x.OpenReadAsync(It.IsAny <AccessCondition>())).ReturnsAsync(() => LegacyStream); LegacyBlob.Setup(x => x.Uri).Returns(new Uri("https://example/reg/something.json")); LegacyBlob .Setup(x => x.UploadFromStreamAsync(It.IsAny <Stream>(), It.IsAny <AccessCondition>())) .Returns(Task.CompletedTask) .Callback <Stream, AccessCondition>((s, _) => s.CopyTo(LegacyStream)); LegacyBlob.Setup(x => x.ExistsAsync()).ReturnsAsync(true); LegacyContainer .Setup(x => x.ListBlobsSegmentedAsync( It.IsAny <string>(), It.IsAny <bool>(), It.IsAny <BlobListingDetails>(), It.IsAny <int?>(), It.IsAny <BlobContinuationToken>(), It.IsAny <BlobRequestOptions>(), It.IsAny <OperationContext>(), It.IsAny <CancellationToken>())) .Returns(() => Task.FromResult(LegacySegment.Object)); LegacySegment.Setup(x => x.Results).Returns(new List <ISimpleCloudBlob>()); GzippedBlob.Setup(x => x.Properties).Returns(new BlobProperties()); GzippedBlob.Setup(x => x.OpenReadAsync(It.IsAny <AccessCondition>())).ReturnsAsync(() => GzippedStream); GzippedBlob.Setup(x => x.Uri).Returns(new Uri("https://example/reg-gz/something.json")); GzippedBlob .Setup(x => x.UploadFromStreamAsync(It.IsAny <Stream>(), It.IsAny <AccessCondition>())) .Returns(Task.CompletedTask) .Callback <Stream, AccessCondition>((s, _) => s.CopyTo(GzippedStream)); GzippedBlob.Setup(x => x.ExistsAsync()).ReturnsAsync(true); SemVer2Blob.Setup(x => x.Properties).Returns(new BlobProperties()); SemVer2Blob.Setup(x => x.OpenReadAsync(It.IsAny <AccessCondition>())).ReturnsAsync(() => SemVer2Stream); SemVer2Blob.Setup(x => x.Uri).Returns(new Uri("https://example/reg-gz-semver2/something.json")); SemVer2Blob .Setup(x => x.UploadFromStreamAsync(It.IsAny <Stream>(), It.IsAny <AccessCondition>())) .Returns(Task.CompletedTask) .Callback <Stream, AccessCondition>((s, _) => s.CopyTo(SemVer2Stream)); SemVer2Blob.Setup(x => x.ExistsAsync()).ReturnsAsync(true); SerializeToStream(LegacyStream, new Dictionary <string, string> { { "@id", LegacyBlob.Object.Uri.AbsoluteUri } }); SerializeToStream(GzippedStream, new Dictionary <string, string> { { "@id", GzippedBlob.Object.Uri.AbsoluteUri } }); SerializeToStream(SemVer2Stream, new Dictionary <string, string> { { "@id", SemVer2Blob.Object.Uri.AbsoluteUri } }); Target = new HiveStorage( CloudBlobClient.Object, new RegistrationUrlBuilder(Options.Object), EntityBuilder.Object, Throttle.Object, Options.Object, Logger); }
public RegistrationIndexModule(IPackageService packageService) { _packages = packageService ?? throw new ArgumentNullException(nameof(packageService)); this.Get("v3/registration/{id}/index.json", async(req, res, routeData) => { string id = routeData.As <string>("id"); // Documentation: https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource var packages = await _packages.FindAsync(id, includeUnlisted: false, includeDependencies: true); var versions = packages.Select(p => p.Version).ToList(); if (!packages.Any()) { res.StatusCode = 404; return; } // TODO: Paging of registration items. // "Un-paged" example: https://api.nuget.org/v3/registration3/newtonsoft.json/index.json // Paged example: https://api.nuget.org/v3/registration3/fake/index.json await res.AsJson(new { Count = packages.Count, TotalDownloads = packages.Sum(p => p.Downloads), Items = new[] { new RegistrationIndexItem( packageId: id, items: packages.Select(p => ToRegistrationIndexLeaf(req, p)).ToList(), lower: versions.Min().ToNormalizedString(), upper: versions.Max().ToNormalizedString() ), } }); }); this.Get("v3/registration/{id}/{version}.json", async(req, res, routeData) => { string id = routeData.As <string>("id"); string version = routeData.As <string>("version"); if (!NuGetVersion.TryParse(version, out var nugetVersion)) { res.StatusCode = 400; return; } var package = await _packages.FindAsync(id, nugetVersion, false, includeDependencies: false); if (package == null) { res.StatusCode = 404; return; } // Documentation: https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource var result = new RegistrationLeaf( registrationUri: req.PackageRegistration(id, nugetVersion, ""), listed: package.Listed, downloads: package.Downloads, packageContentUri: req.PackageDownload(id, nugetVersion, ""), published: package.Published, registrationIndexUri: req.PackageRegistration(id, "")); await res.AsJson(result); }); }
public void UpdateLeafUrls(RegistrationLeaf leaf, HiveType fromHive, HiveType toHive) { leaf.Url = _urlBuilder.ConvertHive(fromHive, toHive, leaf.Url); leaf.Registration = _urlBuilder.ConvertHive(fromHive, toHive, leaf.Registration); }
public CacheRegistrationIndexModule(IMirrorService mirror) { this._mirror = mirror ?? throw new ArgumentNullException(nameof(mirror)); this.Get("cache/v3/registration/{id}/index.json", async(req, res, routeData) => { string id = routeData.As <string>("id"); // Documentation: https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource var upstreamPackages = (await _mirror.FindUpstreamMetadataAsync(id, CancellationToken.None)).ToList(); var versions = upstreamPackages.Select(p => p.Identity.Version).ToList(); if (!upstreamPackages.Any()) { res.StatusCode = 404; return; } // TODO: Paging of registration items. // "Un-paged" example: https://api.nuget.org/v3/registration3/newtonsoft.json/index.json // Paged example: https://api.nuget.org/v3/registration3/fake/index.json await res.AsJson(new { Count = upstreamPackages.Count, TotalDownloads = upstreamPackages.Sum(p => p.DownloadCount), Items = new[] { new RegistrationIndexItem( packageId: id, items: upstreamPackages.Select(p => ToRegistrationIndexLeaf(req, p)).ToList(), lower: versions.Min().ToNormalizedString(), upper: versions.Max().ToNormalizedString() ), } }); }); this.Get("cache/v3/registration/{id}/{version}.json", async(req, res, routeData) => { string id = routeData.As <string>("id"); string version = routeData.As <string>("version"); if (!NuGetVersion.TryParse(version, out var nugetVersion)) { res.StatusCode = 400; return; } // Allow read-through caching to happen if it is confiured. await _mirror.MirrorAsync(id, nugetVersion, CancellationToken.None); var package = await _mirror.FindAsync(new PackageIdentity(id, nugetVersion)); if (package == null) { res.StatusCode = 404; return; } // Documentation: https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource var result = new RegistrationLeaf( registrationUri: req.PackageRegistration(id, nugetVersion, "cache"), listed: package.IsListed, downloads: package.DownloadCount.GetValueOrDefault(), packageContentUri: req.PackageDownload(id, nugetVersion, "cache"), published: package.Published.GetValueOrDefault(), registrationIndexUri: req.PackageRegistration(id, "cache")); await res.AsJson(result); }); }
public Facts(ITestOutputHelper output) { Storage = new Mock <IHiveStorage>(); Merger = new Mock <IHiveMerger>(); EntityBuilder = new Mock <IEntityBuilder>(); Options = new Mock <IOptionsSnapshot <Catalog2RegistrationConfiguration> >(); Logger = output.GetLogger <HiveUpdater>(); Config = new Catalog2RegistrationConfiguration(); Hive = HiveType.SemVer2; ReplicaHives = new List <HiveType>(); Id = "NuGet.Versioning"; Entries = new List <CatalogCommitItem>(); EntryToCatalogLeaf = new Dictionary <CatalogCommitItem, PackageDetailsCatalogLeaf>( ReferenceEqualityComparer <CatalogCommitItem> .Default); RegistrationIndex = new RegistrationIndex { Items = new List <RegistrationPage> { new RegistrationPage { Lower = "1.0.0", Upper = "3.0.0", Count = 2, Items = new List <RegistrationLeafItem> { new RegistrationLeafItem { Url = $"https://example/reg/{Id.ToLowerInvariant()}/1.0.0.json", CatalogEntry = new RegistrationCatalogEntry { Version = "1.0.0", } }, new RegistrationLeafItem { Url = $"https://example/reg/{Id.ToLowerInvariant()}/3.0.0.json", CatalogEntry = new RegistrationCatalogEntry { Version = "3.0.0", } }, } } } }; MergeResult = new HiveMergeResult( new HashSet <PageInfo>(), new HashSet <LeafInfo>(), new HashSet <LeafInfo>()); RegistrationLeaf = new RegistrationLeaf(); RegistrationCommit = new CatalogCommit( "b580f835-f041-4361-aa46-57e5dc338a63", new DateTimeOffset(2019, 10, 25, 0, 0, 0, TimeSpan.Zero)); Options.Setup(x => x.Value).Returns(() => Config); Storage .Setup(x => x.ReadIndexOrNullAsync(It.IsAny <HiveType>(), It.IsAny <string>())) .ReturnsAsync(() => RegistrationIndex); var concreteHiveMerger = new HiveMerger(Options.Object, output.GetLogger <HiveMerger>()); Merger .Setup(x => x.MergeAsync(It.IsAny <IndexInfo>(), It.IsAny <IReadOnlyList <CatalogCommitItem> >())) .Returns <IndexInfo, IReadOnlyList <CatalogCommitItem> >((i, e) => concreteHiveMerger.MergeAsync(i, e)); EntityBuilder .Setup(x => x.NewLeaf(It.IsAny <RegistrationLeafItem>())) .Returns(() => RegistrationLeaf); EntityBuilder .Setup(x => x.UpdateNonInlinedPageItem( It.IsAny <RegistrationPage>(), It.IsAny <HiveType>(), It.IsAny <string>(), It.IsAny <int>(), It.IsAny <NuGetVersion>(), It.IsAny <NuGetVersion>())) .Callback <RegistrationPage, HiveType, string, int, NuGetVersion, NuGetVersion>((p, h, id, c, l, u) => { p.Url = $"https://example/reg/" + $"{id.ToLowerInvariant()}/" + $"{l.ToNormalizedString().ToLowerInvariant()}/" + $"{u.ToNormalizedString().ToLowerInvariant()}.json"; }); Target = new HiveUpdater( Storage.Object, Merger.Object, EntityBuilder.Object, Options.Object, Logger); }
/// <summary> /// Handle the logic to index a package from the <paramref name="nuspecReader"/> and store information in the <paramref name="registrationIndex"/> if provided or create a new entry /// </summary> /// <param name="nuspecReader">The nuspecReader to be used to read package information</param> /// <param name="registrationIndex">The <see cref="RegistrationResult"/> instance which should be used to create new version if package id already exists</param> /// <returns>The <paramref name="registrationIndex"/> instance of provided or a new one</returns> protected virtual async Task <RegistrationResult> IndexPackageCore(INuspecCoreReader nuspecReader, RegistrationResult registrationIndex) { bool isNewPackageId = false; var metadata = nuspecReader.GetMetadata(); string version = nuspecReader.GetVersion().ToNormalizedString(); string packageRegistrationBaseUrl = $"{registrationServiceUrl + nuspecReader.GetId()}/index.json"; if (registrationIndex == null) { registrationIndex = new RegistrationResult() { Id = packageRegistrationBaseUrl }; isNewPackageId = true; } RegistrationPage registrationPage = registrationIndex.Items.FirstOrDefault(); if (registrationPage == null) { registrationPage = new RegistrationPage(); registrationIndex.Items.Add(registrationPage); } bool versionExists = this.IsVersionAlreadyExisting(nuspecReader.GetId(), version); if (versionExists) { throw new PackageVersionAlreadyExistsException($"The version {version} already exists for package {nuspecReader.GetId()}"); } var packageSummary = new NuGetPackageSummary() { PackageMetadataUrl = registrationServiceUrl + nuspecReader.GetId() + "/index.json", Id = nuspecReader.GetId(), Version = nuspecReader.GetVersion().ToFullString() }; await this.AddNewVersion(packageSummary.Id, new NuGetPackageVersion() { PackageMetadataUrl = registrationServiceUrl + nuspecReader.GetId() + "/" + version, Version = version, Downloads = 0 }); foreach (var m in metadata) { if ("title".Equals(m.Key, StringComparison.InvariantCultureIgnoreCase)) { packageSummary.Title = m.Value; } else if ("description".Equals(m.Key, StringComparison.InvariantCultureIgnoreCase)) { packageSummary.Description = m.Value; } else if ("authors".Equals(m.Key, StringComparison.InvariantCultureIgnoreCase) && !string.IsNullOrEmpty(m.Value)) { packageSummary.Authors = m.Value.Split(","); } else if ("owners".Equals(m.Key, StringComparison.InvariantCultureIgnoreCase) && !string.IsNullOrEmpty(m.Value)) { packageSummary.Owners = m.Value.Split(","); } else if ("requireLicenseAcceptance".Equals(m.Key, StringComparison.InvariantCultureIgnoreCase)) { packageSummary.RequireLicenseAcceptance = bool.Parse(m.Value); } else if ("licenseUrl".Equals(m.Key, StringComparison.InvariantCultureIgnoreCase)) { packageSummary.LicenseUrl = m.Value; } else if ("projectUrl".Equals(m.Key, StringComparison.InvariantCultureIgnoreCase)) { packageSummary.ProjectUrl = m.Value; } else if ("iconUrl".Equals(m.Key, StringComparison.InvariantCultureIgnoreCase)) { packageSummary.IconUrl = m.Value; } else if ("copyright".Equals(m.Key, StringComparison.InvariantCultureIgnoreCase)) { packageSummary.Copyright = m.Value; } else if ("tags".Equals(m.Key, StringComparison.InvariantCultureIgnoreCase)) { packageSummary.Tags = m.Value; } } RegistrationLeaf registrationLeaf = new RegistrationLeaf(); registrationLeaf.CatalogEntry = packageSummary; registrationLeaf.PackageContent = $"{this.packageContentServiceUrl}{nuspecReader.GetId()}/{version}/{nuspecReader.GetId()}.{version}.nupkg"; var versions = await this.GetAllVersions(packageSummary.Id).ConfigureAwait(false); string lowerVersion = versions.First(); string upperVersion = versions.Last(); registrationPage.Id = $"{registrationServiceUrl + nuspecReader.GetId()}/index.json/#page/{lowerVersion}/{upperVersion}"; registrationPage.Items.Add(registrationLeaf); registrationPage.Lower = lowerVersion; registrationPage.Upper = upperVersion; if (isNewPackageId) { await this.AddRegistrationResult(registrationIndex).ConfigureAwait(false); } return(registrationIndex); }