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

mendersoftware / mender-server / 1559917075

25 Nov 2024 09:11PM UTC coverage: 73.485% (+0.7%) from 72.77%
1559917075

Pull #213

gitlab-ci

mzedel
fix(gui): fixed an issue that caused version information to be parsed wrong

Ticket: None
Changelog: None
Signed-off-by: Manuel Zedel <manuel.zedel@northern.tech>
Pull Request #213: fix(gui): fixed an issue that caused version information to be parsed wrong

4174 of 6072 branches covered (68.74%)

Branch coverage included in aggregate %.

39904 of 53910 relevant lines covered (74.02%)

16.69 hits per line

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

63.21
/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, 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: { component: ChangeFallback, actionFormatter: UserFormatter, accessCheck: defaultAccess },
72
  tenant: { actionFormatter: TenantFormatter, accessCheck: defaultAccess, component: ChangeFallback }
73
};
74

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

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

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

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

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

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

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

194
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