asfgit closed pull request #24: IGNITE-9697 Autocomplete branch for TC field URL: https://github.com/apache/ignite-teamcity-bot/pull/24
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/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 @@ /** 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 @@ default SingleBuildRunCtx loadTestsAndProblems(@Nonnull Build build, @Deprecated */ 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 @@ -1168,6 +1168,26 @@ public void setExecutor(ExecutorService executor) { return teamcity.sendJiraComment(ticket, comment); } + /** {@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 @@ /** 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 void init(@Nullable String tcName) { } 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 void init(@Nullable String tcName) { /** {@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 + "\"}"); @@ -187,9 +201,32 @@ public void init(@Nullable String tcName) { } } + /** {@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 void init(@Nullable String tcName) { } } - //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 @@ 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.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 SimpleResult triggerBuilds( 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 @@ success: function(result) { $("#loadStatus").html(""); showSuitesForPrCheckData(result); + tryToFillAutocompleteLists(); }, error: showErrInLoadStatus }); @@ -51,6 +52,7 @@ url: "rest/branches/getServerIds", success: function(result) { $("#loadStatus").html(""); + setupAutocompleteList(result); showBuildsOnServers(result); }, error: showErrInLoadStatus @@ -78,24 +80,26 @@ 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 @@ 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 @@ 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 @@ 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); } ---------------------------------------------------------------- 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: us...@infra.apache.org With regards, Apache Git Services