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

mendersoftware / gui / 1201121080

05 Mar 2024 08:10AM UTC coverage: 83.549% (-16.4%) from 99.964%
1201121080

Pull #4348

gitlab-ci

mineralsfree
fix: Download log button failing to download log file

Ticket: MEN-7029
Changelog: None

Signed-off-by: Mikita Pilinka <mikita.pilinka@northern.tech>
Pull Request #4348: MEN-7029: Download log button failing to download file

4431 of 6324 branches covered (70.07%)

10 of 11 new or added lines in 1 file covered. (90.91%)

1639 existing lines in 162 files now uncovered.

8405 of 10060 relevant lines covered (83.55%)

140.67 hits per line

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

86.27
/src/js/components/devices/device-details/deployments.js
1
// Copyright 2023 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
import { Link } from 'react-router-dom';
17

18
import { Button, Table, TableBody, TableCell, TableHead, TableRow, buttonClasses, tableCellClasses } from '@mui/material';
19
import { makeStyles } from 'tss-react/mui';
20

21
import Cookies from 'universal-cookie';
22

23
import { deploymentsApiUrl, getDeviceDeployments, resetDeviceDeployments } from '../../../actions/deploymentActions';
24
import { deploymentDisplayStates, deploymentStatesToSubstates } from '../../../constants/deploymentConstants';
25
import { DEVICE_LIST_DEFAULTS } from '../../../constants/deviceConstants';
26
import { getCurrentSession } from '../../../selectors/index.js';
27
import Confirm from '../../common/confirm';
28
import Pagination from '../../common/pagination';
29
import { MaybeTime } from '../../common/time';
30
import { HELPTOOLTIPS, MenderHelpTooltip } from '../../helptips/helptooltips';
31
import { DeviceStateSelection } from '../authorized-devices';
32

33
const cookies = new Cookies();
8✔
34
const useStyles = makeStyles()(theme => ({
8✔
35
  deletion: { justifyContent: 'flex-end' },
36
  table: {
37
    minHeight: '10vh',
38
    [`.deleted > .${tableCellClasses.root}, .deleted a`]: {
39
      background: theme.palette.background.lightgrey,
40
      color: theme.palette.grey[700],
41
      [`.${buttonClasses.root}`]: { color: theme.palette.text.primary }
42
    }
43
  }
44
}));
45

46
const EmptyState = ({ isFiltered }) => (
8✔
UNCOV
47
  <div className="flexbox column centered margin-large">
×
48
    <p className="align-center muted">
49
      No deployments were found.
50
      <br />
51
      {isFiltered && <>Try adjusting the filters</>}
×
52
    </p>
53
  </div>
54
);
55

56
const History = ({ className, items, page, perPage, setPage, setPerPage, total }) => {
8✔
57
  const { token } = useSelector(getCurrentSession);
2✔
58
  const columns = [
2✔
59
    {
60
      content: 'Release',
61
      key: 'release',
62
      Component: ({ deviceDeployment: { release } }) => <Link to={`/releases/${release}`}>{release}</Link>
2✔
63
    },
64
    {
65
      content: 'Target device(s)',
66
      key: 'target',
67
      Component: ({ deviceDeployment: { id, target, route } }) => <Link to={`/deployments/${route}?id=${id}&open=true`}>{target}</Link>
2✔
68
    },
69
    {
70
      content: 'Started',
71
      key: 'created',
72
      Component: ({ deviceDeployment: { created } }) => <MaybeTime value={created} />
2✔
73
    },
74
    {
75
      content: 'Finished',
76
      key: 'finished',
77
      Component: ({ deviceDeployment: { finished } }) => <MaybeTime value={finished} />
2✔
78
    },
79
    {
80
      content: 'Deployment progress',
81
      key: 'deploymentStatus',
82
      Component: ({ deviceDeployment: { deploymentStatus } }) => deploymentDisplayStates[deploymentStatus] ?? deploymentStatus
2!
83
    },
84
    { content: 'Device status', key: 'status', Component: ({ deviceDeployment: { status } }) => status },
2✔
85
    {
86
      content: '',
87
      key: 'log',
88
      Component: ({ deviceDeployment: { id, log, deviceId } }) =>
89
        log && (
2✔
90
          <Button
91
            component="a"
92
            href={`${window.location.origin}${deploymentsApiUrl}/deployments/${id}/devices/${deviceId}/log`}
93
            onClick={() =>
NEW
94
              cookies.set('JWT', token, {
×
95
                path: '/',
96
                secure: true,
97
                sameSite: 'strict',
98
                maxAge: 5
99
              })
100
            }
101
            target={'_blank'}
102
          >
103
            Log
104
          </Button>
105
        )
106
    }
107
  ];
108
  const onChangeRowsPerPage = perPage => {
2✔
UNCOV
109
    setPage(1);
×
UNCOV
110
    setPerPage(perPage);
×
111
  };
112
  const wasReset = items.reduce((accu, { deleted }) => {
2✔
113
    if (!accu) {
2!
114
      return !!deleted;
2✔
115
    }
UNCOV
116
    return accu;
×
117
  }, false);
118
  return (
2✔
119
    <div className={className}>
120
      <Table>
121
        <TableHead>
122
          <TableRow>
123
            {columns.map(({ content, key }) => (
124
              <TableCell key={key}>{content}</TableCell>
14✔
125
            ))}
126
          </TableRow>
127
        </TableHead>
128
        <TableBody>
129
          {items.map(item => (
130
            <TableRow className={item.deleted ? 'deleted' : ''} key={item.id}>
2!
131
              {columns.map(({ key, Component }) => (
132
                <TableCell key={`${item.id}-${key}`}>
14✔
133
                  <Component deviceDeployment={item} />
134
                </TableCell>
135
              ))}
136
            </TableRow>
137
          ))}
138
        </TableBody>
139
      </Table>
140
      <div className="flexbox space-between">
141
        <Pagination
142
          count={total}
143
          onChangePage={setPage}
144
          onChangeRowsPerPage={onChangeRowsPerPage}
145
          page={page}
146
          rowsPerPage={perPage}
147
          rowsPerPageOptions={[10, 20]}
148
        />
149
        {wasReset && <MenderHelpTooltip id={HELPTOOLTIPS.resetHistory.id} />}
2!
150
      </div>
151
    </div>
152
  );
153
};
154

