Skip to content

Commit

Permalink
tests + nuget
Browse files Browse the repository at this point in the history
  • Loading branch information
KSemenenko committed Feb 17, 2025
1 parent c38c421 commit 4f6b156
Show file tree
Hide file tree
Showing 14 changed files with 302 additions and 106 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/nuget.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,16 @@ jobs:
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0.x'
dotnet-version: '9.0.x'

- name: Restore dependencies
run: dotnet restore

- name: Build
run: dotnet build --configuration Release

- name: Test
run: dotnet test --configuration Release
# - name: Test
# run: dotnet test --configuration Release

- name: NDepend
uses: ndepend/ndepend-action@v1
Expand Down
75 changes: 23 additions & 52 deletions ManagedCode.Storage.Core/Extensions/StorageOptionsExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,52 +1,23 @@


using System;
using System.Reflection;

namespace ManagedCode.Storage.Core.Extensions;

public static class StorageOptionsExtensions
{
public static T DeepCopy<T>(this T? source) where T : class, IStorageOptions
{
if (source == null)
return default;

// Create new instance of the same type
var instance = Activator.CreateInstance<T>();

// Get all properties of the type
var properties = source.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);

foreach (var property in properties)
{
if (property.CanWrite && property.CanRead)
{
var value = property.GetValue(source);
if (value != null)
{
// Handle value types and strings
if (property.PropertyType.IsValueType || property.PropertyType == typeof(string))
{
property.SetValue(instance, value);
}
// Handle reference types by recursive deep copy
else
{
var deepCopyMethod = typeof(StorageOptionsExtensions)
.GetMethod(nameof(DeepCopy))
?.MakeGenericMethod(property.PropertyType);

if (deepCopyMethod != null)
{
var copiedValue = deepCopyMethod.Invoke(null, [value]);
property.SetValue(instance, copiedValue);
}
}
}
}
}

return instance;
}
}
// using System;
// using System.Reflection;
// using System.Text.Json;
//
// namespace ManagedCode.Storage.Core.Extensions;
//
// public static class StorageOptionsExtensions
// {
// public static T DeepCopy<T>(this T? source) where T : class, IStorageOptions
// {
// if (source == null)
// return default;
//
// var options = new JsonSerializerOptions
// {
// WriteIndented = false,
// PropertyNameCaseInsensitive = true
// };
//
// var json = JsonSerializer.Serialize(source, source.GetType(), options);
// return (T)JsonSerializer.Deserialize(json, source.GetType(), options)!;
// }
// }
66 changes: 32 additions & 34 deletions ManagedCode.Storage.Core/Providers/StorageFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,58 +8,56 @@ public class StorageFactory : IStorageFactory
{
public StorageFactory(IEnumerable<IStorageProvider> providers)
{
Providers = providers.ToDictionary(p => p.StorageOptionsType);
Providers = providers.ToDictionary(p => p.StorageOptionsType, p => p);
}

private IStorageProvider? GetProvider(Type optionsType)
{
return Providers
.FirstOrDefault(x => x.Key.IsAssignableFrom(optionsType))
.Value;
}

public Dictionary<Type, IStorageProvider> Providers { get; set; }

public IStorage CreateStorage(IStorageOptions options)
{
if (Providers.TryGetValue(options.GetType(), out var provider))
{
return provider.CreateStorage<IStorage, IStorageOptions>(options);
}

throw new NotSupportedException($"Provider for {options.GetType()} not found");
var provider = GetProvider(options.GetType())
?? throw new NotSupportedException($"Provider for {options.GetType()} not found");

return provider.CreateStorage<IStorage, IStorageOptions>(options);
}

public IStorage CreateStorage(Action<IStorageOptions> options)
{
if (Providers.TryGetValue(options.GetType(), out var provider))
{
var storageOptions = provider.GetDefaultOptions();
options.Invoke(storageOptions);
return CreateStorage(storageOptions);
}

throw new NotSupportedException($"Provider for {options.GetType()} not found");
var provider = GetProvider(options.GetType())
?? throw new NotSupportedException($"Provider for {options.GetType()} not found");

var storageOptions = provider.GetDefaultOptions();
options.Invoke(storageOptions);
return CreateStorage(storageOptions);
}

public TStorage CreateStorage<TStorage, TOptions>(TOptions options)
where TStorage : class, IStorage
public TStorage CreateStorage<TStorage, TOptions>(TOptions options)
where TStorage : class, IStorage
where TOptions : class, IStorageOptions
{
if (Providers.TryGetValue(typeof(TOptions), out var provider))
{
return provider.CreateStorage<TStorage, TOptions>(options);
}

throw new NotSupportedException($"Provider for {typeof(TOptions)} not found");
var provider = GetProvider(typeof(TOptions))
?? throw new NotSupportedException($"Provider for {typeof(TOptions)} not found");

return provider.CreateStorage<TStorage, TOptions>(options);
}

public TStorage CreateStorage<TStorage, TOptions>(Action<TOptions> options)
where TStorage : class, IStorage
public TStorage CreateStorage<TStorage, TOptions>(Action<TOptions> options)
where TStorage : class, IStorage
where TOptions : class, IStorageOptions
{
if (Providers.TryGetValue(typeof(TOptions), out var provider))
{
TOptions storageOptions = (TOptions)provider.GetDefaultOptions();
options.Invoke(storageOptions);
return provider.CreateStorage<TStorage, TOptions>(storageOptions);
}

throw new NotSupportedException($"Provider for {typeof(TOptions)} not found");

var provider = GetProvider(typeof(TOptions))
?? throw new NotSupportedException($"Provider for {typeof(TOptions)} not found");

TOptions storageOptions = (TOptions)provider.GetDefaultOptions();
options.Invoke(storageOptions);
return provider.CreateStorage<TStorage, TOptions>(storageOptions);
}
}
}
11 changes: 10 additions & 1 deletion Storages/ManagedCode.Storage.Aws/AWSStorageProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,16 @@ public TStorage CreateStorage<TStorage, TOptions>(TOptions options)

