Skip to content

Commit f50cc3a

Browse files
committed
camelToSnakeJson for json body
1 parent 0f6bde1 commit f50cc3a

File tree

1 file changed

+44
-4
lines changed

1 file changed

+44
-4
lines changed

app/pages/system/AuditLog.tsx

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,42 @@ import { docLinks } from '~/util/links'
3131

3232
export const handle = { crumb: 'Audit Log' }
3333

34+
/**
35+
* Convert API response JSON from the camel-cased version we get out of the TS
36+
* client back into snake-case, which is what we get from the API. This is truly
37+
* stupid but I can't think of a better way.
38+
*/
39+
function camelToSnakeJson(o: Record<string, unknown>): Record<string, unknown> {
40+
const result: Record<string, unknown> = {}
41+
42+
for (const originalKey in o) {
43+
if (!Object.prototype.hasOwnProperty.call(o, originalKey)) {
44+
continue
45+
}
46+
47+
const snakeKey = originalKey
48+
.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`)
49+
.replace(/^_/, '')
50+
const value = o[originalKey]
51+
52+
if (value !== null && typeof value === 'object') {
53+
if (Array.isArray(value)) {
54+
result[snakeKey] = value.map((item) =>
55+
item !== null && typeof item === 'object' && !Array.isArray(item)
56+
? camelToSnakeJson(item as Record<string, unknown>)
57+
: item
58+
)
59+
} else {
60+
result[snakeKey] = camelToSnakeJson(value as Record<string, unknown>)
61+
}
62+
} else {
63+
result[snakeKey] = value
64+
}
65+
}
66+
67+
return result
68+
}
69+
3470
const Indent = ({ depth }: { depth: number }) => (
3571
<span className="inline-block" style={{ width: `${depth * 4 + 1}ch` }} />
3672
)
@@ -41,6 +77,8 @@ const Primitive = ({ value }: { value: null | boolean | number | string }) => (
4177
</span>
4278
)
4379

80+
// TODO: avoid converting JSON to string and then parsing again. just need a better memo
81+
4482
// silly faux highlighting
4583
// avoids unnecessary import of a library and all that overhead
4684
const HighlightJSON = memo(({ jsonString }: { jsonString: string }) => {
@@ -215,7 +253,10 @@ export default function SiloAuditLogsPage() {
215253
{rowVirtualizer.getVirtualItems().map((virtualRow) => {
216254
const log = allItems[virtualRow.index]
217255
const isExpanded = expandedItem === virtualRow.index.toString()
218-
const jsonString = JSON.stringify(log, null, 2)
256+
// only bother doing all this computation if we're the expanded row
257+
const jsonString = isExpanded
258+
? JSON.stringify(camelToSnakeJson(log), null, 2)
259+
: ''
219260

220261
const [userId, siloId] = match(log.actor)
221262
.with({ kind: 'silo_user' }, (actor) => [actor.siloUserId, actor.siloId])
@@ -232,7 +273,7 @@ export default function SiloAuditLogsPage() {
232273
transform: `translateY(${virtualRow.start}px)`,
233274
}}
234275
>
235-
<button
276+
<div
236277
className={cn(
237278
'grid h-9 w-full cursor-pointer items-center gap-8 px-[var(--content-gutter)] text-left text-sans-md border-secondary',
238279
isExpanded ? 'bg-raise' : 'hover:bg-raise',
@@ -243,7 +284,6 @@ export default function SiloAuditLogsPage() {
243284
const newValue = isExpanded ? null : virtualRow.index.toString()
244285
handleToggle(newValue)
245286
}}
246-
type="button"
247287
>
248288
{/* TODO: might be especially useful here to get the original UTC timestamp in a tooltip */}
249289
<div className="overflow-hidden whitespace-nowrap text-mono-sm">
@@ -292,7 +332,7 @@ export default function SiloAuditLogsPage() {
292332
{differenceInMilliseconds(new Date(log.timeCompleted), log.timeStarted)}
293333
ms
294334
</div>
295-
</button>
335+
</div>
296336
{isExpanded && (
297337
<div className="h-72 border-t px-[var(--content-gutter)] py-3 border-secondary">
298338
<pre className="h-full overflow-auto border-l pl-4 text-mono-code border-secondary">

0 commit comments

Comments
 (0)