Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(168)

Side by Side Diff: appengine/swarming/elements/build/elements.html

Issue 2302973002: Refactor post requests, implement bot cancel/terminate (Closed) Base URL: https://chromium.googlesource.com/external/github.com/luci/luci-py@basic-layout
Patch Set: rebase Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 <!DOCTYPE html><html><head><!-- 1 <!DOCTYPE html><html><head><!--
2 @license 2 @license
3 Copyright (c) 2016 The Polymer Project Authors. All rights reserved. 3 Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
4 This code may only be used under the BSD style license found at http://polymer.g ithub.io/LICENSE.txt 4 This code may only be used under the BSD style license found at http://polymer.g ithub.io/LICENSE.txt
5 The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt 5 The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
6 The complete set of contributors may be found at http://polymer.github.io/CONTRI BUTORS.txt 6 The complete set of contributors may be found at http://polymer.github.io/CONTRI BUTORS.txt
7 Code distributed by Google as part of the polymer project is also 7 Code distributed by Google as part of the polymer project is also
8 subject to an additional IP rights grant found at http://polymer.github.io/PATEN TS.txt 8 subject to an additional IP rights grant found at http://polymer.github.io/PATEN TS.txt
9 --><!-- 9 --><!--
10 @license 10 @license
(...skipping 8128 matching lines...) Expand 10 before | Expand all | Expand 10 after
8139 }, 8139 },
8140 8140
8141 _or: function() { 8141 _or: function() {
8142 var result = false; 8142 var result = false;
8143 // can't use .foreach, as arguments isn't really an Array. 8143 // can't use .foreach, as arguments isn't really an Array.
8144 for (var i = 0; i < arguments.length; i++) { 8144 for (var i = 0; i < arguments.length; i++) {
8145 result = result || arguments[i]; 8145 result = result || arguments[i];
8146 } 8146 }
8147 return result; 8147 return result;
8148 }, 8148 },
8149
8150 // _postWithToast makes a post request and updates the provided paper-toas t
8151 // element with the response, regardless of failure.
8152 _postWithToast: function(url, toast, auth_headers) {
8153 // Keep toast displayed until we hear back from the request.
8154 toast.duration = 0;
8155 toast.open();
8156 sk.request("POST", url, undefined, auth_headers).then(function(response) {
8157 toast.close();
8158 toast.show({
8159 text: "Request sent. Response: "+response,
8160 duration: 3000,
8161 });
8162 }.bind(this)).catch(function(reason) {
8163 console.log("Request failed", reason);
8164 toast.close();
8165 toast.show({
8166 text: "Request failed. Reason: "+reason,
8167 duration: 3000,
8168 });
8169 }.bind(this));
8170 }
8149 }; 8171 };
8150 })(); 8172 })();
8151 </script> 8173 </script>
8152 <script> 8174 <script>
8153 Polymer.AppLayout = Polymer.AppLayout || {}; 8175 Polymer.AppLayout = Polymer.AppLayout || {};
8154 8176
8155 Polymer.AppLayout._scrollEffects = Polymer.AppLayout._scrollEffects || {}; 8177 Polymer.AppLayout._scrollEffects = Polymer.AppLayout._scrollEffects || {};
8156 8178
8157 Polymer.AppLayout.scrollTimingFunction = function easeOutQuad(t, b, c, d) { 8179 Polymer.AppLayout.scrollTimingFunction = function easeOutQuad(t, b, c, d) {
8158 t /= d; 8180 t /= d;
(...skipping 16436 matching lines...) Expand 10 before | Expand all | Expand 10 after
24595 }, 24617 },
24596 24618
24597 _cancelTask: function(e) { 24619 _cancelTask: function(e) {
24598 var task = e.model.task; 24620 var task = e.model.task;
24599 if (!task || !task.task_id) { 24621 if (!task || !task.task_id) {
24600 console.log("Missing task info", task); 24622 console.log("Missing task info", task);
24601 return 24623 return
24602 } 24624 }
24603 var id = task.task_id 24625 var id = task.task_id
24604 24626
24605 // Keep toast displayed until we hear back from the cancel.
24606 this.$.toast.duration = 0;
24607 this.$.toast.text="Canceling task " + id; 24627 this.$.toast.text="Canceling task " + id;
24608 this.$.toast.open();
24609 var url = "/_ah/api/swarming/v1/task/" + id +"/cancel"; 24628 var url = "/_ah/api/swarming/v1/task/" + id +"/cancel";
24610 sk.request("POST", url, undefined, this._auth_headers).then(function(res ponse) { 24629 this._postWithToast(url, this.$.toast, this._auth_headers);
24611 this.$.toast.close();
24612 this.$.toast.show({
24613 text: "Request sent. Response: "+response,
24614 duration: 3000,
24615 });
24616 }.bind(this)).catch(function(reason) {
24617 console.log("Cancellation failed", reason);
24618 this.$.toast.close();
24619 this.$.toast.show({
24620 text: "Request failed. Reason: "+reason,
24621 duration: 3000,
24622 });
24623 }.bind(this));
24624 }, 24630 },
24625 24631
24626 _tag: function(task, col) { 24632 _tag: function(task, col) {
24627 if (!task || !task.tagMap) { 24633 if (!task || !task.tagMap) {
24628 return undefined; 24634 return undefined;
24629 } 24635 }
24630 return task.tagMap[col]; 24636 return task.tagMap[col];
24631 }, 24637 },
24632 24638
24633 _taskLink: function(taskId) { 24639 _taskLink: function(taskId) {
(...skipping 255 matching lines...) Expand 10 before | Expand all | Expand 10 after
24889 _calcSize: function() { 24895 _calcSize: function() {
24890 return this.getBoundingClientRect()[this.dimension] + 'px'; 24896 return this.getBoundingClientRect()[this.dimension] + 'px';
24891 } 24897 }
24892 24898
24893 }); 24899 });
24894 24900
24895 </script> 24901 </script>
24896 <script> 24902 <script>
24897 24903
24898 /** 24904 /**
24905 * `Polymer.NeonAnimatableBehavior` is implemented by elements containing anim ations for use with
24906 * elements implementing `Polymer.NeonAnimationRunnerBehavior`.
24907 * @polymerBehavior
24908 */
24909 Polymer.NeonAnimatableBehavior = {
24910
24911 properties: {
24912
24913 /**
24914 * Animation configuration. See README for more info.
24915 */
24916 animationConfig: {
24917 type: Object
24918 },
24919
24920 /**
24921 * Convenience property for setting an 'entry' animation. Do not set `anim ationConfig.entry`
24922 * manually if using this. The animated node is set to `this` if using thi s property.
24923 */
24924 entryAnimation: {
24925 observer: '_entryAnimationChanged',
24926 type: String
24927 },
24928
24929 /**
24930 * Convenience property for setting an 'exit' animation. Do not set `anima tionConfig.exit`
24931 * manually if using this. The animated node is set to `this` if using thi s property.
24932 */
24933 exitAnimation: {
24934 observer: '_exitAnimationChanged',
24935 type: String
24936 }
24937
24938 },
24939
24940 _entryAnimationChanged: function() {
24941 this.animationConfig = this.animationConfig || {};
24942 this.animationConfig['entry'] = [{
24943 name: this.entryAnimation,
24944 node: this
24945 }];
24946 },
24947
24948 _exitAnimationChanged: function() {
24949 this.animationConfig = this.animationConfig || {};
24950 this.animationConfig['exit'] = [{
24951 name: this.exitAnimation,
24952 node: this
24953 }];
24954 },
24955
24956 _copyProperties: function(config1, config2) {
24957 // shallowly copy properties from config2 to config1
24958 for (var property in config2) {
24959 config1[property] = config2[property];
24960 }
24961 },
24962
24963 _cloneConfig: function(config) {
24964 var clone = {
24965 isClone: true
24966 };
24967 this._copyProperties(clone, config);
24968 return clone;
24969 },
24970
24971 _getAnimationConfigRecursive: function(type, map, allConfigs) {
24972 if (!this.animationConfig) {
24973 return;
24974 }
24975
24976 if(this.animationConfig.value && typeof this.animationConfig.value === 'fu nction') {
24977 this._warn(this._logf('playAnimation', "Please put 'animationConfig' ins ide of your components 'properties' object instead of outside of it."));
24978 return;
24979 }
24980
24981 // type is optional
24982 var thisConfig;
24983 if (type) {
24984 thisConfig = this.animationConfig[type];
24985 } else {
24986 thisConfig = this.animationConfig;
24987 }
24988
24989 if (!Array.isArray(thisConfig)) {
24990 thisConfig = [thisConfig];
24991 }
24992
24993 // iterate animations and recurse to process configurations from child nod es
24994 if (thisConfig) {
24995 for (var config, index = 0; config = thisConfig[index]; index++) {
24996 if (config.animatable) {
24997 config.animatable._getAnimationConfigRecursive(config.type || type, map, allConfigs);
24998 } else {
24999 if (config.id) {
25000 var cachedConfig = map[config.id];
25001 if (cachedConfig) {
25002 // merge configurations with the same id, making a clone lazily
25003 if (!cachedConfig.isClone) {
25004 map[config.id] = this._cloneConfig(cachedConfig)
25005 cachedConfig = map[config.id];
25006 }
25007 this._copyProperties(cachedConfig, config);
25008 } else {
25009 // put any configs with an id into a map
25010 map[config.id] = config;
25011 }
25012 } else {
25013 allConfigs.push(config);
25014 }
25015 }
25016 }
25017 }
25018 },
25019
25020 /**
25021 * An element implementing `Polymer.NeonAnimationRunnerBehavior` calls this method to configure
25022 * an animation with an optional type. Elements implementing `Polymer.NeonAn imatableBehavior`
25023 * should define the property `animationConfig`, which is either a configura tion object
25024 * or a map of animation type to array of configuration objects.
25025 */
25026 getAnimationConfig: function(type) {
25027 var map = {};
25028 var allConfigs = [];
25029 this._getAnimationConfigRecursive(type, map, allConfigs);
25030 // append the configurations saved in the map to the array
25031 for (var key in map) {
25032 allConfigs.push(map[key]);
25033 }
25034 return allConfigs;
25035 }
25036
25037 };
25038
25039 </script>
25040 <script>
25041
25042 /**
25043 * `Polymer.NeonAnimationRunnerBehavior` adds a method to run animations.
25044 *
25045 * @polymerBehavior Polymer.NeonAnimationRunnerBehavior
25046 */
25047 Polymer.NeonAnimationRunnerBehaviorImpl = {
25048
25049 _configureAnimations: function(configs) {
25050 var results = [];
25051 if (configs.length > 0) {
25052 for (var config, index = 0; config = configs[index]; index++) {
25053 var neonAnimation = document.createElement(config.name);
25054 // is this element actually a neon animation?
25055 if (neonAnimation.isNeonAnimation) {
25056 var result = null;
25057 // configuration or play could fail if polyfills aren't loaded
25058 try {
25059 result = neonAnimation.configure(config);
25060 // Check if we have an Effect rather than an Animation
25061 if (typeof result.cancel != 'function') {
25062 result = document.timeline.play(result);
25063 }
25064 } catch (e) {
25065 result = null;
25066 console.warn('Couldnt play', '(', config.name, ').', e);
25067 }
25068 if (result) {
25069 results.push({
25070 neonAnimation: neonAnimation,
25071 config: config,
25072 animation: result,
25073 });
25074 }
25075 } else {
25076 console.warn(this.is + ':', config.name, 'not found!');
25077 }
25078 }
25079 }
25080 return results;
25081 },
25082
25083 _shouldComplete: function(activeEntries) {
25084 var finished = true;
25085 for (var i = 0; i < activeEntries.length; i++) {
25086 if (activeEntries[i].animation.playState != 'finished') {
25087 finished = false;
25088 break;
25089 }
25090 }
25091 return finished;
25092 },
25093
25094 _complete: function(activeEntries) {
25095 for (var i = 0; i < activeEntries.length; i++) {
25096 activeEntries[i].neonAnimation.complete(activeEntries[i].config);
25097 }
25098 for (var i = 0; i < activeEntries.length; i++) {
25099 activeEntries[i].animation.cancel();
25100 }
25101 },
25102
25103 /**
25104 * Plays an animation with an optional `type`.
25105 * @param {string=} type
25106 * @param {!Object=} cookie
25107 */
25108 playAnimation: function(type, cookie) {
25109 var configs = this.getAnimationConfig(type);
25110 if (!configs) {
25111 return;
25112 }
25113 this._active = this._active || {};
25114 if (this._active[type]) {
25115 this._complete(this._active[type]);
25116 delete this._active[type];
25117 }
25118
25119 var activeEntries = this._configureAnimations(configs);
25120
25121 if (activeEntries.length == 0) {
25122 this.fire('neon-animation-finish', cookie, {bubbles: false});
25123 return;
25124 }
25125
25126 this._active[type] = activeEntries;
25127
25128 for (var i = 0; i < activeEntries.length; i++) {
25129 activeEntries[i].animation.onfinish = function() {
25130 if (this._shouldComplete(activeEntries)) {
25131 this._complete(activeEntries);
25132 delete this._active[type];
25133 this.fire('neon-animation-finish', cookie, {bubbles: false});
25134 }
25135 }.bind(this);
25136 }
25137 },
25138
25139 /**
25140 * Cancels the currently running animations.
25141 */
25142 cancelAnimation: function() {
25143 for (var k in this._animations) {
25144 this._animations[k].cancel();
25145 }
25146 this._animations = {};
25147 }
25148 };
25149
25150 /** @polymerBehavior Polymer.NeonAnimationRunnerBehavior */
25151 Polymer.NeonAnimationRunnerBehavior = [
25152 Polymer.NeonAnimatableBehavior,
25153 Polymer.NeonAnimationRunnerBehaviorImpl
25154 ];
25155 </script>
25156 <script>
25157
25158 /**
25159 Use `Polymer.PaperDialogBehavior` and `paper-dialog-shared-styles.html` to imple ment a Material Design
25160 dialog.
25161
25162 For example, if `<paper-dialog-impl>` implements this behavior:
25163
25164 <paper-dialog-impl>
25165 <h2>Header</h2>
25166 <div>Dialog body</div>
25167 <div class="buttons">
25168 <paper-button dialog-dismiss>Cancel</paper-button>
25169 <paper-button dialog-confirm>Accept</paper-button>
25170 </div>
25171 </paper-dialog-impl>
25172
25173 `paper-dialog-shared-styles.html` provide styles for a header, content area, and an action area for buttons.
25174 Use the `<h2>` tag for the header and the `buttons` class for the action area. Y ou can use the
25175 `paper-dialog-scrollable` element (in its own repository) if you need a scrollin g content area.
25176
25177 Use the `dialog-dismiss` and `dialog-confirm` attributes on interactive controls to close the
25178 dialog. If the user dismisses the dialog with `dialog-confirm`, the `closingReas on` will update
25179 to include `confirmed: true`.
25180
25181 ### Accessibility
25182
25183 This element has `role="dialog"` by default. Depending on the context, it may be more appropriate
25184 to override this attribute with `role="alertdialog"`.
25185
25186 If `modal` is set, the element will prevent the focus from exiting the element.
25187 It will also ensure that focus remains in the dialog.
25188
25189 @hero hero.svg
25190 @demo demo/index.html
25191 @polymerBehavior Polymer.PaperDialogBehavior
25192 */
25193
25194 Polymer.PaperDialogBehaviorImpl = {
25195
25196 hostAttributes: {
25197 'role': 'dialog',
25198 'tabindex': '-1'
25199 },
25200
25201 properties: {
25202
25203 /**
25204 * If `modal` is true, this implies `no-cancel-on-outside-click`, `no-canc el-on-esc-key` and `with-backdrop`.
25205 */
25206 modal: {
25207 type: Boolean,
25208 value: false
25209 }
25210
25211 },
25212
25213 observers: [
25214 '_modalChanged(modal, _readied)'
25215 ],
25216
25217 listeners: {
25218 'tap': '_onDialogClick'
25219 },
25220
25221 ready: function () {
25222 // Only now these properties can be read.
25223 this.__prevNoCancelOnOutsideClick = this.noCancelOnOutsideClick;
25224 this.__prevNoCancelOnEscKey = this.noCancelOnEscKey;
25225 this.__prevWithBackdrop = this.withBackdrop;
25226 },
25227
25228 _modalChanged: function(modal, readied) {
25229 // modal implies noCancelOnOutsideClick, noCancelOnEscKey and withBackdrop .
25230 // We need to wait for the element to be ready before we can read the
25231 // properties values.
25232 if (!readied) {
25233 return;
25234 }
25235
25236 if (modal) {
25237 this.__prevNoCancelOnOutsideClick = this.noCancelOnOutsideClick;
25238 this.__prevNoCancelOnEscKey = this.noCancelOnEscKey;
25239 this.__prevWithBackdrop = this.withBackdrop;
25240 this.noCancelOnOutsideClick = true;
25241 this.noCancelOnEscKey = true;
25242 this.withBackdrop = true;
25243 } else {
25244 // If the value was changed to false, let it false.
25245 this.noCancelOnOutsideClick = this.noCancelOnOutsideClick &&
25246 this.__prevNoCancelOnOutsideClick;
25247 this.noCancelOnEscKey = this.noCancelOnEscKey &&
25248 this.__prevNoCancelOnEscKey;
25249 this.withBackdrop = this.withBackdrop && this.__prevWithBackdrop;
25250 }
25251 },
25252
25253 _updateClosingReasonConfirmed: function(confirmed) {
25254 this.closingReason = this.closingReason || {};
25255 this.closingReason.confirmed = confirmed;
25256 },
25257
25258 /**
25259 * Will dismiss the dialog if user clicked on an element with dialog-dismiss
25260 * or dialog-confirm attribute.
25261 */
25262 _onDialogClick: function(event) {
25263 // Search for the element with dialog-confirm or dialog-dismiss,
25264 // from the root target until this (excluded).
25265 var path = Polymer.dom(event).path;
25266 for (var i = 0; i < path.indexOf(this); i++) {
25267 var target = path[i];
25268 if (target.hasAttribute && (target.hasAttribute('dialog-dismiss') || tar get.hasAttribute('dialog-confirm'))) {
25269 this._updateClosingReasonConfirmed(target.hasAttribute('dialog-confirm '));
25270 this.close();
25271 event.stopPropagation();
25272 break;
25273 }
25274 }
25275 }
25276
25277 };
25278
25279 /** @polymerBehavior */
25280 Polymer.PaperDialogBehavior = [Polymer.IronOverlayBehavior, Polymer.PaperDialo gBehaviorImpl];
25281
25282 </script>
25283
25284
25285 <dom-module id="paper-dialog-shared-styles" assetpath="/res/imp/bower_components /paper-dialog-behavior/">
25286 <template>
25287 <style>
25288 :host {
25289 display: block;
25290 margin: 24px 40px;
25291
25292 background: var(--paper-dialog-background-color, --primary-background-co lor);
25293 color: var(--paper-dialog-color, --primary-text-color);
25294
25295 @apply(--paper-font-body1);
25296 @apply(--shadow-elevation-16dp);
25297 @apply(--paper-dialog);
25298 }
25299
25300 :host > ::content > * {
25301 margin-top: 20px;
25302 padding: 0 24px;
25303 }
25304
25305 :host > ::content > .no-padding {
25306 padding: 0;
25307 }
25308
25309 :host > ::content > *:first-child {
25310 margin-top: 24px;
25311 }
25312
25313 :host > ::content > *:last-child {
25314 margin-bottom: 24px;
25315 }
25316
25317 :host > ::content h2 {
25318 position: relative;
25319 margin: 0;
25320 @apply(--paper-font-title);
25321
25322 @apply(--paper-dialog-title);
25323 }
25324
25325 :host > ::content .buttons {
25326 position: relative;
25327 padding: 8px 8px 8px 24px;
25328 margin: 0;
25329
25330 color: var(--paper-dialog-button-color, --primary-color);
25331
25332 @apply(--layout-horizontal);
25333 @apply(--layout-end-justified);
25334 }
25335 </style>
25336 </template>
25337 </dom-module>
25338
25339
25340 <dom-module id="paper-dialog" assetpath="/res/imp/bower_components/paper-dialog/ ">
25341 <template>
25342 <style include="paper-dialog-shared-styles"></style>
25343 <content></content>
25344 </template>
25345 </dom-module>
25346
25347 <script>
25348
25349 (function() {
25350
25351 Polymer({
25352
25353 is: 'paper-dialog',
25354
25355 behaviors: [
25356 Polymer.PaperDialogBehavior,
25357 Polymer.NeonAnimationRunnerBehavior
25358 ],
25359
25360 listeners: {
25361 'neon-animation-finish': '_onNeonAnimationFinish'
25362 },
25363
25364 _renderOpened: function() {
25365 this.cancelAnimation();
25366 this.playAnimation('entry');
25367 },
25368
25369 _renderClosed: function() {
25370 this.cancelAnimation();
25371 this.playAnimation('exit');
25372 },
25373
25374 _onNeonAnimationFinish: function() {
25375 if (this.opened) {
25376 this._finishRenderOpened();
25377 } else {
25378 this._finishRenderClosed();
25379 }
25380 }
25381
25382 });
25383
25384 })();
25385
25386 </script>
25387 <script>
25388
25389 /**
24899 * `Polymer.IronMenuBehavior` implements accessible menu behavior. 25390 * `Polymer.IronMenuBehavior` implements accessible menu behavior.
24900 * 25391 *
24901 * @demo demo/index.html 25392 * @demo demo/index.html
24902 * @polymerBehavior Polymer.IronMenuBehavior 25393 * @polymerBehavior Polymer.IronMenuBehavior
24903 */ 25394 */
24904 Polymer.IronMenuBehaviorImpl = { 25395 Polymer.IronMenuBehaviorImpl = {
24905 25396
24906 properties: { 25397 properties: {
24907 25398
24908 /** 25399 /**
(...skipping 1354 matching lines...) Expand 10 before | Expand all | Expand 10 after
26263 margin-bottom: 5px; 26754 margin-bottom: 5px;
26264 } 26755 }
26265 td, th { 26756 td, th {
26266 border: 1px solid #BBB; 26757 border: 1px solid #BBB;
26267 padding: 5px; 26758 padding: 5px;
26268 } 26759 }
26269 26760
26270 .quarantined, .failed_task { 26761 .quarantined, .failed_task {
26271 background-color: #ffdddd; 26762 background-color: #ffdddd;
26272 } 26763 }
26273 .dead { 26764 .dead, .bot_died {
26274 background-color: #cccccc; 26765 background-color: #cccccc;
26275 } 26766 }
26276 26767
26277 .message { 26768 .message {
26278 white-space: pre-line; 26769 white-space: pre-line;
26279 font-family: monospace; 26770 font-family: monospace;
26280 } 26771 }
26281 26772
26282 .bot_state { 26773 .bot_state {
26283 white-space: pre; 26774 white-space: pre;
(...skipping 22 matching lines...) Expand all
26306 } 26797 }
26307 26798
26308 paper-tab.iron-selected { 26799 paper-tab.iron-selected {
26309 background-color: #A6CEE3; 26800 background-color: #A6CEE3;
26310 border: 3px solid #1F78B4; 26801 border: 3px solid #1F78B4;
26311 color: #000; 26802 color: #000;
26312 font-weight: bold; 26803 font-weight: bold;
26313 text-decoration: underline; 26804 text-decoration: underline;
26314 } 26805 }
26315 26806
26807 paper-dialog {
26808 border-radius: 6px;
26809 }
26810
26316 </style> 26811 </style>
26317 26812
26318 <url-param name="id" value="{{bot_id}}"> 26813 <url-param name="id" value="{{bot_id}}">
26319 </url-param> 26814 </url-param>
26320 <url-param name="show_all_events" value="{{_show_all}}"> 26815 <url-param name="show_all_events" value="{{_show_all}}">
26321 </url-param> 26816 </url-param>
26322 <url-param name="selected" value="{{_selected}}"> 26817 <url-param name="selected" value="{{_selected}}">
26323 </url-param> 26818 </url-param>
26324 <url-param name="show_state" value="{{_show_state}}"> 26819 <url-param name="show_state" value="{{_show_state}}">
26325 </url-param> 26820 </url-param>
26326 26821
26327 <swarming-app client_id="[[client_id]]" auth_headers="{{_auth_headers}}" per missions="{{_permissions}}" signed_in="{{_signed_in}}" busy="[[_busy]]" name="Sw arming Bot Page"> 26822 <swarming-app client_id="[[client_id]]" auth_headers="{{_auth_headers}}" per missions="{{_permissions}}" signed_in="{{_signed_in}}" busy="[[_busy]]" name="Sw arming Bot Page">
26328 26823
26329 <h2 hidden$="[[_signed_in]]">You must sign in to see anything useful.</h2> 26824 <h2 hidden$="[[_signed_in]]">You must sign in to see anything useful.</h2>
26330 26825
26331 <div hidden$="[[_not(_signed_in)]]"> 26826 <div hidden$="[[_not(_signed_in)]]">
26332 26827
26333 <bot-page-data auth_headers="[[_auth_headers]]" bot_id="[[bot_id]]" bot= "{{_bot}}" busy="{{_busy}}" events="{{_events}}" tasks="{{_tasks}}"> 26828 <bot-page-data id="data" auth_headers="[[_auth_headers]]" bot_id="[[bot_ id]]" bot="{{_bot}}" busy="{{_busy}}" events="{{_events}}" tasks="{{_tasks}}">
26334 </bot-page-data> 26829 </bot-page-data>
26335 26830
26336 <div class="header horizontal layout"> 26831 <div class="header horizontal layout">
26337 <paper-input class="id_input" label="Bot id" value="{{bot_id}}"></pape r-input> 26832 <paper-input class="id_input" label="Bot id" value="{{bot_id}}"></pape r-input>
26338 <button> 26833 <button on-click="_refresh">
26339 <iron-icon class="refresh" icon="icons:refresh"></iron-icon> 26834 <iron-icon class="refresh" icon="icons:refresh"></iron-icon>
26340 </button> 26835 </button>
26341 </div> 26836 </div>
26342 26837
26343 <div> 26838 <div>
26344 <table> 26839 <table>
26345 <tbody><tr class$="[[_isDead(_bot)]]"> 26840 <tbody><tr class$="[[_isDead(_bot)]]">
26346 <td>Last Seen</td> 26841 <td>Last Seen</td>
26347 <td title="[[_bot.human_last_seen_ts]]"> 26842 <td title="[[_bot.human_last_seen_ts]]">
26348 [[_timeDiffExact(_bot.last_seen_ts)]] ago</td> 26843 [[_timeDiffExact(_bot.last_seen_ts)]] ago</td>
26349 <td> 26844 <td>
26350 26845
26351 <template is="dom-if" if="[[_canShutdown(_bot,_permissions)]]"> 26846 <template is="dom-if" if="[[_canShutdown(_bot,_permissions)]]">
26352 <button class="raised"> 26847 <button class="raised" on-click="_promptShutdown">
26353 Shut Down Gracefully 26848 Shut Down Gracefully
26354 </button> 26849 </button>
26355 </template> 26850 </template>
26356 <template is="dom-if" if="[[_canDelete(_bot,_permissions)]]"> 26851 <template is="dom-if" if="[[_canDelete(_bot,_permissions)]]">
26357 <button class="raised"> 26852 <button class="raised" on-click="_promptDelete">
26358 Delete 26853 Delete
26359 </button> 26854 </button>
26360 </template> 26855 </template>
26361 </td> 26856 </td>
26362 </tr> 26857 </tr>
26363 <template is="dom-if" if="[[_bot.quarantined]]"> 26858 <template is="dom-if" if="[[_bot.quarantined]]">
26364 <tr class="quarantined"> 26859 <tr class="quarantined">
26365 <td>Quarantined</td> 26860 <td>Quarantined</td>
26366 <td colspan="2">[[_quarantineMessage(_bot)]]</td> 26861 <td colspan="2">[[_quarantineMessage(_bot)]]</td>
26367 </tr> 26862 </tr>
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after
26450 <thead> 26945 <thead>
26451 <tr> 26946 <tr>
26452 <th>Task</th> 26947 <th>Task</th>
26453 <th>Started</th> 26948 <th>Started</th>
26454 <th>Duration</th> 26949 <th>Duration</th>
26455 <th>Result</th> 26950 <th>Result</th>
26456 </tr> 26951 </tr>
26457 </thead> 26952 </thead>
26458 <tbody> 26953 <tbody>
26459 <template is="dom-repeat" items="{{_tasks}}" as="task"> 26954 <template is="dom-repeat" items="{{_tasks}}" as="task">
26460 <tr> 26955 <tr class$="[[_taskClass(task)]]">
26461 <td><a target="_blank" href$="[[_taskLink(task.task_id)]]">[[t ask.name]]</a></td> 26956 <td><a target="_blank" href$="[[_taskLink(task.task_id)]]">[[t ask.name]]</a></td>
26462 <td>[[task.human_started_ts]]</td> 26957 <td>[[task.human_started_ts]]</td>
26463 <td title="[[task.human_completed_ts]]">[[task.human_duration] ]</td> 26958 <td title="[[task.human_completed_ts]]">[[task.human_duration] ]</td>
26464 <td>[[task.state]]</td> 26959 <td>[[task.state]]</td>
26465 </tr> 26960 </tr>
26466 </template> 26961 </template>
26467 </tbody> 26962 </tbody>
26468 </table> 26963 </table>
26469 </template> 26964 </template>
26470 26965
(...skipping 18 matching lines...) Expand all
26489 <td> 26984 <td>
26490 <a target="_blank" href$="[[_luciLink(_bot.version)]]">[[_sh orten(_bot.version,'8')]]</a> 26985 <a target="_blank" href$="[[_luciLink(_bot.version)]]">[[_sh orten(_bot.version,'8')]]</a>
26491 </td> 26986 </td>
26492 </tr> 26987 </tr>
26493 </template> 26988 </template>
26494 </tbody> 26989 </tbody>
26495 </table> 26990 </table>
26496 </template> 26991 </template>
26497 </div> 26992 </div>
26498 26993
26994 </swarming-app>
26499 26995
26500 </swarming-app> 26996 <paper-dialog id="prompt" modal="" on-iron-overlay-closed="_promptClosed">
26997 <h2>Are you sure?</h2>
26998 <div>Are you sure you want to [[_dialogPrompt]]?</div>
26999 <div class="buttons">
27000 <paper-button dialog-dismiss="" autofocus="">No</paper-button>
27001 <paper-button dialog-confirm="">Yes</paper-button>
27002 </div>
27003 </paper-dialog>
27004
27005 <paper-toast id="toast"></paper-toast>
26501 27006
26502 </template> 27007 </template>
26503 <script> 27008 <script>
26504 (function(){ 27009 (function(){
26505 27010
26506 27011
26507 Polymer({ 27012 Polymer({
26508 is: 'bot-page', 27013 is: 'bot-page',
26509 27014
26510 behaviors: [ 27015 behaviors: [
26511 SwarmingBehaviors.BotPageBehavior, 27016 SwarmingBehaviors.BotPageBehavior,
26512 ], 27017 ],
26513 27018
26514 properties: { 27019 properties: {
26515 bot_id: { 27020 bot_id: {
26516 type: String, 27021 type: String,
26517 }, 27022 },
26518 client_id: { 27023 client_id: {
26519 type: String, 27024 type: String,
26520 }, 27025 },
26521 27026
27027 _auth_headers: {
27028 type: Object,
27029 },
26522 _bot: { 27030 _bot: {
26523 type: Object, 27031 type: Object,
26524 }, 27032 },
27033 _dialogPrompt: {
27034 type: String,
27035 value: "",
27036 },
26525 _selected: { 27037 _selected: {
26526 type: Number, 27038 type: Number,
26527 }, 27039 },
26528 _show_all: { 27040 _show_all: {
26529 type: Boolean, 27041 type: Boolean,
26530 }, 27042 },
26531 _show_state: { 27043 _show_state: {
26532 type: Boolean, 27044 type: Boolean,
26533 } 27045 }
26534 }, 27046 },
(...skipping 10 matching lines...) Expand all
26545 return bot && !bot.is_dead && permissions.terminate_bot; 27057 return bot && !bot.is_dead && permissions.terminate_bot;
26546 }, 27058 },
26547 27059
26548 _concat: function(arr) { 27060 _concat: function(arr) {
26549 if (!arr) { 27061 if (!arr) {
26550 return ""; 27062 return "";
26551 } 27063 }
26552 return arr.join(" | "); 27064 return arr.join(" | ");
26553 }, 27065 },
26554 27066
27067 _deleteBot: function() {
27068 this.$.toast.text="Deleting "+this.bot_id;
27069 this._postWithToast("/_ah/api/swarming/v1/bot/"+this.bot_id+"/delete",
27070 this.$.toast, this._auth_headers);
27071 },
27072
26555 _eventList(events, showAll) { 27073 _eventList(events, showAll) {
26556 if (!events) { 27074 if (!events) {
26557 return []; 27075 return [];
26558 } 27076 }
26559 return events.filter(function(e){ 27077 return events.filter(function(e){
26560 return showAll || e.message; 27078 return showAll || e.message;
26561 }); 27079 });
26562 }, 27080 },
26563 27081
26564 _isDead(bot){ 27082 _isDead(bot){
(...skipping 16 matching lines...) Expand all
26581 return 1; 27099 return 1;
26582 } 27100 }
26583 return 1 + arr.length; 27101 return 1 + arr.length;
26584 }, 27102 },
26585 27103
26586 _prettyPrint: function(obj) { 27104 _prettyPrint: function(obj) {
26587 obj = obj || {}; 27105 obj = obj || {};
26588 return JSON.stringify(obj, null, 2); 27106 return JSON.stringify(obj, null, 2);
26589 }, 27107 },
26590 27108
27109 _promptClosed: function(e) {
27110 if (e.detail.confirmed) {
27111 if (this._dialogPrompt.startsWith("shut down")) {
27112 this._shutdownBot();
27113 } else {
27114 this._deleteBot();
27115 }
27116 }
27117 },
27118
27119 _promptDelete: function() {
27120 this.set("_dialogPrompt", "delete "+this.bot_id);
27121 this.$.prompt.open();
27122 },
27123
27124 _promptShutdown: function() {
27125 this.set("_dialogPrompt", "shut down "+this.bot_id);
27126 this.$.prompt.open();
27127 },
27128
26591 _quarantineMessage: function(bot) { 27129 _quarantineMessage: function(bot) {
26592 if (bot && bot.quarantined) { 27130 if (bot && bot.quarantined) {
26593 var msg = bot.state.quarantined; 27131 var msg = bot.state.quarantined;
26594 // Sometimes, the quarantined message is actually in "error". This 27132 // Sometimes, the quarantined message is actually in "error". This
26595 // happens when the bot code has thrown an exception. 27133 // happens when the bot code has thrown an exception.
26596 if (msg === undefined || msg === "true" || msg === true) { 27134 if (msg === undefined || msg === "true" || msg === true) {
26597 msg = this._attribute(bot, "error"); 27135 msg = this._attribute(bot, "error");
26598 } 27136 }
26599 return msg || "True"; 27137 return msg || "True";
26600 } 27138 }
26601 return ""; 27139 return "";
26602 }, 27140 },
26603 27141
27142 _refresh: function() {
27143 this.$.data.request();
27144 },
27145
26604 _shorten: function(str, length) { 27146 _shorten: function(str, length) {
26605 if (!str || ! length) { 27147 if (!str || ! length) {
26606 return ""; 27148 return "";
26607 } 27149 }
26608 return str.substring(0, length); 27150 return str.substring(0, length);
26609 }, 27151 },
26610 27152
27153 _shutdownBot: function() {
27154 this.$.toast.text="Shutting down "+this.bot_id;
27155 this._postWithToast("/_ah/api/swarming/v1/bot/"+this.bot_id+"/terminate" ,
27156 this.$.toast, this._auth_headers);
27157 },
27158
26611 _task: function(bot) { 27159 _task: function(bot) {
26612 return (bot && bot.task_id) || "idle"; 27160 return (bot && bot.task_id) || "idle";
26613 }, 27161 },
26614 27162
27163 _taskClass: function(task) {
27164 if (task && task.internal_failure) {
27165 return "bot_died";
27166 }
27167 if (task && task.failure) {
27168 return "failed_task";
27169 }
27170 return "";
27171 },
27172
26615 _taskLink: function(task_id) { 27173 _taskLink: function(task_id) {
26616 // TODO(kjlubick): Migrate this to /newui/ when ready 27174 // TODO(kjlubick): Migrate this to /newui/ when ready
26617 if (task_id) { 27175 if (task_id) {
26618 return "/user/task/" + task_id; 27176 return "/user/task/" + task_id;
26619 } 27177 }
26620 return undefined; 27178 return undefined;
26621 }, 27179 },
26622 27180
26623 _toggleState: function() { 27181 _toggleState: function() {
26624 this.set("_show_state", !this._show_state); 27182 this.set("_show_state", !this._show_state);
26625 } 27183 }
26626 27184
26627 }); 27185 });
26628 })(); 27186 })();
26629 </script> 27187 </script>
26630 </dom-module></div></body></html> 27188 </dom-module></div></body></html>
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698