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/17 14:40:38 UTC

[ignite-teamcity-bot] branch master updated: IGNITE-10930: PR-less contributions: handle PR less contributions branch naming - Fixes #107.

This is an automated email from the ASF dual-hosted git repository.

dpavlov pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ignite-teamcity-bot.git


The following commit(s) were added to refs/heads/master by this push:
     new 9517e45  IGNITE-10930: PR-less contributions: handle PR less contributions branch naming - Fixes #107.
9517e45 is described below

commit 9517e457aad1a77f95cb3a68a5fd35bb20fe5a3d
Author: Dmitriy Pavlov <dp...@apache.org>
AuthorDate: Thu Jan 17 17:40:23 2019 +0300

    IGNITE-10930: PR-less contributions: handle PR less contributions branch naming - Fixes #107.
    
    Signed-off-by: Dmitriy Pavlov <dp...@apache.org>
---
 conf/apache.auth.properties                        |  29 +-
 .../java/org/apache/ignite/ci/HelperConfig.java    |   5 +-
 .../ignite/ci/IAnalyticsEnabledTeamcity.java       |   1 +
 .../main/java/org/apache/ignite/ci/ITcHelper.java  |  17 -
 .../main/java/org/apache/ignite/ci/ITeamcity.java  |  36 --
 .../apache/ignite/ci/IgnitePersistentTeamcity.java |  35 +-
 .../apache/ignite/ci/IgniteTeamcityConnection.java |  56 +--
 .../main/java/org/apache/ignite/ci/TcHelper.java   | 177 +--------
 .../org/apache/ignite/ci/di/IgniteTcBotModule.java |  13 +-
 .../org/apache/ignite/ci/github/PullRequest.java   |   2 +-
 .../ci/github/ignited/IGitHubConnIgnited.java      |  11 -
 .../ignite/ci/github/pure/IGitHubConnection.java   |   2 +-
 .../java/org/apache/ignite/ci/jira/Fields.java     |   9 +
 .../java/org/apache/ignite/ci/jira/Status.java     |  13 +
 .../java/org/apache/ignite/ci/jira/Ticket.java     |  27 +-
 .../java/org/apache/ignite/ci/jira/Tickets.java    |   1 +
 .../ignite/ci/jira/ignited/IJiraIgnited.java       |  65 +++
 .../IJiraIgnitedProvider.java}                     |   7 +-
 .../apache/ignite/ci/jira/ignited/JiraIgnited.java |  90 +++++
 .../JiraIgnitedModule.java}                        |  12 +-
 .../JiraIgnitedProvider.java}                      |  27 +-
 .../ignite/ci/jira/ignited/JiraTicketDao.java      |  17 +-
 .../ignite/ci/jira/ignited/JiraTicketSync.java     |  54 ++-
 .../ignite/ci/jira/ignited/TicketCompacted.java    |  26 +-
 .../ci/jira/{ => pure}/IJiraIntegration.java       |  61 +--
 .../jira/{ => pure}/IJiraIntegrationProvider.java  |   2 +-
 .../org/apache/ignite/ci/jira/{ => pure}/Jira.java |  92 ++++-
 .../ci/jira/{ => pure}/JiraIntegrationModule.java  |   4 +-
 .../jira/{ => pure}/JiraIntegrationProvider.java   |   6 +-
 .../apache/ignite/ci/observer/ObserverTask.java    |  14 +-
 .../ignite/ci/tcbot/chain/PrChainsProcessor.java   |   8 +-
 .../ci/tcbot/visa/ContributionCheckStatus.java     |   7 +-
 .../ignite/ci/tcbot/visa/ContributionToCheck.java  |   5 +-
 .../tcbot/visa/TcBotTriggerAndSignOffService.java  | 438 +++++++++++++++++----
 .../ci/teamcity/ignited/ITeamcityIgnited.java      |   2 +
 .../ci/teamcity/ignited/TeamcityIgnitedImpl.java   |  77 ++--
 .../ci/teamcity/ignited/buildref/BuildRefDao.java  |   2 +-
 .../ignite/ci/teamcity/pure/ITeamcityConn.java     |   5 +
 .../org/apache/ignite/ci/web/model/Version.java    |   2 +-
 .../java/org/apache/ignite/ci/web/model/Visa.java  |   6 +-
 .../model/current/ChainAtServerCurrentStatus.java  |  18 +-
 .../ignite/ci/web/model/current/UpdateInfo.java    |   6 +-
 .../apache/ignite/ci/web/rest/TriggerBuilds.java   |   5 +-
 .../ignite/ci/web/rest/pr/GetPrTestFailures.java   |   9 +-
 .../ignite/ci/web/rest/visa/TcBotVisaService.java  |  10 +-
 ignite-tc-helper-web/src/main/webapp/js/prs-1.1.js |  17 +-
 ignite-tc-helper-web/src/main/webapp/prs.html      |  80 ++--
 .../ci/tcbot/chain/MockBasedTcBotModule.java       |   8 +-
 .../ignited/IgnitedTcInMemoryIntegrationTest.java  |   2 +-
 .../ci/teamcity/ignited/TeamcityIgnitedMock.java   |   2 +
 50 files changed, 997 insertions(+), 623 deletions(-)

diff --git a/conf/apache.auth.properties b/conf/apache.auth.properties
index dcc4f07..eb56fbb 100644
--- a/conf/apache.auth.properties
+++ b/conf/apache.auth.properties
@@ -1,15 +1,32 @@
+## Teamcity Integration Parameters
+# Teamcity Host, HTTPs is highly recommended because of Basic Auth used.
 host=https://ci.ignite.apache.org/
- 
+
+# TC bot downloaded logs path
 logs=apache_logs
 
+## GitHub integration parameters.
+# REST API Url for GitHub. Includes owner (apache) and project (ignite)
 git.api_url=https://api.github.com/repos/apache/ignite/
+
+# Specify GitHub Auth token (if needed), go to User->Settings->Developers options->create new.
+# Created token needs to be encrypted using org.apache.ignite.ci.conf.PasswordEncoder
+github.auth_token=
+
+## Branch identification rules
+# PR-less contribution branch name mandatory prefix
+git.branch_prefix=ignite-
+
+## JIRA integration parameters.
 jira.api_url=https://issues.apache.org/jira/rest/api/2/
 
+# JIRA Url, HTTPs is highly recommended because of Basic Auth used.
 jira.url=https://issues.apache.org/jira/
-jira.ticket_template=IGNITE-
 
-#specify JIRA Auth token (if needed)
-jira.auth_token=
+# Ticket template: project code and dash.
+jira.ticket_template=IGNITE-
 
-#specify GitHub Auth token (if needed)
-github.auth_token=
\ No newline at end of file
+# Specify JIRA Auth token (if needed).
+# Base-64 pre-encoded username and password, which will be included into Basic Auth requests.
+# Encoded token needs to be encrypted using org.apache.ignite.ci.conf.PasswordEncoder
+jira.auth_token=
\ No newline at end of file
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..a0cd102 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
@@ -51,6 +51,9 @@ public class HelperConfig {
     /** GitHub authorization token property name. */
     public static final String GITHUB_AUTH_TOKEN = "github.auth_token";
 
+    /** Git branch naming prefix for PRLess contributions. */
+    public static final String GIT_BRANCH_PREFIX = "git.branch_prefix";
+
     /** JIRA authorization token property name. */
     public static final String JIRA_AUTH_TOKEN = "jira.auth_token";
 
@@ -159,7 +162,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/IAnalyticsEnabledTeamcity.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/IAnalyticsEnabledTeamcity.java
index 2499adc..00e524b 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/IAnalyticsEnabledTeamcity.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/IAnalyticsEnabledTeamcity.java
@@ -20,6 +20,7 @@ package org.apache.ignite.ci;
 /**
  * Combination of REST data and persisted statistics.
  */
+@Deprecated
 public interface IAnalyticsEnabledTeamcity extends ITeamcity, ITcAnalytics {
     public void init(ITeamcity conn);
 }
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..bfa6376 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
@@ -512,6 +512,11 @@ public class IgnitePersistentTeamcity implements IAnalyticsEnabledTeamcity, ITea
         return teamcity.getProjects();
     }
 
+    /** {@inheritDoc} */
+    @Override public String gitBranchPrefix() {
+        return teamcity.gitBranchPrefix();
+    }
+
     @Override public Statistics getStatistics(int buildId) {
         return teamcity.getStatistics(buildId);
     }
