diff --git a/src/main/java/org/rundeck/api/RundeckClient.java b/src/main/java/org/rundeck/api/RundeckClient.java index 8cf1989..f32f457 100644 --- a/src/main/java/org/rundeck/api/RundeckClient.java +++ b/src/main/java/org/rundeck/api/RundeckClient.java @@ -26,6 +26,7 @@ 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.ProjectConfigPropertyGenerator; import org.rundeck.api.generator.ProjectGenerator; import org.rundeck.api.parser.*; import org.rundeck.api.query.ExecutionQuery; @@ -380,6 +381,92 @@ public class RundeckClient implements Serializable { return new ApiCall(this) .get(new ApiPathBuilder("/project/", projectName, "/config"), new ProjectConfigParser("/config")); } + /** + * Get a single project configuration key + * + * @param projectName name of the project - mandatory + * @param key name of the configuration key + * + * @return value, or null if the value is not set + * + * @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 String getProjectConfig(final String projectName, final String key) throws + RundeckApiException, RundeckApiLoginException, + RundeckApiTokenException, IllegalArgumentException { + + AssertUtil.notBlank(projectName, "projectName is mandatory to get the config of a project !"); + AssertUtil.notBlank(key, "key is mandatory to get the config key value!"); + + ConfigProperty configProperty = null; + try { + configProperty = new ApiCall(this) + .get(new ApiPathBuilder("/project/", projectName, "/config/", key), + new ProjectConfigPropertyParser("/property")); + } catch (RundeckApiException.RundeckApiHttpStatusException e) { + if(404==e.getStatusCode()){ + return null; + } + throw e; + } + return configProperty.getValue(); + } + /** + * Set a single project configuration property value + * + * @param projectName name of the project - mandatory + * @param key name of the configuration property + * @param value value of the property + * + * @return new value + * + * @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 String setProjectConfig(final String projectName, final String key, final String value) throws + RundeckApiException, RundeckApiLoginException, + RundeckApiTokenException, IllegalArgumentException { + + AssertUtil.notBlank(projectName, "projectName is mandatory to set the config of a project !"); + AssertUtil.notBlank(key, "key is mandatory to set the config key value!"); + AssertUtil.notBlank(value, "value is mandatory to set the config key value!"); + + final ConfigProperty configProperty = new ApiCall(this) + .put(new ApiPathBuilder("/project/", projectName, "/config/", key) + .xml(new ProjectConfigPropertyGenerator(new ConfigProperty(key, value))), + new ProjectConfigPropertyParser("/property")); + + return configProperty.getValue(); + } + /** + * Set a single project configuration property value + * + * @param projectName name of the project - mandatory + * @param key name of the configuration property + * @param value value of the property + * + * @return new value + * + * @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 void deleteProjectConfig(final String projectName, final String key) throws + RundeckApiException, RundeckApiLoginException, + RundeckApiTokenException, IllegalArgumentException { + + AssertUtil.notBlank(projectName, "projectName is mandatory to set the config of a project !"); + AssertUtil.notBlank(key, "key is mandatory to set the config key value!"); + + new ApiCall(this).delete(new ApiPathBuilder("/project/", projectName, "/config/", + key).accept("application/xml")); + } /** * Return the configuration of a project * diff --git a/src/main/java/org/rundeck/api/domain/ConfigProperty.java b/src/main/java/org/rundeck/api/domain/ConfigProperty.java new file mode 100644 index 0000000..d15d773 --- /dev/null +++ b/src/main/java/org/rundeck/api/domain/ConfigProperty.java @@ -0,0 +1,68 @@ +package org.rundeck.api.domain; + +import java.io.Serializable; + +/** + * ConfigProperty is a single configuration property key and value. + * + * @author greg + * @since 2014-03-07 + */ +public class ConfigProperty implements Serializable { + + private static final long serialVersionUID = 1L; + private String key; + private String value; + + public ConfigProperty() { + } + + public ConfigProperty(String key, String value) { + this.key = key; + this.value = value; + } + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof ConfigProperty)) return false; + + ConfigProperty that = (ConfigProperty) o; + + if (!key.equals(that.key)) return false; + if (!value.equals(that.value)) return false; + + return true; + } + + @Override + public int hashCode() { + int result = key.hashCode(); + result = 31 * result + value.hashCode(); + return result; + } + + @Override + public String toString() { + return "ConfigProperty{" + + "key='" + key + '\'' + + ", value='" + value + '\'' + + '}'; + } +} diff --git a/src/main/java/org/rundeck/api/generator/ProjectConfigPropertyGenerator.java b/src/main/java/org/rundeck/api/generator/ProjectConfigPropertyGenerator.java new file mode 100644 index 0000000..f41766b --- /dev/null +++ b/src/main/java/org/rundeck/api/generator/ProjectConfigPropertyGenerator.java @@ -0,0 +1,28 @@ +package org.rundeck.api.generator; + +import org.dom4j.DocumentFactory; +import org.dom4j.Element; +import org.rundeck.api.domain.ConfigProperty; + +/** + * ProjectConfigPropertyGenerator generates a {@literal } element representing a configuration property. + * + * @author greg + * @since 2014-03-07 + */ +public class ProjectConfigPropertyGenerator extends BaseDocGenerator { + private ConfigProperty property; + + public ProjectConfigPropertyGenerator(ConfigProperty property) { + this.property = property; + } + + @Override + public Element generateXmlElement() { + Element propElem = DocumentFactory.getInstance().createElement("property"); + propElem.addAttribute("key", property.getKey()); + propElem.addAttribute("value", property.getValue()); + + return propElem; + } +} diff --git a/src/main/java/org/rundeck/api/parser/ProjectConfigPropertyParser.java b/src/main/java/org/rundeck/api/parser/ProjectConfigPropertyParser.java new file mode 100644 index 0000000..9a57114 --- /dev/null +++ b/src/main/java/org/rundeck/api/parser/ProjectConfigPropertyParser.java @@ -0,0 +1,41 @@ +package org.rundeck.api.parser; + +import org.dom4j.Node; +import org.rundeck.api.domain.ConfigProperty; + +/** + * ProjectConfigPropertyParser parses a {@literal } element representing + * a configuration property. + * + * @author greg + * @since 2014-03-07 + */ +public class ProjectConfigPropertyParser implements XmlNodeParser { + private String xpath; + + public ProjectConfigPropertyParser() { + } + + public ProjectConfigPropertyParser(final String xpath) { + this.setXpath(xpath); + } + + @Override + public ConfigProperty parseXmlNode(final Node node) { + final Node propnode = getXpath() != null ? node.selectSingleNode(getXpath()) : node; + final String key = propnode.valueOf("@key"); + final String value = propnode.valueOf("@value"); + final ConfigProperty config = new ConfigProperty(); + config.setKey(key); + config.setValue(value); + return config; + } + + public String getXpath() { + return xpath; + } + + public void setXpath(final String xpath) { + this.xpath = xpath; + } +} diff --git a/src/test/java/org/rundeck/api/RundeckClientTest.java b/src/test/java/org/rundeck/api/RundeckClientTest.java index 0034aa6..ad475fd 100644 --- a/src/test/java/org/rundeck/api/RundeckClientTest.java +++ b/src/test/java/org/rundeck/api/RundeckClientTest.java @@ -116,6 +116,35 @@ public class RundeckClientTest { Assert.assertEquals("a big amazing thingy so there.", result.getProperties().get("blha.blee")); } + @Test + @Betamax(tape = "get_project_config_keyedv11") + public void getProjectConfigKeyed() throws Exception { + String value = createClient(TEST_TOKEN_6, 11).getProjectConfig("ABC", "project.name"); + Assert.assertNotNull(value); + Assert.assertEquals("ABC", value); + } + @Test + @Betamax(tape = "get_project_config_keyed_dne_v11") + public void getProjectConfigKeyedDNE() throws Exception { + String value = createClient(TEST_TOKEN_6, 11).getProjectConfig("ABC", "does-not-exist"); + Assert.assertNull(value); + } + @Test + @Betamax(tape = "set_project_config_keyedv11") + public void setProjectConfigKeyed() throws Exception { + String value = createClient(TEST_TOKEN_6, 11).setProjectConfig("ABC", "monkey-burrito", "lemon pie"); + Assert.assertNotNull(value); + Assert.assertEquals("lemon pie", value); + } + @Test + @Betamax(tape = "delete_project_config_keyedv11") + public void deleteProjectConfigKeyed() throws Exception { + RundeckClient client1 = createClient(TEST_TOKEN_6, 11); + Assert.assertEquals("7up", client1.setProjectConfig("ABC", "monkey-burrito", "7up")); + client1.deleteProjectConfig("ABC", "monkey-burrito"); + String value=client1.getProjectConfig("ABC", "monkey-burrito"); + Assert.assertNull(value); + } @Test @Betamax(tape = "get_history") public void getHistory() throws Exception { diff --git a/src/test/java/org/rundeck/api/parser/ProjectConfigPropertyParserTest.java b/src/test/java/org/rundeck/api/parser/ProjectConfigPropertyParserTest.java new file mode 100644 index 0000000..0fcac10 --- /dev/null +++ b/src/test/java/org/rundeck/api/parser/ProjectConfigPropertyParserTest.java @@ -0,0 +1,48 @@ +package org.rundeck.api.parser; + +import junit.framework.Assert; +import org.dom4j.Document; +import org.junit.Test; +import org.junit.runners.JUnit4; +import org.rundeck.api.domain.ConfigProperty; +import org.rundeck.api.domain.ProjectConfig; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; + +/** + * Test + * + * @author greg + * @since 2014-03-07 + */ +public class ProjectConfigPropertyParserTest { + @Test + public void parseFromProject() throws Exception { + InputStream input = getClass().getResourceAsStream("projectv11.xml"); + Document document = ParserHelper.loadDocument(input); + + ConfigProperty config = new ProjectConfigPropertyParser("project/config/property[1]").parseXmlNode(document); + Assert.assertEquals("project.name", config.getKey()); + Assert.assertEquals("ziggy", config.getValue()); + /** + * + + + */ + } + @Test + public void parseProperty() throws Exception { + Document document = ParserHelper.loadDocument(new ByteArrayInputStream(("").getBytes())); + + ConfigProperty config = new ProjectConfigPropertyParser("/property").parseXmlNode(document); + Assert.assertEquals("project.name", config.getKey()); + Assert.assertEquals("ABC", config.getValue()); + /** + * + + + */ + } +} diff --git a/src/test/resources/betamax/tapes/delete_project_config_keyedv11.yaml b/src/test/resources/betamax/tapes/delete_project_config_keyedv11.yaml new file mode 100644 index 0000000..c107f56 --- /dev/null +++ b/src/test/resources/betamax/tapes/delete_project_config_keyedv11.yaml @@ -0,0 +1,57 @@ +!tape +name: delete_project_config_keyedv11 +interactions: +- recorded: 2014-03-07T19:59:51.228Z + request: + method: PUT + uri: http://rundeck.local:4440/api/11/project/ABC/config/monkey-burrito + 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=bolnwf54stai1bo049hylrsua;Path=/ + X-Rundeck-API-Version: '11' + X-Rundeck-API-XML-Response-Wrapper: 'false' + body: !!binary |- + PHByb3BlcnR5IGtleT0nbW9ua2V5LWJ1cnJpdG8nIHZhbHVlPSc3dXAnIC8+ +- recorded: 2014-03-07T19:59:51.325Z + request: + method: DELETE + uri: http://rundeck.local:4440/api/11/project/ABC/config/monkey-burrito + headers: + Host: rundeck.local:4440 + Proxy-Connection: Keep-Alive + User-Agent: RunDeck API Java Client 11 + X-RunDeck-Auth-Token: Do4d3NUD5DKk21DR4sNK755RcPk618vn + response: + status: 204 + headers: + Content-Type: text/html;charset=UTF-8 + Server: Jetty(7.6.0.v20120127) +- recorded: 2014-03-07T19:59:51.402Z + request: + method: GET + uri: http://rundeck.local:4440/api/11/project/ABC/config/monkey-burrito + headers: + Accept: text/xml + Host: rundeck.local:4440 + Proxy-Connection: Keep-Alive + User-Agent: RunDeck API Java Client 11 + X-RunDeck-Auth-Token: Do4d3NUD5DKk21DR4sNK755RcPk618vn + response: + status: 404 + headers: + Content-Type: text/xml;charset=UTF-8 + Server: Jetty(7.6.0.v20120127) + X-Rundeck-API-Version: '11' + body: "\n \n property does not exist: monkey-burrito\n \n" diff --git a/src/test/resources/betamax/tapes/get_project_config_keyed_dne_v11.yaml b/src/test/resources/betamax/tapes/get_project_config_keyed_dne_v11.yaml new file mode 100644 index 0000000..d17090c --- /dev/null +++ b/src/test/resources/betamax/tapes/get_project_config_keyed_dne_v11.yaml @@ -0,0 +1,22 @@ +!tape +name: get_project_config_keyed_dne_v11 +interactions: +- recorded: 2014-03-07T20:19:47.533Z + request: + method: GET + uri: http://rundeck.local:4440/api/11/project/ABC/config/does-not-exist + headers: + Accept: text/xml + Host: rundeck.local:4440 + Proxy-Connection: Keep-Alive + User-Agent: RunDeck API Java Client 11 + X-RunDeck-Auth-Token: Do4d3NUD5DKk21DR4sNK755RcPk618vn + response: + status: 404 + headers: + Content-Type: text/xml;charset=UTF-8 + Expires: Thu, 01 Jan 1970 00:00:00 GMT + Server: Jetty(7.6.0.v20120127) + Set-Cookie: JSESSIONID=2367tnltmmec14cn79ps4fam9;Path=/ + X-Rundeck-API-Version: '11' + body: "\n \n property does not exist: does-not-exist\n \n" diff --git a/src/test/resources/betamax/tapes/get_project_config_keyedv11.yaml b/src/test/resources/betamax/tapes/get_project_config_keyedv11.yaml new file mode 100644 index 0000000..c0ba023 --- /dev/null +++ b/src/test/resources/betamax/tapes/get_project_config_keyedv11.yaml @@ -0,0 +1,24 @@ +!tape +name: get_project_config_keyedv11 +interactions: +- recorded: 2014-03-07T19:50:29.035Z + request: + method: GET + uri: http://rundeck.local:4440/api/11/project/ABC/config/project.name + headers: + Accept: text/xml + Host: rundeck.local:4440 + Proxy-Connection: Keep-Alive + 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=jgign9nyxeyp4istq65l86lp;Path=/ + X-Rundeck-API-Version: '11' + X-Rundeck-API-XML-Response-Wrapper: 'false' + body: !!binary |- + PHByb3BlcnR5IGtleT0ncHJvamVjdC5uYW1lJyB2YWx1ZT0nQUJDJyAvPg== diff --git a/src/test/resources/betamax/tapes/set_project_config_keyedv11.yaml b/src/test/resources/betamax/tapes/set_project_config_keyedv11.yaml new file mode 100644 index 0000000..88cfd10 --- /dev/null +++ b/src/test/resources/betamax/tapes/set_project_config_keyedv11.yaml @@ -0,0 +1,26 @@ +!tape +name: set_project_config_keyedv11 +interactions: +- recorded: 2014-03-07T19:59:51.009Z + request: + method: PUT + uri: http://rundeck.local:4440/api/11/project/ABC/config/monkey-burrito + 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=3ssp8chdwsuw16hihk5frgpzy;Path=/ + X-Rundeck-API-Version: '11' + X-Rundeck-API-XML-Response-Wrapper: 'false' + body: !!binary |- + PHByb3BlcnR5IGtleT0nbW9ua2V5LWJ1cnJpdG8nIHZhbHVlPSdsZW1vbiBwaWUnIC8+