From 2ab8b01156667802e384f4c6e1f8112265ad9c19 Mon Sep 17 00:00:00 2001 From: sinclair-aefinder <156668112+sinclair-aefinder@users.noreply.github.com> Date: Wed, 9 Oct 2024 10:42:52 +0800 Subject: [PATCH] Feature: Deployment supports attachment uploading (#7) * feat: supports attachment * feat: separately updated attachments are supported --- .../Options/CommonOptions.cs | 4 +- .../Options/DeployAppOptions.cs | 9 +++-- .../Options/InitAppOptions.cs | 2 +- .../Options/UpdateAppOptions.cs | 12 ++++-- src/AeFinder.Cli.Core/Services/AppService.cs | 38 +++++++++++++++---- .../Services/DevelopmentTemplateAppService.cs | 4 +- 6 files changed, 51 insertions(+), 18 deletions(-) diff --git a/src/AeFinder.Cli.Core/Options/CommonOptions.cs b/src/AeFinder.Cli.Core/Options/CommonOptions.cs index 047ce03..7eb77f9 100644 --- a/src/AeFinder.Cli.Core/Options/CommonOptions.cs +++ b/src/AeFinder.Cli.Core/Options/CommonOptions.cs @@ -4,10 +4,10 @@ namespace AeFinder.Cli.Options; public class CommonOptions { - [Option(longName: "appid", Required = true, HelpText = "The appid of the AeFinder App.")] + [Option(longName: "appid", Required = true, HelpText = "The appid of the AeIndexer.")] public string AppId { get; set; } - [Option(longName: "key", Required = true, HelpText = "The deploy key of the AeFinder App.")] + [Option(longName: "key", Required = true, HelpText = "The deploy key of the AeIndexer.")] public string Key { get; set; } [Option(longName: "network", Required = false, Default = AeFinderNetwork.MainNet, HelpText = "The AeFinder network (MainNet or TestNet).")] diff --git a/src/AeFinder.Cli.Core/Options/DeployAppOptions.cs b/src/AeFinder.Cli.Core/Options/DeployAppOptions.cs index c1d9ce5..9925479 100644 --- a/src/AeFinder.Cli.Core/Options/DeployAppOptions.cs +++ b/src/AeFinder.Cli.Core/Options/DeployAppOptions.cs @@ -2,12 +2,15 @@ namespace AeFinder.Cli.Options; -[Verb("deploy", HelpText = "Deploy AeFinder App.")] +[Verb("deploy", HelpText = "Deploy AeIndexer.")] public class DeployAppOptions : CommonOptions { - [Option(longName: "code", Required = true, HelpText = "The code file path of your AeFinder App.")] + [Option(longName: "code", Required = true, HelpText = "The code file path of your AeIndexer.")] public string Code { get; set; } - [Option(longName: "manifest", Required = true, HelpText = "The manifest file path of your AeFinder App.")] + [Option(longName: "manifest", Required = true, HelpText = "The manifest file path of your AeIndexer.")] public string Manifest { get; set; } + + [Option(longName: "attachments", Required = false, HelpText = "The Attachment list path of your AeIndexer.")] + public IEnumerable Attachments { get; set; } } \ No newline at end of file diff --git a/src/AeFinder.Cli.Core/Options/InitAppOptions.cs b/src/AeFinder.Cli.Core/Options/InitAppOptions.cs index a955788..eb9df32 100644 --- a/src/AeFinder.Cli.Core/Options/InitAppOptions.cs +++ b/src/AeFinder.Cli.Core/Options/InitAppOptions.cs @@ -2,7 +2,7 @@ namespace AeFinder.Cli.Options; -[Verb("init", HelpText = "Init AeFinder App development project.")] +[Verb("init", HelpText = "Init AeIndexer development project.")] public class InitAppOptions : CommonOptions { [Option(longName: "name", Required = true, HelpText = "The project name.")] diff --git a/src/AeFinder.Cli.Core/Options/UpdateAppOptions.cs b/src/AeFinder.Cli.Core/Options/UpdateAppOptions.cs index abb7abe..6e16856 100644 --- a/src/AeFinder.Cli.Core/Options/UpdateAppOptions.cs +++ b/src/AeFinder.Cli.Core/Options/UpdateAppOptions.cs @@ -2,15 +2,21 @@ namespace AeFinder.Cli.Options; -[Verb("update", HelpText = "Update AeFinder App.")] +[Verb("update", HelpText = "Update AeIndexer.")] public class UpdateAppOptions : CommonOptions { - [Option(longName: "code", Required = false, HelpText = "The code file path of your AeFinder App.")] + [Option(longName: "code", Required = false, HelpText = "The code file path of your AeIndexer.")] public string Code { get; set; } - [Option(longName: "manifest", Required = false, HelpText = "The manifest file path of your AeFinder App.")] + [Option(longName: "manifest", Required = false, HelpText = "The manifest file path of your AeIndexer.")] public string Manifest { get; set; } [Option(longName: "version", Required = true, HelpText = "The version to update.")] public string Version { get; set; } + + [Option(longName: "delete-attachments", Required = false, HelpText = "The attachment keys to delete.")] + public IEnumerable DeleteAttachmentKeys { get; set; } + + [Option(longName: "attachments", Required = false, HelpText = "The attachment path list.")] + public IEnumerable Attachments { get; set; } } \ No newline at end of file diff --git a/src/AeFinder.Cli.Core/Services/AppService.cs b/src/AeFinder.Cli.Core/Services/AppService.cs index 67ab87a..b9d6b86 100644 --- a/src/AeFinder.Cli.Core/Services/AppService.cs +++ b/src/AeFinder.Cli.Core/Services/AppService.cs @@ -30,7 +30,7 @@ public AppService(IAuthService authService, public async Task DeployAsync(DeployAppOptions options) { - _logger.LogInformation("Deploying app..."); + _logger.LogInformation("Deploying AeIndexer..."); var token = await _authService.GetAccessTokenAsync(options.Network, options.AppId, options.Key); var url = $"{CliConsts.AeFinderEndpoints[options.Network].ApiEndpoint}api/apps/subscriptions"; @@ -39,6 +39,10 @@ public async Task DeployAsync(DeployAppOptions options) var formDataContent = new MultipartFormDataContent(); formDataContent.Add(new StringContent(await File.ReadAllTextAsync(options.Manifest)), "Manifest"); formDataContent.Add(new StreamContent(new MemoryStream(await File.ReadAllBytesAsync(options.Code))), "Code", "code.dll"); + foreach (var path in options.Attachments) + { + formDataContent.Add(new StreamContent(new MemoryStream(await File.ReadAllBytesAsync(path))), "AttachmentList", Path.GetFileName(path)); + } using var response = await client.PostAsync( url, @@ -49,14 +53,16 @@ public async Task DeployAsync(DeployAppOptions options) await _remoteServiceExceptionHandler.EnsureSuccessfulHttpResponseAsync(response); var responseContent = await response.Content.ReadAsStringAsync(); - _logger.LogInformation("Deploy app successfully. Version: {Version}", responseContent); + _logger.LogInformation("Deploy AeIndexer successfully. Version: {Version}", responseContent); } public async Task UpdateAsync(UpdateAppOptions options) { var token = await _authService.GetAccessTokenAsync(options.Network, options.AppId, options.Key); - if (!options.Code.IsNullOrWhiteSpace()) + if (!options.Code.IsNullOrWhiteSpace() || + (options.DeleteAttachmentKeys != null && options.DeleteAttachmentKeys.Any()) || + (options.Attachments != null && options.Attachments.Any())) { await UpdateCodeAsync(options, token); } @@ -69,7 +75,7 @@ public async Task UpdateAsync(UpdateAppOptions options) private async Task UpdateCodeAsync(UpdateAppOptions options, string token) { - _logger.LogInformation("Updating app code..."); + _logger.LogInformation("Updating AeIndexer code..."); var url = $"{CliConsts.AeFinderEndpoints[options.Network].ApiEndpoint}api/apps/subscriptions/code/{options.Version}"; @@ -77,7 +83,25 @@ private async Task UpdateCodeAsync(UpdateAppOptions options, string token) var client = _cliHttpClientFactory.CreateClient(token); var formDataContent = new MultipartFormDataContent(); - formDataContent.Add(new StreamContent(new MemoryStream(await File.ReadAllBytesAsync(options.Code))), "Code", "code.dll"); + if (!options.Code.IsNullOrWhiteSpace()) + { + formDataContent.Add(new StreamContent(new MemoryStream(await File.ReadAllBytesAsync(options.Code))), "Code", + "code.dll"); + } + + if (options.DeleteAttachmentKeys != null && options.DeleteAttachmentKeys.Any()) + { + formDataContent.Add(new StringContent(options.DeleteAttachmentKeys.JoinAsString(",")), "AttachmentDeleteFileKeyList"); + } + + if (options.Attachments != null) + { + foreach (var path in options.Attachments) + { + formDataContent.Add(new StreamContent(new MemoryStream(await File.ReadAllBytesAsync(path))), + "AttachmentList", Path.GetFileName(path)); + } + } using var response = await client.PutAsync( url, @@ -87,12 +111,12 @@ private async Task UpdateCodeAsync(UpdateAppOptions options, string token) await _remoteServiceExceptionHandler.EnsureSuccessfulHttpResponseAsync(response); - _logger.LogInformation("Update code successfully."); + _logger.LogInformation("Update AeIndexer successfully."); } private async Task UpdateManifestAsync(UpdateAppOptions options, string token) { - _logger.LogInformation("Updating app manifest..."); + _logger.LogInformation("Updating AeIndexer manifest..."); var url = $"{CliConsts.AeFinderEndpoints[options.Network].ApiEndpoint}api/apps/subscriptions/manifest/{options.Version}"; diff --git a/src/AeFinder.Cli.Core/Services/DevelopmentTemplateAppService.cs b/src/AeFinder.Cli.Core/Services/DevelopmentTemplateAppService.cs index c696c41..8e1c435 100644 --- a/src/AeFinder.Cli.Core/Services/DevelopmentTemplateAppService.cs +++ b/src/AeFinder.Cli.Core/Services/DevelopmentTemplateAppService.cs @@ -36,7 +36,7 @@ public DevelopmentTemplateAppService(IAuthService authService, IJsonSerializer j public async Task CreateProjectAsync(InitAppOptions options) { - _logger.LogInformation("Initialize app..."); + _logger.LogInformation("Initialize AeIndexer..."); CheckOptions(options); @@ -66,7 +66,7 @@ public async Task CreateProjectAsync(InitAppOptions options) var result = await response.Content.ReadAsStreamAsync(); ZipHelper.UnZip(result, options.Directory); - _logger.LogInformation("The AeFinder App: {App} is initialized successfully. Directory: {Directory}", options.Name, options.Directory); + _logger.LogInformation("The AeIndexer: {App} is initialized successfully. Directory: {Directory}", options.Name, options.Directory); } private static void CheckOptions(InitAppOptions options)