From f9d28b0f8b9c02658f14392d9537ae08a9af55fe Mon Sep 17 00:00:00 2001 From: Kira Pilot Date: Tue, 7 Feb 2023 14:56:53 +0000 Subject: [PATCH 1/9] changed bbuild string --- .../AuditLogRow/AuditLogDescription.test.tsx | 6 +-- .../AuditLogRow/AuditLogDescription.tsx | 54 +++++++++++++++---- site/src/i18n/en/auditLog.json | 5 +- 3 files changed, 49 insertions(+), 16 deletions(-) diff --git a/site/src/components/AuditLogRow/AuditLogDescription.test.tsx b/site/src/components/AuditLogRow/AuditLogDescription.test.tsx index ad618c4b1b8d3..bf8590a588162 100644 --- a/site/src/components/AuditLogRow/AuditLogDescription.test.tsx +++ b/site/src/components/AuditLogRow/AuditLogDescription.test.tsx @@ -33,9 +33,7 @@ describe("AuditLogDescription", () => { it("renders the correct string for a workspace_build stop audit log", async () => { render() - expect( - getByTextContent("TestUser stopped build for workspace test2"), - ).toBeDefined() + expect(getByTextContent("TestUser stopped workspace test2")).toBeDefined() }) it("renders the correct string for a workspace_build audit log with a duplicate word", async () => { @@ -48,7 +46,7 @@ describe("AuditLogDescription", () => { render() expect( - getByTextContent("TestUser stopped build for workspace workspace"), + getByTextContent("TestUser stopped workspace workspace"), ).toBeDefined() }) it("renders the correct string for a workspace created for a different owner", async () => { diff --git a/site/src/components/AuditLogRow/AuditLogDescription.tsx b/site/src/components/AuditLogRow/AuditLogDescription.tsx index a17f88073f95a..303e5c769e8e0 100644 --- a/site/src/components/AuditLogRow/AuditLogDescription.tsx +++ b/site/src/components/AuditLogRow/AuditLogDescription.tsx @@ -5,26 +5,56 @@ import Link from "@material-ui/core/Link" import { makeStyles } from "@material-ui/core/styles" import i18next from "i18next" +const BuildAuditDescription: FC<{ auditLog: AuditLog }> = ({ + auditLog, +}): JSX.Element => { + const { t } = i18next + + // audit logs with a resource_type of workspace build use workspace name as a target + const target = auditLog.additional_fields?.workspace_name?.trim() + // workspaces can be started/stopped by a user, or kicked off automatically by Coder + const user = + auditLog.additional_fields?.build_reason && + auditLog.additional_fields?.build_reason !== "initiator" + ? t("auditLog:table.logRow.buildReason") + : auditLog.user?.username.trim() + + const actionVerb = + auditLog.action === "start" + ? t("auditLog:table.logRow.started") + : t("auditLog:table.logRow.stopped") + + return ( + + <> + {user}{" "} + {auditLog.resource_link ? ( + + {actionVerb} + + ) : ( + { actionVerb } + )}{" "} + {t("auditLog:table.logRow.workspace")} + {target} + + + ) +} + export const AuditLogDescription: FC<{ auditLog: AuditLog }> = ({ auditLog, }): JSX.Element => { const classes = useStyles() const { t } = i18next - let target = auditLog.resource_target.trim() - let user = auditLog.user + const target = auditLog.resource_target.trim() + const user = auditLog.user ? auditLog.user.username.trim() : t("auditLog:table.logRow.unknownUser") if (auditLog.resource_type === "workspace_build") { - // audit logs with a resource_type of workspace build use workspace name as a target - target = auditLog.additional_fields?.workspace_name?.trim() - // workspaces can be started/stopped by a user, or kicked off automatically by Coder - user = - auditLog.additional_fields?.build_reason && - auditLog.additional_fields?.build_reason !== "initiator" - ? t("auditLog:table.logRow.buildReason") - : user + return } // SSH key entries have no links @@ -59,7 +89,9 @@ export const AuditLogDescription: FC<{ auditLog: AuditLog }> = ({ )} {/* logs for workspaces created on behalf of other users indicate ownership in the description */} {auditLog.additional_fields.workspace_owner && - auditLog.additional_fields.workspace_owner !== "unknown" && ( + auditLog.additional_fields.workspace_owner !== "unknown" && + auditLog.additional_fields.workspace_owner !== + auditLog.user?.username && ( <> {t("auditLog:table.logRow.onBehalfOf", { diff --git a/site/src/i18n/en/auditLog.json b/site/src/i18n/en/auditLog.json index 2a712f34b1c72..5ca5775462257 100644 --- a/site/src/i18n/en/auditLog.json +++ b/site/src/i18n/en/auditLog.json @@ -14,7 +14,10 @@ "browser": "Browser: ", "onBehalfOf": " on behalf of {{owner}}", "buildReason": "Coder automatically", - "unknownUser": "an unknown user" + "unknownUser": "an unknown user", + "started": "started", + "stopped": "stopped", + "workspace": " workspace " } }, "paywall": { From 7f71c2a6fc5cd867f610e1cd9959aed1b3d3978e Mon Sep 17 00:00:00 2001 From: Kira Pilot Date: Tue, 7 Feb 2023 14:57:05 +0000 Subject: [PATCH 2/9] clean up friendly string --- coderd/audit.go | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/coderd/audit.go b/coderd/audit.go index f26d7ba1104d8..a101047cae7c0 100644 --- a/coderd/audit.go +++ b/coderd/audit.go @@ -270,19 +270,6 @@ func auditLogDescription(alog database.GetAuditLogsOffsetRow, additionalFields a return str } - // Strings for starting/stopping workspace builds follow the below format: - // "{user | 'Coder automatically'} started build #{build_number} for workspace {target}" - // where target is a workspace (name) instead of a workspace build - // passed in on the FE via AuditLog.AdditionalFields rather than derived in request.go:35 - if alog.ResourceType == database.ResourceTypeWorkspaceBuild && alog.Action != database.AuditActionDelete { - if len(additionalFields.BuildNumber) == 0 { - str += " build for" - } else { - str += fmt.Sprintf(" build #%s for", - additionalFields.BuildNumber) - } - } - // We don't display the name (target) for git ssh keys. It's fairly long and doesn't // make too much sense to display. if alog.ResourceType == database.ResourceTypeGitSshKey { From 0125d4f457f2bd8813eaa673cedadeebe570eedd Mon Sep 17 00:00:00 2001 From: Kira Pilot Date: Tue, 7 Feb 2023 16:49:55 +0000 Subject: [PATCH 3/9] using Trans component --- .../AuditLogRow/AuditLogDescription.tsx | 63 +++++++------------ .../AuditLogRow/BuildAuditDescription.tsx | 38 +++++++++++ site/src/i18n/en/auditLog.json | 6 +- 3 files changed, 61 insertions(+), 46 deletions(-) create mode 100644 site/src/components/AuditLogRow/BuildAuditDescription.tsx diff --git a/site/src/components/AuditLogRow/AuditLogDescription.tsx b/site/src/components/AuditLogRow/AuditLogDescription.tsx index 303e5c769e8e0..351ddc4c7f439 100644 --- a/site/src/components/AuditLogRow/AuditLogDescription.tsx +++ b/site/src/components/AuditLogRow/AuditLogDescription.tsx @@ -3,55 +3,19 @@ import { AuditLog } from "api/typesGenerated" import { Link as RouterLink } from "react-router-dom" import Link from "@material-ui/core/Link" import { makeStyles } from "@material-ui/core/styles" -import i18next from "i18next" - -const BuildAuditDescription: FC<{ auditLog: AuditLog }> = ({ - auditLog, -}): JSX.Element => { - const { t } = i18next - - // audit logs with a resource_type of workspace build use workspace name as a target - const target = auditLog.additional_fields?.workspace_name?.trim() - // workspaces can be started/stopped by a user, or kicked off automatically by Coder - const user = - auditLog.additional_fields?.build_reason && - auditLog.additional_fields?.build_reason !== "initiator" - ? t("auditLog:table.logRow.buildReason") - : auditLog.user?.username.trim() - - const actionVerb = - auditLog.action === "start" - ? t("auditLog:table.logRow.started") - : t("auditLog:table.logRow.stopped") - - return ( - - <> - {user}{" "} - {auditLog.resource_link ? ( - - {actionVerb} - - ) : ( - { actionVerb } - )}{" "} - {t("auditLog:table.logRow.workspace")} - {target} - - - ) -} +import { useTranslation } from "react-i18next" +import { BuildAuditDescription } from "./BuildAuditDescription" export const AuditLogDescription: FC<{ auditLog: AuditLog }> = ({ auditLog, }): JSX.Element => { const classes = useStyles() - const { t } = i18next + const { t } = useTranslation("auditLog") const target = auditLog.resource_target.trim() const user = auditLog.user ? auditLog.user.username.trim() - : t("auditLog:table.logRow.unknownUser") + : t("table.logRow.unknownUser") if (auditLog.resource_type === "workspace_build") { return @@ -72,6 +36,21 @@ export const AuditLogDescription: FC<{ auditLog: AuditLog }> = ({ .replace("{user}", `${user}`) .replace("{target}", "") + // return ( + // + // + // {"{{truncatedDescription}}"} + // + // {"{{target}}"} + // + // + // + // ) + return ( {truncatedDescription} @@ -84,7 +63,7 @@ export const AuditLogDescription: FC<{ auditLog: AuditLog }> = ({ )} {auditLog.is_deleted && ( - <>{t("auditLog:table.logRow.deletedLabel")} + <>{t("table.logRow.deletedLabel")} )} {/* logs for workspaces created on behalf of other users indicate ownership in the description */} @@ -94,7 +73,7 @@ export const AuditLogDescription: FC<{ auditLog: AuditLog }> = ({ auditLog.user?.username && ( <> - {t("auditLog:table.logRow.onBehalfOf", { + {t("table.logRow.onBehalfOf", { owner: auditLog.additional_fields.workspace_owner, })} diff --git a/site/src/components/AuditLogRow/BuildAuditDescription.tsx b/site/src/components/AuditLogRow/BuildAuditDescription.tsx new file mode 100644 index 0000000000000..f52cb16ae6d26 --- /dev/null +++ b/site/src/components/AuditLogRow/BuildAuditDescription.tsx @@ -0,0 +1,38 @@ +import { Trans, useTranslation } from "react-i18next" +import { AuditLog } from "api/typesGenerated" +import { FC } from "react" +import { Link as RouterLink } from "react-router-dom" +import Link from "@material-ui/core/Link" + +export const BuildAuditDescription: FC<{ auditLog: AuditLog }> = ({ + auditLog, +}): JSX.Element => { + const { t } = useTranslation("auditLog") + + // audit logs with a resource_type of workspace build use workspace name as a target + const workspaceName = auditLog.additional_fields?.workspace_name?.trim() + // workspaces can be started/stopped by a user, or kicked off automatically by Coder + const user = + auditLog.additional_fields?.build_reason && + auditLog.additional_fields?.build_reason !== "initiator" + ? "Coder automatically" + : auditLog.user?.username.trim() + + const action = auditLog.action === "start" ? "started" : "stopped" + + return ( + + + {"{{user}}"} + + {"{{action}}"} + + workspace{"{{workspaceName}}"} + + + ) +} diff --git a/site/src/i18n/en/auditLog.json b/site/src/i18n/en/auditLog.json index 5ca5775462257..cb5213fa8294d 100644 --- a/site/src/i18n/en/auditLog.json +++ b/site/src/i18n/en/auditLog.json @@ -13,11 +13,9 @@ "os": "OS: ", "browser": "Browser: ", "onBehalfOf": " on behalf of {{owner}}", - "buildReason": "Coder automatically", "unknownUser": "an unknown user", - "started": "started", - "stopped": "stopped", - "workspace": " workspace " + "workspaceBuild": "{{user}} <1>{{action}} workspace {{workspaceName}}", + "auditDescription": "{{truncatedDescription}} <1>{{target}}" } }, "paywall": { From c18235724e9520b67c88807751aa7d40bf24de29 Mon Sep 17 00:00:00 2001 From: Kira Pilot Date: Tue, 7 Feb 2023 20:04:19 +0000 Subject: [PATCH 4/9] general cleanup --- .../AuditLogRow/AuditLogDescription.tsx | 91 ------------------- .../AuditLogDescription.test.tsx | 4 +- .../AuditLogDescription.tsx | 67 ++++++++++++++ .../BuildAuditDescription.tsx | 26 ++++-- .../AuditLogRow/AuditLogDescription/index.ts | 1 + .../{ => AuditLogDiff}/AuditLogDiff.tsx | 0 .../{ => AuditLogDiff}/auditUtils.test.ts | 0 .../{ => AuditLogDiff}/auditUtils.ts | 0 .../AuditLogRow/AuditLogDiff/index.ts | 2 + .../components/AuditLogRow/AuditLogRow.tsx | 27 ++++-- site/src/i18n/en/auditLog.json | 12 ++- 11 files changed, 117 insertions(+), 113 deletions(-) delete mode 100644 site/src/components/AuditLogRow/AuditLogDescription.tsx rename site/src/components/AuditLogRow/{ => AuditLogDescription}/AuditLogDescription.test.tsx (96%) create mode 100644 site/src/components/AuditLogRow/AuditLogDescription/AuditLogDescription.tsx rename site/src/components/AuditLogRow/{ => AuditLogDescription}/BuildAuditDescription.tsx (63%) create mode 100644 site/src/components/AuditLogRow/AuditLogDescription/index.ts rename site/src/components/AuditLogRow/{ => AuditLogDiff}/AuditLogDiff.tsx (100%) rename site/src/components/AuditLogRow/{ => AuditLogDiff}/auditUtils.test.ts (100%) rename site/src/components/AuditLogRow/{ => AuditLogDiff}/auditUtils.ts (100%) create mode 100644 site/src/components/AuditLogRow/AuditLogDiff/index.ts diff --git a/site/src/components/AuditLogRow/AuditLogDescription.tsx b/site/src/components/AuditLogRow/AuditLogDescription.tsx deleted file mode 100644 index 351ddc4c7f439..0000000000000 --- a/site/src/components/AuditLogRow/AuditLogDescription.tsx +++ /dev/null @@ -1,91 +0,0 @@ -import { FC } from "react" -import { AuditLog } from "api/typesGenerated" -import { Link as RouterLink } from "react-router-dom" -import Link from "@material-ui/core/Link" -import { makeStyles } from "@material-ui/core/styles" -import { useTranslation } from "react-i18next" -import { BuildAuditDescription } from "./BuildAuditDescription" - -export const AuditLogDescription: FC<{ auditLog: AuditLog }> = ({ - auditLog, -}): JSX.Element => { - const classes = useStyles() - const { t } = useTranslation("auditLog") - - const target = auditLog.resource_target.trim() - const user = auditLog.user - ? auditLog.user.username.trim() - : t("table.logRow.unknownUser") - - if (auditLog.resource_type === "workspace_build") { - return - } - - // SSH key entries have no links - if (auditLog.resource_type === "git_ssh_key") { - return ( - - {auditLog.description - .replace("{user}", `${auditLog.user?.username.trim()}`) - .replace("{target}", `${target}`)} - - ) - } - - const truncatedDescription = auditLog.description - .replace("{user}", `${user}`) - .replace("{target}", "") - - // return ( - // - // - // {"{{truncatedDescription}}"} - // - // {"{{target}}"} - // - // - // - // ) - - return ( - - {truncatedDescription} - {auditLog.resource_link ? ( - - {target} - - ) : ( - {target} - )} - {auditLog.is_deleted && ( - - <>{t("table.logRow.deletedLabel")} - - )} - {/* logs for workspaces created on behalf of other users indicate ownership in the description */} - {auditLog.additional_fields.workspace_owner && - auditLog.additional_fields.workspace_owner !== "unknown" && - auditLog.additional_fields.workspace_owner !== - auditLog.user?.username && ( - - <> - {t("table.logRow.onBehalfOf", { - owner: auditLog.additional_fields.workspace_owner, - })} - - - )} - - ) -} - -const useStyles = makeStyles((theme) => ({ - deletedLabel: { - ...theme.typography.caption, - color: theme.palette.text.secondary, - }, -})) diff --git a/site/src/components/AuditLogRow/AuditLogDescription.test.tsx b/site/src/components/AuditLogRow/AuditLogDescription/AuditLogDescription.test.tsx similarity index 96% rename from site/src/components/AuditLogRow/AuditLogDescription.test.tsx rename to site/src/components/AuditLogRow/AuditLogDescription/AuditLogDescription.test.tsx index bf8590a588162..fe9e3da4b579e 100644 --- a/site/src/components/AuditLogRow/AuditLogDescription.test.tsx +++ b/site/src/components/AuditLogRow/AuditLogDescription/AuditLogDescription.test.tsx @@ -7,8 +7,8 @@ import { MockAuditLogUnsuccessfulLoginUnknownUser, } from "testHelpers/entities" import { AuditLogDescription } from "./AuditLogDescription" -import { AuditLogRow } from "./AuditLogRow" -import { render } from "../../testHelpers/renderHelpers" +import { AuditLogRow } from "../AuditLogRow" +import { render } from "../../../testHelpers/renderHelpers" import { screen } from "@testing-library/react" const getByTextContent = (text: string) => { diff --git a/site/src/components/AuditLogRow/AuditLogDescription/AuditLogDescription.tsx b/site/src/components/AuditLogRow/AuditLogDescription/AuditLogDescription.tsx new file mode 100644 index 0000000000000..8b2d15e2f3ca2 --- /dev/null +++ b/site/src/components/AuditLogRow/AuditLogDescription/AuditLogDescription.tsx @@ -0,0 +1,67 @@ +import { FC } from "react" +import { AuditLog } from "api/typesGenerated" +import { Link as RouterLink } from "react-router-dom" +import Link from "@material-ui/core/Link" +import { Trans, useTranslation } from "react-i18next" +import { BuildAuditDescription } from "./BuildAuditDescription" + +export const AuditLogDescription: FC<{ auditLog: AuditLog }> = ({ + auditLog, +}): JSX.Element => { + const { t } = useTranslation("auditLog") + + let target = auditLog.resource_target.trim() + const user = auditLog.user ? auditLog.user.username.trim() : "an unknown user" + + if (auditLog.resource_type === "workspace_build") { + return + } + + // SSH key entries have no links + if (auditLog.resource_type === "git_ssh_key") { + target = "" + } + + const truncatedDescription = auditLog.description + .replace("{user}", `${user}`) + .replace("{target}", "") + + // logs for workspaces created on behalf of other users indicate ownership in the description + const onBehalfOf = + auditLog.additional_fields.workspace_owner && + auditLog.additional_fields.workspace_owner !== "unknown" && + auditLog.additional_fields.workspace_owner !== auditLog.user?.username && + `on behalf of ${auditLog.additional_fields.workspace_owner}` + + if (auditLog.resource_link) { + return ( + + + {"{{truncatedDescription}}"} + + {"{{target}}"} + + {"{{onBehalfOf}}"} + + + ) + } + + return ( + + + {"{{truncatedDescription}}"} + {"{{target}}"} + {"{{onBehalfOf}}"} + + + ) +} diff --git a/site/src/components/AuditLogRow/BuildAuditDescription.tsx b/site/src/components/AuditLogRow/AuditLogDescription/BuildAuditDescription.tsx similarity index 63% rename from site/src/components/AuditLogRow/BuildAuditDescription.tsx rename to site/src/components/AuditLogRow/AuditLogDescription/BuildAuditDescription.tsx index f52cb16ae6d26..0bce7f981ca71 100644 --- a/site/src/components/AuditLogRow/BuildAuditDescription.tsx +++ b/site/src/components/AuditLogRow/AuditLogDescription/BuildAuditDescription.tsx @@ -9,7 +9,6 @@ export const BuildAuditDescription: FC<{ auditLog: AuditLog }> = ({ }): JSX.Element => { const { t } = useTranslation("auditLog") - // audit logs with a resource_type of workspace build use workspace name as a target const workspaceName = auditLog.additional_fields?.workspace_name?.trim() // workspaces can be started/stopped by a user, or kicked off automatically by Coder const user = @@ -20,18 +19,33 @@ export const BuildAuditDescription: FC<{ auditLog: AuditLog }> = ({ const action = auditLog.action === "start" ? "started" : "stopped" + if (auditLog.resource_link) { + return ( + + + {"{{user}}"} + + {"{{action}}"} + + workspace{"{{workspaceName}}"} + + + ) + } + return ( {"{{user}}"} - - {"{{action}}"} - - workspace{"{{workspaceName}}"} + {"{{action}}"}workspace{"{{workspaceName}}"} ) diff --git a/site/src/components/AuditLogRow/AuditLogDescription/index.ts b/site/src/components/AuditLogRow/AuditLogDescription/index.ts new file mode 100644 index 0000000000000..46e989d1d5c39 --- /dev/null +++ b/site/src/components/AuditLogRow/AuditLogDescription/index.ts @@ -0,0 +1 @@ +export {AuditLogDescription} from "./AuditLogDescription" diff --git a/site/src/components/AuditLogRow/AuditLogDiff.tsx b/site/src/components/AuditLogRow/AuditLogDiff/AuditLogDiff.tsx similarity index 100% rename from site/src/components/AuditLogRow/AuditLogDiff.tsx rename to site/src/components/AuditLogRow/AuditLogDiff/AuditLogDiff.tsx diff --git a/site/src/components/AuditLogRow/auditUtils.test.ts b/site/src/components/AuditLogRow/AuditLogDiff/auditUtils.test.ts similarity index 100% rename from site/src/components/AuditLogRow/auditUtils.test.ts rename to site/src/components/AuditLogRow/AuditLogDiff/auditUtils.test.ts diff --git a/site/src/components/AuditLogRow/auditUtils.ts b/site/src/components/AuditLogRow/AuditLogDiff/auditUtils.ts similarity index 100% rename from site/src/components/AuditLogRow/auditUtils.ts rename to site/src/components/AuditLogRow/AuditLogDiff/auditUtils.ts diff --git a/site/src/components/AuditLogRow/AuditLogDiff/index.ts b/site/src/components/AuditLogRow/AuditLogDiff/index.ts new file mode 100644 index 0000000000000..14f6c3535b692 --- /dev/null +++ b/site/src/components/AuditLogRow/AuditLogDiff/index.ts @@ -0,0 +1,2 @@ +export {AuditLogDiff} from './AuditLogDiff' +export {determineGroupDiff} from './auditUtils' diff --git a/site/src/components/AuditLogRow/AuditLogRow.tsx b/site/src/components/AuditLogRow/AuditLogRow.tsx index 4813932a17351..a573885d492a3 100644 --- a/site/src/components/AuditLogRow/AuditLogRow.tsx +++ b/site/src/components/AuditLogRow/AuditLogRow.tsx @@ -13,10 +13,9 @@ import { UserAvatar } from "components/UserAvatar/UserAvatar" import { useState } from "react" import { PaletteIndex } from "theme/palettes" import userAgentParser from "ua-parser-js" -import { AuditLogDiff } from "./AuditLogDiff" -import i18next from "i18next" +import { AuditLogDiff, determineGroupDiff } from "./AuditLogDiff" +import { useTranslation } from "react-i18next" import { AuditLogDescription } from "./AuditLogDescription" -import { determineGroupDiff } from "./auditUtils" const httpStatusColor = (httpStatus: number): PaletteIndex => { if (httpStatus >= 300 && httpStatus < 500) { @@ -41,14 +40,14 @@ export const AuditLogRow: React.FC = ({ defaultIsDiffOpen = false, }) => { const styles = useStyles() - const { t } = i18next + const { t } = useTranslation("auditLog") const [isDiffOpen, setIsDiffOpen] = useState(defaultIsDiffOpen) const diffs = Object.entries(auditLog.diff) const shouldDisplayDiff = diffs.length > 0 const { os, browser } = userAgentParser(auditLog.user_agent) const displayBrowserInfo = browser.name ? `${browser.name} ${browser.version}` - : t("auditLog:table.logRow.notAvailable") + : t("table.logRow.notAvailable") let auditDiff = auditLog.diff @@ -110,6 +109,11 @@ export const AuditLogRow: React.FC = ({ spacing={1} > + {auditLog.is_deleted && ( + + <>{t("table.logRow.deletedLabel")} + + )} {new Date(auditLog.time).toLocaleTimeString()} @@ -119,23 +123,23 @@ export const AuditLogRow: React.FC = ({ {auditLog.ip && ( - <>{t("auditLog:table.logRow.ip")} + <>{t("table.logRow.ip")} {auditLog.ip} )} - <>{t("auditLog:table.logRow.os")} + <>{t("table.logRow.os")} {os.name ? os.name : // https://github.com/i18next/next-i18next/issues/1795 - t("auditLog:table.logRow.notAvailable")} + t("table.logRow.notAvailable")} - <>{t("auditLog:table.logRow.browser")} + <>{t("table.logRow.browser")} {displayBrowserInfo} @@ -215,4 +219,9 @@ const useStyles = makeStyles((theme) => ({ paddingRight: 10, fontWeight: 600, }, + + deletedLabel: { + ...theme.typography.caption, + color: theme.palette.text.secondary, + }, })) diff --git a/site/src/i18n/en/auditLog.json b/site/src/i18n/en/auditLog.json index cb5213fa8294d..20bbf49eedbc1 100644 --- a/site/src/i18n/en/auditLog.json +++ b/site/src/i18n/en/auditLog.json @@ -8,14 +8,16 @@ "emptyPage": "No audit logs available on this page", "noLogs": "No audit logs available", "logRow": { + "description": { + "linkedWorkspaceBuild": "{{user}} <1>{{action}} workspace {{workspaceName}}", + "unlinkedWorkspaceBuild": "{{user}} {{action}} workspace {{workspaceName}}", + "linkedAuditDescription": "{{truncatedDescription}} <1>{{target}} {{onBehalfOf}}", + "unlinkedAuditDescription": "{{truncatedDescription}} {{target}} {{onBehalfOf}}" + }, "deletedLabel": " (deleted)", "ip": "IP: ", "os": "OS: ", - "browser": "Browser: ", - "onBehalfOf": " on behalf of {{owner}}", - "unknownUser": "an unknown user", - "workspaceBuild": "{{user}} <1>{{action}} workspace {{workspaceName}}", - "auditDescription": "{{truncatedDescription}} <1>{{target}}" + "browser": "Browser: " } }, "paywall": { From 7ca2a8b25bf596863971e98dd92647b6d2ef98a3 Mon Sep 17 00:00:00 2001 From: Kira Pilot Date: Tue, 7 Feb 2023 21:53:31 +0000 Subject: [PATCH 5/9] fixed tests --- site/package.json | 2 +- .../AuditLogDescription.test.tsx | 53 +++++++++++++++---- .../AuditLogRow/AuditLogDescription/index.ts | 2 +- .../AuditLogRow/AuditLogDiff/index.ts | 4 +- site/yarn.lock | 31 ++++++++--- 5 files changed, 72 insertions(+), 20 deletions(-) diff --git a/site/package.json b/site/package.json index c4ed02b1e4dfa..afd06708b147d 100644 --- a/site/package.json +++ b/site/package.json @@ -45,6 +45,7 @@ "@xstate/inspect": "0.6.5", "@xstate/react": "3.0.1", "axios": "0.26.1", + "canvas": "^2.11.0", "chart.js": "3.9.1", "chartjs-adapter-date-fns": "3.0.0", "color-convert": "2.0.1", @@ -107,7 +108,6 @@ "@typescript-eslint/eslint-plugin": "5.50.0", "@typescript-eslint/parser": "5.45.1", "@xstate/cli": "0.3.0", - "canvas": "2.10.0", "chromatic": "6.15.0", "eslint": "8.33.0", "eslint-config-prettier": "8.5.0", diff --git a/site/src/components/AuditLogRow/AuditLogDescription/AuditLogDescription.test.tsx b/site/src/components/AuditLogRow/AuditLogDescription/AuditLogDescription.test.tsx index fe9e3da4b579e..7b4fc82096ef0 100644 --- a/site/src/components/AuditLogRow/AuditLogDescription/AuditLogDescription.test.tsx +++ b/site/src/components/AuditLogRow/AuditLogDescription/AuditLogDescription.test.tsx @@ -8,8 +8,12 @@ import { } from "testHelpers/entities" import { AuditLogDescription } from "./AuditLogDescription" import { AuditLogRow } from "../AuditLogRow" -import { render } from "../../../testHelpers/renderHelpers" +import { render } from "testHelpers/renderHelpers" import { screen } from "@testing-library/react" +import { i18n } from "i18n" + +const t = (str: string, variables?: Record) => + i18n.t(str, variables) const getByTextContent = (text: string) => { return screen.getByText((_, element) => { @@ -25,9 +29,8 @@ describe("AuditLogDescription", () => { it("renders the correct string for a workspace create audit log", async () => { render() - expect( - getByTextContent("TestUser created workspace bruno-dev"), - ).toBeDefined() + expect(screen.getByText("TestUser created workspace")).toBeDefined() + expect(screen.getByText("bruno-dev")).toBeDefined() }) it("renders the correct string for a workspace_build stop audit log", async () => { @@ -55,27 +58,59 @@ describe("AuditLogDescription", () => { auditLog={MockWorkspaceCreateAuditLogForDifferentOwner} />, ) + expect( - getByTextContent( - `TestUser created workspace bruno-dev on behalf of ${MockWorkspaceCreateAuditLogForDifferentOwner.additional_fields.workspace_owner}`, + screen.getByText( + `on behalf of ${MockWorkspaceCreateAuditLogForDifferentOwner.additional_fields.workspace_owner}`, + { exact: false }, ), ).toBeDefined() }) it("renders the correct string for successful login", async () => { render() - expect(getByTextContent(`TestUser logged in`)).toBeDefined() + + expect( + screen.getByText( + t("auditLog:table.logRow.description.unlinkedAuditDescription", { + truncatedDescription: `${MockAuditLogSuccessfulLogin.user?.username} logged in`, + target: "", + onBehalfOf: undefined, + }).trim(), + ), + ).toBeInTheDocument() + const statusPill = screen.getByRole("status") expect(statusPill).toHaveTextContent("201") }) it("renders the correct string for unsuccessful login for a known user", async () => { render() - expect(getByTextContent(`TestUser logged in`)).toBeDefined() + + expect( + screen.getByText( + t("auditLog:table.logRow.description.unlinkedAuditDescription", { + truncatedDescription: `${MockAuditLogUnsuccessfulLoginKnownUser.user?.username} logged in`, + target: "", + onBehalfOf: undefined, + }).trim(), + ), + ).toBeInTheDocument() + const statusPill = screen.getByRole("status") expect(statusPill).toHaveTextContent("401") }) it("renders the correct string for unsuccessful login for an unknown user", async () => { render() - expect(getByTextContent(`an unknown user logged in`)).toBeDefined() + + expect( + screen.getByText( + t("auditLog:table.logRow.description.unlinkedAuditDescription", { + truncatedDescription: "an unknown user logged in", + target: "", + onBehalfOf: undefined, + }).trim(), + ), + ).toBeInTheDocument() + const statusPill = screen.getByRole("status") expect(statusPill).toHaveTextContent("401") }) diff --git a/site/src/components/AuditLogRow/AuditLogDescription/index.ts b/site/src/components/AuditLogRow/AuditLogDescription/index.ts index 46e989d1d5c39..590ff7c7b0e17 100644 --- a/site/src/components/AuditLogRow/AuditLogDescription/index.ts +++ b/site/src/components/AuditLogRow/AuditLogDescription/index.ts @@ -1 +1 @@ -export {AuditLogDescription} from "./AuditLogDescription" +export { AuditLogDescription } from "./AuditLogDescription" diff --git a/site/src/components/AuditLogRow/AuditLogDiff/index.ts b/site/src/components/AuditLogRow/AuditLogDiff/index.ts index 14f6c3535b692..7243291182ca9 100644 --- a/site/src/components/AuditLogRow/AuditLogDiff/index.ts +++ b/site/src/components/AuditLogRow/AuditLogDiff/index.ts @@ -1,2 +1,2 @@ -export {AuditLogDiff} from './AuditLogDiff' -export {determineGroupDiff} from './auditUtils' +export { AuditLogDiff } from "./AuditLogDiff" +export { determineGroupDiff } from "./auditUtils" diff --git a/site/yarn.lock b/site/yarn.lock index 783cabe62d606..8ee68f45af1db 100644 --- a/site/yarn.lock +++ b/site/yarn.lock @@ -5165,13 +5165,13 @@ caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001304, caniuse-lite@^1.0.300014 resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001431.tgz#e7c59bd1bc518fae03a4656be442ce6c4887a795" integrity sha512-zBUoFU0ZcxpvSt9IU66dXVT/3ctO1cy4y9cscs1szkPlcWb6pasYM144GqrUygUbT+k7cmUCW61cvskjcv0enQ== -canvas@2.10.0: - version "2.10.0" - resolved "https://registry.yarnpkg.com/canvas/-/canvas-2.10.0.tgz#5f48c8d1ff86c96356809097020336c3a1ccce27" - integrity sha512-A0RPxLcH0pPKAY2VN243LdCNcOJXAT8n7nJnN7TZMGv9OuF8we9wfpWgVT/eFMzi+cDYf/384w4BpfjGCD9aKQ== +canvas@^2.11.0: + version "2.11.0" + resolved "https://registry.yarnpkg.com/canvas/-/canvas-2.11.0.tgz#7f0c3e9ae94cf469269b5d3a7963a7f3a9936434" + integrity sha512-bdTjFexjKJEwtIo0oRx8eD4G2yWoUOXP9lj279jmQ2zMnTQhT8C3512OKz3s+ZOaQlLbE7TuVvRDYDB3Llyy5g== dependencies: "@mapbox/node-pre-gyp" "^1.0.0" - nan "^2.15.0" + nan "^2.17.0" simple-get "^3.0.3" capture-exit@^2.0.0: @@ -10783,6 +10783,11 @@ minipass@^3.0.0, minipass@^3.1.1: dependencies: yallist "^4.0.0" +minipass@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-4.0.2.tgz#26fc3364d5ea6cb971c6e5259eac67a0887510d1" + integrity sha512-4Hbzei7ZyBp+1aw0874YWpKOubZd/jc53/XU+gkYry1QV+VvrbO8icLM5CUtm4F0hyXn85DXYKEMIS26gitD3A== + minizlib@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" @@ -10907,7 +10912,7 @@ mute-stream@0.0.8: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== -nan@^2.12.1, nan@^2.15.0: +nan@^2.12.1, nan@^2.17.0: version "2.17.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.17.0.tgz#c0150a2368a182f033e9aa5195ec76ea41a199cb" integrity sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ== @@ -13669,7 +13674,7 @@ tar-stream@^2.0.1: inherits "^2.0.3" readable-stream "^3.1.1" -tar@^6.0.2, tar@^6.1.11: +tar@^6.0.2: version "6.1.12" resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.12.tgz#3b742fb05669b55671fb769ab67a7791ea1a62e6" integrity sha512-jU4TdemS31uABHd+Lt5WEYJuzn+TJTCBLljvIAHZOz6M9Os5pJ4dD+vRFLxPa/n3T0iEFzpi+0x1UfuDZYbRMw== @@ -13681,6 +13686,18 @@ tar@^6.0.2, tar@^6.1.11: mkdirp "^1.0.3" yallist "^4.0.0" +tar@^6.1.11: + version "6.1.13" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.13.tgz#46e22529000f612180601a6fe0680e7da508847b" + integrity sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw== + dependencies: + chownr "^2.0.0" + fs-minipass "^2.0.0" + minipass "^4.0.0" + minizlib "^2.1.1" + mkdirp "^1.0.3" + yallist "^4.0.0" + telejson@^6.0.8: version "6.0.8" resolved "https://registry.yarnpkg.com/telejson/-/telejson-6.0.8.tgz#1c432db7e7a9212c1fbd941c3e5174ec385148f7" From 57c15299b930bdcf86db2cee5bd54c905fa4861c Mon Sep 17 00:00:00 2001 From: Kira Pilot Date: Tue, 7 Feb 2023 22:08:44 +0000 Subject: [PATCH 6/9] fix lint --- coderd/audit.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/coderd/audit.go b/coderd/audit.go index a101047cae7c0..601590b531c31 100644 --- a/coderd/audit.go +++ b/coderd/audit.go @@ -253,13 +253,13 @@ func (api *API) convertAuditLog(ctx context.Context, dblog database.GetAuditLogs StatusCode: dblog.StatusCode, AdditionalFields: dblog.AdditionalFields, User: user, - Description: auditLogDescription(dblog, additionalFields), + Description: auditLogDescription(dblog), ResourceLink: resourceLink, IsDeleted: isDeleted, } } -func auditLogDescription(alog database.GetAuditLogsOffsetRow, additionalFields audit.AdditionalFields) string { +func auditLogDescription(alog database.GetAuditLogsOffsetRow) string { str := fmt.Sprintf("{user} %s", codersdk.AuditAction(alog.Action).Friendly(), ) From d0ba8eb24e40c3af9ac8f044032bc50d57402eac Mon Sep 17 00:00:00 2001 From: Kira Pilot Date: Tue, 7 Feb 2023 22:16:29 +0000 Subject: [PATCH 7/9] fixing bolding --- .../AuditLogRow/AuditLogDescription/AuditLogDescription.tsx | 5 +++-- site/src/i18n/en/auditLog.json | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/site/src/components/AuditLogRow/AuditLogDescription/AuditLogDescription.tsx b/site/src/components/AuditLogRow/AuditLogDescription/AuditLogDescription.tsx index 8b2d15e2f3ca2..129e48094165e 100644 --- a/site/src/components/AuditLogRow/AuditLogDescription/AuditLogDescription.tsx +++ b/site/src/components/AuditLogRow/AuditLogDescription/AuditLogDescription.tsx @@ -30,8 +30,9 @@ export const AuditLogDescription: FC<{ auditLog: AuditLog }> = ({ const onBehalfOf = auditLog.additional_fields.workspace_owner && auditLog.additional_fields.workspace_owner !== "unknown" && - auditLog.additional_fields.workspace_owner !== auditLog.user?.username && - `on behalf of ${auditLog.additional_fields.workspace_owner}` + auditLog.additional_fields.workspace_owner !== auditLog.user?.username + ? `on behalf of ${auditLog.additional_fields.workspace_owner}` + : "" if (auditLog.resource_link) { return ( diff --git a/site/src/i18n/en/auditLog.json b/site/src/i18n/en/auditLog.json index 20bbf49eedbc1..4ae30cf553e64 100644 --- a/site/src/i18n/en/auditLog.json +++ b/site/src/i18n/en/auditLog.json @@ -11,8 +11,8 @@ "description": { "linkedWorkspaceBuild": "{{user}} <1>{{action}} workspace {{workspaceName}}", "unlinkedWorkspaceBuild": "{{user}} {{action}} workspace {{workspaceName}}", - "linkedAuditDescription": "{{truncatedDescription}} <1>{{target}} {{onBehalfOf}}", - "unlinkedAuditDescription": "{{truncatedDescription}} {{target}} {{onBehalfOf}}" + "linkedAuditDescription": "{{truncatedDescription}} <1>{{target}} {{onBehalfOf}}", + "unlinkedAuditDescription": "{{truncatedDescription}} {{target}} {{onBehalfOf}}" }, "deletedLabel": " (deleted)", "ip": "IP: ", From 3a34e5e2d1e8bab8fcb467b1c094902a003a3dfa Mon Sep 17 00:00:00 2001 From: Kira Pilot Date: Tue, 7 Feb 2023 22:47:50 +0000 Subject: [PATCH 8/9] removing dead strings in auditLogRow --- .../components/AuditLogRow/AuditLogRow.tsx | 32 ++++++++----------- 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/site/src/components/AuditLogRow/AuditLogRow.tsx b/site/src/components/AuditLogRow/AuditLogRow.tsx index a573885d492a3..c3459300be724 100644 --- a/site/src/components/AuditLogRow/AuditLogRow.tsx +++ b/site/src/components/AuditLogRow/AuditLogRow.tsx @@ -45,9 +45,6 @@ export const AuditLogRow: React.FC = ({ const diffs = Object.entries(auditLog.diff) const shouldDisplayDiff = diffs.length > 0 const { os, browser } = userAgentParser(auditLog.user_agent) - const displayBrowserInfo = browser.name - ? `${browser.name} ${browser.version}` - : t("table.logRow.notAvailable") let auditDiff = auditLog.diff @@ -127,21 +124,20 @@ export const AuditLogRow: React.FC = ({ {auditLog.ip} )} - - - <>{t("table.logRow.os")} - - {os.name - ? os.name - : // https://github.com/i18next/next-i18next/issues/1795 - t("table.logRow.notAvailable")} - - - - - <>{t("table.logRow.browser")} - {displayBrowserInfo} - + {os.name && ( + + <>{t("table.logRow.os")} + {os.name} + + )} + {browser.name && ( + + <>{t("table.logRow.browser")} + + {browser.name} {browser.version} + + + )} Date: Tue, 7 Feb 2023 23:04:06 +0000 Subject: [PATCH 9/9] fix tests --- .../AuditLogDescription.test.tsx | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/site/src/components/AuditLogRow/AuditLogDescription/AuditLogDescription.test.tsx b/site/src/components/AuditLogRow/AuditLogDescription/AuditLogDescription.test.tsx index 7b4fc82096ef0..cb07125efaeba 100644 --- a/site/src/components/AuditLogRow/AuditLogDescription/AuditLogDescription.test.tsx +++ b/site/src/components/AuditLogRow/AuditLogDescription/AuditLogDescription.test.tsx @@ -75,7 +75,10 @@ describe("AuditLogDescription", () => { truncatedDescription: `${MockAuditLogSuccessfulLogin.user?.username} logged in`, target: "", onBehalfOf: undefined, - }).trim(), + }) + .replace(/<[^>]*>/g, " ") + .replace(/\s{2,}/g, " ") + .trim(), ), ).toBeInTheDocument() @@ -91,7 +94,10 @@ describe("AuditLogDescription", () => { truncatedDescription: `${MockAuditLogUnsuccessfulLoginKnownUser.user?.username} logged in`, target: "", onBehalfOf: undefined, - }).trim(), + }) + .replace(/<[^>]*>/g, " ") + .replace(/\s{2,}/g, " ") + .trim(), ), ).toBeInTheDocument() @@ -107,7 +113,10 @@ describe("AuditLogDescription", () => { truncatedDescription: "an unknown user logged in", target: "", onBehalfOf: undefined, - }).trim(), + }) + .replace(/<[^>]*>/g, " ") + .replace(/\s{2,}/g, " ") + .trim(), ), ).toBeInTheDocument()