From 8f99e789e091b948c6369089c5862b939f77cb04 Mon Sep 17 00:00:00 2001 From: Volodymyr Lisovskyi Date: Wed, 22 May 2024 14:17:58 +0200 Subject: [PATCH] feat: return contentresult with stream and filename for content download --- examples/Console/Fax/DownloadFax.cs | 14 ++++--- src/Sinch/Core/ContentResult.cs | 29 +++++++++++++++ src/Sinch/Core/Http.cs | 12 ++++-- src/Sinch/Fax/Faxes/FaxesClient.cs | 58 ++++++++++++++--------------- 4 files changed, 75 insertions(+), 38 deletions(-) create mode 100644 src/Sinch/Core/ContentResult.cs diff --git a/examples/Console/Fax/DownloadFax.cs b/examples/Console/Fax/DownloadFax.cs index 0a8a0b1..7b7e754 100644 --- a/examples/Console/Fax/DownloadFax.cs +++ b/examples/Console/Fax/DownloadFax.cs @@ -7,17 +7,19 @@ public class DownloadFax public static async Task Example() { var sinchClient = new SinchClient("PROJECT_ID", "KEY_ID", "KEY_SECRET"); - var faxId = "FAX_ID"; - await using var responseStream = await sinchClient.Fax.Faxes.DownloadContent("faxId"); + const string faxId = "FAX_ID"; - if (!Path.Exists("C:\\Downloads\\")) + await using var contentResult = await sinchClient.Fax.Faxes.DownloadContent("faxId"); + const string directory = @"C:\Downloads\"; + if (!Path.Exists(directory)) { - Directory.CreateDirectory("C:\\Downloads\\"); + Directory.CreateDirectory(directory); } await using var fileStream = - new FileStream($"C:\\Downloads\\{faxId}.pdf", FileMode.Create, FileAccess.Write); - await responseStream.CopyToAsync(fileStream); + new FileStream(Path.Combine(directory, contentResult.FileName ?? $"{faxId}.pdf"), FileMode.Create, + FileAccess.Write); + await contentResult.Stream.CopyToAsync(fileStream); } } } diff --git a/src/Sinch/Core/ContentResult.cs b/src/Sinch/Core/ContentResult.cs new file mode 100644 index 0000000..ded668b --- /dev/null +++ b/src/Sinch/Core/ContentResult.cs @@ -0,0 +1,29 @@ +using System; +using System.IO; +using System.Threading.Tasks; + +namespace Sinch.Core +{ + public sealed class ContentResult : IDisposable, IAsyncDisposable + { + /// + /// The Stream containing data of the file + /// + public Stream Stream { get; init; } = null!; + + /// + /// Name of the file, if available. + /// + public string? FileName { get; init; } + + public void Dispose() + { + Stream.Dispose(); + } + + public async ValueTask DisposeAsync() + { + await Stream.DisposeAsync(); + } + } +} diff --git a/src/Sinch/Core/Http.cs b/src/Sinch/Core/Http.cs index 2ac9d1d..1ccf23e 100644 --- a/src/Sinch/Core/Http.cs +++ b/src/Sinch/Core/Http.cs @@ -263,13 +263,19 @@ private async Task SendHttpContent(Uri uri, HttpMethod htt // NOTE: there wil probably be other files supported in the future if (result.IsPdf()) { - if (typeof(TResponse) != typeof(Stream)) + if (typeof(TResponse) != typeof(ContentResult)) { throw new InvalidOperationException( - "Received pdf, but expected response type is not a Stream."); + $"Received pdf, but expected response type is not a {nameof(ContentResult)}."); } - return (TResponse)(object)await result.Content.ReadAsStreamAsync(cancellationToken); + // yes, the header currently returns double quotes ""IFOFJSLJ12313.pdf"" + var fileName = result.Content.Headers.ContentDisposition?.FileName?.Trim('"'); + return (TResponse)(object)new ContentResult() + { + Stream = await result.Content.ReadAsStreamAsync(cancellationToken), + FileName = fileName + }; } if (result.IsJson()) diff --git a/src/Sinch/Fax/Faxes/FaxesClient.cs b/src/Sinch/Fax/Faxes/FaxesClient.cs index d13fc32..92e81a4 100644 --- a/src/Sinch/Fax/Faxes/FaxesClient.cs +++ b/src/Sinch/Fax/Faxes/FaxesClient.cs @@ -29,8 +29,8 @@ public interface ISinchFaxFaxes /// /// public Task Send(string to, SendFaxRequest request, CancellationToken cancellationToken = default); - - + + /// /// Create and send a fax to multiple receivers.

/// Fax content may be supplied via one or more files or URLs of supported filetypes.

@@ -43,7 +43,7 @@ public interface ISinchFaxFaxes /// public Task> Send(List to, SendFaxRequest request, CancellationToken cancellationToken = default); - + /// /// List faxes sent (OUTBOUND) or received (INBOUND), set parameters to filter the list. /// @@ -51,7 +51,7 @@ public Task> Send(List to, SendFaxRequest request, /// /// Task List(ListFaxesRequest listFaxesRequest, CancellationToken cancellationToken = default); - + /// /// Automatically List faxes sent (OUTBOUND) or received (INBOUND), set parameters to filter the list. /// @@ -60,7 +60,7 @@ public Task> Send(List to, SendFaxRequest request, /// IAsyncEnumerable ListAuto(ListFaxesRequest listFaxesRequest, CancellationToken cancellationToken = default); - + /// /// Get fax information using the ID number of the fax. /// @@ -68,7 +68,7 @@ IAsyncEnumerable ListAuto(ListFaxesRequest listFaxesRequest, /// /// Task Get(string id, CancellationToken cancellationToken = default); - + /// /// Delete the fax content for a fax using the ID number of the fax. Please note that this only deletes the content of the fax from storage. /// @@ -76,29 +76,29 @@ IAsyncEnumerable ListAuto(ListFaxesRequest listFaxesRequest, /// /// Successful task if response is 204. Task DeleteContent(string id, CancellationToken cancellationToken = default); - + /// /// Download the fax content. Currently, supports only pdf. /// /// /// /// - Task DownloadContent(string id, CancellationToken cancellationToken = default); + Task DownloadContent(string id, CancellationToken cancellationToken = default); } - + internal sealed class FaxesClient : ISinchFaxFaxes { private readonly Uri _uri; private readonly IHttp _http; private readonly ILoggerAdapter? _loggerAdapter; - + internal FaxesClient(string projectId, Uri uri, ILoggerAdapter? loggerAdapter, IHttp httpClient) { _loggerAdapter = loggerAdapter; _http = httpClient; _uri = new Uri(uri, $"/v3/projects/{projectId}/faxes"); } - + /// public async Task Send(string to, SendFaxRequest request, CancellationToken cancellationToken = default) { @@ -106,11 +106,11 @@ public async Task Send(string to, SendFaxRequest request, CancellationToken { throw new ArgumentNullException(nameof(to), "Should have a value"); } - + var faxes = await Send(new List() { to }, request, cancellationToken); return faxes.First(); } - + /// // the fax will return a PLAIN fax if there is ONE TO number, but an array if there is > 1 public async Task> Send(List to, SendFaxRequest request, @@ -125,12 +125,12 @@ public async Task> Send(List to, SendFaxRequest request, return await _http.SendMultipart>(_uri, request, request.FileContent, request.FileName!, cancellationToken: cancellationToken); } - + var fax = await _http.SendMultipart(_uri, request, request.FileContent, request.FileName!, cancellationToken: cancellationToken); return new List() { fax }; } - + if (request.ContentUrl?.Any() == true) { _loggerAdapter?.LogInformation("Sending fax with content urls..."); @@ -139,16 +139,16 @@ public async Task> Send(List to, SendFaxRequest request, return await _http.Send>(_uri, HttpMethod.Post, request, cancellationToken: cancellationToken); } - + var fax = await _http.Send(_uri, HttpMethod.Post, request, cancellationToken: cancellationToken); return new List() { fax }; } - + throw new InvalidOperationException( "Neither content urls or file content provided for a create fax request."); } - + /// public async Task List(ListFaxesRequest listFaxesRequest, CancellationToken cancellationToken = default) @@ -158,16 +158,16 @@ public async Task List(ListFaxesRequest listFaxesRequest, { Query = listFaxesRequest.ToQueryString() }; - + return await _http.Send(uriBuilder.Uri, HttpMethod.Get, cancellationToken); } - + /// public async IAsyncEnumerable ListAuto(ListFaxesRequest listFaxesRequest, [EnumeratorCancellation] CancellationToken cancellationToken = default) { _loggerAdapter?.LogDebug("Auto Listing faxes"); - + var response = await List(listFaxesRequest, cancellationToken); while (!Utils.IsLastPage(response.PageNumber, response.PageSize, response.TotalItems, PageStart.One)) { @@ -178,7 +178,7 @@ public async IAsyncEnumerable ListAuto(ListFaxesRequest listFaxesRequest, response = await List(listFaxesRequest, cancellationToken); } } - + /// public Task Get(string id, CancellationToken cancellationToken = default) { @@ -186,13 +186,13 @@ public Task Get(string id, CancellationToken cancellationToken = default) { throw new ArgumentNullException(nameof(id), "Fax id should have a value."); } - + _loggerAdapter?.LogInformation("Getting the fax with {id}", id); var uriBuilder = new UriBuilder(_uri); uriBuilder.Path += "/" + id; return _http.Send(uriBuilder.Uri, HttpMethod.Get, cancellationToken); } - + /// public Task DeleteContent(string id, CancellationToken cancellationToken = default) { @@ -200,25 +200,25 @@ public Task DeleteContent(string id, CancellationToken cancellationToken = defau { throw new ArgumentNullException(nameof(id), "Fax id should have a value."); } - + _loggerAdapter?.LogInformation("Deleting the content of the fax with {id}", id); var uriBuilder = new UriBuilder(_uri); uriBuilder.Path += $"/{id}/file"; return _http.Send(uriBuilder.Uri, HttpMethod.Delete, cancellationToken); } - + /// - public Task DownloadContent(string id, CancellationToken cancellationToken = default) + public Task DownloadContent(string id, CancellationToken cancellationToken = default) { if (string.IsNullOrEmpty(id)) { throw new ArgumentNullException(nameof(id), "Fax id should have a value."); } - + _loggerAdapter?.LogInformation("Downloading the content of the fax with {id}", id); var uriBuilder = new UriBuilder(_uri); uriBuilder.Path += $"/{id}/file.pdf"; // only pdf is supported for now - return _http.Send(uriBuilder.Uri, HttpMethod.Get, cancellationToken); + return _http.Send(uriBuilder.Uri, HttpMethod.Get, cancellationToken); } } }