Merge branch 'v1.1'
# Conflicts: # backend/src/main/java/io/metersphere/api/service/APITestService.java
This commit is contained in:
commit
2660ebb975
|
@ -95,7 +95,7 @@ public class APITestController {
|
||||||
|
|
||||||
@PostMapping(value = "/import", consumes = {"multipart/form-data"})
|
@PostMapping(value = "/import", consumes = {"multipart/form-data"})
|
||||||
@RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR)
|
@RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR)
|
||||||
public ApiTest testCaseImport(@RequestPart(value = "file") MultipartFile file, @RequestPart("request") ApiTestImportRequest request) {
|
public ApiTest testCaseImport(@RequestPart(value = "file", required = false) MultipartFile file, @RequestPart("request") ApiTestImportRequest request) {
|
||||||
return apiTestService.apiTestImport(file, request);
|
return apiTestService.apiTestImport(file, request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,7 @@ import org.springframework.web.multipart.MultipartFile;
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.File;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
|
@ -5,6 +5,7 @@ import io.metersphere.base.domain.ApiTestEnvironmentWithBLOBs;
|
||||||
import io.metersphere.base.mapper.ApiTestEnvironmentMapper;
|
import io.metersphere.base.mapper.ApiTestEnvironmentMapper;
|
||||||
import io.metersphere.commons.exception.MSException;
|
import io.metersphere.commons.exception.MSException;
|
||||||
import io.metersphere.i18n.Translator;
|
import io.metersphere.i18n.Translator;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
@ -34,6 +35,7 @@ public class ApiTestEnvironmentService {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void update(ApiTestEnvironmentWithBLOBs apiTestEnvironment) {
|
public void update(ApiTestEnvironmentWithBLOBs apiTestEnvironment) {
|
||||||
|
checkEnvironmentExist(apiTestEnvironment);
|
||||||
apiTestEnvironmentMapper.updateByPrimaryKeyWithBLOBs(apiTestEnvironment);
|
apiTestEnvironmentMapper.updateByPrimaryKeyWithBLOBs(apiTestEnvironment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,9 +49,12 @@ public class ApiTestEnvironmentService {
|
||||||
private void checkEnvironmentExist (ApiTestEnvironmentWithBLOBs environment) {
|
private void checkEnvironmentExist (ApiTestEnvironmentWithBLOBs environment) {
|
||||||
if (environment.getName() != null) {
|
if (environment.getName() != null) {
|
||||||
ApiTestEnvironmentExample example = new ApiTestEnvironmentExample();
|
ApiTestEnvironmentExample example = new ApiTestEnvironmentExample();
|
||||||
example.createCriteria()
|
ApiTestEnvironmentExample.Criteria criteria = example.createCriteria();
|
||||||
.andNameEqualTo(environment.getName())
|
criteria.andNameEqualTo(environment.getName())
|
||||||
.andProjectIdEqualTo(environment.getProjectId());
|
.andProjectIdEqualTo(environment.getProjectId());
|
||||||
|
if (StringUtils.isNotBlank(environment.getId())) {
|
||||||
|
criteria.andIdNotEqualTo(environment.getId());
|
||||||
|
}
|
||||||
if (apiTestEnvironmentMapper.selectByExample(example).size() > 0) {
|
if (apiTestEnvironmentMapper.selectByExample(example).size() > 0) {
|
||||||
MSException.throwException(Translator.get("api_test_environment_already_exists"));
|
MSException.throwException(Translator.get("api_test_environment_already_exists"));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,100 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
|
||||||
<mapper namespace="io.metersphere.base.mapper.ext.ExtTestPlanTestCaseMapper">
|
<mapper namespace="io.metersphere.base.mapper.ext.ExtTestPlanTestCaseMapper">
|
||||||
|
<sql id="condition">
|
||||||
|
<choose>
|
||||||
|
<when test='${object}.operator == "like"'>
|
||||||
|
like CONCAT('%', #{${object}.value},'%')
|
||||||
|
</when>
|
||||||
|
<when test='${object}.operator == "not like"'>
|
||||||
|
not like CONCAT('%', #{${object}.value},'%')
|
||||||
|
</when>
|
||||||
|
<when test='${object}.operator == "in"'>
|
||||||
|
in
|
||||||
|
<foreach collection="${object}.value" item="v" separator="," open="(" close=")">
|
||||||
|
#{v}
|
||||||
|
</foreach>
|
||||||
|
</when>
|
||||||
|
<when test='${object}.operator == "not in"'>
|
||||||
|
not in
|
||||||
|
<foreach collection="${object}.value" item="v" separator="," open="(" close=")">
|
||||||
|
#{v}
|
||||||
|
</foreach>
|
||||||
|
</when>
|
||||||
|
<when test='${object}.operator == "between"'>
|
||||||
|
between #{${object}.value[0]} and #{${object}.value[1]}
|
||||||
|
</when>
|
||||||
|
<when test='${object}.operator == "gt"'>
|
||||||
|
> #{${object}.value}
|
||||||
|
</when>
|
||||||
|
<when test='${object}.operator == "lt"'>
|
||||||
|
< #{${object}.value}
|
||||||
|
</when>
|
||||||
|
<when test='${object}.operator == "ge"'>
|
||||||
|
>= #{${object}.value}
|
||||||
|
</when>
|
||||||
|
<when test='${object}.operator == "le"'>
|
||||||
|
<= #{${object}.value}
|
||||||
|
</when>
|
||||||
|
<when test='${object}.operator == "current user"'>
|
||||||
|
= '${@io.metersphere.commons.utils.SessionUtils@getUserId()}'
|
||||||
|
</when>
|
||||||
|
<otherwise>
|
||||||
|
= #{${object}.value}
|
||||||
|
</otherwise>
|
||||||
|
</choose>
|
||||||
|
</sql>
|
||||||
|
|
||||||
|
<sql id="combine">
|
||||||
|
<if test="${condition}.name != null">
|
||||||
|
and test_case.name
|
||||||
|
<include refid="condition">
|
||||||
|
<property name="object" value="${condition}.name"/>
|
||||||
|
</include>
|
||||||
|
</if>
|
||||||
|
<if test="${condition}.module != null">
|
||||||
|
and test_case.node_path
|
||||||
|
<include refid="condition">
|
||||||
|
<property name="object" value="${condition}.module"/>
|
||||||
|
</include>
|
||||||
|
</if>
|
||||||
|
<if test="${condition}.priority != null">
|
||||||
|
and test_case.priority
|
||||||
|
<include refid="condition">
|
||||||
|
<property name="object" value="${condition}.priority"/>
|
||||||
|
</include>
|
||||||
|
</if>
|
||||||
|
<if test="${condition}.createTime != null">
|
||||||
|
and test_case.create_time
|
||||||
|
<include refid="condition">
|
||||||
|
<property name="object" value="${condition}.createTime"/>
|
||||||
|
</include>
|
||||||
|
</if>
|
||||||
|
<if test="${condition}.type != null">
|
||||||
|
and test_case.type
|
||||||
|
<include refid="condition">
|
||||||
|
<property name="object" value="${condition}.type"/>
|
||||||
|
</include>
|
||||||
|
</if>
|
||||||
|
<if test="${condition}.updateTime != null">
|
||||||
|
and test_case.update_time
|
||||||
|
<include refid="condition">
|
||||||
|
<property name="object" value="${condition}.updateTime"/>
|
||||||
|
</include>
|
||||||
|
</if>
|
||||||
|
<if test="${condition}.method != null">
|
||||||
|
and test_case.method
|
||||||
|
<include refid="condition">
|
||||||
|
<property name="object" value="${condition}.method"/>
|
||||||
|
</include>
|
||||||
|
</if>
|
||||||
|
<if test="${condition}.creator != null">
|
||||||
|
and test_case.maintainer
|
||||||
|
<include refid="condition">
|
||||||
|
<property name="object" value="${condition}.creator"/>
|
||||||
|
</include>
|
||||||
|
</if>
|
||||||
|
</sql>
|
||||||
|
|
||||||
<select id="getReportMetric" parameterType="java.lang.String"
|
<select id="getReportMetric" parameterType="java.lang.String"
|
||||||
resultType="io.metersphere.track.dto.TestCaseReportStatusResultDTO">
|
resultType="io.metersphere.track.dto.TestCaseReportStatusResultDTO">
|
||||||
|
@ -25,9 +119,20 @@
|
||||||
from test_plan_test_case
|
from test_plan_test_case
|
||||||
inner join test_case on test_plan_test_case.case_id = test_case.id
|
inner join test_case on test_plan_test_case.case_id = test_case.id
|
||||||
<where>
|
<where>
|
||||||
<if test="request.name != null">
|
<choose>
|
||||||
and test_case.name like CONCAT('%', #{request.name},'%')
|
<!--高级-->
|
||||||
</if>
|
<when test="request.combine != null">
|
||||||
|
<include refid="combine">
|
||||||
|
<property name="condition" value="request.combine"/>
|
||||||
|
</include>
|
||||||
|
</when>
|
||||||
|
<!--普通-->
|
||||||
|
<otherwise>
|
||||||
|
<if test="request.name != null">
|
||||||
|
and test_case.name like CONCAT('%', #{request.name},'%')
|
||||||
|
</if>
|
||||||
|
</otherwise>
|
||||||
|
</choose>
|
||||||
<if test="request.id != null">
|
<if test="request.id != null">
|
||||||
and test_case.id = #{request.id}
|
and test_case.id = #{request.id}
|
||||||
</if>
|
</if>
|
||||||
|
|
|
@ -14,11 +14,13 @@ import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
|
||||||
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
|
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
|
||||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
|
import org.springframework.context.EnvironmentAware;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.annotation.DependsOn;
|
import org.springframework.context.annotation.DependsOn;
|
||||||
import org.springframework.context.event.ContextRefreshedEvent;
|
import org.springframework.context.event.ContextRefreshedEvent;
|
||||||
import org.springframework.context.event.EventListener;
|
import org.springframework.context.event.EventListener;
|
||||||
|
import org.springframework.core.env.Environment;
|
||||||
|
|
||||||
import javax.servlet.DispatcherType;
|
import javax.servlet.DispatcherType;
|
||||||
import javax.servlet.Filter;
|
import javax.servlet.Filter;
|
||||||
|
@ -27,7 +29,8 @@ import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
public class ShiroConfig {
|
public class ShiroConfig implements EnvironmentAware {
|
||||||
|
private Environment env;
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager sessionManager) {
|
public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager sessionManager) {
|
||||||
|
@ -115,9 +118,11 @@ public class ShiroConfig {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public SessionManager sessionManager(MemoryConstrainedCacheManager memoryConstrainedCacheManager) {
|
public SessionManager sessionManager(MemoryConstrainedCacheManager memoryConstrainedCacheManager) {
|
||||||
|
Long sessionTimeout = env.getProperty("session.timeout", Long.class, 1800L); // 默认1800s, 半个小时
|
||||||
|
|
||||||
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
|
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
|
||||||
sessionManager.setSessionIdUrlRewritingEnabled(false);
|
sessionManager.setSessionIdUrlRewritingEnabled(false);
|
||||||
sessionManager.setGlobalSessionTimeout(1800000L);
|
sessionManager.setGlobalSessionTimeout(sessionTimeout * 1000);// 超时时间ms
|
||||||
sessionManager.setDeleteInvalidSessions(true);
|
sessionManager.setDeleteInvalidSessions(true);
|
||||||
sessionManager.setSessionValidationSchedulerEnabled(true);
|
sessionManager.setSessionValidationSchedulerEnabled(true);
|
||||||
SimpleCookie sessionIdCookie = new SimpleCookie();
|
SimpleCookie sessionIdCookie = new SimpleCookie();
|
||||||
|
@ -137,4 +142,9 @@ public class ShiroConfig {
|
||||||
ShiroDBRealm shiroDBRealm = (ShiroDBRealm) context.getBean("shiroDBRealm");
|
ShiroDBRealm shiroDBRealm = (ShiroDBRealm) context.getBean("shiroDBRealm");
|
||||||
((DefaultWebSecurityManager) context.getBean("securityManager")).setRealm(shiroDBRealm);
|
((DefaultWebSecurityManager) context.getBean("securityManager")).setRealm(shiroDBRealm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setEnvironment(Environment environment) {
|
||||||
|
this.env = environment;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,12 +7,12 @@ import io.metersphere.commons.exception.MSException;
|
||||||
import io.metersphere.controller.ResultHolder;
|
import io.metersphere.controller.ResultHolder;
|
||||||
import io.metersphere.controller.request.LoginRequest;
|
import io.metersphere.controller.request.LoginRequest;
|
||||||
import io.metersphere.i18n.Translator;
|
import io.metersphere.i18n.Translator;
|
||||||
import io.metersphere.ldap.domain.Person;
|
|
||||||
import io.metersphere.ldap.service.LdapService;
|
import io.metersphere.ldap.service.LdapService;
|
||||||
import io.metersphere.service.SystemParameterService;
|
import io.metersphere.service.SystemParameterService;
|
||||||
import io.metersphere.service.UserService;
|
import io.metersphere.service.UserService;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.shiro.SecurityUtils;
|
import org.apache.shiro.SecurityUtils;
|
||||||
|
import org.springframework.ldap.core.DirContextOperations;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
|
@ -36,29 +36,37 @@ public class LdapController {
|
||||||
MSException.throwException(Translator.get("ldap_authentication_not_enabled"));
|
MSException.throwException(Translator.get("ldap_authentication_not_enabled"));
|
||||||
}
|
}
|
||||||
|
|
||||||
Person person = ldapService.authenticate(request);
|
DirContextOperations dirContext = ldapService.authenticate(request);
|
||||||
|
String email = ldapService.getMappingAttr("email", dirContext);
|
||||||
|
String userId = ldapService.getMappingAttr("username", dirContext);
|
||||||
|
|
||||||
SecurityUtils.getSubject().getSession().setAttribute("authenticate", UserSource.LDAP.name());
|
SecurityUtils.getSubject().getSession().setAttribute("authenticate", UserSource.LDAP.name());
|
||||||
|
SecurityUtils.getSubject().getSession().setAttribute("email", email);
|
||||||
|
|
||||||
String username = request.getUsername();
|
|
||||||
|
|
||||||
String email = person.getEmail();
|
|
||||||
|
|
||||||
if (StringUtils.isBlank(email)) {
|
if (StringUtils.isBlank(email)) {
|
||||||
MSException.throwException(Translator.get("login_fail_email_null"));
|
MSException.throwException(Translator.get("login_fail_email_null"));
|
||||||
}
|
}
|
||||||
|
|
||||||
User u = userService.selectUser(request.getUsername());
|
// userId 或 email 有一个相同即为存在本地用户
|
||||||
|
User u = userService.selectUser(userId, email);
|
||||||
if (u == null) {
|
if (u == null) {
|
||||||
|
|
||||||
|
// 新建用户 获取LDAP映射属性
|
||||||
|
String name = ldapService.getMappingAttr("name", dirContext);
|
||||||
|
|
||||||
User user = new User();
|
User user = new User();
|
||||||
user.setId(username);
|
user.setId(userId);
|
||||||
user.setName(username);
|
user.setName(name);
|
||||||
user.setEmail(email);
|
user.setEmail(email);
|
||||||
user.setSource(UserSource.LDAP.name());
|
user.setSource(UserSource.LDAP.name());
|
||||||
userService.addLdapUser(user);
|
userService.addLdapUser(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
return userService.login(request);
|
// 执行 ShiroDBRealm 中 LDAP 登录逻辑
|
||||||
|
LoginRequest loginRequest = new LoginRequest();
|
||||||
|
loginRequest.setUsername(userId);
|
||||||
|
return userService.login(loginRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/test/connect")
|
@PostMapping("/test/connect")
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
package io.metersphere.ldap.dao;
|
|
||||||
|
|
||||||
|
|
||||||
import io.metersphere.ldap.domain.Person;
|
|
||||||
|
|
||||||
public interface PersonRepo {
|
|
||||||
|
|
||||||
Person getDnForUser(String name);
|
|
||||||
}
|
|
|
@ -1,177 +0,0 @@
|
||||||
package io.metersphere.ldap.dao;
|
|
||||||
|
|
||||||
|
|
||||||
import io.metersphere.commons.constants.ParamConstants;
|
|
||||||
import io.metersphere.commons.exception.MSException;
|
|
||||||
import io.metersphere.commons.utils.EncryptUtils;
|
|
||||||
import io.metersphere.i18n.Translator;
|
|
||||||
import io.metersphere.ldap.domain.Person;
|
|
||||||
import io.metersphere.service.SystemParameterService;
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
import org.apache.shiro.realm.ldap.LdapUtils;
|
|
||||||
import org.springframework.ldap.AuthenticationException;
|
|
||||||
import org.springframework.ldap.InvalidNameException;
|
|
||||||
import org.springframework.ldap.InvalidSearchFilterException;
|
|
||||||
import org.springframework.ldap.NameNotFoundException;
|
|
||||||
import org.springframework.ldap.core.*;
|
|
||||||
import org.springframework.ldap.core.support.AbstractContextMapper;
|
|
||||||
import org.springframework.ldap.core.support.DefaultDirObjectFactory;
|
|
||||||
import org.springframework.ldap.core.support.LdapContextSource;
|
|
||||||
import org.springframework.ldap.query.SearchScope;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
|
||||||
import javax.naming.directory.DirContext;
|
|
||||||
import javax.naming.ldap.LdapContext;
|
|
||||||
import java.util.Hashtable;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import static org.springframework.ldap.query.LdapQueryBuilder.query;
|
|
||||||
|
|
||||||
@Service
|
|
||||||
public class PersonRepoImpl implements PersonRepo {
|
|
||||||
|
|
||||||
@Resource
|
|
||||||
private SystemParameterService service;
|
|
||||||
|
|
||||||
public boolean authenticate(String dn, String credentials) {
|
|
||||||
LdapTemplate ldapTemplate = getConnection();
|
|
||||||
return authenticate(dn, credentials, ldapTemplate);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean authenticate(String dn, String credentials, LdapTemplate ldapTemplate) throws AuthenticationException {
|
|
||||||
DirContext ctx = null;
|
|
||||||
try {
|
|
||||||
ctx = ldapTemplate.getContextSource().getContext(dn, credentials);
|
|
||||||
return true;
|
|
||||||
} finally {
|
|
||||||
// It is imperative that the created DirContext instance is always closed
|
|
||||||
LdapUtils.closeContext((LdapContext) ctx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Person getDnForUser(String username) {
|
|
||||||
LdapTemplate ldapTemplate = getConnection();
|
|
||||||
|
|
||||||
String filter = getUserFilter();
|
|
||||||
String[] arr = getUserOu();
|
|
||||||
|
|
||||||
List<Person> result = null;
|
|
||||||
for (String ou : arr) {
|
|
||||||
try {
|
|
||||||
result = ldapTemplate.search(query().base(ou.trim()).filter(filter, username), getContextMapper());
|
|
||||||
if (result.size() == 1) {
|
|
||||||
return result.get(0);
|
|
||||||
}
|
|
||||||
} catch (NameNotFoundException e) {
|
|
||||||
MSException.throwException(Translator.get("login_fail_ou_error"));
|
|
||||||
} catch (InvalidNameException e) {
|
|
||||||
MSException.throwException(Translator.get("login_fail_ou_error"));
|
|
||||||
} catch (InvalidSearchFilterException e) {
|
|
||||||
MSException.throwException(Translator.get("login_fail_filter_error"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result.size() != 1) {
|
|
||||||
MSException.throwException(Translator.get("user_not_found_or_not_unique"));
|
|
||||||
}
|
|
||||||
|
|
||||||
return result.get(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getUserFilter() {
|
|
||||||
String filter = service.getValue(ParamConstants.LDAP.FILTER.getValue());
|
|
||||||
|
|
||||||
if (StringUtils.isBlank(filter)) {
|
|
||||||
MSException.throwException(Translator.get("ldap_user_filter_is_null"));
|
|
||||||
}
|
|
||||||
|
|
||||||
return filter;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String[] getUserOu() {
|
|
||||||
String ou = service.getValue(ParamConstants.LDAP.OU.getValue());
|
|
||||||
|
|
||||||
if (StringUtils.isBlank(ou)) {
|
|
||||||
MSException.throwException(Translator.get("ldap_ou_is_null"));
|
|
||||||
}
|
|
||||||
|
|
||||||
String[] arr = ou.split("\\|");
|
|
||||||
|
|
||||||
return arr;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected ContextMapper getContextMapper() {
|
|
||||||
return new PersonContextMapper();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class PersonContextMapper extends AbstractContextMapper<Person> {
|
|
||||||
@Override
|
|
||||||
public Person doMapFromContext(DirContextOperations context) {
|
|
||||||
Person person = new Person();
|
|
||||||
person.setDn(context.getNameInNamespace());
|
|
||||||
person.setUid(context.getStringAttribute("uid"));
|
|
||||||
person.setCommonName(context.getStringAttribute("cn"));
|
|
||||||
person.setSurName(context.getStringAttribute("sn"));
|
|
||||||
person.setUsername(context.getStringAttribute("sAMAccountName"));
|
|
||||||
person.setEmail(context.getStringAttribute("mail"));
|
|
||||||
return person;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public LdapTemplate getConnection() {
|
|
||||||
|
|
||||||
String url = service.getValue(ParamConstants.LDAP.URL.getValue());
|
|
||||||
String dn = service.getValue(ParamConstants.LDAP.DN.getValue());
|
|
||||||
String password = service.getValue(ParamConstants.LDAP.PASSWORD.getValue());
|
|
||||||
|
|
||||||
preConnect(url, dn, password);
|
|
||||||
|
|
||||||
String credentials = EncryptUtils.aesDecrypt(password).toString();
|
|
||||||
|
|
||||||
LdapContextSource sourceLdapCtx = new LdapContextSource();
|
|
||||||
sourceLdapCtx.setUrl(url);
|
|
||||||
sourceLdapCtx.setUserDn(dn);
|
|
||||||
sourceLdapCtx.setPassword(credentials);
|
|
||||||
sourceLdapCtx.setDirObjectFactory(DefaultDirObjectFactory.class);
|
|
||||||
sourceLdapCtx.afterPropertiesSet();
|
|
||||||
LdapTemplate ldapTemplate = new LdapTemplate(sourceLdapCtx);
|
|
||||||
ldapTemplate.setIgnorePartialResultException(true);
|
|
||||||
Map<String, Object> baseEnv = new Hashtable<>();
|
|
||||||
baseEnv.put("com.sun.jndi.ldap.connect.timeout", "3000");
|
|
||||||
baseEnv.put("com.sun.jndi.ldap.read.timeout", "3000");
|
|
||||||
sourceLdapCtx.setBaseEnvironmentProperties(baseEnv);
|
|
||||||
ldapTemplate.setDefaultSearchScope(SearchScope.SUBTREE.getId());
|
|
||||||
|
|
||||||
// ldapTemplate 是否可用
|
|
||||||
try {
|
|
||||||
authenticate(dn, credentials, ldapTemplate);
|
|
||||||
} catch (AuthenticationException e) {
|
|
||||||
MSException.throwException(Translator.get("ldap_connect_fail_user"));
|
|
||||||
} catch (Exception e) {
|
|
||||||
MSException.throwException(Translator.get("ldap_connect_fail"));
|
|
||||||
}
|
|
||||||
|
|
||||||
return ldapTemplate;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void preConnect(String url, String dn, String password) {
|
|
||||||
|
|
||||||
if (StringUtils.isBlank(url)) {
|
|
||||||
MSException.throwException(Translator.get("ldap_url_is_null"));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (StringUtils.isBlank(dn)) {
|
|
||||||
MSException.throwException(Translator.get("ldap_dn_is_null"));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (StringUtils.isBlank(password)) {
|
|
||||||
MSException.throwException(Translator.get("ldap_password_is_null"));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,39 +1,221 @@
|
||||||
package io.metersphere.ldap.service;
|
package io.metersphere.ldap.service;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import io.metersphere.commons.constants.ParamConstants;
|
||||||
import io.metersphere.commons.exception.MSException;
|
import io.metersphere.commons.exception.MSException;
|
||||||
|
import io.metersphere.commons.utils.EncryptUtils;
|
||||||
import io.metersphere.controller.request.LoginRequest;
|
import io.metersphere.controller.request.LoginRequest;
|
||||||
import io.metersphere.i18n.Translator;
|
import io.metersphere.i18n.Translator;
|
||||||
import io.metersphere.ldap.dao.PersonRepoImpl;
|
import io.metersphere.service.SystemParameterService;
|
||||||
import io.metersphere.ldap.domain.Person;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.apache.shiro.realm.ldap.LdapUtils;
|
||||||
import org.springframework.ldap.AuthenticationException;
|
import org.springframework.ldap.AuthenticationException;
|
||||||
|
import org.springframework.ldap.InvalidNameException;
|
||||||
|
import org.springframework.ldap.InvalidSearchFilterException;
|
||||||
|
import org.springframework.ldap.NameNotFoundException;
|
||||||
|
import org.springframework.ldap.core.DirContextOperations;
|
||||||
|
import org.springframework.ldap.core.LdapTemplate;
|
||||||
|
import org.springframework.ldap.core.support.AbstractContextMapper;
|
||||||
|
import org.springframework.ldap.core.support.DefaultDirObjectFactory;
|
||||||
|
import org.springframework.ldap.core.support.LdapContextSource;
|
||||||
|
import org.springframework.ldap.query.SearchScope;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
|
import javax.naming.directory.DirContext;
|
||||||
|
import javax.naming.ldap.LdapContext;
|
||||||
|
import java.util.Hashtable;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static org.springframework.ldap.query.LdapQueryBuilder.query;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class LdapService {
|
public class LdapService {
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private PersonRepoImpl personRepo;
|
private SystemParameterService service;
|
||||||
|
|
||||||
|
public DirContextOperations authenticate(LoginRequest request) {
|
||||||
public Person authenticate(LoginRequest request) {
|
|
||||||
String username = request.getUsername();
|
String username = request.getUsername();
|
||||||
String credentials = request.getPassword();
|
String credentials = request.getPassword();
|
||||||
Person person = null;
|
DirContextOperations dirContextOperations = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
person = personRepo.getDnForUser(username);
|
// 获取LDAP用户相关信息
|
||||||
personRepo.authenticate(person.getDn(), credentials);
|
dirContextOperations = getContextMapper(username);
|
||||||
|
// 执行登录认证
|
||||||
|
authenticate(String.valueOf(dirContextOperations.getDn()), credentials);
|
||||||
} catch (AuthenticationException e) {
|
} catch (AuthenticationException e) {
|
||||||
MSException.throwException(Translator.get("authentication_failed"));
|
MSException.throwException(Translator.get("authentication_failed"));
|
||||||
}
|
}
|
||||||
|
|
||||||
return person;
|
// 检查属性是否存在
|
||||||
|
getMappingAttr("name", dirContextOperations);
|
||||||
|
getMappingAttr("email", dirContextOperations);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return dirContextOperations;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testConnect() {
|
public void testConnect() {
|
||||||
personRepo.getConnection();
|
getConnection();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean authenticate(String dn, String credentials) {
|
||||||
|
LdapTemplate ldapTemplate = getConnection();
|
||||||
|
return authenticate(dn, credentials, ldapTemplate);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean authenticate(String dn, String credentials, LdapTemplate ldapTemplate) throws AuthenticationException {
|
||||||
|
DirContext ctx = null;
|
||||||
|
try {
|
||||||
|
ctx = ldapTemplate.getContextSource().getContext(dn, credentials);
|
||||||
|
return true;
|
||||||
|
} finally {
|
||||||
|
// It is imperative that the created DirContext instance is always closed
|
||||||
|
LdapUtils.closeContext((LdapContext) ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public DirContextOperations getContextMapper(String username) {
|
||||||
|
LdapTemplate ldapTemplate = getConnection();
|
||||||
|
|
||||||
|
String filter = getUserFilter();
|
||||||
|
String[] arr = getUserOu();
|
||||||
|
|
||||||
|
List<DirContextOperations> result = null;
|
||||||
|
// 多OU
|
||||||
|
for (String ou : arr) {
|
||||||
|
try {
|
||||||
|
result = ldapTemplate.search(query().base(ou.trim()).filter(filter, username), new MsContextMapper());
|
||||||
|
if (result.size() == 1) {
|
||||||
|
return result.get(0);
|
||||||
|
}
|
||||||
|
} catch (NameNotFoundException e) {
|
||||||
|
MSException.throwException(Translator.get("login_fail_ou_error"));
|
||||||
|
} catch (InvalidNameException e) {
|
||||||
|
MSException.throwException(Translator.get("login_fail_ou_error"));
|
||||||
|
} catch (InvalidSearchFilterException e) {
|
||||||
|
MSException.throwException(Translator.get("login_fail_filter_error"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.size() != 1) {
|
||||||
|
MSException.throwException(Translator.get("user_not_found_or_not_unique"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getUserFilter() {
|
||||||
|
String filter = service.getValue(ParamConstants.LDAP.FILTER.getValue());
|
||||||
|
|
||||||
|
if (StringUtils.isBlank(filter)) {
|
||||||
|
MSException.throwException(Translator.get("ldap_user_filter_is_null"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String[] getUserOu() {
|
||||||
|
String ou = service.getValue(ParamConstants.LDAP.OU.getValue());
|
||||||
|
|
||||||
|
if (StringUtils.isBlank(ou)) {
|
||||||
|
MSException.throwException(Translator.get("ldap_ou_is_null"));
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] arr = ou.split("\\|");
|
||||||
|
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class MsContextMapper extends AbstractContextMapper<DirContextOperations> {
|
||||||
|
@Override
|
||||||
|
public DirContextOperations doMapFromContext(DirContextOperations context) {
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public LdapTemplate getConnection() {
|
||||||
|
|
||||||
|
String url = service.getValue(ParamConstants.LDAP.URL.getValue());
|
||||||
|
String dn = service.getValue(ParamConstants.LDAP.DN.getValue());
|
||||||
|
String password = service.getValue(ParamConstants.LDAP.PASSWORD.getValue());
|
||||||
|
|
||||||
|
preConnect(url, dn, password);
|
||||||
|
|
||||||
|
String credentials = EncryptUtils.aesDecrypt(password).toString();
|
||||||
|
|
||||||
|
LdapContextSource sourceLdapCtx = new LdapContextSource();
|
||||||
|
sourceLdapCtx.setUrl(url);
|
||||||
|
sourceLdapCtx.setUserDn(dn);
|
||||||
|
sourceLdapCtx.setPassword(credentials);
|
||||||
|
sourceLdapCtx.setDirObjectFactory(DefaultDirObjectFactory.class);
|
||||||
|
sourceLdapCtx.afterPropertiesSet();
|
||||||
|
LdapTemplate ldapTemplate = new LdapTemplate(sourceLdapCtx);
|
||||||
|
ldapTemplate.setIgnorePartialResultException(true);
|
||||||
|
Map<String, Object> baseEnv = new Hashtable<>();
|
||||||
|
baseEnv.put("com.sun.jndi.ldap.connect.timeout", "3000");
|
||||||
|
baseEnv.put("com.sun.jndi.ldap.read.timeout", "3000");
|
||||||
|
sourceLdapCtx.setBaseEnvironmentProperties(baseEnv);
|
||||||
|
ldapTemplate.setDefaultSearchScope(SearchScope.SUBTREE.getId());
|
||||||
|
|
||||||
|
// ldapTemplate 是否可用
|
||||||
|
try {
|
||||||
|
authenticate(dn, credentials, ldapTemplate);
|
||||||
|
} catch (AuthenticationException e) {
|
||||||
|
MSException.throwException(Translator.get("ldap_connect_fail_user"));
|
||||||
|
} catch (Exception e) {
|
||||||
|
MSException.throwException(Translator.get("ldap_connect_fail"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ldapTemplate;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void preConnect(String url, String dn, String password) {
|
||||||
|
|
||||||
|
if (StringUtils.isBlank(url)) {
|
||||||
|
MSException.throwException(Translator.get("ldap_url_is_null"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StringUtils.isBlank(dn)) {
|
||||||
|
MSException.throwException(Translator.get("ldap_dn_is_null"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StringUtils.isBlank(password)) {
|
||||||
|
MSException.throwException(Translator.get("ldap_password_is_null"));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getLdapMapping() {
|
||||||
|
String mapping = service.getValue(ParamConstants.LDAP.MAPPING.getValue());
|
||||||
|
|
||||||
|
if (StringUtils.isBlank(mapping)) {
|
||||||
|
MSException.throwException(Translator.get("ldap_user_mapping_is_null"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return mapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMappingAttr(String attr, DirContextOperations dirContext) {
|
||||||
|
// 检查LDAP映射属性
|
||||||
|
String mapping = getLdapMapping();
|
||||||
|
JSONObject jsonObject = JSONObject.parseObject(mapping);
|
||||||
|
|
||||||
|
String mapAttr = jsonObject.getString(attr);
|
||||||
|
if (StringUtils.isBlank(mapAttr)) {
|
||||||
|
MSException.throwException(Translator.get("check_ldap_mapping") + " " + attr);
|
||||||
|
}
|
||||||
|
|
||||||
|
String result = dirContext.getStringAttribute(mapAttr);
|
||||||
|
if (StringUtils.isBlank(result)) {
|
||||||
|
MSException.throwException(Translator.get("ldap_mapping_value_null") + " " + mapAttr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -112,15 +112,19 @@ public class ShiroDBRealm extends AuthorizingRealm {
|
||||||
|
|
||||||
|
|
||||||
private AuthenticationInfo loginLdapMode(String userId, String password) {
|
private AuthenticationInfo loginLdapMode(String userId, String password) {
|
||||||
//
|
// userId 或 email 有一个相同就返回User
|
||||||
|
String email = (String) SecurityUtils.getSubject().getSession().getAttribute("email");
|
||||||
UserDTO user = userService.getLoginUser(userId, Arrays.asList(UserSource.LDAP.name(), UserSource.LOCAL.name()));
|
UserDTO user = userService.getLoginUser(userId, Arrays.asList(UserSource.LDAP.name(), UserSource.LOCAL.name()));
|
||||||
String msg;
|
String msg;
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
msg = "The user does not exist: " + userId;
|
user = userService.getLoginUserByEmail(email, Arrays.asList(UserSource.LDAP.name(), UserSource.LOCAL.name()));
|
||||||
logger.warn(msg);
|
if (user == null) {
|
||||||
throw new UnknownAccountException(Translator.get("user_not_exist") + userId);
|
msg = "The user does not exist: " + userId;
|
||||||
|
logger.warn(msg);
|
||||||
|
throw new UnknownAccountException(Translator.get("user_not_exist") + userId);
|
||||||
|
}
|
||||||
|
userId = user.getId();
|
||||||
}
|
}
|
||||||
userId = user.getId();
|
|
||||||
|
|
||||||
SessionUser sessionUser = SessionUser.fromUser(user);
|
SessionUser sessionUser = SessionUser.fromUser(user);
|
||||||
SessionUtils.putUser(sessionUser);
|
SessionUtils.putUser(sessionUser);
|
||||||
|
@ -132,7 +136,7 @@ public class ShiroDBRealm extends AuthorizingRealm {
|
||||||
UserDTO user = userService.getLoginUser(userId, Collections.singletonList(UserSource.LOCAL.name()));
|
UserDTO user = userService.getLoginUser(userId, Collections.singletonList(UserSource.LOCAL.name()));
|
||||||
String msg;
|
String msg;
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
user = userService.getLoginUserByEmail(userId, UserSource.LOCAL.name());
|
user = userService.getLoginUserByEmail(userId, Collections.singletonList(UserSource.LOCAL.name()));
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
msg = "The user does not exist: " + userId;
|
msg = "The user does not exist: " + userId;
|
||||||
logger.warn(msg);
|
logger.warn(msg);
|
||||||
|
|
|
@ -78,8 +78,18 @@ public class UserService {
|
||||||
return getUserDTO(user.getId());
|
return getUserDTO(user.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
public User selectUser(String id) {
|
public User selectUser(String userId, String email) {
|
||||||
return userMapper.selectByPrimaryKey(id);
|
User user = userMapper.selectByPrimaryKey(userId);
|
||||||
|
if (user == null) {
|
||||||
|
UserExample example = new UserExample();
|
||||||
|
example.createCriteria().andEmailEqualTo(email);
|
||||||
|
List<User> users = userMapper.selectByExample(example);
|
||||||
|
if (!CollectionUtils.isEmpty(users)) {
|
||||||
|
return users.get(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return user;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void insertUserRole(List<Map<String, Object>> roles, String userId) {
|
private void insertUserRole(List<Map<String, Object>> roles, String userId) {
|
||||||
|
@ -199,9 +209,9 @@ public class UserService {
|
||||||
return getUserDTO(users.get(0).getId());
|
return getUserDTO(users.get(0).getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
public UserDTO getLoginUserByEmail(String email, String source) {
|
public UserDTO getLoginUserByEmail(String email, List<String> list) {
|
||||||
UserExample example = new UserExample();
|
UserExample example = new UserExample();
|
||||||
example.createCriteria().andEmailEqualTo(email).andSourceEqualTo(source);
|
example.createCriteria().andEmailEqualTo(email).andSourceIn(list);
|
||||||
List<User> users = userMapper.selectByExample(example);
|
List<User> users = userMapper.selectByExample(example);
|
||||||
if (users == null || users.size() <= 0) {
|
if (users == null || users.size() <= 0) {
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -33,4 +33,6 @@ public class QueryTestPlanCaseRequest extends TestPlanTestCase {
|
||||||
private String node;
|
private String node;
|
||||||
|
|
||||||
private String method;
|
private String method;
|
||||||
|
|
||||||
|
private Map<String, Object> combine;
|
||||||
}
|
}
|
||||||
|
|
|
@ -126,6 +126,7 @@ ldap_password_is_null=LDAP password is empty
|
||||||
ldap_connect_fail=Connection LDAP failed
|
ldap_connect_fail=Connection LDAP failed
|
||||||
ldap_connect_fail_user=Connection LDAP failed, wrong DN or password bound
|
ldap_connect_fail_user=Connection LDAP failed, wrong DN or password bound
|
||||||
ldap_user_filter_is_null=LDAP user filter is empty
|
ldap_user_filter_is_null=LDAP user filter is empty
|
||||||
|
ldap_user_mapping_is_null=LDAP user mapping is empty
|
||||||
authentication_failed=User authentication failed,wrong user name or password
|
authentication_failed=User authentication failed,wrong user name or password
|
||||||
user_not_found_or_not_unique=User does not exist or is not unique
|
user_not_found_or_not_unique=User does not exist or is not unique
|
||||||
find_more_user=Multiple users found
|
find_more_user=Multiple users found
|
||||||
|
@ -133,3 +134,5 @@ ldap_authentication_not_enabled=LDAP authentication is not enabled
|
||||||
login_fail_email_null=Login failed, user mailbox is empty
|
login_fail_email_null=Login failed, user mailbox is empty
|
||||||
login_fail_ou_error=Login failed, please check the user OU
|
login_fail_ou_error=Login failed, please check the user OU
|
||||||
login_fail_filter_error=Login failed, please check the user filter
|
login_fail_filter_error=Login failed, please check the user filter
|
||||||
|
check_ldap_mapping=Check LDAP attribute mapping
|
||||||
|
ldap_mapping_value_null=LDAP user attribute mapping field is empty
|
|
@ -126,6 +126,7 @@ ldap_password_is_null=LDAP密码为空
|
||||||
ldap_connect_fail=连接LDAP失败
|
ldap_connect_fail=连接LDAP失败
|
||||||
ldap_connect_fail_user=连接LDAP失败,绑定的DN或密码错误
|
ldap_connect_fail_user=连接LDAP失败,绑定的DN或密码错误
|
||||||
ldap_user_filter_is_null=LDAP用户过滤器为空
|
ldap_user_filter_is_null=LDAP用户过滤器为空
|
||||||
|
ldap_user_mapping_is_null=LDAP用户属性映射为空
|
||||||
authentication_failed=用户认证失败,用户名或密码错误
|
authentication_failed=用户认证失败,用户名或密码错误
|
||||||
user_not_found_or_not_unique=用户不存在或者不唯一
|
user_not_found_or_not_unique=用户不存在或者不唯一
|
||||||
find_more_user=查找到多个用户
|
find_more_user=查找到多个用户
|
||||||
|
@ -133,5 +134,7 @@ ldap_authentication_not_enabled=LDAP认证未启用
|
||||||
login_fail_email_null=登录失败,用户邮箱为空
|
login_fail_email_null=登录失败,用户邮箱为空
|
||||||
login_fail_ou_error=登录失败,请检查用户OU
|
login_fail_ou_error=登录失败,请检查用户OU
|
||||||
login_fail_filter_error=登录失败,请检查用户过滤器
|
login_fail_filter_error=登录失败,请检查用户过滤器
|
||||||
|
check_ldap_mapping=检查LDAP属性映射
|
||||||
|
ldap_mapping_value_null=LDAP用户属性映射字段为空值
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -126,6 +126,7 @@ ldap_password_is_null=LDAP密碼為空
|
||||||
ldap_connect_fail=連接LDAP失敗
|
ldap_connect_fail=連接LDAP失敗
|
||||||
ldap_connect_fail_user=連接LDAP失敗,綁定的DN或密碼錯誤
|
ldap_connect_fail_user=連接LDAP失敗,綁定的DN或密碼錯誤
|
||||||
ldap_user_filter_is_null=LDAP用戶過濾器為空
|
ldap_user_filter_is_null=LDAP用戶過濾器為空
|
||||||
|
ldap_user_mapping_is_null=LDAP用戶屬性映射為空
|
||||||
authentication_failed=用戶認證失敗,用戶名或密碼錯誤
|
authentication_failed=用戶認證失敗,用戶名或密碼錯誤
|
||||||
user_not_found_or_not_unique=用戶不存在或者不唯一
|
user_not_found_or_not_unique=用戶不存在或者不唯一
|
||||||
find_more_user=查找到多個用戶
|
find_more_user=查找到多個用戶
|
||||||
|
@ -133,3 +134,5 @@ ldap_authentication_not_enabled=LDAP認證未啟用
|
||||||
login_fail_email_null=登錄失敗,用戶郵箱為空
|
login_fail_email_null=登錄失敗,用戶郵箱為空
|
||||||
login_fail_ou_error=登錄失敗,請檢查用戶OU
|
login_fail_ou_error=登錄失敗,請檢查用戶OU
|
||||||
login_fail_filter_error=登錄失敗,請檢查用戶過濾器
|
login_fail_filter_error=登錄失敗,請檢查用戶過濾器
|
||||||
|
check_ldap_mapping=檢查LDAP屬性映射
|
||||||
|
ldap_mapping_value_null=LDAP用戶屬性映射預設為空值
|
||||||
|
|
|
@ -49,13 +49,16 @@
|
||||||
this.projectId = projectId;
|
this.projectId = projectId;
|
||||||
this.getEnvironments();
|
this.getEnvironments();
|
||||||
},
|
},
|
||||||
deleteEnvironment(environment) {
|
deleteEnvironment(environment, index) {
|
||||||
if (environment.id) {
|
if (environment.id) {
|
||||||
this.result = this.$get('/api/environment/delete/' + environment.id, () => {
|
this.result = this.$get('/api/environment/delete/' + environment.id, () => {
|
||||||
this.$success(this.$t('commons.delete_success'));
|
this.$success(this.$t('commons.delete_success'));
|
||||||
this.getEnvironments();
|
this.getEnvironments();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
this.environments.splice(index, 1);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
copyEnvironment(environment) {
|
copyEnvironment(environment) {
|
||||||
if (!environment.id) {
|
if (!environment.id) {
|
||||||
|
@ -149,4 +152,5 @@
|
||||||
height: 100%;
|
height: 100%;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -53,6 +53,15 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
'regex.subject'() {
|
||||||
|
this.setRegexDescription();
|
||||||
|
},
|
||||||
|
'regex.expression'() {
|
||||||
|
this.setRegexDescription();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
add: function () {
|
add: function () {
|
||||||
this.list.push(this.getRegex());
|
this.list.push(this.getRegex());
|
||||||
|
@ -65,6 +74,9 @@
|
||||||
let regex = new Regex(this.regex);
|
let regex = new Regex(this.regex);
|
||||||
regex.description = regex.subject + " has: " + regex.expression;
|
regex.description = regex.subject + " has: " + regex.expression;
|
||||||
return regex;
|
return regex;
|
||||||
|
},
|
||||||
|
setRegexDescription() {
|
||||||
|
this.regex.description = this.regex.subject + " has: " + this.regex.expression;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item v-if="(selectedPlatformValue != 'Postman' && useEnvironment) || selectedPlatformValue == 'Swagger2'" :label="$t('api_test.environment.environment_config')" prop="environmentId">
|
<el-form-item v-if="(selectedPlatformValue != 'Postman' && useEnvironment) || selectedPlatformValue == 'Swagger2'" :label="$t('api_test.environment.environment_config')" prop="environmentId">
|
||||||
<el-select size="small" v-model="formData.environmentId" class="environment-select" clearable>
|
<el-select v-if="showEnvironmentSelect" size="small" v-model="formData.environmentId" class="environment-select" clearable>
|
||||||
<el-option v-for="(environment, index) in environments" :key="index" :label="environment.name + ': ' + environment.protocol + '://' + environment.socket" :value="environment.id"/>
|
<el-option v-for="(environment, index) in environments" :key="index" :label="environment.name + ': ' + environment.protocol + '://' + environment.socket" :value="environment.id"/>
|
||||||
<el-button class="environment-button" size="mini" type="primary" @click="openEnvironmentConfig">{{$t('api_test.environment.environment_config')}}</el-button>
|
<el-button class="environment-button" size="mini" type="primary" @click="openEnvironmentConfig">{{$t('api_test.environment.environment_config')}}</el-button>
|
||||||
<template v-slot:empty>
|
<template v-slot:empty>
|
||||||
|
@ -42,11 +42,23 @@
|
||||||
<el-form-item v-if="selectedPlatformValue == 'Metersphere'" prop="useEnvironment">
|
<el-form-item v-if="selectedPlatformValue == 'Metersphere'" prop="useEnvironment">
|
||||||
<el-checkbox v-model="useEnvironment">{{$t('api_test.environment.config_environment')}}</el-checkbox>
|
<el-checkbox v-model="useEnvironment">{{$t('api_test.environment.config_environment')}}</el-checkbox>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item :label="'Swagger URL'" prop="wgerUrl" v-if="selectedPlatformValue == 'Swagger2' && swaggerUrlEable">
|
||||||
|
<el-input size="small" v-model="formData.swaggerUrl" clearable show-word-limit />
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item v-if="selectedPlatformValue == 'Swagger2'">
|
||||||
|
<el-switch
|
||||||
|
v-model="swaggerUrlEable"
|
||||||
|
:active-text="$t('api_test.api_import.swagger_url_import')">
|
||||||
|
</el-switch>
|
||||||
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="1">
|
|
||||||
|
<el-col :span="1" v-if="selectedPlatformValue != 'Swagger2' || (selectedPlatformValue == 'Swagger2' && !swaggerUrlEable)">
|
||||||
<el-divider direction="vertical"/>
|
<el-divider direction="vertical"/>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="12">
|
<el-col :span="12" v-if="selectedPlatformValue != 'Swagger2' || (selectedPlatformValue == 'Swagger2' && !swaggerUrlEable)">
|
||||||
<el-upload
|
<el-upload
|
||||||
class="api-upload"
|
class="api-upload"
|
||||||
drag
|
drag
|
||||||
|
@ -89,6 +101,8 @@
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
visible: false,
|
visible: false,
|
||||||
|
swaggerUrlEable: false,
|
||||||
|
showEnvironmentSelect: true,
|
||||||
platforms: [
|
platforms: [
|
||||||
{
|
{
|
||||||
name: 'Metersphere',
|
name: 'Metersphere',
|
||||||
|
@ -122,7 +136,8 @@
|
||||||
name: '',
|
name: '',
|
||||||
environmentId: '',
|
environmentId: '',
|
||||||
projectId: '',
|
projectId: '',
|
||||||
file: undefined
|
file: undefined,
|
||||||
|
swaggerUrl: ''
|
||||||
},
|
},
|
||||||
rules: {
|
rules: {
|
||||||
name: [
|
name: [
|
||||||
|
@ -202,7 +217,9 @@
|
||||||
this.$error(this.$t('api_test.select_project'));
|
this.$error(this.$t('api_test.select_project'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
this.showEnvironmentSelect = false;
|
||||||
this.$refs.environmentConfig.open(this.formData.projectId);
|
this.$refs.environmentConfig.open(this.formData.projectId);
|
||||||
|
this.showEnvironmentSelect = true;
|
||||||
},
|
},
|
||||||
save() {
|
save() {
|
||||||
this.$refs.form.validate(valid => {
|
this.$refs.form.validate(valid => {
|
||||||
|
@ -211,10 +228,13 @@
|
||||||
Object.assign(param, this.formData);
|
Object.assign(param, this.formData);
|
||||||
param.platform = this.selectedPlatformValue;
|
param.platform = this.selectedPlatformValue;
|
||||||
param.useEnvironment = this.useEnvironment;
|
param.useEnvironment = this.useEnvironment;
|
||||||
if (!this.formData.file) {
|
if ((this.selectedPlatformValue != 'Swagger2' || (this.selectedPlatformValue == 'Swagger2' && !this.swaggerUrlEable)) && !this.formData.file) {
|
||||||
this.$warning(this.$t('commons.please_upload'));
|
this.$warning(this.$t('commons.please_upload'));
|
||||||
return ;
|
return ;
|
||||||
}
|
}
|
||||||
|
if (!this.swaggerUrlEable) {
|
||||||
|
param.swaggerUrl = undefined;
|
||||||
|
}
|
||||||
this.result = this.$fileUpload('/api/import', param.file, param,response => {
|
this.result = this.$fileUpload('/api/import', param.file, param,response => {
|
||||||
let res = response.data;
|
let res = response.data;
|
||||||
this.$success(this.$t('test_track.case.import.success'));
|
this.$success(this.$t('test_track.case.import.success'));
|
||||||
|
@ -231,7 +251,8 @@
|
||||||
name: '',
|
name: '',
|
||||||
environmentId: '',
|
environmentId: '',
|
||||||
projectId: '',
|
projectId: '',
|
||||||
file: undefined
|
file: undefined,
|
||||||
|
swaggerUrl: ''
|
||||||
};
|
};
|
||||||
this.fileList = [];
|
this.fileList = [];
|
||||||
}
|
}
|
||||||
|
@ -252,6 +273,7 @@
|
||||||
|
|
||||||
.api-upload >>> .el-upload {
|
.api-upload >>> .el-upload {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
max-width: 350px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.api-upload >>> .el-upload-dragger {
|
.api-upload >>> .el-upload-dragger {
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
<div :style="{'height': itemBarHeight + 'px'}" v-for="(item, index) in data" :key="index" class="item-bar" @click="itemSelected(index, item)" :class="{'item-selected' : index == selectIndex}">
|
<div :style="{'height': itemBarHeight + 'px'}" v-for="(item, index) in data" :key="index" class="item-bar" @click="itemSelected(index, item)" :class="{'item-selected' : index == selectIndex}">
|
||||||
<input class="item-input" :style="{'height': itemBarHeight - 12 + 'px', 'line-height': itemBarHeight - 12 + 'px', 'width': width - 90 + 'px'}" v-model="item.name" :placeholder="$t('commons.input_content')"/>
|
<input class="item-input" :style="{'height': itemBarHeight - 12 + 'px', 'line-height': itemBarHeight - 12 + 'px', 'width': width - 90 + 'px'}" v-model="item.name" :placeholder="$t('commons.input_content')"/>
|
||||||
<span :style="{'line-height': itemBarHeight - 10 + 'px'}" class="item-right">
|
<span :style="{'line-height': itemBarHeight - 10 + 'px'}" class="item-right">
|
||||||
<i v-for="(operator, index) in itemOperators" :key="index" :class="operator.icon" @click.stop="operator.func(item)"/>
|
<i v-for="(operator, operatorIndex) in itemOperators" :key="operatorIndex" :class="operator.icon" @click.stop="operator.func(item, index)"/>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</ms-aside-container>
|
</ms-aside-container>
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<el-tabs type="border-card" :stretch="true">
|
<el-tabs type="border-card" :stretch="true">
|
||||||
<el-tab-pane v-for="item in resource" :key="item.resourceId" :label="item.resourceName" class="logging-content">
|
<el-tab-pane v-for="item in resource" :key="item.resourceId" :label="item.resourceName" class="logging-content">
|
||||||
<ul class="infinite-list" v-infinite-scroll="load(item.resourceId)" infinite-scroll-disabled="disabled">
|
<ul class="infinite-list" v-infinite-scroll="load(item.resourceId)" infinite-scroll-disabled="disabled">
|
||||||
<li class="infinite-list-item" v-for="log in logContent" :key="log.id">{{ log.content }}</li>
|
<li class="infinite-list-item" v-for="(log, index) in logContent" :key="index">{{ log.content }}</li>
|
||||||
</ul>
|
</ul>
|
||||||
<el-link type="primary" @click="downloadLogFile(item)">{{$t('load_test.download_log_file')}}</el-link>
|
<el-link type="primary" @click="downloadLogFile(item)">{{$t('load_test.download_log_file')}}</el-link>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
|
|
|
@ -29,7 +29,8 @@
|
||||||
<span>{{$t('commons.workspace')}}</span>
|
<span>{{$t('commons.workspace')}}</span>
|
||||||
</template>
|
</template>
|
||||||
<el-menu-item index="/setting/member">{{$t('commons.member')}}</el-menu-item>
|
<el-menu-item index="/setting/member">{{$t('commons.member')}}</el-menu-item>
|
||||||
<el-menu-item index="/setting/testcase/report/template">{{$t('test_track.plan_view.report_template')}}</el-menu-item>
|
<el-menu-item index="/setting/testcase/report/template">{{$t('test_track.plan_view.report_template')}}
|
||||||
|
</el-menu-item>
|
||||||
</el-submenu>
|
</el-submenu>
|
||||||
|
|
||||||
<el-submenu index="4">
|
<el-submenu index="4">
|
||||||
|
@ -38,7 +39,9 @@
|
||||||
<span>{{$t('commons.personal_info')}}</span>
|
<span>{{$t('commons.personal_info')}}</span>
|
||||||
</template>
|
</template>
|
||||||
<el-menu-item index="/setting/personsetting">{{$t('commons.personal_setting')}}</el-menu-item>
|
<el-menu-item index="/setting/personsetting">{{$t('commons.personal_setting')}}</el-menu-item>
|
||||||
<el-menu-item index="/setting/apikeys">API Keys</el-menu-item>
|
<el-menu-item v-permission="['admin', 'org_admin', 'test_manager', 'test_user', 'test_viewer']"
|
||||||
|
index="/setting/apikeys">API Keys
|
||||||
|
</el-menu-item>
|
||||||
</el-submenu>
|
</el-submenu>
|
||||||
|
|
||||||
</el-menu>
|
</el-menu>
|
||||||
|
|
|
@ -18,9 +18,9 @@
|
||||||
<el-form-item :label="$t('ldap.filter')" prop="filter">
|
<el-form-item :label="$t('ldap.filter')" prop="filter">
|
||||||
<el-input v-model="form.filter" :placeholder="$t('ldap.input_filter_placeholder')"></el-input>
|
<el-input v-model="form.filter" :placeholder="$t('ldap.input_filter_placeholder')"></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<!-- <el-form-item :label="$t('ldap.mapping')" prop="mapping">-->
|
<el-form-item :label="$t('ldap.mapping')" prop="mapping">
|
||||||
<!-- <el-input v-model="form.mapping" :placeholder="$t('ldap.input_mapping')"></el-input>-->
|
<el-input v-model="form.mapping" :placeholder="$t('ldap.input_mapping_placeholder')"></el-input>
|
||||||
<!-- </el-form-item>-->
|
</el-form-item>
|
||||||
<el-form-item :label="$t('ldap.open')" prop="open">
|
<el-form-item :label="$t('ldap.open')" prop="open">
|
||||||
<el-checkbox v-model="form.open"></el-checkbox>
|
<el-checkbox v-model="form.open"></el-checkbox>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
@ -82,7 +82,8 @@
|
||||||
dn: {required: true, message: this.$t('ldap.input_dn'), trigger: ['change', 'blur']},
|
dn: {required: true, message: this.$t('ldap.input_dn'), trigger: ['change', 'blur']},
|
||||||
password: {required: true, message: this.$t('ldap.input_password'), trigger: ['change', 'blur']},
|
password: {required: true, message: this.$t('ldap.input_password'), trigger: ['change', 'blur']},
|
||||||
ou: {required: true, message: this.$t('ldap.input_ou'), trigger: ['change', 'blur']},
|
ou: {required: true, message: this.$t('ldap.input_ou'), trigger: ['change', 'blur']},
|
||||||
filter: {required: true, message: this.$t('ldap.input_filter'), trigger: ['change', 'blur']}
|
filter: {required: true, message: this.$t('ldap.input_filter'), trigger: ['change', 'blur']},
|
||||||
|
mapping: {required: true, message: this.$t('ldap.input_mapping'), trigger: ['change', 'blur']}
|
||||||
},
|
},
|
||||||
loginFormRules: {
|
loginFormRules: {
|
||||||
username: {required: true, message: this.$t('ldap.input_username'), trigger: 'blur'},
|
username: {required: true, message: this.$t('ldap.input_username'), trigger: 'blur'},
|
||||||
|
@ -142,6 +143,11 @@
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!this.form.mapping) {
|
||||||
|
this.$warning(this.$t('ldap.mapping_cannot_be_empty'));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
this.loginForm = {};
|
this.loginForm = {};
|
||||||
this.loginVisible = true;
|
this.loginVisible = true;
|
||||||
},
|
},
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
<span>{{ scope.row.createTime | timestampFormatDate }}</span>
|
<span>{{ scope.row.createTime | timestampFormatDate }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
<el-table-column prop="source" :label="$t('user.source')"/>
|
||||||
<el-table-column :label="$t('commons.operating')">
|
<el-table-column :label="$t('commons.operating')">
|
||||||
<template v-slot:default="scope">
|
<template v-slot:default="scope">
|
||||||
<ms-table-operator @editClick="edit(scope.row)" @deleteClick="del(scope.row)">
|
<ms-table-operator @editClick="edit(scope.row)" @deleteClick="del(scope.row)">
|
||||||
|
|
|
@ -297,7 +297,7 @@
|
||||||
responseType: 'blob'
|
responseType: 'blob'
|
||||||
};
|
};
|
||||||
this.result = this.$request(config).then(response => {
|
this.result = this.$request(config).then(response => {
|
||||||
const filename = '测试用例.xlsx'
|
const filename = this.$t('test_track.case.test_case') + ".xlsx";
|
||||||
const blob = new Blob([response.data]);
|
const blob = new Blob([response.data]);
|
||||||
if ("download" in document.createElement("a")) {
|
if ("download" in document.createElement("a")) {
|
||||||
let aTag = document.createElement('a');
|
let aTag = document.createElement('a');
|
||||||
|
|
|
@ -2,19 +2,30 @@
|
||||||
<div class="card-container">
|
<div class="card-container">
|
||||||
<el-card class="card-content" v-loading="result.loading">
|
<el-card class="card-content" v-loading="result.loading">
|
||||||
<template v-slot:header>
|
<template v-slot:header>
|
||||||
<ms-table-header :is-tester-permission="true" :condition.sync="condition" @search="initTableData" :show-create="false">
|
<ms-table-header :is-tester-permission="true" :condition.sync="condition" @search="initTableData"
|
||||||
|
:show-create="false">
|
||||||
<template v-slot:title>
|
<template v-slot:title>
|
||||||
<node-breadcrumb class="table-title" :nodes="selectParentNodes" @refresh="refresh"/>
|
<node-breadcrumb class="table-title" :nodes="selectParentNodes" @refresh="refresh"/>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:button>
|
<template v-slot:button>
|
||||||
<ms-table-button :is-tester-permission="true" v-if="!showMyTestCase" icon="el-icon-s-custom" :content="$t('test_track.plan_view.my_case')" @click="searchMyTestCase"/>
|
<ms-table-button :is-tester-permission="true" v-if="!showMyTestCase" icon="el-icon-s-custom"
|
||||||
<ms-table-button :is-tester-permission="true" v-if="showMyTestCase" icon="el-icon-files" :content="$t('test_track.plan_view.all_case')" @click="searchMyTestCase"/>
|
:content="$t('test_track.plan_view.my_case')" @click="searchMyTestCase"/>
|
||||||
<ms-table-button :is-tester-permission="true" icon="el-icon-connection" :content="$t('test_track.plan_view.relevance_test_case')" @click="$emit('openTestCaseRelevanceDialog')"/>
|
<ms-table-button :is-tester-permission="true" v-if="showMyTestCase" icon="el-icon-files"
|
||||||
<ms-table-button :is-tester-permission="true" icon="el-icon-unlock" :content="$t('test_track.plan_view.cancel_relevance')" @click="handleBatch('delete')"/>
|
:content="$t('test_track.plan_view.all_case')" @click="searchMyTestCase"/>
|
||||||
<ms-table-button :is-tester-permission="true" icon="el-icon-edit-outline" :content="$t('test_track.plan_view.change_execution_results')" @click="handleBatch('status')"/>
|
<ms-table-button :is-tester-permission="true" icon="el-icon-connection"
|
||||||
<ms-table-button :is-tester-permission="true" icon="el-icon-user" :content="$t('test_track.plan_view.change_executor')" @click="handleBatch('executor')"/>
|
:content="$t('test_track.plan_view.relevance_test_case')"
|
||||||
<ms-table-button :is-tester-permission="true" v-if="!testPlan.reportId" icon="el-icon-document" :content="$t('test_track.plan_view.create_report')" @click="openTestReport"/>
|
@click="$emit('openTestCaseRelevanceDialog')"/>
|
||||||
<ms-table-button :is-tester-permission="true" v-if="testPlan.reportId" icon="el-icon-document" :content="$t('test_track.plan_view.view_report')" @click="openReport"/>
|
<ms-table-button :is-tester-permission="true" icon="el-icon-unlock"
|
||||||
|
:content="$t('test_track.plan_view.cancel_relevance')" @click="handleBatch('delete')"/>
|
||||||
|
<ms-table-button :is-tester-permission="true" icon="el-icon-edit-outline"
|
||||||
|
:content="$t('test_track.plan_view.change_execution_results')"
|
||||||
|
@click="handleBatch('status')"/>
|
||||||
|
<ms-table-button :is-tester-permission="true" icon="el-icon-user"
|
||||||
|
:content="$t('test_track.plan_view.change_executor')" @click="handleBatch('executor')"/>
|
||||||
|
<ms-table-button :is-tester-permission="true" v-if="!testPlan.reportId" icon="el-icon-document"
|
||||||
|
:content="$t('test_track.plan_view.create_report')" @click="openTestReport"/>
|
||||||
|
<ms-table-button :is-tester-permission="true" v-if="testPlan.reportId" icon="el-icon-document"
|
||||||
|
:content="$t('test_track.plan_view.view_report')" @click="openReport"/>
|
||||||
</template>
|
</template>
|
||||||
</ms-table-header>
|
</ms-table-header>
|
||||||
</template>
|
</template>
|
||||||
|
@ -93,7 +104,7 @@
|
||||||
:label="$t('test_track.plan_view.execute_result')">
|
:label="$t('test_track.plan_view.execute_result')">
|
||||||
<template v-slot:default="scope">
|
<template v-slot:default="scope">
|
||||||
<span @click.stop="clickt = 'stop'">
|
<span @click.stop="clickt = 'stop'">
|
||||||
<el-dropdown class="test-case-status" @command="statusChange" >
|
<el-dropdown class="test-case-status" @command="statusChange">
|
||||||
<span class="el-dropdown-link">
|
<span class="el-dropdown-link">
|
||||||
<status-table-item :value="scope.row.status"/>
|
<status-table-item :value="scope.row.status"/>
|
||||||
</span>
|
</span>
|
||||||
|
@ -101,10 +112,12 @@
|
||||||
<el-dropdown-item :disabled="!isTestManagerOrTestUser" :command="{id: scope.row.id, status: 'Pass'}">
|
<el-dropdown-item :disabled="!isTestManagerOrTestUser" :command="{id: scope.row.id, status: 'Pass'}">
|
||||||
{{$t('test_track.plan_view.pass')}}
|
{{$t('test_track.plan_view.pass')}}
|
||||||
</el-dropdown-item>
|
</el-dropdown-item>
|
||||||
<el-dropdown-item :disabled="!isTestManagerOrTestUser" :command="{id: scope.row.id, status: 'Failure'}">
|
<el-dropdown-item :disabled="!isTestManagerOrTestUser"
|
||||||
|
:command="{id: scope.row.id, status: 'Failure'}">
|
||||||
{{$t('test_track.plan_view.failure')}}
|
{{$t('test_track.plan_view.failure')}}
|
||||||
</el-dropdown-item>
|
</el-dropdown-item>
|
||||||
<el-dropdown-item :disabled="!isTestManagerOrTestUser" :command="{id: scope.row.id, status: 'Blocking'}">
|
<el-dropdown-item :disabled="!isTestManagerOrTestUser"
|
||||||
|
:command="{id: scope.row.id, status: 'Blocking'}">
|
||||||
{{$t('test_track.plan_view.blocking')}}
|
{{$t('test_track.plan_view.blocking')}}
|
||||||
</el-dropdown-item>
|
</el-dropdown-item>
|
||||||
<el-dropdown-item :disabled="!isTestManagerOrTestUser" :command="{id: scope.row.id, status: 'Skip'}">
|
<el-dropdown-item :disabled="!isTestManagerOrTestUser" :command="{id: scope.row.id, status: 'Skip'}">
|
||||||
|
@ -128,8 +141,10 @@
|
||||||
<el-table-column
|
<el-table-column
|
||||||
:label="$t('commons.operating')">
|
:label="$t('commons.operating')">
|
||||||
<template v-slot:default="scope">
|
<template v-slot:default="scope">
|
||||||
<ms-table-operator-button :is-tester-permission="true" :tip="$t('commons.edit')" icon="el-icon-edit" @exec="handleEdit(scope.row)" />
|
<ms-table-operator-button :is-tester-permission="true" :tip="$t('commons.edit')" icon="el-icon-edit"
|
||||||
<ms-table-operator-button :is-tester-permission="true" :tip="$t('test_track.plan_view.cancel_relevance')" icon="el-icon-unlock" type="danger" @exec="handleDelete(scope.row)"/>
|
@exec="handleEdit(scope.row)"/>
|
||||||
|
<ms-table-operator-button :is-tester-permission="true" :tip="$t('test_track.plan_view.cancel_relevance')"
|
||||||
|
icon="el-icon-unlock" type="danger" @exec="handleDelete(scope.row)"/>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
|
@ -162,12 +177,7 @@
|
||||||
import NodeBreadcrumb from '../../../common/NodeBreadcrumb';
|
import NodeBreadcrumb from '../../../common/NodeBreadcrumb';
|
||||||
|
|
||||||
import {ROLE_TEST_MANAGER, ROLE_TEST_USER, TokenKey} from '../../../../../../common/js/constants';
|
import {ROLE_TEST_MANAGER, ROLE_TEST_USER, TokenKey} from '../../../../../../common/js/constants';
|
||||||
import {
|
import {_filter, _sort, checkoutTestManagerOrTestUser, hasRoles} from '../../../../../../common/js/utils';
|
||||||
_filter,
|
|
||||||
_sort,
|
|
||||||
checkoutTestManagerOrTestUser,
|
|
||||||
hasRoles
|
|
||||||
} from '../../../../../../common/js/utils';
|
|
||||||
import PriorityTableItem from "../../../common/tableItems/planview/PriorityTableItem";
|
import PriorityTableItem from "../../../common/tableItems/planview/PriorityTableItem";
|
||||||
import StatusTableItem from "../../../common/tableItems/planview/StatusTableItem";
|
import StatusTableItem from "../../../common/tableItems/planview/StatusTableItem";
|
||||||
import TypeTableItem from "../../../common/tableItems/planview/TypeTableItem";
|
import TypeTableItem from "../../../common/tableItems/planview/TypeTableItem";
|
||||||
|
@ -176,239 +186,251 @@
|
||||||
import MsTableOperatorButton from "../../../../common/components/MsTableOperatorButton";
|
import MsTableOperatorButton from "../../../../common/components/MsTableOperatorButton";
|
||||||
import TestReportTemplateList from "./TestReportTemplateList";
|
import TestReportTemplateList from "./TestReportTemplateList";
|
||||||
import TestCaseReportView from "./report/TestCaseReportView";
|
import TestCaseReportView from "./report/TestCaseReportView";
|
||||||
|
import {TEST_CASE_CONFIGS} from "../../../../common/components/search/search-components";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "TestPlanTestCaseList",
|
name: "TestPlanTestCaseList",
|
||||||
components: {
|
components: {
|
||||||
TestCaseReportView,
|
TestCaseReportView,
|
||||||
TestReportTemplateList,
|
TestReportTemplateList,
|
||||||
MsTableOperatorButton,
|
MsTableOperatorButton,
|
||||||
MsTableOperator,
|
MsTableOperator,
|
||||||
MethodTableItem,
|
MethodTableItem,
|
||||||
TypeTableItem,
|
TypeTableItem,
|
||||||
StatusTableItem,
|
StatusTableItem,
|
||||||
PriorityTableItem, StatusEdit, ExecutorEdit, MsTipButton, MsTablePagination,
|
PriorityTableItem, StatusEdit, ExecutorEdit, MsTipButton, MsTablePagination,
|
||||||
TestPlanTestCaseEdit, MsTableHeader, NodeBreadcrumb, MsTableButton},
|
TestPlanTestCaseEdit, MsTableHeader, NodeBreadcrumb, MsTableButton
|
||||||
data() {
|
},
|
||||||
return {
|
data() {
|
||||||
result: {},
|
return {
|
||||||
deletePath: "/test/case/delete",
|
result: {},
|
||||||
condition: {},
|
deletePath: "/test/case/delete",
|
||||||
showMyTestCase: false,
|
condition: {
|
||||||
tableData: [],
|
components: TEST_CASE_CONFIGS
|
||||||
currentPage: 1,
|
|
||||||
pageSize: 10,
|
|
||||||
total: 0,
|
|
||||||
selectIds: new Set(),
|
|
||||||
testPlan: {},
|
|
||||||
isReadOnly: false,
|
|
||||||
isTestManagerOrTestUser: false,
|
|
||||||
priorityFilters: [
|
|
||||||
{text: 'P0', value: 'P0'},
|
|
||||||
{text: 'P1', value: 'P1'},
|
|
||||||
{text: 'P2', value: 'P2'},
|
|
||||||
{text: 'P3', value: 'P3'}
|
|
||||||
],
|
|
||||||
methodFilters: [
|
|
||||||
{text: this.$t('test_track.case.manual'), value: 'manual'},
|
|
||||||
{text: this.$t('test_track.case.auto'), value: 'auto'}
|
|
||||||
],
|
|
||||||
typeFilters: [
|
|
||||||
{text: this.$t('commons.functional'), value: 'functional'},
|
|
||||||
{text: this.$t('commons.performance'), value: 'performance'},
|
|
||||||
{text: this.$t('commons.api'), value: 'api'}
|
|
||||||
],
|
|
||||||
statusFilters: [
|
|
||||||
{text: this.$t('test_track.plan.plan_status_prepare'), value: 'Prepare'},
|
|
||||||
{text: this.$t('test_track.plan_view.pass'), value: 'Pass'},
|
|
||||||
{text: this.$t('test_track.plan_view.failure'), value: 'Failure'},
|
|
||||||
{text: this.$t('test_track.plan_view.blocking'), value: 'Blocking'},
|
|
||||||
{text: this.$t('test_track.plan_view.skip'), value: 'Skip'},
|
|
||||||
{text: this.$t('test_track.plan.plan_status_running'), value: 'Underway'},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
props:{
|
|
||||||
planId: {
|
|
||||||
type: String
|
|
||||||
},
|
},
|
||||||
selectNodeIds: {
|
showMyTestCase: false,
|
||||||
type: Array
|
tableData: [],
|
||||||
},
|
currentPage: 1,
|
||||||
selectParentNodes: {
|
pageSize: 10,
|
||||||
type: Array
|
total: 0,
|
||||||
}
|
selectIds: new Set(),
|
||||||
|
testPlan: {},
|
||||||
|
isReadOnly: false,
|
||||||
|
isTestManagerOrTestUser: false,
|
||||||
|
priorityFilters: [
|
||||||
|
{text: 'P0', value: 'P0'},
|
||||||
|
{text: 'P1', value: 'P1'},
|
||||||
|
{text: 'P2', value: 'P2'},
|
||||||
|
{text: 'P3', value: 'P3'}
|
||||||
|
],
|
||||||
|
methodFilters: [
|
||||||
|
{text: this.$t('test_track.case.manual'), value: 'manual'},
|
||||||
|
{text: this.$t('test_track.case.auto'), value: 'auto'}
|
||||||
|
],
|
||||||
|
typeFilters: [
|
||||||
|
{text: this.$t('commons.functional'), value: 'functional'},
|
||||||
|
{text: this.$t('commons.performance'), value: 'performance'},
|
||||||
|
{text: this.$t('commons.api'), value: 'api'}
|
||||||
|
],
|
||||||
|
statusFilters: [
|
||||||
|
{text: this.$t('test_track.plan.plan_status_prepare'), value: 'Prepare'},
|
||||||
|
{text: this.$t('test_track.plan_view.pass'), value: 'Pass'},
|
||||||
|
{text: this.$t('test_track.plan_view.failure'), value: 'Failure'},
|
||||||
|
{text: this.$t('test_track.plan_view.blocking'), value: 'Blocking'},
|
||||||
|
{text: this.$t('test_track.plan_view.skip'), value: 'Skip'},
|
||||||
|
{text: this.$t('test_track.plan.plan_status_running'), value: 'Underway'},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
planId: {
|
||||||
|
type: String
|
||||||
},
|
},
|
||||||
watch: {
|
selectNodeIds: {
|
||||||
planId() {
|
type: Array
|
||||||
this.refreshTableAndPlan();
|
|
||||||
},
|
|
||||||
selectNodeIds() {
|
|
||||||
this.search();
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
mounted() {
|
selectParentNodes: {
|
||||||
|
type: Array
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
planId() {
|
||||||
this.refreshTableAndPlan();
|
this.refreshTableAndPlan();
|
||||||
this.isTestManagerOrTestUser = checkoutTestManagerOrTestUser();
|
|
||||||
},
|
},
|
||||||
methods: {
|
selectNodeIds() {
|
||||||
initTableData() {
|
this.search();
|
||||||
if (this.planId) {
|
}
|
||||||
this.condition.planId = this.planId;
|
},
|
||||||
this.condition.nodeIds = this.selectNodeIds;
|
mounted() {
|
||||||
this.result = this.$post(this.buildPagePath('/test/plan/case/list'), this.condition, response => {
|
this.refreshTableAndPlan();
|
||||||
let data = response.data;
|
this.isTestManagerOrTestUser = checkoutTestManagerOrTestUser();
|
||||||
this.total = data.itemCount;
|
},
|
||||||
this.tableData = data.listObject;
|
methods: {
|
||||||
this.selectIds.clear();
|
initTableData(combine) {
|
||||||
});
|
// 只有在点击高级搜索的查询按钮时combine才有值
|
||||||
}
|
let condition = combine ? {combine: combine} : this.condition;
|
||||||
},
|
if (this.planId) {
|
||||||
showDetail(row, event, column) {
|
// param.planId = this.planId;
|
||||||
this.isReadOnly = true;
|
condition.planId = this.planId;
|
||||||
this.$refs.testPlanTestCaseEdit.openTestCaseEdit(row);
|
|
||||||
},
|
|
||||||
refresh() {
|
|
||||||
this.condition = {};
|
|
||||||
this.selectIds.clear();
|
|
||||||
this.$emit('refresh');
|
|
||||||
},
|
|
||||||
refreshTableAndPlan() {
|
|
||||||
this.getTestPlanById();
|
|
||||||
this.initTableData();
|
|
||||||
},
|
|
||||||
refreshTestPlanRecent() {
|
|
||||||
if (hasRoles(ROLE_TEST_USER, ROLE_TEST_MANAGER)) {
|
|
||||||
let param = {};
|
|
||||||
param.id = this.planId;
|
|
||||||
param.updateTime = Date.now();
|
|
||||||
this.$post('/test/plan/edit', param);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
search() {
|
|
||||||
this.initTableData();
|
|
||||||
},
|
|
||||||
buildPagePath(path) {
|
|
||||||
return path + "/" + this.currentPage + "/" + this.pageSize;
|
|
||||||
},
|
|
||||||
handleEdit(testCase, index) {
|
|
||||||
this.isReadOnly = false;
|
|
||||||
if (!checkoutTestManagerOrTestUser()) {
|
|
||||||
this.isReadOnly = true;
|
|
||||||
}
|
|
||||||
this.$refs.testPlanTestCaseEdit.openTestCaseEdit(testCase);
|
|
||||||
},
|
|
||||||
handleDelete(testCase) {
|
|
||||||
this.$alert(this.$t('test_track.plan_view.confirm_cancel_relevance') + ' ' + testCase.name + " ?", '', {
|
|
||||||
confirmButtonText: this.$t('commons.confirm'),
|
|
||||||
callback: (action) => {
|
|
||||||
if (action === 'confirm') {
|
|
||||||
this._handleDelete(testCase);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
handleDeleteBatch() {
|
|
||||||
this.$alert(this.$t('test_track.plan_view.confirm_cancel_relevance') + " ?", '', {
|
|
||||||
confirmButtonText: this.$t('commons.confirm'),
|
|
||||||
callback: (action) => {
|
|
||||||
if (action === 'confirm') {
|
|
||||||
this.$post('/test/plan/case/batch/delete', {ids: [...this.selectIds]}, () => {
|
|
||||||
this.selectIds.clear();
|
|
||||||
this.$emit("refresh");
|
|
||||||
this.$success(this.$t('commons.delete_success'));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
_handleDelete(testCase) {
|
|
||||||
let testCaseId = testCase.id;
|
|
||||||
this.$post('/test/plan/case/delete/' + testCaseId, {}, () => {
|
|
||||||
this.$emit("refresh");
|
|
||||||
this.$success(this.$t('commons.delete_success'));
|
|
||||||
});
|
|
||||||
},
|
|
||||||
handleSelectAll(selection) {
|
|
||||||
if(selection.length > 0) {
|
|
||||||
this.tableData.forEach(item => {
|
|
||||||
this.selectIds.add(item.id);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.selectIds.clear();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
handleSelectionChange(selection, row) {
|
|
||||||
if(this.selectIds.has(row.id)){
|
|
||||||
this.selectIds.delete(row.id);
|
|
||||||
} else {
|
|
||||||
this.selectIds.add(row.id);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
handleBatch(type){
|
|
||||||
if (this.selectIds.size < 1) {
|
|
||||||
this.$warning(this.$t('test_track.plan_view.select_manipulate'));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (type === 'executor'){
|
|
||||||
this.$refs.executorEdit.openExecutorEdit();
|
|
||||||
} else if (type === 'status'){
|
|
||||||
this.$refs.statusEdit.openStatusEdit();
|
|
||||||
} else if (type === 'delete') {
|
|
||||||
this.handleDeleteBatch();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
searchMyTestCase() {
|
|
||||||
this.showMyTestCase = !this.showMyTestCase;
|
|
||||||
if (this.showMyTestCase) {
|
|
||||||
let user = JSON.parse(localStorage.getItem(TokenKey));
|
|
||||||
this.condition.executor = user.id;
|
|
||||||
} else {
|
|
||||||
this.condition.executor = null;
|
|
||||||
}
|
|
||||||
this.initTableData();
|
|
||||||
},
|
|
||||||
openTestReport() {
|
|
||||||
this.$refs.testReporTtemplateList.open(this.planId);
|
|
||||||
},
|
|
||||||
statusChange(param) {
|
|
||||||
this.$post('/test/plan/case/edit' , param, () => {
|
|
||||||
for (let i = 0; i < this.tableData.length; i++) {
|
|
||||||
if (this.tableData[i].id == param.id) {
|
|
||||||
this.tableData[i].status = param.status;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
getTestPlanById() {
|
|
||||||
if (this.planId) {
|
|
||||||
this.$post('/test/plan/get/' + this.planId, {}, response => {
|
|
||||||
this.testPlan = response.data;
|
|
||||||
this.refreshTestPlanRecent();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
openReport(planId, id) {
|
|
||||||
this.getTestPlanById();
|
|
||||||
if (!id) {
|
|
||||||
id = this.testPlan.reportId;
|
|
||||||
}
|
|
||||||
if (!planId) {
|
|
||||||
planId = this.planId;
|
|
||||||
}
|
|
||||||
this.$refs.testCaseReportView.open(planId, id);
|
|
||||||
},
|
|
||||||
filter(filters) {
|
|
||||||
_filter(filters, this.condition);
|
|
||||||
this.initTableData();
|
|
||||||
},
|
|
||||||
sort(column) {
|
|
||||||
_sort(column, this.condition);
|
|
||||||
this.initTableData();
|
|
||||||
}
|
}
|
||||||
|
if (this.selectNodeIds && this.selectNodeIds.length > 0) {
|
||||||
|
// param.nodeIds = this.selectNodeIds;
|
||||||
|
condition.nodeIds = this.selectNodeIds;
|
||||||
|
}
|
||||||
|
if (this.planId) {
|
||||||
|
this.result = this.$post(this.buildPagePath('/test/plan/case/list'), condition, response => {
|
||||||
|
let data = response.data;
|
||||||
|
this.total = data.itemCount;
|
||||||
|
this.tableData = data.listObject;
|
||||||
|
this.selectIds.clear();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
showDetail(row, event, column) {
|
||||||
|
this.isReadOnly = true;
|
||||||
|
this.$refs.testPlanTestCaseEdit.openTestCaseEdit(row);
|
||||||
|
},
|
||||||
|
refresh() {
|
||||||
|
this.condition = {components: TEST_CASE_CONFIGS};
|
||||||
|
this.selectIds.clear();
|
||||||
|
this.$emit('refresh');
|
||||||
|
},
|
||||||
|
refreshTableAndPlan() {
|
||||||
|
this.getTestPlanById();
|
||||||
|
this.initTableData();
|
||||||
|
},
|
||||||
|
refreshTestPlanRecent() {
|
||||||
|
if (hasRoles(ROLE_TEST_USER, ROLE_TEST_MANAGER)) {
|
||||||
|
let param = {};
|
||||||
|
param.id = this.planId;
|
||||||
|
param.updateTime = Date.now();
|
||||||
|
this.$post('/test/plan/edit', param);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
search() {
|
||||||
|
this.initTableData();
|
||||||
|
},
|
||||||
|
buildPagePath(path) {
|
||||||
|
return path + "/" + this.currentPage + "/" + this.pageSize;
|
||||||
|
},
|
||||||
|
handleEdit(testCase, index) {
|
||||||
|
this.isReadOnly = false;
|
||||||
|
if (!checkoutTestManagerOrTestUser()) {
|
||||||
|
this.isReadOnly = true;
|
||||||
|
}
|
||||||
|
this.$refs.testPlanTestCaseEdit.openTestCaseEdit(testCase);
|
||||||
|
},
|
||||||
|
handleDelete(testCase) {
|
||||||
|
this.$alert(this.$t('test_track.plan_view.confirm_cancel_relevance') + ' ' + testCase.name + " ?", '', {
|
||||||
|
confirmButtonText: this.$t('commons.confirm'),
|
||||||
|
callback: (action) => {
|
||||||
|
if (action === 'confirm') {
|
||||||
|
this._handleDelete(testCase);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleDeleteBatch() {
|
||||||
|
this.$alert(this.$t('test_track.plan_view.confirm_cancel_relevance') + " ?", '', {
|
||||||
|
confirmButtonText: this.$t('commons.confirm'),
|
||||||
|
callback: (action) => {
|
||||||
|
if (action === 'confirm') {
|
||||||
|
this.$post('/test/plan/case/batch/delete', {ids: [...this.selectIds]}, () => {
|
||||||
|
this.selectIds.clear();
|
||||||
|
this.$emit("refresh");
|
||||||
|
this.$success(this.$t('commons.delete_success'));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
_handleDelete(testCase) {
|
||||||
|
let testCaseId = testCase.id;
|
||||||
|
this.$post('/test/plan/case/delete/' + testCaseId, {}, () => {
|
||||||
|
this.$emit("refresh");
|
||||||
|
this.$success(this.$t('commons.delete_success'));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleSelectAll(selection) {
|
||||||
|
if (selection.length > 0) {
|
||||||
|
this.tableData.forEach(item => {
|
||||||
|
this.selectIds.add(item.id);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.selectIds.clear();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleSelectionChange(selection, row) {
|
||||||
|
if (this.selectIds.has(row.id)) {
|
||||||
|
this.selectIds.delete(row.id);
|
||||||
|
} else {
|
||||||
|
this.selectIds.add(row.id);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleBatch(type) {
|
||||||
|
if (this.selectIds.size < 1) {
|
||||||
|
this.$warning(this.$t('test_track.plan_view.select_manipulate'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (type === 'executor') {
|
||||||
|
this.$refs.executorEdit.openExecutorEdit();
|
||||||
|
} else if (type === 'status') {
|
||||||
|
this.$refs.statusEdit.openStatusEdit();
|
||||||
|
} else if (type === 'delete') {
|
||||||
|
this.handleDeleteBatch();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
searchMyTestCase() {
|
||||||
|
this.showMyTestCase = !this.showMyTestCase;
|
||||||
|
if (this.showMyTestCase) {
|
||||||
|
let user = JSON.parse(localStorage.getItem(TokenKey));
|
||||||
|
this.condition.executor = user.id;
|
||||||
|
} else {
|
||||||
|
this.condition.executor = null;
|
||||||
|
}
|
||||||
|
this.initTableData();
|
||||||
|
},
|
||||||
|
openTestReport() {
|
||||||
|
this.$refs.testReporTtemplateList.open(this.planId);
|
||||||
|
},
|
||||||
|
statusChange(param) {
|
||||||
|
this.$post('/test/plan/case/edit', param, () => {
|
||||||
|
for (let i = 0; i < this.tableData.length; i++) {
|
||||||
|
if (this.tableData[i].id == param.id) {
|
||||||
|
this.tableData[i].status = param.status;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
getTestPlanById() {
|
||||||
|
if (this.planId) {
|
||||||
|
this.$post('/test/plan/get/' + this.planId, {}, response => {
|
||||||
|
this.testPlan = response.data;
|
||||||
|
this.refreshTestPlanRecent();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
openReport(planId, id) {
|
||||||
|
this.getTestPlanById();
|
||||||
|
if (!id) {
|
||||||
|
id = this.testPlan.reportId;
|
||||||
|
}
|
||||||
|
if (!planId) {
|
||||||
|
planId = this.planId;
|
||||||
|
}
|
||||||
|
this.$refs.testCaseReportView.open(planId, id);
|
||||||
|
},
|
||||||
|
filter(filters) {
|
||||||
|
_filter(filters, this.condition);
|
||||||
|
this.initTableData();
|
||||||
|
},
|
||||||
|
sort(column) {
|
||||||
|
_sort(column, this.condition);
|
||||||
|
this.initTableData();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
@ -418,8 +440,8 @@
|
||||||
width: 240px;
|
width: 240px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.test-case-status,.el-table {
|
.test-case-status, .el-table {
|
||||||
cursor:pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -81,7 +81,7 @@
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
runTest() {
|
runTest() {
|
||||||
this.result = this.$post("/api/run", {id: this.test.id}, (response) => {
|
this.result = this.$post("/api/run", {id: this.test.id, triggerMode: 'MANUAL'}, (response) => {
|
||||||
this.$success(this.$t('api_test.running'));
|
this.$success(this.$t('api_test.running'));
|
||||||
this.$emit('runTest', response.data)
|
this.$emit('runTest', response.data)
|
||||||
});
|
});
|
||||||
|
|
|
@ -224,7 +224,8 @@ export default {
|
||||||
email_format_is_incorrect: 'Email format is incorrect',
|
email_format_is_incorrect: 'Email format is incorrect',
|
||||||
delete_confirm: 'Are you sure you want to delete this User?',
|
delete_confirm: 'Are you sure you want to delete this User?',
|
||||||
apikey_delete_confirm: 'Are you sure you want to delete this API Key?',
|
apikey_delete_confirm: 'Are you sure you want to delete this API Key?',
|
||||||
input_id_placeholder: 'Please enter ID (only supports numbers and English letters)'
|
input_id_placeholder: 'Please enter ID (only supports numbers and English letters)',
|
||||||
|
source: 'Source'
|
||||||
},
|
},
|
||||||
role: {
|
role: {
|
||||||
please_choose_role: 'Please Choose Role',
|
please_choose_role: 'Please Choose Role',
|
||||||
|
@ -436,6 +437,7 @@ export default {
|
||||||
postman_export_tip: "Export the test collection by Postman",
|
postman_export_tip: "Export the test collection by Postman",
|
||||||
swagger_export_tip: "Export jSON-formatted files via Swagger website",
|
swagger_export_tip: "Export jSON-formatted files via Swagger website",
|
||||||
suffixFormatErr: "The file format does not meet the requirements",
|
suffixFormatErr: "The file format does not meet the requirements",
|
||||||
|
swagger_url_import: "Import using URL",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
api_report: {
|
api_report: {
|
||||||
|
@ -679,6 +681,7 @@ export default {
|
||||||
input_url_placeholder: 'Please enter the LDAP address (eg ldap://localhost:389)',
|
input_url_placeholder: 'Please enter the LDAP address (eg ldap://localhost:389)',
|
||||||
input_ou_placeholder: 'Enter user OU (use | to separate each OU)',
|
input_ou_placeholder: 'Enter user OU (use | to separate each OU)',
|
||||||
input_filter_placeholder: 'Input filter [Possible options are cn or uid or sAMAccountName={0}, eg: (uid={0})]',
|
input_filter_placeholder: 'Input filter [Possible options are cn or uid or sAMAccountName={0}, eg: (uid={0})]',
|
||||||
|
input_mapping_placeholder: 'eg:{"username":"uid","name":"sn","email":"mail"}, The username mapping option may be cn or uid or sAMAccountName',
|
||||||
test_connect: 'Test Connection',
|
test_connect: 'Test Connection',
|
||||||
test_login: 'Test Login',
|
test_login: 'Test Login',
|
||||||
edit: 'Edit',
|
edit: 'Edit',
|
||||||
|
@ -688,6 +691,7 @@ export default {
|
||||||
ou_cannot_be_empty: 'LDAP OU cannot be empty',
|
ou_cannot_be_empty: 'LDAP OU cannot be empty',
|
||||||
filter_cannot_be_empty: 'LDAP user filter cannot be empty',
|
filter_cannot_be_empty: 'LDAP user filter cannot be empty',
|
||||||
password_cannot_be_empty: 'LDAP password cannot be empty',
|
password_cannot_be_empty: 'LDAP password cannot be empty',
|
||||||
|
mapping_cannot_be_empty: 'LDAP mapping cannot be empty',
|
||||||
},
|
},
|
||||||
schedule: {
|
schedule: {
|
||||||
not_set: "Not Set",
|
not_set: "Not Set",
|
||||||
|
|
|
@ -222,7 +222,8 @@ export default {
|
||||||
email_format_is_incorrect: '邮箱格式不正确',
|
email_format_is_incorrect: '邮箱格式不正确',
|
||||||
delete_confirm: '这个用户确定要删除吗?',
|
delete_confirm: '这个用户确定要删除吗?',
|
||||||
apikey_delete_confirm: '这个 API Key 确定要删除吗?',
|
apikey_delete_confirm: '这个 API Key 确定要删除吗?',
|
||||||
input_id_placeholder: '请输入ID (只支持数字、英文字母)'
|
input_id_placeholder: '请输入ID (只支持数字、英文字母)',
|
||||||
|
source: '用户来源'
|
||||||
},
|
},
|
||||||
role: {
|
role: {
|
||||||
please_choose_role: '请选择角色',
|
please_choose_role: '请选择角色',
|
||||||
|
@ -436,6 +437,7 @@ export default {
|
||||||
post_export_tip: "通过 Postman 导出测试集合",
|
post_export_tip: "通过 Postman 导出测试集合",
|
||||||
swagger_export_tip: "通过 Swagger 页面导出",
|
swagger_export_tip: "通过 Swagger 页面导出",
|
||||||
suffixFormatErr: "文件格式不符合要求",
|
suffixFormatErr: "文件格式不符合要求",
|
||||||
|
swagger_url_import: "使用URL导入",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
api_report: {
|
api_report: {
|
||||||
|
@ -679,6 +681,7 @@ export default {
|
||||||
input_url_placeholder: '请输入LDAP地址 (如 ldap://localhost:389)',
|
input_url_placeholder: '请输入LDAP地址 (如 ldap://localhost:389)',
|
||||||
input_ou_placeholder: '输入用户OU (使用|分隔各OU)',
|
input_ou_placeholder: '输入用户OU (使用|分隔各OU)',
|
||||||
input_filter_placeholder: '输入过滤器 [可能的选项是cn或uid或sAMAccountName={0}, 如:(uid={0})]',
|
input_filter_placeholder: '输入过滤器 [可能的选项是cn或uid或sAMAccountName={0}, 如:(uid={0})]',
|
||||||
|
input_mapping_placeholder: '如:{"username":"uid","name":"sn","email":"mail"}, username映射的选项可能是cn或uid或sAMAccountName',
|
||||||
test_connect: '测试连接',
|
test_connect: '测试连接',
|
||||||
test_login: '测试登录',
|
test_login: '测试登录',
|
||||||
edit: '编辑',
|
edit: '编辑',
|
||||||
|
@ -687,6 +690,7 @@ export default {
|
||||||
dn_cannot_be_empty: 'LDAP DN不能为空',
|
dn_cannot_be_empty: 'LDAP DN不能为空',
|
||||||
ou_cannot_be_empty: 'LDAP OU不能为空',
|
ou_cannot_be_empty: 'LDAP OU不能为空',
|
||||||
filter_cannot_be_empty: 'LDAP 用户过滤器不能为空',
|
filter_cannot_be_empty: 'LDAP 用户过滤器不能为空',
|
||||||
|
mapping_cannot_be_empty: 'LDAP 用户属性映射不能为空',
|
||||||
password_cannot_be_empty: 'LDAP 密码不能为空',
|
password_cannot_be_empty: 'LDAP 密码不能为空',
|
||||||
},
|
},
|
||||||
schedule: {
|
schedule: {
|
||||||
|
|
|
@ -221,7 +221,8 @@ export default {
|
||||||
email_format_is_incorrect: '郵箱格式不正確',
|
email_format_is_incorrect: '郵箱格式不正確',
|
||||||
delete_confirm: '這個用戶確定要刪除嗎?',
|
delete_confirm: '這個用戶確定要刪除嗎?',
|
||||||
apikey_delete_confirm: '這個 API Key 確定要刪除嗎?',
|
apikey_delete_confirm: '這個 API Key 確定要刪除嗎?',
|
||||||
input_id_placeholder: '請輸入ID (只支持數字、英文字母)'
|
input_id_placeholder: '請輸入ID (只支持數字、英文字母)',
|
||||||
|
source: '用戶來源'
|
||||||
},
|
},
|
||||||
role: {
|
role: {
|
||||||
please_choose_role: '請選擇角色',
|
please_choose_role: '請選擇角色',
|
||||||
|
@ -435,6 +436,7 @@ export default {
|
||||||
post_export_tip: "通過 Postman 導出測試集合",
|
post_export_tip: "通過 Postman 導出測試集合",
|
||||||
swagger_export_tip: "通過 Swagger 頁面導出",
|
swagger_export_tip: "通過 Swagger 頁面導出",
|
||||||
suffixFormatErr: "文件格式不符合要求",
|
suffixFormatErr: "文件格式不符合要求",
|
||||||
|
swagger_url_import: "使用URL導入",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
api_report: {
|
api_report: {
|
||||||
|
@ -677,6 +679,7 @@ export default {
|
||||||
input_url_placeholder: '請輸入LDAP地址 (如 ldap://localhost:389)',
|
input_url_placeholder: '請輸入LDAP地址 (如 ldap://localhost:389)',
|
||||||
input_ou_placeholder: '輸入用戶OU (使用|分隔各OU)',
|
input_ou_placeholder: '輸入用戶OU (使用|分隔各OU)',
|
||||||
input_filter_placeholder: '輸入過濾器 [可能的選項是cn或uid或sAMAccountName={0}, 如:(uid={0})]',
|
input_filter_placeholder: '輸入過濾器 [可能的選項是cn或uid或sAMAccountName={0}, 如:(uid={0})]',
|
||||||
|
input_mapping_placeholder: '如:{"username":"uid","name":"sn","email":"mail"}, username映射的選項可能是cn或uid或sAMAccountName',
|
||||||
test_connect: '測試連接',
|
test_connect: '測試連接',
|
||||||
test_login: '測試登錄',
|
test_login: '測試登錄',
|
||||||
edit: '編輯',
|
edit: '編輯',
|
||||||
|
@ -686,6 +689,7 @@ export default {
|
||||||
ou_cannot_be_empty: 'LDAP OU不能為空',
|
ou_cannot_be_empty: 'LDAP OU不能為空',
|
||||||
filter_cannot_be_empty: 'LDAP 用戶過濾器不能為空',
|
filter_cannot_be_empty: 'LDAP 用戶過濾器不能為空',
|
||||||
password_cannot_be_empty: 'LDAP 密碼不能為空',
|
password_cannot_be_empty: 'LDAP 密碼不能為空',
|
||||||
|
mapping_cannot_be_empty: 'LDAP 用戶屬性映射不能為空',
|
||||||
},
|
},
|
||||||
schedule: {
|
schedule: {
|
||||||
not_set: "未設置",
|
not_set: "未設置",
|
||||||
|
|
Loading…
Reference in New Issue