| /** |
| * @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. |
| */ |
| |
| /** |
| * This defines the Gerrit instance. All methods directly attached to Gerrit |
| * should be defined or linked here. |
| */ |
| import {getPluginLoader, PluginOptionMap} from './gr-plugin-loader'; |
| import {send} from './gr-api-utils'; |
| import {getAppContext, AppContext} from '../../../services/app-context'; |
| import {PluginApi} from '../../../api/plugin'; |
| import {AuthService} from '../../../services/gr-auth/gr-auth'; |
| import {RestApiService} from '../../../services/gr-rest-api/gr-rest-api'; |
| import {ReportingService} from '../../../services/gr-reporting/gr-reporting'; |
| import {HttpMethod} from '../../../constants/constants'; |
| import {RequestPayload} from '../../../types/common'; |
| import { |
| EventCallback, |
| EventEmitterService, |
| } from '../../../services/gr-event-interface/gr-event-interface'; |
| import {GerritNav} from '../../core/gr-navigation/gr-navigation'; |
| import {Gerrit} from '../../../api/gerrit'; |
| import {fontStyles} from '../../../styles/gr-font-styles'; |
| import {formStyles} from '../../../styles/gr-form-styles'; |
| import {menuPageStyles} from '../../../styles/gr-menu-page-styles'; |
| import {spinnerStyles} from '../../../styles/gr-spinner-styles'; |
| import {subpageStyles} from '../../../styles/gr-subpage-styles'; |
| import {tableStyles} from '../../../styles/gr-table-styles'; |
| import {assertIsDefined} from '../../../utils/common-util'; |
| |
| /** |
| * These are the methods and properties that are exposed explicitly in the |
| * public global `Gerrit` interface. In reality JavaScript plugins do depend |
| * on some of this "internal" stuff. But we want to convert plugins to |
| * TypeScript one by one and while doing that remove those dependencies. |
| */ |
| export interface GerritInternal extends EventEmitterService, Gerrit { |
| css(rule: string): string; |
| install( |
| callback: (plugin: PluginApi) => void, |
| opt_version?: string, |
| src?: string |
| ): void; |
| getLoggedIn(): Promise<boolean>; |
| get(url: string, callback?: (response: unknown) => void): void; |
| post( |
| url: string, |
| payload?: RequestPayload, |
| callback?: (response: unknown) => void |
| ): void; |
| put( |
| url: string, |
| payload?: RequestPayload, |
| callback?: (response: unknown) => void |
| ): void; |
| delete(url: string, callback?: (response: unknown) => void): void; |
| isPluginLoaded(pathOrUrl: string): boolean; |
| awaitPluginsLoaded(): Promise<unknown>; |
| _loadPlugins(plugins: string[], opts: PluginOptionMap): void; |
| _arePluginsLoaded(): boolean; |
| _isPluginEnabled(pathOrUrl: string): boolean; |
| _isPluginLoaded(pathOrUrl: string): boolean; |
| _customStyleSheet?: CSSStyleSheet; |
| |
| // exposed methods |
| Nav: typeof GerritNav; |
| Auth: AuthService; |
| } |
| |
| export function initGerritPluginApi(appContext: AppContext) { |
| window.Gerrit = window.Gerrit ?? new GerritImpl(appContext); |
| } |
| |
| export function _testOnly_getGerritInternalPluginApi(): GerritInternal { |
| if (!window.Gerrit) throw new Error('initGerritPluginApi was not called'); |
| return window.Gerrit as GerritInternal; |
| } |
| |
| export function deprecatedDelete( |
| url: string, |
| callback?: (response: Response) => void |
| ) { |
| console.warn('.delete() is deprecated! Use plugin.restApi().delete()'); |
| return getAppContext() |
| .restApiService.send(HttpMethod.DELETE, url) |
| .then(response => { |
| if (response.status !== 204) { |
| return response.text().then(text => { |
| if (text) { |
| return Promise.reject(new Error(text)); |
| } else { |
| return Promise.reject(new Error(`${response.status}`)); |
| } |
| }); |
| } |
| if (callback) callback(response); |
| return response; |
| }); |
| } |
| |
| const fakeApi = { |
| getPluginName: () => 'global', |
| }; |
| |
| /** |
| * TODO(brohlfs): Reduce this step by step until it only contains install(). |
| */ |
| class GerritImpl implements GerritInternal { |
| _customStyleSheet?: CSSStyleSheet; |
| |
| public readonly Nav = GerritNav; |
| |
| public readonly Auth: AuthService; |
| |
| private readonly reportingService: ReportingService; |
| |
| private readonly eventEmitter: EventEmitterService; |
| |
| private readonly restApiService: RestApiService; |
| |
| public readonly styles = { |
| font: fontStyles, |
| form: formStyles, |
| menuPage: menuPageStyles, |
| spinner: spinnerStyles, |
| subPage: subpageStyles, |
| table: tableStyles, |
| }; |
| |
| constructor(appContext: AppContext) { |
| this.Auth = appContext.authService; |
| this.reportingService = appContext.reportingService; |
| this.eventEmitter = appContext.eventEmitter; |
| this.restApiService = appContext.restApiService; |
| assertIsDefined(this.reportingService, 'reportingService'); |
| assertIsDefined(this.eventEmitter, 'eventEmitter'); |
| assertIsDefined(this.restApiService, 'restApiService'); |
| } |
| |
| finalize() {} |
| |
| /** |
| * @deprecated Use plugin.styles().css(rulesStr) instead. Please, consult |
| * the documentation how to replace it accordingly. |
| */ |
| css(rulesStr: string) { |
| this.reportingService.trackApi(fakeApi, 'global', 'css'); |
| console.warn( |
| 'Gerrit.css(rulesStr) is deprecated!', |
| 'Use plugin.styles().css(rulesStr)' |
| ); |
| if (!this._customStyleSheet) { |
| const styleEl = document.createElement('style'); |
| document.head.appendChild(styleEl); |
| this._customStyleSheet = styleEl.sheet!; |
| } |
| |
| const name = `__pg_js_api_class_${this._customStyleSheet.cssRules.length}`; |
| this._customStyleSheet.insertRule('.' + name + '{' + rulesStr + '}', 0); |
| return name; |
| } |
| |
| install( |
| callback: (plugin: PluginApi) => void, |
| version?: string, |
| src?: string |
| ) { |
| getPluginLoader().install(callback, version, src); |
| } |
| |
| getLoggedIn() { |
| this.reportingService.trackApi(fakeApi, 'global', 'getLoggedIn'); |
| console.warn( |
| 'Gerrit.getLoggedIn() is deprecated! ' + |
| 'Use plugin.restApi().getLoggedIn()' |
| ); |
| return this.restApiService.getLoggedIn(); |
| } |
| |
| get(url: string, callback?: (response: unknown) => void) { |
| this.reportingService.trackApi(fakeApi, 'global', 'get'); |
| console.warn('.get() is deprecated! Use plugin.restApi().get()'); |
| send(this.restApiService, HttpMethod.GET, url, callback); |
| } |
| |
| post( |
| url: string, |
| payload?: RequestPayload, |
| callback?: (response: unknown) => void |
| ) { |
| this.reportingService.trackApi(fakeApi, 'global', 'post'); |
| console.warn('.post() is deprecated! Use plugin.restApi().post()'); |
| send(this.restApiService, HttpMethod.POST, url, callback, payload); |
| } |
| |
| put( |
| url: string, |
| payload?: RequestPayload, |
| callback?: (response: unknown) => void |
| ) { |
| this.reportingService.trackApi(fakeApi, 'global', 'put'); |
| console.warn('.put() is deprecated! Use plugin.restApi().put()'); |
| send(this.restApiService, HttpMethod.PUT, url, callback, payload); |
| } |
| |
| delete(url: string, callback?: (response: Response) => void) { |
| this.reportingService.trackApi(fakeApi, 'global', 'delete'); |
| deprecatedDelete(url, callback); |
| } |
| |
| awaitPluginsLoaded() { |
| this.reportingService.trackApi(fakeApi, 'global', 'awaitPluginsLoaded'); |
| return getPluginLoader().awaitPluginsLoaded(); |
| } |
| |
| // TODO(taoalpha): consider removing these proxy methods |
| // and using getPluginLoader() directly |
| _loadPlugins(plugins: string[] = []) { |
| this.reportingService.trackApi(fakeApi, 'global', '_loadPlugins'); |
| getPluginLoader().loadPlugins(plugins); |
| } |
| |
| _arePluginsLoaded() { |
| this.reportingService.trackApi(fakeApi, 'global', '_arePluginsLoaded'); |
| return getPluginLoader().arePluginsLoaded(); |
| } |
| |
| _isPluginEnabled(pathOrUrl: string) { |
| this.reportingService.trackApi(fakeApi, 'global', '_isPluginEnabled'); |
| return getPluginLoader().isPluginEnabled(pathOrUrl); |
| } |
| |
| isPluginLoaded(pathOrUrl: string) { |
| return this._isPluginLoaded(pathOrUrl); |
| } |
| |
| _isPluginLoaded(pathOrUrl: string) { |
| this.reportingService.trackApi(fakeApi, 'global', '_isPluginLoaded'); |
| return getPluginLoader().isPluginLoaded(pathOrUrl); |
| } |
| |
| /** |
| * Enabling EventEmitter interface on Gerrit. |
| * |
| * This will enable to signal across different parts of js code without relying on DOM, |
| * including core to core, plugin to plugin and also core to plugin. |
| * |
| * @example |
| * |
| * // Emit this event from pluginA |
| * Gerrit.install(pluginA => { |
| * fetch("some-api").then(() => { |
| * Gerrit.on("your-special-event", {plugin: pluginA}); |
| * }); |
| * }); |
| * |
| * // Listen on your-special-event from pluginB |
| * Gerrit.install(pluginB => { |
| * Gerrit.on("your-special-event", ({plugin}) => { |
| * // do something, plugin is pluginA |
| * }); |
| * }); |
| */ |
| addListener(eventName: string, cb: EventCallback) { |
| this.reportingService.trackApi(fakeApi, 'global', 'addListener'); |
| return this.eventEmitter.addListener(eventName, cb); |
| } |
| |
| // eslint-disable-next-line @typescript-eslint/no-explicit-any |
| dispatch(eventName: string, detail: any) { |
| this.reportingService.trackApi(fakeApi, 'global', 'dispatch'); |
| return this.eventEmitter.dispatch(eventName, detail); |
| } |
| |
| // eslint-disable-next-line @typescript-eslint/no-explicit-any |
| emit(eventName: string, detail: any) { |
| this.reportingService.trackApi(fakeApi, 'global', 'emit'); |
| return this.eventEmitter.emit(eventName, detail); |
| } |
| |
| off(eventName: string, cb: EventCallback) { |
| this.reportingService.trackApi(fakeApi, 'global', 'off'); |
| this.eventEmitter.off(eventName, cb); |
| } |
| |
| on(eventName: string, cb: EventCallback) { |
| this.reportingService.trackApi(fakeApi, 'global', 'on'); |
| return this.eventEmitter.on(eventName, cb); |
| } |
| |
| once(eventName: string, cb: EventCallback) { |
| this.reportingService.trackApi(fakeApi, 'global', 'once'); |
| return this.eventEmitter.once(eventName, cb); |
| } |
| |
| removeAllListeners(eventName: string) { |
| this.reportingService.trackApi(fakeApi, 'global', 'removeAllListeners'); |
| this.eventEmitter.removeAllListeners(eventName); |
| } |
| |
| removeListener(eventName: string, cb: EventCallback) { |
| this.reportingService.trackApi(fakeApi, 'global', 'removeListener'); |
| this.eventEmitter.removeListener(eventName, cb); |
| } |
| } |