Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: Added advanced filters #24

Merged
merged 6 commits into from
Mar 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Copy/Clients/Exchange.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public bool DoFileExist(string path)
throw new NotImplementedException();
}

public string[] ListFiles(string path)
public string[] ListFiles(string path, CopyFilter filter)
{
throw new NotImplementedException();
}
Expand Down
10 changes: 8 additions & 2 deletions Copy/Clients/FTP.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Copy.Types;
using FluentFTP;
using System.Net;
using System.Text.RegularExpressions;

namespace Copy.Clients
{
Expand Down Expand Up @@ -37,15 +38,20 @@ public bool DoFileExist(string path)
return FtpClient.FileExists(path);
}

public string[] ListFiles(string path)
public string[] ListFiles(string path, CopyFilter filter)
{
if (!FtpClient.DirectoryExists(path))
{
Logger.Error($"Directory {path} does not exist");
throw new DirectoryNotFoundException($"Directory {path} does not exist");
}

FtpListItem[] files = FtpClient.GetListing(path);
Regex nameRegex = new(filter.Name);
Regex authorRegex = new(filter.Author);

FtpListItem[] files = FtpClient.GetListing(path)
.Where(f => nameRegex.IsMatch(f.Name) && authorRegex.IsMatch(f.RawOwner) && f.Created >= filter.CreatedAfter && (ulong)f.Size <= filter.MaxSize && (ulong)f.Size >= filter.MinSize)
.ToArray();
return files.Select(f => Path.Combine(path, f.Name)).ToArray();
}

Expand Down
18 changes: 16 additions & 2 deletions Copy/Clients/Local.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
using Copy.Types;
using System.Runtime.Versioning;
using System.Security.Principal;
using System.Text.RegularExpressions;

namespace Copy.Clients
{
Expand Down Expand Up @@ -28,15 +31,26 @@ public bool DoFileExist(string path)
return File.Exists(path);
}

public string[] ListFiles(string path)
[SupportedOSPlatform("windows")]
public string[] ListFiles(string path, CopyFilter filter)
{
if (!Directory.Exists(path))
{
Logger.Error($"Directory {path} does not exist");
throw new DirectoryNotFoundException($"Directory {path} does not exist");
}

return Directory.GetFiles(path);
Regex nameRegex = new(filter.Name);
Regex authorRegex = new(filter.Author);

return Directory.GetFiles(path)
.Where(f =>
{
FileInfo fileInfo = new(f);
bool authorMatch = authorRegex.IsMatch(fileInfo.GetAccessControl().GetOwner(typeof(NTAccount))?.Value ?? throw new FileOwnerNotFoundException($"Impossible to get owner of {f}"));
return nameRegex.IsMatch(Path.GetFileName(f)) && authorMatch && File.GetCreationTime(f) >= filter.CreatedAfter && (ulong)fileInfo.Length <= filter.MaxSize && (ulong)fileInfo.Length >= filter.MinSize;
})
.ToArray();
}

public Stream GetFile(string path)
Expand Down
11 changes: 9 additions & 2 deletions Copy/Clients/SFTP.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Copy.Types;
using Renci.SshNet;
using System.Text.RegularExpressions;

namespace Copy.Clients
{
Expand All @@ -23,15 +24,21 @@ public SFTP(Client credentials)
SftpClient.Connect();
}

public string[] ListFiles(string path)
public string[] ListFiles(string path, CopyFilter filter)
{
if (!SftpClient.Exists(path))
{
Logger.Error($"Directory {path} does not exist");
throw new DirectoryNotFoundException($"Directory {path} does not exist");
}

var files = SftpClient.ListDirectory(path);
if (filter.Author != ".*") Logger.Warn($"Filtering by author is not supported by SFTP");

Regex nameRegex = new(filter.Name);
Regex authorRegex = new(filter.Author);

var files = SftpClient.ListDirectory(path)
.Where(f => nameRegex.IsMatch(f.Name) && f.LastWriteTime >= filter.CreatedAfter && (ulong)f.Length <= filter.MaxSize && (ulong)f.Length >= filter.MinSize);
return files.Select(f => f.FullName.Remove(0, 1)).ToArray();
}

