/// <summary>
    /// Get a page's worth of Groups
    /// </summary>
    /// <param name="onlineGroups"></param>
    /// <param name="pageToRequest">Page # we are requesting (1 based)</param>
    /// <param name="totalNumberPages">Total # of pages of data that Server can return us</param>
    private void ExecuteRequest_ForPage(
        List <SiteGroup> onlineGroups,
        int pageToRequest,
        bool downloadMemberList,
        out int totalNumberPages)
    {
        int pageSize = _onlineUrls.PageSize;
        //Create a web request, in including the users logged-in auth information in the request headers
        var urlQuery = _onlineUrls.Url_GroupsList(_onlineSession, pageSize, pageToRequest);

        _onlineSession.StatusLog.AddStatus("Web request: " + urlQuery, -10);
        XmlDocument xmlDoc = ResourceSafe_PerformWebRequest_GetXmlDocument(urlQuery, "get groups list");

        //Get all the group nodes
        var nsManager = XmlHelper.CreateTableauXmlNamespaceManager("iwsOnline");
        var groups    = xmlDoc.SelectNodes("//iwsOnline:group", nsManager);

        //Get information for each of the data sources
        foreach (XmlNode itemXml in groups)
        {
            SiteGroup thisGroup = null;
            try
            {
                thisGroup = new SiteGroup(
                    itemXml,
                    null);   //We'll get and add the list of users later (see below)
                onlineGroups.Add(thisGroup);
                SanityCheckGroup(thisGroup, itemXml);
            }
            catch (Exception exGetGroup)
            {
                AppDiagnostics.Assert(false, "Group parse error");
                _onlineSession.StatusLog.AddError("Error parsing group: " + itemXml.OuterXml + ", " + exGetGroup.Message);
            }


            //==============================================================
            //Get the set of users in the group
            //==============================================================
            if ((thisGroup != null) && (downloadMemberList))
            {
                try
                {
                    var downloadUsersInGroup = new DownloadUsersListInGroup(
                        _onlineSession,
                        thisGroup.Id);
                    downloadUsersInGroup.ExecuteRequest();
                    thisGroup.AddUsers(downloadUsersInGroup.Users);
                }
                catch (Exception exGetUsers)
                {
                    _onlineSession.StatusLog.AddError("Error parsing group's users: " + exGetUsers.Message);
                }
            }
        } //end: foreach

        //-------------------------------------------------------------------
        //Get the updated page-count
        //-------------------------------------------------------------------
        totalNumberPages = DownloadPaginationHelper.GetNumberOfPagesFromPagination(
            xmlDoc.SelectSingleNode("//iwsOnline:pagination", nsManager),
            pageSize);
    }
    /// <summary>
    /// Provisioning for a single group
    /// </summary>
    /// <param name="siteSignIn"></param>
    /// <param name="thisProvisionGroup"></param>
    /// <param name="existingGroups"></param>
    private void Execute_ProvisionGroups_SingleGroup(
        TableauServerSignIn siteSignIn,
        ProvisioningGroup thisProvisionGroup,
        DownloadGroupsList existingGroups,
        DownloadUsersList siteUsersList)
    {
        _statusLogs.AddStatusHeader("Provision the group: " + thisProvisionGroup.GroupName);

        var thisExistingGroup = existingGroups.FindGroupWithName(thisProvisionGroup.GroupName);
        ICollection <SiteUser> existingUsersInGroup = new List <SiteUser>();

        //If the Group does not exist on server then create it
        if (thisExistingGroup == null)
        {
            var createGroup = new SendCreateGroup(siteSignIn, thisProvisionGroup.GroupName);
            thisExistingGroup = createGroup.ExecuteRequest();

            CSVRecord_GroupModified(thisExistingGroup.Name, "created group", "");
            _statusLogs.AddStatus("Created group: " + thisExistingGroup.Name);
        }
        else
        {
            //Download the members of the group
            var downloadGroupMembers = new DownloadUsersListInGroup(siteSignIn, thisExistingGroup.Id);
            downloadGroupMembers.ExecuteRequest();
            existingUsersInGroup = downloadGroupMembers.Users;
        }

        //====================================================================================
        //Keep a list of the remaining users in the Server Site's group
        //====================================================================================
        var workingListUnexaminedUsers = new WorkingListSiteUsers(existingUsersInGroup);

        //====================================================================================
        //Go through each of the users we need to provision, and see if they are in the group
        //already
        //====================================================================================
        foreach (var provisionThisUser in thisProvisionGroup.Members)
        {
            var userInGroup = workingListUnexaminedUsers.FindUser(provisionThisUser);
            if (userInGroup != null)
            {
                //The user is already in the group, no need to add them
                workingListUnexaminedUsers.RemoveUser(userInGroup);
            }
            else
            {
                //Add the user to the group
                try
                {
                    Execute_ProvisionGroups_SingleGroup_AddUser(siteSignIn, provisionThisUser, thisExistingGroup, siteUsersList);
                }
                catch (Exception exAddUserToGroup) //Unexpected error case
                {
                    IwsDiagnostics.Assert(false, "811-700: Internal error adding user to group: " + exAddUserToGroup.Message);
                    _statusLogs.AddError("811-700: Internal error adding user to group: " + exAddUserToGroup.Message);
                }
            }
        }

        //==============================================================================
        //Remove any remaining users that are in the Server Site's Group but not in
        //our provisioning list
        //==============================================================================
        foreach (var unexpectedUser in workingListUnexaminedUsers.GetUsers())
        {
            try
            {
                Execute_ProvisionGroups_RemoveSingleUser(siteSignIn, unexpectedUser, thisExistingGroup);
            }
            catch (Exception exUnxpectedUsers)
            {
                _statusLogs.AddError("Error removing unexpected user in GROUP " + unexpectedUser.ToString() + ", " + exUnxpectedUsers.Message);
                CSVRecord_Error(unexpectedUser.Name, unexpectedUser.SiteRole, unexpectedUser.SiteAuthentication, "Error removing unexpected user in GROUP" + unexpectedUser.ToString() + ", " + exUnxpectedUsers.Message);
            }
        }
    }
    /// <summary>
    /// Get a page's worth of Groups
    /// </summary>
    /// <param name="onlineGroups"></param>
    /// <param name="pageToRequest">Page # we are requesting (1 based)</param>
    /// <param name="totalNumberPages">Total # of pages of data that Server can return us</param>
    private void ExecuteRequest_ForPage(
        List<SiteGroup> onlineGroups, 
        int pageToRequest, 
        out int totalNumberPages)
    {
        int pageSize = _onlineUrls.PageSize;
        //Create a web request, in including the users logged-in auth information in the request headers
        var urlQuery = _onlineUrls.Url_GroupsList(_onlineSession, pageSize, pageToRequest);
        var webRequest = CreateLoggedInWebRequest(urlQuery);
        webRequest.Method = "GET";

        _onlineSession.StatusLog.AddStatus("Web request: " + urlQuery, -10);
        var response = GetWebReponseLogErrors(webRequest, "get groups list");
        var xmlDoc = GetWebResponseAsXml(response);

        //Get all the group nodes
        var nsManager = XmlHelper.CreateTableauXmlNamespaceManager("iwsOnline");
        var groups = xmlDoc.SelectNodes("//iwsOnline:group", nsManager);

        //Get information for each of the data sources
        foreach (XmlNode itemXml in groups)
        {
            SiteGroup thisGroup = null;
            try
            {
                thisGroup = new SiteGroup(
                    itemXml, 
                    null);   //We'll get and add the list of users later (see below)
                onlineGroups.Add(thisGroup);
                SanityCheckGroup(thisGroup, itemXml);
            }
            catch(Exception exGetGroup)
            {
                AppDiagnostics.Assert(false, "Group parse error");
                _onlineSession.StatusLog.AddError("Error parsing group: " + itemXml.OuterXml + ", " + exGetGroup.Message);
            }


            //==============================================================
            //Get the set of users in the group
            //==============================================================
            if (thisGroup != null)
            {
                try
                {
                    var downloadUsersInGroup = new DownloadUsersListInGroup(
                        _onlineUrls, 
                        _onlineSession, 
                        thisGroup.Id);
                    downloadUsersInGroup.ExecuteRequest();
                    thisGroup.AddUsers(downloadUsersInGroup.Users);
                }
                catch (Exception exGetUsers)
                {
                    _onlineSession.StatusLog.AddError("Error parsing group's users: " + exGetUsers.Message);
                }
            }

        } //end: foreach

        //-------------------------------------------------------------------
        //Get the updated page-count
        //-------------------------------------------------------------------
        totalNumberPages = DownloadPaginationHelper.GetNumberOfPagesFromPagination(
            xmlDoc.SelectSingleNode("//iwsOnline:pagination", nsManager),
            pageSize);
    }