Skip to content

Commit 10c9a6e

Browse files
authored
fix: styling of select boxes for issue and version parameter (#761)
fixes #760
1 parent 185cc31 commit 10c9a6e

File tree

9 files changed

+315
-48
lines changed

9 files changed

+315
-48
lines changed

src/main/java/hudson/plugins/jira/listissuesparameter/JiraIssueParameterDefinition.java

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,22 +23,29 @@
2323
import com.atlassian.jira.rest.client.api.domain.IssueField;
2424
import hudson.Extension;
2525
import hudson.cli.CLICommand;
26+
import hudson.model.Item;
2627
import hudson.model.Job;
2728
import hudson.model.ParameterDefinition;
2829
import hudson.model.ParameterValue;
30+
import hudson.model.ParametersDefinitionProperty;
2931
import hudson.plugins.jira.JiraSession;
3032
import hudson.plugins.jira.JiraSite;
33+
import hudson.plugins.jira.Messages;
34+
import hudson.util.ListBoxModel;
3135
import java.io.IOException;
3236
import java.util.ArrayList;
3337
import java.util.List;
3438
import java.util.concurrent.TimeoutException;
3539
import net.sf.json.JSONObject;
3640
import org.apache.commons.lang.StringUtils;
3741
import org.jenkinsci.Symbol;
42+
import org.kohsuke.stapler.AncestorInPath;
3843
import org.kohsuke.stapler.DataBoundConstructor;
3944
import org.kohsuke.stapler.DataBoundSetter;
45+
import org.kohsuke.stapler.QueryParameter;
4046
import org.kohsuke.stapler.Stapler;
4147
import org.kohsuke.stapler.StaplerRequest2;
48+
import org.kohsuke.stapler.interceptor.RequirePOST;
4249

4350
public class JiraIssueParameterDefinition extends ParameterDefinition {
4451
private static final long serialVersionUID = 3927562542249244416L;
@@ -55,7 +62,7 @@ public JiraIssueParameterDefinition(String name, String description, String jira
5562
@Override
5663
public ParameterValue createValue(StaplerRequest2 req) {
5764
String[] values = req.getParameterValues(getName());
58-
if (values == null || values.length != 1) {
65+
if (values == null || values.length != 1 || values[0].isEmpty()) {
5966
return null;
6067
}
6168

@@ -76,7 +83,10 @@ public ParameterValue createValue(CLICommand command, String value) throws IOExc
7683
public List<JiraIssueParameterDefinition.Result> getIssues()
7784
throws IOException, TimeoutException, RestClientException {
7885
Job<?, ?> job = Stapler.getCurrentRequest2().findAncestorObject(Job.class);
86+
return getIssues(job);
87+
}
7988

89+
List<JiraIssueParameterDefinition.Result> getIssues(Job<?, ?> job) {
8090
JiraSite site = JiraSite.get(job);
8191
if (site == null) {
8292
throw new IllegalStateException(
@@ -123,6 +133,25 @@ public static class DescriptorImpl extends ParameterDescriptor {
123133
public String getDisplayName() {
124134
return "Jira Issue Parameter";
125135
}
136+
137+
@RequirePOST
138+
public ListBoxModel doFillValueItems(@AncestorInPath Job<?, ?> job, @QueryParameter String name) {
139+
ListBoxModel items = new ListBoxModel();
140+
if (job.hasPermission(Item.BUILD)) {
141+
ParametersDefinitionProperty prop = job.getProperty(ParametersDefinitionProperty.class);
142+
if (prop != null) {
143+
ParameterDefinition def = prop.getParameterDefinition(name);
144+
if (def instanceof JiraIssueParameterDefinition jiraIssueDef) {
145+
List<Result> issueValues = jiraIssueDef.getIssues(job);
146+
issueValues.forEach(it -> items.add(it.key + ": " + it.summary, it.key));
147+
}
148+
}
149+
}
150+
if (items.isEmpty()) {
151+
items.add(Messages.JiraIssueParameterDefinition_NoIssueMatchedSearch(), "");
152+
}
153+
return items;
154+
}
126155
}
127156

128157
public static class Result {

src/main/java/hudson/plugins/jira/versionparameter/JiraVersionParameterDefinition.java

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,28 @@
44
import com.atlassian.jira.rest.client.api.domain.Version;
55
import hudson.Extension;
66
import hudson.cli.CLICommand;
7+
import hudson.model.Item;
78
import hudson.model.Job;
89
import hudson.model.ParameterDefinition;
910
import hudson.model.ParameterValue;
11+
import hudson.model.ParametersDefinitionProperty;
1012
import hudson.plugins.jira.JiraSession;
1113
import hudson.plugins.jira.JiraSite;
14+
import hudson.plugins.jira.Messages;
15+
import hudson.util.ListBoxModel;
1216
import java.io.IOException;
1317
import java.util.List;
1418
import java.util.Objects;
1519
import java.util.regex.Pattern;
1620
import java.util.stream.Collectors;
1721
import net.sf.json.JSONObject;
1822
import org.jenkinsci.Symbol;
23+
import org.kohsuke.stapler.AncestorInPath;
1924
import org.kohsuke.stapler.DataBoundConstructor;
25+
import org.kohsuke.stapler.QueryParameter;
2026
import org.kohsuke.stapler.Stapler;
2127
import org.kohsuke.stapler.StaplerRequest2;
28+
import org.kohsuke.stapler.interceptor.RequirePOST;
2229

2330
public class JiraVersionParameterDefinition extends ParameterDefinition {
2431
private static final long serialVersionUID = 4232979892748310160L;
@@ -50,7 +57,7 @@ public JiraVersionParameterDefinition(
5057
@Override
5158
public ParameterValue createValue(StaplerRequest2 req) {
5259
String[] values = req.getParameterValues(getName());
53-
if (values == null || values.length != 1) {
60+
if (values == null || values.length != 1 || values[0].isEmpty()) {
5461
return null;
5562
}
5663
return new JiraVersionParameterValue(getName(), values[0]);
@@ -69,7 +76,10 @@ public ParameterValue createValue(CLICommand command, String value) throws IOExc
6976

7077
public List<JiraVersionParameterDefinition.Result> getVersions() throws IOException, RestClientException {
7178
Job<?, ?> contextJob = Stapler.getCurrentRequest2().findAncestorObject(Job.class);
79+
return getVersions(contextJob);
80+
}
7281

82+
List<JiraVersionParameterDefinition.Result> getVersions(Job<?, ?> contextJob) {
7383
JiraSite site = JiraSite.get(contextJob);
7484
if (site == null) {
7585
throw new IllegalStateException(
@@ -173,6 +183,25 @@ public static class DescriptorImpl extends ParameterDescriptor {
173183
public String getDisplayName() {
174184
return "Jira Release Version Parameter";
175185
}
186+
187+
@RequirePOST
188+
public ListBoxModel doFillVersionItems(@AncestorInPath Job<?, ?> job, @QueryParameter String name) {
189+
ListBoxModel items = new ListBoxModel();
190+
if (job.hasPermission(Item.BUILD)) {
191+
ParametersDefinitionProperty prop = job.getProperty(ParametersDefinitionProperty.class);
192+
if (prop != null) {
193+
ParameterDefinition def = prop.getParameterDefinition(name);
194+
if (def instanceof JiraVersionParameterDefinition jiraVersionDef) {
195+
List<JiraVersionParameterDefinition.Result> issueValues = jiraVersionDef.getVersions(job);
196+
issueValues.forEach(it -> items.add(it.name));
197+
}
198+
}
199+
}
200+
if (items.isEmpty()) {
201+
items.add(Messages.JiraVersionParameterDefinition_NoVersionsMatchedSearch(), "");
202+
}
203+
return items;
204+
}
176205
}
177206

178207
public static class Result {

src/main/resources/hudson/plugins/jira/Messages.properties

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,5 @@ ErrorCommentingIssues=[Jira] Could not comment on some issues: {0}
4747
JiraSite.threadExecutorMinimunSize = Thread Executor Size must be at least {0} (higher values are recommended)
4848
JiraSite.timeoutMinimunValue = Connection timeout must be at least {0}
4949
JiraSite.readTimeoutMinimunValue = Read timeout must be at least {0}
50+
JiraIssueParameterDefinition.NoIssueMatchedSearch = No issues matched the search
51+
JiraVersionParameterDefinition.NoVersionsMatchedSearch = No version matched the search

src/main/resources/hudson/plugins/jira/listissuesparameter/JiraIssueParameterDefinition/index.jelly

Lines changed: 8 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -16,26 +16,12 @@ limitations under the License.
1616
<!-- this is the page fragment displayed when triggering a new build -->
1717
<?jelly escape-by-default='true'?>
1818
<j:jelly xmlns:j="jelly:core" xmlns:f="/lib/form">
19-
<j:set var="escapeEntryTitleAndDescription" value="false"/>
20-
<f:entry title="${h.escape(it.name)}" description="${it.formattedDescription}">
21-
<!-- this div is required because of ParametersDefinitionProperty.java#117 -->
22-
<div name="parameter">
23-
<input type="hidden" name="name" value="${it.name}"/>
24-
<j:choose>
25-
<j:when test="${it.issues == null or it.issues.size() == 0}">
26-
<!-- no tags at all -->
27-
${%No issues matched the search.}<br/>
28-
${%If you trigger the build, it will likely fail.}
29-
</j:when>
30-
<j:otherwise>
31-
<!-- everything is fine, we can display the drop-down list to the user -->
32-
<select name="value">
33-
<j:forEach var="issue" items="${it.issues}">
34-
<option value="${issue.key}">${issue.key}: ${issue.summary}</option>
35-
</j:forEach>
36-
</select>
37-
</j:otherwise>
38-
</j:choose>
39-
</div>
40-
</f:entry>
19+
<j:set var="escapeEntryTitleAndDescription" value="false"/>
20+
<j:set var="descriptor" value="${it.descriptor}"/>
21+
<f:entry field="value" title="${h.escape(it.name)}" description="${it.formattedDescription}">
22+
<div name="parameter">
23+
<input type="hidden" name="name" value="${it.name}"/>
24+
<f:select/>
25+
</div>
26+
</f:entry>
4127
</j:jelly>

src/main/resources/hudson/plugins/jira/listissuesparameter/JiraIssueParameterValue/value.jelly

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ limitations under the License.
1717
<j:jelly xmlns:j="jelly:core" xmlns:f="/lib/form">
1818
<j:set var="escapeEntryTitleAndDescription" value="false"/>
1919
<f:entry title="${h.escape(it.name)}">
20-
<f:textbox name="${it.name}" value="${it.issue}" />
20+
<j:set var="readOnlyMode" value="true"/>
21+
<f:textbox name="${it.name}" value="${it.value}"/>
2122
</f:entry>
2223
</j:jelly>
Lines changed: 8 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,12 @@
11
<!-- this is the page fragment displayed when triggering a new build -->
22
<?jelly escape-by-default='true'?>
33
<j:jelly xmlns:j="jelly:core" xmlns:f="/lib/form">
4-
<j:set var="escapeEntryTitleAndDescription" value="false"/>
5-
<f:entry title="${h.escape(it.name)}" description="${it.formattedDescription}">
6-
<!-- this div is required because of ParametersDefinitionProperty.java#117 -->
7-
<div name="parameter">
8-
<input type="hidden" name="name" value="${it.name}"/>
9-
<j:choose>
10-
<j:when test="${it.versions == null or it.versions.size() == 0}">
11-
<!-- no tags at all -->
12-
${%No versions found meeting your filter criteria.}<br/>
13-
${%If you trigger the build, it will likely fail.}
14-
</j:when>
15-
<j:otherwise>
16-
<!-- everything is fine, we can display the drop-down list to the user -->
17-
<select name="version">
18-
<j:forEach var="version" items="${it.versions}">
19-
<option value="${version.name}">${version.name}</option>
20-
</j:forEach>
21-
</select>
22-
</j:otherwise>
23-
</j:choose>
24-
</div>
25-
</f:entry>
4+
<j:set var="escapeEntryTitleAndDescription" value="false"/>
5+
<j:set var="descriptor" value="${it.descriptor}"/>
6+
<f:entry field="version" title="${h.escape(it.name)}" description="${it.formattedDescription}">
7+
<div name="parameter">
8+
<input type="hidden" name="name" value="${it.name}"/>
9+
<f:select/>
10+
</div>
11+
</f:entry>
2612
</j:jelly>

src/main/resources/hudson/plugins/jira/versionparameter/JiraVersionParameterValue/value.jelly

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
<j:jelly xmlns:j="jelly:core" xmlns:f="/lib/form">
33
<j:set var="escapeEntryTitleAndDescription" value="false"/>
44
<f:entry title="${h.escape(it.name)}">
5-
<f:textbox name="${it.name}" value="${it.version}" />
5+
<j:set var="readOnlyMode" value="true"/>
6+
<f:textbox name="${it.name}" value="${it.version}"/>
67
</f:entry>
78
</j:jelly>
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
package hudson.plugins.jira.listissuesparameter;
2+
3+
import static org.hamcrest.MatcherAssert.assertThat;
4+
import static org.hamcrest.Matchers.hasSize;
5+
import static org.junit.jupiter.api.Assertions.assertEquals;
6+
import static org.junit.jupiter.api.Assertions.assertNotNull;
7+
import static org.junit.jupiter.api.Assertions.assertNull;
8+
import static org.mockito.ArgumentMatchers.any;
9+
import static org.mockito.Mockito.verify;
10+
import static org.mockito.Mockito.when;
11+
12+
import com.atlassian.jira.rest.client.api.domain.Issue;
13+
import hudson.model.Item;
14+
import hudson.model.Job;
15+
import hudson.model.ParameterValue;
16+
import hudson.model.ParametersDefinitionProperty;
17+
import hudson.plugins.jira.Messages;
18+
import hudson.util.ListBoxModel;
19+
import java.util.List;
20+
import java.util.stream.Stream;
21+
import org.junit.jupiter.api.Nested;
22+
import org.junit.jupiter.api.Test;
23+
import org.junit.jupiter.api.extension.ExtendWith;
24+
import org.junit.jupiter.params.ParameterizedTest;
25+
import org.junit.jupiter.params.provider.Arguments;
26+
import org.junit.jupiter.params.provider.MethodSource;
27+
import org.junit.jupiter.params.provider.NullSource;
28+
import org.kohsuke.stapler.StaplerRequest2;
29+
import org.mockito.Mock;
30+
import org.mockito.junit.jupiter.MockitoExtension;
31+
32+
@ExtendWith(MockitoExtension.class)
33+
class JiraIssueParameterDefinitionTest {
34+
35+
private JiraIssueParameterDefinition definition =
36+
new JiraIssueParameterDefinition("PARAM_NAME", "desc", "jqlQuery");
37+
38+
static Stream<Arguments> createValueInvalidParameters() {
39+
return Stream.of(
40+
Arguments.of((Object) new String[] {}),
41+
Arguments.of((Object) new String[] {"a", "b"}),
42+
Arguments.of((Object) new String[] {""}));
43+
}
44+
45+
@ParameterizedTest
46+
@NullSource
47+
@MethodSource("createValueInvalidParameters")
48+
void shouldCreateNullParameterForInvalidValues(String[] values, @Mock StaplerRequest2 req) {
49+
when(req.getParameterValues(any())).thenReturn(values);
50+
51+
ParameterValue result = definition.createValue(req);
52+
53+
assertNull(result);
54+
}
55+
56+
@Test
57+
void shouldCreateValue(@Mock StaplerRequest2 req) {
58+
when(req.getParameterValues(any())).thenReturn(new String[] {"value"});
59+
60+
ParameterValue result = definition.createValue(req);
61+
62+
assertNotNull(result);
63+
assertEquals("PARAM_NAME", result.getName());
64+
assertEquals("value", result.getValue());
65+
}
66+
67+
@Nested
68+
class DescriptorImplTest {
69+
70+
private JiraIssueParameterDefinition.DescriptorImpl uut = new JiraIssueParameterDefinition.DescriptorImpl();
71+
72+
@Test
73+
void shouldFillValueItems(
74+
@Mock Job<?, ?> job,
75+
@Mock ParametersDefinitionProperty propertyDef,
76+
@Mock JiraIssueParameterDefinition paramDef,
77+
@Mock Issue issue) {
78+
when(job.hasPermission(Item.BUILD)).thenReturn(true);
79+
when(job.getProperty(ParametersDefinitionProperty.class)).thenReturn(propertyDef);
80+
when(propertyDef.getParameterDefinition("PARAM_NAME")).thenReturn(paramDef);
81+
when(issue.getKey()).thenReturn("JIRA-1234");
82+
when(issue.getSummary()).thenReturn("Summary");
83+
JiraIssueParameterDefinition.Result item = new JiraIssueParameterDefinition.Result(issue, null);
84+
when(paramDef.getIssues(any())).thenReturn(List.of(item));
85+
86+
ListBoxModel result = uut.doFillValueItems(job, "PARAM_NAME");
87+
88+
assertThat(result, hasSize(1));
89+
ListBoxModel.Option option = result.get(0);
90+
assertEquals("JIRA-1234", option.value);
91+
assertEquals("JIRA-1234: Summary", option.name);
92+
verify(job).hasPermission(Item.BUILD);
93+
}
94+
95+
@Test
96+
void shouldNotFillValueItemsIfPermissionMissing(@Mock Job<?, ?> job) {
97+
ListBoxModel result = uut.doFillValueItems(job, "PARAM_NAME");
98+
99+
assertThat(result, hasSize(1));
100+
ListBoxModel.Option option = result.get(0);
101+
assertEquals("", option.value);
102+
assertEquals(Messages.JiraIssueParameterDefinition_NoIssueMatchedSearch(), option.name);
103+
verify(job).hasPermission(Item.BUILD);
104+
}
105+
106+
@Test
107+
void shouldHaveNoSearchMatchesItemIfSearchMatchesNoItem(
108+
@Mock Job<?, ?> job,
109+
@Mock ParametersDefinitionProperty propertyDef,
110+
@Mock JiraIssueParameterDefinition paramDef,
111+
@Mock Issue issue) {
112+
when(job.hasPermission(Item.BUILD)).thenReturn(true);
113+
when(job.getProperty(ParametersDefinitionProperty.class)).thenReturn(propertyDef);
114+
when(propertyDef.getParameterDefinition("PARAM_NAME")).thenReturn(paramDef);
115+
116+
ListBoxModel result = uut.doFillValueItems(job, "PARAM_NAME");
117+
118+
assertThat(result, hasSize(1));
119+
ListBoxModel.Option option = result.get(0);
120+
assertEquals("", option.value);
121+
assertEquals(Messages.JiraIssueParameterDefinition_NoIssueMatchedSearch(), option.name);
122+
verify(job).hasPermission(Item.BUILD);
123+
}
124+
}
125+
}

0 commit comments

Comments
 (0)