Repository: kylin Updated Branches: refs/heads/master 2814ce774 -> c31c8490b
KYLIN-2180 Add project config Signed-off-by: Yang Li <liy...@apache.org> Project: http://git-wip-us.apache.org/repos/asf/kylin/repo Commit: http://git-wip-us.apache.org/repos/asf/kylin/commit/ea60803e Tree: http://git-wip-us.apache.org/repos/asf/kylin/tree/ea60803e Diff: http://git-wip-us.apache.org/repos/asf/kylin/diff/ea60803e Branch: refs/heads/master Commit: ea60803e20ac17489f513bf6af29b183829db020 Parents: 2814ce7 Author: kangkaisen <kangkai...@live.com> Authored: Fri Nov 11 16:21:32 2016 +0800 Committer: Yang Li <liy...@apache.org> Committed: Thu Dec 8 21:09:11 2016 +0800 ---------------------------------------------------------------------- .../apache/kylin/common/KylinConfigTest.java | 13 ++- .../org/apache/kylin/cube/model/CubeDesc.java | 11 ++- .../kylin/cube/ProjectSpecificConfigTest.java | 60 +++++++++++++ .../kylin/metadata/project/ProjectInstance.java | 25 +++++- .../kylin/metadata/project/ProjectManager.java | 13 +-- .../localmeta/project/default.json | 10 ++- .../rest/request/CreateProjectRequest.java | 11 +++ .../rest/request/UpdateProjectRequest.java | 11 +++ .../kylin/rest/service/ProjectService.java | 9 +- webapp/app/js/controllers/page.js | 56 ++++++++++-- .../app/partials/projects/project_create.html | 93 ++++++++++++++------ 11 files changed, 255 insertions(+), 57 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/kylin/blob/ea60803e/core-common/src/test/java/org/apache/kylin/common/KylinConfigTest.java ---------------------------------------------------------------------- diff --git a/core-common/src/test/java/org/apache/kylin/common/KylinConfigTest.java b/core-common/src/test/java/org/apache/kylin/common/KylinConfigTest.java index d05f58b..209986f 100644 --- a/core-common/src/test/java/org/apache/kylin/common/KylinConfigTest.java +++ b/core-common/src/test/java/org/apache/kylin/common/KylinConfigTest.java @@ -30,7 +30,6 @@ import org.junit.Test; import com.google.common.collect.Maps; public class KylinConfigTest extends LocalFileMetadataTestCase { - @Before public void setUp() throws Exception { this.createTestMetadata(); @@ -49,33 +48,33 @@ public class KylinConfigTest extends LocalFileMetadataTestCase { assertEquals("test1", override.get("test1")); assertEquals("test2", override.get("test2")); } - + @Test public void testBackwardCompatibility() { KylinConfig config = KylinConfig.getInstanceFromEnv(); final String oldk = "kylin.test.bcc.old.key"; final String newk = "kylin.test.bcc.new.key"; - + assertNull(config.getOptional(oldk)); assertNotNull(config.getOptional(newk)); - + Map<String, String> override = Maps.newHashMap(); override.put(oldk, "1"); KylinConfigExt ext = KylinConfigExt.createInstance(config, override); assertEquals(ext.getOptional(oldk), null); assertEquals(ext.getOptional(newk), "1"); assertNotEquals(config.getOptional(newk), "1"); - + config.setProperty(oldk, "2"); assertEquals(config.getOptional(newk), "2"); } - + @Test public void testExtShareTheBase() { KylinConfig config = KylinConfig.getInstanceFromEnv(); Map<String, String> override = Maps.newHashMap(); KylinConfig configExt = KylinConfigExt.createInstance(config, override); - + assertTrue(config.properties == configExt.properties); config.setProperty("1234", "1234"); assertEquals("1234", configExt.getOptional("1234")); http://git-wip-us.apache.org/repos/asf/kylin/blob/ea60803e/core-cube/src/main/java/org/apache/kylin/cube/model/CubeDesc.java ---------------------------------------------------------------------- diff --git a/core-cube/src/main/java/org/apache/kylin/cube/model/CubeDesc.java b/core-cube/src/main/java/org/apache/kylin/cube/model/CubeDesc.java index f8c316c..56a8b7e 100644 --- a/core-cube/src/main/java/org/apache/kylin/cube/model/CubeDesc.java +++ b/core-cube/src/main/java/org/apache/kylin/cube/model/CubeDesc.java @@ -529,7 +529,16 @@ public class CubeDesc extends RootPersistentEntity implements IEngineAware { // note CubeDesc.name == CubeInstance.name List<ProjectInstance> ownerPrj = ProjectManager.getInstance(config).findProjects(RealizationType.CUBE, name); - logger.info("CubeDesc '" + name + "' is owned by " + ownerPrj); + + // cube inherit the project override props + if (ownerPrj.size() == 1) { + Map<String, String> prjOverrideProps = ownerPrj.get(0).getOverrideKylinProps(); + for (Entry<String, String> entry : prjOverrideProps.entrySet()) { + if (!overrideKylinProps.containsKey(entry.getKey())) { + overrideKylinProps.put(entry.getKey(), entry.getValue()); + } + } + } this.config = KylinConfigExt.createInstance(config, overrideKylinProps); http://git-wip-us.apache.org/repos/asf/kylin/blob/ea60803e/core-cube/src/test/java/org/apache/kylin/cube/ProjectSpecificConfigTest.java ---------------------------------------------------------------------- diff --git a/core-cube/src/test/java/org/apache/kylin/cube/ProjectSpecificConfigTest.java b/core-cube/src/test/java/org/apache/kylin/cube/ProjectSpecificConfigTest.java new file mode 100644 index 0000000..5008daa --- /dev/null +++ b/core-cube/src/test/java/org/apache/kylin/cube/ProjectSpecificConfigTest.java @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +package org.apache.kylin.cube; + +import static org.junit.Assert.assertEquals; + +import org.apache.kylin.common.KylinConfig; +import org.apache.kylin.common.util.LocalFileMetadataTestCase; +import org.apache.kylin.cube.model.CubeDesc; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +public class ProjectSpecificConfigTest extends LocalFileMetadataTestCase { + + @Before + public void setUp() throws Exception { + this.createTestMetadata(); + } + + @After + public void after() throws Exception { + this.cleanupTestMetadata(); + } + + @Test + public void testProject1() { + KylinConfig baseConfig = KylinConfig.getInstanceFromEnv(); + CubeDesc cubeDesc = CubeDescManager.getInstance(baseConfig).getCubeDesc("ssb"); + verifyProjectOverride(baseConfig, cubeDesc.getConfig()); + } + + @Test + public void testProject2() { + KylinConfig baseConfig = KylinConfig.getInstanceFromEnv(); + CubeInstance cube = CubeManager.getInstance(baseConfig).getCube("ssb"); + verifyProjectOverride(baseConfig, cube.getConfig()); + } + + private void verifyProjectOverride(KylinConfig base, KylinConfig override) { + assertEquals("who...@kylin.apache.org", base.getKylinOwner()); + assertEquals("ky...@kylin.apache.org", override.getKylinOwner()); + } +} http://git-wip-us.apache.org/repos/asf/kylin/blob/ea60803e/core-metadata/src/main/java/org/apache/kylin/metadata/project/ProjectInstance.java ---------------------------------------------------------------------- diff --git a/core-metadata/src/main/java/org/apache/kylin/metadata/project/ProjectInstance.java b/core-metadata/src/main/java/org/apache/kylin/metadata/project/ProjectInstance.java index 1afc603..74648cd 100644 --- a/core-metadata/src/main/java/org/apache/kylin/metadata/project/ProjectInstance.java +++ b/core-metadata/src/main/java/org/apache/kylin/metadata/project/ProjectInstance.java @@ -19,12 +19,14 @@ package org.apache.kylin.metadata.project; import java.util.ArrayList; +import java.util.LinkedHashMap; import java.util.List; import java.util.Set; import java.util.TreeSet; import javax.annotation.Nullable; +import com.fasterxml.jackson.annotation.JsonInclude; import org.apache.commons.lang3.StringUtils; import org.apache.kylin.common.persistence.ResourceStore; import org.apache.kylin.common.persistence.RootPersistentEntity; @@ -78,6 +80,10 @@ public class ProjectInstance extends RootPersistentEntity { @JsonProperty("ext_filters") private Set<String> extFilters = new TreeSet<String>(); + @JsonProperty("overrideKylinProps") + @JsonInclude(JsonInclude.Include.NON_NULL) + private LinkedHashMap<String, String> overrideKylinProps; + public String getResourcePath() { return concatResourcePath(name); } @@ -93,7 +99,7 @@ public class ProjectInstance extends RootPersistentEntity { return project.toUpperCase(); } - public static ProjectInstance create(String name, String owner, String description, List<RealizationEntry> realizationEntries, List<String> models) { + public static ProjectInstance create(String name, String owner, String description, LinkedHashMap<String, String> overrideProps, List<RealizationEntry> realizationEntries, List<String> models) { ProjectInstance projectInstance = new ProjectInstance(); projectInstance.updateRandomUuid(); @@ -102,6 +108,11 @@ public class ProjectInstance extends RootPersistentEntity { projectInstance.setDescription(description); projectInstance.setStatus(ProjectStatusEnum.ENABLED); projectInstance.setCreateTimeUTC(System.currentTimeMillis()); + if (overrideProps != null) { + projectInstance.setOverrideKylinProps(overrideProps); + } else { + projectInstance.setOverrideKylinProps(new LinkedHashMap<String, String>()); + } if (realizationEntries != null) projectInstance.setRealizationEntries(realizationEntries); else @@ -285,6 +296,14 @@ public class ProjectInstance extends RootPersistentEntity { } } + public LinkedHashMap<String, String> getOverrideKylinProps() { + return overrideKylinProps; + } + + public void setOverrideKylinProps(LinkedHashMap<String, String> overrideKylinProps) { + this.overrideKylinProps = overrideKylinProps; + } + public void init() { if (name == null) name = ProjectInstance.DEFAULT_PROJECT_NAME; @@ -296,6 +315,10 @@ public class ProjectInstance extends RootPersistentEntity { if (tables == null) tables = new TreeSet<String>(); + if (overrideKylinProps == null) { + overrideKylinProps = new LinkedHashMap<>(); + } + if (StringUtils.isBlank(this.name)) throw new IllegalStateException("Project name must not be blank"); } http://git-wip-us.apache.org/repos/asf/kylin/blob/ea60803e/core-metadata/src/main/java/org/apache/kylin/metadata/project/ProjectManager.java ---------------------------------------------------------------------- diff --git a/core-metadata/src/main/java/org/apache/kylin/metadata/project/ProjectManager.java b/core-metadata/src/main/java/org/apache/kylin/metadata/project/ProjectManager.java index b547843..ca4f7f1 100644 --- a/core-metadata/src/main/java/org/apache/kylin/metadata/project/ProjectManager.java +++ b/core-metadata/src/main/java/org/apache/kylin/metadata/project/ProjectManager.java @@ -20,6 +20,7 @@ package org.apache.kylin.metadata.project; import java.io.IOException; import java.util.ArrayList; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; @@ -138,7 +139,6 @@ public class ProjectManager { } private ProjectInstance reloadProjectLocalAt(String path) throws IOException { - ProjectInstance projectInstance = getStore().getResource(path, ProjectInstance.class, PROJECT_SERIALIZER); if (projectInstance == null) { logger.warn("reload project at path:" + path + " not found, this:" + this.toString()); @@ -162,12 +162,12 @@ public class ProjectManager { return projectMap.get(projectName); } - public ProjectInstance createProject(String projectName, String owner, String description) throws IOException { + public ProjectInstance createProject(String projectName, String owner, String description, LinkedHashMap<String, String> overrideProps) throws IOException { logger.info("Creating project " + projectName); ProjectInstance currentProject = getProject(projectName); if (currentProject == null) { - currentProject = ProjectInstance.create(projectName, owner, description, null, null); + currentProject = ProjectInstance.create(projectName, owner, description, overrideProps, null, null); } else { throw new IllegalStateException("The project named " + projectName + "already exists"); } @@ -207,9 +207,9 @@ public class ProjectManager { } //update project itself - public ProjectInstance updateProject(ProjectInstance project, String newName, String newDesc) throws IOException { + public ProjectInstance updateProject(ProjectInstance project, String newName, String newDesc, LinkedHashMap<String, String> overrideProps) throws IOException { if (!project.getName().equals(newName)) { - ProjectInstance newProject = this.createProject(newName, project.getOwner(), newDesc); + ProjectInstance newProject = this.createProject(newName, project.getOwner(), newDesc, overrideProps); newProject.setCreateTimeUTC(project.getCreateTimeUTC()); newProject.recordUpdateTime(System.currentTimeMillis()); @@ -225,6 +225,7 @@ public class ProjectManager { } else { project.setName(newName); project.setDescription(newDesc); + project.setOverrideKylinProps(overrideProps); if (project.getUuid() == null) project.updateRandomUuid(); @@ -291,7 +292,7 @@ public class ProjectManager { String newProjectName = norm(project); ProjectInstance newProject = getProject(newProjectName); if (newProject == null) { - newProject = this.createProject(newProjectName, user, "This is a project automatically added when adding realization " + realizationName + "(" + type + ")"); + newProject = this.createProject(newProjectName, user, "This is a project automatically added when adding realization " + realizationName + "(" + type + ")", null); } newProject.addRealizationEntry(type, realizationName); updateProject(newProject); http://git-wip-us.apache.org/repos/asf/kylin/blob/ea60803e/examples/test_case_data/localmeta/project/default.json ---------------------------------------------------------------------- diff --git a/examples/test_case_data/localmeta/project/default.json b/examples/test_case_data/localmeta/project/default.json index 695f3b7..cd3eae8 100644 --- a/examples/test_case_data/localmeta/project/default.json +++ b/examples/test_case_data/localmeta/project/default.json @@ -41,6 +41,11 @@ "name": "test_kylin_cube_with_view_inner_join_empty", "type": "CUBE", "realization": "test_kylin_cube_with_view_inner_join_empty" + }, + { + "name": "ssb", + "type": "CUBE", + "realization": "ssb" } ], "tables": [ @@ -59,5 +64,8 @@ "test_kylin_left_join_model_desc", "test_kylin_left_join_view_model_desc", "test_streaming_table_model_desc" - ] + ], + "overrideKylinProps" :{ + "kylin.storage.hbase.owner-tag": "ky...@kylin.apache.org" + } } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/kylin/blob/ea60803e/server-base/src/main/java/org/apache/kylin/rest/request/CreateProjectRequest.java ---------------------------------------------------------------------- diff --git a/server-base/src/main/java/org/apache/kylin/rest/request/CreateProjectRequest.java b/server-base/src/main/java/org/apache/kylin/rest/request/CreateProjectRequest.java index 71cd1c4..00fe1eb 100644 --- a/server-base/src/main/java/org/apache/kylin/rest/request/CreateProjectRequest.java +++ b/server-base/src/main/java/org/apache/kylin/rest/request/CreateProjectRequest.java @@ -18,11 +18,14 @@ package org.apache.kylin.rest.request; +import java.util.LinkedHashMap; + /** */ public class CreateProjectRequest { private String name; private String description; + private LinkedHashMap<String, String> overrideKylinProps; public CreateProjectRequest() { } @@ -43,4 +46,12 @@ public class CreateProjectRequest { this.description = description; } + public LinkedHashMap<String, String> getOverrideKylinProps() { + return overrideKylinProps; + } + + public void setOverrideKylinProps(LinkedHashMap<String, String> overrideKylinProps) { + this.overrideKylinProps = overrideKylinProps; + } + } http://git-wip-us.apache.org/repos/asf/kylin/blob/ea60803e/server-base/src/main/java/org/apache/kylin/rest/request/UpdateProjectRequest.java ---------------------------------------------------------------------- diff --git a/server-base/src/main/java/org/apache/kylin/rest/request/UpdateProjectRequest.java b/server-base/src/main/java/org/apache/kylin/rest/request/UpdateProjectRequest.java index 29ba162..f253c9c 100644 --- a/server-base/src/main/java/org/apache/kylin/rest/request/UpdateProjectRequest.java +++ b/server-base/src/main/java/org/apache/kylin/rest/request/UpdateProjectRequest.java @@ -18,12 +18,15 @@ package org.apache.kylin.rest.request; +import java.util.LinkedHashMap; + /** */ public class UpdateProjectRequest { private String formerProjectName; private String newProjectName; private String newDescription; + private LinkedHashMap<String, String> overrideKylinProps; public UpdateProjectRequest() { } @@ -52,4 +55,12 @@ public class UpdateProjectRequest { public void setNewProjectName(String newProjectName) { this.newProjectName = newProjectName; } + + public LinkedHashMap<String, String> getOverrideKylinProps() { + return overrideKylinProps; + } + + public void setOverrideKylinProps(LinkedHashMap<String, String> overrideKylinProps) { + this.overrideKylinProps = overrideKylinProps; + } } http://git-wip-us.apache.org/repos/asf/kylin/blob/ea60803e/server-base/src/main/java/org/apache/kylin/rest/service/ProjectService.java ---------------------------------------------------------------------- diff --git a/server-base/src/main/java/org/apache/kylin/rest/service/ProjectService.java b/server-base/src/main/java/org/apache/kylin/rest/service/ProjectService.java index b4cceb2..283cf4a 100644 --- a/server-base/src/main/java/org/apache/kylin/rest/service/ProjectService.java +++ b/server-base/src/main/java/org/apache/kylin/rest/service/ProjectService.java @@ -20,6 +20,7 @@ package org.apache.kylin.rest.service; import java.io.IOException; import java.util.Collections; +import java.util.LinkedHashMap; import java.util.List; import org.apache.kylin.metadata.project.ProjectInstance; @@ -53,13 +54,15 @@ public class ProjectService extends BasicService { public ProjectInstance createProject(CreateProjectRequest projectRequest) throws IOException { String projectName = projectRequest.getName(); String description = projectRequest.getDescription(); + LinkedHashMap<String, String> overrideProps = projectRequest.getOverrideKylinProps(); + ProjectInstance currentProject = getProjectManager().getProject(projectName); if (currentProject != null) { throw new InternalErrorException("The project named " + projectName + " already exists"); } String owner = SecurityContextHolder.getContext().getAuthentication().getName(); - ProjectInstance createdProject = getProjectManager().createProject(projectName, owner, description); + ProjectInstance createdProject = getProjectManager().createProject(projectName, owner, description, overrideProps); accessService.init(createdProject, AclPermission.ADMINISTRATION); logger.debug("New project created."); @@ -71,19 +74,19 @@ public class ProjectService extends BasicService { String formerProjectName = projectRequest.getFormerProjectName(); String newProjectName = projectRequest.getNewProjectName(); String newDescription = projectRequest.getNewDescription(); + LinkedHashMap<String, String> overrideProps = projectRequest.getOverrideKylinProps(); if (currentProject == null) { throw new InternalErrorException("The project named " + formerProjectName + " does not exists"); } - ProjectInstance updatedProject = getProjectManager().updateProject(currentProject, newProjectName, newDescription); + ProjectInstance updatedProject = getProjectManager().updateProject(currentProject, newProjectName, newDescription, overrideProps); logger.debug("Project updated."); return updatedProject; } - @PostFilter(Constant.ACCESS_POST_FILTER_READ) public List<ProjectInstance> listProjects(final Integer limit, final Integer offset) { List<ProjectInstance> projects = listAllProjects(limit, offset); http://git-wip-us.apache.org/repos/asf/kylin/blob/ea60803e/webapp/app/js/controllers/page.js ---------------------------------------------------------------------- diff --git a/webapp/app/js/controllers/page.js b/webapp/app/js/controllers/page.js index c65a264..2b6bc64 100644 --- a/webapp/app/js/controllers/page.js +++ b/webapp/app/js/controllers/page.js @@ -25,8 +25,8 @@ KylinApp.controller('PageCtrl', function ($scope, $q, AccessService, $modal, $lo $log.debug(data); kylinConfig.initWebConfigInfo(); }); - $rootScope.userAction={ - 'islogout':false + $rootScope.userAction = { + 'islogout': false } $scope.kylinConfig = kylinConfig; @@ -162,7 +162,7 @@ KylinApp.controller('PageCtrl', function ($scope, $q, AccessService, $modal, $lo $scope.$watch('projectModel.selectedProject', function (newValue, oldValue) { if (newValue != oldValue) { - if(!$rootScope.userAction.islogout) { + if (!$rootScope.userAction.islogout) { //$log.log("project updated in page controller,from:"+oldValue+" To:"+newValue); $cookieStore.put("project", $scope.projectModel.selectedProject); } @@ -195,12 +195,21 @@ var projCtrl = function ($scope, $location, $modalInstance, ProjectService, Mess projectIdx: -1 }; $scope.isEdit = false; - $scope.proj = {name: '', description: ''}; + $scope.proj = {name: '', description: '', overrideKylinProps: {}}; + $scope.convertedProperties = []; if (project) { $scope.state.isEdit = true; $scope.state.oldProjName = project.name; $scope.proj = project; + + for (var key in $scope.proj.overrideKylinProps) { + $scope.convertedProperties.push({ + name: key, + value: $scope.proj.overrideKylinProps[key] + }); + } + for (var i = 0; i < projects.length; i++) { if (projects[i].name === $scope.state.oldProjName) { $scope.state.projectIdx = i; @@ -215,7 +224,8 @@ var projCtrl = function ($scope, $location, $modalInstance, ProjectService, Mess var requestBody = { formerProjectName: $scope.state.oldProjName, newProjectName: $scope.proj.name, - newDescription: $scope.proj.description + newDescription: $scope.proj.description, + overrideKylinProps: $scope.proj.overrideKylinProps }; ProjectService.update({}, requestBody, function (newProj) { SweetAlert.swal('Success!', 'Project update successfully!', 'success'); @@ -239,10 +249,6 @@ var projCtrl = function ($scope, $location, $modalInstance, ProjectService, Mess ProjectService.save({}, $scope.proj, function (newProj) { SweetAlert.swal('Success!', 'New project created successfully!', 'success'); $modalInstance.dismiss('cancel'); -// if(projects) { -// projects.push(newProj); -// } -// ProjectModel.addProject(newProj); $cookieStore.put("project", newProj.name); location.reload(); }, function (e) { @@ -264,4 +270,36 @@ var projCtrl = function ($scope, $location, $modalInstance, ProjectService, Mess $modalInstance.dismiss('cancel'); }; + $scope.addNewProperty = function () { + if ($scope.proj.overrideKylinProps.hasOwnProperty('')) { + return; + } + $scope.proj.overrideKylinProps[''] = ''; + $scope.convertedProperties.push({ + name: '', + value: '' + }); + }; + + $scope.refreshPropertiesObj = function () { + $scope.proj.overrideKylinProps = {}; + angular.forEach($scope.convertedProperties, function (item, index) { + $scope.proj.overrideKylinProps[item.name] = item.value; + }) + }; + + + $scope.refreshProperty = function (list, index, item) { + $scope.convertedProperties[index] = item; + $scope.refreshPropertiesObj(); + }; + + + $scope.removeProperty = function (arr, index, item) { + if (index > -1) { + arr.splice(index, 1); + } + delete $scope.proj.overrideKylinProps[item.name]; + } + }; http://git-wip-us.apache.org/repos/asf/kylin/blob/ea60803e/webapp/app/partials/projects/project_create.html ---------------------------------------------------------------------- diff --git a/webapp/app/partials/projects/project_create.html b/webapp/app/partials/projects/project_create.html index 36cbaf5..2dc76da 100644 --- a/webapp/app/partials/projects/project_create.html +++ b/webapp/app/partials/projects/project_create.html @@ -17,40 +17,75 @@ --> <script type="text/ng-template" id="project.html"> - <ng-form name="proj_create_form"> - <div class="modal-header"> - <h4>New Project</h4> - </div> - <div class="modal-body" style="background-color: white"> - <div class="form-group"> - <label><b>Project Name</b></label> + <ng-form name="proj_create_form"> + <div class="modal-header"> + <h4>New Project</h4> + </div> + <div class="modal-body" style="background-color: white"> + <div class="form-group"> + <label><b>Project Name</b></label> - <div class="clearfix"> - <input name="name_input" type="text" class="form-control" ng-model="proj.name" required - placeholder="You can use letters, numbers, and underscore characters '_'" - ng-maxlength = 100 ng-pattern="/^\w+$/" /> - <span class="text-warning" ng-if="proj_create_form.name_input.$error.required && proj_create_form.name_input.$dirty" - > The project name is required</span> - <span class="text-warning" ng-if="proj_create_form.name_input.$invalid && proj_create_form.name_input.$dirty && !proj_create_form.name_input.$error.required" - > The project name is invalid</span> - </div> - </div> - <div class="form-group"> - <label><b>Project Description</b></label> + <div class="clearfix"> + <input name="name_input" type="text" class="form-control" ng-model="proj.name" required + placeholder="You can use letters, numbers, and underscore characters '_'" + ng-maxlength=100 ng-pattern="/^\w+$/"/> + <span class="text-warning" + ng-if="proj_create_form.name_input.$error.required && proj_create_form.name_input.$dirty" + > The project name is required</span> + <span class="text-warning" + ng-if="proj_create_form.name_input.$invalid && proj_create_form.name_input.$dirty && !proj_create_form.name_input.$error.required" + > The project name is invalid</span> + </div> + </div> + <div class="form-group"> + <label><b>Project Description</b></label> - <div class="clearfix"> + <div class="clearfix"> <textarea name="description_input" type="text" class="form-control" placeholder="Project Description.." ng-model="proj.description" - ></textarea> - </div> + ></textarea> + </div> + </div> + <div> + <label><b>Project Config</b></label> + <div ng-repeat=" prop in convertedProperties track by $index " class="form-group"> + <div class="row"> + <label class="col-xs-12 col-sm-6 control-label no-padding-right" ng-class="{'has-error':prop.name==''}"> + <input ng-model="prop.name" placeholder="key" ng-change="refreshProperty(convertedProperties,$index,prop)" + class="form-control"/> + <small style="font-weight: normal !important;" class="help-block" ng-show="prop.name==''">Property name is + required. + </small> + </label> + <div class="col-xs-12 col-sm-5" ng-class="{'has-error':prop.value==''}"> + <input ng-model="prop.value" placeholder="value" + ng-change="refreshProperty(convertedProperties,$index,prop)" class="form-control"/> + <small class="help-block" ng-show="prop.value==''">Property value is required.</small> </div> + <div class="col-sm-1"> + <button class="btn btn-xs btn-info" ng-click="removeProperty(convertedProperties,$index,prop)"><i + class="fa fa-minus"></i> + </button> + </div> + + </div> </div> - <div class="modal-footer"> - <button class="btn btn-primary" ng-click="cancel()">Close</button> - <button class="btn btn-success" ng-click="createOrUpdate()" ng-disabled="proj_create_form.$invalid"> - Submit - </button> + <!--Add Project Property--> + <div class="form-group"> + <button class="btn btn-sm btn-info" ng-click="addNewProperty();"> + <i class="fa fa-plus"></i> Property + </button> </div> - </ng-form> -</script> \ No newline at end of file + + </div> + </div> + <div class="modal-footer"> + <button class="btn btn-primary" ng-click="cancel()">Close</button> + <button class="btn btn-success" ng-click="createOrUpdate()" ng-disabled="proj_create_form.$invalid"> + Submit + </button> + </div> + </ng-form> +</script> +