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

mendersoftware / gui / 1032268278

10 Oct 2023 04:28PM UTC coverage: 82.723% (-17.2%) from 99.964%
1032268278

Pull #4096

gitlab-ci

mzedel
chore: made offline settings saving less aggressive

Signed-off-by: Manuel Zedel <manuel.zedel@northern.tech>
Pull Request #4096: MEN-6781

4340 of 6277 branches covered (0.0%)

1 of 2 new or added lines in 1 file covered. (50.0%)

1719 existing lines in 164 files now uncovered.

8307 of 10042 relevant lines covered (82.72%)

201.86 hits per line

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

82.19
/src/js/components/deployments/pastdeployments.js
1
// Copyright 2015 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, useRef, useState } from 'react';
15
import { useDispatch, useSelector } from 'react-redux';
16

17
// material ui
18
import { TextField } from '@mui/material';
19

20
import historyImage from '../../../assets/img/history.png';
21
import { setSnackbar } from '../../actions/appActions';
22
import { getDeploymentsByStatus, setDeploymentsState } from '../../actions/deploymentActions';
23
import { advanceOnboarding } from '../../actions/onboardingActions';
24
import { BEGINNING_OF_TIME, SORTING_OPTIONS } from '../../constants/appConstants';
25
import { DEPLOYMENT_STATES, DEPLOYMENT_TYPES } from '../../constants/deploymentConstants';
26
import { onboardingSteps } from '../../constants/onboardingConstants';
27
import { getISOStringBoundaries } from '../../helpers';
28
import {
29
  getDeploymentsSelectionState,
30
  getDevicesById,
31
  getGroupNames,
32
  getIdAttribute,
33
  getMappedDeploymentSelection,
34
  getOnboardingState,
35
  getUserCapabilities
36
} from '../../selectors';
37
import { getOnboardingComponentFor } from '../../utils/onboardingmanager';
38
import useWindowSize from '../../utils/resizehook';
39
import { clearAllRetryTimers, clearRetryTimer, setRetryTimer } from '../../utils/retrytimer';
40
import { ControlledAutoComplete } from '../common/forms/autocomplete';
41
import Filters from '../common/forms/filters';
42
import TimeframePicker from '../common/forms/timeframe-picker';
43
import { DeploymentSize, DeploymentStatus } from './deploymentitem';
44
import { defaultRefreshDeploymentsLength as refreshDeploymentsLength } from './deployments';
45
import DeploymentsList, { defaultHeaders } from './deploymentslist';
46

47
const headers = [
7✔
48
  ...defaultHeaders.slice(0, defaultHeaders.length - 1),
49
  { title: 'Status', renderer: DeploymentStatus },
50
  { title: 'Data downloaded', renderer: DeploymentSize }
51
];
52

53
const type = DEPLOYMENT_STATES.finished;
7✔
54

