blob: e0c0d30a352158a0aa98d12d4763e1cdc12ae158 [file] [log] [blame]
/**
* @license
* Copyright 2017 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import '../../shared/gr-account-label/gr-account-label';
import {
GroupInfo,
AccountInfo,
EncodedGroupId,
GroupAuditEventInfo,
GroupAuditGroupEventInfo,
isGroupAuditGroupEventInfo,
} from '../../../types/common';
import {firePageError, fireTitleChange} from '../../../utils/event-util';
import {getAppContext} from '../../../services/app-context';
import {ErrorCallback} from '../../../api/rest';
import {sharedStyles} from '../../../styles/shared-styles';
import {tableStyles} from '../../../styles/gr-table-styles';
import {LitElement, PropertyValues, css, html} from 'lit';
import {customElement, property, state} from 'lit/decorators.js';
import {createGroupUrl} from '../../../models/views/group';
declare global {
interface HTMLElementTagNameMap {
'gr-group-audit-log': GrGroupAuditLog;
}
}
@customElement('gr-group-audit-log')
export class GrGroupAuditLog extends LitElement {
@property({type: String})
groupId?: EncodedGroupId;
@state() private auditLog?: GroupAuditEventInfo[];
@state() private loading = true;
private readonly restApiService = getAppContext().restApiService;
override connectedCallback() {
super.connectedCallback();
fireTitleChange(this, 'Audit Log');
}
static override get styles() {
return [
sharedStyles,
tableStyles,
css`
/* GenericList style centers the last column, but we don't want that here. */
.genericList tr th:last-of-type,
.genericList tr td:last-of-type {
text-align: left;
}
`,
];
}
override render() {
return html`
<table id="list" class="genericList">
<tbody>
<tr class="headerRow">
<th class="date topHeader">Date</th>
<th class="type topHeader">Type</th>
<th class="member topHeader">Member</th>
<th class="by-user topHeader">By User</th>
</tr>
${this.renderLoading()}
</tbody>
${this.renderAuditLogTable()}
</table>
`;
}
private renderLoading() {
if (!this.loading) return;
return html`
<tr id="loading" class="loadingMsg loading">
<td>Loading...</td>
</tr>
`;
}
private renderAuditLogTable() {
if (this.loading) return;
return html`
<tbody>
${this.auditLog?.map(audit => this.renderAuditLog(audit))}
</tbody>
`;
}
private renderAuditLog(audit: GroupAuditEventInfo) {
return html`
<tr class="table">
<td class="date">
<gr-date-formatter withTooltip .dateStr=${audit.date}>
</gr-date-formatter>
</td>
<td class="type">${this.itemType(audit.type)}</td>
<td class="member">
${this.isGroupEvent(audit)
? html`<a href=${this.computeGroupUrl(audit.member)}
>${this.getNameForGroup(audit.member)}</a
>`
: html`<gr-account-label
.account=${audit.member}
clickable
></gr-account-label
>${this.getIdForUser(audit.member)}`}
</td>
<td class="by-user">
<gr-account-label clickable .account=${audit.user}></gr-account-label>
${this.getIdForUser(audit.user)}
</td>
</tr>
`;
}
override willUpdate(changedProperties: PropertyValues) {
if (changedProperties.has('groupId')) {
this.getAuditLogs();
}
}
// private but used in test
getAuditLogs() {
if (!this.groupId) return;
const errFn: ErrorCallback = response => {
firePageError(response);
};
this.loading = true;
return this.restApiService
.getGroupAuditLog(this.groupId, errFn)
.then(auditLog => {
this.auditLog = auditLog ?? [];
})
.finally(() => {
this.loading = false;
});
}
private itemType(type: string) {
let item;
switch (type) {
case 'ADD_GROUP':
case 'ADD_USER':
item = 'Added';
break;
case 'REMOVE_GROUP':
case 'REMOVE_USER':
item = 'Removed';
break;
default:
item = '';
}
return item;
}
// private but used in test
isGroupEvent(event: GroupAuditEventInfo): event is GroupAuditGroupEventInfo {
return isGroupAuditGroupEventInfo(event);
}
private computeGroupUrl(group?: GroupInfo) {
if (!group?.id) return '';
return createGroupUrl({groupId: group.id});
}
// private but used in test
getIdForUser(account: AccountInfo) {
return account._account_id ? ` (${account._account_id})` : '';
}
// private but used in test
getNameForGroup(group: GroupInfo) {
if (group && group.name) {
return group.name;
} else if (group && group.id) {
// The URL encoded id of the member
return decodeURIComponent(group.id);
}
return '';
}
}