Skip to content

Commit

Permalink
SIANXSVC-1193: dotnet-e5e does not handle binary requests as expected (
Browse files Browse the repository at this point in the history
…#5)

* SIANXSVC-1193: dotnet-e5e does not handle binary requests as expected
* SIANXSVC-1193: Add example code to the InlineHandler

Closes SIANXSVC-1193
  • Loading branch information
nachtjasmin authored Feb 1, 2024
1 parent c1c785e commit 07038d8
Show file tree
Hide file tree
Showing 16 changed files with 422 additions and 39 deletions.
12 changes: 12 additions & 0 deletions examples/InlineHandler/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,18 @@ await Host.CreateDefaultBuilder(args)
var res = E5EResponse.From("test");
return Task.FromResult(res);
});
builder.RegisterEntrypoint("Binary", request =>
{
// This entrypoint receives one file (type: binary) and returns an anonymous object with the length.
var fileData = request.Event.AsBytes();
return Task.FromResult(E5EResponse.From(new { FileLength = fileData?.LongLength }));
});
builder.RegisterEntrypoint("ReturnFirstFile", request =>
{
// This entrypoint receives multiple files as a mixed request and returns the first.
var files = request.Event.AsFiles();
return Task.FromResult(E5EResponse.From(files.First()));
});
})
.UseConsoleLifetime()
.Build()
Expand Down
10 changes: 10 additions & 0 deletions src/Anexia.E5E.Tests/Anexia.E5E.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<TargetFrameworks>net6.0;net8.0</TargetFrameworks>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<LangVersion>12</LangVersion>
</PropertyGroup>

<ItemGroup>
Expand All @@ -27,4 +28,13 @@
<ProjectReference Include="..\Anexia.E5E\Anexia.E5E.csproj"/>
</ItemGroup>

<ItemGroup>
<None Update="TestData\binary_request_with_multiple_files.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="TestData\binary_request_unknown_content_type.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>

</Project>
107 changes: 107 additions & 0 deletions src/Anexia.E5E.Tests/Integration/BinaryRequestIntegrationTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
using System.Threading.Tasks;

using Anexia.E5E.Exceptions;
using Anexia.E5E.Functions;
using Anexia.E5E.Tests.TestHelpers;

using static Anexia.E5E.Tests.TestData.TestData;

using Xunit;
using Xunit.Abstractions;

namespace Anexia.E5E.Tests.Integration;

public sealed class BinaryRequestIntegrationTests(ITestOutputHelper outputHelper) : IntegrationTestBase(outputHelper)
{
[Fact]
public async Task DecodeToBytesThrowsForMixedRequest()
{
await Host.StartWithTestEntrypointAsync(request =>
{
Assert.Throws<E5EInvalidConversionException>(() => request.Event.AsBytes());
return null!;
});
await Host.WriteOnceAsync(BinaryRequestWithMultipleFiles);
}

[Fact]
public async Task MultipleFilesAreProperlyDecoded()
{
await Host.StartWithTestEntrypointAsync(request =>
{
var content = "Hello world!"u8.ToArray();
var files = request.Event.AsFiles();
Assert.Collection(files, first =>
{
Assert.NotNull(first);
Assert.Equivalent(first, new E5EFileData(content)
{
FileSizeInBytes = 12,
Filename = "my-file-1.name",
ContentType = "application/my-content-type-1",
Charset = "utf-8",
});
}, second =>
{
Assert.NotNull(second);
Assert.Equivalent(second, new E5EFileData(content)
{
FileSizeInBytes = 12,
Filename = "my-file-2.name",
ContentType = "application/my-content-type-2",
Charset = "utf-8",
});
});
return null!;
});
await Host.WriteOnceAsync(BinaryRequestWithMultipleFiles);
}

[Fact]
public async Task UnknownContentType()
{
await Host.StartWithTestEntrypointAsync(request =>
{
Assert.Equal("Hello world!"u8.ToArray(), request.Event.AsBytes());
return null!;
});
await Host.WriteOnceAsync(BinaryRequestWithUnknownContentType);
}

[Fact]
public async Task FallbackForByteArrayReturnsValidResponse()
{
// act
await Host.StartWithTestEntrypointAsync(_ => E5EResponse.From("Hello world!"u8.ToArray()));
var response = await Host.WriteOnceAsync(x => x.WithData("test"));

// assert
const string expected =
"""
{"data":{"binary":"SGVsbG8gd29ybGQh","type":"binary","size":0,"name":"dotnet-e5e-binary-response.blob","content_type":"application/octet-stream","charset":"utf-8"},"type":"binary"}
""";
Assert.Contains(expected, response.Stdout);
}

[Fact]
public async Task FileDataReturnsValidResponse()
{
// act
await Host.StartWithTestEntrypointAsync(_ => E5EResponse.From(new E5EFileData("Hello world!"u8.ToArray())
{
Type = "binary",
FileSizeInBytes = 16,
Filename = "hello-world.txt",
ContentType = "text/plain",
Charset = "utf-8",
}));
var response = await Host.WriteOnceAsync(x => x.WithData("test"));

// assert
const string expected =
"""
{"data":{"binary":"SGVsbG8gd29ybGQh","type":"binary","size":16,"name":"hello-world.txt","content_type":"text/plain","charset":"utf-8"},"type":"binary"}
""";
Assert.Contains(expected, response.Stdout);
}
}
11 changes: 8 additions & 3 deletions src/Anexia.E5E.Tests/Integration/DocumentationTests.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;

