From ba6387cc882b2fb95e0b4f5e303edf45a1a5ac88 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Thu, 16 Mar 2023 16:20:13 +0200 Subject: [PATCH 1/7] Something graph --- src/lib/ConsoleApp2/ConsoleApp2.csproj | 14 ++++++++++++ src/lib/ConsoleApp2/Program.cs | 31 ++++++++++++++++++++++++++ src/lib/PnP.Framework.sln | 10 +++++++-- 3 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 src/lib/ConsoleApp2/ConsoleApp2.csproj create mode 100644 src/lib/ConsoleApp2/Program.cs diff --git a/src/lib/ConsoleApp2/ConsoleApp2.csproj b/src/lib/ConsoleApp2/ConsoleApp2.csproj new file mode 100644 index 000000000..a0856280c --- /dev/null +++ b/src/lib/ConsoleApp2/ConsoleApp2.csproj @@ -0,0 +1,14 @@ + + + + Exe + net6.0 + enable + enable + + + + + + + diff --git a/src/lib/ConsoleApp2/Program.cs b/src/lib/ConsoleApp2/Program.cs new file mode 100644 index 000000000..8dfc4859c --- /dev/null +++ b/src/lib/ConsoleApp2/Program.cs @@ -0,0 +1,31 @@ +using Microsoft.SharePoint.Client; +using PnP.Framework; +using System.Security; + +namespace ConsoleApp2 +{ + internal class Program + { + static void Main(string[] args) + { + Console.WriteLine("Hello, World!"); + + var username = "gautam@gautamdev.onmicrosoft.com"; + var pass = "wQ5FROvgTNp121mVUNhrdrPOuyUa9OiO"; + + var securePassword = new SecureString(); + + foreach (char c in pass) + securePassword.AppendChar(c); + + using (var authenticationMgr = new AuthenticationManager(username, securePassword)) + { + var ctx = authenticationMgr.GetContext("https://gautamdev.sharepoint.com/sites/testtz1"); + + ctx.Web.EnsureProperty(w => w.Title); + + var value = ctx.Web; + } + } + } +} \ No newline at end of file diff --git a/src/lib/PnP.Framework.sln b/src/lib/PnP.Framework.sln index 5c1bdd30a..815f755dc 100644 --- a/src/lib/PnP.Framework.sln +++ b/src/lib/PnP.Framework.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.30503.244 +# Visual Studio Version 17 +VisualStudioVersion = 17.4.33213.308 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{C3C026AD-0B78-4779-9DC4-F875B42A48F4}" ProjectSection(SolutionItems) = preProject @@ -14,6 +14,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PnP.Framework.Test", "PnP.F EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PnP.Framework.Modernization.Test", "PnP.Framework.Modernization.Test\PnP.Framework.Modernization.Test.csproj", "{2185B0F4-9423-4458-BFB6-7502D98A4259}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleApp2", "ConsoleApp2\ConsoleApp2.csproj", "{3DC23543-2BB5-4FDF-842C-CE9058EFF25E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -32,6 +34,10 @@ Global {2185B0F4-9423-4458-BFB6-7502D98A4259}.Debug|Any CPU.Build.0 = Debug|Any CPU {2185B0F4-9423-4458-BFB6-7502D98A4259}.Release|Any CPU.ActiveCfg = Release|Any CPU {2185B0F4-9423-4458-BFB6-7502D98A4259}.Release|Any CPU.Build.0 = Release|Any CPU + {3DC23543-2BB5-4FDF-842C-CE9058EFF25E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3DC23543-2BB5-4FDF-842C-CE9058EFF25E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3DC23543-2BB5-4FDF-842C-CE9058EFF25E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3DC23543-2BB5-4FDF-842C-CE9058EFF25E}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From c83e8b3ed7d991ca05d16ad0ade1652917263860 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Thu, 16 Mar 2023 16:20:29 +0200 Subject: [PATCH 2/7] Something grpah --- src/lib/PnP.Framework/Graph/GraphUtility.cs | 53 +++-- src/lib/PnP.Framework/Graph/GroupsUtility.cs | 184 ++++++++++-------- .../PnP.Framework/Graph/PnPHttpProvider.cs | 92 ++++----- .../Graph/SubscriptionsUtility.cs | 64 ++---- .../Graph/UnifiedGroupsUtility.cs | 135 ++++++------- src/lib/PnP.Framework/Graph/UsersUtility.cs | 13 +- src/lib/PnP.Framework/PnP.Framework.csproj | 32 +-- .../ObjectHandlers/TokenParser.cs | 5 +- 8 files changed, 281 insertions(+), 297 deletions(-) diff --git a/src/lib/PnP.Framework/Graph/GraphUtility.cs b/src/lib/PnP.Framework/Graph/GraphUtility.cs index 76e1ef47b..cc0ee409d 100644 --- a/src/lib/PnP.Framework/Graph/GraphUtility.cs +++ b/src/lib/PnP.Framework/Graph/GraphUtility.cs @@ -1,11 +1,28 @@ using Microsoft.Graph; +using Microsoft.Graph.Models; +using Microsoft.Graph.Models.ODataErrors; +using Microsoft.Kiota.Abstractions.Authentication; using PnP.Framework.Diagnostics; using System; -using System.Net.Http.Headers; +using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; namespace PnP.Framework.Graph { + public class TokenProvider : IAccessTokenProvider + { + public Task GetAuthorizationTokenAsync(Uri uri, Dictionary additionalAuthenticationContext = default, + CancellationToken cancellationToken = default) + { + var token = AccessToken; + // get the token and return it in your own way + return Task.FromResult(token); + } + public string AccessToken { get; set; } + public AllowedHostsValidator AllowedHostsValidator { get; } + } + /// /// Utility class to perform Graph operations. /// @@ -30,18 +47,24 @@ public static GraphServiceClient CreateGraphClient(string accessToken, int retry // Creates a new GraphServiceClient instance using a custom PnPHttpProvider // which natively supports retry logic for throttled requests // Default are 10 retries with a base delay of 500ms - var result = new GraphServiceClient(baseUrl, new DelegateAuthenticationProvider( - async (requestMessage) => - { - await Task.Run(() => - { - if (!string.IsNullOrEmpty(accessToken)) - { - // Configure the HTTP bearer Authorization Header - requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", accessToken); - } - }); - }), new PnPHttpProvider(retryCount, delay)); + //var result = new GraphServiceClient(baseUrl, new DelegateAuthenticationProvider( + // async (requestMessage) => + // { + // await Task.Run(() => + // { + // if (!string.IsNullOrEmpty(accessToken)) + // { + // // Configure the HTTP bearer Authorization Header + // requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", accessToken); + // } + // }); + // }), new PnPHttpProvider(retryCount, delay)); + + var tokenProvider = new TokenProvider(); + tokenProvider.AccessToken = accessToken; + + var authenticationProvider = new BaseBearerTokenAuthenticationProvider(tokenProvider); + var result = new GraphServiceClient(authenticationProvider, baseUrl); return (result); } @@ -86,9 +109,9 @@ public static Invitation InviteGuestUser(string accessToken, string guestUserEma // Create the graph client and send the invitation. GraphServiceClient graphClient = CreateGraphClient(accessToken, azureEnvironment: azureEnvironment); - inviteUserResponse = graphClient.Invitations.Request().AddAsync(invite).Result; + inviteUserResponse = graphClient.Invitations.PostAsync(invite).Result; } - catch (ServiceException ex) + catch (ODataError ex) { Log.Error(Constants.LOGGING_SOURCE, CoreResources.GraphExtensions_ErrorOccured, ex.Error.Message); throw; diff --git a/src/lib/PnP.Framework/Graph/GroupsUtility.cs b/src/lib/PnP.Framework/Graph/GroupsUtility.cs index d028267bf..7369415b0 100644 --- a/src/lib/PnP.Framework/Graph/GroupsUtility.cs +++ b/src/lib/PnP.Framework/Graph/GroupsUtility.cs @@ -1,4 +1,6 @@ using Microsoft.Graph; +using Microsoft.Graph.Models; +using Microsoft.Graph.Models.ODataErrors; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using PnP.Framework.Diagnostics; @@ -7,6 +9,7 @@ using PnP.Framework.Utilities.Graph; using System; using System.Collections.Generic; +using System.Diagnostics.Metrics; using System.Linq; using System.Net.Http.Headers; using System.Threading.Tasks; @@ -30,23 +33,24 @@ public static class GroupsUtility /// Azure environment to use, needed to get the correct Microsoft Graph URL /// private static GraphServiceClient CreateGraphClient(string accessToken, int retryCount = defaultRetryCount, int delay = defaultDelay, AzureEnvironment azureEnvironment = AzureEnvironment.Production) - { + { // Creates a new GraphServiceClient instance using a custom PnPHttpProvider // which natively supports retry logic for throttled requests // Default are 10 retries with a base delay of 500ms - var result = new GraphServiceClient($"{AuthenticationManager.GetGraphBaseEndPoint(azureEnvironment)}v1.0", new DelegateAuthenticationProvider( - async (requestMessage) => - { - await Task.Run(() => - { - if (!String.IsNullOrEmpty(accessToken)) - { - // Configure the HTTP bearer Authorization Header - requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", accessToken); - } - }); - }), new PnPHttpProvider(retryCount, delay)); - + //var result = new GraphServiceClient($"{AuthenticationManager.GetGraphBaseEndPoint(azureEnvironment)}v1.0", new DelegateAuthenticationProvider( + // async (requestMessage) => + // { + // await Task.Run(() => + // { + // if (!String.IsNullOrEmpty(accessToken)) + // { + // // Configure the HTTP bearer Authorization Header + // requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", accessToken); + // } + // }); + // }), new PnPHttpProvider(retryCount, delay)); + + var result = GraphUtility.CreateGraphClient(accessToken, retryCount, delay, azureEnvironment); return (result); } @@ -69,7 +73,7 @@ public static GroupEntity CreateGroup(string displayName, string description, st string accessToken, string[] owners = null, string[] members = null, int retryCount = 10, int delay = 500, AzureEnvironment azureEnvironment = AzureEnvironment.Production) { GroupEntity result = null; - + var baseUrl = $"https://{AuthenticationManager.GetGraphEndPoint(azureEnvironment)}/v1.0"; if (String.IsNullOrEmpty(displayName)) { throw new ArgumentNullException(nameof(displayName)); @@ -95,7 +99,7 @@ public static GroupEntity CreateGroup(string displayName, string description, st var graphClient = CreateGraphClient(accessToken, retryCount, delay, azureEnvironment); // Prepare the group resource object - var newGroup = new GroupExtended + var newGroup = new Group { DisplayName = displayName, Description = string.IsNullOrEmpty(description) ? null : description, @@ -109,7 +113,7 @@ public static GroupEntity CreateGroup(string displayName, string description, st var users = GetUsers(graphClient, owners); if (users != null && users.Count > 0) { - newGroup.OwnersODataBind = users.Select(u => string.Format("{1}/users/{0}", u.Id, graphClient.BaseUrl)).ToArray(); + newGroup.Owners = users; } } @@ -118,12 +122,12 @@ public static GroupEntity CreateGroup(string displayName, string description, st var users = GetUsers(graphClient, members); if (users != null && users.Count > 0) { - newGroup.MembersODataBind = users.Select(u => string.Format("{1}/users/{0}", u.Id, graphClient.BaseUrl)).ToArray(); + newGroup.Members = users.Select(u => string.Format("{1}/users/{0}", u.Id, baseUrl)).ToArray(); } } // Create the group - Microsoft.Graph.Group addedGroup = await graphClient.Groups.Request().AddAsync(newGroup); + Microsoft.Graph.Models.Group addedGroup = await graphClient.Groups.PostAsync(newGroup); if (addedGroup != null) { @@ -141,7 +145,7 @@ public static GroupEntity CreateGroup(string displayName, string description, st }).GetAwaiter().GetResult(); } - catch (ServiceException ex) + catch (ODataError ex) { Log.Error(Constants.LOGGING_SOURCE, CoreResources.GraphExtensions_ErrorOccured, ex.Error.Message); throw; @@ -156,24 +160,30 @@ public static GroupEntity CreateGroup(string displayName, string description, st /// GraphClient instance to use to communicate with the Microsoft Graph /// Id of the group which needs the owners added /// If set to true, all existing members which are not specified through will be removed as a member from the group - private static async Task UpdateMembers(string[] members, GraphServiceClient graphClient, string groupId, bool removeOtherMembers) + private static async Task UpdateMembers(string[] members, GraphServiceClient graphClient, string groupId, bool removeOtherMembers, AzureEnvironment azureEnvironment = AzureEnvironment.Production) { + var baseUrl = $"https://{AuthenticationManager.GetGraphEndPoint(azureEnvironment)}/v1.0"; foreach (var m in members) { // Search for the user object - var memberQuery = await graphClient.Users - .Request() - .Filter($"userPrincipalName eq '{Uri.EscapeDataString(m.Replace("'", "''"))}'") - .GetAsync(); + var memberQuery = await graphClient.Users + .GetAsync(requestConfiguration => + { + requestConfiguration.QueryParameters.Filter = $"userPrincipalName eq '{Uri.EscapeDataString(m.Replace("'", "''"))}'"; + }); - var member = memberQuery.FirstOrDefault(); + var member = memberQuery.Value.FirstOrDefault(); if (member != null) { try - { + { + var expectedRequestBody = new ReferenceCreate() + { + OdataId = string.Format("{0}/directoryObjects/{1}", string.Format(baseUrl, "v1.0"), member.Id), + }; // And if any, add it to the collection of group's owners - await graphClient.Groups[groupId].Members.References.Request().AddAsync(member); + await graphClient.Groups[groupId].Members.Ref.PostAsync(expectedRequestBody); } catch (Exception ex) { @@ -199,23 +209,28 @@ private static async Task UpdateMembers(string[] members, GraphServiceClient gra } // Remove any leftover member - var fullListOfMembers = await graphClient.Groups[groupId].Members.Request().Select("userPrincipalName, Id").GetAsync(); + var fullListOfMembers = await graphClient.Groups[groupId].Members + .GetAsync(requestConfiguration => + { + requestConfiguration.QueryParameters.Select = new string[] { "userPrincipalName", "Id" }; + }); + var pageExists = true; while (pageExists) { - foreach (var member in fullListOfMembers) + foreach (var member in fullListOfMembers.Value) { - var currentMemberPrincipalName = (member as Microsoft.Graph.User)?.UserPrincipalName; + var currentMemberPrincipalName = (member as Microsoft.Graph.Models.User)?.UserPrincipalName; if (!string.IsNullOrEmpty(currentMemberPrincipalName) && !members.Contains(currentMemberPrincipalName, StringComparer.InvariantCultureIgnoreCase)) { try { // If it is not in the list of current owners, just remove it - await graphClient.Groups[groupId].Members[member.Id].Reference.Request().DeleteAsync(); + await graphClient.Groups[groupId].Members[member.Id].Ref.DeleteAsync(); } - catch (ServiceException ex) + catch (ODataError ex) { if (ex.Error.Code == "Request_BadRequest") { @@ -229,9 +244,9 @@ private static async Task UpdateMembers(string[] members, GraphServiceClient gra } } - if (fullListOfMembers.NextPageRequest != null) + if (fullListOfMembers.OdataNextLink != null) { - fullListOfMembers = await fullListOfMembers.NextPageRequest.GetAsync(); + //fullListOfMembers = await fullListOfMembers.OdataNextLink.GetAsync(); } else { @@ -247,24 +262,30 @@ private static async Task UpdateMembers(string[] members, GraphServiceClient gra /// GraphClient instance to use to communicate with the Microsoft Graph /// Id of the group which needs the owners added /// If set to true, all existing owners which are not specified through will be removed as an owner from the group - private static async Task UpdateOwners(string[] owners, GraphServiceClient graphClient, string groupId, bool removeOtherOwners) + private static async Task UpdateOwners(string[] owners, GraphServiceClient graphClient, string groupId, bool removeOtherOwners, AzureEnvironment azureEnvironment = AzureEnvironment.Production) { + var baseUrl = $"https://{AuthenticationManager.GetGraphEndPoint(azureEnvironment)}/1.0"; foreach (var o in owners) { // Search for the user object var ownerQuery = await graphClient.Users - .Request() - .Filter($"userPrincipalName eq '{Uri.EscapeDataString(o.Replace("'", "''"))}'") - .GetAsync(); - - var owner = ownerQuery.FirstOrDefault(); + .GetAsync(requestConfiguration => + { + requestConfiguration.QueryParameters.Filter = $"userPrincipalName eq '{Uri.EscapeDataString(o.Replace("'", "''"))}'"; + }); + + var owner = ownerQuery?.Value.FirstOrDefault(); if (owner != null) { try { + var expectedRequestBody = new ReferenceCreate() + { + OdataId = string.Format("{0}/directoryObjects/{1}", string.Format(baseUrl, "v1.0"), owner.Id), + }; // And if any, add it to the collection of group's owners - await graphClient.Groups[groupId].Owners.References.Request().AddAsync(owner); + await graphClient.Groups[groupId].Owners.Ref.PostAsync(expectedRequestBody); } catch (Exception ex) { @@ -297,7 +318,7 @@ private static async Task UpdateOwners(string[] owners, GraphServiceClient graph { foreach (var owner in fullListOfOwners) { - var currentOwnerPrincipalName = (owner as Microsoft.Graph.User)?.UserPrincipalName; + var currentOwnerPrincipalName = (owner as Microsoft.Graph.Models.User)?.UserPrincipalName; if (!string.IsNullOrEmpty(currentOwnerPrincipalName) && !owners.Contains(currentOwnerPrincipalName, StringComparer.InvariantCultureIgnoreCase)) { @@ -306,7 +327,7 @@ private static async Task UpdateOwners(string[] owners, GraphServiceClient graph // If it is not in the list of current owners, just remove it await graphClient.Groups[groupId].Owners[owner.Id].Reference.Request().DeleteAsync(); } - catch (ServiceException ex) + catch (ODataError ex) { if (ex.Error.Code == "Request_BadRequest") { @@ -371,7 +392,7 @@ public static void SetGroupVisibility(string groupId, string accessToken, bool? contentType: "application/json", accessToken: accessToken); } - catch (ServiceException ex) + catch (ODataError ex) { Log.Error(Constants.LOGGING_SOURCE, CoreResources.GraphExtensions_ErrorOccured, ex.Error.Message); throw; @@ -405,8 +426,7 @@ public static bool UpdateGroup(string groupId, { var graphClient = CreateGraphClient(accessToken, retryCount, delay, azureEnvironment); - var groupToUpdate = await graphClient.Groups[groupId] - .Request() + var groupToUpdate = await graphClient.Groups[groupId] .GetAsync(); // Workaround for the PATCH request, needed after update to Graph Library @@ -467,9 +487,7 @@ public static bool UpdateGroup(string groupId, // If the Group has to be updated, just do it if (updateGroup) { - var updatedGroup = await graphClient.Groups[groupId] - .Request() - .UpdateAsync(clonedGroup); + var updatedGroup = await graphClient.Groups[groupId].PatchAsync(groupToUpdate); groupUpdated = true; } @@ -481,7 +499,7 @@ public static bool UpdateGroup(string groupId, }).GetAwaiter().GetResult(); } - catch (ServiceException ex) + catch (ODataError ex) { Log.Error(Constants.LOGGING_SOURCE, CoreResources.GraphExtensions_ErrorOccured, ex.Error.Message); throw; @@ -514,11 +532,11 @@ public static void DeleteGroup(string groupId, string accessToken, int retryCoun Task.Run(async () => { var graphClient = CreateGraphClient(accessToken, retryCount, delay, azureEnvironment); - await graphClient.Groups[groupId].Request().DeleteAsync(); + await graphClient.Groups[groupId].DeleteAsync(); }).GetAwaiter().GetResult(); } - catch (ServiceException ex) + catch (ODataError ex) { Log.Error(Constants.LOGGING_SOURCE, CoreResources.GraphExtensions_ErrorOccured, ex.Error.Message); throw; @@ -556,7 +574,7 @@ public static GroupEntity GetGroup(string groupId, string accessToken, int retry var graphClient = CreateGraphClient(accessToken, retryCount, delay, azureEnvironment); - var g = await graphClient.Groups[groupId].Request().GetAsync(); + var g = await graphClient.Groups[groupId].GetAsync(); group = new GroupEntity { @@ -574,7 +592,7 @@ public static GroupEntity GetGroup(string groupId, string accessToken, int retry }).GetAwaiter().GetResult(); } - catch (ServiceException ex) + catch (ODataError ex) { Log.Error(Constants.LOGGING_SOURCE, CoreResources.GraphExtensions_ErrorOccured, ex.Error.Message); throw; @@ -667,7 +685,7 @@ public static List GetGroups(string accessToken, return (groups); }).GetAwaiter().GetResult(); } - catch (ServiceException ex) + catch (ODataError ex) { Log.Error(Constants.LOGGING_SOURCE, CoreResources.GraphExtensions_ErrorOccured, ex.Error.Message); throw; @@ -706,7 +724,7 @@ public static List GetGroupMembers(GroupEntity group, string accessTo var graphClient = CreateGraphClient(accessToken, retryCount, delay, azureEnvironment); // Get the members of the group - groupUsersCollection = await graphClient.Groups[group.GroupId].Members.Request().GetAsync(); + groupUsersCollection = await graphClient.Groups[group.GroupId].Members.GetAsync(); if (groupUsersCollection.CurrentPage != null && groupUsersCollection.CurrentPage.Count > 0) { groupGraphUsers = new List(); @@ -733,7 +751,7 @@ public static List GetGroupMembers(GroupEntity group, string accessTo { switch(usr) { - case Microsoft.Graph.User userType: + case Microsoft.Graph.Models.User userType: groupUsers.Add(new GroupUser { UserPrincipalName = userType.UserPrincipalName != null ? userType.UserPrincipalName : string.Empty, @@ -742,7 +760,7 @@ public static List GetGroupMembers(GroupEntity group, string accessTo }); break; - case Microsoft.Graph.Group groupType: + case Microsoft.Graph.Models.Group groupType: groupUsers.Add(new GroupUser { UserPrincipalName = groupType.Id != null ? groupType.Id : string.Empty, @@ -758,7 +776,7 @@ public static List GetGroupMembers(GroupEntity group, string accessTo }).GetAwaiter().GetResult(); } - catch (ServiceException ex) + catch (ODataError ex) { Log.Error(Constants.LOGGING_SOURCE, CoreResources.GraphExtensions_ErrorOccured, ex.Error.Message); throw; @@ -793,7 +811,7 @@ public static void AddGroupOwners(string groupId, string[] owners, string access }).GetAwaiter().GetResult(); } - catch (ServiceException ex) + catch (ODataError ex) { Log.Error(Constants.LOGGING_SOURCE, CoreResources.GraphExtensions_ErrorOccured, ex.Error.Message); throw; @@ -827,7 +845,7 @@ public static void AddGroupMembers(string groupId, string[] members, string acce }).GetAwaiter().GetResult(); } - catch (ServiceException ex) + catch (ODataError ex) { Log.Error(Constants.LOGGING_SOURCE, CoreResources.GraphExtensions_ErrorOccured, ex.Error.Message); throw; @@ -860,20 +878,21 @@ public static void RemoveGroupMembers(string groupId, string[] members, string a { // Search for the user object var memberQuery = await graphClient.Users - .Request() - .Filter($"userPrincipalName eq '{Uri.EscapeDataString(m.Replace("'", "''"))}'") - .GetAsync(); + .GetAsync(requestConfiguration => + { + requestConfiguration.QueryParameters.Filter = $"userPrincipalName eq '{Uri.EscapeDataString(m.Replace("'", "''"))}'"; + }); - var member = memberQuery.FirstOrDefault(); + var member = memberQuery?.Value?.FirstOrDefault(); if (member != null) { try { // If it is not in the list of current members, just remove it - await graphClient.Groups[groupId].Members[member.Id].Reference.Request().DeleteAsync(); + await graphClient.Groups[groupId].Members[member.Id].Ref.DeleteAsync(); } - catch (ServiceException ex) + catch (ODataError ex) { if (ex.Error.Code == "Request_BadRequest") { @@ -889,7 +908,7 @@ public static void RemoveGroupMembers(string groupId, string[] members, string a }).GetAwaiter().GetResult(); } - catch (ServiceException ex) + catch (ODataError ex) { Log.Error(Constants.LOGGING_SOURCE, CoreResources.GraphExtensions_ErrorOccured, ex.Error.Message); throw; @@ -922,22 +941,23 @@ public static void RemoveGroupOwners(string groupId, string[] owners, string acc { // Search for the user object var memberQuery = await graphClient.Users - .Request() - .Filter($"userPrincipalName eq '{Uri.EscapeDataString(m.Replace("'", "''"))}'") - .GetAsync(); + .GetAsync(requestConfiguration => + { + requestConfiguration.QueryParameters.Filter = $"userPrincipalName eq '{Uri.EscapeDataString(m.Replace("'", "''"))}'"; + }); - var member = memberQuery.FirstOrDefault(); + var member = memberQuery?.Value?.FirstOrDefault(); if (member != null) { // If it is not in the list of current owners, just remove it - await graphClient.Groups[groupId].Owners[member.Id].Reference.Request().DeleteAsync(); + await graphClient.Groups[groupId].Owners[member.Id].Ref.DeleteAsync(); } } }).GetAwaiter().GetResult(); } - catch (ServiceException ex) + catch (ODataError ex) { Log.Error(Constants.LOGGING_SOURCE, CoreResources.GraphExtensions_ErrorOccured, ex.Error.Message); throw; @@ -963,7 +983,7 @@ public static void ClearGroupOwners(string groupId, string accessToken, int retr var currentOwners = GetGroupOwners(new GroupEntity { GroupId = groupId }, accessToken, retryCount, delay); RemoveGroupOwners(groupId, currentOwners.Select(o => o.UserPrincipalName).ToArray(), accessToken, retryCount, delay); } - catch (ServiceException ex) + catch (ODataError ex) { Log.Error(Constants.LOGGING_SOURCE, CoreResources.GraphExtensions_ErrorOccured, ex.Error.Message); throw; @@ -991,7 +1011,7 @@ public static void ClearGroupMembers(string groupId, string accessToken, int ret RemoveGroupMembers(groupId, currentMembers.Select(o => o.UserPrincipalName).ToArray(), accessToken, retryCount, delay); } - catch (ServiceException ex) + catch (ODataError ex) { Log.Error(Constants.LOGGING_SOURCE, CoreResources.GraphExtensions_ErrorOccured, ex.Error.Message); throw; @@ -1025,7 +1045,7 @@ public static List GetGroupOwners(GroupEntity group, string accessTok var graphClient = CreateGraphClient(accessToken, retryCount, delay, azureEnvironment); // Get the owners of an Office 365 group. - groupUsersCollection = await graphClient.Groups[group.GroupId].Owners.Request().GetAsync(); + groupUsersCollection = await graphClient.Groups[group.GroupId].Owners.GetAsync(); if (groupUsersCollection.CurrentPage != null && groupUsersCollection.CurrentPage.Count > 0) { groupGraphUsers = new List(); @@ -1050,7 +1070,7 @@ public static List GetGroupOwners(GroupEntity group, string accessTok { switch(usr) { - case Microsoft.Graph.User userType: + case Microsoft.Graph.Models.User userType: groupUsers.Add(new GroupUser { UserPrincipalName = userType.UserPrincipalName != null ? userType.UserPrincipalName : string.Empty, @@ -1059,7 +1079,7 @@ public static List GetGroupOwners(GroupEntity group, string accessTok }); break; - case Microsoft.Graph.Group groupType: + case Microsoft.Graph.Models.Group groupType: groupUsers.Add(new GroupUser { UserPrincipalName = groupType.Id != null ? groupType.Id : string.Empty, @@ -1075,7 +1095,7 @@ public static List GetGroupOwners(GroupEntity group, string accessTok }).GetAwaiter().GetResult(); } - catch (ServiceException ex) + catch (ODataError ex) { Log.Error(Constants.LOGGING_SOURCE, CoreResources.GraphExtensions_ErrorOccured, ex.Error.Message); throw; @@ -1197,7 +1217,7 @@ private static List GetUsers(GraphServiceClient graphClient, string[] grou usersResult.Add(user); } } - catch (ServiceException) + catch (ODataError) { // skip, group provisioning shouldnt stop because of error in user object } diff --git a/src/lib/PnP.Framework/Graph/PnPHttpProvider.cs b/src/lib/PnP.Framework/Graph/PnPHttpProvider.cs index 8a4ab3e66..ee5500757 100644 --- a/src/lib/PnP.Framework/Graph/PnPHttpProvider.cs +++ b/src/lib/PnP.Framework/Graph/PnPHttpProvider.cs @@ -1,52 +1,52 @@ -using Microsoft.Graph; -using PnP.Framework.Utilities; -using System; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; +//using Microsoft.Graph; +//using PnP.Framework.Utilities; +//using System; +//using System.Net.Http; +//using System.Threading; +//using System.Threading.Tasks; -namespace PnP.Framework.Graph -{ - /// - /// Class that deals with PnPHttpProvider methods - /// - public class PnPHttpProvider : HttpProvider, IHttpProvider - { - private readonly string _userAgent; - private readonly PnPHttpRetryHandler _retryHandler; +//namespace PnP.Framework.Graph +//{ +// /// +// /// Class that deals with PnPHttpProvider methods +// /// +// public class PnPHttpProvider : HttpProvider, IHttpProvider +// { +// private readonly string _userAgent; +// private readonly PnPHttpRetryHandler _retryHandler; - /// - /// Constructor for the PnPHttpProvider class - /// - /// Maximum retry Count - /// Delay Time - /// User-Agent string to set - public PnPHttpProvider(int retryCount = 10, int delay = 500, string userAgent = null) : base() - { - if (retryCount <= 0) - throw new ArgumentException("Provide a retry count greater than zero."); +// /// +// /// Constructor for the PnPHttpProvider class +// /// +// /// Maximum retry Count +// /// Delay Time +// /// User-Agent string to set +// public PnPHttpProvider(int retryCount = 10, int delay = 500, string userAgent = null) : base() +// { +// if (retryCount <= 0) +// throw new ArgumentException("Provide a retry count greater than zero."); - if (delay <= 0) - throw new ArgumentException("Provide a delay greater than zero."); +// if (delay <= 0) +// throw new ArgumentException("Provide a delay greater than zero."); - this._userAgent = userAgent; - this._retryHandler = new PnPHttpRetryHandler(retryCount, delay); - } +// this._userAgent = userAgent; +// this._retryHandler = new PnPHttpRetryHandler(retryCount, delay); +// } - /// - /// Custom implementation of the IHttpProvider.SendAsync method to handle retry logic - /// - /// The HTTP Request Message - /// The completion option - /// The cancellation token - /// The result of the asynchronous request - /// See here for further details: https://graph.microsoft.io/en-us/docs/overview/errors - async Task IHttpProvider.SendAsync(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationToken cancellationToken) - { - // Add the PnP User Agent string - request.Headers.UserAgent.TryParseAdd(string.IsNullOrEmpty(_userAgent) ? $"{PnPCoreUtilities.PnPCoreUserAgent}" : _userAgent); +// /// +// /// Custom implementation of the IHttpProvider.SendAsync method to handle retry logic +// /// +// /// The HTTP Request Message +// /// The completion option +// /// The cancellation token +// /// The result of the asynchronous request +// /// See here for further details: https://graph.microsoft.io/en-us/docs/overview/errors +// async Task IHttpProvider.SendAsync(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationToken cancellationToken) +// { +// // Add the PnP User Agent string +// request.Headers.UserAgent.TryParseAdd(string.IsNullOrEmpty(_userAgent) ? $"{PnPCoreUtilities.PnPCoreUserAgent}" : _userAgent); - return await _retryHandler.SendRetryAsync(request, (r) => base.SendAsync(r, completionOption, cancellationToken), cancellationToken); - } - } -} +// return await _retryHandler.SendRetryAsync(request, (r) => base.SendAsync(r, completionOption, cancellationToken), cancellationToken); +// } +// } +//} diff --git a/src/lib/PnP.Framework/Graph/SubscriptionsUtility.cs b/src/lib/PnP.Framework/Graph/SubscriptionsUtility.cs index 026d7dfc8..c5124c36e 100644 --- a/src/lib/PnP.Framework/Graph/SubscriptionsUtility.cs +++ b/src/lib/PnP.Framework/Graph/SubscriptionsUtility.cs @@ -1,4 +1,6 @@ using Microsoft.Graph; +using Microsoft.Graph.Models; +using Microsoft.Graph.Models.ODataErrors; using PnP.Framework.Diagnostics; using System; using System.Collections.Generic; @@ -33,7 +35,6 @@ public static Model.Subscription GetSubscription(string accessToken, Guid subscr var graphClient = GraphUtility.CreateGraphClient(accessToken, retryCount, delay, azureEnvironment: azureEnvironment); var subscription = await graphClient.Subscriptions[subscriptionId.ToString()] - .Request() .GetAsync(); var subscriptionModel = MapGraphEntityToModel(subscription); @@ -42,7 +43,7 @@ public static Model.Subscription GetSubscription(string accessToken, Guid subscr return result; } - catch (ServiceException ex) + catch (ODataError ex) { Log.Error(Constants.LOGGING_SOURCE, CoreResources.GraphExtensions_ErrorOccured, ex.Error.Message); throw; @@ -76,42 +77,22 @@ public static Model.Subscription GetSubscription(string accessToken, Guid subscr var graphClient = GraphUtility.CreateGraphClient(accessToken, retryCount, delay, azureEnvironment: azureEnvironment); - var pagedSubscriptions = await graphClient.Subscriptions - .Request() - .GetAsync(); - - int pageCount = 0; - int currentIndex = 0; + var pagedSubscriptions = await graphClient.Subscriptions + .GetAsync(); - while (true) + var pageIterator = PageIterator.CreatePageIterator(graphClient, pagedSubscriptions, (subscription) => { - pageCount++; - - foreach (var s in pagedSubscriptions) - { - currentIndex++; - - if (currentIndex >= startIndex) - { - var subscription = MapGraphEntityToModel(s); - subscriptions.Add(subscription); - } - } - - if (pagedSubscriptions.NextPageRequest != null && currentIndex < endIndex) - { - pagedSubscriptions = await pagedSubscriptions.NextPageRequest.GetAsync(); - } - else - { - break; - } - } + var subscriptionModel = MapGraphEntityToModel(subscription); + subscriptions.Add(subscriptionModel); + return true; + }); + + await pageIterator.IterateAsync(); return subscriptions; }).GetAwaiter().GetResult(); } - catch (ServiceException ex) + catch (ODataError ex) { Log.Error(Constants.LOGGING_SOURCE, CoreResources.GraphExtensions_ErrorOccured, ex.Error.Message); throw; @@ -165,9 +146,8 @@ public static Model.Subscription CreateSubscription(Enums.GraphSubscriptionChang ClientState = clientState }; - var subscription = await graphClient.Subscriptions - .Request() - .AddAsync(newSubscription); + var subscription = await graphClient.Subscriptions + .PostAsync(newSubscription); if (subscription == null) { @@ -179,7 +159,7 @@ public static Model.Subscription CreateSubscription(Enums.GraphSubscriptionChang }).GetAwaiter().GetResult(); } - catch (ServiceException ex) + catch (ODataError ex) { Log.Error(Constants.LOGGING_SOURCE, CoreResources.GraphExtensions_ErrorOccured, ex.Error.Message); throw; @@ -219,9 +199,8 @@ public static Model.Subscription UpdateSubscription(string subscriptionId, DateT ExpirationDateTime = expirationDateTime }; - var subscription = await graphClient.Subscriptions[subscriptionId] - .Request() - .UpdateAsync(updatedSubscription); + var subscription = await graphClient.Subscriptions[subscriptionId] + .PatchAsync(updatedSubscription); if (subscription == null) { @@ -233,7 +212,7 @@ public static Model.Subscription UpdateSubscription(string subscriptionId, DateT }).GetAwaiter().GetResult(); } - catch (ServiceException ex) + catch (ODataError ex) { Log.Error(Constants.LOGGING_SOURCE, CoreResources.GraphExtensions_ErrorOccured, ex.Error.Message); throw; @@ -263,13 +242,12 @@ public static void DeleteSubscription(string subscriptionId, { var graphClient = GraphUtility.CreateGraphClient(accessToken, retryCount, delay); - await graphClient.Subscriptions[subscriptionId] - .Request() + await graphClient.Subscriptions[subscriptionId] .DeleteAsync(); }).GetAwaiter().GetResult(); } - catch (ServiceException ex) + catch (ODataError ex) { Log.Error(Constants.LOGGING_SOURCE, CoreResources.GraphExtensions_ErrorOccured, ex.Error.Message); throw; diff --git a/src/lib/PnP.Framework/Graph/UnifiedGroupsUtility.cs b/src/lib/PnP.Framework/Graph/UnifiedGroupsUtility.cs index e063a8ed4..fee6e4943 100644 --- a/src/lib/PnP.Framework/Graph/UnifiedGroupsUtility.cs +++ b/src/lib/PnP.Framework/Graph/UnifiedGroupsUtility.cs @@ -1,4 +1,6 @@ using Microsoft.Graph; +using Microsoft.Graph.Models; +using Microsoft.Graph.Models.ODataErrors; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using PnP.Framework.Diagnostics; @@ -42,22 +44,9 @@ private static GraphServiceClient CreateGraphClient(String accessToken, int retr { // Creates a new GraphServiceClient instance using a custom PnPHttpProvider // which natively supports retry logic for throttled requests - // Default are 10 retries with a base delay of 500ms + // Default are 10 retries with a base delay of 500ms - var baseUrl = $"https://{AuthenticationManager.GetGraphEndPoint(azureEnvironment)}/v1.0"; - - var result = new GraphServiceClient(baseUrl, new DelegateAuthenticationProvider( - async (requestMessage) => - { - await Task.Run(() => - { - if (!String.IsNullOrEmpty(accessToken)) - { - // Configure the HTTP bearer Authorization Header - requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", accessToken); - } - }); - }), new PnPHttpProvider(retryCount, delay)); + var result = GraphUtility.CreateGraphClient(accessToken, retryCount, delay, azureEnvironment: azureEnvironment); return (result); } @@ -90,7 +79,7 @@ public static string GetUnifiedGroupSiteUrl(string groupId, string accessToken, result = Convert.ToString(response["webUrl"]); } - catch (ServiceException ex) + catch (ODataError ex) { Log.Error(Constants.LOGGING_SOURCE, CoreResources.GraphExtensions_ErrorOccured, ex.Error.Message); throw; @@ -140,7 +129,7 @@ public static UnifiedGroupEntity CreateUnifiedGroup(string displayName, string d } var labels = new List(); - if(assignedLabels != null) + if (assignedLabels != null) { foreach (var label in assignedLabels) { @@ -153,7 +142,7 @@ public static UnifiedGroupEntity CreateUnifiedGroup(string displayName, string d } } } - + try { @@ -213,13 +202,13 @@ public static UnifiedGroupEntity CreateUnifiedGroup(string displayName, string d newGroup.AdditionalData.Add("resourceBehaviorOptions", new string[] { "WelcomeEmailDisabled" }); } - Microsoft.Graph.Group addedGroup = null; + Microsoft.Graph.Models.Group addedGroup = null; string modernSiteUrl = null; // Add the group to the collection of groups (if it does not exist) if (addedGroup == null) { - addedGroup = await graphClient.Groups.Request().AddAsync(newGroup); + addedGroup = await graphClient.Groups.PostAsync(newGroup); if (addedGroup != null) { @@ -306,7 +295,7 @@ public static UnifiedGroupEntity CreateUnifiedGroup(string displayName, string d }).GetAwaiter().GetResult(); } - catch (ServiceException ex) + catch (ODataError ex) { Log.Error(Constants.LOGGING_SOURCE, CoreResources.GraphExtensions_ErrorOccured, ex.Error.Message); throw; @@ -329,7 +318,6 @@ private static async Task UpdateMembers(string[] members, GraphServiceClient gra { // Search for the user object var memberQuery = await graphClient.Users - .Request() .Filter($"userPrincipalName eq '{Uri.EscapeDataString(m.Replace("'", "''"))}'") .GetAsync(); @@ -340,7 +328,7 @@ private static async Task UpdateMembers(string[] members, GraphServiceClient gra try { // And if any, add it to the collection of group's owners - await graphClient.Groups[groupId].Members.References.Request().AddAsync(member); + await graphClient.Groups[groupId].Members.Ref.PostAsync(member); } catch (Exception ex) { @@ -373,7 +361,7 @@ private static async Task UpdateMembers(string[] members, GraphServiceClient gra { foreach (var member in fullListOfMembers) { - var currentMemberPrincipalName = (member as Microsoft.Graph.User)?.UserPrincipalName; + var currentMemberPrincipalName = (member as Microsoft.Graph.Models.User)?.UserPrincipalName; if (!string.IsNullOrEmpty(currentMemberPrincipalName) && !members.Contains(currentMemberPrincipalName, StringComparer.InvariantCultureIgnoreCase)) { @@ -382,7 +370,7 @@ private static async Task UpdateMembers(string[] members, GraphServiceClient gra // If it is not in the list of current owners, just remove it await graphClient.Groups[groupId].Members[member.Id].Reference.Request().DeleteAsync(); } - catch (ServiceException ex) + catch (ODataError ex) { if (ex.Error.Code == "Request_BadRequest") { @@ -431,7 +419,7 @@ private static async Task UpdateOwners(string[] owners, GraphServiceClient graph try { // And if any, add it to the collection of group's owners - await graphClient.Groups[groupId].Owners.References.Request().AddAsync(owner); + await graphClient.Groups[groupId].Owners.Ref.PostAsync(owner); } catch (Exception ex) { @@ -464,7 +452,7 @@ private static async Task UpdateOwners(string[] owners, GraphServiceClient graph { foreach (var owner in fullListOfOwners) { - var currentOwnerPrincipalName = (owner as Microsoft.Graph.User)?.UserPrincipalName; + var currentOwnerPrincipalName = (owner as Microsoft.Graph.Models.User)?.UserPrincipalName; if (!string.IsNullOrEmpty(currentOwnerPrincipalName) && !owners.Contains(currentOwnerPrincipalName, StringComparer.InvariantCultureIgnoreCase)) { @@ -473,7 +461,7 @@ private static async Task UpdateOwners(string[] owners, GraphServiceClient graph // If it is not in the list of current owners, just remove it await graphClient.Groups[groupId].Owners[owner.Id].Reference.Request().DeleteAsync(); } - catch (ServiceException ex) + catch (ODataError ex) { if (ex.Error.Code == "Request_BadRequest") { @@ -539,7 +527,7 @@ public static void SetUnifiedGroupVisibility(string groupId, string accessToken, contentType: "application/json", accessToken: accessToken); } - catch (ServiceException ex) + catch (ODataError ex) { Log.Error(Constants.LOGGING_SOURCE, CoreResources.GraphExtensions_ErrorOccured, ex.Error.Message); throw; @@ -564,13 +552,10 @@ public static void RenewUnifiedGroup(string groupId, { var graphClient = CreateGraphClient(accessToken, retryCount, delay, azureEnvironment); - await graphClient.Groups[groupId] - .Renew() - .Request() - .PostAsync(); + await graphClient.Groups[groupId].Renew.PostAsync(); }).GetAwaiter().GetResult(); } - catch (ServiceException ex) + catch (ODataError ex) { Log.Error(Constants.LOGGING_SOURCE, CoreResources.GraphExtensions_ErrorOccured, ex.Error.Message); throw; @@ -607,7 +592,6 @@ public static bool UpdateUnifiedGroup(string groupId, var graphClient = CreateGraphClient(accessToken, retryCount, delay, azureEnvironment); var groupToUpdate = await graphClient.Groups[groupId] - .Request() .GetAsync(); // Workaround for the PATCH request, needed after update to Graph Library @@ -669,8 +653,7 @@ public static bool UpdateUnifiedGroup(string groupId, if (updateGroup) { var updatedGroup = await graphClient.Groups[groupId] - .Request() - .UpdateAsync(clonedGroup); + .PatchAsync(clonedGroup); groupUpdated = true; } @@ -683,7 +666,7 @@ public static bool UpdateUnifiedGroup(string groupId, if (groupLogo != null) { - await graphClient.Groups[groupId].Photo.Content.Request().PutAsync(groupLogo); + await graphClient.Groups[groupId].Photo.Content.PutAsync(groupLogo); logoUpdated = true; } @@ -694,7 +677,7 @@ public static bool UpdateUnifiedGroup(string groupId, }).GetAwaiter().GetResult(); } - catch (ServiceException ex) + catch (ODataError ex) { Log.Error(Constants.LOGGING_SOURCE, CoreResources.GraphExtensions_ErrorOccured, ex.Error.Message); throw; @@ -797,11 +780,11 @@ public static void DeleteUnifiedGroup(string groupId, string accessToken, int re Task.Run(async () => { var graphClient = CreateGraphClient(accessToken, retryCount, delay, azureEnvironment); - await graphClient.Groups[groupId].Request().DeleteAsync(); + await graphClient.Groups[groupId].DeleteAsync(); }).GetAwaiter().GetResult(); } - catch (ServiceException ex) + catch (ODataError ex) { Log.Error(Constants.LOGGING_SOURCE, CoreResources.GraphExtensions_ErrorOccured, ex.Error.Message); throw; @@ -841,7 +824,7 @@ public static UnifiedGroupEntity GetUnifiedGroup(string groupId, string accessTo var graphClient = CreateGraphClient(accessToken, retryCount, delay, azureEnvironment); - var g = await graphClient.Groups[groupId].Request().GetAsync(); + var g = await graphClient.Groups[groupId].GetAsync(); if (g.GroupTypes.Contains("Unified")) { @@ -860,7 +843,7 @@ public static UnifiedGroupEntity GetUnifiedGroup(string groupId, string accessTo { group.SiteUrl = GetUnifiedGroupSiteUrl(groupId, accessToken); } - catch (ServiceException e) + catch (ODataError e) { group.SiteUrl = e.Error.Message; } @@ -881,7 +864,7 @@ public static UnifiedGroupEntity GetUnifiedGroup(string groupId, string accessTo }).GetAwaiter().GetResult(); } - catch (ServiceException ex) + catch (ODataError ex) { Log.Error(Constants.LOGGING_SOURCE, CoreResources.GraphExtensions_ErrorOccured, ex.Error.Message); throw; @@ -965,7 +948,7 @@ public static List ListUnifiedGroups(string accessToken, { group.SiteUrl = GetUnifiedGroupSiteUrl(g.Id, accessToken); } - catch (ServiceException e) + catch (ODataError e) { group.SiteUrl = e.Error.Message; } @@ -998,7 +981,7 @@ public static List ListUnifiedGroups(string accessToken, return (groups); }).GetAwaiter().GetResult(); } - catch (ServiceException ex) + catch (ODataError ex) { Log.Error(Constants.LOGGING_SOURCE, CoreResources.GraphExtensions_ErrorOccured, ex.Error.Message); throw; @@ -1081,7 +1064,7 @@ public static List GetUnifiedGroups(string accessToken, { group.SiteUrl = GetUnifiedGroupSiteUrl(g.Id, accessToken); } - catch (ServiceException e) + catch (ODataError e) { group.SiteUrl = e.Error.Message; } @@ -1114,7 +1097,7 @@ public static List GetUnifiedGroups(string accessToken, return (groups); }).GetAwaiter().GetResult(); } - catch (ServiceException ex) + catch (ODataError ex) { Log.Error(Constants.LOGGING_SOURCE, CoreResources.GraphExtensions_ErrorOccured, ex.Error.Message); throw; @@ -1197,7 +1180,7 @@ public static List GetUnifiedGroupMembers(UnifiedGroupEntity g }).GetAwaiter().GetResult(); } - catch (ServiceException ex) + catch (ODataError ex) { Log.Error(Constants.LOGGING_SOURCE, CoreResources.GraphExtensions_ErrorOccured, ex.Error.Message); throw; @@ -1236,7 +1219,7 @@ public static List GetNestedUnifiedGroupMembers(UnifiedGroupEn var graphClient = CreateGraphClient(accessToken, retryCount, delay, azureEnvironment); // Get the members of an Office 365 group. - groupUsers = await graphClient.Groups[group.GroupId].Members.Request().GetAsync(); + groupUsers = await graphClient.Groups[group.GroupId].Members.GetAsync(); if (groupUsers.CurrentPage != null && groupUsers.CurrentPage.Count > 0) { unifiedGroupGraphUsers = new List(); @@ -1279,7 +1262,7 @@ public static List GetNestedUnifiedGroupMembers(UnifiedGroupEn }).GetAwaiter().GetResult(); } - catch (ServiceException ex) + catch (ODataError ex) { Log.Error(Constants.LOGGING_SOURCE, CoreResources.GraphExtensions_ErrorOccured, ex.Error.Message); throw; @@ -1314,7 +1297,7 @@ public static void AddUnifiedGroupOwners(string groupId, string[] owners, string }).GetAwaiter().GetResult(); } - catch (ServiceException ex) + catch (ODataError ex) { Log.Error(Constants.LOGGING_SOURCE, CoreResources.GraphExtensions_ErrorOccured, ex.Error.Message); throw; @@ -1348,7 +1331,7 @@ public static void AddUnifiedGroupMembers(string groupId, string[] members, stri }).GetAwaiter().GetResult(); } - catch (ServiceException ex) + catch (ODataError ex) { Log.Error(Constants.LOGGING_SOURCE, CoreResources.GraphExtensions_ErrorOccured, ex.Error.Message); throw; @@ -1381,20 +1364,21 @@ public static void RemoveUnifiedGroupMembers(string groupId, string[] members, s { // Search for the user object var memberQuery = await graphClient.Users - .Request() - .Filter($"userPrincipalName eq '{Uri.EscapeDataString(m.Replace("'", "''"))}'") - .GetAsync(); + .GetAsync(requestConfiguration => + { + requestConfiguration.QueryParameters.Filter = $"userPrincipalName eq '{Uri.EscapeDataString(m.Replace("'", "''"))}'"; + }); - var member = memberQuery.FirstOrDefault(); + var member = memberQuery.Value.FirstOrDefault(); if (member != null) { try { // If it is not in the list of current members, just remove it - await graphClient.Groups[groupId].Members[member.Id].Reference.Request().DeleteAsync(); + await graphClient.Groups[groupId].Members[member.Id].Ref.DeleteAsync(); } - catch (ServiceException ex) + catch (ODataError ex) { if (ex.Error.Code == "Request_BadRequest") { @@ -1410,7 +1394,7 @@ public static void RemoveUnifiedGroupMembers(string groupId, string[] members, s }).GetAwaiter().GetResult(); } - catch (ServiceException ex) + catch (ODataError ex) { Log.Error(Constants.LOGGING_SOURCE, CoreResources.GraphExtensions_ErrorOccured, ex.Error.Message); throw; @@ -1443,20 +1427,21 @@ public static void RemoveUnifiedGroupOwners(string groupId, string[] owners, str { // Search for the user object var memberQuery = await graphClient.Users - .Request() - .Filter($"userPrincipalName eq '{Uri.EscapeDataString(m.Replace("'", "''"))}'") - .GetAsync(); - - var member = memberQuery.FirstOrDefault(); + .GetAsync(requestConfiguration => + { + requestConfiguration.QueryParameters.Filter = $"userPrincipalName eq '{Uri.EscapeDataString(m.Replace("'", "''"))}'"; + }); + + var member = memberQuery?.Value?.FirstOrDefault(); if (member != null) { try { // If it is not in the list of current owners, just remove it - await graphClient.Groups[groupId].Owners[member.Id].Reference.Request().DeleteAsync(); + await graphClient.Groups[groupId].Owners[member.Id].Ref.DeleteAsync(); } - catch (ServiceException ex) + catch (ODataError ex) { if (ex.Error.Code == "Request_BadRequest") { @@ -1472,7 +1457,7 @@ public static void RemoveUnifiedGroupOwners(string groupId, string[] owners, str }).GetAwaiter().GetResult(); } - catch (ServiceException ex) + catch (ODataError ex) { Log.Error(Constants.LOGGING_SOURCE, CoreResources.GraphExtensions_ErrorOccured, ex.Error.Message); throw; @@ -1499,7 +1484,7 @@ public static void ClearUnifiedGroupOwners(string groupId, string accessToken, i var currentOwners = GetUnifiedGroupOwners(new UnifiedGroupEntity { GroupId = groupId }, accessToken, retryCount, delay, azureEnvironment); RemoveUnifiedGroupOwners(groupId, currentOwners.Select(o => o.UserPrincipalName).ToArray(), accessToken, retryCount, delay, azureEnvironment); } - catch (ServiceException ex) + catch (ODataError ex) { Log.Error(Constants.LOGGING_SOURCE, CoreResources.GraphExtensions_ErrorOccured, ex.Error.Message); throw; @@ -1526,7 +1511,7 @@ public static void ClearUnifiedGroupMembers(string groupId, string accessToken, var currentMembers = GetUnifiedGroupMembers(new UnifiedGroupEntity { GroupId = groupId }, accessToken, retryCount, delay, azureEnvironment); RemoveUnifiedGroupMembers(groupId, currentMembers.Select(o => o.UserPrincipalName).ToArray(), accessToken, retryCount, delay, azureEnvironment); } - catch (ServiceException ex) + catch (ODataError ex) { Log.Error(Constants.LOGGING_SOURCE, CoreResources.GraphExtensions_ErrorOccured, ex.Error.Message); throw; @@ -1560,7 +1545,7 @@ public static List GetUnifiedGroupOwners(UnifiedGroupEntity gr var graphClient = CreateGraphClient(accessToken, retryCount, delay, azureEnvironment); // Get the owners of an Office 365 group. - groupUsers = await graphClient.Groups[group.GroupId].Owners.Request().GetAsync(); + groupUsers = await graphClient.Groups[group.GroupId].Owners.GetAsync(); if (groupUsers.CurrentPage != null && groupUsers.CurrentPage.Count > 0) { unifiedGroupGraphUsers = new List(); @@ -1603,7 +1588,7 @@ public static List GetUnifiedGroupOwners(UnifiedGroupEntity gr }).GetAwaiter().GetResult(); } - catch (ServiceException ex) + catch (ODataError ex) { Log.Error(Constants.LOGGING_SOURCE, CoreResources.GraphExtensions_ErrorOccured, ex.Error.Message); throw; @@ -1725,7 +1710,7 @@ private static List GetUsers(GraphServiceClient graphClient, string[] grou usersResult.Add(user); } } - catch (ServiceException) + catch (ODataError) { // skip, group provisioning shouldnt stop because of error in user object } @@ -1772,7 +1757,7 @@ public static string GetGroupClassification(string groupId, string accessToken, } } - catch (ServiceException e) + catch (ODataError e) { classification = e.Error.Message; } @@ -1820,7 +1805,7 @@ public static bool HasTeamsTeam(string groupId, string accessToken, AzureEnviron } } } - catch (ServiceException ex) + catch (ODataError ex) { Log.Error(Constants.LOGGING_SOURCE, CoreResources.GraphExtensions_ErrorOccured, ex.Error.Message); throw; diff --git a/src/lib/PnP.Framework/Graph/UsersUtility.cs b/src/lib/PnP.Framework/Graph/UsersUtility.cs index e061d9d2f..5cd63bd09 100644 --- a/src/lib/PnP.Framework/Graph/UsersUtility.cs +++ b/src/lib/PnP.Framework/Graph/UsersUtility.cs @@ -1,4 +1,5 @@ using Microsoft.Graph; +using Microsoft.Graph.Models; using Newtonsoft.Json; using PnP.Framework.Diagnostics; using System; @@ -160,9 +161,9 @@ public static Model.User GetUser(string accessToken, string userPrincipalName, s return users; }).GetAwaiter().GetResult(); } - catch (ServiceException ex) + catch (Exception ex) { - Log.Error(Constants.LOGGING_SOURCE, CoreResources.GraphExtensions_ErrorOccured, ex.Error.Message); + Log.Error(Constants.LOGGING_SOURCE, CoreResources.GraphExtensions_ErrorOccured, ex.Message); throw; } return result; @@ -282,9 +283,9 @@ public static Model.UserDelta ListUserDelta(string accessToken, string deltaToke return usersDelta; }).GetAwaiter().GetResult(); } - catch (ServiceException ex) + catch (Exception ex) { - Log.Error(Constants.LOGGING_SOURCE, CoreResources.GraphExtensions_ErrorOccured, ex.Error.Message); + Log.Error(Constants.LOGGING_SOURCE, CoreResources.GraphExtensions_ErrorOccured, ex.Message); throw; } return result; @@ -395,9 +396,9 @@ public static Model.TemporaryAccessPassResponse RequestTemporaryAccessPass(strin return accessPassResponse; } - catch (ServiceException ex) + catch (Exception ex) { - Log.Error(Constants.LOGGING_SOURCE, CoreResources.GraphExtensions_ErrorOccured, ex.Error.Message); + Log.Error(Constants.LOGGING_SOURCE, CoreResources.GraphExtensions_ErrorOccured, ex.Message); throw; } } diff --git a/src/lib/PnP.Framework/PnP.Framework.csproj b/src/lib/PnP.Framework/PnP.Framework.csproj index ba5ad4f06..5cae24fcd 100644 --- a/src/lib/PnP.Framework/PnP.Framework.csproj +++ b/src/lib/PnP.Framework/PnP.Framework.csproj @@ -1,7 +1,7 @@  - netstandard2.0;net6.0;net7.0 + net6.0;net7.0 10.0 PnP.Framework PnP.Framework @@ -185,21 +185,6 @@ SR.Designer.cs - - - - - - - - - - - - - - - @@ -238,24 +223,15 @@ - - + + - - - - - - $(PnPCoreSdkPath) - true - false - - + diff --git a/src/lib/PnP.Framework/Provisioning/ObjectHandlers/TokenParser.cs b/src/lib/PnP.Framework/Provisioning/ObjectHandlers/TokenParser.cs index ddd0667d3..8ce4a2a66 100644 --- a/src/lib/PnP.Framework/Provisioning/ObjectHandlers/TokenParser.cs +++ b/src/lib/PnP.Framework/Provisioning/ObjectHandlers/TokenParser.cs @@ -1,4 +1,5 @@ -using Microsoft.Online.SharePoint.TenantAdministration; +using Microsoft.Graph.Models.ODataErrors; +using Microsoft.Online.SharePoint.TenantAdministration; using Microsoft.SharePoint.Client; using Microsoft.SharePoint.Client.Taxonomy; using Newtonsoft.Json; @@ -502,7 +503,7 @@ private void AddGroupTokens(Web web, ProvisioningTemplateApplyingInformation app } } } - catch (Microsoft.Graph.ServiceException ex) + catch (ODataError ex) { // If we don't have permission to access the O365 groups, just skip it Log.Warning(Constants.LOGGING_SOURCE, CoreResources.GraphExtensions_ErrorOccured, ex.Error.Message); From 90af0307cf95d2c63be967557d248b4bd2c87df7 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Mon, 20 Mar 2023 16:48:57 +0200 Subject: [PATCH 3/7] Updates for Graph SDK v5 --- src/lib/PnP.Framework/Graph/GroupsUtility.cs | 264 ++++----- .../Graph/UnifiedGroupsUtility.cs | 542 ++++++------------ src/lib/PnP.Framework/Graph/UsersUtility.cs | 84 +-- 3 files changed, 309 insertions(+), 581 deletions(-) diff --git a/src/lib/PnP.Framework/Graph/GroupsUtility.cs b/src/lib/PnP.Framework/Graph/GroupsUtility.cs index 7369415b0..dbd5dac40 100644 --- a/src/lib/PnP.Framework/Graph/GroupsUtility.cs +++ b/src/lib/PnP.Framework/Graph/GroupsUtility.cs @@ -99,7 +99,7 @@ public static GroupEntity CreateGroup(string displayName, string description, st var graphClient = CreateGraphClient(accessToken, retryCount, delay, azureEnvironment); // Prepare the group resource object - var newGroup = new Group + var newGroup = new GroupExtended { DisplayName = displayName, Description = string.IsNullOrEmpty(description) ? null : description, @@ -113,7 +113,7 @@ public static GroupEntity CreateGroup(string displayName, string description, st var users = GetUsers(graphClient, owners); if (users != null && users.Count > 0) { - newGroup.Owners = users; + newGroup.OwnersODataBind = users.Select(u => string.Format("{1}/users/{0}", u.Id, baseUrl)).ToArray(); } } @@ -122,7 +122,7 @@ public static GroupEntity CreateGroup(string displayName, string description, st var users = GetUsers(graphClient, members); if (users != null && users.Count > 0) { - newGroup.Members = users.Select(u => string.Format("{1}/users/{0}", u.Id, baseUrl)).ToArray(); + newGroup.MembersODataBind = users.Select(u => string.Format("{1}/users/{0}", u.Id, baseUrl)).ToArray(); } } @@ -311,45 +311,38 @@ private static async Task UpdateOwners(string[] owners, GraphServiceClient graph } // Remove any leftover owner - var fullListOfOwners = await graphClient.Groups[groupId].Owners.Request().Select("userPrincipalName, Id").GetAsync(); - var pageExists = true; + var fullListOfOwners = await graphClient.Groups[groupId].Owners + .GetAsync(requestConfiguration => + { + requestConfiguration.QueryParameters.Select = new string[] { "userPrincipalName", "Id" }; + }); - while (pageExists) + var pageIterator = PageIterator.CreatePageIterator(graphClient, fullListOfOwners, (owner) => { - foreach (var owner in fullListOfOwners) + var currentOwnerPrincipalName = (owner as Microsoft.Graph.Models.User)?.UserPrincipalName; + if (!string.IsNullOrEmpty(currentOwnerPrincipalName) && + !owners.Contains(currentOwnerPrincipalName, StringComparer.InvariantCultureIgnoreCase)) { - var currentOwnerPrincipalName = (owner as Microsoft.Graph.Models.User)?.UserPrincipalName; - if (!string.IsNullOrEmpty(currentOwnerPrincipalName) && - !owners.Contains(currentOwnerPrincipalName, StringComparer.InvariantCultureIgnoreCase)) + try { - try + // If it is not in the list of current owners, just remove it + graphClient.Groups[groupId].Owners[owner.Id].Ref.DeleteAsync().GetAwaiter().GetResult(); + } + catch (ODataError ex) + { + if (ex.Error.Code == "Request_BadRequest") { - // If it is not in the list of current owners, just remove it - await graphClient.Groups[groupId].Owners[owner.Id].Reference.Request().DeleteAsync(); + // Skip any failing removal } - catch (ODataError ex) + else { - if (ex.Error.Code == "Request_BadRequest") - { - // Skip any failing removal - } - else - { - throw ex; - } + throw ex; } } } - - if (fullListOfOwners.NextPageRequest != null) - { - fullListOfOwners = await fullListOfOwners.NextPageRequest.GetAsync(); - } - else - { - pageExists = false; - } - } + return true; + }); + await pageIterator.IterateAsync(); } /// @@ -638,49 +631,30 @@ public static List GetGroups(string accessToken, var mailNicknameFilter = !string.IsNullOrEmpty(mailNickname) ? $"(MailNickname eq '{Uri.EscapeDataString(mailNickname.Replace("'", "''"))}')" : string.Empty; var pagedGroups = await graphClient.Groups - .Request() - .Filter($"{displayNameFilter}{(!string.IsNullOrEmpty(displayNameFilter) && !string.IsNullOrEmpty(mailNicknameFilter) ? " and " : "")}{mailNicknameFilter}") - .Top(pageSize) - .GetAsync(); - - Int32 pageCount = 0; - Int32 currentIndex = 0; + .GetAsync(requestConfiguration => + { + requestConfiguration.QueryParameters.Filter = $"{displayNameFilter}{(!string.IsNullOrEmpty(displayNameFilter) && !string.IsNullOrEmpty(mailNicknameFilter) ? " and " : "")}{mailNicknameFilter}"; + requestConfiguration.QueryParameters.Top = pageSize; + }); - while (true) + var pageIterator = PageIterator.CreatePageIterator(graphClient, pagedGroups, (g) => { - pageCount++; - - foreach (var g in pagedGroups) + var group = new GroupEntity { - currentIndex++; - - if (currentIndex >= startIndex) - { - var group = new GroupEntity - { - GroupId = g.Id, - DisplayName = g.DisplayName, - Description = g.Description, - Mail = g.Mail, - MailNickname = g.MailNickname, - MailEnabled = g.MailEnabled, - SecurityEnabled = g.SecurityEnabled, - GroupTypes = g.GroupTypes != null ? g.GroupTypes.ToArray() : null - }; - - groups.Add(group); - } - } + GroupId = g.Id, + DisplayName = g.DisplayName, + Description = g.Description, + Mail = g.Mail, + MailNickname = g.MailNickname, + MailEnabled = g.MailEnabled, + SecurityEnabled = g.SecurityEnabled, + GroupTypes = g.GroupTypes != null ? g.GroupTypes.ToArray() : null + }; + groups.Add(group); + return true; + }); - if (pagedGroups.NextPageRequest != null && (endIndex == null || groups.Count < endIndex)) - { - pagedGroups = await pagedGroups.NextPageRequest.GetAsync(); - } - else - { - break; - } - } + await pageIterator.IterateAsync(); return (groups); }).GetAwaiter().GetResult(); @@ -704,9 +678,7 @@ public static List GetGroups(string accessToken, /// Members of an Azure Active Directory group public static List GetGroupMembers(GroupEntity group, string accessToken, int retryCount = 10, int delay = 500, AzureEnvironment azureEnvironment = AzureEnvironment.Production) { - List groupUsers = null; - List groupGraphUsers = null; - IGroupMembersCollectionWithReferencesPage groupUsersCollection = null; + List groupUsers = new List(); if (String.IsNullOrEmpty(accessToken)) { @@ -723,55 +695,35 @@ public static List GetGroupMembers(GroupEntity group, string accessTo { var graphClient = CreateGraphClient(accessToken, retryCount, delay, azureEnvironment); - // Get the members of the group - groupUsersCollection = await graphClient.Groups[group.GroupId].Members.GetAsync(); - if (groupUsersCollection.CurrentPage != null && groupUsersCollection.CurrentPage.Count > 0) - { - groupGraphUsers = new List(); - groupGraphUsers.AddRange(groupUsersCollection.CurrentPage); - //GenerateGraphUserCollection(groupUsersCollection.CurrentPage, groupGraphUsers); - } - - // Retrieve users when the results are paged. - while (groupUsersCollection.NextPageRequest != null) - { - groupUsersCollection = groupUsersCollection.NextPageRequest.GetAsync().GetAwaiter().GetResult(); - if (groupUsersCollection.CurrentPage != null && groupUsersCollection.CurrentPage.Count > 0) - { - groupGraphUsers.AddRange(groupUsersCollection.CurrentPage); - //GenerateGraphUserCollection(groupUsersCollection.CurrentPage, groupGraphUsers); - } - } + var groupUsersCollection = await graphClient.Groups[group.GroupId].Members.GetAsync(); - // Create the collection of type OfficeDevPnP groupuser after all users are retrieved, including paged data. - if (groupGraphUsers != null && groupGraphUsers.Count > 0) + var iterator = PageIterator.CreatePageIterator(graphClient, groupUsersCollection, (usr) => { - groupUsers = new List(); - foreach (DirectoryObject usr in groupGraphUsers) + switch (usr) { - switch(usr) - { - case Microsoft.Graph.Models.User userType: - groupUsers.Add(new GroupUser - { - UserPrincipalName = userType.UserPrincipalName != null ? userType.UserPrincipalName : string.Empty, - DisplayName = userType.DisplayName != null ? userType.DisplayName : string.Empty, - Type = Enums.GroupUserType.User - }); + case Microsoft.Graph.Models.User userType: + groupUsers.Add(new GroupUser + { + UserPrincipalName = userType.UserPrincipalName != null ? userType.UserPrincipalName : string.Empty, + DisplayName = userType.DisplayName != null ? userType.DisplayName : string.Empty, + Type = Enums.GroupUserType.User + }); break; - case Microsoft.Graph.Models.Group groupType: - groupUsers.Add(new GroupUser - { - UserPrincipalName = groupType.Id != null ? groupType.Id : string.Empty, - DisplayName = groupType.DisplayName != null ? groupType.DisplayName : string.Empty, - Type = Enums.GroupUserType.Group - }); - break; - } - + case Microsoft.Graph.Models.Group groupType: + groupUsers.Add(new GroupUser + { + UserPrincipalName = groupType.Id != null ? groupType.Id : string.Empty, + DisplayName = groupType.DisplayName != null ? groupType.DisplayName : string.Empty, + Type = Enums.GroupUserType.Group + }); + break; } - } + return true; + }); + + await iterator.IterateAsync(); + return groupUsers; }).GetAwaiter().GetResult(); @@ -1030,8 +982,6 @@ public static void ClearGroupMembers(string groupId, string accessToken, int ret public static List GetGroupOwners(GroupEntity group, string accessToken, int retryCount = 10, int delay = 500, AzureEnvironment azureEnvironment = AzureEnvironment.Production) { List groupUsers = null; - List groupGraphUsers = null; - IGroupOwnersCollectionWithReferencesPage groupUsersCollection = null; if (String.IsNullOrEmpty(accessToken)) { @@ -1045,52 +995,35 @@ public static List GetGroupOwners(GroupEntity group, string accessTok var graphClient = CreateGraphClient(accessToken, retryCount, delay, azureEnvironment); // Get the owners of an Office 365 group. - groupUsersCollection = await graphClient.Groups[group.GroupId].Owners.GetAsync(); - if (groupUsersCollection.CurrentPage != null && groupUsersCollection.CurrentPage.Count > 0) - { - groupGraphUsers = new List(); - GenerateGraphUserCollection(groupUsersCollection.CurrentPage, groupGraphUsers); - } + var groupUsersCollection = await graphClient.Groups[group.GroupId].Owners.GetAsync(); - // Retrieve users when the results are paged. - while (groupUsersCollection.NextPageRequest != null) + var pageIterator = PageIterator.CreatePageIterator(graphClient, groupUsersCollection, (usr) => { - groupUsersCollection = groupUsersCollection.NextPageRequest.GetAsync().GetAwaiter().GetResult(); - if (groupUsersCollection.CurrentPage != null && groupUsersCollection.CurrentPage.Count > 0) + switch (usr) { - GenerateGraphUserCollection(groupUsersCollection.CurrentPage, groupGraphUsers); - } - } + case Microsoft.Graph.Models.User userType: + groupUsers.Add(new GroupUser + { + UserPrincipalName = userType.UserPrincipalName != null ? userType.UserPrincipalName : string.Empty, + DisplayName = userType.DisplayName != null ? userType.DisplayName : string.Empty, + Type = Enums.GroupUserType.User + }); + break; - // Create the collection of type OfficeDevPnP 'UnifiedGroupUser' after all users are retrieved, including paged data. - if (groupGraphUsers != null && groupGraphUsers.Count > 0) - { - groupUsers = new List(); - foreach (DirectoryObject usr in groupGraphUsers) - { - switch(usr) - { - case Microsoft.Graph.Models.User userType: - groupUsers.Add(new GroupUser - { - UserPrincipalName = userType.UserPrincipalName != null ? userType.UserPrincipalName : string.Empty, - DisplayName = userType.DisplayName != null ? userType.DisplayName : string.Empty, - Type = Enums.GroupUserType.User - }); + case Microsoft.Graph.Models.Group groupType: + groupUsers.Add(new GroupUser + { + UserPrincipalName = groupType.Id != null ? groupType.Id : string.Empty, + DisplayName = groupType.DisplayName != null ? groupType.DisplayName : string.Empty, + Type = Enums.GroupUserType.Group + }); break; + } + return true; + }); - case Microsoft.Graph.Models.Group groupType: - groupUsers.Add(new GroupUser - { - UserPrincipalName = groupType.Id != null ? groupType.Id : string.Empty, - DisplayName = groupType.DisplayName != null ? groupType.DisplayName : string.Empty, - Type = Enums.GroupUserType.Group - }); - break; - } + await pageIterator.IterateAsync(); - } - } return groupUsers; }).GetAwaiter().GetResult(); @@ -1205,13 +1138,14 @@ private static List GetUsers(GraphServiceClient graphClient, string[] grou try { // Search for the user object - IGraphServiceUsersCollectionPage userQuery = await graphClient.Users - .Request() - .Select("Id") - .Filter($"userPrincipalName eq '{Uri.EscapeDataString(groupUser.Replace("'", "''"))}'") - .GetAsync(); - - User user = userQuery.FirstOrDefault(); + var userQuery = await graphClient.Users + .GetAsync(requestConfiguration => + { + requestConfiguration.QueryParameters.Select = new string[] { "Id" }; + requestConfiguration.QueryParameters.Filter = $"userPrincipalName eq '{Uri.EscapeDataString(groupUser.Replace("'", "''"))}'"; + }); + + User user = userQuery?.Value.FirstOrDefault(); if (user != null) { usersResult.Add(user); diff --git a/src/lib/PnP.Framework/Graph/UnifiedGroupsUtility.cs b/src/lib/PnP.Framework/Graph/UnifiedGroupsUtility.cs index fee6e4943..0f765a552 100644 --- a/src/lib/PnP.Framework/Graph/UnifiedGroupsUtility.cs +++ b/src/lib/PnP.Framework/Graph/UnifiedGroupsUtility.cs @@ -163,7 +163,7 @@ public static UnifiedGroupEntity CreateUnifiedGroup(string displayName, string d SecurityEnabled = false, Visibility = isPrivate == true ? "Private" : "Public", GroupTypes = new List { "Unified" } - }; + }; if (labels.Any()) { @@ -310,25 +310,32 @@ public static UnifiedGroupEntity CreateUnifiedGroup(string displayName, string d /// GraphClient instance to use to communicate with the Microsoft Graph /// Id of the group which needs the owners added /// If set to true, all existing members which are not specified through will be removed as a member from the group - private static async Task UpdateMembers(string[] members, GraphServiceClient graphClient, string groupId, bool removeOtherMembers) + private static async Task UpdateMembers(string[] members, GraphServiceClient graphClient, string groupId, bool removeOtherMembers, AzureEnvironment azureEnvironment) { + var baseUrl = $"https://{AuthenticationManager.GetGraphEndPoint(azureEnvironment)}/v1.0"; if (members != null && members.Length > 0) { foreach (var m in members) { // Search for the user object var memberQuery = await graphClient.Users - .Filter($"userPrincipalName eq '{Uri.EscapeDataString(m.Replace("'", "''"))}'") - .GetAsync(); + .GetAsync(requestConfiguration => + { + requestConfiguration.QueryParameters.Filter = $"userPrincipalName eq '{Uri.EscapeDataString(m.Replace("'", "''"))}'"; + }); - var member = memberQuery.FirstOrDefault(); + var member = memberQuery?.Value.FirstOrDefault(); if (member != null) { try - { + { + var expectedRequestBody = new ReferenceCreate() + { + OdataId = string.Format("{0}/directoryObjects/{1}", string.Format(baseUrl, "v1.0"), member.Id), + }; // And if any, add it to the collection of group's owners - await graphClient.Groups[groupId].Members.Ref.PostAsync(member); + await graphClient.Groups[groupId].Members.Ref.PostAsync(expectedRequestBody); } catch (Exception ex) { @@ -354,45 +361,39 @@ private static async Task UpdateMembers(string[] members, GraphServiceClient gra } // Remove any leftover member - var fullListOfMembers = await graphClient.Groups[groupId].Members.Request().Select("userPrincipalName, Id").GetAsync(); - var pageExists = true; + var fullListOfMembers = await graphClient.Groups[groupId].Members + .GetAsync(requestConfiguration => + { + requestConfiguration.QueryParameters.Select = new string[] { "userPrincipalName", "Id" }; + }); - while (pageExists) + var pageIterator = PageIterator.CreatePageIterator(graphClient, fullListOfMembers, (member) => { - foreach (var member in fullListOfMembers) + var currentMemberPrincipalName = (member as Microsoft.Graph.Models.User)?.UserPrincipalName; + if (!string.IsNullOrEmpty(currentMemberPrincipalName) && + !members.Contains(currentMemberPrincipalName, StringComparer.InvariantCultureIgnoreCase)) { - var currentMemberPrincipalName = (member as Microsoft.Graph.Models.User)?.UserPrincipalName; - if (!string.IsNullOrEmpty(currentMemberPrincipalName) && - !members.Contains(currentMemberPrincipalName, StringComparer.InvariantCultureIgnoreCase)) + try { - try + // If it is not in the list of current owners, just remove it + graphClient.Groups[groupId].Members[member.Id].Ref.DeleteAsync().GetAwaiter().GetResult(); + } + catch (ODataError ex) + { + if (ex.Error.Code == "Request_BadRequest") { - // If it is not in the list of current owners, just remove it - await graphClient.Groups[groupId].Members[member.Id].Reference.Request().DeleteAsync(); + // Skip any failing removal } - catch (ODataError ex) + else { - if (ex.Error.Code == "Request_BadRequest") - { - // Skip any failing removal - } - else - { - throw ex; - } + throw ex; } } } + return true; + }); - if (fullListOfMembers.NextPageRequest != null) - { - fullListOfMembers = await fullListOfMembers.NextPageRequest.GetAsync(); - } - else - { - pageExists = false; - } - } + await pageIterator.IterateAsync(); } /// @@ -402,24 +403,30 @@ private static async Task UpdateMembers(string[] members, GraphServiceClient gra /// GraphClient instance to use to communicate with the Microsoft Graph /// Id of the group which needs the owners added /// If set to true, all existing owners which are not specified through will be removed as an owner from the group - private static async Task UpdateOwners(string[] owners, GraphServiceClient graphClient, string groupId, bool removeOtherOwners) + private static async Task UpdateOwners(string[] owners, GraphServiceClient graphClient, string groupId, bool removeOtherOwners, AzureEnvironment azureEnvironment) { + var baseUrl = $"https://{AuthenticationManager.GetGraphEndPoint(azureEnvironment)}/v1.0"; foreach (var o in owners) { // Search for the user object var ownerQuery = await graphClient.Users - .Request() - .Filter($"userPrincipalName eq '{Uri.EscapeDataString(o.Replace("'", "''"))}'") - .GetAsync(); + .GetAsync(requestConfiguration => + { + requestConfiguration.QueryParameters.Filter = $"userPrincipalName eq '{Uri.EscapeDataString(o.Replace("'", "''"))}'"; + }); - var owner = ownerQuery.FirstOrDefault(); + var owner = ownerQuery?.Value.FirstOrDefault(); if (owner != null) { try { + var expectedRequestBody = new ReferenceCreate() + { + OdataId = string.Format("{0}/directoryObjects/{1}", string.Format(baseUrl, "v1.0"), owner.Id), + }; // And if any, add it to the collection of group's owners - await graphClient.Groups[groupId].Owners.Ref.PostAsync(owner); + await graphClient.Groups[groupId].Owners.Ref.PostAsync(expectedRequestBody); } catch (Exception ex) { @@ -445,45 +452,39 @@ private static async Task UpdateOwners(string[] owners, GraphServiceClient graph } // Remove any leftover owner - var fullListOfOwners = await graphClient.Groups[groupId].Owners.Request().Select("userPrincipalName, Id").GetAsync(); - var pageExists = true; + var fullListOfOwners = await graphClient.Groups[groupId].Owners + .GetAsync(requestConfiguration => + { + requestConfiguration.QueryParameters.Select = new string[] { "userPrincipalName", "Id" }; + }); - while (pageExists) + var pageIterator = PageIterator.CreatePageIterator(graphClient, fullListOfOwners, (owner) => { - foreach (var owner in fullListOfOwners) + var currentOwnerPrincipalName = (owner as Microsoft.Graph.Models.User)?.UserPrincipalName; + if (!string.IsNullOrEmpty(currentOwnerPrincipalName) && + !owners.Contains(currentOwnerPrincipalName, StringComparer.InvariantCultureIgnoreCase)) { - var currentOwnerPrincipalName = (owner as Microsoft.Graph.Models.User)?.UserPrincipalName; - if (!string.IsNullOrEmpty(currentOwnerPrincipalName) && - !owners.Contains(currentOwnerPrincipalName, StringComparer.InvariantCultureIgnoreCase)) + try { - try + // If it is not in the list of current owners, just remove it + graphClient.Groups[groupId].Owners[owner.Id].Ref.DeleteAsync().GetAwaiter().GetResult(); + } + catch (ODataError ex) + { + if (ex.Error.Code == "Request_BadRequest") { - // If it is not in the list of current owners, just remove it - await graphClient.Groups[groupId].Owners[owner.Id].Reference.Request().DeleteAsync(); + // Skip any failing removal } - catch (ODataError ex) + else { - if (ex.Error.Code == "Request_BadRequest") - { - // Skip any failing removal - } - else - { - throw ex; - } + throw ex; } - } + }; } + return true; + }); - if (fullListOfOwners.NextPageRequest != null) - { - fullListOfOwners = await fullListOfOwners.NextPageRequest.GetAsync(); - } - else - { - pageExists = false; - } - } + await pageIterator.IterateAsync(); } /// @@ -631,7 +632,7 @@ public static bool UpdateUnifiedGroup(string groupId, if (owners != null && owners.Length > 0) { // For each and every owner - await UpdateOwners(owners, graphClient, groupToUpdate.Id, true); + await UpdateOwners(owners, graphClient, groupToUpdate.Id, true, azureEnvironment); updateGroup = true; } @@ -639,7 +640,7 @@ public static bool UpdateUnifiedGroup(string groupId, if (members != null && members.Length > 0) { // For each and every owner - await UpdateMembers(members, graphClient, groupToUpdate.Id, true); + await UpdateMembers(members, graphClient, groupToUpdate.Id, true, azureEnvironment); updateGroup = true; } @@ -870,124 +871,7 @@ public static UnifiedGroupEntity GetUnifiedGroup(string groupId, string accessTo throw; } return (result); - } - - /// - /// Returns all the Office 365 Groups in the current Tenant based on a startIndex. IncludeSite adds additional properties about the Modern SharePoint Site backing the group - /// - /// The OAuth 2.0 Access Token to use for invoking the Microsoft Graph - /// The DisplayName of the Office 365 Group - /// The MailNickname of the Office 365 Group - /// Not relevant anymore - /// Not relevant anymore - /// Defines whether to return details about the Modern SharePoint Site backing the group. Default is true. - /// Number of times to retry the request in case of throttling - /// Milliseconds to wait before retrying the request. The delay will be increased (doubled) every retry - /// Defines whether or not to return details about the Modern Site classification value. - /// Defines whether to check for each unified group if it has a Microsoft Team provisioned for it. Default is false. - /// Defines the Azure Cloud Deployment. This is used to determine the MS Graph EndPoint to call which differs per Azure Cloud deployments. Defaults to Production (graph.microsoft.com). - /// An IList of SiteEntity objects - [Obsolete("ListUnifiedGroups is deprecated, please use GetUnifiedGroups instead.")] - public static List ListUnifiedGroups(string accessToken, - string displayName = null, string mailNickname = null, - int startIndex = 0, int endIndex = 999, bool includeSite = true, - int retryCount = 10, int delay = 500, bool includeClassification = false, - bool includeHasTeam = false, AzureEnvironment azureEnvironment = AzureEnvironment.Production) - { - if (string.IsNullOrEmpty(accessToken)) - { - throw new ArgumentNullException(nameof(accessToken)); - } - - List result = null; - try - { - // Use a synchronous model to invoke the asynchronous process - result = Task.Run(async () => - { - List groups = new List(); - - var graphClient = CreateGraphClient(accessToken, retryCount, delay, azureEnvironment); - - // Apply the DisplayName filter, if any - var displayNameFilter = !string.IsNullOrEmpty(displayName) ? $" and (DisplayName eq '{Uri.EscapeDataString(displayName.Replace("'", "''"))}')" : string.Empty; - var mailNicknameFilter = !string.IsNullOrEmpty(mailNickname) ? $" and (MailNickname eq '{Uri.EscapeDataString(mailNickname.Replace("'", "''"))}')" : string.Empty; - - var pagedGroups = await graphClient.Groups - .Request() - .Filter($"groupTypes/any(grp: grp eq 'Unified'){displayNameFilter}{mailNicknameFilter}") - .Top(endIndex) - .GetAsync(); - - Int32 pageCount = 0; - Int32 currentIndex = 0; - - while (true) - { - pageCount++; - - foreach (var g in pagedGroups) - { - currentIndex++; - - if (currentIndex >= startIndex) - { - var group = new UnifiedGroupEntity - { - GroupId = g.Id, - DisplayName = g.DisplayName, - Description = g.Description, - Mail = g.Mail, - MailNickname = g.MailNickname, - Visibility = g.Visibility - }; - - if (includeSite) - { - try - { - group.SiteUrl = GetUnifiedGroupSiteUrl(g.Id, accessToken); - } - catch (ODataError e) - { - group.SiteUrl = e.Error.Message; - } - } - - if (includeClassification) - { - group.Classification = g.Classification; - } - - if (includeHasTeam) - { - group.HasTeam = HasTeamsTeam(group.GroupId, accessToken); - } - - groups.Add(group); - } - } - - if (pagedGroups.NextPageRequest != null && groups.Count < endIndex) - { - pagedGroups = await pagedGroups.NextPageRequest.GetAsync(); - } - else - { - break; - } - } - - return (groups); - }).GetAwaiter().GetResult(); - } - catch (ODataError ex) - { - Log.Error(Constants.LOGGING_SOURCE, CoreResources.GraphExtensions_ErrorOccured, ex.Error.Message); - throw; - } - return (result); - } + } /// /// Returns all the Office 365 Groups in the current Tenant based on a startIndex. IncludeSite adds additional properties about the Modern SharePoint Site backing the group @@ -1030,69 +914,51 @@ public static List GetUnifiedGroups(string accessToken, var mailNicknameFilter = !string.IsNullOrEmpty(mailNickname) ? $" and (MailNickname eq '{Uri.EscapeDataString(mailNickname.Replace("'", "''"))}')" : string.Empty; var pagedGroups = await graphClient.Groups - .Request() - .Filter($"groupTypes/any(grp: grp eq 'Unified'){displayNameFilter}{mailNicknameFilter}") - .Top(pageSize) - .GetAsync(); - - Int32 pageCount = 0; - Int32 currentIndex = 0; + .GetAsync(requestConfiguration => + { + requestConfiguration.QueryParameters.Filter = $"groupTypes/any(grp: grp eq 'Unified'){displayNameFilter}{mailNicknameFilter}"; + requestConfiguration.QueryParameters.Top = pageSize; + }); - while (true) + var pageIterator = PageIterator.CreatePageIterator(graphClient, pagedGroups, (groupEntity) => { - pageCount++; - - foreach (var g in pagedGroups) + var group = new UnifiedGroupEntity { - currentIndex++; + GroupId = groupEntity.Id, + DisplayName = groupEntity.DisplayName, + Description = groupEntity.Description, + Mail = groupEntity.Mail, + MailNickname = groupEntity.MailNickname, + Visibility = groupEntity.Visibility + }; - if (currentIndex >= startIndex) + if (includeSite) + { + try { - var group = new UnifiedGroupEntity - { - GroupId = g.Id, - DisplayName = g.DisplayName, - Description = g.Description, - Mail = g.Mail, - MailNickname = g.MailNickname, - Visibility = g.Visibility - }; - - if (includeSite) - { - try - { - group.SiteUrl = GetUnifiedGroupSiteUrl(g.Id, accessToken); - } - catch (ODataError e) - { - group.SiteUrl = e.Error.Message; - } - } - - if (includeClassification) - { - group.Classification = g.Classification; - } - - if (includeHasTeam) - { - group.HasTeam = HasTeamsTeam(group.GroupId, accessToken, azureEnvironment); - } - - groups.Add(group); + group.SiteUrl = GetUnifiedGroupSiteUrl(groupEntity.Id, accessToken); + } + catch (ODataError e) + { + group.SiteUrl = e.Error.Message; } } - if (pagedGroups.NextPageRequest != null && (endIndex == null || groups.Count < endIndex)) + if (includeClassification) { - pagedGroups = await pagedGroups.NextPageRequest.GetAsync(); + group.Classification = groupEntity.Classification; } - else + + if (includeHasTeam) { - break; + group.HasTeam = HasTeamsTeam(group.GroupId, accessToken, azureEnvironment); } - } + + groups.Add(group); + return true; + }); + + await pageIterator.IterateAsync(); return (groups); }).GetAwaiter().GetResult(); @@ -1117,8 +983,8 @@ public static List GetUnifiedGroups(string accessToken, public static List GetUnifiedGroupMembers(UnifiedGroupEntity group, string accessToken, int retryCount = 10, int delay = 500, AzureEnvironment azureEnvironment = AzureEnvironment.Production) { List unifiedGroupUsers = null; - List unifiedGroupGraphUsers = null; - IGroupMembersCollectionWithReferencesPage groupUsers = null; + List unifiedGroupGraphUsers = new List(); + if (String.IsNullOrEmpty(accessToken)) { @@ -1136,46 +1002,29 @@ public static List GetUnifiedGroupMembers(UnifiedGroupEntity g var graphClient = CreateGraphClient(accessToken, retryCount, delay, azureEnvironment); // Get the members of an Office 365 group. - groupUsers = await graphClient.Groups[group.GroupId].Members.Request().GetAsync(); - if (groupUsers.CurrentPage != null && groupUsers.CurrentPage.Count > 0) - { - unifiedGroupGraphUsers = new List(); - - GenerateGraphUserCollection(groupUsers.CurrentPage, unifiedGroupGraphUsers); - } + var groupUsers = await graphClient.Groups[group.GroupId].Members.GetAsync(); - // Retrieve users when the results are paged. - while (groupUsers.NextPageRequest != null) + var pageIterator = PageIterator.CreatePageIterator(graphClient, groupUsers, (unifiedGroupGraphUser) => { - groupUsers = groupUsers.NextPageRequest.GetAsync().GetAwaiter().GetResult(); - if (groupUsers.CurrentPage != null && groupUsers.CurrentPage.Count > 0) + var usr = unifiedGroupGraphUser as User; + UnifiedGroupUser groupUser = new UnifiedGroupUser { - GenerateGraphUserCollection(groupUsers.CurrentPage, unifiedGroupGraphUsers); - } - } + Id = usr.Id, + UserPrincipalName = usr.UserPrincipalName != null ? usr.UserPrincipalName : string.Empty, + DisplayName = usr.DisplayName != null ? usr.DisplayName : string.Empty, + GivenName = usr.GivenName != null ? usr.GivenName : string.Empty, + Surname = usr.Surname != null ? usr.Surname : string.Empty, + Email = usr.Mail != null ? usr.Mail : string.Empty, + MobilePhone = usr.MobilePhone != null ? usr.DisplayName : string.Empty, + PreferredLanguage = usr.PreferredLanguage != null ? usr.PreferredLanguage : string.Empty, + JobTitle = usr.JobTitle != null ? usr.DisplayName : string.Empty, + BusinessPhones = usr.BusinessPhones != null ? usr.BusinessPhones.ToArray() : null + }; + unifiedGroupUsers.Add(groupUser); + return true; + }); - // Create the collection of type OfficeDevPnP 'UnifiedGroupUser' after all users are retrieved, including paged data. - if (unifiedGroupGraphUsers != null && unifiedGroupGraphUsers.Count > 0) - { - unifiedGroupUsers = new List(); - foreach (User usr in unifiedGroupGraphUsers) - { - UnifiedGroupUser groupUser = new UnifiedGroupUser - { - Id = usr.Id, - UserPrincipalName = usr.UserPrincipalName != null ? usr.UserPrincipalName : string.Empty, - DisplayName = usr.DisplayName != null ? usr.DisplayName : string.Empty, - GivenName = usr.GivenName != null ? usr.GivenName : string.Empty, - Surname = usr.Surname != null ? usr.Surname : string.Empty, - Email = usr.Mail != null ? usr.Mail : string.Empty, - MobilePhone = usr.MobilePhone != null ? usr.DisplayName : string.Empty, - PreferredLanguage = usr.PreferredLanguage != null ? usr.PreferredLanguage : string.Empty, - JobTitle = usr.JobTitle != null ? usr.DisplayName : string.Empty, - BusinessPhones = usr.BusinessPhones != null ? usr.BusinessPhones.ToArray() : null - }; - unifiedGroupUsers.Add(groupUser); - } - } + await pageIterator.IterateAsync(); return unifiedGroupUsers; }).GetAwaiter().GetResult(); @@ -1200,8 +1049,7 @@ public static List GetUnifiedGroupMembers(UnifiedGroupEntity g public static List GetNestedUnifiedGroupMembers(UnifiedGroupEntity group, string accessToken, int retryCount = 10, int delay = 500, AzureEnvironment azureEnvironment = AzureEnvironment.Production) { List unifiedGroupUsers = new List(); - List unifiedGroupGraphUsers = null; - IGroupMembersCollectionWithReferencesPage groupUsers = null; + List unifiedGroupGraphUsers = new List(); if (String.IsNullOrEmpty(accessToken)) { @@ -1219,45 +1067,29 @@ public static List GetNestedUnifiedGroupMembers(UnifiedGroupEn var graphClient = CreateGraphClient(accessToken, retryCount, delay, azureEnvironment); // Get the members of an Office 365 group. - groupUsers = await graphClient.Groups[group.GroupId].Members.GetAsync(); - if (groupUsers.CurrentPage != null && groupUsers.CurrentPage.Count > 0) - { - unifiedGroupGraphUsers = new List(); + var groupUsers = await graphClient.Groups[group.GroupId].Members.GetAsync(); - GenerateNestedGraphUserCollection(groupUsers.CurrentPage, unifiedGroupGraphUsers, unifiedGroupUsers, accessToken); - } - - // Retrieve users when the results are paged. - while (groupUsers.NextPageRequest != null) + var pageIterator = PageIterator.CreatePageIterator(graphClient, groupUsers, (unifiedGroupGraphUser) => { - groupUsers = groupUsers.NextPageRequest.GetAsync().GetAwaiter().GetResult(); - if (groupUsers.CurrentPage != null && groupUsers.CurrentPage.Count > 0) + var usr = unifiedGroupGraphUser as User; + UnifiedGroupUser groupUser = new UnifiedGroupUser { - GenerateNestedGraphUserCollection(groupUsers.CurrentPage, unifiedGroupGraphUsers, unifiedGroupUsers, accessToken); - } - } + Id = usr.Id, + UserPrincipalName = usr.UserPrincipalName != null ? usr.UserPrincipalName : string.Empty, + DisplayName = usr.DisplayName != null ? usr.DisplayName : string.Empty, + GivenName = usr.GivenName != null ? usr.GivenName : string.Empty, + Surname = usr.Surname != null ? usr.Surname : string.Empty, + Email = usr.Mail != null ? usr.Mail : string.Empty, + MobilePhone = usr.MobilePhone != null ? usr.DisplayName : string.Empty, + PreferredLanguage = usr.PreferredLanguage != null ? usr.PreferredLanguage : string.Empty, + JobTitle = usr.JobTitle != null ? usr.DisplayName : string.Empty, + BusinessPhones = usr.BusinessPhones != null ? usr.BusinessPhones.ToArray() : null + }; + unifiedGroupUsers.Add(groupUser); + return true; + }); - // Create the collection of type OfficeDevPnP 'UnifiedGroupUser' after all users are retrieved, including paged data. - if (unifiedGroupGraphUsers != null && unifiedGroupGraphUsers.Count > 0) - { - foreach (User usr in unifiedGroupGraphUsers) - { - UnifiedGroupUser groupUser = new UnifiedGroupUser - { - Id = usr.Id, - UserPrincipalName = usr.UserPrincipalName != null ? usr.UserPrincipalName : string.Empty, - DisplayName = usr.DisplayName != null ? usr.DisplayName : string.Empty, - GivenName = usr.GivenName != null ? usr.GivenName : string.Empty, - Surname = usr.Surname != null ? usr.Surname : string.Empty, - Email = usr.Mail != null ? usr.Mail : string.Empty, - MobilePhone = usr.MobilePhone != null ? usr.DisplayName : string.Empty, - PreferredLanguage = usr.PreferredLanguage != null ? usr.PreferredLanguage : string.Empty, - JobTitle = usr.JobTitle != null ? usr.DisplayName : string.Empty, - BusinessPhones = usr.BusinessPhones != null ? usr.BusinessPhones.ToArray() : null - }; - unifiedGroupUsers.Add(groupUser); - } - } + await pageIterator.IterateAsync(); return unifiedGroupUsers; }).GetAwaiter().GetResult(); @@ -1293,7 +1125,7 @@ public static void AddUnifiedGroupOwners(string groupId, string[] owners, string { var graphClient = CreateGraphClient(accessToken, retryCount, delay, azureEnvironment); - await UpdateOwners(owners, graphClient, groupId, removeExistingOwners); + await UpdateOwners(owners, graphClient, groupId, removeExistingOwners, azureEnvironment); }).GetAwaiter().GetResult(); } @@ -1327,7 +1159,7 @@ public static void AddUnifiedGroupMembers(string groupId, string[] members, stri { var graphClient = CreateGraphClient(accessToken, retryCount, delay, azureEnvironment); - await UpdateMembers(members, graphClient, groupId, removeExistingMembers); + await UpdateMembers(members, graphClient, groupId, removeExistingMembers, azureEnvironment); }).GetAwaiter().GetResult(); } @@ -1530,8 +1362,7 @@ public static void ClearUnifiedGroupMembers(string groupId, string accessToken, public static List GetUnifiedGroupOwners(UnifiedGroupEntity group, string accessToken, int retryCount = 10, int delay = 500, AzureEnvironment azureEnvironment = AzureEnvironment.Production) { List unifiedGroupUsers = null; - List unifiedGroupGraphUsers = null; - IGroupOwnersCollectionWithReferencesPage groupUsers = null; + List unifiedGroupGraphUsers = new List(); if (String.IsNullOrEmpty(accessToken)) { @@ -1542,48 +1373,32 @@ public static List GetUnifiedGroupOwners(UnifiedGroupEntity gr { var result = Task.Run(async () => { - var graphClient = CreateGraphClient(accessToken, retryCount, delay, azureEnvironment); - + var graphClient = CreateGraphClient(accessToken, retryCount, delay, azureEnvironment); // Get the owners of an Office 365 group. - groupUsers = await graphClient.Groups[group.GroupId].Owners.GetAsync(); - if (groupUsers.CurrentPage != null && groupUsers.CurrentPage.Count > 0) - { - unifiedGroupGraphUsers = new List(); - GenerateGraphUserCollection(groupUsers.CurrentPage, unifiedGroupGraphUsers); - } + var groupUsers = await graphClient.Groups[group.GroupId].Owners.GetAsync(); - // Retrieve users when the results are paged. - while (groupUsers.NextPageRequest != null) + var pageIterator = PageIterator.CreatePageIterator(graphClient, groupUsers, (unifiedGroupGraphUser) => { - groupUsers = groupUsers.NextPageRequest.GetAsync().GetAwaiter().GetResult(); - if (groupUsers.CurrentPage != null && groupUsers.CurrentPage.Count > 0) + var unifiedGroupUser = unifiedGroupGraphUser as User; + UnifiedGroupUser groupUser = new UnifiedGroupUser { - GenerateGraphUserCollection(groupUsers.CurrentPage, unifiedGroupGraphUsers); - } - } + Id = unifiedGroupUser.Id, + UserPrincipalName = unifiedGroupUser.UserPrincipalName != null ? unifiedGroupUser.UserPrincipalName : string.Empty, + DisplayName = unifiedGroupUser.DisplayName != null ? unifiedGroupUser.DisplayName : string.Empty, + GivenName = unifiedGroupUser.GivenName != null ? unifiedGroupUser.GivenName : string.Empty, + Surname = unifiedGroupUser.Surname != null ? unifiedGroupUser.Surname : string.Empty, + Email = unifiedGroupUser.Mail != null ? unifiedGroupUser.Mail : string.Empty, + MobilePhone = unifiedGroupUser.MobilePhone != null ? unifiedGroupUser.DisplayName : string.Empty, + PreferredLanguage = unifiedGroupUser.PreferredLanguage != null ? unifiedGroupUser.PreferredLanguage : string.Empty, + JobTitle = unifiedGroupUser.JobTitle != null ? unifiedGroupUser.DisplayName : string.Empty, + BusinessPhones = unifiedGroupUser.BusinessPhones != null ? unifiedGroupUser.BusinessPhones.ToArray() : null + }; + unifiedGroupUsers.Add(groupUser); + return true; + }); + + await pageIterator.IterateAsync(); - // Create the collection of type OfficeDevPnP 'UnifiedGroupUser' after all users are retrieved, including paged data. - if (unifiedGroupGraphUsers != null && unifiedGroupGraphUsers.Count > 0) - { - unifiedGroupUsers = new List(); - foreach (User usr in unifiedGroupGraphUsers) - { - UnifiedGroupUser groupUser = new UnifiedGroupUser - { - Id = usr.Id, - UserPrincipalName = usr.UserPrincipalName != null ? usr.UserPrincipalName : string.Empty, - DisplayName = usr.DisplayName != null ? usr.DisplayName : string.Empty, - GivenName = usr.GivenName != null ? usr.GivenName : string.Empty, - Surname = usr.Surname != null ? usr.Surname : string.Empty, - Email = usr.Mail != null ? usr.Mail : string.Empty, - MobilePhone = usr.MobilePhone != null ? usr.DisplayName : string.Empty, - PreferredLanguage = usr.PreferredLanguage != null ? usr.PreferredLanguage : string.Empty, - JobTitle = usr.JobTitle != null ? usr.DisplayName : string.Empty, - BusinessPhones = usr.BusinessPhones != null ? usr.BusinessPhones.ToArray() : null - }; - unifiedGroupUsers.Add(groupUser); - } - } return unifiedGroupUsers; }).GetAwaiter().GetResult(); @@ -1698,13 +1513,14 @@ private static List GetUsers(GraphServiceClient graphClient, string[] grou try { // Search for the user object - IGraphServiceUsersCollectionPage userQuery = await graphClient.Users - .Request() - .Select("Id") - .Filter($"userPrincipalName eq '{Uri.EscapeDataString(groupUser.Replace("'", "''"))}'") - .GetAsync(); + var userQuery = await graphClient.Users + .GetAsync(requestConfiguration => + { + requestConfiguration.QueryParameters.Select = new string[] { "Id" }; + requestConfiguration.QueryParameters.Filter = $"userPrincipalName eq '{Uri.EscapeDataString(groupUser.Replace("'", "''"))}'"; + }); - User user = userQuery.FirstOrDefault(); + User user = userQuery?.Value.FirstOrDefault(); if (user != null) { usersResult.Add(user); diff --git a/src/lib/PnP.Framework/Graph/UsersUtility.cs b/src/lib/PnP.Framework/Graph/UsersUtility.cs index 5cd63bd09..d98418810 100644 --- a/src/lib/PnP.Framework/Graph/UsersUtility.cs +++ b/src/lib/PnP.Framework/Graph/UsersUtility.cs @@ -1,5 +1,6 @@ using Microsoft.Graph; using Microsoft.Graph.Models; +using Microsoft.Kiota.Abstractions; using Newtonsoft.Json; using PnP.Framework.Diagnostics; using System; @@ -115,48 +116,26 @@ public static Model.User GetUser(string accessToken, string userPrincipalName, s var graphClient = GraphUtility.CreateGraphClient(accessToken, retryCount, delay, useBetaEndPoint: useBetaEndPoint); - IGraphServiceUsersCollectionPage pagedUsers; - // Retrieve the first batch of users. 999 is the maximum amount of users that Graph allows to be trieved in 1 go. Use maximum size batches to lessen the chance of throttling when retrieving larger amounts of users. - pagedUsers = await graphClient.Users.Request() - .Select(string.Join(",", propertiesToSelect)) - .Filter(filter) - .OrderBy(orderby) - .Top(!endIndex.HasValue ? 999 : endIndex.Value >= 999 ? 999 : endIndex.Value) - .GetAsync(); - - int pageCount = 0; - int currentIndex = 0; - - while (true) + var pagedUsers = await graphClient.Users + .GetAsync(requestConfiguration => + { + requestConfiguration.QueryParameters.Select = propertiesToSelect.ToArray(); + requestConfiguration.QueryParameters.Filter = filter; + requestConfiguration.QueryParameters.Orderby = new string[] { orderby }; + requestConfiguration.QueryParameters.Top = !endIndex.HasValue ? 999 : endIndex.Value >= 999 ? 999 : endIndex.Value; + }); + + var pageIterator = PageIterator.CreatePageIterator(graphClient, pagedUsers, (graphUser) => { - pageCount++; - - foreach (var pagedUser in pagedUsers) + if(graphUser != null) { - currentIndex++; - - if(endIndex.HasValue && endIndex.Value < currentIndex) - { - break; - } - - if (currentIndex >= startIndex) - { - users.Add(MapUserEntity(pagedUser, selectProperties)); - } + users.Add(MapUserEntity(graphUser, selectProperties)); } + return true; + }); - if (pagedUsers.NextPageRequest != null && (!endIndex.HasValue || currentIndex < endIndex.Value)) - { - // Retrieve the next batch of users. The possible oData instructions such as select and filter are already incorporated in the nextLink provided by Graph and thus do not need to be specified again. - pagedUsers = await pagedUsers.NextPageRequest.GetAsync(); - } - else - { - break; - } - } + await pageIterator.IterateAsync(); return users; }).GetAwaiter().GetResult(); @@ -206,13 +185,6 @@ public static Model.UserDelta ListUserDelta(string accessToken, string deltaToke } } - var queryOptions = new List(); - - if(!string.IsNullOrWhiteSpace(deltaToken)) - { - queryOptions.Add(new QueryOption("$skiptoken", deltaToken)); - } - Model.UserDelta result = null; try { @@ -224,16 +196,22 @@ public static Model.UserDelta ListUserDelta(string accessToken, string deltaToke var graphClient = GraphUtility.CreateGraphClient(accessToken, retryCount, delay, useBetaEndPoint: useBetaEndPoint); - IUserDeltaCollectionPage pagedUsers; - // Retrieve the first batch of users. 999 is the maximum amount of users that Graph allows to be trieved in 1 go. Use maximum size batches to lessen the chance of throttling when retrieving larger amounts of users. - pagedUsers = await graphClient.Users.Delta() - .Request(queryOptions) - .Select(string.Join(",", propertiesToSelect)) - .Filter(filter) - .OrderBy(orderby) - .Top(!endIndex.HasValue ? 999 : endIndex.Value >= 999 ? 999 : endIndex.Value) - .GetAsync(); + var pagedUsers = graphClient.Users.Delta + .ToGetRequestInformation((requestConfiguration) => + { + requestConfiguration.QueryParameters.Select = propertiesToSelect.ToArray(); + requestConfiguration.QueryParameters.Filter = filter; + requestConfiguration.QueryParameters.Orderby = new string[] { orderby }; + requestConfiguration.QueryParameters.Top = !endIndex.HasValue ? 999 : endIndex.Value >= 999 ? 999 : endIndex.Value; + }); + + if (!string.IsNullOrEmpty(deltaToken)) + { + pagedUsers.QueryParameters.Add("%24skiptoken", deltaToken); + } + + var result = await graphClient.RequestAdapter.SendAsync(pagedUsers, UserCollectionResponse.CreateFromDiscriminatorValue); int pageCount = 0; int currentIndex = 0; From 07f3725db058a004a3d7b05e539c6ea4e94d7007 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Mon, 20 Mar 2023 16:53:00 +0200 Subject: [PATCH 4/7] Undo pass --- src/lib/ConsoleApp2/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/ConsoleApp2/Program.cs b/src/lib/ConsoleApp2/Program.cs index 8dfc4859c..92f7ee915 100644 --- a/src/lib/ConsoleApp2/Program.cs +++ b/src/lib/ConsoleApp2/Program.cs @@ -11,7 +11,7 @@ static void Main(string[] args) Console.WriteLine("Hello, World!"); var username = "gautam@gautamdev.onmicrosoft.com"; - var pass = "wQ5FROvgTNp121mVUNhrdrPOuyUa9OiO"; + var pass = ""; var securePassword = new SecureString(); From bf761363a58010cddff48be17ea53fc82c30b48e Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Mon, 20 Mar 2023 16:58:06 +0200 Subject: [PATCH 5/7] Remove test console app --- src/lib/PnP.Framework.sln | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/lib/PnP.Framework.sln b/src/lib/PnP.Framework.sln index 72489ccf0..ee64611c7 100644 --- a/src/lib/PnP.Framework.sln +++ b/src/lib/PnP.Framework.sln @@ -14,8 +14,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PnP.Framework.Test", "PnP.F EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PnP.Framework.Modernization.Test", "PnP.Framework.Modernization.Test\PnP.Framework.Modernization.Test.csproj", "{2185B0F4-9423-4458-BFB6-7502D98A4259}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleApp2", "ConsoleApp2\ConsoleApp2.csproj", "{3DC23543-2BB5-4FDF-842C-CE9058EFF25E}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -34,10 +32,6 @@ Global {2185B0F4-9423-4458-BFB6-7502D98A4259}.Debug|Any CPU.Build.0 = Debug|Any CPU {2185B0F4-9423-4458-BFB6-7502D98A4259}.Release|Any CPU.ActiveCfg = Release|Any CPU {2185B0F4-9423-4458-BFB6-7502D98A4259}.Release|Any CPU.Build.0 = Release|Any CPU - {3DC23543-2BB5-4FDF-842C-CE9058EFF25E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3DC23543-2BB5-4FDF-842C-CE9058EFF25E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3DC23543-2BB5-4FDF-842C-CE9058EFF25E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3DC23543-2BB5-4FDF-842C-CE9058EFF25E}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From aadc80e5d87ffab43dcc71369465226d8e30abac Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Mon, 20 Mar 2023 17:00:32 +0200 Subject: [PATCH 6/7] Delete ConsoleApp2.csproj --- src/lib/ConsoleApp2/ConsoleApp2.csproj | 14 -------------- 1 file changed, 14 deletions(-) delete mode 100644 src/lib/ConsoleApp2/ConsoleApp2.csproj diff --git a/src/lib/ConsoleApp2/ConsoleApp2.csproj b/src/lib/ConsoleApp2/ConsoleApp2.csproj deleted file mode 100644 index a0856280c..000000000 --- a/src/lib/ConsoleApp2/ConsoleApp2.csproj +++ /dev/null @@ -1,14 +0,0 @@ - - - - Exe - net6.0 - enable - enable - - - - - - - From 96e99664904bce195b60745d1a4c2d6529a4fcd2 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Mon, 20 Mar 2023 17:00:39 +0200 Subject: [PATCH 7/7] Delete Program.cs --- src/lib/ConsoleApp2/Program.cs | 31 ------------------------------- 1 file changed, 31 deletions(-) delete mode 100644 src/lib/ConsoleApp2/Program.cs diff --git a/src/lib/ConsoleApp2/Program.cs b/src/lib/ConsoleApp2/Program.cs deleted file mode 100644 index 92f7ee915..000000000 --- a/src/lib/ConsoleApp2/Program.cs +++ /dev/null @@ -1,31 +0,0 @@ -using Microsoft.SharePoint.Client; -using PnP.Framework; -using System.Security; - -namespace ConsoleApp2 -{ - internal class Program - { - static void Main(string[] args) - { - Console.WriteLine("Hello, World!"); - - var username = "gautam@gautamdev.onmicrosoft.com"; - var pass = ""; - - var securePassword = new SecureString(); - - foreach (char c in pass) - securePassword.AppendChar(c); - - using (var authenticationMgr = new AuthenticationManager(username, securePassword)) - { - var ctx = authenticationMgr.GetContext("https://gautamdev.sharepoint.com/sites/testtz1"); - - ctx.Web.EnsureProperty(w => w.Title); - - var value = ctx.Web; - } - } - } -} \ No newline at end of file