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/03 16:44:57 UTC
[ignite-teamcity-bot] 01/02: IGNITE-9697 Autocomplete branch for TC
field - Fixes #24.
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
commit ce7aef2b1d0ac5cf6c2e28dc6d59d9a53023846f
Author: Dmitrii Ryabov <So...@gmail.com>
AuthorDate: Wed Oct 3 19:32:34 2018 +0300
IGNITE-9697 Autocomplete branch for TC field - Fixes #24.
Signed-off-by: Dmitriy Pavlov <dp...@apache.org>
---
.../java/org/apache/ignite/ci/HelperConfig.java | 6 +
.../main/java/org/apache/ignite/ci/ITeamcity.java | 19 +++
.../apache/ignite/ci/IgnitePersistentTeamcity.java | 20 +++
.../apache/ignite/ci/IgniteTeamcityConnection.java | 42 +++++-
.../ignite/ci/conf/ServerIntegrationLinks.java | 43 ++++++
.../apache/ignite/ci/web/rest/TriggerBuild.java | 20 +++
ignite-tc-helper-web/src/main/webapp/index.html | 12 +-
.../src/main/webapp/js/common-1.6.js | 156 +++++++++++++++++++++
ignite-tc-helper-web/src/main/webapp/services.html | 13 +-
9 files changed, 321 insertions(+), 10 deletions(-)
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/HelperConfig.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/HelperConfig.java
index 67be102..c39fd1e 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/HelperConfig.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/HelperConfig.java
@@ -54,6 +54,12 @@ public class HelperConfig {
/** JIRA authorization token property name. */
public static final String JIRA_AUTH_TOKEN = "jira.auth_token";
+ /** JIRA authorization token property name. */
+ public static final String GIT_API_URL = "git.api_url";
+
+ /** JIRA authorization token property name. */
+ public static final String JIRA_API_URL = "jira.api_url";
+
/** Slack authorization token property name. */
public static final String SLACK_AUTH_TOKEN = "slack.auth_token";
public static final String SLACK_CHANNEL = "slack.channel";
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 3193ea8..8f58657 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
@@ -319,6 +319,25 @@ public interface ITeamcity {
*/
boolean sendJiraComment(String ticket, String comment);
+ /**
+ * @param url URL for git integration.
+ */
+ void setGitApiUrl(String url);
+
+ /**
+ * @return URL for git integration.
+ */
+ String getGitApiUrl();
+
+ /**
+ * @param url URL for JIRA integration.
+ */
+ void setJiraApiUrl(String url);
+
+ /**
+ * @return URL for JIRA integration.
+ */
+ String getJiraApiUrl();
default void setAuthData(String user, String password) {
setAuthToken(
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 ec53d9b..2e9f873 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
@@ -1169,6 +1169,26 @@ public class IgnitePersistentTeamcity implements IAnalyticsEnabledTeamcity, ITea
}
/** {@inheritDoc} */
+ @Override public void setGitApiUrl(String url) {
+ teamcity.setGitApiUrl(url);
+ }
+
+ /** {@inheritDoc} */
+ @Override public String getGitApiUrl() {
+ return teamcity.getGitApiUrl();
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setJiraApiUrl(String url) {
+ teamcity.setJiraApiUrl(url);
+ }
+
+ /** {@inheritDoc} */
+ @Override public String getJiraApiUrl() {
+ return teamcity.getJiraApiUrl();
+ }
+
+ /** {@inheritDoc} */
@Override public PullRequest getPullRequest(String branchForTc) {
return teamcity.getPullRequest(branchForTc);
}
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 6b63f1b..3d68ccb 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
@@ -104,6 +104,12 @@ public class IgniteTeamcityConnection implements ITeamcity {
/** JIRA authorization token. */
private String jiraBasicAuthTok;
+ /** URL for git integration. */
+ private String gitApiUrl;
+
+ /** URL for JIRA integration. */
+ private String jiraApiUrl;
+
private String configName; //main properties file name
private String tcName;
@@ -130,8 +136,10 @@ public class IgniteTeamcityConnection implements ITeamcity {
}
setGitToken(HelperConfig.prepareGithubHttpAuthToken(props));
+ setGitApiUrl(props.getProperty(HelperConfig.GIT_API_URL));
setJiraToken(HelperConfig.prepareJiraHttpAuthToken(props));
+ setJiraApiUrl(props.getProperty(HelperConfig.JIRA_API_URL));
final File logsDirFile = HelperConfig.resolveLogs(workDir, props);
@@ -173,8 +181,14 @@ public class IgniteTeamcityConnection implements ITeamcity {
/** {@inheritDoc} */
@AutoProfiling
@Override public boolean sendJiraComment(String ticket, String comment) {
+ if (isNullOrEmpty(jiraApiUrl)) {
+ logger.error("Failed to notify JIRA [errMsg=JIRA API URL is not configured for this server.]");
+
+ return false;
+ }
+
try {
- String url = "https://issues.apache.org/jira/rest/api/2/issue/" + ticket + "/comment";
+ String url = jiraApiUrl + "issue/" + ticket + "/comment";
HttpUtil.sendPostAsStringToJira(jiraBasicAuthTok, url, "{\"body\": \"" + comment + "\"}");
@@ -188,8 +202,31 @@ public class IgniteTeamcityConnection implements ITeamcity {
}
/** {@inheritDoc} */
+ @Override public void setGitApiUrl(String url) {
+ gitApiUrl = url;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String getGitApiUrl() {
+ return gitApiUrl;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setJiraApiUrl(String url) {
+ jiraApiUrl = url;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String getJiraApiUrl() {
+ return jiraApiUrl;
+ }
+
+ /** {@inheritDoc} */
@AutoProfiling
@Override public PullRequest getPullRequest(String branchForTc) {
+ if (!isNullOrEmpty(gitApiUrl))
+ throw new IllegalStateException("Git API URL is not configured for this server.");
+
String id = null;
// Get PR id from string "pull/XXXX/head"
@@ -203,8 +240,7 @@ public class IgniteTeamcityConnection implements ITeamcity {
}
}
- //todo github address can be probably associated with server
- String pr = "https://api.github.com/repos/apache/ignite/pulls/" + id;
+ String pr = gitApiUrl + "pulls/" + id;
try (InputStream is = HttpUtil.sendGetToGit(gitAuthTok, pr)) {
InputStreamReader reader = new InputStreamReader(is);
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/conf/ServerIntegrationLinks.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/conf/ServerIntegrationLinks.java
new file mode 100644
index 0000000..a0f0eb9
--- /dev/null
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/conf/ServerIntegrationLinks.java
@@ -0,0 +1,43 @@
+/*
+ * 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.conf;
+
+/**
+ *
+ */
+public class ServerIntegrationLinks {
+ /** Server ID. */
+ public final String srvId;
+
+ /** URL for git integration. */
+ public final String gitApiUrl;
+
+ /** URL for JIRA integration. */
+ public final String jiraApiUrl;
+
+ /**
+ * @param srvId Server ID.
+ * @param gitApiUrl URL for git integration.
+ * @param jiraApiUrl URL for JIRA integration.
+ */
+ public ServerIntegrationLinks(String srvId, String gitApiUrl, String jiraApiUrl) {
+ this.srvId = srvId;
+ this.gitApiUrl = gitApiUrl;
+ this.jiraApiUrl = jiraApiUrl;
+ }
+}
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/rest/TriggerBuild.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/rest/TriggerBuild.java
index c5da089..6a0ce3a 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/rest/TriggerBuild.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/rest/TriggerBuild.java
@@ -20,6 +20,9 @@ package org.apache.ignite.ci.web.rest;
import com.google.common.base.Strings;
import java.util.Arrays;
import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.GET;
@@ -30,6 +33,7 @@ import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import org.apache.ignite.ci.ITcHelper;
import org.apache.ignite.ci.ITeamcity;
+import org.apache.ignite.ci.conf.ServerIntegrationLinks;
import org.apache.ignite.ci.github.PullRequest;
import org.apache.ignite.ci.observer.BuildObserver;
import org.apache.ignite.ci.tcmodel.result.Build;
@@ -222,4 +226,20 @@ public class TriggerBuild {
return new SimpleResult("OK");
}
+ @GET
+ @Path("integrationUrls")
+ public Set<ServerIntegrationLinks> getIntegrationUrls(@NotNull @QueryParam("serverIds") String srvIds) {
+ final ICredentialsProv prov = ICredentialsProv.get(req);
+
+ String[] srvIds0 = srvIds.split(",");
+
+ return Arrays.stream(srvIds0).map(srvId -> {
+ if (!prov.hasAccess(srvId))
+ return null;
+
+ ITeamcity teamcity = CtxListener.server(srvId, context, req);
+
+ return new ServerIntegrationLinks(srvId, teamcity.getGitApiUrl(), teamcity.getGitApiUrl());
+ }).filter(Objects::nonNull).collect(Collectors.toSet());
+ }
}
diff --git a/ignite-tc-helper-web/src/main/webapp/index.html b/ignite-tc-helper-web/src/main/webapp/index.html
index 1dc6eba..2e0dd83 100644
--- a/ignite-tc-helper-web/src/main/webapp/index.html
+++ b/ignite-tc-helper-web/src/main/webapp/index.html
@@ -43,6 +43,7 @@ function loadData() {
success: function(result) {
$("#loadStatus").html("");
showSuitesForPrCheckData(result);
+ tryToFillAutocompleteLists();
},
error: showErrInLoadStatus
});
@@ -51,6 +52,7 @@ function loadData() {
url: "rest/branches/getServerIds",
success: function(result) {
$("#loadStatus").html("");
+ setupAutocompleteList(result);
showBuildsOnServers(result);
},
error: showErrInLoadStatus
@@ -78,24 +80,26 @@ function showCurrentBranchesData(result) {
function showSuitesForPrCheckData(result) {
var res = "";
+
for (var i = 0; i < result.length; i++) {
var chainAtServer = result[i];
- //res+="<a href='pr.html?serverId=private&branchForTc=ignite-gg-12790-1&suiteId=id8xIgniteGridGainTests_RunAll="+id+"'>Check PR</a><br>";
res += "<form action='pr.html'>";
res += "Server: <input type='text' name='serverId' value=" + chainAtServer.serverId + " readonly>";
res += "Chain: <input type='text' name='suiteId' value=" + chainAtServer.suiteId + ">";
- res += "Base branch: <input type='text' name='baseBranchForTc' title='Etalon branch,e.g refs/heads/master'> ";
- res += "<b>Branch:</b> <input type='text' name='branchForTc' title='Tested branch, e.g. pull/4790/head or ignite-9349' required> ";
+ res += "Base branch: <input class='branchForTc" + chainAtServer.serverId +
+ "' type='text' name='baseBranchForTc' title='Etalon branch, e.g refs/heads/master'> ";
+ res += "<b>Branch:</b> <input class='branchForTc" + chainAtServer.serverId +
+ "' type='text' name='branchForTc' title='Tested branch, e.g. pull/4790/head or ignite-9349' required> ";
res += "<input type='submit' name='action' value='Latest' title='Show latest runs'>";
// res+="<input type='submit' name='action' value='Chain'>";
res += "<input type='submit' name='action' value='History' title='Show last 10 runs merged'>";
res += "</form>";
}
+
$("#suitesForPrCheck").html(res);
}
-
function showBuildsOnServers(result) {
var res = "";
for (var i = 0; i < result.length; i++) {
diff --git a/ignite-tc-helper-web/src/main/webapp/js/common-1.6.js b/ignite-tc-helper-web/src/main/webapp/js/common-1.6.js
index 08cffdf..8f15680 100644
--- a/ignite-tc-helper-web/src/main/webapp/js/common-1.6.js
+++ b/ignite-tc-helper-web/src/main/webapp/js/common-1.6.js
@@ -210,3 +210,159 @@ function tcHelperLogout() {
} catch (e) {
}
}
+
+/**
+ * Change autocomplete filter to show results only when they starts from written text.
+ */
+$.ui.autocomplete.filter = function (array, term) {
+ var matcher = new RegExp("^" + $.ui.autocomplete.escapeRegex(term), "i");
+
+ return $.grep(array, function (value) {
+ return matcher.test(value.label || value.value || value);
+ });
+};
+
+var callbackRegistry = {};
+
+/**
+ * Send request to another site.
+ *
+ * @param url URL.
+ * @param onSuccess Function for success response.
+ * @param onError Function for fail response.
+ */
+function scriptRequest(url, onSuccess, onError) {
+ var scriptOk = false;
+ var callbackName = 'cb' + String(Math.random()).slice(-6);
+
+ url += ~url.indexOf('?') ? '&' : '?';
+ url += 'callback=callbackRegistry.' + callbackName;
+
+ callbackRegistry[callbackName] = function(data) {
+ scriptOk = true;
+
+ delete callbackRegistry[callbackName];
+
+ onSuccess(data);
+ };
+
+ function checkCallback() {
+ if (scriptOk)
+ return;
+
+ delete callbackRegistry[callbackName];
+
+ console.error("Request to \"" + url + "\" was failed.")
+ }
+
+ var script = document.createElement('script');
+
+ script.onload = script.onerror = checkCallback;
+ script.src = url;
+
+ document.body.appendChild(script);
+}
+
+/** Key-value map. Key - server id. Value - url to git api. */
+var gitUrls = new Map();
+
+/** Branches for TeamCity. */
+var branchesForTc = {};
+
+/**
+ * Fill git URLs and send requests to them.
+ *
+ * @param srvIds - Set of server IDs.
+ */
+function setupAutocompleteList(srvIds) {
+ for (let srvId of srvIds)
+ gitUrls.set(srvId, "");
+
+ startFillAutocompleteListsProcess();
+}
+
+/**
+ * Retrieves recent PR numbers and fills autocomplete lists for the fields branchForTc.
+ */
+function startFillAutocompleteListsProcess() {
+ _receiveIntegrationUrls();
+}
+
+/**
+ * Receive api links for the servers.
+ *
+ * @private
+ */
+function _receiveIntegrationUrls() {
+ var url = "rest/build/integrationUrls?serverIds=";
+
+ for (let key of gitUrls.keys())
+ url += key + ",";
+
+ $.ajax({
+ url: url,
+ success: function (result) {
+ _fillGitUrls(result);
+ _sendRequestsToFillAutocompleteLists();
+ }
+ });
+}
+
+/**
+ * Fill git api URLs.
+ *
+ * @param result Array of ServerIntegrationLinks.
+ *
+ * @private
+ */
+function _fillGitUrls(result) {
+ for (let links of result)
+ gitUrls.set(links.srvId, links.gitApiUrl);
+}
+
+/**
+ * Send requests to the git to get pull requests for the branch autocomplete lists.
+ *
+ * @private
+ */
+function _sendRequestsToFillAutocompleteLists() {
+ for (var entry of gitUrls.entries())
+ scriptRequest(entry[1] + "pulls?sort=updated&direction=desc", _fillBranchAutocompleteList);
+}
+
+/**
+ * Takes all "branchForTc<server>" and add autocomplete list to them.
+ *
+ * @param result Response from git.
+ */
+function _fillBranchAutocompleteList(result) {
+ if (!result.data || !result.data[0])
+ return;
+
+ for (var entry of gitUrls.entries()) {
+ if (!result.data[0].url.startsWith(entry[1]))
+ continue;
+
+ branchesForTc[entry[0]] = [{label:"master", value:"refs/heads/master"}];
+
+ for (let pr of result.data)
+ branchesForTc[entry[0]].push({label: pr.number, value: "pull/" + pr.number + "/head"});
+
+ $(".branchForTc" + entry[0]).autocomplete({source: branchesForTc[entry[0]]});
+ }
+}
+
+/**
+ * Fills autocomplete lists for the branchForTc fields, if lists are available.
+ */
+function tryToFillAutocompleteLists() {
+ for (var entry of gitUrls.entries()) {
+ var fields = $(".branchForTc" + entry[0]);
+
+ for (let field of fields) {
+ if (branchesForTc[entry[0]] && branchesForTc[entry[0]].length > 1 &&
+ field.autocomplete("option", "source").length < 2)
+ field.autocomplete({source: branchesForTc[entry[0]]});
+ }
+ }
+}
diff --git a/ignite-tc-helper-web/src/main/webapp/services.html b/ignite-tc-helper-web/src/main/webapp/services.html
index 17746f0..eb33737 100644
--- a/ignite-tc-helper-web/src/main/webapp/services.html
+++ b/ignite-tc-helper-web/src/main/webapp/services.html
@@ -37,10 +37,16 @@ function loadData() {
success: function(result) {
$("#loadStatus").html("");
showSuitesForTeamCityRunData(result);
+ tryToFillAutocompleteLists();
showCommentJiraForm(result);
},
error: showErrInLoadStatus
});
+
+ $.ajax({
+ url: "rest/branches/getServerIds",
+ success: setupAutocompleteList
+ });
}
function showSuitesForTeamCityRunData(result) {
@@ -55,7 +61,8 @@ function showSuitesForTeamCityRunData(result) {
res += "Server: <input type='text' name='serverId' value='" + chainAtServer.serverId + "' readonly>";
res += "Chain: <input type='text' name='suiteId' value='" + chainAtServer.suiteId + "' readonly>";
- res += "Branch: <input type='text' name='branchForTc' required> ";
+ res += "Branch: <input type='text' name='branchForTc' class='branchForTc" + chainAtServer.serverId +
+ "' required> ";
res += "Ticket: <input type='text' name='ticketId'>";
res += "<button name='jira' type='button' onclick='trigBuild(\"tests\")'>Start tests</button>";
res += "<button name='jira' onclick='trigBuild(\"tests+jira\")'>Start tests and comment JIRA ticket on ready</button>";
@@ -85,12 +92,12 @@ function showCommentJiraForm(result) {
res += "Server: <input type='text' name='serverId' value=" + chainAtServer.serverId +" readonly>" ;
res += "Chain: <input type='text' name='suiteId' value='" + chainAtServer.suiteId + "' readonly>";
- res += "Branch: <input type='text' name='branchForTc' required> ";
+ res += "Branch: <input type='text' name='branchForTc' class='branchForTc" + chainAtServer.serverId +
+ "' required> ";
res += "Ticket: <input type='text' name='ticketId'> ";
res += "<button name='action' onclick='notifyJira()'>Notify</button>";
}
- //todo enabled this once feature is ready
$("#notifyJira").html(res);
}