You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@ignite.apache.org by GitBox <gi...@apache.org> on 2018/12/13 16:03:51 UTC

[GitHub] asfgit closed pull request #101: IGNITE-10454 add ticket status to mutes

asfgit closed pull request #101: IGNITE-10454 add ticket status to mutes
URL: https://github.com/apache/ignite-teamcity-bot/pull/101
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

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 ba70533e..1c084c1d 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
@@ -17,8 +17,7 @@
 
 package org.apache.ignite.ci;
 
-import java.util.Collection;
-import java.util.List;
+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.teamcity.restcached.ITcServerProvider;
@@ -65,4 +64,12 @@
      * @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 29f06807..4a62cb7b 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
@@ -123,6 +123,12 @@
      */
     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.
      */
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 08f7a428..23849a29 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
@@ -595,6 +595,11 @@ public Executor getExecutor() {
         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);
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 af287190..cd88aea3 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
@@ -180,6 +180,14 @@ public void init(@Nullable String tcName) {
         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;
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 c293e91b..f4e8c093 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
@@ -18,12 +18,12 @@
 package org.apache.ignite.ci;
 
 import com.fasterxml.jackson.databind.ObjectMapper;
-import java.util.Collection;
+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.conf.BranchesTracked;
+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;
@@ -184,6 +184,22 @@ public TcHelper() {
         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.
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
new file mode 100644
index 00000000..f9aa8e94
--- /dev/null
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/Fields.java
@@ -0,0 +1,26 @@
+/*
+ * 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;
+
+/**
+ *
+ */
+public class Fields {
+    /** Ticket status. */
+    public Status status;
+}
\ No newline at end of file
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 d952b2fd..12b3836a 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
@@ -42,6 +42,16 @@
     public Visa notifyJira(String srvId, ICredentialsProv prov, String buildTypeId, String branchForTc,
         String ticket);
 
+    /**
+     * Produce wrapper for collection of Jira tickets for given server.
+     *
+     * @param srvId Server id.
+     * @param prov Prov.
+     * @param ticketId Ticket id.
+     * @return Jira tickets.
+     */
+    public Tickets getTickets(String srvId, ICredentialsProv prov, String ticketId);
+
     /** */
     public String jiraUrl();
 
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/Jira.java
index b23d840a..8755b90c 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/Jira.java
@@ -60,6 +60,11 @@
         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);
+    }
+
     /** {@inheritDoc} */
     @Override public String generateTicketUrl(String ticketFullName) {
         Preconditions.checkState(!isNullOrEmpty(jiraUrl), "Jira URL is not configured for this server.");
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
new file mode 100644
index 00000000..62ceeb3d
--- /dev/null
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/Status.java
@@ -0,0 +1,33 @@
+/*
+ * 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;
+
+/**
+ * Status for Jira ticket.
+ */
+public class Status {
+    /** Status text (open, resolved, etc). */
+    public String name;
+
+    /**
+     * @param name Name.
+     */
+    public Status(String name) {
+        this.name = 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
new file mode 100644
index 00000000..f69c1bf7
--- /dev/null
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/Ticket.java
@@ -0,0 +1,47 @@
+/*
+ * 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;
+
+/**
+ * See example of GSON here
+ * https://issues.apache.org/jira/rest/api/2/issue/IGNITE-123
+ */
+public class Ticket {
+    /** Id. */
+    public long id;
+
+    /** Ticket full name like "IGNITE-123". */
+    public String key;
+
+    /** Fields. */
+    public Fields fields;
+
+    /**
+     * @return Ignite id (like 123 in IGNITE-123).
+     */
+    public int igniteId() {
+        return Integer.valueOf(key.substring("IGNITE-".length()));
+    }
+
+    /**
+     * @return Ticket status (open, resolved, etc);
+     */
+    public String status() {
+        return fields.status.name;
+    }
+}
\ 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
new file mode 100644
index 00000000..f265b375
--- /dev/null
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/Tickets.java
@@ -0,0 +1,58 @@
+/*
+ * 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;
+
+import java.util.Collection;
+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
+ */
+public class Tickets {
+    /** Start at. */
+    public int startAt;
+
+    /** Max amount of tickets on the page. */
+    public int maxResults;
+
+    /** Total tickets. */
+    public int total;
+
+    /** Jira tickets. */
+    public Collection<Ticket> issues;
+
+    /**
+     * @return Start index for next page. Return -1 if it is last page.
+     */
+    public int nextStart() {
+        int next = startAt + maxResults;
+
+        if (next < total)
+            return next;
+
+        return -1;
+    }
+
+    /**
+     * @return Jira tickets.
+     */
+    public Collection<Ticket> issuesNotNull() {
+        return issues == null ? Collections.emptyList() : issues;
+    }
+}
\ No newline at end of file
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
new file mode 100644
index 00000000..4a6e4142
--- /dev/null
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/ignited/JiraTicketDao.java
@@ -0,0 +1,113 @@
+/*
+ * 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 com.google.common.base.Preconditions;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Set;
+import javax.cache.Cache;
+import javax.inject.Inject;
+import javax.inject.Provider;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.ci.db.TcHelperDb;
+import org.apache.ignite.ci.di.AutoProfiling;
+import org.apache.ignite.ci.jira.Ticket;
+import org.apache.ignite.ci.teamcity.ignited.IStringCompactor;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.internal.U;
+
+/**
+ *
+ */
+public class JiraTicketDao {
+    /** Cache name. */
+    public static final String TEAMCITY_JIRA_TICKET_CACHE_NAME = "jiraTickets";
+
+    /** Ignite provider. */
+    @Inject private Provider<Ignite> igniteProvider;
+
+    /** Builds cache. */
+    private IgniteCache<Long, TicketCompacted> jiraCache;
+
+    /** Compactor. */
+    @Inject private IStringCompactor compactor;
+
+    /**
+     *
+     */
+    public void init() {
+        jiraCache = igniteProvider.get().getOrCreateCache(TcHelperDb.getCache8PartsConfig(TEAMCITY_JIRA_TICKET_CACHE_NAME));
+    }
+
+    /**
+     * @param srvIdMaskHigh Server id mask high.
+     * @return Jira tickets.
+     */
+    public Set<Ticket> getTickets(int srvIdMaskHigh) {
+        Preconditions.checkNotNull(jiraCache, "init() was not called");
+        long srvId = (long) srvIdMaskHigh << 32;
+
+        Set<Ticket> res = new HashSet<>();
+
+        for (Cache.Entry<Long, TicketCompacted> entry : jiraCache) {
+            if ((entry.getKey() & srvId) == srvId)
+                res.add(entry.getValue().toTicket(compactor));
+        }
+
+        return res;
+    }
+
+    /**
+     * Combine server and project into key for storage.
+     *
+     * @param srvIdMaskHigh Server id mask high.
+     * @param igniteId Ticket.
+     * @return Key from server-project pair.
+     */
+    public static long ticketToCacheKey(int srvIdMaskHigh, int igniteId) {
+        return (long) igniteId | (long) srvIdMaskHigh << 32;
+    }
+
+    /**
+     * Save small part of loaded mutes.
+     *
+     * @param srvIdMaskHigh Server id mask high.
+     * @param chunk Chunk.
+     */
+    @AutoProfiling
+    public void saveChunk(int srvIdMaskHigh, Collection<Ticket> chunk) {
+        Preconditions.checkNotNull(jiraCache, "init() was not called");
+
+        if (F.isEmpty(chunk))
+            return;
+
+        HashMap<Long, TicketCompacted> compactedTickets = new HashMap<>(U.capacity(chunk.size()));
+
+        for (Ticket ticket : chunk) {
+            long key = ticketToCacheKey(srvIdMaskHigh, ticket.igniteId());
+            TicketCompacted val = new TicketCompacted(ticket, compactor);
+
+            compactedTickets.put(key, val);
+        }
+
+        jiraCache.putAll(compactedTickets);
+    }
+}
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
new file mode 100644
index 00000000..c5dabbcc
--- /dev/null
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/ignited/JiraTicketSync.java
@@ -0,0 +1,94 @@
+/*
+ * 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.util.Collection;
+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.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.internal.util.typedef.F;
+
+/**
+ * 
+ */
+public class JiraTicketSync {
+    /** Scheduler. */
+    @Inject private IScheduler scheduler;
+
+    /** Mute DAO. */
+    @Inject private JiraTicketDao jiraDao;
+
+    /** Jira integration provider. */
+    @Inject IJiraIntegrationProvider jiraIntegrationProvider;
+
+    /**
+     * @param taskName Task name.
+     * @param srvIdMaskHigh Server id mask high.
+     * @param creds Credentials.
+     * @param conn Connection.
+     */
+    public void ensureActualizeJiraTickets(String taskName, int srvIdMaskHigh, ICredentialsProv creds, ITeamcityConn conn) {
+        scheduler.sheduleNamed(taskName, () -> actualizeJiraTickets(srvIdMaskHigh, conn, creds), 15, TimeUnit.MINUTES);
+    }
+
+    /**
+     * @param srvIdMaskHigh Server id mask high.
+     * @param conn Connection.
+     * @param creds Credentials.
+     */
+    @MonitoredTask(name = "Actualize Jira", nameExtArgsIndexes = {0})
+    private String actualizeJiraTickets(int srvIdMaskHigh, ITeamcityConn conn, ICredentialsProv creds) {
+        String srvId = conn.serverId();
+        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);
+        Collection<Ticket> page = tickets.issuesNotNull();
+
+        if (F.isEmpty(page))
+            return "Something went wrong - no tickets found. Check jira availability.";
+
+        jiraDao.saveChunk(srvIdMaskHigh, page);
+
+        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();
+
+            tickets = jira.getTickets(srvId, creds, url);
+
+            page = tickets.issuesNotNull();
+
+            if (F.isEmpty(page))
+                break;
+
+            jiraDao.saveChunk(srvIdMaskHigh, page);
+
+            ticketsSaved += page.size();
+        }
+
+        return "Jira tickets saved " + ticketsSaved + " for " + srvId;
+    }
+}
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
new file mode 100644
index 00000000..8989994f
--- /dev/null
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/jira/ignited/TicketCompacted.java
@@ -0,0 +1,61 @@
+/*
+ * 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 org.apache.ignite.ci.jira.Fields;
+import org.apache.ignite.ci.jira.Status;
+import org.apache.ignite.ci.jira.Ticket;
+import org.apache.ignite.ci.teamcity.ignited.IStringCompactor;
+
+/**
+ *
+ */
+public class TicketCompacted {
+    /** Id. */
+    public long id;
+
+    /** Ticket full name like "IGNITE-123". */
+    public int igniteId;
+
+    /** Fields. */
+    public int status;
+
+    /**
+     * @param ticket Jira ticket.
+     * @param comp Compactor.
+     */
+    public TicketCompacted(Ticket ticket, IStringCompactor comp) {
+        id = ticket.id;
+        igniteId = Integer.valueOf(ticket.key.substring("IGNITE-".length()));
+        status = comp.getStringId(ticket.fields.status.name);
+    }
+
+    /**
+     * @param comp Compactor.
+     */
+    public Ticket toTicket(IStringCompactor comp) {
+        Ticket ticket = new Ticket();
+
+        ticket.id = id;
+        ticket.key = "IGNITE-" + igniteId;
+        ticket.fields = new Fields();
+        ticket.fields.status = new Status(comp.getStringFromId(status));
+
+        return ticket;
+    }
+}
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 e45b3534..aecc4d16 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
@@ -182,7 +182,7 @@ else if (RUNNING_STATUS.equals(buildsStatus))
     public Set<MuteInfo> getMutes(String srvId, String projectId, ICredentialsProv creds) {
         ITeamcityIgnited ignited = teamcityIgnitedProvider.server(srvId, creds);
 
-        Set<MuteInfo> infos = ignited.getMutes(projectId);
+        Set<MuteInfo> infos = ignited.getMutes(projectId, creds);
 
         for (MuteInfo info : infos)
             info.assignment.muteDate = THREAD_FORMATTER.get().format(new Date(info.assignment.timestamp()));
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcmodel/mute/MuteAssignment.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcmodel/mute/MuteAssignment.java
index 0c1c3ff4..d70f894c 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcmodel/mute/MuteAssignment.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcmodel/mute/MuteAssignment.java
@@ -22,7 +22,7 @@
 import org.apache.ignite.ci.util.TimeUtil;
 
 /**
- *
+ * Mute additional information. Contains mute date and it's comment.
  */
 @XmlRootElement(name = "assignment")
 public class MuteAssignment {
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcmodel/mute/MuteInfo.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcmodel/mute/MuteInfo.java
index 87925f89..61a153b9 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcmodel/mute/MuteInfo.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcmodel/mute/MuteInfo.java
@@ -26,7 +26,10 @@
 import org.jetbrains.annotations.NotNull;
 
 /**
- *
+ * TeamCity mute.
+ * <p>
+ * See example of XML here
+ * https://ci.ignite.apache.org/app/rest/mutes/
  */
 @XmlRootElement(name = "mute")
 @XmlAccessorType(XmlAccessType.FIELD)
@@ -43,6 +46,9 @@
     /** Target. */
     @XmlElement public MuteTarget target;
 
+    /** Jira ticket status. TeamCity don't send it, we fill it when send mutes.html. */
+    @XmlElement public String ticketStatus;
+
     /** {@inheritDoc} */
     @Override public boolean equals(Object o) {
         if (this == o)
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcmodel/mute/MuteScope.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcmodel/mute/MuteScope.java
index ac2ae28d..e2137d62 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcmodel/mute/MuteScope.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcmodel/mute/MuteScope.java
@@ -26,7 +26,7 @@
 import org.apache.ignite.ci.tcmodel.conf.Project;
 
 /**
- *
+ * Mute additional information. Contains project or build types - scope affected by mute.
  */
 @XmlAccessorType(XmlAccessType.FIELD)
 public class MuteScope {
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcmodel/mute/MuteTarget.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcmodel/mute/MuteTarget.java
index 2d437de3..cd52735d 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcmodel/mute/MuteTarget.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcmodel/mute/MuteTarget.java
@@ -25,7 +25,7 @@
 import org.apache.ignite.ci.tcmodel.result.tests.TestRef;
 
 /**
- * Mute target (e.g. muted test).
+ * Mute additional information. Contains what was muted (tests, problems).
  */
 @XmlAccessorType(XmlAccessType.FIELD)
 public class MuteTarget {
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcmodel/mute/Mutes.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcmodel/mute/Mutes.java
index 9b7fe407..c8177b48 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcmodel/mute/Mutes.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcmodel/mute/Mutes.java
@@ -31,9 +31,8 @@
 /**
  * Mute entities from TeamCity. On TeamCity this object represent page with several mutes.
  * <p>
- * But we unite mutes into single object to store in mute cache.
- * <p>
- * See https://ci.ignite.apache.org/app/rest/mutes/
+ * See example of XML here
+ * https://ci.ignite.apache.org/app/rest/mutes/
  */
 @XmlRootElement(name = "mutes")
 @XmlAccessorType(XmlAccessType.FIELD)
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 36a54e98..ead6dd71 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
@@ -30,6 +30,7 @@
 import org.apache.ignite.ci.teamcity.ignited.buildtype.BuildTypeRefCompacted;
 import org.apache.ignite.ci.teamcity.ignited.change.ChangeCompacted;
 import org.apache.ignite.ci.teamcity.ignited.fatbuild.FatBuildCompacted;
+import org.apache.ignite.ci.user.ICredentialsProv;
 import org.jetbrains.annotations.NotNull;
 
 /**
@@ -59,9 +60,10 @@
 
     /**
      * @param projectId Project id.
+     * @param creds Credentials.
      * @return Mutes for associated server and given project pair.
      */
-    Set<MuteInfo> getMutes(String projectId);
+    public Set<MuteInfo> getMutes(String projectId, ICredentialsProv creds);
 
     /**
      * Return all builds for branch and suite with finish status.
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 d305d5b4..4a4e19f3 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
@@ -27,6 +27,9 @@
 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.mute.MuteInfo;
 import org.apache.ignite.ci.teamcity.ignited.mute.MuteDao;
@@ -50,6 +53,8 @@
 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;
@@ -108,6 +113,12 @@
     /** 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;
 
@@ -146,6 +157,7 @@ public void init(String srvId, ITeamcityConn conn) {
         changesDao.init();
         runHistCompactedDao.init();
         muteDao.init();
+        jiraTicketDao.init();
     }
 
     /**
@@ -326,10 +338,45 @@ else if (midValStartDate.before(key))
     }
 
     /** {@inheritDoc} */
-    @Override public Set<MuteInfo> getMutes(String projectId) {
+    @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);
 
-        return muteDao.getMutes(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} */
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/TeamcityIgnitedModule.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/TeamcityIgnitedModule.java
index a6d76f28..1dd0fe55 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/TeamcityIgnitedModule.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/TeamcityIgnitedModule.java
@@ -18,6 +18,8 @@
 
 import com.google.inject.AbstractModule;
 import com.google.inject.internal.SingletonScope;
+import org.apache.ignite.ci.jira.ignited.JiraTicketDao;
+import org.apache.ignite.ci.jira.ignited.JiraTicketSync;
 import org.apache.ignite.ci.teamcity.ignited.buildcondition.BuildConditionDao;
 import org.apache.ignite.ci.teamcity.ignited.buildref.BuildRefDao;
 import org.apache.ignite.ci.teamcity.ignited.buildref.BuildRefSync;
@@ -29,6 +31,7 @@
 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.ITeamcityHttpConnection;
@@ -58,6 +61,9 @@
         bind(RunHistCompactedDao.class).in(new SingletonScope());
         bind(RunHistSync.class).in(new SingletonScope());
         bind(MuteDao.class).in(new SingletonScope());
+        bind(MuteSync.class).in(new SingletonScope());
+        bind(JiraTicketDao.class).in(new SingletonScope());
+        bind(JiraTicketSync.class).in(new SingletonScope());
 
         bind(IStringCompactor.class).to(IgniteStringCompactor.class).in(new SingletonScope());
 
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/mute/MuteDao.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/mute/MuteDao.java
index a955701d..7dfb0178 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/mute/MuteDao.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/mute/MuteDao.java
@@ -62,9 +62,9 @@ public void init() {
      * @return Server mutes.
      */
     @AutoProfiling
-    public SortedSet<MuteInfo> getMutes(long srvIdMaskHigh) {
+    public SortedSet<MuteInfo> getMutes(int srvIdMaskHigh) {
         Preconditions.checkNotNull(muteCache, "init() was not called");
-        long srvId = srvIdMaskHigh << 32;
+        long srvId = (long) srvIdMaskHigh << 32;
 
         TreeSet<MuteInfo> res = new TreeSet<>();
 
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/mute/MuteSync.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/mute/MuteSync.java
index 53f5a573..ee118e45 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/mute/MuteSync.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/mute/MuteSync.java
@@ -23,8 +23,10 @@
 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.IJiraIntegrationProvider;
 import org.apache.ignite.ci.tcmodel.mute.MuteInfo;
 import org.apache.ignite.ci.teamcity.pure.ITeamcityConn;
+import org.apache.ignite.ci.user.ICredentialsProv;
 import org.apache.ignite.internal.util.typedef.F;
 
 /**
@@ -48,6 +50,8 @@ public void ensureActualizeMutes(String taskName, String projectId, int srvIdMas
      * Refresh mutes for given project.
      *
      * @param projectId Project id.
+     * @param srvIdMaskHigh Server id mask high.
+     * @param conn TeamCity connection.
      * @return Message with loading result.
      */
     @MonitoredTask(name = "Actualize Mute", nameExtArgsIndexes = {0})
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/util/HttpUtil.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/util/HttpUtil.java
index 817fa047..24181899 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/util/HttpUtil.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/util/HttpUtil.java
@@ -228,7 +228,7 @@ public static String sendPostAsStringToGit(String githubAuthTok, String url, Str
     }
 
     /**
-     * Send POST request to the GitHub url.
+     * Send POST request to the JIRA url.
      *
      * @param jiraAuthTok Authorization Base64 token.
      * @param url URL.
@@ -257,7 +257,37 @@ public static String sendPostAsStringToJira(String jiraAuthTok, String url, Stri
 
         logger.info("\nSending 'POST' request to URL : " + url + "\n" + body);
 
-        try (InputStream inputStream = getInputStream(con)){
+        try (InputStream inputStream = getInputStream(con)) {
+            return readIsToString(inputStream);
+        }
+    }
+
+    /**
+     * Send GET request to the JIRA url.
+     *
+     * @param jiraAuthTok Jira auth token.
+     * @param url Url.
+     */
+    public static String sendGetToJira(String jiraAuthTok, String url) throws IOException {
+        Stopwatch started = Stopwatch.createStarted();
+        URL obj = new URL(url);
+        HttpURLConnection con = (HttpURLConnection)obj.openConnection();
+        Charset charset = StandardCharsets.UTF_8;
+
+        con.setRequestProperty("accept-charset", charset.toString());
+        con.setRequestProperty("Authorization", "Basic " + jiraAuthTok);
+        con.setRequestProperty("content-type", "application/json");
+        con.setRequestProperty("Connection", "Keep-Alive");
+        con.setRequestProperty("Keep-Alive", "header");
+
+        con.setRequestMethod("GET");
+
+        int resCode = con.getResponseCode();
+
+        logger.info(Thread.currentThread().getName() + ": Required: " + started.elapsed(TimeUnit.MILLISECONDS)
+            + "ms : Sending 'GET' request to : " + url + " Response: " + resCode);
+
+        try (InputStream inputStream = getInputStream(con)) {
             return readIsToString(inputStream);
         }
     }
diff --git a/ignite-tc-helper-web/src/main/webapp/mutes.html b/ignite-tc-helper-web/src/main/webapp/mutes.html
index b714b8cb..ee8e7675 100644
--- a/ignite-tc-helper-web/src/main/webapp/mutes.html
+++ b/ignite-tc-helper-web/src/main/webapp/mutes.html
@@ -54,30 +54,30 @@
             $.ajax({
                     url: "rest/tracked/mutes?serverId=apache",
                     success: function (result) {
-                        showVisasTable(result);
+                        showTable(result);
                     },
                     error: showErrInLoadStatus
                 }
             );
         }
 
-        function showVisasTable(result) {
+        function showTable(result) {
             // Debug info
             // if (isDefinedAndFilled(result)) {
             //     result.sort((a, b) => (a.id < b.id) ? -1 : ((a.id > b.id) ? 1 : 0));
             //     console.log(result);
             // }
 
-            let visasTable = $('#visasTable');
+            let table = $('#table');
 
             let testNameMatcher = new RegExp("\\.(\\w+\\d*):.*\\.([a-z][$\\w]+\\w+\\d*)");
             let testNameMatcher2 = new RegExp("(\\w+\.Test\\w+\\d*)$");
             let suiteNameMatcher2 = new RegExp("(.*):");
             let ticketMatcher = new RegExp("https:\\/\\/issues.apache.org\\/jira\\/browse\\/(IGNITE-\\d+)");
 
-            visasTable.dataTable().fnDestroy();
+            table.dataTable().fnDestroy();
 
-            visasTable.DataTable({
+            table.DataTable({
                 data: result,
                 "iDisplayLength": 30, //rows to be shown by default
                 stateSave: true,
@@ -166,6 +166,13 @@
                                 + ticket[1] + "</a>";
                         }
                     },
+                    {
+                        title: "Status",
+                        width: 50,
+                        "data": function (data, type, row, meta) {
+                            return data.ticketStatus;
+                        }
+                    },
                     {
                         title: "Mute Date",
                         "data": function (data, type, row, meta) {
@@ -195,7 +202,7 @@
 <br>
 <div id="loadStatus"></div>
 <br>
-<table id="visasTable" class="row-border" style="width:100%">
+<table id="table" class="row-border" style="width:100%">
     <thead>
     <tr class="ui-widget-header ">
         <th>.</th>
@@ -204,6 +211,7 @@
         <th>.</th>
         <th>.</th>
         <th>.</th>
+        <th>.</th>
     </tr>
     </thead>
 </table>


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
users@infra.apache.org


With regards,
Apache Git Services