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

mendersoftware / gui / 901187442

pending completion
901187442

Pull #3795

gitlab-ci

mzedel
feat: increased chances of adopting our intended navigation patterns instead of unsupported browser navigation

Ticket: None
Changelog: None
Signed-off-by: Manuel Zedel <manuel.zedel@northern.tech>
Pull Request #3795: feat: increased chances of adopting our intended navigation patterns instead of unsupported browser navigation

4389 of 6365 branches covered (68.96%)

5 of 5 new or added lines in 1 file covered. (100.0%)

1729 existing lines in 165 files now uncovered.

8274 of 10019 relevant lines covered (82.58%)

144.86 hits per line

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

91.38
/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, { useEffect, useState } from 'react';
15
import { Link } from 'react-router-dom';
16

17
import { ImportExport as ImportExportIcon, InfoOutlined as InfoIcon, Launch as LaunchIcon } from '@mui/icons-material';
18
import { Button, Typography } from '@mui/material';
19
import { useTheme } from '@mui/material/styles';
20

21
import { mdiConsole as ConsoleIcon } from '@mdi/js';
22

23
import { BEGINNING_OF_TIME } from '../../../constants/appConstants';
24
import { ALL_DEVICES, DEVICE_CONNECT_STATES } from '../../../constants/deviceConstants';
25
import { AUDIT_LOGS_TYPES } from '../../../constants/organizationConstants';
26
import { checkPermissionsObject, uiPermissionsById } from '../../../constants/userConstants';
27
import { formatAuditlogs } from '../../../utils/locationutils';
28
import MaterialDesignIcon from '../../common/materialdesignicon';
29
import MenderTooltip from '../../common/mendertooltip';
30
import Time from '../../common/time';
31
import Troubleshootdialog from '../dialogs/troubleshootdialog';
32
import DeviceDataCollapse from './devicedatacollapse';
33

34
const buttonStyle = { textTransform: 'none', textAlign: 'left' };
11✔
35
export const PortForwardLink = ({ docsVersion }) => (
11✔
36
  <MenderTooltip
3✔
37
    arrow
38
    title={
39
      <div style={{ whiteSpace: 'normal' }}>
40
        <h3>Port forwarding</h3>
41
        <p>Port forwarding allows you to troubleshoot or use services on or via the device, without opening any ports on the device itself.</p>
42
        <p>
43
          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
44
          more.
45
        </p>
46
      </div>
47
    }
48
  >
49
    <a href={`https://docs.mender.io/${docsVersion}add-ons/port-forward`} className="flexbox centered margin-left" target="_blank" rel="noopener noreferrer">
50
      Enable port forwarding
51
      <LaunchIcon className="margin-left-small" fontSize="small" />
52
    </a>
53
  </MenderTooltip>
54
);
55

56
export const DeviceConnectionNote = ({ children, style = buttonStyle }) => {
11✔
57
  const theme = useTheme();
7✔
58
  return (
7✔
59
    <div className="flexbox muted">
60
      <InfoIcon fontSize="small" style={{ marginRight: theme.spacing() }} />
61
      <Typography variant="body1" style={style}>
62
        {children}
63
      </Typography>
64
    </div>
65
  );
66
};
67

68
export const DeviceConnectionMissingNote = ({ docsVersion }) => (
11✔
69
  <DeviceConnectionNote>
3✔
70
    The troubleshoot add-on does not seem to be enabled on this device.
71
    <br />
72
    Please{' '}
73
    <a target="_blank" rel="noopener noreferrer" href={`https://docs.mender.io/${docsVersion}add-ons/remote-terminal`}>
74
      see the documentation
75
    </a>{' '}
76
    for a description on how it works and how to enable it.
77
  </DeviceConnectionNote>
78
);
79

80
export const DeviceDisconnectedNote = ({ docsVersion, lastConnectionTs }) => (
11✔
81
  <DeviceConnectionNote>
3✔
82
    The troubleshoot add-on is not currently connected on this device, it was last connected on <Time value={lastConnectionTs} />.
83
    <br />
84
    Please{' '}
85
    <a target="_blank" rel="noopener noreferrer" href={`https://docs.mender.io/${docsVersion}add-ons/remote-terminal`}>
86
      see the documentation
87
    </a>{' '}
88
    for more information.
89
  </DeviceConnectionNote>
90
);
91

92
export const TroubleshootButton = ({ disabled, item, onClick }) => {
11✔
93
  const theme = useTheme();
4✔
94
  return (
4✔
UNCOV
95
    <Button onClick={() => onClick(item.key)} disabled={disabled} startIcon={item.icon} style={{ marginRight: theme.spacing(2) }}>
×
96
      <Typography variant="subtitle2" style={buttonStyle}>
97
        {item.title}
98
      </Typography>
99
    </Button>
100
  );
101
};
102

