diff --git a/.editorconfig b/.editorconfig
index 64ad963b34..f2ea6175b6 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -37,22 +37,37 @@ trim_trailing_whitespace = true
tab_width = 4
# New line preferences
csharp_new_line_before_open_brace = all
+csharp_new_line_before_open_brace_for_methods = true
+csharp_new_line_before_open_brace_for_control_blocks = true
+csharp_preserve_single_line_statements = false
+csharp_style_allow_embedded_statements_on_same_line_experimental = false
csharp_new_line_before_else = true
csharp_new_line_before_catch = true
csharp_new_line_before_finally = true
csharp_new_line_before_members_in_object_initializers = true
csharp_new_line_before_members_in_anonymous_types = true
+csharp_new_line_between_query_expression_clauses = true
csharp_new_line_within_query_expression_clauses = true
csharp_wrap_before_eq = false
place_attribute_on_same_line = "never"
# csharp_style_namespace_declarations = file_scoped:warning
+# https://dotnettips.wordpress.com/2023/06/27/microsoft-net-code-analysis-always-add-braces-in-c/
+dotnet_diagnostic.IDE0011.severity = warning
+csharp_prefer_braces = true:warning
+dotnet_diagnostic.SA1500.severity = warning
+dotnet_diagnostic.SA1503.severity = warning
+dotnet_diagnostic.SA1520.severity = warning
+
+
# Indentation preferences
csharp_indent_block_contents = true
csharp_indent_braces = false
csharp_indent_case_contents = true
csharp_indent_switch_labels = true
csharp_indent_labels = one_less_than_current
+csharp_indent_case_contents_when_block = false
+csharp_space_after_cast = true
# Prefer "var" everywhere
csharp_style_var_for_built_in_types = true:suggestion
diff --git a/starsky/starsky.foundation.database/Query/Query.cs b/starsky/starsky.foundation.database/Query/Query.cs
index d27ab6be29..f0fe289d76 100644
--- a/starsky/starsky.foundation.database/Query/Query.cs
+++ b/starsky/starsky.foundation.database/Query/Query.cs
@@ -49,12 +49,19 @@ public Query(ApplicationDbContext context,
/// FileIndex-objects with database data
public FileIndexItem? GetObjectByFilePath(string filePath)
{
- if ( filePath != "/" ) filePath = PathHelper.RemoveLatestSlash(filePath);
+ if ( filePath != "/" )
+ {
+ filePath = PathHelper.RemoveLatestSlash(filePath);
+ }
FileIndexItem? LocalQuery(ApplicationDbContext context)
{
var item = context.FileIndex.FirstOrDefault(p => p.FilePath == filePath);
- if ( item != null ) item.Status = FileIndexItem.ExifStatus.Ok;
+ if ( item != null )
+ {
+ item.Status = FileIndexItem.ExifStatus.Ok;
+ }
+
return item;
}
@@ -85,7 +92,11 @@ public Query(ApplicationDbContext context,
_cache.TryGetValue(
GetObjectByFilePathAsyncCacheName(filePath), out var data) )
{
- if ( !( data is FileIndexItem fileIndexItem ) ) return null;
+ if ( !( data is FileIndexItem fileIndexItem ) )
+ {
+ return null;
+ }
+
fileIndexItem.Status = FileIndexItem.ExifStatus.OkAndSame;
return fileIndexItem;
}
@@ -95,7 +106,9 @@ public Query(ApplicationDbContext context,
// cache code:
if ( cacheTime == null || _appSettings.AddMemoryCache != true || result == null )
+ {
return result;
+ }
SetGetObjectByFilePathCache(filePath, result.Clone(), cacheTime);
@@ -120,19 +133,25 @@ public void SetGetObjectByFilePathCache(string filePath,
public async Task GetSubPathByHashAsync(string fileHash)
{
// The CLI programs uses no cache
- if ( !IsCacheEnabled() || _cache == null ) return await QueryGetItemByHashAsync(fileHash);
+ if ( !IsCacheEnabled() || _cache == null )
+ {
+ return await QueryGetItemByHashAsync(fileHash);
+ }
// Return values from IMemoryCache
var queryHashListCacheName = CachingDbName("hashList", fileHash);
// if result is not null return cached value
if ( _cache.TryGetValue(queryHashListCacheName, out var cachedSubPath)
- && !string.IsNullOrEmpty(( string? )cachedSubPath) ) return ( string )cachedSubPath;
+ && !string.IsNullOrEmpty(( string? ) cachedSubPath) )
+ {
+ return ( string ) cachedSubPath;
+ }
cachedSubPath = await QueryGetItemByHashAsync(fileHash);
_cache.Set(queryHashListCacheName, cachedSubPath, new TimeSpan(48, 0, 0));
- return ( string? )cachedSubPath;
+ return ( string? ) cachedSubPath;
}
///
@@ -141,11 +160,17 @@ public void SetGetObjectByFilePathCache(string filePath,
/// base32 fileHash
public void ResetItemByHash(string? fileHash)
{
- if ( _cache == null || _appSettings.AddMemoryCache == false ) return;
+ if ( _cache == null || _appSettings.AddMemoryCache == false )
+ {
+ return;
+ }
var queryCacheName = CachingDbName("hashList", fileHash);
- if ( _cache.TryGetValue(queryCacheName, out _) ) _cache.Remove(queryCacheName);
+ if ( _cache.TryGetValue(queryCacheName, out _) )
+ {
+ _cache.Remove(queryCacheName);
+ }
}
///
@@ -165,7 +190,9 @@ async Task LocalQuery(ApplicationDbContext context, FileIndexItem fileIndexItem)
CacheUpdateItem(new List { updateStatusContent.Clone() });
if ( _appSettings.Verbose == true )
// Ef core changes debug
+ {
_logger.LogDebug(context.ChangeTracker.DebugView.LongView);
+ }
// object cache path is used to avoid updates
SetGetObjectByFilePathCache(fileIndexItem.FilePath!, updateStatusContent,
@@ -204,7 +231,11 @@ await RetryQueryUpdateSaveChangesAsync(updateStatusContent, e,
{
// Skip if Duplicate entry
// MySqlConnector.MySqlException (0x80004005): Duplicate entry for key 'PRIMARY'
- if ( !exception.Message.Contains("Duplicate") ) throw;
+ if ( !exception.Message.Contains("Duplicate") )
+ {
+ throw;
+ }
+
_logger.LogError(exception,
$"[UpdateItemAsync] Skipped MySqlException Duplicate entry for key {updateStatusContent.FilePath}");
}
@@ -221,12 +252,16 @@ await RetryQueryUpdateSaveChangesAsync(updateStatusContent, e,
public async Task> UpdateItemAsync(
List updateStatusContentList)
{
- if ( updateStatusContentList.Count == 0 ) return new List();
+ if ( updateStatusContentList.Count == 0 )
+ {
+ return new List();
+ }
async Task> LocalQuery(DbContext context,
List fileIndexItems)
{
foreach ( var item in fileIndexItems )
+ {
try
{
context.Attach(item).State = EntityState.Modified;
@@ -236,11 +271,14 @@ async Task> LocalQuery(DbContext context,
// System.InvalidOperationException: The property 'FileIndexItem.Id' has a temporary value while attempting to change the entity's state to 'Modified'
// Issue #994
}
+ }
await context.SaveChangesAsync();
foreach ( var item in fileIndexItems )
+ {
context.Attach(item).State = EntityState.Detached;
+ }
CacheUpdateItem(fileIndexItems);
return fileIndexItems;
@@ -288,14 +326,19 @@ async Task> LocalQuery(DbContext context,
/// items to update
public void CacheUpdateItem(List updateStatusContent)
{
- if ( _cache == null || _appSettings.AddMemoryCache == false ) return;
+ if ( _cache == null || _appSettings.AddMemoryCache == false )
+ {
+ return;
+ }
var skippedCacheItems = new HashSet();
foreach ( var item in updateStatusContent.ToList() )
{
if ( item.Status == FileIndexItem.ExifStatus.OkAndSame ||
item.Status == FileIndexItem.ExifStatus.Default )
+ {
item.Status = FileIndexItem.ExifStatus.Ok;
+ }
// ToList() > Collection was modified; enumeration operation may not execute.
var queryCacheName = CachingDbName(nameof(FileIndexItem),
@@ -309,7 +352,7 @@ public void CacheUpdateItem(List updateStatusContent)
}
objectFileFolders ??= new List();
- var displayFileFolders = ( List )objectFileFolders;
+ var displayFileFolders = ( List ) objectFileFolders;
// make it a list to avoid enum errors
displayFileFolders = displayFileFolders.ToList();
@@ -317,12 +360,16 @@ public void CacheUpdateItem(List updateStatusContent)
var obj = displayFileFolders.Find(p => p.FilePath == item.FilePath);
if ( obj != null )
// remove add again
+ {
displayFileFolders.Remove(obj);
+ }
if ( item.Status ==
FileIndexItem.ExifStatus.Ok ) // ExifStatus.default is already changed
// Add here item to cached index
+ {
displayFileFolders.Add(item);
+ }
// make it a list to avoid enum errors
displayFileFolders = displayFileFolders.ToList();
@@ -334,8 +381,10 @@ public void CacheUpdateItem(List updateStatusContent)
}
if ( skippedCacheItems.Count >= 1 && _appSettings.Verbose == true )
+ {
_logger.LogInformation(
$"[CacheUpdateItem] skipped: {string.Join(", ", skippedCacheItems)}");
+ }
}
///
@@ -343,11 +392,19 @@ public void CacheUpdateItem(List updateStatusContent)
/// This Does remove a SINGLE item from the cache NOT from the database
///
///
+ [SuppressMessage("ReSharper",
+ "S4136: All 'RemoveCacheItem' method overloads should be adjacent.")]
public void RemoveCacheItem(List updateStatusContent)
{
- if ( _cache == null || _appSettings.AddMemoryCache == false ) return;
+ if ( _cache == null || _appSettings.AddMemoryCache == false )
+ {
+ return;
+ }
- foreach ( var item in updateStatusContent.ToList() ) RemoveCacheItem(item);
+ foreach ( var item in updateStatusContent.ToList() )
+ {
+ RemoveCacheItem(item);
+ }
}
///
@@ -357,11 +414,17 @@ public void RemoveCacheItem(List updateStatusContent)
public bool RemoveCacheParentItem(string directoryName)
{
// Add protection for disabled caching
- if ( _cache == null || _appSettings.AddMemoryCache == false ) return false;
+ if ( _cache == null || _appSettings.AddMemoryCache == false )
+ {
+ return false;
+ }
var queryCacheName = CachingDbName(nameof(FileIndexItem),
PathHelper.RemoveLatestSlash(directoryName.Clone().ToString()!));
- if ( !_cache.TryGetValue(queryCacheName, out _) ) return false;
+ if ( !_cache.TryGetValue(queryCacheName, out _) )
+ {
+ return false;
+ }
_cache.Remove(queryCacheName);
return true;
@@ -375,7 +438,10 @@ public bool RemoveCacheParentItem(string directoryName)
public bool AddCacheParentItem(string directoryName, List items)
{
// Add protection for disabled caching
- if ( _cache == null || _appSettings.AddMemoryCache == false ) return false;
+ if ( _cache == null || _appSettings.AddMemoryCache == false )
+ {
+ return false;
+ }
var queryCacheName = CachingDbName(nameof(FileIndexItem),
PathHelper.RemoveLatestSlash(directoryName.Clone().ToString()!));
@@ -402,7 +468,11 @@ async Task LocalDefaultQuery()
async Task LocalQuery(ApplicationDbContext context)
{
// only in test case fileIndex is null
- if ( context.FileIndex != null ) await context.FileIndex.AddAsync(fileIndexItem);
+ if ( context.FileIndex != null )
+ {
+ await context.FileIndex.AddAsync(fileIndexItem);
+ }
+
await context.SaveChangesAsync();
// Fix for: The instance of entity type 'Item' cannot be tracked because
// another instance with the same key value for {'Id'} is already being tracked
@@ -462,7 +532,9 @@ public async Task> AddParentItemsAsync(string subPath)
var toAddList = new List();
// ReSharper disable once ForeachCanBeConvertedToQueryUsingAnotherGetEnumerator
foreach ( var pathShouldExist in pathListShouldExist )
+ {
if ( !indexItems.Select(p => p.FilePath).Contains(pathShouldExist) )
+ {
toAddList.Add(new FileIndexItem(pathShouldExist)
{
IsDirectory = true,
@@ -471,6 +543,8 @@ public async Task> AddParentItemsAsync(string subPath)
Software = pathShouldExist == "/" ? "Root object" : string.Empty,
Status = FileIndexItem.ExifStatus.Ok
});
+ }
+ }
await AddRangeAsync(toAddList);
return toAddList;
@@ -492,7 +566,11 @@ internal static string GetObjectByFilePathAsyncCacheName(string subPath)
private async Task GetObjectByFilePathQueryAsync(
string filePath)
{
- if ( filePath != "/" ) filePath = PathHelper.RemoveLatestSlash(filePath);
+ if ( filePath != "/" )
+ {
+ filePath = PathHelper.RemoveLatestSlash(filePath);
+ }
+
var paths = new List { filePath };
return ( await GetObjectsByFilePathQueryAsync(paths) )
.FirstOrDefault();
@@ -528,7 +606,11 @@ internal static string GetObjectByFilePathAsyncCacheName(string subPath)
internal static string CachingDbName(string functionName, string? singleItemDbPath)
{
// when is nothing assume its the home item
- if ( string.IsNullOrWhiteSpace(singleItemDbPath) ) singleItemDbPath = "/";
+ if ( string.IsNullOrWhiteSpace(singleItemDbPath) )
+ {
+ singleItemDbPath = "/";
+ }
+
// For creating an unique name: DetailView_/2018/01/1.jpg
var uniqueSingleDbCacheNameBuilder = new StringBuilder();
uniqueSingleDbCacheNameBuilder.Append(functionName + "_" + singleItemDbPath);
@@ -560,7 +642,11 @@ async Task LocalRetrySaveChangesAsyncQuery()
// https://go.microsoft.com/fwlink/?linkid=2097913
await Task.Delay(delay);
var context = new InjectServiceScope(_scopeFactory).Context();
- if ( context == null! ) throw new AggregateException("Query Context is null");
+ if ( context == null! )
+ {
+ throw new AggregateException("Query Context is null");
+ }
+
context.Attach(updateStatusContent).State = EntityState.Modified;
await context.SaveChangesAsync();
context.Attach(updateStatusContent).State = EntityState.Unchanged;
@@ -605,7 +691,11 @@ async Task LocalRetrySaveChangesAsyncQuery()
/// true when enabled
internal bool IsCacheEnabled()
{
- if ( _cache == null || _appSettings.AddMemoryCache == false ) return false;
+ if ( _cache == null || _appSettings.AddMemoryCache == false )
+ {
+ return false;
+ }
+
return true;
}
@@ -619,17 +709,26 @@ internal bool IsCacheEnabled()
internal void AddCacheItem(FileIndexItem updateStatusContent)
{
// If cache is turned of
- if ( _cache == null || _appSettings.AddMemoryCache == false ) return;
+ if ( _cache == null || _appSettings.AddMemoryCache == false )
+ {
+ return;
+ }
var queryCacheName = CachingDbName(nameof(FileIndexItem),
updateStatusContent.ParentDirectory!);
- if ( !_cache.TryGetValue(queryCacheName, out var objectFileFolders) ) return;
+ if ( !_cache.TryGetValue(queryCacheName, out var objectFileFolders) )
+ {
+ return;
+ }
objectFileFolders ??= new List();
- var displayFileFolders = ( List )objectFileFolders;
+ var displayFileFolders = ( List ) objectFileFolders;
- if ( updateStatusContent.FilePath == "/" ) return;
+ if ( updateStatusContent.FilePath == "/" )
+ {
+ return;
+ }
displayFileFolders.Add(updateStatusContent);
// Order by filename
@@ -647,15 +746,21 @@ internal void AddCacheItem(FileIndexItem updateStatusContent)
public void RemoveCacheItem(FileIndexItem updateStatusContent)
{
// Add protection for disabled caching
- if ( _cache == null || _appSettings.AddMemoryCache == false ) return;
+ if ( _cache == null || _appSettings.AddMemoryCache == false )
+ {
+ return;
+ }
var queryCacheName = CachingDbName(nameof(FileIndexItem),
updateStatusContent.ParentDirectory!);
- if ( !_cache.TryGetValue(queryCacheName, out var objectFileFolders) ) return;
+ if ( !_cache.TryGetValue(queryCacheName, out var objectFileFolders) )
+ {
+ return;
+ }
objectFileFolders ??= new List();
- var displayFileFolders = ( List )objectFileFolders;
+ var displayFileFolders = ( List ) objectFileFolders;
// Order by filename
displayFileFolders = displayFileFolders
diff --git a/starsky/starsky.foundation.storage/ArchiveFormats/TarBal.cs b/starsky/starsky.foundation.storage/ArchiveFormats/TarBal.cs
index ffcafb6856..288a2ce2fb 100644
--- a/starsky/starsky.foundation.storage/ArchiveFormats/TarBal.cs
+++ b/starsky/starsky.foundation.storage/ArchiveFormats/TarBal.cs
@@ -6,119 +6,150 @@
using System.Threading;
using System.Threading.Tasks;
using starsky.foundation.platform.Helpers;
+using starsky.foundation.platform.Interfaces;
using starsky.foundation.storage.Interfaces;
-namespace starsky.foundation.storage.ArchiveFormats
+namespace starsky.foundation.storage.ArchiveFormats;
+
+[SuppressMessage("Usage",
+ "CA1835:Prefer the 'Memory'-based overloads for 'ReadAsync' and 'WriteAsync")]
+public sealed class TarBal
{
- [SuppressMessage("Usage", "CA1835:Prefer the 'Memory'-based overloads for 'ReadAsync' and 'WriteAsync")]
- public sealed class TarBal
+ private readonly IWebLogger _logger;
+ private readonly char _pathSeparator;
+ private readonly IStorage _storage;
+
+ public TarBal(IStorage storage, IWebLogger logger, char pathSeparator = '/')
+ {
+ _storage = storage;
+ _pathSeparator = pathSeparator;
+ _logger = logger;
+ }
+
+ ///
+ /// Extracts a .tar.gz archive stream to the specified directory.
+ ///
+ /// The .tar.gz to decompress and extract.
+ /// Output directory to write the files.
+ /// cancellationToken
+ public async Task ExtractTarGz(Stream stream, string outputDir,
+ CancellationToken cancellationToken)
{
- private readonly IStorage _storage;
- private readonly char _pathSeparator;
+ // A GZipStream is not seekable, so copy it first to a MemoryStream
+ using var gzip = new GZipStream(stream, CompressionMode.Decompress);
+ const int chunk = 4096;
+ using var memStr = new MemoryStream();
+ int read;
+ var buffer = new byte[chunk];
- public TarBal(IStorage storage, char pathSeparator = '/')
+ while ( ( read = await gzip.ReadAsync(buffer, 0, buffer.Length, cancellationToken) ) > 0 )
{
- _storage = storage;
- _pathSeparator = pathSeparator;
+ await memStr.WriteAsync(buffer, 0, read, cancellationToken);
}
- ///
- /// Extracts a .tar.gz archive stream to the specified directory.
- ///
- /// The .tar.gz to decompress and extract.
- /// Output directory to write the files.
- /// cancellationToken
- public async Task ExtractTarGz(Stream stream, string outputDir, CancellationToken cancellationToken)
+ gzip.Close();
+ memStr.Seek(0, SeekOrigin.Begin);
+ await ExtractTar(memStr, outputDir, cancellationToken);
+ }
+
+ ///
+ /// Extracts a tar archive to the specified directory.
+ ///
+ /// The .tar to extract.
+ /// Output directory to write the files.
+ /// Cancel token
+ public async Task ExtractTar(Stream stream, string outputDir,
+ CancellationToken cancellationToken)
+ {
+ var buffer = new byte[100];
+ var longFileName = string.Empty;
+
+ while ( true )
{
- // A GZipStream is not seekable, so copy it first to a MemoryStream
- using var gzip = new GZipStream(stream, CompressionMode.Decompress);
- const int chunk = 4096;
- using var memStr = new MemoryStream();
- int read;
- var buffer = new byte[chunk];
-
- while ( ( read = await gzip.ReadAsync(buffer, 0, buffer.Length, cancellationToken) ) > 0 )
+ // Read the first 100 bytes to get the name
+ var bytesRead = await stream.ReadAsync(buffer, 0, 100, cancellationToken);
+ if ( bytesRead != 100 )
{
- await memStr.WriteAsync(buffer, 0, read, cancellationToken);
+ _logger.LogError("[ExtractTar] less than 100 bytes read {outputDir}", outputDir);
+ // If fewer bytes are read, it indicates an incomplete read or end of stream.
+ break;
}
- gzip.Close();
- memStr.Seek(0, SeekOrigin.Begin);
- await ExtractTar(memStr, outputDir, cancellationToken);
- }
+ var name = string.IsNullOrEmpty(longFileName)
+ ? Encoding.ASCII.GetString(buffer).Trim('\0')
+ : longFileName; // Use longFileName if we have one read
- ///
- /// Extracts a tar archive to the specified directory.
- ///
- /// The .tar to extract.
- /// Output directory to write the files.
- /// cancel token
- public async Task ExtractTar(Stream stream, string outputDir, CancellationToken cancellationToken)
- {
- var buffer = new byte[100];
- var longFileName = string.Empty;
- while ( true )
+ if ( string.IsNullOrWhiteSpace(name) )
{
- await stream.ReadAsync(buffer, 0, 100, cancellationToken);
- var name = string.IsNullOrEmpty(longFileName) ? Encoding.ASCII.GetString(buffer).Trim('\0') : longFileName; //Use longFileName if we have one read
- if ( string.IsNullOrWhiteSpace(name) )
- break;
- stream.Seek(24, SeekOrigin.Current);
- await stream.ReadAsync(buffer, 0, 12, cancellationToken);
- var size = Convert.ToInt64(Encoding.UTF8.GetString(buffer, 0, 12).Trim('\0').Trim(), 8);
- stream.Seek(20, SeekOrigin.Current); //Move head to typeTag byte
- var typeTag = stream.ReadByte();
- stream.Seek(355L, SeekOrigin.Current); //Move head to beginning of data (byte 512)
-
- if ( typeTag == 'L' )
- {
- //We have a long file name
- longFileName = await CreateLongFileName(size, stream, cancellationToken);
- }
- else
- {
- //We have a normal file or directory
- longFileName = string.Empty; //Reset longFileName if current entry is not indicating one
- await CreateFileOrDirectory(outputDir, name, size, stream, cancellationToken);
- }
-
- //Move head to next 512 byte block
- var pos = stream.Position;
- var offset = 512 - ( pos % 512 );
- if ( offset == 512 )
- offset = 0;
-
- stream.Seek(offset, SeekOrigin.Current);
+ break;
}
- }
- private static async Task CreateLongFileName(long size, Stream stream, CancellationToken cancellationToken)
- {
- //If Type Tag is 'L' we have a filename that is longer than the 100 bytes reserved for it in the header.
- //We read it here and save it temporarily as it will be the file name of the next block where the actual data is
- var buf = new byte[size];
- await stream.ReadAsync(buf, 0, buf.Length, cancellationToken);
- return Encoding.ASCII.GetString(buf).Trim('\0');
- }
+ stream.Seek(24, SeekOrigin.Current);
- private async Task CreateFileOrDirectory(string outputDir, string name, long size, Stream stream, CancellationToken cancellationToken)
- {
- var output = $"{outputDir}{_pathSeparator}{name}";
+ // Read the size
+ bytesRead = await stream.ReadAsync(buffer, 0, 12, cancellationToken);
+ if ( bytesRead != 12 )
+ {
+ break;
+ }
+
+ var size = Convert.ToInt64(Encoding.UTF8.GetString(buffer, 0, 12).Trim('\0').Trim(), 8);
+ stream.Seek(20, SeekOrigin.Current); // Move head to typeTag byte
+ var typeTag = stream.ReadByte();
+ stream.Seek(355L, SeekOrigin.Current); // Move head to beginning of data (byte 512)
- if ( !_storage.ExistFolder(FilenamesHelper.GetParentPath(output)) )
+ if ( typeTag == 'L' )
{
- _storage.CreateDirectory(FilenamesHelper.GetParentPath(output));
+ // We have a long file name
+ longFileName = await CreateLongFileName(size, stream, cancellationToken);
+ }
+ else
+ {
+ // We have a normal file or directory
+ longFileName = string.Empty;
+ // Reset longFileName if current entry is not indicating one
+ await CreateFileOrDirectory(outputDir, name, size, stream, cancellationToken);
}
- if ( !name.EndsWith('/') ) // Directories are zero size and don't need anything written
+ // Move head to next 512 byte block
+ var pos = stream.Position;
+ var offset = 512 - pos % 512;
+ if ( offset == 512 )
{
- var str = new MemoryStream();
- var buf = new byte[size];
- await stream.ReadAsync(buf, 0, buf.Length, cancellationToken);
- str.Write(buf, 0, buf.Length);
- _storage.WriteStreamOpenOrCreate(str, output);
+ offset = 0;
}
+
+ stream.Seek(offset, SeekOrigin.Current);
+ }
+ }
+
+ private static async Task CreateLongFileName(long size, Stream stream,
+ CancellationToken cancellationToken)
+ {
+ //If Type Tag is 'L' we have a filename that is longer than the 100 bytes reserved for it in the header.
+ //We read it here and save it temporarily as it will be the file name of the next block where the actual data is
+ var buf = new byte[size];
+ await stream.ReadAsync(buf, 0, buf.Length, cancellationToken);
+ return Encoding.ASCII.GetString(buf).Trim('\0');
+ }
+
+ private async Task CreateFileOrDirectory(string outputDir, string name, long size,
+ Stream stream, CancellationToken cancellationToken)
+ {
+ var output = $"{outputDir}{_pathSeparator}{name}";
+
+ if ( !_storage.ExistFolder(FilenamesHelper.GetParentPath(output)) )
+ {
+ _storage.CreateDirectory(FilenamesHelper.GetParentPath(output));
}
+ if ( !name.EndsWith('/') ) // Directories are zero size and don't need anything written
+ {
+ var str = new MemoryStream();
+ var buf = new byte[size];
+ await stream.ReadAsync(buf, 0, buf.Length, cancellationToken);
+ await str.WriteAsync(buf, 0, buf.Length, cancellationToken);
+ _storage.WriteStreamOpenOrCreate(str, output);
+ }
}
}
diff --git a/starsky/starsky.foundation.writemeta/Services/ExifToolDownload.cs b/starsky/starsky.foundation.writemeta/Services/ExifToolDownload.cs
index 2c79bc9849..acb98196f1 100644
--- a/starsky/starsky.foundation.writemeta/Services/ExifToolDownload.cs
+++ b/starsky/starsky.foundation.writemeta/Services/ExifToolDownload.cs
@@ -20,384 +20,441 @@
using starsky.foundation.writemeta.Interfaces;
[assembly: InternalsVisibleTo("starskytest")]
-namespace starsky.foundation.writemeta.Services
-{
- [Service(typeof(IExifToolDownload), InjectionLifetime = InjectionLifetime.Singleton)]
- [SuppressMessage("Usage", "S1075:Refactor your code not to use hardcoded absolute paths or URIs", Justification = "Source of files")]
- public sealed class ExifToolDownload : IExifToolDownload
- {
- private readonly IHttpClientHelper _httpClientHelper;
- private readonly AppSettings _appSettings;
- private readonly IStorage _hostFileSystemStorage;
- private readonly IWebLogger _logger;
-
- private const string CheckSumLocation = "https://exiftool.org/checksums.txt";
- private const string CheckSumLocationMirror = "https://qdraw.nl/special/mirror/exiftool/checksums.txt";
- private const string ExiftoolDownloadBasePath = "https://exiftool.org/"; // with slash at the end
- private const string ExiftoolDownloadBasePathMirror = "https://qdraw.nl/special/mirror/exiftool/"; // with slash at the end
- public ExifToolDownload(IHttpClientHelper httpClientHelper, AppSettings appSettings, IWebLogger logger)
- {
- _httpClientHelper = httpClientHelper;
- _appSettings = appSettings;
- _hostFileSystemStorage = new StorageHostFullPathFilesystem(logger);
- _logger = logger;
- }
+namespace starsky.foundation.writemeta.Services;
- internal ExifToolDownload(IHttpClientHelper httpClientHelper, AppSettings appSettings, IWebLogger logger, IStorage storage)
- {
- _httpClientHelper = httpClientHelper;
- _appSettings = appSettings;
- _hostFileSystemStorage = storage;
- _logger = logger;
- }
+[Service(typeof(IExifToolDownload), InjectionLifetime = InjectionLifetime.Singleton)]
+[SuppressMessage("Usage", "S1075:Refactor your code not to use hardcoded absolute paths or URIs",
+ Justification = "Source of files")]
+public sealed class ExifToolDownload : IExifToolDownload
+{
+ private const string CheckSumLocation = "https://exiftool.org/checksums.txt";
- ///
- /// Auto Download Exiftool
- ///
- /// download Windows version if true
- /// check for min file size in bytes (Default = 30 bytes)
- ///
- public async Task DownloadExifTool(bool isWindows, int minimumSize = 30)
- {
- if ( _appSettings.ExiftoolSkipDownloadOnStartup == true || _appSettings is { AddSwaggerExport: true, AddSwaggerExportExitAfter: true } )
- {
- var name = _appSettings.ExiftoolSkipDownloadOnStartup == true
- ? "ExiftoolSkipDownloadOnStartup"
- : "AddSwaggerExport and AddSwaggerExportExitAfter";
- _logger.LogInformation($"[DownloadExifTool] Skipped due true of {name} setting");
- return false;
- }
+ private const string CheckSumLocationMirror =
+ "https://qdraw.nl/special/mirror/exiftool/checksums.txt";
- CreateDirectoryDependenciesFolderIfNotExists();
+ private const string
+ ExiftoolDownloadBasePath = "https://exiftool.org/"; // with slash at the end
- if ( isWindows &&
- ( !_hostFileSystemStorage.ExistFile(ExeExifToolWindowsFullFilePath()) ||
- _hostFileSystemStorage.Info(ExeExifToolWindowsFullFilePath()).Size <= minimumSize ) )
- {
- return await StartDownloadForWindows();
- }
+ private const string ExiftoolDownloadBasePathMirror =
+ "https://qdraw.nl/special/mirror/exiftool/"; // with slash at the end
- if ( !isWindows &&
- ( !_hostFileSystemStorage.ExistFile(ExeExifToolUnixFullFilePath()) ||
- _hostFileSystemStorage.Info(ExeExifToolUnixFullFilePath()).Size <= minimumSize ) )
- {
- return await StartDownloadForUnix();
- }
+ private readonly AppSettings _appSettings;
+ private readonly IStorage _hostFileSystemStorage;
+ private readonly IHttpClientHelper _httpClientHelper;
+ private readonly IWebLogger _logger;
- if ( _appSettings.IsVerbose() )
- {
- var debugPath = isWindows ? ExeExifToolWindowsFullFilePath()
- : ExeExifToolUnixFullFilePath();
- _logger.LogInformation($"[DownloadExifTool] {debugPath}");
- }
+ public ExifToolDownload(IHttpClientHelper httpClientHelper, AppSettings appSettings,
+ IWebLogger logger)
+ {
+ _httpClientHelper = httpClientHelper;
+ _appSettings = appSettings;
+ _hostFileSystemStorage = new StorageHostFullPathFilesystem(logger);
+ _logger = logger;
+ }
- // When running deploy scripts rights might reset (only for unix)
- if ( isWindows ) return true;
+ internal ExifToolDownload(IHttpClientHelper httpClientHelper, AppSettings appSettings,
+ IWebLogger logger, IStorage storage)
+ {
+ _httpClientHelper = httpClientHelper;
+ _appSettings = appSettings;
+ _hostFileSystemStorage = storage;
+ _logger = logger;
+ }
- return await RunChmodOnExifToolUnixExe();
+ ///
+ /// Auto Download Exiftool
+ ///
+ /// download Windows version if true
+ /// check for min file size in bytes (Default = 30 bytes)
+ ///
+ public async Task DownloadExifTool(bool isWindows, int minimumSize = 30)
+ {
+ if ( _appSettings.ExiftoolSkipDownloadOnStartup == true || _appSettings is
+ { AddSwaggerExport: true, AddSwaggerExportExitAfter: true } )
+ {
+ var name = _appSettings.ExiftoolSkipDownloadOnStartup == true
+ ? "ExiftoolSkipDownloadOnStartup"
+ : "AddSwaggerExport and AddSwaggerExportExitAfter";
+ _logger.LogInformation($"[DownloadExifTool] Skipped due true of {name} setting");
+ return false;
}
- private void CreateDirectoryDependenciesFolderIfNotExists()
+ CreateDirectoryDependenciesFolderIfNotExists();
+
+ if ( isWindows &&
+ ( !_hostFileSystemStorage.ExistFile(ExeExifToolWindowsFullFilePath()) ||
+ _hostFileSystemStorage.Info(ExeExifToolWindowsFullFilePath()).Size <= minimumSize ) )
{
- if ( _hostFileSystemStorage.ExistFolder(
- _appSettings.DependenciesFolder) )
- {
- return;
- }
- _logger.LogInformation("[DownloadExifTool] Create Directory: " + _appSettings.DependenciesFolder);
- _hostFileSystemStorage.CreateDirectory(_appSettings.DependenciesFolder);
+ return await StartDownloadForWindows();
}
- internal async Task?> DownloadCheckSums()
+ if ( !isWindows &&
+ ( !_hostFileSystemStorage.ExistFile(ExeExifToolUnixFullFilePath()) ||
+ _hostFileSystemStorage.Info(ExeExifToolUnixFullFilePath()).Size <= minimumSize ) )
{
- var baseLocationResult = await DownloadCheckSums(CheckSumLocation);
- if ( baseLocationResult == null )
- {
- return await DownloadCheckSums(CheckSumLocationMirror);
- }
- return baseLocationResult;
+ return await StartDownloadForUnix();
}
- internal async Task?> DownloadCheckSums(string checkSumUrl)
+ if ( _appSettings.IsVerbose() )
{
- var checksums = await _httpClientHelper.ReadString(checkSumUrl);
- if ( checksums.Key )
- {
- return checksums;
- }
-
- _logger.LogError($"Checksum loading failed {CheckSumLocation}, next retry from mirror ~ error > " + checksums.Value);
- return null;
+ var debugPath = isWindows
+ ? ExeExifToolWindowsFullFilePath()
+ : ExeExifToolUnixFullFilePath();
+ _logger.LogInformation($"[DownloadExifTool] {debugPath}");
}
- internal async Task StartDownloadForUnix()
+ // When running deploy scripts rights might reset (only for unix)
+ if ( isWindows )
{
- var checksums = await DownloadCheckSums();
- if ( checksums == null )
- {
- return false;
- }
- var matchExifToolForUnixName = GetUnixTarGzFromChecksum(checksums.Value.Value);
- return await DownloadForUnix(matchExifToolForUnixName,
- GetChecksumsFromTextFile(checksums.Value.Value));
+ return true;
}
- internal static string GetUnixTarGzFromChecksum(string checksumsValue)
+ return await RunChmodOnExifToolUnixExe();
+ }
+
+ private void CreateDirectoryDependenciesFolderIfNotExists()
+ {
+ if ( _hostFileSystemStorage.ExistFolder(
+ _appSettings.DependenciesFolder) )
{
- // (?<=SHA1\()Image-ExifTool-[\d\.]+\.zip
- var regexExifToolForWindowsName = new Regex(@"(?<=SHA256\()Image-ExifTool-[0-9\.]+\.tar.gz",
- RegexOptions.None, TimeSpan.FromMilliseconds(100));
- return regexExifToolForWindowsName.Match(checksumsValue).Value;
+ return;
}
- private string ExeExifToolUnixFullFilePath()
+ _logger.LogInformation("[DownloadExifTool] Create Directory: " +
+ _appSettings.DependenciesFolder);
+ _hostFileSystemStorage.CreateDirectory(_appSettings.DependenciesFolder);
+ }
+
+ internal async Task?> DownloadCheckSums()
+ {
+ var baseLocationResult = await DownloadCheckSums(CheckSumLocation);
+ if ( baseLocationResult == null )
{
- var path = Path.Combine(_appSettings.DependenciesFolder,
- "exiftool-unix",
- "exiftool");
- return path;
+ return await DownloadCheckSums(CheckSumLocationMirror);
}
- internal async Task DownloadForUnix(string matchExifToolForUnixName, string[] getChecksumsFromTextFile)
- {
- var result = await DownloadForUnix(ExiftoolDownloadBasePath, matchExifToolForUnixName,
- getChecksumsFromTextFile);
-
- if ( result )
- {
- return true;
- }
+ return baseLocationResult;
+ }
- return await DownloadForUnix(ExiftoolDownloadBasePathMirror, matchExifToolForUnixName,
- getChecksumsFromTextFile);
+ internal async Task?> DownloadCheckSums(string checkSumUrl)
+ {
+ var checksums = await _httpClientHelper.ReadString(checkSumUrl);
+ if ( checksums.Key )
+ {
+ return checksums;
}
-
- private async Task DownloadForUnix(string exiftoolDownloadBasePath, string matchExifToolForUnixName,
- string[] getChecksumsFromTextFile)
+ _logger.LogError(
+ $"Checksum loading failed {CheckSumLocation}, next retry from mirror ~ error > " +
+ checksums.Value);
+ return null;
+ }
+
+ internal async Task StartDownloadForUnix()
+ {
+ var checksums = await DownloadCheckSums();
+ if ( checksums == null )
{
+ return false;
+ }
- if ( _hostFileSystemStorage.ExistFile(ExeExifToolUnixFullFilePath()) )
- {
- return true;
- }
+ var matchExifToolForUnixName = GetUnixTarGzFromChecksum(checksums.Value.Value);
+ return await DownloadForUnix(matchExifToolForUnixName,
+ GetChecksumsFromTextFile(checksums.Value.Value));
+ }
- var tarGzArchiveFullFilePath = Path.Combine(_appSettings.DependenciesFolder, "exiftool.tar.gz");
+ internal static string GetUnixTarGzFromChecksum(string checksumsValue)
+ {
+ // (?<=SHA1\()Image-ExifTool-[\d\.]+\.zip
+ var regexExifToolForWindowsName = new Regex(@"(?<=SHA256\()Image-ExifTool-[0-9\.]+\.tar.gz",
+ RegexOptions.None, TimeSpan.FromMilliseconds(100));
+ return regexExifToolForWindowsName.Match(checksumsValue).Value;
+ }
- var url = $"{exiftoolDownloadBasePath}{matchExifToolForUnixName}";
+ private string ExeExifToolUnixFullFilePath()
+ {
+ var path = Path.Combine(_appSettings.DependenciesFolder,
+ "exiftool-unix",
+ "exiftool");
+ return path;
+ }
- var unixDownloaded = await _httpClientHelper.Download(url, tarGzArchiveFullFilePath);
- if ( !unixDownloaded )
- {
- _logger.LogError($"file is not downloaded {matchExifToolForUnixName}");
- return false;
- }
-
- if ( !CheckSha256(tarGzArchiveFullFilePath, getChecksumsFromTextFile) )
- {
- _logger.LogError($"Checksum for {tarGzArchiveFullFilePath} is not valid");
- _hostFileSystemStorage.FileDelete(tarGzArchiveFullFilePath);
- return false;
- }
+ internal async Task DownloadForUnix(string matchExifToolForUnixName,
+ string[] getChecksumsFromTextFile)
+ {
+ var result = await DownloadForUnix(ExiftoolDownloadBasePath, matchExifToolForUnixName,
+ getChecksumsFromTextFile);
- await new TarBal(_hostFileSystemStorage).ExtractTarGz(
- _hostFileSystemStorage.ReadStream(tarGzArchiveFullFilePath), _appSettings.DependenciesFolder, CancellationToken.None);
+ if ( result )
+ {
+ return true;
+ }
- var imageExifToolVersionFolder = _hostFileSystemStorage.GetDirectories(_appSettings.DependenciesFolder)
- .FirstOrDefault(p => p.StartsWith(Path.Combine(_appSettings.DependenciesFolder, "Image-ExifTool-")));
- if ( imageExifToolVersionFolder != null )
- {
- var exifToolUnixFolderFullFilePath = Path.Combine(_appSettings.DependenciesFolder, "exiftool-unix");
- if ( _hostFileSystemStorage.ExistFolder(exifToolUnixFolderFullFilePath) )
- {
- _hostFileSystemStorage.FolderDelete(
- exifToolUnixFolderFullFilePath);
- }
- _hostFileSystemStorage.FolderMove(imageExifToolVersionFolder, exifToolUnixFolderFullFilePath);
- }
- else
- {
- _logger.LogError($"[DownloadForUnix] ExifTool folder does not exists");
- return false;
- }
+ return await DownloadForUnix(ExiftoolDownloadBasePathMirror, matchExifToolForUnixName,
+ getChecksumsFromTextFile);
+ }
- // remove tar.gz file afterwards
- _hostFileSystemStorage.FileDelete(tarGzArchiveFullFilePath);
- var exifToolExePath = Path.Combine(_appSettings.DependenciesFolder, "exiftool-unix", "exiftool");
- _logger.LogInformation($"[DownloadForUnix] ExifTool is just downloaded: {exifToolExePath} for {_appSettings.ApplicationType}");
- return await RunChmodOnExifToolUnixExe();
+ private async Task DownloadForUnix(string exiftoolDownloadBasePath,
+ string matchExifToolForUnixName,
+ string[] getChecksumsFromTextFile)
+ {
+ if ( _hostFileSystemStorage.ExistFile(ExeExifToolUnixFullFilePath()) )
+ {
+ return true;
}
- internal async Task RunChmodOnExifToolUnixExe()
+ var tarGzArchiveFullFilePath =
+ Path.Combine(_appSettings.DependenciesFolder, "exiftool.tar.gz");
+
+ var url = $"{exiftoolDownloadBasePath}{matchExifToolForUnixName}";
+
+ var unixDownloaded = await _httpClientHelper.Download(url, tarGzArchiveFullFilePath);
+ if ( !unixDownloaded )
{
- // need to check again
- // when not exist
- if ( !_hostFileSystemStorage.ExistFile(ExeExifToolUnixFullFilePath()) ) return false;
- if ( _appSettings.IsWindows ) return true;
+ _logger.LogError($"file is not downloaded {matchExifToolForUnixName}");
+ return false;
+ }
+
+ if ( !CheckSha256(tarGzArchiveFullFilePath, getChecksumsFromTextFile) )
+ {
+ _logger.LogError($"Checksum for {tarGzArchiveFullFilePath} is not valid");
+ _hostFileSystemStorage.FileDelete(tarGzArchiveFullFilePath);
+ return false;
+ }
- if ( !_hostFileSystemStorage.ExistFile("/bin/chmod") )
+ await new TarBal(_hostFileSystemStorage, _logger).ExtractTarGz(
+ _hostFileSystemStorage.ReadStream(tarGzArchiveFullFilePath),
+ _appSettings.DependenciesFolder, CancellationToken.None);
+
+ var imageExifToolVersionFolder = _hostFileSystemStorage
+ .GetDirectories(_appSettings.DependenciesFolder)
+ .FirstOrDefault(p =>
+ p.StartsWith(Path.Combine(_appSettings.DependenciesFolder, "Image-ExifTool-")));
+ if ( imageExifToolVersionFolder != null )
+ {
+ var exifToolUnixFolderFullFilePath =
+ Path.Combine(_appSettings.DependenciesFolder, "exiftool-unix");
+ if ( _hostFileSystemStorage.ExistFolder(exifToolUnixFolderFullFilePath) )
{
- _logger.LogError("[RunChmodOnExifToolUnixExe] WARNING: /bin/chmod does not exist");
- return true;
+ _hostFileSystemStorage.FolderDelete(
+ exifToolUnixFolderFullFilePath);
}
- // command.run does not care about the $PATH
- var result = await Command.Run("/bin/chmod", "0755", ExeExifToolUnixFullFilePath()).Task;
- if ( result.Success ) return true;
-
- _logger.LogError($"command failed with exit code {result.ExitCode}: {result.StandardError}");
+ _hostFileSystemStorage.FolderMove(imageExifToolVersionFolder,
+ exifToolUnixFolderFullFilePath);
+ }
+ else
+ {
+ _logger.LogError("[DownloadForUnix] ExifTool folder does not exists");
return false;
}
- internal async Task StartDownloadForWindows()
- {
- var checksums = await DownloadCheckSums();
- if ( checksums == null ) return false;
+ // remove tar.gz file afterwards
+ _hostFileSystemStorage.FileDelete(tarGzArchiveFullFilePath);
+
+ var exifToolExePath =
+ Path.Combine(_appSettings.DependenciesFolder, "exiftool-unix", "exiftool");
+ _logger.LogInformation(
+ $"[DownloadForUnix] ExifTool is just downloaded: {exifToolExePath} for {_appSettings.ApplicationType}");
+ return await RunChmodOnExifToolUnixExe();
+ }
- var matchExifToolForWindowsName = GetWindowsZipFromChecksum(checksums.Value.Value);
- return await DownloadForWindows(matchExifToolForWindowsName,
- GetChecksumsFromTextFile(checksums.Value.Value));
+ internal async Task RunChmodOnExifToolUnixExe()
+ {
+ // need to check again
+ // when not exist
+ if ( !_hostFileSystemStorage.ExistFile(ExeExifToolUnixFullFilePath()) )
+ {
+ return false;
}
- internal static string GetWindowsZipFromChecksum(string checksumsValue)
+ if ( _appSettings.IsWindows )
{
- // (?<=SHA256\()exiftool-[\d\.]+_64\.zip
- var regexExifToolForWindowsName = new Regex(@"(?<=SHA256\()exiftool-[0-9\.]+_64\.zip",
- RegexOptions.None, TimeSpan.FromMilliseconds(100));
- return regexExifToolForWindowsName.Match(checksumsValue).Value;
+ return true;
}
- ///
- /// Parse the content of checksum file
- ///
- /// input file: see test for example
- /// max number of SHA256 results
- ///
- internal string[] GetChecksumsFromTextFile(string checksumsValue, int max = 20)
+ if ( !_hostFileSystemStorage.ExistFile("/bin/chmod") )
{
- // SHA256 = 64 characters, SHA1 = 40 characters
- var regexExifToolForWindowsName = new Regex("[a-z0-9]{64}",
- RegexOptions.None, TimeSpan.FromMilliseconds(100));
- var results = regexExifToolForWindowsName.Matches(checksumsValue).
- Select(m => m.Value).
- ToArray();
- if ( results.Length < max )
- {
- return results;
- }
+ _logger.LogError("[RunChmodOnExifToolUnixExe] WARNING: /bin/chmod does not exist");
+ return true;
+ }
- _logger.LogError($"More than {max} checksums found, this is not expected, code stops now");
- return [];
+ // command.run does not care about the $PATH
+ var result = await Command.Run("/bin/chmod", "0755", ExeExifToolUnixFullFilePath()).Task;
+ if ( result.Success )
+ {
+ return true;
}
- ///
- /// Check if SHA256 hash is valid
- /// Instead of SHA1CryptoServiceProvider, we use SHA256.Create
- ///
- /// path of exiftool.exe
- /// list of SHA256 hashes
- ///
- internal bool CheckSha256(string fullFilePath, IEnumerable checkSumOptions)
+ _logger.LogError(
+ $"command failed with exit code {result.ExitCode}: {result.StandardError}");
+ return false;
+ }
+
+ internal async Task StartDownloadForWindows()
+ {
+ var checksums = await DownloadCheckSums();
+ if ( checksums == null )
{
- using var buffer = _hostFileSystemStorage.ReadStream(fullFilePath);
- using var hashAlgorithm = SHA256.Create();
+ return false;
+ }
- var byteHash = hashAlgorithm.ComputeHash(buffer);
- var hash = BitConverter.ToString(byteHash).Replace("-", string.Empty).ToLowerInvariant();
- return checkSumOptions.AsEnumerable().Any(p => p.Equals(hash, StringComparison.InvariantCultureIgnoreCase));
+ var matchExifToolForWindowsName = GetWindowsZipFromChecksum(checksums.Value.Value);
+ return await DownloadForWindows(matchExifToolForWindowsName,
+ GetChecksumsFromTextFile(checksums.Value.Value));
+ }
+
+ internal static string GetWindowsZipFromChecksum(string checksumsValue)
+ {
+ // (?<=SHA256\()exiftool-[\d\.]+_64\.zip
+ var regexExifToolForWindowsName = new Regex(@"(?<=SHA256\()exiftool-[0-9\.]+_64\.zip",
+ RegexOptions.None, TimeSpan.FromMilliseconds(100));
+ return regexExifToolForWindowsName.Match(checksumsValue).Value;
+ }
+
+ ///
+ /// Parse the content of checksum file
+ ///
+ /// input file: see test for example
+ /// max number of SHA256 results
+ ///
+ internal string[] GetChecksumsFromTextFile(string checksumsValue, int max = 20)
+ {
+ // SHA256 = 64 characters, SHA1 = 40 characters
+ var regexExifToolForWindowsName = new Regex("[a-z0-9]{64}",
+ RegexOptions.None, TimeSpan.FromMilliseconds(100));
+ var results = regexExifToolForWindowsName.Matches(checksumsValue).Select(m => m.Value)
+ .ToArray();
+ if ( results.Length < max )
+ {
+ return results;
}
- private string ExeExifToolWindowsFullFilePath()
+ _logger.LogError($"More than {max} checksums found, this is not expected, code stops now");
+ return [];
+ }
+
+ ///
+ /// Check if SHA256 hash is valid
+ /// Instead of SHA1CryptoServiceProvider, we use SHA256.Create
+ ///
+ /// path of exiftool.exe
+ /// list of SHA256 hashes
+ ///
+ internal bool CheckSha256(string fullFilePath, IEnumerable checkSumOptions)
+ {
+ using var buffer = _hostFileSystemStorage.ReadStream(fullFilePath);
+ using var hashAlgorithm = SHA256.Create();
+
+ var byteHash = hashAlgorithm.ComputeHash(buffer);
+ var hash = BitConverter.ToString(byteHash).Replace("-", string.Empty).ToLowerInvariant();
+ return checkSumOptions.AsEnumerable()
+ .Any(p => p.Equals(hash, StringComparison.InvariantCultureIgnoreCase));
+ }
+
+ private string ExeExifToolWindowsFullFilePath()
+ {
+ return Path.Combine(Path.Combine(_appSettings.DependenciesFolder, "exiftool-windows"),
+ "exiftool.exe");
+ }
+
+ internal async Task DownloadForWindows(string matchExifToolForWindowsName,
+ string[] getChecksumsFromTextFile)
+ {
+ var result = await DownloadForWindows(ExiftoolDownloadBasePath, matchExifToolForWindowsName,
+ getChecksumsFromTextFile);
+
+ if ( result )
{
- return Path.Combine(Path.Combine(_appSettings.DependenciesFolder, "exiftool-windows"), "exiftool.exe");
+ return true;
}
- internal async Task DownloadForWindows(string matchExifToolForWindowsName, string[] getChecksumsFromTextFile)
+ return await DownloadForWindows(ExiftoolDownloadBasePathMirror, matchExifToolForWindowsName,
+ getChecksumsFromTextFile);
+ }
+
+ private async Task DownloadForWindows(string exiftoolDownloadBasePath,
+ string matchExifToolForWindowsName,
+ string[] getChecksumsFromTextFile)
+ {
+ if ( _hostFileSystemStorage.ExistFile(
+ ExeExifToolWindowsFullFilePath()) )
{
- var result = await DownloadForWindows(ExiftoolDownloadBasePath, matchExifToolForWindowsName,
- getChecksumsFromTextFile);
-
- if ( result )
- {
- return true;
- }
+ return true;
+ }
+
+ var zipArchiveFullFilePath = Path.Combine(_appSettings.DependenciesFolder, "exiftool.zip");
+ var windowsExifToolFolder =
+ Path.Combine(_appSettings.DependenciesFolder, "exiftool-windows");
- return await DownloadForWindows(ExiftoolDownloadBasePathMirror, matchExifToolForWindowsName,
- getChecksumsFromTextFile);
+ var url = $"{exiftoolDownloadBasePath}{matchExifToolForWindowsName}";
+ var windowsDownloaded = await _httpClientHelper.Download(url, zipArchiveFullFilePath);
+ if ( !windowsDownloaded )
+ {
+ _logger.LogError($"file is not downloaded {matchExifToolForWindowsName}");
+ return false;
}
-
- private async Task DownloadForWindows(string exiftoolDownloadBasePath, string matchExifToolForWindowsName,
- string[] getChecksumsFromTextFile)
+
+ if ( !CheckSha256(zipArchiveFullFilePath, getChecksumsFromTextFile) )
{
- if ( _hostFileSystemStorage.ExistFile(
- ExeExifToolWindowsFullFilePath()) )
- {
- return true;
- }
+ _logger.LogError($"Checksum for {zipArchiveFullFilePath} is not valid");
+ return false;
+ }
- var zipArchiveFullFilePath = Path.Combine(_appSettings.DependenciesFolder, "exiftool.zip");
- var windowsExifToolFolder = Path.Combine(_appSettings.DependenciesFolder, "exiftool-windows");
+ _hostFileSystemStorage.CreateDirectory(windowsExifToolFolder);
- var url = $"{exiftoolDownloadBasePath}{matchExifToolForWindowsName}";
- var windowsDownloaded = await _httpClientHelper.Download(url, zipArchiveFullFilePath);
- if ( !windowsDownloaded )
- {
- _logger.LogError($"file is not downloaded {matchExifToolForWindowsName}");
- return false;
- }
+ new Zipper(_logger).ExtractZip(zipArchiveFullFilePath, windowsExifToolFolder);
- if ( !CheckSha256(zipArchiveFullFilePath, getChecksumsFromTextFile) )
- {
- _logger.LogError($"Checksum for {zipArchiveFullFilePath} is not valid");
- return false;
- }
+ MoveFileIfExist(windowsExifToolFolder, "exiftool(-k).exe", windowsExifToolFolder,
+ "exiftool.exe");
+ MoveFileIfExist(windowsExifToolFolder, "exiftool_files", windowsExifToolFolder,
+ "exiftool_files");
- _hostFileSystemStorage.CreateDirectory(windowsExifToolFolder);
+ _logger.LogInformation(
+ $"[DownloadForWindows] ExifTool downloaded: {ExeExifToolWindowsFullFilePath()}");
+ return _hostFileSystemStorage.ExistFile(Path.Combine(windowsExifToolFolder,
+ "exiftool.exe"));
+ }
- new Zipper(_logger).ExtractZip(zipArchiveFullFilePath, windowsExifToolFolder);
-
- MoveFileIfExist(windowsExifToolFolder, "exiftool(-k).exe", windowsExifToolFolder, "exiftool.exe");
- MoveFileIfExist(windowsExifToolFolder, "exiftool_files", windowsExifToolFolder, "exiftool_files");
+ internal void MoveFileIfExist(string searchFolder, string searchForFileOrFolder,
+ string destFolder, string destFileNameOrFolderName)
+ {
+ var files = _hostFileSystemStorage.GetAllFilesInDirectoryRecursive(searchFolder).ToList();
+ var folders = _hostFileSystemStorage.GetDirectoryRecursive(searchFolder).Select(p => p.Key);
+ List folderAndFiles = [..files, ..folders];
- _logger.LogInformation($"[DownloadForWindows] ExifTool downloaded: {ExeExifToolWindowsFullFilePath()}");
- return _hostFileSystemStorage.ExistFile(Path.Combine(windowsExifToolFolder,
- "exiftool.exe"));
+ var srcFullPaths = folderAndFiles.Where(p => p.EndsWith(searchForFileOrFolder)).ToList();
+ if ( srcFullPaths.Count == 0 )
+ {
+ _logger.LogError($"[MoveFileIfExist] Could not find {searchForFileOrFolder} file");
+ return;
}
- internal void MoveFileIfExist(string searchFolder, string searchForFileOrFolder, string destFolder, string destFileNameOrFolderName)
+ foreach ( var srcFullPath in srcFullPaths )
{
- var files = _hostFileSystemStorage.GetAllFilesInDirectoryRecursive(searchFolder).ToList();
- var folders = _hostFileSystemStorage.GetDirectoryRecursive(searchFolder).Select(p => p.Key);
- List folderAndFiles = [..files, ..folders];
-
- var srcFullPaths = folderAndFiles.Where(p => p.EndsWith(searchForFileOrFolder)).ToList();
- if ( srcFullPaths.Count == 0 )
- {
- _logger.LogError($"[MoveFileIfExist] Could not find {searchForFileOrFolder} file");
- return;
- }
+ var isFolderOrFile = _hostFileSystemStorage.Info(srcFullPath).IsFolderOrFile;
+ var destFullPath = Path.Combine(destFolder, destFileNameOrFolderName);
- foreach ( var srcFullPath in srcFullPaths )
+ switch ( isFolderOrFile )
{
- var isFolderOrFile = _hostFileSystemStorage.Info(srcFullPath).IsFolderOrFile;
- var destFullPath = Path.Combine(destFolder, destFileNameOrFolderName);
-
- switch ( isFolderOrFile )
- {
- case FolderOrFileModel.FolderOrFileTypeList.Folder:
- if ( !_hostFileSystemStorage.ExistFolder(srcFullPath) ) continue;
- _hostFileSystemStorage.FolderMove(srcFullPath, destFullPath);
- break;
- case FolderOrFileModel.FolderOrFileTypeList.File:
- if ( !_hostFileSystemStorage.ExistFile(srcFullPath) ) continue;
- _hostFileSystemStorage.FileMove(srcFullPath, destFullPath);
- break;
- default:
- break;
- }
-
+ case FolderOrFileModel.FolderOrFileTypeList.Folder:
+ if ( !_hostFileSystemStorage.ExistFolder(srcFullPath) )
+ {
+ continue;
+ }
+
+ _hostFileSystemStorage.FolderMove(srcFullPath, destFullPath);
+ break;
+ case FolderOrFileModel.FolderOrFileTypeList.File:
+ if ( !_hostFileSystemStorage.ExistFile(srcFullPath) )
+ {
+ continue;
+ }
+
+ _hostFileSystemStorage.FileMove(srcFullPath, destFullPath);
+ break;
}
}
}
diff --git a/starsky/starsky/Controllers/AllowedTypesController.cs b/starsky/starsky/Controllers/AllowedTypesController.cs
index b60eb91e9f..0882b6ccd4 100644
--- a/starsky/starsky/Controllers/AllowedTypesController.cs
+++ b/starsky/starsky/Controllers/AllowedTypesController.cs
@@ -5,61 +5,69 @@
using starsky.foundation.platform.Helpers;
using starsky.project.web.Helpers;
-namespace starsky.Controllers
+namespace starsky.Controllers;
+
+[Authorize]
+public sealed class AllowedTypesController : Controller
{
- [Authorize]
- public sealed class AllowedTypesController : Controller
+ ///
+ /// A (string) list of allowed MIME-types ExtensionSyncSupportedList
+ ///
+ /// Json list
+ /// list
+ /// please login first
+ [HttpGet("/api/allowed-types/mimetype/sync")]
+ [ProducesResponseType(typeof(HashSet), 200)]
+ [Produces("application/json")]
+ public IActionResult AllowedTypesMimetypeSync()
{
- ///
- /// A (string) list of allowed MIME-types ExtensionSyncSupportedList
- ///
- /// Json list
- /// list
- /// please login first
- [HttpGet("/api/allowed-types/mimetype/sync")]
- [ProducesResponseType(typeof(HashSet), 200)]
- [Produces("application/json")]
- public IActionResult AllowedTypesMimetypeSync()
- {
- var mimeTypes = ExtensionRolesHelper.ExtensionSyncSupportedList
- .Select(MimeHelper.GetMimeType).ToHashSet();
- return Json(mimeTypes);
- }
+ var mimeTypes = ExtensionRolesHelper.ExtensionSyncSupportedList
+ .Select(MimeHelper.GetMimeType).ToHashSet();
+ return Json(mimeTypes);
+ }
- ///
- /// A (string) list of allowed ExtensionThumbSupportedList MimeTypes
- ///
- /// Json list
- /// list
- /// please login first
- [HttpGet("/api/allowed-types/mimetype/thumb")]
- [ProducesResponseType(typeof(HashSet), 200)]
- [Produces("application/json")]
- public IActionResult AllowedTypesMimetypeSyncThumb()
+ ///
+ /// A (string) list of allowed ExtensionThumbSupportedList MimeTypes
+ ///
+ /// Json list
+ /// list
+ /// please login first
+ [HttpGet("/api/allowed-types/mimetype/thumb")]
+ [ProducesResponseType(typeof(HashSet), 200)]
+ [Produces("application/json")]
+ public IActionResult AllowedTypesMimetypeSyncThumb()
+ {
+ var mimeTypes = ExtensionRolesHelper.ExtensionThumbSupportedList
+ .Select(MimeHelper.GetMimeType).ToHashSet();
+ return Json(mimeTypes);
+ }
+
+ ///
+ /// Check if IsExtensionThumbnailSupported
+ ///
+ /// Json list
+ /// the name with extension and no parent path
+ /// is supported
+ /// the extenstion from the filename is not supported to generate thumbnails
+ /// please log in first
+ [HttpGet("/api/allowed-types/thumb")]
+ [ProducesResponseType(typeof(bool), 200)]
+ [ProducesResponseType(typeof(bool), 415)]
+ [Produces("application/json")]
+ public IActionResult AllowedTypesThumb(string f)
+ {
+ if ( !ModelState.IsValid )
{
- var mimeTypes = ExtensionRolesHelper.ExtensionThumbSupportedList
- .Select(MimeHelper.GetMimeType).ToHashSet();
- return Json(mimeTypes);
+ return BadRequest("ModelState is not valid");
}
- ///
- /// Check if IsExtensionThumbnailSupported
- ///
- /// Json list
- /// the name with extension and no parent path
- /// is supported
- /// the extenstion from the filename is not supported to generate thumbnails
- /// please login first
- [HttpGet("/api/allowed-types/thumb")]
- [ProducesResponseType(typeof(bool), 200)]
- [ProducesResponseType(typeof(bool), 415)]
- [Produces("application/json")]
- public IActionResult AllowedTypesThumb(string f)
+ var result = ExtensionRolesHelper.IsExtensionThumbnailSupported(f);
+ if ( !result )
{
- var result = ExtensionRolesHelper.IsExtensionThumbnailSupported(f);
- if ( !result ) Response.StatusCode = 415;
- return Json(result);
+ Response.StatusCode = 415;
}
+
+ return Json(result);
}
}
diff --git a/starsky/starsky/Controllers/AppSettingsController.cs b/starsky/starsky/Controllers/AppSettingsController.cs
index ad18e0b25b..d077a49ed2 100644
--- a/starsky/starsky/Controllers/AppSettingsController.cs
+++ b/starsky/starsky/Controllers/AppSettingsController.cs
@@ -8,69 +8,73 @@
using starsky.foundation.accountmanagement.Services;
using starsky.foundation.platform.Models;
-namespace starsky.Controllers
+namespace starsky.Controllers;
+
+[Authorize]
+public sealed class AppSettingsController : Controller
{
- [Authorize]
- public sealed class AppSettingsController : Controller
+ private readonly AppSettings _appSettings;
+ private readonly IUpdateAppSettingsByPath _updateAppSettingsByPath;
+
+ public AppSettingsController(AppSettings appSettings,
+ IUpdateAppSettingsByPath updateAppSettingsByPath)
{
- private readonly AppSettings _appSettings;
- private readonly IUpdateAppSettingsByPath _updateAppSettingsByPath;
+ _appSettings = appSettings;
+ _updateAppSettingsByPath = updateAppSettingsByPath;
+ }
- public AppSettingsController(AppSettings appSettings,
- IUpdateAppSettingsByPath updateAppSettingsByPath)
- {
- _appSettings = appSettings;
- _updateAppSettingsByPath = updateAppSettingsByPath;
- }
+ ///
+ /// Show the runtime settings (dont allow AllowAnonymous)
+ ///
+ /// config data, except connection strings
+ /// returns the runtime settings of Starsky
+ [HttpHead("/api/env")]
+ [HttpGet("/api/env")]
+ [Produces("application/json")]
+ [ProducesResponseType(typeof(AppSettings), 200)]
+ [ProducesResponseType(typeof(AppSettings), 401)]
+ [SuppressMessage("ReSharper", "ConditionIsAlwaysTrueOrFalse",
+ Justification = "Request in tests")]
+ public IActionResult Env()
+ {
+ var appSettings = _appSettings.CloneToDisplay();
- ///
- /// Show the runtime settings (dont allow AllowAnonymous)
- ///
- /// config data, except connection strings
- /// returns the runtime settings of Starsky
- [HttpHead("/api/env")]
- [HttpGet("/api/env")]
- [Produces("application/json")]
- [ProducesResponseType(typeof(AppSettings), 200)]
- [ProducesResponseType(typeof(AppSettings), 401)]
- [SuppressMessage("ReSharper", "ConditionIsAlwaysTrueOrFalse",
- Justification = "Request in tests")]
- public IActionResult Env()
+ // For end-to-end testing
+ if ( Request != null! && Request.Headers.Any(p => p.Key == "x-force-html") )
{
- var appSettings = _appSettings.CloneToDisplay();
-
- // For end-to-end testing
- if ( Request != null! && Request.Headers.Any(p => p.Key == "x-force-html") )
- {
- Response.Headers.ContentType = "text/html; charset=utf-8";
- }
-
- return Json(appSettings);
+ Response.Headers.ContentType = "text/html; charset=utf-8";
}
- ///
- /// Show the runtime settings (dont allow AllowAnonymous)
- ///
- /// config data, except connection strings
- /// returns the runtime settings of Starsky
- [HttpPost("/api/env")]
- [Produces("application/json")]
- [ProducesResponseType(typeof(AppSettings), 200)]
- [ProducesResponseType(typeof(AppSettings), 401)]
- [Permission(UserManager.AppPermissions.AppSettingsWrite)]
- public async Task UpdateAppSettings(
- AppSettingsTransferObject appSettingTransferObject)
+ return Json(appSettings);
+ }
+
+ ///
+ /// Show the runtime settings (dont allow AllowAnonymous)
+ ///
+ /// config data, except connection strings
+ /// returns the runtime settings of Starsky
+ [HttpPost("/api/env")]
+ [Produces("application/json")]
+ [ProducesResponseType(typeof(AppSettings), 200)]
+ [ProducesResponseType(typeof(AppSettings), 401)]
+ [Permission(UserManager.AppPermissions.AppSettingsWrite)]
+ public async Task UpdateAppSettings(
+ AppSettingsTransferObject appSettingTransferObject)
+ {
+ if ( !ModelState.IsValid )
{
- var result = await _updateAppSettingsByPath.UpdateAppSettingsAsync(
- appSettingTransferObject);
+ return BadRequest("ModelState is not valid");
+ }
- if ( !result.IsError )
- {
- return Env();
- }
+ var result = await _updateAppSettingsByPath.UpdateAppSettingsAsync(
+ appSettingTransferObject);
- Response.StatusCode = result.StatusCode;
- return Content(result.Message);
+ if ( !result.IsError )
+ {
+ return Env();
}
+
+ Response.StatusCode = result.StatusCode;
+ return Content(result.Message);
}
}
diff --git a/starsky/starsky/Controllers/CacheIndexController.cs b/starsky/starsky/Controllers/CacheIndexController.cs
index 65c0d1d79a..21e3597301 100644
--- a/starsky/starsky/Controllers/CacheIndexController.cs
+++ b/starsky/starsky/Controllers/CacheIndexController.cs
@@ -4,80 +4,100 @@
using starsky.foundation.database.Interfaces;
using starsky.foundation.platform.Models;
-namespace starsky.Controllers
+namespace starsky.Controllers;
+
+[Authorize]
+public sealed class CacheIndexController : Controller
{
- [Authorize]
- public sealed class CacheIndexController : Controller
+ private readonly AppSettings _appSettings;
+ private readonly IQuery _query;
+
+ public CacheIndexController(
+ IQuery query, AppSettings appSettings)
{
- private readonly IQuery _query;
- private readonly AppSettings _appSettings;
+ _appSettings = appSettings;
+ _query = query;
+ }
- public CacheIndexController(
- IQuery query, AppSettings appSettings)
+ ///
+ /// Get Database Cache (only the cache)
+ ///
+ /// subPath (only direct so no dot;comma list)
+ /// redirect or if json enabled a status
+ /// when json"
+ /// "cache disabled in config"
+ ///
+ /// ignored, please check if the 'f' path exist or use a folder string to clear
+ /// the cache
+ ///
+ /// User unauthorized
+ [HttpGet("/api/cache/list")]
+ public IActionResult ListCache(string f = "/")
+ {
+ if ( !ModelState.IsValid )
{
- _appSettings = appSettings;
- _query = query;
+ return BadRequest("ModelState is not valid");
}
- ///
- /// Get Database Cache (only the cache)
- ///
- /// subPath (only direct so no dot;comma list)
- /// redirect or if json enabled a status
- /// when json"
- /// "cache disabled in config"
- /// ignored, please check if the 'f' path exist or use a folder string to clear the cache
- /// User unauthorized
- [HttpGet("/api/cache/list")]
- public IActionResult ListCache(string f = "/")
+ //For folder paths only
+ if ( _appSettings.AddMemoryCache == false )
{
- //For folder paths only
- if ( _appSettings.AddMemoryCache == false )
- {
- Response.StatusCode = 412;
- return Json("cache disabled in config");
- }
-
- var (success, singleItem) = _query.CacheGetParentFolder(f);
- if ( !success || singleItem == null )
- return BadRequest(
- "ignored, please check if the 'f' path exist or use a folder string to get the cache");
+ Response.StatusCode = 412;
+ return Json("cache disabled in config");
+ }
- return Json(singleItem);
+ var (success, singleItem) = _query.CacheGetParentFolder(f);
+ if ( !success || singleItem == null )
+ {
+ return BadRequest(
+ "ignored, please check if the 'f' path exist or use a folder string to get the cache");
}
- ///
- /// Delete Database Cache (only the cache)
- ///
- /// subPath (only direct so no dot;comma list)
- /// redirect or if json enabled a status
- /// when json is true, "cache successful cleared"
- /// "cache disabled in config"
- /// ignored, please check if the 'f' path exist or use a folder string to clear the cache
- /// redirect back to the url
- /// User unauthorized
- [HttpGet("/api/remove-cache")]
- [HttpPost("/api/remove-cache")]
- [ProducesResponseType(200)] // "cache successful cleared"
- [ProducesResponseType(412)] // "cache disabled in config"
- [ProducesResponseType(400)] // "ignored, please check if the 'f' path exist or use a folder string to clear the cache"
- [ProducesResponseType(302)] // redirect back to the url
- public IActionResult RemoveCache(string f = "/")
+ return Json(singleItem);
+ }
+
+ ///
+ /// Delete Database Cache (only the cache)
+ ///
+ /// subPath (only direct so no dot;comma list)
+ /// redirect or if json enabled a status
+ /// when json is true, "cache successful cleared"
+ /// "cache disabled in config"
+ ///
+ /// ignored, please check if the 'f' path exist or use a folder string to clear
+ /// the cache
+ ///
+ /// redirect back to the url
+ /// User unauthorized
+ [HttpGet("/api/remove-cache")]
+ [HttpPost("/api/remove-cache")]
+ [ProducesResponseType(200)] // "cache successful cleared"
+ [ProducesResponseType(412)] // "cache disabled in config"
+ [ProducesResponseType(400)] // "ignored, please check if the 'f' path exist or use a folder string to clear the cache"
+ [ProducesResponseType(302)] // redirect back to the url
+ public IActionResult RemoveCache(string f = "/")
+ {
+ if ( !ModelState.IsValid )
{
- //For folder paths only
- if ( _appSettings.AddMemoryCache == false )
- {
- Response.StatusCode = 412;
- return Json("cache disabled in config");
- }
+ return BadRequest("ModelState is not valid");
+ }
- var singleItem = _query.SingleItem(f);
- if ( singleItem == null || !singleItem.IsDirectory )
- return BadRequest(
- "ignored, please check if the 'f' path exist or use a folder string to clear the cache");
+ //For folder paths only
+ if ( _appSettings.AddMemoryCache == false )
+ {
+ Response.StatusCode = 412;
+ return Json("cache disabled in config");
+ }
- return Json(_query.RemoveCacheParentItem(f) ? "cache successful cleared" : "cache did not exist");
+ var singleItem = _query.SingleItem(f);
+ if ( singleItem == null || !singleItem.IsDirectory )
+ {
+ return BadRequest(
+ "ignored, please check if the 'f' path exist or use a folder string to clear the cache");
}
+ return Json(_query.RemoveCacheParentItem(f)
+ ? "cache successful cleared"
+ : "cache did not exist");
}
}
diff --git a/starsky/starsky/Controllers/DeleteController.cs b/starsky/starsky/Controllers/DeleteController.cs
index 9257b03242..e7b8cf45b9 100644
--- a/starsky/starsky/Controllers/DeleteController.cs
+++ b/starsky/starsky/Controllers/DeleteController.cs
@@ -5,44 +5,52 @@
using starsky.feature.metaupdate.Interfaces;
using starsky.foundation.database.Models;
-namespace starsky.Controllers
+namespace starsky.Controllers;
+
+[Authorize]
+public sealed class DeleteController : Controller
{
- [Authorize]
- public sealed class DeleteController : Controller
+ private readonly IDeleteItem _deleteItem;
+
+ public DeleteController(IDeleteItem deleteItem)
{
- private readonly IDeleteItem _deleteItem;
+ _deleteItem = deleteItem;
+ }
- public DeleteController(IDeleteItem deleteItem)
+ ///
+ /// Remove files from the disk, but the file must contain the !delete!
+ /// (TrashKeyword.TrashKeywordString) tag
+ ///
+ /// subPaths, separated by dot comma
+ /// true is to update files with the same name before the extenstion
+ /// list of deleted files
+ /// file is gone
+ ///
+ /// item not found on disk or !delete! (TrashKeyword.TrashKeywordString) tag is
+ /// missing
+ ///
+ /// User unauthorized
+ [HttpDelete("/api/delete")]
+ [ProducesResponseType(typeof(List), 200)]
+ [ProducesResponseType(typeof(List), 404)]
+ [Produces("application/json")]
+ public async Task Delete(string f, bool collections = false)
+ {
+ if ( !ModelState.IsValid )
{
- _deleteItem = deleteItem;
+ return BadRequest("ModelState is not valid");
}
- ///
- /// Remove files from the disk, but the file must contain the !delete! (TrashKeyword.TrashKeywordString) tag
- ///
- /// subPaths, separated by dot comma
- /// true is to update files with the same name before the extenstion
- /// list of deleted files
- /// file is gone
- /// item not found on disk or !delete! (TrashKeyword.TrashKeywordString) tag is missing
- /// User unauthorized
- [HttpDelete("/api/delete")]
- [ProducesResponseType(typeof(List), 200)]
- [ProducesResponseType(typeof(List), 404)]
- [Produces("application/json")]
- public async Task Delete(string f, bool collections = false)
+ var fileIndexResultsList = await _deleteItem.DeleteAsync(f, collections);
+ // When all items are not found
+ // ok = file is deleted
+ if ( fileIndexResultsList.TrueForAll(p =>
+ p.Status != FileIndexItem.ExifStatus.Ok) )
{
- var fileIndexResultsList = await _deleteItem.DeleteAsync(f, collections);
- // When all items are not found
- // ok = file is deleted
- if ( fileIndexResultsList.TrueForAll(p =>
- p.Status != FileIndexItem.ExifStatus.Ok) )
- {
- return NotFound(fileIndexResultsList);
- }
+ return NotFound(fileIndexResultsList);
+ }
- return Json(fileIndexResultsList);
- }
+ return Json(fileIndexResultsList);
}
}
diff --git a/starsky/starsky/Controllers/DesktopEditorController.cs b/starsky/starsky/Controllers/DesktopEditorController.cs
index 0cfdbbd1ce..9dc2cd584f 100644
--- a/starsky/starsky/Controllers/DesktopEditorController.cs
+++ b/starsky/starsky/Controllers/DesktopEditorController.cs
@@ -18,13 +18,13 @@ public DesktopEditorController(IOpenEditorDesktopService openEditorDesktopServic
}
///
- /// Open a file in the default editor or a specific editor on the desktop
+ /// Open a file in the default editor or a specific editor on the desktop
///
/// single or multiple subPaths
/// to combine files with the same name before the extension
///
/// returns a list of items from the database
- /// list with no content
+ /// list with no content
/// subPath not found in the database
/// User unauthorized
[HttpPost("/api/desktop-editor/open")]
@@ -37,6 +37,11 @@ public async Task OpenAsync(
string f = "",
bool collections = true)
{
+ if ( !ModelState.IsValid )
+ {
+ return BadRequest("ModelState is not valid");
+ }
+
var (success, status, list) =
await _openEditorDesktopService.OpenAsync(f, collections);
@@ -54,7 +59,7 @@ public async Task OpenAsync(
///
- /// Check the amount of files to open before
+ /// Check the amount of files to open before
///
/// single or multiple subPaths
///
@@ -64,8 +69,14 @@ public async Task OpenAsync(
[Produces("application/json")]
[ProducesResponseType(typeof(bool), 200)]
[ProducesResponseType(401)]
+ [ProducesResponseType(400)]
public IActionResult OpenAmountConfirmationChecker(string f)
{
+ if ( !ModelState.IsValid )
+ {
+ return BadRequest("ModelState is not valid");
+ }
+
var result = _openEditorDesktopService.OpenAmountConfirmationChecker(f);
return Json(result);
}
diff --git a/starsky/starsky/Controllers/DiskController.cs b/starsky/starsky/Controllers/DiskController.cs
index e8c8666dc4..7f61dddfea 100644
--- a/starsky/starsky/Controllers/DiskController.cs
+++ b/starsky/starsky/Controllers/DiskController.cs
@@ -1,4 +1,5 @@
using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@@ -15,146 +16,149 @@
using starsky.foundation.storage.Storage;
using starsky.project.web.ViewModels;
-namespace starsky.Controllers
+namespace starsky.Controllers;
+
+[Authorize]
+public sealed class DiskController : Controller
{
- [Authorize]
- public sealed class DiskController : Controller
+ private readonly IWebSocketConnectionsService _connectionsService;
+ private readonly IStorage _iStorage;
+ private readonly INotificationQuery _notificationQuery;
+ private readonly IQuery _query;
+
+ public DiskController(IQuery query, ISelectorStorage selectorStorage,
+ IWebSocketConnectionsService connectionsService, INotificationQuery notificationQuery)
{
- private readonly IQuery _query;
- private readonly IStorage _iStorage;
- private readonly IWebSocketConnectionsService _connectionsService;
- private readonly INotificationQuery _notificationQuery;
+ _query = query;
+ _iStorage = selectorStorage.Get(SelectorStorage.StorageServices.SubPath);
+ _connectionsService = connectionsService;
+ _notificationQuery = notificationQuery;
+ }
- public DiskController(IQuery query, ISelectorStorage selectorStorage,
- IWebSocketConnectionsService connectionsService, INotificationQuery notificationQuery)
+ ///
+ /// Make a directory (-p)
+ ///
+ /// subPaths split by dot comma
+ /// list of changed files IActionResult Mkdir
+ /// create the item on disk and in db
+ /// A conflict, Directory already exist
+ /// missing path
+ /// User unauthorized
+ [HttpPost("/api/disk/mkdir")]
+ [ProducesResponseType(typeof(List), 200)]
+ [ProducesResponseType(typeof(List), 409)]
+ [ProducesResponseType(typeof(List), 400)]
+ [ProducesResponseType(typeof(string), 401)]
+ [Produces("application/json")]
+ public async Task Mkdir(string f)
+ {
+ var inputFilePaths = PathHelper.SplitInputFilePaths(f).ToList();
+ if ( inputFilePaths.Count == 0 )
{
- _query = query;
- _iStorage = selectorStorage.Get(SelectorStorage.StorageServices.SubPath);
- _connectionsService = connectionsService;
- _notificationQuery = notificationQuery;
+ Response.StatusCode = 400;
+ return Json(new List());
}
- ///
- /// Make a directory (-p)
- ///
- /// subPaths split by dot comma
- /// list of changed files IActionResult Mkdir
- /// create the item on disk and in db
- /// A conflict, Directory already exist
- /// missing path
- /// User unauthorized
- [HttpPost("/api/disk/mkdir")]
- [ProducesResponseType(typeof(List), 200)]
- [ProducesResponseType(typeof(List), 409)]
- [ProducesResponseType(typeof(List), 400)]
- [ProducesResponseType(typeof(string), 401)]
- [Produces("application/json")]
- public async Task Mkdir(string f)
+ var syncResultsList = new List();
+
+ foreach ( var subPath in inputFilePaths.Select(PathHelper.RemoveLatestSlash) )
{
- var inputFilePaths = PathHelper.SplitInputFilePaths(f).ToList();
- if ( inputFilePaths.Count == 0 )
+ var toAddStatus = new SyncViewModel
{
- Response.StatusCode = 400;
- return Json(new List());
- }
+ FilePath = subPath, Status = FileIndexItem.ExifStatus.Ok
+ };
- var syncResultsList = new List();
-
- foreach ( var subPath in inputFilePaths.Select(PathHelper.RemoveLatestSlash) )
+ if ( _iStorage.ExistFolder(subPath) )
{
- var toAddStatus = new SyncViewModel
- {
- FilePath = subPath, Status = FileIndexItem.ExifStatus.Ok
- };
-
- if ( _iStorage.ExistFolder(subPath) )
- {
- toAddStatus.Status = FileIndexItem.ExifStatus.OperationNotSupported;
- syncResultsList.Add(toAddStatus);
- continue;
- }
-
- await _query.AddItemAsync(new FileIndexItem(subPath)
- {
- IsDirectory = true, ImageFormat = ExtensionRolesHelper.ImageFormat.directory
- });
-
- // add to fs
- _iStorage.CreateDirectory(subPath);
-
+ toAddStatus.Status = FileIndexItem.ExifStatus.OperationNotSupported;
syncResultsList.Add(toAddStatus);
+ continue;
}
- // When all items are not found
- if ( syncResultsList.TrueForAll(p => p.Status != FileIndexItem.ExifStatus.Ok) )
- Response.StatusCode = 409; // A conflict, Directory already exist
+ await _query.AddItemAsync(new FileIndexItem(subPath)
+ {
+ IsDirectory = true, ImageFormat = ExtensionRolesHelper.ImageFormat.directory
+ });
- await SyncMessageToSocket(syncResultsList, ApiNotificationType.Mkdir);
+ // add to fs
+ _iStorage.CreateDirectory(subPath);
- return Json(syncResultsList);
+ syncResultsList.Add(toAddStatus);
}
- ///
- /// Update other users with a message from SyncViewModel
- ///
- /// SyncViewModel
- /// optional debug name
- /// Completed send of Socket SendToAllAsync
- private async Task SyncMessageToSocket(IEnumerable syncResultsList,
- ApiNotificationType type = ApiNotificationType.Unknown)
+ // When all items are not found
+ if ( syncResultsList.TrueForAll(p => p.Status != FileIndexItem.ExifStatus.Ok) )
{
- var list = syncResultsList.Select(t => new FileIndexItem(t.FilePath)
- {
- Status = t.Status, IsDirectory = true
- }).ToList();
+ Response.StatusCode = 409; // A conflict, Directory already exist
+ }
- var webSocketResponse = new ApiNotificationResponseModel<
- List>(list, type);
+ await SyncMessageToSocket(syncResultsList, ApiNotificationType.Mkdir);
- await _notificationQuery.AddNotification(webSocketResponse);
- await _connectionsService.SendToAllAsync(webSocketResponse, CancellationToken.None);
- }
+ return Json(syncResultsList);
+ }
- ///
- /// Rename file/folder and update it in the database
- ///
- /// from subPath
- /// to subPath
- /// is collections bool
- /// default is to not included files that are removed in result
- /// list of details form changed files (IActionResult Rename)
- /// the item including the updated content
- /// item not found in the database or on disk
- /// User unauthorized
- [ProducesResponseType(typeof(List), 200)]
- [ProducesResponseType(typeof(List), 404)]
- [HttpPost("/api/disk/rename")]
- [Produces("application/json")]
- public async Task Rename(string f, string to, bool collections = true,
- bool currentStatus = true)
+ ///
+ /// Update other users with a message from SyncViewModel
+ ///
+ /// SyncViewModel
+ /// optional debug name
+ /// Completed send of Socket SendToAllAsync
+ private async Task SyncMessageToSocket(IEnumerable syncResultsList,
+ ApiNotificationType type = ApiNotificationType.Unknown)
+ {
+ var list = syncResultsList.Select(t => new FileIndexItem(t.FilePath)
{
- if ( string.IsNullOrEmpty(f) )
- {
- return BadRequest("No input files");
- }
+ Status = t.Status, IsDirectory = true
+ }).ToList();
- var rename = await new RenameService(_query, _iStorage).Rename(f, to, collections);
+ var webSocketResponse = new ApiNotificationResponseModel<
+ List>(list, type);
- // When all items are not found
- if ( rename.TrueForAll(p => p.Status != FileIndexItem.ExifStatus.Ok) )
- return NotFound(rename);
+ await _notificationQuery.AddNotification(webSocketResponse);
+ await _connectionsService.SendToAllAsync(webSocketResponse, CancellationToken.None);
+ }
- var webSocketResponse =
- new ApiNotificationResponseModel>(rename,
- ApiNotificationType.Rename);
+ ///
+ /// Rename file/folder and update it in the database
+ ///
+ /// from subPath
+ /// to subPath
+ /// is collections bool
+ /// default is to not included files that are removed in result
+ /// list of details form changed files (IActionResult Rename)
+ /// the item including the updated content
+ /// item not found in the database or on disk
+ /// User unauthorized
+ [ProducesResponseType(typeof(List), 200)]
+ [ProducesResponseType(typeof(List), 404)]
+ [HttpPost("/api/disk/rename")]
+ [Produces("application/json")]
+ public async Task Rename([Required] string f, string to, bool collections = true,
+ bool currentStatus = true)
+ {
+ if ( string.IsNullOrEmpty(f) || !ModelState.IsValid )
+ {
+ return BadRequest("No input files");
+ }
- await _notificationQuery.AddNotification(webSocketResponse);
- await _connectionsService.SendToAllAsync(webSocketResponse, CancellationToken.None);
+ var rename = await new RenameService(_query, _iStorage).Rename(f, to, collections);
- return Json(currentStatus
- ? rename.Where(p => p.Status
- != FileIndexItem.ExifStatus.NotFoundSourceMissing).ToList()
- : rename);
+ // When all items are not found
+ if ( rename.TrueForAll(p => p.Status != FileIndexItem.ExifStatus.Ok) )
+ {
+ return NotFound(rename);
}
+
+ var webSocketResponse =
+ new ApiNotificationResponseModel>(rename,
+ ApiNotificationType.Rename);
+
+ await _notificationQuery.AddNotification(webSocketResponse);
+ await _connectionsService.SendToAllAsync(webSocketResponse, CancellationToken.None);
+
+ return Json(currentStatus
+ ? rename.Where(p => p.Status
+ != FileIndexItem.ExifStatus.NotFoundSourceMissing).ToList()
+ : rename);
}
}
diff --git a/starsky/starsky/Controllers/DownloadPhotoController.cs b/starsky/starsky/Controllers/DownloadPhotoController.cs
index 6593541b9f..57bf7b5af9 100644
--- a/starsky/starsky/Controllers/DownloadPhotoController.cs
+++ b/starsky/starsky/Controllers/DownloadPhotoController.cs
@@ -1,3 +1,4 @@
+using System.ComponentModel.DataAnnotations;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
@@ -12,129 +13,147 @@
using starsky.Helpers;
using starsky.project.web.Helpers;
-namespace starsky.Controllers
+namespace starsky.Controllers;
+
+[Authorize]
+public sealed class DownloadPhotoController : Controller
{
- [Authorize]
- public sealed class DownloadPhotoController : Controller
+ private readonly IStorage _iStorage;
+ private readonly IWebLogger _logger;
+ private readonly IQuery _query;
+ private readonly IThumbnailService _thumbnailService;
+ private readonly IStorage _thumbnailStorage;
+
+ public DownloadPhotoController(IQuery query, ISelectorStorage selectorStorage,
+ IWebLogger logger, IThumbnailService thumbnailService)
+ {
+ _query = query;
+ _iStorage = selectorStorage.Get(SelectorStorage.StorageServices.SubPath);
+ _thumbnailStorage = selectorStorage.Get(SelectorStorage.StorageServices.Thumbnail);
+ _thumbnailService = thumbnailService;
+ _logger = logger;
+ }
+
+ ///
+ /// Download sidecar file for example image.xmp
+ ///
+ /// string, subPath to find the file
+ /// FileStream with image
+ /// returns content of the file
+ /// source image missing
+ /// User unauthorized
+ [HttpGet("/api/download-sidecar")]
+ [ProducesResponseType(200)] // file
+ [ProducesResponseType(404)] // not found
+ [ProducesResponseType(400)]
+ public IActionResult DownloadSidecar([Required] string f)
{
- private readonly IQuery _query;
- private readonly IStorage _iStorage;
- private readonly IStorage _thumbnailStorage;
- private readonly IWebLogger _logger;
- private readonly IThumbnailService _thumbnailService;
-
- public DownloadPhotoController(IQuery query, ISelectorStorage selectorStorage,
- IWebLogger logger, IThumbnailService thumbnailService)
+ if ( !ModelState.IsValid )
{
- _query = query;
- _iStorage = selectorStorage.Get(SelectorStorage.StorageServices.SubPath);
- _thumbnailStorage = selectorStorage.Get(SelectorStorage.StorageServices.Thumbnail);
- _thumbnailService = thumbnailService;
- _logger = logger;
+ return BadRequest("ModelState is not valid");
}
- ///
- /// Download sidecar file for example image.xmp
- ///
- /// string, subPath to find the file
- /// FileStream with image
- /// returns content of the file
- /// source image missing
- /// User unauthorized
- [HttpGet("/api/download-sidecar")]
- [ProducesResponseType(200)] // file
- [ProducesResponseType(404)] // not found
- public IActionResult DownloadSidecar(string f)
+ if ( !ExtensionRolesHelper.IsExtensionSidecar(f) )
{
- if ( !ExtensionRolesHelper.IsExtensionSidecar(f) )
- {
- return NotFound("FileName is not a sidecar");
- }
+ return NotFound("FileName is not a sidecar");
+ }
- if ( !_iStorage.ExistFile(f) )
- return NotFound($"source image missing {f}");
+ if ( !_iStorage.ExistFile(f) )
+ {
+ return NotFound($"source image missing {f}");
+ }
- var fs = _iStorage.ReadStream(f);
- return File(fs, MimeHelper.GetMimeTypeByFileName(f));
+ var fs = _iStorage.ReadStream(f);
+ return File(fs, MimeHelper.GetMimeTypeByFileName(f));
+ }
+
+ ///
+ /// Select manually the original or thumbnail
+ ///
+ /// string, 'sub path' to find the file
+ /// true = 1000px thumb (if supported)
+ /// true = send client headers to cache
+ /// FileStream with image
+ /// returns content of the file
+ /// source image missing
+ /// "Thumbnail generation failed"
+ /// User unauthorized
+ [HttpGet("/api/download-photo")]
+ [ProducesResponseType(200)] // file
+ [ProducesResponseType(404)] // not found
+ [ProducesResponseType(500)] // "Thumbnail generation failed"
+ public async Task DownloadPhoto(string f, bool isThumbnail = true,
+ bool cache = true)
+ {
+ if ( !ModelState.IsValid )
+ {
+ return BadRequest("ModelState is not valid");
}
- ///
- /// Select manually the original or thumbnail
- ///
- /// string, 'sub path' to find the file
- /// true = 1000px thumb (if supported)
- /// true = send client headers to cache
- /// FileStream with image
- /// returns content of the file
- /// source image missing
- /// "Thumbnail generation failed"
- /// User unauthorized
- [HttpGet("/api/download-photo")]
- [ProducesResponseType(200)] // file
- [ProducesResponseType(404)] // not found
- [ProducesResponseType(500)] // "Thumbnail generation failed"
- public async Task DownloadPhoto(string f, bool isThumbnail = true,
- bool cache = true)
+ if ( f.Contains("?isthumbnail") )
{
// f = subpath/filepath
- if ( f.Contains("?isthumbnail") )
- {
- return NotFound("please use &isthumbnail = instead of ?isthumbnail= ");
- }
+ return NotFound("please use &isthumbnail = instead of ?isthumbnail= ");
+ }
- var fileIndexItem = await _query.GetObjectByFilePathAsync(f);
- if ( fileIndexItem == null )
+ var fileIndexItem = await _query.GetObjectByFilePathAsync(f);
+ if ( fileIndexItem == null )
+ {
+ return NotFound("not in index " + f);
+ }
+
+ if ( !_iStorage.ExistFile(fileIndexItem.FilePath!) )
+ {
+ return NotFound($"source image missing {fileIndexItem.FilePath}");
+ }
+
+ // Return full image
+ if ( !isThumbnail )
+ {
+ if ( cache )
{
- return NotFound("not in index " + f);
+ CacheControlOverwrite.SetExpiresResponseHeaders(Request);
}
- if ( !_iStorage.ExistFile(fileIndexItem.FilePath!) )
- return NotFound($"source image missing {fileIndexItem.FilePath}");
+ var fileStream = _iStorage.ReadStream(fileIndexItem.FilePath!);
- // Return full image
- if ( !isThumbnail )
- {
- if ( cache ) CacheControlOverwrite.SetExpiresResponseHeaders(Request);
- var fileStream = _iStorage.ReadStream(fileIndexItem.FilePath!);
+ // Return the right mime type (enableRangeProcessing = needed for safari and mp4)
+ return File(fileStream, MimeHelper.GetMimeTypeByFileName(fileIndexItem.FilePath!),
+ true);
+ }
- // Return the right mime type (enableRangeProcessing = needed for safari and mp4)
- return File(fileStream, MimeHelper.GetMimeTypeByFileName(fileIndexItem.FilePath!),
- true);
- }
+ if ( !_thumbnailStorage.ExistFolder("/") )
+ {
+ return NotFound("ThumbnailTempFolder not found");
+ }
- if ( !_thumbnailStorage.ExistFolder("/") )
- {
- return NotFound("ThumbnailTempFolder not found");
- }
+ var data = new ThumbnailSizesExistStatusModel
+ {
+ Small = _thumbnailStorage.ExistFile(
+ ThumbnailNameHelper.Combine(fileIndexItem.FileHash!, ThumbnailSize.Small)),
+ Large = _thumbnailStorage.ExistFile(
+ ThumbnailNameHelper.Combine(fileIndexItem.FileHash!, ThumbnailSize.Large)),
+ ExtraLarge = _thumbnailStorage.ExistFile(
+ ThumbnailNameHelper.Combine(fileIndexItem.FileHash!, ThumbnailSize.ExtraLarge))
+ };
- var data = new ThumbnailSizesExistStatusModel
- {
- Small = _thumbnailStorage.ExistFile(
- ThumbnailNameHelper.Combine(fileIndexItem.FileHash!, ThumbnailSize.Small)),
- Large = _thumbnailStorage.ExistFile(
- ThumbnailNameHelper.Combine(fileIndexItem.FileHash!, ThumbnailSize.Large)),
- ExtraLarge = _thumbnailStorage.ExistFile(
- ThumbnailNameHelper.Combine(fileIndexItem.FileHash!, ThumbnailSize.ExtraLarge))
- };
-
- if ( !data.Small || !data.Large || !data.ExtraLarge )
+ if ( !data.Small || !data.Large || !data.ExtraLarge )
+ {
+ _logger.LogDebug("Thumbnail generation started");
+ await _thumbnailService.CreateThumbAsync(fileIndexItem.FilePath!,
+ fileIndexItem.FileHash!);
+
+ if ( !_thumbnailStorage.ExistFile(
+ ThumbnailNameHelper.Combine(fileIndexItem.FileHash!,
+ ThumbnailSize.Large)) )
{
- _logger.LogDebug("Thumbnail generation started");
- await _thumbnailService.CreateThumbAsync(fileIndexItem.FilePath!,
- fileIndexItem.FileHash!);
-
- if ( !_thumbnailStorage.ExistFile(
- ThumbnailNameHelper.Combine(fileIndexItem.FileHash!,
- ThumbnailSize.Large)) )
- {
- Response.StatusCode = 500;
- return Json("Thumbnail generation failed");
- }
+ Response.StatusCode = 500;
+ return Json("Thumbnail generation failed");
}
-
- var thumbnailFileStream = _thumbnailStorage.ReadStream(
- ThumbnailNameHelper.Combine(fileIndexItem.FileHash!, ThumbnailSize.Large));
- return File(thumbnailFileStream, "image/jpeg");
}
+
+ var thumbnailFileStream = _thumbnailStorage.ReadStream(
+ ThumbnailNameHelper.Combine(fileIndexItem.FileHash!, ThumbnailSize.Large));
+ return File(thumbnailFileStream, "image/jpeg");
}
}
diff --git a/starsky/starsky/Controllers/ErrorController.cs b/starsky/starsky/Controllers/ErrorController.cs
index 5ad82af93d..57689595a5 100644
--- a/starsky/starsky/Controllers/ErrorController.cs
+++ b/starsky/starsky/Controllers/ErrorController.cs
@@ -3,37 +3,40 @@
using Microsoft.AspNetCore.Mvc;
using starsky.foundation.platform.Models;
-namespace starsky.Controllers
+namespace starsky.Controllers;
+
+[AllowAnonymous]
+public sealed class ErrorController : Controller
{
- [AllowAnonymous]
- public sealed class ErrorController : Controller
+ private readonly string _clientApp;
+
+ public ErrorController(AppSettings appSettings)
{
- private readonly string _clientApp;
+ _clientApp = Path.Combine(appSettings.BaseDirectoryProject,
+ "clientapp", "build", "index.html");
+ }
- public ErrorController(AppSettings appSettings)
+ ///
+ /// Return Error page (HTML)
+ ///
+ /// to add the status code to the response
+ /// Any Error html page
+ [HttpGet("/error")]
+ [Produces("text/html")]
+ public IActionResult Error(int? statusCode = null)
+ {
+ if ( !ModelState.IsValid )
{
- _clientApp = Path.Combine(appSettings.BaseDirectoryProject,
- "clientapp", "build", "index.html");
+ return BadRequest("Model is invalid");
}
- ///
- /// Return Error page (HTML)
- ///
- /// to add the status code to the response
- /// Any Error html page
- [HttpGet("/error")]
- [Produces("text/html")]
- public IActionResult Error(int? statusCode = null)
+ if ( statusCode.HasValue )
{
- if ( statusCode.HasValue )
- {
- // here is the trick
- HttpContext.Response.StatusCode = statusCode.Value;
- }
-
- // or "~/error/${statusCode}.html"
- return PhysicalFile(_clientApp, "text/html");
+ // here is the trick
+ HttpContext.Response.StatusCode = statusCode.Value;
}
- }
+ // or "~/error/${statusCode}.html"
+ return PhysicalFile(_clientApp, "text/html");
+ }
}
diff --git a/starsky/starsky/Controllers/UploadController.cs b/starsky/starsky/Controllers/UploadController.cs
index 61840c98a3..e5ec1643ba 100644
--- a/starsky/starsky/Controllers/UploadController.cs
+++ b/starsky/starsky/Controllers/UploadController.cs
@@ -15,7 +15,6 @@
using starsky.foundation.database.Interfaces;
using starsky.foundation.database.Models;
using starsky.foundation.http.Streaming;
-using starsky.foundation.thumbnailmeta.Interfaces;
using starsky.foundation.platform.Enums;
using starsky.foundation.platform.Helpers;
using starsky.foundation.platform.Interfaces;
@@ -24,301 +23,324 @@
using starsky.foundation.storage.Interfaces;
using starsky.foundation.storage.Storage;
using starsky.foundation.sync.SyncServices;
+using starsky.foundation.thumbnailmeta.Interfaces;
+
+namespace starsky.Controllers;
-namespace starsky.Controllers
+[Authorize] // <- should be logged in!
+[SuppressMessage("Usage", "S5693:Make sure the content " +
+ "length limit is safe here", Justification = "Is checked")]
+public sealed class UploadController : Controller
{
- [Authorize] // <- should be logged in!
- [SuppressMessage("Usage", "S5693:Make sure the content " +
- "length limit is safe here", Justification = "Is checked")]
- public sealed class UploadController : Controller
+ private readonly AppSettings _appSettings;
+ private readonly IStorage _iHostStorage;
+ private readonly IImport _import;
+ private readonly IStorage _iStorage;
+ private readonly IWebLogger _logger;
+ private readonly IMetaExifThumbnailService _metaExifThumbnailService;
+ private readonly IMetaUpdateStatusThumbnailService _metaUpdateStatusThumbnailService;
+ private readonly IQuery _query;
+ private readonly IRealtimeConnectionsService _realtimeService;
+ private readonly ISelectorStorage _selectorStorage;
+
+ [SuppressMessage("Usage",
+ "S107: Constructor has 8 parameters, which is greater than the 7 authorized")]
+ public UploadController(IImport import, AppSettings appSettings,
+ ISelectorStorage selectorStorage, IQuery query,
+ IRealtimeConnectionsService realtimeService, IWebLogger logger,
+ IMetaExifThumbnailService metaExifThumbnailService,
+ IMetaUpdateStatusThumbnailService metaUpdateStatusThumbnailService)
{
- private readonly AppSettings _appSettings;
- private readonly IImport _import;
- private readonly IStorage _iStorage;
- private readonly IStorage _iHostStorage;
- private readonly IQuery _query;
- private readonly ISelectorStorage _selectorStorage;
- private readonly IRealtimeConnectionsService _realtimeService;
- private readonly IWebLogger _logger;
- private readonly IMetaExifThumbnailService _metaExifThumbnailService;
- private readonly IMetaUpdateStatusThumbnailService _metaUpdateStatusThumbnailService;
-
- [SuppressMessage("Usage",
- "S107: Constructor has 8 parameters, which is greater than the 7 authorized")]
- public UploadController(IImport import, AppSettings appSettings,
- ISelectorStorage selectorStorage, IQuery query,
- IRealtimeConnectionsService realtimeService, IWebLogger logger,
- IMetaExifThumbnailService metaExifThumbnailService,
- IMetaUpdateStatusThumbnailService metaUpdateStatusThumbnailService)
+ _appSettings = appSettings;
+ _import = import;
+ _query = query;
+ _selectorStorage = selectorStorage;
+ _iStorage = selectorStorage.Get(SelectorStorage.StorageServices.SubPath);
+ _iHostStorage = selectorStorage.Get(SelectorStorage.StorageServices.HostFilesystem);
+ _realtimeService = realtimeService;
+ _logger = logger;
+ _metaExifThumbnailService = metaExifThumbnailService;
+ _metaUpdateStatusThumbnailService =
+ metaUpdateStatusThumbnailService;
+ }
+
+ ///
+ /// Upload to specific folder (does not check if already has been imported)
+ /// Use the header 'to' to determine the location to where to upload
+ /// Add header 'filename' when uploading direct without form
+ /// (ActionResult UploadToFolder)
+ ///
+ /// done
+ /// folder not found
+ /// Wrong input (e.g. wrong extenstion type)
+ /// missing 'to' header
+ /// the ImportIndexItem of the imported files
+ [HttpPost("/api/upload")]
+ [DisableFormValueModelBinding]
+ [RequestFormLimits(MultipartBodyLengthLimit = 320_000_000)]
+ [RequestSizeLimit(320_000_000)] // in bytes, 305MB
+ [ProducesResponseType(typeof(List), 200)] // yes
+ [ProducesResponseType(typeof(string), 400)]
+ [ProducesResponseType(typeof(List), 404)]
+ [ProducesResponseType(typeof(List),
+ 415)] // Wrong input (e.g. wrong extenstion type)
+ [Produces("application/json")]
+ [SuppressMessage("Usage", "S6932: Use model binding instead of accessing the raw request data")]
+ public async Task UploadToFolder()
+ {
+ var to = Request.Headers["to"].ToString();
+ if ( string.IsNullOrWhiteSpace(to) )
{
- _appSettings = appSettings;
- _import = import;
- _query = query;
- _selectorStorage = selectorStorage;
- _iStorage = selectorStorage.Get(SelectorStorage.StorageServices.SubPath);
- _iHostStorage = selectorStorage.Get(SelectorStorage.StorageServices.HostFilesystem);
- _realtimeService = realtimeService;
- _logger = logger;
- _metaExifThumbnailService = metaExifThumbnailService;
- _metaUpdateStatusThumbnailService =
- metaUpdateStatusThumbnailService;
+ return BadRequest("missing 'to' header");
}
- ///
- /// Upload to specific folder (does not check if already has been imported)
- /// Use the header 'to' to determine the location to where to upload
- /// Add header 'filename' when uploading direct without form
- /// (ActionResult UploadToFolder)
- ///
- /// done
- /// folder not found
- /// Wrong input (e.g. wrong extenstion type)
- /// missing 'to' header
- /// the ImportIndexItem of the imported files
- [HttpPost("/api/upload")]
- [DisableFormValueModelBinding]
- [RequestFormLimits(MultipartBodyLengthLimit = 320_000_000)]
- [RequestSizeLimit(320_000_000)] // in bytes, 305MB
- [ProducesResponseType(typeof(List), 200)] // yes
- [ProducesResponseType(typeof(string), 400)]
- [ProducesResponseType(typeof(List), 404)]
- [ProducesResponseType(typeof(List),
- 415)] // Wrong input (e.g. wrong extenstion type)
- [Produces("application/json")]
- public async Task UploadToFolder()
+ var parentDirectory = GetParentDirectoryFromRequestHeader();
+ if ( parentDirectory == null )
{
- var to = Request.Headers["to"].ToString();
- if ( string.IsNullOrWhiteSpace(to) ) return BadRequest("missing 'to' header");
+ return NotFound(new ImportIndexItem { Status = ImportStatus.ParentDirectoryNotFound });
+ }
+
+ var tempImportPaths = await Request.StreamFile(_appSettings, _selectorStorage);
+
+ var fileIndexResultsList = await _import.Preflight(tempImportPaths,
+ new ImportSettingsModel { IndexMode = false });
+ // fail/pass, right type, string=subPath, string?2= error reason
+ var metaResultsList = new List<(bool, bool, string, string?)>();
- var parentDirectory = GetParentDirectoryFromRequestHeader();
- if ( parentDirectory == null )
+ for ( var i = 0; i < fileIndexResultsList.Count; i++ )
+ {
+ if ( fileIndexResultsList[i].Status != ImportStatus.Ok )
{
- return NotFound(new ImportIndexItem
- {
- Status = ImportStatus.ParentDirectoryNotFound
- });
+ continue;
}
- var tempImportPaths = await Request.StreamFile(_appSettings, _selectorStorage);
+ var tempFileStream = _iHostStorage.ReadStream(tempImportPaths[i]);
- var fileIndexResultsList = await _import.Preflight(tempImportPaths,
- new ImportSettingsModel { IndexMode = false });
- // fail/pass, right type, string=subPath, string?2= error reason
- var metaResultsList = new List<(bool, bool, string, string?)>();
+ var fileName = Path.GetFileName(tempImportPaths[i]);
- for ( var i = 0; i < fileIndexResultsList.Count; i++ )
+ // subPath is always unix style
+ var subPath = PathHelper.AddSlash(parentDirectory) + fileName;
+ if ( parentDirectory == "/" )
{
- if ( fileIndexResultsList[i].Status != ImportStatus.Ok )
- {
- continue;
- }
-
- var tempFileStream = _iHostStorage.ReadStream(tempImportPaths[i]);
-
- var fileName = Path.GetFileName(tempImportPaths[i]);
-
- // subPath is always unix style
- var subPath = PathHelper.AddSlash(parentDirectory) + fileName;
- if ( parentDirectory == "/" ) subPath = parentDirectory + fileName;
-
- // to get the output in the result right
- fileIndexResultsList[i].FileIndexItem!.FileName = fileName;
- fileIndexResultsList[i].FileIndexItem!.ParentDirectory = parentDirectory;
- fileIndexResultsList[i].FilePath = subPath;
- // Do sync action before writing it down
- fileIndexResultsList[i].FileIndexItem =
- await SyncItem(fileIndexResultsList[i].FileIndexItem!);
-
- var writeStatus =
- await _iStorage.WriteStreamAsync(tempFileStream, subPath + ".tmp");
- // Is already flushed / disposed when the stream is written
-
- // to avoid partly written stream to be read by an other application
- _iStorage.FileDelete(subPath);
- _iStorage.FileMove(subPath + ".tmp", subPath);
- _logger.LogInformation($"[UploadController] write {subPath} is {writeStatus}");
-
- // clear directory cache
- _query.RemoveCacheParentItem(subPath);
-
- var deleteStatus = _iHostStorage.FileDelete(tempImportPaths[i]);
- _logger.LogInformation(
- $"[UploadController] delete {tempImportPaths[i]} is {deleteStatus}");
-
- var parentPath = Directory.GetParent(tempImportPaths[i])?.FullName;
- if ( !string.IsNullOrEmpty(parentPath) && parentPath != _appSettings.TempFolder )
- {
- _iHostStorage.FolderDelete(parentPath);
- }
-
- metaResultsList.Add(( await _metaExifThumbnailService.AddMetaThumbnail(subPath,
- fileIndexResultsList[i].FileIndexItem!.FileHash!) ));
+ subPath = parentDirectory + fileName;
}
- // send all uploads as list
- var socketResult = fileIndexResultsList
- .Where(p => p.Status == ImportStatus.Ok)
- .Select(item => item.FileIndexItem).Cast().ToList();
+ // to get the output in the result right
+ fileIndexResultsList[i].FileIndexItem!.FileName = fileName;
+ fileIndexResultsList[i].FileIndexItem!.ParentDirectory = parentDirectory;
+ fileIndexResultsList[i].FilePath = subPath;
+ // Do sync action before writing it down
+ fileIndexResultsList[i].FileIndexItem =
+ await SyncItem(fileIndexResultsList[i].FileIndexItem!);
+
+ var writeStatus =
+ await _iStorage.WriteStreamAsync(tempFileStream, subPath + ".tmp");
+ // Is already flushed / disposed when the stream is written
- var webSocketResponse = new ApiNotificationResponseModel>(
- socketResult, ApiNotificationType.UploadFile);
- await _realtimeService.NotificationToAllAsync(webSocketResponse,
- CancellationToken.None);
+ // to avoid partly written stream to be read by an other application
+ _iStorage.FileDelete(subPath);
+ _iStorage.FileMove(subPath + ".tmp", subPath);
+ _logger.LogInformation($"[UploadController] write {subPath} is {writeStatus}");
- await _metaUpdateStatusThumbnailService.UpdateStatusThumbnail(metaResultsList);
+ // clear directory cache
+ _query.RemoveCacheParentItem(subPath);
- // Wrong input (extension is not allowed)
- if ( fileIndexResultsList.TrueForAll(p => p.Status == ImportStatus.FileError) )
+ var deleteStatus = _iHostStorage.FileDelete(tempImportPaths[i]);
+ _logger.LogInformation(
+ $"[UploadController] delete {tempImportPaths[i]} is {deleteStatus}");
+
+ var parentPath = Directory.GetParent(tempImportPaths[i])?.FullName;
+ if ( !string.IsNullOrEmpty(parentPath) && parentPath != _appSettings.TempFolder )
{
- _logger.LogInformation($"Wrong input extension is not allowed" +
- $" {string.Join(",", fileIndexResultsList.Select(p => p.FilePath))}");
- Response.StatusCode = 415;
+ _iHostStorage.FolderDelete(parentPath);
}
- return Json(fileIndexResultsList);
+ metaResultsList.Add(await _metaExifThumbnailService.AddMetaThumbnail(subPath,
+ fileIndexResultsList[i].FileIndexItem!.FileHash!));
}
- ///
- /// Perform database updates
- ///
- /// to update to
- /// updated item
- private async Task SyncItem(FileIndexItem metaDataItem)
+ // send all uploads as list
+ var socketResult = fileIndexResultsList
+ .Where(p => p.Status == ImportStatus.Ok)
+ .Select(item => item.FileIndexItem).Cast().ToList();
+
+ var webSocketResponse = new ApiNotificationResponseModel>(
+ socketResult, ApiNotificationType.UploadFile);
+ await _realtimeService.NotificationToAllAsync(webSocketResponse,
+ CancellationToken.None);
+
+ await _metaUpdateStatusThumbnailService.UpdateStatusThumbnail(metaResultsList);
+
+ // Wrong input (extension is not allowed)
+ if ( fileIndexResultsList.TrueForAll(p => p.Status == ImportStatus.FileError) )
{
- var itemFromDatabase = await _query.GetObjectByFilePathAsync(metaDataItem.FilePath!);
- if ( itemFromDatabase == null )
- {
- AddOrRemoveXmpSidecarFileToDatabase(metaDataItem);
- await _query.AddItemAsync(metaDataItem);
- return metaDataItem;
- }
+ _logger.LogInformation($"Wrong input extension is not allowed" +
+ $" {string.Join(",", fileIndexResultsList.Select(p => p.FilePath))}");
+ Response.StatusCode = 415;
+ }
- FileIndexCompareHelper.Compare(itemFromDatabase, metaDataItem);
- AddOrRemoveXmpSidecarFileToDatabase(metaDataItem);
+ return Json(fileIndexResultsList);
+ }
- await _query.UpdateItemAsync(itemFromDatabase);
- return itemFromDatabase;
+ ///
+ /// Perform database updates
+ ///
+ /// to update to
+ /// updated item
+ private async Task SyncItem(FileIndexItem metaDataItem)
+ {
+ var itemFromDatabase = await _query.GetObjectByFilePathAsync(metaDataItem.FilePath!);
+ if ( itemFromDatabase == null )
+ {
+ AddOrRemoveXmpSidecarFileToDatabase(metaDataItem);
+ await _query.AddItemAsync(metaDataItem);
+ return metaDataItem;
}
- private void AddOrRemoveXmpSidecarFileToDatabase(FileIndexItem metaDataItem)
+ FileIndexCompareHelper.Compare(itemFromDatabase, metaDataItem);
+ AddOrRemoveXmpSidecarFileToDatabase(metaDataItem);
+
+ await _query.UpdateItemAsync(itemFromDatabase);
+ return itemFromDatabase;
+ }
+
+ private void AddOrRemoveXmpSidecarFileToDatabase(FileIndexItem metaDataItem)
+ {
+ if ( _iStorage.ExistFile(ExtensionRolesHelper.ReplaceExtensionWithXmp(metaDataItem
+ .FilePath)) )
{
- if ( _iStorage.ExistFile(ExtensionRolesHelper.ReplaceExtensionWithXmp(metaDataItem
- .FilePath)) )
- {
- metaDataItem.AddSidecarExtension("xmp");
- return;
- }
+ metaDataItem.AddSidecarExtension("xmp");
+ return;
+ }
+
+ metaDataItem.RemoveSidecarExtension("xmp");
+ }
- metaDataItem.RemoveSidecarExtension("xmp");
+ ///
+ /// Check if xml can be parsed
+ /// Used by sidecar upload
+ ///
+ /// string with xml
+ /// true when parsed
+ private bool IsValidXml(string xml)
+ {
+ try
+ {
+ // ReSharper disable once ReturnValueOfPureMethodIsNotUsed
+ XDocument.Parse(xml);
+ return true;
+ }
+ catch
+ {
+ _logger.LogInformation("[IsValidXml] non valid xml");
+ return false;
}
+ }
- ///
- /// Check if xml can be parsed
- /// Used by sidecar upload
- ///
- /// string with xml
- /// true when parsed
- private bool IsValidXml(string xml)
+ ///
+ /// Upload sidecar file to specific folder (does not check if already has been imported)
+ /// Use the header 'to' to determine the location to where to upload
+ /// Add header 'filename' when uploading direct without form
+ /// (ActionResult UploadToFolderSidecarFile)
+ ///
+ /// done
+ /// parent folder not found
+ /// Wrong input (e.g. wrong extenstion type)
+ /// missing 'to' header
+ /// the ImportIndexItem of the imported files
+ [HttpPost("/api/upload-sidecar")]
+ [DisableFormValueModelBinding]
+ [RequestFormLimits(MultipartBodyLengthLimit = 3_000_000)]
+ [RequestSizeLimit(3_000_000)] // in bytes, 3 MB
+ [ProducesResponseType(typeof(List), 200)] // yes
+ [ProducesResponseType(typeof(string), 400)]
+ [ProducesResponseType(typeof(List), 404)] // parent dir not found
+ [ProducesResponseType(typeof(List),
+ 415)] // Wrong input (e.g. wrong extenstion type)
+ [Produces("application/json")]
+ [SuppressMessage("Usage", "S6932: Use model binding instead of accessing the raw request data")]
+ public async Task UploadToFolderSidecarFile()
+ {
+ var to = Request.Headers["to"].ToString();
+ if ( string.IsNullOrWhiteSpace(to) )
{
- try
- {
- // ReSharper disable once ReturnValueOfPureMethodIsNotUsed
- XDocument.Parse(xml);
- return true;
- }
- catch
- {
- _logger.LogInformation("[IsValidXml] non valid xml");
- return false;
- }
+ return BadRequest("missing 'to' header");
}
- ///
- /// Upload sidecar file to specific folder (does not check if already has been imported)
- /// Use the header 'to' to determine the location to where to upload
- /// Add header 'filename' when uploading direct without form
- /// (ActionResult UploadToFolderSidecarFile)
- ///
- /// done
- /// parent folder not found
- /// Wrong input (e.g. wrong extenstion type)
- /// missing 'to' header
- /// the ImportIndexItem of the imported files
- [HttpPost("/api/upload-sidecar")]
- [DisableFormValueModelBinding]
- [RequestFormLimits(MultipartBodyLengthLimit = 3_000_000)]
- [RequestSizeLimit(3_000_000)] // in bytes, 3 MB
- [ProducesResponseType(typeof(List), 200)] // yes
- [ProducesResponseType(typeof(string), 400)]
- [ProducesResponseType(typeof(List), 404)] // parent dir not found
- [ProducesResponseType(typeof(List),
- 415)] // Wrong input (e.g. wrong extenstion type)
- [Produces("application/json")]
- public async Task UploadToFolderSidecarFile()
+ _logger.LogInformation($"[UploadToFolderSidecarFile] to:{to}");
+
+ var parentDirectory = GetParentDirectoryFromRequestHeader();
+ if ( parentDirectory == null )
{
- var to = Request.Headers["to"].ToString();
- if ( string.IsNullOrWhiteSpace(to) ) return BadRequest("missing 'to' header");
- _logger.LogInformation($"[UploadToFolderSidecarFile] to:{to}");
+ return NotFound(new ImportIndexItem());
+ }
+
+ var tempImportPaths = await Request.StreamFile(_appSettings, _selectorStorage);
- var parentDirectory = GetParentDirectoryFromRequestHeader();
- if ( parentDirectory == null )
+ var importedList = new List();
+ foreach ( var tempImportSinglePath in tempImportPaths )
+ {
+ var data = await StreamToStringHelper.StreamToStringAsync(
+ _iHostStorage.ReadStream(tempImportSinglePath));
+ if ( !IsValidXml(data) )
{
- return NotFound(new ImportIndexItem());
+ continue;
}
- var tempImportPaths = await Request.StreamFile(_appSettings, _selectorStorage);
+ var tempFileStream = _iHostStorage.ReadStream(tempImportSinglePath);
+ var fileName = Path.GetFileName(tempImportSinglePath);
- var importedList = new List();
- foreach ( var tempImportSinglePath in tempImportPaths )
+ var subPath = PathHelper.AddSlash(parentDirectory) + fileName;
+ if ( parentDirectory == "/" )
{
- var data = await StreamToStringHelper.StreamToStringAsync(
- _iHostStorage.ReadStream(tempImportSinglePath));
- if ( !IsValidXml(data) ) continue;
-
- var tempFileStream = _iHostStorage.ReadStream(tempImportSinglePath);
- var fileName = Path.GetFileName(tempImportSinglePath);
-
- var subPath = PathHelper.AddSlash(parentDirectory) + fileName;
- if ( parentDirectory == "/" ) subPath = parentDirectory + fileName;
-
- if ( _appSettings.UseDiskWatcher == false )
- {
- await new SyncSingleFile(_appSettings, _query,
- _iStorage, null!, _logger).UpdateSidecarFile(subPath);
- }
-
- await _iStorage.WriteStreamAsync(tempFileStream, subPath);
- await tempFileStream.DisposeAsync();
- importedList.Add(subPath);
-
- var deleteStatus = _iHostStorage.FileDelete(tempImportSinglePath);
- _logger.LogInformation($"delete {tempImportSinglePath} is {deleteStatus}");
+ subPath = parentDirectory + fileName;
}
- if ( importedList.Count == 0 )
+ if ( _appSettings.UseDiskWatcher == false )
{
- Response.StatusCode = 415;
+ await new SyncSingleFile(_appSettings, _query,
+ _iStorage, null!, _logger).UpdateSidecarFile(subPath);
}
- return Json(importedList);
+ await _iStorage.WriteStreamAsync(tempFileStream, subPath);
+ await tempFileStream.DisposeAsync();
+ importedList.Add(subPath);
+
+ var deleteStatus = _iHostStorage.FileDelete(tempImportSinglePath);
+ _logger.LogInformation($"delete {tempImportSinglePath} is {deleteStatus}");
}
- internal string? GetParentDirectoryFromRequestHeader()
+ if ( importedList.Count == 0 )
{
- var to = Request.Headers["to"].ToString();
- if ( to == "/" ) return "/";
+ Response.StatusCode = 415;
+ }
- // only used for direct import
- if ( _iStorage.ExistFolder(FilenamesHelper.GetParentPath(to)) &&
- FilenamesHelper.IsValidFileName(FilenamesHelper.GetFileName(to)) )
- {
- Request.Headers["filename"] = FilenamesHelper.GetFileName(to);
- return FilenamesHelper.GetParentPath(PathHelper.RemoveLatestSlash(to));
- }
+ return Json(importedList);
+ }
+
+ [SuppressMessage("Usage", "S6932: Use model binding instead of accessing the raw request data")]
+ internal string? GetParentDirectoryFromRequestHeader()
+ {
+ var to = Request.Headers["to"].ToString();
+ if ( to == "/" )
+ {
+ return "/";
+ }
+
+ // only used for direct import
+ if ( _iStorage.ExistFolder(FilenamesHelper.GetParentPath(to)) &&
+ FilenamesHelper.IsValidFileName(FilenamesHelper.GetFileName(to)) )
+ {
+ Request.Headers["filename"] = FilenamesHelper.GetFileName(to);
+ return FilenamesHelper.GetParentPath(PathHelper.RemoveLatestSlash(to));
+ }
- // ReSharper disable once ConvertIfStatementToReturnStatement
- if ( !_iStorage.ExistFolder(PathHelper.RemoveLatestSlash(to)) ) return null;
- return PathHelper.RemoveLatestSlash(to);
+ // ReSharper disable once ConvertIfStatementToReturnStatement
+ if ( !_iStorage.ExistFolder(PathHelper.RemoveLatestSlash(to)) )
+ {
+ return null;
}
+
+ return PathHelper.RemoveLatestSlash(to);
}
}
diff --git a/starsky/starskytest/Controllers/AllowedTypesControllerTest.cs b/starsky/starskytest/Controllers/AllowedTypesControllerTest.cs
index 20540ca6a1..5fd86c048e 100644
--- a/starsky/starskytest/Controllers/AllowedTypesControllerTest.cs
+++ b/starsky/starskytest/Controllers/AllowedTypesControllerTest.cs
@@ -4,43 +4,62 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using starsky.Controllers;
-namespace starskytest.Controllers
+namespace starskytest.Controllers;
+
+[TestClass]
+public sealed class AllowedTypesControllerTest
{
- [TestClass]
- public sealed class AllowedTypesControllerTest
+ private readonly HttpContext _httpContext = new DefaultHttpContext();
+
+ [TestMethod]
+ public void AllowedTypesController_MimetypeSync()
+ {
+ var jsonResult = new AllowedTypesController().AllowedTypesMimetypeSync() as JsonResult;
+ var allowedApiResult = jsonResult?.Value as HashSet;
+ Assert.IsTrue(allowedApiResult?.Contains("image/jpeg"));
+ }
+
+ [TestMethod]
+ public void AllowedTypesController_MimetypeSyncThumb()
{
- private readonly HttpContext _httpContext = new DefaultHttpContext();
-
- [TestMethod]
- public void AllowedTypesController_MimetypeSync()
- {
- var jsonResult = new AllowedTypesController().AllowedTypesMimetypeSync() as JsonResult;
- var allowedApiResult = jsonResult?.Value as HashSet;
- Assert.IsTrue(allowedApiResult?.Contains("image/jpeg"));
- }
-
- [TestMethod]
- public void AllowedTypesController_MimetypeSyncThumb()
- {
- var jsonResult = new AllowedTypesController().AllowedTypesMimetypeSyncThumb() as JsonResult;
- var allowedApiResult = jsonResult?.Value as HashSet;
- Assert.IsTrue(allowedApiResult?.Contains("image/jpeg"));
- }
-
- [TestMethod]
- public void AllowedTypesController_AllowedTypesThumb_NoInput()
- {
- var jsonResult = new AllowedTypesController{ ControllerContext = {HttpContext = _httpContext}}.AllowedTypesThumb("") as JsonResult;
- var allowedApiResult = bool.Parse(jsonResult?.Value?.ToString()!);
- Assert.IsFalse(allowedApiResult);
- }
-
- [TestMethod]
- public void AllowedTypesController_AllowedTypesThumb_Example()
- {
- var jsonResult = new AllowedTypesController{ ControllerContext = {HttpContext = _httpContext}}.AllowedTypesThumb("test.jpg") as JsonResult;
- var allowedApiResult = bool.Parse(jsonResult?.Value?.ToString()!);
- Assert.IsTrue(allowedApiResult);
- }
+ var jsonResult = new AllowedTypesController().AllowedTypesMimetypeSyncThumb() as JsonResult;
+ var allowedApiResult = jsonResult?.Value as HashSet;
+ Assert.IsTrue(allowedApiResult?.Contains("image/jpeg"));
+ }
+
+ [TestMethod]
+ public void AllowedTypesController_AllowedTypesThumb_NoInput()
+ {
+ var jsonResult =
+ new AllowedTypesController { ControllerContext = { HttpContext = _httpContext } }
+ .AllowedTypesThumb("") as JsonResult;
+ var allowedApiResult = bool.Parse(jsonResult?.Value?.ToString()!);
+ Assert.IsFalse(allowedApiResult);
+ }
+
+ [TestMethod]
+ public void AllowedTypesController_AllowedTypesThumb_Example()
+ {
+ var jsonResult =
+ new AllowedTypesController { ControllerContext = { HttpContext = _httpContext } }
+ .AllowedTypesThumb("test.jpg") as JsonResult;
+ var allowedApiResult = bool.Parse(jsonResult?.Value?.ToString()!);
+ Assert.IsTrue(allowedApiResult);
+ }
+
+ [TestMethod]
+ public void AllowedTypesController_AllowedTypesThumb_ReturnsBadRequest()
+ {
+ // Arrange
+ var controller =
+ new AllowedTypesController { ControllerContext = { HttpContext = _httpContext } };
+
+ controller.ModelState.AddModelError("Key", "ErrorMessage");
+
+ // Act
+ var result = controller.AllowedTypesThumb("test.jpg");
+
+ // Assert
+ Assert.IsInstanceOfType(result);
}
}
diff --git a/starsky/starskytest/Controllers/AppSettingsControllerTest.cs b/starsky/starskytest/Controllers/AppSettingsControllerTest.cs
index d16a7bc7a4..7f6b488097 100644
--- a/starsky/starskytest/Controllers/AppSettingsControllerTest.cs
+++ b/starsky/starskytest/Controllers/AppSettingsControllerTest.cs
@@ -13,173 +13,189 @@
using starsky.foundation.storage.Helpers;
using starskytest.FakeMocks;
-namespace starskytest.Controllers
+namespace starskytest.Controllers;
+
+[TestClass]
+public sealed class AppSettingsControllerTest
{
- [TestClass]
- public sealed class AppSettingsControllerTest
+ [TestMethod]
+ public void ENV_StarskyTestEnv()
{
- [TestMethod]
- public void ENV_StarskyTestEnv()
- {
- var controller =
- new AppSettingsController(new AppSettings(), new FakeIUpdateAppSettingsByPath());
- var actionResult = controller.Env() as JsonResult;
- var resultAppSettings = actionResult?.Value as AppSettings;
- Assert.AreEqual("Starsky", resultAppSettings?.Name);
- }
-
- [TestMethod]
- public void ENV_StarskyTestEnv_ForceHtml()
- {
- var controller = new AppSettingsController(new AppSettings(),
- new FakeIUpdateAppSettingsByPath());
- controller.ControllerContext.HttpContext = new DefaultHttpContext();
- controller.ControllerContext.HttpContext.Request.Headers.Append("x-force-html", "true");
- var actionResult = controller.Env() as JsonResult;
- var resultAppSettings = actionResult?.Value as AppSettings;
- Assert.AreEqual("Starsky", resultAppSettings?.Name);
- Assert.AreEqual("text/html; charset=utf-8",
- controller.ControllerContext.HttpContext.Response.Headers.ContentType.ToString());
- }
-
- [TestMethod]
- public async Task UpdateAppSettings_Verbose()
- {
- var appSettings = new AppSettings();
- var storage = new FakeIStorage(new List { "/" });
- var controller = new AppSettingsController(appSettings,
- new UpdateAppSettingsByPath(appSettings, new FakeSelectorStorage(storage)));
-
- var actionResult =
- await controller.UpdateAppSettings(new AppSettingsTransferObject { Verbose = true })
- as JsonResult;
- var result = actionResult?.Value as AppSettings;
- Assert.IsTrue(result?.Verbose);
- }
-
- [TestMethod]
- public async Task UpdateAppSettings_StorageFolder()
- {
- var appSettings = new AppSettings();
- var controller = new AppSettingsController(appSettings, new UpdateAppSettingsByPath(
- appSettings,
- new FakeSelectorStorage(
- new FakeIStorage(new List { $"{Path.DirectorySeparatorChar}test" }))));
-
- controller.ControllerContext.HttpContext = new DefaultHttpContext();
-
- var actionResult = await controller.UpdateAppSettings(new AppSettingsTransferObject
- {
- Verbose = true, StorageFolder = $"{Path.DirectorySeparatorChar}test"
- }) as JsonResult;
-
- var result = actionResult?.Value as AppSettings;
- Assert.IsTrue(result?.Verbose);
- Assert.AreEqual(Path.DirectorySeparatorChar + PathHelper.AddBackslash("test"),
- result?.StorageFolder);
- }
-
- [TestMethod]
- public async Task UpdateAppSettingsTest_IgnoreWhenEnvIsSet()
+ var controller =
+ new AppSettingsController(new AppSettings(), new FakeIUpdateAppSettingsByPath());
+ var actionResult = controller.Env() as JsonResult;
+ var resultAppSettings = actionResult?.Value as AppSettings;
+ Assert.AreEqual("Starsky", resultAppSettings?.Name);
+ }
+
+ [TestMethod]
+ public void ENV_StarskyTestEnv_ForceHtml()
+ {
+ var controller = new AppSettingsController(new AppSettings(),
+ new FakeIUpdateAppSettingsByPath());
+ controller.ControllerContext.HttpContext = new DefaultHttpContext();
+ controller.ControllerContext.HttpContext.Request.Headers.Append("x-force-html", "true");
+ var actionResult = controller.Env() as JsonResult;
+ var resultAppSettings = actionResult?.Value as AppSettings;
+ Assert.AreEqual("Starsky", resultAppSettings?.Name);
+ Assert.AreEqual("text/html; charset=utf-8",
+ controller.ControllerContext.HttpContext.Response.Headers.ContentType.ToString());
+ }
+
+ [TestMethod]
+ public async Task UpdateAppSettings_Verbose()
+ {
+ var appSettings = new AppSettings();
+ var storage = new FakeIStorage(new List { "/" });
+ var controller = new AppSettingsController(appSettings,
+ new UpdateAppSettingsByPath(appSettings, new FakeSelectorStorage(storage)));
+
+ var actionResult =
+ await controller.UpdateAppSettings(new AppSettingsTransferObject { Verbose = true })
+ as JsonResult;
+ var result = actionResult?.Value as AppSettings;
+ Assert.IsTrue(result?.Verbose);
+ }
+
+ [TestMethod]
+ public async Task UpdateAppSettings_StorageFolder()
+ {
+ var appSettings = new AppSettings();
+ var controller = new AppSettingsController(appSettings, new UpdateAppSettingsByPath(
+ appSettings,
+ new FakeSelectorStorage(
+ new FakeIStorage(new List { $"{Path.DirectorySeparatorChar}test" }))));
+
+ controller.ControllerContext.HttpContext = new DefaultHttpContext();
+
+ var actionResult = await controller.UpdateAppSettings(new AppSettingsTransferObject
{
- Environment.SetEnvironmentVariable("app__storageFolder",
- "any_value");
-
- var appSettings = new AppSettings();
- var controller = new AppSettingsController(appSettings,
- new FakeIUpdateAppSettingsByPath(
- new UpdateAppSettingsStatusModel { StatusCode = 403 }));
- controller.ControllerContext.HttpContext = new DefaultHttpContext();
- await controller.UpdateAppSettings(
- new AppSettingsTransferObject { StorageFolder = "test" });
+ Verbose = true, StorageFolder = $"{Path.DirectorySeparatorChar}test"
+ }) as JsonResult;
+
+ var result = actionResult?.Value as AppSettings;
+ Assert.IsTrue(result?.Verbose);
+ Assert.AreEqual(Path.DirectorySeparatorChar + PathHelper.AddBackslash("test"),
+ result?.StorageFolder);
+ }
+
+ [TestMethod]
+ public async Task UpdateAppSettingsTest_IgnoreWhenEnvIsSet()
+ {
+ Environment.SetEnvironmentVariable("app__storageFolder",
+ "any_value");
+
+ var appSettings = new AppSettings();
+ var controller = new AppSettingsController(appSettings,
+ new FakeIUpdateAppSettingsByPath(
+ new UpdateAppSettingsStatusModel { StatusCode = 403 }));
+ controller.ControllerContext.HttpContext = new DefaultHttpContext();
+ await controller.UpdateAppSettings(
+ new AppSettingsTransferObject { StorageFolder = "test" });
+
+ Assert.AreEqual(403, controller.Response.StatusCode);
+ }
+
+ [TestMethod]
+ public async Task UpdateAppSettingsTest_DirNotFound()
+ {
+ var appSettings = new AppSettings();
+ var controller = new AppSettingsController(appSettings,
+ new FakeIUpdateAppSettingsByPath(
+ new UpdateAppSettingsStatusModel { StatusCode = 404 }));
+ controller.ControllerContext.HttpContext = new DefaultHttpContext();
- Assert.AreEqual(403, controller.Response.StatusCode);
- }
+ await controller.UpdateAppSettings(
+ new AppSettingsTransferObject { StorageFolder = "not_found" });
- [TestMethod]
- public async Task UpdateAppSettingsTest_DirNotFound()
+ Assert.AreEqual(404, controller.Response.StatusCode);
+ }
+
+ [TestMethod]
+ public async Task UpdateAppSettingsTest_StorageFolder_JsonCheck()
+ {
+ var storage = new FakeIStorage(new List { "test" });
+ Environment.SetEnvironmentVariable("app__storageFolder", string.Empty);
+
+ var appSettings = new AppSettings
{
- var appSettings = new AppSettings();
- var controller = new AppSettingsController(appSettings,
- new FakeIUpdateAppSettingsByPath(
- new UpdateAppSettingsStatusModel { StatusCode = 404 }));
- controller.ControllerContext.HttpContext = new DefaultHttpContext();
+ AppSettingsPath =
+ $"{Path.DirectorySeparatorChar}temp{Path.DirectorySeparatorChar}appsettings.json"
+ };
+ var controller = new AppSettingsController(appSettings,
+ new UpdateAppSettingsByPath(appSettings, new FakeSelectorStorage(storage)));
+ await controller.UpdateAppSettings(
+ new AppSettingsTransferObject { Verbose = true, StorageFolder = "test" });
+
+ Assert.IsTrue(storage.ExistFile(appSettings.AppSettingsPath));
+
+ var jsonContent = await StreamToStringHelper.StreamToStringAsync(
+ storage.ReadStream(appSettings.AppSettingsPath));
+ Assert.IsTrue(jsonContent.Contains("app\": {"));
+ Assert.IsTrue(jsonContent.Contains("\"StorageFolder\": \""));
+ }
+
+ [TestMethod]
+ public async Task UpdateAppSettings_UseLocalDesktop()
+ {
+ var appSettings = new AppSettings();
+ var storage = new FakeIStorage(new List { "/" });
+ var controller = new AppSettingsController(appSettings,
+ new UpdateAppSettingsByPath(appSettings, new FakeSelectorStorage(storage)));
+
+ var actionResult =
await controller.UpdateAppSettings(
- new AppSettingsTransferObject { StorageFolder = "not_found" });
+ new AppSettingsTransferObject { UseLocalDesktop = true }) as JsonResult;
+ var result = actionResult?.Value as AppSettings;
+ Assert.IsTrue(result?.UseLocalDesktop);
+ }
- Assert.AreEqual(404, controller.Response.StatusCode);
- }
+ [TestMethod]
+ public async Task UpdateAppSettings_UseSystemTrash()
+ {
+ var appSettings = new AppSettings();
+ var storage = new FakeIStorage(new List { "/" });
+ var controller = new AppSettingsController(appSettings,
+ new UpdateAppSettingsByPath(appSettings, new FakeSelectorStorage(storage)));
- [TestMethod]
- public async Task UpdateAppSettingsTest_StorageFolder_JsonCheck()
- {
- var storage = new FakeIStorage(new List { "test" });
- Environment.SetEnvironmentVariable("app__storageFolder", string.Empty);
-
- var appSettings = new AppSettings
- {
- AppSettingsPath =
- $"{Path.DirectorySeparatorChar}temp{Path.DirectorySeparatorChar}appsettings.json"
- };
- var controller = new AppSettingsController(appSettings,
- new UpdateAppSettingsByPath(appSettings, new FakeSelectorStorage(storage)));
+ var actionResult =
await controller.UpdateAppSettings(
- new AppSettingsTransferObject { Verbose = true, StorageFolder = "test" });
+ new AppSettingsTransferObject { UseSystemTrash = true }) as JsonResult;
+ var result = actionResult?.Value as AppSettings;
+ Assert.IsTrue(result?.UseSystemTrash);
+ }
- Assert.IsTrue(storage.ExistFile(appSettings.AppSettingsPath));
+ [TestMethod]
+ public async Task UpdateAppSettings_Verbose_IgnoreSystemTrashValue()
+ {
+ var appSettings = new AppSettings();
+ var storage = new FakeIStorage(new List { "/" });
+ var controller = new AppSettingsController(appSettings,
+ new UpdateAppSettingsByPath(appSettings, new FakeSelectorStorage(storage)));
- var jsonContent = await StreamToStringHelper.StreamToStringAsync(
- storage.ReadStream(appSettings.AppSettingsPath));
+ var actionResult =
+ await controller.UpdateAppSettings(new AppSettingsTransferObject { Verbose = true })
+ as JsonResult;
+ var result = actionResult?.Value as AppSettings;
- Assert.IsTrue(jsonContent.Contains("app\": {"));
- Assert.IsTrue(jsonContent.Contains("\"StorageFolder\": \""));
- }
+ Assert.AreEqual(appSettings.UseSystemTrash, result?.UseSystemTrash);
+ }
- [TestMethod]
- public async Task UpdateAppSettings_UseLocalDesktop()
- {
- var appSettings = new AppSettings();
- var storage = new FakeIStorage(new List { "/" });
- var controller = new AppSettingsController(appSettings,
- new UpdateAppSettingsByPath(appSettings, new FakeSelectorStorage(storage)));
-
- var actionResult =
- await controller.UpdateAppSettings(
- new AppSettingsTransferObject { UseLocalDesktop = true }) as JsonResult;
- var result = actionResult?.Value as AppSettings;
- Assert.IsTrue(result?.UseLocalDesktop);
- }
-
- [TestMethod]
- public async Task UpdateAppSettings_UseSystemTrash()
- {
- var appSettings = new AppSettings();
- var storage = new FakeIStorage(new List { "/" });
- var controller = new AppSettingsController(appSettings,
- new UpdateAppSettingsByPath(appSettings, new FakeSelectorStorage(storage)));
-
- var actionResult =
- await controller.UpdateAppSettings(
- new AppSettingsTransferObject { UseSystemTrash = true }) as JsonResult;
- var result = actionResult?.Value as AppSettings;
- Assert.IsTrue(result?.UseSystemTrash);
- }
-
- [TestMethod]
- public async Task UpdateAppSettings_Verbose_IgnoreSystemTrashValue()
- {
- var appSettings = new AppSettings();
- var storage = new FakeIStorage(new List { "/" });
- var controller = new AppSettingsController(appSettings,
- new UpdateAppSettingsByPath(appSettings, new FakeSelectorStorage(storage)));
-
- var actionResult =
- await controller.UpdateAppSettings(new AppSettingsTransferObject { Verbose = true })
- as JsonResult;
- var result = actionResult?.Value as AppSettings;
-
- Assert.AreEqual(appSettings.UseSystemTrash, result?.UseSystemTrash);
- }
+ [TestMethod]
+ public async Task UpdateAppSettings_AllowedTypesThumb_ReturnsBadRequest()
+ {
+ // Arrange
+ var appSettings = new AppSettings();
+ var controller = new AppSettingsController(appSettings,
+ new UpdateAppSettingsByPath(appSettings, new FakeSelectorStorage(new FakeIStorage())));
+
+ controller.ModelState.AddModelError("Key", "ErrorMessage");
+
+ // Act
+ var result = await controller.UpdateAppSettings(null!);
+
+ // Assert
+ Assert.IsInstanceOfType(result);
}
}
diff --git a/starsky/starskytest/Controllers/CacheIndexControllerTest.cs b/starsky/starskytest/Controllers/CacheIndexControllerTest.cs
index 841639ca04..b303ebcf1d 100644
--- a/starsky/starskytest/Controllers/CacheIndexControllerTest.cs
+++ b/starsky/starskytest/Controllers/CacheIndexControllerTest.cs
@@ -21,197 +21,225 @@
using starskytest.FakeCreateAn;
using starskytest.FakeMocks;
+namespace starskytest.Controllers;
-namespace starskytest.Controllers
+[TestClass]
+public sealed class CacheIndexControllerTest
{
- [TestClass]
- public sealed class CacheIndexControllerTest
+ private readonly AppSettings _appSettings;
+ private readonly ApplicationDbContext _context;
+ private readonly Query _query;
+
+ public CacheIndexControllerTest()
+ {
+ var provider = new ServiceCollection()
+ .AddMemoryCache()
+ .BuildServiceProvider();
+ var memoryCache = provider.GetService();
+
+ var builderDb = new DbContextOptionsBuilder();
+ builderDb.UseInMemoryDatabase("test1234");
+ var options = builderDb.Options;
+ _context = new ApplicationDbContext(options);
+ _query = new Query(_context, new AppSettings(), null!, new FakeIWebLogger(),
+ memoryCache);
+
+ // Inject Fake ExifTool; dependency injection
+ var services = new ServiceCollection();
+
+ // Fake the readMeta output
+ services.AddSingleton();
+
+ // Inject Config helper
+ services.AddSingleton(new ConfigurationBuilder().Build());
+ // random config
+ var createAnImage = new CreateAnImage();
+ var dict = new Dictionary
+ {
+ { "App:StorageFolder", createAnImage.BasePath },
+ { "App:ThumbnailTempFolder", createAnImage.BasePath },
+ { "App:Verbose", "true" }
+ };
+ // Start using dependency injection
+ var builder = new ConfigurationBuilder();
+ // Add random config to dependency injection
+ builder.AddInMemoryCollection(dict);
+ // build config
+ var configuration = builder.Build();
+ // inject config as object to a service
+ services.ConfigurePoCo(configuration.GetSection("App"));
+
+ // Add Background services
+ services.AddSingleton();
+ services.AddSingleton();
+
+ // build the service
+ var serviceProvider = services.BuildServiceProvider();
+ // get the service
+ _appSettings = serviceProvider.GetRequiredService();
+ }
+
+ [TestMethod]
+ public async Task CacheIndexController_CheckIfCacheIsRemoved_CleanCache()
{
- private readonly Query _query;
- private readonly AppSettings _appSettings;
- private readonly ApplicationDbContext _context;
+ // Act
+ var controller = new CacheIndexController(_query, _appSettings);
+ controller.ControllerContext.HttpContext = new DefaultHttpContext();
- public CacheIndexControllerTest()
+ await _query.AddItemAsync(new FileIndexItem
{
- var provider = new ServiceCollection()
- .AddMemoryCache()
- .BuildServiceProvider();
- var memoryCache = provider.GetService();
-
- var builderDb = new DbContextOptionsBuilder();
- builderDb.UseInMemoryDatabase("test1234");
- var options = builderDb.Options;
- _context = new ApplicationDbContext(options);
- _query = new Query(_context, new AppSettings(), null!, new FakeIWebLogger(),
- memoryCache);
-
- // Inject Fake ExifTool; dependency injection
- var services = new ServiceCollection();
-
- // Fake the readMeta output
- services.AddSingleton();
-
- // Inject Config helper
- services.AddSingleton(new ConfigurationBuilder().Build());
- // random config
- var createAnImage = new CreateAnImage();
- var dict = new Dictionary
- {
- { "App:StorageFolder", createAnImage.BasePath },
- { "App:ThumbnailTempFolder", createAnImage.BasePath },
- { "App:Verbose", "true" }
- };
- // Start using dependency injection
- var builder = new ConfigurationBuilder();
- // Add random config to dependency injection
- builder.AddInMemoryCollection(dict);
- // build config
- var configuration = builder.Build();
- // inject config as object to a service
- services.ConfigurePoCo(configuration.GetSection("App"));
-
- // Add Background services
- services.AddSingleton();
- services.AddSingleton();
-
- // build the service
- var serviceProvider = services.BuildServiceProvider();
- // get the service
- _appSettings = serviceProvider.GetRequiredService();
- }
-
- [TestMethod]
- public async Task CacheIndexController_CheckIfCacheIsRemoved_CleanCache()
+ FileName = "cacheDeleteTest", ParentDirectory = "/", IsDirectory = true
+ });
+
+ await _query.AddItemAsync(new FileIndexItem
{
- // Act
- var controller = new CacheIndexController(_query, _appSettings);
- controller.ControllerContext.HttpContext = new DefaultHttpContext();
+ FileName = "file.jpg", ParentDirectory = "/cacheDeleteTest", IsDirectory = false
+ });
- await _query.AddItemAsync(new FileIndexItem
- {
- FileName = "cacheDeleteTest", ParentDirectory = "/", IsDirectory = true
- });
+ Assert.IsTrue(_query.DisplayFileFolders("/cacheDeleteTest").Any());
- await _query.AddItemAsync(new FileIndexItem
- {
- FileName = "file.jpg", ParentDirectory = "/cacheDeleteTest", IsDirectory = false
- });
+ // Ask the cache
+ _query.DisplayFileFolders("/cacheDeleteTest");
- Assert.IsTrue(_query.DisplayFileFolders("/cacheDeleteTest").Any());
+ // Don't notify the cache that there is an update
+ var newItem = new FileIndexItem
+ {
+ FileName = "file2.jpg", ParentDirectory = "/cacheDeleteTest", IsDirectory = false
+ };
+ _context.FileIndex.Add(newItem);
+ _context.SaveChanges();
+ // Write changes to database
+
+ // Check if there is one item in the cache
+ var beforeQuery = _query.DisplayFileFolders("/cacheDeleteTest");
+ Assert.AreEqual(1, beforeQuery.Count());
+
+ // Act, remove content from cache
+ var actionResult = controller.RemoveCache("/cacheDeleteTest") as JsonResult;
+ Assert.AreEqual("cache successful cleared", actionResult?.Value);
+
+ // Check if there are now two items in the cache
+ var newQuery = _query.DisplayFileFolders("/cacheDeleteTest");
+ Assert.AreEqual(2, newQuery.Count());
+ }
- // Ask the cache
- _query.DisplayFileFolders("/cacheDeleteTest");
+ [TestMethod]
+ public async Task RemoveCache_CacheDidNotExist()
+ {
+ // Act
+ var controller = new CacheIndexController(_query, _appSettings);
+ controller.ControllerContext.HttpContext = new DefaultHttpContext();
- // Don't notify the cache that there is an update
- var newItem = new FileIndexItem
- {
- FileName = "file2.jpg",
- ParentDirectory = "/cacheDeleteTest",
- IsDirectory = false
- };
- _context.FileIndex.Add(newItem);
- _context.SaveChanges();
- // Write changes to database
-
- // Check if there is one item in the cache
- var beforeQuery = _query.DisplayFileFolders("/cacheDeleteTest");
- Assert.AreEqual(1, beforeQuery.Count());
-
- // Act, remove content from cache
- var actionResult = controller.RemoveCache("/cacheDeleteTest") as JsonResult;
- Assert.AreEqual("cache successful cleared", actionResult?.Value);
-
- // Check if there are now two items in the cache
- var newQuery = _query.DisplayFileFolders("/cacheDeleteTest");
- Assert.AreEqual(2, newQuery.Count());
- }
-
- [TestMethod]
- public async Task RemoveCache_CacheDidNotExist()
+ await _query.AddItemAsync(new FileIndexItem
{
- // Act
- var controller = new CacheIndexController(_query, _appSettings);
- controller.ControllerContext.HttpContext = new DefaultHttpContext();
+ FileName = "cacheDeleteTest2", ParentDirectory = "/", IsDirectory = true
+ });
- await _query.AddItemAsync(new FileIndexItem
- {
- FileName = "cacheDeleteTest2", ParentDirectory = "/", IsDirectory = true
- });
+ // Act, remove content from cache
+ var actionResult = controller.RemoveCache("/cacheDeleteTest2") as JsonResult;
+ Assert.AreEqual("cache did not exist", actionResult?.Value);
+ }
- // Act, remove content from cache
- var actionResult = controller.RemoveCache("/cacheDeleteTest2") as JsonResult;
- Assert.AreEqual("cache did not exist", actionResult?.Value);
- }
+ [TestMethod]
+ public void RemoveCache_ReturnsBadRequest()
+ {
+ // Arrange
+ var controller = new CacheIndexController(_query, _appSettings);
+ controller.ControllerContext.HttpContext = new DefaultHttpContext();
- [TestMethod]
- public void CacheIndexController_NonExistingCacheRemove()
- {
- // Act
- var controller = new CacheIndexController(_query, _appSettings);
- controller.ControllerContext.HttpContext = new DefaultHttpContext();
+ controller.ModelState.AddModelError("Key", "ErrorMessage");
- var actionResult = controller.RemoveCache("/404page") as BadRequestObjectResult;
- Assert.AreEqual(400, actionResult?.StatusCode);
- }
+ // Act
+ var result = controller.RemoveCache(null!);
- [TestMethod]
- public void CacheIndexController_CacheDisabled()
- {
- var controller =
- new CacheIndexController(_query, new AppSettings { AddMemoryCache = false });
- controller.ControllerContext.HttpContext = new DefaultHttpContext();
+ // Assert
+ Assert.IsInstanceOfType(result);
+ }
- var actionResult = controller.RemoveCache("/404page") as JsonResult;
- Assert.AreEqual("cache disabled in config", actionResult?.Value);
- }
+ [TestMethod]
+ public void CacheIndexController_NonExistingCacheRemove()
+ {
+ // Act
+ var controller = new CacheIndexController(_query, _appSettings);
+ controller.ControllerContext.HttpContext = new DefaultHttpContext();
- [TestMethod]
- public void ListCache_CacheDidNotExist()
- {
- // Act
- var controller = new CacheIndexController(_query, _appSettings);
- controller.ControllerContext.HttpContext = new DefaultHttpContext();
-
- // Act, remove content from cache
- var actionResult = controller.ListCache("/cacheDeleteTest2") as BadRequestObjectResult;
- Assert.AreEqual("ignored, please check if the 'f' path " +
- "exist or use a folder string to get the cache", actionResult?.Value);
- }
-
- [TestMethod]
- public void ListCache_CacheDisabled()
- {
- var controller =
- new CacheIndexController(_query, new AppSettings { AddMemoryCache = false });
- controller.ControllerContext.HttpContext = new DefaultHttpContext();
+ var actionResult = controller.RemoveCache("/404page") as BadRequestObjectResult;
+ Assert.AreEqual(400, actionResult?.StatusCode);
+ }
+
+ [TestMethod]
+ public void CacheIndexController_CacheDisabled()
+ {
+ var controller =
+ new CacheIndexController(_query, new AppSettings { AddMemoryCache = false });
+ controller.ControllerContext.HttpContext = new DefaultHttpContext();
- var actionResult = controller.ListCache("/404page") as JsonResult;
- Assert.AreEqual("cache disabled in config", actionResult?.Value);
- }
+ var actionResult = controller.RemoveCache("/404page") as JsonResult;
+ Assert.AreEqual("cache disabled in config", actionResult?.Value);
+ }
- [TestMethod]
- public void ListCache_GetCache()
- {
- // Act
- var controller = new CacheIndexController(_query, _appSettings);
- controller.ControllerContext.HttpContext = new DefaultHttpContext();
+ [TestMethod]
+ public void ListCache_ReturnsBadRequest()
+ {
+ // Arrange
+ var controller = new CacheIndexController(_query, _appSettings);
+ controller.ControllerContext.HttpContext = new DefaultHttpContext();
+
+ controller.ModelState.AddModelError("Key", "ErrorMessage");
+
+ // Act
+ var result = controller.ListCache(null!);
+
+ // Assert
+ Assert.IsInstanceOfType(result);
+ }
+
+ [TestMethod]
+ public void ListCache_CacheDidNotExist()
+ {
+ // Act
+ var controller = new CacheIndexController(_query, _appSettings);
+ controller.ControllerContext.HttpContext = new DefaultHttpContext();
+
+ // Act, remove content from cache
+ var actionResult = controller.ListCache("/cacheDeleteTest2") as BadRequestObjectResult;
+ Assert.AreEqual("ignored, please check if the 'f' path " +
+ "exist or use a folder string to get the cache", actionResult?.Value);
+ }
+
+ [TestMethod]
+ public void ListCache_CacheDisabled()
+ {
+ var controller =
+ new CacheIndexController(_query, new AppSettings { AddMemoryCache = false });
+ controller.ControllerContext.HttpContext = new DefaultHttpContext();
+
+ var actionResult = controller.ListCache("/404page") as JsonResult;
+ Assert.AreEqual("cache disabled in config", actionResult?.Value);
+ }
- _query.AddCacheParentItem("/list-cache",
- new List
+ [TestMethod]
+ public void ListCache_GetCache()
+ {
+ // Act
+ var controller = new CacheIndexController(_query, _appSettings);
+ controller.ControllerContext.HttpContext = new DefaultHttpContext();
+
+ _query.AddCacheParentItem("/list-cache",
+ new List
+ {
+ new()
{
- new FileIndexItem
- {
- FileName = "cacheDeleteTest2",
- ParentDirectory = "/list-cache",
- IsDirectory = true
- }
- });
-
- // Act, remove content from cache
- var actionResult = controller.ListCache("/list-cache") as JsonResult;
-
- Assert.IsNotNull(actionResult);
- Assert.IsNotNull(actionResult.Value);
- }
+ FileName = "cacheDeleteTest2",
+ ParentDirectory = "/list-cache",
+ IsDirectory = true
+ }
+ });
+
+ // Act, remove content from cache
+ var actionResult = controller.ListCache("/list-cache") as JsonResult;
+
+ Assert.IsNotNull(actionResult);
+ Assert.IsNotNull(actionResult.Value);
}
}
diff --git a/starsky/starskytest/Controllers/DeleteControllerTest.cs b/starsky/starskytest/Controllers/DeleteControllerTest.cs
index e855bfd09a..8627599d4f 100644
--- a/starsky/starskytest/Controllers/DeleteControllerTest.cs
+++ b/starsky/starskytest/Controllers/DeleteControllerTest.cs
@@ -26,169 +26,195 @@
using starskytest.FakeCreateAn;
using starskytest.FakeMocks;
-namespace starskytest.Controllers
+namespace starskytest.Controllers;
+
+[TestClass]
+public sealed class DeleteControllerTest
{
- [TestClass]
- public sealed class DeleteControllerTest
+ private readonly AppSettings _appSettings;
+ private readonly CreateAnImage _createAnImage;
+ private readonly IStorage _iStorage;
+
+ private readonly Query _query;
+
+ public DeleteControllerTest()
{
-
- private readonly Query _query;
- private readonly AppSettings _appSettings;
- private readonly CreateAnImage _createAnImage;
- private readonly IStorage _iStorage;
+ var provider = new ServiceCollection()
+ .AddMemoryCache()
+ .BuildServiceProvider();
+ var memoryCache = provider.GetService();
+
+ var builderDb = new DbContextOptionsBuilder();
+ builderDb.UseInMemoryDatabase("test1234");
+ var options = builderDb.Options;
+ var context = new ApplicationDbContext(options);
+ _query = new Query(context,
+ new AppSettings(), null, new FakeIWebLogger(), memoryCache);
- public DeleteControllerTest()
+ // Inject Fake ExifTool; dependency injection
+ var services = new ServiceCollection();
+
+ // Fake the readMeta output
+ services.AddSingleton();
+
+ // Inject Config helper
+ services.AddSingleton(new ConfigurationBuilder().Build());
+ // random config
+ _createAnImage = new CreateAnImage();
+ var dict = new Dictionary
{
- var provider = new ServiceCollection()
- .AddMemoryCache()
- .BuildServiceProvider();
- var memoryCache = provider.GetService();
-
- var builderDb = new DbContextOptionsBuilder();
- builderDb.UseInMemoryDatabase("test1234");
- var options = builderDb.Options;
- var context = new ApplicationDbContext(options);
- _query = new Query(context,
- new AppSettings(), null, new FakeIWebLogger(),memoryCache);
-
- // Inject Fake ExifTool; dependency injection
- var services = new ServiceCollection();
-
- // Fake the readMeta output
- services.AddSingleton();
-
- // Inject Config helper
- services.AddSingleton(new ConfigurationBuilder().Build());
- // random config
- _createAnImage = new CreateAnImage();
- var dict = new Dictionary
- {
- { "App:StorageFolder", _createAnImage.BasePath },
- { "App:ThumbnailTempFolder",_createAnImage.BasePath },
- { "App:Verbose", "true" }
- };
- // Start using dependency injection
- var builder = new ConfigurationBuilder();
- // Add random config to dependency injection
- builder.AddInMemoryCollection(dict);
- // build config
- var configuration = builder.Build();
- // inject config as object to a service
- services.ConfigurePoCo(configuration.GetSection("App"));
-
- // Add Background services
- services.AddSingleton();
- services.AddSingleton();
-
- // build the service
- var serviceProvider = services.BuildServiceProvider();
- // get the service
- _appSettings = serviceProvider.GetRequiredService();
-
- _iStorage = new StorageSubPathFilesystem(_appSettings, new FakeIWebLogger());
- }
-
- private async Task InsertSearchData(bool delete = false)
+ { "App:StorageFolder", _createAnImage.BasePath },
+ { "App:ThumbnailTempFolder", _createAnImage.BasePath },
+ { "App:Verbose", "true" }
+ };
+ // Start using dependency injection
+ var builder = new ConfigurationBuilder();
+ // Add random config to dependency injection
+ builder.AddInMemoryCollection(dict);
+ // build config
+ var configuration = builder.Build();
+ // inject config as object to a service
+ services.ConfigurePoCo(configuration.GetSection("App"));
+
+ // Add Background services
+ services.AddSingleton();
+ services.AddSingleton();
+
+ // build the service
+ var serviceProvider = services.BuildServiceProvider();
+ // get the service
+ _appSettings = serviceProvider.GetRequiredService();
+
+ _iStorage = new StorageSubPathFilesystem(_appSettings, new FakeIWebLogger());
+ }
+
+ private async Task InsertSearchData(bool delete = false)
+ {
+ var fileHashCode = new FileHash(_iStorage).GetHashCode(_createAnImage.DbPath).Key;
+
+ if ( string.IsNullOrEmpty(await _query.GetSubPathByHashAsync(fileHashCode)) )
{
- var fileHashCode = new FileHash(_iStorage).GetHashCode(_createAnImage.DbPath).Key;
-
- if (string.IsNullOrEmpty(await _query.GetSubPathByHashAsync(fileHashCode)))
+ var isDelete = string.Empty;
+ if ( delete )
{
- var isDelete = string.Empty;
- if (delete) isDelete = TrashKeyword.TrashKeywordString;
- await _query.AddItemAsync(new FileIndexItem
- {
- FileName = _createAnImage.FileName,
- ParentDirectory = "/",
- FileHash = fileHashCode,
- ColorClass = ColorClassParser.Color.Winner, // 1
- Tags = isDelete
- });
+ isDelete = TrashKeyword.TrashKeywordString;
}
- return _query.GetObjectByFilePath(_createAnImage.DbPath);
- }
-
- [TestMethod]
- public async Task ApiController_Delete_API_HappyFlow_Test()
- {
- var createAnImage = await InsertSearchData(true);
- _appSettings.DatabaseType = AppSettings.DatabaseTypeList.InMemoryDatabase;
-
- // RealFs Storage
- var selectorStorage = new FakeSelectorStorage(new StorageSubPathFilesystem(_appSettings, new FakeIWebLogger()));
-
- var deleteItem = new DeleteItem(_query,_appSettings,selectorStorage);
- var controller = new DeleteController(deleteItem);
-
- Console.WriteLine("createAnImage.FilePath");
- Console.WriteLine("@#~ "+ createAnImage?.FilePath);
-
- // create an image
- var createAnImage1 = new CreateAnImage();
- Assert.IsNotNull(createAnImage1);
-
- var actionResult = await controller.Delete(createAnImage?.FilePath!) as JsonResult;
- Assert.AreNotEqual(null,actionResult);
- var jsonCollection = actionResult?.Value as List;
- Assert.AreEqual(createAnImage?.FilePath,jsonCollection?.FirstOrDefault()?.FilePath);
-
- var createAnImage2 = new CreateAnImage(); //restore afterwards
- Assert.IsNotNull(createAnImage2);
- }
-
- [TestMethod]
- public async Task ApiController_Delete_API_RemoveNotAllowedFile_Test()
- {
- // re add data
- var createAnImage = await InsertSearchData();
- Assert.IsNotNull(createAnImage?.FilePath);
-
- // Clean existing items to avoid errors
- var itemByHash = _query.SingleItem(createAnImage.FilePath);
- Assert.IsNotNull(itemByHash);
- Assert.IsNotNull(itemByHash.FileIndexItem);
-
- itemByHash.FileIndexItem.Tags = string.Empty;
- await _query.UpdateItemAsync(itemByHash.FileIndexItem);
-
- _appSettings.DatabaseType = AppSettings.DatabaseTypeList.InMemoryDatabase;
-
- var selectorStorage =
- new FakeSelectorStorage(new StorageSubPathFilesystem(_appSettings, new FakeIWebLogger()));
-
- var deleteItem = new DeleteItem(_query,_appSettings,selectorStorage);
- var controller = new DeleteController(deleteItem);
-
- var notFoundResult = await controller.Delete(createAnImage.FilePath) as NotFoundObjectResult;
- Assert.AreEqual(404,notFoundResult?.StatusCode);
- var jsonCollection = notFoundResult?.Value as List;
-
- Assert.AreEqual(FileIndexItem.ExifStatus.OperationNotSupported,
- jsonCollection?.FirstOrDefault()?.Status);
-
- await _query.RemoveItemAsync(_query.SingleItem(createAnImage.FilePath)?.FileIndexItem!);
- }
-
-
- [TestMethod]
- public async Task ApiController_Delete_SourceImageMissingOnDisk_WithFakeExiftool()
- {
await _query.AddItemAsync(new FileIndexItem
{
- FileName = "345678765434567.jpg",
+ FileName = _createAnImage.FileName,
ParentDirectory = "/",
- FileHash = "345678765434567"
+ FileHash = fileHashCode,
+ ColorClass = ColorClassParser.Color.Winner, // 1
+ Tags = isDelete
});
-
- var selectorStorage = new FakeSelectorStorage(new StorageSubPathFilesystem(_appSettings, new FakeIWebLogger()));
- var deleteItem = new DeleteItem(_query,_appSettings,selectorStorage);
- var controller = new DeleteController(deleteItem);
- var notFoundResult = await controller.Delete("/345678765434567.jpg") as NotFoundObjectResult;
- Assert.AreEqual(404,notFoundResult?.StatusCode);
-
- await _query.RemoveItemAsync(_query.SingleItem("/345678765434567.jpg")?.FileIndexItem!);
}
+ return _query.GetObjectByFilePath(_createAnImage.DbPath);
+ }
+
+
+ [TestMethod]
+ public async Task ApiController_Delete_API_HappyFlow_Test()
+ {
+ var createAnImage = await InsertSearchData(true);
+ _appSettings.DatabaseType = AppSettings.DatabaseTypeList.InMemoryDatabase;
+
+ // RealFs Storage
+ var selectorStorage =
+ new FakeSelectorStorage(
+ new StorageSubPathFilesystem(_appSettings, new FakeIWebLogger()));
+
+ var deleteItem = new DeleteItem(_query, _appSettings, selectorStorage);
+ var controller = new DeleteController(deleteItem);
+
+ Console.WriteLine("createAnImage.FilePath");
+ Console.WriteLine("@#~ " + createAnImage?.FilePath);
+
+ // create an image
+ var createAnImage1 = new CreateAnImage();
+ Assert.IsNotNull(createAnImage1);
+
+ var actionResult = await controller.Delete(createAnImage?.FilePath!) as JsonResult;
+ Assert.AreNotEqual(null, actionResult);
+ var jsonCollection = actionResult?.Value as List;
+ Assert.AreEqual(createAnImage?.FilePath, jsonCollection?.FirstOrDefault()?.FilePath);
+
+ var createAnImage2 = new CreateAnImage(); //restore afterwards
+ Assert.IsNotNull(createAnImage2);
+ }
+
+ [TestMethod]
+ public async Task Delete_ReturnsBadRequest()
+ {
+ // Arrange
+ var deleteItem = new DeleteItem(_query, _appSettings, new FakeSelectorStorage());
+ var controller = new DeleteController(deleteItem);
+
+ controller.ModelState.AddModelError("Key", "ErrorMessage");
+
+ // Act
+ var result = await controller.Delete(null!);
+
+ // Assert
+ Assert.IsInstanceOfType(result);
+ }
+
+ [TestMethod]
+ public async Task ApiController_Delete_API_RemoveNotAllowedFile_Test()
+ {
+ // re add data
+ var createAnImage = await InsertSearchData();
+ Assert.IsNotNull(createAnImage?.FilePath);
+
+ // Clean existing items to avoid errors
+ var itemByHash = _query.SingleItem(createAnImage.FilePath);
+ Assert.IsNotNull(itemByHash);
+ Assert.IsNotNull(itemByHash.FileIndexItem);
+
+ itemByHash.FileIndexItem.Tags = string.Empty;
+ await _query.UpdateItemAsync(itemByHash.FileIndexItem);
+
+ _appSettings.DatabaseType = AppSettings.DatabaseTypeList.InMemoryDatabase;
+
+ var selectorStorage =
+ new FakeSelectorStorage(
+ new StorageSubPathFilesystem(_appSettings, new FakeIWebLogger()));
+
+ var deleteItem = new DeleteItem(_query, _appSettings, selectorStorage);
+ var controller = new DeleteController(deleteItem);
+
+ var notFoundResult =
+ await controller.Delete(createAnImage.FilePath) as NotFoundObjectResult;
+ Assert.AreEqual(404, notFoundResult?.StatusCode);
+ var jsonCollection = notFoundResult?.Value as List;
+
+ Assert.AreEqual(FileIndexItem.ExifStatus.OperationNotSupported,
+ jsonCollection?.FirstOrDefault()?.Status);
+
+ await _query.RemoveItemAsync(_query.SingleItem(createAnImage.FilePath)?.FileIndexItem!);
+ }
+
+
+ [TestMethod]
+ public async Task ApiController_Delete_SourceImageMissingOnDisk_WithFakeExiftool()
+ {
+ await _query.AddItemAsync(new FileIndexItem
+ {
+ FileName = "345678765434567.jpg",
+ ParentDirectory = "/",
+ FileHash = "345678765434567"
+ });
+
+ var selectorStorage =
+ new FakeSelectorStorage(
+ new StorageSubPathFilesystem(_appSettings, new FakeIWebLogger()));
+ var deleteItem = new DeleteItem(_query, _appSettings, selectorStorage);
+ var controller = new DeleteController(deleteItem);
+ var notFoundResult =
+ await controller.Delete("/345678765434567.jpg") as NotFoundObjectResult;
+ Assert.AreEqual(404, notFoundResult?.StatusCode);
+
+ await _query.RemoveItemAsync(_query.SingleItem("/345678765434567.jpg")?.FileIndexItem!);
}
}
diff --git a/starsky/starskytest/Controllers/DesktopEditorControllerTest.cs b/starsky/starskytest/Controllers/DesktopEditorControllerTest.cs
index ef09835d47..12468292ed 100644
--- a/starsky/starskytest/Controllers/DesktopEditorControllerTest.cs
+++ b/starsky/starskytest/Controllers/DesktopEditorControllerTest.cs
@@ -31,13 +31,30 @@ public void OpenAmountConfirmationChecker_FeatureToggleEnabled()
var result = controller.OpenAmountConfirmationChecker("/test.jpg;/test2.jpg");
- var castedResult = ( JsonResult )result;
- var boolValue = ( bool? )castedResult.Value;
+ var castedResult = ( JsonResult ) result;
+ var boolValue = ( bool? ) castedResult.Value;
// mock is always true
Assert.IsTrue(boolValue);
}
+ [TestMethod]
+ public void OpenAmountConfirmationChecker_ReturnsBadRequest()
+ {
+ // Arrange
+ var controller = new DesktopEditorController(new OpenEditorDesktopService(new AppSettings(),
+ new FakeIOpenApplicationNativeService(new List(), "test"),
+ new FakeIOpenEditorPreflight(new List())));
+
+ controller.ModelState.AddModelError("Key", "ErrorMessage");
+
+ // Act
+ var result = controller.OpenAmountConfirmationChecker(null!);
+
+ // Assert
+ Assert.IsInstanceOfType(result);
+ }
+
[TestMethod]
public async Task OpenAsync_FeatureToggleDisabled()
{
@@ -51,11 +68,28 @@ public async Task OpenAsync_FeatureToggleDisabled()
};
var result = await controller.OpenAsync("/test.jpg;/test2.jpg");
- var castedResult = ( BadRequestObjectResult )result;
+ var castedResult = ( BadRequestObjectResult ) result;
Assert.AreEqual(400, castedResult.StatusCode);
}
+ [TestMethod]
+ public async Task OpenAsync_ReturnsBadRequest()
+ {
+ // Arrange
+ var controller = new DesktopEditorController(new OpenEditorDesktopService(new AppSettings(),
+ new FakeIOpenApplicationNativeService(new List(), "test"),
+ new FakeIOpenEditorPreflight(new List())));
+
+ controller.ModelState.AddModelError("Key", "ErrorMessage");
+
+ // Act
+ var result = await controller.OpenAsync(null!);
+
+ // Assert
+ Assert.IsInstanceOfType(result);
+ }
+
[TestMethod]
public async Task OpenAsync_NoResultsBack()
{
@@ -72,8 +106,8 @@ public async Task OpenAsync_NoResultsBack()
var result = await controller.OpenAsync("/test.jpg;/test2.jpg");
Assert.AreEqual(206, controller.HttpContext.Response.StatusCode);
- var castedResult = ( JsonResult )result;
- var arrayValues = ( List? )castedResult.Value;
+ var castedResult = ( JsonResult ) result;
+ var arrayValues = ( List? ) castedResult.Value;
Assert.AreEqual(0, arrayValues?.Count);
}
@@ -83,7 +117,7 @@ public async Task OpenAsync_HappyFlow()
{
var preflight = new FakeIOpenEditorPreflight(new List
{
- new PathImageFormatExistsAppPathModel
+ new()
{
AppPath = "test",
Status = FileIndexItem.ExifStatus.Ok,
@@ -105,8 +139,8 @@ public async Task OpenAsync_HappyFlow()
var result = await controller.OpenAsync("/test.jpg;/test2.jpg");
Assert.AreEqual(200, controller.HttpContext.Response.StatusCode);
- var castedResult = ( JsonResult )result;
- var arrayValues = ( List? )castedResult.Value;
+ var castedResult = ( JsonResult ) result;
+ var arrayValues = ( List? ) castedResult.Value;
Assert.AreEqual(1, arrayValues?.Count);
}
diff --git a/starsky/starskytest/Controllers/DiskControllerTest.cs b/starsky/starskytest/Controllers/DiskControllerTest.cs
index 72e2471710..b6c34d5593 100644
--- a/starsky/starskytest/Controllers/DiskControllerTest.cs
+++ b/starsky/starskytest/Controllers/DiskControllerTest.cs
@@ -26,297 +26,315 @@
using starskytest.FakeCreateAn;
using starskytest.FakeMocks;
-namespace starskytest.Controllers
+namespace starskytest.Controllers;
+
+[TestClass]
+public sealed class DiskControllerTest
{
- [TestClass]
- public sealed class DiskControllerTest
+ private readonly CreateAnImage _createAnImage;
+ private readonly Query _query;
+ private IStorage? _iStorage;
+
+ public DiskControllerTest()
{
- private readonly Query _query;
- private readonly CreateAnImage _createAnImage;
- private IStorage? _iStorage;
+ var provider = new ServiceCollection()
+ .AddMemoryCache()
+ .BuildServiceProvider();
+ var memoryCache = provider.GetService();
- public DiskControllerTest()
- {
- var provider = new ServiceCollection()
- .AddMemoryCache()
- .BuildServiceProvider();
- var memoryCache = provider.GetService();
+ var builderDb = new DbContextOptionsBuilder();
+ builderDb.UseInMemoryDatabase("SyncControllerTest");
+ var options = builderDb.Options;
+ var context = new ApplicationDbContext(options);
- var builderDb = new DbContextOptionsBuilder();
- builderDb.UseInMemoryDatabase("SyncControllerTest");
- var options = builderDb.Options;
- var context = new ApplicationDbContext(options);
+ // Inject Fake Exiftool; dependency injection
+ var services = new ServiceCollection();
+ services.AddSingleton();
- // Inject Fake Exiftool; dependency injection
- var services = new ServiceCollection();
- services.AddSingleton();
+ // Fake the readmeta output
+ services.AddSingleton();
- // Fake the readmeta output
- services.AddSingleton();
+ // Inject Config helper
+ services.AddSingleton(new ConfigurationBuilder().Build());
+ // random config
+ _createAnImage = new CreateAnImage();
+ var dict = new Dictionary
+ {
+ { "App:StorageFolder", _createAnImage.BasePath },
+ { "App:ThumbnailTempFolder", _createAnImage.BasePath },
+ { "App:Verbose", "true" }
+ };
+ // Start using dependency injection
+ var builder = new ConfigurationBuilder();
+ // Add random config to dependency injection
+ builder.AddInMemoryCollection(dict);
+ // build config
+ var configuration = builder.Build();
+ // inject config as object to a service
+ services.ConfigurePoCo(configuration.GetSection("App"));
+
+ // Add Background services
+ services.AddSingleton();
+ services.AddSingleton();
+
+ // build the service
+ var serviceProvider = services.BuildServiceProvider();
+ // get the service
+ var appSettings = serviceProvider.GetRequiredService();
+
+ var scopeFactory = serviceProvider.GetRequiredService();
+ _query = new Query(context, appSettings, scopeFactory, new FakeIWebLogger(),
+ memoryCache);
+ }
- // Inject Config helper
- services.AddSingleton(new ConfigurationBuilder().Build());
- // random config
- _createAnImage = new CreateAnImage();
- var dict = new Dictionary
- {
- { "App:StorageFolder", _createAnImage.BasePath },
- { "App:ThumbnailTempFolder", _createAnImage.BasePath },
- { "App:Verbose", "true" }
- };
- // Start using dependency injection
- var builder = new ConfigurationBuilder();
- // Add random config to dependency injection
- builder.AddInMemoryCollection(dict!);
- // build config
- var configuration = builder.Build();
- // inject config as object to a service
- services.ConfigurePoCo(configuration.GetSection("App"));
-
- // Add Background services
- services.AddSingleton();
- services.AddSingleton();
-
- // build the service
- var serviceProvider = services.BuildServiceProvider();
- // get the service
- var appSettings = serviceProvider.GetRequiredService();
-
- var scopeFactory = serviceProvider.GetRequiredService();
- _query = new Query(context, appSettings, scopeFactory, new FakeIWebLogger(),
- memoryCache);
- }
+ private async Task InsertSearchData()
+ {
+ _iStorage = new FakeIStorage(new List { "/" },
+ new List { _createAnImage.DbPath });
+ var fileHashCode =
+ ( await new FileHash(_iStorage).GetHashCodeAsync(_createAnImage.DbPath) ).Key;
- private async Task InsertSearchData()
+ if ( string.IsNullOrEmpty(await _query.GetSubPathByHashAsync(fileHashCode)) )
{
- _iStorage = new FakeIStorage(new List { "/" },
- new List { _createAnImage.DbPath });
- var fileHashCode =
- ( await new FileHash(_iStorage).GetHashCodeAsync(_createAnImage.DbPath) ).Key;
+ await _query.AddItemAsync(new FileIndexItem
+ {
+ FileName = "/", ParentDirectory = "/", IsDirectory = true
+ });
- if ( string.IsNullOrEmpty(await _query.GetSubPathByHashAsync(fileHashCode)) )
+ await _query.AddItemAsync(new FileIndexItem
{
- await _query.AddItemAsync(new FileIndexItem
- {
- FileName = "/", ParentDirectory = "/", IsDirectory = true
- });
-
- await _query.AddItemAsync(new FileIndexItem
- {
- FileName = _createAnImage.FileName,
- ParentDirectory = "/",
- FileHash = fileHashCode,
- ColorClass = ColorClassParser.Color.Winner, // 1
- });
- }
-
- _query.GetObjectByFilePath(_createAnImage.DbPath);
+ FileName = _createAnImage.FileName,
+ ParentDirectory = "/",
+ FileHash = fileHashCode,
+ ColorClass = ColorClassParser.Color.Winner // 1
+ });
}
+ await _query.GetObjectByFilePathAsync(_createAnImage.DbPath);
+ }
- [TestMethod]
- public async Task SyncControllerTest_Rename_NotFoundInIndex()
- {
- var context = new ControllerContext { HttpContext = new DefaultHttpContext() };
- var fakeStorage = new FakeIStorage();
- var storageSelector = new FakeSelectorStorage(fakeStorage);
- var controller = new DiskController(_query, storageSelector,
- new FakeIWebSocketConnectionsService(), new FakeINotificationQuery());
- controller.ControllerContext = context;
+ [TestMethod]
+ public async Task SyncControllerTest_Rename_NotFoundInIndex()
+ {
+ var context = new ControllerContext { HttpContext = new DefaultHttpContext() };
+ var fakeStorage = new FakeIStorage();
+ var storageSelector = new FakeSelectorStorage(fakeStorage);
- var result =
- await controller.Rename("/notfound-image.jpg", "/test.jpg") as NotFoundObjectResult;
+ var controller = new DiskController(_query, storageSelector,
+ new FakeIWebSocketConnectionsService(), new FakeINotificationQuery());
+ controller.ControllerContext = context;
- Assert.AreEqual(404, result?.StatusCode);
- }
+ var result =
+ await controller.Rename("/notfound-image.jpg", "/test.jpg") as NotFoundObjectResult;
- [TestMethod]
- public async Task SyncControllerTest_BadRequest()
- {
- var context = new ControllerContext { HttpContext = new DefaultHttpContext() };
- var fakeStorage = new FakeIStorage();
- var storageSelector = new FakeSelectorStorage(fakeStorage);
+ Assert.AreEqual(404, result?.StatusCode);
+ }
- var controller = new DiskController(_query, storageSelector,
- new FakeIWebSocketConnectionsService(), new FakeINotificationQuery());
- controller.ControllerContext = context;
+ [TestMethod]
+ public async Task Rename_BadRequest()
+ {
+ var context = new ControllerContext { HttpContext = new DefaultHttpContext() };
+ var fakeStorage = new FakeIStorage();
+ var storageSelector = new FakeSelectorStorage(fakeStorage);
- var result =
- await controller.Rename(string.Empty, "/test.jpg") as BadRequestObjectResult;
+ var controller = new DiskController(_query, storageSelector,
+ new FakeIWebSocketConnectionsService(), new FakeINotificationQuery());
+ controller.ControllerContext = context;
- Assert.AreEqual(400, result?.StatusCode);
- }
+ var result =
+ await controller.Rename(string.Empty, "/test.jpg") as BadRequestObjectResult;
- [TestMethod]
- public async Task SyncControllerTest_Rename_Good()
- {
- await InsertSearchData();
+ Assert.AreEqual(400, result?.StatusCode);
+ }
- var context = new ControllerContext { HttpContext = new DefaultHttpContext() };
+ [TestMethod]
+ public async Task Rename_ReturnsBadRequest()
+ {
+ var context = new ControllerContext { HttpContext = new DefaultHttpContext() };
+ var fakeStorage = new FakeIStorage();
+ var storageSelector = new FakeSelectorStorage(fakeStorage);
- var fakeStorage = new FakeIStorage(new List { "/" },
- new List { _createAnImage.DbPath });
- var storageSelector = new FakeSelectorStorage(fakeStorage);
+ var controller = new DiskController(_query, storageSelector,
+ new FakeIWebSocketConnectionsService(), new FakeINotificationQuery());
+ controller.ControllerContext = context;
+ controller.ModelState.AddModelError("Key", "ErrorMessage");
- var controller =
- new DiskController(_query, storageSelector,
- new FakeIWebSocketConnectionsService(), new FakeINotificationQuery())
- {
- ControllerContext = context
- };
+ var result =
+ await controller.Rename(string.Empty, "/test.jpg") as BadRequestObjectResult;
- var result = await controller.Rename(_createAnImage.DbPath, "/test.jpg") as JsonResult;
- var list = result?.Value as List;
+ Assert.AreEqual(400, result?.StatusCode);
+ Assert.IsInstanceOfType(result);
+ }
- Assert.AreEqual(FileIndexItem.ExifStatus.Ok, list?.FirstOrDefault()?.Status);
+ [TestMethod]
+ public async Task SyncControllerTest_Rename_Good()
+ {
+ await InsertSearchData();
- await _query.RemoveItemAsync(( await _query.GetObjectByFilePathAsync("/test.jpg") )!);
- }
+ var context = new ControllerContext { HttpContext = new DefaultHttpContext() };
- [TestMethod]
- public async Task SyncControllerTest_Rename_WithCurrentStatusDisabled()
- {
- await InsertSearchData();
+ var fakeStorage = new FakeIStorage(new List { "/" },
+ new List { _createAnImage.DbPath });
+ var storageSelector = new FakeSelectorStorage(fakeStorage);
- var context = new ControllerContext { HttpContext = new DefaultHttpContext() };
+ var controller =
+ new DiskController(_query, storageSelector,
+ new FakeIWebSocketConnectionsService(), new FakeINotificationQuery())
+ {
+ ControllerContext = context
+ };
- var fakeStorage = new FakeIStorage(new List { "/" },
- new List { _createAnImage.DbPath });
- var storageSelector = new FakeSelectorStorage(fakeStorage);
+ var result = await controller.Rename(_createAnImage.DbPath, "/test.jpg") as JsonResult;
+ var list = result?.Value as List;
- var controller =
- new DiskController(_query, storageSelector,
- new FakeIWebSocketConnectionsService(), new FakeINotificationQuery())
- {
- ControllerContext = context
- };
+ Assert.AreEqual(FileIndexItem.ExifStatus.Ok, list?.FirstOrDefault()?.Status);
- var result =
- await controller.Rename(_createAnImage.DbPath, "/test.jpg", true, false) as
- JsonResult;
- var list = result?.Value as List;
+ await _query.RemoveItemAsync(( await _query.GetObjectByFilePathAsync("/test.jpg") )!);
+ }
- Assert.AreEqual(FileIndexItem.ExifStatus.Ok, list?[0].Status);
- Assert.AreEqual(FileIndexItem.ExifStatus.NotFoundSourceMissing, list?[1].Status);
+ [TestMethod]
+ public async Task SyncControllerTest_Rename_WithCurrentStatusDisabled()
+ {
+ await InsertSearchData();
- await _query.RemoveItemAsync(( await _query.GetObjectByFilePathAsync("/test.jpg") )!);
- }
+ var context = new ControllerContext { HttpContext = new DefaultHttpContext() };
- [TestMethod]
- public async Task SyncControllerTest_Rename_Good_SocketUpdate()
- {
- await InsertSearchData();
+ var fakeStorage = new FakeIStorage(new List { "/" },
+ new List { _createAnImage.DbPath });
+ var storageSelector = new FakeSelectorStorage(fakeStorage);
- var context = new ControllerContext { HttpContext = new DefaultHttpContext() };
- var socket = new FakeIWebSocketConnectionsService();
+ var controller =
+ new DiskController(_query, storageSelector,
+ new FakeIWebSocketConnectionsService(), new FakeINotificationQuery())
+ {
+ ControllerContext = context
+ };
- var fakeStorage = new FakeIStorage(new List { "/" },
- new List { _createAnImage.DbPath });
- var storageSelector = new FakeSelectorStorage(fakeStorage);
+ var result =
+ await controller.Rename(_createAnImage.DbPath, "/test.jpg", true, false) as
+ JsonResult;
+ var list = result?.Value as List;
- var controller =
- new DiskController(_query, storageSelector,
- socket, new FakeINotificationQuery()) { ControllerContext = context };
+ Assert.AreEqual(FileIndexItem.ExifStatus.Ok, list?[0].Status);
+ Assert.AreEqual(FileIndexItem.ExifStatus.NotFoundSourceMissing, list?[1].Status);
- await controller.Rename(_createAnImage.DbPath, "/test.jpg");
+ await _query.RemoveItemAsync(( await _query.GetObjectByFilePathAsync("/test.jpg") )!);
+ }
- Assert.AreEqual(1, socket.FakeSendToAllAsync.Count(p => !p.Contains("[system]")));
- Assert.IsTrue(socket.FakeSendToAllAsync[0].Contains("/test.jpg"));
+ [TestMethod]
+ public async Task SyncControllerTest_Rename_Good_SocketUpdate()
+ {
+ await InsertSearchData();
- await _query.RemoveItemAsync(( await _query.GetObjectByFilePathAsync("/test.jpg") )!);
- }
+ var context = new ControllerContext { HttpContext = new DefaultHttpContext() };
+ var socket = new FakeIWebSocketConnectionsService();
- [TestMethod]
- public async Task SyncControllerTest_Mkdir_Good()
- {
- await InsertSearchData();
- var context = new ControllerContext { HttpContext = new DefaultHttpContext() };
-
- var fakeStorage = new FakeIStorage(new List { "/" },
- new List { _createAnImage.DbPath });
- var storageSelector = new FakeSelectorStorage(fakeStorage);
-
- var controller =
- new DiskController(_query, storageSelector,
- new FakeIWebSocketConnectionsService(), new FakeINotificationQuery())
- {
- ControllerContext = context
- };
-
- var result = await controller.Mkdir("/test_dir") as JsonResult;
- var list = result?.Value as List;
- Assert.AreEqual(FileIndexItem.ExifStatus.Ok, list?.FirstOrDefault()?.Status);
- }
+ var fakeStorage = new FakeIStorage(new List { "/" },
+ new List { _createAnImage.DbPath });
+ var storageSelector = new FakeSelectorStorage(fakeStorage);
- [TestMethod]
- public async Task SyncControllerTest_Mkdir_Good_SocketUpdate()
- {
- await InsertSearchData();
- var context = new ControllerContext { HttpContext = new DefaultHttpContext() };
+ var controller =
+ new DiskController(_query, storageSelector,
+ socket, new FakeINotificationQuery()) { ControllerContext = context };
- var socket = new FakeIWebSocketConnectionsService();
- var fakeStorage = new FakeIStorage(new List { "/" },
- new List { _createAnImage.DbPath });
- var storageSelector = new FakeSelectorStorage(fakeStorage);
+ await controller.Rename(_createAnImage.DbPath, "/test.jpg");
- var controller =
- new DiskController(_query, storageSelector,
- socket, new FakeINotificationQuery()) { ControllerContext = context };
+ Assert.AreEqual(1, socket.FakeSendToAllAsync.Count(p => !p.Contains("[system]")));
+ Assert.IsTrue(socket.FakeSendToAllAsync[0].Contains("/test.jpg"));
- await controller.Mkdir("/test_dir");
+ await _query.RemoveItemAsync(( await _query.GetObjectByFilePathAsync("/test.jpg") )!);
+ }
- var value = socket.FakeSendToAllAsync.Find(p =>
- !p.StartsWith("[system]"));
+ [TestMethod]
+ public async Task SyncControllerTest_Mkdir_Good()
+ {
+ await InsertSearchData();
+ var context = new ControllerContext { HttpContext = new DefaultHttpContext() };
- Assert.IsNotNull(value);
- Assert.IsTrue(value.Contains("/test_dir"));
- }
+ var fakeStorage = new FakeIStorage(new List { "/" },
+ new List { _createAnImage.DbPath });
+ var storageSelector = new FakeSelectorStorage(fakeStorage);
- [TestMethod]
- public async Task SyncControllerTest_Mkdir_Exist()
- {
- await InsertSearchData();
- var context = new ControllerContext { HttpContext = new DefaultHttpContext() };
-
- var fakeStorage = new FakeIStorage(new List