using Anexia.E5E.Functions;
Expand Down Expand Up @@ -42,7 +43,11 @@ await Host.StartWithTestEntrypointAsync(request =>
var data = request.Event.As<SumData>()!;
return E5EResponse.From(data.A + data.B);
});
var response = await Host.WriteRequestOnceAsync(x => x.WithData(new SumData { A = 3, B = 2 }));
var response = await Host.WriteRequestOnceAsync(x => x.WithData(new SumData
{
A = 3,
B = 2,
}));
Assert.Equal(5, response.As<int>());
}

Expand All @@ -55,8 +60,8 @@ await Host.StartWithTestEntrypointAsync(request =>
var resp = Encoding.UTF8.GetBytes($"Hello {name}");
return E5EResponse.From(resp);
});
var response = await Host.WriteRequestOnceAsync(x => x.WithData(Encoding.UTF8.GetBytes("Luna")));
Assert.Equal("\"SGVsbG8gTHVuYQ==\"", response.Data.GetRawText());
var response = await Host.WriteRequestOnceAsync(x => x.WithData(new E5EFileData("Luna"u8.ToArray())));
Assert.Equal("Hello Luna"u8.ToArray(), response.Data.Deserialize<E5EFileData>()!.Bytes);
Assert.Equal(E5EResponseType.Binary, response.Type);
}

Expand Down
25 changes: 13 additions & 12 deletions src/Anexia.E5E.Tests/Serialization/SerializationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;

Expand Down Expand Up @@ -98,8 +97,9 @@ public void ResponseSerializationWorksBidirectional(string _, E5EResponse input)
public void ResponseSerializationRecognisesCorrectType()
{
Assert.Equal(E5EResponseType.Text, E5EResponse.From("test").Type);
Assert.Equal(E5EResponseType.Binary, E5EResponse.From(Encoding.UTF8.GetBytes("test")).Type);
Assert.Equal(E5EResponseType.Binary, E5EResponse.From(Encoding.UTF8.GetBytes("test").AsEnumerable()).Type);
Assert.Equal(E5EResponseType.Binary, E5EResponse.From("test"u8.ToArray()).Type);
Assert.Equal(E5EResponseType.Binary, E5EResponse.From("test"u8.ToArray().AsEnumerable()).Type);
Assert.Equal(E5EResponseType.Binary, E5EResponse.From(new E5EFileData("something"u8.ToArray())).Type);
Assert.Equal(E5EResponseType.StructuredObject, E5EResponse.From(new E5ERuntimeMetadata()).Type);
}

Expand Down Expand Up @@ -159,6 +159,7 @@ private class SerializationTestsData : IEnumerable<object[]>
new E5EContext("generic", DateTimeOffset.FromUnixTimeSeconds(0), true),
new E5ERequestParameters(),
new E5ERuntimeMetadata(),
new E5EFileData("data"u8.ToArray()),
};

private IEnumerable<object[]> Data => _objects.Select(obj => new[] { obj });
Expand All @@ -179,11 +180,8 @@ private class RequestSerializationTestsData : IEnumerable<object[]>
private readonly Dictionary<string, E5EEvent> _tests = new()
{
{ "simple text request", new TestRequestBuilder().WithData("test").BuildEvent() },
{ "simple binary request", new TestRequestBuilder().WithData(Encoding.UTF8.GetBytes("test")).BuildEvent() },
{
"simple object request",
new TestRequestBuilder().WithData(new Dictionary<string, string> { { "test", "value" } }).BuildEvent()
},
{ "simple binary request", new TestRequestBuilder().WithData(new E5EFileData("hello"u8.ToArray())).BuildEvent() },
{ "simple object request", new TestRequestBuilder().WithData(new Dictionary<string, string> { { "test", "value" } }).BuildEvent() },
{
"request with headers and parameters", new TestRequestBuilder().WithData("test")
.AddParam("param", "value")
Expand All @@ -210,12 +208,15 @@ private class ResponseSerializationTestsData : IEnumerable<object[]>
private readonly Dictionary<string, E5EResponse> _tests = new()
{
{ "simple text response", E5EResponse.From("test") },
{ "simple binary response", E5EResponse.From(Encoding.UTF8.GetBytes("test")) },
{ "simple object response", E5EResponse.From(new Dictionary<string, int> { { "a", 1 }, { "b", 2 } }) },
{ "simple binary response", E5EResponse.From("hello"u8.ToArray()) },
{
"text response with headers and status code", E5EResponse.From("test", HttpStatusCode.Moved,
new E5EHttpHeaders { { "Location", "https://example.com" } })
"simple object response", E5EResponse.From(new Dictionary<string, int>
{
{ "a", 1 },
{ "b", 2 },
})
},
{ "text response with headers and status code", E5EResponse.From("test", HttpStatusCode.Moved, new E5EHttpHeaders { { "Location", "https://example.com" } }) },
};

private IEnumerable<object[]> Data => _tests.Select(obj => new object[] { obj.Key, obj.Value });
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"type":"binary","data":"dGVzdA=="}
{"type":"binary","data":{"binary":"aGVsbG8=","type":"binary","size":0,"name":null,"content_type":null,"charset":"utf-8"}}
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"data":"dGVzdA==","type":"binary"}
{"data":{"binary":"aGVsbG8=","type":"binary","size":0,"name":"dotnet-e5e-binary-response.blob","content_type":"application/octet-stream","charset":"utf-8"},"type":"binary"}
11 changes: 11 additions & 0 deletions src/Anexia.E5E.Tests/TestData/TestData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System.IO;

