Merge "Allow Jira REST API to be located outside of the server root"
diff --git a/BUILD b/BUILD
index 8762bf6..c22e753 100644
--- a/BUILD
+++ b/BUILD
@@ -1,9 +1,14 @@
-load("//tools/bzl:plugin.bzl", "gerrit_plugin")
+load("//tools/bzl:junit.bzl", "junit_tests")
+load(
+ "//tools/bzl:plugin.bzl",
+ "gerrit_plugin",
+ "PLUGIN_DEPS",
+ "PLUGIN_TEST_DEPS",
+)
gerrit_plugin(
name = "its-jira",
srcs = glob(["src/main/java/**/*.java"]),
- resources = glob(["src/main/resources/**/*"]),
manifest_entries = [
"Gerrit-PluginName: its-jira",
"Gerrit-Module: com.googlesource.gerrit.plugins.its.jira.JiraModule",
@@ -12,8 +17,31 @@
"Implementation-Title: Jira ITS Plugin",
"Implementation-URL: http://www.gerritforge.com",
],
+ resources = glob(["src/main/resources/**/*"]),
deps = [
"//plugins/its-base",
],
)
+junit_tests(
+ name = "its_jira_tests",
+ testonly = 1,
+ srcs = glob(
+ ["src/test/java/**/*.java"],
+ ),
+ tags = ["its-jira"],
+ deps = [
+ "its-jira__plugin_test_deps",
+ ],
+)
+
+java_library(
+ name = "its-jira__plugin_test_deps",
+ testonly = 1,
+ visibility = ["//visibility:public"],
+ exports = PLUGIN_DEPS + PLUGIN_TEST_DEPS + [
+ ":its-jira__plugin",
+ "//plugins/its-base",
+ "@mockito//jar",
+ ],
+)
diff --git a/external_plugin_deps.bzl b/external_plugin_deps.bzl
new file mode 100644
index 0000000..2709df3
--- /dev/null
+++ b/external_plugin_deps.bzl
@@ -0,0 +1,25 @@
+load("//tools/bzl:maven_jar.bzl", "maven_jar")
+
+def external_plugin_deps():
+ maven_jar(
+ name = "mockito",
+ artifact = "org.mockito:mockito-core:2.13.0",
+ sha1 = "8e372943974e4a121fb8617baced8ebfe46d54f0",
+ deps = [
+ '@byte-buddy//jar',
+ '@objenesis//jar',
+ ],
+ )
+
+ maven_jar(
+ name = "byte-buddy",
+ artifact = "net.bytebuddy:byte-buddy:1.7.9",
+ sha1 = "51218a01a882c04d0aba8c028179cce488bbcb58",
+ )
+
+ maven_jar(
+ name = "objenesis",
+ artifact = "org.objenesis:objenesis:2.6",
+ sha1 = "639033469776fd37c08358c6b92a4761feb2af4b",
+ )
+
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/jira/JiraClient.java b/src/main/java/com/googlesource/gerrit/plugins/its/jira/JiraClient.java
index ab1ca48..72c5acc 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/its/jira/JiraClient.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/jira/JiraClient.java
@@ -84,7 +84,7 @@
public List<JiraTransition.Item> getTransitions(String issueKey) throws IOException {
JiraRestApi<JiraTransition> api = apiBuilder.get(JiraTransition.class, "/issue");
- return Arrays.asList(api.doGet("/" + issueKey + "/transitions", HTTP_OK).transitions);
+ return Arrays.asList(api.doGet("/" + issueKey + "/transitions", HTTP_OK).getTransitions());
}
/**
@@ -144,4 +144,25 @@
}
return null;
}
+
+ public String healthCheckAccess() throws IOException {
+ sysInfo();
+ String result = "{\"status\"=\"ok\"}";
+ log.debug("Health check on access result: {}", result);
+ return result;
+ }
+
+ public String healthCheckSysinfo() throws IOException {
+ JiraServerInfo info = sysInfo();
+ String result =
+ "{\"status\"=\"ok\",\"system\"=\"Jira\",\"version\"=\""
+ + info.getVersion()
+ + "\",\"url\"=\""
+ + info.getBaseUri()
+ + "\",\"build\"=\""
+ + info.getBuildNumber()
+ + "\"}";
+ log.debug("Health check on sysinfo result: {}", result);
+ return result;
+ }
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/jira/JiraConfig.java b/src/main/java/com/googlesource/gerrit/plugins/its/jira/JiraConfig.java
new file mode 100644
index 0000000..b757b65
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/jira/JiraConfig.java
@@ -0,0 +1,79 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License"),
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.googlesource.gerrit.plugins.its.jira;
+
+import static java.lang.String.format;
+
+import com.google.gerrit.extensions.annotations.PluginName;
+import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import org.eclipse.jgit.lib.Config;
+
+/** The JIRA plugin configuration as read from Gerrit config. */
+@Singleton
+public class JiraConfig {
+ static final String ERROR_MSG = "Unable to load plugin %s. Cause: Wrong configuration ";
+ static final String GERRIT_CONFIG_URL = "url";
+ static final String GERRIT_CONFIG_USERNAME = "username";
+ static final String GERRIT_CONFIG_PASSWORD = "password";
+
+ private final String jiraUrl;
+ private final String jiraUsername;
+ private final String jiraPassword;
+
+ /**
+ * Builds an JiraConfig.
+ *
+ * @param config the gerrit server config
+ * @param pluginName the name of this very plugin
+ */
+ @Inject
+ JiraConfig(@GerritServerConfig Config config, @PluginName String pluginName) {
+ jiraUrl = config.getString(pluginName, null, GERRIT_CONFIG_URL);
+ jiraUsername = config.getString(pluginName, null, GERRIT_CONFIG_USERNAME);
+ jiraPassword = config.getString(pluginName, null, GERRIT_CONFIG_PASSWORD);
+ if (jiraUrl == null || jiraUsername == null || jiraPassword == null) {
+ throw new RuntimeException(format(ERROR_MSG, pluginName));
+ }
+ }
+
+ /**
+ * The Jira url to connect to.
+ *
+ * @return the jira url
+ */
+ public String getJiraUrl() {
+ return jiraUrl;
+ }
+
+ /**
+ * The username to connect to a Jira server.
+ *
+ * @return the username
+ */
+ public String getUsername() {
+ return jiraUsername;
+ }
+
+ /**
+ * The password to connect to a Jira server.
+ *
+ * @return the password
+ */
+ public String getPassword() {
+ return jiraPassword;
+ }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/jira/JiraItsFacade.java b/src/main/java/com/googlesource/gerrit/plugins/its/jira/JiraItsFacade.java
index cb20458..790cefd 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/its/jira/JiraItsFacade.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/jira/JiraItsFacade.java
@@ -14,8 +14,6 @@
package com.googlesource.gerrit.plugins.its.jira;
-import com.google.gerrit.extensions.annotations.PluginName;
-import com.google.gerrit.server.config.GerritServerConfig;
import com.google.inject.Inject;
import com.googlesource.gerrit.plugins.its.base.its.InvalidTransitionException;
import com.googlesource.gerrit.plugins.its.base.its.ItsFacade;
@@ -25,30 +23,23 @@
import java.net.MalformedURLException;
import java.net.URL;
import java.util.concurrent.Callable;
-import org.eclipse.jgit.lib.Config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class JiraItsFacade implements ItsFacade {
- private static final String GERRIT_CONFIG_USERNAME = "username";
- private static final String GERRIT_CONFIG_PASSWORD = "password";
- private static final String GERRIT_CONFIG_URL = "url";
-
private static final int MAX_ATTEMPTS = 3;
private Logger log = LoggerFactory.getLogger(JiraItsFacade.class);
- private final String pluginName;
- private Config gerritConfig;
+ private final JiraConfig jiraConfig;
private JiraClient client;
@Inject
- public JiraItsFacade(@PluginName String pluginName, @GerritServerConfig Config cfg) {
- this.pluginName = pluginName;
+ public JiraItsFacade(JiraConfig jiraConfig) {
+ this.jiraConfig = jiraConfig;
try {
- this.gerritConfig = cfg;
JiraServerInfo info = client().sysInfo();
log.info(
"Connected to JIRA at {}, reported version is {}", info.getBaseUri(), info.getVersion());
@@ -61,17 +52,19 @@
}
@Override
- public String healthCheck(final Check check) throws IOException {
+ public String healthCheck(Check check) throws IOException {
return execute(
() -> {
- if (check.equals(Check.ACCESS)) return healthCheckAccess();
- return healthCheckSysinfo();
+ if (check.equals(Check.ACCESS)) {
+ return client().healthCheckAccess();
+ }
+ return client().healthCheckSysinfo();
});
}
@Override
- public void addComment(final String issueKey, final String comment) throws IOException {
+ public void addComment(String issueKey, String comment) throws IOException {
execute(
() -> {
@@ -83,14 +76,14 @@
}
@Override
- public void addRelatedLink(final String issueKey, final URL relatedUrl, String description)
+ public void addRelatedLink(String issueKey, URL relatedUrl, String description)
throws IOException {
addComment(
issueKey, "Related URL: " + createLinkForWebui(relatedUrl.toExternalForm(), description));
}
@Override
- public void performAction(final String issueKey, final String actionName) throws IOException {
+ public void performAction(String issueKey, String actionName) throws IOException {
execute(
() -> {
@@ -100,7 +93,7 @@
});
}
- private void doPerformAction(final String issueKey, final String actionName)
+ private void doPerformAction(String issueKey, String actionName)
throws IOException, InvalidTransitionException {
log.debug("Trying to perform action: {} on issue {}", actionName, issueKey);
boolean ret = client().doTransition(issueKey, actionName);
@@ -112,15 +105,17 @@
}
@Override
- public boolean exists(final String issueKey) throws IOException {
+ public boolean exists(String issueKey) throws IOException {
return execute(() -> client().issueExists(issueKey));
}
private JiraClient client() throws MalformedURLException {
if (client == null) {
- log.debug("Connecting to jira at {}", getUrl());
- client = new JiraClient(getUrl(), getUsername(), getPassword());
- log.debug("Authenticating as User {}", getUsername());
+ log.debug("Connecting to jira at {}", jiraConfig.getJiraUrl());
+ client =
+ new JiraClient(
+ jiraConfig.getJiraUrl(), jiraConfig.getUsername(), jiraConfig.getPassword());
+ log.debug("Authenticating as User {}", jiraConfig.getUsername());
}
return client;
}
@@ -146,41 +141,8 @@
return className.startsWith("java.net");
}
- private String getPassword() {
- return gerritConfig.getString(pluginName, null, GERRIT_CONFIG_PASSWORD);
- }
-
- private String getUsername() {
- return gerritConfig.getString(pluginName, null, GERRIT_CONFIG_USERNAME);
- }
-
- private String getUrl() {
- return gerritConfig.getString(pluginName, null, GERRIT_CONFIG_URL);
- }
-
@Override
public String createLinkForWebui(String url, String text) {
return "[" + text + "|" + url + "]";
}
-
- private String healthCheckAccess() throws IOException {
- client().sysInfo();
- final String result = "{\"status\"=\"ok\",\"username\"=\"" + getUsername() + "\"}";
- log.debug("Health check on access result: {}", result);
- return result;
- }
-
- private String healthCheckSysinfo() throws IOException {
- JiraServerInfo info = client().sysInfo();
- final String result =
- "{\"status\"=\"ok\",\"system\"=\"Jira\",\"version\"=\""
- + info.getVersion()
- + "\",\"url\"=\""
- + getUrl()
- + "\",\"build\"=\""
- + info.getBuildNumber()
- + "\"}";
- log.debug("Health check on sysinfo result: {}", result);
- return result;
- }
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/jira/JiraModule.java b/src/main/java/com/googlesource/gerrit/plugins/its/jira/JiraModule.java
index 2b1119e..ac3889a 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/its/jira/JiraModule.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/jira/JiraModule.java
@@ -47,7 +47,7 @@
protected void configure() {
if (gerritConfig.getString(pluginName, null, "url") != null) {
LOG.info("JIRA is configured as ITS");
- bind(ItsFacade.class).toInstance(new JiraItsFacade(pluginName, gerritConfig));
+ bind(ItsFacade.class).to(JiraItsFacade.class).asEagerSingleton();
install(new ItsHookModule(pluginName, pluginCfgFactory));
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/jira/restapi/JiraComment.java b/src/main/java/com/googlesource/gerrit/plugins/its/jira/restapi/JiraComment.java
index e3e2f0e..eb2755d 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/its/jira/restapi/JiraComment.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/jira/restapi/JiraComment.java
@@ -16,9 +16,13 @@
public class JiraComment {
- final String body;
+ private final String body;
public JiraComment(String body) {
this.body = body;
}
+
+ public String getBody() {
+ return body;
+ }
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/jira/restapi/JiraIssue.java b/src/main/java/com/googlesource/gerrit/plugins/its/jira/restapi/JiraIssue.java
index 8a22098..0eab61b 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/its/jira/restapi/JiraIssue.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/jira/restapi/JiraIssue.java
@@ -15,6 +15,19 @@
package com.googlesource.gerrit.plugins.its.jira.restapi;
public class JiraIssue {
- String id;
- String key;
+ private final String id;
+ private final String key;
+
+ public JiraIssue(String id, String key) {
+ this.id = id;
+ this.key = key;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public String getKey() {
+ return key;
+ }
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/jira/restapi/JiraProject.java b/src/main/java/com/googlesource/gerrit/plugins/its/jira/restapi/JiraProject.java
index d244cb4..fb4b3be 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/its/jira/restapi/JiraProject.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/jira/restapi/JiraProject.java
@@ -16,9 +16,15 @@
public class JiraProject {
- String id;
- String key;
- String name;
+ private final String id;
+ private final String key;
+ private final String name;
+
+ public JiraProject(String id, String key, String name) {
+ this.id = id;
+ this.key = key;
+ this.name = name;
+ }
public String getId() {
return id;
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/jira/restapi/JiraTransition.java b/src/main/java/com/googlesource/gerrit/plugins/its/jira/restapi/JiraTransition.java
index 4bbad2c..b541217 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/its/jira/restapi/JiraTransition.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/jira/restapi/JiraTransition.java
@@ -17,18 +17,35 @@
public class JiraTransition {
// 'Get Transactions' require a list of items
- public Item[] transitions;
+ private Item[] transitions;
// 'Do Transaction' require a single item
- Item transition;
+ private Item transition;
+
+ public JiraTransition(Item[] transitions) {
+ this.transitions = transitions;
+ }
public JiraTransition(Item transition) {
this.transition = transition;
}
+ public Item[] getTransitions() {
+ return transitions;
+ }
+
+ public Item getTransition() {
+ return transition;
+ }
+
public static class Item {
- String name;
- String id;
+ private final String name;
+ private final String id;
+
+ public Item(String name, String id) {
+ this.name = name;
+ this.id = id;
+ }
public String getName() {
return name;
diff --git a/src/main/resources/Documentation/build.md b/src/main/resources/Documentation/build.md
index 1235321..4e44633 100644
--- a/src/main/resources/Documentation/build.md
+++ b/src/main/resources/Documentation/build.md
@@ -7,6 +7,14 @@
[plugins/its-base](https://gerrit-review.googlesource.com/#/admin/projects/plugins/its-base)
to the `plugins` directory of Gerrit's source tree.
+Put the external dependency Bazel build file into the Gerrit plugins directory,
+replacing the existing empty one.
+
+```
+ cd gerrit/plugins
+ ln -sf @PLUGIN@/external_plugin_deps.bzl .
+```
+
Then issue
```
@@ -22,13 +30,20 @@
```
This project can be imported into the Eclipse IDE.
-Add the plugin name to the `CUSTOM_PLUGINS` set in
-Gerrit core in `tools/bzl/plugins.bzl`, and execute:
+Add the plugin name to the `CUSTOM_PLUGINS` and
+`CUSTOM_PLUGINS_TEST_DEPS` sets in the file
+`<gerrit_source_code>/tools/bzl/plugins.bzl` and execute:
```
./tools/eclipse/project.py
```
+To execute the tests run:
+
+```
+ bazel test plugins/@PLUGIN@:its_jira_tests
+```
+
[Back to @PLUGIN@ documentation index][index]
[index]: index.html
\ No newline at end of file
diff --git a/src/main/resources/Documentation/config.md b/src/main/resources/Documentation/config.md
index 3eff578..5864b75 100644
--- a/src/main/resources/Documentation/config.md
+++ b/src/main/resources/Documentation/config.md
@@ -134,3 +134,37 @@
optional
Issue-id enforced in commit message [MANDATORY/?]: suggested
+**Sample actions-@Plugin@.config:**
+
+ [rule "open"]
+ event-type = patchset-created
+ action = add-velocity-comment inline Change ${its.formatLink($changeUrl)} is created.
+ action = In Progress
+ [rule "resolve"]
+ event-type = comment-added
+ approval-Code-Review = 2
+ action = add-velocity-comment inline Change ${its.formatLink($changeUrl)} is verified.
+ action = In Review
+ [rule "merged"]
+ event-type = change-merged
+ action = add-velocity-comment inline Change ${its.formatLink($changeUrl)} is merged.
+ action = Done
+ [rule "abandoned"]
+ event-type = change-abandoned'
+ action = add-velocity-comment inline Change ${its.formatLink($changeUrl)} is abandoned.
+ action = To Do
+
+The first rule triggers an action which adds a comment and a hyperlink to the change created
+in gerrit. The comment will appear in an Jira issue's `Comment` section whenever a patchset-created event
+is triggered. The second action item in the first rule transitions the state of the issue
+in Jira to `In Progress`. The title of the action `In Progress` should match the workflow actions
+used by the JIRA server as different versions of JIRA can have different workflow actions.
+
+**Note:** Velocity comments were deprecated in Gerrit 2.14 and will be removed in Gerrit 2.16/3.0;
+the `actions-@Plugin@.config` needs to be changed accordingly. For example, to use Soy comments instead
+of velocity comments:
+
+ [rule "open"]
+ event-type = patchset-created
+ action = add-soy-comment Change ${its.formatLink($changeUrl)} is created.
+ action = In Progress
diff --git a/src/test/java/com/googlesource/gerrit/plugins/its/jira/JiraConfigTest.java b/src/test/java/com/googlesource/gerrit/plugins/its/jira/JiraConfigTest.java
new file mode 100644
index 0000000..f7f5a40
--- /dev/null
+++ b/src/test/java/com/googlesource/gerrit/plugins/its/jira/JiraConfigTest.java
@@ -0,0 +1,63 @@
+// Copyright (C) 2018 Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License"),
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.googlesource.gerrit.plugins.its.jira;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.googlesource.gerrit.plugins.its.jira.JiraConfig.ERROR_MSG;
+import static com.googlesource.gerrit.plugins.its.jira.JiraConfig.GERRIT_CONFIG_PASSWORD;
+import static com.googlesource.gerrit.plugins.its.jira.JiraConfig.GERRIT_CONFIG_URL;
+import static com.googlesource.gerrit.plugins.its.jira.JiraConfig.GERRIT_CONFIG_USERNAME;
+import static java.lang.String.format;
+import static org.mockito.Mockito.when;
+
+import org.eclipse.jgit.lib.Config;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+public class JiraConfigTest {
+
+ private static final String PASS = "pass";
+ private static final String URL = "http://jira_example.com";
+ private static final String USER = "user";
+ private static final String PLUGIN_NAME = "its-jira";
+
+ @Rule public ExpectedException thrown = ExpectedException.none();
+ @Mock private Config cfg;
+
+ private JiraConfig jiraConfig;
+
+ @Test
+ public void gerritConfigContainsSaneValues() throws Exception {
+ when(cfg.getString(PLUGIN_NAME, null, GERRIT_CONFIG_URL)).thenReturn(URL);
+ when(cfg.getString(PLUGIN_NAME, null, GERRIT_CONFIG_USERNAME)).thenReturn(USER);
+ when(cfg.getString(PLUGIN_NAME, null, GERRIT_CONFIG_PASSWORD)).thenReturn(PASS);
+ jiraConfig = new JiraConfig(cfg, PLUGIN_NAME);
+ assertThat(jiraConfig.getUsername()).isEqualTo(USER);
+ assertThat(jiraConfig.getPassword()).isEqualTo(PASS);
+ assertThat(jiraConfig.getJiraUrl()).isEqualTo(URL);
+ }
+
+ @Test
+ public void gerritConfigContainsNullValues() throws Exception {
+ thrown.expect(RuntimeException.class);
+ thrown.expectMessage(format(ERROR_MSG, PLUGIN_NAME));
+ jiraConfig = new JiraConfig(cfg, PLUGIN_NAME);
+ }
+}