/// <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); }
/// <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); }
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 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)]); } }; }
/// <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); }
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); }
public ApiModel(string packageName, NapackSpec spec, List <NapackMajorVersion> dependencies) { this.NapackFullName = packageName; this.Spec = spec; this.Dependencies = dependencies; }
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 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 })); }; }