• 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

85.88
/src/js/components/deployments/deployment-report/phaseprogress.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

16
import { CheckCircle, ErrorRounded, Pause, PlayArrow, Warning as WarningIcon } from '@mui/icons-material';
17
import { Button } from '@mui/material';
18
import { makeStyles } from 'tss-react/mui';
19

20
import moment from 'moment';
21
import momentDurationFormatSetup from 'moment-duration-format';
22
import pluralize from 'pluralize';
23

24
import inprogressImage from '../../../../assets/img/pending_status.png';
25
import { deploymentDisplayStates, deploymentSubstates, installationSubstatesMap, pauseMap } from '../../../constants/deploymentConstants';
26
import { getDeploymentState, groupDeploymentStats, statCollector } from '../../../helpers';
27
import Confirm from '../../common/confirm';
28
import { ProgressChartComponent } from '../progressChart';
29

30
const useStyles = makeStyles()(theme => ({
17✔
31
  active: { borderLeftColor: theme.palette.text.primary },
32
  borderColor: { borderLeftStyle: 'solid', borderLeftWidth: 1, height: '100%', zIndex: 1 },
33
  continueButton: { marginRight: theme.spacing(2) },
34
  failureIcon: { fontSize: 16, marginRight: 10 },
35
  inactive: { borderLeftColor: theme.palette.grey[500] },
36
  phaseInfo: { marginBottom: theme.spacing() },
37
  phaseIndex: { margin: theme.spacing(0.5) }
38
}));
39

40
momentDurationFormatSetup(moment);
17✔
41

42
const shortCircuitIndicators = [deploymentSubstates.alreadyInstalled, deploymentSubstates.noartifact];
17✔
43

44
const substateIconMap = {
17✔
45
  finished: { state: 'finished', icon: <CheckCircle fontSize="small" /> },
46
  inprogress: { state: 'inprogress', icon: <img src={inprogressImage} /> },
47
  failed: { state: 'failed', icon: <ErrorRounded fontSize="small" /> },
48
  paused: { state: 'paused', icon: <Pause fontSize="small" /> },
49
  pendingPause: { state: 'pendingPause', icon: <Pause fontSize="small" color="disabled" /> }
50
};
51

52
const stepTotalWidth = 100 / Object.keys(installationSubstatesMap).length;
17✔
53

54
const PhaseDelimiter = ({ classes, compact, index, phase = {} }) => {
17!
55
  const { classes: localClasses } = useStyles();
6✔
56
  const { status, substate: phaseSubstate } = phase;
6✔
57
  const isActive = status === substateIconMap.inprogress.state;
6✔
58
  const substate = phaseSubstate.done;
6✔
59

60
  const offset = `${stepTotalWidth * (index + 1) - stepTotalWidth / 2}%`;
6✔
61
  return (
6✔
62
    <div className={`${classes.phaseDelimiter} ${compact ? 'compact' : ''}`} style={{ left: offset, width: `${stepTotalWidth}%` }}>
6✔
63
      <div className={`${localClasses.borderColor} ${isActive ? localClasses.active : localClasses.inactive}`} />
6!
64
      {substateIconMap[status] ? substateIconMap[status].icon : <div />}
6✔
65
      {compact ? <div /> : <div className="capitalized slightly-smaller">{substate}</div>}
6✔
66
    </div>
67
  );
68
};
69

70
const determineSubstateStatus = (successes, failures, totalDeviceCount, pauseIndicator, hasPauseDefined) => {
17✔
71
  let status;
72
  if (successes === totalDeviceCount) {
8✔
73
    status = substateIconMap.finished.state;
2✔
74
  } else if (failures === totalDeviceCount) {
6!
75
    status = substateIconMap.failed.state;
×
76
  } else if (pauseIndicator) {
6!
77
    status = substateIconMap.paused.state;
×
78
  } else if (successes || failures) {
6!
79
    status = substateIconMap.inprogress.state;
×
80
  } else if (hasPauseDefined) {
6!
81
    status = substateIconMap.pendingPause.state;
×
82
  }
83
  return status;
8✔
84
};
85

