Skip to content
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

feat: display value selector #85

Merged
merged 9 commits into from
Oct 21, 2021
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import com.cloudbees.plugins.credentials.common.StandardCredentials;
import hudson.Extension;
import hudson.model.Item;
import io.jenkins.plugins.restlistparam.model.Item;
import hudson.model.ParameterDefinition;
import hudson.model.ParameterValue;
import hudson.model.SimpleParameterDefinition;
Expand Down Expand Up @@ -36,23 +36,25 @@ public final class RestListParameterDefinition extends SimpleParameterDefinition
private final String credentialId;
private final MimeType mimeType;
private final String valueExpression;
private String displayExpression;
private ValueOrder valueOrder;
private String defaultValue;
private String filter;
private Integer cacheTime;
private String errorMsg;
private List<String> values;
private List<Item> values;

@DataBoundConstructor
public RestListParameterDefinition(final String name,
final String description,
final String restEndpoint,
final String credentialId,
final MimeType mimeType,
final String valueExpression)
final String valueExpression,
final String displayExpression)
{
this(name, description, restEndpoint, credentialId, mimeType, valueExpression,
ValueOrder.NONE, ".*", config.getCacheTime(), "");
displayExpression, ValueOrder.NONE, ".*", config.getCacheTime(), "");
}

