diff --git a/Frends.FTP.DownloadFiles/CHANGELOG.md b/Frends.FTP.DownloadFiles/CHANGELOG.md index 29b123e..ad98f37 100644 --- a/Frends.FTP.DownloadFiles/CHANGELOG.md +++ b/Frends.FTP.DownloadFiles/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## [1.1.2] - 2024-01-16 +### Improved +- Improved Operations log by adding more logging steps. + ## [1.1.1] - 2024-01-04 ### Added - Added setup for FtpClient.ReadTimeout, FtpClient.DataConnectionConnectTimeout and FtpClient.DataConnectionReadTimeout which were all defaulting to 15 seconds. diff --git a/Frends.FTP.DownloadFiles/Frends.FTP.DownloadFiles.Tests/DownloadFilesTests.cs b/Frends.FTP.DownloadFiles/Frends.FTP.DownloadFiles.Tests/DownloadFilesTests.cs index 7ca68fa..d3a62e9 100644 --- a/Frends.FTP.DownloadFiles/Frends.FTP.DownloadFiles.Tests/DownloadFilesTests.cs +++ b/Frends.FTP.DownloadFiles/Frends.FTP.DownloadFiles.Tests/DownloadFilesTests.cs @@ -172,8 +172,7 @@ public void DownloadFTPS_IncorrectFingerprint() // Test and assert var ex = Assert.Throws(() => { - var result = FTP.DownloadFiles(source, destination, connection, new Options(), new Info(), - new CancellationToken()); + FTP.DownloadFiles(source, destination, connection, new Options(), new Info(), default); }); @@ -208,8 +207,7 @@ public void DownloadFTPS_CurrentUserHasNoCertificates() var ex = Assert.Throws(() => { - var result = FTP.DownloadFiles(source, destination, connection, new Options(), new Info(), - new CancellationToken()); + FTP.DownloadFiles(source, destination, connection, new Options(), new Info(), default); }); @@ -221,7 +219,7 @@ public void DownloadFTPS_CurrentUserHasNoCertificates() [Test] public void DownloadFTP_LargeFiles() { - FtpHelper.CreateLargeFileOnFTP(FtpDir, 5); + FtpHelper.CreateLargeFileOnFTP(FtpDir, 1); var source = new Source { Directory = FtpDir, FileName = "*.bin", Operation = SourceOperation.Delete }; var destination = new Destination { Directory = LocalDirFullPath, Action = DestinationAction.Overwrite }; var connection = new Connection @@ -235,7 +233,7 @@ public void DownloadFTP_LargeFiles() // Test and assert var result = FTP.DownloadFiles(source, destination, connection, new Options(), new Info(), new CancellationToken()); Assert.IsTrue(result.Success, result.UserResultMessage); - Assert.AreEqual(5, result.SuccessfulTransferCount); + Assert.AreEqual(1, result.SuccessfulTransferCount); } } } \ No newline at end of file diff --git a/Frends.FTP.DownloadFiles/Frends.FTP.DownloadFiles.Tests/Lib/DownloadFilesTestBase.cs b/Frends.FTP.DownloadFiles/Frends.FTP.DownloadFiles.Tests/Lib/DownloadFilesTestBase.cs index 5eb5272..a7368da 100644 --- a/Frends.FTP.DownloadFiles/Frends.FTP.DownloadFiles.Tests/Lib/DownloadFilesTestBase.cs +++ b/Frends.FTP.DownloadFiles/Frends.FTP.DownloadFiles.Tests/Lib/DownloadFilesTestBase.cs @@ -20,6 +20,7 @@ public void OneTimeSetUp() public void OneTimeTearDown() { FtpHelper.Dispose(); + FtpHelper.RemoveLocalTestFiles(); } protected bool LocalFileExists(string fileName, string subDir = null) diff --git a/Frends.FTP.DownloadFiles/Frends.FTP.DownloadFiles.Tests/Lib/FtpHelper.cs b/Frends.FTP.DownloadFiles/Frends.FTP.DownloadFiles.Tests/Lib/FtpHelper.cs index 7ed6f3d..24ad2c5 100644 --- a/Frends.FTP.DownloadFiles/Frends.FTP.DownloadFiles.Tests/Lib/FtpHelper.cs +++ b/Frends.FTP.DownloadFiles/Frends.FTP.DownloadFiles.Tests/Lib/FtpHelper.cs @@ -24,7 +24,6 @@ private FtpClient Client get { client ??= new FtpClient(FtpHost, FtpPort, FtpUsername, FtpPassword); - //client.connConnect(); return client; } } @@ -118,4 +117,13 @@ public void DeleteDirectoryOnFTP(string ftpDir) { client.DeleteDirectory(ftpDir, FtpListOption.Recursive); } + + public static void RemoveLocalTestFiles() + { + var path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "../../../DockerVolumes/", "data"); + foreach (var dir in Directory.GetDirectories(path)) + { + Directory.Delete(dir, true); + } + } } \ No newline at end of file diff --git a/Frends.FTP.DownloadFiles/Frends.FTP.DownloadFiles.Tests/MacrosTests.cs b/Frends.FTP.DownloadFiles/Frends.FTP.DownloadFiles.Tests/MacrosTests.cs index 62fb657..b58096e 100644 --- a/Frends.FTP.DownloadFiles/Frends.FTP.DownloadFiles.Tests/MacrosTests.cs +++ b/Frends.FTP.DownloadFiles/Frends.FTP.DownloadFiles.Tests/MacrosTests.cs @@ -1,109 +1,109 @@ -using System; -using System.IO; -using System.Threading; -using Frends.FTP.DownloadFiles.Enums; -using Frends.FTP.DownloadFiles.TaskConfiguration; -using Frends.FTP.DownloadFiles.TaskResult; -using Frends.FTP.DownloadFiles.Tests.Lib; -using NUnit.Framework; - -namespace Frends.FTP.DownloadFiles.Tests; - -/// -/// We have several places that support macros: -/// - source dir -/// - destination dir -/// - destination file name -/// -/// NOTE: -/// - source file name does not support macros for some reason in Cobalt, currently not implementing because -/// need to understand reasoning better -/// -[TestFixture] -public class MacrosTests : DownloadFilesTestBase -{ - [Test] - public void MacrosWorkInSourceDirectory() - { - // Setup - var year = DateTime.Now.Year; +using System; +using System.IO; +using System.Threading; +using Frends.FTP.DownloadFiles.Enums; +using Frends.FTP.DownloadFiles.TaskConfiguration; +using Frends.FTP.DownloadFiles.TaskResult; +using Frends.FTP.DownloadFiles.Tests.Lib; +using NUnit.Framework; + +namespace Frends.FTP.DownloadFiles.Tests; + +/// +/// We have several places that support macros: +/// - source dir +/// - destination dir +/// - destination file name +/// +/// NOTE: +/// - source file name does not support macros for some reason in Cobalt, currently not implementing because +/// need to understand reasoning better +/// +[TestFixture] +public class MacrosTests : DownloadFilesTestBase +{ + [Test] + public void MacrosWorkInSourceDirectory() + { + // Setup + var year = DateTime.Now.Year; FtpHelper.CreateFileOnFTP($"dir{year}", "file1.txt"); - var result = CallDownloadFiles( - "dir%Year%", - "file*.txt", + var result = CallDownloadFiles( + "dir%Year%", + "file*.txt", LocalDirFullPath); - Assert.IsTrue(result.Success, result.UserResultMessage); - Assert.AreEqual(1, result.SuccessfulTransferCount); - Assert.IsTrue(LocalFileExists("file1.txt")); + Assert.IsTrue(result.Success, result.UserResultMessage); + Assert.AreEqual(1, result.SuccessfulTransferCount); + Assert.IsTrue(LocalFileExists("file1.txt")); } - [Test] - public void MacrosWorkInDestinationDirectory() - { - var year = DateTime.Now.Year; - var guid = Guid.NewGuid().ToString(); - FtpHelper.CreateFileOnFTP(FtpDir, "file1.txt"); - var destinationDirWithMacros = Path.Combine(Path.GetTempPath(), $"transfer-%Year%-{guid}"); + [Test] + public void MacrosWorkInDestinationDirectory() + { + var year = DateTime.Now.Year; + var guid = Guid.NewGuid().ToString(); + FtpHelper.CreateFileOnFTP(FtpDir, "file1.txt"); + var destinationDirWithMacros = Path.Combine(Path.GetTempPath(), $"transfer-%Year%-{guid}"); var destinationDirWithMacrosExpanded = Path.Combine(Path.GetTempPath(), $"transfer-{year}-{guid}"); - var result = CallDownloadFiles( - FtpDir, - "file1.txt", + var result = CallDownloadFiles( + FtpDir, + "file1.txt", destinationDirWithMacros); - Assert.IsTrue(result.Success, result.UserResultMessage); - Assert.AreEqual(1, result.SuccessfulTransferCount); - Assert.IsTrue(File.Exists(Path.Combine(destinationDirWithMacrosExpanded, "file1.txt")), result.UserResultMessage); + Assert.IsTrue(result.Success, result.UserResultMessage); + Assert.AreEqual(1, result.SuccessfulTransferCount); + Assert.IsTrue(File.Exists(Path.Combine(destinationDirWithMacrosExpanded, "file1.txt")), result.UserResultMessage); } - [Test] - public void MacrosWorkInDestinationFilename() - { - var year = DateTime.Now.Year; - var guid = Guid.NewGuid().ToString(); - FtpHelper.CreateFileOnFTP(FtpDir, "file1.txt"); - var destinationFileNameWithMacros = $"f-%Year%-%SourceFileName%-{guid}"; + [Test] + public void MacrosWorkInDestinationFilename() + { + var year = DateTime.Now.Year; + var guid = Guid.NewGuid().ToString(); + FtpHelper.CreateFileOnFTP(FtpDir, "file1.txt"); + var destinationFileNameWithMacros = $"f-%Year%-%SourceFileName%-{guid}"; var destinationFileNameWithMacrosExpanded = $"f-{year}-file1-{guid}"; - var result = CallDownloadFiles( - FtpDir, - "file1.txt", - LocalDirFullPath, + var result = CallDownloadFiles( + FtpDir, + "file1.txt", + LocalDirFullPath, destinationFileNameWithMacros); - Assert.IsTrue(result.Success, result.UserResultMessage); - Assert.AreEqual(1, result.SuccessfulTransferCount); - Assert.IsTrue(File.Exists(Path.Combine(LocalDirFullPath, destinationFileNameWithMacrosExpanded)), result.UserResultMessage); + Assert.IsTrue(result.Success, result.UserResultMessage); + Assert.AreEqual(1, result.SuccessfulTransferCount); + Assert.IsTrue(File.Exists(Path.Combine(LocalDirFullPath, destinationFileNameWithMacrosExpanded)), result.UserResultMessage); } - private Result CallDownloadFiles( - string sourceDirectory, - string sourceFileName, - string targetDirectory, - string targetFileName = null, - string moveToDir = null, - string renameTo = null) - { - var source = new Source - { + private static Result CallDownloadFiles( + string sourceDirectory, + string sourceFileName, + string targetDirectory, + string targetFileName = null, + string moveToDir = null, + string renameTo = null) + { + var source = new Source + { Directory = sourceDirectory, - FileName = sourceFileName, - Operation = SourceOperation.Delete, - DirectoryToMoveAfterTransfer = moveToDir, - FileNameAfterTransfer = renameTo - }; + FileName = sourceFileName, + Operation = SourceOperation.Delete, + DirectoryToMoveAfterTransfer = moveToDir, + FileNameAfterTransfer = renameTo + }; var destination = new Destination { Directory = targetDirectory, Action = DestinationAction.Overwrite, FileName = targetFileName - }; - var options = new Options { CreateDestinationDirectories = true, RenameSourceFileBeforeTransfer = true }; - var connection = FtpHelper.GetFtpsConnection(); - - var result = FTP.DownloadFiles(source, destination, connection, options, new Info(), new CancellationToken()); - return result; - } + }; + var options = new Options { CreateDestinationDirectories = true, RenameSourceFileBeforeTransfer = true }; + var connection = FtpHelper.GetFtpsConnection(); + + var result = FTP.DownloadFiles(source, destination, connection, options, new Info(), new CancellationToken()); + return result; + } } \ No newline at end of file diff --git a/Frends.FTP.DownloadFiles/Frends.FTP.DownloadFiles.Tests/SourceFilesNotFoundActionTests.cs b/Frends.FTP.DownloadFiles/Frends.FTP.DownloadFiles.Tests/SourceFilesNotFoundActionTests.cs index 0dd361f..9340d38 100644 --- a/Frends.FTP.DownloadFiles/Frends.FTP.DownloadFiles.Tests/SourceFilesNotFoundActionTests.cs +++ b/Frends.FTP.DownloadFiles/Frends.FTP.DownloadFiles.Tests/SourceFilesNotFoundActionTests.cs @@ -1,80 +1,80 @@ -using System.Linq; -using System.Threading; -using Frends.FTP.DownloadFiles.Enums; -using Frends.FTP.DownloadFiles.TaskConfiguration; -using Frends.FTP.DownloadFiles.TaskResult; -using Frends.FTP.DownloadFiles.Tests.Lib; -using NUnit.Framework; - -namespace Frends.FTP.DownloadFiles.Tests; - -[TestFixture] -public class SourceFilesNotFoundActionTests : DownloadFilesTestBase -{ - [Test] - public void SourceFilesNotFoundAction_Ignore() - { - var result = CallDownloadFiles( - SourceNotFoundAction.Ignore, "file*.txt", +using System.Linq; +using System.Threading; +using Frends.FTP.DownloadFiles.Enums; +using Frends.FTP.DownloadFiles.TaskConfiguration; +using Frends.FTP.DownloadFiles.TaskResult; +using Frends.FTP.DownloadFiles.Tests.Lib; +using NUnit.Framework; + +namespace Frends.FTP.DownloadFiles.Tests; + +[TestFixture] +public class SourceFilesNotFoundActionTests : DownloadFilesTestBase +{ + [Test] + public void SourceFilesNotFoundAction_Ignore() + { + var result = CallDownloadFiles( + SourceNotFoundAction.Ignore, "file*.txt", nameof(SourceFilesNotFoundAction_Ignore)); - Assert.IsTrue(result.Success); + Assert.IsTrue(result.Success); Assert.IsTrue(result.ActionSkipped); // User result should contain message - Assert.IsTrue(result.UserResultMessage.Contains("No source files")); - // Operations logs should NOT contain message - Assert.IsFalse(result.OperationsLog.Any(o => o.Value.Contains("No source files"))); + Assert.IsTrue(result.UserResultMessage.Contains("No source files")); + // Operations logs should NOT contain message + Assert.IsFalse(result.OperationsLog.Any(o => o.Value.Contains("No source files"))); } - [Test] - public void SourceFilesNotFoundAction_Info() - { - var result = CallDownloadFiles( - SourceNotFoundAction.Info, "file*.txt", + [Test] + public void SourceFilesNotFoundAction_Info() + { + var result = CallDownloadFiles( + SourceNotFoundAction.Info, "file*.txt", nameof(SourceFilesNotFoundAction_Info)); - Assert.IsTrue(result.Success); + Assert.IsTrue(result.Success); Assert.IsTrue(result.ActionSkipped); // User result should contain message - Assert.IsTrue(result.UserResultMessage.Contains("No source files")); - // Operations logs should contain message - Assert.IsTrue(result.OperationsLog.Any(o => o.Value.Contains("No source files"))); + Assert.IsTrue(result.UserResultMessage.Contains("No source files")); + // Operations logs should contain message + Assert.IsTrue(result.OperationsLog.Any(o => o.Value.Contains("No source files"))); } - [Test] - public void SourceFilesNotFoundAction_Error() - { - var result = CallDownloadFiles( - SourceNotFoundAction.Error, "file*.txt", + [Test] + public void SourceFilesNotFoundAction_Error() + { + var result = CallDownloadFiles( + SourceNotFoundAction.Error, "file*.txt", nameof(SourceFilesNotFoundAction_Error)); - Assert.IsFalse(result.Success, result.UserResultMessage); + Assert.IsFalse(result.Success, result.UserResultMessage); Assert.IsFalse(result.ActionSkipped, result.UserResultMessage); // User result should contain message - Assert.IsTrue(result.UserResultMessage.Contains("No source files")); - // Operations logs should contain message - Assert.IsTrue(result.OperationsLog.Any(o => o.Value.Contains("No source files"))); + Assert.IsTrue(result.UserResultMessage.Contains("No source files")); + // Operations logs should contain message + Assert.IsTrue(result.OperationsLog.Any(o => o.Value.Contains("No source files"))); } - private Result CallDownloadFiles(SourceNotFoundAction sourceNotFoundAction, string sourceFileName, string targetDir) - { - var source = new Source - { + private Result CallDownloadFiles(SourceNotFoundAction sourceNotFoundAction, string sourceFileName, string targetDir) + { + var source = new Source + { Directory = FtpDir, FileName = sourceFileName, - Operation = SourceOperation.Delete, - NotFoundAction = sourceNotFoundAction - }; + Operation = SourceOperation.Delete, + NotFoundAction = sourceNotFoundAction + }; var destination = new Destination - { Directory = targetDir, Action = DestinationAction.Overwrite }; - var connection = FtpHelper.GetFtpConnection(); - - var result = FTP.DownloadFiles( + { Directory = targetDir, Action = DestinationAction.Overwrite }; + var connection = FtpHelper.GetFtpConnection(); + + var result = FTP.DownloadFiles( source, destination, connection, - new Options { OperationLog = true }, new Info(), new CancellationToken()); - return result; - } + new Options { OperationLog = true }, new Info(), new CancellationToken()); + return result; + } } \ No newline at end of file diff --git a/Frends.FTP.DownloadFiles/Frends.FTP.DownloadFiles.Tests/SourceOperationTests.cs b/Frends.FTP.DownloadFiles/Frends.FTP.DownloadFiles.Tests/SourceOperationTests.cs index cfeb6fe..729f3e2 100644 --- a/Frends.FTP.DownloadFiles/Frends.FTP.DownloadFiles.Tests/SourceOperationTests.cs +++ b/Frends.FTP.DownloadFiles/Frends.FTP.DownloadFiles.Tests/SourceOperationTests.cs @@ -114,6 +114,7 @@ public void SourceOperation_MoveToFolderDoesNotExist_ProducesError() // Check that original file is still there Assert.IsTrue(FtpHelper.FileExistsOnFTP(FtpDir, "file1.txt"), result.UserResultMessage); + FtpHelper.DeleteDirectoryOnFTP(moveToSubDir); } [Test] diff --git a/Frends.FTP.DownloadFiles/Frends.FTP.DownloadFiles/Definitions/FileTransporter.cs b/Frends.FTP.DownloadFiles/Frends.FTP.DownloadFiles/Definitions/FileTransporter.cs index eb1ecad..9a49f8a 100644 --- a/Frends.FTP.DownloadFiles/Frends.FTP.DownloadFiles/Definitions/FileTransporter.cs +++ b/Frends.FTP.DownloadFiles/Frends.FTP.DownloadFiles/Definitions/FileTransporter.cs @@ -43,7 +43,7 @@ public FileTransferResult Run(CancellationToken cancellationToken) if (!client.IsConnected) { - _logger.NotifyError(null, "Error while connecting to FTP: ", new Exception(userResultMessage)); + _logger.NotifyError(_batchContext, "Error while connecting to FTP: ", new Exception(userResultMessage)); return FormFailedFileTransferResult(userResultMessage); } @@ -52,6 +52,7 @@ public FileTransferResult Run(CancellationToken cancellationToken) { // If source directory doesn't exist, modify userResultMessage accordingly. userResultMessage = $"FTP directory '{_sourceDirectoryWithMacrosExpanded}' doesn't exist."; + _logger.NotifyError(_batchContext, userResultMessage, new Exception(userResultMessage)); return FormFailedFileTransferResult(userResultMessage); } @@ -72,6 +73,13 @@ public FileTransferResult Run(CancellationToken cancellationToken) foreach (var file in files) { + // Check that the connection is alive and if not try to connect again + if (!client.IsConnected) + { + client.Connect(); + _logger.NotifyInformation(_batchContext, "Reconnected."); + } + cancellationToken.ThrowIfCancellationRequested(); var singleTransfer = new SingleFileTransfer(file, _batchContext, client, _renamingPolicy, _logger); @@ -86,6 +94,7 @@ public FileTransferResult Run(CancellationToken cancellationToken) catch (SocketException) { userResultMessage = $"Unable to establish the socket: No such host is known."; + _logger.NotifyError(_batchContext, userResultMessage, new Exception(userResultMessage)); return FormFailedFileTransferResult(userResultMessage); } @@ -105,12 +114,11 @@ private bool CreateDestinationDirIfNeeded(out FileTransferResult fileTransferRes } catch (Exception ex) { - userResultMessage = - $"Error while creating destination directory '{_destinationDirectoryWithMacrosExpanded}': {ex.Message}"; - { - fileTransferResult = FormFailedFileTransferResult(userResultMessage); - return false; - } + userResultMessage = $"Error while creating destination directory '{_destinationDirectoryWithMacrosExpanded}': {ex.Message}"; + + fileTransferResult = FormFailedFileTransferResult(userResultMessage); + _logger.NotifyError(_batchContext, userResultMessage, new Exception(userResultMessage)); + return false; } } else @@ -150,7 +158,7 @@ private static FtpClient CreateFtpClient(Connection connect) client.ClientCertificates.Add(new X509Certificate2(connect.ClientCertificatePath)); else { - using (X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser)) + using (var store = new X509Store(StoreName.My, StoreLocation.CurrentUser)) { try { @@ -231,17 +239,12 @@ private Tuple, bool> GetSourceFiles(FtpClient client) var ftpFiles = client.GetListing(_sourceDirectoryWithMacrosExpanded); var list = new List(); - foreach (var ftpFile in ftpFiles) - { - if (ftpFile.Type == FtpFileSystemObjectType.Directory || - ftpFile.Type == FtpFileSystemObjectType.Link) - continue; // skip directories and links - if (!Util.FileMatchesMask(ftpFile.Name, _batchContext.Source.FileName)) continue; + list.AddRange(ftpFiles + .Where(e => e.Type != FtpFileSystemObjectType.Directory + || e.Type != FtpFileSystemObjectType.Link + || !Util.FileMatchesMask(e.Name, _batchContext.Source.FileName)).Select(f => new FileItem(f))); - var fItm = new FileItem(ftpFile); - list.Add(fItm); - } return new Tuple, bool>(list, true); } @@ -296,12 +299,11 @@ private static string GetUserResultMessage(IList resul $"{errorMessages.Count} Errors: {string.Join(", ", errorMessages)}"); var transferredFiles = results.Select(x => x.TransferredFile).Where(x => x != null).ToList(); - if (transferredFiles.Any()) - userResultMessage = MessageJoin(userResultMessage, + userResultMessage = transferredFiles.Any() + ? MessageJoin(userResultMessage, string.Format("{0} files transferred: {1}", transferredFiles.Count, - string.Join(", ", transferredFiles))); - else - userResultMessage = MessageJoin(userResultMessage, "No files transferred."); + string.Join(", ", transferredFiles))) + : MessageJoin(userResultMessage, "No files transferred."); return userResultMessage; } diff --git a/Frends.FTP.DownloadFiles/Frends.FTP.DownloadFiles/Definitions/RenamingPolicy.cs b/Frends.FTP.DownloadFiles/Frends.FTP.DownloadFiles/Definitions/RenamingPolicy.cs index f26b250..9f939eb 100644 --- a/Frends.FTP.DownloadFiles/Frends.FTP.DownloadFiles/Definitions/RenamingPolicy.cs +++ b/Frends.FTP.DownloadFiles/Frends.FTP.DownloadFiles/Definitions/RenamingPolicy.cs @@ -1,263 +1,260 @@ -using System.Text.RegularExpressions; - -namespace Frends.FTP.DownloadFiles.Definitions; - -/// -/// Policies for creating names for remote files: expands macros etc. -/// -internal class RenamingPolicy -{ - private readonly IDictionary> _macroHandlers; - private readonly IDictionary> _sourceFileNameMacroHandlers; - - public RenamingPolicy(string transferName, Guid transferId) - { - _macroHandlers = InitializeMacroHandlers(transferName, transferId); - _sourceFileNameMacroHandlers = InitializeSourceFileNameMacroHandlers(); - } - - /// - /// Creates a remote file name based on the settings and remote file definition - /// - /// The original file. - /// The remote file path with macros. - /// Remote file name with expanded macros. - public string CreateRemoteFileName(string originalFileName, string remoteFileDefinition) - { - if (!string.IsNullOrEmpty(remoteFileDefinition) && remoteFileDefinition.Contains('?')) - throw new ArgumentException("Character '?' not allowed in remote filename.", nameof(remoteFileDefinition)); - - if (string.IsNullOrEmpty(originalFileName)) - throw new ArgumentException("Original filename must be set.", nameof(originalFileName)); - - var originalFileNameWithoutPath = Path.GetFileName(originalFileName); - - if (string.IsNullOrEmpty(remoteFileDefinition)) - return originalFileNameWithoutPath; - - if (!IsFileMask(remoteFileDefinition) && - !IsFileMacro(remoteFileDefinition, _macroHandlers) && - !IsFileMacro(remoteFileDefinition, _sourceFileNameMacroHandlers)) - { - // remoteFileDefinition does not have macros - var remoteFileName = Path.GetFileName(remoteFileDefinition); - - if (string.IsNullOrEmpty(remoteFileName)) - remoteFileDefinition = Path.Combine(remoteFileDefinition, originalFileNameWithoutPath); - - return remoteFileDefinition; - } - - var result = ExpandMacrosAndMasks(originalFileName, remoteFileDefinition); - - if (result.EndsWith("\\")) - result = Path.Combine(result, originalFileNameWithoutPath); - - return result; - } - - /// - /// Method for expanding source/destination endpoint directory name for macros when opening the endpoint connection - /// - /// Directory path including unexpanded macros - /// Directory with macros expanded. - public string ExpandDirectoryForMacros(string directory) - { - if (directory.Contains("%SourceFileName%") || directory.Contains("%SourceFileExtension%")) - throw new Exception("'%SourceFileName%' and '%SourceFileExtension%' are not supported macros for source and destination directories."); - - return ExpandFileMacros(directory); - } - - /// - /// Creates the file path to use for source operation: Move - /// The SourceOperationTo should always be a directory. If it is empty, an error should be thrown - /// The current file name should always be appended to the directory name - /// The directory name cannot use file macros or file masks - /// - public static string CreateRemoteFileNameForMove(string sourceOperationTo, string sourceFilePath) - { - var directoryName = sourceOperationTo; - if (string.IsNullOrEmpty(directoryName)) - { - throw new ArgumentException( - "When using move as a source operation, you should always define a directory", - nameof(sourceOperationTo)); +using System.Text.RegularExpressions; + +namespace Frends.FTP.DownloadFiles.Definitions; + +/// +/// Policies for creating names for remote files: expands macros etc. +/// +internal class RenamingPolicy +{ + private readonly IDictionary> _macroHandlers; + private readonly IDictionary> _sourceFileNameMacroHandlers; + + public RenamingPolicy(string transferName, Guid transferId) + { + _macroHandlers = InitializeMacroHandlers(transferName, transferId); + _sourceFileNameMacroHandlers = InitializeSourceFileNameMacroHandlers(); + } + + /// + /// Creates a remote file name based on the settings and remote file definition + /// + /// The original file. + /// The remote file path with macros. + /// Remote file name with expanded macros. + public string CreateRemoteFileName(string originalFileName, string remoteFileDefinition) + { + if (!string.IsNullOrEmpty(remoteFileDefinition) && remoteFileDefinition.Contains('?')) + throw new ArgumentException("Character '?' not allowed in remote filename.", nameof(remoteFileDefinition)); + + if (string.IsNullOrEmpty(originalFileName)) + throw new ArgumentException("Original filename must be set.", nameof(originalFileName)); + + var originalFileNameWithoutPath = Path.GetFileName(originalFileName); + + if (string.IsNullOrEmpty(remoteFileDefinition)) + return originalFileNameWithoutPath; + + if (!IsFileMask(remoteFileDefinition) && + !IsFileMacro(remoteFileDefinition, _macroHandlers) && + !IsFileMacro(remoteFileDefinition, _sourceFileNameMacroHandlers)) + { + // remoteFileDefinition does not have macros + var remoteFileName = Path.GetFileName(remoteFileDefinition); + + if (string.IsNullOrEmpty(remoteFileName)) + remoteFileDefinition = Path.Combine(remoteFileDefinition, originalFileNameWithoutPath); + + return remoteFileDefinition; } - directoryName = CanonizeAndCheckPath(directoryName); - - // this should always be a directory - if (!directoryName.EndsWith("/")) directoryName += "/"; - - var sourceFileName = Path.GetFileName(sourceFilePath); - return Path.Combine(directoryName, sourceFileName); - } - - private static string CanonizeAndCheckPath(string path) - { - // make all the paths use forward slashes - this should be supported on File, FTP, and SFTP - path = path.Replace(Path.DirectorySeparatorChar, '/'); - - if (path.IndexOfAny(GetInvalidChars()) != -1) - throw new ArgumentException("Illegal characters in path: " + path); - return path; - } - - private static char[] GetInvalidChars() - { - List invalidCharacters = new(Path.GetInvalidFileNameChars()); - invalidCharacters.Remove('/'); // remove the forward slash, as it is supported - invalidCharacters.Remove(':'); // also the colon is supported - return invalidCharacters.ToArray(); - } - - - public string CreateFilePathForRename(string originalFilePath, string sourceOperationTo) - { - if (string.IsNullOrEmpty(sourceOperationTo)) - throw new ArgumentException("When using rename as a source operation, you need to define the new name"); - + var result = ExpandMacrosAndMasks(originalFileName, remoteFileDefinition); + + if (result.EndsWith("\\")) + result = Path.Combine(result, originalFileNameWithoutPath); + + return result; + } + + /// + /// Method for expanding source/destination endpoint directory name for macros when opening the endpoint connection + /// + /// Directory path including unexpanded macros + /// Directory with macros expanded. + public string ExpandDirectoryForMacros(string directory) + { + if (directory.Contains("%SourceFileName%") || directory.Contains("%SourceFileExtension%")) + throw new Exception("'%SourceFileName%' and '%SourceFileExtension%' are not supported macros for source and destination directories."); + + return ExpandFileMacros(directory); + } + + /// + /// Creates the file path to use for source operation: Move + /// The SourceOperationTo should always be a directory. If it is empty, an error should be thrown + /// The current file name should always be appended to the directory name + /// The directory name cannot use file macros or file masks + /// + public static string CreateRemoteFileNameForMove(string sourceOperationTo, string sourceFilePath) + { + var directoryName = sourceOperationTo; + if (string.IsNullOrEmpty(directoryName)) + { + throw new ArgumentException( + "When using move as a source operation, you should always define a directory", + nameof(sourceOperationTo)); + } + + directoryName = CanonizeAndCheckPath(directoryName); + + // this should always be a directory + if (!directoryName.EndsWith("/")) directoryName += "/"; + + var sourceFileName = Path.GetFileName(sourceFilePath); + return Path.Combine(directoryName, sourceFileName); + } + + private static string CanonizeAndCheckPath(string path) + { + // make all the paths use forward slashes - this should be supported on File, FTP, and SFTP + path = path.Replace(Path.DirectorySeparatorChar, '/'); + + if (path.IndexOfAny(GetInvalidChars()) != -1) + throw new ArgumentException("Illegal characters in path: " + path); + return path; + } + + private static char[] GetInvalidChars() + { + List invalidCharacters = new(Path.GetInvalidFileNameChars()); + invalidCharacters.Remove('/'); // remove the forward slash, as it is supported + invalidCharacters.Remove(':'); // also the colon is supported + return invalidCharacters.ToArray(); + } + + + public string CreateFilePathForRename(string originalFilePath, string sourceOperationTo) + { + if (string.IsNullOrEmpty(sourceOperationTo)) + throw new ArgumentException("When using rename as a source operation, you need to define the new name"); + var filePath = ExpandMacrosAndMasks(originalFilePath, sourceOperationTo); - var result = CanonizeAndCheckPath(filePath); + var result = CanonizeAndCheckPath(filePath); var originalFileDirectory = Path.GetDirectoryName(originalFilePath); // Path Combine will ignore originalFileDirectory is result already // contains absolute path. Thus we either get the whole path in result or, if // it is not an absolute path - then we get a path with original file dir as base. - return Path.Combine(originalFileDirectory, result); - } - - private string ExpandMacrosAndMasks(string originalFilePath, string filePath) - { - var expandedPath = ExpandFileMacros(filePath); - expandedPath = ExpandSourceFileNameMacros(expandedPath, originalFilePath); - expandedPath = ExpandFileMasks(expandedPath, originalFilePath); - - return expandedPath; - } - - private string ExpandFileMacros(string filePath) - { - string filename = filePath; - if (IsFileMacro(filename, _macroHandlers)) - filename = ReplaceMacro(filename); - - return filename; - } - - private string ExpandSourceFileNameMacros(string filePath, string originalFile) - { - string filename = filePath; - if (IsFileMacro(filename, _sourceFileNameMacroHandlers)) - filename = ReplaceSourceFileMacro(filename, originalFile); - - return filename; - } - - private static string ExpandFileMasks(string filePath, string originalFileName) - { - string filename = filePath; - if (IsFileMask(filename)) - filename = NameByMask(originalFileName, filename); - - return filename; - } - - private static string NameByMask(string filename, string mask) - { - //remove extension if it is wanted to be changed, new extension is added later on to new filename - if (mask.Contains("*.") && Path.HasExtension(filename)) - filename = Path.GetFileNameWithoutExtension(filename); - - int i = mask.IndexOf("*", StringComparison.InvariantCulture); - if (i >= 0) - { - string tmp = mask.Substring(0, i); - return tmp + filename + mask.Substring(i + 1, (mask.Length - (i + 1))); - } - - //Not an mask return mask. - return mask; - } - - private static bool IsFileMacro(string s, IDictionary> macroDictionary) - { - if (s == null) return false; - - foreach (var key in macroDictionary.Keys) - { - if (s.ToUpperInvariant().Contains(key.ToUpperInvariant())) - return true; - } - - return false; + return Path.Combine(originalFileDirectory, result); + } + + private string ExpandMacrosAndMasks(string originalFilePath, string filePath) + { + var expandedPath = ExpandFileMacros(filePath); + expandedPath = ExpandSourceFileNameMacros(expandedPath, originalFilePath); + expandedPath = ExpandFileMasks(expandedPath, originalFilePath); + + return expandedPath; + } + + private string ExpandFileMacros(string filePath) + { + string filename = filePath; + if (IsFileMacro(filename, _macroHandlers)) + filename = ReplaceMacro(filename); + + return filename; + } + + private string ExpandSourceFileNameMacros(string filePath, string originalFile) + { + string filename = filePath; + if (IsFileMacro(filename, _sourceFileNameMacroHandlers)) + filename = ReplaceSourceFileMacro(filename, originalFile); + + return filename; + } + + private static string ExpandFileMasks(string filePath, string originalFileName) + { + string filename = filePath; + if (IsFileMask(filename)) + filename = NameByMask(originalFileName, filename); + + return filename; + } + + private static string NameByMask(string filename, string mask) + { + //remove extension if it is wanted to be changed, new extension is added later on to new filename + if (mask.Contains("*.") && Path.HasExtension(filename)) + filename = Path.GetFileNameWithoutExtension(filename); + + int i = mask.IndexOf("*", StringComparison.InvariantCulture); + if (i >= 0) + { + string tmp = mask.Substring(0, i); + return tmp + filename + mask.Substring(i + 1, (mask.Length - (i + 1))); + } + + //Not an mask return mask. + return mask; + } + + private static bool IsFileMacro(string s, IDictionary> macroDictionary) + { + if (s == null) return false; + + if (macroDictionary.Keys.Where(e => s.ToUpperInvariant().Contains(e.ToUpperInvariant())).Any()) + return true; + + return false; + } + + private static bool IsFileMask(string input) + { + var isFileMask = false; + if (string.IsNullOrEmpty(input)) return false; + if (input.Contains('*')) isFileMask = true; + if (input.Contains('?')) isFileMask = true; + return isFileMask; + } + + private static IDictionary> InitializeSourceFileNameMacroHandlers() + { + return new Dictionary> + { + {"%SourceFileName%", Path.GetFileNameWithoutExtension}, + {"%SourceFileExtension%", (originalFile) => Path.HasExtension(originalFile) ? Path.GetExtension(originalFile) : String.Empty}, + }; + } + + private static IDictionary> InitializeMacroHandlers(string transferName, Guid transferId) + { + return new Dictionary> + { + {"%Ticks%", (s) => DateTime.Now.Ticks.ToString()}, + {"%DateTimeMs%", (s) => DateTime.Now.ToString("yyyy-MM-dd-HH-mm-ss-fff")}, + {"%DateTime%", (s) => DateTime.Now.ToString("yyyy-MM-dd-HH-mm-ss")}, + {"%Date%", (s) => DateTime.Now.ToString("yyyy-MM-dd")}, + {"%Time%", (s) => DateTime.Now.ToString("HH-mm-ss")}, + {"%Year%", (s) => DateTime.Now.ToString("yyyy")}, + {"%Month%", (s) => DateTime.Now.ToString("MM")}, + {"%Day%", (s) => DateTime.Now.ToString("dd")}, + {"%Hour%", (s) => DateTime.Now.ToString("HH")}, + {"%Minute%", (s) => DateTime.Now.ToString("mm")}, + {"%Second%", (s) => DateTime.Now.ToString("ss")}, + {"%Millisecond%", (s) => DateTime.Now.ToString("fff")}, + {"%Guid%", (s) => Guid.NewGuid().ToString()}, + {"%TransferName%", (s) => !String.IsNullOrEmpty(transferName) ? transferName : String.Empty}, + {"%TransferId%", (s) => transferId.ToString().ToUpper()}, + {"%WeekDay%", (s) => (DateTime.Now.DayOfWeek > 0 ? (int)DateTime.Now.DayOfWeek : 7).ToString()} + }; + } + + private string ReplaceSourceFileMacro(string fileDefinition, string originalFile) + { + return ExpandMacrosFromDictionary(fileDefinition, _sourceFileNameMacroHandlers, originalFile); + } + + private string ReplaceMacro(string fileDefinition) + { + return ExpandMacrosFromDictionary(fileDefinition, _macroHandlers, ""); + } + + private static string ExpandMacrosFromDictionary(string fileDefinition, IDictionary> macroHandlers, string originalFile) + { + foreach (var macroHandler in macroHandlers) + { + fileDefinition = Regex.Replace( + fileDefinition, + Regex.Escape(macroHandler.Key), + macroHandler.Value.Invoke(originalFile), + RegexOptions.IgnoreCase); + } + + return fileDefinition; } +} - private static bool IsFileMask(string input) - { - var isFileMask = false; - if (string.IsNullOrEmpty(input)) return false; - if (input.Contains('*')) isFileMask = true; - if (input.Contains('?')) isFileMask = true; - return isFileMask; - } - - private static IDictionary> InitializeSourceFileNameMacroHandlers() - { - return new Dictionary> - { - {"%SourceFileName%", Path.GetFileNameWithoutExtension}, - {"%SourceFileExtension%", (originalFile) => Path.HasExtension(originalFile) ? Path.GetExtension(originalFile) : String.Empty}, - }; - } - - private static IDictionary> InitializeMacroHandlers(string transferName, Guid transferId) - { - return new Dictionary> - { - {"%Ticks%", (s) => DateTime.Now.Ticks.ToString()}, - {"%DateTimeMs%", (s) => DateTime.Now.ToString("yyyy-MM-dd-HH-mm-ss-fff")}, - {"%DateTime%", (s) => DateTime.Now.ToString("yyyy-MM-dd-HH-mm-ss")}, - {"%Date%", (s) => DateTime.Now.ToString("yyyy-MM-dd")}, - {"%Time%", (s) => DateTime.Now.ToString("HH-mm-ss")}, - {"%Year%", (s) => DateTime.Now.ToString("yyyy")}, - {"%Month%", (s) => DateTime.Now.ToString("MM")}, - {"%Day%", (s) => DateTime.Now.ToString("dd")}, - {"%Hour%", (s) => DateTime.Now.ToString("HH")}, - {"%Minute%", (s) => DateTime.Now.ToString("mm")}, - {"%Second%", (s) => DateTime.Now.ToString("ss")}, - {"%Millisecond%", (s) => DateTime.Now.ToString("fff")}, - {"%Guid%", (s) => Guid.NewGuid().ToString()}, - {"%TransferName%", (s) => !String.IsNullOrEmpty(transferName) ? transferName : String.Empty}, - {"%TransferId%", (s) => transferId.ToString().ToUpper()}, - {"%WeekDay%", (s) => (DateTime.Now.DayOfWeek > 0 ? (int)DateTime.Now.DayOfWeek : 7).ToString()} - }; - } - - private string ReplaceSourceFileMacro(string fileDefinition, string originalFile) - { - return ExpandMacrosFromDictionary(fileDefinition, _sourceFileNameMacroHandlers, originalFile); - } - - private string ReplaceMacro(string fileDefinition) - { - return ExpandMacrosFromDictionary(fileDefinition, _macroHandlers, ""); - } - - private static string ExpandMacrosFromDictionary(string fileDefinition, IDictionary> macroHandlers, string originalFile) - { - foreach (var macroHandler in macroHandlers) - { - fileDefinition = Regex.Replace( - fileDefinition, - Regex.Escape(macroHandler.Key), - macroHandler.Value.Invoke(originalFile), - RegexOptions.IgnoreCase); - } - - return fileDefinition; - } -} - diff --git a/Frends.FTP.DownloadFiles/Frends.FTP.DownloadFiles/Definitions/SingleFileTransfer.cs b/Frends.FTP.DownloadFiles/Frends.FTP.DownloadFiles/Definitions/SingleFileTransfer.cs index 0c5e6e1..c2c0fae 100644 --- a/Frends.FTP.DownloadFiles/Frends.FTP.DownloadFiles/Definitions/SingleFileTransfer.cs +++ b/Frends.FTP.DownloadFiles/Frends.FTP.DownloadFiles/Definitions/SingleFileTransfer.cs @@ -58,6 +58,7 @@ public SingleFileTransferResult TransferSingleFile() PutDestinationFile(removeExisting: true); break; case DestinationAction.Error: + Trace(TransferState.CheckIfDestinationFileExists, "Checking if destination file {0} exists", SourceFile.Name); throw new DestinationFileExistsException(DestinationFileNameWithMacrosExpanded); } } @@ -265,7 +266,7 @@ private void HandleTransferError(Exception exception, string sourceFileRestoreMe { _result.Success = false; // the routine instance should be marked as failed if even one transfer fails var errorMessage = - $"Failure in {_state}: File '{SourceFile.Name}' could not be transferred to '{_destinationDirectoryWithMacrosExpanded}'. Error: {exception.Message}"; + $"Failure in {_state}: File '{SourceFile.Name}' could not be transferred to '{_destinationDirectoryWithMacrosExpanded}'. Error: {exception.Message}. InnerException: {exception.InnerException}"; if (!string.IsNullOrEmpty(sourceFileRestoreMessage)) errorMessage += " " + sourceFileRestoreMessage; @@ -359,14 +360,11 @@ private bool ShouldSourceFileBeRestoredOnError() return true; } - switch (_batchContext.Source.Operation) + return _batchContext.Source.Operation switch { - case SourceOperation.Move: - case SourceOperation.Rename: - return true; - default: - return false; - } + SourceOperation.Move or SourceOperation.Rename => true, + _ => false, + }; } private void Trace(TransferState state, string format, params object[] args) diff --git a/Frends.FTP.DownloadFiles/Frends.FTP.DownloadFiles/Frends.FTP.DownloadFiles.csproj b/Frends.FTP.DownloadFiles/Frends.FTP.DownloadFiles/Frends.FTP.DownloadFiles.csproj index dee312a..3b519b2 100644 --- a/Frends.FTP.DownloadFiles/Frends.FTP.DownloadFiles/Frends.FTP.DownloadFiles.csproj +++ b/Frends.FTP.DownloadFiles/Frends.FTP.DownloadFiles/Frends.FTP.DownloadFiles.csproj @@ -11,7 +11,7 @@ true Frends true - 1.1.1 + 1.1.2 Task for downloading files from FTP(S) servers. diff --git a/Frends.FTP.DownloadFiles/Frends.FTP.DownloadFiles/Logging/FtpLogger.cs b/Frends.FTP.DownloadFiles/Frends.FTP.DownloadFiles/Logging/FtpLogger.cs index 21424fb..1d029ff 100644 --- a/Frends.FTP.DownloadFiles/Frends.FTP.DownloadFiles/Logging/FtpLogger.cs +++ b/Frends.FTP.DownloadFiles/Frends.FTP.DownloadFiles/Logging/FtpLogger.cs @@ -1,181 +1,182 @@ -using System.Collections.Concurrent; -using Frends.FTP.DownloadFiles.Definitions; -using Frends.FTP.DownloadFiles.Enums; -using Serilog; - -namespace Frends.FTP.DownloadFiles.Logging; - -internal interface IFtpLogger : IDisposable -{ +using System.Collections.Concurrent; +using Frends.FTP.DownloadFiles.Definitions; +using Frends.FTP.DownloadFiles.Enums; +using Serilog; + +namespace Frends.FTP.DownloadFiles.Logging; + +internal interface IFtpLogger : IDisposable +{ void NotifyError(BatchContext context, string msg, Exception e); - void NotifyInformation(BatchContext context, string msg); - - void NotifyTrace(string message); - - void LogTransferSuccess(SingleFileTransfer transfer, BatchContext context); - - void LogTransferFailed(SingleFileTransfer transfer, BatchContext context, string errorMessage, Exception exception); -} - -internal class FtpLogger : IFtpLogger -{ - private ConcurrentBag _fileTransfers; - private ILogger _log; - - private bool _disposed; - - public FtpLogger(ILogger log) - { - _fileTransfers = new ConcurrentBag(); - _log = log; - } - - ~FtpLogger() - { - Dispose(false); - } - - public void NotifyError(BatchContext context, string msg, Exception e) - { - try - { - if (context == null) context = new BatchContext(); - - var sourceEndPointName = GetSourceEndPointName(context); - var destinationEndPointName = GetDestinationEndPointName(context); - var transferName = context.Info == null ? "unknown" : context.Info.TransferName; - var transferNameForLog = transferName ?? string.Empty; - - var errorMessage = $"\r\n\r\nFRENDS FTP file transfer '{transferNameForLog}' from '{sourceEndPointName}' to '{destinationEndPointName}': \r\n{msg}\r\n"; - _log.Error(errorMessage, e); - } - catch (Exception ex) - { - _log.Error("Error when logging error message: " + ex.Message, ex); - } - } - - public void NotifyInformation(BatchContext context, string msg) - { - try - { - _log.Information(msg); - } - catch (Exception ex) - { - _log.Error("Error when logging information message: " + ex.Message, ex); - } - } - - public void LogTransferSuccess(SingleFileTransfer transfer, BatchContext context) - { - try - { - var fileTransferInfoForSuccess = CreateFileTransferInfo(TransferResult.Success, transfer, context); - _fileTransfers.Add(fileTransferInfoForSuccess); - _log.Information("File transfer succeeded: " + transfer.SourceFile.Name); - } - catch (Exception ex) - { - _log.Error("Error when logging success message: " + ex.Message, ex); - } + void NotifyInformation(BatchContext context, string msg); + + void NotifyTrace(string message); + + void LogTransferSuccess(SingleFileTransfer transfer, BatchContext context); + + void LogTransferFailed(SingleFileTransfer transfer, BatchContext context, string errorMessage, Exception exception); +} + +internal class FtpLogger : IFtpLogger +{ + private ConcurrentBag _fileTransfers; + private ILogger _log; + + private bool _disposed; + + public FtpLogger(ILogger log) + { + _fileTransfers = new ConcurrentBag(); + _log = log; + } + + ~FtpLogger() + { + Dispose(false); + } + + public void NotifyError(BatchContext context, string msg, Exception e) + { + try + { + if (context == null) context = new BatchContext(); + + var sourceEndPointName = GetSourceEndPointName(context); + var destinationEndPointName = GetDestinationEndPointName(context); + var transferName = context.Info == null ? "unknown" : context.Info.TransferName; + var transferNameForLog = transferName ?? string.Empty; + + var errorMessage = $"\r\n\r\nFrends FTP file transfer '{transferNameForLog}' from '{sourceEndPointName}' to '{destinationEndPointName}': \r\n{msg}\r\n"; + _log.Error(errorMessage, e); + } + catch (Exception ex) + { + _log.Error("Error when logging error message: " + ex.Message, ex); + } + } + + public void NotifyInformation(BatchContext context, string msg) + { + try + { + _log.Information(msg); + } + catch (Exception ex) + { + _log.Error("Error when logging information message: " + ex.Message, ex); + } + } + + public void LogTransferSuccess(SingleFileTransfer transfer, BatchContext context) + { + try + { + var fileTransferInfoForSuccess = CreateFileTransferInfo(TransferResult.Success, transfer, context); + _fileTransfers.Add(fileTransferInfoForSuccess); + _log.Information("File transfer succeeded: " + transfer.SourceFile.Name); + } + catch (Exception ex) + { + _log.Error("Error when logging success message: " + ex.Message, ex); + } + } + + public void LogTransferFailed(SingleFileTransfer transfer, BatchContext context, string errorMessage, Exception exception) + { + try + { + var fileTransferInfoForFailure = CreateFileTransferInfo(TransferResult.Fail, transfer, context, errorMessage); + _fileTransfers.Add(fileTransferInfoForFailure); + + _log.Error("File transfer failed: " + fileTransferInfoForFailure.ErrorInfo, exception); + } + catch (Exception ex) + { + _log.Error("Error when logging failure: " + ex.Message, ex); + } + } + + public void NotifyTrace(string message) + { + // only log to debug trace + _log.Debug(message); + } + + private static string GetSourceEndPointName(BatchContext context) + { + return "File: " + context.Source.Directory + context.Source.FileName; + } + + private static string GetDestinationEndPointName(BatchContext context) + { + return $"FTP://{context.Connection.Address}/{context.Destination.Directory}/{context.Destination.FileName}"; + } + + public static FileTransferInfo CreateFileTransferInfo(TransferResult result, SingleFileTransfer transfer, BatchContext context, string errorMessage = null) + { + // Create 2 dummy endpoints and initialize some local variables which are needed in case if cobalt config is not + // successfully initialized, i.e. when there has been a failure creating the config (invalid xml etc..) and config elements are left null + var sourceFile = string.Empty; + var destinationFile = string.Empty; + var localFileName = string.Empty; + + if (transfer != null) + { + sourceFile = transfer.SourceFile.Name; + destinationFile = transfer.DestinationFileNameWithMacrosExpanded; + if (context != null) + localFileName = context.Info != null ? context.Info.WorkDir : string.Empty; + } + + var transferStarted = DateTime.UtcNow; + var batchId = Guid.Empty; + + var transferName = string.Empty; + + if (context != null) + { + transferStarted = context.BatchTransferStartTime; + batchId = context.InstanceId; + transferName = context.Info != null ? context.Info.TransferName : string.Empty; + } + + return new FileTransferInfo + { + Result = result, + SourceFile = sourceFile ?? string.Empty, + DestinationFile = destinationFile ?? string.Empty, + FileSize = GetFileSize(localFileName), + TransferStarted = transferStarted, + TransferEnded = DateTime.UtcNow, + BatchId = batchId, + TransferName = transferName ?? string.Empty, + ErrorInfo = errorMessage ?? string.Empty + }; + } + + private static long GetFileSize(string filepath) + { + return File.Exists(filepath) ? new FileInfo(filepath).Length : 0; + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private void Dispose(bool disposing) + { + if (_disposed) + { + return; + } + + _fileTransfers = null; + _log = null; + + _disposed = true; } +} - public void LogTransferFailed(SingleFileTransfer transfer, BatchContext context, string errorMessage, Exception exception) - { - try - { - var fileTransferInfoForFailure = CreateFileTransferInfo(TransferResult.Fail, transfer, context, errorMessage); - _fileTransfers.Add(fileTransferInfoForFailure); - - _log.Error("File transfer failed: " + fileTransferInfoForFailure.ErrorInfo, exception); - } - catch (Exception ex) - { - _log.Error("Error when logging failure: " + ex.Message, ex); - } - } - - public void NotifyTrace(string message) - { - // only log to debug trace - _log.Debug(message); - } - - private static string GetSourceEndPointName(BatchContext context) - { - return "File: " + context.Source.Directory + context.Source.FileName; - } - - private static string GetDestinationEndPointName(BatchContext context) - { - return $"FTP://{context.Connection.Address}/{context.Destination.Directory}/{context.Destination.FileName}"; - } - - public static FileTransferInfo CreateFileTransferInfo(TransferResult result, SingleFileTransfer transfer, BatchContext context, string errorMessage = null) - { - // Create 2 dummy endpoints and initialize some local variables which are needed in case if cobalt config is not - // successfully initialized, i.e. when there has been a failure creating the config (invalid xml etc..) and config elements are left null - var sourceFile = string.Empty; - var destinationFile = string.Empty; - var localFileName = string.Empty; - - if (transfer != null) - { - sourceFile = transfer.SourceFile.Name; - destinationFile = transfer.DestinationFileNameWithMacrosExpanded; - localFileName = context.Info.WorkDir; - } - - var transferStarted = DateTime.UtcNow; - var batchId = Guid.Empty; - - var transferName = string.Empty; - - if (context != null) - { - transferStarted = context.BatchTransferStartTime; - batchId = context.InstanceId; - transferName = context.Info != null ? context.Info.TransferName : string.Empty; - } - - return new FileTransferInfo - { - Result = result, - SourceFile = sourceFile ?? string.Empty, - DestinationFile = destinationFile ?? string.Empty, - FileSize = GetFileSize(localFileName), - TransferStarted = transferStarted, - TransferEnded = DateTime.UtcNow, - BatchId = batchId, - TransferName = transferName ?? string.Empty, - ErrorInfo = errorMessage ?? string.Empty - }; - } - - private static long GetFileSize(string filepath) - { - return File.Exists(filepath) ? new FileInfo(filepath).Length : 0; - } - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - private void Dispose(bool disposing) - { - if (_disposed) - { - return; - } - - _fileTransfers = null; - _log = null; - - _disposed = true; - } -} -