Skip to content

Commit ad22789

Browse files
committed
feat: bundle download retries on fail
1 parent d904da2 commit ad22789

File tree

5 files changed

+73
-19
lines changed

5 files changed

+73
-19
lines changed

src/main/java/com/crowdin/cli/client/ClientBundle.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public interface ClientBundle extends Client {
1717

1818
URL downloadBundle(Long id, String exportId);
1919

20-
BundleExport startExportingBundle(Long id);
20+
BundleExport startExportingBundle(Long id) throws ResponseException;
2121

2222
BundleExport checkExportingBundle(Long tmId, String exportId);
2323

src/main/java/com/crowdin/cli/client/CrowdinClientBundle.java

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
package com.crowdin.cli.client;
22

3+
import com.crowdin.cli.utils.Utils;
34
import com.crowdin.client.bundles.model.AddBundleRequest;
45
import com.crowdin.client.bundles.model.Bundle;
56
import com.crowdin.client.bundles.model.BundleExport;
67
import lombok.SneakyThrows;
78

89
import java.net.URL;
10+
import java.util.LinkedHashMap;
911
import java.util.List;
12+
import java.util.Map;
13+
import java.util.function.BiPredicate;
1014

1115
public class CrowdinClientBundle extends CrowdinClientCore implements ClientBundle {
1216

@@ -46,10 +50,20 @@ public URL downloadBundle(Long id, String exportId) {
4650
}
4751

4852
@Override
49-
public BundleExport startExportingBundle(Long id) {
50-
return executeRequest(() -> this.client.getBundlesApi()
53+
public BundleExport startExportingBundle(Long id) throws ResponseException {
54+
Map<BiPredicate<String, String>, ResponseException> errorHandler = new LinkedHashMap<>() {{
55+
put((code, message) -> Utils.isServerErrorCode(code), new RepeatException("Server Error"));
56+
put((code, message) -> message.contains("Request aborted"), new RepeatException("Request aborted"));
57+
put((code, message) -> message.contains("Connection reset"), new RepeatException("Connection reset"));
58+
}};
59+
return executeRequestWithPossibleRetries(
60+
errorHandler,
61+
() -> this.client.getBundlesApi()
5162
.exportBundle(Long.valueOf(projectId), id)
52-
.getData());
63+
.getData(),
64+
3,
65+
3 * 1000
66+
);
5367
}
5468

5569
@Override

src/main/java/com/crowdin/cli/commands/actions/BundleDownloadAction.java

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.crowdin.cli.commands.actions;
22

33
import com.crowdin.cli.client.ClientBundle;
4+
import com.crowdin.cli.client.MaxNumberOfRetriesException;
45
import com.crowdin.cli.commands.NewAction;
56
import com.crowdin.cli.commands.Outputter;
67
import com.crowdin.cli.commands.functionality.*;
@@ -19,8 +20,7 @@
1920
import java.util.stream.Collectors;
2021

2122
import static com.crowdin.cli.BaseCli.RESOURCE_BUNDLE;
22-
import static com.crowdin.cli.utils.console.ExecutionStatus.ERROR;
23-
import static com.crowdin.cli.utils.console.ExecutionStatus.OK;
23+
import static com.crowdin.cli.utils.console.ExecutionStatus.*;
2424

2525
public class BundleDownloadAction implements NewAction<ProjectProperties, ClientBundle> {
2626

@@ -48,6 +48,9 @@ public void act(Outputter out, ProjectProperties pb, ClientBundle client) {
4848
this.out = out;
4949
Bundle bundle = getBundle(client);
5050
BundleExport status = this.buildBundle(out, client, bundle.getId());
51+
if (status == null) {
52+
throw new RuntimeException(RESOURCE_BUNDLE.getString("error.bundle.build_bundle"));
53+
}
5154
to = new File("bundle-" + status.getIdentifier() + ".zip");
5255
downloadBundle(client, bundle.getId(), status.getIdentifier());
5356
out.println(OK.withIcon(String.format(RESOURCE_BUNDLE.getString("message.bundle.download_success"), bundle.getId(), bundle.getName())));
@@ -94,21 +97,25 @@ private BundleExport buildBundle(Outputter out, ClientBundle client, Long bundle
9497
this.noProgress,
9598
false,
9699
() -> {
97-
BundleExport status = client.startExportingBundle(bundleId);
100+
BundleExport status = null;
101+
try {
102+
status = client.startExportingBundle(bundleId);
98103

99-
while (!status.getStatus().equalsIgnoreCase("finished")) {
100-
ConsoleSpinner.update(String.format(RESOURCE_BUNDLE.getString("message.spinner.building_bundle_percents"), status.getProgress()));
101-
Thread.sleep(1000);
104+
while (!status.getStatus().equalsIgnoreCase("finished")) {
105+
ConsoleSpinner.update(String.format(RESOURCE_BUNDLE.getString("message.spinner.building_bundle_percents"), status.getProgress()));
106+
Thread.sleep(1000);
102107

103-
status = client.checkExportingBundle(bundleId, status.getIdentifier());
108+
status = client.checkExportingBundle(bundleId, status.getIdentifier());
104109

105-
if (status.getStatus().equalsIgnoreCase("failed")) {
106-
throw new RuntimeException(RESOURCE_BUNDLE.getString("message.spinner.build_has_failed"));
110+
if (status.getStatus().equalsIgnoreCase("failed")) {
111+
throw new RuntimeException(RESOURCE_BUNDLE.getString("message.spinner.build_has_failed"));
112+
}
107113
}
108-
}
109-
110-
ConsoleSpinner.update(String.format(RESOURCE_BUNDLE.getString("message.spinner.building_bundle_percents"), 100));
111114

115+
ConsoleSpinner.update(String.format(RESOURCE_BUNDLE.getString("message.spinner.building_bundle_percents"), 100));
116+
} catch (MaxNumberOfRetriesException e) {
117+
ConsoleSpinner.stop(WARNING, RESOURCE_BUNDLE.getString("message.warning.maximum_retries_exceeded"));
118+
}
112119
return status;
113120
}
114121
);

src/main/resources/messages/messages.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -818,6 +818,7 @@ message.warning.auto_approve_option_with_mt='--auto-approve-option' is used only
818818
message.warning.no_file_to_download=Couldn't find any file to download
819819
message.warning.no_file_to_download_skipuntranslated=Couldn't find any file to download. Since you are using the 'Skip untranslated files' option, please make sure you have fully translated files
820820
message.warning.another_build_in_progress=Another build is currently in progress. Please wait until it's finished
821+
message.warning.maximum_retries_exceeded=The operation failed after several retries. Please try again later.
821822
message.spinner.fetching_project_info=Fetching project info
822823
message.spinner.building_translations=Building translations
823824
message.spinner.building_translation=Building translation

src/test/java/com/crowdin/cli/commands/actions/BundleDownloadActionTest.java

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import com.crowdin.cli.MockitoUtils;
44
import com.crowdin.cli.client.ClientBundle;
5+
import com.crowdin.cli.client.MaxNumberOfRetriesException;
6+
import com.crowdin.cli.client.ResponseException;
57
import com.crowdin.cli.commands.NewAction;
68
import com.crowdin.cli.commands.Outputter;
79
import com.crowdin.cli.commands.functionality.FilesInterface;
@@ -16,8 +18,8 @@
1618
import org.junit.jupiter.api.Test;
1719

1820
import java.net.URL;
19-
import java.util.Optional;
2021

22+
import static org.junit.jupiter.api.Assertions.assertThrows;
2123
import static org.mockito.Mockito.*;
2224

2325
public class BundleDownloadActionTest {
@@ -35,7 +37,7 @@ public void deleteProj() {
3537
}
3638

3739
@Test
38-
public void testDownloadBundle() {
40+
public void testDownloadBundle() throws ResponseException {
3941
PropertiesWithFiles pb = NewPropertiesWithFilesUtilBuilder
4042
.minimalBuiltPropertiesBean(
4143
"/values/strings.xml", "/values-%two_letters_code%/%original_file_name%",
@@ -73,7 +75,7 @@ public void testDownloadBundle() {
7375
}
7476

7577
@Test
76-
public void testDryRun() {
78+
public void testDryRun() throws ResponseException {
7779
PropertiesWithFiles pb = NewPropertiesWithFilesUtilBuilder
7880
.minimalBuiltPropertiesBean(
7981
"/values/strings.xml", "/values-%two_letters_code%/%original_file_name%",
@@ -110,4 +112,34 @@ public void testDryRun() {
110112

111113
verifyNoMoreInteractions(client);
112114
}
115+
116+
@Test
117+
public void testDownloadBundle_FailBundleExport() throws ResponseException {
118+
PropertiesWithFiles pb = NewPropertiesWithFilesUtilBuilder
119+
.minimalBuiltPropertiesBean(
120+
"/values/strings.xml", "/values-%two_letters_code%/%original_file_name%",
121+
null, "/common/%original_file_name%")
122+
.setBasePath(project.getBasePath())
123+
.build();
124+
125+
Bundle bundle = new Bundle();
126+
bundle.setId(1L);
127+
ClientBundle client = mock(ClientBundle.class);
128+
129+
when(client.getBundle(bundle.getId()))
130+
.thenReturn(bundle);
131+
when(client.startExportingBundle(bundle.getId()))
132+
.thenThrow(new MaxNumberOfRetriesException());
133+
134+
FilesInterface files = mock(FilesInterface.class);
135+
136+
NewAction<ProjectProperties, ClientBundle> action =
137+
new BundleDownloadAction(bundle.getId(), files, false, false, false, true);
138+
assertThrows(RuntimeException.class, () -> action.act(Outputter.getDefault(), pb, client));
139+
140+
verify(client).getBundle(bundle.getId());
141+
verify(client).startExportingBundle(bundle.getId());
142+
143+
verifyNoMoreInteractions(client);
144+
}
113145
}

0 commit comments

Comments
 (0)