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/10 18:23:25 UTC

[ignite-teamcity-bot] branch master updated: Steps to get visa adding to UI: Comment JIRA button was supported - Fixes #33.

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 b783352  Steps to get visa adding to UI: Comment JIRA button was supported - Fixes #33.
b783352 is described below

commit b78335229dcdc7655963f9d4e187947bfcdf6cca
Author: Dmitriy Pavlov <dp...@apache.org>
AuthorDate: Wed Oct 10 21:23:15 2018 +0300

    Steps to get visa adding to UI: Comment JIRA button was supported - Fixes #33.
    
    Signed-off-by: Dmitriy Pavlov <dp...@apache.org>
---
 .../main/java/org/apache/ignite/ci/ITeamcity.java  |   4 +-
 .../apache/ignite/ci/jira/IJiraIntegration.java    |   2 +-
 ...onToCheck.java => ContributionCheckStatus.java} |  26 ++---
 .../ignite/ci/tcbot/visa/ContributionToCheck.java  |   3 +
 .../tcbot/visa/TcBotTriggerAndSignOffService.java  | 102 +++++++++++++-----
 .../apache/ignite/ci/tcmodel/hist/BuildRef.java    |  12 +++
 .../ci/teamcity/ignited/ITeamcityIgnited.java      |   2 +-
 .../apache/ignite/ci/web/rest/TriggerBuilds.java   |   6 ++
 .../ignite/ci/web/rest/visa/TcBotVisaService.java  |   8 +-
 .../src/main/webapp/css/style-1.5.css              |  22 ++++
 ignite-tc-helper-web/src/main/webapp/js/prs-1.0.js | 119 ++++++++++++++++-----
 11 files changed, 228 insertions(+), 78 deletions(-)

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 38da9c1..f213ca3 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
@@ -288,14 +288,14 @@ public interface ITeamcity extends ITeamcityConn {
     boolean isJiraTokenAvailable();
 
     /**
-     * @param ticket JIRA ticket full name.
+     * @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.
      */
-    String sendJiraComment(String ticket, String comment) throws IOException;
+    public String sendJiraComment(String ticket, String comment) throws IOException;
 
     /**
      * @param url URL for JIRA integration.
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/IJiraIntegration.java
index 633ab3a..a20ead4 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/IJiraIntegration.java
@@ -31,7 +31,7 @@ public interface IJiraIntegration {
      * @param prov Credentials.
      * @param buildTypeId Suite name.
      * @param branchForTc Branch for TeamCity.
-     * @param ticket JIRA ticket full name.
+     * @param ticket JIRA ticket full name. E.g. IGNITE-5555
      * @return {@code True} if JIRA was notified.
      */
     public String notifyJira(String srvId, ICredentialsProv prov, String buildTypeId, String branchForTc,
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/ContributionCheckStatus.java
similarity index 62%
copy from ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcbot/visa/ContributionToCheck.java
copy to ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcbot/visa/ContributionCheckStatus.java
index 1b189ab..55b0c3f 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/ContributionCheckStatus.java
@@ -17,24 +17,14 @@
 package org.apache.ignite.ci.tcbot.visa;
 
 /**
- *
+ * Status of contribution check details returned from server
  */
-@SuppressWarnings("PublicField") public class ContributionToCheck {
-    /** Pr number. */
-    public Integer prNumber;
-
-    /** Pr title. */
-    public String prTitle;
-
-    /** Pr author. */
-    public String prAuthor;
-
-    /** Pr author avatar url. */
-    public String prAuthorAvatarUrl;
-
-    /** Pr html url. */
-    public String prHtmlUrl;
+@SuppressWarnings("PublicField") public class ContributionCheckStatus {
+    public int queuedBuilds;
+    public int runningBuilds;
+    /** Branch with finished run all results, null if run all is running or in case there was no run alls at all. */
+    public String branchWithFinishedRunAll;
 
-    /** Branch Name for team city */
-    public String tcBranchName;
+    /** Resolved run all branch: Some branch probably with finished or queued builds in in, or default pull/nnnn/head. */
+    public String resolvedBranch;
 }
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 1b189ab..a19e3ca 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
@@ -37,4 +37,7 @@ package org.apache.ignite.ci.tcbot.visa;
 
     /** Branch Name for team city */
     public String tcBranchName;
+
+    /** JIRA issue without server URL, but with project name */
+    public String jiraIssueId;
 }
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 1a5fd08..2a263d2 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
@@ -19,9 +19,11 @@ package org.apache.ignite.ci.tcbot.visa;
 
 import com.google.common.base.Strings;
 import com.google.inject.Provider;
+import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
 import java.util.stream.Collectors;
+import javax.annotation.Nonnull;
 import javax.inject.Inject;
 import javax.ws.rs.QueryParam;
 import org.apache.ignite.ci.tcmodel.hist.BuildRef;
@@ -30,7 +32,6 @@ 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;
 import org.apache.ignite.ci.github.ignited.IGitHubConnIgnitedProvider;
 import org.apache.ignite.ci.github.pure.IGitHubConnection;
 import org.apache.ignite.ci.github.pure.IGitHubConnectionProvider;
@@ -62,9 +63,9 @@ public class TcBotTriggerAndSignOffService {
 
     /**
      * @param pr Pull Request.
-     * @return JIRA ticket number.
+     * @return JIRA ticket full name or empty string.
      */
-    @NotNull public static String getTicketId(PullRequest pr) {
+    @NotNull public static String getTicketFullName(PullRequest pr) {
         String ticketId = "";
 
         if (pr.getTitle().startsWith("IGNITE-")) {
@@ -74,12 +75,13 @@ public class TcBotTriggerAndSignOffService {
             while (endIdx < pr.getTitle().length() && Character.isDigit(pr.getTitle().charAt(endIdx)))
                 endIdx++;
 
-            ticketId = pr.getTitle().substring(beginIdx, endIdx);
+            ticketId = "IGNITE-" + pr.getTitle().substring(beginIdx, endIdx);
         }
 
         return ticketId;
     }
 
+
     @NotNull public String triggerBuildsAndObserve(
         @Nullable String srvId,
         @Nullable String branchForTc,
@@ -100,7 +102,7 @@ public class TcBotTriggerAndSignOffService {
             builds[i] = teamcity.triggerBuild(suiteIds[i], branchForTc, false, top != null && top);
 
         if (observe != null && observe)
-            jiraRes = observeJira(srvId, branchForTc, ticketId, teamcity, prov, builds);
+            jiraRes = observeJira(srvId, branchForTc, ticketId, prov, builds);
 
         return jiraRes;
     }
@@ -108,8 +110,7 @@ public class TcBotTriggerAndSignOffService {
     /**
      * @param srvId Server id.
      * @param branchForTc Branch for TeamCity.
-     * @param ticketId JIRA ticket number.
-     * @param teamcity TeamCity.
+     * @param ticketFullName JIRA ticket number.
      * @param prov Credentials.
      * @param builds Builds.
      * @return Message with result.
@@ -117,20 +118,19 @@ public class TcBotTriggerAndSignOffService {
     private String observeJira(
         String srvId,
         String branchForTc,
-        @Nullable String ticketId,
-        ITeamcity teamcity,
+        @Nullable String ticketFullName,
         ICredentialsProv prov,
         Build... builds
     ) {
-        if (F.isEmpty(ticketId)) {
+        if (F.isEmpty(ticketFullName)) {
             try {
                 IGitHubConnection gitHubConnection = gitHubConnectionProvider.server(srvId);
 
                 PullRequest pr = gitHubConnection.getPullRequest(branchForTc);
 
-                ticketId = getTicketId(pr);
+                ticketFullName = getTicketFullName(pr);
 
-                if (ticketId.isEmpty()) {
+                if (ticketFullName.isEmpty()) {
                     return "JIRA ticket will not be notified after the tests are completed - " +
                         "PR title \"" + pr.getTitle() + "\" should starts with \"IGNITE-XXXX\"." +
                         " Please, rename PR according to the" +
@@ -143,30 +143,40 @@ public class TcBotTriggerAndSignOffService {
                     "exception happened when server tried to get ticket ID from Pull Request [errMsg=" +
                     e.getMessage() + ']';
             }
+        } else {
+            //todo remove once every ticket is with IGnite prefix
+            ticketFullName = ticketFullName.toUpperCase().startsWith("IGNITE-") ? ticketFullName : "IGNITE-" + ticketFullName;
         }
 
-        buildObserverProvider.get().observe(srvId, prov, "ignite-" + ticketId, builds);
+        buildObserverProvider.get().observe(srvId, prov, "ignite-" + ticketFullName, builds);
 
-        return "JIRA ticket IGNITE-" + ticketId + " will be notified after the tests are completed.";
+        return "JIRA ticket IGNITE-" + ticketFullName + " will be notified after the tests are completed.";
     }
 
+    /**
+     * @param srvId Server id.
+     * @param branchForTc Branch for tc.
+     * @param suiteId Suite id.
+     * @param ticketFullName Ticket full name with IGNITE- prefix.
+     * @param prov Prov.
+     */
     @NotNull
     public SimpleResult commentJiraEx(
         @QueryParam("serverId") @Nullable String srvId,
         @QueryParam("branchName") @Nullable String branchForTc,
         @QueryParam("suiteId") @Nullable String suiteId,
-        @QueryParam("ticketId") @Nullable String ticketId,
+        @QueryParam("ticketId") @Nullable String ticketFullName,
         ICredentialsProv prov) {
         String jiraRes = "";
 
-        if (Strings.isNullOrEmpty(ticketId)) {
+        if (Strings.isNullOrEmpty(ticketFullName)) {
             try {
                 IGitHubConnection gitHubConn = gitHubConnectionProvider.server(srvId);
                 PullRequest pr = gitHubConn.getPullRequest(branchForTc);
 
-                ticketId = getTicketId(pr);
+                ticketFullName = getTicketFullName(pr);
 
-                if (ticketId.isEmpty()) {
+                if (ticketFullName.isEmpty()) {
                     jiraRes = "JIRA ticket can't be commented - " +
                         "PR title \"" + pr.getTitle() + "\" should starts with \"IGNITE-XXXX\"." +
                         " Please, rename PR according to the" +
@@ -178,10 +188,13 @@ public class TcBotTriggerAndSignOffService {
             catch (RuntimeException e) {
                 jiraRes = "Exception happened when server tried to get ticket ID from Pull Request - " + e.getMessage();
             }
+        } else {
+            //todo remove once every ticket is with IGnite prefix
+            ticketFullName = ticketFullName.toUpperCase().startsWith("IGNITE-") ? ticketFullName : "IGNITE-" + ticketFullName;
         }
 
-        if (!Strings.isNullOrEmpty(ticketId)) {
-            jiraRes = jiraIntegration.notifyJira(srvId, prov, suiteId, branchForTc, "ignite-" + ticketId);
+        if (!Strings.isNullOrEmpty(ticketFullName)) {
+            jiraRes = jiraIntegration.notifyJira(srvId, prov, suiteId, branchForTc, ticketFullName);
 
             return new SimpleResult(jiraRes);
         }
@@ -190,8 +203,7 @@ public class TcBotTriggerAndSignOffService {
     }
 
     public List<ContributionToCheck> getContributionsToCheck(String srvId) {
-        IGitHubConnIgnited gitHubConn = gitHubConnIgnitedProvider.server(srvId);
-        List<PullRequest> requests = gitHubConn.getPullRequests();
+        List<PullRequest> requests = gitHubConnIgnitedProvider.server(srvId).getPullRequests();
         if (requests == null)
             return null;
 
@@ -207,25 +219,29 @@ public class TcBotTriggerAndSignOffService {
                 check.prAuthorAvatarUrl = user.avatarUrl();
             }
 
+            check.jiraIssueId = Strings.emptyToNull(getTicketFullName(pr));
+
             return check;
         }).collect(Collectors.toList());
     }
 
-    @Nullable public String findBranchForPr(String srvId, ICredentialsProv prov, String suiteId, String prId) {
+    @Nonnull private List<BuildRef> findRunAllsForPr(String srvId, ICredentialsProv prov, String suiteId, String prId) {
         ITeamcityIgnited srv = teamcityIgnitedProvider.server(srvId, prov);
 
         String branchName = branchForTcA(prId);
         List<BuildRef> buildHist = srv.getBuildHistory(suiteId, branchName);
 
         if (!buildHist.isEmpty())
-            return buildHist.get(0).branchName();
+            return buildHist;
 
+
+        //todo multibranch requestst
         buildHist = srv.getBuildHistory(suiteId, branchForTcB(prId));
 
         if (!buildHist.isEmpty())
-            return buildHist.get(0).branchName();
+            return buildHist;
 
-        return null;
+        return Collections.emptyList();
     }
 
     String branchForTcA(String prId) {
@@ -235,4 +251,38 @@ public class TcBotTriggerAndSignOffService {
     String branchForTcB(String prId) {
         return "pull/" + prId + "/merge";
     }
+
+    /**
+     * @param srvId Server id.
+     * @param prov Prov.
+     * @param suiteId Suite id.
+     * @param prId Pr id.
+     */
+    public ContributionCheckStatus contributionStatus(String srvId, ICredentialsProv prov, String suiteId,
+        String prId) {
+        ContributionCheckStatus status = new ContributionCheckStatus();
+
+        List<BuildRef> allRunAlls = findRunAllsForPr(srvId, prov, suiteId, prId);
+
+        boolean finishedRunAllPresent = allRunAlls.stream().filter(BuildRef::isNotCancelled).anyMatch(BuildRef::isFinished);
+
+        status.branchWithFinishedRunAll = finishedRunAllPresent ? allRunAlls.get(0).branchName : null;
+
+        if (status.branchWithFinishedRunAll == null) {
+            if (!allRunAlls.isEmpty())
+                status.resolvedBranch = allRunAlls.get(0).branchName;
+            else
+                status.resolvedBranch = branchForTcA(prId);
+        }
+        else
+            //todo take into account running/queued
+            status.resolvedBranch = status.branchWithFinishedRunAll;
+
+
+        //todo take into accounts not only run alls:
+        status.queuedBuilds = (int)allRunAlls.stream().filter(BuildRef::isNotCancelled).filter(BuildRef::isQueued).count();
+        status.runningBuilds = (int)allRunAlls.stream().filter(BuildRef::isNotCancelled).filter(BuildRef::isRunning).count();
+
+        return status;
+    }
 }
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcmodel/hist/BuildRef.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcmodel/hist/BuildRef.java
index 6d35959..10d312d 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcmodel/hist/BuildRef.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcmodel/hist/BuildRef.java
@@ -170,4 +170,16 @@ public class BuildRef extends AbstractRef {
     public String state() {
         return state;
     }
+
+    public boolean isFinished() {
+        return STATE_FINISHED.equals(state());
+    }
+
+    public boolean isQueued() {
+        return STATE_QUEUED.equals(state());
+    }
+
+    public boolean isRunning() {
+        return STATE_RUNNING.equals(state());
+    }
 }
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 67ac302..4bdd2cf 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
@@ -27,7 +27,7 @@ public interface ITeamcityIgnited {
     /**
      * @param buildTypeId
      * @param branchName
-     * @return list of builds in history
+     * @return list of builds in history, includes all statuses: queued, running, etc
      */
     public List<BuildRef> getBuildHistory(
         @Nullable String buildTypeId,
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 e86886d..111d335 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
@@ -82,6 +82,12 @@ public class TriggerBuilds {
         return new SimpleResult("Tests started." + (!jiraRes.isEmpty() ? "<br>" + jiraRes : ""));
     }
 
+    /**
+     * @param srvId Server id.
+     * @param branchForTc Branch for tc.
+     * @param suiteId Suite id.
+     * @param ticketId Ticket full name with IGNITE- prefix.
+     */
     @GET
     @Path("commentJira")
     public SimpleResult commentJira(
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 bb2c71f..5bc1cbf 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
@@ -26,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.tcbot.visa.ContributionCheckStatus;
 import org.apache.ignite.ci.user.ICredentialsProv;
 import org.apache.ignite.ci.tcbot.visa.ContributionToCheck;
 import org.apache.ignite.ci.tcbot.visa.TcBotTriggerAndSignOffService;
@@ -59,18 +60,17 @@ public class TcBotVisaService {
     }
 
     @GET
-    @Path("findBranchForPr")
-    public SimpleResult findBranchForPr(@Nullable @QueryParam("serverId") String srvId,
+    @Path("contributionStatus")
+    public ContributionCheckStatus contributionStatus(@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 new SimpleResult(instance.findBranchForPr(srvId, prov, suiteId, prId));
+        return instance.contributionStatus(srvId, prov, suiteId, prId);
     }
 }
diff --git a/ignite-tc-helper-web/src/main/webapp/css/style-1.5.css b/ignite-tc-helper-web/src/main/webapp/css/style-1.5.css
index f059b3b..d1065bd 100644
--- a/ignite-tc-helper-web/src/main/webapp/css/style-1.5.css
+++ b/ignite-tc-helper-web/src/main/webapp/css/style-1.5.css
@@ -135,6 +135,28 @@ form, .formgroup {
     width: 100px;
     min-height: 100px;
     height: 100px;
+	border-radius: 3px;
+}
+
+.visaStage {
+	float: left;
+	background: #b4b4b4;
+	height: 30px;
+	width: 30px;
+	color: #FFF;
+	font-weight: bold;
+	font-family: sans-serif;
+	font-size: 15px;
+    display: table-cell;
+    vertical-align: middle;
+	align-items: center;
+	margin: 8px;
+    line-height: 30px;
+	box-shadow: 3px 2px 6px -2px rgba(0,0,0,0.64);
+	border: 1px solid #FFF;
+	border-radius: 15px;
+	transition: ease-in 0.175s;
+	text-align: center;
 }
 
 html {
diff --git a/ignite-tc-helper-web/src/main/webapp/js/prs-1.0.js b/ignite-tc-helper-web/src/main/webapp/js/prs-1.0.js
index dc34fb4..92816ec 100644
--- a/ignite-tc-helper-web/src/main/webapp/js/prs-1.0.js
+++ b/ignite-tc-helper-web/src/main/webapp/js/prs-1.0.js
@@ -9,6 +9,7 @@ function drawTable(srvId, suiteId, element) {
         "                <th>Loading</th>\n" +
         "                <th>...</th>\n" +
         "                <th>.</th>\n" +
+        "                <th>.</th>\n" +
         "            </tr>\n" +
         "            </thead>\n" +
         "        </table>\n");
@@ -83,6 +84,10 @@ function showContributionsTable(result, srvId, suiteId) {
 
             },
             {
+                "data": "jiraIssueId",
+                title: "JIRA Issue"
+            },
+            {
                 "data": "tcBranchName",
                 title: "Resolved Branch Name",
                 "render": function (data, type, row, meta) {
@@ -118,47 +123,109 @@ function showContributionsTable(result, srvId, suiteId) {
     });
 }
 
-function showButtonForPr(srvId, suiteId, prId, branchName) {
-    var showRunRes = "<a id='link_" + prId + "' href='" +
-        prShowHref(srvId, suiteId, branchName) +
-        "'>" +
-        "<button id='show_" + prId + "'>Show " + branchName + " branch report</button></a>";
-
-    return showRunRes;
+function showStageResult(stageNum, prId, passed, failed) {
+    let stageOneStatus = $('#visaStage_' + stageNum + '_' + prId);
+    let html;
+    if (passed) {
+        html = "&#x2714;";
+        stageOneStatus.css('background', '#12AD5E');
+    } else {
+        html = "&#x274C;";
+        if(failed)
+        stageOneStatus.css('background', 'red');
+    }
+    stageOneStatus.html(html);
 }
 
 /* Formatting function for row details - modify as you need */
-function formatContributionDetails(rowData, srvId, suiteId) {
-    // `rowData` is the original data object for the row
+function formatContributionDetails(row, srvId, suiteId) {
+    //  row  is the original data object for the row
+    if(!isDefinedAndFilled(row))
+        return;
 
-    let prId = rowData.prNumber;
+    let prId = row.prNumber;
     var res = "";
-    res+="<div class='formgroup'>";
-    res+="<table cellpadding='5' cellspacing='0' border='0' style='padding-left:50px;'>\n" +
-        "        <tr>\n" +
-        "            <td>PR number:</td>\n" +
-        "            <td>" + rowData.prNumber + "</td>\n" +
+    res += "<div class='formgroup'>";
+    res += "<table cellpadding='5' cellspacing='0' border='0' style='padding-left:50px;'>\n";
+
+    //icon of stage
+    res += "<tr>\n" +
+        "                <th><span class='visaStage' id='visaStage_1_" + prId + "'></span></th>\n" +
+        "                <th><span class='visaStage' id='visaStage_2_" + prId + "'></span></th>\n" +
+        "                <th><span class='visaStage' id='visaStage_3_" + prId + "'></span></th>\n" +
+         "                <th><span class='visaStage' id='visaStage_4_" + prId + "'></span></th>\n" +
+        //todo validityCheck;"                <th><span class='visaStage' id='visaStage_5_" + prId + "'></span></th>\n" +
+        "            </tr>\n";
+
+    //caption of stage
+    res += "<tr>\n" +
+        "                <td>PR with issue name</td>\n" +
+        "                <td>Build is triggered</td>\n" +
+        "                <td>Results ready</td>\n" +
+        "                <td>JIRA comment</td>\n" +
+        //todo  "                <td>Validity check</td>\n" +
+        "            </tr>\n";
+
+    //action for stage
+    res += "        <tr>\n" +
+        "            <td>Edit PR: " + "<a href='" + row.prHtmlUrl + "'>#" + row.prNumber + "</a>" + "</td>\n" +
+        "               <td id='triggerBuildFor" + prId + "'>Loading builds...</td>\n" +
+        "               <td id='showResultFor" + prId + "'>Loading builds...</td>\n" +
+        "               <td id='commentJiraFor" + prId + "'></td>\n" +
         "        </tr>" +
-        "        <tr>\n" +
-        "            <td>Show Run All Results:</td>\n" +
-        "            <td id='branchFor_" + prId + "'>Loading builds...</td>\n" +
-        "        </tr>\n" +
         "    </table>";
-    res+="</div>";
+
+    res += "</div>";
+
+
     $.ajax({
-        url: "rest/visa/findBranchForPr?serverId=" + srvId +
+        url: "rest/visa/contributionStatus?serverId=" + srvId +
             "&suiteId=" + suiteId +
             "&prId=" + prId,
         success:
             function (result) {
-                // console.log("Contribution " + prId + " bransh: " + result + " ");
-                let branchName = result.result;
-                let tdForPr = $('#branchFor_' + prId);
-                if (isDefinedAndFilled(branchName)) {
-                    tdForPr.html(showButtonForPr(srvId, suiteId, prId, branchName));
+                let finishedBranch = result.branchWithFinishedRunAll;
+                let tdForPr = $('#showResultFor' + prId);
+                let buildIsCompleted = isDefinedAndFilled(finishedBranch);
+                let hasJiraIssue = isDefinedAndFilled(row.jiraIssueId);
+                if (buildIsCompleted) {
+                    tdForPr.html("<a id='link_" + prId + "' href='" + prShowHref(srvId, suiteId, finishedBranch) +  "'>" +
+                        "<button id='show_" + prId + "'>Show " + finishedBranch + " report</button></a>");
+
+                    if (hasJiraIssue)
+                        {
+                            let jiraBtn;
+                            jiraBtn = "<button onclick='" +
+                                "commentJira(" +
+                                "\"" + srvId + "\", " +
+                                "\"" + suiteId + "\", " +
+                                "\"" + finishedBranch + "\", " +
+                                "\"" + row.jiraIssueId + "\"" +
+                                ")'>Comment JIRA</button>";
+                            $('#commentJiraFor' + prId).html(jiraBtn);
+                        }
                 } else {
                     tdForPr.html("No builds, please trigger " + suiteId);
                 }
+
+                let hasQueued = result.queuedBuilds > 0 || result.runningBuilds > 0;
+
+                showStageResult(1, prId, hasJiraIssue, !hasJiraIssue);
+                let noNeedToTrigger = hasQueued || buildIsCompleted;
+                showStageResult(2, prId, noNeedToTrigger, false);
+                showStageResult(3, prId, buildIsCompleted, false);
+
+                if(isDefinedAndFilled(result.resolvedBranch)) {
+                    var trig ="";
+                    trig+= "<button onClick='triggerBuilds(\"" + srvId + "\", \"" + suiteId + "\", \"" +
+                        result.resolvedBranch + "\", false, false)'" ;
+
+                    if(noNeedToTrigger) {
+                        trig+=" class='disabledbtn'";
+                    }
+                    trig+=">Trigger build</button>";
+                    $('#triggerBuildFor' + prId).html(trig);
+                }
             }
     });
     return res;