Skip to content

Commit 042d56e

Browse files
committed
Add HandlerMethodArgumentResolver for @option
- New OptionMethodArgumentResolver which is similar than ShellOptionMethodArgumentResolver for @ShellOption. - Add missing annotation commands for e2e test command. - Fixes #767
1 parent afacf86 commit 042d56e

File tree

3 files changed

+114
-4
lines changed

3 files changed

+114
-4
lines changed

spring-shell-autoconfigure/src/main/java/org/springframework/shell/boot/ParameterResolverAutoConfiguration.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2022 the original author or authors.
2+
* Copyright 2022-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -25,6 +25,7 @@
2525
import org.springframework.shell.command.ArgumentHeaderMethodArgumentResolver;
2626
import org.springframework.shell.command.CommandContextMethodArgumentResolver;
2727
import org.springframework.shell.command.CommandExecution.CommandExecutionHandlerMethodArgumentResolvers;
28+
import org.springframework.shell.command.annotation.support.OptionMethodArgumentResolver;
2829
import org.springframework.shell.completion.CompletionResolver;
2930
import org.springframework.shell.completion.RegistrationOptionsCompletionResolver;
3031
import org.springframework.shell.config.ShellConversionServiceSupplier;
@@ -46,6 +47,7 @@ public CommandExecutionHandlerMethodArgumentResolvers commandExecutionHandlerMet
4647
resolvers.add(new HeadersMethodArgumentResolver());
4748
resolvers.add(new CommandContextMethodArgumentResolver());
4849
resolvers.add(new ShellOptionMethodArgumentResolver(shellConversionServiceSupplier.get(), null));
50+
resolvers.add(new OptionMethodArgumentResolver(shellConversionServiceSupplier.get(), null));
4951
return new CommandExecutionHandlerMethodArgumentResolvers(resolvers);
5052
}
5153
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/*
2+
* Copyright 2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.shell.command.annotation.support;
17+
18+
import java.util.Arrays;
19+
import java.util.List;
20+
import java.util.stream.Collectors;
21+
22+
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
23+
import org.springframework.core.MethodParameter;
24+
import org.springframework.core.convert.ConversionService;
25+
import org.springframework.lang.Nullable;
26+
import org.springframework.messaging.Message;
27+
import org.springframework.messaging.MessageHandlingException;
28+
import org.springframework.shell.command.annotation.Option;
29+
import org.springframework.shell.support.AbstractArgumentMethodArgumentResolver;
30+
import org.springframework.util.Assert;
31+
import org.springframework.util.StringUtils;
32+
33+
/**
34+
* Resolver for {@link Option @Option} arguments.
35+
*
36+
* @author Janne Valkealahti
37+
*/
38+
public class OptionMethodArgumentResolver extends AbstractArgumentMethodArgumentResolver {
39+
40+
public OptionMethodArgumentResolver(ConversionService conversionService,
41+
@Nullable ConfigurableBeanFactory beanFactory) {
42+
super(conversionService, beanFactory);
43+
}
44+
45+
@Override
46+
public boolean supportsParameter(MethodParameter parameter) {
47+
return parameter.hasParameterAnnotation(Option.class);
48+
}
49+
50+
@Override
51+
protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
52+
Option annot = parameter.getParameterAnnotation(Option.class);
53+
Assert.state(annot != null, "No Option annotation");
54+
List<String> names = Arrays.stream(annot != null ? annot.longNames() : new String[0])
55+
.collect(Collectors.toList());
56+
return new HeaderNamedValueInfo(annot, names);
57+
}
58+
59+
@Override
60+
@Nullable
61+
protected Object resolveArgumentInternal(MethodParameter parameter, Message<?> message, List<String> names)
62+
throws Exception {
63+
for (String name : names) {
64+
if (message.getHeaders().containsKey(ARGUMENT_PREFIX + name)) {
65+
return message.getHeaders().get(ARGUMENT_PREFIX + name);
66+
}
67+
}
68+
return null;
69+
}
70+
71+
@Override
72+
protected void handleMissingValue(List<String> headerName, MethodParameter parameter, Message<?> message) {
73+
throw new MessageHandlingException(message,
74+
"Missing headers '" + StringUtils.collectionToCommaDelimitedString(headerName)
75+
+ "' for method parameter type [" + parameter.getParameterType() + "]");
76+
}
77+
78+
private static final class HeaderNamedValueInfo extends NamedValueInfo {
79+
80+
private HeaderNamedValueInfo(Option annotation, List<String> names) {
81+
super(names, false, null);
82+
}
83+
}
84+
}

spring-shell-samples/src/main/java/org/springframework/shell/samples/e2e/OptionNamingCommands.java

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717

1818
import org.springframework.context.annotation.Bean;
1919
import org.springframework.shell.command.CommandRegistration;
20+
import org.springframework.shell.command.annotation.Command;
21+
import org.springframework.shell.command.annotation.Option;
2022
import org.springframework.shell.standard.ShellComponent;
2123
import org.springframework.shell.standard.ShellMethod;
2224
import org.springframework.shell.standard.ShellOption;
@@ -28,12 +30,28 @@ public class OptionNamingCommands {
2830
public static class LegacyAnnotation extends BaseE2ECommands {
2931

3032
@ShellMethod(key = LEGACY_ANNO + "option-naming-1", group = GROUP)
31-
public void testOptionNaming1Annotation(
33+
public String testOptionNaming1Annotation(
3234
@ShellOption("from_snake") String snake,
3335
@ShellOption("fromCamel") String camel,
3436
@ShellOption("from-kebab") String kebab,
3537
@ShellOption("FromPascal") String pascal
3638
) {
39+
return String.format("snake='%s' camel='%s' kebab='%s' pascal='%s' ", snake, camel, kebab, pascal);
40+
}
41+
42+
}
43+
44+
@Command(command = BaseE2ECommands.ANNO, group = BaseE2ECommands.GROUP)
45+
public static class Annotation extends BaseE2ECommands {
46+
47+
@Command(command = "option-naming-1")
48+
public String testOptionNaming1Annotation(
49+
@Option(longNames = "from_snake") String snake,
50+
@Option(longNames = "fromCamel") String camel,
51+
@Option(longNames = "from-kebab") String kebab,
52+
@Option(longNames = "FromPascal") String pascal
53+
) {
54+
return String.format("snake='%s' camel='%s' kebab='%s' pascal='%s' ", snake, camel, kebab, pascal);
3755
}
3856

3957
}
@@ -65,10 +83,16 @@ public CommandRegistration testOptionNaming1Registration() {
6583
.withOption()
6684
.longNames("arg1")
6785
.nameModifier(name -> "x" + name)
68-
.required()
86+
// .required()
6987
.and()
7088
.withTarget()
71-
.consumer(ctx -> {})
89+
.function(ctx -> {
90+
String snake = ctx.getOptionValue("from_snake");
91+
String camel = ctx.getOptionValue("fromCamel");
92+
String kebab = ctx.getOptionValue("from-kebab");
93+
String pascal = ctx.getOptionValue("FromPascal");
94+
return String.format("snake='%s' camel='%s' kebab='%s' pascal='%s' ", snake, camel, kebab, pascal);
95+
})
7296
.and()
7397
.build();
7498
}

0 commit comments

Comments
 (0)