Merge branch 'stable-2.16'
* stable-2.16:
Support multiple Jira instances
Change-Id: Ibd8ba989f4ffca8479e0b1c96528feaeefd9c2ff
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 5fd69ba..ab6cba2 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
@@ -53,9 +53,8 @@
* @param issueKey Jira Issue key
* @return true if issue exists
*/
- public boolean issueExists(String issueKey) throws IOException {
- JiraRestApi<JiraIssue> api = apiBuilder.getIssue();
-
+ public boolean issueExists(JiraItsServerInfo server, String issueKey) throws IOException {
+ JiraRestApi<JiraIssue> api = apiBuilder.getIssue(server);
api.doGet(issueKey, HTTP_OK, new int[] {HTTP_NOT_FOUND, HTTP_FORBIDDEN});
Integer code = api.getResponseCode();
switch (code) {
@@ -78,9 +77,9 @@
* @return Iterable of available transitions
* @throws IOException
*/
- public List<JiraTransition.Item> getTransitions(String issueKey) throws IOException {
-
- JiraRestApi<JiraTransition> api = apiBuilder.get(JiraTransition.class, "/issue");
+ public List<JiraTransition.Item> getTransitions(JiraItsServerInfo server, String issueKey)
+ throws IOException {
+ JiraRestApi<JiraTransition> api = apiBuilder.get(server, JiraTransition.class, "/issue");
return Arrays.asList(api.doGet(issueKey + "/transitions", HTTP_OK).getTransitions());
}
@@ -89,12 +88,13 @@
* @param comment String to be added
* @throws IOException
*/
- public void addComment(String issueKey, String comment) throws IOException {
+ public void addComment(JiraItsServerInfo server, String issueKey, String comment)
+ throws IOException {
- if (issueExists(issueKey)) {
+ if (issueExists(server, issueKey)) {
log.debug("Trying to add comment for issue {}", issueKey);
apiBuilder
- .getIssue()
+ .getIssue(server)
.doPost(issueKey + "/comment", gson.toJson(new JiraComment(comment)), HTTP_CREATED);
log.debug("Comment added to issue {}", issueKey);
} else {
@@ -114,33 +114,33 @@
* @param transition JiraTransition.Item to perform
* @return true if successful
*/
- public boolean doTransition(String issueKey, String transition)
+ public boolean doTransition(JiraItsServerInfo server, String issueKey, String transition)
throws IOException, InvalidTransitionException {
log.debug("Making transition to {} for {}", transition, issueKey);
- JiraTransition.Item t = getTransitionByName(issueKey, transition);
+ JiraTransition.Item t = getTransitionByName(server, issueKey, transition);
if (t == null) {
throw new InvalidTransitionException(
"Action " + transition + " not executable on issue " + issueKey);
}
log.debug("Transition issue {} to '{}' ({})", issueKey, transition, t.getId());
return apiBuilder
- .getIssue()
+ .getIssue(server)
.doPost(issueKey + "/transitions", gson.toJson(new JiraTransition(t)), HTTP_NO_CONTENT);
}
/** @return Serverinformation of jira */
- public JiraServerInfo sysInfo() throws IOException {
- return apiBuilder.getServerInfo().doGet("", HTTP_OK);
+ public JiraServerInfo sysInfo(JiraItsServerInfo server) throws IOException {
+ return apiBuilder.getServerInfo(server).doGet("", HTTP_OK);
}
/** @return List of all projects we have access to in jira */
- public JiraProject[] getProjects() throws IOException {
- return apiBuilder.getProjects().doGet("", HTTP_OK);
+ public JiraProject[] getProjects(JiraItsServerInfo server) throws IOException {
+ return apiBuilder.getProjects(server).doGet("", HTTP_OK);
}
- private JiraTransition.Item getTransitionByName(String issueKey, String transition)
- throws IOException {
- for (JiraTransition.Item t : getTransitions(issueKey)) {
+ private JiraTransition.Item getTransitionByName(
+ JiraItsServerInfo server, String issueKey, String transition) throws IOException {
+ for (JiraTransition.Item t : getTransitions(server, issueKey)) {
if (transition.equals(t.getName())) {
return t;
}
@@ -148,15 +148,15 @@
return null;
}
- public String healthCheckAccess() throws IOException {
- sysInfo();
+ public String healthCheckAccess(JiraItsServerInfo server) throws IOException {
+ sysInfo(server);
String result = "{\"status\"=\"ok\"}";
log.debug("Health check on access result: {}", result);
return result;
}
- public String healthCheckSysinfo() throws IOException {
- JiraServerInfo info = sysInfo();
+ public String healthCheckSysinfo(JiraItsServerInfo server) throws IOException {
+ JiraServerInfo info = sysInfo(server);
String result =
"{\"status\"=\"ok\",\"system\"=\"Jira\",\"version\"=\""
+ info.getVersion()
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
index a39e059..5b4e197 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/its/jira/JiraConfig.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/jira/JiraConfig.java
@@ -14,73 +14,135 @@
package com.googlesource.gerrit.plugins.its.jira;
-import static java.lang.String.format;
-
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.CharMatcher;
+import com.google.common.base.Strings;
import com.google.gerrit.extensions.annotations.PluginName;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.gerrit.server.config.PluginConfig;
+import com.google.gerrit.server.config.PluginConfigFactory;
+import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.git.meta.MetaDataUpdate;
+import com.google.gerrit.server.project.CommentLinkInfoImpl;
+import com.google.gerrit.server.project.NoSuchProjectException;
+import com.google.gerrit.server.project.ProjectCache;
+import com.google.gerrit.server.project.ProjectConfig;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.googlesource.gerrit.plugins.its.jira.restapi.JiraURL;
-import java.net.MalformedURLException;
+import java.io.IOException;
+import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.Repository;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
-/** The JIRA plugin configuration as read from Gerrit config. */
+/** The JIRA plugin configuration as read from project config. */
@Singleton
public class JiraConfig {
- static final String ERROR_MSG = "Unable to load plugin %s because of invalid configuration: %s";
- static final String GERRIT_CONFIG_URL = "url";
- static final String GERRIT_CONFIG_USERNAME = "username";
- static final String GERRIT_CONFIG_PASSWORD = "password";
+ static final String PROJECT_CONFIG_URL_KEY = "instanceUrl";
+ static final String PROJECT_CONFIG_USERNAME_KEY = "username";
+ static final String PROJECT_CONFIG_PASSWORD_KEY = "password";
- private final JiraURL jiraUrl;
- private final String jiraUsername;
- private final String jiraPassword;
+ private static final Logger log = LoggerFactory.getLogger(JiraConfig.class);
+ private static final String COMMENTLINK = "commentlink";
+ private static final String GERRIT_CONFIG_URL = "url";
+ private static final String GERRIT_CONFIG_USERNAME = "username";
+ private static final String GERRIT_CONFIG_PASSWORD = "password";
- /**
- * Builds an JiraConfig.
- *
- * @param config the gerrit server config
- * @param pluginName the name of this very plugin
- */
+ private final String pluginName;
+ private final PluginConfigFactory cfgFactory;
+ private final Config gerritConfig;
+ private final JiraItsServerInfo defaultJiraServerInfo;
+ private final GitRepositoryManager repoManager;
+ private final ProjectCache projectCache;
+ private final PersonIdent serverUser;
+
@Inject
- JiraConfig(@GerritServerConfig Config config, @PluginName String pluginName) {
- try {
- jiraUrl = new JiraURL(config.getString(pluginName, null, GERRIT_CONFIG_URL)).adjustUrlPath();
- } catch (MalformedURLException e) {
- throw new RuntimeException(format(ERROR_MSG, pluginName, e.getLocalizedMessage()));
- }
+ JiraConfig(
+ @GerritServerConfig Config config,
+ @PluginName String pluginName,
+ PluginConfigFactory cfgFactory,
+ @GerritPersonIdent PersonIdent serverUser,
+ ProjectCache projectCache,
+ GitRepositoryManager repoManager) {
+ this.gerritConfig = config;
+ this.pluginName = pluginName;
+ this.cfgFactory = cfgFactory;
+ this.serverUser = serverUser;
+ this.projectCache = projectCache;
+ this.repoManager = repoManager;
+ this.defaultJiraServerInfo = buildDefaultServerInfo(gerritConfig, pluginName);
+ }
- 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, "missing username/password"));
+ private static JiraItsServerInfo buildDefaultServerInfo(Config gerritConfig, String pluginName) {
+ return JiraItsServerInfo.builder()
+ .url(gerritConfig.getString(pluginName, null, GERRIT_CONFIG_URL))
+ .username(gerritConfig.getString(pluginName, null, GERRIT_CONFIG_USERNAME))
+ .password(gerritConfig.getString(pluginName, null, GERRIT_CONFIG_PASSWORD))
+ .build();
+ }
+
+ JiraItsServerInfo getDefaultServerInfo() {
+ return defaultJiraServerInfo;
+ }
+
+ String getCommentLinkFromGerritConfig(String key) {
+ return gerritConfig.getString(COMMENTLINK, pluginName, key);
+ }
+
+ JiraItsServerInfo getServerInfoFor(String projectName) {
+ PluginConfig pluginConfig = getPluginConfigFor(projectName);
+ return JiraItsServerInfo.builder()
+ .url(pluginConfig.getString(PROJECT_CONFIG_URL_KEY, null))
+ .username(pluginConfig.getString(PROJECT_CONFIG_USERNAME_KEY, null))
+ .password(pluginConfig.getString(PROJECT_CONFIG_PASSWORD_KEY, null))
+ .build();
+ }
+
+ void addCommentLinksSection(Project.NameKey projectName, JiraItsServerInfo jiraItsServerInfo) {
+ try (Repository git = repoManager.openRepository(projectName);
+ MetaDataUpdate md = new MetaDataUpdate(GitReferenceUpdated.DISABLED, projectName, git)) {
+ ProjectConfig config = ProjectConfig.read(md);
+ String link =
+ CharMatcher.is('/').trimFrom(jiraItsServerInfo.getUrl().toString()) + JiraURL.URL_SUFFIX;
+ if (!commentLinksExist(config, link)) {
+ String match = getCommentLinkFromGerritConfig("match");
+ CommentLinkInfoImpl commentlinkSection =
+ new CommentLinkInfoImpl(pluginName, match, link, null, true);
+ config.addCommentLinkSection(commentlinkSection);
+ md.getCommitBuilder().setAuthor(serverUser);
+ md.getCommitBuilder().setCommitter(serverUser);
+ projectCache.evict(config.getProject());
+ config.commit(md);
+ }
+ } catch (ConfigInvalidException | IOException e) {
+ throw new RuntimeException(e);
}
}
- /**
- * The Jira url to connect to.
- *
- * @return the jira url
- */
- public JiraURL getJiraUrl() {
- return jiraUrl;
+ private boolean commentLinksExist(ProjectConfig config, String link) {
+ return config.getCommentLinkSections().stream().map(c -> c.link).anyMatch(link::equals);
}
- /**
- * 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;
+ @VisibleForTesting
+ PluginConfig getPluginConfigFor(String projectName) {
+ if (!Strings.isNullOrEmpty(projectName)) {
+ try {
+ return cfgFactory.getFromProjectConfigWithInheritance(
+ new Project.NameKey(projectName), pluginName);
+ } catch (NoSuchProjectException e) {
+ log.warn(
+ "Unable to get project configuration for {}: project '{}' not found ",
+ pluginName,
+ projectName,
+ e);
+ }
+ }
+ return new PluginConfig(pluginName, new Config());
}
}
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 4d066d5..4480c08 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
@@ -17,8 +17,6 @@
import com.google.inject.Inject;
import com.googlesource.gerrit.plugins.its.base.its.InvalidTransitionException;
import com.googlesource.gerrit.plugins.its.base.its.ItsFacade;
-import com.googlesource.gerrit.plugins.its.jira.restapi.JiraProject;
-import com.googlesource.gerrit.plugins.its.jira.restapi.JiraServerInfo;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.Callable;
@@ -33,19 +31,11 @@
private final JiraClient jiraClient;
+ private JiraItsServerInfo itsServerInfo;
+
@Inject
public JiraItsFacade(JiraClient jiraClient) {
this.jiraClient = jiraClient;
- try {
- JiraServerInfo info = this.jiraClient.sysInfo();
- log.info(
- "Connected to JIRA at {}, reported version is {}", info.getBaseUri(), info.getVersion());
- for (JiraProject p : this.jiraClient.getProjects()) {
- log.info("Found project: {} (key: {})", p.getName(), p.getKey());
- }
- } catch (Exception ex) {
- log.warn("Jira is currently not available", ex);
- }
}
@Override
@@ -54,19 +44,18 @@
return execute(
() -> {
if (check.equals(Check.ACCESS)) {
- return jiraClient.healthCheckAccess();
+ return jiraClient.healthCheckAccess(getJiraServerInstance());
}
- return jiraClient.healthCheckSysinfo();
+ return jiraClient.healthCheckSysinfo(getJiraServerInstance());
});
}
@Override
public void addComment(String issueKey, String comment) throws IOException {
-
execute(
() -> {
log.debug("Adding comment {} to issue {}", comment, issueKey);
- jiraClient.addComment(issueKey, comment);
+ jiraClient.addComment(getJiraServerInstance(), issueKey, comment);
log.debug("Added comment {} to issue {}", comment, issueKey);
return issueKey;
});
@@ -81,7 +70,6 @@
@Override
public void performAction(String issueKey, String actionName) throws IOException {
-
execute(
() -> {
log.debug("Performing action {} on issue {}", actionName, issueKey);
@@ -93,7 +81,7 @@
private void doPerformAction(String issueKey, String actionName)
throws IOException, InvalidTransitionException {
log.debug("Trying to perform action: {} on issue {}", actionName, issueKey);
- boolean ret = jiraClient.doTransition(issueKey, actionName);
+ boolean ret = jiraClient.doTransition(getJiraServerInstance(), issueKey, actionName);
if (ret) {
log.debug("Action {} successful on Issue {}", actionName, issueKey);
} else {
@@ -113,7 +101,15 @@
@Override
public boolean exists(String issueKey) throws IOException {
- return execute(() -> jiraClient.issueExists(issueKey));
+ return execute(() -> jiraClient.issueExists(getJiraServerInstance(), issueKey));
+ }
+
+ private JiraItsServerInfo getJiraServerInstance() {
+ return itsServerInfo;
+ }
+
+ public void setJiraServerInstance(JiraItsServerInfo server) {
+ itsServerInfo = server;
}
private <P> P execute(Callable<P> function) throws IOException {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/jira/JiraItsServer.java b/src/main/java/com/googlesource/gerrit/plugins/its/jira/JiraItsServer.java
new file mode 100644
index 0000000..0a698aa
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/jira/JiraItsServer.java
@@ -0,0 +1,72 @@
+// 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 com.google.gerrit.reviewdb.client.Project;
+import com.google.inject.Inject;
+import com.googlesource.gerrit.plugins.its.base.its.ItsFacadeFactory;
+
+/**
+ * Provides information about the single/current server configured. The information is tunneled back
+ * to its-base to perform the its-actions.
+ */
+public class JiraItsServer implements ItsFacadeFactory {
+ private final JiraConfig jiraConfig;
+ private final JiraItsFacade itsFacade;
+ private final JiraItsServerCache serverCache;
+
+ @Inject
+ public JiraItsServer(
+ JiraConfig jiraConfig, JiraItsFacade itsFacade, JiraItsServerCache serverCache) {
+ this.jiraConfig = jiraConfig;
+ this.itsFacade = itsFacade;
+ this.serverCache = serverCache;
+ }
+
+ /**
+ * Gets the server configuration from project.config. If the project config values are valid, it
+ * creates a commentlinks section for "its-jira" in the project config. Returns default
+ * configuration values from gerrit.config if no project config was provided. In case of invalid
+ * project config, its-jira tells the user that it is not able to connect.
+ *
+ * @param projectName the oroject for which the Jira server configuration should be returned
+ * @return the Jira server configuration for the project or the default Jira server configuration
+ * if the project does not define a project-level Jira configuration
+ */
+ @Override
+ public JiraItsFacade getFacade(Project.NameKey projectName) {
+ JiraItsServerInfo jiraItsServerInfo = serverCache.get(projectName.get());
+ if (jiraItsServerInfo.isValid()) {
+ jiraConfig.addCommentLinksSection(projectName, jiraItsServerInfo);
+ } else {
+ jiraItsServerInfo = jiraConfig.getDefaultServerInfo();
+ }
+
+ if (!jiraItsServerInfo.isValid()) {
+ throw new RuntimeException(
+ String.format(
+ "No valid Jira server configuration was found for project '%s' %n."
+ + "Missing one or more configuration values: url: %s, username: %s, password: %s",
+ projectName.get(),
+ jiraItsServerInfo.getUrl(),
+ jiraItsServerInfo.getUsername(),
+ jiraItsServerInfo.getPassword()));
+ }
+
+ itsFacade.setJiraServerInstance(jiraItsServerInfo);
+ return itsFacade;
+ }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/jira/JiraItsServerCache.java b/src/main/java/com/googlesource/gerrit/plugins/its/jira/JiraItsServerCache.java
new file mode 100644
index 0000000..23e7310
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/jira/JiraItsServerCache.java
@@ -0,0 +1,34 @@
+// 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;
+
+/** Cache of project-specific Jira servers */
+interface JiraItsServerCache {
+
+ /**
+ * Get the cached Jira server for a project
+ *
+ * @param projectName name of the project.
+ * @return the cached Jira server.
+ */
+ JiraItsServerInfo get(String projectName);
+
+ /**
+ * Invalidate the cached Jira server for the given project.
+ *
+ * @param projectName project for which the Jira server is being evicted.
+ */
+ void evict(String projectName);
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/jira/JiraItsServerCacheImpl.java b/src/main/java/com/googlesource/gerrit/plugins/its/jira/JiraItsServerCacheImpl.java
new file mode 100644
index 0000000..1e2c43b
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/jira/JiraItsServerCacheImpl.java
@@ -0,0 +1,81 @@
+// 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 com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import com.google.gerrit.extensions.events.GitReferenceUpdatedListener;
+import com.google.gerrit.extensions.registration.DynamicSet;
+import com.google.gerrit.server.cache.CacheModule;
+import com.google.inject.Inject;
+import com.google.inject.Module;
+import com.google.inject.name.Named;
+import java.util.concurrent.ExecutionException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+class JiraItsServerCacheImpl implements JiraItsServerCache {
+ private static final Logger log = LoggerFactory.getLogger(JiraItsServerCacheImpl.class);
+ private static final String CACHE_NAME = "jira_server_project";
+
+ private final LoadingCache<String, JiraItsServerInfo> cache;
+
+ @Inject
+ JiraItsServerCacheImpl(@Named(CACHE_NAME) LoadingCache<String, JiraItsServerInfo> cache) {
+ this.cache = cache;
+ }
+
+ @Override
+ public JiraItsServerInfo get(String projectName) {
+ try {
+ return cache.get(projectName);
+ } catch (ExecutionException e) {
+ log.warn("Cannot get project specific rules for project {}", projectName, e);
+ return JiraItsServerInfo.builder().url(null).username(null).password(null).build();
+ }
+ }
+
+ @Override
+ public void evict(String projectName) {
+ cache.invalidate(projectName);
+ }
+
+ public static Module module() {
+ return new CacheModule() {
+ @Override
+ protected void configure() {
+ cache(CACHE_NAME, String.class, JiraItsServerInfo.class).loader(Loader.class);
+ bind(JiraItsServerCacheImpl.class);
+ bind(JiraItsServerCache.class).to(JiraItsServerCacheImpl.class);
+ DynamicSet.bind(binder(), GitReferenceUpdatedListener.class)
+ .to(JiraItsServerProjectCacheRefresher.class);
+ }
+ };
+ }
+
+ static class Loader extends CacheLoader<String, JiraItsServerInfo> {
+ private final JiraConfig jiraConfig;
+
+ @Inject
+ Loader(JiraConfig jiraConfig) {
+ this.jiraConfig = jiraConfig;
+ }
+
+ @Override
+ public JiraItsServerInfo load(String projectName) {
+ return jiraConfig.getServerInfoFor(projectName);
+ }
+ }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/jira/JiraItsServerInfo.java b/src/main/java/com/googlesource/gerrit/plugins/its/jira/JiraItsServerInfo.java
new file mode 100644
index 0000000..b384189
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/jira/JiraItsServerInfo.java
@@ -0,0 +1,74 @@
+// 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 com.googlesource.gerrit.plugins.its.jira.restapi.JiraURL;
+import java.net.MalformedURLException;
+
+public class JiraItsServerInfo {
+ public static class Builder {
+ private JiraItsServerInfo instance = new JiraItsServerInfo();
+
+ private Builder() {}
+
+ public Builder url(String projectUrl) {
+ try {
+ instance.url = projectUrl != null ? new JiraURL(projectUrl) : null;
+ return this;
+ } catch (MalformedURLException e) {
+ throw new IllegalArgumentException("Unable to resolve URL", e);
+ }
+ }
+
+ public Builder username(String username) {
+ instance.username = username;
+ return this;
+ }
+
+ public Builder password(String password) {
+ instance.password = password;
+ return this;
+ }
+
+ public JiraItsServerInfo build() {
+ return instance;
+ }
+ }
+
+ private JiraURL url;
+ private String username;
+ private String password;
+
+ public static Builder builder() {
+ return new JiraItsServerInfo.Builder();
+ }
+
+ public JiraURL getUrl() {
+ return url;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public boolean isValid() {
+ return url != null && username != null && password != null;
+ }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/jira/JiraItsServerProjectCacheRefresher.java b/src/main/java/com/googlesource/gerrit/plugins/its/jira/JiraItsServerProjectCacheRefresher.java
new file mode 100644
index 0000000..def23ea
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/jira/JiraItsServerProjectCacheRefresher.java
@@ -0,0 +1,54 @@
+// 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 com.google.gerrit.extensions.api.GerritApi;
+import com.google.gerrit.extensions.common.ProjectInfo;
+import com.google.gerrit.extensions.events.GitReferenceUpdatedListener;
+import com.google.gerrit.extensions.restapi.RestApiException;
+import com.google.gerrit.reviewdb.client.RefNames;
+import com.google.inject.Inject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+class JiraItsServerProjectCacheRefresher implements GitReferenceUpdatedListener {
+ private static final Logger log =
+ LoggerFactory.getLogger(JiraItsServerProjectCacheRefresher.class);
+
+ private final GerritApi gApi;
+ private final JiraItsServerCache jiraItsServerCache;
+
+ @Inject
+ JiraItsServerProjectCacheRefresher(GerritApi gApi, JiraItsServerCache jiraItsServerCache) {
+ this.gApi = gApi;
+ this.jiraItsServerCache = jiraItsServerCache;
+ }
+
+ @Override
+ public void onGitReferenceUpdated(Event event) {
+ if (!event.getRefName().equals(RefNames.REFS_CONFIG)) {
+ return;
+ }
+ String project = event.getProjectName();
+ jiraItsServerCache.evict(project);
+ try {
+ for (ProjectInfo childProject : gApi.projects().name(project).children()) {
+ jiraItsServerCache.evict(childProject.name);
+ }
+ } catch (RestApiException e) {
+ log.warn("Unable to evict its-jira server cache", e);
+ }
+ }
+}
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 0a0a2dc..b9e98b3 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
@@ -14,16 +14,20 @@
package com.googlesource.gerrit.plugins.its.jira;
+import static com.googlesource.gerrit.plugins.its.jira.JiraConfig.PROJECT_CONFIG_PASSWORD_KEY;
+import static com.googlesource.gerrit.plugins.its.jira.JiraConfig.PROJECT_CONFIG_URL_KEY;
+import static com.googlesource.gerrit.plugins.its.jira.JiraConfig.PROJECT_CONFIG_USERNAME_KEY;
+
+import com.google.gerrit.extensions.annotations.Exports;
import com.google.gerrit.extensions.annotations.PluginName;
-import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.config.PluginConfigFactory;
+import com.google.gerrit.server.config.ProjectConfigEntry;
import com.google.inject.AbstractModule;
import com.google.inject.Inject;
import com.googlesource.gerrit.plugins.its.base.ItsHookModule;
+import com.googlesource.gerrit.plugins.its.base.its.ItsConfig;
import com.googlesource.gerrit.plugins.its.base.its.ItsFacade;
import com.googlesource.gerrit.plugins.its.base.its.ItsFacadeFactory;
-import com.googlesource.gerrit.plugins.its.base.its.SingleItsServer;
-import org.eclipse.jgit.lib.Config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -32,27 +36,30 @@
private static final Logger LOG = LoggerFactory.getLogger(JiraModule.class);
private final String pluginName;
- private final Config gerritConfig;
private final PluginConfigFactory pluginCfgFactory;
@Inject
- public JiraModule(
- @PluginName String pluginName,
- @GerritServerConfig Config config,
- PluginConfigFactory pluginCfgFactory) {
+ public JiraModule(@PluginName String pluginName, PluginConfigFactory pluginCfgFactory) {
this.pluginName = pluginName;
- this.gerritConfig = config;
this.pluginCfgFactory = pluginCfgFactory;
}
@Override
protected void configure() {
- if (gerritConfig.getString(pluginName, null, "url") != null) {
- LOG.info("JIRA is configured as ITS");
- bind(ItsFacade.class).to(JiraItsFacade.class).asEagerSingleton();
- bind(ItsFacadeFactory.class).to(SingleItsServer.class);
-
- install(new ItsHookModule(pluginName, pluginCfgFactory));
- }
+ bind(ItsFacade.class).to(JiraItsFacade.class);
+ bind(ItsFacadeFactory.class).to(JiraItsServer.class).asEagerSingleton();
+ bind(ProjectConfigEntry.class)
+ .annotatedWith(Exports.named(PROJECT_CONFIG_URL_KEY))
+ .toInstance(new JiraUrlProjectConfigEntry("Server URL"));
+ bind(ProjectConfigEntry.class)
+ .annotatedWith(Exports.named(PROJECT_CONFIG_USERNAME_KEY))
+ .toInstance(new ProjectConfigEntry("JIRA username", ""));
+ bind(ProjectConfigEntry.class)
+ .annotatedWith(Exports.named(PROJECT_CONFIG_PASSWORD_KEY))
+ .toInstance(new ProjectConfigEntry("JIRA password", ""));
+ bind(ItsConfig.class);
+ install(new ItsHookModule(pluginName, pluginCfgFactory));
+ install(JiraItsServerCacheImpl.module());
+ LOG.info("JIRA is configured as ITS");
}
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/jira/JiraUrlProjectConfigEntry.java b/src/main/java/com/googlesource/gerrit/plugins/its/jira/JiraUrlProjectConfigEntry.java
new file mode 100644
index 0000000..40c248c
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/jira/JiraUrlProjectConfigEntry.java
@@ -0,0 +1,47 @@
+// Copyright (C) 2018 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 com.google.gerrit.extensions.api.projects.ConfigValue;
+import com.google.gerrit.server.config.ProjectConfigEntry;
+import com.googlesource.gerrit.plugins.its.jira.restapi.JiraURL;
+import java.net.MalformedURLException;
+
+/** A {@link ProjectConfigEntry} for the Jira url. */
+class JiraUrlProjectConfigEntry extends ProjectConfigEntry {
+
+ public static final String INVALID_URL_MSG = "******* Invalid URL *******";
+
+ /**
+ * Builds a @{link ProjectConfigEntry}.
+ *
+ * @param displayName the display name
+ */
+ JiraUrlProjectConfigEntry(String displayName) {
+ super(displayName, "");
+ }
+
+ @Override
+ public ConfigValue preUpdate(ConfigValue configValue) {
+ if (configValue.value != null && !configValue.value.isEmpty()) {
+ try {
+ new JiraURL(configValue.value);
+ } catch (MalformedURLException e) {
+ configValue.value = INVALID_URL_MSG;
+ }
+ }
+ return configValue;
+ }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/jira/restapi/JiraRestApiProvider.java b/src/main/java/com/googlesource/gerrit/plugins/its/jira/restapi/JiraRestApiProvider.java
index 7ca8434..17809ab 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/its/jira/restapi/JiraRestApiProvider.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/jira/restapi/JiraRestApiProvider.java
@@ -14,36 +14,30 @@
package com.googlesource.gerrit.plugins.its.jira.restapi;
-import com.google.inject.Inject;
-import com.googlesource.gerrit.plugins.its.jira.JiraConfig;
+import com.googlesource.gerrit.plugins.its.jira.JiraItsServerInfo;
public class JiraRestApiProvider {
- private JiraConfig jiraConfig;
- @Inject
- public JiraRestApiProvider(JiraConfig jiraConfig) {
- this.jiraConfig = jiraConfig;
- }
-
- public <T> JiraRestApi<T> get(Class<T> classOfT, String classPrefix) {
+ public <T> JiraRestApi<T> get(
+ JiraItsServerInfo serverInfo, Class<T> classOfT, String classPrefix) {
return new JiraRestApi<>(
- jiraConfig.getJiraUrl(),
- jiraConfig.getUsername(),
- jiraConfig.getPassword(),
+ serverInfo.getUrl(),
+ serverInfo.getUsername(),
+ serverInfo.getPassword(),
classOfT,
classPrefix);
}
- public JiraRestApi<JiraIssue> getIssue() {
- return get(JiraIssue.class, "/issue");
+ public JiraRestApi<JiraIssue> getIssue(JiraItsServerInfo serverInfo) {
+ return get(serverInfo, JiraIssue.class, "/issue");
}
- public JiraRestApi<JiraServerInfo> getServerInfo() {
- return get(JiraServerInfo.class, "/serverInfo");
+ public JiraRestApi<JiraServerInfo> getServerInfo(JiraItsServerInfo server) {
+ return get(server, JiraServerInfo.class, "/serverInfo");
}
- public JiraRestApi<JiraProject[]> getProjects() {
- return get(JiraProject[].class, "/project");
+ public JiraRestApi<JiraProject[]> getProjects(JiraItsServerInfo serverInfo) {
+ return get(serverInfo, JiraProject[].class, "/project");
}
public JiraRestApi<JiraVersion[]> getVersions() {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/jira/restapi/JiraURL.java b/src/main/java/com/googlesource/gerrit/plugins/its/jira/restapi/JiraURL.java
index 03935a5..ed0cac6 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/its/jira/restapi/JiraURL.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/jira/restapi/JiraURL.java
@@ -22,13 +22,14 @@
import java.net.Proxy;
import java.net.ProxySelector;
import java.net.URL;
-import java.util.Arrays;
import java.util.Objects;
import org.eclipse.jgit.util.HttpSupport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class JiraURL {
+ /** Suffix to create a comment link based on this URL */
+ public static final String URL_SUFFIX = "/browse/$1";
private static final Logger log = LoggerFactory.getLogger(JiraURL.class);
@@ -43,7 +44,7 @@
}
public JiraURL resolveUrl(String... paths) {
- String relativePath = String.join("", Arrays.asList(paths));
+ String relativePath = String.join("", paths);
try {
return new JiraURL(new URL(url, relativePath));
} catch (MalformedURLException e) {
diff --git a/src/main/resources/Documentation/config.md b/src/main/resources/Documentation/config.md
index 3f50930..db66b9b 100644
--- a/src/main/resources/Documentation/config.md
+++ b/src/main/resources/Documentation/config.md
@@ -134,6 +134,17 @@
optional
Issue-id enforced in commit message [MANDATORY/?]: suggested
+The connectivity of its-jira plugin with Jira server happens on-request. When an
+action is requested, a connection is established based on any of the two
+configuration's availability, i.e., global config extracted from gerrit.config or
+project level config from project.config.
+
+The way a Jira issue and its corresponding gerrit change are annotated can be
+configured by specifying rules in a separate config file. Global rules, applied
+by all configured ITS plugins, can be defined in the file
+`review_site/etc/its/actions.config`. Rules specific to @PLUGIN@ are defined in
+the file `review_site/etc/its/actions-@PLUGIN@.config`.
+
**Sample actions-@Plugin@.config:**
[rule "open"]
@@ -168,3 +179,64 @@
event-type = patchset-created
action = add-soy-comment Change ${its.formatLink($changeUrl)} is created.
action = In Progress
+
+Multiple Jira servers integration
+---------------------------------
+
+```
+Please note that this feature is considered EXPERIMENTAL and should be used with
+caution, as it could expose sensitive information.
+```
+
+In corporate environments, it is not unusual to have multiple Jira servers
+and it is a common requirement to integrate Gerrit projects with those.
+
+This plugin offers the possibility of configuring integrations with multiple Jira
+servers at the Gerrit project level, i.e., a Gerrit project can be associated with
+a particular Jira instance. This is done by specifying the Jira server URL,
+username and password in the project configuration using the GUI controls
+this plugin adds to the project's General page. In this case, the *commentlink*
+section is automatically added by the plugin. It is also possible to add the
+configuration entries by manually editing the *project.config* file in the
+*refs/meta/config* branch.
+
+A typical Jira server configuration in the *project.config* file will look like:
+
+ [plugin "its-jira"]
+ enabled = true
+ instanceUrl = http://jiraserver:8075/
+ jiraUsername = *user*
+ password = *pass*
+
+ [commentlink "its-jira"]
+ match = ([A-Z]+-[0-9]+)
+ link = http://jiraserver:8075/browse/$1
+
+A different project could define its own Jira server in its *project.config*
+file:
+
+ [plugin "its-jira"]
+ enabled = true
+ instanceUrl = http://other_jiraserver:7171/
+ jiraUsername = *another_user*
+ password = *another_pass*
+
+ [commentlink "its-jira"]
+ match = (JIRA-ISSUE:[0-9]+)
+ link = http://other_jiraserver:7171/browse/$1
+
+In case its-jira plugin is enabled for a project but no Jira server is configured
+for the project, i.e., it is not specified in the *project.config* file, the
+default configuration will be the one defined in *gerrit.config*.
+
+If no Jira server information is defined in *gerrit.config* either, an error is
+logged and the Jira integration is disabled for the project.
+
+The credentials mentioned at the project level, i.e., in the *project.config* file,
+will take precedence over the global credentials mentioned in *secure.config*.
+It is important to notice that __the credentials at the project level are stored as
+clear text and will be visible to anyone having access to the
+*refs/meta/config branch* like project owners and site administrators__. This is a
+limitation and the reason why this feature is marked as experimental, i.e., not
+production ready. Additional work is needed in order to offer a secure level of
+encryption for this information.
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
index e25d1ef..66e151e 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/its/jira/JiraConfigTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/its/jira/JiraConfigTest.java
@@ -15,14 +15,17 @@
package com.googlesource.gerrit.plugins.its.jira;
import static com.google.common.truth.Truth.assertThat;
-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 org.mockito.Mockito.when;
-import com.googlesource.gerrit.plugins.its.jira.restapi.JiraURL;
-import java.net.MalformedURLException;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.server.config.PluginConfig;
+import com.google.gerrit.server.config.PluginConfigFactory;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.project.NoSuchProjectException;
+import com.google.gerrit.server.project.ProjectCache;
import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
@@ -32,39 +35,31 @@
@RunWith(MockitoJUnitRunner.class)
public class JiraConfigTest {
-
- private static final String PASS = "pass";
- private static final JiraURL TEST_URL = newUrl("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;
+ @Mock private PluginConfigFactory cfgFactory;
+ @Mock private PersonIdent serverUser;
+ @Mock private ProjectCache projectCache;
+ @Mock private GitRepositoryManager repoManager;
private JiraConfig jiraConfig;
- @Test
- public void gerritConfigContainsSaneValues() throws Exception {
- when(cfg.getString(PLUGIN_NAME, null, GERRIT_CONFIG_URL)).thenReturn(TEST_URL.toString());
- 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(TEST_URL);
+ @Before
+ public void createJiraConfig() {
+ jiraConfig =
+ new JiraConfig(cfg, PLUGIN_NAME, cfgFactory, serverUser, projectCache, repoManager);
}
@Test
- public void gerritConfigContainsNullValues() throws Exception {
- thrown.expect(RuntimeException.class);
- jiraConfig = new JiraConfig(cfg, PLUGIN_NAME);
- }
-
- private static JiraURL newUrl(String url) {
- try {
- return new JiraURL(url);
- } catch (MalformedURLException e) {
- throw new RuntimeException(e);
- }
+ public void testGetPluginConfigFor() throws NoSuchProjectException {
+ Project.NameKey project = new Project.NameKey("$project");
+ PluginConfig pluginCfg = new PluginConfig(PLUGIN_NAME, new Config());
+ when(cfgFactory.getFromProjectConfigWithInheritance(project, PLUGIN_NAME))
+ .thenReturn(pluginCfg);
+ jiraConfig.getPluginConfigFor(project.get());
+ assertThat(pluginCfg).isNotNull();
}
}
diff --git a/src/test/java/com/googlesource/gerrit/plugins/its/jira/JiraItsFacadeTest.java b/src/test/java/com/googlesource/gerrit/plugins/its/jira/JiraItsFacadeTest.java
index b5bae41..56dfb09 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/its/jira/JiraItsFacadeTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/its/jira/JiraItsFacadeTest.java
@@ -14,17 +14,12 @@
package com.googlesource.gerrit.plugins.its.jira;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
import com.googlesource.gerrit.plugins.its.base.its.InvalidTransitionException;
import com.googlesource.gerrit.plugins.its.base.its.ItsFacade.Check;
-import com.googlesource.gerrit.plugins.its.jira.restapi.JiraProject;
-import com.googlesource.gerrit.plugins.its.jira.restapi.JiraServerInfo;
import java.io.IOException;
import java.net.URL;
-import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -39,54 +34,42 @@
private static final String PROJECT_KEY = "projectKey";
@Mock private JiraClient jiraClient;
-
+ private JiraItsServerInfo server;
private JiraItsFacade jiraFacade;
- @Before
- public void setUp() throws Exception {
- JiraServerInfo jiraServerInfo = mock(JiraServerInfo.class);
- when(jiraServerInfo.getBaseUri()).thenReturn("http://jira-server.com");
- when(jiraServerInfo.getVersion()).thenReturn("v1");
- when(jiraClient.sysInfo()).thenReturn(jiraServerInfo);
- JiraProject jiraProject = mock(JiraProject.class);
- when(jiraProject.getKey()).thenReturn("key1");
- when(jiraProject.getName()).thenReturn("testProject");
- when(jiraClient.getProjects()).thenReturn(new JiraProject[] {jiraProject});
- }
-
@Test
public void healthCheckAccess() throws IOException {
jiraFacade = new JiraItsFacade(jiraClient);
jiraFacade.healthCheck(Check.ACCESS);
- verify(jiraClient).healthCheckAccess();
+ verify(jiraClient).healthCheckAccess(server);
}
@Test
public void healthCheckSysInfo() throws IOException {
jiraFacade = new JiraItsFacade(jiraClient);
jiraFacade.healthCheck(Check.SYSINFO);
- verify(jiraClient).healthCheckSysinfo();
+ verify(jiraClient).healthCheckSysinfo(server);
}
@Test
public void addComment() throws IOException {
jiraFacade = new JiraItsFacade(jiraClient);
jiraFacade.addComment(ISSUE_KEY, COMMENT);
- verify(jiraClient).addComment(ISSUE_KEY, COMMENT);
+ verify(jiraClient).addComment(server, ISSUE_KEY, COMMENT);
}
@Test
public void addRelatedLink() throws IOException {
jiraFacade = new JiraItsFacade(jiraClient);
jiraFacade.addRelatedLink(ISSUE_KEY, new URL("http://jira.com"), "description");
- verify(jiraClient).addComment(ISSUE_KEY, "Related URL: [description|http://jira.com]");
+ verify(jiraClient).addComment(server, ISSUE_KEY, "Related URL: [description|http://jira.com]");
}
@Test
public void performAction() throws IOException, InvalidTransitionException {
jiraFacade = new JiraItsFacade(jiraClient);
jiraFacade.performAction(ISSUE_KEY, ACTION);
- verify(jiraClient).doTransition(ISSUE_KEY, ACTION);
+ verify(jiraClient).doTransition(server, ISSUE_KEY, ACTION);
}
@Test
@@ -100,6 +83,6 @@
public void exists() throws IOException {
jiraFacade = new JiraItsFacade(jiraClient);
jiraFacade.exists(ISSUE_KEY);
- verify(jiraClient).issueExists(ISSUE_KEY);
+ verify(jiraClient).issueExists(server, ISSUE_KEY);
}
}
diff --git a/src/test/java/com/googlesource/gerrit/plugins/its/jira/JiraItsServerTest.java b/src/test/java/com/googlesource/gerrit/plugins/its/jira/JiraItsServerTest.java
new file mode 100644
index 0000000..06daa15
--- /dev/null
+++ b/src/test/java/com/googlesource/gerrit/plugins/its/jira/JiraItsServerTest.java
@@ -0,0 +1,72 @@
+// 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 org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import com.google.gerrit.reviewdb.client.Project;
+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 JiraItsServerTest {
+ private static final Project.NameKey PROJECT_NAMEKEY = new Project.NameKey("project");
+
+ @Mock private JiraConfig jiraConfig;
+ @Mock private JiraItsFacade itsFacade;
+ @Mock private JiraItsServerCache serverCache;
+ @Mock private JiraItsServerInfo jiraItsServerInfo;
+
+ @Rule public ExpectedException expectedException = ExpectedException.none();
+
+ private JiraItsServer jiraItsServer;
+
+ @Test
+ public void testValidServerInfoIsreturnedFromTheCache() throws Exception {
+ when(jiraItsServerInfo.isValid()).thenReturn(true);
+ when(serverCache.get(PROJECT_NAMEKEY.get())).thenReturn(jiraItsServerInfo);
+ jiraItsServer = new JiraItsServer(jiraConfig, itsFacade, serverCache);
+ jiraItsServer.getFacade(PROJECT_NAMEKEY);
+ verify(jiraConfig).addCommentLinksSection(PROJECT_NAMEKEY, jiraItsServerInfo);
+ verify(itsFacade).setJiraServerInstance(jiraItsServerInfo);
+ }
+
+ @Test
+ public void testGetDefaultServerInfo() throws Exception {
+ when(jiraItsServerInfo.isValid()).thenReturn(false).thenReturn(true);
+ when(serverCache.get(PROJECT_NAMEKEY.get())).thenReturn(jiraItsServerInfo);
+ when(jiraConfig.getDefaultServerInfo()).thenReturn(jiraItsServerInfo);
+ jiraItsServer = new JiraItsServer(jiraConfig, itsFacade, serverCache);
+ jiraItsServer.getFacade(PROJECT_NAMEKEY);
+ verify(jiraConfig, never()).addCommentLinksSection(PROJECT_NAMEKEY, jiraItsServerInfo);
+ verify(itsFacade).setJiraServerInstance(jiraItsServerInfo);
+ }
+
+ @Test
+ public void testNoConfiguredServerInfo() throws Exception {
+ when(serverCache.get(PROJECT_NAMEKEY.get())).thenReturn(jiraItsServerInfo);
+ when(jiraItsServerInfo.isValid()).thenReturn(false).thenReturn(false);
+ when(jiraConfig.getDefaultServerInfo()).thenReturn(jiraItsServerInfo);
+ jiraItsServer = new JiraItsServer(jiraConfig, itsFacade, serverCache);
+ expectedException.expect(RuntimeException.class);
+ jiraItsServer.getFacade(PROJECT_NAMEKEY);
+ }
+}
diff --git a/src/test/java/com/googlesource/gerrit/plugins/its/jira/restapi/JiraRestApiTest.java b/src/test/java/com/googlesource/gerrit/plugins/its/jira/restapi/JiraRestApiTest.java
index 1ee2ab2..1fa7f6a 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/its/jira/restapi/JiraRestApiTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/its/jira/restapi/JiraRestApiTest.java
@@ -32,10 +32,10 @@
public class JiraRestApiTest {
private static final String ISSUE_CLASS_PREFIX = "/issue/";
private static final String JSON_PAYLOAD = "{}";
+ private static final String USERNAME = "user";
+ private static final String PASSWORD = "pass";
private JiraURL url;
- private String user = "user";
- private String password = "pass";
private JiraRestApi restApi;
private void setURL(String jiraUrl) throws MalformedURLException {
@@ -45,7 +45,7 @@
@Test
public void testJiraServerInfoForNonRootJiraUrl() throws Exception {
setURL("http://jira.mycompany.com/myroot/");
- restApi = new JiraRestApi(url, user, password, JiraIssue.class, ISSUE_CLASS_PREFIX);
+ restApi = new JiraRestApi(url, USERNAME, PASSWORD, JiraIssue.class, ISSUE_CLASS_PREFIX);
String jiraApiUrl = restApi.getBaseUrl().toString();
assertThat(jiraApiUrl).startsWith(url.toString());
}
@@ -53,15 +53,15 @@
@Test
public void testJiraServerInfoForNonRootJiraUrlNotEndingWithSlash() throws Exception {
setURL("http://jira.mycompany.com/myroot");
- restApi = new JiraRestApi(url, user, password, JiraIssue.class, ISSUE_CLASS_PREFIX);
+ restApi = new JiraRestApi(url, USERNAME, PASSWORD, JiraIssue.class, ISSUE_CLASS_PREFIX);
String jiraApiUrl = restApi.getBaseUrl().toString();
assertThat(jiraApiUrl).startsWith(url.toString());
}
@Test
public void testJiraServerInfoForRootJiraUrl() throws Exception {
- setURL("http://jira.mycompany.com");
- restApi = new JiraRestApi(url, user, password, JiraIssue.class, ISSUE_CLASS_PREFIX);
+ setURL("http://jira.mycompany.com/myroot");
+ restApi = new JiraRestApi(url, USERNAME, PASSWORD, JiraIssue.class, ISSUE_CLASS_PREFIX);
String jiraApiUrl = restApi.getBaseUrl().toString();
assertThat(jiraApiUrl).startsWith(url.toString());
}
@@ -78,7 +78,7 @@
when(connection.getOutputStream()).thenReturn(connectionOutputStream);
when(connection.getResponseCode()).thenReturn(HTTP_NO_CONTENT);
- restApi = new JiraRestApi(url, user, password, JiraIssue.class, ISSUE_CLASS_PREFIX);
+ restApi = new JiraRestApi(url, USERNAME, PASSWORD, JiraIssue.class, ISSUE_CLASS_PREFIX);
boolean pass = restApi.doPut(ISSUE_CLASS_PREFIX, JSON_PAYLOAD, HTTP_NO_CONTENT);
verify(connection).setRequestMethod("PUT");