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);
        }
Exemplo n.º 3
0
        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));
        }
Exemplo n.º 4
0
        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);
        }
Exemplo n.º 5
0
        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();
            });
        }
Exemplo n.º 6
0
 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,
     });
 }
Exemplo n.º 7
0
        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)]);
                }
            };
        }
Exemplo n.º 8
0
        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);
        }
Exemplo n.º 9
0
        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);
                }
            });
        }
Exemplo n.º 10
0
        /// <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);
        }
Exemplo n.º 11
0
        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
                }));
            };
        }
Exemplo n.º 12
0
        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);
        }
Exemplo n.º 13
0
        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);
        }
Exemplo n.º 14
0
 public void UpdatePackageMetadata(NapackMetadata metadata)
 {
     packageMetadataStore[metadata.Name] = metadata;
 }
Exemplo n.º 15
0
 private string GetSafeDescriptionAndTags(NapackMetadata metadata)
 {
     return((metadata.Description + " " + string.Join(" ", metadata.Tags)).Replace("'", "''"));
 }
Exemplo n.º 16
0
        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));
            };
        }