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 2018/10/06 18:09:55 UTC

[ignite-teamcity-bot] branch ignite-9800-2 created (now 49aa7e9)

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

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


      at 49aa7e9  IGNITE-9800: Faster builds search engine development: not used yet

This branch includes the following new commits:

     new 49aa7e9  IGNITE-9800: Faster builds search engine development: not used yet

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



[ignite-teamcity-bot] 01/01: IGNITE-9800: Faster builds search engine development: not used yet

Posted by dp...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 49aa7e9c669f6a728137726a50a84258e5cfdd20
Author: Dmitriy Pavlov <dp...@apache.org>
AuthorDate: Sat Oct 6 21:09:07 2018 +0300

    IGNITE-9800: Faster builds search engine development: not used yet
---
 .../main/java/org/apache/ignite/ci/ITcHelper.java  |   1 +
 .../main/java/org/apache/ignite/ci/TcHelper.java   |   1 +
 .../org/apache/ignite/ci/di/IgniteTcBotModule.java |   5 +-
 .../ignite/ci/di/MonitoredTaskInterceptor.java     |  19 +--
 .../org/apache/ignite/ci/issue/IssueDetector.java  |   2 +-
 .../org/apache/ignite/ci/jobs/CheckQueueJob.java   |   2 +-
 .../apache/ignite/ci/observer/ObserverTask.java    |   2 +-
 .../ignite/ci/tcbot/TcBotSystemProperties.java     |   2 +
 .../ignite/ci/tcbot/chain/PrChainsProcessor.java   |   2 +-
 .../tcbot/chain/TrackedBranchChainsProcessor.java  |   2 +-
 .../tcbot/visa/TcBotTriggerAndSignOffService.java  |  30 +++-
 .../ignited/BuildRefCompacted.java}                |  25 ++-
 .../ignited/IStringCompacter.java}                 |   8 +-
 .../ignited/ITeamcityIgnited.java}                 |  11 +-
 .../ignited/ITeamcityIgnitedProvider.java}         |   8 +-
 .../IgniteStringCompacter.java}                    |  33 ++--
 .../ignited/TcIgnitedCachingProvider.java}         |  52 +++---
 .../ci/teamcity/ignited/TeamcityIgnitedImpl.java   | 187 +++++++++++++++++++++
 .../TeamcityIgnitedModule.java}                    |  11 +-
 .../ci/{ => teamcity/pure}/ITcServerProvider.java  |   3 +-
 .../ignite/ci/teamcity/pure/TcLoginImpl.java       |   2 +-
 .../ci/teamcity/pure/TcRealConnectionModule.java   |   1 +
 .../pure}/TcServerCachingProvider.java             |  43 +++--
 .../ignite/ci/teamcity/pure/TeamcityRecorder.java  |   5 +-
 .../teamcity/pure/TeamcityRecordingConnection.java |   1 +
 .../java/org/apache/ignite/ci/web/CtxListener.java |   2 +-
 .../ignite/ci/web/rest/visa/TcBotVisaService.java  |  24 ++-
 .../org/apache/ignite/ci/di/DiContextTest.java     |   4 +-
 28 files changed, 372 insertions(+), 116 deletions(-)

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 5d68ce9..00623f7 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
@@ -20,6 +20,7 @@ package org.apache.ignite.ci;
 import java.util.List;
 import org.apache.ignite.ci.issue.IssueDetector;
 import org.apache.ignite.ci.issue.IssuesStorage;
+import org.apache.ignite.ci.teamcity.pure.ITcServerProvider;
 import org.apache.ignite.ci.user.ICredentialsProv;
 import org.apache.ignite.ci.user.UserAndSessionsStorage;
 
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 5be9135..5e0422d 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
@@ -24,6 +24,7 @@ import org.apache.ignite.ci.issue.IssuesStorage;
 import org.apache.ignite.ci.jira.IJiraIntegration;
 import org.apache.ignite.ci.tcmodel.hist.BuildRef;
 import org.apache.ignite.ci.tcmodel.result.problems.ProblemOccurrence;
+import org.apache.ignite.ci.teamcity.pure.ITcServerProvider;
 import org.apache.ignite.ci.user.ICredentialsProv;
 import org.apache.ignite.ci.user.UserAndSessionsStorage;
 import org.apache.ignite.ci.web.model.current.ChainAtServerCurrentStatus;
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 2f802d4..a3fcc60 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
@@ -31,6 +31,7 @@ import org.apache.ignite.ci.issue.IssueDetector;
 import org.apache.ignite.ci.jira.IJiraIntegration;
 import org.apache.ignite.ci.observer.BuildObserver;
 import org.apache.ignite.ci.observer.ObserverTask;
+import org.apache.ignite.ci.teamcity.ignited.TeamcityIgnitedModule;
 import org.apache.ignite.ci.teamcity.pure.TcRealConnectionModule;
 import org.apache.ignite.ci.user.ICredentialsProv;
 import org.apache.ignite.ci.util.ExceptionUtil;
