From e07c17a1e6a2663dca6ddbd09d2e7045e82b4238 Mon Sep 17 00:00:00 2001 From: Yevheniy Oliynyk Date: Wed, 20 Nov 2024 08:48:23 +0100 Subject: [PATCH] feat: app commands (#868) --- .../crowdin/cli/client/CrowdinClientCore.java | 7 ++ .../cli/client/CrowdinProjectClient.java | 40 ++++++++++ .../com/crowdin/cli/client/ProjectClient.java | 11 +++ .../com/crowdin/cli/commands/Actions.java | 6 ++ .../commands/actions/AppInstallAction.java | 27 +++++++ .../cli/commands/actions/AppListAction.java | 29 +++++++ .../commands/actions/AppUninstallAction.java | 23 ++++++ .../cli/commands/actions/CliActions.java | 15 ++++ .../picocli/AppInstallSubcommand.java | 22 ++++++ .../commands/picocli/AppListSubcommand.java | 26 +++++++ .../picocli/AppUninstallSubcommand.java | 25 +++++++ .../picocli/ApplicationSubcommand.java | 19 +++++ .../cli/commands/picocli/CommandNames.java | 3 + .../cli/commands/picocli/RootCommand.java | 3 +- .../resources/messages/messages.properties | 24 ++++++ .../actions/AppInstallActionTest.java | 50 +++++++++++++ .../commands/actions/AppListActionTest.java | 75 +++++++++++++++++++ .../picocli/AppInstallSubcommandTest.java | 16 ++++ .../picocli/AppListSubcommandTest.java | 16 ++++ .../picocli/AppUninstallSubcommandTest.java | 25 +++++++ .../commands/picocli/PicocliTestUtils.java | 3 + versions.properties | 2 +- website/mantemplates/crowdin-app-install.adoc | 16 ++++ website/mantemplates/crowdin-app-list.adoc | 16 ++++ .../mantemplates/crowdin-app-uninstall.adoc | 16 ++++ website/mantemplates/crowdin-app.adoc | 16 ++++ website/sidebars.js | 15 ++++ 27 files changed, 544 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/crowdin/cli/commands/actions/AppInstallAction.java create mode 100644 src/main/java/com/crowdin/cli/commands/actions/AppListAction.java create mode 100644 src/main/java/com/crowdin/cli/commands/actions/AppUninstallAction.java create mode 100644 src/main/java/com/crowdin/cli/commands/picocli/AppInstallSubcommand.java create mode 100644 src/main/java/com/crowdin/cli/commands/picocli/AppListSubcommand.java create mode 100644 src/main/java/com/crowdin/cli/commands/picocli/AppUninstallSubcommand.java create mode 100644 src/main/java/com/crowdin/cli/commands/picocli/ApplicationSubcommand.java create mode 100644 src/test/java/com/crowdin/cli/commands/actions/AppInstallActionTest.java create mode 100644 src/test/java/com/crowdin/cli/commands/actions/AppListActionTest.java create mode 100644 src/test/java/com/crowdin/cli/commands/picocli/AppInstallSubcommandTest.java create mode 100644 src/test/java/com/crowdin/cli/commands/picocli/AppListSubcommandTest.java create mode 100644 src/test/java/com/crowdin/cli/commands/picocli/AppUninstallSubcommandTest.java create mode 100644 website/mantemplates/crowdin-app-install.adoc create mode 100644 website/mantemplates/crowdin-app-list.adoc create mode 100644 website/mantemplates/crowdin-app-uninstall.adoc create mode 100644 website/mantemplates/crowdin-app.adoc diff --git a/src/main/java/com/crowdin/cli/client/CrowdinClientCore.java b/src/main/java/com/crowdin/cli/client/CrowdinClientCore.java index 6b42db4ca..28e4a23b0 100644 --- a/src/main/java/com/crowdin/cli/client/CrowdinClientCore.java +++ b/src/main/java/com/crowdin/cli/client/CrowdinClientCore.java @@ -100,6 +100,13 @@ protected static T executeRequest(Supplier r) { return executeRequest(new HashMap, RuntimeException>(), r); } + protected static void executeRequest(Runnable r) { + executeRequest(() -> { + r.run(); + return null; + }); + } + protected static T executeRequest(Map, R> errorHandlers, Supplier r) throws R { try { return r.get(); diff --git a/src/main/java/com/crowdin/cli/client/CrowdinProjectClient.java b/src/main/java/com/crowdin/cli/client/CrowdinProjectClient.java index fc38104f4..2b31d637a 100644 --- a/src/main/java/com/crowdin/cli/client/CrowdinProjectClient.java +++ b/src/main/java/com/crowdin/cli/client/CrowdinProjectClient.java @@ -2,6 +2,8 @@ import com.crowdin.cli.commands.picocli.ExitCodeExceptionMapper; import com.crowdin.cli.utils.Utils; +import com.crowdin.client.applications.installations.model.ApplicationInstallation; +import com.crowdin.client.applications.installations.model.InstallApplicationRequest; import com.crowdin.client.branches.model.*; import com.crowdin.client.core.model.PatchRequest; import com.crowdin.client.labels.model.AddLabelRequest; @@ -18,10 +20,15 @@ import com.crowdin.client.stringcomments.model.StringComment; import com.crowdin.client.translations.model.*; import com.crowdin.client.translationstatus.model.LanguageProgress; +import lombok.SneakyThrows; import org.apache.commons.lang3.StringUtils; +import org.json.JSONArray; +import org.json.JSONObject; import java.io.InputStream; import java.net.URL; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import java.util.*; import java.util.function.BiPredicate; @@ -530,4 +537,37 @@ public Project addProject(AddProjectRequest request) { .addProject(request) .getData()); } + + @Override + public List listApplications() { + return executeRequestFullList((limit, offset) -> + this.client.getApplicationsApi().listApplicationInstallations(limit, offset) + ); + } + + @Override + public void uninstallApplication(String id, boolean force) { + executeRequest(() -> this.client.getApplicationsApi().deleteApplicationInstallation(id, force)); + } + + @Override + public void installApplication(String url) { + var req = new InstallApplicationRequest(); + req.setUrl(url); + executeRequest(() -> this.client.getApplicationsApi().installApplication(req)); + } + + @Override + @SneakyThrows + public Optional findManifestUrl(String id) { + var query = URLEncoder.encode( "{\"slug\":{\"_eq\":\"" + id + "\"}}", StandardCharsets.UTF_8); + var url = new URL("https://developer.app.crowdin.net/items/Item?filter=" + query + "&fields=manifest"); + var res = new String(url.openStream().readAllBytes()); + JSONObject json = new JSONObject(res); + var apps = (JSONArray) json.get("data"); + if (apps.isEmpty()) { + return Optional.empty(); + } + return Optional.ofNullable(JSONObject.class.cast(apps.get(0)).get("manifest").toString()); + } } diff --git a/src/main/java/com/crowdin/cli/client/ProjectClient.java b/src/main/java/com/crowdin/cli/client/ProjectClient.java index 67517451c..f38b83b6b 100644 --- a/src/main/java/com/crowdin/cli/client/ProjectClient.java +++ b/src/main/java/com/crowdin/cli/client/ProjectClient.java @@ -1,5 +1,7 @@ package com.crowdin.cli.client; +import com.crowdin.client.applications.installations.model.ApplicationInstallation; +import com.crowdin.client.applications.model.ApplicationDataResponseObject; import com.crowdin.client.branches.model.*; import com.crowdin.client.core.model.PatchRequest; import com.crowdin.client.labels.model.AddLabelRequest; @@ -17,6 +19,7 @@ import java.io.InputStream; import java.net.URL; import java.util.List; +import java.util.Optional; public interface ProjectClient extends Client { @@ -129,4 +132,12 @@ default CrowdinProjectFull downloadFullProject() { List listProjects(); Project addProject(AddProjectRequest request); + + List listApplications(); + + void uninstallApplication(String id, boolean force); + + void installApplication(String url); + + Optional findManifestUrl(String id); } diff --git a/src/main/java/com/crowdin/cli/commands/Actions.java b/src/main/java/com/crowdin/cli/commands/Actions.java index 706a1ed3c..fe78ac24f 100644 --- a/src/main/java/com/crowdin/cli/commands/Actions.java +++ b/src/main/java/com/crowdin/cli/commands/Actions.java @@ -149,4 +149,10 @@ NewAction preTranslate( NewAction projectList(boolean isVerbose); NewAction projectAdd(String name, boolean isStringBased, String sourceLanguage, List languages, boolean isPublic, boolean plainView); + + NewAction listApps(boolean plainView); + + NewAction uninstallApp(String id, Boolean force); + + NewAction installApp(String identifier); } diff --git a/src/main/java/com/crowdin/cli/commands/actions/AppInstallAction.java b/src/main/java/com/crowdin/cli/commands/actions/AppInstallAction.java new file mode 100644 index 000000000..e2825f06a --- /dev/null +++ b/src/main/java/com/crowdin/cli/commands/actions/AppInstallAction.java @@ -0,0 +1,27 @@ +package com.crowdin.cli.commands.actions; + +import com.crowdin.cli.client.ProjectClient; +import com.crowdin.cli.commands.NewAction; +import com.crowdin.cli.commands.Outputter; +import com.crowdin.cli.commands.picocli.ExitCodeExceptionMapper; +import com.crowdin.cli.properties.ProjectProperties; +import com.crowdin.cli.utils.console.ExecutionStatus; +import lombok.RequiredArgsConstructor; + +import static com.crowdin.cli.BaseCli.RESOURCE_BUNDLE; + +@RequiredArgsConstructor +class AppInstallAction implements NewAction { + + private final String id; + + @Override + public void act(Outputter out, ProjectProperties pb, ProjectClient client) { + var manifestUrl = client.findManifestUrl(id); + if (manifestUrl.isEmpty()) { + throw new ExitCodeExceptionMapper.NotFoundException(String.format(RESOURCE_BUNDLE.getString("error.application_not_found"), this.id)); + } + client.installApplication(manifestUrl.get()); + out.println(ExecutionStatus.OK.withIcon(String.format(RESOURCE_BUNDLE.getString("message.application.install"), id))); + } +} diff --git a/src/main/java/com/crowdin/cli/commands/actions/AppListAction.java b/src/main/java/com/crowdin/cli/commands/actions/AppListAction.java new file mode 100644 index 000000000..63ecb9cf4 --- /dev/null +++ b/src/main/java/com/crowdin/cli/commands/actions/AppListAction.java @@ -0,0 +1,29 @@ +package com.crowdin.cli.commands.actions; + +import com.crowdin.cli.client.ProjectClient; +import com.crowdin.cli.commands.NewAction; +import com.crowdin.cli.commands.Outputter; +import com.crowdin.cli.properties.ProjectProperties; + +import static com.crowdin.cli.BaseCli.RESOURCE_BUNDLE; + +class AppListAction implements NewAction { + private final boolean plainView; + + public AppListAction(boolean plainView) { + this.plainView = plainView; + } + + @Override + public void act(Outputter out, ProjectProperties pb, ProjectClient client) { + client + .listApplications() + .forEach(app -> { + if (!plainView) { + out.println(String.format(RESOURCE_BUNDLE.getString("message.application.list"), app.getIdentifier(), app.getName())); + } else { + out.println(app.getIdentifier()); + } + }); + } +} diff --git a/src/main/java/com/crowdin/cli/commands/actions/AppUninstallAction.java b/src/main/java/com/crowdin/cli/commands/actions/AppUninstallAction.java new file mode 100644 index 000000000..9fa635e8b --- /dev/null +++ b/src/main/java/com/crowdin/cli/commands/actions/AppUninstallAction.java @@ -0,0 +1,23 @@ +package com.crowdin.cli.commands.actions; + +import com.crowdin.cli.client.ProjectClient; +import com.crowdin.cli.commands.NewAction; +import com.crowdin.cli.commands.Outputter; +import com.crowdin.cli.properties.ProjectProperties; +import com.crowdin.cli.utils.console.ExecutionStatus; +import lombok.RequiredArgsConstructor; + +import static com.crowdin.cli.BaseCli.RESOURCE_BUNDLE; + +@RequiredArgsConstructor +class AppUninstallAction implements NewAction { + + private final String id; + private final boolean force; + + @Override + public void act(Outputter out, ProjectProperties pb, ProjectClient client) { + client.uninstallApplication(id, force); + out.println(ExecutionStatus.OK.withIcon(String.format(RESOURCE_BUNDLE.getString("message.application.uninstall"), id))); + } +} diff --git a/src/main/java/com/crowdin/cli/commands/actions/CliActions.java b/src/main/java/com/crowdin/cli/commands/actions/CliActions.java index 0e45b101b..fff90eb0e 100644 --- a/src/main/java/com/crowdin/cli/commands/actions/CliActions.java +++ b/src/main/java/com/crowdin/cli/commands/actions/CliActions.java @@ -323,4 +323,19 @@ public NewAction projectList(boolean isVerbose public NewAction projectAdd(String name, boolean isStringBased, String sourceLanguage, List languages, boolean isPublic, boolean plainView) { return new ProjectAddAction(name, isStringBased, sourceLanguage, languages, isPublic, plainView); } + + @Override + public NewAction listApps(boolean plainView) { + return new AppListAction(plainView); + } + + @Override + public NewAction uninstallApp(String id, Boolean force) { + return new AppUninstallAction(id, force); + } + + @Override + public NewAction installApp(String identifier) { + return new AppInstallAction(identifier); + } } diff --git a/src/main/java/com/crowdin/cli/commands/picocli/AppInstallSubcommand.java b/src/main/java/com/crowdin/cli/commands/picocli/AppInstallSubcommand.java new file mode 100644 index 000000000..a940ef61b --- /dev/null +++ b/src/main/java/com/crowdin/cli/commands/picocli/AppInstallSubcommand.java @@ -0,0 +1,22 @@ +package com.crowdin.cli.commands.picocli; + +import com.crowdin.cli.client.ProjectClient; +import com.crowdin.cli.commands.Actions; +import com.crowdin.cli.commands.NewAction; +import com.crowdin.cli.properties.ProjectProperties; +import picocli.CommandLine; + +@CommandLine.Command( + sortOptions = false, + name = CommandNames.INSTALL +) +class AppInstallSubcommand extends ActCommandProject { + + @CommandLine.Parameters(descriptionKey = "crowdin.app.install.identifier") + protected String identifier; + + @Override + protected NewAction getAction(Actions actions) { + return actions.installApp(identifier); + } +} diff --git a/src/main/java/com/crowdin/cli/commands/picocli/AppListSubcommand.java b/src/main/java/com/crowdin/cli/commands/picocli/AppListSubcommand.java new file mode 100644 index 000000000..d005f5e33 --- /dev/null +++ b/src/main/java/com/crowdin/cli/commands/picocli/AppListSubcommand.java @@ -0,0 +1,26 @@ +package com.crowdin.cli.commands.picocli; + +import com.crowdin.cli.client.ProjectClient; +import com.crowdin.cli.commands.Actions; +import com.crowdin.cli.commands.NewAction; +import com.crowdin.cli.properties.ProjectProperties; +import picocli.CommandLine; + +@CommandLine.Command( + name = CommandNames.LIST +) +class AppListSubcommand extends ActCommandProject { + + @Override + protected NewAction getAction(Actions actions) { + return actions.listApps(this.plainView); + } + + @CommandLine.Option(names = {"--plain"}, descriptionKey = "crowdin.list.usage.plain") + protected boolean plainView; + + @Override + protected final boolean isAnsi() { + return super.isAnsi() && !plainView; + } +} diff --git a/src/main/java/com/crowdin/cli/commands/picocli/AppUninstallSubcommand.java b/src/main/java/com/crowdin/cli/commands/picocli/AppUninstallSubcommand.java new file mode 100644 index 000000000..f1554f832 --- /dev/null +++ b/src/main/java/com/crowdin/cli/commands/picocli/AppUninstallSubcommand.java @@ -0,0 +1,25 @@ +package com.crowdin.cli.commands.picocli; + +import com.crowdin.cli.client.ProjectClient; +import com.crowdin.cli.commands.Actions; +import com.crowdin.cli.commands.NewAction; +import com.crowdin.cli.properties.ProjectProperties; +import picocli.CommandLine; + +@CommandLine.Command( + sortOptions = false, + name = CommandNames.UNINSTALL +) +class AppUninstallSubcommand extends ActCommandProject { + + @CommandLine.Parameters(descriptionKey = "crowdin.app.uninstall.identifier") + protected String identifier; + + @CommandLine.Option(names = {"--force"}, descriptionKey = "crowdin.app.uninstall.force", order = -2) + protected boolean force; + + @Override + protected NewAction getAction(Actions actions) { + return actions.uninstallApp(identifier, force); + } +} diff --git a/src/main/java/com/crowdin/cli/commands/picocli/ApplicationSubcommand.java b/src/main/java/com/crowdin/cli/commands/picocli/ApplicationSubcommand.java new file mode 100644 index 000000000..d967eaf0f --- /dev/null +++ b/src/main/java/com/crowdin/cli/commands/picocli/ApplicationSubcommand.java @@ -0,0 +1,19 @@ +package com.crowdin.cli.commands.picocli; + +import picocli.CommandLine; + +@CommandLine.Command( + name = CommandNames.APP, + subcommands = { + AppListSubcommand.class, + AppInstallSubcommand.class, + AppUninstallSubcommand.class + } +) +class ApplicationSubcommand extends HelpCommand { + + @Override + protected CommandLine getCommand(CommandLine rootCommand) { + return rootCommand.getSubcommands().get(CommandNames.APP); + } +} diff --git a/src/main/java/com/crowdin/cli/commands/picocli/CommandNames.java b/src/main/java/com/crowdin/cli/commands/picocli/CommandNames.java index cd89e29f3..76c2d0396 100644 --- a/src/main/java/com/crowdin/cli/commands/picocli/CommandNames.java +++ b/src/main/java/com/crowdin/cli/commands/picocli/CommandNames.java @@ -65,4 +65,7 @@ public final class CommandNames { public static final String PROJECT = "project"; public static final String BROWSE = "browse"; + public static final String APP = "app"; + public static final String UNINSTALL = "uninstall"; + public static final String INSTALL = "install"; } diff --git a/src/main/java/com/crowdin/cli/commands/picocli/RootCommand.java b/src/main/java/com/crowdin/cli/commands/picocli/RootCommand.java index d64b1aabd..1624886ca 100644 --- a/src/main/java/com/crowdin/cli/commands/picocli/RootCommand.java +++ b/src/main/java/com/crowdin/cli/commands/picocli/RootCommand.java @@ -25,7 +25,8 @@ LanguageSubcommand.class, ConfigSubcommand.class, ProjectSubcommand.class, - CompletionSubCommand.class + CompletionSubCommand.class, + ApplicationSubcommand.class }) class RootCommand extends HelpCommand { @Override diff --git a/src/main/resources/messages/messages.properties b/src/main/resources/messages/messages.properties index f712bf6cb..a1a28789c 100755 --- a/src/main/resources/messages/messages.properties +++ b/src/main/resources/messages/messages.properties @@ -476,6 +476,25 @@ crowdin.project.add.source-language=Defines the source language. English by defa crowdin.project.add.public=Defines whether the project is public. Private by default crowdin.project.add.string-based=Defines whether the project is string-based +# CROWDIN APP COMMAND +crowdin.app.usage.description=Manage apps +crowdin.app.usage.customSynopsis=@|fg(green) crowdin app|@ [SUBCOMMAND] [CONFIG OPTIONS] [OPTIONS] + +# CROWDIN APP LIST +crowdin.app.list.usage.description=List installed apps +crowdin.app.list.usage.customSynopsis=@|fg(green) crowdin app list|@ [CONFIG OPTIONS] [OPTIONS] + +# CROWDIN APP INSTALL +crowdin.app.install.usage.description=Install the application +crowdin.app.install.usage.customSynopsis=@|fg(green) crowdin app install|@ [CONFIG OPTIONS] [OPTIONS] +crowdin.app.install.identifier=Application identifier. You can find it on Crowdin Store + +# CROWDIN APP UNINSTALL +crowdin.app.uninstall.usage.description=Uninstall the application +crowdin.app.uninstall.usage.customSynopsis=@|fg(green) crowdin app uninstall|@ [CONFIG OPTIONS] [OPTIONS] +crowdin.app.uninstall.identifier=Application identifier +crowdin.app.uninstall.force=Force to delete application installation + error.collect_project_info=Failed to collect project info. Please contact our support team for help error.no_sources=No sources found for '%s' pattern. Check the source paths in your configuration file error.only_enterprise=Operation is available only for Crowdin Enterprise @@ -521,6 +540,7 @@ error.file_not_exists=Project doesn't contain the '%s' file error.file_required=The '--file' parameter is required for this type of project error.dir_not_exists=Project doesn't contain the '%s' directory error.branch_not_exists=Project doesn't contain the '%s' branch +error.application_not_found=Application with identifier '%s' doesn't exist in Crowdin Store error.source_string_no_edit=Specify some parameters to edit the string error.branch_no_edit=Specify some parameters to edit the branch error.unexpected_response=Unexpected response from %s: %s @@ -771,6 +791,10 @@ message.label.list_empty=No labels found message.label.already_exists=Label '%s' already exists in the project message.label.deleted=@|green Label '%s' deleted successfully|@ +message.application.list=@|yellow %s|@ @|green %s|@ +message.application.uninstall=@|green Application %s has been uninstalled|@ +message.application.install=@|green Application %s has been installed|@ + message.delete_obsolete.obsolete_file_delete='%s' file was deleted message.delete_obsolete.obsolete_directory_delete=No obsolete files were found message.delete_obsolete.no_obsolete_files_found='%s' directory was deleted diff --git a/src/test/java/com/crowdin/cli/commands/actions/AppInstallActionTest.java b/src/test/java/com/crowdin/cli/commands/actions/AppInstallActionTest.java new file mode 100644 index 000000000..b168956a1 --- /dev/null +++ b/src/test/java/com/crowdin/cli/commands/actions/AppInstallActionTest.java @@ -0,0 +1,50 @@ +package com.crowdin.cli.commands.actions; + +import com.crowdin.cli.client.ProjectClient; +import com.crowdin.cli.commands.NewAction; +import com.crowdin.cli.commands.Outputter; +import com.crowdin.cli.commands.picocli.ExitCodeExceptionMapper; +import com.crowdin.cli.properties.ProjectProperties; +import com.crowdin.cli.properties.PropertiesWithFiles; +import org.junit.jupiter.api.Test; + +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.*; + +public class AppInstallActionTest { + + Outputter out = Outputter.getDefault(); + PropertiesWithFiles pb; + + ProjectClient clientMock = mock(ProjectClient.class); + NewAction action; + + @Test + public void testInstall() { + String id = "test"; + String url = "test.com/manifest.json"; + when(clientMock.findManifestUrl(id)).thenReturn(Optional.of(url)); + doNothing().when(clientMock).installApplication(url); + + action = new AppInstallAction(id); + action.act(out, pb, clientMock); + + verify(clientMock).findManifestUrl(id); + verify(clientMock).installApplication(url); + verifyNoMoreInteractions(clientMock); + } + + @Test + public void testInstallFailed() { + String id = "test"; + when(clientMock.findManifestUrl(id)).thenReturn(Optional.empty()); + + action = new AppInstallAction(id); + assertThrows(ExitCodeExceptionMapper.NotFoundException.class, () -> action.act(out, pb, clientMock)); + + verify(clientMock).findManifestUrl(id); + verifyNoMoreInteractions(clientMock); + } +} diff --git a/src/test/java/com/crowdin/cli/commands/actions/AppListActionTest.java b/src/test/java/com/crowdin/cli/commands/actions/AppListActionTest.java new file mode 100644 index 000000000..4b16b9015 --- /dev/null +++ b/src/test/java/com/crowdin/cli/commands/actions/AppListActionTest.java @@ -0,0 +1,75 @@ +package com.crowdin.cli.commands.actions; + +import com.crowdin.cli.client.ProjectClient; +import com.crowdin.cli.commands.NewAction; +import com.crowdin.cli.commands.Outputter; +import com.crowdin.cli.properties.ProjectProperties; +import com.crowdin.cli.properties.PropertiesWithFiles; +import com.crowdin.client.applications.installations.model.ApplicationInstallation; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.mockito.Mockito.*; + +public class AppListActionTest { + + List standardList = Arrays.asList(new ApplicationInstallation(), new ApplicationInstallation()); + List emptyList = Collections.emptyList(); + + Outputter out = Outputter.getDefault(); + PropertiesWithFiles pb; + + ProjectClient clientMock = mock(ProjectClient.class); + NewAction action; + + @Test + public void test_standard() { + when(clientMock.listApplications()) + .thenReturn(standardList); + + action = new AppListAction(false); + action.act(out, pb, clientMock); + + verify(clientMock).listApplications(); + verifyNoMoreInteractions(clientMock); + } + + @Test + public void test_plainView() { + when(clientMock.listApplications()) + .thenReturn(standardList); + + action = new AppListAction(true); + action.act(out, pb, clientMock); + + verify(clientMock).listApplications(); + verifyNoMoreInteractions(clientMock); + } + + @Test + public void test_emptyList() { + when(clientMock.listApplications()) + .thenReturn(emptyList); + + action = new AppListAction(false); + action.act(out, pb, clientMock); + + verify(clientMock).listApplications(); + verifyNoMoreInteractions(clientMock); + } + + @Test + public void test_emptyList_plainView() { + when(clientMock.listApplications()) + .thenReturn(emptyList); + + action = new AppListAction(true); + action.act(out, pb, clientMock); + + verify(clientMock).listApplications(); + verifyNoMoreInteractions(clientMock); + } +} diff --git a/src/test/java/com/crowdin/cli/commands/picocli/AppInstallSubcommandTest.java b/src/test/java/com/crowdin/cli/commands/picocli/AppInstallSubcommandTest.java new file mode 100644 index 000000000..da48e6b8d --- /dev/null +++ b/src/test/java/com/crowdin/cli/commands/picocli/AppInstallSubcommandTest.java @@ -0,0 +1,16 @@ +package com.crowdin.cli.commands.picocli; + +import org.junit.jupiter.api.Test; + +import static org.mockito.Mockito.verify; + +public class AppInstallSubcommandTest extends PicocliTestUtils { + + @Test + public void testAppInstall() { + var appId = "test-app"; + this.execute(CommandNames.APP, CommandNames.INSTALL, appId); + verify(actionsMock).installApp(appId); + this.check(true); + } +} diff --git a/src/test/java/com/crowdin/cli/commands/picocli/AppListSubcommandTest.java b/src/test/java/com/crowdin/cli/commands/picocli/AppListSubcommandTest.java new file mode 100644 index 000000000..a2c99984c --- /dev/null +++ b/src/test/java/com/crowdin/cli/commands/picocli/AppListSubcommandTest.java @@ -0,0 +1,16 @@ +package com.crowdin.cli.commands.picocli; + +import org.junit.jupiter.api.Test; + +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.Mockito.verify; + +public class AppListSubcommandTest extends PicocliTestUtils { + + @Test + public void testAppList() { + this.execute(CommandNames.APP, CommandNames.LIST); + verify(actionsMock).listApps(anyBoolean()); + this.check(true); + } +} diff --git a/src/test/java/com/crowdin/cli/commands/picocli/AppUninstallSubcommandTest.java b/src/test/java/com/crowdin/cli/commands/picocli/AppUninstallSubcommandTest.java new file mode 100644 index 000000000..1215dcfaa --- /dev/null +++ b/src/test/java/com/crowdin/cli/commands/picocli/AppUninstallSubcommandTest.java @@ -0,0 +1,25 @@ +package com.crowdin.cli.commands.picocli; + +import org.junit.jupiter.api.Test; + +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.Mockito.verify; + +public class AppUninstallSubcommandTest extends PicocliTestUtils { + + @Test + public void testAppUninstall() { + var appId = "test-app"; + this.execute(CommandNames.APP, CommandNames.UNINSTALL, appId); + verify(actionsMock).uninstallApp(appId, false); + this.check(true); + } + + @Test + public void testAppUninstallForce() { + var appId = "test-app"; + this.execute(CommandNames.APP, CommandNames.UNINSTALL, appId, "--force"); + verify(actionsMock).uninstallApp(appId, true); + this.check(true); + } +} diff --git a/src/test/java/com/crowdin/cli/commands/picocli/PicocliTestUtils.java b/src/test/java/com/crowdin/cli/commands/picocli/PicocliTestUtils.java index 7f87d52d8..e5a9d276e 100644 --- a/src/test/java/com/crowdin/cli/commands/picocli/PicocliTestUtils.java +++ b/src/test/java/com/crowdin/cli/commands/picocli/PicocliTestUtils.java @@ -145,6 +145,9 @@ void mockActions() { .thenReturn(actionMock); when(actionsMock.branchEdit(any(), any(), any(), any(), anyBoolean(), anyBoolean())) .thenReturn(actionMock); + when(actionsMock.listApps(anyBoolean())).thenReturn(actionMock); + when(actionsMock.uninstallApp(anyString(), anyBoolean())).thenReturn(actionMock); + when(actionsMock.installApp(anyString())).thenReturn(actionMock); } private void mockBuilders() { diff --git a/versions.properties b/versions.properties index 2e6549ba9..b93d55e9c 100644 --- a/versions.properties +++ b/versions.properties @@ -43,7 +43,7 @@ version.commons-io..commons-io=2.16.1 version.commons-cli..commons-cli=1.7.0 -version.com.github.crowdin..crowdin-api-client-java=1.19.2 +version.com.github.crowdin..crowdin-api-client-java=1.19.4 plugin.org.asciidoctor.jvm.convert=3.3.2 diff --git a/website/mantemplates/crowdin-app-install.adoc b/website/mantemplates/crowdin-app-install.adoc new file mode 100644 index 000000000..0f5515c12 --- /dev/null +++ b/website/mantemplates/crowdin-app-install.adoc @@ -0,0 +1,16 @@ +:includedir: ../generated-picocli-docs +:command: crowdin-app-install + +== crowdin app install + +include::{includedir}/{command}.adoc[tag=picocli-generated-man-section-description] + +include::{includedir}/{command}.adoc[tag=picocli-generated-man-section-synopsis] + +include::{includedir}/{command}.adoc[tag=picocli-generated-man-section-arguments] + +include::{includedir}/{command}.adoc[tag=picocli-generated-man-section-commands] + +include::{includedir}/{command}.adoc[tag=picocli-generated-man-section-options] + +include::{includedir}/{command}.adoc[tag=picocli-generated-man-section-footer] diff --git a/website/mantemplates/crowdin-app-list.adoc b/website/mantemplates/crowdin-app-list.adoc new file mode 100644 index 000000000..ce6edd667 --- /dev/null +++ b/website/mantemplates/crowdin-app-list.adoc @@ -0,0 +1,16 @@ +:includedir: ../generated-picocli-docs +:command: crowdin-app-list + +== crowdin app list + +include::{includedir}/{command}.adoc[tag=picocli-generated-man-section-description] + +include::{includedir}/{command}.adoc[tag=picocli-generated-man-section-synopsis] + +include::{includedir}/{command}.adoc[tag=picocli-generated-man-section-arguments] + +include::{includedir}/{command}.adoc[tag=picocli-generated-man-section-commands] + +include::{includedir}/{command}.adoc[tag=picocli-generated-man-section-options] + +include::{includedir}/{command}.adoc[tag=picocli-generated-man-section-footer] diff --git a/website/mantemplates/crowdin-app-uninstall.adoc b/website/mantemplates/crowdin-app-uninstall.adoc new file mode 100644 index 000000000..2a06be9f9 --- /dev/null +++ b/website/mantemplates/crowdin-app-uninstall.adoc @@ -0,0 +1,16 @@ +:includedir: ../generated-picocli-docs +:command: crowdin-app-uninstall + +== crowdin app uninstall + +include::{includedir}/{command}.adoc[tag=picocli-generated-man-section-description] + +include::{includedir}/{command}.adoc[tag=picocli-generated-man-section-synopsis] + +include::{includedir}/{command}.adoc[tag=picocli-generated-man-section-arguments] + +include::{includedir}/{command}.adoc[tag=picocli-generated-man-section-commands] + +include::{includedir}/{command}.adoc[tag=picocli-generated-man-section-options] + +include::{includedir}/{command}.adoc[tag=picocli-generated-man-section-footer] diff --git a/website/mantemplates/crowdin-app.adoc b/website/mantemplates/crowdin-app.adoc new file mode 100644 index 000000000..1e2ea0584 --- /dev/null +++ b/website/mantemplates/crowdin-app.adoc @@ -0,0 +1,16 @@ +:includedir: ../generated-picocli-docs +:command: crowdin-app + +== crowdin app + +include::{includedir}/{command}.adoc[tag=picocli-generated-man-section-description] + +include::{includedir}/{command}.adoc[tag=picocli-generated-man-section-synopsis] + +include::{includedir}/{command}.adoc[tag=picocli-generated-man-section-arguments] + +include::{includedir}/{command}.adoc[tag=picocli-generated-man-section-commands] + +include::{includedir}/{command}.adoc[tag=picocli-generated-man-section-options] + +include::{includedir}/{command}.adoc[tag=picocli-generated-man-section-footer] diff --git a/website/sidebars.js b/website/sidebars.js index d95a6da21..e277d7235 100644 --- a/website/sidebars.js +++ b/website/sidebars.js @@ -281,6 +281,21 @@ const sidebars = { 'commands/crowdin-label-add', 'commands/crowdin-label-delete', ] + }, + { + type: 'category', + label: 'crowdin app', + link: { + type: 'doc', + id: 'commands/crowdin-app' + }, + collapsible: true, + collapsed: true, + items: [ + 'commands/crowdin-app-list', + 'commands/crowdin-app-install', + 'commands/crowdin-app-uninstall', + ] } ], },