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+