• 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

67.44
/src/js/components/devices/troubleshoot/filetransfer.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, useState } from 'react';
15
import { useDispatch } from 'react-redux';
16

17
import { FileCopy as CopyPasteIcon } from '@mui/icons-material';
18
import { Button, Divider, IconButton, InputAdornment, Tab, Tabs, TextField, Tooltip } from '@mui/material';
19
import { makeStyles } from 'tss-react/mui';
20

21
import { deviceFileUpload } from '../../../actions/deviceActions';
22
import { canAccess } from '../../../constants/appConstants';
23
import FileUpload from '../../common/forms/fileupload';
24

25
const tabs = [
12✔
26
  { key: 'upload', canAccess: ({ userCapabilities: { canTroubleshoot, canWriteDevices } }) => canTroubleshoot && canWriteDevices },
4✔
27
  { key: 'download', canAccess }
28
];
29

30
const useStyles = makeStyles()(theme => ({
12✔
31
  column: { maxWidth: theme.spacing(80) },
32
  inputWrapper: { alignItems: 'end', gap: theme.spacing(2), display: 'grid', gridTemplateColumns: `${theme.spacing(60)} min-content` }
33
}));
34

35
const CopyPasteButton = ({ onClick }) => (
12✔
36
  <InputAdornment position="end">
6✔
37
    <Tooltip title="Paste" placement="top">
38
      <IconButton onClick={onClick}>
39
        <CopyPasteIcon />
40
      </IconButton>
41
    </Tooltip>
42
  </InputAdornment>
43
);
44

45
export const FileTransfer = ({
12✔
46
  device: { id: deviceId },
47
  downloadPath,
48
  file,
49
  onDownload,
50
  setFile,
51
  setDownloadPath,
52
  setUploadPath,
53
  uploadPath,
54
  userCapabilities
55
}) => {
56
  const { classes } = useStyles();
6✔
57
  const [currentTab, setCurrentTab] = useState(tabs[0].key);
6✔
58
  const [isValidDestination, setIsValidDestination] = useState(true);
6✔
59
  const [availableTabs, setAvailableTabs] = useState(tabs);
6✔
60
  const dispatch = useDispatch();
6✔
61

62
  useEffect(() => {
6✔
63
    let destination = currentTab === 'download' ? downloadPath : uploadPath;
4!
64
    const isValid = destination.length ? /^(?:\/|[a-z]+:\/\/)/.test(destination) : true;
4!
65
    setIsValidDestination(isValid);
4✔
66
  }, [currentTab, downloadPath, uploadPath]);
67

68
  useEffect(() => {
6✔
69
    const availableTabs = tabs.reduce((accu, item) => {
4✔
70
      if (item.canAccess({ userCapabilities })) {
8!
71
        accu.push(item);
8✔
72
      }
73
      return accu;
8✔
74
    }, []);
75
    setAvailableTabs(availableTabs);
4✔
76
    setCurrentTab(availableTabs[0].key);
4✔
77
    // eslint-disable-next-line react-hooks/exhaustive-deps
78
  }, [JSON.stringify(userCapabilities)]);
79

80
  const onPasteDownloadClick = async () => {
6✔
81
    const path = await navigator.clipboard.readText();
×
82
    setDownloadPath(path);
×
83
  };
84

85
  const onPasteUploadClick = async () => {
6✔
86
    const path = await navigator.clipboard.readText();
×
87
    setUploadPath(path);
×
88
  };
89

90
  const onFileSelect = selectedFile => {
6✔
91
    let path;
92
    if (selectedFile) {
×
93
      path = `${uploadPath}/${selectedFile.name}`;
×
94
    } else {
95
      path = file && uploadPath.includes(file.name) ? uploadPath.substring(0, uploadPath.lastIndexOf('/')) : uploadPath;
×
96
    }
97
    setUploadPath(path);
×
98
    setFile(selectedFile);
×
99
  };
100

101
  const onUploadClick = useCallback(() => dispatch(deviceFileUpload(deviceId, uploadPath, file)), [dispatch, deviceId, uploadPath, file]);
6✔
102

103
  const fileInputProps = {
6✔
104
    error: !isValidDestination,
105
    fullWidth: true,
106
    InputLabelProps: { shrink: true }
107
  };
108

109
  return (
6✔
110
    <div className={classes.column}>
111
      <Tabs onChange={(e, item) => setCurrentTab(item)} value={currentTab} visibleScrollbar>
×
112
        {availableTabs.map(({ key }) => (
113
          <Tab className="capitalized" key={key} label={key} value={key} />
12✔
114
        ))}
115
      </Tabs>
116
      <Divider />
117
      {currentTab === 'upload' ? (
6!
118
        <div className={classes.column}>
119
          <div>
120
            <p className="margin-top">
121
              <b>Upload a file to the device</b>
122
            </p>
123
            <FileUpload
124
              enableContentReading={false}
125
              fileNameSelection={file?.name}
126
              onFileChange={() => undefined}
×
127
              onFileSelect={onFileSelect}
128
              placeholder={
129
                <>
130
                  Drag here or <a>browse</a> to upload a file
131
                </>
132
              }
133
            />
134
          </div>
135
          <p className="margin-top margin-bottom-none">
136
            <b>Destination directory on the device where the file will be transferred</b>
137
          </p>
138
          <div className={classes.inputWrapper}>
139
            <TextField
140
              {...fileInputProps}
141
              helperText={!isValidDestination && <div className="warning">Destination has to be an absolute path</div>}
6!
142
              onChange={e => setUploadPath(e.target.value)}
×
143
              placeholder="Example: /opt/installed-by-single-file"
144
              value={uploadPath}
145
              InputProps={{ endAdornment: <CopyPasteButton onClick={onPasteUploadClick} /> }}
146
            />
147
            <Button variant="contained" color="primary" disabled={!(file && uploadPath && isValidDestination)} onClick={onUploadClick}>
6!
148
              Upload
149
            </Button>
150
          </div>
151
        </div>
152
      ) : (
153
        <div>
154
          <p className="margin-top margin-bottom-none">
155
            <b>Path to the file on the device</b>
156
          </p>
157
          <div className={classes.inputWrapper}>
158
            <TextField
159
              {...fileInputProps}
160
              helperText={!isValidDestination && <div className="warning">Destination has to be an absolute path</div>}
×
161
              onChange={e => setDownloadPath(e.target.value)}
×
162
              placeholder="Example: /home/mender/"
163
              value={downloadPath}
164
              InputProps={{ endAdornment: <CopyPasteButton onClick={onPasteDownloadClick} /> }}
165
            />
166
            <Button
167
              variant="contained"
168
              color="primary"
169
              disabled={!(downloadPath && isValidDestination)}
×
170
              onClick={() => onDownload(downloadPath)}
×
171
              style={{ alignSelf: 'flex-end' }}
172
            >
173
              Download
174
            </Button>
175
          </div>
176
        </div>
177
      )}
178
    </div>
179
  );
180
};
181

182
export default FileTransfer;
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