Implicitly depend on cr-buildbucket-client without copying files

Bug: webrtc:9205
Change-Id: I2119f750d1eed6ce30c83fa3da0689dde3c88ba8
diff --git a/static/binary-size-view.html b/static/binary-size-view.html
index de6a220..a264086 100644
--- a/static/binary-size-view.html
+++ b/static/binary-size-view.html
@@ -3,7 +3,7 @@
 Use of this source code is governed by a BSD-style license that can be
 found in the LICENSE file.
 -->
-<link rel="import" href="./cr-buildbucket-client.html">
+<link rel="import" href="../../buildbucket/static/cr-buildbucket-client.html">
 
 <dom-module id="binary-size-view">
   <template>
diff --git a/static/binary-size-view.js b/static/binary-size-view.js
index aa993ab..3c03678 100644
--- a/static/binary-size-view.js
+++ b/static/binary-size-view.js
@@ -104,6 +104,10 @@
       if (!this._pluginConfig) {
         return;
       }
+      if (!this.$.client || !this.$.client.searchBuilds) {
+        console.error('The "binary-size" plugin requires the "buildbucket"' +
+                      ' plugin for searching builds. Please activate both.');
+      }
 
       const newBinarySizeInfo = await this._tryGetNewBinarySizeInfo();
       if (newBinarySizeInfo != null) {
diff --git a/static/cr-buildbucket-client.html b/static/cr-buildbucket-client.html
deleted file mode 100644
index f8e0d23..0000000
--- a/static/cr-buildbucket-client.html
+++ /dev/null
@@ -1,9 +0,0 @@
-<!--
-Copyright 2017 The Chromium Authors. All rights reserved.
-Use of this source code is governed by a BSD-style license that can be
-found in the LICENSE file.
--->
-<script src="https://apis.google.com/js/platform.js"></script>
-<dom-module id="cr-buildbucket-client">
-  <script src="cr-buildbucket-client.js"></script>
-</dom-module>
diff --git a/static/cr-buildbucket-client.js b/static/cr-buildbucket-client.js
deleted file mode 100644
index b7fdef1..0000000
--- a/static/cr-buildbucket-client.js
+++ /dev/null
@@ -1,323 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-(function() {
-  'use strict';
-
-  const EMAIL_SCOPE = 'email';
-
-  Polymer({
-    is: 'cr-buildbucket-client',
-
-    /**
-     * Compare build IDs by creation time.
-     *
-     * Build IDs are monotonically decreasing.
-     *
-     * A buildbucket build ID is a positive int64. This is too large for
-     * JavaScript numbers; we cannot just parse them to integers, so we
-     * keep them as strings.
-     *
-     * @param {string} a
-     * @param {string} b
-     * @return {number} Negative if `a` should go before `b`,
-     *     0 if they're equal, and postive if `a` should go after `b`.
-     */
-    compareIds(a, b) {
-      const d = b.length - a.length;
-      if (d != 0) {
-        return d;
-      }
-      if (a > b) {
-        return -1;
-      }
-      if (a < b) {
-        return 1;
-      }
-      return 0;
-    },
-
-    /**
-     * Fired when the auth state changes.
-     *
-     * @event auth-change
-     * @param {boolean} loggedIn Whether the OAuth flow successfully completed.
-     */
-
-    properties: {
-      baseUrl: String,
-      plugin: Object,
-
-      // The auth state can be re-used for multiple instances of this
-      // element on the same page, e.g. for the instances in both
-      // cr-tryjob-picker and cr-buildbucket-view.
-      _sharedAuthState: {
-        type: Object,
-        value: {
-          oauth_client_id: null,
-          oauth_configured: false,
-          oauth_email: null,
-          oauth_token: null,
-        },
-      },
-    },
-
-    /**
-     * Fetch and store a new OAuth token if necessary.
-     *
-     * @return {Promise} Resolves when finished.
-     */
-    async getOAuthToken() {
-      if (this._sharedAuthState.oauth_token) {
-        return;
-      }
-
-      await this._configureOAuthLibrary();
-      try {
-        const token = await this._refreshToken();
-        if (!this._oauthTokenIsValid(token)) {
-          throw 'Received an invalid token';
-        }
-        this._sharedAuthState.oauth_token = token;
-        this.fire('auth-change', {loggedIn: true});
-      } catch (err) {
-        console.warn('Failed to get refresh token; error:', err);
-        this.fire('auth-change', {loggedIn: false});
-      }
-    },
-
-    /**
-     * Fetch the config object for this project.
-     *
-     * @param {string} project Gerrit project name.
-     * @param {string} plugin Plugin name.
-     * @return {Promise} Resolves to the fetched config object.
-     */
-    async getConfig(project, plugin) {
-      return await this.plugin.restApi().get(
-          `/projects/${encodeURIComponent(project)}/` +
-          `${encodeURIComponent(plugin)}~config`);
-    },
-
-    /**
-     * Search for Buildbucket builds given a criteria.
-     *
-     * @param {Object|Array} query Build search query.
-     * See https://cr-buildbucket.appspot.com/_ah/api/explorer/#p/buildbucket/v1/buildbucket.search
-     * for possible keys.
-     *
-     * If there are multiple values for the same parameter, pass an array
-     * of arrays where each subarray is exactly two elements: param name and
-     * value. Example: [["tag", "a:b"], ["tag", "c:d"]].
-     * @return {Promise} Resolves to an Array of builds.
-     */
-    async searchBuilds(query) {
-      const path = '_ah/api/buildbucket/v1/search';
-      const q = new URLSearchParams(query);
-      const res = await this._fetch('GET', path, q);
-      return res.builds || [];
-    },
-
-    /**
-     * Schedule builds.
-     *
-     * @param {Object[]} buildRequests An array of build requests.
-     * @return {Promise} Resolves to an array of results, in the order of
-     * buildRequests. Each result has either build or error property.
-     */
-    async scheduleBuilds(buildRequests) {
-      if (buildRequests.length == 0) {
-        return [];
-      }
-      const path = '_ah/api/buildbucket/v1/builds/batch';
-      const req = {builds: buildRequests};
-      const res = await this._fetch('PUT', path, null, req);
-      return res.results || [];
-    },
-
-    /**
-     * Make a Buildbucket request.
-     *
-     * @param {string} method HTTP method.
-     * @param {Object} path URL path relative to this.baseURL.
-     * @param {string|URLSearchParams} [query=null] URL query.
-     * Either a falsy or a value with toString() method, e.g. a string or a
-     * URLSearchParams.
-     * @param {Object} [body=null] Request body for non-GET requests.
-     * @return {Promise} Resolves to parsed JSON response body.
-     */
-    async _fetch(method, path, query = null, body = null) {
-      if (path.startsWith('/')) {
-        throw new Error('path parameter must be relative');
-      }
-
-      const baseUrl = this.baseUrl.replace(/\/^/, '');
-      const qs = query ? query.toString() : '';
-      const url = `${baseUrl}/${path}?${qs}`;
-
-      const headers = new Headers({
-        'Accept': 'application/json',
-
-        // TODO(nodir): include version information crbug.com/gerrit/8791
-        'User-Agent': 'Gerrit-Buildbucket plugin',
-      });
-
-      const accessToken = this._getSharedAccessToken();
-      if (accessToken) {
-        headers.set('Authorization', 'Bearer ' + accessToken);
-      } else {
-        console.log(`Making request with no bearer token to: ${url}`);
-      }
-
-      if (body) {
-        headers.set('Content-Type', 'application/json');
-      }
-
-      const opts = {
-        method,
-        headers,
-        body: body ? JSON.stringify(body) : null,
-      };
-      const response = await fetch(url, opts);
-      if (!response.ok) {
-        console.warn(
-            `Buildbucket HTTP ${response.status}: ${method} ${path}`, query);
-        const text = await response.text();
-        let reason = null;
-        try {
-          // Try to parse the response as JSON.
-          reason = JSON.parse(text);
-        } catch (ex) {
-          // Otherwise, reject with response text.
-          reason = text;
-        }
-        throw reason;
-      }
-
-      const resBody = response.json();
-      if (resBody.error) {
-        throw resBody.error;
-      }
-      return resBody;
-    },
-
-    _getSharedAccessToken() {
-      const token = this._sharedAuthState.oauth_token;
-      if (!token) { return null; }
-      return token.access_token;
-    },
-
-    /**
-     * Use the gapi library to configure OAuth if necessary.
-     *
-     * This involves fetching the OAuth config from Gerrit, then loading the
-     * gapi.auth library and initializing it. When completed, the state of this
-     * element is updated.
-     *
-     * @return {Promise} Resolves upon completion.
-     */
-    async _configureOAuthLibrary() {
-      if (this._sharedAuthState.oauth_configured) {
-        return;
-      }
-
-      let failed = false;
-      try {
-        const loggedIn = await this.plugin.restApi().getLoggedIn();
-        // No need to configure OAuth if no user is logged in, or if it's
-        // already configured.
-        if (!loggedIn) {
-          return;
-        }
-
-        // The gapi library shares state between clients, so if this library
-        // is used by another plugin, there may be unexpected results;
-        // thus log a warning so that we can easily check if this happens.
-        if (gapi.config && gapi.config.get()) {
-          console.warn('gapi config loaded twice, gapi.config.get() =>',
-                       gapi.config.get());
-        }
-        await new Promise((resolve) => {gapi.load('config_min', resolve);});
-        const config = await this._getOAuthConfig();
-
-        if (config.hasOwnProperty('auth_url') && config.auth_url) {
-          gapi.config.update('oauth-flow/authUrl', config.auth_url);
-        }
-        if (config.hasOwnProperty('proxy_url') && config.proxy_url) {
-          gapi.config.update('oauth-flow/proxyUrl', config.proxy_url);
-        }
-        this._sharedAuthState.oauth_client_id = config.client_id;
-        this._sharedAuthState.oauth_email = config.email;
-
-        // Loading auth has a side-effect. The URLs should be set before
-        // loading it.
-        await new Promise((resolve) => {
-          gapi.load('auth', () => { gapi.auth.init(resolve); });
-        });
-      } catch (ex) {
-        failed = true;
-        console.warn('Failed to configure oauth:', ex)
-      } finally {
-        this._sharedAuthState.oauth_configured = !failed;
-      }
-    },
-
-    /**
-     * Request OAuth config info from the Gerrit REST API.
-     *
-     * @return {Promise} Resolves to a token object.
-     */
-    async _getOAuthConfig() {
-      return await this.plugin.restApi().get('/accounts/self/oauthconfig');
-    },
-
-    /**
-     * Validate the given token object.
-     *
-     * @param {Object} token A token object, see https://goo.gl/HZH4Nq.
-     * @return {boolean} True if valid.
-     */
-    _oauthTokenIsValid(token) {
-      if (!token) { return false; }
-      if (!token.access_token || !token.expires_at) { return false; }
-
-      const expiration = new Date(parseInt(token.expires_at, 10) * 1000);
-      if (Date.now() >= expiration) { return false; }
-
-      return true;
-    },
-
-    /**
-     * Request a new OAuth token using gapi.
-     *
-     * This requires gapi.auth to be configured first with
-     * _configureOAuthLibrary().
-     *
-     * @param {Number} timeoutMs Milliseconds before timeout,
-     *     can be specified in unit tests.
-     * @return {Promise} Resolves to a token object, or rejects
-     *     if there was an error or no token, or a time-out.
-     */
-    async _refreshToken(timeoutMs = 1000) {
-      const opts = {
-        client_id: this._sharedAuthState.oauth_client_id,
-        immediate: true,
-        scope: EMAIL_SCOPE,
-        login_hint: this._sharedAuthState.oauth_email,
-      };
-      return await new Promise((resolve, reject) => {
-        const timeout = setTimeout(reject, timeoutMs);
-        gapi.auth.authorize(opts, (token) => {
-          clearTimeout(timeout);
-          if (!token) {
-            reject('No token returned');
-          } else if (token.error) {
-            reject(token.error);
-          } else {
-            resolve(token);
-          }
-        });
-      });
-    }
-  });
-})();
diff --git a/test/binary-size-view_test.html b/test/binary-size-view_test.html
index fb4dc0d..b4ce636 100644
--- a/test/binary-size-view_test.html
+++ b/test/binary-size-view_test.html
@@ -53,7 +53,11 @@
           _number: 1,
         },
       });
