From 8c794fa7bdb233ba189e572ed5dcc91c4a35827b Mon Sep 17 00:00:00 2001 From: Azmi TOUIL <42934070+AzmiTouil@users.noreply.github.com> Date: Thu, 10 Aug 2023 22:56:14 +0200 Subject: [PATCH] feat: GitHub connectors Set repositories to watch in a project - MEED-2300 - Meeds-io/MIPs#64 (#82) Allow rewarding users to enable/disable organization repositories from watch --- .../github/model/RemoteRepository.java | 35 +++ .../github/rest/GithubWebHookRest.java | 9 +- .../github/rest/HooksManagementRest.java | 82 +++++- .../github/rest/builder/WebHookBuilder.java | 3 +- .../github/rest/model/RepositoryList.java | 39 +++ .../rest/model/RepositoryRestEntity.java | 34 +++ .../github/rest/model/WebHookRestEntity.java | 6 +- .../github/services/WebhookService.java | 78 ++++++ .../services/impl/WebhookServiceImpl.java | 157 ++++++++++- .../gamification/github/utils/Utils.java | 9 + .../GitHubWebHookManagement_en.properties | 9 + .../components/GithubAdminConnectorHook.vue | 18 +- .../GithubAdminConnectorHookDetail.vue | 126 +++++++++ .../components/GithubAdminConnectorItem.vue | 255 +++++++++--------- .../GithubAdminConnectorRepositoryItem.vue | 51 ++++ .../initComponents.js | 4 + .../js/GithubConnectorService.js | 51 ++++ 17 files changed, 833 insertions(+), 133 deletions(-) create mode 100644 gamification-github-services/src/main/java/org/exoplatform/gamification/github/model/RemoteRepository.java create mode 100644 gamification-github-services/src/main/java/org/exoplatform/gamification/github/rest/model/RepositoryList.java create mode 100644 gamification-github-services/src/main/java/org/exoplatform/gamification/github/rest/model/RepositoryRestEntity.java create mode 100644 gamification-github-webapp/src/main/webapp/vue-app/githubAdminConnectorExtension/components/GithubAdminConnectorHookDetail.vue create mode 100644 gamification-github-webapp/src/main/webapp/vue-app/githubAdminConnectorExtension/components/GithubAdminConnectorRepositoryItem.vue diff --git a/gamification-github-services/src/main/java/org/exoplatform/gamification/github/model/RemoteRepository.java b/gamification-github-services/src/main/java/org/exoplatform/gamification/github/model/RemoteRepository.java new file mode 100644 index 00000000..d270ee21 --- /dev/null +++ b/gamification-github-services/src/main/java/org/exoplatform/gamification/github/model/RemoteRepository.java @@ -0,0 +1,35 @@ +/* + * This file is part of the Meeds project (https://meeds.io/). + * + * Copyright (C) 2020 - 2023 Meeds Association contact@meeds.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.exoplatform.gamification.github.model; + +import lombok.*; + +@AllArgsConstructor +@NoArgsConstructor +@Data +@ToString(callSuper = true) +public class RemoteRepository { + + private long id; + + private String name; + + private String description; + + private boolean enabled; +} diff --git a/gamification-github-services/src/main/java/org/exoplatform/gamification/github/rest/GithubWebHookRest.java b/gamification-github-services/src/main/java/org/exoplatform/gamification/github/rest/GithubWebHookRest.java index a0e95a59..120b4439 100644 --- a/gamification-github-services/src/main/java/org/exoplatform/gamification/github/rest/GithubWebHookRest.java +++ b/gamification-github-services/src/main/java/org/exoplatform/gamification/github/rest/GithubWebHookRest.java @@ -41,13 +41,16 @@ public GithubWebHookRest(WebhookService webhookService, GithubTriggerService git public Response githubEvent(// NOSONAR @HeaderParam("x-github-event") String event, @HeaderParam("x-hub-signature") String signature, - String obj) { + String payload) { - if (!webhookService.verifyWebhookSecret(obj, signature)) { + if (!webhookService.verifyWebhookSecret(payload, signature)) { return Response.status(Response.Status.UNAUTHORIZED).build(); } + if (!webhookService.isWebHookRepositoryEnabled(payload)) { + return Response.noContent().build(); + } try { - githubTriggerService.handleTrigger(obj, event); + githubTriggerService.handleTrigger(payload, event); return Response.ok().build(); } catch (Exception e) { return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build(); 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 e4832409..c51d3d5e 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 @@ -28,8 +28,10 @@ import org.exoplatform.commons.ObjectAlreadyExistsException; import org.exoplatform.commons.exception.ObjectNotFoundException; +import org.exoplatform.gamification.github.model.RemoteRepository; import org.exoplatform.gamification.github.model.WebHook; import org.exoplatform.gamification.github.rest.builder.WebHookBuilder; +import org.exoplatform.gamification.github.rest.model.RepositoryList; import org.exoplatform.gamification.github.rest.model.WebHookList; import org.exoplatform.gamification.github.rest.model.WebHookRestEntity; import org.exoplatform.gamification.github.services.WebhookService; @@ -45,6 +47,8 @@ @Path("/gamification/connectors/github/hooks") public class HooksManagementRest implements ResourceContainer { + public static final String GITHUB_HOOK_NOT_FOUND = "The GitHub hook doesn't exit"; + private final WebhookService webhookService; public HooksManagementRest(WebhookService webhookService) { @@ -134,7 +138,7 @@ public Response updateWebHookAccessToken(@Parameter(description = "webHook id", } catch (IllegalAccessException e) { return Response.status(Response.Status.UNAUTHORIZED).entity(e.getMessage()).build(); } catch (ObjectNotFoundException e) { - return Response.status(Response.Status.NOT_FOUND).entity("The GitHub hook doesn't exit").build(); + return Response.status(Response.Status.NOT_FOUND).entity(GITHUB_HOOK_NOT_FOUND).build(); } } @@ -158,7 +162,81 @@ public Response deleteWebhookHook(@Parameter(description = "GitHub organization } 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("The GitHub hook doesn't exit").build(); + return Response.status(Response.Status.NOT_FOUND).entity(GITHUB_HOOK_NOT_FOUND).build(); + } + } + + @GET + @Path("{organizationId}/repos") + @Produces(MediaType.APPLICATION_JSON) + @RolesAllowed("users") + @Operation(summary = "Retrieves the list GitHub organization repositories", method = "GET") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Request fulfilled"), + @ApiResponse(responseCode = "401", description = "Unauthorized operation"), }) + public Response getWebHookRepos(@Parameter(description = "GitHub organization id", required = true) @PathParam("organizationId") long organizationId, + @QueryParam("offset") int offset, + @Parameter(description = "Query results limit", required = true) @QueryParam("limit") int limit, + @Parameter(description = "Repositories total size") @Schema(defaultValue = "false") @QueryParam("returnSize") boolean returnSize) { + + String currentUser = getCurrentUser(); + List remoteRepositories; + try { + RepositoryList repositoryList = new RepositoryList(); + remoteRepositories = webhookService.retrieveOrganizationRepos(organizationId, currentUser, offset, limit); + if (returnSize) { + int size = webhookService.countOrganizationRepos(organizationId, currentUser); + repositoryList.setSize(size); + } + repositoryList.setRemoteRepositories(remoteRepositories); + repositoryList.setOffset(offset); + repositoryList.setLimit(limit); + return Response.ok(repositoryList).build(); + } catch (IllegalAccessException e) { + return Response.status(Response.Status.UNAUTHORIZED).build(); + } catch (ObjectNotFoundException e) { + return Response.status(Response.Status.NOT_FOUND).entity(GITHUB_HOOK_NOT_FOUND).build(); + } + } + + @Path("repo/status") + @POST + @RolesAllowed("users") + @Operation(summary = "enables/disables webhook repository.", description = "enables/disables webhook repository", 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 updateWebHookRepoStatus(@Parameter(description = "GitHub organization remote Id", required = true) @FormParam("organizationId") long organizationId, + @Parameter(description = "Organization repository remote Id", required = true) @FormParam("repositoryId") long repositoryId, + @Parameter(description = "Organization repository status enabled/disabled. possible values: true for enabled, else false", required = true) @FormParam("enabled") boolean enabled) { + + String currentUser = getCurrentUser(); + try { + webhookService.setWebHookRepositoryEnabled(organizationId, repositoryId, enabled, currentUser); + return Response.noContent().build(); + } catch (IllegalAccessException e) { + return Response.status(Response.Status.UNAUTHORIZED).entity(e.getMessage()).type(MediaType.TEXT_PLAIN).build(); + } + } + + @Path("watchScope/status") + @POST + @RolesAllowed("users") + @Operation(summary = "Limit webhook watch scope or not", description = "Limit webhook watch scope or not", 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 updateWebHookWatchScope(@Parameter(description = "GitHub organization remote Id", required = true) @FormParam("organizationId") long organizationId, + @Parameter(description = "webhook watch scope limited status enabled/disabled. possible values: true for enabled, else false", required = true) @FormParam("enabled") boolean enabled) { + + String currentUser = getCurrentUser(); + try { + webhookService.setWebHookWatchLimitEnabled(organizationId, enabled, currentUser); + return Response.noContent().build(); + } catch (IllegalAccessException e) { + return Response.status(Response.Status.UNAUTHORIZED).entity(e.getMessage()).type(MediaType.TEXT_PLAIN).build(); } } diff --git a/gamification-github-services/src/main/java/org/exoplatform/gamification/github/rest/builder/WebHookBuilder.java b/gamification-github-services/src/main/java/org/exoplatform/gamification/github/rest/builder/WebHookBuilder.java index a717272d..730fc933 100644 --- a/gamification-github-services/src/main/java/org/exoplatform/gamification/github/rest/builder/WebHookBuilder.java +++ b/gamification-github-services/src/main/java/org/exoplatform/gamification/github/rest/builder/WebHookBuilder.java @@ -51,7 +51,8 @@ public static WebHookRestEntity toRestEntity(WebhookService webhookService, WebH remoteOrganization.getName(), remoteOrganization.getTitle(), remoteOrganization.getDescription(), - remoteOrganization.getAvatarUrl()); + remoteOrganization.getAvatarUrl(), + webhookService.isWebHookWatchLimitEnabled(webHook.getOrganizationId())); } public static List toRestEntities(WebhookService webhookService, Collection webHooks) { diff --git a/gamification-github-services/src/main/java/org/exoplatform/gamification/github/rest/model/RepositoryList.java b/gamification-github-services/src/main/java/org/exoplatform/gamification/github/rest/model/RepositoryList.java new file mode 100644 index 00000000..5889e87c --- /dev/null +++ b/gamification-github-services/src/main/java/org/exoplatform/gamification/github/rest/model/RepositoryList.java @@ -0,0 +1,39 @@ +/* + * This file is part of the Meeds project (https://meeds.io/). + * + * Copyright (C) 2020 - 2023 Meeds Association contact@meeds.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.exoplatform.gamification.github.rest.model; + +import java.util.List; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.exoplatform.gamification.github.model.RemoteRepository; + +@NoArgsConstructor +@Getter +@Setter +public class RepositoryList { + + private List remoteRepositories; + + private int offset; + + private int limit; + + private int size; +} diff --git a/gamification-github-services/src/main/java/org/exoplatform/gamification/github/rest/model/RepositoryRestEntity.java b/gamification-github-services/src/main/java/org/exoplatform/gamification/github/rest/model/RepositoryRestEntity.java new file mode 100644 index 00000000..0015b6eb --- /dev/null +++ b/gamification-github-services/src/main/java/org/exoplatform/gamification/github/rest/model/RepositoryRestEntity.java @@ -0,0 +1,34 @@ +/* + * This file is part of the Meeds project (https://meeds.io/). + * + * Copyright (C) 2020 - 2023 Meeds Association contact@meeds.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.exoplatform.gamification.github.rest.model; + +import lombok.*; + +@AllArgsConstructor +@NoArgsConstructor +@Data +public class RepositoryRestEntity { + + private long id; + + private String title; + + private String description; + + private boolean enabled; +} diff --git a/gamification-github-services/src/main/java/org/exoplatform/gamification/github/rest/model/WebHookRestEntity.java b/gamification-github-services/src/main/java/org/exoplatform/gamification/github/rest/model/WebHookRestEntity.java index d13f0e96..38b43a3c 100644 --- a/gamification-github-services/src/main/java/org/exoplatform/gamification/github/rest/model/WebHookRestEntity.java +++ b/gamification-github-services/src/main/java/org/exoplatform/gamification/github/rest/model/WebHookRestEntity.java @@ -50,6 +50,8 @@ public class WebHookRestEntity { private String avatarUrl; + private boolean watchScopeLimited; + public WebHookRestEntity(long id, // NOSONAR long webhookId, long organizationId, @@ -62,7 +64,8 @@ public WebHookRestEntity(long id, // NOSONAR String name, String title, String description, - String avatarUrl) { + String avatarUrl, + boolean watchScopeLimited) { this.id = id; this.webhookId = webhookId; @@ -77,5 +80,6 @@ public WebHookRestEntity(long id, // NOSONAR this.title = title; this.description = description; this.avatarUrl = avatarUrl; + this.watchScopeLimited = watchScopeLimited; } } 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 accce558..29b457c1 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 @@ -18,6 +18,7 @@ import org.exoplatform.commons.ObjectAlreadyExistsException; import org.exoplatform.commons.exception.ObjectNotFoundException; import org.exoplatform.gamification.github.model.RemoteOrganization; +import org.exoplatform.gamification.github.model.RemoteRepository; import org.exoplatform.gamification.github.model.WebHook; import java.util.List; @@ -110,6 +111,82 @@ void updateWebHookAccessToken(long webHookId, String accessToken, String current */ boolean verifyWebhookSecret(String payload, String signature); + /** + * Check if webhook repository is enabled + * + * @param payload payload The raw payload of the webhook request. + * @return true if the intended repository is enabled, else false. + */ + boolean isWebHookRepositoryEnabled(String payload); + + /** + * Check if webhook repository is enabled + * + * @param organizationRemoteId gitHub organization remote Id + * @param repositoryRemoteId gitHub repository remote Id + * @return true if the intended repository is enabled, else false. + */ + boolean isWebHookRepositoryEnabled(long organizationRemoteId, long repositoryRemoteId); + + /** + * enables/disables repository + * + * @param organizationRemoteId gitHub organization remote Id + * @param repositoryRemoteId gitHub repository remote Id + * @param enabled true to enabled, else false + * @param currentUser user name attempting to enables/disables repository. + */ + void setWebHookRepositoryEnabled(long organizationRemoteId, + long repositoryRemoteId, + boolean enabled, + String currentUser) throws IllegalAccessException; + + /** + * Check if webhook watch limit is enabled + * + * @param organizationRemoteId gitHub organization remote Id + * @return true if webHook watch limit is enabled, else false. + */ + boolean isWebHookWatchLimitEnabled(long organizationRemoteId); + + /** + * Limit webhook watch scope or not + * + * @param organizationRemoteId gitHub organization remote Id + * @param enabled true to enabled, else false + * @param currentUser user name attempting to enables/disables webHook watch + * limit. + */ + void setWebHookWatchLimitEnabled(long organizationRemoteId, boolean enabled, String currentUser) throws IllegalAccessException; + + /** + * Retrieve available github organization repositories. + * + * @param organizationRemoteId gitHub organization remote Id + * @param currentUser user name attempting to access remote organization + * repositories + * @throws IllegalAccessException when user is not authorized to access remote + * organization repositories + * @return {@link List} of {@link RemoteRepository} + */ + List retrieveOrganizationRepos(long organizationRemoteId, + String currentUser, + int page, + int perPage) throws IllegalAccessException, ObjectNotFoundException; + + /** + * Count available github organization repositories. + * + * @param organizationRemoteId gitHub organization remote Id + * @param currentUser user name attempting to access remote organization + * repositories + * @throws IllegalAccessException when user is not authorized to access remote + * organization repositories + * @return Repositories count + */ + int countOrganizationRepos(long organizationRemoteId, String currentUser) throws IllegalAccessException, + ObjectNotFoundException; + /** * create gamification history * @@ -119,4 +196,5 @@ void updateWebHookAccessToken(long webHookId, String accessToken, String current * @param object Object link */ void createGamificationHistory(String ruleTitle, String senderId, String receiverId, String object); + } 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 b4f20517..799e190d 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 @@ -19,6 +19,7 @@ import java.net.URI; import java.nio.charset.StandardCharsets; import java.util.*; +import java.util.stream.Collectors; import io.meeds.gamification.utils.Utils; import org.apache.commons.collections.CollectionUtils; @@ -40,9 +41,14 @@ import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.protocol.HTTP; import org.exoplatform.commons.ObjectAlreadyExistsException; +import org.exoplatform.commons.api.settings.data.Context; +import org.exoplatform.commons.api.settings.data.Scope; import org.exoplatform.commons.exception.ObjectNotFoundException; import org.exoplatform.commons.utils.CommonsUtils; import org.exoplatform.gamification.github.exception.GithubConnectionException; +import org.exoplatform.commons.api.settings.SettingService; +import org.exoplatform.commons.api.settings.SettingValue; +import org.exoplatform.gamification.github.model.RemoteRepository; import org.exoplatform.gamification.github.model.WebHook; import org.exoplatform.gamification.github.model.RemoteOrganization; import org.exoplatform.gamification.github.services.GithubTriggerService; @@ -60,10 +66,18 @@ public class WebhookServiceImpl implements WebhookService { - private static final Log LOG = ExoLogger.getLogger(WebhookServiceImpl.class); + private static final Log LOG = ExoLogger.getLogger(WebhookServiceImpl.class); + + private static final Context GITHUB_WEBHOOK_CONTEXT = Context.GLOBAL.id("githubWebhook"); + + private static final Scope WATCH_LIMITED_SCOPE = Scope.APPLICATION.id("watchLimited"); + + private static final Scope DISABLED_REPOS_SCOPE = Scope.APPLICATION.id("disabledRepos"); private final ListenerService listenerService; + private final SettingService settingService; + private final WebHookStorage webHookStorage; private final GithubTriggerService githubTriggerService; @@ -71,9 +85,11 @@ public class WebhookServiceImpl implements WebhookService { private HttpClient client; public WebhookServiceImpl(ListenerService listenerService, + SettingService settingService, GithubTriggerService githubTriggerService, WebHookStorage webHookStorage) { this.listenerService = listenerService; + this.settingService = settingService; this.githubTriggerService = githubTriggerService; this.webHookStorage = webHookStorage; } @@ -208,6 +224,143 @@ public boolean verifyWebhookSecret(String payload, String signature) { return false; } + @Override + public boolean isWebHookRepositoryEnabled(String payload) { + Map payloadMap = fromJsonStringToMap(payload); + String organizationId = extractSubItem(payloadMap, "organization", "id"); + String repositoryId = extractSubItem(payloadMap, "repository", "id"); + if (organizationId != null && repositoryId != null) { + return isWebHookRepositoryEnabled(Long.parseLong(organizationId), Long.parseLong(repositoryId)); + } + return false; + } + + @Override + public boolean isWebHookRepositoryEnabled(long organizationId, long repositoryId) { + List disabledRepositoryList = new ArrayList<>(); + SettingValue settingValue = + settingService.get(GITHUB_WEBHOOK_CONTEXT, DISABLED_REPOS_SCOPE, String.valueOf(organizationId)); + if (settingValue != null && settingValue.getValue() != null && StringUtils.isNotBlank(settingValue.getValue().toString())) { + disabledRepositoryList = Arrays.stream(settingValue.getValue().toString().split(":")) + .map(Long::parseLong) + .toList(); + } + return !disabledRepositoryList.contains(repositoryId); + } + + @Override + public void setWebHookRepositoryEnabled(long organizationId, + long repositoryId, + boolean enabled, + String currentUser) throws IllegalAccessException { + if (!Utils.isRewardingManager(currentUser)) { + throw new IllegalAccessException("The user is not authorized to update repository status"); + } + List disabledRepositoryList = new ArrayList<>(); + SettingValue settingValue = + settingService.get(GITHUB_WEBHOOK_CONTEXT, DISABLED_REPOS_SCOPE, String.valueOf(organizationId)); + if (settingValue != null && settingValue.getValue() != null && StringUtils.isNotBlank(settingValue.getValue().toString())) { + disabledRepositoryList = Arrays.stream(settingValue.getValue().toString().split(":")) + .map(Long::parseLong) + .collect(Collectors.toList()); + } + if (!enabled) { + if (!disabledRepositoryList.contains(repositoryId)) { + disabledRepositoryList.add(repositoryId); + } + } else { + disabledRepositoryList.remove(repositoryId); + } + String disabledRepositories = disabledRepositoryList.stream().map(String::valueOf).collect(Collectors.joining(":")); + settingService.set(GITHUB_WEBHOOK_CONTEXT, + DISABLED_REPOS_SCOPE, + String.valueOf(organizationId), + SettingValue.create(disabledRepositories)); + } + + @Override + public boolean isWebHookWatchLimitEnabled(long organizationId) { + SettingValue settingValue = + settingService.get(GITHUB_WEBHOOK_CONTEXT, WATCH_LIMITED_SCOPE, String.valueOf(organizationId)); + if (settingValue != null && settingValue.getValue() != null && StringUtils.isNotBlank(settingValue.getValue().toString())) { + return Boolean.parseBoolean(settingValue.getValue().toString()); + } + return true; + } + + @Override + public void setWebHookWatchLimitEnabled(long organizationId, + boolean enabled, + String currentUser) throws IllegalAccessException { + if (!Utils.isRewardingManager(currentUser)) { + throw new IllegalAccessException("The user is not authorized to update webHook watch limit status"); + } + settingService.set(GITHUB_WEBHOOK_CONTEXT, WATCH_LIMITED_SCOPE, String.valueOf(organizationId), SettingValue.create(enabled)); + } + + @Override + public List retrieveOrganizationRepos(long organizationRemoteId, + String currentUser, + int page, + int perPage) throws IllegalAccessException, ObjectNotFoundException { + if (!Utils.isRewardingManager(currentUser)) { + throw new IllegalAccessException("The user is not authorized to access organization repositories"); + } + WebHook webHook = webHookStorage.getWebhookByOrganizationId(organizationRemoteId); + if (webHook == null) { + throw new ObjectNotFoundException("webhook with organization id '" + organizationRemoteId + "' doesn't exist"); + } + List remoteRepositories = new ArrayList<>(); + String url = GITHUB_API_URL + organizationRemoteId + "/repos?per_page=" + perPage + "&page=" + page; + + URI uri = URI.create(url); + try { + String response = processGet(uri, webHook.getToken()); + if (response != null) { + Map[] repositoryMaps = fromJsonStringToMapCollection(response); + for (Map repoMap : repositoryMaps) { + RemoteRepository remoteRepository = new RemoteRepository(); + long repoId = Long.parseLong(repoMap.get("id").toString()); + String name = (String) repoMap.get("name"); + String description = (String) repoMap.get("description"); + remoteRepository.setId(repoId); + remoteRepository.setName(name); + remoteRepository.setDescription(description); + remoteRepository.setEnabled(isWebHookRepositoryEnabled(organizationRemoteId, repoId)); + remoteRepositories.add(remoteRepository); + } + return remoteRepositories; + } + } catch (GithubConnectionException e) { + throw new IllegalStateException("Unable to retrieve GitHub organization repos."); + } + return remoteRepositories; + } + + @Override + public int countOrganizationRepos(long organizationRemoteId, String currentUser) throws IllegalAccessException, + ObjectNotFoundException { + if (!Utils.isRewardingManager(currentUser)) { + throw new IllegalAccessException("The user is not authorized to access organization repositories"); + } + WebHook webHook = webHookStorage.getWebhookByOrganizationId(organizationRemoteId); + if (webHook == null) { + throw new ObjectNotFoundException("webhook with organization id '" + organizationRemoteId + "' doesn't exist"); + } + String url = GITHUB_API_URL + organizationRemoteId + "/repos"; + URI uri = URI.create(url); + try { + String response = processGet(uri, webHook.getToken()); + if (response != null) { + Map[] repositoryMaps = fromJsonStringToMapCollection(response); + return (int) Arrays.stream(repositoryMaps).count(); + } + } catch (GithubConnectionException e) { + throw new IllegalStateException("Unable to retrieve GitHub organization repos."); + } + return 0; + } + public void createGamificationHistory(String ruleTitle, String senderId, String receiverId, String object) { try { Map gam = new HashMap<>(); @@ -277,7 +430,7 @@ public RemoteOrganization retrieveRemoteOrganization(long organizationId, String Map resultMap = fromJsonStringToMap(response); RemoteOrganization gitHubOrganization = new RemoteOrganization(); gitHubOrganization.setId((Long.parseLong(resultMap.get(ID).toString()))); - gitHubOrganization.setName(resultMap.get(ID).toString()); + gitHubOrganization.setName(resultMap.get(LOGIN).toString()); gitHubOrganization.setTitle(resultMap.get(NAME).toString()); gitHubOrganization.setDescription(resultMap.get(DESCRIPTION).toString()); gitHubOrganization.setAvatarUrl(resultMap.get(AVATAR_URL).toString()); diff --git a/gamification-github-services/src/main/java/org/exoplatform/gamification/github/utils/Utils.java b/gamification-github-services/src/main/java/org/exoplatform/gamification/github/utils/Utils.java index 215a7e8a..8a067a14 100644 --- a/gamification-github-services/src/main/java/org/exoplatform/gamification/github/utils/Utils.java +++ b/gamification-github-services/src/main/java/org/exoplatform/gamification/github/utils/Utils.java @@ -151,6 +151,15 @@ public static Map fromJsonStringToMap(String jsonString) { } } + public static Map[] fromJsonStringToMapCollection(String jsonString) { + try { + ObjectMapper objectMapper = new ObjectMapper(); + return objectMapper.readValue(jsonString, Map[].class); + } catch (IOException e) { + throw new IllegalStateException("Error converting JSON string to map: " + jsonString, e); + } + } + @SuppressWarnings("unchecked") public static String extractSubItem(Map map, String... keys) { Object currentObject = map; diff --git a/gamification-github-webapp/src/main/resources/locale/portlet/GitHubWebHookManagement_en.properties b/gamification-github-webapp/src/main/resources/locale/portlet/GitHubWebHookManagement_en.properties index 81d7b1d4..40e52dd8 100644 --- a/gamification-github-webapp/src/main/resources/locale/portlet/GitHubWebHookManagement_en.properties +++ b/gamification-github-webapp/src/main/resources/locale/portlet/GitHubWebHookManagement_en.properties @@ -36,6 +36,11 @@ githubConnector.admin.label.accessToken=Personal access token githubConnector.admin.label.accessToken.placeholder=Enter the GitHub personal access token githubConnector.admin.label.Webhook=Webhook +githubConnector.admin.label.repositories=Repositories +githubConnector.admin.label.repositories.placeholder=Monitor repositories to be watched +githubConnector.admin.label.watchScope=Watch scope limited per repository +githubConnector.admin.label.watchScope.placeholder=When creating an action, program owners must choose a repository where the action will be watched. Only Reward Administrators can extend the scope. + githubConnector.webhook.form.label.button.next=Next githubConnector.webhook.form.label.button.back=Back githubConnector.webhook.label.watchedBy=Project watched from {0} by @@ -44,3 +49,7 @@ githubConnector.webhook.label.loadMore=Load More githubConnector.webhook.title.confirmDeleteProject=Delete github project? githubConnector.webhook.message.confirmDeleteConnectorHook=Are you sure you want to delete this github project? +githubConnector.webhook.details.repository=Repository +githubConnector.webhook.details.description=Description +githubConnector.webhook.details.status=Status + diff --git a/gamification-github-webapp/src/main/webapp/vue-app/githubAdminConnectorExtension/components/GithubAdminConnectorHook.vue b/gamification-github-webapp/src/main/webapp/vue-app/githubAdminConnectorExtension/components/GithubAdminConnectorHook.vue index ebc290f4..fae0f4c9 100644 --- a/gamification-github-webapp/src/main/webapp/vue-app/githubAdminConnectorExtension/components/GithubAdminConnectorHook.vue +++ b/gamification-github-webapp/src/main/webapp/vue-app/githubAdminConnectorExtension/components/GithubAdminConnectorHook.vue @@ -16,7 +16,8 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -->