Add cli commands for users and groups (#1993)

Adds cli commands to manage users and groups.

Co-authored-by: Matthias Thieroff <matthias.thieroff@cloudogu.com>
This commit is contained in:
René Pfeuffer
2022-04-11 10:04:19 +02:00
committed by GitHub
parent edd972b1a8
commit d2e81ce121
51 changed files with 4640 additions and 12 deletions

View File

@@ -1,2 +1,2 @@
- type: added
description: Add cli support with repository actions ([#1987](https://github.com/scm-manager/scm-manager/pull/1987))
description: Add cli support for repositories, users and groups ([#1987](https://github.com/scm-manager/scm-manager/pull/1987), [#1993](https://github.com/scm-manager/scm-manager/pull/1993))

View File

@@ -111,7 +111,15 @@ public class TemplateRenderer {
* @return table for templating content
*/
public Table createTable() {
return new Table(getSpecOrDie().resourceBundle());
return new Table(getBundle());
}
protected CliContext getContext() {
return context;
}
protected ResourceBundle getBundle() {
return getSpecOrDie().resourceBundle();
}
private CommandLine.Model.CommandSpec getSpecOrDie() {
@@ -137,7 +145,7 @@ public class TemplateRenderer {
UnaryOperator<String> upper = value -> value.toUpperCase(context.getLocale());
finalModel.put("upper", upper);
ResourceBundle resourceBundle = getSpecOrDie().resourceBundle();
ResourceBundle resourceBundle = getBundle();
if (resourceBundle != null) {
finalModel.put("i18n", new I18n(resourceBundle));
}

View File

@@ -0,0 +1,30 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.repository.cli;
import picocli.CommandLine;
@CommandLine.Command(name = "group")
public class GroupCommand {}

View File

@@ -0,0 +1,30 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.user.cli;
import picocli.CommandLine;
@CommandLine.Command(name = "user")
public class UserCommand {}

View File

@@ -49,7 +49,12 @@ class CommandValidatorTest {
@Test
void shouldValidateCommand() {
ResourceBundle resourceBundle = ResourceBundle.getBundle("sonia.scm.cli.i18n", Locale.ENGLISH);
ResourceBundle resourceBundle = ResourceBundle.getBundle("sonia.scm.cli.i18n", Locale.ENGLISH, new ResourceBundle.Control() {
@Override
public Locale getFallbackLocale(String baseName, Locale locale) {
return Locale.ROOT;
}
});
when(context.getLocale()).thenReturn(Locale.ENGLISH);
CommandLine commandLine = new CommandLine(Command.class, new TestingCommandFactory());
commandLine.setResourceBundle(resourceBundle);
@@ -63,7 +68,12 @@ class CommandValidatorTest {
@Test
void shouldValidateCommandWithGermanLocale() {
ResourceBundle resourceBundle = ResourceBundle.getBundle("sonia.scm.cli.i18n", Locale.GERMAN);
ResourceBundle resourceBundle = ResourceBundle.getBundle("sonia.scm.cli.i18n", Locale.GERMAN, new ResourceBundle.Control() {
@Override
public Locale getFallbackLocale(String baseName, Locale locale) {
return Locale.ROOT;
}
});
when(context.getLocale()).thenReturn(Locale.GERMAN);
CommandLine commandLine = new CommandLine(Command.class, new TestingCommandFactory());
commandLine.setResourceBundle(resourceBundle);
@@ -79,7 +89,7 @@ class CommandValidatorTest {
public static class Command implements Runnable {
@CommandLine.Mixin
private CommandValidator commandValidator;
private final CommandValidator commandValidator;
@Email
@CommandLine.Option(names = "--mail")

View File

@@ -29,6 +29,7 @@ import picocli.AutoComplete;
import picocli.CommandLine;
import javax.inject.Inject;
import java.util.Locale;
import java.util.ResourceBundle;
public class CliProcessor {
@@ -50,7 +51,7 @@ public class CliProcessor {
CommandFactory factory = new CommandFactory(injector, context);
CommandLine cli = new CommandLine(ScmManagerCommand.class, factory);
cli.getCommandSpec().addMixin("help", usageHelp);
cli.setResourceBundle(ResourceBundle.getBundle("sonia.scm.cli.i18n", context.getLocale()));
cli.setResourceBundle(getBundle("sonia.scm.cli.i18n", context.getLocale()));
for (RegisteredCommandNode c : registry.createCommandTree()) {
CommandLine commandline = createCommandline(context, factory, c);
cli.getCommandSpec().addSubcommand(c.getName(), commandline);
@@ -69,7 +70,7 @@ public class CliProcessor {
ResourceBundle resourceBundle = commandLine.getCommandSpec().resourceBundle();
if (resourceBundle != null) {
String resourceBundleBaseName = resourceBundle.getBaseBundleName();
commandLine.setResourceBundle(ResourceBundle.getBundle(resourceBundleBaseName, context.getLocale()));
commandLine.setResourceBundle(getBundle(resourceBundleBaseName, context.getLocale()));
}
for (RegisteredCommandNode child : command.getChildren()) {
if (!commandLine.getCommandSpec().subcommands().containsKey(child.getName())) {
@@ -80,4 +81,13 @@ public class CliProcessor {
return commandLine;
}
private ResourceBundle getBundle(String baseName, Locale locale) {
return ResourceBundle.getBundle(baseName, locale, new ResourceBundle.Control() {
@Override
public Locale getFallbackLocale(String baseName, Locale locale) {
return Locale.ROOT;
}
});
}
}

View File

@@ -0,0 +1,79 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.group.cli;
import com.google.common.annotations.VisibleForTesting;
import picocli.CommandLine;
import sonia.scm.cli.ParentCommand;
import sonia.scm.group.Group;
import sonia.scm.group.GroupManager;
import sonia.scm.repository.cli.GroupCommand;
import javax.inject.Inject;
import java.util.Arrays;
@ParentCommand(GroupCommand.class)
@CommandLine.Command(name = "add-member", aliases = "add")
class GroupAddMemberCommand implements Runnable {
@CommandLine.Mixin
private final GroupTemplateRenderer templateRenderer;
private final GroupManager manager;
@CommandLine.Parameters(index = "0", arity = "1", descriptionKey = "scm.group.add-member.name")
private String name;
@CommandLine.Parameters(index = "1..", arity = "1..", descriptionKey = "scm.group.add-member.members")
private String[] members;
@Inject
GroupAddMemberCommand(GroupTemplateRenderer templateRenderer, GroupManager manager) {
this.templateRenderer = templateRenderer;
this.manager = manager;
}
@Override
public void run() {
Group existingGroup = manager.get(name);
if (existingGroup == null) {
templateRenderer.renderNotFoundError();
} else {
Arrays.stream(members).forEach(existingGroup::add);
manager.modify(existingGroup);
Group modifiedGroup = manager.get(name);
templateRenderer.render(modifiedGroup);
}
}
@SuppressWarnings("SameParameterValue")
@VisibleForTesting
void setName(String name) {
this.name = name;
}
@VisibleForTesting
void setMembers(String[] members) {
this.members = members;
}
}

View File

@@ -0,0 +1,41 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.group.cli;
import lombok.Data;
import java.util.List;
@Data
class GroupCommandBean {
private String name;
private String description;
private List<String> members;
private String membersList;
private boolean external;
private String creationDate;
private String lastModified;
}

View File

@@ -0,0 +1,55 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.group.cli;
import org.mapstruct.AfterMapping;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget;
import sonia.scm.group.Group;
import java.time.Instant;
import java.time.format.DateTimeFormatter;
@Mapper
interface GroupCommandBeanMapper {
@Mapping(target = "membersList", ignore = true)
GroupCommandBean map(Group group);
@AfterMapping
default void mapMembersList(@MappingTarget GroupCommandBean bean) {
if (bean.getMembers() != null) {
bean.setMembersList(String.join(", ", bean.getMembers()));
}
}
default String mapTimestampToISODate(Long timestamp) {
if (timestamp != null) {
return DateTimeFormatter.ISO_INSTANT.format(Instant.ofEpochMilli(timestamp));
}
return null;
}
}

View File

@@ -0,0 +1,87 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.group.cli;
import com.google.common.annotations.VisibleForTesting;
import picocli.CommandLine;
import sonia.scm.cli.ParentCommand;
import sonia.scm.group.Group;
import sonia.scm.group.GroupManager;
import sonia.scm.repository.cli.GroupCommand;
import javax.inject.Inject;
@ParentCommand(GroupCommand.class)
@CommandLine.Command(name = "create")
class GroupCreateCommand implements Runnable {
@CommandLine.Mixin
private final GroupTemplateRenderer templateRenderer;
private final GroupManager manager;
@CommandLine.Parameters(descriptionKey = "scm.group.create.name")
private String name;
@CommandLine.Option(names = {"--description", "-d"}, descriptionKey = "scm.group.create.desc")
private String description;
@CommandLine.Option(names = {"--member", "-m"}, descriptionKey = "scm.group.create.member")
private String[] members;
@CommandLine.Option(names = {"--external", "-e"}, descriptionKey = "scm.group.create.external")
private boolean external;
@Inject
GroupCreateCommand(GroupTemplateRenderer templateRenderer, GroupManager manager) {
this.templateRenderer = templateRenderer;
this.manager = manager;
}
@Override
public void run() {
Group newGroup = new Group("xml", name, members);
newGroup.setDescription(description);
newGroup.setExternal(external);
Group createdGroup = manager.create(newGroup);
templateRenderer.render(createdGroup);
}
@SuppressWarnings("SameParameterValue")
@VisibleForTesting
void setName(String name) {
this.name = name;
}
@SuppressWarnings("SameParameterValue")
@VisibleForTesting
void setDescription(String description) {
this.description = description;
}
@VisibleForTesting
void setMembers(String[] members) {
this.members = members;
}
}

View File

@@ -0,0 +1,80 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.group.cli;
import com.google.common.annotations.VisibleForTesting;
import picocli.CommandLine;
import sonia.scm.cli.ParentCommand;
import sonia.scm.group.Group;
import sonia.scm.group.GroupManager;
import sonia.scm.repository.cli.GroupCommand;
import javax.inject.Inject;
import java.util.Collections;
@CommandLine.Command(name = "delete", aliases = "rm")
@ParentCommand(GroupCommand.class)
class GroupDeleteCommand implements Runnable {
private static final String PROMPT_TEMPLATE = "{{i18n.groupDeletePrompt}}";
@CommandLine.Parameters(descriptionKey = "scm.group.delete.group", paramLabel = "name")
private String name;
@CommandLine.Option(names = {"--yes", "-y"}, descriptionKey = "scm.group.delete.prompt")
private boolean shouldDelete;
@CommandLine.Mixin
private final GroupTemplateRenderer templateRenderer;
private final GroupManager manager;
@Inject
GroupDeleteCommand(GroupTemplateRenderer templateRenderer, GroupManager manager) {
this.templateRenderer = templateRenderer;
this.manager = manager;
}
@Override
public void run() {
if (!shouldDelete) {
templateRenderer.renderToStderr(PROMPT_TEMPLATE, Collections.emptyMap());
return;
}
Group group = manager.get(name);
if (group != null) {
manager.delete(group);
}
}
@VisibleForTesting
void setName(String name) {
this.name = name;
}
@VisibleForTesting
void setShouldDelete(boolean shouldDelete) {
this.shouldDelete = shouldDelete;
}
}

View File

@@ -0,0 +1,67 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.group.cli;
import com.google.common.annotations.VisibleForTesting;
import picocli.CommandLine;
import sonia.scm.cli.ParentCommand;
import sonia.scm.group.Group;
import sonia.scm.group.GroupManager;
import sonia.scm.repository.cli.GroupCommand;
import javax.inject.Inject;
@ParentCommand(GroupCommand.class)
@CommandLine.Command(name = "get")
class GroupGetCommand implements Runnable{
@CommandLine.Parameters(paramLabel = "name")
private String name;
@CommandLine.Mixin
private final GroupTemplateRenderer templateRenderer;
private final GroupManager manager;
@Inject
GroupGetCommand(GroupTemplateRenderer templateRenderer, GroupManager manager) {
this.templateRenderer = templateRenderer;
this.manager = manager;
}
@VisibleForTesting
void setName(String name) {
this.name = name;
}
@Override
public void run() {
Group group = manager.get(name);
if (group != null) {
templateRenderer.render(group);
} else {
templateRenderer.renderNotFoundError();
}
}
}

View File

@@ -0,0 +1,100 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.group.cli;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableMap;
import picocli.CommandLine;
import sonia.scm.cli.ParentCommand;
import sonia.scm.cli.Table;
import sonia.scm.cli.TemplateRenderer;
import sonia.scm.group.GroupManager;
import sonia.scm.repository.cli.GroupCommand;
import javax.inject.Inject;
import java.util.Collection;
import static java.util.stream.Collectors.toList;
@ParentCommand(GroupCommand.class)
@CommandLine.Command(name = "list", aliases = "ls")
class GroupListCommand implements Runnable {
@CommandLine.Mixin
private final TemplateRenderer templateRenderer;
private final GroupManager manager;
private final GroupCommandBeanMapper beanMapper;
@CommandLine.Spec
private CommandLine.Model.CommandSpec spec;
@CommandLine.Option(names = {"--short", "-s"})
private boolean useShortTemplate;
private static final String TABLE_TEMPLATE = String.join("\n",
"{{#rows}}",
"{{#cols}}{{#row.first}}{{#upper}}{{value}}{{/upper}}{{/row.first}}{{^row.first}}{{value}}{{/row.first}}{{^last}} {{/last}}{{/cols}}",
"{{/rows}}"
);
private static final String SHORT_TEMPLATE = String.join("\n",
"{{#groups}}",
"{{name}}",
"{{/groups}}"
);
@Inject
GroupListCommand(TemplateRenderer templateRenderer, GroupManager manager, GroupCommandBeanMapper beanMapper) {
this.templateRenderer = templateRenderer;
this.manager = manager;
this.beanMapper = beanMapper;
}
@Override
public void run() {
Collection<GroupCommandBean> groupCommandBeans = manager.getAll().stream().map(beanMapper::map).collect(toList());
if (useShortTemplate) {
templateRenderer.renderToStdout(SHORT_TEMPLATE, ImmutableMap.of("groups", groupCommandBeans));
} else {
Table table = templateRenderer.createTable();
table.addHeader("scm.group.name", "scm.group.external");
String yes = spec.resourceBundle().getString("yes");
String no = spec.resourceBundle().getString("no");
for (GroupCommandBean bean : groupCommandBeans) {
table.addRow(bean.getName(), bean.isExternal()? yes: no);
}
templateRenderer.renderToStdout(TABLE_TEMPLATE, ImmutableMap.of("rows", table, "groups", groupCommandBeans));
}
}
@VisibleForTesting
void setUseShortTemplate(boolean useShortTemplate) {
this.useShortTemplate = useShortTemplate;
}
@VisibleForTesting
void setSpec(CommandLine.Model.CommandSpec spec) {
this.spec = spec;
}
}

View File

@@ -0,0 +1,108 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.group.cli;
import com.google.common.annotations.VisibleForTesting;
import picocli.CommandLine;
import sonia.scm.cli.ParentCommand;
import sonia.scm.group.Group;
import sonia.scm.group.GroupManager;
import sonia.scm.repository.cli.GroupCommand;
import javax.inject.Inject;
import static java.util.Arrays.asList;
@ParentCommand(GroupCommand.class)
@CommandLine.Command(name = "modify")
class GroupModifyCommand implements Runnable {
@CommandLine.Mixin
private final GroupTemplateRenderer templateRenderer;
private final GroupManager manager;
@CommandLine.Parameters(descriptionKey = "scm.group.modify.name")
private String name;
@CommandLine.Option(names = {"--description", "-d"}, descriptionKey = "scm.group.modify.desc")
private String description;
@CommandLine.Option(names = {"--member", "-m"}, descriptionKey = "scm.group.modify.member")
private String[] members;
@CommandLine.Option(names = {"--external", "-e"}, descriptionKey = "scm.group.modify.external")
private Boolean external;
@Inject
GroupModifyCommand(GroupTemplateRenderer templateRenderer, GroupManager manager) {
this.templateRenderer = templateRenderer;
this.manager = manager;
}
@Override
public void run() {
Group existingGroup = manager.get(name);
if (existingGroup == null) {
templateRenderer.renderNotFoundError();
} else {
if (description != null) {
existingGroup.setDescription(description);
}
if (external != null) {
existingGroup.setExternal(external);
}
if (members != null) {
existingGroup.setMembers(asList(members));
}
manager.modify(existingGroup);
Group modifiedGroup = manager.get(name);
templateRenderer.render(modifiedGroup);
}
}
@SuppressWarnings("SameParameterValue")
@VisibleForTesting
void setName(String name) {
this.name = name;
}
@SuppressWarnings("SameParameterValue")
@VisibleForTesting
void setDescription(String description) {
this.description = description;
}
@SuppressWarnings("SameParameterValue")
@VisibleForTesting
void setMembers(String[] members) {
this.members = members;
}
@SuppressWarnings("SameParameterValue")
@VisibleForTesting
void setExternal(boolean external) {
this.external = external;
}
}

View File

@@ -0,0 +1,79 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.group.cli;
import com.google.common.annotations.VisibleForTesting;
import picocli.CommandLine;
import sonia.scm.cli.ParentCommand;
import sonia.scm.group.Group;
import sonia.scm.group.GroupManager;
import sonia.scm.repository.cli.GroupCommand;
import javax.inject.Inject;
import java.util.Arrays;
@ParentCommand(GroupCommand.class)
@CommandLine.Command(name = "remove-member", aliases = "remove")
class GroupRemoveMemberCommand implements Runnable {
@CommandLine.Mixin
private final GroupTemplateRenderer templateRenderer;
private final GroupManager manager;
@CommandLine.Parameters(index = "0", arity = "1", descriptionKey = "scm.group.remove-member.name")
private String name;
@CommandLine.Parameters(index = "1..", arity = "1..", descriptionKey = "scm.group.remove-member.members")
private String[] members;
@Inject
GroupRemoveMemberCommand(GroupTemplateRenderer templateRenderer, GroupManager manager) {
this.templateRenderer = templateRenderer;
this.manager = manager;
}
@Override
public void run() {
Group existingGroup = manager.get(name);
if (existingGroup == null) {
templateRenderer.renderNotFoundError();
} else {
Arrays.stream(members).forEach(existingGroup::remove);
manager.modify(existingGroup);
Group modifiedGroup = manager.get(name);
templateRenderer.render(modifiedGroup);
}
}
@SuppressWarnings("SameParameterValue")
@VisibleForTesting
void setName(String name) {
this.name = name;
}
@VisibleForTesting
void setMembers(String[] members) {
this.members = members;
}
}

View File

@@ -0,0 +1,75 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.group.cli;
import com.google.common.collect.ImmutableMap;
import sonia.scm.cli.CliContext;
import sonia.scm.cli.ExitCode;
import sonia.scm.cli.Table;
import sonia.scm.cli.TemplateRenderer;
import sonia.scm.group.Group;
import sonia.scm.template.TemplateEngineFactory;
import javax.inject.Inject;
import java.util.Collections;
class GroupTemplateRenderer extends TemplateRenderer {
private static final String NOT_FOUND_TEMPLATE = "{{i18n.groupNotFound}}";
private static final String DETAILS_TABLE_TEMPLATE = String.join("\n",
"{{#rows}}",
"{{#cols}}{{value}}{{/cols}}",
"{{/rows}}"
);
private final GroupCommandBeanMapper mapper;
@Inject
GroupTemplateRenderer(CliContext context, TemplateEngineFactory templateEngineFactory, GroupCommandBeanMapper mapper) {
super(context, templateEngineFactory);
this.mapper = mapper;
}
void render(Group group) {
GroupCommandBean groupBean = mapper.map(group);
Table table = createTable();
String yes = getBundle().getString("yes");
String no = getBundle().getString("no");
table.addLabelValueRow("scm.group.name", groupBean.getName());
table.addLabelValueRow("scm.group.description", groupBean.getDescription());
table.addLabelValueRow("scm.group.members", groupBean.getMembersList());
table.addLabelValueRow("scm.group.external", groupBean.isExternal() ? yes : no);
table.addLabelValueRow("creationDate", groupBean.getCreationDate());
table.addLabelValueRow("lastModified", groupBean.getLastModified());
renderToStdout(DETAILS_TABLE_TEMPLATE, ImmutableMap.of("rows", table, "repo", groupBean));
}
void renderNotFoundError() {
renderToStderr(NOT_FOUND_TEMPLATE, Collections.emptyMap());
getContext().exit(ExitCode.NOT_FOUND);
}
}

View File

@@ -51,7 +51,7 @@ class RepositoryGetCommand implements Runnable {
}
@VisibleForTesting
public void setRepository(String repository) {
void setRepository(String repository) {
this.repository = repository;
}

View File

@@ -0,0 +1,67 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.user.cli;
import picocli.CommandLine;
import sonia.scm.cli.ParentCommand;
import sonia.scm.user.User;
import sonia.scm.user.UserManager;
import javax.inject.Inject;
@ParentCommand(value = UserCommand.class)
@CommandLine.Command(name = "activate")
class UserActivateCommand implements Runnable {
@CommandLine.Mixin
private final UserTemplateRenderer templateRenderer;
private final UserManager manager;
@CommandLine.Parameters(index = "0", paramLabel = "<username>", descriptionKey = "scm.user.username")
private String username;
@Inject
UserActivateCommand(UserTemplateRenderer templateRenderer, UserManager manager) {
this.templateRenderer = templateRenderer;
this.manager = manager;
}
@Override
public void run() {
User user = manager.get(username);
if (user != null) {
if (user.isExternal()) {
templateRenderer.renderExternalActivateError();
} else {
user.setActive(true);
manager.modify(user);
templateRenderer.render(user);
}
} else {
templateRenderer.renderNotFoundError();
}
}
}

View File

@@ -0,0 +1,44 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.user.cli;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@NoArgsConstructor
class UserCommandBean {
private String name;
private String displayName;
private String mail;
private boolean external;
private boolean active;
private String creationDate;
private String lastModified;
}

View File

@@ -0,0 +1,45 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.user.cli;
import org.mapstruct.Mapper;
import sonia.scm.user.User;
import java.time.Instant;
import java.time.format.DateTimeFormatter;
@Mapper
interface UserCommandBeanMapper {
UserCommandBean map(User modelObject);
default String mapTimestampToISODate(Long timestamp) {
if (timestamp != null) {
return DateTimeFormatter.ISO_INSTANT.format(Instant.ofEpochMilli(timestamp));
}
return null;
}
}

View File

@@ -0,0 +1,63 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.user.cli;
import picocli.CommandLine;
import sonia.scm.cli.ParentCommand;
import sonia.scm.user.User;
import sonia.scm.user.UserManager;
import javax.inject.Inject;
@ParentCommand(value = UserCommand.class)
@CommandLine.Command(name = "convert-to-external", aliases = "conv-ext")
class UserConvertToExternalCommand implements Runnable {
@CommandLine.Mixin
private final UserTemplateRenderer templateRenderer;
private final UserManager manager;
@CommandLine.Parameters(index = "0", paramLabel = "<username>", descriptionKey = "scm.user.username")
private String username;
@Inject
UserConvertToExternalCommand(UserTemplateRenderer templateRenderer, UserManager manager) {
this.templateRenderer = templateRenderer;
this.manager = manager;
}
@Override
public void run() {
User user = manager.get(username);
if (user != null) {
user.setExternal(true);
manager.modify(user);
templateRenderer.render(user);
} else {
templateRenderer.renderNotFoundError();
}
}
}

View File

@@ -0,0 +1,74 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.user.cli;
import com.google.common.annotations.VisibleForTesting;
import picocli.CommandLine;
import sonia.scm.cli.ParentCommand;
import sonia.scm.user.User;
import sonia.scm.user.UserManager;
import javax.inject.Inject;
@ParentCommand(value = UserCommand.class)
@CommandLine.Command(name = "convert-to-internal", aliases = "conv-int")
class UserConvertToInternalCommand implements Runnable {
@CommandLine.Mixin
private final UserTemplateRenderer templateRenderer;
private final UserManager manager;
@CommandLine.Parameters(index = "0", paramLabel = "<username>", descriptionKey = "scm.user.username")
private String username;
@CommandLine.Parameters(index = "1", paramLabel = "<password>", descriptionKey = "scm.user.password")
private String password;
@Inject
UserConvertToInternalCommand(UserTemplateRenderer templateRenderer, UserManager manager) {
this.templateRenderer = templateRenderer;
this.manager = manager;
}
@Override
public void run() {
User user = manager.get(username);
if (user != null) {
user.setExternal(false);
user.setPassword(password);
manager.modify(user);
templateRenderer.render(user);
} else {
templateRenderer.renderNotFoundError();
}
}
@SuppressWarnings("SameParameterValue")
@VisibleForTesting
void setPassword(String password) {
this.password = password;
}
}

View File

@@ -0,0 +1,133 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.user.cli;
import com.google.common.annotations.VisibleForTesting;
import picocli.CommandLine;
import sonia.scm.cli.CommandValidator;
import sonia.scm.cli.ParentCommand;
import sonia.scm.user.User;
import sonia.scm.user.UserManager;
import javax.inject.Inject;
import javax.validation.constraints.Email;
@CommandLine.Command(name = "create")
@ParentCommand(value = UserCommand.class)
class UserCreateCommand implements Runnable {
@CommandLine.Mixin
private final UserTemplateRenderer templateRenderer;
@CommandLine.Mixin
private final CommandValidator validator;
private final UserManager manager;
@CommandLine.Parameters(index = "0", paramLabel = "<username>", descriptionKey = "scm.user.username")
private String username;
@CommandLine.Parameters(index = "1", paramLabel = "<displayname>", descriptionKey = "scm.user.displayName")
private String displayName;
@Email
@CommandLine.Option(names = {"--email", "-e"}, descriptionKey = "scm.user.email")
private String email;
@CommandLine.Option(names = {"--external", "-x"}, descriptionKey = "scm.user.create.external")
private boolean external;
@CommandLine.Option(names = {"--password", "-p"}, descriptionKey = "scm.user.password")
private String password;
@CommandLine.Option(names = {"--deactivate", "-d"}, descriptionKey = "scm.user.create.deactivate")
private boolean inactive;
@Inject
public UserCreateCommand(UserTemplateRenderer templateRenderer,
CommandValidator validator,
UserManager manager) {
this.templateRenderer = templateRenderer;
this.validator = validator;
this.manager = manager;
}
@Override
public void run() {
validator.validate();
User newUser = new User();
newUser.setName(username);
newUser.setDisplayName(displayName);
newUser.setMail(email);
newUser.setExternal(external);
if (!external) {
if (password == null) {
templateRenderer.renderPasswordError();
}
newUser.setPassword(password);
newUser.setActive(!inactive);
} else {
if (inactive) {
templateRenderer.renderExternalDeactivateError();
}
}
User createdUser = manager.create(newUser);
templateRenderer.render(createdUser);
}
@SuppressWarnings("SameParameterValue")
@VisibleForTesting
void setUsername(String username) {
this.username = username;
}
@SuppressWarnings("SameParameterValue")
@VisibleForTesting
void setDisplayName(String displayName) {
this.displayName = displayName;
}
@SuppressWarnings("SameParameterValue")
@VisibleForTesting
void setEmail(String email) {
this.email = email;
}
@SuppressWarnings("SameParameterValue")
@VisibleForTesting
void setExternal(boolean external) {
this.external = external;
}
@SuppressWarnings("SameParameterValue")
@VisibleForTesting
void setPassword(String password) {
this.password = password;
}
@SuppressWarnings("SameParameterValue")
@VisibleForTesting
void setInactive(boolean inactive) {
this.inactive = inactive;
}
}

View File

@@ -0,0 +1,67 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.user.cli;
import picocli.CommandLine;
import sonia.scm.cli.ParentCommand;
import sonia.scm.user.User;
import sonia.scm.user.UserManager;
import javax.inject.Inject;
@ParentCommand(value = UserCommand.class)
@CommandLine.Command(name = "deactivate")
class UserDeactivateCommand implements Runnable {
@CommandLine.Mixin
private final UserTemplateRenderer templateRenderer;
private final UserManager manager;
@CommandLine.Parameters(index = "0", paramLabel = "<username>", descriptionKey = "scm.user.username")
private String username;
@Inject
UserDeactivateCommand(UserTemplateRenderer templateRenderer, UserManager manager) {
this.templateRenderer = templateRenderer;
this.manager = manager;
}
@Override
public void run() {
User user = manager.get(username);
if (user != null) {
if (user.isExternal()) {
templateRenderer.renderExternalDeactivateError();
} else {
user.setActive(false);
manager.modify(user);
templateRenderer.render(user);
}
} else {
templateRenderer.renderNotFoundError();
}
}
}

View File

@@ -0,0 +1,77 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.user.cli;
import com.google.common.annotations.VisibleForTesting;
import picocli.CommandLine;
import sonia.scm.cli.ParentCommand;
import sonia.scm.user.User;
import sonia.scm.user.UserManager;
import javax.inject.Inject;
import java.util.Collections;
@CommandLine.Command(name = "delete", aliases = "rm")
@ParentCommand(UserCommand.class)
class UserDeleteCommand implements Runnable {
private static final String CONFIRM_TEMPLATE = "{{i18n.scmUserDeleteConfirm}}";
private static final String SUCCESS_TEMPLATE = "{{i18n.scmUserDeleteSuccess}}";
@CommandLine.Parameters(index = "0", paramLabel = "<username>", descriptionKey = "scm.user.username")
private String username;
@CommandLine.Option(names = {"--yes", "-y"}, descriptionKey = "scm.user.delete.prompt")
private boolean shouldDelete;
@CommandLine.Mixin
private final UserTemplateRenderer templateRenderer;
private final UserManager manager;
@Inject
public UserDeleteCommand(UserTemplateRenderer templateRenderer, UserManager manager) {
this.templateRenderer = templateRenderer;
this.manager = manager;
}
@Override
public void run() {
if (!shouldDelete) {
templateRenderer.renderToStderr(CONFIRM_TEMPLATE, Collections.emptyMap());
return;
}
User user = manager.get(username);
if (user != null) {
manager.delete(user);
templateRenderer.renderToStdout(SUCCESS_TEMPLATE, Collections.emptyMap());
}
}
@SuppressWarnings("SameParameterValue")
@VisibleForTesting
void setShouldDelete(boolean shouldDelete) {
this.shouldDelete = shouldDelete;
}
}

View File

@@ -0,0 +1,60 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.user.cli;
import picocli.CommandLine;
import sonia.scm.cli.ParentCommand;
import sonia.scm.user.User;
import sonia.scm.user.UserManager;
import javax.inject.Inject;
@ParentCommand(value = UserCommand.class)
@CommandLine.Command(name = "get")
class UserGetCommand implements Runnable {
@CommandLine.Parameters(index = "0", paramLabel = "<username>", descriptionKey = "scm.user.username")
private String username;
@CommandLine.Mixin
private final UserTemplateRenderer templateRenderer;
private final UserManager manager;
@Inject
UserGetCommand(UserTemplateRenderer templateRenderer, UserManager manager) {
this.templateRenderer = templateRenderer;
this.manager = manager;
}
@Override
public void run() {
User user = manager.get(username);
if (user != null) {
templateRenderer.render(user);
} else {
templateRenderer.renderNotFoundError();
}
}
}

View File

@@ -0,0 +1,99 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.user.cli;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableMap;
import picocli.CommandLine;
import sonia.scm.cli.ParentCommand;
import sonia.scm.cli.Table;
import sonia.scm.cli.TemplateRenderer;
import sonia.scm.user.UserManager;
import javax.inject.Inject;
import java.util.Collection;
import java.util.stream.Collectors;
@ParentCommand(value = UserCommand.class)
@CommandLine.Command(name = "list", aliases = "ls")
class UserListCommand implements Runnable {
@CommandLine.Mixin
private final TemplateRenderer templateRenderer;
private final UserManager manager;
private final UserCommandBeanMapper mapper;
@CommandLine.Spec
private CommandLine.Model.CommandSpec spec;
@CommandLine.Option(names = {"--short", "-s"})
private boolean useShortTemplate;
private static final String TABLE_TEMPLATE = String.join("\n",
"{{#rows}}",
"{{#cols}}{{#row.first}}{{#upper}}{{value}}{{/upper}}{{/row.first}}{{^row.first}}{{value}}{{/row.first}}{{^last}} {{/last}}{{/cols}}",
"{{/rows}}"
);
private static final String SHORT_TEMPLATE = String.join("\n",
"{{#users}}",
"{{name}}",
"{{/users}}"
);
@Inject
public UserListCommand(TemplateRenderer templateRenderer, UserManager manager, UserCommandBeanMapper mapper) {
this.templateRenderer = templateRenderer;
this.manager = manager;
this.mapper = mapper;
}
@Override
public void run() {
Collection<UserCommandBean> beans = manager.getAll().stream().map(mapper::map).collect(Collectors.toList());
if (useShortTemplate) {
templateRenderer.renderToStdout(SHORT_TEMPLATE, ImmutableMap.of("users", beans));
} else {
Table table = templateRenderer.createTable();
String yes = spec.resourceBundle().getString("yes");
String no = spec.resourceBundle().getString("no");
table.addHeader("scm.user.username", "scm.user.displayName", "scm.user.email", "scm.user.external", "scm.user.active");
for (UserCommandBean bean : beans) {
table.addRow(bean.getName(), bean.getDisplayName(), bean.getMail(), bean.isExternal() ? yes : no, bean.isActive() ? yes : no);
}
templateRenderer.renderToStdout(TABLE_TEMPLATE, ImmutableMap.of("rows", table, "users", beans));
}
}
@SuppressWarnings("SameParameterValue")
@VisibleForTesting
void setUseShortTemplate(boolean useShortTemplate) {
this.useShortTemplate = useShortTemplate;
}
@VisibleForTesting
void setSpec(CommandLine.Model.CommandSpec spec) {
this.spec = spec;
}
}

View File

@@ -0,0 +1,107 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.user.cli;
import com.google.common.annotations.VisibleForTesting;
import picocli.CommandLine;
import sonia.scm.cli.CommandValidator;
import sonia.scm.cli.ParentCommand;
import sonia.scm.user.User;
import sonia.scm.user.UserManager;
import javax.inject.Inject;
import javax.validation.constraints.Email;
@ParentCommand(value = UserCommand.class)
@CommandLine.Command(name = "modify")
class UserModifyCommand implements Runnable {
@CommandLine.Mixin
private final UserTemplateRenderer templateRenderer;
@CommandLine.Mixin
private final CommandValidator validator;
private final UserManager manager;
@CommandLine.Parameters(index = "0", paramLabel = "<username>", descriptionKey = "scm.user.username")
private String username;
@CommandLine.Option(names = {"--displayname", "-d"}, descriptionKey = "scm.user.displayName")
private String displayName;
@Email
@CommandLine.Option(names = {"--email", "-e"}, descriptionKey = "scm.user.email")
private String email;
@CommandLine.Option(names = {"--password", "-p"}, descriptionKey = "scm.user.password")
private String password;
@Inject
UserModifyCommand(UserTemplateRenderer templateRenderer, CommandValidator validator, UserManager manager) {
this.templateRenderer = templateRenderer;
this.validator = validator;
this.manager = manager;
}
@Override
public void run() {
validator.validate();
User user = manager.get(username);
if (user != null) {
if (displayName != null) {
user.setDisplayName(displayName);
}
if (email != null) {
user.setMail(email);
}
if (password != null) {
user.setPassword(password);
}
manager.modify(user);
templateRenderer.render(user);
} else {
templateRenderer.renderNotFoundError();
}
}
@SuppressWarnings("SameParameterValue")
@VisibleForTesting
void setDisplayName(String displayName) {
this.displayName = displayName;
}
@SuppressWarnings("SameParameterValue")
@VisibleForTesting
void setEmail(String email) {
this.email = email;
}
@SuppressWarnings("SameParameterValue")
@VisibleForTesting
void setPassword(String password) {
this.password = password;
}
}

View File

@@ -0,0 +1,95 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.user.cli;
import com.google.common.collect.ImmutableMap;
import sonia.scm.cli.CliContext;
import sonia.scm.cli.ExitCode;
import sonia.scm.cli.Table;
import sonia.scm.cli.TemplateRenderer;
import sonia.scm.template.TemplateEngineFactory;
import sonia.scm.user.User;
import javax.inject.Inject;
import java.util.Collections;
class UserTemplateRenderer extends TemplateRenderer {
private static final String DETAILS_TABLE_TEMPLATE = String.join("\n",
"{{#rows}}",
"{{#cols}}{{value}}{{/cols}}",
"{{/rows}}"
);
private static final String PASSWORD_ERROR_TEMPLATE = "{{i18n.scmUserErrorPassword}}";
private static final String EXTERNAL_ACTIVATE_TEMPLATE = "{{i18n.scmUserErrorExternalActivate}}";
private static final String EXTERNAL_DEACTIVATE_TEMPLATE = "{{i18n.scmUserErrorExternalDeactivate}}";
private static final String NOT_FOUND_TEMPLATE = "{{i18n.scmUserErrorNotFound}}";
private final CliContext context;
private final UserCommandBeanMapper mapper;
@Inject
UserTemplateRenderer(CliContext context, TemplateEngineFactory templateEngineFactory, UserCommandBeanMapper mapper) {
super(context, templateEngineFactory);
this.context = context;
this.mapper = mapper;
}
public void render(User user) {
Table table = createTable();
String yes = getBundle().getString("yes");
String no = getBundle().getString("no");
UserCommandBean bean = mapper.map(user);
table.addLabelValueRow("scm.user.username", bean.getName());
table.addLabelValueRow("scm.user.displayName", bean.getDisplayName());
table.addLabelValueRow("scm.user.email", bean.getMail());
table.addLabelValueRow("scm.user.external", bean.isExternal() ? yes : no);
table.addLabelValueRow("scm.user.active", bean.isActive() ? yes : no);
table.addLabelValueRow("creationDate", bean.getCreationDate());
table.addLabelValueRow("lastModified", bean.getLastModified());
renderToStdout(DETAILS_TABLE_TEMPLATE, ImmutableMap.of("rows", table, "user", bean));
}
public void renderPasswordError() {
renderToStderr(PASSWORD_ERROR_TEMPLATE, Collections.emptyMap());
context.exit(ExitCode.USAGE);
}
public void renderExternalActivateError() {
renderToStderr(EXTERNAL_ACTIVATE_TEMPLATE, Collections.emptyMap());
context.exit(ExitCode.USAGE);
}
public void renderExternalDeactivateError() {
renderToStderr(EXTERNAL_DEACTIVATE_TEMPLATE, Collections.emptyMap());
context.exit(ExitCode.USAGE);
}
public void renderNotFoundError() {
renderToStderr(NOT_FOUND_TEMPLATE, Collections.emptyMap());
context.exit(ExitCode.NOT_FOUND);
}
}

View File

@@ -28,6 +28,11 @@ errorLabel= ERROR
errorExecutionFailed = Error
transactionId = Transaction ID
creationDate = Creation Date
lastModified = Last Modified
yes = yes
no = no
repoNamespace = Namespace
repoName = Name
repoDescription= Description
@@ -54,12 +59,12 @@ scm.logout.usage.description.0 = Removes the api key from server and local cli c
scm.ping.usage.description.0 = Returns PONG if the server is available
## Repo
scm.repo.usage.description.0 = Parent command for all repository-related actions
scm.repo.usage.description.0 = Resource command for all repository-related actions
repoNotFound= Could not find repository
repoInvalidInput= Invalid input. Use namespace/name
### Get repo
scm.repo.get.usage.description.0 = Returns repository related information
scm.repo.get.usage.description.0 = Returns repository-related information
### List repo
scm.repo.list.usage.description.0 = List all repositories on server
@@ -81,3 +86,102 @@ repoDeletePrompt= If you really want to delete this repository please pass --yes
### Modify repo
scm.repo.modify.usage.description.0 = Modify repository on server
scm.repo.modify.repository = Repository namespace/name
## User
scm.user.username = Username
scm.user.displayName = Display Name
scm.user.email = Email address
scm.user.external = External
scm.user.password = Password
scm.user.active = Active
### User error
scmUserErrorPassword = Password is required for internal users
scmUserErrorExternalActivate = External users cannot be activated
scmUserErrorExternalDeactivate = External users cannot be deactivated
scmUserErrorNotFound = Could not find user
### User command
scm.user.usage.description.0 = Resource command for all user-related actions
### Get user
scm.user.get.usage.description.0 = Returns user-related information
### List user
scm.user.list.usage.description.0 = List all users on server
### Create user
scm.user.create.usage.description.0 = Creates new user on server
scm.user.create.deactivate = User is deactivated
scm.user.create.external = User is managed by an external system
### Delete user
scm.user.delete.usage.description.0 = Deletes user on server
scm.user.delete.prompt = Set this flag to agree to delete the user
scmUserDeleteConfirm = If you really want to delete this user please pass --yes
scmUserDeleteSuccess = Successfully deleted user
### Modify user
scm.user.modify.usage.description.0 = Modify user on server
### Activate user
scm.user.activate.usage.description.0 = Activate user on server
### Deactivate user
scm.user.deactivate.usage.description.0 = Deactivate user on server
### Convert to external user
scm.user.convert-to-external.usage.description.0 = Convert user to external user on server
### Convert to internal user
scm.user.convert-to-internal.usage.description.0 = Convert user to internal user on server
### Group
scm.group.name = Name
scm.group.description = Description
scm.group.members = Members
scm.group.membersList = Members
scm.group.external = External
### Group command
scm.group.usage.description.0 = Resource command for all group-related actions
### Get group
scm.group.get.usage.description.0 = Returns group-related information
scm.group.get.name = Name of the group
### List group
scm.group.list.usage.description.0 = List all groups on server
scm.group.list.short = List only names of groups
### Create group
scm.group.create.usage.description.0 = Creates new group on server
scm.group.create.name = Name of the group
scm.group.create.description = Description of the new group
scm.group.create.member = Members of the new group
scm.group.create.external = Group is managed by an external system
### Delete group
scm.group.delete.usage.description.0 = Delete group on server
scm.group.delete.group = Name of the group
scm.group.delete.prompt = Set this flag to agree to delete the group
groupDeletePrompt = If you really want to delete this group please pass --yes
### Modify group
scm.group.modify.usage.description.0 = Modify group on server
scm.group.modify.name = Name of the group
scm.group.modify.desc = New description of the group
scm.group.modify.member = New members of the group
scm.group.modify.external = Whether the group is an external group or not
### Add members to group
scm.group.add-member.usage.description.0 = Add members to an existing group
scm.group.add-member.name = Name of the group
scm.group.add-member.members = Members to add to the group
### Remove members form group
scm.group.remove-member.usage.description.0 = Remove members from an existing group
scm.group.remove-member.name = Name of the group
scm.group.remove-member.members = Members to remove from the group
groupNotFound = Could not find group

View File

@@ -28,6 +28,11 @@ errorLabel= FEHLER
errorExecutionFailed = Fehler
transactionId = Transaktions-ID
creationDate = Erstellt
lastModified = Zuletzt bearbeitet
yes = ja
no = nein
repoNamespace = Namespace
repoName = Name
repoDescription= Beschreibung
@@ -54,7 +59,7 @@ scm.logout.usage.description.0 = Entfernt den API Key vom Server und die lokale
scm.ping.usage.description.0 = Antwortet PONG, wenn der Server erreichbar ist
## Repo
scm.repo.usage.description.0 = Gruppen Befehl f<>r alle Repository Befehle
scm.repo.usage.description.0 = Ressourcenkommando f<>r alle Repositoryaktionen
repoNotFound= Repository konnte nicht gefunden werden
repoInvalidInput= Ung<EFBFBD>ltige Eingabe. Nutzen Sie namespace/name
@@ -81,3 +86,102 @@ repoDeletePrompt= Wenn dieses Repository endg
### Modify repo
scm.repo.modify.usage.description.0 = Aktualisiert ein Repository auf dem Server
scm.repo.modify.repository = Repository Namespace/Name
## User
scm.user.username = Benutzername
scm.user.displayName = Anzeigename
scm.user.email = E-Mail-Adresse
scm.user.external = Extern
scm.user.password = Passwort
scm.user.active = Aktiv
### User error
scmUserErrorPassword = F<EFBFBD>r interne Benutzer muss ein Passwort gesetzt werden
scmUserErrorExternalActivate = Externe Benutzer k<>nnen nicht aktiviert werden
scmUserErrorExternalDeactivate = Externe Benutzer k<>nnen nicht deaktiviert werden
scmUserErrorNotFound= Benutzer konnte nicht gefunden werden
### User command
scm.user.usage.description.0 = Ressourcenkommando f<>r alle Benutzeraktionen
### Get user
scm.user.get.usage.description.0 = Liefert Informationen zum Benutzer
### List user
scm.user.list.usage.description.0 = Listet alle Benutzer auf dem Server
### Create user
scm.user.create.usage.description.0 = Erzeugt einen neuen Benutzer auf dem Server
scm.user.create.deactivate = Benutzer ist deaktiviert
scm.user.create.external = Der Benutzer wird von einem Fremdsystem verwaltet
### Delete user
scm.user.delete.usage.description.0 = L<EFBFBD>scht einen Benutzer auf dem Server
scm.user.delete.prompt = Setzen Sie diese Option, um einen Benutzer endg<64>ltig zu l<>schen
scmUserDeleteConfirm = Wenn dieser Benutzer endg<64>ltig gel<65>scht werden soll, setzen Sie bitte --yes
scmUserDeleteSuccess = Benutzer erfolgreich gel<65>scht
### Modify user
scm.user.modify.usage.description.0 = Aktualisiert einen Benutzer auf dem Server
### Activate user
scm.user.activate.usage.description.0 = Aktiviert einen Benutzer auf dem Server
### Deactivate user
scm.user.deactivate.usage.description.0 = Deaktiviert einen Benutzer auf dem Server
### Convert to external user
scm.user.convert-to-external.usage.description.0 = Konvertiert einen Benutzer zu einem externen Benutzer
### Convert to internal user
scm.user.convert-to-internal.usage.description.0 = Konvertiert einen Benutzer zu einem internen Benutzer
### Group
scm.group.name = Name
scm.group.description = Beschreibung
scm.group.members = Mitglieder
scm.group.membersList = Mitglieder
scm.group.external = Extern
### Group command
scm.group.usage.description.0 = Ressourcenkommando f<>r alle Gruppenaktionen
### Get group
scm.group.get.usage.description.0 = Liefert Informationen zur Gruppe
scm.group.get.name = Name der Gruppe
### List group
scm.group.list.usage.description.0 = Listet alle Gruppen auf dem Server
scm.group.list.short = Listet nur die Namen der Gruppen
### Create group
scm.group.create.usage.description.0 = Erzeugt eine neue Gruppe auf dem Server
scm.group.create.name = Name der Gruppe
scm.group.create.description = Beschreibung der Gruppe
scm.group.create.member = Mitglieder der Gruppe
scm.group.create.external = Die Gruppe wird von einem externen System verwaltet
### Delete group
scm.group.delete.usage.description.0 = L<EFBFBD>scht eine Gruppe auf dem Server
scm.group.delete.group = Name der Gruppe
scm.group.delete.prompt = Setzen Sie diese Option, um eine Gruppe endg<64>ltig zu l<>schen
groupDeletePrompt = Wenn diese Gruppe endg<64>ltig gel<65>scht werden soll, setzen Sie bitte --yes
### Modify group
scm.group.modify.usage.description.0 = <EFBFBD>ndert eine Gruppe auf dem Server
scm.group.modify.name = Name der Gruppe
scm.group.modify.desc = Neue Beschreibung der Gruppe
scm.group.modify.member = Neue Mitglieder der Gruppe
scm.group.modify.external = Flag, ob diese Gruppe von einem Fremdsystem verwaltet wird
### Add members to group
scm.group.add-member.usage.description.0 = F<EFBFBD>gt neue Mitglieder zu einer Gruppe hinzu
scm.group.add-member.name = Name der Gruppe
scm.group.add-member.members = Hinzuzuf<EFBFBD>gende Mitglieder
### Remove members form group
scm.group.remove-member.usage.description.0 = Entfernt Mitglieder von einer Gruppe
scm.group.remove-member.name = Name der Gruppe
scm.group.remove-member.members = Zu l<>schende Mitglieder
groupNotFound = Gruppe konnte nicht gefunden werden

View File

@@ -0,0 +1,117 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.cli;
import com.google.common.io.Resources;
import picocli.CommandLine;
import sonia.scm.template.MustacheTemplateTestEngine;
import sonia.scm.template.TemplateEngine;
import sonia.scm.template.TemplateEngineFactory;
import javax.servlet.ServletContext;
import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
import java.util.Locale;
import java.util.ResourceBundle;
import static java.util.Collections.singleton;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
* This is a helper to create a {@link TemplateRenderer} that can be used for
* command tests
*/
public class TemplateTestRenderer {
private final ByteArrayOutputStream stdOut = new ByteArrayOutputStream();
private final ByteArrayOutputStream stdErr = new ByteArrayOutputStream();
private final CliContext context = mock(CliContext.class);
private final TemplateEngineFactory templateEngineFactory;
@SuppressWarnings("UnstableApiUsage")
public TemplateTestRenderer() {
lenient().when(context.getStdout()).thenReturn(new PrintWriter(stdOut));
lenient().when(context.getStderr()).thenReturn(new PrintWriter(stdErr));
lenient().doThrow(CliExitException.class).when(context).exit(anyInt());
ServletContext servletContext = mock(ServletContext.class);
lenient().when(servletContext.getResourceAsStream(any()))
.thenAnswer(invocation -> Resources.getResource(invocation.getArgument(0, String.class)).openStream());
TemplateEngine engine = new MustacheTemplateTestEngine().createEngine(servletContext);
when(context.getLocale()).thenReturn(new Locale("en"));
templateEngineFactory = new TemplateEngineFactory(singleton(engine), engine);
}
public TemplateRenderer createTemplateRenderer() {
return new TemplateRenderer(context, templateEngineFactory) {
@Override
protected ResourceBundle getBundle() {
return TemplateTestRenderer.this.getResourceBundle();
}
};
}
public ResourceBundle getResourceBundle() {
return ResourceBundle.getBundle("sonia.scm.cli.i18n", context.getLocale(), new ResourceBundle.Control() {
@Override
public Locale getFallbackLocale(String baseName, Locale locale) {
return Locale.ROOT;
}
});
}
public void setLocale(String locale) {
lenient().when(context.getLocale()).thenReturn(new Locale(locale));
}
public String getStdOut() {
return stdOut.toString();
}
public String getStdErr() {
return stdErr.toString();
}
public CliContext getContextMock() {
return context;
}
public TemplateEngineFactory getTemplateEngineFactory() {
return templateEngineFactory;
}
public CommandLine.Model.CommandSpec getMockedSpeck() {
ResourceBundle resourceBundle = getResourceBundle();
CommandLine.Model.CommandSpec mock = mock(CommandLine.Model.CommandSpec.class);
lenient().when(mock.resourceBundle()).thenReturn(resourceBundle);
return mock;
}
}

View File

@@ -0,0 +1,122 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.group.cli;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import sonia.scm.cli.CliExitException;
import sonia.scm.group.Group;
import sonia.scm.group.GroupManager;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class GroupAddMemberCommandTest {
private final GroupTemplateTestRenderer testRenderer = new GroupTemplateTestRenderer();
@Mock
private GroupManager manager;
private GroupAddMemberCommand command;
@BeforeEach
void initCommand() {
command = new GroupAddMemberCommand(testRenderer.getTemplateRenderer(), manager);
}
@Nested
class ForSuccessfulModificationTest {
private Group group;
@BeforeEach
void mockModification() {
group = new Group("test", "hog", "zaphod");
when(manager.get("hog")).thenAnswer(invocation -> group);
doAnswer(invocation -> {
Group modifiedGroup = invocation.getArgument(0, Group.class);
modifiedGroup.setLastModified(1649662000000L);
group = modifiedGroup;
return null;
}).when(manager).modify(any());
command.setName("hog");
command.setMembers(new String[]{"trillian", "arthur", "ford"});
}
@Test
void shouldModifyGroup() {
command.run();
verify(manager).modify(argThat(argument -> {
assertThat(argument.getName()).isEqualTo("hog");
assertThat(argument.getMembers()).contains("zaphod", "trillian", "arthur", "ford");
return true;
}));
}
@Test
void shouldPrintGroupAfterModification() {
command.run();
assertThat(testRenderer.getStdOut())
.contains(
"Name: hog",
"Members: zaphod, trillian, arthur, ford",
"Last Modified: 2022-04-11T07:26:40Z"
);
assertThat(testRenderer.getStdErr())
.isEmpty();
}
}
@Test
void shouldFailIfGroupDoesNotExists() {
when(manager.get("hog")).thenReturn(null);
command.setName("hog");
Assertions.assertThrows(
CliExitException.class,
() -> command.run()
);
assertThat(testRenderer.getStdOut())
.isEmpty();
assertThat(testRenderer.getStdErr())
.isEqualTo("Could not find group");
}
}

View File

@@ -0,0 +1,103 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.group.cli;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import sonia.scm.group.Group;
import sonia.scm.group.GroupManager;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class GroupCreateCommandTest {
private final GroupTemplateTestRenderer testRenderer = new GroupTemplateTestRenderer();
@Mock
private GroupManager manager;
private GroupCreateCommand command;
@BeforeEach
void initCommand() {
command = new GroupCreateCommand(testRenderer.getTemplateRenderer(), manager);
}
@Nested
class ForSuccessfulCreationTest {
@BeforeEach
void mockCreation() {
when(manager.create(any()))
.thenAnswer(invocation -> {
Group createdGroup = invocation.getArgument(0, Group.class);
createdGroup.setCreationDate(1649262000000L);
return createdGroup;
});
command.setName("hog");
command.setDescription("Crew of the Heart of Gold");
command.setMembers(new String[]{"zaphod", "trillian"});
}
@Test
void shouldCreateGroup() {
command.run();
verify(manager).create(argThat(argument -> {
assertThat(argument.getName()).isEqualTo("hog");
assertThat(argument.getDescription()).isEqualTo("Crew of the Heart of Gold");
assertThat(argument.getMembers()).contains("zaphod", "trillian");
return true;
}));
}
@Test
void shouldPrintGroupAfterCreation() {
command.run();
assertThat(testRenderer.getStdOut())
.contains(
"Name: hog",
"Description: Crew of the Heart of Gold",
"Members: zaphod, trillian",
"External: no",
"Creation Date: 2022-04-06T16:20:00Z",
"Last Modified: "
);
assertThat(testRenderer.getStdErr())
.isEmpty();
}
}
}

View File

@@ -0,0 +1,98 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.group.cli;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import sonia.scm.group.Group;
import sonia.scm.group.GroupManager;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class GroupDeleteCommandTest {
@Mock
private GroupManager manager;
private final GroupTemplateTestRenderer testRenderer = new GroupTemplateTestRenderer();
private GroupDeleteCommand command;
@BeforeEach
void initCommand() {
command = new GroupDeleteCommand(testRenderer.getTemplateRenderer(), manager);
}
@Test
void shouldRenderPromptWithoutYesFlag() {
command.setName("hog");
command.run();
assertThat(testRenderer.getStdOut())
.isEmpty();
assertThat(testRenderer.getStdErr())
.isEqualTo("If you really want to delete this group please pass --yes");
}
@Test
void shouldDeleteGroup() {
Group groupToDelete = new Group("test", "vogons");
when(manager.get("vogons")).thenReturn(groupToDelete);
command.setShouldDelete(true);
command.setName("vogons");
command.run();
verify(manager).delete(groupToDelete);
assertThat(testRenderer.getStdOut())
.isEmpty();
assertThat(testRenderer.getStdErr())
.isEmpty();
}
@Test
void shouldNotFailForNotExistingGroup() {
when(manager.get("vogons")).thenReturn(null);
command.setShouldDelete(true);
command.setName("vogons");
command.run();
verify(manager, never()).delete(any());
assertThat(testRenderer.getStdOut())
.isEmpty();
assertThat(testRenderer.getStdErr())
.isEmpty();
}
}

View File

@@ -0,0 +1,95 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.group.cli;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import sonia.scm.cli.CliExitException;
import sonia.scm.group.Group;
import sonia.scm.group.GroupManager;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class GroupGetCommandTest {
private final GroupTemplateTestRenderer testRenderer = new GroupTemplateTestRenderer();
@Mock
private GroupManager manager;
private GroupGetCommand command;
@BeforeEach
void initCommand() {
command = new GroupGetCommand(testRenderer.getTemplateRenderer(), manager);
}
@Test
void shouldGetGroup() {
Group group = new Group("test", "hog", "zaphod", "trillian");
group.setCreationDate(1649262000000L);
group.setLastModified(1649462000000L);
group.setDescription("Crew of the Heart of Gold");
when(manager.get("hog")).thenReturn(group);
command.setName("hog");
command.run();
assertThat(testRenderer.getStdOut())
.contains(
"Name: hog",
"Description: Crew of the Heart of Gold",
"Members: zaphod, trillian",
"External: no",
"Creation Date: 2022-04-06T16:20:00Z",
"Last Modified: 2022-04-08T23:53:20Z"
);
assertThat(testRenderer.getStdErr())
.isEmpty();
}
@Test
void shouldFailForNotExistingGroup() {
command.setName("hog");
Assertions.assertThrows(
CliExitException.class,
() -> command.run()
);
assertThat(testRenderer.getStdOut())
.isEmpty();
assertThat(testRenderer.getStdErr())
.contains("Could not find group");
}
}

View File

@@ -0,0 +1,85 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.group.cli;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import sonia.scm.cli.TemplateTestRenderer;
import sonia.scm.group.Group;
import sonia.scm.group.GroupManager;
import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class GroupListCommandTest {
@Mock
private GroupManager manager;
private final GroupCommandBeanMapper beanMapper = new GroupCommandBeanMapperImpl();
private final TemplateTestRenderer testRenderer = new TemplateTestRenderer();
private GroupListCommand command;
@BeforeEach
void initCommand() {
command = new GroupListCommand(testRenderer.createTemplateRenderer(), manager, beanMapper);
command.setSpec(testRenderer.getMockedSpeck());
}
@BeforeEach
void mockGroups() {
Group internalGroup = new Group("test", "hog");
Group externalGroup = new Group("test", "vogons");
externalGroup.setExternal(true);
when(manager.getAll())
.thenReturn(
asList(
internalGroup,
externalGroup));
}
@Test
void shouldRenderShortTable() {
command.setUseShortTemplate(true);
command.run();
assertThat(testRenderer.getStdOut()).isEqualTo("hog\nvogons\n");
}
@Test
void shouldRenderLongTable() {
command.run();
assertThat(testRenderer.getStdOut())
.isEqualTo("NAME EXTERNAL\nhog no \nvogons yes \n");
}
}

View File

@@ -0,0 +1,180 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.group.cli;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import sonia.scm.cli.CliExitException;
import sonia.scm.group.Group;
import sonia.scm.group.GroupManager;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class GroupModifyCommandTest {
private final GroupTemplateTestRenderer testRenderer = new GroupTemplateTestRenderer();
@Mock
private GroupManager manager;
private GroupModifyCommand command;
@BeforeEach
void initCommand() {
command = new GroupModifyCommand(testRenderer.getTemplateRenderer(), manager);
}
@Nested
class ForSuccessfulModificationTest {
private Group group;
@BeforeEach
void mockModification() {
group = new Group("test", "hog", "zaphod", "trillian");
group.setCreationDate(1649262000000L);
group.setLastModified(1649462000000L);
group.setDescription("Crew of the Heart of Gold");
when(manager.get("hog")).thenAnswer(invocation -> group);
doAnswer(invocation -> {
Group modifiedGroup = invocation.getArgument(0, Group.class);
modifiedGroup.setLastModified(1649662000000L);
group = modifiedGroup;
return null;
}).when(manager).modify(any());
command.setName("hog");
}
@Test
void shouldModifyGroup() {
command.setDescription("Earthlings on the Heart of Gold");
command.setMembers(new String[]{"arthur", "trillian"});
command.run();
verify(manager).modify(argThat(argument -> {
assertThat(argument.getName()).isEqualTo("hog");
assertThat(argument.getDescription()).isEqualTo("Earthlings on the Heart of Gold");
assertThat(argument.getMembers()).contains("arthur", "trillian");
assertThat(argument.isExternal()).isFalse();
return true;
}));
}
@Test
void shouldNotModifyDescriptionIfNotSet() {
command.run();
verify(manager).modify(argThat(argument -> {
assertThat(argument.getDescription()).isEqualTo("Crew of the Heart of Gold");
return true;
}));
}
@Test
void shouldNotModifyExternalIfNotSet() {
group.setExternal(true);
command.run();
verify(manager).modify(argThat(argument -> {
assertThat(argument.isExternal()).isTrue();
return true;
}));
}
@Test
void shouldNotModifyMembersIfNotSet() {
command.run();
verify(manager).modify(argThat(argument -> {
assertThat(argument.getMembers()).contains("zaphod", "trillian");
return true;
}));
}
@Test
void shouldSetGroupExternal() {
command.setExternal(true);
command.run();
verify(manager).modify(argThat(argument -> {
assertThat(argument.getMembers()).isEmpty();
assertThat(argument.isExternal()).isTrue();
return true;
}));
}
@Test
void shouldPrintGroupAfterModification() {
command.setDescription("Earthlings on the Heart of Gold");
command.setMembers(new String[]{"arthur", "trillian"});
command.run();
assertThat(testRenderer.getStdOut())
.contains(
"Name: hog",
"Description: Earthlings on the Heart of Gold",
"Members: arthur, trillian",
"External: no",
"Creation Date: 2022-04-06T16:20:00Z",
"Last Modified: 2022-04-11T07:26:40Z"
);
assertThat(testRenderer.getStdErr())
.isEmpty();
}
}
@Test
void shouldFailIfGroupDoesNotExists() {
when(manager.get("hog")).thenReturn(null);
command.setName("hog");
Assertions.assertThrows(
CliExitException.class,
() -> command.run()
);
assertThat(testRenderer.getStdOut())
.isEmpty();
assertThat(testRenderer.getStdErr())
.isEqualTo("Could not find group");
}
}

View File

@@ -0,0 +1,122 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.group.cli;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import sonia.scm.cli.CliExitException;
import sonia.scm.group.Group;
import sonia.scm.group.GroupManager;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class GroupRemoveMemberCommandTest {
private final GroupTemplateTestRenderer testRenderer = new GroupTemplateTestRenderer();
@Mock
private GroupManager manager;
private GroupRemoveMemberCommand command;
@BeforeEach
void initCommand() {
command = new GroupRemoveMemberCommand(testRenderer.getTemplateRenderer(), manager);
}
@Nested
class ForSuccessfulModificationTest {
private Group group;
@BeforeEach
void mockModification() {
group = new Group("test", "hog", "zaphod", "marvin", "trillian");
when(manager.get("hog")).thenAnswer(invocation -> group);
doAnswer(invocation -> {
Group modifiedGroup = invocation.getArgument(0, Group.class);
modifiedGroup.setLastModified(1649662000000L);
group = modifiedGroup;
return null;
}).when(manager).modify(any());
command.setName("hog");
command.setMembers(new String[]{"zaphod", "marvin"});
}
@Test
void shouldModifyGroup() {
command.run();
verify(manager).modify(argThat(argument -> {
assertThat(argument.getName()).isEqualTo("hog");
assertThat(argument.getMembers()).contains("trillian");
return true;
}));
}
@Test
void shouldPrintGroupAfterModification() {
command.run();
assertThat(testRenderer.getStdOut())
.contains(
"Name: hog",
"Members: trillian",
"Last Modified: 2022-04-11T07:26:40Z"
);
assertThat(testRenderer.getStdErr())
.isEmpty();
}
}
@Test
void shouldFailIfGroupDoesNotExists() {
when(manager.get("hog")).thenReturn(null);
command.setName("hog");
Assertions.assertThrows(
CliExitException.class,
() -> command.run()
);
assertThat(testRenderer.getStdOut())
.isEmpty();
assertThat(testRenderer.getStdErr())
.isEqualTo("Could not find group");
}
}

View File

@@ -0,0 +1,52 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.group.cli;
import sonia.scm.cli.TemplateTestRenderer;
import java.util.ResourceBundle;
class GroupTemplateTestRenderer {
private final TemplateTestRenderer testRenderer = new TemplateTestRenderer();
private final GroupCommandBeanMapper beanMapper = new GroupCommandBeanMapperImpl();
private final GroupTemplateRenderer templateRenderer = new GroupTemplateRenderer(testRenderer.getContextMock(), testRenderer.getTemplateEngineFactory(), beanMapper) {
@Override
protected ResourceBundle getBundle() {
return testRenderer.getResourceBundle();
}
};
GroupTemplateRenderer getTemplateRenderer() {
return templateRenderer;
}
String getStdOut() {
return testRenderer.getStdOut();
}
String getStdErr() {
return testRenderer.getStdErr();
}
}

View File

@@ -0,0 +1,54 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.template;
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
import sonia.scm.plugin.PluginLoader;
import javax.servlet.ServletContext;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
* This class can be used to create a mustache renderer in unit test
*/
public class MustacheTemplateTestEngine {
public TemplateEngine createEngine(ServletContext context) {
PluginLoader loader = mock(PluginLoader.class);
when(loader.getUberClassLoader()).thenReturn(
Thread.currentThread().getContextClassLoader());
MustacheTemplateEngine.PluginLoaderHolder pluginLoaderHolder = new MustacheTemplateEngine.PluginLoaderHolder();
pluginLoaderHolder.pluginLoader = loader;
MustacheTemplateEngine.MeterRegistryHolder meterRegistryHolder = new MustacheTemplateEngine.MeterRegistryHolder();
meterRegistryHolder.registry = new SimpleMeterRegistry();
return new MustacheTemplateEngine(context, pluginLoaderHolder, meterRegistryHolder);
}
}

View File

@@ -0,0 +1,188 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.user.cli;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import sonia.scm.cli.CliExitException;
import sonia.scm.user.User;
import sonia.scm.user.UserManager;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class UserActivateCommandTest {
private final UserTemplateTestRenderer testRenderer = new UserTemplateTestRenderer();
@Mock
private UserManager manager;
private UserActivateCommand command;
@BeforeEach
void initCommand() {
command = new UserActivateCommand(testRenderer.getTemplateRenderer(), manager);
}
@Nested
class ForSuccessfulActivationTest {
@BeforeEach
void mockGet() {
User user = new User("havelock", "Havelock Vetinari", "havelock.vetinari@discworld");
user.setPassword("patrician");
user.setExternal(false);
user.setActive(false);
user.setCreationDate(1649262000000L);
user.setLastModified(1649272000000L);
when(manager.get(any())).thenReturn(user);
}
@Test
void shouldActivateInternalUser() {
command.run();
verify(manager).modify(argThat(argument -> {
assertThat(argument.getName()).isEqualTo("havelock");
assertThat(argument.getDisplayName()).isEqualTo("Havelock Vetinari");
assertThat(argument.isExternal()).isFalse();
assertThat(argument.getPassword()).isEqualTo("patrician");
assertThat(argument.getMail()).isEqualTo("havelock.vetinari@discworld");
assertThat(argument.isActive()).isTrue();
return true;
}));
}
@Test
void shouldPrintUserAfterActivationInEnglish() {
testRenderer.setLocale("en");
command.run();
assertThat(testRenderer.getStdOut())
.contains(
"Username: havelock",
"Display Name: Havelock Vetinari",
"Email address: havelock.vetinari@discworld",
"External: no",
"Active: yes",
"Creation Date: 2022-04-06T16:20:00Z",
"Last Modified: 2022-04-06T19:06:40Z"
);
assertThat(testRenderer.getStdErr()).isEmpty();
}
@Test
void shouldPrintUserAfterActivationInGerman() {
testRenderer.setLocale("de");
command.run();
assertThat(testRenderer.getStdOut())
.contains(
"Benutzername: havelock",
"Anzeigename: Havelock Vetinari ",
"E-Mail-Adresse: havelock.vetinari@discworld",
"Extern: nein",
"Aktiv: ja",
"Erstellt: 2022-04-06T16:20:00Z",
"Zuletzt bearbeitet: 2022-04-06T19:06:40Z"
);
assertThat(testRenderer.getStdErr()).isEmpty();
}
}
@Nested
class ForUnsuccessfulActivationTest {
@Test
void shouldFailWithEnglishMsgIfUserNotFound() {
testRenderer.setLocale("en");
when(manager.get(any())).thenReturn(null);
assertThrows(CliExitException.class, () -> command.run());
verify(manager, never()).modify(any());
assertThat(testRenderer.getStdOut()).isEmpty();
assertThat(testRenderer.getStdErr()).contains("Could not find user");
}
@Test
void shouldFailWithGermanMsgIfUserNotFound() {
testRenderer.setLocale("de");
when(manager.get(any())).thenReturn(null);
assertThrows(CliExitException.class, () -> command.run());
verify(manager, never()).modify(any());
assertThat(testRenderer.getStdOut()).isEmpty();
assertThat(testRenderer.getStdErr()).contains("Benutzer konnte nicht gefunden werden");
}
@Nested
class ForExternalUserTest {
@BeforeEach
void mockExternalUser() {
User user = new User();
user.setExternal(true);
when(manager.get(any())).thenReturn(user);
}
@Test
void shouldFailWithEnglishMsgIfUserIsExternal() {
testRenderer.setLocale("en");
assertThrows(CliExitException.class, () -> command.run());
verify(manager, never()).modify(any());
assertThat(testRenderer.getStdOut()).isEmpty();
assertThat(testRenderer.getStdErr()).contains("External users cannot be activated");
}
@Test
void shouldFailWithGermanMsgIfUserIsExternal() {
testRenderer.setLocale("de");
assertThrows(CliExitException.class, () -> command.run());
verify(manager, never()).modify(any());
assertThat(testRenderer.getStdOut()).isEmpty();
assertThat(testRenderer.getStdErr()).contains("Externe Benutzer können nicht aktiviert werden");
}
}
}
}

View File

@@ -0,0 +1,155 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.user.cli;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import sonia.scm.cli.CliExitException;
import sonia.scm.user.User;
import sonia.scm.user.UserManager;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class UserConvertToExternalCommandTest {
private final UserTemplateTestRenderer testRenderer = new UserTemplateTestRenderer();
@Mock
private UserManager manager;
private UserConvertToExternalCommand command;
@BeforeEach
void initCommand() {
command = new UserConvertToExternalCommand(testRenderer.getTemplateRenderer(), manager);
}
@Nested
class ForSuccessfulConversionTest {
@BeforeEach
void mockGet() {
User user = new User("havelock", "Havelock Vetinari", "havelock.vetinari@discworld");
user.setPassword("patrician");
user.setExternal(false);
user.setActive(true);
user.setCreationDate(1649262000000L);
user.setLastModified(1649272000000L);
when(manager.get(any())).thenReturn(user);
}
@Test
void shouldConvertToExternalUser() {
command.run();
verify(manager).modify(argThat(argument -> {
assertThat(argument.getName()).isEqualTo("havelock");
assertThat(argument.getDisplayName()).isEqualTo("Havelock Vetinari");
assertThat(argument.isExternal()).isTrue();
assertThat(argument.getPassword()).isEqualTo("patrician");
assertThat(argument.getMail()).isEqualTo("havelock.vetinari@discworld");
assertThat(argument.isActive()).isTrue();
return true;
}));
}
@Test
void shouldPrintUserAfterConversionInEnglish() {
testRenderer.setLocale("en");
command.run();
assertThat(testRenderer.getStdOut())
.contains(
"Username: havelock",
"Display Name: Havelock Vetinari",
"Email address: havelock.vetinari@discworld",
"External: yes",
"Active: yes",
"Creation Date: 2022-04-06T16:20:00Z",
"Last Modified: 2022-04-06T19:06:40Z"
);
assertThat(testRenderer.getStdErr()).isEmpty();
}
@Test
void shouldPrintUserAfterConversionInGerman() {
testRenderer.setLocale("de");
command.run();
assertThat(testRenderer.getStdOut())
.contains(
"Benutzername: havelock",
"Anzeigename: Havelock Vetinari ",
"E-Mail-Adresse: havelock.vetinari@discworld",
"Extern: ja",
"Aktiv: ja",
"Erstellt: 2022-04-06T16:20:00Z",
"Zuletzt bearbeitet: 2022-04-06T19:06:40Z"
);
assertThat(testRenderer.getStdErr()).isEmpty();
}
}
@Nested
class ForUnsuccessfulConversionTest {
@Test
void shouldFailWithEnglishMsgIfUserNotFound() {
testRenderer.setLocale("en");
when(manager.get(any())).thenReturn(null);
assertThrows(CliExitException.class, () -> command.run());
verify(manager, never()).modify(any());
assertThat(testRenderer.getStdOut()).isEmpty();
assertThat(testRenderer.getStdErr()).contains("Could not find user");
}
@Test
void shouldFailWithGermanMsgIfUserNotFound() {
testRenderer.setLocale("de");
when(manager.get(any())).thenReturn(null);
assertThrows(CliExitException.class, () -> command.run());
verify(manager, never()).modify(any());
assertThat(testRenderer.getStdOut()).isEmpty();
assertThat(testRenderer.getStdErr()).contains("Benutzer konnte nicht gefunden werden");
}
}
}

View File

@@ -0,0 +1,157 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.user.cli;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import sonia.scm.cli.CliExitException;
import sonia.scm.user.User;
import sonia.scm.user.UserManager;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class UserConvertToInternalCommandTest {
private final UserTemplateTestRenderer testRenderer = new UserTemplateTestRenderer();
@Mock
private UserManager manager;
private UserConvertToInternalCommand command;
@BeforeEach
void initCommand() {
command = new UserConvertToInternalCommand(testRenderer.getTemplateRenderer(), manager);
}
@Nested
class ForSuccessfulActivationTest {
@BeforeEach
void mockGet() {
User user = new User("havelock", "Havelock Vetinari", "havelock.vetinari@discworld");
user.setPassword("patrician");
user.setExternal(true);
user.setActive(true);
user.setCreationDate(1649262000000L);
user.setLastModified(1649272000000L);
when(manager.get(any())).thenReturn(user);
}
@Test
void shouldActivateInternalUser() {
command.setPassword("havelock123");
command.run();
verify(manager).modify(argThat(argument -> {
assertThat(argument.getName()).isEqualTo("havelock");
assertThat(argument.getDisplayName()).isEqualTo("Havelock Vetinari");
assertThat(argument.isExternal()).isFalse();
assertThat(argument.getPassword()).isEqualTo("havelock123");
assertThat(argument.getMail()).isEqualTo("havelock.vetinari@discworld");
assertThat(argument.isActive()).isTrue();
return true;
}));
}
@Test
void shouldPrintUserAfterActivationInEnglish() {
testRenderer.setLocale("en");
command.run();
assertThat(testRenderer.getStdOut())
.contains(
"Username: havelock",
"Display Name: Havelock Vetinari",
"Email address: havelock.vetinari@discworld",
"External: no",
"Active: yes",
"Creation Date: 2022-04-06T16:20:00Z",
"Last Modified: 2022-04-06T19:06:40Z"
);
assertThat(testRenderer.getStdErr()).isEmpty();
}
@Test
void shouldPrintUserAfterActivationInGerman() {
testRenderer.setLocale("de");
command.run();
assertThat(testRenderer.getStdOut())
.contains(
"Benutzername: havelock",
"Anzeigename: Havelock Vetinari ",
"E-Mail-Adresse: havelock.vetinari@discworld",
"Extern: nein",
"Aktiv: ja",
"Erstellt: 2022-04-06T16:20:00Z",
"Zuletzt bearbeitet: 2022-04-06T19:06:40Z"
);
assertThat(testRenderer.getStdErr()).isEmpty();
}
}
@Nested
class ForUnsuccessfulConversionTest {
@Test
void shouldFailWithEnglishMsgIfUserNotFound() {
testRenderer.setLocale("en");
when(manager.get(any())).thenReturn(null);
assertThrows(CliExitException.class, () -> command.run());
verify(manager, never()).modify(any());
assertThat(testRenderer.getStdOut()).isEmpty();
assertThat(testRenderer.getStdErr()).contains("Could not find user");
}
@Test
void shouldFailWithGermanMsgIfUserNotFound() {
testRenderer.setLocale("de");
when(manager.get(any())).thenReturn(null);
assertThrows(CliExitException.class, () -> command.run());
verify(manager, never()).modify(any());
assertThat(testRenderer.getStdOut()).isEmpty();
assertThat(testRenderer.getStdErr()).contains("Benutzer konnte nicht gefunden werden");
}
}
}

View File

@@ -0,0 +1,237 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.user.cli;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import sonia.scm.cli.CliExitException;
import sonia.scm.cli.CommandValidator;
import sonia.scm.user.User;
import sonia.scm.user.UserManager;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class UserCreateCommandTest {
private final UserTemplateTestRenderer testRenderer = new UserTemplateTestRenderer();
@Mock
private CommandValidator validator;
@Mock
private UserManager manager;
private UserCreateCommand command;
@BeforeEach
void initCommand() {
command = new UserCreateCommand(testRenderer.getTemplateRenderer(), validator, manager);
}
@Nested
class ForSuccessfulCreationTest {
@BeforeEach
void mockCreation() {
when(manager.create(any()))
.thenAnswer(invocation -> {
User createdUser = invocation.getArgument(0, User.class);
createdUser.setCreationDate(1649262000000L);
return createdUser;
});
command.setUsername("havelock");
command.setDisplayName("Havelock Vetinari");
command.setEmail("havelock.vetinari@discworld");
}
@Test
void shouldCreateInternalUser() {
command.setPassword("patrician");
command.run();
verify(manager).create(argThat(argument -> {
assertThat(argument.getName()).isEqualTo("havelock");
assertThat(argument.getDisplayName()).isEqualTo("Havelock Vetinari");
assertThat(argument.isExternal()).isFalse();
assertThat(argument.getPassword()).isEqualTo("patrician");
assertThat(argument.getMail()).isEqualTo("havelock.vetinari@discworld");
assertThat(argument.isActive()).isTrue();
return true;
}));
}
@Test
void shouldCreateExternalUser() {
command.setExternal(true);
command.run();
verify(manager).create(argThat(argument -> {
assertThat(argument.getName()).isEqualTo("havelock");
assertThat(argument.getDisplayName()).isEqualTo("Havelock Vetinari");
assertThat(argument.isExternal()).isTrue();
assertThat(argument.getPassword()).isNull();
assertThat(argument.getMail()).isEqualTo("havelock.vetinari@discworld");
assertThat(argument.isActive()).isTrue();
return true;
}));
}
@Test
void shouldCreateInactiveUser() {
command.setPassword("patrician");
command.setInactive(true);
command.run();
verify(manager).create(argThat(argument -> {
assertThat(argument.getName()).isEqualTo("havelock");
assertThat(argument.getDisplayName()).isEqualTo("Havelock Vetinari");
assertThat(argument.isExternal()).isFalse();
assertThat(argument.getPassword()).isEqualTo("patrician");
assertThat(argument.getMail()).isEqualTo("havelock.vetinari@discworld");
assertThat(argument.isActive()).isFalse();
return true;
}));
}
@Test
void shouldPrintUserAfterCreationInEnglish() {
testRenderer.setLocale("en");
command.setPassword("patrician");
command.run();
assertThat(testRenderer.getStdOut())
.contains(
"Username: havelock",
"Display Name: Havelock Vetinari",
"Email address: havelock.vetinari@discworld",
"External: no",
"Active: yes",
"Creation Date: 2022-04-06T16:20:00Z",
"Last Modified:"
);
assertThat(testRenderer.getStdErr()).isEmpty();
}
@Test
void shouldPrintUserAfterCreationInGerman() {
testRenderer.setLocale("de");
command.setPassword("patrician");
command.run();
assertThat(testRenderer.getStdOut())
.contains(
"Benutzername: havelock",
"Anzeigename: Havelock Vetinari ",
"E-Mail-Adresse: havelock.vetinari@discworld",
"Extern: nein",
"Aktiv: ja",
"Erstellt: 2022-04-06T16:20:00Z",
"Zuletzt bearbeitet:"
);
assertThat(testRenderer.getStdErr()).isEmpty();
}
}
@Nested
class ForUnsuccessfulCreationTest {
@Test
void shouldFailIfValidatorFails() {
doThrow(picocli.CommandLine.ParameterException.class).when(validator).validate();
assertThrows(
picocli.CommandLine.ParameterException.class,
() -> command.run()
);
assertThat(testRenderer.getStdOut()).isEmpty();
}
@Test
void shouldFailWithEnglishMsgIfInternalUserWithoutPassword() {
testRenderer.setLocale("en");
assertThrows(CliExitException.class, () -> command.run());
verifyNoInteractions(manager);
assertThat(testRenderer.getStdOut()).isEmpty();
assertThat(testRenderer.getStdErr()).contains("Password is required for internal users");
}
@Test
void shouldFailWithGermanMsgIfInternalUserWithoutPassword() {
testRenderer.setLocale("de");
assertThrows(CliExitException.class, () -> command.run());
verifyNoInteractions(manager);
assertThat(testRenderer.getStdOut()).isEmpty();
assertThat(testRenderer.getStdErr()).contains("Für interne Benutzer muss ein Passwort gesetzt werden");
}
@Test
void shouldFailWithEnglishMsgIfExternalUserAndInactive() {
testRenderer.setLocale("en");
command.setExternal(true);
command.setInactive(true);
assertThrows(CliExitException.class, () -> command.run());
verifyNoInteractions(manager);
assertThat(testRenderer.getStdOut()).isEmpty();
assertThat(testRenderer.getStdErr()).contains("External users cannot be deactivated");
}
@Test
void shouldFailWithGermanMsgIfExternalUserWithInactive() {
testRenderer.setLocale("de");
command.setExternal(true);
command.setInactive(true);
assertThrows(CliExitException.class, () -> command.run());
verifyNoInteractions(manager);
assertThat(testRenderer.getStdOut()).isEmpty();
assertThat(testRenderer.getStdErr()).contains("Externe Benutzer können nicht deaktiviert werden");
}
}
}

View File

@@ -0,0 +1,188 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.user.cli;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import sonia.scm.cli.CliExitException;
import sonia.scm.user.User;
import sonia.scm.user.UserManager;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class UserDeactivateCommandTest {
private final UserTemplateTestRenderer testRenderer = new UserTemplateTestRenderer();
@Mock
private UserManager manager;
private UserDeactivateCommand command;
@BeforeEach
void initCommand() {
command = new UserDeactivateCommand(testRenderer.getTemplateRenderer(), manager);
}
@Nested
class ForSuccessfulDeactivationTest {
@BeforeEach
void mockGet() {
User user = new User("havelock", "Havelock Vetinari", "havelock.vetinari@discworld");
user.setPassword("patrician");
user.setExternal(false);
user.setActive(true);
user.setCreationDate(1649262000000L);
user.setLastModified(1649272000000L);
when(manager.get(any())).thenReturn(user);
}
@Test
void shouldDeactivateInternalUser() {
command.run();
verify(manager).modify(argThat(argument -> {
assertThat(argument.getName()).isEqualTo("havelock");
assertThat(argument.getDisplayName()).isEqualTo("Havelock Vetinari");
assertThat(argument.isExternal()).isFalse();
assertThat(argument.getPassword()).isEqualTo("patrician");
assertThat(argument.getMail()).isEqualTo("havelock.vetinari@discworld");
assertThat(argument.isActive()).isFalse();
return true;
}));
}
@Test
void shouldPrintUserAfterDeactivationInEnglish() {
testRenderer.setLocale("en");
command.run();
assertThat(testRenderer.getStdOut())
.contains(
"Username: havelock",
"Display Name: Havelock Vetinari",
"Email address: havelock.vetinari@discworld",
"External: no",
"Active: no",
"Creation Date: 2022-04-06T16:20:00Z",
"Last Modified: 2022-04-06T19:06:40Z"
);
assertThat(testRenderer.getStdErr()).isEmpty();
}
@Test
void shouldPrintUserAfterDeactivationInGerman() {
testRenderer.setLocale("de");
command.run();
assertThat(testRenderer.getStdOut())
.contains(
"Benutzername: havelock",
"Anzeigename: Havelock Vetinari ",
"E-Mail-Adresse: havelock.vetinari@discworld",
"Extern: nein",
"Aktiv: nein",
"Erstellt: 2022-04-06T16:20:00Z",
"Zuletzt bearbeitet: 2022-04-06T19:06:40Z"
);
assertThat(testRenderer.getStdErr()).isEmpty();
}
}
@Nested
class ForUnsuccessfulActivationTest {
@Test
void shouldFailWithEnglishMsgIfUserNotFound() {
testRenderer.setLocale("en");
when(manager.get(any())).thenReturn(null);
assertThrows(CliExitException.class, () -> command.run());
verify(manager, never()).modify(any());
assertThat(testRenderer.getStdOut()).isEmpty();
assertThat(testRenderer.getStdErr()).contains("Could not find user");
}
@Test
void shouldFailWithGermanMsgIfUserNotFound() {
testRenderer.setLocale("de");
when(manager.get(any())).thenReturn(null);
assertThrows(CliExitException.class, () -> command.run());
verify(manager, never()).modify(any());
assertThat(testRenderer.getStdOut()).isEmpty();
assertThat(testRenderer.getStdErr()).contains("Benutzer konnte nicht gefunden werden");
}
@Nested
class ForExternalUserTest {
@BeforeEach
void mockExternalUser() {
User user = new User();
user.setExternal(true);
when(manager.get(any())).thenReturn(user);
}
@Test
void shouldFailWithEnglishMsgIfUserIsExternal() {
testRenderer.setLocale("en");
assertThrows(CliExitException.class, () -> command.run());
verify(manager, never()).modify(any());
assertThat(testRenderer.getStdOut()).isEmpty();
assertThat(testRenderer.getStdErr()).contains("External users cannot be deactivated");
}
@Test
void shouldFailWithGermanMsgIfUserIsExternal() {
testRenderer.setLocale("de");
assertThrows(CliExitException.class, () -> command.run());
verify(manager, never()).modify(any());
assertThat(testRenderer.getStdOut()).isEmpty();
assertThat(testRenderer.getStdErr()).contains("Externe Benutzer können nicht deaktiviert werden");
}
}
}
}

View File

@@ -0,0 +1,132 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.user.cli;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import sonia.scm.user.User;
import sonia.scm.user.UserManager;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class UserDeleteCommandTest {
private final UserTemplateTestRenderer testRenderer = new UserTemplateTestRenderer();
@Mock
private UserManager manager;
private UserDeleteCommand command;
@BeforeEach
void initCommand() {
command = new UserDeleteCommand(testRenderer.getTemplateRenderer(), manager);
}
@Nested
class ForSuccessfulDeletionTest {
private User user;
@BeforeEach
void mockGet() {
user = new User();
lenient().when(manager.get(any())).thenReturn(user);
}
@Test
void shouldRenderPromptInEnglishWithoutYesFlag() {
testRenderer.setLocale("en");
command.run();
verifyNoInteractions(manager);
assertThat(testRenderer.getStdOut()).isEmpty();
assertThat(testRenderer.getStdErr()).contains("If you really want to delete this user please pass --yes");
}
@Test
void shouldRenderPromptInGermanWithoutYesFlag() {
testRenderer.setLocale("de");
command.run();
verifyNoInteractions(manager);
assertThat(testRenderer.getStdOut()).isEmpty();
assertThat(testRenderer.getStdErr()).contains("Wenn dieser Benutzer endgültig gelöscht werden soll, setzen Sie bitte --yes");
}
@Test
void shouldDeleteUserWithEnglishMsg() {
testRenderer.setLocale("en");
command.setShouldDelete(true);
command.run();
verify(manager).delete(user);
assertThat(testRenderer.getStdOut()).contains("Successfully deleted user");
assertThat(testRenderer.getStdErr()).isEmpty();
}
@Test
void shouldDeleteUserWithGermanMsg() {
testRenderer.setLocale("de");
command.setShouldDelete(true);
command.run();
verify(manager).delete(user);
assertThat(testRenderer.getStdOut()).contains("Benutzer erfolgreich gelöscht");
assertThat(testRenderer.getStdErr()).isEmpty();
}
}
@Nested
class ForUnsuccessfulDeletionTest {
@Test
void shouldFailSilentlyIfUserNotFound() {
when(manager.get(any())).thenReturn(null);
command.setShouldDelete(true);
command.run();
verify(manager, never()).delete(any());
assertThat(testRenderer.getStdOut()).isEmpty();
assertThat(testRenderer.getStdErr()).isEmpty();
}
}
}

View File

@@ -0,0 +1,136 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.user.cli;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import sonia.scm.cli.CliExitException;
import sonia.scm.user.User;
import sonia.scm.user.UserManager;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class UserGetCommandTest {
private final UserTemplateTestRenderer testRenderer = new UserTemplateTestRenderer();
@Mock
private UserManager manager;
private UserGetCommand command;
@BeforeEach
void initCommand() {
command = new UserGetCommand(testRenderer.getTemplateRenderer(), manager);
}
@Nested
class ForSuccessfulGetTest {
@BeforeEach
void mockGet() {
User user = new User("havelock", "Havelock Vetinari", "havelock.vetinari@discworld");
user.setPassword("patrician");
user.setExternal(true);
user.setActive(false);
user.setCreationDate(1649262000000L);
user.setLastModified(1649272000000L);
when(manager.get(any())).thenReturn(user);
}
@Test
void shouldPrintUserInEnglish() {
testRenderer.setLocale("en");
command.run();
assertThat(testRenderer.getStdOut())
.contains(
"Username: havelock",
"Display Name: Havelock Vetinari",
"Email address: havelock.vetinari@discworld",
"External: yes",
"Active: no",
"Creation Date: 2022-04-06T16:20:00Z",
"Last Modified: 2022-04-06T19:06:40Z"
);
assertThat(testRenderer.getStdErr()).isEmpty();
}
@Test
void shouldPrintUserInGerman() {
testRenderer.setLocale("de");
command.run();
assertThat(testRenderer.getStdOut())
.contains(
"Benutzername: havelock",
"Anzeigename: Havelock Vetinari ",
"E-Mail-Adresse: havelock.vetinari@discworld",
"Extern: ja",
"Aktiv: nein",
"Erstellt: 2022-04-06T16:20:00Z",
"Zuletzt bearbeitet: 2022-04-06T19:06:40Z"
);
assertThat(testRenderer.getStdErr())
.isEmpty();
}
}
@Nested
class ForUnsuccessfulGetTest {
@Test
void shouldFailWithEnglishMsgIfUserNotFound() {
testRenderer.setLocale("en");
when(manager.get(any())).thenReturn(null);
assertThrows(CliExitException.class, () -> command.run());
assertThat(testRenderer.getStdOut()).isEmpty();
assertThat(testRenderer.getStdErr()).contains("Could not find user");
}
@Test
void shouldFailWithGermanMsgIfUserNotFound() {
testRenderer.setLocale("de");
when(manager.get(any())).thenReturn(null);
assertThrows(CliExitException.class, () -> command.run());
assertThat(testRenderer.getStdOut()).isEmpty();
assertThat(testRenderer.getStdErr()).contains("Benutzer konnte nicht gefunden werden");
}
}
}

View File

@@ -0,0 +1,120 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.user.cli;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import sonia.scm.cli.TemplateTestRenderer;
import sonia.scm.user.User;
import sonia.scm.user.UserManager;
import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class UserListCommandTest {
@Mock
private UserManager manager;
private final UserCommandBeanMapper beanMapper = new UserCommandBeanMapperImpl();
private final TemplateTestRenderer testRenderer = new TemplateTestRenderer();
private UserListCommand command;
@BeforeEach
void initCommand() {
command = new UserListCommand(testRenderer.createTemplateRenderer(), manager, beanMapper);
}
@BeforeEach
void mockGroups() {
User internalUser = new User("havelock", "Havelock Vetinari", "havelock.vetinari@discworld");
User externalUser = new User("mustrum", "Mustrum Ridcully", "mustrum.ridcully@discworld");
externalUser.setExternal(true);
externalUser.setActive(false);
when(manager.getAll())
.thenReturn(
asList(
internalUser,
externalUser));
}
@Test
void shouldRenderShortTableInEnglish() {
testRenderer.setLocale("en");
command.setSpec(testRenderer.getMockedSpeck());
command.setUseShortTemplate(true);
command.run();
assertThat(testRenderer.getStdOut()).isEqualTo("havelock\nmustrum\n");
assertThat(testRenderer.getStdErr()).isEmpty();
}
@Test
void shouldRenderShortTableInGerman() {
testRenderer.setLocale("de");
command.setSpec(testRenderer.getMockedSpeck());
command.setUseShortTemplate(true);
command.run();
assertThat(testRenderer.getStdOut()).isEqualTo("havelock\nmustrum\n");
assertThat(testRenderer.getStdErr()).isEmpty();
}
@Test
void shouldRenderLongTableInEnglish() {
testRenderer.setLocale("en");
command.setSpec(testRenderer.getMockedSpeck());
command.run();
assertThat(testRenderer.getStdOut())
.isEqualTo("USERNAME DISPLAY NAME EMAIL ADDRESS EXTERNAL ACTIVE\n" +
"havelock Havelock Vetinari havelock.vetinari@discworld no yes \n" +
"mustrum Mustrum Ridcully mustrum.ridcully@discworld yes no \n");
assertThat(testRenderer.getStdErr()).isEmpty();
}
@Test
void shouldRenderLongTableInGerman() {
testRenderer.setLocale("de");
command.setSpec(testRenderer.getMockedSpeck());
command.run();
assertThat(testRenderer.getStdOut())
.isEqualTo("BENUTZERNAME ANZEIGENAME E-MAIL-ADRESSE EXTERN AKTIV\n" +
"havelock Havelock Vetinari havelock.vetinari@discworld nein ja \n" +
"mustrum Mustrum Ridcully mustrum.ridcully@discworld ja nein \n");
assertThat(testRenderer.getStdErr()).isEmpty();
}
}

View File

@@ -0,0 +1,233 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.user.cli;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import sonia.scm.cli.CliExitException;
import sonia.scm.cli.CommandValidator;
import sonia.scm.user.User;
import sonia.scm.user.UserManager;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class UserModifyCommandTest {
private final UserTemplateTestRenderer testRenderer = new UserTemplateTestRenderer();
@Mock
private CommandValidator validator;
@Mock
private UserManager manager;
private UserModifyCommand command;
@BeforeEach
void initCommand() {
command = new UserModifyCommand(testRenderer.getTemplateRenderer(), validator, manager);
}
@Nested
class ForSuccessfulModificationTest {
@BeforeEach
void mockGet() {
User user = new User("havelock", "Havelock Vetinari", "havelock.vetinari@discworld");
user.setPassword("patrician");
user.setExternal(false);
user.setActive(true);
user.setCreationDate(1649262000000L);
user.setLastModified(1649272000000L);
when(manager.get(any())).thenReturn(user);
}
@Test
void shouldModifyUser() {
command.setDisplayName("Lord Vetinari");
command.setEmail("patrician@discworld");
command.setPassword("havelock");
command.run();
verify(manager).modify(argThat(argument -> {
assertThat(argument.getName()).isEqualTo("havelock");
assertThat(argument.getDisplayName()).isEqualTo("Lord Vetinari");
assertThat(argument.isExternal()).isFalse();
assertThat(argument.getPassword()).isEqualTo("havelock");
assertThat(argument.getMail()).isEqualTo("patrician@discworld");
assertThat(argument.isActive()).isTrue();
return true;
}));
}
@Test
void shouldNotModifyDisplayNameIfNotSet() {
command.setEmail("patrician@discworld");
command.setPassword("havelock");
command.run();
verify(manager).modify(argThat(argument -> {
assertThat(argument.getName()).isEqualTo("havelock");
assertThat(argument.getDisplayName()).isEqualTo("Havelock Vetinari");
assertThat(argument.isExternal()).isFalse();
assertThat(argument.getPassword()).isEqualTo("havelock");
assertThat(argument.getMail()).isEqualTo("patrician@discworld");
assertThat(argument.isActive()).isTrue();
return true;
}));
}
@Test
void shouldNotModifyEmailIfNotSet() {
command.setDisplayName("Lord Vetinari");
command.setPassword("havelock");
command.run();
verify(manager).modify(argThat(argument -> {
assertThat(argument.getName()).isEqualTo("havelock");
assertThat(argument.getDisplayName()).isEqualTo("Lord Vetinari");
assertThat(argument.isExternal()).isFalse();
assertThat(argument.getPassword()).isEqualTo("havelock");
assertThat(argument.getMail()).isEqualTo("havelock.vetinari@discworld");
assertThat(argument.isActive()).isTrue();
return true;
}));
}
@Test
void shouldNotModifyPasswordIfNotSet() {
command.setEmail("patrician@discworld");
command.setDisplayName("Lord Vetinari");
command.run();
verify(manager).modify(argThat(argument -> {
assertThat(argument.getName()).isEqualTo("havelock");
assertThat(argument.getDisplayName()).isEqualTo("Lord Vetinari");
assertThat(argument.isExternal()).isFalse();
assertThat(argument.getPassword()).isEqualTo("patrician");
assertThat(argument.getMail()).isEqualTo("patrician@discworld");
assertThat(argument.isActive()).isTrue();
return true;
}));
}
@Test
void shouldPrintUserAfterModificationInEnglish() {
testRenderer.setLocale("en");
command.setDisplayName("Lord Vetinari");
command.setEmail("patrician@discworld");
command.run();
assertThat(testRenderer.getStdOut())
.contains(
"Username: havelock",
"Display Name: Lord Vetinari",
"Email address: patrician@discworld",
"External: no",
"Active: yes",
"Creation Date: 2022-04-06T16:20:00Z",
"Last Modified: 2022-04-06T19:06:40Z"
);
assertThat(testRenderer.getStdErr()).isEmpty();
}
@Test
void shouldPrintUserAfterModificationInGerman() {
testRenderer.setLocale("de");
command.setDisplayName("Lord Vetinari");
command.setEmail("patrician@discworld");
command.run();
assertThat(testRenderer.getStdOut())
.contains(
"Benutzername: havelock",
"Anzeigename: Lord Vetinari ",
"E-Mail-Adresse: patrician@discworld",
"Extern: nein",
"Aktiv: ja",
"Erstellt: 2022-04-06T16:20:00Z",
"Zuletzt bearbeitet: 2022-04-06T19:06:40Z"
);
assertThat(testRenderer.getStdErr()).isEmpty();
}
}
@Nested
class ForUnsuccessfulModificationTest {
@Test
void shouldFailIfValidatorFails() {
doThrow(picocli.CommandLine.ParameterException.class).when(validator).validate();
assertThrows(
picocli.CommandLine.ParameterException.class,
() -> command.run()
);
assertThat(testRenderer.getStdOut()).isEmpty();
}
@Test
void shouldFailWithEnglishMsgIfUserNotFound() {
testRenderer.setLocale("en");
when(manager.get(any())).thenReturn(null);
assertThrows(CliExitException.class, () -> command.run());
verify(manager, never()).modify(any());
assertThat(testRenderer.getStdOut()).isEmpty();
assertThat(testRenderer.getStdErr()).contains("Could not find user");
}
@Test
void shouldFailWithGermanMsgIfUserNotFound() {
testRenderer.setLocale("de");
when(manager.get(any())).thenReturn(null);
assertThrows(CliExitException.class, () -> command.run());
verify(manager, never()).modify(any());
assertThat(testRenderer.getStdOut()).isEmpty();
assertThat(testRenderer.getStdErr()).contains("Benutzer konnte nicht gefunden werden");
}
}
}

View File

@@ -0,0 +1,56 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.user.cli;
import sonia.scm.cli.TemplateTestRenderer;
import java.util.ResourceBundle;
class UserTemplateTestRenderer {
private final TemplateTestRenderer testRenderer = new TemplateTestRenderer();
private final UserCommandBeanMapper beanMapper = new UserCommandBeanMapperImpl();
private final UserTemplateRenderer templateRenderer = new UserTemplateRenderer(testRenderer.getContextMock(), testRenderer.getTemplateEngineFactory(), beanMapper) {
@Override
protected ResourceBundle getBundle() {
return testRenderer.getResourceBundle();
}
};
UserTemplateRenderer getTemplateRenderer() {
return templateRenderer;
}
String getStdOut() {
return testRenderer.getStdOut();
}
String getStdErr() {
return testRenderer.getStdErr();
}
public void setLocale(String locale) {
testRenderer.setLocale(locale);
}
}