completed migration script; completed adding and editing multiple schedules to monitor

This commit is contained in:
Haileyesus Zemed
2021-01-13 17:45:31 +03:00
parent b48fc673b1
commit 6fd41fe5f1
12 changed files with 273 additions and 100 deletions

View File

@@ -238,23 +238,10 @@ router.post('/:projectId', getUser, isAuthorized, isUserAdmin, async function(
data.projectId = projectId;
data.thirdPartyVariable = [data.name];
const monitor = await MonitorService.create(data);
if (data.callScheduleId) {
const schedule = await ScheduleService.findOneBy({
_id: data.callScheduleId,
});
let monitors = schedule.monitorIds;
if (monitors.length > 0) {
monitors.push({ _id: monitor._id, name: monitor.name });
} else {
monitors = Array(monitor._id);
}
const scheduleData = {
projectId: projectId,
monitorIds: monitors,
};
await ScheduleService.updateOneBy(
{ _id: data.callScheduleId },
scheduleData
if (data.callScheduleIds && data.callScheduleIds.length) {
await ScheduleService.addMonitorToSchedules(
data.callScheduleIds,
monitor._id
);
}
@@ -360,6 +347,15 @@ router.put(
});
}
}
await ScheduleService.deleteMonitor(req.params.monitorId);
if (data.callScheduleIds && data.callScheduleIds.length) {
await ScheduleService.addMonitorToSchedules(
data.callScheduleIds,
req.params.monitorId
);
}
let unsetData;
if (!data.resourceCategory || data.resourceCategory === '') {
unsetData = { resourceCategory: '' };
@@ -369,6 +365,7 @@ router.put(
data,
unsetData
);
if (monitor) {
return sendItemResponse(req, res, monitor);
} else {

View File

@@ -61,11 +61,11 @@ const monitorSchema = new Schema({
default: Date.now,
},
criteria: {
up: [criterionEventSchema],
degraded: [criterionEventSchema],
down: [criterionEventSchema],
up: { type: [criterionEventSchema], default: [] },
degraded: { type: [criterionEventSchema], default: [] },
down: { type: [criterionEventSchema], default: [] },
},
lastMatchedCriterion: criterionEventSchema,
lastMatchedCriterion: { type: criterionEventSchema, default: {} },
method: String,
bodyType: String,
formData: [Object],

View File

@@ -435,8 +435,29 @@ module.exports = {
limit,
skip
);
const monitorsWithSchedules = await Promise.all(
monitors.map(async monitor => {
const monitorSchedules = await ScheduleService.findBy(
{
monitorIds: monitor._id,
}
);
return {
...monitor.toObject(),
schedules: monitorSchedules,
};
})
);
const count = await _this.countBy({ projectId: id });
return { monitors, count, _id: id, skip, limit };
return {
monitors: monitorsWithSchedules,
count,
_id: id,
skip,
limit,
};
})
);
return subProjectMonitors;

View File

@@ -137,6 +137,24 @@ module.exports = {
}
},
addMonitorToSchedules: async function(scheduleIds, monitorId) {
try {
await ScheduleModel.updateMany(
{
_id: { $in: scheduleIds },
},
{
$addToSet: {
monitorIds: monitorId,
},
}
);
} catch (error) {
ErrorService.log('scheduleService.addMonitor', error);
throw error;
}
},
removeMonitor: async function(monitorId) {
try {
const schedule = await ScheduleModel.findOneAndUpdate(
@@ -242,7 +260,7 @@ module.exports = {
deleteMonitor: async function(monitorId) {
try {
await ScheduleModel.update(
await ScheduleModel.updateMany(
{ deleted: false },
{ $pull: { monitorIds: monitorId } }
);

View File

@@ -981,7 +981,7 @@ describe('SMS/Calls Incident Alerts', function() {
const newMonitorId = newMonitor.body._id;
// let the probe server generate incident
await sleep(60 * 1000);
await sleep(120 * 1000);
const { _id: lastIncidentId } = await IncidentService.findOneBy({
monitorId: newMonitorId,
@@ -1034,7 +1034,7 @@ describe('SMS/Calls Incident Alerts', function() {
/*
* run the probe server for this test
*/
this.timeout(120 * 1000);
this.timeout(180 * 1000);
// first add a team member
const userData = {
email: `${generateRandomString}@fyipe.com`,
@@ -1133,7 +1133,7 @@ describe('SMS/Calls Incident Alerts', function() {
const newMonitorId = newMonitor.body._id;
// let the probe server generate incident
await sleep(30 * 1000);
await sleep(120 * 1000);
const { _id: lastIncidentId } = await IncidentService.findOneBy({
monitorId: newMonitorId,

View File

@@ -753,7 +753,7 @@ export function setMonitorCriteria(
monitorName,
monitorCategory,
monitorSubProject,
monitorCallSchedule,
monitorCallSchedules,
monitorSla,
incidentCommunicationSla,
monitorType
@@ -765,7 +765,7 @@ export function setMonitorCriteria(
name: monitorName,
category: monitorCategory,
subProject: monitorSubProject,
schedule: monitorCallSchedule,
schedules: monitorCallSchedules,
type: monitorType,
monitorSla,
incidentCommunicationSla,

View File

@@ -10,6 +10,7 @@ import {
formValueSelector,
change,
isValid,
FieldArray,
} from 'redux-form';
import {
createMonitor,
@@ -57,6 +58,7 @@ import { UploadFile } from '../basic/UploadFile';
import CRITERIA_TYPES from '../../constants/CRITERIA_TYPES';
import { Tab, TabList, TabPanel, Tabs } from 'react-tabs';
import Fade from 'react-reveal/Fade';
import ScheduleInput from '../schedule/ScheduleInput';
const selector = formValueSelector('NewMonitor');
const dJSON = require('dirty-json');
@@ -340,7 +342,17 @@ class NewMonitor extends Component {
: this.props.type;
postObj.resourceCategory =
values[`resourceCategory_${this.props.index}`];
postObj.callScheduleId = values[`callSchedule_${this.props.index}`];
const callSchedules = values[`callSchedules_${this.props.index}`];
let monitorSchedules = [];
if (callSchedules && callSchedules.length) {
monitorSchedules = callSchedules
.filter(schedule => Object.values(schedule)[0] === true)
.map(schedule => {
return Object.keys(schedule)[0];
});
}
postObj.callScheduleIds = monitorSchedules;
if (postObj.type === 'manual')
postObj.data.description =
values[`description_${this.props.index}`] || null;
@@ -596,7 +608,7 @@ class NewMonitor extends Component {
this.props.name,
this.props.category,
this.props.subProject,
this.props.schedule,
this.props.monitorSchedules,
this.props.monitorSla,
this.props.incidentCommunicationSla,
value
@@ -1856,7 +1868,7 @@ class NewMonitor extends Component {
Call Duties
</span>
</span>
<p>
<p className="Flex-flex Flex-alignItems--center">
<span>
Set the
configuration
@@ -1864,55 +1876,7 @@ class NewMonitor extends Component {
Monitor&apos;s
Call duties.
</span>
</p>
</div>
</div>
<div className="nm-Fieldset-row">
<label className="bs-Fieldset-label" />
<label className="new-monitor-label">
Call Schedule
</label>
</div>
<div className="bs-Fieldset-row">
<label className="bs-Fieldset-label" />
<div className="bs-Fieldset-fields">
<span className="flex">
<Field
className="db-select-nw"
component={
RenderSelect
}
name={`callSchedule_${this.props.index}`}
id="callSchedule"
placeholder="Call Duty"
disabled={
requesting
}
style={{
height:
'28px',
}}
options={[
{
value:
'',
label:
'Select call schedule',
},
...(schedules &&
schedules.length >
0
? schedules.map(
schedule => ({
value:
schedule._id,
label:
schedule.name,
})
)
: []),
]}
/>
<Tooltip title="Call Schedule">
<div>
<p>
@@ -1947,6 +1911,40 @@ class NewMonitor extends Component {
</p>
</div>
</Tooltip>
</p>
</div>
</div>
<div className="bs-Fieldset-row">
<label className="bs-Fieldset-label"></label>
<div className="bs-Fieldset-fields">
<span className="flex">
<FieldArray
className="db-select-nw"
component={
ScheduleInput
}
name={`callSchedules_${this.props.index}`}
id="callSchedule"
placeholder="Call Duty"
disabled={
requesting
}
style={{
height:
'28px',
}}
schedules={
this
.props
.schedules
}
currentProject={
this
.props
.currentProject
}
/>
</span>
</div>
</div>
@@ -2620,7 +2618,7 @@ const mapStateToProps = (state, ownProps) => {
const mode = selector(state, 'mode_1000');
const authentication = selector(state, 'authentication_1000');
const category = selector(state, 'resourceCategory_1000');
const schedule = selector(state, 'callSchedule_1000');
const monitorSchedules = selector(state, 'callSchedules_1000');
const monitorSla = selector(state, 'monitorSla');
const incidentCommunicationSla = selector(
state,
@@ -2663,7 +2661,7 @@ const mapStateToProps = (state, ownProps) => {
category,
identityFile: state.monitor.file,
uploadingIdentityFile: state.monitor.uploadFileRequest,
schedule,
monitorSchedules,
monitorSla,
incidentCommunicationSla,
subProjects: state.subProject.subProjects.subProjects,
@@ -2699,7 +2697,7 @@ const mapStateToProps = (state, ownProps) => {
category,
identityFile: state.monitor.file,
uploadingIdentityFile: state.monitor.uploadFileRequest,
schedule,
monitorSchedules,
monitorSla,
incidentCommunicationSla,
resourceCategoryList:
@@ -2747,7 +2745,7 @@ NewMonitor.propTypes = {
authentication: PropTypes.string,
category: PropTypes.string,
subProject: PropTypes.string,
schedule: PropTypes.string,
monitorSchedules: PropTypes.array,
monitorSla: PropTypes.string,
incidentCommunicationSla: PropTypes.string,
resourceCategoryList: PropTypes.array,

View File

@@ -13,6 +13,7 @@ import IsAdminSubProject from '../basic/IsAdminSubProject';
import IsOwnerSubProject from '../basic/IsOwnerSubProject';
import { logEvent } from '../../analytics';
import { SHOULD_LOG_ANALYTICS } from '../../config';
import Tooltip from '../basic/Tooltip';
function submitMonitorForm(values, dispatch, props) {
const { subProjectId, scheduleId } = props.match.params;
@@ -121,6 +122,32 @@ export function MonitorBox(props) {
Schedule Monitors
</span>
</label>
<Tooltip title="Moniors and Criteria Using Schedule">
<div>
<p>
These are the
list of monitors
and criteria in
a monitor using
the schedule.
Note that if an
incident matches
a criterion, the
schedules
associated with
that criterion
will be
executed. If the
criterion has no
schedules
associated, the
monitor&apos;s
schedules will
be used.
</p>
</div>
</Tooltip>
<div className="bs-Fieldset-fields bs-Fieldset-fields--wide">
<div
className="Box-root"

View File

@@ -19,12 +19,27 @@ function MonitorInputs({ monitors, subProject, currentProject, schedule }) {
...monitor.criteria.up,
...monitor.criteria.degraded,
...monitor.criteria.down,
].forEach(criterion => {
].forEach((criterion, index) => {
const monitorUpCriteriaCount =
monitor.criteria.up.length;
const monitorDegradedCriteriaCount =
monitor.criteria.degraded.length;
const type =
index < monitorUpCriteriaCount
? 'Up'
: index <
monitorUpCriteriaCount +
monitorDegradedCriteriaCount
? 'Degraded'
: 'Down';
if (criterion.scheduleIds.includes(schedule._id)) {
criteriaUsingSchedule.push({
id: criterion.id,
name: `${criterion.name ||
'Unnamed Criterion'}`,
name: criterion.default
? 'Default Criterion'
: `${criterion.name ||
'Unnamed Criterion'} (${type})`,
});
}
});
@@ -77,16 +92,19 @@ function MonitorInputs({ monitors, subProject, currentProject, schedule }) {
</div>
</div>
<div className="Box-root Margin-left--32">
{criteriaUsingSchedule.map(criterion => {
return (
<span
key={criterion.id}
className="bs-Fieldset-explanation"
>
{criterion.name}
</span>
);
})}
<ul>
{criteriaUsingSchedule.map(
(criterion, index) => {
return (
<li key={index}>
<span className="bs-Fieldset-explanation">
{criterion.name}
</span>
</li>
);
}
)}
</ul>
</div>
</div>
);

View File

@@ -762,6 +762,7 @@ class MonitorView extends React.Component {
const mapStateToProps = (state, props) => {
const scheduleWarning = [];
const { projectId, componentId, monitorId } = props.match.params;
const schedules = state.schedule.schedules;
state.schedule.subProjectSchedules.forEach(item => {
item.schedules.forEach(item => {
@@ -804,6 +805,18 @@ const mapStateToProps = (state, props) => {
initialValues[`subProject_${monitor._id}`] = monitor.projectId._id;
initialValues[`resourceCategory_${monitor._id}`] =
monitor.resourceCategory && monitor.resourceCategory._id;
const monitorSchedules = [];
if (schedules && schedules.data) {
schedules.data.forEach(schedule => {
monitorSchedules.push({
[schedule._id]: schedule.monitorIds.some(
monitorId => monitorId._id === monitor._id
),
});
});
}
initialValues[`callSchedules_${monitor._id}`] = monitorSchedules;
if (
monitor.incidentCommunicationSla &&
monitor.incidentCommunicationSla._id
@@ -874,7 +887,6 @@ const mapStateToProps = (state, props) => {
/**
* @type { {data : Array}}
*/
const schedules = state.schedule.schedules;
if (
criterionScheduleIds &&
@@ -886,7 +898,7 @@ const mapStateToProps = (state, props) => {
// for each schedule, check if the criterion is already associated with it
schedules.data.forEach(schedule => {
const scheduleId = schedule._id.toString();
const scheduleId = schedule._id;
criterionSchedules.push({
[scheduleId]: criterionScheduleIds.includes(
scheduleId

View File

@@ -1127,7 +1127,7 @@ export default function monitor(state = INITIAL_STATE, action) {
name_1000: action.payload.name,
resourceCategory_1000: action.payload.category,
subProject_1000: action.payload.subProject,
callSchedule_1000: action.payload.schedule,
callSchedules_1000: action.payload.schedules,
monitorSla: action.payload.monitorSla,
incidentCommunicationSla:
action.payload.incidentCommunicationSla,

View File

@@ -0,0 +1,82 @@
const { find, update } = require('../util/db');
const MONITOR_COLLECTION = 'monitors';
async function run() {
const monitorsWithOldCriteria = await find(MONITOR_COLLECTION, {
$or: [
{ 'criteria.up': { $not: { $type: 'array' } } },
{ 'criteria.degraded': { $not: { $type: 'array' } } },
{ 'criteria.down': { $not: { $type: 'array' } } },
],
});
monitorsWithOldCriteria.forEach(monitor => {
const newUpCriteria = [];
const newDegradedCriteria = [];
const newDownCriteria = [];
const newFields = {
scheduleIds: [],
title: '',
description: '',
default: false,
};
// add default criterion
newDownCriteria.push({
createAlert:
monitor.criteria && monitor.criteria.down
? monitor.criteria.down.createAlert
: true,
autoAcknowledge:
monitor.criteria && monitor.criteria.down
? monitor.criteria.down.autoAcknowledge
: false,
autoResolve:
monitor.criteria && monitor.criteria.down
? monitor.criteria.down.autoResolve
: false,
...newFields,
default: true,
});
if (monitor.criteria) {
if (monitor.criteria.up) {
newUpCriteria.push({
...monitor.criteria.up,
...newFields,
name: 'Online',
});
}
if (monitor.criteria.degraded) {
newDegradedCriteria.push({
...monitor.criteria.degraded,
...newFields,
name: 'Degraded',
});
}
if (monitor.criteria.down) {
newDownCriteria.push({
...monitor.criteria.down,
...newFields,
name: 'Offline',
});
}
}
update(
MONITOR_COLLECTION,
{ _id: monitor._id },
{
criteria: {
up: newUpCriteria,
degraded: newDegradedCriteria,
down: newDownCriteria,
},
lastMatchedCriterion: {},
}
);
});
}
module.exports = run;