Skip to content
Open
Show file tree
Hide file tree
Changes from all 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 @@ -39,4 +39,20 @@ public static Collection<?> toCanonicalCollection(String data) {

return list;
}

public static String traverseJson(String data, String xpath) {
if (StringUtils.isEmpty(xpath)) {
return data;
}
JSON json = JSONSerializer.toJSON(data);

for (String part: xpath.split("/")) {
if (json instanceof JSONObject) {
json = JSONSerializer.toJSON(((JSONObject) json).get(part));
} else if (json instanceof JSONArray) {
json = JSONSerializer.toJSON(((JSONArray) json).get(Integer.parseInt(part)));
}
}
return json.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,19 @@ public class RemoteDataProvider extends AutocompleteDataProvider {
private boolean prefetch;
private String autoCompleteUrl;
private String credentialsId;

private String xpath;

@DataBoundConstructor
public RemoteDataProvider(boolean prefetch, String autoCompleteUrl, String credentialsId) {
public RemoteDataProvider(boolean prefetch, String autoCompleteUrl, String credentialsId, String xpath) {
this.prefetch = prefetch;
this.autoCompleteUrl = autoCompleteUrl;
this.credentialsId = credentialsId;
this.xpath = xpath;
}

@Override
public Collection<?> getData() {
return JSONUtils.toCanonicalCollection(performRequest(autoCompleteUrl, credentialsId));
return mapData(performRequest(autoCompleteUrl, credentialsId));
}

@Override
Expand All @@ -55,29 +57,30 @@ public Collection<?> filter(String query) {
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
return JSONUtils.toCanonicalCollection(
performRequest(
StrSubstitutor.replace(autoCompleteUrl, parameters)
, credentialsId
)
);
String url = StrSubstitutor.replace(autoCompleteUrl, parameters);
return mapData(performRequest(url, credentialsId));
}

@Override
public boolean isPrefetch() {
return prefetch;
}

@Exported
public String getXpath() {
return xpath;
}

@Exported
public String getCredentialsId() {
return credentialsId;
}

@Exported
public String getAutoCompleteUrl() {
return autoCompleteUrl;
}

@Extension
public static final class DescriptorImpl extends Descriptor<AutocompleteDataProvider> {
@Override
Expand Down Expand Up @@ -105,7 +108,7 @@ public ListBoxModel doFillCredentialsIdItems(@AncestorInPath Item context, @Quer
.includeCurrentValue(credentialsId);
}
}

private static String performRequest(String uri, String credentialsId) {
return RequestBuilder
.url(GlobalVariableUtils.resolveVariables(uri))
Expand All @@ -115,4 +118,9 @@ private static String performRequest(String uri, String credentialsId) {
.get()
.content;
}

private Collection<?> mapData(String data) {
String options = JSONUtils.traverseJson(data, xpath);
return JSONUtils.toCanonicalCollection(options);
}
}
4 changes: 2 additions & 2 deletions src/main/webapp/js/autocomplete-parameter.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ function evaluateExpression(expression, bindings, errorHandler)
var script = [];
script.push("(function(){");
for (var key in v) {
var value=v[key];
script.push("var " + key+"='"+value.replace(/'/g,"\\'")+"';");
var value=v[key].toString().replace(/'/g,"\\'");
script.push("var " + key+"='"+value+"';");
}
var expr = expression.substr(1,expression.length-2);
if (expr.trim().length == 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ public Project setupJobWithRemoteDataProvider(RemoteServerMock server) throws IO
String slowEndpoint = server.getAddress() + "/rest/users?slow=true";
FreeStyleProject project = j.createFreeStyleProject("remote");
AutoCompleteStringParameterDefinition prefetchedParameter = new AutoCompleteStringParameterDefinition("user", "", "", "name", "email", false
, new RemoteDataProvider(true, endpoint, "credentials"));
, new RemoteDataProvider(true, endpoint, "credentials", ""));
AutoCompleteStringParameterDefinition asyncParameter = new AutoCompleteStringParameterDefinition("other", "", "", "name", "email", false
, new RemoteDataProvider(false, slowEndpoint, "credentials"));
, new RemoteDataProvider(false, slowEndpoint, "credentials", ""));
project.addProperty(new ParametersDefinitionProperty(
prefetchedParameter, asyncParameter
));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ public Project setupJobWithRemoteDataProvider(RemoteServerMock server) throws IO
String slowEndpoint = server.getAddress() + "/rest/users?slow=true";
FreeStyleProject project = j.createFreeStyleProject("remote");
DropdownAutocompleteParameterDefinition prefetchedParameter = new DropdownAutocompleteParameterDefinition("leader", "", "name", "email", "beethoven@mail.com"
, new RemoteDataProvider(true, endpoint, "credentials"));
, new RemoteDataProvider(true, endpoint, "credentials", ""));
DropdownAutocompleteParameterDefinition asyncParameter = new DropdownAutocompleteParameterDefinition("sub-leader", "", "name", "email", ""
, new RemoteDataProvider(false, slowEndpoint, "credentials"));
, new RemoteDataProvider(false, slowEndpoint, "credentials", ""));
project.addProperty(new ParametersDefinitionProperty(
prefetchedParameter, asyncParameter
));
Expand All @@ -43,7 +43,7 @@ public Project setupJobWithRemoteDataProvider(RemoteServerMock server) throws IO
public Project setupJobWithAllDataProviders() throws IOException {
FreeStyleProject project = j.createFreeStyleProject();
DropdownAutocompleteParameterDefinition remoteParameter = new DropdownAutocompleteParameterDefinition("remote", "", "description", "full_name", ""
, new RemoteDataProvider(true, "https://api.github.com/search/repositories?q=${query}+user:jenkinsci", null));
, new RemoteDataProvider(true, "https://api.github.com/search/repositories?q=${query}+user:jenkinsci", null, ""));
DropdownAutocompleteParameterDefinition groovyParameter = new DropdownAutocompleteParameterDefinition("groovy", "", "value", "key", ""
, new GroovyDataProvider("return ['1':'One', '2':'Two', '3':'Three', '5':'Five', '8':'Eight', '13':'Thirteen'].entrySet()"
, true, null));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package org.jenkinsci.plugins.autocompleteparameter;

import org.junit.Assert;
import org.junit.Test;

public class JSONUtilsTest {

private String sample = "{" +
"'page': 1, " +
"'entries': [ " +
" {'name':'Eddard','house':'Stark'}," +
" {'name':'Robert','house':'Baratheon'}" +
" ], " +
"'inner': [" +
" {'data': [{'a': 1}]}, " +
" {'data': [{'a': 2}]}" +
" ] " +
"}";

@Test
public void traverseJson_empty() {
String array = "[{\"name\":\"Eddard\",\"house\":\"Stark\"},{\"name\":\"Robert\",\"house\":\"Baratheon\"}]";
String result = JSONUtils.traverseJson(array, "");
Assert.assertEquals(array, result);
}

@Test
public void traverseJson_root() {
String result = JSONUtils.traverseJson(sample, "entries");
Assert.assertEquals("[{\"name\":\"Eddard\",\"house\":\"Stark\"},{\"name\":\"Robert\",\"house\":\"Baratheon\"}]", result);
}

@Test
public void traverseJson_inner_array() {
String result = JSONUtils.traverseJson(sample, "inner/1/data");
Assert.assertEquals("[{\"a\":2}]", result);
}

@Test
public void traverseJson_null() {
String result = JSONUtils.traverseJson(sample, "inner/1/data/0/b");
Assert.assertEquals("null", result);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public void handle(HttpRequest arg0, HttpResponse response, HttpContext arg2) th
.create();
server.start();
try {
RemoteDataProvider subject = new RemoteDataProvider(true, "http://localhost:11331/test", null);
RemoteDataProvider subject = new RemoteDataProvider(true, "http://localhost:11331/test", null, "");
@SuppressWarnings("unchecked")
Collection<MorphDynaBean> actual = (Collection<MorphDynaBean>) subject.getData();
Iterator<MorphDynaBean> it = actual.iterator();
Expand All @@ -55,6 +55,37 @@ public void handle(HttpRequest arg0, HttpResponse response, HttpContext arg2) th
}
}

@Test
public void happyDayFilter() throws Exception {
HttpServer server = ServerBootstrap.bootstrap().setListenerPort(11331)
.registerHandler("/test/query=smth", new HttpRequestHandler() {
@Override
public void handle(HttpRequest arg0, HttpResponse response, HttpContext arg2) throws IOException {
response.setEntity(new StringEntity("{'start': 1, 'entries': [{'name':'Eddard'," +
"'house':'Stark'}, {'name':'Robert','house':'Baratheon'}]}"));
response.setStatusCode(200);
}
})
.create();
server.start();
try {
RemoteDataProvider subject = new RemoteDataProvider(true, "http://localhost:11331/test/query=smth", null, "entries");
@SuppressWarnings("unchecked")
Collection<MorphDynaBean> actual = (Collection<MorphDynaBean>) subject.filter("smth");
Iterator<MorphDynaBean> it = actual.iterator();

MorphDynaBean actual1 = it.next();
Assert.assertEquals("Eddard", actual1.get("name"));
Assert.assertEquals("Stark", actual1.get("house"));

MorphDynaBean actual2 = it.next();
Assert.assertEquals("Robert", actual2.get("name"));
Assert.assertEquals("Baratheon", actual2.get("house"));
} finally {
server.stop();
}
}

@Test
public void timeout() throws IOException {
// given
Expand All @@ -74,7 +105,7 @@ public void handle(HttpRequest arg0, HttpResponse response, HttpContext arg2) th
.create();
server.start();
try {
RemoteDataProvider subject = new RemoteDataProvider(true, "http://localhost:13311/test", null);
RemoteDataProvider subject = new RemoteDataProvider(true, "http://localhost:13311/test", null, "");

try {
// when
Expand Down