From be3a56c08de555f931e12da78378d665f39032cf Mon Sep 17 00:00:00 2001 From: Anna Shumilova Date: Mon, 22 Apr 2019 13:22:35 +0300 Subject: [PATCH] Add devfile support in UD Signed-off-by: Anna Shumilova --- .../recent-workspaces.controller.ts | 6 +- .../recent-workspaces/recent-workspaces.html | 2 +- .../create-workspace.controller.ts | 2 +- .../create-workspace.service.ts | 54 +++-- .../list-workspaces.controller.ts | 5 +- .../workspace-item.controller.ts | 8 +- .../workspace-item/workspace-item.html | 10 +- .../workspace-devfile-editor.controller.ts | 116 ++++++++++ .../workspace-devfile-editor.directive.ts | 44 ++++ .../devfile/workspace-devfile-editor.html | 23 ++ .../devfile/workspace-devfile-editor.styl | 26 +++ .../export-workspace-dialog.controller.ts | 9 +- .../dialog/export-workspace-dialog.styl | 8 +- .../workspace-details-config.ts | 4 + .../workspace-details.controller.ts | 58 +++-- .../workspace-details.directive.spec.ts | 8 + .../workspace-details/workspace-details.html | 43 +++- .../workspace-editors.controller.ts | 41 ++-- .../workspace-editors.directive.ts | 2 +- .../workspace-details-overview.controller.ts | 43 +++- .../workspace-details-overview.html | 2 +- .../workspace-plugins.controller.ts | 39 ++-- .../workspace-plugins.directive.ts | 2 +- .../workspace-details-projects.controller.ts | 19 +- .../workspace-details-projects.service.ts | 2 +- .../src/app/workspaces/workspaces.service.ts | 15 +- .../api/workspace/che-workspace.factory.ts | 30 ++- .../api/workspace/workspace-data-manager.ts | 218 ++++++++++++++++++ dashboard/src/components/typings/che.d.ts | 12 +- 29 files changed, 703 insertions(+), 148 deletions(-) create mode 100644 dashboard/src/app/workspaces/workspace-details/devfile/workspace-devfile-editor.controller.ts create mode 100644 dashboard/src/app/workspaces/workspace-details/devfile/workspace-devfile-editor.directive.ts create mode 100644 dashboard/src/app/workspaces/workspace-details/devfile/workspace-devfile-editor.html create mode 100644 dashboard/src/app/workspaces/workspace-details/devfile/workspace-devfile-editor.styl create mode 100644 dashboard/src/components/api/workspace/workspace-data-manager.ts diff --git a/dashboard/src/app/navbar/recent-workspaces/recent-workspaces.controller.ts b/dashboard/src/app/navbar/recent-workspaces/recent-workspaces.controller.ts index e657da9035d..857d006dfcb 100644 --- a/dashboard/src/app/navbar/recent-workspaces/recent-workspaces.controller.ts +++ b/dashboard/src/app/navbar/recent-workspaces/recent-workspaces.controller.ts @@ -226,7 +226,7 @@ export class NavbarRecentWorkspacesController { */ getWorkspaceName(workspaceId: string): string { let workspace = this.cheWorkspace.getWorkspaceById(workspaceId); - return workspace ? workspace.config.name : 'unknown'; + return workspace ? this.cheWorkspace.getWorkspaceDataManager().getName(workspace) : 'unknown'; } /** @@ -256,7 +256,7 @@ export class NavbarRecentWorkspacesController { * @returns {string} */ getIdeLink(workspace: che.IWorkspace): string { - return '#/ide/' + (workspace ? (workspace.namespace + '/' + workspace.config.name) : 'unknown'); + return '#/ide/' + (workspace ? (workspace.namespace + '/' + this.cheWorkspace.getWorkspaceDataManager().getName(workspace)) : 'unknown'); } /** @@ -265,7 +265,7 @@ export class NavbarRecentWorkspacesController { * @returns {string} */ getWorkspaceDetailsLink(workspace: che.IWorkspace): string { - return '#/workspace/' + workspace.namespace + '/' + workspace.config.name; + return '#/workspace/' + workspace.namespace + '/' + this.cheWorkspace.getWorkspaceDataManager().getName(workspace); } /** diff --git a/dashboard/src/app/navbar/recent-workspaces/recent-workspaces.html b/dashboard/src/app/navbar/recent-workspaces/recent-workspaces.html index f98e7d2e799..2cab6dde761 100644 --- a/dashboard/src/app/navbar/recent-workspaces/recent-workspaces.html +++ b/dashboard/src/app/navbar/recent-workspaces/recent-workspaces.html @@ -39,7 +39,7 @@ - {{workspace.config.name}} + {{navbarRecentWorkspacesController.getWorkspaceName(workspace.id)}} diff --git a/dashboard/src/app/workspaces/create-workspace/create-workspace.controller.ts b/dashboard/src/app/workspaces/create-workspace/create-workspace.controller.ts index 3ffb26e07ae..041f7f64f7e 100644 --- a/dashboard/src/app/workspaces/create-workspace/create-workspace.controller.ts +++ b/dashboard/src/app/workspaces/create-workspace/create-workspace.controller.ts @@ -358,7 +358,7 @@ export class CreateWorkspaceController { } let attributes = {stackId: this.stack.id}; - return this.createWorkspaceSvc.createWorkspace(this.workspaceConfig, attributes); + return this.createWorkspaceSvc.createWorkspaceFromConfig(this.workspaceConfig, attributes); } /** diff --git a/dashboard/src/app/workspaces/create-workspace/create-workspace.service.ts b/dashboard/src/app/workspaces/create-workspace/create-workspace.service.ts index 2627d45cb58..3673ff17f47 100644 --- a/dashboard/src/app/workspaces/create-workspace/create-workspace.service.ts +++ b/dashboard/src/app/workspaces/create-workspace/create-workspace.service.ts @@ -156,20 +156,13 @@ export class CreateWorkspaceSvc { return defer.promise; } - /** - * Creates a workspace from config. - * - * @param {che.IWorkspaceConfig} workspaceConfig the config of workspace which will be created - * @param {any} attributes the attributes of the workspace - * @returns {ng.IPromise} - */ - createWorkspace(workspaceConfig: che.IWorkspaceConfig, attributes: any): ng.IPromise { + createWorkspaceFromConfig(workspaceConfig: che.IWorkspaceConfig, attributes: any): ng.IPromise { const namespaceId = this.namespaceSelectorSvc.getNamespaceId(), projectTemplates = this.projectSourceSelectorService.getProjectTemplates(); return this.checkEditingProgress().then(() => { workspaceConfig.projects = projectTemplates; - this.addProjectCommands(workspaceConfig, projectTemplates); + this.addProjectCommands({config: workspaceConfig}, projectTemplates); return this.cheWorkspace.createWorkspaceFromConfig(namespaceId, workspaceConfig, attributes).then((workspace: che.IWorkspace) => { return this.cheWorkspace.fetchWorkspaces().then(() => this.cheWorkspace.getWorkspaceById(workspace.id)); }) @@ -196,6 +189,39 @@ export class CreateWorkspaceSvc { }); } + /*createWorkspaceFromDevfile(workspaceDevfile: che.IWorkspaceDevfile, attributes: any): ng.IPromise { + const namespaceId = this.namespaceSelectorSvc.getNamespaceId(), + projectTemplates = this.projectSourceSelectorService.getProjectTemplates(); + + return this.checkEditingProgress().then(() => { + workspaceDevfile.projects = projectTemplates; + this.addProjectCommands({devfile: workspaceDevfile}, projectTemplates); + return this.cheWorkspace.createWorkspaceFromDevfile(namespaceId, workspaceDevfile, attributes).then((workspace: che.IWorkspace) => { + return this.cheWorkspace.fetchWorkspaces().then(() => this.cheWorkspace.getWorkspaceById(workspace.id)); + }) + .then((workspace: che.IWorkspace) => { + this.projectSourceSelectorService.clearTemplatesList(); + const workspaces = this.cheWorkspace.getWorkspaces(); + if (workspaces.findIndex((_workspace: che.IWorkspace) => { + return _workspace.id === workspace.id; + }) === -1) { + workspaces.push(workspace); + } + this.cheWorkspace.startUpdateWorkspaceStatus(workspace.id); + + return workspace; + }, (error: any) => { + let errorMessage = 'Creation workspace failed.'; + if (error && error.data && error.data.message) { + errorMessage = error.data.message; + } + this.cheNotification.showError(errorMessage); + + return this.$q.reject(error); + }); + }); + }*/ + /** * Show confirmation dialog when project editing is not completed. * @@ -233,19 +259,17 @@ export class CreateWorkspaceSvc { } /** - * Adds commands from the bunch of project templates to provided workspace config. + * Adds commands from the bunch of project templates to provided workspace. * - * @param {che.IWorkspaceConfig} workspaceConfig workspace config + * @param {che.IWorkspace} workspace * @param {Array} projectTemplates the list of project templates */ - addProjectCommands(workspaceConfig: che.IWorkspaceConfig, projectTemplates: Array): void { - workspaceConfig.commands = workspaceConfig.commands || []; - + addProjectCommands(workspace: che.IWorkspace, projectTemplates: Array): void { projectTemplates.forEach((template: che.IProjectTemplate) => { let projectName = template.name; template.commands.forEach((command: any) => { command.name = projectName + ':' + command.name; - workspaceConfig.commands.push(command); + this.cheWorkspace.getWorkspaceDataManager().addCommand(workspace, command); }); }); } diff --git a/dashboard/src/app/workspaces/list-workspaces/list-workspaces.controller.ts b/dashboard/src/app/workspaces/list-workspaces/list-workspaces.controller.ts index 12de8bebe14..eadda87f134 100644 --- a/dashboard/src/app/workspaces/list-workspaces/list-workspaces.controller.ts +++ b/dashboard/src/app/workspaces/list-workspaces/list-workspaces.controller.ts @@ -164,7 +164,8 @@ export class ListWorkspacesCtrl { return this.$q.when(); } let message = error.data && error.data.message ? ' Reason: ' + error.data.message : ''; - this.cheNotification.showError('Failed to retrieve workspace ' + workspace.config.name + ' data.' + message) ; + let workspaceName = this.cheWorkspace.getWorkspaceDataManager().getName(workspace); + this.cheNotification.showError('Failed to retrieve workspace ' + workspaceName + ' data.' + message) ; return this.$q.reject(error); }) .then(() => { @@ -249,7 +250,7 @@ export class ListWorkspacesCtrl { if (!workspace) { return; } - workspaceName = workspace.config.name; + workspaceName = this.cheWorkspace.getWorkspaceDataManager().getName(workspace); let stoppedStatusPromise = this.cheWorkspace.fetchStatusChange(workspaceId, 'STOPPED'); // stop workspace if it's status is RUNNING diff --git a/dashboard/src/app/workspaces/list-workspaces/workspace-item/workspace-item.controller.ts b/dashboard/src/app/workspaces/list-workspaces/workspace-item/workspace-item.controller.ts index 57ae0c29395..fbb1cf568ed 100644 --- a/dashboard/src/app/workspaces/list-workspaces/workspace-item/workspace-item.controller.ts +++ b/dashboard/src/app/workspaces/list-workspaces/workspace-item/workspace-item.controller.ts @@ -29,6 +29,7 @@ export class WorkspaceItemCtrl { workspacesService: WorkspacesService; workspace: che.IWorkspace; + workspaceName: string; /** * Default constructor that is using resource @@ -41,6 +42,7 @@ export class WorkspaceItemCtrl { this.lodash = lodash; this.cheWorkspace = cheWorkspace; this.workspacesService = workspacesService; + this.workspaceName = this.cheWorkspace.getWorkspaceDataManager().getName(this.workspace); } /** @@ -57,7 +59,7 @@ export class WorkspaceItemCtrl { * @param tab {string} */ redirectToWorkspaceDetails(tab?: string): void { - this.$location.path('/workspace/' + this.workspace.namespace + '/' + this.workspace.config.name).search({tab: tab ? tab : 'Overview'}); + this.$location.path('/workspace/' + this.workspace.namespace + '/' + this.workspaceName).search({tab: tab ? tab : 'Overview'}); } getDefaultEnvironment(workspace: che.IWorkspace): che.IWorkspaceEnvironment { @@ -68,6 +70,10 @@ export class WorkspaceItemCtrl { } getMemoryLimit(workspace: che.IWorkspace): string { + if (!workspace.config && workspace.devfile) { + return '-'; + } + let environment = this.getDefaultEnvironment(workspace); if (environment) { let limits = this.lodash.pluck(environment.machines, 'attributes.memoryLimitBytes'); diff --git a/dashboard/src/app/workspaces/list-workspaces/workspace-item/workspace-item.html b/dashboard/src/app/workspaces/list-workspaces/workspace-item/workspace-item.html index bb1632c28bc..64a581cd77c 100644 --- a/dashboard/src/app/workspaces/list-workspaces/workspace-item/workspace-item.html +++ b/dashboard/src/app/workspaces/list-workspaces/workspace-item/workspace-item.html @@ -13,7 +13,7 @@ -->
Name
-
+
- {{workspaceItemCtrl.workspace.namespace}}/{{workspaceItemCtrl.workspace.config.name}} + {{workspaceItemCtrl.workspace.namespace}}/{{workspaceItemCtrl.workspaceName}}
@@ -86,7 +86,7 @@ - diff --git a/dashboard/src/app/workspaces/workspace-details/devfile/workspace-devfile-editor.controller.ts b/dashboard/src/app/workspaces/workspace-details/devfile/workspace-devfile-editor.controller.ts new file mode 100644 index 00000000000..4d2553f81e6 --- /dev/null +++ b/dashboard/src/app/workspaces/workspace-details/devfile/workspace-devfile-editor.controller.ts @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2015-2018 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 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ +'use strict'; +/** + * @ngdoc controller + * @name workspaces.devfile-editor.controller:WorkspaceDevfileEditorController + * @description This class is handling the controller for the workspace devfile editor widget + * @author Anna Shumilova + */ +export class WorkspaceDevfileEditorController { + + static $inject = ['$log', '$scope', '$timeout']; + + $log: ng.ILogService; + $scope: ng.IScope; + $timeout: ng.ITimeoutService; + + editorOptions: { + lineWrapping: boolean, + lineNumbers: boolean, + matchBrackets: boolean, + mode: string, + onLoad: Function + }; + devfileValidationMessages: string[] = []; + isActive: boolean; + workspaceDevfile: che.IWorkspaceDevfile; + devfileYaml: string; + newWorkspaceDevfile: che.IWorkspaceDevfile; + workspaceDevfileOnChange: Function; + private saveTimeoutPromise: ng.IPromise; + private isSaving: boolean; + + + /** + * Default constructor that is using resource + */ + constructor($log: ng.ILogService, $scope: ng.IScope, $timeout: ng.ITimeoutService) { + this.$log = $log; + this.$scope = $scope; + this.$timeout = $timeout; + this.isSaving = false; + this.devfileYaml = jsyaml.dump(this.workspaceDevfile); + + $scope.$watch(() => { + return this.workspaceDevfile; + }, () => { + let editedWorkspaceDevfile; + try { + editedWorkspaceDevfile = jsyaml.load(this.devfileYaml); + angular.extend(editedWorkspaceDevfile, this.workspaceDevfile); + } catch (e) { + editedWorkspaceDevfile = this.workspaceDevfile; + } + this.devfileYaml = jsyaml.dump(this.workspaceDevfile); + const validateOnly = true; + this.onChange(validateOnly); + }, true); + + } + + /** + * Callback when editor content is changed. + */ + onChange(validateOnly?: boolean): void { + this.devfileValidationMessages = []; + if (!this.devfileYaml) { + return; + } + + let devfile; + try { + devfile = jsyaml.load(this.devfileYaml); + } catch (e) { + if (e.name === 'YAMLException') { + this.devfileValidationMessages = [e.message]; + } + if (this.devfileValidationMessages.length === 0) { + this.devfileValidationMessages = ['Devfile is invalid.']; + } + this.$log.error(e); + } + + if (validateOnly || !this.isActive) { + return; + } + this.isSaving = (this.devfileValidationMessages.length === 0) && !angular.equals(devfile, this.workspaceDevfile); + + if (this.saveTimeoutPromise) { + this.$timeout.cancel(this.saveTimeoutPromise); + } + + this.saveTimeoutPromise = this.$timeout(() => { + // immediately apply config on IU + this.newWorkspaceDevfile = angular.copy(devfile); + this.isSaving = false; + this.applyChanges(); + }, 2000); + } + + /** + * Callback when user applies new config. + */ + applyChanges(): void { + this.workspaceDevfileOnChange({devfile: this.newWorkspaceDevfile}); + } +} diff --git a/dashboard/src/app/workspaces/workspace-details/devfile/workspace-devfile-editor.directive.ts b/dashboard/src/app/workspaces/workspace-details/devfile/workspace-devfile-editor.directive.ts new file mode 100644 index 00000000000..ea262f46028 --- /dev/null +++ b/dashboard/src/app/workspaces/workspace-details/devfile/workspace-devfile-editor.directive.ts @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2015-2018 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 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ +'use strict'; + +/** + * Defines a directive for displaying devfile editor widget. + * @author Anna Shumilova + */ +export class WorkspaceDevfileEditor { + restrict: string = 'E'; + templateUrl: string = 'app/workspaces/workspace-details/devfile/workspace-devfile-editor.html'; + replace: boolean = false; + + controller: string = 'WorkspaceDevfileEditorController'; + controllerAs: string = 'workspaceDevfileEditorController'; + + bindToController: boolean = true; + + scope: { + [paramName: string]: string; + }; + + /** + * Default constructor that is using resource + */ + constructor() { + // scope values + this.scope = { + isActive: '=', + workspaceDevfile: '=', + workspaceDevfileOnChange: '&' + }; + } + +} diff --git a/dashboard/src/app/workspaces/workspace-details/devfile/workspace-devfile-editor.html b/dashboard/src/app/workspaces/workspace-details/devfile/workspace-devfile-editor.html new file mode 100644 index 00000000000..0e6b49cc039 --- /dev/null +++ b/dashboard/src/app/workspaces/workspace-details/devfile/workspace-devfile-editor.html @@ -0,0 +1,23 @@ +
+
+ Saving workspace devfile... +
+
+ +
Devfile is required.
+
+
+
+
+
+ {{errorMessage}} +
+
+
+
+ diff --git a/dashboard/src/app/workspaces/workspace-details/devfile/workspace-devfile-editor.styl b/dashboard/src/app/workspaces/workspace-details/devfile/workspace-devfile-editor.styl new file mode 100644 index 00000000000..5622486dd14 --- /dev/null +++ b/dashboard/src/app/workspaces/workspace-details/devfile/workspace-devfile-editor.styl @@ -0,0 +1,26 @@ +.config-import + margin-left 10px + + .config-editor + min-width 500px + max-width 1000px + margin-top 20px + margin-right 8px + + .config-editor .CodeMirror + border 1px solid $list-separator-color + height 300px + min-height 300px + font-size 12px + + .config-docs-link + margin-top 5px + + .error-message + color $error-color + + .saving-message + color $label-info-color + height 18px + top 12px + position absolute \ No newline at end of file diff --git a/dashboard/src/app/workspaces/workspace-details/export-workspace/dialog/export-workspace-dialog.controller.ts b/dashboard/src/app/workspaces/workspace-details/export-workspace/dialog/export-workspace-dialog.controller.ts index 7d36fbbfa0c..ace5af6b98e 100644 --- a/dashboard/src/app/workspaces/workspace-details/export-workspace/dialog/export-workspace-dialog.controller.ts +++ b/dashboard/src/app/workspaces/workspace-details/export-workspace/dialog/export-workspace-dialog.controller.ts @@ -95,9 +95,12 @@ export class ExportWorkspaceDialogController { * @returns {*} */ getCopyOfConfig() { - let copyOfConfig = angular.copy(this.workspaceDetails.config); - - return this.removeLinks(copyOfConfig); + if (this.workspaceDetails.config) { + let copyOfConfig = angular.copy(this.workspaceDetails.config); + return this.removeLinks(copyOfConfig); + } else if (this.workspaceDetails.devfile) { + return this.workspaceDetails.devfile; + } } /** diff --git a/dashboard/src/app/workspaces/workspace-details/export-workspace/dialog/export-workspace-dialog.styl b/dashboard/src/app/workspaces/workspace-details/export-workspace/dialog/export-workspace-dialog.styl index 87f785638ec..73cbc86fa10 100644 --- a/dashboard/src/app/workspaces/workspace-details/export-workspace/dialog/export-workspace-dialog.styl +++ b/dashboard/src/app/workspaces/workspace-details/export-workspace/dialog/export-workspace-dialog.styl @@ -1,9 +1,13 @@ .export-workspace-dialog - width 100% + width 700px + max-width 700px .workspace-editor margin-top 15px - max-width 800px + width 700px + + .CodeMirror-wrap + width 700px .che-input:first-child margin-top 10px diff --git a/dashboard/src/app/workspaces/workspace-details/workspace-details-config.ts b/dashboard/src/app/workspaces/workspace-details/workspace-details-config.ts index fee68be1f16..bd5a8620ff0 100644 --- a/dashboard/src/app/workspaces/workspace-details/workspace-details-config.ts +++ b/dashboard/src/app/workspaces/workspace-details/workspace-details-config.ts @@ -28,6 +28,8 @@ import {WorkspaceRecipeAuthoringController} from './select-stack/recipe-authorin import {WorkspaceRecipeAuthoring} from './select-stack/recipe-authoring/workspace-recipe-authoring.directive'; import {WorkspaceConfigImportController} from './config-import/workspace-config-import.controller'; import {WorkspaceConfigImport} from './config-import/workspace-config-import.directive'; +import {WorkspaceDevfileEditorController} from './devfile/workspace-devfile-editor.controller'; +import {WorkspaceDevfileEditor} from './devfile/workspace-devfile-editor.directive'; import {ReadyToGoStacksController} from './select-stack/ready-to-go-stacks/ready-to-go-stacks.controller'; import {ReadyToGoStacks} from './select-stack/ready-to-go-stacks/ready-to-go-stacks.directive'; import {CreateProjectStackLibraryController} from './select-stack/stack-library/create-project-stack-library.controller'; @@ -115,6 +117,8 @@ export class WorkspaceDetailsConfig { register.directive('cheWorkspaceRecipeAuthoring', WorkspaceRecipeAuthoring); register.controller('WorkspaceConfigImportController', WorkspaceConfigImportController); register.directive('cheWorkspaceConfigImport', WorkspaceConfigImport); + register.controller('WorkspaceDevfileEditorController', WorkspaceDevfileEditorController); + register.directive('workspaceDevfileEditor', WorkspaceDevfileEditor); register.controller('ReadyToGoStacksController', ReadyToGoStacksController); register.directive('readyToGoStacks', ReadyToGoStacks); register.controller('CreateProjectStackLibraryController', CreateProjectStackLibraryController); diff --git a/dashboard/src/app/workspaces/workspace-details/workspace-details.controller.ts b/dashboard/src/app/workspaces/workspace-details/workspace-details.controller.ts index ca77e233dc7..22d0739b9a6 100644 --- a/dashboard/src/app/workspaces/workspace-details/workspace-details.controller.ts +++ b/dashboard/src/app/workspaces/workspace-details/workspace-details.controller.ts @@ -24,8 +24,6 @@ export interface IInitData { workspaceDetails: che.IWorkspace; } -const TAB: Array = ['Overview', 'Projects', 'Containers', 'Servers', 'Env_Variables', 'Volumes', 'Config', 'SSH', 'Plugins', 'Editors']; - /** * @ngdoc controller * @name workspaces.workspace.details.controller:WorkspaceDetailsController @@ -66,6 +64,8 @@ export class WorkspaceDetailsController { private errorMessage: string = ''; private tabsValidationTimeout: ng.IPromise; private pluginRegistry: string; + private TAB: Array; + /** * There are unsaved changes to apply (with restart) when is't true. */ @@ -123,7 +123,7 @@ export class WorkspaceDetailsController { this.originWorkspaceDetails = angular.copy(initData.workspaceDetails); this.workspaceDetails = angular.copy(initData.workspaceDetails); this.checkEditMode(); - + this.TAB = this.workspaceDetails.config ? ['Overview', 'Projects', 'Containers', 'Servers', 'Env_Variables', 'Volumes', 'Config', 'SSH', 'Plugins', 'Editors'] : ['Overview', 'Projects', 'Plugins', 'Editors', 'Devfile']; this.updateTabs(); this.updateSelectedTab(this.$location.search().tab); @@ -182,7 +182,7 @@ export class WorkspaceDetailsController { */ updateTabs(): void { this.tab = {}; - TAB.forEach((tab: string, $index: number) => { + this.TAB.forEach((tab: string, $index: number) => { const index = $index.toString(); this.tab[tab] = index; this.tab[index] = tab; @@ -271,7 +271,6 @@ export class WorkspaceDetailsController { if (angular.equals(this.workspaceDetails.config, config)) { return; } - if (this.newName !== config.name) { this.newName = config.name; } @@ -300,6 +299,36 @@ export class WorkspaceDetailsController { } } + /** + * Callback when workspace devfile has been changed in editor. + * + * @param {che.IWorkspaceDevfile} devfile workspace devfile + */ + updateWorkspaceDevfile(devfile: che.IWorkspaceDevfile): void { + if (!devfile) { + return; + } + if (angular.equals(this.workspaceDetails.devfile, devfile)) { + return; + } + + if (this.newName !== devfile.name) { + this.newName = devfile.name; + } + + this.workspaceDetails.devfile = devfile; + + + if (!this.originWorkspaceDetails || !this.workspaceDetails) { + return; + } + + this.workspaceDetailsService.publishWorkspaceChange(this.workspaceDetails); + + let runningWorkspace = this.getWorkspaceStatus() === WorkspaceStatus[WorkspaceStatus.STARTING] || this.getWorkspaceStatus() === WorkspaceStatus[WorkspaceStatus.RUNNING]; + this.saveConfigChanges(false, runningWorkspace); + } + /** * This method checks form validity on each tab and returns true if * all forms are valid. @@ -331,10 +360,6 @@ export class WorkspaceDetailsController { return `Current infrastructure doesn't support this workspace recipe type.`; } - if (this.isSwitchToPlugins()) { - return 'Installers and plugins are different concepts, which are not compatible between each others. Enabling plugins causes disabling all installers.'; - } - if (failedTabs && failedTabs.length > 0) { const url = this.$location.absUrl().split('?')[0]; let message = ` 0 || this.unsavedChangesToApply || this.workspaceDetailsService.getRestartToApply(this.workspaceId); @@ -396,7 +420,7 @@ export class WorkspaceDetailsController { } return this.tabsValidationTimeout = this.$timeout(() => { - const configIsDiffer = !angular.equals(this.originWorkspaceDetails.config, this.workspaceDetails.config); + const configIsDiffer = this.originWorkspaceDetails.config ? !angular.equals(this.originWorkspaceDetails.config, this.workspaceDetails.config) : !angular.equals(this.originWorkspaceDetails.devfile, this.workspaceDetails.devfile); // the workspace should be restarted only if its status is STARTING or RUNNING if (this.getWorkspaceStatus() === WorkspaceStatus[WorkspaceStatus.STARTING] || this.getWorkspaceStatus() === WorkspaceStatus[WorkspaceStatus.RUNNING]) { @@ -472,7 +496,8 @@ export class WorkspaceDetailsController { if (refreshPage) { return this.cheWorkspace.fetchWorkspaceDetails(this.originWorkspaceDetails.id).then(() => { - this.$location.path('/workspace/' + this.namespaceId + '/' + this.workspaceDetails.config.name).search({tab: this.tab[this.selectedTabIndex]}); + let name = this.cheWorkspace.getWorkspaceDataManager().getName(this.workspaceDetails); + this.$location.path('/workspace/' + this.namespaceId + '/' + name).search({tab: this.tab[this.selectedTabIndex]}); }); } }) @@ -548,15 +573,6 @@ export class WorkspaceDetailsController { return tabs.some((tabKey: string) => this.checkFormsNotValid(tabKey)); } - /** - * Checks whether "plugins" were disabled in origin workspace config and are enabled now. - */ - isSwitchToPlugins(): boolean { - let originEditor = this.originWorkspaceDetails.config.attributes.editor || ''; - let originPlugins = this.originWorkspaceDetails.config.attributes.plugins || ''; - return this.isPluginsEnabled() && (originEditor.length === 0 && originPlugins.length === 0); - } - /** * Checks whether "plugins" are enabled in workspace config. */ diff --git a/dashboard/src/app/workspaces/workspace-details/workspace-details.directive.spec.ts b/dashboard/src/app/workspaces/workspace-details/workspace-details.directive.spec.ts index 683675d3742..282318faa1f 100644 --- a/dashboard/src/app/workspaces/workspace-details/workspace-details.directive.spec.ts +++ b/dashboard/src/app/workspaces/workspace-details/workspace-details.directive.spec.ts @@ -201,6 +201,14 @@ describe(`WorkspaceDetailsController >`, () => { this.getWorkspaceSettings = () => { return {}; }; + + this.getWorkspaceDataManager = () => { + return { + getName(data: che.IWorkspace): string { + return 'name'; + } + }; + }; }) // terminal directives which prevent to execute an original ones .directive('mdTab', function () { diff --git a/dashboard/src/app/workspaces/workspace-details/workspace-details.html b/dashboard/src/app/workspaces/workspace-details/workspace-details.html index aaa446edbe4..8c21ec7c966 100644 --- a/dashboard/src/app/workspaces/workspace-details/workspace-details.html +++ b/dashboard/src/app/workspaces/workspace-details/workspace-details.html @@ -58,7 +58,8 @@ - + Containers @@ -69,7 +70,8 @@ - + Servers @@ -85,7 +87,8 @@ - + Env Variables @@ -101,7 +104,8 @@ - + Volumes @@ -117,7 +121,8 @@ - - + SSH @@ -154,7 +160,7 @@ Plugins - @@ -168,13 +174,34 @@ Editors - + + + + + Devfile + + + + + + + + + + { - return this.workspaceConfig; - }, (workspaceConfig: IWorkspaceConfig) => { - if (!workspaceConfig) { + return this.workspace; + }, (workspace: che.IWorkspace) => { + if (!workspace) { return; } this.updateEditors(); @@ -114,32 +116,17 @@ export class WorkspaceEditorsController { updateEditor(plugin: IPlugin): void { if (plugin.type === EDITOR_TYPE) { this.selectedEditor = plugin.isEnabled ? plugin.id : ''; - this.workspaceConfig.attributes.editor = this.selectedEditor; + this.cheWorkspace.getWorkspaceDataManager().setEditor(this.workspace, this.selectedEditor); } - - this.cleanupInstallers(); + this.onChange(); } - /** - * Clean up all the installers in all machines, when plugin is selected. - */ - cleanupInstallers(): void { - let defaultEnv: string = this.workspaceConfig.defaultEnv; - let machines: any = this.workspaceConfig.environments[defaultEnv].machines; - let machineNames: Array = Object.keys(machines); - machineNames.forEach((machineName: string) => { - machines[machineName].installers = []; - }); - } - /** * Update the state of plugins. */ private updateEditors(): void { - // get selected plugins from workspace configuration attribute - "editor": - this.selectedEditor = this.workspaceConfig && this.workspaceConfig.attributes && this.workspaceConfig.attributes.editor ? - this.workspaceConfig.attributes.editor : ''; + this.selectedEditor = this.cheWorkspace.getWorkspaceDataManager().getEditor(this.workspace); // check each editor's enabled state: this.editors.forEach((editor: IPlugin) => { diff --git a/dashboard/src/app/workspaces/workspace-details/workspace-editors/workspace-editors.directive.ts b/dashboard/src/app/workspaces/workspace-details/workspace-editors/workspace-editors.directive.ts index 9df1718a23f..55999d5ad1e 100644 --- a/dashboard/src/app/workspaces/workspace-details/workspace-editors/workspace-editors.directive.ts +++ b/dashboard/src/app/workspaces/workspace-details/workspace-editors/workspace-editors.directive.ts @@ -41,7 +41,7 @@ export class WorkspaceEditors implements ng.IDirective { constructor() { this.scope = { onChange: '&', - workspaceConfig: '=', + workspace: '=', pluginRegistryLocation: '=' }; } diff --git a/dashboard/src/app/workspaces/workspace-details/workspace-overview/workspace-details-overview.controller.ts b/dashboard/src/app/workspaces/workspace-details/workspace-overview/workspace-details-overview.controller.ts index 794596ea8fd..081be4aa5b0 100644 --- a/dashboard/src/app/workspaces/workspace-details/workspace-overview/workspace-details-overview.controller.ts +++ b/dashboard/src/app/workspaces/workspace-details/workspace-overview/workspace-details-overview.controller.ts @@ -15,6 +15,7 @@ import {CheNotification} from '../../../../components/notification/che-notificat import {ConfirmDialogService} from '../../../../components/service/confirm-dialog/confirm-dialog.service'; import {NamespaceSelectorSvc} from '../../create-workspace/namespace-selector/namespace-selector.service'; import {WorkspaceDetailsService} from '../workspace-details.service'; +import {WorkspacesService} from '../../workspaces.service'; const STARTING = WorkspaceStatus[WorkspaceStatus.STARTING]; const RUNNING = WorkspaceStatus[WorkspaceStatus.RUNNING]; @@ -28,7 +29,7 @@ const STOPPED = WorkspaceStatus[WorkspaceStatus.STOPPED]; */ export class WorkspaceDetailsOverviewController { - static $inject = ['$q', '$route', '$timeout', '$location', 'cheWorkspace', 'cheNotification', 'confirmDialogService', 'namespaceSelectorSvc', 'workspaceDetailsService']; + static $inject = ['$scope', '$q', '$route', '$timeout', '$location', 'cheWorkspace', 'cheNotification', 'confirmDialogService', 'namespaceSelectorSvc', 'workspaceDetailsService']; onChange: Function; @@ -45,16 +46,18 @@ export class WorkspaceDetailsOverviewController { private workspaceDetailsService: WorkspaceDetailsService; private namespaceId: string; private workspaceName: string; + private name: string; private usedNamesList: Array; private inputmodel: ng.INgModelController; private isLoading: boolean; private isEphemeralMode: boolean; + private attributes: che.IWorkspaceConfigAttributes; private attributesCopy: che.IWorkspaceConfigAttributes; /** * Default constructor that is using resource */ - constructor($q: ng.IQService, $route: ng.route.IRouteService, $timeout: ng.ITimeoutService, $location: ng.ILocationService, + constructor($scope: ng.IScope, $q: ng.IQService, $route: ng.route.IRouteService, $timeout: ng.ITimeoutService, $location: ng.ILocationService, cheWorkspace: CheWorkspace, cheNotification: CheNotification, confirmDialogService: ConfirmDialogService, namespaceSelectorSvc: NamespaceSelectorSvc, workspaceDetailsService: WorkspaceDetailsService) { this.$q = $q; @@ -70,11 +73,28 @@ export class WorkspaceDetailsOverviewController { const routeParams = $route.current.params; this.namespaceId = routeParams.namespace; this.workspaceName = routeParams.workspaceName; + this.init(); - this.isEphemeralMode = this.workspaceDetails && this.workspaceDetails.config && this.workspaceDetails.config.attributes && this.workspaceDetails.config.attributes.persistVolumes ? !JSON.parse(this.workspaceDetails.config.attributes.persistVolumes) : false; - this.attributesCopy = angular.copy(this.workspaceDetails.config.attributes); + const deRegistrationFn = $scope.$watch(() => { + return this.workspaceDetails; + }, (workspace: che.IWorkspace) => { + if (!workspace) { + return; + } + this.init(); + }, true); + + $scope.$on('$destroy', () => { + deRegistrationFn(); + }); + } - this.fillInListOfUsedNames(); + init(): void { + this.attributes = this.cheWorkspace.getWorkspaceDataManager().getAttributes(this.workspaceDetails); + this.name = this.cheWorkspace.getWorkspaceDataManager().getName(this.workspaceDetails); + this.isEphemeralMode = this.attributes && this.attributes.persistVolumes ? !JSON.parse(this.attributes.persistVolumes) : false; + + this.attributesCopy = angular.copy(this.cheWorkspace.getWorkspaceDataManager().getAttributes(this.workspaceDetails)); } /** @@ -187,9 +207,9 @@ export class WorkspaceDetailsOverviewController { */ buildInListOfUsedNames(workspaces: Array): Array { return workspaces.filter((workspace: che.IWorkspace) => { - return workspace.namespace === this.namespaceId && workspace.config.name !== this.workspaceName; + return workspace.namespace === this.namespaceId && this.cheWorkspace.getWorkspaceDataManager().getName(workspace) !== this.workspaceName; }).map((workspace: che.IWorkspace) => { - return workspace.config.name; + return this.cheWorkspace.getWorkspaceDataManager().getName(workspace); }); } @@ -247,7 +267,7 @@ export class WorkspaceDetailsOverviewController { * Removes current workspace. */ deleteWorkspace(): void { - const content = 'Would you like to delete workspace \'' + this.workspaceDetails.config.name + '\'?'; + const content = 'Would you like to delete workspace \'' + this.cheWorkspace.getWorkspaceDataManager().getName(this.workspaceDetails) + '\'?'; this.confirmDialogService.showConfirmDialog('Delete workspace', content, 'Delete').then(() => { if ([RUNNING, STARTING].indexOf(this.getWorkspaceStatus()) !== -1) { this.cheWorkspace.stopWorkspace(this.workspaceDetails.id); @@ -267,12 +287,12 @@ export class WorkspaceDetailsOverviewController { */ onEphemeralModeChange(): void { if (this.isEphemeralMode) { - this.workspaceDetails.config.attributes.persistVolumes = 'false'; + this.attributes.persistVolumes = 'false'; } else { if (this.attributesCopy.persistVolumes) { - this.workspaceDetails.config.attributes.persistVolumes = 'true'; + this.attributes.persistVolumes = 'true'; } else { - delete this.workspaceDetails.config.attributes.persistVolumes; + delete this.attributes.persistVolumes; } } this.onChange(); @@ -283,6 +303,7 @@ export class WorkspaceDetailsOverviewController { */ onNameChange() { this.$timeout(() => { + this.cheWorkspace.getWorkspaceDataManager().setName(this.workspaceDetails, this.name); this.onChange(); }); } diff --git a/dashboard/src/app/workspaces/workspace-details/workspace-overview/workspace-details-overview.html b/dashboard/src/app/workspaces/workspace-details/workspace-overview/workspace-details-overview.html index 0d69c2212a9..5fec462ccd7 100644 --- a/dashboard/src/app/workspaces/workspace-details/workspace-overview/workspace-details-overview.html +++ b/dashboard/src/app/workspaces/workspace-details/workspace-overview/workspace-details-overview.html @@ -8,7 +8,7 @@ che-place-holder="Name of the workspace" aria-label="Name of the workspace" ng-model-options="{ allowInvalid: true }" - ng-model="workspaceDetailsOverviewController.workspaceDetails.config.name" + ng-model="workspaceDetailsOverviewController.name" che-on-change="workspaceDetailsOverviewController.onNameChange()" required ng-minlength="3" diff --git a/dashboard/src/app/workspaces/workspace-details/workspace-plugins/workspace-plugins.controller.ts b/dashboard/src/app/workspaces/workspace-details/workspace-plugins/workspace-plugins.controller.ts index d7f0b353324..0bd6afc0e9e 100644 --- a/dashboard/src/app/workspaces/workspace-details/workspace-plugins/workspace-plugins.controller.ts +++ b/dashboard/src/app/workspaces/workspace-details/workspace-plugins/workspace-plugins.controller.ts @@ -10,9 +10,9 @@ * Red Hat, Inc. - initial API and implementation */ 'use strict'; -import { IPlugin, PluginRegistry } from '../../../../components/api/plugin-registry.factory'; -import IWorkspaceConfig = che.IWorkspaceConfig; -import { CheNotification } from '../../../../components/notification/che-notification.factory'; +import {IPlugin, PluginRegistry} from '../../../../components/api/plugin-registry.factory'; +import {CheNotification} from '../../../../components/notification/che-notification.factory'; +import {CheWorkspace} from '../../../../components/api/workspace/che-workspace.factory'; const PLUGIN_SEPARATOR = ','; const PLUGIN_VERSION_SEPARATOR = ':'; @@ -25,13 +25,14 @@ const EDITOR_TYPE = 'Che Editor'; * @author Ann Shumilova */ export class WorkspacePluginsController { - static $inject = ['pluginRegistry', 'cheListHelperFactory', '$scope', 'cheNotification']; + static $inject = ['pluginRegistry', 'cheListHelperFactory', '$scope', 'cheNotification', 'cheWorkspace']; - workspaceConfig: IWorkspaceConfig; + workspace: che.IWorkspace; pluginRegistryLocation: string; pluginRegistry: PluginRegistry; cheNotification: CheNotification; + cheWorkspace: CheWorkspace; onChange: Function; isLoading: boolean; @@ -47,9 +48,10 @@ export class WorkspacePluginsController { * Default constructor that is using resource */ constructor(pluginRegistry: PluginRegistry, cheListHelperFactory: che.widget.ICheListHelperFactory, $scope: ng.IScope, - cheNotification: CheNotification) { + cheNotification: CheNotification, cheWorkspace: CheWorkspace) { this.pluginRegistry = pluginRegistry; this.cheNotification = cheNotification; + this.cheWorkspace = cheWorkspace; const helperId = 'workspace-plugins'; this.cheListHelper = cheListHelperFactory.getHelper(helperId); @@ -61,9 +63,9 @@ export class WorkspacePluginsController { this.pluginFilter = { displayName: '' }; const deRegistrationFn = $scope.$watch(() => { - return this.workspaceConfig; - }, (workspaceConfig: IWorkspaceConfig) => { - if (!workspaceConfig) { + return this.workspace; + }, (workspace: che.IWorkspace) => { + if (!workspace) { return; } this.updatePlugins(); @@ -119,32 +121,17 @@ export class WorkspacePluginsController { } else { this.selectedPlugins.splice(this.selectedPlugins.indexOf(plugin.id), 1); } - this.workspaceConfig.attributes.plugins = this.selectedPlugins.join(PLUGIN_SEPARATOR); + this.cheWorkspace.getWorkspaceDataManager().setPlugins(this.workspace, this.selectedPlugins); } - this.cleanupInstallers(); this.onChange(); } - /** - * Clean up all the installers in all machines, when plugin is selected. - */ - cleanupInstallers(): void { - let defaultEnv: string = this.workspaceConfig.defaultEnv; - let machines: any = this.workspaceConfig.environments[defaultEnv].machines; - let machineNames: Array = Object.keys(machines); - machineNames.forEach((machineName: string) => { - machines[machineName].installers = []; - }); - } - /** * Update the state of plugins. */ private updatePlugins(): void { - // get selected plugins from workspace configuration attribute - "plugins" (coma separated values): - this.selectedPlugins = this.workspaceConfig && this.workspaceConfig.attributes && this.workspaceConfig.attributes.plugins ? - this.workspaceConfig.attributes.plugins.split(PLUGIN_SEPARATOR) : []; + this.selectedPlugins = this.cheWorkspace.getWorkspaceDataManager().getPlugins(this.workspace); // check each plugin's enabled state: this.plugins.forEach((plugin: IPlugin) => { plugin.isEnabled = this.isPluginEnabled(plugin); diff --git a/dashboard/src/app/workspaces/workspace-details/workspace-plugins/workspace-plugins.directive.ts b/dashboard/src/app/workspaces/workspace-details/workspace-plugins/workspace-plugins.directive.ts index 07af9504031..cc4d28c2271 100644 --- a/dashboard/src/app/workspaces/workspace-details/workspace-plugins/workspace-plugins.directive.ts +++ b/dashboard/src/app/workspaces/workspace-details/workspace-plugins/workspace-plugins.directive.ts @@ -41,7 +41,7 @@ export class WorkspacePlugins implements ng.IDirective { constructor() { this.scope = { onChange: '&', - workspaceConfig: '=', + workspace: '=', pluginRegistryLocation: '=' }; } diff --git a/dashboard/src/app/workspaces/workspace-details/workspace-projects/workspace-details-projects.controller.ts b/dashboard/src/app/workspaces/workspace-details/workspace-projects/workspace-details-projects.controller.ts index 1ef90c53294..09e67b37278 100644 --- a/dashboard/src/app/workspaces/workspace-details/workspace-projects/workspace-details-projects.controller.ts +++ b/dashboard/src/app/workspaces/workspace-details/workspace-projects/workspace-details-projects.controller.ts @@ -18,6 +18,7 @@ import {WorkspaceDetailsProjectsService} from './workspace-details-projects.serv import {WorkspaceDetailsService} from '../workspace-details.service'; import {CreateWorkspaceSvc} from '../../create-workspace/create-workspace.service'; import {WorkspaceStatus} from '../../../../components/api/workspace/che-workspace.factory'; +import {WorkspaceDataManager} from '../../../../components/api/workspace/workspace-data-manager'; /** * @ngdoc controller @@ -63,7 +64,8 @@ export class WorkspaceDetailsProjectsCtrl { * Workspace details service. */ private workspaceDetailsService: WorkspaceDetailsService; - + private workspaceDataManager: WorkspaceDataManager; + private projects: Array ; private projectFilter: any; private profileCreationDate: any; /** @@ -104,6 +106,7 @@ export class WorkspaceDetailsProjectsCtrl { }); const preferences = cheAPI.getPreferences().getPreferences(); + this.workspaceDataManager = cheAPI.getWorkspace().getWorkspaceDataManager(); this.profileCreationDate = preferences['che:created']; this.projectFilter = {name: ''}; @@ -149,7 +152,8 @@ export class WorkspaceDetailsProjectsCtrl { } this.workspaceDetails = workspaceDetails; this.stackSelectorSvc.setStackId(this.workspaceDetails.attributes.stackId); - this.cheListHelper.setList(this.workspaceDetails.config.projects, 'name'); + this.projects = this.workspaceDataManager.getProjects(this.workspaceDetails); + this.cheListHelper.setList(this.projects, 'name'); } /** @@ -188,10 +192,9 @@ export class WorkspaceDetailsProjectsCtrl { } this.workspaceDetailsProjectsService.addProjectTemplate(projectTemplate); - this.workspaceDetails.config.projects.push(projectTemplate); + this.workspaceDataManager.addProject(this.workspaceDetails, projectTemplate); }); - - this.createWorkspaceSvc.addProjectCommands(this.workspaceDetails.config, projectTemplates); + this.createWorkspaceSvc.addProjectCommands(this.workspaceDetails, projectTemplates); this.projectsOnChange(); } @@ -202,7 +205,7 @@ export class WorkspaceDetailsProjectsCtrl { * @return {boolean} */ isProjectNameUnique(name: string): boolean { - return this.workspaceDetails.config.projects.every((project: che.IProject) => { + return this.projects.every((project: che.IProject) => { return project.name !== name; }); } @@ -234,10 +237,10 @@ export class WorkspaceDetailsProjectsCtrl { }); this.showDeleteProjectsConfirmation(selectedProjects.length).then(() => { - this.workspaceDetails.config.projects = this.workspaceDetails.config.projects.filter((project: che.IProject) => { + this.projects = this.projects.filter((project: che.IProject) => { return selectedProjectsNames.indexOf(project.name) === -1; }); - + this.workspaceDataManager.setProjects(this.workspaceDetails, this.projects); this.workspaceDetailsProjectsService.addProjectNamesToDelete(selectedProjectsNames); this.projectsOnChange(); diff --git a/dashboard/src/app/workspaces/workspace-details/workspace-projects/workspace-details-projects.service.ts b/dashboard/src/app/workspaces/workspace-details/workspace-projects/workspace-details-projects.service.ts index 4a375859b08..7a6b6776fc6 100644 --- a/dashboard/src/app/workspaces/workspace-details/workspace-projects/workspace-details-projects.service.ts +++ b/dashboard/src/app/workspaces/workspace-details/workspace-projects/workspace-details-projects.service.ts @@ -168,7 +168,7 @@ export class WorkspaceDetailsProjectsService { const workspaceAgent = this.cheWorkspace.getWorkspaceAgent(workspaceId); if (!workspaceAgent) { - return this.$q.reject({message: 'Workspace isn\'t run. Cannot delete any project.'}); + return this.$q.all(deleteProjectPromises); } const projectService = workspaceAgent.getProject(); diff --git a/dashboard/src/app/workspaces/workspaces.service.ts b/dashboard/src/app/workspaces/workspaces.service.ts index c0912cd600d..2c4bb7152b6 100644 --- a/dashboard/src/app/workspaces/workspaces.service.ts +++ b/dashboard/src/app/workspaces/workspaces.service.ts @@ -43,12 +43,13 @@ export class WorkspacesService { * @returns {boolean} */ isSupported(workspace: che.IWorkspace, envName?: string): boolean { - envName = envName || workspace.config.defaultEnv; - - const supportedRecipeTypes = this.cheWorkspace.getSupportedRecipeTypes(), - envRecipeType = envName ? workspace.config.environments[envName].recipe.type : 'no-environment'; - - return supportedRecipeTypes.indexOf(envRecipeType) !== -1; + if (workspace.config) { + envName = envName || workspace.config.defaultEnv; + const supportedRecipeTypes = this.cheWorkspace.getSupportedRecipeTypes(), + envRecipeType = envName ? workspace.config.environments[envName].recipe.type : 'no-environment'; + return supportedRecipeTypes.indexOf(envRecipeType) !== -1; + } else if (workspace.devfile) { + return true; + } } - } diff --git a/dashboard/src/components/api/workspace/che-workspace.factory.ts b/dashboard/src/components/api/workspace/che-workspace.factory.ts index a8a9be41080..c4b34dac6b2 100644 --- a/dashboard/src/components/api/workspace/che-workspace.factory.ts +++ b/dashboard/src/components/api/workspace/che-workspace.factory.ts @@ -20,6 +20,7 @@ import {CheBranding} from '../../branding/che-branding.factory'; import {CheEnvironmentManager} from '../environment/che-environment-manager.factory'; import {CheRecipeTypes} from '../recipe/che-recipe-types'; import {CheNotification} from '../../notification/che-notification.factory'; +import {WorkspaceDataManager} from './workspace-data-manager'; const WS_AGENT_HTTP_LINK: string = 'wsagent/http'; const WS_AGENT_WS_LINK: string = 'wsagent/ws'; @@ -89,6 +90,10 @@ export class CheWorkspace { * Map with promises. */ private workspacePromises: Map> = new Map(); + /** + * + */ + private workspaceDataManager: WorkspaceDataManager; /** * Default constructor that is using resource @@ -115,6 +120,7 @@ export class CheWorkspace { this.$websocket = $websocket; this.lodash = lodash; this.cheNotification = cheNotification; + this.workspaceDataManager = new WorkspaceDataManager(); // current list of workspaces this.workspaces = []; @@ -140,6 +146,8 @@ export class CheWorkspace { // having 2 methods for creation to ensure namespace parameter won't be send at all if value is null or undefined create: {method: 'POST', url: '/api/workspace'}, createWithNamespace: {method: 'POST', url: '/api/workspace?namespace=:namespace'}, + createDevfile: {method: 'POST', url: '/api/workspace/devfile'}, + createDevWithNamespace: {method: 'POST', url: '/api/workspace?namespace=:namespace'}, deleteWorkspace: {method: 'DELETE', url: '/api/workspace/:workspaceId'}, updateWorkspace: {method: 'PUT', url: '/api/workspace/:workspaceId'}, addProject: {method: 'POST', url: '/api/workspace/:workspaceId/project'}, @@ -219,7 +227,6 @@ export class CheWorkspace { if (this.workspaceAgents.has(workspaceId)) { return this.workspaceAgents.get(workspaceId); } - const runtimeConfig = this.getWorkspaceById(workspaceId).runtime; if (runtimeConfig) { const machineToken = runtimeConfig.machineToken; @@ -236,6 +243,10 @@ export class CheWorkspace { } }); + if (!wsAgentLink) { + return null; + } + const workspaceAgentData: IWorkspaceAgentData = { path: wsAgentLink.url, websocket: wsAgentWebocketLink.url, @@ -284,7 +295,7 @@ export class CheWorkspace { getWorkspaceByName(namespace: string, name: string): che.IWorkspace { return this.lodash.find(this.workspaces, (workspace: che.IWorkspace) => { - return workspace.namespace === namespace && workspace.config.name === name; + return workspace.namespace === namespace && this.workspaceDataManager.getName(workspace) === name; }); } @@ -527,6 +538,17 @@ export class CheWorkspace { this.remoteWorkspaceAPI.create({attribute: attrs}, workspaceConfig).$promise; } + /*createWorkspaceFromDevfile(namespace: string, devfile: che.IWorkspaceDevfile, attributes: any): ng.IPromise { + let attrs = this.lodash.map(this.lodash.pairs(attributes || {}), (item: any) => { + return item[0] + ':' + item[1]; + }); + return namespace ? this.remoteWorkspaceAPI.createWithNamespace({ + namespace: namespace, + attribute: attrs + }, workspaceDev).$promise : + this.remoteWorkspaceAPI.create({attribute: attrs}, workspaceDevfile).$promise; + }*/ + /** * Add a command into the workspace * @param workspaceId {string} the id of the workspace on which we want to add the command @@ -777,6 +799,10 @@ export class CheWorkspace { return this.jsonRpcApiLocation; } + getWorkspaceDataManager(): WorkspaceDataManager { + return this.workspaceDataManager; + } + private updateWorkspacesList(workspace: che.IWorkspace): void { if (workspace.temporary) { this.workspacesById.set(workspace.id, workspace); diff --git a/dashboard/src/components/api/workspace/workspace-data-manager.ts b/dashboard/src/components/api/workspace/workspace-data-manager.ts new file mode 100644 index 00000000000..1c37f449a73 --- /dev/null +++ b/dashboard/src/components/api/workspace/workspace-data-manager.ts @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2015-2018 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 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ +'use strict'; + +const PLUGIN_SEPARATOR = ','; +const EDITOR_TYPE = 'cheEditor'; +const PLUGIN_TYPE = 'chePlugin'; + + +/** + * + * + * @author Ann Shumilova + */ +export class WorkspaceDataManager { + + /** + * Returns the name of the pointed workspace. + * + * @param workspace workspace name + */ + getName(workspace: che.IWorkspace): string { + if (workspace.config) { + return workspace.config.name; + } else if (workspace.devfile) { + return workspace.devfile.name; + } + } + + /** + * Sets the name of the pointed workspace. + * + * @param workspace workspace + * @param name workspace name + */ + setName(workspace: che.IWorkspace, name: string): void { + if (workspace.config) { + workspace.config.name = name; + } else if (workspace.devfile) { + workspace.devfile.name = name; + } + } + + /** + * Returns the attributes of the pointed workspace. + * + * @param workspace workspace + */ + getAttributes(workspace: che.IWorkspace): che.IWorkspaceConfigAttributes { + if (workspace.config) { + return workspace.config.attributes; + } else if (workspace.devfile) { + return workspace.devfile.attributes; + } + } + + /** + * Returns the projects of the pointed workspace. + * + * @param workspace workspace + */ + getProjects(workspace: che.IWorkspace): Array { + if (workspace.config) { + return workspace.config.projects; + } else if (workspace.devfile) { + return workspace.devfile.projects; + } + } + + /** + * Sets the projects of the pointed workspace. + * + * @param workspace workspace + * @param projects workspace projects + */ + setProjects(workspace: che.IWorkspace, projects: Array): void { + if (workspace.config) { + workspace.config.projects = projects; + } else if (workspace.devfile) { + workspace.devfile.projects = projects; + } + } + + /** + * Adds the project to the pointed workspace. + * + * @param workspace workspace + * @param project project to be added to pointed workspace + */ + addProject(workspace: che.IWorkspace, project: any): void { + if (workspace.config) { + workspace.config.projects.push(project); + } else if (workspace.devfile) { + workspace.devfile.projects.push(project); + } + } + + /** + * Adds the command to the pointed workspace. + * + * @param workspace workspace + * @param command command to be added to pointed workspace + */ + addCommand(workspace: che.IWorkspace, command: any): void { + if (workspace.config) { + workspace.config.commands.push(command); + } else if (workspace.devfile) { + workspace.devfile.commands.push(command); + } + } + + /** + * Returns the list of plugin ids of the pointed workspace. + * + * @param workspace workspace + */ + getPlugins(workspace: che.IWorkspace): Array { + let plugins: Array = []; + if (workspace.config) { + return workspace.config.attributes && workspace.config.attributes.plugins ? + workspace.config.attributes.plugins.split(',') : []; + } else if (workspace.devfile) { + workspace.devfile.components.forEach(component => { + if (component.type === PLUGIN_TYPE) { + plugins.push(component.id); + } + }); + } + return plugins; + } + + /** + * Sets the list of plugins of the pointed workspace. + * + * @param workspace workspace + * @param plugins the list of plugins + */ + setPlugins(workspace: che.IWorkspace, plugins: Array): void { + if (workspace.config) { + workspace.config.attributes.plugins = plugins.join(','); + } else if (workspace.devfile) { + let pluginComponents = []; + workspace.devfile.components.forEach(component => { + if (component.type === PLUGIN_TYPE) { + pluginComponents.push(component); + } + }); + + pluginComponents.forEach((pluginComponent: any) => { + let index = plugins.indexOf(pluginComponent.id); + if (index >= 0) { + plugins.splice(index, 1); + } else { + workspace.devfile.components.splice(workspace.devfile.components.indexOf(pluginComponent), 1); + } + }); + + plugins.forEach((plugin: string) => { + workspace.devfile.components.push({id: plugin, type: PLUGIN_TYPE}); + }); + } + } + + /** + * Returns editor's id. + * + * @param workspace workspace + */ + getEditor(workspace: che.IWorkspace): string { + if (workspace.config) { + return workspace.config.attributes && workspace.config.attributes.editor ? + workspace.config.attributes.editor : null; + } else if (workspace.devfile) { + let editor = null; + workspace.devfile.components.forEach(component => { + if (component.type === EDITOR_TYPE) { + editor = component.id; + } + }); + return editor; + } + } + + /** + * Sets the editor of the pointed workspace. + * + * @param workspace workspace + * @param editor editor's id + */ + setEditor(workspace: che.IWorkspace, editor: string): void { + if (workspace.config) { + workspace.config.attributes.editor = editor; + } else if (workspace.devfile) { + let editorComponents = []; + workspace.devfile.components.forEach(component => { + if (component.type === EDITOR_TYPE) { + editorComponents.push(component); + } + }); + + editorComponents.forEach((editor: any) => { + workspace.devfile.components.splice(workspace.devfile.components.indexOf(editor), 1); + }); + + workspace.devfile.components.push({id: editor, type: EDITOR_TYPE}); + } + } + +} diff --git a/dashboard/src/components/typings/che.d.ts b/dashboard/src/components/typings/che.d.ts index 49930e5d058..76b778addaf 100755 --- a/dashboard/src/components/typings/che.d.ts +++ b/dashboard/src/components/typings/che.d.ts @@ -294,7 +294,8 @@ declare namespace che { status?: string; namespace?: string; attributes?: IWorkspaceAttributes; - config: IWorkspaceConfig; + config?: IWorkspaceConfig; + devfile?: IWorkspaceDevfile; runtime?: IWorkspaceRuntime; isLocked?: boolean; usedResources?: string; @@ -325,6 +326,15 @@ declare namespace che { plugins?: string; } + export interface IWorkspaceDevfile { + specVersion: string; + name: string; + components: Array; + projects?: Array ; + commands?: Array ; + attributes?: che.IWorkspaceConfigAttributes; + } + export interface IWorkspaceEnvironment { machines: { [machineName: string]: IEnvironmentMachine