-      element.$.client.getOAuthToken = async () => {};
+      element.$.client = {
+          getOAuthToken: async () => null,
+          searchBuilds: async () => [],
+          compareIds: (a, b) => b - a,
+      }
       sandbox = sinon.sandbox.create();
     });
 
diff --git a/test/cr-buildbucket-client_test.html b/test/cr-buildbucket-client_test.html
deleted file mode 100644
index 77f0558..0000000
--- a/test/cr-buildbucket-client_test.html
+++ /dev/null
@@ -1,270 +0,0 @@
-<!DOCTYPE html>
-<!--
-Copyright 2017 The Chromium Authors. All rights reserved.
-Use of this source code is governed by a BSD-style license that can be
-found in the LICENSE file.
--->
-<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
-<title>cr-buildbucket-client</title>
-
-<script src="./bower_components/webcomponentsjs/webcomponents-lite.js"></script>
-<script src="./bower_components/web-component-tester/browser.js"></script>
-
-<link rel="import" href="./bower_components/iron-test-helpers/iron-test-helpers.html">
-<link rel="import" href="../static/cr-buildbucket-client.html">
-
-<test-fixture id="instanceOne">
-  <template>
-    <cr-buildbucket-client></cr-buildbucket-client>
-  </template>
-</test-fixture>
-<test-fixture id="instanceTwo">
-  <template>
-    <cr-buildbucket-client></cr-buildbucket-client>
-  </template>
-</test-fixture>
-
-<script>
-  suite('cr-buildbucket-client basic tests', () => {
-    let element;
-    let otherElement;
-    let sandbox;
-    let pluginStub;
-
-    async function assertRejects(promise, expectedReason = null) {
-      try {
-        await promise();
-        assert.fail('exception', 'no exception');
-      } catch (actualReason) {
-        if (expectedReason) {
-          assert.deepEqual(actualReason, expectedReason);
-        }
-      }
-    }
-
-    setup(() => {
-      element = fixture('instanceOne');
-      otherElement = fixture('instanceTwo');
-      sandbox = sinon.sandbox.create();
-      pluginStub = {
-        restApi() {
-          return {getLoggedIn: async () => true};
-        },
-      };
-      element.plugin = pluginStub;
-      element.baseUrl = 'http://buildbucket.example.com';
-      element._sharedAuthState.oauth_client_id = null;
-      element._sharedAuthState.oauth_configured = false;
-      element._sharedAuthState.oauth_token = null;
-      element._sharedAuthState.oauth_email = null;
-      sandbox.stub(gapi, 'load', (_, resolve) => { resolve(); });
-      gapi.auth = {
-        init: () => { throw Error('should be stubbed'); },
-        authorize: () => { throw Error('should be stubbed'); },
-      };
-    });
-
-    teardown(() => {
-      sandbox.restore();
-    });
-
-    test('compare ids', () => {
-      assert.isBelow(element.compareIds('9999', '999'), 0);
-      assert.isAbove(element.compareIds('999', '9999'), 0);
-      assert.isBelow(element.compareIds('999', '998'), 0);
-      assert.isAbove(element.compareIds('998', '999'), 0);
-      assert.equal(element.compareIds('0', '0'), 0);
-      assert.equal(element.compareIds('999999999999', '999999999999'), 0);
-    });
-
-    test('valid token', () => {
-      const now = ~~(Date.now() / 1000);
-      const expiration = (now + 3600) + '';
-      assert.isFalse(element._oauthTokenIsValid(null));
-      assert.isFalse(element._oauthTokenIsValid(undefined));
-      assert.isFalse(element._oauthTokenIsValid({access_token: 'foo'}));
-      assert.isFalse(element._oauthTokenIsValid({expires_at: expiration}));
-      assert.isTrue(element._oauthTokenIsValid({
-        access_token: 'foo',
-        expires_at: expiration,
-      }));
-      assert.isFalse(element._oauthTokenIsValid({
-        access_token: 'foo',
-        expires_at: (now - 5) + '',
-      }));
-    });
-
-    test('multiple oauth config calls', async () => {
-      const oauthConfigStub =
-          sandbox.stub(element, '_getOAuthConfig', async () => ({
-            auth_url: '',
-            proxy_url: '',
-            client_id: 'test_client_id',
-            email: 'andybons@chromium.org',
-          }));
-      sandbox.stub(gapi.auth, 'init', (resolve) => { resolve(); });
-      await element._configureOAuthLibrary();
-      await element._configureOAuthLibrary();
-      assert.equal(oauthConfigStub.callCount, 1);
-      console.log(element._sharedAuthState);
-      console.log(otherElement._sharedAuthState);
-      assert.strictEqual(element._sharedAuthState, otherElement._sharedAuthState);
-      assert.equal(element._sharedAuthState.oauth_client_id, 'test_client_id');
-      assert.equal(element._sharedAuthState.oauth_email, 'andybons@chromium.org');
-      assert.isTrue(element._sharedAuthState.oauth_configured);
-    });
-
-    test('get oauth token', async () => {
-      const now = ~~(Date.now() / 1000);
-      const token = {
-        state: '',
-        access_token: '1234abcdabcd1234',
-        token_type: 'Bearer',
-        expires_in: '3600',
-        scope: 'test scope',
-        client_id: 'test_client_id',
-        response_type: 'token',
-        issued_at: now + '',
-        expires_at: (now + 3600) + '',
-        _aa: '1',
-        status: {
-          google_logged_in: false,
-          signed_in: true,
-          method: 'AUTO',
-        },
-      };
-      const refreshTokenStub =
-          sandbox.stub(element, '_refreshToken', async () => token);
-      const configureStub = sandbox.stub(element, '_configureOAuthLibrary',
-          async () => {});
-
-      await element.getOAuthToken();
-      await element.getOAuthToken();
-      assert.equal(configureStub.callCount, 1);
-      assert.equal(refreshTokenStub.callCount, 1);
-      assert.equal(element._sharedAuthState.oauth_token, token);
-    });
-
-    test('search builds', async () => {
-      const builds = [
-        {id: '1'},
-        {id: '1'},
-      ];
-
-      sandbox.stub(window, 'fetch', async (url, opts) => {
-        assert.equal(
-            url,
-            ('http://buildbucket.example.com/' +
-             '_ah/api/buildbucket/v1/search?bucket=x'));
-        return {
-          ok: true,
-          json: async () => ({builds}),
-        };
-      });
-      assert.deepEqual(await element.searchBuilds({bucket: 'x'}), builds);
-    });
-
-    test('fetch', async () => {
-      const res = {a: 1};
-      sandbox.stub(window, 'fetch', async (url, opts) => {
-        assert.equal(url, 'http://buildbucket.example.com/a/b?c=d');
-
-        assert.equal(opts.method, 'GET');
-        const headers = Array.from(opts.headers.entries()).map(
-            ([k, v]) => `${k}: ${v}`);
-        headers.sort();
-        assert.deepEqual(headers, [
-          'accept: application/json',
-          'user-agent: Gerrit-Buildbucket plugin',
-        ]);
-
-        return {
-          ok: true,
-          json: async () => res,
-        };
-      });
-      assert.deepEqual(await element._fetch('GET', 'a/b', 'c=d'), res);
-    });
-
-    test('fetch with auth token includes bearer token header', async () => {
-      element._sharedAuthState = {
-        oauth_token: {access_token: 'foo'},
-        oauth_configured: true,
-      };
-      const fetchStub = sandbox.stub(window, 'fetch', async (url, opts) => {
-        assert.equal(opts.headers.get('authorization'), 'Bearer foo');
-        return {ok: true, json: async () => ({})};
-      });
-      await element._fetch('GET', 'somepath', 'somequery');
-      assert.equal(fetchStub.callCount, 1);
-    });
-
-    test('fetch POST', async () => {
-      const req = {body: 1};
-      const res = {a: 1};
-
-      sandbox.stub(window, 'fetch', async (url, opts) => {
-        assert.equal(opts.method, 'POST');
-        assert.deepEqual(JSON.parse(opts.body), req);
-        assert.equal(
-            opts.headers.get('content-type'),
-            'application/json');
-
-        return {
-          ok: true,
-          json: async () => res,
-        };
-      });
-
-      assert.deepEqual(await element._fetch('POST', 'a/b', null, req), res);
-    });
-
-    test('fetch fails with json response', async () => {
-      const res = {a: 1};
-      sandbox.stub(window, 'fetch', async (url, opts) => ({
-        ok: false,
-        status: 403,
-        text: async () => JSON.stringify(res),
-      }));
-
-      await assertRejects(async () => await element._fetch('GET', 'a/b'), res);
-    });
-
-    test('fetch fails with textual response', async () => {
-      const res = 'no';
-      sandbox.stub(window, 'fetch', async (url, opts) => ({
-        ok: false,
-        status: 403,
-        text: async () => res,
-      }));
-
-      await assertRejects(async () => await element._fetch('GET', 'a/b'), res);
-    });
-
-    test('_refreshToken resolves when authorize gives token', async () => {
-      const token = {access_token: 'test_access_token'};
-      sandbox.stub(window.gapi.auth, 'authorize', (opts, callback) => {
-        callback(token);
-      });
-      const value = await element._refreshToken(100);
-      assert.deepEqual(value, token);
-    });
-
-    test('_refreshToken rejects when authorize gives error', async () => {
-      const token = {error: 'could not authorize'};
-      sandbox.stub(window.gapi.auth, 'authorize', (opts, callback) => {
-        callback(token);
-      });
-
-      await assertRejects(async () => await element._refreshToken(100));
-    });
-
-    test('_refreshToken rejects when authorize times out', async () => {
-      // In this test, we make it so that authorize never resolves,
-      // and the timeout is made smaller so that the test completes faster.
-      sandbox.stub(window.gapi.auth, 'authorize', () => new Promise(() => {}));
-      await assertRejects(async () => element._refreshToken(0));
-    });
-  });
-</script>
-
diff --git a/test/index.html b/test/index.html
index 4127036..3408eb1 100644
--- a/test/index.html
+++ b/test/index.html
@@ -12,8 +12,6 @@
 <script>
   // Load and run all tests (.html, .js) as one suite:
   WCT.loadSuites([
-    'cr-buildbucket-client_test.html',
-    'cr-buildbucket-client_test.html?dom=shadow',
     'binary-size-view_test.html',
     'binary-size-view_test.html?dom=shadow',
   ]);