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());
                }
            }
        }
Exemple #3
0
        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);
        }
Exemple #5
0
        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);
        }