86
const getDisplayablePhases = ({ pauseMap, deployment, stepWidth, substatesMap, totalDeviceCount }) => {
17✔
87
  const { statistics = {}, update_control_map = {} } = deployment;
2!
88
  const { status: stats = {} } = statistics;
2!
89
  const currentPauseState = Object.keys(pauseMap)
2✔
90
    .reverse()
91
    .find(key => stats[key] > 0);
6✔
92
  return Object.values(substatesMap).reduce(
2✔
93
    (accu, substate, index) => {
94
      let successes = statCollector(substate.successIndicators, stats);
8✔
95
      let failures = statCollector(substate.failureIndicators, stats);
8✔
96
      if (
8!
97
        !currentPauseState ||
8!
98
        index <= Object.keys(pauseMap).indexOf(currentPauseState) ||
99
        (index && accu.displayablePhases[index - 1].failures + accu.displayablePhases[index - 1].success === totalDeviceCount)
100
      ) {
101
        failures = accu.displayablePhases[index - 1]?.failures || failures;
8✔
102
        successes = successes + accu.shortCutSuccesses;
8✔
103
      }
104
      successes = Math.min(totalDeviceCount, successes);
8✔
105
      failures = Math.min(totalDeviceCount - successes, failures);
8✔
106
      const successWidth = (successes / totalDeviceCount) * 100 || 0;
8✔
107
      const failureWidth = (failures / totalDeviceCount) * 100 || 0;
8✔
108
      const { states = {} } = update_control_map;
8✔
109
      const hasPauseDefined = states[substate.pauseConfigurationIndicator]?.action === 'pause';
8✔
110
      const status = determineSubstateStatus(successes, failures, totalDeviceCount, !!stats[substate.pauseIndicator], hasPauseDefined);
8✔
111
      accu.displayablePhases.push({ substate, successes, failures, offset: stepWidth * index, width: stepWidth, successWidth, failureWidth, status });
8✔
112
      return accu;
8✔
113
    },
114
    { displayablePhases: [], shortCutSuccesses: statCollector(shortCircuitIndicators, stats) }
115
  ).displayablePhases;
116
};
117

118
const statusMap = {
17✔
119
  inprogress: <PlayArrow fontSize="inherit" />,
120
  paused: <Pause fontSize="inherit" />
121
};
122

123
const Header = ({ device_count, failures, totalDeviceCount, showDetails, status }) => {
17✔
124
  const { classes } = useStyles();
2✔
125
  return showDetails ? (
2✔
126
    <>
127
      <div className={`flexbox space-between ${classes.phaseInfo}`}>
128
        {statusMap[status] ? statusMap[status] : <div />}
1!
129
        <div className="flexbox center-aligned">
130
          {!!failures && <WarningIcon className={classes.failureIcon} />}
1!
131
          {`${failures} ${pluralize('failure', failures)}`}
132
        </div>
133
      </div>
134
      <div className={`muted slightly-smaller ${classes.phaseIndex}`}>Phase 1 of 1</div>
135
    </>
136
  ) : (
137
    <>
138
      Phase 1: {Math.round((device_count / totalDeviceCount || 0) * 100)}% ({device_count} {pluralize('device', device_count)})
1!
139
    </>
140
  );
141
};
142

143
export const PhaseProgressDisplay = ({ className = '', deployment, showDetails = true, status, ...remainder }) => {
17✔
144
  const { failures } = groupDeploymentStats(deployment);
2✔
145

146
  const { device_count = 0, max_devices = 0 } = deployment;
2!
147
  const totalDeviceCount = Math.max(device_count, max_devices);
2✔
148

149
  const displayablePhases = getDisplayablePhases({ deployment, pauseMap, stepWidth: stepTotalWidth, substatesMap: installationSubstatesMap, totalDeviceCount });
2✔
150

151
  return (
2✔
152
    <ProgressChartComponent
153
      className={`flexbox column stepped-progress ${showDetails ? '' : 'no-background'} ${className}`}
2✔
154
      phases={displayablePhases}
155
      PhaseDelimiter={PhaseDelimiter}
156
      Header={Header}
157
      headerProps={{ device_count, failures, showDetails, status, totalDeviceCount }}
158
      {...remainder}
159
    />
160
  );
161
};
162

