OLD | NEW |
---|---|
1 // Copyright 2008 Google Inc. | 1 // Copyright 2008 Google Inc. |
2 // | 2 // |
3 // Licensed under the Apache License, Version 2.0 (the "License"); | 3 // Licensed under the Apache License, Version 2.0 (the "License"); |
4 // you may not use this file except in compliance with the License. | 4 // you may not use this file except in compliance with the License. |
5 // You may obtain a copy of the License at | 5 // You may obtain a copy of the License at |
6 // | 6 // |
7 // http://www.apache.org/licenses/LICENSE-2.0 | 7 // http://www.apache.org/licenses/LICENSE-2.0 |
8 // | 8 // |
9 // Unless required by applicable law or agreed to in writing, software | 9 // Unless required by applicable law or agreed to in writing, software |
10 // distributed under the License is distributed on an "AS IS" BASIS, | 10 // distributed under the License is distributed on an "AS IS" BASIS, |
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 // See the License for the specific language governing permissions and | 12 // See the License for the specific language governing permissions and |
13 // limitations under the License. | 13 // limitations under the License. |
14 | 14 |
15 // Global Rietveld namespace. | |
16 var rietveld = { | |
17 preferredDomainName: document.location.hostname, | |
18 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
| |
19 // Current rietveld user. | |
20 user: null, | |
21 gapi: { | |
22 // Client ID to use. | |
23 clientId: null, | |
24 } | |
25 }; | |
26 | |
15 // Generic helpers | 27 // Generic helpers |
16 | 28 |
17 /** | 29 /** |
18 * Create a new XMLHttpRequest in a cross-browser-compatible way. | 30 * Create a new XMLHttpRequest in a cross-browser-compatible way. |
19 * @return XMLHttpRequest object | 31 * @return XMLHttpRequest object |
20 */ | 32 */ |
21 function M_getXMLHttpRequest() { | 33 function M_getXMLHttpRequest() { |
22 try { | 34 try { |
23 return new XMLHttpRequest(); | 35 return new XMLHttpRequest(); |
24 } catch (e) { } | 36 } catch (e) { } |
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
113 } | 125 } |
114 return y; | 126 return y; |
115 } | 127 } |
116 | 128 |
117 function M_editPatchsetTitle(issue, patchset, xsrf_token, | 129 function M_editPatchsetTitle(issue, patchset, xsrf_token, |
118 original_patchset_title, patch_count) { | 130 original_patchset_title, patch_count) { |
119 | 131 |
120 var new_patchset_title = prompt( | 132 var new_patchset_title = prompt( |
121 'Please enter the new title of Patch Set ' + patch_count, | 133 'Please enter the new title of Patch Set ' + patch_count, |
122 original_patchset_title); | 134 original_patchset_title); |
123 if (new_patchset_title == null) { | 135 if (new_patchset_title == null) { |
124 return false; | 136 return false; |
125 } else if (new_patchset_title == original_patchset_title) { | 137 } else if (new_patchset_title == original_patchset_title) { |
126 // Do not make an HTTP req if the new specified title is exactly the same. | 138 // Do not make an HTTP req if the new specified title is exactly the same. |
127 return false; | 139 return false; |
128 } | 140 } |
129 | 141 |
130 //Build POST data for request. | 142 //Build POST data for request. |
131 var data = []; | 143 var data = []; |
132 data.push('xsrf_token=' + xsrf_token); | 144 data.push('xsrf_token=' + xsrf_token); |
133 data.push('patchset_title=' + new_patchset_title); | 145 data.push('patchset_title=' + new_patchset_title); |
(...skipping 486 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
620 } | 632 } |
621 | 633 |
622 M_sendEditFlagsRequest(issue, req.join("&"), function(xhr) { | 634 M_sendEditFlagsRequest(issue, req.join("&"), function(xhr) { |
623 if (xhr.status == 200) | 635 if (xhr.status == 200) |
624 window.location.reload(); | 636 window.location.reload(); |
625 }); | 637 }); |
626 return true; | 638 return true; |
627 } | 639 } |
628 | 640 |
629 /** | 641 /** |
630 * Edit the list of pending try jobs for the given patchset. | 642 * Edit the list of pending try jobs for the given patchset. |
631 * @param {String} patchset The patchset key. | 643 * @param {String} patchset The patchset key. |
632 */ | 644 */ |
633 function M_editPendingTryJobs(patchset) { | 645 function M_editPendingTryJobs(patchset) { |
634 var checkboxContainer = document.getElementById('trybot-popup-checkboxes'); | 646 var checkboxContainer = document.getElementById('trybot-popup-checkboxes'); |
635 if (! checkboxContainer) { | 647 if (! checkboxContainer) { |
636 checkboxContainer = document.getElementById( | 648 checkboxContainer = document.getElementById( |
637 'trybot-popup-checkboxes-with-categories') | 649 'trybot-popup-checkboxes-with-categories') |
638 } | 650 } |
639 var checkboxElements = checkboxContainer.getElementsByTagName('input'); | 651 var checkboxElements = checkboxContainer.getElementsByTagName('input'); |
640 for (var checkbox, i = 0; checkbox = checkboxElements[i]; i++) { | 652 for (var checkbox, i = 0; checkbox = checkboxElements[i]; i++) { |
641 checkbox.checked = false; | 653 checkbox.checked = false; |
642 checkbox.disabled = false; | 654 checkbox.disabled = false; |
643 } | 655 } |
644 | 656 |
645 // Position popup below anchor. | 657 // Position popup below anchor. |
646 var anchor = document.getElementById('tryjobchange-' + patchset); | 658 var anchor = document.getElementById('tryjobchange-' + patchset); |
647 var anchorRect = anchor.getBoundingClientRect(); | 659 var anchorRect = anchor.getBoundingClientRect(); |
648 var popupElement = document.getElementById('trybot-popup'); | 660 var popupElement = document.getElementById('trybot-popup'); |
649 popupElement.style.left = anchorRect.left + 'px'; | 661 popupElement.style.left = anchorRect.left + 'px'; |
650 popupElement.style.top = anchorRect.bottom + 'px'; | 662 popupElement.style.top = anchorRect.bottom + 'px'; |
651 popupElement.style.display = ''; | 663 popupElement.style.display = ''; |
652 | 664 |
653 // Extra padding to allow for scrollbars. | 665 // Extra padding to allow for scrollbars. |
654 var scrollbarWidth = 20; | 666 var scrollbarWidth = 20; |
655 | 667 |
656 // Move popup as need to be on screen. | 668 // Move popup as need to be on screen. |
657 var popupRect = popupElement.getBoundingClientRect(); | 669 var popupRect = popupElement.getBoundingClientRect(); |
658 if (popupRect.bottom > window.innerHeight) | 670 if (popupRect.bottom > window.innerHeight) |
659 popupElement.style.top = (anchorRect.bottom - (popupRect.bottom - window.inn erHeight) - scrollbarWidth) + 'px'; | 671 popupElement.style.top = (anchorRect.bottom - (popupRect.bottom - window.inn erHeight) - scrollbarWidth) + 'px'; |
660 if (popupRect.right > window.innerWidth) | 672 if (popupRect.right > window.innerWidth) |
661 popupElement.style.left = (anchorRect.left - (popupRect.right - window.inner Width) - scrollbarWidth) + 'px'; | 673 popupElement.style.left = (anchorRect.left - (popupRect.right - window.inner Width) - scrollbarWidth) + 'px'; |
662 } | 674 } |
663 | 675 |
664 /** | 676 /** |
665 * Updates the pending builders for the patchset. | 677 * Updates the pending builders for the patchset. |
666 * @param {String} issue The issue key. | 678 * @param {String} issue The issue key. |
667 * @param {String} patchset The patchset key. | 679 * @param {String} patchset The patchset key. |
668 * @param {String} xsrf_token Security token. | 680 * @param {String} project Issue project. |
669 */ | 681 */ |
670 function M_updatePendingTrybots(issue, patchset, xsrf_token) { | 682 function M_updatePendingTrybots(issue, patchset, project) { |
671 // Find which builder are checked. | 683 // Find which builder are checked. |
672 var builders = []; | 684 var builds = []; |
673 var popup = jQuery('#trybot-popup'); | 685 var popup = jQuery('#trybot-popup'); |
674 jQuery('input:checkbox', popup).each(function(i) { | 686 jQuery('input:checkbox', popup).each(function(i) { |
675 var self = jQuery(this); | 687 var self = jQuery(this); |
676 if (self.attr('checked')) | 688 if (!self.attr('checked')) { |
677 builders.push(self.attr('name')); | 689 return; |
690 } | |
691 var name = self.attr('name'); | |
692 var nameParts = name.split(":", 2); | |
693 builds.push({ | |
694 name: name, | |
695 master: nameParts[0], | |
696 builder: nameParts[1] | |
697 }); | |
678 }); | 698 }); |
679 | 699 |
680 // Build POST data for request. | 700 var buildPromises = []; |
681 var data = []; | 701 var alerted403 = false; |
682 data.push('xsrf_token=' + xsrf_token); | 702 |
683 data.push('last_patchset=' + patchset); | 703 for (var i = 0; i < builds.length; i++) { |
684 data.push('builders=' + builders.join(',')); | 704 var build = builds[i]; |
685 | 705 var buildPromise = M_scheduleBuild( |
esprehn
2015/04/14 01:00:11
Is this making one http call per builder?
| |
686 M_sendEditFlagsRequest(issue, data.join("&"), function(xhr) { | 706 build.master, build.builder, issue, patchset, project); |
687 if (xhr.status == 200) | 707 buildPromise.then(null, function (reason) { |
688 window.location.reload(); | 708 console.log('Failed to schedule build ' + build.name + ":", reason); |
709 if (reason.status == 403 && !alerted403) { | |
710 alerted403 = true; | |
711 alert( | |
712 "Permission denied. User " + rietveld.user.email + " does not " + | |
713 "have access to build bucket for master " + build.master | |
714 ); | |
715 } | |
716 return Promise.reject(reason); | |
717 }); | |
718 buildPromises.push(buildPromise); | |
719 } | |
720 // Reload after all builds are scheduled. | |
721 Promise.all(buildPromises).then(function () { | |
722 window.location.reload(); | |
689 }); | 723 }); |
690 | 724 |
691 // Hide the popup. | 725 // Hide the popup. |
692 jQuery('#trybot-popup').css('display', 'none'); | 726 jQuery('#trybot-popup').css('display', 'none'); |
693 return true; | 727 return true; |
694 } | 728 } |
695 | 729 |
696 /** | 730 /** |
697 * Hide the pending builders popup. | 731 * Hide the pending builders popup. |
698 */ | 732 */ |
699 function M_closePendingTrybots() { | 733 function M_closePendingTrybots() { |
700 jQuery('#trybot-popup').css('display', 'none'); | 734 jQuery('#trybot-popup').css('display', 'none'); |
701 } | 735 } |
702 | 736 |
703 /** | 737 /** |
704 * Show or hide older try bot results. | 738 * Show or hide older try bot results. |
705 * @param {String} id The id of the div elements that holds all the try job | 739 * @param {String} id The id of the div elements that holds all the try job |
706 * a elements. | 740 * a elements. |
707 * @param makeVisible If true, makes older try bots visible. | 741 * @param makeVisible If true, makes older try bots visible. |
708 */ | 742 */ |
709 function M_showTryJobResult(id, makeVisible) { | 743 function M_showTryJobResult(id, makeVisible) { |
710 // This set keeps track of the first occurance of each try job result for | 744 // This set keeps track of the first occurance of each try job result for |
711 // a given builder. The try job results are ordered reverse chronologically, | 745 // a given builder. The try job results are ordered reverse chronologically, |
712 // so we visit them from newest to oldest. | 746 // so we visit them from newest to oldest. |
713 var firstBuilderSet = {}; | 747 var firstBuilderSet = {}; |
714 var oldBuildersExist = false; | 748 var oldBuildersExist = false; |
(...skipping 2942 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
3657 httpreq.open("DELETE", base_url + this.issue_id + "/draft_message", true); | 3691 httpreq.open("DELETE", base_url + this.issue_id + "/draft_message", true); |
3658 httpreq.send(""); | 3692 httpreq.send(""); |
3659 } | 3693 } |
3660 | 3694 |
3661 /** | 3695 /** |
3662 * Helper function that returns the dialog's HTML container. | 3696 * Helper function that returns the dialog's HTML container. |
3663 */ | 3697 */ |
3664 M_draftMessage.prototype.get_dialog_ = function() { | 3698 M_draftMessage.prototype.get_dialog_ = function() { |
3665 return document.getElementById(this.id_dlg_container); | 3699 return document.getElementById(this.id_dlg_container); |
3666 } | 3700 } |
3701 | |
3702 /** | |
3703 * Schedules a build on buildbucket. | |
esprehn
2015/04/14 01:00:11
This makes N http requests now when scheduling a b
| |
3704 * @param {String} masterName Master name where the build will be scheduled, | |
3705 * without "master." prefix. | |
3706 * @param {String} builderName Builder name where the build will be scheduled. | |
3707 * @return A scheduled build as a promise. | |
3708 */ | |
3709 function M_scheduleBuild(masterName, builderName, issue, patchset, project) { | |
3710 console.log("Scheduling a build: ", masterName, builderName); | |
3711 return M_ensureBuildBucketLoaded().then(function () { | |
3712 // Buildset tag: see https://cr-buildbucket.appspot.com/#docs/conventions | |
3713 var buildset = ( | |
3714 "patch/rietveld/" + rietveld.preferredDomainName + | |
3715 "/" + issue + "/" + patchset | |
3716 ); | |
3717 var callPut = gapi.client.buildbucket.put({ | |
nodir
2015/04/13 16:56:48
This call has to be compatible with existing recip
| |
3718 bucket: "master." + masterName, | |
3719 tags: [ | |
3720 "builder:" + builderName, | |
3721 "buildset:" + buildset, | |
3722 "master:" + masterName, | |
3723 "user_agent:rietveld", | |
3724 ], | |
3725 parameters_json: JSON.stringify({ | |
3726 builder_name: builderName, | |
3727 changes: [{ | |
3728 author: {email: rietveld.user.email } | |
3729 }], | |
3730 properties: { | |
3731 clobber: false, | |
3732 issue: issue, | |
3733 master: masterName, | |
3734 patch_project: project, | |
3735 patch_storage: "rietveld", | |
3736 patchset: patchset, | |
3737 revision: "HEAD", | |
3738 rietveld: "https://" + rietveld.preferredDomainName | |
3739 } | |
3740 }) | |
3741 }); | |
3742 | |
3743 return callPut.then(function (resp) { | |
3744 var build = resp.result.build; | |
3745 console.log("Scheduled build", build.id); | |
3746 return build; | |
3747 }); | |
3748 }); | |
3749 } | |
3750 | |
3751 /** | |
3752 * Loads buildbucket api. | |
3753 * @return A promise with no value. | |
3754 */ | |
3755 function M_ensureBuildBucketLoaded() { | |
3756 // remove this | |
3757 return M_ensureGapiAuthorized().then(function () { | |
3758 var root = "https://" + rietveld.buildbucketHostname + "/_ah/api"; | |
3759 return gapi.client.load("buildbucket", "v1", null, root); | |
3760 }); | |
3761 } | |
3762 | |
3763 /** | |
3764 * Acquires an access token to talk to Google APIs. | |
3765 * @return A promise with no value. | |
3766 */ | |
3767 function M_ensureGapiAuthorized() { | |
3768 // API authentication and Rietveld authentication are orthogonal | |
3769 // but we try to merge them so users don't notice the difference. | |
3770 // Rietveld authentication is considered primary: | |
3771 // * API authentication does not happen until Rietveld user is not logged in. | |
3772 // * When authenticating to Google APIs, we enforce API user to match | |
3773 // Rietveld user by email. | |
3774 if (!rietveld.user) { | |
3775 throw "This function may not be called if Rietveld user is not logged in"; | |
3776 } | |
3777 if (!rietveld.gapi.clientId) { | |
3778 var msg = "ClientID is not configured for this Rietveld instance"; | |
3779 alert(msg); | |
3780 throw msg; | |
3781 } | |
3782 | |
3783 var authOptions = { | |
3784 client_id: rietveld.gapi.clientId, | |
3785 scope: "email", | |
3786 immediate: true, | |
3787 // Enfoce API user to match Rietveld user. | |
3788 login_hint: rietveld.user.email | |
3789 }; | |
3790 return gapi.auth.authorize(authOptions).then(null, function () { | |
3791 console.log("non-immediate gapi.auth.authorize failed"); | |
3792 // If immediate (windowless) authorization fails, try non-immediate one. | |
3793 authOptions.immediate = false; | |
3794 return gapi.auth.authorize(authOptions); | |
3795 }); | |
3796 } | |
OLD | NEW |