Skip to content

Commit 2ed7a0d

Browse files
authored
Merge pull request #209 from abayer/pipeline-syntax-additions
Switch Snippetizer sidepanel links to use an extension point
2 parents a5143ee + 46e46cd commit 2ed7a0d

File tree

5 files changed

+273
-5
lines changed

5 files changed

+273
-5
lines changed

src/main/java/org/jenkinsci/plugins/workflow/cps/Snippetizer.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
package org.jenkinsci.plugins.workflow.cps;
2626

2727
import hudson.Extension;
28+
import hudson.ExtensionList;
2829
import hudson.Functions;
2930
import hudson.model.Action;
3031
import hudson.model.Describable;
@@ -45,6 +46,7 @@
4546
import java.util.logging.Level;
4647
import java.util.logging.Logger;
4748
import javax.annotation.CheckForNull;
49+
import javax.annotation.Nonnull;
4850
import javax.lang.model.SourceVersion;
4951
import jenkins.model.Jenkins;
5052
import jenkins.model.TransientActionFactory;
@@ -525,6 +527,14 @@ public HttpResponse doGenerateSnippet(StaplerRequest req, @QueryParameter String
525527
return req.findAncestorObject(Item.class);
526528
}
527529

530+
/**
531+
* Used to generate the list of links on the sidepanel.
532+
*/
533+
@Nonnull
534+
public List<SnippetizerLink> getSnippetizerLinks() {
535+
return ExtensionList.lookup(SnippetizerLink.class);
536+
}
537+
528538
@Restricted(DoNotUse.class)
529539
@Extension public static class PerJobAdder extends TransientActionFactory<Job> {
530540

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
/*
2+
* The MIT License
3+
*
4+
* Copyright (c) 2018, CloudBees, Inc.
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in
14+
* all copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22+
* THE SOFTWARE.
23+
*/
24+
25+
package org.jenkinsci.plugins.workflow.cps;
26+
27+
import hudson.Extension;
28+
import hudson.ExtensionPoint;
29+
import hudson.model.Item;
30+
import hudson.model.Job;
31+
import org.kohsuke.stapler.Stapler;
32+
import org.kohsuke.stapler.StaplerRequest;
33+
34+
import javax.annotation.Nonnull;
35+
36+
import java.net.URI;
37+
import java.net.URISyntaxException;
38+
import java.util.logging.Level;
39+
import java.util.logging.Logger;
40+
41+
import static org.jenkinsci.plugins.workflow.cps.Snippetizer.ACTION_URL;
42+
43+
/**
44+
* A link that will show up on the side panel of the snippet generator and other similar pages.
45+
* Display order is determined by extension ordinal - highest ordinal first.
46+
*
47+
* @author Andrew Bayer
48+
*/
49+
public abstract class SnippetizerLink implements ExtensionPoint {
50+
private static final Logger LOGGER = Logger.getLogger(SnippetizerLink.class.getName());
51+
52+
/**
53+
* Get the URL this link should point to, which will be used by {@link #getDisplayUrl()}. If this is not absolute,
54+
* {@link #getDisplayUrl()} will link to this within the current context.
55+
*/
56+
@Nonnull
57+
public abstract String getUrl();
58+
59+
/**
60+
* Get the actual URL to use in sidepanel.jelly. If {@link #getUrl()} is not absolute, this will try to get the
61+
* current Job context and return a url starting with that job's {@link Job#getUrl()} appended with {@link #getUrl()}.
62+
*/
63+
@Nonnull
64+
public final String getDisplayUrl() {
65+
String u = getUrl();
66+
67+
try {
68+
if (new URI(u).isAbsolute()) {
69+
return u;
70+
}
71+
} catch (URISyntaxException e) {
72+
LOGGER.log(Level.WARNING, "Failed to parse URL for " + u, e);
73+
return "";
74+
}
75+
76+
StaplerRequest req = Stapler.getCurrentRequest();
77+
if (req == null) {
78+
return u;
79+
}
80+
81+
Item i = req.findAncestorObject(Item.class);
82+
83+
StringBuilder toAppend = new StringBuilder();
84+
85+
toAppend.append(req.getContextPath());
86+
87+
if (!req.getContextPath().endsWith("/")) {
88+
toAppend.append("/");
89+
}
90+
91+
if (i == null) {
92+
toAppend.append(u);
93+
} else {
94+
toAppend.append(i.getUrl());
95+
96+
if (!i.getUrl().endsWith("/")) {
97+
toAppend.append("/");
98+
}
99+
100+
toAppend.append(u);
101+
}
102+
103+
return toAppend.toString();
104+
}
105+
106+
/**
107+
* Get the icon information for the link.
108+
*/
109+
@Nonnull
110+
public String getIcon() {
111+
return "icon-help icon-md";
112+
}
113+
114+
/**
115+
* Get the display name for the link.
116+
*/
117+
@Nonnull
118+
public abstract String getDisplayName();
119+
120+
/**
121+
* Check whether the link should target a new window - this defaults to false;
122+
*/
123+
public boolean inNewWindow() {
124+
return false;
125+
}
126+
127+
@Extension(ordinal = 1000L)
128+
public static class GeneratorLink extends SnippetizerLink {
129+
@Override
130+
@Nonnull
131+
public String getUrl() {
132+
return ACTION_URL;
133+
}
134+
135+
@Override
136+
@Nonnull
137+
public String getIcon() {
138+
return "icon-gear2 icon-md";
139+
}
140+
141+
@Override
142+
@Nonnull
143+
public String getDisplayName() {
144+
return Messages.SnippetizerLink_GeneratorLink_displayName();
145+
}
146+
}
147+
148+
@Extension(ordinal = 900L)
149+
public static class StepReferenceLink extends SnippetizerLink {
150+
@Override
151+
@Nonnull
152+
public String getUrl() {
153+
return ACTION_URL + "/html";
154+
}
155+
156+
@Override
157+
@Nonnull
158+
public String getDisplayName() {
159+
return Messages.SnippetizerLink_StepReferenceLink_displayName();
160+
}
161+
}
162+
163+
@Extension(ordinal = 800L)
164+
public static class GlobalsReferenceLink extends SnippetizerLink {
165+
@Override
166+
@Nonnull
167+
public String getUrl() {
168+
return ACTION_URL + "/globals";
169+
}
170+
171+
@Override
172+
@Nonnull
173+
public String getDisplayName() {
174+
return Messages.SnippetizerLink_GlobalsReferenceLink_displayName();
175+
}
176+
}
177+
178+
@Extension(ordinal = 700L)
179+
public static class OnlineDocsLink extends SnippetizerLink {
180+
@Override
181+
@Nonnull
182+
public String getUrl() {
183+
return "https://jenkins.io/doc/pipeline/";
184+
}
185+
186+
@Override
187+
@Nonnull
188+
public String getDisplayName() {
189+
return Messages.SnippetizerLink_OnlineDocsLink_displayName();
190+
}
191+
192+
@Override
193+
public boolean inNewWindow() {
194+
return true;
195+
}
196+
}
197+
198+
@Extension(ordinal = 600L)
199+
public static class GDSLLink extends SnippetizerLink {
200+
@Override
201+
@Nonnull
202+
public String getUrl() {
203+
return ACTION_URL + "/gdsl";
204+
}
205+
206+
@Override
207+
@Nonnull
208+
public String getDisplayName() {
209+
return Messages.SnippetizerLink_GDSLLink_displayName();
210+
}
211+
212+
@Override
213+
public boolean inNewWindow() {
214+
return true;
215+
}
216+
}
217+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,7 @@
11
Snippetizer.this_step_should_not_normally_be_used_in=This step should not normally be used in your script. Consult the inline help for details.
2+
SnippetizerLink.GeneratorLink.displayName=Snippet Generator
3+
SnippetizerLink.StepReferenceLink.displayName=Steps Reference
4+
SnippetizerLink.GlobalsReferenceLink.displayName=Global Variables Reference
5+
SnippetizerLink.OnlineDocsLink.displayName=Online Documentation
6+
SnippetizerLink.GDSLLink.displayName=IntelliJ IDEA GDSL
27
SandboxContinuable.ScriptApprovalLink=Administrators can decide whether to approve or reject this signature.

src/main/resources/org/jenkinsci/plugins/workflow/cps/Snippetizer/sidepanel.jelly

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,16 @@ THE SOFTWARE.
3838
</d:tag>
3939
</d:taglib>
4040
<l:task href=".." icon="icon-up icon-md" title="${%Back}"/>
41-
<l:task href="." icon="icon-gear2 icon-md" title="${%Snippet Generator}"/>
42-
<l:task href="html" icon="icon-help icon-md" title="${%Step Reference}"/>
43-
<l:task href="globals" icon="icon-help icon-md" title="${%Global Variables Reference}"/>
44-
<local:taskWithTarget href="https://jenkins.io/doc/pipeline/" icon="icon-help icon-md" target="_blank" title="${%Online Documentation}"/>
45-
<local:taskWithTarget href="gdsl" icon="icon-package icon-md" target="_blank" title="${%IntelliJ IDEA GDSL}"/>
41+
<j:forEach var="link" items="${it.snippetizerLinks}">
42+
<j:choose>
43+
<j:when test="${link.inNewWindow}">
44+
<local:taskWithTarget href="${link.displayUrl}" icon="${link.icon}" title="${link.displayName}" target="_blank" />
45+
</j:when>
46+
<j:otherwise>
47+
<l:task href="${link.displayUrl}" icon="${link.icon}" title="${link.displayName}" />
48+
</j:otherwise>
49+
</j:choose>
50+
</j:forEach>
4651
<!-- TODO not yet ready: <local:taskWithTarget href="dsld" icon="icon-package icon-md" target="_blank" title="${%Eclipse DSLD}"/> -->
4752
</l:tasks>
4853
</l:side-panel>

src/test/java/org/jenkinsci/plugins/workflow/cps/SnippetizerTest.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import org.jenkinsci.Symbol;
3838
import org.jenkinsci.plugins.structs.describable.DescribableModel;
3939
import org.jenkinsci.plugins.workflow.cps.steps.ParallelStep;
40+
import org.jenkinsci.plugins.workflow.job.WorkflowJob;
4041
import org.jenkinsci.plugins.workflow.steps.CatchErrorStep;
4142
import org.jenkinsci.plugins.workflow.steps.CoreStep;
4243
import org.jenkinsci.plugins.workflow.steps.EchoStep;
@@ -359,4 +360,34 @@ public void parallelStepDocs() throws Exception {
359360
a.setHealthScaleFactor(0.5);
360361
st.assertRoundTrip(new CoreStep(a), "junit healthScaleFactor: 0.5, testResults: '*.xml'");
361362
}
363+
364+
@Test
365+
public void snippetizerLinks() throws Exception {
366+
WorkflowJob job = r.jenkins.createProject(WorkflowJob.class, "p");
367+
JenkinsRule.WebClient wc = r.createWebClient();
368+
String html = wc.getPage(job, Snippetizer.ACTION_URL).getWebResponse().getContentAsString();
369+
assertThat("Snippet Generator link is included", html,
370+
containsString("href=\"" + r.contextPath + "/" + job.getUrl() + Snippetizer.ACTION_URL + "\""));
371+
assertThat("Steps Reference link is included", html,
372+
containsString("href=\"" + r.contextPath + "/" + job.getUrl() + Snippetizer.ACTION_URL + "/html\""));
373+
assertThat("Globals Reference link is included", html,
374+
containsString("href=\"" + r.contextPath + "/" + job.getUrl() + Snippetizer.ACTION_URL + "/globals\""));
375+
assertThat("Online docs link is included", html,
376+
containsString("href=\"https://jenkins.io/doc/pipeline/\""));
377+
assertThat("GDSL link is included", html,
378+
containsString("href=\"" + r.contextPath + "/" + job.getUrl() + Snippetizer.ACTION_URL + "/gdsl\""));
379+
380+
// Now verify that the links are still present and correct when we're not within a job.
381+
String rootHtml = wc.goTo(Snippetizer.ACTION_URL).getWebResponse().getContentAsString();
382+
assertThat("Snippet Generator link is included", rootHtml,
383+
containsString("href=\"" + r.contextPath + "/" + Snippetizer.ACTION_URL + "\""));
384+
assertThat("Steps Reference link is included", rootHtml,
385+
containsString("href=\"" + r.contextPath + "/" + Snippetizer.ACTION_URL + "/html\""));
386+
assertThat("Globals Reference link is included", rootHtml,
387+
containsString("href=\"" + r.contextPath + "/" + Snippetizer.ACTION_URL + "/globals\""));
388+
assertThat("Online docs link is included", rootHtml,
389+
containsString("href=\"https://jenkins.io/doc/pipeline/\""));
390+
assertThat("GDSL link is included", rootHtml,
391+
containsString("href=\"" + r.contextPath + "/" + Snippetizer.ACTION_URL + "/gdsl\""));
392+
}
362393
}

0 commit comments

Comments
 (0)