-
Notifications
You must be signed in to change notification settings - Fork 437
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
Refactor binding pipeline to allow type info to flow through #1002
Conversation
{ | ||
// TODO: we should optimize this to remove the double | ||
// serialization/deserialization | ||
string json = ToJson(value); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure of an easier out of the box way to recursively convert from an ExpandoObject to a JObject. No worse that what we had before, but we have room for improvement.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems simple enough. For some reason, though, I just noticed we have a custom converter for ExpandoObject
do we have a good reason for that? There's an out of the box converter with Json.NET : http://www.newtonsoft.com/json/help/html/t_newtonsoft_json_converters_expandoobjectconverter.htm
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wasn't aware of that converter. I'll remove our custom converter :)
return Task.CompletedTask; | ||
} | ||
|
||
internal static HttpResponseMessage CreateResponse(HttpRequestMessage request, object content) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Making this logic unit testable - I'm going to add a bunch of tests
@@ -1035,6 +1035,36 @@ public void ExcludedFunction_NotAddedToHost() | |||
Assert.True(false, "There was no log found for duplicate calls to done"); | |||
} | |||
|
|||
[Fact] | |||
public async Task HttpTrigger_Scenarios_Buffer() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Stole this test verbatim (and supporting function) from @mamaso's PR :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A couple of small comments.
{ | ||
// TODO: we should optimize this to remove the double | ||
// serialization/deserialization | ||
string json = ToJson(value); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems simple enough. For some reason, though, I just noticed we have a custom converter for ExpandoObject
do we have a good reason for that? There's an out of the box converter with Json.NET : http://www.newtonsoft.com/json/help/html/t_newtonsoft_json_converters_expandoobjectconverter.htm
statusCode = (HttpStatusCode)(int)value; | ||
} | ||
if ((responseObject.TryGetValue("status", out value) && value is int) || | ||
(responseObject.TryGetValue("statusCode", out value) && value is int)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This logic could be simplified to:
(responseObject.TryGetValue("status", out value) || responseObject.TryGetValue("statusCode", out value)) && value is int
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
{ | ||
isRawResponse = (bool)value; | ||
} | ||
if (responseObject.TryGetValue("isRaw", out value) && value is bool) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's tempting to add a generic extension method to avoid those && value is <type>
checks....
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
@@ -248,7 +265,7 @@ private static void AddResponseHeader(HttpResponseMessage response, KeyValuePair | |||
} | |||
break; | |||
case "content-md5": | |||
response.Content.Headers.ContentMD5 = header.Value.Value<byte[]>(); | |||
response.Content.Headers.ContentMD5 = header.Value as byte[]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A string here would be valid as well. (I believe I had a similar comment on @mamaso's PR)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch - missed that in the other review. Fixed.
@@ -240,23 +236,6 @@ private static void EnsureInitialized() | |||
} | |||
} | |||
|
|||
/// <summary> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎉 👍 😄
public static IDictionary<string, TValue> ToCaseInsensitive<TValue>(this IDictionary<string, TValue> dictionary) | ||
{ | ||
return new Dictionary<string, TValue>(dictionary, StringComparer.OrdinalIgnoreCase); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, just think about how nice it would be to have a TryGetValue here... 😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. Also added missing test suites + tests for new and some existing code
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
awesome stuff!
return new Dictionary<string, TValue>(dictionary, StringComparer.OrdinalIgnoreCase); | ||
} | ||
|
||
public static bool TryGetValue<TValue>(this IDictionary<string, object> obj, string name, out TValue value) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like it, type safe TryGetValue
|
||
public static IDictionary<string, TValue> AsCaseInsensitive<TValue>(this IDictionary<string, TValue> dictionary) | ||
{ | ||
return new Dictionary<string, TValue>(dictionary, StringComparer.OrdinalIgnoreCase); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this throw if there are dupe keys in the input dictionary? ie 'key' and 'KEY'. Not sure if we'd prefer to throw or just overwrite one of the props in that case
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmmm, not sure. I'll add a test and figure out the right behavior.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point indeed. This will surely throw an exception complaining about duplicate keys being added. I think throwing here is the right behavior, we just need to make sure this is properly handled and we provide a meaningful, detailed message from the calling code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed the whole case insensitive thing. Modified TryGetValue helper to support case insensitive queries, which matches the logic we had before with JObject
statusCode = (HttpStatusCode)(int)value; | ||
} | ||
int statusValue; | ||
if (responseObject.TryGetValue<int>("status", out statusValue) || |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pretty sure we need to support string for this as well :/ I remember some tests breaking when I treated status as an integer.
Seems like that cast is supported
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All tests are passing with these changes. What is the repro you're thinking of?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just tried out this in prod and it gives a 400, think it will fail with this new logic.
context.res = {
body: true,
status: "400"
};
context.done();
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've added support for both integers and strings
isRawResponse = (bool)value; | ||
} | ||
bool isRawValue; | ||
if (responseObject.TryGetValue<bool>("isRaw", out isRawValue)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
potentially same cast issue
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The previous code had a check that verified the JToken was of type Boolean, so I don't think strings were supported. However, I don't think we want to support strings for this. I could put in logic to support string values, but I don't think it's worth it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, since isRaw is so new and mainly internal I think we're good on this one
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just the one comment, tests look good too
|
||
if (!found && ignoreCase) | ||
{ | ||
// try a case insensitive match - first match wins |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As a small optimization we could consider special casing for first letter, i.e. TryGetValue
'body' and 'Body', I think that would catch 99% of these with no linear scan
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree that would catch most cases, but I don't think this is a big perf issue really. I'd rather not get in the business of checking a reduced set of permutations (e.g. Key, key, KEY, Key)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
totally fair :)
Merged. I'll address any additional feedback people have. |
Merged in commit 6b4c50b |
This builds on the work/investigations that @mamaso did in #871 to address #814.
I wanted to make this a deeper fix across bindings, allowing ExpandoObjects to flow into the binding layer (preserving type info).
I'm going to add a bunch of new unit tests for some of the new logic - working on those now.