Skip to content

Commit

Permalink
Feature/more ways to download (#1)
Browse files Browse the repository at this point in the history
* Add new endpoint to send file as DataResponse with string content

* add test and edit readme
  • Loading branch information
Gramli authored Jun 18, 2024
1 parent ebdf1ae commit b4a0dc9
Show file tree
Hide file tree
Showing 12 changed files with 90 additions and 12 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ REST API solution demonstrates how to create clean and modern API with Clean Arc

Example API allows to **upload/download files with .txt, .json, .csv, .xml, .yml extensions** and get collection of uploaded files. Also allow to **convert between json, xml, yml formats**.

Two methods for downloading files are demonstrated:
* Using the **GetFileAsync** extension method, which returns the file as a byte array.
* Using the **GetJsonFileAsync** method, which returns the file inside a JSON object. The file is converted to a Base64 string to preserve encoding.

# Menu
* [Get Started](#get-started)
* [Motivation](#motivation)
Expand Down
9 changes: 9 additions & 0 deletions src/File.API/EndpointBuilders/FileEndpointsBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,15 @@ await handler.GetFileAsync(new DownloadFileQuery(id), cancellationToken))
.Produces<FileContentHttpResult>()
.WithName("DownloadFile")
.WithTags("Get");

endpointRouteBuilder.MapGet("v1/downloadAsJson",
async (int id, [FromServices] IDownloadFileQueryHandler handler, CancellationToken cancellationToken) =>
await handler.GetJsonFileAsync(new DownloadFileQuery(id), cancellationToken))
.DisableAntiforgery()
.Produces<DataResponse<StringContentFileDto>>()
.WithName("DownloadFileAsJson")
.WithTags("Get");

return endpointRouteBuilder;
}

Expand Down
22 changes: 22 additions & 0 deletions src/File.API/Extensions/IHandlerExtension.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using File.Core.Abstractions;
using File.Domain.Dtos;
using File.Domain.Http;
using System.Buffers.Text;

namespace File.API.Extensions
{
Expand All @@ -20,5 +22,25 @@ internal static async Task<IResult> GetFileAsync<TResponse, TRequest>(this IRequ
}
return Results.NotFound();
}

internal static async Task<IResult> GetJsonFileAsync<TResponse, TRequest>(this IRequestHandler<TResponse, TRequest> requestHandler, TRequest request, CancellationToken cancellationToken) where TResponse : FileDto
{
var response = await requestHandler.HandleAsync(request, cancellationToken);
if (response.Data is not null)
{
return Results.Json(new DataResponse<StringContentFileDto>
{
Errors = response.Errors,
Data = new StringContentFileDto
{
Data = Convert.ToBase64String(response.Data.Data),
ContentType = response.Data.ContentType,
FileName = response.Data.FileName,
Length = response.Data.Length
},
}, statusCode: (int)response.StatusCode);
}
return Results.NotFound();
}
}
}
2 changes: 1 addition & 1 deletion src/File.Domain/Commands/AddFilesCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ namespace File.Domain.Commands
{
public sealed class AddFilesCommand
{
public IEnumerable<IFile> Files { get; init; } = Enumerable.Empty<IFile>();
public IEnumerable<IFile> Files { get; init; } = [];

public AddFilesCommand(IEnumerable<IFile> files)
{
Expand Down
11 changes: 11 additions & 0 deletions src/File.Domain/Dtos/BaseFileDto.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace File.Domain.Dtos
{
public abstract class BaseFileDto
{
public string FileName { get; init; } = string.Empty;

public string ContentType { get; init; } = string.Empty;

public long Length { get; init; }
}
}
8 changes: 1 addition & 7 deletions src/File.Domain/Dtos/FileDto.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,7 @@
namespace File.Domain.Dtos
{
public class FileDto
public class FileDto : BaseFileDto
{
public string FileName { get; init; } = string.Empty;

public string ContentType { get; init; } = string.Empty;

public long Length { get; init; }

public byte[] Data { get; init; } = [];
}
}
7 changes: 7 additions & 0 deletions src/File.Domain/Dtos/StringContentFileDto.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace File.Domain.Dtos
{
public sealed class StringContentFileDto : BaseFileDto
{
public string Data { get; init; } = string.Empty;
}
}
2 changes: 1 addition & 1 deletion src/File.Domain/Http/DataResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ public class DataResponse<T>
{
public T? Data { get; init; }

public IList<string> Errors { get; init; } = new List<string>();
public IList<string> Errors { get; init; } = [];
}
}
2 changes: 1 addition & 1 deletion src/File.Domain/Options/AllowedFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ public sealed class AllowedFile

public string ContentType { get; set; } = string.Empty;

public string[] CanBeExportedTo { get; set; } = new string[0];
public string[] CanBeExportedTo { get; set; } = [];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ internal sealed class FileEntity
public int Id { get; set; }
public string FileName { get; init; } = string.Empty;
public string ContentType { get; init; } = string.Empty;
public byte[] Data { get; set; } = new byte[0];
public byte[] Data { get; set; } = [];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,13 @@ public async Task<Result<FileDto>> GetFile(DownloadFileQuery downloadFileQuery,
return Result.Fail(string.Format(ErrorMessages.FileNotExists, downloadFileQuery.Id));
}

return Result.Ok(file.Adapt<FileDto>());
return Result.Ok(new FileDto
{
ContentType = file.ContentType,
Data = file.Data,
FileName = file.FileName,
Length = file.Data.Length,
});
}

public async Task<IEnumerable<FileInfoDto>> GetFilesInfo(CancellationToken cancellationToken)
Expand Down
25 changes: 25 additions & 0 deletions src/Tests/SystemTests/File.API.SystemTests/Tests/DownloadTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using File.API.SystemTests.Extensions;
using File.Domain.Dtos;
using File.Domain.Http;
using System.Net.Http.Json;

namespace File.API.SystemTests.Tests
{
Expand Down Expand Up @@ -29,5 +30,29 @@ public async Task DownloadFileAsync()
//Assert
Assert.True(stream.Length > 0);
}

[Fact]
public async Task DownloadJsonFileAsync()
{
//Arrange
using var uploadResponse = (await _httpClient.UploadAssetsFile("new.json"))
.EnsureSuccessStatusCode();

using var fileInfo = await _httpClient.GetAsync("file/v1/files-info");
var fileToDownload = (await fileInfo.GetResponseData<DataResponse<IEnumerable<FileInfoDto>>>())?.Data?.First();

if (fileToDownload is null)
{
Assert.Fail("Downloaded file is empty.");
}

//Act
using var response = await _httpClient.GetAsync($"file/v1/downloadAsJson/?id={fileToDownload.Id}");
var responseFile = await response.Content.ReadFromJsonAsync<DataResponse<StringContentFileDto>>();

//Assert
Assert.True(responseFile!.Data!.Data.Length > 0);
Assert.NotEmpty(responseFile!.Data!.FileName);
}
}
}

0 comments on commit b4a0dc9

Please sign in to comment.