/// <summary> /// Prunes old packages that exceed the maximum version count. /// </summary> /// <param name="id">Id of the package to prune.</param> /// <param name="maxCount">Maximum number of package versions to retain.</param> /// <param name="database">Database to check for registrations.</param> /// <param name="cancellationToken">Token used to cancel the asynchronous operation.</param> /// <returns>Pruned package information objects.</returns> public async Task <IEnumerable <PackageInfo> > PrunePackageAsync(string id, int maxCount, SlimGetContext database, CancellationToken cancellationToken) { var versions = database.PackageVersions.Where(x => x.PackageId == id).OrderByDescending(x => x.NuGetVersion); if (versions.Count() <= maxCount) { return(Enumerable.Empty <PackageInfo>()); } var prunable = versions.Skip(maxCount); var pruned = prunable.Select(x => new PackageInfo(x.PackageId, x.NuGetVersion)); database.RemoveRange(prunable); await database.SaveChangesAsync(cancellationToken).ConfigureAwait(false); return(pruned); }
/// <summary> /// Registers debug symbols with the package database. /// </summary> /// <param name="packageInfo">Metadata of the package to register.</param> /// <param name="database">Database to register to.</param> /// <param name="userId">ID of the user uploading the package.</param> /// <param name="symbolFileNames">Mapping of symbol id to symbol virtual identifiers.</param> /// <param name="cancellationToken">Token used to cancel the asynchronous operation.</param> /// <returns>Symbol registration result.</returns> public async Task <RegisterSymbolResult> RegisterSymbolsAsync(ParsedPackageInfo packageInfo, SlimGetContext database, string userId, IDictionary <SymbolIdentifier, string> symbolFileNames, CancellationToken cancellationToken) { var pkginfo = packageInfo.Info; var pkg = database.Packages.FirstOrDefault(x => x.IdLowercase == pkginfo.IdLowercase); if (pkg == null) { return(RegisterPackageResult.DoesNotExist); } if (pkg.OwnerId != userId) { return(RegisterPackageResult.OwnerMismatch); } if (pkg.Id != pkginfo.Id) { return(RegisterPackageResult.IdMismatch); } var pkgv = database.PackageVersions.FirstOrDefault(x => x.PackageId == pkginfo.Id && x.Version == pkginfo.NormalizedVersion); if (pkgv == null) { return(RegisterPackageResult.DoesNotExist); } var dbsymbols = database.PackageSymbols .Where(x => x.PackageId == pkginfo.Id && x.PackageVersion == pkginfo.NormalizedVersion) .GroupBy(x => x.Identifier) .ToDictionary(x => x.Key, x => x); var regids = new Dictionary <SymbolIdentifier, string>(); foreach (var bin in packageInfo.Binaries.OfType <ParsedIndexedBinarySymbols>()) { var identifier = new SymbolIdentifier(bin.Identifier, bin.Age, bin.Kind); if (!dbsymbols.TryGetValue(bin.Identifier, out var dbsymbolg) || !symbolFileNames.TryGetValue(identifier, out var fnsymbol)) { continue; } var fx = bin.Framework.GetFrameworkString(); var dbsymbol = dbsymbolg.FirstOrDefault(x => x.Framework == fx); if (dbsymbol == null) { continue; } if (dbsymbol.Filename != null) { return(RegisterPackageResult.DuplicateSymbols); } regids[identifier] = bin.Entry; dbsymbol.Name = bin.Name; dbsymbol.Filename = fnsymbol; database.PackageSymbols.Update(dbsymbol); } await database.SaveChangesAsync(cancellationToken).ConfigureAwait(false); return(regids); }
/// <summary> /// Registers a package with the package database. /// </summary> /// <param name="packageInfo">Metadata of the package to register.</param> /// <param name="database">Database to register to.</param> /// <param name="userId">ID of the user uploading the package.</param> /// <param name="packageFileName">Virtual file ID of the package file.</param> /// <param name="manifestFileName">Virtual file ID of the manifest file.</param> /// <param name="cancellationToken">Token used to cancel the asynchronous operation.</param> /// <returns>Registration result.</returns> public async Task <RegisterPackageResult> RegisterPackageAsync(ParsedPackageInfo packageInfo, SlimGetContext database, string userId, string packageFileName, string manifestFileName, CancellationToken cancellationToken) { var pkginfo = packageInfo.Info; var pkg = database.Packages.FirstOrDefault(x => x.IdLowercase == pkginfo.IdLowercase); var pkgv = default(PackageVersion); var result = RegisterPackageResult.VersionCreated; if (pkg != null) { if (pkg.OwnerId != userId) { return(RegisterPackageResult.OwnerMismatch); } if (pkg.Id != pkginfo.Id) { return(RegisterPackageResult.IdMismatch); } pkgv = database.PackageVersions.FirstOrDefault(x => x.PackageId == pkginfo.Id && x.Version == pkginfo.NormalizedVersion); if (pkgv != null) { return(RegisterPackageResult.AlreadyExists); } // package exists, ownership matches, id matches, version does not exist, update and create pkg.Description = packageInfo.Description; pkg.Language = packageInfo.Language; pkg.MinimumClientVersion = packageInfo.MinimumClientVersion; pkg.RequiresLicenseAcceptance = packageInfo.RequireLicenseAcceptance; pkg.Summary = packageInfo.Summary; pkg.Title = packageInfo.Title; pkg.IconUrl = packageInfo.IconUrl; pkg.LicenseUrl = packageInfo.LicenseUrl; pkg.ProjectUrl = packageInfo.ProjectUrl; pkg.RepositoryUrl = packageInfo.RepositoryUrl; pkg.RepositoryType = packageInfo.RepositoryType; pkg.SemVerLevel = packageInfo.SemVerLevel; database.Packages.Update(pkg); var oldAuthors = database.PackageAuthors.Where(x => x.PackageId == pkginfo.Id); database.PackageAuthors.RemoveRange(oldAuthors); var oldTags = database.PackageTags.Where(x => x.PackageId == pkginfo.Id); database.PackageTags.RemoveRange(oldTags); } else { // package does not exist, create it result = RegisterPackageResult.PackageCreated; pkg = new Package { Id = pkginfo.Id, IdLowercase = pkginfo.IdLowercase, Description = packageInfo.Description, DownloadCount = 0, Language = packageInfo.Language, IsListed = true, MinimumClientVersion = packageInfo.MinimumClientVersion, PublishedAt = DateTime.UtcNow, RequiresLicenseAcceptance = packageInfo.RequireLicenseAcceptance, Summary = packageInfo.Summary, Title = packageInfo.Title, IconUrl = packageInfo.IconUrl, LicenseUrl = packageInfo.LicenseUrl, ProjectUrl = packageInfo.ProjectUrl, RepositoryUrl = packageInfo.RepositoryUrl, RepositoryType = packageInfo.RepositoryType, SemVerLevel = packageInfo.SemVerLevel, OwnerId = userId }; await database.Packages.AddAsync(pkg, cancellationToken).ConfigureAwait(false); } foreach (var author in packageInfo.Authors) { await database.PackageAuthors.AddAsync(new PackageAuthor { PackageId = pkginfo.Id, Name = author.Trim() }, cancellationToken).ConfigureAwait(false); } foreach (var tag in packageInfo.Tags) { await database.PackageTags.AddAsync(new PackageTag { PackageId = pkginfo.Id, Tag = tag.Trim() }, cancellationToken).ConfigureAwait(false); } pkgv = new PackageVersion { PackageId = pkginfo.Id, Version = pkginfo.NormalizedVersion, VersionLowercase = pkginfo.NormalizedVersion.ToLowerInvariant(), DownloadCount = 0, IsPrerelase = packageInfo.IsPrerelase, PublishedAt = DateTime.UtcNow, IsListed = true, PackageFilename = packageFileName, ManifestFilename = manifestFileName }; await database.PackageVersions.AddAsync(pkgv, cancellationToken).ConfigureAwait(false); foreach (var fx in packageInfo.Frameworks) { await database.PackageFrameworks.AddAsync(new PackageFramework { PackageId = pkginfo.Id, PackageVersion = pkginfo.NormalizedVersion, Framework = fx.GetFrameworkString() }, cancellationToken).ConfigureAwait(false); } foreach (var dep in packageInfo.Dependencies) { await database.PackageDependencies.AddAsync(new PackageDependency { PackageId = pkginfo.Id, PackageVersion = pkginfo.NormalizedVersion, TargetFramework = dep.Framework.GetFrameworkString(), Id = dep.PackageId, MaxVersion = dep.MaxVersion?.ToNormalizedString(), MinVersion = dep.MinVersion?.ToNormalizedString(), IsMaxVersionInclusive = dep.IsMaxInclusive, IsMinVersionInclusive = dep.IsMinInclusive }, cancellationToken).ConfigureAwait(false); } foreach (var bin in packageInfo.Binaries.OfType <ParsedIndexedBinaryExecutable>()) { await database.PackageBinaries.AddAsync(new PackageBinary { PackageId = pkginfo.Id, PackageVersion = pkginfo.NormalizedVersion, Framework = bin.Framework.GetFrameworkString(), Name = bin.Entry, Length = bin.Length, Hash = bin.Sha256 }, cancellationToken).ConfigureAwait(false); foreach (var symbolId in bin.SymbolIdentifiers) { await database.PackageSymbols.AddAsync(new PackageSymbols { PackageId = pkginfo.Id, PackageVersion = pkginfo.NormalizedVersion, Framework = bin.Framework.GetFrameworkString(), BinaryName = bin.Entry, Identifier = symbolId.Identifier, Age = symbolId.Age, Kind = symbolId.Kind, Signature = $"{symbolId.Identifier.ToString("N").ToUpperInvariant()}{symbolId.Age:x}" }, cancellationToken).ConfigureAwait(false); } } await database.SaveChangesAsync(cancellationToken).ConfigureAwait(false); return(result); }