@@ -75,7 +76,6 @@ public class IgniteTcBotModule extends AbstractModule {
         //With REST persistence
         bind(IAnalyticsEnabledTeamcity.class).to(IgnitePersistentTeamcity.class);
         bind(ITcServerFactory.class).to(InitializingServerFactory.class).in(new SingletonScope());
-        bind(ITcServerProvider.class).to(TcServerCachingProvider.class).in(new SingletonScope());
         bind(TcUpdatePool.class).in(new SingletonScope());
         bind(IssueDetector.class).in(new SingletonScope());
         bind(ObserverTask.class).in(new SingletonScope());
@@ -85,7 +85,8 @@ public class IgniteTcBotModule extends AbstractModule {
         bind(IJiraIntegration.class).to(Jira.class).in(new SingletonScope());
 
         bind(BackgroundUpdater.class).in(new SingletonScope());
-        install(new TcRealConnectionModule());
+
+        install(new TeamcityIgnitedModule());
         install(new GitHubIgnitedModule());
         install(new SchedulerModule());
     }
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/di/MonitoredTaskInterceptor.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/di/MonitoredTaskInterceptor.java
index 45fb425..262530c 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/di/MonitoredTaskInterceptor.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/di/MonitoredTaskInterceptor.java
@@ -86,8 +86,7 @@ public class MonitoredTaskInterceptor implements MethodInterceptor {
         }
     }
 