163
const confirmationStyle = {
17✔
164
  justifyContent: 'flex-start',
165
  paddingLeft: 100
166
};
167

168
const PhaseLabel = ({ classes, phase }) => <div className={`capitalized progress-step-number ${classes.progressStepNumber}`}>{phase.substate.title}</div>;
17✔
169

170
export const PhaseProgress = ({ className = '', deployment = {}, onAbort, onUpdateControlChange }) => {
17!
171
  const { classes } = useStyles();
1✔
172
  const [shouldContinue, setShouldContinue] = useState(false);
1✔
173
  const [shouldAbort, setShouldAbort] = useState(false);
1✔
174
  const [isLoading, setIsLoading] = useState(false);
1✔
175

176
  const { id, statistics = {}, update_control_map = {} } = deployment;
1!
177
  const { status: stats = {} } = statistics;
1!
178
  const { states = {} } = update_control_map;
1✔
179
  const { failures: totalFailureCount, paused: totalPausedCount } = groupDeploymentStats(deployment);
1✔
180

181
  const status = getDeploymentState(deployment);
1✔
182
  const currentPauseState = Object.keys(pauseMap)
1✔
183
    .reverse()
184
    .find(key => stats[key] > 0);
3✔
185

186
  useEffect(() => {
1✔
187
    if (!isLoading) {
1!
188
      return;
1✔
189
    }
190
    setIsLoading(false);
×
191
  }, [isLoading, status]);
192

193
  const onAbortClick = () => {
1✔
194
    setShouldAbort(false);
×
195
    onAbort(id);
×
196
  };
197

198
  const onContinueClick = () => {
1✔
199
    setIsLoading(true);
×
200
    setShouldContinue(false);
×
201
    onUpdateControlChange({ states: { [pauseMap[currentPauseState].followUp]: { action: 'continue' } } });
×
202
  };
203

204
  const isPaused = status === deploymentDisplayStates.paused;
1✔
205
  const canContinue = isPaused && states[pauseMap[currentPauseState].followUp];
1!
206
  const disableContinuationButtons = isLoading || (canContinue && states[pauseMap[currentPauseState].followUp].action !== 'pause');
1!
207
  return (
1✔
208
    <div className={`flexbox column ${className}`}>
209
      <PhaseProgressDisplay compact deployment={deployment} PhaseLabel={PhaseLabel} showDetails={false} status={status} />
210
      <div className="margin-top">
211
        Deployment is <span className="uppercased">{status}</span> with {totalFailureCount} {pluralize('failure', totalFailureCount)}
212
        {isPaused && !canContinue && ` - waiting for an action on the ${pluralize('device', totalPausedCount)} to continue`}
1!
213
      </div>
214
      {canContinue && (
1!
215
        <div className="margin-top margin-bottom relative">
216
          {shouldContinue && (
×
217
            <Confirm
218
              type="deploymentContinuation"
219
              classes="confirmation-overlay"
220
              action={onContinueClick}
221
              cancel={() => setShouldContinue(false)}
×
222
              style={confirmationStyle}
223
            />
224
          )}
225
          {shouldAbort && (
×
226
            <Confirm
227
              type="deploymentAbort"
228
              classes="confirmation-overlay"
229
              action={onAbortClick}
230
              cancel={() => setShouldAbort(false)}
×
231
              style={confirmationStyle}
232
            />
233
          )}
234
          <Button color="primary" disabled={disableContinuationButtons} onClick={setShouldContinue} variant="contained" className={classes.continueButton}>
235
            Continue
236
          </Button>
237
          <Button disabled={disableContinuationButtons} onClick={setShouldAbort}>
238
            Abort
239
          </Button>
240
        </div>
241
      )}
242
    </div>
243
  );
244
};
245

246
export default PhaseProgress;
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