diff --git a/src/ImageSharp.Web.Providers.AWS/AmazonS3BucketClient.cs b/src/ImageSharp.Web.Providers.AWS/AmazonS3BucketClient.cs new file mode 100644 index 0000000..d894066 --- /dev/null +++ b/src/ImageSharp.Web.Providers.AWS/AmazonS3BucketClient.cs @@ -0,0 +1,56 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using Amazon.S3; + +namespace SixLabors.ImageSharp.Web; + +/// +/// Represents a scoped Amazon S3 client instance that is explicitly associated with a single S3 bucket. +/// This wrapper provides a strongly-typed link between the client and the bucket it operates on, +/// and optionally manages the lifetime of the underlying . +/// +public sealed class AmazonS3BucketClient : IDisposable +{ + private readonly bool disposeClient; + + /// + /// Initializes a new instance of the class. + /// + /// + /// The bucket name associated with this client instance. + /// + /// + /// The underlying Amazon S3 client instance. This should be an already configured instance of . + /// + /// + /// A value indicating whether the underlying client should be disposed when this instance is disposed. + /// + public AmazonS3BucketClient(string bucketName, AmazonS3Client client, bool disposeClient = true) + { + Guard.NotNullOrWhiteSpace(bucketName, nameof(bucketName)); + Guard.NotNull(client, nameof(client)); + this.BucketName = bucketName; + this.Client = client; + this.disposeClient = disposeClient; + } + + /// + /// Gets the bucket name associated with this client instance. + /// + public string BucketName { get; } + + /// + /// Gets the underlying Amazon S3 client instance. + /// + public AmazonS3Client Client { get; } + + /// + public void Dispose() + { + if (this.disposeClient) + { + this.Client.Dispose(); + } + } +} diff --git a/src/ImageSharp.Web.Providers.AWS/AmazonS3ClientFactory.cs b/src/ImageSharp.Web.Providers.AWS/AmazonS3ClientFactory.cs index bbbf557..ff21b56 100644 --- a/src/ImageSharp.Web.Providers.AWS/AmazonS3ClientFactory.cs +++ b/src/ImageSharp.Web.Providers.AWS/AmazonS3ClientFactory.cs @@ -14,25 +14,20 @@ internal static class AmazonS3ClientFactory /// with the same name does not already exist. /// /// The AWS S3 Storage cache options. - /// The current service provider. /// /// A new . /// /// Invalid configuration. - public static AmazonS3Client CreateClient(IAWSS3BucketClientOptions options, IServiceProvider serviceProvider) + public static AmazonS3BucketClient CreateClient(IAWSS3BucketClientOptions options) { - if (options.S3ClientProvider != null) - { - return options.S3ClientProvider(options, serviceProvider); - } - else if (!string.IsNullOrWhiteSpace(options.Endpoint)) + if (!string.IsNullOrWhiteSpace(options.Endpoint)) { // AccessKey can be empty. // AccessSecret can be empty. // PathStyle endpoint doesn't support AccelerateEndpoint. AmazonS3Config config = new() { ServiceURL = options.Endpoint, ForcePathStyle = true, AuthenticationRegion = options.Region }; SetTimeout(config, options.Timeout); - return new AmazonS3Client(options.AccessKey, options.AccessSecret, config); + return new(options.BucketName, new AmazonS3Client(options.AccessKey, options.AccessSecret, config)); } else if (!string.IsNullOrWhiteSpace(options.AccessKey)) { @@ -41,14 +36,14 @@ public static AmazonS3Client CreateClient(IAWSS3BucketClientOptions options, ISe RegionEndpoint region = RegionEndpoint.GetBySystemName(options.Region); AmazonS3Config config = new() { RegionEndpoint = region, UseAccelerateEndpoint = options.UseAccelerateEndpoint }; SetTimeout(config, options.Timeout); - return new AmazonS3Client(options.AccessKey, options.AccessSecret, config); + return new(options.BucketName, new AmazonS3Client(options.AccessKey, options.AccessSecret, config)); } else if (!string.IsNullOrWhiteSpace(options.Region)) { RegionEndpoint region = RegionEndpoint.GetBySystemName(options.Region); AmazonS3Config config = new() { RegionEndpoint = region, UseAccelerateEndpoint = options.UseAccelerateEndpoint }; SetTimeout(config, options.Timeout); - return new AmazonS3Client(config); + return new(options.BucketName, new AmazonS3Client(config)); } else { diff --git a/src/ImageSharp.Web.Providers.AWS/Caching/AWSS3StorageCache.cs b/src/ImageSharp.Web.Providers.AWS/Caching/AWSS3StorageCache.cs index a22db3f..aef41ac 100644 --- a/src/ImageSharp.Web.Providers.AWS/Caching/AWSS3StorageCache.cs +++ b/src/ImageSharp.Web.Providers.AWS/Caching/AWSS3StorageCache.cs @@ -13,11 +13,12 @@ namespace SixLabors.ImageSharp.Web.Caching.AWS; /// /// Implements an AWS S3 Storage based cache. /// -public class AWSS3StorageCache : IImageCache +public class AWSS3StorageCache : IImageCache, IDisposable { - private readonly IAmazonS3 amazonS3Client; + private readonly AmazonS3BucketClient amazonS3Client; private readonly string bucketName; private readonly string cacheFolder; + private bool isDisposed; /// /// Initializes a new instance of the class. @@ -28,8 +29,13 @@ public AWSS3StorageCache(IOptions cacheOptions, IServi { Guard.NotNull(cacheOptions, nameof(cacheOptions)); AWSS3StorageCacheOptions options = cacheOptions.Value; - this.bucketName = options.BucketName; - this.amazonS3Client = AmazonS3ClientFactory.CreateClient(options, serviceProvider); + + this.amazonS3Client = + options.S3ClientFactory?.Invoke(options, serviceProvider) + ?? AmazonS3ClientFactory.CreateClient(options); + + this.bucketName = this.amazonS3Client.BucketName; + this.cacheFolder = string.IsNullOrEmpty(options.CacheFolder) ? string.Empty : options.CacheFolder.Trim().Trim('/') + '/'; @@ -43,8 +49,8 @@ public AWSS3StorageCache(IOptions cacheOptions, IServi try { // HEAD request throws a 404 if not found. - MetadataCollection metadata = (await this.amazonS3Client.GetObjectMetadataAsync(request)).Metadata; - return new AWSS3StorageCacheResolver(this.amazonS3Client, this.bucketName, keyWithFolder, metadata); + MetadataCollection metadata = (await this.amazonS3Client.Client.GetObjectMetadataAsync(request)).Metadata; + return new AWSS3StorageCacheResolver(this.amazonS3Client.Client, this.bucketName, keyWithFolder, metadata); } catch { @@ -70,7 +76,7 @@ public Task SetAsync(string key, Stream stream, ImageCacheMetadata metadata) request.Metadata.Add(d.Key, d.Value); } - return this.amazonS3Client.PutObjectAsync(request); + return this.amazonS3Client.Client.PutObjectAsync(request); } /// @@ -84,23 +90,42 @@ public Task SetAsync(string key, Stream stream, ImageCacheMetadata metadata) /// and object data. specifies that the bucket /// data is private to the account owner. /// - /// The current service provider. /// /// If the bucket does not already exist, a describing the newly /// created bucket. If the container already exists, . /// - public static PutBucketResponse? CreateIfNotExists( - AWSS3StorageCacheOptions options, - S3CannedACL acl, - IServiceProvider serviceProvider) - => AsyncHelper.RunSync(() => CreateIfNotExistsAsync(options, acl, serviceProvider)); - - private static async Task CreateIfNotExistsAsync( - AWSS3StorageCacheOptions options, - S3CannedACL acl, - IServiceProvider serviceProvider) + public static PutBucketResponse? CreateIfNotExists(AWSS3StorageCacheOptions options, S3CannedACL acl) + => AsyncHelper.RunSync(() => CreateIfNotExistsAsync(options, acl)); + + /// + /// Releases the unmanaged resources used by the and optionally releases the managed resources. + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + protected virtual void Dispose(bool disposing) + { + if (!this.isDisposed) + { + if (disposing) + { + this.amazonS3Client?.Dispose(); + } + + this.isDisposed = true; + } + } + + /// + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + this.Dispose(disposing: true); + GC.SuppressFinalize(this); + } + + private static async Task CreateIfNotExistsAsync(AWSS3StorageCacheOptions options, S3CannedACL acl) { - AmazonS3Client client = AmazonS3ClientFactory.CreateClient(options, serviceProvider); + using AmazonS3BucketClient bucketClient = AmazonS3ClientFactory.CreateClient(options); + AmazonS3Client client = bucketClient.Client; bool foundBucket = false; ListBucketsResponse listBucketsResponse = await client.ListBucketsAsync(); diff --git a/src/ImageSharp.Web.Providers.AWS/Caching/AWSS3StorageCacheOptions.cs b/src/ImageSharp.Web.Providers.AWS/Caching/AWSS3StorageCacheOptions.cs index 68d5c94..e9c4a60 100644 --- a/src/ImageSharp.Web.Providers.AWS/Caching/AWSS3StorageCacheOptions.cs +++ b/src/ImageSharp.Web.Providers.AWS/Caching/AWSS3StorageCacheOptions.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using Amazon.S3; - namespace SixLabors.ImageSharp.Web.Caching.AWS; /// @@ -11,7 +9,7 @@ namespace SixLabors.ImageSharp.Web.Caching.AWS; public class AWSS3StorageCacheOptions : IAWSS3BucketClientOptions { /// - public Func? S3ClientProvider { get; set; } = null!; + public Func? S3ClientFactory { get; set; } /// public string? Region { get; set; } diff --git a/src/ImageSharp.Web.Providers.AWS/IAWSS3BucketClientOptions.cs b/src/ImageSharp.Web.Providers.AWS/IAWSS3BucketClientOptions.cs index 25310fa..62bd79f 100644 --- a/src/ImageSharp.Web.Providers.AWS/IAWSS3BucketClientOptions.cs +++ b/src/ImageSharp.Web.Providers.AWS/IAWSS3BucketClientOptions.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using Amazon.S3; - namespace SixLabors.ImageSharp.Web; /// @@ -11,49 +9,54 @@ namespace SixLabors.ImageSharp.Web; public interface IAWSS3BucketClientOptions { /// - /// Gets or sets a custom Azure AmazonS3Client provider + /// Gets or sets a custom factory method to create an . /// - Func? S3ClientProvider { get; set; } + public Func? S3ClientFactory { get; set; } /// /// Gets or sets the AWS region endpoint (us-east-1/us-west-1/ap-southeast-2). /// - string? Region { get; set; } + public string? Region { get; set; } /// /// Gets or sets the AWS bucket name. + /// Cannot be when is not set. /// - string BucketName { get; set; } + public string BucketName { get; set; } /// /// Gets or sets the AWS key - Can be used to override keys provided by the environment. /// If deploying inside an EC2 instance AWS keys will already be available via environment - /// variables and don't need to be specified. Follow AWS best security practices on . + /// variables and don't need to be specified. Follow AWS best security practices on + /// . /// - string? AccessKey { get; set; } + public string? AccessKey { get; set; } /// /// Gets or sets the AWS endpoint - used to override the default service endpoint. /// If deploying inside an EC2 instance AWS keys will already be available via environment - /// variables and don't need to be specified. Follow AWS best security practices on . + /// variables and don't need to be specified. Follow AWS best security practices on + /// . /// - string? AccessSecret { get; set; } + public string? AccessSecret { get; set; } /// /// Gets or sets the AWS endpoint - used for testing to over region endpoint allowing it /// to be set to localhost. /// - string? Endpoint { get; set; } + public string? Endpoint { get; set; } /// /// Gets or sets a value indicating whether the S3 accelerate endpoint is used. - /// The feature must be enabled on the bucket. Follow AWS instruction on . + /// The feature must be enabled on the bucket. Follow AWS instruction on + /// . /// - bool UseAccelerateEndpoint { get; set; } + public bool UseAccelerateEndpoint { get; set; } /// /// Gets or sets a value indicating the timeout for the S3 client. - /// If the value is set, the value is assigned to the Timeout property of the HttpWebRequest/HttpClient object used to send requests. + /// If the value is set, the value is assigned to the Timeout property of the HttpWebRequest/HttpClient + /// object used to send requests. /// - TimeSpan? Timeout { get; set; } + public TimeSpan? Timeout { get; set; } } diff --git a/src/ImageSharp.Web.Providers.AWS/Providers/AWSS3StorageImageProvider.cs b/src/ImageSharp.Web.Providers.AWS/Providers/AWSS3StorageImageProvider.cs index 4c3d5b2..20ab9b1 100644 --- a/src/ImageSharp.Web.Providers.AWS/Providers/AWSS3StorageImageProvider.cs +++ b/src/ImageSharp.Web.Providers.AWS/Providers/AWSS3StorageImageProvider.cs @@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp.Web.Providers.AWS; /// /// Returns images stored in AWS S3. /// -public class AWSS3StorageImageProvider : IImageProvider +public class AWSS3StorageImageProvider : IImageProvider, IDisposable { /// /// Character array to remove from paths. @@ -24,11 +24,12 @@ public class AWSS3StorageImageProvider : IImageProvider /// /// The containers for the blob services. /// - private readonly Dictionary buckets + private readonly Dictionary buckets = new(); private readonly AWSS3StorageImageProviderOptions storageOptions; private Func? match; + private bool isDisposed; /// /// Contains various helper methods based on the current configuration. @@ -41,7 +42,10 @@ private readonly Dictionary buckets /// The S3 storage options /// Contains various format helper methods based on the current configuration. /// The current service provider. - public AWSS3StorageImageProvider(IOptions storageOptions, FormatUtilities formatUtilities, IServiceProvider serviceProvider) + public AWSS3StorageImageProvider( + IOptions storageOptions, + FormatUtilities formatUtilities, + IServiceProvider serviceProvider) { Guard.NotNull(storageOptions, nameof(storageOptions)); @@ -51,7 +55,11 @@ public AWSS3StorageImageProvider(IOptions stor foreach (AWSS3BucketClientOptions bucket in this.storageOptions.S3Buckets) { - this.buckets.Add(bucket.BucketName, AmazonS3ClientFactory.CreateClient(bucket, serviceProvider)); + AmazonS3BucketClient s3Client = + bucket.S3ClientFactory?.Invoke(bucket, serviceProvider) + ?? AmazonS3ClientFactory.CreateClient(bucket); + + this.buckets.Add(s3Client.BucketName, s3Client); } } @@ -76,7 +84,7 @@ public bool IsValidRequest(HttpContext context) // the remaining path string as the key. // Path has already been correctly parsed before here. string bucketName = string.Empty; - IAmazonS3? s3Client = null; + AmazonS3Client? s3Client = null; // We want an exact match here to ensure that bucket names starting with // the same prefix are not mixed up. @@ -88,14 +96,14 @@ public bool IsValidRequest(HttpContext context) } int index = path.IndexOfAny(SlashChars); - string nameToMatch = index != -1 ? path.Substring(0, index) : path; + string nameToMatch = index != -1 ? path[..index] : path; foreach (string k in this.buckets.Keys) { if (nameToMatch.Equals(k, StringComparison.OrdinalIgnoreCase)) { bucketName = k; - s3Client = this.buckets[k]; + s3Client = this.buckets[k].Client; break; } } @@ -107,7 +115,7 @@ public bool IsValidRequest(HttpContext context) } // Key should be the remaining path string. - string key = path.Substring(bucketName.Length).TrimStart(SlashChars); + string key = path[bucketName.Length..].TrimStart(SlashChars); if (string.IsNullOrWhiteSpace(key)) { @@ -125,7 +133,7 @@ public bool IsValidRequest(HttpContext context) private bool IsMatch(HttpContext context) { - // Only match loosly here for performance. + // Only match loosely here for performance. // Path matching conflicts should be dealt with by configuration. string? path = context.Request.Path.Value?.TrimStart(SlashChars); @@ -178,6 +186,36 @@ private static async Task KeyExists(IAmazonS3 s3Client, string } } + /// + /// Releases the unmanaged resources used by the and optionally releases the managed resources. + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + protected virtual void Dispose(bool disposing) + { + if (!this.isDisposed) + { + if (disposing) + { + foreach (AmazonS3BucketClient client in this.buckets.Values) + { + client?.Dispose(); + } + + this.buckets.Clear(); + } + + this.isDisposed = true; + } + } + + /// + public void Dispose() + { + this.Dispose(true); + + GC.SuppressFinalize(this); + } + private readonly record struct KeyExistsResult(GetObjectMetadataResponse Metadata) { public bool Exists => this.Metadata is not null; diff --git a/src/ImageSharp.Web.Providers.AWS/Providers/AWSS3StorageImageProviderOptions.cs b/src/ImageSharp.Web.Providers.AWS/Providers/AWSS3StorageImageProviderOptions.cs index 00c97a5..187aec7 100644 --- a/src/ImageSharp.Web.Providers.AWS/Providers/AWSS3StorageImageProviderOptions.cs +++ b/src/ImageSharp.Web.Providers.AWS/Providers/AWSS3StorageImageProviderOptions.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using Amazon.S3; - namespace SixLabors.ImageSharp.Web.Providers.AWS; /// @@ -22,7 +20,7 @@ public class AWSS3StorageImageProviderOptions public class AWSS3BucketClientOptions : IAWSS3BucketClientOptions { /// - public Func? S3ClientProvider { get; set; } = null!; + public Func? S3ClientFactory { get; set; } /// public string? Region { get; set; } diff --git a/src/ImageSharp.Web.Providers.Azure/Caching/AzureBlobStorageCache.cs b/src/ImageSharp.Web.Providers.Azure/Caching/AzureBlobStorageCache.cs index 089062d..e49d281 100644 --- a/src/ImageSharp.Web.Providers.Azure/Caching/AzureBlobStorageCache.cs +++ b/src/ImageSharp.Web.Providers.Azure/Caching/AzureBlobStorageCache.cs @@ -28,10 +28,11 @@ public AzureBlobStorageCache(IOptions cacheOptions Guard.NotNull(cacheOptions, nameof(cacheOptions)); AzureBlobStorageCacheOptions options = cacheOptions.Value; - this.container = options.BlobContainerClientProvider == null - ? new BlobContainerClient(options.ConnectionString, options.ContainerName) - : options.BlobContainerClientProvider(options, serviceProvider); - this.cacheFolder = string.IsNullOrEmpty(options.CacheFolder) + this.container = + options.BlobContainerClientFactory?.Invoke(options, serviceProvider) + ?? new BlobContainerClient(options.ConnectionString, options.ContainerName); + + this.cacheFolder = string.IsNullOrWhiteSpace(options.CacheFolder) ? string.Empty : options.CacheFolder.Trim().Trim('/') + '/'; } diff --git a/src/ImageSharp.Web.Providers.Azure/Caching/AzureBlobStorageCacheOptions.cs b/src/ImageSharp.Web.Providers.Azure/Caching/AzureBlobStorageCacheOptions.cs index c74ae30..ab119aa 100644 --- a/src/ImageSharp.Web.Providers.Azure/Caching/AzureBlobStorageCacheOptions.cs +++ b/src/ImageSharp.Web.Providers.Azure/Caching/AzureBlobStorageCacheOptions.cs @@ -11,9 +11,9 @@ namespace SixLabors.ImageSharp.Web.Caching.Azure; public class AzureBlobStorageCacheOptions { /// - /// Gets or sets a custom Azure BlobContainerClient provider + /// Gets or sets a factory method to create an . /// - public Func? BlobContainerClientProvider { get; set; } = null!; + public Func? BlobContainerClientFactory { get; set; } /// /// Gets or sets the Azure Blob Storage connection string. diff --git a/src/ImageSharp.Web.Providers.Azure/Providers/AzureBlobStorageImageProvider.cs b/src/ImageSharp.Web.Providers.Azure/Providers/AzureBlobStorageImageProvider.cs index 41da728..60069a5 100644 --- a/src/ImageSharp.Web.Providers.Azure/Providers/AzureBlobStorageImageProvider.cs +++ b/src/ImageSharp.Web.Providers.Azure/Providers/AzureBlobStorageImageProvider.cs @@ -53,11 +53,11 @@ public AzureBlobStorageImageProvider( foreach (AzureBlobContainerClientOptions container in storageOptions.Value.BlobContainers) { - BlobContainerClient containerClient = container.BlobContainerClientProvider == null - ? new BlobContainerClient(container.ConnectionString, container.ContainerName) - : container.BlobContainerClientProvider(container, serviceProvider); + BlobContainerClient client = + container.BlobContainerClientFactory?.Invoke(container, serviceProvider) + ?? new BlobContainerClient(container.ConnectionString, container.ContainerName); - this.containers.Add(container.ContainerName!, containerClient); + this.containers.Add(client.Name, client); } } @@ -109,7 +109,7 @@ public Func Match } // Blob name should be the remaining path string. - string blobName = path.Substring(containerName.Length).TrimStart(SlashChars); + string blobName = path[containerName.Length..].TrimStart(SlashChars); if (string.IsNullOrWhiteSpace(blobName)) { @@ -132,7 +132,7 @@ public bool IsValidRequest(HttpContext context) private bool IsMatch(HttpContext context) { - // Only match loosly here for performance. + // Only match loosely here for performance. // Path matching conflicts should be dealt with by configuration. string? path = context.Request.Path.Value?.TrimStart(SlashChars); diff --git a/src/ImageSharp.Web.Providers.Azure/Providers/AzureBlobStorageImageProviderOptions.cs b/src/ImageSharp.Web.Providers.Azure/Providers/AzureBlobStorageImageProviderOptions.cs index aa5e316..9b1b8a1 100644 --- a/src/ImageSharp.Web.Providers.Azure/Providers/AzureBlobStorageImageProviderOptions.cs +++ b/src/ImageSharp.Web.Providers.Azure/Providers/AzureBlobStorageImageProviderOptions.cs @@ -22,20 +22,20 @@ public class AzureBlobStorageImageProviderOptions public class AzureBlobContainerClientOptions { /// - /// Gets or sets a custom Azure BlobServiceClient provider + /// Gets or sets a factory method to create an . /// - public Func? BlobContainerClientProvider { get; set; } = null!; + public Func? BlobContainerClientFactory { get; set; } /// /// Gets or sets the Azure Blob Storage connection string. /// /// - public string? ConnectionString { get; set; } + public string ConnectionString { get; set; } = null!; /// /// Gets or sets the Azure Blob Storage container name. - /// Must conform to Azure Blob Storage container naming guidlines. + /// Must conform to Azure Blob Storage container naming guidelines. /// /// - public string? ContainerName { get; set; } + public string ContainerName { get; set; } = null!; } diff --git a/tests/ImageSharp.Web.Tests/TestUtilities/AWSS3StorageCacheCacheFolderTestServerFixture.cs b/tests/ImageSharp.Web.Tests/TestUtilities/AWSS3StorageCacheCacheFolderTestServerFixture.cs index 89c7726..d452ef6 100644 --- a/tests/ImageSharp.Web.Tests/TestUtilities/AWSS3StorageCacheCacheFolderTestServerFixture.cs +++ b/tests/ImageSharp.Web.Tests/TestUtilities/AWSS3StorageCacheCacheFolderTestServerFixture.cs @@ -35,7 +35,7 @@ protected override void ConfigureCustomServices(IServiceCollection services, IIm o.Timeout = TestConstants.AWSTimeout; o.CacheFolder = TestConstants.AWSCacheFolder; - AWSS3StorageCache.CreateIfNotExists(o, S3CannedACL.Private, services.BuildServiceProvider()); + AWSS3StorageCache.CreateIfNotExists(o, S3CannedACL.Private); }) .SetCache(); } diff --git a/tests/ImageSharp.Web.Tests/TestUtilities/AWSS3StorageCacheTestServerFixture.cs b/tests/ImageSharp.Web.Tests/TestUtilities/AWSS3StorageCacheTestServerFixture.cs index c3ac3ac..cb016f2 100644 --- a/tests/ImageSharp.Web.Tests/TestUtilities/AWSS3StorageCacheTestServerFixture.cs +++ b/tests/ImageSharp.Web.Tests/TestUtilities/AWSS3StorageCacheTestServerFixture.cs @@ -34,7 +34,7 @@ protected override void ConfigureCustomServices(IServiceCollection services, IIm o.Region = TestConstants.AWSRegion; o.Timeout = TestConstants.AWSTimeout; - AWSS3StorageCache.CreateIfNotExists(o, S3CannedACL.Private, services.BuildServiceProvider()); + AWSS3StorageCache.CreateIfNotExists(o, S3CannedACL.Private); }) .SetCache(); } diff --git a/tests/ImageSharp.Web.Tests/TestUtilities/AWSS3StorageImageProviderFactory.cs b/tests/ImageSharp.Web.Tests/TestUtilities/AWSS3StorageImageProviderFactory.cs index 297c1cb..58ef0d8 100644 --- a/tests/ImageSharp.Web.Tests/TestUtilities/AWSS3StorageImageProviderFactory.cs +++ b/tests/ImageSharp.Web.Tests/TestUtilities/AWSS3StorageImageProviderFactory.cs @@ -27,8 +27,8 @@ private static async Task InitializeAWSStorageAsync(IServiceProvider services, A { // Upload an image to the AWS Test Storage; AWSS3BucketClientOptions bucketOptions = options.S3Buckets.First(); - AmazonS3Client amazonS3Client = AmazonS3ClientFactory.CreateClient(bucketOptions, services); - ListBucketsResponse listBucketsResponse = await amazonS3Client.ListBucketsAsync(); + using AmazonS3BucketClient amazonS3Client = AmazonS3ClientFactory.CreateClient(bucketOptions); + ListBucketsResponse listBucketsResponse = await amazonS3Client.Client.ListBucketsAsync(); bool foundBucket = false; foreach (S3Bucket b in listBucketsResponse.Buckets) @@ -51,12 +51,12 @@ private static async Task InitializeAWSStorageAsync(IServiceProvider services, A CannedACL = S3CannedACL.PublicRead }; - await amazonS3Client.PutBucketAsync(putBucketRequest); + await amazonS3Client.Client.PutBucketAsync(putBucketRequest); } catch (AmazonS3Exception e) { // CI tests are run in parallel and can sometimes return a - // false negative for the existance of a bucket. + // false negative for the existence of a bucket. if (string.Equals(e.ErrorCode, "BucketAlreadyExists", StringComparison.Ordinal)) { return; @@ -76,7 +76,7 @@ private static async Task InitializeAWSStorageAsync(IServiceProvider services, A Key = TestConstants.ImagePath }; - await amazonS3Client.GetObjectAsync(request); + await amazonS3Client.Client.GetObjectAsync(request); } catch { @@ -105,7 +105,7 @@ private static async Task InitializeAWSStorageAsync(IServiceProvider services, A UseChunkEncoding = false, }; - await amazonS3Client.PutObjectAsync(putRequest); + await amazonS3Client.Client.PutObjectAsync(putRequest); } } } diff --git a/tests/ImageSharp.Web.Tests/TestUtilities/ServerTestBase.cs b/tests/ImageSharp.Web.Tests/TestUtilities/ServerTestBase.cs index 89bb989..3cce43d 100644 --- a/tests/ImageSharp.Web.Tests/TestUtilities/ServerTestBase.cs +++ b/tests/ImageSharp.Web.Tests/TestUtilities/ServerTestBase.cs @@ -23,11 +23,11 @@ protected ServerTestBase(TFixture fixture, ITestOutputHelper outputHelper, strin this.OutputHelper.WriteLine(typeof(TFixture).Name); - //this.OutputHelper.WriteLine("EnvironmentalVariables"); - //foreach (DictionaryEntry item in Environment.GetEnvironmentVariables()) - //{ + // this.OutputHelper.WriteLine("EnvironmentalVariables"); + // foreach (DictionaryEntry item in Environment.GetEnvironmentVariables()) + // { // this.OutputHelper.WriteLine($"Key = {item.Key}, Value = {item.Value}"); - //} + // } } public TFixture Fixture { get; }