Skip to content

Commit

Permalink
Merge pull request #2683 from szpak/feature/JENKINS-40718-searchByBui…
Browse files Browse the repository at this point in the history
…ldParams

[JENKINS-40718] Search by build params in build history widget
  • Loading branch information
daniel-beck committed Mar 8, 2017
2 parents a43506c + 5a6103c commit 62adfa8
Show file tree
Hide file tree
Showing 3 changed files with 310 additions and 10 deletions.
52 changes: 44 additions & 8 deletions core/src/main/java/jenkins/widgets/HistoryPageFilter.java
Expand Up @@ -25,9 +25,13 @@

import com.google.common.collect.Iterables;
import com.google.common.collect.Iterators;
import hudson.model.AbstractBuild;
import hudson.model.Job;
import hudson.model.ParameterValue;
import hudson.model.ParametersAction;
import hudson.model.Queue;
import hudson.model.Run;
import hudson.search.UserSearchProperty;
import hudson.widgets.HistoryWidget;

import javax.annotation.Nonnull;
Expand All @@ -37,6 +41,7 @@
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
* History page filter.
Expand All @@ -52,8 +57,8 @@ public class HistoryPageFilter<T> {

// Need to use different Lists for Queue.Items and Runs because
// we need access to them separately in the jelly files for rendering.
public final List<HistoryPageEntry<Queue.Item>> queueItems = new ArrayList<HistoryPageEntry<Queue.Item>>();
public final List<HistoryPageEntry<Run>> runs = new ArrayList<HistoryPageEntry<Run>>();
public final List<HistoryPageEntry<Queue.Item>> queueItems = new ArrayList<>();
public final List<HistoryPageEntry<Run>> runs = new ArrayList<>();

public boolean hasUpPage = false; // there are newer builds than on this page
public boolean hasDownPage = false; // there are older builds than on this page
Expand Down Expand Up @@ -343,6 +348,13 @@ private boolean fitsSearchParams(@Nonnull Run run) {
return true;
} else if (fitsSearchString(run.getResult())) {
return true;
} else if (run instanceof AbstractBuild && fitsSearchBuildVariables((AbstractBuild) run)) {
return true;
} else {
ParametersAction parametersAction = run.getAction(ParametersAction.class);
if (parametersAction != null && fitsSearchBuildParameters(parametersAction)) {
return true;
}
}

// Non of the fuzzy matches "liked" the search term.
Expand All @@ -354,14 +366,38 @@ private boolean fitsSearchString(Object data) {
return true;
}

if (data != null) {
if (data instanceof Number) {
return data.toString().equals(searchString);
if (data == null) {
return false;
}

if (data instanceof Number) {
return data.toString().equals(searchString);
} else {
if (UserSearchProperty.isCaseInsensitive()) {
return data.toString().toLowerCase().contains(searchString.toLowerCase());
} else {
return data.toString().toLowerCase().contains(searchString);
return data.toString().contains(searchString);
}
}
}

private boolean fitsSearchBuildVariables(AbstractBuild<?, ?> runAsBuild) {
Map<String, String> buildVariables = runAsBuild.getBuildVariables();
for (String paramsValues : buildVariables.values()) {
if (fitsSearchString(paramsValues)) {
return true;
}
}

return false;
}
}

private boolean fitsSearchBuildParameters(ParametersAction parametersAction) {
List<ParameterValue> parameters = parametersAction.getParameters();
for (ParameterValue parameter : parameters) {
if (!parameter.isSensitive() && fitsSearchString(parameter.getValue())) {
return true;
}
}
return false;
}
}
154 changes: 152 additions & 2 deletions core/src/test/java/jenkins/widgets/HistoryPageFilterTest.java
Expand Up @@ -23,24 +23,39 @@
*/
package jenkins.widgets;

import hudson.model.Build;
import hudson.model.FreeStyleBuild;
import hudson.model.FreeStyleProject;
import hudson.model.Job;
import hudson.model.MockItem;
import hudson.model.ModelObject;
import hudson.model.ParameterValue;
import hudson.model.ParametersAction;
import hudson.model.Queue;
import hudson.model.Result;
import hudson.model.Run;
import jenkins.widgets.HistoryPageEntry;
import jenkins.widgets.HistoryPageFilter;
import hudson.model.StringParameterValue;

import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
import org.mockito.Mockito;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;

