diff --git a/dashboard/src/app/workspaces/create-workspace/create-workspace.controller.ts b/dashboard/src/app/workspaces/create-workspace/create-workspace.controller.ts deleted file mode 100644 index df300990120..00000000000 --- a/dashboard/src/app/workspaces/create-workspace/create-workspace.controller.ts +++ /dev/null @@ -1,314 +0,0 @@ -/* - * Copyright (c) 2015-2016 Codenvy, S.A. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Codenvy, S.A. - initial API and implementation - */ -'use strict'; - -/** - * @ngdoc controller - * @name workspaces.create.workspace.controller:CreateWorkspaceController - * @description This class is handling the controller for workspace creation - * @author Ann Shumilova - * @author Oleksii Orel - */ -export class CreateWorkspaceController { - - /** - * Default constructor that is using resource - * @ngInject for Dependency injection - */ - constructor($location, cheAPI, cheNotification, lodash, $rootScope, cheEnvironmentRegistry) { - this.$location = $location; - this.cheAPI = cheAPI; - this.cheNotification = cheNotification; - this.lodash = lodash; - this.$rootScope = $rootScope; - this.cheEnvironmentRegistry = cheEnvironmentRegistry; - this.stackMachines = {}; - - this.selectSourceOption = 'select-source-recipe'; - - // default RAM value - this.workspaceRam = 2 * Math.pow(1024,3); - - this.editorOptions = { - lineWrapping: true, - lineNumbers: false, - matchBrackets: true, - mode: 'application/json' - }; - - // fetch default recipe if we haven't one - if (!cheAPI.getRecipeTemplate().getDefaultRecipe()) { - cheAPI.getRecipeTemplate().fetchDefaultRecipe(); - } - - this.stack = null; - this.recipeUrl = null; - this.recipeScript = null; - this.recipeFormat = null; - this.importWorkspace = ''; - this.defaultWorkspaceName = null; - - this.usedNamesList = []; - cheAPI.cheWorkspace.fetchWorkspaces().then(() => { - let workspaces = cheAPI.cheWorkspace.getWorkspaces(); - workspaces.forEach((workspace) => { - this.usedNamesList.push(workspace.config.name); - }); - }); - - $rootScope.showIDE = false; - } - - /** - * Callback when tab has been change - * @param tabName the select tab name - */ - setStackTab(tabName) { - if (tabName === 'custom-stack') { - this.cheStackLibrarySelecter(null); - this.isCustomStack = true; - this.generateWorkspaceName(); - } - } - - /** - * Gets object keys from target object. - * - * @param targetObject - * @returns [*] - */ - getObjectKeys(targetObject) { - return Object.keys(targetObject); - } - - /** - * Callback when stack has been set - * @param stack the selected stack - */ - cheStackLibrarySelecter(stack) { - if (stack) { - this.isCustomStack = false; - this.recipeUrl = null; - } - if (this.stack !== stack && stack && stack.workspaceConfig && stack.workspaceConfig.name) { - let workspaceName = stack.workspaceConfig.name; - if (this.usedNamesList.includes(workspaceName)) { - this.generateWorkspaceName(); - } else { - this.setWorkspaceName(workspaceName); - } - } else { - this.generateWorkspaceName(); - } - this.stack = stack; - } - - /** - * Set workspace name - * @param name - */ - setWorkspaceName(name) { - if (!name) { - return; - } - if (!this.defaultWorkspaceName || this.defaultWorkspaceName === this.workspaceName) { - this.defaultWorkspaceName = name; - this.workspaceName = angular.copy(name); - } - } - - /** - * Generates a default workspace name - */ - generateWorkspaceName() { - // starts with wksp - let name = 'wksp'; - name += '-' + (('0000' + (Math.random() * Math.pow(36, 4) << 0).toString(36)).slice(-4)); // jshint ignore:line - this.setWorkspaceName(name); - } - - /** - * Create a new workspace - */ - createWorkspace() { - let source = {}; - source.type = 'dockerfile'; - //User provides recipe URL or recipe's content: - if (this.isCustomStack) { - this.stack = null; - source.type = 'environment'; - source.format = this.recipeFormat; - if (this.recipeUrl && this.recipeUrl.length > 0) { - source.location = this.recipeUrl; - this.submitWorkspace(source); - } else { - source.content = this.recipeScript; - this.submitWorkspace(source); - } - } else if (this.selectSourceOption === 'select-source-import') { - this.importWorkspaceConfig.name = this.workspaceName; - this.setEnvironment(this.importWorkspaceConfig); - let creationPromise = this.cheAPI.getWorkspace().createWorkspaceFromConfig(null, this.importWorkspaceConfig); - this.redirectAfterSubmitWorkspace(creationPromise); - } else { - //check predefined recipe location - if (this.stack && this.stack.source && this.stack.source.type === 'location') { - this.recipeUrl = this.stack.source.origin; - source.location = this.recipeUrl; - this.submitWorkspace(source); - } else { - source = this.getSourceFromStack(this.stack); - this.submitWorkspace(source); - } - } - } - - /** - * Perform actions when content of workspace config is changed. - */ - onWorkspaceSourceChange() { - this.importWorkspaceConfig = null; - this.importWorkspaceMachines = null; - - if (this.importWorkspace && this.importWorkspace.length > 0) { - try { - this.importWorkspaceConfig = angular.fromJson(this.importWorkspace); - this.workspaceName = this.importWorkspaceConfig.name; - let environment = this.importWorkspaceConfig.environments[this.importWorkspaceConfig.defaultEnv]; - let recipeType = environment.recipe.type; - let environmentManager = this.cheEnvironmentRegistry.getEnvironmentManager(recipeType); - this.importWorkspaceMachines = environmentManager.getMachines(environment); - } catch (error) { - - } - } - } - - /** - * Returns the list of environments to be displayed. - * - * @returns {*} list of environments - */ - getEnvironments() { - if (this.selectSourceOption === 'select-source-import') { - return (this.importWorkspaceConfig && this.importWorkspaceConfig.environments) ? this.importWorkspaceConfig.environments : []; - } else if (this.stack) { - return this.stack.workspaceConfig.environments; - } - return []; - } - - /** - * Detects machine source from pointed stack. - * - * @param stack to retrieve described source - * @returns {source} machine source config - */ - getSourceFromStack(stack) { - let source = {}; - source.type = 'dockerfile'; - - switch (stack.source.type.toLowerCase()) { - case 'image': - source.content = 'FROM ' + stack.source.origin; - break; - case 'dockerfile': - source.content = stack.source.origin; - break; - default: - throw 'Not implemented'; - } - - return source; - } - - /** - * Submit a new workspace from current workspace name, source and workspace ram - * - * @param source machine source - */ - submitWorkspace(source) { - let attributes = this.stack ? {stackId: this.stack.id} : {}; - let stackWorkspaceConfig = this.stack ? this.stack.workspaceConfig : {}; - this.setEnvironment(stackWorkspaceConfig); - let workspaceConfig = this.cheAPI.getWorkspace().formWorkspaceConfig(stackWorkspaceConfig, this.workspaceName, source, this.workspaceRam); - - let creationPromise = this.cheAPI.getWorkspace().createWorkspaceFromConfig(null, workspaceConfig, attributes); - this.redirectAfterSubmitWorkspace(creationPromise); - } - - - /** - * Handle the redirect for the given promise after workspace has been created - * @param promise used to gather workspace data - */ - redirectAfterSubmitWorkspace(promise) { - promise.then((workspaceData) => { - // update list of workspaces - // for new workspace to show in recent workspaces - this.updateRecentWorkspace(workspaceData.id); - - let infoMessage = 'Workspace ' + workspaceData.config.name + ' successfully created.'; - this.cheNotification.showInfo(infoMessage); - this.cheAPI.cheWorkspace.fetchWorkspaces().then(() => { - this.$location.path('/workspace/' + workspaceData.namespace + '/' + workspaceData.config.name); - }); - }, (error) => { - let errorMessage = error.data.message ? error.data.message : 'Error during workspace creation.'; - this.cheNotification.showError(errorMessage); - }); - } - - /** - * Emit event to move workspace immediately - * to top of the recent workspaces list - * - * @param workspaceId - */ - updateRecentWorkspace(workspaceId) { - this.$rootScope.$broadcast('recent-workspace:set', workspaceId); - } - - getMachines(environment) { - let recipeType = environment.recipe.type; - let environmentManager = this.cheEnvironmentRegistry.getEnvironmentManager(recipeType); - - if (this.selectSourceOption === 'select-source-import') { - return this.importWorkspaceMachines; - } - - if (!this.stackMachines[this.stack.id]) { - this.stackMachines[this.stack.id] = environmentManager.getMachines(environment); - } - - return this.stackMachines[this.stack.id]; - } - - /** - * Updates the workspace's environment with data entered by user. - * - * @param workspace workspace to update - */ - setEnvironment(workspace) { - if (!workspace.defaultEnv || !workspace.environments || workspace.environments.length === 0) { - return; - } - - let environment = workspace.environments[workspace.defaultEnv]; - if (!environment) { - return; - } - - let recipeType = environment.recipe.type; - let environmentManager = this.cheEnvironmentRegistry.getEnvironmentManager(recipeType); - workspace.environments[workspace.defaultEnv] = environmentManager.getEnvironment(environment, this.getMachines(environment)); - } -} diff --git a/dashboard/src/app/workspaces/create-workspace/create-workspace.html b/dashboard/src/app/workspaces/create-workspace/create-workspace.html deleted file mode 100644 index 9cb60a2407c..00000000000 --- a/dashboard/src/app/workspaces/create-workspace/create-workspace.html +++ /dev/null @@ -1,94 +0,0 @@ - - - - - - - - - Workspace using recipe - Import an existing workspace configuration - - - - - - - - - - - -
- -
A name is required.
-
Workspace name may contain digits, latin letters, _ , . , - and should start only - with digits, latin - letters or underscores -
-
The name has to be more than 3 characters long.
-
The name has to be less than 20 characters long.
-
This workspace name is already used.
-
-
-
- -
- ENVIRONMENT: {{environmentKey}} - -
-
-
- MACHINE: {{machine.name}} - -
-
-
-
- -
-
- - -
- diff --git a/dashboard/src/app/workspaces/create-workspace/create-workspace.styl b/dashboard/src/app/workspaces/create-workspace/create-workspace.styl deleted file mode 100644 index c6050ed5eea..00000000000 --- a/dashboard/src/app/workspaces/create-workspace/create-workspace.styl +++ /dev/null @@ -1,41 +0,0 @@ -.create-workspace - padding 0 14px - - button - margin-left 0 - margin-right 0 - margin-bottom 16px - - .workspace-environment-name + div - margin-top 8px - - .workspace-machine - margin-left 45px - - .che-ram-allocation-slider .slider-wrapper - margin-bottom -15px - -md-select.create-workspace-select - width 100% - margin-top 0 - margin-bottom 0 - -.create-workspace-input-with-description - margin-bottom -15px - -.create-workspace-input-with-description > span:last-child - bottom 15px - font-size 12px - line-height 12px - position relative - color $disabled-color - -.create-workspace-space-top - margin-top 15px - -.create-workspace-members .workspace-members-content - padding-left 0 - padding-right 0 - -.create-workspace .che-label-container-content .create-workspace-input - margin -6px 0 diff --git a/dashboard/src/app/workspaces/workspace-details/environments/environments.controller.ts b/dashboard/src/app/workspaces/workspace-details/environments/environments.controller.ts index 27baaef10c9..fa021fde006 100644 --- a/dashboard/src/app/workspaces/workspace-details/environments/environments.controller.ts +++ b/dashboard/src/app/workspaces/workspace-details/environments/environments.controller.ts @@ -9,6 +9,8 @@ * Codenvy, S.A. - initial API and implementation */ 'use strict'; +import {CheEnvironmentRegistry} from '../../../../components/api/environment/che-environment-registry.factory'; +import {EnvironmentManager} from '../../../../components/api/environment/environment-manager'; /** * @ngdoc controller @@ -16,27 +18,40 @@ * @description This class is handling the controller for details of workspace : section environments * @author Oleksii Kurinnyi */ -export class WorkspaceEnvironmentsController { - cheEnvironmentRegistry; - environmentManager; - editorOptions; +const MIN_WORKSPACE_RAM: number = Math.pow(1024, 3); +const MAX_WORKSPACE_RAM: number = 100 * MIN_WORKSPACE_RAM; +const DEFAULT_WORKSPACE_RAM: number = 2 * MIN_WORKSPACE_RAM; - workspaceConfig; - environment; +export class WorkspaceEnvironmentsController { + cheEnvironmentRegistry: CheEnvironmentRegistry; + environmentManager: EnvironmentManager; + + editorOptions: { + lineWrapping: boolean, + lineNumbers: boolean, + readOnly: boolean, + mode?: string, + gutters: any[], + onLoad: Function + }; + + workspaceConfig: any; + environment: any; environmentName: string; newEnvironmentName: string; recipeType: string; - machines: Array; - machinesViewStatus; + machines: any[]; + machinesViewStatus: any; + devMachineName: string; - environmentOnChange; + environmentOnChange: Function; /** * Default constructor that is using resource injection * @ngInject for Dependency injection */ - constructor($scope, $timeout, cheEnvironmentRegistry) { + constructor($scope: ng.IScope, $timeout: ng.ITimeoutService, cheEnvironmentRegistry: CheEnvironmentRegistry) { this.cheEnvironmentRegistry = cheEnvironmentRegistry; this.editorOptions = { @@ -44,7 +59,7 @@ export class WorkspaceEnvironmentsController { lineNumbers: false, readOnly: true, gutters: [], - onLoad: (editor) => { + onLoad: (editor: any) => { $timeout(() => { editor.refresh(); }, 1000); @@ -52,16 +67,22 @@ export class WorkspaceEnvironmentsController { }; $scope.$watch(() => { - return this.workspaceConfig.environments; + return (this.workspaceConfig && this.workspaceConfig.environments); }, () => { - this.init(); + if (this.workspaceConfig && + this.workspaceConfig.environments && + this.workspaceConfig.environments[this.environmentName] && + this.workspaceConfig.environments[this.environmentName].recipe && + this.workspaceConfig.environments[this.environmentName].recipe.type) { + this.init(); + } }); } /** * Sets initial values */ - init() { + init(): void { this.newEnvironmentName = this.environmentName; this.environment = this.workspaceConfig.environments[this.environmentName]; @@ -71,6 +92,7 @@ export class WorkspaceEnvironmentsController { this.editorOptions.mode = this.environmentManager.editorMode; this.machines = this.environmentManager.getMachines(this.environment); + this.devMachineName = this.getDevMachineName(); if (!this.machinesViewStatus[this.environmentName]) { this.machinesViewStatus[this.environmentName] = {}; @@ -83,7 +105,7 @@ export class WorkspaceEnvironmentsController { * @param name {string} environment name to validate * @returns {boolean} */ - isUnique(name) { + isUnique(name: string): boolean { return name === this.environmentName || !this.workspaceConfig.environments[name]; } @@ -91,7 +113,7 @@ export class WorkspaceEnvironmentsController { * Updates name of environment * @param isFormValid {boolean} */ - updateEnvironmentName(isFormValid) { + updateEnvironmentName(isFormValid: boolean): void { if (!isFormValid || this.newEnvironmentName === this.environmentName) { return; } @@ -110,19 +132,38 @@ export class WorkspaceEnvironmentsController { this.doUpdateEnvironments(); } - changeMachineDev(machineName) { + /** + * Returns name of machine which includes 'ws-agent' + * + * @returns {string} name of dev machine + */ + getDevMachineName(): string { + let devMachine: any = this.machines.find((machine: any) => { + return this.environmentManager.isDev(machine); + }); + + return devMachine ? devMachine.name : ''; + } + + /** + * Add 'ws-agent' to list of agents of specified machine and remove it from lists of agents of other machines. + * + * @param machineName + * @returns {ng.IPromise} + */ + changeMachineDev(machineName: string): ng.IPromise { if (!machineName) { return; } // remove ws-agent from machine which is the dev machine now - this.machines.forEach((machine) => { + this.machines.forEach((machine: any) => { if (this.environmentManager.isDev(machine)) { this.environmentManager.setDev(machine, false); } }); - let machine = this.machines.find(machine => { + let machine = this.machines.find((machine: any) => { return machine.name === machineName; }); @@ -140,9 +181,10 @@ export class WorkspaceEnvironmentsController { /** * Callback which is called in order to update environment config - * @returns {Promise} + * + * @returns {ng.IPromise} */ - updateEnvironmentConfig() { + updateEnvironmentConfig(): ng.IPromise { let newEnvironment = this.environmentManager.getEnvironment(this.environment, this.machines); this.workspaceConfig.environments[this.newEnvironmentName] = newEnvironment; this.environment = newEnvironment; @@ -153,9 +195,10 @@ export class WorkspaceEnvironmentsController { * Callback which is called in order to rename specified machine * @param oldName * @param newName - * @returns {*} + * + * @returns {ng.IPromise} */ - updateMachineName(oldName, newName) { + updateMachineName(oldName: string, newName: string): ng.IPromise { let newEnvironment = this.environmentManager.renameMachine(this.environment, oldName, newName); this.workspaceConfig.environments[this.newEnvironmentName] = newEnvironment; @@ -164,27 +207,72 @@ export class WorkspaceEnvironmentsController { return this.doUpdateEnvironments().then(() => { this.init(); - }) + }); } /** * Callback which is called in order to delete specified machine + * * @param name - * @returns {*} + * @returns {ng.IPromise} */ - deleteMachine(name) { + deleteMachine(name: string): ng.IPromise { let newEnvironment = this.environmentManager.deleteMachine(this.environment, name); this.workspaceConfig.environments[this.newEnvironmentName] = newEnvironment; return this.doUpdateEnvironments().then(() => { this.init(); - }) + }); + } + + /** + * Callback when stack has been changed. + * + * @param config {object} workspace config + */ + changeWorkspaceStack(config: any): void { + this.workspaceConfig = config; + + if (!this.environmentName) { + this.environmentName = config.defaultEnv; + this.newEnvironmentName = this.environmentName; + } else if (this.newEnvironmentName !== config.defaultEnv) { + this.workspaceConfig.environments[this.newEnvironmentName] = this.workspaceConfig.environments[config.defaultEnv]; + delete this.workspaceConfig.environments[config.defaultEnv]; + + this.workspaceConfig.defaultEnv = this.newEnvironmentName; + } + + // for compose recipe + // check if there are machines without memory limit + let environment = this.workspaceConfig.environments[this.environmentName]; + if (environment.recipe && environment.recipe.type === 'compose') { + let recipeType = environment.recipe.type, + environmentManager = this.cheEnvironmentRegistry.getEnvironmentManager(recipeType); + let machines: any = environmentManager.getMachines(environment); + machines.forEach((machine: any) => { + if (!machine.attributes.memoryLimitBytes || machine.attributes.memoryLimitBytes < MIN_WORKSPACE_RAM || machine.attributes.memoryLimitBytes > MAX_WORKSPACE_RAM) { + environmentManager.setMemoryLimit(machine, DEFAULT_WORKSPACE_RAM); + } + }); + + // if recipe contains only one machine + // then this is the dev machine + if (machines.length === 1) { + environmentManager.setDev(machines[0], true); + } + + this.workspaceConfig.environments[this.environmentName] = environmentManager.getEnvironment(environment, machines); + } + + this.machinesViewStatus = {}; } /** * Calls parent controller's callback to update environment - * @returns {IPromise|*|Promise.} + * + * @returns {ng.IPromise} */ - doUpdateEnvironments() { + doUpdateEnvironments(): ng.IPromise { return this.environmentOnChange(); } diff --git a/dashboard/src/app/workspaces/workspace-details/environments/environments.directive.ts b/dashboard/src/app/workspaces/workspace-details/environments/environments.directive.ts index 260a211dc82..ea80b5067a4 100644 --- a/dashboard/src/app/workspaces/workspace-details/environments/environments.directive.ts +++ b/dashboard/src/app/workspaces/workspace-details/environments/environments.directive.ts @@ -25,25 +25,31 @@ * @author Oleksii Kurinnyi */ export class WorkspaceEnvironments { + restrict: string = 'E'; + templateUrl: string = 'app/workspaces/workspace-details/environments/environments.html'; + + controller: string = 'WorkspaceEnvironmentsController'; + controllerAs: string = 'workspaceEnvironmentsController'; + bindToController: boolean = true; + + scope: { + [propName: string]: string + }; /** * Default constructor that is using resource * @ngInject for Dependency injection */ constructor () { - this.restrict = 'E'; - this.templateUrl = 'app/workspaces/workspace-details/environments/environments.html'; - - this.controller = 'WorkspaceEnvironmentsController'; - this.controllerAs = 'workspaceEnvironmentsController'; - this.bindToController = true; - + // scope values this.scope = { + workspaceCreationFlow: '=', + workspaceName: '=', environmentName: '=', machinesViewStatus: '=', workspaceConfig: '=', environmentOnChange: '&' - } + }; } } diff --git a/dashboard/src/app/workspaces/workspace-details/environments/environments.html b/dashboard/src/app/workspaces/workspace-details/environments/environments.html index c6852a09495..c939c4a0396 100644 --- a/dashboard/src/app/workspaces/workspace-details/environments/environments.html +++ b/dashboard/src/app/workspaces/workspace-details/environments/environments.html @@ -38,9 +38,32 @@ + + + + + + + - + +
+ +
The environment should contain exactly one dev machine. Switch on Dev property to have terminal, SSH and IDE tooling injected to the machine.
+
+
+
; + machinesList: any[]; machineName: string; newDev: boolean; newRam: number; diff --git a/dashboard/src/app/workspaces/workspace-details/export-workspace/export-workspace.directive.ts b/dashboard/src/app/workspaces/workspace-details/export-workspace/export-workspace.directive.ts index e86836e2136..f9353e3426b 100644 --- a/dashboard/src/app/workspaces/workspace-details/export-workspace/export-workspace.directive.ts +++ b/dashboard/src/app/workspaces/workspace-details/export-workspace/export-workspace.directive.ts @@ -41,7 +41,8 @@ export class ExportWorkspace { // scope values this.scope = { workspaceId: '@workspaceId', - workspaceDetails: '=workspaceDetails' + workspaceDetails: '=workspaceDetails', + workspaceExportDisabled: '=' }; } diff --git a/dashboard/src/app/workspaces/workspace-details/export-workspace/export-workspace.html b/dashboard/src/app/workspaces/workspace-details/export-workspace/export-workspace.html index 1a21de6d176..23722cbc67a 100644 --- a/dashboard/src/app/workspaces/workspace-details/export-workspace/export-workspace.html +++ b/dashboard/src/app/workspaces/workspace-details/export-workspace/export-workspace.html @@ -1,6 +1,8 @@
diff --git a/dashboard/src/app/workspaces/create-workspace/select-stack/ready-to-go-stacks/ready-to-go-stacks.controller.ts b/dashboard/src/app/workspaces/workspace-details/select-stack/ready-to-go-stacks/ready-to-go-stacks.controller.ts similarity index 100% rename from dashboard/src/app/workspaces/create-workspace/select-stack/ready-to-go-stacks/ready-to-go-stacks.controller.ts rename to dashboard/src/app/workspaces/workspace-details/select-stack/ready-to-go-stacks/ready-to-go-stacks.controller.ts diff --git a/dashboard/src/app/workspaces/create-workspace/select-stack/ready-to-go-stacks/ready-to-go-stacks.directive.ts b/dashboard/src/app/workspaces/workspace-details/select-stack/ready-to-go-stacks/ready-to-go-stacks.directive.ts similarity index 90% rename from dashboard/src/app/workspaces/create-workspace/select-stack/ready-to-go-stacks/ready-to-go-stacks.directive.ts rename to dashboard/src/app/workspaces/workspace-details/select-stack/ready-to-go-stacks/ready-to-go-stacks.directive.ts index 6af8433d3d8..d1e955e437c 100644 --- a/dashboard/src/app/workspaces/create-workspace/select-stack/ready-to-go-stacks/ready-to-go-stacks.directive.ts +++ b/dashboard/src/app/workspaces/workspace-details/select-stack/ready-to-go-stacks/ready-to-go-stacks.directive.ts @@ -32,7 +32,7 @@ export class ReadyToGoStacks { */ constructor() { this.restrict = 'E'; - this.templateUrl = 'app/workspaces/create-workspace/select-stack/ready-to-go-stacks/ready-to-go-stacks.html'; + this.templateUrl = 'app/workspaces/workspace-details/select-stack/ready-to-go-stacks/ready-to-go-stacks.html'; this.controller = 'ReadyToGoStacksController'; this.controllerAs = 'readyToGoStacksCtrl'; diff --git a/dashboard/src/app/workspaces/create-workspace/select-stack/ready-to-go-stacks/ready-to-go-stacks.html b/dashboard/src/app/workspaces/workspace-details/select-stack/ready-to-go-stacks/ready-to-go-stacks.html similarity index 100% rename from dashboard/src/app/workspaces/create-workspace/select-stack/ready-to-go-stacks/ready-to-go-stacks.html rename to dashboard/src/app/workspaces/workspace-details/select-stack/ready-to-go-stacks/ready-to-go-stacks.html diff --git a/dashboard/src/app/workspaces/create-workspace/select-stack/ready-to-go-stacks/ready-to-go-stacks.styl b/dashboard/src/app/workspaces/workspace-details/select-stack/ready-to-go-stacks/ready-to-go-stacks.styl similarity index 100% rename from dashboard/src/app/workspaces/create-workspace/select-stack/ready-to-go-stacks/ready-to-go-stacks.styl rename to dashboard/src/app/workspaces/workspace-details/select-stack/ready-to-go-stacks/ready-to-go-stacks.styl diff --git a/dashboard/src/app/workspaces/create-workspace/select-stack/recipe/workspace-recipe.controller.ts b/dashboard/src/app/workspaces/workspace-details/select-stack/recipe/workspace-recipe.controller.ts similarity index 68% rename from dashboard/src/app/workspaces/create-workspace/select-stack/recipe/workspace-recipe.controller.ts rename to dashboard/src/app/workspaces/workspace-details/select-stack/recipe/workspace-recipe.controller.ts index 1886c6baa68..6f638ce9023 100644 --- a/dashboard/src/app/workspaces/create-workspace/select-stack/recipe/workspace-recipe.controller.ts +++ b/dashboard/src/app/workspaces/workspace-details/select-stack/recipe/workspace-recipe.controller.ts @@ -17,17 +17,31 @@ * @author Oleksii Orel */ export class WorkspaceRecipeController { + $timeout: ng.ITimeoutService; + + editingTimeoutPromise: ng.IPromise; + + recipeUrl: string; + recipeFormat: string; + recipeScript: string; + + // default selection + selectSourceOption: string = 'upload-custom-stack'; + + editorOptions: { + lineWrapping: boolean, + lineNumbers: boolean, + matchBrackets: boolean, + mode: string, + onLoad: Function + }; /** * Default constructor that is using resource * @ngInject for Dependency injection */ - constructor($timeout) { + constructor($timeout: ng.ITimeoutService) { this.$timeout = $timeout; - this.recipeUrl = null; - - //set default selection - this.selectSourceOption = 'upload-custom-stack'; this.setDefaultData(); @@ -36,59 +50,50 @@ export class WorkspaceRecipeController { lineNumbers: true, matchBrackets: true, mode: this.recipeFormat, - onLoad: (editor) => { + onLoad: (editor: any) => { this.setEditor(editor); } }; } - setDefaultData() { + setDefaultData(): void { this.recipeUrl = null; this.recipeScript = ''; this.recipeFormat = 'compose'; } - setEditor(editor) { - this.editor = editor; + setEditor(editor: any): void { editor.on('paste', () => { - this.detectFormat(editor, true); + this.detectFormat(editor); }); editor.on('change', () => { this.trackChangesInProgress(editor); }); } - trackChangesInProgress(editor) { + trackChangesInProgress(editor: any): void { if (this.editingTimeoutPromise) { this.$timeout.cancel(this.editingTimeoutPromise); } this.editingTimeoutPromise = this.$timeout(() => { - this.detectFormat(editor, false); + this.detectFormat(editor); }, 1000); } - detectFormat(editor, doFormating) { + detectFormat(editor: any): void { let content = editor.getValue(); - //compose format detection: + // compose format detection: if (content.match(/^services:\n/m)) { this.recipeFormat = 'compose'; this.editorOptions.mode = 'text/x-yaml'; } - //docker file format detection - if (content.match(/^FROM /m)) { + // docker file format detection + if (content.match(/^FROM\s+\w+/m)) { this.recipeFormat = 'dockerfile'; this.editorOptions.mode = 'text/x-dockerfile'; } } - - formatLines(editor) { - this.$timeout(() => { - for(var i = 0; i <= editor.lineCount(); i++) { - editor.indentLine(i); - } - }, 100); - } } diff --git a/dashboard/src/app/workspaces/create-workspace/select-stack/recipe/workspace-recipe.directive.ts b/dashboard/src/app/workspaces/workspace-details/select-stack/recipe/workspace-recipe.directive.ts similarity index 90% rename from dashboard/src/app/workspaces/create-workspace/select-stack/recipe/workspace-recipe.directive.ts rename to dashboard/src/app/workspaces/workspace-details/select-stack/recipe/workspace-recipe.directive.ts index 38955fe30b0..72f53558736 100644 --- a/dashboard/src/app/workspaces/create-workspace/select-stack/recipe/workspace-recipe.directive.ts +++ b/dashboard/src/app/workspaces/workspace-details/select-stack/recipe/workspace-recipe.directive.ts @@ -22,7 +22,7 @@ export class WorkspaceRecipe { */ constructor() { this.restrict = 'E'; - this.templateUrl = 'app/workspaces/create-workspace/select-stack/recipe/workspace-recipe.html'; + this.templateUrl = 'app/workspaces/workspace-details/select-stack/recipe/workspace-recipe.html'; this.replace = false; this.controller = 'WorkspaceRecipeController'; diff --git a/dashboard/src/app/workspaces/create-workspace/select-stack/recipe/workspace-recipe.html b/dashboard/src/app/workspaces/workspace-details/select-stack/recipe/workspace-recipe.html similarity index 78% rename from dashboard/src/app/workspaces/create-workspace/select-stack/recipe/workspace-recipe.html rename to dashboard/src/app/workspaces/workspace-details/select-stack/recipe/workspace-recipe.html index 4f443499335..4d81f7afd62 100644 --- a/dashboard/src/app/workspaces/create-workspace/select-stack/recipe/workspace-recipe.html +++ b/dashboard/src/app/workspaces/workspace-details/select-stack/recipe/workspace-recipe.html @@ -16,8 +16,11 @@ che-pattern="(^((ftp|http|https)://[\w@.\-\_]+(:\d{1,5})?(/[\w#!:.?+=&%@!\_\-/]+)*){1}$)" che-width="auto" ng-model="workspaceRecipeCtrl.recipeUrl" + minlength="3" + ng-required ng-maxlength="255"> -
The name has to be less than 255 characters long.
+
URL is required.
+
URL has to be less than 255 characters long.
URL is not valid.
@@ -40,7 +43,15 @@
+ +
The recipe is required.
+
diff --git a/dashboard/src/app/workspaces/create-workspace/select-stack/recipe/workspace-recipe.styl b/dashboard/src/app/workspaces/workspace-details/select-stack/recipe/workspace-recipe.styl similarity index 93% rename from dashboard/src/app/workspaces/create-workspace/select-stack/recipe/workspace-recipe.styl rename to dashboard/src/app/workspaces/workspace-details/select-stack/recipe/workspace-recipe.styl index ca2c0bafa53..c75e38c3b66 100644 --- a/dashboard/src/app/workspaces/create-workspace/select-stack/recipe/workspace-recipe.styl +++ b/dashboard/src/app/workspaces/workspace-details/select-stack/recipe/workspace-recipe.styl @@ -11,7 +11,8 @@ .recipe-editor .CodeMirror border 1px solid $list-separator-color - min-height 500px + height 300px + min-height 300px .recipe-docs-link margin-top 5px diff --git a/dashboard/src/app/workspaces/create-workspace/select-stack/stack-library/create-project-stack-library-selected-stack.filter.ts b/dashboard/src/app/workspaces/workspace-details/select-stack/stack-library/create-project-stack-library-selected-stack.filter.ts similarity index 100% rename from dashboard/src/app/workspaces/create-workspace/select-stack/stack-library/create-project-stack-library-selected-stack.filter.ts rename to dashboard/src/app/workspaces/workspace-details/select-stack/stack-library/create-project-stack-library-selected-stack.filter.ts diff --git a/dashboard/src/app/workspaces/create-workspace/select-stack/stack-library/create-project-stack-library.controller.ts b/dashboard/src/app/workspaces/workspace-details/select-stack/stack-library/create-project-stack-library.controller.ts similarity index 100% rename from dashboard/src/app/workspaces/create-workspace/select-stack/stack-library/create-project-stack-library.controller.ts rename to dashboard/src/app/workspaces/workspace-details/select-stack/stack-library/create-project-stack-library.controller.ts diff --git a/dashboard/src/app/workspaces/create-workspace/select-stack/stack-library/create-project-stack-library.directive.ts b/dashboard/src/app/workspaces/workspace-details/select-stack/stack-library/create-project-stack-library.directive.ts similarity index 90% rename from dashboard/src/app/workspaces/create-workspace/select-stack/stack-library/create-project-stack-library.directive.ts rename to dashboard/src/app/workspaces/workspace-details/select-stack/stack-library/create-project-stack-library.directive.ts index 88e0999b5b3..99501abba2b 100644 --- a/dashboard/src/app/workspaces/create-workspace/select-stack/stack-library/create-project-stack-library.directive.ts +++ b/dashboard/src/app/workspaces/workspace-details/select-stack/stack-library/create-project-stack-library.directive.ts @@ -29,7 +29,7 @@ export class CreateProjectStackLibrary { */ constructor() { this.restrict = 'E'; - this.templateUrl = 'app/workspaces/create-workspace/select-stack/stack-library/create-project-stack-library.html'; + this.templateUrl = 'app/workspaces/workspace-details/select-stack/stack-library/create-project-stack-library.html'; this.controller = 'CreateProjectStackLibraryController'; this.controllerAs = 'createProjectStackLibraryCtrl'; diff --git a/dashboard/src/app/workspaces/create-workspace/select-stack/stack-library/create-project-stack-library.html b/dashboard/src/app/workspaces/workspace-details/select-stack/stack-library/create-project-stack-library.html similarity index 100% rename from dashboard/src/app/workspaces/create-workspace/select-stack/stack-library/create-project-stack-library.html rename to dashboard/src/app/workspaces/workspace-details/select-stack/stack-library/create-project-stack-library.html diff --git a/dashboard/src/app/workspaces/create-workspace/select-stack/stack-library/stack-library-filter/che-stack-library-filter.controller.ts b/dashboard/src/app/workspaces/workspace-details/select-stack/stack-library/stack-library-filter/che-stack-library-filter.controller.ts similarity index 100% rename from dashboard/src/app/workspaces/create-workspace/select-stack/stack-library/stack-library-filter/che-stack-library-filter.controller.ts rename to dashboard/src/app/workspaces/workspace-details/select-stack/stack-library/stack-library-filter/che-stack-library-filter.controller.ts diff --git a/dashboard/src/app/workspaces/create-workspace/select-stack/stack-library/stack-library-filter/che-stack-library-filter.directive.ts b/dashboard/src/app/workspaces/workspace-details/select-stack/stack-library/stack-library-filter/che-stack-library-filter.directive.ts similarity index 94% rename from dashboard/src/app/workspaces/create-workspace/select-stack/stack-library/stack-library-filter/che-stack-library-filter.directive.ts rename to dashboard/src/app/workspaces/workspace-details/select-stack/stack-library/stack-library-filter/che-stack-library-filter.directive.ts index 58aaba4999c..9d9d400e097 100644 --- a/dashboard/src/app/workspaces/create-workspace/select-stack/stack-library/stack-library-filter/che-stack-library-filter.directive.ts +++ b/dashboard/src/app/workspaces/workspace-details/select-stack/stack-library/stack-library-filter/che-stack-library-filter.directive.ts @@ -29,7 +29,7 @@ export class CheStackLibraryFilter { */ constructor () { this.restrict = 'E'; - this.templateUrl = 'app/workspaces/create-workspace/select-stack/stack-library/stack-library-filter/che-stack-library-filter.html'; + this.templateUrl = 'app/workspaces/workspace-details/select-stack/stack-library/stack-library-filter/che-stack-library-filter.html'; this.controller = 'CheStackLibraryFilterController'; this.controllerAs = 'cheStackLibraryFilterCtrl'; diff --git a/dashboard/src/app/workspaces/create-workspace/select-stack/stack-library/stack-library-filter/che-stack-library-filter.html b/dashboard/src/app/workspaces/workspace-details/select-stack/stack-library/stack-library-filter/che-stack-library-filter.html similarity index 100% rename from dashboard/src/app/workspaces/create-workspace/select-stack/stack-library/stack-library-filter/che-stack-library-filter.html rename to dashboard/src/app/workspaces/workspace-details/select-stack/stack-library/stack-library-filter/che-stack-library-filter.html diff --git a/dashboard/src/app/workspaces/create-workspace/select-stack/stack-library/stack-library-filter/che-stack-library-filter.styl b/dashboard/src/app/workspaces/workspace-details/select-stack/stack-library/stack-library-filter/che-stack-library-filter.styl similarity index 100% rename from dashboard/src/app/workspaces/create-workspace/select-stack/stack-library/stack-library-filter/che-stack-library-filter.styl rename to dashboard/src/app/workspaces/workspace-details/select-stack/stack-library/stack-library-filter/che-stack-library-filter.styl diff --git a/dashboard/src/app/workspaces/create-workspace/select-stack/stack-library/stack-library-selecter/che-stack-library-selecter.directive.ts b/dashboard/src/app/workspaces/workspace-details/select-stack/stack-library/stack-library-selecter/che-stack-library-selecter.directive.ts similarity index 87% rename from dashboard/src/app/workspaces/create-workspace/select-stack/stack-library/stack-library-selecter/che-stack-library-selecter.directive.ts rename to dashboard/src/app/workspaces/workspace-details/select-stack/stack-library/stack-library-selecter/che-stack-library-selecter.directive.ts index 9d572c42df3..58b3c291fad 100644 --- a/dashboard/src/app/workspaces/create-workspace/select-stack/stack-library/stack-library-selecter/che-stack-library-selecter.directive.ts +++ b/dashboard/src/app/workspaces/workspace-details/select-stack/stack-library/stack-library-selecter/che-stack-library-selecter.directive.ts @@ -23,7 +23,7 @@ export class CheStackLibrarySelecter { */ constructor () { this.restrict='E'; - this.templateUrl = 'app/workspaces/create-workspace/select-stack/stack-library/stack-library-selecter/che-stack-library-selecter.html'; + this.templateUrl = 'app/workspaces/workspace-details/select-stack/stack-library/stack-library-selecter/che-stack-library-selecter.html'; // scope values this.scope = { diff --git a/dashboard/src/app/workspaces/create-workspace/select-stack/stack-library/stack-library-selecter/che-stack-library-selecter.html b/dashboard/src/app/workspaces/workspace-details/select-stack/stack-library/stack-library-selecter/che-stack-library-selecter.html similarity index 100% rename from dashboard/src/app/workspaces/create-workspace/select-stack/stack-library/stack-library-selecter/che-stack-library-selecter.html rename to dashboard/src/app/workspaces/workspace-details/select-stack/stack-library/stack-library-selecter/che-stack-library-selecter.html diff --git a/dashboard/src/app/workspaces/create-workspace/select-stack/stack-library/stack-library-selecter/che-stack-library-selecter.styl b/dashboard/src/app/workspaces/workspace-details/select-stack/stack-library/stack-library-selecter/che-stack-library-selecter.styl similarity index 100% rename from dashboard/src/app/workspaces/create-workspace/select-stack/stack-library/stack-library-selecter/che-stack-library-selecter.styl rename to dashboard/src/app/workspaces/workspace-details/select-stack/stack-library/stack-library-selecter/che-stack-library-selecter.styl diff --git a/dashboard/src/app/workspaces/create-workspace/select-stack/workspace-select-stack.controller.ts b/dashboard/src/app/workspaces/workspace-details/select-stack/workspace-select-stack.controller.ts similarity index 100% rename from dashboard/src/app/workspaces/create-workspace/select-stack/workspace-select-stack.controller.ts rename to dashboard/src/app/workspaces/workspace-details/select-stack/workspace-select-stack.controller.ts diff --git a/dashboard/src/app/workspaces/create-workspace/select-stack/workspace-select-stack.directive.ts b/dashboard/src/app/workspaces/workspace-details/select-stack/workspace-select-stack.directive.ts similarity index 91% rename from dashboard/src/app/workspaces/create-workspace/select-stack/workspace-select-stack.directive.ts rename to dashboard/src/app/workspaces/workspace-details/select-stack/workspace-select-stack.directive.ts index 4429cc01888..d3ab9a30cc4 100644 --- a/dashboard/src/app/workspaces/create-workspace/select-stack/workspace-select-stack.directive.ts +++ b/dashboard/src/app/workspaces/workspace-details/select-stack/workspace-select-stack.directive.ts @@ -22,7 +22,7 @@ export class WorkspaceSelectStack { */ constructor() { this.restrict = 'E'; - this.templateUrl = 'app/workspaces/create-workspace/select-stack/workspace-select-stack.html'; + this.templateUrl = 'app/workspaces/workspace-details/select-stack/workspace-select-stack.html'; this.replace = true; this.bindToController = true; this.controller = 'WorkspaceSelectStackController'; diff --git a/dashboard/src/app/workspaces/create-workspace/select-stack/workspace-select-stack.html b/dashboard/src/app/workspaces/workspace-details/select-stack/workspace-select-stack.html similarity index 90% rename from dashboard/src/app/workspaces/create-workspace/select-stack/workspace-select-stack.html rename to dashboard/src/app/workspaces/workspace-details/select-stack/workspace-select-stack.html index dc7b44cf997..5b61ec95c7c 100644 --- a/dashboard/src/app/workspaces/create-workspace/select-stack/workspace-select-stack.html +++ b/dashboard/src/app/workspaces/workspace-details/select-stack/workspace-select-stack.html @@ -24,7 +24,8 @@ Custom stack - 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 64b93784560..18aab4950e5 100644 --- a/dashboard/src/app/workspaces/workspace-details/workspace-details.controller.ts +++ b/dashboard/src/app/workspaces/workspace-details/workspace-details.controller.ts @@ -9,127 +9,168 @@ * Codenvy, S.A. - initial API and implementation */ 'use strict'; +import {CheEnvironmentRegistry} from '../../../components/api/environment/che-environment-registry.factory'; +import {CheNotification} from '../../../components/notification/che-notification.factory'; +import {CheWorkspace} from '../../../components/api/che-workspace.factory'; +import IdeSvc from '../../ide/ide.service'; +import {WorkspaceDetailsService} from './workspace-details.service'; /** - * Controller for a workspace details. + * @ngdoc controller + * @name workspaces.workspace.details.controller:WorkspaceDetailsController + * @description This class is handling the controller for workspace to create and edit. * @author Ann Shumilova + * @author Oleksii Kurinnyi */ + +enum Tab {Settings, Runtime} + export class WorkspaceDetailsController { - $location; - $log; - $mdDialog; - $q; - $rootScope: angular.IRootScopeService; - $route; - $scope: angular.IScope; - $timeout; - cheNotification; - cheWorkspace; - ideSvc; - lodash; - workspaceDetailsService; - - workspaceDetails; - copyWorkspaceDetails; - machinesViewStatus; + $location: ng.ILocationService; + $log: ng.ILogService; + $mdDialog: ng.material.IDialogService; + $q: ng.IQService; + $route: ng.route.IRouteService; + $rootScope: ng.IRootScopeService; + $scope: ng.IScope; + $timeout: ng.ITimeoutService; + cheEnvironmentRegistry: CheEnvironmentRegistry; + cheNotification: CheNotification; + cheWorkspace: CheWorkspace; + ideSvc: IdeSvc; + workspaceDetailsService: WorkspaceDetailsService; + + loading: boolean = false; + isCreationFlow: boolean = true; + selectedTabIndex: number; + namespace: string; workspaceId: string; workspaceName: string; newName: string; - workspaceKey: string; - editMode: boolean; - showApplyMessage: boolean; - loading: boolean; - timeoutPromise: Promise; - invalidWorkspace: string; - selectedTabIndex; + stack: any; + workspaceDetails: any = {}; + copyWorkspaceDetails: any = {}; + machinesViewStatus: any = {}; errorMessage: string; + invalidWorkspace: string; + editMode: boolean = false; + showApplyMessage: boolean = false; + + usedNamesList: any = []; + + forms: Map = new Map(); + tab: Object; /** * Default constructor that is using resource injection * @ngInject for Dependency injection */ - constructor($scope, $rootScope, $route, $location, cheWorkspace, $mdDialog, cheNotification, ideSvc, $log, workspaceDetailsService, lodash, $q, $timeout) { + constructor($location: ng.ILocationService, $log: ng.ILogService, $mdDialog: ng.material.IDialogService, $q: ng.IQService, $route: ng.route.IRouteService, $rootScope: ng.IRootScopeService, $scope: ng.IScope, $timeout: ng.ITimeoutService, cheEnvironmentRegistry: CheEnvironmentRegistry, cheNotification: CheNotification, cheWorkspace: CheWorkspace, ideSvc: IdeSvc, workspaceDetailsService: WorkspaceDetailsService) { + this.$log = $log; + this.$location = $location; + this.$mdDialog = $mdDialog; + this.$q = $q; + this.$route = $route; this.$rootScope = $rootScope; + this.$scope = $scope; + this.$timeout = $timeout; + this.cheEnvironmentRegistry = cheEnvironmentRegistry; this.cheNotification = cheNotification; this.cheWorkspace = cheWorkspace; - this.$mdDialog = $mdDialog; - this.$location = $location; this.ideSvc = ideSvc; - this.$log = $log; this.workspaceDetailsService = workspaceDetailsService; - this.lodash = lodash; - this.$q = $q; - this.$timeout = $timeout; - this.workspaceDetails = {}; - this.copyWorkspaceDetails = {}; - this.machinesViewStatus = {}; - this.namespace = $route.current.params.namespace; - this.workspaceName = $route.current.params.workspaceName; - this.workspaceKey = this.namespace + ":" + this.workspaceName; - this.editMode = false; - this.showApplyMessage = false; + this.tab = Tab; - this.loading = true; - this.timeoutPromise; - $scope.$on('$destroy', () => { - if (this.timeoutPromise) { - $timeout.cancel(this.timeoutPromise); - } + cheWorkspace.fetchWorkspaces().then(() => { + let workspaces: any[] = cheWorkspace.getWorkspaces(); + workspaces.forEach((workspace: any) => { + this.usedNamesList.push(workspace.config.name); + }); }); - if (!this.cheWorkspace.getWorkspaceByName(this.namespace, this.workspaceName)) { - let promise = this.cheWorkspace.fetchWorkspaceDetails(this.workspaceKey); - promise.then(() => { + (this.$rootScope as any).showIDE = false; + + this.init(); + } + + init(): void { + let routeParams = this.$route.current.params; + if (routeParams && routeParams.namespace && routeParams.workspaceName) { + this.isCreationFlow = false; + this.namespace = routeParams.namespace; + this.workspaceName = routeParams.workspaceName; + + this.fetchWorkspaceDetails().then(() => { + this.workspaceDetails = this.cheWorkspace.getWorkspaceByName(this.namespace, this.workspaceName); this.updateWorkspaceData(); - }, (error) => { - if (error.status === 304) { - this.updateWorkspaceData(); - } else { - this.loading = false; - this.invalidWorkspace = error.statusText; - } + }).finally(() => { + this.loading = false; }); + + // search the selected page + let page = this.$route.current.params.page; + if (!page) { + this.$location.path('/workspace/' + this.namespace + '/' + this.workspaceName); + } else { + let selectedTabIndex = Tab.Settings; + switch (page) { + case 'info': + selectedTabIndex = Tab.Settings; + break; + case 'projects': + selectedTabIndex = 2; + break; + case 'share': + selectedTabIndex = 3; + break; + default: + this.$location.path('/workspace/' + this.namespace + '/' + this.workspaceName); + break; + } + this.$timeout(() => { + this.selectedTabIndex = selectedTabIndex; + }); + } } else { - this.updateWorkspaceData(); + this.isCreationFlow = true; + this.namespace = ''; + this.workspaceName = this.generateWorkspaceName(); + this.copyWorkspaceDetails = {config: {}}; } + this.newName = this.workspaceName; + } - this.cheWorkspace.fetchWorkspaces(); + /** + * Fetches the workspace details. + * + * @returns {Promise} + */ + fetchWorkspaceDetails(): ng.IPromise { + let defer = this.$q.defer(); - //search the selected page - let page = $route.current.params.page; - if (!page) { - $location.path('/workspace/' + this.namespace + '/' + this.workspaceName); + if (this.cheWorkspace.getWorkspaceByName(this.namespace, this.workspaceName)) { + defer.resolve(); } else { - switch (page) { - case 'info': - this.selectedTabIndex = 0; - break; - case 'projects': - this.selectedTabIndex = 2; - break; - case 'share': - this.selectedTabIndex = 3; - break; - default: - $location.path('/workspace/' + this.namespace + '/' + this.workspaceName); - break; - } + this.cheWorkspace.fetchWorkspaceDetails(this.namespace + ':' + this.workspaceName).then(() => { + defer.resolve(); + }, (error: any) => { + if (error.status === 304) { + defer.resolve(); + } else { + this.invalidWorkspace = error.statusText; + defer.reject(error); + } + }); } + return defer.promise; } /** - * Returns workspace details sections (tabs, example - projects) - * @returns {*} + * Creates copy of workspace config. */ - getSections() { - return this.workspaceDetailsService.getSections(); - } - - //Update the workspace data to be displayed. - updateWorkspaceData() { - this.workspaceDetails = this.cheWorkspace.getWorkspaceByName(this.namespace, this.workspaceName); + updateWorkspaceData(): void { if (this.loading) { this.loading = false; } @@ -142,72 +183,83 @@ export class WorkspaceDetailsController { /** * Returns true if name of workspace is changed. + * * @returns {boolean} */ - isNameChanged() { - if (this.workspaceDetails) { + isNameChanged(): boolean { + if (this.workspaceDetails && this.workspaceDetails.config) { return this.workspaceDetails.config.name !== this.newName; } return false; } /** - * Updates name of workspace - * @param isFormValid {boolean} true if workspaceNameForm is valid + * Updates name of workspace in config. + * + * @param isFormValid {boolean} */ - updateName(isFormValid) { + updateName(isFormValid: boolean): void { if (isFormValid === false || !this.isNameChanged()) { return; } this.copyWorkspaceDetails.config.name = this.newName; - this.doUpdateWorkspace(); + + if (!this.isCreationFlow) { + this.doUpdateWorkspace(); + } } /** - * Callback which is called after workspace config was changed - * @returns {Promise} + * Returns current status of workspace. + * + * @returns {String} */ - updateWorkspaceConfig() { - this.editMode = !angular.equals(this.copyWorkspaceDetails.config, this.workspaceDetails.config); - - let status = this.getWorkspaceStatus(); - if (status === 'STOPPED' || status === 'STOPPING') { - this.showApplyMessage = false; - } else { - this.showApplyMessage = true; + getWorkspaceStatus(): string { + if (this.isCreationFlow) { + return 'CREATING'; } - let defer = this.$q.defer(); - defer.resolve(); - return defer.promise; + let unknownStatus = 'unknown'; + let workspace = this.cheWorkspace.getWorkspaceById(this.workspaceId); + return workspace ? workspace.status : unknownStatus; } /** - * Updates workspace info. + * Returns workspace details sections (tabs, example - projects) + * + * @returns {*} */ - doUpdateWorkspace() { - delete this.copyWorkspaceDetails.links; + getSections(): any { + return this.workspaceDetailsService.getSections(); + } - let promise = this.cheWorkspace.updateWorkspace(this.workspaceId, this.copyWorkspaceDetails); - promise.then((data) => { - this.workspaceName = data.config.name; - this.updateWorkspaceData(); - this.cheNotification.showInfo('Workspace updated.'); - return this.$location.path('/workspace/' + this.namespace + '/' + this.workspaceName); - }, (error) => { - this.loading = false; - this.cheNotification.showError(error.data.message !== null ? error.data.message : 'Update workspace failed.'); - this.$log.error(error); - }); + /** + * Callback when environment has been changed. + * + * @returns {ng.IPromise} + */ + updateWorkspaceConfig(): ng.IPromise { + if (!this.isCreationFlow) { + this.editMode = !angular.equals(this.copyWorkspaceDetails.config, this.workspaceDetails.config); + + let status = this.getWorkspaceStatus(); + if (status === 'STOPPED' || status === 'STOPPING') { + this.showApplyMessage = false; + } else { + this.showApplyMessage = true; + } + } - return promise; + let defer = this.$q.defer(); + defer.resolve(); + return defer.promise; } /** * Updates workspace config and restarts workspace if it's necessary */ - applyConfigChanges() { + applyConfigChanges(): void { this.editMode = false; this.showApplyMessage = false; @@ -248,13 +300,117 @@ export class WorkspaceDetailsController { /** * Cancels workspace config changes that weren't stored */ - cancelConfigChanges() { + cancelConfigChanges(): void { this.editMode = false; this.updateWorkspaceData(); } - //Perform workspace deletion. - deleteWorkspace(event) { + /** + * Updates workspace info. + * + * @returns {ng.IPromise} + */ + doUpdateWorkspace(): ng.IPromise { + delete this.copyWorkspaceDetails.links; + + let promise = this.cheWorkspace.updateWorkspace(this.workspaceId, this.copyWorkspaceDetails); + promise.then((data: any) => { + this.workspaceName = data.config.name; + this.workspaceDetails = this.cheWorkspace.getWorkspaceByName(this.namespace, this.workspaceName); + this.updateWorkspaceData(); + this.cheNotification.showInfo('Workspace updated.'); + return this.$location.path('/workspace/' + this.namespace + '/' + this.workspaceName); + }, (error: any) => { + this.loading = false; + this.cheNotification.showError(error.data.message !== null ? error.data.message : 'Update workspace failed.'); + this.$log.error(error); + }); + + return promise; + } + + /** + * Generates a default workspace name + * + * @returns {String} name of workspace + */ + generateWorkspaceName(): string { + let name, + iterations = 100; + while (iterations--) { + name = 'wksp-' + (('0000' + (Math.random() * Math.pow(36, 4) << 0).toString(36)).slice(-4)); // jshint ignore:line + if (!this.usedNamesList.includes(name)) { + break; + } + } + return name; + } + + /** + * Submit a new workspace + */ + createWorkspace(): void { + let attributes = this.stack ? {stackId: this.stack.id} : {}; + let creationPromise = this.cheWorkspace.createWorkspaceFromConfig(null, this.copyWorkspaceDetails.config, attributes); + this.redirectAfterSubmitWorkspace(creationPromise); + } + + + /** + * Handle the redirect for the given promise after workspace has been created + * + * @param promise {ng.IPromise} used to gather workspace data + */ + redirectAfterSubmitWorkspace(promise: ng.IPromise): void { + promise.then((workspaceData: any) => { + // update list of workspaces + // for new workspace to show in recent workspaces + this.updateRecentWorkspace(workspaceData.id); + + let infoMessage = 'Workspace ' + workspaceData.config.name + ' successfully created.'; + this.cheNotification.showInfo(infoMessage); + this.cheWorkspace.fetchWorkspaces().then(() => { + this.$location.path('/workspace/' + workspaceData.namespace + '/' + workspaceData.config.name); + }); + }, (error: any) => { + let errorMessage = error.data.message ? error.data.message : 'Error during workspace creation.'; + this.cheNotification.showError(errorMessage); + }); + } + + /** + * Emit event to move workspace immediately + * to top of the recent workspaces list in left navbar + * + * @param workspaceId {string} + */ + updateRecentWorkspace(workspaceId: string): void { + this.$rootScope.$broadcast('recent-workspace:set', workspaceId); + } + + /** + * Updates the workspace's environment with data entered by user. + * + * @param workspace workspace to update + */ + setEnvironment(workspace: any): void { + if (!workspace.defaultEnv || !workspace.environments || workspace.environments.length === 0) { + return; + } + + let environment = workspace.environments[workspace.defaultEnv]; + if (!environment) { + return; + } + + let recipeType = environment.recipe.type; + let environmentManager = this.cheEnvironmentRegistry.getEnvironmentManager(recipeType); + let machinesList = environmentManager.getMachines(environment); + workspace.environments[workspace.defaultEnv] = environmentManager.getEnvironment(environment, machinesList); + } + + // perform workspace deletion. + deleteWorkspace(event: MouseEvent): void { let confirm = this.$mdDialog.confirm() .title('Would you like to delete workspace \'' + this.workspaceDetails.config.name + '\'?') .ariaLabel('Delete workspace') @@ -274,12 +430,12 @@ export class WorkspaceDetailsController { }); } - removeWorkspace() { + removeWorkspace(): ng.IPromise { let promise = this.cheWorkspace.deleteWorkspaceConfig(this.workspaceId); promise.then(() => { this.$location.path('/workspaces'); - }, (error) => { + }, (error: any) => { this.cheNotification.showError(error.data.message !== null ? error.data.message : 'Delete workspace failed.'); this.$log.error(error); }); @@ -287,30 +443,30 @@ export class WorkspaceDetailsController { return promise; } - runWorkspace() { + runWorkspace(): void { delete this.errorMessage; let promise = this.ideSvc.startIde(this.workspaceDetails); - promise.then(() => {}, (error) => { - let errorMessage; - - if (!error || !(error.data || error.error)) { - errorMessage = 'Unable to start this workspace.'; - } else if (error.error) { - errorMessage = error.error; - } else if (error.data.errorCode === 10000 && error.data.attributes) { - let attributes = error.data.attributes; - - errorMessage = 'Unable to start this workspace.' + - ' There are ' + attributes.workspaces_count + ' running workspaces consuming ' + - attributes.used_ram + attributes.ram_unit + ' RAM.' + - ' Your current RAM limit is ' + attributes.limit_ram + attributes.ram_unit + - '. This workspace requires an additional ' + - attributes.required_ram + attributes.ram_unit + '.' + - ' You can stop other workspaces to free resources.'; - } else { - errorMessage = error.data.message; - } + promise.then(() => {}, (error: any) => { + let errorMessage; + + if (!error || !(error.data || error.error)) { + errorMessage = 'Unable to start this workspace.'; + } else if (error.error) { + errorMessage = error.error; + } else if (error.data.errorCode === 10000 && error.data.attributes) { + let attributes = error.data.attributes; + + errorMessage = 'Unable to start this workspace.' + + ' There are ' + attributes.workspaces_count + ' running workspaces consuming ' + + attributes.used_ram + attributes.ram_unit + ' RAM.' + + ' Your current RAM limit is ' + attributes.limit_ram + attributes.ram_unit + + '. This workspace requires an additional ' + + attributes.required_ram + attributes.ram_unit + '.' + + ' You can stop other workspaces to free resources.'; + } else { + errorMessage = error.data.message; + } this.cheNotification.showError(errorMessage); this.$log.error(error); @@ -319,10 +475,10 @@ export class WorkspaceDetailsController { }); } - stopWorkspace() { + stopWorkspace(): void { let promise = this.cheWorkspace.stopWorkspace(this.workspaceId); - promise.then(() => {}, (error) => { + promise.then(() => {}, (error: any) => { this.cheNotification.showError(error.data.message !== null ? error.data.message : 'Stop workspace failed.'); this.$log.error(error); }); @@ -331,19 +487,54 @@ export class WorkspaceDetailsController { /** * Creates snapshot of workspace */ - createSnapshotWorkspace() { - this.cheWorkspace.createSnapshot(this.workspaceId).then(() => {}, (error) => { + createSnapshotWorkspace(): void { + this.cheWorkspace.createSnapshot(this.workspaceId).then(() => {}, (error: any) => { this.cheNotification.showError(error.data.message !== null ? error.data.message : 'Creating snapshot failed.'); this.$log.error(error); }); } /** - * Returns current status of workspace - * @returns {String} + * Register forms + * + * @param tab {number} tab number + * @param form */ - getWorkspaceStatus() { - let workspace = this.cheWorkspace.getWorkspaceById(this.workspaceId); - return workspace ? workspace.status : 'unknown'; + setForm(tab: number, form: ng.IFormController): void { + this.forms.set(tab, form); + } + + /** + * Returns false if all forms from specified tabs are valid + * + * @param tabs {Array} list of tab IDs + * @returns {boolean} + */ + checkFormsNotValid(tabs: number[]): boolean { + return tabs.some((tab: number) => { + let form = this.forms.get(tab); + return form && form.$invalid; + }); + } + + /** + * Returns true when 'Create' button should be disabled + * + * @returns {boolean} + */ + isCreateButtonDisabled(): boolean { + let tabs = [Tab.Settings, Tab.Runtime]; + + return this.checkFormsNotValid(tabs); + } + + /** + * Returns true when 'Runtime' tab should be disabled + * + * @returns {boolean} + */ + isRuntimeTabDisabled(): boolean { + return this.checkFormsNotValid([Tab.Settings]); } } + diff --git a/dashboard/src/app/workspaces/workspace-details/workspace-details.html b/dashboard/src/app/workspaces/workspace-details/workspace-details.html index 43cafa45149..4bddddb3da7 100644 --- a/dashboard/src/app/workspaces/workspace-details/workspace-details.html +++ b/dashboard/src/app/workspaces/workspace-details/workspace-details.html @@ -1,54 +1,61 @@ - - + +
+ + +
- - - - + + + + md-selected="workspaceDetailsController.selectedTabIndex" + md-center-tabs=""> + + Settings -
+
-
A name is required.
The name should not contain special characters like space, dollar, etc.
-
The name has to be less than 128 characters long.
-
The name has to be less than 128 characters long.
+
The name has to be more than 3 characters long.
+
The name has to be less than 20 characters long.
This workspace name is already used.
@@ -58,26 +65,26 @@
- +
- {{workspaceDetailsCtrl.errorMessage}} + ng-show="(workspaceDetailsController.errorMessage)"> + {{workspaceDetailsController.errorMessage}}
- - + + ng-click="workspaceDetailsController.stopWorkspace()">
- + ng-click="workspaceDetailsController.createSnapshotWorkspace()">
@@ -85,39 +92,47 @@ - + - + ng-disabled="!(workspaceDetailsController.isCreationFlow === false || workspaceDetailsController.getWorkspaceStatus() === 'RUNNING' || workspaceDetailsController.getWorkspaceStatus() === 'STOPPED' || workspaceDetailsController.getWorkspaceStatus() === 'ERROR')" + ng-click="workspaceDetailsController.deleteWorkspace($event)">
- - + + + Runtime - + + + - + {{section.title}} @@ -129,14 +144,12 @@ - + workspace-edit-mode-show-message="workspaceDetailsController.showApplyMessage" + workspace-edit-mode-on-save="workspaceDetailsController.applyConfigChanges()" + workspace-edit-mode-on-cancel="workspaceDetailsController.cancelConfigChanges()"> - - {{workspaceDetailsCtrl.invalidWorkspace}} + + {{workspaceDetailsController.invalidWorkspace}} - - diff --git a/dashboard/src/app/workspaces/workspace-details/workspace-details.styl b/dashboard/src/app/workspaces/workspace-details/workspace-details.styl index 55073eb93da..a1e8acde0b8 100644 --- a/dashboard/src/app/workspaces/workspace-details/workspace-details.styl +++ b/dashboard/src/app/workspaces/workspace-details/workspace-details.styl @@ -1,29 +1,34 @@ -.workspace-details-description - color $disabled-color - .workspace-details-content - padding 0 14px + md-tab-content + padding 0 14px + + .workspace-details-tab-content + + .workspace-details-description + color $disabled-color + + button + margin 0 30px 0 0 + + .workspace-status-indicator .fa-circle + font-size 19px - button - margin 0 30px 0 0 + span.workspace-status-text + color lighten($che-ide-background-color, 40%) !important + margin-bottom 4px + margin-left 10px - .workspace-status-indicator .fa-circle - font-size 19px + .workspace-restart-message + color $disabled-color + margin-top 15px - span.workspace-status-text - color lighten($che-ide-background-color, 40%) !important - margin-bottom 4px - margin-left 10px + .workspace-details-delete-label label + color $che-delete-label-color - .workspace-delete-label label - color $che-delete-label-color + .che-label-container-content - .che-label-container-content - .workspace-details-input - margin -6px 0 - .workspace-details-action-buttons - margin-top 20px + .workspace-details-input + margin -6px 0 - .workspace-details-restart-message - color $disabled-color - margin-top 15px + .workspace-details-action-buttons + margin-top 20px 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 f35f9746525..6bfea1bb7e3 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 @@ -60,6 +60,7 @@ export class WorkspaceDetailsProjectsCtrl { updateProjectsData() { this.workspace = this.cheWorkspace.getWorkspaceByName(this.namespace, this.workspaceName); this.projects = this.workspace.config.projects; + this.workspaceId = this.workspace.id; } /** diff --git a/dashboard/src/app/workspaces/workspace-details/workspace-projects/workspace-details-projects.html b/dashboard/src/app/workspaces/workspace-details/workspace-projects/workspace-details-projects.html index 494cdccb775..0b57d87ef6a 100644 --- a/dashboard/src/app/workspaces/workspace-details/workspace-projects/workspace-details-projects.html +++ b/dashboard/src/app/workspaces/workspace-details/workspace-projects/workspace-details-projects.html @@ -15,7 +15,7 @@ { return this.recipeScript; }, () => { + if (this.isCustomStack) { + this.cheStackLibrarySelecter(null); + } + }); + $scope.$watch(() => { return this.recipeUrl; }, () => { + if (this.isCustomStack) { + this.cheStackLibrarySelecter(null); + } + }); + $scope.$watch(() => { return this.recipeFormat; }, () => { + if (this.isCustomStack) { + this.cheStackLibrarySelecter(null); + } + }); + } + + /** + * Callback when tab has been change. + * + * @param tabName {string} the select tab name + */ + setStackTab(tabName: string): void { + if (tabName === 'custom-stack') { + this.cheStackLibrarySelecter(null); + this.isCustomStack = true; + } + } + + /** + * Callback when stack has been set. + * + * @param stack {object} the selected stack + */ + cheStackLibrarySelecter(stack: any): void { + if (stack) { + this.isCustomStack = false; + this.recipeUrl = null; + } + this.stack = stack; + + let source = this.getSource(); + let config = this.buildWorkspaceConfig(source); + + if (!config.defaultEnv || (!this.stack && !this.recipeFormat)) { + return; + } + + this.workspaceStackOnChange({config: config}); + } + + /** + * Builds workspace config. + * + * @param source + * @returns {config} + */ + buildWorkspaceConfig(source: any): any { + let stackWorkspaceConfig; + if (this.stack) { + stackWorkspaceConfig = this.stack.workspaceConfig; + } else if (!this.stack && source && source.format === 'compose' && source.content) { + let machines = this.composeEnvironmentManager.getMachines({recipe: source}), + environment = this.composeEnvironmentManager.getEnvironment({recipe: source}, machines); + stackWorkspaceConfig = { + defaultEnv: this.workspaceName, + environments: { + [this.workspaceName]: environment + } + }; + } + + + return this.cheWorkspace.formWorkspaceConfig(stackWorkspaceConfig, this.workspaceName, source, DEFAULT_WORKSPACE_RAM); + } + + /** + * Returns stack source. + * + * @returns {object} + */ + getSource(): any { + let source: any = {}; + source.type = 'dockerfile'; + // user provides recipe URL or recipe's content: + if (this.isCustomStack) { + this.stack = null; + source.type = 'environment'; + source.format = this.recipeFormat; + if (this.recipeUrl && this.recipeUrl.length > 0) { + source.location = this.recipeUrl; + } else { + source.content = this.recipeScript; + } + } else if (this.stack) { + // check predefined recipe location + if (this.stack && this.stack.source && this.stack.source.type === 'location') { + this.recipeUrl = this.stack.source.origin; + source.location = this.recipeUrl; + } else { + source = this.getSourceFromStack(this.stack); + } + } + return source; + } + + /** + * Detects machine source from pointed stack. + * + * @param stack {object} to retrieve described source + * @returns {source} machine source config + */ + getSourceFromStack(stack: any): any { + let source: any = {}; + source.type = 'dockerfile'; + + switch (stack.source.type.toLowerCase()) { + case 'image': + source.content = 'FROM ' + stack.source.origin; + break; + case 'dockerfile': + source.content = stack.source.origin; + break; + default: + throw 'Not implemented'; + } + + return source; + } +} diff --git a/dashboard/src/app/workspaces/workspace-details/workspace-stacks/workspace-stacks.directive.ts b/dashboard/src/app/workspaces/workspace-details/workspace-stacks/workspace-stacks.directive.ts new file mode 100644 index 00000000000..373d8c86358 --- /dev/null +++ b/dashboard/src/app/workspaces/workspace-details/workspace-stacks/workspace-stacks.directive.ts @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2015-2016 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + */ +'use strict'; + +/** + * Defines a directive for displaying stacks tab. + * @author Oleksii Kurinnyi + */ +export class WorkspaceStacks { + restrict: string = 'E'; + templateUrl: string = 'app/workspaces/workspace-details/workspace-stacks/workspace-stacks.html'; + replace: boolean = false; + + controller: string = 'WorkspaceStacksController'; + controllerAs: string = 'workspaceStacksController'; + + bindToController: boolean = true; + + scope: { + [propName: string]: string + }; + + /** + * Default constructor that is using resource + * @ngInject for Dependency injection + */ + constructor() { + // scope values + this.scope = { + workspaceName: '=', + workspaceStackOnChange: '&' + }; + } + +} + diff --git a/dashboard/src/app/workspaces/workspace-details/workspace-stacks/workspace-stacks.html b/dashboard/src/app/workspaces/workspace-details/workspace-stacks/workspace-stacks.html new file mode 100644 index 00000000000..b4b66292255 --- /dev/null +++ b/dashboard/src/app/workspaces/workspace-details/workspace-stacks/workspace-stacks.html @@ -0,0 +1,6 @@ + diff --git a/dashboard/src/app/workspaces/workspaces-config.ts b/dashboard/src/app/workspaces/workspaces-config.ts index 57725f404b2..a15989122da 100644 --- a/dashboard/src/app/workspaces/workspaces-config.ts +++ b/dashboard/src/app/workspaces/workspaces-config.ts @@ -14,36 +14,37 @@ import {ListWorkspacesCtrl} from './list-workspaces/list-workspaces.controller'; import {CheWorkspaceItem} from './list-workspaces/workspace-item/workspace-item.directive'; import {CheWorkspaceStatus} from './list-workspaces/workspace-status-action/workspace-status.directive'; import {WorkspaceStatusController} from './list-workspaces/workspace-status-action/workspace-status.controller'; -import {CreateWorkspaceController} from './create-workspace/create-workspace.controller'; +import {WorkspaceDetailsController} from './workspace-details/workspace-details.controller'; +import {WorkspaceStacksController} from './workspace-details/workspace-stacks/workspace-stacks.controller'; +import {WorkspaceStacks} from './workspace-details/workspace-stacks/workspace-stacks.directive'; import {UsageChart} from './list-workspaces/workspace-item/usage-chart.directive'; import {WorkspaceItemCtrl} from './list-workspaces/workspace-item/workspace-item.controller'; import {WorkspaceEditModeOverlay} from './workspace-edit-mode/workspace-edit-mode-overlay.directive'; import {WorkspaceEditModeToolbarButton} from './workspace-edit-mode/workspace-edit-mode-toolbar-button.directive'; -import {WorkspaceDetailsController} from './workspace-details/workspace-details.controller'; import {WorkspaceDetailsProjectsCtrl} from './workspace-details/workspace-projects/workspace-details-projects.controller'; import {WorkspaceDetailsService} from './workspace-details/workspace-details.service'; import {ExportWorkspaceController} from './workspace-details/export-workspace/export-workspace.controller'; import {ExportWorkspace} from './workspace-details/export-workspace/export-workspace.directive'; import {ExportWorkspaceDialogController} from './workspace-details/export-workspace/dialog/export-workspace-dialog.controller'; import {WorkspaceDetailsProjects} from './workspace-details/workspace-projects/workspace-details-projects.directive'; -import {ReadyToGoStacksController} from './create-workspace/select-stack/ready-to-go-stacks/ready-to-go-stacks.controller'; -import {ReadyToGoStacks} from './create-workspace/select-stack/ready-to-go-stacks/ready-to-go-stacks.directive'; -import {WorkspaceRecipeController} from './create-workspace/select-stack/recipe/workspace-recipe.controller'; -import {WorkspaceRecipe} from './create-workspace/select-stack/recipe/workspace-recipe.directive'; -import {CheStackLibrarySelecter} from './create-workspace/select-stack/stack-library/stack-library-selecter/che-stack-library-selecter.directive'; -import {CreateProjectStackLibraryController} from './create-workspace/select-stack/stack-library/create-project-stack-library.controller'; -import {CreateProjectStackLibrary} from './create-workspace/select-stack/stack-library/create-project-stack-library.directive'; -import {WorkspaceSelectStackController} from './create-workspace/select-stack/workspace-select-stack.controller'; -import {WorkspaceSelectStack} from './create-workspace/select-stack/workspace-select-stack.directive'; +import {ReadyToGoStacksController} from './workspace-details/select-stack/ready-to-go-stacks/ready-to-go-stacks.controller'; +import {ReadyToGoStacks} from './workspace-details/select-stack/ready-to-go-stacks/ready-to-go-stacks.directive'; +import {WorkspaceRecipeController} from './workspace-details/select-stack/recipe/workspace-recipe.controller'; +import {WorkspaceRecipe} from './workspace-details/select-stack/recipe/workspace-recipe.directive'; +import {CheStackLibrarySelecter} from './workspace-details/select-stack/stack-library/stack-library-selecter/che-stack-library-selecter.directive'; +import {CreateProjectStackLibraryController} from './workspace-details/select-stack/stack-library/create-project-stack-library.controller'; +import {CreateProjectStackLibrary} from './workspace-details/select-stack/stack-library/create-project-stack-library.directive'; +import {WorkspaceSelectStackController} from './workspace-details/select-stack/workspace-select-stack.controller'; +import {WorkspaceSelectStack} from './workspace-details/select-stack/workspace-select-stack.directive'; import {CheWorkspaceRamAllocationSliderController} from './workspace-ram-slider/che-workspace-ram-allocation-slider.controller'; import {CheWorkspaceRamAllocationSlider} from './workspace-ram-slider/che-workspace-ram-allocation-slider.directive'; import {WorkspaceStatus} from './workspace-status/workspace-status.directive'; import {WorkspaceStatusIndicator} from './workspace-status/workspace-status-indicator.directive'; -import {CheStackLibraryFilterController} from './create-workspace/select-stack/stack-library/stack-library-filter/che-stack-library-filter.controller'; -import {CheStackLibraryFilter} from './create-workspace/select-stack/stack-library/stack-library-filter/che-stack-library-filter.directive'; -import {CreateProjectStackLibrarySelectedStackFilter} from './create-workspace/select-stack/stack-library/create-project-stack-library-selected-stack.filter'; +import {CheStackLibraryFilterController} from './workspace-details/select-stack/stack-library/stack-library-filter/che-stack-library-filter.controller'; +import {CheStackLibraryFilter} from './workspace-details/select-stack/stack-library/stack-library-filter/che-stack-library-filter.directive'; +import {CreateProjectStackLibrarySelectedStackFilter} from './workspace-details/select-stack/stack-library/create-project-stack-library-selected-stack.filter'; import {WorkspaceEnvironmentsController} from './workspace-details/environments/environments.controller'; import {WorkspaceEnvironments} from './workspace-details/environments/environments.directive'; @@ -78,7 +79,9 @@ export class WorkspacesConfig { new CreateProjectStackLibrarySelectedStackFilter(register); register.controller('ListWorkspacesCtrl', ListWorkspacesCtrl); - register.controller('CreateWorkspaceController', CreateWorkspaceController); + register.controller('WorkspaceDetailsController', WorkspaceDetailsController); + register.controller('WorkspaceStacksController', WorkspaceStacksController); + register.directive('workspaceStacks', WorkspaceStacks); register.directive('cheWorkspaceItem', CheWorkspaceItem); register.controller('WorkspaceItemCtrl', WorkspaceItemCtrl); @@ -90,8 +93,6 @@ export class WorkspacesConfig { register.directive('workspaceEditModeOverlay', WorkspaceEditModeOverlay); register.directive('workspaceEditModeToolbarButton', WorkspaceEditModeToolbarButton); - register.controller('WorkspaceDetailsController', WorkspaceDetailsController); - register.controller('WorkspaceDetailsProjectsCtrl', WorkspaceDetailsProjectsCtrl); register.directive('workspaceDetailsProjects', WorkspaceDetailsProjects); register.service('workspaceDetailsService', WorkspaceDetailsService); @@ -146,7 +147,7 @@ export class WorkspacesConfig { title: (params) => { return params.workspaceName;}, templateUrl: 'app/workspaces/workspace-details/workspace-details.html', controller: 'WorkspaceDetailsController', - controllerAs: 'workspaceDetailsCtrl' + controllerAs: 'workspaceDetailsController' }; // config routes @@ -160,11 +161,11 @@ export class WorkspacesConfig { .accessWhen('/workspace/:namespace/:workspaceName', locationProvider) .accessWhen('/workspace/:namespace/:workspaceName/:page', locationProvider) .accessWhen('/create-workspace', { - title: 'New Workspace', - templateUrl: 'app/workspaces/create-workspace/create-workspace.html', - controller: 'CreateWorkspaceController', - controllerAs: 'createWorkspaceCtrl' - }); + title: 'New Workspace', + templateUrl: 'app/workspaces/workspace-details/workspace-details.html', + controller: 'WorkspaceDetailsController', + controllerAs: 'workspaceDetailsController' + }); }); } } diff --git a/dashboard/src/components/api/environment/compose-environment-manager.ts b/dashboard/src/components/api/environment/compose-environment-manager.ts index 76f3fa808f4..008228d9618 100644 --- a/dashboard/src/components/api/environment/compose-environment-manager.ts +++ b/dashboard/src/components/api/environment/compose-environment-manager.ts @@ -17,28 +17,28 @@ import {EnvironmentManager} from './environment-manager'; * * Format sample and specific description: * - *services: - * devmachine: - * image: codenvy/ubuntu_jdk8 - * depends_on: - * - anotherMachine - * mem_limit: 2147483648 - * anotherMachine: - * image: codenvy/ubuntu_jdk8 - * depends_on: - * - thirdMachine - * mem_limit: 1073741824 - * thirdMachine: - * image: codenvy/ubuntu_jdk8 - * mem_limit: 512741824 - * labels: - * com.example.description: "Accounting webapp" - * com.example.department: "Finance" - * com.example.label-with-empty-value: "" - * environment: - * SOME_ENV: development - * SHOW: 'true' - * SESSION_SECRET: + * services: + * devmachine: + * image: codenvy/ubuntu_jdk8 + * depends_on: + * - anotherMachine + * mem_limit: 2147483648 + * anotherMachine: + * image: codenvy/ubuntu_jdk8 + * depends_on: + * - thirdMachine + * mem_limit: 1073741824 + * thirdMachine: + * image: codenvy/ubuntu_jdk8 + * mem_limit: 512741824 + * labels: + * com.example.description: "Accounting webapp" + * com.example.department: "Finance" + * com.example.label-with-empty-value: "" + * environment: + * SOME_ENV: development + * SHOW: 'true' + * SESSION_SECRET: * * * @@ -50,16 +50,22 @@ import {EnvironmentManager} from './environment-manager'; * @author Ann Shumilova */ +interface IComposeRecipe { + services: { + [machineName: string]: any + }; +} export class ComposeEnvironmentManager extends EnvironmentManager { + $log: ng.ILogService; - constructor($log) { + constructor($log: ng.ILogService) { super(); this.$log = $log; } - get editorMode() { + get editorMode(): string { return 'text/x-yaml'; } @@ -67,18 +73,41 @@ export class ComposeEnvironmentManager extends EnvironmentManager { * Parses recipe content * * @param content {string} recipe content - * @returns {object} recipe object + * @returns {IComposeRecipe} recipe object */ - _parseRecipe(content) { - let recipe = {}; + _parseRecipe(content: string): IComposeRecipe { + let recipe = null; try { - recipe = jsyaml.load(content); + recipe = this._validate(jsyaml.load(content)); } catch (e) { this.$log.error(e); } return recipe; } + /** + * Validate given recipe + * + * @param {IComposeRecipe} recipe + * @returns {IComposeRecipe | *} + * @private + */ + _validate(recipe: IComposeRecipe): IComposeRecipe | void { + if (!recipe.services) { + throw new Error('Recipe should contain services section.'); + } + + let services: any = Object.keys(recipe.services); + services.forEach((serviceName: string) => { + let serviceFields: any = Object.keys(recipe.services[serviceName] || {}); + if (!serviceFields || (serviceFields.includes('build') === false && serviceFields.includes('image') === false)) { + throw new Error('Service \'' + serviceName + '\' should contain \'build\' or \'image\' section.'); + } + }); + + return recipe; + } + /** * Dumps recipe object * @@ -86,7 +115,7 @@ export class ComposeEnvironmentManager extends EnvironmentManager { * @returns {string} recipe content */ - _stringifyRecipe(recipe) { + _stringifyRecipe(recipe: IComposeRecipe): string { let content = ''; try { content = jsyaml.dump(recipe); @@ -103,20 +132,27 @@ export class ComposeEnvironmentManager extends EnvironmentManager { * @param environment environment's configuration * @returns {Array} list of machines defined in environment */ - getMachines(environment) { - let recipe = null, - machines = [], - machineNames; + getMachines(environment: any): any[] { + let recipe: any = null, + machines: any[] = [], + machineNames: string[] = []; if (environment.recipe.content) { recipe = this._parseRecipe(environment.recipe.content); - machineNames = Object.keys(recipe.services); - } else { + if (recipe) { + machineNames = Object.keys(recipe.services); + } else if (environment.machines) { + machineNames = Object.keys(environment.machines); + } + } else if (environment.recipe.location) { machineNames = Object.keys(environment.machines); } - machineNames.forEach((machineName) => { - let machine = angular.copy(environment.machines[machineName]) || {}; + machineNames.forEach((machineName: string) => { + let machine: any = {}; + if (environment.machines && environment.machines[machineName]) { + machine = angular.copy(environment.machines[machineName]); + } machine.name = machineName; machine.recipe = recipe ? recipe.services[machineName] : recipe; @@ -138,24 +174,30 @@ export class ComposeEnvironmentManager extends EnvironmentManager { * @param machines the list of machines * @returns environment's configuration */ - getEnvironment(environment, machines) { + getEnvironment(environment: any, machines: any[]): any { let newEnvironment = super.getEnvironment(environment, machines); if (newEnvironment.recipe.content) { - let recipe = this._parseRecipe(newEnvironment.recipe.content); - - machines.forEach((machine) => { - let machineName = machine.name; - if (machine.recipe.environment && Object.keys(machine.recipe.environment).length) { - recipe.services[machineName].environment = angular.copy(machine.recipe.environment); - } else { - delete recipe.services[machineName].environment; + let recipe: IComposeRecipe = this._parseRecipe(newEnvironment.recipe.content); + + if (recipe) { + machines.forEach((machine: any) => { + let machineName = machine.name; + if (!recipe.services[machineName]) { + return; + } + if (machine.recipe.environment && Object.keys(machine.recipe.environment).length) { + recipe.services[machineName].environment = angular.copy(machine.recipe.environment); + } else { + delete recipe.services[machineName].environment; + } + }); + + try { + newEnvironment.recipe.content = this._stringifyRecipe(recipe); + } catch (e) { + this.$log.error('Cannot retrieve environment\'s recipe, error: ', e); } - }); - try { - newEnvironment.recipe.content = this._stringifyRecipe(recipe); - } catch (e) { - this.$log.error('Cannot retrieve environment\'s recipe, error: ', e); } } @@ -168,7 +210,7 @@ export class ComposeEnvironmentManager extends EnvironmentManager { * @param machine {object} * @returns {*} */ - getSource(machine) { + getSource(machine: any): any { if (!machine.recipe) { return null; } @@ -186,7 +228,7 @@ export class ComposeEnvironmentManager extends EnvironmentManager { * @param machine {object} * @returns {boolean} */ - canEditEnvVariables(machine) { + canEditEnvVariables(machine: any): boolean { return !!machine.recipe; } @@ -196,7 +238,7 @@ export class ComposeEnvironmentManager extends EnvironmentManager { * @param machine {object} * @returns {*} */ - getEnvVariables(machine) { + getEnvVariables(machine: any): any { if (!machine.recipe) { return null; } @@ -210,7 +252,7 @@ export class ComposeEnvironmentManager extends EnvironmentManager { * @param machine {object} * @param envVariables {object} */ - setEnvVariables(machine, envVariables) { + setEnvVariables(machine: any, envVariables: any): void { if (!machine.recipe) { return; } @@ -228,7 +270,7 @@ export class ComposeEnvironmentManager extends EnvironmentManager { * @param machine {object} * @returns {boolean} */ - canRenameMachine(machine) { + canRenameMachine(machine: any): boolean { return !!machine.recipe; } @@ -240,12 +282,12 @@ export class ComposeEnvironmentManager extends EnvironmentManager { * @param newName {string} * @returns {*} new environment */ - renameMachine(environment, oldName, newName) { + renameMachine(environment: any, oldName: string, newName: string): any { try { - let recipe = this._parseRecipe(environment.recipe.content); + let recipe: IComposeRecipe = this._parseRecipe(environment.recipe.content); // fix relations to other machines in recipe - Object.keys(recipe.services).forEach((serviceName) => { + Object.keys(recipe.services).forEach((serviceName: any) => { if (serviceName === oldName) { return; } @@ -297,7 +339,7 @@ export class ComposeEnvironmentManager extends EnvironmentManager { * @param machine {object} * @returns {boolean} */ - canDeleteMachine(machine) { + canDeleteMachine(machine: any): boolean { return !!machine.recipe; } @@ -308,12 +350,12 @@ export class ComposeEnvironmentManager extends EnvironmentManager { * @param name {string} name of machine * @returns {*} new environment */ - deleteMachine(environment, name) { + deleteMachine(environment: any, name: string): any { try { - let recipe = this._parseRecipe(environment.recipe.content); + let recipe: IComposeRecipe = this._parseRecipe(environment.recipe.content); // fix relations to other machines in recipe - Object.keys(recipe.services).forEach((serviceName) => { + Object.keys(recipe.services).forEach((serviceName: any) => { if (serviceName === name) { return; } diff --git a/dashboard/src/components/api/environment/docker-file-environment-manager.ts b/dashboard/src/components/api/environment/docker-file-environment-manager.ts index 8b1ee7ed704..6720529cd45 100644 --- a/dashboard/src/components/api/environment/docker-file-environment-manager.ts +++ b/dashboard/src/components/api/environment/docker-file-environment-manager.ts @@ -31,19 +31,22 @@ import {DockerfileParser} from './docker-file-parser'; * * @author Ann Shumilova */ + +const ENV_INSTRUCTION: string = 'ENV'; + export class DockerFileEnvironmentManager extends EnvironmentManager { + $log: ng.ILogService; + parser: DockerfileParser; - constructor($log) { + constructor($log: ng.ILogService) { super(); this.$log = $log; - this.ENV_INSTRUCTION = 'ENV'; - this.parser = new DockerfileParser(); } - get editorMode() { + get editorMode(): string { return 'text/x-dockerfile'; } @@ -54,8 +57,8 @@ export class DockerFileEnvironmentManager extends EnvironmentManager { * @returns {Array} a list of instructions and arguments * @private */ - _parseRecipe(content) { - let recipe = []; + _parseRecipe(content: string): any[] { + let recipe: any[] = null; try { recipe = this.parser.parse(content); } catch (e) { @@ -67,11 +70,11 @@ export class DockerFileEnvironmentManager extends EnvironmentManager { /** * Dumps a list of instructions and arguments into dockerfile * - * @param instructions {array} array of objects + * @param instructions {Array} array of objects * @returns {string} dockerfile * @private */ - _stringifyRecipe(instructions) { + _stringifyRecipe(instructions: any[]): string { let content = ''; try { @@ -90,7 +93,7 @@ export class DockerFileEnvironmentManager extends EnvironmentManager { * @param machines the list of machines * @returns environment's configuration */ - getEnvironment(environment, machines) { + getEnvironment(environment: any, machines: any): any { let newEnvironment = super.getEnvironment(environment, machines); // machines should contain one machine only @@ -111,7 +114,7 @@ export class DockerFileEnvironmentManager extends EnvironmentManager { * @param environment environment's configuration * @returns {Array} list of machines defined in environment */ - getMachines(environment) { + getMachines(environment: any): any[] { let recipe = null, machines = []; @@ -119,7 +122,7 @@ export class DockerFileEnvironmentManager extends EnvironmentManager { recipe = this._parseRecipe(environment.recipe.content); } - Object.keys(environment.machines).forEach((machineName) => { + Object.keys(environment.machines).forEach((machineName: string) => { let machine = angular.copy(environment.machines[machineName]); machine.name = machineName; machine.recipe = recipe; @@ -136,12 +139,12 @@ export class DockerFileEnvironmentManager extends EnvironmentManager { * @param machine * @returns {*} */ - getSource(machine) { + getSource(machine: any): any { if (!machine.recipe) { return null; } - let from = machine.recipe.find((line) => { + let from = machine.recipe.find((line: any) => { return line.instruction === 'FROM'; }); @@ -154,7 +157,7 @@ export class DockerFileEnvironmentManager extends EnvironmentManager { * @param machine {object} * @returns {boolean} */ - canEditEnvVariables(machine) { + canEditEnvVariables(machine: any): boolean { return !!machine.recipe; } @@ -164,18 +167,18 @@ export class DockerFileEnvironmentManager extends EnvironmentManager { * @param machine {object} * @returns {*} */ - getEnvVariables(machine) { + getEnvVariables(machine: any): any { if (!machine.recipe) { return null; } let envVariables = {}; - let envList = machine.recipe.filter((line) => { - return line.instruction === this.ENV_INSTRUCTION; + let envList = machine.recipe.filter((line: any) => { + return line.instruction === ENV_INSTRUCTION; }) || []; - envList.forEach((line) => { + envList.forEach((line: any) => { envVariables[line.argument[0]] = line.argument[1]; }); @@ -188,7 +191,7 @@ export class DockerFileEnvironmentManager extends EnvironmentManager { * @param machine {object} * @param envVariables {object} */ - setEnvVariables(machine, envVariables) { + setEnvVariables(machine: any, envVariables: any): void { if (!machine.recipe) { return; } @@ -196,19 +199,19 @@ export class DockerFileEnvironmentManager extends EnvironmentManager { let newRecipe = []; // new recipe without any 'ENV' instruction - newRecipe = machine.recipe.filter((line) => { - return line.instruction !== this.ENV_INSTRUCTION; + newRecipe = machine.recipe.filter((line: any) => { + return line.instruction !== ENV_INSTRUCTION; }); // add environments if any if (Object.keys(envVariables).length) { - Object.keys(envVariables).forEach((name) => { + Object.keys(envVariables).forEach((name: string) => { let line = { - instruction: this.ENV_INSTRUCTION, + instruction: ENV_INSTRUCTION, argument: [name, envVariables[name]] }; - newRecipe.splice(1,0,line); - }) + newRecipe.splice(1, 0, line); + }); } machine.recipe = newRecipe; diff --git a/dashboard/src/components/api/environment/docker-file-parser.spec.ts b/dashboard/src/components/api/environment/docker-file-parser.spec.ts index 164dd75b63c..68fb6b16359 100644 --- a/dashboard/src/components/api/environment/docker-file-parser.spec.ts +++ b/dashboard/src/components/api/environment/docker-file-parser.spec.ts @@ -32,7 +32,7 @@ describe('Simper dockerfile parser', () => { let result = parser._parseArgument(instruction, argument); let expectedResult = [{ - instruction:'ENV', + instruction: 'ENV', argument: ['name', 'environment variable value'] }]; expect(result).toEqual(expectedResult); @@ -45,13 +45,13 @@ describe('Simper dockerfile parser', () => { let result = parser._parseArgument(instruction, argument); let expectedResult = [{ - instruction:'ENV', + instruction: 'ENV', argument: ['myName', 'John Doe'] - },{ - instruction:'ENV', + }, { + instruction: 'ENV', argument: ['myDog', 'Rex The Dog'] }, { - instruction:'ENV', + instruction: 'ENV', argument: ['myCat', 'fluffy'] }]; expect(result).toEqual(expectedResult); @@ -60,23 +60,22 @@ describe('Simper dockerfile parser', () => { it('should parse a dockerfile', () => { let dockerfile = 'FROM codenvy/ubuntu_jdk8ENV' - +'\nENV myCat fluffy' - +'\nENV myDog Rex The Dog' - +'\nENV myName John Doe'; + + '\n#ENV myCat fluffy' + + '\nENV myDog Rex The Dog' + + '\nENV myName John Doe'; let result = parser.parse(dockerfile); let expectedResult = [{ - instruction:'FROM', + instruction: 'FROM', argument: 'codenvy/ubuntu_jdk8ENV' - },{ - instruction:'ENV', - argument: ['myCat', 'fluffy'] - },{ - instruction:'ENV', + }, { + comment: '#ENV myCat fluffy' + }, { + instruction: 'ENV', argument: ['myDog', 'Rex The Dog'] - },{ - instruction:'ENV', + }, { + instruction: 'ENV', argument: ['myName', 'John Doe'] }]; expect(result).toEqual(expectedResult); @@ -84,25 +83,24 @@ describe('Simper dockerfile parser', () => { it('should stringify an object into a dockerfile', () => { let instructions = [{ - instruction:'FROM', + instruction: 'FROM', argument: 'codenvy/ubuntu_jdk8ENV' - },{ - instruction:'ENV', - argument: ['myCat', 'fluffy'] - },{ - instruction:'ENV', + }, { + comment: '#ENV myCat fluffy' + }, { + instruction: 'ENV', argument: ['myDog', 'Rex The Dog'] - },{ - instruction:'ENV', + }, { + instruction: 'ENV', argument: ['myName', 'John Doe'] }]; let result = parser.dump(instructions); let expectedResult = 'FROM codenvy/ubuntu_jdk8ENV' - +'\nENV myCat fluffy' - +'\nENV myDog Rex The Dog' - +'\nENV myName John Doe'; + + '\n#ENV myCat fluffy' + + '\nENV myDog Rex The Dog' + + '\nENV myName John Doe'; expect(result.trim()).toEqual(expectedResult); }); diff --git a/dashboard/src/components/api/environment/docker-file-parser.ts b/dashboard/src/components/api/environment/docker-file-parser.ts index 73fb0db933e..7f237dc4ac8 100644 --- a/dashboard/src/components/api/environment/docker-file-parser.ts +++ b/dashboard/src/components/api/environment/docker-file-parser.ts @@ -14,11 +14,28 @@ * Simple parser and simple dumper of dockerfiles. * @author Oleksii Kurinnyi */ + +interface IRecipeLine { + instruction?: string; + argument?: string | string[]; + comment?: string; +} + export class DockerfileParser { + fromRE: RegExp; + backslashLineBreakRE: RegExp; + lineBreakRE: RegExp; + commentLineRE: RegExp; + instructionRE: RegExp; + envVariablesRE: RegExp; + quotesRE: RegExp; + backslashSpaceRE: RegExp; constructor() { + this.fromRE = /^FROM\s+\w+/m; this.backslashLineBreakRE = /\\\r?\n(\s+)?/; this.lineBreakRE = /\r?\n/; + this.commentLineRE = /^#/; this.instructionRE = /(\w+)\s+?(.+)/; this.envVariablesRE = /(?:^|(?:\s+))([^\s=]+?)=([^=]+?)(?:(?=\s+\w+=)|$)/g; // | | | | @@ -33,28 +50,40 @@ export class DockerfileParser { /** * Parses a dockerfile into array of pairs of instructions and arguments * - * @param content - * @returns {Array} + * @param {string} content recipe content + * @returns {IRecipeLine[]} */ - parse(content) { + parse(content: string): IRecipeLine[] { + if (!this.fromRE.test(content)) { + throw new TypeError('Dockerfile should start with \'FROM\' instruction. Cannot parse this recipe.'); + } + // join multiline instructions content = this._joinMultilineInstructions(content); // split dockerfile into separate instruction lines - let instructionLines = content.split(this.lineBreakRE); + let instructionLines: string[] = content.split(this.lineBreakRE); // split instruction line into instruction and argument - let instructions = []; - instructionLines.forEach((line) => { + let instructions: IRecipeLine[] = []; + instructionLines.forEach((line: string) => { + line = line.trim(); + + // check for comment line + if (this.commentLineRE.test(line)) { + instructions.push({comment: line}); + return; + } + let m = line.match(this.instructionRE); if (m) { let instruction = m[1], argument = m[2]; // parse argument - let results = this._parseArgument(instruction, argument); + let results: IRecipeLine[] = this._parseArgument(instruction, argument); - results.forEach((result) => { + results.forEach((result: IRecipeLine) => { instructions.push(result); }); } @@ -67,10 +96,10 @@ export class DockerfileParser { * Remove line breaks from lines which end with backslash * * @param content {string} - * @returns {*} + * @returns {string} * @private */ - _joinMultilineInstructions(content) { + _joinMultilineInstructions(content: string): string { return content.replace(this.backslashLineBreakRE, ''); } @@ -79,20 +108,20 @@ export class DockerfileParser { * * @param instruction {string} * @param argumentStr {string} - * @returns {Array} + * @returns {IRecipeLine[]} * @private */ - _parseArgument(instruction, argumentStr) { - let results = []; + _parseArgument(instruction: string, argumentStr: string): IRecipeLine[] { + let results: IRecipeLine[] = []; switch (instruction) { case 'ENV': if (argumentStr.includes('=')) { // this argument string contains one or more environment variables let match; - while(match = this.envVariablesRE.exec(argumentStr)) { - let name = match[1], - value = match[2]; + while (match = this.envVariablesRE.exec(argumentStr)) { + let name: string = match[1], + value: string = match[2]; if (this.quotesRE.test(value)) { value = value.replace(this.quotesRE, ''); } @@ -110,7 +139,7 @@ export class DockerfileParser { let firstSpaceIndex = argumentStr.indexOf(' '); results.push({ instruction: instruction, - argument: [argumentStr.slice(0, firstSpaceIndex), argumentStr.slice(firstSpaceIndex+1)] + argument: [argumentStr.slice(0, firstSpaceIndex), argumentStr.slice(firstSpaceIndex + 1)] }); } break; @@ -125,16 +154,20 @@ export class DockerfileParser { } /** - * Dumps an array into a dockerfile + * Dumps an array of instructions into a dockerfile * - * @param instructions {array} + * @param instructions {IRecipeLine[]} * @returns {string} */ - dump(instructions) { + dump(instructions: IRecipeLine[]): string { let content = ''; - instructions.forEach((line) => { - content += line.instruction + ' ' + this._stringifyArgument(line.instruction, line.argument) + '\n'; + instructions.forEach((line: IRecipeLine) => { + if (line.comment) { + content += line.comment + '\n'; + } else { + content += line.instruction + ' ' + this._stringifyArgument(line) + '\n'; + } }); return content; @@ -143,17 +176,16 @@ export class DockerfileParser { /** * Dumps argument object depending on instruction. * - * @param instruction {string} - * @param argument {*|string} + * @param line {IRecipeLine} * @returns {string} * @private */ - _stringifyArgument(instruction, argument) { - switch (instruction) { + _stringifyArgument(line: IRecipeLine): string { + switch (line.instruction) { case 'ENV': - return argument.join(' '); + return (line.argument as string[]).join(' '); default: - return argument; + return line.argument as string; } } } diff --git a/dashboard/src/components/api/environment/docker-image-environment-manager.ts b/dashboard/src/components/api/environment/docker-image-environment-manager.ts index fbfab0fdee2..0241bae4230 100644 --- a/dashboard/src/components/api/environment/docker-image-environment-manager.ts +++ b/dashboard/src/components/api/environment/docker-image-environment-manager.ts @@ -39,10 +39,10 @@ export class DockerImageEnvironmentManager extends EnvironmentManager { * @param environment environment's configuration * @returns {Array} list of machines defined in environment */ - getMachines(environment) { + getMachines(environment: any): any { let machines = []; - Object.keys(environment.machines).forEach((machineName) => { + Object.keys(environment.machines).forEach((machineName: string) => { let machine = angular.copy(environment.machines[machineName]); machine.name = machineName; machine.recipe = environment.recipe; @@ -60,7 +60,7 @@ export class DockerImageEnvironmentManager extends EnvironmentManager { * @param machines the list of machines * @returns environment's configuration */ - getEnvironment(environment, machines) { + getEnvironment(environment: any, machines: any): any { return super.getEnvironment(environment, machines); } @@ -70,6 +70,6 @@ export class DockerImageEnvironmentManager extends EnvironmentManager { * @param machine {object} * @returns {{image: string}} */ - getSource(machine) { + getSource(machine: any): any { return {image: machine.recipe.location}; }} diff --git a/dashboard/src/components/api/environment/environment-manager.ts b/dashboard/src/components/api/environment/environment-manager.ts index 464e07ab0ee..3a9930e84ea 100644 --- a/dashboard/src/components/api/environment/environment-manager.ts +++ b/dashboard/src/components/api/environment/environment-manager.ts @@ -14,33 +14,62 @@ * This is base class, which describes the environment manager. * It's aim is to handle machines retrieval and editing, based on the type of environment. */ + +const WS_AGENT_NAME: string = 'org.eclipse.che.ws-agent'; +const TERMINAL_AGENT_NAME: string = 'org.eclipse.che.terminal'; +const SSH_AGENT_NAME: string = 'org.eclipse.che.ssh'; + export class EnvironmentManager { - constructor() { - this.WS_AGENT_NAME = 'org.eclipse.che.ws-agent'; + constructor() { } + + get editorMode(): string { + return ''; } - canRenameMachine() { + canRenameMachine(machine: any): boolean { return false; } - canDeleteMachine() { + canDeleteMachine(machine: any): boolean { return false; } - canEditEnvVariables() { + canEditEnvVariables(machine: any): boolean { return false; } /** * Retrieves the list of machines. * + * @param {*} environment * @returns {Array} list of machines defined in environment */ - getMachines() { + getMachines(environment: any): any[] { return []; } + /** + * Renames machine. + * + * @param environment {object} + * @param oldName {string} + * @param newName {string} + */ + renameMachine(environment: any, oldName: string, newName: string): void { + throw new TypeError('EnvironmentManager: cannot rename machine.'); + } + + /** + * Removes machine. + * + * @param environment {object} + * @param name {string} name of machine + */ + deleteMachine(environment: any, name: string): void { + throw new TypeError('EnvironmentManager: cannot delete machine.'); + } + /** * Provides the environment configuration based on machines format. * @@ -48,12 +77,15 @@ export class EnvironmentManager { * @param machines the list of machines * @returns environment's configuration */ - getEnvironment(environment, machines) { + getEnvironment(environment: any, machines: any): any { let newEnvironment = angular.copy(environment); machines.forEach((machine) => { let machineName = machine.name; + if (angular.isUndefined(newEnvironment.machines)) { + newEnvironment.machines = {}; + } if (angular.isUndefined(newEnvironment.machines[machineName])) { newEnvironment.machines[machineName] = {'attributes': {}}; } @@ -71,8 +103,8 @@ export class EnvironmentManager { * @param machine * @returns {boolean} */ - isDev(machine) { - return machine.agents && machine.agents.includes(this.WS_AGENT_NAME); + isDev(machine: any): boolean { + return machine.agents && machine.agents.includes(WS_AGENT_NAME); } /** @@ -81,32 +113,40 @@ export class EnvironmentManager { * @param machine machine to edit * @param isDev defined whether machine is developer or not */ - setDev(machine, isDev) { + setDev(machine: any, isDev: boolean): void { let hasWsAgent = this.isDev(machine); - if (isDev && !hasWsAgent) { + if (isDev) { machine.agents = machine.agents ? machine.agents : []; - machine.agents.push(this.WS_AGENT_NAME); + if (!hasWsAgent) { + machine.agents.push(WS_AGENT_NAME); + } + if (!machine.agents.includes(SSH_AGENT_NAME)) { + machine.agents.push(SSH_AGENT_NAME); + } + if (!machine.agents.includes(TERMINAL_AGENT_NAME)) { + machine.agents.push(TERMINAL_AGENT_NAME); + } return; } if (!isDev && hasWsAgent) { - machine.agents.splice(machine.agents.indexOf(this.WS_AGENT_NAME), 1); + machine.agents.splice(machine.agents.indexOf(WS_AGENT_NAME), 1); } } - getServers(machine) { + getServers(machine: any): any { return machine.servers || {}; } - setServers(machine, servers) { + setServers(machine: any, servers: any): void { machine.servers = angular.copy(servers); } - getAgents(machine) { - return machine.agents || {}; + getAgents(machine: any): any[] { + return machine.agents || []; } - setAgents(machine, agents) { + setAgents(machine: any, agents: any[]): void { machine.agents = angular.copy(agents); } @@ -114,9 +154,9 @@ export class EnvironmentManager { * Returns memory limit from machine's attributes * * @param machine - * @returns {*} memory limit in bytes + * @returns {number|string} memory limit in bytes */ - getMemoryLimit(machine) { + getMemoryLimit(machine: any): number|string { if (machine && machine.attributes && machine.attributes.memoryLimitBytes) { return machine.attributes.memoryLimitBytes; } @@ -131,12 +171,12 @@ export class EnvironmentManager { * @param machine machine to change memory limit * @param limit memory limit */ - setMemoryLimit(machine, limit) { + setMemoryLimit(machine: any, limit: number): void { machine.attributes = machine.attributes ? machine.attributes : {}; machine.attributes.memoryLimitBytes = limit; } - getEnvVariables() { + getEnvVariables(machine: any): any { return null; } } diff --git a/dashboard/src/components/widget/button/che-button.directive.ts b/dashboard/src/components/widget/button/che-button.directive.ts index c3c011d0b76..5116275628a 100644 --- a/dashboard/src/components/widget/button/che-button.directive.ts +++ b/dashboard/src/components/widget/button/che-button.directive.ts @@ -51,6 +51,10 @@ export class CheButton { template = template + ` ng-href="${attrs.ngHref}"`; } + if (attrs.ngDisabled) { + template = template + ` disabled="${attrs.ngHref}"`; + } + template = template + '>'; if (attrs.cheButtonIcon) { diff --git a/dashboard/src/components/widget/input/che-input.styl b/dashboard/src/components/widget/input/che-input.styl index 58a044989dd..d5c1f09ddc4 100644 --- a/dashboard/src/components/widget/input/che-input.styl +++ b/dashboard/src/components/widget/input/che-input.styl @@ -78,5 +78,5 @@ label.che-input-desktop-label box-shadow none background-color inherit - input[readonly] + .che-input-icon + input[readonly] + .che-input-icon, input[type="hidden"] + .che-input-icon display none diff --git a/dashboard/src/components/widget/toolbar/che-toolbar.styl b/dashboard/src/components/widget/toolbar/che-toolbar.styl index 65be03f214c..b2469e74a75 100644 --- a/dashboard/src/components/widget/toolbar/che-toolbar.styl +++ b/dashboard/src/components/widget/toolbar/che-toolbar.styl @@ -71,7 +71,7 @@ $toolbar-height = 60px .che-toolbar-control-button md-icon vertical-align inherit -.che-toolbar-open-button .che-button.md-button +.che-toolbar .che-button.md-button color $white-color !important font-size 13px !important font-weight bold !important