/// <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);
        }
Exemple #2
0
        /// <summary>
        /// Creates a Napack spec for the defined napack files.
        /// </summary>
        /// <param name="napackName">The Napack package name.</param>
        /// <param name="napackFiles">A mapping of file path + name -> file itself.</param>
        /// <remarks>
        /// Restrictions:
        /// - All Napack files with MSBuild type <see cref="NapackFile.CompileType"/> must be able to be analyzed by this system.
        /// - The namespace of all analyzable-files must be the specified napack name (CASE-SENSITIVE, as per C# spec).
        /// - Although C# allows multiple namespaces in a single file, as they all must be the specified napack name doing so is prohibited.
        /// - Partial classes are disallowed as any non-code files won't have their namespace updated. (This may be changed to better support UI elements, etc in the future).
        /// </remarks>
        /// <exception cref="InvalidNapackFileExtensionException">If a Napack file contains a prohibited extension.</exception>
        /// <exception cref="InvalidNamespaceException">If a compilable Napack file is in the wrong namespace.</exception>
        /// <exception cref="InvalidNapackFileException">If a Napack file is listed with MSBuild type <see cref="NapackFile.CompileType"/>, but could not be analyzed.</exception>
        /// <exception cref="UnsupportedNapackFileException">If a Napack file uses C# functionality or syntax that the Napack system explicitly prohibits.</exception>
        public static NapackSpec CreateNapackSpec(string napackName, IDictionary <string, NapackFile> napackFiles)
        {
            NapackAnalyst.NameValidationConfig.Validate(napackName);

            NapackSpec spec = new NapackSpec();

            foreach (KeyValuePair <string, NapackFile> fileEntry in napackFiles)
            {
                // Validate the extension.
                string filename  = Path.GetFileName(fileEntry.Key);
                string extension = Path.GetExtension(fileEntry.Key);
                if (!string.IsNullOrWhiteSpace(extension))
                {
                    NapackAnalyst.PackageValidationConfig.ValidateExtension(filename, extension);
                }

                if (fileEntry.Value.MsbuildType.Equals(NapackFile.CompileType, StringComparison.InvariantCultureIgnoreCase))
                {
                    NapackSpec specForSingleFile = NapackFileAnalyzer.Analyze(napackName, filename, fileEntry.Value.Contents);
                    spec.Classes.AddRange(specForSingleFile.Classes);
                    spec.Interfaces.AddRange(specForSingleFile.Interfaces);
                }
                else
                {
                    spec.UnknownFiles.Add(fileEntry.Value);
                }
            }

            return(spec);
        }
Exemple #3
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();
            });
        }
Exemple #4
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)]);
                }
            };
        }
Exemple #5
0
        /// <summary>
        /// Determines the requried upversioning when code transitions from the old Napack to the new Napack.
        /// </summary>
        /// <remarks>
        /// Major == breaking changes.
        /// Minor == publically-facing API was added (but not changed or removed) from the new Napack.
        /// Patch == The publically-facing API is identical between both Napacks, excluding documentation.
        /// </remarks>
        public static UpversionType DeterminedRequiredUpversioning(NapackSpec oldNapack, NapackSpec newNapack)
        {
            UpversionType maxUpversionType = UpversionType.Patch;

            foreach (ClassSpec oldClassSpec in oldNapack.Classes)
            {
                ClassSpec newClassSpec = newNapack.Classes
                                         .FirstOrDefault(cls => cls.Name.Name.Equals(oldClassSpec.Name.Name, StringComparison.InvariantCulture));
                UpversionType upversionType = NapackAnalyst.AnalyzeClass(oldClassSpec, newClassSpec);
                if (upversionType == UpversionType.Major)
                {
                    // Exit early, as we found a breaking change.
                    return(UpversionType.Major);
                }
                else if (upversionType == UpversionType.Minor)
                {
                    maxUpversionType = UpversionType.Minor;
                }
            }

            foreach (InterfaceSpec oldInterfaceSpec in oldNapack.Interfaces)
            {
                InterfaceSpec newInterfaceSpec = newNapack.Interfaces
                                                 .FirstOrDefault(cls => cls.Name.Name.Equals(oldInterfaceSpec.Name.Name, StringComparison.InvariantCulture));
                UpversionType upversionType = NapackAnalyst.AnalyzeInterface(oldInterfaceSpec, newInterfaceSpec);
                if (upversionType == UpversionType.Major)
                {
                    // Exit early, as we found a breaking change.
                    return(UpversionType.Major);
                }
                else if (upversionType == UpversionType.Minor)
                {
                    maxUpversionType = UpversionType.Minor;
                }
            }

            // No APIs added in the prior classes. Did the new package add classes?
            if (maxUpversionType == UpversionType.Patch && (newNapack.Classes.Count != oldNapack.Classes.Count || newNapack.Interfaces.Count != oldNapack.Interfaces.Count))
            {
                maxUpversionType = UpversionType.Minor;
            }

            return(maxUpversionType);
        }
