From 8af4765fe1f7f5e01e465a92abb9661489b6f78a Mon Sep 17 00:00:00 2001 From: mollerentornos Date: Fri, 13 Sep 2024 09:05:30 +0200 Subject: [PATCH] Microsoft Integration: Correccions i millores (#30) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * EVDOC01-155: Correcció en estat "error de convidats" quan la sincronització * EVDOC01-155: Creació de users a microsoft per batch i control de excepcions de Microsoft --- .../microsoft/api/MicrosoftCommonService.java | 10 +- .../microsoft/api/model/MicrosoftLog.java | 3 + .../impl/MicrosoftCommonServiceImpl.java | 259 +++++++++++++++++- .../MicrosoftSynchronizationServiceImpl.java | 82 +++--- 4 files changed, 306 insertions(+), 48 deletions(-) diff --git a/microsoft-integration/api/src/java/org/sakaiproject/microsoft/api/MicrosoftCommonService.java b/microsoft-integration/api/src/java/org/sakaiproject/microsoft/api/MicrosoftCommonService.java index e6c26810e24..7df04803136 100644 --- a/microsoft-integration/api/src/java/org/sakaiproject/microsoft/api/MicrosoftCommonService.java +++ b/microsoft-integration/api/src/java/org/sakaiproject/microsoft/api/MicrosoftCommonService.java @@ -24,9 +24,11 @@ import org.sakaiproject.microsoft.api.data.MicrosoftTeam; import org.sakaiproject.microsoft.api.data.MicrosoftUser; import org.sakaiproject.microsoft.api.data.MicrosoftUserIdentifier; +import org.sakaiproject.microsoft.api.data.SynchronizationStatus; import org.sakaiproject.microsoft.api.data.TeamsMeetingData; import org.sakaiproject.microsoft.api.exceptions.MicrosoftCredentialsException; import org.sakaiproject.microsoft.api.exceptions.MicrosoftGenericException; +import org.sakaiproject.microsoft.api.model.GroupSynchronization; import org.sakaiproject.microsoft.api.model.SiteSynchronization; import org.sakaiproject.site.api.Group; import org.sakaiproject.user.api.User; @@ -35,6 +37,7 @@ import java.time.Instant; import java.util.ArrayList; import java.util.HashMap; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; @@ -110,7 +113,9 @@ public static enum PermissionRoles { READ, WRITE } boolean addOwnerToGroup(String userId, String groupId) throws MicrosoftCredentialsException; boolean addMemberToTeam(String userId, String teamId) throws MicrosoftCredentialsException; boolean addOwnerToTeam(String userId, String teamId) throws MicrosoftCredentialsException; - + + SynchronizationStatus addUsersToTeamOrGroup(String teamId, List members, SynchronizationStatus status, LinkedList roles) throws MicrosoftCredentialsException; + boolean removeUserFromGroup(String userId, String groupId) throws MicrosoftCredentialsException; boolean removeMemberFromTeam(String memberId, String teamId) throws MicrosoftCredentialsException; boolean removeAllMembersFromTeam(String teamId) throws MicrosoftCredentialsException; @@ -135,7 +140,8 @@ public static enum PermissionRoles { READ, WRITE } boolean addMemberToChannel(String userId, String teamId, String channelId) throws MicrosoftCredentialsException; boolean addOwnerToChannel(String userId, String teamId, String channelId) throws MicrosoftCredentialsException; - + SynchronizationStatus addUsersToChannel(SiteSynchronization ss, GroupSynchronization gs, List members, SynchronizationStatus status, LinkedList roles) throws MicrosoftCredentialsException; + boolean removeMemberFromChannel(String memberId, String teamId, String channelId) throws MicrosoftCredentialsException; // ---------------------------------------- ONLINE MEETINGS -------------------------------------------------- diff --git a/microsoft-integration/api/src/java/org/sakaiproject/microsoft/api/model/MicrosoftLog.java b/microsoft-integration/api/src/java/org/sakaiproject/microsoft/api/model/MicrosoftLog.java index bf20680a1bf..6a455e60b56 100644 --- a/microsoft-integration/api/src/java/org/sakaiproject/microsoft/api/model/MicrosoftLog.java +++ b/microsoft-integration/api/src/java/org/sakaiproject/microsoft/api/model/MicrosoftLog.java @@ -99,6 +99,9 @@ public class MicrosoftLog { public static final String EVENT_AUTOCONFIG = "event.autoconfig"; + public static final String EVENT_TOO_MANY_REQUESTS = "event.too_many_requests"; + public static final String EVENT_USER_NOT_FOUND_ON_TEAM = "event.user_not_found_on_team"; + public enum Status { diff --git a/microsoft-integration/impl/src/main/java/org/sakaiproject/microsoft/impl/MicrosoftCommonServiceImpl.java b/microsoft-integration/impl/src/main/java/org/sakaiproject/microsoft/impl/MicrosoftCommonServiceImpl.java index 64af68998c6..d43adfae29f 100644 --- a/microsoft-integration/impl/src/main/java/org/sakaiproject/microsoft/impl/MicrosoftCommonServiceImpl.java +++ b/microsoft-integration/impl/src/main/java/org/sakaiproject/microsoft/impl/MicrosoftCommonServiceImpl.java @@ -25,6 +25,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Base64; +import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; @@ -45,6 +46,7 @@ import com.microsoft.graph.http.GraphServiceException; import com.microsoft.graph.http.HttpMethod; import com.microsoft.graph.options.QueryOption; +import com.microsoft.graph.requests.ConversationMemberCollectionRequest; import com.microsoft.graph.requests.GroupRequest; import org.apache.commons.lang3.RandomStringUtils; import org.apache.logging.log4j.LogManager; @@ -66,12 +68,15 @@ import org.sakaiproject.microsoft.api.data.MicrosoftTeam; import org.sakaiproject.microsoft.api.data.MicrosoftUser; import org.sakaiproject.microsoft.api.data.MicrosoftUserIdentifier; +import org.sakaiproject.microsoft.api.data.SynchronizationStatus; import org.sakaiproject.microsoft.api.data.TeamsMeetingData; import org.sakaiproject.microsoft.api.exceptions.MicrosoftCredentialsException; import org.sakaiproject.microsoft.api.exceptions.MicrosoftGenericException; import org.sakaiproject.microsoft.api.exceptions.MicrosoftInvalidCredentialsException; import org.sakaiproject.microsoft.api.exceptions.MicrosoftInvalidInvitationException; import org.sakaiproject.microsoft.api.exceptions.MicrosoftNoCredentialsException; +import org.sakaiproject.microsoft.api.model.GroupSynchronization; +import org.sakaiproject.microsoft.api.model.MicrosoftLog; import org.sakaiproject.microsoft.api.model.SiteSynchronization; import org.sakaiproject.microsoft.api.persistence.MicrosoftConfigRepository; import org.sakaiproject.microsoft.api.persistence.MicrosoftLoggingRepository; @@ -287,11 +292,15 @@ public List getUsers() throws MicrosoftCredentialsException { .email(u.mail) .guest(MicrosoftUser.GUEST.equalsIgnoreCase(u.userType)) .build()).collect(Collectors.toList())); + userList.forEach(u -> log.debug(u.toString())); UserCollectionRequestBuilder builder = page.getNextPage(); if (builder == null) break; page = builder.buildRequest().get(); } + HashMap userMap = (HashMap) userList.stream().collect(Collectors.toMap(MicrosoftUser::getId, u -> u)); + getCache().put(CACHE_USERS, userMap); + return userList; } @@ -878,12 +887,6 @@ public boolean deleteTeam(String teamId) throws MicrosoftCredentialsException { @Override public MicrosoftMembersCollection getTeamMembers(String id, MicrosoftUserIdentifier key) throws MicrosoftCredentialsException { - Cache.ValueWrapper cachedValue = getCache().get(CACHE_MEMBERS); - if (cachedValue != null) { - MicrosoftMembersCollection membersMap = (MicrosoftMembersCollection) cachedValue.get(); - return membersMap; - } - MicrosoftMembersCollection ret = new MicrosoftMembersCollection(); try { MicrosoftCredentials credentials = microsoftConfigRepository.getCredentials(); @@ -893,7 +896,6 @@ public MicrosoftMembersCollection getTeamMembers(String id, MicrosoftUserIdentif .members() .buildRequest() .get(); - while (page != null) { for (ConversationMember m : page.getCurrentPage()) { AadUserConversationMember member = (AadUserConversationMember) m; @@ -925,8 +927,6 @@ public MicrosoftMembersCollection getTeamMembers(String id, MicrosoftUserIdentif if (builder == null) break; page = builder.buildRequest().get(); } - - getCache().put(CACHE_MEMBERS, ret); } catch (MicrosoftCredentialsException e) { throw e; } catch (Exception ex) { @@ -1063,6 +1063,77 @@ public boolean addOwnerToTeam(String userId, String teamId) throws MicrosoftCred return true; } + @Override + public SynchronizationStatus addUsersToTeamOrGroup(String teamId, List members, SynchronizationStatus status, LinkedList roles) throws MicrosoftCredentialsException { + boolean res = false; + String dataKey = roles.contains(MicrosoftUser.OWNER) ? "ownerId" : "memberId"; + + ConversationMemberCollectionRequest postMembers = graphClient.teams(teamId).members() + .buildRequest(); + + final int MAX_RETRY = 2; + final int MAX_PER_REQUEST = 20; + final int MAX_REQUESTS = members.size() / MAX_PER_REQUEST; + + for (int i = 0; i <= MAX_REQUESTS; i++) { + List pendingMembers = members.subList(i * MAX_PER_REQUEST, Math.min(MAX_PER_REQUEST * (i +1 ), members.size())); + List successMembers = new LinkedList<>(); + + int retryCount = 0; + while (!pendingMembers.isEmpty() && retryCount < MAX_RETRY) { + BatchRequestContent batchRequestContent = new BatchRequestContent(); + + members.forEach(member -> { + ConversationMember memberToAdd = new ConversationMember(); + + memberToAdd.oDataType = "#microsoft.graph.aadUserConversationMember"; + memberToAdd.roles = roles; + memberToAdd.additionalDataManager().put("user@odata.bind", new JsonPrimitive("https://graph.microsoft.com/v1.0/users('" + member.getId() + "')")); + + batchRequestContent.addBatchRequestStep(postMembers, HttpMethod.POST, memberToAdd); + }); + + BatchResponseContent responseContent = getGraphClient().batch().buildRequest().post(batchRequestContent); + HashMap membersResponse = parseBatchResponse(responseContent, members); + + successMembers.addAll((List) membersResponse.get("success")); + pendingMembers = (List) membersResponse.get("failed"); + List> errors = (List>) membersResponse.get("errors"); + handleMicrosoftExceptions(errors); + retryCount++; + } + + for (MicrosoftUser pendingMember : pendingMembers) { + if (!res && status != SynchronizationStatus.ERROR) { + //once ERROR status is set, do not check it again + status = (pendingMember != null && pendingMember.isGuest()) ? SynchronizationStatus.ERROR_GUEST : SynchronizationStatus.ERROR; + } + + // save log add member + microsoftLoggingRepository.save(MicrosoftLog.builder() + .event(MicrosoftLog.EVENT_ADD_MEMBER) + .status((pendingMember != null && pendingMember.isGuest()) ? MicrosoftLog.Status.OK : MicrosoftLog.Status.KO) + .addData("teamId", teamId) + .addData(dataKey, pendingMember != null ? pendingMember.getId() : "null") + .build()); + + } + + successMembers.forEach(member -> { + // save log add member + microsoftLoggingRepository.save(MicrosoftLog.builder() + .event(MicrosoftLog.EVENT_ADD_MEMBER) + .status(MicrosoftLog.Status.OK) + .addData("teamId", teamId) + .addData(dataKey, member.getId()) + .build()); + }); + + } + + return status; + } + @Override public boolean removeUserFromGroup(String userId, String groupId) throws MicrosoftCredentialsException { try { @@ -1375,6 +1446,9 @@ public List createChannels(List) listToProcess); + break; case "BaseGroup": resultMap = parseBatchResponseToMicrosoftChannel(responseContent, listToProcess); break; @@ -1386,6 +1460,53 @@ public List createChannels(List parseBatchResponseToMicrosoftUser(BatchResponseContent responseContent, List listToProcess) { + HashMap responseMap = new HashMap<>(); + + Map successRequests = + responseContent.responses.stream().filter(r -> r.status <= 299).collect(Collectors.toList()) + .stream().map(r -> { + Map.Entry entry = new AbstractMap.SimpleEntry<>( + r.body.getAsJsonObject().get("userId").getAsString(), + listToProcess.stream().filter(user -> user.getId().equals(r.body.getAsJsonObject().get("userId").getAsString())).findFirst().orElse(null) + ); + return entry; + }).collect(Collectors.toMap( + Map.Entry::getKey, + Map.Entry::getValue + )); + + List pendingRequests = listToProcess.stream() + .filter(user -> !successRequests.containsKey(user.getId())) + .collect(Collectors.toList()); + + List> errors = responseContent.responses.stream() + .filter(r -> r.status > 299) + .map(r -> { + String code, innerError; + try { + code = r.body.getAsJsonObject().get("error").getAsJsonObject().get("code").getAsString(); + innerError = r.body.getAsJsonObject().get("error").getAsJsonObject().get("innerError").getAsJsonObject().get("code").getAsString(); + } catch (Exception e) { + code = "Failure"; + innerError = "Failure"; + } + return Map.of( + "status", r.status, + "retryAfter", r.headers.containsKey("Retry-After") ? r.headers.get("Retry-After") : 5, + "code", code, + "innerError", innerError); + }) + .collect(Collectors.toList()); + + + responseMap.put("success", new ArrayList<>(successRequests.values())); + responseMap.put("failed", pendingRequests); + responseMap.put("errors", errors); + + return responseMap; + } + private HashMap parseBatchResponseToMicrosoftTeam(BatchResponseContent responseContent, List listToProcess) { HashMap responseMap = new HashMap<>(); @@ -1642,6 +1763,126 @@ public boolean addOwnerToChannel(String userId, String teamId, String channelId) return true; } + public SynchronizationStatus addUsersToChannel(SiteSynchronization ss, GroupSynchronization gs, List members, SynchronizationStatus status, LinkedList roles) throws MicrosoftCredentialsException { + String teamId = ss.getTeamId(); + String channelId = gs.getChannelId(); + boolean res = false; + + ConversationMemberCollectionRequest postMembers = graphClient.teams(teamId).channels(channelId).members() + .buildRequest(); + + final int MAX_RETRY = 2; + final int MAX_PER_REQUEST = 20; + final int MAX_REQUESTS = members.size() / MAX_PER_REQUEST; + + for (int i = 0; i <= MAX_REQUESTS; i++) { + List pendingMembers = members.subList(i * MAX_PER_REQUEST, Math.min(MAX_PER_REQUEST * (i +1 ), members.size())); + List successMembers = new LinkedList<>(); + + int retryCount = 0; + while (!pendingMembers.isEmpty() && retryCount < MAX_RETRY) { + BatchRequestContent batchRequestContent = new BatchRequestContent(); + + members.forEach(member -> { + ConversationMember memberToAdd = new ConversationMember(); + + memberToAdd.oDataType = "#microsoft.graph.aadUserConversationMember"; + memberToAdd.roles = roles; + memberToAdd.additionalDataManager().put("user@odata.bind", new JsonPrimitive("https://graph.microsoft.com/v1.0/users('" + member.getId() + "')")); + + batchRequestContent.addBatchRequestStep(postMembers, HttpMethod.POST, memberToAdd); + }); + + BatchResponseContent responseContent = getGraphClient().batch().buildRequest().post(batchRequestContent); + + HashMap membersResponse = parseBatchResponse(responseContent, members); + + successMembers.addAll((List) membersResponse.get("success")); + pendingMembers = (List) membersResponse.get("failed"); + List> errors = (List>) membersResponse.get("errors"); + handleMicrosoftExceptions(errors); + retryCount++; + } + + for (MicrosoftUser pendingMember : pendingMembers) { + if (status != SynchronizationStatus.ERROR) { + //once ERROR status is set, do not check it again + status = (pendingMember != null && pendingMember.isGuest()) ? SynchronizationStatus.ERROR_GUEST : SynchronizationStatus.ERROR; + } + + //save log + microsoftLoggingRepository.save(MicrosoftLog.builder() + .event(MicrosoftLog.EVENT_USER_ADDED_TO_CHANNEL) + .status(MicrosoftLog.Status.KO) + .addData("email", pendingMember.getEmail()) + .addData("microsoftUserId", pendingMember.getId()) + .addData("siteId", ss.getSiteId()) + .addData("teamId", ss.getTeamId()) + .addData("groupId", gs.getGroupId()) + .addData("channelId", gs.getChannelId()) + .addData("owner", Boolean.toString(roles.contains(MicrosoftUser.OWNER) && !pendingMember.isGuest())) + .addData("guest", Boolean.toString(pendingMember.isGuest())) + .build()); + + } + + successMembers.forEach(member -> { + //save log + microsoftLoggingRepository.save(MicrosoftLog.builder() + .event(MicrosoftLog.EVENT_USER_ADDED_TO_CHANNEL) + .status(MicrosoftLog.Status.OK) + .addData("email", member.getEmail()) + .addData("microsoftUserId", member.getId()) + .addData("siteId", ss.getSiteId()) + .addData("teamId", ss.getTeamId()) + .addData("groupId", gs.getGroupId()) + .addData("channelId", gs.getChannelId()) + .addData("owner", Boolean.toString(roles.contains(MicrosoftUser.OWNER) && !member.isGuest())) + .addData("guest", Boolean.toString(member.isGuest())) + .build()); + }); + + } + + return status; + } + + private void handleMicrosoftExceptions(List> errors) { + if(!errors.isEmpty()) { + + if(errors.stream().anyMatch(e -> e.containsValue(429))) { + Map error = errors.stream().filter(e -> e.containsValue(429)).findFirst().get(); + microsoftLoggingRepository.save(MicrosoftLog.builder() + .event(MicrosoftLog.EVENT_TOO_MANY_REQUESTS) + .addData("Status", error.get("status").toString()) + .addData("Code", error.get("code").toString()) + .addData("RetryAfter", error.get("retryAfter").toString()) + .addData("InnerError", error.get("innerError").toString()) + .build()); + int retryAfter = Integer.parseInt(error.get("retryAfter").toString()); + + try { + Thread.sleep(retryAfter * 1000L); + } catch (InterruptedException ignored) { + } + } else if (errors.stream().anyMatch(e -> e.containsValue(404))) { + Map error = errors.stream().filter(e -> e.containsValue(404)).findFirst().get(); + microsoftLoggingRepository.save(MicrosoftLog.builder() + .event(MicrosoftLog.EVENT_USER_NOT_FOUND_ON_TEAM) + .addData("Status", error.get("status").toString()) + .addData("Code", error.get("code").toString()) + .addData("RetryAfter", error.get("retryAfter").toString()) + .addData("InnerError", error.get("innerError").toString()) + .build()); + int retryAfter = Integer.parseInt(error.get("retryAfter").toString()); + try { + Thread.sleep(retryAfter * 1000L); + } catch (InterruptedException ignored) { + } + } + } + } + @Override public boolean removeMemberFromChannel(String memberId, String teamId, String channelId) throws MicrosoftCredentialsException { try { diff --git a/microsoft-integration/impl/src/main/java/org/sakaiproject/microsoft/impl/MicrosoftSynchronizationServiceImpl.java b/microsoft-integration/impl/src/main/java/org/sakaiproject/microsoft/impl/MicrosoftSynchronizationServiceImpl.java index b2d5cd985da..6a9affbb1a7 100644 --- a/microsoft-integration/impl/src/main/java/org/sakaiproject/microsoft/impl/MicrosoftSynchronizationServiceImpl.java +++ b/microsoft-integration/impl/src/main/java/org/sakaiproject/microsoft/impl/MicrosoftSynchronizationServiceImpl.java @@ -16,7 +16,12 @@ package org.sakaiproject.microsoft.impl; import java.time.LocalTime; +import java.time.ZoneId; import java.time.ZonedDateTime; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.LinkedList; import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; @@ -651,9 +656,11 @@ public SynchronizationStatus runSiteSynchronization(SiteSynchronization ss) thro Map guestUsers = new HashMap<>(); + //microsoftCommonService.getUsers(); + List membersToMicrosoft = new ArrayList<>(); + //process sakai members not in the team for (String id : filteredSiteMembers.getMembers().keySet()) { - boolean res = false; MicrosoftUser mu = null; //if ID is NOT empty. (can be empty if user property does not exist or is blank) if (!id.startsWith("EMPTY_")) { @@ -677,28 +684,25 @@ public SynchronizationStatus runSiteSynchronization(SiteSynchronization ss) thro .addData("teamId", ss.getTeamId()) .addData("userId", u.getId()) .build()); + } else if (ret != SynchronizationStatus.ERROR) { + //once ERROR status is set, do not check it again + ret = SynchronizationStatus.ERROR_GUEST; } } if (mu != null) { //add to team/group - res = addMemberToMicrosoftGroupOrTeam(ss, mu); - } - if (!res && ret != SynchronizationStatus.ERROR) { - //once ERROR status is set, do not check it again - ret = (mu != null && mu.isGuest()) ? SynchronizationStatus.ERROR_GUEST : SynchronizationStatus.ERROR; + membersToMicrosoft.add(mu); + } else { + ret = SynchronizationStatus.ERROR; } - // save log add member - microsoftLoggingRepository.save(MicrosoftLog.builder() - .event(MicrosoftLog.EVENT_ADD_MEMBER) - .status(res ? MicrosoftLog.Status.OK : MicrosoftLog.Status.KO) - .addData("teamId", ss.getTeamId()) - .addData("memberId", mu != null ? mu.getId() : "null") - .build()); } + //add members to team + ret = microsoftCommonService.addUsersToTeamOrGroup(ss.getTeamId(), membersToMicrosoft, ret, new LinkedList<>()); + + List ownersToMicrosoft = new ArrayList<>(); //process sakai owners not in the team for (String id : filteredSiteMembers.getOwners().keySet()) { - boolean res = false; MicrosoftUser mu = null; if (!id.startsWith("EMPTY_")) { mu = microsoftCommonService.getUser(id, mappedMicrosoftUserId); @@ -713,33 +717,29 @@ public SynchronizationStatus runSiteSynchronization(SiteSynchronization ss) thro //store newly invited user in getsUsers map -> used in group synch in case this user do not appear yet in Microsoft registers id = sakaiProxy.getMemberKeyValue(sakaiProxy.getUser(u.getId()), mappedSakaiUserId); guestUsers.put(id, mu); + + // save log invitation created + microsoftLoggingRepository.save(MicrosoftLog.builder() + .event(MicrosoftLog.EVENT_INVITATION_CREATED) + .status(MicrosoftLog.Status.OK) + .addData("teamId", ss.getTeamId()) + .addData("userId", u.getId()) + .build()); + } else if (ret != SynchronizationStatus.ERROR) { + //once ERROR status is set, do not check it again + ret = SynchronizationStatus.ERROR_GUEST; } - // save log invitation created - microsoftLoggingRepository.save(MicrosoftLog.builder() - .event(MicrosoftLog.EVENT_INVITATION_CREATED) - .status(MicrosoftLog.Status.OK) - .addData("teamId", ss.getTeamId()) - .addData("userId", u.getId()) - .build()); } if (mu != null) { //add to team/group - res = addOwnerToMicrosoftGroupOrTeam(ss, mu); - } - if (!res && ret != SynchronizationStatus.ERROR) { - //once ERROR status is set, do not check it again - ret = (mu != null && mu.isGuest()) ? SynchronizationStatus.ERROR_GUEST : SynchronizationStatus.ERROR; + ownersToMicrosoft.add(mu); + }else { + ret = SynchronizationStatus.ERROR; } - - // save log add owner - microsoftLoggingRepository.save(MicrosoftLog.builder() - .event(MicrosoftLog.EVENT_ADD_OWNER) - .status(res ? MicrosoftLog.Status.OK : MicrosoftLog.Status.KO) - .addData("teamId", ss.getTeamId()) - .addData("ownerId", mu != null ? mu.getId() : "null") - .build()); } + ret = microsoftCommonService.addUsersToTeamOrGroup(ss.getTeamId(), ownersToMicrosoft, ret, new LinkedList<>(Collections.singletonList(MicrosoftUser.OWNER))); + //process all group synchronizations related if (ss.getGroupSynchronizationsList() != null && ss.getGroupSynchronizationsList().size() > 0) { int groupCounter = 0; @@ -765,6 +765,7 @@ public SynchronizationStatus runSiteSynchronization(SiteSynchronization ss) thro groupCounter++; } } + } else { microsoftLoggingRepository.save(MicrosoftLog.builder() .event(MicrosoftLog.EVENT_SITE_SYNCRHO_END) @@ -774,7 +775,6 @@ public SynchronizationStatus runSiteSynchronization(SiteSynchronization ss) thro .addData("forced", Boolean.toString(ss.isForced())) .build()); } - ss.setStatus(ret); ss.setStatusUpdatedAt(ZonedDateTime.now()); saveOrUpdateSiteSynchronization(ss); @@ -979,34 +979,42 @@ private SynchronizationStatus runGroupSynchronization(SiteSynchronization ss, Gr filteredGroupMembers.getOwnerIds().stream().forEach(id -> log.debug("> {}", id)); } + List members = new ArrayList<>(); + List owners = new ArrayList<>(); + for (String id : filteredGroupMembers.getMemberIds()) { //if guest do not ask to microsoft MicrosoftUser mu = guestUsers.get(id) == null ? microsoftCommonService.getUser(id, mappedMicrosoftUserId) : guestUsers.get(id); if (mu != null) { + members.add(mu); //user exists -> add to channel //IMPORTANT: all non-existent users in Site, have been invited. So, should be no users in Group that do not exist in Microsoft //IMPORTANT 2: if user is just added to a group (because is guest/invited), maybe can not be added immediately to a channel - if (!addMemberToMicrosoftChannel(ss, gs, mu) && ret.equals(SynchronizationStatus.OK)) { + if (!addMemberToMicrosoftChannel(ss, gs, mu) && !ret.equals(SynchronizationStatus.ERROR)) { ret = SynchronizationStatus.ERROR_GUEST; } } } + ret = microsoftCommonService.addUsersToChannel(ss, gs, members, ret, new LinkedList<>()); for (String id : filteredGroupMembers.getOwnerIds()) { //if guest do not ask to microsoft MicrosoftUser mu = guestUsers.get(id) == null ? microsoftCommonService.getUser(id, mappedMicrosoftUserId) : guestUsers.get(id); if (mu != null) { + owners.add(mu); //user exists -> add to channel //IMPORTANT: all non-existent users in Site, have been invited. So, there are no users in Group that do not exist in Microsoft //IMPORTANT 2: if user is just added to a group (because is guest/invited), maybe can not be added immediately to a channel - if (!addOwnerToMicrosoftChannel(ss, gs, mu) && ret.equals(SynchronizationStatus.OK)) { + if (!addOwnerToMicrosoftChannel(ss, gs, mu) && !ret.equals(SynchronizationStatus.ERROR)) { ret = SynchronizationStatus.ERROR_GUEST; } } } + ret = microsoftCommonService.addUsersToChannel(ss, gs, owners, ret, new LinkedList<>(Collections.singletonList(MicrosoftUser.OWNER))); + } gs.setStatus(ret); gs.setStatusUpdatedAt(ZonedDateTime.now());