提交 7f0fc9d4 编写于 作者: J Jason Song 提交者: GitHub

Merge pull request #530 from lepdou/app_create

User can add admins when create application
......@@ -11,17 +11,20 @@ import com.ctrip.framework.apollo.common.utils.InputValidator;
import com.ctrip.framework.apollo.common.utils.RequestPrecondition;
import com.ctrip.framework.apollo.core.enums.Env;
import com.ctrip.framework.apollo.portal.component.PortalSettings;
import com.ctrip.framework.apollo.portal.entity.bo.UserInfo;
import com.ctrip.framework.apollo.portal.entity.model.AppModel;
import com.ctrip.framework.apollo.portal.entity.vo.EnvClusterInfo;
import com.ctrip.framework.apollo.portal.listener.AppCreationEvent;
import com.ctrip.framework.apollo.portal.service.AppService;
import com.ctrip.framework.apollo.portal.spi.UserService;
import com.ctrip.framework.apollo.portal.service.RolePermissionService;
import com.ctrip.framework.apollo.portal.spi.UserInfoHolder;
import com.ctrip.framework.apollo.portal.util.RoleUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.data.domain.Pageable;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
......@@ -32,36 +35,37 @@ import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.HttpClientErrorException;
import java.util.List;
import java.util.Set;
@RestController
@RequestMapping("/apps")
public class AppController {
@Autowired
private UserInfoHolder userInfoHolder;
@Autowired
private AppService appService;
@Autowired
private PortalSettings portalSettings;
@Autowired
private ApplicationEventPublisher publisher;
@Autowired
private UserService userService;
private RolePermissionService rolePermissionService;
@RequestMapping(value = "", method = RequestMethod.GET)
public List<App> findApps(@RequestParam(value = "appIds", required = false) String appIds) {
if (StringUtils.isEmpty(appIds)){
if (StringUtils.isEmpty(appIds)) {
return appService.findAll();
}else {
} else {
return appService.findByAppIds(Sets.newHashSet(appIds.split(",")));
}
}
@RequestMapping(value = "/by-owner", method = RequestMethod.GET)
public List<App> findAppsByOwner(@RequestParam("owner") String owner, Pageable page){
public List<App> findAppsByOwner(@RequestParam("owner") String owner, Pageable page) {
return appService.findByOwnerName(owner, page);
}
......@@ -75,38 +79,46 @@ public class AppController {
response.addResponseEntity(RichResponseEntity.ok(appService.createEnvNavNode(env, appId)));
} catch (Exception e) {
response.addResponseEntity(RichResponseEntity.error(HttpStatus.INTERNAL_SERVER_ERROR,
"load env:" + env.name() + " cluster error." + e
.getMessage()));
"load env:" + env.name() + " cluster error." + e
.getMessage()));
}
}
return response;
}
/**
* 创建App流程: 1.先在portal db中创建 2.再保存到各个环境的apollo db中
*
* 只要第一步成功,就算这次创建app是成功操作,如果某个环境的apollo db创建失败,可通过portal db中的app信息再次创建.
*/
@RequestMapping(value = "", method = RequestMethod.POST)
public ResponseEntity<Void> create(@RequestBody App app) {
public App create(@RequestBody AppModel appModel) {
RequestPrecondition.checkArgumentsNotEmpty(app.getName(), app.getAppId(), app.getOwnerName(),
app.getOrgId(), app.getOrgName());
if (!InputValidator.isValidClusterNamespace(app.getAppId())) {
String appId = appModel.getAppId();
String appName = appModel.getName();
String ownerName = appModel.getOwnerName();
String orgId = appModel.getOrgId();
String orgName = appModel.getOrgName();
RequestPrecondition.checkArgumentsNotEmpty(appId, appName, ownerName, orgId, orgName);
if (!InputValidator.isValidClusterNamespace(appModel.getAppId())) {
throw new BadRequestException(String.format("AppId格式错误: %s", InputValidator.INVALID_CLUSTER_NAMESPACE_MESSAGE));
}
UserInfo userInfo = userService.findByUserId(app.getOwnerName());
if (userInfo == null) {
throw new BadRequestException("应用负责人不存在");
}
app.setOwnerEmail(userInfo.getEmail());
appService.enrichUserInfo(app);
App app = new App();
app.setAppId(appId);
app.setName(appName);
app.setOwnerName(ownerName);
app.setOrgId(orgId);
app.setOrgName(orgName);
App createdApp = appService.create(app);
publisher.publishEvent(new AppCreationEvent(createdApp));
return ResponseEntity.ok().build();
Set<String> admins = appModel.getAdmins();
if (!CollectionUtils.isEmpty(admins)) {
rolePermissionService
.assignRoleToUsers(RoleUtils.buildAppMasterRoleName(appId), admins, userInfoHolder.getUser().getUserId());
}
return createdApp;
}
@RequestMapping(value = "/envs/{env}", method = RequestMethod.POST, consumes = {
......@@ -114,7 +126,7 @@ public class AppController {
public ResponseEntity<Void> create(@PathVariable String env, @RequestBody App app) {
RequestPrecondition.checkArgumentsNotEmpty(app.getName(), app.getAppId(), app.getOwnerEmail(), app.getOwnerName(),
app.getOrgId(), app.getOrgName());
app.getOrgId(), app.getOrgName());
if (!InputValidator.isValidClusterNamespace(app.getAppId())) {
throw new BadRequestException(InputValidator.INVALID_CLUSTER_NAMESPACE_MESSAGE);
}
......@@ -143,11 +155,10 @@ public class AppController {
response.addResponseEntity(RichResponseEntity.ok(env));
} else {
response.addResponseEntity(RichResponseEntity.error(HttpStatus.INTERNAL_SERVER_ERROR,
String.format("load appId:%s from env %s error.", appId, env)
+ e.getMessage()));
String.format("load appId:%s from env %s error.", appId,env)
+ e.getMessage()));
}
}
}
return response;
......
package com.ctrip.framework.apollo.portal.entity.model;
import java.util.Set;
public class AppModel {
private String name;
private String appId;
private String orgId;
private String orgName;
private String ownerName;
private Set<String> admins;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAppId() {
return appId;
}
public void setAppId(String appId) {
this.appId = appId;
}
public String getOrgId() {
return orgId;
}
public void setOrgId(String orgId) {
this.orgId = orgId;
}
public String getOrgName() {
return orgName;
}
public void setOrgName(String orgName) {
this.orgName = orgName;
}
public String getOwnerName() {
return ownerName;
}
public void setOwnerName(String ownerName) {
this.ownerName = ownerName;
}
public Set<String> getAdmins() {
return admins;
}
public void setAdmins(Set<String> admins) {
this.admins = admins;
}
}
......@@ -10,9 +10,11 @@ import com.ctrip.framework.apollo.common.utils.ExceptionUtils;
import com.ctrip.framework.apollo.core.enums.Env;
import com.ctrip.framework.apollo.portal.api.AdminServiceAPI;
import com.ctrip.framework.apollo.portal.constant.CatEventType;
import com.ctrip.framework.apollo.portal.entity.bo.UserInfo;
import com.ctrip.framework.apollo.portal.entity.vo.EnvClusterInfo;
import com.ctrip.framework.apollo.portal.repository.AppRepository;
import com.ctrip.framework.apollo.portal.spi.UserInfoHolder;
import com.ctrip.framework.apollo.portal.spi.UserService;
import com.ctrip.framework.apollo.tracer.Tracer;
import org.slf4j.Logger;
......@@ -35,17 +37,17 @@ public class AppService {
@Autowired
private UserInfoHolder userInfoHolder;
@Autowired
private AdminServiceAPI.AppAPI appAPI;
@Autowired
private AppRepository appRepository;
@Autowired
private ClusterService clusterService;
@Autowired
private AppNamespaceService appNamespaceService;
@Autowired
private RoleInitializationService roleInitializationService;
@Autowired
private AdminServiceAPI.AppAPI appAPI;
@Autowired
private AppRepository appRepository;
private UserService userService;
public List<App> findAll() {
......@@ -56,11 +58,11 @@ public class AppService {
return Lists.newArrayList((apps));
}
public List<App> findByAppIds(Set<String> appIds){
public List<App> findByAppIds(Set<String> appIds) {
return appRepository.findByAppIdIn(appIds);
}
public List<App> findByOwnerName(String ownerName, Pageable page){
public List<App> findByOwnerName(String ownerName, Pageable page) {
return appRepository.findByOwnerName(ownerName, page);
}
......@@ -100,16 +102,27 @@ public class AppService {
App managedApp = appRepository.findByAppId(appId);
if (managedApp != null) {
throw new BadRequestException(String.format("app id %s already exists!", app.getAppId()));
} else {
App createdApp = appRepository.save(app);
appNamespaceService.createDefaultAppNamespace(appId);
//role
roleInitializationService.initAppRoles(createdApp);
Tracer.logEvent(CatEventType.CREATE_APP, appId);
return createdApp;
throw new BadRequestException(String.format("App already exists. AppId = %s", appId));
}
UserInfo owner = userService.findByUserId(app.getOwnerName());
if (owner == null) {
throw new BadRequestException("Application's owner not exist.");
}
app.setOwnerEmail(owner.getEmail());
String operator = userInfoHolder.getUser().getUserId();
app.setDataChangeCreatedBy(operator);
app.setDataChangeLastModifiedBy(operator);
App createdApp = appRepository.save(app);
appNamespaceService.createDefaultAppNamespace(appId);
roleInitializationService.initAppRoles(createdApp);
Tracer.logEvent(CatEventType.CREATE_APP, appId);
return createdApp;
}
public EnvClusterInfo createEnvNavNode(Env env, String appId) {
......
......@@ -76,7 +76,7 @@ public class RoleInitializationService {
Set<Permission> appPermissions =
FluentIterable.from(Lists.newArrayList(
PermissionType.CREATE_CLUSTER, PermissionType.CREATE_NAMESPACE, PermissionType.ASSIGN_ROLE))
.transform(permissionType -> createPermisson(appId, permissionType)).toSet();
.transform(permissionType -> createPermission(appId, permissionType)).toSet();
Set<Permission> createdAppPermissions = rolePermissionService.createPermissions(appPermissions);
Set<Long>
appPermissionIds =
......@@ -88,9 +88,9 @@ public class RoleInitializationService {
rolePermissionService.createRoleWithPermissions(appMasterRole, appPermissionIds);
}
private Permission createPermisson(String targetId, String permisson) {
private Permission createPermission(String targetId, String permissionType) {
Permission permission = new Permission();
permission.setPermissionType(permisson);
permission.setPermissionType(permissionType);
permission.setTargetId(targetId);
String userId = userInfoHolder.getUser().getUserId();
permission.setDataChangeCreatedBy(userId);
......@@ -101,17 +101,17 @@ public class RoleInitializationService {
private Role createRole(String roleName) {
Role role = new Role();
role.setRoleName(roleName);
String operaterUserId = userInfoHolder.getUser().getUserId();
role.setDataChangeCreatedBy(operaterUserId);
role.setDataChangeLastModifiedBy(operaterUserId);
String operator = userInfoHolder.getUser().getUserId();
role.setDataChangeCreatedBy(operator);
role.setDataChangeLastModifiedBy(operator);
return role;
}
private void createDefaultNamespaceRole(String appId, String namespaceName, String permissionType, String roleName) {
Permission permisson =
createPermisson(RoleUtils.buildNamespaceTargetId(appId, namespaceName), permissionType);
Permission createdPermission = rolePermissionService.createPermission(permisson);
Permission permission =
createPermission(RoleUtils.buildNamespaceTargetId(appId, namespaceName), permissionType);
Permission createdPermission = rolePermissionService.createPermission(permission);
Role role = createRole(roleName);
rolePermissionService
......
......@@ -29,7 +29,7 @@
<form class="form-horizontal" name="appForm" ng-controller="CreateAppController" valdr-type="App"
ng-submit="create()">
<div class="form-group">
<label class="col-sm-2 control-label">
<label class="col-sm-3 control-label">
<apollorequiredfield></apollorequiredfield>
部门</label>
<div class="col-sm-3">
......@@ -39,7 +39,7 @@
</div>
</div>
<div class="form-group" valdr-form-group>
<label class="col-sm-2 control-label">
<label class="col-sm-3 control-label">
<apollorequiredfield></apollorequiredfield>
应用Id</label>
<div class="col-sm-3">
......@@ -48,7 +48,7 @@
</div>
</div>
<div class="form-group" valdr-form-group>
<label class="col-sm-2 control-label">
<label class="col-sm-3 control-label">
<apollorequiredfield></apollorequiredfield>
应用名称</label>
<div class="col-sm-5">
......@@ -57,18 +57,31 @@
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">
<label class="col-sm-3 control-label">
<apollorequiredfield></apollorequiredfield>
应用负责人</label>
<div class="col-sm-6">
<apollouserselector apollo-id="userSelectWidgetId"></apollouserselector>
<div class="col-sm-6 J_ownerSelectorPanel">
<apollouserselector apollo-id="'ownerSelector'"></apollouserselector>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">项目管理员<br>
</label>
<div class="col-sm-9 J_adminSelectorPanel">
<apollomultipleuserselector apollo-id="'adminSelector'"></apollomultipleuserselector>
<br>
<small>(负责人具有项目管理的最高权限,比如分配配置的修改权,发布权等)</small>
<small>(应用负责人默认具有项目管理员权限,</small>
<br>
<small>项目管理员可以创建Namespace和集群、分配用户权限)</small>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<div class="col-sm-offset-3 col-sm-9">
<button type="submit" class="btn btn-primary"
ng-disabled="appForm.$invalid || submitBtnDisabled">提交
......
......@@ -35,7 +35,7 @@
<div class="row">
<div class="form-horizontal">
<div class="form-group">
<label class="col-sm-2 control-label">管理员<br><small>(可以创建Namspace<br>可以创建集群<br>可以分配用户权限)</small></label>
<label class="col-sm-2 control-label">管理员<br><small>(可以创建Namespace<br>可以创建集群<br>可以分配用户权限)</small></label>
<div class="col-sm-8">
<form class="form-inline" ng-submit="assignMasterRoleToUser()">
<div class="form-group">
......
app_module.controller('CreateAppController', ['$scope', '$window', 'toastr', 'AppService', 'UserService', 'AppUtil', 'OrganizationService',
function ($scope, $window, toastr, AppService, UserService, AppUtil, OrganizationService) {
app_module.controller('CreateAppController',
['$scope', '$window', 'toastr', 'AppService', 'AppUtil', 'OrganizationService',
createAppController]);
$scope.submitBtnDisabled = false;
function createAppController($scope, $window, toastr, AppService, AppUtil, OrganizationService) {
$scope.app = {};
$scope.submitBtnDisabled = false;
$scope.create = create;
init();
function init() {
initOrganization();
}
function initOrganization() {
OrganizationService.find_organizations().then(function (result) {
var organizations = [];
result.forEach(function (item) {
......@@ -13,53 +26,72 @@ app_module.controller('CreateAppController', ['$scope', '$window', 'toastr', 'Ap
organizations.push(org);
});
$('#organization').select2({
placeholder: '请选择部门',
width: '100%',
data: organizations
});
placeholder: '请选择部门',
width: '100%',
data: organizations
});
}, function (result) {
toastr.error(AppUtil.errorMsg(result), "load organizations error");
});
}
$scope.app = {};
UserService.load_user().then(function (result) {
$scope.app.ownerName = result.userId;
}, function (result) {
function create() {
$scope.submitBtnDisabled = true;
});
var selectedOrg = $('#organization').select2('data')[0];
if (!selectedOrg.id) {
toastr.warning("请选择部门");
return;
}
$scope.app.orgId = selectedOrg.id;
$scope.app.orgName = selectedOrg.name;
$scope.userSelectWidgetId = "userSelectWidgetId";
$scope.create = function () {
var selectedOrg = $('#organization').select2('data')[0];
if (!selectedOrg.id) {
toastr.warning("请选择部门");
return;
}
$scope.app.orgId = selectedOrg.id;
$scope.app.orgName = selectedOrg.name;
// ownerName
var user = $('.' + $scope.userSelectWidgetId).select2('data')[0];
if (!user){
toastr.warning("请输入应用负责人");
return;
}
$scope.app.ownerName = user.id;
$scope.submitBtnDisabled = true;
AppService.create($scope.app).then(function (result) {
toastr.success('添加成功!');
setInterval(function () {
$scope.submitBtnDisabled = false;
$window.location.href = '/config.html?#appid=' + result.appId;
}, 1000);
}, function (result) {
// owner
var owner = $('.ownerSelector').select2('data')[0];
if (!owner) {
toastr.warning("请输入应用负责人");
return;
}
$scope.app.ownerName = owner.id;
//admins
$scope.app.admins = [];
var admins = $(".adminSelector").select2('data');
if (admins) {
admins.forEach(function (admin) {
$scope.app.admins.push(admin.id);
})
}
AppService.create($scope.app).then(function (result) {
toastr.success('创建成功!');
setInterval(function () {
$scope.submitBtnDisabled = false;
toastr.error(AppUtil.errorMsg(result), '添加失败!');
});
};
$window.location.href = '/config.html?#appid=' + result.appId;
}, 1000);
}, function (result) {
$scope.submitBtnDisabled = false;
toastr.error(AppUtil.errorMsg(result), '创建失败!');
});
}
$(".J_ownerSelectorPanel").on("select2:select", ".ownerSelector", selectEventHandler);
var $adminSelectorPanel = $(".J_adminSelectorPanel");
$adminSelectorPanel.on("select2:select", ".adminSelector", selectEventHandler);
$adminSelectorPanel.on("select2:unselect", ".adminSelector", selectEventHandler);
function selectEventHandler() {
$('.J_owner').remove();
var owner = $('.ownerSelector').select2('data')[0];
}]);
if (owner) {
$(".adminSelector").parent().find(".select2-selection__rendered").prepend(
'<li class="select2-selection__choice J_owner">'
+ owner.text + '</li>')
}
}
}
......@@ -8,7 +8,6 @@ directive_module.directive('apollonav',
replace: true,
link: function (scope, element, attrs) {
scope.sourceApps = [];
scope.copyedApps = [];
......@@ -226,7 +225,7 @@ directive_module.directive('apolloconfirmdialog', function ($compile, $window) {
detail: '=apolloDetail',
showCancelBtn: '=apolloShowCancelBtn',
doConfirm: '=apolloConfirm',
cancel:'='
cancel: '='
},
link: function (scope, element, attrs) {
......@@ -287,7 +286,7 @@ directive_module.directive('apollouserselector', function ($compile, $window) {
data.forEach(function (user) {
users.push({
id: user.userId,
text: user.userId + " | " + user.name + " | " + user.email
text: user.userId + " | " + user.name
})
});
return {
......@@ -308,4 +307,53 @@ directive_module.directive('apollouserselector', function ($compile, $window) {
}
});
directive_module.directive('apollomultipleuserselector', function ($compile, $window) {
return {
restrict: 'E',
templateUrl: '../../views/component/multiple-user-selector.html',
transclude: true,
replace: true,
scope: {
id: '=apolloId'
},
link: function (scope, element, attrs) {
scope.$watch("id", initSelect2);
var searchUsersAjax = {
ajax: {
url: '/users',
dataType: 'json',
delay: 250,
data: function (params) {
return {
keyword: params.term ? params.term : '',
limit: 100
}
},
processResults: function (data, params) {
var users = [];
data.forEach(function (user) {
users.push({
id: user.userId,
text: user.userId + " | " + user.name
})
});
return {
results: users
}
},
cache: true,
minimumInputLength: 5
}
};
function initSelect2() {
$('.' + scope.id).select2(searchUsersAjax);
}
}
}
});
<select class="{{id}}" style="width: 450px;" multiple="multiple">
</select>
......@@ -3,10 +3,10 @@ package com.ctrip.framework.apollo.portal;
import com.google.gson.Gson;
import com.ctrip.framework.apollo.common.entity.App;
import com.ctrip.framework.apollo.common.exception.ServiceException;
import com.ctrip.framework.apollo.portal.controller.AppController;
import com.ctrip.framework.apollo.portal.spi.UserService;
import com.ctrip.framework.apollo.portal.entity.model.AppModel;
import com.ctrip.framework.apollo.portal.service.AppService;
import org.junit.Assert;
import org.junit.Test;
......@@ -30,7 +30,7 @@ public class ServiceExceptionTest extends AbstractUnitTest {
@InjectMocks
private AppController appController;
@Mock
private UserService userService;
private AppService appService;
@Test
......@@ -51,9 +51,9 @@ public class ServiceExceptionTest extends AbstractUnitTest {
new HttpServerErrorException(HttpStatus.INTERNAL_SERVER_ERROR, "admin server error",
new Gson().toJson(errorAttributes).getBytes(), Charset.defaultCharset());
when(userService.findByUserId(any(String.class))).thenThrow(adminException);
when(appService.create(any())).thenThrow(adminException);
App app = generateSampleApp();
AppModel app = generateSampleApp();
try {
appController.create(app);
} catch (HttpStatusCodeException e) {
......@@ -65,14 +65,13 @@ public class ServiceExceptionTest extends AbstractUnitTest {
}
}
private App generateSampleApp() {
App app = new App();
private AppModel generateSampleApp() {
AppModel app = new AppModel();
app.setAppId("someAppId");
app.setName("someName");
app.setOrgId("someOrgId");
app.setOrgName("someOrgNam");
app.setOwnerName("someOwner");
app.setOwnerEmail("someOwner@ctrip.com");
return app;
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册