Exemple #6
0
        internal static async Task <NapackSpec> AnalyzeSyntaxTree(string napackName, string filename, SyntaxTree tree)
        {
            NapackSpec singleFileSpec = new NapackSpec();
            SyntaxNode root           = await tree.GetRootAsync();

            if (root.ChildNodes().Count(node => node.IsKind(SyntaxKind.NamespaceDeclaration)) > 1)
            {
                throw new UnsupportedNapackFileException(filename, "multiple namespace");
            }
            else if (!root.ChildNodes().Any(node => node.IsKind(SyntaxKind.NamespaceDeclaration)))
            {
                throw new InvalidNapackFileException(filename, "Missing a namespace!");
            }

            // Switch to the namespace as the root and verify (CASE SENSITIVE)
            root = root.ChildNodes().SingleOrDefault(node => node.IsKind(SyntaxKind.NamespaceDeclaration));
            if (!(root as NamespaceDeclarationSyntax).Name.ToString().Equals(napackName, StringComparison.InvariantCulture))
            {
                throw new InvalidNapackFileException(filename, "Namespace name is not " + napackName);
            }

            foreach (ClassDeclarationSyntax classNode in root.ChildNodes().Where(node => node.IsKind(SyntaxKind.ClassDeclaration)))
            {
                if (classNode.Modifiers.Any(modifier => modifier.IsKind(SyntaxKind.PublicKeyword)))
                {
                    singleFileSpec.Classes.Add(AnalyzeClassSyntaxTree(napackName, filename, classNode));
                }
            }

            foreach (InterfaceDeclarationSyntax interfaceNode in root.ChildNodes().Where(node => node.IsKind(SyntaxKind.InterfaceDeclaration)))
            {
                if (interfaceNode.Modifiers.Any(modifier => modifier.IsKind(SyntaxKind.PublicKeyword)))
                {
                    singleFileSpec.Interfaces.Add(AnalyzeInterfaceSyntaxTree(napackName, filename, interfaceNode));
                }
            }

            return(singleFileSpec);
        }
        /// <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 void MajorMethodChangeCausesMajor()
        {
            NapackSpec minorMethodSpec = this.LoadNapackSpec(SampleStrings.LargeSampleMajorMethodChange);

            Assert.AreEqual(NapackAnalyst.UpversionType.Major, NapackAnalyst.DeterminedRequiredUpversioning(oldSpec, minorMethodSpec));
        }
 public void TestInitialize()
 {
     oldSpec = this.LoadNapackSpec(SampleStrings.LargeSampleClass);
 }
Exemple #10
0
 public ApiModel(string packageName, NapackSpec spec, List <NapackMajorVersion> dependencies)
 {
     this.NapackFullName = packageName;
     this.Spec           = spec;
     this.Dependencies   = dependencies;
 }
Exemple #11
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);
        }