55
export const Past = props => {
7✔
56
  const { createClick, isShowingDetails } = props;
68✔
57
  // eslint-disable-next-line no-unused-vars
58
  const size = useWindowSize();
67✔
59
  const [tonight] = useState(getISOStringBoundaries(new Date()).end);
67✔
60
  const [loading, setLoading] = useState(false);
67✔
61
  const deploymentsRef = useRef();
67✔
62
  const timer = useRef();
67✔
63

64
  const dispatch = useDispatch();
67✔
65
  const dispatchedSetSnackbar = useCallback((...args) => dispatch(setSnackbar(...args)), [dispatch]);
67✔
66

67
  const { finished: pastSelectionState } = useSelector(getDeploymentsSelectionState);
67✔
68
  const past = useSelector(state => getMappedDeploymentSelection(state, type));
125✔
69
  const { canConfigure, canDeploy } = useSelector(getUserCapabilities);
67✔
70
  const { attribute: idAttribute } = useSelector(getIdAttribute);
67✔
71
  const onboardingState = useSelector(getOnboardingState);
67✔
72
  const devices = useSelector(getDevicesById);
67✔
73
  const groupNames = useSelector(getGroupNames);
67✔
74

75
  const { endDate, page, perPage, search: deviceGroup, startDate, total: count, type: deploymentType } = pastSelectionState;
67✔
76

77
  /*
78
  / refresh only finished deployments
79
  /
80
  */
81
  const refreshPast = useCallback(
67✔
82
    (
83
      currentPage = page,
×
84
      currentPerPage = perPage,
×
85
      currentStartDate = startDate,
×
86
      currentEndDate = endDate,
×
87
      currentDeviceGroup = deviceGroup,
×
88
      currentType = deploymentType
×
89
    ) => {
UNCOV
90
      const roundedStartDate = Math.round(Date.parse(currentStartDate) / 1000);
×
UNCOV
91
      const roundedEndDate = Math.round(Date.parse(currentEndDate) / 1000);
×
UNCOV
92
      setLoading(true);
×
UNCOV
93
      return dispatch(getDeploymentsByStatus(type, currentPage, currentPerPage, roundedStartDate, roundedEndDate, currentDeviceGroup, currentType))
×
94
        .then(deploymentsAction => {
UNCOV
95
          setLoading(false);
×
UNCOV
96
          clearRetryTimer(type, dispatchedSetSnackbar);
×
UNCOV
97
          const { total, deploymentIds } = deploymentsAction[deploymentsAction.length - 1];
×
UNCOV
98
          if (total && !deploymentIds.length) {
×
UNCOV
99
            return refreshPast(currentPage, currentPerPage, currentStartDate, currentEndDate, currentDeviceGroup);
×
100
          }
101
        })
UNCOV
102
        .catch(err => setRetryTimer(err, 'deployments', `Couldn't load deployments.`, refreshDeploymentsLength, dispatchedSetSnackbar));
×
103
    },
104
    [deploymentType, deviceGroup, dispatch, dispatchedSetSnackbar, endDate, page, perPage, startDate]
105
  );
106

107
  useEffect(() => {
67✔
108
    console.log('getting list with', startDate);
4✔
109
    const roundedStartDate = Math.round(Date.parse(startDate || BEGINNING_OF_TIME) / 1000);
4✔
110
    const roundedEndDate = Math.round(Date.parse(endDate) / 1000);
4✔
111
    setLoading(true);
4✔
112
    dispatch(getDeploymentsByStatus(type, page, perPage, roundedStartDate, roundedEndDate, deviceGroup, deploymentType, true, SORTING_OPTIONS.desc))
4✔
113
      .then(deploymentsAction => {
114
        const deploymentsList = deploymentsAction ? Object.values(deploymentsAction[0].deployments) : [];
3!
115
        if (deploymentsList.length) {
3!
116
          let newStartDate = new Date(deploymentsList[deploymentsList.length - 1].created);
3✔
117
          const { start } = getISOStringBoundaries(newStartDate);
3✔
118
          dispatch(setDeploymentsState({ [DEPLOYMENT_STATES.finished]: { startDate: startDate || start } }));
3✔
119
        }
120
      })
121
      .finally(() => setLoading(false));
3✔
122
    return () => {
4✔
123
      clearAllRetryTimers(dispatchedSetSnackbar);
4✔
124
    };
125
  }, [deploymentType, deviceGroup, dispatch, dispatchedSetSnackbar, endDate, page, perPage, startDate]);
126

127
  useEffect(() => {
67✔
128
    clearInterval(timer.current);
4✔
129
    timer.current = setInterval(refreshPast, refreshDeploymentsLength);
4✔
130
    // refreshPast();
131
    return () => {
4✔
132
      clearInterval(timer.current);
4✔
133
    };
134
  }, [page, perPage, startDate, endDate, deviceGroup, deploymentType, refreshPast]);
135

136
  useEffect(() => {
67✔
137
    if (!past.length || onboardingState.complete) {
28✔
138
      return;
22✔
139
    }
140
    const pastDeploymentsFailed = past.reduce(
6✔
141
      (accu, item) =>
142
        item.status === 'failed' ||
8✔
143
        (item.statistics?.status &&
144
          item.statistics.status.noartifact + item.statistics.status.failure + item.statistics.status['already-installed'] + item.statistics.status.aborted >
145
            0) ||
146
        accu,
147
      false
148
    );
149
    let onboardingStep = onboardingSteps.DEPLOYMENTS_PAST;
6✔
150
    if (pastDeploymentsFailed) {
6!
UNCOV
151
      onboardingStep = onboardingSteps.DEPLOYMENTS_PAST_COMPLETED_FAILURE;
×
152
    }
153
    dispatch(advanceOnboarding(onboardingStep));
6✔
154
  }, [dispatch, onboardingState.complete, past]);
155

156
  let onboardingComponent = null;
67✔
157
  if (deploymentsRef.current) {
67✔
158
    const detailsButtons = deploymentsRef.current.getElementsByClassName('MuiButton-contained');
39✔
159
    const left = detailsButtons.length
39!
160
      ? deploymentsRef.current.offsetLeft + detailsButtons[0].offsetLeft + detailsButtons[0].offsetWidth / 2 + 15
161
      : deploymentsRef.current.offsetWidth;
162
    let anchor = { left: deploymentsRef.current.offsetWidth / 2, top: deploymentsRef.current.offsetTop };
39✔
163
    onboardingComponent = getOnboardingComponentFor(onboardingSteps.DEPLOYMENTS_PAST_COMPLETED, onboardingState, {
39✔
164
      anchor,
165
      setSnackbar: dispatchedSetSnackbar
166
    });
167
    onboardingComponent = getOnboardingComponentFor(
39✔
168
      onboardingSteps.DEPLOYMENTS_PAST_COMPLETED_FAILURE,
169
      onboardingState,
170
      { anchor: { left, top: detailsButtons[0].parentElement.offsetTop + detailsButtons[0].parentElement.offsetHeight } },
171
      onboardingComponent
172
    );
173
  }
174

175
  const onFiltersChange = useCallback(
67✔
176
    ({ endDate, group, startDate, type }) =>
177
      dispatch(setDeploymentsState({ [DEPLOYMENT_STATES.finished]: { page: 1, search: group, type, startDate, endDate } })),
4✔
178
    [dispatch]
179
  );
180

181
  const autoCompleteProps = { autoHighlight: true, autoSelect: true, filterSelectedOptions: true, freeSolo: true, handleHomeEndKeys: true };
67✔
182
  return (
67✔
183
    <div className="fadeIn margin-left margin-top-large">
184
      <Filters
185
        initialValues={{ startDate, endDate, group: deviceGroup, type: deploymentType }}
186
        defaultValues={{ startDate: '', endDate: tonight, group: '', type: '' }}
187
        filters={[
188
          {
189
            key: 'group',
190
            title: 'Device group',
191
            Component: ControlledAutoComplete,
192
            componentProps: {
193
              ...autoCompleteProps,
194
              options: groupNames,
195
              renderInput: params => <TextField {...params} label="Target devices" placeholder="Select a group" InputProps={{ ...params.InputProps }} />
68✔
196
            }
197
          },
198
          {
199
            key: 'type',
200
            title: 'Contains Artifact type',
201
            Component: ControlledAutoComplete,
202
            componentProps: {
203
              ...autoCompleteProps,
204
              options: Object.keys(DEPLOYMENT_TYPES),
205
              renderInput: params => <TextField {...params} label="Deployment type" placeholder="Select a type" InputProps={{ ...params.InputProps }} />
68✔
206
            }
207
          },
208
          {
209
            key: 'timeframe',
210
            title: 'Start time',
211
            Component: TimeframePicker,
212
            componentProps: {
213
              tonight
214
            }
215
          }
216
        ]}
217
        onChange={onFiltersChange}
218
      />
219
      <div className="deploy-table-contain">
220
        {/* TODO: fix status retrieval for past deployments to decide what to show here - */}
221
        {!loading && !!past.length && !!onboardingComponent && !isShowingDetails && onboardingComponent}
114!
222
        {!!past.length && (
109✔
223
          <DeploymentsList
224
            {...props}
225
            canConfigure={canConfigure}
226
            canDeploy={canDeploy}
227
            componentClass="margin-left-small"
228
            count={count}
229
            devices={devices}
230
            headers={headers}
231
            idAttribute={idAttribute}
232
            items={past}
233
            loading={loading}
UNCOV
234
            onChangePage={page => dispatch(setDeploymentsState({ [DEPLOYMENT_STATES.finished]: { page } }))}
×
UNCOV
235
            onChangeRowsPerPage={perPage => dispatch(setDeploymentsState({ [DEPLOYMENT_STATES.finished]: { page: 1, perPage } }))}
×
236
            page={page}
237
            pageSize={perPage}
238
            rootRef={deploymentsRef}
239
            showPagination
240
            type={type}
241
          />
242
        )}
243
        {!(loading || past.length) && (
159✔
244
          <div className="dashboard-placeholder">
245
            <p>No finished deployments were found.</p>
246
            <p>
247
              Try adjusting the filters, or <a onClick={createClick}>Create a new deployment</a> to get started
248
            </p>
249
            <img src={historyImage} alt="Past" />
250
          </div>
251
        )}
252
      </div>
253
    </div>
254
  );
255
};
256

257
export default Past;
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