diff --git a/gamification-crowdin-services/src/main/java/io/meeds/crowdin/gamification/model/RemoteApproval.java b/gamification-crowdin-services/src/main/java/io/meeds/crowdin/gamification/model/RemoteApproval.java new file mode 100644 index 00000000..15681879 --- /dev/null +++ b/gamification-crowdin-services/src/main/java/io/meeds/crowdin/gamification/model/RemoteApproval.java @@ -0,0 +1,41 @@ +/* + * This file is part of the Meeds project (https://meeds.io/). + * + * Copyright (C) 2020 - 2025 Meeds Lab contact@meedslab.com + * + * 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 io.meeds.crowdin.gamification.model; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class RemoteApproval implements Cloneable { + + private long id; + + private String userName; + + private String translationId; + + private String stringId; + + private String languageId; + + +} diff --git a/gamification-crowdin-services/src/main/java/io/meeds/crowdin/gamification/plugin/SuggestionApprovedTriggerPlugin.java b/gamification-crowdin-services/src/main/java/io/meeds/crowdin/gamification/plugin/SuggestionApprovedTriggerPlugin.java index fd2d9df0..2489c8fe 100644 --- a/gamification-crowdin-services/src/main/java/io/meeds/crowdin/gamification/plugin/SuggestionApprovedTriggerPlugin.java +++ b/gamification-crowdin-services/src/main/java/io/meeds/crowdin/gamification/plugin/SuggestionApprovedTriggerPlugin.java @@ -19,8 +19,9 @@ package io.meeds.crowdin.gamification.plugin; import io.meeds.crowdin.gamification.model.Event; +import io.meeds.crowdin.gamification.model.RemoteApproval; import io.meeds.crowdin.gamification.services.CrowdinTriggerService; -import io.meeds.gamification.model.RealizationDTO; +import io.meeds.crowdin.gamification.services.WebhookService; import io.meeds.gamification.service.RealizationService; import jakarta.annotation.PostConstruct; import org.springframework.beans.factory.annotation.Autowired; @@ -41,6 +42,9 @@ public class SuggestionApprovedTriggerPlugin extends CrowdinTriggerPlugin { @Autowired private RealizationService realizationService; + @Autowired + private WebhookService webhookService; + @PostConstruct public void init() { crowdinTriggerService.addPlugin(this); @@ -51,7 +55,7 @@ public List getEvents(String trigger, Map payload) { String objectId = constructObjectIdAsJsonString(payload, TRANSLATION); List eventList = new ArrayList<>(); - eventList.add(new Event(APPROVE_SUGGESTION_EVENT_NAME, + eventList.add(new Event(SUGGESTION_APPROVED_EVENT_NAME, extractSubItem(payload, TRANSLATION, USER, USERNAME), extractSubItem(payload, TRANSLATION, USER, USERNAME), objectId, @@ -63,13 +67,14 @@ public List getEvents(String trigger, Map payload) { trigger.equals(SUGGESTION_DISAPPROVED_TRIGGER), countWords(extractSubItem(payload, TRANSLATION, STRING, TEXT)))); - List realizations = realizationService.findRealizationsByObjectIdAndObjectType(objectId, TRANSLATION); + // Retrieve who made that approval by calling Crowdin API + String translationId = extractSubItem(payload, TRANSLATION, ID); + RemoteApproval remoteApproval = webhookService.getApproval(getProjectId(payload), translationId); - if (!realizations.isEmpty()) { - String earnerId = realizations.get(0).getEarnerId(); - eventList.add(new Event(SUGGESTION_APPROVED_EVENT_NAME, - earnerId, - earnerId, + if (remoteApproval != null) { + eventList.add(new Event(APPROVE_SUGGESTION_EVENT_NAME, + remoteApproval.getUserName(), + remoteApproval.getUserName(), objectId, TRANSLATION, getProjectId(payload), @@ -80,7 +85,6 @@ public List getEvents(String trigger, Map payload) { countWords(extractSubItem(payload, TRANSLATION, STRING, TEXT)))); } - return eventList; } diff --git a/gamification-crowdin-services/src/main/java/io/meeds/crowdin/gamification/services/WebhookService.java b/gamification-crowdin-services/src/main/java/io/meeds/crowdin/gamification/services/WebhookService.java index 39d7688d..abeac12e 100644 --- a/gamification-crowdin-services/src/main/java/io/meeds/crowdin/gamification/services/WebhookService.java +++ b/gamification-crowdin-services/src/main/java/io/meeds/crowdin/gamification/services/WebhookService.java @@ -18,6 +18,7 @@ */ package io.meeds.crowdin.gamification.services; +import io.meeds.crowdin.gamification.model.RemoteApproval; import io.meeds.crowdin.gamification.model.RemoteDirectory; import io.meeds.crowdin.gamification.model.RemoteProject; import io.meeds.crowdin.gamification.model.WebHook; @@ -56,10 +57,18 @@ public List getProjects(String accessToken) throws IllegalAccessE return crowdinConsumerStorage.getProjects(accessToken); } + public RemoteApproval getApproval(String projectId, String translationId) { + WebHook webHook = webHookStorage.getWebhookByProjectId(Long.parseLong(projectId)); + if (webHook != null) { + return crowdinConsumerStorage.getApproval(webHook.getToken(), projectId, translationId); + } + return null; + } + public WebHook createWebhook(long projectId, - String projectName, - String accessToken, - String currentUser) throws ObjectAlreadyExistsException, IllegalAccessException { + String projectName, + String accessToken, + String currentUser) throws ObjectAlreadyExistsException, IllegalAccessException { if (!Utils.isRewardingManager(currentUser)) { throw new IllegalAccessException("The user is not authorized to create Crowdin hook"); } @@ -80,7 +89,7 @@ public WebHook createWebhook(long projectId, } public void updateWebHookAccessToken(long webHookId, String accessToken, String currentUser) throws IllegalAccessException, - ObjectNotFoundException { + ObjectNotFoundException { if (!Utils.isRewardingManager(currentUser)) { throw new IllegalAccessException(AUTHORIZED_TO_ACCESS_CROWDIN_HOOKS); } diff --git a/gamification-crowdin-services/src/main/java/io/meeds/crowdin/gamification/storage/CrowdinConsumerStorage.java b/gamification-crowdin-services/src/main/java/io/meeds/crowdin/gamification/storage/CrowdinConsumerStorage.java index ce7733cd..1587b717 100644 --- a/gamification-crowdin-services/src/main/java/io/meeds/crowdin/gamification/storage/CrowdinConsumerStorage.java +++ b/gamification-crowdin-services/src/main/java/io/meeds/crowdin/gamification/storage/CrowdinConsumerStorage.java @@ -18,6 +18,7 @@ */ package io.meeds.crowdin.gamification.storage; +import io.meeds.crowdin.gamification.model.*; import org.apache.commons.httpclient.HttpStatus; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; @@ -39,10 +40,6 @@ import org.exoplatform.services.log.Log; import io.meeds.crowdin.gamification.exception.CrowdinConnectionException; -import io.meeds.crowdin.gamification.model.RemoteDirectory; -import io.meeds.crowdin.gamification.model.RemoteLanguage; -import io.meeds.crowdin.gamification.model.RemoteProject; -import io.meeds.crowdin.gamification.model.WebHook; import org.json.JSONArray; import org.json.JSONObject; @@ -235,7 +232,7 @@ private String processDelete(URI uri, String accessToken) throws CrowdinConnecti private String processRequest(HttpClient httpClient, HttpRequestBase request) throws IOException, CrowdinConnectionException { HttpResponse response = httpClient.execute(request); boolean isSuccess = response != null - && (response.getStatusLine().getStatusCode() >= 200 && response.getStatusLine().getStatusCode() < 300); + && (response.getStatusLine().getStatusCode() >= 200 && response.getStatusLine().getStatusCode() < 300); if (isSuccess) { return processSuccessResponse(response); } else if (response != null && response.getStatusLine().getStatusCode() == 404) { @@ -250,8 +247,8 @@ private String processSuccessResponse(HttpResponse response) throws IOException if (response.getStatusLine().getStatusCode() == HttpStatus.SC_NO_CONTENT) { return String.valueOf(HttpStatus.SC_NO_CONTENT); } else if ((response.getStatusLine().getStatusCode() == HttpStatus.SC_CREATED - || response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) && response.getEntity() != null - && response.getEntity().getContentLength() != 0) { + || response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) && response.getEntity() != null + && response.getEntity().getContentLength() != 0) { try (InputStream is = response.getEntity().getContent()) { return IOUtils.toString(is, StandardCharsets.UTF_8); } @@ -281,8 +278,8 @@ private HttpClient getHttpClient() { if (client == null) { HttpClientConnectionManager clientConnectionManager = getClientConnectionManager(); HttpClientBuilder httpClientBuilder = HttpClients.custom() - .setConnectionManager(clientConnectionManager) - .setConnectionReuseStrategy(new DefaultConnectionReuseStrategy()); + .setConnectionManager(clientConnectionManager) + .setConnectionReuseStrategy(new DefaultConnectionReuseStrategy()); client = httpClientBuilder.build(); } return client; @@ -380,6 +377,33 @@ public List getProjectDirectories(long remoteProjectId, } } + public RemoteApproval getApproval(String accessToken, String projectId, String translationId) { + try { + URI uri = URI.create(CROWDIN_API_URL + PROJECTS + projectId + APPROVALS + translationId); + + String response = processGet(uri, accessToken); + JSONObject jsonObject = new JSONObject(response).getJSONObject("data"); + + RemoteApproval remoteApproval = new RemoteApproval(); + + JSONObject userJsonObject = jsonObject.getJSONObject("user"); + + remoteApproval.setId(jsonObject.getInt("id")); + + remoteApproval.setUserName(userJsonObject.getString("username")); + remoteApproval.setTranslationId(jsonObject.getString("translationId")); + remoteApproval.setLanguageId(jsonObject.getString("translationId")); + remoteApproval.setStringId(jsonObject.getString("stringId")); + remoteApproval.setLanguageId(jsonObject.getString("languageId")); + + return remoteApproval; + } catch (CrowdinConnectionException e) { + LOG.warn("Unable to retrieve approval for crowdin translation with id {}.", translationId, e); + return null; + } + } + + public void clearCache() { // implemented in cached storage } diff --git a/gamification-crowdin-services/src/main/java/io/meeds/crowdin/gamification/utils/Utils.java b/gamification-crowdin-services/src/main/java/io/meeds/crowdin/gamification/utils/Utils.java index d24c440c..3a6f0797 100644 --- a/gamification-crowdin-services/src/main/java/io/meeds/crowdin/gamification/utils/Utils.java +++ b/gamification-crowdin-services/src/main/java/io/meeds/crowdin/gamification/utils/Utils.java @@ -18,7 +18,7 @@ public class Utils { public static final String CONNECTOR_NAME = "crowdin"; public static final String[] CROWDIN_EVENTS = new String[] { "stringComment.created", - "stringComment.deleted", "suggestion.added", "suggestion.deleted", "suggestion.approved", "suggestion.disapproved" }; + "stringComment.deleted", "suggestion.added", "suggestion.deleted", "suggestion.approved", "suggestion.disapproved" }; public static final String PROJECT_ID = "projectId"; @@ -34,6 +34,8 @@ public class Utils { public static final String WEBHOOKS = "/webhooks/"; + public static final String APPROVALS = "/approvals/"; + public static final String AUTHORIZED_TO_ACCESS_CROWDIN_HOOKS = "The user is not authorized to access crowdin Hooks"; public static final String GAMIFICATION_GENERIC_EVENT = "exo.gamification.generic.action"; @@ -57,7 +59,7 @@ public class Utils { public static final String LANGUAGE_ID = "languageId"; public static final String MUST_BE_HUMAN = "mustBeHuman"; - + public static final String TOTAL_TARGET_ITEM = "totalTargetItem"; public static final String STRING = "string"; @@ -65,7 +67,7 @@ public class Utils { public static final String TARGET_LANGUAGE = "targetLanguage"; public static final String PROJECT = "project"; - + public static final String TEXT = "text"; public static final String ID = "id"; @@ -180,7 +182,7 @@ public static String constructObjectIdAsJsonString(Map payload, String sourceLanguageId = extractSubItem(payload, payloadObjectName, STRING, PROJECT, SOURCE_LANGUAGE_ID); String targetLanguageId = extractSubItem(payload, payloadObjectName, TARGET_LANGUAGE, ID); return "{\"id\":" + id + ",\"stringUrl\":\"https://crowdin.com/editor/" + projectSlug + "/" + fileId + "/" + sourceLanguageId - + "-" + targetLanguageId + "?view=comfortable#" + stringId + "\"}"; + + "-" + targetLanguageId + "?view=comfortable#" + stringId + "\"}"; } public static int countWords(String text) {