Exemple #12
0
        public NapackModule()
            : base("/napacks")
        {
            // 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)
                {
                    // The user is asking for all major versions of the specified package.
                    NapackMetadata package = Global.NapackStorageManager.GetPackageMetadata(packageName, false);
                    return(this.Response.AsJson(package.AsSummaryJson()));
                }
                else
                {
                    // Attempt to parse our the version string.
                    List <int> components;
                    try
                    {
                        components = version.Split(new[] { '.' }, StringSplitOptions.RemoveEmptyEntries).Select(item => int.Parse(item)).ToList();
                    }
                    catch (Exception)
                    {
                        throw new InvalidNapackVersionException();
                    }

                    // Handle the resulting version components.
                    if (components.Count == 1 || components.Count == 2)
                    {
                        NapackMetadata             package      = Global.NapackStorageManager.GetPackageMetadata(packageName, false);
                        NapackMajorVersionMetadata majorVersion = package.GetMajorVersion(components[0]);

                        return(this.Response.AsJson(majorVersion.AsSummaryJson()));
                    }
                    else if (components.Count == 3)
                    {
                        NapackVersion specificVersion = Global.NapackStorageManager.GetPackageVersion(new NapackVersionIdentifier(packageName, components[0], components[1], components[2]));
                        Global.NapackStorageManager.IncrementPackageDownload(packageName);

                        return(this.Response.AsJson(specificVersion.AsSummaryJson()));
                    }
                    else
                    {
                        throw new InvalidNapackVersionException();
                    }
                }
            };

            // Creates a new Napack package.
            Post["/{packageName}"] = parameters =>
            {
                string packageName = parameters.packageName;
                if (Global.NapackStorageManager.ContainsNapack(packageName))
                {
                    throw new DuplicateNapackException();
                }

                // Validate user, name and API.
                NewNapack newNapack = SerializerExtensions.Deserialize <NewNapack>(this.Context);
                newNapack.Validate();

                UserIdentifier.VerifyAuthorization(this.Request.Headers.ToDictionary(hdr => hdr.Key, hdr => hdr.Value), Global.NapackStorageManager, newNapack.metadata.AuthorizedUserIds);
                NapackSpec generatedApiSpec = NapackAnalyst.CreateNapackSpec(packageName, newNapack.NewNapackVersion.Files);
                NapackModule.ValidateDependentPackages(Global.NapackStorageManager, newNapack.NewNapackVersion);

                newNapack.NewNapackVersion.UpdateNamespaceOfFiles(packageName, 1);
                Global.NapackStorageManager.SaveNewNapack(packageName, newNapack, generatedApiSpec);

                return(this.Response.AsJson(new
                {
                    Message = "Created package " + packageName
                }, HttpStatusCode.Created));
            };

            // Updates the definition (metadata) of a Napack package.
            Put["/{packageName"] = parameters =>
            {
                string         packageName = parameters.packageName;
                NapackMetadata package     = Global.NapackStorageManager.GetPackageMetadata(packageName, true);

                NewNapackMetadata metadata = SerializerExtensions.Deserialize <NewNapackMetadata>(this.Context);
                UserIdentifier.VerifyAuthorization(this.Request.Headers.ToDictionary(hdr => hdr.Key, hdr => hdr.Value), Global.NapackStorageManager, metadata.AuthorizedUserIds);

                if (metadata.AuthorizedUserIds.Count == 0)
                {
                    throw new InvalidNapackException("At least one authorized user ID must be provided.");
                }

                package.AuthorizedUserIds = metadata.AuthorizedUserIds;
                package.Description       = metadata.Description;
                package.MoreInformation   = metadata.MoreInformation;
                package.Tags = metadata.Tags;
                Global.NapackStorageManager.UpdatePackageMetadata(package);

                return(this.Response.AsJson(new
                {
                    Message = "Updated package metadata " + packageName
                }));
            };

            // Updates an existing Napack package.
            Patch["/{packageName}"] = parameters =>
            {
                NewNapackVersion newNapackVersion = SerializerExtensions.Deserialize <NewNapackVersion>(this.Context);
                newNapackVersion.Validate();

                string         packageName = parameters.packageName;
                NapackMetadata package     = Global.NapackStorageManager.GetPackageMetadata(packageName, true);
                UserIdentifier.VerifyAuthorization(this.Request.Headers.ToDictionary(hdr => hdr.Key, hdr => hdr.Value), Global.NapackStorageManager, package.AuthorizedUserIds);

                // Validate and create a spec for this new version.
                NapackSpec newVersionSpec = NapackAnalyst.CreateNapackSpec(packageName, newNapackVersion.Files);
                NapackModule.ValidateDependentPackages(Global.NapackStorageManager, newNapackVersion);

                // Determine what upversioning will be performed.
                int majorVersion = package.Versions.Max(version => version.Key);
                int minorVersion = package.Versions[majorVersion].Versions.Max(version => version.Key);
                int patchVersion = package.Versions[majorVersion].Versions[minorVersion].Max();

                NapackAnalyst.UpversionType upversionType = NapackAnalyst.UpversionType.Patch;
                if (newNapackVersion.ForceMajorUpversioning)
                {
                    // Skip analysis as we know we must go to a new major version.
                    upversionType = NapackAnalyst.UpversionType.Major;
                }
                else
                {
                    // Perform specification and license analysis.
                    NapackVersionIdentifier oldVersionId          = new NapackVersionIdentifier(packageName, majorVersion, minorVersion, patchVersion);
                    NapackSpec oldVersionSpec                     = Global.NapackStorageManager.GetPackageSpecification(oldVersionId);
                    NapackAnalyst.UpversionType specUpversionType = NapackAnalyst.DeterminedRequiredUpversioning(oldVersionSpec, newVersionSpec);
                    if (specUpversionType == NapackAnalyst.UpversionType.Major || newNapackVersion.License.NeedsMajorUpversioning(package.GetMajorVersion(majorVersion).License))
                    {
                        upversionType = NapackAnalyst.UpversionType.Major;
                    }
                }

                if (upversionType == NapackAnalyst.UpversionType.Patch && newNapackVersion.ForceMinorUpversioning)
                {
                    upversionType = NapackAnalyst.UpversionType.Minor;
                }

                newNapackVersion.UpdateNamespaceOfFiles(packageName, upversionType != NapackAnalyst.UpversionType.Major ? majorVersion : majorVersion + 1);
                Global.NapackStorageManager.SaveNewNapackVersion(package, new NapackVersionIdentifier(packageName, majorVersion, minorVersion, patchVersion), upversionType, newNapackVersion, newVersionSpec);

                return(this.Response.AsJson(new
                {
                    Message = "Updated package " + packageName,
                    Major = majorVersion,
                    Minor = minorVersion,
                    Patch = patchVersion
                }));
            };
        }