mirror of
https://github.com/scm-manager/scm-manager.git
synced 2026-07-04 11:38:44 +02:00
CLI Support for repository actions (#1987)
To make SCM-Manager more accessible and to make it easier using scripts against the server, we created a command line interface. This command line interface can be used to perform the default actions like create, modify and delete repositories. It is also very flexible and can be extended by plugins. The CLI already supports internationalization, help texts, input validation, loose and table-like templates and nested subcommands. Check the cli guidelines to learn how add new cli commands. Co-authored-by: Sebastian Sdorra <sebastian.sdorra@cloudogu.com>
This commit is contained in:
@@ -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.api.v2.resources;
|
||||
|
||||
import lombok.Data;
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
import sonia.scm.cli.CliProcessor;
|
||||
import sonia.scm.cli.JsonStreamingCliContext;
|
||||
import sonia.scm.security.ApiKeyService;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.DELETE;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.StreamingOutput;
|
||||
import java.util.List;
|
||||
|
||||
@Path("v2/cli")
|
||||
public class CliResource {
|
||||
|
||||
private final CliProcessor processor;
|
||||
private final ApiKeyService service;
|
||||
|
||||
@Inject
|
||||
public CliResource(CliProcessor processor, ApiKeyService service) {
|
||||
this.processor = processor;
|
||||
this.service = service;
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("exec")
|
||||
public StreamingOutput exec(@QueryParam("args") List<String> args, @Context HttpServletRequest request) {
|
||||
return outputStream -> {
|
||||
try (JsonStreamingCliContext context = new JsonStreamingCliContext(request.getLocale(), request.getInputStream(), outputStream)) {
|
||||
int exitCode = processor.execute(context, args.toArray(new String[0]));
|
||||
context.writeExit(exitCode);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@POST
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Path("login")
|
||||
public Response login(CliAuthenticationDto auth) {
|
||||
String username = SecurityUtils.getSubject().getPrincipal().toString();
|
||||
ApiKeyService.CreationResult newKey = service.createNewKey(username, auth.getApiKey(), "*");
|
||||
return Response.ok(newKey.getToken()).build();
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Path("logout/{apiKey}")
|
||||
public Response logout(@PathParam("apiKey") String apiKeyName) {
|
||||
String username = SecurityUtils.getSubject().getPrincipal().toString();
|
||||
service.getKeys(username)
|
||||
.stream()
|
||||
.filter(apiKey -> apiKey.getDisplayName().equals(apiKeyName))
|
||||
.findFirst()
|
||||
.ifPresent(apiKey -> service.remove(username, apiKey.getId()));
|
||||
return Response.noContent().build();
|
||||
}
|
||||
|
||||
@Data
|
||||
static class CliAuthenticationDto {
|
||||
private String apiKey;
|
||||
}
|
||||
}
|
||||
@@ -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.cli;
|
||||
|
||||
import picocli.CommandLine;
|
||||
import sonia.scm.ExceptionWithContext;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
|
||||
public class CliExceptionHandler implements CommandLine.IExecutionExceptionHandler {
|
||||
|
||||
@Override
|
||||
public int handleExecutionException(Exception ex, CommandLine commandLine, CommandLine.ParseResult parseResult) {
|
||||
if (ex instanceof CliExitException) {
|
||||
return ((CliExitException) ex).getExitCode();
|
||||
}
|
||||
|
||||
PrintWriter stdErr = commandLine.getErr();
|
||||
stdErr.print("Execution error");
|
||||
if (ex instanceof ExceptionWithContext) {
|
||||
String code = ((ExceptionWithContext) ex).getCode();
|
||||
stdErr.print(" " + code);
|
||||
}
|
||||
stdErr.print(": ");
|
||||
stdErr.println(ex.getMessage());
|
||||
stdErr.println();
|
||||
commandLine.usage(stdErr);
|
||||
return ExitCode.SERVER_ERROR;
|
||||
}
|
||||
}
|
||||
37
scm-webapp/src/main/java/sonia/scm/cli/CliExitException.java
Normal file
37
scm-webapp/src/main/java/sonia/scm/cli/CliExitException.java
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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 lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public class CliExitException extends RuntimeException {
|
||||
|
||||
private final int exitCode;
|
||||
|
||||
public CliExitException(int exitCode) {
|
||||
this.exitCode = exitCode;
|
||||
}
|
||||
}
|
||||
81
scm-webapp/src/main/java/sonia/scm/cli/CliProcessor.java
Normal file
81
scm-webapp/src/main/java/sonia/scm/cli/CliProcessor.java
Normal file
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* 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.inject.Injector;
|
||||
import picocli.AutoComplete;
|
||||
import picocli.CommandLine;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
public class CliProcessor {
|
||||
|
||||
private final CommandRegistry registry;
|
||||
private final Injector injector;
|
||||
private final CommandLine.Model.CommandSpec usageHelp;
|
||||
|
||||
@Inject
|
||||
public CliProcessor(CommandRegistry registry, Injector injector) {
|
||||
this.registry = registry;
|
||||
this.injector = injector;
|
||||
this.usageHelp = new CommandLine(HelpMixin.class).getCommandSpec();
|
||||
}
|
||||
|
||||
public int execute(CliContext context, String... args) {
|
||||
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()));
|
||||
for (RegisteredCommandNode c : registry.createCommandTree()) {
|
||||
CommandLine commandline = createCommandline(context, factory, c);
|
||||
cli.getCommandSpec().addSubcommand(c.getName(), commandline);
|
||||
}
|
||||
cli.addSubcommand(AutoComplete.GenerateCompletion.class);
|
||||
cli.setErr(context.getStderr());
|
||||
cli.setOut(context.getStdout());
|
||||
cli.setExecutionExceptionHandler(new CliExceptionHandler());
|
||||
return cli.execute(args);
|
||||
}
|
||||
|
||||
private CommandLine createCommandline(CliContext context, CommandFactory factory, RegisteredCommandNode command) {
|
||||
CommandLine commandLine = new CommandLine(command.getCommand(), factory);
|
||||
commandLine.getCommandSpec().addMixin("help", usageHelp);
|
||||
|
||||
ResourceBundle resourceBundle = commandLine.getCommandSpec().resourceBundle();
|
||||
if (resourceBundle != null) {
|
||||
String resourceBundleBaseName = resourceBundle.getBaseBundleName();
|
||||
commandLine.setResourceBundle(ResourceBundle.getBundle(resourceBundleBaseName, context.getLocale()));
|
||||
}
|
||||
for (RegisteredCommandNode child : command.getChildren()) {
|
||||
if (!commandLine.getCommandSpec().subcommands().containsKey(child.getName())) {
|
||||
CommandLine childCommandLine = createCommandline(context, factory, child);
|
||||
commandLine.getCommandSpec().addSubcommand(child.getName(), childCommandLine);
|
||||
}
|
||||
}
|
||||
|
||||
return commandLine;
|
||||
}
|
||||
}
|
||||
57
scm-webapp/src/main/java/sonia/scm/cli/CommandFactory.java
Normal file
57
scm-webapp/src/main/java/sonia/scm/cli/CommandFactory.java
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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.inject.AbstractModule;
|
||||
import com.google.inject.Injector;
|
||||
import picocli.CommandLine;
|
||||
|
||||
public class CommandFactory implements CommandLine.IFactory {
|
||||
|
||||
private final Injector injector;
|
||||
|
||||
public CommandFactory(Injector injector, CliContext context) {
|
||||
this.injector = injector.createChildInjector(new CliContextModule(context));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <K> K create(Class<K> cls) throws Exception {
|
||||
return injector.getInstance(cls);
|
||||
}
|
||||
|
||||
static class CliContextModule extends AbstractModule {
|
||||
|
||||
private final CliContext context;
|
||||
|
||||
private CliContextModule(CliContext context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(CliContext.class).toInstance(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
69
scm-webapp/src/main/java/sonia/scm/cli/CommandRegistry.java
Normal file
69
scm-webapp/src/main/java/sonia/scm/cli/CommandRegistry.java
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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 javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@Singleton
|
||||
public class CommandRegistry {
|
||||
|
||||
private final RegisteredCommandCollector commandCollector;
|
||||
|
||||
@Inject
|
||||
public CommandRegistry(RegisteredCommandCollector commandCollector) {
|
||||
this.commandCollector = commandCollector;
|
||||
}
|
||||
|
||||
public Set<RegisteredCommandNode> createCommandTree() {
|
||||
Set<RegisteredCommandNode> rootCommands = new HashSet<>();
|
||||
Set<RegisteredCommand> registeredCommands = commandCollector.collect();
|
||||
|
||||
Map<Class<?>, RegisteredCommandNode> commandNodes = new HashMap<>();
|
||||
|
||||
for (RegisteredCommand command : registeredCommands) {
|
||||
commandNodes.put(command.getCommand(), new RegisteredCommandNode(command.getName(), command.getCommand()));
|
||||
}
|
||||
|
||||
for (RegisteredCommand command : registeredCommands) {
|
||||
RegisteredCommandNode node = commandNodes.get(command.getCommand());
|
||||
if (command.getParent() == null) {
|
||||
rootCommands.add(node);
|
||||
} else {
|
||||
RegisteredCommandNode parentNode = commandNodes.get(command.getParent());
|
||||
if (parentNode != null) {
|
||||
parentNode.getChildren().add(node);
|
||||
} else {
|
||||
throw new NonExistingParentCommandException("parent command of " + command.getName() + " does not exist");
|
||||
}
|
||||
}
|
||||
}
|
||||
return rootCommands;
|
||||
}
|
||||
}
|
||||
33
scm-webapp/src/main/java/sonia/scm/cli/HelpMixin.java
Normal file
33
scm-webapp/src/main/java/sonia/scm/cli/HelpMixin.java
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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 picocli.CommandLine;
|
||||
|
||||
public class HelpMixin {
|
||||
|
||||
@CommandLine.Option(names = {"--help", "-h"}, usageHelp = true, descriptionKey = "scm.help.usage.description.0")
|
||||
private boolean usageHelp;
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* 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.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.util.MinimalPrettyPrinter;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.Writer;
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
|
||||
public class JsonStreamingCliContext implements CliContext, AutoCloseable {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(JsonStreamingCliContext.class);
|
||||
|
||||
private final Locale locale;
|
||||
private final InputStream stdin;
|
||||
private final PrintWriter stdout;
|
||||
private final PrintWriter stderr;
|
||||
private final JsonGenerator jsonGenerator;
|
||||
|
||||
@SuppressWarnings("java:S2095") // generator is closed in the close method
|
||||
public JsonStreamingCliContext(Locale locale, InputStream stdin, OutputStream output) throws IOException {
|
||||
this.locale = locale;
|
||||
this.stdin = stdin;
|
||||
this.jsonGenerator = mapper.createGenerator(output).setPrettyPrinter(new MinimalPrettyPrinter(""));
|
||||
jsonGenerator.writeStartArray();
|
||||
|
||||
StringingOutputWriter out = new StringingOutputWriter(jsonGenerator, "out");
|
||||
StringingOutputWriter err = new StringingOutputWriter(jsonGenerator, "err");
|
||||
out.setOther(err);
|
||||
err.setOther(out);
|
||||
|
||||
this.stdout = new PrintWriter(out);
|
||||
this.stderr = new PrintWriter(err);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrintWriter getStdout() {
|
||||
return stdout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrintWriter getStderr() {
|
||||
return stderr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getStdin() {
|
||||
return stdin;
|
||||
}
|
||||
|
||||
public void writeExit(int exitcode) throws IOException {
|
||||
stdout.flush();
|
||||
stderr.flush();
|
||||
jsonGenerator.writeStartObject();
|
||||
jsonGenerator.writeNumberField("exit", exitcode);
|
||||
jsonGenerator.writeEndObject();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exit(int exitCode) {
|
||||
throw new CliExitException(exitCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Locale getLocale() {
|
||||
return locale;
|
||||
}
|
||||
|
||||
private static final ObjectMapper mapper = new ObjectMapper();
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
try {
|
||||
stdout.close();
|
||||
stderr.close();
|
||||
jsonGenerator.writeEndArray();
|
||||
jsonGenerator.close();
|
||||
} catch (IOException e) {
|
||||
LOG.error("Could not close cli output streams", e);
|
||||
}
|
||||
try {
|
||||
stdin.close();
|
||||
} catch (IOException e) {
|
||||
LOG.error("Could not close cli input stream", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static class StringingOutputWriter extends Writer {
|
||||
|
||||
private final JsonGenerator jsonGenerator;
|
||||
private final String name;
|
||||
|
||||
private StringBuilder buffer = new StringBuilder();
|
||||
private StringingOutputWriter other;
|
||||
|
||||
private StringingOutputWriter(JsonGenerator jsonGenerator, String name) {
|
||||
this.jsonGenerator = jsonGenerator;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void setOther(StringingOutputWriter other) {
|
||||
this.other = other;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(char[] cbuf, int off, int len) throws IOException {
|
||||
other.flush();
|
||||
buffer.append(Arrays.copyOfRange(cbuf, off, len));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() throws IOException {
|
||||
String content = buffer.toString();
|
||||
if (content.length() > 0) {
|
||||
buffer = new StringBuilder();
|
||||
jsonGenerator.writeStartObject();
|
||||
jsonGenerator.writeStringField(name, content);
|
||||
jsonGenerator.writeEndObject();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
this.flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
30
scm-webapp/src/main/java/sonia/scm/cli/LogoutCommand.java
Normal file
30
scm-webapp/src/main/java/sonia/scm/cli/LogoutCommand.java
Normal 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.cli;
|
||||
|
||||
import picocli.CommandLine;
|
||||
|
||||
@CommandLine.Command(name = "logout")
|
||||
public class LogoutCommand {}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Exception is thrown if a command is registered with parent which does not exist.
|
||||
* @since 2.33.0
|
||||
*/
|
||||
public class NonExistingParentCommandException extends CliException {
|
||||
public NonExistingParentCommandException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
45
scm-webapp/src/main/java/sonia/scm/cli/PingCommand.java
Normal file
45
scm-webapp/src/main/java/sonia/scm/cli/PingCommand.java
Normal 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.cli;
|
||||
|
||||
import picocli.CommandLine;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
@CommandLine.Command(name = "ping", hidden = true)
|
||||
public class PingCommand implements Runnable {
|
||||
|
||||
private final CliContext context;
|
||||
|
||||
@Inject
|
||||
public PingCommand(CliContext context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
context.getStdout().println("PONG");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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 lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
class RegisteredCommand {
|
||||
private String name;
|
||||
private Class<?> command;
|
||||
private Class<?> parent;
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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 org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import sonia.scm.plugin.InstalledPlugin;
|
||||
import sonia.scm.plugin.PluginLoader;
|
||||
import sonia.scm.plugin.ScmModule;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class RegisteredCommandCollector {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(RegisteredCommandCollector.class);
|
||||
|
||||
private final PluginLoader pluginLoader;
|
||||
|
||||
@Inject
|
||||
public RegisteredCommandCollector(PluginLoader pluginLoader) {
|
||||
this.pluginLoader = pluginLoader;
|
||||
}
|
||||
|
||||
public Set<RegisteredCommand> collect() {
|
||||
Set<RegisteredCommand> cmds = new HashSet<>();
|
||||
findCommands(pluginLoader.getUberClassLoader(), cmds, pluginLoader.getInstalledModules());
|
||||
findCommands(pluginLoader.getUberClassLoader(), cmds, pluginLoader.getInstalledPlugins().stream().map(InstalledPlugin::getDescriptor).collect(Collectors.toList()));
|
||||
return Collections.unmodifiableSet(cmds);
|
||||
}
|
||||
|
||||
private void findCommands(ClassLoader classLoader, Set<RegisteredCommand> commands, Iterable<? extends ScmModule> modules) {
|
||||
modules.forEach(m -> m.getCliCommands().forEach(c -> {
|
||||
Class<?> command = createCommand(classLoader, c.getClazz());
|
||||
if (command != null && command != ScmManagerCommand.class) {
|
||||
commands.add(new RegisteredCommand(c.getName(), command, getParent(command)));
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
private Class<?> getParent(Class<?> command) {
|
||||
ParentCommand parentAnnotation = command.getAnnotation(ParentCommand.class);
|
||||
if (parentAnnotation != null) {
|
||||
return parentAnnotation.value();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Class<?> createCommand(ClassLoader classLoader, String clazz) {
|
||||
try {
|
||||
return classLoader.loadClass(clazz);
|
||||
} catch (ClassNotFoundException e) {
|
||||
LOG.error("Could not find command class: {}", clazz, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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 lombok.Getter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
public class RegisteredCommandNode {
|
||||
private final String name;
|
||||
private final Class<?> command;
|
||||
private final List<RegisteredCommandNode> children = new ArrayList<>();
|
||||
|
||||
public RegisteredCommandNode(String name, Class<?> command) {
|
||||
this.name = name;
|
||||
this.command = command;
|
||||
}
|
||||
}
|
||||
@@ -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.cli;
|
||||
|
||||
import picocli.CommandLine;
|
||||
|
||||
@CommandLine.Command(name = "scm")
|
||||
public class ScmManagerCommand {}
|
||||
@@ -37,6 +37,7 @@ import sonia.scm.filter.WebElementModule;
|
||||
import sonia.scm.plugin.ExtensionProcessor;
|
||||
import sonia.scm.plugin.PluginLoader;
|
||||
import sonia.scm.repository.ExecutorModule;
|
||||
import sonia.scm.validation.ValidationModule;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
import java.util.ArrayList;
|
||||
@@ -62,6 +63,7 @@ public class ApplicationModuleProvider implements ModuleProvider {
|
||||
|
||||
private List<Module> createModules(ClassOverrides overrides) {
|
||||
List<Module> moduleList = new ArrayList<>();
|
||||
moduleList.add(new ValidationModule());
|
||||
moduleList.add(new ResteasyModule());
|
||||
moduleList.add(ShiroWebModule.guiceFilterModule());
|
||||
moduleList.add(new WebElementModule(pluginLoader));
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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 lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
public class RepositoryCommandDto {
|
||||
private String name;
|
||||
private String namespace;
|
||||
private String type;
|
||||
private String contact;
|
||||
private String description;
|
||||
private String creationDate;
|
||||
private String lastModified;
|
||||
private String url;
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* 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 com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import picocli.CommandLine;
|
||||
import sonia.scm.cli.CommandValidator;
|
||||
import sonia.scm.cli.ParentCommand;
|
||||
import sonia.scm.repository.Repository;
|
||||
import sonia.scm.repository.RepositoryInitializer;
|
||||
import sonia.scm.repository.RepositoryManager;
|
||||
import sonia.scm.repository.RepositoryName;
|
||||
import sonia.scm.repository.RepositoryTypeConstraint;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.validation.constraints.Email;
|
||||
|
||||
@CommandLine.Command(name = "create")
|
||||
@ParentCommand(value = RepositoryCommand.class)
|
||||
public class RepositoryCreateCommand implements Runnable {
|
||||
|
||||
@CommandLine.Mixin
|
||||
private final RepositoryTemplateRenderer templateRenderer;
|
||||
@CommandLine.Mixin
|
||||
private final CommandValidator validator;
|
||||
private final RepositoryManager manager;
|
||||
private final RepositoryInitializer repositoryInitializer;
|
||||
|
||||
@RepositoryTypeConstraint
|
||||
@CommandLine.Parameters(descriptionKey = "scm.repo.create.type")
|
||||
private String type;
|
||||
|
||||
@RepositoryName(namespace = RepositoryName.Namespace.OPTIONAL)
|
||||
@CommandLine.Parameters(descriptionKey = "scm.repo.create.repository", paramLabel = "name")
|
||||
private String repository;
|
||||
|
||||
@CommandLine.Option(names = {"--description", "-d"}, descriptionKey = "scm.repo.create.desc")
|
||||
private String description;
|
||||
|
||||
@Email
|
||||
@CommandLine.Option(names = {"--contact", "-c"})
|
||||
private String contact;
|
||||
|
||||
@CommandLine.Option(names = {"--init", "-i"}, descriptionKey = "scm.repo.create.init")
|
||||
private boolean init;
|
||||
|
||||
@Inject
|
||||
public RepositoryCreateCommand(RepositoryTemplateRenderer templateRenderer,
|
||||
CommandValidator validator,
|
||||
RepositoryManager manager,
|
||||
RepositoryInitializer repositoryInitializer) {
|
||||
this.templateRenderer = templateRenderer;
|
||||
this.validator = validator;
|
||||
this.manager = manager;
|
||||
this.repositoryInitializer = repositoryInitializer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
validator.validate();
|
||||
Repository newRepo = new Repository();
|
||||
String[] splitRepoName = repository.split("/");
|
||||
if (splitRepoName.length == 2) {
|
||||
newRepo.setNamespace(splitRepoName[0]);
|
||||
newRepo.setName(splitRepoName[1]);
|
||||
} else {
|
||||
newRepo.setName(repository);
|
||||
}
|
||||
newRepo.setType(type);
|
||||
newRepo.setDescription(description);
|
||||
newRepo.setContact(contact);
|
||||
Repository createdRepo = manager.create(newRepo);
|
||||
if (init) {
|
||||
repositoryInitializer.initialize(createdRepo, ImmutableMap.of());
|
||||
}
|
||||
templateRenderer.render(createdRepo);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setRepository(String repository) {
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setInit(boolean init) {
|
||||
this.init = init;
|
||||
}
|
||||
}
|
||||
@@ -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.repository.cli;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import picocli.CommandLine;
|
||||
import sonia.scm.cli.ParentCommand;
|
||||
import sonia.scm.repository.NamespaceAndName;
|
||||
import sonia.scm.repository.Repository;
|
||||
import sonia.scm.repository.RepositoryManager;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.Collections;
|
||||
|
||||
@CommandLine.Command(name = "delete", aliases = "rm")
|
||||
@ParentCommand(RepositoryCommand.class)
|
||||
public class RepositoryDeleteCommand implements Runnable {
|
||||
|
||||
private static final String PROMPT_TEMPLATE = "{{i18n.repoDeletePrompt}}";
|
||||
|
||||
@CommandLine.Parameters(descriptionKey = "scm.repo.delete.repository", paramLabel = "namespace/name")
|
||||
private String repository;
|
||||
|
||||
@CommandLine.Option(names = {"--yes", "-y"}, descriptionKey = "scm.repo.delete.prompt")
|
||||
private boolean shouldDelete;
|
||||
|
||||
@CommandLine.Mixin
|
||||
private final RepositoryTemplateRenderer templateRenderer;
|
||||
private final RepositoryManager manager;
|
||||
|
||||
@Inject
|
||||
public RepositoryDeleteCommand(RepositoryManager manager, RepositoryTemplateRenderer templateRenderer) {
|
||||
this.manager = manager;
|
||||
this.templateRenderer = templateRenderer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (!shouldDelete) {
|
||||
templateRenderer.renderToStderr(PROMPT_TEMPLATE, Collections.emptyMap());
|
||||
return;
|
||||
}
|
||||
String[] splitRepo = repository.split("/");
|
||||
if (splitRepo.length == 2) {
|
||||
Repository repo = manager.get(new NamespaceAndName(splitRepo[0], splitRepo[1]));
|
||||
if (repo != null) {
|
||||
manager.delete(repo);
|
||||
}
|
||||
} else {
|
||||
templateRenderer.renderInvalidInputError();
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setRepository(String repository) {
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setShouldDelete(boolean shouldDelete) {
|
||||
this.shouldDelete = shouldDelete;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 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 com.cronutils.utils.VisibleForTesting;
|
||||
import picocli.CommandLine;
|
||||
import sonia.scm.cli.ParentCommand;
|
||||
import sonia.scm.repository.NamespaceAndName;
|
||||
import sonia.scm.repository.Repository;
|
||||
import sonia.scm.repository.RepositoryManager;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
@ParentCommand(value = RepositoryCommand.class)
|
||||
@CommandLine.Command(name = "get")
|
||||
public class RepositoryGetCommand implements Runnable {
|
||||
|
||||
@CommandLine.Parameters(paramLabel = "namespace/name", index = "0")
|
||||
private String repository;
|
||||
|
||||
@CommandLine.Mixin
|
||||
private final RepositoryTemplateRenderer templateRenderer;
|
||||
private final RepositoryManager manager;
|
||||
|
||||
@Inject
|
||||
RepositoryGetCommand(RepositoryTemplateRenderer templateRenderer, RepositoryManager manager) {
|
||||
this.templateRenderer = templateRenderer;
|
||||
this.manager = manager;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void setRepository(String repository) {
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
String[] splitRepo = repository.split("/");
|
||||
if (splitRepo.length == 2) {
|
||||
Repository repo = manager.get(new NamespaceAndName(splitRepo[0], splitRepo[1]));
|
||||
if (repo != null) {
|
||||
templateRenderer.render(repo);
|
||||
} else {
|
||||
templateRenderer.renderNotFoundError();
|
||||
}
|
||||
} else {
|
||||
templateRenderer.renderInvalidInputError();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* 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 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.repository.RepositoryManager;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.Collection;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@ParentCommand(value = RepositoryCommand.class)
|
||||
@CommandLine.Command(name = "list", aliases = "ls")
|
||||
public class RepositoryListCommand implements Runnable {
|
||||
|
||||
@CommandLine.Mixin
|
||||
private final TemplateRenderer templateRenderer;
|
||||
private final RepositoryManager manager;
|
||||
private final RepositoryToRepositoryCommandDtoMapper mapper;
|
||||
|
||||
@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",
|
||||
"{{#repos}}",
|
||||
"{{namespace}}/{{name}}",
|
||||
"{{/repos}}"
|
||||
);
|
||||
|
||||
@Inject
|
||||
public RepositoryListCommand(RepositoryManager manager, TemplateRenderer templateRenderer, RepositoryToRepositoryCommandDtoMapper mapper) {
|
||||
this.manager = manager;
|
||||
this.templateRenderer = templateRenderer;
|
||||
this.mapper = mapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Collection<RepositoryCommandDto> dtos = manager.getAll().stream().map(mapper::map).collect(Collectors.toList());
|
||||
if (useShortTemplate) {
|
||||
templateRenderer.renderToStdout(SHORT_TEMPLATE, ImmutableMap.of("repos", dtos));
|
||||
} else {
|
||||
Table table = templateRenderer.createTable();
|
||||
table.addHeader("repoName", "repoType", "repoUrl");
|
||||
for (RepositoryCommandDto dto : dtos) {
|
||||
table.addRow(dto.getNamespace() + "/" + dto.getName(), dto.getType(), dto.getUrl());
|
||||
}
|
||||
templateRenderer.renderToStdout(TABLE_TEMPLATE, ImmutableMap.of("rows", table, "repos", dtos));
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setShortTemplate(boolean useShortTemplate) {
|
||||
this.useShortTemplate = useShortTemplate;
|
||||
}
|
||||
}
|
||||
@@ -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.repository.cli;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import picocli.CommandLine;
|
||||
import sonia.scm.cli.CommandValidator;
|
||||
import sonia.scm.cli.ParentCommand;
|
||||
import sonia.scm.repository.NamespaceAndName;
|
||||
import sonia.scm.repository.Repository;
|
||||
import sonia.scm.repository.RepositoryManager;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.validation.constraints.Email;
|
||||
|
||||
@ParentCommand(value = RepositoryCommand.class)
|
||||
@CommandLine.Command(name = "modify")
|
||||
public class RepositoryModifyCommand implements Runnable {
|
||||
|
||||
@CommandLine.Mixin
|
||||
private final RepositoryTemplateRenderer templateRenderer;
|
||||
@CommandLine.Mixin
|
||||
private final CommandValidator validator;
|
||||
private final RepositoryManager manager;
|
||||
|
||||
@CommandLine.Parameters(paramLabel = "namespace/name", index = "0", descriptionKey = "scm.repo.modify.repository")
|
||||
private String repository;
|
||||
|
||||
@CommandLine.Option(names = {"--description", "-d"}, descriptionKey = "scm.repo.create.desc")
|
||||
private String description;
|
||||
|
||||
@Email
|
||||
@CommandLine.Option(names = {"--contact", "-c"})
|
||||
private String contact;
|
||||
|
||||
|
||||
@Inject
|
||||
RepositoryModifyCommand(RepositoryTemplateRenderer templateRenderer, CommandValidator validator, RepositoryManager manager) {
|
||||
this.templateRenderer = templateRenderer;
|
||||
this.validator = validator;
|
||||
this.manager = manager;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void setRepository(String repository) {
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
validator.validate();
|
||||
String[] splitRepo = repository.split("/");
|
||||
if (splitRepo.length == 2) {
|
||||
Repository repo = manager.get(new NamespaceAndName(splitRepo[0], splitRepo[1]));
|
||||
|
||||
if (repo != null) {
|
||||
if (contact != null) {
|
||||
repo.setContact(contact);
|
||||
}
|
||||
if (description != null) {
|
||||
repo.setDescription(description);
|
||||
}
|
||||
|
||||
manager.modify(repo);
|
||||
templateRenderer.render(repo);
|
||||
} else {
|
||||
templateRenderer.renderNotFoundError();
|
||||
}
|
||||
} else {
|
||||
templateRenderer.renderInvalidInputError();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* 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 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.repository.Repository;
|
||||
import sonia.scm.template.TemplateEngineFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.Collections;
|
||||
|
||||
public class RepositoryTemplateRenderer extends TemplateRenderer {
|
||||
|
||||
private static final String DETAILS_TABLE_TEMPLATE = String.join("\n",
|
||||
"{{#rows}}",
|
||||
"{{#cols}}{{value}}{{/cols}}",
|
||||
"{{/rows}}"
|
||||
);
|
||||
private static final String INVALID_INPUT_TEMPLATE = "{{i18n.repoInvalidInput}}";
|
||||
private static final String NOT_FOUND_TEMPLATE = "{{i18n.repoNotFound}}";
|
||||
|
||||
private final CliContext context;
|
||||
private final RepositoryToRepositoryCommandDtoMapper mapper;
|
||||
|
||||
@Inject
|
||||
RepositoryTemplateRenderer(CliContext context, TemplateEngineFactory templateEngineFactory, RepositoryToRepositoryCommandDtoMapper mapper) {
|
||||
super(context, templateEngineFactory);
|
||||
this.context = context;
|
||||
this.mapper = mapper;
|
||||
}
|
||||
|
||||
public void render(Repository repository) {
|
||||
Table table = createTable();
|
||||
RepositoryCommandDto dto = mapper.map(repository);
|
||||
table.addLabelValueRow("repoNamespace", dto.getNamespace());
|
||||
table.addLabelValueRow("repoName", dto.getName());
|
||||
table.addLabelValueRow("repoType", dto.getType());
|
||||
table.addLabelValueRow("repoContact", dto.getContact());
|
||||
table.addLabelValueRow("repoCreationDate", dto.getCreationDate());
|
||||
table.addLabelValueRow("repoLastModified", dto.getLastModified());
|
||||
table.addLabelValueRow("repoUrl", dto.getUrl());
|
||||
table.addLabelValueRow("repoDescription", dto.getDescription());
|
||||
renderToStdout(DETAILS_TABLE_TEMPLATE, ImmutableMap.of("rows", table, "repo", dto));
|
||||
}
|
||||
|
||||
public void renderInvalidInputError() {
|
||||
renderToStderr(INVALID_INPUT_TEMPLATE, Collections.emptyMap());
|
||||
context.exit(ExitCode.USAGE);
|
||||
}
|
||||
|
||||
public void renderNotFoundError() {
|
||||
renderToStderr(NOT_FOUND_TEMPLATE, Collections.emptyMap());
|
||||
context.exit(ExitCode.NOT_FOUND);
|
||||
}
|
||||
|
||||
public void renderException(Exception exception) {
|
||||
renderDefaultError(exception);
|
||||
context.exit(ExitCode.SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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 com.google.common.annotations.VisibleForTesting;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.ObjectFactory;
|
||||
import sonia.scm.repository.Repository;
|
||||
import sonia.scm.repository.api.RepositoryService;
|
||||
import sonia.scm.repository.api.RepositoryServiceFactory;
|
||||
import sonia.scm.repository.api.ScmProtocol;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.time.Instant;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Optional;
|
||||
|
||||
@Mapper
|
||||
public abstract class RepositoryToRepositoryCommandDtoMapper {
|
||||
|
||||
@Inject
|
||||
private RepositoryServiceFactory serviceFactory;
|
||||
|
||||
public abstract RepositoryCommandDto map(Repository modelObject);
|
||||
|
||||
@ObjectFactory
|
||||
RepositoryCommandDto createDto(Repository repository) {
|
||||
RepositoryCommandDto dto = new RepositoryCommandDto();
|
||||
try (RepositoryService service = serviceFactory.create(repository)) {
|
||||
Optional<ScmProtocol> protocolUrl = service.getSupportedProtocols().filter(p -> p.getType().equals("http")).findFirst();
|
||||
protocolUrl.ifPresent(scmProtocol -> dto.setUrl(scmProtocol.getUrl()));
|
||||
}
|
||||
return dto;
|
||||
}
|
||||
|
||||
String mapTimestampToISODate(Long timestamp) {
|
||||
if (timestamp != null) {
|
||||
return DateTimeFormatter.ISO_INSTANT.format(Instant.ofEpochMilli(timestamp));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setServiceFactory(RepositoryServiceFactory serviceFactory) {
|
||||
this.serviceFactory = serviceFactory;
|
||||
}
|
||||
}
|
||||
@@ -90,14 +90,18 @@ public class ApiKeyRealm extends AuthenticatingRealm {
|
||||
}
|
||||
|
||||
private AuthenticationInfo buildAuthenticationInfo(AuthenticationToken token, ApiKeyService.CheckResult check) {
|
||||
RepositoryRole repositoryRole = determineRole(check);
|
||||
Scope scope = createScope(repositoryRole);
|
||||
LOG.debug("login for user {} with api key limited to role {}", check.getUser(), check.getPermissionRole());
|
||||
return helper
|
||||
DAORealmHelper.AuthenticationInfoBuilder builder = helper
|
||||
.authenticationInfoBuilder(check.getUser())
|
||||
.withSessionId(getPrincipal(token))
|
||||
.withScope(scope)
|
||||
.build();
|
||||
.withSessionId(getPrincipal(token));
|
||||
|
||||
if (!check.getPermissionRole().equals("*")) {
|
||||
RepositoryRole repositoryRole = determineRole(check);
|
||||
Scope scope = createScope(repositoryRole);
|
||||
LOG.debug("login for user {} with api key limited to role {}", check.getUser(), check.getPermissionRole());
|
||||
builder = builder.withScope(scope);
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private String getPassword(AuthenticationToken token) {
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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.validation;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Provider;
|
||||
import javax.validation.ConstraintValidatorFactory;
|
||||
import javax.validation.Validation;
|
||||
import javax.validation.Validator;
|
||||
|
||||
public class DefaultValidatorProvider implements Provider<Validator> {
|
||||
|
||||
private final ConstraintValidatorFactory constraintValidatorFactory;
|
||||
|
||||
@Inject
|
||||
public DefaultValidatorProvider(ConstraintValidatorFactory constraintValidatorFactory) {
|
||||
this.constraintValidatorFactory = constraintValidatorFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Validator get() {
|
||||
return Validation.buildDefaultValidatorFactory()
|
||||
.usingContext()
|
||||
.constraintValidatorFactory(constraintValidatorFactory)
|
||||
.getValidator();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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.validation;
|
||||
|
||||
import com.google.inject.Injector;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.validation.ConstraintValidator;
|
||||
import javax.validation.ConstraintValidatorFactory;
|
||||
|
||||
public class GuiceConstraintValidatorFactory implements ConstraintValidatorFactory {
|
||||
|
||||
private final Injector injector;
|
||||
|
||||
@Inject
|
||||
public GuiceConstraintValidatorFactory(Injector injector) {
|
||||
this.injector = injector;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends ConstraintValidator<?, ?>> T getInstance(Class<T> key) {
|
||||
return injector.getInstance(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void releaseInstance(ConstraintValidator<?, ?> instance) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* 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.validation;
|
||||
|
||||
import org.jboss.resteasy.plugins.validation.GeneralValidatorImpl;
|
||||
import org.jboss.resteasy.spi.HttpRequest;
|
||||
|
||||
import javax.validation.ConstraintValidatorFactory;
|
||||
import javax.validation.MessageInterpolator;
|
||||
import javax.validation.Validation;
|
||||
import javax.validation.Validator;
|
||||
import javax.validation.ValidatorFactory;
|
||||
import javax.validation.executable.ExecutableType;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
public class ResteasyValidator extends GeneralValidatorImpl {
|
||||
|
||||
private final ValidatorFactory validatorFactory;
|
||||
private final ConstraintValidatorFactory constraintValidatorFactory;
|
||||
|
||||
ResteasyValidator(ValidatorFactory validatorFactory, ConstraintValidatorFactory constraintValidatorFactory, boolean isExecutableValidationEnabled, Set<ExecutableType> defaultValidatedExecutableTypes) {
|
||||
super(Validation.buildDefaultValidatorFactory(), isExecutableValidationEnabled, defaultValidatedExecutableTypes);
|
||||
this.validatorFactory = validatorFactory;
|
||||
this.constraintValidatorFactory = constraintValidatorFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Validator getValidator(HttpRequest request) {
|
||||
Validator v = Validator.class.cast(request.getAttribute(Validator.class.getName()));
|
||||
if (v == null) {
|
||||
Locale locale = getLocaleFrom(request);
|
||||
v = createValidator(locale);
|
||||
request.setAttribute(Validator.class.getName(), v);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
private Validator createValidator(Locale locale) {
|
||||
MessageInterpolator interpolator = new LocaleSpecificMessageInterpolator(validatorFactory.getMessageInterpolator(), locale);
|
||||
return validatorFactory.usingContext()
|
||||
.constraintValidatorFactory(constraintValidatorFactory)
|
||||
.messageInterpolator(interpolator)
|
||||
.getValidator();
|
||||
}
|
||||
|
||||
private Locale getLocaleFrom(HttpRequest request) {
|
||||
if (request == null) {
|
||||
return null;
|
||||
}
|
||||
List<Locale> locales = request.getHttpHeaders().getAcceptableLanguages();
|
||||
return locales == null || locales.isEmpty() ? Locale.ENGLISH : locales.get(0);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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.validation;
|
||||
|
||||
import org.jboss.resteasy.spi.validation.GeneralValidator;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.validation.BootstrapConfiguration;
|
||||
import javax.validation.Configuration;
|
||||
import javax.validation.ConstraintValidatorFactory;
|
||||
import javax.validation.Validation;
|
||||
import javax.ws.rs.ext.ContextResolver;
|
||||
import javax.ws.rs.ext.Provider;
|
||||
|
||||
@Provider
|
||||
public class ResteasyValidatorContextResolver implements ContextResolver<GeneralValidator> {
|
||||
|
||||
private final ConstraintValidatorFactory constraintValidatorFactory;
|
||||
|
||||
@Inject
|
||||
public ResteasyValidatorContextResolver(ConstraintValidatorFactory constraintValidatorFactory) {
|
||||
this.constraintValidatorFactory = constraintValidatorFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeneralValidator getContext(Class<?> type) {
|
||||
Configuration<?> configuration = Validation.byDefaultProvider().configure();
|
||||
BootstrapConfiguration bootstrapConfiguration = configuration.getBootstrapConfiguration();
|
||||
|
||||
return new ResteasyValidator(
|
||||
configuration.buildValidatorFactory(),
|
||||
constraintValidatorFactory,
|
||||
bootstrapConfiguration.isExecutableValidationEnabled(),
|
||||
bootstrapConfiguration.getDefaultValidatedExecutableTypes()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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.validation;
|
||||
|
||||
import com.google.inject.AbstractModule;
|
||||
|
||||
import javax.validation.ConstraintValidatorFactory;
|
||||
import javax.validation.Validator;
|
||||
|
||||
public class ValidationModule extends AbstractModule {
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(ConstraintValidatorFactory.class).to(GuiceConstraintValidatorFactory.class);
|
||||
bind(Validator.class).toProvider(DefaultValidatorProvider.class);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user