Chromium Code Reviews| Index: appengine/chromium_rietveld/static/script.js |
| diff --git a/appengine/chromium_rietveld/static/script.js b/appengine/chromium_rietveld/static/script.js |
| index eb4805fec1c2c9843aace1217a72e1fe96d25f00..0a7da1edd3b26ea4ee580762fdc3b0a05dad7a99 100644 |
| --- a/appengine/chromium_rietveld/static/script.js |
| +++ b/appengine/chromium_rietveld/static/script.js |
| @@ -12,6 +12,18 @@ |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| +// Global Rietveld namespace. |
| +var rietveld = { |
| + preferredDomainName: document.location.hostname, |
| + buildbucketHostname: "cr-buildbucket-test.appspot.com", |
|
jrobbins
2015/04/16 18:09:27
It looks like you hardcoded a test value here.
nodir
2015/04/16 18:15:11
it is overwritten in base.html
|
| + // Current rietveld user. |
| + user: null, |
| + gapi: { |
| + // Client ID to use. |
| + clientId: null, |
| + } |
| +}; |
| + |
| // Generic helpers |
| /** |
| @@ -120,7 +132,7 @@ function M_editPatchsetTitle(issue, patchset, xsrf_token, |
| var new_patchset_title = prompt( |
| 'Please enter the new title of Patch Set ' + patch_count, |
| original_patchset_title); |
| - if (new_patchset_title == null) { |
| + if (new_patchset_title == null) { |
| return false; |
| } else if (new_patchset_title == original_patchset_title) { |
| // Do not make an HTTP req if the new specified title is exactly the same. |
| @@ -627,7 +639,7 @@ function M_editFlags(issue) { |
| } |
| /** |
| - * Edit the list of pending try jobs for the given patchset. |
| + * Edit the list of pending try jobs for the given patchset. |
| * @param {String} patchset The patchset key. |
| */ |
| function M_editPendingTryJobs(patchset) { |
| @@ -656,38 +668,60 @@ function M_editPendingTryJobs(patchset) { |
| // Move popup as need to be on screen. |
| var popupRect = popupElement.getBoundingClientRect(); |
| if (popupRect.bottom > window.innerHeight) |
| - popupElement.style.top = (anchorRect.bottom - (popupRect.bottom - window.innerHeight) - scrollbarWidth) + 'px'; |
| + popupElement.style.top = (anchorRect.bottom - (popupRect.bottom - window.innerHeight) - scrollbarWidth) + 'px'; |
| if (popupRect.right > window.innerWidth) |
| - popupElement.style.left = (anchorRect.left - (popupRect.right - window.innerWidth) - scrollbarWidth) + 'px'; |
| + popupElement.style.left = (anchorRect.left - (popupRect.right - window.innerWidth) - scrollbarWidth) + 'px'; |
| } |
| /** |
| * Updates the pending builders for the patchset. |
| * @param {String} issue The issue key. |
| * @param {String} patchset The patchset key. |
| - * @param {String} xsrf_token Security token. |
| + * @param {String} project Issue project. |
| */ |
| -function M_updatePendingTrybots(issue, patchset, xsrf_token) { |
| +function M_updatePendingTrybots(issue, patchset, project) { |
| // Find which builder are checked. |
| - var builders = []; |
| + var builds = []; |
| var popup = jQuery('#trybot-popup'); |
| jQuery('input:checkbox', popup).each(function(i) { |
| var self = jQuery(this); |
| - if (self.attr('checked')) |
| - builders.push(self.attr('name')); |
| + if (!self.attr('checked')) { |
| + return; |
| + } |
| + var name = self.attr('name'); |
| + var nameParts = name.split(":", 2); |
| + builds.push({ |
| + name: name, |
| + master: nameParts[0], |
| + builder: nameParts[1] |
| + }); |
| }); |
| - |
| - // Build POST data for request. |
| - var data = []; |
| - data.push('xsrf_token=' + xsrf_token); |
| - data.push('last_patchset=' + patchset); |
| - data.push('builders=' + builders.join(',')); |
| - |
| - M_sendEditFlagsRequest(issue, data.join("&"), function(xhr) { |
| - if (xhr.status == 200) |
| - window.location.reload(); |
| + |
| + var buildPromises = []; |
| + var alerted403 = false; |
| + |
| + for (var i = 0; i < builds.length; i++) { |
| + var build = builds[i]; |
| + var buildPromise = M_scheduleBuild( |
|
esprehn
2015/04/14 01:00:11
Is this making one http call per builder?
|
| + build.master, build.builder, issue, patchset, project); |
| + buildPromise.then(null, function (reason) { |
| + console.log('Failed to schedule build ' + build.name + ":", reason); |
| + if (reason.status == 403 && !alerted403) { |
| + alerted403 = true; |
| + alert( |
| + "Permission denied. User " + rietveld.user.email + " does not " + |
| + "have access to build bucket for master " + build.master |
| + ); |
| + } |
| + return Promise.reject(reason); |
| + }); |
| + buildPromises.push(buildPromise); |
| + } |
| + // Reload after all builds are scheduled. |
| + Promise.all(buildPromises).then(function () { |
| + window.location.reload(); |
| }); |
| - |
| + |
| // Hide the popup. |
| jQuery('#trybot-popup').css('display', 'none'); |
| return true; |
| @@ -701,7 +735,7 @@ function M_closePendingTrybots() { |
| } |
| /** |
| - * Show or hide older try bot results. |
| + * Show or hide older try bot results. |
| * @param {String} id The id of the div elements that holds all the try job |
| * a elements. |
| * @param makeVisible If true, makes older try bots visible. |
| @@ -3664,3 +3698,99 @@ M_draftMessage.prototype.discard = function(cb) { |
| M_draftMessage.prototype.get_dialog_ = function() { |
| return document.getElementById(this.id_dlg_container); |
| } |
| + |
| +/** |
| + * Schedules a build on buildbucket. |
|
esprehn
2015/04/14 01:00:11
This makes N http requests now when scheduling a b
|
| + * @param {String} masterName Master name where the build will be scheduled, |
| + * without "master." prefix. |
| + * @param {String} builderName Builder name where the build will be scheduled. |
| + * @return A scheduled build as a promise. |
| + */ |
| +function M_scheduleBuild(masterName, builderName, issue, patchset, project) { |
| + console.log("Scheduling a build: ", masterName, builderName); |
| + return M_ensureBuildBucketLoaded().then(function () { |
| + // Buildset tag: see https://cr-buildbucket.appspot.com/#docs/conventions |
| + var buildset = ( |
| + "patch/rietveld/" + rietveld.preferredDomainName + |
| + "/" + issue + "/" + patchset |
| + ); |
| + var callPut = gapi.client.buildbucket.put({ |
|
nodir
2015/04/13 16:56:48
This call has to be compatible with existing recip
|
| + bucket: "master." + masterName, |
| + tags: [ |
| + "builder:" + builderName, |
| + "buildset:" + buildset, |
| + "master:" + masterName, |
| + "user_agent:rietveld", |
| + ], |
| + parameters_json: JSON.stringify({ |
| + builder_name: builderName, |
| + changes: [{ |
| + author: {email: rietveld.user.email } |
| + }], |
| + properties: { |
| + clobber: false, |
| + issue: issue, |
| + master: masterName, |
| + patch_project: project, |
| + patch_storage: "rietveld", |
| + patchset: patchset, |
| + revision: "HEAD", |
| + rietveld: "https://" + rietveld.preferredDomainName |
| + } |
| + }) |
| + }); |
| + |
| + return callPut.then(function (resp) { |
| + var build = resp.result.build; |
| + console.log("Scheduled build", build.id); |
| + return build; |
| + }); |
| + }); |
| +} |
| + |
| +/** |
| + * Loads buildbucket api. |
| + * @return A promise with no value. |
| + */ |
| +function M_ensureBuildBucketLoaded() { |
| + // remove this |
| + return M_ensureGapiAuthorized().then(function () { |
| + var root = "https://" + rietveld.buildbucketHostname + "/_ah/api"; |
| + return gapi.client.load("buildbucket", "v1", null, root); |
| + }); |
| +} |
| + |
| +/** |
| + * Acquires an access token to talk to Google APIs. |
| + * @return A promise with no value. |
| + */ |
| +function M_ensureGapiAuthorized() { |
| + // API authentication and Rietveld authentication are orthogonal |
| + // but we try to merge them so users don't notice the difference. |
| + // Rietveld authentication is considered primary: |
| + // * API authentication does not happen until Rietveld user is not logged in. |
| + // * When authenticating to Google APIs, we enforce API user to match |
| + // Rietveld user by email. |
| + if (!rietveld.user) { |
| + throw "This function may not be called if Rietveld user is not logged in"; |
| + } |
| + if (!rietveld.gapi.clientId) { |
| + var msg = "ClientID is not configured for this Rietveld instance"; |
| + alert(msg); |
| + throw msg; |
| + } |
| + |
| + var authOptions = { |
| + client_id: rietveld.gapi.clientId, |
| + scope: "email", |
| + immediate: true, |
| + // Enfoce API user to match Rietveld user. |
| + login_hint: rietveld.user.email |
| + }; |
| + return gapi.auth.authorize(authOptions).then(null, function () { |
| + console.log("non-immediate gapi.auth.authorize failed"); |
| + // If immediate (windowless) authorization fails, try non-immediate one. |
| + authOptions.immediate = false; |
| + return gapi.auth.authorize(authOptions); |
| + }); |
| +} |