Skip to content

Commit 6317a8b

Browse files
authored
feat: Implement check header in emulator (#66)
1 parent d158ce0 commit 6317a8b

File tree

4 files changed

+131
-3
lines changed

4 files changed

+131
-3
lines changed

src/Authoring/Configs/CheckHeaderConfig.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ namespace Azure.ApiManagement.PolicyToolkit.Authoring;
66
public record CheckHeaderConfig
77
{
88
public required string Name { get; init; }
9-
public required string FailCheckHttpCode { get; init; }
9+
public required int FailCheckHttpCode { get; init; }
1010
public required string FailCheckErrorMessage { get; init; }
11-
public required string IgnoreCase { get; init; }
11+
public required bool IgnoreCase { get; init; }
1212
public required string[] Values { get; init; }
1313
}

src/Testing/Document/MockCheckHeaderProvider.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,5 +35,11 @@ internal Setup(
3535

3636
public void WithCallback(Action<GatewayContext, CheckHeaderConfig> callback) =>
3737
_handler.CallbackSetup.Add((_predicate, callback).ToTuple());
38+
39+
public void OnCheckPass(Action<GatewayContext, CheckHeaderConfig> onCheckPass) =>
40+
_handler.OnCheckPassed.Add((_predicate, onCheckPass).ToTuple());
41+
42+
public void OnCheckFail(Action<GatewayContext, CheckHeaderConfig> onCheckFail) =>
43+
_handler.OnCheckFailed.Add((_predicate, onCheckFail).ToTuple());
3844
}
3945
}

src/Testing/Emulator/Policies/CheckHeaderHandler.cs

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,60 @@
22
// Licensed under the MIT License.
33

44
using Azure.ApiManagement.PolicyToolkit.Authoring;
5+
using Azure.ApiManagement.PolicyToolkit.Testing.Expressions;
56

67
namespace Azure.ApiManagement.PolicyToolkit.Testing.Emulator.Policies;
78

89
[Section(nameof(IInboundContext))]
910
internal class CheckHeaderHandler : PolicyHandler<CheckHeaderConfig>
1011
{
12+
public List<Tuple<
13+
Func<GatewayContext, CheckHeaderConfig, bool>,
14+
Action<GatewayContext, CheckHeaderConfig>
15+
>> OnCheckPassed { get; } = new();
16+
17+
public List<Tuple<
18+
Func<GatewayContext, CheckHeaderConfig, bool>,
19+
Action<GatewayContext, CheckHeaderConfig>
20+
>> OnCheckFailed { get; } = new();
21+
1122
public override string PolicyName => nameof(IInboundContext.CheckHeader);
1223

1324
protected override void Handle(GatewayContext context, CheckHeaderConfig config)
1425
{
15-
throw new NotImplementedException();
26+
bool pass = false;
27+
if (context.Request.Headers.TryGetValue(config.Name, out var values) && values.Length == 1)
28+
{
29+
pass = config.Values.Length == 0 || config.Values.Contains(values[0], ValueComparer(config));
30+
}
31+
32+
if (pass)
33+
{
34+
OnCheckPassed.Find(tuple => tuple.Item1(context, config))?.Item2(context, config);
35+
return;
36+
}
37+
38+
context.Response = new MockResponse()
39+
{
40+
StatusCode = config.FailCheckHttpCode,
41+
// StatusReason = GetStatusReason(config.FailCheckHttpCode), TODO: Create status reason mapper
42+
Headers = { { "Content-Type", ["application/json"] } },
43+
Body =
44+
{
45+
Content = $$"""
46+
{
47+
"statusCode": {{config.FailCheckHttpCode}},
48+
"message": "{{config.FailCheckErrorMessage}}"
49+
}
50+
"""
51+
}
52+
};
53+
54+
OnCheckFailed.Find(tuple => tuple.Item1(context, config))?.Item2(context, config);
55+
throw new FinishSectionProcessingException();
1656
}
57+
58+
private static StringComparer ValueComparer(CheckHeaderConfig config) => config.IgnoreCase
59+
? StringComparer.InvariantCultureIgnoreCase
60+
: StringComparer.InvariantCulture;
1761
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
using Azure.ApiManagement.PolicyToolkit.Authoring;
5+
using Azure.ApiManagement.PolicyToolkit.Testing;
6+
using Azure.ApiManagement.PolicyToolkit.Testing.Document;
7+
8+
using Newtonsoft.Json.Linq;
9+
10+
namespace Test.Emulator.Emulator.Policies;
11+
12+
[TestClass]
13+
public class CheckHeaderTests
14+
{
15+
class SimpleCheckHeader : IDocument
16+
{
17+
public void Inbound(IInboundContext context)
18+
{
19+
context.CheckHeader(new CheckHeaderConfig()
20+
{
21+
Name = "Test",
22+
FailCheckHttpCode = 401,
23+
FailCheckErrorMessage = "Request do not contain test header",
24+
IgnoreCase = false,
25+
Values = []
26+
});
27+
}
28+
}
29+
30+
[TestMethod]
31+
public void CheckHeader_Callback()
32+
{
33+
var test = new SimpleCheckHeader().AsTestDocument();
34+
var executedCallback = false;
35+
test.SetupInbound().CheckHeader().WithCallback((_, _) =>
36+
{
37+
executedCallback = true;
38+
});
39+
40+
test.RunInbound();
41+
42+
executedCallback.Should().BeTrue();
43+
}
44+
45+
[TestMethod]
46+
public void CheckHeader_PassExistenceCheck()
47+
{
48+
var test = new SimpleCheckHeader().AsTestDocument();
49+
test.Context.Request.Headers["Test"] = ["test"];
50+
51+
test.RunInbound();
52+
53+
var response = test.Context.Response;
54+
response.StatusCode.Should().NotBe(401);
55+
}
56+
57+
[TestMethod]
58+
public void CheckHeader_FailExistenceCheck()
59+
{
60+
var test = new SimpleCheckHeader().AsTestDocument();
61+
62+
test.RunInbound();
63+
64+
var response = test.Context.Response;
65+
response.StatusCode.Should().Be(401);
66+
response.Headers.Should().ContainKey("Content-Type")
67+
.WhoseValue.Should().ContainSingle()
68+
.Which.Should().Be("application/json");
69+
response.Body.Content.Should().NotBeNullOrWhiteSpace();
70+
var body = response.Body.As<JObject>();
71+
body.Should().ContainKey("statusCode")
72+
.WhoseValue.Should().NotBeNull().And
73+
.Subject.Value<int>().Should().Be(401);
74+
body.Should().ContainKey("message")
75+
.WhoseValue.Should().NotBeNull().And
76+
.Subject.Value<string>().Should().Be("Request do not contain test header");
77+
}
78+
}

0 commit comments

Comments
 (0)