279 lines
8.1 KiB
TypeScript
279 lines
8.1 KiB
TypeScript
import {
|
|
Component,
|
|
EventEmitter,
|
|
Input,
|
|
OnInit,
|
|
Output,
|
|
ViewEncapsulation
|
|
} from '@angular/core'
|
|
import { SASjsRequest } from '@sasjs/adapter'
|
|
import moment from 'moment'
|
|
import { HelperService } from 'src/app/services/helper.service'
|
|
import { LoggerService } from '../../services/logger.service'
|
|
import { SasService } from '../../services/sas.service'
|
|
import { ServerType } from '@sasjs/utils/types/serverType'
|
|
interface SASjsRequestExtended extends SASjsRequest {
|
|
parsedTimestamp?: string
|
|
logErrors?: string[]
|
|
logWarnings?: string[]
|
|
selectedTable?: string
|
|
}
|
|
|
|
@Component({
|
|
selector: 'app-requests-modal',
|
|
templateUrl: './requests-modal.component.html',
|
|
styleUrls: ['./requests-modal.component.scss'],
|
|
encapsulation: ViewEncapsulation.None,
|
|
standalone: false
|
|
})
|
|
export class RequestsModalComponent implements OnInit {
|
|
private _opened: boolean = false
|
|
get opened(): boolean {
|
|
return this._opened
|
|
}
|
|
@Input()
|
|
set opened(value: boolean) {
|
|
this._opened = value
|
|
if (value) this.modalOpened()
|
|
|
|
this.loggerService.log(this.sasjsRequests)
|
|
}
|
|
|
|
@Output() openedChange = new EventEmitter()
|
|
|
|
public sasLogActive: boolean = true
|
|
public sasSourceCodeActive: boolean = false
|
|
public sasGeneratedCodeActive: boolean = false
|
|
public tablesActive: boolean = false
|
|
|
|
public sasjsConfig = this.sasService.getSasjsConfig()
|
|
public serverType: string
|
|
public sasjsRequests: SASjsRequestExtended[] = []
|
|
public workTables: any
|
|
|
|
constructor(
|
|
private sasService: SasService,
|
|
private loggerService: LoggerService,
|
|
private helperService: HelperService
|
|
) {
|
|
this.sasjsConfig = this.sasService.getSasjsConfig()
|
|
this.serverType = this.sasService.getServerType()
|
|
}
|
|
|
|
ngOnInit(): void {}
|
|
|
|
public parseLogTimestamp(timestamp: any) {
|
|
return `${this.formatTimestamp(timestamp)} ${this.timestampFromNow(
|
|
timestamp
|
|
)}`
|
|
}
|
|
|
|
public cutAppLoc(link: string) {
|
|
return link.replace(this.sasjsConfig.appLoc + '/', '')
|
|
}
|
|
|
|
public formatTimestamp(timestamp: any) {
|
|
return moment(timestamp).format()
|
|
? moment(timestamp).format('dddd, MMMM Do YYYY, h:mm:ss a')
|
|
: timestamp
|
|
}
|
|
|
|
public timestampFromNow(timestamp: any) {
|
|
return moment(timestamp).format() ? ` (${moment(timestamp).fromNow()})` : ''
|
|
}
|
|
|
|
public modalOpenChange(state: any) {
|
|
this.opened = state
|
|
this.openedChange.emit(this.opened)
|
|
}
|
|
|
|
public modalOpened() {
|
|
this.sasjsRequests = this.sasService.getSasRequests()
|
|
|
|
for (let request of this.sasjsRequests) {
|
|
this.parseErrorsAndWarnings(request)
|
|
|
|
request.serviceLink = this.cutAppLoc(request.serviceLink)
|
|
request.parsedTimestamp = this.parseLogTimestamp(request.timestamp)
|
|
}
|
|
}
|
|
|
|
public goToLogLine(
|
|
linkingLine: string,
|
|
requestStackId: string,
|
|
type: string
|
|
) {
|
|
const logWrapper: HTMLElement | null = document.querySelector(
|
|
`#${requestStackId} .log-wrapper.saslog`
|
|
)
|
|
|
|
if (!logWrapper) return
|
|
|
|
if (this.serverType === 'SASVIYA') {
|
|
// For VIYA (textContent approach)
|
|
const logContent = logWrapper.textContent || ''
|
|
const logLines = logContent.split('\n')
|
|
let lineIndex = -1
|
|
|
|
// Find the matching line index
|
|
for (let i = 0; i < logLines.length; i++) {
|
|
if (logLines[i].includes(linkingLine)) {
|
|
lineIndex = i
|
|
break
|
|
}
|
|
}
|
|
|
|
if (lineIndex === -1) return
|
|
|
|
// Create a temporary div to calculate line height
|
|
const tempDiv = document.createElement('div')
|
|
tempDiv.className = 'temp-line-height-calc'
|
|
tempDiv.textContent = 'X'
|
|
logWrapper.appendChild(tempDiv)
|
|
const lineHeight = tempDiv.clientHeight
|
|
logWrapper.removeChild(tempDiv)
|
|
|
|
// Scroll to the appropriate position
|
|
logWrapper.scrollTop = lineHeight * lineIndex
|
|
|
|
// Create highlighting overlay at that position
|
|
const overlay = document.createElement('div')
|
|
overlay.className = `line-highlight-overlay ${type === 'error' ? 'error-highlight' : 'warning-highlight'}`
|
|
|
|
// We are setting the height using a CSS variable to the HTML element
|
|
// this does not trigger CSP errors because it's driven by JS
|
|
overlay.classList.add('temp-height-setter')
|
|
document.documentElement.style.setProperty(
|
|
'--line-height',
|
|
`${lineHeight}px`
|
|
)
|
|
overlay.classList.add('line-position-setter')
|
|
document.documentElement.style.setProperty(
|
|
'--line-top',
|
|
`${lineHeight * lineIndex}px`
|
|
)
|
|
|
|
logWrapper.appendChild(overlay)
|
|
|
|
setTimeout(() => {
|
|
if (logWrapper.contains(overlay)) {
|
|
logWrapper.removeChild(overlay)
|
|
}
|
|
}, 3000)
|
|
} else {
|
|
// For non-VIYA (innerHTML approach)
|
|
const allLines: NodeListOf<Element> = document.querySelectorAll(
|
|
`#${requestStackId} .log-wrapper.saslog font`
|
|
)
|
|
|
|
for (let line of Array.from(allLines)) {
|
|
if (line.textContent?.includes(linkingLine)) {
|
|
logWrapper.scrollTop =
|
|
(line as HTMLElement).offsetTop - logWrapper.offsetTop
|
|
// Use class instead of inline style
|
|
line.classList.add('highlighted-line')
|
|
|
|
setTimeout(() => {
|
|
line.classList.remove('highlighted-line')
|
|
}, 3000)
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public async parseErrorsAndWarnings(req: any) {
|
|
if (!req || !req.logFile || typeof req.logFile !== 'string') return
|
|
if (req['logErrors'] !== undefined || req['logWarnings'] !== undefined)
|
|
return
|
|
|
|
let errorLines = []
|
|
let warningLines = []
|
|
|
|
// Create a safe version of the log content
|
|
let logLines = req.logFile.split('\n')
|
|
let originalLogLines = [...logLines]
|
|
|
|
for (let i = 0; i < logLines.length; i++) {
|
|
if (/<.*>ERROR/gm.test(logLines[i])) {
|
|
let errorLine = logLines[i].substring(
|
|
logLines[i].indexOf('E'),
|
|
logLines[i].length - 1
|
|
)
|
|
errorLines.push(errorLine)
|
|
} else if (/^ERROR/gm.test(logLines[i])) {
|
|
errorLines.push(logLines[i])
|
|
|
|
if (this.serverType !== 'SASVIYA') {
|
|
logLines[i] = '<font class="error-line">' + logLines[i] + '</font>'
|
|
}
|
|
}
|
|
|
|
if (/<.*>WARNING/gm.test(logLines[i])) {
|
|
let warningLine = logLines[i].substring(
|
|
logLines[i].indexOf('W'),
|
|
logLines[i].length - 1
|
|
)
|
|
warningLines.push(warningLine)
|
|
} else if (/^WARNING/gm.test(logLines[i])) {
|
|
warningLines.push(logLines[i])
|
|
|
|
if (this.serverType !== 'SASVIYA') {
|
|
logLines[i] = '<font class="warning-line">' + logLines[i] + '</font>'
|
|
}
|
|
}
|
|
}
|
|
|
|
this.loggerService.log(warningLines)
|
|
|
|
// For VIYA, store both versions of the log
|
|
if (this.serverType === 'SASVIYA') {
|
|
req.originalLogFile = originalLogLines.join('\n')
|
|
req.logFileLineMap = {}
|
|
|
|
// Create a mapping of error/warning lines to their indices
|
|
errorLines.forEach((errorLine) => {
|
|
for (let i = 0; i < originalLogLines.length; i++) {
|
|
if (originalLogLines[i].includes(errorLine)) {
|
|
if (!req.logFileLineMap.errors) req.logFileLineMap.errors = {}
|
|
req.logFileLineMap.errors[errorLine] = i
|
|
break
|
|
}
|
|
}
|
|
})
|
|
|
|
warningLines.forEach((warningLine) => {
|
|
for (let i = 0; i < originalLogLines.length; i++) {
|
|
if (originalLogLines[i].includes(warningLine)) {
|
|
if (!req.logFileLineMap.warnings) req.logFileLineMap.warnings = {}
|
|
req.logFileLineMap.warnings[warningLine] = i
|
|
break
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
req.logFile = logLines.join('\n')
|
|
req.logErrors = errorLines
|
|
req.logWarnings = warningLines
|
|
}
|
|
|
|
downloadLog(logFile: string) {
|
|
const timestamp = new Date().valueOf()
|
|
this.helperService.downloadTextFile(`logFile-${timestamp}`, logFile)
|
|
}
|
|
|
|
downloadSourceCode(sourceCode: string) {
|
|
const timestamp = new Date().valueOf()
|
|
this.helperService.downloadTextFile(`sourceCode-${timestamp}`, sourceCode)
|
|
}
|
|
|
|
downloadGeneratedCode(generatedCode: string) {
|
|
const timestamp = new Date().valueOf()
|
|
this.helperService.downloadTextFile(
|
|
`generatedCode-${timestamp}`,
|
|
generatedCode
|
|
)
|
|
}
|
|
}
|