Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
JENKINS-38584 - rest API for returning available steps, not whitelisted
- Loading branch information
Showing
6 changed files
with
333 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,3 +2,6 @@ node/ | |
node_modules/ | ||
target/ | ||
work/ | ||
/.classpath | ||
/.project | ||
/.settings/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
55 changes: 55 additions & 0 deletions
55
src/main/java/io/blueocean/rest/pipeline/editor/PipelineStepMetadata.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
package io.blueocean.rest.pipeline.editor; | ||
|
||
import org.kohsuke.stapler.export.Exported; | ||
import org.kohsuke.stapler.export.ExportedBean; | ||
|
||
@ExportedBean | ||
public interface PipelineStepMetadata { | ||
/** | ||
* Identifier used for the 'function' name in the pipeline step, used in the pipeline file | ||
*/ | ||
@Exported | ||
public String getFunctionName(); | ||
|
||
/** | ||
* The Java class name for this step (since we can't seem to export a Class<?>...) | ||
*/ | ||
@Exported | ||
public String getType(); | ||
|
||
/** | ||
* Display Name of the pipeline step, used in the pipeline file | ||
*/ | ||
@Exported | ||
public String getDisplayName(); | ||
|
||
/** | ||
* The Java class names that this pipeline step exports into context | ||
*/ | ||
@Exported | ||
public String[] getProvidedContext(); | ||
|
||
/** | ||
* The Java class names that this pipeline requires to be in context | ||
*/ | ||
@Exported | ||
public String[] getRequiredContext(); | ||
|
||
/** | ||
* Indicates this step wraps a block of other steps | ||
*/ | ||
@Exported | ||
public boolean getIsBlockContainer(); | ||
|
||
/** | ||
* Snippetizer URL for this step (these are the same with different POST parameters...) | ||
*/ | ||
@Exported | ||
public String getSnippetizerUrl(); | ||
|
||
/** | ||
* Properties the steps supports | ||
*/ | ||
@Exported | ||
public PipelineStepPropertyMetadata[] getProperties(); | ||
} |
214 changes: 214 additions & 0 deletions
214
src/main/java/io/blueocean/rest/pipeline/editor/PipelineStepMetadataService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,214 @@ | ||
package io.blueocean.rest.pipeline.editor; | ||
|
||
import java.io.IOException; | ||
import java.lang.reflect.Constructor; | ||
import java.lang.reflect.Method; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
import org.apache.commons.lang.StringUtils; | ||
import org.jenkinsci.plugins.workflow.cps.Snippetizer; | ||
import org.jenkinsci.plugins.workflow.steps.StepDescriptor; | ||
import org.kohsuke.stapler.DataBoundConstructor; | ||
import org.kohsuke.stapler.DataBoundSetter; | ||
import org.kohsuke.stapler.Stapler; | ||
import org.kohsuke.stapler.WebMethod; | ||
import org.kohsuke.stapler.export.Exported; | ||
import org.kohsuke.stapler.export.ExportedBean; | ||
import org.kohsuke.stapler.verb.GET; | ||
import org.springframework.core.LocalVariableTableParameterNameDiscoverer; | ||
import org.springframework.core.ParameterNameDiscoverer; | ||
|
||
import hudson.Extension; | ||
import hudson.ExtensionList; | ||
import io.jenkins.blueocean.commons.stapler.TreeResponse; | ||
import io.jenkins.blueocean.rest.ApiRoutable; | ||
import jenkins.model.Jenkins; | ||
|
||
/** | ||
* This provides and Blueocean REST API endpoint to obtain pipeline step metadata. | ||
*/ | ||
@Extension | ||
public class PipelineStepMetadataService implements ApiRoutable { | ||
ParameterNameDiscoverer nameFinder = new LocalVariableTableParameterNameDiscoverer(); | ||
|
||
@Override | ||
public String getUrlName() { | ||
return "pipeline-step-metadata"; | ||
} | ||
|
||
/** | ||
* Basic exported model for {@link PipelineStepMetadata} | ||
*/ | ||
@ExportedBean | ||
public static class BasicPipelineStepMetadata implements PipelineStepMetadata { | ||
private String displayName; | ||
private String functionName; | ||
private Class<?> type; | ||
private String descriptorUrl; | ||
private List<Class<?>> requiredContext = new ArrayList<Class<?>>(); | ||
private List<Class<?>> providedContext = new ArrayList<Class<?>>(); | ||
private boolean isWrapper = false; | ||
private String snippetizerUrl; | ||
private List<PipelineStepPropertyMetadata> props = new ArrayList<PipelineStepPropertyMetadata>(); | ||
|
||
public BasicPipelineStepMetadata(String functionName, Class<?> type, String displayName) { | ||
super(); | ||
this.displayName = displayName; | ||
this.type = type; | ||
this.functionName = functionName; | ||
} | ||
|
||
@Exported | ||
@Override | ||
public String getDisplayName() { | ||
return displayName; | ||
} | ||
|
||
@Exported | ||
@Override | ||
public String getFunctionName() { | ||
return functionName; | ||
} | ||
|
||
@Exported | ||
@Override | ||
public String[] getRequiredContext() { | ||
List<String> out = new ArrayList<String>(); | ||
for (Class<?> c : requiredContext) { | ||
out.add(c.getName()); | ||
} | ||
return out.toArray(new String[out.size()]); | ||
} | ||
|
||
@Exported | ||
@Override | ||
public String[] getProvidedContext() { | ||
List<String> out = new ArrayList<String>(); | ||
for (Class<?> c : providedContext) { | ||
out.add(c.getName()); | ||
} | ||
return out.toArray(new String[out.size()]); | ||
} | ||
|
||
@Exported | ||
@Override | ||
public String getSnippetizerUrl() { | ||
return snippetizerUrl; | ||
} | ||
|
||
@Exported | ||
public String descriptorUrl() { | ||
return descriptorUrl; | ||
} | ||
|
||
@Exported | ||
@Override | ||
public boolean getIsBlockContainer() { | ||
return isWrapper; | ||
} | ||
|
||
@Exported | ||
@Override | ||
public String getType() { | ||
return type.getName(); | ||
} | ||
|
||
@Exported | ||
@Override | ||
public PipelineStepPropertyMetadata[] getProperties() { | ||
return props.toArray(new PipelineStepPropertyMetadata[props.size()]); | ||
} | ||
} | ||
|
||
/** | ||
* Basic exported model for {@link PipelineStepPropertyDescriptor) | ||
*/ | ||
@ExportedBean | ||
public static class BasicPipelineStepPropertyMetadata implements PipelineStepPropertyMetadata{ | ||
private String name; | ||
private Class<?> type; | ||
private boolean isRequired = false; | ||
|
||
@Exported | ||
@Override | ||
public String getName() { | ||
return name; | ||
} | ||
|
||
@Exported | ||
@Override | ||
public String getType() { | ||
return type.getName(); | ||
} | ||
|
||
@Exported | ||
@Override | ||
public boolean getIsRequired() { | ||
return isRequired; | ||
} | ||
} | ||
|
||
/** | ||
* Function to return all step descriptors present in the system when accessed through the REST API | ||
*/ | ||
@GET | ||
@WebMethod(name = "") | ||
@TreeResponse | ||
public PipelineStepMetadata[] getPipelineStepMetadata() throws IOException { | ||
Jenkins j = Jenkins.getInstance(); | ||
Snippetizer snippetizer = ExtensionList.create(j, Snippetizer.class).get(0); | ||
|
||
List<PipelineStepMetadata> pd = new ArrayList<PipelineStepMetadata>(); | ||
// POST to this with parameter names | ||
// e.g. json:{"time": "1", "unit": "NANOSECONDS", "stapler-class": "org.jenkinsci.plugins.workflow.steps.TimeoutStep", "$class": "org.jenkinsci.plugins.workflow.steps.TimeoutStep"} | ||
String snippetizerUrl = Stapler.getCurrentRequest().getContextPath() + "/" + snippetizer.getUrlName() + "/generateSnippet"; | ||
|
||
for (StepDescriptor d : StepDescriptor.all()) { | ||
PipelineStepMetadata step = descriptorMetadata(d, snippetizerUrl); | ||
pd.add(step); | ||
} | ||
|
||
return pd.toArray(new PipelineStepMetadata[pd.size()]); | ||
} | ||
|
||
private PipelineStepMetadata descriptorMetadata(StepDescriptor d, String snippetizerUrl) { | ||
BasicPipelineStepMetadata meta = new BasicPipelineStepMetadata(d.getFunctionName(), d.clazz, d.getDisplayName()); | ||
meta.snippetizerUrl = snippetizerUrl + "?$class=" + d.clazz.getName(); | ||
|
||
meta.isWrapper = d.takesImplicitBlockArgument(); | ||
meta.requiredContext.addAll(d.getRequiredContext()); | ||
meta.providedContext.addAll(d.getProvidedContext()); | ||
meta.descriptorUrl = d.getDescriptorUrl(); | ||
|
||
for (Method m : d.clazz.getDeclaredMethods()) { | ||
if (m.isAnnotationPresent(DataBoundSetter.class)) { | ||
String paramName = StringUtils.uncapitalize(m.getName().substring(3)); | ||
Class<?> paramType = m.getParameterTypes()[0]; | ||
BasicPipelineStepPropertyMetadata param = new BasicPipelineStepPropertyMetadata(); | ||
param.name = paramName; | ||
param.type = paramType; | ||
meta.props.add(param); | ||
} | ||
} | ||
|
||
for (Constructor<?> c : d.clazz.getDeclaredConstructors()) { | ||
if (c.isAnnotationPresent(DataBoundConstructor.class)) { | ||
Class<?>[] paramTypes = c.getParameterTypes(); | ||
String[] paramNames = nameFinder.getParameterNames(c); | ||
if(paramNames != null) { | ||
for (int i = 0; i < paramNames.length; i++) { | ||
String paramName = paramNames[i]; | ||
Class<?> paramType = paramTypes[i]; | ||
BasicPipelineStepPropertyMetadata param = new BasicPipelineStepPropertyMetadata(); | ||
param.name = paramName; | ||
param.type = paramType; | ||
meta.props.add(param); | ||
} | ||
} | ||
} | ||
} | ||
|
||
return meta; | ||
} | ||
} |
15 changes: 15 additions & 0 deletions
15
src/main/java/io/blueocean/rest/pipeline/editor/PipelineStepPropertyDecorator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package io.blueocean.rest.pipeline.editor; | ||
|
||
import java.util.List; | ||
|
||
import hudson.ExtensionPoint; | ||
|
||
/** | ||
* Allows plugins to modify property metadata, e.g. providing additional form fields and such | ||
*/ | ||
public interface PipelineStepPropertyDecorator extends ExtensionPoint { | ||
/** | ||
* Adjust the PipelineStepPropertyMetadata for the pipeline step | ||
*/ | ||
public PipelineStepPropertyMetadata decorate(PipelineStepMetadata step, List<PipelineStepPropertyMetadata> property); | ||
} |
29 changes: 29 additions & 0 deletions
29
src/main/java/io/blueocean/rest/pipeline/editor/PipelineStepPropertyMetadata.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
package io.blueocean.rest.pipeline.editor; | ||
|
||
import org.kohsuke.stapler.export.Exported; | ||
import org.kohsuke.stapler.export.ExportedBean; | ||
|
||
|
||
/** | ||
* Basic pipeline step property descriptor | ||
*/ | ||
@ExportedBean | ||
public interface PipelineStepPropertyMetadata { | ||
/** | ||
* Name of the property | ||
*/ | ||
@Exported | ||
public String getName(); | ||
|
||
/** | ||
* Indicates this property is required | ||
*/ | ||
@Exported | ||
public boolean getIsRequired(); | ||
|
||
/** | ||
* Java class name for the property | ||
*/ | ||
@Exported | ||
public String getType(); | ||
} |