未验证 提交 adb46345 编写于 作者: J Jason Song 提交者: GitHub

Merge pull request #1476 from nobodyiam/allow-public-namespace-with-no-org-prefix

allow public namespaces created with no org prefix
......@@ -161,16 +161,18 @@ public class NamespaceController {
@PreAuthorize(value = "@permissionValidator.hasCreateAppNamespacePermission(#appId, #appNamespace)")
@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());
if (!InputValidator.isValidAppNamespace(appNamespace.getName())) {
throw new BadRequestException(String.format("Namespace格式错误: %s",
InputValidator.INVALID_CLUSTER_NAMESPACE_MESSAGE + " & "
+ InputValidator.INVALID_NAMESPACE_NAMESPACE_MESSAGE));
InputValidator.INVALID_CLUSTER_NAMESPACE_MESSAGE + " & "
+ InputValidator.INVALID_NAMESPACE_NAMESPACE_MESSAGE));
}
AppNamespace createdAppNamespace = appNamespaceService.createAppNamespaceInLocal(appNamespace);
AppNamespace createdAppNamespace = appNamespaceService.createAppNamespaceInLocal(appNamespace, appendNamespacePrefix);
if (portalConfig.canAppAdminCreatePrivateNamespace() || createdAppNamespace.isPublic()) {
assignNamespaceRoleToOperator(appId, appNamespace.getName());
......
......@@ -12,7 +12,7 @@ public interface AppNamespaceRepository extends PagingAndSortingRepository<AppNa
AppNamespace findByName(String namespaceName);
AppNamespace findByNameAndIsPublic(String namespaceName, boolean isPublic);
List<AppNamespace> findByNameAndIsPublic(String namespaceName, boolean isPublic);
List<AppNamespace> findByIsPublicTrue();
......
......@@ -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.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.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Objects;
import org.springframework.util.CollectionUtils;
@Service
public class AppNamespaceService {
private static final int PRIVATE_APP_NAMESPACE_NOTIFICATION_COUNT = 5;
private static final Joiner APP_NAMESPACE_JOINER = Joiner.on(",").skipNulls();
@Autowired
private UserInfoHolder userInfoHolder;
@Autowired
......@@ -38,7 +45,17 @@ public class AppNamespaceService {
}
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) {
......@@ -69,8 +86,12 @@ public class AppNamespaceService {
return Objects.isNull(appNamespaceRepository.findByAppIdAndName(appId, namespaceName));
}
@Transactional
public AppNamespace createAppNamespaceInLocal(AppNamespace appNamespace) {
return createAppNamespaceInLocal(appNamespace, true);
}
@Transactional
public AppNamespace createAppNamespaceInLocal(AppNamespace appNamespace, boolean appendNamespacePrefix) {
String appId = appNamespace.getAppId();
//add app org id as prefix
......@@ -82,7 +103,7 @@ public class AppNamespaceService {
StringBuilder appNamespaceName = new StringBuilder();
//add prefix postfix
appNamespaceName
.append(appNamespace.isPublic() ? app.getOrgId() + "." : "")
.append(appNamespace.isPublic() && appendNamespacePrefix ? app.getOrgId() + "." : "")
.append(appNamespace.getName())
.append(appNamespace.formatAsEnum() == ConfigFileFormat.Properties ? "" : "." + appNamespace.getFormat());
appNamespace.setName(appNamespaceName.toString());
......@@ -103,12 +124,9 @@ public class AppNamespaceService {
appNamespace.setDataChangeLastModifiedBy(operator);
// unique check
// globally uniqueness check
if (appNamespace.isPublic()) {
AppNamespace publicAppNamespace = findPublicAppNamespace(appNamespace.getName());
if (publicAppNamespace != null) {
throw new BadRequestException("Public AppNamespace " + appNamespace.getName() + " already exists in appId: " + publicAppNamespace.getAppId() + "!");
}
checkAppNamespaceGlobalUniqueness(appNamespace);
}
if (!appNamespace.isPublic() &&
......@@ -124,6 +142,30 @@ public class AppNamespaceService {
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
public AppNamespace deleteAppNamespace(String appId, String namespaceName) {
AppNamespace appNamespace = appNamespaceRepository.findByAppIdAndName(appId, namespaceName);
......
......@@ -95,9 +95,9 @@
<label class="col-sm-3 control-label">
<apollorequiredfield></apollorequiredfield>
名称</label>
<div class="col-sm-4" valdr-form-group>
<div ng-class="{'input-group':appNamespace.isPublic}">
<span class="input-group-addon" ng-show="appNamespace.isPublic"
<div class="col-sm-5" valdr-form-group>
<div ng-class="{'input-group':appNamespace.isPublic && appendNamespacePrefix}">
<span class="input-group-addon" ng-show="appNamespace.isPublic && appendNamespacePrefix"
ng-bind="appBaseInfo.namespacePrefix"></span>
<input type="text" name="namespaceName" class="form-control"
ng-model="appNamespace.name">
......@@ -116,9 +116,25 @@
</div>
&nbsp;&nbsp;
<span ng-show="appNamespace.isPublic" ng-bind="concatNamespace()"
<span ng-show="appNamespace.isPublic && appendNamespacePrefix" ng-bind="concatNamespace()"
style="line-height: 34px;"></span>
</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)">
<label class="col-sm-3 control-label">
<apollorequiredfield></apollorequiredfield>
......
......@@ -11,6 +11,7 @@ namespace_module.controller("LinkNamespaceController",
$scope.step = 1;
$scope.submitBtnDisabled = false;
$scope.appendNamespacePrefix = true;
PermissionService.has_root_permission().then(function (result) {
$scope.hasRootPermission = result.hasPermission;
......@@ -125,7 +126,9 @@ namespace_module.controller("LinkNamespaceController",
}
$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) {
$scope.step = 2;
setTimeout(function () {
......
......@@ -12,7 +12,7 @@ appService.service("NamespaceService", ['$resource', '$q', function ($resource,
},
createAppNamespace: {
method: 'POST',
url: '/apps/:appId/appnamespaces',
url: '/apps/:appId/appnamespaces?appendNamespacePrefix=:appendNamespacePrefix',
isArray: false
},
getNamespacePublishInfo: {
......@@ -60,11 +60,12 @@ appService.service("NamespaceService", ['$resource', '$q', function ($resource,
return d.promise;
}
function createAppNamespace(appId, appnamespace) {
function createAppNamespace(appId, appnamespace, appendNamespacePrefix) {
var d = $q.defer();
namespace_source.createAppNamespace({
appId: appId
}, appnamespace, function (result) {
appId: appId,
appendNamespacePrefix: appendNamespacePrefix
}, appnamespace, function (result) {
d.resolve(result);
}, function (result) {
d.reject(result);
......
......@@ -76,6 +76,31 @@ public class AppNamespaceServiceTest extends AbstractIntegrationTest {
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
@Sql(scripts = "/sql/appnamespaceservice/init-appnamespace.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
@Sql(scripts = "/sql/cleanup.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册