summaryrefslogtreecommitdiffstats
path: root/catalog-ui/src/app/view-models/forms
diff options
context:
space:
mode:
authorMichael Lando <ml636r@att.com>2017-06-09 03:19:04 +0300
committerMichael Lando <ml636r@att.com>2017-06-09 03:19:04 +0300
commited64b5edff15e702493df21aa3230b81593e6133 (patch)
treea4cb01fdaccc34930a8db403a3097c0d1e40914b /catalog-ui/src/app/view-models/forms
parent280f8015d06af1f41a3ef12e8300801c7a5e0d54 (diff)
[SDC-29] catalog 1707 rebase commit.
Change-Id: I43c3dc5cf44abf5da817649bc738938a3e8388c1 Signed-off-by: Michael Lando <ml636r@att.com>
Diffstat (limited to 'catalog-ui/src/app/view-models/forms')
-rw-r--r--catalog-ui/src/app/view-models/forms/artifact-form/artifact-form-view-model.ts358
-rw-r--r--catalog-ui/src/app/view-models/forms/artifact-form/artifact-form-view.html169
-rw-r--r--catalog-ui/src/app/view-models/forms/artifact-form/artifact-form.less44
-rw-r--r--catalog-ui/src/app/view-models/forms/attribute-form/attribute-form-view.html152
-rw-r--r--catalog-ui/src/app/view-models/forms/attribute-form/attribute-from-view-model.ts241
-rw-r--r--catalog-ui/src/app/view-models/forms/env-parameters-form/env-parameters-form.html92
-rw-r--r--catalog-ui/src/app/view-models/forms/env-parameters-form/env-parameters-form.less178
-rw-r--r--catalog-ui/src/app/view-models/forms/env-parameters-form/env-parameters-form.ts153
-rw-r--r--catalog-ui/src/app/view-models/forms/env-parameters-form/env-parametr-description-popover.html4
-rw-r--r--catalog-ui/src/app/view-models/forms/input-form/input-form-view-modal.ts126
-rw-r--r--catalog-ui/src/app/view-models/forms/input-form/input-form-view.html125
-rw-r--r--catalog-ui/src/app/view-models/forms/property-forms/base-property-form/property-form-base-model.ts204
-rw-r--r--catalog-ui/src/app/view-models/forms/property-forms/base-property-form/property-form-base-view.html132
-rw-r--r--catalog-ui/src/app/view-models/forms/property-forms/base-property-form/property-form-base.less63
-rw-r--r--catalog-ui/src/app/view-models/forms/property-forms/component-property-form/property-form-view-model.ts322
-rw-r--r--catalog-ui/src/app/view-models/forms/property-forms/component-property-form/property-form-view.html201
-rw-r--r--catalog-ui/src/app/view-models/forms/property-forms/module-property-modal/module-property-model.ts206
-rw-r--r--catalog-ui/src/app/view-models/forms/property-forms/module-property-modal/module-property-view.html41
-rw-r--r--catalog-ui/src/app/view-models/forms/property-forms/select-datatype-modal/select-datatype-modal-view-model.ts97
-rw-r--r--catalog-ui/src/app/view-models/forms/property-forms/select-datatype-modal/select-datatype-modal-view.html74
-rw-r--r--catalog-ui/src/app/view-models/forms/property-forms/select-datatype-modal/select-datatype-modal.less63
-rw-r--r--catalog-ui/src/app/view-models/forms/resource-instance-name-form/resource-instance-name-model.ts95
-rw-r--r--catalog-ui/src/app/view-models/forms/resource-instance-name-form/resource-instance-name-view.html72
-rw-r--r--catalog-ui/src/app/view-models/forms/resource-instance-name-form/resource-instance-name.less29
24 files changed, 3241 insertions, 0 deletions
diff --git a/catalog-ui/src/app/view-models/forms/artifact-form/artifact-form-view-model.ts b/catalog-ui/src/app/view-models/forms/artifact-form/artifact-form-view-model.ts
new file mode 100644
index 0000000000..3e912706e0
--- /dev/null
+++ b/catalog-ui/src/app/view-models/forms/artifact-form/artifact-form-view-model.ts
@@ -0,0 +1,358 @@
+'use strict';
+import {ArtifactModel, Resource, Component} from "app/models";
+import {ArtifactsUtils, FormState, ValidationUtils, ArtifactType} from "app/utils";
+import {CacheService} from "app/services";
+
+export interface IEditArtifactModel {
+ artifactResource:ArtifactModel;
+ artifactTypes:Array<string>;
+ artifactFile:any;
+}
+
+export interface IArtifactResourceFormViewModelScope extends ng.IScope {
+ forms:any;
+ $$childTail:any;
+ isNew:boolean;
+ isLoading:boolean;
+ validationPattern:RegExp;
+ urlValidationPattern:RegExp;
+ labelValidationPattern:RegExp;
+ integerValidationPattern:RegExp;
+ commentValidationPattern:RegExp;
+ artifactType:string;
+ editArtifactResourceModel:IEditArtifactModel;
+ defaultHeatTimeout:number;
+ validExtensions:any;
+ originalArtifactName:string;
+ editForm:ng.IFormController;
+ footerButtons:Array<any>;
+ modalInstanceArtifact:ng.ui.bootstrap.IModalServiceInstance;
+
+ fileExtensions():string;
+ save(doNotCloseModal?:boolean):void;
+ saveAndAnother():void;
+ close():void;
+ getOptions():Array<string>;
+ isDeploymentHeat():boolean;
+ onFileChange():void;
+ setDefaultTimeout():void;
+ openEditEnvParametersModal(artifact:ArtifactModel):void;
+ getFormTitle():string;
+ fileUploadRequired():string;
+ isArtifactOwner():boolean;
+}
+
+export class ArtifactResourceFormViewModel {
+
+ static '$inject' = [
+ '$scope',
+ '$uibModalInstance',
+ 'artifact',
+ 'Sdc.Services.CacheService',
+ 'ValidationPattern',
+ 'UrlValidationPattern',
+ 'LabelValidationPattern',
+ 'IntegerValidationPattern',
+ 'CommentValidationPattern',
+ 'ValidationUtils',
+ '$base64',
+ '$state',
+ 'ArtifactsUtils',
+ '$uibModal',
+ 'component'
+ ];
+
+ private formState:FormState;
+ private entityId:string;
+
+ constructor(private $scope:IArtifactResourceFormViewModelScope,
+ private $uibModalInstance:ng.ui.bootstrap.IModalServiceInstance,
+ private artifact:ArtifactModel,
+ private cacheService:CacheService,
+ private ValidationPattern:RegExp,
+ private UrlValidationPattern:RegExp,
+ private LabelValidationPattern:RegExp,
+ private IntegerValidationPattern:RegExp,
+ private CommentValidationPattern:RegExp,
+ private ValidationUtils:ValidationUtils,
+ private $base64:any,
+ private $state:any,
+ private artifactsUtils:ArtifactsUtils,
+ private $uibModal:ng.ui.bootstrap.IModalService,
+ private component:Component) {
+
+
+ this.entityId = this.component.uniqueId;
+ this.formState = angular.isDefined(artifact.artifactLabel) ? FormState.UPDATE : FormState.CREATE;
+ this.initScope();
+ }
+
+ private initEntity = ():void => {
+ this.$scope.editArtifactResourceModel.artifactResource = this.artifact;
+ this.$scope.originalArtifactName = this.artifact.artifactName;
+ };
+
+
+ private initFooterButtons = ():void => {
+
+ this.$scope.footerButtons = [
+ {'name': 'Done', 'css': 'blue', 'callback': this.$scope.save}
+ ];
+ if (this.$scope.isNew) {
+ this.$scope.footerButtons.push({
+ 'name': 'Add Another',
+ 'css': 'grey',
+ 'disabled': !this.$scope.isNew && 'deployment' === this.$scope.artifactType,
+ 'callback': this.$scope.saveAndAnother
+ });
+ }
+ };
+
+ private filterDeploymentArtifactTypeByResourceType = (resourceType:string):any => {
+ let result = {};
+ _.each(this.$scope.validExtensions, function (typeSettings:any, typeName:string) {
+ if (!typeSettings.validForResourceTypes || typeSettings.validForResourceTypes.indexOf(resourceType) > -1) {
+ result[typeName] = typeSettings;
+ }
+ });
+
+ return result;
+ };
+
+ private initArtifactTypes = ():void => {
+
+ let artifactTypes:any = this.cacheService.get('UIConfiguration');
+
+ if ('deployment' === this.$scope.artifactType) {
+
+
+ if ('HEAT_ENV' == this.artifact.artifactType || this.component.selectedInstance) {
+ this.$scope.validExtensions = artifactTypes.artifacts.deployment.resourceInstanceDeploymentArtifacts;
+ } else if (this.component.isResource()) {
+ this.$scope.validExtensions = artifactTypes.artifacts.deployment.resourceDeploymentArtifacts;
+ this.$scope.validExtensions = this.filterDeploymentArtifactTypeByResourceType((<Resource>this.component).resourceType);
+ } else {
+ this.$scope.validExtensions = artifactTypes.artifacts.deployment.serviceDeploymentArtifacts;
+ }
+
+ if (this.$scope.validExtensions) {
+ this.$scope.editArtifactResourceModel.artifactTypes = Object.keys(this.$scope.validExtensions);
+ }
+ this.$scope.defaultHeatTimeout = artifactTypes.defaultHeatTimeout;
+ if (this.$scope.isNew) {
+ let isHeat = 'HEAT_ENV' == this.artifact.artifactType;
+ _.remove(this.$scope.editArtifactResourceModel.artifactTypes, (item:string)=> {
+ return 'HEAT' == item.substring(0, 4) || (!isHeat && item == "VF_MODULES_METADATA") ||
+ _.has(ArtifactType.THIRD_PARTY_RESERVED_TYPES, item);
+ });
+ }
+
+ }
+ if (this.$scope.artifactType === 'informational') {
+ this.$scope.editArtifactResourceModel.artifactTypes = artifactTypes.artifacts.other.map((element:any)=> {
+ return element.name;
+ });
+ _.remove(this.$scope.editArtifactResourceModel.artifactTypes, (item:string)=> {
+ return _.has(ArtifactType.THIRD_PARTY_RESERVED_TYPES, item) ||
+ _.has(ArtifactType.TOSCA, item);
+ })
+ }
+
+ if (this.component.isResource() && (<Resource>this.component).isCsarComponent()) {
+ _.remove(this.$scope.editArtifactResourceModel.artifactTypes, (item:string) => {
+ return this.artifactsUtils.isLicenseType(item);
+ })
+ }
+
+ };
+
+ private initEditArtifactResourceModel = ():void => {
+ this.$scope.editArtifactResourceModel = {
+ artifactResource: null,
+ artifactTypes: null,
+ artifactFile: {}
+ };
+
+ this.initEntity();
+ };
+
+ private initScope = ():void => {
+
+ this.$scope.validationPattern = this.ValidationPattern;
+ this.$scope.urlValidationPattern = this.UrlValidationPattern;
+ this.$scope.labelValidationPattern = this.LabelValidationPattern;
+ this.$scope.integerValidationPattern = this.IntegerValidationPattern;
+ this.$scope.commentValidationPattern = this.CommentValidationPattern;
+ this.$scope.isLoading = false;
+ this.$scope.isNew = (this.formState === FormState.CREATE);
+ this.$scope.artifactType = this.artifactsUtils.getArtifactTypeByState(this.$state.current.name);
+ this.$scope.modalInstanceArtifact = this.$uibModalInstance;
+
+ this.initEditArtifactResourceModel();
+ this.initArtifactTypes();
+
+ // In case of edit, show the file name in browse.
+ if (this.artifact.artifactName !== "" && 'HEAT_ENV' !== this.artifact.artifactType) {
+ this.$scope.editArtifactResourceModel.artifactFile = {};
+ this.$scope.editArtifactResourceModel.artifactFile.filename = this.artifact.artifactName;
+ }
+
+ //scope methods
+ this.$scope.isDeploymentHeat = ():boolean => {
+ return !this.$scope.isNew && this.$scope.artifactType === 'deployment'
+ && this.$scope.editArtifactResourceModel.artifactResource.isHEAT();
+
+ };
+ this.$scope.onFileChange = ():void => {
+ if (this.$scope.editArtifactResourceModel.artifactFile && this.$scope.editArtifactResourceModel.artifactFile.filename) {
+ this.$scope.editArtifactResourceModel.artifactResource.artifactName = this.$scope.editArtifactResourceModel.artifactFile.filename;
+ } else {
+ this.$scope.editArtifactResourceModel.artifactResource.artifactName = this.$scope.originalArtifactName;
+ }
+ };
+ this.$scope.setDefaultTimeout = ():void => {
+ if (this.$scope.isDeploymentHeat() && !this.$scope.editArtifactResourceModel.artifactResource.timeout) {
+ this.$scope.editArtifactResourceModel.artifactResource.timeout = this.$scope.defaultHeatTimeout;
+ }
+
+ if (this.$scope.editArtifactResourceModel.artifactFile.filename) {
+ this.$scope.editArtifactResourceModel.artifactFile = {};
+ this.$scope.forms.editForm.myArtifactFile.$setValidity('required', false);
+ }
+ };
+
+ this.$scope.fileExtensions = ():string => {
+ let type:string = this.$scope.editArtifactResourceModel.artifactResource.artifactType;
+ return type && this.$scope.validExtensions && this.$scope.validExtensions[type].acceptedTypes ?
+ this.$scope.validExtensions[type].acceptedTypes.join(',') : "";
+ };
+
+ this.$scope.save = (doNotCloseModal?:boolean):void => {
+ this.$scope.isLoading = true;
+ this.$scope.editArtifactResourceModel.artifactResource.description = this.ValidationUtils.stripAndSanitize(this.$scope.editArtifactResourceModel.artifactResource.description);
+
+ if (!this.$scope.isDeploymentHeat()) {
+ this.$scope.editArtifactResourceModel.artifactResource.timeout = null;
+ }
+
+ if (this.$scope.editArtifactResourceModel.artifactFile) {
+ this.$scope.editArtifactResourceModel.artifactResource.payloadData = this.$scope.editArtifactResourceModel.artifactFile.base64;
+ this.$scope.editArtifactResourceModel.artifactResource.artifactName = this.$scope.editArtifactResourceModel.artifactFile.filename;
+ }
+
+ let onFaild = (response):void => {
+ this.$scope.isLoading = false;
+ console.info('onFaild', response);
+ };
+
+ let onSuccess = (artifactResource:ArtifactModel):void => {
+ this.$scope.isLoading = false;
+ this.$scope.originalArtifactName = "";
+
+ if (this.$scope.isDeploymentHeat()) {
+ if (artifactResource.heatParameters) {
+ this.$scope.openEditEnvParametersModal(artifactResource);
+ }
+ }
+
+ if (!doNotCloseModal) {
+ this.$uibModalInstance.close();
+ } else {
+ this.$scope.editArtifactResourceModel.artifactFile = null;
+ angular.element("input[type='file']").val(null); // for support chrome when upload the same file
+ this.artifactsUtils.addAnotherAfterSave(this.$scope);
+ }
+
+ };
+
+ if ('HEAT_ENV' == this.artifact.artifactType) {
+ if (this.component.selectedInstance) {
+ this.component.uploadInstanceEnvFile(this.$scope.editArtifactResourceModel.artifactResource).then(onSuccess, onFaild);
+ } else {
+ this.component.addOrUpdateArtifact(this.$scope.editArtifactResourceModel.artifactResource).then(onSuccess, onFaild);
+
+ }
+ } else if (this.$scope.isArtifactOwner()) {
+ this.component.addOrUpdateInstanceArtifact(this.$scope.editArtifactResourceModel.artifactResource).then(onSuccess, onFaild);
+ } else {
+ this.component.addOrUpdateArtifact(this.$scope.editArtifactResourceModel.artifactResource).then(onSuccess, onFaild);
+ }
+ };
+
+ this.$scope.isArtifactOwner = ():boolean=> {
+ return this.component.isService() && !!this.component.selectedInstance;
+ };
+
+ this.$scope.saveAndAnother = ():void => {
+ this.$scope.save(true);
+ };
+
+ this.$scope.close = ():void => {
+ this.$uibModalInstance.close();
+ };
+
+ this.$scope.fileUploadRequired = ():string => {
+ if (this.$scope.editArtifactResourceModel.artifactFile.filename) {
+ // This is edit mode
+ return 'false';
+ } else {
+ return 'true';
+ }
+ };
+
+ this.$scope.getFormTitle = ():string => {
+ if ('HEAT_ENV' == this.artifact.artifactType) {
+ return 'Update HEAT ENV';
+ }
+ if (this.$scope.isDeploymentHeat()) {
+ if (!this.$scope.editArtifactResourceModel.artifactResource.artifactChecksum) {
+ return 'Add HEAT Template';
+ }
+ return 'Update HEAT Template';
+ }
+ if (this.$scope.isNew) {
+ return 'Add Artifact';
+ }
+ return 'Update Artifact';
+ };
+
+ this.$scope.openEditEnvParametersModal = (artifactResource:ArtifactModel):void => {
+
+ let modalOptions:ng.ui.bootstrap.IModalSettings = {
+ templateUrl: '../env-parameters-form/env-parameters-form.html',
+ controller: 'Sdc.ViewModels.EnvParametersFormViewModel',
+ size: 'sdc-md',
+ backdrop: 'static',
+ resolve: {
+ artifact: ():ArtifactModel => {
+ return artifactResource;
+ },
+ component: ():Component => {
+ return this.component;
+ }
+ }
+ };
+
+ let modalInstance:ng.ui.bootstrap.IModalServiceInstance = this.$uibModal.open(modalOptions);
+ modalInstance
+ .result
+ .then(():void => {
+ });
+ };
+
+ this.$scope.forms = {};
+
+ this.initFooterButtons();
+
+
+ this.$scope.$watch("forms.editForm.$invalid", (newVal, oldVal) => {
+ if(this.$scope.forms.editForm) {
+ this.$scope.footerButtons[0].disabled = this.$scope.forms.editForm.$invalid;
+ if (this.$scope.isNew) {
+ this.$scope.footerButtons[1].disabled = this.$scope.forms.editForm.$invalid;
+ }
+ }
+ });
+
+ }
+}
diff --git a/catalog-ui/src/app/view-models/forms/artifact-form/artifact-form-view.html b/catalog-ui/src/app/view-models/forms/artifact-form/artifact-form-view.html
new file mode 100644
index 0000000000..0984c6872d
--- /dev/null
+++ b/catalog-ui/src/app/view-models/forms/artifact-form/artifact-form-view.html
@@ -0,0 +1,169 @@
+<sdc-modal modal="modalInstanceArtifact" type="classic" class="sdc-add-artifact" buttons="footerButtons" header="{{getFormTitle()}}" show-close-button="true" get-close-modal-response="close" data-tests-id="sdc-add-artifact">
+
+ <loader data-display="isLoading"></loader>
+
+ <div class="sdc-edit-artifact-form-container"
+ data-ng-class="{'mandatory-artifact': (editArtifactResourceModel.artifactResource.mandatory && artifactType !=='deployment') || artifactType === 'api'}">
+ <form novalidate class="w-sdc-form" name="forms.editForm">
+
+ <!--------------------- ARTIFACT FILE START-------------------->
+ <div class="i-sdc-form-item">
+ <label class="required">Upload File</label>
+ <file-upload id="fileUploadElement"
+ form-element="forms.editForm"
+ element-required="{{::fileUploadRequired()}}"
+ element-name="myArtifactFile"
+ file-model="editArtifactResourceModel.artifactFile"
+ extensions="{{fileExtensions()}}"
+ element-disabled="{{!editArtifactResourceModel.artifactResource.artifactType}}"
+ data-ng-class="{'error': forms.editForm.myArtifactFile.$dirty && forms.editForm.myArtifactFile.$invalid}"></file-upload>
+
+ <div class="input-error-file-upload" data-ng-show="forms.editForm.myArtifactFile.$dirty && forms.editForm.myArtifactFile.$invalid">
+ <span ng-show="forms.editForm.myArtifactFile.$error.required || forms.editForm.myArtifactFile.$error.emptyFile" translate="ADD_ARTIFACT_ERROR_FILE_REQUIRED"></span>
+ <span ng-show="forms.editForm.myArtifactFile.$error.maxsize" translate="VALIDATION_ERROR_MAX_FILE_SIZE"></span>
+ <span ng-if="artifactType === 'deployment'" ng-show="forms.editForm.myArtifactFile.$error.filetype" translate="ADD_ARTIFACT_ERROR_VALID_EXTENSIONS" translate-values="{'extensions': '{{fileExtensions()}}' }"></span>
+ <span ng-show="forms.editForm.myArtifactFile.$error.emptyFile" translate="VALIDATION_ERROR_EMPTY_FILE"></span>
+ </div>
+ </div>
+ <!--------------------- ARTIFACT FILE END -------------------->
+
+ <div class="w-sdc-form-columns-wrapper">
+
+ <div class="w-sdc-form-column" data-ng-if="artifactType === 'deployment' || (!editArtifactResourceModel.artifactResource.mandatory && artifactType !== 'api')">
+
+ <div class="i-sdc-form-item"
+ data-ng-class="{error:(forms.editForm.artifactLabel.$dirty && forms.editForm.artifactLabel.$invalid)}"
+ data-ng-if="!isDeploymentHeat()">
+ <label class="i-sdc-form-label required">Artifact Label</label>
+ <input class="i-sdc-form-input"
+ data-ng-maxlength="25"
+ data-ng-model="editArtifactResourceModel.artifactResource.artifactLabel"
+ type="text"
+ name="artifactLabel"
+ data-required
+ data-ng-model-options="{ debounce: 200 }"
+ data-ng-pattern="labelValidationPattern"
+ maxlength="25"
+ data-ng-disabled="!isNew"
+ data-tests-id="artifactLabel"
+ autofocus/>
+
+ <div class="input-error" data-ng-show="forms.editForm.artifactLabel.$dirty && forms.editForm.artifactLabel.$invalid">
+ <span ng-show="forms.editForm.artifactLabel.$error.required" translate="ADD_ARTIFACT_ERROR_LABEL_REQUIRED"></span>
+ <span ng-show="forms.editForm.artifactLabel.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '25' }"></span>
+ <span ng-show="forms.editForm.artifactLabel.$error.pattern" translate="VALIDATION_ERROR_SPECIAL_CHARS_NOT_ALLOWED"></span>
+ </div>
+
+ </div>
+
+ <div class="i-sdc-form-item" data-ng-class="{error:(forms.editForm.type.$dirty && forms.editForm.type.$invalid)}">
+ <label class="i-sdc-form-label required">Type</label>
+ <select class="i-sdc-form-select"
+ data-required
+ name="type"
+ data-ng-disabled="!isNew"
+ data-ng-change="setDefaultTimeout()"
+ data-ng-model="editArtifactResourceModel.artifactResource.artifactType"
+ data-ng-options="type as type for type in editArtifactResourceModel.artifactTypes track by type | uppercase"
+ data-tests-id="artifacttype">
+ <option value="">Choose Type</option>
+ </select>
+
+ <div class="input-error" data-ng-show="forms.editForm.type.$dirty && forms.editForm.type.$invalid">
+ <span ng-show="forms.editForm.type.$error.required" translate="ADD_ARTIFACT_ERROR_TYPE_REQUIRED"></span>
+ </div>
+
+ </div>
+
+ <div class="i-sdc-form-item" data-ng-class="{error:(forms.editForm.timeout.$dirty && forms.editForm.timeout.$invalid)}" data-ng-if="isDeploymentHeat()">
+ <label class="i-sdc-form-label">Deployment Timeout (minutes)</label>
+ <input class="i-sdc-form-input"
+ data-ng-maxlength="25"
+ data-ng-model="editArtifactResourceModel.artifactResource.timeout"
+ type="number"
+ name="timeout"
+ min="1"
+ max="2147483647"
+ data-ng-disabled="'HEAT_ENV'==editArtifactResourceModel.artifactResource.artifactType"
+ data-ng-model-options="{ debounce: 200 }"
+ data-ng-pattern="integerValidationPattern"
+ data-ng-init="setDefaultTimeout()"
+ data-ng-change="setDefaultTimeout()"
+ maxlength="25"
+ data-tests-id="timeout" />
+
+ <div class="input-error" data-ng-show="forms.editForm.timeout.$dirty && forms.editForm.timeout.$invalid">
+ <span ng-show="forms.editForm.timeout.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '25' }"></span>
+ <span ng-show="forms.editForm.timeout.$error.pattern" translate="ADD_ARTIFACT_ERROR_TIMEOUT_PATTERN"></span>
+ <span ng-show="forms.editForm.timeout.$error.min" translate="ADD_ARTIFACT_ERROR_TIMEOUT_MIN"></span>
+ </div>
+
+ </div>
+
+ </div><!-- w-sdc-form-column -->
+
+ <div class="w-sdc-form-column i-sdc-form-url" data-ng-if="artifactType==='api'">
+
+ <div class="i-sdc-form-item"
+ data-ng-class="{error:(forms.editForm.apiUrl.$dirty && forms.editForm.apiUrl.$invalid)}">
+ <label class="i-sdc-form-label required">URL</label>
+ <input class="i-sdc-form-input"
+ data-ng-maxlength="100"
+ data-ng-model="editArtifactResourceModel.artifactResource.apiUrl"
+ data-ng-model-options="{ debounce: 200 }"
+ type="url"
+ name="apiUrl"
+ data-required
+ ng-pattern="urlValidationPattern"
+ maxlength="100"
+ autofocus
+ invalid-characters=',#?&@$<>~^`\[]{}|")(*!+=;%' />
+
+ <div class="input-error" data-ng-show="forms.editForm.apiUrl.$dirty && forms.editForm.apiUrl.$invalid">
+ <span ng-show="forms.editForm.apiUrl.$error.required" translate="ADD_ARTIFACT_ERROR_APIURL_REQUIRED"></span>
+ <span ng-show="forms.editForm.apiUrl.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '100' }"></span>
+ <span ng-show="forms.editForm.apiUrl.$error.url || forms.editForm.apiUrl.$error.pattern || forms.editForm.apiUrl.$error.invalidCharacters" translate="ADD_ARTIFACT_ERROR_APIURL_URL"></span>
+ </div>
+
+ </div>
+
+ </div><!-- w-sdc-form-column -->
+
+ <div class="w-sdc-form-column">
+
+ <div class="i-sdc-form-item"
+ data-ng-class="{error:(forms.editForm.description.$dirty && forms.editForm.description.$invalid)}">
+ <label class="i-sdc-form-label required">Description</label>
+ <textarea class="i-sdc-form-textarea"
+ data-ng-maxlength="256"
+ maxlength="256"
+ data-required
+ name="description"
+ data-ng-model="editArtifactResourceModel.artifactResource.description"
+ data-ng-model-options="{ debounce: 200 }"
+ data-ng-pattern="commentValidationPattern"
+ data-tests-id="description"></textarea>
+
+ <div class="input-error" data-ng-show="forms.editForm.description.$dirty && forms.editForm.description.$invalid">
+ <span ng-show="forms.editForm.description.$error.required" translate="ADD_ARTIFACT_ERROR_DESCRIPTION_REQUIRED"></span>
+ <span ng-show="forms.editForm.description.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '256' }"></span>
+ <span ng-show="forms.editForm.description.$error.pattern" translate="ADD_ARTIFACT_ERROR_DESCRIPTION_PATTERN"></span>
+ </div>
+
+ <div class="w-sdc-form-column artifact-info" data-ng-show="!isNew && editArtifactResourceModel.artifactResource.esId">
+ UUID <span data-ng-bind="editArtifactResourceModel.artifactResource.artifactUUID"></span>
+ <br />
+ Version <span data-ng-bind="editArtifactResourceModel.artifactResource.artifactVersion"></span>
+ </div>
+ </div>
+
+ </div><!-- w-sdc-form-column -->
+
+ </div><!-- w-sdc-form-columns-wrapper -->
+
+ <span class="w-sdc-form-note" data-ng-show="forms.editForm.$invalid && false" translate="LABEL_ALL_FIELDS_ARE_MANDATORY"></span>
+
+ </form>
+ </div>
+</sdc-modal>
+
diff --git a/catalog-ui/src/app/view-models/forms/artifact-form/artifact-form.less b/catalog-ui/src/app/view-models/forms/artifact-form/artifact-form.less
new file mode 100644
index 0000000000..1f77958c88
--- /dev/null
+++ b/catalog-ui/src/app/view-models/forms/artifact-form/artifact-form.less
@@ -0,0 +1,44 @@
+.sdc-edit-artifact-form-container {
+
+ .w-sdc-form-note {
+ .h_9;
+ display: block;
+ position: relative;
+ top: 13px;
+ }
+
+ .i-sdc-form-textarea{
+ min-height: 95px;
+ }
+
+ .i-sdc-form-url {
+ padding-bottom: 0px;
+ }
+
+ &.mandatory-artifact {
+ .w-sdc-form-column {
+ width: 100%;
+ padding: 0;
+ min-height: initial;
+ }
+ }
+ .w-sdc-form .i-sdc-form-item.upload input[type="file"] {
+ display: none
+ }
+
+ .artifact-info {
+ text-align: left;
+ color: rgb(140, 140, 140);
+ font-size: 13px;
+ margin-top: -10px;
+ margin-bottom: 5px;
+ width: 100%;
+ min-height: initial;
+
+ span {
+ color: #666666;
+ padding-left: 4px;
+ }
+ }
+
+}
diff --git a/catalog-ui/src/app/view-models/forms/attribute-form/attribute-form-view.html b/catalog-ui/src/app/view-models/forms/attribute-form/attribute-form-view.html
new file mode 100644
index 0000000000..90b8f67df4
--- /dev/null
+++ b/catalog-ui/src/app/view-models/forms/attribute-form/attribute-form-view.html
@@ -0,0 +1,152 @@
+<sdc-modal modal="modalInstanceAttribute" type="classic" class="sdc-edit-attribute-container" buttons="footerButtons" header="{{isNew ? 'Add' : 'Update' }} Attribute" show-close-button="true">
+
+ <div class="sdc-edit-attribute-form-container" >
+ <form novalidate class="w-sdc-form two-columns" name="forms.editForm" >
+
+ <div class="w-sdc-form-columns-wrapper">
+
+ <div class="w-sdc-form-column">
+
+ <!-- Name -->
+ <div class="i-sdc-form-item" data-ng-class="{error:(forms.editForm.attributeName.$dirty && forms.editForm.attributeName.$invalid)}">
+ <label class="i-sdc-form-label required">Name</label>
+ <input class="i-sdc-form-input"
+ data-tests-id="attributeName"
+ data-ng-maxlength="50"
+ data-ng-disabled="!isNew"
+ maxlength="50"
+ data-ng-model="editAttributeModel.attribute.name"
+ type="text"
+ name="attributeName"
+ data-ng-pattern="propertyNameValidationPattern"
+ data-required
+ data-ng-model-options="{ debounce: 200 }"
+ data-ng-change="validateName()"
+ autofocus />
+ <div class="input-error" data-ng-show="forms.editForm.attributeName.$dirty && forms.editForm.attributeName.$invalid">
+ <span ng-show="forms.editForm.attributeName.$error.required" translate="VALIDATION_ERROR_REQUIRED" translate-values="{'field': 'Attribute name' }"></span>
+ <span ng-show="forms.editForm.attributeName.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '128' }"></span>
+ <span ng-show="forms.editForm.attributeName.$error.pattern" translate="VALIDATION_ERROR_SPECIAL_CHARS_NOT_ALLOWED"></span>
+ <span ng-show="forms.editForm.attributeName.$error.nameExist" translate="NEW_ATTRIBUTE_ERROR_NAME_EXISTS"></span>
+ </div>
+ </div>
+
+ <!-- Description -->
+ <div class="i-sdc-form-item" data-ng-class="{error:(forms.editForm.description.$dirty && forms.editForm.description.$invalid)}">
+ <label class="i-sdc-form-label">Description</label>
+ <textarea class="i-sdc-form-textarea"
+ data-ng-maxlength="256"
+ data-ng-disabled="editAttributeModel.attribute.readonly"
+ maxlength="256"
+ data-ng-pattern="commentValidationPattern"
+ name="description"
+ data-ng-model="editAttributeModel.attribute.description"
+ data-ng-model-options="{ debounce: 200 }"
+ data-tests-id="description"></textarea>
+ <div class="input-error" data-ng-show="forms.editForm.description.$dirty && forms.editForm.description.$invalid">
+ <span ng-show="forms.editForm.description.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '256' }"></span>
+ <span ng-show="forms.editForm.description.$error.pattern" translate="VALIDATION_ERROR_SPECIAL_CHARS_NOT_ALLOWED"></span>
+ <span ng-show="forms.editForm.description.$error.required" translate="VALIDATION_ERROR_REQUIRED" translate-values="{'field': 'Description' }"></span>
+ </div>
+ </div>
+
+
+ </div>
+
+ <div class="w-sdc-form-column">
+ <!-- Type -->
+ <div class="i-sdc-form-item" data-ng-class="{error:(forms.editForm.type.$dirty && forms.editForm.type.$invalid)}">
+ <label class="i-sdc-form-label required">Type</label>
+ <select class="i-sdc-form-select"
+ data-tests-id="type-field"
+ data-required
+ data-ng-disabled="editAttributeModel.attribute.readonly"
+ name="type"
+ data-ng-change="onTypeChange()"
+ data-ng-model="editAttributeModel.attribute.type"
+ data-ng-options="type for type in editAttributeModel.types">
+ <option value="">Choose Type</option>
+ </select>
+ <div class="input-error" data-ng-show="forms.editForm.type.$dirty && forms.editForm.type.$invalid">
+ <span ng-show="forms.editForm.type.$error.required" translate="VALIDATION_ERROR_REQUIRED" translate-values="{'field': 'Type' }"></span>
+ </div>
+ </div>
+
+ <!-- schema -->
+ <div class="i-sdc-form-item" data-ng-class="{error:(forms.editForm.schema.$dirty && forms.editForm.schema.$invalid)}"
+ data-ng-if="showSchema()">
+ <label class="i-sdc-form-label required">Entry Schema</label>
+ <select class="i-sdc-form-select" ng-if="isSchemaEditable()"
+ data-required
+ name="schema"
+ data-ng-disabled="editAttributeModel.attribute.readonly"
+ data-ng-change="onTypeChange(false)"
+ data-ng-model="editAttributeModel.attribute.schema.property.type"
+ data-ng-options="type for type in editAttributeModel.simpleTypes">
+ <option value="">Choose Schema Type</option>
+ </select>
+ <input class="i-sdc-form-input"
+ ng-if="!isSchemaEditable()"
+ data-tests-id="schema"
+ data-ng-disabled="true"
+ data-ng-model="editAttributeModel.attribute.schema.property.type"
+ type="text"
+ name="schema"/>
+ <div class="input-error" data-ng-show="forms.editForm.schema.$dirty && forms.editForm.schema.$invalid">
+ <span ng-show="forms.editForm.schema.$error.required" translate="VALIDATION_ERROR_REQUIRED" translate-values="{'field': 'Entry schema' }"></span>
+ </div>
+ </div>
+
+ <!-- Default value -->
+ <div class="i-sdc-form-item" data-ng-class="{error:(forms.editForm.value.$dirty && forms.editForm.value.$invalid)}">
+ <label class="i-sdc-form-label">Default Value</label>
+ <input class="i-sdc-form-input"
+ data-tests-id="defaultvalue"
+ ng-if="!(editAttributeModel.attribute.type == 'boolean')"
+ data-ng-maxlength="100"
+ data-ng-disabled="editAttributeModel.attribute.readonly && !isAttributeValueOwner()"
+ maxlength="100"
+ data-ng-model="attributeValue.value"
+ type="text"
+ name="value"
+ data-custom-validation="" data-validation-func="validateUniqueKeys"
+ data-ng-pattern="validationPattern"
+ data-ng-model-options="{ debounce: 200 }"
+ data-ng-change="!forms.editForm.value.$error.pattern && ('integer'==editAttributeModel.attribute.type && forms.editForm.value.$setValidity('pattern', validateIntRange(editAttributeModel.attribute.value)) || onValueChange())"
+ autofocus />
+ <select class="i-sdc-form-select"
+ data-tests-id="booleantype"
+ ng-if="editAttributeModel.attribute.type == 'boolean'"
+ data-ng-disabled="editAttributeModel.attribute.readonly && !isAttributeValueOwner()"
+ name="value"
+ data-ng-change="onValueChange()"
+ data-ng-model="attributeValue.value">
+ <option value="true">true</option>
+ <option value="false">false</option>
+ </select>
+ <div class="input-error" data-ng-show="forms.editForm.value.$dirty && forms.editForm.value.$invalid">
+ <span ng-show="forms.editForm.value.$error.required" translate="VALIDATION_ERROR_REQUIRED" translate-values="{'field': 'Default value' }"></span>
+ <span ng-show="forms.editForm.value.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '100' }"></span>
+ <span ng-show="forms.editForm.value.$error.pattern" translate="{{getValidationTranslate()}}"></span>
+ <span ng-show="forms.editForm.value.$error.customValidation" translate="ATTRIBUTE_EDIT_MAP_UNIQUE_KEYS"></span>
+ </div>
+ </div>
+
+ <!-- hidden -->
+ <div class="i-sdc-form-item" data-ng-if="isAttributeValueOwner()">
+ <label class="i-sdc-form-label">Hidden</label>
+ <input class="i-sdc-form-input"
+ data-tests-id="hidden"
+ data-ng-disabled="editAttributeModel.attribute.readonly && !isAttributeValueOwner()"
+ data-ng-model="editAttributeModel.attribute.hidden"
+ type="checkbox"
+ name="hidden"/>
+ </div>
+ </div>
+
+ </div>
+
+ </form>
+ </div>
+
+</sdc-modal>
diff --git a/catalog-ui/src/app/view-models/forms/attribute-form/attribute-from-view-model.ts b/catalog-ui/src/app/view-models/forms/attribute-form/attribute-from-view-model.ts
new file mode 100644
index 0000000000..122cf10ed2
--- /dev/null
+++ b/catalog-ui/src/app/view-models/forms/attribute-form/attribute-from-view-model.ts
@@ -0,0 +1,241 @@
+'use strict';
+import {AttributeModel, Component} from "app/models";
+import {IMapRegex, ValidationUtils, FormState, PROPERTY_TYPES} from "app/utils";
+
+export interface IEditAttributeModel {
+ attribute:AttributeModel;
+ types:Array<string>;
+ simpleTypes:Array<string>;
+}
+
+export class attributeValue {//in order to solve DE226783, we update the value on another obj
+ value:string;
+}
+
+interface IAttributeFormViewModelScope extends ng.IScope {
+ $$childTail:any;
+ forms:any;
+ editForm:ng.IFormController;
+ footerButtons:Array<any>;
+ isService:boolean;
+ editAttributeModel:IEditAttributeModel;
+ modalInstanceAttribute:ng.ui.bootstrap.IModalServiceInstance;
+ isNew:boolean;
+ listRegex:IMapRegex;
+ mapRegex:IMapRegex;
+ propertyNameValidationPattern:RegExp;
+ commentValidationPattern:RegExp;
+ isLoading:boolean;
+ validationPattern:RegExp;
+ attributeValue:attributeValue;
+
+ save():void;
+ close():void;
+ onTypeChange():void;
+ onValueChange():void;
+ isAttributeValueOwner():boolean;
+ validateIntRange(value:string):boolean;
+ validateUniqueKeys(viewValue:string):boolean;
+ getValidationTranslate():string;
+ showSchema():boolean;
+ isSchemaEditable():boolean;
+ validateName():void;
+}
+
+export class AttributeFormViewModel {
+
+ static '$inject' = [
+ '$scope',
+ '$uibModalInstance',
+ 'attribute',
+ 'ValidationUtils',
+ 'CommentValidationPattern',
+ 'PropertyNameValidationPattern',
+ 'component'
+ ];
+
+ private formState:FormState;
+
+
+ constructor(private $scope:IAttributeFormViewModelScope,
+ private $uibModalInstance:ng.ui.bootstrap.IModalServiceInstance,
+ private attribute:AttributeModel,
+ private ValidationUtils:ValidationUtils,
+ private CommentValidationPattern:RegExp,
+ private PropertyNameValidationPattern:RegExp,
+ private component:Component) {
+ this.formState = angular.isDefined(attribute.name) ? FormState.UPDATE : FormState.CREATE;
+ this.initScope();
+ }
+
+ private initResource = ():void => {
+ this.$scope.editAttributeModel.attribute = new AttributeModel(this.attribute);
+ if (this.$scope.editAttributeModel.types.indexOf(this.attribute.type) === -1) {//attribute defaulte type is string too?
+ this.attribute.type = "string";
+ }
+ };
+
+ private initEditAttributeModel = ():void => {
+ this.$scope.editAttributeModel = {
+ attribute: null,
+ types: ['integer', 'string', 'float', 'boolean', 'list', 'map'],
+ simpleTypes: ['integer', 'string', 'float', 'boolean']
+ };
+
+ this.initResource();
+ };
+
+ private initScope = ():void => {
+
+ //scope attributes
+ this.$scope.forms = {};
+ this.$scope.propertyNameValidationPattern = this.PropertyNameValidationPattern;
+ this.$scope.commentValidationPattern = this.CommentValidationPattern;
+
+ this.$scope.modalInstanceAttribute = this.$uibModalInstance;
+ this.$scope.listRegex = this.ValidationUtils.getPropertyListPatterns();
+ this.$scope.mapRegex = this.ValidationUtils.getPropertyMapPatterns();
+
+ this.$scope.isNew = (this.formState === FormState.CREATE);
+ this.$scope.isLoading = false;
+ this.$scope.attributeValue = new attributeValue();
+
+ this.initEditAttributeModel();
+ this.setValidationPattern();
+
+ //scope methods
+ this.$scope.save = ():void => {
+ if (!this.$scope.forms.editForm.$invalid) {
+ let attribute:AttributeModel = this.$scope.editAttributeModel.attribute;
+ this.$scope.editAttributeModel.attribute.description = this.ValidationUtils.stripAndSanitize(this.$scope.editAttributeModel.attribute.description);
+ ////if read only - just closes the modal
+ if (this.$scope.editAttributeModel.attribute.readonly && !this.$scope.isAttributeValueOwner()) {
+ this.$uibModalInstance.close();
+ return;
+ }
+ this.$scope.isLoading = true;
+ let onAttributeFaild = (response):void => {
+ console.info('onFaild', response);
+ this.$scope.isLoading = false;
+ };
+
+ let onAttributeSuccess = (attributeFromBE:AttributeModel):void => {
+ console.info('onAttributeResourceSuccess : ', attributeFromBE);
+ this.$scope.isLoading = false;
+ this.$uibModalInstance.close();
+ };
+
+ //in case we have uniqueId we call update method
+ if (this.$scope.isAttributeValueOwner()) {
+ attribute.value = this.$scope.attributeValue.value;
+ this.component.updateInstanceAttribute(attribute).then(onAttributeSuccess, onAttributeFaild);
+ } else {
+ attribute.defaultValue = this.$scope.attributeValue.value;
+ this.component.addOrUpdateAttribute(attribute).then(onAttributeSuccess, onAttributeFaild);
+ }
+ }
+ };
+
+ this.$scope.close = ():void => {
+ this.$uibModalInstance.close();
+ };
+
+ this.$scope.validateName = ():void => {
+ let existsAttr:AttributeModel = _.find(this.component.attributes, (attribute:AttributeModel) => {
+ return attribute.name === this.$scope.editAttributeModel.attribute.name;
+ });
+ if (existsAttr) {
+ this.$scope.forms.editForm["attributeName"].$setValidity('nameExist', false);
+ } else {
+ this.$scope.forms.editForm["attributeName"].$setValidity('nameExist', true);
+ }
+
+ };
+
+ this.$scope.onTypeChange = ():void => {
+ this.$scope.editAttributeModel.attribute.value = '';
+ this.$scope.editAttributeModel.attribute.defaultValue = '';
+ this.setValidationPattern();
+ };
+
+ this.$scope.isAttributeValueOwner = ():boolean=> {
+ return this.component.isService() || !!this.component.selectedInstance;
+ };
+
+ this.$scope.onValueChange = ():void => {
+ if (!this.$scope.editAttributeModel.attribute.value) {
+ if (this.$scope.isAttributeValueOwner()) {
+ this.$scope.editAttributeModel.attribute.value = this.$scope.editAttributeModel.attribute.defaultValue;
+ }
+ }
+ };
+
+
+ this.$scope.validateUniqueKeys = (viewValue:string):boolean => {
+ if (this.$scope.editAttributeModel.attribute.type === 'map') {
+ return this.ValidationUtils.validateUniqueKeys(viewValue);
+ }
+ else {
+ return true; //always valid if not a map
+ }
+ };
+
+ this.$scope.validateIntRange = (value:string):boolean => {
+ return !value || this.ValidationUtils.validateIntRange(value);
+ };
+
+ this.$scope.isSchemaEditable = ():boolean => {
+ let schemaType = this.$scope.editAttributeModel.attribute.schema.property.type;
+ return this.$scope.editAttributeModel.simpleTypes.indexOf(schemaType) > -1 || !schemaType;
+ };
+
+ this.$scope.showSchema = ():boolean => {
+ return ['list', 'map'].indexOf(this.$scope.editAttributeModel.attribute.type) > -1;
+ };
+
+ this.$scope.getValidationTranslate = ():string => {
+ let result = "ATTRIBUTE_EDIT_PATTERN";
+ if (this.$scope.showSchema()) {
+
+ result = "ATTRIBUTE_EDIT_" + this.$scope.editAttributeModel.attribute.type.toUpperCase();
+
+ if (this.$scope.editAttributeModel.attribute.schema.property.type === PROPERTY_TYPES.STRING) {
+ result += "_STRING";
+ } else if (this.$scope.editAttributeModel.attribute.schema.property.type === PROPERTY_TYPES.BOOLEAN) {
+ result += "_BOOLEAN";
+ } else {
+ result += "_GENERIC";
+ }
+ }
+
+ return result;
+ };
+
+ // Add the done button at the footer.
+ this.$scope.footerButtons = [
+ {'name': 'Done', 'css': 'blue', 'callback': this.$scope.save},
+ {'name': 'Cancel', 'css': 'grey', 'callback': this.$scope.close}
+ ];
+
+ this.$scope.$watchCollection("forms.editForm.$invalid", (newVal, oldVal) => {
+ this.$scope.footerButtons[0].disabled = this.$scope.forms.editForm.$invalid;
+ });
+
+ this.$scope.attributeValue.value = this.$scope.isAttributeValueOwner() ? this.$scope.editAttributeModel.attribute.value : this.$scope.editAttributeModel.attribute.defaultValue;
+ };
+
+
+ private setValidationPattern = ():void => {
+
+ if (this.$scope.editAttributeModel.attribute.type === 'list') {
+ this.$scope.validationPattern = this.$scope.listRegex[this.$scope.editAttributeModel.attribute.schema.property.type];
+ }
+ else if (this.$scope.editAttributeModel.attribute.type === 'map') {
+ this.$scope.validationPattern = this.$scope.mapRegex[this.$scope.editAttributeModel.attribute.schema.property.type];
+ }
+ else {
+ this.$scope.validationPattern = this.ValidationUtils.getValidationPattern(this.$scope.editAttributeModel.attribute.type);
+ }
+
+ };
+}
diff --git a/catalog-ui/src/app/view-models/forms/env-parameters-form/env-parameters-form.html b/catalog-ui/src/app/view-models/forms/env-parameters-form/env-parameters-form.html
new file mode 100644
index 0000000000..ae13844532
--- /dev/null
+++ b/catalog-ui/src/app/view-models/forms/env-parameters-form/env-parameters-form.html
@@ -0,0 +1,92 @@
+<sdc-modal modal="envParametersModal" type="classic" class="sdc-env-form-container" buttons="buttons" header="{{artifactResource.artifactDisplayName}}" show-close-button="true">
+ <div class="w-sdc-env-form-container">
+ <div class="w-sdc-env-search pull-left">
+ <input type="text" class="w-sdc-env-search-input" placeholder="Search" data-ng-model="searchText" data-tests-id="search-env-param-name"/>
+ <div class="search-icon-container">
+ <span class="w-sdc-search-icon env-search-icon magnification-white"></span>
+ </div>
+ </div>
+ <div class="table-container-flex">
+ <div class="table">
+ <div class="head flex-container">
+ <div class="table-header head-row flex-item" ng-repeat="header in tableHeadersList track by $index">
+ <info-tooltip class="header-info" data-ng-if="header.info" class="info-button" info-message-translate="{{header.info}}" direction="left"></info-tooltip>
+ {{header.title}}
+ </div>
+ </div>
+ <div class="body">
+ <perfect-scrollbar suppress-scroll-x="true" scroll-y-margin-offset="0" include-padding="true" class="scrollbar-container">
+ <ng-form name="forms.editForm" class="w-sdc-form">
+ <div data-ng-repeat="parameter in heatParameters| filter:{filterTerm:searchText} track by $index "
+ class="flex-container data-row"
+ data-ng-init="parameter.filterTerm=parameter.name + ' ' + parameter.currentValue + ' ' + parameter.defaultValue + ' ' +parameter.description">
+ <div class="table-col-general flex-item" data-tests-id="heatParameterName_{{parameter.name}}">
+ {{parameter.name}}
+ <span class="sprite-new show-desc hand"
+ uib-popover-template="templatePopover"
+ popover-class="parameter-description-popover top"
+ popover-title="Parameter Description"
+ popover-placement="top-left"
+ popover-is-open="selectedParameter.name == parameter.name"
+ popover-trigger="'none'"
+ popover-append-to-body="true"
+ data-ng-click="openDescPopover(parameter)"></span>
+ </div>
+
+ <div class="table-col-general flex-item text">
+ <span data-tests-id="default-value-of-{{parameter.name}}" tooltips tooltip-content="{{parameter.defaultValue}}">{{parameter.defaultValue}}</span>
+ </div>
+
+ <!--<div class="table-col-general flex-item">-->
+ <!--<input type="text" value="{{parameter.currentValue}}"/>-->
+ <!--</div>-->
+
+ <div class="table-col-general flex-item left-column-container">
+
+ <div class="i-sdc-form-item" data-ng-class="{error:(forms.editForm[parameter.name].$dirty && forms.editForm[parameter.name].$invalid), required: (parameter.defaultValue)}">
+ <span class="required-symbol">*</span>
+ <div class="input-parameter">
+ <input class="i-sdc-form-input" data-ng-class="{error: (forms.editForm[parameter.name].$invalid)}"
+ data-ng-model-options="{ debounce: 200 }"
+ data-ng-model="parameter.currentValue"
+ value="{{parameter.currentValue}}"
+ type="text"
+ name="{{parameter.name}}"
+ data-ng-pattern="getValidationPattern(parameter.type, 'heat')"
+ data-ng-required="parameter.defaultValue"
+ data-ng-change="'json'==parameter.type && forms.editForm[parameter.name].$setValidity('pattern', validateJson(parameter.currentValue))"
+ data-ng-blur="(forms.editForm[parameter.name].$error.required && (parameter.currentValue=parameter.defaultValue))"
+ data-tests-id="value-field-of-{{parameter.name}}"/>
+
+ <div class="action-button">
+ <div class="sprite-new revert-param" data-ng-if="parameter.defaultValue" data-ng-click="parameter.currentValue = parameter.defaultValue"
+ data-tests-id="revert-{{parameter.name}}">
+ </div>
+ <div class="sprite-new delete-param"
+ data-ng-if="!parameter.defaultValue"
+ data-ng-disabled="!parameter.currentValue"
+ data-ng-class="{disabled:!parameter.currentValue}"
+ data-ng-click="parameter.currentValue = ''"
+ data-tests-id="delete-{{parameter.name}}">
+ </div>
+ </div>
+ </div>
+ <div class="input-error" data-ng-show="forms.editForm[parameter.name].$invalid">
+ <span ng-show="forms.editForm[parameter.name].$error.required" translate="VALIDATION_ERROR_REQUIRED" translate-values="{'field': 'Value'}"></span>
+ <span ng-show="forms.editForm[parameter.name].$error.pattern && parameter.type==='string'" translate="VALIDATION_ERROR_SPECIAL_CHARS_NOT_ALLOWED"></span>
+ <span ng-show="forms.editForm[parameter.name].$error.pattern && !(parameter.type==='string')" translate="VALIDATION_ERROR_TYPE" translate-values="{'type': '{{parameter.type}}'}"></span>
+ </div>
+ </div>
+
+ </div>
+
+ </div>
+ </ng-form>
+
+ </perfect-scrollbar>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+</sdc-modal>
diff --git a/catalog-ui/src/app/view-models/forms/env-parameters-form/env-parameters-form.less b/catalog-ui/src/app/view-models/forms/env-parameters-form/env-parameters-form.less
new file mode 100644
index 0000000000..a25a2c5f62
--- /dev/null
+++ b/catalog-ui/src/app/view-models/forms/env-parameters-form/env-parameters-form.less
@@ -0,0 +1,178 @@
+
+.sdc-env-form-container{
+ .w-sdc-modal-body{
+ padding: 20px 10px 2px 10px;
+ }
+ .w-sdc-modal-body-content{
+ .b_6;
+ display: block;
+ }
+
+ .env-file-generation-label{
+ .p_9;
+ .bold;
+ margin-bottom: 20px;
+ }
+}
+
+.w-sdc-env-form-container {
+ height: 650px;
+
+ .w-sdc-env-search {
+ padding: 10px 20px 20px 0;
+ white-space: nowrap;
+ position: relative;
+ width: 60%;
+ height: 64px;
+
+ .env-search-icon {
+ top: 9px;
+ right: 11px;
+ }
+
+ .magnification-white {
+ .sprite-new;
+ .search-white-icon;
+ .hand;
+ }
+
+ .search-icon-container {
+ width: 35px;
+ height: 30px;
+ background-color: @main_color_a;
+ white-space: nowrap;
+ float: right;
+ position: relative;
+ bottom: 31px;
+ right: 1px;
+ border-radius: 0px 4px 4px 0px;
+ .hand
+ }
+
+ .w-sdc-env-search-input {
+ border: 1px solid @color_e;
+ .border-radius(4px);
+ height: 32px;
+ margin: 0;
+ padding: 0px 28px 3px 10px;
+ vertical-align: 4px;
+ width: 100%;
+ outline: none;
+ font-style: italic;
+ }
+ }
+
+ .table-container-flex {
+ height: 570px;
+
+ .table {
+ height: 100%;
+ .flex-item:nth-child(1) {
+ flex-grow: 20;
+ .show-desc{
+ float: right;
+ top: 10px;
+ position: relative;
+ }
+ }
+
+ .flex-item:nth-child(2) {
+ flex-grow: 10;
+ }
+
+ .flex-item:nth-child(3) {
+ flex-grow: 10;
+ }
+ .scrollbar-container{
+ max-height: 527px;
+ }
+ .left-column-container{
+ .required-symbol {
+ .m_14_m;
+ color: #f33;
+ display: none;
+ position: relative;
+ left: -4px;
+ top: 3px;
+ }
+
+ .i-sdc-form-item{
+ border-right: none;
+ margin: 0px;
+
+ .input-parameter{
+ border: none;
+ height: 30px;
+ width: 254px;
+ float: right;
+ input{
+ .m_13_m;
+ width: 100%;
+ display: inline-flex;
+ padding-right: 33px;
+ }
+ .action-button{
+ border-left: solid 1px @main_color_o;
+ position: relative;
+ height: 20px;
+ width: 25px;
+ top: -25px;
+ left: 228px;
+ padding-left: 6px;
+ background-color: @main_color_p;
+ div:not(.disable){
+ .hand;
+ }
+ }
+ }
+
+ &.required{
+ .required-symbol {
+ display: inline-flex;
+ }
+ .input-parameter {
+ width: 250px;
+ }
+ .action-button{
+ left: 224px;
+ }
+ }
+ }
+
+
+
+ }
+ }
+
+ .text{
+ overflow: hidden;
+ text-overflow: ellipsis;
+ display: inline-block;
+ white-space: nowrap;
+ }
+ }
+
+
+ .parameter-description{
+ background-color: @func_color_r;
+ border-left: 4px solid @main_color_a;
+ padding: 10px 30px;
+ }
+}
+
+.header-info{
+ float: right;
+}
+
+.parameter-description-popover{
+ z-index: 1100;
+ min-width: 210px;
+ .arrow{
+ left: 20px !important;
+ border-width: 7px;
+ bottom: -8px !important;
+ }
+ .popover-content{
+ .f-type._13_m;;
+ }
+}
diff --git a/catalog-ui/src/app/view-models/forms/env-parameters-form/env-parameters-form.ts b/catalog-ui/src/app/view-models/forms/env-parameters-form/env-parameters-form.ts
new file mode 100644
index 0000000000..476af4ada9
--- /dev/null
+++ b/catalog-ui/src/app/view-models/forms/env-parameters-form/env-parameters-form.ts
@@ -0,0 +1,153 @@
+'use strict';
+import {ValidationUtils} from "app/utils";
+import {ArtifactModel, HeatParameterModel, Component} from "app/models";
+
+export interface IEnvParametersFormViewModelScope extends ng.IScope {
+ isLoading:boolean;
+ type:string;
+ heatParameters:Array<HeatParameterModel>;
+ forms:any;
+ artifactResource:ArtifactModel;
+ buttons:Array<any>;
+ envParametersModal:ng.ui.bootstrap.IModalServiceInstance;
+ tableHeadersList:Array<any>;
+ selectedParameter:HeatParameterModel;
+ templatePopover:string;
+
+ getValidationPattern(type:string):RegExp;
+ isInstance():boolean;
+ validateJson(json:string):boolean;
+ close():void;
+ save():void;
+ openDescPopover(selectedParam:HeatParameterModel):void;
+ closeDescriptionPopover():void;
+}
+
+export class EnvParametersFormViewModel {
+
+ static '$inject' = [
+ '$scope',
+ '$templateCache',
+ '$state',
+ '$uibModalInstance',
+ 'artifact',
+ 'ValidationUtils',
+ 'component'
+ ];
+
+ constructor(private $scope:IEnvParametersFormViewModelScope,
+ private $templateCache:ng.ITemplateCacheService,
+ private $state:any,
+ private $uibModalInstance:ng.ui.bootstrap.IModalServiceInstance,
+ private artifact:ArtifactModel,
+ private ValidationUtils:ValidationUtils,
+ private component:Component) {
+
+
+ this.initScope();
+ }
+
+ private updateInstanceHeat = ():void => {
+ let success = (responseArtifact:ArtifactModel):void => {
+ this.$scope.isLoading = false;
+ this.$uibModalInstance.close();
+ };
+
+ let error = ():void => {
+ this.$scope.isLoading = false;
+ console.info('Failed to load save artifact');
+ };
+
+ this.component.addOrUpdateInstanceArtifact(this.$scope.artifactResource).then(success, error);
+ };
+
+ private initScope = ():void => {
+ this.$scope.forms = {};
+ this.$scope.envParametersModal = this.$uibModalInstance;
+ this.$scope.artifactResource = this.artifact;
+ this.$scope.heatParameters = angular.copy(this.artifact.heatParameters);
+
+ this.$scope.tableHeadersList = [
+ {title: "Parameter", property: "name"},
+ {title: "Default Value", property: "defaultValue", info: "DEFAULT_VALUE_INFO"},
+ {title: "Current Value", property: "currentValue", info: "CURRENT_VALUE_INFO"}
+ ];
+
+ this.$templateCache.put("env-parametr-description-popover.html", require('app/view-models/forms/env-parameters-form/env-parametr-description-popover.html'));
+ this.$scope.templatePopover = "env-parametr-description-popover.html";
+
+ this.$scope.getValidationPattern = (validationType:string, parameterType?:string):RegExp => {
+ return this.ValidationUtils.getValidationPattern(validationType, parameterType);
+ };
+
+ this.$scope.validateJson = (json:string):boolean => {
+ if (!json) {
+ return true;
+ }
+ return this.ValidationUtils.validateJson(json);
+ };
+
+ this.$scope.isInstance = ():boolean => {
+ return !!this.component.selectedInstance;
+ };
+
+ this.$scope.save = ():void => {
+ this.$scope.buttons[0].disabled = true;//prevent double click (DE246266)
+ this.$scope.isLoading = true;
+ this.artifact.heatParameters = this.$scope.heatParameters;
+ this.artifact.heatParameters.forEach((parameter:any):void => {
+ /* if ("" === parameter.currentValue) {
+ parameter.currentValue = null;
+ }else */
+ if (!parameter.currentValue && parameter.defaultValue) {
+ parameter.currentValue = parameter.defaultValue;
+ }
+ });
+
+ if (this.$scope.isInstance()) {
+ this.updateInstanceHeat();
+ return;
+ }
+
+ let success = (responseArtifact:ArtifactModel):void => {
+ this.$scope.isLoading = false;
+ this.$uibModalInstance.close();
+
+ };
+
+ let error = ():void => {
+ this.$scope.isLoading = false;
+ console.info('Failed to load save artifact');
+ };
+
+ this.component.addOrUpdateArtifact(this.$scope.artifactResource).then(success, error);
+ };
+
+ this.$scope.close = ():void => {
+ //this.artifact.heatParameters.forEach((parameter:any):void => {
+ // if (!parameter.currentValue && parameter.defaultValue) {
+ // parameter.currentValue = parameter.defaultValue;
+ // }
+ //});
+ this.$uibModalInstance.dismiss();
+ };
+
+ this.$scope.openDescPopover = (selectedParam:HeatParameterModel):void => {
+ this.$scope.selectedParameter = selectedParam;
+ };
+
+ this.$scope.closeDescriptionPopover = ():void => {
+ this.$scope.selectedParameter = null;
+ };
+
+ this.$scope.buttons = [
+ {'name': 'Save', 'css': 'blue', 'callback': this.$scope.save},
+ {'name': 'Cancel', 'css': 'grey', 'callback': this.$scope.close}
+ ];
+
+ this.$scope.$watch("forms.editForm.$invalid", (newVal, oldVal) => {
+ this.$scope.buttons[0].disabled = this.$scope.forms.editForm.$invalid;
+ });
+
+ };
+}
diff --git a/catalog-ui/src/app/view-models/forms/env-parameters-form/env-parametr-description-popover.html b/catalog-ui/src/app/view-models/forms/env-parameters-form/env-parametr-description-popover.html
new file mode 100644
index 0000000000..ed127c6bfb
--- /dev/null
+++ b/catalog-ui/src/app/view-models/forms/env-parameters-form/env-parametr-description-popover.html
@@ -0,0 +1,4 @@
+<div>
+ <span data-tests-id='popover-x-button' data-ng-click='closeDescriptionPopover()' class='tlv-sprite tlv-x-btn close-popover-btn'></span>
+ {{selectedParameter.description}}
+</div>
diff --git a/catalog-ui/src/app/view-models/forms/input-form/input-form-view-modal.ts b/catalog-ui/src/app/view-models/forms/input-form/input-form-view-modal.ts
new file mode 100644
index 0000000000..e87e5c6c7d
--- /dev/null
+++ b/catalog-ui/src/app/view-models/forms/input-form/input-form-view-modal.ts
@@ -0,0 +1,126 @@
+'use strict';
+import {FormState, PROPERTY_TYPES, ValidationUtils, PROPERTY_VALUE_CONSTRAINTS} from "app/utils";
+import {InputModel} from "app/models";
+
+export interface IInputEditModel {
+ editInput:InputModel;
+}
+
+export interface IInputFormViewModelScope extends ng.IScope {
+ forms:any;
+ editForm:ng.IFormController;
+ footerButtons:Array<any>;
+ isService:boolean;
+ modalInstanceInput:ng.ui.bootstrap.IModalServiceInstance;
+ isLoading:boolean;
+ inputEditModel:IInputEditModel;
+ myValue:any;
+ maxLength:number;
+
+ save():void;
+ close():void;
+ validateIntRange(value:string):boolean;
+ validateJson(json:string):boolean;
+ getValidationPattern(type:string):RegExp;
+ showSchema():boolean;
+}
+
+export class InputFormViewModel {
+
+ static '$inject' = [
+ '$scope',
+ '$uibModalInstance',
+ 'ValidationUtils',
+ 'input'
+ ];
+
+ private formState:FormState;
+
+
+ constructor(private $scope:IInputFormViewModelScope,
+ private $uibModalInstance:ng.ui.bootstrap.IModalServiceInstance,
+ private ValidationUtils:ValidationUtils,
+ private input:InputModel) {
+ this.initScope();
+ this.initMyValue();
+ }
+
+ private initMyValue = ():void => {
+ switch (this.$scope.inputEditModel.editInput.type) {
+ case PROPERTY_TYPES.MAP:
+ this.$scope.myValue = this.$scope.inputEditModel.editInput.defaultValue ? JSON.parse(this.$scope.inputEditModel.editInput.defaultValue) : {'': null};
+ break;
+ case PROPERTY_TYPES.LIST:
+ this.$scope.myValue = this.$scope.inputEditModel.editInput.defaultValue ? JSON.parse(this.$scope.inputEditModel.editInput.defaultValue) : [];
+ break;
+ }
+ };
+
+ private initDefaultValueMaxLength = ():void => {
+ switch (this.$scope.inputEditModel.editInput.type) {
+ case PROPERTY_TYPES.MAP:
+ case PROPERTY_TYPES.LIST:
+ this.$scope.maxLength = this.$scope.inputEditModel.editInput.schema.property.type == PROPERTY_TYPES.JSON ?
+ PROPERTY_VALUE_CONSTRAINTS.JSON_MAX_LENGTH :
+ PROPERTY_VALUE_CONSTRAINTS.MAX_LENGTH;
+ break;
+ case PROPERTY_TYPES.JSON:
+ this.$scope.maxLength = PROPERTY_VALUE_CONSTRAINTS.JSON_MAX_LENGTH;
+ break;
+ default:
+ this.$scope.maxLength = PROPERTY_VALUE_CONSTRAINTS.MAX_LENGTH;
+ }
+ };
+
+ private initScope = ():void => {
+ this.$scope.forms = {};
+ this.$scope.modalInstanceInput = this.$uibModalInstance;
+ this.$scope.inputEditModel = {
+ editInput: null
+ };
+ this.$scope.inputEditModel.editInput = this.input;
+ this.initDefaultValueMaxLength();
+
+ //scope methods
+ this.$scope.save = ():void => {
+ if (this.$scope.showSchema()) {
+ this.$scope.inputEditModel.editInput.defaultValue = JSON.stringify(this.$scope.myValue);
+ }
+ };
+
+ this.$scope.close = ():void => {
+ this.$uibModalInstance.close();
+ };
+
+ this.$scope.validateIntRange = (value:string):boolean => {
+ return !value || this.ValidationUtils.validateIntRange(value);
+ };
+
+ this.$scope.validateJson = (json:string):boolean => {
+ if (!json) {
+ return true;
+ }
+ return this.ValidationUtils.validateJson(json);
+ };
+
+ this.$scope.showSchema = ():boolean => {
+ return ['list', 'map'].indexOf(this.$scope.inputEditModel.editInput.type) > -1;
+ };
+
+ this.$scope.getValidationPattern = (type:string):RegExp => {
+ return this.ValidationUtils.getValidationPattern(type);
+ };
+
+ // Add the done button at the footer.
+ this.$scope.footerButtons = [
+ {'name': 'Done', 'css': 'blue', 'callback': this.$scope.save},
+ {'name': 'Cancel', 'css': 'grey', 'callback': this.$scope.close}
+ ];
+
+ this.$scope.$watchCollection("forms.editForm.$invalid", (newVal, oldVal) => {
+ this.$scope.footerButtons[0].disabled = this.$scope.forms.editForm.$invalid;
+ });
+
+ };
+}
+
diff --git a/catalog-ui/src/app/view-models/forms/input-form/input-form-view.html b/catalog-ui/src/app/view-models/forms/input-form/input-form-view.html
new file mode 100644
index 0000000000..1bf6dc4ca9
--- /dev/null
+++ b/catalog-ui/src/app/view-models/forms/input-form/input-form-view.html
@@ -0,0 +1,125 @@
+<sdc-modal modal="modalInstanceInput" type="classic" class="sdc-edit-input-container" buttons="footerButtons" header="Update Input" show-close-button="true">
+
+ <div class="sdc-edit-input-form-container" >
+ <form novalidate class="w-sdc-form two-columns" name="forms.editForm" >
+
+ <div class="w-sdc-form-columns-wrapper">
+
+ <div class="w-sdc-form-column">
+
+ <!-- Name -->
+ <div class="i-sdc-form-item">
+ <label class="i-sdc-form-label">Name</label>
+ <input class="i-sdc-form-input"
+ data-tests-id="inputName"
+ data-ng-maxlength="50"
+ data-ng-disabled="true"
+ maxlength="50"
+ data-ng-model="inputEditModel.editInput.name"
+ type="text"
+ name="inputName"
+ autofocus />
+ </div>
+
+ <!-- Description -->
+ <div class="i-sdc-form-item">
+ <label class="i-sdc-form-label">Description</label>
+ <textarea class="i-sdc-form-textarea"
+ data-ng-disabled="true"
+ name="description"
+ data-ng-model="inputEditModel.editInput.description"
+ data-tests-id="description"></textarea>
+ </div>
+
+
+ </div>
+
+ <div class="w-sdc-form-column">
+ <!-- Type -->
+ <div class="i-sdc-form-item">
+ <label class="i-sdc-form-label">Type</label>
+ <input class="i-sdc-form-input"
+ data-tests-id="type"
+ data-ng-disabled="true"
+ data-ng-model="inputEditModel.editInput.type"
+ type="text"
+ name="type"/>
+ </div>
+ <!-- schema -->
+ <div class="i-sdc-form-item"
+ data-ng-if="showSchema()">
+ <label class="i-sdc-form-label">Entry Schema</label>
+ <input class="i-sdc-form-input"
+ data-tests-id="schema"
+ data-ng-disabled="true"
+ data-ng-model="inputEditModel.editInput.schema.property.type"
+ type="text"
+ name="schema"/>
+ </div>
+ <!-- Default value -->
+ <div class="i-sdc-form-item" data-ng-class="{error:(forms.editForm.value.$dirty && forms.editForm.value.$invalid)}">
+ <label class="i-sdc-form-label">Default Value</label>
+ <div data-ng-switch="inputEditModel.editInput.type">
+ <div ng-switch-when="map">
+ <type-map value-obj-ref="myValue"
+ schema-property="inputEditModel.editInput.schema.property"
+ parent-form-obj="forms.editForm"
+ fields-prefix-name="'input-value-'"
+ read-only="true"
+ default-value=""
+ types="[]"
+ max-length="maxLength"></type-map>
+ </div>
+ <div ng-switch-when="list">
+ <type-list value-obj-ref="myValue"
+ schema-property="inputEditModel.editInput.schema.property"
+ parent-form-obj="forms.editForm"
+ fields-prefix-name="'input-value-'"
+ read-only="true"
+ default-value=""
+ types="[]"
+ max-length="maxLength"></type-list>
+ </div>
+ <div ng-switch-default>
+ <div class="i-sdc-form-item" data-ng-class="{error:(forms.editForm.value.$dirty && forms.editForm.value.$invalid)}">
+ <input class="i-sdc-form-input"
+ data-tests-id="defaultvalue"
+ ng-if="inputEditModel.editInput.type != 'boolean'"
+ data-ng-maxlength="maxLength"
+ data-ng-disabled="true"
+ maxlength="{{maxLength}}"
+ data-ng-model="inputEditModel.editInput.defaultValue"
+ type="text"
+ name="value"
+ data-ng-pattern="getValidationPattern(input.type)"
+ data-ng-model-options="{ debounce: 200 }"
+ data-ng-change="('json'==inputEditModel.editInput.type && forms.editForm.value.$setValidity('pattern', validateJson(inputEditModel.editInput.defaultValue)))
+ ||(!forms.editForm.value.$error.pattern && ('integer'==inputEditModel.editInput.type && forms.editForm.value.$setValidity('pattern', validateIntRange(inputEditModel.editInput.defaultValue)) || onValueChange()))"
+ autofocus />
+ <select class="i-sdc-form-select"
+ data-tests-id="booleantype"
+ ng-if="inputEditModel.editInput.type == 'boolean'"
+ data-ng-disabled="true"
+ name="value"
+ data-ng-model="inputEditModel.editInput.defaultValue">
+ <option value="true">true</option>
+ <option value="false">false</option>
+ </select>
+
+ <div class="input-error" data-ng-show="forms.editForm.value.$dirty && forms.editForm.value.$invalid">
+ <span ng-show="forms.editForm.value.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '{{maxLength}}' }"></span>
+ <span ng-show="forms.editForm.value.$error.pattern" translate="PROPERTY_EDIT_PATTERN"></span>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ </div>
+
+ </div>
+
+ </form>
+ </div>
+
+</sdc-modal>
diff --git a/catalog-ui/src/app/view-models/forms/property-forms/base-property-form/property-form-base-model.ts b/catalog-ui/src/app/view-models/forms/property-forms/base-property-form/property-form-base-model.ts
new file mode 100644
index 0000000000..1ba5a90bb4
--- /dev/null
+++ b/catalog-ui/src/app/view-models/forms/property-forms/base-property-form/property-form-base-model.ts
@@ -0,0 +1,204 @@
+/**
+ * Created by obarda on 1/19/2017.
+ */
+'use strict';
+import {DataTypesService} from "app/services/data-types-service";
+import {PropertyModel, DataTypesMap, Component} from "app/models";
+import {ValidationUtils, PROPERTY_DATA} from "app/utils";
+
+export interface IPropertyFormBaseViewScope extends ng.IScope {
+
+ forms:any;
+ editForm:ng.IFormController;
+
+ property:PropertyModel;
+ types:Array<string>;
+ nonPrimitiveTypes:Array<string>;
+ simpleTypes:Array<string>;
+
+ footerButtons:Array<any>;
+ modalPropertyFormBase:ng.ui.bootstrap.IModalServiceInstance;
+ currentPropertyIndex:number;
+ isLastProperty:boolean;
+ innerViewSrcUrl:string;
+
+ //Disabling filed - each child controller can change this when needed
+ isNew:boolean;
+ isTypeSelectorDisable:boolean;
+ isDeleteDisable:boolean;
+ isNameDisable:boolean;
+ isDescriptionDisable:boolean;
+ isPropertyValueDisable:boolean;
+ isArrowsDisabled:boolean;
+
+ //Validation pattern
+ validationPattern:RegExp;
+ propertyNameValidationPattern:RegExp;
+ commentValidationPattern:RegExp;
+ numberValidationPattern:RegExp;
+
+ dataTypes:DataTypesMap;
+
+ isLoading:boolean;
+
+ save():void;
+ close():void;
+ getNext():void;
+ getPrev():void;
+ getValidationPattern(type:string):RegExp;
+}
+
+export abstract class PropertyFormBaseView {
+
+
+ constructor(protected $scope:IPropertyFormBaseViewScope,
+ protected $uibModalInstance:ng.ui.bootstrap.IModalServiceInstance,
+ protected $injector:ng.auto.IInjectorService,
+ protected originalProperty:PropertyModel,
+ protected component:Component,
+ protected filteredProperties:Array<PropertyModel>,
+ protected DataTypesService:DataTypesService) {
+
+ this.initScope();
+
+ }
+
+ protected validationUtils:ValidationUtils;
+
+ protected isPropertyValueOwner = ():boolean => {
+ return this.component.isService() || !!this.component.selectedInstance;
+ };
+
+ private isDisable = ():boolean => {
+ return this.isPropertyValueOwner() || this.$scope.property.readonly;
+ };
+
+
+ //This is the difault state, Childs screens can change if needed
+ protected initButtonsState = ():void => {
+ let isDisable = this.isDisable();
+
+ this.$scope.isArrowsDisabled = false;
+ this.$scope.isDeleteDisable = isDisable;
+ this.$scope.isDescriptionDisable = isDisable;
+ this.$scope.isNameDisable = isDisable;
+ this.$scope.isTypeSelectorDisable = isDisable;
+ this.$scope.isPropertyValueDisable = this.$scope.property.readonly && !this.isPropertyValueOwner();
+ };
+
+ protected initValidations = ():void => {
+
+ this.$scope.validationPattern = this.$injector.get('ValidationPattern');
+ this.$scope.propertyNameValidationPattern = this.$injector.get('PropertyNameValidationPattern');
+ this.$scope.commentValidationPattern = this.$injector.get('CommentValidationPattern');
+ this.$scope.numberValidationPattern = this.$injector.get('NumberValidationPattern');
+ this.validationUtils = this.$injector.get('ValidationUtils');
+ };
+
+ //Functions implemented on child's scope if needed
+ abstract save(doNotCloseModal?:boolean):ng.IPromise<boolean>;
+
+ protected onPropertyChange():void {
+ };
+
+ private updatePropertyByIndex = (index:number):void => {
+ this.$scope.property = new PropertyModel(this.filteredProperties[index]);
+ this.$scope.isLastProperty = this.$scope.currentPropertyIndex == (this.filteredProperties.length - 1);
+ this.onPropertyChange();
+ };
+
+ private initScope = ():void => {
+
+ this.$scope.forms = {};
+ this.$scope.isLoading = false;
+ this.$scope.property = new PropertyModel(this.originalProperty); //we create a new Object so if user press cance we won't update the property
+ this.$scope.types = PROPERTY_DATA.TYPES; //All types - simple type + map + list
+ this.$scope.simpleTypes = PROPERTY_DATA.SIMPLE_TYPES; //All simple types
+ this.$scope.dataTypes = this.DataTypesService.getAllDataTypes(); //Get all data types in service
+ this.$scope.modalPropertyFormBase = this.$uibModalInstance;
+ this.$scope.isNew = !angular.isDefined(this.$scope.property.name);
+
+ this.initValidations();
+ this.initButtonsState();
+ this.filteredProperties = _.sortBy(this.filteredProperties, 'name');
+ this.$scope.currentPropertyIndex = _.findIndex(this.filteredProperties, propety => propety.uniqueId == this.$scope.property.uniqueId);
+ this.$scope.isLastProperty = this.$scope.currentPropertyIndex == (this.filteredProperties.length - 1);
+
+ this.$scope.nonPrimitiveTypes = _.filter(Object.keys(this.$scope.dataTypes), (type:string)=> {
+ return this.$scope.types.indexOf(type) == -1;
+ });
+
+ this.$scope.close = ():void => {
+ this.$uibModalInstance.close();
+ };
+
+ this.$scope.save = ():void => {
+
+ let onSuccess = ():void => {
+ this.$scope.isLoading = false;
+ };
+ let onFailed = ():void => {
+ this.$scope.isLoading = false;
+ };
+
+ this.$scope.isLoading = true;
+ this.save(true).then(onSuccess, onFailed); // Child controller implement save logic
+ };
+
+ // Add the done button at the footer.
+ this.$scope.footerButtons = [
+ {'name': 'Save', 'css': 'blue', 'callback': this.$scope.save},
+ {'name': 'Cancel', 'css': 'grey', 'callback': this.$scope.close}
+ ];
+
+
+ this.$scope.getPrev = ():void=> {
+
+ let onSuccess = ():void => {
+ this.$scope.isLoading = false;
+ this.updatePropertyByIndex(--this.$scope.currentPropertyIndex);
+ };
+ let onFailed = ():void => {
+ this.$scope.isLoading = false;
+ };
+
+ if (!this.$scope.property.readonly) {
+ this.$scope.isLoading = true;
+ this.save(false).then(onSuccess, onFailed);
+
+ } else {
+ this.updatePropertyByIndex(--this.$scope.currentPropertyIndex);
+ }
+
+ };
+
+ this.$scope.getNext = ():void=> {
+
+ let onSuccess = ():void => {
+ this.$scope.isLoading = false;
+ this.updatePropertyByIndex(++this.$scope.currentPropertyIndex);
+ };
+ let onFailed = ():void => {
+ this.$scope.isLoading = false;
+ };
+
+ if (!this.$scope.property.readonly) {
+ this.$scope.isLoading = true;
+ this.save(false).then(onSuccess, onFailed);
+ } else {
+ this.updatePropertyByIndex(++this.$scope.currentPropertyIndex);
+ }
+
+ };
+
+
+ this.$scope.$watch("forms.editForm.$invalid", (newVal, oldVal) => {
+ this.$scope.footerButtons[0].disabled = this.$scope.forms.editForm.$invalid;
+ });
+
+
+ this.$scope.getValidationPattern = (type:string):RegExp => {
+ return this.validationUtils.getValidationPattern(type);
+ };
+ }
+}
diff --git a/catalog-ui/src/app/view-models/forms/property-forms/base-property-form/property-form-base-view.html b/catalog-ui/src/app/view-models/forms/property-forms/base-property-form/property-form-base-view.html
new file mode 100644
index 0000000000..7cb05bf4ca
--- /dev/null
+++ b/catalog-ui/src/app/view-models/forms/property-forms/base-property-form/property-form-base-view.html
@@ -0,0 +1,132 @@
+<sdc-modal modal="modalPropertyFormBase" type="classic" class="sdc-edit-property-container" buttons="footerButtons" header="{{isNew ? 'Add' : 'Update' }} Property" show-close-button="true" data-tests-id="sdc-edit-property-container">
+ <loader data-display="isLoading" relative="false" size="medium"></loader>
+ <div class="sdc-modal-top-bar" data-ng-if="!isNew">
+ <div class="sdc-modal-top-bar-buttons">
+ <span ng-click="delete(property)" data-ng-class="{'disabled' : isDeleteDisable}" class="sprite-new delete-btn" data-tests-id="delete_property" sdc-smart-tooltip="">Delete</span>
+ <span class="delimiter"></span>
+ <span data-ng-click="getPrev()" data-ng-class="{'disabled' : !currentPropertyIndex || forms.editForm.$invalid || isArrowsDisabled}" class="sprite-new left-arrow" data-tests-id="get-prev" sdc-smart-tooltip="">Previous</span>
+ <span data-ng-click="getNext()" data-ng-class="{'disabled' : isLastProperty || forms.editForm.$invalid || isArrowsDisabled}" class="sprite-new right-arrow" data-tests-id="get-next" sdc-smart-tooltip="">Next</span>
+ </div>
+ </div>
+
+ <div class="sdc-edit-property-form-container" >
+ <perfect-scrollbar scroll-y-margin-offset="0" include-padding="true" class="scrollbar-container">
+ <form class="w-sdc-form two-columns" name="forms.editForm" >
+
+ <div class="w-sdc-form-columns-wrapper">
+
+ <div class="w-sdc-form-column">
+
+ <!-- Name -->
+ <div class="i-sdc-form-item" data-ng-class="{error:(forms.editForm.propertyName.$dirty && forms.editForm.propertyName.$invalid)}">
+ <label class="i-sdc-form-label" ng-class="{'required': !isService}">Name</label>
+ <input class="i-sdc-form-input"
+ data-tests-id="propertyName"
+ data-ng-maxlength="50"
+ data-ng-disabled="isNameDisable"
+ maxlength="50"
+ data-ng-model="property.name"
+ type="text"
+ name="propertyName"
+ data-ng-pattern="propertyNameValidationPattern"
+ data-required
+ data-ng-model-options="{ debounce: 200 }"
+ autofocus />
+
+ <div class="input-error" data-ng-show="forms.editForm.propertyName.$dirty && forms.editForm.propertyName.$invalid">
+ <span ng-show="forms.editForm.propertyName.$error.required" translate="VALIDATION_ERROR_REQUIRED" translate-values="{'field': 'Property name' }"></span>
+ <span ng-show="forms.editForm.propertyName.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '50' }"></span>
+ <span ng-show="forms.editForm.propertyName.$error.pattern" translate="VALIDATION_ERROR_SPECIAL_CHARS_NOT_ALLOWED"></span>
+ </div>
+ </div>
+ </div>
+
+ <div class="w-sdc-form-column">
+ <div class="w-sdc-form-columns-wrapper">
+ <div class="w-sdc-form-column">
+ <!-- Type -->
+ <div class="i-sdc-form-item" data-ng-class="{error:(forms.editForm.type.$dirty && forms.editForm.type.$invalid)}">
+ <label class="i-sdc-form-label" ng-class="{'required': !isService}">Type</label>
+ <select class="i-sdc-form-select"
+ data-tests-id="propertyType"
+ data-required
+ data-ng-disabled="isTypeSelectorDisable"
+ name="type"
+ data-ng-change="onTypeChange()"
+ data-ng-model="property.type">
+ <option value="">Choose Type</option>
+ <option data-ng-repeat="type in types"
+ value="{{type}}">{{type}}</option>
+ <option data-ng-repeat="type in nonPrimitiveTypes"
+ value="{{type}}">{{type.replace("org.openecomp.datatypes.heat.","")}}</option>
+ </select>
+
+ <div class="input-error" data-ng-show="forms.editForm.type.$dirty && forms.editForm.type.$invalid">
+ <span ng-show="forms.editForm.type.$error.required" translate="VALIDATION_ERROR_REQUIRED" translate-values="{'field': 'Type' }"></span>
+ </div>
+ </div>
+ </div>
+ <div class="w-sdc-form-column" data-ng-if="showSchema()">
+ <!-- Entry Schema -->
+ <div class="i-sdc-form-item" data-ng-class="{error:(forms.editForm.schemaType.$dirty && forms.editForm.schemaType.$invalid)}">
+ <label class="i-sdc-form-label required">Entry Schema</label>
+ <select class="i-sdc-form-select"
+ data-required
+ data-tests-id="schema-type"
+ data-ng-disabled="isPropertyValueOwner() || property.readonly"
+ name="schemaType"
+ data-ng-change="onSchemaTypeChange()"
+ data-ng-model="property.schema.property.type">
+ <option value="">Choose Schema Type</option>
+ <option data-ng-repeat="type in simpleTypes"
+ value="{{type}}">{{type}}</option>
+ <option data-ng-repeat="type in nonPrimitiveTypes"
+ value="{{type}}">{{type.replace("org.openecomp.datatypes.heat.","")}}</option>
+ </select>
+
+ <div class="input-error" data-ng-show="forms.editForm.schemaType.$dirty && forms.editForm.schemaType.$invalid">
+ <span ng-show="forms.editForm.schemaType.$error.required" translate="VALIDATION_ERROR_REQUIRED" translate-values="{'field': 'Entry schema' }"></span>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <!-- Constraints by type -->
+ <div class="i-sdc-form-item" data-ng-if="false">
+ <label class="i-sdc-form-label required">Constraints by type</label>
+ <div>
+ Should be constraints by type(TBD)
+ </div>
+ </div>
+
+ </div>
+
+ </div>
+ <!-- Description -->
+ <div class="i-sdc-form-item" data-ng-class="{error:(forms.editForm.description.$dirty && forms.editForm.description.$invalid)}">
+ <label class="i-sdc-form-label">Description</label>
+ <textarea class="i-sdc-form-textarea"
+ data-ng-maxlength="256"
+ data-ng-disabled="isDescriptionDisable"
+ maxlength="256"
+ data-ng-pattern="commentValidationPattern"
+ name="description"
+ data-ng-model="property.description"
+ data-ng-model-options="{ debounce: 200 }"
+ data-tests-id="description"
+ ></textarea>
+
+ <div class="input-error" data-ng-show="forms.editForm.description.$dirty && forms.editForm.description.$invalid">
+ <span ng-show="forms.editForm.description.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '256' }"></span>
+ <span ng-show="forms.editForm.description.$error.pattern" translate="VALIDATION_ERROR_SPECIAL_CHARS_NOT_ALLOWED"></span>
+ <span ng-show="forms.editForm.description.$error.required" translate="VALIDATION_ERROR_REQUIRED" translate-values="{'field': 'Description' }"></span>
+ </div>
+ </div>
+ <!-- Default value - insert in dynamic template url -->
+ <ng-include src="innerViewSrcUrl"></ng-include>
+ <span class="w-sdc-form-note" data-ng-show="forms.editForm.$invalid && false" translate="LABEL_ALL_FIELDS_ARE_MANDATORY"></span>
+ </form>
+ </perfect-scrollbar>
+ </div>
+
+</sdc-modal>
diff --git a/catalog-ui/src/app/view-models/forms/property-forms/base-property-form/property-form-base.less b/catalog-ui/src/app/view-models/forms/property-forms/base-property-form/property-form-base.less
new file mode 100644
index 0000000000..15e30af4ee
--- /dev/null
+++ b/catalog-ui/src/app/view-models/forms/property-forms/base-property-form/property-form-base.less
@@ -0,0 +1,63 @@
+.sdc-edit-property-container {
+ .scrollbar-container{
+ height: 415px;
+ width: 830px;
+ .perfect-scrollbar;
+ }
+
+ form{
+ width: 813px;
+ [name="description"]{
+ min-height:50px;
+ }
+ }
+
+ .sdc-modal-top-bar{
+ height: 40px;
+ .sdc-modal-top-bar-buttons {
+ float: right;
+
+ > span:not(.delimiter){
+ vertical-align: middle;
+ .hand;
+
+ &.sprite-new {
+ text-indent: 100%;
+ }
+ &.disabled, &:hover.disabled {
+ pointer-events: none;
+ }
+ }
+
+ .delete-btn{
+ margin-right: 6px;
+ }
+
+ .left-arrow{
+ margin-right: 8px;
+ }
+
+ .delimiter {
+ height: 20px;
+ width: 1px;
+ background-color: #959595;
+ display: inline-block;
+ vertical-align: middle;
+ margin-right: 10px;
+ }
+ }
+ }
+
+ .w-sdc-form-note {
+ .h_9;
+ display: block;
+ position: relative;
+ top: 13px;
+ }
+
+ .default-value-section{
+ border-top: solid 1px @main_color_a;
+ padding-top: 15px;
+ margin-top: 15px;
+ }
+}
diff --git a/catalog-ui/src/app/view-models/forms/property-forms/component-property-form/property-form-view-model.ts b/catalog-ui/src/app/view-models/forms/property-forms/component-property-form/property-form-view-model.ts
new file mode 100644
index 0000000000..b3557b055f
--- /dev/null
+++ b/catalog-ui/src/app/view-models/forms/property-forms/component-property-form/property-form-view-model.ts
@@ -0,0 +1,322 @@
+'use strict';
+import {
+ PROPERTY_TYPES, ModalsHandler, ValidationUtils, PROPERTY_VALUE_CONSTRAINTS, FormState, PROPERTY_DATA} from "app/utils";
+import {DataTypesService} from "app/services";
+import {PropertyModel, DataTypesMap, Component} from "app/models";
+
+export interface IEditPropertyModel {
+ property:PropertyModel;
+ types:Array<string>;
+ simpleTypes:Array<string>;
+}
+
+interface IPropertyFormViewModelScope extends ng.IScope {
+ forms:any;
+ editForm:ng.IFormController;
+ footerButtons:Array<any>;
+ isNew:boolean;
+ isLoading:boolean;
+ isService:boolean;
+ validationPattern:RegExp;
+ propertyNameValidationPattern:RegExp;
+ commentValidationPattern:RegExp;
+ editPropertyModel:IEditPropertyModel;
+ modalInstanceProperty:ng.ui.bootstrap.IModalServiceInstance;
+ currentPropertyIndex:number;
+ isLastProperty:boolean;
+ myValue:any;
+ nonPrimitiveTypes:Array<string>;
+ dataTypes:DataTypesMap;
+ isTypeDataType:boolean;
+ maxLength:number;
+ isPropertyValueOwner:boolean;
+
+ validateJson(json:string):boolean;
+ save(doNotCloseModal?:boolean):void;
+ getValidationPattern(type:string):RegExp;
+ validateIntRange(value:string):boolean;
+ close():void;
+ onValueChange():void;
+ onSchemaTypeChange():void;
+ onTypeChange(resetSchema:boolean):void;
+ showSchema():boolean;
+ delete(property:PropertyModel):void;
+ getPrev():void;
+ getNext():void;
+ isSimpleType(typeName:string):boolean;
+ getDefaultValue():any;
+}
+
+export class PropertyFormViewModel {
+
+ static '$inject' = [
+ '$scope',
+ 'Sdc.Services.DataTypesService',
+ '$uibModalInstance',
+ 'property',
+ 'ValidationPattern',
+ 'PropertyNameValidationPattern',
+ 'CommentValidationPattern',
+ 'ValidationUtils',
+ 'component',
+ '$filter',
+ 'ModalsHandler',
+ 'filteredProperties',
+ '$timeout',
+ 'isPropertyValueOwner'
+ ];
+
+ private formState:FormState;
+
+ constructor(private $scope:IPropertyFormViewModelScope,
+ private DataTypesService:DataTypesService,
+ private $uibModalInstance:ng.ui.bootstrap.IModalServiceInstance,
+ private property:PropertyModel,
+ private ValidationPattern:RegExp,
+ private PropertyNameValidationPattern:RegExp,
+ private CommentValidationPattern:RegExp,
+ private ValidationUtils:ValidationUtils,
+ private component:Component,
+ private $filter:ng.IFilterService,
+ private ModalsHandler:ModalsHandler,
+ private filteredProperties:Array<PropertyModel>,
+ private $timeout:ng.ITimeoutService,
+ private isPropertyValueOwner:boolean) {
+
+ this.formState = angular.isDefined(property.name) ? FormState.UPDATE : FormState.CREATE;
+ this.initScope();
+ }
+
+ private initResource = ():void => {
+ this.$scope.editPropertyModel.property = new PropertyModel(this.property);
+ this.$scope.editPropertyModel.property.type = this.property.type ? this.property.type : null;
+ this.setMaxLength();
+ this.initAddOnLabels();
+ };
+
+ //init property add-ons labels that show up at the left side of the input.
+ private initAddOnLabels = () => {
+ if (this.$scope.editPropertyModel.property.name == 'network_role' && this.$scope.isService) {
+ //the server sends back the normalized name. Remove it (to prevent interference with validation) and set the addon label to the component name directly.
+ //Note: this cant be done in properties.ts because we dont have access to the component
+ if (this.$scope.editPropertyModel.property.value) {
+ let splitProp = this.$scope.editPropertyModel.property.value.split(new RegExp(this.component.normalizedName + '.', "gi"));
+ this.$scope.editPropertyModel.property.value = splitProp.pop();
+ }
+ this.$scope.editPropertyModel.property.addOn = this.component.name;
+ }
+ }
+
+ private initEditPropertyModel = ():void => {
+ this.$scope.editPropertyModel = {
+ property: null,
+ types: PROPERTY_DATA.TYPES,
+ simpleTypes: PROPERTY_DATA.SIMPLE_TYPES
+ };
+
+ this.initResource();
+ };
+
+ private initForNotSimpleType = ():void => {
+ let property = this.$scope.editPropertyModel.property;
+ this.$scope.isTypeDataType = this.DataTypesService.isDataTypeForPropertyType(this.$scope.editPropertyModel.property);
+ if (property.type && this.$scope.editPropertyModel.simpleTypes.indexOf(property.type) == -1) {
+ if (!(property.value || property.defaultValue)) {
+ switch (property.type) {
+ case PROPERTY_TYPES.MAP:
+ this.$scope.myValue = {'': null};
+ break;
+ case PROPERTY_TYPES.LIST:
+ this.$scope.myValue = [];
+ break;
+ default:
+ this.$scope.myValue = {};
+ }
+ } else {
+ this.$scope.myValue = JSON.parse(property.value || property.defaultValue);
+ }
+ }
+ };
+
+ private setMaxLength = ():void => {
+ switch (this.$scope.editPropertyModel.property.type) {
+ case PROPERTY_TYPES.MAP:
+ case PROPERTY_TYPES.LIST:
+ this.$scope.maxLength = this.$scope.editPropertyModel.property.schema.property.type == PROPERTY_TYPES.JSON ?
+ PROPERTY_VALUE_CONSTRAINTS.JSON_MAX_LENGTH :
+ PROPERTY_VALUE_CONSTRAINTS.MAX_LENGTH;
+ break;
+ case PROPERTY_TYPES.JSON:
+ this.$scope.maxLength = PROPERTY_VALUE_CONSTRAINTS.JSON_MAX_LENGTH;
+ break;
+ default:
+ this.$scope.maxLength =PROPERTY_VALUE_CONSTRAINTS.MAX_LENGTH;
+ }
+ };
+
+
+ private initScope = ():void => {
+
+ //scope properties
+ this.$scope.forms = {};
+ this.$scope.validationPattern = this.ValidationPattern;
+ this.$scope.propertyNameValidationPattern = this.PropertyNameValidationPattern;
+ this.$scope.commentValidationPattern = this.CommentValidationPattern;
+ this.$scope.isLoading = false;
+ this.$scope.isNew = (this.formState === FormState.CREATE);
+ this.$scope.isService = this.component.isService();
+ this.$scope.modalInstanceProperty = this.$uibModalInstance;
+ this.$scope.currentPropertyIndex = _.findIndex(this.filteredProperties, i=> i.name == this.property.name);
+ this.$scope.isLastProperty = this.$scope.currentPropertyIndex == (this.filteredProperties.length - 1);
+ this.$scope.dataTypes = this.DataTypesService.getAllDataTypes();
+ this.$scope.isPropertyValueOwner = this.isPropertyValueOwner;
+ this.initEditPropertyModel();
+
+ this.$scope.nonPrimitiveTypes = _.filter(Object.keys(this.$scope.dataTypes), (type:string)=> {
+ return this.$scope.editPropertyModel.types.indexOf(type) == -1;
+ });
+ this.initForNotSimpleType();
+
+
+ this.$scope.validateJson = (json:string):boolean => {
+ if (!json) {
+ return true;
+ }
+ return this.ValidationUtils.validateJson(json);
+ };
+
+
+ //scope methods
+ this.$scope.save = (doNotCloseModal?:boolean):void => {
+ let property:PropertyModel = this.$scope.editPropertyModel.property;
+ this.$scope.editPropertyModel.property.description = this.ValidationUtils.stripAndSanitize(this.$scope.editPropertyModel.property.description);
+ //if read only - or no changes made - just closes the modal
+ //need to check for property.value changes manually to detect if map properties deleted
+ if ((this.$scope.editPropertyModel.property.readonly && !this.$scope.isPropertyValueOwner)
+ || (!this.$scope.forms.editForm.$dirty && angular.equals(JSON.stringify(this.$scope.myValue), this.$scope.editPropertyModel.property.value))) {
+ this.$uibModalInstance.close();
+ return;
+ }
+
+ this.$scope.isLoading = true;
+
+ let onPropertyFaild = (response):void => {
+ console.info('onFaild', response);
+ this.$scope.isLoading = false;
+ };
+
+ let onPropertySuccess = (propertyFromBE:PropertyModel):void => {
+ console.info('onPropertyResourceSuccess : ', propertyFromBE);
+ this.$scope.isLoading = false;
+
+ if (!doNotCloseModal) {
+ this.$uibModalInstance.close();
+ } else {
+ this.$scope.forms.editForm.$setPristine();
+ this.$scope.editPropertyModel.property = new PropertyModel();
+ }
+ };
+
+ //in case we have uniqueId we call update method
+ if (this.$scope.isPropertyValueOwner) {
+ if (!this.$scope.editPropertyModel.property.simpleType && !this.$scope.isSimpleType(property.type)) {
+ let myValueString:string = JSON.stringify(this.$scope.myValue);
+ property.value = myValueString;
+ }
+ this.component.updateInstanceProperty(property).then(onPropertySuccess, onPropertyFaild);
+ } else {
+ if (!this.$scope.editPropertyModel.property.simpleType && !this.$scope.isSimpleType(property.type)) {
+ let myValueString:string = JSON.stringify(this.$scope.myValue);
+ property.defaultValue = myValueString;
+ } else {
+ this.$scope.editPropertyModel.property.defaultValue = this.$scope.editPropertyModel.property.value;
+ }
+ this.component.addOrUpdateProperty(property).then(onPropertySuccess, onPropertyFaild);
+ }
+ };
+
+ this.$scope.getPrev = ():void=> {
+ this.property = this.filteredProperties[--this.$scope.currentPropertyIndex];
+ this.initResource();
+ this.initForNotSimpleType();
+ this.$scope.isLastProperty = false;
+ };
+
+ this.$scope.getNext = ():void=> {
+ this.property = this.filteredProperties[++this.$scope.currentPropertyIndex];
+ this.initResource();
+ this.initForNotSimpleType();
+ this.$scope.isLastProperty = this.$scope.currentPropertyIndex == (this.filteredProperties.length - 1);
+ };
+
+ this.$scope.isSimpleType = (typeName:string):boolean=> {
+ return typeName && this.$scope.editPropertyModel.simpleTypes.indexOf(typeName) != -1;
+ };
+
+ this.$scope.showSchema = ():boolean => {
+ return [PROPERTY_TYPES.LIST, PROPERTY_TYPES.MAP].indexOf(this.$scope.editPropertyModel.property.type) > -1;
+ };
+
+ this.$scope.getValidationPattern = (type:string):RegExp => {
+ return this.ValidationUtils.getValidationPattern(type);
+ };
+
+ this.$scope.validateIntRange = (value:string):boolean => {
+ return !value || this.ValidationUtils.validateIntRange(value);
+ };
+
+ this.$scope.close = ():void => {
+ this.$uibModalInstance.close();
+ };
+
+ // put default value when instance value is empty
+ this.$scope.onValueChange = ():void => {
+ if (!this.$scope.editPropertyModel.property.value) {
+ if (this.$scope.isPropertyValueOwner) {
+ this.$scope.editPropertyModel.property.value = this.$scope.editPropertyModel.property.defaultValue;
+ }
+ }
+ };
+
+ // Add the done button at the footer.
+ this.$scope.footerButtons = [
+ {'name': 'Save', 'css': 'blue', 'callback': this.$scope.save},
+ {'name': 'Cancel', 'css': 'grey', 'callback': this.$scope.close}
+ ];
+
+ this.$scope.$watch("forms.editForm.$invalid", (newVal, oldVal) => {
+ this.$scope.footerButtons[0].disabled = this.$scope.forms.editForm.$invalid;
+ });
+
+ this.$scope.getDefaultValue = ():any => {
+ return this.$scope.isPropertyValueOwner ? this.$scope.editPropertyModel.property.defaultValue : null;
+ };
+
+ this.$scope.onTypeChange = ():void => {
+ this.$scope.editPropertyModel.property.value = '';
+ this.$scope.editPropertyModel.property.defaultValue = '';
+ this.setMaxLength();
+ this.initForNotSimpleType();
+ };
+
+ this.$scope.onSchemaTypeChange = ():void => {
+ if (this.$scope.editPropertyModel.property.type == PROPERTY_TYPES.MAP) {
+ this.$scope.myValue = {'': null};
+ } else if (this.$scope.editPropertyModel.property.type == PROPERTY_TYPES.LIST) {
+ this.$scope.myValue = [];
+ }
+ this.setMaxLength();
+ };
+
+ this.$scope.delete = (property:PropertyModel):void => {
+ let onOk = ():void => {
+ this.component.deleteProperty(property.uniqueId).then(
+ this.$scope.close
+ );
+ };
+ let title:string = this.$filter('translate')("PROPERTY_VIEW_DELETE_MODAL_TITLE");
+ let message:string = this.$filter('translate')("PROPERTY_VIEW_DELETE_MODAL_TEXT", "{'name': '" + property.name + "'}");
+ this.ModalsHandler.openConfirmationModal(title, message, false).then(onOk);
+ };
+ }
+}
diff --git a/catalog-ui/src/app/view-models/forms/property-forms/component-property-form/property-form-view.html b/catalog-ui/src/app/view-models/forms/property-forms/component-property-form/property-form-view.html
new file mode 100644
index 0000000000..f92d9a5ddc
--- /dev/null
+++ b/catalog-ui/src/app/view-models/forms/property-forms/component-property-form/property-form-view.html
@@ -0,0 +1,201 @@
+<sdc-modal modal="modalInstanceProperty" type="classic" class="sdc-edit-property-container" buttons="footerButtons" header="{{isNew ? 'Add' : 'Update' }} Property" show-close-button="true" data-tests-id="sdc-edit-property-container">
+ <loader data-display="isLoading" relative="false" size="medium"></loader>
+ <div class="sdc-modal-top-bar" data-ng-if="!isNew">
+ <div class="sdc-modal-top-bar-buttons">
+ <span ng-click="delete(editPropertyModel.property)" data-ng-class="{'disabled' : isPropertyValueOwner || editPropertyModel.property.readonly}" class="sprite-new delete-btn" data-tests-id="delete_property" sdc-smart-tooltip="">Delete</span>
+ <span class="delimiter"></span>
+ <span data-ng-click="getPrev()" data-ng-class="{'disabled' : !currentPropertyIndex }" class="sprite-new left-arrow" data-tests-id="get-prev" sdc-smart-tooltip="">Previous</span>
+ <span data-ng-click="getNext()" data-ng-class="{'disabled' : isLastProperty }" class="sprite-new right-arrow" data-tests-id="get-next" sdc-smart-tooltip="">Next</span>
+ </div>
+ </div>
+
+ <div class="sdc-edit-property-form-container" >
+ <perfect-scrollbar scroll-y-margin-offset="0" include-padding="true" class="scrollbar-container">
+ <form novalidate class="w-sdc-form two-columns" name="forms.editForm" >
+
+ <div class="w-sdc-form-columns-wrapper">
+
+ <div class="w-sdc-form-column">
+
+ <!-- Name -->
+ <div class="i-sdc-form-item" data-ng-class="{error:(forms.editForm.propertyName.$dirty && forms.editForm.propertyName.$invalid)}">
+ <label class="i-sdc-form-label" ng-class="{'required': !isService}">Name</label>
+ <input class="i-sdc-form-input"
+ data-tests-id="propertyName"
+ data-ng-maxlength="50"
+ data-ng-disabled="!isNew || editPropertyModel.property.readonly"
+ maxlength="50"
+ data-ng-model="editPropertyModel.property.name"
+ type="text"
+ name="propertyName"
+ data-ng-pattern="propertyNameValidationPattern"
+ data-required
+ data-ng-model-options="{ debounce: 200 }"
+ autofocus />
+
+ <div class="input-error" data-ng-show="forms.editForm.propertyName.$dirty && forms.editForm.propertyName.$invalid">
+ <span ng-show="forms.editForm.propertyName.$error.required" translate="VALIDATION_ERROR_REQUIRED" translate-values="{'field': 'Property name' }"></span>
+ <span ng-show="forms.editForm.propertyName.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '50' }"></span>
+ <span ng-show="forms.editForm.propertyName.$error.pattern" translate="VALIDATION_ERROR_SPECIAL_CHARS_NOT_ALLOWED"></span>
+ </div>
+ </div>
+ </div>
+
+ <div class="w-sdc-form-column">
+ <div class="w-sdc-form-columns-wrapper">
+ <div class="w-sdc-form-column">
+ <!-- Type -->
+ <div class="i-sdc-form-item" data-ng-class="{error:(forms.editForm.type.$dirty && forms.editForm.type.$invalid)}">
+ <label class="i-sdc-form-label" ng-class="{'required': !isService}">Type</label>
+ <select class="i-sdc-form-select"
+ data-tests-id="propertyType"
+ data-required
+ data-ng-disabled="isPropertyValueOwner || editPropertyModel.property.readonly"
+ name="type"
+ data-ng-change="onTypeChange()"
+ data-ng-model="editPropertyModel.property.type">
+ <option value="">Choose Type</option>
+ <option data-ng-repeat="type in editPropertyModel.types"
+ value="{{type}}">{{type}}</option>
+ <option data-ng-repeat="type in nonPrimitiveTypes"
+ value="{{type}}">{{type.replace("org.openecomp.datatypes.heat.","")}}</option>
+ </select>
+
+ <div class="input-error" data-ng-show="forms.editForm.type.$dirty && forms.editForm.type.$invalid">
+ <span ng-show="forms.editForm.type.$error.required" translate="VALIDATION_ERROR_REQUIRED" translate-values="{'field': 'Type' }"></span>
+ </div>
+ </div>
+ </div>
+ <div class="w-sdc-form-column" data-ng-if="showSchema()">
+ <!-- Entry Schema -->
+ <div class="i-sdc-form-item" data-ng-class="{error:(forms.editForm.schemaType.$dirty && forms.editForm.schemaType.$invalid)}">
+ <label class="i-sdc-form-label required">Entry Schema</label>
+ <select class="i-sdc-form-select"
+ data-required
+ data-tests-id="schema-type"
+ data-ng-disabled="isPropertyValueOwner || editPropertyModel.property.readonly"
+ name="schemaType"
+ data-ng-change="onSchemaTypeChange()"
+ data-ng-model="editPropertyModel.property.schema.property.type">
+ <option value="">Choose Schema Type</option>
+ <option data-ng-repeat="type in editPropertyModel.simpleTypes"
+ value="{{type}}">{{type}}</option>
+ <option data-ng-repeat="type in nonPrimitiveTypes"
+ value="{{type}}">{{type.replace("org.openecomp.datatypes.heat.","")}}</option>
+ </select>
+
+ <div class="input-error" data-ng-show="forms.editForm.schemaType.$dirty && forms.editForm.schemaType.$invalid">
+ <span ng-show="forms.editForm.schemaType.$error.required" translate="VALIDATION_ERROR_REQUIRED" translate-values="{'field': 'Entry schema' }"></span>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <!-- Constraints by type -->
+ <div class="i-sdc-form-item" data-ng-if="false">
+ <label class="i-sdc-form-label required">Constraints by type</label>
+ <div>
+ Should be constraints by type(TBD)
+ </div>
+ </div>
+
+ </div>
+
+ </div>
+ <!-- Description -->
+ <div class="i-sdc-form-item" data-ng-class="{error:(forms.editForm.description.$dirty && forms.editForm.description.$invalid)}">
+ <label class="i-sdc-form-label">Description</label>
+ <textarea class="i-sdc-form-textarea"
+ data-ng-maxlength="400"
+ data-ng-disabled="isPropertyValueOwner || editPropertyModel.property.readonly"
+ maxlength="400"
+ data-ng-pattern="commentValidationPattern"
+ name="description"
+ data-ng-model="editPropertyModel.property.description"
+ data-ng-model-options="{ debounce: 200 }"
+ data-tests-id="description"
+ ></textarea>
+
+ <div class="input-error" data-ng-show="forms.editForm.description.$dirty && forms.editForm.description.$invalid">
+ <span ng-show="forms.editForm.description.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '256' }"></span>
+ <span ng-show="forms.editForm.description.$error.pattern" translate="VALIDATION_ERROR_SPECIAL_CHARS_NOT_ALLOWED"></span>
+ <span ng-show="forms.editForm.description.$error.required" translate="VALIDATION_ERROR_REQUIRED" translate-values="{'field': 'Description' }"></span>
+ </div>
+ </div>
+ <!-- Default value -->
+
+ <div class="default-value-section i-sdc-form-item">
+ <label class="i-sdc-form-label">Default Value</label>
+ <div data-ng-if="isTypeDataType">
+ <fields-structure value-obj-ref="myValue"
+ type-name="editPropertyModel.property.type"
+ parent-form-obj="forms.editForm"
+ fields-prefix-name="currentPropertyIndex"
+ read-only="editPropertyModel.property.readonly && !isPropertyValueOwner"
+ default-value="{{getDefaultValue()}}"
+ expand-by-default="true"></fields-structure>
+
+ </div>
+ <div data-ng-if="!isTypeDataType" ng-switch="editPropertyModel.property.type">
+ <div ng-switch-when="map">
+ <type-map value-obj-ref="myValue"
+ schema-property="editPropertyModel.property.schema.property"
+ parent-form-obj="forms.editForm"
+ fields-prefix-name="currentPropertyIndex"
+ read-only="editPropertyModel.property.readonly && !isPropertyValueOwner"
+ default-value="{{getDefaultValue()}}"
+ max-length="maxLength"></type-map>
+ </div>
+ <div ng-switch-when="list">
+ <type-list value-obj-ref="myValue"
+ schema-property="editPropertyModel.property.schema.property"
+ parent-form-obj="forms.editForm"
+ fields-prefix-name="currentPropertyIndex"
+ read-only="editPropertyModel.property.readonly && !isPropertyValueOwner"
+ default-value="{{getDefaultValue()}}"
+ max-length="maxLength"></type-list>
+ </div>
+ <div ng-switch-default>
+ <div class="i-sdc-form-item" data-ng-class="{error:(forms.editForm.value.$dirty && forms.editForm.value.$invalid), 'input-group' : editPropertyModel.property.addOn}">
+ <span ng-if="editPropertyModel.property.addOn" class="input-group-addon">{{editPropertyModel.property.addOn}}</span>
+ <input class="i-sdc-form-input"
+ data-tests-id="defaultvalue"
+ ng-if="!((editPropertyModel.property.simpleType||editPropertyModel.property.type) == 'boolean')"
+ data-ng-maxlength="maxLength"
+ data-ng-disabled="editPropertyModel.property.readonly && !isPropertyValueOwner"
+ maxlength="{{maxLength}}"
+ data-ng-model="editPropertyModel.property.value"
+ type="text"
+ name="value"
+ data-ng-pattern="getValidationPattern((editPropertyModel.property.simpleType||editPropertyModel.property.type))"
+ data-ng-model-options="{ debounce: 200 }"
+ data-ng-change="('json'==editPropertyModel.property.type && forms.editForm.value.$setValidity('pattern', validateJson(editPropertyModel.property.value)))
+ ||(!forms.editForm.value.$error.pattern && ('integer'==editPropertyModel.property.type && forms.editForm.value.$setValidity('pattern', validateIntRange(editPropertyModel.property.value)) || onValueChange()))"
+ data-ng-change=""
+ autofocus />
+ <select class="i-sdc-form-select"
+ data-tests-id="booleantype"
+ ng-if="(editPropertyModel.property.simpleType||editPropertyModel.property.type) == 'boolean'"
+ data-ng-disabled="editPropertyModel.property.readonly && !isPropertyValueOwner"
+ name="value"
+ data-ng-change="onValueChange()"
+ data-ng-model="editPropertyModel.property.value">
+ <option value="true">true</option>
+ <option value="false">false</option>
+ </select>
+
+ <div class="input-error" data-ng-show="forms.editForm.value.$dirty && forms.editForm.value.$invalid">
+ <span ng-show="forms.editForm.value.$error.required" translate="VALIDATION_ERROR_REQUIRED" translate-values="{'field': 'Property' }"></span>
+ <span ng-show="forms.editForm.value.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '{{maxLength}}' }"></span>
+ <span ng-show="forms.editForm.value.$error.pattern" translate="PROPERTY_EDIT_PATTERN"></span>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ <span class="w-sdc-form-note" data-ng-show="forms.editForm.$invalid && false" translate="LABEL_ALL_FIELDS_ARE_MANDATORY"></span>
+ </form>
+ </perfect-scrollbar>
+ </div>
+
+</sdc-modal>
diff --git a/catalog-ui/src/app/view-models/forms/property-forms/module-property-modal/module-property-model.ts b/catalog-ui/src/app/view-models/forms/property-forms/module-property-modal/module-property-model.ts
new file mode 100644
index 0000000000..7359ac0e91
--- /dev/null
+++ b/catalog-ui/src/app/view-models/forms/property-forms/module-property-modal/module-property-model.ts
@@ -0,0 +1,206 @@
+/**
+ * Created by obarda on 1/18/2017.
+ */
+'use strict';
+import {PropertyModel, DisplayModule, Component, Resource, Service, ComponentInstance} from "app/models";
+import {UNIQUE_GROUP_PROPERTIES_NAME} from "app/utils";
+import {IPropertyFormBaseViewScope, PropertyFormBaseView} from "../base-property-form/property-form-base-model";
+import {DataTypesService} from "app/services/data-types-service";
+
+export interface IModulePropertyViewScope extends IPropertyFormBaseViewScope {
+ onValueChange():void;
+}
+
+export class ModulePropertyView extends PropertyFormBaseView {
+
+ static '$inject' = [
+ '$scope',
+ '$templateCache',
+ '$uibModalInstance',
+ '$injector',
+ 'originalProperty',
+ 'component',
+ 'selectedModule',
+ 'Sdc.Services.DataTypesService',
+ '$q'
+ ];
+
+ constructor(protected $scope:IModulePropertyViewScope,
+ protected $templateCache:ng.ITemplateCacheService,
+ protected $uibModalInstance:ng.ui.bootstrap.IModalServiceInstance,
+ protected $injector:ng.auto.IInjectorService,
+ protected originalProperty:PropertyModel,
+ protected component:Component,
+ private selectedModule:DisplayModule,
+ protected DataTypesService:DataTypesService,
+ private $q:ng.IQService) {
+ super($scope, $uibModalInstance, $injector, originalProperty, component, selectedModule.properties, DataTypesService);
+
+ this.$templateCache.put("module-property-view.html", require('app/view-models/forms/property-forms/module-property-modal/module-property-view.html'));
+ this.$scope.innerViewSrcUrl = "module-property-view.html";
+ this.initChildScope();
+ }
+
+ private findPropertyByName = (propertyName:string):PropertyModel => {
+ let property:PropertyModel = _.find(this.filteredProperties, (property:PropertyModel) => {
+ return property.name === propertyName;
+ });
+ return property;
+ };
+
+ save(isNeedToCloseModal):ng.IPromise<boolean> {
+
+ let deferred = this.$q.defer();
+
+ let onSuccess = (properties:Array<PropertyModel>):void => {
+ deferred.resolve(true);
+ if (isNeedToCloseModal === true) {
+ this.$scope.close();
+ }
+ };
+
+ let onFailed = ():void => {
+ deferred.resolve(false);
+ };
+
+ let property = _.find(this.selectedModule.properties, (property) => {
+ return property.uniqueId === this.$scope.property.uniqueId;
+ });
+ if (property.value !== this.$scope.property.value) {
+ if (this.component.isResource()) {
+ (<Resource>this.component).updateResourceGroupProperties(this.selectedModule, [this.$scope.property]).then(onSuccess, onFailed); // for now we only update one property at a time
+ }
+ if (this.component.isService()) {
+ // Find the component instance of the group instance
+ let componentInstance:ComponentInstance = _.find(this.component.componentInstances, (componentInstance:ComponentInstance) => {
+ let groupInstance = _.find(componentInstance.groupInstances, {uniqueId: this.selectedModule.groupInstanceUniqueId});
+ return groupInstance !== undefined;
+
+ });
+ (<Service>this.component).updateGroupInstanceProperties(componentInstance.uniqueId, this.selectedModule, [this.$scope.property]).then(onSuccess, onFailed); // for now we only update one property at a time
+ }
+ } else {
+ deferred.resolve(true);
+ }
+ return deferred.promise;
+ }
+
+ onPropertyChange():void {
+ this.initValidation();
+ }
+
+ protected initValidation = ():void => {
+
+ this.$scope.isDeleteDisable = true;
+ this.$scope.isNameDisable = true;
+ this.$scope.isTypeSelectorDisable = true;
+ this.$scope.isDescriptionDisable = true;
+
+ switch (this.$scope.property.name) {
+ case UNIQUE_GROUP_PROPERTIES_NAME.IS_BASE:
+ case UNIQUE_GROUP_PROPERTIES_NAME.VF_MODULE_TYPE:
+ case UNIQUE_GROUP_PROPERTIES_NAME.VOLUME_GROUP:
+ case UNIQUE_GROUP_PROPERTIES_NAME.VF_MODULE_LABEL:
+ this.$scope.property.readonly = true;
+ break;
+ case UNIQUE_GROUP_PROPERTIES_NAME.VF_MODULE_DESCRIPTION:
+ if (this.component.isService()) {
+ this.$scope.property.readonly = true;
+ } else {
+ this.$scope.property.readonly = false;
+ }
+ break;
+ }
+ };
+
+ private isUniqueProperty = ():boolean => {
+ return this.$scope.property.name === UNIQUE_GROUP_PROPERTIES_NAME.MIN_VF_MODULE_INSTANCES ||
+ this.$scope.property.name === UNIQUE_GROUP_PROPERTIES_NAME.MAX_VF_MODULE_INSTANCES ||
+ this.$scope.property.name === UNIQUE_GROUP_PROPERTIES_NAME.INITIAL_COUNT;
+ };
+
+
+ private initChildScope = ():void => {
+
+ this.initValidation();
+
+ // put default value when instance value is empty
+ this.$scope.onValueChange = ():void => {
+
+ if (!this.$scope.property.value) { // Resetting to default value
+ if (this.isPropertyValueOwner()) {
+ if (this.component.isService()) {
+ this.$scope.property.value = this.$scope.property.parentValue;
+ } else {
+ this.$scope.property.value = this.$scope.property.defaultValue;
+ }
+ }
+ }
+
+ if (this.isUniqueProperty()) {
+
+ let isValid = true;
+ let maxProperty:PropertyModel = this.findPropertyByName(UNIQUE_GROUP_PROPERTIES_NAME.MAX_VF_MODULE_INSTANCES);
+ let minProperty:PropertyModel = this.findPropertyByName(UNIQUE_GROUP_PROPERTIES_NAME.MIN_VF_MODULE_INSTANCES);
+ let initialCountProperty:PropertyModel = this.findPropertyByName(UNIQUE_GROUP_PROPERTIES_NAME.INITIAL_COUNT);
+
+ let maxPropertyValue = parseInt(maxProperty.value);
+ let minPropertyValue = parseInt(minProperty.value);
+ let initialCountPropertyValue = parseInt(initialCountProperty.value);
+ let propertyValue = parseInt(this.$scope.property.value);
+ let parentPropertyValue = parseInt(this.$scope.property.parentValue);
+
+ switch (this.$scope.property.name) {
+
+ case UNIQUE_GROUP_PROPERTIES_NAME.MIN_VF_MODULE_INSTANCES:
+ if (!maxPropertyValue || maxPropertyValue === null) {
+ isValid = propertyValue <= initialCountPropertyValue;
+ }
+ else {
+ isValid = propertyValue && (propertyValue <= maxPropertyValue && propertyValue <= initialCountPropertyValue);
+ }
+ this.$scope.forms.editForm["value"].$setValidity('maxValidation', isValid);
+ if (this.component.isService()) {
+ if (!parentPropertyValue || parentPropertyValue === null) {
+ isValid = true;
+ } else {
+ isValid = propertyValue >= parentPropertyValue;
+ this.$scope.forms.editForm["value"].$setValidity('minValidationVfLevel', isValid);
+ }
+ }
+ break;
+ case UNIQUE_GROUP_PROPERTIES_NAME.MAX_VF_MODULE_INSTANCES:
+ if (!minPropertyValue || minPropertyValue === null) {
+ isValid = propertyValue >= initialCountPropertyValue;
+ } else {
+ isValid = !propertyValue || (propertyValue >= minPropertyValue && propertyValue >= initialCountPropertyValue);
+ }
+ this.$scope.forms.editForm["value"].$setValidity('minValidation', isValid);
+ if (this.component.isService()) {
+ if (!parentPropertyValue || parentPropertyValue === null) {
+ isValid = true;
+ }
+ else {
+ isValid = propertyValue <= parentPropertyValue;
+ this.$scope.forms.editForm["value"].$setValidity('maxValidationVfLevel', isValid);
+ }
+ }
+ break;
+ case UNIQUE_GROUP_PROPERTIES_NAME.INITIAL_COUNT:
+ if ((!minPropertyValue || minPropertyValue === null) && (!maxPropertyValue || maxPropertyValue === null)) {
+ isValid = true;
+ } else if (!minPropertyValue || minPropertyValue === null) {
+ isValid = propertyValue <= maxPropertyValue;
+ } else if (!maxPropertyValue || maxPropertyValue === null) {
+ isValid = propertyValue >= minPropertyValue;
+ } else {
+ isValid = minPropertyValue <= propertyValue && propertyValue <= maxPropertyValue;
+ }
+ this.$scope.forms.editForm["value"].$setValidity('minOrMaxValidation', isValid);
+ break;
+ }
+ }
+ ;
+ }
+ }
+}
diff --git a/catalog-ui/src/app/view-models/forms/property-forms/module-property-modal/module-property-view.html b/catalog-ui/src/app/view-models/forms/property-forms/module-property-modal/module-property-view.html
new file mode 100644
index 0000000000..175f4c199b
--- /dev/null
+++ b/catalog-ui/src/app/view-models/forms/property-forms/module-property-modal/module-property-view.html
@@ -0,0 +1,41 @@
+<div class="default-value-section i-sdc-form-item">
+ <label class="i-sdc-form-label">Default Value</label>
+ <div class="i-sdc-form-item" data-ng-class="{error:(forms.editForm.value.$dirty && forms.editForm.value.$invalid)}">
+ <input class="i-sdc-form-input"
+ data-tests-id="defaultvalue"
+ ng-if="!((property.simpleType||property.type) == 'boolean')"
+ data-ng-maxlength="maxLength"
+ data-ng-disabled="property.readonly && !isPropertyValueOwner()"
+ maxlength="100"
+ data-ng-model="property.value"
+ type="text"
+ name="value"
+ data-ng-pattern="getValidationPattern(property.type)"
+ data-ng-model-options="{ debounce: 200 }"
+ data-ng-change="onValueChange()"
+ />
+ <select class="i-sdc-form-select"
+ data-tests-id="booleantype"
+ ng-if="(property.simpleType||property.type) == 'boolean'"
+ data-ng-disabled="property.readonly && !isPropertyValueOwner()"
+ name="value"
+ data-ng-model="property.value">
+ <option value="true">true</option>
+ <option value="false">false</option>
+ </select>
+
+ <div class="input-error" data-ng-show="forms.editForm.value.$dirty && forms.editForm.value.$invalid">
+ <span ng-show="forms.editForm.value.$error.required" translate="VALIDATION_ERROR_REQUIRED"
+ translate-values="{'field': 'Property' }"></span>
+ <span ng-show="forms.editForm.value.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH"
+ translate-values="{'max': '{{maxLength}}' }"></span>
+ <span ng-show="forms.editForm.value.$error.pattern" translate="PROPERTY_EDIT_PATTERN"></span>
+ <span ng-show="forms.editForm.value.$error.minValidation" translate="MIN_VALIDATION_ERROR"></span>
+ <span ng-show="forms.editForm.value.$error.maxValidation" translate="MAX_VALIDATION_ERROR"></span>
+ <span ng-show="forms.editForm.value.$error.minOrMaxValidation" translate="MIN_MAX_VALIDATION"></span>
+ <span ng-show="forms.editForm.value.$error.minValidationVfLevel" translate="MIN_VALIDATION_VF_LEVE_ERROR"></span>
+ <span ng-show="forms.editForm.value.$error.maxValidationVfLevel" translate="MAX_VALIDATION_VF_LEVE_ERROR"></span>
+
+ </div>
+ </div>
+</div>
diff --git a/catalog-ui/src/app/view-models/forms/property-forms/select-datatype-modal/select-datatype-modal-view-model.ts b/catalog-ui/src/app/view-models/forms/property-forms/select-datatype-modal/select-datatype-modal-view-model.ts
new file mode 100644
index 0000000000..48aa47fdd0
--- /dev/null
+++ b/catalog-ui/src/app/view-models/forms/property-forms/select-datatype-modal/select-datatype-modal-view-model.ts
@@ -0,0 +1,97 @@
+'use strict';
+import {DataTypesService} from "app/services/data-types-service";
+import {PropertyModel, InputPropertyBase, Component} from "app/models";
+import {IPropertyFormBaseViewScope, PropertyFormBaseView} from "../base-property-form/property-form-base-model";
+import {PROPERTY_TYPES} from "app/utils/constants";
+
+interface ISelectDataTypeViewModelScope extends IPropertyFormBaseViewScope {
+ selectedPropertiesName:string;
+ dataTypesService:DataTypesService;
+ path:string;
+ isTypeDataType:boolean;
+ myValue:any;
+ isReadOnly:boolean;
+}
+
+export class SelectDataTypeViewModel extends PropertyFormBaseView {
+
+ static '$inject' = [
+ '$scope',
+ '$templateCache',
+ '$uibModalInstance',
+ '$injector',
+ 'originalProperty',
+ 'component',
+ 'filteredProperties',
+ 'Sdc.Services.DataTypesService',
+ 'propertiesMap',
+ '$q'
+ ];
+
+ constructor(protected $scope:ISelectDataTypeViewModelScope,
+ protected $templateCache:ng.ITemplateCacheService,
+ protected $uibModalInstance:ng.ui.bootstrap.IModalServiceInstance,
+ protected $injector:ng.auto.IInjectorService,
+ protected originalProperty:PropertyModel,
+ protected component:Component,
+ protected filteredProperties:Array<PropertyModel>,
+ protected DataTypesService:DataTypesService,
+ private propertiesMap:Array<InputPropertyBase>,
+ private $q:ng.IQService) {
+ super($scope, $uibModalInstance, $injector, originalProperty, component, filteredProperties, DataTypesService);
+
+ this.$templateCache.put("select-datatype-modal-view.html", require('app/view-models/forms/property-forms/select-datatype-modal/select-datatype-modal-view.html'));
+ this.$scope.innerViewSrcUrl = "select-datatype-modal-view.html";
+ this.initChildScope();
+ }
+
+ //scope methods
+ save(isNeedToCloseModal):ng.IPromise<boolean> {
+ let deferred = this.$q.defer();
+ this.$scope.property.propertiesName = this.DataTypesService.selectedPropertiesName;
+ this.$scope.property.input = this.DataTypesService.selectedInput;
+ this.$scope.property.isAlreadySelected = true;
+ this.$uibModalInstance.close(this.$scope.property);
+ deferred.resolve(true);
+ return deferred.promise;
+ };
+
+ private initForNotSimpleType = ():void => {
+ let property = this.$scope.property;
+ this.$scope.isTypeDataType = this.DataTypesService.isDataTypeForPropertyType(this.$scope.property);
+ if (property.type && this.$scope.simpleTypes.indexOf(property.type) == -1) {
+ if (!(property.value || property.defaultValue)) {
+ switch (property.type) {
+ case PROPERTY_TYPES.MAP:
+ this.$scope.myValue = {'': null};
+ break;
+ case PROPERTY_TYPES.LIST:
+ this.$scope.myValue = [];
+ break;
+ default:
+ this.$scope.myValue = {};
+ }
+ } else {
+ this.$scope.myValue = JSON.parse(property.value || property.defaultValue);
+ }
+ }
+ };
+
+ //remove selection property on the modal
+ private removeSelected = ():void => {
+ this.DataTypesService.selectedPropertiesName = null;
+ this.DataTypesService.selectedInput = null;
+ };
+
+ private initChildScope = ():void => {
+ //scope properties
+ this.$scope.forms = {};
+ this.$scope.path = this.$scope.property.name;
+ this.$scope.isArrowsDisabled = true;
+ this.DataTypesService.alreadySelectedProperties = this.propertiesMap;
+ this.$scope.dataTypesService = this.DataTypesService;
+ this.$scope.isReadOnly = true;
+ this.initForNotSimpleType();
+ this.removeSelected();
+ }
+}
diff --git a/catalog-ui/src/app/view-models/forms/property-forms/select-datatype-modal/select-datatype-modal-view.html b/catalog-ui/src/app/view-models/forms/property-forms/select-datatype-modal/select-datatype-modal-view.html
new file mode 100644
index 0000000000..acb0f292ff
--- /dev/null
+++ b/catalog-ui/src/app/view-models/forms/property-forms/select-datatype-modal/select-datatype-modal-view.html
@@ -0,0 +1,74 @@
+<!--<div>selectedPropertiesName - {{dataTypesService.selectedPropertiesName}}</div>-->
+<!--<div>selectedInput - {{dataTypesService.selectedInput}}</div>-->
+
+<div data-ng-if="dataTypes" class="default-value-section i-sdc-form-item">
+ <label class="i-sdc-form-label">Default Value</label>
+ <div data-ng-if="isTypeDataType">
+ <select-fields-structure value-obj-ref="myValue"
+ type-name="property.type"
+ parent-form-obj="forms.editForm"
+ fields-prefix-name="currentPropertyIndex"
+ read-only="true"
+ default-value="{{getDefaultValue()}}"
+ path="{{property.name}}"
+ is-parent-already-input="false"
+ expand-by-default="true"></select-fields-structure>
+
+ </div>
+ <div data-ng-if="!isTypeDataType" ng-switch="property.type">
+ <div ng-switch-when="map">
+
+ <select-type-map value-obj-ref="myValue"
+ schema-property="property.schema.property"
+ parent-form-obj="forms.editForm"
+ fields-prefix-name="currentPropertyIndex"
+ read-only="true"
+ default-value="{{getDefaultValue()}}"
+ max-length="maxLength"></select-type-map>
+ </div>
+ <div ng-switch-when="list">
+ <select-type-list value-obj-ref="myValue"
+ schema-property="property.schema.property"
+ parent-form-obj="forms.editForm"
+ fields-prefix-name="currentPropertyIndex"
+ read-only="true"
+ default-value="{{getDefaultValue()}}"
+ max-length="maxLength"></select-type-list>
+ </div>
+ <div ng-switch-default>
+ <div class="i-sdc-form-item" data-ng-class="{error:(forms.editForm.value.$dirty && forms.editForm.value.$invalid)}">
+ <input class="i-sdc-form-input"
+ data-tests-id="defaultvalue"
+ ng-if="!((property.simpleType||property.type) == 'boolean')"
+ data-ng-maxlength="maxLength"
+ data-ng-disabled="isReadOnly"
+ maxlength="{{maxLength}}"
+ data-ng-model="property.value"
+ type="text"
+ name="value"
+ data-ng-pattern="getValidationPattern((property.simpleType||property.type))"
+ data-ng-model-options="{ debounce: 200 }"
+ data-ng-change="('json'==property.type && forms.editForm.value.$setValidity('pattern', validateJson(property.value)))
+ ||(!forms.editForm.value.$error.pattern && ('integer'==property.type && forms.editForm.value.$setValidity('pattern', validateIntRange(property.value)) || onValueChange()))"
+ data-ng-change=""
+ autofocus />
+ <select class="i-sdc-form-select"
+ data-tests-id="booleantype"
+ ng-if="(property.simpleType||property.type) == 'boolean'"
+ data-ng-disabled="isReadOnly"
+ name="value"
+ data-ng-change="onValueChange()"
+ data-ng-model="property.value">
+ <option value="true">true</option>
+ <option value="false">false</option>
+ </select>
+
+ <div class="input-error" data-ng-show="forms.editForm.value.$dirty && forms.editForm.value.$invalid">
+ <span ng-show="forms.editForm.value.$error.required" translate="VALIDATION_ERROR_REQUIRED" translate-values="{'field': 'Property' }"></span>
+ <span ng-show="forms.editForm.value.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '{{maxLength}}' }"></span>
+ <span ng-show="forms.editForm.value.$error.pattern" translate="PROPERTY_EDIT_PATTERN"></span>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
diff --git a/catalog-ui/src/app/view-models/forms/property-forms/select-datatype-modal/select-datatype-modal.less b/catalog-ui/src/app/view-models/forms/property-forms/select-datatype-modal/select-datatype-modal.less
new file mode 100644
index 0000000000..15e30af4ee
--- /dev/null
+++ b/catalog-ui/src/app/view-models/forms/property-forms/select-datatype-modal/select-datatype-modal.less
@@ -0,0 +1,63 @@
+.sdc-edit-property-container {
+ .scrollbar-container{
+ height: 415px;
+ width: 830px;
+ .perfect-scrollbar;
+ }
+
+ form{
+ width: 813px;
+ [name="description"]{
+ min-height:50px;
+ }
+ }
+
+ .sdc-modal-top-bar{
+ height: 40px;
+ .sdc-modal-top-bar-buttons {
+ float: right;
+
+ > span:not(.delimiter){
+ vertical-align: middle;
+ .hand;
+
+ &.sprite-new {
+ text-indent: 100%;
+ }
+ &.disabled, &:hover.disabled {
+ pointer-events: none;
+ }
+ }
+
+ .delete-btn{
+ margin-right: 6px;
+ }
+
+ .left-arrow{
+ margin-right: 8px;
+ }
+
+ .delimiter {
+ height: 20px;
+ width: 1px;
+ background-color: #959595;
+ display: inline-block;
+ vertical-align: middle;
+ margin-right: 10px;
+ }
+ }
+ }
+
+ .w-sdc-form-note {
+ .h_9;
+ display: block;
+ position: relative;
+ top: 13px;
+ }
+
+ .default-value-section{
+ border-top: solid 1px @main_color_a;
+ padding-top: 15px;
+ margin-top: 15px;
+ }
+}
diff --git a/catalog-ui/src/app/view-models/forms/resource-instance-name-form/resource-instance-name-model.ts b/catalog-ui/src/app/view-models/forms/resource-instance-name-form/resource-instance-name-model.ts
new file mode 100644
index 0000000000..869e3db584
--- /dev/null
+++ b/catalog-ui/src/app/view-models/forms/resource-instance-name-form/resource-instance-name-model.ts
@@ -0,0 +1,95 @@
+'use strict';
+import {ComponentInstanceFactory} from "app/utils";
+import {ComponentInstance} from "app/models";
+import {Requirement, Component, Capability} from "app/models";
+
+interface IResourceInstanceViewModelScope extends ng.IScope {
+
+ componentInstanceModel:ComponentInstance;
+ validationPattern:RegExp;
+ oldName:string;
+ isAlreadyPressed:boolean;
+ footerButtons:Array<any>;
+ forms:any;
+ modalInstanceName:ng.ui.bootstrap.IModalServiceInstance;
+
+ save():void;
+ close():void;
+}
+
+export class ResourceInstanceNameViewModel {
+
+ static '$inject' = [
+ '$scope',
+ 'ValidationPattern',
+ '$uibModalInstance',
+ 'component'
+ ];
+
+
+ constructor(private $scope:IResourceInstanceViewModelScope,
+ private ValidationPattern:RegExp,
+ private $uibModalInstance:ng.ui.bootstrap.IModalServiceInstance,
+ private component:Component) {
+
+ this.initScope();
+ }
+
+
+ private initScope = ():void => {
+ this.$scope.forms = {};
+ this.$scope.validationPattern = this.ValidationPattern;
+ this.$scope.componentInstanceModel = ComponentInstanceFactory.createComponentInstance(this.component.selectedInstance);
+ this.$scope.oldName = this.component.selectedInstance.name;
+ this.$scope.modalInstanceName = this.$uibModalInstance;
+
+ this.$scope.isAlreadyPressed = false;
+
+
+ this.$scope.close = ():void => {
+ this.$uibModalInstance.dismiss();
+ };
+
+ this.$scope.save = ():void => {
+
+ let onFailed = () => {
+ this.$scope.isAlreadyPressed = true;
+ };
+
+ let onSuccess = (componentInstance:ComponentInstance) => {
+ this.$uibModalInstance.close();
+ this.$scope.isAlreadyPressed = false;
+ this.$scope.componentInstanceModel = componentInstance;
+ //this.component.name = componentInstance.name;//DE219124
+ this.component.selectedInstance.name = componentInstance.name;
+ //update requirements and capabilities owner name
+ _.forEach(this.component.selectedInstance.requirements, (requirementsArray:Array<Requirement>) => {
+ _.forEach(requirementsArray, (requirement:Requirement):void => {
+ requirement.ownerName = componentInstance.name;
+ });
+ });
+
+ _.forEach(this.component.selectedInstance.capabilities, (capabilitiesArray:Array<Capability>) => {
+ _.forEach(capabilitiesArray, (capability:Capability):void => {
+ capability.ownerName = componentInstance.name;
+ });
+ });
+
+ };
+
+ this.$scope.isAlreadyPressed = true;
+ if (this.$scope.oldName != this.$scope.componentInstanceModel.name) {
+ this.component.updateComponentInstance(this.$scope.componentInstanceModel).then(onSuccess, onFailed);
+ }
+ };
+
+ this.$scope.footerButtons = [
+ {'name': 'OK', 'css': 'blue', 'callback': this.$scope.save},
+ {'name': 'Cancel', 'css': 'grey', 'callback': this.$scope.close}
+ ];
+
+ this.$scope.$watch("[forms.editNameForm.$invalid,componentInstanceModel.name,isAlreadyPressed]", (newVal, oldVal) => {
+ this.$scope.footerButtons[0].disabled = this.$scope.forms.editNameForm.$invalid || this.$scope.isAlreadyPressed || this.$scope.componentInstanceModel.name === this.$scope.oldName;
+ });
+ }
+}
diff --git a/catalog-ui/src/app/view-models/forms/resource-instance-name-form/resource-instance-name-view.html b/catalog-ui/src/app/view-models/forms/resource-instance-name-form/resource-instance-name-view.html
new file mode 100644
index 0000000000..e04343adbd
--- /dev/null
+++ b/catalog-ui/src/app/view-models/forms/resource-instance-name-form/resource-instance-name-view.html
@@ -0,0 +1,72 @@
+<sdc-modal modal="modalInstanceName" type="classic" class="w-sdc-modal-resource-instance-name modal-type-confirmation" buttons="footerButtons" header="Instance Name" show-close-button="true">
+
+ <form novalidate class="w-sdc-form" name="forms.editNameForm">
+ <div class="i-sdc-form-item" data-ng-class="{error:(editNameForm.componentInstanceName.$dirty && editNameForm.resourceInstanceName.$invalid)}">
+ <label class="i-sdc-form-label required">Instance Name</label>
+ <input class="w-sdc-modal-resource-instance-input i-sdc-form-input"
+ name="componentInstanceName"
+ data-ng-maxlength="50"
+ data-ng-model="componentInstanceModel.name"
+ type="text"
+ data-required
+ data-ng-pattern="validationPattern"
+ maxlength="50"
+ autofocus
+ placeholder="Enter instance name..."
+ data-tests-id="instanceName"
+ />
+
+ <div class="input-error" data-ng-show="forms.editNameForm.componentInstanceName.$dirty && forms.editNameForm.componentInstanceName.$invalid">
+ <span ng-show="forms.editNameForm.componentInstanceName.$error.required" translate="VALIDATION_ERROR_REQUIRED" translate-values="{'field': 'Resource name' }"></span>
+ <span ng-show="forms.editNameForm.componentInstanceName.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '50' }"></span>
+ <span ng-show="forms.editNameForm.componentInstanceName.$error.pattern" translate="VALIDATION_ERROR_SPECIAL_CHARS_NOT_ALLOWED"></span>
+ </div>
+
+ </div>
+ </form>
+
+</sdc-modal>
+
+
+
+<!--
+
+<div class="w-sdc-modal w-sdc-modal-resource-instance-name">
+ <header>
+ <div class="w-sdc-modal-head">
+ Instance Name
+ </div>
+ </header>
+ <div>
+ <form novalidate class="w-sdc-modal-body w-sdc-form" name="editNameForm">
+ <div class="i-sdc-form-item" data-ng-class="{error:(editNameForm.componentInstanceName.$dirty && editNameForm.resourceInstanceName.$invalid)}">
+ <label class="i-sdc-form-label required">Instance Name</label>
+ <input class="w-sdc-modal-resource-instance-input i-sdc-form-input"
+ name="componentInstanceName"
+ data-ng-maxlength="50"
+ data-ng-model="componentInstanceModel.name"
+ type="text"
+ data-required
+ data-ng-pattern="validationPattern"
+ maxlength="50"
+ autofocus
+ placeholder="Enter instance name..."
+ data-tests-id="instanceName"
+ />
+
+ <div class="input-error" data-ng-show="editNameForm.componentInstanceName.$dirty && editNameForm.componentInstanceName.$invalid">
+ <span ng-show="editNameForm.componentInstanceName.$error.required" translate="VALIDATION_ERROR_REQUIRED" translate-values="{'field': 'Resource name' }"></span>
+ <span ng-show="editNameForm.componentInstanceName.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '50' }"></span>
+ <span ng-show="editNameForm.componentInstanceName.$error.pattern" translate="VALIDATION_ERROR_SPECIAL_CHARS_NOT_ALLOWED"></span>
+ </div>
+
+ </div>
+ </form>
+
+ <div class="w-sdc-modal-action">
+ <button class="w-sdc-btn-blue" data-ng-click="save()" type="button" data-ng-disabled="(!componentInstanceModel.name || componentInstanceModel.name === oldName) || isAlreadyPressed">Ok</button>
+ <button class="w-sdc-btn-dark-gray" data-ng-click="close()" type="button">Cancel</button>
+ </div>
+ </div>
+</div>
+-->
diff --git a/catalog-ui/src/app/view-models/forms/resource-instance-name-form/resource-instance-name.less b/catalog-ui/src/app/view-models/forms/resource-instance-name-form/resource-instance-name.less
new file mode 100644
index 0000000000..57698bef17
--- /dev/null
+++ b/catalog-ui/src/app/view-models/forms/resource-instance-name-form/resource-instance-name.less
@@ -0,0 +1,29 @@
+.w-sdc-modal-resource-instance-name {
+
+ .w-sdc-modal-body {
+ overflow: visible;
+ }
+
+ .w-sdc-modal-action {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ }
+
+ .w-sdc-modal-resource-instance-input {
+ .p_1;
+ border: solid 1px @color_p;
+ height: 45px;
+ padding: 0 20px;
+ margin: 0 auto 0 auto;
+ display: block;
+ }
+ .w-sdc-modal-body {
+ border-bottom: none;
+ }
+
+ .w-sdc-form .i-sdc-form-item.error::after {
+ top: 13px;
+ }
+
+}