From e548c14b242f393406fb46f22b276d085b7366d2 Mon Sep 17 00:00:00 2001 From: Greg Schueler Date: Fri, 7 Mar 2014 10:07:31 -0800 Subject: [PATCH] Add setProjectConfig for apiv11 --- src/main/java/org/rundeck/api/ApiCall.java | 36 ++++++++++------ .../java/org/rundeck/api/ApiPathBuilder.java | 13 ++++++ .../java/org/rundeck/api/RundeckClient.java | 25 ++++++++++- .../api/generator/BaseDocGenerator.java | 18 ++++++++ .../api/generator/ProjectConfigGenerator.java | 34 +++++++++++++++ .../api/generator/ProjectGenerator.java | 18 +++----- .../api/generator/XmlDocumentGenerator.java | 29 +++++++++++++ .../org/rundeck/api/RundeckClientTest.java | 14 +++++++ .../generator/ProjectConfigGeneratorTest.java | 41 +++++++++++++++++++ .../api/generator/ProjectGeneratorTest.java | 2 +- .../betamax/tapes/set_project_configv11.yaml | 26 ++++++++++++ 11 files changed, 230 insertions(+), 26 deletions(-) create mode 100644 src/main/java/org/rundeck/api/generator/BaseDocGenerator.java create mode 100644 src/main/java/org/rundeck/api/generator/ProjectConfigGenerator.java create mode 100644 src/main/java/org/rundeck/api/generator/XmlDocumentGenerator.java create mode 100644 src/test/java/org/rundeck/api/generator/ProjectConfigGeneratorTest.java create mode 100644 src/test/resources/betamax/tapes/set_project_configv11.yaml diff --git a/src/main/java/org/rundeck/api/ApiCall.java b/src/main/java/org/rundeck/api/ApiCall.java index 1dc2059..8d9f857 100644 --- a/src/main/java/org/rundeck/api/ApiCall.java +++ b/src/main/java/org/rundeck/api/ApiCall.java @@ -16,19 +16,10 @@ package org.rundeck.api; import org.apache.commons.lang.StringUtils; -import org.apache.http.Header; -import org.apache.http.HttpException; -import org.apache.http.HttpRequest; -import org.apache.http.HttpRequestInterceptor; -import org.apache.http.HttpResponse; -import org.apache.http.NameValuePair; -import org.apache.http.ParseException; +import org.apache.http.*; import org.apache.http.client.HttpClient; import org.apache.http.client.entity.UrlEncodedFormEntity; -import org.apache.http.client.methods.HttpDelete; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.client.methods.HttpRequestBase; +import org.apache.http.client.methods.*; import org.apache.http.conn.scheme.Scheme; import org.apache.http.conn.ssl.SSLSocketFactory; import org.apache.http.conn.ssl.TrustStrategy; @@ -272,6 +263,27 @@ class ApiCall { public T post(ApiPathBuilder apiPath, XmlNodeParser parser) throws RundeckApiException, RundeckApiLoginException, RundeckApiTokenException { HttpPost httpPost = new HttpPost(client.getUrl() + client.getApiEndpoint() + apiPath); + return requestWithEntity(apiPath, parser, httpPost); + } + /** + * Execute an HTTP PUT request to the RunDeck instance, on the given path. We will login first, and then execute + * the API call. At the end, the given parser will be used to convert the response to a more useful result object. + * + * @param apiPath on which we will make the HTTP request - see {@link ApiPathBuilder} + * @param parser used to parse the response + * @return the result of the call, as formatted by the parser + * @throws RundeckApiException in case of error when calling the API + * @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) + */ + public T put(ApiPathBuilder apiPath, XmlNodeParser parser) throws RundeckApiException, + RundeckApiLoginException, RundeckApiTokenException { + HttpPut httpPut = new HttpPut(client.getUrl() + client.getApiEndpoint() + apiPath); + return requestWithEntity(apiPath, parser, httpPut); + } + + private T requestWithEntity(ApiPathBuilder apiPath, XmlNodeParser parser, HttpEntityEnclosingRequestBase + httpPost) { if(null!= apiPath.getAccept()) { httpPost.setHeader("Accept", apiPath.getAccept()); } @@ -344,7 +356,7 @@ class ApiCall { * @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) */ - private ByteArrayInputStream execute(HttpRequestBase request) throws RundeckApiException, RundeckApiLoginException, + private ByteArrayInputStream execute(HttpUriRequest request) throws RundeckApiException, RundeckApiLoginException, RundeckApiTokenException { HttpClient httpClient = instantiateHttpClient(); try { diff --git a/src/main/java/org/rundeck/api/ApiPathBuilder.java b/src/main/java/org/rundeck/api/ApiPathBuilder.java index e749004..bf27ae9 100644 --- a/src/main/java/org/rundeck/api/ApiPathBuilder.java +++ b/src/main/java/org/rundeck/api/ApiPathBuilder.java @@ -27,6 +27,7 @@ import org.apache.commons.lang.StringUtils; import org.apache.http.NameValuePair; import org.apache.http.message.BasicNameValuePair; import org.dom4j.Document; +import org.rundeck.api.generator.XmlDocumentGenerator; import org.rundeck.api.util.ParametersUtil; /** @@ -280,6 +281,18 @@ class ApiPathBuilder { } return this; } + /** + * When POSTing a request, add the given XMl Document as the content of the request. + * + * @param document XMl document to send + * @return this, for method chaining + */ + public ApiPathBuilder xml(final XmlDocumentGenerator document) { + if (document != null) { + xmlDocument = document.generateXmlDocument(); + } + return this; + } /** * @return all attachments to be POSTed, with their names diff --git a/src/main/java/org/rundeck/api/RundeckClient.java b/src/main/java/org/rundeck/api/RundeckClient.java index cd3739e..8cf1989 100644 --- a/src/main/java/org/rundeck/api/RundeckClient.java +++ b/src/main/java/org/rundeck/api/RundeckClient.java @@ -25,6 +25,7 @@ import org.rundeck.api.RundeckApiException.RundeckApiLoginException; import org.rundeck.api.RundeckApiException.RundeckApiTokenException; import org.rundeck.api.domain.*; import org.rundeck.api.domain.RundeckExecution.ExecutionStatus; +import org.rundeck.api.generator.ProjectConfigGenerator; import org.rundeck.api.generator.ProjectGenerator; import org.rundeck.api.parser.*; import org.rundeck.api.query.ExecutionQuery; @@ -379,6 +380,28 @@ public class RundeckClient implements Serializable { return new ApiCall(this) .get(new ApiPathBuilder("/project/", projectName, "/config"), new ProjectConfigParser("/config")); } + /** + * Return the configuration of a project + * + * @param projectName name of the project - mandatory + * + * @return a {@link ProjectConfig} 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 projectName is blank (null, empty or whitespace) + */ + public ProjectConfig setProjectConfig(String projectName, Map configuration) throws + RundeckApiException, RundeckApiLoginException, + RundeckApiTokenException, IllegalArgumentException { + + AssertUtil.notBlank(projectName, "projectName is mandatory to get the config of a project !"); + return new ApiCall(this) + .put(new ApiPathBuilder("/project/", projectName, "/config") + .xml(new ProjectConfigGenerator(new ProjectConfig(configuration))) + , new ProjectConfigParser("/config")); + } private Document projectDocument(String projectName, Map configuration) { RundeckProject project = new RundeckProject(); @@ -386,7 +409,7 @@ public class RundeckClient implements Serializable { if (null != configuration) { project.setProjectConfig(new ProjectConfig(configuration)); } - return new ProjectGenerator(project).generate(); + return new ProjectGenerator(project).generateXmlDocument(); } /* diff --git a/src/main/java/org/rundeck/api/generator/BaseDocGenerator.java b/src/main/java/org/rundeck/api/generator/BaseDocGenerator.java new file mode 100644 index 0000000..46bf574 --- /dev/null +++ b/src/main/java/org/rundeck/api/generator/BaseDocGenerator.java @@ -0,0 +1,18 @@ +package org.rundeck.api.generator; + +import org.dom4j.Document; +import org.dom4j.DocumentFactory; + +/** + * BaseDocGenerator generates a document using the element as the root. + * + * @author greg + * @since 2014-02-27 + */ +public abstract class BaseDocGenerator implements XmlDocumentGenerator { + @Override + public Document generateXmlDocument() { + return DocumentFactory.getInstance().createDocument(generateXmlElement()); + } + +} diff --git a/src/main/java/org/rundeck/api/generator/ProjectConfigGenerator.java b/src/main/java/org/rundeck/api/generator/ProjectConfigGenerator.java new file mode 100644 index 0000000..547b154 --- /dev/null +++ b/src/main/java/org/rundeck/api/generator/ProjectConfigGenerator.java @@ -0,0 +1,34 @@ +package org.rundeck.api.generator; + +import org.dom4j.Document; +import org.dom4j.DocumentFactory; +import org.dom4j.Element; +import org.rundeck.api.domain.ProjectConfig; + +/** + * ProjectConfigGenerator is ... + * + * @author greg + * @since 2014-02-27 + */ +public class ProjectConfigGenerator extends BaseDocGenerator { + private ProjectConfig config; + + public ProjectConfigGenerator(ProjectConfig config) { + this.config = config; + } + + @Override + public Element generateXmlElement() { + Element configEl = DocumentFactory.getInstance().createElement("config"); + if (null != config.getProperties()) { + for (String s : config.getProperties().keySet()) { + Element property = configEl.addElement("property"); + property.addAttribute("key", s); + property.addAttribute("value", config.getProperties().get(s)); + } + } + return configEl; + } + +} diff --git a/src/main/java/org/rundeck/api/generator/ProjectGenerator.java b/src/main/java/org/rundeck/api/generator/ProjectGenerator.java index 37a45a4..8960acf 100644 --- a/src/main/java/org/rundeck/api/generator/ProjectGenerator.java +++ b/src/main/java/org/rundeck/api/generator/ProjectGenerator.java @@ -12,27 +12,21 @@ import org.rundeck.api.domain.RundeckProject; * @author greg * @since 2014-02-27 */ -public class ProjectGenerator { +public class ProjectGenerator extends BaseDocGenerator { RundeckProject project; public ProjectGenerator(RundeckProject project) { this.project = project; } - public Document generate() { - Document projectDom = DocumentFactory.getInstance().createDocument(); - Element rootElem = projectDom.addElement("project"); + @Override + public Element generateXmlElement() { + Element rootElem = DocumentFactory.getInstance().createElement("project"); rootElem.addElement("name").setText(project.getName()); ProjectConfig configuration = project.getProjectConfig(); if (null != configuration) { - - Element config = rootElem.addElement("config"); - for (String s : configuration.getProperties().keySet()) { - Element property = config.addElement("property"); - property.addAttribute("key", s); - property.addAttribute("value", configuration.getProperties().get(s)); - } + rootElem.add(new ProjectConfigGenerator(configuration).generateXmlElement()); } - return projectDom; + return rootElem; } } diff --git a/src/main/java/org/rundeck/api/generator/XmlDocumentGenerator.java b/src/main/java/org/rundeck/api/generator/XmlDocumentGenerator.java new file mode 100644 index 0000000..7d3faf3 --- /dev/null +++ b/src/main/java/org/rundeck/api/generator/XmlDocumentGenerator.java @@ -0,0 +1,29 @@ +package org.rundeck.api.generator; + +import org.dom4j.Document; +import org.dom4j.Element; +import org.dom4j.Node; + +/** + * XmlDocumentGenerator is ... + * + * @author greg + * @since 2014-02-27 + */ +public interface XmlDocumentGenerator { + + /** + * Generate the XML {@link org.dom4j.Node} + * + * @return any object holding the converted value + */ + Element generateXmlElement(); + /** + * Generate the XML {@link org.dom4j.Node} + * + * @param node + * + * @return any object holding the converted value + */ + Document generateXmlDocument(); +} diff --git a/src/test/java/org/rundeck/api/RundeckClientTest.java b/src/test/java/org/rundeck/api/RundeckClientTest.java index 4ab1fe7..0034aa6 100644 --- a/src/test/java/org/rundeck/api/RundeckClientTest.java +++ b/src/test/java/org/rundeck/api/RundeckClientTest.java @@ -101,6 +101,20 @@ public class RundeckClientTest { Assert.assertEquals(9,config.getProperties().size()); Assert.assertEquals("monkey1", config.getProperties().get("project.name")); } + @Test + @Betamax(tape = "set_project_configv11") + public void setProjectConfig() throws Exception { + HashMap config = new HashMap(); + config.put("alphabetty", "spaghetti"); + config.put("blha.blee", "a big amazing thingy so there."); + ProjectConfig result = createClient(TEST_TOKEN_6, 11).setProjectConfig("monkey1", config); + Assert.assertNotNull(result); + Assert.assertNotNull(result.getProperties()); + Assert.assertEquals(3, result.getProperties().size()); + Assert.assertEquals("monkey1", result.getProperties().get("project.name")); + Assert.assertEquals("spaghetti", result.getProperties().get("alphabetty")); + Assert.assertEquals("a big amazing thingy so there.", result.getProperties().get("blha.blee")); + } @Test @Betamax(tape = "get_history") diff --git a/src/test/java/org/rundeck/api/generator/ProjectConfigGeneratorTest.java b/src/test/java/org/rundeck/api/generator/ProjectConfigGeneratorTest.java new file mode 100644 index 0000000..aa6d5da --- /dev/null +++ b/src/test/java/org/rundeck/api/generator/ProjectConfigGeneratorTest.java @@ -0,0 +1,41 @@ +package org.rundeck.api.generator; + +import junit.framework.Assert; +import org.dom4j.Document; +import org.dom4j.Element; +import org.dom4j.io.XMLWriter; +import org.junit.Test; +import org.rundeck.api.domain.ProjectConfig; +import org.rundeck.api.domain.RundeckProject; + +import java.io.UnsupportedEncodingException; + +/** + * ProjectConfigGeneratorTest is ... + * + * @author greg + * @since 2014-02-27 + */ +public class ProjectConfigGeneratorTest { + @Test + public void generate() throws Exception { + ProjectConfig config = new ProjectConfig(); + config.setProperty("abc", "123"); + config.setProperty("monkey.bonanza", "pale\ncomparison"); + + Document doc = new ProjectConfigGenerator(config).generateXmlDocument(); + XMLWriter xmlWriter = new XMLWriter(System.out); + xmlWriter.write(doc); + xmlWriter.flush(); + Element configElement = doc.getRootElement(); + Assert.assertEquals("config", configElement.getName()); + Assert.assertNotNull(configElement.selectSingleNode("property[1]")); + Assert.assertEquals("abc", configElement.selectSingleNode("property[1]/@key").getText()); + Assert.assertEquals("123", configElement.selectSingleNode("property[1]/@value").getText()); + + Assert.assertNotNull(configElement.selectSingleNode("property[2]")); + Assert.assertEquals("monkey.bonanza", configElement.selectSingleNode("property[2]/@key").getText()); + Assert.assertEquals("pale\ncomparison", configElement.selectSingleNode("property[2]/@value").getText()); + + } +} diff --git a/src/test/java/org/rundeck/api/generator/ProjectGeneratorTest.java b/src/test/java/org/rundeck/api/generator/ProjectGeneratorTest.java index a9c5b1b..26beb33 100644 --- a/src/test/java/org/rundeck/api/generator/ProjectGeneratorTest.java +++ b/src/test/java/org/rundeck/api/generator/ProjectGeneratorTest.java @@ -17,7 +17,7 @@ public class ProjectGeneratorTest { RundeckProject project = new RundeckProject(); project.setName("monkey1"); - Document doc = new ProjectGenerator(project).generate(); + Document doc = new ProjectGenerator(project).generateXmlDocument(); Assert.assertEquals("project", doc.getRootElement().getName()); Assert.assertNotNull(doc.selectSingleNode("/project/name")); Assert.assertEquals("monkey1", doc.selectSingleNode("/project/name").getText()); diff --git a/src/test/resources/betamax/tapes/set_project_configv11.yaml b/src/test/resources/betamax/tapes/set_project_configv11.yaml new file mode 100644 index 0000000..573eeaa --- /dev/null +++ b/src/test/resources/betamax/tapes/set_project_configv11.yaml @@ -0,0 +1,26 @@ +!tape +name: set_project_configv11 +interactions: +- recorded: 2014-02-27T21:00:27.197Z + request: + method: PUT + uri: http://rundeck.local:4440/api/11/project/monkey1/config + headers: + Accept: text/xml + Content-Type: application/xml + Host: rundeck.local:4440 + Proxy-Connection: Keep-Alive + Transfer-Encoding: chunked + User-Agent: RunDeck API Java Client 11 + X-RunDeck-Auth-Token: Do4d3NUD5DKk21DR4sNK755RcPk618vn + response: + status: 200 + headers: + Content-Type: application/xml;charset=UTF-8 + Expires: Thu, 01 Jan 1970 00:00:00 GMT + Server: Jetty(7.6.0.v20120127) + Set-Cookie: JSESSIONID=19npj7cd0hpm71nfljn7nlbvh8;Path=/ + X-Rundeck-API-Version: '11' + X-Rundeck-API-XML-Response-Wrapper: 'false' + body: !!binary |- + PGNvbmZpZz4KICA8cHJvcGVydHkga2V5PSdwcm9qZWN0Lm5hbWUnIHZhbHVlPSdtb25rZXkxJyAvPgogIDxwcm9wZXJ0eSBrZXk9J2FscGhhYmV0dHknIHZhbHVlPSdzcGFnaGV0dGknIC8+CiAgPHByb3BlcnR5IGtleT0nYmxoYS5ibGVlJyB2YWx1ZT0nYSBiaWcgYW1hemluZyB0aGluZ3kgc28gdGhlcmUuJyAvPgo8L2NvbmZpZz4=