New annotation model

- This is a first commit to add new annotation model
  which eventually will replace old legacy annotations
  like ShellComponent, ShellMethod, @ShellOption, etc.
- Adds subset of features needed for parity with manual
  use of CommandRegistration.
- Relates #637
- Relates #638
- Relates #639
- Relates #640
- Relates #641
This commit is contained in:
Janne Valkealahti
2023-01-27 12:04:21 +00:00
parent d1c482cd49
commit de1a3baf18
22 changed files with 1781 additions and 15 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017-2022 the original author or authors.
* 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.
@@ -18,10 +18,11 @@ package org.springframework.shell.samples;
import org.jline.utils.AttributedString;
import org.jline.utils.AttributedStyle;
import org.springframework.boot.SpringApplication;
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;
/**
@@ -33,6 +34,7 @@ import org.springframework.shell.jline.PromptProvider;
* @author Janne Valkealahti
*/
@SpringBootApplication
@CommandScan
public class SpringShellSample {
public static void main(String[] args) throws Exception {

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

@@ -32,6 +32,8 @@ 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;

View File

@@ -23,6 +23,10 @@ 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;
@@ -42,7 +46,6 @@ public class InteractiveCompletionCommands {
return "Hello " + arg1;
}
@Bean
Test1ValuesProvider test1ValuesProvider() {
return new Test1ValuesProvider();
@@ -54,6 +57,34 @@ public class InteractiveCompletionCommands {
}
}
@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(ref = "test1CompletionProvider") String arg1,
@Option(longNames = "arg2", required = true) @OptionValues(ref = "test2CompletionProvider") String arg2
) {
return "Hello " + arg1;
}
@Bean
CompletionProvider test1CompletionProvider() {
return ctx -> {
Test1ValuesProvider test1ValuesProvider = new Test1ValuesProvider();
return test1ValuesProvider.complete(ctx);
};
}
@Bean
CompletionProvider test2CompletionProvider() {
return ctx -> {
Test1ValuesProvider test1ValuesProvider = new Test1ValuesProvider();
return test1ValuesProvider.complete(ctx);
};
}
}
@Component
public static class Registration extends BaseE2ECommands {

View File

@@ -17,6 +17,8 @@ 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;
@@ -39,6 +41,17 @@ public class RequiredValueCommands {
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 {

View File

@@ -18,20 +18,25 @@ package org.springframework.shell.samples.e2e;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.springframework.shell.command.annotation.EnableCommand;
import org.springframework.shell.samples.AbstractSampleTests;
import org.springframework.shell.samples.e2e.RequiredValueCommands.Annotation;
import org.springframework.shell.samples.e2e.RequiredValueCommands.LegacyAnnotation;
import org.springframework.shell.samples.e2e.RequiredValueCommands.Registration;
import org.springframework.shell.test.ShellTestClient.BaseShellSession;
import org.springframework.test.context.ContextConfiguration;
@ContextConfiguration(classes = {LegacyAnnotation.class, Registration.class})
@EnableCommand(Annotation.class)
class RequiredValueCommandsTests extends AbstractSampleTests {
@ParameterizedTest
@CsvSource({
"e2e anno required-value,false",
"e2e annox required-value,false",
"e2e reg required-value,false",
"e2e anno required-value,true",
"e2e annox required-value,true",
"e2e reg required-value,true"
})
void shouldRequireOption(String command, boolean interactive) {