public DisplayPackageViewModel(Package package, User currentUser, IOrderedEnumerable <Package> packageHistory) : this(package, currentUser, (string)null) { HasSemVer2Version = NuGetVersion.IsSemVer2; HasSemVer2Dependency = package.Dependencies.ToList() .Where(pd => !string.IsNullOrEmpty(pd.VersionSpec)) .Select(pd => VersionRange.Parse(pd.VersionSpec)) .Any(p => (p.HasUpperBound && p.MaxVersion.IsSemVer2) || (p.HasLowerBound && p.MinVersion.IsSemVer2)); Dependencies = new DependencySetsViewModel(package.Dependencies); PackageVersions = packageHistory.Select(p => new DisplayPackageViewModel(p, currentUser, GetPushedBy(p, currentUser))); PushedBy = GetPushedBy(package, currentUser); PackageFileSize = package.PackageFileSize; LatestSymbolsPackage = package.LatestSymbolPackage(); if (packageHistory.Any()) { // calculate the number of days since the package registration was created // round to the nearest integer, with a min value of 1 // divide the total download count by this number TotalDaysSinceCreated = Convert.ToInt32(Math.Max(1, Math.Round((DateTime.UtcNow - packageHistory.Min(p => p.Created)).TotalDays))); DownloadsPerDay = TotalDownloadCount / TotalDaysSinceCreated; // for the package DownloadsPerDayLabel = DownloadsPerDay < 1 ? "<1" : DownloadsPerDay.ToNuGetNumberString(); IsDotnetToolPackageType = package.PackageTypes.Any(e => e.Name.Equals("DotnetTool", StringComparison.OrdinalIgnoreCase)); } }
public DisplayPackageViewModel(Package package, User currentUser, string pushedBy) : base(package, currentUser) { Copyright = package.Copyright; DownloadCount = package.DownloadCount; LastEdited = package.LastEdited; TotalDaysSinceCreated = 0; DownloadsPerDay = 0; PushedBy = pushedBy; LatestSymbolsPackage = package.LatestSymbolPackage(); InitializeRepositoryMetadata(package.RepositoryUrl, package.RepositoryType); if (PackageHelper.TryPrepareUrlForRendering(package.ProjectUrl, out string projectUrl)) { ProjectUrl = projectUrl; } if (PackageHelper.TryPrepareUrlForRendering(package.LicenseUrl, out string licenseUrl)) { LicenseUrl = licenseUrl; var licenseNames = package.LicenseNames; if (!string.IsNullOrEmpty(licenseNames)) { LicenseNames = licenseNames.Split(',').Select(l => l.Trim()); } } }
public DisplayPackageViewModel(Package package, User currentUser, PackageDeprecation deprecation) : this(package, currentUser, (string)null) { HasSemVer2Version = NuGetVersion.IsSemVer2; HasSemVer2Dependency = package.Dependencies.ToList() .Where(pd => !string.IsNullOrEmpty(pd.VersionSpec)) .Select(pd => VersionRange.Parse(pd.VersionSpec)) .Any(p => (p.HasUpperBound && p.MaxVersion.IsSemVer2) || (p.HasLowerBound && p.MinVersion.IsSemVer2)); Dependencies = new DependencySetsViewModel(package.Dependencies); var packageHistory = package .PackageRegistration .Packages .OrderByDescending(p => new NuGetVersion(p.Version)) .ToList(); PackageVersions = packageHistory.Select(p => new DisplayPackageViewModel(p, currentUser, GetPushedBy(p, currentUser))).ToList(); PushedBy = GetPushedBy(package, currentUser); PackageFileSize = package.PackageFileSize; LatestSymbolsPackage = package.LatestSymbolPackage(); LatestAvailableSymbolsPackage = LatestSymbolsPackage != null && LatestSymbolsPackage.StatusKey == PackageStatus.Available ? LatestSymbolsPackage : package.LatestAvailableSymbolPackage(); if (packageHistory.Any()) { // calculate the number of days since the package registration was created // round to the nearest integer, with a min value of 1 // divide the total download count by this number TotalDaysSinceCreated = Convert.ToInt32(Math.Max(1, Math.Round((DateTime.UtcNow - packageHistory.Min(p => p.Created)).TotalDays))); DownloadsPerDay = TotalDownloadCount / TotalDaysSinceCreated; // for the package DownloadsPerDayLabel = DownloadsPerDay < 1 ? "<1" : DownloadsPerDay.ToNuGetNumberString(); IsDotnetToolPackageType = package.PackageTypes.Any(e => e.Name.Equals("DotnetTool", StringComparison.OrdinalIgnoreCase)); } if (deprecation != null) { AlternatePackageId = deprecation.AlternatePackageRegistration?.Id; var alternatePackage = deprecation.AlternatePackage; if (alternatePackage != null) { // A deprecation should not have both an alternate package registration and an alternate package. // In case a deprecation does have both, we will hide the alternate package registration's ID in this model. AlternatePackageId = alternatePackage?.Id; AlternatePackageVersion = alternatePackage?.Version; } CustomMessage = deprecation.CustomMessage; } }
private DisplayPackageViewModel SetupInternal( DisplayPackageViewModel viewModel, Package package, User currentUser, PackageDeprecation deprecation, string readMeHtml) { viewModel.HasSemVer2Version = viewModel.NuGetVersion.IsSemVer2; viewModel.HasSemVer2Dependency = package.Dependencies.ToList() .Where(pd => !string.IsNullOrEmpty(pd.VersionSpec)) .Select(pd => VersionRange.Parse(pd.VersionSpec)) .Any(p => (p.HasUpperBound && p.MaxVersion.IsSemVer2) || (p.HasLowerBound && p.MinVersion.IsSemVer2)); viewModel.Dependencies = new DependencySetsViewModel(package.Dependencies); var packageHistory = package .PackageRegistration .Packages .OrderByDescending(p => new NuGetVersion(p.Version)) .ToList(); var pushedByCache = new Dictionary <User, string>(); viewModel.PackageVersions = packageHistory .Select( p => { var vm = new DisplayPackageViewModel(); _listPackageItemViewModelFactory.Setup(vm, p, currentUser); return(SetupCommon(vm, p, GetPushedBy(p, currentUser, pushedByCache))); }) .ToList(); viewModel.PushedBy = GetPushedBy(package, currentUser, pushedByCache); viewModel.PackageFileSize = package.PackageFileSize; viewModel.LatestSymbolsPackage = package.LatestSymbolPackage(); viewModel.LatestAvailableSymbolsPackage = viewModel.LatestSymbolsPackage != null && viewModel.LatestSymbolsPackage.StatusKey == PackageStatus.Available ? viewModel.LatestSymbolsPackage : package.LatestAvailableSymbolPackage(); if (packageHistory.Any()) { // calculate the number of days since the package registration was created // round to the nearest integer, with a min value of 1 // divide the total download count by this number viewModel.TotalDaysSinceCreated = Convert.ToInt32(Math.Max(1, Math.Round((DateTime.UtcNow - packageHistory.Min(p => p.Created)).TotalDays))); viewModel.DownloadsPerDay = viewModel.TotalDownloadCount / viewModel.TotalDaysSinceCreated; // for the package viewModel.DownloadsPerDayLabel = viewModel.DownloadsPerDay < 1 ? "<1" : viewModel.DownloadsPerDay.ToNuGetNumberString(); viewModel.IsDotnetToolPackageType = package.PackageTypes.Any(e => e.Name.Equals("DotnetTool", StringComparison.OrdinalIgnoreCase)); } if (deprecation != null) { viewModel.AlternatePackageId = deprecation.AlternatePackageRegistration?.Id; var alternatePackage = deprecation.AlternatePackage; if (alternatePackage != null) { // A deprecation should not have both an alternate package registration and an alternate package. // In case a deprecation does have both, we will hide the alternate package registration's ID in this model. viewModel.AlternatePackageId = alternatePackage?.Id; viewModel.AlternatePackageVersion = alternatePackage?.Version; } viewModel.CustomMessage = deprecation.CustomMessage; } viewModel.ReadMeHtml = readMeHtml; viewModel.HasEmbeddedIcon = package.HasEmbeddedIcon; return(viewModel); }
private DisplayPackageViewModel SetupInternal( DisplayPackageViewModel viewModel, Package package, IReadOnlyCollection <Package> allVersions, User currentUser, IReadOnlyDictionary <int, PackageDeprecation> packageKeyToDeprecation, IReadOnlyList <PackageRename> packageRenames, RenderedReadMeResult readmeResult) { var dependencies = package.Dependencies.ToList(); viewModel.Dependencies = new DependencySetsViewModel(dependencies); var packageHistory = allVersions .OrderByDescending(p => new NuGetVersion(p.Version)) .ToList(); var pushedByCache = new Dictionary <User, string>(); viewModel.PackageVersions = packageHistory .Select( p => { var vm = new DisplayPackageViewModel(); _listPackageItemViewModelFactory.Setup(vm, p, currentUser); return(SetupCommon(vm, p, GetPushedBy(p, currentUser, pushedByCache), packageKeyToDeprecation)); }) .ToList(); viewModel.PushedBy = GetPushedBy(package, currentUser, pushedByCache); viewModel.PackageFileSize = package.PackageFileSize; viewModel.LatestSymbolsPackage = package.LatestSymbolPackage(); viewModel.LatestAvailableSymbolsPackage = viewModel.LatestSymbolsPackage != null && viewModel.LatestSymbolsPackage.StatusKey == PackageStatus.Available ? viewModel.LatestSymbolsPackage : package.LatestAvailableSymbolPackage(); if (packageHistory.Any()) { // calculate the number of days since the package registration was created // round to the nearest integer, with a min value of 1 // divide the total download count by this number viewModel.TotalDaysSinceCreated = Convert.ToInt32(Math.Max(1, Math.Round((DateTime.UtcNow - packageHistory.Min(p => p.Created)).TotalDays))); viewModel.DownloadsPerDay = viewModel.TotalDownloadCount / viewModel.TotalDaysSinceCreated; // for the package viewModel.DownloadsPerDayLabel = viewModel.DownloadsPerDay < 1 ? "<1" : viewModel.DownloadsPerDay.ToNuGetNumberString(); // Lazily load the package types from the database. viewModel.IsDotnetToolPackageType = package.PackageTypes.Any(e => e.Name.Equals("DotnetTool", StringComparison.OrdinalIgnoreCase)); viewModel.IsDotnetNewTemplatePackageType = package.PackageTypes.Any(e => e.Name.Equals("Template", StringComparison.OrdinalIgnoreCase)); } if (packageKeyToDeprecation != null && packageKeyToDeprecation.TryGetValue(package.Key, out var deprecation)) { viewModel.AlternatePackageId = deprecation.AlternatePackageRegistration?.Id; var alternatePackage = deprecation.AlternatePackage; if (alternatePackage != null) { // A deprecation should not have both an alternate package registration and an alternate package. // In case a deprecation does have both, we will hide the alternate package registration's ID in this model. viewModel.AlternatePackageId = alternatePackage?.Id; viewModel.AlternatePackageVersion = alternatePackage?.Version; } viewModel.CustomMessage = deprecation.CustomMessage; } if (packageRenames != null && packageRenames.Count != 0) { viewModel.PackageRenames = packageRenames; if (package.PackageRegistration?.RenamedMessage != null) { viewModel.RenamedMessage = package.PackageRegistration.RenamedMessage; } } viewModel.ReadMeHtml = readmeResult?.Content; viewModel.ReadMeImagesRewritten = readmeResult != null ? readmeResult.ImagesRewritten : false; viewModel.HasEmbeddedIcon = package.HasEmbeddedIcon; return(viewModel); }
/// <summary> /// This method creates the symbol db entities and invokes the validations for the uploaded snupkg. /// It will send the message for validation and upload the snupkg to the "validations"/"symbols-packages" container /// based on the result. It will then update the references in the database for persistence with appropriate status. /// </summary> /// <param name="package">The package for which symbols package is to be uplloaded</param> /// <param name="symbolPackageStream">The symbols package stream metadata for the uploaded symbols package file.</param> /// <returns>The <see cref="PackageCommitResult"/> for the create and upload symbol package flow.</returns> public async Task <PackageCommitResult> CreateAndUploadSymbolsPackage(Package package, Stream symbolPackageStream) { var packageStreamMetadata = new PackageStreamMetadata { HashAlgorithm = CoreConstants.Sha512HashAlgorithmId, Hash = CryptographyService.GenerateHash( symbolPackageStream.AsSeekableStream(), CoreConstants.Sha512HashAlgorithmId), Size = symbolPackageStream.Length }; Stream symbolPackageFile = symbolPackageStream.AsSeekableStream(); var previousSymbolsPackage = package.LatestSymbolPackage(); var symbolPackage = _symbolPackageService.CreateSymbolPackage(package, packageStreamMetadata); await _validationService.StartValidationAsync(symbolPackage); if (symbolPackage.StatusKey != PackageStatus.Available && symbolPackage.StatusKey != PackageStatus.Validating) { throw new InvalidOperationException( $"The symbol package to commit must have either the {PackageStatus.Available} or {PackageStatus.Validating} package status."); } try { if (symbolPackage.StatusKey == PackageStatus.Validating) { // If the last uploaded symbol package has failed validation, it will leave the snupkg in the // validations container. We could possibly overwrite it, but that might introduce a concurrency // issue on multiple snupkg uploads with a prior failed validation. The best thing to do would be // to delete the failed validation snupkg from validations container and then proceed with normal // upload. if (previousSymbolsPackage != null && previousSymbolsPackage.StatusKey == PackageStatus.FailedValidation) { await DeleteSymbolsPackageAsync(previousSymbolsPackage); } await _symbolPackageFileService.SaveValidationPackageFileAsync(symbolPackage.Package, symbolPackageFile); } else if (symbolPackage.StatusKey == PackageStatus.Available) { if (!symbolPackage.Published.HasValue) { symbolPackage.Published = DateTime.UtcNow; } // Mark any other associated available symbol packages for deletion. var availableSymbolPackages = package .SymbolPackages .Where(sp => sp.StatusKey == PackageStatus.Available && sp != symbolPackage); var overwrite = false; if (availableSymbolPackages.Any()) { // Mark the currently available packages for deletion, and replace the file in the container. foreach (var availableSymbolPackage in availableSymbolPackages) { availableSymbolPackage.StatusKey = PackageStatus.Deleted; } overwrite = true; } // Caveat: This doesn't really affect our prod flow since the package is validating, however, when the async validation // is disabled there is a chance that there could be concurrency issues when pushing multiple symbols simultaneously. // This could result in an inconsistent data or multiple symbol entities marked as available. This could be sovled using etag // for saving files, however since it doesn't really affect nuget.org which happen have async validations flow I will leave it as is. await _symbolPackageFileService.SavePackageFileAsync(symbolPackage.Package, symbolPackageFile, overwrite); } try { // commit all changes to database as an atomic transaction await _entitiesContext.SaveChangesAsync(); } catch (Exception ex) { ex.Log(); // If saving to the DB fails for any reason we need to delete the package we just saved. if (symbolPackage.StatusKey == PackageStatus.Validating) { await _symbolPackageFileService.DeleteValidationPackageFileAsync( package.PackageRegistration.Id, package.Version); } else if (symbolPackage.StatusKey == PackageStatus.Available) { await _symbolPackageFileService.DeletePackageFileAsync( package.PackageRegistration.Id, package.Version); } throw ex; } } catch (FileAlreadyExistsException ex) { ex.Log(); return(PackageCommitResult.Conflict); } _telemetryService.TrackSymbolPackagePushEvent(package.Id, package.NormalizedVersion); return(PackageCommitResult.Success); }