From 754a86ee25d31c1708e9ae60dae3a3ed8391d0bc Mon Sep 17 00:00:00 2001 From: Yevheniy Oliynyk Date: Tue, 12 Nov 2024 20:16:33 +0100 Subject: [PATCH 1/8] feat: app list --- .../cli/client/CrowdinProjectClient.java | 8 +++++ .../com/crowdin/cli/client/ProjectClient.java | 4 +++ .../com/crowdin/cli/commands/Actions.java | 2 ++ .../cli/commands/actions/AppListAction.java | 29 +++++++++++++++++++ .../cli/commands/actions/CliActions.java | 5 ++++ .../commands/picocli/AppListSubcommand.java | 26 +++++++++++++++++ .../picocli/ApplicationSubcommand.java | 17 +++++++++++ .../cli/commands/picocli/CommandNames.java | 1 + .../cli/commands/picocli/RootCommand.java | 3 +- .../resources/messages/messages.properties | 10 +++++++ versions.properties | 2 +- website/mantemplates/crowdin-app-list.adoc | 16 ++++++++++ website/mantemplates/crowdin-app.adoc | 16 ++++++++++ website/sidebars.js | 13 +++++++++ 14 files changed, 150 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/crowdin/cli/commands/actions/AppListAction.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/ApplicationSubcommand.java create mode 100644 website/mantemplates/crowdin-app-list.adoc create mode 100644 website/mantemplates/crowdin-app.adoc diff --git a/src/main/java/com/crowdin/cli/client/CrowdinProjectClient.java b/src/main/java/com/crowdin/cli/client/CrowdinProjectClient.java index fc38104f4..c3deca1dc 100644 --- a/src/main/java/com/crowdin/cli/client/CrowdinProjectClient.java +++ b/src/main/java/com/crowdin/cli/client/CrowdinProjectClient.java @@ -2,6 +2,7 @@ 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.branches.model.*; import com.crowdin.client.core.model.PatchRequest; import com.crowdin.client.labels.model.AddLabelRequest; @@ -530,4 +531,11 @@ public Project addProject(AddProjectRequest request) { .addProject(request) .getData()); } + + @Override + public List listApplications() { + return executeRequestFullList((limit, offset) -> + this.client.getApplicationsApi().listApplicationInstallations(limit, offset) + ); + } } diff --git a/src/main/java/com/crowdin/cli/client/ProjectClient.java b/src/main/java/com/crowdin/cli/client/ProjectClient.java index 67517451c..79bbd2b39 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; @@ -129,4 +131,6 @@ default CrowdinProjectFull downloadFullProject() { List listProjects(); Project addProject(AddProjectRequest request); + + List listApplications(); } diff --git a/src/main/java/com/crowdin/cli/commands/Actions.java b/src/main/java/com/crowdin/cli/commands/Actions.java index 706a1ed3c..c766c16d6 100644 --- a/src/main/java/com/crowdin/cli/commands/Actions.java +++ b/src/main/java/com/crowdin/cli/commands/Actions.java @@ -149,4 +149,6 @@ NewAction preTranslate( NewAction projectList(boolean isVerbose); NewAction projectAdd(String name, boolean isStringBased, String sourceLanguage, List languages, boolean isPublic, boolean plainView); + + NewAction listApps(boolean plainView); } 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/CliActions.java b/src/main/java/com/crowdin/cli/commands/actions/CliActions.java index 0e45b101b..5cddcc7c9 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,9 @@ 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); + } } 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/ApplicationSubcommand.java b/src/main/java/com/crowdin/cli/commands/picocli/ApplicationSubcommand.java new file mode 100644 index 000000000..774c5def8 --- /dev/null +++ b/src/main/java/com/crowdin/cli/commands/picocli/ApplicationSubcommand.java @@ -0,0 +1,17 @@ +package com.crowdin.cli.commands.picocli; + +import picocli.CommandLine; + +@CommandLine.Command( + name = CommandNames.APP, + subcommands = { + AppListSubcommand.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..8f63e59fc 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,5 @@ public final class CommandNames { public static final String PROJECT = "project"; public static final String BROWSE = "browse"; + public static final String APP = "app"; } 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..7d1cfd809 100755 --- a/src/main/resources/messages/messages.properties +++ b/src/main/resources/messages/messages.properties @@ -476,6 +476,14 @@ 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 apps +crowdin.app.list.usage.customSynopsis=@|fg(green) crowdin app list|@ [CONFIG OPTIONS] [OPTIONS] + 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 @@ -771,6 +779,8 @@ 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.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/versions.properties b/versions.properties index 2e6549ba9..6bc2ecdfd 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.3 plugin.org.asciidoctor.jvm.convert=3.3.2 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.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..6d4fa67e4 100644 --- a/website/sidebars.js +++ b/website/sidebars.js @@ -281,6 +281,19 @@ 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', + ] } ], }, From 700ee5fe3cc710b1e593f0b06544024155736b44 Mon Sep 17 00:00:00 2001 From: Yevheniy Oliynyk Date: Wed, 13 Nov 2024 17:02:39 +0100 Subject: [PATCH 2/8] unit tests --- .../commands/actions/AppListActionTest.java | 75 +++++++++++++++++++ .../picocli/AppListSubcommandTest.java | 16 ++++ .../commands/picocli/PicocliTestUtils.java | 1 + 3 files changed, 92 insertions(+) create mode 100644 src/test/java/com/crowdin/cli/commands/actions/AppListActionTest.java create mode 100644 src/test/java/com/crowdin/cli/commands/picocli/AppListSubcommandTest.java 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/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/PicocliTestUtils.java b/src/test/java/com/crowdin/cli/commands/picocli/PicocliTestUtils.java index 7f87d52d8..ce1a4bc43 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,7 @@ void mockActions() { .thenReturn(actionMock); when(actionsMock.branchEdit(any(), any(), any(), any(), anyBoolean(), anyBoolean())) .thenReturn(actionMock); + when(actionsMock.listApps(anyBoolean())).thenReturn(actionMock); } private void mockBuilders() { From 8a8d5536f1fe45e18135484f60df0fc41991c560 Mon Sep 17 00:00:00 2001 From: Yevheniy Oliynyk Date: Wed, 13 Nov 2024 17:39:09 +0100 Subject: [PATCH 3/8] app uninstall --- .../crowdin/cli/client/CrowdinClientCore.java | 7 ++++++ .../cli/client/CrowdinProjectClient.java | 5 ++++ .../com/crowdin/cli/client/ProjectClient.java | 2 ++ .../com/crowdin/cli/commands/Actions.java | 2 ++ .../commands/actions/AppUninstallAction.java | 23 +++++++++++++++++ .../cli/commands/actions/CliActions.java | 5 ++++ .../picocli/AppUninstallSubcommand.java | 25 +++++++++++++++++++ .../picocli/ApplicationSubcommand.java | 1 + .../cli/commands/picocli/CommandNames.java | 1 + .../resources/messages/messages.properties | 7 ++++++ .../picocli/AppUninstallSubcommandTest.java | 25 +++++++++++++++++++ .../commands/picocli/PicocliTestUtils.java | 1 + versions.properties | 2 +- .../mantemplates/crowdin-app-uninstall.adoc | 16 ++++++++++++ website/sidebars.js | 1 + 15 files changed, 122 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/crowdin/cli/commands/actions/AppUninstallAction.java create mode 100644 src/main/java/com/crowdin/cli/commands/picocli/AppUninstallSubcommand.java create mode 100644 src/test/java/com/crowdin/cli/commands/picocli/AppUninstallSubcommandTest.java create mode 100644 website/mantemplates/crowdin-app-uninstall.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 c3deca1dc..f13cbc758 100644 --- a/src/main/java/com/crowdin/cli/client/CrowdinProjectClient.java +++ b/src/main/java/com/crowdin/cli/client/CrowdinProjectClient.java @@ -538,4 +538,9 @@ public List listApplications() { this.client.getApplicationsApi().listApplicationInstallations(limit, offset) ); } + + @Override + public void uninstallApplication(String id, boolean force) { + executeRequest(() -> this.client.getApplicationsApi().deleteApplicationInstallation(id, force)); + } } diff --git a/src/main/java/com/crowdin/cli/client/ProjectClient.java b/src/main/java/com/crowdin/cli/client/ProjectClient.java index 79bbd2b39..6fd730172 100644 --- a/src/main/java/com/crowdin/cli/client/ProjectClient.java +++ b/src/main/java/com/crowdin/cli/client/ProjectClient.java @@ -133,4 +133,6 @@ default CrowdinProjectFull downloadFullProject() { Project addProject(AddProjectRequest request); List listApplications(); + + void uninstallApplication(String id, boolean force); } diff --git a/src/main/java/com/crowdin/cli/commands/Actions.java b/src/main/java/com/crowdin/cli/commands/Actions.java index c766c16d6..017eedef6 100644 --- a/src/main/java/com/crowdin/cli/commands/Actions.java +++ b/src/main/java/com/crowdin/cli/commands/Actions.java @@ -151,4 +151,6 @@ NewAction preTranslate( NewAction projectAdd(String name, boolean isStringBased, String sourceLanguage, List languages, boolean isPublic, boolean plainView); NewAction listApps(boolean plainView); + + NewAction uninstallApp(String id, Boolean force); } 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 5cddcc7c9..04695c536 100644 --- a/src/main/java/com/crowdin/cli/commands/actions/CliActions.java +++ b/src/main/java/com/crowdin/cli/commands/actions/CliActions.java @@ -328,4 +328,9 @@ public NewAction projectAdd(String name, boole public NewAction listApps(boolean plainView) { return new AppListAction(plainView); } + + @Override + public NewAction uninstallApp(String id, Boolean force) { + return new AppUninstallAction(id, force); + } } 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 index 774c5def8..a8bec10b2 100644 --- a/src/main/java/com/crowdin/cli/commands/picocli/ApplicationSubcommand.java +++ b/src/main/java/com/crowdin/cli/commands/picocli/ApplicationSubcommand.java @@ -6,6 +6,7 @@ name = CommandNames.APP, subcommands = { AppListSubcommand.class, + AppUninstallSubcommand.class, } ) class ApplicationSubcommand extends HelpCommand { 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 8f63e59fc..eca29a7f5 100644 --- a/src/main/java/com/crowdin/cli/commands/picocli/CommandNames.java +++ b/src/main/java/com/crowdin/cli/commands/picocli/CommandNames.java @@ -66,4 +66,5 @@ 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"; } diff --git a/src/main/resources/messages/messages.properties b/src/main/resources/messages/messages.properties index 7d1cfd809..6323aab69 100755 --- a/src/main/resources/messages/messages.properties +++ b/src/main/resources/messages/messages.properties @@ -484,6 +484,12 @@ crowdin.app.usage.customSynopsis=@|fg(green) crowdin app|@ [SUBCOMMAND] [CONFIG crowdin.app.list.usage.description=List apps crowdin.app.list.usage.customSynopsis=@|fg(green) crowdin app list|@ [CONFIG OPTIONS] [OPTIONS] +# CROWDIN BRANCH DELETE COMMAND +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 @@ -780,6 +786,7 @@ 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 was uninstalled|@ message.delete_obsolete.obsolete_file_delete='%s' file was deleted message.delete_obsolete.obsolete_directory_delete=No obsolete files were found 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 ce1a4bc43..06b8e08c5 100644 --- a/src/test/java/com/crowdin/cli/commands/picocli/PicocliTestUtils.java +++ b/src/test/java/com/crowdin/cli/commands/picocli/PicocliTestUtils.java @@ -146,6 +146,7 @@ void mockActions() { when(actionsMock.branchEdit(any(), any(), any(), any(), anyBoolean(), anyBoolean())) .thenReturn(actionMock); when(actionsMock.listApps(anyBoolean())).thenReturn(actionMock); + when(actionsMock.uninstallApp(anyString(), anyBoolean())).thenReturn(actionMock); } private void mockBuilders() { diff --git a/versions.properties b/versions.properties index 6bc2ecdfd..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.3 +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-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/sidebars.js b/website/sidebars.js index 6d4fa67e4..feb792426 100644 --- a/website/sidebars.js +++ b/website/sidebars.js @@ -293,6 +293,7 @@ const sidebars = { collapsed: true, items: [ 'commands/crowdin-app-list', + 'commands/crowdin-app-uninstall', ] } ], From 9523bc7448301ac4183357d3c31b8d45bc2cb830 Mon Sep 17 00:00:00 2001 From: Yevheniy Oliynyk Date: Wed, 13 Nov 2024 17:39:58 +0100 Subject: [PATCH 4/8] typo --- src/main/resources/messages/messages.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/messages/messages.properties b/src/main/resources/messages/messages.properties index 6323aab69..4f3134c79 100755 --- a/src/main/resources/messages/messages.properties +++ b/src/main/resources/messages/messages.properties @@ -484,7 +484,7 @@ crowdin.app.usage.customSynopsis=@|fg(green) crowdin app|@ [SUBCOMMAND] [CONFIG crowdin.app.list.usage.description=List apps crowdin.app.list.usage.customSynopsis=@|fg(green) crowdin app list|@ [CONFIG OPTIONS] [OPTIONS] -# CROWDIN BRANCH DELETE COMMAND +# 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 From 85b0fc09fe5db30adec2bbad0ba4ca008dd77d84 Mon Sep 17 00:00:00 2001 From: Yevheniy Oliynyk Date: Thu, 14 Nov 2024 21:02:41 +0100 Subject: [PATCH 5/8] wip --- .../cli/client/CrowdinProjectClient.java | 8 ++++++ .../com/crowdin/cli/client/ProjectClient.java | 2 ++ .../com/crowdin/cli/commands/Actions.java | 2 ++ .../commands/actions/AppInstallAction.java | 27 +++++++++++++++++++ .../cli/commands/actions/CliActions.java | 5 ++++ .../picocli/AppInstallSubcommand.java | 22 +++++++++++++++ .../picocli/ApplicationSubcommand.java | 3 ++- .../cli/commands/picocli/CommandNames.java | 1 + .../resources/messages/messages.properties | 6 +++++ .../picocli/AppInstallSubcommandTest.java | 16 +++++++++++ .../commands/picocli/PicocliTestUtils.java | 1 + website/mantemplates/crowdin-app-install.adoc | 16 +++++++++++ website/sidebars.js | 1 + 13 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/crowdin/cli/commands/actions/AppInstallAction.java create mode 100644 src/main/java/com/crowdin/cli/commands/picocli/AppInstallSubcommand.java create mode 100644 src/test/java/com/crowdin/cli/commands/picocli/AppInstallSubcommandTest.java create mode 100644 website/mantemplates/crowdin-app-install.adoc diff --git a/src/main/java/com/crowdin/cli/client/CrowdinProjectClient.java b/src/main/java/com/crowdin/cli/client/CrowdinProjectClient.java index f13cbc758..e4f31a9f8 100644 --- a/src/main/java/com/crowdin/cli/client/CrowdinProjectClient.java +++ b/src/main/java/com/crowdin/cli/client/CrowdinProjectClient.java @@ -3,6 +3,7 @@ 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; @@ -543,4 +544,11 @@ public List listApplications() { 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)); + } } diff --git a/src/main/java/com/crowdin/cli/client/ProjectClient.java b/src/main/java/com/crowdin/cli/client/ProjectClient.java index 6fd730172..08969c2fe 100644 --- a/src/main/java/com/crowdin/cli/client/ProjectClient.java +++ b/src/main/java/com/crowdin/cli/client/ProjectClient.java @@ -135,4 +135,6 @@ default CrowdinProjectFull downloadFullProject() { List listApplications(); void uninstallApplication(String id, boolean force); + + void installApplication(String url); } diff --git a/src/main/java/com/crowdin/cli/commands/Actions.java b/src/main/java/com/crowdin/cli/commands/Actions.java index 017eedef6..fe78ac24f 100644 --- a/src/main/java/com/crowdin/cli/commands/Actions.java +++ b/src/main/java/com/crowdin/cli/commands/Actions.java @@ -153,4 +153,6 @@ NewAction preTranslate( 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..e803cf7ab --- /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.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) { + client.installApplication(this.findManifestUrl(id)); + out.println(ExecutionStatus.OK.withIcon(String.format(RESOURCE_BUNDLE.getString("message.application.install"), id))); + } + + private String findManifestUrl(String id) { + //TODO fix me + return 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 04695c536..fff90eb0e 100644 --- a/src/main/java/com/crowdin/cli/commands/actions/CliActions.java +++ b/src/main/java/com/crowdin/cli/commands/actions/CliActions.java @@ -333,4 +333,9 @@ public NewAction listApps(boolean plainView) { 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/ApplicationSubcommand.java b/src/main/java/com/crowdin/cli/commands/picocli/ApplicationSubcommand.java index a8bec10b2..d967eaf0f 100644 --- a/src/main/java/com/crowdin/cli/commands/picocli/ApplicationSubcommand.java +++ b/src/main/java/com/crowdin/cli/commands/picocli/ApplicationSubcommand.java @@ -6,7 +6,8 @@ name = CommandNames.APP, subcommands = { AppListSubcommand.class, - AppUninstallSubcommand.class, + AppInstallSubcommand.class, + AppUninstallSubcommand.class } ) class ApplicationSubcommand extends HelpCommand { 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 eca29a7f5..76c2d0396 100644 --- a/src/main/java/com/crowdin/cli/commands/picocli/CommandNames.java +++ b/src/main/java/com/crowdin/cli/commands/picocli/CommandNames.java @@ -67,4 +67,5 @@ public final class CommandNames { 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/resources/messages/messages.properties b/src/main/resources/messages/messages.properties index 4f3134c79..c50cac8f2 100755 --- a/src/main/resources/messages/messages.properties +++ b/src/main/resources/messages/messages.properties @@ -484,6 +484,11 @@ crowdin.app.usage.customSynopsis=@|fg(green) crowdin app|@ [SUBCOMMAND] [CONFIG crowdin.app.list.usage.description=List 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] @@ -787,6 +792,7 @@ message.label.deleted=@|green Label '%s' deleted successfully|@ message.application.list=@|yellow %s|@ @|green %s|@ message.application.uninstall=@|green Application %s was uninstalled|@ +message.application.install=@|green Application %s was installed|@ message.delete_obsolete.obsolete_file_delete='%s' file was deleted message.delete_obsolete.obsolete_directory_delete=No obsolete files were found 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/PicocliTestUtils.java b/src/test/java/com/crowdin/cli/commands/picocli/PicocliTestUtils.java index 06b8e08c5..e5a9d276e 100644 --- a/src/test/java/com/crowdin/cli/commands/picocli/PicocliTestUtils.java +++ b/src/test/java/com/crowdin/cli/commands/picocli/PicocliTestUtils.java @@ -147,6 +147,7 @@ void mockActions() { .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/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/sidebars.js b/website/sidebars.js index feb792426..e277d7235 100644 --- a/website/sidebars.js +++ b/website/sidebars.js @@ -293,6 +293,7 @@ const sidebars = { collapsed: true, items: [ 'commands/crowdin-app-list', + 'commands/crowdin-app-install', 'commands/crowdin-app-uninstall', ] } From 84b49672a39a4dfafa426f409d6099dc75cb93e6 Mon Sep 17 00:00:00 2001 From: Yevheniy Oliynyk Date: Fri, 15 Nov 2024 10:51:41 +0100 Subject: [PATCH 6/8] fixes --- .../cli/client/CrowdinProjectClient.java | 16 ++++++ .../com/crowdin/cli/client/ProjectClient.java | 3 ++ .../commands/actions/AppInstallAction.java | 12 ++--- .../resources/messages/messages.properties | 1 + .../actions/AppInstallActionTest.java | 50 +++++++++++++++++++ 5 files changed, 76 insertions(+), 6 deletions(-) create mode 100644 src/test/java/com/crowdin/cli/commands/actions/AppInstallActionTest.java diff --git a/src/main/java/com/crowdin/cli/client/CrowdinProjectClient.java b/src/main/java/com/crowdin/cli/client/CrowdinProjectClient.java index e4f31a9f8..66ca680d1 100644 --- a/src/main/java/com/crowdin/cli/client/CrowdinProjectClient.java +++ b/src/main/java/com/crowdin/cli/client/CrowdinProjectClient.java @@ -20,7 +20,10 @@ 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; @@ -551,4 +554,17 @@ public void installApplication(String url) { req.setUrl(url); executeRequest(() -> this.client.getApplicationsApi().installApplication(req)); } + + @Override + @SneakyThrows + public Optional findManifestUrl(String id) { + var url = new URL("https://developer.app.crowdin.net/items/Item?filter={\"slug\":{\"_eq\":\"" + id + "\"}}&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 08969c2fe..f38b83b6b 100644 --- a/src/main/java/com/crowdin/cli/client/ProjectClient.java +++ b/src/main/java/com/crowdin/cli/client/ProjectClient.java @@ -19,6 +19,7 @@ import java.io.InputStream; import java.net.URL; import java.util.List; +import java.util.Optional; public interface ProjectClient extends Client { @@ -137,4 +138,6 @@ default CrowdinProjectFull downloadFullProject() { 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/AppInstallAction.java b/src/main/java/com/crowdin/cli/commands/actions/AppInstallAction.java index e803cf7ab..e2825f06a 100644 --- a/src/main/java/com/crowdin/cli/commands/actions/AppInstallAction.java +++ b/src/main/java/com/crowdin/cli/commands/actions/AppInstallAction.java @@ -3,6 +3,7 @@ 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; @@ -16,12 +17,11 @@ class AppInstallAction implements NewAction { @Override public void act(Outputter out, ProjectProperties pb, ProjectClient client) { - client.installApplication(this.findManifestUrl(id)); + 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))); } - - private String findManifestUrl(String id) { - //TODO fix me - return id; - } } diff --git a/src/main/resources/messages/messages.properties b/src/main/resources/messages/messages.properties index c50cac8f2..bed08d84a 100755 --- a/src/main/resources/messages/messages.properties +++ b/src/main/resources/messages/messages.properties @@ -540,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 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); + } +} From 6762c99c3fe5553d848284c902f88a04ac1510af Mon Sep 17 00:00:00 2001 From: Yevheniy Oliynyk Date: Mon, 18 Nov 2024 20:18:49 +0100 Subject: [PATCH 7/8] text fixes --- src/main/resources/messages/messages.properties | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/resources/messages/messages.properties b/src/main/resources/messages/messages.properties index bed08d84a..a1a28789c 100755 --- a/src/main/resources/messages/messages.properties +++ b/src/main/resources/messages/messages.properties @@ -481,7 +481,7 @@ 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 apps +crowdin.app.list.usage.description=List installed apps crowdin.app.list.usage.customSynopsis=@|fg(green) crowdin app list|@ [CONFIG OPTIONS] [OPTIONS] # CROWDIN APP INSTALL @@ -792,8 +792,8 @@ 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 was uninstalled|@ -message.application.install=@|green Application %s was installed|@ +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 From 51f7ac82985ceeb7039d61ed60565743a1d38689 Mon Sep 17 00:00:00 2001 From: Yevheniy Oliynyk Date: Mon, 18 Nov 2024 20:23:27 +0100 Subject: [PATCH 8/8] fix query --- .../java/com/crowdin/cli/client/CrowdinProjectClient.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/crowdin/cli/client/CrowdinProjectClient.java b/src/main/java/com/crowdin/cli/client/CrowdinProjectClient.java index 66ca680d1..2b31d637a 100644 --- a/src/main/java/com/crowdin/cli/client/CrowdinProjectClient.java +++ b/src/main/java/com/crowdin/cli/client/CrowdinProjectClient.java @@ -27,6 +27,8 @@ 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; @@ -558,7 +560,8 @@ public void installApplication(String url) { @Override @SneakyThrows public Optional findManifestUrl(String id) { - var url = new URL("https://developer.app.crowdin.net/items/Item?filter={\"slug\":{\"_eq\":\"" + id + "\"}}&fields=manifest"); + 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");