Skip to content

Commit

Permalink
feat: return contentresult with stream and filename for content download
Browse files Browse the repository at this point in the history
  • Loading branch information
Dovchik committed May 22, 2024
1 parent 4eac7a3 commit 8f99e78
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 38 deletions.
14 changes: 8 additions & 6 deletions examples/Console/Fax/DownloadFax.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
}
29 changes: 29 additions & 0 deletions src/Sinch/Core/ContentResult.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using System;
using System.IO;
using System.Threading.Tasks;

namespace Sinch.Core
{
public sealed class ContentResult : IDisposable, IAsyncDisposable
{
/// <summary>
/// The Stream containing data of the file
/// </summary>
public Stream Stream { get; init; } = null!;

/// <summary>
/// Name of the file, if available.
/// </summary>
public string? FileName { get; init; }

public void Dispose()
{
Stream.Dispose();
}

public async ValueTask DisposeAsync()
{
await Stream.DisposeAsync();
}
}
}
12 changes: 9 additions & 3 deletions src/Sinch/Core/Http.cs
Original file line number Diff line number Diff line change
Expand Up @@ -263,13 +263,19 @@ private async Task<TResponse> SendHttpContent<TResponse>(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())
Expand Down
58 changes: 29 additions & 29 deletions src/Sinch/Fax/Faxes/FaxesClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ public interface ISinchFaxFaxes
/// <param name="cancellationToken"></param>
/// <returns></returns>
public Task<Fax> Send(string to, SendFaxRequest request, CancellationToken cancellationToken = default);


/// <summary>
/// Create and send a fax to multiple receivers.<br/><br/>
/// Fax content may be supplied via one or more files or URLs of supported filetypes.<br/><br/>
Expand All @@ -43,15 +43,15 @@ public interface ISinchFaxFaxes
/// <returns></returns>
public Task<List<Fax>> Send(List<string> to, SendFaxRequest request,
CancellationToken cancellationToken = default);

/// <summary>
/// List faxes sent (OUTBOUND) or received (INBOUND), set parameters to filter the list.
/// </summary>
/// <param name="listFaxesRequest"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<ListFaxResponse> List(ListFaxesRequest listFaxesRequest, CancellationToken cancellationToken = default);

/// <summary>
/// Automatically List faxes sent (OUTBOUND) or received (INBOUND), set parameters to filter the list.
/// </summary>
Expand All @@ -60,57 +60,57 @@ public Task<List<Fax>> Send(List<string> to, SendFaxRequest request,
/// <returns></returns>
IAsyncEnumerable<Fax> ListAuto(ListFaxesRequest listFaxesRequest,
CancellationToken cancellationToken = default);

/// <summary>
/// Get fax information using the ID number of the fax.
/// </summary>
/// <param name="id">The ID of the fax.</param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<Fax> Get(string id, CancellationToken cancellationToken = default);

/// <summary>
/// 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.
/// </summary>
/// <param name="id">The ID of the fax.</param>
/// <param name="cancellationToken"></param>
/// <returns>Successful task if response is 204.</returns>
Task DeleteContent(string id, CancellationToken cancellationToken = default);

/// <summary>
/// Download the fax content. Currently, supports only pdf.
/// </summary>
/// <param name="id"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<Stream> DownloadContent(string id, CancellationToken cancellationToken = default);
Task<ContentResult> DownloadContent(string id, CancellationToken cancellationToken = default);
}

internal sealed class FaxesClient : ISinchFaxFaxes
{
private readonly Uri _uri;
private readonly IHttp _http;
private readonly ILoggerAdapter<ISinchFaxFaxes>? _loggerAdapter;

internal FaxesClient(string projectId, Uri uri, ILoggerAdapter<ISinchFaxFaxes>? loggerAdapter, IHttp httpClient)
{
_loggerAdapter = loggerAdapter;
_http = httpClient;
_uri = new Uri(uri, $"/v3/projects/{projectId}/faxes");
}

/// <inheritdoc />
public async Task<Fax> Send(string to, SendFaxRequest request, CancellationToken cancellationToken = default)
{
if (string.IsNullOrEmpty(to))
{
throw new ArgumentNullException(nameof(to), "Should have a value");
}

var faxes = await Send(new List<string>() { to }, request, cancellationToken);
return faxes.First();
}

/// <inheritdoc />
// the fax will return a PLAIN fax if there is ONE TO number, but an array if there is > 1
public async Task<List<Fax>> Send(List<string> to, SendFaxRequest request,
Expand All @@ -125,12 +125,12 @@ public async Task<List<Fax>> Send(List<string> to, SendFaxRequest request,
return await _http.SendMultipart<SendFaxRequest, List<Fax>>(_uri, request, request.FileContent,
request.FileName!, cancellationToken: cancellationToken);
}

var fax = await _http.SendMultipart<SendFaxRequest, Fax>(_uri, request, request.FileContent,
request.FileName!, cancellationToken: cancellationToken);
return new List<Fax>() { fax };
}

if (request.ContentUrl?.Any() == true)
{
_loggerAdapter?.LogInformation("Sending fax with content urls...");
Expand All @@ -139,16 +139,16 @@ public async Task<List<Fax>> Send(List<string> to, SendFaxRequest request,
return await _http.Send<SendFaxRequest, List<Fax>>(_uri, HttpMethod.Post, request,
cancellationToken: cancellationToken);
}

var fax = await _http.Send<SendFaxRequest, Fax>(_uri, HttpMethod.Post, request,
cancellationToken: cancellationToken);
return new List<Fax>() { fax };
}

throw new InvalidOperationException(
"Neither content urls or file content provided for a create fax request.");
}

/// <inheritdoc />
public async Task<ListFaxResponse> List(ListFaxesRequest listFaxesRequest,
CancellationToken cancellationToken = default)
Expand All @@ -158,16 +158,16 @@ public async Task<ListFaxResponse> List(ListFaxesRequest listFaxesRequest,
{
Query = listFaxesRequest.ToQueryString()
};

return await _http.Send<ListFaxResponse>(uriBuilder.Uri, HttpMethod.Get, cancellationToken);
}

/// <inheritdoc />
public async IAsyncEnumerable<Fax> 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))
{
Expand All @@ -178,47 +178,47 @@ public async IAsyncEnumerable<Fax> ListAuto(ListFaxesRequest listFaxesRequest,
response = await List(listFaxesRequest, cancellationToken);
}
}

/// <inheritdoc />
public Task<Fax> Get(string id, CancellationToken cancellationToken = default)
{
if (string.IsNullOrEmpty(id))
{
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<Fax>(uriBuilder.Uri, HttpMethod.Get, cancellationToken);
}

/// <inheritdoc />
public Task DeleteContent(string id, CancellationToken cancellationToken = default)
{
if (string.IsNullOrEmpty(id))
{
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<EmptyResponse>(uriBuilder.Uri, HttpMethod.Delete, cancellationToken);
}

/// <inheritdoc />
public Task<Stream> DownloadContent(string id, CancellationToken cancellationToken = default)
public Task<ContentResult> 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<Stream>(uriBuilder.Uri, HttpMethod.Get, cancellationToken);
return _http.Send<ContentResult>(uriBuilder.Uri, HttpMethod.Get, cancellationToken);
}
}
}

0 comments on commit 8f99e78

Please sign in to comment.