This repository has been archived by the owner on Apr 4, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 111
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Track the changes in the workspace-specific configuration files
Signed-off-by: Artem Zatsarynnyi <[email protected]>
- Loading branch information
1 parent
f85127c
commit 3409b69
Showing
6 changed files
with
329 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
171 changes: 171 additions & 0 deletions
171
extensions/eclipse-che-theia-workspace/src/browser/workspace-config-files-watcher.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,171 @@ | ||
/********************************************************************** | ||
* Copyright (c) 2021 Red Hat, Inc. | ||
* | ||
* This program and the accompanying materials are made | ||
* available under the terms of the Eclipse Public License 2.0 | ||
* which is available at https://www.eclipse.org/legal/epl-2.0/ | ||
* | ||
* SPDX-License-Identifier: EPL-2.0 | ||
***********************************************************************/ | ||
|
||
import * as jsYaml from 'js-yaml'; | ||
|
||
import { FileChangeType, FileChangesEvent } from '@theia/filesystem/lib/common/files'; | ||
import { FrontendApplication, FrontendApplicationContribution } from '@theia/core/lib/browser'; | ||
import { inject, injectable } from 'inversify'; | ||
|
||
import { ChePluginManager } from '@eclipse-che/theia-plugin-ext/lib/browser/plugin/che-plugin-manager'; | ||
import { DevfileService } from '@eclipse-che/theia-remote-api/lib/common/devfile-service'; | ||
import { FileService } from '@theia/filesystem/lib/browser/file-service'; | ||
import { MessageService } from '@theia/core/lib/common/message-service'; | ||
import URI from '@theia/core/lib/common/uri'; | ||
import { WorkspaceService } from '@theia/workspace/lib/browser/workspace-service'; | ||
|
||
import debounce = require('lodash.debounce'); | ||
|
||
/** | ||
* Abstract watcher allows to track the changes in the project-specific configuration files. | ||
* A concrete implementation can handle the changes in a specific way. | ||
*/ | ||
@injectable() | ||
export abstract class AbstractFileWatcher implements FrontendApplicationContribution { | ||
@inject(FileService) | ||
protected readonly fileService: FileService; | ||
|
||
@inject(WorkspaceService) | ||
protected readonly workspaceService: WorkspaceService; | ||
|
||
/** File name to watch, e.g. '.vscode/extensions.json'. */ | ||
protected abstract fileName: string; | ||
|
||
/** | ||
* Called when the frontend application is started. | ||
*/ | ||
async onStart(app: FrontendApplication): Promise<void> { | ||
this.trackFilesInRoots(); | ||
this.workspaceService.onWorkspaceChanged(() => this.trackFilesInRoots()); | ||
} | ||
|
||
private async trackFilesInRoots(): Promise<void> { | ||
(await this.workspaceService.roots).forEach(root => { | ||
const fileURI = root.resource.resolve(this.fileName); | ||
this.fileService.watch(fileURI); | ||
const onFileChange = async (event: FileChangesEvent) => { | ||
if (event.contains(fileURI, FileChangeType.ADDED)) { | ||
this.handleChange(fileURI, FileChangeType.ADDED); | ||
} else if (event.contains(fileURI, FileChangeType.UPDATED)) { | ||
this.handleChange(fileURI, FileChangeType.UPDATED); | ||
} | ||
}; | ||
this.fileService.onDidFilesChange(debounce(onFileChange, 1000)); | ||
}); | ||
} | ||
|
||
/** | ||
* Allows an implementor to handle a file change. | ||
* | ||
* @param fileURI an URI of the modified file | ||
* @param changeType file change type | ||
*/ | ||
protected abstract handleChange(fileURI: URI, changeType: FileChangeType): void; | ||
} | ||
|
||
@injectable() | ||
export class DevfileWatcher extends AbstractFileWatcher { | ||
protected fileName = 'devfile.yaml'; | ||
|
||
@inject(DevfileService) | ||
protected readonly devfileService: DevfileService; | ||
|
||
@inject(ChePluginManager) | ||
protected readonly chePluginManager: ChePluginManager; | ||
|
||
@inject(MessageService) | ||
protected readonly messageService: MessageService; | ||
|
||
protected async handleChange(fileURI: URI, changeType: FileChangeType): Promise<void> { | ||
const message = | ||
changeType === FileChangeType.ADDED | ||
? `A Devfile is found in ${fileURI}. Do you want to update your Workspace?` | ||
: 'Do you want to update your Workspace with the changed Devfile?'; | ||
const answer = await this.messageService.info(message, 'Yes', 'No'); | ||
if (answer === 'Yes') { | ||
this.updateWorkspaceWithDevfile(fileURI); | ||
} | ||
} | ||
|
||
/** | ||
* Updates the workspace with the given Devfile. | ||
* | ||
* @param devfileURI URI of the Devfile to update the Workspace with | ||
*/ | ||
protected async updateWorkspaceWithDevfile(devfileURI: URI): Promise<void> { | ||
const content = await this.fileService.readFile(devfileURI); | ||
const devfile = jsYaml.load(content.value.toString()); | ||
await this.devfileService.updateDevfile(devfile); | ||
await this.chePluginManager.restartWorkspace(); | ||
} | ||
} | ||
|
||
@injectable() | ||
export class ExtensionsJsonWatcher extends AbstractFileWatcher { | ||
protected fileName = '.vscode/extensions.json'; | ||
|
||
@inject(ChePluginManager) | ||
protected readonly chePluginManager: ChePluginManager; | ||
|
||
@inject(MessageService) | ||
protected readonly messageService: MessageService; | ||
|
||
protected async handleChange(fileURI: URI, changeType: FileChangeType): Promise<void> { | ||
const message = | ||
changeType === FileChangeType.ADDED | ||
? `An extensions list is found in ${fileURI}. Do you want to update your Workspace with these extensions?` | ||
: 'Do you want to update your Workspace with the changed "extensions.json"?'; | ||
const answer = await this.messageService.info(message, 'Yes', 'No'); | ||
if (answer === 'Yes') { | ||
await this.chePluginManager.restartWorkspace(); | ||
} | ||
} | ||
} | ||
|
||
@injectable() | ||
export class PluginsYamlWatcher extends AbstractFileWatcher { | ||
protected fileName = '.che/che-theia-plugins.yaml'; | ||
|
||
@inject(ChePluginManager) | ||
protected readonly chePluginManager: ChePluginManager; | ||
|
||
@inject(MessageService) | ||
protected readonly messageService: MessageService; | ||
|
||
protected async handleChange(fileURI: URI, changeType: FileChangeType): Promise<void> { | ||
const message = | ||
changeType === FileChangeType.ADDED | ||
? `A plug-ins list is found in ${fileURI}. Do you want to update your Workspace with these plug-ins?` | ||
: 'Do you want to update your Workspace with the changed "che-theia-plugins.yaml"?'; | ||
const answer = await this.messageService.info(message, 'Yes', 'No'); | ||
if (answer === 'Yes') { | ||
await this.chePluginManager.restartWorkspace(); | ||
} | ||
} | ||
} | ||
|
||
@injectable() | ||
export class TasksJsonWatcher extends AbstractFileWatcher { | ||
protected fileName = '.vscode/tasks.json'; | ||
|
||
@inject(MessageService) | ||
protected readonly messageService: MessageService; | ||
|
||
protected async handleChange(fileURI: URI, changeType: FileChangeType): Promise<void> { | ||
const answer = await this.messageService.info( | ||
'Do you want to update your Workspace with the "tasks.json" changes?', | ||
'Yes', | ||
'No' | ||
); | ||
if (answer === 'Yes') { | ||
// TODO: set the tasks to the project's attributes | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
106 changes: 106 additions & 0 deletions
106
extensions/eclipse-che-theia-workspace/tests/browser/workspace-config-files-watcher.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
/********************************************************************** | ||
* Copyright (c) 2021 Red Hat, Inc. | ||
* | ||
* This program and the accompanying materials are made | ||
* available under the terms of the Eclipse Public License 2.0 | ||
* which is available at https://www.eclipse.org/legal/epl-2.0/ | ||
* | ||
* SPDX-License-Identifier: EPL-2.0 | ||
***********************************************************************/ | ||
|
||
import 'reflect-metadata'; | ||
|
||
import { AbstractFileWatcher, DevfileWatcher } from '../../src/browser/workspace-config-files-watcher'; | ||
|
||
import { ChePluginManager } from '@eclipse-che/theia-plugin-ext/lib/browser/plugin/che-plugin-manager'; | ||
import { Container } from '@theia/core/shared/inversify'; | ||
import { DevfileService } from '@eclipse-che/theia-remote-api/lib/common/devfile-service'; | ||
import { FileService } from '@theia/filesystem/lib/browser/file-service'; | ||
import { FileStat } from '@theia/filesystem/lib/common/files'; | ||
import { FrontendApplication } from '@theia/core/lib/browser'; | ||
import { MessageService } from '@theia/core'; | ||
import URI from '@theia/core/lib/common/uri'; | ||
import { WorkspaceService } from '@theia/workspace/lib/browser'; | ||
|
||
describe('Test workspace config files watchers', function () { | ||
let container: Container; | ||
|
||
let fileService: FileService; | ||
let workspaceService: WorkspaceService; | ||
|
||
const fileServiceWatchMethod = jest.fn(); | ||
const workspaceServiceOnWorkspaceChangedMethod = jest.fn(); | ||
|
||
beforeEach(() => { | ||
jest.restoreAllMocks(); | ||
jest.resetAllMocks(); | ||
|
||
container = new Container(); | ||
|
||
fileService = ({ | ||
watch: fileServiceWatchMethod, | ||
} as unknown) as FileService; | ||
|
||
workspaceService = ({ | ||
onWorkspaceChanged: workspaceServiceOnWorkspaceChangedMethod, | ||
} as unknown) as WorkspaceService; | ||
|
||
container.bind(FileService).toConstantValue(fileService); | ||
container.bind(WorkspaceService).toConstantValue(workspaceService); | ||
}); | ||
|
||
describe('Test DevfileWatcher', function () { | ||
let devfileWatcher: AbstractFileWatcher; | ||
|
||
const devfileServiceUpdateDevfileMethod = jest.fn(); | ||
const chePluginManagerRestartWorkspaceMethod = jest.fn(); | ||
const messageServiceInfoMethod = jest.fn(); | ||
|
||
beforeEach(() => { | ||
const devfileService = ({ | ||
updateDevfile: devfileServiceUpdateDevfileMethod, | ||
} as unknown) as DevfileService; | ||
|
||
const chePluginManager = ({ | ||
restartWorkspace: chePluginManagerRestartWorkspaceMethod, | ||
} as unknown) as ChePluginManager; | ||
|
||
const messageService = ({ | ||
info: messageServiceInfoMethod, | ||
} as unknown) as MessageService; | ||
|
||
container.bind(DevfileService).toConstantValue(devfileService); | ||
container.bind(ChePluginManager).toConstantValue(chePluginManager); | ||
container.bind(MessageService).toConstantValue(messageService); | ||
container.bind(DevfileWatcher).toSelf().inSingletonScope(); | ||
devfileWatcher = container.get(DevfileWatcher); | ||
}); | ||
|
||
test('shouldWatch', async () => { | ||
const resolveFn = jest.fn(); | ||
const resource = ({ | ||
resolve: resolveFn, | ||
} as unknown) as URI; | ||
resolveFn.mockReturnValue(resource); | ||
|
||
const roots: FileStat[] = [ | ||
{ | ||
name: 'testFile', | ||
isDirectory: false, | ||
isFile: true, | ||
isSymbolicLink: false, | ||
resource: resource, | ||
}, | ||
]; | ||
|
||
Object.defineProperty(workspaceService, 'roots', { | ||
get: jest.fn(() => roots), | ||
}); | ||
|
||
await devfileWatcher.onStart({} as FrontendApplication); | ||
|
||
expect(fileServiceWatchMethod.mock.calls.length).toEqual(1); | ||
expect(fileServiceWatchMethod).toHaveBeenCalledWith(resource); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
/******************************************************************************** | ||
* Copyright (C) 2021 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 | ||
********************************************************************************/ | ||
|
||
'use strict'; | ||
|
||
module.exports = {}; |