From fa8919207c66f45bf3bf1f78b71a4243aceac15b Mon Sep 17 00:00:00 2001 From: Azmi TOUIL <42934070+AzmiTouil@users.noreply.github.com> Date: Mon, 11 Sep 2023 12:26:46 +0200 Subject: [PATCH] Fix: Github connector repos not updated when changing organization - MEED-2454 - Meeds-io/MIPs#64 (#93) Prior to this change, when viewing an organization, repositories of previous organization are listed, it's due to a cache problem --- .../github/entity/WebhookEntity.java | 2 +- .../github/rest/HooksManagementRest.java | 24 ++++++++++++ .../services/GithubConsumerService.java | 10 ++--- .../github/services/WebhookService.java | 14 +++++++ .../impl/GithubConsumerServiceImpl.java | 6 +-- .../services/impl/WebhookServiceImpl.java | 38 ++++++++++++++++++- .../github/storage/GithubConsumerStorage.java | 12 +++--- .../cached/GithubConsumerCachedStorage.java | 15 ++++---- .../github/storage/cached/model/CacheKey.java | 15 ++------ ...ication-github-connector-configuration.xml | 23 ----------- .../github-connector.db.changelog-1.0.0.xml | 8 ++-- .../GithubAdminConnectorEventItem.vue | 2 +- .../js/GithubConnectorService.js | 20 ++++++++++ 13 files changed, 123 insertions(+), 66 deletions(-) diff --git a/gamification-github-services/src/main/java/org/exoplatform/gamification/github/entity/WebhookEntity.java b/gamification-github-services/src/main/java/org/exoplatform/gamification/github/entity/WebhookEntity.java index 0af4e8161..198d153c0 100644 --- a/gamification-github-services/src/main/java/org/exoplatform/gamification/github/entity/WebhookEntity.java +++ b/gamification-github-services/src/main/java/org/exoplatform/gamification/github/entity/WebhookEntity.java @@ -24,7 +24,7 @@ import org.exoplatform.commons.api.persistence.ExoEntity; import lombok.Data; -import org.exoplatform.gamification.github.utils.StringListConverter; +import org.exoplatform.commons.utils.StringListConverter; @Entity(name = "GitHubWebhooks") @ExoEntity diff --git a/gamification-github-services/src/main/java/org/exoplatform/gamification/github/rest/HooksManagementRest.java b/gamification-github-services/src/main/java/org/exoplatform/gamification/github/rest/HooksManagementRest.java index 9911895ae..97b916a7b 100644 --- a/gamification-github-services/src/main/java/org/exoplatform/gamification/github/rest/HooksManagementRest.java +++ b/gamification-github-services/src/main/java/org/exoplatform/gamification/github/rest/HooksManagementRest.java @@ -227,6 +227,30 @@ public Response updateWebHookRepoStatus(@Parameter(description = "GitHub organiz } } + @Path("events/status") + @POST + @RolesAllowed("users") + @Operation(summary = "enables/disables event for gitHub organization.", description = "enables/disables event for gitHub organization", method = "POST") + @ApiResponses(value = { + @ApiResponse(responseCode = "204", description = "Request fulfilled"), + @ApiResponse(responseCode = "400", description = "Bad request"), + @ApiResponse(responseCode = "401", description = "Unauthorized operation"), + @ApiResponse(responseCode = "500", description = "Internal server error"), }) + public Response updateWebHookEventStatus(@Parameter(description = "Event Id", required = true) @FormParam("eventId") long eventId, + @Parameter(description = "Organization remote Id", required = true) @FormParam("organizationId") long organizationId, + @Parameter(description = "Event status enabled/disabled. possible values: true for enabled, else false", required = true) @FormParam("enabled") boolean enabled) { + + String currentUser = getCurrentUser(); + try { + webhookService.setEventEnabledForOrganization(eventId, organizationId, enabled, currentUser); + return Response.noContent().build(); + } catch (IllegalAccessException e) { + return Response.status(Response.Status.UNAUTHORIZED).entity(e.getMessage()).type(MediaType.TEXT_PLAIN).build(); + } catch (ObjectNotFoundException e) { + return Response.status(Response.Status.NOT_FOUND).entity("Event not found").build(); + } + } + @Path("watchScope/status") @POST @RolesAllowed("users") diff --git a/gamification-github-services/src/main/java/org/exoplatform/gamification/github/services/GithubConsumerService.java b/gamification-github-services/src/main/java/org/exoplatform/gamification/github/services/GithubConsumerService.java index e633febc9..e4b94957a 100644 --- a/gamification-github-services/src/main/java/org/exoplatform/gamification/github/services/GithubConsumerService.java +++ b/gamification-github-services/src/main/java/org/exoplatform/gamification/github/services/GithubConsumerService.java @@ -67,23 +67,19 @@ public interface GithubConsumerService { * Retrieve available github organization repositories. * * @param webHook webHook - * repositories - * @throws ObjectNotFoundException when the github organization identified by - * its technical name is not found - * @throws IllegalAccessException when user is not authorized to access remote - * organization repositories + * @param page page + * @param perPage perPage * * @return {@link List} of {@link RemoteRepository} */ List retrieveOrganizationRepos(WebHook webHook, int page, - int perPage) throws IllegalAccessException, ObjectNotFoundException; + int perPage); /** * Count github organization repositories. * * @param webHook webHook - * repositories * * @return repositories count */ diff --git a/gamification-github-services/src/main/java/org/exoplatform/gamification/github/services/WebhookService.java b/gamification-github-services/src/main/java/org/exoplatform/gamification/github/services/WebhookService.java index d197eecf7..108b89ed2 100644 --- a/gamification-github-services/src/main/java/org/exoplatform/gamification/github/services/WebhookService.java +++ b/gamification-github-services/src/main/java/org/exoplatform/gamification/github/services/WebhookService.java @@ -187,4 +187,18 @@ List retrieveOrganizationRepos(long organizationRemoteId, int countOrganizationRepos(long organizationRemoteId, String currentUser) throws IllegalAccessException, ObjectNotFoundException; + /** + * Enables/disables organization event + * + * @param eventId event Id + * @param organizationId organization remote id + * @param enabled true to enabled, else false + * @param currentUser user name attempting to enables/disables event. + * @throws IllegalAccessException when user is not authorized enables/disables + * organization event + */ + void setEventEnabledForOrganization(long eventId, long organizationId, boolean enabled, String currentUser) throws IllegalAccessException, + ObjectNotFoundException; + + } diff --git a/gamification-github-services/src/main/java/org/exoplatform/gamification/github/services/impl/GithubConsumerServiceImpl.java b/gamification-github-services/src/main/java/org/exoplatform/gamification/github/services/impl/GithubConsumerServiceImpl.java index 57c18a811..4ae53a9b1 100644 --- a/gamification-github-services/src/main/java/org/exoplatform/gamification/github/services/impl/GithubConsumerServiceImpl.java +++ b/gamification-github-services/src/main/java/org/exoplatform/gamification/github/services/impl/GithubConsumerServiceImpl.java @@ -47,7 +47,7 @@ public String deleteWebhook(WebHook webHook) { @Override public int countOrganizationRepos(WebHook webHook) { - return githubConsumerStorage.countOrganizationRepos(webHook); + return githubConsumerStorage.countOrganizationRepos(webHook.getOrganizationId(), webHook.getToken()); } @Override @@ -62,8 +62,8 @@ public RemoteOrganization retrieveRemoteOrganization(String organizationName, } @Override - public List retrieveOrganizationRepos(WebHook webHook, int page, int perPage) throws IllegalAccessException { - return githubConsumerStorage.retrieveOrganizationRepos(webHook, page, perPage); + public List retrieveOrganizationRepos(WebHook webHook, int page, int perPage) { + return githubConsumerStorage.retrieveOrganizationRepos(webHook.getOrganizationId(), webHook.getToken(), page, perPage); } @Override diff --git a/gamification-github-services/src/main/java/org/exoplatform/gamification/github/services/impl/WebhookServiceImpl.java b/gamification-github-services/src/main/java/org/exoplatform/gamification/github/services/impl/WebhookServiceImpl.java index 805b78d89..3b2acbcfc 100644 --- a/gamification-github-services/src/main/java/org/exoplatform/gamification/github/services/impl/WebhookServiceImpl.java +++ b/gamification-github-services/src/main/java/org/exoplatform/gamification/github/services/impl/WebhookServiceImpl.java @@ -20,6 +20,8 @@ import java.util.*; import java.util.stream.Collectors; +import io.meeds.gamification.model.EventDTO; +import io.meeds.gamification.service.EventService; import io.meeds.gamification.utils.Utils; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; @@ -49,6 +51,8 @@ public class WebhookServiceImpl implements WebhookService { private static final Scope DISABLED_REPOS_SCOPE = Scope.APPLICATION.id("disabledRepos"); + public static final String ENABLED = ".enabled"; + private final SettingService settingService; private final WebHookStorage webHookStorage; @@ -57,14 +61,18 @@ public class WebhookServiceImpl implements WebhookService { private final GithubConsumerService githubServiceConsumer; + private final EventService eventService; + public WebhookServiceImpl(SettingService settingService, GithubTriggerService githubTriggerService, WebHookStorage webHookStorage, - GithubConsumerService githubServiceConsumer) { + GithubConsumerService githubServiceConsumer, + EventService eventService) { this.settingService = settingService; this.githubTriggerService = githubTriggerService; this.webHookStorage = webHookStorage; this.githubServiceConsumer = githubServiceConsumer; + this.eventService = eventService; } public List getWebhooks(String currentUser, int offset, int limit, boolean forceUpdate) throws IllegalAccessException { @@ -277,6 +285,34 @@ public int countOrganizationRepos(long organizationRemoteId, String currentUser) return githubServiceConsumer.countOrganizationRepos(webHook); } + @Override + public void setEventEnabledForOrganization(long eventId, + long organizationId, + boolean enabled, + String currentUser) throws IllegalAccessException, ObjectNotFoundException { + if (!Utils.isRewardingManager(currentUser)) { + throw new IllegalAccessException("The user is not authorized to enable/disable event for organization"); + } + EventDTO eventDTO = eventService.getEvent(eventId); + if (eventDTO == null) { + throw new ObjectNotFoundException("event not found"); + } + Map eventProperties = eventDTO.getProperties(); + if (eventProperties != null && !eventProperties.isEmpty()) { + String eventProjectStatus = eventProperties.get(organizationId + ENABLED); + if (StringUtils.isNotBlank(eventProjectStatus)) { + eventProperties.remove(organizationId + ENABLED); + eventProperties.put(organizationId + ENABLED, String.valueOf(enabled)); + eventDTO.setProperties(eventProperties); + } + } else { + Map properties = new HashMap<>(); + properties.put(organizationId + ENABLED, String.valueOf(enabled)); + eventDTO.setProperties(properties); + } + eventService.updateEvent(eventDTO); + } + @SuppressWarnings("unchecked") private void forceUpdateWebhook(WebHook webHook) { TokenStatus tokenStatus = githubServiceConsumer.checkGitHubTokenStatus(webHook.getToken()); diff --git a/gamification-github-services/src/main/java/org/exoplatform/gamification/github/storage/GithubConsumerStorage.java b/gamification-github-services/src/main/java/org/exoplatform/gamification/github/storage/GithubConsumerStorage.java index d27bf467a..f25476615 100644 --- a/gamification-github-services/src/main/java/org/exoplatform/gamification/github/storage/GithubConsumerStorage.java +++ b/gamification-github-services/src/main/java/org/exoplatform/gamification/github/storage/GithubConsumerStorage.java @@ -88,14 +88,14 @@ public String deleteWebhookHook(WebHook webHook) { } } - public List retrieveOrganizationRepos(WebHook webHook, int page, int perPage) { + public List retrieveOrganizationRepos(long organizationId, String accessToken , int page, int perPage) { List remoteRepositories = new ArrayList<>(); - String url = GITHUB_API_URL + webHook.getOrganizationId() + "/repos?per_page=" + perPage + "&page=" + page; + String url = GITHUB_API_URL + organizationId + "/repos?per_page=" + perPage + "&page=" + page; URI uri = URI.create(url); try { - String response = processGet(uri, webHook.getToken()); + String response = processGet(uri, accessToken); if (response != null) { Map[] repositoryMaps = fromJsonStringToMapCollection(response); for (Map repoMap : repositoryMaps) { @@ -116,11 +116,11 @@ public List retrieveOrganizationRepos(WebHook webHook, int pag return remoteRepositories; } - public int countOrganizationRepos(WebHook webHook) { - String url = GITHUB_API_URL + webHook.getOrganizationId() + "/repos"; + public int countOrganizationRepos(long organizationId, String accessToken) { + String url = GITHUB_API_URL + organizationId + "/repos"; URI uri = URI.create(url); try { - String response = processGet(uri, webHook.getToken()); + String response = processGet(uri, accessToken); if (response != null) { Map[] repositoryMaps = fromJsonStringToMapCollection(response); return (int) Arrays.stream(repositoryMaps).count(); diff --git a/gamification-github-services/src/main/java/org/exoplatform/gamification/github/storage/cached/GithubConsumerCachedStorage.java b/gamification-github-services/src/main/java/org/exoplatform/gamification/github/storage/cached/GithubConsumerCachedStorage.java index d6054e252..379fb9680 100644 --- a/gamification-github-services/src/main/java/org/exoplatform/gamification/github/storage/cached/GithubConsumerCachedStorage.java +++ b/gamification-github-services/src/main/java/org/exoplatform/gamification/github/storage/cached/GithubConsumerCachedStorage.java @@ -48,11 +48,12 @@ public GithubConsumerCachedStorage(CacheService cacheService) { ExoCache cacheInstance = cacheService.getCacheInstance(GITHUB_CACHE_NAME); this.githubFutureCache = new FutureExoCache<>((context, key) -> { if (ORG_REPOS_CONTEXT == context.getContext()) { - return GithubConsumerCachedStorage.super.retrieveOrganizationRepos(context.getWebHook(), + return GithubConsumerCachedStorage.super.retrieveOrganizationRepos(context.getOrganizationId(), + context.getAccessToken(), context.getPage(), context.getPerPage()); } else if (ORG_REPOS_COUNT_CONTEXT == context.getContext()) { - return GithubConsumerCachedStorage.super.countOrganizationRepos(context.getWebHook()); + return GithubConsumerCachedStorage.super.countOrganizationRepos(context.getOrganizationId(), context.getAccessToken()); } else if (ORG_BY_ID_CONTEXT == context.getContext()) { return GithubConsumerCachedStorage.super.retrieveRemoteOrganization(context.getOrganizationId(), context.getAccessToken()); @@ -74,16 +75,16 @@ public String deleteWebhookHook(WebHook webHook) { } @Override - public List retrieveOrganizationRepos(WebHook webHook, int page, int perPage) { - CacheKey cacheKey = new CacheKey(ORG_REPOS_CONTEXT, webHook, page, perPage); + public List retrieveOrganizationRepos(long organizationId, String accessToken, int page, int perPage) { + CacheKey cacheKey = new CacheKey(ORG_REPOS_CONTEXT, organizationId, accessToken, page, perPage); List remoteRepositories = (List) this.githubFutureCache.get(cacheKey, cacheKey.hashCode()); return remoteRepositories == null ? Collections.emptyList() : remoteRepositories; } @Override - public int countOrganizationRepos(WebHook webHook) { - CacheKey cacheKey = new CacheKey(ORG_REPOS_COUNT_CONTEXT, webHook); + public int countOrganizationRepos(long organizationId, String accessToken) { + CacheKey cacheKey = new CacheKey(ORG_REPOS_COUNT_CONTEXT, organizationId, accessToken); return (int) this.githubFutureCache.get(cacheKey, cacheKey.hashCode()); } @@ -106,7 +107,7 @@ public void clearCache() { @Override public void clearCache(WebHook webHook) { - this.githubFutureCache.remove(new CacheKey(ORG_REPOS_COUNT_CONTEXT, webHook).hashCode()); + this.githubFutureCache.remove(new CacheKey(ORG_REPOS_COUNT_CONTEXT, webHook.getOrganizationId(), webHook.getToken()).hashCode()); this.githubFutureCache.remove(new CacheKey(ORG_BY_ID_CONTEXT, webHook.getOrganizationId(), webHook.getToken()).hashCode()); this.githubFutureCache.remove(new CacheKey(TOKEN_STATUS_CONTEXT, webHook.getToken()).hashCode()); } diff --git a/gamification-github-services/src/main/java/org/exoplatform/gamification/github/storage/cached/model/CacheKey.java b/gamification-github-services/src/main/java/org/exoplatform/gamification/github/storage/cached/model/CacheKey.java index aaaea5297..6e45ada54 100644 --- a/gamification-github-services/src/main/java/org/exoplatform/gamification/github/storage/cached/model/CacheKey.java +++ b/gamification-github-services/src/main/java/org/exoplatform/gamification/github/storage/cached/model/CacheKey.java @@ -24,7 +24,6 @@ import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; -import org.exoplatform.gamification.github.model.WebHook; @AllArgsConstructor @NoArgsConstructor @@ -33,8 +32,6 @@ public class CacheKey implements Serializable { private static final long serialVersionUID = -8995567724453740730L; - private transient WebHook webHook; - private ProgramFilter programFilter; private int page; @@ -45,17 +42,11 @@ public class CacheKey implements Serializable { private String accessToken; - private long programId; - private Integer context; - public CacheKey(Integer context, WebHook webHook) { - this.webHook = webHook; - this.context = context; - } - - public CacheKey(Integer context, WebHook webHook, int page, int perPage) { - this.webHook = webHook; + public CacheKey(Integer context, long organizationId, String accessToken, int page, int perPage) { + this.organizationId = organizationId; + this.accessToken = accessToken; this.page = page; this.perPage = perPage; this.context = context; diff --git a/gamification-github-services/src/main/resources/conf/portal/gamification-github-connector-configuration.xml b/gamification-github-services/src/main/resources/conf/portal/gamification-github-connector-configuration.xml index 80e2106ee..3ccdf995a 100644 --- a/gamification-github-services/src/main/resources/conf/portal/gamification-github-connector-configuration.xml +++ b/gamification-github-services/src/main/resources/conf/portal/gamification-github-connector-configuration.xml @@ -274,29 +274,6 @@ - - RequestReviewForPullRequest - addPlugin - io.meeds.gamification.plugin.EventConfigPlugin - - - event-title - requestReviewForPullRequest - - - event-type - github - - - event-trigger - pull_request - - - event-can-cancel - true - - - diff --git a/gamification-github-services/src/main/resources/db/changelog/github-connector.db.changelog-1.0.0.xml b/gamification-github-services/src/main/resources/db/changelog/github-connector.db.changelog-1.0.0.xml index 6ea9109f3..89e78d203 100644 --- a/gamification-github-services/src/main/resources/db/changelog/github-connector.db.changelog-1.0.0.xml +++ b/gamification-github-services/src/main/resources/db/changelog/github-connector.db.changelog-1.0.0.xml @@ -108,6 +108,9 @@ + + + @@ -137,11 +140,6 @@ - - - - - diff --git a/gamification-github-webapp/src/main/webapp/vue-app/githubAdminConnectorExtension/components/GithubAdminConnectorEventItem.vue b/gamification-github-webapp/src/main/webapp/vue-app/githubAdminConnectorExtension/components/GithubAdminConnectorEventItem.vue index d13f3ca40..fccda8a5c 100644 --- a/gamification-github-webapp/src/main/webapp/vue-app/githubAdminConnectorExtension/components/GithubAdminConnectorEventItem.vue +++ b/gamification-github-webapp/src/main/webapp/vue-app/githubAdminConnectorExtension/components/GithubAdminConnectorEventItem.vue @@ -59,7 +59,7 @@ export default { }, methods: { enableDisableEvent() { - this.$gamificationConnectorService.saveEventStatus(this.id, this.organizationId, !this.enabled); + this.$githubConnectorService.saveEventStatus(this.id, this.organizationId, !this.enabled); }, } }; diff --git a/gamification-github-webapp/src/main/webapp/vue-app/githubAdminConnectorExtension/js/GithubConnectorService.js b/gamification-github-webapp/src/main/webapp/vue-app/githubAdminConnectorExtension/js/GithubConnectorService.js index 56d162edd..0f7421842 100644 --- a/gamification-github-webapp/src/main/webapp/vue-app/githubAdminConnectorExtension/js/GithubConnectorService.js +++ b/gamification-github-webapp/src/main/webapp/vue-app/githubAdminConnectorExtension/js/GithubConnectorService.js @@ -132,6 +132,26 @@ export function enableDisableWatchScope(organizationId, enabled) { }); } +export function saveEventStatus(eventId, organizationId, enabled) { + const formData = new FormData(); + formData.append('eventId', eventId); + formData.append('organizationId', organizationId); + formData.append('enabled', enabled); + + return fetch(`${eXo.env.portal.context}/${eXo.env.portal.rest}/gamification/connectors/github/hooks/events/status`, { + method: 'POST', + credentials: 'include', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + body: new URLSearchParams(formData).toString(), + }).then(resp => { + if (!resp?.ok) { + throw new Error('Response code indicates a server error', resp); + } + }); +} + export function forceUpdateWebhooks() { return fetch(`${eXo.env.portal.context}/${eXo.env.portal.rest}/gamification/connectors/github/hooks/forceUpdate`, { method: 'PATCH',