Commit b949afac authored by nobodyiam's avatar nobodyiam

allow public namespace created with no org prefix

parent 9e3aec54
...@@ -161,7 +161,9 @@ public class NamespaceController { ...@@ -161,7 +161,9 @@ public class NamespaceController {
@PreAuthorize(value = "@permissionValidator.hasCreateAppNamespacePermission(#appId, #appNamespace)") @PreAuthorize(value = "@permissionValidator.hasCreateAppNamespacePermission(#appId, #appNamespace)")
@RequestMapping(value = "/apps/{appId}/appnamespaces", method = RequestMethod.POST) @RequestMapping(value = "/apps/{appId}/appnamespaces", method = RequestMethod.POST)
public AppNamespace createAppNamespace(@PathVariable String appId, @RequestBody AppNamespace appNamespace) { public AppNamespace createAppNamespace(@PathVariable String appId,
@RequestParam(defaultValue = "true") boolean appendNamespacePrefix,
@RequestBody AppNamespace appNamespace) {
RequestPrecondition.checkArgumentsNotEmpty(appNamespace.getAppId(), appNamespace.getName()); RequestPrecondition.checkArgumentsNotEmpty(appNamespace.getAppId(), appNamespace.getName());
if (!InputValidator.isValidAppNamespace(appNamespace.getName())) { if (!InputValidator.isValidAppNamespace(appNamespace.getName())) {
...@@ -170,7 +172,7 @@ public class NamespaceController { ...@@ -170,7 +172,7 @@ public class NamespaceController {
+ InputValidator.INVALID_NAMESPACE_NAMESPACE_MESSAGE)); + InputValidator.INVALID_NAMESPACE_NAMESPACE_MESSAGE));
} }
AppNamespace createdAppNamespace = appNamespaceService.createAppNamespaceInLocal(appNamespace); AppNamespace createdAppNamespace = appNamespaceService.createAppNamespaceInLocal(appNamespace, appendNamespacePrefix);
if (portalConfig.canAppAdminCreatePrivateNamespace() || createdAppNamespace.isPublic()) { if (portalConfig.canAppAdminCreatePrivateNamespace() || createdAppNamespace.isPublic()) {
assignNamespaceRoleToOperator(appId, appNamespace.getName()); assignNamespaceRoleToOperator(appId, appNamespace.getName());
......
...@@ -12,7 +12,7 @@ public interface AppNamespaceRepository extends PagingAndSortingRepository<AppNa ...@@ -12,7 +12,7 @@ public interface AppNamespaceRepository extends PagingAndSortingRepository<AppNa
AppNamespace findByName(String namespaceName); AppNamespace findByName(String namespaceName);
AppNamespace findByNameAndIsPublic(String namespaceName, boolean isPublic); List<AppNamespace> findByNameAndIsPublic(String namespaceName, boolean isPublic);
List<AppNamespace> findByIsPublicTrue(); List<AppNamespace> findByIsPublicTrue();
......
...@@ -9,16 +9,23 @@ import com.ctrip.framework.apollo.core.utils.StringUtils; ...@@ -9,16 +9,23 @@ import com.ctrip.framework.apollo.core.utils.StringUtils;
import com.ctrip.framework.apollo.portal.repository.AppNamespaceRepository; import com.ctrip.framework.apollo.portal.repository.AppNamespaceRepository;
import com.ctrip.framework.apollo.portal.spi.UserInfoHolder; import com.ctrip.framework.apollo.portal.spi.UserInfoHolder;
import com.google.common.base.Joiner;
import com.google.common.collect.Sets;
import java.util.Set;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import org.springframework.util.CollectionUtils;
@Service @Service
public class AppNamespaceService { public class AppNamespaceService {
private static final int PRIVATE_APP_NAMESPACE_NOTIFICATION_COUNT = 5;
private static final Joiner APP_NAMESPACE_JOINER = Joiner.on(",").skipNulls();
@Autowired @Autowired
private UserInfoHolder userInfoHolder; private UserInfoHolder userInfoHolder;
@Autowired @Autowired
...@@ -38,7 +45,17 @@ public class AppNamespaceService { ...@@ -38,7 +45,17 @@ public class AppNamespaceService {
} }
public AppNamespace findPublicAppNamespace(String namespaceName) { public AppNamespace findPublicAppNamespace(String namespaceName) {
return appNamespaceRepository.findByNameAndIsPublic(namespaceName, true); List<AppNamespace> appNamespaces = appNamespaceRepository.findByNameAndIsPublic(namespaceName, true);
if (CollectionUtils.isEmpty(appNamespaces)) {
return null;
}
return appNamespaces.get(0);
}
private List<AppNamespace> findAllPrivateAppNamespaces(String namespaceName) {
return appNamespaceRepository.findByNameAndIsPublic(namespaceName, false);
} }
public AppNamespace findByAppIdAndName(String appId, String namespaceName) { public AppNamespace findByAppIdAndName(String appId, String namespaceName) {
...@@ -69,8 +86,12 @@ public class AppNamespaceService { ...@@ -69,8 +86,12 @@ public class AppNamespaceService {
return Objects.isNull(appNamespaceRepository.findByAppIdAndName(appId, namespaceName)); return Objects.isNull(appNamespaceRepository.findByAppIdAndName(appId, namespaceName));
} }
@Transactional
public AppNamespace createAppNamespaceInLocal(AppNamespace appNamespace) { public AppNamespace createAppNamespaceInLocal(AppNamespace appNamespace) {
return createAppNamespaceInLocal(appNamespace, true);
}
@Transactional
public AppNamespace createAppNamespaceInLocal(AppNamespace appNamespace, boolean appendNamespacePrefix) {
String appId = appNamespace.getAppId(); String appId = appNamespace.getAppId();
//add app org id as prefix //add app org id as prefix
...@@ -82,7 +103,7 @@ public class AppNamespaceService { ...@@ -82,7 +103,7 @@ public class AppNamespaceService {
StringBuilder appNamespaceName = new StringBuilder(); StringBuilder appNamespaceName = new StringBuilder();
//add prefix postfix //add prefix postfix
appNamespaceName appNamespaceName
.append(appNamespace.isPublic() ? app.getOrgId() + "." : "") .append(appNamespace.isPublic() && appendNamespacePrefix ? app.getOrgId() + "." : "")
.append(appNamespace.getName()) .append(appNamespace.getName())
.append(appNamespace.formatAsEnum() == ConfigFileFormat.Properties ? "" : "." + appNamespace.getFormat()); .append(appNamespace.formatAsEnum() == ConfigFileFormat.Properties ? "" : "." + appNamespace.getFormat());
appNamespace.setName(appNamespaceName.toString()); appNamespace.setName(appNamespaceName.toString());
...@@ -103,12 +124,9 @@ public class AppNamespaceService { ...@@ -103,12 +124,9 @@ public class AppNamespaceService {
appNamespace.setDataChangeLastModifiedBy(operator); appNamespace.setDataChangeLastModifiedBy(operator);
// unique check // globally uniqueness check
if (appNamespace.isPublic()) { if (appNamespace.isPublic()) {
AppNamespace publicAppNamespace = findPublicAppNamespace(appNamespace.getName()); checkAppNamespaceGlobalUniqueness(appNamespace);
if (publicAppNamespace != null) {
throw new BadRequestException("Public AppNamespace " + appNamespace.getName() + " already exists in appId: " + publicAppNamespace.getAppId() + "!");
}
} }
if (!appNamespace.isPublic() && if (!appNamespace.isPublic() &&
...@@ -124,6 +142,30 @@ public class AppNamespaceService { ...@@ -124,6 +142,30 @@ public class AppNamespaceService {
return createdAppNamespace; return createdAppNamespace;
} }
private void checkAppNamespaceGlobalUniqueness(AppNamespace appNamespace) {
AppNamespace publicAppNamespace = findPublicAppNamespace(appNamespace.getName());
if (publicAppNamespace != null) {
throw new BadRequestException("Public AppNamespace " + appNamespace.getName() + " already exists in appId: " + publicAppNamespace.getAppId() + "!");
}
List<AppNamespace> privateAppNamespaces = findAllPrivateAppNamespaces(appNamespace.getName());
if (!CollectionUtils.isEmpty(privateAppNamespaces)) {
Set<String> appIds = Sets.newHashSet();
for (AppNamespace ans : privateAppNamespaces) {
appIds.add(ans.getAppId());
if (appIds.size() == PRIVATE_APP_NAMESPACE_NOTIFICATION_COUNT) {
break;
}
}
throw new BadRequestException(
"Public AppNamespace " + appNamespace.getName() + " already exists as private AppNamespace in appId: "
+ APP_NAMESPACE_JOINER.join(appIds) + ", etc. Please select another name!");
}
}
@Transactional @Transactional
public AppNamespace deleteAppNamespace(String appId, String namespaceName) { public AppNamespace deleteAppNamespace(String appId, String namespaceName) {
AppNamespace appNamespace = appNamespaceRepository.findByAppIdAndName(appId, namespaceName); AppNamespace appNamespace = appNamespaceRepository.findByAppIdAndName(appId, namespaceName);
......
...@@ -95,9 +95,9 @@ ...@@ -95,9 +95,9 @@
<label class="col-sm-3 control-label"> <label class="col-sm-3 control-label">
<apollorequiredfield></apollorequiredfield> <apollorequiredfield></apollorequiredfield>
名称</label> 名称</label>
<div class="col-sm-4" valdr-form-group> <div class="col-sm-5" valdr-form-group>
<div ng-class="{'input-group':appNamespace.isPublic}"> <div ng-class="{'input-group':appNamespace.isPublic && appendNamespacePrefix}">
<span class="input-group-addon" ng-show="appNamespace.isPublic" <span class="input-group-addon" ng-show="appNamespace.isPublic && appendNamespacePrefix"
ng-bind="appBaseInfo.namespacePrefix"></span> ng-bind="appBaseInfo.namespacePrefix"></span>
<input type="text" name="namespaceName" class="form-control" <input type="text" name="namespaceName" class="form-control"
ng-model="appNamespace.name"> ng-model="appNamespace.name">
...@@ -116,9 +116,25 @@ ...@@ -116,9 +116,25 @@
</div> </div>
&nbsp;&nbsp; &nbsp;&nbsp;
<span ng-show="appNamespace.isPublic" ng-bind="concatNamespace()" <span ng-show="appNamespace.isPublic && appendNamespacePrefix" ng-bind="concatNamespace()"
style="line-height: 34px;"></span> style="line-height: 34px;"></span>
</div> </div>
<div class="form-group" ng-show="type == 'create' && appNamespace.isPublic">
<label class="col-sm-3 control-label">
自动添加部门前缀
</label>
<div class="col-sm-6" valdr-form-group>
<div>
<label class="checkbox-inline">
<input type="checkbox" ng-model="appendNamespacePrefix" />
{{appBaseInfo.namespacePrefix}}
</label>
</div>
<small>(公共Namespace的名称需要全局唯一,添加部门前缀有助于保证全局唯一性)</small>
</div>
</div>
<div class="form-group" ng-show="type == 'create' && (pageSetting.canAppAdminCreatePrivateNamespace || hasRootPermission)"> <div class="form-group" ng-show="type == 'create' && (pageSetting.canAppAdminCreatePrivateNamespace || hasRootPermission)">
<label class="col-sm-3 control-label"> <label class="col-sm-3 control-label">
<apollorequiredfield></apollorequiredfield> <apollorequiredfield></apollorequiredfield>
......
...@@ -11,6 +11,7 @@ namespace_module.controller("LinkNamespaceController", ...@@ -11,6 +11,7 @@ namespace_module.controller("LinkNamespaceController",
$scope.step = 1; $scope.step = 1;
$scope.submitBtnDisabled = false; $scope.submitBtnDisabled = false;
$scope.appendNamespacePrefix = true;
PermissionService.has_root_permission().then(function (result) { PermissionService.has_root_permission().then(function (result) {
$scope.hasRootPermission = result.hasPermission; $scope.hasRootPermission = result.hasPermission;
...@@ -125,7 +126,9 @@ namespace_module.controller("LinkNamespaceController", ...@@ -125,7 +126,9 @@ namespace_module.controller("LinkNamespaceController",
} }
$scope.submitBtnDisabled = true; $scope.submitBtnDisabled = true;
NamespaceService.createAppNamespace($scope.appId, $scope.appNamespace).then( //only append namespace prefix for public app namespace
var appendNamespacePrefix = $scope.appNamespace.isPublic ? $scope.appendNamespacePrefix : false;
NamespaceService.createAppNamespace($scope.appId, $scope.appNamespace, appendNamespacePrefix).then(
function (result) { function (result) {
$scope.step = 2; $scope.step = 2;
setTimeout(function () { setTimeout(function () {
......
...@@ -12,7 +12,7 @@ appService.service("NamespaceService", ['$resource', '$q', function ($resource, ...@@ -12,7 +12,7 @@ appService.service("NamespaceService", ['$resource', '$q', function ($resource,
}, },
createAppNamespace: { createAppNamespace: {
method: 'POST', method: 'POST',
url: '/apps/:appId/appnamespaces', url: '/apps/:appId/appnamespaces?appendNamespacePrefix=:appendNamespacePrefix',
isArray: false isArray: false
}, },
getNamespacePublishInfo: { getNamespacePublishInfo: {
...@@ -60,10 +60,11 @@ appService.service("NamespaceService", ['$resource', '$q', function ($resource, ...@@ -60,10 +60,11 @@ appService.service("NamespaceService", ['$resource', '$q', function ($resource,
return d.promise; return d.promise;
} }
function createAppNamespace(appId, appnamespace) { function createAppNamespace(appId, appnamespace, appendNamespacePrefix) {
var d = $q.defer(); var d = $q.defer();
namespace_source.createAppNamespace({ namespace_source.createAppNamespace({
appId: appId appId: appId,
appendNamespacePrefix: appendNamespacePrefix
}, appnamespace, function (result) { }, appnamespace, function (result) {
d.resolve(result); d.resolve(result);
}, function (result) { }, function (result) {
......
...@@ -76,6 +76,31 @@ public class AppNamespaceServiceTest extends AbstractIntegrationTest { ...@@ -76,6 +76,31 @@ public class AppNamespaceServiceTest extends AbstractIntegrationTest {
appNamespaceService.createAppNamespaceInLocal(appNamespace); appNamespaceService.createAppNamespaceInLocal(appNamespace);
} }
@Test
@Sql(scripts = "/sql/appnamespaceservice/init-appnamespace.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
@Sql(scripts = "/sql/cleanup.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
public void testCreatePublicAppNamespaceNotExistedWithNoAppendnamespacePrefix() {
AppNamespace appNamespace = assmbleBaseAppNamespace();
appNamespace.setPublic(true);
appNamespace.setName("old");
AppNamespace createdAppNamespace = appNamespaceService.createAppNamespaceInLocal(appNamespace, false);
Assert.assertNotNull(createdAppNamespace);
Assert.assertEquals(appNamespace.getName(), createdAppNamespace.getName());
}
@Test(expected = BadRequestException.class)
@Sql(scripts = "/sql/appnamespaceservice/init-appnamespace.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
@Sql(scripts = "/sql/cleanup.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
public void testCreatePublicAppNamespaceExistedWithNoAppendnamespacePrefix() {
AppNamespace appNamespace = assmbleBaseAppNamespace();
appNamespace.setPublic(true);
appNamespace.setName("datasource");
appNamespaceService.createAppNamespaceInLocal(appNamespace, false);
}
@Test @Test
@Sql(scripts = "/sql/appnamespaceservice/init-appnamespace.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) @Sql(scripts = "/sql/appnamespaceservice/init-appnamespace.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
@Sql(scripts = "/sql/cleanup.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) @Sql(scripts = "/sql/cleanup.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment