Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cli/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<parameters>true</parameters>
<annotationProcessorPaths>
<dependency>
<groupId>org.immutables</groupId>
Expand Down
36 changes: 36 additions & 0 deletions cli/src/main/java/com/box/l10n/mojito/cli/command/PullCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
import com.box.l10n.mojito.rest.entity.RepositoryLocale;
import com.box.l10n.mojito.rest.entity.RepositoryLocaleStatistic;
import com.box.l10n.mojito.rest.entity.RepositoryStatistic;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.AbstractMap.SimpleEntry;
import java.util.HashMap;
Expand Down Expand Up @@ -105,6 +107,12 @@ enum LocaleMappingType {
description = Param.FILTER_OPTIONS_DESCRIPTION)
List<String> filterOptionsParam;

@Parameter(
names = {"--skip-empty-output"},
description =
"Skip writing localized files when the generated content is empty (also requests the Android filter to blank empty resources)")
boolean skipEmptyOutput = false;

@Parameter(
names = {Param.SOURCE_LOCALE_LONG, Param.SOURCE_LOCALE_SHORT},
arity = 1,
Expand Down Expand Up @@ -431,12 +439,40 @@ void writeLocalizedAssetToTargetDirectory(
.getTargetDirectoryPath()
.resolve(sourceFileMatch.getTargetPath(localizedAsset.getBcp47Tag()));

if (skipWritingEmptyOutput(localizedAsset.getContent(), targetPath, sourceFileMatch)) {
return;
}

commandHelper.writeFileContent(localizedAsset.getContent(), targetPath, sourceFileMatch);

Path relativeTargetFilePath = commandDirectories.relativizeWithUserDirectory(targetPath);
consoleWriter.a(" --> ").fg(Color.MAGENTA).a(relativeTargetFilePath.toString()).println();
}

boolean skipWritingEmptyOutput(String content, Path targetPath, FileMatch sourceFileMatch)
throws CommandException {
if (!skipEmptyOutput || !(content == null || content.isBlank())) {
return false;
}

try {
Files.deleteIfExists(targetPath);
} catch (IOException e) {
throw new CommandException(
"Cannot delete empty output file in path: " + targetPath.toString(), e);
}

Path relativeTargetFilePath = commandDirectories.relativizeWithUserDirectory(targetPath);
consoleWriter
.a(" --> ")
.fg(Color.MAGENTA)
.a("skipped empty content: ")
.a(relativeTargetFilePath.toString())
.println();

return true;
}

LocalizedAssetBody getLocalizedAsset(
Repository repository,
FileMatch sourceFileMatch,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ public PullCommandParallel(PullCommand pullCommand) {
this.pullRunName = pullCommand.pullRunName;
this.recordPullRun = pullCommand.recordPullRun;
this.isParallel = pullCommand.isParallel;
this.skipEmptyOutput = pullCommand.skipEmptyOutput;
}

public void pull() throws CommandException {
Expand Down Expand Up @@ -117,6 +118,10 @@ void writeLocalizedAssetToTargetDirectory(
.getTargetDirectoryPath()
.resolve(sourceFileMatch.getTargetPath(localizedAsset.getBcp47Tag()));

if (skipWritingEmptyOutput(localizedAsset.getContent(), targetPath, sourceFileMatch)) {
return;
}

commandHelper.writeFileContent(localizedAsset.getContent(), targetPath, sourceFileMatch);

Path relativeTargetFilePath = commandDirectories.relativizeWithUserDirectory(targetPath);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
import com.box.l10n.mojito.rest.client.exception.ResourceNotCreatedException;
import com.box.l10n.mojito.rest.entity.Role;
import com.box.l10n.mojito.rest.entity.User;
import java.security.SecureRandom;
import java.util.Base64;
import java.util.List;
import org.fusesource.jansi.Ansi;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -70,18 +73,61 @@ public class UserCreateCommand extends Command {
description = Param.COMMON_NAME_DESCRIPTION)
String commonName;

@Parameter(
names = {"--locales", "-l"},
variableArity = true,
description = "List of locales (BCP47 tags) translators can work on, e.g. fr-FR ja-JP")
List<String> locales;

@Parameter(
names = {"--generate-password", "-gp"},
description = "Generate a secure random password instead of prompting")
boolean generatePassword = false;

@Autowired Console console;

private static final SecureRandom SECURE_RANDOM = new SecureRandom();

@Override
protected void execute() throws CommandException {
consoleWriter.a("Create user: ").fg(Ansi.Color.CYAN).a(username).println();

try {
consoleWriter.a("Enter new password for " + username + ":").println();
String password = console.readPassword();
String password;
if (generatePassword) {
password = generateSecurePassword();
consoleWriter
.a("Generated password for ")
.fg(Ansi.Color.CYAN)
.a(username)
.a(": ")
.fg(Ansi.Color.YELLOW)
.a(password)
.println();
} else {
consoleWriter.a("Enter new password for " + username + ":").println();
password = console.readPassword();
}

Role role = Role.fromString(rolename);
User user = userClient.createUser(username, password, role, surname, givenName, commonName);

User user;
if (Role.ROLE_TRANSLATOR.equals(role)) {
boolean hasLocales = locales != null && !locales.isEmpty();
boolean canTranslateAllLocales = !hasLocales;
user =
userClient.createUser(
username,
password,
role,
surname,
givenName,
commonName,
locales,
canTranslateAllLocales);
} else {
user = userClient.createUser(username, password, role, surname, givenName, commonName);
}
consoleWriter
.newLine()
.a("created --> user: ")
Expand All @@ -92,4 +138,10 @@ protected void execute() throws CommandException {
throw new CommandException(ex.getMessage(), ex);
}
}

private String generateSecurePassword() {
byte[] bytes = new byte[18];
SECURE_RANDOM.nextBytes(bytes);
return Base64.getUrlEncoder().withoutPadding().encodeToString(bytes);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -652,6 +652,18 @@ public void pullAndroidStrings() throws Exception {
"-t",
getTargetTestDir("target").getAbsolutePath());

getL10nJCommander()
.run(
"pull",
"-r",
repository.getName(),
"-s",
getInputResourcesTestDir("source").getAbsolutePath(),
"-t",
getTargetTestDir("removeDescription").getAbsolutePath(),
"-fo",
"removeDescription=true");

getL10nJCommander()
.run(
"pull",
Expand All @@ -665,6 +677,42 @@ public void pullAndroidStrings() throws Exception {
checkExpectedGeneratedResources();
}

@Test
public void pullAndroidStringsSkipEmpty() throws Exception {

Repository repository = createTestRepoUsingRepoService();

getL10nJCommander()
.run(
"push",
"-r",
repository.getName(),
"-s",
getInputResourcesTestDir("source").getAbsolutePath());

Asset asset =
assetClient.getAssetByPathAndRepositoryId("res/values/strings.xml", repository.getId());

getL10nJCommander()
.run(
"pull",
"-r",
repository.getName(),
"-s",
getInputResourcesTestDir("source").getAbsolutePath(),
"-t",
getTargetTestDir("target").getAbsolutePath(),
"-fo",
"postEmptyResourcesToEmptyFile=true",
"postRemoveTranslatableFalse=true",
"removeDescription=true",
"--inheritance-mode",
"REMOVE_UNTRANSLATED",
"--skip-empty-output");

checkExpectedGeneratedResources();
}

@Test
public void pullMacStrings() throws Exception {

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.box.l10n.mojito.cli.command;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
Expand All @@ -11,6 +12,10 @@
import com.box.l10n.mojito.entity.security.user.User;
import com.box.l10n.mojito.security.Role;
import com.box.l10n.mojito.service.security.user.UserRepository;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import org.junit.Test;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
Expand Down Expand Up @@ -62,7 +67,46 @@ public void testCreateUserWithDuplicatedUsername() throws Exception {
outputCapture.toString().contains("User with username [" + username + "] already exists"));
}

@Test
public void testCreateUserWithGeneratedPassword() throws Exception {

String username = testIdWatcher.getEntityName("user");
String commonName = createTestUser(username, null, null, true);

User user = userRepository.findByUsername(username);
assertEquals(commonName, user.getCommonName());
assertTrue(outputCapture.toString().contains("Generated password for " + username + ":"));
}

@Test
public void testCreateTranslatorWithLocales() throws Exception {

String username = testIdWatcher.getEntityName("user");
List<String> locales = Arrays.asList("fr-FR", "ja-JP");
String commonName = createTestUser(username, "TRANSLATOR", locales);

User user = userRepository.findByUsername(username);
assertEquals(commonName, user.getCommonName());
assertFalse(user.getCanTranslateAllLocales());
assertEquals(
locales.stream().collect(Collectors.toSet()),
user.getUserLocales().stream()
.map(userLocale -> userLocale.getLocale().getBcp47Tag())
.collect(Collectors.toSet()));
}

private String createTestUser(String username, String role) throws Exception {
return createTestUser(username, role, null);
}

private String createTestUser(String username, String role, List<String> localeTags)
throws Exception {
return createTestUser(username, role, localeTags, false);
}

private String createTestUser(
String username, String role, List<String> localeTags, boolean generatePassword)
throws Exception {
String surname = "Mojito";
String givenName = "Test";
String commonName = "Test Mojito " + username;
Expand All @@ -83,32 +127,34 @@ public String answer(InvocationOnMock invocation) throws Throwable {
userCreateCommand.console = mockConsole;

logger.debug("Creating user with username: {}", username);
if (role == null) {
l10nJCommander.run(
"user-create",
Param.USERNAME_SHORT,
username,
Param.SURNAME_SHORT,
surname,
Param.GIVEN_NAME_SHORT,
givenName,
Param.COMMON_NAME_SHORT,
commonName);
} else {
l10nJCommander.run(
"user-create",
Param.USERNAME_SHORT,
username,
Param.ROLE_SHORT,
role,
Param.SURNAME_SHORT,
surname,
Param.GIVEN_NAME_SHORT,
givenName,
Param.COMMON_NAME_SHORT,
commonName);
List<String> params =
new ArrayList<>(
Arrays.asList(
"user-create",
Param.USERNAME_SHORT,
username,
Param.SURNAME_SHORT,
surname,
Param.GIVEN_NAME_SHORT,
givenName,
Param.COMMON_NAME_SHORT,
commonName));

if (role != null) {
params.addAll(Arrays.asList(Param.ROLE_SHORT, role));
}

if (localeTags != null && !localeTags.isEmpty()) {
params.add("-l");
params.addAll(localeTags);
}

if (generatePassword) {
params.add("-gp");
}

l10nJCommander.run(params.toArray(new String[0]));

assertTrue(outputCapture.toString().contains("created --> user: "));
return commonName;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<resources>
<string name="100_character_description_">
<annotation tag="user">%1$s fr</annotation>
\'Description\' de
<b>100</b>
\"caractères\" :\n
</string>
<string name="15_min_duration">15 min</string>
<string name="1_day_duration">1 jour</string>
<string name="1_hour_duration">1 heure</string>
<string name="1_month_duration">1 mois</string>
</resources>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<resources>
<string name="100_character_description_">
<annotation tag="user">%1$s fr</annotation>
\'Description\' de
<b>100</b>
\"caractères\" :\n
</string>
<string name="15_min_duration">15 min</string>
<string name="1_day_duration">1 jour</string>
<string name="1_hour_duration">1 heure</string>
<string name="1_month_duration">1 mois</string>
</resources>
Loading
Loading