Skip to content

Commit 3d3d372

Browse files
committed
Use correct type with set
- When target is set and only one option argument is given, we should not convert to list as user expects string to xxx Converter to work. - This is how it used to work and previous changes caused regression. - Bug is actually in an old parser and new parser works fine. - Fixes #667
1 parent cf1a6f7 commit 3d3d372

File tree

2 files changed

+291
-0
lines changed

2 files changed

+291
-0
lines changed
Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
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.samples.e2e;
17+
18+
import java.util.Arrays;
19+
import java.util.HashSet;
20+
import java.util.Set;
21+
22+
import org.springframework.context.annotation.Bean;
23+
import org.springframework.context.annotation.Configuration;
24+
import org.springframework.core.convert.converter.Converter;
25+
import org.springframework.shell.command.CommandRegistration;
26+
import org.springframework.shell.command.annotation.Command;
27+
import org.springframework.shell.command.annotation.Option;
28+
import org.springframework.shell.standard.ShellComponent;
29+
import org.springframework.shell.standard.ShellMethod;
30+
import org.springframework.shell.standard.ShellOption;
31+
import org.springframework.stereotype.Component;
32+
33+
public class OptionConversionCommands {
34+
35+
@ShellComponent
36+
public static class LegacyAnnotation extends BaseE2ECommands {
37+
38+
@ShellMethod(key = LEGACY_ANNO + "option-conversion-integer", group = GROUP)
39+
public String optionConversionIntegerAnnotation(
40+
@ShellOption Integer arg1
41+
) {
42+
return "Hello " + arg1;
43+
}
44+
45+
@ShellMethod(key = LEGACY_ANNO + "option-conversion-custom", group = GROUP)
46+
public String optionConversionCustomAnnotation(
47+
@ShellOption MyPojo arg1
48+
) {
49+
return "Hello " + arg1;
50+
}
51+
52+
@ShellMethod(key = LEGACY_ANNO + "option-conversion-customset", group = GROUP)
53+
public String optionConversionCustomSetAnnotation(
54+
@ShellOption Set<MyPojo> arg1
55+
) {
56+
return "Hello " + arg1;
57+
}
58+
59+
@ShellMethod(key = LEGACY_ANNO + "option-conversion-customarray", group = GROUP)
60+
public String optionConversionCustomArrayAnnotation(
61+
@ShellOption MyPojo[] arg1
62+
) {
63+
return "Hello " + Arrays.asList(arg1);
64+
}
65+
}
66+
67+
@Command(command = BaseE2ECommands.ANNO, group = BaseE2ECommands.GROUP)
68+
public static class Annotation extends BaseE2ECommands {
69+
70+
@Command(command = "option-conversion-integer")
71+
public String optionConversionIntegerAnnotation(
72+
@Option(longNames = "arg1")
73+
Integer arg1
74+
) {
75+
return "Hello " + arg1;
76+
}
77+
78+
@Command(command = "option-conversion-custom")
79+
public String optionConversionCustomAnnotation(
80+
@Option(longNames = "arg1")
81+
MyPojo arg1
82+
) {
83+
return "Hello " + arg1;
84+
}
85+
86+
@Command(command = "option-conversion-customset")
87+
public String optionConversionCustomSetAnnotation(
88+
@Option(longNames = "arg1")
89+
Set<MyPojo> arg1
90+
) {
91+
return "Hello " + arg1;
92+
}
93+
94+
@Command(command = "option-conversion-customarray")
95+
public String optionConversionCustomArrayAnnotation(
96+
@Option(longNames = "arg1")
97+
MyPojo[] arg1
98+
) {
99+
return "Hello " + Arrays.asList(arg1);
100+
}
101+
}
102+
103+
@Component
104+
public static class Registration extends BaseE2ECommands {
105+
106+
@Bean
107+
public CommandRegistration optionConversionIntegerRegistration() {
108+
return getBuilder()
109+
.command(REG, "option-conversion-integer")
110+
.group(GROUP)
111+
.withOption()
112+
.longNames("arg1")
113+
.type(Integer.class)
114+
.and()
115+
.withTarget()
116+
.function(ctx -> {
117+
Integer arg1 = ctx.getOptionValue("arg1");
118+
return "Hello " + arg1;
119+
})
120+
.and()
121+
.build();
122+
}
123+
124+
@Bean
125+
public CommandRegistration optionConversionCustomRegistration() {
126+
return getBuilder()
127+
.command(REG, "option-conversion-custom")
128+
.group(GROUP)
129+
.withOption()
130+
.longNames("arg1")
131+
.type(MyPojo.class)
132+
.and()
133+
.withTarget()
134+
.function(ctx -> {
135+
MyPojo arg1 = ctx.getOptionValue("arg1");
136+
return "Hello " + arg1;
137+
})
138+
.and()
139+
.build();
140+
}
141+
142+
@Bean
143+
public CommandRegistration optionConversionCustomSetRegistration() {
144+
return getBuilder()
145+
.command(REG, "option-conversion-customset")
146+
.group(GROUP)
147+
.withOption()
148+
.longNames("arg1")
149+
.type(Set.class)
150+
.and()
151+
.withTarget()
152+
.function(ctx -> {
153+
Set<MyPojo> arg1 = ctx.getOptionValue("arg1");
154+
return "Hello " + arg1;
155+
})
156+
.and()
157+
.build();
158+
}
159+
160+
@Bean
161+
public CommandRegistration optionConversionCustomArrayRegistration() {
162+
return getBuilder()
163+
.command(REG, "option-conversion-customarray")
164+
.group(GROUP)
165+
.withOption()
166+
.longNames("arg1")
167+
.type(MyPojo[].class)
168+
.and()
169+
.withTarget()
170+
.function(ctx -> {
171+
MyPojo[] arg1 = ctx.getOptionValue("arg1");
172+
return "Hello " + Arrays.asList(arg1);
173+
})
174+
.and()
175+
.build();
176+
}
177+
}
178+
179+
@Configuration(proxyBeanMethods = false)
180+
public static class CommonConfiguration {
181+
182+
@Bean
183+
public Converter<String, MyPojo> stringToMyPojoConverter() {
184+
return new StringToMyPojoConverter();
185+
}
186+
187+
@Bean
188+
public Converter<String, Set<MyPojo>> stringToMyPojoSetConverter() {
189+
return new StringToMyPojoSetConverter();
190+
}
191+
}
192+
193+
public static class MyPojo {
194+
private String arg;
195+
196+
public MyPojo(String arg) {
197+
this.arg = arg;
198+
}
199+
200+
public String getArg() {
201+
return arg;
202+
}
203+
204+
public void setArg(String arg) {
205+
this.arg = arg;
206+
}
207+
208+
@Override
209+
public String toString() {
210+
return "MyPojo [arg=" + arg + "]";
211+
}
212+
}
213+
214+
static class StringToMyPojoConverter implements Converter<String, MyPojo> {
215+
216+
@Override
217+
public MyPojo convert(String from) {
218+
return new MyPojo(from);
219+
}
220+
}
221+
222+
static class StringToMyPojoSetConverter implements Converter<String, Set<MyPojo>> {
223+
224+
@Override
225+
public Set<MyPojo> convert(String from) {
226+
Set<MyPojo> set = new HashSet<>();
227+
set.add(new MyPojo(from));
228+
return set;
229+
}
230+
}
231+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
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.samples.e2e;
17+
18+
import org.junit.jupiter.params.ParameterizedTest;
19+
20+
import org.springframework.shell.command.annotation.EnableCommand;
21+
import org.springframework.shell.samples.AbstractSampleTests;
22+
import org.springframework.shell.samples.e2e.OptionConversionCommands.Annotation;
23+
import org.springframework.shell.samples.e2e.OptionConversionCommands.CommonConfiguration;
24+
import org.springframework.shell.samples.e2e.OptionConversionCommands.LegacyAnnotation;
25+
import org.springframework.shell.samples.e2e.OptionConversionCommands.Registration;
26+
import org.springframework.shell.test.ShellTestClient.BaseShellSession;
27+
import org.springframework.test.context.ContextConfiguration;
28+
29+
@ContextConfiguration(classes = { LegacyAnnotation.class, Registration.class, CommonConfiguration.class })
30+
@EnableCommand(Annotation.class)
31+
class OptionConversionCommandsTests extends AbstractSampleTests {
32+
33+
@ParameterizedTest
34+
@E2ESource(command = "option-conversion-integer --arg1 1")
35+
void optionConversionInteger(String command, boolean interactive) {
36+
BaseShellSession<?> session = createSession(command, interactive);
37+
assertScreenContainsText(session, "Hello 1");
38+
}
39+
40+
@ParameterizedTest
41+
@E2ESource(command = "option-conversion-custom --arg1 hi")
42+
void optionConversionCustom(String command, boolean interactive) {
43+
BaseShellSession<?> session = createSession(command, interactive);
44+
assertScreenContainsText(session, "Hello MyPojo [arg=hi]");
45+
}
46+
47+
@ParameterizedTest
48+
@E2ESource(command = "option-conversion-customset --arg1 hi")
49+
void optionConversionCustomSet(String command, boolean interactive) {
50+
BaseShellSession<?> session = createSession(command, interactive);
51+
assertScreenContainsText(session, "Hello [MyPojo [arg=hi]]");
52+
}
53+
54+
@ParameterizedTest
55+
@E2ESource(command = "option-conversion-customarray --arg1 hi")
56+
void optionConversionCustomArray(String command, boolean interactive) {
57+
BaseShellSession<?> session = createSession(command, interactive);
58+
assertScreenContainsText(session, "Hello [MyPojo [arg=hi]]");
59+
}
60+
}

0 commit comments

Comments
 (0)