Skip to content

Commit 9033ae2

Browse files
sergeByishimoAzmiTouil
authored andcommitted
fix: Crowdin proofreader events are not well triggered - Meeds-io/meeds#2655 (#61)
* fix: Crowdin proofreader events are not well triggered - Meeds-io/meeds#2655 The suggestion.approved webhook from Crowdin no longer provides the proofreader information in the payload. Instead, it only includes the translator details. Now to get proofreader details, we call the Crowdin API after receiving the webhook. * fix: Suggested Changes by @AzmiTouil - Meeds-io/meeds#2655 Suggested Changes by @AzmiTouil * fix: exception java.lang.IllegalAccessException is never thrown in body of corresponding try statement - Meeds-io/meeds#2655 exception java.lang.IllegalAccessException is never thrown in body of corresponding try statement * Remove useless imports and format code --------- Co-authored-by: Azmi Touil <atouil@exoplatform.com>
1 parent 29d195f commit 9033ae2

File tree

5 files changed

+106
-26
lines changed

5 files changed

+106
-26
lines changed
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* This file is part of the Meeds project (https://meeds.io/).
3+
*
4+
* Copyright (C) 2020 - 2025 Meeds Lab contact@meedslab.com
5+
*
6+
* This program is free software; you can redistribute it and/or
7+
* modify it under the terms of the GNU Lesser General Public
8+
* License as published by the Free Software Foundation; either
9+
* version 3 of the License, or (at your option) any later version.
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
* Lesser General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Lesser General Public License
16+
* along with this program; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18+
*/
19+
package io.meeds.crowdin.gamification.model;
20+
21+
import lombok.AllArgsConstructor;
22+
import lombok.Data;
23+
import lombok.NoArgsConstructor;
24+
25+
@Data
26+
@AllArgsConstructor
27+
@NoArgsConstructor
28+
public class RemoteApproval implements Cloneable {
29+
30+
private long id;
31+
32+
private String userName;
33+
34+
private String translationId;
35+
36+
private String stringId;
37+
38+
private String languageId;
39+
40+
41+
}

gamification-crowdin-services/src/main/java/io/meeds/crowdin/gamification/plugin/SuggestionApprovedTriggerPlugin.java

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,9 @@
1919
package io.meeds.crowdin.gamification.plugin;
2020

2121
import io.meeds.crowdin.gamification.model.Event;
22+
import io.meeds.crowdin.gamification.model.RemoteApproval;
2223
import io.meeds.crowdin.gamification.services.CrowdinTriggerService;
23-
import io.meeds.gamification.model.RealizationDTO;
24+
import io.meeds.crowdin.gamification.services.WebhookService;
2425
import io.meeds.gamification.service.RealizationService;
2526
import jakarta.annotation.PostConstruct;
2627
import org.springframework.beans.factory.annotation.Autowired;
@@ -41,6 +42,9 @@ public class SuggestionApprovedTriggerPlugin extends CrowdinTriggerPlugin {
4142
@Autowired
4243
private RealizationService realizationService;
4344

45+
@Autowired
46+
private WebhookService webhookService;
47+
4448
@PostConstruct
4549
public void init() {
4650
crowdinTriggerService.addPlugin(this);
@@ -51,7 +55,7 @@ public List<Event> getEvents(String trigger, Map<String, Object> payload) {
5155
String objectId = constructObjectIdAsJsonString(payload, TRANSLATION);
5256

5357
List<Event> eventList = new ArrayList<>();
54-
eventList.add(new Event(APPROVE_SUGGESTION_EVENT_NAME,
58+
eventList.add(new Event(SUGGESTION_APPROVED_EVENT_NAME,
5559
extractSubItem(payload, TRANSLATION, USER, USERNAME),
5660
extractSubItem(payload, TRANSLATION, USER, USERNAME),
5761
objectId,
@@ -63,13 +67,14 @@ public List<Event> getEvents(String trigger, Map<String, Object> payload) {
6367
trigger.equals(SUGGESTION_DISAPPROVED_TRIGGER),
6468
countWords(extractSubItem(payload, TRANSLATION, STRING, TEXT))));
6569

66-
List<RealizationDTO> realizations = realizationService.findRealizationsByObjectIdAndObjectType(objectId, TRANSLATION);
70+
// Retrieve who made that approval by calling Crowdin API
71+
String translationId = extractSubItem(payload, TRANSLATION, ID);
72+
RemoteApproval remoteApproval = webhookService.getApproval(getProjectId(payload), translationId);
6773

68-
if (!realizations.isEmpty()) {
69-
String earnerId = realizations.get(0).getEarnerId();
70-
eventList.add(new Event(SUGGESTION_APPROVED_EVENT_NAME,
71-
earnerId,
72-
earnerId,
74+
if (remoteApproval != null) {
75+
eventList.add(new Event(APPROVE_SUGGESTION_EVENT_NAME,
76+
remoteApproval.getUserName(),
77+
remoteApproval.getUserName(),
7378
objectId,
7479
TRANSLATION,
7580
getProjectId(payload),
@@ -80,7 +85,6 @@ public List<Event> getEvents(String trigger, Map<String, Object> payload) {
8085
countWords(extractSubItem(payload, TRANSLATION, STRING, TEXT))));
8186

8287
}
83-
8488
return eventList;
8589
}
8690

gamification-crowdin-services/src/main/java/io/meeds/crowdin/gamification/services/WebhookService.java

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
*/
1919
package io.meeds.crowdin.gamification.services;
2020

21+
import io.meeds.crowdin.gamification.model.RemoteApproval;
2122
import io.meeds.crowdin.gamification.model.RemoteDirectory;
2223
import io.meeds.crowdin.gamification.model.RemoteProject;
2324
import io.meeds.crowdin.gamification.model.WebHook;
@@ -56,10 +57,18 @@ public List<RemoteProject> getProjects(String accessToken) throws IllegalAccessE
5657
return crowdinConsumerStorage.getProjects(accessToken);
5758
}
5859

60+
public RemoteApproval getApproval(String projectId, String translationId) {
61+
WebHook webHook = webHookStorage.getWebhookByProjectId(Long.parseLong(projectId));
62+
if (webHook != null) {
63+
return crowdinConsumerStorage.getApproval(webHook.getToken(), projectId, translationId);
64+
}
65+
return null;
66+
}
67+
5968
public WebHook createWebhook(long projectId,
60-
String projectName,
61-
String accessToken,
62-
String currentUser) throws ObjectAlreadyExistsException, IllegalAccessException {
69+
String projectName,
70+
String accessToken,
71+
String currentUser) throws ObjectAlreadyExistsException, IllegalAccessException {
6372
if (!Utils.isRewardingManager(currentUser)) {
6473
throw new IllegalAccessException("The user is not authorized to create Crowdin hook");
6574
}
@@ -80,7 +89,7 @@ public WebHook createWebhook(long projectId,
8089
}
8190

8291
public void updateWebHookAccessToken(long webHookId, String accessToken, String currentUser) throws IllegalAccessException,
83-
ObjectNotFoundException {
92+
ObjectNotFoundException {
8493
if (!Utils.isRewardingManager(currentUser)) {
8594
throw new IllegalAccessException(AUTHORIZED_TO_ACCESS_CROWDIN_HOOKS);
8695
}

gamification-crowdin-services/src/main/java/io/meeds/crowdin/gamification/storage/CrowdinConsumerStorage.java

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
*/
1919
package io.meeds.crowdin.gamification.storage;
2020

21+
import io.meeds.crowdin.gamification.model.*;
2122
import org.apache.commons.httpclient.HttpStatus;
2223
import org.apache.commons.io.IOUtils;
2324
import org.apache.commons.lang3.StringUtils;
@@ -39,10 +40,6 @@
3940
import org.exoplatform.services.log.Log;
4041

4142
import io.meeds.crowdin.gamification.exception.CrowdinConnectionException;
42-
import io.meeds.crowdin.gamification.model.RemoteDirectory;
43-
import io.meeds.crowdin.gamification.model.RemoteLanguage;
44-
import io.meeds.crowdin.gamification.model.RemoteProject;
45-
import io.meeds.crowdin.gamification.model.WebHook;
4643

4744
import org.json.JSONArray;
4845
import org.json.JSONObject;
@@ -235,7 +232,7 @@ private String processDelete(URI uri, String accessToken) throws CrowdinConnecti
235232
private String processRequest(HttpClient httpClient, HttpRequestBase request) throws IOException, CrowdinConnectionException {
236233
HttpResponse response = httpClient.execute(request);
237234
boolean isSuccess = response != null
238-
&& (response.getStatusLine().getStatusCode() >= 200 && response.getStatusLine().getStatusCode() < 300);
235+
&& (response.getStatusLine().getStatusCode() >= 200 && response.getStatusLine().getStatusCode() < 300);
239236
if (isSuccess) {
240237
return processSuccessResponse(response);
241238
} else if (response != null && response.getStatusLine().getStatusCode() == 404) {
@@ -250,8 +247,8 @@ private String processSuccessResponse(HttpResponse response) throws IOException
250247
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_NO_CONTENT) {
251248
return String.valueOf(HttpStatus.SC_NO_CONTENT);
252249
} else if ((response.getStatusLine().getStatusCode() == HttpStatus.SC_CREATED
253-
|| response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) && response.getEntity() != null
254-
&& response.getEntity().getContentLength() != 0) {
250+
|| response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) && response.getEntity() != null
251+
&& response.getEntity().getContentLength() != 0) {
255252
try (InputStream is = response.getEntity().getContent()) {
256253
return IOUtils.toString(is, StandardCharsets.UTF_8);
257254
}
@@ -281,8 +278,8 @@ private HttpClient getHttpClient() {
281278
if (client == null) {
282279
HttpClientConnectionManager clientConnectionManager = getClientConnectionManager();
283280
HttpClientBuilder httpClientBuilder = HttpClients.custom()
284-
.setConnectionManager(clientConnectionManager)
285-
.setConnectionReuseStrategy(new DefaultConnectionReuseStrategy());
281+
.setConnectionManager(clientConnectionManager)
282+
.setConnectionReuseStrategy(new DefaultConnectionReuseStrategy());
286283
client = httpClientBuilder.build();
287284
}
288285
return client;
@@ -380,6 +377,33 @@ public List<RemoteDirectory> getProjectDirectories(long remoteProjectId,
380377
}
381378
}
382379

380+
public RemoteApproval getApproval(String accessToken, String projectId, String translationId) {
381+
try {
382+
URI uri = URI.create(CROWDIN_API_URL + PROJECTS + projectId + APPROVALS + translationId);
383+
384+
String response = processGet(uri, accessToken);
385+
JSONObject jsonObject = new JSONObject(response).getJSONObject("data");
386+
387+
RemoteApproval remoteApproval = new RemoteApproval();
388+
389+
JSONObject userJsonObject = jsonObject.getJSONObject("user");
390+
391+
remoteApproval.setId(jsonObject.getInt("id"));
392+
393+
remoteApproval.setUserName(userJsonObject.getString("username"));
394+
remoteApproval.setTranslationId(jsonObject.getString("translationId"));
395+
remoteApproval.setLanguageId(jsonObject.getString("translationId"));
396+
remoteApproval.setStringId(jsonObject.getString("stringId"));
397+
remoteApproval.setLanguageId(jsonObject.getString("languageId"));
398+
399+
return remoteApproval;
400+
} catch (CrowdinConnectionException e) {
401+
LOG.warn("Unable to retrieve approval for crowdin translation with id {}.", translationId, e);
402+
return null;
403+
}
404+
}
405+
406+
383407
public void clearCache() {
384408
// implemented in cached storage
385409
}

gamification-crowdin-services/src/main/java/io/meeds/crowdin/gamification/utils/Utils.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public class Utils {
1818
public static final String CONNECTOR_NAME = "crowdin";
1919

2020
public static final String[] CROWDIN_EVENTS = new String[] { "stringComment.created",
21-
"stringComment.deleted", "suggestion.added", "suggestion.deleted", "suggestion.approved", "suggestion.disapproved" };
21+
"stringComment.deleted", "suggestion.added", "suggestion.deleted", "suggestion.approved", "suggestion.disapproved" };
2222

2323
public static final String PROJECT_ID = "projectId";
2424

@@ -34,6 +34,8 @@ public class Utils {
3434

3535
public static final String WEBHOOKS = "/webhooks/";
3636

37+
public static final String APPROVALS = "/approvals/";
38+
3739
public static final String AUTHORIZED_TO_ACCESS_CROWDIN_HOOKS = "The user is not authorized to access crowdin Hooks";
3840

3941
public static final String GAMIFICATION_GENERIC_EVENT = "exo.gamification.generic.action";
@@ -57,15 +59,15 @@ public class Utils {
5759
public static final String LANGUAGE_ID = "languageId";
5860

5961
public static final String MUST_BE_HUMAN = "mustBeHuman";
60-
62+
6163
public static final String TOTAL_TARGET_ITEM = "totalTargetItem";
6264

6365
public static final String STRING = "string";
6466

6567
public static final String TARGET_LANGUAGE = "targetLanguage";
6668

6769
public static final String PROJECT = "project";
68-
70+
6971
public static final String TEXT = "text";
7072

7173
public static final String ID = "id";
@@ -180,7 +182,7 @@ public static String constructObjectIdAsJsonString(Map<String, Object> payload,
180182
String sourceLanguageId = extractSubItem(payload, payloadObjectName, STRING, PROJECT, SOURCE_LANGUAGE_ID);
181183
String targetLanguageId = extractSubItem(payload, payloadObjectName, TARGET_LANGUAGE, ID);
182184
return "{\"id\":" + id + ",\"stringUrl\":\"https://crowdin.com/editor/" + projectSlug + "/" + fileId + "/" + sourceLanguageId
183-
+ "-" + targetLanguageId + "?view=comfortable#" + stringId + "\"}";
185+
+ "-" + targetLanguageId + "?view=comfortable#" + stringId + "\"}";
184186
}
185187

186188
public static int countWords(String text) {

0 commit comments

Comments
 (0)