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

mendersoftware / mender-server / 1571211123

03 Dec 2024 03:38PM UTC coverage: 73.495% (+0.7%) from 72.795%
1571211123

Pull #227

gitlab-ci

lluiscampos
chore: Use Virtual Device for OS updates in docker composition

For the upcoming release, we will use the same Virtual Device that we
had in the Mender 3.x series. After the release, we will work on
migrating to the Virtual Device for app updates consistently across
docker composition, ui and docs. See more context in the parent JIRA
epic related to this task.

For the e2e tests, use an override to replace the image with the new
Virtual Device (basically moving the previous `docker-compose` service
there).

Ticket: MEN-7788

Signed-off-by: Lluis Campos <lluis.campos@northern.tech>
Pull Request #227: MEN-7788: Use Virtual Device for OS updates in docker composition

4181 of 6081 branches covered (68.76%)

Branch coverage included in aggregate %.

39910 of 53911 relevant lines covered (74.03%)

16.69 hits per line

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

62.96
/frontend/src/js/components/auditlogs/auditlogslist.js
1
// Copyright 2020 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
import { Link } from 'react-router-dom';
16

17
import { Sort as SortIcon } from '@mui/icons-material';
18

19
import DetailsIndicator from '@northern.tech/common-ui/detailsindicator';
20
import DeviceIdentityDisplay from '@northern.tech/common-ui/deviceidentity';
21
import Loader from '@northern.tech/common-ui/loader';
22
import Pagination from '@northern.tech/common-ui/pagination';
23
import Time from '@northern.tech/common-ui/time';
24
import { DEPLOYMENT_ROUTES, SORTING_OPTIONS, auditlogTypes, canAccess } from '@northern.tech/store/constants';
25

26
import EventDetailsDrawer from './eventdetailsdrawer';
27

28
export const defaultRowsPerPage = 20;
4✔
29

30
const ArtifactLink = ({ item }) => <Link to={`/releases/${item.object.artifact.name}`}>View artifact</Link>;
4✔
31
const DeploymentLink = ({ item }) => <Link to={`${DEPLOYMENT_ROUTES.finished.route}?open=true&id=${item.object.id}`}>View deployment</Link>;
11✔
32
const DeviceLink = ({ item }) => <Link to={`/devices?id=${item.object.id}`}>View device</Link>;
4✔
33
const DeviceRejectedLink = ({ item }) => <Link to={`/devices/rejected?id=${item.object.id}`}>View device</Link>;
4✔
34
const TerminalSessionLink = () => <a>View session log</a>;
11✔
35
const ChangeFallback = props => {
4✔
36
  const {
37
    item: { change = '-' }
×
38
  } = props;
11✔
39
  return <div>{change}</div>;
11✔
40
};
41

42
const FallbackFormatter = props => {
4✔
43
  let result = '';
×
44
  try {
×
45
    result = JSON.stringify(props);
×
46
  } catch (error) {
47
    console.log(error);
×
48
  }
49
  return <div>{result}</div>;
×
50
};
51

52
const ArtifactFormatter = ({ artifact }) => <div>{artifact.name}</div>;
4✔
53
const DeploymentFormatter = ({ deployment }) => <div>{deployment.name}</div>;
11✔
54
const DeviceFormatter = ({ id }) => <DeviceIdentityDisplay device={{ id }} />;
11✔
55
const UserFormatter = ({ user }) => <div>{user.email}</div>;
11✔
56
const TenantFormatter = ({ tenant }) => <div>{tenant.name}</div>;
4✔
57

58
const defaultAccess = canAccess;
4✔
59
const changeMap = {
4✔
60
  default: { component: 'div', actionFormatter: FallbackFormatter, title: 'defaultTitle', accessCheck: defaultAccess },
61
  artifact: { actionFormatter: ArtifactFormatter, component: ArtifactLink, accessCheck: ({ canReadReleases }) => canReadReleases },
×
62
  deployment: {
63
    actionFormatter: DeploymentFormatter,
64
    component: DeploymentLink,
65
    accessCheck: ({ canReadDeployments }) => canReadDeployments
11✔
66
  },
67
  deviceDecommissioned: { actionFormatter: DeviceFormatter, component: 'div', accessCheck: defaultAccess },
68
  deviceRejected: { actionFormatter: DeviceFormatter, component: DeviceRejectedLink, accessCheck: ({ canReadDevices }) => canReadDevices },
×
69
  deviceGeneral: { actionFormatter: DeviceFormatter, component: DeviceLink, accessCheck: ({ canReadDevices }) => canReadDevices },
×
70
  deviceTerminalSession: { actionFormatter: DeviceFormatter, component: TerminalSessionLink, accessCheck: defaultAccess },
71
  user: { actionFormatter: UserFormatter, component: ChangeFallback, accessCheck: defaultAccess },
72
  user_access_token: { actionFormatter: FallbackFormatter, component: ChangeFallback, accessCheck: defaultAccess },
73
  tenant: { actionFormatter: TenantFormatter, component: ChangeFallback, accessCheck: defaultAccess }
74
};
75