namespace Anexia.E5E.Tests.TestData;

internal static class TestData
{
private static string ReadTestDataFile(string path) => File.ReadAllText(Path.Combine(Directory.GetCurrentDirectory(), "TestData", path));

internal static string BinaryRequestWithMultipleFiles => ReadTestDataFile("binary_request_with_multiple_files.json");
internal static string BinaryRequestWithUnknownContentType => ReadTestDataFile("binary_request_unknown_content_type.json");
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"context": {
"type": "integration-test",
"async": true,
"date": "2024-01-01T00:00:00Z"
},
"event": {
"type": "binary",
"data": {
"binary": "SGVsbG8gd29ybGQh",
"type": "binary",
"name": "my-file-1.name",
"size": 12,
"content_type": "application/my-content-type-1",
"charset": "utf-8"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"context": {
"type": "integration-test",
"async": true,
"date": "2024-01-01T00:00:00Z"
},
"event": {
"type": "mixed",
"data": [
{
"binary": "SGVsbG8gd29ybGQh",
"type": "binary",
"name": "my-file-1.name",
"size": 12,
"content_type": "application/my-content-type-1",
"charset": "utf-8"
},
{
"binary": "SGVsbG8gd29ybGQh",
"type": "binary",
"name": "my-file-2.name",
"size": 12,
"content_type": "application/my-content-type-2",
"charset": "utf-8"
}
]
}
}
5 changes: 4 additions & 1 deletion src/Anexia.E5E.Tests/TestHelpers/TestRequestBuilder.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using System.Text.Json;

Expand All @@ -17,7 +18,9 @@ public TestRequestBuilder WithData<T>(T data)
_requestType = data switch
{
string => E5ERequestDataType.Text,
IEnumerable<byte> => E5ERequestDataType.Binary,
IEnumerable<byte> => throw new InvalidOperationException(
$"E5E does not compose binary requests just from the bytes. Please convert this call to use {nameof(E5EFileData)} instead."),
E5EFileData => E5ERequestDataType.Binary,
_ => E5ERequestDataType.StructuredObject,
};
_data = JsonSerializer.SerializeToElement(data);
Expand Down
24 changes: 17 additions & 7 deletions src/Anexia.E5E/Exceptions/E5EInvalidConversionException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,36 @@ namespace Anexia.E5E.Exceptions;
[SuppressMessage("ReSharper", "UnusedAutoPropertyAccessor.Global")]
public class E5EInvalidConversionException : E5EException
{
private E5EInvalidConversionException(E5ERequestDataType expected, E5ERequestDataType actual)
: base($"Cannot convert data of type {actual} into the type {expected}")
private E5EInvalidConversionException(E5ERequestDataType actual, E5ERequestDataType[] allowedTypes)
: base($"Cannot convert data of type {actual} into one of {actual}")
{
Expected = expected;
AllowedTypes = allowedTypes;
Actual = actual;
#pragma warning disable CS0618 // Type or member is obsolete
Expected = allowedTypes[0];
#pragma warning restore CS0618 // Type or member is obsolete
}

/// <summary>
/// The required data type for this conversion call.
/// The required data type for this conversion call. Obsolete, got replaced with <see cref="AllowedTypes"/>.
/// </summary>
[Obsolete(
"The library got support for multiple allowedTypes data types per conversion. Please migrate this call to AllowedTypes.")]
public E5ERequestDataType Expected { get; }

/// <summary>
/// The actual data type.
/// </summary>
public E5ERequestDataType Actual { get; }

internal static void ThrowIfNotMatch(E5ERequestDataType expected, E5ERequestDataType actual)
/// <summary>
/// The allowed data types for this conversion call.
/// </summary>
public E5ERequestDataType[] AllowedTypes { get; }

internal static void ThrowIfNotMatch(E5ERequestDataType value, params E5ERequestDataType[] allowed)
{
if (expected != actual)
throw new E5EInvalidConversionException(expected, actual);
if (!allowed.Contains(value))
throw new E5EInvalidConversionException(value, allowed);
}
}
Loading

0 comments on commit 07038d8

Please sign in to comment.