-    @Override
-    public Object invoke(MethodInvocation invocation) throws Throwable {
+    @Override public Object invoke(MethodInvocation invocation) throws Throwable {
         final long startTs = System.currentTimeMillis();
 
         String fullKey = taskName(invocation);
@@ -121,21 +120,19 @@ public class MonitoredTaskInterceptor implements MethodInterceptor {
 
         final MonitoredTask annotation = method.getAnnotation(MonitoredTask.class);
         if (annotation != null) {
-            if (!Strings.isNullOrEmpty(annotation.name())) {
-                fullKey = annotation.name();
-            } else {
-                fullKey = cls + "." + mtd;
-            }
+            String activityName = annotation.name();
+
+            fullKey = !Strings.isNullOrEmpty(activityName) ? activityName : cls + "." + mtd;
 
             final Object[] arguments = invocation.getArguments();
 
             final int idx = annotation.nameExtArgIndex();
-            if(arguments!=null && idx >=0 && idx<arguments.length) {
+            if (arguments != null && idx >= 0 && idx < arguments.length)
                 fullKey += "." + Objects.toString(arguments[idx]);
-            }
-        } else {
-            fullKey = cls + "." + mtd;
         }
+        else
+            fullKey = cls + "." + mtd;
+
         return fullKey;
     }
 
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/issue/IssueDetector.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/issue/IssueDetector.java
index 7e2afa8..405f0ea 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/issue/IssueDetector.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/issue/IssueDetector.java
@@ -35,7 +35,7 @@ import javax.inject.Provider;
 import org.apache.ignite.ci.HelperConfig;
 import org.apache.ignite.ci.IAnalyticsEnabledTeamcity;
 import org.apache.ignite.ci.ITcHelper;
-import org.apache.ignite.ci.ITcServerProvider;
+import org.apache.ignite.ci.teamcity.pure.ITcServerProvider;
 import org.apache.ignite.ci.ITeamcity;
 import org.apache.ignite.ci.analysis.RunStat;
 import org.apache.ignite.ci.analysis.SuiteInBranch;
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jobs/CheckQueueJob.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jobs/CheckQueueJob.java
index 5a15afa..5535595 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jobs/CheckQueueJob.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jobs/CheckQueueJob.java
@@ -27,7 +27,7 @@ import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
 import jersey.repackaged.com.google.common.base.Throwables;
 import org.apache.ignite.ci.HelperConfig;
-import org.apache.ignite.ci.ITcServerProvider;
+import org.apache.ignite.ci.teamcity.pure.ITcServerProvider;
 import org.apache.ignite.ci.ITeamcity;
 import org.apache.ignite.ci.conf.BranchTracked;
 import org.apache.ignite.ci.conf.ChainAtServerTracked;
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 374099b..d0a222e 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
@@ -24,7 +24,7 @@ import java.util.TimerTask;
 import java.util.concurrent.ConcurrentLinkedQueue;
 import javax.inject.Inject;
 import org.apache.ignite.ci.IAnalyticsEnabledTeamcity;
-import org.apache.ignite.ci.ITcServerProvider;
+import org.apache.ignite.ci.teamcity.pure.ITcServerProvider;
 import org.apache.ignite.ci.di.AutoProfiling;
 import org.apache.ignite.ci.di.MonitoredTask;
 import org.apache.ignite.ci.jira.IJiraIntegration;
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcbot/TcBotSystemProperties.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcbot/TcBotSystemProperties.java
index a77dbd2..f5ae24c 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcbot/TcBotSystemProperties.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcbot/TcBotSystemProperties.java
@@ -19,4 +19,6 @@ package org.apache.ignite.ci.tcbot;
 
 public class TcBotSystemProperties {
     public static final String DEV_MODE = "DEV_MODE";
+    public static final String TEAMCITY_BOT_RECORDER_URLS = "teamcity.bot.recorder.urls";
+    public static final String TEAMCITY_BOT_RECORDER = "teamcity.bot.recorder";
 }
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 be2a7a3..9f65be2 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
@@ -18,7 +18,7 @@ package org.apache.ignite.ci.tcbot.chain;
 
 import com.google.common.base.Strings;
 import org.apache.ignite.ci.IAnalyticsEnabledTeamcity;
-import org.apache.ignite.ci.ITcServerProvider;
+import org.apache.ignite.ci.teamcity.pure.ITcServerProvider;
 import org.apache.ignite.ci.ITeamcity;
 import org.apache.ignite.ci.analysis.FullChainRunCtx;
 import org.apache.ignite.ci.analysis.mode.LatestRebuildMode;
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcbot/chain/TrackedBranchChainsProcessor.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcbot/chain/TrackedBranchChainsProcessor.java
index c29a964..ccb8b32 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcbot/chain/TrackedBranchChainsProcessor.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcbot/chain/TrackedBranchChainsProcessor.java
@@ -23,7 +23,7 @@ import java.util.stream.Collectors;
 import javax.inject.Inject;
 import org.apache.ignite.ci.HelperConfig;
 import org.apache.ignite.ci.IAnalyticsEnabledTeamcity;
-import org.apache.ignite.ci.ITcServerProvider;
+import org.apache.ignite.ci.teamcity.pure.ITcServerProvider;
 import org.apache.ignite.ci.analysis.FullChainRunCtx;
 import org.apache.ignite.ci.analysis.mode.LatestRebuildMode;
 import org.apache.ignite.ci.analysis.mode.ProcessLogsMode;
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 d3429e3..70a5096 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
@@ -23,7 +23,10 @@ import java.util.List;
 import java.util.stream.Collectors;
 import javax.inject.Inject;
 import javax.ws.rs.QueryParam;
-import org.apache.ignite.ci.ITcServerProvider;
+import org.apache.ignite.ci.tcmodel.hist.BuildRef;
+import org.apache.ignite.ci.teamcity.ignited.ITeamcityIgnited;
+import org.apache.ignite.ci.teamcity.ignited.ITeamcityIgnitedProvider;
+import org.apache.ignite.ci.teamcity.pure.ITcServerProvider;
 import org.apache.ignite.ci.ITeamcity;
 import org.apache.ignite.ci.github.GitHubUser;
 import org.apache.ignite.ci.github.ignited.IGitHubConnIgnited;
@@ -54,6 +57,8 @@ public class TcBotTriggerAndSignOffService {
 
     @Inject IJiraIntegration jiraIntegration;
 
+    @Inject ITeamcityIgnitedProvider teamcityIgnitedProvider;
+
     /**
      * @param pr Pull Request.
      * @return JIRA ticket number.
@@ -199,4 +204,27 @@ public class TcBotTriggerAndSignOffService {
             return check;
         }).collect(Collectors.toList());
     }
+
+    public List<BuildRef> buildsForContribution(String srvId, ICredentialsProv prov,
+        String suiteId, String prId) {
+
+        ITeamcityIgnited srv = teamcityIgnitedProvider.server(srvId, prov);
+
+        List<BuildRef> buildHist = srv.getBuildHistory(suiteId, branchForTcA(prId));
+
+        if (!buildHist.isEmpty())
+            return buildHist;
+
+        buildHist = srv.getBuildHistory(suiteId, branchForTcB(prId));
+
+        return buildHist;
+    }
+
+    String branchForTcA(String prId) {
+        return "pull/" + prId + "/head";
+    }
+
+    String branchForTcB(String prId) {
+        return "pull/" + prId + "/merge";
+    }
 }
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/ITcServerProvider.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/BuildRefCompacted.java
similarity index 64%
copy from ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/ITcServerProvider.java
copy to ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/BuildRefCompacted.java
index 702ca22..452551a 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/ITcServerProvider.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/BuildRefCompacted.java
@@ -14,15 +14,24 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.ignite.ci;
+package org.apache.ignite.ci.teamcity.ignited;
 
-import org.apache.ignite.ci.user.ICredentialsProv;
+import org.apache.ignite.ci.tcmodel.hist.BuildRef;
 
-import javax.annotation.Nullable;
+public class BuildRefCompacted {
+    int buildId = -1;
 
-/**
- * Provides instance to server with appropriate credentials, may cache instances to avoid odd server instances.
- */
-public interface ITcServerProvider {
-    public IAnalyticsEnabledTeamcity server(String srvId, @Nullable ICredentialsProv prov);
+    public BuildRefCompacted() {
+
+    }
+
+    public BuildRefCompacted(BuildRef ref) {
+        buildId = ref.getId() == null ? -1 : ref.getId();
+    }
+
+    public BuildRef toBuildRef() {
+        BuildRef ref = new BuildRef();
+        ref.setId(buildId < 0 ? null : buildId);
+        return ref;
+    }
 }
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcbot/TcBotSystemProperties.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/IStringCompacter.java
similarity index 82%
copy from ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcbot/TcBotSystemProperties.java
copy to ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/IStringCompacter.java
index a77dbd2..c76cc21 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcbot/TcBotSystemProperties.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/IStringCompacter.java
@@ -14,9 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package org.apache.ignite.ci.teamcity.ignited;
 
-package org.apache.ignite.ci.tcbot;
-
-public class TcBotSystemProperties {
-    public static final String DEV_MODE = "DEV_MODE";
+public interface IStringCompacter {
+    public int getStringId(String value);
+    public String getStringFromId(int id);
 }
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcbot/TcBotSystemProperties.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/ITeamcityIgnited.java
similarity index 72%
copy from ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcbot/TcBotSystemProperties.java
copy to ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/ITeamcityIgnited.java
index a77dbd2..829eaff 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcbot/TcBotSystemProperties.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/ITeamcityIgnited.java
@@ -14,9 +14,14 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package org.apache.ignite.ci.teamcity.ignited;
 
-package org.apache.ignite.ci.tcbot;
+import java.util.List;
+import javax.annotation.Nullable;
+import org.apache.ignite.ci.tcmodel.hist.BuildRef;
 
-public class TcBotSystemProperties {
-    public static final String DEV_MODE = "DEV_MODE";
+public interface ITeamcityIgnited {
+    public List<BuildRef> getBuildHistory(
+        @Nullable String buildTypeId,
+        @Nullable String branchName);
 }
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcbot/TcBotSystemProperties.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/ITeamcityIgnitedProvider.java
similarity index 79%
copy from ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcbot/TcBotSystemProperties.java
copy to ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/ITeamcityIgnitedProvider.java
index a77dbd2..590e755 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcbot/TcBotSystemProperties.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/ITeamcityIgnitedProvider.java
@@ -14,9 +14,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package org.apache.ignite.ci.teamcity.ignited;
 
-package org.apache.ignite.ci.tcbot;
+import org.apache.ignite.ci.user.ICredentialsProv;
 
-public class TcBotSystemProperties {
-    public static final String DEV_MODE = "DEV_MODE";
+public interface ITeamcityIgnitedProvider {
+
+    ITeamcityIgnited server(String srvId, ICredentialsProv prov);
 }
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/pure/TeamcityRecordingConnection.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/IgniteStringCompacter.java
similarity index 50%
copy from ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/pure/TeamcityRecordingConnection.java
copy to ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/IgniteStringCompacter.java
index 72ded81..82e3587 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/pure/TeamcityRecordingConnection.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/IgniteStringCompacter.java
@@ -14,19 +14,32 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.ignite.ci.teamcity.pure;
+package org.apache.ignite.ci.teamcity.ignited;
 
-import org.apache.ignite.ci.util.HttpUtil;
+import java.util.concurrent.atomic.AtomicBoolean;
+import org.apache.ignite.cache.affinity.rendezvous.RendezvousAffinityFunction;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.jetbrains.annotations.NotNull;
 
-import javax.inject.Inject;
-import java.io.IOException;
-import java.io.InputStream;
+public class IgniteStringCompacter implements IStringCompacter {
+    AtomicBoolean initGuard=new AtomicBoolean();
 
-public class TeamcityRecordingConnection implements ITeamcityHttpConnection {
-    @Inject private TeamcityRecorder recorder;
+    @Override public int getStringId(String value) {
+        return 0;
+    }
+
+    @Override public String getStringFromId(int id) {
+        return null;
+    }
+
+
+
+    @NotNull
+    public static <K, V> CacheConfiguration<K, V> getCache8PartsConfig(String name) {
+        CacheConfiguration<K, V> ccfg = new CacheConfiguration<>(name);
+
+        ccfg.setAffinity(new RendezvousAffinityFunction(false, 8));
 
-    /** {@inheritDoc} */
-    @Override public InputStream sendGet(String basicAuthTok, String url) throws IOException {
-        return recorder.onGet(HttpUtil.sendGetWithBasicAuth(basicAuthTok, url), url);
+        return ccfg;
     }
 }
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/di/TcServerCachingProvider.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/TcIgnitedCachingProvider.java
similarity index 61%
copy from ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/di/TcServerCachingProvider.java
copy to ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/TcIgnitedCachingProvider.java
index e8e9da3..8218b62 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/di/TcServerCachingProvider.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/TcIgnitedCachingProvider.java
@@ -14,60 +14,56 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.ignite.ci.di;
+package org.apache.ignite.ci.teamcity.ignited;
 
 import com.google.common.base.Strings;
 import com.google.common.cache.Cache;
 import com.google.common.cache.CacheBuilder;
-import org.apache.ignite.ci.IAnalyticsEnabledTeamcity;
-import org.apache.ignite.ci.ITcServerProvider;
-import org.apache.ignite.ci.user.ICredentialsProv;
-import org.apache.ignite.ci.util.ExceptionUtil;
-
-import javax.annotation.Nullable;
-import javax.inject.Inject;
-import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
+import javax.annotation.Nullable;
+import javax.inject.Inject;
+import org.apache.ignite.ci.ITeamcity;
+import org.apache.ignite.ci.teamcity.pure.ITcServerProvider;
+import org.apache.ignite.ci.user.ICredentialsProv;
+import org.apache.ignite.ci.util.ExceptionUtil;
 
 /**
  *
  */
-public class TcServerCachingProvider implements ITcServerProvider {
+class TcIgnitedCachingProvider implements ITeamcityIgnitedProvider {
     /** Server factory. */
     @Inject
-    private ITcServerFactory srvFactory;
+    private ITcServerProvider srvFactory;
 
-    private final Cache<String, IAnalyticsEnabledTeamcity> srvs
-            = CacheBuilder.<String, String>newBuilder()
+    private final Cache<String, ITeamcityIgnited> srvs
+            = CacheBuilder.newBuilder()
             .maximumSize(100)
             .expireAfterAccess(16, TimeUnit.MINUTES)
             .softValues()
             .build();
 
     /** {@inheritDoc} */
-    @Override public IAnalyticsEnabledTeamcity server(String srvId, @Nullable ICredentialsProv prov) {
-        Callable<IAnalyticsEnabledTeamcity> call = () -> {
-            IAnalyticsEnabledTeamcity teamcity = srvFactory.createServer(srvId);
+    @Override public ITeamcityIgnited server(String srvId, @Nullable ICredentialsProv prov) {
+        String fullKey = Strings.nullToEmpty(prov == null ? null : prov.getUser(srvId)) + ":" + Strings.nullToEmpty(srvId);
 
+        try {
+            return srvs.get(fullKey, () -> {
+                ITeamcity teamcity1 = srvFactory.server(srvId, prov);
 
-            if (prov != null) {
-                final String user = prov.getUser(srvId);
-                final String pwd = prov.getPassword(srvId);
-                teamcity.setAuthData(user, pwd);
-            }
+                if (prov != null) {
+                    final String user = prov.getUser(srvId);
+                    final String pwd = prov.getPassword(srvId);
+                    teamcity1.setAuthData(user, pwd);
+                }
 
-            return teamcity;
-        };
-        String fullKey = Strings.nullToEmpty(prov == null ? null : prov.getUser(srvId)) + ":" + Strings.nullToEmpty(srvId);
+                TeamcityIgnitedImpl impl = new TeamcityIgnitedImpl();
 
-        IAnalyticsEnabledTeamcity teamcity;
-        try {
-            teamcity = srvs.get(fullKey, call);
+                return impl;
+            });
         }
         catch (ExecutionException e) {
             throw ExceptionUtil.propagateException(e);
         }
-        return teamcity;
     }
 }
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
new file mode 100644
index 0000000..435222d
--- /dev/null
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/TeamcityIgnitedImpl.java
@@ -0,0 +1,187 @@
+/*
+ * 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.teamcity.ignited;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+import javax.annotation.Nullable;
+import javax.inject.Inject;
+import javax.inject.Provider;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.cache.affinity.rendezvous.RendezvousAffinityFunction;
+import org.apache.ignite.ci.ITeamcity;
+import org.apache.ignite.ci.db.TcHelperDb;
+import org.apache.ignite.ci.di.AutoProfiling;
+import org.apache.ignite.ci.di.MonitoredTask;
+import org.apache.ignite.ci.di.scheduler.IScheduler;
+import org.apache.ignite.ci.tcmodel.hist.BuildRef;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.jetbrains.annotations.NotNull;
+
+//todo currently this implementation is shared between all users
+public class TeamcityIgnitedImpl implements ITeamcityIgnited {
+    /** Cache name*/
+    public static final String TEAMCITY_BUILD_CACHE_NAME = "teamcityBuild";
+
+    //todo move to string compacter
+    /** Cache name */
+    public static final String STRING_CACHE_NAME = "strings";
+
+    /** Server id. */
+    private String srvId;
+
+    /** Pure HTTP Connection API. */
+    private ITeamcity conn;
+
+    /** Ignite provider. */
+    @Inject Provider<Ignite> igniteProvider;
+
+    /** Scheduler. */
+    @Inject IScheduler scheduler;
+
+    /** Server ID mask for cache Entries. */
+    private long srvIdMaskHigh;
+
+    /** PPs cache. */
+    private IgniteCache<Long, BuildRefCompacted> buildsCache;
+
+    public void init(String srvId, ITeamcity conn) {
+        this.srvId = srvId;
+        this.conn = conn;
+
+        srvIdMaskHigh = Math.abs(srvId.hashCode());
+
+        Ignite ignite = igniteProvider.get();
+        buildsCache = ignite.getOrCreateCache(TcHelperDb.getCacheV2Config(TEAMCITY_BUILD_CACHE_NAME));
+    }
+
+    @NotNull
+    public static <K, V> CacheConfiguration<K, V> getCache8PartsConfig(String name) {
+        CacheConfiguration<K, V> ccfg = new CacheConfiguration<>(name);
+
+        ccfg.setAffinity(new RendezvousAffinityFunction(false, 8));
+
+        return ccfg;
+    }
+
+    /** {@inheritDoc} */
+    @AutoProfiling
+    @Override public List<BuildRef> getBuildHistory(
+        @Nullable String buildTypeId,
+        @Nullable String branchName) {
+        scheduler.sheduleNamed(ITeamcityIgnited.class.getSimpleName() + ".actualizeRecentBuilds",
+            this::actualizeRecentBuilds, 2, TimeUnit.MINUTES);
+
+        return allBuildsEver()
+            .filter(e -> Objects.equals(e.buildTypeId, buildTypeId))
+            .filter(e -> Objects.equals(e.branchName, branchName))
+            .collect(Collectors.toList());
+    }
+
+    @NotNull private Stream<BuildRef> allBuildsEver() {
+        return StreamSupport.stream(buildsCache.spliterator(), false)
+            .filter(entry -> entry.getKey() >> 32 == srvIdMaskHigh)
+            .map(javax.cache.Cache.Entry::getValue)
+            .map(BuildRefCompacted::toBuildRef);
+    }
+
+    private void actualizeRecentBuilds() {
+        runAtualizeBuilds(srvId, false);
+
+        // schedule full resync later
+        scheduler.invokeLater(this::sheduleResync, 20, TimeUnit.SECONDS);
+    }
+
+    /**
+     *
+     */
+    private void sheduleResync() {
+        scheduler.sheduleNamed(ITeamcityIgnited.class.getSimpleName() + ".fullReindex",
+            this::fullReindex, 60, TimeUnit.MINUTES);
+    }
+
+    /**
+     *
+     */
+    private void fullReindex() {
+        runAtualizeBuilds(srvId, true);
+    }
+
+    /**
+     * @param srvId Server id.
+     * @param fullReindex Reindex all open PRs
+     */
+    @MonitoredTask(name = "Actualize BuildRefs, full resync", nameExtArgIndex = 1)
+    @AutoProfiling
+    protected String runAtualizeBuilds(String srvId, boolean fullReindex) {
+        AtomicReference<String> outLinkNext = new AtomicReference<>();
+
+        List<BuildRef> tcData = conn.getFinishedBuilds(null, null);//todo, outLinkNext);
+        int cntSaved = saveChunk(tcData);
+        int totalChecked = tcData.size();
+        while (outLinkNext.get() != null) {
+            String nextPageUrl = outLinkNext.get();
+            tcData = conn.getFinishedBuilds(null,null); //todo nextPageUrl, outLinkNext);
+            cntSaved += saveChunk(tcData);
+            totalChecked += tcData.size();
+
+            if(!fullReindex)
+                break; // 2 pages
+        }
+
+        return "Entries saved " + cntSaved + " Builds checked " + totalChecked;
+    }
+
+    private int saveChunk(List<BuildRef> ghData) {
+        Set<Long> ids = ghData.stream().map(BuildRef::getId)
+            .filter(Objects::nonNull)
+            .map(this::buildIdToCacheKey)
+            .collect(Collectors.toSet());
+
+        Map<Long, BuildRefCompacted> existingEntries = buildsCache.getAll(ids);
+        Map<Long, BuildRefCompacted> entriesToPut = new TreeMap<>();
+
+        List<BuildRefCompacted> collect = ghData.stream().map(BuildRefCompacted::new)
+            .collect(Collectors.toList());
+
+        for (BuildRefCompacted next : collect) {
+            long cacheKey = buildIdToCacheKey(next.buildId );
+            BuildRefCompacted buildPersisted = existingEntries.get(cacheKey);
+
+            if (buildPersisted == null || !buildPersisted.equals(next))
+                entriesToPut.put(cacheKey, next);
+        }
+
+        int size = entriesToPut.size();
+        if (size != 0)
+            buildsCache.putAll(entriesToPut);
+        return size;
+    }
+
+    private long buildIdToCacheKey(int buildId) {
+        return (long)buildId | srvIdMaskHigh << 32;
+    }
+}
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/pure/TcRealConnectionModule.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/TeamcityIgnitedModule.java
similarity index 76%
copy from ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/pure/TcRealConnectionModule.java
copy to ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/TeamcityIgnitedModule.java
index 4d154e7..202c834 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/pure/TcRealConnectionModule.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/TeamcityIgnitedModule.java
@@ -14,19 +14,20 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.ignite.ci.teamcity.pure;
+package org.apache.ignite.ci.teamcity.ignited;
 
 import com.google.inject.AbstractModule;
 import com.google.inject.internal.SingletonScope;
+import org.apache.ignite.ci.teamcity.pure.TcRealConnectionModule;
 
 /**
  * Guice module to setup real connected server and all related implementations.
  */
-public class TcRealConnectionModule extends AbstractModule {
+public class TeamcityIgnitedModule extends AbstractModule {
     /** {@inheritDoc} */
     @Override protected void configure() {
-        bind(ITeamcityHttpConnection.class).to(TeamcityRecordingConnection.class);
-        bind(TeamcityRecorder.class).in(new SingletonScope());
-        bind(ITcLogin.class).to(TcLoginImpl.class).in(new SingletonScope());
+        bind(ITeamcityIgnitedProvider.class).to(TcIgnitedCachingProvider.class).in(new SingletonScope());
+
+        install(new TcRealConnectionModule());
     }
 }
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/ITcServerProvider.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/pure/ITcServerProvider.java
similarity index 92%
rename from ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/ITcServerProvider.java
rename to ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/pure/ITcServerProvider.java
index 702ca22..cefafac 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/ITcServerProvider.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/pure/ITcServerProvider.java
@@ -14,8 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.ignite.ci;
+package org.apache.ignite.ci.teamcity.pure;
 
+import org.apache.ignite.ci.IAnalyticsEnabledTeamcity;
 import org.apache.ignite.ci.user.ICredentialsProv;
 
 import javax.annotation.Nullable;
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/pure/TcLoginImpl.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/pure/TcLoginImpl.java
index f8aad00..e16a2d9 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/pure/TcLoginImpl.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/pure/TcLoginImpl.java
@@ -28,7 +28,7 @@ import javax.inject.Provider;
 /**
  * Real implementation of login based on getting current user preferences from the server.
  */
-public class TcLoginImpl implements ITcLogin {
+class TcLoginImpl implements ITcLogin {
     /** Logger. */
     private static final Logger logger = LoggerFactory.getLogger(TcLoginImpl.class);
 
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/pure/TcRealConnectionModule.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/pure/TcRealConnectionModule.java
index 4d154e7..8a2f9ba 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/pure/TcRealConnectionModule.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/pure/TcRealConnectionModule.java
@@ -25,6 +25,7 @@ import com.google.inject.internal.SingletonScope;
 public class TcRealConnectionModule extends AbstractModule {
     /** {@inheritDoc} */
     @Override protected void configure() {
+        bind(ITcServerProvider.class).to(TcServerCachingProvider.class).in(new SingletonScope());
         bind(ITeamcityHttpConnection.class).to(TeamcityRecordingConnection.class);
         bind(TeamcityRecorder.class).in(new SingletonScope());
         bind(ITcLogin.class).to(TcLoginImpl.class).in(new SingletonScope());
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/di/TcServerCachingProvider.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/pure/TcServerCachingProvider.java
similarity index 68%
rename from ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/di/TcServerCachingProvider.java
rename to ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/pure/TcServerCachingProvider.java
index e8e9da3..cb5d629 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/di/TcServerCachingProvider.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/pure/TcServerCachingProvider.java
@@ -14,13 +14,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.ignite.ci.di;
+package org.apache.ignite.ci.teamcity.pure;
 
 import com.google.common.base.Strings;
 import com.google.common.cache.Cache;
 import com.google.common.cache.CacheBuilder;
 import org.apache.ignite.ci.IAnalyticsEnabledTeamcity;
-import org.apache.ignite.ci.ITcServerProvider;
+import org.apache.ignite.ci.di.ITcServerFactory;
 import org.apache.ignite.ci.user.ICredentialsProv;
 import org.apache.ignite.ci.util.ExceptionUtil;
 
@@ -33,41 +33,38 @@ import java.util.concurrent.TimeUnit;
 /**
  *
  */
-public class TcServerCachingProvider implements ITcServerProvider {
+class TcServerCachingProvider implements ITcServerProvider {
     /** Server factory. */
     @Inject
     private ITcServerFactory srvFactory;
 
+    /** Servers. */
     private final Cache<String, IAnalyticsEnabledTeamcity> srvs
-            = CacheBuilder.<String, String>newBuilder()
-            .maximumSize(100)
-            .expireAfterAccess(16, TimeUnit.MINUTES)
-            .softValues()
-            .build();
+        = CacheBuilder.newBuilder()
+        .maximumSize(100)
+        .expireAfterAccess(16, TimeUnit.MINUTES)
+        .softValues()
+        .build();
 
     /** {@inheritDoc} */
     @Override public IAnalyticsEnabledTeamcity server(String srvId, @Nullable ICredentialsProv prov) {
-        Callable<IAnalyticsEnabledTeamcity> call = () -> {
-            IAnalyticsEnabledTeamcity teamcity = srvFactory.createServer(srvId);
-
-
-            if (prov != null) {
-                final String user = prov.getUser(srvId);
-                final String pwd = prov.getPassword(srvId);
-                teamcity.setAuthData(user, pwd);
-            }
-
-            return teamcity;
-        };
         String fullKey = Strings.nullToEmpty(prov == null ? null : prov.getUser(srvId)) + ":" + Strings.nullToEmpty(srvId);
 
-        IAnalyticsEnabledTeamcity teamcity;
         try {
-            teamcity = srvs.get(fullKey, call);
+            return srvs.get(fullKey, () -> {
+                IAnalyticsEnabledTeamcity teamcity = srvFactory.createServer(srvId);
+
+                if (prov != null) {
+                    final String user = prov.getUser(srvId);
+                    final String pwd = prov.getPassword(srvId);
+                    teamcity.setAuthData(user, pwd);
+                }
+
+                return teamcity;
+            });
         }
         catch (ExecutionException e) {
             throw ExceptionUtil.propagateException(e);
         }
-        return teamcity;
     }
 }
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/pure/TeamcityRecorder.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/pure/TeamcityRecorder.java
index 7cae7f3..540e16c 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/pure/TeamcityRecorder.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/pure/TeamcityRecorder.java
@@ -24,6 +24,7 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.concurrent.locks.ReentrantLock;
+import org.apache.ignite.ci.tcbot.TcBotSystemProperties;
 
 /**
  *
@@ -43,14 +44,14 @@ public class TeamcityRecorder {
      * @param url Url.
      */
     public InputStream onGet(InputStream inputStream, String url) throws IOException {
-        if (Boolean.valueOf(System.getProperty("teamcity.bot.recorder.urls"))) {
+        if (Boolean.valueOf(System.getProperty(TcBotSystemProperties.TEAMCITY_BOT_RECORDER_URLS))) {
             urls.add(url);
 
             if (urls.size() > 100)
                 urls.remove();
         }
 
-        if (Boolean.valueOf(System.getProperty("teamcity.bot.recorder"))) {
+        if (Boolean.valueOf(System.getProperty(TcBotSystemProperties.TEAMCITY_BOT_RECORDER))) {
             boolean success = false;
 
             lock.lock();
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/pure/TeamcityRecordingConnection.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/pure/TeamcityRecordingConnection.java
index 72ded81..b5125d7 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/pure/TeamcityRecordingConnection.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/pure/TeamcityRecordingConnection.java
@@ -23,6 +23,7 @@ import java.io.IOException;
 import java.io.InputStream;
 
 public class TeamcityRecordingConnection implements ITeamcityHttpConnection {
+    /** Recorder. */
     @Inject private TeamcityRecorder recorder;
 
     /** {@inheritDoc} */
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/CtxListener.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/CtxListener.java
index 249d554..914da02 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/CtxListener.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/CtxListener.java
@@ -29,7 +29,7 @@ import com.google.inject.Guice;
 import com.google.inject.Injector;
 import org.apache.ignite.Ignite;
 import org.apache.ignite.ci.IAnalyticsEnabledTeamcity;
-import org.apache.ignite.ci.ITcServerProvider;
+import org.apache.ignite.ci.teamcity.pure.ITcServerProvider;
 import org.apache.ignite.ci.ITcHelper;
 import org.apache.ignite.ci.TcHelper;
 import org.apache.ignite.ci.db.TcHelperDb;
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 9b9da8c..fa57009 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
@@ -17,6 +17,7 @@
 package org.apache.ignite.ci.web.rest.visa;
 
 import java.util.List;
+import javax.annotation.Nonnull;
 import javax.servlet.ServletContext;
 import javax.servlet.http.HttpServletRequest;
 import javax.ws.rs.GET;
@@ -25,6 +26,7 @@ import javax.ws.rs.Produces;
 import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
+import org.apache.ignite.ci.tcmodel.hist.BuildRef;
 import org.apache.ignite.ci.user.ICredentialsProv;
 import org.apache.ignite.ci.tcbot.visa.ContributionToCheck;
 import org.apache.ignite.ci.tcbot.visa.TcBotTriggerAndSignOffService;
@@ -43,20 +45,32 @@ public class TcBotVisaService {
     @Context
     private HttpServletRequest req;
 
+    /**
+     * @param srvId Server id.
+     */
     @GET
     @Path("contributions")
-    public List<ContributionToCheck> contributions(
-        @Nullable @QueryParam("serverId") String srvId
-    ) {
+    public List<ContributionToCheck> contributions(@Nullable @QueryParam("serverId") String srvId) {
+        if (!ICredentialsProv.get(req).hasAccess(srvId))
+            throw ServiceUnauthorizedException.noCreds(srvId);
 
-        final ICredentialsProv prov = ICredentialsProv.get(req);
+        return CtxListener.getInjector(ctx)
+            .getInstance(TcBotTriggerAndSignOffService.class).getContributionsToCheck(srvId);
+    }
 
+    @GET
+    @Path("buildsForContribution")
+    public List<BuildRef> buildsForContribution(@Nullable @QueryParam("serverId") String srvId,
+        @Nonnull @QueryParam("suiteId") String suiteId,
+        @QueryParam("prId") String prId) {
+        ICredentialsProv prov = ICredentialsProv.get(req);
         if (!prov.hasAccess(srvId))
             throw ServiceUnauthorizedException.noCreds(srvId);
 
+
         TcBotTriggerAndSignOffService instance = CtxListener.getInjector(ctx)
             .getInstance(TcBotTriggerAndSignOffService.class);
 
-        return instance.getContributionsToCheck(srvId);
+        return instance.buildsForContribution(srvId, prov, suiteId, prId);
     }
 }
diff --git a/ignite-tc-helper-web/src/test/java/org/apache/ignite/ci/di/DiContextTest.java b/ignite-tc-helper-web/src/test/java/org/apache/ignite/ci/di/DiContextTest.java
index 1888ea0..1c04650 100644
--- a/ignite-tc-helper-web/src/test/java/org/apache/ignite/ci/di/DiContextTest.java
+++ b/ignite-tc-helper-web/src/test/java/org/apache/ignite/ci/di/DiContextTest.java
@@ -20,15 +20,13 @@ import com.google.common.base.Preconditions;
 import com.google.inject.Guice;
 import com.google.inject.Injector;
 import org.apache.ignite.ci.ITcHelper;
-import org.apache.ignite.ci.ITcServerProvider;
-import org.apache.ignite.ci.TcHelper;
+import org.apache.ignite.ci.teamcity.pure.ITcServerProvider;
 import org.apache.ignite.ci.observer.BuildObserver;
 import org.apache.ignite.ci.observer.ObserverTask;
 import org.apache.ignite.ci.web.TcUpdatePool;
 import org.junit.Test;
 
 import java.util.Collection;
-import java.util.Iterator;
 
 import static org.junit.Assert.assertTrue;