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

mendersoftware / gui / 991356850

04 Sep 2023 11:31AM UTC coverage: 82.394% (-17.6%) from 99.964%
991356850

Pull #4017

gitlab-ci

web-flow
chore: Bump @stripe/stripe-js from 1.54.2 to 2.1.1

Bumps [@stripe/stripe-js](https://github.com/stripe/stripe-js) from 1.54.2 to 2.1.1.
- [Release notes](https://github.com/stripe/stripe-js/releases)
- [Commits](https://github.com/stripe/stripe-js/compare/v1.54.2...v2.1.1)

---
updated-dependencies:
- dependency-name: "@stripe/stripe-js"
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Pull Request #4017: chore: Bump @stripe/stripe-js from 1.54.2 to 2.1.1

4347 of 6321 branches covered (0.0%)

8260 of 10025 relevant lines covered (82.39%)

193.25 hits per line

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

91.38
/src/js/components/deployments/createdeployment.js
1
// Copyright 2019 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, useRef, useState } from 'react';
15
import { useDispatch, useSelector } from 'react-redux';
16
import { useNavigate } from 'react-router-dom';
17

18
import { Close as CloseIcon, ExpandMore } from '@mui/icons-material';
19
import {
20
  Accordion,
21
  AccordionDetails,
22
  AccordionSummary,
23
  Button,
24
  Checkbox,
25
  Divider,
26
  Drawer,
27
  FormControlLabel,
28
  FormGroup,
29
  IconButton,
30
  Typography,
31
  accordionClasses
32
} from '@mui/material';
33
import { makeStyles } from 'tss-react/mui';
34

35
import moment from 'moment';
36
import pluralize from 'pluralize';
37

38
import DeltaIcon from '../../../assets/img/deltaicon.svg';
39
import { createDeployment, getDeploymentsConfig } from '../../actions/deploymentActions';
40
import { getGroupDevices } from '../../actions/deviceActions';
41
import { advanceOnboarding } from '../../actions/onboardingActions';
42
import { getReleases } from '../../actions/releaseActions';
43
import { ALL_DEVICES } from '../../constants/deviceConstants';
44
import { onboardingSteps } from '../../constants/onboardingConstants';
45
import { toggle, validatePhases } from '../../helpers';
46
import {
47
  getAcceptedDevices,
48
  getDeviceCountsByStatus,
49
  getDevicesById,
50
  getDocsVersion,
51
  getGlobalSettings,
52
  getGroupNames,
53
  getGroupsByIdWithoutUngrouped,
54
  getIdAttribute,
55
  getIsEnterprise,
56
  getOnboardingState,
57
  getReleasesById,
58
  getTenantCapabilities
59
} from '../../selectors';
60
import { getOnboardingComponentFor } from '../../utils/onboardingmanager';
61
import Confirm from '../common/confirm';
62
import { RolloutPatternSelection } from './deployment-wizard/phasesettings';
63
import { ForceDeploy, Retries, RolloutOptions } from './deployment-wizard/rolloutoptions';
64
import { ScheduleRollout } from './deployment-wizard/schedulerollout';
65
import { Devices, ReleasesWarning, Software } from './deployment-wizard/softwaredevices';
66

67
const useStyles = makeStyles()(theme => ({
12✔
68
  accordion: {
69
    backgroundColor: theme.palette.grey[400],
70
    marginTop: theme.spacing(4),
71
    '&:before': {
72
      display: 'none'
73
    },
74
    [`&.${accordionClasses.expanded}`]: {
75
      margin: 'auto',
76
      marginTop: theme.spacing(4)
77
    }
78
  },
79
  columns: {
80
    columnGap: 30,
81
    display: 'grid',
82
    gridTemplateColumns: 'max-content max-content',
83
    '&>p': {
84
      marginTop: theme.spacing(3)
85
    }
86
  },
87
  disabled: { color: theme.palette.text.disabled }
88
}));
89

90
const getAnchor = (element, heightAdjustment = 3) => ({
609✔
91
  top: element.offsetTop + element.offsetHeight / heightAdjustment,
92
  left: element.offsetLeft + element.offsetWidth
93
});
94

95
export const getPhaseStartTime = (phases, index, startDate) => {
12✔
96
  if (index < 1) {
137✔
97
    return startDate?.toISOString ? startDate.toISOString() : startDate;
54✔
98
  }
99
  // since we don't want to get stale phase start times when the creation dialog is open for a long time
100
  // we have to ensure start times are based on delay from previous phases
101
  // since there likely won't be 1000s of phases this should still be fine to recalculate
102
  const newStartTime = phases.slice(0, index).reduce((accu, phase) => moment(accu).add(phase.delay, phase.delayUnit), startDate);
120✔
103
  return newStartTime.toISOString();
83✔
104
};
105

106
export const CreateDeployment = props => {
12✔
107
  const { deploymentObject = {}, onDismiss, onScheduleSubmit, open = true, setDeploymentSettings } = props;
375!
108

109
  const { canRetry, canSchedule, hasFullFiltering } = useSelector(getTenantCapabilities);
375✔
110
  const { createdGroup, groups, hasDynamicGroups } = useSelector(state => {
375✔
111
    const groups = getGroupsByIdWithoutUngrouped(state);
683✔
112
    const createdGroup = Object.keys(groups).length ? Object.keys(groups)[0] : undefined;
683!
113
    const hasDynamicGroups = Object.values(groups).some(group => !!group.id);
1,206✔
114
    return { createdGroup, hasDynamicGroups, groups };
683✔
115
  });
116
  const { hasDelta: hasDeltaEnabled } = useSelector(state => state.deployments.config) ?? {};
683!
117
  const { total: acceptedDeviceCount } = useSelector(getAcceptedDevices);
375✔
118
  const hasDevices = !!acceptedDeviceCount;
375✔
119
  const devicesById = useSelector(getDevicesById);
375✔
120
  const docsVersion = useSelector(getDocsVersion);
375✔
121
  const { pending: hasPending } = useSelector(getDeviceCountsByStatus);
375✔
122
  const { attribute: idAttribute } = useSelector(getIdAttribute);
375✔
123
  const isEnterprise = useSelector(getIsEnterprise);
375✔
124
  const { needsDeploymentConfirmation: needsCheck, previousPhases = [], retries: previousRetries = 0 } = useSelector(getGlobalSettings);
375✔
125
  const onboardingState = useSelector(getOnboardingState) || {};
375!
126
  const { complete: isOnboardingComplete } = onboardingState;
375✔
127
  const releases = useSelector(state => state.releases.releasesList.searchedIds);
683✔
128
  const releasesById = useSelector(getReleasesById);
375✔
129
  const groupNames = useSelector(getGroupNames);
375✔
130
  const dispatch = useDispatch();
375✔
131

132
  const isCreating = useRef(false);
375✔
133
  const [hasNewRetryDefault, setHasNewRetryDefault] = useState(false);
375✔
134
  const [isChecking, setIsChecking] = useState(false);
375✔
135
  const [isExpanded, setIsExpanded] = useState(false);
375✔
136
  const navigate = useNavigate();
375✔
137
  const releaseRef = useRef();
375✔
138
  const groupRef = useRef();
375✔
139
  const deploymentAnchor = useRef();
375✔
140
  const { classes } = useStyles();
375✔
141

142
  useEffect(() => {
375✔
143
    dispatch(getReleases({ page: 1, perPage: 100, searchOnly: true }));
6✔
144
    dispatch(getDeploymentsConfig());
6✔
145
  }, [dispatch]);
146

147
  useEffect(() => {
375✔
148
    const { devices = [], group, release } = deploymentObject;
21✔
149
    if (release) {
21✔
150
      dispatch(advanceOnboarding(onboardingSteps.SCHEDULING_ARTIFACT_SELECTION));
5✔
151
    }
152
    dispatch(advanceOnboarding(onboardingSteps.SCHEDULING_GROUP_SELECTION));
21✔
153
    let nextDeploymentObject = { deploymentDeviceCount: devices.length ? devices.length : 0 };
21!
154
    if (group === ALL_DEVICES) {
21✔
155
      dispatch(advanceOnboarding(onboardingSteps.SCHEDULING_ALL_DEVICES_SELECTION));
3✔
156
      nextDeploymentObject.deploymentDeviceCount = acceptedDeviceCount;
3✔
157
    }
158
    if (groups[group]) {
21!
159
      return dispatch(getGroupDevices(group, { perPage: 1 })).then(({ group: { total: deploymentDeviceCount } }) =>
×
160
        setDeploymentSettings({ deploymentDeviceCount })
×
161
      );
162
    }
163
    setDeploymentSettings(nextDeploymentObject);
21✔
164
    // eslint-disable-next-line react-hooks/exhaustive-deps
165
  }, [acceptedDeviceCount, deploymentObject.group, deploymentObject.release?.Name, dispatch, JSON.stringify(groups), setDeploymentSettings]);
166

167
  useEffect(() => {
375✔
168
    let { deploymentDeviceCount: deviceCount, deploymentDeviceIds: deviceIds = [], devices = [] } = deploymentObject;
53✔
169
    if (devices.length) {
53!
170
      deviceIds = devices.map(({ id }) => id);
×
171
      deviceCount = deviceIds.length;
×
172
      devices = devices.map(({ id }) => ({ id, ...(devicesById[id] ?? {}) }));
×
173
    } else if (deploymentObject.group === ALL_DEVICES) {
53✔
174
      deviceCount = acceptedDeviceCount;
17✔
175
    }
176
    setDeploymentSettings({ deploymentDeviceIds: deviceIds, deploymentDeviceCount: deviceCount, devices });
53✔
177
    // eslint-disable-next-line react-hooks/exhaustive-deps
178
  }, [acceptedDeviceCount, JSON.stringify(deploymentObject), JSON.stringify(devicesById), setDeploymentSettings]);
179

180
  const cleanUpDeploymentsStatus = () => {
375✔
181
    if (!window.location.search) {
3!
182
      return;
3✔
183
    }
184
    const location = window.location.pathname.slice('/ui'.length);
×
185
    navigate(location); // lgtm [js/client-side-unvalidated-url-redirection]
×
186
  };
187

188
  const onSaveRetriesSetting = hasNewRetryDefault => setHasNewRetryDefault(hasNewRetryDefault);
375✔
189

190
  const closeWizard = () => {
375✔
191
    cleanUpDeploymentsStatus();
1✔
192
    onDismiss();
1✔
193
  };
194

195
  const onDeltaToggle = ({ target: { checked } }) => setDeploymentSettings({ delta: checked });
375✔
196

197
  const onScheduleSubmitClick = settings => {
375✔
198
    if (needsCheck && !isChecking) {
2!
199
      return setIsChecking(true);
×
200
    }
201
    isCreating.current = true;
2✔
202
    const { delta, deploymentDeviceIds, devices, filterId, forceDeploy = false, group, phases, release, retries, update_control_map } = settings;
2!
203
    const startTime = phases?.length ? phases[0].start_ts : undefined;
2✔
204
    const retrySetting = canRetry && retries ? { retries } : {};
2✔
205
    const newDeployment = {
2✔
206
      artifact_name: release.Name,
207
      autogenerate_delta: delta,
208
      devices: (filterId || group) && !devices.length ? undefined : deploymentDeviceIds,
8!
209
      filter_id: filterId,
210
      all_devices: !filterId && group === ALL_DEVICES,
4✔
211
      group: group === ALL_DEVICES || devices.length ? undefined : group,
4!
212
      name: devices[0]?.id || (group ? decodeURIComponent(group) : ALL_DEVICES),
6!
213
      phases: phases
2✔
214
        ? phases.map((phase, i, origPhases) => {
215
            phase.start_ts = getPhaseStartTime(origPhases, i, startTime);
3✔
216
            return phase;
3✔
217
          })
218
        : phases,
219
      ...retrySetting,
220
      force_installation: forceDeploy,
221
      update_control_map
222
    };
223
    if (!isOnboardingComplete) {
2!
224
      dispatch(advanceOnboarding(onboardingSteps.SCHEDULING_RELEASE_TO_DEVICES));
2✔
225
    }
226
    return dispatch(createDeployment(newDeployment, hasNewRetryDefault))
2✔
227
      .then(() => {
228
        // successfully retrieved new deployment
229
        cleanUpDeploymentsStatus();
2✔
230
        onScheduleSubmit();
2✔
231
      })
232
      .finally(() => {
233
        isCreating.current = false;
2✔
234
        setIsChecking(false);
2✔
235
      });
236
  };
237

238
  const { delta, deploymentDeviceCount, group, phases } = deploymentObject;
375✔
239

240
  const deploymentSettings = {
375✔
241
    ...deploymentObject,
242
    filterId: groups[group] ? groups[group].id : undefined
375!
243
  };
244
  const disabled =
245
    isCreating.current ||
375✔
246
    !(deploymentSettings.release && (deploymentSettings.deploymentDeviceCount || deploymentSettings.filterId || deploymentSettings.group)) ||
430✔
247
    !validatePhases(phases, deploymentSettings.deploymentDeviceCount, deploymentSettings.filterId);
248

249
  const sharedProps = {
375✔
250
    ...props,
251
    canRetry,
252
    canSchedule,
253
    docsVersion,
254
    groupNames,
255
    groupRef,
256
    groups,
257
    hasDevices,
258
    hasDynamicGroups,
259
    hasFullFiltering,
260
    hasPending,
261
    idAttribute,
262
    isEnterprise,
263
    previousPhases,
264
    previousRetries,
265
    releaseRef,
266
    releases,
267
    releasesById,
268
    commonClasses: classes,
269
    deploymentObject: deploymentSettings,
270
    hasNewRetryDefault,
271
    onSaveRetriesSetting,
272
    open: false,
273
    setDeploymentSettings
274
  };
275
  const hasReleases = !!Object.keys(releasesById).length;
375✔
276
  return (
375✔
277
    <Drawer anchor="right" open={open} onClose={closeWizard} PaperProps={{ style: { minWidth: '50vw' } }}>
278
      <div className="flexbox space-between center-aligned">
279
        <h3>Create a deployment</h3>
280
        <IconButton onClick={closeWizard} aria-label="close" size="large">
281
          <CloseIcon />
282
        </IconButton>
283
      </div>
284
      <Divider className="margin-bottom" />
285
      <FormGroup>
286
        {!hasReleases ? (
375!
287
          <ReleasesWarning />
288
        ) : (
289
          <>
290
            <Devices {...sharedProps} groupRef={groupRef} />
291
            <Software {...sharedProps} releaseRef={releaseRef} />
292
          </>
293
        )}
294
        <ScheduleRollout {...sharedProps} />
295
        <Accordion className={classes.accordion} square expanded={isExpanded} onChange={() => setIsExpanded(toggle)}>
1✔
296
          <AccordionSummary expandIcon={<ExpandMore />}>
297
            <Typography className={classes.disabled} variant="subtitle2">
298
              {isExpanded ? 'Hide' : 'Show'} advanced options
375✔
299
            </Typography>
300
          </AccordionSummary>
301
          <AccordionDetails>
302
            <RolloutPatternSelection {...sharedProps} />
303
            <RolloutOptions {...sharedProps} />
304
            <Retries {...sharedProps} />
305
            <ForceDeploy {...sharedProps} />
306
            {hasDeltaEnabled && (
622✔
307
              <FormControlLabel
308
                control={<Checkbox color="primary" checked={delta} onChange={onDeltaToggle} size="small" />}
309
                label={
310
                  <>
311
                    Generate and deploy Delta Artifacts (where available) <DeltaIcon />
312
                  </>
313
                }
314
              />
315
            )}
316
          </AccordionDetails>
317
        </Accordion>
318
      </FormGroup>
319
      <div className="margin-top">
320
        {isChecking && (
375!
321
          <Confirm
322
            classes="confirmation-overlay"
323
            cancel={() => setIsChecking(false)}
×
324
            action={() => onScheduleSubmitClick(deploymentSettings)}
×
325
            message={`This will deploy ${deploymentSettings.release?.Name} to ${deploymentDeviceCount} ${pluralize(
326
              'device',
327
              deploymentDeviceCount
328
            )}. Are you sure?`}
329
            style={{ paddingLeft: 12, justifyContent: 'flex-start', maxHeight: 44 }}
330
          />
331
        )}
332
        <Button onClick={closeWizard} style={{ marginRight: 10 }}>
333
          Cancel
334
        </Button>
335
        <Button variant="contained" color="primary" ref={deploymentAnchor} disabled={disabled} onClick={() => onScheduleSubmitClick(deploymentSettings)}>
2✔
336
          Create deployment
337
        </Button>
338
      </div>
339
      <OnboardingComponent
340
        releaseRef={releaseRef}
341
        groupRef={groupRef}
342
        deploymentObject={deploymentObject}
343
        deploymentAnchor={deploymentAnchor}
344
        onboardingState={onboardingState}
345
        createdGroup={createdGroup}
346
        releasesById={releasesById}
347
        releases={releases}
348
        hasDevices={hasDevices}
349
      />
350
    </Drawer>
351
  );
352
};
353

354
export default CreateDeployment;
355

356
const OnboardingComponent = ({
12✔
357
  releaseRef,
358
  groupRef,
359
  deploymentAnchor,
360
  deploymentObject,
361
  onboardingState,
362
  createdGroup,
363
  releasesById,
364
  releases,
365
  hasDevices
366
}) => {
367
  const { deploymentDeviceCount, devices, group, release: deploymentRelease = null } = deploymentObject;
278✔
368

369
  let onboardingComponent = null;
278✔
370
  if (releaseRef.current && groupRef.current && deploymentAnchor.current) {
278✔
371
    const anchor = getAnchor(releaseRef.current);
274✔
372
    const groupAnchor = getAnchor(groupRef.current);
274✔
373
    onboardingComponent = getOnboardingComponentFor(onboardingSteps.SCHEDULING_ALL_DEVICES_SELECTION, onboardingState, { anchor: groupAnchor, place: 'right' });
274✔
374
    if (createdGroup) {
274!
375
      onboardingComponent = getOnboardingComponentFor(
274✔
376
        onboardingSteps.SCHEDULING_GROUP_SELECTION,
377
        { ...onboardingState, createdGroup },
378
        { anchor: groupAnchor, place: 'right' },
379
        onboardingComponent
380
      );
381
    }
382
    if (deploymentDeviceCount && !deploymentRelease) {
274✔
383
      onboardingComponent = getOnboardingComponentFor(
2✔
384
        onboardingSteps.SCHEDULING_ARTIFACT_SELECTION,
385
        { ...onboardingState, selectedRelease: releasesById[releases[0]] || {} },
2!
386
        { anchor, place: 'right' },
387
        onboardingComponent
388
      );
389
    }
390
    if (hasDevices && (deploymentDeviceCount || devices?.length) && deploymentRelease) {
274✔
391
      const buttonAnchor = getAnchor(deploymentAnchor.current, 2);
61✔
392
      onboardingComponent = getOnboardingComponentFor(
61✔
393
        onboardingSteps.SCHEDULING_RELEASE_TO_DEVICES,
394
        { ...onboardingState, selectedDevice: devices.length ? devices[0] : undefined, selectedGroup: group, selectedRelease: deploymentRelease },
61!
395
        { anchor: buttonAnchor, place: 'right' },
396
        onboardingComponent
397
      );
398
    }
399
  }
400
  return onboardingComponent;
278✔
401
};
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