summaryrefslogtreecommitdiffstats
path: root/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-public-js-api.js
diff options
context:
space:
mode:
Diffstat (limited to 'polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-public-js-api.js')
-rw-r--r--polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-public-js-api.js394
1 files changed, 320 insertions, 74 deletions
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 ac4129f430..36a428d966 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
@@ -1,35 +1,49 @@
-// Copyright (C) 2016 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.
+/**
+ * @license
+ * Copyright (C) 2016 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 warnNotSupported = function(opt_name) {
- console.warn('Plugin API method ' + (opt_name || '') + ' is not supported');
- };
-
/**
* Hash of loaded and installed plugins, name to Plugin object.
*/
- const plugins = {};
+ const _plugins = {};
- const stubbedMethods = ['_loadedGwt', 'screen', 'settingsScreen', 'panel'];
- const GWT_PLUGIN_STUB = {};
- for (const name of stubbedMethods) {
- GWT_PLUGIN_STUB[name] = warnNotSupported.bind(null, name);
- }
+ /**
+ * 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');
@@ -37,6 +51,14 @@
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 => {
@@ -81,7 +103,33 @@
// http://www.gwtproject.org/doc/latest/DevGuideCodingBasicsJSNI.html
window.$wnd = window;
+ 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.
@@ -92,7 +140,11 @@
url.href, '— Unable to determine name.');
return;
}
- return pathname.split('/')[2];
+ // 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) {
@@ -104,13 +156,24 @@
return;
}
this.deprecated = {
+ _loadedGwt: deprecatedAPI._loadedGwt.bind(this),
install: deprecatedAPI.install.bind(this),
- popup: deprecatedAPI.popup.bind(this),
onAction: deprecatedAPI.onAction.bind(this),
+ panel: deprecatedAPI.panel.bind(this),
+ popup: deprecatedAPI.popup.bind(this),
+ screen: deprecatedAPI.screen.bind(this),
+ settingsScreen: deprecatedAPI.settingsScreen.bind(this),
};
this._url = new URL(opt_url);
this._name = getPluginNameFromUrl(this._url);
+ if (this._url.protocol === PRELOADED_PROTOCOL) {
+ // Original plugin URL is used in plugin assets URLs calculation.
+ const assetsBaseUrl = window.ASSETS_PATH ||
+ (window.location.origin + Gerrit.BaseUrlBehavior.getBaseUrl());
+ this._url = new URL(assetsBaseUrl + '/plugins/' + this._name +
+ '/static/' + this._name + '.js');
+ }
}
Plugin._sharedAPIElement = document.createElement('gr-js-api-interface');
@@ -159,6 +222,13 @@
this._name + (opt_path || '/');
};
+ Plugin.prototype.screenUrl = function(opt_screenName) {
+ const origin = this._url.origin;
+ const base = Gerrit.BaseUrlBehavior.getBaseUrl();
+ const tokenPart = opt_screenName ? '/' + opt_screenName : '';
+ return `${origin}${base}/x/${this.getPluginName()}${tokenPart}`;
+ };
+
Plugin.prototype._send = function(method, url, opt_callback, opt_payload) {
return send(method, this.url(url), opt_callback, opt_payload);
};
@@ -182,6 +252,10 @@
return Gerrit.delete(this.url(url), opt_callback);
};
+ Plugin.prototype.annotationApi = function() {
+ return new GrAnnotationActionsInterface(this);
+ };
+
Plugin.prototype.changeActions = function() {
return new GrChangeActionsInterface(this,
Plugin._sharedAPIElement.getElement(
@@ -203,7 +277,19 @@
};
Plugin.prototype.project = function() {
- return new GrProjectApi(this);
+ return new GrRepoApi(this);
+ };
+
+ Plugin.prototype.changeMetadata = function() {
+ return new GrChangeMetadataApi(this);
+ };
+
+ Plugin.prototype.admin = function() {
+ return new GrAdminApi(this);
+ };
+
+ Plugin.prototype.settings = function() {
+ return new GrSettingsApi(this);
};
/**
@@ -227,13 +313,37 @@
Plugin.prototype.popup = function(moduleName) {
if (typeof moduleName !== 'string') {
- throw new Error('deprecated, use deprecated.popup');
+ console.error('.popup(element) deprecated, use .popup(moduleName)!');
+ return;
}
const api = new GrPopupInterface(this, moduleName);
return api.open();
};
+ Plugin.prototype.panel = function() {
+ console.error('.panel() is deprecated! ' +
+ 'Use registerCustomComponent() instead.');
+ };
+
+ Plugin.prototype.settingsScreen = function() {
+ console.error('.settingsScreen() is deprecated! ' +
+ 'Use .settings() instead.');
+ };
+
+ Plugin.prototype.screen = function(screenName, opt_moduleName) {
+ if (opt_moduleName && typeof opt_moduleName !== 'string') {
+ console.error('.screen(pattern, callback) deprecated, use ' +
+ '.screen(screenName, opt_moduleName)!');
+ return;
+ }
+ return this.registerCustomComponent(
+ Gerrit._getPluginScreenName(this.getPluginName(), screenName),
+ opt_moduleName);
+ };
+
const deprecatedAPI = {
+ _loadedGwt: ()=> {},
+
install() {
console.log('Installing deprecated APIs is deprecated!');
for (const method in this.deprecated) {
@@ -262,32 +372,112 @@
}
this.on('showchange', (change, revision) => {
const details = this.changeActions().getActionDetails(action);
+ if (!details) {
+ console.warn(
+ `${this.getPluginName()} onAction error: ${action} not found!`);
+ return;
+ }
this.changeActions().addTapListener(details.__key, () => {
callback(new GrPluginActionContext(this, details, change, revision));
});
});
},
+ screen(pattern, callback) {
+ console.warn('plugin.deprecated.screen is deprecated,' +
+ ' use plugin.screen instead!');
+ if (pattern instanceof RegExp) {
+ console.error('deprecated.screen() does not support RegExp. ' +
+ 'Please use strings for patterns.');
+ return;
+ }
+ this.hook(Gerrit._getPluginScreenName(this.getPluginName(), pattern))
+ .onAttached(el => {
+ el.style.display = 'none';
+ callback({
+ body: el,
+ token: el.token,
+ onUnload: () => {},
+ setTitle: () => {},
+ setWindowTitle: () => {},
+ show: () => {
+ el.style.display = 'initial';
+ },
+ });
+ });
+ },
+
+ settingsScreen(path, menu, callback) {
+ console.warn('.settingsScreen() is deprecated! Use .settings() instead.');
+ const hook = this.settings()
+ .title(menu)
+ .token(path)
+ .module('div')
+ .build();
+ hook.onAttached(el => {
+ el.style.display = 'none';
+ const body = el.querySelector('div');
+ callback({
+ body,
+ onUnload: () => {},
+ setTitle: () => {},
+ setWindowTitle: () => {},
+ show: () => {
+ el.style.display = 'initial';
+ },
+ });
+ });
+ },
+
+ panel(extensionpoint, callback) {
+ console.warn('.panel() is deprecated! ' +
+ 'Use registerCustomComponent() instead.');
+ const endpoint = PANEL_ENDPOINTS_MAPPING[extensionpoint];
+ if (!endpoint) {
+ console.warn(`.panel ${extensionpoint} not supported!`);
+ return;
+ }
+ this.hook(endpoint).onAttached(el => callback({
+ body: el,
+ p: {
+ CHANGE_INFO: el.change,
+ REVISION_INFO: el.revision,
+ },
+ onUnload: () => {},
+ }));
+ },
};
+ flushPreinstalls();
+
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 = () => {
- for (const k of Object.keys(plugins)) {
- delete plugins[k];
+ _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];
}
};
}
- // Number of plugins to initialize, -1 means 'not yet known'.
- Gerrit._pluginsPending = -1;
-
- Gerrit._endpoints = new GrPluginEndpoints();
-
Gerrit.getPluginName = function() {
console.warn('Gerrit.getPluginName is not supported in PolyGerrit.',
'Please use plugin.getPluginName() instead.');
@@ -307,32 +497,31 @@
};
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);
+ const src = opt_src || (script && (script.src || script.baseURI));
+ const name = getPluginNameFromUrl(src);
+
if (opt_version && opt_version !== API_VERSION) {
- console.warn('Only version ' + API_VERSION +
- ' is supported in PolyGerrit. ' + opt_version + ' was given.');
- Gerrit._pluginInstalled();
+ Gerrit._pluginInstallError(`Plugin ${name} install error: only version ` +
+ API_VERSION + ' is supported in PolyGerrit. ' + opt_version +
+ ' was given.');
return;
}
- const src = opt_src || (document.currentScript &&
- (document.currentScript.src || document.currentScript.baseURI));
- const name = getPluginNameFromUrl(new URL(src));
- const existingPlugin = plugins[name];
+ const existingPlugin = _plugins[name];
const plugin = existingPlugin || new Plugin(src);
try {
callback(plugin);
- plugins[name] = plugin;
+ if (name) {
+ _plugins[name] = plugin;
+ }
+ if (!existingPlugin) {
+ Gerrit._pluginInstalled(src);
+ }
} catch (e) {
- console.warn(`${name} install failed: ${e.name}: ${e.message}`);
- }
- // Don't double count plugins that may have an html and js install.
- // TODO(beckysiegel) remove name check once name issue is resolved.
- // If there isn't a name, it's due to an issue with the polyfill for
- // html imports in Safari/Firefox. In this case, other plugin related
- // features may still be broken, but still make sure to call.
- // _pluginInstalled.
- if (!name || !existingPlugin) {
- Gerrit._pluginInstalled();
+ Gerrit._pluginInstallError(`${e.name}: ${e.message}`);
}
};
@@ -377,48 +566,105 @@
};
/**
- * Polyfill GWT API dependencies to avoid runtime exceptions when loading
- * GWT-compiled plugins.
- * @deprecated Not supported in PolyGerrit.
+ * Install "stepping stones" API for GWT-compiled plugins by default.
+ * @deprecated best effort support, will be removed with GWT UI.
*/
- Gerrit.installGwt = function() {
- Gerrit._pluginInstalled();
- return GWT_PLUGIN_STUB;
+ Gerrit.installGwt = function(url) {
+ const name = getPluginNameFromUrl(url);
+ let plugin;
+ try {
+ plugin = _plugins[name] || new Plugin(url);
+ plugin.deprecated.install();
+ Gerrit._pluginInstalled(url);
+ } catch (e) {
+ Gerrit._pluginInstallError(`${e.name}: ${e.message}`);
+ }
+ return plugin;
};
- Gerrit._allPluginsPromise = null;
- Gerrit._resolveAllPluginsLoaded = null;
-
Gerrit.awaitPluginsLoaded = function() {
- if (!Gerrit._allPluginsPromise) {
+ if (!_allPluginsPromise) {
if (Gerrit._arePluginsLoaded()) {
- Gerrit._allPluginsPromise = Promise.resolve();
+ _allPluginsPromise = Promise.resolve();
} else {
- Gerrit._allPluginsPromise = new Promise(resolve => {
- Gerrit._resolveAllPluginsLoaded = resolve;
- });
+ 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 Gerrit._allPluginsPromise;
+ 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) {
- Gerrit._pluginsPending = count;
+ _pluginsPendingCount = count;
if (Gerrit._arePluginsLoaded()) {
- document.createElement('gr-reporting').pluginsLoaded();
- if (Gerrit._resolveAllPluginsLoaded) {
- Gerrit._resolveAllPluginsLoaded();
+ getReporting().pluginsLoaded(_pluginsInstalled);
+ if (_resolveAllPluginsLoaded) {
+ _resolveAllPluginsLoaded();
}
}
};
- Gerrit._pluginInstalled = function() {
- Gerrit._setPluginsCount(Gerrit._pluginsPending - 1);
+ 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);
+ console.log(`Plugin ${name} installed.`);
+ }
};
Gerrit._arePluginsLoaded = function() {
- return Gerrit._pluginsPending === 0;
+ 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;
+ }
};
window.Gerrit = Gerrit;
+
+ // Preloaded plugins should be installed after Gerrit.install() is set,
+ // since plugin preloader substitutes Gerrit.install() temporarily.
+ installPreloadedPlugins();
})(window);