103
const troubleshootingTools = [
11✔
104
  {
105
    key: 'terminal',
106
    title: 'Launch a new Remote Terminal session',
107
    icon: <MaterialDesignIcon path={ConsoleIcon} />,
108
    needsWriteAccess: true,
109
    needsTroubleshoot: true
110
  },
111
  { key: 'transfer', title: 'Launch File Transfer', icon: <ImportExportIcon />, needsWriteAccess: false, needsTroubleshoot: false },
112
  { key: 'portForward', component: PortForwardLink, needsWriteAccess: false, needsTroubleshoot: true }
113
];
114

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

117
export const DeviceConnection = ({ className = '', device, docsVersion = '', hasAuditlogs, socketClosed, startTroubleshoot, userCapabilities }) => {
11✔
118
  const [availableTabs, setAvailableTabs] = useState(troubleshootingTools);
6✔
119

120
  const { canAuditlog, canTroubleshoot, canWriteDevices: hasWriteAccess, groupsPermissions } = userCapabilities;
6✔
121

122
  useEffect(() => {
6✔
123
    const allowedTabs = troubleshootingTools.reduce((accu, tab) => {
3✔
124
      if (
9!
125
        (tab.needsWriteAccess && (!hasWriteAccess || !checkPermissionsObject(groupsPermissions, uiPermissionsById.connect.value, device.group, ALL_DEVICES))) ||
30✔
126
        (tab.needsTroubleshoot && !canTroubleshoot)
127
      ) {
UNCOV
128
        return accu;
×
129
      }
130
      accu.push(tab);
9✔
131
      return accu;
9✔
132
    }, []);
133
    setAvailableTabs(allowedTabs);
3✔
134
  }, [hasWriteAccess, canTroubleshoot]);
135

136
  const { connect_status = DEVICE_CONNECT_STATES.unknown, connect_updated_ts } = device;
6✔
137
  return (
6✔
138
    <DeviceDataCollapse
139
      header={
140
        <div className={`flexbox ${className}`}>
141
          {connect_status === DEVICE_CONNECT_STATES.unknown && <DeviceConnectionMissingNote docsVersion={docsVersion} />}
8✔
142
          {connect_status === DEVICE_CONNECT_STATES.disconnected && <DeviceDisconnectedNote docsVersion={docsVersion} lastConnectionTs={connect_updated_ts} />}
8✔
143
          {connect_status === DEVICE_CONNECT_STATES.connected &&
8✔
144
            availableTabs.map(item => {
145
              let Component = TroubleshootButton;
6✔
146
              if (item.component) {
6✔
147
                Component = item.component;
2✔
148
              }
149
              return <Component key={item.key} docsVersion={docsVersion} onClick={startTroubleshoot} disabled={!socketClosed} item={item} />;
6✔
150
            })}
151
          {canAuditlog && hasAuditlogs && connect_status !== DEVICE_CONNECT_STATES.unknown && (
16✔
152
            <Link
153
              className="flexbox center-aligned margin-left"
154
              to={`/auditlog?${formatAuditlogs({ pageState: { type: deviceAuditlogType, detail: device.id, startDate: BEGINNING_OF_TIME } }, {})}`}
155
            >
156
              List all log entries for this device
157
            </Link>
158
          )}
159
        </div>
160
      }
161
      isAddOn
162
      title="Troubleshoot"
163
    ></DeviceDataCollapse>
164
  );
165
};
166

167
export default DeviceConnection;
168

169
export const TroubleshootTab = ({
11✔
170
  classes,
171
  device,
172
  docsVersion,
173
  tenantCapabilities: { hasAuditlogs },
174
  socketClosed,
175
  launchTroubleshoot,
176
  userCapabilities,
177
  troubleshootType,
178
  setTroubleshootType,
179
  setSocketClosed
180
}) => (
UNCOV
181
  <>
×
182
    <DeviceConnection
183
      className={classes.deviceConnection}
184
      device={device}
185
      docsVersion={docsVersion}
186
      hasAuditlogs={hasAuditlogs}
187
      socketClosed={socketClosed}
188
      startTroubleshoot={launchTroubleshoot}
189
      userCapabilities={userCapabilities}
190
    />
191
    <Troubleshootdialog
192
      device={device}
193
      open={Boolean(troubleshootType)}
UNCOV
194
      onCancel={() => setTroubleshootType()}
×
195
      setSocketClosed={setSocketClosed}
196
      type={troubleshootType}
197
    />
198
  </>
199
);
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