76
const mapChangeToContent = item => {
4✔
77
  let content = changeMap[item.object.type];
66✔
78
  if (content) {
66✔
79
    return content;
44✔
80
  } else if (item.object.type === 'device' && item.action.includes('terminal')) {
22!
81
    content = changeMap.deviceTerminalSession;
22✔
82
  } else if (item.object.type === 'device' && item.action.includes('reject')) {
×
83
    content = changeMap.deviceRejected;
×
84
  } else if (item.object.type === 'device' && item.action.includes('decommission')) {
×
85
    content = changeMap.deviceDecommissioned;
×
86
  } else if (item.object.type === 'device') {
×
87
    content = changeMap.deviceGeneral;
×
88
  } else {
89
    content = changeMap.default;
×
90
  }
91
  return content;
22✔
92
};
93

94
const actorMap = {
4✔
95
  user: 'email',
96
  device: 'id'
97
};
98

99
const UserDescriptor = (item, index) => <div key={`${item.time}-${index} `}>{item.actor[actorMap[item.actor.type]]}</div>;
33✔
100
const ActionDescriptor = (item, index) => (
4✔
101
  <div className="uppercased" key={`${item.time}-${index}`}>
33✔
102
    {item.action}
103
  </div>
104
);
105
const TypeDescriptor = (item, index) => (
4✔
106
  <div className="capitalized" key={`${item.time}-${index}`}>
33✔
107
    {auditlogTypes[item.object.type]?.title ?? item.object.type}
33!
108
  </div>
109
);
110
const ChangeDescriptor = (item, index) => {
4✔
111
  const FormatterComponent = mapChangeToContent(item).actionFormatter;
33✔
112
  return <FormatterComponent key={`${item.time}-${index}`} {...item.object} />;
33✔
113
};
114
const ChangeDetailsDescriptor = (item, index, userCapabilities) => {
4✔
115
  const { component: Comp, accessCheck } = mapChangeToContent(item);
33✔
116
  const key = `${item.time}-${index}`;
33✔
117
  return accessCheck(userCapabilities) ? <Comp key={key} item={item} /> : <div key={key} />;
33!
118
};
119
const TimeWrapper = (item, index) => <Time key={`${item.time}-${index}`} value={item.time} />;
33✔
120

121
const auditLogColumns = [
4✔
122
  { title: 'Performed by', sortable: false, render: UserDescriptor },
123
  { title: 'Action', sortable: false, render: ActionDescriptor },
124
  { title: 'Type', sortable: false, render: TypeDescriptor },
125
  { title: 'Changed', sortable: false, render: ChangeDescriptor },
126
  { title: 'More details', sortable: false, render: ChangeDetailsDescriptor },
127
  { title: 'Time', sortable: true, render: TimeWrapper }
128
];
129

130
export const AuditLogsList = ({
4✔
131
  eventItem,
132
  items,
133
  loading,
134
  onChangePage,
135
  onChangeRowsPerPage,
136
  onChangeSorting,
137
  selectionState,
138
  setAuditlogsState,
139
  userCapabilities
140
}) => {
141
  const { page, perPage, sort = {}, total: count } = selectionState;
11!
142

143
  const onIssueSelection = selectedIssue =>
11✔
144
    setAuditlogsState({ selectedId: selectedIssue ? btoa(`${selectedIssue.action}|${selectedIssue.time}`) : undefined });
1!
145

146
  return (
11✔
147
    !!items.length && (
22✔
148
      <div className="fadeIn deploy-table-contain auditlogs-list">
149
        <div className="auditlogs-list-item auditlogs-list-item-header muted">
150
          {auditLogColumns.map((column, index) => (
151
            <div
66✔
152
              className="columnHeader"
153
              key={`columnHeader-${index}`}
154
              onClick={() => (column.sortable ? onChangeSorting() : null)}
×
155
              style={column.sortable ? {} : { cursor: 'initial' }}
66✔
156
            >
157
              {column.title}
158
              {column.sortable ? <SortIcon className={`sortIcon selected ${(sort.direction === SORTING_OPTIONS.desc).toString()}`} /> : null}
66✔
159
            </div>
160
          ))}
161
          <div />
162
        </div>
163
        <div className="auditlogs-list">
164
          {items.map(item => {
165
            const allowsExpansion = !!item.change || item.action.includes('terminal') || item.action.includes('portforward');
33!
166
            return (
33✔
167
              <div
168
                className={`auditlogs-list-item ${allowsExpansion ? 'clickable' : ''}`}
33!
169
                key={`event-${item.time}`}
170
                onClick={() => onIssueSelection(allowsExpansion ? item : undefined)}
1!
171
              >
172
                {auditLogColumns.map((column, index) => column.render(item, index, userCapabilities))}
198✔
173
                {allowsExpansion ? <DetailsIndicator /> : <div />}
33!
174
              </div>
175
            );
176
          })}
177
        </div>
178
        <div className="flexbox margin-top">
179
          <Pagination
180
            className="margin-top-none"
181
            count={count}
182
            rowsPerPage={perPage}
183
            onChangeRowsPerPage={onChangeRowsPerPage}
184
            page={page}
185
            onChangePage={onChangePage}
186
          />
187
          <Loader show={loading} small />
188
        </div>
189
        <EventDetailsDrawer eventItem={eventItem} open={Boolean(eventItem)} onClose={() => onIssueSelection()} />
×
190
      </div>
191
    )
192
  );
193
};
194

195
export default AuditLogsList;
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