Support history query v5 additions

This commit is contained in:
Greg Schueler 2012-08-24 15:31:57 -07:00
parent 4d8959a0fa
commit c86a9a1b6e
8 changed files with 266 additions and 17 deletions

View file

@ -18,6 +18,7 @@ package org.rundeck.api;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.ProxySelector;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
@ -235,11 +236,21 @@ class ApiCall {
HttpPost httpPost = new HttpPost(client.getUrl() + RundeckClient.API_ENDPOINT + apiPath);
// POST a multi-part request, with all attachments
MultipartEntity entity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE);
for (Entry<String, InputStream> attachment : apiPath.getAttachments().entrySet()) {
entity.addPart(attachment.getKey(), new InputStreamBody(attachment.getValue(), attachment.getKey()));
if(apiPath.getAttachments().size()>0){
MultipartEntity entity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE);
for (Entry<String, InputStream> attachment : apiPath.getAttachments().entrySet()) {
entity.addPart(attachment.getKey(), new InputStreamBody(attachment.getValue(), attachment.getKey()));
}
httpPost.setEntity(entity);
}else if(apiPath.getForm().size()>0){
try {
httpPost.setEntity(new UrlEncodedFormEntity(apiPath.getForm(), HTTP.UTF_8));
} catch (UnsupportedEncodingException e) {
throw new RundeckApiException("Unsupported encoding: " + e.getMessage(), e);
}
}else {
throw new IllegalArgumentException("No Form or Multipart entity for POST content-body");
}
httpPost.setEntity(entity);
return execute(httpPost, parser);
}

View file

@ -16,11 +16,17 @@
package org.rundeck.api;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.apache.commons.lang.StringUtils;
import org.apache.http.NameValuePair;
import org.apache.http.entity.mime.FormBodyPart;
import org.apache.http.message.BasicNameValuePair;
import org.rundeck.api.util.ParametersUtil;
/**
@ -35,6 +41,7 @@ class ApiPathBuilder {
/** When POSTing, we can add attachments */
private final Map<String, InputStream> attachments;
private final List<NameValuePair> form = new ArrayList<NameValuePair>();
/** Marker for using the right separator between parameters ("?" or "&") */
private boolean firstParamDone = false;
@ -76,6 +83,61 @@ class ApiPathBuilder {
return this;
}
/**
* Append the given parameter (key and value). This will only append the parameter if it is not blank (null, empty
* or whitespace), and make sure to add the right separator ("?" or "&") before. The key and value will be separated
* by the "=" character. Also, the value will be url-encoded.
*
* @param key of the parameter. Must not be null or empty
* @param value of the parameter. May be null/empty/blank. Will be url-encoded.
* @return this, for method chaining
*/
public ApiPathBuilder param(final String key, final Collection<String> values) {
for(final String value: values){
if (StringUtils.isNotBlank(value)) {
appendSeparator();
append(key);
append("=");
append(ParametersUtil.urlEncode(value));
}
}
return this;
}
/**
* Append multiple values for the given Form field. This will be appended if it is not blank (null, empty
* or whitespace). The form field values will only be used for a "post" request
*
* @param key of the field name. Must not be null or empty
* @param values of the field. May be null/empty/blank. Will be url-encoded.
* @return this, for method chaining
*/
public ApiPathBuilder field(final String key, final Collection<String> values) {
if (null!=values) {
for(final String value: values){
if (StringUtils.isNotBlank(value)) {
form.add(new BasicNameValuePair(key, value));
}
}
}
return this;
}
/**
* Append a single value for the given Form field. This will be appended if it is not blank (null, empty
* or whitespace). The form field values will only be used for a "post" request
*
* @param key of the field name. Must not be null or empty
* @param value of the field. May be null/empty/blank. Will be url-encoded.
* @return this, for method chaining
*/
public ApiPathBuilder field(final String key, final String value) {
if (StringUtils.isNotBlank(value)) {
form.add(new BasicNameValuePair(key, value));
}
return this;
}
/**
* Append the given parameter (key and value). This will only append the parameter if it is not null, and make sure
* to add the right separator ("?" or "&") before. The key and value will be separated by the "=" character. Also,
@ -216,4 +278,10 @@ class ApiPathBuilder {
}
}
/**
* Form fields for POST request
*/
public List<NameValuePair> getForm() {
return form;
}
}

View file

