Revert "Move Gerrit.* to a separate file."

This reverts commit 8a56045eba.

Reason for revert: This may break the plugin bundling somehow

Change-Id: Ie2d0be40dd1085b01e7ce396982ff40270d80a69
This commit is contained in:
Tao Zhou
2019-10-14 11:42:42 +00:00
parent 8a56045eba
commit 74d74a0cf3
8 changed files with 430 additions and 651 deletions

View File

@@ -1,95 +0,0 @@
/**
* @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);

View File

@@ -1,78 +0,0 @@
<!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>

View File

@@ -1,280 +0,0 @@
/**
* @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);

View File

@@ -1,188 +0,0 @@
<!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>

View File

@@ -31,7 +31,6 @@ limitations under the License.
<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>
@@ -41,5 +40,4 @@ limitations under the License.
<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>

View File

@@ -73,6 +73,23 @@ limitations under the License.
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'),
@@ -296,6 +313,12 @@ limitations under the License.
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);
@@ -303,6 +326,77 @@ limitations under the License.
});
});
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());
});
@@ -328,6 +422,33 @@ limitations under the License.
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;

View File

@@ -17,18 +17,71 @@
(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',
};
// Import utils methods
const {
getPluginNameFromUrl,
send,
} = window._apiUtils;
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';
/**
* Plugin-provided custom components can affect content in extension
@@ -46,6 +99,50 @@
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);
@@ -378,5 +475,211 @@
},
};
window.Plugin = Plugin;
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);