155
const deploymentStates = {
8✔
156
  any: { key: 'any', title: () => 'any', values: [] },
2✔
157
  pending: { key: 'pending', title: () => 'pending', values: deploymentStatesToSubstates.pending },
2✔
158
  inprogress: { key: 'inprogress', title: () => 'in progress', values: deploymentStatesToSubstates.inprogress },
2✔
159
  paused: { key: 'paused', title: () => 'paused', values: deploymentStatesToSubstates.paused },
2✔
160
  failures: { key: 'failures', title: () => 'failures', values: deploymentStatesToSubstates.failures },
2✔
161
  successes: { key: 'successes', title: () => 'successes', values: deploymentStatesToSubstates.successes }
2✔
162
};
163

164
export const Deployments = ({ device }) => {
8✔
165
  const [filters, setFilters] = useState([deploymentStates.any.key]);
2✔
166
  const [page, setPage] = useState(DEVICE_LIST_DEFAULTS.page);
2✔
167
  const [perPage, setPerPage] = useState(10);
2✔
168
  const [isChecking, setIsChecking] = useState(false);
2✔
169
  const { classes } = useStyles();
2✔
170
  const dispatch = useDispatch();
2✔
171

172
  useEffect(() => {
2✔
173
    if (!device?.id) {
2!
UNCOV
174
      return;
×
175
    }
176
    const filterSelection = deploymentStates[filters[0]].values;
2✔
177
    dispatch(getDeviceDeployments(device.id, { filterSelection, page, perPage }));
2✔
178
  }, [device.id, dispatch, filters, page, perPage]);
179

180
  const onSelectStatus = status => setFilters([status]);
2✔
181

182
  const onResetStart = () => setIsChecking(true);
2✔
183

184
  const onResetConfirm = () => dispatch(resetDeviceDeployments(device.id)).then(() => setIsChecking(false));
2✔
185

186
  const { deviceDeployments = [], deploymentsCount } = device;
2!
187

188
  return (
2✔
189
    <div className="margin-bottom">
190
      <h4 className="margin-bottom-small">Deployments</h4>
191
      <div className="flexbox margin-bottom-small" style={{ alignSelf: 'flex-start' }}>
192
        <DeviceStateSelection onStateChange={onSelectStatus} selectedState={filters[0]} states={deploymentStates} />
193
      </div>
194

195
      {!deviceDeployments.length ? (
2!
196
        <EmptyState isFiltered={filters[0] !== deploymentStates.any.key} />
197
      ) : (
198
        <>
199
          <History
200
            className={classes.table}
201
            items={deviceDeployments}
202
            page={page}
203
            perPage={perPage}
204
            setPage={setPage}
205
            setPerPage={setPerPage}
206
            total={deploymentsCount}
207
          />
208
          <div className={`flexbox margin-top relative ${classes.deletion}`}>
209
            {isChecking && (
2!
210
              <Confirm
211
                classes="confirmation-overlay"
UNCOV
212
                cancel={() => setIsChecking(false)}
×
213
                action={onResetConfirm}
214
                message={`This will reset the stored device deployment history for this device. Are you sure?`}
215
                style={{ marginRight: 0 }}
216
              />
217
            )}
218
            <Button onClick={onResetStart} variant="contained">
219
              Reset device deployment history
220
            </Button>
221
          </div>
222
        </>
223
      )}
224
    </div>
225
  );
226
};
227

228
export default Deployments;
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