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

Different filter escaping behaviour in batch and direct requests #2708

Open
noelroehrig opened this issue Oct 14, 2024 · 0 comments
Open

Different filter escaping behaviour in batch and direct requests #2708

noelroehrig opened this issue Oct 14, 2024 · 0 comments
Labels
status:waiting-for-triage An issue that is yet to be reviewed or assigned type:bug A broken experience

Comments

@noelroehrig
Copy link

noelroehrig commented Oct 14, 2024

Describe the bug

I'm trying to find a user and a selection of their properties through their email. Currently I'm building a filter to find said user like so:

  • filter: identities/any(id:id/issuer eq ' ' and id/issuerAssignedId eq '{emailAddress}')

For most email addresses, the filtering works as expected for both direct requests using _GraphServiceClient.Users.GetAsync() as well as batch requests and building the requests with _GraphServiceClient.Users.ToGetRequestInformation().

I've noticed that some of our users aren't returned by the batch requests but provide valid responses through a direct request.
The emails have in common that they contain a '+' character, e.g. [email protected]. Upon further investigation I've noticed that the Graph API can't handle the '+' character and needs it to be escaped.
Apparently, the filter string is escaped when requesting _GraphServiceClient.Users.GetAsync() but not when using _GraphServiceClient.Users.ToGetRequestInformation() followed by a batch request.

Expected behavior

The filter behaviour should be equal throughout the SDK and not differ between usages. I believe the filter should also be escaped when build a request and sending it through a batch request.

How to reproduce

Assuming I have a list of emails for which I want some B2C properties like the display name.

When I use the the SDK by sending the requests one by one like so, everything works as expected:

var usersResponse = await _GraphServiceClient.Users
            .GetAsync(requestConfig =>
            {
                requestConfig.QueryParameters.Top = 999;
                requestConfig.QueryParameters.Select = ["displayName", "identities", "otherMails", "id"];
                requestConfig.QueryParameters.Filter = $"identities/any(id:id/issuer eq ' ' and id/issuerAssignedId eq '{emailAddress}')";
            }, cancellationToken);

if (usersResponse == null)
{
    return null;
}

var userList = new List<User>();
var pageIterator = PageIterator<User, UserCollectionResponse>.CreatePageIterator(_GraphServiceClient, usersResponse, user =>
{
    userList.Add(user);
    return true;
});
await pageIterator.IterateAsync(cancellationToken);

return userList;

But creating a batch request when the list of email addresses might become bigger creates empty results for emails with a '+' character. The request is built like this:

var batchRequestContent = new BatchRequestContentCollection(_GraphServiceClient);
var requestIdsForEmails = new Dictionary<string, string>();
foreach (var emailAddress in emailAddresses)
{
     var identityRequest = _GraphServiceClient.Users.ToGetRequestInformation(requestConfig =>
     {
          requestConfig.QueryParameters.Select = UserSelections;
          requestConfig.QueryParameters.Filter = $"identities/any(id:id/issuer eq ' ' and id/issuerAssignedId eq '{emailAddress}')"
     });

     var identityRequestId = await batchRequestContent.AddBatchRequestStepAsync(identityRequest);

     requestIdsForEmails.Add(emailAddress, [identityRequestId, otherMailRequestId]);
}
var batchResponse = await _GraphServiceClient.Batch.PostAsync(batchRequestContent, cancellationToken);

var emailsWithUsers = new Dictionary<string, IEnumerable<User>>();
foreach (var emailWithRequestIds in requestIdsForEmails)
{
     try
     {
          var userResponse = await batchResponse.GetResponseByIdAsync<UserCollectionResponse>(emailWithRequestIds.Value);

          emailsWithUsers.Add(emailWithRequestIds.Key, userResponse.Value);
      }
      catch (Exception)
      {
          emailsWithUsers.Add(emailWithRequestIds.Key, []);
      }
}
return emailsWithUsers;

SDK Version

5.59.0

Latest version known to work for scenario above?

No response

Known Workarounds

I've fixed the issue by explicitly escaping the emailAddress before using it in the filter:

var escapedEmail = Uri.EscapeDataString(emailAddress);
var identityRequest = _GraphServiceClient.Users.ToGetRequestInformation(requestConfig =>
{
    requestConfig.QueryParameters.Select = UserSelections;
    requestConfig.QueryParameters.Filter = $"identities/any(id:id/issuer eq ' ' and id/issuerAssignedId eq '{escapedEmail }')"
});

But this creates an odd situation since escaping the emailAddress with a direct request to _GraphServiceClient.Users.GetAsync() breaks the direct request. So I need to handle the email addresses differently which creates a confusing situation in the code base.

Debug output

No response

Configuration

  • OS: Windows 11
  • architecture x64

Other information

No response

@noelroehrig noelroehrig added status:waiting-for-triage An issue that is yet to be reviewed or assigned type:bug A broken experience labels Oct 14, 2024
@noelroehrig noelroehrig changed the title Different URL escaping behaviour in batch and direct requests Different filter escaping behaviour in batch and direct requests Oct 14, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status:waiting-for-triage An issue that is yet to be reviewed or assigned type:bug A broken experience
Projects
None yet
Development

No branches or pull requests

1 participant