-
Notifications
You must be signed in to change notification settings - Fork 78
[JENKINS-31582] ArgumentsAction.getResolvedArguments #44
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -25,20 +25,25 @@ | |
package org.jenkinsci.plugins.workflow.actions; | ||
|
||
import com.google.common.primitives.Primitives; | ||
import org.apache.commons.collections.CollectionUtils; | ||
import org.jenkinsci.plugins.workflow.graph.FlowNode; | ||
import org.jenkinsci.plugins.workflow.graph.StepNode; | ||
import org.jenkinsci.plugins.workflow.steps.Step; | ||
import org.jenkinsci.plugins.workflow.steps.StepDescriptor; | ||
|
||
import javax.annotation.CheckForNull; | ||
import javax.annotation.Nonnull; | ||
import hudson.PluginManager; | ||
import hudson.model.Describable; | ||
import java.util.Collection; | ||
import java.util.Collections; | ||
import java.util.HashMap; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Set; | ||
import java.util.logging.Level; | ||
import java.util.logging.Logger; | ||
import javax.annotation.CheckForNull; | ||
import javax.annotation.Nonnull; | ||
import org.apache.commons.collections.CollectionUtils; | ||
import org.jenkinsci.plugins.structs.describable.DescribableModel; | ||
import org.jenkinsci.plugins.structs.describable.UninstantiatedDescribable; | ||
import org.jenkinsci.plugins.workflow.graph.FlowNode; | ||
import org.jenkinsci.plugins.workflow.graph.StepNode; | ||
import org.jenkinsci.plugins.workflow.steps.Step; | ||
import org.jenkinsci.plugins.workflow.steps.StepDescriptor; | ||
|
||
/** | ||
* Stores some or all of the arguments used to create and configure the {@link Step} executed by a {@link FlowNode}. | ||
|
@@ -49,6 +54,8 @@ | |
*/ | ||
public abstract class ArgumentsAction implements PersistentAction { | ||
|
||
private static final Logger LOGGER = Logger.getLogger(ArgumentsAction.class.getName()); | ||
|
||
/** Used as a placeholder marker for {@link Step} arguments not stored for various reasons. */ | ||
public enum NotStoredReason { | ||
/** Denotes an unsafe value that cannot be stored/displayed due to sensitive info. */ | ||
|
@@ -128,7 +135,7 @@ public Map<String,Object> getArguments() { | |
if (args.isEmpty()) { | ||
return Collections.<String,Object>emptyMap(); | ||
} else { | ||
return Collections.unmodifiableMap(getArgumentsInternal()); | ||
return Collections.unmodifiableMap(args); | ||
} | ||
} | ||
|
||
|
@@ -158,6 +165,7 @@ public Map<String, Object> getFilteredArguments() { | |
HashMap<String, Object> filteredArguments = new HashMap<String, Object>(); | ||
for (Map.Entry<String, Object> entry : internalArgs.entrySet()) { | ||
if (entry.getValue() != null && !(entry.getValue() instanceof NotStoredReason)) { | ||
// TODO this is incorrect: value could be a Map/List with some nested entries that are NotStoredReason | ||
filteredArguments.put(entry.getKey(), entry.getValue()); | ||
} | ||
} | ||
|
@@ -241,6 +249,7 @@ static boolean checkArgumentsLackPlaceholders(@Nonnull Map<String,Object> namedA | |
if (ob instanceof NotStoredReason) { | ||
return false; | ||
} | ||
// TODO this would need to also check nested Map/List arguments | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Originally tried to use archiveArtifacts '1.txt,2.txt,…,9999.txt' will currently claim to be fully-formed even though the argument here is not stored. |
||
} | ||
return true; | ||
} | ||
|
@@ -253,4 +262,47 @@ public boolean isUnmodifiedArguments() { | |
// Cacheable, but arguments lists will be quite short and this is unlikely to get invoked heavily. | ||
return checkArgumentsLackPlaceholders(this.getArgumentsInternal()); | ||
} | ||
|
||
/** | ||
* Like {@link #getArguments(FlowNode)} but attempting to resolve actual classes. | ||
* If you need to reconstruct actual classes of nested objects (for example to pass to {@link PluginManager#whichPlugin}), | ||
* it is not trivial to get this information from the form in which they were supplied to {@link DescribableModel#instantiate}. | ||
* For example, nested objects (where present and not a {@link NotStoredReason}) might have been | ||
* <ul> | ||
* <li>an {@link UninstantiatedDescribable}, if given by a symbol | ||
* <li>a {@link Map}, if given by {@link DescribableModel#CLAZZ} | ||
* <li>a {@link Describable}, if constructed directly (rare) | ||
* </ul> | ||
* This method will instead attempt to return a normalized tree using {@link UninstantiatedDescribable} in all those cases. | ||
* You could use {@link UninstantiatedDescribable#getModel} (where available) and {@link DescribableModel#getType} to access live classes. | ||
* Where information is missing, this will just return the best it can. | ||
*/ | ||
@Nonnull | ||
public static Map<String, ?> getResolvedArguments(@Nonnull FlowNode n) { | ||
ArgumentsAction aa = n.getPersistentAction(ArgumentsAction.class); | ||
if (aa == null) { | ||
return Collections.emptyMap(); | ||
} | ||
Map<String, Object> args = aa.getArgumentsInternal(); | ||
if (n instanceof StepNode) { | ||
StepDescriptor d = ((StepNode) n).getDescriptor(); | ||
if (d != null) { | ||
try { | ||
return resolve(new DescribableModel<>(d.clazz), args).getArguments(); | ||
} catch (Exception x) { // all sorts of things could go wrong here | ||
LOGGER.log(Level.FINE, "coult not resolve " + args + " for " + d.clazz.getName(), x); | ||
// TODO this does not handle NotStoredReason’s well | ||
// more robust to recursively traverse the tree and use e.g. DescribableModel.resolveClass to populate details | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Made a brief attempt to do this but seemed too complicated to take on now, and would require some nontrivial API changes to |
||
// (without actually attempting to instantiate any of the classes) | ||
} | ||
} | ||
} | ||
return args; | ||
} | ||
// helper method to capture generic type | ||
private static <S extends Step> UninstantiatedDescribable resolve(DescribableModel<S> model, Map<String, Object> arguments) throws Exception { | ||
return model.uninstantiate2(model.instantiate(arguments)); | ||
} | ||
|
||
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unrelated micro-optimization