Skip to content

Commit 8bc378d

Browse files
committed
GoogleCloudFunctions
1 parent 433792f commit 8bc378d

File tree

10 files changed

+53
-51
lines changed

10 files changed

+53
-51
lines changed

src/CommandQuery.GoogleCloudFunctions/CommandFunction.cs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,23 +9,26 @@ namespace CommandQuery.GoogleCloudFunctions
99
public class CommandFunction : ICommandFunction
1010
{
1111
private readonly ICommandProcessor _commandProcessor;
12+
private readonly ILogger<CommandFunction> _logger;
1213
private readonly JsonSerializerOptions? _options;
1314

1415
/// <summary>
1516
/// Initializes a new instance of the <see cref="CommandFunction"/> class.
1617
/// </summary>
1718
/// <param name="commandProcessor">An <see cref="ICommandProcessor"/>.</param>
19+
/// <param name="logger">An <see cref="ILogger{T}"/>.</param>
1820
/// <param name="options"><see cref="JsonSerializerOptions"/> to control the behavior during deserialization of <see cref="HttpRequest.Body"/> and serialization of <see cref="HttpResponse.Body"/>.</param>
19-
public CommandFunction(ICommandProcessor commandProcessor, JsonSerializerOptions? options = null)
21+
public CommandFunction(ICommandProcessor commandProcessor, ILogger<CommandFunction> logger, JsonSerializerOptions? options = null)
2022
{
2123
_commandProcessor = commandProcessor;
24+
_logger = logger;
2225
_options = options;
2326
}
2427

2528
/// <inheritdoc />
26-
public async Task HandleAsync(string commandName, HttpContext context, ILogger? logger, CancellationToken cancellationToken = default)
29+
public async Task HandleAsync(string commandName, HttpContext context, CancellationToken cancellationToken = default)
2730
{
28-
logger?.LogInformation("Handle {Command}", commandName);
31+
_logger.LogInformation("Handle {Command}", commandName);
2932

3033
if (context is null)
3134
{
@@ -48,7 +51,7 @@ public async Task HandleAsync(string commandName, HttpContext context, ILogger?
4851
catch (Exception exception)
4952
{
5053
var payload = await context.Request.ReadAsStringAsync().ConfigureAwait(false);
51-
logger?.LogError(exception, "Handle command failed: {Command}, {Payload}", commandName, payload);
54+
_logger.LogError(exception, "Handle command failed: {Command}, {Payload}", commandName, payload);
5255

5356
if (exception.IsHandled())
5457
{

src/CommandQuery.GoogleCloudFunctions/CommandQuery.GoogleCloudFunctions.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@
3737
</ItemGroup>
3838

3939
<ItemGroup>
40-
<PackageReference Include="Microsoft.AspNetCore.Http" Version="2.2.2" />
41-
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.3" />
40+
<PackageReference Include="Microsoft.AspNetCore.Http" Version="2.1.34" />
41+
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.4" />
4242
</ItemGroup>
4343

4444
<ItemGroup>

src/CommandQuery.GoogleCloudFunctions/ICommandFunction.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
using Microsoft.AspNetCore.Http;
2-
using Microsoft.Extensions.Logging;
32

43
namespace CommandQuery.GoogleCloudFunctions
54
{
@@ -13,10 +12,9 @@ public interface ICommandFunction
1312
/// </summary>
1413
/// <param name="commandName">The name of the command.</param>
1514
/// <param name="context">A <see cref="HttpContext"/>.</param>
16-
/// <param name="logger">An <see cref="ILogger"/>.</param>
1715
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
1816
/// <returns>A task that represents the asynchronous operation.</returns>
1917
/// <exception cref="ArgumentNullException"><paramref name="context"/> is <see langword="null"/>.</exception>
20-
Task HandleAsync(string commandName, HttpContext context, ILogger? logger, CancellationToken cancellationToken = default);
18+
Task HandleAsync(string commandName, HttpContext context, CancellationToken cancellationToken = default);
2119
}
2220
}

src/CommandQuery.GoogleCloudFunctions/IQueryFunction.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
using Microsoft.AspNetCore.Http;
2-
using Microsoft.Extensions.Logging;
32

43
namespace CommandQuery.GoogleCloudFunctions
54
{
@@ -13,10 +12,9 @@ public interface IQueryFunction
1312
/// </summary>
1413
/// <param name="queryName">The name of the query.</param>
1514
/// <param name="context">A <see cref="HttpContext"/>.</param>
16-
/// <param name="logger">An <see cref="ILogger"/>.</param>
1715
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
1816
/// <returns>A task that represents the asynchronous operation.</returns>
1917
/// <exception cref="ArgumentNullException"><paramref name="context"/> is <see langword="null"/>.</exception>
20-
Task HandleAsync(string queryName, HttpContext context, ILogger? logger, CancellationToken cancellationToken = default);
18+
Task HandleAsync(string queryName, HttpContext context, CancellationToken cancellationToken = default);
2119
}
2220
}

src/CommandQuery.GoogleCloudFunctions/Internal/HttpExtensions.cs

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,27 +6,22 @@ namespace CommandQuery.GoogleCloudFunctions
66
{
77
internal static class HttpExtensions
88
{
9-
private const int DefaultBufferSize = 1024;
10-
11-
// https://github.com/Azure/azure-webjobs-sdk-extensions/blob/main/src/WebJobs.Extensions.Http/Extensions/HttpRequestExtensions.cs#L26
12-
internal static async Task<string> ReadAsStringAsync(this HttpRequest request)
9+
internal static async Task<string?> ReadAsStringAsync(this HttpRequest req, Encoding? encoding = null)
1310
{
14-
request.EnableBuffering();
15-
16-
string? result = null;
17-
using (var reader = new StreamReader(
18-
request.Body,
19-
encoding: Encoding.UTF8,
20-
detectEncodingFromByteOrderMarks: true,
21-
bufferSize: DefaultBufferSize,
22-
leaveOpen: true))
11+
if (req is null)
2312
{
24-
result = await reader.ReadToEndAsync().ConfigureAwait(false);
13+
throw new ArgumentNullException(nameof(req));
2514
}
2615

27-
request.Body.Seek(0, SeekOrigin.Begin);
16+
if (req.Body is null)
17+
{
18+
return null;
19+
}
2820

29-
return result;
21+
using (var reader = new StreamReader(req.Body, bufferSize: 1024, detectEncodingFromByteOrderMarks: true, encoding: encoding ?? Encoding.UTF8, leaveOpen: true))
22+
{
23+
return await reader.ReadToEndAsync().ConfigureAwait(false);
24+
}
3025
}
3126

3227
internal static async Task OkAsync(this HttpResponse response, object? result, JsonSerializerOptions? options, CancellationToken cancellationToken)

src/CommandQuery.GoogleCloudFunctions/QueryFunction.cs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,23 +9,26 @@ namespace CommandQuery.GoogleCloudFunctions
99
public class QueryFunction : IQueryFunction
1010
{
1111
private readonly IQueryProcessor _queryProcessor;
12+
private readonly ILogger<QueryFunction> _logger;
1213
private readonly JsonSerializerOptions? _options;
1314

1415
/// <summary>
1516
/// Initializes a new instance of the <see cref="QueryFunction"/> class.
1617
/// </summary>
1718
/// <param name="queryProcessor">An <see cref="IQueryProcessor"/>.</param>
19+
/// <param name="logger">An <see cref="ILogger{T}"/>.</param>
1820
/// <param name="options"><see cref="JsonSerializerOptions"/> to control the behavior during deserialization of <see cref="HttpRequest.Body"/> and serialization of <see cref="HttpResponse.Body"/>.</param>
19-
public QueryFunction(IQueryProcessor queryProcessor, JsonSerializerOptions? options = null)
21+
public QueryFunction(IQueryProcessor queryProcessor, ILogger<QueryFunction> logger, JsonSerializerOptions? options = null)
2022
{
2123
_queryProcessor = queryProcessor;
24+
_logger = logger;
2225
_options = options;
2326
}
2427

2528
/// <inheritdoc />
26-
public async Task HandleAsync(string queryName, HttpContext context, ILogger? logger, CancellationToken cancellationToken = default)
29+
public async Task HandleAsync(string queryName, HttpContext context, CancellationToken cancellationToken = default)
2730
{
28-
logger?.LogInformation("Handle {Query}", queryName);
31+
_logger.LogInformation("Handle {Query}", queryName);
2932

3033
if (context is null)
3134
{
@@ -43,7 +46,7 @@ public async Task HandleAsync(string queryName, HttpContext context, ILogger? lo
4346
catch (Exception exception)
4447
{
4548
var payload = context.Request.Method == "GET" ? context.Request.QueryString.Value : await context.Request.ReadAsStringAsync().ConfigureAwait(false);
46-
logger?.LogError(exception, "Handle query failed: {Query}, {Payload}", queryName, payload);
49+
_logger.LogError(exception, "Handle query failed: {Query}, {Payload}", queryName, payload);
4750

4851
if (exception.IsHandled())
4952
{

tests/CommandQuery.GoogleCloudFunctions.Tests/CommandFunctionTests.cs

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
using LoFuUnit.AutoMoq;
77
using LoFuUnit.NUnit;
88
using Microsoft.AspNetCore.Http;
9-
using Microsoft.Extensions.Logging;
109
using Moq;
1110
using NUnit.Framework;
1211

@@ -23,7 +22,6 @@ public void SetUp()
2322
Context = new DefaultHttpContext();
2423
Context.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes("{}"));
2524
Context.Response.Body = new MemoryStream();
26-
Logger = Use<ILogger>();
2725
}
2826

2927
[LoFu, Test]
@@ -34,44 +32,48 @@ public async Task when_handling_the_command()
3432

3533
async Task should_invoke_the_command_processor()
3634
{
35+
Context.Request.Body.Position = 0;
3736
Context.Response.Clear();
38-
await Subject.HandleAsync(CommandName, Context, Logger);
37+
await Subject.HandleAsync(CommandName, Context);
3938

4039
Context.Response.StatusCode.Should().Be(200);
4140
}
4241

4342
async Task should_throw_when_request_is_null()
4443
{
45-
Func<Task> act = () => Subject.HandleAsync(CommandName, null, Logger);
44+
Func<Task> act = () => Subject.HandleAsync(CommandName, null);
4645
await act.Should().ThrowAsync<ArgumentNullException>();
4746
}
4847

4948
async Task should_handle_CommandProcessorException()
5049
{
50+
Context.Request.Body.Position = 0;
5151
Context.Response.Clear();
5252
The<Mock<ICommandProcessor>>().Setup(x => x.ProcessAsync(It.IsAny<FakeCommand>(), It.IsAny<CancellationToken>())).Throws(new CommandProcessorException("fail"));
5353

54-
await Subject.HandleAsync(CommandName, Context, Logger);
54+
await Subject.HandleAsync(CommandName, Context);
5555

5656
await Context.Response.ShouldBeErrorAsync("fail", 400);
5757
}
5858

5959
async Task should_handle_CommandException()
6060
{
61+
Context.Request.Body.Position = 0;
6162
Context.Response.Clear();
6263
The<Mock<ICommandProcessor>>().Setup(x => x.ProcessAsync(It.IsAny<FakeCommand>(), It.IsAny<CancellationToken>())).Throws(new CommandException("invalid"));
6364

64-
await Subject.HandleAsync(CommandName, Context, Logger);
65+
await Subject.HandleAsync(CommandName, Context);
6566

6667
await Context.Response.ShouldBeErrorAsync("invalid", 400);
6768
}
6869

6970
async Task should_handle_Exception()
7071
{
72+
Context.Request.Body.Position = 0;
7173
Context.Response.Clear();
7274
The<Mock<ICommandProcessor>>().Setup(x => x.ProcessAsync(It.IsAny<FakeCommand>(), It.IsAny<CancellationToken>())).Throws(new Exception("fail"));
7375

74-
await Subject.HandleAsync(CommandName, Context, Logger);
76+
await Subject.HandleAsync(CommandName, Context);
7577

7678
await Context.Response.ShouldBeErrorAsync("fail", 500);
7779
}
@@ -89,15 +91,14 @@ async Task should_return_the_result_from_the_command_processor()
8991
var expected = new FakeResult();
9092
The<Mock<ICommandProcessor>>().Setup(x => x.ProcessAsync(It.IsAny<FakeResultCommand>(), It.IsAny<CancellationToken>())).Returns(Task.FromResult(expected));
9193

92-
await Subject.HandleAsync(CommandName, Context, Logger);
94+
await Subject.HandleAsync(CommandName, Context);
9395

9496
Context.Response.StatusCode.Should().Be(200);
9597
Context.Response.Body.Length.Should().BeGreaterThan(0);
9698
}
9799
}
98100

99101
HttpContext Context;
100-
ILogger Logger;
101102
string CommandName;
102103
}
103104
}

tests/CommandQuery.GoogleCloudFunctions.Tests/CommandQuery.GoogleCloudFunctions.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
<PackageReference Include="LoFuUnit.AutoMoq" Version="3.0.0" />
1818
<PackageReference Include="LoFuUnit.NUnit" Version="3.0.0" />
1919
<PackageReference Include="Microsoft.AspNetCore.Http.Extensions" Version="2.2.0" />
20+
<PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.0" />
2021
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
2122
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0">
2223
<PrivateAssets>all</PrivateAssets>

tests/CommandQuery.GoogleCloudFunctions.Tests/QueryFunctionTests.cs

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
using LoFuUnit.NUnit;
88
using Microsoft.AspNetCore.Http;
99
using Microsoft.AspNetCore.Http.Internal;
10-
using Microsoft.Extensions.Logging;
1110
using Microsoft.Extensions.Primitives;
1211
using Moq;
1312
using NUnit.Framework;
@@ -20,10 +19,9 @@ public class QueryFunctionTests : LoFuTest<QueryFunction>
2019
public void SetUp()
2120
{
2221
Clear();
22+
QueryName = "FakeQuery";
2323
Use<Mock<IQueryProcessor>>();
2424
Use<JsonSerializerOptions>(null);
25-
Logger = Use<ILogger>();
26-
QueryName = "FakeQuery";
2725
The<Mock<IQueryProcessor>>().Setup(x => x.GetQueryType(QueryName)).Returns(typeof(FakeQuery));
2826
}
2927

@@ -36,48 +34,52 @@ public async Task when_handling_the_query_via_Post()
3634

3735
async Task should_return_the_result_from_the_query_processor()
3836
{
37+
Context.Request.Body.Position = 0;
3938
Context.Response.Clear();
4039
var expected = new FakeResult();
4140
The<Mock<IQueryProcessor>>().Setup(x => x.ProcessAsync(It.IsAny<FakeQuery>(), It.IsAny<CancellationToken>())).Returns(Task.FromResult(expected));
4241

43-
await Subject.HandleAsync(QueryName, Context, Logger);
42+
await Subject.HandleAsync(QueryName, Context);
4443

4544
Context.Response.StatusCode.Should().Be(200);
4645
Context.Response.Body.Length.Should().BeGreaterThan(0);
4746
}
4847

4948
async Task should_throw_when_request_is_null()
5049
{
51-
Func<Task> act = () => Subject.HandleAsync(QueryName, null, Logger);
50+
Func<Task> act = () => Subject.HandleAsync(QueryName, null);
5251
await act.Should().ThrowAsync<ArgumentNullException>();
5352
}
5453

5554
async Task should_handle_QueryProcessorException()
5655
{
56+
Context.Request.Body.Position = 0;
5757
Context.Response.Clear();
5858
The<Mock<IQueryProcessor>>().Setup(x => x.ProcessAsync(It.IsAny<FakeQuery>(), It.IsAny<CancellationToken>())).Throws(new QueryProcessorException("fail"));
5959

60-
await Subject.HandleAsync(QueryName, Context, Logger);
60+
await Subject.HandleAsync(QueryName, Context);
6161

6262
await Context.Response.ShouldBeErrorAsync("fail", 400);
6363
}
6464

6565
async Task should_handle_QueryException()
6666
{
67+
Context.Request.Body.Position = 0;
6768
Context.Response.Clear();
6869
The<Mock<IQueryProcessor>>().Setup(x => x.ProcessAsync(It.IsAny<FakeQuery>(), It.IsAny<CancellationToken>())).Throws(new QueryException("invalid"));
6970

70-
await Subject.HandleAsync(QueryName, Context, Logger);
71+
await Subject.HandleAsync(QueryName, Context);
7172

7273
await Context.Response.ShouldBeErrorAsync("invalid", 400);
7374
}
7475

7576
async Task should_handle_Exception()
7677
{
78+
Context.Request.Body.Position = 0;
7779
Context.Response.Clear();
7880
The<Mock<IQueryProcessor>>().Setup(x => x.ProcessAsync(It.IsAny<FakeQuery>(), It.IsAny<CancellationToken>())).Throws(new Exception("fail"));
7981

80-
await Subject.HandleAsync(QueryName, Context, Logger);
82+
await Subject.HandleAsync(QueryName, Context);
8183

8284
await Context.Response.ShouldBeErrorAsync("fail", 500);
8385
}
@@ -97,15 +99,14 @@ async Task should_return_the_result_from_the_query_processor()
9799
var expected = new FakeResult();
98100
The<Mock<IQueryProcessor>>().Setup(x => x.ProcessAsync(It.IsAny<FakeQuery>(), It.IsAny<CancellationToken>())).Returns(Task.FromResult(expected));
99101

100-
await Subject.HandleAsync(QueryName, Context, Logger);
102+
await Subject.HandleAsync(QueryName, Context);
101103

102104
Context.Response.StatusCode.Should().Be(200);
103105
Context.Response.Body.Length.Should().BeGreaterThan(0);
104106
}
105107
}
106108

107109
HttpContext Context;
108-
ILogger Logger;
109110
string QueryName;
110111
}
111112
}

tests/CommandQuery.GoogleCloudFunctions.Tests/ServiceCollectionExtensionsTests.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ public void when_AddCommandFunction()
1313
var assembly = typeof(FakeCommandHandler).Assembly;
1414
var serviceCollection = new ServiceCollection();
1515

16+
serviceCollection.AddLogging();
1617
serviceCollection.AddCommandFunction(assembly);
1718
var provider = serviceCollection.BuildServiceProvider();
1819

@@ -25,6 +26,7 @@ public void when_AddQueryFunction()
2526
var assembly = typeof(FakeQueryHandler).Assembly;
2627
var serviceCollection = new ServiceCollection();
2728

29+
serviceCollection.AddLogging();
2830
serviceCollection.AddQueryFunction(assembly);
2931
var provider = serviceCollection.BuildServiceProvider();
3032

0 commit comments

Comments
 (0)