@ -2065,7 +2065,7 @@ public class RundeckClient implements Serializable {
*/
public RundeckHistory getHistory(String project) throws RundeckApiException, RundeckApiLoginException,
RundeckApiTokenException, IllegalArgumentException {
return getHistory(project, null, null, null, null, null, null, null, null);
return getHistory(project, null, null,(String) null, (String) null, null, null, null, null);
}
/**
@ -2083,7 +2083,7 @@ public class RundeckClient implements Serializable {
*/
public RundeckHistory getHistory(String project, Long max, Long offset) throws RundeckApiException,
RundeckApiLoginException, RundeckApiTokenException, IllegalArgumentException {
return getHistory(project, null, null, null, null, null, null, max, offset);
return getHistory(project, null, null, (String)null, (String)null, null, null, max, offset);
}
/**
@ -2181,7 +2181,7 @@ public class RundeckClient implements Serializable {
*/
public RundeckHistory getHistory(String project, Date begin, Date end) throws RundeckApiException,
RundeckApiLoginException, RundeckApiTokenException, IllegalArgumentException {
return getHistory(project, null, null, null, null, begin, end, null, null);
return getHistory(project, null, null, (String)null, (String)null, begin, end, null, null);
}
/**
@ -2201,7 +2201,7 @@ public class RundeckClient implements Serializable {
*/
public RundeckHistory getHistory(String project, Date begin, Date end, Long max, Long offset)
throws RundeckApiException, RundeckApiLoginException, RundeckApiTokenException, IllegalArgumentException {
return getHistory(project, null, null, null, null, begin, end, max, offset);
return getHistory(project, null, null, (String)null, (String) null, begin, end, max, offset);
}
/**
@ -2229,17 +2229,65 @@ public class RundeckClient implements Serializable {
RundeckApiTokenException, IllegalArgumentException {
AssertUtil.notBlank(project, "project is mandatory to get the history !");
return new ApiCall(this).get(new ApiPathBuilder("/history").param("project", project)
.param("jobIdFilter", jobId)
.param("reportIdFilter", reportId)
.param("userFilter", user)
.param("recentFilter", recent)
.param("begin", begin)
.param("end", end)
.param("max", max)
.param("offset", offset),
.param("jobIdFilter", jobId)
.param("reportIdFilter", reportId)
.param("userFilter", user)
.param("recentFilter", recent)
.param("begin", begin)
.param("end", end)
.param("max", max)
.param("offset", offset),
new HistoryParser("result/events"));
}
/**
* Get the (events) history for the given project
*
* @param project name of the project - mandatory
* @param includeJobNames list of job names ("group/name") to include results for
* @param excludeJobNames list of job names ("group/name") to exclude results for
* @param user include only events created by the given user - optional
* @param recent include only events matching the given period of time. Format : "XY", where X is an
* integer, and Y is one of : "h" (hour), "d" (day), "w" (week), "m" (month), "y" (year).
* Example : "2w" (= last 2 weeks), "5d" (= last 5 days), etc. Optional.
* @param begin date for the earlier events to retrieve - optional
* @param end date for the latest events to retrieve - optional
* @param max number of results to return - optional (default to 20)
* @param offset the 0-indexed offset for the first result to return - optional (default to O)
*
* @return a {@link RundeckHistory} instance - won't be null
*
* @throws RundeckApiException in case of error when calling the API (non-existent project with this name)
* @throws RundeckApiLoginException if the login fails (in case of login-based authentication)
* @throws RundeckApiTokenException if the token is invalid (in case of token-based authentication)
* @throws IllegalArgumentException if the project is blank (null, empty or whitespace)
*/
public RundeckHistory getHistory(String project,
String user,
String recent,
List<String> includeJobNames,
List<String> excludeJobNames,
Date begin,
Date end,
Long max,
Long offset)
throws RundeckApiException, RundeckApiLoginException, RundeckApiTokenException, IllegalArgumentException {
AssertUtil.notBlank(project, "project is mandatory to get the history !");
final ApiPathBuilder builder = new ApiPathBuilder("/history").param("project", project)
.field("jobListFilter", includeJobNames)
.field("excludeJobListFilter", excludeJobNames)
.param("userFilter", user)
.param("recentFilter", recent)
.param("begin", begin)
.param("end", end)
.param("max", max)
.param("offset", offset);
return new ApiCall(this).post(builder, new HistoryParser("result/events"));
}
/*
* Nodes
*/

View file

@ -15,11 +15,18 @@
*/
package org.rundeck.api;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import betamax.MatchRule;
import betamax.TapeMode;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.rundeck.api.domain.RundeckEvent;
import org.rundeck.api.domain.RundeckHistory;
import org.rundeck.api.domain.RundeckProject;
import betamax.Betamax;
import betamax.Recorder;
@ -44,6 +51,55 @@ public class RundeckClientTest {
Assert.assertEquals("test", projects.get(0).getName());
Assert.assertNull(projects.get(0).getDescription());
}
@Test
@Betamax(tape = "get_history")
public void getHistory() throws Exception {
final RundeckHistory test = client.getHistory("test");
Assert.assertEquals(3, test.getCount());
Assert.assertEquals(20, test.getMax());
Assert.assertEquals(0, test.getOffset());
Assert.assertEquals(5, test.getTotal());
final List<RundeckEvent> events = test.getEvents();
Assert.assertEquals(3, events.size());
}
@Test
@Betamax(tape = "get_history_joblist",
match = {MatchRule.uri, MatchRule.method, MatchRule.path, MatchRule.query /*, MatchRule.body */})
public void getHistoryJoblist() throws Exception {
final List<String> jobNames = Arrays.asList("malk/blah", "malk/blah2");
final RundeckHistory test = client.getHistory("demo", null, null, jobNames, null, null, null, null, null);
Assert.assertEquals(2, test.getCount());
Assert.assertEquals(20, test.getMax());
Assert.assertEquals(0, test.getOffset());
Assert.assertEquals(2, test.getTotal());
final List<RundeckEvent> events = test.getEvents();
Assert.assertEquals(2, events.size());
final List<String> names = new ArrayList<String>();
for (final RundeckEvent event : events) {
names.add(event.getTitle());
}
Assert.assertEquals(Arrays.asList("malk/blah2", "malk/blah"), names);
}
@Test
@Betamax(tape = "get_history_excludeJoblist",
match = {MatchRule.uri, MatchRule.method, MatchRule.path, MatchRule.query /*, MatchRule.body */})
public void getHistoryExcludeJoblist() throws Exception {
final List<String> jobNames = Arrays.asList("malk/blah", "malk/blah2");
final RundeckHistory test = client.getHistory("demo", null, null, null, jobNames, null, null, null, null);
Assert.assertEquals(2, test.getCount());
Assert.assertEquals(20, test.getMax());
Assert.assertEquals(0, test.getOffset());
Assert.assertEquals(2, test.getTotal());
final List<RundeckEvent> events = test.getEvents();
Assert.assertEquals(2, events.size());
final List<String> names = new ArrayList<String>();
for (final RundeckEvent event : events) {
names.add(event.getTitle());
}
Assert.assertEquals(Arrays.asList("fliff", "malk/blah3"), names);
}
@Before
public void setUp() throws Exception {

View file

@ -1,4 +1,4 @@
betamax.tapeRoot=src/test/resources/betamax/tapes
betamax.proxyPort=1337
betamax.proxyTimeout=5000
betamax.defaultMode=READ_ONLY
#betamax.defaultMode=READ_ONLY

View file

@ -0,0 +1,20 @@
!tape
name: get_projects
interactions:
- recorded: 2011-09-18T16:04:45.973Z
request:
method: GET
uri: http://rundeck.local:4440/api/5/history?project=test
headers:
Host: rundeck.local:4440
Proxy-Connection: Keep-Alive
User-Agent: RunDeck API Java Client 5
X-RunDeck-Auth-Token: PVnN5K3OPc5vduS3uVuVnEsD57pDC5pd
response:
status: 200
headers:
Content-Type: text/xml; charset=utf-8
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Server: Jetty(6.1.21)
Set-Cookie: JSESSIONID=mxrbh6byhvxt;Path=/
body: <result success='true' apiversion='5'><events count='3' total='5' max='20' offset='0'><event starttime='1345764097165' endtime='1345764097283'><title>malk/blah3</title><status>succeeded</status><summary>echo aljdsf</summary><node-summary succeeded='1' failed='0' total='1'/><user>admin</user><project>demo</project><date-started>2012-08-23T23:21:37Z</date-started><date-ended>2012-08-23T23:21:37Z</date-ended><job id='1f8236b9-160b-4c5b-877a-e64409a3998d'/><execution id='3'/></event><event starttime='1345764091112' endtime='1345764091345'><title>malk/blah2</title><status>succeeded</status><summary>echo aljdsf</summary><node-summary succeeded='1' failed='0' total='1'/><user>admin</user><project>demo</project><date-started>2012-08-23T23:21:31Z</date-started><date-ended>2012-08-23T23:21:31Z</date-ended><job id='fffff'/><execution id='2'/></event><event starttime='1345764083979' endtime='1345764085207'><title>malk/blah</title><status>succeeded</status><summary>echo aljdsf</summary><node-summary succeeded='1' failed='0' total='1'/><user>admin</user><project>demo</project><date-started>2012-08-23T23:21:23Z</date-started><date-ended>2012-08-23T23:21:25Z</date-ended><job id='ffdd'/><execution id='1'/></event></events></result>

View file

@ -0,0 +1,23 @@
!tape
name: get_history_excludeJoblist
interactions:
- recorded: 2012-08-24T21:55:44.759Z
request:
method: POST
uri: http://rundeck.local:4440/api/5/history?project=demo
headers:
Content-Length: '66'
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Host: rundeck.localtest:8080
Proxy-Connection: Keep-Alive
User-Agent: RunDeck API Java Client 5
X-RunDeck-Auth-Token: 960412PR40dRREU5d87S2Ce2OeddD5c1
body: 'excludeJobListFilter=malk/blah&excludeJobListFilter=malk/blah2'
response:
status: 200
headers:
Content-Type: text/xml; charset=utf-8
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Server: Jetty(6.1.21)
Set-Cookie: JSESSIONID=hgu6yb4dpooy;Path=/rundeck
body: <result success='true' apiversion='5'><events count='2' total='2' max='20' offset='0'><event starttime='1345766449947' endtime='1345766451261'><title>fliff</title><status>succeeded</status><summary>echo there i was not even counting it</summary><node-summary succeeded='1' failed='0' total='1'/><user>admin</user><project>demo</project><date-started>2012-08-24T00:00:49Z</date-started><date-ended>2012-08-24T00:00:51Z</date-ended><job id='f2dd438a-17cb-4c2d-8c0c-d03b2b601c67'/><execution id='4'/></event><event starttime='1345764097165' endtime='1345764097283'><title>malk/blah3</title><status>succeeded</status><summary>echo aljdsf</summary><node-summary succeeded='1' failed='0' total='1'/><user>admin</user><project>demo</project><date-started>2012-08-23T23:21:37Z</date-started><date-ended>2012-08-23T23:21:37Z</date-ended><job id='1f8236b9-160b-4c5b-877a-e64409a3998d'/><execution id='3'/></event></events></result>

View file

@ -0,0 +1,23 @@
!tape
name: get_history_joblist
interactions:
- recorded: 2012-08-24T21:37:50.531Z
request:
method: POST
uri: http://rundeck.local:4440/api/5/history?project=demo
headers:
Content-Length: '52'
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Host: rundeck.localtest:8080
Proxy-Connection: Keep-Alive
User-Agent: RunDeck API Java Client 5
X-RunDeck-Auth-Token: 960412PR40dRREU5d87S2Ce2OeddD5c1
body: 'jobListFilter=malk/blah&jobListFilter=malk/blah2'
response:
status: 200
headers:
Content-Type: text/xml; charset=utf-8
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Server: Jetty(6.1.21)
Set-Cookie: JSESSIONID=118zw5docyvss;Path=/rundeck
body: <result success='true' apiversion='5'><events count='2' total='2' max='20' offset='0'><event starttime='1345764091112' endtime='1345764091345'><title>malk/blah2</title><status>succeeded</status><summary>echo aljdsf</summary><node-summary succeeded='1' failed='0' total='1'/><user>admin</user><project>demo</project><date-started>2012-08-23T23:21:31Z</date-started><date-ended>2012-08-23T23:21:31Z</date-ended><job id='fffff'/><execution id='2'/></event><event starttime='1345764083979' endtime='1345764085207'><title>malk/blah</title><status>succeeded</status><summary>echo aljdsf</summary><node-summary succeeded='1' failed='0' total='1'/><user>admin</user><project>demo</project><date-started>2012-08-23T23:21:23Z</date-started><date-ended>2012-08-23T23:21:25Z</date-ended><job id='ffdd'/><execution id='1'/></event></events></result>