Split sample app

- spring-shell-sample-commands and spring-shell-sample-e2e
- Needed changes in e2e tests and workflow
- Fixes #754
This commit is contained in:
Janne Valkealahti
2023-06-15 10:31:05 +01:00
parent 71ed64670f
commit c27b85fb0e
58 changed files with 257 additions and 31 deletions

View File

@@ -0,0 +1,52 @@
/*
* Copyright 2017-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.shell.samples;
import org.jline.utils.AttributedString;
import org.jline.utils.AttributedStyle;
import org.springframework.boot.Banner.Mode;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.shell.command.annotation.CommandScan;
import org.springframework.shell.jline.PromptProvider;
/**
* Main entry point for the application.
*
* <p>Creates the application context and start the REPL.</p>
*
* @author Eric Bottard
* @author Janne Valkealahti
*/
@SpringBootApplication
@CommandScan
public class SpringShellSample {
public static void main(String[] args) throws Exception {
SpringApplication application = new SpringApplication(SpringShellSample.class);
application.setBannerMode(Mode.OFF);
application.run(args);
// TODO: follow up with boot why spring.main.banner-mode=off doesn't work
// SpringApplication.run(SpringShellSample.class, args);
}
@Bean
public PromptProvider myPromptProvider() {
return () -> new AttributedString("my-shell:>", AttributedStyle.DEFAULT.foreground(AttributedStyle.YELLOW));
}
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright 2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.shell.samples.e2e;
import org.springframework.context.annotation.Bean;
import org.springframework.shell.command.CommandRegistration;
import org.springframework.shell.command.annotation.Command;
import org.springframework.stereotype.Component;
public class AliasCommands {
@Command(command = BaseE2ECommands.ANNO, alias = BaseE2ECommands.ANNO, group = BaseE2ECommands.GROUP)
public static class AliasCommandsAnnotation extends BaseE2ECommands {
@Command(command = "alias-1", alias = "aliasfor-1")
public String testAlias1Annotation() {
return "Hello from alias command";
}
}
@Component
public static class AliasCommandsRegistration extends BaseE2ECommands {
@Bean
public CommandRegistration testAlias1Registration(CommandRegistration.BuilderSupplier builder) {
return builder.get()
.command(REG, "alias-1")
.group(GROUP)
.withAlias()
.command(REG, "aliasfor-1")
.and()
.withTarget()
.function(ctx -> {
return "Hello from alias command";
})
.and()
.build();
}
}
}

View File

@@ -0,0 +1,174 @@
/*
* Copyright 2022-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.shell.samples.e2e;
import java.util.Arrays;
import org.springframework.context.annotation.Bean;
import org.springframework.shell.command.CommandRegistration;
import org.springframework.shell.command.CommandRegistration.OptionArity;
import org.springframework.shell.command.annotation.Command;
import org.springframework.shell.command.annotation.Option;
import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;
import org.springframework.shell.standard.ShellOption;
import org.springframework.stereotype.Component;
/**
* Commands used for e2e test.
*
* @author Janne Valkealahti
*/
public class ArityCommands {
@ShellComponent
public static class LegacyAnnotation extends BaseE2ECommands {
@ShellMethod(key = LEGACY_ANNO + "arity-boolean-default-true", group = GROUP)
public String testArityBooleanDefaultTrueLegacyAnnotation(
@ShellOption(value = "--overwrite", arity = 1, defaultValue = "true") Boolean overwrite
) {
return "Hello " + overwrite;
}
@ShellMethod(key = LEGACY_ANNO + "arity-string-array", group = GROUP)
public String testArityStringArrayLegacyAnnotation(
@ShellOption(value = "--arg1", arity = 3) String[] arg1
) {
return "Hello " + Arrays.asList(arg1);
}
@ShellMethod(key = LEGACY_ANNO + "arity-float-array", group = GROUP)
public String testArityFloatArrayLegacyAnnotation(
@ShellOption(value = "--arg1", arity = 3) float[] arg1
) {
return "Hello " + stringOfFloats(arg1);
}
}
@Command(command = BaseE2ECommands.ANNO, group = BaseE2ECommands.GROUP)
public static class Annotation extends BaseE2ECommands {
@Command(command = "arity-boolean-default-true")
public String testArityBooleanDefaultTrueAnnotation(
@Option(longNames = "overwrite", defaultValue = "true", arity = OptionArity.ZERO_OR_ONE)
Boolean overwrite
) {
return "Hello " + overwrite;
}
@Command(command = "arity-string-array")
public String testArityStringArrayAnnotation(
@Option(longNames = "arg1", defaultValue = "true", arityMax = 3)
String[] arg1
) {
return "Hello " + Arrays.asList(arg1);
}
@Command(command = "arity-float-array")
public String testArityFloatArrayAnnotation(
@Option(longNames = "arg1", defaultValue = "true", arity = OptionArity.ZERO_OR_MORE)
float[] arg1
) {
return "Hello " + stringOfFloats(arg1);
}
}
@Component
public static class Registration extends BaseE2ECommands {
@Bean
public CommandRegistration testArityBooleanDefaultTrueRegistration() {
return getBuilder()
.command(REG, "arity-boolean-default-true")
.group(GROUP)
.withOption()
.longNames("overwrite")
.type(Boolean.class)
.defaultValue("true")
.arity(OptionArity.ZERO_OR_ONE)
.and()
.withTarget()
.function(ctx -> {
Boolean overwrite = ctx.getOptionValue("overwrite");
return "Hello " + overwrite;
})
.and()
.build();
}
@Bean
public CommandRegistration testArityStringArrayRegistration() {
return getBuilder()
.command(REG, "arity-string-array")
.group(GROUP)
.withOption()
.longNames("arg1")
.required()
.type(String[].class)
.arity(0, 3)
.position(0)
.and()
.withTarget()
.function(ctx -> {
String[] arg1 = ctx.getOptionValue("arg1");
return "Hello " + Arrays.asList(arg1);
})
.and()
.build();
}
@Bean
public CommandRegistration testArityFloatArrayRegistration() {
return getBuilder()
.command(REG, "arity-float-array")
.group(GROUP)
.withOption()
.longNames("arg1")
.type(float[].class)
.arity(0, 3)
.and()
.withTarget()
.function(ctx -> {
float[] arg1 = ctx.getOptionValue("arg1");
return "Hello " + stringOfFloats(arg1);
})
.and()
.build();
}
@Bean
public CommandRegistration testArityErrorsRegistration() {
return getBuilder()
.command(REG, "arity-errors")
.group(GROUP)
.withOption()
.longNames("arg1")
.type(String[].class)
.required()
.arity(1, 2)
.and()
.withTarget()
.function(ctx -> {
String[] arg1 = ctx.getOptionValue("arg1");
return "Hello " + Arrays.asList(arg1);
})
.and()
.build();
}
}
}

View File

@@ -0,0 +1,123 @@
/*
* Copyright 2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.shell.samples.e2e;
import org.springframework.context.annotation.Bean;
import org.springframework.shell.Availability;
import org.springframework.shell.AvailabilityProvider;
import org.springframework.shell.command.CommandRegistration;
import org.springframework.shell.command.annotation.Command;
import org.springframework.shell.command.annotation.CommandAvailability;
import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;
import org.springframework.shell.standard.ShellMethodAvailability;
import org.springframework.stereotype.Component;
public class AvailabilityCommands {
@ShellComponent
public static class LegacyAnnotation extends BaseE2ECommands {
// find from <methodName>Availability
@ShellMethod(key = LEGACY_ANNO + "availability-1", group = GROUP)
public String testAvailability1LegacyAnnotation(
) {
return "Hello";
}
public Availability testAvailability1LegacyAnnotationAvailability() {
return Availability.unavailable("not available 1");
}
// find from method name in @ShellMethodAvailability
@ShellMethod(key = LEGACY_ANNO + "availability-2", group = GROUP)
@ShellMethodAvailability("testAvailability2LegacyAnnotationAvailability2")
public String testAvailability2LegacyAnnotation(
) {
return "Hello";
}
public Availability testAvailability2LegacyAnnotationAvailability2() {
return Availability.unavailable("not available 2");
}
// find backwards from @ShellMethodAvailability command name
@ShellMethod(key = LEGACY_ANNO + "availability-3", group = GROUP)
public String testAvailability3LegacyAnnotation(
) {
return "Hello";
}
@ShellMethodAvailability("e2e anno availability-3")
public Availability testAvailability3LegacyAnnotationAvailability3() {
return Availability.unavailable("not available 3");
}
}
@Command(command = BaseE2ECommands.ANNO, group = BaseE2ECommands.GROUP)
public static class Annotation extends BaseE2ECommands {
@Command(command = "availability-1")
@CommandAvailability(provider = "testAvailability1AnnotationAvailability")
public String testAvailability1Annotation(
) {
return "Hello";
}
@Bean
public AvailabilityProvider testAvailability1AnnotationAvailability() {
return () -> Availability.unavailable("not available");
}
}
@Component
public static class Registration extends BaseE2ECommands {
@Bean
public CommandRegistration testAvailability1Registration() {
return getBuilder()
.command(REG, "availability-1")
.group(GROUP)
.availability(() -> {
return Availability.unavailable("not available");
})
.withTarget()
.function(ctx -> {
return "Hello";
})
.and()
.build();
}
@Bean
public CommandRegistration testAvailability2Registration() {
return getBuilder()
.command(REG, "availability-2")
.group(GROUP)
.availability(testAvailability2AnnotationAvailability())
.withTarget()
.function(ctx -> {
return "Hello";
})
.and()
.build();
}
AvailabilityProvider testAvailability2AnnotationAvailability() {
return () -> Availability.unavailable("not available");
}
}
}

View File

@@ -0,0 +1,66 @@
/*
* Copyright 2022-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.shell.samples.e2e;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.shell.command.CommandRegistration;
import org.springframework.util.StringUtils;
/**
* Base class for all e2e commands.
*
* @author Janne Valkealahti
*/
abstract class BaseE2ECommands {
static final String GROUP = "E2E Commands";
static final String REG = "e2e reg";
static final String LEGACY_ANNO = "e2e anno ";
// TODO: anno should become anno-legacy and annox to anno
static final String ANNO = "e2e annox ";
@Autowired
private CommandRegistration.BuilderSupplier builder;
CommandRegistration.Builder getBuilder() {
return builder.get();
}
static String stringOfStrings(String[] values) {
return String.format("[%s]", StringUtils.arrayToCommaDelimitedString(values));
}
static String stringOfInts(int[] values) {
String joined = IntStream.range(0, values.length)
.mapToLong(i -> values[i])
.boxed()
.map(d -> d.toString())
.collect(Collectors.joining(","));
return String.format("[%s]", joined);
}
static String stringOfFloats(float[] values) {
String joined = IntStream.range(0, values.length)
.mapToDouble(i -> values[i])
.boxed()
.map(d -> d.toString())
.collect(Collectors.joining(","));
return String.format("[%s]", joined);
}
}

View File

@@ -0,0 +1,182 @@
/*
* Copyright 2022-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.shell.samples.e2e;
import org.springframework.context.annotation.Bean;
import org.springframework.shell.command.CommandRegistration;
import org.springframework.shell.command.annotation.Command;
import org.springframework.shell.command.annotation.Option;
import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;
import org.springframework.shell.standard.ShellOption;
import org.springframework.stereotype.Component;
/**
* Commands used for e2e test.
*
* @author Janne Valkealahti
*/
@ShellComponent
public class DefaultValueCommands {
@ShellComponent
public static class LegacyAnnotation extends BaseE2ECommands {
@ShellMethod(key = LEGACY_ANNO + "default-value", group = GROUP)
public String testDefaultValue(
@ShellOption(defaultValue = "hi") String arg1
) {
return "Hello " + arg1;
}
@ShellMethod(key = LEGACY_ANNO + "default-value-boolean1", group = GROUP)
public String testDefaultValueBoolean1(
@ShellOption(defaultValue = "false") boolean arg1
) {
return "Hello " + arg1;
}
@ShellMethod(key = LEGACY_ANNO + "default-value-boolean2", group = GROUP)
public String testDefaultValueBoolean2(
@ShellOption(defaultValue = "true") boolean arg1
) {
return "Hello " + arg1;
}
@ShellMethod(key = LEGACY_ANNO + "default-value-boolean3", group = GROUP)
public String testDefaultValueBoolean3(
@ShellOption boolean arg1
) {
return "Hello " + arg1;
}
}
@Command(command = BaseE2ECommands.ANNO, group = BaseE2ECommands.GROUP)
public static class Annotation extends BaseE2ECommands {
@Command(command = "default-value")
public String testDefaultValueAnnotation(
@Option(longNames = "arg1", defaultValue = "hi")
String arg1
) {
return "Hello " + arg1;
}
@Command(command = "default-value-boolean1")
public String testDefaultValueBoolean1Annotation(
@Option(longNames = "arg1", defaultValue = "false")
boolean arg1
) {
return "Hello " + arg1;
}
@Command(command = "default-value-boolean2")
public String testDefaultValueBoolean2Annotation(
@Option(longNames = "arg1", defaultValue = "true")
boolean arg1
) {
return "Hello " + arg1;
}
@Command(command = "default-value-boolean3")
public String testDefaultValueBoolean3Annotation(
@Option(longNames = "arg1")
boolean arg1
) {
return "Hello " + arg1;
}
}
@Component
public static class Registration extends BaseE2ECommands {
@Bean
public CommandRegistration testDefaultValueRegistration() {
return getBuilder()
.command(REG, "default-value")
.group(GROUP)
.withOption()
.longNames("arg1")
.defaultValue("hi")
.and()
.withTarget()
.function(ctx -> {
String arg1 = ctx.getOptionValue("arg1");
return "Hello " + arg1;
})
.and()
.build();
}
@Bean
public CommandRegistration testDefaultValueBoolean1Registration() {
return getBuilder()
.command(REG, "default-value-boolean1")
.group(GROUP)
.withOption()
.longNames("arg1")
.defaultValue("false")
.type(boolean.class)
.and()
.withTarget()
.function(ctx -> {
boolean arg1 = ctx.getOptionValue("arg1");
return "Hello " + arg1;
})
.and()
.build();
}
@Bean
public CommandRegistration testDefaultValueBoolean2Registration() {
return getBuilder()
.command(REG, "default-value-boolean2")
.group(GROUP)
.withOption()
.longNames("arg1")
.defaultValue("true")
.type(boolean.class)
.and()
.withTarget()
.function(ctx -> {
boolean arg1 = ctx.getOptionValue("arg1");
return "Hello " + arg1;
})
.and()
.build();
}
@Bean
public CommandRegistration testDefaultValueBoolean3Registration() {
return getBuilder()
.command(REG, "default-value-boolean3")
.group(GROUP)
.withOption()
.longNames("arg1")
.required(false)
.type(boolean.class)
.defaultValue("false")
.and()
.withTarget()
.function(ctx -> {
boolean arg1 = ctx.getOptionValue("arg1");
return "Hello " + arg1;
})
.and()
.build();
}
}
}

View File

@@ -0,0 +1,179 @@
/*
* Copyright 2022-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.shell.samples.e2e;
import java.io.IOException;
import java.io.PrintWriter;
import org.jline.terminal.Terminal;
import org.springframework.boot.ExitCodeGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.shell.command.CommandExceptionResolver;
import org.springframework.shell.command.CommandHandlingResult;
import org.springframework.shell.command.CommandRegistration;
import org.springframework.shell.command.annotation.ExceptionResolver;
import org.springframework.shell.command.annotation.ExitCode;
import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;
import org.springframework.stereotype.Component;
/**
* Commands used for e2e test.
*
* @author Janne Valkealahti
*/
public class ErrorHandlingCommands {
@ShellComponent
public static class LegacyAnnotation extends BaseE2ECommands {
@ShellMethod(key = LEGACY_ANNO + "error-handling", group = GROUP)
String testErrorHandling(String arg1) throws IOException {
if ("throw1".equals(arg1)) {
throw new CustomException1();
}
if ("throw2".equals(arg1)) {
throw new CustomException2(11);
}
if ("throw3".equals(arg1)) {
throw new RuntimeException();
}
if ("throw4".equals(arg1)) {
throw new IllegalArgumentException();
}
if ("throw5".equals(arg1)) {
throw new CustomException3();
}
if ("throw6".equals(arg1)) {
throw new CustomException4();
}
return "Hello " + arg1;
}
@ExceptionResolver({ CustomException1.class })
CommandHandlingResult errorHandler1(CustomException1 e) {
return CommandHandlingResult.of("Hi, handled custom exception\n", 42);
}
@ExceptionResolver
CommandHandlingResult errorHandler2(IllegalArgumentException e) {
return CommandHandlingResult.of("Hi, handled illegal exception\n", 42);
}
@ExceptionResolver({ CustomException3.class })
@ExitCode(3)
String errorHandler3(CustomException3 e) {
return "Hi, handled custom exception 3\n";
}
@ExceptionResolver({ CustomException4.class })
@ExitCode(code = 4)
void errorHandler3(CustomException4 e, Terminal terminal) {
PrintWriter writer = terminal.writer();
writer.println(String.format("Hi, handled custom exception %s", e));
writer.flush();
}
}
@Component
public static class Registration extends BaseE2ECommands {
@Bean
CommandRegistration testErrorHandlingRegistration() {
return getBuilder()
.command(REG, "error-handling")
.group(GROUP)
.withOption()
.longNames("arg1")
.required()
.and()
.withErrorHandling()
.resolver(new CustomExceptionResolver())
.and()
.withTarget()
.function(ctx -> {
String arg1 = ctx.getOptionValue("arg1");
if ("throw1".equals(arg1)) {
throw new CustomException1();
}
if ("throw2".equals(arg1)) {
throw new CustomException2(11);
}
if ("throw3".equals(arg1)) {
throw new RuntimeException();
}
if ("throw4".equals(arg1)) {
throw new IllegalArgumentException();
}
if ("throw5".equals(arg1)) {
throw new CustomException3();
}
if ("throw6".equals(arg1)) {
throw new CustomException4();
}
return "Hello " + arg1;
})
.and()
.build();
}
}
private static class CustomException1 extends RuntimeException {
}
private static class CustomException2 extends RuntimeException implements ExitCodeGenerator {
private int code;
CustomException2(int code) {
this.code = code;
}
@Override
public int getExitCode() {
return code;
}
}
private static class CustomException3 extends RuntimeException {
}
private static class CustomException4 extends RuntimeException {
}
private static class CustomExceptionResolver implements CommandExceptionResolver {
@Override
public CommandHandlingResult resolve(Exception e) {
if (e instanceof CustomException1) {
return CommandHandlingResult.of("Hi, handled custom exception\n", 42);
}
if (e instanceof CustomException3) {
return CommandHandlingResult.of("Hi, handled custom exception 3\n", 3);
}
if (e instanceof CustomException4) {
String msg = String.format("Hi, handled custom exception %s\n", e);
return CommandHandlingResult.of(msg, 42);
}
if (e instanceof IllegalArgumentException) {
return CommandHandlingResult.of("Hi, handled illegal exception\n", 42);
}
return null;
}
}
}

View File

@@ -0,0 +1,69 @@
/*
* Copyright 2022-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.shell.samples.e2e;
import org.springframework.context.annotation.Bean;
import org.springframework.shell.command.CommandRegistration;
import org.springframework.stereotype.Component;
/**
* Commands used for e2e test.
*
* @author Janne Valkealahti
*/
public class ExitCodeCommands {
@Component
public static class Registration extends BaseE2ECommands {
@Bean
public CommandRegistration testExitCodeRegistration() {
return getBuilder()
.command(REG, "exit-code")
.group(GROUP)
.withOption()
.longNames("arg1")
.required()
.and()
.withTarget()
.consumer(ctx -> {
String arg1 = ctx.getOptionValue("arg1");
throw new MyException(arg1);
})
.and()
.withExitCode()
.map(MyException.class, 3)
.map(t -> {
String msg = t.getMessage();
if (msg != null && msg.contains("ok")) {
return 0;
}
else if (msg != null && msg.contains("fun")) {
return 4;
}
return 0;
})
.and()
.build();
}
}
static class MyException extends RuntimeException {
MyException(String msg) {
super(msg);
}
}
}

View File

@@ -0,0 +1,81 @@
/*
* Copyright 2022-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.shell.samples.e2e;
import org.springframework.context.annotation.Bean;
import org.springframework.shell.command.CommandRegistration;
import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;
import org.springframework.shell.standard.ShellOption;
import org.springframework.stereotype.Component;
@ShellComponent
public class HelpOptionCommands extends BaseE2ECommands {
@ShellComponent
public static class LegacyAnnotation extends BaseE2ECommands {
@ShellMethod(key = LEGACY_ANNO + "help-option-default", group = GROUP)
public String testHelpOptionDefault(
@ShellOption(defaultValue = "hi") String arg1
) {
return "Hello " + arg1;
}
}
@Component
public static class Registration extends BaseE2ECommands {
@Bean
public CommandRegistration testHelpOptionDefaultRegistration() {
return getBuilder()
.command(REG, "help-option-default")
.group(GROUP)
.withOption()
.longNames("arg1")
.defaultValue("hi")
.and()
.withTarget()
.function(ctx -> {
String arg1 = ctx.getOptionValue("arg1");
return "Hello " + arg1;
})
.and()
.build();
}
@Bean
public CommandRegistration testHelpOptionExistsRegistration() {
return getBuilder()
.command(REG, "help-option-exists")
.group(GROUP)
.withOption()
.longNames("help")
.defaultValue("hi")
.and()
.withHelpOptions()
.longNames("myhelp")
.and()
.withTarget()
.function(ctx -> {
String arg1 = ctx.getOptionValue("help");
return "Hello " + arg1;
})
.and()
.build();
}
}
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright 2022-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.shell.samples.e2e;
import org.springframework.context.annotation.Bean;
import org.springframework.shell.command.CommandRegistration;
import org.springframework.shell.command.annotation.Command;
import org.springframework.stereotype.Component;
public class HiddenCommands {
@Command(command = BaseE2ECommands.ANNO, group = BaseE2ECommands.GROUP)
public static class Annotation extends BaseE2ECommands {
@Command(command = "hidden-1", hidden = true)
public String testHidden1Annotation(
) {
return "Hello from hidden command";
}
}
@Component
public static class Registration extends BaseE2ECommands {
@Bean
public CommandRegistration testHidden1Registration() {
return getBuilder()
.command(REG, "hidden-1")
.group(GROUP)
.hidden()
.withTarget()
.function(ctx -> {
return "Hello from hidden command";
})
.and()
.build();
}
}
}

View File

@@ -0,0 +1,146 @@
/*
* Copyright 2022-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.shell.samples.e2e;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import org.springframework.context.annotation.Bean;
import org.springframework.shell.CompletionContext;
import org.springframework.shell.CompletionProposal;
import org.springframework.shell.command.CommandRegistration;
import org.springframework.shell.command.annotation.Command;
import org.springframework.shell.command.annotation.Option;
import org.springframework.shell.command.annotation.OptionValues;
import org.springframework.shell.completion.CompletionProvider;
import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;
import org.springframework.shell.standard.ShellOption;
import org.springframework.shell.standard.ValueProvider;
import org.springframework.stereotype.Component;
public class InteractiveCompletionCommands {
@ShellComponent
public static class LegacyAnnotation extends BaseE2ECommands {
@ShellMethod(key = LEGACY_ANNO + "interactive-completion-1", group = GROUP)
public String testInteractiveCompletion1(
@ShellOption(valueProvider = Test1ValuesProvider.class) String arg1,
@ShellOption(valueProvider = Test2ValuesProvider.class) String arg2
) {
return "Hello " + arg1;
}
@Bean
Test1ValuesProvider test1ValuesProvider() {
return new Test1ValuesProvider();
}
@Bean
Test2ValuesProvider test2ValuesProvider() {
return new Test2ValuesProvider();
}
}
@Command(command = BaseE2ECommands.ANNO, group = BaseE2ECommands.GROUP)
public static class Annotation extends BaseE2ECommands {
@Command(command = "interactive-completion-1")
public String testRequiredValueAnnotation(
@Option(longNames = "arg1", required = true) @OptionValues(provider = "test1CompletionProvider") String arg1,
@Option(longNames = "arg2", required = true) @OptionValues(provider = "test2CompletionProvider") String arg2
) {
return "Hello " + arg1;
}
@Bean
CompletionProvider test1CompletionProvider() {
return ctx -> {
Test1ValuesProvider test1ValuesProvider = new Test1ValuesProvider();
return test1ValuesProvider.complete(ctx);
};
}
@Bean
CompletionProvider test2CompletionProvider() {
return ctx -> {
Test2ValuesProvider test2ValuesProvider = new Test2ValuesProvider();
return test2ValuesProvider.complete(ctx);
};
}
}
@Component
public static class Registration extends BaseE2ECommands {
@Bean
CommandRegistration testInteractiveCompletion1Registration() {
Test1ValuesProvider test1ValuesProvider = new Test1ValuesProvider();
Test2ValuesProvider test2ValuesProvider = new Test2ValuesProvider();
return getBuilder()
.command(REG, "interactive-completion-1")
.group(GROUP)
.withOption()
.longNames("arg1")
.completion(ctx -> test1ValuesProvider.complete(ctx))
.and()
.withOption()
.longNames("arg2")
.completion(ctx -> test2ValuesProvider.complete(ctx))
.and()
.withTarget()
.function(ctx -> {
String arg1 = ctx.getOptionValue("arg1");
return "Hello " + arg1;
})
.and()
.build();
}
}
static class Test1ValuesProvider implements ValueProvider {
private final static String[] VALUES = new String[] {
"values1Complete1",
"values1Complete2"
};
@Override
public List<CompletionProposal> complete(CompletionContext completionContext) {
return Arrays.stream(VALUES)
.map(CompletionProposal::new)
.collect(Collectors.toList());
}
}
static class Test2ValuesProvider implements ValueProvider {
private final static String[] VALUES = new String[] {
"values2Complete1",
"values2Complete2"
};
@Override
public List<CompletionProposal> complete(CompletionContext completionContext) {
return Arrays.stream(VALUES)
.map(CompletionProposal::new)
.collect(Collectors.toList());
}
}
}

View File

@@ -0,0 +1,225 @@
/*
* Copyright 2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.shell.samples.e2e;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.ResolvableType;
import org.springframework.core.convert.converter.Converter;
import org.springframework.shell.command.CommandRegistration;
import org.springframework.shell.command.annotation.Command;
import org.springframework.shell.command.annotation.Option;
import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;
import org.springframework.shell.standard.ShellOption;
import org.springframework.stereotype.Component;
public class OptionConversionCommands {
@ShellComponent
public static class LegacyAnnotation extends BaseE2ECommands {
@ShellMethod(key = LEGACY_ANNO + "option-conversion-integer", group = GROUP)
public String optionConversionIntegerAnnotation(
@ShellOption Integer arg1
) {
return "Hello " + arg1;
}
@ShellMethod(key = LEGACY_ANNO + "option-conversion-custom", group = GROUP)
public String optionConversionCustomAnnotation(
@ShellOption MyPojo arg1
) {
return "Hello " + arg1;
}
@ShellMethod(key = LEGACY_ANNO + "option-conversion-customset", group = GROUP)
public String optionConversionCustomSetAnnotation(
@ShellOption Set<MyPojo> arg1
) {
return "Hello " + arg1;
}
@ShellMethod(key = LEGACY_ANNO + "option-conversion-customlist", group = GROUP)
public String optionConversionCustomListAnnotation(
@ShellOption List<MyPojo> arg1
) {
return "Hello " + arg1;
}
@ShellMethod(key = LEGACY_ANNO + "option-conversion-customarray", group = GROUP)
public String optionConversionCustomArrayAnnotation(
@ShellOption MyPojo[] arg1
) {
return "Hello " + Arrays.asList(arg1);
}
}
@Command(command = BaseE2ECommands.ANNO, group = BaseE2ECommands.GROUP)
public static class Annotation extends BaseE2ECommands {
@Command(command = "option-conversion-integer")
public String optionConversionIntegerAnnotation(
@Option(longNames = "arg1")
Integer arg1
) {
return "Hello " + arg1;
}
@Command(command = "option-conversion-custom")
public String optionConversionCustomAnnotation(
@Option(longNames = "arg1")
MyPojo arg1
) {
return "Hello " + arg1;
}
@Command(command = "option-conversion-customset")
public String optionConversionCustomSetAnnotation(
@Option(longNames = "arg1")
Set<MyPojo> arg1
) {
return "Hello " + arg1;
}
@Command(command = "option-conversion-customarray")
public String optionConversionCustomArrayAnnotation(
@Option(longNames = "arg1")
MyPojo[] arg1
) {
return "Hello " + Arrays.asList(arg1);
}
}
@Component
public static class Registration extends BaseE2ECommands {
@Bean
public CommandRegistration optionConversionIntegerRegistration() {
return getBuilder()
.command(REG, "option-conversion-integer")
.group(GROUP)
.withOption()
.longNames("arg1")
.type(Integer.class)
.and()
.withTarget()
.function(ctx -> {
Integer arg1 = ctx.getOptionValue("arg1");
return "Hello " + arg1;
})
.and()
.build();
}
@Bean
public CommandRegistration optionConversionCustomRegistration() {
return getBuilder()
.command(REG, "option-conversion-custom")
.group(GROUP)
.withOption()
.longNames("arg1")
.type(MyPojo.class)
.and()
.withTarget()
.function(ctx -> {
MyPojo arg1 = ctx.getOptionValue("arg1");
return "Hello " + arg1;
})
.and()
.build();
}
@Bean
public CommandRegistration optionConversionCustomSetRegistration() {
ResolvableType rtype = ResolvableType.forClassWithGenerics(Set.class, MyPojo.class);
return CommandRegistration.builder()
.command(REG, "option-conversion-customset")
.group(GROUP)
.withOption()
.longNames("arg1")
.type(rtype)
.and()
.withTarget()
.function(ctx -> {
Set<MyPojo> arg1 = ctx.getOptionValue("arg1");
return "Hello " + arg1;
})
.and()
.build();
}
@Bean
public CommandRegistration optionConversionCustomArrayRegistration() {
return getBuilder()
.command(REG, "option-conversion-customarray")
.group(GROUP)
.withOption()
.longNames("arg1")
.type(MyPojo[].class)
.and()
.withTarget()
.function(ctx -> {
MyPojo[] arg1 = ctx.getOptionValue("arg1");
return "Hello " + Arrays.asList(arg1);
})
.and()
.build();
}
}
@Configuration(proxyBeanMethods = false)
public static class CommonConfiguration {
@Bean
public Converter<String, MyPojo> stringToMyPojoConverter() {
return new StringToMyPojoConverter();
}
}
public static class MyPojo {
private String arg;
public MyPojo(String arg) {
this.arg = arg;
}
public String getArg() {
return arg;
}
public void setArg(String arg) {
this.arg = arg;
}
@Override
public String toString() {
return "MyPojo [arg=" + arg + "]";
}
}
static class StringToMyPojoConverter implements Converter<String, MyPojo> {
@Override
public MyPojo convert(String from) {
return new MyPojo(from);
}
}
}

View File

@@ -0,0 +1,100 @@
/*
* Copyright 2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.shell.samples.e2e;
import org.springframework.context.annotation.Bean;
import org.springframework.shell.command.CommandRegistration;
import org.springframework.shell.command.annotation.Command;
import org.springframework.shell.command.annotation.Option;
import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;
import org.springframework.shell.standard.ShellOption;
import org.springframework.stereotype.Component;
public class OptionNamingCommands {
@ShellComponent
public static class LegacyAnnotation extends BaseE2ECommands {
@ShellMethod(key = LEGACY_ANNO + "option-naming-1", group = GROUP)
public String testOptionNaming1Annotation(
@ShellOption("from_snake") String snake,
@ShellOption("fromCamel") String camel,
@ShellOption("from-kebab") String kebab,
@ShellOption("FromPascal") String pascal
) {
return String.format("snake='%s' camel='%s' kebab='%s' pascal='%s' ", snake, camel, kebab, pascal);
}
}
@Command(command = BaseE2ECommands.ANNO, group = BaseE2ECommands.GROUP)
public static class Annotation extends BaseE2ECommands {
@Command(command = "option-naming-1")
public String testOptionNaming1Annotation(
@Option(longNames = "from_snake") String snake,
@Option(longNames = "fromCamel") String camel,
@Option(longNames = "from-kebab") String kebab,
@Option(longNames = "FromPascal") String pascal
) {
return String.format("snake='%s' camel='%s' kebab='%s' pascal='%s' ", snake, camel, kebab, pascal);
}
}
@Component
public static class Registration extends BaseE2ECommands {
@Bean
public CommandRegistration testOptionNaming1Registration() {
return getBuilder()
.command(REG, "option-naming-1")
.group(GROUP)
.withOption()
.longNames("from_snake")
.required()
.and()
.withOption()
.longNames("fromCamel")
.required()
.and()
.withOption()
.longNames("from-kebab")
.required()
.and()
.withOption()
.longNames("FromPascal")
.required()
.and()
.withOption()
.longNames("arg1")
.nameModifier(name -> "x" + name)
// .required()
.and()
.withTarget()
.function(ctx -> {
String snake = ctx.getOptionValue("from_snake");
String camel = ctx.getOptionValue("fromCamel");
String kebab = ctx.getOptionValue("from-kebab");
String pascal = ctx.getOptionValue("FromPascal");
return String.format("snake='%s' camel='%s' kebab='%s' pascal='%s' ", snake, camel, kebab, pascal);
})
.and()
.build();
}
}
}

View File

@@ -0,0 +1,434 @@
/*
* Copyright 2022-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.shell.samples.e2e;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import org.springframework.context.annotation.Bean;
import org.springframework.shell.command.CommandRegistration;
import org.springframework.shell.command.annotation.Command;
import org.springframework.shell.command.annotation.Option;
import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;
import org.springframework.shell.standard.ShellOption;
import org.springframework.stereotype.Component;
/**
* Commands used for e2e test.
*
* @author Janne Valkealahti
*/
public class OptionTypeCommands {
@ShellComponent
public static class LegacyAnnotation extends BaseE2ECommands {
@ShellMethod(key = LEGACY_ANNO + "option-type-string", group = GROUP)
public String optionTypeStringAnnotation(
@ShellOption(help = "Desc arg1") String arg1
) {
return "Hello " + arg1;
}
@ShellMethod(key = LEGACY_ANNO + "option-type-boolean", group = GROUP)
public String optionTypeBooleanAnnotation(
@ShellOption() boolean arg1,
@ShellOption(defaultValue = "true") boolean arg2,
@ShellOption(defaultValue = "false") boolean arg3,
@ShellOption() Boolean arg4,
@ShellOption(defaultValue = "true") Boolean arg5,
@ShellOption(defaultValue = "false") Boolean arg6,
boolean arg7
) {
return String.format("Hello arg1=%s arg2=%s arg3=%s arg4=%s arg5=%s arg6=%s arg7=%s", arg1, arg2, arg3,
arg4, arg5, arg6, arg7);
}
@ShellMethod(key = LEGACY_ANNO + "option-type-integer", group = GROUP)
public String optionTypeIntegerAnnotation(
@ShellOption int arg1,
@ShellOption Integer arg2
) {
return String.format("Hello '%s' '%s'", arg1, arg2);
}
@ShellMethod(key = LEGACY_ANNO + "option-type-enum", group = GROUP)
public String optionTypeEnumAnnotation(
@ShellOption(help = "Desc arg1") OptionTypeEnum arg1
) {
return "Hello " + arg1;
}
@ShellMethod(key = LEGACY_ANNO + "option-type-string-array", group = GROUP)
public String optionTypeStringArrayAnnotation(
@ShellOption(help = "Desc arg1") String[] arg1
) {
return "Hello " + stringOfStrings(arg1);
}
@ShellMethod(key = LEGACY_ANNO + "option-type-int-array", group = GROUP)
public String optionTypeIntArrayAnnotation(
@ShellOption(help = "Desc arg1") int[] arg1
) {
return "Hello " + stringOfInts(arg1);
}
@ShellMethod(key = LEGACY_ANNO + "option-type-string-list", group = GROUP)
public String optionTypeStringListAnnotation(
@ShellOption(help = "Desc arg1") List<String> arg1
) {
return "Hello " + arg1;
}
@ShellMethod(key = LEGACY_ANNO + "option-type-string-set", group = GROUP)
public String optionTypeStringSetAnnotation(
@ShellOption(help = "Desc arg1") Set<String> arg1
) {
return "Hello " + arg1;
}
@ShellMethod(key = LEGACY_ANNO + "option-type-string-collection", group = GROUP)
public String optionTypeStringCollectionAnnotation(
@ShellOption(help = "Desc arg1") Collection<String> arg1
) {
return "Hello " + arg1;
}
}
@Command(command = BaseE2ECommands.ANNO, group = BaseE2ECommands.GROUP)
public static class Annotation extends BaseE2ECommands {
@Command(command = "option-type-string")
public String optionTypeStringAnnotation(
@Option(longNames = "arg1")
String arg1
) {
return "Hello " + arg1;
}
@Command(command = "option-type-boolean")
public String optionTypeBooleanAnnotation(
@Option(longNames = "arg1") boolean arg1,
@Option(longNames = "arg2", defaultValue = "true") boolean arg2,
@Option(longNames = "arg3", defaultValue = "false") boolean arg3,
@Option(longNames = "arg4") Boolean arg4,
@Option(longNames = "arg5", defaultValue = "true") Boolean arg5,
@Option(longNames = "arg6", defaultValue = "false") Boolean arg6,
boolean arg7
) {
return String.format("Hello arg1=%s arg2=%s arg3=%s arg4=%s arg5=%s arg6=%s arg7=%s", arg1, arg2, arg3,
arg4, arg5, arg6, arg7);
}
@Command(command = "option-type-integer")
public String optionTypeIntegerAnnotation(
@Option(longNames = "arg1")
int arg1,
@Option(longNames = "arg2")
Integer arg2
) {
return String.format("Hello '%s' '%s'", arg1, arg2);
}
@Command(command = "option-type-enum")
public String optionTypeEnumAnnotation(
@Option(longNames = "arg1")
OptionTypeEnum arg1
) {
return "Hello " + arg1;
}
@Command(command = "option-type-string-array")
public String optionTypeStringArrayAnnotation(
@Option(longNames = "arg1")
String[] arg1
) {
return "Hello " + stringOfStrings(arg1);
}
@Command(command = "option-type-int-array")
public String optionTypeIntArrayAnnotation(
@Option(longNames = "arg1")
int[] arg1
) {
return "Hello " + stringOfInts(arg1);
}
@Command(command = "option-type-string-list")
public String optionTypeStringListAnnotation(
@Option(longNames = "arg1")
List<String> arg1
) {
return "Hello " + arg1;
}
@Command(command = "option-type-string-set")
public String optionTypeStringSetAnnotation(
@Option(longNames = "arg1")
Set<String> arg1
) {
return "Hello " + arg1;
}
@Command(command = "option-type-string-collection")
public String optionTypeStringCollectionAnnotation(
@Option(longNames = "arg1")
Collection<String> arg1
) {
return "Hello " + arg1;
}
}
@Component
public static class Registration extends BaseE2ECommands {
@Bean
public CommandRegistration optionTypeStringRegistration() {
return getBuilder()
.command(REG, "option-type-string")
.group(GROUP)
.withOption()
.longNames("arg1")
.type(String.class)
.position(0)
.required()
.and()
.withTarget()
.function(ctx -> {
String arg1 = ctx.getOptionValue("arg1");
return "Hello " + arg1;
})
.and()
.build();
}
@Bean
public CommandRegistration optionTypeBooleanRegistration() {
return getBuilder()
.command(REG, "option-type-boolean")
.group(GROUP)
.withOption()
.longNames("arg1")
.type(boolean.class)
.and()
.withOption()
.longNames("arg2")
.type(boolean.class)
.defaultValue("true")
.and()
.withOption()
.longNames("arg3")
.type(boolean.class)
.defaultValue("false")
.and()
.withOption()
.longNames("arg4")
.type(Boolean.class)
.and()
.withOption()
.longNames("arg5")
.type(Boolean.class)
.defaultValue("true")
.and()
.withOption()
.longNames("arg6")
.type(Boolean.class)
.defaultValue("false")
.and()
.withOption()
.longNames("arg7")
.type(boolean.class)
.and()
.withTarget()
.function(ctx -> {
boolean arg1 = ctx.hasMappedOption("arg1") ? ctx.getOptionValue("arg1") : false;
boolean arg2 = ctx.getOptionValue("arg2");
boolean arg3 = ctx.getOptionValue("arg3");
Boolean arg4 = ctx.getOptionValue("arg4");
Boolean arg5 = ctx.getOptionValue("arg5");
Boolean arg6 = ctx.getOptionValue("arg6");
boolean arg7 = ctx.hasMappedOption("arg7") ? ctx.getOptionValue("arg7") : false;
return String.format("Hello arg1=%s arg2=%s arg3=%s arg4=%s arg5=%s arg6=%s arg7=%s", arg1,
arg2, arg3, arg4, arg5, arg6, arg7);
})
.and()
.build();
}
@Bean
public CommandRegistration optionTypeIntegerRegistration() {
return getBuilder()
.command(REG, "option-type-integer")
.group(GROUP)
.withOption()
.longNames("arg1")
.type(int.class)
.required()
.and()
.withOption()
.longNames("arg2")
.type(Integer.class)
.required()
.and()
.withTarget()
.function(ctx -> {
int arg1 = ctx.getOptionValue("arg1");
Integer arg2 = ctx.getOptionValue("arg2");
return String.format("Hello '%s' '%s'", arg1, arg2);
})
.and()
.build();
}
@Bean
public CommandRegistration optionTypeEnumRegistration() {
return getBuilder()
.command(REG, "option-type-enum")
.group(GROUP)
.withOption()
.longNames("arg1")
.type(OptionTypeEnum.class)
.required()
.and()
.withTarget()
.function(ctx -> {
OptionTypeEnum arg1 = ctx.getOptionValue("arg1");
return "Hello " + arg1;
})
.and()
.build();
}
@Bean
public CommandRegistration optionTypeStringArrayRegistration() {
return getBuilder()
.command(REG, "option-type-string-array")
.group(GROUP)
.withOption()
.longNames("arg1")
.type(String[].class)
.required()
.and()
.withTarget()
.function(ctx -> {
String[] arg1 = ctx.getOptionValue("arg1");
return "Hello " + stringOfStrings(arg1);
})
.and()
.build();
}
@Bean
public CommandRegistration optionTypeIntArrayRegistration() {
return getBuilder()
.command(REG, "option-type-int-array")
.group(GROUP)
.withOption()
.longNames("arg1")
.type(int[].class)
.required()
.and()
.withTarget()
.function(ctx -> {
int[] arg1 = ctx.getOptionValue("arg1");
return "Hello " + stringOfInts(arg1);
})
.and()
.build();
}
@Bean
public CommandRegistration optionTypeStringListRegistration() {
return getBuilder()
.command(REG, "option-type-string-list")
.group(GROUP)
.withOption()
.longNames("arg1")
.type(List.class)
.required()
.and()
.withTarget()
.function(ctx -> {
List<String> arg1 = ctx.getOptionValue("arg1");
return "Hello " + arg1;
})
.and()
.build();
}
@Bean
public CommandRegistration optionTypeStringSetRegistration() {
return getBuilder()
.command(REG, "option-type-string-set")
.group(GROUP)
.withOption()
.longNames("arg1")
.type(Set.class)
.required()
.and()
.withTarget()
.function(ctx -> {
Set<String> arg1 = ctx.getOptionValue("arg1");
return "Hello " + arg1;
})
.and()
.build();
}
@Bean
public CommandRegistration optionTypeStringCollectionRegistration() {
return getBuilder()
.command(REG, "option-type-string-collection")
.group(GROUP)
.withOption()
.longNames("arg1")
.type(Collection.class)
.required()
.and()
.withTarget()
.function(ctx -> {
Collection<String> arg1 = ctx.getOptionValue("arg1");
return "Hello " + arg1;
})
.and()
.build();
}
@Bean
public CommandRegistration optionTypeVoidRegistration() {
return getBuilder()
.command(REG, "option-type-void")
.group(GROUP)
.withOption()
.longNames("arg1")
.type(void.class)
.and()
.withTarget()
.function(ctx -> {
return "Hello ";
})
.and()
.build();
}
}
public static enum OptionTypeEnum {
ONE,TWO,THREE
}
}

View File

@@ -0,0 +1,77 @@
/*
* Copyright 2022-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.shell.samples.e2e;
import org.springframework.context.annotation.Bean;
import org.springframework.shell.command.CommandRegistration;
import org.springframework.shell.command.annotation.Command;
import org.springframework.shell.command.annotation.Option;
import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;
import org.springframework.shell.standard.ShellOption;
import org.springframework.stereotype.Component;
/**
* Commands used for e2e test.
*
* @author Janne Valkealahti
*/
public class OptionalValueCommands {
@ShellComponent
public static class LegacyAnnotation extends BaseE2ECommands {
@ShellMethod(key = LEGACY_ANNO + "optional-value", group = GROUP)
public String testOptionalValue(
@ShellOption(defaultValue = ShellOption.NULL) String arg1
) {
return "Hello " + arg1;
}
}
@Command(command = BaseE2ECommands.ANNO, group = BaseE2ECommands.GROUP)
public static class Annotation extends BaseE2ECommands {
@Command(command = "optional-value")
public String testOptionalValueAnnotation(
@Option(longNames = "arg1")
String arg1
) {
return "Hello " + arg1;
}
}
@Component
public static class Registration extends BaseE2ECommands {
@Bean
public CommandRegistration testOptionalValueRegistration() {
return getBuilder()
.command(REG, "optional-value")
.group(GROUP)
.withOption()
.longNames("arg1")
.and()
.withTarget()
.function(ctx -> {
String arg1 = ctx.getOptionValue("arg1");
return "Hello " + arg1;
})
.and()
.build();
}
}
}

View File

@@ -0,0 +1,79 @@
/*
* Copyright 2022-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.shell.samples.e2e;
import org.springframework.context.annotation.Bean;
import org.springframework.shell.command.CommandRegistration;
import org.springframework.shell.command.annotation.Command;
import org.springframework.shell.command.annotation.Option;
import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;
import org.springframework.shell.standard.ShellOption;
import org.springframework.stereotype.Component;
/**
* Commands used for e2e test.
*
* @author Janne Valkealahti
*/
public class RequiredValueCommands {
@ShellComponent
public static class LegacyAnnotation extends BaseE2ECommands {
@ShellMethod(key = LEGACY_ANNO + "required-value", group = GROUP)
public String testRequiredValueAnnotation(
@ShellOption(help = "Desc arg1") String arg1
) {
return "Hello " + arg1;
}
}
@Command(command = BaseE2ECommands.ANNO, group = BaseE2ECommands.GROUP)
public static class Annotation extends BaseE2ECommands {
@Command(command = "required-value")
public String testRequiredValueAnnotation(
@Option(longNames = "arg1", required = true, description = "Desc arg1")
String arg1
) {
return "Hello " + arg1;
}
}
@Component
public static class Registration extends BaseE2ECommands {
@Bean
public CommandRegistration testRequiredValueRegistration() {
return getBuilder()
.command(REG, "required-value")
.group(GROUP)
.withOption()
.longNames("arg1")
.description("Desc arg1")
.required()
.and()
.withTarget()
.function(ctx -> {
String arg1 = ctx.getOptionValue("arg1");
return "Hello " + arg1;
})
.and()
.build();
}
}
}

View File

@@ -0,0 +1,151 @@
/*
* Copyright 2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.shell.samples.e2e;
import org.springframework.context.annotation.Bean;
import org.springframework.shell.command.CommandRegistration;
import org.springframework.shell.command.annotation.Command;
import org.springframework.shell.command.annotation.Option;
import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;
import org.springframework.shell.standard.ShellOption;
import org.springframework.stereotype.Component;
public class ShortOptionTypeCommands {
@ShellComponent
public static class LegacyAnnotation extends BaseE2ECommands {
@ShellMethod(key = LEGACY_ANNO + "short-option-type-string", group = GROUP)
public String shortOptionTypeStringLegacyAnnotation(
@ShellOption(value = { "--arg", "-a" }) String arg) {
return String.format("Hi '%s'", arg);
}
@ShellMethod(key = LEGACY_ANNO + "short-option-type-single-boolean", group = GROUP)
public String shortOptionTypeSingleBooleanLegacyAnnotation(
@ShellOption(value = "-a") boolean a)
{
return String.format("Hi '%s'", a);
}
@ShellMethod(key = LEGACY_ANNO + "short-option-type-multi-boolean", group = GROUP)
public String shortOptionTypeMultiBooleanLegacyAnnotation(
@ShellOption(value = "-a") boolean a,
@ShellOption(value = "-b") boolean b,
@ShellOption(value = "-c") boolean c)
{
return String.format("Hi a='%s' b='%s' c='%s'", a, b, c);
}
}
@Command(command = BaseE2ECommands.ANNO, group = BaseE2ECommands.GROUP)
public static class Annotation extends BaseE2ECommands {
@Command(command = "short-option-type-string")
public String shortOptionTypeStringAnnotation(
@Option(longNames = "arg", shortNames = 'a', required = true) String arg) {
return String.format("Hi '%s'", arg);
}
@Command(command = "short-option-type-single-boolean")
public String shortOptionTypeSingleBooleanAnnotation(
@Option(shortNames = 'a') boolean a) {
return String.format("Hi '%s'", a);
}
@Command(command = "short-option-type-multi-boolean")
public String shortOptionTypeMultiBooleanAnnotation(
@Option(shortNames = 'a') boolean a,
@Option(shortNames = 'b') boolean b,
@Option(shortNames = 'c') boolean c) {
return String.format("Hi a='%s' b='%s' c='%s'", a, b, c);
}
}
@Component
public static class Registration extends BaseE2ECommands {
@Bean
public CommandRegistration shortOptionTypeStringRegistration() {
return getBuilder()
.command(REG, "short-option-type-string")
.group(GROUP)
.withTarget()
.function(ctx -> {
String arg = ctx.hasMappedOption("arg") ? ctx.getOptionValue("arg") : null;
return String.format("Hi arg='%s'", arg);
})
.and()
.withOption()
.longNames("arg")
.shortNames('a')
.required()
.and()
.build();
}
@Bean
public CommandRegistration shortOptionTypeSingleBooleanRegistration() {
return getBuilder()
.command(REG, "short-option-type-single-boolean")
.group(GROUP)
.withTarget()
.function(ctx -> {
Boolean a = ctx.hasMappedOption("a") ? ctx.getOptionValue("a") : null;
return String.format("Hi a='%s'", a);
})
.and()
.withOption()
.shortNames('a')
.type(boolean.class)
.defaultValue("false")
.and()
.build();
}
@Bean
public CommandRegistration shortOptionTypeMultiBooleanRegistration() {
return getBuilder()
.command(REG, "short-option-type-multi-boolean")
.group(GROUP)
.withTarget()
.function(ctx -> {
Boolean a = ctx.hasMappedOption("a") ? ctx.getOptionValue("a") : null;
Boolean b = ctx.hasMappedOption("b") ? ctx.getOptionValue("b") : null;
Boolean c = ctx.hasMappedOption("c") ? ctx.getOptionValue("c") : null;
return String.format("Hi a='%s' b='%s' c='%s'", a, b, c);
})
.and()
.withOption()
.shortNames('a')
.type(boolean.class)
.defaultValue("false")
.and()
.withOption()
.shortNames('b')
.type(boolean.class)
.defaultValue("false")
.and()
.withOption()
.shortNames('c')
.type(boolean.class)
.defaultValue("false")
.and()
.build();
}
}
}

View File

@@ -0,0 +1,79 @@
/*
* Copyright 2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.shell.samples.e2e;
import org.springframework.context.annotation.Bean;
import org.springframework.shell.command.CommandRegistration;
import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;
import org.springframework.shell.standard.ShellOption;
import org.springframework.stereotype.Component;
public class UnrecognisedOptionCommands {
@ShellComponent
public static class LegacyAnnotation extends BaseE2ECommands {
@ShellMethod(key = LEGACY_ANNO + "unrecognised-option-noother", group = GROUP)
public String testUnrecognisedOptionNoOtherAnnotation(
) {
return "Hi";
}
@ShellMethod(key = LEGACY_ANNO + "unrecognised-option-withrequired", group = GROUP)
public String testUnrecognisedOptionWithRequiredAnnotation(
@ShellOption(help = "Desc arg1") String arg1
) {
return "Hello " + arg1;
}
}
@Component
public static class Registration extends BaseE2ECommands {
@Bean
public CommandRegistration testUnrecognisedOptionNoOtherRegistration() {
return getBuilder()
.command(REG, "unrecognised-option-noother")
.group(GROUP)
.withTarget()
.function(ctx -> {
return "Hi";
})
.and()
.build();
}
@Bean
public CommandRegistration testUnrecognisedOptionWithRequiredRegistration() {
return getBuilder()
.command(REG, "unrecognised-option-withrequired")
.group(GROUP)
.withOption()
.longNames("arg1")
.description("Desc arg1")
.required()
.and()
.withTarget()
.function(ctx -> {
String arg1 = ctx.getOptionValue("arg1");
return "Hello " + arg1;
})
.and()
.build();
}
}
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright 2022-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.shell.samples.e2e;
import jakarta.validation.constraints.Min;
import org.springframework.context.annotation.Bean;
import org.springframework.shell.command.CommandRegistration;
import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;
import org.springframework.shell.standard.ShellOption;
import org.springframework.stereotype.Component;
/**
* Commands used for e2e test.
*
* @author Janne Valkealahti
*/
public class ValidatedValueCommands {
@ShellComponent
public static class LegacyAnnotation extends BaseE2ECommands {
@ShellMethod(key = LEGACY_ANNO + "validated-value", group = GROUP)
public String testValidatedValueAnnotation(
@ShellOption @Min(value = 1) Integer arg1,
@ShellOption @Min(value = 1) Integer arg2
) {
return "Hello " + arg1;
}
}
@Component
public static class Registration extends BaseE2ECommands {
@Bean
public CommandRegistration testValidatedValueRegistration() {
return getBuilder()
.command(REG, "validated-value")
.group(GROUP)
.withOption()
.longNames("arg1")
.type(Integer.class)
.required()
.and()
.withOption()
.longNames("arg2")
.type(Integer.class)
.required()
.and()
.withTarget()
.function(ctx -> {
Integer arg1 = ctx.getOptionValue("arg1");
return "Hello " + arg1;
})
.and()
.build();
}
}
}

View File

@@ -0,0 +1,77 @@
/*
* Copyright 2022-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.shell.samples.e2e;
import org.jline.terminal.Terminal;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.shell.command.CommandRegistration;
import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;
import org.springframework.stereotype.Component;
public class WriteCommands {
@ShellComponent
public static class LegacyAnnotation extends BaseE2ECommands {
@Autowired
Terminal terminal;
@ShellMethod(key = LEGACY_ANNO + "write-terminalwriter", group = GROUP)
public void writeTerminalWriterAnnotation() {
terminal.writer().println("hi");
terminal.writer().flush();
}
@ShellMethod(key = LEGACY_ANNO + "write-systemout", group = GROUP)
public void writeSystemOutAnnotation() {
System.out.println("hi");
}
}
@Component
public static class Registration extends BaseE2ECommands {
@Bean
public CommandRegistration writeTerminalWriterRegistration() {
return getBuilder()
.command(REG, "write-terminalwriter")
.group(GROUP)
.withTarget()
.consumer(ctx -> {
ctx.getTerminal().writer().println("hi");
ctx.getTerminal().writer().flush();
})
.and()
.build();
}
@Bean
public CommandRegistration writeSystemOutRegistration() {
return getBuilder()
.command(REG, "write-terminalwriter")
.group(GROUP)
.withTarget()
.consumer(ctx -> {
System.out.println("hi");
})
.and()
.build();
}
}
}

View File

@@ -0,0 +1,34 @@
spring:
main:
banner-mode: off
shell:
## pick global default option naming
# option:
# naming:
# case-type: noop
# case-type: camel
# case-type: snake
# case-type: kebab
# case-type: pascal
config:
env: SPRING_SHELL_SAMPLES_USER_HOME
location: "{userconfig}/spring-shell-samples"
history:
name: spring-shell-samples-history.log
command:
help:
grouping-mode: group
completion:
root-command: spring-shell-samples
## disable console logging
logging:
pattern:
console:
## log debug from a cli
# file:
# name: shell.log
# level:
# root: debug
# org:
# springframework:
# shell: debug