public RestListParameterDefinition(final String name,
Expand All @@ -61,6 +63,7 @@ public RestListParameterDefinition(final String name,
final String credentialId,
final MimeType mimeType,
final String valueExpression,
final String displayExpression,
final ValueOrder valueOrder,
final String filter,
final Integer cacheTime,
Expand All @@ -71,6 +74,7 @@ public RestListParameterDefinition(final String name,
this.mimeType = mimeType;
this.valueExpression = valueExpression;
this.credentialId = StringUtils.isNotBlank(credentialId) ? credentialId : "";
this.displayExpression = displayExpression;
this.defaultValue = StringUtils.isNotBlank(defaultValue) ? defaultValue : "";
this.valueOrder = valueOrder != null ? valueOrder : ValueOrder.NONE;
this.filter = StringUtils.isNotBlank(filter) ? filter : ".*";
Expand Down Expand Up @@ -99,6 +103,15 @@ public String getFilter() {
return filter;
}

public String getDisplayExpression() {
return displayExpression;
}

@DataBoundSetter
public void setDisplayExpression(String displayExpression) {
this.displayExpression = displayExpression;
}

@DataBoundSetter
public void setValueOrder(final ValueOrder valueOrder) {
this.valueOrder = valueOrder;
Expand Down Expand Up @@ -139,15 +152,16 @@ public String getErrorMsg() {
return errorMsg;
}

public List<String> getValues() {
public List<Item> getValues() {
Optional<StandardCredentials> credentials = CredentialsUtils.findCredentials(credentialId);

ResultContainer<List<String>> container = RestValueService.get(
ResultContainer<List<Item>> container = RestValueService.get(
getRestEndpoint(),
credentials.orElse(null),
getMimeType(),
getCacheTime(),
getValueExpression(),
getDisplayExpression(),
getFilter(),
getValueOrder());

Expand All @@ -162,7 +176,7 @@ public ParameterDefinition copyWithDefaultValue(final ParameterValue defaultValu
RestListParameterValue value = (RestListParameterValue) defaultValue;
return new RestListParameterDefinition(
getName(), getDescription(), getRestEndpoint(), getCredentialId(), getMimeType(),
getValueExpression(), getValueOrder(), getFilter(), getCacheTime(), value.getValue());
getValueExpression(), getDisplayExpression(), getValueOrder(), getFilter(), getCacheTime(), value.getValue());
}
else {
return this;
Expand All @@ -171,7 +185,10 @@ public ParameterDefinition copyWithDefaultValue(final ParameterValue defaultValu

@Override
public ParameterValue createValue(final String value) {
RestListParameterValue parameterValue = new RestListParameterValue(getName(), value, getDescription());
RestListParameterValue parameterValue = values.stream().filter(item -> item.getDisplayValue().equals(value)).findFirst()
.map(item -> new RestListParameterValue(getName(), item.getValue(), item.getDisplayValue(), getDescription()))
.orElse(null);

checkValue(parameterValue);
return parameterValue;
}
Expand All @@ -194,7 +211,14 @@ private void checkValue(final RestListParameterValue value) {

@Override
public boolean isValid(ParameterValue value) {
return values.contains(((RestListParameterValue) value).getValue());
if(value == null || value.getValue() == null) {
return false;
}

return values.stream()
.map(Item::getValue)
.filter(Objects::nonNull)
.anyMatch((val) -> value.getValue().equals(val));
}

@Override
Expand Down Expand Up @@ -251,15 +275,15 @@ public Integer getDefaultCacheTime() {
}

@POST
public FormValidation doCheckRestEndpoint(@AncestorInPath final Item context,
public FormValidation doCheckRestEndpoint(@AncestorInPath final hudson.model.Item context,
@QueryParameter final String value,
@QueryParameter final String credentialId)
{
if (context == null) {
Jenkins.get().checkPermission(Jenkins.ADMINISTER);
}
else {
context.checkPermission(Item.CONFIGURE);
context.checkPermission(hudson.model.Item.CONFIGURE);
}

if (StringUtils.isNotBlank(value)) {
Expand All @@ -273,15 +297,15 @@ public FormValidation doCheckRestEndpoint(@AncestorInPath final Item context,
}

@POST
public FormValidation doCheckValueExpression(@AncestorInPath final Item context,
public FormValidation doCheckValueExpression(@AncestorInPath final hudson.model.Item context,
@QueryParameter final String value,
@QueryParameter final MimeType mimeType)
{
if (context == null) {
Jenkins.get().checkPermission(Jenkins.ADMINISTER);
}
else {
context.checkPermission(Item.CONFIGURE);
context.checkPermission(hudson.model.Item.CONFIGURE);
}

if (StringUtils.isNotBlank(value)) {
Expand All @@ -297,27 +321,27 @@ public FormValidation doCheckValueExpression(@AncestorInPath final Item context,
return FormValidation.error(Messages.RLP_DescriptorImpl_ValidationErr_ExpressionEmpty());
}

public ListBoxModel doFillCredentialIdItems(@AncestorInPath final Item context,
public ListBoxModel doFillCredentialIdItems(@AncestorInPath final hudson.model.Item context,
@QueryParameter final String credentialId)
{
return CredentialsUtils.doFillCredentialsIdItems(context, credentialId);
}

public FormValidation doCheckCredentialId(@AncestorInPath final Item context,
public FormValidation doCheckCredentialId(@AncestorInPath final hudson.model.Item context,
@QueryParameter final String value)
{
return CredentialsUtils.doCheckCredentialsId(context, value);
}

@POST
public FormValidation doCheckCacheTime(@AncestorInPath final Item context,
public FormValidation doCheckCacheTime(@AncestorInPath final hudson.model.Item context,
@QueryParameter final Integer cacheTime)
{
if (context == null) {
Jenkins.get().checkPermission(Jenkins.ADMINISTER);
}
else {
context.checkPermission(Item.CONFIGURE);
context.checkPermission(hudson.model.Item.CONFIGURE);
}

if (cacheTime != null && cacheTime >= 0) {
Expand All @@ -328,19 +352,20 @@ public FormValidation doCheckCacheTime(@AncestorInPath final Item context,
}

@POST
public FormValidation doTestConfiguration(@AncestorInPath final Item context,
public FormValidation doTestConfiguration(@AncestorInPath final hudson.model.Item context,
@QueryParameter final String restEndpoint,
@QueryParameter final String credentialId,
@QueryParameter final MimeType mimeType,
@QueryParameter final String valueExpression,
@QueryParameter final String displayExpression,
@QueryParameter final String filter,
@QueryParameter final ValueOrder valueOrder)
{
if (context == null) {
Jenkins.get().checkPermission(Jenkins.ADMINISTER);
}
else {
context.checkPermission(Item.CONFIGURE);
context.checkPermission(hudson.model.Item.CONFIGURE);
}

Optional<StandardCredentials> credentials = CredentialsUtils.findCredentials(credentialId);
Expand All @@ -357,24 +382,24 @@ public FormValidation doTestConfiguration(@AncestorInPath final Item context,
return FormValidation.error(Messages.RLP_DescriptorImpl_ValidationErr_ExpressionEmpty());
}

ResultContainer<List<String>> container = RestValueService.get(
ResultContainer<List<Item>> container = RestValueService.get(
restEndpoint,
credentials.orElse(null),
mimeType,
0,
valueExpression,
filter,
valueOrder);
displayExpression,
filter, valueOrder);

Optional<String> errorMsg = container.getErrorMsg();
List<String> values = container.getValue();
List<Item> values = container.getValue();
if (errorMsg.isPresent()) {
return FormValidation.error(errorMsg.get());
}

// values should NEVER be empty here
// due to all the filtering and error handling done in the RestValueService
return FormValidation.ok(Messages.RLP_DescriptorImpl_ValidationOk_ConfigValid(values.size(), values.get(0)));
return FormValidation.ok(Messages.RLP_DescriptorImpl_ValidationOk_ConfigValid(values.size(), values.get(0).getDisplayValue()));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,26 @@ public final class RestListParameterValue extends ParameterValue {
@Restricted(NoExternalUse.class)
public String value;

@Exported(visibility = 4)
@Restricted(NoExternalUse.class)
public String displayValue;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you need to persist this value in this Class? you are not using the display value within the pipeline and for Replay (with the replay plugin) you don't need that either since that will re-query the API anyway?


@DataBoundConstructor
public RestListParameterValue(String name,
String value)
String value,
String displayValue)
{
this(name, value, null);
this(name, value, displayValue, null);
}

public RestListParameterValue(String name,
String value,
String displayValue,
String description)
{
super(name, description);
this.value = value;
this.displayValue = displayValue;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials;
import hudson.util.FormValidation;
import io.jenkins.plugins.restlistparam.Messages;
import io.jenkins.plugins.restlistparam.model.Item;
import io.jenkins.plugins.restlistparam.model.MimeType;
import io.jenkins.plugins.restlistparam.model.ResultContainer;
import io.jenkins.plugins.restlistparam.model.ValueOrder;
Expand Down Expand Up @@ -39,29 +40,31 @@ private RestValueService() {
* This method uses its parameters to query a REST/Web endpoint to receive a {@link MimeType} response, which then
* gets parsed with a supported Path expression to extract a list of string values.
*
* @param restEndpoint A http/https web address to the REST/Web endpoint
* @param credentials The credentials required to access said endpoint
* @param mimeType The MIME type of the expected REST/Web response
* @param cacheTime Time for how long the REST response gets cached for in minutes
* @param expression The Json-Path or xPath expression to filter the values
* @param filter additional regex filter on any parsed values
* @param order Set a {@link ValueOrder} to optionally reorder the values
* @param restEndpoint A http/https web address to the REST/Web endpoint
* @param credentials The credentials required to access said endpoint
* @param mimeType The MIME type of the expected REST/Web response
* @param cacheTime Time for how long the REST response gets cached for in minutes
* @param valueExpression The Json-Path or xPath expression to filter the values
* @param displayExpression The Json-Path or xPath expression to filter the display values
* @param filter additional regex filter on any parsed values
* @param order Set a {@link ValueOrder} to optionally reorder the values
* @return A {@link ResultContainer} that capsules either the desired values or a user friendly error message.
*/
public static ResultContainer<List<String>> get(final String restEndpoint,
final StandardCredentials credentials,
final MimeType mimeType,
final Integer cacheTime,
final String expression,
final String filter,
final ValueOrder order)
public static ResultContainer<List<Item>> get(final String restEndpoint,
final StandardCredentials credentials,
final MimeType mimeType,
final Integer cacheTime,
final String valueExpression,
final String displayExpression,
final String filter,
final ValueOrder order)
{
ResultContainer<List<String>> valueList = new ResultContainer<>(Collections.emptyList());
ResultContainer<List<Item>> valueList = new ResultContainer<>(Collections.emptyList());
ResultContainer<String> rawValues = getValueStringFromRestEndpoint(restEndpoint, credentials, mimeType, cacheTime);
Optional<String> rawValueError = rawValues.getErrorMsg();

if (!rawValueError.isPresent()) {
valueList = convertToValuesList(mimeType, rawValues.getValue(), expression);
valueList = convertToValuesList(mimeType, rawValues.getValue(), valueExpression, displayExpression);
}
else {
valueList.setErrorMsg(rawValueError.get());
Expand Down Expand Up @@ -224,21 +227,23 @@ else if (credentials instanceof StringCredentials) {
*
* @param mimeType The {@link MimeType} of the {@code valueString}
* @param valueString The value string to be parsed
* @param expression The Json-Path or xPath expression to apply on the {@code valueString}
* @param valueExpression The Json-Path or xPath expression to apply on the {@code valueString}
* @param displayExpression Derives the value to be displayed to the user parsed by value expression
* @return A {@link ResultContainer} capsuling the results of the applied expression or an error message
*/
private static ResultContainer<List<String>> convertToValuesList(final MimeType mimeType,
private static ResultContainer<List<Item>> convertToValuesList(final MimeType mimeType,
final String valueString,
final String expression)
final String valueExpression,
final String displayExpression)
{
ResultContainer<List<String>> container;
ResultContainer<List<Item>> container;

switch (mimeType) {
case APPLICATION_JSON:
container = ValueResolver.resolveJsonPath(valueString, expression);
container = ValueResolver.resolveJsonPath(valueString, valueExpression, displayExpression);
break;
case APPLICATION_XML:
container = ValueResolver.resolveXPath(valueString, expression);
container = ValueResolver.resolveXPath(valueString, valueExpression, displayExpression);
break;
default:
throw new IllegalStateException("Unexpected value: " + mimeType);
Expand All @@ -255,18 +260,18 @@ private static ResultContainer<List<String>> convertToValuesList(final MimeType
* @param order The Order to apply (if any)
* @return A {@link ResultContainer} capsuling a filtered string list or a user friendly error message
*/
private static ResultContainer<List<String>> filterAndSortValues(final List<String> values,
private static ResultContainer<List<Item>> filterAndSortValues(final List<Item> values,
final String filter,
final ValueOrder order)
{
ResultContainer<List<String>> container = new ResultContainer<>(Collections.emptyList());
ResultContainer<List<Item>> container = new ResultContainer<>(Collections.emptyList());

try {
List<String> updatedValues;
List<Item> updatedValues;

if (isFilterSet(filter) && !isOrderSet(order)) {
updatedValues = values.stream()
.filter(value -> value.matches(filter))
.filter(value -> value.getValue().matches(filter))
.collect(Collectors.toList());
}
else if (!isFilterSet(filter) && isOrderSet(order)) {
Expand All @@ -276,7 +281,7 @@ else if (!isFilterSet(filter) && isOrderSet(order)) {
}
else {
updatedValues = values.stream()
.filter(value -> value.matches(filter))
.filter(value -> value.getValue().matches(filter))
.sorted(order == ValueOrder.ASC ? Comparator.naturalOrder() : Comparator.reverseOrder())
.collect(Collectors.toList());
}
Expand Down
Loading