From 32f112b0abd6f7acf555ea16ffa1c853f1391ea8 Mon Sep 17 00:00:00 2001 From: Ronen Mars Date: Tue, 18 Feb 2025 16:22:55 +0200 Subject: [PATCH] feat: download logs (#1015) --- src/assets/image/icons/Download.svg | 1 + src/assets/image/icons/index.ts | 2 + .../organisms/deployments/sessions/viewer.tsx | 99 +++++++++++++++---- src/constants/global.constants.ts | 1 + src/constants/index.ts | 1 + src/locales/en/deployments/translation.json | 4 + 6 files changed, 91 insertions(+), 17 deletions(-) create mode 100644 src/assets/image/icons/Download.svg diff --git a/src/assets/image/icons/Download.svg b/src/assets/image/icons/Download.svg new file mode 100644 index 000000000..08469e7d1 --- /dev/null +++ b/src/assets/image/icons/Download.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/image/icons/index.ts b/src/assets/image/icons/index.ts index eb1d79625..91ded87a4 100644 --- a/src/assets/image/icons/index.ts +++ b/src/assets/image/icons/index.ts @@ -25,6 +25,8 @@ export { default as DeploymentsIcon } from "@assets/image/icons/Deployments.svg? // Taken from: https://www.svgrepo.com/svg/450093/discord export { default as DiscordNoColorIcon } from "@assets/image/icons/DiscordNoColor.svg?react"; export { default as DownloadDownArrowIcon } from "@assets/image/icons/DownloadDownArrow.svg?react"; +// Taken from: https://fontawesome.com/icons/download?s=solid +export { default as DownloadIcon } from "@assets/image/icons/Download.svg?react"; export { default as EditIcon } from "@assets/image/icons/Edit.svg?react"; // Taken from: https://www.figma.com/design/klCZL68fUd6pw0DEu0Sj7f/Autokitteh%3A-Look-%26-Feel-design-versions?node-id=3744-126275&t=RHAsCZWKowRf5e4d-4 export { default as EventsFlag } from "@assets/image/icons/EventsFlag.svg?react"; diff --git a/src/components/organisms/deployments/sessions/viewer.tsx b/src/components/organisms/deployments/sessions/viewer.tsx index 09a517f6d..a6f02f95a 100644 --- a/src/components/organisms/deployments/sessions/viewer.tsx +++ b/src/components/organisms/deployments/sessions/viewer.tsx @@ -10,6 +10,7 @@ import ReactTimeAgo from "react-time-ago"; import { dateTimeFormat, defaultSessionTab, + maxInt32ValueInGo, namespaces, sessionLogRowHeight, sessionTabs, @@ -26,7 +27,7 @@ import { Frame, IconSvg, Loader, LogoCatLarge, Tab } from "@components/atoms"; import { Accordion, IdCopyButton, RefreshButton } from "@components/molecules"; import { SessionsTableState } from "@components/organisms/deployments"; -import { ArrowRightIcon, CircleMinusIcon, CirclePlusIcon } from "@assets/image/icons"; +import { DownloadIcon, ArrowRightIcon, CircleMinusIcon, CirclePlusIcon } from "@assets/image/icons"; export const SessionViewer = () => { const { deploymentId, projectId, sessionId } = useParams<{ @@ -48,6 +49,54 @@ export const SessionViewer = () => { const { loading: loadingOutputs, loadLogs: loadOutputs } = useOutputsCacheStore(); const { loading: loadingActivities, loadLogs: loadActivities } = useActivitiesCacheStore(); + const downloadSessionLogs = useCallback(async () => { + if (!sessionId || !sessionInfo) return; + + try { + const { data: sessionLogsData } = await SessionsService.getOutputsBySessionId( + sessionId, + "", + maxInt32ValueInGo + ); + + if (!sessionLogsData?.logs?.length) { + addToast({ + message: t("noLogsToDownload"), + type: "error", + }); + return; + } + + const logContent = sessionLogsData.logs + .reverse() + .map((log) => `[${log.time}]: ${log.print}`) + .join("\n"); + + const blob = new Blob([logContent], { type: "text/plain" }); + const url = URL.createObjectURL(blob); + + const dateTime = moment().local().format(dateTimeFormat); + + const fileName = `${sessionInfo.sourceType?.toLowerCase() || "session"}-${dateTime}.log`; + + const link = Object.assign(document.createElement("a"), { + href: url, + download: fileName, + }); + + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + URL.revokeObjectURL(url); + } catch (error) { + LoggerService.error(namespaces.ui.sessionsViewer, t("errorDownloadingLogsExtended", { error })); + addToast({ + message: t("errorDownloadingLogs"), + type: "error", + }); + } + }, [sessionId, sessionInfo, addToast, t]); + const closeEditor = useCallback(() => { if (deploymentId) { navigate(`/projects/${projectId}/deployments/${deploymentId}/sessions`); @@ -74,6 +123,7 @@ export const SessionViewer = () => { return; } setSessionInfo(sessionInfoResponse); + if (isInitialLoad) { setIsInitialLoad(false); } @@ -155,7 +205,7 @@ export const SessionViewer = () => { ) : ( -
+
@@ -223,23 +273,38 @@ export const SessionViewer = () => {
- {sessionInfo.inputs ? ( -
- +
+ {sessionInfo.inputs ? ( +
+ + + +
+ ) : null} +
+ +
+
- ) : null} +
diff --git a/src/constants/global.constants.ts b/src/constants/global.constants.ts index ab401a0db..d38868282 100644 --- a/src/constants/global.constants.ts +++ b/src/constants/global.constants.ts @@ -29,3 +29,4 @@ export const supportedProgrammingLanguages = [".py", ".star"]; export const allowedManualRunExtensions = ["python", "starlark"]; export const AKRoutes = isProduction ? Sentry.withSentryReactRouterV7Routing(Routes) : Routes; export const sentryDsn = import.meta.env.SENTRY_DSN; +export const maxInt32ValueInGo = 2147483647; diff --git a/src/constants/index.ts b/src/constants/index.ts index d1934c865..02c5821dd 100644 --- a/src/constants/index.ts +++ b/src/constants/index.ts @@ -22,6 +22,7 @@ export { hubSpotId, hubSpotFormId, sentryDsn, + maxInt32ValueInGo, } from "@constants/global.constants"; export { integrationToEditComponent } from "@constants/connections/editComponentsMapping.constants"; export { formsPerIntegrationsMapping } from "@constants/connections/formsPerIntegrationsMapping.constants"; diff --git a/src/locales/en/deployments/translation.json b/src/locales/en/deployments/translation.json index 4991a5b1c..0d67cd904 100644 --- a/src/locales/en/deployments/translation.json +++ b/src/locales/en/deployments/translation.json @@ -121,6 +121,10 @@ "buttons":{ "ariaCloseEditor": "Close session log viewer" }, + "downloadLogs": "Logs", + "errorDownloadingLogs": "Error downloading logs", + "errorDownloadingLogsExtended": "Error downloading logs, error: {{error}}", + "noLogsToDownload": "No logs to download", "trigger": "Trigger", "source": "Source", "sourceType": "Source Type",