mirror of
https://github.com/wahyd4/pinpoint.git
synced 2026-08-16 08:16:15 +10:00
@@ -1,113 +1,114 @@
|
||||
/*
|
||||
* Copyright 2014 NAVER Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.navercorp.pinpoint.web.alarm;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
|
||||
import org.springframework.batch.core.ExitStatus;
|
||||
import org.springframework.batch.core.StepExecution;
|
||||
import org.springframework.batch.core.StepExecutionListener;
|
||||
import org.springframework.batch.item.ItemReader;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import com.navercorp.pinpoint.web.alarm.DataCollectorFactory.DataCollectorCategory;
|
||||
import com.navercorp.pinpoint.web.alarm.checker.AlarmChecker;
|
||||
import com.navercorp.pinpoint.web.alarm.collector.DataCollector;
|
||||
import com.navercorp.pinpoint.web.alarm.vo.Rule;
|
||||
import com.navercorp.pinpoint.web.dao.AlarmResourceDao;
|
||||
import com.navercorp.pinpoint.web.dao.ApplicationIndexDao;
|
||||
import com.navercorp.pinpoint.web.vo.Application;
|
||||
|
||||
/**
|
||||
* @author minwoo.jung
|
||||
*/
|
||||
public class AlarmReader implements ItemReader<AlarmChecker>, StepExecutionListener {
|
||||
|
||||
@Autowired
|
||||
private DataCollectorFactory dataCollectorFactory;
|
||||
|
||||
@Autowired
|
||||
private ApplicationIndexDao applicationIndexDao;
|
||||
|
||||
@Autowired
|
||||
private AlarmResourceDao alarmResourceDao;
|
||||
|
||||
private final Queue<AlarmChecker> checkers = new LinkedList<AlarmChecker>();
|
||||
|
||||
public AlarmReader() {
|
||||
}
|
||||
|
||||
protected AlarmReader(DataCollectorFactory dataCollectorFactory, ApplicationIndexDao applicationIndexDao, AlarmResourceDao alarmResourceDao) {
|
||||
this.dataCollectorFactory = dataCollectorFactory;
|
||||
this.applicationIndexDao = applicationIndexDao;
|
||||
this.alarmResourceDao = alarmResourceDao;
|
||||
}
|
||||
|
||||
public AlarmChecker read() {
|
||||
return checkers.poll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeStep(StepExecution stepExecution) {
|
||||
List<Application> applicationList = applicationIndexDao.selectAllApplicationNames();
|
||||
int appSize = applicationList.size();
|
||||
int partitionNumber = (Integer) stepExecution.getExecutionContext().get(AlarmPartitioner.PARTITION_NUMBER);
|
||||
int from = (partitionNumber - 1) * AlarmPartitioner.APP_COUNT;
|
||||
int to = partitionNumber * AlarmPartitioner.APP_COUNT;
|
||||
|
||||
if (appSize < from) {
|
||||
return;
|
||||
}
|
||||
if (appSize < to) {
|
||||
to = appSize;
|
||||
}
|
||||
|
||||
|
||||
for(int i = from; i < to; i++) {
|
||||
addChecker(applicationList.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
private void addChecker(Application application) {
|
||||
List<Rule> rules = alarmResourceDao.selectAppRule(application.getName());
|
||||
long timeSlotEndTime = System.currentTimeMillis();
|
||||
Map<DataCollectorCategory, DataCollector> collectorMap = new HashMap<DataCollectorCategory, DataCollector>();
|
||||
|
||||
for (Rule rule : rules) {
|
||||
CheckerCategory checkerCategory = CheckerCategory.getValue(rule.getCheckerName());
|
||||
DataCollector collector = collectorMap.get(checkerCategory.getDataCollectorCategory());
|
||||
|
||||
if(collector == null) {
|
||||
collector = dataCollectorFactory.createDataCollector(checkerCategory, application, timeSlotEndTime);
|
||||
collectorMap.put(collector.getDataCollectorCategory(), collector);
|
||||
}
|
||||
|
||||
AlarmChecker checker = checkerCategory.createChecker(collector, rule);
|
||||
checkers.add(checker);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExitStatus afterStep(StepExecution stepExecution) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright 2014 NAVER Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.navercorp.pinpoint.web.alarm;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
|
||||
import org.springframework.batch.core.ExitStatus;
|
||||
import org.springframework.batch.core.StepExecution;
|
||||
import org.springframework.batch.core.StepExecutionListener;
|
||||
import org.springframework.batch.item.ItemReader;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import com.navercorp.pinpoint.web.alarm.DataCollectorFactory.DataCollectorCategory;
|
||||
import com.navercorp.pinpoint.web.alarm.checker.AlarmChecker;
|
||||
import com.navercorp.pinpoint.web.alarm.collector.DataCollector;
|
||||
import com.navercorp.pinpoint.web.alarm.vo.Rule;
|
||||
import com.navercorp.pinpoint.web.dao.AlarmDao;
|
||||
import com.navercorp.pinpoint.web.dao.ApplicationIndexDao;
|
||||
import com.navercorp.pinpoint.web.service.AlarmService;
|
||||
import com.navercorp.pinpoint.web.vo.Application;
|
||||
|
||||
/**
|
||||
* @author minwoo.jung
|
||||
*/
|
||||
public class AlarmReader implements ItemReader<AlarmChecker>, StepExecutionListener {
|
||||
|
||||
@Autowired
|
||||
private DataCollectorFactory dataCollectorFactory;
|
||||
|
||||
@Autowired
|
||||
private ApplicationIndexDao applicationIndexDao;
|
||||
|
||||
@Autowired
|
||||
private AlarmService alarmService;
|
||||
|
||||
private final Queue<AlarmChecker> checkers = new LinkedList<AlarmChecker>();
|
||||
|
||||
public AlarmReader() {
|
||||
}
|
||||
|
||||
protected AlarmReader(DataCollectorFactory dataCollectorFactory, ApplicationIndexDao applicationIndexDao, AlarmService alarmService) {
|
||||
this.dataCollectorFactory = dataCollectorFactory;
|
||||
this.applicationIndexDao = applicationIndexDao;
|
||||
this.alarmService = alarmService;
|
||||
}
|
||||
|
||||
public AlarmChecker read() {
|
||||
return checkers.poll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeStep(StepExecution stepExecution) {
|
||||
List<Application> applicationList = applicationIndexDao.selectAllApplicationNames();
|
||||
int appSize = applicationList.size();
|
||||
int partitionNumber = (Integer) stepExecution.getExecutionContext().get(AlarmPartitioner.PARTITION_NUMBER);
|
||||
int from = (partitionNumber - 1) * AlarmPartitioner.APP_COUNT;
|
||||
int to = partitionNumber * AlarmPartitioner.APP_COUNT;
|
||||
|
||||
if (appSize < from) {
|
||||
return;
|
||||
}
|
||||
if (appSize < to) {
|
||||
to = appSize;
|
||||
}
|
||||
|
||||
|
||||
for(int i = from; i < to; i++) {
|
||||
addChecker(applicationList.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
private void addChecker(Application application) {
|
||||
List<Rule> rules = alarmService.selectRuleByApplicationId(application.getName());
|
||||
long timeSlotEndTime = System.currentTimeMillis();
|
||||
Map<DataCollectorCategory, DataCollector> collectorMap = new HashMap<DataCollectorCategory, DataCollector>();
|
||||
|
||||
for (Rule rule : rules) {
|
||||
CheckerCategory checkerCategory = CheckerCategory.getValue(rule.getCheckerName());
|
||||
DataCollector collector = collectorMap.get(checkerCategory.getDataCollectorCategory());
|
||||
|
||||
if(collector == null) {
|
||||
collector = dataCollectorFactory.createDataCollector(checkerCategory, application, timeSlotEndTime);
|
||||
collectorMap.put(collector.getDataCollectorCategory(), collector);
|
||||
}
|
||||
|
||||
AlarmChecker checker = checkerCategory.createChecker(collector, rule);
|
||||
checkers.add(checker);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExitStatus afterStep(StepExecution stepExecution) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,190 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014 NAVER Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.navercorp.pinpoint.web.controller;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
import com.navercorp.pinpoint.web.alarm.CheckerCategory;
|
||||
import com.navercorp.pinpoint.web.alarm.vo.AlarmEmp;
|
||||
import com.navercorp.pinpoint.web.alarm.vo.Rule;
|
||||
import com.navercorp.pinpoint.web.dao.mysql.MySqlAlarmResourceDao;
|
||||
import com.navercorp.pinpoint.web.service.CommonService;
|
||||
import com.navercorp.pinpoint.web.vo.Application;
|
||||
|
||||
/**
|
||||
* @author minwoo.jung
|
||||
*/
|
||||
@Controller
|
||||
public class AlarmRuleController {
|
||||
|
||||
@Autowired
|
||||
private MySqlAlarmResourceDao dao;
|
||||
|
||||
@Autowired
|
||||
private CommonService commonService;
|
||||
|
||||
@RequestMapping(value = "/alarmGroupList")
|
||||
public List<String> alarmGroupList() {
|
||||
return dao.selectEmpGroupName();
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/alarmGroup/getMember")
|
||||
public ModelAndView getMember(String groupName) {
|
||||
ModelAndView mv = new ModelAndView();
|
||||
|
||||
if (groupName != null) {
|
||||
List<AlarmEmp> members = dao.selectEmpGroupMember(groupName);
|
||||
mv.addObject("groupMember", members);
|
||||
}
|
||||
|
||||
List<String> groupNameList = alarmGroupList();
|
||||
mv.addObject("groupNameList", groupNameList);
|
||||
|
||||
mv.setViewName("alarm/empGroup");
|
||||
|
||||
return mv;
|
||||
}
|
||||
|
||||
public static class EmpGroup {
|
||||
private List<AlarmEmp> emps;
|
||||
|
||||
public List<AlarmEmp> getEmps() {
|
||||
return emps;
|
||||
}
|
||||
|
||||
public void setEmps(List<AlarmEmp> emps) {
|
||||
this.emps = emps;
|
||||
}
|
||||
}
|
||||
|
||||
private List<AlarmEmp> removeEmptyEmp(List<AlarmEmp> emps) {
|
||||
List<AlarmEmp> newEmps = new LinkedList<AlarmEmp>();
|
||||
|
||||
for(AlarmEmp emp : emps) {
|
||||
if (emp.getGroupName() != null && emp.getEmpId() != null) {
|
||||
if (!emp.getGroupName().isEmpty() && !emp.getEmpId().isEmpty()) {
|
||||
newEmps.add(emp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return newEmps;
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/alarmGroup/insertMember")
|
||||
public String insertMember(EmpGroup empGroup) {
|
||||
List<AlarmEmp> emps = removeEmptyEmp(empGroup.getEmps());
|
||||
dao.insertEmpGroupMember(emps);
|
||||
return "redirect:/alarmGroup/getMember.pinpoint?groupName=" + emps.get(0).getGroupName();
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/alarmGroup/deleteMember")
|
||||
public String deleteMember(EmpGroup empGroup) {
|
||||
dao.deleteEmpGroupMember(empGroup.getEmps().get(0).getGroupName());
|
||||
return "redirect:/alarmGroup/getMember.pinpoint?groupName=" + empGroup.getEmps().get(0).getGroupName();
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/alarmGroup/updateMember")
|
||||
public String updateMember(EmpGroup empGroup) {
|
||||
List<AlarmEmp> emps = removeEmptyEmp(empGroup.getEmps());
|
||||
dao.deleteEmpGroupMember(emps.get(0).getGroupName());
|
||||
dao.insertEmpGroupMember(emps);
|
||||
return "redirect:/alarmGroup/getMember.pinpoint?groupName=" + emps.get(0).getGroupName();
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/alarmRule/ruleNameList")
|
||||
public List<String> getAlarmRuleNames() {
|
||||
return CheckerCategory.getNames();
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/alarmRule/getRule")
|
||||
public ModelAndView getRule(String applicationName) {
|
||||
ModelAndView mv = new ModelAndView();
|
||||
|
||||
if (applicationName != null) {
|
||||
List<Rule> ruleList = dao.selectAppRule(applicationName);
|
||||
mv.addObject("ruleList", ruleList);
|
||||
}
|
||||
|
||||
List<Application> applicationList = commonService.selectAllApplicationNames();
|
||||
List<String> applicationNameList = new LinkedList<String>();
|
||||
|
||||
for(Application application : applicationList) {
|
||||
applicationNameList.add(application.getName());
|
||||
}
|
||||
|
||||
mv.addObject("applicationNameList", applicationNameList);
|
||||
mv.addObject("empGroupNameList", alarmGroupList());
|
||||
mv.addObject("checkerNameList", getAlarmRuleNames());
|
||||
mv.setViewName("alarm/rule");
|
||||
return mv;
|
||||
}
|
||||
|
||||
public static class RuleGroup {
|
||||
private List<Rule> ruleList;
|
||||
|
||||
public List<Rule> getRuleList() {
|
||||
return ruleList;
|
||||
}
|
||||
|
||||
public void setRuleList(List<Rule> ruleList) {
|
||||
this.ruleList = ruleList;
|
||||
}
|
||||
}
|
||||
|
||||
private List<Rule> removeEmptyRule(List<Rule> ruleList) {
|
||||
List<Rule> newRuleList = new LinkedList<Rule>();
|
||||
|
||||
for(Rule rule : ruleList) {
|
||||
if (rule.getApplicationId() != null && !rule.getApplicationId().isEmpty()) {
|
||||
if (rule.getThreshold() >= 0) {
|
||||
newRuleList.add(rule);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return newRuleList;
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/alarmRule/insertRule")
|
||||
public String insertRule(RuleGroup ruleGroup) {
|
||||
List<Rule> ruleList = removeEmptyRule(ruleGroup.getRuleList());
|
||||
dao.insertAppRule(ruleList);
|
||||
return "redirect:/alarmRule/getRule.pinpoint?applicationName=" + ruleList.get(0).getApplicationId();
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/alarmRule/deleteRule")
|
||||
public String deleteRule(RuleGroup ruleGroup) {
|
||||
dao.deleteAppRule(ruleGroup.getRuleList().get(0).getApplicationId());
|
||||
return "redirect:/alarmRule/getRule.pinpoint?applicationName=" + ruleGroup.getRuleList().get(0).getApplicationId();
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/alarmRule/updateRule")
|
||||
public String updateRule(RuleGroup ruleGroup) {
|
||||
List<Rule> ruleList = removeEmptyRule(ruleGroup.getRuleList());
|
||||
dao.deleteAppRule(ruleList.get(0).getApplicationId());
|
||||
dao.insertAppRule(ruleList);
|
||||
return "redirect:/alarmRule/getRule.pinpoint?applicationName=" + ruleGroup.getRuleList().get(0).getApplicationId();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -31,7 +31,8 @@ public interface AlarmDao {
|
||||
void deleteRuleByUserGroupId(String userGroupId);
|
||||
|
||||
List<Rule> selectRuleByUserGroupId(String userGroupId);
|
||||
|
||||
List<Rule> selectRuleByApplicationId(String applicationId);
|
||||
|
||||
void updateRule(Rule rule);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014 NAVER Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.navercorp.pinpoint.web.dao;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.navercorp.pinpoint.web.alarm.vo.AlarmEmp;
|
||||
import com.navercorp.pinpoint.web.alarm.vo.Rule;
|
||||
|
||||
|
||||
public interface AlarmResourceDao {
|
||||
|
||||
List<Rule> selectAppRule(String applicationName);
|
||||
|
||||
void insertAppRule(List<Rule> rules);
|
||||
|
||||
void deleteAppRule(String applicationName);
|
||||
|
||||
List<String> selectEmpGroupPhoneNumber(String empGroup);
|
||||
|
||||
List<String> selectEmpGroupEmail(String empGroup);
|
||||
|
||||
List<String> selectEmpGroupName();
|
||||
|
||||
List<AlarmEmp> selectEmpGroupMember(String alarmGroup);
|
||||
|
||||
void insertEmpGroupMember(List<AlarmEmp> emps);
|
||||
|
||||
void deleteEmpGroupMember(String groupName);
|
||||
}
|
||||
@@ -40,4 +40,8 @@ public interface UserGroupDao {
|
||||
|
||||
void updateMember(UserGroupMember userGroupMember);
|
||||
|
||||
List<String> selectPhoneNumberOfMember(String userGroupId);
|
||||
|
||||
List<String> selectEmailOfMember(String userGroupId);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014 NAVER Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.navercorp.pinpoint.web.dao.mysql;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.mybatis.spring.SqlSessionTemplate;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import com.navercorp.pinpoint.web.alarm.vo.AlarmEmp;
|
||||
import com.navercorp.pinpoint.web.alarm.vo.Rule;
|
||||
import com.navercorp.pinpoint.web.dao.AlarmResourceDao;
|
||||
|
||||
@Repository
|
||||
public class MySqlAlarmResourceDao implements AlarmResourceDao {
|
||||
|
||||
private static final String NAMESPACE = AlarmResourceDao.class.getPackage().getName() + "." + AlarmResourceDao.class.getSimpleName() + ".";
|
||||
|
||||
@Autowired
|
||||
@Qualifier("sqlSessionTemplate")
|
||||
private SqlSessionTemplate sqlSessionTemplate;
|
||||
|
||||
public SqlSessionTemplate getSqlSessionTemplate() {
|
||||
return sqlSessionTemplate;
|
||||
}
|
||||
|
||||
public List<Rule> selectAppRule(String applicationName) {
|
||||
return getSqlSessionTemplate().selectList(NAMESPACE + "selectRules", applicationName);
|
||||
}
|
||||
|
||||
public void insertAppRule(List<Rule> rules) {
|
||||
getSqlSessionTemplate().selectList(NAMESPACE + "insertAppRule", rules);
|
||||
}
|
||||
|
||||
public void deleteAppRule(String applicationName) {
|
||||
getSqlSessionTemplate().selectList(NAMESPACE + "deleteAppRule", applicationName);
|
||||
}
|
||||
|
||||
public List<String> selectEmpGroupPhoneNumber(String empGroup) {
|
||||
return getSqlSessionTemplate().selectList(NAMESPACE + "selectEmpGroupPhoneNumber", empGroup);
|
||||
}
|
||||
|
||||
public List<String> selectEmpGroupEmail(String empGroup) {
|
||||
return getSqlSessionTemplate().selectList(NAMESPACE + "selectEmpGroupEmail", empGroup);
|
||||
}
|
||||
|
||||
public List<String> selectEmpGroupName() {
|
||||
return getSqlSessionTemplate().selectList(NAMESPACE + "selectAlarmGroupList");
|
||||
}
|
||||
|
||||
public List<AlarmEmp> selectEmpGroupMember(String alarmGroup) {
|
||||
return getSqlSessionTemplate().selectList(NAMESPACE + "selectAlarmGroupMember", alarmGroup);
|
||||
}
|
||||
|
||||
public void insertEmpGroupMember(List<AlarmEmp> emps) {
|
||||
getSqlSessionTemplate().insert(NAMESPACE + "insertAlarmGroupMember", emps);
|
||||
}
|
||||
|
||||
public void deleteEmpGroupMember(String groupName) {
|
||||
getSqlSessionTemplate().insert(NAMESPACE + "deleteAlarmGroupMember", groupName);
|
||||
}
|
||||
}
|
||||
@@ -57,10 +57,14 @@ public class MysqlAlarmDao implements AlarmDao {
|
||||
public List<Rule> selectRuleByUserGroupId(String userGroupId) {
|
||||
return sqlSessionTemplate.selectList(NAMESPACE + "selectRuleByUserGroupId", userGroupId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Rule> selectRuleByApplicationId(String applicationId) {
|
||||
return sqlSessionTemplate.selectList(NAMESPACE + "selectRuleByApplicationId", applicationId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateRule(Rule rule) {
|
||||
sqlSessionTemplate.update(NAMESPACE + "updateRule", rule);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -81,5 +81,15 @@ public class MysqlUserGroupDao implements UserGroupDao {
|
||||
public void updateMember(UserGroupMember userGroupMember) {
|
||||
sqlSessionTemplate.delete(NAMESPACE + "updateMember", userGroupMember);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> selectPhoneNumberOfMember(String userGroupId) {
|
||||
return sqlSessionTemplate.selectList(NAMESPACE + "selectPhoneNumberOfMember", userGroupId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> selectEmailOfMember(String userGroupId) {
|
||||
return sqlSessionTemplate.selectList(NAMESPACE + "selectEmailOfMember", userGroupId);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -30,6 +30,9 @@ public interface AlarmService {
|
||||
|
||||
List<Rule> selectRuleByUserGroupId(String userGroupId);
|
||||
|
||||
List<Rule> selectRuleByApplicationId(String applicationId);
|
||||
|
||||
void updateRule(Rule rule);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -48,9 +48,15 @@ public class AlarmServiceImpl implements AlarmService {
|
||||
return alarmDao.selectRuleByUserGroupId(userGroupId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Rule> selectRuleByApplicationId(String applicationId) {
|
||||
return alarmDao.selectRuleByApplicationId(applicationId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateRule(Rule rule) {
|
||||
alarmDao.updateRule(rule);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -40,4 +40,8 @@ public interface UserGroupService {
|
||||
|
||||
void updateMember(UserGroupMember userGroupMember);
|
||||
|
||||
List<String> selectPhoneNumberOfMember(String userGroupId);
|
||||
|
||||
List<String> selectEmailOfMember(String userGroupId);
|
||||
|
||||
}
|
||||
|
||||
@@ -73,6 +73,18 @@ public class UserGroupServiceImpl implements UserGroupService {
|
||||
public void updateMember(UserGroupMember userGroupMember) {
|
||||
userGroupDao.updateMember(userGroupMember);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> selectPhoneNumberOfMember(String userGroupId) {
|
||||
return userGroupDao.selectPhoneNumberOfMember(userGroupId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> selectEmailOfMember(String userGroupId) {
|
||||
return userGroupDao.selectEmailOfMember(userGroupId);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -2,25 +2,11 @@
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
|
||||
<mapper namespace="com.navercorp.pinpoint.web.dao.AlarmResourceDao">
|
||||
<select id="selectRules" resultType="rule" parameterType="String">
|
||||
SELECT *
|
||||
FROM alarm_rule
|
||||
WHERE application_id = #{applicationId}
|
||||
</select>
|
||||
|
||||
<insert id="insertAppRule" parameterType="java.util.List">
|
||||
INSERT INTO alarm_rule(application_id, checker_name, threshold, user_group_id, sms_send, email_send, notes)
|
||||
VALUES
|
||||
<foreach collection="list" item="rule" separator=",">
|
||||
(#{rule.applicationId}, #{rule.checkerName}, #{rule.threshold}, #{rule.userGroupId}, #{rule.smsSend}, #{rule.emailSend}, #{rule.notes})
|
||||
</foreach>
|
||||
</insert>
|
||||
|
||||
<delete id="deleteAppRule" parameterType="String">
|
||||
DELETE
|
||||
FROM alarm_rule
|
||||
WHERE application_id = #{applicationId}
|
||||
</delete>
|
||||
<!-- <select id="selectRules" resultType="rule" parameterType="String"> -->
|
||||
<!-- SELECT * -->
|
||||
<!-- FROM alarm_rule -->
|
||||
<!-- WHERE application_id = #{applicationId} -->
|
||||
<!-- </select> -->
|
||||
|
||||
<select id="selectEmpGroupPhoneNumber" resultType="String" parameterType="String">
|
||||
SELECT sms
|
||||
@@ -41,29 +27,44 @@
|
||||
WHERE group_name = #{group_name}
|
||||
)
|
||||
</select>
|
||||
|
||||
<!-- <insert id="insertAppRule" parameterType="java.util.List"> -->
|
||||
<!-- INSERT INTO alarm_rule(application_id, checker_name, threshold, user_group_id, sms_send, email_send, notes) -->
|
||||
<!-- VALUES -->
|
||||
<!-- <foreach collection="list" item="rule" separator=","> -->
|
||||
<!-- (#{rule.applicationId}, #{rule.checkerName}, #{rule.threshold}, #{rule.userGroupId}, #{rule.smsSend}, #{rule.emailSend}, #{rule.notes}) -->
|
||||
<!-- </foreach> -->
|
||||
<!-- </insert> -->
|
||||
|
||||
<select id="selectAlarmGroupList" resultType="String" parameterType="String">
|
||||
SELECT DISTINCT(group_name)
|
||||
FROM alarm_group
|
||||
</select>
|
||||
<!-- <delete id="deleteAppRule" parameterType="String"> -->
|
||||
<!-- DELETE -->
|
||||
<!-- FROM alarm_rule -->
|
||||
<!-- WHERE application_id = #{applicationId} -->
|
||||
<!-- </delete> -->
|
||||
|
||||
<select id="selectAlarmGroupMember" resultType="alarmEmp" parameterType="String">
|
||||
SELECT *
|
||||
FROM alarm_group
|
||||
WHERE group_name = #{group_name}
|
||||
</select>
|
||||
|
||||
<insert id="insertAlarmGroupMember" parameterType="java.util.List">
|
||||
INSERT INTO alarm_group(group_name, emp_id)
|
||||
VALUES
|
||||
<foreach collection="list" item="alarmEmp" separator=",">
|
||||
(#{alarmEmp.groupName}, #{alarmEmp.empId})
|
||||
</foreach>
|
||||
</insert>
|
||||
<!-- <select id="selectAlarmGroupList" resultType="String" parameterType="String"> -->
|
||||
<!-- SELECT DISTINCT(group_name) -->
|
||||
<!-- FROM alarm_group -->
|
||||
<!-- </select> -->
|
||||
|
||||
<delete id="deleteAlarmGroupMember" parameterType="String">
|
||||
DELETE
|
||||
FROM alarm_group
|
||||
WHERE group_name = #{group_name}
|
||||
</delete>
|
||||
<!-- <select id="selectAlarmGroupMember" resultType="alarmEmp" parameterType="String"> -->
|
||||
<!-- SELECT * -->
|
||||
<!-- FROM alarm_group -->
|
||||
<!-- WHERE group_name = #{group_name} -->
|
||||
<!-- </select> -->
|
||||
|
||||
<!-- <insert id="insertAlarmGroupMember" parameterType="java.util.List"> -->
|
||||
<!-- INSERT INTO alarm_group(group_name, emp_id) -->
|
||||
<!-- VALUES -->
|
||||
<!-- <foreach collection="list" item="alarmEmp" separator=","> -->
|
||||
<!-- (#{alarmEmp.groupName}, #{alarmEmp.empId}) -->
|
||||
<!-- </foreach> -->
|
||||
<!-- </insert> -->
|
||||
|
||||
<!-- <delete id="deleteAlarmGroupMember" parameterType="String"> -->
|
||||
<!-- DELETE -->
|
||||
<!-- FROM alarm_group -->
|
||||
<!-- WHERE group_name = #{group_name} -->
|
||||
<!-- </delete> -->
|
||||
</mapper>
|
||||
|
||||
@@ -26,6 +26,12 @@
|
||||
FROM alarm_rule
|
||||
WHERE user_group_id = #{userGroupId}
|
||||
</select>
|
||||
|
||||
<select id="selectRuleByApplicationId" resultType="Rule">
|
||||
SELECT *
|
||||
FROM alarm_rule
|
||||
WHERE application_id = #{applicationId}
|
||||
</select>
|
||||
|
||||
<update id="updateRule">
|
||||
UPDATE alarm_rule
|
||||
|
||||
@@ -48,4 +48,25 @@
|
||||
WHERE number = #{number}
|
||||
</update>
|
||||
|
||||
<select id="selectPhoneNumberOfMember" resultType="String" parameterType="String">
|
||||
SELECT phoneNumber
|
||||
FROM user
|
||||
WHERE user_id IN (
|
||||
SELECT member_id
|
||||
FROM user_group_member
|
||||
WHERE user_group_id = #{userGroupId}
|
||||
)
|
||||
</select>
|
||||
|
||||
<select id="selectEmailOfMember" resultType="String" parameterType="String">
|
||||
SELECT email
|
||||
FROM user
|
||||
WHERE user_id IN (
|
||||
SELECT member_id
|
||||
FROM user_group_member
|
||||
WHERE user_group_id = #{userGroupId}
|
||||
)
|
||||
</select>
|
||||
|
||||
|
||||
</mapper>
|
||||
|
||||
@@ -1,153 +1,149 @@
|
||||
/*
|
||||
* Copyright 2014 NAVER Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.navercorp.pinpoint.web.alarm;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.springframework.batch.core.StepExecution;
|
||||
import org.springframework.batch.item.ExecutionContext;
|
||||
|
||||
import com.navercorp.pinpoint.common.trace.ServiceType;
|
||||
import com.navercorp.pinpoint.web.alarm.AlarmPartitioner;
|
||||
import com.navercorp.pinpoint.web.alarm.AlarmReader;
|
||||
import com.navercorp.pinpoint.web.alarm.CheckerCategory;
|
||||
import com.navercorp.pinpoint.web.alarm.DataCollectorFactory;
|
||||
import com.navercorp.pinpoint.web.alarm.collector.DataCollector;
|
||||
import com.navercorp.pinpoint.web.alarm.collector.ResponseTimeDataCollector;
|
||||
import com.navercorp.pinpoint.web.alarm.vo.Rule;
|
||||
import com.navercorp.pinpoint.web.dao.AlarmResourceDao;
|
||||
import com.navercorp.pinpoint.web.dao.ApplicationIndexDao;
|
||||
import com.navercorp.pinpoint.web.dao.mysql.MySqlAlarmResourceDao;
|
||||
import com.navercorp.pinpoint.web.vo.Application;
|
||||
|
||||
public class ReaderTest {
|
||||
|
||||
private static ApplicationIndexDao applicationIndexDao;
|
||||
private static AlarmResourceDao alarmResourceDao;
|
||||
private static DataCollectorFactory dataCollectorFactory;
|
||||
private static final String APP_NAME = "app";
|
||||
|
||||
@Test
|
||||
public void readTest() {
|
||||
StepExecution stepExecution = new StepExecution("alarmStep", null);
|
||||
ExecutionContext executionContext = new ExecutionContext();
|
||||
executionContext.put(AlarmPartitioner.PARTITION_NUMBER, 1);
|
||||
stepExecution.setExecutionContext(executionContext);
|
||||
|
||||
AlarmReader reader = new AlarmReader(dataCollectorFactory, applicationIndexDao, alarmResourceDao);
|
||||
|
||||
reader.beforeStep(stepExecution);
|
||||
|
||||
for(int i = 0; i < 5; i++) {
|
||||
assertNotNull(reader.read());
|
||||
}
|
||||
|
||||
assertNull(reader.read());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readTest2() {
|
||||
StepExecution stepExecution = new StepExecution("alarmStep", null);
|
||||
ExecutionContext executionContext = new ExecutionContext();
|
||||
executionContext.put(AlarmPartitioner.PARTITION_NUMBER, 2);
|
||||
stepExecution.setExecutionContext(executionContext);
|
||||
|
||||
AlarmReader reader = new AlarmReader(dataCollectorFactory, applicationIndexDao, alarmResourceDao);
|
||||
|
||||
reader.beforeStep(stepExecution);
|
||||
|
||||
for(int i = 0; i < 2; i++) {
|
||||
assertNotNull(reader.read());
|
||||
}
|
||||
|
||||
assertNull(reader.read());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readTest3() {
|
||||
StepExecution stepExecution = new StepExecution("alarmStep", null);
|
||||
ExecutionContext executionContext = new ExecutionContext();
|
||||
executionContext.put(AlarmPartitioner.PARTITION_NUMBER, 2);
|
||||
stepExecution.setExecutionContext(executionContext);
|
||||
|
||||
MySqlAlarmResourceDao alarmResourceDao = new MySqlAlarmResourceDao() {
|
||||
@Override
|
||||
public java.util.List<Rule> selectAppRule(String applicationName) {
|
||||
return new LinkedList<Rule>();
|
||||
}
|
||||
};
|
||||
|
||||
AlarmReader reader = new AlarmReader(dataCollectorFactory, applicationIndexDao, alarmResourceDao);
|
||||
reader.beforeStep(stepExecution);
|
||||
assertNull(reader.read());
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeClass() {
|
||||
applicationIndexDao = new ApplicationIndexDao() {
|
||||
|
||||
@Override
|
||||
public List<Application> selectAllApplicationNames() {
|
||||
List<Application> apps = new LinkedList<Application>();
|
||||
|
||||
for(int i = 0; i < 7; i++) {
|
||||
apps.add(new Application(APP_NAME + i, ServiceType.STAND_ALONE));
|
||||
}
|
||||
return apps;
|
||||
}
|
||||
|
||||
@Override public List<String> selectAgentIds(String applicationName) {return null;}
|
||||
@Override public void deleteApplicationName(String applicationName) { }
|
||||
@Override public void deleteAgentId(String applicationName, String agentId) {}
|
||||
|
||||
};
|
||||
|
||||
alarmResourceDao = new MySqlAlarmResourceDao() {
|
||||
private Map<String, Rule> ruleMap ;
|
||||
|
||||
{
|
||||
ruleMap = new HashMap<String, Rule>();
|
||||
|
||||
for(int i = 0; i <=6; i++) {
|
||||
ruleMap.put(APP_NAME + i, new Rule(APP_NAME + i, CheckerCategory.SLOW_COUNT.getName(), 76, "testGroup", false, false, ""));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public java.util.List<Rule> selectAppRule(String applicationName) {
|
||||
List<Rule> rules = new LinkedList<Rule>();
|
||||
rules.add(ruleMap.get(applicationName));
|
||||
return rules;
|
||||
}
|
||||
};
|
||||
|
||||
dataCollectorFactory = new DataCollectorFactory() {
|
||||
@Override
|
||||
public DataCollector createDataCollector(CheckerCategory checker, Application application, long timeSlotEndTime) {
|
||||
return new ResponseTimeDataCollector(DataCollectorCategory.RESPONSE_TIME, null, null, 0, 0);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright 2014 NAVER Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.navercorp.pinpoint.web.alarm;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.springframework.batch.core.StepExecution;
|
||||
import org.springframework.batch.item.ExecutionContext;
|
||||
|
||||
import com.navercorp.pinpoint.common.trace.ServiceType;
|
||||
import com.navercorp.pinpoint.web.alarm.collector.DataCollector;
|
||||
import com.navercorp.pinpoint.web.alarm.collector.ResponseTimeDataCollector;
|
||||
import com.navercorp.pinpoint.web.alarm.vo.Rule;
|
||||
import com.navercorp.pinpoint.web.dao.ApplicationIndexDao;
|
||||
import com.navercorp.pinpoint.web.service.AlarmService;
|
||||
import com.navercorp.pinpoint.web.service.AlarmServiceImpl;
|
||||
import com.navercorp.pinpoint.web.vo.Application;
|
||||
|
||||
public class ReaderTest {
|
||||
|
||||
private static ApplicationIndexDao applicationIndexDao;
|
||||
private static AlarmService alarmService;
|
||||
private static DataCollectorFactory dataCollectorFactory;
|
||||
private static final String APP_NAME = "app";
|
||||
|
||||
@Test
|
||||
public void readTest() {
|
||||
StepExecution stepExecution = new StepExecution("alarmStep", null);
|
||||
ExecutionContext executionContext = new ExecutionContext();
|
||||
executionContext.put(AlarmPartitioner.PARTITION_NUMBER, 1);
|
||||
stepExecution.setExecutionContext(executionContext);
|
||||
|
||||
AlarmReader reader = new AlarmReader(dataCollectorFactory, applicationIndexDao, alarmService);
|
||||
|
||||
reader.beforeStep(stepExecution);
|
||||
|
||||
for(int i = 0; i < 5; i++) {
|
||||
assertNotNull(reader.read());
|
||||
}
|
||||
|
||||
assertNull(reader.read());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readTest2() {
|
||||
StepExecution stepExecution = new StepExecution("alarmStep", null);
|
||||
ExecutionContext executionContext = new ExecutionContext();
|
||||
executionContext.put(AlarmPartitioner.PARTITION_NUMBER, 2);
|
||||
stepExecution.setExecutionContext(executionContext);
|
||||
|
||||
AlarmReader reader = new AlarmReader(dataCollectorFactory, applicationIndexDao, alarmService);
|
||||
|
||||
reader.beforeStep(stepExecution);
|
||||
|
||||
for(int i = 0; i < 2; i++) {
|
||||
assertNotNull(reader.read());
|
||||
}
|
||||
|
||||
assertNull(reader.read());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readTest3() {
|
||||
StepExecution stepExecution = new StepExecution("alarmStep", null);
|
||||
ExecutionContext executionContext = new ExecutionContext();
|
||||
executionContext.put(AlarmPartitioner.PARTITION_NUMBER, 2);
|
||||
stepExecution.setExecutionContext(executionContext);
|
||||
|
||||
AlarmServiceImpl alarmService = new AlarmServiceImpl() {
|
||||
@Override
|
||||
public java.util.List<Rule> selectRuleByApplicationId(String applicationId) {
|
||||
return new LinkedList<Rule>();
|
||||
};
|
||||
};
|
||||
|
||||
AlarmReader reader = new AlarmReader(dataCollectorFactory, applicationIndexDao, alarmService);
|
||||
reader.beforeStep(stepExecution);
|
||||
assertNull(reader.read());
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeClass() {
|
||||
applicationIndexDao = new ApplicationIndexDao() {
|
||||
|
||||
@Override
|
||||
public List<Application> selectAllApplicationNames() {
|
||||
List<Application> apps = new LinkedList<Application>();
|
||||
|
||||
for(int i = 0; i < 7; i++) {
|
||||
apps.add(new Application(APP_NAME + i, ServiceType.STAND_ALONE));
|
||||
}
|
||||
return apps;
|
||||
}
|
||||
|
||||
@Override public List<String> selectAgentIds(String applicationName) {return null;}
|
||||
@Override public void deleteApplicationName(String applicationName) { }
|
||||
@Override public void deleteAgentId(String applicationName, String agentId) {}
|
||||
|
||||
};
|
||||
|
||||
alarmService = new AlarmServiceImpl() {
|
||||
private Map<String, Rule> ruleMap ;
|
||||
|
||||
{
|
||||
ruleMap = new HashMap<String, Rule>();
|
||||
|
||||
for(int i = 0; i <=6; i++) {
|
||||
ruleMap.put(APP_NAME + i, new Rule(APP_NAME + i, CheckerCategory.SLOW_COUNT.getName(), 76, "testGroup", false, false, ""));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Rule> selectRuleByApplicationId(String applicationId) {
|
||||
List<Rule> rules = new LinkedList<Rule>();
|
||||
rules.add(ruleMap.get(applicationId));
|
||||
return rules;
|
||||
}
|
||||
};
|
||||
|
||||
dataCollectorFactory = new DataCollectorFactory() {
|
||||
@Override
|
||||
public DataCollector createDataCollector(CheckerCategory checker, Application application, long timeSlotEndTime) {
|
||||
return new ResponseTimeDataCollector(DataCollectorCategory.RESPONSE_TIME, null, null, 0, 0);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user