1
0
mirror of https://github.com/sasjs/server.git synced 2025-12-12 11:54:35 +00:00

feat(log): use improved log for SAS run time only

This commit is contained in:
Yury Shkoda
2023-04-11 14:18:42 +03:00
parent 3a887dec55
commit 7b12591595
5 changed files with 134 additions and 86 deletions

View File

@@ -23,6 +23,7 @@ import LogTabWithIcons from './internal/components/log/logTabWithIcons'
import { usePrompt } from '../../utils/hooks' import { usePrompt } from '../../utils/hooks'
import { getLanguageFromExtension } from './internal/helper' import { getLanguageFromExtension } from './internal/helper'
import useEditor from './internal/hooks/useEditor' import useEditor from './internal/hooks/useEditor'
import { RunTimeType } from '../../context/appContext'
const StyledTabPanel = styled(TabPanel)(() => ({ const StyledTabPanel = styled(TabPanel)(() => ({
padding: '10px' padding: '10px'
@@ -110,7 +111,11 @@ const SASjsEditor = ({
/> />
) )
const logWithErrorsOrWarnings = log?.errors.length || log?.warnings.length // INFO: variable indicating if selected run type is SAS if there are any errors or warnings in the log
const logWithErrorsOrWarnings =
selectedRunTime === RunTimeType.SAS &&
log &&
(log?.errors.length !== 0 || log?.warnings.length !== 0)
return ( return (
<Box sx={{ width: '100%', typography: 'body1', marginTop: '50px' }}> <Box sx={{ width: '100%', typography: 'body1', marginTop: '50px' }}>
@@ -155,6 +160,11 @@ const SASjsEditor = ({
icon={ icon={
logWithErrorsOrWarnings ? <LogTabWithIcons log={log} /> : '' logWithErrorsOrWarnings ? <LogTabWithIcons log={log} /> : ''
} }
onClick={() => {
const logWrapper = document.querySelector(`#logWrapper`)
if (logWrapper) logWrapper.scrollTop = 0
}}
/> />
<StyledTab <StyledTab
label={ label={
@@ -205,7 +215,9 @@ const SASjsEditor = ({
</Paper> </Paper>
</StyledTabPanel> </StyledTabPanel>
<StyledTabPanel value="log"> <StyledTabPanel value="log">
{log && <LogComponent log={log} />} {log && (
<LogComponent log={log} selectedRunTime={selectedRunTime} />
)}
</StyledTabPanel> </StyledTabPanel>
<StyledTabPanel value="webout"> <StyledTabPanel value="webout">
<div> <div>

View File

@@ -6,6 +6,7 @@ import { ListItemText } from '@mui/material'
import { makeStyles } from '@mui/styles' import { makeStyles } from '@mui/styles'
import Highlight from 'react-highlight' import Highlight from 'react-highlight'
import { LogObject } from '../../../../../utils' import { LogObject } from '../../../../../utils'
import { RunTimeType } from '../../../../../context/appContext'
const useStyles: any = makeStyles((theme: any) => ({ const useStyles: any = makeStyles((theme: any) => ({
expansionDescription: { expansionDescription: {
@@ -30,10 +31,11 @@ const useStyles: any = makeStyles((theme: any) => ({
interface LogComponentProps { interface LogComponentProps {
log: LogObject log: LogObject
selectedRunTime: RunTimeType
} }
const LogComponent = (props: LogComponentProps) => { const LogComponent = (props: LogComponentProps) => {
const { log } = props const { log, selectedRunTime } = props
const classes = useStyles() const classes = useStyles()
@@ -63,77 +65,91 @@ const LogComponent = (props: LogComponentProps) => {
} }
return ( return (
<div <>
id="logWrapper" {selectedRunTime === RunTimeType.SAS ? (
style={{ overflowY: 'auto', maxHeight: 'calc(100vh - 130px)' }}
>
<div style={{ backgroundColor: 'white' }}>
<div <div
style={{ id="logWrapper"
display: 'flex', style={{ overflowY: 'auto', maxHeight: 'calc(100vh - 130px)' }}
flexDirection: 'row', >
justifyContent: 'center', <div style={{ backgroundColor: 'white' }}>
alignItems: 'center', <div
gap: 10 style={{
}} display: 'flex',
></div> flexDirection: 'row',
<div style={{ paddingBottom: 10 }}> justifyContent: 'center',
<TreeView alignItems: 'center',
defaultCollapseIcon={<ExpandMore />} gap: 10
defaultExpandIcon={<ChevronRight />} }}
> ></div>
{log?.errors.length && ( <div style={{ paddingBottom: 10 }}>
<TreeItem <TreeView
nodeId="errors" defaultCollapseIcon={<ExpandMore />}
label={ defaultExpandIcon={<ChevronRight />}
<Typography color="error">
{`Errors (${log.errors.length})`}
</Typography>
}
> >
{log.errors && {log?.errors.length !== 0 && (
log.errors.map((error, ind) => ( <TreeItem
<TreeItem nodeId="errors"
nodeId={`error_${ind}`} label={
label={<ListItemText primary={error} />} <Typography color="error">
key={`error_${ind}`} {`Errors (${log.errors.length})`}
onClick={() => goToLogLine('error', ind)} </Typography>
/> }
))} >
</TreeItem> {log.errors &&
)} log.errors.map((error, ind) => (
{log?.warnings.length && ( <TreeItem
<TreeItem nodeId={`error_${ind}`}
nodeId="warnings" label={<ListItemText primary={error} />}
label={ key={`error_${ind}`}
<Typography>{`Warnings (${log.warnings.length})`}</Typography> onClick={() => goToLogLine('error', ind)}
} />
> ))}
{log.warnings && </TreeItem>
log.warnings.map((warning, ind) => ( )}
<TreeItem {log?.warnings.length !== 0 && (
nodeId={`warning_${ind}`} <TreeItem
label={<ListItemText primary={warning} />} nodeId="warnings"
key={`warning_${ind}`} label={
onClick={() => goToLogLine('warning', ind)} <Typography>{`Warnings (${log.warnings.length})`}</Typography>
/> }
))} >
</TreeItem> {log.warnings &&
)} log.warnings.map((warning, ind) => (
</TreeView> <TreeItem
</div> nodeId={`warning_${ind}`}
</div> label={<ListItemText primary={warning} />}
key={`warning_${ind}`}
onClick={() => goToLogLine('warning', ind)}
/>
))}
</TreeItem>
)}
</TreeView>
</div>
</div>
<Typography <Typography
id={`log_container`} id={`log_container`}
variant="h5" variant="h5"
className={classes.expansionDescription} className={classes.expansionDescription}
> >
<Highlight className={'html'} innerHTML={true}> <Highlight className={'html'} innerHTML={true}>
{decodeHtml(log?.body || '')} {decodeHtml(log?.body || '')}
</Highlight> </Highlight>
</Typography> </Typography>
</div> </div>
) : (
<div>
<h2>Log</h2>
<pre
id="log"
style={{ overflow: 'auto', height: 'calc(100vh - 220px)' }}
>
{log}
</pre>
</div>
)}
</>
) )
} }

View File

@@ -16,15 +16,14 @@ const LogTabWithIcons = (props: LogTabProps) => {
gap: 6, gap: 6,
alignItems: 'center' alignItems: 'center'
}} }}
onClick={() => {
const logWrapper = document.querySelector(`#logWrapper`)
if (logWrapper) logWrapper.scrollTop = 0
}}
> >
<span>log</span> <span>log</span>
{errors.length && <ErrorOutline color="error" style={{ fontSize: 20 }} />} {errors.length !== 0 && (
{warnings.length && <Warning style={{ fontSize: 20 }} />}{' '} <ErrorOutline color="error" style={{ fontSize: 20 }} />
)}
{warnings.length !== 0 && (
<Warning style={{ fontSize: 20, color: 'green' }} />
)}{' '}
</div> </div>
) )
} }

View File

@@ -18,6 +18,7 @@ import {
useSnackbar, useSnackbar,
useStateWithCallback useStateWithCallback
} from '../../../../utils/hooks' } from '../../../../utils/hooks'
import { parseErrorsAndWarnings, LogObject } from '../../../../utils'
const SASJS_LOGS_SEPARATOR = const SASJS_LOGS_SEPARATOR =
'SASJS_LOGS_SEPARATOR_163ee17b6ff24f028928972d80a26784' 'SASJS_LOGS_SEPARATOR_163ee17b6ff24f028928972d80a26784'
@@ -41,10 +42,12 @@ const useEditor = ({
const [prevFileContent, setPrevFileContent] = useStateWithCallback('') const [prevFileContent, setPrevFileContent] = useStateWithCallback('')
const [fileContent, setFileContent] = useState('') const [fileContent, setFileContent] = useState('')
const [log, setLog] = useState('') const [log, setLog] = useState<LogObject>()
const [webout, setWebout] = useState('') const [webout, setWebout] = useState('')
const [runTimes, setRunTimes] = useState<string[]>([]) const [runTimes, setRunTimes] = useState<string[]>([])
const [selectedRunTime, setSelectedRunTime] = useState('') const [selectedRunTime, setSelectedRunTime] = useState<RunTimeType>(
RunTimeType.SAS
)
const [selectedFileExtension, setSelectedFileExtension] = useState('') const [selectedFileExtension, setSelectedFileExtension] = useState('')
const [openFilePathInputModal, setOpenFilePathInputModal] = useState(false) const [openFilePathInputModal, setOpenFilePathInputModal] = useState(false)
const [showDiff, setShowDiff] = useState(false) const [showDiff, setShowDiff] = useState(false)
@@ -150,6 +153,13 @@ const useEditor = ({
const runCode = useCallback( const runCode = useCallback(
(code: string) => { (code: string) => {
setIsLoading(true) setIsLoading(true)
// Scroll to bottom of log
const logElement = document.getElementById('log')
if (logElement) logElement.scrollTop = logElement.scrollHeight
setIsLoading(false)
axios axios
.post(`/SASjsApi/code/execute`, { .post(`/SASjsApi/code/execute`, {
code: programPathInjection( code: programPathInjection(
@@ -160,8 +170,18 @@ const useEditor = ({
runTime: selectedRunTime runTime: selectedRunTime
}) })
.then((res: any) => { .then((res: any) => {
const { errors, warnings, logLines } = parseErrorsAndWarnings(
res.data.split(SASJS_LOGS_SEPARATOR)[1]
)
const log: LogObject = {
body: logLines.join(`\n`),
errors,
warnings
}
setWebout(res.data.split(SASJS_LOGS_SEPARATOR)[0] ?? '') setWebout(res.data.split(SASJS_LOGS_SEPARATOR)[0] ?? '')
setLog(res.data.split(SASJS_LOGS_SEPARATOR)[1] ?? '') setLog(log)
setTab('log') setTab('log')
// Scroll to bottom of log // Scroll to bottom of log
@@ -249,7 +269,7 @@ const useEditor = ({
}, [appContext.runTimes]) }, [appContext.runTimes])
useEffect(() => { useEffect(() => {
if (runTimes.length) setSelectedRunTime(runTimes[0]) if (runTimes.length) setSelectedRunTime(runTimes[0] as RunTimeType)
}, [runTimes]) }, [runTimes])
useEffect(() => { useEffect(() => {
@@ -280,7 +300,6 @@ const useEditor = ({
const content = localStorage.getItem('fileContent') ?? '' const content = localStorage.getItem('fileContent') ?? ''
setFileContent(content) setFileContent(content)
} }
setLog('')
setWebout('') setWebout('')
setTab('code') setTab('code')
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
@@ -294,7 +313,9 @@ const useEditor = ({
useEffect(() => { useEffect(() => {
const fileExtension = selectedFileExtension.toLowerCase() const fileExtension = selectedFileExtension.toLowerCase()
if (runTimes.includes(fileExtension)) setSelectedRunTime(fileExtension)
if (runTimes.includes(fileExtension))
setSelectedRunTime(fileExtension as RunTimeType)
}, [selectedFileExtension, runTimes]) }, [selectedFileExtension, runTimes])
return { return {

View File

@@ -15,7 +15,7 @@ export const parseErrorsAndWarnings = (log: string) => {
errorLines.push(line) errorLines.push(line)
logLines[index] = logLines[index] =
`<font id="error_${errorLines.length - 1}">` + `<font id="error_${errorLines.length - 1}" style="color: red;">` +
logLines[index] + logLines[index] +
'</font>' '</font>'
} }
@@ -31,7 +31,7 @@ export const parseErrorsAndWarnings = (log: string) => {
warningLines.push(line) warningLines.push(line)
logLines[index] = logLines[index] =
`<font id="warning_${warningLines.length - 1}">` + `<font id="warning_${warningLines.length - 1}" style="color: green;">` +
logLines[index] + logLines[index] +
'</font>' '</font>'
} }