private void UpdatePackageMetadataStore(NapackMetadata package, NapackVersionIdentifier nextVersion, NapackAnalyst.UpversionType upversionType, NewNapackVersion newNapackVersion) { if (upversionType == NapackAnalyst.UpversionType.Major) { NapackMajorVersionMetadata newMajorVersionMetadata = new NapackMajorVersionMetadata() { Recalled = false, Versions = new Dictionary <int, List <int> > { [0] = new List <int> { 0 } }, License = newNapackVersion.License }; package.Versions.Add(nextVersion.Major, newMajorVersionMetadata); } else if (upversionType == NapackAnalyst.UpversionType.Minor) { package.Versions[nextVersion.Major].Versions.Add(nextVersion.Minor, new List <int> { 0 }); } else { package.Versions[nextVersion.Major].Versions[nextVersion.Minor].Add(nextVersion.Patch); } statsStore[nextVersion.NapackName].AddVersion(newNapackVersion); searchIndices[nextVersion.NapackName].LastUpdateTime = statsStore[nextVersion.NapackName].LastUpdateTime; searchIndices[nextVersion.NapackName].LastUsedLicense = newNapackVersion.License; packageMetadataStore[nextVersion.NapackName] = package; }
/// <summary> /// Creates and saves a new napack. /// </summary> /// <param name="napackName">The name of the napack</param> /// <param name="newNapack">The new napack to create/save</param> /// <param name="napackSpec">The napack specification</param> public void SaveNewNapack(string napackName, NewNapack newNapack, NapackSpec napackSpec) { NapackVersionIdentifier version = new NapackVersionIdentifier(napackName, 1, 0, 0); NapackMetadata metadata = NapackMetadata.CreateFromNewNapack(napackName, newNapack); NapackVersion packageVersion = NapackVersion.CreateFromNewNapack(newNapack.NewNapackVersion); foreach (string author in newNapack.NewNapackVersion.Authors) { AddAuthorConsumption(author, version); } foreach (string userId in newNapack.metadata.AuthorizedUserIds) { AddUserAuthorization(userId, napackName); } foreach (NapackMajorVersion consumedPackage in newNapack.NewNapackVersion.Dependencies) { AddConsumingPackage(consumedPackage, version); } // Add the new napack to all the various stores. NapackStats stats = new NapackStats(); stats.AddVersion(newNapack.NewNapackVersion); searchIndices.Add(version.NapackName, NapackSearchIndex.CreateFromMetadataAndStats(metadata, stats)); statsStore.Add(version.NapackName, stats); packageMetadataStore.Add(version.NapackName, metadata); packageStore.Add(version.GetFullName(), packageVersion); specStore.Add(version.GetFullName(), napackSpec); }
public static Response DeleteUser(IResponseFormatter responseFormatter, UserIdentifier user) { UserIdentifier storedUser = Global.NapackStorageManager.GetUser(user.Email); IEnumerable <string> authorizedPackages = Global.NapackStorageManager.GetAuthorizedPackages(storedUser.Email); List <string> orphanedPackages = new List <string>(); foreach (string authorizedPackage in authorizedPackages) { NapackMetadata metadata = Global.NapackStorageManager.GetPackageMetadata(authorizedPackage, true); metadata.AuthorizedUserIds.Remove(storedUser.Email); if (metadata.AuthorizedUserIds.Any()) { orphanedPackages.Add(authorizedPackage); } Global.NapackStorageManager.UpdatePackageMetadata(metadata); } Global.NapackStorageManager.RemoveUser(user); return(responseFormatter.AsJson(new { NapacksDeauthenticatedFrom = authorizedPackages, OrphanedPackages = orphanedPackages, }, HttpStatusCode.Gone)); }
public static NapackMetadata CreateFromNewNapack(string napackName, NewNapack newNapack) { NapackMetadata napackMetadata = new NapackMetadata() { Name = napackName, Description = newNapack.metadata.Description, MoreInformation = newNapack.metadata.MoreInformation, Tags = newNapack.metadata.Tags ?? new List <string>(), AuthorizedUserIds = newNapack.metadata.AuthorizedUserIds, Versions = new Dictionary <int, NapackMajorVersionMetadata>() }; NapackMajorVersionMetadata majorVersionMetadata = new NapackMajorVersionMetadata() { Recalled = false, License = newNapack.NewNapackVersion.License, Versions = new Dictionary <int, List <int> > { [0] = new List <int> { 0 } } }; napackMetadata.Versions.Add(1, majorVersionMetadata); return(napackMetadata); }
public void SaveNewNapack(string napackName, NewNapack newNapack, NapackSpec napackSpec) { NapackVersionIdentifier version = new NapackVersionIdentifier(napackName, 1, 0, 0); NapackMetadata metadata = NapackMetadata.CreateFromNewNapack(napackName, newNapack); NapackVersion packageVersion = NapackVersion.CreateFromNewNapack(newNapack.NewNapackVersion); foreach (string author in newNapack.NewNapackVersion.Authors) { AddItem(author.ToUpperInvariant(), version, AuthorPackageTable, "authorName", "packageVersionList", false); } foreach (string userId in newNapack.metadata.AuthorizedUserIds) { AddItem(userId.ToUpperInvariant(), napackName, UserPackageTable, "userId", "packageNameList", true); } foreach (NapackMajorVersion consumedPackage in newNapack.NewNapackVersion.Dependencies) { AddItem(consumedPackage.ToString(), version, PackageConsumersTable, "packageMajorVersionId", "consumingPackages", false); } // Add the new napack to all the various stores. NapackStats stats = new NapackStats(); stats.AddVersion(newNapack.NewNapackVersion); ExecuteTransactionCommand((command) => { string statsEncoded = Serializer.Serialize(stats); command.Parameters.Add(napackName); command.Parameters.Add(statsEncoded); command.CommandText = $"INSERT INTO {PackageStatsTable} VALUES ($1, $2)"; command.ExecuteNonQuery(); command.Parameters.Clear(); string metadataEncoded = Serializer.Serialize(metadata); string safeDescriptionAndTags = GetSafeDescriptionAndTags(metadata); command.Parameters.Add(napackName); command.Parameters.Add(safeDescriptionAndTags); command.Parameters.Add(metadataEncoded); command.CommandText = $"INSERT INTO {PackageMetadataTable} VALUES ($1, $2, $3)"; command.ExecuteNonQuery(); command.Parameters.Clear(); string napackSpecEncoded = Serializer.Serialize(napackSpec); command.Parameters.Add(version.GetFullName()); command.Parameters.Add(napackSpecEncoded); command.CommandText = $"INSERT INTO {PackageSpecsTable} VALUES ($1, $2)"; command.ExecuteNonQuery(); command.Parameters.Clear(); string packageVersionEncoded = Serializer.Serialize(packageVersion); command.Parameters.Add(version.GetFullName()); command.Parameters.Add(packageVersionEncoded); command.CommandText = $"INSERT INTO {PackageStoreTable} VALUES ($1, $2)"; command.ExecuteNonQuery(); command.Parameters.Clear(); }); }
public static NapackSearchIndex CreateFromMetadataAndStats(NapackMetadata metadata, NapackStats stats) { return(new NapackSearchIndex() { Name = metadata.Name, Description = metadata.Description, LastUsedLicense = metadata.Versions[metadata.Versions.Keys.Max()].License, Downloads = stats.Downloads, Views = stats.Views, LastUpdateTime = stats.LastUpdateTime, }); }
public ApiModule() : base("/api") { // Gets a Napack package or series of package versions. Get["/{packageName}/{version?}"] = parameters => { string packageName = parameters.packageName; string version = null; try { version = parameters.version; } catch (RuntimeBinderException) { } if (version == null) { NapackMetadata metadata = Global.NapackStorageManager.GetPackageMetadata(packageName, false); return(View["NapackVersions", new VersionsModel(metadata)]); } else { // Attempt to parse our the version string. List <int> components; try { components = version.Split(new[] { '.' }, StringSplitOptions.RemoveEmptyEntries).Select(item => int.Parse(item)).ToList(); if (components.Count != 3) { throw new Exception(); } } catch (Exception) { throw new InvalidNapackVersionException(); } NapackVersionIdentifier versionId = new NapackVersionIdentifier(packageName, components[0], components[1], components[2]); NapackSpec spec = Global.NapackStorageManager.GetPackageSpecification(versionId); NapackVersion packageVersion = Global.NapackStorageManager.GetPackageVersion(versionId); return(View["NapackApi", new ApiModel(versionId.GetFullName(), spec, packageVersion.Dependencies)]); } }; }
private void UpdatePackageMetadataStore(NapackMetadata package, NapackVersionIdentifier nextVersion, NapackAnalyst.UpversionType upversionType, NewNapackVersion newNapackVersion) { if (upversionType == NapackAnalyst.UpversionType.Major) { NapackMajorVersionMetadata newMajorVersionMetadata = new NapackMajorVersionMetadata() { Recalled = false, Versions = new Dictionary <int, List <int> > { [0] = new List <int> { 0 } }, License = newNapackVersion.License }; package.Versions.Add(nextVersion.Major, newMajorVersionMetadata); } else if (upversionType == NapackAnalyst.UpversionType.Minor) { package.Versions[nextVersion.Major].Versions.Add(nextVersion.Minor, new List <int> { 0 }); } else { package.Versions[nextVersion.Major].Versions[nextVersion.Minor].Add(nextVersion.Patch); } ExecuteTransactionCommand((command) => { NapackStats stats = GetItem <NapackStats>(command, PackageStatsTable, "packageName", package.Name, "packageStat", () => { throw new NapackStatsNotFoundException(package.Name); }); stats.AddVersion(newNapackVersion); string statsEncoded = Serializer.Serialize(stats); command.Parameters.Add(statsEncoded); command.Parameters.Add(package.Name); command.CommandText = $"UPDATE {PackageStatsTable} SET packageStat = $1 WHERE packageName = $2"; command.ExecuteNonQuery(); command.Parameters.Clear(); }); UpdatePackageMetadata(package); }
public void UpdatePackageMetadata(NapackMetadata package) { // Updating a package *always* clears the lock. package.ConcurrentLock = DateTime.MinValue; string metadataEncoded = Serializer.Serialize(package); ExecuteCommand($"UPDATE {PackageMetadataTable} SET metadata = $1, descriptionAndTags = $2 WHERE packageName = $3", command => { command.Parameters.Add(metadataEncoded); command.Parameters.Add(GetSafeDescriptionAndTags(package)); command.Parameters.Add(package.Name); int rowsUpdated = command.ExecuteNonQuery(); command.Parameters.Clear(); if (rowsUpdated == 0) { throw new NapackNotFoundException(package.Name); } }); }
/// <summary> /// Saves a new napack version onto an existing napack. /// </summary> /// <param name="package">The package metadata.</param> /// <param name="currentVersion">The current version of the napack.</param> /// <param name="upversionType">The type of upversioning to perform.</param> /// <param name="newNapackVersion">The new napack version.</param> /// <param name="newVersionSpec">The napack specification.</param> public void SaveNewNapackVersion(NapackMetadata package, NapackVersionIdentifier currentVersion, NapackAnalyst.UpversionType upversionType, NewNapackVersion newNapackVersion, NapackSpec newVersionSpec) { NapackVersionIdentifier nextVersion = new NapackVersionIdentifier(currentVersion.NapackName, currentVersion.Major, currentVersion.Minor, currentVersion.Patch); NapackVersion packageVersion = NapackVersion.CreateFromNewNapack(newNapackVersion); foreach (string author in newNapackVersion.Authors) { AddAuthorConsumption(author, nextVersion); } // Changes in user authorization do not occur through napack version updates. foreach (NapackMajorVersion consumedPackage in newNapackVersion.Dependencies) { AddConsumingPackage(consumedPackage, nextVersion); } UpdatePackageMetadataStore(package, nextVersion, upversionType, newNapackVersion); packageStore.Add(nextVersion.GetFullName(), packageVersion); specStore.Add(nextVersion.GetFullName(), newVersionSpec); }
public SearchModule() : base("/search") { // Retrieves and displays the basic search page. Get["/"] = parameters => { return(View["Search"]); }; // Retrieves a sequence of search results. Get["/query"] = parameters => { string query = (string)this.Request.Query["search"]; int skip = int.Parse(this.Request.Query["$skip"]); int top = Math.Min(int.Parse(this.Request.Query["$top"]), SearchModule.MaxResultsPerPage); List <NapackSearchIndex> packagesFound = Global.NapackStorageManager.FindPackages(query, skip, top); return(this.Response.AsJson(new { Results = packagesFound.Select(package => package.ToAnonymousType()), CanContinue = packagesFound.Count == top })); }; // Retrives the detailed information about a search result. Get["/result/{packageName}"] = parameters => { string packageName = parameters.packageName; NapackMetadata metadata = Global.NapackStorageManager.GetPackageMetadata(packageName, false); NapackStats stats = Global.NapackStorageManager.GetPackageStatistics(packageName); return(this.Response.AsJson(new { Authors = stats.AllAuthors, AuthorizedUsers = metadata.AuthorizedUserIds, MoreInformation = metadata.MoreInformation, Tags = metadata.Tags })); }; }
public void SaveNewNapackVersion(NapackMetadata package, NapackVersionIdentifier currentVersion, NapackAnalyst.UpversionType upversionType, NewNapackVersion newNapackVersion, NapackSpec newVersionSpec) { NapackVersionIdentifier nextVersion = new NapackVersionIdentifier(currentVersion.NapackName, currentVersion.Major, currentVersion.Minor, currentVersion.Patch); NapackVersion packageVersion = NapackVersion.CreateFromNewNapack(newNapackVersion); foreach (string author in newNapackVersion.Authors) { AddItem(author.ToUpperInvariant(), nextVersion, AuthorPackageTable, "authorName", "packageVersionList", false); } // // // Changes in user authorization do not occur through napack version updates. // foreach (NapackMajorVersion consumedPackage in newNapackVersion.Dependencies) { AddItem(consumedPackage.ToString(), nextVersion, PackageConsumersTable, "packageMajorVersionId", "consumingPackages", false); } ExecuteTransactionCommand((command) => { string napackSpecEncoded = Serializer.Serialize(newVersionSpec); command.Parameters.Add(nextVersion.GetFullName()); command.Parameters.Add(napackSpecEncoded); command.CommandText = $"INSERT INTO {PackageSpecsTable} VALUES ($1, $2)"; command.ExecuteNonQuery(); command.Parameters.Clear(); string packageVersionEncoded = Serializer.Serialize(packageVersion); command.Parameters.Add(nextVersion.GetFullName()); command.Parameters.Add(packageVersionEncoded); command.CommandText = $"INSERT INTO {PackageStoreTable} VALUES ($1, $2)"; command.ExecuteNonQuery(); command.Parameters.Clear(); }); // Our lock is cleared at last here. UpdatePackageMetadataStore(package, nextVersion, upversionType, newNapackVersion); }
public NapackMetadata GetPackageMetadata(string packageName, bool lockPackage) { // Before we do any updates, we need to verify someone else isn't concurrently updating the same Napack. // If someone else is, we will exit with a concurrency exception instead of performing all our calculations again. NapackMetadata package = null; ExecuteTransactionCommand((command) => { package = this.GetItem <NapackMetadata>(command, PackageMetadataTable, "packageName", packageName, "metadata", () => { throw new NapackNotFoundException(packageName); }); if (lockPackage) { if (package.ConcurrentLock + TimeSpan.FromMinutes(10) > DateTime.UtcNow) { // Honestly, this is pretty hacky. Someone could *theoretically* make the code from here on down take > 10 minutes, causing a concurrency problem. // Ideally, this entire workflow needs another redesign to fail faster -- or I need to implement an ETag-style concurrency mechanism with my data. throw new ConcurrentOperationException(); } // Take a transient lock package.ConcurrentLock = DateTime.UtcNow; string metadataEncoded = Serializer.Serialize(package); command.CommandText = $"UPDATE {PackageMetadataTable} SET metadata = $1, descriptionAndTags = $2 WHERE packageName = $3"; command.Parameters.Add(metadataEncoded); command.Parameters.Add(GetSafeDescriptionAndTags(package)); command.Parameters.Add(package.Name); int rowsUpdated = command.ExecuteNonQuery(); command.Parameters.Clear(); if (rowsUpdated == 0) { throw new NapackNotFoundException(package.Name); } } }); return(package); }
public void UpdatePackageMetadata(NapackMetadata metadata) { packageMetadataStore[metadata.Name] = metadata; }
private string GetSafeDescriptionAndTags(NapackMetadata metadata) { return((metadata.Description + " " + string.Join(" ", metadata.Tags)).Replace("'", "''")); }
public AdminModule() : base("/admin") { // Shuts down the Napack Framework Server cleanly. Post["/shutdown"] = parameters => { AdminModule.ValidateAdmin(this.Context); Global.ShutdownEvent.Set(); return(this.Response.AsJson(new { UtcTime = DateTime.UtcNow }, HttpStatusCode.ImATeapot)); }; // Performs the specified user modification to the given user. Patch["/users"] = parameters => { UserModification userModification = SerializerExtensions.Deserialize <UserModification>(this.Context); AdminModule.ValidateAdmin(this.Context); UserIdentifier user = Global.NapackStorageManager.GetUser(userModification.UserId); switch (userModification.Operation) { case Operation.DeleteUser: return(UsersModule.DeleteUser(this.Response, user)); case Operation.UpdateAccessKeys: UsersModule.AssignSecretsAndSendVerificationEmail(user); Global.NapackStorageManager.UpdateUser(user); break; } return(this.Response.AsJson(new { OperationPerformed = userModification.Operation })); }; // Recalls a package. Post["/recall/{packageName}/{majorVersion}"] = parameters => { string packageName = parameters.packageName; int majorVersion = int.Parse(parameters.majorVersion); AdminModule.ValidateAdmin(this.Context); NapackMetadata metadata = Global.NapackStorageManager.GetPackageMetadata(packageName, true); NapackMajorVersionMetadata majorVersionMetadata = metadata.GetMajorVersion(majorVersion); majorVersionMetadata.Recalled = true; Global.NapackStorageManager.UpdatePackageMetadata(metadata); return(this.Response.AsJson(new { VersionRecalled = majorVersion })); }; // Deletes a package. // // Deleting a Napack involves removing: // - The package statistics. // - All of the specs. // - All of the packages. // - The metadata // In addition, the package is removed from. // - The listing of packages an author has authored. // - The listing of packages a user has access to. // - Each package that took a dependency on this package* // // Finally, an email is sent to all affected users and authorized users. Delete["/manage/{packageName}"] = parameters => { string packageName = parameters.packageName; AdminModule.ValidateAdmin(this.Context); // TODO there's a lot of hardening that can be done here to prevent failures. NapackMetadata metadata = Global.NapackStorageManager.GetPackageMetadata(packageName, true); Global.NapackStorageManager.RemovePackageStatistics(packageName); foreach (string authorizedUser in metadata.AuthorizedUserIds) { Global.NapackStorageManager.RemoveAuthoredPackages(authorizedUser, packageName); } Dictionary <string, List <NapackVersionIdentifier> > packagesToRemovePerAuthor = new Dictionary <string, List <NapackVersionIdentifier> >(); foreach (KeyValuePair <int, NapackMajorVersionMetadata> majorVersion in metadata.Versions) { foreach (KeyValuePair <int, List <int> > minorVersion in majorVersion.Value.Versions) { foreach (int patchVersion in minorVersion.Value) { NapackVersionIdentifier versionIdentifier = new NapackVersionIdentifier(packageName, majorVersion.Key, minorVersion.Key, patchVersion); NapackVersion version = Global.NapackStorageManager.GetPackageVersion(versionIdentifier); foreach (string author in version.Authors) { if (!packagesToRemovePerAuthor.ContainsKey(author)) { packagesToRemovePerAuthor.Add(author, new List <NapackVersionIdentifier>()); } packagesToRemovePerAuthor[author].Add(versionIdentifier); } Global.NapackStorageManager.RemovePackageVersion(versionIdentifier); Global.NapackStorageManager.RemovePackageSpecification(versionIdentifier); } } } HashSet <string> affectedPackages = new HashSet <string>(); foreach (NapackMajorVersion majorVersion in metadata.Versions.Keys.Select(value => new NapackMajorVersion(packageName, value))) { List <NapackVersionIdentifier> consumingPackages = Global.NapackStorageManager.GetPackageConsumers(majorVersion).ToList(); foreach (NapackVersionIdentifier consumingPackage in consumingPackages) { NapackVersion version = Global.NapackStorageManager.GetPackageVersion(consumingPackage); if (version.Dependencies.Remove(majorVersion)) { Global.NapackStorageManager.UpdatePackageVersion(consumingPackage, version); affectedPackages.Add(consumingPackage.NapackName); } } } HashSet <string> affectedUsers = new HashSet <string>(); foreach (string affectedPackage in affectedPackages) { NapackMetadata affectedPackageMetadata = Global.NapackStorageManager.GetPackageMetadata(affectedPackage, true); foreach (string authorizedUserId in affectedPackageMetadata.AuthorizedUserIds) { affectedUsers.Add(authorizedUserId); } } // Send the emails now that we're all done. foreach (string authorizedUserId in metadata.AuthorizedUserIds) { UserIdentifier user = Global.NapackStorageManager.GetUser(authorizedUserId); Global.EmailManager.SendPackageDeletionEmail(user, packageName, false); Global.NapackStorageManager.UpdateUser(user); } foreach (string authorizedUserId in affectedUsers) { UserIdentifier user = Global.NapackStorageManager.GetUser(authorizedUserId); Global.EmailManager.SendPackageDeletionEmail(user, packageName, true); Global.NapackStorageManager.UpdateUser(user); } return(this.Response.AsJson(new { AuthorizedUsersNotified = metadata.AuthorizedUserIds, AffectedUsersNotified = affectedUsers, Deleted = true }, HttpStatusCode.Gone)); }; }