From 1957a76285a7620e48b5d37bef8ca84a08c5cd9e Mon Sep 17 00:00:00 2001 From: Yunus Emre Kalkan Date: Tue, 30 Mar 2021 13:41:53 +0300 Subject: [PATCH] Cli: add-package with source code for Angular --- .../Abp/Cli/Commands/AddPackageCommand.cs | 88 ++++++++++--- .../Services/SourceCodeDownloadService.cs | 60 ++++++++- .../ProjectBuilding/AbpIoSourceCodeStore.cs | 2 +- .../NpmPackageProjectBuildPipelineBuilder.cs | 18 +++ ...ugetPackageProjectBuildPipelineBuilder.cs} | 2 +- .../Building/ProjectBuildContext.cs | 14 ++- .../INpmPackageInfoProvider.cs | 10 ++ .../ProjectBuilding/ModuleProjectBuilder.cs | 1 + .../ProjectBuilding/NpmPackageInfoProvider.cs | 62 +++++++++ .../NpmPackageProjectBuilder.cs | 110 ++++++++++++++++ .../NugetPackageInfoProvider.cs | 2 +- ...ilder.cs => NugetPackageProjectBuilder.cs} | 13 +- .../Cli/ProjectBuilding/SourceCodeTypes.cs | 4 +- .../ProjectBuilding/TemplateProjectBuilder.cs | 1 + ...CodeAdder.cs => AngularSourceCodeAdder.cs} | 32 ++++- .../ProjectNpmPackageAdder.cs | 118 +++++++++++++++++- .../ProjectNugetPackageAdder.cs | 2 +- .../SolutionModuleAdder.cs | 12 +- 18 files changed, 496 insertions(+), 55 deletions(-) create mode 100644 framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/NpmPackageProjectBuildPipelineBuilder.cs rename framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/{PackageProjectBuildPipelineBuilder.cs => NugetPackageProjectBuildPipelineBuilder.cs} (91%) create mode 100644 framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/INpmPackageInfoProvider.cs create mode 100644 framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/NpmPackageInfoProvider.cs create mode 100644 framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/NpmPackageProjectBuilder.cs rename framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/{PackageProjectBuilder.cs => NugetPackageProjectBuilder.cs} (90%) rename framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/{AngularModuleSourceCodeAdder.cs => AngularSourceCodeAdder.cs} (90%) diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/AddPackageCommand.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/AddPackageCommand.cs index b2d407c00f..bebc854208 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/AddPackageCommand.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/AddPackageCommand.cs @@ -18,9 +18,12 @@ namespace Volo.Abp.Cli.Commands protected ProjectNugetPackageAdder ProjectNugetPackageAdder { get; } - public AddPackageCommand(ProjectNugetPackageAdder projectNugetPackageAdder) + public ProjectNpmPackageAdder ProjectNpmPackageAdder { get; } + + public AddPackageCommand(ProjectNugetPackageAdder projectNugetPackageAdder, ProjectNpmPackageAdder projectNpmPackageAdder) { ProjectNugetPackageAdder = projectNugetPackageAdder; + ProjectNpmPackageAdder = projectNpmPackageAdder; Logger = NullLogger.Instance; } @@ -35,19 +38,42 @@ namespace Volo.Abp.Cli.Commands ); } + var isAngularPackage = false; + var isNugetPackage = true; + + if (commandLineArgs.Target.StartsWith("@")) + { + isAngularPackage = true; + isNugetPackage = false; + } + var version = commandLineArgs.Options.GetOrNull(Options.Version.Short, Options.Version.Long); var withSourceCode =commandLineArgs.Options.ContainsKey(Options.SourceCode.Long); - var addSourceCodeToSolutionFile = withSourceCode && commandLineArgs.Options.ContainsKey("add-to-solution-file"); - - await ProjectNugetPackageAdder.AddAsync( - GetSolutionFile(commandLineArgs), - GetProjectFile(commandLineArgs), - commandLineArgs.Target, - version, - true, - withSourceCode, - addSourceCodeToSolutionFile - ); + + if (isNugetPackage) + { + var addSourceCodeToSolutionFile = withSourceCode && commandLineArgs.Options.ContainsKey("add-to-solution-file"); + + await ProjectNugetPackageAdder.AddAsync( + GetSolutionFile(commandLineArgs), + GetProjectFile(commandLineArgs), + commandLineArgs.Target, + version, + true, + withSourceCode, + addSourceCodeToSolutionFile + ); + } + else if (isAngularPackage) + { + await ProjectNpmPackageAdder.AddAngularPackageAsync( + GetAngularDirectory(commandLineArgs), + commandLineArgs.Target, + version, + withSourceCode + ); + } + } public string GetUsageInfo() @@ -56,7 +82,7 @@ namespace Volo.Abp.Cli.Commands sb.AppendLine(""); sb.AppendLine("'add-package' command is used to add an ABP package to a project."); - sb.AppendLine("It should be used in a folder containing a .csproj file."); + sb.AppendLine("It should be used in a folder containing a .csproj file, .sln file or angular.json."); sb.AppendLine(""); sb.AppendLine("Usage:"); sb.AppendLine(""); @@ -64,13 +90,18 @@ namespace Volo.Abp.Cli.Commands sb.AppendLine(""); sb.AppendLine("Options:"); sb.AppendLine(""); - sb.AppendLine(" -p|--project Specify the project file explicitly."); - sb.AppendLine(" -v|--version Specify the version of the package. Default is your project's ABP version or latest ABP version."); + sb.AppendLine(" -p|--project Specify the project file explicitly. (Only available for Nuget packages)"); + sb.AppendLine(" -s|--solution Specify the project file explicitly. (Only available for Nuget packages)"); + sb.AppendLine(" --with-source-code Downloads the source code of the Npm/Nuget package and make other projects depends on it."); + sb.AppendLine(" --add-to-solution-file Adds the downloaded project to .sln file, if source code is downloaded. (Only available for Nuget packages)"); + sb.AppendLine(" -ad|--angular-directory Specify the Angular project directory explicitly. (Only available for Angular packages)"); + sb.AppendLine(" -v|--version Specify the version of the package. Default is your project's ABP version or latest ABP version."); sb.AppendLine(""); sb.AppendLine("Examples:"); sb.AppendLine(""); - sb.AppendLine(" abp add-package Volo.Abp.FluentValidation Adds the package to the current project."); - sb.AppendLine(" abp add-package Volo.Abp.FluentValidation -p Acme.BookStore.Application Adds the package to the given project."); + sb.AppendLine(" abp add-package Volo.Abp.FluentValidation Adds the nuget package to the current project."); + sb.AppendLine(" abp add-package Volo.Abp.FluentValidation -p Acme.BookStore.Application Adds the nuget package to the given project."); + sb.AppendLine(" abp add-package @abp/ng.theme.basic Adds the npm package to the given angular project."); sb.AppendLine(""); sb.AppendLine("See the documentation for more info: https://docs.abp.io/en/abp/latest/CLI"); @@ -116,6 +147,23 @@ namespace Volo.Abp.Cli.Commands return Directory.GetFiles(Directory.GetCurrentDirectory(), "*.sln").FirstOrDefault(); } + protected virtual string GetAngularDirectory(CommandLineArgs commandLineArgs) + { + var providedAngularDirectory = PathHelper.NormalizePath( + commandLineArgs.Options.GetOrNull( + Options.AngularDirectory.Short, + Options.AngularDirectory.Long + ) + ); + + if (!providedAngularDirectory.IsNullOrWhiteSpace()) + { + return providedAngularDirectory; + } + + return Directory.GetCurrentDirectory(); + } + public static class Options { public static class Project @@ -130,6 +178,12 @@ namespace Volo.Abp.Cli.Commands public const string Long = "solution"; } + public static class AngularDirectory + { + public const string Short = "ad"; + public const string Long = "angular-directory"; + } + public static class Version { public const string Short = "v"; diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/Services/SourceCodeDownloadService.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/Services/SourceCodeDownloadService.cs index ad1c5cb7fe..088cadb707 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/Services/SourceCodeDownloadService.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/Services/SourceCodeDownloadService.cs @@ -17,13 +17,17 @@ namespace Volo.Abp.Cli.Commands.Services public class SourceCodeDownloadService : ITransientDependency { public ModuleProjectBuilder ModuleProjectBuilder { get; } - public PackageProjectBuilder PackageProjectBuilder { get; } + public NugetPackageProjectBuilder NugetPackageProjectBuilder { get; } + public NpmPackageProjectBuilder NpmPackageProjectBuilder { get; } public ILogger Logger { get; set; } - public SourceCodeDownloadService(ModuleProjectBuilder moduleProjectBuilder, PackageProjectBuilder packageProjectBuilder) + public SourceCodeDownloadService(ModuleProjectBuilder moduleProjectBuilder, + NugetPackageProjectBuilder nugetPackageProjectBuilder, + NpmPackageProjectBuilder npmPackageProjectBuilder) { ModuleProjectBuilder = moduleProjectBuilder; - PackageProjectBuilder = packageProjectBuilder; + NugetPackageProjectBuilder = nugetPackageProjectBuilder; + NpmPackageProjectBuilder = npmPackageProjectBuilder; Logger = NullLogger.Instance; } @@ -92,13 +96,13 @@ namespace Volo.Abp.Cli.Commands.Services Logger.LogInformation($"'{moduleName}' has been successfully downloaded to '{outputFolder}'"); } - public async Task DownloadPackageAsync(string packageName, string outputFolder, string version) + public async Task DownloadNugetPackageAsync(string packageName, string outputFolder, string version) { Logger.LogInformation("Downloading source code of " + packageName); Logger.LogInformation("Version: " + version); Logger.LogInformation("Output folder: " + outputFolder); - var result = await PackageProjectBuilder.BuildAsync( + var result = await NugetPackageProjectBuilder.BuildAsync( new ProjectBuildArgs( SolutionName.Parse(packageName), packageName, @@ -113,12 +117,56 @@ namespace Volo.Abp.Cli.Commands.Services var zipEntry = zipInputStream.GetNextEntry(); while (zipEntry != null) { - if (IsAngularTestFile(zipEntry.Name)) + var fullZipToPath = Path.Combine(outputFolder, zipEntry.Name); + var directoryName = Path.GetDirectoryName(fullZipToPath); + + if (!string.IsNullOrEmpty(directoryName)) + { + Directory.CreateDirectory(directoryName); + } + + var fileName = Path.GetFileName(fullZipToPath); + if (fileName.Length == 0) { zipEntry = zipInputStream.GetNextEntry(); continue; } + var buffer = new byte[4096]; // 4K is optimum + using (var streamWriter = File.Create(fullZipToPath)) + { + StreamUtils.Copy(zipInputStream, streamWriter, buffer); + } + + zipEntry = zipInputStream.GetNextEntry(); + } + } + } + + Logger.LogInformation($"'{packageName}' has been successfully downloaded to '{outputFolder}'"); + } + + public async Task DownloadNpmPackageAsync(string packageName, string outputFolder, string version) + { + Logger.LogInformation("Downloading source code of " + packageName); + Logger.LogInformation("Version: " + version); + Logger.LogInformation("Output folder: " + outputFolder); + + var result = await NpmPackageProjectBuilder.BuildAsync( + new ProjectBuildArgs( + SolutionName.Parse(packageName), + packageName, + version + ) + ); + + using (var templateFileStream = new MemoryStream(result.ZipContent)) + { + using (var zipInputStream = new ZipInputStream(templateFileStream)) + { + var zipEntry = zipInputStream.GetNextEntry(); + while (zipEntry != null) + { var fullZipToPath = Path.Combine(outputFolder, zipEntry.Name); var directoryName = Path.GetDirectoryName(fullZipToPath); diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/AbpIoSourceCodeStore.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/AbpIoSourceCodeStore.cs index 29ea93bd3d..fdafa7c01c 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/AbpIoSourceCodeStore.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/AbpIoSourceCodeStore.cs @@ -91,7 +91,7 @@ namespace Volo.Abp.Cli.ProjectBuilding return new TemplateFile(File.ReadAllBytes(Path.Combine(templateSource, name + "-" + version + ".zip")), version, latestVersion, nugetVersion); } - var localCacheFile = Path.Combine(CliPaths.TemplateCache, name + "-" + version + ".zip"); + var localCacheFile = Path.Combine(CliPaths.TemplateCache, name.Replace("/",".") + "-" + version + ".zip"); #if DEBUG if (File.Exists(localCacheFile)) diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/NpmPackageProjectBuildPipelineBuilder.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/NpmPackageProjectBuildPipelineBuilder.cs new file mode 100644 index 0000000000..4c28f08ccd --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/NpmPackageProjectBuildPipelineBuilder.cs @@ -0,0 +1,18 @@ +using Volo.Abp.Cli.ProjectBuilding.Building.Steps; +using Volo.Abp.Cli.ProjectBuilding.Templates; + +namespace Volo.Abp.Cli.ProjectBuilding.Building +{ + public static class NpmPackageProjectBuildPipelineBuilder + { + public static ProjectBuildPipeline Build(ProjectBuildContext context) + { + var pipeline = new ProjectBuildPipeline(context); + + pipeline.Steps.Add(new FileEntryListReadStep()); + pipeline.Steps.Add(new CreateProjectResultZipStep()); + + return pipeline; + } + } +} diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/PackageProjectBuildPipelineBuilder.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/NugetPackageProjectBuildPipelineBuilder.cs similarity index 91% rename from framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/PackageProjectBuildPipelineBuilder.cs rename to framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/NugetPackageProjectBuildPipelineBuilder.cs index 79245dfaec..0bc074e7b8 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/PackageProjectBuildPipelineBuilder.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/NugetPackageProjectBuildPipelineBuilder.cs @@ -3,7 +3,7 @@ using Volo.Abp.Cli.ProjectBuilding.Templates; namespace Volo.Abp.Cli.ProjectBuilding.Building { - public static class PackageProjectBuildPipelineBuilder + public static class NugetPackageProjectBuildPipelineBuilder { public static ProjectBuildPipeline Build(ProjectBuildContext context) { diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/ProjectBuildContext.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/ProjectBuildContext.cs index b6c2baec88..6b1f606e28 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/ProjectBuildContext.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/ProjectBuildContext.cs @@ -17,24 +17,28 @@ namespace Volo.Abp.Cli.ProjectBuilding.Building public ModuleInfo Module { get; } - public NugetPackageInfo Package { get; } + public NugetPackageInfo NugetPackage { get; } + + public NpmPackageInfo NpmPackage { get; } public FileEntryList Files { get; set; } public ProjectResult Result { get; set; } - + public List Symbols { get; } //TODO: Fill the symbols, like "UI-Angular", "CMS-KIT"! - + public ProjectBuildContext( TemplateInfo template, ModuleInfo module, - NugetPackageInfo package, + NugetPackageInfo nugetPackage, + NpmPackageInfo npmPackage, [NotNull] TemplateFile templateFile, [NotNull] ProjectBuildArgs buildArgs) { Template = template; Module = module; - Package = package; + NugetPackage = nugetPackage; + NpmPackage = npmPackage; TemplateFile = Check.NotNull(templateFile, nameof(templateFile)); BuildArgs = Check.NotNull(buildArgs, nameof(buildArgs)); Symbols = new List(); diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/INpmPackageInfoProvider.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/INpmPackageInfoProvider.cs new file mode 100644 index 0000000000..130afa4a60 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/INpmPackageInfoProvider.cs @@ -0,0 +1,10 @@ +using System.Threading.Tasks; +using Volo.Abp.Cli.ProjectModification; + +namespace Volo.Abp.Cli.ProjectBuilding +{ + public interface INpmPackageInfoProvider + { + Task GetAsync(string name); + } +} diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/ModuleProjectBuilder.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/ModuleProjectBuilder.cs index 029b687015..e621c58acc 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/ModuleProjectBuilder.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/ModuleProjectBuilder.cs @@ -68,6 +68,7 @@ namespace Volo.Abp.Cli.ProjectBuilding null, moduleInfo, null, + null, templateFile, args ); diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/NpmPackageInfoProvider.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/NpmPackageInfoProvider.cs new file mode 100644 index 0000000000..458a419840 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/NpmPackageInfoProvider.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Volo.Abp.Json; +using Volo.Abp.Cli.Http; +using Volo.Abp.Cli.ProjectModification; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Threading; + +namespace Volo.Abp.Cli.ProjectBuilding +{ + public class NpmPackageInfoProvider : INpmPackageInfoProvider, ITransientDependency + { + public IJsonSerializer JsonSerializer { get; } + public ICancellationTokenProvider CancellationTokenProvider { get; } + public IRemoteServiceExceptionHandler RemoteServiceExceptionHandler { get; } + + private readonly CliHttpClientFactory _cliHttpClientFactory; + + public NpmPackageInfoProvider( + IJsonSerializer jsonSerializer, + ICancellationTokenProvider cancellationTokenProvider, + IRemoteServiceExceptionHandler remoteServiceExceptionHandler, + CliHttpClientFactory cliHttpClientFactory) + { + JsonSerializer = jsonSerializer; + CancellationTokenProvider = cancellationTokenProvider; + RemoteServiceExceptionHandler = remoteServiceExceptionHandler; + _cliHttpClientFactory = cliHttpClientFactory; + } + + public async Task GetAsync(string name) + { + var packageList = await GetPackageListInternalAsync(); + + var package = packageList.FirstOrDefault(m => m.Name == name); + + if (package == null) + { + throw new Exception("Package is not found or downloadable!"); + } + + return package; + } + + private async Task> GetPackageListInternalAsync() + { + var client = _cliHttpClientFactory.CreateClient(); + + using (var responseMessage = await client.GetAsync( + $"{CliUrls.WwwAbpIo}api/download/npmPackages/", + CancellationTokenProvider.Token + )) + { + await RemoteServiceExceptionHandler.EnsureSuccessfulHttpResponseAsync(responseMessage); + var result = await responseMessage.Content.ReadAsStringAsync(); + return JsonSerializer.Deserialize>(result); + } + } + } +} diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/NpmPackageProjectBuilder.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/NpmPackageProjectBuilder.cs new file mode 100644 index 0000000000..d60e76d9a7 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/NpmPackageProjectBuilder.cs @@ -0,0 +1,110 @@ +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.Options; +using System; +using System.Linq; +using System.Threading.Tasks; +using Volo.Abp.Cli.Commands; +using Volo.Abp.Cli.Licensing; +using Volo.Abp.Cli.ProjectBuilding.Analyticses; +using Volo.Abp.Cli.ProjectBuilding.Building; +using Volo.Abp.Cli.ProjectModification; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Json; + +namespace Volo.Abp.Cli.ProjectBuilding +{ + public class NpmPackageProjectBuilder : IProjectBuilder, ITransientDependency + { + public ILogger Logger { get; set; } + protected ISourceCodeStore SourceCodeStore { get; } + protected INpmPackageInfoProvider NpmPackageInfoProvider { get; } + protected ICliAnalyticsCollect CliAnalyticsCollect { get; } + protected AbpCliOptions Options { get; } + protected IJsonSerializer JsonSerializer { get; } + protected IApiKeyService ApiKeyService { get; } + + public NpmPackageProjectBuilder(ISourceCodeStore sourceCodeStore, + INpmPackageInfoProvider npmPackageInfoProvider, + ICliAnalyticsCollect cliAnalyticsCollect, + IOptions options, + IJsonSerializer jsonSerializer, + IApiKeyService apiKeyService) + { + SourceCodeStore = sourceCodeStore; + NpmPackageInfoProvider = npmPackageInfoProvider; + CliAnalyticsCollect = cliAnalyticsCollect; + Options = options.Value; + JsonSerializer = jsonSerializer; + ApiKeyService = apiKeyService; + + Logger = NullLogger.Instance; + } + + public async Task BuildAsync(ProjectBuildArgs args) + { + var packageInfo = await GetPackageInfoAsync(args); + + var templateFile = await SourceCodeStore.GetAsync( + args.TemplateName, + SourceCodeTypes.NpmPackage, + args.Version, + null, + args.ExtraProperties.ContainsKey(GetSourceCommand.Options.Preview.Long) + ); + + var apiKeyResult = await ApiKeyService.GetApiKeyOrNullAsync(); + if (apiKeyResult?.ApiKey != null) + { + args.ExtraProperties["api-key"] = apiKeyResult.ApiKey; + } + + if (apiKeyResult?.LicenseCode != null) + { + args.ExtraProperties["license-code"] = apiKeyResult.LicenseCode; + } + + var context = new ProjectBuildContext( + null, + null, + null, + packageInfo, + templateFile, + args + ); + + NpmPackageProjectBuildPipelineBuilder.Build(context).Execute(); + + // Exclude unwanted or known options. + var options = args.ExtraProperties + .Where(x => !x.Key.Equals(CliConsts.Command, StringComparison.InvariantCultureIgnoreCase)) + .Where(x => !x.Key.Equals(NewCommand.Options.OutputFolder.Long, StringComparison.InvariantCultureIgnoreCase) && + !x.Key.Equals(NewCommand.Options.OutputFolder.Short, StringComparison.InvariantCultureIgnoreCase)) + .Where(x => !x.Key.Equals(NewCommand.Options.Version.Long, StringComparison.InvariantCultureIgnoreCase) && + !x.Key.Equals(NewCommand.Options.Version.Short, StringComparison.InvariantCultureIgnoreCase)) + .Where(x => !x.Key.Equals(NewCommand.Options.TemplateSource.Short, StringComparison.InvariantCultureIgnoreCase) && + !x.Key.Equals(NewCommand.Options.TemplateSource.Long, StringComparison.InvariantCultureIgnoreCase)) + .Select(x => x.Key).ToList(); + + await CliAnalyticsCollect.CollectAsync(new CliAnalyticsCollectInputDto + { + Tool = Options.ToolName, + Command = args.ExtraProperties.ContainsKey(CliConsts.Command) ? args.ExtraProperties[CliConsts.Command] : "", + DatabaseProvider = null, + IsTiered = null, + UiFramework = null, + Options = JsonSerializer.Serialize(options), + ProjectName = null, + TemplateName = args.TemplateName, + TemplateVersion = templateFile.Version + }); + + return new ProjectBuildResult(context.Result.ZipContent, args.TemplateName); + } + + private async Task GetPackageInfoAsync(ProjectBuildArgs args) + { + return await NpmPackageInfoProvider.GetAsync(args.TemplateName); + } + } +} diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/NugetPackageInfoProvider.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/NugetPackageInfoProvider.cs index 06c39e66f7..af1bee7d23 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/NugetPackageInfoProvider.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/NugetPackageInfoProvider.cs @@ -49,7 +49,7 @@ namespace Volo.Abp.Cli.ProjectBuilding var client = _cliHttpClientFactory.CreateClient(); using (var responseMessage = await client.GetAsync( - $"{CliUrls.WwwAbpIo}api/download/packages/", + $"{CliUrls.WwwAbpIo}api/download/nugetPackages/", CancellationTokenProvider.Token )) { diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/PackageProjectBuilder.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/NugetPackageProjectBuilder.cs similarity index 90% rename from framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/PackageProjectBuilder.cs rename to framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/NugetPackageProjectBuilder.cs index 04518332a4..c68a45e080 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/PackageProjectBuilder.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/NugetPackageProjectBuilder.cs @@ -14,9 +14,9 @@ using Volo.Abp.Json; namespace Volo.Abp.Cli.ProjectBuilding { - public class PackageProjectBuilder : IProjectBuilder, ITransientDependency + public class NugetPackageProjectBuilder : IProjectBuilder, ITransientDependency { - public ILogger Logger { get; set; } + public ILogger Logger { get; set; } protected ISourceCodeStore SourceCodeStore { get; } protected INugetPackageInfoProvider NugetPackageInfoProvider { get; } protected ICliAnalyticsCollect CliAnalyticsCollect { get; } @@ -24,7 +24,7 @@ namespace Volo.Abp.Cli.ProjectBuilding protected IJsonSerializer JsonSerializer { get; } protected IApiKeyService ApiKeyService { get; } - public PackageProjectBuilder(ISourceCodeStore sourceCodeStore, + public NugetPackageProjectBuilder(ISourceCodeStore sourceCodeStore, INugetPackageInfoProvider nugetPackageInfoProvider, ICliAnalyticsCollect cliAnalyticsCollect, IOptions options, @@ -38,7 +38,7 @@ namespace Volo.Abp.Cli.ProjectBuilding JsonSerializer = jsonSerializer; ApiKeyService = apiKeyService; - Logger = NullLogger.Instance; + Logger = NullLogger.Instance; } public async Task BuildAsync(ProjectBuildArgs args) @@ -47,7 +47,7 @@ namespace Volo.Abp.Cli.ProjectBuilding var templateFile = await SourceCodeStore.GetAsync( args.TemplateName, - SourceCodeTypes.Package, + SourceCodeTypes.NugetPackage, args.Version, null, args.ExtraProperties.ContainsKey(GetSourceCommand.Options.Preview.Long) @@ -68,11 +68,12 @@ namespace Volo.Abp.Cli.ProjectBuilding null, null, packageInfo, + null, templateFile, args ); - PackageProjectBuildPipelineBuilder.Build(context).Execute(); + NugetPackageProjectBuildPipelineBuilder.Build(context).Execute(); // Exclude unwanted or known options. var options = args.ExtraProperties diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/SourceCodeTypes.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/SourceCodeTypes.cs index 813ffe6b54..014f1959f0 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/SourceCodeTypes.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/SourceCodeTypes.cs @@ -6,6 +6,8 @@ public const string Module = "module"; - public const string Package = "package"; + public const string NugetPackage = "nugetPackage"; + + public const string NpmPackage = "npmPackage"; } } diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/TemplateProjectBuilder.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/TemplateProjectBuilder.cs index 5823c3c488..0f224a20cb 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/TemplateProjectBuilder.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/TemplateProjectBuilder.cs @@ -106,6 +106,7 @@ namespace Volo.Abp.Cli.ProjectBuilding templateInfo, null, null, + null, templateFile, args ); diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/AngularModuleSourceCodeAdder.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/AngularSourceCodeAdder.cs similarity index 90% rename from framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/AngularModuleSourceCodeAdder.cs rename to framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/AngularSourceCodeAdder.cs index 08912f2f25..fdd4d58652 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/AngularModuleSourceCodeAdder.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/AngularSourceCodeAdder.cs @@ -11,16 +11,16 @@ using Volo.Abp.DependencyInjection; namespace Volo.Abp.Cli.ProjectModification { - public class AngularModuleSourceCodeAdder : ITransientDependency + public class AngularSourceCodeAdder : ITransientDependency { public ILogger Logger { get; set; } - public AngularModuleSourceCodeAdder() + public AngularSourceCodeAdder() { Logger = NullLogger.Instance; } - public async Task AddAsync(string solutionFilePath, string angularPath) + public async Task AddFromModuleAsync(string solutionFilePath, string angularPath) { try { @@ -36,7 +36,7 @@ namespace Volo.Abp.Cli.ProjectModification await AddPathsToTsConfigAsync(angularPath, angularProjectsPath, projects); await CreateTsConfigProdJsonAsync(angularPath); await AddScriptsToPackageJsonAsync(angularPath); - await AddProjectToAngularJsonAsync(angularPath, projects); + await AddProjectsToAngularJsonAsync(angularPath, projects); } catch (Exception e) { @@ -44,7 +44,29 @@ namespace Volo.Abp.Cli.ProjectModification } } - private async Task AddProjectToAngularJsonAsync(string angularPath, List projects) + public async Task AddAsync(string angularPath, NpmPackageInfo package) + { + try + { + var angularProjectsPath = Path.Combine(angularPath, "projects"); + + var projects = new List + { + package.Name.RemovePreFix("@").Replace("/","-") + }; + + await AddPathsToTsConfigAsync(angularPath, angularProjectsPath, projects); + await CreateTsConfigProdJsonAsync(angularPath); + await AddScriptsToPackageJsonAsync(angularPath); + await AddProjectsToAngularJsonAsync(angularPath, projects); + } + catch (Exception e) + { + Logger.LogError("Unable to add angular source code: " + e.Message + Environment.NewLine + e.StackTrace); + } + } + + private async Task AddProjectsToAngularJsonAsync(string angularPath, List projects) { var angularJsonFilePath = Path.Combine(angularPath, "angular.json"); var fileContent = File.ReadAllText(angularJsonFilePath); diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/ProjectNpmPackageAdder.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/ProjectNpmPackageAdder.cs index 7ed15f7b86..a0ee3b463a 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/ProjectNpmPackageAdder.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/ProjectNpmPackageAdder.cs @@ -1,23 +1,107 @@ -using System.IO; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; using System.Threading.Tasks; +using System.Xml; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; +using Volo.Abp.Cli.Args; +using Volo.Abp.Cli.Commands; +using Volo.Abp.Cli.Commands.Services; +using Volo.Abp.Cli.Http; +using Volo.Abp.Cli.ProjectBuilding; using Volo.Abp.Cli.Utils; using Volo.Abp.DependencyInjection; using Volo.Abp.IO; +using Volo.Abp.Json; namespace Volo.Abp.Cli.ProjectModification { public class ProjectNpmPackageAdder : ITransientDependency { + public IJsonSerializer JsonSerializer { get; } + public SourceCodeDownloadService SourceCodeDownloadService { get; } + public AngularSourceCodeAdder AngularSourceCodeAdder { get; } + public IRemoteServiceExceptionHandler RemoteServiceExceptionHandler { get; } + private readonly CliHttpClientFactory _cliHttpClientFactory; public ILogger Logger { get; set; } - public ProjectNpmPackageAdder() + public ProjectNpmPackageAdder(CliHttpClientFactory cliHttpClientFactory, + IJsonSerializer jsonSerializer, + SourceCodeDownloadService sourceCodeDownloadService, + AngularSourceCodeAdder angularSourceCodeAdder, + IRemoteServiceExceptionHandler remoteServiceExceptionHandler) { + JsonSerializer = jsonSerializer; + SourceCodeDownloadService = sourceCodeDownloadService; + AngularSourceCodeAdder = angularSourceCodeAdder; + RemoteServiceExceptionHandler = remoteServiceExceptionHandler; + _cliHttpClientFactory = cliHttpClientFactory; Logger = NullLogger.Instance; } - public Task AddAsync(string directory, NpmPackageInfo npmPackage, bool skipGulpCommand = false) + public async Task AddAngularPackageAsync(string directory, string npmPackageName, string version = null, bool withSourceCode = false) + { + await AddAngularPackageAsync( + directory, + await FindNpmPackageInfoAsync(npmPackageName), + version, + withSourceCode + ); + } + + public async Task AddAngularPackageAsync(string directory, NpmPackageInfo npmPackage, string version = null, bool withSourceCode = false) + { + var packageJsonFilePath = Path.Combine(directory, "package.json"); + if (!File.Exists(packageJsonFilePath)) + { + Logger.LogError($"package.json not found!"); + return; + } + + Logger.LogInformation($"Installing '{npmPackage.Name}' package to the project '{packageJsonFilePath}'..."); + + if (!File.ReadAllText(packageJsonFilePath).Contains($"\"{npmPackage.Name}\"")) + { + var versionPostfix = version != null ? $"@{version}" : string.Empty; + + using (DirectoryHelper.ChangeCurrentDirectory(directory)) + { + Logger.LogInformation("yarn add " + npmPackage.Name + versionPostfix); + CmdHelper.RunCmd("yarn add " + npmPackage.Name + versionPostfix); + } + } + else + { + Logger.LogInformation($"'{npmPackage.Name}' is already installed."); + } + + if (withSourceCode) + { + await DownloadAngularSourceCode(directory, npmPackage, version); + await AngularSourceCodeAdder.AddAsync(directory, npmPackage); + } + } + + protected virtual async Task DownloadAngularSourceCode(string angularDirectory, NpmPackageInfo package, string version = null) + { + var targetFolder = Path.Combine(angularDirectory, "projects", package.Name.RemovePreFix("@").Replace("/","-")); + + if (Directory.Exists(targetFolder)) + { + Directory.Delete(targetFolder, true); + } + + await SourceCodeDownloadService.DownloadNpmPackageAsync( + package.Name, + targetFolder, + version + ); + } + + public Task AddMvcPackageAsync(string directory, NpmPackageInfo npmPackage, string version = null, bool skipGulpCommand = false) { var packageJsonFilePath = Path.Combine(directory, "package.json"); if (!File.Exists(packageJsonFilePath) || @@ -29,11 +113,12 @@ namespace Volo.Abp.Cli.ProjectModification Logger.LogInformation($"Installing '{npmPackage.Name}' package to the project '{packageJsonFilePath}'..."); + var versionPostfix = version != null ? $"@{version}" : string.Empty; using (DirectoryHelper.ChangeCurrentDirectory(directory)) { - Logger.LogInformation("yarn add " + npmPackage.Name); - CmdHelper.RunCmd("yarn add " + npmPackage.Name); + Logger.LogInformation("yarn add " + npmPackage.Name + versionPostfix); + CmdHelper.RunCmd("yarn add " + npmPackage.Name + versionPostfix); if (skipGulpCommand) { @@ -46,5 +131,28 @@ namespace Volo.Abp.Cli.ProjectModification return Task.CompletedTask; } + + private async Task FindNpmPackageInfoAsync(string packageName) + { + + var url = $"{CliUrls.WwwAbpIo}api/app/npmPackage/byName/?name=" + packageName; + var client = _cliHttpClientFactory.CreateClient(); + + using (var response = await client.GetAsync(url, _cliHttpClientFactory.GetCancellationToken())) + { + if (!response.IsSuccessStatusCode) + { + if (response.StatusCode == HttpStatusCode.NotFound) + { + throw new CliUsageException($"'{packageName}' npm package could not be found!"); + } + + await RemoteServiceExceptionHandler.EnsureSuccessfulHttpResponseAsync(response); + } + + var responseContent = await response.Content.ReadAsStringAsync(); + return JsonSerializer.Deserialize(responseContent); + } + } } } diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/ProjectNugetPackageAdder.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/ProjectNugetPackageAdder.cs index 8b08775c93..7d9f81056f 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/ProjectNugetPackageAdder.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/ProjectNugetPackageAdder.cs @@ -194,7 +194,7 @@ namespace Volo.Abp.Cli.ProjectModification protected virtual async Task DownloadSourceCode(string targetFolder, NugetPackageInfo package, string version = null) { - await SourceCodeDownloadService.DownloadPackageAsync( + await SourceCodeDownloadService.DownloadNugetPackageAsync( package.Name, targetFolder, version diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/SolutionModuleAdder.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/SolutionModuleAdder.cs index c3b13d7e07..4e66954dcc 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/SolutionModuleAdder.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/SolutionModuleAdder.cs @@ -27,7 +27,7 @@ namespace Volo.Abp.Cli.ProjectModification public SourceCodeDownloadService SourceCodeDownloadService { get; } public SolutionFileModifier SolutionFileModifier { get; } public NugetPackageToLocalReferenceConverter NugetPackageToLocalReferenceConverter { get; } - public AngularModuleSourceCodeAdder AngularModuleSourceCodeAdder { get; } + public AngularSourceCodeAdder AngularSourceCodeAdder { get; } public NewCommand NewCommand { get; } public BundleCommand BundleCommand { get; } @@ -55,7 +55,7 @@ namespace Volo.Abp.Cli.ProjectModification SourceCodeDownloadService sourceCodeDownloadService, SolutionFileModifier solutionFileModifier, NugetPackageToLocalReferenceConverter nugetPackageToLocalReferenceConverter, - AngularModuleSourceCodeAdder angularModuleSourceCodeAdder, + AngularSourceCodeAdder angularSourceCodeAdder, NewCommand newCommand, BundleCommand bundleCommand, CliHttpClientFactory cliHttpClientFactory) @@ -71,7 +71,7 @@ namespace Volo.Abp.Cli.ProjectModification SourceCodeDownloadService = sourceCodeDownloadService; SolutionFileModifier = solutionFileModifier; NugetPackageToLocalReferenceConverter = nugetPackageToLocalReferenceConverter; - AngularModuleSourceCodeAdder = angularModuleSourceCodeAdder; + AngularSourceCodeAdder = angularSourceCodeAdder; NewCommand = newCommand; BundleCommand = bundleCommand; _cliHttpClientFactory = cliHttpClientFactory; @@ -327,7 +327,7 @@ namespace Volo.Abp.Cli.ProjectModification { foreach (var npmPackage in angularPackages) { - await ProjectNpmPackageAdder.AddAsync(angularPath, npmPackage, true); + await ProjectNpmPackageAdder.AddAngularPackageAsync(angularPath, npmPackage); } } } @@ -347,7 +347,7 @@ namespace Volo.Abp.Cli.ProjectModification MoveAngularFolderInNewTemplate(modulesFolderInSolution, moduleName); } - await AngularModuleSourceCodeAdder.AddAsync(solutionFilePath, angularPath); + await AngularSourceCodeAdder.AddFromModuleAsync(solutionFilePath, angularPath); } private static void DeleteAngularDirectoriesInModulesFolder(string modulesFolderInSolution) @@ -490,7 +490,7 @@ namespace Volo.Abp.Cli.ProjectModification { foreach (var npmPackage in mvcNpmPackages) { - await ProjectNpmPackageAdder.AddAsync(Path.GetDirectoryName(targetProject), npmPackage); + await ProjectNpmPackageAdder.AddMvcPackageAsync(Path.GetDirectoryName(targetProject), npmPackage, null, true); } } }