src/app/shared/requests-modal/requests-modal.component.ts

Extends

SASjsRequest

Relationships

Used by

No results matching.

Depends on

  • SASjsRequest

Index

Properties

Properties

logErrors
logErrors: string[]
Type : string[]
Optional
logWarnings
logWarnings: string[]
Type : string[]
Optional
parsedTimestamp
parsedTimestamp: string
Type : string
Optional
selectedTable
selectedTable: string
Type : string
Optional
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
    )
  }
}

results matching ""

    No results matching ""