@@ -540,36 +545,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..6a75fc8 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;
@@ -115,6 +110,10 @@ public class IgniteTeamcityConnection implements ITeamcity {
     /** Build logger processing running. */
     private ConcurrentHashMap<Integer, CompletableFuture<LogCheckTask>> buildLogProcessingRunning = new ConcurrentHashMap<>();
 
+    /** Git branch prefix for seach TC runs in PR-less contributions. */
+    @NotNull
+    private String gitBranchPrefix;
+
     public Executor getExecutor() {
         return executor;
     }
@@ -140,8 +139,7 @@ public class IgniteTeamcityConnection implements ITeamcity {
             logger.error("Failed to set credentials", e);
         }
 
-        setJiraToken(HelperConfig.prepareJiraHttpAuthToken(props));
-        setJiraApiUrl(props.getProperty(HelperConfig.JIRA_API_URL));
+        this.gitBranchPrefix = props.getProperty(HelperConfig.GIT_BRANCH_PREFIX, "ignite-");
 
         final File logsDirFile = HelperConfig.resolveLogs(workDir, props);
 
@@ -160,45 +158,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
@@ -377,6 +336,11 @@ public class IgniteTeamcityConnection implements ITeamcity {
     }
 
     /** {@inheritDoc} */
+    @Override public String gitBranchPrefix() {
+        return gitBranchPrefix;
+    }
+
+    /** {@inheritDoc} */
     @Override public List<BuildType> getBuildTypes(String projectId) {
         return sendGetXmlParseJaxb(host + "app/rest/latest/projects/" + projectId, Project.class)
             .getBuildTypesNonNull();
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 cbd550b..a1e275d 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
@@ -17,45 +17,21 @@
 
 package org.apache.ignite.ci;
 
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.google.gson.Gson;
-import java.util.List;
 import java.util.Objects;
 import java.util.concurrent.atomic.AtomicBoolean;
 import javax.inject.Inject;
-import org.apache.ignite.ci.jira.Tickets;
-import org.apache.ignite.ci.tcbot.issue.IssueDetector;
 import org.apache.ignite.ci.issue.IssuesStorage;
-import org.apache.ignite.ci.jira.IJiraIntegration;
-import org.apache.ignite.ci.tcbot.chain.PrChainsProcessor;
-import org.apache.ignite.ci.tcmodel.result.Build;
-import org.apache.ignite.ci.teamcity.ignited.IStringCompactor;
-import org.apache.ignite.ci.teamcity.ignited.ITeamcityIgnited;
-import org.apache.ignite.ci.teamcity.ignited.ITeamcityIgnitedProvider;
-import org.apache.ignite.ci.teamcity.ignited.buildtype.BuildTypeRefCompacted;
-import org.apache.ignite.ci.teamcity.ignited.fatbuild.FatBuildCompacted;
+import org.apache.ignite.ci.tcbot.issue.IssueDetector;
 import org.apache.ignite.ci.teamcity.restcached.ITcServerProvider;
 import org.apache.ignite.ci.user.ICredentialsProv;
 import org.apache.ignite.ci.user.UserAndSessionsStorage;
-import org.apache.ignite.ci.web.model.JiraCommentResponse;
-import org.apache.ignite.ci.web.model.Visa;
-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.jetbrains.annotations.Nullable;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import static org.apache.ignite.ci.util.XmlUtil.xmlEscapeText;
 
 /**
  * TC Bot implementation. To be migrated to smaller injected classes
  */
 @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 +46,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 +90,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/di/IgniteTcBotModule.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/di/IgniteTcBotModule.java
index bbb68ec..e45db99 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/di/IgniteTcBotModule.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/di/IgniteTcBotModule.java
@@ -21,6 +21,10 @@ import com.google.inject.AbstractModule;
 import com.google.inject.Injector;
 import com.google.inject.internal.SingletonScope;
 import com.google.inject.matcher.Matchers;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import javax.inject.Provider;
 import org.apache.ignite.Ignite;
 import org.apache.ignite.ci.ITcHelper;
 import org.apache.ignite.ci.TcHelper;
@@ -28,7 +32,7 @@ import org.apache.ignite.ci.db.Ignite1Init;
 import org.apache.ignite.ci.di.cache.GuavaCachedModule;
 import org.apache.ignite.ci.di.scheduler.SchedulerModule;
 import org.apache.ignite.ci.github.ignited.GitHubIgnitedModule;
-import org.apache.ignite.ci.jira.JiraIntegrationModule;
+import org.apache.ignite.ci.jira.ignited.JiraIgnitedModule;
 import org.apache.ignite.ci.observer.BuildObserver;
 import org.apache.ignite.ci.observer.ObserverTask;
 import org.apache.ignite.ci.tcbot.TcBotBusinessServicesModule;
@@ -40,11 +44,6 @@ import org.apache.ignite.ci.web.TcUpdatePool;
 import org.apache.ignite.ci.web.model.hist.VisasHistoryStorage;
 import org.apache.ignite.ci.web.rest.exception.ServiceStartingException;
 
-import javax.inject.Provider;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
 /**
  *
  */
@@ -81,7 +80,7 @@ public class IgniteTcBotModule extends AbstractModule {
         bind(BackgroundUpdater.class).in(new SingletonScope());
 
         install(new TeamcityIgnitedModule());
-        install(new JiraIntegrationModule());
+        install(new JiraIgnitedModule());
         install(new GitHubIgnitedModule());
         install(new SchedulerModule());
         install(new TcBotBusinessServicesModule());
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/github/PullRequest.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/github/PullRequest.java
index 1763274..e434417 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/github/PullRequest.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/github/PullRequest.java
@@ -61,7 +61,7 @@ public class PullRequest implements IVersionedEntity {
      * @return Pull Request time update.
      */
     public String getTimeUpdate() {
-        return  updatedAt;
+        return updatedAt;
     }
 
     /**
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/github/ignited/IGitHubConnIgnited.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/github/ignited/IGitHubConnIgnited.java
index 81d36c9..c61619b 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/github/ignited/IGitHubConnIgnited.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/github/ignited/IGitHubConnIgnited.java
@@ -35,17 +35,6 @@ public interface IGitHubConnIgnited {
     /** */
     public PullRequest getPullRequest(int prNum);
 
-    /** */
-    @AutoProfiling
-    @Nullable
-    public default PullRequest getPullRequest(String branchForTc) {
-        Integer prId = IGitHubConnection.convertBranchToId(branchForTc);
-
-        Preconditions.checkNotNull(prId, "Invalid TC branch name");
-
-        return getPullRequest(prId);
-    }
-
     /**
      * Send POST request with given body.
      *
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/github/pure/IGitHubConnection.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/github/pure/IGitHubConnection.java
index 2bf08ca..b2ab22a 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/github/pure/IGitHubConnection.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/github/pure/IGitHubConnection.java
@@ -55,7 +55,7 @@ public interface IGitHubConnection {
     /**
      * @return PR id from string "pull/XXXX/head"
      */
-    public @Nullable static Integer convertBranchToId(String branchForTc) {
+    @Nullable public static Integer convertBranchToId(String branchForTc) {
         Integer res = null;
 
         if (Objects.isNull(branchForTc))
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/Fields.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/Fields.java
index f9aa8e9..2f6e271 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/Fields.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/Fields.java
@@ -17,10 +17,19 @@
 
 package org.apache.ignite.ci.jira;
 
+import com.google.common.base.MoreObjects;
+
 /**
  *
  */
 public class Fields {
     /** Ticket status. */
     public Status status;
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return MoreObjects.toStringHelper(this)
+            .add("status", status)
+            .toString();
+    }
 }
\ No newline at end of file
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/Status.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/Status.java
index 62ceeb3..94d2131 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/Status.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/Status.java
@@ -17,10 +17,18 @@
 
 package org.apache.ignite.ci.jira;
 
+import com.google.common.base.MoreObjects;
+
 /**
  * Status for Jira ticket.
  */
 public class Status {
+    /** Patch Available status name. */
+    public static final String PA_NAME = "Patch Available";
+
+    /** In Progress status name. */
+    public static final String IP_NAME = "In Progress";
+
     /** Status text (open, resolved, etc). */
     public String name;
 
@@ -30,4 +38,9 @@ public class Status {
     public Status(String name) {
         this.name = name;
     }
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return name;
+    }
 }
\ No newline at end of file
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/Ticket.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/Ticket.java
index 5160471..dc8806f 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/Ticket.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/Ticket.java
@@ -17,6 +17,8 @@
 
 package org.apache.ignite.ci.jira;
 
+import com.google.common.base.MoreObjects;
+
 /**
  * See example of GSON here
  * https://issues.apache.org/jira/rest/api/2/issue/IGNITE-123
@@ -32,11 +34,11 @@ public class Ticket {
     public Fields fields;
 
     /**
-     * @param ticketTemplate Ticket name template.
-     * @return Ignite id (like 123 in IGNITE-123).
+     * @param ticketPrefix Ticket name fixed prefix.
+     * @return Ignite ticket Number ignoring project code (like 123 in IGNITE-123).
      */
-    public int igniteId(String ticketTemplate) {
-        return Integer.valueOf(key.substring(ticketTemplate.length()));
+    public int igniteId(String ticketPrefix) {
+        return Integer.valueOf(key.substring(ticketPrefix.length()));
     }
 
     /**
@@ -45,4 +47,21 @@ public class Ticket {
     public String status() {
         return fields.status.name;
     }
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return MoreObjects.toStringHelper(this)
+            .add("id", id)
+            .add("key", key)
+            .add("fields", fields)
+            .toString();
+    }
+
+    /**
+     * Checks if ticket relates to some Active (In progress/Patch Available) Contribution.
+     */
+    public boolean isActiveContribution() {
+        return Status.PA_NAME.equals(status())
+            || Status.IP_NAME.equals(status());
+    }
 }
\ No newline at end of file
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/Tickets.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/Tickets.java
index f265b37..930a1bf 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/Tickets.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/Tickets.java
@@ -24,6 +24,7 @@ import java.util.Collections;
  * See example of GSON here
  * https://issues.apache.org/jira/rest/api/2/search?jql=project%20=%20IGNITE%20order%20by%20updated%20DESC&fields=status
  */
+@SuppressWarnings("PublicField")
 public class Tickets {
     /** Start at. */
     public int startAt;
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/ignited/IJiraIgnited.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/ignited/IJiraIgnited.java
new file mode 100644
index 0000000..be1011a
--- /dev/null
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/ignited/IJiraIgnited.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.ci.jira.ignited;
+
+import java.io.IOException;
+import java.util.Set;
+import org.apache.ignite.ci.jira.Ticket;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ *
+ */
+public interface IJiraIgnited {
+    /**
+     *
+     */
+    @NotNull public String ticketPrefix();
+
+    /**
+     *
+     */
+    @NotNull public String projectName();
+
+
+    /**
+     * @return Jira tickets.
+     */
+    public Set<Ticket> getTickets();
+
+    /**
+     * @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);
+
+    /**
+     * @param id Ticket full ID (e.g IGNITE-8331)
+     * @return URL which is used as link to Jira ticket with specified name.
+     */
+    public String generateTicketUrl(String id);
+
+
+    /**
+     * @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 postJiraComment(String ticket, String comment) throws IOException;
+}
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/IJiraIntegrationProvider.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/ignited/IJiraIgnitedProvider.java
similarity index 86%
copy from ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/IJiraIntegrationProvider.java
copy to ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/ignited/IJiraIgnitedProvider.java
index 095379b..8ca0db5 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/IJiraIntegrationProvider.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/ignited/IJiraIgnitedProvider.java
@@ -14,15 +14,14 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-package org.apache.ignite.ci.jira;
+package org.apache.ignite.ci.jira.ignited;
 
 /**
  *
  */
-public interface IJiraIntegrationProvider {
+public interface IJiraIgnitedProvider {
     /**
      * @param srvId Server id.
      */
-    public IJiraIntegration server(String srvId);
+    public IJiraIgnited server(String srvId);
 }
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/ignited/JiraIgnited.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/ignited/JiraIgnited.java
new file mode 100644
index 0000000..1ffe5ec
--- /dev/null
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/ignited/JiraIgnited.java
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.ci.jira.ignited;
+
+import java.io.IOException;
+import java.util.Set;
+import javax.inject.Inject;
+import org.apache.ignite.ci.jira.Ticket;
+import org.apache.ignite.ci.jira.pure.IJiraIntegration;
+import org.apache.ignite.ci.teamcity.ignited.ITeamcityIgnited;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ *
+ */
+class JiraIgnited implements IJiraIgnited {
+    /** Pure HTTP Jira connection. */
+    private IJiraIntegration jira;
+
+    /** Jira ticket DAO. */
+    @Inject private JiraTicketDao jiraTicketDao;
+
+    /** Jira ticket Sync. */
+    @Inject private JiraTicketSync jiraTicketSync;
+
+    /** Server id. */
+    private String srvId;
+
+    /** Server id mask high. */
+    private int srvIdMaskHigh;
+
+    /**
+     * @param jira Pure Jira integration.
+     */
+    public void init(IJiraIntegration jira) {
+        this.jira = jira;
+
+        srvId = jira.getServiceId();
+
+        srvIdMaskHigh = ITeamcityIgnited.serverIdToInt(srvId);
+
+        jiraTicketDao.init();
+    }
+
+    /** {@inheritDoc} */
+    @Override public String ticketPrefix() {
+        return jira.ticketPrefix();
+    }
+
+    /** {@inheritDoc} */
+    @NotNull @Override public String projectName() {
+        return jira.projectName();
+    }
+
+    /** {@inheritDoc} */
+    @Override public Set<Ticket> getTickets() {
+        jiraTicketSync.ensureActualizeJiraTickets(srvId);
+
+        return jiraTicketDao.getTickets(srvIdMaskHigh, ticketPrefix());
+    }
+
+    /** {@inheritDoc} */
+    @Override public String generateCommentUrl(String ticketFullName, int commentId) {
+        return jira.generateCommentUrl(ticketFullName, commentId);
+    }
+
+    /** {@inheritDoc} */
+    @Override public String generateTicketUrl(String id) {
+        return jira.generateTicketUrl(id);
+    }
+
+    /** {@inheritDoc} */
+    @Override public String postJiraComment(String ticket, String comment) throws IOException {
+        return jira.postJiraComment(ticket, comment);
+    }
+}
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/JiraIntegrationModule.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/ignited/JiraIgnitedModule.java
similarity index 75%
copy from ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/JiraIntegrationModule.java
copy to ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/ignited/JiraIgnitedModule.java
index a4b6bfc..ef237d4 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/JiraIntegrationModule.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/ignited/JiraIgnitedModule.java
@@ -14,19 +14,19 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-package org.apache.ignite.ci.jira;
+package org.apache.ignite.ci.jira.ignited;
 
 import com.google.inject.AbstractModule;
-import com.google.inject.internal.SingletonScope;
+import org.apache.ignite.ci.jira.pure.JiraIntegrationModule;
 
 /**
  *
  */
-public class JiraIntegrationModule extends AbstractModule {
+public class JiraIgnitedModule extends AbstractModule {
     /** {@inheritDoc} */
     @Override protected void configure() {
-        bind(IJiraIntegration.class).to(Jira.class);
-        bind(IJiraIntegrationProvider.class).to(JiraIntegrationProvider.class).in(new SingletonScope());
+        bind(IJiraIgnitedProvider.class).to(JiraIgnitedProvider.class);
+
+        install(new JiraIntegrationModule());
     }
 }
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/JiraIntegrationProvider.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/ignited/JiraIgnitedProvider.java
similarity index 71%
copy from ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/JiraIntegrationProvider.java
copy to ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/ignited/JiraIgnitedProvider.java
index 62a6618..451c75f 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/JiraIntegrationProvider.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/ignited/JiraIgnitedProvider.java
@@ -14,8 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-package org.apache.ignite.ci.jira;
+package org.apache.ignite.ci.jira.ignited;
 
 import com.google.common.base.Strings;
 import com.google.common.cache.Cache;
@@ -24,19 +23,22 @@ import com.google.inject.Provider;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
 import javax.inject.Inject;
+import org.apache.ignite.ci.jira.pure.IJiraIntegration;
+import org.apache.ignite.ci.jira.pure.IJiraIntegrationProvider;
 import org.apache.ignite.ci.util.ExceptionUtil;
 
 /**
- * Class for providing {@link IJiraIntegration} instance for specified Jira
- * server. Instance for each server is cached with defining instance
- * expiration time.
+ *
  */
-public class JiraIntegrationProvider implements IJiraIntegrationProvider {
+public class JiraIgnitedProvider implements IJiraIgnitedProvider {
+    /** */
+    @Inject IJiraIntegrationProvider pureProv;
+
     /** */
-    @Inject Provider<IJiraIntegration> factory;
+    @Inject Provider<JiraIgnited> factory;
 
     /** */
-    private final Cache<String, IJiraIntegration> srvs
+    private final Cache<String, IJiraIgnited> srvs
         = CacheBuilder.newBuilder()
         .maximumSize(100)
         .expireAfterAccess(16, TimeUnit.MINUTES)
@@ -44,14 +46,15 @@ public class JiraIntegrationProvider implements IJiraIntegrationProvider {
         .build();
 
     /** */
-    @Override public IJiraIntegration server(String srvId) {
+    @Override public IJiraIgnited server(String srvId) {
         try {
             return srvs.get(Strings.nullToEmpty(srvId), () -> {
-                IJiraIntegration jiraIntegration = factory.get();
+                IJiraIntegration pure = pureProv.server(srvId);
 
-                jiraIntegration.init(srvId);
+                JiraIgnited ignited = factory.get();
+                ignited.init(pure);
 
-                return jiraIntegration;
+                return ignited;
             });
         }
         catch (ExecutionException e) {
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/ignited/JiraTicketDao.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/ignited/JiraTicketDao.java
index ee3bb30..6502488 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/ignited/JiraTicketDao.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/ignited/JiraTicketDao.java
@@ -44,7 +44,7 @@ public class JiraTicketDao {
     /** Ignite provider. */
     @Inject private Provider<Ignite> igniteProvider;
 
-    /** Builds cache. */
+    /** JIRA tickets cache : (srvId || ticketNuber) -> Ticket data compacted. */
     private IgniteCache<Long, TicketCompacted> jiraCache;
 
     /** Compactor. */
@@ -59,9 +59,10 @@ public class JiraTicketDao {
 
     /**
      * @param srvIdMaskHigh Server id mask high.
+     * @param ticketPrefix Fixed prefix for JIRA tickets.
      * @return Jira tickets.
      */
-    public Set<Ticket> getTickets(int srvIdMaskHigh) {
+    public Set<Ticket> getTickets(int srvIdMaskHigh, String ticketPrefix) {
         Preconditions.checkNotNull(jiraCache, "init() was not called");
         long srvId = (long) srvIdMaskHigh << 32;
 
@@ -69,7 +70,7 @@ public class JiraTicketDao {
 
         for (Cache.Entry<Long, TicketCompacted> entry : jiraCache) {
             if ((entry.getKey() & srvId) == srvId)
-                res.add(entry.getValue().toTicket(compactor));
+                res.add(entry.getValue().toTicket(compactor, ticketPrefix));
         }
 
         return res;
@@ -79,7 +80,7 @@ public class JiraTicketDao {
      * Combine server and project into key for storage.
      *
      * @param srvIdMaskHigh Server id mask high.
-     * @param igniteId Ticket.
+     * @param igniteId Ticket number without project name.
      * @return Key from server-project pair.
      */
     public static long ticketToCacheKey(int srvIdMaskHigh, int igniteId) {
@@ -91,10 +92,10 @@ public class JiraTicketDao {
      *
      * @param srvIdMaskHigh Server id mask high.
      * @param chunk Chunk.
-     * @param ticketTemplate Ticket name template.
+     * @param ticketPrefix Ticket name template.
      */
     @AutoProfiling
-    public void saveChunk(int srvIdMaskHigh, Collection<Ticket> chunk, String ticketTemplate) {
+    public void saveChunk(int srvIdMaskHigh, Collection<Ticket> chunk, String ticketPrefix) {
         Preconditions.checkNotNull(jiraCache, "init() was not called");
 
         if (F.isEmpty(chunk))
@@ -103,8 +104,8 @@ public class JiraTicketDao {
         HashMap<Long, TicketCompacted> compactedTickets = new HashMap<>(U.capacity(chunk.size()));
 
         for (Ticket ticket : chunk) {
-            long key = ticketToCacheKey(srvIdMaskHigh, ticket.igniteId(ticketTemplate));
-            TicketCompacted val = new TicketCompacted(ticket, compactor, ticketTemplate);
+            long key = ticketToCacheKey(srvIdMaskHigh, ticket.igniteId(ticketPrefix));
+            TicketCompacted val = new TicketCompacted(ticket, compactor, ticketPrefix);
 
             compactedTickets.put(key, val);
         }
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/ignited/JiraTicketSync.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/ignited/JiraTicketSync.java
index b7fbfd7..178ced6 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/ignited/JiraTicketSync.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/ignited/JiraTicketSync.java
@@ -22,16 +22,18 @@ import java.util.concurrent.TimeUnit;
 import javax.inject.Inject;
 import org.apache.ignite.ci.di.MonitoredTask;
 import org.apache.ignite.ci.di.scheduler.IScheduler;
-import org.apache.ignite.ci.jira.IJiraIntegration;
-import org.apache.ignite.ci.jira.IJiraIntegrationProvider;
+import org.apache.ignite.ci.jira.pure.IJiraIntegration;
+import org.apache.ignite.ci.jira.pure.IJiraIntegrationProvider;
 import org.apache.ignite.ci.jira.Ticket;
 import org.apache.ignite.ci.jira.Tickets;
-import org.apache.ignite.ci.teamcity.pure.ITeamcityConn;
-import org.apache.ignite.ci.user.ICredentialsProv;
+import org.apache.ignite.ci.teamcity.ignited.ITeamcityIgnited;
 import org.apache.ignite.internal.util.typedef.F;
+import org.jetbrains.annotations.NotNull;
+
+import static org.apache.ignite.ci.util.UrlUtil.escape;
 
 /**
- * 
+ * Sync serving requests for all JIRA servers.
  */
 public class JiraTicketSync {
     /** Scheduler. */
@@ -44,26 +46,36 @@ public class JiraTicketSync {
     @Inject IJiraIntegrationProvider jiraIntegrationProvider;
 
     /**
-     * @param taskName Task name.
-     * @param srvIdMaskHigh Server id mask high.
-     * @param creds Credentials.
-     * @param conn Connection.
+     * @param srvId Server ID
      */
-    public void ensureActualizeJiraTickets(String taskName, int srvIdMaskHigh, ICredentialsProv creds, ITeamcityConn conn) {
-        scheduler.sheduleNamed(taskName, () -> actualizeJiraTickets(srvIdMaskHigh, conn, creds), 15, TimeUnit.MINUTES);
+    public void ensureActualizeJiraTickets(String srvId) {
+        scheduler.sheduleNamed(taskName("actualizeJiraTickets", srvId),
+            () -> actualizeJiraTickets(srvId), 15, TimeUnit.MINUTES);
     }
 
     /**
-     * @param srvIdMaskHigh Server id mask high.
-     * @param conn Connection.
-     * @param creds Credentials.
+     * @param taskName Task name.
+     * @param srvId Service ID
+     * @return Task name concatenated with server name.
+     */
+    @NotNull
+    private String taskName(String taskName, String srvId) {
+        return JiraTicketSync.class.getSimpleName() + "." + taskName + "." + srvId;
+    }
+    /**
+     * @param srvId Server internal identification.
      */
     @MonitoredTask(name = "Actualize Jira", nameExtArgsIndexes = {0})
-    private String actualizeJiraTickets(int srvIdMaskHigh, ITeamcityConn conn, ICredentialsProv creds) {
-        String srvId = conn.serverId();
+    protected String actualizeJiraTickets(String srvId) {
+        int srvIdMaskHigh = ITeamcityIgnited.serverIdToInt(srvId);
         IJiraIntegration jira = jiraIntegrationProvider.server(srvId);
-        String url = "search?jql=project%20=%20IGNITE%20order%20by%20updated%20DESC&fields=status&maxResults=100";
-        Tickets tickets = jira.getTickets(srvId, creds, url);
+
+        String projectName = jira.projectName();
+        String baseUrl = "search?jql=" + escape("project=" + projectName + " order by updated DESC")
+            + "&fields=status&maxResults=100";
+
+        String url = baseUrl;
+        Tickets tickets = jira.getTicketsPage(srvId, url);
         Collection<Ticket> page = tickets.issuesNotNull();
 
         if (F.isEmpty(page))
@@ -74,16 +86,16 @@ public class JiraTicketSync {
         int ticketsSaved = page.size();
 
         while (tickets.nextStart() > 0) {
-            url = "search?jql=project%20=%20IGNITE%20order%20by%20updated%20DESC&fields=status&maxResults=100&startAt=" +
-                tickets.nextStart();
+            url = baseUrl + "&startAt=" + tickets.nextStart();
 
-            tickets = jira.getTickets(srvId, creds, url);
+            tickets = jira.getTicketsPage(srvId, url);
 
             page = tickets.issuesNotNull();
 
             if (F.isEmpty(page))
                 break;
 
+            //todo find not updated chunk and exit
             jiraDao.saveChunk(srvIdMaskHigh, page, jira.ticketPrefix());
 
             ticketsSaved += page.size();
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/ignited/TicketCompacted.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/ignited/TicketCompacted.java
index 094ae6e..d1a22f9 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/ignited/TicketCompacted.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/ignited/TicketCompacted.java
@@ -17,6 +17,7 @@
 
 package org.apache.ignite.ci.jira.ignited;
 
+import com.google.common.base.Objects;
 import org.apache.ignite.ci.jira.Fields;
 import org.apache.ignite.ci.jira.Status;
 import org.apache.ignite.ci.jira.Ticket;
@@ -48,15 +49,36 @@ public class TicketCompacted {
 
     /**
      * @param comp Compactor.
+     * @param ticketPrefix Ticket name fixed prefix for the project.
      */
-    public Ticket toTicket(IStringCompactor comp) {
+    public Ticket toTicket(IStringCompactor comp, String ticketPrefix) {
         Ticket ticket = new Ticket();
 
         ticket.id = id;
-        ticket.key = "IGNITE-" + igniteId;
+        ticket.key = ticketPrefix + igniteId;
         ticket.fields = new Fields();
         ticket.fields.status = new Status(comp.getStringFromId(status));
 
         return ticket;
     }
+
+    /** {@inheritDoc} */
+    @Override public boolean equals(Object o) {
+        if (this == o)
+            return true;
+
+        if (o == null || getClass() != o.getClass())
+            return false;
+
+        TicketCompacted compacted = (TicketCompacted)o;
+
+        return id == compacted.id &&
+            igniteId == compacted.igniteId &&
+            status == compacted.status;
+    }
+
+    /** {@inheritDoc} */
+    @Override public int hashCode() {
+        return Objects.hashCode(id, igniteId, status);
+    }
 }
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/IJiraIntegration.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/pure/IJiraIntegration.java
similarity index 62%
rename from ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/IJiraIntegration.java
rename to ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/pure/IJiraIntegration.java
index c66a736..6ff8fea 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/IJiraIntegration.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/pure/IJiraIntegration.java
@@ -15,64 +15,71 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.ci.jira;
+package org.apache.ignite.ci.jira.pure;
 
-import org.apache.ignite.ci.web.model.Visa;
-import org.apache.ignite.ci.user.ICredentialsProv;
+import java.io.IOException;
+import org.apache.ignite.ci.jira.Tickets;
 import org.jetbrains.annotations.NotNull;
 
 /**
  * Reperesents methods to provide interaction with Jira servers.
  */
 public interface IJiraIntegration {
-    /** Message to show user when JIRA ticket was successfully commented by the Bot. */
-    public static String JIRA_COMMENTED = "JIRA commented.";
+
+    /** @return JIRA ticket prefix. */
+    @NotNull public String ticketPrefix();
 
     /**
-     * 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);
+    @NotNull public default String projectName() {
+        return ticketPrefix().replaceAll("-", "");
+    }
+
+    /**
+     * @return Internal identified service ID.
+     */
+    public void init(String srvId);
+
+    /**
+     * @return Internal identified service ID.
+     */
+    public String getServiceId();
+
+    /**
+     * @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 postJiraComment(String ticket, String comment) throws IOException;
 
     /**
      * Produce wrapper for collection of Jira tickets for given server.
      *
      * @param srvId Server id.
-     * @param prov Prov.
-     * @param ticketId Ticket id.
+     * @param url Tickets loading URL and parameters.
      * @return Jira tickets.
      */
-    public Tickets getTickets(String srvId, ICredentialsProv prov, String ticketId);
+    public Tickets getTicketsPage(@Deprecated String srvId, String url);
 
     /** */
     public String jiraUrl();
 
-    /** @return JIRA ticket prefix. */
-    @NotNull public String ticketPrefix();
-
-    /** */
-    public void init(String srvId);
-
     /**
      * @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/IJiraIntegrationProvider.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/pure/IJiraIntegrationProvider.java
similarity index 95%
rename from ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/IJiraIntegrationProvider.java
rename to ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/pure/IJiraIntegrationProvider.java
index 095379b..984233b 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/IJiraIntegrationProvider.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/pure/IJiraIntegrationProvider.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.ci.jira;
+package org.apache.ignite.ci.jira.pure;
 
 /**
  *
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/Jira.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/pure/Jira.java
similarity index 50%
rename from ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/Jira.java
rename to ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/pure/Jira.java
index b413193..e5a006e 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/Jira.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/pure/Jira.java
@@ -15,26 +15,30 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.ci.jira;
+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.ITcHelper;
-import org.apache.ignite.ci.user.ICredentialsProv;
-import org.apache.ignite.ci.web.model.Visa;
+import org.apache.ignite.ci.di.AutoProfiling;
+import org.apache.ignite.ci.jira.Tickets;
+import org.apache.ignite.ci.util.HttpUtil;
 import org.jetbrains.annotations.NotNull;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import static com.google.common.base.Strings.isNullOrEmpty;
 
 /**
  *
  */
-public class Jira implements IJiraIntegration {
-    /** */
-    @Inject ITcHelper helper;
+class Jira implements IJiraIntegration {
+    /** Logger. */
+    private static final Logger logger = LoggerFactory.getLogger(Jira.class);
 
     /** */
     private String jiraUrl;
@@ -42,8 +46,16 @@ public class Jira implements IJiraIntegration {
     /** JIRA ticket prefix. */
     @NotNull private String jiraTicketPrefix;
 
+    /** JIRA authorization token. */
+    private String jiraBasicAuthTok;
+
+    /** URL for JIRA integration. */
+    private String jiraApiUrl;
+    private String srvId;
+
     /** {@inheritDoc} */
     @Override public void init(String srvId) {
+        this.srvId = srvId;
         final File workDir = HelperConfig.resolveWorkDir();
 
         final String cfgName = HelperConfig.prepareConfigName(srvId);
@@ -53,8 +65,16 @@ public 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;
@@ -66,14 +86,18 @@ public 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);
-    }
-
-    /** {@inheritDoc} */
-    @Override public Tickets getTickets(String srvId, ICredentialsProv prov, String url) {
-        return helper.getJiraTickets(srvId, prov, url);
+    @Override public Tickets getTicketsPage(String srvId, 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} */
@@ -90,4 +114,40 @@ public 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 postJiraComment(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);
+    }
+
+    public String getServiceId() {
+        return srvId;
+    }
 }
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/JiraIntegrationModule.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/pure/JiraIntegrationModule.java
similarity index 94%
rename from ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/JiraIntegrationModule.java
rename to ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/pure/JiraIntegrationModule.java
index a4b6bfc..3c19a9f 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/JiraIntegrationModule.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/pure/JiraIntegrationModule.java
@@ -15,13 +15,13 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.ci.jira;
+package org.apache.ignite.ci.jira.pure;
 
 import com.google.inject.AbstractModule;
 import com.google.inject.internal.SingletonScope;
 
 /**
- *
+ * JIRA pure integration module
  */
 public class JiraIntegrationModule extends AbstractModule {
     /** {@inheritDoc} */
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/JiraIntegrationProvider.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/pure/JiraIntegrationProvider.java
similarity index 89%
rename from ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/JiraIntegrationProvider.java
rename to ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/pure/JiraIntegrationProvider.java
index 62a6618..c2c8cdc 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/JiraIntegrationProvider.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/pure/JiraIntegrationProvider.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.ci.jira;
+package org.apache.ignite.ci.jira.pure;
 
 import com.google.common.base.Strings;
 import com.google.common.cache.Cache;
@@ -24,6 +24,8 @@ import com.google.inject.Provider;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
 import javax.inject.Inject;
+import org.apache.ignite.ci.jira.pure.IJiraIntegration;
+import org.apache.ignite.ci.jira.pure.IJiraIntegrationProvider;
 import org.apache.ignite.ci.util.ExceptionUtil;
 
 /**
@@ -31,7 +33,7 @@ import org.apache.ignite.ci.util.ExceptionUtil;
  * server. Instance for each server is cached with defining instance
  * expiration time.
  */
-public class JiraIntegrationProvider implements IJiraIntegrationProvider {
+class JiraIntegrationProvider implements IJiraIntegrationProvider {
     /** */
     @Inject Provider<IJiraIntegration> factory;
 
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 f3a5197..24b32d5 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
@@ -30,8 +30,9 @@ import javax.inject.Inject;
 import org.apache.ignite.ci.ITcHelper;
 import org.apache.ignite.ci.di.AutoProfiling;
 import org.apache.ignite.ci.di.MonitoredTask;
-import org.apache.ignite.ci.jira.IJiraIntegration;
-import org.apache.ignite.ci.jira.IJiraIntegrationProvider;
+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;
@@ -62,11 +63,10 @@ public class ObserverTask extends TimerTask {
     @Inject private ITeamcityIgnitedProvider teamcityIgnitedProvider;
 
     /** */
-    @Inject private IJiraIntegrationProvider jiraIntegrationProvider;
-
-    /** */
     @Inject private VisasHistoryStorage visasHistStorage;
 
+    @Inject private TcBotTriggerAndSignOffService visaIssuer;
+
     /** */
     private ReentrantLock observationLock = new ReentrantLock();
 
@@ -192,9 +192,7 @@ public class ObserverTask extends TimerTask {
                 if (!visa.isSuccess()) {
                     ICredentialsProv creds = tcHelper.getServerAuthorizerCreds();
 
-                    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 f981abd..8c280e9 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
@@ -24,8 +24,8 @@ import org.apache.ignite.ci.IAnalyticsEnabledTeamcity;
 import org.apache.ignite.ci.analysis.MultBuildRunCtx;
 import org.apache.ignite.ci.github.ignited.IGitHubConnIgnited;
 import org.apache.ignite.ci.github.ignited.IGitHubConnIgnitedProvider;
-import org.apache.ignite.ci.jira.IJiraIntegration;
-import org.apache.ignite.ci.jira.IJiraIntegrationProvider;
+import org.apache.ignite.ci.jira.pure.IJiraIntegration;
+import org.apache.ignite.ci.jira.pure.IJiraIntegrationProvider;
 import org.apache.ignite.ci.tcmodel.result.problems.ProblemOccurrence;
 import org.apache.ignite.ci.teamcity.ignited.ITeamcityIgnited;
 import org.apache.ignite.ci.teamcity.ignited.ITeamcityIgnitedProvider;
@@ -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))
@@ -160,7 +160,7 @@ public class PrChainsProcessor {
             //fail rate reference is always default (master)
             chainStatus.initFromContext(tcIgnited, teamcity, ctx, teamcity, baseBranch);
 
-            chainStatus.initJiraAndGitInfo(jiraIntegration, gitHubConnIgnited);
+            chainStatus.initJiraAndGitInfo(tcIgnited, jiraIntegration, gitHubConnIgnited);
         }
 
         res.addChainOnServer(chainStatus);
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcbot/visa/ContributionCheckStatus.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcbot/visa/ContributionCheckStatus.java
index a710870..ba7e027 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcbot/visa/ContributionCheckStatus.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcbot/visa/ContributionCheckStatus.java
@@ -20,10 +20,14 @@ import java.util.LinkedList;
 import java.util.List;
 
 /**
- * Status of contribution check details returned from server
+ * Status of contribution check details returned from server. UI model for displaying detailed {@link
+ * ContributionToCheck} status.
  */
 @SuppressWarnings("PublicField") public class ContributionCheckStatus {
+    /** Queued builds. */
     public int queuedBuilds;
+
+    /** Running builds. */
     public int runningBuilds;
 
     /** Suite id. */
@@ -41,6 +45,7 @@ import java.util.List;
     /** Observations status: Filled if build observer has something sheduled related to {@link #resolvedBranch} */
     public String observationsStatus;
 
+    /** Web links to queued suites. */
     public List<String> webLinksQueuedSuites = new LinkedList<>();
 
     public ContributionCheckStatus() {
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcbot/visa/ContributionToCheck.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcbot/visa/ContributionToCheck.java
index 1a2a308..6238b23 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcbot/visa/ContributionToCheck.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcbot/visa/ContributionToCheck.java
@@ -17,10 +17,11 @@
 package org.apache.ignite.ci.tcbot.visa;
 
 /**
- *
+ * Short version of contribution status.
+ * Full version is placed in {@link ContributionCheckStatus}.
  */
 @SuppressWarnings("PublicField") public class ContributionToCheck {
-    /** Pr number. */
+    /** Pr number. Positive value implies PR number, negative value is for JIRA ticket for PR-less contributions. */
     public Integer prNumber;
 
     /** Pr title. */
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 1ed9dfd..d47933b 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,11 +17,14 @@
 
 package org.apache.ignite.ci.tcbot.visa;
 
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.base.Preconditions;
 import com.google.common.base.Strings;
 import com.google.inject.Provider;
 import java.text.DateFormat;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.Date;
 import java.util.LinkedHashSet;
@@ -42,9 +45,10 @@ import org.apache.ignite.ci.github.PullRequest;
 import org.apache.ignite.ci.github.ignited.IGitHubConnIgnited;
 import org.apache.ignite.ci.github.ignited.IGitHubConnIgnitedProvider;
 import org.apache.ignite.ci.github.pure.IGitHubConnection;
-import org.apache.ignite.ci.github.pure.IGitHubConnectionProvider;
-import org.apache.ignite.ci.jira.IJiraIntegration;
-import org.apache.ignite.ci.jira.IJiraIntegrationProvider;
+import org.apache.ignite.ci.jira.Ticket;
+import org.apache.ignite.ci.jira.ignited.IJiraIgnited;
+import org.apache.ignite.ci.jira.ignited.IJiraIgnitedProvider;
+import org.apache.ignite.ci.jira.pure.IJiraIntegrationProvider;
 import org.apache.ignite.ci.observer.BuildObserver;
 import org.apache.ignite.ci.observer.BuildsInfo;
 import org.apache.ignite.ci.tcbot.chain.PrChainsProcessor;
@@ -57,26 +61,39 @@ 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
+ * TC Bot Visa Facade.
+ * Provides method for TC Bot Visa obtaining.
+ * Contains features for adding comment to the ticket based on latest state.
+ *
  */
 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() {
@@ -87,20 +104,20 @@ public class TcBotTriggerAndSignOffService {
     /** Build observer provider. */
     @Inject Provider<BuildObserver> buildObserverProvider;
 
-    /** GitHub (pure) HTTP connection provider. */
-    @Inject IGitHubConnectionProvider gitHubConnProvider;
-
     /** GitHub connection ignited provider. */
     @Inject IGitHubConnIgnitedProvider gitHubConnIgnitedProvider;
 
     /** TC ignited provider. */
     @Inject ITeamcityIgnitedProvider tcIgnitedProv;
 
-    /** */
-    @Inject IJiraIntegrationProvider jiraIntegrationProvider;
+    /** Direct connection to JIRA provider */
+    @Inject IJiraIgnitedProvider jiraIgnProv;
+
+    /** Direct connection to JIRA provider */
+    @Inject IJiraIntegrationProvider jiraPureProvider;
 
     /** */
-    @Inject private VisasHistoryStorage visasHistoryStorage;
+    @Inject private VisasHistoryStorage visasHistStorage;
 
     /** */
     @Inject private IStringCompactor strCompactor;
@@ -111,9 +128,11 @@ public class TcBotTriggerAndSignOffService {
     /** Helper. */
     @Inject ITcHelper tcHelper;
 
-
     @Inject PrChainsProcessor prChainsProcessor;
 
+    /** Jackson serializer. */
+    private final ObjectMapper objMapper = new ObjectMapper();
+
     /** */
     public void startObserver() {
         buildObserverProvider.get();
@@ -125,9 +144,9 @@ public class TcBotTriggerAndSignOffService {
 
         ITeamcityIgnited ignited = tcIgnitedProv.server(srvId, prov);
 
-        IJiraIntegration jiraIntegration = jiraIntegrationProvider.server(srvId);
+        IJiraIgnited jiraIntegration = jiraIgnProv.server(srvId);
 
-        for (VisaRequest visaRequest : visasHistoryStorage.getVisas()) {
+        for (VisaRequest visaRequest : visasHistStorage.getVisas()) {
             VisaStatus visaStatus = new VisaStatus();
 
             BuildsInfo info = visaRequest.getInfo();
@@ -181,23 +200,46 @@ public class TcBotTriggerAndSignOffService {
     public Set<MuteInfo> getMutes(String srvId, String projectId, ICredentialsProv creds) {
         ITeamcityIgnited ignited = tcIgnitedProv.server(srvId, creds);
 
-        Set<MuteInfo> infos = ignited.getMutes(projectId, creds);
+        Set<MuteInfo> mutes = ignited.getMutes(projectId, creds);
+
+        IJiraIgnited jiraIgn = jiraIgnProv.server(srvId);
+
+        String browseUrl = jiraIgn.generateTicketUrl("");
+
+        insertTicketStatus(mutes, jiraIgn.getTickets(), browseUrl);
 
-        for (MuteInfo info : infos)
+        for (MuteInfo info : mutes)
             info.assignment.muteDate = THREAD_FORMATTER.get().format(new Date(info.assignment.timestamp()));
 
-        return infos;
+        return mutes;
     }
 
     /**
-     * @param pr Pull Request.
-     * @return JIRA ticket full name or empty string.
+     * Insert ticket status for all mutes, if they have ticket in description.
+     *  @param mutes Mutes.
+     * @param tickets Tickets.
+     * @param browseUrl JIRA URL for browsing tickets, e.g. https://issues.apache.org/jira/browse/
      */
-    @Deprecated
-    @NotNull public static String getTicketFullName(PullRequest pr) {
-        String prefix = "IGNITE-";
+    private void insertTicketStatus(Set<MuteInfo> mutes, Collection<Ticket> tickets, String browseUrl) {
+        for (MuteInfo mute : mutes) {
+            if (F.isEmpty(mute.assignment.text))
+                continue;
+
+            int pos = mute.assignment.text.indexOf(browseUrl);
+
+            if (pos == -1)
+                continue;
+
+            for (Ticket ticket : tickets) {
+                String muteTicket = mute.assignment.text.substring(pos + browseUrl.length());
 
-        return getTicketFullName(pr, prefix);
+                if (ticket.key.equals(muteTicket)) {
+                    mute.ticketStatus = ticket.status();
+
+                    break;
+                }
+            }
+        }
     }
 
     /**
@@ -262,24 +304,30 @@ public class TcBotTriggerAndSignOffService {
         String parentSuiteId,
         Build... builds
     ) {
-        IJiraIntegration jiraIntegration = jiraIntegrationProvider.server(srvId);
+        IJiraIgnited jiraIntegration = jiraIgnProv.server(srvId);
 
         String prefix = jiraIntegration.ticketPrefix();
 
         if (F.isEmpty(ticketFullName)) {
             try {
-                IGitHubConnIgnited gitHubConn = gitHubConnIgnitedProvider.server(srvId);
+                ticketFullName = prLessTicket(srvId, branchForTc, prov, prefix);
 
-                PullRequest pr = gitHubConn.getPullRequest(branchForTc);
+                PullRequest pr = null;
 
-                ticketFullName = getTicketFullName(pr, prefix);
+                if (Strings.isNullOrEmpty(ticketFullName)) {
+                    pr = findPrForBranch(srvId, branchForTc);
 
-                if (ticketFullName.isEmpty()) {
-                    return "JIRA ticket will not be notified after the tests are completed - " +
-                        "PR title \"" + pr.getTitle() + "\" should starts with \"" + prefix + "-NNNNN\"." +
+                    if (pr != null)
+                        ticketFullName = getTicketFullName(pr, prefix);
+                }
+
+                if (Strings.isNullOrEmpty(ticketFullName)) {
+                    return "JIRA ticket will not be notified - " +
+                        "PR title \"" + (pr == null ? "" : pr.getTitle()) + "\" should starts with \"" + prefix + "NNNNN\"." +
                         " Please, rename PR according to the" +
                         " <a href='https://cwiki.apache.org/confluence/display/IGNITE/How+to+Contribute" +
-                        "#HowtoContribute-1.CreateGitHubpull-request'>contributing guide</a>.";
+                        "#HowtoContribute-1.CreateGitHubpull-request'>contributing guide</a>" +
+                        " or use branch name according ticket name.";
                 }
             }
             catch (Exception e) {
@@ -316,24 +364,28 @@ public class TcBotTriggerAndSignOffService {
         ICredentialsProv prov) {
         String jiraRes = "";
 
-        IJiraIntegration jiraIntegration = jiraIntegrationProvider.server(srvId);
-
-        String prefix = jiraIntegration.ticketPrefix();
+        String prefix = jiraIgnProv.server(srvId).ticketPrefix();
 
         if (Strings.isNullOrEmpty(ticketFullName)) {
             try {
-                IGitHubConnIgnited gitHubConn = gitHubConnIgnitedProvider.server(srvId);
-                PullRequest pr = gitHubConn.getPullRequest(branchForTc);
+                ticketFullName = prLessTicket(srvId, branchForTc, prov, prefix);
 
-                ticketFullName = getTicketFullName(pr, prefix);
+                PullRequest pr = null;
 
-                if (ticketFullName.isEmpty()) {
+                if (Strings.isNullOrEmpty(ticketFullName)) {
+                    pr = findPrForBranch(srvId, branchForTc);
+
+                    if (pr != null)
+                        ticketFullName = getTicketFullName(pr, prefix);
+                }
+
+                if (Strings.isNullOrEmpty(ticketFullName)) {
                     jiraRes = "JIRA ticket can't be commented - " +
-                        "PR title \"" + pr.getTitle() + "\" should starts with \"" + prefix + "NNNNN\"." +
+                        "PR title \"" + (pr == null ? "" : pr.getTitle()) + "\" should starts with \"" + prefix + "NNNNN\"." +
                         " Please, rename PR according to the" +
                         " <a href='https://cwiki.apache.org/confluence/display/IGNITE/How+to+Contribute" +
                         "#HowtoContribute-1.CreateGitHubpull-request'>contributing guide</a>" +
-                        " or enter ticket id in the form.";
+                        " or use branch name according ticket name.";
                 }
             }
             catch (RuntimeException e) {
@@ -347,17 +399,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);
         }
@@ -365,17 +416,65 @@ public class TcBotTriggerAndSignOffService {
             return new SimpleResult("JIRA wasn't commented." + (!jiraRes.isEmpty() ? "<br>" + jiraRes : ""));
     }
 
+    @Nullable public PullRequest findPrForBranch(
+        @Nullable @QueryParam("serverId") String srvId,
+        @Nullable @QueryParam("branchName") String branchForTc) {
+        Integer prId = IGitHubConnection.convertBranchToId(branchForTc);
+
+        if (prId == null)
+            return null;
+
+        IGitHubConnIgnited gh = gitHubConnIgnitedProvider.server(srvId);
+
+        return gh.getPullRequest(prId);
+    }
+
+    /**
+     * @param srvId Server id.
+     * @param branchForTc Branch for tc.
+     * @param prov Credentials Prov.
+     * @param ticketPrefix Ticket prefix.
+     */
+    @Nullable public String prLessTicket(@Nullable @QueryParam("serverId") String srvId,
+        String branchForTc, ICredentialsProv prov, String ticketPrefix) {
+        return prLessTicket(branchForTc, ticketPrefix, tcIgnitedProv.server(srvId, prov));
+    }
+
+    /**
+     * @param branchForTc Branch for tc.
+     * @param ticketPrefix Ticket prefix.
+     * @param tcIgn Tc ign.
+     */
+    @Nullable public static String prLessTicket(String branchForTc, String ticketPrefix, ITeamcityIgnited tcIgn) {
+        String branchPrefix = tcIgn.gitBranchPrefix();
+
+        if (!branchForTc.startsWith(branchPrefix))
+            return null;
+
+        try {
+            int ticketNum = Integer.parseInt(branchForTc.substring(branchPrefix.length()));
+
+            return ticketPrefix + ticketNum;
+        }
+        catch (NumberFormatException ignored) {
+        }
+        return null;
+    }
+
     /**
      * @param srvId Server id.
+     * @param credsProv Credentials
      */
-    public List<ContributionToCheck> getContributionsToCheck(String srvId) {
-        IJiraIntegration jiraIntegration = jiraIntegrationProvider.server(srvId);
+    public List<ContributionToCheck> getContributionsToCheck(String srvId,
+        ICredentialsProv credsProv) {
+        IJiraIgnited jiraIntegration = jiraIgnProv.server(srvId);
 
         List<PullRequest> requests = gitHubConnIgnitedProvider.server(srvId).getPullRequests();
         if (requests == null)
             return null;
 
-        return requests.stream().map(pr -> {
+
+        List<ContributionToCheck> contribsList = requests.stream().map(pr -> {
             ContributionToCheck check = new ContributionToCheck();
             check.prNumber = pr.getNumber();
             check.prTitle = pr.getTitle();
@@ -387,6 +486,10 @@ public class TcBotTriggerAndSignOffService {
                 check.prAuthor = user.login();
                 check.prAuthorAvatarUrl = user.avatarUrl();
             }
+            else {
+                check.prAuthor = "";
+                check.prAuthorAvatarUrl = "";
+            }
 
             String prefix = jiraIntegration.ticketPrefix();
             check.jiraIssueId = Strings.emptyToNull(getTicketFullName(pr, prefix));
@@ -396,21 +499,61 @@ public class TcBotTriggerAndSignOffService {
 
             return check;
         }).collect(Collectors.toList());
+
+
+        Set<Ticket> tickets = jiraIntegration.getTickets();
+
+        List<Ticket> paTickets = tickets.stream().filter(Ticket::isActiveContribution).collect(Collectors.toList());
+
+        ITeamcityIgnited tcIgn = tcIgnitedProv.server(srvId, credsProv);
+
+        paTickets.forEach(ticket -> {
+            int ticketId = ticket.igniteId(jiraIntegration.ticketPrefix());
+            String branch = tcIgn.gitBranchPrefix() + ticketId;
+
+            String defBtForMaster = findDefaultBranchBuildType(srvId);
+
+            if(tcIgn.getAllBuildsCompacted(defBtForMaster, branch).isEmpty())
+                return; //Skipping contributions without builds
+
+            ContributionToCheck contribution = new ContributionToCheck();
+
+            contribution.jiraIssueId = ticket.key;
+            contribution.jiraIssueUrl = jiraIntegration.generateTicketUrl( ticket.key);
+            contribution.tcBranchName = branch;
+
+            contribution.prNumber = -ticketId;
+            contribution.prTitle = ""; //todo ticket title
+            contribution.prHtmlUrl = "";
+            contribution.prTimeUpdate = ""; //todo ticket updateTime
+
+            contribution.prAuthor = "";
+            contribution.prAuthorAvatarUrl = "";
+
+            contribsList.add(contribution);
+        });
+
+        return contribsList;
     }
 
     @Nonnull private List<BuildRefCompacted> findBuildsForPr(String suiteId, String prId,
         IGitHubConnIgnited ghConn, ITeamcityIgnited srv) {
-        List<BuildRefCompacted> buildHist = srv.getAllBuildsCompacted(suiteId, branchForTcA(prId));
+
+        List<BuildRefCompacted> buildHist = srv.getAllBuildsCompacted(suiteId, branchForTcDefault(prId, srv));
 
         if (!buildHist.isEmpty())
             return buildHist;
 
+        Integer prNum = Integer.valueOf(prId);
+        if (prNum < 0)
+            return buildHist; // Don't iterate for other options if PR ID is absent
+
         buildHist = srv.getAllBuildsCompacted(suiteId, branchForTcB(prId));
 
         if (!buildHist.isEmpty())
             return buildHist;
 
-        PullRequest pr = ghConn.getPullRequest(Integer.valueOf(prId));
+        PullRequest pr = ghConn.getPullRequest(prNum);
 
         if (pr != null) {
             GitHubBranch head = pr.head();
@@ -428,11 +571,19 @@ public class TcBotTriggerAndSignOffService {
         return Collections.emptyList();
     }
 
-    String branchForTcA(String prId) {
+    private String branchForTcDefault(String prId, ITeamcityIgnited srv) {
+        Integer prNum = Integer.valueOf(prId);
+        if (prNum < 0)
+            return srv.gitBranchPrefix() + (-prNum); // Checking "ignite-10930" builds only
+
+        return branchForTcA(prId);
+    }
+
+    private String branchForTcA(String prId) {
         return "pull/" + prId + "/head";
     }
 
-    String branchForTcB(String prId) {
+    private String branchForTcB(String prId) {
         return "pull/" + prId + "/merge";
     }
 
@@ -449,17 +600,11 @@ public class TcBotTriggerAndSignOffService {
 
         IGitHubConnIgnited ghConn = gitHubConnIgnitedProvider.server(srvId);
 
-        StringBuilder buildTypeId = new StringBuilder();
+        String defBtForMaster = findDefaultBranchBuildType(srvId);
 
-        HelperConfig.getTrackedBranches().get(DEFAULT_TRACKED_BRANCH_NAME)
-            .ifPresent(
-                b -> b.getChainsStream()
-                    .filter(c -> Objects.equals(srvId, c.serverId))
-                    .filter(c -> c.branchForRest.equals(ITeamcity.DEFAULT))
-                    .findFirst()
-                    .ifPresent(ch -> buildTypeId.append(ch.suiteId)));
-
-        BuildTypeCompacted buildType = buildTypeId.length() > 0 ? teamcity.getBuildType(buildTypeId.toString()) : null;
+        BuildTypeCompacted buildType = Strings.isNullOrEmpty(defBtForMaster)
+            ? null
+            : teamcity.getBuildType(defBtForMaster);
 
         List<String> compositeBuildTypeIds;
         String projectId;
@@ -476,21 +621,36 @@ public class TcBotTriggerAndSignOffService {
 
             compositeBuildTypeIds = new ArrayList<>();
 
-            if (buildTypeId.length() > 0)
-                compositeBuildTypeIds.add(buildTypeId.toString());
+            if (!Strings.isNullOrEmpty(defBtForMaster))
+                compositeBuildTypeIds.add(defBtForMaster);
         }
 
 
         for (String btId : compositeBuildTypeIds) {
-            List<BuildRefCompacted> forTests = findBuildsForPr(btId, prId, ghConn, teamcity);
+            List<BuildRefCompacted> compBuilds = findBuildsForPr(btId, prId, ghConn, teamcity);
 
-            statuses.add(forTests.isEmpty() ? new ContributionCheckStatus(btId, branchForTcA(prId)) :
-                contributionStatus(srvId, btId, forTests, teamcity, prId));
+            statuses.add(compBuilds.isEmpty()
+                ? new ContributionCheckStatus(btId, branchForTcDefault(prId, teamcity))
+                : contributionStatus(srvId, btId, compBuilds, teamcity, prId));
         }
 
         return statuses;
     }
 
+    @NotNull public String findDefaultBranchBuildType(String srvId) {
+        StringBuilder buildTypeId = new StringBuilder();
+
+        HelperConfig.getTrackedBranches().get(DEFAULT_TRACKED_BRANCH_NAME)
+            .ifPresent(
+                b -> b.getChainsStream()
+                    .filter(c -> Objects.equals(srvId, c.serverId))
+                    .filter(c -> c.branchForRest.equals(ITeamcity.DEFAULT))
+                    .findFirst()
+                    .ifPresent(ch -> buildTypeId.append(ch.suiteId)));
+
+        return buildTypeId.toString();
+    }
+
     /**
      * @param srvId Server id.
      * @param suiteId Suite id.
@@ -520,7 +680,7 @@ public class TcBotTriggerAndSignOffService {
             status.resolvedBranch = status.branchWithFinishedSuite;
             //todo take into account running/queued
         else
-            status.resolvedBranch = !builds.isEmpty() ? builds.get(0).branchName(compactor) : branchForTcA(prId);
+            status.resolvedBranch = !builds.isEmpty() ? builds.get(0).branchName(compactor) : branchForTcDefault(prId, teamcity);
 
         String observationsStatus = buildObserverProvider.get().getObservationStatus(new ContributionKey(srvId, status.resolvedBranch));
 
@@ -574,4 +734,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);
+
+        IJiraIgnited jira = jiraIgnProv.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.postJiraComment(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(Visa.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/teamcity/ignited/ITeamcityIgnited.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/ITeamcityIgnited.java
index 16f44e0..28b6ca4 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/ITeamcityIgnited.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/ITeamcityIgnited.java
@@ -179,4 +179,6 @@ public interface ITeamcityIgnited {
     @Nullable public IRunStat getSuiteRunStatAllBranches(String suiteBuildTypeId);
 
     List<String> getAllProjectsIds();
+
+    String gitBranchPrefix();
 }
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/TeamcityIgnitedImpl.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/TeamcityIgnitedImpl.java
index 65930eb..3938c27 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/TeamcityIgnitedImpl.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/TeamcityIgnitedImpl.java
@@ -19,7 +19,22 @@ package org.apache.ignite.ci.teamcity.ignited;
 
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.OptionalInt;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.stream.Collectors;
+import javax.annotation.Nullable;
+import javax.inject.Inject;
 import org.apache.ignite.ci.ITeamcity;
 import org.apache.ignite.ci.analysis.SuiteInBranch;
 import org.apache.ignite.ci.analysis.TestInBranch;
@@ -27,45 +42,35 @@ import org.apache.ignite.ci.di.AutoProfiling;
 import org.apache.ignite.ci.di.MonitoredTask;
 import org.apache.ignite.ci.di.cache.GuavaCached;
 import org.apache.ignite.ci.di.scheduler.IScheduler;
-import org.apache.ignite.ci.jira.ignited.JiraTicketDao;
-import org.apache.ignite.ci.jira.ignited.JiraTicketSync;
-import org.apache.ignite.ci.jira.Ticket;
 import org.apache.ignite.ci.tcbot.trends.MasterTrendsService;
 import org.apache.ignite.ci.tcmodel.conf.Project;
 import org.apache.ignite.ci.tcmodel.mute.MuteInfo;
-import org.apache.ignite.ci.teamcity.ignited.mute.MuteDao;
-import org.apache.ignite.ci.teamcity.ignited.mute.MuteSync;
+import org.apache.ignite.ci.tcmodel.result.Build;
 import org.apache.ignite.ci.teamcity.ignited.buildcondition.BuildCondition;
 import org.apache.ignite.ci.teamcity.ignited.buildcondition.BuildConditionDao;
-import org.apache.ignite.ci.tcmodel.result.Build;
 import org.apache.ignite.ci.teamcity.ignited.buildref.BuildRefDao;
 import org.apache.ignite.ci.teamcity.ignited.buildref.BuildRefSync;
+import org.apache.ignite.ci.teamcity.ignited.buildtype.BuildTypeCompacted;
+import org.apache.ignite.ci.teamcity.ignited.buildtype.BuildTypeDao;
 import org.apache.ignite.ci.teamcity.ignited.buildtype.BuildTypeRefCompacted;
 import org.apache.ignite.ci.teamcity.ignited.buildtype.BuildTypeRefDao;
 import org.apache.ignite.ci.teamcity.ignited.buildtype.BuildTypeSync;
-import org.apache.ignite.ci.teamcity.ignited.buildtype.BuildTypeCompacted;
-import org.apache.ignite.ci.teamcity.ignited.buildtype.BuildTypeDao;
 import org.apache.ignite.ci.teamcity.ignited.change.ChangeCompacted;
 import org.apache.ignite.ci.teamcity.ignited.change.ChangeDao;
 import org.apache.ignite.ci.teamcity.ignited.change.ChangeSync;
 import org.apache.ignite.ci.teamcity.ignited.fatbuild.FatBuildCompacted;
 import org.apache.ignite.ci.teamcity.ignited.fatbuild.FatBuildDao;
 import org.apache.ignite.ci.teamcity.ignited.fatbuild.ProactiveFatBuildSync;
+import org.apache.ignite.ci.teamcity.ignited.mute.MuteDao;
+import org.apache.ignite.ci.teamcity.ignited.mute.MuteSync;
 import org.apache.ignite.ci.teamcity.ignited.runhist.RunHistCompactedDao;
 import org.apache.ignite.ci.teamcity.ignited.runhist.RunHistSync;
 import org.apache.ignite.ci.teamcity.pure.ITeamcityConn;
 import org.apache.ignite.ci.user.ICredentialsProv;
-import org.apache.ignite.internal.util.typedef.F;
 import org.jetbrains.annotations.NotNull;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import javax.annotation.Nullable;
-import javax.inject.Inject;
-import java.util.*;
-import java.util.concurrent.TimeUnit;
-import java.util.stream.Collectors;
-
 import static org.apache.ignite.ci.tcmodel.hist.BuildRef.STATUS_UNKNOWN;
 
 /**
@@ -114,12 +119,6 @@ public class TeamcityIgnitedImpl implements ITeamcityIgnited {
     /** Mute Sync. */
     @Inject private MuteSync muteSync;
 
-    /** Jira ticket DAO. */
-    @Inject private JiraTicketDao jiraTicketDao;
-
-    /** Jira ticket Sync. */
-    @Inject private JiraTicketSync jiraTicketSync;
-
     /** Changes DAO. */
     @Inject private ChangeDao changesDao;
 
@@ -158,7 +157,6 @@ public class TeamcityIgnitedImpl implements ITeamcityIgnited {
         changesDao.init();
         runHistCompactedDao.init();
         muteDao.init();
-        jiraTicketDao.init();
     }
 
     /**
@@ -344,44 +342,14 @@ public class TeamcityIgnitedImpl implements ITeamcityIgnited {
     /** {@inheritDoc} */
     @Override public Set<MuteInfo> getMutes(String projectId, ICredentialsProv creds) {
         muteSync.ensureActualizeMutes(taskName("actualizeMutes"), projectId, srvIdMaskHigh, conn);
-        jiraTicketSync.ensureActualizeJiraTickets(taskName("actualizeJiraTickets"), srvIdMaskHigh, creds, conn);
 
         SortedSet<MuteInfo> mutes = muteDao.getMutes(srvIdMaskHigh);
-        Collection<Ticket> tickets = jiraTicketDao.getTickets(srvIdMaskHigh);
 
-        insertTicketStatus(mutes, tickets);
 
         return mutes;
     }
 
-    /**
-     * Insert ticket status for all mutes, if they have ticket in description.
-     *
-     * @param mutes Mutes.
-     * @param tickets Tickets.
-     */
-    private void insertTicketStatus(SortedSet<MuteInfo> mutes, Collection<Ticket> tickets) {
-        for (MuteInfo mute : mutes) {
-            if (F.isEmpty(mute.assignment.text))
-                continue;
-
-            int pos = mute.assignment.text.indexOf("https://issues.apache.org/jira/browse/");
-
-            if (pos == -1)
-                continue;
-
-            for (Ticket ticket : tickets) {
-                String muteTicket = mute.assignment.text.substring(pos +
-                    "https://issues.apache.org/jira/browse/".length());
 
-                if (ticket.key.equals(muteTicket)) {
-                    mute.ticketStatus = ticket.status();
-
-                    break;
-                }
-            }
-        }
-    }
 
     /** {@inheritDoc} */
     @AutoProfiling
@@ -434,6 +402,11 @@ public class TeamcityIgnitedImpl implements ITeamcityIgnited {
     }
 
     /** {@inheritDoc} */
+    @Override public String gitBranchPrefix() {
+        return conn.gitBranchPrefix();
+    }
+
+    /** {@inheritDoc} */
     @Override public List<String> getCompositeBuildTypesIdsSortedByBuildNumberCounter(String projectId) {
         return buildTypeSync.getCompositeBuildTypesIdsSortedByBuildNumberCounter(srvIdMaskHigh, projectId, conn);
     }
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/buildref/BuildRefDao.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/buildref/BuildRefDao.java
index 0d33822..e254e9a 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/buildref/BuildRefDao.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/buildref/BuildRefDao.java
@@ -153,7 +153,7 @@ public class BuildRefDao {
 
         return getBuildsForBranch(srvId, bracnhNameQry).stream()
             .filter(e -> e.buildTypeId() == buildTypeIdId)
-                .collect(Collectors.toList());
+            .collect(Collectors.toList());
     }
 
     /**
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/pure/ITeamcityConn.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/pure/ITeamcityConn.java
index 4a1bfeb..4c69296 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/pure/ITeamcityConn.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/pure/ITeamcityConn.java
@@ -109,4 +109,9 @@ public interface ITeamcityConn {
      * @return List of all project available at Teamcity server.
      */
     List<Project> getProjects();
+
+    /**
+     * @return Branch name mandatory prefix for all PR-less contributions, e.g. "ignite-".
+     */
+    public String gitBranchPrefix();
 }
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/model/Version.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/model/Version.java
index 8d4f178..f3c1fb3 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/model/Version.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/model/Version.java
@@ -28,7 +28,7 @@ package org.apache.ignite.ci.web.model;
     public static final String GITHUB_REF = "https://github.com/apache/ignite-teamcity-bot";
 
     /** TC Bot Version. */
-    public static final String VERSION = "20190114";
+    public static final String VERSION = "20190117";
 
     /** TC Bot Version. */
     public String version = VERSION;
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/model/Visa.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/model/Visa.java
index 5c694cc..201db9b 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/model/Visa.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/model/Visa.java
@@ -17,7 +17,6 @@
 
 package org.apache.ignite.ci.web.model;
 
-import org.apache.ignite.ci.jira.IJiraIntegration;
 import org.jetbrains.annotations.Nullable;
 
 /**
@@ -29,6 +28,9 @@ public class Visa {
     /** Determines Visa with no results and info. */
     public static final String EMPTY_VISA_STATUS = "emptyVisa";
 
+    /** Message to show user when JIRA ticket was successfully commented by the Bot. */
+    public static final String JIRA_COMMENTED = "JIRA commented.";
+
     /** */
     public final String status;
 
@@ -71,7 +73,7 @@ public class Visa {
 
     /** */
     public boolean isSuccess() {
-        return IJiraIntegration.JIRA_COMMENTED.equals(status)
+        return JIRA_COMMENTED.equals(status)
             && jiraCommentRes != null;
     }
 
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/model/current/ChainAtServerCurrentStatus.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/model/current/ChainAtServerCurrentStatus.java
index b1d10ff..5d4402b 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/model/current/ChainAtServerCurrentStatus.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/model/current/ChainAtServerCurrentStatus.java
@@ -18,6 +18,7 @@
 package org.apache.ignite.ci.web.model.current;
 
 import com.google.common.base.Objects;
+import com.google.common.base.Strings;
 import java.util.ArrayList;
 import java.util.Comparator;
 import java.util.List;
@@ -33,7 +34,7 @@ import org.apache.ignite.ci.analysis.MultBuildRunCtx;
 import org.apache.ignite.ci.github.PullRequest;
 import org.apache.ignite.ci.github.ignited.IGitHubConnIgnited;
 import org.apache.ignite.ci.github.pure.IGitHubConnection;
-import org.apache.ignite.ci.jira.IJiraIntegration;
+import org.apache.ignite.ci.jira.pure.IJiraIntegration;
 import org.apache.ignite.ci.tcbot.visa.TcBotTriggerAndSignOffService;
 import org.apache.ignite.ci.tcmodel.conf.BuildType;
 import org.apache.ignite.ci.analysis.TestInBranch;
@@ -126,7 +127,8 @@ public class ChainAtServerCurrentStatus {
     }
 
     /** */
-    public void initJiraAndGitInfo(IJiraIntegration jiraIntegration, IGitHubConnIgnited gitHubConnIgnited) {
+    public void initJiraAndGitInfo(ITeamcityIgnited tcIgnited,
+        IJiraIntegration jiraIntegration, IGitHubConnIgnited gitHubConnIgnited) {
         Integer prNum = IGitHubConnection.convertBranchToId(branchName);
 
         String prUrl = null;
@@ -135,18 +137,22 @@ public class ChainAtServerCurrentStatus {
 
         String ticketUrl = null;
 
+        String ticketPrefix = jiraIntegration.ticketPrefix();
+
         if (prNum != null) {
             PullRequest pullReq = gitHubConnIgnited.getPullRequest(prNum);
 
             if (pullReq != null && pullReq.getTitle() != null) {
                 prUrl = pullReq.htmlUrl();
 
-                ticketFullName = TcBotTriggerAndSignOffService.getTicketFullName(pullReq, jiraIntegration.ticketPrefix());
-
-                if (!ticketFullName.isEmpty())
-                    ticketUrl = jiraIntegration.generateTicketUrl(ticketFullName);
+                ticketFullName = TcBotTriggerAndSignOffService.getTicketFullName(pullReq, ticketPrefix);
             }
         }
+        else
+            ticketFullName = TcBotTriggerAndSignOffService.prLessTicket(branchName, ticketPrefix, tcIgnited);
+
+        if (!Strings.isNullOrEmpty(ticketFullName))
+            ticketUrl = jiraIntegration.generateTicketUrl(ticketFullName);
 
         setPrInfo(prNum, prUrl);
         setJiraTicketInfo(ticketFullName, ticketUrl);
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/pr/GetPrTestFailures.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/rest/pr/GetPrTestFailures.java
index 88d9d30..7745e73 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/rest/pr/GetPrTestFailures.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/rest/pr/GetPrTestFailures.java
@@ -17,6 +17,7 @@
 
 package org.apache.ignite.ci.web.rest.pr;
 
+import com.google.common.base.Preconditions;
 import com.google.inject.Injector;
 import javax.annotation.Nonnull;
 import javax.servlet.ServletContext;
@@ -32,6 +33,7 @@ import javax.ws.rs.core.MediaType;
 import org.apache.ignite.ci.github.PullRequest;
 import org.apache.ignite.ci.github.ignited.IGitHubConnIgnited;
 import org.apache.ignite.ci.github.ignited.IGitHubConnIgnitedProvider;
+import org.apache.ignite.ci.github.pure.IGitHubConnection;
 import org.apache.ignite.ci.tcbot.chain.PrChainsProcessor;
 import org.apache.ignite.ci.teamcity.ignited.SyncMode;
 import org.apache.ignite.ci.user.ICredentialsProv;
@@ -140,7 +142,12 @@ public class GetPrTestFailures {
         PullRequest pr;
 
         try {
-            pr = srv.getPullRequest(branchForTc);
+            Integer prId = IGitHubConnection.convertBranchToId(branchForTc);
+
+            if (prId == null)
+                return "Invalid TC branch name: [" + branchForTc + "]";
+
+            pr = srv.getPullRequest(prId);
         }
         catch (RuntimeException e) {
             return "Exception happened - " + e.getMessage();
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 3ce734c..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
@@ -75,15 +75,18 @@ public class TcBotVisaService {
 
     /**
      * @param srvId Server id.
+     * @return Contribution list for PRs and branches can be checked by TC bot.
      */
     @GET
     @Path("contributions")
     public List<ContributionToCheck> contributions(@Nullable @QueryParam("serverId") String srvId) {
-        if (!ICredentialsProv.get(req).hasAccess(srvId))
+        ICredentialsProv credsProv = ICredentialsProv.get(req);
+
+        if (!credsProv.hasAccess(srvId))
             throw ServiceUnauthorizedException.noCreds(srvId);
 
         return CtxListener.getInjector(ctx)
-            .getInstance(TcBotTriggerAndSignOffService.class).getContributionsToCheck(srvId);
+            .getInstance(TcBotTriggerAndSignOffService.class).getContributionsToCheck(srvId, credsProv);
     }
 
     @GET
@@ -107,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);
     }
diff --git a/ignite-tc-helper-web/src/main/webapp/js/prs-1.1.js b/ignite-tc-helper-web/src/main/webapp/js/prs-1.1.js
index e96428c..2ecb6fc 100644
--- a/ignite-tc-helper-web/src/main/webapp/js/prs-1.1.js
+++ b/ignite-tc-helper-web/src/main/webapp/js/prs-1.1.js
@@ -81,7 +81,7 @@ function showContributionsTable(result, srvId, suiteId) {
                 "data": "prTimeUpdate",
                 title: "Update Time",
                 "render": function (data, type, row, meta) {
-                    if (type === 'display') {
+                    if (type === 'display' && isDefinedAndFilled(data) && data.length >0) {
                         let date = new Date(data);
 
                         data = normalizeDateNum(date.getFullYear()) + '-' + normalizeDateNum(date.getMonth() + 1) +
@@ -96,7 +96,7 @@ function showContributionsTable(result, srvId, suiteId) {
                 "data": "prHtmlUrl",
                 title: "PR Number",
                 "render": function (data, type, row, meta) {
-                    if (type === 'display') {
+                    if (type === 'display' && row.prNumber > 0) {
                         data = "<a href='" + data + "'>#" + row.prNumber + "</a>";
                     }
 
@@ -111,7 +111,7 @@ function showContributionsTable(result, srvId, suiteId) {
                 "data": "prAuthor",
                 title: "Author",
                 "render": function (data, type, row, meta) {
-                    if (type === 'display') {
+                    if (type === 'display' && isDefinedAndFilled(row.prAuthorAvatarUrl) && row.prAuthorAvatarUrl.length >0) {
                         data = "<img src='" + row.prAuthorAvatarUrl + "' width='20px' height='20px'> " + data + "";
                     }
 
@@ -256,9 +256,14 @@ function formatContributionDetails(row, srvId) {
         "           </tr>";
 
     //References
-    res += "        <tr>\n" +
-        "            <td>Edit PR: " + "<a href='" + row.prHtmlUrl + "'>#" + row.prNumber + "</a>" + "</td>\n" +
-        "            <td id='viewQueuedBuildsFor" + prId + "'></td>\n" +
+    res += "        <tr>\n";
+
+    if (row.prNumber > 0)
+        res += "            <td>Edit PR: " + "<a href='" + row.prHtmlUrl + "'>#" + row.prNumber + "</a>" + "</td>\n";
+    else
+        res += "            <td></td>\n";
+
+    res += "            <td id='viewQueuedBuildsFor" + prId + "'></td>\n" +
         "            <td></td>\n" +
         "            <td></td>\n" +
         "        </tr>";
diff --git a/ignite-tc-helper-web/src/main/webapp/prs.html b/ignite-tc-helper-web/src/main/webapp/prs.html
index ca254b2..1eabc89 100644
--- a/ignite-tc-helper-web/src/main/webapp/prs.html
+++ b/ignite-tc-helper-web/src/main/webapp/prs.html
@@ -35,6 +35,59 @@
             loadData();
         });
 
+        function showTabs() {
+            var index = 'prsCheck_lastOpenedTab';  //  Define friendly index name
+            var dataStore = window.sessionStorage;  //  Define friendly data store name
+            var oldIndex;
+            try {
+                // getter: Fetch previous value
+                oldIndex = dataStore.getItem(index);
+            } catch (e) {
+                // getter: Always default to first tab in error state
+                oldIndex = 0;
+            }
+            $('#tabs').tabs({
+                // The zero-based index of the panel that is active (open)
+                active: oldIndex,
+                // Triggered after a tab has been activated
+                activate: function (event, ui) {
+                    //  Get future value
+                    var newIndex = ui.newTab.parent().children().index(ui.newTab);
+                    //  Set future value
+                    dataStore.setItem(index, newIndex)
+                }
+            });
+        }
+
+        function showServerContribTabs(result) {
+            const processed = new Set();
+            for (let chainAtServer of result) {
+                let serverId = chainAtServer.serverId;
+
+                if (processed.has(serverId))
+                    continue;
+
+                processed.add(serverId);
+
+                let selSrv = findGetParameter("server");
+                if (isDefinedAndFilled(selSrv)) {
+                    if (serverId !== selSrv)
+                        continue;
+                }
+
+                let tabsInsertion = $("#tabs-insertionPoint");
+                let tabId = "tabs-" + serverId;
+                let tabRef = "#" + tabId;
+                tabsInsertion.prepend("<li><a href=\"" + tabRef + "\">" + serverId + "</a></li>");
+
+                tabsInsertion.after("<div id=\"" + tabId + "\">" + "</div>");
+
+                requestTableForServer(serverId, chainAtServer.suiteId, $(tabRef));
+            }
+
+            showTabs();
+        }
+
         function loadData() {
             $("#loadStatus").html("<img src='https://www.wallies.com/filebin/images/loading_apple.gif' width=20px height=20px> Please wait");
 
@@ -52,32 +105,7 @@
                     showFormAndSuitesForPrCheck(result);
                     showCommentJiraForm(result);
 
-                    const processed = new Set();
-                    for (let chainAtServer of result) {
-                        let serverId = chainAtServer.serverId;
-
-                        if(processed.has(serverId))
-                            continue;
-
-                        processed.add(serverId);
-
-                        let selSrv = findGetParameter("server");
-                        if (isDefinedAndFilled(selSrv)) {
-                            if (serverId !== selSrv)
-                                continue;
-                        }
-
-                        let tabsInsertion = $("#tabs-insertionPoint");
-                        let tabId = "tabs-" + serverId;
-                        let tabRef = "#" + tabId;
-                        tabsInsertion.prepend("<li><a href=\"" + tabRef + "\">" + serverId + "</a></li>");
-
-                        tabsInsertion.after("<div id=\"" + tabId + "\">" + "</div>");
-
-                        requestTableForServer(serverId, chainAtServer.suiteId, $(tabRef));
-                    }
-
-                    $( "#tabs" ).tabs();
+                    showServerContribTabs(result);
                 },
                 error: showErrInLoadStatus
             });
diff --git a/ignite-tc-helper-web/src/test/java/org/apache/ignite/ci/tcbot/chain/MockBasedTcBotModule.java b/ignite-tc-helper-web/src/test/java/org/apache/ignite/ci/tcbot/chain/MockBasedTcBotModule.java
index 26c9340..f734d13 100644
--- a/ignite-tc-helper-web/src/test/java/org/apache/ignite/ci/tcbot/chain/MockBasedTcBotModule.java
+++ b/ignite-tc-helper-web/src/test/java/org/apache/ignite/ci/tcbot/chain/MockBasedTcBotModule.java
@@ -17,6 +17,7 @@
 
 package org.apache.ignite.ci.tcbot.chain;
 
+import com.google.common.base.Preconditions;
 import com.google.inject.AbstractModule;
 import com.google.inject.internal.SingletonScope;
 import org.apache.ignite.ci.IAnalyticsEnabledTeamcity;
@@ -26,8 +27,8 @@ import org.apache.ignite.ci.github.ignited.IGitHubConnIgnited;
 import org.apache.ignite.ci.github.ignited.IGitHubConnIgnitedProvider;
 import org.apache.ignite.ci.github.pure.IGitHubConnection;
 import org.apache.ignite.ci.github.pure.IGitHubConnectionProvider;
-import org.apache.ignite.ci.jira.IJiraIntegration;
-import org.apache.ignite.ci.jira.IJiraIntegrationProvider;
+import org.apache.ignite.ci.jira.pure.IJiraIntegration;
+import org.apache.ignite.ci.jira.pure.IJiraIntegrationProvider;
 import org.apache.ignite.ci.tcbot.conf.ITcBotConfig;
 import org.apache.ignite.ci.tcbot.issue.IIssuesStorage;
 import org.apache.ignite.ci.tcbot.user.IUserStorage;
@@ -40,6 +41,7 @@ import org.apache.ignite.ci.user.ICredentialsProv;
 import org.mockito.Mockito;
 
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.when;
 
@@ -76,7 +78,7 @@ public class MockBasedTcBotModule extends AbstractModule {
 
         when(pullReq.getTitle()).thenReturn("");
 
-        when(gitHubConnIgnited.getPullRequest(anyString())).thenReturn(pullReq);
+        when(gitHubConnIgnited.getPullRequest(anyInt())).thenReturn(pullReq);
 
         when(gitHubConnIgnitedProvider.server(anyString())).thenReturn(gitHubConnIgnited);
 
diff --git a/ignite-tc-helper-web/src/test/java/org/apache/ignite/ci/teamcity/ignited/IgnitedTcInMemoryIntegrationTest.java b/ignite-tc-helper-web/src/test/java/org/apache/ignite/ci/teamcity/ignited/IgnitedTcInMemoryIntegrationTest.java
index 357adb6..e768ceb 100644
--- a/ignite-tc-helper-web/src/test/java/org/apache/ignite/ci/teamcity/ignited/IgnitedTcInMemoryIntegrationTest.java
+++ b/ignite-tc-helper-web/src/test/java/org/apache/ignite/ci/teamcity/ignited/IgnitedTcInMemoryIntegrationTest.java
@@ -46,7 +46,7 @@ import org.apache.ignite.ci.analysis.TestInBranch;
 import org.apache.ignite.ci.db.TcHelperDb;
 import org.apache.ignite.ci.di.scheduler.DirectExecNoWaitScheduler;
 import org.apache.ignite.ci.di.scheduler.IScheduler;
-import org.apache.ignite.ci.jira.IJiraIntegrationProvider;
+import org.apache.ignite.ci.jira.pure.IJiraIntegrationProvider;
 import org.apache.ignite.ci.tcbot.chain.PrChainsProcessorTest;
 import org.apache.ignite.ci.tcmodel.changes.ChangesList;
 import org.apache.ignite.ci.tcmodel.conf.BuildType;
diff --git a/ignite-tc-helper-web/src/test/java/org/apache/ignite/ci/teamcity/ignited/TeamcityIgnitedMock.java b/ignite-tc-helper-web/src/test/java/org/apache/ignite/ci/teamcity/ignited/TeamcityIgnitedMock.java
index 2e0489a..b5b1ace 100644
--- a/ignite-tc-helper-web/src/test/java/org/apache/ignite/ci/teamcity/ignited/TeamcityIgnitedMock.java
+++ b/ignite-tc-helper-web/src/test/java/org/apache/ignite/ci/teamcity/ignited/TeamcityIgnitedMock.java
@@ -116,6 +116,8 @@ public class TeamcityIgnitedMock {
                     return runHistCompacted;
                 });
 
+        when(tcIgnited.gitBranchPrefix()).thenReturn("ignite-");
+
         return tcIgnited;
     }