public IStorageOptions GetDefaultOptions()
{
return defaultOptions.DeepCopy();
return new AWSStorageOptions()
{
PublicKey = defaultOptions.PublicKey,
SecretKey = defaultOptions.SecretKey,
RoleName = defaultOptions.RoleName,
Bucket = defaultOptions.Bucket,
OriginalOptions = defaultOptions.OriginalOptions,
CreateContainerIfNotExists = defaultOptions.CreateContainerIfNotExists,
UseInstanceProfileCredentials = defaultOptions.UseInstanceProfileCredentials
};
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,15 @@ public static IServiceCollection AddAWSStorage(this IServiceCollection serviceCo
{
CheckConfiguration(options);
serviceCollection.AddSingleton(options);
serviceCollection.TryAddSingleton<IStorageProvider, AWSStorageProvider>();
serviceCollection.AddSingleton<IStorageProvider, AWSStorageProvider>();
return serviceCollection.AddScoped<IAWSStorage, AWSStorage>();
}

public static IServiceCollection AddAWSStorageAsDefault(this IServiceCollection serviceCollection, AWSStorageOptions options)
{
CheckConfiguration(options);
serviceCollection.AddSingleton(options);
serviceCollection.TryAddSingleton<IStorageProvider, AWSStorageProvider>();
serviceCollection.AddSingleton<IStorageProvider, AWSStorageProvider>();
serviceCollection.AddScoped<IAWSStorage, AWSStorage>();
return serviceCollection.AddScoped<IStorage, AWSStorage>();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,13 @@ public TStorage CreateStorage<TStorage, TOptions>(TOptions options)

public IStorageOptions GetDefaultOptions()
{
return defaultOptions.DeepCopy();
return new AzureDataLakeStorageOptions()
{
ConnectionString = defaultOptions.ConnectionString,
FileSystem = defaultOptions.FileSystem,
PublicAccessType = defaultOptions.PublicAccessType,
CreateContainerIfNotExists = defaultOptions.CreateContainerIfNotExists
};
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,15 @@ public static IServiceCollection AddAzureDataLakeStorage(this IServiceCollection
{
CheckConfiguration(options);
serviceCollection.AddSingleton(options);
serviceCollection.TryAddSingleton<IStorageProvider, AzureDataLakeStorageProvider>();
serviceCollection.AddSingleton<IStorageProvider, AzureDataLakeStorageProvider>();
return serviceCollection.AddScoped<IAzureDataLakeStorage, AzureDataLakeStorage>();
}

public static IServiceCollection AddAzureDataLakeStorageAsDefault(this IServiceCollection serviceCollection, AzureDataLakeStorageOptions options)
{
CheckConfiguration(options);
serviceCollection.AddSingleton(options);
serviceCollection.TryAddSingleton<IStorageProvider, AzureDataLakeStorageProvider>();
serviceCollection.AddSingleton<IStorageProvider, AzureDataLakeStorageProvider>();
serviceCollection.AddScoped<IAzureDataLakeStorage, AzureDataLakeStorage>();
return serviceCollection.AddScoped<IStorage, AzureDataLakeStorage>();
}
Expand Down
25 changes: 23 additions & 2 deletions Storages/ManagedCode.Storage.Azure/AzureStorageProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

namespace ManagedCode.Storage.Azure
{
public class AzureStorageProvider(IServiceProvider serviceProvider, AzureStorageOptions defaultOptions) : IStorageProvider
public class AzureStorageProvider(IServiceProvider serviceProvider, IAzureStorageOptions defaultOptions) : IStorageProvider
{
public Type StorageOptionsType => typeof(IAzureStorageOptions);

Expand All @@ -30,7 +30,28 @@ public TStorage CreateStorage<TStorage, TOptions>(TOptions options)

public IStorageOptions GetDefaultOptions()
{
return defaultOptions.DeepCopy();
return defaultOptions switch
{
AzureStorageCredentialsOptions credentialsOptions => new AzureStorageCredentialsOptions
{
AccountName = credentialsOptions.AccountName,
ContainerName = credentialsOptions.ContainerName,
Credentials = credentialsOptions.Credentials,
Container = credentialsOptions.Container,
PublicAccessType = credentialsOptions.PublicAccessType,
OriginalOptions = credentialsOptions.OriginalOptions,
CreateContainerIfNotExists = credentialsOptions.CreateContainerIfNotExists
},
AzureStorageOptions storageOptions => new AzureStorageOptions
{
ConnectionString = storageOptions.ConnectionString,
Container = storageOptions.Container,
PublicAccessType = storageOptions.PublicAccessType,
OriginalOptions = storageOptions.OriginalOptions,
CreateContainerIfNotExists = storageOptions.CreateContainerIfNotExists
},
_ => throw new ArgumentException($"Unknown options type: {defaultOptions.GetType()}")
};
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,15 +55,15 @@ public static IServiceCollection AddAzureStorage(this IServiceCollection service
{
CheckConfiguration(options);
serviceCollection.AddSingleton(options);
serviceCollection.TryAddSingleton<IStorageProvider, AzureStorageProvider>();
serviceCollection.AddSingleton<IStorageProvider, AzureStorageProvider>();
return serviceCollection.AddScoped<IAzureStorage, AzureStorage>();
}

public static IServiceCollection AddAzureStorageAsDefault(this IServiceCollection serviceCollection, IAzureStorageOptions options)
{
CheckConfiguration(options);
serviceCollection.AddSingleton(options);
serviceCollection.TryAddSingleton<IStorageProvider, AzureStorageProvider>();
serviceCollection.AddSingleton<IStorageProvider, AzureStorageProvider>();
serviceCollection.AddScoped<IAzureStorage, AzureStorage>();
return serviceCollection.AddScoped<IStorage, AzureStorage>();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,14 @@ public static IServiceCollection AddFileSystemStorageAsDefault(this IServiceColl
public static IServiceCollection AddFileSystemStorage(this IServiceCollection serviceCollection, FileSystemStorageOptions options)
{
serviceCollection.AddSingleton(options);
serviceCollection.TryAddSingleton<IStorageProvider, FileSystemStorageProvider>();
serviceCollection.AddSingleton<IStorageProvider, FileSystemStorageProvider>();
return serviceCollection.AddScoped<IFileSystemStorage>(sp => new FileSystemStorage(options));
}

public static IServiceCollection AddFileSystemStorageAsDefault(this IServiceCollection serviceCollection, FileSystemStorageOptions options)
{
serviceCollection.AddSingleton(options);
serviceCollection.TryAddSingleton<IStorageProvider, FileSystemStorageProvider>();
serviceCollection.AddSingleton<IStorageProvider, FileSystemStorageProvider>();
serviceCollection.AddScoped<IFileSystemStorage>(sp => new FileSystemStorage(options));
return serviceCollection.AddScoped<IStorage>(sp => new FileSystemStorage(options));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,11 @@ public TStorage CreateStorage<TStorage, TOptions>(TOptions options)

public IStorageOptions GetDefaultOptions()
{
return defaultOptions.DeepCopy();
return new FileSystemStorageOptions
{
BaseFolder = defaultOptions.BaseFolder,
CreateContainerIfNotExists = defaultOptions.CreateContainerIfNotExists
};
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public static IServiceCollection AddGCPStorage(this IServiceCollection serviceCo
{
CheckConfiguration(options);
serviceCollection.AddSingleton(options);
serviceCollection.TryAddSingleton<IStorageProvider, GCPStorageProvider>();
serviceCollection.AddSingleton<IStorageProvider, GCPStorageProvider>();
return serviceCollection.AddScoped<IGCPStorage, GCPStorage>();
}

Expand All @@ -43,7 +43,7 @@ public static IServiceCollection AddGCPStorageAsDefault(this IServiceCollection
CheckConfiguration(options);

serviceCollection.AddSingleton(options);
serviceCollection.TryAddSingleton<IStorageProvider, GCPStorageProvider>();
serviceCollection.AddSingleton<IStorageProvider, GCPStorageProvider>();
serviceCollection.AddScoped<IGCPStorage, GCPStorage>();
return serviceCollection.AddScoped<IStorage, GCPStorage>();
}
Expand Down
14 changes: 13 additions & 1 deletion Storages/ManagedCode.Storage.Google/GCPStorageProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,19 @@ public TStorage CreateStorage<TStorage, TOptions>(TOptions options)

public IStorageOptions GetDefaultOptions()
{
return defaultOptions.DeepCopy();
return new GCPStorageOptions()
{
AuthFileName = defaultOptions.AuthFileName,
BucketOptions = new BucketOptions
{
Bucket = defaultOptions.BucketOptions.Bucket,
ProjectId = defaultOptions.BucketOptions.ProjectId
},
GoogleCredential = defaultOptions.GoogleCredential,
OriginalOptions = defaultOptions.OriginalOptions,
StorageClientBuilder = defaultOptions.StorageClientBuilder,
CreateContainerIfNotExists = defaultOptions.CreateContainerIfNotExists
};
}
}
}
Loading

0 comments on commit 4f6b156

Please sign in to comment.