• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mendersoftware / gui / 1113439055

19 Dec 2023 09:01PM UTC coverage: 82.752% (-17.2%) from 99.964%
1113439055

Pull #4258

gitlab-ci

mender-test-bot
chore: Types update

Signed-off-by: Mender Test Bot <mender@northern.tech>
Pull Request #4258: chore: Types update

4326 of 6319 branches covered (0.0%)

8348 of 10088 relevant lines covered (82.75%)

189.39 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.55
/src/js/components/devices/device-details/monitoring.js
1
// Copyright 2021 Northern.tech AS
2
//
3
//    Licensed under the Apache License, Version 2.0 (the "License");
4
//    you may not use this file except in compliance with the License.
5
//    You may obtain a copy of the License at
6
//
7
//        http://www.apache.org/licenses/LICENSE-2.0
8
//
9
//    Unless required by applicable law or agreed to in writing, software
10
//    distributed under the License is distributed on an "AS IS" BASIS,
11
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
//    See the License for the specific language governing permissions and
13
//    limitations under the License.
14
import React, { useEffect, useState } from 'react';
15
import { useDispatch, useSelector } from 'react-redux';
16

17
import { useTheme } from '@mui/material/styles';
18

19
import { getDeviceAlerts, setAlertListState } from '../../../actions/monitorActions';
20
import { BENEFITS } from '../../../constants/appConstants';
21
import { DEVICE_LIST_DEFAULTS } from '../../../constants/deviceConstants';
22
import { getOfflineThresholdSettings, getTenantCapabilities } from '../../../selectors';
23
import DocsLink from '../../common/docslink';
24
import EnterpriseNotification from '../../common/enterpriseNotification';
25
import Pagination from '../../common/pagination';
26
import Time from '../../common/time';
27
import MonitorDetailsDialog from '../dialogs/monitordetailsdialog';
28
import { DeviceConnectionNote } from './connection';
29
import DeviceDataCollapse from './devicedatacollapse';
30
import { DeviceOfflineHeaderNotification, NoAlertsHeaderNotification, monitoringSeverities, severityMap } from './notifications';
31

32
const { page: defaultPage, perPage: defaultPerPage } = DEVICE_LIST_DEFAULTS;
9✔
33

34
export const DeviceMonitorsMissingNote = () => (
9✔
35
  <DeviceConnectionNote>
1✔
36
    No alert monitor is currently configured for this device.
37
    <br />
38
    Please <DocsLink path="add-ons/monitor" title="see the documentation" /> for a description on how to configure different kinds of monitors.
39
  </DeviceConnectionNote>
40
);
41

42
const MonitoringAlert = ({ alert, onDetailsClick, style }) => {
9✔
43
  const { description, lines_before = [], lines_after = [], line_matching = '' } = alert.subject.details;
2✔
44
  const lines = [...lines_before, line_matching, ...lines_after].filter(i => i);
2✔
45
  return (
2✔
46
    <div className="monitoring-alert column-data" style={style}>
47
      {(severityMap[alert.level] ?? severityMap[monitoringSeverities.UNKNOWN]).icon}
2!
48
      <div className="key muted">
49
        <b>{alert.name}</b>
50
      </div>
51
      <div>{alert.level}</div>
52
      <Time value={alert.timestamp} />
53
      {(lines.length || description) && <a onClick={() => onDetailsClick(alert)}>view {lines.length ? 'log' : 'details'}</a>}
×
54
    </div>
55
  );
56
};
57

58
const paginationCutoff = defaultPerPage;
9✔
59
export const DeviceMonitoring = ({ device, onDetailsClick }) => {
9✔
60
  const theme = useTheme();
1✔
61
  const { hasMonitor } = useSelector(state => getTenantCapabilities(state));
2✔
62
  const { alerts = [], latest: latestAlerts = [] } = useSelector(state => state.monitor.alerts.byDeviceId[device.id]) ?? {};
2!
63
  const alertListState = useSelector(state => state.monitor.alerts.alertList) ?? {};
2!
64
  const offlineThresholdSettings = useSelector(getOfflineThresholdSettings);
1✔
65
  const dispatch = useDispatch();
1✔
66
  const { page: pageNo = defaultPage, perPage: pageLength = defaultPerPage, total: alertCount } = alertListState;
1!
67

68
  useEffect(() => {
1✔
69
    if (hasMonitor) {
1!
70
      dispatch(getDeviceAlerts(device.id, alertListState));
1✔
71
    }
72
    // eslint-disable-next-line react-hooks/exhaustive-deps
73
  }, [device.id, dispatch, pageNo, pageLength]);
74

75
  const onChangePage = page => dispatch(setAlertListState({ page }));
1✔
76

77
  const onChangeRowsPerPage = perPage => dispatch(setAlertListState({ page: 1, perPage }));
1✔
78

79
  const { monitors = [], isOffline, updated_ts = '' } = device;
1!
80
  const hasMonitorsDefined = !!(monitors.length || alerts.length || latestAlerts.length);
1!
81

82
  return (
1✔
83
    <DeviceDataCollapse
84
      header={
85
        hasMonitorsDefined || isOffline ? (
2!
86
          <>
87
            {hasMonitorsDefined && !latestAlerts.length && <NoAlertsHeaderNotification />}
2!
88
            {latestAlerts.map(alert => (
89
              <MonitoringAlert alert={alert} key={alert.id} onDetailsClick={onDetailsClick} style={{ marginBottom: theme.spacing() }} />
1✔
90
            ))}
91
            {isOffline && <DeviceOfflineHeaderNotification offlineThresholdSettings={offlineThresholdSettings} />}
1!
92
          </>
93
        ) : (
94
          <DeviceMonitorsMissingNote />
95
        )
96
      }
97
      isAddOn
98
      title={
99
        <div className="flexbox center-aligned">
100
          <h4 className="margin-bottom-small margin-right">Monitoring</h4>
101
          {!!monitors.length && <Time className="muted" value={updated_ts} />}
1!
102
          <EnterpriseNotification className="margin-left-small" id={BENEFITS.deviceMonitor.id} />
103
        </div>
104
      }
105
    >
106
      {alerts.length ? (
1!
107
        <>
108
          <div>
109
            <h4 className="muted">Alert history</h4>
110
            {alerts.map(alert => (
111
              <MonitoringAlert alert={alert} key={alert.id} onDetailsClick={onDetailsClick} />
1✔
112
            ))}
113
          </div>
114
          <div className="flexbox margin-top">
115
            {alertCount > paginationCutoff && (
2✔
116
              <Pagination
117
                className="margin-top-none"
118
                count={alertCount}
119
                rowsPerPage={pageLength}
120
                onChangeRowsPerPage={onChangeRowsPerPage}
121
                page={pageNo}
122
                onChangePage={onChangePage}
123
              />
124
            )}
125
          </div>
126
        </>
127
      ) : (
128
        hasMonitorsDefined && (
×
129
          <p className="muted margin-left-large" style={{ fontSize: 'larger' }}>
130
            There are currently no issues reported
131
          </p>
132
        )
133
      )}
134
    </DeviceDataCollapse>
135
  );
136
};
137

138
export const MonitoringTab = ({ device }) => {
9✔
139
  const [monitorDetails, setMonitorDetails] = useState();
1✔
140

141
  return (
1✔
142
    <>
143
      <DeviceMonitoring device={device} onDetailsClick={setMonitorDetails} />
144
      <MonitorDetailsDialog alert={monitorDetails} onClose={() => setMonitorDetails()} />
×
145
    </>
146
  );
147
};
148

149
export default MonitoringTab;
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc