You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by dp...@apache.org on 2019/01/14 16:33:32 UTC
[ignite-teamcity-bot] branch ignite-10930 updated: IGNITE-10930:
PR-less contributions: refactoring of JIRA integration
This is an automated email from the ASF dual-hosted git repository.
dpavlov pushed a commit to branch ignite-10930
in repository https://gitbox.apache.org/repos/asf/ignite-teamcity-bot.git
The following commit(s) were added to refs/heads/ignite-10930 by this push:
new fdf593e IGNITE-10930: PR-less contributions: refactoring of JIRA integration
fdf593e is described below
commit fdf593e01b05fe1f90e5539215fd58ee9d94471b
Author: Dmitriy Pavlov <dp...@apache.org>
AuthorDate: Mon Jan 14 19:33:27 2019 +0300
IGNITE-10930: PR-less contributions: refactoring of JIRA integration
---
.../java/org/apache/ignite/ci/HelperConfig.java | 2 +-
.../main/java/org/apache/ignite/ci/ITcHelper.java | 17 --
.../main/java/org/apache/ignite/ci/ITeamcity.java | 36 ----
.../apache/ignite/ci/IgnitePersistentTeamcity.java | 30 ----
.../apache/ignite/ci/IgniteTeamcityConnection.java | 47 +-----
.../main/java/org/apache/ignite/ci/TcHelper.java | 154 -----------------
.../ignite/ci/jira/pure/IJiraIntegration.java | 25 ++-
.../java/org/apache/ignite/ci/jira/pure/Jira.java | 79 ++++++++-
.../apache/ignite/ci/observer/ObserverTask.java | 5 +-
.../ignite/ci/tcbot/chain/PrChainsProcessor.java | 2 +-
.../tcbot/visa/TcBotTriggerAndSignOffService.java | 184 +++++++++++++++++++--
.../ignite/ci/web/model/current/UpdateInfo.java | 6 +-
.../apache/ignite/ci/web/rest/TriggerBuilds.java | 5 +-
.../ignite/ci/web/rest/visa/TcBotVisaService.java | 3 +-
14 files changed, 266 insertions(+), 329 deletions(-)
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/HelperConfig.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/HelperConfig.java
index f773100..fe38350 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/HelperConfig.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/HelperConfig.java
@@ -159,7 +159,7 @@ public class HelperConfig {
* @param props Properties, where token is placed.
* @return Null or decoded auth token for Github.
*/
- @Nullable static String prepareJiraHttpAuthToken(Properties props) {
+ @Nullable public static String prepareJiraHttpAuthToken(Properties props) {
String tok = props.getProperty(JIRA_AUTH_TOKEN);
if (isNullOrEmpty(tok))
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/ITcHelper.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/ITcHelper.java
index 1c084c1..e93dbab 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/ITcHelper.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/ITcHelper.java
@@ -55,21 +55,4 @@ public interface ITcHelper extends ITcServerProvider {
/** */
boolean isServerAuthorized();
- /**
- * @param srvId Server id.
- * @param prov Credentials.
- * @param buildTypeId Suite name.
- * @param branchForTc Branch for TeamCity.
- * @param ticket JIRA ticket full name.
- * @return {@code Visa} which contains info about JIRA notification.
- */
- Visa notifyJira(String srvId, ICredentialsProv prov, String buildTypeId, String branchForTc, String ticket);
-
- /**
- * @param srvId Server id.
- * @param prov Credentials.
- * @param ticketId Ticket.
- * @return Jira tickets.
- */
- Tickets getJiraTickets(String srvId, ICredentialsProv prov, String ticketId);
}
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/ITeamcity.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/ITeamcity.java
index fa1f3a7..4ce064d 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/ITeamcity.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/ITeamcity.java
@@ -91,42 +91,6 @@ public interface ITeamcity extends ITeamcityConn {
boolean isTeamCityTokenAvailable();
- /**
- * @param tok Jira authorization token.
- */
- void setJiraToken(String tok);
-
- /**
- * @return {@code True} if JIRA authorization token is available.
- */
- boolean isJiraTokenAvailable();
-
- /**
- * @param ticket JIRA ticket full name. E.g 'IGNITE-5555'.
- * @param comment Comment to be placed in the ticket conversation.
- * @return {@code True} if ticket was succesfully commented. Otherwise - {@code false}.
- *
- * @throws IOException If failed to comment JIRA ticket.
- * @throws IllegalStateException If can't find URL to the JIRA.
- */
- public String sendJiraComment(String ticket, String comment) throws IOException;
-
- /**
- * @param url Url.
- * @return Response as gson string.
- */
- String sendGetToJira(String url) throws IOException;
-
- /**
- * @param url URL for JIRA integration.
- */
- void setJiraApiUrl(String url);
-
- /**
- * @return URL for JIRA integration.
- */
- String getJiraApiUrl();
-
default void setAuthData(String user, String pwd) {
setAuthToken(
Base64Util.encodeUtf8String(user + ":" + pwd));
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/IgnitePersistentTeamcity.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/IgnitePersistentTeamcity.java
index 76db429..bdf0309 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/IgnitePersistentTeamcity.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/IgnitePersistentTeamcity.java
@@ -540,36 +540,6 @@ public class IgnitePersistentTeamcity implements IAnalyticsEnabledTeamcity, ITea
}
/** {@inheritDoc} */
- @Override public void setJiraToken(String tok) {
- teamcity.setJiraToken(tok);
- }
-
- /** {@inheritDoc} */
- @Override public boolean isJiraTokenAvailable() {
- return teamcity.isJiraTokenAvailable();
- }
-
- /** {@inheritDoc} */
- @Override public String sendJiraComment(String ticket, String comment) throws IOException {
- return teamcity.sendJiraComment(ticket, comment);
- }
-
- /** {@inheritDoc} */
- @Override public String sendGetToJira(String url) throws IOException {
- return teamcity.sendGetToJira(url);
- }
-
- /** {@inheritDoc} */
- @Override public void setJiraApiUrl(String url) {
- teamcity.setJiraApiUrl(url);
- }
-
- /** {@inheritDoc} */
- @Override public String getJiraApiUrl() {
- return teamcity.getJiraApiUrl();
- }
-
- /** {@inheritDoc} */
@Override public List<Agent> agents(boolean connected, boolean authorized) {
return teamcity.agents(connected, authorized);
}
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/IgniteTeamcityConnection.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/IgniteTeamcityConnection.java
index 50c707f..6ce1986 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/IgniteTeamcityConnection.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/IgniteTeamcityConnection.java
@@ -103,11 +103,6 @@ public class IgniteTeamcityConnection implements ITeamcity {
/** Teamcity http connection. */
@Inject private ITeamcityHttpConnection teamcityHttpConn;
- /** JIRA authorization token. */
- private String jiraBasicAuthTok;
-
- /** URL for JIRA integration. */
- private String jiraApiUrl;
private String configName; //main properties file name
private String tcName;
@@ -140,8 +135,7 @@ public class IgniteTeamcityConnection implements ITeamcity {
logger.error("Failed to set credentials", e);
}
- setJiraToken(HelperConfig.prepareJiraHttpAuthToken(props));
- setJiraApiUrl(props.getProperty(HelperConfig.JIRA_API_URL));
+
final File logsDirFile = HelperConfig.resolveLogs(workDir, props);
@@ -160,45 +154,6 @@ public class IgniteTeamcityConnection implements ITeamcity {
return basicAuthTok != null;
}
- /** {@inheritDoc} */
- @Override public void setJiraToken(String tok) {
- jiraBasicAuthTok = tok;
- }
-
- /** {@inheritDoc} */
- @Override public boolean isJiraTokenAvailable() {
- return jiraBasicAuthTok != null;
- }
-
- /** {@inheritDoc} */
- @AutoProfiling
- @Override public String sendJiraComment(String ticket, String comment) throws IOException {
- if (isNullOrEmpty(jiraApiUrl))
- throw new IllegalStateException("JIRA API URL is not configured for this server.");
-
- String url = jiraApiUrl + "issue/" + ticket + "/comment";
-
- return HttpUtil.sendPostAsStringToJira(jiraBasicAuthTok, url, "{\"body\": \"" + comment + "\"}");
- }
-
- /** {@inheritDoc} */
- @Override public String sendGetToJira(String url) throws IOException {
- if (isNullOrEmpty(jiraApiUrl))
- throw new IllegalStateException("JIRA API URL is not configured for this server.");
-
- return HttpUtil.sendGetToJira(jiraBasicAuthTok, jiraApiUrl + url);
- }
-
- /** {@inheritDoc} */
- @Override public void setJiraApiUrl(String url) {
- jiraApiUrl = url;
- }
-
- /** {@inheritDoc} */
- @Override public String getJiraApiUrl() {
- return jiraApiUrl;
- }
-
/** {@inheritDoc} */
@AutoProfiling
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/TcHelper.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/TcHelper.java
index 5640139..459a669 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/TcHelper.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/TcHelper.java
@@ -53,9 +53,6 @@ import static org.apache.ignite.ci.util.XmlUtil.xmlEscapeText;
*/
@Deprecated
public class TcHelper implements ITcHelper {
- /** Logger. */
- private static final Logger logger = LoggerFactory.getLogger(TcHelper.class);
-
/** Stop guard. */
private AtomicBoolean stop = new AtomicBoolean();
@@ -70,18 +67,6 @@ public class TcHelper implements ITcHelper {
@Inject private UserAndSessionsStorage userAndSessionsStorage;
- @Inject private PrChainsProcessor prChainsProcessor;
-
- @Inject private ITeamcityIgnitedProvider tcProv;
-
- @Inject private IStringCompactor compactor;
-
- /** */
- private final ObjectMapper objectMapper;
-
- public TcHelper() {
- objectMapper = new ObjectMapper();
- }
/** {@inheritDoc} */
@Override public void setServerAuthorizerCreds(ICredentialsProv creds) {
@@ -126,145 +111,6 @@ public class TcHelper implements ITcHelper {
}
- /** {@inheritDoc} */
- @Override public Visa notifyJira(
- String srvId,
- ICredentialsProv prov,
- String buildTypeId,
- String branchForTc,
- String ticket
- ) {
- IAnalyticsEnabledTeamcity teamcity = server(srvId, prov);
-
- ITeamcityIgnited tcIgnited = tcProv.server(srvId, prov);
-
- List<Integer> builds = tcIgnited.getLastNBuildsFromHistory(buildTypeId, branchForTc, 1);
-
- if (builds.isEmpty())
- return new Visa("JIRA wasn't commented - no finished builds to analyze.");
-
- Integer buildId = builds.get(0);
-
- FatBuildCompacted fatBuild = tcIgnited.getFatBuild(buildId);
- Build build = fatBuild.toBuild(compactor);
-
- build.webUrl = tcIgnited.host() + "viewLog.html?buildId=" + build.getId() + "&buildTypeId=" + build.buildTypeId;
-
- int blockers;
-
- JiraCommentResponse res;
-
- try {
- List<SuiteCurrentStatus> suitesStatuses = prChainsProcessor.getBlockersSuitesStatuses(buildTypeId, build.branchName, srvId, prov);
-
- if (suitesStatuses == null)
- return new Visa("JIRA wasn't commented - no finished builds to analyze.");
-
- String comment = generateJiraComment(suitesStatuses, build.webUrl, buildTypeId, tcIgnited);
-
- blockers = suitesStatuses.stream()
- .mapToInt(suite -> {
- if (suite.testFailures.isEmpty())
- return 1;
-
- return suite.testFailures.size();
- })
- .sum();
-
- res = objectMapper.readValue(teamcity.sendJiraComment(ticket, comment), JiraCommentResponse.class);
- }
- catch (Exception e) {
- String errMsg = "Exception happened during commenting JIRA ticket " +
- "[build=" + build.getId() + ", errMsg=" + e.getMessage() + ']';
-
- logger.error(errMsg);
-
- return new Visa("JIRA wasn't commented - " + errMsg);
- }
-
- return new Visa(IJiraIntegration.JIRA_COMMENTED, res, blockers);
- }
-
- /** {@inheritDoc} */
- @Override public Tickets getJiraTickets(String srvId, ICredentialsProv prov, String url) {
- IAnalyticsEnabledTeamcity teamcity = server(srvId, prov);
-
- try {
- return new Gson().fromJson(teamcity.sendGetToJira(url), Tickets.class);
- }
- catch (Exception e) {
- String errMsg = "Exception happened during receiving JIRA tickets " +
- "[url=" + url + ", errMsg=" + e.getMessage() + ']';
-
- logger.error(errMsg);
-
- return new Tickets();
- }
- }
-
- /**
- * @param suites Suite Current Status.
- * @param webUrl Build URL.
- * @return Comment, which should be sent to the JIRA ticket.
- */
- private String generateJiraComment(List<SuiteCurrentStatus> suites, String webUrl, String buildTypeId,
- ITeamcityIgnited tcIgnited) {
- BuildTypeRefCompacted bt = tcIgnited.getBuildTypeRef(buildTypeId);
-
- String suiteName = (bt != null ? bt.name(compactor) : buildTypeId);
-
- StringBuilder res = new StringBuilder();
-
- for (SuiteCurrentStatus suite : suites) {
- res.append("{color:#d04437}").append(suite.name).append("{color}");
- res.append(" [[tests ").append(suite.failedTests);
-
- if (suite.result != null && !suite.result.isEmpty())
- res.append(' ').append(suite.result);
-
- res.append('|').append(suite.webToBuild).append("]]\\n");
-
- for (TestFailure failure : suite.testFailures) {
- res.append("* ");
-
- if (failure.suiteName != null && failure.testName != null)
- res.append(failure.suiteName).append(": ").append(failure.testName);
- else
- res.append(failure.name);
-
- FailureSummary recent = failure.histBaseBranch.recent;
-
- if (recent != null) {
- if (recent.failureRate != null) {
- res.append(" - ").append(recent.failureRate).append("% fails in last ")
- .append(recent.runs).append(" master runs.");
- }
- else if (recent.failures != null && recent.runs != null) {
- res.append(" - ").append(recent.failures).append(" fails / ")
- .append(recent.runs).append(" master runs.");
- }
- }
-
- res.append("\\n");
- }
-
- res.append("\\n");
- }
-
- if (res.length() > 0) {
- res.insert(0, "{panel:title=" + suiteName + ": Possible Blockers|" +
- "borderStyle=dashed|borderColor=#ccc|titleBGColor=#F7D6C1}\\n")
- .append("{panel}");
- }
- else {
- res.append("{panel:title=").append(suiteName).append(": No blockers found!|")
- .append("borderStyle=dashed|borderColor=#ccc|titleBGColor=#D6F7C1}{panel}");
- }
-
- res.append("\\n").append("[TeamCity *").append(suiteName).append("* Results|").append(webUrl).append(']');
-
- return xmlEscapeText(res.toString());
- }
public void close() {
if (stop.compareAndSet(false, true))
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/pure/IJiraIntegration.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/pure/IJiraIntegration.java
index ef55cc6..b068366 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/pure/IJiraIntegration.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/pure/IJiraIntegration.java
@@ -17,6 +17,7 @@
package org.apache.ignite.ci.jira.pure;
+import java.io.IOException;
import org.apache.ignite.ci.jira.Tickets;
import org.apache.ignite.ci.web.model.Visa;
import org.apache.ignite.ci.user.ICredentialsProv;
@@ -30,19 +31,13 @@ public interface IJiraIntegration {
public static String JIRA_COMMENTED = "JIRA commented.";
/**
- * Produce visa message(see {@link Visa}) based on passed
- * parameters and publish it as a comment for specified ticket
- * on Jira server.
- *
- * @param srvId TC Server ID to take information about token from.
- * @param prov Credentials.
- * @param buildTypeId Suite name.
- * @param branchForTc Branch for TeamCity.
- * @param ticket JIRA ticket full name. E.g. IGNITE-5555
- * @return {@link Visa} instance.
+ * @param ticket JIRA ticket full name. E.g 'IGNITE-5555'.
+ * @param comment Comment to be placed in the ticket conversation.
+ * @return {@code True} if ticket was succesfully commented. Otherwise - {@code false}.
+ * @throws IOException If failed to comment JIRA ticket.
+ * @throws IllegalStateException If can't find URL to the JIRA.
*/
- public Visa notifyJira(String srvId, ICredentialsProv prov, String buildTypeId, String branchForTc,
- String ticket);
+ public String sendJiraComment(String ticket, String comment) throws IOException;
/**
* Produce wrapper for collection of Jira tickets for given server.
@@ -65,15 +60,17 @@ public interface IJiraIntegration {
/**
* @param ticketFullName Ticket full name (e.g IGNITE-8331)
- *
* @return URL which is used as link to Jira ticket with specified name.
*/
public String generateTicketUrl(String ticketFullName);
/**
* @param ticketFullName Ticket full name (e.g IGNITE-8331)
- *
* @return URL which is used as link to Jira comment with specified id.
*/
public String generateCommentUrl(String ticketFullName, int commentId);
+
+ String getJiraApiUrl();
+
+ boolean isJiraTokenAvailable();
}
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/pure/Jira.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/pure/Jira.java
index 7a163cc..d0866dd 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/pure/Jira.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/pure/Jira.java
@@ -18,15 +18,24 @@
package org.apache.ignite.ci.jira.pure;
import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
+import com.google.gson.Gson;
import java.io.File;
+import java.io.IOException;
import java.util.Properties;
import javax.inject.Inject;
import org.apache.ignite.ci.HelperConfig;
+import org.apache.ignite.ci.IAnalyticsEnabledTeamcity;
import org.apache.ignite.ci.ITcHelper;
+import org.apache.ignite.ci.di.AutoProfiling;
import org.apache.ignite.ci.jira.Tickets;
+import org.apache.ignite.ci.tcbot.visa.TcBotTriggerAndSignOffService;
import org.apache.ignite.ci.user.ICredentialsProv;
+import org.apache.ignite.ci.util.HttpUtil;
import org.apache.ignite.ci.web.model.Visa;
import org.jetbrains.annotations.NotNull;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import static com.google.common.base.Strings.isNullOrEmpty;
@@ -34,8 +43,8 @@ import static com.google.common.base.Strings.isNullOrEmpty;
*
*/
class Jira implements IJiraIntegration {
- /** */
- @Inject ITcHelper helper;
+ /** Logger. */
+ private static final Logger logger = LoggerFactory.getLogger(Jira.class);
/** */
private String jiraUrl;
@@ -43,6 +52,12 @@ class Jira implements IJiraIntegration {
/** JIRA ticket prefix. */
@NotNull private String jiraTicketPrefix;
+ /** JIRA authorization token. */
+ private String jiraBasicAuthTok;
+
+ /** URL for JIRA integration. */
+ private String jiraApiUrl;
+
/** {@inheritDoc} */
@Override public void init(String srvId) {
final File workDir = HelperConfig.resolveWorkDir();
@@ -54,8 +69,16 @@ class Jira implements IJiraIntegration {
jiraUrl = props.getProperty(HelperConfig.JIRA_URL);
jiraTicketPrefix = props.getProperty(HelperConfig.JIRA_TICKET_TEMPLATE, "IGNITE-");
+
+ jiraBasicAuthTok = HelperConfig.prepareJiraHttpAuthToken(props);
+ jiraApiUrl = props.getProperty(HelperConfig.JIRA_API_URL);
}
+ /**
+ * @return {@code True} if JIRA authorization token is available.
+ */
+ // boolean isJiraTokenAvailable();
+
/** {@inheritDoc} */
@Override public String jiraUrl() {
return jiraUrl;
@@ -67,14 +90,23 @@ class Jira implements IJiraIntegration {
}
/** {@inheritDoc} */
- @Override public Visa notifyJira(String srvId, ICredentialsProv prov, String buildTypeId, String branchForTc,
- String ticket) {
- return helper.notifyJira(srvId, prov, buildTypeId, branchForTc, ticket);
+ @Override public Tickets getTickets(String srvId, ICredentialsProv prov, String url) {
+ return getJiraTickets(srvId, prov, url);
}
/** {@inheritDoc} */
- @Override public Tickets getTickets(String srvId, ICredentialsProv prov, String url) {
- return helper.getJiraTickets(srvId, prov, url);
+ public Tickets getJiraTickets(String srvId, ICredentialsProv prov, String url) {
+ try {
+ return new Gson().fromJson(sendGetToJira(url), Tickets.class);
+ }
+ catch (Exception e) {
+ String errMsg = "Exception happened during receiving JIRA tickets " +
+ "[url=" + url + ", errMsg=" + e.getMessage() + ']';
+
+ logger.error(errMsg);
+
+ return new Tickets();
+ }
}
/** {@inheritDoc} */
@@ -91,4 +123,37 @@ class Jira implements IJiraIntegration {
"&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#comment-" +
commentId;
}
+
+ /** {@inheritDoc} */
+ @Override public String getJiraApiUrl() {
+ return jiraApiUrl;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean isJiraTokenAvailable() {
+ return !Strings.isNullOrEmpty(jiraBasicAuthTok);
+ }
+
+ /** {@inheritDoc} */
+ @AutoProfiling
+ @Override public String sendJiraComment(String ticket, String comment) throws IOException {
+ if (isNullOrEmpty(jiraApiUrl))
+ throw new IllegalStateException("JIRA API URL is not configured for this server.");
+
+ String url = jiraApiUrl + "issue/" + ticket + "/comment";
+
+ return HttpUtil.sendPostAsStringToJira(jiraBasicAuthTok, url, "{\"body\": \"" + comment + "\"}");
+ }
+
+ /**
+ * @param url Url.
+ * @return Response as gson string.
+ */
+ public String sendGetToJira(String url) throws IOException {
+ if (isNullOrEmpty(jiraApiUrl))
+ throw new IllegalStateException("JIRA API URL is not configured for this server.");
+
+ return HttpUtil.sendGetToJira(jiraBasicAuthTok, jiraApiUrl + url);
+ }
+
}
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/observer/ObserverTask.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/observer/ObserverTask.java
index cd7cdc9..5dd7091 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/observer/ObserverTask.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/observer/ObserverTask.java
@@ -32,6 +32,7 @@ import org.apache.ignite.ci.di.AutoProfiling;
import org.apache.ignite.ci.di.MonitoredTask;
import org.apache.ignite.ci.jira.pure.IJiraIntegration;
import org.apache.ignite.ci.jira.pure.IJiraIntegrationProvider;
+import org.apache.ignite.ci.tcbot.visa.TcBotTriggerAndSignOffService;
import org.apache.ignite.ci.teamcity.ignited.IStringCompactor;
import org.apache.ignite.ci.teamcity.ignited.ITeamcityIgnited;
import org.apache.ignite.ci.teamcity.ignited.ITeamcityIgnitedProvider;
@@ -67,6 +68,8 @@ public class ObserverTask extends TimerTask {
/** */
@Inject private VisasHistoryStorage visasHistStorage;
+ @Inject private TcBotTriggerAndSignOffService visaIssuer;
+
/** */
private ReentrantLock observationLock = new ReentrantLock();
@@ -194,7 +197,7 @@ public class ObserverTask extends TimerTask {
IJiraIntegration jiraIntegration = jiraIntegrationProvider.server(info.srvId);
- Visa updatedVisa = jiraIntegration.notifyJira(info.srvId, creds, info.buildTypeId,
+ Visa updatedVisa = visaIssuer.notifyJira(info.srvId, creds, info.buildTypeId,
info.branchForTc, info.ticket);
visasHistStorage.updateLastVisaRequest(info.getContributionKey(), (req -> req.setResult(updatedVisa)));
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcbot/chain/PrChainsProcessor.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcbot/chain/PrChainsProcessor.java
index a4f5d69..6d84015 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcbot/chain/PrChainsProcessor.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcbot/chain/PrChainsProcessor.java
@@ -108,7 +108,7 @@ public class PrChainsProcessor {
IJiraIntegration jiraIntegration = jiraIntegrationProvider.server(srvId);
- res.setJavaFlags(teamcity, gitHubConn);
+ res.setJavaFlags(teamcity, gitHubConn, jiraIntegration);
LatestRebuildMode rebuild;
if (FullQueryParams.HISTORY.equals(act))
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcbot/visa/TcBotTriggerAndSignOffService.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcbot/visa/TcBotTriggerAndSignOffService.java
index 2921b29..a36944b 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcbot/visa/TcBotTriggerAndSignOffService.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcbot/visa/TcBotTriggerAndSignOffService.java
@@ -17,6 +17,7 @@
package org.apache.ignite.ci.tcbot.visa;
+import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Strings;
import com.google.inject.Provider;
import java.text.DateFormat;
@@ -36,6 +37,7 @@ import javax.ws.rs.QueryParam;
import org.apache.ignite.ci.HelperConfig;
import org.apache.ignite.ci.ITcHelper;
import org.apache.ignite.ci.ITeamcity;
+import org.apache.ignite.ci.TcHelper;
import org.apache.ignite.ci.github.GitHubBranch;
import org.apache.ignite.ci.github.GitHubUser;
import org.apache.ignite.ci.github.PullRequest;
@@ -56,26 +58,36 @@ import org.apache.ignite.ci.teamcity.ignited.ITeamcityIgnitedProvider;
import org.apache.ignite.ci.teamcity.ignited.SyncMode;
import org.apache.ignite.ci.teamcity.ignited.buildtype.BuildTypeCompacted;
import org.apache.ignite.ci.teamcity.ignited.buildtype.BuildTypeRefCompacted;
+import org.apache.ignite.ci.teamcity.ignited.fatbuild.FatBuildCompacted;
import org.apache.ignite.ci.user.ICredentialsProv;
import org.apache.ignite.ci.web.model.ContributionKey;
+import org.apache.ignite.ci.web.model.JiraCommentResponse;
import org.apache.ignite.ci.web.model.SimpleResult;
import org.apache.ignite.ci.web.model.Visa;
import org.apache.ignite.ci.web.model.VisaRequest;
import org.apache.ignite.ci.web.model.current.SuiteCurrentStatus;
+import org.apache.ignite.ci.web.model.current.TestFailure;
+import org.apache.ignite.ci.web.model.hist.FailureSummary;
import org.apache.ignite.ci.web.model.hist.VisasHistoryStorage;
import org.apache.ignite.internal.util.typedef.F;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import static org.apache.ignite.ci.observer.BuildsInfo.CANCELLED_STATUS;
import static org.apache.ignite.ci.observer.BuildsInfo.FINISHED_STATUS;
import static org.apache.ignite.ci.observer.BuildsInfo.RUNNING_STATUS;
+import static org.apache.ignite.ci.util.XmlUtil.xmlEscapeText;
import static org.apache.ignite.ci.web.rest.parms.FullQueryParams.DEFAULT_TRACKED_BRANCH_NAME;
/**
* Provides method for TC Bot Visa obtaining
*/
public class TcBotTriggerAndSignOffService {
+ /** Logger. */
+ private static final Logger logger = LoggerFactory.getLogger(TcBotTriggerAndSignOffService.class);
+
/** */
private static final ThreadLocal<DateFormat> THREAD_FORMATTER = new ThreadLocal<DateFormat>() {
@Override protected DateFormat initialValue() {
@@ -96,7 +108,7 @@ public class TcBotTriggerAndSignOffService {
@Inject IJiraIntegrationProvider jiraIntegrationProvider;
/** */
- @Inject private VisasHistoryStorage visasHistoryStorage;
+ @Inject private VisasHistoryStorage visasHistStorage;
/** */
@Inject private IStringCompactor strCompactor;
@@ -107,9 +119,11 @@ public class TcBotTriggerAndSignOffService {
/** Helper. */
@Inject ITcHelper tcHelper;
-
@Inject PrChainsProcessor prChainsProcessor;
+ /** Jackson serializer. */
+ private final ObjectMapper objMapper = new ObjectMapper();
+
/** */
public void startObserver() {
buildObserverProvider.get();
@@ -123,7 +137,7 @@ public class TcBotTriggerAndSignOffService {
IJiraIntegration jiraIntegration = jiraIntegrationProvider.server(srvId);
- for (VisaRequest visaRequest : visasHistoryStorage.getVisas()) {
+ for (VisaRequest visaRequest : visasHistStorage.getVisas()) {
VisaStatus visaStatus = new VisaStatus();
BuildsInfo info = visaRequest.getInfo();
@@ -187,17 +201,6 @@ public class TcBotTriggerAndSignOffService {
/**
* @param pr Pull Request.
- * @return JIRA ticket full name or empty string.
- */
- @Deprecated
- @NotNull public static String getTicketFullName(PullRequest pr) {
- String prefix = "IGNITE-";
-
- return getTicketFullName(pr, prefix);
- }
-
- /**
- * @param pr Pull Request.
* @param prefix Ticket prefix.
* @return JIRA ticket full name or empty string.
*/
@@ -343,17 +346,16 @@ public class TcBotTriggerAndSignOffService {
if (!Strings.isNullOrEmpty(ticketFullName)) {
BuildsInfo buildsInfo = new BuildsInfo(srvId, prov, ticketFullName, branchForTc, suiteId);
- VisaRequest lastVisaReq = visasHistoryStorage.getLastVisaRequest(buildsInfo.getContributionKey());
+ VisaRequest lastVisaReq = visasHistStorage.getLastVisaRequest(buildsInfo.getContributionKey());
if (Objects.nonNull(lastVisaReq) && lastVisaReq.isObserving())
return new SimpleResult("Jira wasn't commented." +
" \"Re-run possible blockers & Comment JIRA\" was triggered for current branch." +
" Wait for the end or cancel exsiting observing.");
+ Visa visa = notifyJira(srvId, prov, suiteId, branchForTc, ticketFullName);
- Visa visa = jiraIntegration.notifyJira(srvId, prov, suiteId, branchForTc, ticketFullName);
-
- visasHistoryStorage.put(new VisaRequest(buildsInfo).setResult(visa));
+ visasHistStorage.put(new VisaRequest(buildsInfo).setResult(visa));
return new SimpleResult(visa.status);
}
@@ -587,4 +589,150 @@ public class TcBotTriggerAndSignOffService {
return status;
}
+ /**
+ * @param srvId Server id.
+ * @param prov Credentials.
+ * @param buildTypeId Suite name.
+ * @param branchForTc Branch for TeamCity.
+ * @param ticket JIRA ticket full name.
+ * @return {@code Visa} which contains info about JIRA notification.
+ */
+ //Visa notifyJira(String srvId, ICredentialsProv prov, String buildTypeId, String branchForTc, String ticket);
+
+
+ /**
+ * Produce visa message(see {@link Visa}) based on passed
+ * parameters and publish it as a comment for specified ticket
+ * on Jira server.
+ *
+ * @param srvId TC Server ID to take information about token from.
+ * @param prov Credentials.
+ * @param buildTypeId Suite name.
+ * @param branchForTc Branch for TeamCity.
+ * @param ticket JIRA ticket full name. E.g. IGNITE-5555
+ * @return {@link Visa} instance.
+ */
+ public Visa notifyJira(
+ String srvId,
+ ICredentialsProv prov,
+ String buildTypeId,
+ String branchForTc,
+ String ticket
+ ) {
+ ITeamcityIgnited tcIgnited = tcIgnitedProv.server(srvId, prov);
+
+ IJiraIntegration jira = jiraIntegrationProvider.server(srvId);
+
+ List<Integer> builds = tcIgnited.getLastNBuildsFromHistory(buildTypeId, branchForTc, 1);
+
+ if (builds.isEmpty())
+ return new Visa("JIRA wasn't commented - no finished builds to analyze.");
+
+ Integer buildId = builds.get(0);
+
+ FatBuildCompacted fatBuild = tcIgnited.getFatBuild(buildId);
+ Build build = fatBuild.toBuild(compactor);
+
+ build.webUrl = tcIgnited.host() + "viewLog.html?buildId=" + build.getId() + "&buildTypeId=" + build.buildTypeId;
+
+ int blockers;
+
+ JiraCommentResponse res;
+
+ try {
+ List<SuiteCurrentStatus> suitesStatuses = prChainsProcessor.getBlockersSuitesStatuses(buildTypeId, build.branchName, srvId, prov);
+
+ if (suitesStatuses == null)
+ return new Visa("JIRA wasn't commented - no finished builds to analyze.");
+
+ String comment = generateJiraComment(suitesStatuses, build.webUrl, buildTypeId, tcIgnited);
+
+ blockers = suitesStatuses.stream()
+ .mapToInt(suite -> {
+ if (suite.testFailures.isEmpty())
+ return 1;
+
+ return suite.testFailures.size();
+ })
+ .sum();
+
+ res = objMapper.readValue(jira.sendJiraComment(ticket, comment), JiraCommentResponse.class);
+ }
+ catch (Exception e) {
+ String errMsg = "Exception happened during commenting JIRA ticket " +
+ "[build=" + build.getId() + ", errMsg=" + e.getMessage() + ']';
+
+ logger.error(errMsg);
+
+ return new Visa("JIRA wasn't commented - " + errMsg);
+ }
+
+ return new Visa(IJiraIntegration.JIRA_COMMENTED, res, blockers);
+ }
+
+
+ /**
+ * @param suites Suite Current Status.
+ * @param webUrl Build URL.
+ * @return Comment, which should be sent to the JIRA ticket.
+ */
+ private String generateJiraComment(List<SuiteCurrentStatus> suites, String webUrl, String buildTypeId,
+ ITeamcityIgnited tcIgnited) {
+ BuildTypeRefCompacted bt = tcIgnited.getBuildTypeRef(buildTypeId);
+
+ String suiteName = (bt != null ? bt.name(compactor) : buildTypeId);
+
+ StringBuilder res = new StringBuilder();
+
+ for (SuiteCurrentStatus suite : suites) {
+ res.append("{color:#d04437}").append(suite.name).append("{color}");
+ res.append(" [[tests ").append(suite.failedTests);
+
+ if (suite.result != null && !suite.result.isEmpty())
+ res.append(' ').append(suite.result);
+
+ res.append('|').append(suite.webToBuild).append("]]\\n");
+
+ for (TestFailure failure : suite.testFailures) {
+ res.append("* ");
+
+ if (failure.suiteName != null && failure.testName != null)
+ res.append(failure.suiteName).append(": ").append(failure.testName);
+ else
+ res.append(failure.name);
+
+ FailureSummary recent = failure.histBaseBranch.recent;
+
+ if (recent != null) {
+ if (recent.failureRate != null) {
+ res.append(" - ").append(recent.failureRate).append("% fails in last ")
+ .append(recent.runs).append(" master runs.");
+ }
+ else if (recent.failures != null && recent.runs != null) {
+ res.append(" - ").append(recent.failures).append(" fails / ")
+ .append(recent.runs).append(" master runs.");
+ }
+ }
+
+ res.append("\\n");
+ }
+
+ res.append("\\n");
+ }
+
+ if (res.length() > 0) {
+ res.insert(0, "{panel:title=" + suiteName + ": Possible Blockers|" +
+ "borderStyle=dashed|borderColor=#ccc|titleBGColor=#F7D6C1}\\n")
+ .append("{panel}");
+ }
+ else {
+ res.append("{panel:title=").append(suiteName).append(": No blockers found!|")
+ .append("borderStyle=dashed|borderColor=#ccc|titleBGColor=#D6F7C1}{panel}");
+ }
+
+ res.append("\\n").append("[TeamCity *").append(suiteName).append("* Results|").append(webUrl).append(']');
+
+ return xmlEscapeText(res.toString());
+ }
+
}
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/model/current/UpdateInfo.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/model/current/UpdateInfo.java
index 381855d..84810c4 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/model/current/UpdateInfo.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/model/current/UpdateInfo.java
@@ -19,6 +19,7 @@ package org.apache.ignite.ci.web.model.current;
import org.apache.ignite.ci.IAnalyticsEnabledTeamcity;
import org.apache.ignite.ci.github.pure.IGitHubConnection;
+import org.apache.ignite.ci.jira.pure.IJiraIntegration;
/**
*
@@ -57,16 +58,17 @@ import org.apache.ignite.ci.github.pure.IGitHubConnection;
/**
* @param teamcity TeamCity to get info about tokens.
* @param gitHubConn GitHub integration associated with this server.
+ * @param jiraIntegration
*/
public void setJavaFlags(IAnalyticsEnabledTeamcity teamcity,
- IGitHubConnection gitHubConn) {
+ IGitHubConnection gitHubConn, IJiraIntegration jiraIntegration) {
if (teamcity.isTeamCityTokenAvailable())
javaFlags |= TEAMCITY_FLAG;
if (gitHubConn.isGitTokenAvailable())
javaFlags |= GITHUB_FLAG;
- if (teamcity.isJiraTokenAvailable())
+ if (jiraIntegration.isJiraTokenAvailable())
javaFlags |= JIRA_FLAG;
}
}
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/rest/TriggerBuilds.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/rest/TriggerBuilds.java
index f983758..d6f0169 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/rest/TriggerBuilds.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/rest/TriggerBuilds.java
@@ -36,6 +36,8 @@ import org.apache.ignite.ci.ITeamcity;
import org.apache.ignite.ci.conf.ServerIntegrationLinks;
import org.apache.ignite.ci.github.pure.IGitHubConnection;
import org.apache.ignite.ci.github.pure.IGitHubConnectionProvider;
+import org.apache.ignite.ci.jira.pure.IJiraIntegration;
+import org.apache.ignite.ci.jira.pure.IJiraIntegrationProvider;
import org.apache.ignite.ci.user.ICredentialsProv;
import org.apache.ignite.ci.tcbot.visa.TcBotTriggerAndSignOffService;
import org.apache.ignite.ci.web.CtxListener;
@@ -124,10 +126,11 @@ public class TriggerBuilds {
ITcHelper tcHelper = injector.getInstance(ITcHelper.class);
final ICredentialsProv creds = ICredentialsProv.get(req);
ITeamcity teamcity = tcHelper.server(srvId, creds);
+ IJiraIntegration jira = injector.getInstance(IJiraIntegrationProvider.class).server(srvId);
IGitHubConnection gh = injector.getInstance(IGitHubConnectionProvider.class).server(srvId);
- return new ServerIntegrationLinks(srvId, gh.gitApiUrl(), teamcity.getJiraApiUrl());
+ return new ServerIntegrationLinks(srvId, gh.gitApiUrl(), jira.getJiraApiUrl());
}).filter(Objects::nonNull).collect(Collectors.toSet());
}
}
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/rest/visa/TcBotVisaService.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/rest/visa/TcBotVisaService.java
index 241d2f7..f7bfa78 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/rest/visa/TcBotVisaService.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/rest/visa/TcBotVisaService.java
@@ -110,7 +110,8 @@ public class TcBotVisaService {
if (!prov.hasAccess(srvId))
throw ServiceUnauthorizedException.noCreds(srvId);
- TcBotTriggerAndSignOffService instance = CtxListener.getInjector(ctx).getInstance(TcBotTriggerAndSignOffService.class);
+ TcBotTriggerAndSignOffService instance = CtxListener.getInjector(ctx)
+ .getInstance(TcBotTriggerAndSignOffService.class);
return instance.currentVisaStatus(srvId, prov, suiteId, tcBranch);
}