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

Enable use of nullable maps for headers, params and query #694

Open
wants to merge 6 commits into
base: dev
Choose a base branch
from

Conversation

kamperiadis
Copy link
Collaborator

BREAKING CHANGE: Enabled the UseNullableValueDictionaryForHttp capability which makes the host send back request information in nullable maps (i.e. When the UseNullableValueDictionaryForHttp capability is enabled, the host stores the values in nullable_headers, nullable_params and nullable_query instead of headers, params and query)

Issue describing the changes in this PR

Context:
This is the sample function app code run for the screenshots below:
image
Before:
Without this capability, if the customer were to set an empty string as the query value, the host would not add the entry to the query map. This means that if the customer were to try to access that entry from the query map (via the getQueryParameters function), they would just get null.
beforeChanges
After:
With this capability, if the customer were to set an empty string as the query value, the host would add the entry to the query map with the value just being the empty string.
withChanges

This is meant to resolve issue #683

Pull request checklist

  • My changes do not require documentation changes
    • Otherwise: Documentation issue linked to PR
  • My changes should not be added to the release notes for the next release
    • Otherwise: I've added my notes to release_notes.md
  • My changes do not need to be backported to a previous version
    • Otherwise: Backport tracked by issue/PR #issue_or_pr
  • I have added all required tests (Unit tests, E2E tests)

this.fields = Arrays.asList(
convertFromNullableMap(this.httpPayload.getNullableHeadersMap()),
convertFromNullableMap(this.httpPayload.getNullableQueryMap()),
convertFromNullableMap(this.httpPayload.getNullableParamsMap()));
Copy link
Member

Choose a reason for hiding this comment

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

not related to this PR, but do we know difference between query map and param map?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Looking at the host code, it looks like the three key differences between query map and param map are that:

  1. They come from different parts of the request object (i.e. request.Query vs request.HttpContext.Items.TryGetValue(HttpExtensionConstants.AzureWebJobsHttpRouteDataKey, out object routeData))
  2. We do not add null values to the parameters map, even if the UseNullableValueDictionaryForHttp capability is on
  3. We add empty strings to the parameters map, even if the UseNullableValueDictionaryForHttp capability is off

@@ -41,14 +56,150 @@ public static RpcHttp getTestRpcHttp(Object inputBody) throws Exception {
return httpBuilder.build();
}

@Test
public void rpcHttpDataSource_To_HttpRequestMessage_NullableQueryParamsEmpty_EnvSettingEnabled() throws Exception {
Copy link
Member

Choose a reason for hiding this comment

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

let's use camel-case syntax for method name, the old tests were wrote by c# expert, seems they are using c# name convention.

parameters[0].getParameterizedType());
BindingData actualArg = actualBindingData.orElseThrow(WrongMethodTypeException::new);
HttpRequestMessage<?> requestMsg = (HttpRequestMessage<?>) actualArg.getValue();
assertEquals(requestMsg.getQueryParameters().get("name"), "");
Copy link
Member

Choose a reason for hiding this comment

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

let's follow the convention that expect value comes first and test value comes second. I think we can use assertEmpty here.

parameters[0].getParameterizedType());
BindingData actualArg = actualBindingData.orElseThrow(WrongMethodTypeException::new);
HttpRequestMessage<?> requestMsg = (HttpRequestMessage<?>) actualArg.getValue();
assertEquals(requestMsg.getQueryParameters().get("name"), null);
Copy link
Member

Choose a reason for hiding this comment

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

we can use assertNull

}

@Test
public void rpcHttpDataSource_To_HttpRequestMessage_NullableQueryParamsNonEmpty() throws Exception {
Copy link
Member

Choose a reason for hiding this comment

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

I think we can have the test cases more clear. There are two major scenarios we want to test

  1. appsetting to false
  2. appsettting to true

each of those scenarios we want to test

  1. normal case -- param value empty
  2. param value empty

So we can have two test cases in total

  1. with appsetting to true
  • have one normal param -- expect same value
  • have one param with empty value -- expect empty string
  1. with appsetting to false
  • have one normal param -- expect normal value
  • have one param with empty value -- expect null

We can test header map and query map in same test method.
So we can reduce the test methods number to 2 but still have full coverage. Let me know if this make sense or not. Thanks.

@kaibocai kaibocai linked an issue Jan 24, 2023 that may be closed by this pull request
kaibocai
kaibocai previously approved these changes Jan 26, 2023
@kaibocai
Copy link
Member

Once this feature is released, need to document it somewhere (repo wiki?) for customer. Maybe create an issue to track creating the document.

Copy link
Member

@shreyas-gopalakrishna shreyas-gopalakrishna left a comment

Choose a reason for hiding this comment

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

LGTM! Just one minor refactor

@@ -86,4 +91,16 @@ public Builder createResponseBuilder(HttpStatus status) {
return new HttpRequestMessageImpl(v, bodyData.getValue());
});
}

private static Map<String, String> convertFromNullableMap(Map<String, NullableTypes.NullableString> nullableMap) {
if (Util.isTrue(System.getenv("FUNCTIONS_WORKER_NULLABLE_VALUES_ENABLED"))) {

Choose a reason for hiding this comment

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

Let us make FUNCTIONS_WORKER_NULLABLE_VALUES_ENABLED a constant.

Copy link
Member

Choose a reason for hiding this comment

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

nice catch! there is a constant file you can define it there.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Empty string cannot be passed in HttpTrigger query string
3 participants