From 727b6c5a1e650a1357652610b48482a9cc5c9bae Mon Sep 17 00:00:00 2001 From: Vitalii Parfonov Date: Fri, 10 Jul 2020 16:10:34 +0300 Subject: [PATCH] Rsync events (#771) * Add event tracking and show progress in status bar Signed-off-by: Vitalii Parfonov --- che-theia-init-sources.yml | 1 + .../.gitignore | 1 + .../README.md | 7 ++ .../package.json | 30 +++++ .../che-theia-file-sync-frontend-module.ts | 24 ++++ .../src/browser/sync-process-tracker.ts | 115 ++++++++++++++++++ .../tsconfig.json | 14 +++ 7 files changed, 192 insertions(+) create mode 100644 extensions/eclipse-che-theia-file-sync-tracker/.gitignore create mode 100644 extensions/eclipse-che-theia-file-sync-tracker/README.md create mode 100644 extensions/eclipse-che-theia-file-sync-tracker/package.json create mode 100644 extensions/eclipse-che-theia-file-sync-tracker/src/browser/che-theia-file-sync-frontend-module.ts create mode 100644 extensions/eclipse-che-theia-file-sync-tracker/src/browser/sync-process-tracker.ts create mode 100644 extensions/eclipse-che-theia-file-sync-tracker/tsconfig.json diff --git a/che-theia-init-sources.yml b/che-theia-init-sources.yml index 612e985aa..af6e8f49a 100644 --- a/che-theia-init-sources.yml +++ b/che-theia-init-sources.yml @@ -14,6 +14,7 @@ sources: - extensions/eclipse-che-theia-git-provisioner - extensions/eclipse-che-theia-logging - extensions/eclipse-che-theia-messaging + - extensions/eclipse-che-theia-file-sync-tracker - extensions/eclipse-che-theia-cli-endpoint plugins: - plugins/containers-plugin diff --git a/extensions/eclipse-che-theia-file-sync-tracker/.gitignore b/extensions/eclipse-che-theia-file-sync-tracker/.gitignore new file mode 100644 index 000000000..c3af85790 --- /dev/null +++ b/extensions/eclipse-che-theia-file-sync-tracker/.gitignore @@ -0,0 +1 @@ +lib/ diff --git a/extensions/eclipse-che-theia-file-sync-tracker/README.md b/extensions/eclipse-che-theia-file-sync-tracker/README.md new file mode 100644 index 000000000..0ee2f148d --- /dev/null +++ b/extensions/eclipse-che-theia-file-sync-tracker/README.md @@ -0,0 +1,7 @@ +# theia-file-sync-tracker extension + +This extension tracks events of file synchronization progress during workspace startup. + +### How does the extension work? + +If a workspace is configured to use asynchronous storage, extension will open websocket connection to special service and show progress in status bar. In case some error, error message will be shown in status bar, the popup with error details will disappear after mouse click. After successfully file synchronization, status bar also will be updated with corresponding message, in this case message will disappear in 5 seconds. diff --git a/extensions/eclipse-che-theia-file-sync-tracker/package.json b/extensions/eclipse-che-theia-file-sync-tracker/package.json new file mode 100644 index 000000000..86564e43f --- /dev/null +++ b/extensions/eclipse-che-theia-file-sync-tracker/package.json @@ -0,0 +1,30 @@ +{ + "name": "@eclipse-che/theia-file-sync-tracker", + "keywords": [ + "theia-extension" + ], + "version": "0.0.1", + "files": [ + "lib", + "src" + ], + "dependencies": { + "@theia/core": "next", + "@eclipse-che/theia-plugin-ext": "^0.0.1" + }, + "scripts": { + "prepare": "yarn clean && yarn build", + "clean": "rimraf lib", + "format": "tsfmt -r --useTsfmt ../../configs/tsfmt.json", + "lint": "eslint --cache=true --no-error-on-unmatched-pattern=true \"{src,test}/**/*.{ts,tsx}\"", + "compile": "tsc", + "build": "concurrently -n \"format,lint,compile\" -c \"red,green,blue\" \"yarn format\" \"yarn lint\" \"yarn compile\"", + "watch": "tsc -w" + }, + "license": "EPL-2.0", + "theiaExtensions": [ + { + "frontend": "lib/browser/che-theia-file-sync-frontend-module" + } + ] +} diff --git a/extensions/eclipse-che-theia-file-sync-tracker/src/browser/che-theia-file-sync-frontend-module.ts b/extensions/eclipse-che-theia-file-sync-tracker/src/browser/che-theia-file-sync-frontend-module.ts new file mode 100644 index 000000000..8fd7f3df1 --- /dev/null +++ b/extensions/eclipse-che-theia-file-sync-tracker/src/browser/che-theia-file-sync-frontend-module.ts @@ -0,0 +1,24 @@ +/******************************************************************************** + * Copyright (C) 2020 Red Hat, Inc. and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ + +import { ContainerModule } from 'inversify'; +import { FrontendApplicationContribution } from '@theia/core/lib/browser'; +import { SyncProcessTracker } from './sync-process-tracker'; + +export default new ContainerModule(bind => { + + bind(FrontendApplicationContribution).to(SyncProcessTracker).inSingletonScope(); +}); diff --git a/extensions/eclipse-che-theia-file-sync-tracker/src/browser/sync-process-tracker.ts b/extensions/eclipse-che-theia-file-sync-tracker/src/browser/sync-process-tracker.ts new file mode 100644 index 000000000..825e6b11d --- /dev/null +++ b/extensions/eclipse-che-theia-file-sync-tracker/src/browser/sync-process-tracker.ts @@ -0,0 +1,115 @@ +/******************************************************************************** + * Copyright (C) 2020 Red Hat, Inc. and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ + +import { injectable, inject } from 'inversify'; +import { FrontendApplicationContribution } from '@theia/core/lib/browser'; +import { MessageService } from '@theia/core/lib/common'; +import { StatusBar, StatusBarAlignment, StatusBarEntry } from '@theia/core/lib/browser/status-bar/status-bar'; +import { CheApiService } from '@eclipse-che/theia-plugin-ext/lib/common/che-protocol'; +import URI from '@theia/core/lib/common/uri'; +import ReconnectingWebSocket from 'reconnecting-websocket'; +import { DisposableCollection, Disposable } from '@theia/core'; + +@injectable() +export class SyncProcessTracker implements FrontendApplicationContribution { + + @inject(StatusBar) + private statusBar: StatusBar; + @inject(MessageService) + protected readonly messageService: MessageService; + @inject(CheApiService) + protected cheApiService: CheApiService; + private readonly ID = 'file-synchronization-indicator-id'; + protected readonly statusBarDisposable = new DisposableCollection(); + + private readonly tooltip = 'File synchronization progress'; + private readonly fail = 'File Sync: Failed'; + private readonly done = 'File Sync: Done'; + + async initialize(): Promise { + this.connect(await this.getSyncServiceURL()); + } + + async getSyncServiceURL(): Promise { + const server = await this.cheApiService.findUniqueServerByAttribute('type', 'rsync'); + if (server) { + return new URI(server.url).resolve('track').toString(); + } else { + return Promise.reject(new Error('Server rsync not found')); + } + } + + private connect(endpointAddress: string): void { + const websocket = new ReconnectingWebSocket(endpointAddress, undefined, { + maxRetries: Infinity, + }); + websocket.onerror = err => { + console.log(err); + this.messageService.info('Can\'t establish connetion to rsync server. Cause:' + err); + }; + websocket.onmessage = ev => { + this.updateStatusBar(ev.data, websocket); + }; + websocket.onclose = () => console.log('File synchronization tracking connection closed'); + websocket.onopen = () => console.log('File synchronization tracking connection opened'); + }; + + private updateStatusBar(data: string, websocket: ReconnectingWebSocket): void { + this.statusBarDisposable.dispose(); + const obj = JSON.parse(data); + if (obj.state === 'DONE') { + websocket.close(); + this.setStatusBarEntry({ + text: this.done, + tooltip: this.tooltip, + alignment: StatusBarAlignment.LEFT, + onclick: (e: MouseEvent) => this.messageService.info(this.done), + priority: 150 + }); + (async () => { + await this.delay(5000); // hide message in 5 sec + this.statusBarDisposable.dispose(); + })(); + } else if (obj.state === 'ERROR') { + websocket.close(); + this.setStatusBarEntry({ + text: this.fail, + tooltip: this.tooltip, + alignment: StatusBarAlignment.LEFT, + onclick: (e: MouseEvent) => this.messageService.error(obj.info), + priority: 150 + }); + } else { + const msg = `File Sync: ${obj.info}`; + this.setStatusBarEntry({ + text: msg, + tooltip: this.tooltip, + alignment: StatusBarAlignment.LEFT, + onclick: (e: MouseEvent) => this.messageService.info(msg), + priority: 150 + }); + } + } + + private setStatusBarEntry(entry: StatusBarEntry): void { + this.statusBar.setElement(this.ID, entry); + this.statusBarDisposable.push(Disposable.create(() => this.statusBar.removeElement(this.ID))); + } + + private delay(ms: number): Promise { + return new Promise(resolve => setTimeout(resolve, ms)); + } +} diff --git a/extensions/eclipse-che-theia-file-sync-tracker/tsconfig.json b/extensions/eclipse-che-theia-file-sync-tracker/tsconfig.json new file mode 100644 index 000000000..a6a832536 --- /dev/null +++ b/extensions/eclipse-che-theia-file-sync-tracker/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": "../../configs/base.tsconfig.json", + "compilerOptions": { + "lib": [ + "es6", + "dom" + ], + "rootDir": "src", + "outDir": "lib" + }, + "include": [ + "src" + ] +}