Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes #358 MockGeneratorPlugin errors if the response headers have a duplicate #372

Merged
merged 12 commits into from
Jan 11, 2024
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public class GraphBatchResponsePayloadResponse {
[JsonPropertyName("body")]
public dynamic? Body { get; set; }
[JsonPropertyName("headers")]
public Dictionary<string, string>? Headers { get; set; }
public List<Dictionary<string, string>>? Headers { get; set; }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems odd to define headers as a list of dictionaries. Shouldn't it be a list of key-value pairs instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using a Dictionary (which is in effect the same thing AFAIK), means we can use Linq to do "ToDictionary()"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has now changed to a List<KeyValuePair<string,string>> due to the dictionary complicating some other areas.

}

public class GraphBatchResponsePayloadResponseBody {
Expand Down
4 changes: 2 additions & 2 deletions m365-developer-proxy-plugins/Behavior/RateLimitingPlugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -242,8 +242,8 @@ _urlsToWatch is null ||
if (_configuration.CustomResponse is not null)
{
var headers = _configuration.CustomResponse.ResponseHeaders is not null ?
_configuration.CustomResponse.ResponseHeaders.Select(h => new HttpHeader(h.Key, h.Value)) :
Array.Empty<HttpHeader>();
_configuration.CustomResponse.ResponseHeaders.SelectMany(dict => dict.Select(kv => new HttpHeader(kv.Key, kv.Value))).ToArray():
Array.Empty<HttpHeader>();

// allow custom throttling response
var responseCode = (HttpStatusCode)(_configuration.CustomResponse.ResponseCode ?? 200);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
using System.Net;
using System.Text.Json;
using System.Text.RegularExpressions;
using Microsoft.EntityFrameworkCore.Metadata;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we using EntityFrmeworkCore for something or is this a leftover?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Must be a leftover. Removed

using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Microsoft365.DeveloperProxy.Abstractions;

namespace Microsoft365.DeveloperProxy.Plugins.MockResponses;
Expand Down Expand Up @@ -34,7 +36,7 @@ protected override async Task OnRequest(object? sender, ProxyRequestArgs e)
GraphBatchResponsePayloadResponse? response = null;
var requestId = Guid.NewGuid().ToString();
var requestDate = DateTime.Now.ToString();
var headers = ProxyUtils
Dictionary<string, string> headers = ProxyUtils
.BuildGraphResponseHeaders(e.Session.HttpClient.Request, requestId, requestDate)
.ToDictionary(h => h.Name, h => h.Value);

Expand All @@ -45,7 +47,7 @@ protected override async Task OnRequest(object? sender, ProxyRequestArgs e)
{
Id = request.Id,
Status = (int)HttpStatusCode.BadGateway,
Headers = headers,
Headers = new List<Dictionary<string, string>> { headers },
Body = new GraphBatchResponsePayloadResponseBody
{
Error = new GraphBatchResponsePayloadResponseBodyError
Expand All @@ -69,10 +71,7 @@ protected override async Task OnRequest(object? sender, ProxyRequestArgs e)

if (mockResponse.ResponseHeaders is not null)
{
foreach (var key in mockResponse.ResponseHeaders.Keys)
{
headers[key] = mockResponse.ResponseHeaders[key];
}
mockResponse.ResponseHeaders.SelectMany(dict => dict.Select(kv => headers[kv.Key] = kv.Value));
}
// default the content type to application/json unless set in the mock response
if (!headers.Any(h => h.Key.Equals("content-type", StringComparison.OrdinalIgnoreCase)))
Expand Down Expand Up @@ -112,7 +111,7 @@ protected override async Task OnRequest(object? sender, ProxyRequestArgs e)
{
Id = request.Id,
Status = (int)statusCode,
Headers = headers,
Headers = new List<Dictionary<string, string>> { headers },
Body = body
};

Expand Down
2 changes: 1 addition & 1 deletion m365-developer-proxy-plugins/MockResponses/MockResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,5 @@ public class MockResponse {
[JsonPropertyName("responseBody")]
public dynamic? ResponseBody { get; set; }
[JsonPropertyName("responseHeaders")]
public IDictionary<string, string>? ResponseHeaders { get; set; }
public List<Dictionary<string, string>>? ResponseHeaders { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -179,8 +179,8 @@ private void ProcessMockResponse(SessionEventArgs e, MockResponse matchingRespon
}

if (matchingResponse.ResponseHeaders is not null) {
foreach (var key in matchingResponse.ResponseHeaders.Keys) {
headers.Add(new HttpHeader(key, matchingResponse.ResponseHeaders[key]));
foreach (var key in matchingResponse.ResponseHeaders.SelectMany(dict => dict.Select(kv => new HttpHeader(kv.Key, kv.Value)))) {
headers.Add(key);
}
}
// default the content type to application/json unless set in the mock response
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public void LoadResponses() {
using (FileStream stream = new FileStream(_responsesFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) {
using (StreamReader reader = new StreamReader(stream)) {
var responsesString = reader.ReadToEnd();
var responsesConfig = JsonSerializer.Deserialize<MockResponseConfiguration>(responsesString);
var responsesConfig = JsonSerializer.Deserialize<MockResponseConfiguration>(responsesString, new JsonSerializerOptions { AllowTrailingCommas = true, ReadCommentHandling= JsonCommentHandling.Skip});
IEnumerable<MockResponse>? configResponses = responsesConfig?.Responses;
if (configResponses is not null) {
_configuration.Responses = configResponses;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,9 +135,9 @@ private void FailBatch(ProxyRequestArgs e) {
var retryAfterDate = DateTime.Now.AddSeconds(retryAfterInSeconds);
var requestUrl = ProxyUtils.GetAbsoluteRequestUrlFromBatch(e.Session.HttpClient.Request.RequestUri, request.Url);
e.ThrottledRequests.Add(new ThrottlerInfo(GraphUtils.BuildThrottleKey(requestUrl), ShouldThrottle, retryAfterDate));
response.Headers = new Dictionary<string, string>{
response.Headers = new List<Dictionary<string, string>>{new Dictionary<string, string>{
{ "Retry-After", retryAfterInSeconds.ToString() }
};
}};
}

responses.Add(response);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ private void AfterRecordingStop(object? sender, RecordingArgs e)
return;
}

var methodAndUrlComparer = new MethodAndUrlComparer();
var mocks = new List<MockResponse>();

foreach (var request in e.RequestLogs)
Expand All @@ -56,9 +55,9 @@ request.Context is null ||
Method = methodAndUrl.Item1,
Url = methodAndUrl.Item2,
ResponseCode = response.StatusCode,
ResponseHeaders = response.Headers
ResponseHeaders = new List<Dictionary<string, string>>{ response.Headers
.Select(h => new KeyValuePair<string, string>(h.Name, h.Value))
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value),
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value) },
ResponseBody = GetResponseBody(request.Context.Session).Result
};
// skip mock if it's 200 but has no body
Expand Down