| Index: appengine/swarming/elements/build/elements.html
|
| diff --git a/appengine/swarming/elements/build/elements.html b/appengine/swarming/elements/build/elements.html
|
| index 04f7bc5bddc0e84069d4e12aee5ea588594014dc..ca1e4793d932ae7f86ebd1aa79dfbb1b939bcc29 100644
|
| --- a/appengine/swarming/elements/build/elements.html
|
| +++ b/appengine/swarming/elements/build/elements.html
|
| @@ -8094,1061 +8094,59 @@ this.fire('dom-change');
|
|
|
|
|
|
|
| -<script>function MakePromise (asap) {
|
| - function Promise(fn) {
|
| - if (typeof this !== 'object' || typeof fn !== 'function') throw new TypeError();
|
| - this._state = null;
|
| - this._value = null;
|
| - this._deferreds = []
|
| -
|
| - doResolve(fn, resolve.bind(this), reject.bind(this));
|
| - }
|
| -
|
| - function handle(deferred) {
|
| - var me = this;
|
| - if (this._state === null) {
|
| - this._deferreds.push(deferred);
|
| - return
|
| - }
|
| - asap(function() {
|
| - var cb = me._state ? deferred.onFulfilled : deferred.onRejected
|
| - if (typeof cb !== 'function') {
|
| - (me._state ? deferred.resolve : deferred.reject)(me._value);
|
| - return;
|
| - }
|
| - var ret;
|
| - try {
|
| - ret = cb(me._value);
|
| - }
|
| - catch (e) {
|
| - deferred.reject(e);
|
| - return;
|
| - }
|
| - deferred.resolve(ret);
|
| - })
|
| - }
|
| -
|
| - function resolve(newValue) {
|
| - try { //Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure
|
| - if (newValue === this) throw new TypeError();
|
| - if (newValue && (typeof newValue === 'object' || typeof newValue === 'function')) {
|
| - var then = newValue.then;
|
| - if (typeof then === 'function') {
|
| - doResolve(then.bind(newValue), resolve.bind(this), reject.bind(this));
|
| - return;
|
| - }
|
| - }
|
| - this._state = true;
|
| - this._value = newValue;
|
| - finale.call(this);
|
| - } catch (e) { reject.call(this, e); }
|
| - }
|
| -
|
| - function reject(newValue) {
|
| - this._state = false;
|
| - this._value = newValue;
|
| - finale.call(this);
|
| - }
|
| -
|
| - function finale() {
|
| - for (var i = 0, len = this._deferreds.length; i < len; i++) {
|
| - handle.call(this, this._deferreds[i]);
|
| - }
|
| - this._deferreds = null;
|
| - }
|
| -
|
| - /**
|
| - * Take a potentially misbehaving resolver function and make sure
|
| - * onFulfilled and onRejected are only called once.
|
| - *
|
| - * Makes no guarantees about asynchrony.
|
| - */
|
| - function doResolve(fn, onFulfilled, onRejected) {
|
| - var done = false;
|
| - try {
|
| - fn(function (value) {
|
| - if (done) return;
|
| - done = true;
|
| - onFulfilled(value);
|
| - }, function (reason) {
|
| - if (done) return;
|
| - done = true;
|
| - onRejected(reason);
|
| - })
|
| - } catch (ex) {
|
| - if (done) return;
|
| - done = true;
|
| - onRejected(ex);
|
| - }
|
| - }
|
| -
|
| - Promise.prototype['catch'] = function (onRejected) {
|
| - return this.then(null, onRejected);
|
| - };
|
| -
|
| - Promise.prototype.then = function(onFulfilled, onRejected) {
|
| - var me = this;
|
| - return new Promise(function(resolve, reject) {
|
| - handle.call(me, {
|
| - onFulfilled: onFulfilled,
|
| - onRejected: onRejected,
|
| - resolve: resolve,
|
| - reject: reject
|
| - });
|
| - })
|
| - };
|
| -
|
| - Promise.resolve = function (value) {
|
| - if (value && typeof value === 'object' && value.constructor === Promise) {
|
| - return value;
|
| - }
|
| -
|
| - return new Promise(function (resolve) {
|
| - resolve(value);
|
| - });
|
| - };
|
| -
|
| - Promise.reject = function (value) {
|
| - return new Promise(function (resolve, reject) {
|
| - reject(value);
|
| - });
|
| - };
|
| -
|
| -
|
| - return Promise;
|
| -}
|
| -
|
| -if (typeof module !== 'undefined') {
|
| - module.exports = MakePromise;
|
| -}
|
| -
|
| -</script>
|
| -<script>
|
| -if (!window.Promise) {
|
| - window.Promise = MakePromise(Polymer.Base.async);
|
| -}
|
| -</script>
|
| <script>
|
| - 'use strict';
|
| -
|
| - Polymer({
|
| - is: 'iron-request',
|
| -
|
| - hostAttributes: {
|
| - hidden: true
|
| - },
|
| -
|
| - properties: {
|
| -
|
| - /**
|
| - * A reference to the XMLHttpRequest instance used to generate the
|
| - * network request.
|
| - *
|
| - * @type {XMLHttpRequest}
|
| - */
|
| - xhr: {
|
| - type: Object,
|
| - notify: true,
|
| - readOnly: true,
|
| - value: function() {
|
| - return new XMLHttpRequest();
|
| - }
|
| - },
|
| -
|
| - /**
|
| - * A reference to the parsed response body, if the `xhr` has completely
|
| - * resolved.
|
| - *
|
| - * @type {*}
|
| - * @default null
|
| - */
|
| - response: {
|
| - type: Object,
|
| - notify: true,
|
| - readOnly: true,
|
| - value: function() {
|
| - return null;
|
| - }
|
| - },
|
| -
|
| - /**
|
| - * A reference to the status code, if the `xhr` has completely resolved.
|
| - */
|
| - status: {
|
| - type: Number,
|
| - notify: true,
|
| - readOnly: true,
|
| - value: 0
|
| - },
|
| -
|
| - /**
|
| - * A reference to the status text, if the `xhr` has completely resolved.
|
| - */
|
| - statusText: {
|
| - type: String,
|
| - notify: true,
|
| - readOnly: true,
|
| - value: ''
|
| - },
|
| -
|
| - /**
|
| - * A promise that resolves when the `xhr` response comes back, or rejects
|
| - * if there is an error before the `xhr` completes.
|
| - *
|
| - * @type {Promise}
|
| - */
|
| - completes: {
|
| - type: Object,
|
| - readOnly: true,
|
| - notify: true,
|
| - value: function() {
|
| - return new Promise(function (resolve, reject) {
|
| - this.resolveCompletes = resolve;
|
| - this.rejectCompletes = reject;
|
| - }.bind(this));
|
| - }
|
| - },
|
| -
|
| - /**
|
| - * An object that contains progress information emitted by the XHR if
|
| - * available.
|
| - *
|
| - * @default {}
|
| - */
|
| - progress: {
|
| - type: Object,
|
| - notify: true,
|
| - readOnly: true,
|
| - value: function() {
|
| - return {};
|
| - }
|
| - },
|
| -
|
| - /**
|
| - * Aborted will be true if an abort of the request is attempted.
|
| - */
|
| - aborted: {
|
| - type: Boolean,
|
| - notify: true,
|
| - readOnly: true,
|
| - value: false,
|
| - },
|
| -
|
| - /**
|
| - * Errored will be true if the browser fired an error event from the
|
| - * XHR object (mainly network errors).
|
| - */
|
| - errored: {
|
| - type: Boolean,
|
| - notify: true,
|
| - readOnly: true,
|
| - value: false
|
| - },
|
| -
|
| - /**
|
| - * TimedOut will be true if the XHR threw a timeout event.
|
| - */
|
| - timedOut: {
|
| - type: Boolean,
|
| - notify: true,
|
| - readOnly: true,
|
| - value: false
|
| - }
|
| - },
|
| -
|
| - /**
|
| - * Succeeded is true if the request succeeded. The request succeeded if it
|
| - * loaded without error, wasn't aborted, and the status code is ≥ 200, and
|
| - * < 300, or if the status code is 0.
|
| - *
|
| - * The status code 0 is accepted as a success because some schemes - e.g.
|
| - * file:// - don't provide status codes.
|
| - *
|
| - * @return {boolean}
|
| - */
|
| - get succeeded() {
|
| - if (this.errored || this.aborted || this.timedOut) {
|
| - return false;
|
| - }
|
| - var status = this.xhr.status || 0;
|
| -
|
| - // Note: if we are using the file:// protocol, the status code will be 0
|
| - // for all outcomes (successful or otherwise).
|
| - return status === 0 ||
|
| - (status >= 200 && status < 300);
|
| - },
|
| -
|
| - /**
|
| - * Sends an HTTP request to the server and returns the XHR object.
|
| - *
|
| - * The handling of the `body` parameter will vary based on the Content-Type
|
| - * header. See the docs for iron-ajax's `body` param for details.
|
| - *
|
| - * @param {{
|
| - * url: string,
|
| - * method: (string|undefined),
|
| - * async: (boolean|undefined),
|
| - * body: (ArrayBuffer|ArrayBufferView|Blob|Document|FormData|null|string|undefined|Object),
|
| - * headers: (Object|undefined),
|
| - * handleAs: (string|undefined),
|
| - * jsonPrefix: (string|undefined),
|
| - * withCredentials: (boolean|undefined)}} options -
|
| - * url The url to which the request is sent.
|
| - * method The HTTP method to use, default is GET.
|
| - * async By default, all requests are sent asynchronously. To send synchronous requests,
|
| - * set to false.
|
| - * body The content for the request body for POST method.
|
| - * headers HTTP request headers.
|
| - * handleAs The response type. Default is 'text'.
|
| - * withCredentials Whether or not to send credentials on the request. Default is false.
|
| - * timeout: (Number|undefined)
|
| - * @return {Promise}
|
| - */
|
| - send: function (options) {
|
| - var xhr = this.xhr;
|
| -
|
| - if (xhr.readyState > 0) {
|
| - return null;
|
| - }
|
| -
|
| - xhr.addEventListener('progress', function (progress) {
|
| - this._setProgress({
|
| - lengthComputable: progress.lengthComputable,
|
| - loaded: progress.loaded,
|
| - total: progress.total
|
| - });
|
| - }.bind(this))
|
| -
|
| - xhr.addEventListener('error', function (error) {
|
| - this._setErrored(true);
|
| - this._updateStatus();
|
| - this.rejectCompletes(error);
|
| - }.bind(this));
|
| -
|
| - xhr.addEventListener('timeout', function (error) {
|
| - this._setTimedOut(true);
|
| - this._updateStatus();
|
| - this.rejectCompletes(error);
|
| - }.bind(this));
|
| -
|
| - xhr.addEventListener('abort', function () {
|
| - this._updateStatus();
|
| - this.rejectCompletes(new Error('Request aborted.'));
|
| - }.bind(this));
|
| -
|
| - // Called after all of the above.
|
| - xhr.addEventListener('loadend', function () {
|
| - this._updateStatus();
|
| - this._setResponse(this.parseResponse());
|
| -
|
| - if (!this.succeeded) {
|
| - this.rejectCompletes(new Error('The request failed with status code: ' + this.xhr.status));
|
| + window.SwarmingBehaviors = window.SwarmingBehaviors || {};
|
| + (function(){
|
| + // This behavior wraps up all the shared swarming functionality.
|
| + SwarmingBehaviors.CommonBehavior = {
|
| +
|
| + // _getJsonAsync makes an XHR to a url, parses the response as JSON
|
| + // and sticks the resulting object into the property with the name given
|
| + // by "bindTo". If busy is defined, the property with that name will be
|
| + // set to true while the request is in flight and false afterwards.
|
| + // request headers (e.g. authentication) and query params will be used if
|
| + // provided. Query params is an object like {String:Array<String>}. On
|
| + // error, bindTo will be set to false. It is not set to undefined
|
| + // because computed values in Polymer don't fire if a property is
|
| + // undefined. Clients should check that bindTo is not falsey.
|
| + _getJsonAsync: function(bindTo, url, busy, headers, params) {
|
| + if (!bindTo || !url) {
|
| + console.log("Need at least a polymer element to bind to and a url");
|
| return;
|
| }
|
| -
|
| - this.resolveCompletes(this);
|
| - }.bind(this));
|
| -
|
| - this.url = options.url;
|
| - xhr.open(
|
| - options.method || 'GET',
|
| - options.url,
|
| - options.async !== false
|
| - );
|
| -
|
| - var acceptType = {
|
| - 'json': 'application/json',
|
| - 'text': 'text/plain',
|
| - 'html': 'text/html',
|
| - 'xml': 'application/xml',
|
| - 'arraybuffer': 'application/octet-stream'
|
| - }[options.handleAs];
|
| - var headers = options.headers || Object.create(null);
|
| - var newHeaders = Object.create(null);
|
| - for (var key in headers) {
|
| - newHeaders[key.toLowerCase()] = headers[key];
|
| - }
|
| - headers = newHeaders;
|
| -
|
| - if (acceptType && !headers['accept']) {
|
| - headers['accept'] = acceptType;
|
| - }
|
| - Object.keys(headers).forEach(function (requestHeader) {
|
| - if (/[A-Z]/.test(requestHeader)) {
|
| - Polymer.Base._error('Headers must be lower case, got', requestHeader);
|
| - }
|
| - xhr.setRequestHeader(
|
| - requestHeader,
|
| - headers[requestHeader]
|
| - );
|
| - }, this);
|
| -
|
| - if (options.async !== false) {
|
| - if (options.async) {
|
| - xhr.timeout = options.timeout;
|
| + if (busy) {
|
| + this.set(busy, true);
|
| }
|
| -
|
| - var handleAs = options.handleAs;
|
| -
|
| - // If a JSON prefix is present, the responseType must be 'text' or the
|
| - // browser won’t be able to parse the response.
|
| - if (!!options.jsonPrefix || !handleAs) {
|
| - handleAs = 'text';
|
| - }
|
| -
|
| - // In IE, `xhr.responseType` is an empty string when the response
|
| - // returns. Hence, caching it as `xhr._responseType`.
|
| - xhr.responseType = xhr._responseType = handleAs;
|
| -
|
| - // Cache the JSON prefix, if it exists.
|
| - if (!!options.jsonPrefix) {
|
| - xhr._jsonPrefix = options.jsonPrefix;
|
| - }
|
| - }
|
| -
|
| - xhr.withCredentials = !!options.withCredentials;
|
| -
|
| -
|
| - var body = this._encodeBodyObject(options.body, headers['content-type']);
|
| -
|
| - xhr.send(
|
| - /** @type {ArrayBuffer|ArrayBufferView|Blob|Document|FormData|
|
| - null|string|undefined} */
|
| - (body));
|
| -
|
| - return this.completes;
|
| - },
|
| -
|
| - /**
|
| - * Attempts to parse the response body of the XHR. If parsing succeeds,
|
| - * the value returned will be deserialized based on the `responseType`
|
| - * set on the XHR.
|
| - *
|
| - * @return {*} The parsed response,
|
| - * or undefined if there was an empty response or parsing failed.
|
| - */
|
| - parseResponse: function () {
|
| - var xhr = this.xhr;
|
| - var responseType = xhr.responseType || xhr._responseType;
|
| - var preferResponseText = !this.xhr.responseType;
|
| - var prefixLen = (xhr._jsonPrefix && xhr._jsonPrefix.length) || 0;
|
| -
|
| - try {
|
| - switch (responseType) {
|
| - case 'json':
|
| - // If the xhr object doesn't have a natural `xhr.responseType`,
|
| - // we can assume that the browser hasn't parsed the response for us,
|
| - // and so parsing is our responsibility. Likewise if response is
|
| - // undefined, as there's no way to encode undefined in JSON.
|
| - if (preferResponseText || xhr.response === undefined) {
|
| - // Try to emulate the JSON section of the response body section of
|
| - // the spec: https://xhr.spec.whatwg.org/#response-body
|
| - // That is to say, we try to parse as JSON, but if anything goes
|
| - // wrong return null.
|
| - try {
|
| - return JSON.parse(xhr.responseText);
|
| - } catch (_) {
|
| - return null;
|
| - }
|
| - }
|
| -
|
| - return xhr.response;
|
| - case 'xml':
|
| - return xhr.responseXML;
|
| - case 'blob':
|
| - case 'document':
|
| - case 'arraybuffer':
|
| - return xhr.response;
|
| - case 'text':
|
| - default: {
|
| - // If `prefixLen` is set, it implies the response should be parsed
|
| - // as JSON once the prefix of length `prefixLen` is stripped from
|
| - // it. Emulate the behavior above where null is returned on failure
|
| - // to parse.
|
| - if (prefixLen) {
|
| - try {
|
| - return JSON.parse(xhr.responseText.substring(prefixLen));
|
| - } catch (_) {
|
| - return null;
|
| - }
|
| - }
|
| - return xhr.responseText;
|
| + url = url + "?" + sk.query.fromParamSet(params);
|
| + sk.request("GET", url, "", headers).then(JSON.parse).then(function(json){
|
| + this.set(bindTo, json);
|
| + if (busy) {
|
| + this.set(busy, false);
|
| }
|
| - }
|
| - } catch (e) {
|
| - this.rejectCompletes(new Error('Could not parse response. ' + e.message));
|
| - }
|
| - },
|
| -
|
| - /**
|
| - * Aborts the request.
|
| - */
|
| - abort: function () {
|
| - this._setAborted(true);
|
| - this.xhr.abort();
|
| - },
|
| -
|
| - /**
|
| - * @param {*} body The given body of the request to try and encode.
|
| - * @param {?string} contentType The given content type, to infer an encoding
|
| - * from.
|
| - * @return {*} Either the encoded body as a string, if successful,
|
| - * or the unaltered body object if no encoding could be inferred.
|
| - */
|
| - _encodeBodyObject: function(body, contentType) {
|
| - if (typeof body == 'string') {
|
| - return body; // Already encoded.
|
| - }
|
| - var bodyObj = /** @type {Object} */ (body);
|
| - switch(contentType) {
|
| - case('application/json'):
|
| - return JSON.stringify(bodyObj);
|
| - case('application/x-www-form-urlencoded'):
|
| - return this._wwwFormUrlEncode(bodyObj);
|
| - }
|
| - return body;
|
| - },
|
| -
|
| - /**
|
| - * @param {Object} object The object to encode as x-www-form-urlencoded.
|
| - * @return {string} .
|
| - */
|
| - _wwwFormUrlEncode: function(object) {
|
| - if (!object) {
|
| - return '';
|
| - }
|
| - var pieces = [];
|
| - Object.keys(object).forEach(function(key) {
|
| - // TODO(rictic): handle array values here, in a consistent way with
|
| - // iron-ajax params.
|
| - pieces.push(
|
| - this._wwwFormUrlEncodePiece(key) + '=' +
|
| - this._wwwFormUrlEncodePiece(object[key]));
|
| - }, this);
|
| - return pieces.join('&');
|
| - },
|
| -
|
| - /**
|
| - * @param {*} str A key or value to encode as x-www-form-urlencoded.
|
| - * @return {string} .
|
| - */
|
| - _wwwFormUrlEncodePiece: function(str) {
|
| - // Spec says to normalize newlines to \r\n and replace %20 spaces with +.
|
| - // jQuery does this as well, so this is likely to be widely compatible.
|
| - if (str === null) {
|
| - return '';
|
| - }
|
| - return encodeURIComponent(str.toString().replace(/\r?\n/g, '\r\n'))
|
| - .replace(/%20/g, '+');
|
| - },
|
| -
|
| - /**
|
| - * Updates the status code and status text.
|
| - */
|
| - _updateStatus: function() {
|
| - this._setStatus(this.xhr.status);
|
| - this._setStatusText((this.xhr.statusText === undefined) ? '' : this.xhr.statusText);
|
| - }
|
| - });
|
| -</script>
|
| -
|
| -<script>
|
| - 'use strict';
|
| -
|
| - Polymer({
|
| -
|
| - is: 'iron-ajax',
|
| -
|
| - /**
|
| - * Fired when a request is sent.
|
| - *
|
| - * @event request
|
| - * @event iron-ajax-request
|
| - */
|
| -
|
| - /**
|
| - * Fired when a response is received.
|
| - *
|
| - * @event response
|
| - * @event iron-ajax-response
|
| - */
|
| -
|
| - /**
|
| - * Fired when an error is received.
|
| - *
|
| - * @event error
|
| - * @event iron-ajax-error
|
| - */
|
| -
|
| - hostAttributes: {
|
| - hidden: true
|
| - },
|
| -
|
| - properties: {
|
| - /**
|
| - * The URL target of the request.
|
| - */
|
| - url: {
|
| - type: String
|
| - },
|
| -
|
| - /**
|
| - * An object that contains query parameters to be appended to the
|
| - * specified `url` when generating a request. If you wish to set the body
|
| - * content when making a POST request, you should use the `body` property
|
| - * instead.
|
| - */
|
| - params: {
|
| - type: Object,
|
| - value: function() {
|
| - return {};
|
| - }
|
| - },
|
| -
|
| - /**
|
| - * The HTTP method to use such as 'GET', 'POST', 'PUT', or 'DELETE'.
|
| - * Default is 'GET'.
|
| - */
|
| - method: {
|
| - type: String,
|
| - value: 'GET'
|
| - },
|
| -
|
| - /**
|
| - * HTTP request headers to send.
|
| - *
|
| - * Example:
|
| - *
|
| - * <iron-ajax
|
| - * auto
|
| - * url="http://somesite.com"
|
| - * headers='{"X-Requested-With": "XMLHttpRequest"}'
|
| - * handle-as="json"></iron-ajax>
|
| - *
|
| - * Note: setting a `Content-Type` header here will override the value
|
| - * specified by the `contentType` property of this element.
|
| - */
|
| - headers: {
|
| - type: Object,
|
| - value: function() {
|
| - return {};
|
| - }
|
| - },
|
| -
|
| - /**
|
| - * Content type to use when sending data. If the `contentType` property
|
| - * is set and a `Content-Type` header is specified in the `headers`
|
| - * property, the `headers` property value will take precedence.
|
| - *
|
| - * Varies the handling of the `body` param.
|
| - */
|
| - contentType: {
|
| - type: String,
|
| - value: null
|
| - },
|
| -
|
| - /**
|
| - * Body content to send with the request, typically used with "POST"
|
| - * requests.
|
| - *
|
| - * If body is a string it will be sent unmodified.
|
| - *
|
| - * If Content-Type is set to a value listed below, then
|
| - * the body will be encoded accordingly.
|
| - *
|
| - * * `content-type="application/json"`
|
| - * * body is encoded like `{"foo":"bar baz","x":1}`
|
| - * * `content-type="application/x-www-form-urlencoded"`
|
| - * * body is encoded like `foo=bar+baz&x=1`
|
| - *
|
| - * Otherwise the body will be passed to the browser unmodified, and it
|
| - * will handle any encoding (e.g. for FormData, Blob, ArrayBuffer).
|
| - *
|
| - * @type (ArrayBuffer|ArrayBufferView|Blob|Document|FormData|null|string|undefined|Object)
|
| - */
|
| - body: {
|
| - type: Object,
|
| - value: null
|
| - },
|
| -
|
| - /**
|
| - * Toggle whether XHR is synchronous or asynchronous. Don't change this
|
| - * to true unless You Know What You Are Doing™.
|
| - */
|
| - sync: {
|
| - type: Boolean,
|
| - value: false
|
| - },
|
| -
|
| - /**
|
| - * Specifies what data to store in the `response` property, and
|
| - * to deliver as `event.detail.response` in `response` events.
|
| - *
|
| - * One of:
|
| - *
|
| - * `text`: uses `XHR.responseText`.
|
| - *
|
| - * `xml`: uses `XHR.responseXML`.
|
| - *
|
| - * `json`: uses `XHR.responseText` parsed as JSON.
|
| - *
|
| - * `arraybuffer`: uses `XHR.response`.
|
| - *
|
| - * `blob`: uses `XHR.response`.
|
| - *
|
| - * `document`: uses `XHR.response`.
|
| - */
|
| - handleAs: {
|
| - type: String,
|
| - value: 'json'
|
| - },
|
| -
|
| - /**
|
| - * Set the withCredentials flag on the request.
|
| - */
|
| - withCredentials: {
|
| - type: Boolean,
|
| - value: false
|
| - },
|
| -
|
| - /**
|
| - * Set the timeout flag on the request.
|
| - */
|
| - timeout: {
|
| - type: Number,
|
| - value: 0
|
| - },
|
| -
|
| - /**
|
| - * If true, automatically performs an Ajax request when either `url` or
|
| - * `params` changes.
|
| - */
|
| - auto: {
|
| - type: Boolean,
|
| - value: false
|
| - },
|
| -
|
| - /**
|
| - * If true, error messages will automatically be logged to the console.
|
| - */
|
| - verbose: {
|
| - type: Boolean,
|
| - value: false
|
| - },
|
| -
|
| - /**
|
| - * The most recent request made by this iron-ajax element.
|
| - */
|
| - lastRequest: {
|
| - type: Object,
|
| - notify: true,
|
| - readOnly: true
|
| - },
|
| -
|
| - /**
|
| - * True while lastRequest is in flight.
|
| - */
|
| - loading: {
|
| - type: Boolean,
|
| - notify: true,
|
| - readOnly: true
|
| - },
|
| -
|
| - /**
|
| - * lastRequest's response.
|
| - *
|
| - * Note that lastResponse and lastError are set when lastRequest finishes,
|
| - * so if loading is true, then lastResponse and lastError will correspond
|
| - * to the result of the previous request.
|
| - *
|
| - * The type of the response is determined by the value of `handleAs` at
|
| - * the time that the request was generated.
|
| - *
|
| - * @type {Object}
|
| - */
|
| - lastResponse: {
|
| - type: Object,
|
| - notify: true,
|
| - readOnly: true
|
| + }.bind(this)).catch(function(reason){
|
| + console.log("Reason for failure of request to " + url, reason);
|
| + this.set(bindTo, false);
|
| + if (busy) {
|
| + this.set(busy, false);
|
| + }
|
| + }.bind(this));
|
| },
|
|
|
| - /**
|
| - * lastRequest's error, if any.
|
| - *
|
| - * @type {Object}
|
| - */
|
| - lastError: {
|
| - type: Object,
|
| - notify: true,
|
| - readOnly: true
|
| + _not: function(a) {
|
| + return !a;
|
| },
|
|
|
| - /**
|
| - * An Array of all in-flight requests originating from this iron-ajax
|
| - * element.
|
| - */
|
| - activeRequests: {
|
| - type: Array,
|
| - notify: true,
|
| - readOnly: true,
|
| - value: function() {
|
| - return [];
|
| + _or: function() {
|
| + var result = false;
|
| + // can't use .foreach, as arguments isn't really an Array.
|
| + for (var i = 0; i < arguments.length; i++) {
|
| + result = result || arguments[i];
|
| }
|
| + return result;
|
| },
|
| -
|
| - /**
|
| - * Length of time in milliseconds to debounce multiple automatically generated requests.
|
| - */
|
| - debounceDuration: {
|
| - type: Number,
|
| - value: 0,
|
| - notify: true
|
| - },
|
| -
|
| - /**
|
| - * Prefix to be stripped from a JSON response before parsing it.
|
| - *
|
| - * In order to prevent an attack using CSRF with Array responses
|
| - * (http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx/)
|
| - * many backends will mitigate this by prefixing all JSON response bodies
|
| - * with a string that would be nonsensical to a JavaScript parser.
|
| - *
|
| - */
|
| - jsonPrefix: {
|
| - type: String,
|
| - value: ''
|
| - },
|
| -
|
| - /**
|
| - * By default, iron-ajax's events do not bubble. Setting this attribute will cause its
|
| - * request and response events as well as its iron-ajax-request, -response, and -error
|
| - * events to bubble to the window object. The vanilla error event never bubbles when
|
| - * using shadow dom even if this.bubbles is true because a scoped flag is not passed with
|
| - * it (first link) and because the shadow dom spec did not used to allow certain events,
|
| - * including events named error, to leak outside of shadow trees (second link).
|
| - * https://www.w3.org/TR/shadow-dom/#scoped-flag
|
| - * https://www.w3.org/TR/2015/WD-shadow-dom-20151215/#events-that-are-not-leaked-into-ancestor-trees
|
| - */
|
| - bubbles: {
|
| - type: Boolean,
|
| - value: false
|
| - },
|
| -
|
| - _boundHandleResponse: {
|
| - type: Function,
|
| - value: function() {
|
| - return this._handleResponse.bind(this);
|
| - }
|
| - }
|
| - },
|
| -
|
| - observers: [
|
| - '_requestOptionsChanged(url, method, params.*, headers, contentType, ' +
|
| - 'body, sync, handleAs, jsonPrefix, withCredentials, timeout, auto)'
|
| - ],
|
| -
|
| - /**
|
| - * The query string that should be appended to the `url`, serialized from
|
| - * the current value of `params`.
|
| - *
|
| - * @return {string}
|
| - */
|
| - get queryString () {
|
| - var queryParts = [];
|
| - var param;
|
| - var value;
|
| -
|
| - for (param in this.params) {
|
| - value = this.params[param];
|
| - param = window.encodeURIComponent(param);
|
| -
|
| - if (Array.isArray(value)) {
|
| - for (var i = 0; i < value.length; i++) {
|
| - queryParts.push(param + '=' + window.encodeURIComponent(value[i]));
|
| - }
|
| - } else if (value !== null) {
|
| - queryParts.push(param + '=' + window.encodeURIComponent(value));
|
| - } else {
|
| - queryParts.push(param);
|
| - }
|
| - }
|
| -
|
| - return queryParts.join('&');
|
| - },
|
| -
|
| - /**
|
| - * The `url` with query string (if `params` are specified), suitable for
|
| - * providing to an `iron-request` instance.
|
| - *
|
| - * @return {string}
|
| - */
|
| - get requestUrl() {
|
| - var queryString = this.queryString;
|
| - var url = this.url || '';
|
| -
|
| - if (queryString) {
|
| - var bindingChar = url.indexOf('?') >= 0 ? '&' : '?';
|
| - return url + bindingChar + queryString;
|
| - }
|
| -
|
| - return url;
|
| - },
|
| -
|
| - /**
|
| - * An object that maps header names to header values, first applying the
|
| - * the value of `Content-Type` and then overlaying the headers specified
|
| - * in the `headers` property.
|
| - *
|
| - * @return {Object}
|
| - */
|
| - get requestHeaders() {
|
| - var headers = {};
|
| - var contentType = this.contentType;
|
| - if (contentType == null && (typeof this.body === 'string')) {
|
| - contentType = 'application/x-www-form-urlencoded';
|
| - }
|
| - if (contentType) {
|
| - headers['content-type'] = contentType;
|
| - }
|
| - var header;
|
| -
|
| - if (this.headers instanceof Object) {
|
| - for (header in this.headers) {
|
| - headers[header] = this.headers[header].toString();
|
| - }
|
| - }
|
| -
|
| - return headers;
|
| - },
|
| -
|
| - /**
|
| - * Request options suitable for generating an `iron-request` instance based
|
| - * on the current state of the `iron-ajax` instance's properties.
|
| - *
|
| - * @return {{
|
| - * url: string,
|
| - * method: (string|undefined),
|
| - * async: (boolean|undefined),
|
| - * body: (ArrayBuffer|ArrayBufferView|Blob|Document|FormData|null|string|undefined|Object),
|
| - * headers: (Object|undefined),
|
| - * handleAs: (string|undefined),
|
| - * jsonPrefix: (string|undefined),
|
| - * withCredentials: (boolean|undefined)}}
|
| - */
|
| - toRequestOptions: function() {
|
| - return {
|
| - url: this.requestUrl || '',
|
| - method: this.method,
|
| - headers: this.requestHeaders,
|
| - body: this.body,
|
| - async: !this.sync,
|
| - handleAs: this.handleAs,
|
| - jsonPrefix: this.jsonPrefix,
|
| - withCredentials: this.withCredentials,
|
| - timeout: this.timeout
|
| - };
|
| - },
|
| -
|
| - /**
|
| - * Performs an AJAX request to the specified URL.
|
| - *
|
| - * @return {!IronRequestElement}
|
| - */
|
| - generateRequest: function() {
|
| - var request = /** @type {!IronRequestElement} */ (document.createElement('iron-request'));
|
| - var requestOptions = this.toRequestOptions();
|
| -
|
| - this.push('activeRequests', request);
|
| -
|
| - request.completes.then(
|
| - this._boundHandleResponse
|
| - ).catch(
|
| - this._handleError.bind(this, request)
|
| - ).then(
|
| - this._discardRequest.bind(this, request)
|
| - );
|
| -
|
| - request.send(requestOptions);
|
| -
|
| - this._setLastRequest(request);
|
| - this._setLoading(true);
|
| -
|
| - this.fire('request', {
|
| - request: request,
|
| - options: requestOptions
|
| - }, {bubbles: this.bubbles});
|
| -
|
| - this.fire('iron-ajax-request', {
|
| - request: request,
|
| - options: requestOptions
|
| - }, {bubbles: this.bubbles});
|
| -
|
| - return request;
|
| - },
|
| -
|
| - _handleResponse: function(request) {
|
| - if (request === this.lastRequest) {
|
| - this._setLastResponse(request.response);
|
| - this._setLastError(null);
|
| - this._setLoading(false);
|
| - }
|
| - this.fire('response', request, {bubbles: this.bubbles});
|
| - this.fire('iron-ajax-response', request, {bubbles: this.bubbles});
|
| - },
|
| -
|
| - _handleError: function(request, error) {
|
| - if (this.verbose) {
|
| - Polymer.Base._error(error);
|
| - }
|
| -
|
| - if (request === this.lastRequest) {
|
| - this._setLastError({
|
| - request: request,
|
| - error: error,
|
| - status: request.xhr.status,
|
| - statusText: request.xhr.statusText,
|
| - response: request.xhr.response
|
| - });
|
| - this._setLastResponse(null);
|
| - this._setLoading(false);
|
| - }
|
| -
|
| - // Tests fail if this goes after the normal this.fire('error', ...)
|
| - this.fire('iron-ajax-error', {
|
| - request: request,
|
| - error: error
|
| - }, {bubbles: this.bubbles});
|
| -
|
| - this.fire('error', {
|
| - request: request,
|
| - error: error
|
| - }, {bubbles: this.bubbles});
|
| - },
|
| -
|
| - _discardRequest: function(request) {
|
| - var requestIndex = this.activeRequests.indexOf(request);
|
| -
|
| - if (requestIndex > -1) {
|
| - this.splice('activeRequests', requestIndex, 1);
|
| - }
|
| - },
|
| -
|
| - _requestOptionsChanged: function() {
|
| - this.debounce('generate-request', function() {
|
| - if (this.url == null) {
|
| - return;
|
| - }
|
| -
|
| - if (this.auto) {
|
| - this.generateRequest();
|
| - }
|
| - }, this.debounceDuration);
|
| - },
|
| -
|
| - });
|
| + };
|
| + })();
|
| </script>
|
| -
|
| -
|
| <script>
|
| Polymer.AppLayout = Polymer.AppLayout || {};
|
|
|
| @@ -14578,7 +13576,7 @@ You can bind to `isAuthorized` property to monitor authorization state.
|
| <app-header fixed="">
|
| <app-toolbar>
|
| <div class="title left">[[name]]</div>
|
| - <paper-spinner-lite class="left" active="[[busy]]"></paper-spinner-lite>
|
| + <paper-spinner-lite class="left" active="[[_or(busy,_busy)]]"></paper-spinner-lite>
|
|
|
| <a class="left" href="/newui/">Home</a>
|
| <a class="left" href="/newui/botlist">Bot List</a>
|
| @@ -14597,36 +13595,55 @@ You can bind to `isAuthorized` property to monitor authorization state.
|
| <script>
|
| Polymer({
|
| is: 'swarming-app',
|
| +
|
| + behaviors: [
|
| + SwarmingBehaviors.CommonBehavior,
|
| + ],
|
| +
|
| properties: {
|
| + // input
|
| + busy: {
|
| + type: Boolean,
|
| + },
|
| client_id: {
|
| type: String,
|
| },
|
| + name: {
|
| + type: String,
|
| + },
|
| + // outputs
|
| auth_headers: {
|
| type: Object,
|
| notify: true,
|
| + observer: "_loadPermissions"
|
| },
|
| +
|
| + permissions: {
|
| + type: Object,
|
| + value: function() {
|
| + // If we aren't logged in, default to no permissions.
|
| + return {};
|
| + },
|
| + notify: true,
|
| + },
|
| +
|
| signed_in: {
|
| type: Boolean,
|
| value: false,
|
| notify:true,
|
| },
|
| - permissions: {
|
| - type: Object,
|
| - value: function() {
|
| - // TODO(kjlubick): Make this call the whoami endpoint after signing in.
|
| - return {
|
| - can_cancel_task: true,
|
| - }
|
| - },
|
| - notify: true,
|
| - },
|
|
|
| - busy: {
|
| + // private
|
| + _busy: {
|
| type: Boolean,
|
| - },
|
| - name: {
|
| - type: String,
|
| - },
|
| + value: false,
|
| + }
|
| +
|
| + },
|
| +
|
| + _loadPermissions: function() {
|
| + this._getJsonAsync("permissions", "/_ah/api/swarming/v1/server/permissions",
|
| + "_busy", this.auth_headers);
|
| },
|
|
|
| });
|
| @@ -14647,17 +13664,81 @@ You can bind to `isAuthorized` property to monitor authorization state.
|
| <dom-module id="swarming-index" assetpath="/res/imp/index/">
|
| <template>
|
| <style include="swarming-app-style">
|
| -
|
| + .command {
|
| + font-family: monospace;
|
| + margin-bottom: 10px;
|
| + margin-top: 10px;
|
| + white-space: pre-wrap;
|
| + background-color: #f5f5f5;
|
| + border: 1px solid #ccc;
|
| + border-radius: 4px;
|
| + }
|
| </style>
|
|
|
| - <swarming-app client_id="[[client_id]]" auth_headers="{{auth_headers}}" name="Swarming" busy="[[busy]]">
|
| -
|
| - <iron-ajax id="request" url="/_ah/api/swarming/v1/server/details" headers="[[auth_headers]]" handle-as="json" last-response="{{serverDetails}}" loading="{{busy}}">
|
| - </iron-ajax>
|
| -
|
| - <h1>HELLO WORLD</h1>
|
| + <swarming-app client_id="[[client_id]]" auth_headers="{{auth_headers}}" permissions="{{_permissions}}" name="Swarming Server" busy="[[_busy]]">
|
|
|
| + <h2>Service Status</h2>
|
| <div>Server Version: [[serverDetails.server_version]]</div>
|
| + <ul>
|
| + <li>
|
| +
|
| + <a href="/stats">Usage statistics</a>
|
| + </li>
|
| + <li>
|
| + <a href="/restricted/mapreduce/status">Map Reduce Jobs</a>
|
| + </li>
|
| + <li>
|
| + <a href$="[[_makeInstancesUrl(serverDetails,_project_id)]]">View version's instances on Cloud Console</a>
|
| + </li>
|
| + <li>
|
| + <a></a><a href$="[[_makeErrorUrl(_project_id)]]">View server errors on Cloud Console</a>
|
| + </li>
|
| + <li>
|
| + <a></a><a href$="[[_makeLogUrl(_project_id)]]">View logs for HTTP 5xx on Cloud Console</a>
|
| + </li>
|
| + </ul>
|
| +
|
| + <h2>Configuration</h2>
|
| + <ul>
|
| +
|
| + <li>
|
| + <a href="/restricted/config">View server config</a>
|
| + </li>
|
| + <li>
|
| + <a href="/restricted/upload/bootstrap">View/upload bootstrap.py</a>
|
| + </li>
|
| + <li>
|
| + <a href="/restricted/upload/bot_config">View/upload bot_config.py</a>
|
| + </li>
|
| + <li>
|
| + <a href="/auth/groups">View/edit user groups</a>
|
| + </li>
|
| + </ul>
|
| +
|
| + <div hidden$="[[_cannotBootstrap(_permissions)]]">
|
| + <h2>Bootstrapping a bot</h2>
|
| + To bootstrap a bot, run one of these (all links are valid for 1 hour):
|
| + <ol>
|
| + <li>
|
| + <strong> TL;DR; </strong>
|
| + <pre class="command">python -c "import urllib; exec urllib.urlopen('[[_host_url]]/bootstrap?tok=[[_bootstrap_token]]').read()"</pre>
|
| + </li>
|
| + <li>
|
| + Escaped version to pass as a ssh argument:
|
| + <pre class="command">'python -c "import urllib; exec urllib.urlopen('"'[[_host_url]]/bootstrap?tok=[[_bootstrap_token]]'"').read()"'</pre>
|
| + </li>
|
| + <li>
|
| + Manually:
|
| + <pre class="command" style="margin-bottom:0">mkdir bot; cd bot
|
| + rm -f swarming_bot.zip; curl -sSLOJ [[_host_url]]/bot_code?tok=[[_bootstrap_token]]
|
| + python swarming_bot.zip</pre>
|
| + </li>
|
| + </ol>
|
| + </div>
|
| +
|
| + <h2>Stats</h2>
|
| + <div>TODO(kjlubick) add these in</div>
|
| +
|
|
|
| </swarming-app>
|
|
|
| @@ -14666,6 +13747,10 @@ You can bind to `isAuthorized` property to monitor authorization state.
|
| Polymer({
|
| is: 'swarming-index',
|
|
|
| + behaviors: [
|
| + SwarmingBehaviors.CommonBehavior,
|
| + ],
|
| +
|
| properties: {
|
| client_id: {
|
| type: String,
|
| @@ -14677,70 +13762,63 @@ You can bind to `isAuthorized` property to monitor authorization state.
|
| },
|
|
|
| serverDetails: {
|
| + type: Object,
|
| + },
|
| +
|
| + _bootstrap_token: {
|
| type: String,
|
| + // TODO(kjlubick): fetch this from an API
|
| + value: "abc123",
|
| + },
|
| + _busy: {
|
| + type: Boolean,
|
| + value: false,
|
| + },
|
| + _host_url: {
|
| + type: String,
|
| + value: function() {
|
| + return location.origin;
|
| + },
|
| + },
|
| + _permissions: {
|
| + type: Object,
|
| + },
|
| + _project_id: {
|
| + type: String,
|
| + value: function() {
|
| + var idx = location.hostname.indexOf(".appspot.com");
|
| + return location.hostname.substring(0, idx);
|
| + },
|
| }
|
| },
|
|
|
| signIn: function(){
|
| - this.$.request.generateRequest();
|
| + this._getJsonAsync("serverDetails", "/_ah/api/swarming/v1/server/details",
|
| + "_busy", this.auth_headers);
|
| + },
|
| +
|
| + _cannotBootstrap: function(permissions) {
|
| + return !(permissions && permissions.get_bootstrap_token);
|
| + },
|
| +
|
| + _makeInstancesUrl: function(details, project_id) {
|
| + return "https://console.cloud.google.com/appengine/instances?project="+
|
| + project_id+"&versionId="+details.server_version;
|
| + },
|
| +
|
| + _makeErrorUrl: function(project_id) {
|
| + return "https://console.cloud.google.com/errors?project="+
|
| + project_id;
|
| + },
|
| +
|
| + _makeLogUrl: function(project_id) {
|
| + return "https://pantheon.corp.google.com/logs/viewer?filters=text:status:500..599&project="+
|
| + project_id;
|
| },
|
|
|
| });
|
| </script>
|
| -</dom-module><script>
|
| - window.SwarmingBehaviors = window.SwarmingBehaviors || {};
|
| - (function(){
|
| - // This behavior wraps up all the shared swarming functionality.
|
| - SwarmingBehaviors.CommonBehavior = {
|
| -
|
| - // _getJsonAsync makes an XHR to a url, parses the response as JSON
|
| - // and sticks the resulting object into the property with the name given
|
| - // by "bindTo". If busy is defined, the property with that name will be
|
| - // set to true while the request is in flight and false afterwards.
|
| - // request headers (e.g. authentication) and query params will be used if
|
| - // provided. Query params is an object like {String:Array<String>}. On
|
| - // error, bindTo will be set to false. It is not set to undefined
|
| - // because computed values in Polymer don't fire if a property is
|
| - // undefined. Clients should check that bindTo is not falsey.
|
| - _getJsonAsync: function(bindTo, url, busy, headers, params) {
|
| - if (!bindTo || !url) {
|
| - console.log("Need at least a polymer element to bind to and a url");
|
| - return;
|
| - }
|
| - if (busy) {
|
| - this.set(busy, true);
|
| - }
|
| - url = url + "?" + sk.query.fromParamSet(params);
|
| - sk.request("GET", url, "", headers).then(JSON.parse).then(function(json){
|
| - this.set(bindTo, json);
|
| - if (busy) {
|
| - this.set(busy, false);
|
| - }
|
| - }.bind(this)).catch(function(reason){
|
| - console.log("Reason for failure of request to " + url, reason);
|
| - this.set(bindTo, false);
|
| - if (busy) {
|
| - this.set(busy, false);
|
| - }
|
| - }.bind(this));
|
| - },
|
| -
|
| - _not: function(a) {
|
| - return !a;
|
| - },
|
| -
|
| - _or: function() {
|
| - var result = false;
|
| - // can't use .foreach, as arguments isn't really an Array.
|
| - for (var i = 0; i < arguments.length; i++) {
|
| - result = result || arguments[i];
|
| - }
|
| - return result;
|
| - },
|
| - };
|
| - })();
|
| -</script>
|
| -<dom-module id="dynamic-table-style" assetpath="/res/imp/common/">
|
| +</dom-module><dom-module id="dynamic-table-style" assetpath="/res/imp/common/">
|
| <template>
|
| <style>
|
| table {
|
| @@ -25232,7 +24310,7 @@ the fleet.">
|
| },
|
|
|
| _cannotCancel: function(task, permissions) {
|
| - return !(permissions && permissions.can_cancel_task &&
|
| + return !(permissions && permissions.cancel_task &&
|
| this._column("state", task) === "PENDING");
|
| },
|
|
|
|
|