Skip to content

Commit b30165a

Browse files
committed
Improve human readable message
1 parent 7479538 commit b30165a

File tree

3 files changed

+139
-31
lines changed

3 files changed

+139
-31
lines changed

site/src/api/api.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -395,18 +395,23 @@ export interface AuditLog {
395395
// eslint-disable-next-line @typescript-eslint/no-explicit-any
396396
readonly ip: any
397397
readonly user_agent: string
398-
readonly resource_type: any
398+
readonly resource_type: "organization" | "template" | "template_version" | "user" | "workspace"
399399
readonly resource_id: string
400400
readonly resource_target: string
401401
readonly action: any
402-
readonly diff: any
402+
readonly diff: Record<string, { old: string; new: string }>
403403
readonly status_code: number
404404
// This is likely an enum in an external package ("encoding/json.RawMessage")
405405
readonly additional_fields: any
406406
readonly description: string
407407
readonly user?: User
408408
// This is likely an enum in an external package ("encoding/json.RawMessage")
409-
readonly resource: any
409+
readonly resource:
410+
| TypesGen.Organization
411+
| TypesGen.Template
412+
| TypesGen.TemplateVersion
413+
| TypesGen.User
414+
| TypesGen.Workspace
410415
}
411416

412417
export const getAuditLogs = async (): Promise<AuditLog[]> => {

site/src/pages/AuditPage/AuditPageView.tsx

Lines changed: 129 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import Collapse from "@material-ui/core/Collapse"
2+
import Link from "@material-ui/core/Link"
23
import { makeStyles } from "@material-ui/core/styles"
34
import Table from "@material-ui/core/Table"
45
import TableBody from "@material-ui/core/TableBody"
@@ -7,6 +8,7 @@ import TableContainer from "@material-ui/core/TableContainer"
78
import TableHead from "@material-ui/core/TableHead"
89
import TableRow from "@material-ui/core/TableRow"
910
import { AuditLog } from "api/api"
11+
import { Template, Workspace } from "api/typesGenerated"
1012
import { CodeExample } from "components/CodeExample/CodeExample"
1113
import { CloseDropdown, OpenDropdown } from "components/DropdownArrows/DropdownArrows"
1214
import { Margins } from "components/Margins/Margins"
@@ -17,40 +19,124 @@ import { TableLoader } from "components/TableLoader/TableLoader"
1719
import { AuditHelpTooltip } from "components/Tooltips"
1820
import { UserAvatar } from "components/UserAvatar/UserAvatar"
1921
import { FC, useState } from "react"
22+
import { Link as RouterLink } from "react-router-dom"
23+
import { colors } from "theme/colors"
24+
import { combineClasses } from "util/combineClasses"
2025
import { createDayString } from "util/createDayString"
2126

22-
const AuditDiff = () => {
27+
const getDiffValue = (value: number | string | boolean) => {
28+
if (typeof value === "string") {
29+
return `"${value}"`
30+
}
31+
32+
return value.toString()
33+
}
34+
35+
const AuditDiff: React.FC<{ diff: AuditLog["diff"] }> = ({ diff }) => {
2336
const styles = useStyles()
37+
const diffEntries = Object.entries(diff)
2438

2539
return (
2640
<div className={styles.diff}>
27-
<div className={styles.diffOld}>
28-
<div className={styles.diffRow}>
29-
<div className={styles.diffLine}>1</div>
30-
<div className={styles.diffIcon}>-</div>
31-
<div className={styles.diffContent}>
32-
workspace_name: <span>alice-workspace</span>
41+
<div className={combineClasses([styles.diffColumn, styles.diffOld])}>
42+
{diffEntries.map(([attrName, valueDiff], index) => (
43+
<div key={attrName} className={styles.diffRow}>
44+
<div className={styles.diffLine}>{index + 1}</div>
45+
<div className={styles.diffIcon}>-</div>
46+
<div className={styles.diffContent}>
47+
{attrName}:{" "}
48+
<span className={combineClasses([styles.diffValue, styles.diffValueOld])}>
49+
{getDiffValue(valueDiff.old)}
50+
</span>
51+
</div>
3352
</div>
34-
</div>
53+
))}
3554
</div>
36-
<div className={styles.diffNew}>
37-
<div className={styles.diffRow}>
38-
<div className={styles.diffLine}>1</div>
39-
<div className={styles.diffIcon}>+</div>
40-
<div className={styles.diffContent}>
41-
workspace_name: <span>bruno-workspace</span>
55+
<div className={combineClasses([styles.diffColumn, styles.diffNew])}>
56+
{diffEntries.map(([attrName, valueDiff], index) => (
57+
<div key={attrName} className={styles.diffRow}>
58+
<div className={styles.diffLine}>{index + 1}</div>
59+
<div className={styles.diffIcon}>+</div>
60+
<div className={styles.diffContent}>
61+
{attrName}:{" "}
62+
<span className={combineClasses([styles.diffValue, styles.diffValueNew])}>
63+
{getDiffValue(valueDiff.new)}
64+
</span>
65+
</div>
4266
</div>
43-
</div>
67+
))}
4468
</div>
4569
</div>
4670
)
4771
}
4872

73+
const getResourceLabel = (resource: AuditLog["resource"]): string => {
74+
if ("name" in resource) {
75+
return resource.name
76+
}
77+
78+
return resource.username
79+
}
80+
81+
const getResourceHref = (
82+
resource: AuditLog["resource"],
83+
resourceType: AuditLog["resource_type"],
84+
): string | undefined => {
85+
switch (resourceType) {
86+
case "user":
87+
return `/users`
88+
case "template":
89+
return `/templates/${(resource as Template).name}`
90+
case "workspace":
91+
return `/workspaces/@${(resource as Workspace).owner_name}/${(resource as Workspace).name}`
92+
case "organization":
93+
return
94+
}
95+
}
96+
97+
const ResourceLink: React.FC<{
98+
resource: AuditLog["resource"]
99+
resourceType: AuditLog["resource_type"]
100+
}> = ({ resource, resourceType }) => {
101+
const href = getResourceHref(resource, resourceType)
102+
const label = <strong>{getResourceLabel(resource)}</strong>
103+
104+
if (!href) {
105+
return label
106+
}
107+
108+
return (
109+
<Link component={RouterLink} to={href}>
110+
{label}
111+
</Link>
112+
)
113+
}
114+
115+
const actionLabelByAction: Record<AuditLog["action"], string> = {
116+
create: "created",
117+
write: "updated",
118+
delete: "deleted",
119+
}
120+
121+
const resourceLabelByResourceType: Record<AuditLog["resource_type"], string> = {
122+
organization: "organization",
123+
template: "template",
124+
template_version: "template version",
125+
user: "user",
126+
workspace: "workspace",
127+
}
128+
129+
const readableActionMessage = (auditLog: AuditLog) => {
130+
return `${actionLabelByAction[auditLog.action]} ${
131+
resourceLabelByResourceType[auditLog.resource_type]
132+
}`
133+
}
134+
49135
const AuditLogRow: React.FC<{ auditLog: AuditLog }> = ({ auditLog }) => {
50136
const styles = useStyles()
51137
const [isDiffOpen, setIsDiffOpen] = useState(false)
52138
const diffs = Object.entries(auditLog.diff)
53-
const shouldDisplayDiff = diffs.length > 1
139+
const shouldDisplayDiff = diffs.length > 0
54140

55141
const toggle = () => {
56142
if (shouldDisplayDiff) {
@@ -84,8 +170,11 @@ const AuditLogRow: React.FC<{ auditLog: AuditLog }> = ({ auditLog }) => {
84170
<UserAvatar username={auditLog.user?.username ?? ""} />
85171
<div>
86172
<span className={styles.auditLogResume}>
87-
<strong>{auditLog.user?.username}</strong> {auditLog.action}{" "}
88-
<strong>{auditLog.resource.name}</strong>
173+
<strong>{auditLog.user?.username}</strong> {readableActionMessage(auditLog)}{" "}
174+
<ResourceLink
175+
resource={auditLog.resource}
176+
resourceType={auditLog.resource_type}
177+
/>
89178
</span>
90179
<span className={styles.auditLogTime}>{createDayString(auditLog.time)}</span>
91180
</div>
@@ -111,7 +200,7 @@ const AuditLogRow: React.FC<{ auditLog: AuditLog }> = ({ auditLog }) => {
111200

112201
{shouldDisplayDiff && (
113202
<Collapse in={isDiffOpen}>
114-
<AuditDiff />
203+
<AuditDiff diff={auditLog.diff} />
115204
</Collapse>
116205
)}
117206
</TableCell>
@@ -205,12 +294,16 @@ const useStyles = makeStyles((theme) => ({
205294
borderTop: `1px solid ${theme.palette.divider}`,
206295
},
207296

297+
diffColumn: {
298+
flex: 1,
299+
paddingTop: theme.spacing(2),
300+
paddingBottom: theme.spacing(2.5),
301+
lineHeight: "160%",
302+
},
303+
208304
diffOld: {
209305
backgroundColor: theme.palette.error.dark,
210306
color: theme.palette.error.contrastText,
211-
flex: 1,
212-
paddingTop: theme.spacing(1),
213-
paddingBottom: theme.spacing(1),
214307
},
215308

216309
diffRow: {
@@ -220,13 +313,12 @@ const useStyles = makeStyles((theme) => ({
220313

221314
diffLine: {
222315
opacity: 0.5,
223-
padding: theme.spacing(1),
316+
224317
width: theme.spacing(8),
225318
textAlign: "right",
226319
},
227320

228321
diffIcon: {
229-
padding: theme.spacing(1),
230322
width: theme.spacing(4),
231323
textAlign: "center",
232324
fontSize: theme.typography.body1.fontSize,
@@ -237,8 +329,18 @@ const useStyles = makeStyles((theme) => ({
237329
diffNew: {
238330
backgroundColor: theme.palette.success.dark,
239331
color: theme.palette.success.contrastText,
240-
flex: 1,
241-
paddingTop: theme.spacing(1),
242-
paddingBottom: theme.spacing(1),
332+
},
333+
334+
diffValue: {
335+
padding: 1,
336+
borderRadius: theme.shape.borderRadius / 2,
337+
},
338+
339+
diffValueOld: {
340+
backgroundColor: colors.red[12],
341+
},
342+
343+
diffValueNew: {
344+
backgroundColor: colors.green[12],
243345
},
244346
}))

site/src/testHelpers/entities.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { AuditLog } from "api/api"
12
import { FieldError } from "api/errors"
23
import * as Types from "../api/types"
34
import * as TypesGen from "../api/typesGenerated"
@@ -685,7 +686,7 @@ export const MockEntitlementsWithAuditLog: TypesGen.Entitlements = {
685686
},
686687
}
687688

688-
export const MockAuditLog = {
689+
export const MockAuditLog: AuditLog = {
689690
id: "fbd2116a-8961-4954-87ae-e4575bd29ce0",
690691
request_id: "53bded77-7b9d-4e82-8771-991a34d759f9",
691692
time: "2022-05-19T16:45:57.122Z",

0 commit comments

Comments
 (0)