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

mendersoftware / gui / 1057188406

01 Nov 2023 04:24AM UTC coverage: 82.824% (-17.1%) from 99.964%
1057188406

Pull #4134

gitlab-ci

web-flow
chore: Bump uuid from 9.0.0 to 9.0.1

Bumps [uuid](https://github.com/uuidjs/uuid) from 9.0.0 to 9.0.1.
- [Changelog](https://github.com/uuidjs/uuid/blob/main/CHANGELOG.md)
- [Commits](https://github.com/uuidjs/uuid/compare/v9.0.0...v9.0.1)

---
updated-dependencies:
- dependency-name: uuid
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Pull Request #4134: chore: Bump uuid from 9.0.0 to 9.0.1

4349 of 6284 branches covered (0.0%)

8313 of 10037 relevant lines covered (82.82%)

200.97 hits per line

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

82.26
/src/js/components/devices/device-details/connection.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, { useCallback, useEffect, useRef, useState } from 'react';
15
import { useDispatch, useSelector } from 'react-redux';
16
import { Link } from 'react-router-dom';
17

18
import { InfoOutlined as InfoIcon, Launch as LaunchIcon } from '@mui/icons-material';
19
import { Button, Typography } from '@mui/material';
20
import { makeStyles } from 'tss-react/mui';
21

22
import { setSnackbar } from '../../../actions/appActions';
23
import { getDeviceFileDownloadLink } from '../../../actions/deviceActions';
24
import { BEGINNING_OF_TIME, BENEFITS, TIMEOUTS } from '../../../constants/appConstants';
25
import { ALL_DEVICES, DEVICE_CONNECT_STATES } from '../../../constants/deviceConstants';
26
import { AUDIT_LOGS_TYPES } from '../../../constants/organizationConstants';
27
import { checkPermissionsObject, uiPermissionsById } from '../../../constants/userConstants';
28
import { createDownload } from '../../../helpers';
29
import { getTenantCapabilities, getUserCapabilities } from '../../../selectors';
30
import { formatAuditlogs } from '../../../utils/locationutils';
31
import DocsLink from '../../common/docslink';
32
import EnterpriseNotification from '../../common/enterpriseNotification';
33
import Loader from '../../common/loader';
34
import MenderTooltip from '../../common/mendertooltip';
35
import Time from '../../common/time';
36
import FileTransfer from '../troubleshoot/filetransfer';
37
import TroubleshootContent from '../troubleshoot/terminal-wrapper';
38
import DeviceDataCollapse from './devicedatacollapse';
39

40
const useStyles = makeStyles()(theme => ({
11✔
41
  buttonStyle: { textTransform: 'none', textAlign: 'left' },
42
  connectionIcon: { marginRight: theme.spacing() },
43
  content: { maxWidth: 1280 },
44
  title: { marginRight: theme.spacing(0.5) },
45
  troubleshootButton: { marginRight: theme.spacing(2) }
46
}));
47

48
export const PortForwardLink = () => (
11✔
49
  <MenderTooltip
7✔
50
    arrow
51
    title={
52
      <div style={{ whiteSpace: 'normal' }}>
53
        <h3>Port forwarding</h3>
54
        <p>Port forwarding allows you to troubleshoot or use services on or via the device, without opening any ports on the device itself.</p>
55
        <p>
56
          To enable port forwarding you will need to install and configure the necessary software on the device and your workstation. Follow the link to learn
57
          more.
58
        </p>
59
      </div>
60
    }
61
  >
62
    <DocsLink className="flexbox centered margin-left" path="add-ons/port-forward">
63
      Enable port forwarding
64
      <LaunchIcon className="margin-left-small" fontSize="small" />
65
    </DocsLink>
66
  </MenderTooltip>
67
);
68

69
export const DeviceConnectionNote = ({ children }) => {
11✔
70
  const { classes } = useStyles();
5✔
71
  return (
5✔
72
    <div className="flexbox muted">
73
      <InfoIcon className={classes.connectionIcon} fontSize="small" />
74
      <Typography className={classes.buttonStyle} variant="body1">
75
        {children}
76
      </Typography>
77
    </div>
78
  );
79
};
80

81
export const DeviceConnectionMissingNote = () => (
11✔
82
  <DeviceConnectionNote>
1✔
83
    The troubleshoot add-on does not seem to be enabled on this device.
84
    <br />
85
    Please <DocsLink path="add-ons/remote-terminal" title="see the documentation" /> for a description on how it works and how to enable it.
86
  </DeviceConnectionNote>
87
);
88

89
export const DeviceDisconnectedNote = ({ lastConnectionTs }) => (
11✔
90
  <DeviceConnectionNote>
3✔
91
    The troubleshoot add-on is not currently connected on this device, it was last connected on <Time value={lastConnectionTs} />.
92
    <br />
93
    Please <DocsLink path="add-ons/remote-terminal" title="see the documentation" /> for more information.
94
  </DeviceConnectionNote>
95
);
96

97
export const TroubleshootButton = ({ disabled, item, onClick }) => {
11✔
98
  const { classes } = useStyles();
×
99
  return (
×
100
    <Button className={classes.troubleshootButton} onClick={() => onClick(item.key)} disabled={disabled} startIcon={item.icon}>
×
101
      <Typography className={classes.buttonStyle} variant="subtitle2">
102
        {item.title}
103
      </Typography>
104
    </Button>
105
  );
106
};
107

108
const deviceAuditlogType = AUDIT_LOGS_TYPES.find(type => type.value === 'device');
33✔
109

110
const tabs = {
11✔
111
  terminal: {
112
    title: 'Remote terminal',
113
    value: 'terminal',
114
    canShow: ({ canTroubleshoot, canWriteDevices, groupsPermissions }, { group }) =>
115
      (canTroubleshoot && canWriteDevices) || checkPermissionsObject(groupsPermissions, uiPermissionsById.connect.value, group, ALL_DEVICES),
3!
116
    Component: TroubleshootContent
117
  },
118
  transfer: { title: 'File transfer', value: 'transfer', canShow: ({ canTroubleshoot }) => canTroubleshoot, Component: FileTransfer }
3✔
119
};
120

121
export const DeviceConnection = ({ className = '', device }) => {
11✔
122
  const [socketClosed, setSocketClosed] = useState();
6✔
123
  const [availableTabs, setAvailableTabs] = useState(Object.values(tabs));
6✔
124
  const [downloadPath, setDownloadPath] = useState('');
6✔
125
  const [file, setFile] = useState();
6✔
126
  const [socketInitialized, setSocketInitialized] = useState(undefined);
6✔
127
  const [uploadPath, setUploadPath] = useState('');
6✔
128
  const closeTimer = useRef();
6✔
129
  const initTimer = useRef();
6✔
130

131
  const userCapabilities = useSelector(getUserCapabilities);
6✔
132
  const { canAuditlog, canTroubleshoot } = userCapabilities;
6✔
133
  const { hasAuditlogs } = useSelector(getTenantCapabilities);
6✔
134
  const { classes } = useStyles();
6✔
135
  const { connect_status, connect_updated_ts, isOffline } = device;
6✔
136
  const [connectionStatus, setConnectionStatus] = useState(connect_status);
6✔
137

138
  const dispatch = useDispatch();
6✔
139
  const dispatchedSetSnackbar = useCallback((...args) => dispatch(setSnackbar(...args)), [dispatch]);
6✔
140

141
  useEffect(() => {
6✔
142
    if (!socketClosed) {
3!
143
      return;
3✔
144
    }
145
    clearTimeout(closeTimer.current);
×
146
    closeTimer.current = setTimeout(() => setSocketClosed(false), TIMEOUTS.fiveSeconds);
×
147
  }, [socketClosed]);
148

149
  useEffect(() => {
6✔
150
    setConnectionStatus(connect_status);
3✔
151
    clearTimeout(initTimer.current);
3✔
152
    if (connectionStatus) {
3✔
153
      return;
2✔
154
    }
155
    initTimer.current = setTimeout(() => {
1✔
156
      setConnectionStatus(!connect_status || isOffline ? DEVICE_CONNECT_STATES.unknown : connect_status);
×
157
    }, TIMEOUTS.fiveSeconds);
158
    return () => clearTimeout(initTimer.current);
1✔
159
  }, [connect_status, connectionStatus, device.id, isOffline]);
160

161
  useEffect(() => {
6✔
162
    const allowedTabs = Object.values(tabs).reduce((accu, tab) => {
3✔
163
      if (tab.canShow(userCapabilities, device) && connectionStatus === DEVICE_CONNECT_STATES.connected) {
6✔
164
        accu.push(tab);
2✔
165
      }
166
      return accu;
6✔
167
    }, []);
168
    setAvailableTabs(allowedTabs);
3✔
169
    // eslint-disable-next-line react-hooks/exhaustive-deps
170
  }, [connectionStatus, JSON.stringify(device), JSON.stringify(userCapabilities)]);
171

172
  const onDownloadClick = useCallback(
6✔
173
    path => {
174
      setDownloadPath(path);
×
175
      dispatch(setSnackbar('Downloading file'));
×
176
      dispatch(getDeviceFileDownloadLink(device.id, path)).then(address => {
×
177
        const filename = path.substring(path.lastIndexOf('/') + 1) || 'file';
×
178
        createDownload(address, filename);
×
179
      });
180
    },
181
    [dispatch, device.id]
182
  );
183

184
  return (
6✔
185
    <DeviceDataCollapse
186
      isAddOn
187
      title={
188
        <div className="flexbox center-aligned">
189
          <h4>Troubleshooting</h4>
190
          <div className={`flexbox ${className}`}>
191
            {connectionStatus !== DEVICE_CONNECT_STATES.unknown && canTroubleshoot && <PortForwardLink />}
18✔
192
            {canAuditlog && hasAuditlogs && (
12!
193
              <Link
194
                className="flexbox center-aligned margin-left"
195
                to={`/auditlog?${formatAuditlogs({ pageState: { type: deviceAuditlogType, detail: device.id, startDate: BEGINNING_OF_TIME } }, {})}`}
196
              >
197
                List all log entries for this device
198
              </Link>
199
            )}
200
          </div>
201
          <EnterpriseNotification className="margin-left-small" id={BENEFITS.deviceTroubleshoot.id} />
202
        </div>
203
      }
204
    >
205
      <div className={`flexbox column ${classes.content}`}>
206
        {!connectionStatus && (
8✔
207
          <div className="flexbox centered">
208
            <Loader show />
209
          </div>
210
        )}
211
        {connectionStatus === DEVICE_CONNECT_STATES.unknown && <DeviceConnectionMissingNote />}
6!
212
        {connectionStatus === DEVICE_CONNECT_STATES.disconnected && <DeviceDisconnectedNote lastConnectionTs={connect_updated_ts} />}
8✔
213
        {availableTabs.map(({ Component, title, value }) => (
214
          <div key={value}>
8✔
215
            <h4 className="margin-top-large">{title}</h4>
216
            <Component
217
              device={device}
218
              downloadPath={downloadPath}
219
              file={file}
220
              onDownload={onDownloadClick}
221
              setDownloadPath={setDownloadPath}
222
              setFile={setFile}
223
              setSnackbar={dispatchedSetSnackbar}
224
              setSocketClosed={setSocketClosed}
225
              setSocketInitialized={setSocketInitialized}
226
              setUploadPath={setUploadPath}
227
              socketInitialized={socketInitialized}
228
              uploadPath={uploadPath}
229
              userCapabilities={userCapabilities}
230
            />
231
          </div>
232
        ))}
233
      </div>
234
    </DeviceDataCollapse>
235
  );
236
};
237

238
export default DeviceConnection;
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