Merge "Move Gerrit.* to a separate file."
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-api-utils.js b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-api-utils.js
new file mode 100644
index 0000000..cf2376e
--- /dev/null
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-api-utils.js
@@ -0,0 +1,95 @@
+/**
+ * @license
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+(function(window) {
+ 'use strict';
+
+ const PRELOADED_PROTOCOL = 'preloaded:';
+
+ let _restAPI;
+
+ const getRestAPI = () => {
+ if (!_restAPI) {
+ _restAPI = document.createElement('gr-rest-api-interface');
+ }
+ return _restAPI;
+ };
+
+ function getPluginNameFromUrl(url) {
+ if (!(url instanceof URL)) {
+ try {
+ url = new URL(url);
+ } catch (e) {
+ console.warn(e);
+ return null;
+ }
+ }
+ if (url.protocol === PRELOADED_PROTOCOL) {
+ return url.pathname;
+ }
+ const base = Gerrit.BaseUrlBehavior.getBaseUrl();
+ const pathname = url.pathname.replace(base, '');
+ // Site theme is server from predefined path.
+ if (pathname === '/static/gerrit-theme.html') {
+ return 'gerrit-theme';
+ } else if (!pathname.startsWith('/plugins')) {
+ console.warn('Plugin not being loaded from /plugins base path:',
+ url.href, '— Unable to determine name.');
+ return null;
+ }
+ // Pathname should normally look like this:
+ // /plugins/PLUGINNAME/static/SCRIPTNAME.html
+ // Or, for app/samples:
+ // /plugins/PLUGINNAME.html
+ return pathname.split('/')[2].split('.')[0];
+ }
+
+ // TODO (viktard): deprecate in favor of GrPluginRestApi.
+ function send(method, url, opt_callback, opt_payload) {
+ return getRestAPI().send(method, url, opt_payload).then(response => {
+ if (response.status < 200 || response.status >= 300) {
+ return response.text().then(text => {
+ if (text) {
+ return Promise.reject(text);
+ } else {
+ return Promise.reject(response.status);
+ }
+ });
+ } else {
+ return getRestAPI().getResponseObject(response);
+ }
+ }).then(response => {
+ if (opt_callback) {
+ opt_callback(response);
+ }
+ return response;
+ });
+ }
+
+ function resetInternalState() {
+ _restAPI = null;
+ }
+
+ window._apiUtils = {
+ getPluginNameFromUrl,
+ send,
+ getRestAPI,
+
+ // TEST only methods
+ resetInternalState,
+ };
+})(window);
\ No newline at end of file
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-api-utils_test.html b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-api-utils_test.html
new file mode 100644
index 0000000..c407aa8
--- /dev/null
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-api-utils_test.html
@@ -0,0 +1,78 @@
+<!DOCTYPE html>
+<!--
+@license
+Copyright (C) 2019 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+
+<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<title>gr-api-interface</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
+
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
+<link rel="import" href="../../../test/common-test-setup.html"/>
+<link rel="import" href="gr-js-api-interface.html">
+
+<script>void(0);</script>
+
+<script>
+
+ const PRELOADED_PROTOCOL = 'preloaded:';
+
+ suite('gr-api-utils tests', () => {
+ suite('test getPluginNameFromUrl', () => {
+ const {getPluginNameFromUrl} = window._apiUtils;
+
+ test('with empty string', () => {
+ assert.equal(getPluginNameFromUrl(''), null);
+ });
+
+ test('with invalid url', () => {
+ assert.equal(getPluginNameFromUrl('test'), null);
+ });
+
+ test('with random invalid url', () => {
+ assert.equal(getPluginNameFromUrl('http://example.com'), null);
+ assert.equal(
+ getPluginNameFromUrl('http://example.com/static/a.html'),
+ null
+ );
+ });
+
+ test('with valid urls', () => {
+ assert.equal(
+ getPluginNameFromUrl('http://example.com/plugins/a.html'),
+ 'a'
+ );
+ assert.equal(
+ getPluginNameFromUrl('http://example.com/plugins/a/static/t.html'),
+ 'a'
+ );
+ });
+
+ test('with preloaded urls', () => {
+ assert.equal(getPluginNameFromUrl(`${PRELOADED_PROTOCOL}a`), 'a');
+ });
+
+ test('with gerrit-theme override', () => {
+ assert.equal(
+ getPluginNameFromUrl('http://example.com/static/gerrit-theme.html'),
+ 'gerrit-theme'
+ );
+ });
+ });
+ });
+</script>
\ No newline at end of file
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-gerrit.js b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-gerrit.js
new file mode 100644
index 0000000..8cdbcb7
--- /dev/null
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-gerrit.js
@@ -0,0 +1,280 @@
+/**
+ * @license
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+(function(window) {
+ 'use strict';
+
+ /**
+ * Hash of loaded and installed plugins, name to Plugin object.
+ */
+ const _plugins = {};
+
+ /**
+ * Array of plugin URLs to be loaded, name to url.
+ */
+ let _pluginsPending = {};
+
+ let _pluginsInstalled = [];
+
+ let _pluginsPendingCount = -1;
+
+ const UNKNOWN_PLUGIN = 'unknown';
+ const PRELOADED_PROTOCOL = 'preloaded:';
+
+ const PLUGIN_LOADING_TIMEOUT_MS = 10000;
+
+ let _reporting;
+ const getReporting = () => {
+ if (!_reporting) {
+ _reporting = document.createElement('gr-reporting');
+ }
+ return _reporting;
+ };
+
+ // Import utils methods
+ const {
+ getPluginNameFromUrl,
+ send,
+ getRestAPI,
+ resetInternalState,
+ } = window._apiUtils;
+
+ const API_VERSION = '0.1';
+
+ window.Gerrit = window.Gerrit || {};
+ const Gerrit = window.Gerrit;
+
+ let _resolveAllPluginsLoaded = null;
+ let _allPluginsPromise = null;
+
+ Gerrit._endpoints = new GrPluginEndpoints();
+
+ // Provide reset plugins function to clear installed plugins between tests.
+ const app = document.querySelector('#app');
+ if (!app) {
+ // No gr-app found (running tests)
+ Gerrit._installPreloadedPlugins = installPreloadedPlugins;
+ Gerrit._flushPreinstalls = flushPreinstalls;
+ Gerrit._resetPlugins = () => {
+ _allPluginsPromise = null;
+ _pluginsInstalled = [];
+ _pluginsPending = {};
+ _pluginsPendingCount = -1;
+ _reporting = null;
+ _resolveAllPluginsLoaded = null;
+ resetInternalState();
+ Gerrit._endpoints = new GrPluginEndpoints();
+ for (const k of Object.keys(_plugins)) {
+ delete _plugins[k];
+ }
+ };
+ }
+
+ /**
+ * @deprecated Use plugin.styles().css(rulesStr) instead. Please, consult
+ * the documentation how to replace it accordingly.
+ */
+ Gerrit.css = function(rulesStr) {
+ console.warn('Gerrit.css(rulesStr) is deprecated!',
+ 'Use plugin.styles().css(rulesStr)');
+ if (!Gerrit._customStyleSheet) {
+ const styleEl = document.createElement('style');
+ document.head.appendChild(styleEl);
+ Gerrit._customStyleSheet = styleEl.sheet;
+ }
+
+ const name = '__pg_js_api_class_' +
+ Gerrit._customStyleSheet.cssRules.length;
+ Gerrit._customStyleSheet.insertRule('.' + name + '{' + rulesStr + '}', 0);
+ return name;
+ };
+
+ Gerrit.install = function(callback, opt_version, opt_src) {
+ // HTML import polyfill adds __importElement pointing to the import tag.
+ const script = document.currentScript &&
+ (document.currentScript.__importElement || document.currentScript);
+
+ let src = opt_src || (script && script.src);
+ if (!src || src.startsWith('data:')) {
+ src = script && script.baseURI;
+ }
+ const name = getPluginNameFromUrl(src);
+
+ if (opt_version && opt_version !== API_VERSION) {
+ Gerrit._pluginInstallError(`Plugin ${name} install error: only version ` +
+ API_VERSION + ' is supported in PolyGerrit. ' + opt_version +
+ ' was given.');
+ return;
+ }
+
+ const existingPlugin = _plugins[name];
+ const plugin = existingPlugin || new Plugin(src);
+ try {
+ callback(plugin);
+ if (name) {
+ _plugins[name] = plugin;
+ }
+ if (!existingPlugin) {
+ Gerrit._pluginInstalled(src);
+ }
+ } catch (e) {
+ Gerrit._pluginInstallError(`${e.name}: ${e.message}`);
+ }
+ };
+
+ Gerrit.getLoggedIn = function() {
+ console.warn('Gerrit.getLoggedIn() is deprecated! ' +
+ 'Use plugin.restApi().getLoggedIn()');
+ return document.createElement('gr-rest-api-interface').getLoggedIn();
+ };
+
+ Gerrit.get = function(url, callback) {
+ console.warn('.get() is deprecated! Use plugin.restApi().get()');
+ send('GET', url, callback);
+ };
+
+ Gerrit.post = function(url, payload, callback) {
+ console.warn('.post() is deprecated! Use plugin.restApi().post()');
+ send('POST', url, callback, payload);
+ };
+
+ Gerrit.put = function(url, payload, callback) {
+ console.warn('.put() is deprecated! Use plugin.restApi().put()');
+ send('PUT', url, callback, payload);
+ };
+
+ Gerrit.delete = function(url, opt_callback) {
+ console.warn('.delete() is deprecated! Use plugin.restApi().delete()');
+ return getRestAPI().send('DELETE', url).then(response => {
+ if (response.status !== 204) {
+ return response.text().then(text => {
+ if (text) {
+ return Promise.reject(text);
+ } else {
+ return Promise.reject(response.status);
+ }
+ });
+ }
+ if (opt_callback) {
+ opt_callback(response);
+ }
+ return response;
+ });
+ };
+
+ Gerrit.awaitPluginsLoaded = function() {
+ if (!_allPluginsPromise) {
+ if (Gerrit._arePluginsLoaded()) {
+ _allPluginsPromise = Promise.resolve();
+ } else {
+ let timeoutId;
+ _allPluginsPromise =
+ Promise.race([
+ new Promise(resolve => _resolveAllPluginsLoaded = resolve),
+ new Promise(resolve => timeoutId = setTimeout(
+ Gerrit._pluginLoadingTimeout, PLUGIN_LOADING_TIMEOUT_MS)),
+ ]).then(() => clearTimeout(timeoutId));
+ }
+ }
+ return _allPluginsPromise;
+ };
+
+ Gerrit._pluginLoadingTimeout = function() {
+ console.error(`Failed to load plugins: ${Object.keys(_pluginsPending)}`);
+ Gerrit._setPluginsPending([]);
+ };
+
+ Gerrit._setPluginsPending = function(plugins) {
+ _pluginsPending = plugins.reduce((o, url) => {
+ // TODO(viktard): Remove guard (@see Issue 8962)
+ o[getPluginNameFromUrl(url) || UNKNOWN_PLUGIN] = url;
+ return o;
+ }, {});
+ Gerrit._setPluginsCount(Object.keys(_pluginsPending).length);
+ };
+
+ Gerrit._setPluginsCount = function(count) {
+ _pluginsPendingCount = count;
+ if (Gerrit._arePluginsLoaded()) {
+ getReporting().pluginsLoaded(_pluginsInstalled);
+ if (_resolveAllPluginsLoaded) {
+ _resolveAllPluginsLoaded();
+ }
+ }
+ };
+
+ Gerrit._pluginInstallError = function(message) {
+ document.dispatchEvent(new CustomEvent('show-alert', {
+ detail: {
+ message: `Plugin install error: ${message}`,
+ },
+ }));
+ console.info(`Plugin install error: ${message}`);
+ Gerrit._setPluginsCount(_pluginsPendingCount - 1);
+ };
+
+ Gerrit._pluginInstalled = function(url) {
+ const name = getPluginNameFromUrl(url) || UNKNOWN_PLUGIN;
+ if (!_pluginsPending[name]) {
+ console.warn(`Unexpected plugin ${name} installed from ${url}.`);
+ } else {
+ delete _pluginsPending[name];
+ _pluginsInstalled.push(name);
+ Gerrit._setPluginsCount(_pluginsPendingCount - 1);
+ getReporting().pluginLoaded(name);
+ console.log(`Plugin ${name} installed.`);
+ }
+ };
+
+ Gerrit._arePluginsLoaded = function() {
+ return _pluginsPendingCount === 0;
+ };
+
+ Gerrit._getPluginScreenName = function(pluginName, screenName) {
+ return `${pluginName}-screen-${screenName}`;
+ };
+
+ Gerrit._isPluginPreloaded = function(url) {
+ const name = getPluginNameFromUrl(url);
+ if (name && Gerrit._preloadedPlugins) {
+ return name in Gerrit._preloadedPlugins;
+ } else {
+ return false;
+ }
+ };
+
+ function flushPreinstalls() {
+ if (window.Gerrit.flushPreinstalls) {
+ window.Gerrit.flushPreinstalls();
+ }
+ }
+
+ function installPreloadedPlugins() {
+ if (!Gerrit._preloadedPlugins) { return; }
+ for (const name in Gerrit._preloadedPlugins) {
+ if (!Gerrit._preloadedPlugins.hasOwnProperty(name)) { continue; }
+ const callback = Gerrit._preloadedPlugins[name];
+ Gerrit.install(callback, API_VERSION, PRELOADED_PROTOCOL + name);
+ }
+ }
+
+ flushPreinstalls();
+
+ // Preloaded plugins should be installed after Gerrit.install() is set,
+ // since plugin preloader substitutes Gerrit.install() temporarily.
+ installPreloadedPlugins();
+})(window);
\ No newline at end of file
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-gerrit_test.html b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-gerrit_test.html
new file mode 100644
index 0000000..b3ec30f
--- /dev/null
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-gerrit_test.html
@@ -0,0 +1,188 @@
+<!DOCTYPE html>
+<!--
+@license
+Copyright (C) 2019 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+
+<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<title>gr-api-interface</title>
+<script src="/test/common-test-setup.js"></script>
+<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
+
+<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<script src="/bower_components/web-component-tester/browser.js"></script>
+<link rel="import" href="../../../test/common-test-setup.html"/>
+<link rel="import" href="gr-js-api-interface.html">
+
+<script>void(0);</script>
+
+<test-fixture id="basic">
+ <template>
+ <gr-js-api-interface></gr-js-api-interface>
+ </template>
+</test-fixture>
+
+<script>
+ suite('gr-gerrit tests', () => {
+ let element;
+ let plugin;
+ let sandbox;
+ let sendStub;
+
+ setup(() => {
+ sandbox = sinon.sandbox.create();
+ sendStub = sandbox.stub().returns(Promise.resolve({status: 200}));
+ stub('gr-rest-api-interface', {
+ getAccount() {
+ return Promise.resolve({name: 'Judy Hopps'});
+ },
+ send(...args) {
+ return sendStub(...args);
+ },
+ });
+ element = fixture('basic');
+ Gerrit.install(p => { plugin = p; }, '0.1',
+ 'http://test.com/plugins/testplugin/static/test.js');
+ Gerrit._setPluginsPending([]);
+ });
+
+ teardown(() => {
+ sandbox.restore();
+ element._removeEventCallbacks();
+ plugin = null;
+ });
+
+ test('reuse plugin for install calls', () => {
+ let otherPlugin;
+ Gerrit.install(p => { otherPlugin = p; }, '0.1',
+ 'http://test.com/plugins/testplugin/static/test.js');
+ assert.strictEqual(plugin, otherPlugin);
+ });
+
+ test('flushes preinstalls if provided', () => {
+ assert.doesNotThrow(() => {
+ Gerrit._flushPreinstalls();
+ });
+ window.Gerrit.flushPreinstalls = sandbox.stub();
+ Gerrit._flushPreinstalls();
+ assert.isTrue(window.Gerrit.flushPreinstalls.calledOnce);
+ delete window.Gerrit.flushPreinstalls;
+ });
+
+ test('versioning', () => {
+ const callback = sandbox.spy();
+ Gerrit.install(callback, '0.0pre-alpha');
+ assert(callback.notCalled);
+ });
+
+ test('_setPluginsCount', done => {
+ stub('gr-reporting', {
+ pluginsLoaded() {
+ done();
+ },
+ });
+ Gerrit._setPluginsCount(0);
+ });
+
+ test('_arePluginsLoaded', () => {
+ assert.isTrue(Gerrit._arePluginsLoaded());
+ Gerrit._setPluginsCount(1);
+ assert.isFalse(Gerrit._arePluginsLoaded());
+ Gerrit._setPluginsCount(0);
+ assert.isTrue(Gerrit._arePluginsLoaded());
+ });
+
+ test('_pluginInstalled', () => {
+ const pluginsLoadedStub = sandbox.stub();
+ stub('gr-reporting', {
+ pluginsLoaded: (...args) => pluginsLoadedStub(...args),
+ });
+ const plugins = [
+ 'http://test.com/plugins/foo/static/test.js',
+ 'http://test.com/plugins/bar/static/test.js',
+ ];
+ Gerrit._setPluginsPending(plugins);
+ Gerrit._pluginInstalled(plugins[0]);
+ Gerrit._pluginInstalled(plugins[1]);
+ assert.isTrue(pluginsLoadedStub.calledWithExactly(['foo', 'bar']));
+ });
+
+ test('install calls _pluginInstalled', () => {
+ sandbox.stub(Gerrit, '_pluginInstalled');
+ Gerrit.install(p => { plugin = p; }, '0.1',
+ 'http://test.com/plugins/testplugin/static/test.js');
+
+ // testplugin has already been installed once (in setup).
+ assert.isFalse(Gerrit._pluginInstalled.called);
+
+ // testplugin2 plugin has not yet been installed.
+ Gerrit.install(p => { plugin = p; }, '0.1',
+ 'http://test.com/plugins/testplugin2/static/test.js');
+ assert.isTrue(Gerrit._pluginInstalled.calledOnce);
+ });
+
+ test('plugin install errors mark plugins as loaded', () => {
+ Gerrit._setPluginsCount(1);
+ Gerrit.install(() => {}, '0.0pre-alpha');
+ return Gerrit.awaitPluginsLoaded();
+ });
+
+ test('multiple ui plugins per java plugin', () => {
+ const file1 = 'http://test.com/plugins/qaz/static/foo.nocache.js';
+ const file2 = 'http://test.com/plugins/qaz/static/bar.js';
+ Gerrit._setPluginsPending([file1, file2]);
+ Gerrit.install(() => {}, '0.1', file1);
+ Gerrit.install(() => {}, '0.1', file2);
+ return Gerrit.awaitPluginsLoaded();
+ });
+
+ test('plugin install errors shows toasts', () => {
+ const alertStub = sandbox.stub();
+ document.addEventListener('show-alert', alertStub);
+ Gerrit._setPluginsCount(1);
+ Gerrit.install(() => {}, '0.0pre-alpha');
+ return Gerrit.awaitPluginsLoaded().then(() => {
+ assert.isTrue(alertStub.calledOnce);
+ });
+ });
+
+ test('Gerrit._isPluginPreloaded', () => {
+ Gerrit._preloadedPlugins = {baz: ()=>{}};
+ assert.isFalse(Gerrit._isPluginPreloaded('plugins/foo/bar'));
+ assert.isFalse(Gerrit._isPluginPreloaded('http://a.com/42'));
+ assert.isTrue(Gerrit._isPluginPreloaded('preloaded:baz'));
+ Gerrit._preloadedPlugins = null;
+ });
+
+ test('preloaded plugins are installed', () => {
+ const installStub = sandbox.stub();
+ Gerrit._preloadedPlugins = {foo: installStub};
+ Gerrit._installPreloadedPlugins();
+ assert.isTrue(installStub.called);
+ const pluginApi = installStub.lastCall.args[0];
+ assert.strictEqual(pluginApi.getPluginName(), 'foo');
+ });
+
+ test('installing preloaded plugin', () => {
+ let plugin;
+ window.ASSETS_PATH = 'http://blips.com/chitz';
+ Gerrit.install(p => { plugin = p; }, '0.1', 'preloaded:foo');
+ assert.strictEqual(plugin.getPluginName(), 'foo');
+ assert.strictEqual(plugin.url('/some/thing.html'),
+ 'http://blips.com/chitz/plugins/foo/some/thing.html');
+ delete window.ASSETS_PATH;
+ });
+ });
+</script>
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface.html b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface.html
index e460660..7fa2250 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface.html
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface.html
@@ -31,6 +31,7 @@
<link rel="import" href="../gr-rest-api-interface/gr-rest-api-interface.html">
<dom-module id="gr-js-api-interface">
+ <script src="gr-api-utils.js"></script>
<script src="gr-annotation-actions-context.js"></script>
<script src="gr-annotation-actions-js-api.js"></script>
<script src="gr-change-actions-js-api.js"></script>
@@ -40,4 +41,5 @@
<script src="gr-plugin-action-context.js"></script>
<script src="gr-plugin-rest-api.js"></script>
<script src="gr-public-js-api.js"></script>
+ <script src="gr-gerrit.js"></script>
</dom-module>
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface_test.html b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface_test.html
index c7e4f09..330310f 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface_test.html
@@ -73,23 +73,6 @@
plugin = null;
});
- test('reuse plugin for install calls', () => {
- let otherPlugin;
- Gerrit.install(p => { otherPlugin = p; }, '0.1',
- 'http://test.com/plugins/testplugin/static/test.js');
- assert.strictEqual(plugin, otherPlugin);
- });
-
- test('flushes preinstalls if provided', () => {
- assert.doesNotThrow(() => {
- Gerrit._flushPreinstalls();
- });
- window.Gerrit.flushPreinstalls = sandbox.stub();
- Gerrit._flushPreinstalls();
- assert.isTrue(window.Gerrit.flushPreinstalls.calledOnce);
- delete window.Gerrit.flushPreinstalls;
- });
-
test('url', () => {
assert.equal(plugin.url(), 'http://test.com/plugins/testplugin/');
assert.equal(plugin.url('/static/test.js'),
@@ -313,12 +296,6 @@
element.handleEvent(element.EventType.HIGHLIGHTJS_LOADED, {hljs: testHljs});
});
- test('versioning', () => {
- const callback = sandbox.spy();
- Gerrit.install(callback, '0.0pre-alpha');
- assert(callback.notCalled);
- });
-
test('getAccount', done => {
plugin.restApi().getLoggedIn().then(loggedIn => {
assert.isTrue(loggedIn);
@@ -326,77 +303,6 @@
});
});
- test('_setPluginsCount', done => {
- stub('gr-reporting', {
- pluginsLoaded() {
- done();
- },
- });
- Gerrit._setPluginsCount(0);
- });
-
- test('_arePluginsLoaded', () => {
- assert.isTrue(Gerrit._arePluginsLoaded());
- Gerrit._setPluginsCount(1);
- assert.isFalse(Gerrit._arePluginsLoaded());
- Gerrit._setPluginsCount(0);
- assert.isTrue(Gerrit._arePluginsLoaded());
- });
-
- test('_pluginInstalled', () => {
- const pluginsLoadedStub = sandbox.stub();
- stub('gr-reporting', {
- pluginsLoaded: (...args) => pluginsLoadedStub(...args),
- });
- const plugins = [
- 'http://test.com/plugins/foo/static/test.js',
- 'http://test.com/plugins/bar/static/test.js',
- ];
- Gerrit._setPluginsPending(plugins);
- Gerrit._pluginInstalled(plugins[0]);
- Gerrit._pluginInstalled(plugins[1]);
- assert.isTrue(pluginsLoadedStub.calledWithExactly(['foo', 'bar']));
- });
-
- test('install calls _pluginInstalled', () => {
- sandbox.stub(Gerrit, '_pluginInstalled');
- Gerrit.install(p => { plugin = p; }, '0.1',
- 'http://test.com/plugins/testplugin/static/test.js');
-
- // testplugin has already been installed once (in setup).
- assert.isFalse(Gerrit._pluginInstalled.called);
-
- // testplugin2 plugin has not yet been installed.
- Gerrit.install(p => { plugin = p; }, '0.1',
- 'http://test.com/plugins/testplugin2/static/test.js');
- assert.isTrue(Gerrit._pluginInstalled.calledOnce);
- });
-
- test('plugin install errors mark plugins as loaded', () => {
- Gerrit._setPluginsCount(1);
- Gerrit.install(() => {}, '0.0pre-alpha');
- return Gerrit.awaitPluginsLoaded();
- });
-
- test('multiple ui plugins per java plugin', () => {
- const file1 = 'http://test.com/plugins/qaz/static/foo.nocache.js';
- const file2 = 'http://test.com/plugins/qaz/static/bar.js';
- Gerrit._setPluginsPending([file1, file2]);
- Gerrit.install(() => {}, '0.1', file1);
- Gerrit.install(() => {}, '0.1', file2);
- return Gerrit.awaitPluginsLoaded();
- });
-
- test('plugin install errors shows toasts', () => {
- const alertStub = sandbox.stub();
- document.addEventListener('show-alert', alertStub);
- Gerrit._setPluginsCount(1);
- Gerrit.install(() => {}, '0.0pre-alpha');
- return Gerrit.awaitPluginsLoaded().then(() => {
- assert.isTrue(alertStub.calledOnce);
- });
- });
-
test('attributeHelper', () => {
assert.isOk(plugin.attributeHelper());
});
@@ -422,33 +328,6 @@
element.EventType.ADMIN_MENU_LINKS);
});
- test('Gerrit._isPluginPreloaded', () => {
- Gerrit._preloadedPlugins = {baz: ()=>{}};
- assert.isFalse(Gerrit._isPluginPreloaded('plugins/foo/bar'));
- assert.isFalse(Gerrit._isPluginPreloaded('http://a.com/42'));
- assert.isTrue(Gerrit._isPluginPreloaded('preloaded:baz'));
- Gerrit._preloadedPlugins = null;
- });
-
- test('preloaded plugins are installed', () => {
- const installStub = sandbox.stub();
- Gerrit._preloadedPlugins = {foo: installStub};
- Gerrit._installPreloadedPlugins();
- assert.isTrue(installStub.called);
- const pluginApi = installStub.lastCall.args[0];
- assert.strictEqual(pluginApi.getPluginName(), 'foo');
- });
-
- test('installing preloaded plugin', () => {
- let plugin;
- window.ASSETS_PATH = 'http://blips.com/chitz';
- Gerrit.install(p => { plugin = p; }, '0.1', 'preloaded:foo');
- assert.strictEqual(plugin.getPluginName(), 'foo');
- assert.strictEqual(plugin.url('/some/thing.html'),
- 'http://blips.com/chitz/plugins/foo/some/thing.html');
- delete window.ASSETS_PATH;
- });
-
suite('test plugin with base url', () => {
let baseUrlPlugin;
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-public-js-api.js b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-public-js-api.js
index 3b7b80d..d44acea 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-public-js-api.js
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-public-js-api.js
@@ -17,71 +17,18 @@
(function(window) {
'use strict';
- /**
- * Hash of loaded and installed plugins, name to Plugin object.
- */
- const _plugins = {};
-
- /**
- * Array of plugin URLs to be loaded, name to url.
- */
- let _pluginsPending = {};
-
- let _pluginsInstalled = [];
-
- let _pluginsPendingCount = -1;
-
const PRELOADED_PROTOCOL = 'preloaded:';
- const UNKNOWN_PLUGIN = 'unknown';
-
const PANEL_ENDPOINTS_MAPPING = {
CHANGE_SCREEN_BELOW_COMMIT_INFO_BLOCK: 'change-view-integration',
CHANGE_SCREEN_BELOW_CHANGE_INFO_BLOCK: 'change-metadata-item',
};
- const PLUGIN_LOADING_TIMEOUT_MS = 10000;
-
- let _restAPI;
-
- const getRestAPI = () => {
- if (!_restAPI) {
- _restAPI = document.createElement('gr-rest-api-interface');
- }
- return _restAPI;
- };
-
- let _reporting;
- const getReporting = () => {
- if (!_reporting) {
- _reporting = document.createElement('gr-reporting');
- }
- return _reporting;
- };
-
- // TODO (viktard): deprecate in favor of GrPluginRestApi.
- function send(method, url, opt_callback, opt_payload) {
- return getRestAPI().send(method, url, opt_payload).then(response => {
- if (response.status < 200 || response.status >= 300) {
- return response.text().then(text => {
- if (text) {
- return Promise.reject(text);
- } else {
- return Promise.reject(response.status);
- }
- });
- } else {
- return getRestAPI().getResponseObject(response);
- }
- }).then(response => {
- if (opt_callback) {
- opt_callback(response);
- }
- return response;
- });
- }
-
- const API_VERSION = '0.1';
+ // Import utils methods
+ const {
+ getPluginNameFromUrl,
+ send,
+ } = window._apiUtils;
/**
* Plugin-provided custom components can affect content in extension
@@ -99,50 +46,6 @@
STYLE: 'style',
};
- function flushPreinstalls() {
- if (window.Gerrit.flushPreinstalls) {
- window.Gerrit.flushPreinstalls();
- }
- }
-
- function installPreloadedPlugins() {
- if (!Gerrit._preloadedPlugins) { return; }
- for (const name in Gerrit._preloadedPlugins) {
- if (!Gerrit._preloadedPlugins.hasOwnProperty(name)) { continue; }
- const callback = Gerrit._preloadedPlugins[name];
- Gerrit.install(callback, API_VERSION, PRELOADED_PROTOCOL + name);
- }
- }
-
- function getPluginNameFromUrl(url) {
- if (!(url instanceof URL)) {
- try {
- url = new URL(url);
- } catch (e) {
- console.warn(e);
- return null;
- }
- }
- if (url.protocol === PRELOADED_PROTOCOL) {
- return url.pathname;
- }
- const base = Gerrit.BaseUrlBehavior.getBaseUrl();
- const pathname = url.pathname.replace(base, '');
- // Site theme is server from predefined path.
- if (pathname === '/static/gerrit-theme.html') {
- return 'gerrit-theme';
- } else if (!pathname.startsWith('/plugins')) {
- console.warn('Plugin not being loaded from /plugins base path:',
- url.href, '— Unable to determine name.');
- return;
- }
- // Pathname should normally look like this:
- // /plugins/PLUGINNAME/static/SCRIPTNAME.html
- // Or, for app/samples:
- // /plugins/PLUGINNAME.html
- return pathname.split('/')[2].split('.')[0];
- }
-
function Plugin(opt_url) {
this._domHooks = new GrDomHooksManager(this);
@@ -475,211 +378,5 @@
},
};
- flushPreinstalls();
-
- window.Gerrit = window.Gerrit || {};
- const Gerrit = window.Gerrit;
-
- let _resolveAllPluginsLoaded = null;
- let _allPluginsPromise = null;
-
- Gerrit._endpoints = new GrPluginEndpoints();
-
- // Provide reset plugins function to clear installed plugins between tests.
- const app = document.querySelector('#app');
- if (!app) {
- // No gr-app found (running tests)
- Gerrit._installPreloadedPlugins = installPreloadedPlugins;
- Gerrit._flushPreinstalls = flushPreinstalls;
- Gerrit._resetPlugins = () => {
- _allPluginsPromise = null;
- _pluginsInstalled = [];
- _pluginsPending = {};
- _pluginsPendingCount = -1;
- _reporting = null;
- _resolveAllPluginsLoaded = null;
- _restAPI = null;
- Gerrit._endpoints = new GrPluginEndpoints();
- for (const k of Object.keys(_plugins)) {
- delete _plugins[k];
- }
- };
- }
-
- /**
- * @deprecated Use plugin.styles().css(rulesStr) instead. Please, consult
- * the documentation how to replace it accordingly.
- */
- Gerrit.css = function(rulesStr) {
- console.warn('Gerrit.css(rulesStr) is deprecated!',
- 'Use plugin.styles().css(rulesStr)');
- if (!Gerrit._customStyleSheet) {
- const styleEl = document.createElement('style');
- document.head.appendChild(styleEl);
- Gerrit._customStyleSheet = styleEl.sheet;
- }
-
- const name = '__pg_js_api_class_' +
- Gerrit._customStyleSheet.cssRules.length;
- Gerrit._customStyleSheet.insertRule('.' + name + '{' + rulesStr + '}', 0);
- return name;
- };
-
- Gerrit.install = function(callback, opt_version, opt_src) {
- // HTML import polyfill adds __importElement pointing to the import tag.
- const script = document.currentScript &&
- (document.currentScript.__importElement || document.currentScript);
-
- let src = opt_src || (script && script.src);
- if (!src || src.startsWith('data:')) {
- src = script && script.baseURI;
- }
- const name = getPluginNameFromUrl(src);
-
- if (opt_version && opt_version !== API_VERSION) {
- Gerrit._pluginInstallError(`Plugin ${name} install error: only version ` +
- API_VERSION + ' is supported in PolyGerrit. ' + opt_version +
- ' was given.');
- return;
- }
-
- const existingPlugin = _plugins[name];
- const plugin = existingPlugin || new Plugin(src);
- try {
- callback(plugin);
- if (name) {
- _plugins[name] = plugin;
- }
- if (!existingPlugin) {
- Gerrit._pluginInstalled(src);
- }
- } catch (e) {
- Gerrit._pluginInstallError(`${e.name}: ${e.message}`);
- }
- };
-
- Gerrit.getLoggedIn = function() {
- console.warn('Gerrit.getLoggedIn() is deprecated! ' +
- 'Use plugin.restApi().getLoggedIn()');
- return document.createElement('gr-rest-api-interface').getLoggedIn();
- };
-
- Gerrit.get = function(url, callback) {
- console.warn('.get() is deprecated! Use plugin.restApi().get()');
- send('GET', url, callback);
- };
-
- Gerrit.post = function(url, payload, callback) {
- console.warn('.post() is deprecated! Use plugin.restApi().post()');
- send('POST', url, callback, payload);
- };
-
- Gerrit.put = function(url, payload, callback) {
- console.warn('.put() is deprecated! Use plugin.restApi().put()');
- send('PUT', url, callback, payload);
- };
-
- Gerrit.delete = function(url, opt_callback) {
- console.warn('.delete() is deprecated! Use plugin.restApi().delete()');
- return getRestAPI().send('DELETE', url).then(response => {
- if (response.status !== 204) {
- return response.text().then(text => {
- if (text) {
- return Promise.reject(text);
- } else {
- return Promise.reject(response.status);
- }
- });
- }
- if (opt_callback) {
- opt_callback(response);
- }
- return response;
- });
- };
-
- Gerrit.awaitPluginsLoaded = function() {
- if (!_allPluginsPromise) {
- if (Gerrit._arePluginsLoaded()) {
- _allPluginsPromise = Promise.resolve();
- } else {
- let timeoutId;
- _allPluginsPromise =
- Promise.race([
- new Promise(resolve => _resolveAllPluginsLoaded = resolve),
- new Promise(resolve => timeoutId = setTimeout(
- Gerrit._pluginLoadingTimeout, PLUGIN_LOADING_TIMEOUT_MS)),
- ]).then(() => clearTimeout(timeoutId));
- }
- }
- return _allPluginsPromise;
- };
-
- Gerrit._pluginLoadingTimeout = function() {
- console.error(`Failed to load plugins: ${Object.keys(_pluginsPending)}`);
- Gerrit._setPluginsPending([]);
- };
-
- Gerrit._setPluginsPending = function(plugins) {
- _pluginsPending = plugins.reduce((o, url) => {
- // TODO(viktard): Remove guard (@see Issue 8962)
- o[getPluginNameFromUrl(url) || UNKNOWN_PLUGIN] = url;
- return o;
- }, {});
- Gerrit._setPluginsCount(Object.keys(_pluginsPending).length);
- };
-
- Gerrit._setPluginsCount = function(count) {
- _pluginsPendingCount = count;
- if (Gerrit._arePluginsLoaded()) {
- getReporting().pluginsLoaded(_pluginsInstalled);
- if (_resolveAllPluginsLoaded) {
- _resolveAllPluginsLoaded();
- }
- }
- };
-
- Gerrit._pluginInstallError = function(message) {
- document.dispatchEvent(new CustomEvent('show-alert', {
- detail: {
- message: `Plugin install error: ${message}`,
- },
- }));
- console.info(`Plugin install error: ${message}`);
- Gerrit._setPluginsCount(_pluginsPendingCount - 1);
- };
-
- Gerrit._pluginInstalled = function(url) {
- const name = getPluginNameFromUrl(url) || UNKNOWN_PLUGIN;
- if (!_pluginsPending[name]) {
- console.warn(`Unexpected plugin ${name} installed from ${url}.`);
- } else {
- delete _pluginsPending[name];
- _pluginsInstalled.push(name);
- Gerrit._setPluginsCount(_pluginsPendingCount - 1);
- getReporting().pluginLoaded(name);
- console.log(`Plugin ${name} installed.`);
- }
- };
-
- Gerrit._arePluginsLoaded = function() {
- return _pluginsPendingCount === 0;
- };
-
- Gerrit._getPluginScreenName = function(pluginName, screenName) {
- return `${pluginName}-screen-${screenName}`;
- };
-
- Gerrit._isPluginPreloaded = function(url) {
- const name = getPluginNameFromUrl(url);
- if (name && Gerrit._preloadedPlugins) {
- return name in Gerrit._preloadedPlugins;
- } else {
- return false;
- }
- };
-
- // Preloaded plugins should be installed after Gerrit.install() is set,
- // since plugin preloader substitutes Gerrit.install() temporarily.
- installPreloadedPlugins();
+ window.Plugin = Plugin;
})(window);
diff --git a/polygerrit-ui/app/test/index.html b/polygerrit-ui/app/test/index.html
index 10d5aff..a0e85ef 100644
--- a/polygerrit-ui/app/test/index.html
+++ b/polygerrit-ui/app/test/index.html
@@ -188,7 +188,9 @@
'shared/gr-js-api-interface/gr-annotation-actions-js-api_test.html',
'shared/gr-js-api-interface/gr-change-actions-js-api_test.html',
'shared/gr-js-api-interface/gr-change-reply-js-api_test.html',
+ 'shared/gr-js-api-interface/gr-api-utils_test.html',
'shared/gr-js-api-interface/gr-js-api-interface_test.html',
+ 'shared/gr-js-api-interface/gr-gerrit_test.html',
// TODO: uncomment file & fix tests. The file was missed in this list for a long time.
// 'shared/gr-js-api-interface/gr-plugin-action-context_test.html',
'shared/gr-js-api-interface/gr-plugin-endpoints_test.html',