Expand Down
10 changes: 8 additions & 2 deletions Copy/Config.cs
Original file line number Diff line number Diff line change
Expand Up @@ -111,15 +111,21 @@ public static string GetDefault()
Source = "source",
Destination = "destination",
Client = "SFTP",
Filter = "*.txt"
Filter = new CopyFilter()
{
Name = ".*\\.txt"
}
},
new CopyTask()
{
Source = "source",
Destination = "destination",
Client = "Local",
Delete = true,
Filter = "*.txt"
Filter = new CopyFilter()
{
Name = ".*\\.txt"
}
},
new CopyTask()
{
Expand Down
8 changes: 3 additions & 5 deletions Copy/Program.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using Copy.Clients;
using Copy.Types;
using System.Text.RegularExpressions;

namespace Copy
{
Expand Down Expand Up @@ -63,10 +62,9 @@ public static void Main(string[] args)
throw new ClientNotFoundException($"Client {task.Source} not found");
}

Regex filter = new(task.Filter);
IEnumerable<string> files = client.ListFiles(task.Source).Where(f => filter.IsMatch(f));
string[] files = client.ListFiles(task.Source, task.Filter);

Logger.Info($"Treating {files.Count()} files");
Logger.Info($"Treating {files.Length} files");

foreach (string file in files)
{
Expand All @@ -86,7 +84,7 @@ public static void Main(string[] args)
Logger.Debug($"Copied {file} to {task.Destination}");
}

Logger.Info($"Copied {files.Count()} files from {task.Source} to {task.Destination} using {task.Client}");
Logger.Info($"Copied {files.Length} files from {task.Source} to {task.Destination} using {task.Client}");
}

Logger.Info("All tasks executed");
Expand Down
36 changes: 36 additions & 0 deletions Copy/Types/CopyFilter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using Newtonsoft.Json;

namespace Copy.Types
{
/// <summary>
/// Filter for files to copy.
/// </summary>
public class CopyFilter
{
/// <summary>
/// Pattern the name of the files must match.
/// </summary>
[JsonProperty(PropertyName = "Name", Required = Required.DisallowNull)]
public string Name { get; set; } = ".*";
/// <summary>
/// Pattern the author of the files must match.
/// </summary>
[JsonProperty(PropertyName = "Author", Required = Required.DisallowNull)]
public string Author { get; set; } = ".*";
/// <summary>
/// Date the files must be created after.
/// </summary>
[JsonProperty(PropertyName = "CreatedAfter", Required = Required.DisallowNull)]
public DateTime CreatedAfter { get; set; } = DateTime.MinValue;
/// <summary>
/// Size the files must not exceed.
/// </summary>
[JsonProperty(PropertyName = "MaxSize", Required = Required.DisallowNull)]
public ulong MaxSize { get; set; } = ulong.MaxValue;
/// <summary>
/// Size the files must exceed.
/// </summary>
[JsonProperty(PropertyName = "MinSize", Required = Required.DisallowNull)]
public ulong MinSize { get; set; } = 0;
}
}
2 changes: 1 addition & 1 deletion Copy/Types/CopyTask.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,6 @@ public class CopyTask
/// Filter for files to copy.
/// </summary>
[JsonProperty(PropertyName = "Filter", Required = Required.DisallowNull)]
public string Filter { get; set; } = ".*";
public CopyFilter Filter { get; set; } = new();
}
}
8 changes: 8 additions & 0 deletions Copy/Types/Exceptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,12 @@
internal class ClientNotFoundException(string message) : Exception(message)
{
}

/// <summary>
/// Exception thrown when a file owner is not found.
/// </summary>
/// <param name="message">The message to display.</param>
internal class FileOwnerNotFoundException(string message) : Exception(message)
{
}
}
3 changes: 2 additions & 1 deletion Copy/Types/IClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ internal interface IClient : IDisposable
/// List files in a directory.
/// </summary>
/// <param name="path">Path to the directory.</param>
/// <param name="filter">Filter for files to list.</param>
/// <returns>Array of file names.</returns>
string[] ListFiles(string path);
string[] ListFiles(string path, CopyFilter filter);
/// <summary>
/// Check if a file exists.
/// </summary>
Expand Down