/**
* @author <a href="mailto:tom.fennelly@gmail.com">tom.fennelly@gmail.com</a>
*
* See also HistoryPageFilterInsensitiveSearchTest integration test.
*/
public class HistoryPageFilterTest {

Expand Down Expand Up @@ -293,6 +308,80 @@ public void test_laterItemsNotEvaluated() throws IOException {
Assert.assertEquals(HistoryPageEntry.getEntryId(6), historyPageFilter.oldestOnPage);
}

@Test
public void test_search_runs_by_build_number() throws IOException {
//given
HistoryPageFilter<ModelObject> historyPageFilter = newPage(5, null, null);
List<ModelObject> runs = newRuns(23, 24);
List<Queue.Item> queueItems = newQueueItems(25, 26);
//and
historyPageFilter.setSearchString("23");

//when
historyPageFilter.add(runs, queueItems);

//then
Assert.assertEquals(1, historyPageFilter.runs.size());
Assert.assertEquals(HistoryPageEntry.getEntryId(23), historyPageFilter.runs.get(0).getEntryId());
}

@Test
public void test_search_should_be_case_sensitive_for_anonymous_user() throws IOException {
//given
HistoryPageFilter<ModelObject> historyPageFilter = newPage(5, null, null);
//and
historyPageFilter.setSearchString("failure");
//and
List<ModelObject> runs = Lists.<ModelObject>newArrayList(new MockRun(2, Result.FAILURE), new MockRun(1, Result.SUCCESS));
List<Queue.Item> queueItems = newQueueItems(3, 4);

//when
historyPageFilter.add(runs, queueItems);

//then
Assert.assertEquals(0, historyPageFilter.runs.size());
}

@Test
public void test_search_builds_by_build_variables() throws IOException {
List<ModelObject> runs = ImmutableList.<ModelObject>of(
new MockBuild(2).withBuildVariables(ImmutableMap.of("env", "dummyEnv")),
new MockBuild(1).withBuildVariables(ImmutableMap.of("env", "otherEnv")));
assertOneMatchingBuildForGivenSearchStringAndRunItems("dummyEnv", runs);
}

@Test
public void test_search_builds_by_build_params() throws IOException {
List<ModelObject> runs = ImmutableList.<ModelObject>of(
new MockBuild(2).withBuildParameters(ImmutableMap.of("env", "dummyEnv")),
new MockBuild(1).withBuildParameters(ImmutableMap.of("env", "otherEnv")));
assertOneMatchingBuildForGivenSearchStringAndRunItems("dummyEnv", runs);
}

@Test
public void test_ignore_sensitive_parameters_in_search_builds_by_build_params() throws IOException {
List<ModelObject> runs = ImmutableList.<ModelObject>of(
new MockBuild(2).withBuildParameters(ImmutableMap.of("plainPassword", "pass1plain")),
new MockBuild(1).withSensitiveBuildParameters("password", "pass1"));
assertOneMatchingBuildForGivenSearchStringAndRunItems("pass1", runs);
}

private void assertOneMatchingBuildForGivenSearchStringAndRunItems(String searchString, List<ModelObject> runs) {
//given
HistoryPageFilter<ModelObject> historyPageFilter = newPage(5, null, null);
//and
historyPageFilter.setSearchString(searchString);
//and
List<Queue.Item> queueItems = newQueueItems(3, 4);

//when
historyPageFilter.add(runs, queueItems);

//then
Assert.assertEquals(1, historyPageFilter.runs.size());
Assert.assertEquals(HistoryPageEntry.getEntryId(2), historyPageFilter.runs.get(0).getEntryId());
}

private List<Queue.Item> newQueueItems(long startId, long endId) {
List<Queue.Item> items = new ArrayList<>();
for (long queueId = startId; queueId <= endId; queueId++) {
Expand Down Expand Up @@ -329,6 +418,11 @@ public MockRun(long queueId) throws IOException {
this.queueId = queueId;
}

public MockRun(long queueId, Result result) throws IOException {
this(queueId);
this.result = result;
}

@Override
public int compareTo(Run o) {
return 0;
Expand Down Expand Up @@ -373,4 +467,60 @@ public int getNumber() {
return super.getNumber();
}
}

private static class MockBuild extends Build<FreeStyleProject, FreeStyleBuild> {

private final int buildNumber;

private Map<String, String> buildVariables = Collections.emptyMap();

private MockBuild(int buildNumber) {
super(Mockito.mock(FreeStyleProject.class), Mockito.mock(Calendar.class));
this.buildNumber = buildNumber;
}

@Override
public int getNumber() {
return buildNumber;
}

@Override
public Map<String, String> getBuildVariables() {
return buildVariables;
}

MockBuild withBuildVariables(Map<String, String> buildVariables) {
this.buildVariables = buildVariables;
return this;
}

MockBuild withBuildParameters(Map<String, String> buildParametersAsMap) throws IOException {
addAction(new ParametersAction(buildPropertiesMapToParameterValues(buildParametersAsMap), buildParametersAsMap.keySet()));
return this;
}

//TODO: Rewrite in functional style when Java 8 is available
private List<ParameterValue> buildPropertiesMapToParameterValues(Map<String, String> buildParametersAsMap) {
List<ParameterValue> parameterValues = new ArrayList<>();
for (Map.Entry<String, String> parameter : buildParametersAsMap.entrySet()) {
parameterValues.add(new StringParameterValue(parameter.getKey(), parameter.getValue()));
}
return parameterValues;
}

MockBuild withSensitiveBuildParameters(String paramName, String paramValue) throws IOException {
addAction(new ParametersAction(ImmutableList.<ParameterValue>of(createSensitiveStringParameterValue(paramName, paramValue)),
ImmutableList.of(paramName)));
return this;
}

private StringParameterValue createSensitiveStringParameterValue(final String paramName, final String paramValue) {
return new StringParameterValue(paramName, paramValue) {
@Override
public boolean isSensitive() {
return true;
}
};
}
}
}

0 comments on commit 62adfa8

Please sign in to comment.