From 8d00c4b8e47d6cae70ee6489e1230704aa54513e Mon Sep 17 00:00:00 2001 From: Kateryna Oblakevych Date: Mon, 5 Feb 2024 16:15:21 +0200 Subject: [PATCH] feat: strings based project functionality (#714) --- .../cli/client/ClientDistribution.java | 10 +- .../cli/client/CrowdinClientDistribution.java | 24 +- .../cli/client/CrowdinProjectClient.java | 22 +- .../com/crowdin/cli/client/ProjectClient.java | 9 +- .../com/crowdin/cli/commands/Actions.java | 4 +- .../cli/commands/actions/CliActions.java | 8 +- .../actions/DistributionAddAction.java | 46 +++- .../actions/DistributionReleaseAction.java | 57 +++- .../commands/actions/FileDeleteAction.java | 3 +- .../commands/actions/FileDownloadAction.java | 3 +- .../FileDownloadTranslationAction.java | 3 +- .../commands/actions/FileUploadAction.java | 20 +- .../actions/FileUploadTranslationAction.java | 14 +- .../commands/actions/PreTranslateAction.java | 85 +++--- .../cli/commands/actions/StringAddAction.java | 27 +- .../commands/actions/StringListAction.java | 23 +- .../commands/actions/UploadSourcesAction.java | 115 +++++--- .../actions/UploadTranslationsAction.java | 251 +++++++++++------- .../functionality/RequestBuilder.java | 48 +++- .../picocli/DistributionAddSubcommand.java | 4 - .../DistributionReleaseSubcommand.java | 6 +- .../commands/picocli/StringAddSubcommand.java | 5 +- .../resources/messages/messages.properties | 1 + .../cli/commands/actions/CliActionsTest.java | 4 +- .../actions/DistributionAddActionTest.java | 41 ++- .../DistributionReleaseActionTest.java | 43 ++- .../commands/actions/StringAddActionTest.java | 52 +++- .../actions/StringListActionTest.java | 33 ++- .../actions/UploadSourcesActionTest.java | 119 +++++++-- .../actions/UploadTranslationsActionTest.java | 63 ++++- .../DistributionAddSubcommandTest.java | 7 +- .../commands/picocli/PicocliTestUtils.java | 2 +- .../picocli/StringAddSubcommandTest.java | 4 +- 33 files changed, 868 insertions(+), 288 deletions(-) diff --git a/src/main/java/com/crowdin/cli/client/ClientDistribution.java b/src/main/java/com/crowdin/cli/client/ClientDistribution.java index 6e25a2aaf..59c66de8d 100644 --- a/src/main/java/com/crowdin/cli/client/ClientDistribution.java +++ b/src/main/java/com/crowdin/cli/client/ClientDistribution.java @@ -1,8 +1,6 @@ package com.crowdin.cli.client; -import com.crowdin.client.distributions.model.AddDistributionRequest; -import com.crowdin.client.distributions.model.Distribution; -import com.crowdin.client.distributions.model.DistributionRelease; +import com.crowdin.client.distributions.model.*; import java.util.List; @@ -12,7 +10,13 @@ public interface ClientDistribution extends Client { Distribution addDistribution(AddDistributionRequest request); + Distribution addDistributionStringsBased(AddDistributionStringsBasedRequest request); + DistributionRelease release(String hash); + DistributionStringsBasedRelease releaseStringsBased(String hash); + DistributionRelease getDistributionRelease(String hash); + + DistributionStringsBasedRelease getDistributionStringsBasedRelease(String hash); } diff --git a/src/main/java/com/crowdin/cli/client/CrowdinClientDistribution.java b/src/main/java/com/crowdin/cli/client/CrowdinClientDistribution.java index b9be24b39..a3c3c5fb3 100644 --- a/src/main/java/com/crowdin/cli/client/CrowdinClientDistribution.java +++ b/src/main/java/com/crowdin/cli/client/CrowdinClientDistribution.java @@ -1,8 +1,6 @@ package com.crowdin.cli.client; -import com.crowdin.client.distributions.model.AddDistributionRequest; -import com.crowdin.client.distributions.model.Distribution; -import com.crowdin.client.distributions.model.DistributionRelease; +import com.crowdin.client.distributions.model.*; import java.util.List; @@ -29,6 +27,13 @@ public Distribution addDistribution(AddDistributionRequest distributionRequest) .getData()); } + @Override + public Distribution addDistributionStringsBased(AddDistributionStringsBasedRequest distributionRequest) { + return executeRequest(() -> this.client.getDistributionsApi() + .addDistributionStringsBased(Long.valueOf(projectId), distributionRequest) + .getData()); + } + @Override public DistributionRelease release(String hash) { return executeRequest(() -> this.client.getDistributionsApi() @@ -36,6 +41,13 @@ public DistributionRelease release(String hash) { .getData()); } + @Override + public DistributionStringsBasedRelease releaseStringsBased(String hash) { + return executeRequest(() -> this.client.getDistributionsApi() + .createDistributionStringsBasedRelease(Long.valueOf(projectId), hash) + .getData()); + } + @Override public DistributionRelease getDistributionRelease(String hash) { return executeRequest(() -> this.client.getDistributionsApi() @@ -43,4 +55,10 @@ public DistributionRelease getDistributionRelease(String hash) { .getData()); } + @Override + public DistributionStringsBasedRelease getDistributionStringsBasedRelease(String hash) { + return executeRequest(() -> this.client.getDistributionsApi() + .getDistributionStringsBasedRelease(Long.valueOf(projectId), hash) + .getData()); + } } diff --git a/src/main/java/com/crowdin/cli/client/CrowdinProjectClient.java b/src/main/java/com/crowdin/cli/client/CrowdinProjectClient.java index ccd96b7bf..3d6e7a809 100644 --- a/src/main/java/com/crowdin/cli/client/CrowdinProjectClient.java +++ b/src/main/java/com/crowdin/cli/client/CrowdinProjectClient.java @@ -6,10 +6,7 @@ import com.crowdin.client.projectsgroups.model.ProjectSettings; import com.crowdin.client.projectsgroups.model.Type; import com.crowdin.client.sourcefiles.model.*; -import com.crowdin.client.sourcestrings.model.AddSourceStringRequest; -import com.crowdin.client.sourcestrings.model.SourceString; -import com.crowdin.client.sourcestrings.model.UploadStringsProgress; -import com.crowdin.client.sourcestrings.model.UploadStringsRequest; +import com.crowdin.client.sourcestrings.model.*; import com.crowdin.client.storage.model.Storage; import com.crowdin.client.stringcomments.model.AddStringCommentRequest; import com.crowdin.client.stringcomments.model.StringComment; @@ -67,7 +64,8 @@ private void populateProjectWithStructure(CrowdinProjectFull project, String bra .ifPresent(project::setBranch); Long branchId = Optional.ofNullable(project.getBranch()).map(Branch::getId).orElse(null); - if (Objects.equals(project.getType(), Type.STRINGS_BASED)) { + boolean isStringsBasedProject = Objects.equals(project.getType(), Type.STRINGS_BASED); + if (isStringsBasedProject) { return; } project.setFiles(executeRequestFullList((limit, offset) -> this.client.getSourceFilesApi() @@ -328,6 +326,13 @@ public SourceString addSourceString(AddSourceStringRequest request) { .getData()); } + @Override + public SourceString addSourceStringStringsBased(AddSourceStringStringsBasedRequest request) { + return executeRequest(() -> this.client.getSourceStringsApi() + .addSourceStringStringsBased(this.projectId, request) + .getData()); + } + @Override public List listSourceString(Long fileId, Long branchId, String labelIds, String filter, String croql) { return executeRequestFullList((limit, offset) -> this.client.getSourceStringsApi() @@ -391,6 +396,13 @@ public PreTranslationStatus startPreTranslation(ApplyPreTranslationRequest reque .getData()); } + @Override + public PreTranslationStatus startPreTranslationStringsBased(ApplyPreTranslationStringsBasedRequest request) { + return executeRequest(() ->this.client.getTranslationsApi() + .applyPreTranslationStringsBased(this.projectId, request) + .getData()); + } + @Override public PreTranslationStatus checkPreTranslation(String preTranslationId) { return executeRequest(() -> this.client.getTranslationsApi() diff --git a/src/main/java/com/crowdin/cli/client/ProjectClient.java b/src/main/java/com/crowdin/cli/client/ProjectClient.java index 1b8d852d5..f1deb5f17 100644 --- a/src/main/java/com/crowdin/cli/client/ProjectClient.java +++ b/src/main/java/com/crowdin/cli/client/ProjectClient.java @@ -6,10 +6,7 @@ import com.crowdin.client.labels.model.AddLabelRequest; import com.crowdin.client.labels.model.Label; import com.crowdin.client.sourcefiles.model.*; -import com.crowdin.client.sourcestrings.model.AddSourceStringRequest; -import com.crowdin.client.sourcestrings.model.SourceString; -import com.crowdin.client.sourcestrings.model.UploadStringsProgress; -import com.crowdin.client.sourcestrings.model.UploadStringsRequest; +import com.crowdin.client.sourcestrings.model.*; import com.crowdin.client.stringcomments.model.AddStringCommentRequest; import com.crowdin.client.stringcomments.model.StringComment; import com.crowdin.client.translations.model.*; @@ -83,6 +80,8 @@ default CrowdinProjectFull downloadFullProject() { SourceString addSourceString(AddSourceStringRequest request); + SourceString addSourceStringStringsBased(AddSourceStringStringsBasedRequest request); + List listSourceString(Long fileId, Long branchId, String labelIds, String filter, String croql); void deleteSourceString(Long id); @@ -101,5 +100,7 @@ default CrowdinProjectFull downloadFullProject() { PreTranslationStatus startPreTranslation(ApplyPreTranslationRequest request); + PreTranslationStatus startPreTranslationStringsBased(ApplyPreTranslationStringsBasedRequest request); + PreTranslationStatus checkPreTranslation(String preTranslationId); } diff --git a/src/main/java/com/crowdin/cli/commands/Actions.java b/src/main/java/com/crowdin/cli/commands/Actions.java index 6a9702c96..997c17d40 100644 --- a/src/main/java/com/crowdin/cli/commands/Actions.java +++ b/src/main/java/com/crowdin/cli/commands/Actions.java @@ -48,7 +48,7 @@ NewAction status( boolean noProgress, String branchName, String languageId, String file, String directory, boolean isVerbose, boolean showTranslated, boolean showApproved, boolean failIfIncomplete); NewAction stringAdd( - boolean noProgress, String text, String identifier, Integer maxLength, String context, List files, List labelNames, Boolean hidden); + boolean noProgress, String text, String identifier, Integer maxLength, String context, List files, List labelNames, String branch, Boolean hidden); NewAction stringComment(boolean plainView, boolean noProgress, String text, String stringId, String language, String type, String issueType); @@ -97,7 +97,7 @@ NewAction taskAdd( NewAction distributionAdd(boolean noProgress, boolean plainView, String name, ExportMode exportMode, List files, List bundleIds, String branch, ProjectClient projectClient); - NewAction distributionRelease(boolean noProgress, boolean plainView, String hash); + NewAction distributionRelease(boolean noProgress, boolean plainView, String hash, ProjectClient projectClient); NewAction commentList(boolean plainView, boolean isVerbose, String stringId, com.crowdin.client.stringcomments.model.Type type, com.crowdin.client.issues.model.Type issueType, IssueStatus status); 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 2d9a71fab..98518ffd3 100644 --- a/src/main/java/com/crowdin/cli/commands/actions/CliActions.java +++ b/src/main/java/com/crowdin/cli/commands/actions/CliActions.java @@ -80,9 +80,9 @@ public NewAction status( @Override public NewAction stringAdd( - boolean noProgress, String text, String identifier, Integer maxLength, String context, List files, List labelNames, Boolean hidden + boolean noProgress, String text, String identifier, Integer maxLength, String context, List files, List labelNames, String branch, Boolean hidden ) { - return new StringAddAction(noProgress, text, identifier, maxLength, context, files, labelNames, hidden); + return new StringAddAction(noProgress, text, identifier, maxLength, context, files, labelNames, branch, hidden); } @Override public NewAction stringComment(boolean plainView, @@ -189,8 +189,8 @@ public NewAction distributionAdd(boolean } @Override - public NewAction distributionRelease(boolean noProgress, boolean plainView, String hash) { - return new DistributionReleaseAction(noProgress, plainView, hash); + public NewAction distributionRelease(boolean noProgress, boolean plainView, String hash, ProjectClient projectClient) { + return new DistributionReleaseAction(noProgress, plainView, hash, projectClient); } @Override diff --git a/src/main/java/com/crowdin/cli/commands/actions/DistributionAddAction.java b/src/main/java/com/crowdin/cli/commands/actions/DistributionAddAction.java index a3f84615e..d747e1426 100644 --- a/src/main/java/com/crowdin/cli/commands/actions/DistributionAddAction.java +++ b/src/main/java/com/crowdin/cli/commands/actions/DistributionAddAction.java @@ -10,8 +10,10 @@ import com.crowdin.cli.utils.Utils; import com.crowdin.cli.utils.console.ConsoleSpinner; import com.crowdin.client.distributions.model.AddDistributionRequest; +import com.crowdin.client.distributions.model.AddDistributionStringsBasedRequest; import com.crowdin.client.distributions.model.Distribution; import com.crowdin.client.distributions.model.ExportMode; +import com.crowdin.client.projectsgroups.model.Type; import com.crowdin.client.sourcefiles.model.Branch; import com.crowdin.client.sourcefiles.model.FileInfo; import lombok.AllArgsConstructor; @@ -19,11 +21,13 @@ import java.nio.file.Paths; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.stream.Collectors; import static com.crowdin.cli.BaseCli.RESOURCE_BUNDLE; import static com.crowdin.cli.utils.console.ExecutionStatus.OK; +import static com.crowdin.client.distributions.model.ExportMode.DEFAULT; @AllArgsConstructor class DistributionAddAction implements NewAction { @@ -47,8 +51,13 @@ public void act(Outputter out, ProjectProperties pb, ClientDistribution client) this.plainView, () -> this.projectClient.downloadFullProject(this.branch) ); + boolean isStringsBasedProject = Objects.equals(project.getType(), Type.STRINGS_BASED); + List fileIds = null; if (files != null) { + if (isStringsBasedProject) { + throw new RuntimeException(RESOURCE_BUNDLE.getString("message.no_file_string_project")); + } Map projectBranches = project.getBranches().values().stream() .collect(Collectors.toMap(Branch::getName, Branch::getId)); List projectFiles = project.getFiles().stream() @@ -74,19 +83,36 @@ public void act(Outputter out, ProjectProperties pb, ClientDistribution client) .filter(file -> files.contains(file.getPath())) .map(FileInfo::getId) .collect(Collectors.toList()); + } else if (exportMode == DEFAULT && !isStringsBasedProject) { + throw new RuntimeException(RESOURCE_BUNDLE.getString("error.distribution.empty_file")); } - Distribution distribution; - AddDistributionRequest addDistributionRequest = RequestBuilder.addDistribution(name, exportMode, fileIds, bundleIds); - Optional.ofNullable(name).ifPresent(addDistributionRequest::setName); - Optional.ofNullable(exportMode).ifPresent(addDistributionRequest::setExportMode); - Optional.ofNullable(fileIds).ifPresent(addDistributionRequest::setFileIds); - Optional.ofNullable(bundleIds).ifPresent(addDistributionRequest::setBundleIds); + Distribution distribution = null; + if (!isStringsBasedProject) { + AddDistributionRequest addDistributionRequest = RequestBuilder.addDistribution(name, exportMode, fileIds, bundleIds); + Optional.ofNullable(name).ifPresent(addDistributionRequest::setName); + Optional.ofNullable(exportMode).ifPresent(addDistributionRequest::setExportMode); + Optional.ofNullable(fileIds).ifPresent(addDistributionRequest::setFileIds); + Optional.ofNullable(bundleIds).ifPresent(addDistributionRequest::setBundleIds); + + try { + distribution = client.addDistribution(addDistributionRequest); + } catch (Exception e) { + throw new RuntimeException(String.format(RESOURCE_BUNDLE.getString("error.distribution_is_not_added"), addDistributionRequest), e); + } + } else if (isStringsBasedProject) { + AddDistributionStringsBasedRequest addDistributionRequest = new AddDistributionStringsBasedRequest(); + addDistributionRequest.setName(name); + if (Objects.isNull(bundleIds)) { + throw new RuntimeException(RESOURCE_BUNDLE.getString("error.distribution.empty_bundle_ids")); + } + addDistributionRequest.setBundleIds(bundleIds); - try { - distribution = client.addDistribution(addDistributionRequest); - } catch (Exception e) { - throw new RuntimeException(String.format(RESOURCE_BUNDLE.getString("error.distribution_is_not_added"), addDistributionRequest), e); + try { + distribution = client.addDistributionStringsBased(addDistributionRequest); + } catch (Exception e) { + throw new RuntimeException(String.format(RESOURCE_BUNDLE.getString("error.distribution_is_not_added"), addDistributionRequest), e); + } } if (!plainView) { diff --git a/src/main/java/com/crowdin/cli/commands/actions/DistributionReleaseAction.java b/src/main/java/com/crowdin/cli/commands/actions/DistributionReleaseAction.java index 989837961..5b6ae340f 100644 --- a/src/main/java/com/crowdin/cli/commands/actions/DistributionReleaseAction.java +++ b/src/main/java/com/crowdin/cli/commands/actions/DistributionReleaseAction.java @@ -1,13 +1,19 @@ package com.crowdin.cli.commands.actions; import com.crowdin.cli.client.ClientDistribution; +import com.crowdin.cli.client.CrowdinProjectInfo; +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.ConsoleSpinner; import com.crowdin.client.distributions.model.DistributionRelease; +import com.crowdin.client.distributions.model.DistributionStringsBasedRelease; +import com.crowdin.client.projectsgroups.model.Type; import lombok.AllArgsConstructor; +import java.util.Objects; + import static com.crowdin.cli.BaseCli.RESOURCE_BUNDLE; import static com.crowdin.cli.utils.console.ExecutionStatus.OK; @@ -18,13 +24,28 @@ class DistributionReleaseAction implements NewAction this.projectClient.downloadProjectInfo() + ); + boolean isStringsBasedProject = Objects.equals(project.getType(), Type.STRINGS_BASED); + + if (!isStringsBasedProject) { + this.releaseDistributionFilesBased(out, client); + } else { + this.releaseDistributionStringsBased(out, client); + } out.println(OK.withIcon(String.format(RESOURCE_BUNDLE.getString("message.distribution.released"), hash))); } - private DistributionRelease releaseDistribution(Outputter out, ClientDistribution client) { + private DistributionRelease releaseDistributionFilesBased(Outputter out, ClientDistribution client) { return ConsoleSpinner.execute( out, "message.spinner.releasing_distribution", @@ -43,7 +64,37 @@ private DistributionRelease releaseDistribution(Outputter out, ClientDistributio release = client.getDistributionRelease(hash); if ("failed".equalsIgnoreCase(release.getStatus())) { - throw new RuntimeException(RESOURCE_BUNDLE.getString("message.spinner.build_has_failed")); + throw new RuntimeException(RESOURCE_BUNDLE.getString("error.distribution_failed")); + } + } + + ConsoleSpinner.update(String.format(RESOURCE_BUNDLE.getString("message.spinner.releasing_distribution_percents"), 100)); + + return release; + } + ); + } + + private DistributionStringsBasedRelease releaseDistributionStringsBased(Outputter out, ClientDistribution client) { + return ConsoleSpinner.execute( + out, + "message.spinner.releasing_distribution", + "error.distribution_is_not_released", + this.noProgress, + false, + () -> { + DistributionStringsBasedRelease release = client.releaseStringsBased(hash); + + while (!"success".equalsIgnoreCase(release.getStatus())) { + ConsoleSpinner.update( + String.format(RESOURCE_BUNDLE.getString("message.spinner.releasing_distribution_percents"), + release.getProgress())); + Thread.sleep(1000); + + release = client.getDistributionStringsBasedRelease(hash); + + if ("failed".equalsIgnoreCase(release.getStatus())) { + throw new RuntimeException(RESOURCE_BUNDLE.getString("error.distribution_failed")); } } diff --git a/src/main/java/com/crowdin/cli/commands/actions/FileDeleteAction.java b/src/main/java/com/crowdin/cli/commands/actions/FileDeleteAction.java index 83babd4de..f37f7713d 100644 --- a/src/main/java/com/crowdin/cli/commands/actions/FileDeleteAction.java +++ b/src/main/java/com/crowdin/cli/commands/actions/FileDeleteAction.java @@ -30,7 +30,8 @@ public void act(Outputter out, ProjectProperties properties, ProjectClient clien CrowdinProjectFull project = ConsoleSpinner .execute(out, "message.spinner.fetching_project_info", "error.collect_project_info", true, true, client::downloadFullProject); - if (Objects.equals(project.getType(), Type.STRINGS_BASED)) { + boolean isStringsBasedProject = Objects.equals(project.getType(), Type.STRINGS_BASED); + if (isStringsBasedProject) { out.println(SKIPPED.withIcon(RESOURCE_BUNDLE.getString("message.no_file_string_project"))); return; } diff --git a/src/main/java/com/crowdin/cli/commands/actions/FileDownloadAction.java b/src/main/java/com/crowdin/cli/commands/actions/FileDownloadAction.java index 86b2a6c18..8b9a021e2 100644 --- a/src/main/java/com/crowdin/cli/commands/actions/FileDownloadAction.java +++ b/src/main/java/com/crowdin/cli/commands/actions/FileDownloadAction.java @@ -35,7 +35,8 @@ public void act(Outputter out, ProjectProperties properties, ProjectClient clien CrowdinProjectFull project = ConsoleSpinner .execute(out, "message.spinner.fetching_project_info", "error.collect_project_info", false, false, () -> client.downloadFullProject(branch)); - if (Objects.equals(project.getType(), Type.STRINGS_BASED)) { + boolean isStringsBasedProject = Objects.equals(project.getType(), Type.STRINGS_BASED); + if (isStringsBasedProject) { out.println(WARNING.withIcon(RESOURCE_BUNDLE.getString("message.no_file_string_project"))); return; } diff --git a/src/main/java/com/crowdin/cli/commands/actions/FileDownloadTranslationAction.java b/src/main/java/com/crowdin/cli/commands/actions/FileDownloadTranslationAction.java index dc2e3ff5a..5d9937b94 100644 --- a/src/main/java/com/crowdin/cli/commands/actions/FileDownloadTranslationAction.java +++ b/src/main/java/com/crowdin/cli/commands/actions/FileDownloadTranslationAction.java @@ -39,7 +39,8 @@ public void act(Outputter out, ProjectProperties properties, ProjectClient clien CrowdinProjectFull project = ConsoleSpinner .execute(out, "message.spinner.fetching_project_info", "error.collect_project_info", false, false, client::downloadFullProject); - if (Objects.equals(project.getType(), Type.STRINGS_BASED)) { + boolean isStringsBasedProject = Objects.equals(project.getType(), Type.STRINGS_BASED); + if (isStringsBasedProject) { out.println(WARNING.withIcon(RESOURCE_BUNDLE.getString("message.no_file_string_project"))); return; } diff --git a/src/main/java/com/crowdin/cli/commands/actions/FileUploadAction.java b/src/main/java/com/crowdin/cli/commands/actions/FileUploadAction.java index fb9a2f9c3..e9931e479 100644 --- a/src/main/java/com/crowdin/cli/commands/actions/FileUploadAction.java +++ b/src/main/java/com/crowdin/cli/commands/actions/FileUploadAction.java @@ -4,6 +4,7 @@ import com.crowdin.cli.commands.NewAction; import com.crowdin.cli.commands.Outputter; import com.crowdin.cli.commands.functionality.*; +import com.crowdin.cli.properties.FileBean; import com.crowdin.cli.properties.ProjectProperties; import com.crowdin.cli.utils.PlaceholderUtil; import com.crowdin.cli.utils.Utils; @@ -47,6 +48,17 @@ class FileUploadAction implements NewAction { public void act(Outputter out, ProjectProperties properties, ProjectClient client) { CrowdinProjectFull project = ConsoleSpinner.execute(out, "message.spinner.fetching_project_info", "error.collect_project_info", this.plainView, this.plainView, client::downloadFullProject); + boolean isStringsBasedProject = Objects.equals(project.getType(), Type.STRINGS_BASED); + + if (!project.isManagerAccess()) { + if (!plainView) { + out.println(WARNING.withIcon(RESOURCE_BUNDLE.getString("message.no_manager_access"))); + return; + } else { + throw new RuntimeException(RESOURCE_BUNDLE.getString("message.no_manager_access")); + } + } + PlaceholderUtil placeholderUtil = new PlaceholderUtil(project.getSupportedLanguages(), project.getProjectLanguages(false), properties.getBasePath()); Optional> attachLabelIds = Optional.empty(); @@ -56,7 +68,7 @@ public void act(Outputter out, ProjectProperties properties, ProjectClient clien String fileFullPath = file.getPath(); String fileDestName = file.getName(); - if (Objects.equals(Type.FILES_BASED, project.getType())) { + if (!isStringsBasedProject) { String commonPath = SourcesUtils.getCommonPath(Collections.singletonList(this.file.getAbsolutePath()), properties.getBasePath()); final String filePath = (nonNull(dest)) ? PropertiesBeanUtils.prepareDest(dest, StringUtils.removeStart(file.getAbsolutePath(), properties.getBasePath()), placeholderUtil) @@ -110,7 +122,7 @@ public void act(Outputter out, ProjectProperties properties, ProjectClient clien Optional branch = Optional.empty(); if (StringUtils.isNotEmpty(branchName)) { branch = Optional.ofNullable(BranchUtils.getOrCreateBranch(out, branchName, client, project, plainView)); - } else if (Objects.equals(Type.STRINGS_BASED, project.getType())) { + } else if (isStringsBasedProject) { throw new RuntimeException(RESOURCE_BUNDLE.getString("error.branch_required_string_project")); } @@ -121,7 +133,7 @@ public void act(Outputter out, ProjectProperties properties, ProjectClient clien excludedLanguageNames = Optional.of(filterExcludedLanguages(excludedLanguages, project)); } - if (Objects.equals(Type.FILES_BASED, project.getType())) { + if (!isStringsBasedProject) { AddFileRequest request = new AddFileRequest(); request.setName(fileDestName); request.setStorageId(storageId); @@ -141,7 +153,7 @@ public void act(Outputter out, ProjectProperties properties, ProjectClient clien } } - if (Objects.equals(Type.STRINGS_BASED, project.getType())) { + if (isStringsBasedProject) { UploadStringsRequest request = new UploadStringsRequest(); request.setBranchId(branch.orElseThrow(() -> new RuntimeException(RESOURCE_BUNDLE.getString("error.branch_required_string_project"))).getId()); request.setCleanupMode(cleanupMode); diff --git a/src/main/java/com/crowdin/cli/commands/actions/FileUploadTranslationAction.java b/src/main/java/com/crowdin/cli/commands/actions/FileUploadTranslationAction.java index 5dd5b7e43..1f56ec0ac 100644 --- a/src/main/java/com/crowdin/cli/commands/actions/FileUploadTranslationAction.java +++ b/src/main/java/com/crowdin/cli/commands/actions/FileUploadTranslationAction.java @@ -39,12 +39,22 @@ public class FileUploadTranslationAction implements NewAction client.downloadFullProject(branchName)); + boolean isStringsBasedProject = Objects.equals(project.getType(), Type.STRINGS_BASED); + + if (!project.isManagerAccess()) { + if (!plainView) { + out.println(WARNING.withIcon(RESOURCE_BUNDLE.getString("message.no_manager_access"))); + return; + } else { + throw new RuntimeException(RESOURCE_BUNDLE.getString("message.no_manager_access")); + } + } if (!project.findLanguageById(languageId, true).isPresent()) { throw new RuntimeException(String.format(RESOURCE_BUNDLE.getString("error.language_not_exist"), languageId)); } - if (Objects.equals(Type.FILES_BASED, project.getType())) { + if (!isStringsBasedProject) { if (Objects.isNull(dest)) throw new RuntimeException(String.format(RESOURCE_BUNDLE.getString("error.file.dest_required"), languageId)); String sourcePath = Utils.toUnixPath(Utils.sepAtStart(dest)); @@ -63,7 +73,7 @@ public void act(Outputter out, ProjectProperties properties, ProjectClient clien throw new RuntimeException(String.format( RESOURCE_BUNDLE.getString("error.upload_translation"), file.getPath()), e); } - } else if (Objects.equals(Type.STRINGS_BASED, project.getType())) { + } else { UploadTranslationsStringsRequest request = new UploadTranslationsStringsRequest(); Branch branch = project.findBranchByName(branchName) .orElseThrow(() -> new RuntimeException(RESOURCE_BUNDLE.getString("error.branch_required_string_project"))); diff --git a/src/main/java/com/crowdin/cli/commands/actions/PreTranslateAction.java b/src/main/java/com/crowdin/cli/commands/actions/PreTranslateAction.java index 8af7f492f..2e99ff9df 100644 --- a/src/main/java/com/crowdin/cli/commands/actions/PreTranslateAction.java +++ b/src/main/java/com/crowdin/cli/commands/actions/PreTranslateAction.java @@ -6,28 +6,21 @@ import com.crowdin.cli.client.ProjectClient; import com.crowdin.cli.commands.NewAction; import com.crowdin.cli.commands.Outputter; -import com.crowdin.cli.commands.functionality.ProjectFilesUtils; -import com.crowdin.cli.commands.functionality.PropertiesBeanUtils; -import com.crowdin.cli.commands.functionality.RequestBuilder; -import com.crowdin.cli.commands.functionality.SourcesUtils; +import com.crowdin.cli.commands.functionality.*; import com.crowdin.cli.properties.PropertiesWithFiles; import com.crowdin.cli.utils.PlaceholderUtil; import com.crowdin.cli.utils.Utils; import com.crowdin.cli.utils.console.ConsoleSpinner; import com.crowdin.client.labels.model.Label; import com.crowdin.client.languages.model.Language; +import com.crowdin.client.projectsgroups.model.Type; +import com.crowdin.client.sourcefiles.model.Branch; import com.crowdin.client.sourcefiles.model.FileInfo; -import com.crowdin.client.translations.model.ApplyPreTranslationRequest; -import com.crowdin.client.translations.model.AutoApproveOption; -import com.crowdin.client.translations.model.Method; -import com.crowdin.client.translations.model.PreTranslationStatus; +import com.crowdin.client.translations.model.*; import org.apache.commons.lang3.StringUtils; import java.io.File; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Objects; +import java.util.*; import java.util.stream.Collectors; import static com.crowdin.cli.BaseCli.RESOURCE_BUNDLE; @@ -72,20 +65,28 @@ public PreTranslateAction( public void act(Outputter out, PropertiesWithFiles properties, ProjectClient client) { CrowdinProjectFull project = ConsoleSpinner.execute(out, "message.spinner.fetching_project_info", "error.collect_project_info", this.noProgress, this.plainView, () -> client.downloadFullProject(this.branchName)); + boolean isStringsBasedProject = Objects.equals(project.getType(), Type.STRINGS_BASED); List languages = this.prepareLanguageIds(project); - List fileIds = this.prepareFileIds(out, properties, project); List labelIds = this.prepareLabelIds(out, client); - if (fileIds == null || fileIds.isEmpty()) { - throw new RuntimeException(String.format(RESOURCE_BUNDLE.getString("error.no_files_found_for_pre_translate"))); + if (!isStringsBasedProject) { + List fileIds = this.prepareFileIds(out, properties, project); + if (fileIds == null || fileIds.isEmpty()) { + throw new RuntimeException(String.format(RESOURCE_BUNDLE.getString("error.no_files_found_for_pre_translate"))); + } + ApplyPreTranslationRequest request = RequestBuilder.applyPreTranslation( + languages, fileIds, method, engineId, autoApproveOption, + duplicateTranslations, translateUntranslatedOnly, translateWithPerfectMatchOnly, labelIds); + this.applyPreTranslation(out, client, request); + } else { + Branch branch = project.findBranchByName(branchName) + .orElseThrow(() -> new RuntimeException(RESOURCE_BUNDLE.getString("error.branch_required_string_project"))); + ApplyPreTranslationStringsBasedRequest request = RequestBuilder.applyPreTranslationStringsBased( + languages, Collections.singletonList(branch.getId()), method, engineId, autoApproveOption, + duplicateTranslations, translateUntranslatedOnly, translateWithPerfectMatchOnly, labelIds); + this.applyPreTranslationStringsBased(out, client, request); } - - ApplyPreTranslationRequest request = RequestBuilder.applyPreTranslation( - languages, fileIds, method, engineId, autoApproveOption, - duplicateTranslations, translateUntranslatedOnly, translateWithPerfectMatchOnly, labelIds); - - this.applyPreTranslation(out, client, request); } private List prepareLanguageIds(CrowdinProjectInfo projectInfo) { @@ -176,19 +177,25 @@ private void applyPreTranslation(Outputter out, ProjectClient client, ApplyPreTr this.plainView, () -> { PreTranslationStatus preTranslationStatus = client.startPreTranslation(request); + preTranslationStatus = handlePreTranslationStatus(client, preTranslationStatus); - while (!preTranslationStatus.getStatus().equalsIgnoreCase("finished")) { - ConsoleSpinner.update( - String.format(RESOURCE_BUNDLE.getString("message.spinner.pre_translate_percents"), - Math.toIntExact(preTranslationStatus.getProgress()))); - Thread.sleep(1000); + ConsoleSpinner.update(String.format(RESOURCE_BUNDLE.getString("message.spinner.pre_translate_done"), 100)); - preTranslationStatus = client.checkPreTranslation(preTranslationStatus.getIdentifier()); + return preTranslationStatus; + } + ); + } - if (preTranslationStatus.getStatus().equalsIgnoreCase("failed")) { - throw new RuntimeException(); - } - } + private void applyPreTranslationStringsBased(Outputter out, ProjectClient client, ApplyPreTranslationStringsBasedRequest request) { + ConsoleSpinner.execute( + out, + "message.spinner.pre_translate", + "error.spinner.pre_translate", + this.noProgress, + this.plainView, + () -> { + PreTranslationStatus preTranslationStatus = client.startPreTranslationStringsBased(request); + preTranslationStatus = handlePreTranslationStatus(client, preTranslationStatus); ConsoleSpinner.update(String.format(RESOURCE_BUNDLE.getString("message.spinner.pre_translate_done"), 100)); @@ -196,4 +203,20 @@ private void applyPreTranslation(Outputter out, ProjectClient client, ApplyPreTr } ); } + + private PreTranslationStatus handlePreTranslationStatus(ProjectClient client, PreTranslationStatus preTranslationStatus) throws InterruptedException { + while (!preTranslationStatus.getStatus().equalsIgnoreCase("finished")) { + ConsoleSpinner.update( + String.format(RESOURCE_BUNDLE.getString("message.spinner.pre_translate_percents"), + Math.toIntExact(preTranslationStatus.getProgress()))); + Thread.sleep(1000); + + preTranslationStatus = client.checkPreTranslation(preTranslationStatus.getIdentifier()); + + if (preTranslationStatus.getStatus().equalsIgnoreCase("failed")) { + throw new RuntimeException(); + } + } + return preTranslationStatus; + } } diff --git a/src/main/java/com/crowdin/cli/commands/actions/StringAddAction.java b/src/main/java/com/crowdin/cli/commands/actions/StringAddAction.java index bf203459d..11c890fbe 100644 --- a/src/main/java/com/crowdin/cli/commands/actions/StringAddAction.java +++ b/src/main/java/com/crowdin/cli/commands/actions/StringAddAction.java @@ -4,16 +4,21 @@ import com.crowdin.cli.client.ProjectClient; import com.crowdin.cli.commands.NewAction; import com.crowdin.cli.commands.Outputter; +import com.crowdin.cli.commands.functionality.BranchUtils; import com.crowdin.cli.commands.functionality.ProjectFilesUtils; import com.crowdin.cli.commands.functionality.RequestBuilder; import com.crowdin.cli.properties.ProjectProperties; import com.crowdin.cli.utils.console.ConsoleSpinner; import com.crowdin.client.labels.model.Label; +import com.crowdin.client.projectsgroups.model.Type; +import com.crowdin.client.sourcefiles.model.Branch; import com.crowdin.client.sourcefiles.model.FileInfo; import com.crowdin.client.sourcestrings.model.AddSourceStringRequest; +import com.crowdin.client.sourcestrings.model.AddSourceStringStringsBasedRequest; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.stream.Collectors; import static com.crowdin.cli.BaseCli.RESOURCE_BUNDLE; @@ -29,10 +34,11 @@ class StringAddAction implements NewAction { private final String context; private final List files; private final List labelNames; + private final String branchName; private final Boolean hidden; public StringAddAction( - boolean noProgress, String text, String identifier, Integer maxLength, String context, List files, List labelNames, Boolean hidden + boolean noProgress, String text, String identifier, Integer maxLength, String context, List files, List labelNames, String branchName, Boolean hidden ) { this.noProgress = noProgress; this.text = text; @@ -41,6 +47,7 @@ public StringAddAction( this.context = context; this.files = files; this.labelNames = labelNames; + this.branchName = branchName; this.hidden = hidden; } @@ -48,14 +55,27 @@ public StringAddAction( public void act(Outputter out, ProjectProperties pb, ProjectClient client) { CrowdinProjectFull project = ConsoleSpinner.execute(out, "message.spinner.fetching_project_info", "error.collect_project_info", this.noProgress, false, client::downloadFullProject); + boolean isStringsBasedProject = Objects.equals(project.getType(), Type.STRINGS_BASED); List labelIds = (labelNames != null && !labelNames.isEmpty()) ? this.prepareLabelIds(client) : null; if (files == null || files.isEmpty()) { - AddSourceStringRequest request = RequestBuilder.addString(this.text, this.identifier, this.maxLength, this.context, null, this.hidden, labelIds); - client.addSourceString(request); + if (isStringsBasedProject) { + Branch branch = BranchUtils.getOrCreateBranch(out, branchName, client, project, false); + if (Objects.isNull(branch)) { + throw new RuntimeException(RESOURCE_BUNDLE.getString("error.branch_required_string_project")); + } + AddSourceStringStringsBasedRequest request = RequestBuilder.addStringStringsBased(this.text, this.identifier, this.maxLength, this.context, branch.getId(), this.hidden, labelIds); + client.addSourceStringStringsBased(request); + } else { + AddSourceStringRequest request = RequestBuilder.addString(this.text, this.identifier, this.maxLength, this.context, null, this.hidden, labelIds); + client.addSourceString(request); + } out.println(OK.withIcon(RESOURCE_BUNDLE.getString("message.source_string_uploaded"))); } else { + if (isStringsBasedProject) { + throw new RuntimeException(RESOURCE_BUNDLE.getString("message.no_file_string_project")); + } Map paths = ProjectFilesUtils.buildFilePaths(project.getDirectories(), project.getBranches(), project.getFileInfos()); boolean containsError = false; for (String file : files) { @@ -79,7 +99,6 @@ public void act(Outputter out, ProjectProperties pb, ProjectClient client) { throw new RuntimeException(); } } - } private List prepareLabelIds(ProjectClient client) { diff --git a/src/main/java/com/crowdin/cli/commands/actions/StringListAction.java b/src/main/java/com/crowdin/cli/commands/actions/StringListAction.java index 1497756d7..b78b66ba4 100644 --- a/src/main/java/com/crowdin/cli/commands/actions/StringListAction.java +++ b/src/main/java/com/crowdin/cli/commands/actions/StringListAction.java @@ -9,6 +9,7 @@ import com.crowdin.cli.utils.Utils; import com.crowdin.cli.utils.console.ConsoleSpinner; import com.crowdin.client.labels.model.Label; +import com.crowdin.client.projectsgroups.model.Type; import com.crowdin.client.sourcefiles.model.Branch; import com.crowdin.client.sourcefiles.model.FileInfo; import com.crowdin.client.sourcestrings.model.SourceString; @@ -16,6 +17,7 @@ import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.stream.Collectors; @@ -44,6 +46,7 @@ public StringListAction(boolean noProgress, boolean isVerbose, String file, Stri public void act(Outputter out, ProjectProperties pb, ProjectClient client) { CrowdinProjectFull project = ConsoleSpinner.execute(out, "message.spinner.fetching_project_info", "error.collect_project_info", this.noProgress, false, () -> client.downloadFullProject(this.branchName)); + boolean isStringsBasedProject = Objects.equals(project.getType(), Type.STRINGS_BASED); Long branchId = Optional.ofNullable(project.getBranch()) .map(Branch::getId) @@ -52,10 +55,15 @@ public void act(Outputter out, ProjectProperties pb, ProjectClient client) { Map labels = client.listLabels().stream() .collect(Collectors.toMap(Label::getId, Label::getTitle)); - Map paths = ProjectFilesUtils.buildFilePaths(project.getDirectories(), project.getBranches(), project.getFileInfos()); - Map reversePaths = paths.entrySet() - .stream() - .collect(Collectors.toMap((entry) -> entry.getValue().getId(), Map.Entry::getKey)); + Map paths = null; + Map reversePaths = null; + if (!isStringsBasedProject) { + paths = ProjectFilesUtils.buildFilePaths(project.getDirectories(), project.getBranches(), project.getFileInfos()); + reversePaths = paths.entrySet() + .stream() + .collect(Collectors.toMap((entry) -> entry.getValue().getId(), Map.Entry::getKey)); + } + Map finalReversePaths = reversePaths; String encodedFilter = filter != null ? Utils.encodeURL(filter) : null; String encodedCroql = croql != null ? Utils.encodeURL(croql) : null; @@ -64,6 +72,9 @@ public void act(Outputter out, ProjectProperties pb, ProjectClient client) { if (StringUtils.isEmpty(file)) { sourceStrings = client.listSourceString(null, branchId, null, encodedFilter, encodedCroql); } else { + if (isStringsBasedProject) { + throw new RuntimeException(RESOURCE_BUNDLE.getString("message.no_file_string_project")); + } if (paths.containsKey(file)) { sourceStrings = client.listSourceString(paths.get(file).getId(), branchId, null, encodedFilter, encodedCroql); } else { @@ -86,8 +97,8 @@ public void act(Outputter out, ProjectProperties pb, ProjectClient client) { out.println(String.format( RESOURCE_BUNDLE.getString("message.source_string_list_context"), ss.getContext().trim().replaceAll("\n", "\n\t\t"))); } - if (ss.getFileId() != null) { - out.println(String.format(RESOURCE_BUNDLE.getString("message.source_string_list_file"), reversePaths.get(ss.getFileId()))); + if (!isStringsBasedProject && (ss.getFileId() != null)) { + out.println(String.format(RESOURCE_BUNDLE.getString("message.source_string_list_file"), finalReversePaths.get(ss.getFileId()))); } if (ss.getMaxLength() != null && ss.getMaxLength() != 0) { out.println(String.format(RESOURCE_BUNDLE.getString("message.source_string_list_max_length"), ss.getMaxLength())); diff --git a/src/main/java/com/crowdin/cli/commands/actions/UploadSourcesAction.java b/src/main/java/com/crowdin/cli/commands/actions/UploadSourcesAction.java index cd31356c6..4f741a4ff 100644 --- a/src/main/java/com/crowdin/cli/commands/actions/UploadSourcesAction.java +++ b/src/main/java/com/crowdin/cli/commands/actions/UploadSourcesAction.java @@ -1,10 +1,6 @@ package com.crowdin.cli.commands.actions; -import com.crowdin.cli.client.CrowdinProjectFull; -import com.crowdin.cli.client.EmptyFileException; -import com.crowdin.cli.client.ExistsResponseException; -import com.crowdin.cli.client.FileInUpdateException; -import com.crowdin.cli.client.ProjectClient; +import com.crowdin.cli.client.*; import com.crowdin.cli.commands.NewAction; import com.crowdin.cli.commands.Outputter; import com.crowdin.cli.commands.actions.subactions.DeleteObsoleteProjectFilesSubAction; @@ -15,23 +11,12 @@ import com.crowdin.cli.utils.Utils; import com.crowdin.cli.utils.concurrency.ConcurrencyUtil; import com.crowdin.cli.utils.console.ConsoleSpinner; -import com.crowdin.cli.utils.console.ExecutionStatus; import com.crowdin.client.core.model.PatchRequest; import com.crowdin.client.labels.model.Label; import com.crowdin.client.languages.model.Language; -import com.crowdin.client.sourcefiles.model.AddBranchRequest; -import com.crowdin.client.sourcefiles.model.AddFileRequest; -import com.crowdin.client.sourcefiles.model.Branch; -import com.crowdin.client.sourcefiles.model.ExportOptions; -import com.crowdin.client.sourcefiles.model.FileInfo; -import com.crowdin.client.sourcefiles.model.GeneralFileExportOptions; -import com.crowdin.client.sourcefiles.model.ImportOptions; -import com.crowdin.client.sourcefiles.model.OtherFileImportOptions; -import com.crowdin.client.sourcefiles.model.PropertyFileExportOptions; -import com.crowdin.client.sourcefiles.model.SpreadsheetFileImportOptions; -import com.crowdin.client.sourcefiles.model.JavaScriptFileExportOptions; -import com.crowdin.client.sourcefiles.model.UpdateFileRequest; -import com.crowdin.client.sourcefiles.model.XmlFileImportOptions; +import com.crowdin.client.projectsgroups.model.Type; +import com.crowdin.client.sourcefiles.model.*; +import com.crowdin.client.sourcestrings.model.UploadStringsRequest; import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang3.StringUtils; @@ -42,16 +27,13 @@ import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.Optional; import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; import static com.crowdin.cli.BaseCli.RESOURCE_BUNDLE; -import static com.crowdin.cli.utils.console.ExecutionStatus.OK; -import static com.crowdin.cli.utils.console.ExecutionStatus.SKIPPED; -import static com.crowdin.cli.utils.console.ExecutionStatus.WARNING; -import static com.crowdin.client.sourcefiles.model.ExportQuotes.SINGLE; +import static com.crowdin.cli.utils.console.ExecutionStatus.*; import static com.crowdin.client.sourcefiles.model.ExportQuotes.DOUBLE; +import static com.crowdin.client.sourcefiles.model.ExportQuotes.SINGLE; class UploadSourcesAction implements NewAction { @@ -75,6 +57,7 @@ public UploadSourcesAction(String branchName, boolean deleteObsolete, boolean no public void act(Outputter out, PropertiesWithFiles pb, ProjectClient client) { CrowdinProjectFull project = ConsoleSpinner.execute(out, "message.spinner.fetching_project_info", "error.collect_project_info", this.noProgress, this.plainView, client::downloadFullProject); + boolean isStringsBasedProject = Objects.equals(project.getType(), Type.STRINGS_BASED); boolean containsExcludedLanguages = pb.getFiles().stream() .map(FileBean::getExcludedTargetLanguages).filter(Objects::nonNull).anyMatch(l -> !l.isEmpty()); @@ -92,18 +75,25 @@ public void act(Outputter out, PropertiesWithFiles pb, ProjectClient client) { Branch branch = (branchName != null) ? BranchUtils.getOrCreateBranch(out, branchName, client, project, plainView) : null; Long branchId = (branch != null) ? branch.getId() : null; - Map directoryPaths = ProjectFilesUtils.buildDirectoryPaths(project.getDirectories(), project.getBranches()) - .entrySet().stream().collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey)); - Map paths = ProjectFilesUtils.buildFilePaths(project.getDirectories(), project.getBranches(), project.getFileInfos()); - + Map directoryPaths = null; + Map paths = null; DeleteObsoleteProjectFilesSubAction deleteObsoleteProjectFilesSubAction = new DeleteObsoleteProjectFilesSubAction(out, client); - if (deleteObsolete) { - Map directories = ProjectFilesUtils.buildDirectoryPaths(project.getDirectories(branchId)) + + if (!isStringsBasedProject) { + directoryPaths = ProjectFilesUtils.buildDirectoryPaths(project.getDirectories(), project.getBranches()) .entrySet().stream().collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey)); - Map projectFiles = ProjectFilesUtils.buildFilePaths(project.getDirectories(branchId), project.getFiles(branchId)); - deleteObsoleteProjectFilesSubAction.setData(projectFiles, directories, pb.getPreserveHierarchy(), this.plainView); + paths = ProjectFilesUtils.buildFilePaths(project.getDirectories(), project.getBranches(), project.getFileInfos()); + if (deleteObsolete) { + Map directories = ProjectFilesUtils.buildDirectoryPaths(project.getDirectories(branchId)) + .entrySet().stream().collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey)); + Map projectFiles = ProjectFilesUtils.buildFilePaths(project.getDirectories(branchId), project.getFiles(branchId)); + deleteObsoleteProjectFilesSubAction.setData(projectFiles, directories, pb.getPreserveHierarchy(), this.plainView); + } } + Map finalDirectoryPaths = directoryPaths; + Map finalPaths = paths; + List uploadedSources = new ArrayList<>(); Map labels = client.listLabels().stream() @@ -185,8 +175,8 @@ public void act(Outputter out, PropertiesWithFiles pb, ProjectClient client) { uploadedSources.add(fileFullPath); } - FileInfo projectFile = paths.get(fileFullPath); - if (autoUpdate && projectFile != null) { + FileInfo projectFile = !isStringsBasedProject ? finalPaths.get(fileFullPath) : null; + if (!isStringsBasedProject && autoUpdate && projectFile != null) { final UpdateFileRequest request = new UpdateFileRequest(); request.setExportOptions(buildExportOptions(sourceFile, file, pb.getBasePath())); request.setImportOptions(buildImportOptions(sourceFile, file, srxStorageId)); @@ -235,7 +225,7 @@ public void act(Outputter out, PropertiesWithFiles pb, ProjectClient client) { throw new RuntimeException(String.format(RESOURCE_BUNDLE.getString("error.uploading_file"), fileFullPath), e); } }; - } else if (projectFile == null) { + } else if (projectFile == null && !isStringsBasedProject) { final AddFileRequest request = new AddFileRequest(); request.setName(fileName); request.setExportOptions(buildExportOptions(sourceFile, file, pb.getBasePath())); @@ -254,7 +244,7 @@ public void act(Outputter out, PropertiesWithFiles pb, ProjectClient client) { return (Runnable) () -> { Long directoryId; try { - directoryId = ProjectUtils.createPath(out, client, directoryPaths, filePath, branch, plainView); + directoryId = ProjectUtils.createPath(out, client, finalDirectoryPaths, filePath, branch, plainView); } catch (Exception e) { errorsPresented.set(true); throw new RuntimeException(RESOURCE_BUNDLE.getString("error.creating_directories"), e); @@ -268,7 +258,7 @@ public void act(Outputter out, PropertiesWithFiles pb, ProjectClient client) { try (InputStream fileStream = new FileInputStream(sourceFile)) { request.setStorageId(client.uploadStorage(source.substring(source.lastIndexOf(Utils.PATH_SEPARATOR) + 1), fileStream)); - } catch (EmptyFileException e){ + } catch (EmptyFileException e) { errorsPresented.set(false); out.println(SKIPPED.withIcon(String.format(RESOURCE_BUNDLE.getString("message.uploading_file_skipped"), fileFullPath))); return; @@ -292,6 +282,47 @@ public void act(Outputter out, PropertiesWithFiles pb, ProjectClient client) { out.println(fileFullPath); } }; + } else if (isStringsBasedProject) { + final UploadStringsRequest request = new UploadStringsRequest(); + request.setImportOptions(buildImportOptionsStringsBased(sourceFile, file, srxStorageId)); + if (file.getType() != null) { + request.setType(file.getType()); + } + if (file.getLabels() != null) { + List labelsIds = file.getLabels().stream().map(labels::get) + .collect(Collectors.toList()); + request.setLabelIds(labelsIds); + } + + return (Runnable) () -> { + if (branch == null) { + throw new RuntimeException(RESOURCE_BUNDLE.getString("error.branch_required_string_project")); + } + request.setBranchId(branch.getId()); + + try (InputStream fileStream = new FileInputStream(sourceFile)) { + request.setStorageId(client.uploadStorage(source.substring(source.lastIndexOf(Utils.PATH_SEPARATOR) + 1), fileStream)); + } catch (EmptyFileException e) { + errorsPresented.set(false); + out.println(SKIPPED.withIcon(String.format(RESOURCE_BUNDLE.getString("message.uploading_file_skipped"), fileFullPath))); + return; + } catch (Exception e) { + errorsPresented.set(true); + throw new RuntimeException( + String.format(RESOURCE_BUNDLE.getString("error.upload_to_storage"), sourceFile.getAbsolutePath()), e); + } + try { + client.addSourceStringsBased(request); + } catch (Exception e) { + errorsPresented.set(true); + throw new RuntimeException(String.format(RESOURCE_BUNDLE.getString("error.uploading_file"), fileFullPath), e); + } + if (!plainView) { + out.println(OK.withIcon(String.format(RESOURCE_BUNDLE.getString("message.uploading_file"), fileFullPath))); + } else { + out.println(fileFullPath); + } + }; } else { return (Runnable) () -> { if (!plainView) { @@ -338,6 +369,16 @@ private ImportOptions buildImportOptions(java.io.File sourceFile, FileBean fileB } } + private com.crowdin.client.sourcestrings.model.ImportOptions buildImportOptionsStringsBased(java.io.File sourceFile, FileBean fileBean, Long srxStorageId) { + com.crowdin.client.sourcestrings.model.ImportOptions importOptions = new com.crowdin.client.sourcestrings.model.ImportOptions(); + if (isSpreadsheet(sourceFile, fileBean)) { + importOptions.setFirstLineContainsHeader(fileBean.getFirstLineContainsHeader()); + importOptions.setScheme(PropertiesBeanUtils.getSchemeObject(fileBean.getScheme())); + importOptions.setImportTranslations(fileBean.getImportTranslations()); + } + return importOptions; + } + private boolean isSpreadsheet(java.io.File file, FileBean fileBean) { return (fileBean.getDest() != null) ? FilenameUtils.isExtension(fileBean.getDest(), "csv", "xls", "xlsx") diff --git a/src/main/java/com/crowdin/cli/commands/actions/UploadTranslationsAction.java b/src/main/java/com/crowdin/cli/commands/actions/UploadTranslationsAction.java index 2a40031ab..b49a35e60 100644 --- a/src/main/java/com/crowdin/cli/commands/actions/UploadTranslationsAction.java +++ b/src/main/java/com/crowdin/cli/commands/actions/UploadTranslationsAction.java @@ -18,17 +18,17 @@ import com.crowdin.cli.utils.concurrency.ConcurrencyUtil; import com.crowdin.cli.utils.console.ConsoleSpinner; import com.crowdin.client.languages.model.Language; +import com.crowdin.client.projectsgroups.model.Type; +import com.crowdin.client.sourcefiles.model.Branch; import com.crowdin.client.sourcefiles.model.File; import com.crowdin.client.translations.model.UploadTranslationsRequest; +import com.crowdin.client.translations.model.UploadTranslationsStringsRequest; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; import java.io.FileInputStream; import java.io.InputStream; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; @@ -67,6 +67,7 @@ public UploadTranslationsAction( public void act(Outputter out, PropertiesWithFiles pb, ProjectClient client) { CrowdinProjectFull project = ConsoleSpinner.execute(out, "message.spinner.fetching_project_info", "error.collect_project_info", this.noProgress, this.plainView, () -> client.downloadFullProject(this.branchName)); + boolean isStringsBasedProject = Objects.equals(project.getType(), Type.STRINGS_BASED); if (!project.isManagerAccess()) { if (!plainView) { @@ -81,8 +82,6 @@ public void act(Outputter out, PropertiesWithFiles pb, ProjectClient client) { LanguageMapping serverLanguageMapping = project.getLanguageMapping(); - Map paths = ProjectFilesUtils.buildFilePaths(project.getDirectories(), project.getBranches(), project.getFiles()); - List languages = (languageId != null) ? project.findLanguageById(languageId, true) .map(Collections::singletonList) @@ -105,108 +104,131 @@ public void act(Outputter out, PropertiesWithFiles pb, ProjectClient client) { } continue; } - - Map, UploadTranslationsRequest>> preparedRequests = new HashMap<>(); - String branchPath = (StringUtils.isNotEmpty(this.branchName) ? branchName + Utils.PATH_SEPARATOR : ""); + List tasks = null; AtomicBoolean containsErrors = new AtomicBoolean(false); - fileSourcesWithoutIgnores.forEach(source -> { - String filePath = branchPath + (StringUtils.isNotEmpty(file.getDest()) - ? PropertiesBeanUtils.prepareDest(file.getDest(), StringUtils.removeStart(source, pb.getBasePath()), placeholderUtil) - : StringUtils.removeStart(source, pb.getBasePath() + commonPath)); - - if (!paths.containsKey(filePath)) { - containsErrors.set(true); - if (!plainView) { - out.println(ERROR.withIcon(String.format( - RESOURCE_BUNDLE.getString("error.source_not_exists_in_project"), - StringUtils.removeStart(source, pb.getBasePath()), filePath))); - } - return; - } - Long fileId = paths.get(filePath).getId(); + if (!isStringsBasedProject) { + Map paths = ProjectFilesUtils.buildFilePaths(project.getDirectories(), project.getBranches(), project.getFiles()); + Map, UploadTranslationsRequest>> preparedRequests = new HashMap<>(); + String branchPath = (StringUtils.isNotEmpty(this.branchName) ? branchName + Utils.PATH_SEPARATOR : ""); + fileSourcesWithoutIgnores.forEach(source -> { + String filePath = branchPath + (StringUtils.isNotEmpty(file.getDest()) + ? PropertiesBeanUtils.prepareDest(file.getDest(), StringUtils.removeStart(source, pb.getBasePath()), placeholderUtil) + : StringUtils.removeStart(source, pb.getBasePath() + commonPath)); -// build filePath to each source and project language - String fileSource = StringUtils.removeStart(source, pb.getBasePath()); - String translation = TranslationsUtils.replaceDoubleAsterisk(file.getSource(), file.getTranslation(), fileSource); - translation = placeholderUtil.replaceFileDependentPlaceholders(translation, new java.io.File(source)); - if (file.getScheme() != null && !PlaceholderUtil.containsLangPlaceholders(translation)) { - java.io.File transFile = new java.io.File(pb.getBasePath() + Utils.PATH_SEPARATOR + translation); - if (!transFile.exists()) { + if (!paths.containsKey(filePath)) { + containsErrors.set(true); if (!plainView) { - out.println(SKIPPED.withIcon(String.format( - RESOURCE_BUNDLE.getString("error.translation_not_exists"), - StringUtils.removeStart(transFile.getAbsolutePath(), pb.getBasePath())))); + out.println(ERROR.withIcon(String.format( + RESOURCE_BUNDLE.getString("error.source_not_exists_in_project"), + StringUtils.removeStart(source, pb.getBasePath()), filePath))); } return; } - UploadTranslationsRequest request = RequestBuilder.uploadTranslations(fileId, importEqSuggestions, autoApproveImported, translateHidden); - preparedRequests.put(transFile, Pair.of(languages, request)); - } else { - for (Language language : languages) { - LanguageMapping localLanguageMapping = - LanguageMapping.fromConfigFileLanguageMapping(file.getLanguagesMapping()); - LanguageMapping languageMapping = - LanguageMapping.populate(localLanguageMapping, serverLanguageMapping); - - String transFileName = placeholderUtil.replaceLanguageDependentPlaceholders(translation, languageMapping, language); - transFileName = PropertiesBeanUtils.useTranslationReplace(transFileName, file.getTranslationReplace()); - java.io.File transFile = new java.io.File(pb.getBasePath() + Utils.PATH_SEPARATOR + transFileName); - if (!transFile.exists()) { - if (!plainView) { - out.println(SKIPPED.withIcon(String.format( - RESOURCE_BUNDLE.getString("error.translation_not_exists"), - StringUtils.removeStart(transFile.getAbsolutePath(), pb.getBasePath())))); - } - continue; - } + Long fileId = paths.get(filePath).getId(); + +// build filePath to each source and project language + String fileSource = StringUtils.removeStart(source, pb.getBasePath()); + String translation = TranslationsUtils.replaceDoubleAsterisk(file.getSource(), file.getTranslation(), fileSource); + translation = placeholderUtil.replaceFileDependentPlaceholders(translation, new java.io.File(source)); + if (file.getScheme() != null && !PlaceholderUtil.containsLangPlaceholders(translation)) { + java.io.File transFile = getTranslationFile(out, pb, translation); + if (Objects.isNull(transFile)) return; UploadTranslationsRequest request = RequestBuilder.uploadTranslations(fileId, importEqSuggestions, autoApproveImported, translateHidden); - preparedRequests.put(transFile, Pair.of(Collections.singletonList(language), request)); - } - } - }); - - List tasks = preparedRequests.entrySet() - .stream() - .map(entry -> (Runnable) () -> { - java.io.File translationFile = entry.getKey(); - List langs = entry.getValue().getLeft(); - UploadTranslationsRequest request = entry.getValue().getRight(); - try (InputStream fileStream = new FileInputStream(translationFile)) { - Long storageId = client.uploadStorage(translationFile.getName(), fileStream); - request.setStorageId(storageId); - } catch (Exception e) { - containsErrors.set(true); - throw new RuntimeException(String.format( - RESOURCE_BUNDLE.getString("error.upload_translation_to_storage"), - StringUtils.removeStart(translationFile.getAbsolutePath(), pb.getBasePath()) - ), e); + preparedRequests.put(transFile, Pair.of(languages, request)); + } else { + for (Language language : languages) { + java.io.File transFile = getTranslationFileWithPlaceholders(out, pb, placeholderUtil, serverLanguageMapping, file, translation, language); + if (Objects.isNull(transFile)) continue; + UploadTranslationsRequest request = RequestBuilder.uploadTranslations(fileId, importEqSuggestions, autoApproveImported, translateHidden); + preparedRequests.put(transFile, Pair.of(Collections.singletonList(language), request)); + } } - try { - for (Language lang : langs) { - try { - client.uploadTranslations(lang.getId(), request); - } catch (WrongLanguageException e) { - out.println(WARNING.withIcon(String.format( - RESOURCE_BUNDLE.getString("message.warning.file_not_uploaded_cause_of_language"), - StringUtils.removeStart(translationFile.getAbsolutePath(), pb.getBasePath()), lang.getName()))); + }); + + tasks = preparedRequests.entrySet() + .stream() + .map(entry -> (Runnable) () -> { + java.io.File translationFile = entry.getKey(); + List langs = entry.getValue().getLeft(); + UploadTranslationsRequest request = entry.getValue().getRight(); + request.setStorageId(uploadToStorage(pb, client, containsErrors, translationFile)); + try { + for (Language lang : langs) { + try { + client.uploadTranslations(lang.getId(), request); + } catch (WrongLanguageException e) { + out.println(WARNING.withIcon(String.format( + RESOURCE_BUNDLE.getString("message.warning.file_not_uploaded_cause_of_language"), + StringUtils.removeStart(translationFile.getAbsolutePath(), pb.getBasePath()), lang.getName()))); + } } + } catch (Exception e) { + containsErrors.set(true); + throw new RuntimeException(String.format( + RESOURCE_BUNDLE.getString("error.upload_translation"), + StringUtils.removeStart(translationFile.getAbsolutePath(), pb.getBasePath()) + ), e); } - } catch (Exception e) { - containsErrors.set(true); - throw new RuntimeException(String.format( - RESOURCE_BUNDLE.getString("error.upload_translation"), - StringUtils.removeStart(translationFile.getAbsolutePath(), pb.getBasePath()) - ), e); - } - if (!plainView) { - out.println(OK.withIcon(String.format( - RESOURCE_BUNDLE.getString("message.translation_uploaded"), - StringUtils.removeStart(translationFile.getAbsolutePath(), pb.getBasePath())))); + if (!plainView) { + out.println(OK.withIcon(String.format( + RESOURCE_BUNDLE.getString("message.translation_uploaded"), + StringUtils.removeStart(translationFile.getAbsolutePath(), pb.getBasePath())))); + } else { + out.println(StringUtils.removeStart(translationFile.getAbsolutePath(), pb.getBasePath())); + } + }) + .collect(Collectors.toList()); + } else { + Map, UploadTranslationsStringsRequest>> preparedRequests = new HashMap<>(); + Branch branch = project.findBranchByName(branchName) + .orElseThrow(() -> new RuntimeException(RESOURCE_BUNDLE.getString("error.branch_required_string_project"))); + fileSourcesWithoutIgnores.forEach(source -> { + String fileSource = StringUtils.removeStart(source, pb.getBasePath()); + String translation = TranslationsUtils.replaceDoubleAsterisk(file.getSource(), file.getTranslation(), fileSource); + translation = placeholderUtil.replaceFileDependentPlaceholders(translation, new java.io.File(source)); + if (file.getScheme() != null && !PlaceholderUtil.containsLangPlaceholders(translation)) { + java.io.File transFile = getTranslationFile(out, pb, translation); + if (Objects.isNull(transFile)) return; + UploadTranslationsStringsRequest request = RequestBuilder.uploadTranslationsStrings(branch.getId(), importEqSuggestions, autoApproveImported, translateHidden); + preparedRequests.put(transFile, Pair.of(languages, request)); } else { - out.println(StringUtils.removeStart(translationFile.getAbsolutePath(), pb.getBasePath())); + for (Language language : languages) { + java.io.File transFile = getTranslationFileWithPlaceholders(out, pb, placeholderUtil, serverLanguageMapping, file, translation, language); + if (Objects.isNull(transFile)) continue; + UploadTranslationsStringsRequest request = RequestBuilder.uploadTranslationsStrings(branch.getId(), importEqSuggestions, autoApproveImported, translateHidden); + preparedRequests.put(transFile, Pair.of(Collections.singletonList(language), request)); + } } - }) - .collect(Collectors.toList()); + + }); + tasks = preparedRequests.entrySet() + .stream() + .map(entry -> (Runnable) () -> { + java.io.File translationFile = entry.getKey(); + List langs = entry.getValue().getLeft(); + UploadTranslationsStringsRequest request = entry.getValue().getRight(); + request.setStorageId(uploadToStorage(pb, client, containsErrors, translationFile)); + try { + for (Language lang : langs) { + client.uploadTranslationStringsBased(lang.getId(), request); + } + } catch (Exception e) { + containsErrors.set(true); + throw new RuntimeException(String.format( + RESOURCE_BUNDLE.getString("error.upload_translation"), + StringUtils.removeStart(translationFile.getAbsolutePath(), pb.getBasePath()) + ), e); + } + if (!plainView) { + out.println(OK.withIcon(String.format( + RESOURCE_BUNDLE.getString("message.translation_uploaded"), + StringUtils.removeStart(translationFile.getAbsolutePath(), pb.getBasePath())))); + } else { + out.println(StringUtils.removeStart(translationFile.getAbsolutePath(), pb.getBasePath())); + } + }) + .collect(Collectors.toList()); + } ConcurrencyUtil.executeAndWait(tasks, debug); if (containsErrors.get()) { @@ -214,4 +236,41 @@ public void act(Outputter out, PropertiesWithFiles pb, ProjectClient client) { } } } + + private Long uploadToStorage(PropertiesWithFiles pb, ProjectClient client, AtomicBoolean containsErrors, java.io.File translationFile) { + try (InputStream fileStream = new FileInputStream(translationFile)) { + Long storageId = client.uploadStorage(translationFile.getName(), fileStream); + return storageId; + } catch (Exception e) { + containsErrors.set(true); + throw new RuntimeException(String.format( + RESOURCE_BUNDLE.getString("error.upload_translation_to_storage"), + StringUtils.removeStart(translationFile.getAbsolutePath(), pb.getBasePath()) + ), e); + } + } + + private java.io.File getTranslationFileWithPlaceholders(Outputter out, PropertiesWithFiles pb, PlaceholderUtil placeholderUtil, LanguageMapping serverLanguageMapping, FileBean file, String translation, Language language) { + LanguageMapping localLanguageMapping = + LanguageMapping.fromConfigFileLanguageMapping(file.getLanguagesMapping()); + LanguageMapping languageMapping = + LanguageMapping.populate(localLanguageMapping, serverLanguageMapping); + + String transFileName = placeholderUtil.replaceLanguageDependentPlaceholders(translation, languageMapping, language); + transFileName = PropertiesBeanUtils.useTranslationReplace(transFileName, file.getTranslationReplace()); + return getTranslationFile(out, pb, transFileName); + } + + private java.io.File getTranslationFile(Outputter out, PropertiesWithFiles pb, String translation) { + java.io.File transFile = new java.io.File(pb.getBasePath() + Utils.PATH_SEPARATOR + translation); + if (!transFile.exists()) { + if (!plainView) { + out.println(SKIPPED.withIcon(String.format( + RESOURCE_BUNDLE.getString("error.translation_not_exists"), + StringUtils.removeStart(transFile.getAbsolutePath(), pb.getBasePath())))); + } + return null; + } + return transFile; + } } diff --git a/src/main/java/com/crowdin/cli/commands/functionality/RequestBuilder.java b/src/main/java/com/crowdin/cli/commands/functionality/RequestBuilder.java index 51715fe5d..bc323cdc2 100644 --- a/src/main/java/com/crowdin/cli/commands/functionality/RequestBuilder.java +++ b/src/main/java/com/crowdin/cli/commands/functionality/RequestBuilder.java @@ -12,6 +12,7 @@ import com.crowdin.client.labels.model.AddLabelRequest; import com.crowdin.client.sourcefiles.model.AddBranchRequest; import com.crowdin.client.sourcestrings.model.AddSourceStringRequest; +import com.crowdin.client.sourcestrings.model.AddSourceStringStringsBasedRequest; import com.crowdin.client.stringcomments.model.AddStringCommentRequest; import com.crowdin.client.stringcomments.model.Type; import com.crowdin.client.tasks.model.CreateTaskRequest; @@ -20,14 +21,7 @@ import com.crowdin.client.translationmemory.model.TranslationMemoryExportRequest; import com.crowdin.client.translationmemory.model.TranslationMemoryFormat; import com.crowdin.client.translationmemory.model.TranslationMemoryImportRequest; -import com.crowdin.client.translations.model.ApplyPreTranslationRequest; -import com.crowdin.client.translations.model.AutoApproveOption; -import com.crowdin.client.translations.model.CharTransformation; -import com.crowdin.client.translations.model.CrowdinTranslationCraeteProjectPseudoBuildForm; -import com.crowdin.client.translations.model.CrowdinTranslationCreateProjectBuildForm; -import com.crowdin.client.translations.model.ExportProjectTranslationRequest; -import com.crowdin.client.translations.model.Method; -import com.crowdin.client.translations.model.UploadTranslationsRequest; +import com.crowdin.client.translations.model.*; import java.util.ArrayList; import java.util.List; @@ -48,6 +42,18 @@ public static AddSourceStringRequest addString(String text, String identifier, I return request; } + public static AddSourceStringStringsBasedRequest addStringStringsBased(String text, String identifier, Integer maxLength, String context, Long branchId, Boolean hidden, List labelIds) { + AddSourceStringStringsBasedRequest request = new AddSourceStringStringsBasedRequest(); + request.setText(text); + request.setIdentifier(identifier); + request.setMaxLength(maxLength); + request.setContext(context); + request.setBranchId(branchId); + request.setIsHidden(hidden); + request.setLabelIds(labelIds); + return request; + } + public static AddStringCommentRequest addComment(String text, String type, String language, String issueType, String stringId) { AddStringCommentRequest request = new AddStringCommentRequest(); @@ -104,6 +110,15 @@ public static UploadTranslationsRequest uploadTranslations(Long fileId, boolean return request; } + public static UploadTranslationsStringsRequest uploadTranslationsStrings(Long branchId, boolean importEqSuggestions, boolean autoApproveImported, boolean translateHidden) { + UploadTranslationsStringsRequest request = new UploadTranslationsStringsRequest(); + request.setBranchId(branchId); + request.setImportEqSuggestions(importEqSuggestions); + request.setAutoApproveImported(autoApproveImported); + request.setTranslateHidden(translateHidden); + return request; + } + public static PatchRequest patch(Object value, PatchOperation op, String path) { PatchRequest request = new PatchRequest(); request.setValue(value); @@ -285,6 +300,23 @@ public static ApplyPreTranslationRequest applyPreTranslation( return request; } + public static ApplyPreTranslationStringsBasedRequest applyPreTranslationStringsBased( + List languageIds, List branchId, Method method, Long engineId, AutoApproveOption autoApproveOption, + Boolean duplicateTranslations, Boolean translateUntranslatedOnly, Boolean translateWithPerfectMatchOnly, List labelIds + ) { + ApplyPreTranslationStringsBasedRequest request = new ApplyPreTranslationStringsBasedRequest(); + request.setLanguageIds(languageIds); + request.setBranchIds(branchId); + request.setMethod(method); + request.setEngineId(engineId); + request.setAutoApproveOption(autoApproveOption); + request.setDuplicateTranslations(duplicateTranslations); + request.setTranslateUntranslatedOnly(translateUntranslatedOnly); + request.setTranslateWithPerfectMatchOnly(translateWithPerfectMatchOnly); + request.setLabelIds(labelIds); + return request; + } + public static AddBranchRequest addBranch(String name, String title, String exportPattern, Priority priority) { AddBranchRequest request = new AddBranchRequest(); request.setName(name); diff --git a/src/main/java/com/crowdin/cli/commands/picocli/DistributionAddSubcommand.java b/src/main/java/com/crowdin/cli/commands/picocli/DistributionAddSubcommand.java index 198a1c988..2cb4325f9 100644 --- a/src/main/java/com/crowdin/cli/commands/picocli/DistributionAddSubcommand.java +++ b/src/main/java/com/crowdin/cli/commands/picocli/DistributionAddSubcommand.java @@ -56,10 +56,6 @@ protected List checkOptions() { errors.add(RESOURCE_BUNDLE.getString("error.distribution.empty_name")); } - if (exportMode == DEFAULT && files == null) { - errors.add(RESOURCE_BUNDLE.getString("error.distribution.empty_file")); - } - if (exportMode == BUNDLE && bundleIds == null) { errors.add(RESOURCE_BUNDLE.getString("error.distribution.empty_bundle_ids")); } diff --git a/src/main/java/com/crowdin/cli/commands/picocli/DistributionReleaseSubcommand.java b/src/main/java/com/crowdin/cli/commands/picocli/DistributionReleaseSubcommand.java index c14cc1b4b..3232ef2f9 100644 --- a/src/main/java/com/crowdin/cli/commands/picocli/DistributionReleaseSubcommand.java +++ b/src/main/java/com/crowdin/cli/commands/picocli/DistributionReleaseSubcommand.java @@ -1,8 +1,10 @@ package com.crowdin.cli.commands.picocli; import com.crowdin.cli.client.ClientDistribution; +import com.crowdin.cli.client.ProjectClient; import com.crowdin.cli.commands.Actions; import com.crowdin.cli.commands.NewAction; +import com.crowdin.cli.commands.Outputter; import com.crowdin.cli.properties.ProjectProperties; import picocli.CommandLine; @@ -20,6 +22,8 @@ class DistributionReleaseSubcommand extends ActCommandDistribution { @Override protected NewAction getAction(Actions actions) { - return actions.distributionRelease(noProgress, plainView, hash); + Outputter out = new PicocliOutputter(System.out, isAnsi()); + ProjectClient projectClient = this.getProjectClient(this.getProperties(propertiesBuilders, out)); + return actions.distributionRelease(noProgress, plainView, hash, projectClient); } } diff --git a/src/main/java/com/crowdin/cli/commands/picocli/StringAddSubcommand.java b/src/main/java/com/crowdin/cli/commands/picocli/StringAddSubcommand.java index 0be2193a7..9c25c170d 100644 --- a/src/main/java/com/crowdin/cli/commands/picocli/StringAddSubcommand.java +++ b/src/main/java/com/crowdin/cli/commands/picocli/StringAddSubcommand.java @@ -35,6 +35,9 @@ class StringAddSubcommand extends ActCommandProject { @CommandLine.Option(names = {"--label"}, descriptionKey = "params.label", paramLabel = "...", order = -2) protected List labelNames; + @CommandLine.Option(names = {"-b", "--branch"}, descriptionKey = "branch", paramLabel = "...", order = -2) + protected String branch; + @CommandLine.Option(names = {"--hidden"}, order = -2) protected Boolean isHidden; @@ -55,6 +58,6 @@ protected List checkOptions() { @Override protected NewAction getAction(Actions actions) { - return actions.stringAdd(noProgress, text, identifier, maxLength, context, files, labelNames, isHidden); + return actions.stringAdd(noProgress, text, identifier, maxLength, context, files, labelNames, branch, isHidden); } } diff --git a/src/main/resources/messages/messages.properties b/src/main/resources/messages/messages.properties index d4f063754..2638ca3f4 100755 --- a/src/main/resources/messages/messages.properties +++ b/src/main/resources/messages/messages.properties @@ -541,6 +541,7 @@ error.comment_should_not_have_issue_type=Comment should not have the --issue-typ error.distribution_is_not_added=Distribution was not added error.distribution_is_not_released=Distribution was not released +error.distribution_failed=Distribution release failed error.distribution.empty_file=The '--file' value can not be empty for the 'default' export mode error.distribution.empty_bundle_ids=The '--bundle-id' value can not be empty for the 'bundle' export mode error.distribution.incorrect_file_command_usage=The '--file' is used only for the 'default' export mode diff --git a/src/test/java/com/crowdin/cli/commands/actions/CliActionsTest.java b/src/test/java/com/crowdin/cli/commands/actions/CliActionsTest.java index c3fa94c98..fa8800d13 100644 --- a/src/test/java/com/crowdin/cli/commands/actions/CliActionsTest.java +++ b/src/test/java/com/crowdin/cli/commands/actions/CliActionsTest.java @@ -49,7 +49,7 @@ public void testStatus() { @Test public void testStringAdd() { - assertNotNull(actions.stringAdd(false, null, null, null, null, null, null, null)); + assertNotNull(actions.stringAdd(false, null, null, null, null, null, null, null, null)); } @Test @@ -119,7 +119,7 @@ public void testDistributionList() { @Test public void testDistributionRelease() { - assertNotNull(actions.distributionRelease(false,true,null)); + assertNotNull(actions.distributionRelease(false,true,null, null)); } @Test diff --git a/src/test/java/com/crowdin/cli/commands/actions/DistributionAddActionTest.java b/src/test/java/com/crowdin/cli/commands/actions/DistributionAddActionTest.java index d97021a1c..bc149f075 100644 --- a/src/test/java/com/crowdin/cli/commands/actions/DistributionAddActionTest.java +++ b/src/test/java/com/crowdin/cli/commands/actions/DistributionAddActionTest.java @@ -1,6 +1,7 @@ package com.crowdin.cli.commands.actions; import com.crowdin.cli.client.ClientDistribution; +import com.crowdin.cli.client.CrowdinProjectFull; import com.crowdin.cli.client.ProjectBuilder; import com.crowdin.cli.client.ProjectClient; import com.crowdin.cli.commands.NewAction; @@ -13,8 +14,10 @@ import com.crowdin.cli.properties.helper.TempProject; import com.crowdin.cli.utils.Utils; import com.crowdin.client.distributions.model.AddDistributionRequest; +import com.crowdin.client.distributions.model.AddDistributionStringsBasedRequest; import com.crowdin.client.distributions.model.Distribution; import com.crowdin.client.distributions.model.ExportMode; +import com.crowdin.client.projectsgroups.model.Type; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -68,8 +71,10 @@ public void testDistributionAdd(String name, ExportMode exportMode, List "/%original_file_name%-CR-%locale%"))); ProjectClient projectClient = mock(ProjectClient.class); + CrowdinProjectFull build = projectBuilder.build(); + build.setType(Type.FILES_BASED); when(projectClient.downloadFullProject(branch)) - .thenReturn(projectBuilder.build()); + .thenReturn(build); action = new DistributionAddAction(true, true, name, exportMode, files, bundleIds, branch, projectClient); action.act(Outputter.getDefault(), pb, client); @@ -105,4 +110,38 @@ public void testAddDistributionThrows() { verifyNoMoreInteractions(client); } + @Test + public void testDistributionAdd_StringsBasedProject() { + TempProject project = new TempProject(FileHelperTest.class); + + NewPropertiesWithFilesUtilBuilder pbBuilder = NewPropertiesWithFilesUtilBuilder + .minimalBuiltPropertiesBean("*", Utils.PATH_SEPARATOR + "%original_file_name%-CR-%locale%") + .setBasePath(project.getBasePath()); + PropertiesWithFiles pb = pbBuilder.build(); + + AddDistributionStringsBasedRequest request = new AddDistributionStringsBasedRequest(); + request.setName("My Distribution 1"); + request.setBundleIds(Arrays.asList(9)); + + ClientDistribution client = mock(ClientDistribution.class); + when(client.addDistributionStringsBased(request)) + .thenReturn(new Distribution() {{ + setName(request.getName()); + setBundleIds(request.getBundleIds()); + }}); + + ProjectBuilder projectBuilder = ProjectBuilder.emptyProject(Long.parseLong(pb.getProjectId())); + projectBuilder.addBranches(1L, "main"); + ProjectClient projectClient = mock(ProjectClient.class); + CrowdinProjectFull build = projectBuilder.build(); + build.setType(Type.STRINGS_BASED); + when(projectClient.downloadFullProject("main")) + .thenReturn(build); + + action = new DistributionAddAction(true, true, "My Distribution 1", ExportMode.BUNDLE, null, Arrays.asList(9), "main", projectClient); + action.act(Outputter.getDefault(), pb, client); + verify(client).addDistributionStringsBased(request); + verifyNoMoreInteractions(client); + } + } diff --git a/src/test/java/com/crowdin/cli/commands/actions/DistributionReleaseActionTest.java b/src/test/java/com/crowdin/cli/commands/actions/DistributionReleaseActionTest.java index dd7f926bd..159efa812 100644 --- a/src/test/java/com/crowdin/cli/commands/actions/DistributionReleaseActionTest.java +++ b/src/test/java/com/crowdin/cli/commands/actions/DistributionReleaseActionTest.java @@ -1,6 +1,9 @@ package com.crowdin.cli.commands.actions; import com.crowdin.cli.client.ClientDistribution; +import com.crowdin.cli.client.CrowdinProjectInfo; +import com.crowdin.cli.client.ProjectBuilder; +import com.crowdin.cli.client.ProjectClient; import com.crowdin.cli.commands.NewAction; import com.crowdin.cli.commands.Outputter; import com.crowdin.cli.properties.NewPropertiesWithFilesUtilBuilder; @@ -8,6 +11,8 @@ import com.crowdin.cli.properties.PropertiesWithFiles; import com.crowdin.cli.utils.Utils; import com.crowdin.client.distributions.model.DistributionRelease; +import com.crowdin.client.distributions.model.DistributionStringsBasedRelease; +import com.crowdin.client.projectsgroups.model.Type; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -26,11 +31,17 @@ public void releaseDistributionTest_whenSuccess() { .setBasePath(Utils.PATH_SEPARATOR); PropertiesWithFiles pb = pbBuilder.build(); ClientDistribution client = mock(ClientDistribution.class); + ProjectClient projectClient = mock(ProjectClient.class); + CrowdinProjectInfo projectInfo = mock(CrowdinProjectInfo.class); + when(projectInfo.getType()) + .thenReturn(Type.FILES_BASED); + when(projectClient.downloadProjectInfo()) + .thenReturn(projectInfo); DistributionRelease distributionRelease = new DistributionRelease(); distributionRelease.setStatus("success"); when(client.release(HASH)).thenReturn(distributionRelease); - action = new DistributionReleaseAction(true, true, HASH); + action = new DistributionReleaseAction(true, true, HASH, projectClient); action.act(Outputter.getDefault(), pb, client); verify(client).release(HASH); } @@ -42,14 +53,42 @@ public void releaseDistributionTest_whenFailed() { .setBasePath(Utils.PATH_SEPARATOR); PropertiesWithFiles pb = pbBuilder.build(); ClientDistribution client = mock(ClientDistribution.class); + ProjectClient projectClient = mock(ProjectClient.class); + CrowdinProjectInfo projectInfo = mock(CrowdinProjectInfo.class); + when(projectInfo.getType()) + .thenReturn(Type.FILES_BASED); + when(projectClient.downloadProjectInfo()) + .thenReturn(projectInfo); DistributionRelease distributionRelease = new DistributionRelease(); distributionRelease.setStatus("failed"); when(client.release(any())).thenReturn(distributionRelease); when(client.getDistributionRelease(eq(HASH))).thenReturn(distributionRelease); - action = new DistributionReleaseAction(true, true, HASH); + action = new DistributionReleaseAction(true, true, HASH, projectClient); assertThrows(RuntimeException.class, () -> action.act(Outputter.getDefault(), pb, client)); verify(client).release(HASH); verify(client).getDistributionRelease(HASH); } + + @Test + public void releaseDistributionTest_StringsBasedProject() { + NewPropertiesWithFilesUtilBuilder pbBuilder = NewPropertiesWithFilesUtilBuilder + .minimalBuiltPropertiesBean("*", Utils.PATH_SEPARATOR + "%original_file_name%-CR-%locale%") + .setBasePath(Utils.PATH_SEPARATOR); + PropertiesWithFiles pb = pbBuilder.build(); + ClientDistribution client = mock(ClientDistribution.class); + ProjectClient projectClient = mock(ProjectClient.class); + CrowdinProjectInfo projectInfo = mock(CrowdinProjectInfo.class); + when(projectInfo.getType()) + .thenReturn(Type.STRINGS_BASED); + when(projectClient.downloadProjectInfo()) + .thenReturn(projectInfo); + DistributionStringsBasedRelease distributionRelease = new DistributionStringsBasedRelease(); + distributionRelease.setStatus("success"); + when(client.releaseStringsBased(HASH)).thenReturn(distributionRelease); + + action = new DistributionReleaseAction(true, true, HASH, projectClient); + action.act(Outputter.getDefault(), pb, client); + verify(client).releaseStringsBased(HASH); + } } diff --git a/src/test/java/com/crowdin/cli/commands/actions/StringAddActionTest.java b/src/test/java/com/crowdin/cli/commands/actions/StringAddActionTest.java index f1ba8f845..9e701368d 100644 --- a/src/test/java/com/crowdin/cli/commands/actions/StringAddActionTest.java +++ b/src/test/java/com/crowdin/cli/commands/actions/StringAddActionTest.java @@ -1,5 +1,6 @@ package com.crowdin.cli.commands.actions; +import com.crowdin.cli.client.CrowdinProjectFull; import com.crowdin.cli.client.ProjectClient; import com.crowdin.cli.client.ProjectBuilder; import com.crowdin.cli.client.ResponseException; @@ -11,7 +12,9 @@ import com.crowdin.cli.properties.NewPropertiesWithFilesUtilBuilder; import com.crowdin.cli.utils.Utils; import com.crowdin.client.labels.model.Label; +import com.crowdin.client.projectsgroups.model.Type; import com.crowdin.client.sourcestrings.model.AddSourceStringRequest; +import com.crowdin.client.sourcestrings.model.AddSourceStringStringsBasedRequest; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -40,7 +43,7 @@ public class StringAddActionTest { @MethodSource public void testStringAdd( String text, String identifier, Integer maxLength, String context, List labelIds, List labelNames, Boolean hidden, Map files, String[] stringFiles - ) throws ResponseException { + ) { NewPropertiesWithFilesUtilBuilder pbBuilder = NewPropertiesWithFilesUtilBuilder .minimalBuiltPropertiesBean("*", Utils.PATH_SEPARATOR + "%original_file_name%-CR-%locale%") .setBasePath(Utils.PATH_SEPARATOR); @@ -67,11 +70,13 @@ public void testStringAdd( } ProjectClient client = mock(ProjectClient.class); + CrowdinProjectFull build = projectBuilder.build(); + build.setType(Type.FILES_BASED); when(client.downloadFullProject()) - .thenReturn(projectBuilder.build()); + .thenReturn(build); action = - new StringAddAction(true, text, identifier, maxLength, context, Arrays.asList(stringFiles), labelNames, hidden); + new StringAddAction(true, text, identifier, maxLength, context, Arrays.asList(stringFiles), labelNames, null, hidden); action.act(Outputter.getDefault(), pb, client); verify(client).downloadFullProject(); @@ -131,11 +136,13 @@ public void testStringAdd_throwsNotFound() { } ProjectClient client = mock(ProjectClient.class); + CrowdinProjectFull build = projectBuilder.build(); + build.setType(Type.FILES_BASED); when(client.downloadFullProject()) - .thenReturn(projectBuilder.build()); + .thenReturn(build); action = - new StringAddAction(true, text, identifier, maxLength, context, Arrays.asList(stringFiles), labelNames, hidden); + new StringAddAction(true, text, identifier, maxLength, context, Arrays.asList(stringFiles), labelNames, null, hidden); assertThrows(RuntimeException.class, () -> action.act(Outputter.getDefault(), pb, client)); verify(client).downloadFullProject(); @@ -157,7 +164,7 @@ public void testGetProjectThrows() throws ResponseException { when(client.downloadFullProject()) .thenThrow(new RuntimeException("Whoops")); - action = new StringAddAction(false, null, null, null, null, null, null, null); + action = new StringAddAction(false, null, null, null, null, null, null, null, null); assertThrows(RuntimeException.class, () -> action.act(Outputter.getDefault(), pb, client)); verify(client).downloadFullProject(); @@ -205,8 +212,10 @@ public void testStringAdd_UseLabels() { } ProjectClient client = mock(ProjectClient.class); + CrowdinProjectFull build = projectBuilder.build(); + build.setType(Type.FILES_BASED); when(client.downloadFullProject()) - .thenReturn(projectBuilder.build()); + .thenReturn(build); List