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

@@ -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);
}
}