diff --git a/cli/src/main/java/com/box/l10n/mojito/cli/command/EnumConverter.java b/cli/src/main/java/com/box/l10n/mojito/cli/command/EnumConverter.java index 295499fc88..0a4f441657 100644 --- a/cli/src/main/java/com/box/l10n/mojito/cli/command/EnumConverter.java +++ b/cli/src/main/java/com/box/l10n/mojito/cli/command/EnumConverter.java @@ -2,6 +2,7 @@ import com.beust.jcommander.IStringConverter; import com.beust.jcommander.ParameterException; +import com.google.common.base.Strings; import java.util.Arrays; @@ -15,7 +16,7 @@ public final T convert(String value) { T result = null; if (value != null) { try { - result = Enum.valueOf(getGenericClass(), value.toUpperCase()); + result = Enum.valueOf(getGenericClass(), Strings.nullToEmpty(value).trim().toUpperCase()); } catch (IllegalArgumentException iae) { String msg = "Invalid type [" + value + "], should be one of: " + Arrays.toString(getGenericClass().getEnumConstants()); throw new ParameterException(msg); @@ -24,4 +25,4 @@ public final T convert(String value) { return result; } -} \ No newline at end of file +} diff --git a/cli/src/main/java/com/box/l10n/mojito/cli/command/ImportLocalizedAssetCommand.java b/cli/src/main/java/com/box/l10n/mojito/cli/command/ImportLocalizedAssetCommand.java index 967b041682..6bae532fb6 100644 --- a/cli/src/main/java/com/box/l10n/mojito/cli/command/ImportLocalizedAssetCommand.java +++ b/cli/src/main/java/com/box/l10n/mojito/cli/command/ImportLocalizedAssetCommand.java @@ -54,8 +54,7 @@ public class ImportLocalizedAssetCommand extends Command { @Parameter(names = {Param.TARGET_DIRECTORY_LONG, Param.TARGET_DIRECTORY_SHORT}, arity = 1, required = false, description = Param.TARGET_DIRECTORY_DESCRIPTION) String targetDirectoryParam; - @Parameter(names = {Param.REPOSITORY_LOCALES_MAPPING_LONG, Param.REPOSITORY_LOCALES_MAPPING_SHORT}, arity = 1, required = false, description = "Locale mapping, format: \"fr:fr-FR,ja:ja-JP\". " - + "The keys contain BCP47 tags of the generated files and the values indicate which repository locales are used to fetch the translations.") + @Parameter(names = {Param.REPOSITORY_LOCALES_MAPPING_LONG, Param.REPOSITORY_LOCALES_MAPPING_SHORT}, arity = 1, required = false, description = Param.REPOSITORY_LOCALES_MAPPING_DESCRIPTION) String localeMappingParam; @Parameter(names = {Param.FILE_TYPE_LONG, Param.FILE_TYPE_SHORT}, arity = 1, required = false, description = Param.FILE_TYPE_DESCRIPTION, diff --git a/cli/src/main/java/com/box/l10n/mojito/cli/command/PullCommand.java b/cli/src/main/java/com/box/l10n/mojito/cli/command/PullCommand.java index c62e03f238..9978ddf983 100644 --- a/cli/src/main/java/com/box/l10n/mojito/cli/command/PullCommand.java +++ b/cli/src/main/java/com/box/l10n/mojito/cli/command/PullCommand.java @@ -53,8 +53,7 @@ public class PullCommand extends Command { @Parameter(names = {Param.TARGET_DIRECTORY_LONG, Param.TARGET_DIRECTORY_SHORT}, arity = 1, required = false, description = Param.TARGET_DIRECTORY_DESCRIPTION) String targetDirectoryParam; - @Parameter(names = {Param.REPOSITORY_LOCALES_MAPPING_LONG, Param.REPOSITORY_LOCALES_MAPPING_SHORT}, arity = 1, required = false, description = "Locale mapping, format: \"fr:fr-FR,ja:ja-JP\". " - + "The keys contain BCP47 tags of the generated files and the values indicate which repository locales are used to fetch the translations.") + @Parameter(names = {Param.REPOSITORY_LOCALES_MAPPING_LONG, Param.REPOSITORY_LOCALES_MAPPING_SHORT}, arity = 1, required = false, description = Param.REPOSITORY_LOCALES_MAPPING_DESCRIPTION) String localeMappingParam; @Parameter(names = {Param.FILE_TYPE_LONG, Param.FILE_TYPE_SHORT}, arity = 1, required = false, description = Param.FILE_TYPE_DESCRIPTION, diff --git a/cli/src/main/java/com/box/l10n/mojito/cli/command/ScreenshotCommand.java b/cli/src/main/java/com/box/l10n/mojito/cli/command/ScreenshotCommand.java index e9c28df06b..e41fda5a92 100644 --- a/cli/src/main/java/com/box/l10n/mojito/cli/command/ScreenshotCommand.java +++ b/cli/src/main/java/com/box/l10n/mojito/cli/command/ScreenshotCommand.java @@ -64,8 +64,7 @@ public class ScreenshotCommand extends Command { @Parameter(names = {Param.REPOSITORY_LONG, Param.REPOSITORY_SHORT}, arity = 1, required = true, description = Param.REPOSITORY_DESCRIPTION) String repositoryParam; - @Parameter(names = {Param.REPOSITORY_LOCALES_MAPPING_LONG, Param.REPOSITORY_LOCALES_MAPPING_SHORT}, arity = 1, required = false, description = "Locale mapping, format: \"fr:fr-FR,ja:ja-JP\". " - + "The keys contain BCP47 tags of the generated files and the values indicate which repository locales are used to fetch the translations.") + @Parameter(names = {Param.REPOSITORY_LOCALES_MAPPING_LONG, Param.REPOSITORY_LOCALES_MAPPING_SHORT}, arity = 1, required = false, description = Param.REPOSITORY_LOCALES_MAPPING_DESCRIPTION) String localeMappingParam; @Parameter(names = {Param.SOURCE_DIRECTORY_LONG, Param.SOURCE_DIRECTORY_SHORT}, arity = 1, required = false, description = "Directory to scan for screenshot") diff --git a/cli/src/main/java/com/box/l10n/mojito/cli/command/ThirdPartySyncActionsConverter.java b/cli/src/main/java/com/box/l10n/mojito/cli/command/ThirdPartySyncActionsConverter.java index e1c1574419..07ac0d0f03 100644 --- a/cli/src/main/java/com/box/l10n/mojito/cli/command/ThirdPartySyncActionsConverter.java +++ b/cli/src/main/java/com/box/l10n/mojito/cli/command/ThirdPartySyncActionsConverter.java @@ -1,15 +1,15 @@ package com.box.l10n.mojito.cli.command; -import com.box.l10n.mojito.rest.client.ThirdPartySync; +import com.box.l10n.mojito.rest.ThirdPartySyncAction; /** * * @author sdemyanenko */ -public class ThirdPartySyncActionsConverter extends EnumConverter { +public class ThirdPartySyncActionsConverter extends EnumConverter { @Override - protected Class getGenericClass() { - return ThirdPartySync.Action.class; + protected Class getGenericClass() { + return ThirdPartySyncAction.class; } } diff --git a/cli/src/main/java/com/box/l10n/mojito/cli/command/ThirdPartySyncCommand.java b/cli/src/main/java/com/box/l10n/mojito/cli/command/ThirdPartySyncCommand.java index d846ef9e2b..23d3b30b95 100644 --- a/cli/src/main/java/com/box/l10n/mojito/cli/command/ThirdPartySyncCommand.java +++ b/cli/src/main/java/com/box/l10n/mojito/cli/command/ThirdPartySyncCommand.java @@ -4,8 +4,8 @@ import com.beust.jcommander.Parameters; import com.box.l10n.mojito.cli.command.param.Param; import com.box.l10n.mojito.cli.console.ConsoleWriter; +import com.box.l10n.mojito.rest.ThirdPartySyncAction; import com.box.l10n.mojito.rest.client.ThirdPartyClient; -import com.box.l10n.mojito.rest.client.ThirdPartySync; import com.box.l10n.mojito.rest.entity.PollableTask; import com.box.l10n.mojito.rest.entity.Repository; import org.fusesource.jansi.Ansi; @@ -44,14 +44,20 @@ public class ThirdPartySyncCommand extends Command { String thirdPartyProjectId; @Parameter(names = {"--actions", "-a"}, variableArity = true, required = false, description = "Actions to synchronize", converter = ThirdPartySyncActionsConverter.class) - List actions = Arrays.asList(ThirdPartySync.Action.MAP_TEXTUNIT, ThirdPartySync.Action.PUSH_SCREENSHOT); + List actions = Arrays.asList(ThirdPartySyncAction.MAP_TEXTUNIT, ThirdPartySyncAction.PUSH_SCREENSHOT); @Parameter(names = {"--plural-separator", "-ps"}, arity = 1, required = false, description = "Plural separator for name") String pluralSeparator; - @Parameter(names = {Param.REPOSITORY_LOCALES_MAPPING_LONG, Param.REPOSITORY_LOCALES_MAPPING_SHORT}, arity = 1, required = false, description = "Locale mapping") + @Parameter(names = {Param.REPOSITORY_LOCALES_MAPPING_LONG, Param.REPOSITORY_LOCALES_MAPPING_SHORT}, arity = 1, required = false, description = Param.REPOSITORY_LOCALES_MAPPING_DESCRIPTION) String localeMapping; + @Parameter(names = {"--skip-text-units-with-pattern", "-st"}, arity = 1, required = false, description = "Do not process text units matching with the SQL LIKE expression") + String skipTextUnitsWithPattern; + + @Parameter(names = {"--skip-assets-path-pattern", "-sa"}, arity = 1, required = false, description = "Do not process text units whose assets path match the SQL LIKE expression") + String skipAssetsWithPathPattern; + @Parameter(names = {"--options", "-o"}, variableArity = true, required = false, description = "Options to synchronize") List options; @@ -69,15 +75,18 @@ public void execute() throws CommandException { .a(" actions: ").fg(CYAN).a(Objects.toString(actions)).reset() .a(" plural-separator: ").fg(CYAN).a(Objects.toString(pluralSeparator)).reset() .a(" locale-mapping: ").fg(CYAN).a(Objects.toString(localeMapping)).reset() + .a(" skip-text-units-with-pattern: ").fg(CYAN).a(Objects.toString(skipTextUnitsWithPattern)).reset() + .a(" skip-assets-path-pattern: ").fg(CYAN).a(Objects.toString(skipAssetsWithPathPattern)).reset() .a(" options: ").fg(CYAN).a(Objects.toString(options)).println(2); Repository repository = commandHelper.findRepositoryByName(repositoryParam); - PollableTask pollableTask = thirdPartyClient.sync(repository.getId(), thirdPartyProjectId, pluralSeparator, localeMapping, actions, options); + PollableTask pollableTask = thirdPartyClient.sync(repository.getId(), thirdPartyProjectId, pluralSeparator, localeMapping, + actions, skipTextUnitsWithPattern, skipAssetsWithPathPattern, options); commandHelper.waitForPollableTask(pollableTask.getId()); consoleWriter.fg(Ansi.Color.GREEN).newLine().a("Finished").println(2); } -} \ No newline at end of file +} diff --git a/cli/src/main/java/com/box/l10n/mojito/cli/command/param/Param.java b/cli/src/main/java/com/box/l10n/mojito/cli/command/param/Param.java index 8049178e8d..01ef179fb4 100644 --- a/cli/src/main/java/com/box/l10n/mojito/cli/command/param/Param.java +++ b/cli/src/main/java/com/box/l10n/mojito/cli/command/param/Param.java @@ -59,6 +59,8 @@ public class Param { public static final String REPOSITORY_LOCALES_MAPPING_LONG = "--locale-mapping"; public static final String REPOSITORY_LOCALES_MAPPING_SHORT = "-lm"; + public static final String REPOSITORY_LOCALES_MAPPING_DESCRIPTION = "Locale mapping, format: \"fr:fr-FR,ja:ja-JP\". " + + "The keys contain BCP47 tags of the generated files and the values indicate which repository locales are used to fetch the translations."; public static final String REPOSITORY_SOURCE_LOCALE_LONG = "--source-locale"; public static final String REPOSITORY_SOURCE_LOCALE_SHORT = "-sl"; diff --git a/cli/src/test/java/com/box/l10n/mojito/cli/command/ThirdPartySyncCommandTest.java b/cli/src/test/java/com/box/l10n/mojito/cli/command/ThirdPartySyncCommandTest.java index f7b61753a6..a43eab2326 100644 --- a/cli/src/test/java/com/box/l10n/mojito/cli/command/ThirdPartySyncCommandTest.java +++ b/cli/src/test/java/com/box/l10n/mojito/cli/command/ThirdPartySyncCommandTest.java @@ -1,32 +1,87 @@ package com.box.l10n.mojito.cli.command; import com.box.l10n.mojito.cli.CLITestBase; +import com.box.l10n.mojito.cli.utils.PollableTaskJobMatcher; +import com.box.l10n.mojito.cli.utils.TestingJobListener; import com.box.l10n.mojito.entity.Repository; -import com.box.l10n.mojito.rest.client.ThirdPartySync; +import com.box.l10n.mojito.json.ObjectMapper; +import com.box.l10n.mojito.rest.ThirdPartySyncAction; +import com.box.l10n.mojito.service.thirdparty.ThirdPartySyncJob; +import com.box.l10n.mojito.service.thirdparty.ThirdPartySyncJobInput; +import org.junit.Before; import org.junit.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.quartz.JobKey; +import org.quartz.Matcher; +import org.quartz.Scheduler; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import java.util.Arrays; +import java.util.List; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; public class ThirdPartySyncCommandTest extends CLITestBase { - /** - * logger - */ - static Logger logger = LoggerFactory.getLogger(ThirdPartySyncCommandTest.class); + @Autowired + @Qualifier("fail_on_unknown_properties_false") + ObjectMapper objectMapper; + + @Autowired + Scheduler scheduler; + + Matcher jobMatcher; + TestingJobListener testingJobListener; + + @Before + public void setUp() throws Exception { + testingJobListener = new TestingJobListener(objectMapper); + jobMatcher = new PollableTaskJobMatcher<>(ThirdPartySyncJob.class); + scheduler.getListenerManager().addJobListener(testingJobListener, jobMatcher); + } @Test public void execute() throws Exception { + String repoName = testIdWatcher.getEntityName("thirdpartysync_execute"); + Repository repository = repositoryService.createRepository(repoName, repoName + " description", null, false); - getL10nJCommander().run("thirdparty-sync", "-r", repository.getName(), "-p", "does-not-matter-yet", "-ps", " _"); + String projectId = testIdWatcher.getEntityName("projectId"); + + // TODO: For a plural separator like " _" this test will fail. The current version we have for + // JCommander trims the argument values, even when quoted. + // https://github.com/cbeust/jcommander/issues/417 + // https://github.com/cbeust/jcommander/commit/4aec38b4a0ea63a8dc6f41636fa81c2ebafddc18 + String pluralSeparator = "_"; + String skipTextUnitPattern = "%skip_text_pattern"; + String skipAssetPattern = "%skip_asset_pattern%"; + List options = Arrays.asList( + "special-option=value@of%Option", + "smartling-placeholder-custom=\\{\\{\\}\\}|\\{\\{?.+?\\}\\}?|\\%\\%\\(.+?\\)s|\\%\\(.+?\\)s|\\%\\(.+?\\)d|\\%\\%s|\\%s" + ); + + getL10nJCommander().run("thirdparty-sync", + "-r", repository.getName(), + "-p", projectId, + "-a", "MAP_TEXTUNIT", "PUSH_SCREENSHOT", + "-ps", pluralSeparator, + "-st", skipTextUnitPattern, + "-sa", skipAssetPattern, + "-o", options.get(0), options.get(1)); + + waitForCondition("Ensure ThirdPartySyncJob gets executed", + () -> testingJobListener.getExecuted().size() > 0); + + ThirdPartySyncJobInput jobInput = testingJobListener.getFirstInputMapAs(ThirdPartySyncJobInput.class); - String outputString = outputCapture.toString(); - assertTrue(outputString.contains(Arrays.asList(ThirdPartySync.Action.MAP_TEXTUNIT, ThirdPartySync.Action.PUSH_SCREENSHOT).toString())); + assertThat(jobInput).isNotNull(); + assertThat(jobInput.getRepositoryId()).isEqualTo(repository.getId()); + assertThat(jobInput.getThirdPartyProjectId()).isEqualTo(projectId); + assertThat(jobInput.getActions()).containsExactlyInAnyOrder(ThirdPartySyncAction.MAP_TEXTUNIT, ThirdPartySyncAction.PUSH_SCREENSHOT); + assertThat(jobInput.getPluralSeparator()).isEqualTo(pluralSeparator); + assertThat(jobInput.getSkipTextUnitsWithPattern()).isEqualTo(skipTextUnitPattern); + assertThat(jobInput.getSkipAssetsWithPathPattern()).isEqualTo(skipAssetPattern); + assertThat(jobInput.getOptions()).containsExactlyInAnyOrderElementsOf(options); } } diff --git a/cli/src/test/java/com/box/l10n/mojito/cli/utils/PollableTaskJobMatcher.java b/cli/src/test/java/com/box/l10n/mojito/cli/utils/PollableTaskJobMatcher.java new file mode 100644 index 0000000000..91e9093940 --- /dev/null +++ b/cli/src/test/java/com/box/l10n/mojito/cli/utils/PollableTaskJobMatcher.java @@ -0,0 +1,21 @@ +package com.box.l10n.mojito.cli.utils; + +import com.box.l10n.mojito.quartz.QuartzPollableJob; +import org.quartz.JobKey; +import org.quartz.Matcher; + +import static com.box.l10n.mojito.quartz.QuartzConfig.DYNAMIC_GROUP_NAME; + +public class PollableTaskJobMatcher implements Matcher { + + private final Class target; + + public PollableTaskJobMatcher(Class target) { + this.target = target; + } + + @Override + public boolean isMatch(JobKey key) { + return key.getName().startsWith(target.getName()) && DYNAMIC_GROUP_NAME.equals(key.getGroup()); + } +} diff --git a/cli/src/test/java/com/box/l10n/mojito/cli/utils/TestingJobListener.java b/cli/src/test/java/com/box/l10n/mojito/cli/utils/TestingJobListener.java new file mode 100644 index 0000000000..9fb13cc7f8 --- /dev/null +++ b/cli/src/test/java/com/box/l10n/mojito/cli/utils/TestingJobListener.java @@ -0,0 +1,65 @@ +package com.box.l10n.mojito.cli.utils; + +import com.box.l10n.mojito.json.ObjectMapper; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; +import org.quartz.JobListener; + +import java.util.LinkedList; +import java.util.Queue; + +import static com.box.l10n.mojito.quartz.QuartzPollableJob.INPUT; + +public class TestingJobListener implements JobListener { + + private final Queue toBeExecuted = new LinkedList<>(); + private final Queue executed = new LinkedList<>(); + private final Queue exceptions = new LinkedList<>(); + private final ObjectMapper objectMapper; + + public TestingJobListener(ObjectMapper objectMapper) { + this.objectMapper = objectMapper; + } + + public TestingJobListener() { + this.objectMapper = new ObjectMapper(); + } + + @Override + public String getName() { + return "TestingJobListener"; + } + + @Override + public void jobToBeExecuted(JobExecutionContext context) { + toBeExecuted.offer(context); + } + + @Override + public void jobExecutionVetoed(JobExecutionContext context) { + } + + @Override + public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) { + executed.offer(context); + exceptions.offer(jobException); + } + + public Queue getExecuted() { + return executed; + } + + public Queue getToBeExecuted() { + return toBeExecuted; + } + + public T getFirstInputMapAs(Class klass) { + String input = getExecuted().stream() + .filter(context -> context.getMergedJobDataMap().containsKey(INPUT)) + .map(context -> context.getMergedJobDataMap().getString(INPUT)) + .findFirst() + .orElse(""); + + return objectMapper.readValueUnchecked(input, klass); + } +} diff --git a/restclient/src/main/java/com/box/l10n/mojito/rest/ThirdPartySyncAction.java b/restclient/src/main/java/com/box/l10n/mojito/rest/ThirdPartySyncAction.java new file mode 100644 index 0000000000..2b9c2d8988 --- /dev/null +++ b/restclient/src/main/java/com/box/l10n/mojito/rest/ThirdPartySyncAction.java @@ -0,0 +1,9 @@ +package com.box.l10n.mojito.rest; + +public enum ThirdPartySyncAction { + PUSH, + PUSH_TRANSLATION, + PULL, + MAP_TEXTUNIT, + PUSH_SCREENSHOT +} diff --git a/restclient/src/main/java/com/box/l10n/mojito/rest/client/ThirdPartyClient.java b/restclient/src/main/java/com/box/l10n/mojito/rest/client/ThirdPartyClient.java index fb10bcee20..82ac302ee2 100644 --- a/restclient/src/main/java/com/box/l10n/mojito/rest/client/ThirdPartyClient.java +++ b/restclient/src/main/java/com/box/l10n/mojito/rest/client/ThirdPartyClient.java @@ -1,8 +1,7 @@ package com.box.l10n.mojito.rest.client; +import com.box.l10n.mojito.rest.ThirdPartySyncAction; import com.box.l10n.mojito.rest.entity.PollableTask; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import org.springframework.web.util.UriComponentsBuilder; @@ -16,19 +15,19 @@ @Component public class ThirdPartyClient extends BaseClient { - /** - * logger - */ - static Logger logger = LoggerFactory.getLogger(ThirdPartyClient.class); - @Override public String getEntityName() { return "thirdparty"; } - public PollableTask sync(Long repositoryId, String projectId, String pluralSeparator, String localeMapping, - List actions, List options) - { + public PollableTask sync(Long repositoryId, + String projectId, + String pluralSeparator, + String localeMapping, + List actions, + String skipTextUnitsWithPattern, + String skipAssetsWithPathPattern, + List options) { ThirdPartySync thirdPartySync = new ThirdPartySync(); @@ -37,6 +36,8 @@ public PollableTask sync(Long repositoryId, String projectId, String pluralSepar thirdPartySync.setActions(actions); thirdPartySync.setPluralSeparator(pluralSeparator); thirdPartySync.setLocaleMapping(localeMapping); + thirdPartySync.setSkipTextUnitsWithPattern(skipTextUnitsWithPattern); + thirdPartySync.setSkipAssetsWithPathPattern(skipAssetsWithPathPattern); thirdPartySync.setOptions(options); UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromPath(getBasePathForEntity()).pathSegment("sync"); diff --git a/restclient/src/main/java/com/box/l10n/mojito/rest/client/ThirdPartySync.java b/restclient/src/main/java/com/box/l10n/mojito/rest/client/ThirdPartySync.java index 422d7cfc34..1d40807f88 100644 --- a/restclient/src/main/java/com/box/l10n/mojito/rest/client/ThirdPartySync.java +++ b/restclient/src/main/java/com/box/l10n/mojito/rest/client/ThirdPartySync.java @@ -1,23 +1,20 @@ package com.box.l10n.mojito.rest.client; +import com.box.l10n.mojito.rest.ThirdPartySyncAction; + +import java.util.ArrayList; import java.util.List; public class ThirdPartySync { - public enum Action { - PUSH, - PUSH_TRANSLATION, - PULL, - MAP_TEXTUNIT, - PUSH_SCREENSHOT - } - Long repositoryId; String projectId; - List actions; + List actions = new ArrayList<>(); String pluralSeparator; String localeMapping; - List options; + String skipTextUnitsWithPattern; + String skipAssetsWithPathPattern; + List options = new ArrayList<>(); public Long getRepositoryId() { return repositoryId; @@ -35,11 +32,11 @@ public void setProjectId(String projectId) { this.projectId = projectId; } - public List getActions() { + public List getActions() { return actions; } - public void setActions(List actions) { + public void setActions(List actions) { this.actions = actions; } @@ -59,6 +56,22 @@ public void setLocaleMapping(String localeMapping) { this.localeMapping = localeMapping; } + public String getSkipTextUnitsWithPattern() { + return skipTextUnitsWithPattern; + } + + public void setSkipTextUnitsWithPattern(String skipTextUnitsWithPattern) { + this.skipTextUnitsWithPattern = skipTextUnitsWithPattern; + } + + public String getSkipAssetsWithPathPattern() { + return skipAssetsWithPathPattern; + } + + public void setSkipAssetsWithPathPattern(String skipAssetsWithPathPattern) { + this.skipAssetsWithPathPattern = skipAssetsWithPathPattern; + } + public List getOptions() { return options; } diff --git a/webapp/pom.xml b/webapp/pom.xml index 92e9af6c1d..42193294db 100644 --- a/webapp/pom.xml +++ b/webapp/pom.xml @@ -205,6 +205,7 @@ junit junit + test @@ -217,7 +218,6 @@ com.box.l10n.mojito mojito-restclient 0.111-SNAPSHOT - test diff --git a/webapp/src/main/java/com/box/l10n/mojito/rest/thirdparty/ThirdPartySync.java b/webapp/src/main/java/com/box/l10n/mojito/rest/thirdparty/ThirdPartySync.java deleted file mode 100644 index ba280be056..0000000000 --- a/webapp/src/main/java/com/box/l10n/mojito/rest/thirdparty/ThirdPartySync.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.box.l10n.mojito.rest.thirdparty; - -import com.box.l10n.mojito.service.thirdparty.ThirdPartyService; - -import java.util.List; - -public class ThirdPartySync { - - Long repositoryId; - String projectId; - List actions; - String pluralSeparator; - String localMapping; - List options; - - public Long getRepositoryId() { - return repositoryId; - } - - public void setRepositoryId(Long repositoryId) { - this.repositoryId = repositoryId; - } - - public String getProjectId() { - return projectId; - } - - public void setProjectId(String projectId) { - this.projectId = projectId; - } - - public List getActions() { - return actions; - } - - public void setActions(List actions) { - this.actions = actions; - } - - public String getPluralSeparator() { - return pluralSeparator; - } - - public void setPluralSeparator(String pluralSeparator) { - this.pluralSeparator = pluralSeparator; - } - - public String getLocalMapping() { - return localMapping; - } - - public void setLocalMapping(String localMapping) { - this.localMapping = localMapping; - } - - public List getOptions() { - return options; - } - - public void setOptions(List options) { - this.options = options; - } -} diff --git a/webapp/src/main/java/com/box/l10n/mojito/rest/thirdparty/ThirdPartyWS.java b/webapp/src/main/java/com/box/l10n/mojito/rest/thirdparty/ThirdPartyWS.java index ddc49c4cc3..3130199588 100644 --- a/webapp/src/main/java/com/box/l10n/mojito/rest/thirdparty/ThirdPartyWS.java +++ b/webapp/src/main/java/com/box/l10n/mojito/rest/thirdparty/ThirdPartyWS.java @@ -1,7 +1,7 @@ package com.box.l10n.mojito.rest.thirdparty; import com.box.l10n.mojito.entity.PollableTask; -import com.box.l10n.mojito.service.pollableTask.PollableFuture; +import com.box.l10n.mojito.rest.client.ThirdPartySync; import com.box.l10n.mojito.service.thirdparty.ThirdPartyService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -12,7 +12,7 @@ import org.springframework.web.bind.annotation.RestController; @RestController - public class ThirdPartyWS { +public class ThirdPartyWS { /** * logger @@ -23,13 +23,9 @@ public class ThirdPartyWS { ThirdPartyService thirdPartyService; @RequestMapping(value = "/api/thirdparty/sync", method = RequestMethod.POST) - public PollableTask thirdpartySyn(@RequestBody ThirdPartySync thirdPartySync) { - logger.debug("Sync repository: {} with third party project: {} actions: {} plural separator: {} locale mapping: {} options: {}", - thirdPartySync.getRepositoryId(), thirdPartySync.getProjectId(), thirdPartySync.getActions(), - thirdPartySync.getPluralSeparator(), thirdPartySync.getLocalMapping(), thirdPartySync.getOptions()); - PollableFuture pollableFuture = thirdPartyService.asyncSyncMojitoWithThirdPartyTMS( - thirdPartySync.getRepositoryId(), thirdPartySync.getProjectId(), thirdPartySync.getActions(), - thirdPartySync.getPluralSeparator(), thirdPartySync.getLocalMapping(), thirdPartySync.getOptions()); - return pollableFuture.getPollableTask(); + public PollableTask sync(@RequestBody ThirdPartySync thirdPartySync) { + logger.debug("Sync repository: {}", thirdPartySync); + return thirdPartyService.asyncSyncMojitoWithThirdPartyTMS(thirdPartySync) + .getPollableTask(); } } diff --git a/webapp/src/main/java/com/box/l10n/mojito/service/thirdparty/ThirdPartyService.java b/webapp/src/main/java/com/box/l10n/mojito/service/thirdparty/ThirdPartyService.java index 75c8dc738d..835823c92c 100644 --- a/webapp/src/main/java/com/box/l10n/mojito/service/thirdparty/ThirdPartyService.java +++ b/webapp/src/main/java/com/box/l10n/mojito/service/thirdparty/ThirdPartyService.java @@ -7,6 +7,8 @@ import com.box.l10n.mojito.entity.TMTextUnit; import com.box.l10n.mojito.entity.ThirdPartyScreenshot; import com.box.l10n.mojito.quartz.QuartzPollableTaskScheduler; +import com.box.l10n.mojito.rest.ThirdPartySyncAction; +import com.box.l10n.mojito.rest.client.ThirdPartySync; import com.box.l10n.mojito.service.asset.AssetRepository; import com.box.l10n.mojito.service.image.ImageService; import com.box.l10n.mojito.service.pollableTask.PollableFuture; @@ -27,8 +29,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; -import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; @@ -49,14 +49,6 @@ @Component public class ThirdPartyService { - public enum Action { - PUSH, - PUSH_TRANSLATION, - PULL, - MAP_TEXTUNIT, - PUSH_SCREENSHOT - } - static Logger logger = LoggerFactory.getLogger(ThirdPartyService.class); @Autowired @@ -92,39 +84,46 @@ public enum Action { @Autowired ThirdPartyTMS thirdPartyTMS; - public PollableFuture asyncSyncMojitoWithThirdPartyTMS(Long repositoryId, String thirdPartyProjectId, List actions, String pluralSeparator, String localeMapping, List options) { + public PollableFuture asyncSyncMojitoWithThirdPartyTMS(ThirdPartySync thirdPartySync) { ThirdPartySyncJobInput thirdPartySyncJobInput = new ThirdPartySyncJobInput(); - thirdPartySyncJobInput.setRepositoryId(repositoryId); - thirdPartySyncJobInput.setThirdPartyProjectId(thirdPartyProjectId); - thirdPartySyncJobInput.setActions(actions); - thirdPartySyncJobInput.setPluralSeparator(pluralSeparator); - thirdPartySyncJobInput.setLocaleMapping(localeMapping); - thirdPartySyncJobInput.setOptions(options); + thirdPartySyncJobInput.setRepositoryId(thirdPartySync.getRepositoryId()); + thirdPartySyncJobInput.setThirdPartyProjectId(thirdPartySync.getProjectId()); + thirdPartySyncJobInput.setActions(thirdPartySync.getActions()); + thirdPartySyncJobInput.setPluralSeparator(thirdPartySync.getPluralSeparator()); + thirdPartySyncJobInput.setLocaleMapping(thirdPartySync.getLocaleMapping()); + thirdPartySyncJobInput.setSkipTextUnitsWithPattern(thirdPartySync.getSkipTextUnitsWithPattern()); + thirdPartySyncJobInput.setSkipAssetsWithPathPattern(thirdPartySync.getSkipAssetsWithPathPattern()); + thirdPartySyncJobInput.setOptions(thirdPartySync.getOptions()); return quartzPollableTaskScheduler.scheduleJob(ThirdPartySyncJob.class, thirdPartySyncJobInput); } - void syncMojitoWithThirdPartyTMS(Long repositoryId, String thirdPartyProjectId, List actions, String pluralSeparator, String localeMapping, List options) { + void syncMojitoWithThirdPartyTMS(Long repositoryId, + String thirdPartyProjectId, + List actions, + String pluralSeparator, + String localeMapping, + String skipTextUnitsWithPattern, + String skipAssetsWithPathPattern, + List options) { logger.debug("thirdparty TMS: {}", thirdPartyTMS); Repository repository = repositoryRepository.findOne(repositoryId); - Map optionMap = Optional.ofNullable(options).orElse(Collections.emptyList()).stream().collect( - Collectors.toMap(str -> str.split("=")[0], str -> str.split("=")[1], (a, b) -> a, HashMap::new)); - if (actions.contains(Action.PUSH)) { + if (actions.contains(ThirdPartySyncAction.PUSH)) { throw new UnsupportedOperationException(); } - if (actions.contains(Action.PUSH_TRANSLATION)) { + if (actions.contains(ThirdPartySyncAction.PUSH_TRANSLATION)) { throw new UnsupportedOperationException(); } - if (actions.contains(Action.PULL)) { + if (actions.contains(ThirdPartySyncAction.PULL)) { throw new UnsupportedOperationException(); } - if (actions.contains(Action.MAP_TEXTUNIT)) { + if (actions.contains(ThirdPartySyncAction.MAP_TEXTUNIT)) { mapMojitoAndThirdPartyTextUnits(repository, thirdPartyProjectId); } - if (actions.contains(Action.PUSH_SCREENSHOT)) { + if (actions.contains(ThirdPartySyncAction.PUSH_SCREENSHOT)) { uploadScreenshotsAndCreateMappings(repository, thirdPartyProjectId); } } diff --git a/webapp/src/main/java/com/box/l10n/mojito/service/thirdparty/ThirdPartySyncJob.java b/webapp/src/main/java/com/box/l10n/mojito/service/thirdparty/ThirdPartySyncJob.java index 15204f14ff..a7047d12b6 100644 --- a/webapp/src/main/java/com/box/l10n/mojito/service/thirdparty/ThirdPartySyncJob.java +++ b/webapp/src/main/java/com/box/l10n/mojito/service/thirdparty/ThirdPartySyncJob.java @@ -25,7 +25,7 @@ public class ThirdPartySyncJob extends QuartzPollableJob actions; + List actions; String pluralSeparator; String localeMapping; + String skipTextUnitsWithPattern; + String skipAssetsWithPathPattern; List options; public Long getRepositoryId() { @@ -30,11 +34,11 @@ public void setThirdPartyProjectId(String thirdPartyProjectId) { this.thirdPartyProjectId = thirdPartyProjectId; } - public List getActions() { + public List getActions() { return actions; } - public void setActions(List actions) { + public void setActions(List actions) { this.actions = actions; } @@ -54,6 +58,22 @@ public void setLocaleMapping(String localeMapping) { this.localeMapping = localeMapping; } + public String getSkipTextUnitsWithPattern() { + return skipTextUnitsWithPattern; + } + + public void setSkipTextUnitsWithPattern(String skipTextUnitsWithPattern) { + this.skipTextUnitsWithPattern = skipTextUnitsWithPattern; + } + + public String getSkipAssetsWithPathPattern() { + return skipAssetsWithPathPattern; + } + + public void setSkipAssetsWithPathPattern(String skipAssetsWithPathPattern) { + this.skipAssetsWithPathPattern = skipAssetsWithPathPattern; + } + public List getOptions() { return options; } diff --git a/webapp/src/test/java/com/box/l10n/mojito/service/thirdparty/ThirdPartyServiceTest.java b/webapp/src/test/java/com/box/l10n/mojito/service/thirdparty/ThirdPartyServiceTest.java index efd71f4647..dc79e4971b 100644 --- a/webapp/src/test/java/com/box/l10n/mojito/service/thirdparty/ThirdPartyServiceTest.java +++ b/webapp/src/test/java/com/box/l10n/mojito/service/thirdparty/ThirdPartyServiceTest.java @@ -5,11 +5,12 @@ import com.box.l10n.mojito.entity.Screenshot; import com.box.l10n.mojito.entity.ScreenshotRun; import com.box.l10n.mojito.entity.ThirdPartyScreenshot; +import com.box.l10n.mojito.rest.ThirdPartySyncAction; +import com.box.l10n.mojito.rest.client.ThirdPartySync; import com.box.l10n.mojito.service.asset.AssetRepository; import com.box.l10n.mojito.service.asset.AssetService; import com.box.l10n.mojito.service.assetExtraction.AssetExtractionService; import com.box.l10n.mojito.service.assetExtraction.ServiceTestBase; -import com.box.l10n.mojito.okapi.asset.UnsupportedAssetFilterTypeException; import com.box.l10n.mojito.service.assetcontent.AssetContentService; import com.box.l10n.mojito.service.image.ImageService; import com.box.l10n.mojito.service.pollableTask.PollableTaskService; @@ -122,7 +123,7 @@ void initThirdPartyTMSMock() { } @Test - public void mapMojitoAndThirdPartyTextUnits() throws RepositoryNameAlreadyUsedException, InterruptedException, ExecutionException, UnsupportedAssetFilterTypeException, IOException { + public void mapMojitoAndThirdPartyTextUnits() throws InterruptedException, ExecutionException { ThirdPartyServiceTestData thirdPartyServiceTestData = new ThirdPartyServiceTestData(testIdWatcher); Repository repository = thirdPartyServiceTestData.repository; @@ -139,9 +140,15 @@ public void mapMojitoAndThirdPartyTextUnits() throws RepositoryNameAlreadyUsedEx doNothing().when(thirdPartyTMSMock).createImageToTextUnitMappings(any(), any()); logger.debug("Invoke function to test"); - thirdPartyService.asyncSyncMojitoWithThirdPartyTMS(repository.getId(), projectId, - Arrays.asList(ThirdPartyService.Action.MAP_TEXTUNIT, ThirdPartyService.Action.PUSH_SCREENSHOT), - " _", null, new ArrayList<>()).get(); + + ThirdPartySync thirdPartySync = new ThirdPartySync(); + thirdPartySync.setRepositoryId(repository.getId()); + thirdPartySync.setProjectId(projectId); + thirdPartySync.setActions(Arrays.asList(ThirdPartySyncAction.MAP_TEXTUNIT, ThirdPartySyncAction.PUSH_SCREENSHOT)); + thirdPartySync.setPluralSeparator(" _"); + thirdPartySync.setOptions(new ArrayList<>()); + + thirdPartyService.asyncSyncMojitoWithThirdPartyTMS(thirdPartySync).get(); logger.debug("Verify states"); thirdPartyTextUnitRepository.findAll().stream() @@ -226,9 +233,15 @@ public void duplicatedNamesSubSequentMapping() throws ExecutionException, Interr doNothing().when(thirdPartyTMSMock).createImageToTextUnitMappings(any(), any()); logger.debug("Invoke function to test"); - thirdPartyService.asyncSyncMojitoWithThirdPartyTMS(repository.getId(), projectId, - Arrays.asList(ThirdPartyService.Action.MAP_TEXTUNIT), - " _", null, new ArrayList<>()).get(); + + ThirdPartySync thirdPartySync = new ThirdPartySync(); + thirdPartySync.setRepositoryId(repository.getId()); + thirdPartySync.setProjectId(projectId); + thirdPartySync.setActions(Arrays.asList(ThirdPartySyncAction.MAP_TEXTUNIT)); + thirdPartySync.setPluralSeparator(" _"); + thirdPartySync.setOptions(new ArrayList<>()); + + thirdPartyService.asyncSyncMojitoWithThirdPartyTMS(thirdPartySync).get(); logger.debug("Verify states"); thirdPartyTextUnitRepository.findAll().stream() @@ -249,9 +262,12 @@ public void duplicatedNamesSubSequentMapping() throws ExecutionException, Interr assertEquals("3rd-hello", thirdPartyTextUnits.get(0).getThirdPartyId()); logger.debug("Invoke function to test - duplicate name"); - thirdPartyService.asyncSyncMojitoWithThirdPartyTMS(repository.getId(), projectId, - Arrays.asList(ThirdPartyService.Action.MAP_TEXTUNIT), - " _", null, new ArrayList<>()).get(); + thirdPartySync = new ThirdPartySync(); + thirdPartySync.setRepositoryId(repository.getId()); + thirdPartySync.setProjectId(projectId); + thirdPartySync.setActions(Arrays.asList(ThirdPartySyncAction.MAP_TEXTUNIT)); + thirdPartySync.setPluralSeparator(" _"); + thirdPartySync.setOptions(new ArrayList<>()); logger.debug("Verify states - duplicate name"); thirdPartyTextUnits = thirdPartyTextUnitRepository.findAll().stream() @@ -322,4 +338,4 @@ ThirdPartyTextUnit createThirdPartyTextUnit(String assetPath, String id, String return thirdPartyTextUnit; } -} \ No newline at end of file +} diff --git a/webapp/src/test/java/com/box/l10n/mojito/service/thirdparty/ThirdPartySyncJobInputTest.java b/webapp/src/test/java/com/box/l10n/mojito/service/thirdparty/ThirdPartySyncJobInputTest.java new file mode 100644 index 0000000000..e3be0ff9d7 --- /dev/null +++ b/webapp/src/test/java/com/box/l10n/mojito/service/thirdparty/ThirdPartySyncJobInputTest.java @@ -0,0 +1,113 @@ +package com.box.l10n.mojito.service.thirdparty; + +import com.box.l10n.mojito.json.ObjectMapper; +import com.box.l10n.mojito.rest.ThirdPartySyncAction; +import com.fasterxml.jackson.databind.DeserializationFeature; +import org.junit.Test; + +import java.util.Arrays; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ThirdPartySyncJobInputTest { + + @Test + public void testBackwardsCompatibility() { + + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + + // The old representation of the ThirdPartySyncJobInput class + Long repositoryId = 120L; + String thirdPartyProjectId = "0p9o8i7u"; + String localeMapping = "tr:tr-TR"; + String pluralSeparator = " _"; + List actions = Arrays.asList(ThirdPartySyncAction.MAP_TEXTUNIT, ThirdPartySyncAction.PULL); + List options = Arrays.asList( + "smartling-placeholder=CUSTOM", + "smartling-placeholder-custom=\\{\\{\\}\\}|\\{\\{?.+?\\}\\}?|\\%\\%\\(.+?\\)s|\\%\\(.+?\\)s|\\%\\(.+?\\)d|\\%\\%s|\\%s"); + + TPSyncJobInput oldInput = new TPSyncJobInput(); + oldInput.setRepositoryId(repositoryId); + oldInput.setThirdPartyProjectId(thirdPartyProjectId); + oldInput.setActions(actions); + oldInput.setPluralSeparator(pluralSeparator); + oldInput.setLocaleMapping(localeMapping); + oldInput.setOptions(options); + + String oldRes = objectMapper.writeValueAsStringUnchecked(oldInput); + + ThirdPartySyncJobInput input = objectMapper.readValueUnchecked(oldRes, ThirdPartySyncJobInput.class); + assertThat(input).isNotNull(); + assertThat(input.getRepositoryId()).isEqualTo(repositoryId); + assertThat(input.getThirdPartyProjectId()).isEqualTo(thirdPartyProjectId); + assertThat(input.getActions()).containsExactlyInAnyOrderElementsOf(actions); + assertThat(input.getPluralSeparator()).isEqualTo(pluralSeparator); + assertThat(input.getLocaleMapping()).isEqualTo(localeMapping); + assertThat(input.getOptions()).containsExactlyInAnyOrderElementsOf(options); + assertThat(input.getSkipTextUnitsWithPattern()).isNull(); + assertThat(input.getSkipAssetsWithPathPattern()).isNull(); + } + + /** + * Copy of the signature of the {@link ThirdPartySyncJobInput} class before we introduced new parameters to it + */ + private static class TPSyncJobInput { + + Long repositoryId; + String thirdPartyProjectId; + List actions; + String pluralSeparator; + String localeMapping; + List options; + + public Long getRepositoryId() { + return repositoryId; + } + + public void setRepositoryId(Long repositoryId) { + this.repositoryId = repositoryId; + } + + public String getThirdPartyProjectId() { + return thirdPartyProjectId; + } + + public void setThirdPartyProjectId(String thirdPartyProjectId) { + this.thirdPartyProjectId = thirdPartyProjectId; + } + + public List getActions() { + return actions; + } + + public void setActions(List actions) { + this.actions = actions; + } + + public String getPluralSeparator() { + return pluralSeparator; + } + + public void setPluralSeparator(String pluralSeparator) { + this.pluralSeparator = pluralSeparator; + } + + public String getLocaleMapping() { + return localeMapping; + } + + public void setLocaleMapping(String localeMapping) { + this.localeMapping = localeMapping; + } + + public List getOptions() { + return options; + } + + public void setOptions(List options) { + this.options = options; + } + } +}