Describe the bug
After updating the MS Graph SDK from 5.37 to 5.105. Members@delta is empty during initial group delta sync. Below is the code snippet & response attached for both versions.
Expected behavior
Expecting members delta under Additional data as shown
"AdditionalData": { "members@delta": [ { "@odata.type": "#microsoft.graph.user", "id": "<Guid>" }, { "@odata.type": "#microsoft.graph.user", "id": "<Guid>" }, { "@odata.type": "#microsoft.graph.user", "id": "<Guid>" } ] },
with latest SDK getting the empty response as show below
"AdditionalData": { "members@delta": {} },
How to reproduce
Code used to reproduce.
private static async Task DebugGroupMembersSyncWorkflow(GraphServiceClient client, string groupId = null!, string deltaLink = null!)
{
var nextLink = deltaLink;
do
{
var deltaResponse = string.IsNullOrEmpty(nextLink)
? await GetDeltaResponse(client, groupId).ConfigureAwait(false)
: await GetDeltaResponse(client, new Uri(nextLink)).ConfigureAwait(false);
nextLink = deltaResponse.NextLink;
deltaLink = deltaResponse.DeltaLink;
if (!string.IsNullOrEmpty(nextLink))
{
var memberCount = deltaResponse.MemberIds.Count();
var removedCount = deltaResponse.RemovedMemberIds.Count();
var hasNextLink = !string.IsNullOrEmpty(deltaResponse.NextLink);
Console.WriteLine($"MemberIds count: {memberCount}, RemovedMemberIds count: {removedCount}, HasNextLink: {hasNextLink}");
}
Console.WriteLine($"HasNextLink: {!string.IsNullOrEmpty(deltaResponse.NextLink)}, HasDeltaLink: {!string.IsNullOrEmpty(deltaResponse.DeltaLink)}");
} while (!string.IsNullOrEmpty(nextLink));
Console.WriteLine($"Completed group members delta sync and has DeltaSyncLink {!string.IsNullOrEmpty(deltaLink)}");
Console.WriteLine("Would you want to run delta sync again using the delta link? (y/n): ");
var input = Console.ReadLine();
if(input == "y")
{
await DebugGroupMembersSyncWorkflow(client, deltaLink: deltaLink);
}
}
private static async Task<GroupMembersDeltaResponse> GetDeltaResponse(GraphServiceClient client, string groupId)
{
var deltaResponse = await client.Groups.Delta.GetAsDeltaGetResponseAsync(
config =>
{
config.QueryParameters.Filter = $"id eq '{groupId}'";
config.QueryParameters.Select = ["displayName", "members"];
},
CancellationToken.None).ConfigureAwait(false);
string serlializedResponse = System.Text.Json.JsonSerializer.Serialize(deltaResponse);
return deltaResponse!.GetGroupMembersDelta();
}
private static async Task<GroupMembersDeltaResponse> GetDeltaResponse(GraphServiceClient client, Uri url)
{
var deltaResponse = await client.Groups.Delta
.WithUrl(url.ToString())
.GetAsDeltaGetResponseAsync(cancellationToken: CancellationToken.None).ConfigureAwait(false);
ArgumentNullException.ThrowIfNull(deltaResponse, nameof(deltaResponse));
return deltaResponse.GetGroupMembersDelta();
}
private static GroupMembersDeltaResponse GetGroupMembersDelta(this Microsoft.Graph.Groups.Delta.DeltaGetResponse deltaGetResponse)
{
ArgumentNullException.ThrowIfNull(deltaGetResponse, nameof(deltaGetResponse));
var group = deltaGetResponse.Value?.FirstOrDefault();
if (group == null)
{
return new GroupMembersDeltaResponse
{
NextLink = deltaGetResponse.OdataNextLink ?? string.Empty,
DeltaLink = deltaGetResponse.OdataDeltaLink ?? string.Empty,
};
}
var groupMembersDeltaResponse = new GroupMembersDeltaResponse
{
GroupId = group.Id!,
GroupName = group.DisplayName!,
MemberIds = Enumerable.Empty<string>(),
RemovedMemberIds = Enumerable.Empty<string>(),
NextLink = deltaGetResponse.OdataNextLink!,
DeltaLink = deltaGetResponse.OdataDeltaLink!,
};
if (group.AdditionalData.TryGetValue("members@delta", out var membersDelta) && membersDelta is JsonElement membersDeltaElement)
{
var members = System.Text.Json.JsonSerializer.Deserialize<IEnumerable<GraphUserRef>>(membersDeltaElement.GetRawText());
groupMembersDeltaResponse.MemberIds = members?.Where(x => x.Removed == null).Select(x => x.Id) ?? Enumerable.Empty<string>();
groupMembersDeltaResponse.RemovedMemberIds = members?.Where(x => x.Removed != null).Select(x => x.Id) ?? Enumerable.Empty<string>();
}
return groupMembersDeltaResponse;
}
}
public record GraphUserRef(
[JsonPropertyName("@odata.type")] string ODataType,
[JsonPropertyName("id")] string Id,
[JsonPropertyName("@removed")] string? Removed
);
public class GroupMembersDeltaResponse
{
/// <summary>
/// Gets or sets the unique identifier of the group.
/// </summary>
public string GroupId { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the name of the group.
/// </summary>
public string GroupName { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the member IDs of the group.
/// </summary>
public IEnumerable<string> MemberIds { get; set; } = Array.Empty<string>();
/// <summary>
/// Gets or sets the removed member IDs of the group.
/// </summary>
public IEnumerable<string> RemovedMemberIds { get; set; } = Array.Empty<string>();
/// <summary>
/// Gets or sets the next link for pagination.
/// </summary>
public string NextLink { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the URL that can be used to retrieve changes since the last request.
/// </summary>
public string DeltaLink { get; set; } = string.Empty;
}
SDK Version
5.105
Latest version known to work for scenario above?
5.37
Known Workarounds
Downgrading the package works for now.
Debug output
Click to expand log
```
</details>
### Configuration
_No response_
### Other information
_No response_
Describe the bug
After updating the MS Graph SDK from 5.37 to 5.105. Members@delta is empty during initial group delta sync. Below is the code snippet & response attached for both versions.
Expected behavior
Expecting members delta under Additional data as shown
"AdditionalData": { "members@delta": [ { "@odata.type": "#microsoft.graph.user", "id": "<Guid>" }, { "@odata.type": "#microsoft.graph.user", "id": "<Guid>" }, { "@odata.type": "#microsoft.graph.user", "id": "<Guid>" } ] },with latest SDK getting the empty response as show below
"AdditionalData": { "members@delta": {} },How to reproduce
Code used to reproduce.
SDK Version
5.105
Latest version known to work for scenario above?
5.37
Known Workarounds
Downgrading the package works for now.
Debug output
Click to expand log
```