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

mendersoftware / gui / 1235064739

01 Apr 2024 05:03AM UTC coverage: 83.603% (-16.4%) from 99.964%
1235064739

Pull #4368

gitlab-ci

web-flow
chore: Bump the development-dependencies group with 18 updates

Bumps the development-dependencies group with 18 updates:

| Package | From | To |
| --- | --- | --- |
| [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) | `7.24.0` | `7.24.3` |
| [@babel/eslint-parser](https://github.com/babel/babel/tree/HEAD/eslint/babel-eslint-parser) | `7.23.10` | `7.24.1` |
| [@babel/plugin-transform-runtime](https://github.com/babel/babel/tree/HEAD/packages/babel-plugin-transform-runtime) | `7.24.0` | `7.24.3` |
| [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) | `7.24.0` | `7.24.3` |
| [@babel/preset-react](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-react) | `7.23.3` | `7.24.1` |
| [@testing-library/react](https://github.com/testing-library/react-testing-library) | `14.2.1` | `14.2.2` |
| [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) | `7.1.0` | `7.4.0` |
| [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) | `7.1.0` | `7.4.0` |
| [autoprefixer](https://github.com/postcss/autoprefixer) | `10.4.17` | `10.4.19` |
| [core-js](https://github.com/zloirock/core-js/tree/HEAD/packages/core-js) | `3.36.0` | `3.36.1` |
| [esbuild-loader](https://github.com/privatenumber/esbuild-loader) | `4.0.3` | `4.1.0` |
| [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) | `7.33.2` | `7.34.1` |
| [eslint-plugin-sonarjs](https://github.com/SonarSource/eslint-plugin-sonarjs) | `0.24.0` | `0.25.0` |
| [eslint-webpack-plugin](https://github.com/webpack-contrib/eslint-webpack-plugin) | `4.0.1` | `4.1.0` |
| [msw](https://github.com/mswjs/msw) | `2.2.2` | `2.2.13` |
| [postcss](https://github.com/postcss/postcss) | `8.4.35` | `8.4.38` |
| [webpack](https://github.com/webpack/webpack) | `5.90.3` | `5.91.0` |
| [yarn](https://github.com/yarnpkg/yarn) | ... (continued)
Pull Request #4368: chore: Bump the development-dependencies group with 18 updates

4434 of 6325 branches covered (70.1%)

8408 of 10057 relevant lines covered (83.6%)

140.68 hits per line

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

98.08
/src/js/components/devices/device-details/installedsoftware.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 from 'react';
15

16
import { deepmerge } from '@mui/utils';
17
import { makeStyles } from 'tss-react/mui';
18

19
import { rootfsImageVersion, softwareTitleMap } from '../../../constants/releaseConstants';
20
import { extractSoftware, isEmpty } from '../../../helpers';
21
import { TwoColumnData } from '../../common/configurationobject';
22
import DeviceDataCollapse from './devicedatacollapse';
23
import DeviceInventoryLoader from './deviceinventoryloader';
24

25
const borderStyle = theme => ({ borderLeft: 'solid 1px', borderLeftColor: theme.palette.grey[500] });
11✔
26

27
const useStyles = makeStyles()(theme => ({
11✔
28
  paddingOnly: { paddingLeft: theme.spacing(2) },
29
  nestingBorders: { ...borderStyle(theme), paddingLeft: theme.spacing(2), paddingBottom: theme.spacing(2) },
30
  topLevelBorder: { ...borderStyle(theme), paddingBottom: theme.spacing(2), marginBottom: theme.spacing(-2) }
31
}));
32

33
const mapLayerInformation = (key, value, i, path) => {
11✔
34
  const infoItems = key.split('.');
93✔
35
  let priority = i;
93✔
36
  let title = infoItems[0];
93✔
37
  if (softwareTitleMap[path]) {
93✔
38
    priority = softwareTitleMap[path].priority;
20✔
39
  }
40
  const itemKey = infoItems[infoItems.length - 1];
93✔
41
  let contents = {};
93✔
42
  if (infoItems.length > 2) {
93✔
43
    contents = { content: {}, children: { [infoItems[1]]: mapLayerInformation(infoItems.slice(1).join('.'), value, i + 1, key) } };
50✔
44
  } else {
45
    contents = { content: { [itemKey]: value }, children: {} };
43✔
46
  }
47
  return {
93✔
48
    priority,
49
    key: infoItems.slice(0, -1).join('.'),
50
    title,
51
    ...contents
52
  };
53
};
54

55
const sortAndHoist = thing =>
11✔
56
  Object.entries(thing)
38✔
57
    .sort((a, b) => a[1].priority - b[1].priority)
10✔
58
    .reduce((accu, entry) => {
59
      let { children, content, title } = entry[1];
34✔
60
      title = Object.keys(content).reduce((layerTitle, key) => {
34✔
61
        if (softwareTitleMap[`${title}.${key}`]) {
25✔
62
          return softwareTitleMap[`${title}.${key}`].title;
2✔
63
        }
64
        return layerTitle;
23✔
65
      }, title);
66
      children = sortAndHoist(children);
34✔
67
      if (isEmpty(content) && children.length === 1) {
34✔
68
        title = `${title}.${children[0].title}`;
18✔
69
        content = children[0].content;
18✔
70
        children = [];
18✔
71
      }
72
      accu.push({ ...entry[1], children, content, title });
34✔
73
      return accu;
34✔
74
    }, []);
75

76
/**
77
 * to get information about the software installed on the device we first need to:
78
 * - parse the inventory attributes that are likely software references (those with a key ending on '.version')
79
 * - recursively descend the attribute to create a tree and group software based on shared prefixes
80
 * - for a shallower tree rendering the resulting tree is descended recursively once more and all
81
 *    software with only a single "sublayer" is hoisted up & listed under the shared title
82
 */
83
export const extractSoftwareInformation = (attributes = {}, sort = true) => {
11!
84
  const { software } = extractSoftware(attributes);
22✔
85

86
  const softwareLayers = software.reduce((accu, item, index) => {
22✔
87
    const layer = mapLayerInformation(item[0], item[1], index, item[0]);
43✔
88
    if (!accu[layer.title]) {
43✔
89
      accu[layer.title] = { content: {}, children: {} };
28✔
90
    }
91
    accu[layer.title] = {
43✔
92
      ...accu[layer.title],
93
      ...layer,
94
      children: deepmerge(accu[layer.title].children, layer.children),
95
      content: deepmerge(accu[layer.title].content, layer.content),
96
      priority: accu[layer.title].priority < layer.priority ? accu[layer.title].priority : layer.priority
43✔
97
    };
98
    return accu;
43✔
99
  }, {});
100
  if (sort) {
22✔
101
    return sortAndHoist(softwareLayers);
4✔
102
  }
103
  return softwareLayers;
18✔
104
};
105

106
const SoftwareLayer = ({ classes, layer, isNested, overviewOnly, setSnackbar }) => (
11✔
107
  <div className={`margin-top-small ${overviewOnly ? classes.paddingOnly : ''}`}>
11!
108
    <div className="muted">{layer.title}</div>
109
    {!isEmpty(layer.content) && (
20✔
110
      <div className={isNested || overviewOnly ? '' : classes.topLevelBorder}>
22✔
111
        <TwoColumnData
112
          className={`${isNested || overviewOnly ? 'margin-bottom-small' : ''} margin-left-small margin-top-small`}
22✔
113
          config={layer.content}
114
          compact
115
          setSnackbar={setSnackbar}
116
        />
117
      </div>
118
    )}
119
    {!overviewOnly && !!layer.children.length && (
25✔
120
      <div className={classes.nestingBorders}>
121
        {layer.children.map(child => (
122
          <SoftwareLayer classes={classes} key={child.key} layer={child} isNested setSnackbar={setSnackbar} />
6✔
123
        ))}
124
      </div>
125
    )}
126
  </div>
127
);
128

129
export const InstalledSoftware = ({ device, docsVersion, setSnackbar }) => {
11✔
130
  const { classes } = useStyles();
1✔
131

132
  const { attributes = {} } = device;
1!
133

134
  let softwareInformation = extractSoftwareInformation(attributes);
1✔
135

136
  if (!softwareInformation.length) {
1!
137
    softwareInformation = [
×
138
      {
139
        children: [],
140
        content: { [softwareTitleMap[rootfsImageVersion].title]: attributes.artifact_name },
141
        title: softwareTitleMap[rootfsImageVersion].title
142
      }
143
    ];
144
  }
145

146
  const waiting = !Object.values(attributes).some(i => i);
1✔
147
  return (
1✔
148
    <DeviceDataCollapse header={waiting && <DeviceInventoryLoader docsVersion={docsVersion} />} title="Installed software">
1!
149
      <div className={classes.nestingBorders}>
150
        {softwareInformation.map(layer => (
151
          <SoftwareLayer classes={classes} key={layer.key} layer={layer} setSnackbar={setSnackbar} />
5✔
152
        ))}
153
      </div>
154
    </DeviceDataCollapse>
155
  );
156
};
157

158
export default InstalledSoftware;
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